Merge branch 'feature' into master-merge-feature

This commit is contained in:
MadCcc 2025-05-06 16:35:41 +08:00
commit c7cf4d12a8
252 changed files with 6829 additions and 2506 deletions

View file

@ -0,0 +1,87 @@
import React from 'react';
import useLocale from '../hooks/useLocale';
import SemanticPreview from './SemanticPreview';
export const locales = {
cn: {
root: '根元素',
'popup.root': '弹出菜单元素',
},
en: {
root: 'Root element',
'popup.root': 'Popup element',
},
};
interface BlockProps {
component: React.ComponentType<any>;
options?: { value: string; label: string }[];
defaultValue?: string;
style?: React.CSSProperties;
[key: string]: any;
}
const Block: React.FC<BlockProps> = ({ component: Component, options, defaultValue, ...props }) => {
const divRef = React.useRef<HTMLDivElement>(null);
return (
<div ref={divRef} style={{ position: 'absolute', marginBottom: 80 }}>
<Component
{...props}
open
placement="bottomLeft"
defaultValue={defaultValue}
getPopupContainer={() => divRef.current}
options={options}
styles={{
popup: { zIndex: 1 },
}}
/>
</div>
);
};
export interface SelectSemanticTemplateProps {
component: React.ComponentType<any>;
componentName: string;
defaultValue?: string;
options?: { value: string; label: string }[];
height?: number;
onSearch?: (text: string) => void;
placeholder?: string;
style?: React.CSSProperties;
[key: string]: any;
}
const SelectSemanticTemplate: React.FC<SelectSemanticTemplateProps> = ({
component,
defaultValue,
options,
height,
style,
componentName,
...restProps
}) => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName={componentName}
semantics={[
{ name: 'root', desc: locale.root, version: '5.25.0' },
{ name: 'popup.root', desc: locale['popup.root'], version: '5.25.0' },
]}
height={height}
>
<Block
component={component}
defaultValue={defaultValue}
options={options}
style={style}
{...restProps}
/>
</SemanticPreview>
);
};
export default SelectSemanticTemplate;

View file

@ -1,9 +1,12 @@
/* eslint-disable react-hooks-extra/no-direct-set-state-in-use-effect */
import React from 'react';
import { InfoCircleOutlined } from '@ant-design/icons';
import get from 'rc-util/lib/utils/get';
import set from 'rc-util/lib/utils/set';
import { Col, ConfigProvider, Flex, Popover, Row, Tag, theme, Typography } from 'antd';
import { createStyles, css } from 'antd-style';
import classnames from 'classnames';
import { InfoCircleOutlined } from '@ant-design/icons';
import Prism from 'prismjs';
const MARK_BORDER_SIZE = 2;
@ -19,6 +22,9 @@ const useStyle = createStyles(({ token }, markPos: [number, number, number, numb
padding: ${token.paddingMD}px;
overflow: hidden;
`,
colWrapPaddingLess: css`
padding: 0;
`,
listWrap: css`
display: flex;
flex-direction: column;
@ -66,37 +72,77 @@ const useStyle = createStyles(({ token }, markPos: [number, number, number, numb
`,
}));
function getSemanticCells(semanticPath: string) {
return semanticPath.split('.');
}
function HighlightExample(props: { componentName: string; semanticName: string }) {
const { componentName, semanticName } = props;
const highlightCode = React.useMemo(() => {
const classNames = set({}, getSemanticCells(semanticName), `my-classname`);
const styles = set({}, getSemanticCells(semanticName), { color: 'red' });
function format(obj: object) {
const str = JSON.stringify(obj, null, 2);
return (
str
// Add space
.split('\n')
.map((line) => ` ${line}`)
.join('\n')
.trim()
// Replace quotes
.replace(/"/g, "'")
// Remove key quotes
.replace(/'([^']+)':/g, '$1:')
);
}
const code = `
<${componentName}
classNames={${format(classNames)}}
styles={${format(styles)}}
/>`.trim();
return Prism.highlight(code, Prism.languages.javascript, 'jsx');
}, [componentName, semanticName]);
return (
// biome-ignore lint: lint/security/noDangerouslySetInnerHtml
<div dangerouslySetInnerHTML={{ __html: highlightCode }} />
);
}
export interface SemanticPreviewProps {
componentName: string;
semantics: { name: string; desc: string; version?: string }[];
children: React.ReactElement<any>;
height?: number;
padding?: false;
}
const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
const { semantics = [], children, height, componentName = 'Component' } = props;
const { semantics = [], children, height, padding, componentName = 'Component' } = props;
const { token } = theme.useToken();
// ======================= Semantic =======================
const getMarkClassName = React.useCallback(
(semanticKey: string) => `semantic-mark-${semanticKey}`,
(semanticKey: string) => `semantic-mark-${semanticKey}`.replace(/\./g, '-'),
[],
);
const semanticClassNames = React.useMemo<Record<string, string>>(() => {
const classNames: Record<string, string> = {};
let classNames: Record<string, string> = {};
semantics.forEach((semantic) => {
classNames[semantic.name] = getMarkClassName(semantic.name);
const pathCell = getSemanticCells(semantic.name);
classNames = set(classNames, pathCell, getMarkClassName(semantic.name));
});
return classNames;
}, [semantics]);
const cloneNode = React.cloneElement(children, {
classNames: semanticClassNames,
});
// ======================== Hover =========================
const containerRef = React.useRef<HTMLDivElement>(null);
@ -137,11 +183,33 @@ const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
};
}, [hoverSemantic]);
const hoveredSemanticClassNames = React.useMemo(() => {
if (!hoverSemantic) {
return semanticClassNames;
}
const hoverCell = getSemanticCells(hoverSemantic);
const clone = set(
semanticClassNames,
hoverCell,
classnames(get(semanticClassNames, hoverCell), getMarkClassName('active')),
);
return clone;
}, [semanticClassNames, hoverSemantic]);
// ======================== Render ========================
const cloneNode = React.cloneElement(children, {
classNames: hoveredSemanticClassNames,
});
return (
<div className={classnames(styles.container)} ref={containerRef}>
<Row style={{ minHeight: height }}>
<Col span={16} className={classnames(styles.colWrap)}>
<Col
span={16}
className={classnames(styles.colWrap, padding === false && styles.colWrapPaddingLess)}
>
<ConfigProvider theme={{ token: { motion: false } }}>{cloneNode}</ConfigProvider>
</Col>
<Col span={8}>
@ -166,16 +234,10 @@ const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
<Typography style={{ fontSize: 12, minWidth: 300 }}>
<pre dir="ltr">
<code dir="ltr">
{`<${componentName}
classNames={{
${semantic.name}: 'my-${componentName.toLowerCase()}',
}}
styles={{
${semantic.name}: { color: 'red' },
}}
>
...
</${componentName}>`}
<HighlightExample
componentName={componentName}
semanticName={semantic.name}
/>
</code>
</pre>
</Typography>

View file

@ -160,7 +160,7 @@ const SubTokenTable: React.FC<SubTokenTableProps> = (props) => {
{title}
<Popover
title={null}
destroyTooltipOnHide
destroyOnClose
styles={{ root: { width: 400 } }}
content={
<Typography>

View file

@ -277,7 +277,7 @@ const ComponentChangelog: React.FC<Readonly<React.PropsWithChildren>> = (props)
{version}
{bugVersionInfo.match && (
<Popover
destroyTooltipOnHide
destroyOnClose
placement="right"
title={<span className={styles.bugReasonTitle}>{locale.bugList}</span>}
content={

View file

@ -306,7 +306,7 @@ const Header: React.FC = () => {
className={styles.versionSelect}
defaultValue={pkg.version}
onChange={handleVersionChange}
dropdownStyle={getDropdownStyle}
styles={{ popup: { root: getDropdownStyle } }}
popupMatchSelectWidth={false}
getPopupContainer={(trigger) => trigger.parentNode}
options={versionOptions}
@ -338,7 +338,7 @@ const Header: React.FC = () => {
target="_blank"
rel="noreferrer"
>
<Tooltip title="GitHub" destroyTooltipOnHide>
<Tooltip title="GitHub" destroyOnClose>
<Button type="text" icon={<GithubOutlined />} style={{ fontSize: 16 }} />
</Tooltip>
</a>,

View file

@ -1,8 +1,8 @@
<!--
First of all, thank you for your contribution! 😄
For requesting to pull a new feature or bugfix, please send it from a feature/bugfix branch based on the `master` branch.
Before submitting your pull request, please make sure the checklist below is confirmed.
Your pull requests will be merged after one of the collaborators approve.
Before submitting your pull request, please make sure the checklist below is filled out.
Your pull requests will be merged after one of the collaborators approves.
Thank you!
-->
@ -41,7 +41,7 @@ Thank you!
### 📝 Change Log
> - Read [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) like a cat tracks a laser pointer.
> - Read [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)! Track your changes, like a cat tracks a laser pointer.
> - Describe the impact of the changes on developers, not the solution approach.
> - Reference: https://ant.design/changelog

View file

@ -9,9 +9,9 @@ tag: vVERSION
#### Release Schedule
- Weekly release: patch version at the end of every week for routine bugfix (anytime for urgent bugfix).
- Weekly release: patch version at the end of every week for routine bugfixes (anytime for an urgent bugfix).
- Monthly release: minor version at the end of every month for new features.
- Major version release is not included in this schedule for breaking change and new features.
- Major version release is not included in this schedule for breaking changes and new features.
---

View file

@ -195,7 +195,6 @@ $ npm start
<img src="https://openomy.app/svg?repo=ant-design/ant-design&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
</a>
请参考[贡献指南](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》](https://www.chiark.greenend.org.uk/%7Esgtatham/bugs-cn.html)、[《如何向开源项目提交无法解答的问题》](https://zhuanlan.zhihu.com/p/25795393),更好的问题更容易获得帮助。

View file

@ -138,7 +138,7 @@ $ npm install
$ 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).
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)](https://makeapullrequest.com)
@ -179,7 +179,7 @@ Open your browser and visit http://127.0.0.1:8001 , see more at [Development](ht
Let's build a better antd together.
We warmly invite contributions from everyone. Before you get started, please take a moment to review our [Contributing Guide](https://ant.design/docs/react/contributing). Feel free to share your ideas through [Pull Requests](https://github.com/ant-design/ant-design/pulls) or [GitHub Issues](https://github.com/ant-design/ant-design/issues). If you're interested in enhancing our codebase, explore the [Development Instructions](https://github.com/ant-design/ant-design/wiki/Development) and enjoy your coding journey! :)
We warmly invite contributions from everyone. Before you get started, please take a moment to review our [Contribution Guide](https://ant.design/docs/react/contributing). Feel free to share your ideas through [Pull Requests](https://github.com/ant-design/ant-design/pulls) or [GitHub Issues](https://github.com/ant-design/ant-design/issues). If you're interested in enhancing our codebase, explore the [Development Instructions](https://github.com/ant-design/ant-design/wiki/Development) and enjoy your coding journey! :)
For collaborators, adhere to our [Pull Request Principle](https://github.com/ant-design/ant-design/wiki/PR-principle) and utilize our [Pull Request Template](https://github.com/ant-design/ant-design/wiki/PR-principle#pull-request-template) when creating a Pull Request.

View file

@ -5,23 +5,20 @@ import useResponsiveObserver from '../responsiveObserver';
describe('Test ResponsiveObserve', () => {
it('test ResponsiveObserve subscribe and unsubscribe', () => {
let responsiveObserveRef: any;
const Demo = () => {
let responsiveRef: any = null;
const Demo: React.FC = () => {
const responsiveObserver = useResponsiveObserver();
responsiveObserveRef = responsiveObserver;
responsiveRef = responsiveObserver;
return null;
};
render(<Demo />);
const subscribeFunc = jest.fn();
const token = responsiveObserveRef.subscribe(subscribeFunc);
expect(
responsiveObserveRef.matchHandlers[responsiveObserveRef.responsiveMap.xs].mql.matches,
).toBeTruthy();
const token = responsiveRef.subscribe(subscribeFunc);
expect(responsiveRef.matchHandlers[responsiveRef.responsiveMap.xs].mql.matches).toBeTruthy();
expect(subscribeFunc).toHaveBeenCalledTimes(1);
responsiveObserveRef.unsubscribe(token);
responsiveRef.unsubscribe(token);
expect(
responsiveObserveRef.matchHandlers[responsiveObserveRef.responsiveMap.xs].mql.removeListener,
responsiveRef.matchHandlers[responsiveRef.responsiveMap.xs].mql?.removeEventListener,
).toHaveBeenCalled();
});
});

View file

@ -0,0 +1,20 @@
import type { ReactNode } from 'react';
import { isValidElement } from 'react';
import type { TooltipProps } from '../tooltip';
function convertToTooltipProps<P extends TooltipProps>(tooltip: P | ReactNode): P | null {
// isNil
if (tooltip === undefined || tooltip === null) {
return null;
}
if (typeof tooltip === 'object' && !isValidElement(tooltip)) {
return tooltip as P;
}
return {
title: tooltip,
} as P;
}
export default convertToTooltipProps;

View file

@ -5,6 +5,10 @@ import type { DialogProps } from 'rc-dialog';
import pickAttrs from 'rc-util/lib/pickAttrs';
import extendsObject from '../extendsObject';
import { useLocale } from '../../locale';
import defaultLocale from '../../locale/en_US';
import type { HTMLAriaDataAttributes } from '../aria-data-attrs';
export type ClosableType = DialogProps['closable'];
export type BaseContextClosable = { closable?: ClosableType; closeIcon?: ReactNode };
@ -59,7 +63,6 @@ function useClosableConfig(closableCollection?: ClosableCollection | null) {
...closable,
};
}
return closableConfig;
}, [closable, closeIcon]);
}
@ -85,10 +88,17 @@ export default function useClosable(
propCloseCollection?: ClosableCollection,
contextCloseCollection?: ClosableCollection | null,
fallbackCloseCollection: FallbackCloseCollection = EmptyFallbackCloseCollection,
): [closable: boolean, closeIcon: React.ReactNode, closeBtnIsDisabled: boolean] {
): [
closable: boolean,
closeIcon: React.ReactNode,
closeBtnIsDisabled: boolean,
ariaOrDataProps?: HTMLAriaDataAttributes,
] {
// Align the `props`, `context` `fallback` to config object first
const propCloseConfig = useClosableConfig(propCloseCollection);
const contextCloseConfig = useClosableConfig(contextCloseCollection);
const [contextLocale] = useLocale('global', defaultLocale.global);
const closeBtnIsDisabled =
typeof propCloseConfig !== 'boolean' ? !!propCloseConfig?.disabled : false;
const mergedFallbackCloseCollection = React.useMemo(
@ -128,30 +138,34 @@ export default function useClosable(
// Calculate the final closeIcon
return React.useMemo(() => {
if (mergedClosableConfig === false) {
return [false, null, closeBtnIsDisabled];
return [false, null, closeBtnIsDisabled, {}];
}
const { closeIconRender } = mergedFallbackCloseCollection;
const { closeIcon } = mergedClosableConfig;
let mergedCloseIcon: ReactNode = closeIcon;
// Wrap the closeIcon with aria props
const ariaOrDataProps = pickAttrs(mergedClosableConfig, true);
if (mergedCloseIcon !== null && mergedCloseIcon !== undefined) {
// Wrap the closeIcon if needed
if (closeIconRender) {
mergedCloseIcon = closeIconRender(closeIcon);
}
// Wrap the closeIcon with aria props
const ariaProps = pickAttrs(mergedClosableConfig, true);
if (Object.keys(ariaProps).length) {
mergedCloseIcon = React.isValidElement(mergedCloseIcon) ? (
React.cloneElement(mergedCloseIcon, ariaProps)
) : (
<span {...ariaProps}>{mergedCloseIcon}</span>
);
}
mergedCloseIcon = React.isValidElement(mergedCloseIcon) ? (
React.cloneElement(mergedCloseIcon, {
'aria-label': contextLocale.close,
...ariaOrDataProps,
} as HTMLAriaDataAttributes)
) : (
<span aria-label={contextLocale.close} {...ariaOrDataProps}>
{mergedCloseIcon}
</span>
);
}
return [true, mergedCloseIcon, closeBtnIsDisabled];
return [true, mergedCloseIcon, closeBtnIsDisabled, ariaOrDataProps];
}, [mergedClosableConfig, mergedFallbackCloseCollection]);
}

View file

@ -0,0 +1,19 @@
type MQListenerHandler = (mql: MediaQueryList, handler: (e: MediaQueryListEvent) => void) => void;
export const addMediaQueryListener: MQListenerHandler = (mql, handler) => {
// Don't delete here, please keep the code compatible
if (typeof mql?.addEventListener !== 'undefined') {
mql.addEventListener('change', handler);
} else if (typeof mql?.addListener !== 'undefined') {
mql.addListener(handler);
}
};
export const removeMediaQueryListener: MQListenerHandler = (mql, handler) => {
// Don't delete here, please keep the code compatible
if (typeof mql?.removeEventListener !== 'undefined') {
mql.removeEventListener('change', handler);
} else if (typeof mql?.removeListener !== 'undefined') {
mql.removeListener(handler);
}
};

View file

@ -2,6 +2,7 @@ import React from 'react';
import type { GlobalToken } from '../theme/internal';
import { useToken } from '../theme/internal';
import { addMediaQueryListener, removeMediaQueryListener } from './mediaQueryUtil';
export type Breakpoint = 'xxl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs';
export type BreakpointMap = Record<Breakpoint, string>;
@ -121,22 +122,20 @@ const useResponsiveObserver = () => {
}
},
register() {
Object.keys(responsiveMap).forEach((screen) => {
const matchMediaQuery = responsiveMap[screen as Breakpoint];
Object.entries(responsiveMap).forEach(([screen, mediaQuery]) => {
const listener = ({ matches }: { matches: boolean }) => {
this.dispatch({ ...screens, [screen]: matches });
};
const mql = window.matchMedia(matchMediaQuery);
mql.addListener(listener);
this.matchHandlers[matchMediaQuery] = { mql, listener };
const mql = window.matchMedia(mediaQuery);
addMediaQueryListener(mql, listener);
this.matchHandlers[mediaQuery] = { mql, listener };
listener(mql);
});
},
unregister() {
Object.keys(responsiveMap).forEach((screen) => {
const matchMediaQuery = responsiveMap[screen as Breakpoint];
const handler = this.matchHandlers[matchMediaQuery];
handler?.mql.removeListener(handler?.listener);
Object.values(responsiveMap).forEach((mediaQuery) => {
const handler = this.matchHandlers[mediaQuery];
removeMediaQueryListener(handler?.mql, handler?.listener);
});
subscribers.clear();
},

View file

@ -1,5 +1,5 @@
import * as React from 'react';
import classNames from 'classnames';
import cls from 'classnames';
import type { BaseSelectRef } from 'rc-select';
import toArray from 'rc-util/lib/Children/toArray';
import omit from 'rc-util/lib/omit';
@ -36,12 +36,21 @@ export interface AutoCompleteProps<
/** @deprecated Please use `options` instead */
dataSource?: DataSourceItemType[];
status?: InputStatus;
/** @deprecated Please use `classNames.popup.root` instead */
popupClassName?: string;
/** @deprecated Please use `popupClassName` instead */
/** @deprecated Please use `classNames.popup.root` instead */
dropdownClassName?: string;
/** @deprecated Please use `popupMatchSelectWidth` instead */
dropdownMatchSelectWidth?: boolean | number;
popupMatchSelectWidth?: boolean | number;
/** @deprecated Please use `popupRender` instead */
dropdownRender?: (menu: React.ReactElement) => React.ReactElement;
popupRender?: (menu: React.ReactElement) => React.ReactElement;
/** @deprecated Please use `styles.popup.root` instead */
dropdownStyle?: React.CSSProperties;
/** @deprecated Please use `onOpenChange` instead */
onDropdownVisibleChange?: (visible: boolean) => void;
onOpenChange?: (visible: boolean) => void;
}
function isSelectOptionOrSelectOptGroup(child: any): boolean {
@ -59,9 +68,21 @@ const AutoComplete: React.ForwardRefRenderFunction<RefSelectProps, AutoCompleteP
dropdownClassName,
children,
dataSource,
dropdownStyle,
dropdownRender,
popupRender,
onDropdownVisibleChange,
onOpenChange,
styles,
classNames,
} = props;
const childNodes: React.ReactElement[] = toArray(children);
const mergedPopupStyle = styles?.popup?.root || dropdownStyle;
const mergedPopupClassName = classNames?.popup?.root || popupClassName || dropdownClassName;
const mergedPopupRender = popupRender || dropdownRender;
const mergedOnOpenChange = onOpenChange || onDropdownVisibleChange;
// ============================= Input =============================
let customizeInput: React.ReactElement | undefined;
@ -112,15 +133,25 @@ const AutoComplete: React.ForwardRefRenderFunction<RefSelectProps, AutoCompleteP
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('AutoComplete');
warning.deprecated(!('dataSource' in props), 'dataSource', 'options');
warning(
!customizeInput || !('size' in props),
'usage',
'You need to control style self instead of setting `size` when using customize input.',
);
warning.deprecated(!dropdownClassName, 'dropdownClassName', 'popupClassName');
const deprecatedProps = {
dropdownMatchSelectWidth: 'popupMatchSelectWidth',
dropdownStyle: 'styles.popup.root',
dropdownClassName: 'classNames.popup.root',
popupClassName: 'classNames.popup.root',
dropdownRender: 'popupRender',
onDropdownVisibleChange: 'onOpenChange',
dataSource: 'options',
};
Object.entries(deprecatedProps).forEach(([oldProp, newProp]) => {
warning.deprecated(!(oldProp in props), oldProp, newProp);
});
}
const { getPrefixCls } = React.useContext<ConfigConsumerProps>(ConfigContext);
@ -128,21 +159,33 @@ const AutoComplete: React.ForwardRefRenderFunction<RefSelectProps, AutoCompleteP
const prefixCls = getPrefixCls('select', customizePrefixCls);
// ============================ zIndex ============================
const [zIndex] = useZIndex('SelectLike', props.dropdownStyle?.zIndex as number);
const [zIndex] = useZIndex('SelectLike', mergedPopupStyle?.zIndex as number);
return (
<Select
ref={ref}
suffixIcon={null}
{...omit(props, ['dataSource', 'dropdownClassName'])}
{...omit(props, ['dataSource', 'dropdownClassName', 'popupClassName'])}
prefixCls={prefixCls}
popupClassName={popupClassName || dropdownClassName}
dropdownStyle={{
...props.dropdownStyle,
zIndex,
classNames={{
popup: {
root: mergedPopupClassName,
},
root: classNames?.root,
}}
className={classNames(`${prefixCls}-auto-complete`, className)}
styles={{
popup: {
root: {
...mergedPopupStyle,
zIndex,
},
},
root: styles?.root,
}}
className={cls(`${prefixCls}-auto-complete`, className)}
mode={Select.SECRET_COMBOBOX_MODE_DO_NOT_USE as SelectProps['mode']}
popupRender={mergedPopupRender}
onOpenChange={mergedOnOpenChange}
{...{
// Internal api
getInputElement,

View file

@ -728,7 +728,7 @@ exports[`renders components/auto-complete/demo/basic.tsx extend context correctl
exports[`renders components/auto-complete/demo/certain-category.tsx extend context correctly 1`] = `
<div
class="ant-select ant-select-lg ant-select-outlined ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
class="ant-select ant-select-outlined ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
style="width: 250px;"
>
<div
@ -1096,11 +1096,7 @@ exports[`renders components/auto-complete/demo/certain-category.tsx extend conte
</div>
`;
exports[`renders components/auto-complete/demo/certain-category.tsx extend context correctly 2`] = `
[
"Warning: [antd: AutoComplete] You need to control style self instead of setting \`size\` when using customize input.",
]
`;
exports[`renders components/auto-complete/demo/certain-category.tsx extend context correctly 2`] = `[]`;
exports[`renders components/auto-complete/demo/custom.tsx extend context correctly 1`] = `
<div
@ -2899,7 +2895,7 @@ exports[`renders components/auto-complete/demo/status.tsx extend context correct
exports[`renders components/auto-complete/demo/uncertain-category.tsx extend context correctly 1`] = `
<div
class="ant-select ant-select-lg ant-select-outlined ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
class="ant-select ant-select-outlined ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
style="width: 300px;"
>
<div
@ -2986,11 +2982,7 @@ exports[`renders components/auto-complete/demo/uncertain-category.tsx extend con
</div>
`;
exports[`renders components/auto-complete/demo/uncertain-category.tsx extend context correctly 2`] = `
[
"Warning: [antd: AutoComplete] You need to control style self instead of setting \`size\` when using customize input.",
]
`;
exports[`renders components/auto-complete/demo/uncertain-category.tsx extend context correctly 2`] = `[]`;
exports[`renders components/auto-complete/demo/variant.tsx extend context correctly 1`] = `
<div

View file

@ -445,7 +445,7 @@ Array [
exports[`renders components/auto-complete/demo/certain-category.tsx correctly 1`] = `
<div
class="ant-select ant-select-lg ant-select-outlined ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
class="ant-select ant-select-outlined ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
style="width:250px"
>
<div
@ -1669,7 +1669,7 @@ exports[`renders components/auto-complete/demo/status.tsx correctly 1`] = `
exports[`renders components/auto-complete/demo/uncertain-category.tsx correctly 1`] = `
<div
class="ant-select ant-select-lg ant-select-outlined ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
class="ant-select ant-select-outlined ant-select-auto-complete ant-select-single ant-select-customize-input ant-select-show-search"
style="width:300px"
>
<div

View file

@ -111,10 +111,101 @@ describe('AutoComplete', () => {
/>,
);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: AutoComplete] `dropdownClassName` is deprecated. Please use `popupClassName` instead.',
'Warning: [antd: AutoComplete] `dropdownClassName` is deprecated. Please use `classNames.popup.root` instead.',
);
expect(container.querySelector('.legacy')).toBeTruthy();
errSpy.mockRestore();
});
it('deprecated popupClassName', () => {
resetWarned();
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const { container } = render(
<AutoComplete
popupClassName="legacy"
open
options={[{ label: 'little', value: 'little' }]}
searchValue="l"
/>,
);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: AutoComplete] `popupClassName` is deprecated. Please use `classNames.popup.root` instead.',
);
expect(container.querySelector('.legacy')).toBeTruthy();
errSpy.mockRestore();
});
it('deprecated dropdownMatchSelectWidth', () => {
resetWarned();
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(
<AutoComplete
dropdownMatchSelectWidth
open
options={[{ label: 'little', value: 'little' }]}
/>,
);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: AutoComplete] `dropdownMatchSelectWidth` is deprecated. Please use `popupMatchSelectWidth` instead.',
);
errSpy.mockRestore();
});
it('deprecated dropdownStyle', () => {
resetWarned();
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(
<AutoComplete
dropdownStyle={{ color: 'red' }}
open
options={[{ label: 'little', value: 'little' }]}
/>,
);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: AutoComplete] `dropdownStyle` is deprecated. Please use `styles.popup.root` instead.',
);
errSpy.mockRestore();
});
it('deprecated dropdownRender', () => {
resetWarned();
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(
<AutoComplete
dropdownRender={(menu) => <div>{menu}</div>}
open
options={[{ label: 'little', value: 'little' }]}
/>,
);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: AutoComplete] `dropdownRender` is deprecated. Please use `popupRender` instead.',
);
errSpy.mockRestore();
});
it('deprecated onDropdownVisibleChange', () => {
resetWarned();
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(
<AutoComplete
onDropdownVisibleChange={() => {}}
options={[{ label: 'little', value: 'little' }]}
/>,
);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: AutoComplete] `onDropdownVisibleChange` is deprecated. Please use `onOpenChange` instead.',
);
errSpy.mockRestore();
});
});

View file

@ -0,0 +1,32 @@
import React from 'react';
import { AutoComplete } from 'antd';
import SelectSemanticTemplate from '../../../.dumi/components/SelectSemanticTemplate';
const mockVal = (str: string, repeat = 1) => ({
value: str.repeat(repeat),
label: str.repeat(repeat),
});
const getPanelValue = (searchText: string) =>
!searchText ? [] : [mockVal(searchText), mockVal(searchText, 2), mockVal(searchText, 3)];
const App: React.FC = () => {
const [options, setOptions] = React.useState([
{ value: 'aojunhao123', label: 'aojunhao123' },
{ value: 'thinkasany', label: 'thinkasany' },
]);
return (
<SelectSemanticTemplate
component={AutoComplete}
componentName="AutoComplete"
style={{ width: 200 }}
options={options}
onSearch={(text: string) => setOptions(getPanelValue(text))}
placeholder="input here"
/>
);
};
export default App;

View file

@ -40,11 +40,10 @@ const options = [
const App: React.FC = () => (
<AutoComplete
popupClassName="certain-category-search-dropdown"
classNames={{ popup: { root: 'certain-category-search-dropdown' } }}
popupMatchSelectWidth={500}
style={{ width: 250 }}
options={options}
size="large"
>
<Input.Search size="large" placeholder="input here" />
</AutoComplete>

View file

@ -53,7 +53,6 @@ const App: React.FC = () => {
options={options}
onSelect={onSelect}
onSearch={handleSearch}
size="large"
>
<Input.Search size="large" placeholder="input here" enterButton />
</AutoComplete>

View file

@ -48,12 +48,15 @@ Common props ref[Common props](/docs/react/common-props)
| backfill | If backfill selected item the input when using keyboard | boolean | false | |
| children (for customize input element) | Customize input element | HTMLInputElement \| HTMLTextAreaElement \| React.ReactElement&lt;InputProps> | &lt;Input /> | |
| children (for dataSource) | Data source to auto complete | React.ReactElement&lt;OptionProps> \| Array&lt;React.ReactElement&lt;OptionProps>> | - | |
| classNames | Semantic DOM class | [Record<SemanticDOM, string>](#semantic-dom) | - | 5.25.0 |
| defaultActiveFirstOption | Whether active first option by default | boolean | true | |
| defaultOpen | Initial open state of dropdown | boolean | - | |
| defaultValue | Initial selected option | string | - | |
| disabled | Whether disabled select | boolean | false | |
| dropdownRender | Customize dropdown content | (menus: ReactNode) => ReactNode | - | 4.24.0 |
| popupClassName | The className of dropdown menu | string | - | 4.23.0 |
| ~~dropdownRender~~ | Customize dropdown content, use `popupRender` instead | (originNode: ReactNode) => ReactNode | - | 4.24.0 |
| popupRender | Customize dropdown content | (originNode: ReactNode) => ReactNode | - | |
| ~~dropdownStyle~~ | The style of dropdown menu, use `styles.popup.root` instead | CSSProperties | - | |
| ~~popupClassName~~ | The className of dropdown menu, use `classNames.popup.root` instead | string | - | 4.23.0 |
| popupMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width. Default set `min-width` same as input. Will ignore when value less than select width. `false` will disable virtual scroll | boolean \| number | true | |
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns true, the option will be included in the filtered set; Otherwise, it will be excluded | boolean \| function(inputValue, option) | true | |
| getPopupContainer | Parent node of the dropdown. Default to body, if you encountered positioning problems during scroll, try changing to the scrollable area and position relative to it. [Example](https://codesandbox.io/s/4j168r7jw0) | function(triggerNode) | () => document.body | |
@ -64,11 +67,13 @@ Common props ref[Common props](/docs/react/common-props)
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
| size | The size of the input box | `large` \| `middle` \| `small` | - | |
| value | Selected option | string | - | |
| styles | Semantic DOM style | [Record<SemanticDOM, CSSProperties>](#semantic-dom) | - | 5.25.0 |
| variant | Variants of input | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 |
| virtual | Disable virtual scroll when set to false | boolean | true | 4.1.0 |
| onBlur | Called when leaving the component | function() | - | |
| onChange | Called when selecting an option or changing an input value | function(value) | - | |
| onDropdownVisibleChange | Call when dropdown open | function(open) | - | |
| ~~onDropdownVisibleChange~~ | Called when dropdown open, use `onOpenChange` instead | (open: boolean) => void | - | |
| onOpenChange | Called when dropdown open | (open: boolean) => void | - | |
| onFocus | Called when entering the component | function() | - | |
| onSearch | Called when searching items | function(value) | - | |
| onSelect | Called when a option is selected. param is option's value and option instance | function(value, option) | - | |
@ -83,6 +88,10 @@ Common props ref[Common props](/docs/react/common-props)
| blur() | Remove focus | |
| focus() | Get focus | |
## Semantic DOM
<code src="./demo/_semantic.tsx" simplify="true"></code>
## Design Token
<ComponentTokenTable component="Select"></ComponentTokenTable>

View file

@ -49,12 +49,15 @@ demo:
| backfill | 使用键盘选择选项的时候把选中项回填到输入框中 | boolean | false | |
| children (自动完成的数据源) | 自动完成的数据源,不能和自定义输入框同时配置 | React.ReactElement&lt;OptionProps> \| Array&lt;React.ReactElement&lt;OptionProps>> | - | |
| children (自定义输入框) | 自定义输入框,不能和自动完成的数据源同时配置 | HTMLInputElement \| HTMLTextAreaElement \| React.ReactElement&lt;InputProps> | &lt;Input /> | |
| classNames | 语义化结构 class | [Record<SemanticDOM, string>](#semantic-dom) | - | 5.25.0 |
| defaultActiveFirstOption | 是否默认高亮第一个选项 | boolean | true | |
| defaultOpen | 是否默认展开下拉菜单 | boolean | - | |
| defaultValue | 指定默认选中的条目 | string | - | |
| disabled | 是否禁用 | boolean | false | |
| dropdownRender | 自定义下拉框内容 | (menus: ReactNode) => ReactNode | - | 4.24.0 |
| popupClassName | 下拉菜单的 className 属性 | string | - | 4.23.0 |
| ~~dropdownRender~~ | 自定义下拉框内容,使用 `popupRender` 替换 | (originNode: ReactNode) => ReactNode | - | 4.24.0 |
| popupRender | 自定义下拉框内容 | (originNode: ReactNode) => ReactNode | - | |
| ~~popupClassName~~ | 下拉菜单的 className 属性,使用 `classNames.popup.root` 替换 | string | - | 4.23.0 |
| ~~dropdownStyle~~ | 下拉菜单的 style 属性,使用 `styles.popup.root` 替换 | CSSProperties | - | |
| popupMatchSelectWidth | 下拉菜单和选择器同宽。默认将设置 `min-width`当值小于选择框宽度时会被忽略。false 时会关闭虚拟滚动 | boolean \| number | true | |
| filterOption | 是否根据输入项进行筛选。当其为一个函数时,会接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 true反之则返回 false | boolean \| function(inputValue, option) | true | |
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](https://codesandbox.io/s/4j168r7jw0) | function(triggerNode) | () => document.body | |
@ -64,12 +67,14 @@ demo:
| placeholder | 输入框提示 | string | - | |
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
| size | 控件大小 | `large` \| `middle` \| `small` | - | |
| styles | 语义化结构 style | [Record<SemanticDOM, CSSProperties>](#semantic-dom) | - | 5.25.0 |
| value | 指定当前选中的条目 | string | - | |
| variant | 形态变体 | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 |
| virtual | 设置 false 时关闭虚拟滚动 | boolean | true | 4.1.0 |
| onBlur | 失去焦点时的回调 | function() | - | |
| onChange | 选中 option或 input 的 value 变化时,调用此函数 | function(value) | - | |
| onDropdownVisibleChange | 展开下拉菜单的回调 | function(open) | - | |
| ~~onDropdownVisibleChange~~ | 展开下拉菜单的回调,使用 `onOpenChange` 替换 | (open: boolean) => void | - | |
| onOpenChange | 展开下拉菜单的回调 | (open: boolean) => void | - | |
| onFocus | 获得焦点时的回调 | function() | - | |
| onSearch | 搜索补全项的时候调用 | function(value) | - | |
| onSelect | 被选中时调用,参数为选中项的 value 值 | function(value, option) | - | |
@ -84,6 +89,10 @@ demo:
| blur() | 移除焦点 | |
| focus() | 获取焦点 | |
## Semantic DOM
<code src="./demo/_semantic.tsx" simplify="true"></code>
## 主题变量Design Token
<ComponentTokenTable component="Select"></ComponentTokenTable>

View file

@ -127,7 +127,7 @@ const AvatarGroup: React.FC<AvatarGroupProps> = (props) => {
};
childrenShow.push(
<Popover key="avatar-popover-key" destroyTooltipOnHide {...mergeProps}>
<Popover key="avatar-popover-key" destroyOnClose {...mergeProps}>
<Avatar style={mergeStyle}>{`+${numOfChildren - mergeCount}`}</Avatar>
</Popover>,
);

View file

@ -508,9 +508,46 @@ describe('Button', () => {
const { getByRole } = render(
<Button href="https://example.com" onClick={handleClick}>
Link
</Button>
</Button>,
);
fireEvent.click(getByRole('link'));
expect(handleClick).toHaveBeenCalled();
});
it('ConfigProvider support button variant', () => {
const { container } = render(
<ConfigProvider button={{ variant: 'dashed', color: 'blue' }}>
<Button>Button</Button>
</ConfigProvider>,
);
expect(container.firstChild).toHaveClass('ant-btn-variant-dashed');
expect(container.firstChild).toHaveClass('ant-btn-color-blue');
});
it('should show the component internal properties', () => {
const { container } = render(
<ConfigProvider button={{ variant: 'dashed', color: 'blue' }}>
<Button variant="filled" color="green">
Button
</Button>
</ConfigProvider>,
);
expect(container.firstChild).toHaveClass('ant-btn-variant-filled');
expect(container.firstChild).toHaveClass('ant-btn-color-green');
});
it('button type win the context', () => {
const { container } = render(
<ConfigProvider button={{ variant: 'dashed', color: 'green' }}>
<Button type="primary" danger>
Button
</Button>
</ConfigProvider>,
);
expect(container.querySelector('.ant-btn-variant-solid')).toBeTruthy();
expect(container.querySelector('.ant-btn-color-dangerous')).toBeTruthy();
});
});

View file

@ -5,7 +5,7 @@ import { useComposeRef } from 'rc-util/lib/ref';
import { devUseWarning } from '../_util/warning';
import Wave from '../_util/wave';
import { useComponentConfig } from '../config-provider/context';
import { ConfigContext, useComponentConfig } from '../config-provider/context';
import DisabledContext from '../config-provider/DisabledContext';
import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext';
@ -127,20 +127,31 @@ const InternalCompoundedButton = React.forwardRef<
// https://github.com/ant-design/ant-design/issues/47605
// Compatible with original `type` behavior
const mergedType = type || 'default';
const { button } = React.useContext(ConfigContext);
const [mergedColor, mergedVariant] = useMemo<ColorVariantPairType>(() => {
// >>>>> Local
// Color & Variant
if (color && variant) {
return [color, variant];
}
const colorVariantPair = ButtonTypeMap[mergedType] || [];
if (danger) {
return ['danger', colorVariantPair[1]];
// Sugar syntax
if (type || danger) {
const colorVariantPair = ButtonTypeMap[mergedType] || [];
if (danger) {
return ['danger', colorVariantPair[1]];
}
return colorVariantPair;
}
return colorVariantPair;
}, [type, color, variant, danger]);
// >>> Context fallback
if (button?.color && button?.variant) {
return [button.color, button.variant];
}
return ['default', 'outlined'];
}, [type, color, variant, danger, button?.variant, button?.color]);
const isDanger = mergedColor === 'danger';
const mergedColorText = isDanger ? 'dangerous' : mergedColor;
@ -291,9 +302,10 @@ const InternalCompoundedButton = React.forwardRef<
cssVarCls,
{
[`${prefixCls}-${shape}`]: shape !== 'default' && shape,
// line(253 - 254): Compatible with versions earlier than 5.21.0
// Compatible with versions earlier than 5.21.0
[`${prefixCls}-${mergedType}`]: mergedType,
[`${prefixCls}-dangerous`]: danger,
[`${prefixCls}-color-${mergedColorText}`]: mergedColorText,
[`${prefixCls}-variant-${mergedVariant}`]: mergedVariant,
[`${prefixCls}-${sizeCls}`]: sizeCls,

View file

@ -93,13 +93,11 @@ describe('Cascader', () => {
});
it('popup correctly when panel is open', () => {
const onPopupVisibleChange = jest.fn();
const { container } = render(
<Cascader options={options} onPopupVisibleChange={onPopupVisibleChange} />,
);
const onOpenChange = jest.fn();
const { container } = render(<Cascader options={options} onOpenChange={onOpenChange} />);
toggleOpen(container);
expect(isOpen(container)).toBeTruthy();
expect(onPopupVisibleChange).toHaveBeenCalledWith(true);
expect(onOpenChange).toHaveBeenCalledWith(true);
});
it('support controlled mode', () => {
@ -548,13 +546,87 @@ describe('Cascader', () => {
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const { container } = render(<Cascader dropdownClassName="legacy" open />);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Cascader] `dropdownClassName` is deprecated. Please use `popupClassName` instead.',
'Warning: [antd: Cascader] `dropdownClassName` is deprecated. Please use `classNames.popup.root` instead.',
);
expect(container.querySelector('.legacy')).toBeTruthy();
errSpy.mockRestore();
});
it('legacy dropdownStyle', () => {
resetWarned();
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const customStyle = { background: 'red' };
const { container } = render(<Cascader dropdownStyle={customStyle} open />);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Cascader] `dropdownStyle` is deprecated. Please use `styles.popup.root` instead.',
);
expect(container.querySelector('.ant-select-dropdown')?.getAttribute('style')).toContain(
'background: red',
);
errSpy.mockRestore();
});
it('legacy dropdownRender', () => {
resetWarned();
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const customContent = <div className="custom-dropdown-content">Custom Content</div>;
const dropdownRender = (menu: React.ReactElement) => (
<>
{menu}
{customContent}
</>
);
const { container } = render(<Cascader dropdownRender={dropdownRender} open />);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Cascader] `dropdownRender` is deprecated. Please use `popupRender` instead.',
);
expect(container.querySelector('.custom-dropdown-content')).toBeTruthy();
errSpy.mockRestore();
});
it('legacy dropdownMenuColumnStyle', () => {
resetWarned();
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const columnStyle = { background: 'red' };
const { getByRole } = render(
<Cascader
options={[{ label: 'test', value: 1 }]}
dropdownMenuColumnStyle={columnStyle}
open
/>,
);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Cascader] `dropdownMenuColumnStyle` is deprecated. Please use `popupMenuColumnStyle` instead.',
);
const menuColumn = getByRole('menuitemcheckbox');
expect(menuColumn.style.background).toBe('red');
errSpy.mockRestore();
});
it('legacy onDropdownVisibleChange', () => {
resetWarned();
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const onDropdownVisibleChange = jest.fn();
const { container } = render(<Cascader onDropdownVisibleChange={onDropdownVisibleChange} />);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Cascader] `onDropdownVisibleChange` is deprecated. Please use `onOpenChange` instead.',
);
toggleOpen(container);
expect(onDropdownVisibleChange).toHaveBeenCalledWith(true);
errSpy.mockRestore();
});
it('should support showCheckedStrategy child', () => {
const multipleOptions = [
{

View file

@ -0,0 +1,78 @@
import React from 'react';
import { Cascader } from 'antd';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const locales = {
cn: {
root: '根元素',
'popup.root': '弹出菜单元素',
},
en: {
root: 'Root element',
'popup.root': 'Popup element',
},
};
const options = [
{
value: 'contributors',
label: 'contributors',
children: [
{
value: 'aojunhao123',
label: 'aojunhao123',
},
{
value: 'thinkasany',
label: 'thinkasany',
},
],
},
];
const Block = (props: any) => {
const divRef = React.useRef<HTMLDivElement>(null);
const [value, setValue] = React.useState<string[]>(['contributors', 'aojunhao123']);
const onChange = (newValue: string[]) => {
setValue(newValue);
};
return (
<div ref={divRef} style={{ marginBottom: 60 }}>
<Cascader
{...props}
open
styles={{
popup: {
root: {
zIndex: 1,
height: 70,
},
},
}}
getPopupContainer={() => divRef.current}
value={value}
onChange={onChange}
options={options}
placement="bottomLeft"
/>
</div>
);
};
const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
componentName="Cascader"
semantics={[
{ name: 'root', desc: locale.root, version: '5.25.0' },
{ name: 'popup.root', desc: locale['popup.root'], version: '5.25.0' },
]}
>
<Block />
</SemanticPreview>
);
};
export default App;

View file

@ -42,7 +42,7 @@ const options: Option[] = [
},
];
const dropdownRender = (menus: React.ReactNode) => (
const popupRender = (menus: React.ReactNode) => (
<div>
{menus}
<Divider style={{ margin: 0 }} />
@ -51,7 +51,7 @@ const dropdownRender = (menus: React.ReactNode) => (
);
const App: React.FC = () => (
<Cascader options={options} dropdownRender={dropdownRender} placeholder="Please select" />
<Cascader options={options} popupRender={popupRender} placeholder="Please select" />
);
export default App;

View file

@ -55,13 +55,16 @@ Common props ref[Common props](/docs/react/common-props)
| autoFocus | If get focus when component mounted | boolean | false | |
| changeOnSelect | Change value on each selection if set to true, see above demo for details | boolean | false | |
| className | The additional css class | string | - | |
| classNames | Semantic DOM class | [Record<SemanticDOM, string>](#semantic-dom) | - | 5.25.0 |
| defaultOpen | Initial visible of cascader popup | boolean | - | |
| defaultValue | Initial selected value | string\[] \| number\[] | \[] | |
| disabled | Whether disabled select | boolean | false | |
| displayRender | The render function of displaying selected options | (label, selectedOptions) => ReactNode | label => label.join(`/`) | `multiple`: 4.18.0 |
| tagRender | Custom render function for tags in `multiple` mode | (label: string, onClose: function, value: string) => ReactNode | - | |
| popupClassName | The additional className of popup overlay | string | - | 4.23.0 |
| dropdownRender | Customize dropdown content | (menus: ReactNode) => ReactNode | - | 4.4.0 |
| ~~popupClassName~~ | The additional className of popup overlay, use `classNames.popup.root` instead | string | - | 4.23.0 |
| ~~dropdownRender~~ | Customize dropdown content, use `popupRender` instead | (menus: ReactNode) => ReactNode | - | 4.4.0 |
| popupRender | Customize dropdown content | (menus: ReactNode) => ReactNode | - | |
| ~~dropdownStyle~~ | The style of dropdown menu, use `styles.popup.root` instead | CSSProperties | - | |
| expandIcon | Customize the current item expand icon | ReactNode | - | 4.4.0 |
| expandTrigger | expand current item when click or hover, one of `click` `hover` | string | `click` | |
| fieldNames | Custom field name for label and value and children | object | { label: `label`, value: `value`, children: `children` } | |
@ -79,18 +82,20 @@ Common props ref[Common props](/docs/react/common-props)
| showSearch | Whether show search input in single mode | boolean \| [Object](#showsearch) | false | |
| size | The input size | `large` \| `middle` \| `small` | - | |
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
| style | The additional style | CSSProperties | - | |
| styles | Semantic DOM style | [Record<SemanticDOM, CSSProperties>](#semantic-dom) | - | 5.25.0 |
| suffixIcon | The custom suffix icon | ReactNode | - | |
| value | The selected value | string\[] \| number\[] | - | |
| variant | Variants of selector | `outlined` \| `borderless` \| `filled` \| `underlined` | `outlined` | 5.13.0 \| `underlined`: 5.24.0 |
| onChange | Callback when finishing cascader select | (value, selectedOptions) => void | - | |
| onDropdownVisibleChange | Callback when popup shown or hidden | (value) => void | - | 4.17.0 |
| ~~onDropdownVisibleChange~~ | Callback when popup shown or hidden, use `onOpenChange` instead | (value) => void | - | 4.17.0 |
| onOpenChange | Callback when popup shown or hidden | (value) => void | - | |
| multiple | Support multiple or not | boolean | - | 4.17.0 |
| removeIcon | The custom remove icon | ReactNode | - | |
| showCheckedStrategy | The way show selected item in box. ** `SHOW_CHILD`: ** just show child treeNode. **`Cascader.SHOW_PARENT`:** just show parent treeNode (when all child treeNode under the parent treeNode are checked) | `Cascader.SHOW_PARENT` \| `Cascader.SHOW_CHILD` | `Cascader.SHOW_PARENT` | 4.20.0 |
| searchValue | Set search value, Need work with `showSearch` | string | - | 4.17.0 |
| onSearch | The callback function triggered when input changed | (search: string) => void | - | 4.17.0 |
| dropdownMenuColumnStyle | The style of the drop-down menu column | CSSProperties | - | |
| ~~dropdownMenuColumnStyle~~ | The style of the drop-down menu column, use `popupMenuColumnStyle` instead | CSSProperties | - | |
| popupMenuColumnStyle | The style of the drop-down menu column | CSSProperties | - | |
| loadingIcon | The appearance of lazy loading (now is useless) | ReactNode | - | |
| optionRender | Customize the rendering dropdown options | (option: Option) => React.ReactNode | - | 5.16.0 |
@ -126,6 +131,10 @@ interface Option {
| blur() | Remove focus | |
| focus() | Get focus | |
## Semantic DOM
<code src="./demo/_semantic.tsx" simplify="true"></code>
## Design Token
<ComponentTokenTable component="Cascader"></ComponentTokenTable>

View file

@ -1,5 +1,5 @@
import * as React from 'react';
import classNames from 'classnames';
import cls from 'classnames';
import type {
BaseOptionType,
DefaultOptionType,
@ -50,6 +50,9 @@ export type FieldNamesType = FieldNames;
export type FilledFieldNamesType = Required<FieldNamesType>;
type SemanticName = 'root';
type PopupSemantic = 'root';
const { SHOW_CHILD, SHOW_PARENT } = RcCascader;
function highlightKeyword(str: string, lowerKeyword: string, prefixCls?: string) {
@ -127,14 +130,32 @@ export interface CascaderProps<
autoClearSearchValue?: boolean;
rootClassName?: string;
/** @deprecated Please use `classNames.popup.root` instead */
popupClassName?: string;
/** @deprecated Please use `popupClassName` instead */
/** @deprecated Please use `classNames.popup.root` instead */
dropdownClassName?: string;
/** @deprecated Please use `styles.popup.root` instead */
dropdownStyle?: React.CSSProperties;
/** @deprecated Please use `popupRender` instead */
dropdownRender?: (menu: React.ReactElement) => React.ReactElement;
popupRender?: (menu: React.ReactElement) => React.ReactElement;
/** @deprecated Please use `popupMenuColumnStyle` instead */
dropdownMenuColumnStyle?: React.CSSProperties;
popupMenuColumnStyle?: React.CSSProperties;
/** @deprecated Please use `onOpenChange` instead */
onDropdownVisibleChange?: (visible: boolean) => void;
onOpenChange?: (visible: boolean) => void;
/**
* @since 5.13.0
* @default "outlined"
*/
variant?: Variant;
classNames?: Partial<Record<SemanticName, string>> & {
popup?: Partial<Record<PopupSemantic, string>>;
};
styles?: Partial<Record<SemanticName, React.CSSProperties>> & {
popup?: Partial<Record<PopupSemantic, React.CSSProperties>>;
};
}
export type CascaderAutoProps<
OptionType extends DefaultOptionType = DefaultOptionType,
@ -173,6 +194,15 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
builtinPlacements,
style,
variant: customVariant,
dropdownRender,
onDropdownVisibleChange,
dropdownMenuColumnStyle,
popupRender,
dropdownStyle,
popupMenuColumnStyle,
onOpenChange,
styles,
classNames,
...rest
} = props;
@ -183,6 +213,8 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
getPopupContainer: getContextPopupContainer,
className: contextClassName,
style: contextStyle,
classNames: contextClassNames,
styles: contextStyles,
} = useComponentConfig('cascader');
const { popupOverflow } = React.useContext(ConfigContext);
@ -200,15 +232,25 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('Cascader');
warning.deprecated(!dropdownClassName, 'dropdownClassName', 'popupClassName');
// v5 deprecated dropdown api
const deprecatedProps = {
dropdownClassName: 'classNames.popup.root',
dropdownStyle: 'styles.popup.root',
dropdownRender: 'popupRender',
dropdownMenuColumnStyle: 'popupMenuColumnStyle',
onDropdownVisibleChange: 'onOpenChange',
bordered: 'variant',
};
Object.entries(deprecatedProps).forEach(([oldProp, newProp]) => {
warning.deprecated(!(oldProp in props), oldProp, newProp);
});
warning(
!('showArrow' in props),
'deprecated',
'`showArrow` is deprecated which will be removed in next major version. It will be a default behavior, you can hide it by setting `suffixIcon` to null.',
);
warning.deprecated(!('bordered' in props), 'bordered', 'variant');
}
// ==================== Prefix =====================
@ -235,19 +277,26 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
);
// =================== Dropdown ====================
const mergedDropdownClassName = classNames(
popupClassName || dropdownClassName,
const mergedPopupClassName = cls(
classNames?.popup?.root || contextClassNames.popup?.root || popupClassName || dropdownClassName,
`${cascaderPrefixCls}-dropdown`,
{
[`${cascaderPrefixCls}-dropdown-rtl`]: mergedDirection === 'rtl',
},
rootClassName,
rootCls,
contextClassNames.root,
classNames?.root,
cascaderRootCls,
hashId,
cssVarCls,
);
const mergedPopupRender = popupRender || dropdownRender;
const mergedPopupMenuColumnStyle = popupMenuColumnStyle || dropdownMenuColumnStyle;
const mergedOnOpenChange = onOpenChange || onDropdownVisibleChange;
const mergedPopupStyle = styles?.popup?.root || contextStyles.popup?.root || dropdownStyle;
// ==================== Search =====================
const mergedShowSearch = React.useMemo(() => {
if (!showSearch) {
@ -304,13 +353,13 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
const mergedAllowClear = allowClear === true ? { clearIcon } : allowClear;
// ============================ zIndex ============================
const [zIndex] = useZIndex('SelectLike', restProps.dropdownStyle?.zIndex as number);
const [zIndex] = useZIndex('SelectLike', mergedPopupStyle?.zIndex as number);
// ==================== Render =====================
const renderNode = (
<RcCascader
prefixCls={prefixCls}
className={classNames(
className={cls(
!customizePrefixCls && cascaderPrefixCls,
{
[`${prefixCls}-lg`]: mergedSize === 'large',
@ -324,13 +373,15 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
contextClassName,
className,
rootClassName,
classNames?.root,
contextClassNames.root,
rootCls,
cascaderRootCls,
hashId,
cssVarCls,
)}
disabled={mergedDisabled}
style={{ ...contextStyle, ...style }}
style={{ ...contextStyles.root, ...styles?.root, ...contextStyle, ...style }}
{...(restProps as any)}
builtinPlacements={mergedBuiltinPlacements(builtinPlacements, popupOverflow)}
direction={mergedDirection}
@ -343,9 +394,12 @@ const Cascader = React.forwardRef<CascaderRef, CascaderProps<any>>((props, ref)
removeIcon={removeIcon}
loadingIcon={loadingIcon}
checkable={checkable}
dropdownClassName={mergedDropdownClassName}
dropdownClassName={mergedPopupClassName}
dropdownPrefixCls={customizePrefixCls || cascaderPrefixCls}
dropdownStyle={{ ...restProps.dropdownStyle, zIndex }}
dropdownStyle={{ ...mergedPopupStyle, zIndex }}
dropdownRender={mergedPopupRender}
dropdownMenuColumnStyle={mergedPopupMenuColumnStyle}
onOpenChange={mergedOnOpenChange}
choiceTransitionName={getTransitionName(rootPrefixCls, '', choiceTransitionName)}
transitionName={getTransitionName(rootPrefixCls, 'slide-up', transitionName)}
getPopupContainer={getPopupContainer || getContextPopupContainer}

View file

@ -56,13 +56,16 @@ demo:
| autoFocus | 自动获取焦点 | boolean | false | |
| changeOnSelect | 单选时生效multiple 下始终都可以选择),点选每级菜单选项值都会发生变化。 | boolean | false | |
| className | 自定义类名 | string | - | |
| classNames | 语义化结构 class | [Record<SemanticDOM, string>](#semantic-dom) | - | 5.25.0 |
| defaultOpen | 是否默认展示浮层 | boolean | - | |
| defaultValue | 默认的选中项 | string\[] \| number\[] | \[] | |
| disabled | 禁用 | boolean | false | |
| displayRender | 选择后展示的渲染函数 | (label, selectedOptions) => ReactNode | label => label.join(`/`) | `multiple`: 4.18.0 |
| tagRender | 自定义 tag 内容 render仅在多选时生效 | ({ label: string, onClose: function, value: string }) => ReactNode | - | |
| popupClassName | 自定义浮层类名 | string | - | 4.23.0 |
| dropdownRender | 自定义下拉框内容 | (menus: ReactNode) => ReactNode | - | 4.4.0 |
| ~~popupClassName~~ | 自定义浮层类名,使用 `classNames.popup.root` 替换 | string | - | 4.23.0 |
| ~~dropdownRender~~ | 自定义下拉框内容,请使用 `popupRender` 替换 | (menus: ReactNode) => ReactNode | - | 4.4.0 |
| popupRender | 自定义下拉框内容 | (menus: ReactNode) => ReactNode | - | |
| ~~dropdownStyle~~ | 下拉菜单的 style 属性,使用 `styles.popup.root` 替换 | CSSProperties | - | |
| expandIcon | 自定义次级菜单展开图标 | ReactNode | - | 4.4.0 |
| expandTrigger | 次级菜单的展开方式,可选 'click' 和 'hover' | string | `click` | |
| fieldNames | 自定义 options 中 label value children 的字段 | object | { label: `label`, value: `value`, children: `children` } | |
@ -80,18 +83,20 @@ demo:
| showSearch | 在选择框中显示搜索框 | boolean \| [Object](#showsearch) | false | |
| size | 输入框大小 | `large` \| `middle` \| `small` | - | |
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
| style | 自定义样式 | CSSProperties | - | |
| styles | 语义化结构 style | [Record<SemanticDOM, CSSProperties>](#semantic-dom) | - | 5.25.0 |
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
| value | 指定选中项 | string\[] \| number\[] | - | |
| variant | 形态变体 | `outlined` \| `borderless` \| `filled` \| `underlined` | `outlined` | 5.13.0 \| `underlined`: 5.24.0 |
| onChange | 选择完成后的回调 | (value, selectedOptions) => void | - | |
| onDropdownVisibleChange | 显示/隐藏浮层的回调 | (value) => void | - | 4.17.0 |
| ~~onDropdownVisibleChange~~ | 显示/隐藏浮层的回调,请使用 `onOpenChange` 替换 | (value) => void | - | 4.17.0 |
| onOpenChange | 显示/隐藏浮层的回调 | (value) => void | - | |
| multiple | 支持多选节点 | boolean | - | 4.17.0 |
| showCheckedStrategy | 定义选中项回填的方式。`Cascader.SHOW_CHILD`: 只显示选中的子节点。`Cascader.SHOW_PARENT`: 只显示父节点(当父节点下所有子节点都选中时)。 | `Cascader.SHOW_PARENT` \| `Cascader.SHOW_CHILD` | `Cascader.SHOW_PARENT` | 4.20.0 |
| removeIcon | 自定义的多选框清除图标 | ReactNode | - | |
| searchValue | 设置搜索的值,需要与 `showSearch` 配合使用 | string | - | 4.17.0 |
| onSearch | 监听搜索,返回输入的值 | (search: string) => void | - | 4.17.0 |
| dropdownMenuColumnStyle | 下拉菜单列的样式 | CSSProperties | - | |
| ~~dropdownMenuColumnStyle~~ | 下拉菜单列的样式,请使用 `popupMenuColumnStyle` 替换 | CSSProperties | - | |
| popupMenuColumnStyle | 下拉菜单列的样式 | CSSProperties | - | |
| optionRender | 自定义渲染下拉选项 | (option: Option) => React.ReactNode | - | 5.16.0 |
### showSearch
@ -129,6 +134,10 @@ interface Option {
> 注意,如果需要获得中国省市区数据,可以参考 [china-division](https://gist.github.com/afc163/7582f35654fd03d5be7009444345ea17)。
## Semantic DOM
<code src="./demo/_semantic.tsx" simplify="true"></code>
## 主题变量Design Token
<ComponentTokenTable component="Cascader"></ComponentTokenTable>

View file

@ -14,6 +14,7 @@ export interface CheckboxOptionType<T = any> {
label: React.ReactNode;
value: T;
style?: React.CSSProperties;
className?: string; // 👈 5.25.0+
disabled?: boolean;
title?: string;
id?: string;
@ -125,7 +126,7 @@ const CheckboxGroup = React.forwardRef(
value={option.value}
checked={value.includes(option.value)}
onChange={option.onChange}
className={`${groupPrefixCls}-item`}
className={classNames(`${groupPrefixCls}-item`, option.className)}
style={option.style}
title={option.title}
id={option.id}
@ -160,6 +161,7 @@ const CheckboxGroup = React.forwardRef(
rootCls,
hashId,
);
return wrapCSSVar(
<div className={classString} style={style} {...domProps} ref={ref}>
<GroupContext.Provider value={memoizedContext}>{childrenNode}</GroupContext.Provider>

View file

@ -691,7 +691,7 @@ Array [
class="ant-checkbox-group"
>
<label
class="ant-checkbox-wrapper ant-checkbox-group-item"
class="ant-checkbox-wrapper ant-checkbox-group-item label-1"
>
<span
class="ant-checkbox ant-wave-target"
@ -712,7 +712,7 @@ Array [
</span>
</label>
<label
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-group-item"
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-group-item label-2"
>
<span
class="ant-checkbox ant-wave-target ant-checkbox-checked"
@ -734,7 +734,7 @@ Array [
</span>
</label>
<label
class="ant-checkbox-wrapper ant-checkbox-group-item"
class="ant-checkbox-wrapper ant-checkbox-group-item label-3"
>
<span
class="ant-checkbox ant-wave-target"
@ -761,7 +761,7 @@ Array [
class="ant-checkbox-group"
>
<label
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-disabled ant-checkbox-group-item"
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-disabled ant-checkbox-group-item label-1"
>
<span
class="ant-checkbox ant-wave-target ant-checkbox-checked ant-checkbox-disabled"
@ -784,7 +784,7 @@ Array [
</span>
</label>
<label
class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item"
class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item label-2"
>
<span
class="ant-checkbox ant-wave-target ant-checkbox-disabled"
@ -806,7 +806,7 @@ Array [
</span>
</label>
<label
class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item"
class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item label-3"
>
<span
class="ant-checkbox ant-wave-target ant-checkbox-disabled"

View file

@ -649,7 +649,7 @@ Array [
class="ant-checkbox-group"
>
<label
class="ant-checkbox-wrapper ant-checkbox-group-item"
class="ant-checkbox-wrapper ant-checkbox-group-item label-1"
>
<span
class="ant-checkbox ant-wave-target"
@ -670,7 +670,7 @@ Array [
</span>
</label>
<label
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-group-item"
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-group-item label-2"
>
<span
class="ant-checkbox ant-wave-target ant-checkbox-checked"
@ -692,7 +692,7 @@ Array [
</span>
</label>
<label
class="ant-checkbox-wrapper ant-checkbox-group-item"
class="ant-checkbox-wrapper ant-checkbox-group-item label-3"
>
<span
class="ant-checkbox ant-wave-target"
@ -719,7 +719,7 @@ Array [
class="ant-checkbox-group"
>
<label
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-disabled ant-checkbox-group-item"
class="ant-checkbox-wrapper ant-checkbox-wrapper-checked ant-checkbox-wrapper-disabled ant-checkbox-group-item label-1"
>
<span
class="ant-checkbox ant-wave-target ant-checkbox-checked ant-checkbox-disabled"
@ -742,7 +742,7 @@ Array [
</span>
</label>
<label
class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item"
class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item label-2"
>
<span
class="ant-checkbox ant-wave-target ant-checkbox-disabled"
@ -764,7 +764,7 @@ Array [
</span>
</label>
<label
class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item"
class="ant-checkbox-wrapper ant-checkbox-wrapper-disabled ant-checkbox-group-item label-3"
>
<span
class="ant-checkbox ant-wave-target ant-checkbox-disabled"

View file

@ -1,6 +1,6 @@
import React from 'react';
import { Checkbox } from 'antd';
import type { GetProp } from 'antd';
import type { CheckboxOptionType, GetProp } from 'antd';
const onChange: GetProp<typeof Checkbox.Group, 'onChange'> = (checkedValues) => {
console.log('checked = ', checkedValues);
@ -8,16 +8,16 @@ const onChange: GetProp<typeof Checkbox.Group, 'onChange'> = (checkedValues) =>
const plainOptions = ['Apple', 'Pear', 'Orange'];
const options = [
{ label: 'Apple', value: 'Apple' },
{ label: 'Pear', value: 'Pear' },
{ label: 'Orange', value: 'Orange' },
const options: CheckboxOptionType<string>[] = [
{ label: 'Apple', value: 'Apple', className: 'label-1' },
{ label: 'Pear', value: 'Pear', className: 'label-2' },
{ label: 'Orange', value: 'Orange', className: 'label-3' },
];
const optionsWithDisabled = [
{ label: 'Apple', value: 'Apple' },
{ label: 'Pear', value: 'Pear' },
{ label: 'Orange', value: 'Orange', disabled: false },
const optionsWithDisabled: CheckboxOptionType<string>[] = [
{ label: 'Apple', value: 'Apple', className: 'label-1' },
{ label: 'Pear', value: 'Pear', className: 'label-2' },
{ label: 'Orange', value: 'Orange', className: 'label-3', disabled: false },
];
const App: React.FC = () => (

View file

@ -44,7 +44,7 @@ Common props ref[Common props](/docs/react/common-props)
| onBlur | Called when leaving the component | function() | - | |
| onFocus | Called when entering the component | function() | - | |
#### Checkbox Group
#### Checkbox.Group
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
@ -53,6 +53,9 @@ Common props ref[Common props](/docs/react/common-props)
| name | The `name` property of all `input[type="checkbox"]` children | string | - | |
| options | Specifies options | string\[] \| number\[] \| Option\[] | \[] | |
| value | Used for setting the currently selected value | (string \| number \| boolean)\[] | \[] | |
| title | title of the option | `string` | - | |
| className | className of the option | `string` | - | 5.25.0 |
| style | styles of the option | `React.CSSProperties` | - | |
| onChange | The callback function that is triggered when the state changes | (checkedValue: T[]) => void | - | |
##### Option

View file

@ -45,7 +45,7 @@ demo:
| onBlur | 失去焦点时的回调 | function() | - | |
| onFocus | 获得焦点时的回调 | function() | - | |
#### Checkbox Group
#### Checkbox.Group
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
@ -54,6 +54,9 @@ demo:
| name | CheckboxGroup 下所有 `input[type="checkbox"]``name` 属性 | string | - | |
| options | 指定可选项 | string\[] \| number\[] \| Option\[] | \[] | |
| value | 指定选中的选项 | (string \| number \| boolean)\[] | \[] | |
| title | 选项的 title | `string` | - | |
| className | 选项的类名 | `string` | - | 5.25.0 |
| style | 选项的样式 | `React.CSSProperties` | - | |
| onChange | 变化时的回调函数 | (checkedValue: T[]) => void | - | |
##### Option

View file

@ -2,6 +2,7 @@ import React from 'react';
import { resetWarned } from '../../_util/warning';
import { act, fireEvent, render, waitFakeTimer } from '../../../tests/utils';
import ConfigProvider from '../../config-provider';
describe('Collapse', () => {
const Collapse = require('..').default;
@ -275,6 +276,52 @@ describe('Collapse', () => {
);
});
it('should support borderlessContentBg component token', () => {
const { container } = render(
<ConfigProvider
theme={{
components: {
Collapse: {
borderlessContentBg: 'red',
},
},
}}
>
<Collapse bordered={false} defaultActiveKey={['1']}>
<Collapse.Panel header="This is panel header 1" key="1">
content
</Collapse.Panel>
</Collapse>
</ConfigProvider>,
);
expect(container.querySelector('.ant-collapse-content')).toHaveStyle({
backgroundColor: 'red',
});
});
it('should support borderlessContentPadding component token', () => {
const { container } = render(
<ConfigProvider
theme={{
components: {
Collapse: {
borderlessContentPadding: '10px',
},
},
}}
>
<Collapse bordered={false} defaultActiveKey={['1']}>
<Collapse.Panel header="This is panel header 1" key="1">
content
</Collapse.Panel>
</Collapse>
</ConfigProvider>,
);
expect(container.querySelector('.ant-collapse-content-box')).toHaveStyle({
padding: '10px',
});
});
it('should support styles and classNames', () => {
const { container } = render(
<Collapse

View file

@ -29,6 +29,16 @@ export interface ComponentToken {
* @descEN Background of content
*/
contentBg: string;
/**
* @desc
* @descEN Padding of content in borderless style
*/
borderlessContentPadding: CSSProperties['padding'];
/**
* @desc
* @descEN Background of content in borderless style
*/
borderlessContentBg: string;
}
type CollapseToken = FullToken<'Collapse'> & {
@ -271,13 +281,8 @@ const genArrowStyle: GenerateStyle<CollapseToken> = (token) => {
};
const genBorderlessStyle: GenerateStyle<CollapseToken> = (token) => {
const {
componentCls,
headerBg,
paddingXXS,
colorBorder,
} = token;
const { componentCls, headerBg, borderlessContentPadding, borderlessContentBg, colorBorder } =
token;
return {
[`${componentCls}-borderless`]: {
@ -300,12 +305,12 @@ const genBorderlessStyle: GenerateStyle<CollapseToken> = (token) => {
},
[`> ${componentCls}-item > ${componentCls}-content`]: {
backgroundColor: 'transparent',
backgroundColor: borderlessContentBg,
borderTop: 0,
},
[`> ${componentCls}-item > ${componentCls}-content > ${componentCls}-content-box`]: {
paddingTop: paddingXXS,
padding: borderlessContentPadding,
},
},
};
@ -337,6 +342,8 @@ export const prepareComponentToken: GetDefaultToken<'Collapse'> = (token) => ({
headerBg: token.colorFillAlter,
contentPadding: `${token.padding}px 16px`, // Fixed Value
contentBg: token.colorBgContainer,
borderlessContentPadding: `${token.paddingXXS}px 16px ${token.padding}px`,
borderlessContentBg: 'transparent',
});
export default genStyleHooks(

View file

@ -60,6 +60,7 @@ const ColorPicker: CompoundedComponent = (props) => {
getPopupContainer,
autoAdjustOverflow = true,
destroyTooltipOnHide,
destroyOnClose,
disabledFormat,
...rest
} = props;
@ -211,7 +212,7 @@ const ColorPicker: CompoundedComponent = (props) => {
rootClassName,
getPopupContainer,
autoAdjustOverflow,
destroyTooltipOnHide,
destroyOnClose: destroyOnClose ?? !!destroyTooltipOnHide,
};
const mergedStyle: React.CSSProperties = { ...colorPicker?.style, ...style };

View file

@ -50,7 +50,8 @@ Common props ref[Common props](/docs/react/common-props)
| disabled | Disable ColorPicker | boolean | - | |
| disabledAlpha | Disable Alpha | boolean | - | 5.8.0 |
| disabledFormat | Disable format of color | boolean | - | |
| destroyTooltipOnHide | Whether destroy popover when hidden | `boolean` | false | 5.7.0 |
| ~~destroyTooltipOnHide~~ | Whether destroy dom when close | `boolean` | false | 5.7.0 |
| destroyOnClose | Whether destroy dom when close | `boolean` | false | 5.25.0 |
| format | Format of color | `rgb` \| `hex` \| `hsb` | - | |
| mode | Configure single or gradient color | `'single' \| 'gradient' \| ('single' \| 'gradient')[]` | `single` | 5.20.0 |
| open | Whether to show popup | boolean | - | |

View file

@ -51,7 +51,8 @@ group:
| disabled | 禁用颜色选择器 | boolean | - | |
| disabledAlpha | 禁用透明度 | boolean | - | 5.8.0 |
| disabledFormat | 禁用选择颜色格式 | boolean | - | |
| destroyTooltipOnHide | 关闭后是否销毁弹窗 | `boolean` | false | 5.7.0 |
| ~~destroyTooltipOnHide~~ | 关闭后是否销毁弹窗 | `boolean` | false | 5.7.0 |
| destroyOnClose | 关闭后是否销毁弹窗 | `boolean` | false | 5.25.0 |
| format | 颜色格式 | `rgb` \| `hex` \| `hsb` | - | |
| mode | 选择器模式,用于配置单色与渐变 | `'single' \| 'gradient' \| ('single' \| 'gradient')[]` | `single` | 5.20.0 |
| open | 是否显示弹出窗口 | boolean | - | |

View file

@ -94,4 +94,7 @@ export type ColorPickerProps = Omit<
onClear?: () => void;
onChangeComplete?: (value: AggregationColor) => void;
disabledFormat?: boolean;
} & Pick<PopoverProps, 'getPopupContainer' | 'autoAdjustOverflow' | 'destroyTooltipOnHide'>;
} & Pick<
PopoverProps,
'getPopupContainer' | 'autoAdjustOverflow' | 'destroyTooltipOnHide' | 'destroyOnClose'
>;

View file

@ -14405,14 +14405,14 @@ exports[`ConfigProvider components Divider configProvider componentSize large 1`
exports[`ConfigProvider components Divider configProvider componentSize middle 1`] = `
<div
class="config-divider config-divider-horizontal"
class="config-divider config-divider-horizontal config-divider-md"
role="separator"
/>
`;
exports[`ConfigProvider components Divider configProvider componentSize small 1`] = `
<div
class="config-divider config-divider-horizontal"
class="config-divider config-divider-horizontal config-divider-sm"
role="separator"
/>
`;
@ -18568,6 +18568,7 @@ exports[`ConfigProvider components Modal configProvider 1`] = `
type="button"
>
<span
aria-label="Close"
class="config-modal-close-x"
>
<span
@ -18660,6 +18661,7 @@ exports[`ConfigProvider components Modal configProvider componentDisabled 1`] =
type="button"
>
<span
aria-label="Close"
class="config-modal-close-x"
>
<span
@ -18752,6 +18754,7 @@ exports[`ConfigProvider components Modal configProvider componentSize large 1`]
type="button"
>
<span
aria-label="Close"
class="config-modal-close-x"
>
<span
@ -18844,6 +18847,7 @@ exports[`ConfigProvider components Modal configProvider componentSize middle 1`]
type="button"
>
<span
aria-label="Close"
class="config-modal-close-x"
>
<span
@ -18936,6 +18940,7 @@ exports[`ConfigProvider components Modal configProvider componentSize small 1`]
type="button"
>
<span
aria-label="Close"
class="config-modal-close-x"
>
<span
@ -19028,6 +19033,7 @@ exports[`ConfigProvider components Modal normal 1`] = `
type="button"
>
<span
aria-label="Close"
class="ant-modal-close-x"
>
<span
@ -19120,6 +19126,7 @@ exports[`ConfigProvider components Modal prefixCls 1`] = `
type="button"
>
<span
aria-label="Close"
class="prefix-Modal-close-x"
>
<span

View file

@ -161,7 +161,7 @@ export type TextAreaConfig = ComponentStyleConfig &
Pick<TextAreaProps, 'autoComplete' | 'classNames' | 'styles' | 'allowClear' | 'variant'>;
export type ButtonConfig = ComponentStyleConfig &
Pick<ButtonProps, 'classNames' | 'styles' | 'autoInsertSpace'>;
Pick<ButtonProps, 'classNames' | 'styles' | 'autoInsertSpace' | 'variant' | 'color'>;
export type NotificationConfig = ComponentStyleConfig & Pick<ArgsProps, 'closeIcon'>;
@ -184,7 +184,8 @@ export type FloatButtonGroupConfig = Pick<FloatButtonGroupProps, 'closeIcon'>;
export type PaginationConfig = ComponentStyleConfig & Pick<PaginationProps, 'showSizeChanger'>;
export type SelectConfig = ComponentStyleConfig & Pick<SelectProps, 'showSearch' | 'variant'>;
export type SelectConfig = ComponentStyleConfig &
Pick<SelectProps, 'showSearch' | 'variant' | 'classNames' | 'styles'>;
export type SpaceConfig = ComponentStyleConfig & Pick<SpaceProps, 'size' | 'classNames' | 'styles'>;
@ -203,9 +204,11 @@ export type SpinConfig = ComponentStyleConfig & Pick<SpinProps, 'indicator'>;
export type InputNumberConfig = ComponentStyleConfig & Pick<InputNumberProps, 'variant'>;
export type CascaderConfig = ComponentStyleConfig & Pick<CascaderProps, 'variant'>;
export type CascaderConfig = ComponentStyleConfig &
Pick<CascaderProps, 'variant' | 'styles' | 'classNames'>;
export type TreeSelectConfig = ComponentStyleConfig & Pick<TreeSelectProps, 'variant'>;
export type TreeSelectConfig = ComponentStyleConfig &
Pick<TreeSelectProps, 'variant' | 'styles' | 'classNames'>;
export type DatePickerConfig = ComponentStyleConfig & Pick<DatePickerProps, 'variant'>;

View file

@ -113,11 +113,11 @@ const {
| avatar | Set Avatar common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| badge | Set Badge common props | { className?: string, style?: React.CSSProperties, classNames?: [BadgeProps\["classNames"\]](/components/badge#api), styles?: [BadgeProps\["styles"\]](/components/badge#api) } | - | 5.7.0 |
| breadcrumb | Set Breadcrumb common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| button | Set Button common props | { className?: string, style?: React.CSSProperties, classNames?: [ButtonProps\["classNames"\]](/components/button#api), styles?: [ButtonProps\["styles"\]](/components/button#api), autoInsertSpace?: boolean } | - | 5.6.0, `autoInsertSpace`: 5.17.0 |
| button | Set Button common props | { className?: string, style?: React.CSSProperties, classNames?: [ButtonProps\["classNames"\]](/components/button#api), styles?: [ButtonProps\["styles"\]](/components/button#api), autoInsertSpace?: boolean, variant?: ButtonVariantType, color?: ButtonColorType } | - | 5.6.0, `autoInsertSpace`: 5.17.0, `variant` and `color`: 5.25.0 |
| card | Set Card common props | { className?: string, style?: React.CSSProperties, classNames?: [CardProps\["classNames"\]](/components/card#api), styles?: [CardProps\["styles"\]](/components/card#api) } | - | 5.7.0, `classNames` and `styles`: 5.14.0 |
| calendar | Set Calendar common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| carousel | Set Carousel common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| cascader | Set Cascader common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| cascader | Set Cascader common props | { className?: string, style?: React.CSSProperties, classNames?: [CascaderProps\["classNames"\]](/components/cascader#semantic-dom), styles?: [CascaderProps\["styles"\]](/components/cascader#semantic-dom) } | - | 5.7.0, `classNames` and `styles`: 5.25.0 |
| checkbox | Set Checkbox common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| collapse | Set Collapse common props | { className?: string, style?: React.CSSProperties, expandIcon?: (props) => ReactNode } | - | 5.7.0, `expandIcon`: 5.15.0 |
| colorPicker | Set ColorPicker common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
@ -148,7 +148,7 @@ const {
| result | Set Result common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| skeleton | Set Skeleton common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| segmented | Set Segmented common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| select | Set Select common props | { className?: string, showSearch?: boolean, style?: React.CSSProperties } | - | 5.7.0 |
| select | Set Select common props | { className?: string, showSearch?: boolean, style?: React.CSSProperties, classNames?: [SelectProps\["classNames"\]](/components/select#api), styles?: [SelectProps\["styles"\]](/components/select#api) } | - | 5.7.0, `classNames` and `styles`: 5.25.0 |
| slider | Set Slider common props | { className?: string, style?: React.CSSProperties, classNames?: [SliderProps\["classNames"\]](/components/slider#api), styles?: [SliderProps\["styles"\]](/components/slider#api) } | - | 5.7.0, `classNames` and `styles`: 5.23.0 |
| switch | Set Switch common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| space | Set Space common props, ref [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number`, className?: string, style?: React.CSSProperties, classNames?: [SpaceProps\["classNames"\]](/components/space#api), styles?: [SpaceProps\["styles"\]](/components/space#api) } | - | 5.6.0 |
@ -167,6 +167,7 @@ const {
| popconfirm | Set Popconfirm common props | { className?: string, style?: React.CSSProperties, classNames?:[Popconfirm\["classNames"\]](/components/popconfirm#api), styles?: [Popconfirm\["styles"\]](/components/popconfirm#api) } | - | 5.23.0 |
| transfer | Set Transfer common props | { className?: string, style?: React.CSSProperties, selectionsIcon?: React.ReactNode } | - | 5.7.0, `selectionsIcon`: 5.14.0 |
| tree | Set Tree common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| treeSelect | Set TreeSelect common props | { classNames?:[TreeSelect\["classNames"\]](/components/tree-select#api), styles?: [TreeSelect\["styles"\]](/components/tree-select#api) } | - | 5.25.0 |
| typography | Set Typography common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| upload | Set Upload common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| wave | Config wave effect | { disabled?: boolean, showEffect?: (node: HTMLElement, info: { className, token, component }) => void } | - | 5.8.0 |

View file

@ -115,11 +115,11 @@ const {
| avatar | 设置 Avatar 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| badge | 设置 Badge 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [BadgeProps\["classNames"\]](/components/badge-cn#api), styles?: [BadgeProps\["styles"\]](/components/badge-cn#api) } | - | 5.7.0 |
| breadcrumb | 设置 Breadcrumb 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| button | 设置 Button 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [ButtonProps\["classNames"\]](/components/button-cn#api), styles?: [ButtonProps\["styles"\]](/components/button-cn#api), autoInsertSpace?: boolean } | - | 5.6.0, `autoInsertSpace`: 5.17.0 |
| button | 设置 Button 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [ButtonProps\["classNames"\]](/components/button-cn#api), styles?: [ButtonProps\["styles"\]](/components/button-cn#api), autoInsertSpace?: boolean, variant?: ButtonVariantType, color?: ButtonColorType } | - | 5.6.0, `autoInsertSpace`: 5.17.0, `variant``color`: 5.25.0 |
| calendar | 设置 Calendar 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| card | 设置 Card 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [CardProps\["classNames"\]](/components/card-cn#api), styles?: [CardProps\["styles"\]](/components/card-cn#api) } | - | 5.7.0, `classNames``styles`: 5.14.0 |
| carousel | 设置 Carousel 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| cascader | 设置 Cascader 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| cascader | 设置 Cascader 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [CascaderProps\["classNames"\]](/components/cascader-cn#semantic-dom), styles?: [CascaderProps\["styles"\]](/components/cascader-cn#semantic-dom) } | - | 5.7.0, `classNames``styles`: 5.25.0 |
| checkbox | 设置 Checkbox 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| collapse | 设置 Collapse 组件的通用属性 | { className?: string, style?: React.CSSProperties, expandIcon?: (props) => ReactNode } | - | 5.7.0, `expandIcon`: 5.15.0 |
| colorPicker | 设置 ColorPicker 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
@ -150,7 +150,7 @@ const {
| result | 设置 Result 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| skeleton | 设置 Skeleton 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| segmented | 设置 Segmented 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| select | 设置 Select 组件的通用属性 | { className?: string, showSearch?: boolean, style?: React.CSSProperties } | - | 5.7.0 |
| select | 设置 Select 组件的通用属性 | { className?: string, showSearch?: boolean, style?: React.CSSProperties, classNames?: [SelectProps\["classNames"\]](/components/select-cn#api), styles?: [SelectProps\["styles"\]](/components/select-cn#api) } | - | 5.7.0, `classNames``styles`: 5.25.0 |
| slider | 设置 Slider 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [SliderProps\["classNames"\]](/components/slider-cn#api), styles?: [SliderProps\["styles"\]](/components/slider-cn#api) } | - | 5.7.0, `classNames``styles`: 5.23.0 |
| switch | 设置 Switch 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| space | 设置 Space 的通用属性,参考 [Space](/components/space-cn) | { size: `small` \| `middle` \| `large` \| `number`, className?: string, style?: React.CSSProperties, classNames?: [SpaceProps\["classNames"\]](/components/space-cn#api), styles?: [SpaceProps\["styles"\]](/components/space-cn#api) } | - | 5.6.0 |
@ -169,6 +169,7 @@ const {
| popconfirm | 设置 Popconfirm 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?:[Popconfirm\["classNames"\]](/components/popconfirm-cn#api), styles?: [Popconfirm\["styles"\]](/components/popconfirm-cn#api) } | - | 5.23.0 |
| transfer | 设置 Transfer 组件的通用属性 | { className?: string, style?: React.CSSProperties, selectionsIcon?: React.ReactNode } | - | 5.7.0, `selectionsIcon`: 5.14.0 |
| tree | 设置 Tree 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| treeSelect | 设置 TreeSelect 组件的通用属性 | { classNames?:[TreeSelect\["classNames"\]](/components/tree-select-cn#api), styles?: [TreeSelect\["styles"\]](/components/tree-select-cn#api) } | - | 5.25.0 |
| typography | 设置 Typography 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| upload | 设置 Upload 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| wave | 设置水波纹特效 | { disabled?: boolean, showEffect?: (node: HTMLElement, info: { className, token, component }) => void } | - | 5.8.0 |

View file

@ -203,6 +203,37 @@ Array [
exports[`renders components/divider/demo/plain.tsx extend context correctly 2`] = `[]`;
exports[`renders components/divider/demo/size.tsx extend context correctly 1`] = `
Array [
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider ant-divider-horizontal ant-divider-sm"
role="separator"
/>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider ant-divider-horizontal ant-divider-md"
role="separator"
/>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider ant-divider-horizontal"
role="separator"
/>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
]
`;
exports[`renders components/divider/demo/size.tsx extend context correctly 2`] = `[]`;
exports[`renders components/divider/demo/variant.tsx extend context correctly 1`] = `
Array [
<p>

View file

@ -195,6 +195,35 @@ Array [
]
`;
exports[`renders components/divider/demo/size.tsx correctly 1`] = `
Array [
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider ant-divider-horizontal ant-divider-sm"
role="separator"
/>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider ant-divider-horizontal ant-divider-md"
role="separator"
/>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
<div
class="ant-divider ant-divider-horizontal"
role="separator"
/>,
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>,
]
`;
exports[`renders components/divider/demo/variant.tsx correctly 1`] = `
Array [
<p>

View file

@ -1,4 +1,5 @@
import * as React from 'react';
import { ConfigProvider } from 'antd';
import Divider from '..';
import mountTest from '../../../tests/shared/mountTest';
@ -40,4 +41,28 @@ describe('Divider', () => {
borderStyle: 'dotted',
});
});
it('should apply the componentSize of ConfigProvider', () => {
const { container, rerender } = render(
<ConfigProvider componentSize="middle">
<Divider />
</ConfigProvider>,
);
expect(container.querySelector<HTMLSpanElement>('.ant-divider-md')).toBeTruthy();
rerender(
<ConfigProvider componentSize="small">
<Divider />
</ConfigProvider>,
);
expect(container.querySelector<HTMLSpanElement>('.ant-divider-sm')).toBeTruthy();
});
it('support vertical size', () => {
const { container, rerender } = render(<Divider type="vertical" size="middle" />);
expect(container.querySelector<HTMLSpanElement>('.ant-divider-md')).toBeTruthy();
rerender(<Divider type="vertical" size="small" />);
expect(container.querySelector<HTMLSpanElement>('.ant-divider-sm')).toBeTruthy();
});
});

View file

@ -0,0 +1,7 @@
## zh-CN
间距的大小。
## en-US
The size of the spacing.

View file

@ -0,0 +1,28 @@
import React from 'react';
import { Divider } from 'antd';
const App: React.FC = () => (
<>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider size="small" />
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider size="middle" />
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
<Divider size="large" />
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti licere mihi ista
probare, quae sunt a te dicta? Refert tamen, quo modo.
</p>
</>
);
export default App;

View file

@ -21,6 +21,7 @@ group:
<!-- prettier-ignore -->
<code src="./demo/horizontal.tsx">Horizontal</code>
<code src="./demo/with-text.tsx">Divider with title</code>
<code src="./demo/size.tsx" version="5.25.0">Set the spacing size of the divider</code>
<code src="./demo/plain.tsx">Text without heading style</code>
<code src="./demo/vertical.tsx">Vertical</code>
<code src="./demo/customize-style.tsx" debug>Style Customization</code>
@ -41,6 +42,7 @@ Common props ref[Common props](/docs/react/common-props)
| orientationMargin | The margin-left/right between the title and its closest border, while the `orientation` should not be `center`, If a numeric value of type `string` is provided without a unit, it is assumed to be in pixels (px) by default. | string \| number | - | |
| plain | Divider text show as plain style | boolean | true | 4.2.0 |
| style | The style object of container | CSSProperties | - | |
| size | The size of divider. Only valid for horizontal layout | `small` \| `middle` \| `large` | - | 5.25.0 |
| type | The direction type of divider | `horizontal` \| `vertical` | `horizontal` | |
## Design Token

View file

@ -3,6 +3,8 @@ import classNames from 'classnames';
import { devUseWarning } from '../_util/warning';
import { useComponentConfig } from '../config-provider/context';
import useSize from '../config-provider/hooks/useSize';
import { SizeType } from '../config-provider/SizeContext';
import useStyle from './style';
export interface DividerProps {
@ -28,9 +30,12 @@ export interface DividerProps {
*/
variant?: 'dashed' | 'dotted' | 'solid';
style?: React.CSSProperties;
size?: SizeType;
plain?: boolean;
}
const sizeClassNameMap: Record<string, string> = { small: 'sm', middle: 'md' };
const Divider: React.FC<DividerProps> = (props) => {
const {
getPrefixCls,
@ -51,12 +56,16 @@ const Divider: React.FC<DividerProps> = (props) => {
variant = 'solid',
plain,
style,
size: customSize,
...restProps
} = props;
const prefixCls = getPrefixCls('divider', customizePrefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls);
const sizeFullName = useSize(customSize);
const sizeCls = sizeClassNameMap[sizeFullName];
const hasChildren = !!children;
const mergedOrientation = React.useMemo<'start' | 'end' | 'center'>(() => {
@ -88,6 +97,7 @@ const Divider: React.FC<DividerProps> = (props) => {
[`${prefixCls}-rtl`]: direction === 'rtl',
[`${prefixCls}-no-default-orientation-margin-start`]: hasMarginStart,
[`${prefixCls}-no-default-orientation-margin-end`]: hasMarginEnd,
[`${prefixCls}-${sizeCls}`]: !!sizeCls,
},
className,
rootClassName,

View file

@ -22,6 +22,7 @@ group:
<!-- prettier-ignore -->
<code src="./demo/horizontal.tsx">水平分割线</code>
<code src="./demo/with-text.tsx">带文字的分割线</code>
<code src="./demo/size.tsx" version="5.25.0">设置分割线的间距大小</code>
<code src="./demo/plain.tsx">分割文字使用正文样式</code>
<code src="./demo/vertical.tsx">垂直分割线</code>
<code src="./demo/customize-style.tsx" debug>样式自定义</code>
@ -42,6 +43,7 @@ group:
| orientationMargin | 标题和最近 left/right 边框之间的距离,去除了分割线,同时 `orientation` 不能为 `center`。如果传入 `string` 类型的数字且不带单位,默认单位是 px | string \| number | - | |
| plain | 文字是否显示为普通正文样式 | boolean | false | 4.2.0 |
| style | 分割线样式对象 | CSSProperties | - | |
| size | 间距大小,仅对水平布局有效 | `small` \| `middle` \| `large` | - | 5.25.0 |
| type | 水平还是垂直类型 | `horizontal` \| `vertical` | `horizontal` | |
## 主题变量Design Token

View file

@ -40,13 +40,28 @@ interface DividerToken extends FullToken<'Divider'> {
* @descEN Horizontal margin of divider with text
*/
dividerHorizontalWithTextGutterMargin: number | string;
/**
* @desc 线
* @descEN Horizontal margin of divider
*/
dividerHorizontalGutterMargin: number | string;
}
// ============================== Size ================================
const genSizeDividerStyle: GenerateStyle<DividerToken> = (token): CSSObject => {
const { componentCls } = token;
return {
[componentCls]: {
'&-horizontal': {
[`&${componentCls}`]: {
'&-sm': {
marginBlock: token.marginXS,
},
'&-md': {
marginBlock: token.margin,
},
},
},
},
};
};
// ============================== Shared ==============================
const genSharedDividerStyle: GenerateStyle<DividerToken> = (token): CSSObject => {
const {
@ -82,7 +97,7 @@ const genSharedDividerStyle: GenerateStyle<DividerToken> = (token): CSSObject =>
clear: 'both',
width: '100%',
minWidth: '100%', // Fix https://github.com/ant-design/ant-design/issues/10914
margin: `${unit(token.dividerHorizontalGutterMargin)} 0`,
margin: `${unit(token.marginLG)} 0`,
},
[`&-horizontal${componentCls}-with-text`]: {
@ -223,10 +238,9 @@ export default genStyleHooks(
(token) => {
const dividerToken = mergeToken<DividerToken>(token, {
dividerHorizontalWithTextGutterMargin: token.margin,
dividerHorizontalGutterMargin: token.marginLG,
sizePaddingEdgeHorizontal: 0,
});
return [genSharedDividerStyle(dividerToken)];
return [genSharedDividerStyle(dividerToken), genSizeDividerStyle(dividerToken)];
},
prepareComponentToken,
{

View file

@ -4,8 +4,8 @@ import type { DrawerProps as RCDrawerProps } from 'rc-drawer';
import useClosable, { pickClosable } from '../_util/hooks/useClosable';
import type { ClosableType } from '../_util/hooks/useClosable';
import Skeleton from '../skeleton';
import { useComponentConfig } from '../config-provider/context';
import Skeleton from '../skeleton';
export interface DrawerClassNames extends NonNullable<RCDrawerProps['classNames']> {
header?: string;
@ -74,7 +74,7 @@ const DrawerPanel: React.FC<DrawerPanelProps> = (props) => {
const customCloseIconRender = React.useCallback(
(icon: React.ReactNode) => (
<button type="button" onClick={onClose} aria-label="Close" className={`${prefixCls}-close`}>
<button type="button" onClick={onClose} className={`${prefixCls}-close`}>
{icon}
</button>
),

View file

@ -39,7 +39,7 @@ Array [
class="ant-drawer-header-title"
>
<button
aria-label="Close"
aria-label="Close Button"
class="ant-drawer-close"
type="button"
>

View file

@ -17,7 +17,12 @@ const App: React.FC = () => {
<Button type="primary" onClick={showDrawer}>
Open
</Button>
<Drawer title="Basic Drawer" onClose={onClose} open={open}>
<Drawer
title="Basic Drawer"
closable={{ 'aria-label': 'Close Button' }}
onClose={onClose}
open={open}
>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>

View file

@ -1,4 +1,5 @@
import React from 'react';
import { resetWarned } from 'rc-util/lib/warning';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
@ -170,4 +171,18 @@ describe('DropdownButton', () => {
await waitFakeTimer();
expect(container.querySelector('.ant-dropdown-menu-item-active')).toBeTruthy();
});
it('legacy destroyPopupOnHide with Dropdown.Button', () => {
resetWarned();
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
render(
<DropdownButton destroyPopupOnHide menu={{ items: [] }}>
test
</DropdownButton>,
);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Dropdown] `destroyPopupOnHide` is deprecated. Please use `destroyOnClose` instead.',
);
errorSpy.mockRestore();
});
});

View file

@ -1,6 +1,5 @@
import React from 'react';
import type { TriggerProps } from '@rc-component/trigger';
import ConfigProvider from '../../config-provider';
import type { DropDownProps } from '..';
import Dropdown from '..';
@ -8,6 +7,7 @@ import { resetWarned } from '../../_util/warning';
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';
let triggerProps: TriggerProps;
@ -130,10 +130,14 @@ describe('Dropdown', () => {
</div>,
);
expect(error).toHaveBeenCalledWith(
expect.stringContaining("[antd: Dropdown] You are using 'bottomCenter'"),
expect.stringContaining(
'[antd: Dropdown] `placement: bottomCenter` is deprecated. Please use `placement: bottom` instead.',
),
);
expect(error).toHaveBeenCalledWith(
expect.stringContaining("[antd: Dropdown] You are using 'topCenter'"),
expect.stringContaining(
'[antd: Dropdown] `placement: topCenter` is deprecated. Please use `placement: top` instead.',
),
);
error.mockRestore();
});
@ -255,6 +259,50 @@ describe('Dropdown', () => {
errorSpy.mockRestore();
});
it('legacy dropdownRender & legacy destroyPopupOnHide', () => {
resetWarned();
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const dropdownRender = jest.fn((menu) => (
<div className="custom-dropdown">
{menu}
<div className="extra-content">Extra Content</div>
</div>
));
const { container } = render(
<Dropdown
open
destroyPopupOnHide
dropdownRender={dropdownRender}
menu={{
items: [
{
label: <div className="menu-item">Menu Item</div>,
key: 'item',
},
],
}}
>
<a className="trigger" />
</Dropdown>,
);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Dropdown] `dropdownRender` is deprecated. Please use `popupRender` instead.',
);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Dropdown] `destroyPopupOnHide` is deprecated. Please use `destroyOnClose` instead.',
);
expect(dropdownRender).toHaveBeenCalled();
expect(container.querySelector('.custom-dropdown')).toBeTruthy();
expect(container.querySelector('.menu-item')).toBeTruthy();
expect(container.querySelector('.extra-content')).toBeTruthy();
expect(container.querySelector('.extra-content')?.textContent).toBe('Extra Content');
errorSpy.mockRestore();
});
it('not block ref', () => {
const divRef = React.createRef<HTMLDivElement>();
render(

View file

@ -50,7 +50,7 @@ const App: React.FC = () => {
return (
<Dropdown
menu={{ items }}
dropdownRender={(menu) => (
popupRender={(menu) => (
<div style={contentStyle}>
{React.cloneElement(
menu as React.ReactElement<{

View file

@ -67,14 +67,18 @@ const DropdownButton: CompoundedComponent = (props) => {
mouseLeaveDelay,
overlayClassName,
overlayStyle,
destroyOnClose,
destroyPopupOnHide,
dropdownRender,
popupRender,
...restProps
} = props;
const prefixCls = getPrefixCls('dropdown', customizePrefixCls);
const buttonPrefixCls = `${prefixCls}-button`;
const mergedPopupRender = popupRender || dropdownRender;
const dropdownProps: DropdownProps = {
menu,
arrow,
@ -88,14 +92,18 @@ const DropdownButton: CompoundedComponent = (props) => {
mouseLeaveDelay,
overlayClassName,
overlayStyle,
destroyPopupOnHide,
dropdownRender,
destroyOnClose,
popupRender: mergedPopupRender,
};
const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction);
const classes = classNames(buttonPrefixCls, compactItemClassnames, className);
if ('destroyPopupOnHide' in props) {
dropdownProps.destroyPopupOnHide = destroyPopupOnHide;
}
if ('overlay' in props) {
dropdownProps.overlay = overlay;
}

View file

@ -49,11 +49,18 @@ export interface DropdownProps {
autoFocus?: boolean;
arrow?: boolean | DropdownArrowOptions;
trigger?: ('click' | 'hover' | 'contextMenu')[];
/** @deprecated Please use `popupRender` instead */
dropdownRender?: (originNode: React.ReactNode) => React.ReactNode;
popupRender?: (originNode: React.ReactNode) => React.ReactNode;
onOpenChange?: (open: boolean, info: { source: 'trigger' | 'menu' }) => void;
open?: boolean;
disabled?: boolean;
/** @deprecated Please use `destroyOnClose` instead */
destroyPopupOnHide?: boolean;
/**
* @since 5.25.0
*/
destroyOnClose?: boolean;
align?: AlignType;
getPopupContainer?: (triggerNode: HTMLElement) => HTMLElement;
prefixCls?: string;
@ -92,6 +99,7 @@ const Dropdown: CompoundedComponent = (props) => {
trigger,
disabled,
dropdownRender,
popupRender,
getPopupContainer,
overlayClassName,
rootClassName,
@ -107,7 +115,10 @@ const Dropdown: CompoundedComponent = (props) => {
placement = '',
overlay,
transitionName,
destroyOnClose,
destroyPopupOnHide,
} = props;
const {
getPopupContainer: getContextPopupContainer,
getPrefixCls,
@ -115,18 +126,31 @@ const Dropdown: CompoundedComponent = (props) => {
dropdown,
} = React.useContext(ConfigContext);
const mergedPopupRender = popupRender || dropdownRender;
// Warning for deprecated usage
const warning = devUseWarning('Dropdown');
if (process.env.NODE_ENV !== 'production') {
[
['visible', 'open'],
['onVisibleChange', 'onOpenChange'],
].forEach(([deprecatedName, newName]) => {
const deprecatedProps = {
visible: 'open',
onVisibleChange: 'onOpenChange',
overlay: 'menu',
dropdownRender: 'popupRender',
destroyPopupOnHide: 'destroyOnClose',
};
Object.entries(deprecatedProps).forEach(([deprecatedName, newName]) => {
warning.deprecated(!(deprecatedName in props), deprecatedName, newName);
});
warning.deprecated(!('overlay' in props), 'overlay', 'menu');
if (placement.includes('Center')) {
warning.deprecated(
!placement.includes('Center'),
`placement: ${placement}`,
`placement: ${placement.slice(0, placement.indexOf('Center'))}`,
);
}
}
const memoTransitionName = React.useMemo<string>(() => {
@ -153,24 +177,6 @@ const Dropdown: CompoundedComponent = (props) => {
return placement as DropdownPlacement;
}, [placement, direction]);
if (process.env.NODE_ENV !== 'production') {
if (placement.includes('Center')) {
const newPlacement = placement.slice(0, placement.indexOf('Center')) as DropdownPlacement;
warning(
!placement.includes('Center'),
'deprecated',
`You are using '${placement}' placement in Dropdown, which is deprecated. Try to use '${newPlacement}' instead.`,
);
}
[
['visible', 'open'],
['onVisibleChange', 'onOpenChange'],
].forEach(([deprecatedName, newName]) => {
warning.deprecated(!(deprecatedName in props), deprecatedName, newName);
});
}
const prefixCls = getPrefixCls('dropdown', customizePrefixCls);
const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);
@ -184,7 +190,7 @@ const Dropdown: CompoundedComponent = (props) => {
disabled?: boolean;
}>;
const dropdownTrigger = cloneElement(child, {
const popupTrigger = cloneElement(child, {
className: classNames(
`${prefixCls}-trigger`,
{
@ -247,8 +253,8 @@ const Dropdown: CompoundedComponent = (props) => {
} else {
overlayNode = overlay;
}
if (dropdownRender) {
overlayNode = dropdownRender(overlayNode);
if (mergedPopupRender) {
overlayNode = mergedPopupRender(overlayNode);
}
overlayNode = React.Children.only(
typeof overlayNode === 'string' ? <span>{overlayNode}</span> : overlayNode,
@ -305,8 +311,9 @@ const Dropdown: CompoundedComponent = (props) => {
placement={memoPlacement}
onVisibleChange={onInnerOpenChange}
overlayStyle={{ ...dropdown?.style, ...overlayStyle, zIndex }}
autoDestroy={destroyOnClose ?? destroyPopupOnHide}
>
{dropdownTrigger}
{popupTrigger}
</RcDropdown>
);

View file

@ -48,8 +48,10 @@ Common props ref[Common props](/docs/react/common-props)
| autoAdjustOverflow | Whether to adjust dropdown placement automatically when dropdown is off screen | boolean | true | 5.2.0 |
| autoFocus | Focus element in `overlay` when opened | boolean | false | 4.21.0 |
| disabled | Whether the dropdown menu is disabled | boolean | - | |
| destroyPopupOnHide | Whether destroy dropdown when hidden | boolean | false | |
| dropdownRender | Customize dropdown content | (menus: ReactNode) => ReactNode | - | 4.24.0 |
| ~~destroyPopupOnHide~~ | Whether destroy dropdown when hidden, use `destroyOnClose` instead | boolean | false | |
| destroyOnClose | Whether destroy dropdown when hidden | boolean | false | 5.25.0 |
| ~~dropdownRender~~ | Customize dropdown content, use `popupRender` instead | (menus: ReactNode) => ReactNode | - | 4.24.0 |
| popupRender | Customize popup content | (menus: ReactNode) => ReactNode | - | 5.25.0 |
| getPopupContainer | To set the container of the dropdown menu. The default is to create a div element in body, but you can reset it to the scrolling area and make a relative reposition. [Example on CodePen](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | (triggerNode: HTMLElement) => HTMLElement | () => document.body | |
| menu | The menu props | [MenuProps](/components/menu/#api) | - | 4.24.0 |
| overlayClassName | The class name of the dropdown root element | string | - | |

View file

@ -52,8 +52,10 @@ demo:
| autoAdjustOverflow | 下拉框被遮挡时自动调整位置 | boolean | true | 5.2.0 |
| autoFocus | 打开后自动聚焦下拉框 | boolean | false | 4.21.0 |
| disabled | 菜单是否禁用 | boolean | - | |
| destroyPopupOnHide | 关闭后是否销毁 Dropdown | boolean | false | |
| dropdownRender | 自定义下拉框内容 | (menus: ReactNode) => ReactNode | - | 4.24.0 |
| ~~destroyPopupOnHide~~ | 关闭后是否销毁 Dropdown使用 `destroyOnClose` 替换 | boolean | false | |
| destroyOnClose | 关闭后是否销毁 Dropdown | boolean | false | 5.25.0 |
| ~~dropdownRender~~ | 自定义下拉框内容,使用 `popupRender` 替换 | (menus: ReactNode) => ReactNode | - | 4.24.0 |
| popupRender | 自定义弹出框内容 | (menus: ReactNode) => ReactNode | - | 5.25.0 |
| getPopupContainer | 菜单渲染父节点。默认渲染到 body 上,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。[示例](https://codepen.io/afc163/pen/zEjNOy?editors=0010) | (triggerNode: HTMLElement) => HTMLElement | () => document.body | |
| menu | 菜单配置项 | [MenuProps](/components/menu-cn#api) | - | 4.24.0 |
| overlayClassName | 下拉根元素的类名称 | string | - | |

View file

@ -2,6 +2,7 @@ import React, { useContext } from 'react';
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import convertToTooltipProps from '../_util/convertToTooltipProps';
import { useZIndex } from '../_util/hooks/useZIndex';
import { devUseWarning } from '../_util/warning';
import Badge from '../badge';
@ -74,12 +75,10 @@ const InternalFloatButton = React.forwardRef<FloatButtonElement, FloatButtonProp
buttonNode = <Badge {...badgeProps}>{buttonNode}</Badge>;
}
if ('tooltip' in props) {
buttonNode = (
<Tooltip title={tooltip} placement={direction === 'rtl' ? 'right' : 'left'}>
{buttonNode}
</Tooltip>
);
// ============================ Tooltip ============================
const tooltipProps = convertToTooltipProps(tooltip);
if (tooltipProps) {
buttonNode = <Tooltip {...tooltipProps}>{buttonNode}</Tooltip>;
}
if (process.env.NODE_ENV !== 'production') {

View file

@ -140,12 +140,12 @@ Array [
</sup>
</span>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-tooltip-placement-left"
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-tooltip-placement-top"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; top: 0px; right: 0px;"
style="position: absolute; bottom: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
@ -2038,65 +2038,125 @@ Array [
exports[`renders components/float-button/demo/shape.tsx extend context correctly 2`] = `[]`;
exports[`renders components/float-button/demo/tooltip.tsx extend context correctly 1`] = `
<button
class="ant-float-btn ant-float-btn-default ant-float-btn-circle"
type="button"
>
<div
aria-describedby="test-id"
class="ant-float-btn-body"
Array [
<button
class="ant-float-btn ant-float-btn-default ant-float-btn-circle"
style="inset-block-end: 108px;"
type="button"
>
<div
class="ant-float-btn-content"
aria-describedby="test-id"
class="ant-float-btn-body"
>
<div
class="ant-float-btn-icon"
class="ant-float-btn-content"
>
<span
aria-label="file-text"
class="anticon anticon-file-text"
role="img"
<div
class="ant-float-btn-icon"
>
<svg
aria-hidden="true"
data-icon="file-text"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="file-text"
class="anticon anticon-file-text"
role="img"
>
<path
d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494zM504 618H320c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM312 490v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H320c-4.4 0-8 3.6-8 8z"
/>
</svg>
</span>
</div>
</div>
</div>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-tooltip-placement-left"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; top: 0px; right: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
>
<div>
Documents
<svg
aria-hidden="true"
data-icon="file-text"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494zM504 618H320c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM312 490v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H320c-4.4 0-8 3.6-8 8z"
/>
</svg>
</span>
</div>
</div>
</div>
</div>
</button>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-tooltip-blue ant-tooltip-placement-top"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; bottom: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
>
Since 5.25.0+
</div>
</div>
</div>
</button>,
<button
class="ant-float-btn ant-float-btn-default ant-float-btn-circle"
type="button"
>
<div
aria-describedby="test-id"
class="ant-float-btn-body"
>
<div
class="ant-float-btn-content"
>
<div
class="ant-float-btn-icon"
>
<span
aria-label="file-text"
class="anticon anticon-file-text"
role="img"
>
<svg
aria-hidden="true"
data-icon="file-text"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494zM504 618H320c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM312 490v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H320c-4.4 0-8 3.6-8 8z"
/>
</svg>
</span>
</div>
</div>
</div>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-tooltip-placement-top"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; bottom: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
>
<div>
Documents
</div>
</div>
</div>
</div>
</button>,
]
`;
exports[`renders components/float-button/demo/tooltip.tsx extend context correctly 2`] = `[]`;

View file

@ -1974,43 +1974,83 @@ Array [
`;
exports[`renders components/float-button/demo/tooltip.tsx correctly 1`] = `
<button
class="ant-float-btn ant-float-btn-default ant-float-btn-circle"
type="button"
>
<div
aria-describedby="test-id"
class="ant-float-btn-body"
Array [
<button
class="ant-float-btn ant-float-btn-default ant-float-btn-circle"
style="inset-block-end:108px"
type="button"
>
<div
class="ant-float-btn-content"
aria-describedby="test-id"
class="ant-float-btn-body"
>
<div
class="ant-float-btn-icon"
class="ant-float-btn-content"
>
<span
aria-label="file-text"
class="anticon anticon-file-text"
role="img"
<div
class="ant-float-btn-icon"
>
<svg
aria-hidden="true"
data-icon="file-text"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
<span
aria-label="file-text"
class="anticon anticon-file-text"
role="img"
>
<path
d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494zM504 618H320c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM312 490v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H320c-4.4 0-8 3.6-8 8z"
/>
</svg>
</span>
<svg
aria-hidden="true"
data-icon="file-text"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494zM504 618H320c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM312 490v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H320c-4.4 0-8 3.6-8 8z"
/>
</svg>
</span>
</div>
</div>
</div>
</div>
</button>
</button>,
<button
class="ant-float-btn ant-float-btn-default ant-float-btn-circle"
type="button"
>
<div
aria-describedby="test-id"
class="ant-float-btn-body"
>
<div
class="ant-float-btn-content"
>
<div
class="ant-float-btn-icon"
>
<span
aria-label="file-text"
class="anticon anticon-file-text"
role="img"
>
<svg
aria-hidden="true"
data-icon="file-text"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M854.6 288.6L639.4 73.4c-6-6-14.1-9.4-22.6-9.4H192c-17.7 0-32 14.3-32 32v832c0 17.7 14.3 32 32 32h640c17.7 0 32-14.3 32-32V311.3c0-8.5-3.4-16.7-9.4-22.7zM790.2 326H602V137.8L790.2 326zm1.8 562H232V136h302v216a42 42 0 0042 42h216v494zM504 618H320c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8zM312 490v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H320c-4.4 0-8 3.6-8 8z"
/>
</svg>
</span>
</div>
</div>
</div>
</button>,
]
`;
exports[`renders components/float-button/demo/type.tsx correctly 1`] = `

View file

@ -63,15 +63,27 @@ describe('FloatButton', () => {
errSpy.mockRestore();
});
it('tooltip should support number `0`', async () => {
jest.useFakeTimers();
const { container } = render(<FloatButton tooltip={0} />);
fireEvent.mouseEnter(container.querySelector<HTMLDivElement>('.ant-float-btn-body')!);
await waitFakeTimer();
const element = container.querySelector('.ant-tooltip')?.querySelector('.ant-tooltip-inner');
expect(element?.textContent).toBe('0');
jest.clearAllTimers();
jest.useRealTimers();
describe('tooltip', () => {
it('tooltip should support number `0`', async () => {
jest.useFakeTimers();
const { container } = render(<FloatButton tooltip={0} />);
fireEvent.mouseEnter(container.querySelector<HTMLDivElement>('.ant-float-btn-body')!);
await waitFakeTimer();
const element = container.querySelector('.ant-tooltip')?.querySelector('.ant-tooltip-inner');
expect(element?.textContent).toBe('0');
jest.clearAllTimers();
jest.useRealTimers();
});
it('tooltip should support tooltipProps', async () => {
jest.useFakeTimers();
const { container } = render(<FloatButton tooltip={{ title: 'hi' }} />);
fireEvent.mouseEnter(container.querySelector<HTMLDivElement>('.ant-float-btn-body')!);
await waitFakeTimer();
const element = container.querySelector('.ant-tooltip')?.querySelector('.ant-tooltip-inner');
expect(element?.textContent).toBe('hi');
jest.clearAllTimers();
jest.useRealTimers();
});
});
it('getOffset should return 0 when radius is 0', () => {

View file

@ -1,6 +1,19 @@
import React from 'react';
import { FloatButton } from 'antd';
const App: React.FC = () => <FloatButton tooltip={<div>Documents</div>} />;
const App: React.FC = () => (
<>
<FloatButton
style={{ insetBlockEnd: 108 }}
tooltip={{
// tooltipProps is supported starting from version 5.25.0.
title: 'Since 5.25.0+',
color: 'blue',
placement: 'top',
}}
/>
<FloatButton tooltip={<div>Documents</div>} />
</>
);
export default App;

View file

@ -44,7 +44,7 @@ Common props ref[Common props](/docs/react/common-props)
| --- | --- | --- | --- | --- |
| icon | Set the icon component of button | ReactNode | - | |
| description | Text and other | ReactNode | - | |
| tooltip | The text shown in the tooltip | ReactNode \| () => ReactNode | | |
| tooltip | The text shown in the tooltip | ReactNode \| [TooltipProps](/components/tooltip#api) | - | TooltipProps: 5.25.0 |
| type | Setting button type | `default` \| `primary` | `default` | |
| shape | Setting button shape | `circle` \| `square` | `circle` | |
| onClick | Set the handler to handle `click` event | (event) => void | - | |

View file

@ -45,7 +45,7 @@ tag: 5.0.0
| --- | --- | --- | --- | --- |
| icon | 自定义图标 | ReactNode | - | |
| description | 文字及其它内容 | ReactNode | - | |
| tooltip | 气泡卡片的内容 | ReactNode \| () => ReactNode | - | |
| tooltip | 气泡卡片的内容 | ReactNode \| [TooltipProps](/components/tooltip-cn#api) | - | TooltipProps: 5.25.0 |
| type | 设置按钮类型 | `default` \| `primary` | `default` | |
| shape | 设置按钮形状 | `circle` \| `square` | `circle` | |
| onClick | 点击按钮时的回调 | (event) => void | - | |

View file

@ -27,7 +27,7 @@ export interface FloatButtonProps extends React.DOMAttributes<FloatButtonElement
description?: React.ReactNode;
type?: FloatButtonType;
shape?: FloatButtonShape;
tooltip?: TooltipProps['title'];
tooltip?: React.ReactNode | TooltipProps;
href?: string;
target?: React.HTMLAttributeAnchorTarget;
badge?: FloatButtonBadgeProps;

View file

@ -2,6 +2,7 @@ import * as React from 'react';
import QuestionCircleOutlined from '@ant-design/icons/QuestionCircleOutlined';
import classNames from 'classnames';
import convertToTooltipProps from '../_util/convertToTooltipProps';
import type { ColProps } from '../grid/col';
import Col from '../grid/col';
import { useLocale } from '../locale';
@ -19,20 +20,6 @@ export type WrapperTooltipProps = TooltipProps & {
export type LabelTooltipType = WrapperTooltipProps | React.ReactNode;
function toTooltipProps(tooltip: LabelTooltipType): WrapperTooltipProps | null {
if (!tooltip) {
return null;
}
if (typeof tooltip === 'object' && !React.isValidElement(tooltip)) {
return tooltip as WrapperTooltipProps;
}
return {
title: tooltip,
};
}
export interface FormItemLabelProps {
colon?: boolean;
htmlFor?: string;
@ -98,7 +85,7 @@ const FormItemLabel: React.FC<FormItemLabelProps & { required?: boolean; prefixC
}
// Tooltip
const tooltipProps = toTooltipProps(tooltip);
const tooltipProps = convertToTooltipProps(tooltip);
if (tooltipProps) {
const { icon = <QuestionCircleOutlined />, ...restTooltipProps } = tooltipProps;

View file

@ -42,7 +42,14 @@ exports[`Grid should render Row 1`] = `
/>
`;
exports[`Grid when typeof gutter is object array in large screen 1`] = `
exports[`Grid when typeof gutter is object array in large screen 0 1`] = `
<div
class="ant-row"
style="margin-left: -20px; margin-right: -20px; row-gap: 400px;"
/>
`;
exports[`Grid when typeof gutter is object array in large screen 1 1`] = `
<div
class="ant-row"
style="margin-left: -20px; margin-right: -20px; row-gap: 400px;"

View file

@ -6,6 +6,27 @@ import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render } from '../../../tests/utils';
import useBreakpoint from '../hooks/useBreakpoint';
const createImplFn = (value: string | number) => {
return [
(query: string) => ({
matches: query === value,
addEventListener: (type: string, cb: (e: { matches: boolean }) => void) => {
if (type === 'change') {
cb({ matches: query === value });
}
},
removeEventListener: jest.fn(),
}),
(query: string) => ({
matches: query === value,
addListener: (cb: (e: { matches: boolean }) => void) => {
cb({ matches: query === value });
},
removeListener: jest.fn(),
}),
];
};
// Mock for `responsiveObserve` to test `unsubscribe` call
jest.mock('../../_util/responsiveObserver', () => {
const modules = jest.requireActual('../../_util/responsiveObserver');
@ -74,36 +95,27 @@ describe('Grid', () => {
expect(container.querySelector('div')!.style.marginRight).toEqual('-4px');
});
it('when typeof gutter is object array in large screen', () => {
jest.spyOn(window, 'matchMedia').mockImplementation(
(query) =>
({
addListener: (cb: (e: { matches: boolean }) => void) => {
cb({ matches: query === '(min-width: 1200px)' });
},
removeListener: jest.fn(),
matches: query === '(min-width: 1200px)',
}) as any,
);
const { container, asFragment } = render(
<Row
gutter={[
{ xs: 8, sm: 16, md: 24, lg: 32, xl: 40 },
{ xs: 8, sm: 16, md: 24, lg: 100, xl: 400 },
]}
/>,
);
expect(asFragment().firstChild).toMatchSnapshot();
expect(container.querySelector('div')?.style.marginLeft).toBe('-20px');
expect(container.querySelector('div')?.style.marginRight).toBe('-20px');
expect(container.querySelector('div')?.style.marginTop).toBe('');
expect(container.querySelector('div')?.style.marginBottom).toBe('');
createImplFn('(min-width: 1200px)').forEach((impl, i) => {
it(`when typeof gutter is object array in large screen ${i}`, () => {
jest.spyOn(window, 'matchMedia').mockImplementation(impl as any);
const { container, asFragment } = render(
<Row
gutter={[
{ xs: 8, sm: 16, md: 24, lg: 32, xl: 40 },
{ xs: 8, sm: 16, md: 24, lg: 100, xl: 400 },
]}
/>,
);
expect(asFragment().firstChild).toMatchSnapshot();
expect(container.querySelector('div')?.style.marginLeft).toBe('-20px');
expect(container.querySelector('div')?.style.marginRight).toBe('-20px');
expect(container.querySelector('div')?.style.marginTop).toBe('');
expect(container.querySelector('div')?.style.marginBottom).toBe('');
});
});
it('renders wrapped Col correctly', () => {
const MyCol = () => <Col span={12} />;
const MyCol: React.FC = () => <Col span={12} />;
const { asFragment } = render(
<Row gutter={20}>
<div>
@ -112,7 +124,6 @@ describe('Grid', () => {
<MyCol />
</Row>,
);
expect(asFragment().firstChild).toMatchSnapshot();
});
@ -140,75 +151,49 @@ describe('Grid', () => {
// By jsdom mock, actual jsdom not implemented matchMedia
// https://jestjs.io/docs/en/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom
it('should work with useBreakpoint', () => {
const matchMediaSpy = jest.spyOn(window, 'matchMedia');
matchMediaSpy.mockImplementation(
(query) =>
({
addListener: (cb: (e: { matches: boolean }) => void) => {
cb({ matches: query === '(max-width: 575px)' });
},
removeListener: jest.fn(),
matches: query === '(max-width: 575px)',
}) as any,
);
let screensVar: any = null;
function Demo() {
const screens = useBreakpoint();
screensVar = screens;
return <div />;
}
render(<Demo />);
expect(screensVar).toEqual({
xs: true,
sm: false,
md: false,
lg: false,
xl: false,
xxl: false,
createImplFn('(max-width: 575px)').forEach((impl, i) => {
it(`should work with useBreakpoint ${i}`, () => {
jest.spyOn(window, 'matchMedia').mockImplementation(impl as any);
let screensVar: any = null;
const Demo: React.FC = () => {
const screens = useBreakpoint();
screensVar = screens;
return null;
};
render(<Demo />);
expect(screensVar).toEqual({
xs: true,
sm: false,
md: false,
lg: false,
xl: false,
xxl: false,
});
});
});
it('should align by responsive align prop', () => {
const matchMediaSpy = jest.spyOn(window, 'matchMedia');
matchMediaSpy.mockImplementation(
(query) =>
({
addListener: (cb: (e: { matches: boolean }) => void) => {
cb({ matches: query === '(max-width: 575px)' });
},
removeListener: jest.fn(),
matches: query === '(max-width: 575px)',
}) as any,
);
const { container } = render(<Row align="middle" />);
expect(container.innerHTML).toContain('ant-row-middle');
const { container: container2 } = render(<Row align={{ xs: 'middle' }} />);
expect(container2.innerHTML).toContain('ant-row-middle');
const { container: container3 } = render(<Row align={{ lg: 'middle' }} />);
expect(container3.innerHTML).not.toContain('ant-row-middle');
createImplFn('(max-width: 575px)').forEach((impl, i) => {
it(`should align by responsive align prop ${i}`, () => {
jest.spyOn(window, 'matchMedia').mockImplementation(impl as any);
const { container } = render(<Row align="middle" />);
expect(container.innerHTML).toContain('ant-row-middle');
const { container: container2 } = render(<Row align={{ xs: 'middle' }} />);
expect(container2.innerHTML).toContain('ant-row-middle');
const { container: container3 } = render(<Row align={{ lg: 'middle' }} />);
expect(container3.innerHTML).not.toContain('ant-row-middle');
});
});
it('should justify by responsive justify prop', () => {
const matchMediaSpy = jest.spyOn(window, 'matchMedia');
matchMediaSpy.mockImplementation(
(query) =>
({
addListener: (cb: (e: { matches: boolean }) => void) => {
cb({ matches: query === '(max-width: 575px)' });
},
removeListener: jest.fn(),
matches: query === '(max-width: 575px)',
}) as any,
);
const { container } = render(<Row justify="center" />);
expect(container.innerHTML).toContain('ant-row-center');
const { container: container2 } = render(<Row justify={{ xs: 'center' }} />);
expect(container2.innerHTML).toContain('ant-row-center');
const { container: container3 } = render(<Row justify={{ lg: 'center' }} />);
expect(container3.innerHTML).not.toContain('ant-row-center');
createImplFn('(max-width: 575px)').forEach((impl, i) => {
it(`should justify by responsive justify prop ${i}`, () => {
jest.spyOn(window, 'matchMedia').mockImplementation(impl as any);
const { container } = render(<Row justify="center" />);
expect(container.innerHTML).toContain('ant-row-center');
const { container: container2 } = render(<Row justify={{ xs: 'center' }} />);
expect(container2.innerHTML).toContain('ant-row-center');
const { container: container3 } = render(<Row justify={{ lg: 'center' }} />);
expect(container3.innerHTML).not.toContain('ant-row-center');
});
});
// https://github.com/ant-design/ant-design/issues/39690

View file

@ -130,7 +130,7 @@ export type { SpaceProps } from './space';
export { default as Spin } from './spin';
export type { SpinProps } from './spin';
export { default as Statistic } from './statistic';
export type { CountdownProps, StatisticProps } from './statistic';
export type { StatisticTimerProps, CountdownProps, StatisticProps } from './statistic';
export { default as Steps } from './steps';
export type { StepProps, StepsProps } from './steps';
export { default as Switch } from './switch';

View file

@ -6,6 +6,7 @@ import RightOutlined from '@ant-design/icons/RightOutlined';
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import { addMediaQueryListener, removeMediaQueryListener } from '../_util/mediaQueryUtil';
import { ConfigContext } from '../config-provider';
import { LayoutContext } from './context';
import useStyle from './style/sider';
@ -118,28 +119,16 @@ const Sider = React.forwardRef<HTMLDivElement, SiderProps>((props, ref) => {
useEffect(() => {
function responsiveHandler(mql: MediaQueryListEvent | MediaQueryList) {
return responsiveHandlerRef.current!(mql);
return responsiveHandlerRef.current?.(mql);
}
let mql: MediaQueryList;
if (typeof window !== 'undefined') {
const { matchMedia } = window;
if (matchMedia! && breakpoint && breakpoint in dimensionMaxMap) {
mql = matchMedia(`screen and (max-width: ${dimensionMaxMap[breakpoint]})`);
try {
mql.addEventListener('change', responsiveHandler);
} catch {
mql.addListener(responsiveHandler);
}
responsiveHandler(mql);
}
if (typeof window?.matchMedia !== 'undefined' && breakpoint && breakpoint in dimensionMaxMap) {
mql = window.matchMedia(`screen and (max-width: ${dimensionMaxMap[breakpoint]})`);
addMediaQueryListener(mql, responsiveHandler);
responsiveHandler(mql);
}
return () => {
try {
mql?.removeEventListener('change', responsiveHandler);
} catch {
mql?.removeListener(responsiveHandler);
}
removeMediaQueryListener(mql, responsiveHandler);
};
}, [breakpoint]); // in order to accept dynamic 'breakpoint' property, we need to add 'breakpoint' into dependency array.

View file

@ -1,8 +1,8 @@
import React, { useState } from 'react';
import type { Breakpoint } from '../..';
import { fireEvent, render } from '../../../tests/utils';
import Sider from '../Sider';
import type { Breakpoint } from '../..';
const Content = () => {
const [breakpoint, setBreakpoint] = useState<Breakpoint>('sm');

View file

@ -7177,6 +7177,7 @@ exports[`Locale Provider should display the text as ar 1`] = `
type="button"
>
<span
aria-label="إغلاق"
class="ant-modal-close-x"
>
<span
@ -12498,6 +12499,7 @@ exports[`Locale Provider should display the text as az 1`] = `
type="button"
>
<span
aria-label="Bağla"
class="ant-modal-close-x"
>
<span
@ -17819,6 +17821,7 @@ exports[`Locale Provider should display the text as bg 1`] = `
type="button"
>
<span
aria-label="Затвори"
class="ant-modal-close-x"
>
<span
@ -23140,6 +23143,7 @@ exports[`Locale Provider should display the text as bn-bd 1`] = `
type="button"
>
<span
aria-label="বন্ধ"
class="ant-modal-close-x"
>
<span
@ -28461,6 +28465,7 @@ exports[`Locale Provider should display the text as by 1`] = `
type="button"
>
<span
aria-label="Закрыць"
class="ant-modal-close-x"
>
<span
@ -33782,6 +33787,7 @@ exports[`Locale Provider should display the text as ca 1`] = `
type="button"
>
<span
aria-label="Tancar"
class="ant-modal-close-x"
>
<span
@ -39103,6 +39109,7 @@ exports[`Locale Provider should display the text as cs 1`] = `
type="button"
>
<span
aria-label="Zavřít"
class="ant-modal-close-x"
>
<span
@ -44424,6 +44431,7 @@ exports[`Locale Provider should display the text as da 1`] = `
type="button"
>
<span
aria-label="Luk"
class="ant-modal-close-x"
>
<span
@ -49745,6 +49753,7 @@ exports[`Locale Provider should display the text as de 1`] = `
type="button"
>
<span
aria-label="Schließen"
class="ant-modal-close-x"
>
<span
@ -55066,6 +55075,7 @@ exports[`Locale Provider should display the text as el 1`] = `
type="button"
>
<span
aria-label="Κλείσιμο"
class="ant-modal-close-x"
>
<span
@ -60387,6 +60397,7 @@ exports[`Locale Provider should display the text as en 1`] = `
type="button"
>
<span
aria-label="Close"
class="ant-modal-close-x"
>
<span
@ -65708,6 +65719,7 @@ exports[`Locale Provider should display the text as en-gb 1`] = `
type="button"
>
<span
aria-label="Close"
class="ant-modal-close-x"
>
<span
@ -71029,6 +71041,7 @@ exports[`Locale Provider should display the text as es 1`] = `
type="button"
>
<span
aria-label="Cerrar"
class="ant-modal-close-x"
>
<span
@ -76350,6 +76363,7 @@ exports[`Locale Provider should display the text as et 1`] = `
type="button"
>
<span
aria-label="Sulge"
class="ant-modal-close-x"
>
<span
@ -81671,6 +81685,7 @@ exports[`Locale Provider should display the text as eu 1`] = `
type="button"
>
<span
aria-label="Itxi"
class="ant-modal-close-x"
>
<span
@ -86992,6 +87007,7 @@ exports[`Locale Provider should display the text as fa 1`] = `
type="button"
>
<span
aria-label="بستن"
class="ant-modal-close-x"
>
<span
@ -92313,6 +92329,7 @@ exports[`Locale Provider should display the text as fi 1`] = `
type="button"
>
<span
aria-label="Sulje"
class="ant-modal-close-x"
>
<span
@ -97634,6 +97651,7 @@ exports[`Locale Provider should display the text as fr 1`] = `
type="button"
>
<span
aria-label="Fermer"
class="ant-modal-close-x"
>
<span
@ -102955,6 +102973,7 @@ exports[`Locale Provider should display the text as fr 2`] = `
type="button"
>
<span
aria-label="Fermer"
class="ant-modal-close-x"
>
<span
@ -108276,6 +108295,7 @@ exports[`Locale Provider should display the text as fr 3`] = `
type="button"
>
<span
aria-label="Fermer"
class="ant-modal-close-x"
>
<span
@ -113597,6 +113617,7 @@ exports[`Locale Provider should display the text as ga 1`] = `
type="button"
>
<span
aria-label="Dún"
class="ant-modal-close-x"
>
<span
@ -118918,6 +118939,7 @@ exports[`Locale Provider should display the text as gl 1`] = `
type="button"
>
<span
aria-label="Cerrar"
class="ant-modal-close-x"
>
<span
@ -124239,6 +124261,7 @@ exports[`Locale Provider should display the text as he 1`] = `
type="button"
>
<span
aria-label="סגור"
class="ant-modal-close-x"
>
<span
@ -129560,6 +129583,7 @@ exports[`Locale Provider should display the text as hi 1`] = `
type="button"
>
<span
aria-label="बंद"
class="ant-modal-close-x"
>
<span
@ -134881,6 +134905,7 @@ exports[`Locale Provider should display the text as hr 1`] = `
type="button"
>
<span
aria-label="Zatvori"
class="ant-modal-close-x"
>
<span
@ -140202,6 +140227,7 @@ exports[`Locale Provider should display the text as hu 1`] = `
type="button"
>
<span
aria-label="Bezárás"
class="ant-modal-close-x"
>
<span
@ -145523,6 +145549,7 @@ exports[`Locale Provider should display the text as hy-am 1`] = `
type="button"
>
<span
aria-label="Դադարեցնել"
class="ant-modal-close-x"
>
<span
@ -150844,6 +150871,7 @@ exports[`Locale Provider should display the text as id 1`] = `
type="button"
>
<span
aria-label="Tutup"
class="ant-modal-close-x"
>
<span
@ -156165,6 +156193,7 @@ exports[`Locale Provider should display the text as is 1`] = `
type="button"
>
<span
aria-label="Loka"
class="ant-modal-close-x"
>
<span
@ -161486,6 +161515,7 @@ exports[`Locale Provider should display the text as it 1`] = `
type="button"
>
<span
aria-label="Chiudi"
class="ant-modal-close-x"
>
<span
@ -166807,6 +166837,7 @@ exports[`Locale Provider should display the text as ja 1`] = `
type="button"
>
<span
aria-label="閉じる"
class="ant-modal-close-x"
>
<span
@ -172128,6 +172159,7 @@ exports[`Locale Provider should display the text as ka 1`] = `
type="button"
>
<span
aria-label="დახურვა"
class="ant-modal-close-x"
>
<span
@ -177449,6 +177481,7 @@ exports[`Locale Provider should display the text as kk 1`] = `
type="button"
>
<span
aria-label="Жабу"
class="ant-modal-close-x"
>
<span
@ -182768,6 +182801,7 @@ exports[`Locale Provider should display the text as km 1`] = `
type="button"
>
<span
aria-label="បិទ"
class="ant-modal-close-x"
>
<span
@ -188089,6 +188123,7 @@ exports[`Locale Provider should display the text as kn 1`] = `
type="button"
>
<span
aria-label="ಮುಚ್ಚಿ"
class="ant-modal-close-x"
>
<span
@ -193410,6 +193445,7 @@ exports[`Locale Provider should display the text as ko 1`] = `
type="button"
>
<span
aria-label="닫기"
class="ant-modal-close-x"
>
<span
@ -198731,6 +198767,7 @@ exports[`Locale Provider should display the text as ku 1`] = `
type="button"
>
<span
aria-label="Betal ke"
class="ant-modal-close-x"
>
<span
@ -204052,6 +204089,7 @@ exports[`Locale Provider should display the text as ku-iq 1`] = `
type="button"
>
<span
aria-label="Betal ke"
class="ant-modal-close-x"
>
<span
@ -209373,6 +209411,7 @@ exports[`Locale Provider should display the text as lt 1`] = `
type="button"
>
<span
aria-label="Uždaryti"
class="ant-modal-close-x"
>
<span
@ -214694,6 +214733,7 @@ exports[`Locale Provider should display the text as lv 1`] = `
type="button"
>
<span
aria-label="Aizvērt"
class="ant-modal-close-x"
>
<span
@ -220015,6 +220055,7 @@ exports[`Locale Provider should display the text as mk 1`] = `
type="button"
>
<span
aria-label="Затвори"
class="ant-modal-close-x"
>
<span
@ -225336,6 +225377,7 @@ exports[`Locale Provider should display the text as ml 1`] = `
type="button"
>
<span
aria-label="മുടക്കുക"
class="ant-modal-close-x"
>
<span
@ -230657,6 +230699,7 @@ exports[`Locale Provider should display the text as mn-mn 1`] = `
type="button"
>
<span
aria-label="Хаах"
class="ant-modal-close-x"
>
<span
@ -235978,6 +236021,7 @@ exports[`Locale Provider should display the text as ms-my 1`] = `
type="button"
>
<span
aria-label="Tutup"
class="ant-modal-close-x"
>
<span
@ -241299,6 +241343,7 @@ exports[`Locale Provider should display the text as my 1`] = `
type="button"
>
<span
aria-label="ပိတ်ပါ"
class="ant-modal-close-x"
>
<span
@ -246620,6 +246665,7 @@ exports[`Locale Provider should display the text as nb 1`] = `
type="button"
>
<span
aria-label="Lukk"
class="ant-modal-close-x"
>
<span
@ -251941,6 +251987,7 @@ exports[`Locale Provider should display the text as ne-np 1`] = `
type="button"
>
<span
aria-label="बन्द"
class="ant-modal-close-x"
>
<span
@ -257262,6 +257309,7 @@ exports[`Locale Provider should display the text as nl 1`] = `
type="button"
>
<span
aria-label="Sluiten"
class="ant-modal-close-x"
>
<span
@ -262583,6 +262631,7 @@ exports[`Locale Provider should display the text as nl-be 1`] = `
type="button"
>
<span
aria-label="Sluiten"
class="ant-modal-close-x"
>
<span
@ -267904,6 +267953,7 @@ exports[`Locale Provider should display the text as pl 1`] = `
type="button"
>
<span
aria-label="Zamknij"
class="ant-modal-close-x"
>
<span
@ -273225,6 +273275,7 @@ exports[`Locale Provider should display the text as pt 1`] = `
type="button"
>
<span
aria-label="Fechar"
class="ant-modal-close-x"
>
<span
@ -278546,6 +278597,7 @@ exports[`Locale Provider should display the text as pt-br 1`] = `
type="button"
>
<span
aria-label="Fechar"
class="ant-modal-close-x"
>
<span
@ -283867,6 +283919,7 @@ exports[`Locale Provider should display the text as ro 1`] = `
type="button"
>
<span
aria-label="Închide"
class="ant-modal-close-x"
>
<span
@ -289188,6 +289241,7 @@ exports[`Locale Provider should display the text as ru 1`] = `
type="button"
>
<span
aria-label="Закрыть"
class="ant-modal-close-x"
>
<span
@ -294509,6 +294563,7 @@ exports[`Locale Provider should display the text as si 1`] = `
type="button"
>
<span
aria-label="වසන්න"
class="ant-modal-close-x"
>
<span
@ -299830,6 +299885,7 @@ exports[`Locale Provider should display the text as sk 1`] = `
type="button"
>
<span
aria-label="Zavrieť"
class="ant-modal-close-x"
>
<span
@ -305151,6 +305207,7 @@ exports[`Locale Provider should display the text as sl 1`] = `
type="button"
>
<span
aria-label="Zapri"
class="ant-modal-close-x"
>
<span
@ -310472,6 +310529,7 @@ exports[`Locale Provider should display the text as sr 1`] = `
type="button"
>
<span
aria-label="Zatvori"
class="ant-modal-close-x"
>
<span
@ -315793,6 +315851,7 @@ exports[`Locale Provider should display the text as sv 1`] = `
type="button"
>
<span
aria-label="Stäng"
class="ant-modal-close-x"
>
<span
@ -321114,6 +321173,7 @@ exports[`Locale Provider should display the text as ta 1`] = `
type="button"
>
<span
aria-label="மூடு"
class="ant-modal-close-x"
>
<span
@ -326435,6 +326495,7 @@ exports[`Locale Provider should display the text as th 1`] = `
type="button"
>
<span
aria-label="ปิด"
class="ant-modal-close-x"
>
<span
@ -331756,6 +331817,7 @@ exports[`Locale Provider should display the text as tk 1`] = `
type="button"
>
<span
aria-label="Ýagty"
class="ant-modal-close-x"
>
<span
@ -337077,6 +337139,7 @@ exports[`Locale Provider should display the text as tr 1`] = `
type="button"
>
<span
aria-label="Kapat"
class="ant-modal-close-x"
>
<span
@ -342398,6 +342461,7 @@ exports[`Locale Provider should display the text as uk 1`] = `
type="button"
>
<span
aria-label="Закрити"
class="ant-modal-close-x"
>
<span
@ -347719,6 +347783,7 @@ exports[`Locale Provider should display the text as ur 1`] = `
type="button"
>
<span
aria-label="بند کریں"
class="ant-modal-close-x"
>
<span
@ -353040,6 +353105,7 @@ exports[`Locale Provider should display the text as uz-latn 1`] = `
type="button"
>
<span
aria-label="Yopish"
class="ant-modal-close-x"
>
<span
@ -358361,6 +358427,7 @@ exports[`Locale Provider should display the text as vi 1`] = `
type="button"
>
<span
aria-label="Đóng"
class="ant-modal-close-x"
>
<span
@ -363682,6 +363749,7 @@ exports[`Locale Provider should display the text as zh-cn 1`] = `
type="button"
>
<span
aria-label="关闭"
class="ant-modal-close-x"
>
<span
@ -369003,6 +369071,7 @@ exports[`Locale Provider should display the text as zh-hk 1`] = `
type="button"
>
<span
aria-label="關閉"
class="ant-modal-close-x"
>
<span
@ -374324,6 +374393,7 @@ exports[`Locale Provider should display the text as zh-tw 1`] = `
type="button"
>
<span
aria-label="關閉"
class="ant-modal-close-x"
>
<span

View file

@ -15,6 +15,7 @@ const localeValues: Locale = {
Calendar,
global: {
placeholder: 'يرجى التحديد',
close: 'إغلاق',
},
Table: {
filterTitle: 'الفلاتر',

View file

@ -15,6 +15,7 @@ const localeValues: Locale = {
Calendar,
global: {
placeholder: 'Zəhmət olmasa seçin',
close: 'Bağla',
},
Table: {
filterTitle: 'Filter menyu',

View file

@ -13,6 +13,9 @@ const localeValues: Locale = {
DatePicker,
TimePicker,
Calendar,
global: {
close: 'Затвори',
},
Table: {
filterTitle: 'Филтриране',
filterConfirm: 'Добре',
@ -20,6 +23,11 @@ const localeValues: Locale = {
selectAll: 'Избор на текуща страница',
selectInvert: 'Обръщане',
},
Tour: {
Next: 'Следващ',
Previous: 'Предишен',
Finish: 'Завърши',
},
Modal: {
okText: 'Добре',
cancelText: 'Отказ',

View file

@ -15,6 +15,7 @@ const localeValues: Locale = {
Calendar,
global: {
placeholder: 'অনুগ্রহ করে নির্বাচন করুন',
close: 'বন্ধ',
},
Table: {
filterTitle: 'ফিল্টার মেনু',
@ -33,6 +34,11 @@ const localeValues: Locale = {
triggerAsc: 'আরোহী বাছাই করতে ক্লিক করুন',
cancelSort: 'বাছাই বাতিল করতে ক্লিক করুন',
},
Tour: {
Next: 'পরবর্তী',
Previous: 'পূর্ববর্তী',
Finish: 'সমাপ্ত',
},
Modal: {
okText: 'ঠিক',
cancelText: 'বাতিল',

View file

@ -15,6 +15,7 @@ const localeValues: Locale = {
Calendar,
global: {
placeholder: 'Калі ласка, выберыце',
close: 'Закрыць',
},
Table: {
filterTitle: 'Фільтр',
@ -35,6 +36,11 @@ const localeValues: Locale = {
triggerAsc: 'Націсніце для сартавання па ўзрастанні',
cancelSort: 'Націсніце, каб адмяніць сартаванне',
},
Tour: {
Next: 'Наступны',
Previous: 'Папярэдняя',
Finish: 'Завяршыць',
},
Modal: {
okText: 'OK',
cancelText: 'Адмена',

View file

@ -15,6 +15,7 @@ const localeValues: Locale = {
Calendar,
global: {
placeholder: 'Seleccionar',
close: 'Tancar',
},
Table: {
filterTitle: 'Filtrar el menú',
@ -31,6 +32,11 @@ const localeValues: Locale = {
triggerAsc: 'Ordre ascendent',
cancelSort: 'Desactivar lordre',
},
Tour: {
Next: 'Següent',
Previous: 'Anterior',
Finish: 'Finalitzar',
},
Modal: {
okText: 'Dacord',
cancelText: 'Cancel·lar',

View file

@ -15,6 +15,7 @@ const localeValues: Locale = {
Calendar,
global: {
placeholder: 'Prosím vyber',
close: 'Zavřít',
},
Table: {
filterTitle: 'Filtr',
@ -35,6 +36,11 @@ const localeValues: Locale = {
triggerAsc: 'Klikni pro vzestupné řazení',
cancelSort: 'Klikni pro zrušení řazení',
},
Tour: {
Next: 'Další',
Previous: 'Předchozí',
Finish: 'Dokončit',
},
Modal: {
okText: 'OK',
cancelText: 'Zrušit',

View file

@ -12,6 +12,9 @@ const localeValues: Locale = {
TimePicker,
Calendar,
Pagination,
global: {
close: 'Luk',
},
Table: {
filterTitle: 'Filtermenu',
filterConfirm: 'OK',
@ -29,6 +32,11 @@ const localeValues: Locale = {
triggerAsc: 'Klik for at sortere stigende',
cancelSort: 'Klik for at annullere sortering',
},
Tour: {
Next: 'Næste',
Previous: 'Forrige',
Finish: 'Færdiggørelse',
},
Modal: {
okText: 'OK',
cancelText: 'Afbryd',

View file

@ -15,6 +15,7 @@ const localeValues: Locale = {
Calendar,
global: {
placeholder: 'Bitte auswählen',
close: 'Schließen',
},
Table: {
filterTitle: 'Filter-Menü',

View file

@ -15,6 +15,7 @@ const localeValues: Locale = {
Calendar,
global: {
placeholder: 'Παρακαλώ επιλέξτε',
close: 'Κλείσιμο',
},
Table: {
filterTitle: 'Μενού φίλτρων',

View file

@ -15,6 +15,7 @@ const localeValues: Locale = {
Calendar,
global: {
placeholder: 'Please select',
close: 'Close',
},
Table: {
filterTitle: 'Filter menu',
@ -35,6 +36,11 @@ const localeValues: Locale = {
triggerAsc: 'Click to sort ascending',
cancelSort: 'Click to cancel sorting',
},
Tour: {
Next: 'Next',
Previous: 'Previous',
Finish: 'Finish',
},
Modal: {
okText: 'OK',
cancelText: 'Cancel',

View file

@ -15,6 +15,7 @@ const localeValues: Locale = {
Calendar,
global: {
placeholder: 'Please select',
close: 'Close',
},
Table: {
filterTitle: 'Filter menu',

View file

@ -15,6 +15,7 @@ const localeValues: Locale = {
Calendar,
global: {
placeholder: 'Seleccione',
close: 'Cerrar',
},
Table: {
filterTitle: 'Filtrar menú',

View file

@ -15,6 +15,7 @@ const localeValues: Locale = {
Calendar,
global: {
placeholder: 'Palun vali',
close: 'Sulge',
},
Table: {
filterTitle: 'Filtri menüü',
@ -35,6 +36,11 @@ const localeValues: Locale = {
triggerAsc: 'Klõpsa kasvavalt sortimiseks',
cancelSort: 'Klõpsa sortimise tühistamiseks',
},
Tour: {
Next: 'Järgmine',
Previous: 'Eelmine',
Finish: 'Lõpetada',
},
Modal: {
okText: 'OK',
cancelText: 'Tühista',

View file

@ -15,6 +15,7 @@ const localeValues: Locale = {
Calendar,
global: {
placeholder: 'Aukeratu',
close: 'Itxi',
},
Table: {
filterTitle: 'Iragazi menua',

View file

@ -15,6 +15,7 @@ const localeValues: Locale = {
Calendar,
global: {
placeholder: 'لطفاً انتخاب کنید',
close: 'بستن',
},
Table: {
filterTitle: 'منوی فیلتر',

View file

@ -11,6 +11,9 @@ const localeValues: Locale = {
DatePicker,
TimePicker,
Calendar,
global: {
close: 'Sulje',
},
Table: {
filterTitle: 'Suodatus valikko',
filterConfirm: 'OK',
@ -22,6 +25,11 @@ const localeValues: Locale = {
triggerAsc: 'Lajittele nousevasti',
cancelSort: 'Peruuta lajittelu',
},
Tour: {
Next: 'Seuraava',
Previous: 'Edellinen',
Finish: 'Valmis',
},
Modal: {
okText: 'OK',
cancelText: 'Peruuta',

View file

@ -13,6 +13,9 @@ const localeValues: Locale = {
DatePicker,
TimePicker,
Calendar,
global: {
close: 'Fermer',
},
Table: {
filterTitle: 'Filtrer',
filterConfirm: 'OK',

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