import React from 'react'; import type { InternalAffixClass } from '..'; import Affix from '..'; import accessibilityTest from '../../../tests/shared/accessibilityTest'; import rtlTest from '../../../tests/shared/rtlTest'; import { render, sleep, triggerResize } from '../../../tests/utils'; import Button from '../../button'; import { getObserverEntities } from '../utils'; const events: Partial) => void>> = {}; class AffixMounter extends React.Component<{ offsetBottom?: number; offsetTop?: number; onTestUpdatePosition?(): void; onChange?: () => void; getInstance?: (inst: InternalAffixClass) => void; }> { private container: HTMLDivElement; componentDidMount() { this.container.addEventListener = jest .fn() .mockImplementation((event: keyof HTMLElementEventMap, cb: (ev: Partial) => void) => { events[event] = cb; }); } getTarget = () => this.container; render() { const { getInstance, ...restProps } = this.props; return (
{ this.container = node!; }} className="container" > { getInstance?.(ele!); }} {...restProps} >
); } } describe('Affix Render', () => { rtlTest(Affix); accessibilityTest(Affix); const domMock = jest.spyOn(HTMLElement.prototype, 'getBoundingClientRect'); const classRect: Record = { container: { top: 0, bottom: 100, } as DOMRect, }; beforeAll(() => { domMock.mockImplementation(function fn(this: HTMLElement) { return ( classRect[this.className] || { top: 0, bottom: 0, } ); }); }); afterAll(() => { domMock.mockRestore(); }); const movePlaceholder = async (top: number) => { classRect.fixed = { top, bottom: top, } as DOMRect; if (events.scroll == null) { throw new Error('scroll should be set'); } events.scroll({ type: 'scroll', }); await sleep(20); }; it('Anchor render perfectly', async () => { const { container } = render(); await sleep(20); await movePlaceholder(0); expect(container.querySelector('.ant-affix')).toBeFalsy(); await movePlaceholder(-100); expect(container.querySelector('.ant-affix')).toBeTruthy(); await movePlaceholder(0); expect(container.querySelector('.ant-affix')).toBeFalsy(); }); it('support offsetBottom', async () => { const { container } = render(); await sleep(20); await movePlaceholder(300); expect(container.querySelector('.ant-affix')).toBeTruthy(); await movePlaceholder(0); expect(container.querySelector('.ant-affix')).toBeFalsy(); await movePlaceholder(300); expect(container.querySelector('.ant-affix')).toBeTruthy(); }); it('updatePosition when offsetTop changed', async () => { const onChange = jest.fn(); const { container, rerender } = render(); await sleep(20); await movePlaceholder(-100); expect(onChange).toHaveBeenLastCalledWith(true); expect(container.querySelector('.ant-affix')).toHaveStyle({ top: 0 }); rerender(); await sleep(20); expect(container.querySelector('.ant-affix')).toHaveStyle({ top: `10px` }); }); describe('updatePosition when target changed', () => { it('function change', async () => { document.body.innerHTML = '
'; const container = document.querySelector('#id') as HTMLDivElement; const getTarget = () => container; let affixInstance: InternalAffixClass; const { rerender } = render( { affixInstance = node as InternalAffixClass; }} target={getTarget} > {null} , ); rerender( { affixInstance = node as InternalAffixClass; }} target={() => null} > {null} , ); expect(affixInstance!.state.status).toBe(0); expect(affixInstance!.state.affixStyle).toBe(undefined); expect(affixInstance!.state.placeholderStyle).toBe(undefined); await sleep(100); }); it('instance change', async () => { const getObserverLength = () => Object.keys(getObserverEntities()).length; const container = document.createElement('div'); document.body.appendChild(container); let target: HTMLDivElement | null = container; const originLength = getObserverLength(); const getTarget = () => target; const { rerender } = render({null}); await sleep(100); expect(getObserverLength()).toBe(originLength + 1); target = null; rerender({null}); await sleep(100); expect(getObserverLength()).toBe(originLength); }); }); describe('updatePosition when size changed', () => { it('add class automatically', async () => { document.body.innerHTML = '
'; let affixInstance: InternalAffixClass | null = null; render( { affixInstance = inst; }} offsetBottom={0} />, { container: document.getElementById('mounter')!, }, ); await sleep(20); await movePlaceholder(300); expect(affixInstance!.state.affixStyle).toBeTruthy(); }); // Trigger inner and outer element for the two s. it.each([ { selector: '.ant-btn' }, // inner { selector: '.fixed' }, // outer ])('trigger listener when size change', async ({ selector }) => { const updateCalled = jest.fn(); const { container } = render( , { container: document.getElementById('mounter')!, }, ); updateCalled.mockReset(); triggerResize(container.querySelector(selector)!); await sleep(20); expect(updateCalled).toHaveBeenCalled(); }); }); });