mirror of
https://github.com/ant-design/ant-design.git
synced 2025-06-07 17:44:35 +08:00
Merge branch 'master' into next-merge-master
This commit is contained in:
commit
ab4bcc4463
@ -110,7 +110,7 @@ timeline: true
|
|||||||
- 💄 修复 RTL 下 Input.Search 边框样式问题。[#37980](https://github.com/ant-design/ant-design/pull/37980) [@foryuki](https://github.com/foryuki)
|
- 💄 修复 RTL 下 Input.Search 边框样式问题。[#37980](https://github.com/ant-design/ant-design/pull/37980) [@foryuki](https://github.com/foryuki)
|
||||||
- 🐞 修复 AutoComplete 会报未使用的废弃属性 `dropdownClassName` 的问题。[#37974](https://github.com/ant-design/ant-design/pull/37974) [@heiyu4585](https://github.com/heiyu4585)
|
- 🐞 修复 AutoComplete 会报未使用的废弃属性 `dropdownClassName` 的问题。[#37974](https://github.com/ant-design/ant-design/pull/37974) [@heiyu4585](https://github.com/heiyu4585)
|
||||||
- 🐞 修复 Typography 省略算法在计算一些元素 fontSize 时为空字符串的情况[#37928](https://github.com/ant-design/ant-design/pull/37928) [@zheeeng](https://github.com/zheeeng)
|
- 🐞 修复 Typography 省略算法在计算一些元素 fontSize 时为空字符串的情况[#37928](https://github.com/ant-design/ant-design/pull/37928) [@zheeeng](https://github.com/zheeeng)
|
||||||
- 🐞 Fix Tabs 添加按钮在某些边界情况下无法展示的问题。[#37937](https://github.com/ant-design/ant-design/pull/37937)
|
- 🐞 修复 Tabs 添加按钮在某些边界情况下无法展示的问题。[#37937](https://github.com/ant-design/ant-design/pull/37937)
|
||||||
- 🐞 修复 RangePicker 在某些情况下面板会闪烁的问题。[#439](https://github.com/react-component/picker/pull/439)
|
- 🐞 修复 RangePicker 在某些情况下面板会闪烁的问题。[#439](https://github.com/react-component/picker/pull/439)
|
||||||
- 🛠 重构 Spin 为 Function Component。[#37969](https://github.com/ant-design/ant-design/pull/37969) [@li-jia-nan](https://github.com/li-jia-nan)
|
- 🛠 重构 Spin 为 Function Component。[#37969](https://github.com/ant-design/ant-design/pull/37969) [@li-jia-nan](https://github.com/li-jia-nan)
|
||||||
- 🛠 重构 Statistic.Countdown 为 Function Component.[#37938](https://github.com/ant-design/ant-design/pull/37938) [@li-jia-nan](https://github.com/li-jia-nan)
|
- 🛠 重构 Statistic.Countdown 为 Function Component.[#37938](https://github.com/ant-design/ant-design/pull/37938) [@li-jia-nan](https://github.com/li-jia-nan)
|
||||||
@ -1463,7 +1463,7 @@ timeline: true
|
|||||||
- Dropdown
|
- Dropdown
|
||||||
- 🐞 修复 Dropdown 带图标的菜单项禁用样式丢失的问题。[#29433](https://github.com/ant-design/ant-design/pull/29433)
|
- 🐞 修复 Dropdown 带图标的菜单项禁用样式丢失的问题。[#29433](https://github.com/ant-design/ant-design/pull/29433)
|
||||||
- 🐞 修复 Dropdown 内 Menu 不支持 `expandIcon` 的问题。[#29338](https://github.com/ant-design/ant-design/pull/29338)
|
- 🐞 修复 Dropdown 内 Menu 不支持 `expandIcon` 的问题。[#29338](https://github.com/ant-design/ant-design/pull/29338)
|
||||||
- 🐞 Fix 在本地开发时会报 tree-shaking 警告信息的问题。[#29378](https://github.com/ant-design/ant-design/pull/29378)
|
- 🐞 修复在本地开发时会报 tree-shaking 警告信息的问题。[#29378](https://github.com/ant-design/ant-design/pull/29378)
|
||||||
- 🇰🇷 修复 TimePicker 本地化。[#29540](https://github.com/ant-design/ant-design/pull/29540)
|
- 🇰🇷 修复 TimePicker 本地化。[#29540](https://github.com/ant-design/ant-design/pull/29540)
|
||||||
- TypeScript
|
- TypeScript
|
||||||
- 🤖 修复 Form.Item 的泛型定义问题。[#29397](https://github.com/ant-design/ant-design/pull/29397) [@mumiao](https://github.com/mumiao)
|
- 🤖 修复 Form.Item 的泛型定义问题。[#29397](https://github.com/ant-design/ant-design/pull/29397) [@mumiao](https://github.com/mumiao)
|
||||||
@ -1860,7 +1860,7 @@ timeline: true
|
|||||||
- Modal
|
- Modal
|
||||||
- 🆕 `modal.update()` 支持函数式更新。[#27163](https://github.com/ant-design/ant-design/pull/27163) [@Mongkii](https://github.com/Mongkii)
|
- 🆕 `modal.update()` 支持函数式更新。[#27163](https://github.com/ant-design/ant-design/pull/27163) [@Mongkii](https://github.com/Mongkii)
|
||||||
- 🆕 Modal method 增加 `bodyStyle` 属性。[#27292](https://github.com/ant-design/ant-design/pull/27292)
|
- 🆕 Modal method 增加 `bodyStyle` 属性。[#27292](https://github.com/ant-design/ant-design/pull/27292)
|
||||||
- 🐞 Fix Modal missing `modalRender` prop。[#27272](https://github.com/ant-design/ant-design/pull/27272) [@jieny](https://github.com/jieny)
|
- 🐞 修复 Modal 丢失 `modalRender` 属性 TS 定义。[#27272](https://github.com/ant-design/ant-design/pull/27272) [@jieny](https://github.com/jieny)
|
||||||
- 🐞 `Modal.config` 中设置的 `rootPrefixCls` 可以对 `title` 和 `content` 下使用的 antd 组件生效。[#27376](https://github.com/ant-design/ant-design/pull/27376) [@Chersquwn](https://github.com/Chersquwn)
|
- 🐞 `Modal.config` 中设置的 `rootPrefixCls` 可以对 `title` 和 `content` 下使用的 antd 组件生效。[#27376](https://github.com/ant-design/ant-design/pull/27376) [@Chersquwn](https://github.com/Chersquwn)
|
||||||
- Input
|
- Input
|
||||||
- 🆕 Input.Textarea 支持 `size` 属性。[#27110](https://github.com/ant-design/ant-design/pull/27110)
|
- 🆕 Input.Textarea 支持 `size` 属性。[#27110](https://github.com/ant-design/ant-design/pull/27110)
|
||||||
|
@ -2,9 +2,11 @@ import classNames from 'classnames';
|
|||||||
import toArray from 'rc-util/lib/Children/toArray';
|
import toArray from 'rc-util/lib/Children/toArray';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { ConfigContext } from '../config-provider';
|
import { ConfigContext } from '../config-provider';
|
||||||
|
import type { DropdownProps } from '../dropdown';
|
||||||
import Menu from '../menu';
|
import Menu from '../menu';
|
||||||
import { cloneElement } from '../_util/reactNode';
|
import { cloneElement } from '../_util/reactNode';
|
||||||
import warning from '../_util/warning';
|
import warning from '../_util/warning';
|
||||||
|
import type { BreadcrumbItemProps } from './BreadcrumbItem';
|
||||||
import BreadcrumbItem from './BreadcrumbItem';
|
import BreadcrumbItem from './BreadcrumbItem';
|
||||||
import BreadcrumbSeparator from './BreadcrumbSeparator';
|
import BreadcrumbSeparator from './BreadcrumbSeparator';
|
||||||
|
|
||||||
@ -52,7 +54,7 @@ function defaultItemRender(route: Route, params: any, routes: Route[], paths: st
|
|||||||
|
|
||||||
const getPath = (path: string, params: any) => {
|
const getPath = (path: string, params: any) => {
|
||||||
path = (path || '').replace(/^\//, '');
|
path = (path || '').replace(/^\//, '');
|
||||||
Object.keys(params).forEach(key => {
|
Object.keys(params).forEach((key) => {
|
||||||
path = path.replace(`:${key}`, params[key]);
|
path = path.replace(`:${key}`, params[key]);
|
||||||
});
|
});
|
||||||
return path;
|
return path;
|
||||||
@ -92,18 +94,18 @@ const Breadcrumb: BreadcrumbInterface = ({
|
|||||||
if (routes && routes.length > 0) {
|
if (routes && routes.length > 0) {
|
||||||
// generated by route
|
// generated by route
|
||||||
const paths: string[] = [];
|
const paths: string[] = [];
|
||||||
crumbs = routes.map(route => {
|
crumbs = routes.map((route) => {
|
||||||
const path = getPath(route.path, params);
|
const path = getPath(route.path, params);
|
||||||
|
|
||||||
if (path) {
|
if (path) {
|
||||||
paths.push(path);
|
paths.push(path);
|
||||||
}
|
}
|
||||||
// generated overlay by route.children
|
// generated overlay by route.children
|
||||||
let overlay;
|
let overlay: DropdownProps['overlay'];
|
||||||
if (route.children && route.children.length) {
|
if (route.children && route.children.length) {
|
||||||
overlay = (
|
overlay = (
|
||||||
<Menu
|
<Menu
|
||||||
items={route.children.map(child => ({
|
items={route.children.map((child) => ({
|
||||||
key: child.path || child.breadcrumbName,
|
key: child.path || child.breadcrumbName,
|
||||||
label: itemRender(child, params, routes, addChildPath(paths, child.path, params)),
|
label: itemRender(child, params, routes, addChildPath(paths, child.path, params)),
|
||||||
}))}
|
}))}
|
||||||
@ -111,8 +113,14 @@ const Breadcrumb: BreadcrumbInterface = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const itemProps: BreadcrumbItemProps = { separator };
|
||||||
|
|
||||||
|
if (overlay) {
|
||||||
|
itemProps.overlay = overlay;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BreadcrumbItem overlay={overlay} separator={separator} key={path || route.breadcrumbName}>
|
<BreadcrumbItem {...itemProps} key={path || route.breadcrumbName}>
|
||||||
{itemRender(route, params, routes, paths)}
|
{itemRender(route, params, routes, paths)}
|
||||||
</BreadcrumbItem>
|
</BreadcrumbItem>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import DownOutlined from '@ant-design/icons/DownOutlined';
|
import DownOutlined from '@ant-design/icons/DownOutlined';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import warning from '../_util/warning';
|
import warning from '../_util/warning';
|
||||||
import { ConfigContext } from '../config-provider';
|
import { ConfigContext } from '../config-provider';
|
||||||
import type { DropdownProps } from '../dropdown/dropdown';
|
import type { DropdownProps } from '../dropdown/dropdown';
|
||||||
@ -23,7 +22,7 @@ export interface BreadcrumbItemProps {
|
|||||||
interface BreadcrumbItemInterface extends React.FC<BreadcrumbItemProps> {
|
interface BreadcrumbItemInterface extends React.FC<BreadcrumbItemProps> {
|
||||||
__ANT_BREADCRUMB_ITEM: boolean;
|
__ANT_BREADCRUMB_ITEM: boolean;
|
||||||
}
|
}
|
||||||
const BreadcrumbItem: BreadcrumbItemInterface = props => {
|
const BreadcrumbItem: BreadcrumbItemInterface = (props) => {
|
||||||
const {
|
const {
|
||||||
prefixCls: customizePrefixCls,
|
prefixCls: customizePrefixCls,
|
||||||
separator = '/',
|
separator = '/',
|
||||||
|
@ -181,4 +181,24 @@ describe('Breadcrumb', () => {
|
|||||||
expect(container.querySelectorAll('.ant-breadcrumb-link')[1].textContent).toBe('0');
|
expect(container.querySelectorAll('.ant-breadcrumb-link')[1].textContent).toBe('0');
|
||||||
expect(container.firstChild).toMatchSnapshot();
|
expect(container.firstChild).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should console Error when `overlay` in props', () => {
|
||||||
|
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||||
|
render(
|
||||||
|
<Breadcrumb>
|
||||||
|
<Breadcrumb.Item overlay={<div>test</div>} />
|
||||||
|
</Breadcrumb>,
|
||||||
|
);
|
||||||
|
expect(errSpy).toHaveBeenCalledWith(
|
||||||
|
'Warning: [antd: Breadcrumb.Item] `overlay` is deprecated. Please use `menu` instead.',
|
||||||
|
);
|
||||||
|
errSpy.mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not console Error when `overlay` not in props', () => {
|
||||||
|
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||||
|
render(<Breadcrumb routes={[{ path: '/', breadcrumbName: 'Test' }]} />);
|
||||||
|
expect(errSpy).not.toHaveBeenCalled();
|
||||||
|
errSpy.mockRestore();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -131,7 +131,7 @@ describe('DropdownButton', () => {
|
|||||||
'ant-btn',
|
'ant-btn',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
it('should console Error then `overlay` in props', () => {
|
it('should console Error when `overlay` in props', () => {
|
||||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||||
render(<DropdownButton overlay={<div>test</div>} />);
|
render(<DropdownButton overlay={<div>test</div>} />);
|
||||||
expect(errSpy).toHaveBeenCalledWith(
|
expect(errSpy).toHaveBeenCalledWith(
|
||||||
@ -139,7 +139,7 @@ describe('DropdownButton', () => {
|
|||||||
);
|
);
|
||||||
errSpy.mockRestore();
|
errSpy.mockRestore();
|
||||||
});
|
});
|
||||||
it('should not console Error then `overlay` not in props', () => {
|
it('should not console Error when `overlay` not in props', () => {
|
||||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||||
render(<DropdownButton />);
|
render(<DropdownButton />);
|
||||||
expect(errSpy).not.toHaveBeenCalled();
|
expect(errSpy).not.toHaveBeenCalled();
|
||||||
|
@ -165,7 +165,7 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
|
|||||||
// >>>>> Collect noStyle Field error to the top FormItem
|
// >>>>> Collect noStyle Field error to the top FormItem
|
||||||
const onSubItemMetaChange = (subMeta: Meta & { destroy: boolean }, uniqueKeys: React.Key[]) => {
|
const onSubItemMetaChange = (subMeta: Meta & { destroy: boolean }, uniqueKeys: React.Key[]) => {
|
||||||
// Only `noStyle` sub item will trigger
|
// Only `noStyle` sub item will trigger
|
||||||
setSubFieldErrors(prevSubFieldErrors => {
|
setSubFieldErrors((prevSubFieldErrors) => {
|
||||||
const clone = {
|
const clone = {
|
||||||
...prevSubFieldErrors,
|
...prevSubFieldErrors,
|
||||||
};
|
};
|
||||||
@ -191,7 +191,7 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
|
|||||||
const errorList: string[] = [...meta.errors];
|
const errorList: string[] = [...meta.errors];
|
||||||
const warningList: string[] = [...meta.warnings];
|
const warningList: string[] = [...meta.warnings];
|
||||||
|
|
||||||
Object.values(subFieldErrors).forEach(subFieldError => {
|
Object.values(subFieldErrors).forEach((subFieldError) => {
|
||||||
errorList.push(...(subFieldError.errors || []));
|
errorList.push(...(subFieldError.errors || []));
|
||||||
warningList.push(...(subFieldError.warnings || []));
|
warningList.push(...(subFieldError.warnings || []));
|
||||||
});
|
});
|
||||||
@ -262,7 +262,7 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
|
|||||||
? required
|
? required
|
||||||
: !!(
|
: !!(
|
||||||
rules &&
|
rules &&
|
||||||
rules.some(rule => {
|
rules.some((rule) => {
|
||||||
if (rule && typeof rule === 'object' && rule.required && !rule.warningOnly) {
|
if (rule && typeof rule === 'object' && rule.required && !rule.warningOnly) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -284,13 +284,13 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
|
|||||||
warning(
|
warning(
|
||||||
!(shouldUpdate && dependencies),
|
!(shouldUpdate && dependencies),
|
||||||
'Form.Item',
|
'Form.Item',
|
||||||
"`shouldUpdate` and `dependencies` shouldn't be used together. See https://u.ant.design/#form-deps.",
|
"`shouldUpdate` and `dependencies` shouldn't be used together. See https://u.ant.design/form-deps.",
|
||||||
);
|
);
|
||||||
if (Array.isArray(children) && hasName) {
|
if (Array.isArray(children) && hasName) {
|
||||||
warning(
|
warning(
|
||||||
false,
|
false,
|
||||||
'Form.Item',
|
'Form.Item',
|
||||||
'A `Form.Item` with a `name` prop must have a single child element. For information on how to render more complex form items, see https://u.ant.design/#complex-form-item.',
|
'A `Form.Item` with a `name` prop must have a single child element. For information on how to render more complex form items, see https://u.ant.design/complex-form-item.',
|
||||||
);
|
);
|
||||||
childNode = children;
|
childNode = children;
|
||||||
} else if (isRenderProps && (!(shouldUpdate || dependencies) || hasName)) {
|
} else if (isRenderProps && (!(shouldUpdate || dependencies) || hasName)) {
|
||||||
@ -351,7 +351,7 @@ function InternalFormItem<Values = any>(props: FormItemProps<Values>): React.Rea
|
|||||||
...toArray(mergedValidateTrigger),
|
...toArray(mergedValidateTrigger),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
triggers.forEach(eventName => {
|
triggers.forEach((eventName) => {
|
||||||
childProps[eventName] = (...args: any[]) => {
|
childProps[eventName] = (...args: any[]) => {
|
||||||
mergedControl[eventName]?.(...args);
|
mergedControl[eventName]?.(...args);
|
||||||
children.props[eventName]?.(...args);
|
children.props[eventName]?.(...args);
|
||||||
|
@ -212,7 +212,7 @@ describe('Form', () => {
|
|||||||
</Form>,
|
</Form>,
|
||||||
);
|
);
|
||||||
expect(errorSpy).toHaveBeenCalledWith(
|
expect(errorSpy).toHaveBeenCalledWith(
|
||||||
"Warning: [antd: Form.Item] `shouldUpdate` and `dependencies` shouldn't be used together. See https://u.ant.design/#form-deps.",
|
"Warning: [antd: Form.Item] `shouldUpdate` and `dependencies` shouldn't be used together. See https://u.ant.design/form-deps.",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ describe('Form', () => {
|
|||||||
</Form>,
|
</Form>,
|
||||||
);
|
);
|
||||||
expect(errorSpy).toHaveBeenCalledWith(
|
expect(errorSpy).toHaveBeenCalledWith(
|
||||||
'Warning: [antd: Form.Item] A `Form.Item` with a `name` prop must have a single child element. For information on how to render more complex form items, see https://u.ant.design/#complex-form-item.',
|
'Warning: [antd: Form.Item] A `Form.Item` with a `name` prop must have a single child element. For information on how to render more complex form items, see https://u.ant.design/complex-form-item.',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1494,7 +1494,7 @@ describe('Form', () => {
|
|||||||
it('item customize margin', async () => {
|
it('item customize margin', async () => {
|
||||||
const computeSpy = jest
|
const computeSpy = jest
|
||||||
.spyOn(window, 'getComputedStyle')
|
.spyOn(window, 'getComputedStyle')
|
||||||
.mockImplementation(() => ({ marginBottom: 24 } as unknown as CSSStyleDeclaration));
|
.mockImplementation(() => ({ marginBottom: 24 }) as unknown as CSSStyleDeclaration);
|
||||||
|
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<Form>
|
<Form>
|
||||||
|
@ -62,7 +62,7 @@ const Search = React.forwardRef<InputRef, SearchProps>((props, ref) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMouseDown: React.MouseEventHandler<HTMLElement> = e => {
|
const onMouseDown: React.MouseEventHandler<HTMLElement> = (e) => {
|
||||||
if (document.activeElement === inputRef.current?.input) {
|
if (document.activeElement === inputRef.current?.input) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
@ -75,7 +75,7 @@ const Search = React.forwardRef<InputRef, SearchProps>((props, ref) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onPressEnter = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
const onPressEnter = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
if (composedRef.current) {
|
if (composedRef.current || loading) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onSearch(e);
|
onSearch(e);
|
||||||
@ -140,12 +140,12 @@ const Search = React.forwardRef<InputRef, SearchProps>((props, ref) => {
|
|||||||
className,
|
className,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOnCompositionStart: React.CompositionEventHandler<HTMLInputElement> = e => {
|
const handleOnCompositionStart: React.CompositionEventHandler<HTMLInputElement> = (e) => {
|
||||||
composedRef.current = true;
|
composedRef.current = true;
|
||||||
onCompositionStart?.(e);
|
onCompositionStart?.(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnCompositionEnd: React.CompositionEventHandler<HTMLInputElement> = e => {
|
const handleOnCompositionEnd: React.CompositionEventHandler<HTMLInputElement> = (e) => {
|
||||||
composedRef.current = false;
|
composedRef.current = false;
|
||||||
onCompositionEnd?.(e);
|
onCompositionEnd?.(e);
|
||||||
};
|
};
|
||||||
|
@ -161,6 +161,13 @@ describe('Input.Search', () => {
|
|||||||
expect(asFragmentWithEnterButton().firstChild).toMatchSnapshot();
|
expect(asFragmentWithEnterButton().firstChild).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not trigger onSearch when press enter while loading', () => {
|
||||||
|
const onSearch = jest.fn();
|
||||||
|
const { container } = render(<Search loading onSearch={onSearch} />);
|
||||||
|
fireEvent.keyDown(container.querySelector('input')!, { key: 'Enter', keyCode: 13 });
|
||||||
|
expect(onSearch).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('should support addonAfter and suffix for loading', () => {
|
it('should support addonAfter and suffix for loading', () => {
|
||||||
const { asFragment } = render(<Search loading suffix="suffix" addonAfter="addonAfter" />);
|
const { asFragment } = render(<Search loading suffix="suffix" addonAfter="addonAfter" />);
|
||||||
const { asFragment: asFragmentWithEnterButton } = render(
|
const { asFragment: asFragmentWithEnterButton } = render(
|
||||||
|
@ -420,7 +420,7 @@ exports[`renders ./components/layout/demo/fixed.tsx extend context correctly 1`]
|
|||||||
>
|
>
|
||||||
<header
|
<header
|
||||||
class="ant-layout-header"
|
class="ant-layout-header"
|
||||||
style="position:fixed;z-index:1;width:100%"
|
style="position:sticky;top:0;z-index:1;width:100%"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="logo"
|
class="logo"
|
||||||
@ -656,7 +656,7 @@ exports[`renders ./components/layout/demo/fixed.tsx extend context correctly 1`]
|
|||||||
</header>
|
</header>
|
||||||
<main
|
<main
|
||||||
class="ant-layout-content site-layout"
|
class="ant-layout-content site-layout"
|
||||||
style="padding:0 50px;margin-top:64px"
|
style="padding:0 50px"
|
||||||
>
|
>
|
||||||
<nav
|
<nav
|
||||||
class="ant-breadcrumb"
|
class="ant-breadcrumb"
|
||||||
|
@ -287,7 +287,7 @@ exports[`renders ./components/layout/demo/fixed.tsx correctly 1`] = `
|
|||||||
>
|
>
|
||||||
<header
|
<header
|
||||||
class="ant-layout-header"
|
class="ant-layout-header"
|
||||||
style="position:fixed;z-index:1;width:100%"
|
style="position:sticky;top:0;z-index:1;width:100%"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="logo"
|
class="logo"
|
||||||
@ -379,7 +379,7 @@ exports[`renders ./components/layout/demo/fixed.tsx correctly 1`] = `
|
|||||||
</header>
|
</header>
|
||||||
<main
|
<main
|
||||||
class="ant-layout-content site-layout"
|
class="ant-layout-content site-layout"
|
||||||
style="padding:0 50px;margin-top:64px"
|
style="padding:0 50px"
|
||||||
>
|
>
|
||||||
<nav
|
<nav
|
||||||
class="ant-breadcrumb"
|
class="ant-breadcrumb"
|
||||||
|
@ -5,7 +5,7 @@ const { Header, Content, Footer } = Layout;
|
|||||||
|
|
||||||
const App: React.FC = () => (
|
const App: React.FC = () => (
|
||||||
<Layout>
|
<Layout>
|
||||||
<Header style={{ position: 'fixed', zIndex: 1, width: '100%' }}>
|
<Header style={{ position: 'sticky', top: 0, zIndex: 1, width: '100%' }}>
|
||||||
<div className="logo" />
|
<div className="logo" />
|
||||||
<Menu
|
<Menu
|
||||||
theme="dark"
|
theme="dark"
|
||||||
|
@ -11,7 +11,9 @@ import warning from '../_util/warning';
|
|||||||
import { renderCloseIcon, renderFooter } from './PurePanel';
|
import { renderCloseIcon, renderFooter } from './PurePanel';
|
||||||
import useStyle from './style';
|
import useStyle from './style';
|
||||||
|
|
||||||
let mousePosition: { x: number; y: number } | null;
|
type MousePosition = { x: number; y: number } | null;
|
||||||
|
|
||||||
|
let mousePosition: MousePosition;
|
||||||
|
|
||||||
// ref: https://github.com/ant-design/ant-design/issues/15795
|
// ref: https://github.com/ant-design/ant-design/issues/15795
|
||||||
const getClickPosition = (e: MouseEvent) => {
|
const getClickPosition = (e: MouseEvent) => {
|
||||||
@ -82,6 +84,7 @@ export interface ModalProps {
|
|||||||
modalRender?: (node: React.ReactNode) => React.ReactNode;
|
modalRender?: (node: React.ReactNode) => React.ReactNode;
|
||||||
focusTriggerAfterClose?: boolean;
|
focusTriggerAfterClose?: boolean;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
|
mousePosition?: MousePosition;
|
||||||
|
|
||||||
// Legacy
|
// Legacy
|
||||||
/** @deprecated Please use `open` instead. */
|
/** @deprecated Please use `open` instead. */
|
||||||
@ -137,7 +140,7 @@ export interface ModalLocale {
|
|||||||
justOkText: string;
|
justOkText: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Modal: React.FC<ModalProps> = props => {
|
const Modal: React.FC<ModalProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
getPopupContainer: getContextPopupContainer,
|
getPopupContainer: getContextPopupContainer,
|
||||||
getPrefixCls,
|
getPrefixCls,
|
||||||
@ -208,7 +211,7 @@ const Modal: React.FC<ModalProps> = props => {
|
|||||||
onCancel: handleCancel,
|
onCancel: handleCancel,
|
||||||
})}
|
})}
|
||||||
visible={open ?? visible}
|
visible={open ?? visible}
|
||||||
mousePosition={mousePosition}
|
mousePosition={restProps.mousePosition ?? mousePosition}
|
||||||
onClose={handleCancel}
|
onClose={handleCancel}
|
||||||
closeIcon={renderCloseIcon(prefixCls, closeIcon)}
|
closeIcon={renderCloseIcon(prefixCls, closeIcon)}
|
||||||
focusTriggerAfterClose={focusTriggerAfterClose}
|
focusTriggerAfterClose={focusTriggerAfterClose}
|
||||||
|
@ -92,6 +92,25 @@ describe('Modal', () => {
|
|||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('custom mouse position', () => {
|
||||||
|
const Demo = () => {
|
||||||
|
const containerRef = React.useRef<HTMLDivElement>(null);
|
||||||
|
return (
|
||||||
|
<div ref={containerRef}>
|
||||||
|
<Modal
|
||||||
|
open
|
||||||
|
getContainer={() => containerRef.current!}
|
||||||
|
mousePosition={{ x: 100, y: 100 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const { container } = render(<Demo />);
|
||||||
|
expect(
|
||||||
|
(container.querySelectorAll('.ant-modal')[0] as HTMLDivElement).style.transformOrigin,
|
||||||
|
).toBe('100px 100px');
|
||||||
|
});
|
||||||
|
|
||||||
it('deprecated warning', () => {
|
it('deprecated warning', () => {
|
||||||
resetWarned();
|
resetWarned();
|
||||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||||
|
7
components/modal/demo/custom-mouse-position.md
Normal file
7
components/modal/demo/custom-mouse-position.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
## zh-CN
|
||||||
|
|
||||||
|
通过 `mousePosition` 控制弹框动画原点.
|
||||||
|
|
||||||
|
## en-US
|
||||||
|
|
||||||
|
pass `mousePosition` to control modal's animation origin position
|
39
components/modal/demo/custom-mouse-position.tsx
Normal file
39
components/modal/demo/custom-mouse-position.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { Button, Modal } from 'antd';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
const App: React.FC = () => {
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
|
||||||
|
const showModal = () => {
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOk = () => {
|
||||||
|
setIsModalOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
setIsModalOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button type="primary" onClick={showModal}>
|
||||||
|
Open Modal
|
||||||
|
</Button>
|
||||||
|
<Modal
|
||||||
|
title="Basic Modal"
|
||||||
|
open={isModalOpen}
|
||||||
|
onOk={handleOk}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
mousePosition={{ x: 300, y: 300 }}
|
||||||
|
>
|
||||||
|
<p>Some contents...</p>
|
||||||
|
<p>Some contents...</p>
|
||||||
|
<p>Some contents...</p>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
@ -29,41 +29,42 @@ When requiring users to interact with the application, but without jumping to a
|
|||||||
<code src="./demo/hooks.tsx">Use hooks to get context</code>
|
<code src="./demo/hooks.tsx">Use hooks to get context</code>
|
||||||
<code src="./demo/modal-render.tsx">Custom modal content render</code>
|
<code src="./demo/modal-render.tsx">Custom modal content render</code>
|
||||||
<code src="./demo/width.tsx">To customize the width of modal</code>
|
<code src="./demo/width.tsx">To customize the width of modal</code>
|
||||||
<code src="./demo/render-panel.tsx" debug>_InternalPanelDoNotUseOrYouWillBeFired</code>
|
<code src="./demo/render-panel.tsx" debug>\_InternalPanelDoNotUseOrYouWillBeFired</code>
|
||||||
|
<code src="./demo/custom-mouse-position.tsx" debug>控制弹框动画原点</code>
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
| Property | Description | Type | Default | Version |
|
| Property | Description | Type | Default | Version |
|
||||||
| --- | --- | --- | --- | --- |
|
| ---------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | ----------------------- | ------- |
|
||||||
| afterClose | Specify a function that will be called when modal is closed completely | function | - | |
|
| afterClose | Specify a function that will be called when modal is closed completely | function | - | |
|
||||||
| bodyStyle | Body style for modal body element. Such as height, padding etc | CSSProperties | | |
|
| bodyStyle | Body style for modal body element. Such as height, padding etc | CSSProperties | | |
|
||||||
| cancelButtonProps | The cancel button props | [ButtonProps](/components/button/#API) | - | |
|
| cancelButtonProps | The cancel button props | [ButtonProps](/components/button/#API) | - | |
|
||||||
| cancelText | Text of the Cancel button | ReactNode | `Cancel` | |
|
| cancelText | Text of the Cancel button | ReactNode | `Cancel` | |
|
||||||
| centered | Centered Modal | boolean | false | |
|
| centered | Centered Modal | boolean | false | |
|
||||||
| closable | Whether a close (x) button is visible on top right of the modal dialog or not | boolean | true | |
|
| closable | Whether a close (x) button is visible on top right of the modal dialog or not | boolean | true | |
|
||||||
| closeIcon | Custom close icon | ReactNode | <CloseOutlined /> | |
|
| closeIcon | Custom close icon | ReactNode | <CloseOutlined /> | |
|
||||||
| confirmLoading | Whether to apply loading visual effect for OK button or not | boolean | false | |
|
| confirmLoading | Whether to apply loading visual effect for OK button or not | boolean | false | |
|
||||||
| destroyOnClose | Whether to unmount child components on onClose | boolean | false | |
|
| destroyOnClose | Whether to unmount child components on onClose | boolean | false | |
|
||||||
| focusTriggerAfterClose | Whether need to focus trigger element after dialog is closed | boolean | true | 4.9.0 |
|
| focusTriggerAfterClose | Whether need to focus trigger element after dialog is closed | boolean | true | 4.9.0 |
|
||||||
| footer | Footer content, set as `footer={null}` when you don't need default buttons | ReactNode | (OK and Cancel buttons) | |
|
| footer | Footer content, set as `footer={null}` when you don't need default buttons | ReactNode | (OK and Cancel buttons) | |
|
||||||
| forceRender | Force render Modal | boolean | false | |
|
| forceRender | Force render Modal | boolean | false | |
|
||||||
| getContainer | The mounted node for Modal but still display at fullscreen | HTMLElement \| () => HTMLElement \| Selectors \| false | document.body | |
|
| getContainer | The mounted node for Modal but still display at fullscreen | HTMLElement \| () => HTMLElement \| Selectors \| false | document.body | |
|
||||||
| keyboard | Whether support press esc to close | boolean | true | |
|
| keyboard | Whether support press esc to close | boolean | true | |
|
||||||
| mask | Whether show mask or not | boolean | true | |
|
| mask | Whether show mask or not | boolean | true | |
|
||||||
| maskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | boolean | true | |
|
| maskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | boolean | true | |
|
||||||
| maskStyle | Style for modal's mask element | CSSProperties | | |
|
| maskStyle | Style for modal's mask element | CSSProperties | | |
|
||||||
| modalRender | Custom modal content render | (node: ReactNode) => ReactNode | - | 4.7.0 |
|
| modalRender | Custom modal content render | (node: ReactNode) => ReactNode | - | 4.7.0 |
|
||||||
| okButtonProps | The ok button props | [ButtonProps](/components/button/#API) | - | |
|
| okButtonProps | The ok button props | [ButtonProps](/components/button/#API) | - | |
|
||||||
| okText | Text of the OK button | ReactNode | `OK` | |
|
| okText | Text of the OK button | ReactNode | `OK` | |
|
||||||
| okType | Button `type` of the OK button | string | `primary` | |
|
| okType | Button `type` of the OK button | string | `primary` | |
|
||||||
| style | Style of floating layer, typically used at least for adjusting the position | CSSProperties | - | |
|
| style | Style of floating layer, typically used at least for adjusting the position | CSSProperties | - | |
|
||||||
| title | The modal dialog's title | ReactNode | - | |
|
| title | The modal dialog's title | ReactNode | - | |
|
||||||
| open | Whether the modal dialog is visible or not | boolean | false | |
|
| open | Whether the modal dialog is visible or not | boolean | false | |
|
||||||
| width | Width of the modal dialog | string \| number | 520 | |
|
| width | Width of the modal dialog | string \| number | 520 | |
|
||||||
| wrapClassName | The class name of the container of the modal dialog | string | - | |
|
| wrapClassName | The class name of the container of the modal dialog | string | - | |
|
||||||
| zIndex | The `z-index` of the Modal | number | 1000 | |
|
| zIndex | The `z-index` of the Modal | number | 1000 | |
|
||||||
| onCancel | Specify a function that will be called when a user clicks mask, close button on top right or Cancel button | function(e) | - | |
|
| onCancel | Specify a function that will be called when a user clicks mask, close button on top right or Cancel button | function(e) | - | |
|
||||||
| onOk | Specify a function that will be called when a user clicks the OK button | function(e) | - | |
|
| onOk | Specify a function that will be called when a user clicks the OK button | function(e) | - | |
|
||||||
|
|
||||||
#### Note
|
#### Note
|
||||||
|
|
||||||
@ -83,34 +84,34 @@ There are five ways to display the information based on the content's nature:
|
|||||||
|
|
||||||
The items listed above are all functions, expecting a settings object as parameter. The properties of the object are follows:
|
The items listed above are all functions, expecting a settings object as parameter. The properties of the object are follows:
|
||||||
|
|
||||||
| Property | Description | Type | Default | Version |
|
| Property | Description | Type | Default | Version |
|
||||||
| --- | --- | --- | --- | --- |
|
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | --------------------- | ------- |
|
||||||
| afterClose | Specify a function that will be called when modal is closed completely | function | - | 4.9.0 |
|
| afterClose | Specify a function that will be called when modal is closed completely | function | - | 4.9.0 |
|
||||||
| autoFocusButton | Specify which button to autofocus | null \| `ok` \| `cancel` | `ok` | |
|
| autoFocusButton | Specify which button to autofocus | null \| `ok` \| `cancel` | `ok` | |
|
||||||
| bodyStyle | Body style for modal body element. Such as height, padding etc | CSSProperties | | 4.8.0 |
|
| bodyStyle | Body style for modal body element. Such as height, padding etc | CSSProperties | | 4.8.0 |
|
||||||
| cancelButtonProps | The cancel button props | [ButtonProps](/components/button/#API) | - | |
|
| cancelButtonProps | The cancel button props | [ButtonProps](/components/button/#API) | - | |
|
||||||
| cancelText | Text of the Cancel button with Modal.confirm | string | `Cancel` | |
|
| cancelText | Text of the Cancel button with Modal.confirm | string | `Cancel` | |
|
||||||
| centered | Centered Modal | boolean | false | |
|
| centered | Centered Modal | boolean | false | |
|
||||||
| className | The className of container | string | - | |
|
| className | The className of container | string | - | |
|
||||||
| closable | Whether a close (x) button is visible on top right of the confirm dialog or not | boolean | false | 4.9.0 |
|
| closable | Whether a close (x) button is visible on top right of the confirm dialog or not | boolean | false | 4.9.0 |
|
||||||
| closeIcon | Custom close icon | ReactNode | undefined | 4.9.0 |
|
| closeIcon | Custom close icon | ReactNode | undefined | 4.9.0 |
|
||||||
| content | Content | ReactNode | - | |
|
| content | Content | ReactNode | - | |
|
||||||
| getContainer | Return the mount node for Modal | HTMLElement \| () => HTMLElement \| Selectors \| false | document.body | |
|
| getContainer | Return the mount node for Modal | HTMLElement \| () => HTMLElement \| Selectors \| false | document.body | |
|
||||||
| icon | Custom icon | ReactNode | <QuestionCircle /> | |
|
| icon | Custom icon | ReactNode | <QuestionCircle /> | |
|
||||||
| keyboard | Whether support press esc to close | boolean | true | |
|
| keyboard | Whether support press esc to close | boolean | true | |
|
||||||
| mask | Whether show mask or not. | boolean | true | |
|
| mask | Whether show mask or not. | boolean | true | |
|
||||||
| maskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | boolean | false | |
|
| maskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | boolean | false | |
|
||||||
| maskStyle | Style for modal's mask element | object | {} | |
|
| maskStyle | Style for modal's mask element | object | {} | |
|
||||||
| okButtonProps | The ok button props | [ButtonProps](/components/button/#API) | - | |
|
| okButtonProps | The ok button props | [ButtonProps](/components/button/#API) | - | |
|
||||||
| okText | Text of the OK button | string | `OK` | |
|
| okText | Text of the OK button | string | `OK` | |
|
||||||
| okType | Button `type` of the OK button | string | `primary` | |
|
| okType | Button `type` of the OK button | string | `primary` | |
|
||||||
| style | Style of floating layer, typically used at least for adjusting the position | CSSProperties | - | |
|
| style | Style of floating layer, typically used at least for adjusting the position | CSSProperties | - | |
|
||||||
| title | Title | ReactNode | - | |
|
| title | Title | ReactNode | - | |
|
||||||
| width | Width of the modal dialog | string \| number | 416 | |
|
| width | Width of the modal dialog | string \| number | 416 | |
|
||||||
| wrapClassName | The class name of the container of the modal dialog | string | - | 4.18.0 |
|
| wrapClassName | The class name of the container of the modal dialog | string | - | 4.18.0 |
|
||||||
| zIndex | The `z-index` of the Modal | number | 1000 | |
|
| zIndex | The `z-index` of the Modal | number | 1000 | |
|
||||||
| onCancel | Specify a function that will be called when the user clicks the Cancel button. The parameter of this function is a function whose execution should include closing the dialog. If the function does not take any parameter (`!onCancel.length`) then modal dialog will be closed unless returned value is `true` (`!!onCancel()`). You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function(close) | - | |
|
| onCancel | Specify a function that will be called when the user clicks the Cancel button. The parameter of this function is a function whose execution should include closing the dialog. If the function does not take any parameter (`!onCancel.length`) then modal dialog will be closed unless returned value is `true` (`!!onCancel()`). You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function(close) | - | |
|
||||||
| onOk | Specify a function that will be called when the user clicks the OK button. The parameter of this function is a function whose execution should include closing the dialog. If the function does not take any parameter (`!onOk.length`) then modal dialog will be closed unless returned value is `true` (`!!onOk()`). You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function(close) | - | |
|
| onOk | Specify a function that will be called when the user clicks the OK button. The parameter of this function is a function whose execution should include closing the dialog. If the function does not take any parameter (`!onOk.length`) then modal dialog will be closed unless returned value is `true` (`!!onOk()`). You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function(close) | - | |
|
||||||
|
|
||||||
All the `Modal.method`s will return a reference, and then we can update and close the modal dialog by the reference.
|
All the `Modal.method`s will return a reference, and then we can update and close the modal dialog by the reference.
|
||||||
|
|
||||||
|
@ -32,41 +32,42 @@ demo:
|
|||||||
<code src="./demo/hooks.tsx">使用 hooks 获得上下文</code>
|
<code src="./demo/hooks.tsx">使用 hooks 获得上下文</code>
|
||||||
<code src="./demo/modal-render.tsx">自定义渲染对话框</code>
|
<code src="./demo/modal-render.tsx">自定义渲染对话框</code>
|
||||||
<code src="./demo/width.tsx">自定义模态的宽度</code>
|
<code src="./demo/width.tsx">自定义模态的宽度</code>
|
||||||
<code src="./demo/render-panel.tsx" debug>_InternalPanelDoNotUseOrYouWillBeFired</code>
|
<code src="./demo/render-panel.tsx" debug>\_InternalPanelDoNotUseOrYouWillBeFired</code>
|
||||||
|
<code src="./demo/custom-mouse-position.tsx" debug>control modal's animation origin position</code>
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||||
| --- | --- | --- | --- | --- |
|
| ---------------------- | ----------------------------------------------------------------- | ------------------------------------------------------ | -------------------- | ----- |
|
||||||
| afterClose | Modal 完全关闭后的回调 | function | - | |
|
| afterClose | Modal 完全关闭后的回调 | function | - | |
|
||||||
| bodyStyle | Modal body 样式 | CSSProperties | | |
|
| bodyStyle | Modal body 样式 | CSSProperties | | |
|
||||||
| cancelButtonProps | cancel 按钮 props | [ButtonProps](/components/button/#API) | - | |
|
| cancelButtonProps | cancel 按钮 props | [ButtonProps](/components/button/#API) | - | |
|
||||||
| cancelText | 取消按钮文字 | ReactNode | `取消` | |
|
| cancelText | 取消按钮文字 | ReactNode | `取消` | |
|
||||||
| centered | 垂直居中展示 Modal | boolean | false | |
|
| centered | 垂直居中展示 Modal | boolean | false | |
|
||||||
| closable | 是否显示右上角的关闭按钮 | boolean | true | |
|
| closable | 是否显示右上角的关闭按钮 | boolean | true | |
|
||||||
| closeIcon | 自定义关闭图标 | ReactNode | <CloseOutlined /> | |
|
| closeIcon | 自定义关闭图标 | ReactNode | <CloseOutlined /> | |
|
||||||
| confirmLoading | 确定按钮 loading | boolean | false | |
|
| confirmLoading | 确定按钮 loading | boolean | false | |
|
||||||
| destroyOnClose | 关闭时销毁 Modal 里的子元素 | boolean | false | |
|
| destroyOnClose | 关闭时销毁 Modal 里的子元素 | boolean | false | |
|
||||||
| focusTriggerAfterClose | 对话框关闭后是否需要聚焦触发元素 | boolean | true | 4.9.0 |
|
| focusTriggerAfterClose | 对话框关闭后是否需要聚焦触发元素 | boolean | true | 4.9.0 |
|
||||||
| footer | 底部内容,当不需要默认底部按钮时,可以设为 `footer={null}` | ReactNode | (确定取消按钮) | |
|
| footer | 底部内容,当不需要默认底部按钮时,可以设为 `footer={null}` | ReactNode | (确定取消按钮) | |
|
||||||
| forceRender | 强制渲染 Modal | boolean | false | |
|
| forceRender | 强制渲染 Modal | boolean | false | |
|
||||||
| getContainer | 指定 Modal 挂载的节点,但依旧为全局展示,`false` 为挂载在当前位置 | HTMLElement \| () => HTMLElement \| Selectors \| false | document.body | |
|
| getContainer | 指定 Modal 挂载的节点,但依旧为全局展示,`false` 为挂载在当前位置 | HTMLElement \| () => HTMLElement \| Selectors \| false | document.body | |
|
||||||
| keyboard | 是否支持键盘 esc 关闭 | boolean | true | |
|
| keyboard | 是否支持键盘 esc 关闭 | boolean | true | |
|
||||||
| mask | 是否展示遮罩 | boolean | true | |
|
| mask | 是否展示遮罩 | boolean | true | |
|
||||||
| maskClosable | 点击蒙层是否允许关闭 | boolean | true | |
|
| maskClosable | 点击蒙层是否允许关闭 | boolean | true | |
|
||||||
| maskStyle | 遮罩样式 | CSSProperties | | |
|
| maskStyle | 遮罩样式 | CSSProperties | | |
|
||||||
| modalRender | 自定义渲染对话框 | (node: ReactNode) => ReactNode | - | 4.7.0 |
|
| modalRender | 自定义渲染对话框 | (node: ReactNode) => ReactNode | - | 4.7.0 |
|
||||||
| okButtonProps | ok 按钮 props | [ButtonProps](/components/button/#API) | - | |
|
| okButtonProps | ok 按钮 props | [ButtonProps](/components/button/#API) | - | |
|
||||||
| okText | 确认按钮文字 | ReactNode | `确定` | |
|
| okText | 确认按钮文字 | ReactNode | `确定` | |
|
||||||
| okType | 确认按钮类型 | string | `primary` | |
|
| okType | 确认按钮类型 | string | `primary` | |
|
||||||
| style | 可用于设置浮层的样式,调整浮层位置等 | CSSProperties | - | |
|
| style | 可用于设置浮层的样式,调整浮层位置等 | CSSProperties | - | |
|
||||||
| title | 标题 | ReactNode | - | |
|
| title | 标题 | ReactNode | - | |
|
||||||
| open | 对话框是否可见 | boolean | - | |
|
| open | 对话框是否可见 | boolean | - | |
|
||||||
| width | 宽度 | string \| number | 520 | |
|
| width | 宽度 | string \| number | 520 | |
|
||||||
| wrapClassName | 对话框外层容器的类名 | string | - | |
|
| wrapClassName | 对话框外层容器的类名 | string | - | |
|
||||||
| zIndex | 设置 Modal 的 `z-index` | number | 1000 | |
|
| zIndex | 设置 Modal 的 `z-index` | number | 1000 | |
|
||||||
| onCancel | 点击遮罩层或右上角叉或取消按钮的回调 | function(e) | - | |
|
| onCancel | 点击遮罩层或右上角叉或取消按钮的回调 | function(e) | - | |
|
||||||
| onOk | 点击确定回调 | function(e) | - | |
|
| onOk | 点击确定回调 | function(e) | - | |
|
||||||
|
|
||||||
#### 注意
|
#### 注意
|
||||||
|
|
||||||
@ -86,34 +87,34 @@ demo:
|
|||||||
|
|
||||||
以上均为一个函数,参数为 object,具体属性如下:
|
以上均为一个函数,参数为 object,具体属性如下:
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||||
| --- | --- | --- | --- | --- |
|
| ----------------- | ---------------------------------------------------------------- | ------------------------------------------------------ | --------------------- | ------ |
|
||||||
| afterClose | Modal 完全关闭后的回调 | function | - | 4.9.0 |
|
| afterClose | Modal 完全关闭后的回调 | function | - | 4.9.0 |
|
||||||
| autoFocusButton | 指定自动获得焦点的按钮 | null \| `ok` \| `cancel` | `ok` | |
|
| autoFocusButton | 指定自动获得焦点的按钮 | null \| `ok` \| `cancel` | `ok` | |
|
||||||
| bodyStyle | Modal body 样式 | CSSProperties | | 4.8.0 |
|
| bodyStyle | Modal body 样式 | CSSProperties | | 4.8.0 |
|
||||||
| cancelButtonProps | cancel 按钮 props | [ButtonProps](/components/button/#API) | - | |
|
| cancelButtonProps | cancel 按钮 props | [ButtonProps](/components/button/#API) | - | |
|
||||||
| cancelText | 设置 Modal.confirm 取消按钮文字 | string | `取消` | |
|
| cancelText | 设置 Modal.confirm 取消按钮文字 | string | `取消` | |
|
||||||
| centered | 垂直居中展示 Modal | boolean | false | |
|
| centered | 垂直居中展示 Modal | boolean | false | |
|
||||||
| className | 容器类名 | string | - | |
|
| className | 容器类名 | string | - | |
|
||||||
| closable | 是否显示右上角的关闭按钮 | boolean | false | 4.9.0 |
|
| closable | 是否显示右上角的关闭按钮 | boolean | false | 4.9.0 |
|
||||||
| closeIcon | 自定义关闭图标 | ReactNode | undefined | 4.9.0 |
|
| closeIcon | 自定义关闭图标 | ReactNode | undefined | 4.9.0 |
|
||||||
| content | 内容 | ReactNode | - | |
|
| content | 内容 | ReactNode | - | |
|
||||||
| getContainer | 指定 Modal 挂载的 HTML 节点, false 为挂载在当前 dom | HTMLElement \| () => HTMLElement \| Selectors \| false | document.body | |
|
| getContainer | 指定 Modal 挂载的 HTML 节点, false 为挂载在当前 dom | HTMLElement \| () => HTMLElement \| Selectors \| false | document.body | |
|
||||||
| icon | 自定义图标 | ReactNode | <QuestionCircle /> | |
|
| icon | 自定义图标 | ReactNode | <QuestionCircle /> | |
|
||||||
| keyboard | 是否支持键盘 esc 关闭 | boolean | true | |
|
| keyboard | 是否支持键盘 esc 关闭 | boolean | true | |
|
||||||
| mask | 是否展示遮罩 | boolean | true | |
|
| mask | 是否展示遮罩 | boolean | true | |
|
||||||
| maskClosable | 点击蒙层是否允许关闭 | boolean | false | |
|
| maskClosable | 点击蒙层是否允许关闭 | boolean | false | |
|
||||||
| maskStyle | 遮罩样式 | object | {} | |
|
| maskStyle | 遮罩样式 | object | {} | |
|
||||||
| okButtonProps | ok 按钮 props | [ButtonProps](/components/button/#API) | - | |
|
| okButtonProps | ok 按钮 props | [ButtonProps](/components/button/#API) | - | |
|
||||||
| okText | 确认按钮文字 | string | `确定` | |
|
| okText | 确认按钮文字 | string | `确定` | |
|
||||||
| okType | 确认按钮类型 | string | `primary` | |
|
| okType | 确认按钮类型 | string | `primary` | |
|
||||||
| style | 可用于设置浮层的样式,调整浮层位置等 | CSSProperties | - | |
|
| style | 可用于设置浮层的样式,调整浮层位置等 | CSSProperties | - | |
|
||||||
| title | 标题 | ReactNode | - | |
|
| title | 标题 | ReactNode | - | |
|
||||||
| width | 宽度 | string \| number | 416 | |
|
| width | 宽度 | string \| number | 416 | |
|
||||||
| wrapClassName | 对话框外层容器的类名 | string | - | 4.18.0 |
|
| wrapClassName | 对话框外层容器的类名 | string | - | 4.18.0 |
|
||||||
| zIndex | 设置 Modal 的 `z-index` | number | 1000 | |
|
| zIndex | 设置 Modal 的 `z-index` | number | 1000 | |
|
||||||
| onCancel | 取消回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function(close) | - | |
|
| onCancel | 取消回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function(close) | - | |
|
||||||
| onOk | 点击确定回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function(close) | - | |
|
| onOk | 点击确定回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function(close) | - | |
|
||||||
|
|
||||||
以上函数调用后,会返回一个引用,可以通过该引用更新和关闭弹窗。
|
以上函数调用后,会返回一个引用,可以通过该引用更新和关闭弹窗。
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ describe('Table.pagination', () => {
|
|||||||
container
|
container
|
||||||
.querySelector('.ant-table-tbody')
|
.querySelector('.ant-table-tbody')
|
||||||
?.querySelectorAll('tr')
|
?.querySelectorAll('tr')
|
||||||
?.forEach(tr => {
|
?.forEach((tr) => {
|
||||||
namesList.push(tr.querySelector('td')?.textContent);
|
namesList.push(tr.querySelector('td')?.textContent);
|
||||||
});
|
});
|
||||||
return namesList;
|
return namesList;
|
||||||
@ -352,7 +352,7 @@ describe('Table.pagination', () => {
|
|||||||
rerender(
|
rerender(
|
||||||
createTable({
|
createTable({
|
||||||
pagination: { current: 10, pageSize: 10, onChange },
|
pagination: { current: 10, pageSize: 10, onChange },
|
||||||
dataSource: longData.filter(item => item.key < 60),
|
dataSource: longData.filter((item) => item.key < 60),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -418,7 +418,7 @@ describe('Table.pagination', () => {
|
|||||||
* to `pagination`, since they misunderstand that `pagination` can accept a boolean value.
|
* to `pagination`, since they misunderstand that `pagination` can accept a boolean value.
|
||||||
*/
|
*/
|
||||||
it('Accepts pagination as true', () => {
|
it('Accepts pagination as true', () => {
|
||||||
const { asFragment } = render(createTable({ pagination: true } as TableProps<any>));
|
const { asFragment } = render(createTable({ pagination: true as any } as TableProps<any>));
|
||||||
expect(asFragment().firstChild).toMatchSnapshot();
|
expect(asFragment().firstChild).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -603,7 +603,7 @@ describe('Table.pagination', () => {
|
|||||||
const dataProp = { data: [] } as any;
|
const dataProp = { data: [] } as any;
|
||||||
const Demo: React.FC = () => {
|
const Demo: React.FC = () => {
|
||||||
const [p, setP] = React.useState<TablePaginationConfig>({
|
const [p, setP] = React.useState<TablePaginationConfig>({
|
||||||
showTotal: t => `>${t}<`,
|
showTotal: (t) => `>${t}<`,
|
||||||
total: 200,
|
total: 200,
|
||||||
current: 1,
|
current: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
@ -613,7 +613,7 @@ describe('Table.pagination', () => {
|
|||||||
{...dataProp}
|
{...dataProp}
|
||||||
columns={[]}
|
columns={[]}
|
||||||
pagination={p}
|
pagination={p}
|
||||||
onChange={pg => {
|
onChange={(pg) => {
|
||||||
setP({
|
setP({
|
||||||
...pg,
|
...pg,
|
||||||
total: 23,
|
total: 23,
|
||||||
|
@ -39,7 +39,7 @@ describe('Table.sorter', () => {
|
|||||||
container
|
container
|
||||||
?.querySelector('.ant-table-tbody')
|
?.querySelector('.ant-table-tbody')
|
||||||
?.querySelectorAll('tr')
|
?.querySelectorAll('tr')
|
||||||
?.forEach(tr => {
|
?.forEach((tr) => {
|
||||||
namesList.push(tr.querySelector('td')?.textContent);
|
namesList.push(tr.querySelector('td')?.textContent);
|
||||||
});
|
});
|
||||||
return namesList;
|
return namesList;
|
||||||
@ -129,7 +129,7 @@ describe('Table.sorter', () => {
|
|||||||
expect(getNameColumn()?.getAttribute('aria-sort')).toEqual('descending');
|
expect(getNameColumn()?.getAttribute('aria-sort')).toEqual('descending');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sort records with keydown', () => {
|
it('sort records when press enter', () => {
|
||||||
const { container } = render(createTable());
|
const { container } = render(createTable());
|
||||||
|
|
||||||
// ascend
|
// ascend
|
||||||
@ -141,6 +141,30 @@ describe('Table.sorter', () => {
|
|||||||
expect(renderedNames(container)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
expect(renderedNames(container)).toEqual(['Tom', 'Lucy', 'Jack', 'Jerry']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// https://github.com/ant-design/ant-design/issues/38579
|
||||||
|
it('should not sort records when press enter in filter dropdown', () => {
|
||||||
|
const { container } = render(
|
||||||
|
createTable({
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
...column,
|
||||||
|
filters: [{ text: 'J', value: 'J' }],
|
||||||
|
onFilter: (value: any, record: any) => record.name.includes(value),
|
||||||
|
filterDropdownOpen: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// don't trigger ascend
|
||||||
|
fireEvent.keyDown(container.querySelector('.ant-table-filter-dropdown')!, { keyCode: 13 });
|
||||||
|
expect(renderedNames(container)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
||||||
|
|
||||||
|
// don't trigger descend
|
||||||
|
fireEvent.keyDown(container.querySelector('.ant-table-filter-dropdown')!, { keyCode: 13 });
|
||||||
|
expect(renderedNames(container)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
|
||||||
|
});
|
||||||
|
|
||||||
describe('can be controlled by sortOrder', () => {
|
describe('can be controlled by sortOrder', () => {
|
||||||
it('single', () => {
|
it('single', () => {
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
@ -434,7 +458,7 @@ describe('Table.sorter', () => {
|
|||||||
class TableTest extends React.Component {
|
class TableTest extends React.Component {
|
||||||
state = { pagination: {} };
|
state = { pagination: {} };
|
||||||
|
|
||||||
onChange: TableProps<any>['onChange'] = pagination => {
|
onChange: TableProps<any>['onChange'] = (pagination) => {
|
||||||
this.setState({ pagination });
|
this.setState({ pagination });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -494,7 +518,7 @@ describe('Table.sorter', () => {
|
|||||||
pagination: {},
|
pagination: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
onChange: TableProps<any>['onChange'] = pagination => {
|
onChange: TableProps<any>['onChange'] = (pagination) => {
|
||||||
this.setState({ pagination });
|
this.setState({ pagination });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -561,7 +585,7 @@ describe('Table.sorter', () => {
|
|||||||
pagination: {},
|
pagination: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
onChange: TableProps<any>['onChange'] = pagination => {
|
onChange: TableProps<any>['onChange'] = (pagination) => {
|
||||||
this.setState({ pagination });
|
this.setState({ pagination });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
import KeyCode from 'rc-util/lib/KeyCode';
|
||||||
|
|
||||||
export interface FilterDropdownMenuWrapperProps {
|
export interface FilterDropdownMenuWrapperProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (event) => {
|
||||||
|
const { keyCode } = event;
|
||||||
|
if (keyCode === KeyCode.ENTER) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const FilterDropdownMenuWrapper = (props: FilterDropdownMenuWrapperProps) => (
|
const FilterDropdownMenuWrapper = (props: FilterDropdownMenuWrapperProps) => (
|
||||||
<div className={props.className} onClick={e => e.stopPropagation()}>
|
<div className={props.className} onClick={(e) => e.stopPropagation()} onKeyDown={onKeyDown}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -277,7 +277,7 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
|
|||||||
const cssLineClamp = mergedEnableEllipsis && rows > 1 && cssEllipsis;
|
const cssLineClamp = mergedEnableEllipsis && rows > 1 && cssEllipsis;
|
||||||
|
|
||||||
// >>>>> Expand
|
// >>>>> Expand
|
||||||
const onExpandClick: React.MouseEventHandler<HTMLElement> = e => {
|
const onExpandClick: React.MouseEventHandler<HTMLElement> = (e) => {
|
||||||
setExpanded(true);
|
setExpanded(true);
|
||||||
ellipsisConfig.onExpand?.(e);
|
ellipsisConfig.onExpand?.(e);
|
||||||
};
|
};
|
||||||
@ -510,6 +510,7 @@ const Base = React.forwardRef<HTMLElement, BlockProps>((props, ref) => {
|
|||||||
},
|
},
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
prefixCls={customizePrefixCls}
|
||||||
style={{
|
style={{
|
||||||
...style,
|
...style,
|
||||||
WebkitLineClamp: cssLineClamp ? rows : undefined,
|
WebkitLineClamp: cssLineClamp ? rows : undefined,
|
||||||
|
20
components/typography/__tests__/prefixCls.test.tsx
Normal file
20
components/typography/__tests__/prefixCls.test.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render } from '../../../tests/utils';
|
||||||
|
|
||||||
|
import Base from '../Base';
|
||||||
|
|
||||||
|
describe('Typography keep prefixCls', () => {
|
||||||
|
describe('Base', () => {
|
||||||
|
it('should support className when has prefix', () => {
|
||||||
|
const { container: wrapper } = render(
|
||||||
|
<Base component="p" prefixCls="custom-prefixCls" className="custom-class">
|
||||||
|
test prefixCls
|
||||||
|
</Base>,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
(wrapper.firstChild as HTMLElement)?.className.includes('custom-prefixCls'),
|
||||||
|
).toBeTruthy();
|
||||||
|
expect((wrapper.firstChild as HTMLElement)?.className.includes('custom-class')).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -297,7 +297,7 @@
|
|||||||
"sylvanas": "^0.6.1",
|
"sylvanas": "^0.6.1",
|
||||||
"theme-switcher": "^1.0.2",
|
"theme-switcher": "^1.0.2",
|
||||||
"ts-node": "^10.8.2",
|
"ts-node": "^10.8.2",
|
||||||
"typescript": "~4.8.4",
|
"typescript": "~4.9.3",
|
||||||
"webpack-bundle-analyzer": "^4.1.0",
|
"webpack-bundle-analyzer": "^4.1.0",
|
||||||
"xhr-mock": "^2.4.1",
|
"xhr-mock": "^2.4.1",
|
||||||
"yaml-front-matter": "^4.0.0"
|
"yaml-front-matter": "^4.0.0"
|
||||||
@ -325,7 +325,7 @@
|
|||||||
"mode": "npm"
|
"mode": "npm"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"*.{ts,tsx,js,json,less}": "rome format --write",
|
"*.{ts,tsx,js}": "rome format --write",
|
||||||
"*.{md}": "prettier --ignore-unknown --write"
|
"*.{json,less,md}": "prettier --ignore-unknown --write"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user