diff --git a/src/affix/Affix.tsx b/src/affix/Affix.tsx index d0db2483fa..d950033b57 100644 --- a/src/affix/Affix.tsx +++ b/src/affix/Affix.tsx @@ -44,16 +44,16 @@ const Affix = forwardRef((props, ref) => { } const calcTop = wrapToTop - containerToTop; // 节点顶部到 container 顶部的距离 - const containerHeight = - scrollContainer.current[scrollContainer.current instanceof Window ? 'innerHeight' : 'clientHeight'] - - wrapHeight; + const isWindow = typeof window !== 'undefined' && scrollContainer.current === window; + + const containerHeight = scrollContainer.current[isWindow ? 'innerHeight' : 'clientHeight'] - wrapHeight; const calcBottom = containerToTop + containerHeight - (offsetBottom ?? 0); // 计算 bottom 相对应的 top 值 let fixedTop: number | false; - if (offsetTop !== undefined && calcTop <= offsetTop) { + if (props.offsetTop !== undefined && calcTop <= offsetTop) { // top 的触发 fixedTop = containerToTop + offsetTop; - } else if (offsetBottom !== undefined && wrapToTop >= calcBottom) { + } else if (props?.offsetBottom !== undefined && wrapToTop >= calcBottom) { // bottom 的触发 fixedTop = calcBottom; } else { @@ -102,7 +102,7 @@ const Affix = forwardRef((props, ref) => { }); } ticking.current = true; - }, [classPrefix, offsetBottom, offsetTop, onFixedChange, zIndex]); + }, [classPrefix, offsetBottom, offsetTop, onFixedChange, props?.offsetBottom, props.offsetTop, zIndex]); useImperativeHandle(ref, () => ({ handleScroll, diff --git a/src/affix/__tests__/affix.test.tsx b/src/affix/__tests__/affix.test.tsx index 33bd0cadd6..9771f5e972 100644 --- a/src/affix/__tests__/affix.test.tsx +++ b/src/affix/__tests__/affix.test.tsx @@ -5,11 +5,23 @@ import Affix from '../index'; describe('Affix 组件测试', () => { const mockFn = vi.spyOn(HTMLDivElement.prototype, 'getBoundingClientRect'); const mockScrollTo = async (top: number) => { - mockFn.mockImplementation(() => ({ top, bottom: 0 })); + mockFn.mockImplementation(() => ({ + top, + bottom: 0, + left: 0, + right: 0, + height: 0, + width: 0, + x: 0, + y: 0, + toJSON: () => ({}), + })); }; + beforeEach(async () => { await mockScrollTo(0); }); + test('render perfectly', async () => { const { queryByText } = render( @@ -19,24 +31,115 @@ describe('Affix 组件测试', () => { expect(queryByText('固钉')).toBeInTheDocument(); }); - test('offsetTop and onFixedChange and zIndex', async () => { + + test('className', async () => { + const { container } = render( + +
固钉
+
, + ); + + const affixElement = container.querySelector('.custom-class-name'); + expect(affixElement).not.toBeNull(); + expect(affixElement?.className).toContain('custom-class-name'); + }); + + test('style', async () => { + const { container } = render( + +
固钉
+
, + ); + const affixElement = container.querySelector('.custom-class-name'); + expect(affixElement).not.toBeNull(); + expect((affixElement as HTMLElement)?.style.background).toBe('red'); + }); + + test('content', async () => { + const Children = () =>
固钉
; + const { queryByText } = render(} />); + expect(queryByText('固钉')).toBeInTheDocument(); + }); + + test('offsetTop trigger onFixedChange and zIndex', async () => { + const onFixedChangeMock = vi.fn(); + + const { getByText } = render( + +
固钉
+
, + ); + // 默认 + expect(onFixedChangeMock).toHaveBeenCalledTimes(0); + expect(getByText('固钉').parentNode).not.toHaveClass('t-affix'); + expect(getByText('固钉').parentElement?.style.zIndex).toBe(''); + + // offsetTop + await mockScrollTo(30); + await mockScrollTo(10); + await mockTimeout(() => false, 200); + expect(onFixedChangeMock).toHaveBeenCalledTimes(1); + + expect(getByText('固钉').parentNode).toHaveClass('t-affix'); + expect(getByText('固钉').parentElement?.style.zIndex).toBe('2'); + }); + + test('offsetBottom trigger onFixedChange and zIndex', async () => { const onFixedChangeMock = vi.fn(); const { getByText } = render( - +
固钉
, ); + // 默认 + expect(onFixedChangeMock).toHaveBeenCalledTimes(0); + expect(getByText('固钉').parentNode).not.toHaveClass('t-affix'); + expect(getByText('固钉').parentElement?.style.zIndex).toBe(''); + + // offsetBottom + const isWindow = typeof window !== 'undefined' && window.innerHeight !== undefined; + const { clientHeight } = document.documentElement; + const { innerHeight } = window; + await mockScrollTo((isWindow ? innerHeight : clientHeight) - 40); + await mockScrollTo(isWindow ? innerHeight : clientHeight); + await mockTimeout(() => false, 200); + expect(onFixedChangeMock).toHaveBeenCalledTimes(1); + + expect(getByText('固钉').parentNode).toHaveClass('t-affix'); + expect(getByText('固钉').parentElement?.style.zIndex).toBe('2'); + }); + + test('offsetTop and offsetBottom trigger onFixedChange and zIndex', async () => { + const onFixedChangeMock = vi.fn(); - expect(onFixedChangeMock).toBeCalledTimes(0); + const { getByText } = render( + +
固钉
+
, + ); + // 默认 + expect(onFixedChangeMock).toHaveBeenCalledTimes(0); expect(getByText('固钉').parentNode).not.toHaveClass('t-affix'); - expect(getByText('固钉').parentElement.style.zIndex).toBe(''); + expect(getByText('固钉').parentElement?.style.zIndex).toBe(''); - await mockScrollTo(-10); + // offsetTop + await mockScrollTo(30); + await mockScrollTo(10); + await mockTimeout(() => false, 200); + expect(onFixedChangeMock).toHaveBeenCalledTimes(1); + // offsetBottom + const isWindow = typeof window !== 'undefined' && window.innerHeight !== undefined; + const { clientHeight } = document.documentElement; + const { innerHeight } = window; + await mockScrollTo((isWindow ? innerHeight : clientHeight) - 40); + await mockScrollTo(isWindow ? innerHeight : clientHeight); await mockTimeout(() => false, 200); + expect(onFixedChangeMock).toHaveBeenCalledTimes(1); + expect(getByText('固钉').parentNode).toHaveClass('t-affix'); - expect(getByText('固钉').parentElement.style.zIndex).toBe('2'); + expect(getByText('固钉').parentElement?.style.zIndex).toBe('2'); }); });