refactor: change anchor to fc for the cssinjs prepare (#35073)

* refactor: change anchor to fc for the cssinjs prepare

* fix: fix anchor test
This commit is contained in:
Long Hao (龙濠) 2022-04-19 16:44:47 +08:00 committed by GitHub
parent d6b230958a
commit eb7f8c7ef1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 125 additions and 84 deletions

View File

@ -3,7 +3,6 @@ import classNames from 'classnames';
import memoizeOne from 'memoize-one'; import memoizeOne from 'memoize-one';
import addEventListener from 'rc-util/lib/Dom/addEventListener'; import addEventListener from 'rc-util/lib/Dom/addEventListener';
import Affix from '../affix'; import Affix from '../affix';
import AnchorLink from './AnchorLink';
import { ConfigContext, ConfigConsumerProps } from '../config-provider'; import { ConfigContext, ConfigConsumerProps } from '../config-provider';
import scrollTo from '../_util/scrollTo'; import scrollTo from '../_util/scrollTo';
import getScroll from '../_util/getScroll'; import getScroll from '../_util/getScroll';
@ -62,6 +61,10 @@ export interface AnchorProps {
onChange?: (currentActiveLink: string) => void; onChange?: (currentActiveLink: string) => void;
} }
interface InternalAnchorProps extends AnchorProps {
anchorPrefixCls: string;
}
export interface AnchorState { export interface AnchorState {
activeLink: null | string; activeLink: null | string;
} }
@ -84,9 +87,7 @@ export interface AntAnchor {
) => void; ) => void;
} }
export default class Anchor extends React.Component<AnchorProps, AnchorState, ConfigConsumerProps> { class Anchor extends React.Component<InternalAnchorProps, AnchorState, ConfigConsumerProps> {
static Link: typeof AnchorLink;
static defaultProps = { static defaultProps = {
affix: true, affix: true,
showInkInFixed: false, showInkInFixed: false,
@ -268,9 +269,9 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState, Co
); );
render() { render() {
const { getPrefixCls, direction } = this.context; const { direction } = this.context;
const { const {
prefixCls: customizePrefixCls, anchorPrefixCls: prefixCls,
className = '', className = '',
style, style,
offsetTop, offsetTop,
@ -281,8 +282,6 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState, Co
} = this.props; } = this.props;
const { activeLink } = this.state; const { activeLink } = this.state;
const prefixCls = getPrefixCls('anchor', customizePrefixCls);
// To support old version react. // To support old version react.
// Have to add prefixCls on the instance. // Have to add prefixCls on the instance.
// https://github.com/facebook/react/issues/12397 // https://github.com/facebook/react/issues/12397
@ -335,3 +334,22 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState, Co
); );
} }
} }
// just use in test
export type InternalAnchorClass = Anchor;
const AnchorFC = React.forwardRef<Anchor, AnchorProps>((props, ref) => {
const { prefixCls: customizePrefixCls } = props;
const { getPrefixCls } = React.useContext(ConfigContext);
const anchorPrefixCls = getPrefixCls('anchor', customizePrefixCls);
const anchorProps: InternalAnchorProps = {
...props,
anchorPrefixCls,
};
return <Anchor {...anchorProps} ref={ref} />;
});
export default AnchorFC;

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import Anchor from '..'; import Anchor from '..';
import type { InternalAnchorClass } from '../Anchor';
import { sleep, render } from '../../../tests/utils'; import { sleep, render } from '../../../tests/utils';
const { Link } = Anchor; const { Link } = Anchor;
@ -47,43 +48,42 @@ describe('Anchor Render', () => {
it('Anchor render perfectly', () => { it('Anchor render perfectly', () => {
const hash = getHashUrl(); const hash = getHashUrl();
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor> <Anchor>
<Link href={`#${hash}`} title={hash} /> <Link href={`#${hash}`} title={hash} />
</Anchor>, </Anchor>,
); );
wrapper.find(`a[href="#${hash}"]`).simulate('click'); wrapper.find(`a[href="#${hash}"]`).simulate('click');
const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
wrapper.find<Anchor>(Anchor).instance().handleScroll(); anchorInstance.handleScroll();
expect(wrapper.find(Anchor).instance().state).not.toBe(null); expect(anchorInstance.state).not.toBe(null);
}); });
it('Anchor render perfectly for complete href - click', () => { it('Anchor render perfectly for complete href - click', () => {
const hash = getHashUrl(); const hash = getHashUrl();
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor> <Anchor>
<Link href={`http://www.example.com/#${hash}`} title={hash} /> <Link href={`http://www.example.com/#${hash}`} title={hash} />
</Anchor>, </Anchor>,
); );
wrapper.find(`a[href="http://www.example.com/#${hash}"]`).simulate('click'); wrapper.find(`a[href="http://www.example.com/#${hash}"]`).simulate('click');
expect(wrapper.find<Anchor>(Anchor).instance().state.activeLink).toBe( const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
`http://www.example.com/#${hash}`, expect(anchorInstance.state.activeLink).toBe(`http://www.example.com/#${hash}`);
);
}); });
it('Anchor render perfectly for complete href - hash router', async () => { it('Anchor render perfectly for complete href - hash router', async () => {
const root = createDiv(); const root = createDiv();
const scrollToSpy = jest.spyOn(window, 'scrollTo'); const scrollToSpy = jest.spyOn(window, 'scrollTo');
mount(<div id="/faq?locale=en#Q1">Q1</div>, { attachTo: root }); mount(<div id="/faq?locale=en#Q1">Q1</div>, { attachTo: root });
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor> <Anchor>
<Link href="/#/faq?locale=en#Q1" title="Q1" /> <Link href="/#/faq?locale=en#Q1" title="Q1" />
</Anchor>, </Anchor>,
); );
const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
wrapper.find<Anchor>(Anchor).instance().handleScrollTo('/#/faq?locale=en#Q1'); anchorInstance.handleScrollTo('/#/faq?locale=en#Q1');
expect(wrapper.find<Anchor>(Anchor).instance().state.activeLink).toBe('/#/faq?locale=en#Q1'); expect(anchorInstance.state.activeLink).toBe('/#/faq?locale=en#Q1');
expect(scrollToSpy).not.toHaveBeenCalled(); expect(scrollToSpy).not.toHaveBeenCalled();
await sleep(1000); await sleep(1000);
expect(scrollToSpy).toHaveBeenCalled(); expect(scrollToSpy).toHaveBeenCalled();
@ -93,15 +93,15 @@ describe('Anchor Render', () => {
const hash = getHashUrl(); const hash = getHashUrl();
const root = createDiv(); const root = createDiv();
mount(<div id={hash}>Hello</div>, { attachTo: root }); mount(<div id={hash}>Hello</div>, { attachTo: root });
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor> <Anchor>
<Link href={`http://www.example.com/#${hash}`} title={hash} /> <Link href={`http://www.example.com/#${hash}`} title={hash} />
</Anchor>, </Anchor>,
); );
wrapper.find<Anchor>(Anchor).instance().handleScroll(); const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
expect(wrapper.find<Anchor>(Anchor).instance().state.activeLink).toBe(
`http://www.example.com/#${hash}`, anchorInstance.handleScroll();
); expect(anchorInstance.state.activeLink).toBe(`http://www.example.com/#${hash}`);
}); });
it('Anchor render perfectly for complete href - scrollTo', async () => { it('Anchor render perfectly for complete href - scrollTo', async () => {
@ -109,13 +109,15 @@ describe('Anchor Render', () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo'); const scrollToSpy = jest.spyOn(window, 'scrollTo');
const root = createDiv(); const root = createDiv();
mount(<div id={`#${hash}`}>Hello</div>, { attachTo: root }); mount(<div id={`#${hash}`}>Hello</div>, { attachTo: root });
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor> <Anchor>
<Link href={`##${hash}`} title={hash} /> <Link href={`##${hash}`} title={hash} />
</Anchor>, </Anchor>,
); );
wrapper.find<Anchor>(Anchor).instance().handleScrollTo(`##${hash}`); const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
expect(wrapper.find<Anchor>(Anchor).instance().state.activeLink).toBe(`##${hash}`);
anchorInstance.handleScrollTo(`##${hash}`);
expect(anchorInstance.state.activeLink).toBe(`##${hash}`);
const calls = scrollToSpy.mock.calls.length; const calls = scrollToSpy.mock.calls.length;
await sleep(1000); await sleep(1000);
expect(scrollToSpy.mock.calls.length).toBeGreaterThan(calls); expect(scrollToSpy.mock.calls.length).toBeGreaterThan(calls);
@ -123,15 +125,14 @@ describe('Anchor Render', () => {
it('should remove listener when unmount', async () => { it('should remove listener when unmount', async () => {
const hash = getHashUrl(); const hash = getHashUrl();
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor> <Anchor>
<Link href={`#${hash}`} title={hash} /> <Link href={`#${hash}`} title={hash} />
</Anchor>, </Anchor>,
); );
const removeListenerSpy = jest.spyOn( const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
(wrapper.find<Anchor>(Anchor).instance() as any).scrollEvent,
'remove', const removeListenerSpy = jest.spyOn((anchorInstance as any).scrollEvent, 'remove');
);
wrapper.unmount(); wrapper.unmount();
expect(removeListenerSpy).toHaveBeenCalled(); expect(removeListenerSpy).toHaveBeenCalled();
}); });
@ -153,23 +154,20 @@ describe('Anchor Render', () => {
it('should update links when link href update', async () => { it('should update links when link href update', async () => {
const hash = getHashUrl(); const hash = getHashUrl();
let anchorInstance: Anchor | null = null;
function AnchorUpdate({ href }: { href: string }) { function AnchorUpdate({ href }: { href: string }) {
return ( return (
<Anchor <Anchor>
ref={c => {
anchorInstance = c;
}}
>
<Link href={href} title={hash} /> <Link href={href} title={hash} />
</Anchor> </Anchor>
); );
} }
const wrapper = mount(<AnchorUpdate href={`#${hash}`} />); const wrapper = mount(<AnchorUpdate href={`#${hash}`} />);
const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
if (anchorInstance == null) { if (anchorInstance == null) {
throw new Error('anchorInstance should not be null'); throw new Error('anchorInstance should not be null');
} }
expect((anchorInstance as any).links).toEqual([`#${hash}`]); expect((anchorInstance as any).links).toEqual([`#${hash}`]);
wrapper.setProps({ href: `#${hash}_1` }); wrapper.setProps({ href: `#${hash}_1` });
expect((anchorInstance as any).links).toEqual([`#${hash}_1`]); expect((anchorInstance as any).links).toEqual([`#${hash}_1`]);
@ -190,7 +188,7 @@ describe('Anchor Render', () => {
const href = `#${hash}`; const href = `#${hash}`;
const title = hash; const title = hash;
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor onClick={handleClick}> <Anchor onClick={handleClick}>
<Link href={href} title={title} /> <Link href={href} title={title} />
</Anchor>, </Anchor>,
@ -198,7 +196,9 @@ describe('Anchor Render', () => {
wrapper.find(`a[href="${href}"]`).simulate('click'); wrapper.find(`a[href="${href}"]`).simulate('click');
wrapper.find<Anchor>(Anchor).instance().handleScroll(); const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
anchorInstance.handleScroll();
expect(event).not.toBe(undefined); expect(event).not.toBe(undefined);
expect(link).toEqual({ href, title }); expect(link).toEqual({ href, title });
}); });
@ -210,15 +210,14 @@ describe('Anchor Render', () => {
const getContainerA = createGetContainer(hash); const getContainerA = createGetContainer(hash);
const getContainerB = createGetContainer(hash); const getContainerB = createGetContainer(hash);
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor getContainer={getContainerA}> <Anchor getContainer={getContainerA}>
<Link href={`#${hash}`} title={hash} /> <Link href={`#${hash}`} title={hash} />
</Anchor>, </Anchor>,
); );
const removeListenerSpy = jest.spyOn( const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
(wrapper.find<Anchor>(Anchor).instance() as any).scrollEvent,
'remove', const removeListenerSpy = jest.spyOn((anchorInstance as any).scrollEvent, 'remove');
);
await sleep(1000); await sleep(1000);
wrapper.setProps({ getContainer: getContainerB }); wrapper.setProps({ getContainer: getContainerB });
expect(removeListenerSpy).not.toHaveBeenCalled(); expect(removeListenerSpy).not.toHaveBeenCalled();
@ -237,16 +236,15 @@ describe('Anchor Render', () => {
); );
const getContainerA = createGetContainer(hash1); const getContainerA = createGetContainer(hash1);
const getContainerB = createGetContainer(hash2); const getContainerB = createGetContainer(hash2);
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor getContainer={getContainerA}> <Anchor getContainer={getContainerA}>
<Link href={`#${hash1}`} title={hash1} /> <Link href={`#${hash1}`} title={hash1} />
<Link href={`#${hash2}`} title={hash2} /> <Link href={`#${hash2}`} title={hash2} />
</Anchor>, </Anchor>,
); );
const removeListenerSpy = jest.spyOn( const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
(wrapper.find<Anchor>(Anchor).instance() as any).scrollEvent,
'remove', const removeListenerSpy = jest.spyOn((anchorInstance as any).scrollEvent, 'remove');
);
expect(removeListenerSpy).not.toHaveBeenCalled(); expect(removeListenerSpy).not.toHaveBeenCalled();
await sleep(1000); await sleep(1000);
wrapper.setProps({ getContainer: getContainerB }); wrapper.setProps({ getContainer: getContainerB });
@ -264,8 +262,10 @@ describe('Anchor Render', () => {
</Anchor>, </Anchor>,
); );
wrapper.find(`a[href="#${hash}"]`).simulate('click'); wrapper.find(`a[href="#${hash}"]`).simulate('click');
(wrapper.find<Anchor>(Anchor).instance() as any).handleScroll(); const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
expect(wrapper.find<Anchor>(Anchor).instance().state).not.toBe(null);
(anchorInstance as any).handleScroll();
expect(anchorInstance.state).not.toBe(null);
}); });
it('Same function returns different DOM', async () => { it('Same function returns different DOM', async () => {
@ -294,10 +294,9 @@ describe('Anchor Render', () => {
<Link href={`#${hash2}`} title={hash2} /> <Link href={`#${hash2}`} title={hash2} />
</Anchor>, </Anchor>,
); );
const removeListenerSpy = jest.spyOn( const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
(wrapper.find<Anchor>(Anchor).instance() as any).scrollEvent,
'remove', const removeListenerSpy = jest.spyOn((anchorInstance as any).scrollEvent, 'remove');
);
expect(removeListenerSpy).not.toHaveBeenCalled(); expect(removeListenerSpy).not.toHaveBeenCalled();
await sleep(1000); await sleep(1000);
holdContainer.container = document.getElementById(hash2); holdContainer.container = document.getElementById(hash2);
@ -325,24 +324,26 @@ describe('Anchor Render', () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo'); const scrollToSpy = jest.spyOn(window, 'scrollTo');
const root = createDiv(); const root = createDiv();
mount(<h1 id={hash}>Hello</h1>, { attachTo: root }); mount(<h1 id={hash}>Hello</h1>, { attachTo: root });
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor> <Anchor>
<Link href={`#${hash}`} title={hash} /> <Link href={`#${hash}`} title={hash} />
</Anchor>, </Anchor>,
); );
wrapper.find<Anchor>(Anchor).instance().handleScrollTo(`#${hash}`); const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
anchorInstance.handleScrollTo(`#${hash}`);
await sleep(30); await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
dateNowMock = dataNowMockFn(); dateNowMock = dataNowMockFn();
wrapper.setProps({ offsetTop: 100 }); wrapper.setProps({ offsetTop: 100 });
wrapper.find<Anchor>(Anchor).instance().handleScrollTo(`#${hash}`); anchorInstance.handleScrollTo(`#${hash}`);
await sleep(30); await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
dateNowMock = dataNowMockFn(); dateNowMock = dataNowMockFn();
wrapper.setProps({ targetOffset: 200 }); wrapper.setProps({ targetOffset: 200 });
wrapper.find<Anchor>(Anchor).instance().handleScrollTo(`#${hash}`); anchorInstance.handleScrollTo(`#${hash}`);
await sleep(30); await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
@ -370,24 +371,26 @@ describe('Anchor Render', () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo'); const scrollToSpy = jest.spyOn(window, 'scrollTo');
const root = createDiv(); const root = createDiv();
mount(<h1 id={hash}>Hello</h1>, { attachTo: root }); mount(<h1 id={hash}>Hello</h1>, { attachTo: root });
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor> <Anchor>
<Link href={`#${hash}`} title={hash} /> <Link href={`#${hash}`} title={hash} />
</Anchor>, </Anchor>,
); );
wrapper.find<Anchor>(Anchor).instance().handleScrollTo(`#${hash}`); const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
anchorInstance.handleScrollTo(`#${hash}`);
await sleep(30); await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
dateNowMock = dataNowMockFn(); dateNowMock = dataNowMockFn();
wrapper.setProps({ offsetTop: 100 }); wrapper.setProps({ offsetTop: 100 });
wrapper.find<Anchor>(Anchor).instance().handleScrollTo(`#${hash}`); anchorInstance.handleScrollTo(`#${hash}`);
await sleep(30); await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
dateNowMock = dataNowMockFn(); dateNowMock = dataNowMockFn();
wrapper.setProps({ targetOffset: 200 }); wrapper.setProps({ targetOffset: 200 });
wrapper.find<Anchor>(Anchor).instance().handleScrollTo(`#${hash}`); anchorInstance.handleScrollTo(`#${hash}`);
await sleep(30); await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
@ -398,20 +401,22 @@ describe('Anchor Render', () => {
const hash1 = getHashUrl(); const hash1 = getHashUrl();
const hash2 = getHashUrl(); const hash2 = getHashUrl();
const onChange = jest.fn(); const onChange = jest.fn();
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor onChange={onChange}> <Anchor onChange={onChange}>
<Link href={`#${hash1}`} title={hash1} /> <Link href={`#${hash1}`} title={hash1} />
<Link href={`#${hash2}`} title={hash2} /> <Link href={`#${hash2}`} title={hash2} />
</Anchor>, </Anchor>,
); );
const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
expect(onChange).toHaveBeenCalledTimes(1); expect(onChange).toHaveBeenCalledTimes(1);
wrapper.find<Anchor>(Anchor).instance().handleScrollTo(hash2); anchorInstance.handleScrollTo(hash2);
expect(onChange).toHaveBeenCalledTimes(2); expect(onChange).toHaveBeenCalledTimes(2);
expect(onChange).toHaveBeenCalledWith(hash2); expect(onChange).toHaveBeenCalledWith(hash2);
}); });
it('invalid hash', async () => { it('invalid hash', async () => {
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor> <Anchor>
<Link href="notexsited" title="title" /> <Link href="notexsited" title="title" />
</Anchor>, </Anchor>,
@ -419,8 +424,10 @@ describe('Anchor Render', () => {
wrapper.find(`a[href="notexsited"]`).simulate('click'); wrapper.find(`a[href="notexsited"]`).simulate('click');
wrapper.find<Anchor>(Anchor).instance().handleScrollTo('notexsited'); const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
expect(wrapper.find<Anchor>(Anchor).instance().state).not.toBe(null);
anchorInstance.handleScrollTo('notexsited');
expect(anchorInstance.state).not.toBe(null);
}); });
it('test edge case when getBoundingClientRect return zero size', async () => { it('test edge case when getBoundingClientRect return zero size', async () => {
@ -448,24 +455,26 @@ describe('Anchor Render', () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo'); const scrollToSpy = jest.spyOn(window, 'scrollTo');
const root = createDiv(); const root = createDiv();
mount(<h1 id={hash}>Hello</h1>, { attachTo: root }); mount(<h1 id={hash}>Hello</h1>, { attachTo: root });
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor> <Anchor>
<Link href={`#${hash}`} title={hash} /> <Link href={`#${hash}`} title={hash} />
</Anchor>, </Anchor>,
); );
wrapper.find<Anchor>(Anchor).instance().handleScrollTo(`#${hash}`); const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
anchorInstance.handleScrollTo(`#${hash}`);
await sleep(30); await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
dateNowMock = dataNowMockFn(); dateNowMock = dataNowMockFn();
wrapper.setProps({ offsetTop: 100 }); wrapper.setProps({ offsetTop: 100 });
wrapper.find<Anchor>(Anchor).instance().handleScrollTo(`#${hash}`); anchorInstance.handleScrollTo(`#${hash}`);
await sleep(30); await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
dateNowMock = dataNowMockFn(); dateNowMock = dataNowMockFn();
wrapper.setProps({ targetOffset: 200 }); wrapper.setProps({ targetOffset: 200 });
wrapper.find<Anchor>(Anchor).instance().handleScrollTo(`#${hash}`); anchorInstance.handleScrollTo(`#${hash}`);
await sleep(30); await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
@ -497,24 +506,26 @@ describe('Anchor Render', () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo'); const scrollToSpy = jest.spyOn(window, 'scrollTo');
const root = createDiv(); const root = createDiv();
mount(<h1 id={hash}>Hello</h1>, { attachTo: root }); mount(<h1 id={hash}>Hello</h1>, { attachTo: root });
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor getContainer={() => document.body}> <Anchor getContainer={() => document.body}>
<Link href={`#${hash}`} title={hash} /> <Link href={`#${hash}`} title={hash} />
</Anchor>, </Anchor>,
); );
wrapper.find<Anchor>(Anchor).instance().handleScrollTo(`#${hash}`); const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
anchorInstance.handleScrollTo(`#${hash}`);
await sleep(30); await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
dateNowMock = dataNowMockFn(); dateNowMock = dataNowMockFn();
wrapper.setProps({ offsetTop: 100 }); wrapper.setProps({ offsetTop: 100 });
wrapper.find<Anchor>(Anchor).instance().handleScrollTo(`#${hash}`); anchorInstance.handleScrollTo(`#${hash}`);
await sleep(30); await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
dateNowMock = dataNowMockFn(); dateNowMock = dataNowMockFn();
wrapper.setProps({ targetOffset: 200 }); wrapper.setProps({ targetOffset: 200 });
wrapper.find<Anchor>(Anchor).instance().handleScrollTo(`#${hash}`); anchorInstance.handleScrollTo(`#${hash}`);
await sleep(30); await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800); expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
@ -526,13 +537,15 @@ describe('Anchor Render', () => {
const hash1 = getHashUrl(); const hash1 = getHashUrl();
const hash2 = getHashUrl(); const hash2 = getHashUrl();
const getCurrentAnchor = () => `#${hash2}`; const getCurrentAnchor = () => `#${hash2}`;
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor getCurrentAnchor={getCurrentAnchor}> <Anchor getCurrentAnchor={getCurrentAnchor}>
<Link href={`#${hash1}`} title={hash1} /> <Link href={`#${hash1}`} title={hash1} />
<Link href={`#${hash2}`} title={hash2} /> <Link href={`#${hash2}`} title={hash2} />
</Anchor>, </Anchor>,
); );
expect(wrapper.find<Anchor>(Anchor).instance().state.activeLink).toBe(`#${hash2}`); const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
expect(anchorInstance.state.activeLink).toBe(`#${hash2}`);
}); });
// https://github.com/ant-design/ant-design/issues/30584 // https://github.com/ant-design/ant-design/issues/30584
@ -540,14 +553,16 @@ describe('Anchor Render', () => {
const hash1 = getHashUrl(); const hash1 = getHashUrl();
const hash2 = getHashUrl(); const hash2 = getHashUrl();
const onChange = jest.fn(); const onChange = jest.fn();
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor onChange={onChange} getCurrentAnchor={() => hash1}> <Anchor onChange={onChange} getCurrentAnchor={() => hash1}>
<Link href={`#${hash1}`} title={hash1} /> <Link href={`#${hash1}`} title={hash1} />
<Link href={`#${hash2}`} title={hash2} /> <Link href={`#${hash2}`} title={hash2} />
</Anchor>, </Anchor>,
); );
const anchorInstance = wrapper.find('Anchor').last().instance() as any as InternalAnchorClass;
expect(onChange).toHaveBeenCalledTimes(1); expect(onChange).toHaveBeenCalledTimes(1);
wrapper.find<Anchor>(Anchor).instance().handleScrollTo(hash2); anchorInstance.handleScrollTo(hash2);
expect(onChange).toHaveBeenCalledTimes(2); expect(onChange).toHaveBeenCalledTimes(2);
expect(onChange).toHaveBeenCalledWith(hash2); expect(onChange).toHaveBeenCalledWith(hash2);
}); });
@ -557,7 +572,7 @@ describe('Anchor Render', () => {
const hash1 = getHashUrl(); const hash1 = getHashUrl();
const hash2 = getHashUrl(); const hash2 = getHashUrl();
const getCurrentAnchor = jest.fn(); const getCurrentAnchor = jest.fn();
const wrapper = mount<Anchor>( const wrapper = mount(
<Anchor getCurrentAnchor={getCurrentAnchor}> <Anchor getCurrentAnchor={getCurrentAnchor}>
<Link href={`#${hash1}`} title={hash1} /> <Link href={`#${hash1}`} title={hash1} />
<Link href={`#${hash2}`} title={hash2} /> <Link href={`#${hash2}`} title={hash2} />

View File

@ -1,8 +1,16 @@
import Anchor from './Anchor'; import InternalAnchor from './Anchor';
import AnchorLink from './AnchorLink'; import AnchorLink from './AnchorLink';
export { AnchorProps } from './Anchor'; export { AnchorProps } from './Anchor';
export { AnchorLinkProps } from './AnchorLink'; export { AnchorLinkProps } from './AnchorLink';
type InternalAnchorType = typeof InternalAnchor;
interface AnchorInterface extends InternalAnchorType {
Link: typeof AnchorLink;
}
const Anchor = InternalAnchor as AnchorInterface;
Anchor.Link = AnchorLink; Anchor.Link = AnchorLink;
export default Anchor; export default Anchor;