Merge branch 'feature' into feat/collapse-icon-vertical-position

This commit is contained in:
afc163 2025-01-21 14:41:40 +08:00 committed by GitHub
commit ca90069956
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
641 changed files with 29849 additions and 6261 deletions

View File

@ -66,7 +66,7 @@ const useStyle = createStyles(({ token }, markPos: [number, number, number, numb
export interface SemanticPreviewProps {
semantics: { name: string; desc: string; version?: string }[];
children: React.ReactElement;
children: React.ReactElement<any>;
height?: number;
}
@ -97,7 +97,7 @@ const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
// ======================== Hover =========================
const containerRef = React.useRef<HTMLDivElement>(null);
const timerRef = React.useRef<ReturnType<typeof setTimeout>>();
const timerRef = React.useRef<ReturnType<typeof setTimeout>>(null);
const [positionMotion, setPositionMotion] = React.useState<boolean>(false);
const [hoverSemantic, setHoverSemantic] = React.useState<string | null>(null);
@ -111,12 +111,14 @@ const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
const targetElement = containerRef.current?.querySelector<HTMLElement>(`.${targetClassName}`);
const containerRect = containerRef.current?.getBoundingClientRect();
const targetRect = targetElement?.getBoundingClientRect();
setMarkPos([
(targetRect?.left || 0) - (containerRect?.left || 0),
(targetRect?.top || 0) - (containerRect?.top || 0),
targetRect?.width || 0,
targetRect?.height || 0,
]);
timerRef.current = setTimeout(() => {
setPositionMotion(true);
}, 10);

View File

@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react';
import { ColorPicker, Flex, Input } from 'antd';
import { createStyles } from 'antd-style';
import type { ColorPickerProps, GetProp } from 'antd';
import { createStyles } from 'antd-style';
import { generateColor } from 'antd/es/color-picker/util';
import classNames from 'classnames';
@ -61,7 +61,7 @@ const DebouncedColorPicker: React.FC<React.PropsWithChildren<ThemeColorPickerPro
<ColorPicker
value={value}
onChange={setValue}
presets={[{ label: 'PresetColors', colors: PRESET_COLORS }]}
presets={[{ label: 'PresetColors', key: 'PresetColors', colors: PRESET_COLORS }]}
>
{children}
</ColorPicker>

View File

@ -6,7 +6,7 @@ import {
HomeOutlined,
QuestionCircleOutlined,
} from '@ant-design/icons';
import { TinyColor } from '@ctrl/tinycolor';
import { FastColor } from '@ant-design/fast-color';
import type { ColorPickerProps, GetProp, MenuProps, ThemeConfig } from 'antd';
import {
Breadcrumb,
@ -324,7 +324,7 @@ const ThemesInfo: Record<THEME, Partial<ThemeData>> = {
const normalize = (value: number) => value / 255;
function rgbToColorMatrix(color: string) {
const rgb = new TinyColor(color).toRgb();
const rgb = new FastColor(color).toRgb();
const { r, g, b } = rgb;
const invertValue = normalize(r) * 100;

View File

@ -0,0 +1,15 @@
import * as React from 'react';
import * as all from 'antd';
interface AntdProps {
component: keyof typeof all;
}
function Antd(props: AntdProps) {
const { component, ...restProps } = props;
const Component = (all[component] ?? React.Fragment) as React.ComponentType;
return <Component {...restProps} />;
}
export default Antd;

View File

@ -1,6 +1,6 @@
import * as React from 'react';
import type { ColorInput } from '@ctrl/tinycolor';
import { TinyColor } from '@ctrl/tinycolor';
import { FastColor } from '@ant-design/fast-color';
import type { ColorInput } from '@ant-design/fast-color';
import { createStyles } from 'antd-style';
const useStyle = createStyles(({ token, css }) => ({
@ -22,7 +22,7 @@ const useStyle = createStyles(({ token, css }) => ({
}));
interface ColorChunkProps {
value?: ColorInput;
value: ColorInput;
}
const ColorChunk: React.FC<React.PropsWithChildren<ColorChunkProps>> = (props) => {
@ -30,7 +30,7 @@ const ColorChunk: React.FC<React.PropsWithChildren<ColorChunkProps>> = (props) =
const { value, children } = props;
const dotColor = React.useMemo(() => {
const _color = new TinyColor(value).toHex8String();
const _color = new FastColor(value).toHexString();
return _color.endsWith('ff') ? _color.slice(0, -2) : _color;
}, [value]);

View File

@ -154,7 +154,9 @@ const ComponentMeta: React.FC<ComponentMetaProps> = (props) => {
colon={false}
column={1}
style={{ marginTop: token.margin }}
labelStyle={{ paddingInlineEnd: token.padding, width: 56 }}
styles={{
label: { paddingInlineEnd: token.padding, width: 56 },
}}
items={
[
{

View File

@ -243,7 +243,7 @@ const Overview: React.FC = () => {
{cardContent}
</a>
) : (
<Link to={url} prefetch key={component.title}>
<Link to={url} key={component.title}>
{cardContent}
</Link>
);

View File

@ -157,7 +157,7 @@ const SubTokenTable: React.FC<SubTokenTableProps> = (props) => {
{title}
<Popover
title={null}
overlayStyle={{ width: 400 }}
styles={{ root: { width: 400 } }}
content={
<Typography>
{/* <SourceCode lang="jsx">{code}</SourceCode> */}

View File

@ -1,13 +1,14 @@
import React, { useContext } from 'react';
import React, { Suspense, useContext } from 'react';
import { BugOutlined, CodeOutlined, ExperimentOutlined } from '@ant-design/icons';
import { ConfigProvider, Tooltip, Button } from 'antd';
import classNames from 'classnames';
import { DumiDemoGrid, FormattedMessage } from 'dumi';
import { DumiDemoGrid, FormattedMessage, DumiDemo } from 'dumi';
import { css, Global } from '@emotion/react';
import useLayoutState from '../../../hooks/useLayoutState';
import useLocale from '../../../hooks/useLocale';
import DemoContext from '../../slots/DemoContext';
import DemoFallback from '../Previewer/DemoFallback';
const locales = {
cn: {
@ -113,7 +114,14 @@ const DemoWrapper: typeof DumiDemoGrid = ({ items }) => {
</Tooltip>
</span>
<ConfigProvider theme={{ cssVar: enableCssVar, hashed: !enableCssVar }}>
<DumiDemoGrid items={demos} />
<DumiDemoGrid
items={demos}
demoRender={(item) => (
<Suspense fallback={<DemoFallback />}>
<DumiDemo key={item.demo.id} {...item} />
</Suspense>
)}
/>
</ConfigProvider>
</div>
);

View File

@ -1,9 +1,9 @@
import type { CSSProperties } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import Icon, * as AntdIcons from '@ant-design/icons';
import type { SegmentedProps } from 'antd';
import { Affix, Empty, Input, Segmented } from 'antd';
import { createStyles, useTheme } from 'antd-style';
import type { SegmentedOptions } from 'antd/es/segmented';
import { useIntl } from 'dumi';
import debounce from 'lodash/debounce';
@ -28,26 +28,6 @@ const useStyle = createStyles(({ token, css }) => ({
`,
}));
const options = (
formatMessage: (values: Record<string, string>) => React.ReactNode,
): SegmentedProps['options'] => [
{
value: ThemeType.Outlined,
icon: <Icon component={OutlinedIcon} />,
label: formatMessage({ id: 'app.docs.components.icon.outlined' }),
},
{
value: ThemeType.Filled,
icon: <Icon component={FilledIcon} />,
label: formatMessage({ id: 'app.docs.components.icon.filled' }),
},
{
value: ThemeType.TwoTone,
icon: <Icon component={TwoToneIcon} />,
label: formatMessage({ id: 'app.docs.components.icon.two-tone' }),
},
];
interface IconSearchState {
theme: ThemeType;
searchKey: string;
@ -124,14 +104,35 @@ const IconSearch: React.FC = () => {
backgroundColor: colorBgContainer,
};
const memoizedOptions = React.useMemo<SegmentedOptions<ThemeType>>(
() => [
{
value: ThemeType.Outlined,
icon: <Icon component={OutlinedIcon} />,
label: intl.formatMessage({ id: 'app.docs.components.icon.outlined' }),
},
{
value: ThemeType.Filled,
icon: <Icon component={FilledIcon} />,
label: intl.formatMessage({ id: 'app.docs.components.icon.filled' }),
},
{
value: ThemeType.TwoTone,
icon: <Icon component={TwoToneIcon} />,
label: intl.formatMessage({ id: 'app.docs.components.icon.two-tone' }),
},
],
[intl],
);
return (
<div className="markdown">
<Affix offsetTop={anchorTop} onChange={setSearchBarAffixed}>
<div className={styles.iconSearchAffix} style={searchBarAffixed ? affixedStyle : {}}>
<Segmented
<Segmented<ThemeType>
size="large"
value={displayState.theme}
options={options(intl.formatMessage)}
options={memoizedOptions}
onChange={handleChangeTheme}
/>
<Input.Search

View File

@ -0,0 +1,80 @@
import React, { Suspense, useEffect, useState } from 'react';
import { Tooltip } from 'antd';
import { FormattedMessage } from 'dumi';
import { ping } from '../../utils';
let pingDeferrer: PromiseLike<boolean>;
const codeBlockJs =
'https://renderoffice.a' +
'lipay' +
'objects.com/p' +
'/yuyan/180020010001206410/parseFileData-v1.0.1.js';
function useShowCodeBlockButton() {
const [showCodeBlockButton, setShowCodeBlockButton] = useState(false);
useEffect(() => {
pingDeferrer ??= new Promise<boolean>((resolve) => {
ping((status) => {
if (status !== 'timeout' && status !== 'error') {
// Async insert `codeBlockJs` into body end
const script = document.createElement('script');
script.src = codeBlockJs;
script.async = true;
document.body.appendChild(script);
return resolve(true);
}
return resolve(false);
});
});
pingDeferrer.then(setShowCodeBlockButton);
}, []);
return showCodeBlockButton;
}
interface CodeBlockButtonProps {
title?: string;
dependencies: Record<PropertyKey, string>;
jsx: string;
}
const CodeBlockButton: React.FC<CodeBlockButtonProps> = ({ title, dependencies = {}, jsx }) => {
const showCodeBlockButton = useShowCodeBlockButton();
const codeBlockPrefillConfig = {
title: `${title} - antd@${dependencies.antd}`,
js: `${
/import React(\D*)from 'react';/.test(jsx) ? '' : `import React from 'react';\n`
}import { createRoot } from 'react-dom/client';\n${jsx.replace(
/export default/,
'const ComponentDemo =',
)}\n\ncreateRoot(mountNode).render(<ComponentDemo />);\n`,
css: '',
json: JSON.stringify({ name: 'antd-demo', dependencies }, null, 2),
};
return showCodeBlockButton ? (
<Tooltip title={<FormattedMessage id="app.demo.codeblock" />}>
<div className="code-box-code-action">
<img
alt="codeblock"
src="https://mdn.alipayobjects.com/huamei_wtld8u/afts/img/A*K8rjSJpTNQ8AAAAAAAAAAAAADhOIAQ/original"
className="code-box-codeblock"
onClick={() => {
openHituCodeBlock(JSON.stringify(codeBlockPrefillConfig));
}}
/>
</div>
</Tooltip>
) : null;
};
export default (props: CodeBlockButtonProps) => (
<Suspense>
<CodeBlockButton {...props} />
</Suspense>
);

View File

@ -16,11 +16,10 @@ import EditButton from '../../common/EditButton';
import CodePenIcon from '../../icons/CodePenIcon';
import CodeSandboxIcon from '../../icons/CodeSandboxIcon';
import ExternalLinkIcon from '../../icons/ExternalLinkIcon';
import RiddleIcon from '../../icons/RiddleIcon';
import DemoContext from '../../slots/DemoContext';
import type { SiteContextProps } from '../../slots/SiteContext';
import SiteContext from '../../slots/SiteContext';
import { ping } from '../../utils';
import CodeBlockButton from './CodeBlockButton';
import type { AntdPreviewerProps } from './Previewer';
const { ErrorBoundary } = Alert;
@ -39,27 +38,6 @@ const track = ({ type, demo }: { type: string; demo: string }) => {
window.gtag('event', 'demo', { event_category: type, event_label: demo });
};
let pingDeferrer: PromiseLike<boolean>;
function useShowRiddleButton() {
const [showRiddleButton, setShowRiddleButton] = useState(false);
useEffect(() => {
pingDeferrer ??= new Promise<boolean>((resolve) => {
ping((status) => {
if (status !== 'timeout' && status !== 'error') {
return resolve(true);
}
return resolve(false);
});
});
pingDeferrer.then(setShowRiddleButton);
}, []);
return showRiddleButton;
}
const useStyle = createStyles(({ token }) => {
const { borderRadius } = token;
return {
@ -98,7 +76,7 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
title,
description,
originDebug,
jsx,
jsx = '',
style,
compact,
background,
@ -117,7 +95,6 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
const entryName = 'index.tsx';
const entryCode = asset.dependencies[entryName].value;
const showRiddleButton = useShowRiddleButton();
const previewDemo = useRef<React.ReactNode>(null);
const demoContainer = useRef<HTMLElement>(null);
@ -127,11 +104,10 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
setSource: setLiveDemoSource,
} = useLiveDemo(asset.id, {
iframe: Boolean(iframe),
containerRef: demoContainer,
containerRef: demoContainer as React.RefObject<HTMLElement>,
});
const anchorRef = useRef<HTMLAnchorElement>(null);
const codeSandboxIconRef = useRef<HTMLFormElement>(null);
const riddleIconRef = useRef<HTMLFormElement>(null);
const codepenIconRef = useRef<HTMLFormElement>(null);
const [codeExpand, setCodeExpand] = useState<boolean>(false);
const { theme } = useContext<SiteContextProps>(SiteContext);
@ -278,18 +254,6 @@ const CodePreviewer: React.FC<AntdPreviewerProps> = (props) => {
js_pre_processor: 'typescript',
};
const riddlePrefillConfig = {
title: `${localizedTitle} - antd@${dependencies.antd}`,
js: `${
/import React(\D*)from 'react';/.test(jsx) ? '' : `import React from 'react';\n`
}import { createRoot } from 'react-dom/client';\n${jsx.replace(
/export default/,
'const ComponentDemo =',
)}\n\ncreateRoot(mountNode).render(<ComponentDemo />);\n`,
css: '',
json: JSON.stringify({ name: 'antd-demo', dependencies }, null, 2),
};
// Reorder source code
let parsedSourceCode = suffix === 'tsx' ? entryCode : jsx;
let importReactContent = "import React from 'react';";
@ -440,24 +404,7 @@ createRoot(document.getElementById('container')).render(<Demo />);
<CodeSandboxIcon className="code-box-codesandbox" />
</Tooltip>
</form>
{showRiddleButton ? (
<form
className="code-box-code-action"
action="//riddle.alibaba-inc.com/riddles/define"
method="POST"
target="_blank"
ref={riddleIconRef}
onClick={() => {
track({ type: 'riddle', demo: asset.id });
riddleIconRef.current?.submit();
}}
>
<input type="hidden" name="data" value={JSON.stringify(riddlePrefillConfig)} />
<Tooltip title={<FormattedMessage id="app.demo.riddle" />}>
<RiddleIcon className="code-box-riddle" />
</Tooltip>
</form>
) : null}
<CodeBlockButton title={localizedTitle} dependencies={dependencies} jsx={jsx} />
<Tooltip title={<FormattedMessage id="app.demo.stackblitz" />}>
<span
className="code-box-code-action"

View File

@ -0,0 +1,27 @@
import React from 'react';
import { Skeleton } from 'antd';
import { createStyles } from 'antd-style';
const useStyle = createStyles(({ token, css }) => ({
skeletonWrapper: css`
width: 100% !important;
height: 250px;
margin-bottom: ${token.margin}px;
border-radius: ${token.borderRadiusLG}px;
`,
}));
const DemoFallback = () => {
const { styles } = useStyle();
return (
<Skeleton.Node
active
className={styles.skeletonWrapper}
style={{ width: '100%', height: '100%' }}
>
{' '}
</Skeleton.Node>
);
};
export default DemoFallback;

View File

@ -7,6 +7,7 @@ import DesignPreviewer from './DesignPreviewer';
export interface AntdPreviewerProps extends IPreviewerProps {
originDebug?: IPreviewerProps['debug'];
jsx?: string;
}
const Previewer: React.FC<AntdPreviewerProps> = (props) => {

View File

@ -0,0 +1,91 @@
import React, { Suspense, useEffect, useRef, useState } from 'react';
import { Tooltip } from 'antd';
import { FormattedMessage } from 'dumi';
import type { IPreviewerProps } from 'dumi';
import RiddleIcon from '../../icons/RiddleIcon';
import { ping } from '../../utils';
let pingDeferrer: PromiseLike<boolean>;
function useShowRiddleButton() {
const [showRiddleButton, setShowRiddleButton] = useState(false);
useEffect(() => {
pingDeferrer ??= new Promise<boolean>((resolve) => {
ping((status) => {
if (status !== 'timeout' && status !== 'error') {
return resolve(true);
}
return resolve(false);
});
});
pingDeferrer.then(setShowRiddleButton);
}, []);
return showRiddleButton;
}
interface RiddleButtonProps {
title?: string;
dependencies: Record<PropertyKey, string>;
jsx: string;
track: ({
type,
demo,
}: {
type: string;
demo: string;
}) => void;
asset: IPreviewerProps['asset'];
}
const RiddleButton: React.FC<RiddleButtonProps> = ({
title,
dependencies = {},
jsx,
track,
asset,
}) => {
const riddleIconRef = useRef<HTMLFormElement>(null);
const showRiddleButton = useShowRiddleButton();
const riddlePrefillConfig = {
title: `${title} - antd@${dependencies.antd}`,
js: `${
/import React(\D*)from 'react';/.test(jsx) ? '' : `import React from 'react';\n`
}import { createRoot } from 'react-dom/client';\n${jsx.replace(
/export default/,
'const ComponentDemo =',
)}\n\ncreateRoot(mountNode).render(<ComponentDemo />);\n`,
css: '',
json: JSON.stringify({ name: 'antd-demo', dependencies }, null, 2),
};
return showRiddleButton ? (
<form
className="code-box-code-action"
action="//riddle.alibaba-inc.com/riddles/define"
method="POST"
target="_blank"
ref={riddleIconRef}
onClick={() => {
track({ type: 'riddle', demo: asset.id });
riddleIconRef.current?.submit();
}}
>
<input type="hidden" name="data" value={JSON.stringify(riddlePrefillConfig)} />
<Tooltip title={<FormattedMessage id="app.demo.riddle" />}>
<RiddleIcon className="code-box-riddle" />
</Tooltip>
</form>
) : null;
};
export default (props: RiddleButtonProps) => (
<Suspense>
<RiddleButton {...props} />
</Suspense>
);

View File

@ -1,39 +1,17 @@
import React, { Suspense } from 'react';
import { Alert, Skeleton } from 'antd';
import { createStyles } from 'antd-style';
import { Alert } from 'antd';
import Previewer from './Previewer';
import DemoFallback from './DemoFallback';
import type { IPreviewerProps } from 'dumi';
const { ErrorBoundary } = Alert;
const useStyle = createStyles(({ token, css }) => ({
skeletonWrapper: css`
width: 100% !important;
height: 250px;
margin-bottom: ${token.margin}px;
border-radius: ${token.borderRadiusLG}px;
`,
}));
const PreviewerSuspense: React.FC<IPreviewerProps> = (props) => {
const { styles } = useStyle();
return (
<ErrorBoundary>
<Suspense
fallback={
<Skeleton.Node
active
className={styles.skeletonWrapper}
style={{ width: '100%', height: '100%' }}
>
{' '}
</Skeleton.Node>
}
>
<Previewer {...props} />
</Suspense>
</ErrorBoundary>
);
};
const PreviewerSuspense: React.FC<IPreviewerProps> = (props) => (
<ErrorBoundary>
<Suspense fallback={<DemoFallback />}>
<Previewer {...props} />
</Suspense>
</ErrorBoundary>
);
export default PreviewerSuspense;

View File

@ -1,6 +1,6 @@
// 用于 color.md 中的颜色对比
import React from 'react';
import { TinyColor } from '@ctrl/tinycolor';
import { FastColor } from '@ant-design/fast-color';
import { Flex, theme } from 'antd';
import { createStyles } from 'antd-style';
import tokenMeta from 'antd/es/version/token-meta.json';
@ -55,7 +55,7 @@ const useStyle = createStyles(({ token, css }) => {
});
function color2Rgba(color: string) {
return `#${new TinyColor(color).toHex8().toUpperCase()}`;
return `#${new FastColor(color).toHexString().toUpperCase()}`;
}
interface ColorCircleProps {

View File

@ -99,7 +99,7 @@ const TokenTable: FC<TokenTableProps> = ({ type }) => {
name: token,
desc: lang === 'cn' ? meta.desc : meta.descEn,
type: meta.type,
value: defaultToken[token],
value: defaultToken[token as keyof typeof defaultToken],
})),
[type, lang],
);

View File

@ -1,7 +1,7 @@
import React, { useMemo, useState } from 'react';
import { Col, ColorPicker, Row } from 'antd';
import { FormattedMessage } from 'dumi';
import type { Color } from 'antd/es/color-picker';
import { FormattedMessage } from 'dumi';
import useLocale from '../../../hooks/useLocale';
import ColorPatterns from './ColorPatterns';
@ -34,7 +34,7 @@ const ColorPaletteTool: React.FC = () => {
setPrimaryColorInstance(color);
};
const handleChangeBackgroundColor = (_, hex: string) => {
const handleChangeBackgroundColor = (_: Color, hex: string) => {
setBackgroundColor(hex);
};

View File

@ -5,6 +5,9 @@ import CopyToClipboard from 'react-copy-to-clipboard';
const rgbToHex = (rgbString: string): string => {
const rgb = rgbString.match(/\d+/g);
if (!rgb) {
return '';
}
let r = parseInt(rgb[0], 10).toString(16);
let g = parseInt(rgb[1], 10).toString(16);
let b = parseInt(rgb[2], 10).toString(16);

View File

@ -20,6 +20,7 @@ interface ChangelogInfo {
version: string;
changelog: string;
refs: string[];
contributors: string[];
releaseDate: string;
}
@ -160,14 +161,30 @@ const ParseChangelog: React.FC<{ changelog: string }> = (props) => {
return <span>{parsedChangelog}</span>;
};
const RefLinks: React.FC<{ refs: string[] }> = ({ refs }) => {
const RefLinks: React.FC<{ refs: string[]; contributors: string[] }> = ({ refs, contributors }) => {
const { styles } = useStyle();
return (
<>
{refs?.map((ref) => (
<a className={styles.linkRef} key={ref} href={ref} target="_blank" rel="noreferrer">
#{ref.match(/^.*\/(\d+)$/)?.[1]}
</a>
<React.Fragment key={ref}>
<a className={styles.linkRef} key={ref} href={ref} target="_blank" rel="noreferrer">
#{ref.match(/[^/]+$/)?.[0]}
</a>
</React.Fragment>
))}
{contributors?.map((contributor) => (
<React.Fragment key={contributor}>
<a
className={styles.linkRef}
key={contributor}
href={`https://github.com/${contributor}`}
target="_blank"
rel="noreferrer"
>
@{contributor}
</a>
</React.Fragment>
))}
</>
);
@ -178,7 +195,7 @@ const RenderChangelogList: React.FC<{ changelogList: ChangelogInfo[] }> = ({ cha
const { styles } = useStyle();
const len = changelogList.length;
for (let i = 0; i < len; i += 1) {
const { refs, changelog } = changelogList[i];
const { refs, changelog, contributors } = changelogList[i];
// Check if the next line is an image link and append it to the current line
if (i + 1 < len && changelogList[i + 1].changelog.trim().startsWith('<img')) {
const imgDom = new DOMParser().parseFromString(changelogList[i + 1].changelog, 'text/html');
@ -186,7 +203,7 @@ const RenderChangelogList: React.FC<{ changelogList: ChangelogInfo[] }> = ({ cha
elements.push(
<li key={i}>
<ParseChangelog changelog={changelog} />
<RefLinks refs={refs} />
<RefLinks refs={refs} contributors={contributors} />
<br />
<img
src={imgElement?.getAttribute('src') || ''}
@ -200,7 +217,7 @@ const RenderChangelogList: React.FC<{ changelogList: ChangelogInfo[] }> = ({ cha
elements.push(
<li key={i}>
<ParseChangelog changelog={changelog} />
<RefLinks refs={refs} />
<RefLinks refs={refs} contributors={contributors} />
</li>,
);
}
@ -306,7 +323,7 @@ const ComponentChangelog: React.FC<Readonly<React.PropsWithChildren>> = (props)
return (
<>
{isValidElement(children) &&
cloneElement(children as React.ReactElement, {
cloneElement(children as React.ReactElement<any>, {
onClick: () => setShow(true),
})}
<Drawer

View File

@ -1,16 +1,20 @@
import React, { useEffect, useRef } from 'react';
import type { JSONEditorPropsOptional } from 'vanilla-jsoneditor';
import { JSONEditor, Mode } from 'vanilla-jsoneditor';
import type { JsonEditor, JSONEditorPropsOptional } from 'vanilla-jsoneditor';
import { createJSONEditor, Mode } from 'vanilla-jsoneditor';
const Editor: React.FC<JSONEditorPropsOptional> = (props) => {
const editorRef = useRef<JSONEditor>(null);
const editorRef = useRef<JsonEditor>();
const container = useRef<HTMLDivElement>(null);
useEffect(() => {
editorRef.current = new JSONEditor({
target: container.current,
props: { mode: Mode.text },
});
if (container.current) {
editorRef.current = createJSONEditor({
target: container.current,
props: {
mode: Mode.text,
},
});
}
return () => {
editorRef.current?.destroy();
};

View File

@ -1,7 +1,6 @@
import type { MouseEvent, MouseEventHandler } from 'react';
import React, { forwardRef, useLayoutEffect, useTransition } from 'react';
import { Link as DumiLink, useLocation, useNavigate } from 'dumi';
import nprogress from 'nprogress';
import React, { useMemo, forwardRef } from 'react';
import { Link as DumiLink, useLocation, useAppData, useNavigate } from 'dumi';
export interface LinkProps {
to: string | { pathname?: string; search?: string; hash?: string };
@ -9,64 +8,49 @@ export interface LinkProps {
className?: string;
onClick?: MouseEventHandler;
component?: React.ComponentType<any>;
children?: React.ReactNode;
}
nprogress.configure({ showSpinner: false });
const Link = forwardRef<HTMLAnchorElement, React.PropsWithChildren<LinkProps>>((props, ref) => {
const { to, children, component, ...rest } = props;
const [isPending, startTransition] = useTransition();
const navigate = useNavigate();
const { pathname } = useLocation();
const href = React.useMemo<string>(() => {
if (typeof to === 'object') {
return `${to.pathname || pathname}${to.search || ''}${to.hash || ''}`;
}
return to;
}, [to]);
const handleClick = (e: MouseEvent<HTMLAnchorElement>) => {
props.onClick?.(e);
if (!href?.startsWith('http')) {
// Should support open in new tab
if (!e.metaKey && !e.ctrlKey && !e.shiftKey) {
e.preventDefault();
startTransition(() => {
if (href) {
navigate(href);
}
});
const Link = forwardRef<HTMLAnchorElement, React.PropsWithChildren<LinkProps>>(
({ component, children, to, ...rest }, ref) => {
const { pathname } = useLocation();
const { preloadRoute } = useAppData();
const navigate = useNavigate();
const href = useMemo<string>(() => {
if (typeof to === 'object') {
return `${to.pathname || pathname}${to.search || ''}${to.hash || ''}`;
}
return to;
}, [to]);
const onClick = (e: MouseEvent<HTMLAnchorElement>) => {
rest.onClick?.(e);
if (!href?.startsWith('http')) {
// Should support open in new tab
if (!e.metaKey && !e.ctrlKey && !e.shiftKey) {
e.preventDefault();
navigate(href);
}
}
};
if (component) {
return React.createElement(
component,
{
...rest,
ref,
href,
onClick,
onMouseEnter: () => preloadRoute?.(href),
},
children,
);
}
};
useLayoutEffect(() => {
if (isPending) {
nprogress.start();
} else {
nprogress.done();
}
}, [isPending]);
if (component) {
return React.createElement(
component,
{
...rest,
ref,
onClick: handleClick,
href,
},
children,
return (
<DumiLink ref={ref} {...rest} to={href} prefetch>
{children}
</DumiLink>
);
}
return (
<DumiLink ref={ref} onClick={handleClick} {...rest} to={href} prefetch>
{children}
</DumiLink>
);
});
},
);
export default Link;

View File

@ -141,13 +141,23 @@ const PrevAndNext: React.FC<{ rtl?: boolean }> = ({ rtl }) => {
return (
<section className={styles.prevNextNav}>
{prev &&
React.cloneElement(prev.label as ReactElement, {
className: classNames(styles.pageNav, styles.prevNav, prev.className),
})}
React.cloneElement(
prev.label as ReactElement<{
className: string;
}>,
{
className: classNames(styles.pageNav, styles.prevNav, prev.className),
},
)}
{next &&
React.cloneElement(next.label as ReactElement, {
className: classNames(styles.pageNav, styles.nextNav, next.className),
})}
React.cloneElement(
next.label as ReactElement<{
className: string;
}>,
{
className: classNames(styles.pageNav, styles.nextNav, next.className),
},
)}
</section>
);
};

View File

@ -282,12 +282,13 @@ const GlobalDemoStyles: React.FC = () => {
cursor: pointer;
}
&-riddle {
width: 14px;
height: 14px;
&-codeblock {
width: 16px;
height: 16px;
overflow: hidden;
border: 0;
cursor: pointer;
max-width: 100% !important;
}
&-codesandbox {
@ -347,12 +348,12 @@ const GlobalDemoStyles: React.FC = () => {
display: flex;
align-items: center;
column-gap: ${token.marginXS}px;
}
${antCls}-btn {
opacity: 0.6;
&.icon-enabled {
background: ${token.colorFillSecondary};
opacity: 1;
${antCls}-btn {
&.icon-enabled {
background-color: ${token.colorFillSecondary};
opacity: 1;
${iconCls} {
color: ${token.colorTextBase};
font-weight: bold;

View File

@ -1,5 +1,5 @@
import React from 'react';
import { TinyColor } from '@ctrl/tinycolor';
import { FastColor } from '@ant-design/fast-color';
import { css, Global } from '@emotion/react';
import { useTheme } from 'antd-style';
@ -410,7 +410,7 @@ const GlobalStyle: React.FC = () => {
background: ${demoGridColor};
&:nth-child(2n + 1) {
background: ${new TinyColor(demoGridColor).setAlpha(0.75).toHex8String()};
background: ${new FastColor(demoGridColor).setA(0.75).toHexString()};
}
}
@ -426,12 +426,12 @@ const GlobalStyle: React.FC = () => {
}
${antCls}-row .demo-col-1 {
background: ${new TinyColor(demoGridColor).setAlpha(0.75).toHexString()};
background: ${new FastColor(demoGridColor).setA(0.75).toHexString()};
}
${antCls}-row .demo-col-2,
.code-box-demo ${antCls}-row .demo-col-2 {
background: ${new TinyColor(demoGridColor).setAlpha(0.75).toHexString()};
background: ${new FastColor(demoGridColor).setA(0.75).toHexString()};
}
${antCls}-row .demo-col-3,
@ -442,7 +442,7 @@ const GlobalStyle: React.FC = () => {
${antCls}-row .demo-col-4,
.code-box-demo ${antCls}-row .demo-col-4 {
background: ${new TinyColor(demoGridColor).setAlpha(0.6).toHexString()};
background: ${new FastColor(demoGridColor).setA(0.6).toHexString()};
}
${antCls}-row .demo-col-5,

View File

@ -37,7 +37,7 @@ const DocLayout: React.FC = () => {
const location = useLocation();
const { pathname, search, hash } = location;
const [locale, lang] = useLocale(locales);
const timerRef = useRef<ReturnType<typeof setTimeout>>();
const timerRef = useRef<ReturnType<typeof setTimeout>>(null!);
const { direction } = useContext(SiteContext);
const { loading } = useSiteData();

View File

@ -1,4 +1,5 @@
import React, { Suspense, useCallback, useEffect } from 'react';
import { Monitoring } from 'react-scan/monitoring';
import {
createCache,
extractStyle,
@ -12,7 +13,13 @@ import { getSandpackCssText } from '@codesandbox/sandpack-react';
import { theme as antdTheme, App } from 'antd';
import type { MappingAlgorithm } from 'antd';
import type { DirectionType, ThemeConfig } from 'antd/es/config-provider';
import { createSearchParams, useOutlet, useSearchParams, useServerInsertedHTML } from 'dumi';
import {
createSearchParams,
useOutlet,
useParams,
useSearchParams,
useServerInsertedHTML,
} from 'dumi';
import { DarkContext } from '../../hooks/useDark';
import useLayoutState from '../../hooks/useLayoutState';
@ -22,6 +29,8 @@ import SiteThemeProvider from '../SiteThemeProvider';
import type { SiteContextProps } from '../slots/SiteContext';
import SiteContext from '../slots/SiteContext';
import '@ant-design/v5-patch-for-react-19';
const ThemeSwitch = React.lazy(() => import('../common/ThemeSwitch'));
type Entries<T> = { [K in keyof T]: [K, T[K]] }[keyof T][];
@ -61,6 +70,7 @@ const getAlgorithm = (themes: ThemeName[] = []) =>
const GlobalLayout: React.FC = () => {
const outlet = useOutlet();
const { pathname } = useLocation();
const params = useParams();
const [searchParams, setSearchParams] = useSearchParams();
const [{ theme = [], direction, isMobile, bannerVisible = false }, setSiteState] =
useLayoutState<SiteState>({
@ -70,6 +80,9 @@ const GlobalLayout: React.FC = () => {
bannerVisible: false,
});
// TODO: This can be remove in v6
const useCssVar = searchParams.get('cssVar') !== 'false';
const updateSiteConfig = useCallback(
(props: SiteState) => {
setSiteState((prev) => ({ ...prev, ...props }));
@ -150,8 +163,8 @@ const GlobalLayout: React.FC = () => {
() => ({
algorithm: getAlgorithm(theme),
token: { motion: !theme.includes('motion-off') },
cssVar: true,
hashed: false,
cssVar: useCssVar,
hashed: !useCssVar,
}),
[theme],
);
@ -220,7 +233,17 @@ const GlobalLayout: React.FC = () => {
>
<SiteContext.Provider value={siteContextValue}>
<SiteThemeProvider theme={themeConfig}>
<HappyProvider disabled={!theme.includes('happy-work')}>{content}</HappyProvider>
<HappyProvider disabled={!theme.includes('happy-work')}>
{content}
<Monitoring
apiKey="GhrCCNrHZHXlf4P6E03ntrFwhRLxJL30" // Safe to expose publically
url="https://monitoring.react-scan.com/api/v1/ingest"
commit={process.env.COMMIT_HASH}
branch={process.env.BRANCH}
params={params as Record<string, string>}
path={pathname}
/>
</HappyProvider>
</SiteThemeProvider>
</SiteContext.Provider>
</StyleProvider>

View File

@ -35,7 +35,7 @@
"app.demo.codepen": "Open in CodePen",
"app.demo.codesandbox": "Open in CodeSandbox",
"app.demo.stackblitz": "Open in Stackblitz",
"app.demo.riddle": "Open in Riddle",
"app.demo.codeblock": "Open in Hitu",
"app.demo.separate": "Open in a new window",
"app.demo.online": "Online Address",
"app.home.introduce": "A design system for enterprise-level products. Create an efficient and enjoyable work experience.",

View File

@ -35,7 +35,7 @@
"app.demo.codepen": "在 CodePen 中打开",
"app.demo.codesandbox": "在 CodeSandbox 中打开",
"app.demo.stackblitz": "在 Stackblitz 中打开",
"app.demo.riddle": "在 Riddle 中打开",
"app.demo.codeblock": "在海兔中打开",
"app.demo.separate": "在新窗口打开",
"app.demo.online": "线上地址",
"app.home.introduce": "企业级产品设计体系,创造高效愉悦的工作体验",

View File

@ -2,7 +2,6 @@ import { createHash } from 'crypto';
import fs from 'fs';
import path from 'path';
import createEmotionServer from '@emotion/server/create-instance';
import chalk from 'chalk';
import type { IApi, IRoute } from 'dumi';
import ReactTechStack from 'dumi/dist/techStacks/react';
import sylvanas from 'sylvanas';
@ -126,7 +125,8 @@ class AntdReactTechStack extends ReactTechStack {
const resolve = (p: string): string => require.resolve(p);
const RoutesPlugin = (api: IApi) => {
const RoutesPlugin = async (api: IApi) => {
const chalk = await import('chalk').then((m) => m.default);
// const ssrCssFileName = `ssr-${Date.now()}.css`;
const writeCSSFile = (key: string, hashKey: string, cssString: string) => {

View File

@ -8,9 +8,6 @@ import SiteContext from '../SiteContext';
import ContributorAvatar from './ContributorAvatar';
const useStyle = createStyles(({ token, css }) => ({
contributorsList: css`
margin-top: 120px !important;
`,
listMobile: css`
margin: 1em 0 !important;
`,
@ -50,7 +47,7 @@ const Contributors: React.FC<ContributorsProps> = ({ filename }) => {
}
return (
<div className={classNames(styles.contributorsList, { [styles.listMobile]: isMobile })}>
<div className={classNames({ [styles.listMobile]: isMobile })}>
<div className={styles.title}>{formatMessage({ id: 'app.content.contributors' })}</div>
<ContributorsList
cache

View File

@ -94,9 +94,11 @@ const Content: React.FC<React.PropsWithChildren> = ({ children }) => {
juejinLink={meta.frontmatter.juejin_url}
/>
</InViewSuspense>
<InViewSuspense fallback={<div style={{ height: 50, marginTop: 120 }} />}>
<Contributors filename={meta.frontmatter.filename} />
</InViewSuspense>
<div style={{ marginTop: 120 }}>
<InViewSuspense fallback={<div style={{ height: 50 }} />}>
<Contributors filename={meta.frontmatter.filename} />
</InViewSuspense>
</div>
</article>
<InViewSuspense fallback={null}>
<PrevAndNext rtl={isRTL} />

View File

@ -13,7 +13,7 @@ import {
UsergroupAddOutlined,
ZhihuOutlined,
} from '@ant-design/icons';
import { TinyColor } from '@ctrl/tinycolor';
import { FastColor } from '@ant-design/fast-color';
import { createStyles } from 'antd-style';
import getAlphaColor from 'antd/es/theme/util/getAlphaColor';
import { FormattedMessage, Link } from 'dumi';
@ -37,7 +37,7 @@ const locales = {
const useStyle = () => {
const { isMobile } = useContext(SiteContext);
return createStyles(({ token, css }) => {
const background = new TinyColor(getAlphaColor('#f0f3fa', '#fff'))
const background = new FastColor(getAlphaColor('#f0f3fa', '#fff'))
.onBackground(token.colorBgContainer)
.toHexString();
@ -101,6 +101,11 @@ const Footer: React.FC = () => {
const col1 = {
title: <FormattedMessage id="app.footer.resources" />,
items: [
{
title: 'Ant Design X',
url: isZhCN ? 'https://ant-design-x.antgroup.com' : 'https://x.ant.design',
openExternal: true,
},
{
title: 'Ant Design Charts',
url: isZhCN ? 'https://ant-design-charts.antgroup.com' : 'https://charts.ant.design',
@ -112,7 +117,7 @@ const Footer: React.FC = () => {
openExternal: true,
},
{
title: 'Ant Design Pro Components',
title: 'Pro Components',
url: 'https://procomponents.ant.design',
openExternal: true,
},
@ -126,6 +131,11 @@ const Footer: React.FC = () => {
url: isZhCN ? 'https://ant-design-mini.antgroup.com/' : 'https://mini.ant.design',
openExternal: true,
},
{
title: 'Ant Design Web3',
url: isZhCN ? 'https://web3.antdigital.dev' : 'https://web3.ant.design',
openExternal: true,
},
{
title: 'Ant Design Landing',
description: <FormattedMessage id="app.footer.landing" />,
@ -156,12 +166,6 @@ const Footer: React.FC = () => {
url: 'https://qiankun.umijs.org',
openExternal: true,
},
{
title: 'ahooks',
description: <FormattedMessage id="app.footer.hooks" />,
url: 'https://github.com/alibaba/hooks',
openExternal: true,
},
{
title: 'Ant Motion',
description: <FormattedMessage id="app.footer.motion" />,

View File

@ -357,7 +357,7 @@ const Header: React.FC = () => {
<header className={headerClassName}>
{isMobile && (
<Popover
overlayClassName={styles.popoverMenu}
classNames={{ root: styles.popoverMenu }}
placement="bottomRight"
content={menu}
trigger="click"

View File

@ -8,13 +8,14 @@ import useMenu from '../../../hooks/useMenu';
import SiteContext from '../SiteContext';
const useStyle = createStyles(({ token, css }) => {
const { antCls, fontFamily, colorSplit } = token;
const { antCls, fontFamily, colorSplit, marginXXL, paddingXXS } = token;
return {
asideContainer: css`
min-height: 100%;
padding-bottom: 48px;
padding-bottom: ${marginXXL}px !important;
font-family: Avenir, ${fontFamily}, sans-serif;
padding: 0 ${paddingXXS}px;
&${antCls}-menu-inline {
${antCls}-menu-submenu-title h4,
@ -94,14 +95,10 @@ const useStyle = createStyles(({ token, css }) => {
position: sticky;
top: ${token.headerHeight + token.contentMarginTop}px;
width: 100%;
height: 100%;
max-height: calc(100vh - ${token.headerHeight + token.contentMarginTop}px);
overflow: hidden;
scrollbar-width: thin;
scrollbar-gutter: stable;
.ant-menu {
padding: 0 4px;
}
&:hover {
overflow-y: auto;

View File

@ -9,7 +9,11 @@ import { version } from './package.json';
export default defineConfig({
plugins: ['dumi-plugin-color-chunk'],
// For <Link prefetch />
routePrefetch: {},
manifest: {},
conventionRoutes: {
// to avoid generate routes for .dumi/pages/index/components/xx
exclude: [/index\/components\//],

View File

@ -0,0 +1,86 @@
name: Issue Inactivity Reminder
on:
schedule:
- cron: "0 0 * * *" # Run at 00:00 every day
permissions:
issues: write
jobs:
reminder_job:
runs-on: ubuntu-latest
steps:
- name: Send reminders for inactive issues
uses: actions/github-script@v7
with:
script: |
const daysBeforeReminder = 14;
let page = 1;
const perPage = 100;
const reminderSignature = "This issue has been inactive for more than 14 days";
while (true) {
const { data: issues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
assignee: '*',
per_page: perPage,
page: page,
});
if (issues.length === 0) {
break;
}
const now = new Date();
for (const issue of issues) {
if (issue.pull_request) continue;
const updatedAt = new Date(issue.updated_at);
const daysInactive = (now - updatedAt) / (1000 * 60 * 60 * 24);
const { data: timeline } = await github.rest.issues.listEventsForTimeline({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
});
const hasLinkedPR = timeline.some(event =>
event.event === 'connected' || // PR connected via keywords
event.event === 'referenced' && event.commit_id // Connected via commit
);
if (daysInactive >= daysBeforeReminder && !hasLinkedPR) {
// get issue comments
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
});
// check if reminder has been sent
const hasReminder = comments.some(comment =>
comment.body.includes(reminderSignature)
);
if (!hasReminder) {
const assigneesMentions = issue.assignees
.map(user => `@${user.login}`)
.join(' ');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: `${assigneesMentions} 这个 issue 已经超过 14 天没有更新或关联 PR。如果您仍在处理这个 issue请更新进度如果您无法继续处理请联系维护者重新分配。\n\nThis issue has been inactive for more than 14 days without any updates or linked PR. If you are still working on this issue, please provide a progress update. If you are unable to continue, please contact the maintainers for reassignment.`,
});
}
}
}
page += 1;
}

View File

@ -13,7 +13,7 @@ jobs:
issues: write # for actions-cool/issues-helper to update issues
pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
if: (github.event.pull_request.head.ref == 'feature' || github.event.pull_request.head.ref == 'master') && github.event.pull_request.head.user.login == 'ant-design'
if: (github.event.pull_request.head.ref == 'next' || github.event.pull_request.head.ref == 'feature' || github.event.pull_request.head.ref == 'master') && github.event.pull_request.head.user.login == 'ant-design'
steps:
- uses: actions-cool/issues-helper@v3
with:

View File

@ -61,7 +61,7 @@ jobs:
steps:
# We need get PR id first
- name: download pr artifact
uses: dawidd6/action-download-artifact@v6
uses: dawidd6/action-download-artifact@v7
with:
workflow: ${{ github.event.workflow_run.workflow_id }}
run_id: ${{ github.event.workflow_run.id }}
@ -81,7 +81,7 @@ jobs:
# Download site artifact
- name: download site artifact
if: ${{ fromJSON(needs.upstream-workflow-summary.outputs.build-success) }}
uses: dawidd6/action-download-artifact@v6
uses: dawidd6/action-download-artifact@v7
with:
workflow: ${{ github.event.workflow_run.workflow_id }}
run_id: ${{ github.event.workflow_run.id }}

View File

@ -11,9 +11,14 @@ permissions:
contents: write
jobs:
build-and-deploy:
build-site:
runs-on: ubuntu-latest
if: (startsWith(github.ref, 'refs/tags/') && (contains(github.ref_name, '-') == false)) || github.event_name == 'workflow_dispatch'
# https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#example-defining-outputs-for-a-job
outputs:
formatted_version: ${{ steps.shared-formatted_version.outputs.VERSION }}
steps:
- name: checkout
uses: actions/checkout@v4
@ -33,15 +38,42 @@ jobs:
ANALYZER: 1
NODE_OPTIONS: --max_old_space_size=4096
- name: Get version
id: publish-version
- name: move report.html to _site
run: |
if [ -f report.html ]; then
mv report.html _site && echo "report.html moved to _site"
fi
- name: upload site artifact
uses: actions/upload-artifact@v4
with:
name: real-site
path: _site/
retention-days: 1 # Not need to keep for too long
- name: Format version
if: ${{ always() }}
id: shared-formatted_version
run: echo "VERSION=$(echo ${{ github.ref_name }} | sed 's/\./-/g')" >> $GITHUB_OUTPUT
deploy-to-pages:
runs-on: ubuntu-latest
needs: build-site
steps:
- uses: oven-sh/setup-bun@v2
- name: download site artifact
uses: actions/download-artifact@v4
with:
name: real-site
path: _site
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./_site
exclude_files: ./_site/report.html # 👈 这个功能是 beta, 但即便不排除,也不 care
force_orphan: true
# Since force_orphan will not trigger Sync to Gitee, we need to force run it here
@ -55,14 +87,37 @@ jobs:
- name: Deploy to Surge (with TAG)
run: |
export DEPLOY_DOMAIN=ant-design-${{ steps.publish-version.outputs.VERSION }}.surge.sh
cp report.html ./_site
export DEPLOY_DOMAIN=ant-design-${{ needs.build-site.outputs.formatted_version }}.surge.sh
bunx surge --project ./_site --domain $DEPLOY_DOMAIN --token ${{ secrets.SURGE_TOKEN }}
- name: Create Commit Comment
uses: peter-evans/commit-comment@v3
with:
body: |
- Documentation site for this release: https://ant-design-${{ steps.publish-version.outputs.VERSION }}.surge.sh
- Webpack bundle analyzer report page: https://ant-design-${{ steps.publish-version.outputs.VERSION }}.surge.sh/report.html
- Documentation site for this release: https://ant-design-${{ needs.build-site.outputs.formatted_version }}.surge.sh
- Webpack bundle analyzer report page: https://ant-design-${{ needs.build-site.outputs.formatted_version }}.surge.sh/report.html
# https://github.com/ant-design/ant-design/pull/49213/files#r1625446496
upload-to-release:
runs-on: ubuntu-latest
# 仅在 tag 的时候工作,因为我们要将内容发布到以 tag 为版本号的 release 里
if: startsWith(github.ref, 'refs/tags/')
needs: build-site
steps:
- name: download site artifact
uses: actions/download-artifact@v4
with:
name: real-site
path: _site
- name: Tarball site
run: |
cd ./_site
tar -czf ../website.tar.gz --transform 's|^|antd-${{ needs.build-site.outputs.formatted_version }}-website/|' .
cd ..
- name: Upload to Release
uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0
with:
fail_on_unmatched_files: true
files: website.tar.gz

233
.github/workflows/test-v6.yml vendored Normal file
View File

@ -0,0 +1,233 @@
# Origin Source
# https://github.com/ant-design/ant-design/blob/79f566b7f8abb1012ef55b0d2793bfdf5595b85d/.github/workflows/test.yml
name: ✅ test v6
on:
push:
branches: [next]
pull_request:
branches: [next]
# Cancel prev CI if new commit come
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install
- run: bun run lint
################################ Test ################################
test-react-legacy:
name: test-react-legacy
strategy:
matrix:
react: ['18']
shard: [1/2, 2/2]
env:
REACT: ${{ matrix.react }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install
- name: install react 18
if: ${{ matrix.react == '18' }}
run: bun run bun-install-react-18
# dom test
- name: dom test
run: bun run test -- --maxWorkers=2 --shard=${{matrix.shard}}
test-node:
name: test-node
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install
- run: bun run test:node
test-react-latest:
name: test-react-latest
strategy:
matrix:
module: [dom]
shard: [1/2, 2/2]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install
# dom test
- name: dom test
run: bun run test -- --maxWorkers=2 --shard=${{matrix.shard}} --coverage
- name: persist coverages
run: |
mkdir persist-coverage
mv coverage/coverage-final.json persist-coverage/react-test-${{matrix.module}}-${{strategy.job-index}}.json
- uses: actions/upload-artifact@v4
name: upload coverages
with:
name: coverage-artifacts-${{ matrix.module }}-${{ strategy.job-index }}
path: persist-coverage/
test-react-latest-dist:
name: test-react-latest-dist
strategy:
matrix:
module: [dist, dist-min]
shard: [1/2, 2/2]
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install
- name: restore cache from dist
uses: actions/cache@v4
with:
path: dist
key: dist-${{ github.sha }}
- name: dist-min test
if: ${{ matrix.module == 'dist-min' }}
run: bun run test
env:
LIB_DIR: dist-min
- name: dist test
if: ${{ matrix.module == 'dist' }}
run: bun run test
env:
LIB_DIR: dist
############################ Test Coverage ###########################
upload-test-coverage:
name: test-coverage
runs-on: ubuntu-latest
needs: test-react-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install
- uses: actions/download-artifact@v4
with:
pattern: coverage-artifacts-*
merge-multiple: true
path: persist-coverage
- name: Merge Code Coverage
run: |
bunx nyc merge persist-coverage/ coverage/coverage-final.json
bunx nyc report --reporter text -t coverage --report-dir coverage
rm -rf persist-coverage
- name: Upload coverage to codecov
uses: codecov/codecov-action@v5
with:
# use own token to upload coverage reports
token: ${{ secrets.CODECOV_TOKEN }}
########################### Compile & Test ###########################
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install
- name: cache lib
uses: actions/cache@v4
with:
path: lib
key: lib-${{ github.sha }}
- name: cache es
uses: actions/cache@v4
with:
path: es
key: es-${{ github.sha }}
- name: compile
run: bun run compile
- name: cache dist
uses: actions/cache@v4
with:
path: dist
key: dist-${{ github.sha }}
- name: dist
run: bun run dist
env:
NODE_OPTIONS: --max_old_space_size=4096
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
CI: 1
- name: check build files
run: bun run test:dekko
# Artifact build files
- uses: actions/upload-artifact@v4
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
with:
name: build artifacts
path: |
dist
locale
es
lib
- name: zip builds
if: github.repository == 'ant-design/ant-design' && github.event_name == 'push' && github.ref == 'refs/heads/master'
env:
ALI_OSS_AK_ID: ${{ secrets.ALI_OSS_AK_ID }}
ALI_OSS_AK_SECRET: ${{ secrets.ALI_OSS_AK_SECRET }}
HEAD_SHA: ${{ github.sha }}
run: |
zip -r oss-artifacts.zip dist locale es lib
echo "🤖 Uploading"
node scripts/visual-regression/upload.js ./oss-artifacts.zip --ref=$HEAD_SHA
test-lib-es:
name: test lib/es module
runs-on: ubuntu-latest
strategy:
matrix:
module: [lib, es]
shard: [1/2, 2/2]
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install
- name: restore cache from ${{ matrix.module }}
# lib only run in master branch not in pull request
if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }}
uses: actions/cache@v4
with:
path: ${{ matrix.module }}
key: ${{ matrix.module }}-${{ github.sha }}
- name: compile
# lib only run in master branch not in pull request
if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }}
run: bun run compile
- name: test
# lib only run in master branch not in pull request
if: ${{ github.event_name != 'pull_request' || matrix.module != 'lib' }}
run: bun run test -- --maxWorkers=2 --shard=${{matrix.shard}}
env:
LIB_DIR: ${{ matrix.module }}

View File

@ -2,7 +2,11 @@
# https://github.com/ant-design/ant-design/blob/79f566b7f8abb1012ef55b0d2793bfdf5595b85d/.github/workflows/test.yml
name: ✅ test
on: [push, pull_request]
on:
push:
branches: [master, feature]
pull_request:
branches: [master, feature]
# Cancel prev CI if new commit come
concurrency:
@ -133,7 +137,7 @@ jobs:
bunx nyc report --reporter text -t coverage --report-dir coverage
rm -rf persist-coverage
- name: Upload coverage to codecov
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v5
with:
# use own token to upload coverage reports
token: ${{ secrets.CODECOV_TOKEN }}
@ -171,10 +175,11 @@ jobs:
run: bun run dist
env:
NODE_OPTIONS: --max_old_space_size=4096
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
CI: 1
- name: check build files
run: node ./tests/dekko/index.test.js
run: bun run test:dekko
# Artifact build files
- uses: actions/upload-artifact@v4

View File

@ -58,7 +58,13 @@ jobs:
if (comment.body.includes('VISUAL_DIFF_FAILED')) {
hasDiffFailed = true;
}
if (comment.body.includes('- [x] Visual diff is acceptable')) {
// https://regex101.com/r/kLjudz/1
const RE = /(?<=\>\s\[!IMPORTANT\].*?- \[ \])/s;
if (
comment.body.includes('- [x] Visual diff is acceptable') &&
comment.body.match(RE) == null /** 检查 IMPORTANT 是否存在未勾选的 */
) {
hasMemberApprove = true;
}
}
@ -71,15 +77,26 @@ jobs:
const mergedStatus = diffPassed ? 'success' : hasDiffFailed ? 'failure' : 'pending';
console.log('Status:', mergedStatus);
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: prHeadSha,
state: mergedStatus,
context: 'Visual Regression Diff Wait Approve',
description: diffPassed ? 'Visual diff is acceptable' : 'Visual diff is not pass',
const { data: currentStatuses } = await github.rest.repos.listCommitStatusesForRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: prHeadSha,
});
const currentStatus = currentStatuses.find(status => status.context === 'Visual Regression Diff Wait Approve');
if (currentStatus && currentStatus.state === mergedStatus) {
console.log('Status has not changed, no need to update:', currentStatus.state);
} else {
await github.rest.repos.createCommitStatus({
owner: context.repo.owner,
repo: context.repo.repo,
sha: prHeadSha,
state: mergedStatus,
context: 'Visual Regression Diff Wait Approve',
description: diffPassed ? 'Visual diff is acceptable' : 'Visual diff is not pass',
});
}
if (hasDiffSuccess || (hasDiffFailed && hasMemberApprove)) {
return 'success';
} else if (hasDiffFailed) {

View File

@ -4,7 +4,7 @@ name: 👀 Visual Regression Diff Build
on:
pull_request:
branches: [master, feature]
branches: [master, feature, next]
types: [opened, synchronize, reopened]
# Cancel prev CI if new commit come

View File

@ -68,7 +68,7 @@ jobs:
# We need get persist-index first
- name: download image snapshot artifact
uses: dawidd6/action-download-artifact@v6
uses: dawidd6/action-download-artifact@v7
with:
workflow: ${{ github.event.workflow_run.workflow_id }}
run_id: ${{ github.event.workflow_run.id }}
@ -90,7 +90,7 @@ jobs:
- name: download report artifact
id: download_report
if: ${{ needs.upstream-workflow-summary.outputs.build-status == 'success' || needs.upstream-workflow-summary.outputs.build-status == 'failure' }}
uses: dawidd6/action-download-artifact@v6
uses: dawidd6/action-download-artifact@v7
with:
workflow: ${{ github.event.workflow_run.workflow_id }}
run_id: ${{ github.event.workflow_run.id }}

View File

@ -8,7 +8,7 @@ name: 👀 Visual Regression Diff Start
on:
pull_request_target:
branches: [master, feature]
branches: [master, feature, next]
types: [opened, synchronize, reopened]
permissions:

View File

@ -65,7 +65,7 @@ jobs:
# We need get persist key first
- name: Download Visual Regression Ref
uses: dawidd6/action-download-artifact@v6
uses: dawidd6/action-download-artifact@v7
with:
workflow: ${{ github.event.workflow_run.workflow_id }}
run_id: ${{ github.event.workflow_run.id }}
@ -79,7 +79,7 @@ jobs:
- name: Download Visual-Regression Artifact
if: ${{ fromJSON(needs.upstream-workflow-summary.outputs.build-success) }}
uses: dawidd6/action-download-artifact@v6
uses: dawidd6/action-download-artifact@v7
with:
workflow: ${{ github.event.workflow_run.workflow_id }}
run_id: ${{ github.event.workflow_run.id }}
@ -87,7 +87,7 @@ jobs:
path: ./tmp
- name: Persist Image Snapshot to OSS
if: github.repository == 'ant-design/ant-design' && github.event.workflow_run.event == 'push' && (github.event.workflow_run.head_branch == 'master' || github.event.workflow_run.head_branch == 'feature')
if: github.repository == 'ant-design/ant-design' && github.event.workflow_run.event == 'push' && (github.event.workflow_run.head_branch == 'master' || github.event.workflow_run.head_branch == 'feature' || github.event.workflow_run.head_branch == 'next')
env:
ALI_OSS_AK_ID: ${{ secrets.ALI_OSS_AK_ID }}
ALI_OSS_AK_SECRET: ${{ secrets.ALI_OSS_AK_SECRET }}

View File

@ -7,6 +7,7 @@ on:
branches:
- master
- feature
- next
permissions:
contents: read

2
.husky/pre-commit Executable file → Normal file
View File

@ -1 +1 @@
lint-staged
lint-staged

View File

@ -5,6 +5,7 @@ const compileModules = [
'@ant-design',
'countup.js',
'.pnpm',
'@asamuzakjp/css-color',
];
const ignoreList = [];

View File

@ -61,5 +61,19 @@
"5.21.0": [
"https://github.com/ant-design/ant-design/issues/50960",
"https://github.com/ant-design/ant-design/issues/50969"
]
],
"5.21.6": [
"https://github.com/ant-design/ant-design/issues/51420",
"https://github.com/ant-design/ant-design/issues/51430"
],
"5.22.0": [
"https://github.com/ant-design/ant-design/issues/51601",
"https://github.com/ant-design/ant-design/issues/51420",
"https://github.com/ant-design/ant-design/issues/51430"
],
"5.22.1": [
"https://github.com/ant-design/ant-design/issues/51420",
"https://github.com/ant-design/ant-design/issues/51430"
],
"5.22.6": ["https://github.com/ant-design/ant-design/issues/52124"]
}

View File

@ -15,6 +15,206 @@ tag: vVERSION
---
## 5.23.1
`2025-01-13`
- 🆕 Add Tree leaf node className for differentiate node type. [#52274](https://github.com/ant-design/ant-design/pull/52274) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
- 🐞 Fix DatePicker switch buttons is not hidden when `superPrevIcon/superNextIcon/prevIcon/nextIcon` is null. [#52327](https://github.com/ant-design/ant-design/pull/52327) [@afc163](https://github.com/afc163)
- 🐞 Fix Select throws `error not a valid selector` in Jest tests. [#51844](https://github.com/ant-design/ant-design/pull/51844) [@renovate](https://github.com/renovate)
- 🐞 Fix Layout.Sider under ConfigProvider directly, the `theme` not working. [#52302](https://github.com/ant-design/ant-design/pull/52302) [@zombieJ](https://github.com/zombieJ)
- 🐞 Fix Splitter lost previous state when re-expanding. [#52222](https://github.com/ant-design/ant-design/pull/52222) [@jjlstruggle](https://github.com/jjlstruggle)
- 🐞 Fix Table unexpected row selections when set `checkStrictly` to false in tree mode. [#52338](https://github.com/ant-design/ant-design/pull/52338) [@LeeSSHH](https://github.com/LeeSSHH)
- Button
- 🐞 Fix Button alignment and icon centering by adjusting the icon size for icon-only Buttons. [#52353](https://github.com/ant-design/ant-design/pull/52353) [@afc163](https://github.com/afc163)
- 💄 Fix Button missing `box-shadow` style. [#52304](https://github.com/ant-design/ant-design/pull/52304) [@zombieJ](https://github.com/zombieJ)
- RTL
- 💄 Fix Collapse arrow direction in RTL mode. [#52374](https://github.com/ant-design/ant-design/pull/52374) [@aojunhao123](https://github.com/aojunhao123)
- 💄 Fix Layout.Sider arrow direction in RTL mode. [#52374](https://github.com/ant-design/ant-design/pull/52374) [@aojunhao123](https://github.com/aojunhao123)
## 5.23.0
`2025-01-06`
- 🔥 TreeSelect support `maxCount` to limit the maximum number of selections. [#51759](https://github.com/ant-design/ant-design/pull/51759) [@aojunhao123](https://github.com/aojunhao123)
- 🔥 Modal `width` support responsive size. [#51653](https://github.com/ant-design/ant-design/pull/51653) [@zombieJ](https://github.com/zombieJ)
- 🔥 Splitter support `lazy` mode. [#51557](https://github.com/ant-design/ant-design/pull/51557) [@OysterD3](https://github.com/OysterD3)
- Button
- 🔥 Button `color` support full color palette. [#51550](https://github.com/ant-design/ant-design/pull/51550) [@OysterD3](https://github.com/OysterD3)
<img width="520" alt="Button Colors" src="https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*ApyYQpXQQfgAAAAAAAAAAAAADgCCAQ/original">
- 🆕 Button support `loading={{ icon: ReactNode }}` to customize loading icon. [#51758](https://github.com/ant-design/ant-design/pull/51758) [@zhangchao-wooc](https://github.com/zhangchao-wooc)
- Menu
- 🐞 Fix Menu `extra` font size and vertical align issue. [#52217](https://github.com/ant-design/ant-design/pull/52217) [@guoyunhe](https://github.com/guoyunhe)
- 🆕 Menu add token `subMenuItemSelectedColor` to resolve submenu title color being overrided by `itemSelectedColor`. [#52182](https://github.com/ant-design/ant-design/pull/52182) [@afc163](https://github.com/afc163)
- 🆕 Semantic Props
- 🆕 ConfigProvider support Empty semantic props `classNames` and `styles`. [#52208](https://github.com/ant-design/ant-design/pull/52208) [@thinkasany](https://github.com/thinkasany)
- 🆕 ConfigProvider support Popconfirm semantic props `classNames` and `styles`. [#52126](https://github.com/ant-design/ant-design/pull/52126) [@thinkasany](https://github.com/thinkasany)
- 🆕 ConfigProvider support Popover semantic props `classNames` and `styles`. [#52110](https://github.com/ant-design/ant-design/pull/52110) [@thinkasany](https://github.com/thinkasany)
- 🆕 ConfigProvider support Tooltip semantic props `classNames` and `styles`. [#51872](https://github.com/ant-design/ant-design/pull/51872) [@thinkasany](https://github.com/thinkasany)
- 🆕 ConfigProvider support Descriptions semantic props `classNames` and `styles`. [#52120](https://github.com/ant-design/ant-design/pull/52120) [@thinkasany](https://github.com/thinkasany)
- 🆕 ConfigProvider support Slider semantic props `classNames` and `styles`. [#52185](https://github.com/ant-design/ant-design/pull/52185) [@thinkasany](https://github.com/thinkasany)
- 🆕 Transfer support `showSearch` config `defaultValue` & `placeholder`. [#52125](https://github.com/ant-design/ant-design/pull/52125) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
- 🆕 Calendar now supports `showWeek` prop. [#52072](https://github.com/ant-design/ant-design/pull/52072) [@afc163](https://github.com/afc163)
- 🆕 Mentions support `onPopupScroll` props. [#51858](https://github.com/ant-design/ant-design/pull/51858) [@OysterD3](https://github.com/OysterD3)
- 🆕 Card support `bodyPaddingSM`, `headerPaddingSM`, `bodyPadding`, `headerPadding` component token. [#51762](https://github.com/ant-design/ant-design/pull/51762) [@thinkasany](https://github.com/thinkasany)
- 🆕 ColorPicker `presets` support `key` prop. [#51794](https://github.com/ant-design/ant-design/pull/51794) [@li-jia-nan](https://github.com/li-jia-nan)
- 🆕 Cascader support `optionSelectedColor` token. [#51769](https://github.com/ant-design/ant-design/pull/51769) [@thinkasany](https://github.com/thinkasany)
- Tree
- 🛠 Refactor Tree part code to Function Component for React 19 perf preparing. [#52209](https://github.com/ant-design/ant-design/pull/52209) [@li-jia-nan](https://github.com/li-jia-nan)
- 💄 Optimize Tree `disabled` & `selected` node display style. [#52173](https://github.com/ant-design/ant-design/pull/52173) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
- 🐞 Fix Slider crash when `tipFormatter` is undefined. [#52184](https://github.com/ant-design/ant-design/pull/52184) [@thinkasany](https://github.com/thinkasany)
- 🐞 Fix Layout.Sider `trigger` style not correct. [#46a8eff](https://github.com/ant-design/ant-design/commit/46a8eff) [@Wxh16144](https://github.com/Wxh16144)
- Table
- 🐞 Fix Table `fixedright` is not working in `expandable`. [#52176](https://github.com/ant-design/ant-design/pull/52176) [@afc163](https://github.com/afc163)
- 🐞 Fix Table sticky scrollbar not working in rtl direction. [#52176](https://github.com/ant-design/ant-design/pull/52176) [@afc163](https://github.com/afc163)
- 💄 Optimize Flex to always reset `margin` & `padding` for customize component. [#52170](https://github.com/ant-design/ant-design/pull/52170) [@li-jia-nan](https://github.com/li-jia-nan)
- 🐞 Fix DatePicker.RangePicker `needConfirm` sometime can switch panel without confirm. [#52102](https://github.com/ant-design/ant-design/pull/52102) [@Zyf665](https://github.com/Zyf665)
- 💄 Optimize Collapse focus styles and items border radius. [#52086](https://github.com/ant-design/ant-design/pull/52086) [@aojunhao123](https://github.com/aojunhao123)
- ⌨️ Add Radio.Group default `name` prop to improve a11y. [#52076](https://github.com/ant-design/ant-design/pull/52076) [@aojunhao123](https://github.com/aojunhao123)
- ⌨️ Input.Search add `type=search` by default. [#52083](https://github.com/ant-design/ant-design/pull/52083) [@Kaikiat1126](https://github.com/Kaikiat1126)
- ⌨️ Improve Tabs focus style for keyboard operation. [#52002](https://github.com/ant-design/ant-design/pull/52002) [@aojunhao123](https://github.com/aojunhao123)
- Segmented
- ⌨️ Optimize Segmented focus style to improve a11y. [#51934](https://github.com/ant-design/ant-design/pull/51934) [@aojunhao123](https://github.com/aojunhao123)
- ⌨️ Segmented support `name` prop to improve a11y. [#51725](https://github.com/ant-design/ant-design/pull/51725) [@thinkasany](https://github.com/thinkasany)
- 📦 MISC: Reduce bundle size by replacing `@ctrl/tinycolor` with `@ant-design/fast-color`. [#52190](https://github.com/ant-design/ant-design/pull/52190) [#52157](https://github.com/ant-design/ant-design/pull/52157) [@aojunhao123](https://github.com/aojunhao123)
- ⌨️ Adjust Input, InputNumber, Mentions, Textarea clear icon from `span` to `button` to improve a11y. [#52180](https://github.com/ant-design/ant-design/pull/52180) [@li-jia-nan](https://github.com/li-jia-nan)
- 🐞 MISC: Fix build error when using React 19. [#52168](https://github.com/ant-design/ant-design/pull/52168) [@zombieJ](https://github.com/zombieJ)
- TypeScript
- 🤖 Adjust Table `ref` type to React.Ref. [#52205](https://github.com/ant-design/ant-design/pull/52205) [@li-jia-nan](https://github.com/li-jia-nan)
- 🤖 Calendar export CalendarMode type. [#52160](https://github.com/ant-design/ant-design/pull/52160) [@Kaikiat1126](https://github.com/Kaikiat1126)
## 5.22.7
`2024-12-27`
- 🐞 Fix Button text and icon not align. [#52132](https://github.com/ant-design/ant-design/pull/52132) [@afc163](https://github.com/afc163)
- 🐞 Fix Button throws `reactRender is not a function` under React 19. [#52105](https://github.com/ant-design/ant-design/pull/52105) [@afc163](https://github.com/afc163)
- TypeScript
- 🤖 Fix Menu interface type error from external module. [#51715](https://github.com/ant-design/ant-design/pull/51715) [@msyavuz](https://github.com/msyavuz)
## 5.22.6
`2024-12-23`
- 🐞 Align Button with and without icons consistently. [#52070](https://github.com/ant-design/ant-design/pull/52070)
- 🐞 Fix Splitter collapsible icon `z-index` too low. [#52065](https://github.com/ant-design/ant-design/pull/52065) [@wanpan11](https://github.com/wanpan11)
- 🐞 Fix Button motion not smooth when set `loading`. [#52059](https://github.com/ant-design/ant-design/pull/52059) [@zombieJ](https://github.com/zombieJ)
- 🐞 Fix Button issue where solid default button text disappears on hover in dark mode. [#52024](https://github.com/ant-design/ant-design/pull/52024) [@DDDDD12138](https://github.com/DDDDD12138)
## 5.22.5
`2024-12-15`
- 🛠 Refactor Wave/Menu/Form `ref` check logic to resolve React 19 `ref` conflict (Note, this is not finally support React 19 but we will resolve step by step in future version). [#51952](https://github.com/ant-design/ant-design/pull/51952) [@zombieJ](https://github.com/zombieJ)
- 🐞 Fix Dropdown cannot accept ReactNode as `children`. [#50174](https://github.com/ant-design/ant-design/pull/50174) [@coding-ice](https://github.com/coding-ice)
- 🐞 Fix Carousel cannot display correctly in Modal without icon. [#51988](https://github.com/ant-design/ant-design/pull/51988) [@quan060798](https://github.com/quan060798)
- 🐞 Fix Select label overflow issue. [#52011](https://github.com/ant-design/ant-design/pull/52011) [@OysterD3](https://github.com/OysterD3)
- 🐞 Fix Form `setFieldValue` not reset field validation. [#51993](https://github.com/ant-design/ant-design/pull/51993) [@zombieJ](https://github.com/zombieJ)
- 🐞 Fix Pagination with setting `showSizeChanger.showSearch` not working. [#51962](https://github.com/ant-design/ant-design/pull/51962) [@zombieJ](https://github.com/zombieJ)
- 🇰🇷 Improve Korean locales for DatePicker. [#51983](https://github.com/ant-design/ant-design/pull/51983) [@DevLeti](https://github.com/DevLeti)
- 🤖 Export `CheckboxChangeEvent` from antd. [#52008](https://github.com/ant-design/ant-design/pull/52008) [@SpecLad](https://github.com/SpecLad)
## 5.22.4
`2024-12-09`
- Transfer
- 🐞 Fix the background overflow when Transfer selects the last item on the current page. [#51884](https://github.com/ant-design/ant-design/pull/51884) [@ayangweb](https://github.com/ayangweb)
- 🐞 Fix Transfer toggle button being enabled when all items are disabled. [#51784](https://github.com/ant-design/ant-design/pull/51784) [@WwwHhhYran](https://github.com/WwwHhhYran)
- 🐞 Fix the arrow would be outside the container when the Tooltip content was too small. [#51904](https://github.com/ant-design/ant-design/pull/51904)
- 🐞 Fix where clicking the Radio or Checkbox under Upload would trigger the popup window twice. [#51874](https://github.com/ant-design/ant-design/pull/51874)
- 💄 Fix Menu icon alignment when using `collapsedIconSize`. [#51863](https://github.com/ant-design/ant-design/pull/51863) [@Gnomeek](https://github.com/Gnomeek)
- 💄 Fix incorrect styling of Tabs component when `type="editable-card"`. [#51935](https://github.com/ant-design/ant-design/pull/51935) [@aojunhao123](https://github.com/aojunhao123)
- 💄 Fix insufficient trigger style priority in Layout.Sider component in `zero-width` mode. [#51936](https://github.com/ant-design/ant-design/pull/51936) [@aojunhao123](https://github.com/aojunhao123)
- 💄 MISC: Fix the icon styles were created repeatedly. [#51897](https://github.com/ant-design/ant-design/pull/51897) [@YumoImer](https://github.com/YumoImer)
- 💄 MISC: Inline styles refactored to cssinjs. [#51843](https://github.com/ant-design/ant-design/pull/51843)
## 5.22.3
`2024-12-02`
- 🐞 Fix Select clear button may has incorrect position within Form.item. [#51649](https://github.com/ant-design/ant-design/pull/51649) [@dislido](https://github.com/dislido)
- 🐞 Fix InputNumber `handleVisible` token not work as expected. [#51728](https://github.com/ant-design/ant-design/pull/51728) [@dengfuping](https://github.com/dengfuping)
- 🐞 Fix ColorPicker error when pass `ReactNode` to `label` field of `presets` property. [#51808](https://github.com/ant-design/ant-design/pull/51808) [@li-jia-nan](https://github.com/li-jia-nan)
- 🐞 Fix Menu `inlineCollapsed` property not works bug within Layout. [#51775](https://github.com/ant-design/ant-design/pull/51775) [@coderz-w](https://github.com/coderz-w)
- 🐞 Fix Table `onHeaderCell` provided part `style` can not override. [#51793](https://github.com/ant-design/ant-design/pull/51793) [@Wxh16144](https://github.com/Wxh16144)
- ⌨️ Improve Collapse accessibility. [#51836](https://github.com/ant-design/ant-design/pull/51836) [@aojunhao123](https://github.com/aojunhao123)
- TypeScript
- 🤖 Add Table argument type for `clearFilters` function property. [#51754](https://github.com/ant-design/ant-design/pull/51754) [@fubd](https://github.com/fubd)
- 🤖 Fix Form.List with nest field will miss value with remove when set Form `preserve` to `false`. [#51796](https://github.com/ant-design/ant-design/pull/51796) [@zombieJ](https://github.com/zombieJ)
## 5.22.2
`2024-11-21`
- 🐞 Fix Input.OTP focus from advancing when previous input is empty. [#51664](https://github.com/ant-design/ant-design/pull/51664) [@thecodesalim](https://github.com/thecodesalim)
- 🐞 Adjust Modal function call not to scroll the confirm button when it get auto focused. [#51647](https://github.com/ant-design/ant-design/pull/51647) [@zombieJ](https://github.com/zombieJ)
- 🐞 Fix Form `rules` with same error content will cause React render warning. [#51636](https://github.com/ant-design/ant-design/pull/51636) [@zombieJ](https://github.com/zombieJ)
- 🐞 Refactor Button `focus` logic trigger with `useEffect` to resolve some async load case not get `autoFocus`. [#51624](https://github.com/ant-design/ant-design/pull/51624) [@zombieJ](https://github.com/zombieJ)
- 🐞 Fix Button custom icon not center-aligned. [#51652](https://github.com/ant-design/ant-design/pull/51652) [@afc163](https://github.com/afc163)
- 🐞 Fix Table `getCheckboxProps` event handlers being overridden by internal selection logic. [#51661](https://github.com/ant-design/ant-design/pull/51661) [@Zyf665](https://github.com/Zyf665)
- 🐞 Fix Tree that `onCheck` and `onSelect` were not properly triggered. [#51448](https://github.com/ant-design/ant-design/pull/51448) [@Wxh16144](https://github.com/Wxh16144)
- 🐞 Fix vertical alignment of clear icon in Input component. [#51700](https://github.com/ant-design/ant-design/pull/51700) [@jynxio](https://github.com/jynxio)
- 🐞 Fix Select with `prefix` style issue with color, line break, status error. [#51694](https://github.com/ant-design/ant-design/pull/51694) [@zombieJ](https://github.com/zombieJ)
- 🌐 Localization
- 🇷🇺 Add support for Russian translation. [#51619](https://github.com/ant-design/ant-design/pull/51619) [@avvakumovid](https://github.com/avvakumovid)
- 🇮🇹 Add support for Italian translation in TimePicker. [#51685](https://github.com/ant-design/ant-design/pull/51685) [@LorenzoCardinali](https://github.com/LorenzoCardinali)
## 5.22.1
`2024-11-13`
- 🛠 Adjust DatePicker.RangePicker to not allow switching to the next field by clicking the input when `needConfirm` and the user has not submitted the date. [#51591](https://github.com/ant-design/ant-design/pull/51591) [@zombieJ](https://github.com/zombieJ)
- 🛠 Lock Input.OTP `ctrl + z` operation to avoid data not correct. [#51609](https://github.com/ant-design/ant-design/pull/51609) [@zombieJ](https://github.com/zombieJ)
- 🐞 Fix Select `tags` or `multiple` mode display issue. [#51605](https://github.com/ant-design/ant-design/pull/51605) [@guoyunhe](https://github.com/guoyunhe)
- 🐞 Fix Badge `count` motion missing in Safari. [#51598](https://github.com/ant-design/ant-design/pull/51598) [@zombieJ](https://github.com/zombieJ)
- 🐞 Fix Tabs with `centered` the tabs can not fully display. [#51571](https://github.com/ant-design/ant-design/pull/51571) [@DDDDD12138](https://github.com/DDDDD12138)
- 🐞 Fix Transfer with controlled `dataSource` & `selectedKeys` sometime miss sync checked state. [#51523](https://github.com/ant-design/ant-design/pull/51523) [@IsKaros](https://github.com/IsKaros)
- 🐞 Revert Button `display` `inline-flex` back to `inline-block` to resolve Icon align issue. [#51588](https://github.com/ant-design/ant-design/pull/51588) [@Wxh16144](https://github.com/Wxh16144)
## 5.22.0
`2024-11-12`
- Form
- 🆕 Form.Item supports hiding labels. [#51524](https://github.com/ant-design/ant-design/pull/51524) [@crazyair](https://github.com/crazyair)
- 🐞 Form removes the div used to expand the error height, wraps errorDom and extraDom with a div, and sets a minimum height for the div. [#51254](https://github.com/ant-design/ant-design/pull/51254) [@hongzzz](https://github.com/hongzzz)
- 🐞 Fix the problem that `onValuesChange` is still triggered when the Form field triggers change but the value does not change. [#51437](https://github.com/ant-design/ant-design/pull/51437) [@crazyair](https://github.com/crazyair)
- 🆕 Form supports the focus property in scrollToFirstError when form validation fails. [#51231](https://github.com/ant-design/ant-design/pull/51231) [@nathanlao](https://github.com/nathanlao)
- Table
- 🆕 Table column filter drop-down box supports `filterDropdownProps`. [#51297](https://github.com/ant-design/ant-design/pull/51297) [@Wxh16144](https://github.com/Wxh16144)
- 🆕 Table `expandedRowClassName` supports string . [#51067](https://github.com/ant-design/ant-design/pull/51067) [@li-jia-nan](https://github.com/li-jia-nan)
- Tree
- 💄 Fix the problem of missing padding style for selected nodes in Tree. [#51492](https://github.com/ant-design/ant-design/pull/51492) [@zombieJ](https://github.com/zombieJ)
- 🆕 Tree component Token adds `nodeHoverColor` and `nodeSelectedColor` support. [#51367](https://github.com/ant-design/ant-design/pull/51367) [@zmbxy](https://github.com/zmbxy)
- 🆕 Tree adds `indentSize` token for custom indent width. [#51010](https://github.com/ant-design/ant-design/pull/51010) [@afc163](https://github.com/afc163)
- DatePicker
- 🆕 DatePicker supports prefix attribute. [#51335](https://github.com/ant-design/ant-design/pull/51335) [@guoyunhe](https://github.com/guoyunhe)
- 💄 Fixed the issue of DatePicker.RangePicker flashing when the mouse moves between cells. [#51533](https://github.com/ant-design/ant-design/pull/51533) [@afc163](https://github.com/afc163)
- Input.OTP
- 🆕 In the `Input.OTP` component, add `onInput` event to get the value of each user input. At the same time, the relevant documentation has been updated. [#51289](https://github.com/ant-design/ant-design/pull/51289) [@aojunhao123](https://github.com/aojunhao123)
- 🐞 Fixed the problem that Input.OTP cannot specify `inputMode`. [#51271](https://github.com/ant-design/ant-design/pull/51271) [@alan-rudzinski](https://github.com/alan-rudzinski)
- 🆕 ColorPicker supports `disabledFormat`. [#51539](https://github.com/ant-design/ant-design/pull/51539) [@su-muzhi](https://github.com/su-muzhi)
- 🆕 Add `cursor` configuration item to the `focus` method of InputNumber component to control the cursor position. [#51444](https://github.com/ant-design/ant-design/pull/51444) [@aojunhao123](https://github.com/aojunhao123)
- 🆕 Cascader adds `disabled` attribute to disable all first-level directory items of the component. [#51272](https://github.com/ant-design/ant-design/pull/51272) [@aojunhao123](https://github.com/aojunhao123)
- 🆕 Descriptions supports single-line spreading. [#51365](https://github.com/ant-design/ant-design/pull/51365) [@crazyair](https://github.com/crazyair)
- 🆕 Select/TreeSelect/Cascader components add `prefix` property to support custom prefix. [#51186](https://github.com/ant-design/ant-design/pull/51186) [@guoyunhe](https://github.com/guoyunhe)
- 🐞 Fix the problem that the preview image class name is lost when setting `ImageProps.preview.rootClassName` in Image. [#51538](https://github.com/ant-design/ant-design/pull/51538) [@dislido](https://github.com/dislido)
- 🐞 Fixed the issue that the last item in the TimePicker panel column cannot be scrolled to the top. [#51481](https://github.com/ant-design/ant-design/pull/51481) [@zombieJ](https://github.com/zombieJ)
- 🐞 Fix TreeSelect dropdown height not enough. [#51567](https://github.com/ant-design/ant-design/pull/51567) [@afc163](https://github.com/afc163)
- 🐞 Fixed the issue that Typography is not updated immediately when the ConfigProvider language is switched. [#51453](https://github.com/ant-design/ant-design/pull/51453) [@thinkasany](https://github.com/thinkasany)
- 🐞 Fixed the issue that Upload `itemRender` calling `action.preview` will cause a crash. [#51419](https://github.com/ant-design/ant-design/pull/51419) [@yoyo837](https://github.com/yoyo837)
- 🐞 Fixed Splitter pseudo-element symbol issue. [#51536](https://github.com/ant-design/ant-design/pull/51536) [@dislido](https://github.com/dislido)
- 💄 Optimize Collapse accessibility attribute and mouse hover style. [#51400](https://github.com/ant-design/ant-design/pull/51400) [@afc163](https://github.com/afc163)
- 💄 Fix styling issue of Menu title content. [#51425](https://github.com/ant-design/ant-design/pull/51425) [@coding-ice](https://github.com/coding-ice)
- 🇵🇹 Fix translation in Portuguese (pt_PT) localization file for better accuracy and consistency. [#51501](https://github.com/ant-design/ant-design/pull/51501) [@alexandre-p-marques-alb](https://github.com/alexandre-p-marques-alb)
- 🇺🇿 Optimize uz_UZ internationalization. [#51407](https://github.com/ant-design/ant-design/pull/51407) [@Zukhrik](https://github.com/Zukhrik)
- TypeScript
- 🤖 Upload exports type DraggerProps. [#51546](https://github.com/ant-design/ant-design/pull/51546) [@DBvc](https://github.com/DBvc)
- 🤖 Add defaultValue property to TimePicker.RangePicker example. [#51413](https://github.com/ant-design/ant-design/pull/51413) [@nathanlao](https://github.com/nathanlao)
- 🤖 Message Optimize the top type in message.config. [#51468](https://github.com/ant-design/ant-design/pull/51468) [@Fog3211](https://github.com/Fog3211)
- 🤖 Optimize the TS definition of Tree and TreeSelect. [#51251](https://github.com/ant-design/ant-design/pull/51251) [@afc163](https://github.com/afc163)
## 5.21.6
`2024-10-28`
@ -1902,7 +2102,7 @@ tag: vVERSION
- 💄 Adjust Select, TreeSelect, Cascader always show the `arrow` by default when multiple. [#41028](https://github.com/ant-design/ant-design/pull/41028)
- 🐞 Fix Form `Form.Item.useStatus` problem with sever-side-rendering. [#40977](https://github.com/ant-design/ant-design/pull/40977) [@AndyBoat](https://github.com/AndyBoat)
- 🐞 MISC: Fix arrow shape in some components. [#40971](https://github.com/ant-design/ant-design/pull/40971)
- 🐞 Fix Layout throw `React does not recognize the `suffixCls` prop on a DOM element` warning. [#40969](https://github.com/ant-design/ant-design/pull/40969)
- 🐞 Fix Layout throw "React does not recognize the `suffixCls` prop on a DOM element" warning. [#40969](https://github.com/ant-design/ant-design/pull/40969)
- 🐞 Fix Watermark that text will be displayed when the picture loads abnormally. [#40770](https://github.com/ant-design/ant-design/pull/40770) [@OriginRing](https://github.com/OriginRing)
- 🐞 Image support flip function in preview mode. Fix Image `fallback` when used in ssr. [#40660](https://github.com/ant-design/ant-design/pull/40660)
- 🐞 Fix Typography component is not centered in the Select component. [#40422](https://github.com/ant-design/ant-design/pull/40422) [@Yuiai01](https://github.com/Yuiai01)

View File

@ -15,6 +15,206 @@ tag: vVERSION
---
## 5.23.1
`2025-01-13`
- 🆕 新增 Tree 组件叶子节点的 className 用于区分节点类型。[#52274](https://github.com/ant-design/ant-design/pull/52274) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
- 🐞 修复 DatePicker `superPrevIcon/superNextIcon/prevIcon/nextIcon` 设置为 null 时切换按钮依旧存在的问题。[#52327](https://github.com/ant-design/ant-design/pull/52327) [@afc163](https://github.com/afc163)
- 🐞 修复 Select 组件在 jest 测试中报错 `not a valid selector` 的问题。[#51844](https://github.com/ant-design/ant-design/pull/51844) [@renovate](https://github.com/renovate)
- 🐞 修复 Layout.Sider 直接嵌套在 ConfigProvider 下时,`theme` 配置无效的问题。[#52302](https://github.com/ant-design/ant-design/pull/52302) [@zombieJ](https://github.com/zombieJ)
- 🐞 修复 Splitter 二次展开时丢失上一次状态的问题。[#52222](https://github.com/ant-design/ant-design/pull/52222) [@jjlstruggle](https://github.com/jjlstruggle)
- 🐞 修复 Table 树形展示且设置 `checkStrictly` 为 false 时,某些行被错误选中的问题。[#52338](https://github.com/ant-design/ant-design/pull/52338) [@LeeSSHH](https://github.com/LeeSSHH)
- Button
- 🐞 调整 Button 纯图标的大小从而修复按钮对齐和图标居中问题。[#52353](https://github.com/ant-design/ant-design/pull/52353) [@afc163](https://github.com/afc163)
- 💄 修复 Button 丢失阴影样式的问题。[#52304](https://github.com/ant-design/ant-design/pull/52304) [@zombieJ](https://github.com/zombieJ)
- RTL
- 💄 修复 Collapse 在 RTL 模式下的箭头方向。[#52374](https://github.com/ant-design/ant-design/pull/52374) [@aojunhao123](https://github.com/aojunhao123)
- 💄 修复 Layout.Sider 在 RTL 模式下的箭头方向。[#52374](https://github.com/ant-design/ant-design/pull/52374) [@aojunhao123](https://github.com/aojunhao123)
## 5.23.0
`2025-01-06`
- 🔥 TreeSelect 新增 `maxCount` 属性以限制最大选择数量。[#51759](https://github.com/ant-design/ant-design/pull/51759) [@aojunhao123](https://github.com/aojunhao123)
- 🔥 Modal `width` 支持响应式尺寸。[#51653](https://github.com/ant-design/ant-design/pull/51653) [@zombieJ](https://github.com/zombieJ)
- 🔥 Splitter 增加 `lazy` 模式。[#51557](https://github.com/ant-design/ant-design/pull/51557) [@OysterD3](https://github.com/OysterD3)
- Button
- 🔥 Button `color` 属性支持完整色板。[#51550](https://github.com/ant-design/ant-design/pull/51550) [@OysterD3](https://github.com/OysterD3)
<img width="520" alt="Button Colors" src="https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*ApyYQpXQQfgAAAAAAAAAAAAADgCCAQ/original">
- 🆕 Button 组件新增 `loading={{ icon: ReactNode }}` 以自定义加载图标。[#51758](https://github.com/ant-design/ant-design/pull/51758) [@zhangchao-wooc](https://github.com/zhangchao-wooc)
- Menu
- 🆕 Menu 新增 token `subMenuItemSelectedColor`,避免 `itemSelectedColor` 覆盖子菜单标题样式。[#52182](https://github.com/ant-design/ant-design/pull/52182) [@afc163](https://github.com/afc163)
- 🐞 修复 Menu `extra` 字体大小和垂直居中对齐问题。[#52217](https://github.com/ant-design/ant-design/pull/52217) [@guoyunhe](https://github.com/guoyunhe)
- 🆕 语义化
- 🆕 ConfigProvider 支持 Empty 组件语义化 `classNames``styles`。[#52208](https://github.com/ant-design/ant-design/pull/52208) [@thinkasany](https://github.com/thinkasany)
- 🆕 ConfigProvider 支持 Slider 组件语义化 `classNames``styles`。[#52185](https://github.com/ant-design/ant-design/pull/52185) [@thinkasany](https://github.com/thinkasany)
- 🆕 ConfigProvider 支持 Popconfirm 组件语义化 `classNames``styles`。[#52126](https://github.com/ant-design/ant-design/pull/52126) [@thinkasany](https://github.com/thinkasany)
- 🆕 ConfigProvider 支持 Popover 组件语义化 `classNames``styles`。[#52110](https://github.com/ant-design/ant-design/pull/52110) [@thinkasany](https://github.com/thinkasany)
- 🆕 ConfigProvider 支持 Tooltip 组件语义化 `classNames``styles`。[#51872](https://github.com/ant-design/ant-design/pull/51872) [@thinkasany](https://github.com/thinkasany)
- 🆕 ConfigProvider 支持 Descriptions 组件语义化 `classNames``styles`。[#52120](https://github.com/ant-design/ant-design/pull/52120) [@thinkasany](https://github.com/thinkasany)
- Tree
- 🛠 重构 Tree 部分代码为 Function Component 以为 React 19 做更好性能准备。[#52209](https://github.com/ant-design/ant-design/pull/52209) [@li-jia-nan](https://github.com/li-jia-nan)
- 💄 优化 Tree `disabled``selected` 节点状态下的颜色展示。[#52173](https://github.com/ant-design/ant-design/pull/52173) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
- 🆕 Transfer 支持 `showSearch` 配置 `defaultValue``placeholder`。[#52125](https://github.com/ant-design/ant-design/pull/52125) [@EmilyyyLiu](https://github.com/EmilyyyLiu)
- 🆕 Calendar 支持 `showWeek` 属性用于显示周数列。[#52072](https://github.com/ant-design/ant-design/pull/52072) [@afc163](https://github.com/afc163)
- 🆕 Mentions 新增 `onPopupScroll` 属性。[#51858](https://github.com/ant-design/ant-design/pull/51858) [@OysterD3](https://github.com/OysterD3)
- 🆕 Card 增加 `bodyPaddingSM`、`headerPaddingSM`、`bodyPadding`、`headerPadding` 组件 token。[#51762](https://github.com/ant-design/ant-design/pull/51762) [@thinkasany](https://github.com/thinkasany)
- 🆕 ColorPicker `presets` 支持传入 `key`。[#51794](https://github.com/ant-design/ant-design/pull/51794) [@li-jia-nan](https://github.com/li-jia-nan)
- 🆕 Cascader 新增 `optionSelectedColor` token。[#51769](https://github.com/ant-design/ant-design/pull/51769) [@thinkasany](https://github.com/thinkasany)
- 🐞 修复 Layout.Sider `trigger` 样式不正确的问题。[#46a8eff](https://github.com/ant-design/ant-design/commit/46a8eff) [@Wxh16144](https://github.com/Wxh16144)
- Table
- 🐞 修复 Table `expandable` 中设置 `fixedright` 不生效的问题。[#52176](https://github.com/ant-design/ant-design/pull/52176) [@afc163](https://github.com/afc163)
- 🐞 修复 Table `sticky` 模式下水平固定滚动条在 rtl 模式下不生效的问题。[#52176](https://github.com/ant-design/ant-design/pull/52176) [@afc163](https://github.com/afc163)
- 💄 优化 Flex 使其在自定义渲染组件时总是重置 `margin`、`padding` 样式。[#52170](https://github.com/ant-design/ant-design/pull/52170) [@li-jia-nan](https://github.com/li-jia-nan)
- 🐞 修复 DatePicker.RangePicker `needConfirm` 模式偶尔在不确认仍然可以切换面板的问题。[#52102](https://github.com/ant-design/ant-design/pull/52102) [@Zyf665](https://github.com/Zyf665)
- 🐞 修复 Slider 当 `tipFormatter` 未定义时导致崩溃的问题。[#52184](https://github.com/ant-design/ant-design/pull/52184) [@thinkasany](https://github.com/thinkasany)
- 💄 优化 Collapse 聚焦样式以及折叠项圆角。[#52086](https://github.com/ant-design/ant-design/pull/52086) [@aojunhao123](https://github.com/aojunhao123)
- ⌨️ 为 Radio.Group 添加默认 `name` 属性以提升无障碍体验。[#52076](https://github.com/ant-design/ant-design/pull/52076) [@aojunhao123](https://github.com/aojunhao123)
- ⌨️ Input.Search 添加默认 `type=search` 类型。[#52083](https://github.com/ant-design/ant-design/pull/52083) [@Kaikiat1126](https://github.com/Kaikiat1126)
- ⌨️ 优化 Tabs 键盘操作时的焦点样式。[#52002](https://github.com/ant-design/ant-design/pull/52002) [@aojunhao123](https://github.com/aojunhao123)
- Segmented
- ⌨️ 优化 Segmented 聚焦样式以提升无障碍体验。[#51934](https://github.com/ant-design/ant-design/pull/51934) [@aojunhao123](https://github.com/aojunhao123)
- ⌨️ Segmented 支持 `name` 属性以提升无障碍体验。[#51725](https://github.com/ant-design/ant-design/pull/51725) [@thinkasany](https://github.com/thinkasany)
- 📦 MISC: 用 `@ant-design/fast-color` 替换 `@ctrl/tinycolor` 以降低打包体积。[#52190](https://github.com/ant-design/ant-design/pull/52190) [#52157](https://github.com/ant-design/ant-design/pull/52157) [@aojunhao123](https://github.com/aojunhao123)
- ⌨️ 调整 Input、InputNumber、Mentions、Textarea 组件清除图标从 `span` 元素更改为 `button` 元素,提高了可访问性和交互性。[#52180](https://github.com/ant-design/ant-design/pull/52180) [@li-jia-nan](https://github.com/li-jia-nan)
- 🐞 MISC: 修复 React 19 下构建报错的问题。[#52168](https://github.com/ant-design/ant-design/pull/52168) [@zombieJ](https://github.com/zombieJ)
- TypeScript
- 🤖 调整 Table `ref` 类型为 React.Ref。[#52205](https://github.com/ant-design/ant-design/pull/52205) [@li-jia-nan](https://github.com/li-jia-nan)
- 🤖 Calendar 导出 CalendarMode 类型。[#52160](https://github.com/ant-design/ant-design/pull/52160) [@Kaikiat1126](https://github.com/Kaikiat1126)
## 5.22.7
`2024-12-27`
- 🐞 修复 Button 文字和图标不对齐的问题。[#52132](https://github.com/ant-design/ant-design/pull/52132) [@afc163](https://github.com/afc163)
- 🐞 修复在 React 19 下点击 Button 时抛出 `reactRender is not a function` 错误的问题。[#52105](https://github.com/ant-design/ant-design/pull/52105) [@afc163](https://github.com/afc163)
- TypeScript
- 🤖 修复 Menu `component` 属性类型抛错。[#51715](https://github.com/ant-design/ant-design/pull/51715) [@msyavuz](https://github.com/msyavuz)
## 5.22.6
`2024-12-23`
- 🐞 修复 Button 有图标和无图标按钮对齐差一像素的问题。[#52070](https://github.com/ant-design/ant-design/pull/52070)
- 🐞 修复 Splitter 组件折叠图标 `z-index` 层级过低问题。[#52065](https://github.com/ant-design/ant-design/pull/52065) [@wanpan11](https://github.com/wanpan11)
- 🐞 修复 Button 启用 `loading` 时,动画不够顺滑的问题。[#52059](https://github.com/ant-design/ant-design/pull/52059) [@zombieJ](https://github.com/zombieJ)
- 🐞 修复 Button 暗色模式下默认填充按钮文本在悬停时消失的问题。[#52024](https://github.com/ant-design/ant-design/pull/52024) [@DDDDD12138](https://github.com/DDDDD12138)
## 5.22.5
`2024-12-15`
- 🛠 重构 Wave/Menu/Form `ref` 检查逻辑以解决 React 19 `ref` 部分冲突(注:该更新不会完全解决 React 19 兼容问题,后续将会持续更新)。[#51952](https://github.com/ant-design/ant-design/pull/51952) [@zombieJ](https://github.com/zombieJ)
- 🐞 修复 Dropdown `children` 不支持传入 ReactNode 的问题。[#50174](https://github.com/ant-design/ant-design/pull/50174) [@coding-ice](https://github.com/coding-ice)
- 🐞 修复 Carousel 某些情况下在 Modal 中无法正确展示的问题。[#51988](https://github.com/ant-design/ant-design/pull/51988) [@quan060798](https://github.com/quan060798)
- 🐞 修复 Select 选中文本溢出的问题 。[#52011](https://github.com/ant-design/ant-design/pull/52011) [@OysterD3](https://github.com/OysterD3)
- 🐞 修复 Form `setFieldValue` 没有重置字段校验信息的问题。[#51993](https://github.com/ant-design/ant-design/pull/51993) [@zombieJ](https://github.com/zombieJ)
- 🐞 修复 Pagination 配置 `showSizeChanger.showSearch` 无效的问题。[#51962](https://github.com/ant-design/ant-design/pull/51962) [@zombieJ](https://github.com/zombieJ)
- 🇰🇷 优化 DatePicker 韩语本地化文案。[#51983](https://github.com/ant-design/ant-design/pull/51983) [@DevLeti](https://github.com/DevLeti)
- 🤖 从 antd 里导出 `CheckboxChangeEvent` 类型。[#52008](https://github.com/ant-design/ant-design/pull/52008) [@SpecLad](https://github.com/SpecLad)
## 5.22.4
`2024-12-09`
- Transfer
- 🐞 修复 Transfer 选中当前页最后一项时背景溢出的问题。[#51884](https://github.com/ant-design/ant-design/pull/51884) [@ayangweb](https://github.com/ayangweb)
- 🐞 修正 Transfer 切换按钮当所有 item 禁用时依然可用的问题。[#51784](https://github.com/ant-design/ant-design/pull/51784) [@WwwHhhYran](https://github.com/WwwHhhYran)
- 🐞 修复 Tooltip 内容过少时,箭头会在容器外的问题。[#51904](https://github.com/ant-design/ant-design/pull/51904)
- 🐞 修复点击 Upload 下的 Radio 或 Checkbox 会触发两次弹窗的问题。[#51874](https://github.com/ant-design/ant-design/pull/51874)
- 💄 修复 Menu 在使用 `collapsedIconSize` 时图标对齐的问题。[#51863](https://github.com/ant-design/ant-design/pull/51863) [@Gnomeek](https://github.com/Gnomeek)
- 💄 修复 Tabs 组件在 `type="editable-card"` 时样式不正确的问题。[#51935](https://github.com/ant-design/ant-design/pull/51935) [@aojunhao123](https://github.com/aojunhao123)
- 💄 修复 Layout.Sider 组件在 `zero-width` 模式下触发器样式优先级不足的问题。[#51936](https://github.com/ant-design/ant-design/pull/51936) [@aojunhao123](https://github.com/aojunhao123)
- 💄 MISC: 修复 icon 样式被重复创建的问题。[#51897](https://github.com/ant-design/ant-design/pull/51897) [@YumoImer](https://github.com/YumoImer)
- 💄 MISC: 行内样式重构为 cssinjs。[#51843](https://github.com/ant-design/ant-design/pull/51843)
## 5.22.3
`2024-12-02`
- 🐞 修复 Select 清除按钮在 Form.Item 中位置可能错误的问题。[#51649](https://github.com/ant-design/ant-design/pull/51649) [@dislido](https://github.com/dislido)
- 🐞 修复 InputNumber `handleVisible` token 不生效的问题。[#51728](https://github.com/ant-design/ant-design/pull/51728) [@dengfuping](https://github.com/dengfuping)
- 🐞 修复 ColorPicker 的 `presets` 属性中的 `label` 字段传入 `ReactNode` 会报错的问题。[#51808](https://github.com/ant-design/ant-design/pull/51808) [@li-jia-nan](https://github.com/li-jia-nan)
- 🐞 修复 Menu 的 `inlineCollapsed` 属性在 Layout 中不生效的问题。[#51775](https://github.com/ant-design/ant-design/pull/51775) [@coderz-w](https://github.com/coderz-w)
- 🐞 修复 Table `onHeaderCell` 提供的 `style` 无法被覆盖的问题。[#51793](https://github.com/ant-design/ant-design/pull/51793) [@Wxh16144](https://github.com/Wxh16144)
- ⌨️ 优化 Collapse 组件的可访问性。[#51836](https://github.com/ant-design/ant-design/pull/51836) [@aojunhao123](https://github.com/aojunhao123)
- TypeScript
- 🤖 增加 Table 属性中 `clearFilters` 函数的入参类型。[#51754](https://github.com/ant-design/ant-design/pull/51754) [@fubd](https://github.com/fubd)
- 🤖 修复 Form 设置 `preserve``false`Form.List 中嵌套的字段卸载会丢失值的问题。[#51796](https://github.com/ant-design/ant-design/pull/51796) [@zombieJ](https://github.com/zombieJ)
## 5.22.2
`2024-11-21`
- 🐞 修复 Input.OTP 组件在有非法输入时仍会切换到下一个输入框的问题。[#51664](https://github.com/ant-design/ant-design/pull/51664) [@thecodesalim](https://github.com/thecodesalim)
- 🐞 调整 Modal 确认函数,使其在弹出后聚焦确认按钮时不要滚动窗体。[#51647](https://github.com/ant-design/ant-design/pull/51647) [@zombieJ](https://github.com/zombieJ)
- 🐞 修复 Form `rules` 生成多条相同错误时会报 React 渲染错误的问题。[#51636](https://github.com/ant-design/ant-design/pull/51636) [@zombieJ](https://github.com/zombieJ)
- 🐞 调整 Button 使用 `useEffect` 来触发 `autoFocus` 逻辑,以解决一些异步渲染场景下 Button 无法自动聚焦的问题。[#51624](https://github.com/ant-design/ant-design/pull/51624) [@zombieJ](https://github.com/zombieJ)
- 🐞 修复 Button 中使用自定义三方图标库时图标未居中的问题。[#51652](https://github.com/ant-design/ant-design/pull/51652) [@afc163](https://github.com/afc163)
- 🐞 修复 Table 组件 `getCheckboxProps` 中的事件处理器被内部选择逻辑覆盖的问题。[#51661](https://github.com/ant-design/ant-design/pull/51661) [@Zyf665](https://github.com/Zyf665)
- 🐞 修复 Tree 组件的 `onCheck``onSelect` 事件没有被正确触发的问题。[#51448](https://github.com/ant-design/ant-design/pull/51448) [@Wxh16144](https://github.com/Wxh16144)
- 🐞 修复 Input 组件的清除按钮未能垂直居中的问题。[#51700](https://github.com/ant-design/ant-design/pull/51700) [@jynxio](https://github.com/jynxio)
- 🐞 修复 Select 组件的 `prefix` 组合导致的颜色、折行、状态等一系列样式问题。[#51694](https://github.com/ant-design/ant-design/pull/51694) [@zombieJ](https://github.com/zombieJ)
- 🌐 本地化
- 🇷🇺 添加了俄语翻译支持。[#51619](https://github.com/ant-design/ant-design/pull/51619) [@avvakumovid](https://github.com/avvakumovid)
- 🇮🇹 为 TimePicker 添加了意大利语翻译。[#51685](https://github.com/ant-design/ant-design/pull/51685) [@LorenzoCardinali](https://github.com/LorenzoCardinali)
## 5.22.1
`2024-11-13`
- 🛠 调整 DatePicker.RangePicker 当 `needConfirm` 切用户未提交日期时,不允许通过点击输入框切换到下一个字段。[#51591](https://github.com/ant-design/ant-design/pull/51591) [@zombieJ](https://github.com/zombieJ)
- 🛠 禁用 Input.OTP `ctrl + z` 操作以防止数据变化非预期的问题。[#51609](https://github.com/ant-design/ant-design/pull/51609) [@zombieJ](https://github.com/zombieJ)
- 🐞 修复 Select 标签模式下展示异常的问题。[#51605](https://github.com/ant-design/ant-design/pull/51605) [@guoyunhe](https://github.com/guoyunhe)
- 🐞 修复 Badge `count` 在 Safari 下动画丢失的问题。[#51598](https://github.com/ant-design/ant-design/pull/51598) [@zombieJ](https://github.com/zombieJ)
- 🐞 修复 Tabs `centered` 下标签展示不全的问题。[#51571](https://github.com/ant-design/ant-design/pull/51571) [@DDDDD12138](https://github.com/DDDDD12138)
- 🐞 修复 Transfer 受控 `dataSource``selectedKeys` 时,偶尔会出现勾选不正确的问题。[#51523](https://github.com/ant-design/ant-design/pull/51523) [@IsKaros](https://github.com/IsKaros)
- 🐞 回滚 Button `display``inline-flex``inline-block` 以解决 Icon 位置偏移的问题。[#51588](https://github.com/ant-design/ant-design/pull/51588) [@Wxh16144](https://github.com/Wxh16144)
## 5.22.0
`2024-11-12`
- Form
- 🆕 Form.Item 支持隐藏 label。[#51524](https://github.com/ant-design/ant-design/pull/51524) [@crazyair](https://github.com/crazyair)
- 🐞 Form 移除了用于撑开 error 高度的 div将 errorDom 和 extraDom 用一个 div 包裹,并为该 div 设置了最小高度。[#51254](https://github.com/ant-design/ant-design/pull/51254) [@hongzzz](https://github.com/hongzzz)
- 🐞 修复 Form 在字段触发 change 但是值没有变化时,`onValuesChange` 仍然会触发的问题。[#51437](https://github.com/ant-design/ant-design/pull/51437) [@crazyair](https://github.com/crazyair)
- 🆕 Form 支持在表单验证失败时scrollToFirstError 中的 focus 属性。[#51231](https://github.com/ant-design/ant-design/pull/51231) [@nathanlao](https://github.com/nathanlao)
- Table
- 🆕 Table 列过滤下拉框支持 `filterDropdownProps`。[#51297](https://github.com/ant-design/ant-design/pull/51297) [@Wxh16144](https://github.com/Wxh16144)
- 🆕 Table `expandedRowClassName` 支持 string 。[#51067](https://github.com/ant-design/ant-design/pull/51067) [@li-jia-nan](https://github.com/li-jia-nan)
- Tree
- 💄 修复 Tree 选中节点丢失 padding 样式的问题。[#51492](https://github.com/ant-design/ant-design/pull/51492) [@zombieJ](https://github.com/zombieJ)
- 🆕 Tree 组件 Token 增加 `nodeHoverColor``nodeSelectedColor` 支持。[#51367](https://github.com/ant-design/ant-design/pull/51367) [@zmbxy](https://github.com/zmbxy)
- 🆕 Tree 新增 `indentSize` token 用于自定义缩进宽度。[#51010](https://github.com/ant-design/ant-design/pull/51010) [@afc163](https://github.com/afc163)
- DatePicker
- 🆕 DatePicker 支持 `prefix` 属性。[#51335](https://github.com/ant-design/ant-design/pull/51335) [@guoyunhe](https://github.com/guoyunhe)
- 💄 修复 DatePicker.RangePicker 当鼠标移动到单元格之间时出现闪烁样式的问题。[#51533](https://github.com/ant-design/ant-design/pull/51533) [@afc163](https://github.com/afc163)
- Input.OTP
- 🆕 Input.OTP 组件新增 `onInput` 事件用于获取用户每一次输入的值。[#51289](https://github.com/ant-design/ant-design/pull/51289) [@aojunhao123](https://github.com/aojunhao123)
- 🐞 修复 Input.OTP 无法指定 `inputMode` 的问题。[#51271](https://github.com/ant-design/ant-design/pull/51271) [@alan-rudzinski](https://github.com/alan-rudzinski)
- 🆕 ColorPicker 支持 `disabledFormat` 属性以禁用格式切换器。[#51539](https://github.com/ant-design/ant-design/pull/51539) [@su-muzhi](https://github.com/su-muzhi)
- 🆕 为 InputNumber 组件的 `focus` 方法增加 `cursor` 配置项以控制光标位置。[#51444](https://github.com/ant-design/ant-design/pull/51444) [@aojunhao123](https://github.com/aojunhao123)
- 🆕 Cascader 新增 `disabled` 属性以禁用组件的所有一级目录项。[#51272](https://github.com/ant-design/ant-design/pull/51272) [@aojunhao123](https://github.com/aojunhao123)
- 🆕 Descriptions 支持单行铺满。[#51365](https://github.com/ant-design/ant-design/pull/51365) [@crazyair](https://github.com/crazyair)
- 🆕 Select/TreeSelect/Cascader 组件增加 `prefix` 属性以支持自定义前缀。[#51186](https://github.com/ant-design/ant-design/pull/51186) [@guoyunhe](https://github.com/guoyunhe)
- 🐞 修复 Image 设置 `ImageProps.preview.rootClassName` 导致预览图类名丢失。[#51538](https://github.com/ant-design/ant-design/pull/51538) [@dislido](https://github.com/dislido)
- 🐞 修复 TimePicker 面板列的最后一项无法滚动到最上面的问题。[#51481](https://github.com/ant-design/ant-design/pull/51481) [@zombieJ](https://github.com/zombieJ)
- 🐞 修复 TreeSelect 弹层高度不够的问题。[#51567](https://github.com/ant-design/ant-design/pull/51567) [@afc163](https://github.com/afc163)
- 🐞 修复 Typography 在 ConfigProvider 语言切换时候没有立即更新。[#51453](https://github.com/ant-design/ant-design/pull/51453) [@thinkasany](https://github.com/thinkasany)
- 🐞 修复 Upload `itemRender` 调用 `action.preview` 会导致崩溃的问题。[#51419](https://github.com/ant-design/ant-design/pull/51419) [@yoyo837](https://github.com/yoyo837)
- 🐞 修复 Splitter 伪元素符号问题。[#51536](https://github.com/ant-design/ant-design/pull/51536) [@dislido](https://github.com/dislido)
- 💄 优化 Collapse 可访问性属性和鼠标 hover 样式。[#51400](https://github.com/ant-design/ant-design/pull/51400) [@afc163](https://github.com/afc163)
- 💄 修复 Menu title 内容的样式问题。[#51425](https://github.com/ant-design/ant-design/pull/51425) [@coding-ice](https://github.com/coding-ice)
- 🇵🇹 修正葡萄牙语 (pt_PT) 本地化文件中的翻译,以提高准确性和一致性。[#51501](https://github.com/ant-design/ant-design/pull/51501) [@alexandre-p-marques-alb](https://github.com/alexandre-p-marques-alb)
- 🇺🇿 优化 uz_UZ 国际化。[#51407](https://github.com/ant-design/ant-design/pull/51407) [@Zukhrik](https://github.com/Zukhrik)
- TypeScript
- 🤖 Upload 导出类型 DraggerProps。[#51546](https://github.com/ant-design/ant-design/pull/51546) [@DBvc](https://github.com/DBvc)
- 🤖 将 defaultValue 属性添加到 TimePicker.RangePicker 示例中。[#51413](https://github.com/ant-design/ant-design/pull/51413) [@nathanlao](https://github.com/nathanlao)
- 🤖 Message 优化 message.config 中的 top 类型。[#51468](https://github.com/ant-design/ant-design/pull/51468) [@Fog3211](https://github.com/Fog3211)
- 🤖 优化 Tree 和 TreeSelect 的 TS 定义。[#51251](https://github.com/ant-design/ant-design/pull/51251) [@afc163](https://github.com/afc163)
## 5.21.6
`2024-10-28`
@ -52,6 +252,7 @@ tag: vVERSION
- 💄 修改 Button `textHoverBg` 在悬浮状态下的背景色为 `colorFillTertiary`。[#51187](https://github.com/ant-design/ant-design/pull/51187) [@coding-ice](https://github.com/coding-ice)
- TypeScript
- 🤖 优化 Switch `eventHandler` 类型。[#51165](https://github.com/ant-design/ant-design/pull/51165) [@thinkasany](https://github.com/thinkasany)
## 5.21.3
`2024-10-09`
@ -100,17 +301,17 @@ tag: vVERSION
<img width="520" alt="Splitter" src="https://github.com/user-attachments/assets/25fc4e3c-1aa5-41bb-8f39-f34f7149e0f6">
- Button
- 🔥 Button 支持 `variant` 变体和 `color` 颜色属性,以支持更多组合样式。[#50051](https://github.com/ant-design/ant-design/pull/50051) [@coding-ice](https://github.com/coding-ice)
<img width="420" alt="Button" src="https://github.com/user-attachments/assets/cd5cb7fb-25e8-445f-b217-7fdd4ae0f9b4">
<img width="420" alt="Button" src="https://github.com/user-attachments/assets/cd5cb7fb-25e8-445f-b217-7fdd4ae0f9b4">
- 💄 Button 添加 `textColor`、`textHoverColor` 和 `textActiveColor` 三个 token。[#47870](https://github.com/ant-design/ant-design/pull/47870) [@madocto](https://github.com/madocto)
- FloatButton
- 🆕 FloatButton 组件支持 `placement` 属性,支持从四个方向弹出菜单。(实现方式改为 `position: absolute` + flex 布局,可能会对你现有的布局造成 breaking change请注意兼容[#50407](https://github.com/ant-design/ant-design/pull/50407) [@li-jia-nan](https://github.com/li-jia-nan)
<img width="300" alt="float button" src="https://github.com/user-attachments/assets/4b53c0f6-7bdd-4e2a-91cc-2fb804f6e6d3" />
<img width="300" alt="float button" src="https://github.com/user-attachments/assets/4b53c0f6-7bdd-4e2a-91cc-2fb804f6e6d3" />
- 💄 统一 FloatButton 和 FloatButton.Group 的按钮圆角。[#50513](https://github.com/ant-design/ant-design/pull/50513) [@Layouwen](https://github.com/Layouwen)
- 💄 FloatButton 组件的 `z-index` 加入 `useZIndex` 管理,兼容弹层类组件。[#50311](https://github.com/ant-design/ant-design/pull/50311) [@li-jia-nan](https://github.com/li-jia-nan)
- 🆕 FloatButton 支持传入 `htmlType` 属性。[#50892](https://github.com/ant-design/ant-design/pull/50892) [@li-jia-nan](https://github.com/li-jia-nan)
- Menu
- 🆕 Menu.Item 和 Dropdown 的 menu 支持 `extra` 属性。[#50431](https://github.com/ant-design/ant-design/pull/50431) [@coding-ice](https://github.com/coding-ice)
<img width="259" alt="menu extra" src="https://github.com/user-attachments/assets/fee57c43-b948-4f98-8a6b-0d94094a8a65">
<img width="259" alt="menu extra" src="https://github.com/user-attachments/assets/fee57c43-b948-4f98-8a6b-0d94094a8a65">
- 🐞 修复 Menu `popupStyle` 在 SubMenu 上失效的问题。[#50922](https://github.com/ant-design/ant-design/pull/50922) [@Wxh16144](https://github.com/Wxh16144)
- Table
- 🆕 Table 列支持配置 `minWidth` 属性。[#50416](https://github.com/ant-design/ant-design/pull/50416) [@linxianxi](https://github.com/linxianxi)
@ -1902,7 +2103,7 @@ tag: vVERSION
- 💄 调整 Select, TreeSelect, Cascader 在多选时总是默认显示下拉箭头。[#41028](https://github.com/ant-design/ant-design/pull/41028)
- 🐞 修复 Form 组件 `Form.Item.useStatus` 导致的服务端渲染问题。[#40977](https://github.com/ant-design/ant-design/pull/40977) [@AndyBoat](https://github.com/AndyBoat)
- 🐞 杂项:修复部分组件箭头形状问题。[#40971](https://github.com/ant-design/ant-design/pull/40971)
- 🐞 修复 Layout 报错 `React does not recognize the `suffixCls` prop on a DOM element` 的问题。[#40969](https://github.com/ant-design/ant-design/pull/40969)
- 🐞 修复 Layout 报错 "React does not recognize the `suffixCls` prop on a DOM element" 的问题。[#40969](https://github.com/ant-design/ant-design/pull/40969)
- 🐞 修复 Watermark 组件图片加载异常时的问题,默认展示文字。[#40770](https://github.com/ant-design/ant-design/pull/40770) [@OriginRing](https://github.com/OriginRing)
- 🐞 Image 预览新增图片翻转功能。并修复 Image `fallback` 在 ssr 下失效的问题。[#40660](https://github.com/ant-design/ant-design/pull/40660)
- 🐞 修复 Select 中使用 Typography 不居中的问题。[#40422](https://github.com/ant-design/ant-design/pull/40422) [@Yuiai01](https://github.com/Yuiai01)

View File

@ -18,8 +18,8 @@
[![](https://opencollective.com/ant-design/tiers/sponsors.svg?avatarHeight=72)](https://opencollective.com/ant-design/contribute/sponsors-218/checkout) [![](https://opencollective.com/ant-design/tiers/backers.svg?avatarHeight=72)](https://opencollective.com/ant-design/contribute/backers-217/checkout)
[npm-image]: http://img.shields.io/npm/v/antd.svg?style=flat-square
[npm-url]: http://npmjs.org/package/antd
[npm-image]: https://img.shields.io/npm/v/antd.svg?style=flat-square
[npm-url]: https://npmjs.org/package/antd
[github-action-image]: https://github.com/ant-design/ant-design/workflows/%E2%9C%85%20test/badge.svg
[github-action-url]: https://github.com/ant-design/ant-design/actions?query=workflow%3A%22%E2%9C%85+test%22
[codecov-image]: https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square
@ -65,7 +65,7 @@
- 支持服务端渲染。
- [Electron](https://www.electronjs.org/)
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Electron |
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="Edge" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)<br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)<br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)<br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)<br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)<br>Electron |
| --- | --- | --- | --- | --- |
| Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
@ -115,18 +115,17 @@ export default App;
- [首页](https://ant.design/)
- [所有组件](https://ant.design/components/overview-cn)
- [Ant Design Pro](http://pro.ant.design/)
- [更新日志](CHANGELOG.zh-CN.md)
- [React 底层基础组件](http://react-component.github.io/)
- [移动端组件](http://mobile.ant.design)
- [小程序组件](http://mini.ant.design)
- [页面级组件](https://procomponents.ant.design)
- [Ant Design 图表](https://charts.ant.design)
- [Ant Design 图标](https://github.com/ant-design/ant-design-icons)
- [Ant Design 色彩](https://github.com/ant-design/ant-design-colors)
- [首页模板集](https://landing.ant.design)
- [React 底层基础组件](https://react-component.github.io/)
- [🆕 Ant Design X](https://x.ant.design/index-cn)
- [Ant Design Pro](https://pro.ant.design/)
- [Pro Components](https://procomponents.ant.design)
- [Ant Design Mobile](https://mobile.ant.design)
- [Ant Design Mini](https://mini.ant.design)
- [Ant Design Charts](https://charts.ant.design)
- [Ant Design Web3](https://web3.ant.design)
- [动效](https://motion.ant.design)
- [脚手架市场](http://scaffold.ant.design)
- [脚手架市场](https://scaffold.ant.design)
- [设计规范速查手册](https://github.com/ant-design/ant-design/wiki/Ant-Design-%E8%AE%BE%E8%AE%A1%E5%9F%BA%E7%A1%80%E7%AE%80%E7%89%88)
- [开发者说明](https://github.com/ant-design/ant-design/wiki/Development)
- [版本发布规则](https://github.com/ant-design/ant-design/wiki/%E8%BD%AE%E5%80%BC%E8%A7%84%E5%88%99%E5%92%8C%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E6%B5%81%E7%A8%8B)
@ -153,7 +152,7 @@ $ npm start
打开浏览器访问 http://127.0.0.1:8001 ,更多[本地开发文档](https://github.com/ant-design/ant-design/wiki/Development)。
## 🤝 参与共建 [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
## 🤝 参与共建 [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com)
<table>
<tr>
@ -188,7 +187,7 @@ $ npm start
请参考[贡献指南](https://ant.design/docs/react/contributing-cn).
> 强烈推荐阅读 [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way)、[《如何向开源社区提问题》](https://github.com/seajs/seajs/issues/545) 和 [《如何有效地报告 Bug》](http://www.chiark.greenend.org.uk/%7Esgtatham/bugs-cn.html)、[《如何向开源项目提交无法解答的问题》](https://zhuanlan.zhihu.com/p/25795393),更好的问题更容易获得帮助。
> 强烈推荐阅读 [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way)、[《如何向开源社区提问题》](https://github.com/seajs/seajs/issues/545) 和 [《如何有效地报告 Bug》](https://www.chiark.greenend.org.uk/%7Esgtatham/bugs-cn.html)、[《如何向开源项目提交无法解答的问题》](https://zhuanlan.zhihu.com/p/25795393),更好的问题更容易获得帮助。
[![赞助链接](https://raw.githubusercontent.com/BoostIO/issuehunt-materials/master/v1/issuehunt-button-v1.svg)](https://issuehunt.io/repos/34526884)
@ -201,7 +200,7 @@ $ npm start
通过 Stack Overflow 或者 Segment Fault 提问时,建议加上 `antd` 标签。
1. [GitHub Discussions](https://github.com/ant-design/ant-design/discussions)
2. [Stack Overflow](http://stackoverflow.com/questions/tagged/antd)(英文)
2. [Stack Overflow](https://stackoverflow.com/questions/tagged/antd)(英文)
3. [Segment Fault](https://segmentfault.com/t/antd)(中文)
## Issue 赞助

View File

@ -18,8 +18,8 @@ An enterprise-class UI design language and React UI library.
[![](https://opencollective.com/ant-design/tiers/sponsors.svg?avatarHeight=72)](https://opencollective.com/ant-design/contribute/sponsors-218/checkout) [![](https://opencollective.com/ant-design/tiers/backers.svg?avatarHeight=72)](https://opencollective.com/ant-design/contribute/backers-217/checkout)
[npm-image]: http://img.shields.io/npm/v/antd.svg?style=flat-square
[npm-url]: http://npmjs.org/package/antd
[npm-image]: https://img.shields.io/npm/v/antd.svg?style=flat-square
[npm-url]: https://npmjs.org/package/antd
[github-action-image]: https://github.com/ant-design/ant-design/workflows/%E2%9C%85%20test/badge.svg
[github-action-url]: https://github.com/ant-design/ant-design/actions?query=workflow%3A%22%E2%9C%85+test%22
[codecov-image]: https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square
@ -63,7 +63,7 @@ An enterprise-class UI design language and React UI library.
- Server-side Rendering
- [Electron](https://www.electronjs.org/)
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)<br>Electron |
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="Edge" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)<br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)<br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)<br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)<br>Safari | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/electron/electron_48x48.png" alt="Electron" width="24px" height="24px" />](https://godban.github.io/browsers-support-badges/)<br>Electron |
| --- | --- | --- | --- | --- |
| Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
@ -98,18 +98,18 @@ export default () => (
- [Home page](https://ant.design/)
- [Components Overview](https://ant.design/components/overview)
- [Ant Design Pro](http://pro.ant.design/)
- [Change Log](CHANGELOG.en-US.md)
- [rc-components](http://react-component.github.io/)
- [Mobile UI](http://mobile.ant.design)
- [Mini Program UI](http://mini.ant.design)
- [Ant Design Pro Components](https://procomponents.ant.design)
- [rc-components](https://react-component.github.io/)
- [🆕 Ant Design X](https://x.ant.design/index-cn)
- [Ant Design Pro](https://pro.ant.design/)
- [Pro Components](https://procomponents.ant.design)
- [Ant Design Mobile](https://mobile.ant.design)
- [Ant Design Mini](https://mini.ant.design)
- [Ant Design Charts](https://charts.ant.design)
- [Ant Design Icons](https://github.com/ant-design/ant-design-icons)
- [Ant Design Colors](https://github.com/ant-design/ant-design-colors)
- [Ant Design Web3](https://web3.ant.design)
- [Landing Pages](https://landing.ant.design)
- [Motion](https://motion.ant.design)
- [Scaffold Market](http://scaffold.ant.design)
- [Ant Motion](https://motion.ant.design)
- [Scaffold Market](https://scaffold.ant.design)
- [Developer Instruction](https://github.com/ant-design/ant-design/wiki/Development)
- [Versioning Release Note](https://github.com/ant-design/ant-design/wiki/%E8%BD%AE%E5%80%BC%E8%A7%84%E5%88%99%E5%92%8C%E7%89%88%E6%9C%AC%E5%8F%91%E5%B8%83%E6%B5%81%E7%A8%8B)
- [FAQ](https://ant.design/docs/react/faq)
@ -134,7 +134,7 @@ $ npm start
Open your browser and visit http://127.0.0.1:8001 , see more at [Development](https://github.com/ant-design/ant-design/wiki/Development).
## 🤝 Contributing [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
## 🤝 Contributing [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com)
<table>
<tr>

View File

@ -65,6 +65,11 @@
}
}
},
"css": {
"formatter": {
"quoteStyle": "single"
}
},
"overrides": [
{
"include": ["**/*.test.ts", "**/*.test.tsx", "tests/**/*", "scripts/**/*", ".dumi/**/*"],

View File

@ -74,6 +74,7 @@ exports[`antd exports modules correctly 1`] = `
"message",
"notification",
"theme",
"unstableSetRender",
"version",
]
`;

View File

@ -0,0 +1,44 @@
import * as ReactDOM from 'react-dom';
import { Modal, unstableSetRender } from 'antd';
import { waitFakeTimer19 } from '../../tests/utils';
// TODO: Remove this. Mock for React 19
jest.mock('react-dom', () => {
const realReactDOM = jest.requireActual('react-dom');
if (realReactDOM.version.startsWith('19')) {
const realReactDOMClient = jest.requireActual('react-dom/client');
realReactDOM.createRoot = realReactDOMClient.createRoot;
}
return realReactDOM;
});
describe('unstable', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
it('unstableSetRender', async () => {
if (ReactDOM.version.startsWith('19')) {
unstableSetRender((node, container) => {
const root = (ReactDOM as any).createRoot(container);
root.render(node);
return async () => {
root.unmount();
};
});
Modal.info({ content: 'unstableSetRender' });
await waitFakeTimer19();
expect(document.querySelector('.ant-modal')).toBeTruthy();
}
});
});

View File

@ -52,7 +52,9 @@ const ActionButton: React.FC<ActionButtonProps> = (props) => {
let timeoutId: ReturnType<typeof setTimeout> | null = null;
if (autoFocus) {
timeoutId = setTimeout(() => {
buttonRef.current?.focus();
buttonRef.current?.focus({
preventScroll: true,
});
});
}
return () => {

View File

@ -20,9 +20,10 @@ export interface BaseProps {
/* istanbul ignore next */
const genPurePanel = <ComponentProps extends BaseProps = BaseProps>(
Component: any,
defaultPrefixCls?: string,
getDropdownCls?: null | ((prefixCls: string) => string),
alignPropName?: 'align' | 'dropdownAlign' | 'popupAlign',
postProps?: (props: ComponentProps) => ComponentProps,
defaultPrefixCls?: string,
getDropdownCls?: (prefixCls: string) => string,
) => {
type WrapProps = ComponentProps & AnyObject;
@ -55,7 +56,6 @@ const genPurePanel = <ComponentProps extends BaseProps = BaseProps>(
? `.${getDropdownCls(prefixCls)}`
: `.${prefixCls}-dropdown`;
const popup = holderRef.current?.querySelector(dropdownCls);
if (popup) {
clearInterval(interval);
resizeObserver.observe(popup);
@ -83,6 +83,16 @@ const genPurePanel = <ComponentProps extends BaseProps = BaseProps>(
if (postProps) {
mergedProps = postProps(mergedProps);
}
if (alignPropName) {
Object.assign(mergedProps, {
[alignPropName]: {
overflow: {
adjustX: false,
adjustY: false,
},
},
});
}
const mergedStyle: React.CSSProperties = {
paddingBottom: popupHeight,
position: 'relative',

View File

@ -28,6 +28,18 @@ import { consumerBaseZIndexOffset, containerBaseZIndexOffset, useZIndex } from '
import { resetWarned } from '../warning';
import zIndexContext from '../zindexContext';
// TODO: Remove this. Mock for React 19
jest.mock('react-dom', () => {
const realReactDOM = jest.requireActual('react-dom');
if (realReactDOM.version.startsWith('19')) {
const realReactDOMClient = jest.requireActual('react-dom/client');
realReactDOM.createRoot = realReactDOMClient.createRoot;
}
return realReactDOM;
});
const WrapWithProvider: React.FC<PropsWithChildren<{ container: ZIndexContainer }>> = ({
children,
container,

View File

@ -9,6 +9,18 @@ import { TARGET_CLS } from '../wave/interface';
(global as any).isVisible = true;
// TODO: Remove this. Mock for React 19
jest.mock('react-dom', () => {
const realReactDOM = jest.requireActual('react-dom');
if (realReactDOM.version.startsWith('19')) {
const realReactDOMClient = jest.requireActual('react-dom/client');
realReactDOM.createRoot = realReactDOMClient.createRoot;
}
return realReactDOM;
});
jest.mock('rc-util/lib/Dom/isVisible', () => {
const mockFn = () => (global as any).isVisible;
return mockFn;
@ -96,6 +108,7 @@ describe('Wave component', () => {
expect(document.querySelector('.ant-wave')).toBeFalsy();
expect(errorSpy).not.toHaveBeenCalled();
errorSpy.mockRestore();
unmount();
});

View File

@ -0,0 +1,3 @@
const isPrimitive = (value: unknown) => (typeof value !== 'object' && typeof value !== 'function') || value === null;
export default isPrimitive;

View File

@ -69,13 +69,13 @@ export default function useResponsiveObserver() {
return React.useMemo(() => {
const subscribers = new Map<number, SubscribeFunc>();
let subUid = -1;
let screens = {};
let screens: Partial<Record<Breakpoint, boolean>> = {};
return {
matchHandlers: {} as {
[prop: string]: {
mql: MediaQueryList;
listener: ((this: MediaQueryList, ev: MediaQueryListEvent) => any) | null;
listener: (this: MediaQueryList, ev: MediaQueryListEvent) => void;
};
},
dispatch(pointMap: ScreenMap) {
@ -84,7 +84,9 @@ export default function useResponsiveObserver() {
return subscribers.size >= 1;
},
subscribe(func: SubscribeFunc): number {
if (!subscribers.size) this.register();
if (!subscribers.size) {
this.register();
}
subUid += 1;
subscribers.set(subUid, func);
func(screens);
@ -92,7 +94,9 @@ export default function useResponsiveObserver() {
},
unsubscribe(paramToken: number) {
subscribers.delete(paramToken);
if (!subscribers.size) this.unregister();
if (!subscribers.size) {
this.unregister();
}
},
unregister() {
Object.keys(responsiveMap).forEach((screen) => {
@ -117,7 +121,6 @@ export default function useResponsiveObserver() {
mql,
listener,
};
listener(mql);
});
},
@ -129,7 +132,7 @@ export default function useResponsiveObserver() {
export const matchScreen = (screens: ScreenMap, screenSizes?: ScreenSizeMap) => {
if (screenSizes && typeof screenSizes === 'object') {
for (let i = 0; i < responsiveArray.length; i++) {
const breakpoint: Breakpoint = responsiveArray[i];
const breakpoint = responsiveArray[i];
if (screens[breakpoint] && screenSizes[breakpoint] !== undefined) {
return screenSizes[breakpoint];
}

View File

@ -2,9 +2,9 @@ import * as React from 'react';
import classNames from 'classnames';
import CSSMotion from 'rc-motion';
import raf from 'rc-util/lib/raf';
import { render, unmount } from 'rc-util/lib/React/render';
import { composeRef } from 'rc-util/lib/ref';
import { getReactRender, type UnmountType } from '../../config-provider/UnstableContext';
import { TARGET_CLS } from './interface';
import type { ShowWaveEffect } from './interface';
import { getTargetWaveColor } from './util';
@ -17,12 +17,21 @@ export interface WaveEffectProps {
className: string;
target: HTMLElement;
component?: string;
registerUnmount: () => UnmountType | null;
}
const WaveEffect: React.FC<WaveEffectProps> = (props) => {
const { className, target, component } = props;
const WaveEffect = (props: WaveEffectProps) => {
const { className, target, component, registerUnmount } = props;
const divRef = React.useRef<HTMLDivElement>(null);
// ====================== Refs ======================
const unmountRef = React.useRef<UnmountType>(null);
React.useEffect(() => {
unmountRef.current = registerUnmount();
}, []);
// ===================== Effect =====================
const [color, setWaveColor] = React.useState<string | null>(null);
const [borderRadius, setBorderRadius] = React.useState<number[]>([]);
const [left, setLeft] = React.useState(0);
@ -119,7 +128,7 @@ const WaveEffect: React.FC<WaveEffectProps> = (props) => {
onAppearEnd={(_, event) => {
if (event.deadline || (event as TransitionEvent).propertyName === 'opacity') {
const holder = divRef.current?.parentElement!;
unmount(holder).then(() => {
unmountRef.current?.().then(() => {
holder?.remove();
});
}
@ -152,7 +161,18 @@ const showWaveEffect: ShowWaveEffect = (target, info) => {
holder.style.top = '0px';
target?.insertBefore(holder, target?.firstChild);
render(<WaveEffect {...info} target={target} />, holder);
const reactRender = getReactRender();
let unmountCallback: UnmountType | null = null;
function registerUnmount() {
return unmountCallback;
}
unmountCallback = reactRender(
<WaveEffect {...info} target={target} registerUnmount={registerUnmount} />,
holder,
);
};
export default showWaveEffect;

View File

@ -1,7 +1,7 @@
import React, { useContext, useRef } from 'react';
import classNames from 'classnames';
import isVisible from 'rc-util/lib/Dom/isVisible';
import { composeRef, supportRef } from 'rc-util/lib/ref';
import { composeRef, getNodeRef, supportRef } from 'rc-util/lib/ref';
import type { ConfigConsumerProps } from '../../config-provider';
import { ConfigContext } from '../../config-provider';
@ -19,7 +19,7 @@ export interface WaveProps {
const Wave: React.FC<WaveProps> = (props) => {
const { children, disabled, component } = props;
const { getPrefixCls } = useContext<ConfigConsumerProps>(ConfigContext);
const containerRef = useRef<HTMLElement>(null);
const containerRef = useRef<HTMLElement>(null!);
// ============================== Style ===============================
const prefixCls = getPrefixCls('wave');
@ -64,7 +64,7 @@ const Wave: React.FC<WaveProps> = (props) => {
return children ?? null;
}
const ref = supportRef(children) ? composeRef((children as any).ref, containerRef) : containerRef;
const ref = supportRef(children) ? composeRef(getNodeRef(children), containerRef) : containerRef;
return cloneElement(children, { ref });
};

View File

@ -28,10 +28,16 @@ const useWave = (
const { showEffect } = wave || {};
// Customize wave effect
(showEffect || showWaveEffect)(targetNode, { className, token, component, event, hashId });
(showEffect || showWaveEffect)(targetNode, {
className,
token,
component,
event,
hashId,
});
});
const rafId = React.useRef<number>();
const rafId = React.useRef<number>(null);
// Merge trigger event into one for each frame
const showDebounceWave: ShowWave = (event) => {

View File

@ -1,5 +1,7 @@
import { imageDemoTest } from '../../../tests/shared/imageTest';
describe('Affix image', () => {
imageDemoTest('affix');
imageDemoTest('affix', {
onlyViewport: ['debug.tsx'],
});
});

View File

@ -80,7 +80,7 @@ const Affix = React.forwardRef<AffixRef, AffixProps>((props, ref) => {
const status = React.useRef<AffixStatus>(AFFIX_STATUS_NONE);
const prevTarget = React.useRef<Window | HTMLElement | null>(null);
const prevListener = React.useRef<EventListener>();
const prevListener = React.useRef<EventListener>(null);
const placeholderNodeRef = React.useRef<HTMLDivElement>(null);
const fixedNodeRef = React.useRef<HTMLDivElement>(null);

View File

@ -76,9 +76,14 @@ const IconNode: React.FC<IconNodeProps> = (props) => {
const iconType = iconMapFilled[type!] || null;
if (icon) {
return replaceElement(icon, <span className={`${prefixCls}-icon`}>{icon}</span>, () => ({
className: classNames(`${prefixCls}-icon`, {
[(icon as ReactElement).props.className]: (icon as ReactElement).props.className,
}),
className: classNames(
`${prefixCls}-icon`,
(
icon as ReactElement<{
className?: string;
}>
).props.className,
),
})) as ReactElement;
}
return React.createElement(iconType, { className: `${prefixCls}-icon` });

View File

@ -5,7 +5,7 @@ import { resetWarned } from 'rc-util/lib/warning';
import Alert from '..';
import { accessibilityTest } from '../../../tests/shared/accessibilityTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { act, render, screen, waitFakeTimer } from '../../../tests/utils';
import { act, fireEvent, render, screen, waitFakeTimer } from '../../../tests/utils';
import Button from '../../button';
import Popconfirm from '../../popconfirm';
import Tooltip from '../../tooltip';
@ -28,7 +28,7 @@ describe('Alert', () => {
it('should show close button and could be closed', async () => {
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const onClose = jest.fn();
render(
const { container } = render(
<Alert
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
type="warning"
@ -37,10 +37,7 @@ describe('Alert', () => {
/>,
);
await act(async () => {
await userEvent.click(screen.getByRole('button', { name: /close/i }));
jest.runAllTimers();
});
fireEvent.click(container.querySelector('.ant-alert-close-icon')!);
expect(onClose).toHaveBeenCalledTimes(1);
expect(errSpy).not.toHaveBeenCalled();

View File

@ -381,8 +381,6 @@ describe('Anchor Render', () => {
},
]}
/>,
// https://github.com/testing-library/react-testing-library/releases/tag/v13.0.0
{ legacyRoot: true },
);
expect(onChange).toHaveBeenCalledTimes(1);
@ -556,16 +554,14 @@ describe('Anchor Render', () => {
{ key: hash2, href: `#${hash2}`, title: hash2 },
]}
/>,
// https://github.com/testing-library/react-testing-library/releases/tag/v13.0.0
{ legacyRoot: true },
);
// Should be 2 times:
// 1. ''
// 2. hash1 (Since `getCurrentAnchor` still return same hash)
expect(onChange).toHaveBeenCalledTimes(2);
const calledTimes = onChange.mock.calls.length;
fireEvent.click(container.querySelector(`a[href="#${hash2}"]`)!);
expect(onChange).toHaveBeenCalledTimes(3);
expect(onChange).toHaveBeenCalledTimes(calledTimes + 1);
expect(onChange).toHaveBeenLastCalledWith(`#${hash2}`);
});

View File

@ -202,10 +202,12 @@ describe('App', () => {
expect(container.querySelector('.anticon')).toBeTruthy();
const dynamicStyles = Array.from(document.querySelectorAll('style[data-css-hash]'));
// Self-contained .anticon style
const regex = /(?:^|\})\s*\.anticon\s*{[^}]*}/;
expect(
dynamicStyles.some((style) => {
const { innerHTML } = style;
return innerHTML.startsWith('.anticon');
return regex.test(innerHTML);
}),
).toBeTruthy();
});

View File

@ -2267,15 +2267,7 @@ exports[`renders components/auto-complete/demo/render-panel.tsx extend context c
</div>
`;
exports[`renders components/auto-complete/demo/render-panel.tsx extend context correctly 2`] = `
[
"Warning: Received \`%s\` for a non-boolean attribute \`%s\`.
If you want to write it to the DOM, pass a string instead: %s="%s" or %s={value.toString()}.
If you used to conditionally omit it with %s={condition && value}, pass %s={condition ? value : undefined} instead.%s",
]
`;
exports[`renders components/auto-complete/demo/render-panel.tsx extend context correctly 2`] = `[]`;
exports[`renders components/auto-complete/demo/status.tsx extend context correctly 1`] = `
<div

View File

@ -1,3 +1,5 @@
import { extendTest } from '../../../tests/shared/demoTest';
extendTest('auto-complete');
extendTest('auto-complete', {
skip: ['row-selection-debug.tsx'],
});

View File

@ -1,5 +1,7 @@
import { imageDemoTest } from '../../../tests/shared/imageTest';
describe('AutoComplete image', () => {
imageDemoTest('auto-complete');
imageDemoTest('auto-complete', {
skip: ['row-selection-debug.tsx'],
});
});

View File

@ -170,7 +170,9 @@ const RefAutoComplete = React.forwardRef<RefSelectProps, AutoCompleteProps>(
// We don't care debug panel
/* istanbul ignore next */
const PurePanel = genPurePanel(RefAutoComplete);
const PurePanel = genPurePanel(RefAutoComplete, 'dropdownAlign', (props: any) =>
omit(props, ['visible']),
);
RefAutoComplete.Option = Option;
RefAutoComplete._InternalPanelDoNotUseOrYouWillBeFired = PurePanel;

View File

@ -271,8 +271,8 @@ describe('Avatar Render', () => {
count: 2,
popover: {
placement: 'bottomRight',
overlayClassName: 'wanpan-111',
overlayStyle: { background: 'red' },
classNames: { root: 'wanpan-111' },
styles: { root: { background: 'red' } },
content: 'Avatar.Group',
open: true,
},

View File

@ -441,6 +441,7 @@ Array [
</span>
</span>
<span
aria-describedby="test-id"
class="ant-avatar ant-avatar-circle"
style="color: rgb(245, 106, 0); background-color: rgb(253, 227, 207);"
>
@ -464,12 +465,14 @@ Array [
>
<div
class="ant-popover-inner"
id="test-id"
role="tooltip"
>
<div
class="ant-popover-inner-content"
>
<span
aria-describedby="test-id"
class="ant-avatar ant-avatar-circle ant-avatar-icon"
style="background-color: rgb(135, 208, 104);"
>
@ -506,6 +509,7 @@ Array [
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
>
Ant User
@ -731,6 +735,7 @@ Array [
</span>
</a>
<span
aria-describedby="test-id"
class="ant-avatar ant-avatar-circle ant-avatar-icon"
style="background-color: rgb(135, 208, 104);"
>
@ -767,6 +772,7 @@ Array [
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
>
Ant User
@ -824,6 +830,7 @@ Array [
</span>
</span>
<span
aria-describedby="test-id"
class="ant-avatar ant-avatar-circle"
style="color: rgb(245, 106, 0); background-color: rgb(253, 227, 207);"
>
@ -847,12 +854,14 @@ Array [
>
<div
class="ant-popover-inner"
id="test-id"
role="tooltip"
>
<div
class="ant-popover-inner-content"
>
<span
aria-describedby="test-id"
class="ant-avatar ant-avatar-circle ant-avatar-icon"
style="background-color: rgb(135, 208, 104);"
>
@ -889,6 +898,7 @@ Array [
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
>
Ant User
@ -950,6 +960,7 @@ Array [
</span>
</span>
<span
aria-describedby="test-id"
class="ant-avatar ant-avatar-lg ant-avatar-circle"
style="color: rgb(245, 106, 0); background-color: rgb(253, 227, 207);"
>
@ -973,12 +984,14 @@ Array [
>
<div
class="ant-popover-inner"
id="test-id"
role="tooltip"
>
<div
class="ant-popover-inner-content"
>
<span
aria-describedby="test-id"
class="ant-avatar ant-avatar-lg ant-avatar-circle ant-avatar-icon"
style="background-color: rgb(135, 208, 104);"
>
@ -1015,6 +1028,7 @@ Array [
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
>
Ant User
@ -1076,6 +1090,7 @@ Array [
</span>
</span>
<span
aria-describedby="test-id"
class="ant-avatar ant-avatar-lg ant-avatar-circle"
style="color: rgb(245, 106, 0); background-color: rgb(253, 227, 207); cursor: pointer;"
>
@ -1099,12 +1114,14 @@ Array [
>
<div
class="ant-popover-inner"
id="test-id"
role="tooltip"
>
<div
class="ant-popover-inner-content"
>
<span
aria-describedby="test-id"
class="ant-avatar ant-avatar-lg ant-avatar-circle ant-avatar-icon"
style="background-color: rgb(135, 208, 104);"
>
@ -1141,6 +1158,7 @@ Array [
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
>
Ant User

View File

@ -437,6 +437,7 @@ Array [
</span>
</span>
<span
aria-describedby="test-id"
class="ant-avatar ant-avatar-circle"
style="color:#f56a00;background-color:#fde3cf"
>
@ -632,6 +633,7 @@ Array [
</span>
</a>
<span
aria-describedby="test-id"
class="ant-avatar ant-avatar-circle ant-avatar-icon"
style="background-color:#87d068"
>
@ -706,6 +708,7 @@ Array [
</span>
</span>
<span
aria-describedby="test-id"
class="ant-avatar ant-avatar-circle"
style="color:#f56a00;background-color:#fde3cf"
>
@ -743,6 +746,7 @@ Array [
</span>
</span>
<span
aria-describedby="test-id"
class="ant-avatar ant-avatar-lg ant-avatar-circle"
style="color:#f56a00;background-color:#fde3cf"
>
@ -780,6 +784,7 @@ Array [
</span>
</span>
<span
aria-describedby="test-id"
class="ant-avatar ant-avatar-lg ant-avatar-circle"
style="color:#f56a00;background-color:#fde3cf;cursor:pointer"
>

View File

@ -102,7 +102,10 @@ const Group: React.FC<GroupProps> = (props) => {
);
const childrenWithProps = toArray(children).map((child, index) =>
cloneElement(child, { key: `avatar-key-${index}` }),
cloneElement(child, {
// eslint-disable-next-line react/no-array-index-key
key: `avatar-key-${index}`,
}),
);
const mergeCount = max?.count || maxCount;
@ -118,7 +121,7 @@ const Group: React.FC<GroupProps> = (props) => {
const mergeProps = {
content: childrenHidden,
...max?.popover,
overlayClassName: classNames(`${groupPrefixCls}-popover`, max?.popover?.overlayClassName),
classNames: { root: classNames(`${groupPrefixCls}-popover`, max?.popover?.classNames?.root) },
placement: mergePopoverPlacement,
trigger: mergePopoverTrigger,
};

View File

@ -10,7 +10,7 @@ export interface ScrollNumberProps {
className?: string;
motionClassName?: string;
count?: string | number | null;
children?: React.ReactElement<HTMLElement>;
children?: React.ReactElement;
component?: React.ComponentType<any>;
style?: React.CSSProperties;
title?: string | number | null;

View File

@ -79,23 +79,29 @@ const SingleNumber: React.FC<Readonly<SingleNumberProps>> = (props) => {
unitNumberList.push(index);
}
const unit = prevCount < count ? 1 : -1;
// Fill with number unit nodes
const prevIndex = unitNumberList.findIndex((n) => n % 10 === prevValue);
unitNodes = unitNumberList.map((n, index) => {
// Cut list
const cutUnitNumberList =
unit < 0 ? unitNumberList.slice(0, prevIndex + 1) : unitNumberList.slice(prevIndex);
unitNodes = cutUnitNumberList.map((n, index) => {
const singleUnit = n % 10;
return (
<UnitNumber
{...props}
key={n}
value={singleUnit}
offset={index - prevIndex}
offset={unit < 0 ? index - prevIndex : index}
current={index === prevIndex}
/>
);
});
// Calculate container offset value
const unit = prevCount < count ? 1 : -1;
offsetStyle = {
transform: `translateY(${-getOffset(prevValue, value, unit)}00%)`,
};

View File

@ -305,60 +305,6 @@ exports[`Badge should render when count is changed 1`] = `
>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -900%; left: 0px;"
>
0
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -800%; left: 0px;"
>
1
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -700%; left: 0px;"
>
2
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -600%; left: 0px;"
>
3
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -500%; left: 0px;"
>
4
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -400%; left: 0px;"
>
5
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -300%; left: 0px;"
>
6
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -200%; left: 0px;"
>
7
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -100%; left: 0px;"
>
8
</span>
<span
class="ant-scroll-number-only-unit current"
>
9
</span>
@ -400,60 +346,6 @@ exports[`Badge should render when count is changed 2`] = `
>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -900%; left: 0px;"
>
1
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -800%; left: 0px;"
>
2
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -700%; left: 0px;"
>
3
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -600%; left: 0px;"
>
4
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -500%; left: 0px;"
>
5
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -400%; left: 0px;"
>
6
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -300%; left: 0px;"
>
7
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -200%; left: 0px;"
>
8
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: -100%; left: 0px;"
>
9
</span>
<span
class="ant-scroll-number-only-unit current"
>
0
</span>
@ -495,7 +387,6 @@ exports[`Badge should render when count is changed 3`] = `
>
<span
class="ant-scroll-number-only-unit current"
style=""
>
1
</span>
@ -579,60 +470,6 @@ exports[`Badge should render when count is changed 6`] = `
>
0
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: 100%; left: 0px;"
>
1
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: 200%; left: 0px;"
>
2
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: 300%; left: 0px;"
>
3
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: 400%; left: 0px;"
>
4
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: 500%; left: 0px;"
>
5
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: 600%; left: 0px;"
>
6
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: 700%; left: 0px;"
>
7
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: 800%; left: 0px;"
>
8
</span>
<span
class="ant-scroll-number-only-unit"
style="position: absolute; top: 900%; left: 0px;"
>
9
</span>
</span>
</bdi>
</sup>

View File

@ -3,7 +3,7 @@ import React from 'react';
import type { GetRef } from '../../_util/type';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { act, fireEvent, render, waitFakeTimer } from '../../../tests/utils';
import { act, fireEvent, render, waitFakeTimer19 } from '../../../tests/utils';
import Tooltip from '../../tooltip';
import Badge from '../index';
@ -50,7 +50,7 @@ describe('Badge', () => {
const { container } = render(<Comp />);
fireEvent.click(container.querySelector('button')!);
await waitFakeTimer();
await waitFakeTimer19();
expect(errSpy).not.toHaveBeenCalled();
errSpy.mockRestore();

View File

@ -26,7 +26,7 @@ export interface BreadcrumbItemType {
*/
path?: string;
title?: React.ReactNode;
/* @deprecated Please use `title` instead */
/** @deprecated Please use `title` instead */
breadcrumbName?: string;
menu?: BreadcrumbItemProps['menu'];
/** @deprecated Please use `menu` instead */
@ -199,6 +199,7 @@ const Breadcrumb = <T extends AnyObject = AnyObject>(props: BreadcrumbProps<T>)
const isLastItem = index === childrenLength - 1;
return cloneElement(element, {
separator: isLastItem ? '' : separator,
// eslint-disable-next-line react/no-array-index-key
key: index,
});
});

View File

@ -144,6 +144,7 @@ exports[`renders components/breadcrumb/demo/component-token.tsx extend context c
tabindex="0"
>
<li
aria-describedby="test-id"
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-1"
role="menuitem"
@ -174,11 +175,13 @@ exports[`renders components/breadcrumb/demo/component-token.tsx extend context c
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
/>
</div>
</div>
<li
aria-describedby="test-id"
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-2"
role="menuitem"
@ -209,11 +212,13 @@ exports[`renders components/breadcrumb/demo/component-token.tsx extend context c
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
/>
</div>
</div>
<li
aria-describedby="test-id"
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-3"
role="menuitem"
@ -244,6 +249,7 @@ exports[`renders components/breadcrumb/demo/component-token.tsx extend context c
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
/>
</div>
@ -402,6 +408,7 @@ exports[`renders components/breadcrumb/demo/debug-routes.tsx extend context corr
tabindex="0"
>
<li
aria-describedby="test-id"
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-0"
role="menuitem"
@ -430,11 +437,13 @@ exports[`renders components/breadcrumb/demo/debug-routes.tsx extend context corr
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
/>
</div>
</div>
<li
aria-describedby="test-id"
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-1"
role="menuitem"
@ -463,6 +472,7 @@ exports[`renders components/breadcrumb/demo/debug-routes.tsx extend context corr
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
/>
</div>
@ -563,6 +573,7 @@ exports[`renders components/breadcrumb/demo/overlay.tsx extend context correctly
tabindex="0"
>
<li
aria-describedby="test-id"
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-1"
role="menuitem"
@ -593,11 +604,13 @@ exports[`renders components/breadcrumb/demo/overlay.tsx extend context correctly
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
/>
</div>
</div>
<li
aria-describedby="test-id"
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-2"
role="menuitem"
@ -628,11 +641,13 @@ exports[`renders components/breadcrumb/demo/overlay.tsx extend context correctly
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
/>
</div>
</div>
<li
aria-describedby="test-id"
class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child"
data-menu-id="rc-menu-uuid-test-3"
role="menuitem"
@ -663,6 +678,7 @@ exports[`renders components/breadcrumb/demo/overlay.tsx extend context correctly
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
/>
</div>

View File

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`react router react router 3 1`] = `
exports[`react router react router legacy 1`] = `
<nav
class="ant-breadcrumb"
>

View File

@ -1,29 +1,10 @@
import React, { useMemo } from 'react';
import type { RouterProps } from 'react-router-dom';
import { Link, MemoryRouter, Route, Routes, useLocation, useNavigate } from 'react-router-dom';
import React from 'react';
import { MemoryRouter, useLocation } from 'react-router-dom';
import type { Location as ReactRouterLocation } from 'react-router-dom';
import { fireEvent, render } from '../../../tests/utils';
import { render } from '../../../tests/utils';
import Breadcrumb from '../index';
const Apps: React.FC = () => (
<ul className="app-list">
<li>
<Link to="/apps/1">Application1</Link><Link to="/apps/1/detail">Detail</Link>
</li>
<li>
<Link to="/apps/2">Application2</Link><Link to="/apps/2/detail">Detail</Link>
</li>
</ul>
);
const breadcrumbNameMap = {
'/apps': 'Application List',
'/apps/1': 'Application1',
'/apps/2': 'Application2',
'/apps/1/detail': 'Detail',
'/apps/2/detail': 'Detail',
};
describe('react router', () => {
beforeAll(() => {
jest.useFakeTimers();
@ -33,63 +14,37 @@ describe('react router', () => {
jest.useRealTimers();
});
it('react router 6', () => {
const Home: React.FC = () => {
const location = useLocation();
const navigate = useNavigate();
const pathSnippets = location.pathname.split('/').filter((i) => i);
const extraBreadcrumbItems = pathSnippets.map((_, index) => {
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
return (
<Breadcrumb.Item key={url}>
<Link to={url}>{breadcrumbNameMap[url as keyof typeof breadcrumbNameMap]}</Link>
</Breadcrumb.Item>
);
});
const breadcrumbItems = [
<Breadcrumb.Item key="home">
<Link to="/">Home</Link>
</Breadcrumb.Item>,
].concat(extraBreadcrumbItems);
const componentProps = useMemo<RouterProps>(
() => ({ component: Apps }) as unknown as RouterProps,
[],
);
const renderProps = useMemo<RouterProps>(
() => ({ render: () => <span>Home Page</span> }) as unknown as RouterProps,
[],
);
return (
<div className="demo">
<div className="demo-nav">
<a onClick={() => navigate('/')}>Home</a>
<a onClick={() => navigate('/apps')}>Application List</a>
</div>
<Routes>
<Route path="/apps" {...componentProps} />
<Route {...renderProps} />
</Routes>
<Breadcrumb>{breadcrumbItems}</Breadcrumb>
</div>
);
it('memoizes the current location', () => {
let location1: ReactRouterLocation | undefined;
const CaptureLocation1: React.FC = () => {
location1 = useLocation();
return null;
};
const { container } = render(
<MemoryRouter initialEntries={['/']} initialIndex={0}>
<Home />
const { container: container1 } = render(
<MemoryRouter>
<CaptureLocation1 />
</MemoryRouter>,
);
expect(container.querySelectorAll('.ant-breadcrumb-link').length).toBe(1);
expect(container.querySelectorAll('.ant-breadcrumb-link')[0].textContent).toBe('Home');
expect(container1).toBeTruthy();
expect(location1).toBeDefined();
fireEvent.click(container.querySelectorAll('.demo-nav a')[1]);
expect(container.querySelectorAll('.ant-breadcrumb-link').length).toBe(2);
expect(container.querySelectorAll('.ant-breadcrumb-link')[1].textContent).toBe(
'Application List',
let location2: ReactRouterLocation | undefined;
const CaptureLocation2: React.FC = () => {
location2 = useLocation();
return null;
};
const { container: container2 } = render(
<MemoryRouter>
<CaptureLocation2 />
</MemoryRouter>,
);
expect(container2).toBeTruthy();
expect(location2).toBeDefined();
expect(location1).toEqual(location2);
});
it('react router 3', () => {
it('react router legacy', () => {
const routes = [
{
name: 'home',

View File

@ -23,12 +23,13 @@ const InnerLoadingIcon = forwardRef<HTMLSpanElement, InnerLoadingIconProps>((pro
);
});
export type LoadingIconProps = {
export type DefaultLoadingIconProps = {
prefixCls: string;
existIcon: boolean;
loading?: boolean | object;
className?: string;
style?: React.CSSProperties;
mount: boolean;
};
const getCollapsedWidth = (): React.CSSProperties => ({
@ -43,8 +44,8 @@ const getRealWidth = (node: HTMLElement): React.CSSProperties => ({
transform: 'scale(1)',
});
const LoadingIcon: React.FC<LoadingIconProps> = (props) => {
const { prefixCls, loading, existIcon, className, style } = props;
const DefaultLoadingIcon: React.FC<DefaultLoadingIconProps> = (props) => {
const { prefixCls, loading, existIcon, className, style, mount } = props;
const visible = !!loading;
if (existIcon) {
@ -54,9 +55,11 @@ const LoadingIcon: React.FC<LoadingIconProps> = (props) => {
return (
<CSSMotion
visible={visible}
// We do not really use this motionName
// Used for minus flex gap style only
motionName={`${prefixCls}-loading-icon-motion`}
motionLeave={visible}
motionAppear={!mount}
motionEnter={!mount}
motionLeave={!mount}
removeOnLeave
onAppearStart={getCollapsedWidth}
onAppearActive={getRealWidth}
@ -65,17 +68,20 @@ const LoadingIcon: React.FC<LoadingIconProps> = (props) => {
onLeaveStart={getRealWidth}
onLeaveActive={getCollapsedWidth}
>
{({ className: motionCls, style: motionStyle }, ref: React.Ref<HTMLSpanElement>) => (
<InnerLoadingIcon
prefixCls={prefixCls}
className={className}
style={{ ...style, ...motionStyle }}
ref={ref}
iconClassName={motionCls}
/>
)}
{({ className: motionCls, style: motionStyle }, ref: React.Ref<HTMLSpanElement>) => {
const mergedStyle = { ...style, ...motionStyle };
return (
<InnerLoadingIcon
prefixCls={prefixCls}
className={classNames(className, motionCls)}
style={mergedStyle}
ref={ref}
/>
);
}}
</CSSMotion>
);
};
export default LoadingIcon;
export default DefaultLoadingIcon;

View File

@ -301,12 +301,35 @@ exports[`renders components/button/demo/chinese-chars-loading.tsx correctly 1`]
</div>
`;
exports[`renders components/button/demo/chinese-space.tsx correctly 1`] = `
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
class="ant-btn ant-btn-primary ant-btn-color-primary ant-btn-variant-solid"
type="button"
>
<span>
确定
</span>
</button>
<button
class="ant-btn ant-btn-primary ant-btn-color-primary ant-btn-variant-solid"
type="button"
>
<span>
确 定
</span>
</button>
</div>
`;
exports[`renders components/button/demo/color-variant.tsx correctly 1`] = `
<div
class="ant-flex ant-flex-align-stretch ant-flex-gap-middle ant-flex-vertical"
class="ant-flex ant-flex-align-stretch ant-flex-gap-small ant-flex-vertical"
>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-middle"
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-solid ant-btn-sm"
@ -358,7 +381,7 @@ exports[`renders components/button/demo/color-variant.tsx correctly 1`] = `
</button>
</div>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-middle"
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
class="ant-btn ant-btn-default ant-btn-color-primary ant-btn-variant-solid ant-btn-sm"
@ -410,7 +433,7 @@ exports[`renders components/button/demo/color-variant.tsx correctly 1`] = `
</button>
</div>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-middle"
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
class="ant-btn ant-btn-default ant-btn-color-dangerous ant-btn-variant-solid ant-btn-sm"
@ -461,6 +484,162 @@ exports[`renders components/button/demo/color-variant.tsx correctly 1`] = `
</span>
</button>
</div>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-solid ant-btn-sm"
type="button"
>
<span>
Solid
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-outlined ant-btn-sm"
type="button"
>
<span>
Outlined
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-dashed ant-btn-sm"
type="button"
>
<span>
Dashed
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-filled ant-btn-sm"
type="button"
>
<span>
Filled
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-text ant-btn-sm"
type="button"
>
<span>
Text
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-pink ant-btn-variant-link ant-btn-sm"
type="button"
>
<span>
Link
</span>
</button>
</div>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-solid ant-btn-sm"
type="button"
>
<span>
Solid
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-outlined ant-btn-sm"
type="button"
>
<span>
Outlined
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-dashed ant-btn-sm"
type="button"
>
<span>
Dashed
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-filled ant-btn-sm"
type="button"
>
<span>
Filled
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-text ant-btn-sm"
type="button"
>
<span>
Text
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-purple ant-btn-variant-link ant-btn-sm"
type="button"
>
<span>
Link
</span>
</button>
</div>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-solid ant-btn-sm"
type="button"
>
<span>
Solid
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-outlined ant-btn-sm"
type="button"
>
<span>
Outlined
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-dashed ant-btn-sm"
type="button"
>
<span>
Dashed
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-filled ant-btn-sm"
type="button"
>
<span>
Filled
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-text ant-btn-sm"
type="button"
>
<span>
Text
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-cyan ant-btn-variant-link ant-btn-sm"
type="button"
>
<span>
Link
</span>
</button>
</div>
</div>
`;
@ -577,7 +756,7 @@ exports[`renders components/button/demo/debug-block.tsx correctly 1`] = `
exports[`renders components/button/demo/debug-color-variant.tsx correctly 1`] = `
<div
class="ant-flex ant-flex-align-stretch ant-flex-gap-middle ant-flex-vertical"
class="ant-flex ant-flex-align-stretch ant-flex-gap-small ant-flex-vertical"
>
<div
class="ant-flex ant-flex-gap-small"
@ -704,6 +883,7 @@ Array [
<input
checked=""
class="ant-radio-button-input"
name="test-id"
type="radio"
value="large"
/>
@ -711,7 +891,9 @@ Array [
class="ant-radio-button-inner"
/>
</span>
<span>
<span
class="ant-radio-button-label"
>
Large
</span>
</label>
@ -723,6 +905,7 @@ Array [
>
<input
class="ant-radio-button-input"
name="test-id"
type="radio"
value="default"
/>
@ -730,7 +913,9 @@ Array [
class="ant-radio-button-inner"
/>
</span>
<span>
<span
class="ant-radio-button-label"
>
Default
</span>
</label>
@ -742,6 +927,7 @@ Array [
>
<input
class="ant-radio-button-input"
name="test-id"
type="radio"
value="small"
/>
@ -749,7 +935,9 @@ Array [
class="ant-radio-button-inner"
/>
</span>
<span>
<span
class="ant-radio-button-label"
>
Small
</span>
</label>
@ -771,6 +959,7 @@ Array [
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-circle ant-btn-primary ant-btn-color-primary ant-btn-variant-solid ant-btn-lg ant-btn-icon-only"
type="button"
>
@ -838,6 +1027,7 @@ Array [
</span>
</button>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg ant-btn-icon-only"
type="button"
>
@ -901,6 +1091,7 @@ Array [
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg ant-btn-icon-only"
type="button"
>
@ -960,6 +1151,7 @@ Array [
</span>
</button>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-circle ant-btn-dashed ant-btn-color-default ant-btn-variant-dashed ant-btn-lg ant-btn-icon-only"
type="button"
>
@ -1022,6 +1214,7 @@ Array [
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg ant-btn-icon-only"
href="https://www.google.com"
tabindex="0"
target="_blank"
>
<span
class="ant-btn-icon"
@ -1075,6 +1268,297 @@ Array [
</span>
</button>
</div>
<div
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-plain"
role="separator"
>
<span
class="ant-divider-inner-text"
>
👇🏻 https://github.com/ant-design/ant-design/issues/51811 👇🏻
</span>
</div>
<div>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg"
type="button"
>
<span>
without icon
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg"
type="button"
>
<span
class="ant-btn-icon"
>
<span
aria-label="search"
class="anticon anticon-search"
role="img"
>
<svg
aria-hidden="true"
data-icon="search"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0011.6 0l43.6-43.5a8.2 8.2 0 000-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"
/>
</svg>
</span>
</span>
<span>
with icon
</span>
</button>
</div>
<div
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-plain"
role="separator"
>
<span
class="ant-divider-inner-text"
>
👇🏻 https://github.com/ant-design/ant-design/issues/52124 👇🏻
</span>
</div>
<div>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg"
style="height:60px"
type="button"
>
<span>
without icon
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg"
style="height:60px"
type="button"
>
<span
class="ant-btn-icon"
>
<span
aria-label="search"
class="anticon anticon-search"
role="img"
>
<svg
aria-hidden="true"
data-icon="search"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0011.6 0l43.6-43.5a8.2 8.2 0 000-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"
/>
</svg>
</span>
</span>
<span>
with icon
</span>
</button>
</div>
<div
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-plain"
role="separator"
>
<span
class="ant-divider-inner-text"
>
👇🏻 https://github.com/ant-design/ant-design/issues/51380 👇🏻
</span>
</div>
<div>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg ant-btn-icon-only"
type="button"
>
<span
class="ant-btn-icon"
>
<svg
class="my-class-name"
fill="none"
height="1em"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 3h7a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-7m0-18H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h7m0-18v18"
/>
</svg>
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg"
type="button"
>
<span
class="ant-btn-icon"
>
<svg
fill="none"
height="1em"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 3h7a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-7m0-18H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h7m0-18v18"
/>
</svg>
</span>
<span>
custom icon
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg ant-btn-icon-only"
type="button"
>
<span
class="ant-btn-icon"
>
<span
aria-label="search"
class="anticon anticon-search"
role="img"
>
<svg
aria-hidden="true"
data-icon="search"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0011.6 0l43.6-43.5a8.2 8.2 0 000-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"
/>
</svg>
</span>
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg"
type="button"
>
<span
class="ant-btn-icon"
>
<span
aria-label="search"
class="anticon anticon-search"
role="img"
>
<svg
aria-hidden="true"
data-icon="search"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0011.6 0l43.6-43.5a8.2 8.2 0 000-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"
/>
</svg>
</span>
</span>
<span>
with icon
</span>
</button>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg"
type="button"
>
<span>
without icon
</span>
</button>
<span
class="ant-input-group-wrapper ant-input-group-wrapper-lg ant-input-group-wrapper-outlined ant-input-search ant-input-search-large"
style="width:100px"
>
<span
class="ant-input-wrapper ant-input-group"
>
<input
class="ant-input ant-input-lg ant-input-outlined"
type="search"
value=""
/>
<span
class="ant-input-group-addon"
>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg ant-btn-icon-only ant-input-search-button"
type="button"
>
<span
class="ant-btn-icon"
>
<span
aria-label="search"
class="anticon anticon-search"
role="img"
>
<svg
aria-hidden="true"
data-icon="search"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0011.6 0l43.6-43.5a8.2 8.2 0 000-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"
/>
</svg>
</span>
</span>
</button>
</span>
</span>
</span>
</div>
<div
class="ant-divider ant-divider-horizontal ant-divider-with-text ant-divider-with-text-center ant-divider-plain"
role="separator"
>
<span
class="ant-divider-inner-text"
>
👇🏻 https://github.com/ant-design/ant-design/issues/51380 👇🏻
</span>
</div>
<div
class="ant-flex ant-flex-gap-small"
style="transform:scale(3);transform-origin:left top"
@ -1415,6 +1899,7 @@ exports[`renders components/button/demo/icon.tsx correctly 1`] = `
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-circle ant-btn-primary ant-btn-color-primary ant-btn-variant-solid ant-btn-icon-only"
type="button"
>
@ -1482,6 +1967,7 @@ exports[`renders components/button/demo/icon.tsx correctly 1`] = `
</span>
</button>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-icon-only"
type="button"
>
@ -1545,6 +2031,7 @@ exports[`renders components/button/demo/icon.tsx correctly 1`] = `
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-icon-only"
type="button"
>
@ -1604,6 +2091,7 @@ exports[`renders components/button/demo/icon.tsx correctly 1`] = `
</span>
</button>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-circle ant-btn-dashed ant-btn-color-default ant-btn-variant-dashed ant-btn-icon-only"
type="button"
>
@ -1666,6 +2154,7 @@ exports[`renders components/button/demo/icon.tsx correctly 1`] = `
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-icon-only"
href="https://www.google.com"
tabindex="0"
target="_blank"
>
<span
class="ant-btn-icon"
@ -1714,6 +2203,7 @@ Array [
>
<input
class="ant-radio-button-input"
name="test-id"
type="radio"
value="start"
/>
@ -1721,7 +2211,9 @@ Array [
class="ant-radio-button-inner"
/>
</span>
<span>
<span
class="ant-radio-button-label"
>
start
</span>
</label>
@ -1734,6 +2226,7 @@ Array [
<input
checked=""
class="ant-radio-button-input"
name="test-id"
type="radio"
value="end"
/>
@ -1741,7 +2234,9 @@ Array [
class="ant-radio-button-inner"
/>
</span>
<span>
<span
class="ant-radio-button-label"
>
end
</span>
</label>
@ -1765,6 +2260,7 @@ Array [
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-circle ant-btn-primary ant-btn-color-primary ant-btn-variant-solid ant-btn-icon-only"
type="button"
>
@ -1832,6 +2328,7 @@ Array [
</span>
</button>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-icon-only"
type="button"
>
@ -1895,6 +2392,7 @@ Array [
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-circle ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-icon-only"
type="button"
>
@ -1954,6 +2452,7 @@ Array [
</span>
</button>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-circle ant-btn-dashed ant-btn-color-default ant-btn-variant-dashed ant-btn-icon-only"
type="button"
>
@ -2016,6 +2515,7 @@ Array [
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-icon-only ant-btn-icon-end"
href="https://www.google.com"
tabindex="0"
target="_blank"
>
<span
class="ant-btn-icon"
@ -2099,6 +2599,7 @@ Array [
</span>
</button>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-primary ant-btn-color-primary ant-btn-variant-solid ant-btn-sm ant-btn-icon-only"
disabled=""
type="button"
@ -2128,6 +2629,7 @@ Array [
</span>
</button>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-primary ant-btn-color-primary ant-btn-variant-solid ant-btn-sm ant-btn-icon-only"
type="button"
>
@ -2177,6 +2679,7 @@ Array [
</span>
</button>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-primary ant-btn-color-primary ant-btn-variant-solid ant-btn-icon-only"
disabled=""
type="button"
@ -2206,6 +2709,7 @@ Array [
</span>
</button>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-primary ant-btn-color-primary ant-btn-variant-solid ant-btn-icon-only"
type="button"
>
@ -2255,6 +2759,7 @@ Array [
</span>
</button>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-primary ant-btn-color-primary ant-btn-variant-solid ant-btn-lg ant-btn-icon-only"
disabled=""
type="button"
@ -2284,6 +2789,7 @@ Array [
</span>
</button>
<button
aria-describedby="test-id"
class="ant-btn ant-btn-primary ant-btn-color-primary ant-btn-variant-solid ant-btn-lg ant-btn-icon-only"
type="button"
>
@ -2323,7 +2829,7 @@ exports[`renders components/button/demo/linear-gradient.tsx correctly 1`] = `
class="ant-space-item"
>
<button
class="ant-btn ant-btn-primary ant-btn-color-primary ant-btn-variant-solid ant-btn-lg acss-9mi5l3"
class="ant-btn ant-btn-primary ant-btn-color-primary ant-btn-variant-solid ant-btn-lg acss-wkbm77"
type="button"
>
<span
@ -2358,7 +2864,7 @@ exports[`renders components/button/demo/linear-gradient.tsx correctly 1`] = `
class="ant-space-item"
>
<button
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg acss-9mi5l3"
class="ant-btn ant-btn-default ant-btn-color-default ant-btn-variant-outlined ant-btn-lg acss-wkbm77"
type="button"
>
<span>
@ -2466,6 +2972,37 @@ exports[`renders components/button/demo/loading.tsx correctly 1`] = `
</span>
</span>
</button>
<button
class="ant-btn ant-btn-primary ant-btn-color-primary ant-btn-variant-solid ant-btn-loading"
type="button"
>
<span
class="ant-btn-icon"
>
<span
aria-label="sync"
class="anticon anticon-sync anticon-spin"
role="img"
>
<svg
aria-hidden="true"
data-icon="sync"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M168 504.2c1-43.7 10-86.1 26.9-126 17.3-41 42.1-77.7 73.7-109.4S337 212.3 378 195c42.4-17.9 87.4-27 133.9-27s91.5 9.1 133.8 27A341.5 341.5 0 01755 268.8c9.9 9.9 19.2 20.4 27.8 31.4l-60.2 47a8 8 0 003 14.1l175.7 43c5 1.2 9.9-2.6 9.9-7.7l.8-180.9c0-6.7-7.7-10.5-12.9-6.3l-56.4 44.1C765.8 155.1 646.2 92 511.8 92 282.7 92 96.3 275.6 92 503.8a8 8 0 008 8.2h60c4.4 0 7.9-3.5 8-7.8zm756 7.8h-60c-4.4 0-7.9 3.5-8 7.8-1 43.7-10 86.1-26.9 126-17.3 41-42.1 77.8-73.7 109.4A342.45 342.45 0 01512.1 856a342.24 342.24 0 01-243.2-100.8c-9.9-9.9-19.2-20.4-27.8-31.4l60.2-47a8 8 0 00-3-14.1l-175.7-43c-5-1.2-9.9 2.6-9.9 7.7l-.7 181c0 6.7 7.7 10.5 12.9 6.3l56.4-44.1C258.2 868.9 377.8 932 512.2 932c229.2 0 415.5-183.7 419.8-411.8a8 8 0 00-8-8.2z"
/>
</svg>
</span>
</span>
<span>
Loading Icon
</span>
</button>
</div>
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-small"
@ -2475,7 +3012,15 @@ exports[`renders components/button/demo/loading.tsx correctly 1`] = `
type="button"
>
<span>
Click me!
Icon Start
</span>
</button>
<button
class="ant-btn ant-btn-primary ant-btn-color-primary ant-btn-variant-solid ant-btn-icon-end"
type="button"
>
<span>
Icon End
</span>
</button>
<button
@ -2506,7 +3051,7 @@ exports[`renders components/button/demo/loading.tsx correctly 1`] = `
</span>
</span>
<span>
Click me!
Icon Replace
</span>
</button>
<button
@ -2537,6 +3082,37 @@ exports[`renders components/button/demo/loading.tsx correctly 1`] = `
</span>
</span>
</button>
<button
class="ant-btn ant-btn-primary ant-btn-color-primary ant-btn-variant-solid"
type="button"
>
<span
class="ant-btn-icon"
>
<span
aria-label="poweroff"
class="anticon anticon-poweroff"
role="img"
>
<svg
aria-hidden="true"
data-icon="poweroff"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M705.6 124.9a8 8 0 00-11.6 7.2v64.2c0 5.5 2.9 10.6 7.5 13.6a352.2 352.2 0 0162.2 49.8c32.7 32.8 58.4 70.9 76.3 113.3a355 355 0 0127.9 138.7c0 48.1-9.4 94.8-27.9 138.7a355.92 355.92 0 01-76.3 113.3 353.06 353.06 0 01-113.2 76.4c-43.8 18.6-90.5 28-138.5 28s-94.7-9.4-138.5-28a353.06 353.06 0 01-113.2-76.4A355.92 355.92 0 01184 650.4a355 355 0 01-27.9-138.7c0-48.1 9.4-94.8 27.9-138.7 17.9-42.4 43.6-80.5 76.3-113.3 19-19 39.8-35.6 62.2-49.8 4.7-2.9 7.5-8.1 7.5-13.6V132c0-6-6.3-9.8-11.6-7.2C178.5 195.2 82 339.3 80 506.3 77.2 745.1 272.5 943.5 511.2 944c239 .5 432.8-193.3 432.8-432.4 0-169.2-97-315.7-238.4-386.7zM480 560h64c4.4 0 8-3.6 8-8V88c0-4.4-3.6-8-8-8h-64c-4.4 0-8 3.6-8 8v464c0 4.4 3.6 8 8 8z"
/>
</svg>
</span>
</span>
<span>
Loading Icon
</span>
</button>
</div>
</div>
`;
@ -2604,29 +3180,6 @@ exports[`renders components/button/demo/multiple.tsx correctly 1`] = `
</div>
`;
exports[`renders components/button/demo/noSpace.tsx correctly 1`] = `
<div
class="ant-flex ant-flex-wrap-wrap ant-flex-gap-middle"
>
<button
class="ant-btn ant-btn-primary ant-btn-color-primary ant-btn-variant-solid"
type="button"
>
<span>
确定
</span>
</button>
<button
class="ant-btn ant-btn-primary ant-btn-color-primary ant-btn-variant-solid"
type="button"
>
<span>
确 定
</span>
</button>
</div>
`;
exports[`renders components/button/demo/size.tsx correctly 1`] = `
Array [
<div
@ -2641,6 +3194,7 @@ Array [
<input
checked=""
class="ant-radio-button-input"
name="test-id"
type="radio"
value="large"
/>
@ -2648,7 +3202,9 @@ Array [
class="ant-radio-button-inner"
/>
</span>
<span>
<span
class="ant-radio-button-label"
>
Large
</span>
</label>
@ -2660,6 +3216,7 @@ Array [
>
<input
class="ant-radio-button-input"
name="test-id"
type="radio"
value="default"
/>
@ -2667,7 +3224,9 @@ Array [
class="ant-radio-button-inner"
/>
</span>
<span>
<span
class="ant-radio-button-label"
>
Default
</span>
</label>
@ -2679,6 +3238,7 @@ Array [
>
<input
class="ant-radio-button-input"
name="test-id"
type="radio"
value="small"
/>
@ -2686,7 +3246,9 @@ Array [
class="ant-radio-button-inner"
/>
</span>
<span>
<span
class="ant-radio-button-label"
>
Small
</span>
</label>

View File

@ -177,11 +177,10 @@ exports[`Button renders Chinese characters correctly 6`] = `
>
<span
class="ant-btn-icon ant-btn-loading-icon"
style="width: 0px; opacity: 0; transform: scale(0);"
>
<span
aria-label="loading"
class="anticon anticon-loading anticon-spin ant-btn-loading-icon-motion-appear ant-btn-loading-icon-motion-appear-start ant-btn-loading-icon-motion"
class="anticon anticon-loading anticon-spin"
role="img"
>
<svg

View File

@ -2,13 +2,14 @@ import React, { Suspense, useRef, useState } from 'react';
import { SearchOutlined } from '@ant-design/icons';
import { resetWarned } from 'rc-util/lib/warning';
import Button from '..';
import Button, { _ButtonVariantTypes } from '..';
import type { GetRef } from '../../_util/type';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { act, fireEvent, render, waitFakeTimer } from '../../../tests/utils';
import ConfigProvider from '../../config-provider';
import theme from '../../theme';
import { PresetColors } from '../../theme/interface';
import type { BaseButtonProps } from '../button';
describe('Button', () => {
@ -474,4 +475,24 @@ describe('Button', () => {
'--ant-button-solid-text-color': '#000',
});
});
it('should render preset colors and variants correctly', () => {
PresetColors.forEach((color) => {
_ButtonVariantTypes.forEach((variant) => {
const { container } = render(
<Button color={color} variant={variant}>
{color}
</Button>,
);
expect(container.firstChild).toHaveClass(`ant-btn-color-${color}`);
expect(container.firstChild).toHaveClass(`ant-btn-variant-${variant}`);
});
});
});
it('autoFocus should work', () => {
const { container } = render(<Button autoFocus>button</Button>);
expect(container.querySelector('button')).toBe(document.activeElement);
});
});

View File

@ -4,6 +4,18 @@ import userEvent from '@testing-library/user-event';
import Button from '..';
import { act, fireEvent, render } from '../../../tests/utils';
// TODO: Remove this. Mock for React 19
jest.mock('react-dom', () => {
const realReactDOM = jest.requireActual('react-dom');
if (realReactDOM.version.startsWith('19')) {
const realReactDOMClient = jest.requireActual('react-dom/client');
realReactDOM.createRoot = realReactDOMClient.createRoot;
}
return realReactDOM;
});
jest.mock('rc-util/lib/Dom/isVisible', () => {
const mockFn = () => true;
return mockFn;

View File

@ -1,7 +1,7 @@
import React, { Children, createRef, useContext, useEffect, useMemo, useState } from 'react';
import React, { Children, useContext, useEffect, useMemo, useRef, useState } from 'react';
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import { composeRef } from 'rc-util/lib/ref';
import { useComposeRef } from 'rc-util/lib/ref';
import { devUseWarning } from '../_util/warning';
import Wave from '../_util/wave';
@ -20,9 +20,9 @@ import type {
} from './buttonHelpers';
import { isTwoCNChar, isUnBorderedButtonVariant, spaceChildren } from './buttonHelpers';
import IconWrapper from './IconWrapper';
import LoadingIcon from './LoadingIcon';
import DefaultLoadingIcon from './DefaultLoadingIcon';
import useStyle from './style';
import CompactCmp from './style/compactCmp';
import Compact from './style/compact';
export type LegacyButtonType = ButtonType | 'danger';
@ -35,7 +35,7 @@ export interface BaseButtonProps {
shape?: ButtonShape;
size?: SizeType;
disabled?: boolean;
loading?: boolean | { delay?: number };
loading?: boolean | { delay?: number; icon?: React.ReactNode };
prefixCls?: string;
className?: string;
rootClassName?: string;
@ -119,6 +119,7 @@ const InternalCompoundedButton = React.forwardRef<
classNames: customClassNames,
style: customStyle = {},
autoInsertSpace,
autoFocus,
...rest
} = props;
@ -162,13 +163,26 @@ const InternalCompoundedButton = React.forwardRef<
const [hasTwoCNChar, setHasTwoCNChar] = useState<boolean>(false);
const internalRef = createRef<HTMLButtonElement | HTMLAnchorElement>();
const buttonRef = useRef<HTMLButtonElement | HTMLAnchorElement>(null);
const buttonRef = composeRef(ref, internalRef);
const mergedRef = useComposeRef(ref, buttonRef);
const needInserted =
Children.count(children) === 1 && !icon && !isUnBorderedButtonVariant(mergedVariant);
// ========================= Mount ==========================
// Record for mount status.
// This will help to no to show the animation of loading on the first mount.
const isMountRef = useRef(true);
React.useEffect(() => {
isMountRef.current = false;
return () => {
isMountRef.current = true;
};
}, []);
// ========================= Effect =========================
// Loading
useEffect(() => {
let delayTimer: ReturnType<typeof setTimeout> | null = null;
if (loadingOrDelay.delay > 0) {
@ -190,12 +204,13 @@ const InternalCompoundedButton = React.forwardRef<
return cleanupTimer;
}, [loadingOrDelay]);
// Two chinese characters check
useEffect(() => {
// FIXME: for HOC usage like <FormatMessage />
if (!buttonRef || !(buttonRef as any).current || !mergedInsertSpace) {
if (!buttonRef.current || !mergedInsertSpace) {
return;
}
const buttonText = (buttonRef as any).current.textContent;
const buttonText = buttonRef.current.textContent || '';
if (needInserted && isTwoCNChar(buttonText)) {
if (!hasTwoCNChar) {
setHasTwoCNChar(true);
@ -203,8 +218,16 @@ const InternalCompoundedButton = React.forwardRef<
} else if (hasTwoCNChar) {
setHasTwoCNChar(false);
}
}, [buttonRef]);
});
// Auto focus
useEffect(() => {
if (autoFocus && buttonRef.current) {
buttonRef.current.focus();
}
}, []);
// ========================= Events =========================
const handleClick = React.useCallback(
(e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement, MouseEvent>) => {
// FIXME: https://github.com/ant-design/ant-design/issues/30207
@ -217,6 +240,7 @@ const InternalCompoundedButton = React.forwardRef<
[props.onClick, innerLoading, mergedDisabled],
);
// ========================== Warn ==========================
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('Button');
@ -233,6 +257,7 @@ const InternalCompoundedButton = React.forwardRef<
);
}
// ========================== Size ==========================
const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction);
const sizeClassNameMap = { large: 'lg', small: 'sm', middle: undefined };
@ -245,6 +270,7 @@ const InternalCompoundedButton = React.forwardRef<
const linkButtonRestProps = omit(rest as ButtonProps & { navigate: any }, ['navigate']);
// ========================= Render =========================
const classes = classNames(
prefixCls,
hashId,
@ -284,8 +310,17 @@ const InternalCompoundedButton = React.forwardRef<
<IconWrapper prefixCls={prefixCls} className={iconClasses} style={iconStyle}>
{icon}
</IconWrapper>
) : typeof loading === 'object' && loading.icon ? (
<IconWrapper prefixCls={prefixCls} className={iconClasses} style={iconStyle}>
{loading.icon}
</IconWrapper>
) : (
<LoadingIcon existIcon={!!icon} prefixCls={prefixCls} loading={innerLoading} />
<DefaultLoadingIcon
existIcon={!!icon}
prefixCls={prefixCls}
loading={innerLoading}
mount={isMountRef.current}
/>
);
const kids =
@ -301,7 +336,7 @@ const InternalCompoundedButton = React.forwardRef<
href={mergedDisabled ? undefined : linkButtonRestProps.href}
style={fullStyle}
onClick={handleClick}
ref={buttonRef as React.Ref<HTMLAnchorElement>}
ref={mergedRef as React.Ref<HTMLAnchorElement>}
tabIndex={mergedDisabled ? -1 : 0}
>
{iconNode}
@ -318,12 +353,11 @@ const InternalCompoundedButton = React.forwardRef<
style={fullStyle}
onClick={handleClick}
disabled={mergedDisabled}
ref={buttonRef as React.Ref<HTMLButtonElement>}
ref={mergedRef as React.Ref<HTMLButtonElement>}
>
{iconNode}
{kids}
{/* Styles: compact */}
{!!compactItemClassnames && <CompactCmp key="compact" prefixCls={prefixCls} />}
{compactItemClassnames && <Compact prefixCls={prefixCls} />}
</button>
);

View File

@ -1,6 +1,7 @@
import React from 'react';
import { cloneElement, isFragment } from '../_util/reactNode';
import { PresetColors } from '../theme/interface';
import type { BaseButtonProps, LegacyButtonType } from './button';
const rxTwoCNChar = /^[\u4E00-\u9FA5]{2}$/;
@ -15,7 +16,7 @@ export function convertLegacyProps(
return { type };
}
export function isString(str: any): str is string {
export function isString(str: unknown): str is string {
return typeof str === 'string';
}
@ -34,10 +35,22 @@ function splitCNCharsBySpace(child: React.ReactElement | string | number, needIn
typeof child !== 'string' &&
typeof child !== 'number' &&
isString(child.type) &&
isTwoCNChar(child.props.children)
isTwoCNChar(
(
child as React.ReactElement<{
children: string;
}>
).props.children,
)
) {
return cloneElement(child, {
children: child.props.children.split('').join(SPACE),
children: (
child as React.ReactElement<{
children: string;
}>
).props.children
.split('')
.join(SPACE),
});
}
@ -94,5 +107,6 @@ export const _ButtonVariantTypes = [
] as const;
export type ButtonVariantType = (typeof _ButtonVariantTypes)[number];
export const _ButtonColorTypes = ['default', 'primary', 'danger'] as const;
export const _ButtonColorTypes = ['default', 'primary', 'danger', ...PresetColors] as const;
export type ButtonColorType = (typeof _ButtonColorTypes)[number];

View File

@ -2,7 +2,7 @@ import React from 'react';
import { Button, Flex } from 'antd';
const App: React.FC = () => (
<Flex gap="middle" wrap>
<Flex gap="small" wrap>
<Button type="primary" autoInsertSpace={false}>
</Button>

Some files were not shown because too many files have changed in this diff Show More