From 706c010671b03ebb81be1485522168f485c79c20 Mon Sep 17 00:00:00 2001 From: lijianan <574980606@qq.com> Date: Tue, 11 Nov 2025 11:21:39 +0800 Subject: [PATCH] chore: reset deprecated BackTop (#55668) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: reset deprecated BackTop * chore: adjust import * chore: update deps * update * update * update * update * update snap * update --------- Co-authored-by: 二货机器人 --- .../__snapshots__/index.test.ts.snap | 1 + .../__snapshots__/demo-extend.test.ts.snap | 44 ++++ .../__snapshots__/demo.test.tsx.snap | 38 +++ .../__snapshots__/index.test.tsx.snap | 7 + components/back-top/__tests__/a11y.test.ts | 5 + .../back-top/__tests__/demo-extend.test.ts | 3 + components/back-top/__tests__/demo.test.tsx | 3 + components/back-top/__tests__/index.test.tsx | 55 ++++ components/back-top/demo/basic.md | 7 + components/back-top/demo/basic.tsx | 11 + components/back-top/index.tsx | 130 ++++++++++ components/back-top/style/index.ts | 158 ++++++++++++ .../__snapshots__/components.test.tsx.snap | 238 ++++++++++++++++++ .../__tests__/components.test.tsx | 4 + components/float-button/BackTop.tsx | 2 +- components/index.ts | 2 + components/input/Input.tsx | 4 +- components/theme/interface/components.ts | 2 + package.json | 4 +- tests/__snapshots__/index.test.ts.snap | 1 + 20 files changed, 714 insertions(+), 5 deletions(-) create mode 100644 components/back-top/__tests__/__snapshots__/demo-extend.test.ts.snap create mode 100644 components/back-top/__tests__/__snapshots__/demo.test.tsx.snap create mode 100644 components/back-top/__tests__/__snapshots__/index.test.tsx.snap create mode 100644 components/back-top/__tests__/a11y.test.ts create mode 100644 components/back-top/__tests__/demo-extend.test.ts create mode 100644 components/back-top/__tests__/demo.test.tsx create mode 100644 components/back-top/__tests__/index.test.tsx create mode 100644 components/back-top/demo/basic.md create mode 100644 components/back-top/demo/basic.tsx create mode 100644 components/back-top/index.tsx create mode 100644 components/back-top/style/index.ts diff --git a/components/__tests__/__snapshots__/index.test.ts.snap b/components/__tests__/__snapshots__/index.test.ts.snap index 24753185e6..1cfdd99af9 100644 --- a/components/__tests__/__snapshots__/index.test.ts.snap +++ b/components/__tests__/__snapshots__/index.test.ts.snap @@ -8,6 +8,7 @@ exports[`antd exports modules correctly 1`] = ` "App", "AutoComplete", "Avatar", + "BackTop", "Badge", "Breadcrumb", "Button", diff --git a/components/back-top/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/back-top/__tests__/__snapshots__/demo-extend.test.ts.snap new file mode 100644 index 0000000000..201dd876ab --- /dev/null +++ b/components/back-top/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`renders components/back-top/demo/basic.tsx extend context correctly 1`] = ` +Array [ +
+
+
+ + + +
+
+
, + Scroll down to see the bottom-right., +] +`; + +exports[`renders components/back-top/demo/basic.tsx extend context correctly 2`] = ` +[ + "Warning: [antd: BackTop] \`BackTop\` is deprecated. Please use \`FloatButton.BackTop\` instead.", +] +`; diff --git a/components/back-top/__tests__/__snapshots__/demo.test.tsx.snap b/components/back-top/__tests__/__snapshots__/demo.test.tsx.snap new file mode 100644 index 0000000000..c6f401d2fa --- /dev/null +++ b/components/back-top/__tests__/__snapshots__/demo.test.tsx.snap @@ -0,0 +1,38 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`renders components/back-top/demo/basic.tsx correctly 1`] = ` +Array [ +
+
+
+ + + +
+
+
, + Scroll down to see the bottom-right., +] +`; diff --git a/components/back-top/__tests__/__snapshots__/index.test.tsx.snap b/components/back-top/__tests__/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000..737aeebc9b --- /dev/null +++ b/components/back-top/__tests__/__snapshots__/index.test.tsx.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`BackTop rtl render component should be rendered correctly in RTL direction 1`] = ` +
+`; diff --git a/components/back-top/__tests__/a11y.test.ts b/components/back-top/__tests__/a11y.test.ts new file mode 100644 index 0000000000..a8da556386 --- /dev/null +++ b/components/back-top/__tests__/a11y.test.ts @@ -0,0 +1,5 @@ +import accessibilityDemoTest from '../../../tests/shared/accessibilityTest'; + +describe('back-top demo a11y', () => { + accessibilityDemoTest('back-top'); +}); diff --git a/components/back-top/__tests__/demo-extend.test.ts b/components/back-top/__tests__/demo-extend.test.ts new file mode 100644 index 0000000000..99a80bdf61 --- /dev/null +++ b/components/back-top/__tests__/demo-extend.test.ts @@ -0,0 +1,3 @@ +import { extendTest } from '../../../tests/shared/demoTest'; + +extendTest('back-top'); diff --git a/components/back-top/__tests__/demo.test.tsx b/components/back-top/__tests__/demo.test.tsx new file mode 100644 index 0000000000..0c60d8fb26 --- /dev/null +++ b/components/back-top/__tests__/demo.test.tsx @@ -0,0 +1,3 @@ +import demoTest from '../../../tests/shared/demoTest'; + +demoTest('back-top'); diff --git a/components/back-top/__tests__/index.test.tsx b/components/back-top/__tests__/index.test.tsx new file mode 100644 index 0000000000..7a959b4573 --- /dev/null +++ b/components/back-top/__tests__/index.test.tsx @@ -0,0 +1,55 @@ +import React from 'react'; + +import BackTop from '..'; +import mountTest from '../../../tests/shared/mountTest'; +import rtlTest from '../../../tests/shared/rtlTest'; +import { fireEvent, render, waitFakeTimer } from '../../../tests/utils'; + +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 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-back-top')!); + await waitFakeTimer(); + expect(document.documentElement.scrollTop).toBe(0); + scrollToSpy.mockRestore(); + }); + + it('support onClick', () => { + const onClick = jest.fn(); + const { container } = render(); + fireEvent.click(container.querySelector('.ant-back-top')!); + expect(onClick).toHaveBeenCalled(); + }); + + it('invalid target', () => { + const onClick = jest.fn(); + const { container } = render(); + fireEvent.click(container.querySelector('.ant-back-top')!); + expect(onClick).toHaveBeenCalled(); + }); + it('should console Error', () => { + const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + render(); + expect(errSpy).toHaveBeenCalledWith( + 'Warning: [antd: BackTop] `BackTop` is deprecated. Please use `FloatButton.BackTop` instead.', + ); + errSpy.mockRestore(); + }); +}); diff --git a/components/back-top/demo/basic.md b/components/back-top/demo/basic.md new file mode 100644 index 0000000000..2ec4364007 --- /dev/null +++ b/components/back-top/demo/basic.md @@ -0,0 +1,7 @@ +## zh-CN + +最简单的用法。(已废弃) + +## en-US + +The most basic usage. (Deprecated) diff --git a/components/back-top/demo/basic.tsx b/components/back-top/demo/basic.tsx new file mode 100644 index 0000000000..cccdc532b9 --- /dev/null +++ b/components/back-top/demo/basic.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { BackTop } from 'antd'; + +const Demo = () => ( + <> + + Scroll down to see the bottom-right. + +); + +export default Demo; diff --git a/components/back-top/index.tsx b/components/back-top/index.tsx new file mode 100644 index 0000000000..3e1cae2152 --- /dev/null +++ b/components/back-top/index.tsx @@ -0,0 +1,130 @@ +import React from 'react'; +import VerticalAlignTopOutlined from '@ant-design/icons/VerticalAlignTopOutlined'; +import CSSMotion from '@rc-component/motion'; +import omit from '@rc-component/util/lib/omit'; +import { clsx } from 'clsx'; + +import getScroll from '../_util/getScroll'; +import { cloneElement } from '../_util/reactNode'; +import scrollTo from '../_util/scrollTo'; +import throttleByAnimationFrame from '../_util/throttleByAnimationFrame'; +import { devUseWarning } from '../_util/warning'; +import type { ConfigConsumerProps } from '../config-provider'; +import { ConfigContext } from '../config-provider'; +import useCSSVarCls from '../config-provider/hooks/useCSSVarCls'; +import useStyle from './style'; + +export interface BackTopProps { + visibilityHeight?: number; + onClick?: React.MouseEventHandler; + target?: () => HTMLElement | Window | Document; + prefixCls?: string; + className?: string; + rootClassName?: string; + style?: React.CSSProperties; + duration?: number; +} + +const BackTop: React.FC> = (props) => { + const { + prefixCls: customizePrefixCls, + className, + rootClassName, + visibilityHeight = 400, + target, + onClick, + duration = 450, + children, + } = props; + + const [visible, setVisible] = React.useState(visibilityHeight === 0); + + const ref = React.useRef(null); + + const getDefaultTarget = () => ref.current?.ownerDocument || window; + + const handleScroll = throttleByAnimationFrame( + (e: React.UIEvent | { target: any }) => { + const scrollTop = getScroll(e.target); + setVisible(scrollTop >= visibilityHeight); + }, + ); + + if (process.env.NODE_ENV !== 'production') { + const warning = devUseWarning('BackTop'); + warning.deprecated(false, 'BackTop', 'FloatButton.BackTop'); + } + + React.useEffect(() => { + const getTarget = target || getDefaultTarget; + const container = getTarget(); + handleScroll({ target: container }); + container?.addEventListener('scroll', handleScroll); + return () => { + handleScroll.cancel(); + container?.removeEventListener('scroll', handleScroll); + }; + }, [target]); + + const scrollToTop = (e: React.MouseEvent) => { + scrollTo(0, { getContainer: target || getDefaultTarget, duration }); + onClick?.(e); + }; + + const { getPrefixCls, direction } = React.useContext(ConfigContext); + + const prefixCls = getPrefixCls('back-top', customizePrefixCls); + + const rootPrefixCls = getPrefixCls(); + + const rootCls = useCSSVarCls(prefixCls); + + const [hashId, cssVarCls] = useStyle(prefixCls, rootCls); + + const classString = clsx( + hashId, + cssVarCls, + prefixCls, + { + [`${prefixCls}-rtl`]: direction === 'rtl', + }, + className, + rootClassName, + ); + + // fix https://fb.me/react-unknown-prop + const divProps = omit(props, [ + 'prefixCls', + 'className', + 'rootClassName', + 'children', + 'visibilityHeight', + 'target', + ]); + + const defaultElement = ( +
+
+ +
+
+ ); + + return ( +
+ + {({ className: motionClassName }) => + cloneElement(children || defaultElement, ({ className: cloneCls }) => ({ + className: clsx(motionClassName, cloneCls), + })) + } + +
+ ); +}; + +if (process.env.NODE_ENV !== 'production') { + BackTop.displayName = 'BackTop'; +} + +export default BackTop; diff --git a/components/back-top/style/index.ts b/components/back-top/style/index.ts new file mode 100644 index 0000000000..33ac631c8a --- /dev/null +++ b/components/back-top/style/index.ts @@ -0,0 +1,158 @@ +import { unit } from '@ant-design/cssinjs'; +import type { CSSObject } from '@ant-design/cssinjs'; + +import { resetComponent } from '../../style'; +import type { FullToken, GenerateStyle, GetDefaultToken } from '../../theme/internal'; +import { genStyleHooks, mergeToken } from '../../theme/internal'; + +/** Component only token. Which will handle additional calculation of alias token */ +export interface ComponentToken { + /** + * @desc 弹出层的 z-index + * @descEN z-index of popup + */ + zIndexPopup: number; +} + +type BackTopToken = FullToken<'BackTop'> & { + /** + * @desc BackTop 背景颜色 + * @descEN Background color of BackTop + */ + backTopBackground: string; + /** + * @desc BackTop 文字颜色 + * @descEN Text color of BackTop + */ + backTopColor: string; + /** + * @desc BackTop 悬停背景颜色 + * @descEN Hover background color of BackTop + */ + backTopHoverBackground: string; + /** + * @desc BackTop 字体大小 + * @descEN Font size of BackTop + */ + backTopFontSize: number; + /** + * @desc BackTop 尺寸 + * @descEN Size of BackTop + */ + backTopSize: number; + + // Position + /** + * @desc BackTop 底部偏移量 + * @descEN Bottom offset of BackTop + */ + backTopBlockEnd: number | string; + /** + * @desc BackTop 右侧偏移量 + * @descEN Right offset of BackTop + */ + backTopInlineEnd: number | string; + /** + * @desc BackTop 中等屏幕右侧偏移量 + * @descEN Right offset of BackTop on medium screens + */ + backTopInlineEndMD: number | string; + /** + * @desc BackTop 小屏幕右侧偏移量 + * @descEN Right offset of BackTop on small screens + */ + backTopInlineEndXS: number | string; +}; + +// ============================== Shared ============================== +const genSharedBackTopStyle: GenerateStyle = (token): CSSObject => { + const { componentCls, backTopFontSize, backTopSize, zIndexPopup } = token; + + return { + [componentCls]: { + ...resetComponent(token), + + position: 'fixed', + insetInlineEnd: token.backTopInlineEnd, + insetBlockEnd: token.backTopBlockEnd, + zIndex: zIndexPopup, + width: 40, + height: 40, + cursor: 'pointer', + + '&:empty': { + display: 'none', + }, + + [`${componentCls}-content`]: { + width: backTopSize, + height: backTopSize, + overflow: 'hidden', + color: token.backTopColor, + textAlign: 'center', + backgroundColor: token.backTopBackground, + borderRadius: backTopSize, + transition: `all ${token.motionDurationMid}`, + + '&:hover': { + backgroundColor: token.backTopHoverBackground, + transition: `all ${token.motionDurationMid}`, + }, + }, + + // change to .backtop .backtop-icon + [`${componentCls}-icon`]: { + fontSize: backTopFontSize, + lineHeight: unit(backTopSize), + }, + }, + }; +}; + +const genMediaBackTopStyle: GenerateStyle = (token): CSSObject => { + const { componentCls, screenMD, screenXS, backTopInlineEndMD, backTopInlineEndXS } = token; + return { + [`@media (max-width: ${unit(screenMD)})`]: { + [componentCls]: { + insetInlineEnd: backTopInlineEndMD, + }, + }, + [`@media (max-width: ${unit(screenXS)})`]: { + [componentCls]: { + insetInlineEnd: backTopInlineEndXS, + }, + }, + }; +}; + +export const prepareComponentToken: GetDefaultToken<'BackTop'> = (token) => ({ + zIndexPopup: token.zIndexBase + 10, +}); + +// ============================== Export ============================== +export default genStyleHooks( + 'BackTop', + (token) => { + const { + fontSizeHeading3, + colorTextDescription, + colorTextLightSolid, + colorText, + controlHeightLG, + calc, + } = token; + const backTopToken = mergeToken(token, { + backTopBackground: colorTextDescription, + backTopColor: colorTextLightSolid, + backTopHoverBackground: colorText, + backTopFontSize: fontSizeHeading3, + backTopSize: controlHeightLG, + backTopBlockEnd: calc(controlHeightLG).mul(1.25).equal(), + backTopInlineEnd: calc(controlHeightLG).mul(2.5).equal(), + backTopInlineEndMD: calc(controlHeightLG).mul(1.5).equal(), + backTopInlineEndXS: calc(controlHeightLG).mul(0.5).equal(), + }); + return [genSharedBackTopStyle(backTopToken), genMediaBackTopStyle(backTopToken)]; + }, + prepareComponentToken, +); diff --git a/components/config-provider/__tests__/__snapshots__/components.test.tsx.snap b/components/config-provider/__tests__/__snapshots__/components.test.tsx.snap index dfa414e813..76cce1b813 100644 --- a/components/config-provider/__tests__/__snapshots__/components.test.tsx.snap +++ b/components/config-provider/__tests__/__snapshots__/components.test.tsx.snap @@ -638,6 +638,244 @@ exports[`ConfigProvider components Avatar prefixCls 1`] = ` `; +exports[`ConfigProvider components BackTop configProvider 1`] = ` +
+
+
+ + + +
+
+
+`; + +exports[`ConfigProvider components BackTop configProvider componentDisabled 1`] = ` +
+
+
+ + + +
+
+
+`; + +exports[`ConfigProvider components BackTop configProvider componentSize large 1`] = ` +
+
+
+ + + +
+
+
+`; + +exports[`ConfigProvider components BackTop configProvider componentSize middle 1`] = ` +
+
+
+ + + +
+
+
+`; + +exports[`ConfigProvider components BackTop configProvider componentSize small 1`] = ` +
+
+
+ + + +
+
+
+`; + +exports[`ConfigProvider components BackTop normal 1`] = ` +
+
+
+ + + +
+
+
+`; + +exports[`ConfigProvider components BackTop prefixCls 1`] = ` +
+
+
+ + + +
+
+
+`; + exports[`ConfigProvider components Badge configProvider 1`] = `
{ // Avatar testPair('Avatar', (props) => ); + // BackTop + testPair('BackTop', (props) => ); + // Badge testPair('Badge', (props) => { const newProps = { ...props }; diff --git a/components/float-button/BackTop.tsx b/components/float-button/BackTop.tsx index 3db02af6e5..ab4a97855b 100644 --- a/components/float-button/BackTop.tsx +++ b/components/float-button/BackTop.tsx @@ -112,7 +112,7 @@ const BackTop = React.forwardRef((props, ref) => { }); if (process.env.NODE_ENV !== 'production') { - BackTop.displayName = 'BackTop'; + BackTop.displayName = 'FloatButton.BackTop'; } export default BackTop; diff --git a/components/index.ts b/components/index.ts index 0e730c9af1..d9d4d50a1b 100644 --- a/components/index.ts +++ b/components/index.ts @@ -12,6 +12,8 @@ export { default as AutoComplete } from './auto-complete'; export type { AutoCompleteProps } from './auto-complete'; export { default as Avatar } from './avatar'; export type { AvatarProps } from './avatar'; +export { default as BackTop } from './back-top'; +export type { BackTopProps } from './back-top'; export { default as Badge } from './badge'; export type { BadgeProps } from './badge'; export { default as Breadcrumb } from './breadcrumb'; diff --git a/components/input/Input.tsx b/components/input/Input.tsx index 2e47f924e5..03728857d6 100644 --- a/components/input/Input.tsx +++ b/components/input/Input.tsx @@ -1,8 +1,8 @@ import React, { forwardRef, useContext, useEffect, useRef } from 'react'; import type { InputRef, InputProps as RcInputProps } from '@rc-component/input'; import RcInput from '@rc-component/input'; -import { triggerFocus } from '@rc-component/input/lib/utils/commonUtils'; -import type { InputFocusOptions } from '@rc-component/input/lib/utils/commonUtils'; +import { triggerFocus } from '@rc-component/util/lib/Dom/focus'; +import type { InputFocusOptions } from '@rc-component/util/lib/Dom/focus'; import { composeRef } from '@rc-component/util/lib/ref'; import { clsx } from 'clsx'; diff --git a/components/theme/interface/components.ts b/components/theme/interface/components.ts index 1a2e76bc99..4b31be36ed 100644 --- a/components/theme/interface/components.ts +++ b/components/theme/interface/components.ts @@ -4,6 +4,7 @@ import type { ComponentToken as AlertComponentToken } from '../../alert/style'; import type { ComponentToken as AnchorComponentToken } from '../../anchor/style'; import type { ComponentToken as AppComponentToken } from '../../app/style'; import type { ComponentToken as AvatarComponentToken } from '../../avatar/style'; +import type { ComponentToken as BackTopComponentToken } from '../../back-top/style'; import type { ComponentToken as BadgeComponentToken } from '../../badge/style'; import type { ComponentToken as BreadcrumbComponentToken } from '../../breadcrumb/style'; import type { ComponentToken as ButtonComponentToken } from '../../button/style'; @@ -70,6 +71,7 @@ export interface ComponentTokenMap { Alert?: AlertComponentToken; Anchor?: AnchorComponentToken; Avatar?: AvatarComponentToken; + BackTop?: BackTopComponentToken; Badge?: BadgeComponentToken; Button?: ButtonComponentToken; Breadcrumb?: BreadcrumbComponentToken; diff --git a/package.json b/package.json index f6bd322e00..4aadf67592 100644 --- a/package.json +++ b/package.json @@ -125,7 +125,7 @@ "@rc-component/form": "~1.4.0", "@rc-component/image": "~1.5.1", "@rc-component/input": "~1.1.0", - "@rc-component/input-number": "~1.5.0", + "@rc-component/input-number": "~1.5.1", "@rc-component/mentions": "~1.5.5", "@rc-component/menu": "~1.1.4", "@rc-component/motion": "~1.1.4", @@ -151,7 +151,7 @@ "@rc-component/tree-select": "~1.3.0", "@rc-component/trigger": "^3.6.15", "@rc-component/upload": "~1.1.0", - "@rc-component/util": "^1.3.1", + "@rc-component/util": "^1.4.0", "clsx": "^2.1.1", "dayjs": "^1.11.11", "scroll-into-view-if-needed": "^3.1.0", diff --git a/tests/__snapshots__/index.test.ts.snap b/tests/__snapshots__/index.test.ts.snap index ccf24ac5b5..f70b653e7b 100644 --- a/tests/__snapshots__/index.test.ts.snap +++ b/tests/__snapshots__/index.test.ts.snap @@ -8,6 +8,7 @@ exports[`antd dist files exports modules correctly 1`] = ` "App", "AutoComplete", "Avatar", + "BackTop", "Badge", "Breadcrumb", "Button",