diff --git a/.dumi/hooks/useMenu.tsx b/.dumi/hooks/useMenu.tsx index e0a66b317a..154875ff99 100644 --- a/.dumi/hooks/useMenu.tsx +++ b/.dumi/hooks/useMenu.tsx @@ -1,10 +1,10 @@ -import type { ReactNode } from 'react'; -import React, { useMemo } from 'react'; import type { MenuProps } from 'antd'; -import { useFullSidebarData, useSidebarData } from 'dumi'; import { Tag, theme } from 'antd'; -import useLocation from './useLocation'; +import { useFullSidebarData, useSidebarData } from 'dumi'; +import type { ReactNode } from 'react'; +import { useMemo } from 'react'; import Link from '../theme/common/Link'; +import useLocation from './useLocation'; export type UseMenuOptions = { before?: ReactNode; diff --git a/.dumi/theme/SiteThemeProvider.tsx b/.dumi/theme/SiteThemeProvider.tsx index f342aaab10..da43b7aac4 100644 --- a/.dumi/theme/SiteThemeProvider.tsx +++ b/.dumi/theme/SiteThemeProvider.tsx @@ -1,18 +1,24 @@ -import type { FC } from 'react'; -import React, { useContext } from 'react'; -import { ConfigContext } from 'antd/es/config-provider'; +import { ConfigProvider, theme as antdTheme } from 'antd'; import type { ThemeProviderProps } from 'antd-style'; import { ThemeProvider } from 'antd-style'; -import { theme } from 'antd'; +import type { FC } from 'react'; +import React, { useContext } from 'react'; -const SiteThemeProvider: FC = ({ children, ...rest }) => { - const { getPrefixCls, iconPrefixCls } = useContext(ConfigContext); +const SiteThemeProvider: FC = ({ children, theme, ...rest }) => { + const { getPrefixCls, iconPrefixCls } = useContext(ConfigProvider.ConfigContext); const rootPrefixCls = getPrefixCls(); - const { token } = theme.useToken(); + const { token } = antdTheme.useToken(); + + React.useEffect(() => { + ConfigProvider.config({ + theme, + }); + }, [theme]); return ( { }); }); - describe('getDataOrAriaProps', () => { - it('returns all data-* properties from an object', () => { - const props = { - onClick: () => {}, - isOpen: true, - 'data-test': 'test-id', - 'data-id': 1234, - }; - const results = getDataOrAriaProps(props); - expect(results).toEqual({ - 'data-test': 'test-id', - 'data-id': 1234, - }); - }); - - it('does not return data-__ properties from an object', () => { - const props = { - onClick: () => {}, - isOpen: true, - 'data-__test': 'test-id', - 'data-__id': 1234, - }; - const results = getDataOrAriaProps(props); - expect(results).toEqual({}); - }); - - it('returns all aria-* properties from an object', () => { - const props = { - onClick: () => {}, - isOpen: true, - 'aria-labelledby': 'label-id', - 'aria-label': 'some-label', - }; - const results = getDataOrAriaProps(props); - expect(results).toEqual({ - 'aria-labelledby': 'label-id', - 'aria-label': 'some-label', - }); - }); - - it('returns role property from an object', () => { - const props = { - onClick: () => {}, - isOpen: true, - role: 'search', - }; - const results = getDataOrAriaProps(props); - expect(results).toEqual({ role: 'search' }); - }); - }); - describe('TransButton', () => { it('can be focus/blur', () => { const ref = React.createRef(); diff --git a/components/_util/getDataOrAriaProps.ts b/components/_util/getDataOrAriaProps.ts deleted file mode 100644 index 1984714db6..0000000000 --- a/components/_util/getDataOrAriaProps.ts +++ /dev/null @@ -1,11 +0,0 @@ -export default function getDataOrAriaProps(props: any) { - return Object.keys(props).reduce((prev: any, key: string) => { - if ( - (key.startsWith('data-') || key.startsWith('aria-') || key === 'role') && - !key.startsWith('data-__') - ) { - prev[key] = props[key]; - } - return prev; - }, {}); -} diff --git a/components/alert/index.tsx b/components/alert/index.tsx index e5cbedd01b..1af120f207 100644 --- a/components/alert/index.tsx +++ b/components/alert/index.tsx @@ -5,11 +5,11 @@ import ExclamationCircleFilled from '@ant-design/icons/ExclamationCircleFilled'; import InfoCircleFilled from '@ant-design/icons/InfoCircleFilled'; import classNames from 'classnames'; import CSSMotion from 'rc-motion'; +import pickAttrs from 'rc-util/lib/pickAttrs'; import type { ReactElement } from 'react'; import * as React from 'react'; -import { ConfigContext } from '../config-provider'; -import getDataOrAriaProps from '../_util/getDataOrAriaProps'; import { replaceElement } from '../_util/reactNode'; +import { ConfigContext } from '../config-provider'; import ErrorBoundary from './ErrorBoundary'; // CSSINJS @@ -117,7 +117,7 @@ const Alert: CompoundedComponent = ({ }) => { const [closed, setClosed] = React.useState(false); - const ref = React.useRef(); + const ref = React.useRef(null); const { getPrefixCls, direction } = React.useContext(ConfigContext); const prefixCls = getPrefixCls('alert', customizePrefixCls); const [wrapSSR, hashId] = useStyle(prefixCls); @@ -157,7 +157,10 @@ const Alert: CompoundedComponent = ({ hashId, ); - const dataOrAriaProps = getDataOrAriaProps(props); + const dataOrAriaProps = pickAttrs(props, { + aria: true, + data: true, + }); return wrapSSR( `; +exports[`renders components/anchor/demo/component-token.tsx extend context correctly 1`] = ` +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+`; + exports[`renders components/anchor/demo/customizeHighlight.tsx extend context correctly 1`] = `
`; +exports[`renders components/anchor/demo/component-token.tsx correctly 1`] = ` +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+`; + exports[`renders components/anchor/demo/customizeHighlight.tsx correctly 1`] = `
( + + + +
+
+
+ + + + + + +); diff --git a/components/anchor/index.en-US.md b/components/anchor/index.en-US.md index 3469aa1326..31b1d67444 100644 --- a/components/anchor/index.en-US.md +++ b/components/anchor/index.en-US.md @@ -30,6 +30,7 @@ For displaying anchor hyperlinks on page and jumping between them. Set Anchor scroll offset Listening for anchor link change Deprecated JSX demo +Component Token ## API diff --git a/components/anchor/index.zh-CN.md b/components/anchor/index.zh-CN.md index 2a393c4bb6..bed0635ec4 100644 --- a/components/anchor/index.zh-CN.md +++ b/components/anchor/index.zh-CN.md @@ -31,6 +31,7 @@ group: 设置锚点滚动偏移量 监听锚点链接改变 废弃的 JSX 示例 +组件 Token ## API diff --git a/components/anchor/style/index.ts b/components/anchor/style/index.ts index 4727387fca..cbaa7934a2 100644 --- a/components/anchor/style/index.ts +++ b/components/anchor/style/index.ts @@ -1,15 +1,16 @@ import type { CSSObject } from '@ant-design/cssinjs'; +import { resetComponent, textEllipsis } from '../../style'; import type { FullToken, GenerateStyle } from '../../theme/internal'; import { genComponentStyleHook, mergeToken } from '../../theme/internal'; -import { resetComponent, textEllipsis } from '../../style'; -export interface ComponentToken {} +export interface ComponentToken { + linkPaddingBlock: number; + linkPaddingInlineStart: number; +} interface AnchorToken extends FullToken<'Anchor'> { holderOffsetBlock: number; - anchorPaddingBlock: number; anchorPaddingBlockSecondary: number; - anchorPaddingInline: number; anchorBallSize: number; anchorTitleBlock: number; } @@ -34,16 +35,14 @@ const genSharedAnchorStyle: GenerateStyle = (token): CSSObject => { // delete overflow: auto // overflow: 'auto', - backgroundColor: 'transparent', - [componentCls]: { ...resetComponent(token), position: 'relative', paddingInlineStart: lineWidthBold, [`${componentCls}-link`]: { - paddingBlock: token.anchorPaddingBlock, - paddingInline: `${token.anchorPaddingInline}px 0`, + paddingBlock: token.linkPaddingBlock, + paddingInline: `${token.linkPaddingInlineStart}px 0`, '&-title': { ...textEllipsis, @@ -152,16 +151,21 @@ const genSharedAnchorHorizontalStyle: GenerateStyle = (token): CSSO }; // ============================== Export ============================== -export default genComponentStyleHook('Anchor', (token) => { - const { fontSize, fontSizeLG, padding, paddingXXS } = token; +export default genComponentStyleHook( + 'Anchor', + (token) => { + const { fontSize, fontSizeLG, paddingXXS } = token; - const anchorToken = mergeToken(token, { - holderOffsetBlock: paddingXXS, - anchorPaddingBlock: paddingXXS, - anchorPaddingBlockSecondary: paddingXXS / 2, - anchorPaddingInline: padding, - anchorTitleBlock: (fontSize / 14) * 3, - anchorBallSize: fontSizeLG / 2, - }); - return [genSharedAnchorStyle(anchorToken), genSharedAnchorHorizontalStyle(anchorToken)]; -}); + const anchorToken = mergeToken(token, { + holderOffsetBlock: paddingXXS, + anchorPaddingBlockSecondary: paddingXXS / 2, + anchorTitleBlock: (fontSize / 14) * 3, + anchorBallSize: fontSizeLG / 2, + }); + return [genSharedAnchorStyle(anchorToken), genSharedAnchorHorizontalStyle(anchorToken)]; + }, + (token) => ({ + linkPaddingBlock: token.paddingXXS, + linkPaddingInlineStart: token.padding, + }), +); diff --git a/components/avatar/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/avatar/__tests__/__snapshots__/demo-extend.test.ts.snap index 7787432769..bb5f9548b8 100644 --- a/components/avatar/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/avatar/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -342,6 +342,243 @@ exports[`renders components/avatar/demo/basic.tsx extend context correctly 1`] =
`; +exports[`renders components/avatar/demo/component-token.tsx extend context correctly 1`] = ` +Array [ +
+
+ + + +
+
, +
+
+
+ + + + + + K + + + + + +2 + + +
+
+
+ +
+
+
+
, +
+
+ + + + + + + + + + 1 + + + + +
+
+ + + + + + + + +
+
, +] +`; + exports[`renders components/avatar/demo/dynamic.tsx extend context correctly 1`] = ` Array [ `; +exports[`renders components/avatar/demo/component-token.tsx correctly 1`] = ` +Array [ +
+
+ + + +
+
, +
+
+
+ + + + + + K + + + + + +2 + + +
+
+
, +
+
+ + + + + + + + + + 1 + + + + +
+
+ + + + + + + + +
+
, +] +`; + exports[`renders components/avatar/demo/dynamic.tsx correctly 1`] = ` Array [ ( + + + + A + + + + + + K + + } /> + + } /> + + + + + } /> + + + } /> + + + +); + +export default App; diff --git a/components/avatar/index.en-US.md b/components/avatar/index.en-US.md index 6c20f7b7bd..123026e0d1 100644 --- a/components/avatar/index.en-US.md +++ b/components/avatar/index.en-US.md @@ -23,6 +23,7 @@ Avatars can be used to represent people or objects. It supports images, `Icon`s, Calculate text style when hiding Responsive Size Fallback +Component Token ## API diff --git a/components/avatar/index.zh-CN.md b/components/avatar/index.zh-CN.md index b6bccedbf3..84660b407c 100644 --- a/components/avatar/index.zh-CN.md +++ b/components/avatar/index.zh-CN.md @@ -28,6 +28,7 @@ group: 隐藏情况下计算字符对齐 响应式尺寸 图片不存在时 +组件 Token ## API diff --git a/components/avatar/style/index.ts b/components/avatar/style/index.ts index f9cd790101..e096bf5ab0 100644 --- a/components/avatar/style/index.ts +++ b/components/avatar/style/index.ts @@ -1,23 +1,24 @@ import type { CSSObject } from '@ant-design/cssinjs'; +import { resetComponent } from '../../style'; import type { FullToken, GenerateStyle } from '../../theme/internal'; import { genComponentStyleHook, mergeToken } from '../../theme/internal'; -import { resetComponent } from '../../style'; -export interface ComponentToken {} +export interface ComponentToken { + containerSize: number; + containerSizeLG: number; + containerSizeSM: number; + textFontSize: number; + textFontSizeLG: number; + textFontSizeSM: number; + groupSpace: number; + groupOverlapping: number; + groupBorderColor: string; +} type AvatarToken = FullToken<'Avatar'> & { + avatarBgColor: string; avatarBg: string; avatarColor: string; - avatarSizeBase: number; - avatarSizeLG: number; - avatarSizeSM: number; - avatarFontSizeBase: number; - avatarFontSizeLG: number; - avatarFontSizeSM: number; - avatarGroupOverlapping: number; - avatarGroupSpace: number; - avatarGroupBorderColor: string; - avatarBgColor: string; }; const genBaseStyle: GenerateStyle = (token) => { @@ -27,12 +28,12 @@ const genBaseStyle: GenerateStyle = (token) => { iconCls, avatarBg, avatarColor, - avatarSizeBase, - avatarSizeLG, - avatarSizeSM, - avatarFontSizeBase, - avatarFontSizeLG, - avatarFontSizeSM, + containerSize, + containerSizeLG, + containerSizeSM, + textFontSize, + textFontSizeLG, + textFontSizeSM, borderRadius, borderRadiusLG, borderRadiusSM, @@ -89,14 +90,14 @@ const genBaseStyle: GenerateStyle = (token) => { display: 'block', }, - ...avatarSizeStyle(avatarSizeBase, avatarFontSizeBase, borderRadius), + ...avatarSizeStyle(containerSize, textFontSize, borderRadius), [`&-lg`]: { - ...avatarSizeStyle(avatarSizeLG, avatarFontSizeLG, borderRadiusLG), + ...avatarSizeStyle(containerSizeLG, textFontSizeLG, borderRadiusLG), }, [`&-sm`]: { - ...avatarSizeStyle(avatarSizeSM, avatarFontSizeSM, borderRadiusSM), + ...avatarSizeStyle(containerSizeSM, textFontSizeSM, borderRadiusSM), }, '> img': { @@ -110,55 +111,65 @@ const genBaseStyle: GenerateStyle = (token) => { }; const genGroupStyle: GenerateStyle = (token) => { - const { componentCls, avatarGroupBorderColor, avatarGroupSpace } = token; + const { componentCls, groupBorderColor, groupOverlapping, groupSpace } = token; return { [`${componentCls}-group`]: { display: 'inline-flex', [`${componentCls}`]: { - borderColor: avatarGroupBorderColor, + borderColor: groupBorderColor, }, [`> *:not(:first-child)`]: { - marginInlineStart: avatarGroupSpace, + marginInlineStart: groupOverlapping, + }, + }, + [`${componentCls}-group-popover`]: { + [`${componentCls} + ${componentCls}`]: { + marginInlineStart: groupSpace, }, }, }; }; -export default genComponentStyleHook('Avatar', (token) => { - const { - colorTextLightSolid, +export default genComponentStyleHook( + 'Avatar', + (token) => { + const { colorTextLightSolid, colorTextPlaceholder } = token; + const avatarToken = mergeToken(token, { + avatarBg: colorTextPlaceholder, + avatarColor: colorTextLightSolid, + }); + return [genBaseStyle(avatarToken), genGroupStyle(avatarToken)]; + }, + (token) => { + const { + controlHeight, + controlHeightLG, + controlHeightSM, - controlHeight, - controlHeightLG, - controlHeightSM, + fontSize, + fontSizeLG, + fontSizeXL, + fontSizeHeading3, - fontSize, - fontSizeLG, - fontSizeXL, - fontSizeHeading3, + marginXS, + marginXXS, + colorBorderBg, + } = token; + return { + containerSize: controlHeight, + containerSizeLG: controlHeightLG, + containerSizeSM: controlHeightSM, - marginXS, - colorBorderBg, - colorTextPlaceholder, - } = token; + textFontSize: Math.round((fontSizeLG + fontSizeXL) / 2), + textFontSizeLG: fontSizeHeading3, + textFontSizeSM: fontSize, - const avatarToken = mergeToken(token, { - avatarBg: colorTextPlaceholder, - avatarColor: colorTextLightSolid, - - avatarSizeBase: controlHeight, - avatarSizeLG: controlHeightLG, - avatarSizeSM: controlHeightSM, - - avatarFontSizeBase: Math.round((fontSizeLG + fontSizeXL) / 2), - avatarFontSizeLG: fontSizeHeading3, - avatarFontSizeSM: fontSize, - avatarGroupSpace: -marginXS, - avatarGroupBorderColor: colorBorderBg, - }); - - return [genBaseStyle(avatarToken), genGroupStyle(avatarToken)]; -}); + groupSpace: marginXXS, + groupOverlapping: -marginXS, + groupBorderColor: colorBorderBg, + }; + }, +); diff --git a/components/breadcrumb/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/breadcrumb/__tests__/__snapshots__/demo-extend.test.ts.snap index 80de3decff..c91b2f2c4f 100644 --- a/components/breadcrumb/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/breadcrumb/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -63,6 +63,333 @@ exports[`renders components/breadcrumb/demo/basic.tsx extend context correctly 1 `; +exports[`renders components/breadcrumb/demo/component-token.tsx extend context correctly 1`] = ` + +`; + exports[`renders components/breadcrumb/demo/debug-routes.tsx extend context correctly 1`] = `
`; +exports[`renders components/calendar/demo/component-token.tsx extend context correctly 1`] = ` +Array [ +
+
+
+
+ + + + + 2016 + +
+
+
+
+
+ 2006 +
+
+ 2007 +
+
+
+
+
+
+
+
+ 2006 +
+
+
+
+ 2007 +
+
+
+
+ 2008 +
+
+
+
+ 2009 +
+
+
+
+ 2010 +
+
+
+
+ 2011 +
+
+
+
+ 2012 +
+
+
+
+ 2013 +
+
+
+
+ 2014 +
+
+
+
+ 2015 +
+
+
+
+ 2016 +
+
+
+
+ 2017 +
+
+
+
+ 2018 +
+
+
+
+ 2019 +
+
+
+
+ 2020 +
+
+
+
+ 2021 +
+
+
+
+ 2022 +
+
+
+
+ 2023 +
+
+
+
+ 2024 +
+
+
+
+ 2025 +
+
+
+
+
+
+
+
+ +
+
+
+ + + + + Nov + +
+
+
+
+
+ 0 +
+
+ 1 +
+
+
+
+
+
+
+
+ Jan +
+
+
+
+ Feb +
+
+
+
+ Mar +
+
+
+
+ Apr +
+
+
+
+ May +
+
+
+
+ Jun +
+
+
+
+ Jul +
+
+
+
+ Aug +
+
+
+
+ Sep +
+
+
+
+ Oct +
+
+
+
+ Nov +
+
+
+
+ Dec +
+
+
+
+
+
+
+
+ +
+
+ + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Su + + Mo + + Tu + + We + + Th + + Fr + + Sa +
+
+
+ 30 +
+
+
+
+
+
+ 31 +
+
+
+
+
+
+ 01 +
+
+
+
+
+
+ 02 +
+
+
+
+
+
+ 03 +
+
+
+
+
+
+ 04 +
+
+
+
+
+
+ 05 +
+
+
+
+
+
+ 06 +
+
+
+
+
+
+ 07 +
+
+
+
+
+
+ 08 +
+
+
+
+
+
+ 09 +
+
+
+
+
+
+ 10 +
+
+
+
+
+
+ 11 +
+
+
+
+
+
+ 12 +
+
+
+
+
+
+ 13 +
+
+
+
+
+
+ 14 +
+
+
+
+
+
+ 15 +
+
+
+
+
+
+ 16 +
+
+
+
+
+
+ 17 +
+
+
+
+
+
+ 18 +
+
+
+
+
+
+ 19 +
+
+
+
+
+
+ 20 +
+
+
+
+
+
+ 21 +
+
+
+
+
+
+ 22 +
+
+
+
+
+
+ 23 +
+
+
+
+
+
+ 24 +
+
+
+
+
+
+ 25 +
+
+
+
+
+
+ 26 +
+
+
+
+
+
+ 27 +
+
+
+
+
+
+ 28 +
+
+
+
+
+
+ 29 +
+
+
+
+
+
+ 30 +
+
+
+
+
+
+ 01 +
+
+
+
+
+
+ 02 +
+
+
+
+
+
+ 03 +
+
+
+
+
+
+ 04 +
+
+
+
+
+
+ 05 +
+
+
+
+
+
+ 06 +
+
+
+
+
+
+ 07 +
+
+
+
+
+
+ 08 +
+
+
+
+
+
+ 09 +
+
+
+
+
+
+ 10 +
+
+
+
+
+
+
+
, +
, +
+
+
+
+ + + + + 2016 + +
+
+
+
+
+ 2006 +
+
+ 2007 +
+
+
+
+
+
+
+
+ 2006 +
+
+
+
+ 2007 +
+
+
+
+ 2008 +
+
+
+
+ 2009 +
+
+
+
+ 2010 +
+
+
+
+ 2011 +
+
+
+
+ 2012 +
+
+
+
+ 2013 +
+
+
+
+ 2014 +
+
+
+
+ 2015 +
+
+
+
+ 2016 +
+
+
+
+ 2017 +
+
+
+
+ 2018 +
+
+
+
+ 2019 +
+
+
+
+ 2020 +
+
+
+
+ 2021 +
+
+
+
+ 2022 +
+
+
+
+ 2023 +
+
+
+
+ 2024 +
+
+
+
+ 2025 +
+
+
+
+
+
+
+
+ +
+
+
+ + + + + Nov + +
+
+
+
+
+ 0 +
+
+ 1 +
+
+
+
+
+
+
+
+ Jan +
+
+
+
+ Feb +
+
+
+
+ Mar +
+
+
+
+ Apr +
+
+
+
+ May +
+
+
+
+ Jun +
+
+
+
+ Jul +
+
+
+
+ Aug +
+
+
+
+ Sep +
+
+
+
+ Oct +
+
+
+
+ Nov +
+
+
+
+ Dec +
+
+
+
+
+
+
+
+ +
+
+ + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Su + + Mo + + Tu + + We + + Th + + Fr + + Sa +
+
+
+ 30 +
+
+
+
+
+
+ 31 +
+
+
+
+
+
+ 01 +
+
+
+
+
+
+ 02 +
+
+
+
+
+
+ 03 +
+
+
+
+
+
+ 04 +
+
+
+
+
+
+ 05 +
+
+
+
+
+
+ 06 +
+
+
+
+
+
+ 07 +
+
+
+
+
+
+ 08 +
+
+
+
+
+
+ 09 +
+
+
+
+
+
+ 10 +
+
+
+
+
+
+ 11 +
+
+
+
+
+
+ 12 +
+
+
+
+
+
+ 13 +
+
+
+
+
+
+ 14 +
+
+
+
+
+
+ 15 +
+
+
+
+
+
+ 16 +
+
+
+
+
+
+ 17 +
+
+
+
+
+
+ 18 +
+
+
+
+
+
+ 19 +
+
+
+
+
+
+ 20 +
+
+
+
+
+
+ 21 +
+
+
+
+
+
+ 22 +
+
+
+
+
+
+ 23 +
+
+
+
+
+
+ 24 +
+
+
+
+
+
+ 25 +
+
+
+
+
+
+ 26 +
+
+
+
+
+
+ 27 +
+
+
+
+
+
+ 28 +
+
+
+
+
+
+ 29 +
+
+
+
+
+
+ 30 +
+
+
+
+
+
+ 01 +
+
+
+
+
+
+ 02 +
+
+
+
+
+
+ 03 +
+
+
+
+
+
+ 04 +
+
+
+
+
+
+ 05 +
+
+
+
+
+
+ 06 +
+
+
+
+
+
+ 07 +
+
+
+
+
+
+ 08 +
+
+
+
+
+
+ 09 +
+
+
+
+
+
+ 10 +
+
+
+
+
+
+
+
, +] +`; + exports[`renders components/calendar/demo/customize-header.tsx extend context correctly 1`] = `
`; +exports[`renders components/calendar/demo/component-token.tsx correctly 1`] = ` +Array [ +
+
+
+
+ + + + + 2016 + +
+ +
+
+
+ + + + + Nov + +
+ +
+
+ + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Su + + Mo + + Tu + + We + + Th + + Fr + + Sa +
+
+
+ 30 +
+
+
+
+
+
+ 31 +
+
+
+
+
+
+ 01 +
+
+
+
+
+
+ 02 +
+
+
+
+
+
+ 03 +
+
+
+
+
+
+ 04 +
+
+
+
+
+
+ 05 +
+
+
+
+
+
+ 06 +
+
+
+
+
+
+ 07 +
+
+
+
+
+
+ 08 +
+
+
+
+
+
+ 09 +
+
+
+
+
+
+ 10 +
+
+
+
+
+
+ 11 +
+
+
+
+
+
+ 12 +
+
+
+
+
+
+ 13 +
+
+
+
+
+
+ 14 +
+
+
+
+
+
+ 15 +
+
+
+
+
+
+ 16 +
+
+
+
+
+
+ 17 +
+
+
+
+
+
+ 18 +
+
+
+
+
+
+ 19 +
+
+
+
+
+
+ 20 +
+
+
+
+
+
+ 21 +
+
+
+
+
+
+ 22 +
+
+
+
+
+
+ 23 +
+
+
+
+
+
+ 24 +
+
+
+
+
+
+ 25 +
+
+
+
+
+
+ 26 +
+
+
+
+
+
+ 27 +
+
+
+
+
+
+ 28 +
+
+
+
+
+
+ 29 +
+
+
+
+
+
+ 30 +
+
+
+
+
+
+ 01 +
+
+
+
+
+
+ 02 +
+
+
+
+
+
+ 03 +
+
+
+
+
+
+ 04 +
+
+
+
+
+
+ 05 +
+
+
+
+
+
+ 06 +
+
+
+
+
+
+ 07 +
+
+
+
+
+
+ 08 +
+
+
+
+
+
+ 09 +
+
+
+
+
+
+ 10 +
+
+
+
+
+
+
+
, +
, +
+
+
+
+ + + + + 2016 + +
+ +
+
+
+ + + + + Nov + +
+ +
+
+ + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Su + + Mo + + Tu + + We + + Th + + Fr + + Sa +
+
+
+ 30 +
+
+
+
+
+
+ 31 +
+
+
+
+
+
+ 01 +
+
+
+
+
+
+ 02 +
+
+
+
+
+
+ 03 +
+
+
+
+
+
+ 04 +
+
+
+
+
+
+ 05 +
+
+
+
+
+
+ 06 +
+
+
+
+
+
+ 07 +
+
+
+
+
+
+ 08 +
+
+
+
+
+
+ 09 +
+
+
+
+
+
+ 10 +
+
+
+
+
+
+ 11 +
+
+
+
+
+
+ 12 +
+
+
+
+
+
+ 13 +
+
+
+
+
+
+ 14 +
+
+
+
+
+
+ 15 +
+
+
+
+
+
+ 16 +
+
+
+
+
+
+ 17 +
+
+
+
+
+
+ 18 +
+
+
+
+
+
+ 19 +
+
+
+
+
+
+ 20 +
+
+
+
+
+
+ 21 +
+
+
+
+
+
+ 22 +
+
+
+
+
+
+ 23 +
+
+
+
+
+
+ 24 +
+
+
+
+
+
+ 25 +
+
+
+
+
+
+ 26 +
+
+
+
+
+
+ 27 +
+
+
+
+
+
+ 28 +
+
+
+
+
+
+ 29 +
+
+
+
+
+
+ 30 +
+
+
+
+
+
+ 01 +
+
+
+
+
+
+ 02 +
+
+
+
+
+
+ 03 +
+
+
+
+
+
+ 04 +
+
+
+
+
+
+ 05 +
+
+
+
+
+
+ 06 +
+
+
+
+
+
+ 07 +
+
+
+
+
+
+ 08 +
+
+
+
+
+
+ 09 +
+
+
+
+
+
+ 10 +
+
+
+
+
+
+
+
, +] +`; + exports[`renders components/calendar/demo/customize-header.tsx correctly 1`] = `
{ const { container } = render(); fireEvent.click(container.querySelector('.ant-picker-cell')!); - expect(onSelect).toHaveBeenCalledWith(expect.anything()); + expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'date' }); const value = onSelect.mock.calls[0][0]; expect(Dayjs.isDayjs(value)).toBe(true); @@ -270,7 +270,7 @@ describe('Calendar', () => { const end = Dayjs('2019-11-01'); const onValueChange = jest.fn(); createWrapper(start, end, value, onValueChange); - expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(3)); + expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(3), 'year'); }); it('if start.month > value.month, set value.month to start.month', () => { @@ -279,7 +279,7 @@ describe('Calendar', () => { const end = Dayjs('2019-03-01'); const onValueChange = jest.fn(); createWrapper(start, end, value, onValueChange); - expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(10)); + expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(10), 'year'); }); it('if change year and month > end month, set value.month to end.month', () => { @@ -302,7 +302,7 @@ describe('Calendar', () => { fireEvent.click( Array.from(wrapper.container.querySelectorAll('.ant-select-item-option')).at(-1)!, ); - expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(2)); + expect(onValueChange).toHaveBeenCalledWith(value.year(2019).month(2), 'year'); }); it('onMonthChange should work correctly', () => { @@ -324,7 +324,7 @@ describe('Calendar', () => { ); openSelect(wrapper.container, '.ant-picker-calendar-month-select'); clickSelectItem(wrapper.container); - expect(onValueChange).toHaveBeenCalledWith(value.month(10)); + expect(onValueChange).toHaveBeenCalledWith(value.month(10), 'month'); }); it('onTypeChange should work correctly', () => { diff --git a/components/calendar/__tests__/select.test.tsx b/components/calendar/__tests__/select.test.tsx new file mode 100644 index 0000000000..6996a50e4d --- /dev/null +++ b/components/calendar/__tests__/select.test.tsx @@ -0,0 +1,99 @@ +import Dayjs from 'dayjs'; +import 'dayjs/locale/zh-cn'; +import MockDate from 'mockdate'; +import { resetWarned } from 'rc-util/lib/warning'; +import React from 'react'; +import Calendar from '..'; +import { fireEvent, render, waitFakeTimer } from '../../../tests/utils'; + +describe('Calendar.onSelect', () => { + beforeAll(() => { + MockDate.set(Dayjs('2000-01-01').valueOf()); + }); + + beforeEach(() => { + resetWarned(); + jest.useFakeTimers(); + jest.clearAllTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('source of year select', async () => { + const onSelect = jest.fn(); + const { container } = render(); + + fireEvent.mouseDown(container.querySelector('.ant-select-selector')!); + await waitFakeTimer(); + + fireEvent.click(container.querySelector('.ant-select-item-option')!); + await waitFakeTimer(); + + expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'year' }); + }); + + it('source of month select', async () => { + const onSelect = jest.fn(); + const { container } = render(); + + fireEvent.mouseDown(container.querySelectorAll('.ant-select-selector')[1]!); + await waitFakeTimer(); + + fireEvent.click(container.querySelector('.ant-select-item-option')!); + await waitFakeTimer(); + + expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'month' }); + }); + + it('source of customize', async () => { + const onSelect = jest.fn(); + const { container } = render( + ( + + )} + />, + ); + + fireEvent.click(container.querySelector('.bamboo')!); + await waitFakeTimer(); + + expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'customize' }); + }); + + it('source of date', () => { + const onSelect = jest.fn(); + const { container } = render(); + + fireEvent.click(container.querySelector('.ant-picker-cell')!); + expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'date' }); + }); + + it('source of date with month panel', async () => { + const onSelect = jest.fn(); + const onPanelChange = jest.fn(); + const { container } = render(); + + // Default is month radio + fireEvent.click(container.querySelector('.ant-picker-cell')!); + expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'date' }); + + // Click year radio + fireEvent.click(container.querySelectorAll('.ant-radio-button-input')[1]!); + expect(onPanelChange).toHaveBeenCalledWith(expect.anything(), 'year'); + + fireEvent.click(container.querySelector('.ant-picker-cell')!); + expect(onSelect).toHaveBeenCalledWith(expect.anything(), { source: 'month' }); + }); +}); diff --git a/components/calendar/demo/component-token.md b/components/calendar/demo/component-token.md new file mode 100644 index 0000000000..de91480d0a --- /dev/null +++ b/components/calendar/demo/component-token.md @@ -0,0 +1,7 @@ +## zh-CN + +Component Token Debug. + +## en-US + +Component Token Debug. diff --git a/components/calendar/demo/component-token.tsx b/components/calendar/demo/component-token.tsx new file mode 100644 index 0000000000..ee816d1143 --- /dev/null +++ b/components/calendar/demo/component-token.tsx @@ -0,0 +1,29 @@ +import { Calendar, ConfigProvider } from 'antd'; +import type { CalendarMode } from 'antd/es/calendar/generateCalendar'; +import type { Dayjs } from 'dayjs'; +import React from 'react'; + +/** Test usage. Do not use in your production. */ +export default () => { + const onPanelChange = (value: Dayjs, mode: CalendarMode) => { + console.log(value.format('YYYY-MM-DD'), mode); + }; + + return ( + + +
+ +
+ ); +}; diff --git a/components/calendar/generateCalendar.tsx b/components/calendar/generateCalendar.tsx index 278a91253f..f53530d254 100644 --- a/components/calendar/generateCalendar.tsx +++ b/components/calendar/generateCalendar.tsx @@ -1,12 +1,12 @@ import classNames from 'classnames'; import { PickerPanel as RCPickerPanel } from 'rc-picker'; -import type { GenerateConfig } from 'rc-picker/lib/generate'; -import type { CellRenderInfo } from 'rc-picker/lib/interface'; import type { PickerPanelBaseProps as RCPickerPanelBaseProps, PickerPanelDateProps as RCPickerPanelDateProps, PickerPanelTimeProps as RCPickerPanelTimeProps, } from 'rc-picker/lib/PickerPanel'; +import type { GenerateConfig } from 'rc-picker/lib/generate'; +import type { CellRenderInfo } from 'rc-picker/lib/interface'; import useMergedState from 'rc-util/lib/hooks/useMergedState'; import * as React from 'react'; import { ConfigContext } from '../config-provider'; @@ -14,8 +14,8 @@ import { useLocale } from '../locale'; import CalendarHeader from './Header'; import enUS from './locale/en_US'; -import useStyle from './style'; import warning from '../_util/warning'; +import useStyle from './style'; type InjectDefaultProps = Omit< Props, @@ -43,6 +43,10 @@ export type HeaderRender = (config: { onTypeChange: (type: CalendarMode) => void; }) => React.ReactNode; +export interface SelectInfo { + source: 'year' | 'month' | 'date' | 'customize'; +} + export interface CalendarProps { prefixCls?: string; className?: string; @@ -68,7 +72,7 @@ export interface CalendarProps { fullscreen?: boolean; onChange?: (date: DateType) => void; onPanelChange?: (date: DateType, mode: CalendarMode) => void; - onSelect?: (date: DateType) => void; + onSelect?: (date: DateType, selectInfo: SelectInfo) => void; } function generateCalendar(generateConfig: GenerateConfig) { @@ -198,10 +202,10 @@ function generateCalendar(generateConfig: GenerateConfig) { triggerPanelChange(mergedValue, newMode); }; - const onInternalSelect = (date: DateType) => { + const onInternalSelect = (date: DateType, source: SelectInfo['source']) => { triggerChange(date); - onSelect?.(date); + onSelect?.(date, { source }); }; // ====================== Locale ====================== @@ -310,7 +314,9 @@ function generateCalendar(generateConfig: GenerateConfig) { headerRender({ value: mergedValue, type: mergedMode, - onChange: onInternalSelect, + onChange: (nextDate) => { + onInternalSelect(nextDate, 'customize'); + }, onTypeChange: triggerModeChange, }) ) : ( @@ -332,7 +338,9 @@ function generateCalendar(generateConfig: GenerateConfig) { locale={contextLocale?.lang} generateConfig={generateConfig} cellRender={mergedCellRender} - onSelect={onInternalSelect} + onSelect={(nextDate) => { + onInternalSelect(nextDate, panelMode); + }} mode={panelMode} picker={panelMode} disabledDate={mergedDisabledDate} diff --git a/components/calendar/index.en-US.md b/components/calendar/index.en-US.md index 5a534c9c00..5286131b7e 100644 --- a/components/calendar/index.en-US.md +++ b/components/calendar/index.en-US.md @@ -20,6 +20,7 @@ When data is in the form of dates, such as schedules, timetables, prices calenda Card Selectable Calendar Customize Header +Component Token ## API @@ -55,7 +56,7 @@ When data is in the form of dates, such as schedules, timetables, prices calenda | value | The current selected date | [dayjs](https://day.js.org/) | - | | | onChange | Callback for when date changes | function(date: Dayjs) | - | | | onPanelChange | Callback for when panel changes | function(date: Dayjs, mode: string) | - | | -| onSelect | Callback for when a date is selected | function(date: Dayjs) | - | | +| onSelect | Callback for when a date is selected, include source info | function(date: Dayjs, info: { source: 'year' \| 'month' \| 'date' \| 'customize' }) | - | `info`: 5.6.0 | ## Design Token @@ -74,3 +75,17 @@ See [How to set locale for date-related components](/components/date-picker/#loc ### Date-related components locale is not working? See FAQ [Date-related-components-locale-is-not-working?](/docs/react/faq#date-related-components-locale-is-not-working) + +### How to get date from panel click? + +`onSelect` provide `info.source` to help on this: + +```tsx + { + if (source === 'date') { + console.log('Panel Select:', source); + } + }} +/> +``` diff --git a/components/calendar/index.zh-CN.md b/components/calendar/index.zh-CN.md index 56039fdb92..158ff6c113 100644 --- a/components/calendar/index.zh-CN.md +++ b/components/calendar/index.zh-CN.md @@ -21,6 +21,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*-p-wQLik200AAA 卡片模式 选择功能 自定义头部 +组件 Token ## API @@ -60,7 +61,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*-p-wQLik200AAA | value | 展示日期 | [dayjs](https://day.js.org/) | - | | | onChange | 日期变化回调 | function(date: Dayjs) | - | | | onPanelChange | 日期面板变化回调 | function(date: Dayjs, mode: string) | - | | -| onSelect | 点击选择日期回调 | function(date: Dayjs) | - | | +| onSelect | 选择日期回调,包含来源信息 | function(date: Dayjs, info: { source: 'year' \| 'month' \| 'date' \| 'customize' }) | - | `info`: 5.6.0 | ## Design Token @@ -79,3 +80,17 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*-p-wQLik200AAA ### 为什么时间类组件的国际化 locale 设置不生效? 参考 FAQ [为什么时间类组件的国际化 locale 设置不生效?](/docs/react/faq#为什么时间类组件的国际化-locale-设置不生效)。 + +### 如何仅获取来自面板点击的日期? + +`onSelect` 事件提供额外的来源信息,你可以通过 `info.source` 来判断来源: + +```tsx + { + if (source === 'date') { + console.log('Panel Select:', source); + } + }} +/> +``` diff --git a/components/calendar/style/index.ts b/components/calendar/style/index.ts index 33b406d664..dca20ff1d4 100644 --- a/components/calendar/style/index.ts +++ b/components/calendar/style/index.ts @@ -1,9 +1,9 @@ import type { CSSObject } from '@ant-design/cssinjs'; -import { resetComponent } from '../../style'; import type { PickerPanelToken } from '../../date-picker/style'; import { genPanelStyle, initPickerPanelToken } from '../../date-picker/style'; import type { InputToken } from '../../input/style'; import { initInputToken } from '../../input/style'; +import { resetComponent } from '../../style'; import type { FullToken } from '../../theme/internal'; import { genComponentStyleHook, mergeToken } from '../../theme/internal'; @@ -11,26 +11,25 @@ export interface ComponentToken { yearControlWidth: number; monthControlWidth: number; miniContentHeight: number; + fullBg: string; + fullPanelBg: string; + itemActiveBg: string; } interface CalendarToken extends InputToken>, PickerPanelToken { calendarCls: string; - calendarFullBg: string; - calendarFullPanelBg: string; - calendarItemActiveBg: string; dateValueHeight: number; weekHeight: number; dateContentHeight: number; } export const genCalendarStyles = (token: CalendarToken): CSSObject => { - const { calendarCls, componentCls, calendarFullBg, calendarFullPanelBg, calendarItemActiveBg } = - token; + const { calendarCls, componentCls, fullBg, fullPanelBg, itemActiveBg } = token; return { [calendarCls]: { ...genPanelStyle(token), ...resetComponent(token), - background: calendarFullBg, + background: fullBg, '&-rtl': { direction: 'rtl', }, @@ -52,7 +51,7 @@ export const genCalendarStyles = (token: CalendarToken): CSSObject => { }, }, [`${calendarCls} ${componentCls}-panel`]: { - background: calendarFullPanelBg, + background: fullPanelBg, border: 0, borderTop: `${token.lineWidth}px ${token.lineType} ${token.colorSplit}`, borderRadius: 0, @@ -92,7 +91,7 @@ export const genCalendarStyles = (token: CalendarToken): CSSObject => { display: 'block', width: '100%', textAlign: 'end', - background: calendarFullBg, + background: fullBg, border: 0, [`${componentCls}-body`]: { 'th, td': { @@ -121,7 +120,7 @@ export const genCalendarStyles = (token: CalendarToken): CSSObject => { // >>> Selected [`&-in-view${componentCls}-cell-selected`]: { [`${calendarCls}-date, ${calendarCls}-date-today`]: { - background: calendarItemActiveBg, + background: itemActiveBg, }, }, '&-selected, &-selected:hover': { @@ -198,9 +197,6 @@ export default genComponentStyleHook( { calendarCls, pickerCellInnerCls: `${token.componentCls}-cell-inner`, - calendarFullBg: token.colorBgContainer, - calendarFullPanelBg: token.colorBgContainer, - calendarItemActiveBg: token.controlItemBgActive, dateValueHeight: token.controlHeightSM, weekHeight: token.controlHeightSM * 0.75, dateContentHeight: @@ -210,9 +206,12 @@ export default genComponentStyleHook( return [genCalendarStyles(calendarToken)]; }, - { + (token) => ({ + fullBg: token.colorBgContainer, + fullPanelBg: token.colorBgContainer, + itemActiveBg: token.controlItemBgActive, yearControlWidth: 80, monthControlWidth: 70, miniContentHeight: 256, - }, + }), ); diff --git a/components/card/Card.tsx b/components/card/Card.tsx index fe416d5167..fd0a63ee06 100644 --- a/components/card/Card.tsx +++ b/components/card/Card.tsx @@ -1,4 +1,5 @@ import classNames from 'classnames'; +import type { Tab } from 'rc-tabs/lib/interface'; import omit from 'rc-util/lib/omit'; import * as React from 'react'; import { ConfigContext } from '../config-provider'; @@ -12,10 +13,11 @@ import useStyle from './style'; export type CardType = 'inner'; export type CardSize = 'default' | 'small'; -export interface CardTabListType { +export interface CardTabListType extends Omit { key: string; - tab: React.ReactNode; - disabled?: boolean; + /** @deprecated Please use `label` instead */ + tab?: React.ReactNode; + label?: React.ReactNode; } export interface CardProps extends Omit, 'title'> { @@ -123,10 +125,9 @@ const Card = React.forwardRef((props, ref) => { {...extraProps} className={`${prefixCls}-head-tabs`} onChange={onTabChange} - items={tabList.map((item) => ({ - label: item.tab, - key: item.key, - disabled: item.disabled ?? false, + items={tabList.map(({ tab, ...item }) => ({ + label: tab, + ...item, }))} /> ) : null; diff --git a/components/card/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/card/__tests__/__snapshots__/demo-extend.test.ts.snap index e2846da4c8..ceffcc67cf 100644 --- a/components/card/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/card/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -130,6 +130,284 @@ exports[`renders components/card/demo/border-less.tsx extend context correctly 1
`; +exports[`renders components/card/demo/component-token.tsx extend context correctly 1`] = ` +Array [ +
+
+
+
+ Card title +
+
+ More +
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+ +
+
    + +
+
+
+
+
+
+
+
+
+
+

+ Card content +

+

+ Card content +

+

+ Card content +

+
+
    +
  • + + + + + +
  • +
  • + + + + + +
  • +
  • + + + + + +
  • +
+
, +
, +] +`; + exports[`renders components/card/demo/flexible-content.tsx extend context correctly 1`] = `
`; +exports[`renders components/card/demo/component-token.tsx correctly 1`] = ` +Array [ +
+
+
+
+ Card title +
+
+ More +
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+

+ Card content +

+

+ Card content +

+

+ Card content +

+
+
    +
  • + + + + + +
  • +
  • + + + + + +
  • +
  • + + + + + +
  • +
+
, +
+
+
+
+ Small size card +
+ +
+
+
+

+ Card content +

+

+ Card content +

+

+ Card content +

+
+
, +] +`; + exports[`renders components/card/demo/flexible-content.tsx correctly 1`] = `