= (props) => {
'children',
'visibilityHeight',
'target',
- 'visible',
]);
+ const defaultElement = (
+
+ );
+
return wrapSSR(
-
- {props.children}
-
+
+ {({ className: motionClassName }) =>
+ cloneElement(props.children || defaultElement, ({ className: cloneCls }) => ({
+ className: classNames(motionClassName, cloneCls),
+ }))
+ }
+
,
);
};
diff --git a/components/config-provider/__tests__/components.test.tsx b/components/config-provider/__tests__/components.test.tsx
index f2b95ed8e9..d9c4ebea4f 100644
--- a/components/config-provider/__tests__/components.test.tsx
+++ b/components/config-provider/__tests__/components.test.tsx
@@ -144,7 +144,7 @@ describe('ConfigProvider', () => {
testPair('Avatar', (props) => );
// BackTop
- testPair('BackTop', (props) => );
+ testPair('BackTop', (props) => );
// Badge
testPair('Badge', (props) => {
diff --git a/components/float-button/BackTop.tsx b/components/float-button/BackTop.tsx
index 7d2431e22b..827086bfd8 100644
--- a/components/float-button/BackTop.tsx
+++ b/components/float-button/BackTop.tsx
@@ -2,8 +2,7 @@ import VerticalAlignTopOutlined from '@ant-design/icons/VerticalAlignTopOutlined
import classNames from 'classnames';
import CSSMotion from 'rc-motion';
import addEventListener from 'rc-util/lib/Dom/addEventListener';
-import useMergedState from 'rc-util/lib/hooks/useMergedState';
-import React, { memo, useContext, useEffect, useRef } from 'react';
+import React, { memo, useContext, useEffect, useRef, useState } from 'react';
import FloatButton, { floatButtonPrefixCls } from './FloatButton';
import type { ConfigConsumerProps } from '../config-provider';
import { ConfigContext } from '../config-provider';
@@ -11,7 +10,7 @@ import getScroll from '../_util/getScroll';
import scrollTo from '../_util/scrollTo';
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame';
import FloatButtonGroupContext from './context';
-import type { BackTopProps, FloatButtonShape } from './interface';
+import type { BackTopProps, FloatButtonProps, FloatButtonShape } from './interface';
import useStyle from './style';
const BackTop: React.FC = (props) => {
@@ -28,46 +27,39 @@ const BackTop: React.FC = (props) => {
...restProps
} = props;
- const [visible, setVisible] = useMergedState(false, { value: props.visible });
+ const [visible, setVisible] = useState(visibilityHeight === 0);
const ref = useRef(null);
-
- const scrollEvent = useRef(null);
+ const scrollEvent = useRef | null>(null);
const getDefaultTarget = (): HTMLElement | Document | Window =>
ref.current && ref.current.ownerDocument ? ref.current.ownerDocument : window;
const handleScroll = throttleByAnimationFrame(
- (e: React.UIEvent | { target: any }) => {
+ (e: React.UIEvent | { target: any }) => {
const scrollTop = getScroll(e.target, true);
- setVisible(scrollTop > visibilityHeight!);
+ setVisible(scrollTop >= visibilityHeight);
},
);
const bindScrollEvent = () => {
const getTarget = target || getDefaultTarget;
const container = getTarget();
- scrollEvent.current = addEventListener(container, 'scroll', (e: React.UIEvent) => {
- handleScroll(e);
- });
+ scrollEvent.current = addEventListener(container, 'scroll', handleScroll);
handleScroll({ target: container });
};
useEffect(() => {
bindScrollEvent();
return () => {
- if (scrollEvent.current) {
- scrollEvent.current.remove();
- }
handleScroll.cancel();
+ scrollEvent.current?.remove();
};
}, [target]);
const scrollToTop: React.MouseEventHandler = (e) => {
scrollTo(0, { getContainer: target || getDefaultTarget, duration });
- if (typeof onClick === 'function') {
- onClick(e);
- }
+ onClick?.(e);
};
const { getPrefixCls } = useContext(ConfigContext);
@@ -80,7 +72,7 @@ const BackTop: React.FC = (props) => {
const mergeShape = groupShape || shape;
- const contentProps = { prefixCls, icon, type, shape: mergeShape, ...restProps };
+ const contentProps: FloatButtonProps = { prefixCls, icon, type, shape: mergeShape, ...restProps };
return wrapSSR(
diff --git a/components/float-button/PurePanel.tsx b/components/float-button/PurePanel.tsx
index ae9a3817e5..2732a06331 100644
--- a/components/float-button/PurePanel.tsx
+++ b/components/float-button/PurePanel.tsx
@@ -7,7 +7,7 @@ import BackTop from './BackTop';
import type { FloatButtonProps, FloatButtonGroupProps } from './interface';
import { ConfigContext } from '../config-provider';
-export interface PureFloatButtonProps extends FloatButtonProps {
+export interface PureFloatButtonProps extends Omit {
backTop?: boolean;
}
@@ -18,11 +18,10 @@ export interface PurePanelProps
items?: PureFloatButtonProps[];
}
-function PureFloatButton({ backTop, ...props }: PureFloatButtonProps) {
- return backTop ? : ;
-}
+const PureFloatButton: React.FC = ({ backTop, ...props }) =>
+ backTop ? : ;
-export default function PurePanel({ className, items, ...props }: PurePanelProps) {
+function PurePanel({ className, items, ...props }: PurePanelProps) {
const { prefixCls: customizePrefixCls } = props;
const { getPrefixCls } = React.useContext(ConfigContext);
@@ -41,3 +40,5 @@ export default function PurePanel({ className, items, ...props }: PurePanelProps
return ;
}
+
+export default React.memo(PurePanel);
diff --git a/components/float-button/__tests__/back-top.test.tsx b/components/float-button/__tests__/back-top.test.tsx
index 8695db60aa..251ddd9dec 100644
--- a/components/float-button/__tests__/back-top.test.tsx
+++ b/components/float-button/__tests__/back-top.test.tsx
@@ -2,52 +2,53 @@ import React from 'react';
import FloatButton from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
-import { fireEvent, render, sleep } from '../../../tests/utils';
+import { fireEvent, render, waitFakeTimer } from '../../../tests/utils';
const { BackTop } = FloatButton;
describe('BackTop', () => {
+ beforeEach(() => {
+ jest.useFakeTimers();
+ });
+ afterEach(() => {
+ jest.useRealTimers();
+ });
mountTest(BackTop);
rtlTest(BackTop);
it('should scroll to top after click it', async () => {
- const { container } = render();
+ const { container } = render();
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((_, y) => {
window.scrollY = y;
window.pageYOffset = y;
document.documentElement.scrollTop = y;
});
window.scrollTo(0, 400);
+ await waitFakeTimer();
expect(document.documentElement.scrollTop).toBe(400);
- fireEvent.click(container.querySelector('.ant-float-btn')!);
- await sleep(500);
+ fireEvent.click(container.querySelector('.ant-float-btn')!);
+ await waitFakeTimer();
expect(document.documentElement.scrollTop).toBe(0);
scrollToSpy.mockRestore();
});
it('support onClick', () => {
const onClick = jest.fn();
- const { container } = render();
- const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((_, y) => {
- window.scrollY = y;
- window.pageYOffset = y;
- });
- document.dispatchEvent(new Event('scroll'));
- window.scrollTo(0, 400);
- fireEvent.click(container.querySelector('.ant-float-btn')!);
+ const { container } = render();
+ fireEvent.click(container.querySelector('.ant-float-btn')!);
expect(onClick).toHaveBeenCalled();
- scrollToSpy.mockRestore();
});
- it('invalid target', () => {
+ it('support invalid target', () => {
const onClick = jest.fn();
- const { container } = render();
- fireEvent.click(container.querySelector('.ant-float-btn')!);
+ const { container } = render(
+ ,
+ );
+ fireEvent.click(container.querySelector('.ant-float-btn')!);
expect(onClick).toHaveBeenCalled();
});
it('pass style to float button', () => {
- const { container } = render();
- const btn = container.querySelector('.ant-float-btn')!;
- expect(btn).toHaveAttribute('style', 'color: red;');
+ const { container } = render();
+ expect(container.querySelector('.ant-float-btn')?.style.color).toBe('red');
});
});
diff --git a/components/float-button/index.tsx b/components/float-button/index.tsx
index 1c7da440bd..d3f6bdccb8 100644
--- a/components/float-button/index.tsx
+++ b/components/float-button/index.tsx
@@ -3,8 +3,8 @@ import FloatButtonGroup from './FloatButtonGroup';
import BackTop from './BackTop';
import PurePanel from './PurePanel';
-FloatButton.Group = FloatButtonGroup;
FloatButton.BackTop = BackTop;
+FloatButton.Group = FloatButtonGroup;
FloatButton._InternalPanelDoNotUseOrYouWillBeFired = PurePanel;
export default FloatButton;
diff --git a/components/float-button/interface.ts b/components/float-button/interface.ts
index 923f2cab0b..26216be0e3 100644
--- a/components/float-button/interface.ts
+++ b/components/float-button/interface.ts
@@ -53,7 +53,6 @@ export interface BackTopProps extends Omit {
className?: string;
style?: React.CSSProperties;
duration?: number;
- visible?: boolean; // Only for test. Don't use it.
}
export type CompoundedComponent = React.ForwardRefExoticComponent<