mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-11-30 02:59:04 +08:00
Merge branch 'master' into next-merge-master
This commit is contained in:
commit
eb9179464b
@ -15,6 +15,17 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.23.2
|
||||
|
||||
`2022-09-17`
|
||||
|
||||
- 🐞 Fix Card console warning when using `tabList` prop. [#37537](https://github.com/ant-design/ant-design/pull/37537) [@edc-hui](https://github.com/edc-hui)
|
||||
- 🐞 Fix Table `filterSearch` is not executed when `filterMode="tree"`. [#37587](https://github.com/ant-design/ant-design/pull/37587) [@edc-hui](https://github.com/edc-hui)
|
||||
- 🐞 Fix Tooltip should not replace children `className` when it's not a string type. [#37598](https://github.com/ant-design/ant-design/pull/37598)
|
||||
- 🐞 Fix Tree that TreeNodes not aligned when draggable and some node is disabled. [#37534](https://github.com/ant-design/ant-design/pull/37534)
|
||||
- TypeScript
|
||||
- 🤖 Replace deprecated `React.ReactChild` type. [#37551](https://github.com/ant-design/ant-design/pull/37551) [@bowen-wu](https://github.com/bowen-wu)
|
||||
|
||||
## 4.23.1
|
||||
|
||||
`2022-09-09`
|
||||
@ -202,7 +213,7 @@ timeline: true
|
||||
- 💄 Fix nested Table margin style. [#36209](https://github.com/ant-design/ant-design/pull/36209)
|
||||
- 🐞 Fix Table filter dropdown with multiple subMenu may not closed. [#36132](https://github.com/ant-design/ant-design/pull/36132)
|
||||
- 🐞 Table reset the last selection key when deselect or bulk operations. [#34705](https://github.com/ant-design/ant-design/pull/34705) [@Dunqing](https://github.com/Dunqing)
|
||||
- 🐞 Fix Popover arrow color with custom `color` prop. [#36241](https://github.com/ant-design/ant-design/pull/36241) [@MadCcc](https://github.com/MadCcc)
|
||||
- 🐞 Fix Popover arrow color with custom `color` prop. [#36241](https://github.com/ant-design/ant-design/pull/36241)
|
||||
- 🐞 Fix Upload `listType="picture-card"` select button not being hidden when children is empty. [#36196](https://github.com/ant-design/ant-design/pull/36196)
|
||||
- 🐞 Fix Menu deprecated warning when `item={undefined}`. [#36190](https://github.com/ant-design/ant-design/pull/36190)
|
||||
- 💄 Fix Button `loading` icon margin style lost. [#36168](https://github.com/ant-design/ant-design/pull/36168)
|
||||
@ -425,7 +436,7 @@ timeline: true
|
||||
- Less
|
||||
- 💄 Replace less html selector with related variable. [#35186](https://github.com/ant-design/ant-design/pull/35186) [@jeffdrumgod](https://github.com/jeffdrumgod)
|
||||
- 💄 Modify less `danger` value from the function to variable. [#35113](https://github.com/ant-design/ant-design/pull/35113) [@TrickyPi](https://github.com/TrickyPi)
|
||||
- 🐞 Arrow border radius variable use fixed value. [#35086](https://github.com/ant-design/ant-design/pull/35086) [@MadCcc](https://github.com/MadCcc)
|
||||
- 🐞 Arrow border radius variable use fixed value. [#35086](https://github.com/ant-design/ant-design/pull/35086)
|
||||
- TypeScript
|
||||
- 🤖 Fixed `Upload` component `UploadChangeParam<T>` internal `fileList` not using generics. [#35158](https://github.com/ant-design/ant-design/pull/35158) [@rendaoer](https://github.com/rendaoer)
|
||||
- 🤖 Update TypeScript definition for `@types/react@18` compatible. [#35075](https://github.com/ant-design/ant-design/pull/35075) [@AliRezaBeigy](https://github.com/AliRezaBeigy) [#35076](https://github.com/ant-design/ant-design/pull/35076) [@littledian](https://github.com/littledian)
|
||||
@ -580,7 +591,7 @@ timeline: true
|
||||
- 💄 Improve Menu `:focus-visible` style. [#34008](https://github.com/ant-design/ant-design/pull/34008)
|
||||
- 💄 Fix Pagination and Rate style problem in Safari. [#34002](https://github.com/ant-design/ant-design/pull/34002)
|
||||
- 💄 Fix Row and Col component styles when using prefixCls. [#33969](https://github.com/ant-design/ant-design/pull/33969) [@mic-web](https://github.com/mic-web)
|
||||
- 🐞 Fix Timeline icons with custom color not working. [#33951](https://github.com/ant-design/ant-design/pull/33951) [@MadCcc](https://github.com/MadCcc)
|
||||
- 🐞 Fix Timeline icons with custom color not working. [#33951](https://github.com/ant-design/ant-design/pull/33951)
|
||||
- TypeScript
|
||||
- 🤖 Optimize Cascader `onChange` definition with `multiple` prop. [#33947](https://github.com/ant-design/ant-design/pull/33947) [@babycannotsay](https://github.com/babycannotsay)
|
||||
|
||||
@ -663,7 +674,7 @@ timeline: true
|
||||
|
||||
`2021-12-29`
|
||||
|
||||
- 🐞 Fix Popconfirm throws `Can't perform a React state update on an unmounted component.` warning in some async case. [#33432](https://github.com/ant-design/ant-design/pull/33432) [@MadCcc](https://github.com/MadCcc)
|
||||
- 🐞 Fix Popconfirm throws `Can't perform a React state update on an unmounted component.` warning in some async case. [#33432](https://github.com/ant-design/ant-design/pull/33432)
|
||||
- 🐞 Fix Input with `suffix` will crash when `value` is number type. [#33462](https://github.com/ant-design/ant-design/pull/33462)
|
||||
- 🐞 Fix Divider with text dashed border color error. [#33452](https://github.com/ant-design/ant-design/pull/33452) [@chen-jingjie](https://github.com/chen-jingjie)
|
||||
- 🐞 Fix Dropdown.Button not support `destroyPopupOnHide`. [#33442](https://github.com/ant-design/ant-design/pull/33442) [@LongHaoo](https://github.com/LongHaoo)
|
||||
|
@ -15,6 +15,17 @@ timeline: true
|
||||
|
||||
---
|
||||
|
||||
## 4.23.2
|
||||
|
||||
`2022-09-17`
|
||||
|
||||
- 🐞 修复 Card 传入 `tabList` 属性时控制台出现废弃警告的问题。[#37537](https://github.com/ant-design/ant-design/pull/37537) [@edc-hui](https://github.com/edc-hui)
|
||||
- 🐞 修复 Table `filterMode="tree"` 时 `filterSearch` 函数未执行的问题。[#37587](https://github.com/ant-design/ant-design/pull/37587) [@edc-hui](https://github.com/edc-hui)
|
||||
- 🐞 修复 Tooltip 的子元素 `className` 非 string 类型时会被覆盖的问题。[#37598](https://github.com/ant-design/ant-design/pull/37598)
|
||||
- 🐞 修复 Tree 组件 TreeNode 在可拖拽并且禁用状态下不对齐的问题。[#37534](https://github.com/ant-design/ant-design/pull/37534)
|
||||
- TypeScript
|
||||
- 🤖 替换已废弃的 `React.ReactChild` 定义。[#37551](https://github.com/ant-design/ant-design/pull/37551) [@bowen-wu](https://github.com/bowen-wu)
|
||||
|
||||
## 4.23.1
|
||||
|
||||
`2022-09-09`
|
||||
@ -203,7 +214,7 @@ timeline: true
|
||||
- 🐞 Table 取消选择或批量操作时重置上一次选择的 key。[#34705](https://github.com/ant-design/ant-design/pull/34705) [@Dunqing](https://github.com/Dunqing)
|
||||
- 🐞 修复 Table 过滤列表在某些场景下多级展开无法关闭的问题。[#36132](https://github.com/ant-design/ant-design/pull/36132)
|
||||
- 🐞 修复 Upload `listType="picture-card"` 当 children 为空时上传文件按钮没有隐藏的问题。[#36196](https://github.com/ant-design/ant-design/pull/36196)
|
||||
- 🐞 修复 Popover 自定义 `color` 时箭头颜色问题。[#36241](https://github.com/ant-design/ant-design/pull/36241) [@MadCcc](https://github.com/MadCcc)
|
||||
- 🐞 修复 Popover 自定义 `color` 时箭头颜色问题。[#36241](https://github.com/ant-design/ant-design/pull/36241)
|
||||
- 🐞 修复 Menu `item={undefined}` 时会有废弃警告的问题。[#36190](https://github.com/ant-design/ant-design/pull/36190)
|
||||
- 💄 修复 Button `loading` 图标的间距丢失的问题。[#36168](https://github.com/ant-design/ant-design/pull/36168)
|
||||
- 🐞 修复 Dropdown 中 Menu 分组下的 Item 点击不会关闭的问题。[#36148](https://github.com/ant-design/ant-design/pull/36148)
|
||||
@ -421,12 +432,12 @@ timeline: true
|
||||
- 🐞 修复 Title、Text、Paragraph 组件不支持 `ref` 的问题。[#34847](https://github.com/ant-design/ant-design/pull/34847) [@MQuy](https://github.com/MQuy)
|
||||
- Input
|
||||
- 💄 Input.Group 对子组件屏蔽 Form.Item 的样式。[#34764](https://github.com/ant-design/ant-design/pull/34764)
|
||||
- 💄 调整 Form 下 TextArea 的样式。[#34714](https://github.com/ant-design/ant-design/pull/34714) [@MadCcc](https://github.com/MadCcc)
|
||||
- 💄 调整 Form 下 TextArea 的样式。[#34714](https://github.com/ant-design/ant-design/pull/34714)
|
||||
- ⌨️ 修复 Checkbox 缺少 `aria-checked` 属性导致屏幕阅读器识别错误的问题。[#34862](https://github.com/ant-design/ant-design/pull/34862) [@SpaNb4](https://github.com/SpaNb4)
|
||||
- Less
|
||||
- 💄 替换 less 中的 html 选择器为对应变量。[#35186](https://github.com/ant-design/ant-design/pull/35186) [@jeffdrumgod](https://github.com/jeffdrumgod)
|
||||
- 💄 修改 less 中 `danger` 值从函数改为变量。[#35113](https://github.com/ant-design/ant-design/pull/35113) [@TrickyPi](https://github.com/TrickyPi)
|
||||
- 🐞 箭头圆角使用固定值 2px 变量。[#35086](https://github.com/ant-design/ant-design/pull/35086) [@MadCcc](https://github.com/MadCcc)
|
||||
- 🐞 箭头圆角使用固定值 2px 变量。[#35086](https://github.com/ant-design/ant-design/pull/35086)
|
||||
- TypeScript
|
||||
- 🤖 修正 Upload 组件中 `UploadChangeParam<T>` 内部 `fileList` 不使用泛型问题。[#35158](https://github.com/ant-design/ant-design/pull/35158) [@rendaoer](https://github.com/rendaoer)
|
||||
- 🤖 更新 TypeScript 定义以兼容 `@types/react@18`。[#35075](https://github.com/ant-design/ant-design/pull/35075) [@AliRezaBeigy](https://github.com/AliRezaBeigy) [#35076](https://github.com/ant-design/ant-design/pull/35076) [@littledian](https://github.com/littledian)
|
||||
@ -582,7 +593,7 @@ timeline: true
|
||||
- 💄 优化 Menu `:focus-visible` 的样式。[#34008](https://github.com/ant-design/ant-design/pull/34008)
|
||||
- 💄 修复 Pagination 和 Rate 在 Safari 下部分样式丢失的问题,比如分页按钮禁用样式失效。[#34002](https://github.com/ant-design/ant-design/pull/34002)
|
||||
- 💄 修复 Row 与 Col 在配置 `prefixCls` 的样式问题。[#33969](https://github.com/ant-design/ant-design/pull/33969) [@mic-web](https://github.com/mic-web)
|
||||
- 🐞 修复 Timeline 的自定义图标颜色无效的问题。[#33951](https://github.com/ant-design/ant-design/pull/33951) [@MadCcc](https://github.com/MadCcc)
|
||||
- 🐞 修复 Timeline 的自定义图标颜色无效的问题。[#33951](https://github.com/ant-design/ant-design/pull/33951)
|
||||
- TypeScript
|
||||
- 🤖 优化 Cascader `multiple` 属性对应的 `onChange` 类型推断。[#33947](https://github.com/ant-design/ant-design/pull/33947) [@babycannotsay](https://github.com/babycannotsay)
|
||||
|
||||
@ -665,7 +676,7 @@ timeline: true
|
||||
|
||||
`2021-12-29`
|
||||
|
||||
- 🐞 修复 Popconfirm 在某些情况下会出现 `Can't perform a React state update on an unmounted component.` 的错误。[#33432](https://github.com/ant-design/ant-design/pull/33432) [@MadCcc](https://github.com/MadCcc)
|
||||
- 🐞 修复 Popconfirm 在某些情况下会出现 `Can't perform a React state update on an unmounted component.` 的错误。[#33432](https://github.com/ant-design/ant-design/pull/33432)
|
||||
- 🐞 修复 Input 配置 `suffix` 时 `value` 为数字类型会崩溃的问题。[#33462](https://github.com/ant-design/ant-design/pull/33462)
|
||||
- 🐞 修复 Divider with text dashed 的边框颜色错误问题。[#33452](https://github.com/ant-design/ant-design/pull/33452) [@chen-jingjie](https://github.com/chen-jingjie)
|
||||
- 🐞 修复 Dropdown.Button 不支持 `destroyPopupOnHide` 的问题。[#33442](https://github.com/ant-design/ant-design/pull/33442) [@LongHaoo](https://github.com/LongHaoo)
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import { render, sleep, fireEvent } from '../../../tests/utils';
|
||||
import { render, sleep, fireEvent, act } from '../../../tests/utils';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import Wave from '../wave';
|
||||
|
||||
@ -270,4 +270,31 @@ describe('Wave component', () => {
|
||||
expect(styles[0].innerHTML).toContain('--antd-wave-shadow-color: red;');
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('Wave style should append to validate element', () => {
|
||||
jest.useFakeTimers();
|
||||
const { container } = render(
|
||||
<Wave>
|
||||
<div className="bamboo" style={{ borderColor: 'red' }} />
|
||||
</Wave>,
|
||||
);
|
||||
|
||||
// Mock shadow container
|
||||
const fakeDoc = document.createElement('div');
|
||||
fakeDoc.append('text');
|
||||
fakeDoc.appendChild(document.createElement('span'));
|
||||
expect(fakeDoc.childNodes).toHaveLength(2);
|
||||
|
||||
(container.querySelector('.bamboo') as any).getRootNode = () => fakeDoc;
|
||||
|
||||
// Click should not throw
|
||||
fireEvent.click(container.querySelector('.bamboo')!);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect(fakeDoc.querySelector('style')).toBeTruthy();
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
@ -18,6 +18,16 @@ function isHidden(element: HTMLElement) {
|
||||
return !element || element.offsetParent === null || element.hidden;
|
||||
}
|
||||
|
||||
function getValidateContainer(nodeRoot: Node): Element {
|
||||
if (nodeRoot instanceof Document) {
|
||||
return nodeRoot.body;
|
||||
}
|
||||
|
||||
return Array.from(nodeRoot.childNodes).find(
|
||||
ele => ele?.nodeType === Node.ELEMENT_NODE,
|
||||
) as Element;
|
||||
}
|
||||
|
||||
function isNotGrey(color: string) {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\d.]*)?\)/);
|
||||
@ -119,8 +129,7 @@ class InternalWave extends React.Component<WaveProps> {
|
||||
extraNode.style.borderColor = waveColor;
|
||||
|
||||
const nodeRoot = node.getRootNode?.() || node.ownerDocument;
|
||||
const nodeBody: Element =
|
||||
nodeRoot instanceof Document ? nodeRoot.body : (nodeRoot.firstChild as Element) ?? nodeRoot;
|
||||
const nodeBody = getValidateContainer(nodeRoot) ?? nodeRoot;
|
||||
|
||||
styleForPseudo = updateCSS(
|
||||
`
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Alert action of Alert custom action 1`] = `
|
||||
exports[`Alert custom action 1`] = `
|
||||
<div
|
||||
class="ant-alert ant-alert-success"
|
||||
data-show="true"
|
||||
@ -74,29 +74,6 @@ exports[`Alert action of Alert custom action 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Alert could accept none react element icon 1`] = `
|
||||
<div
|
||||
class="ant-alert ant-alert-success"
|
||||
data-show="true"
|
||||
role="alert"
|
||||
>
|
||||
<span
|
||||
class="ant-alert-icon"
|
||||
>
|
||||
icon
|
||||
</span>
|
||||
<div
|
||||
class="ant-alert-content"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-message"
|
||||
>
|
||||
Success Tips
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Alert rtl render component should be rendered correctly in RTL direction 1`] = `
|
||||
<div
|
||||
class="ant-alert ant-alert-info ant-alert-no-icon ant-alert-rtl"
|
||||
@ -108,30 +85,3 @@ exports[`Alert rtl render component should be rendered correctly in RTL directio
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Alert support closeIcon 1`] = `
|
||||
<div
|
||||
class="ant-alert ant-alert-warning ant-alert-no-icon"
|
||||
data-show="true"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-content"
|
||||
>
|
||||
<div
|
||||
class="ant-alert-message"
|
||||
>
|
||||
Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="ant-alert-close-icon"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
close
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
@ -1,8 +1,9 @@
|
||||
import React from 'react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import Alert from '..';
|
||||
import accessibilityTest from '../../../tests/shared/accessibilityTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { fireEvent, render, sleep, act } from '../../../tests/utils';
|
||||
import { render, act, screen } from '../../../tests/utils';
|
||||
import Button from '../../button';
|
||||
import Popconfirm from '../../popconfirm';
|
||||
import Tooltip from '../../tooltip';
|
||||
@ -21,9 +22,9 @@ describe('Alert', () => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('could be closed', () => {
|
||||
it('should show close button and could be closed', async () => {
|
||||
const onClose = jest.fn();
|
||||
const { container } = render(
|
||||
render(
|
||||
<Alert
|
||||
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
|
||||
type="warning"
|
||||
@ -32,125 +33,102 @@ describe('Alert', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
jest.useFakeTimers();
|
||||
fireEvent.click(container.querySelector('.ant-alert-close-icon')!);
|
||||
await userEvent.click(screen.getByRole('button', { name: /close/i }));
|
||||
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
jest.useRealTimers();
|
||||
|
||||
expect(onClose).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
describe('action of Alert', () => {
|
||||
it('custom action', () => {
|
||||
const { container } = render(
|
||||
<Alert
|
||||
message="Success Tips"
|
||||
type="success"
|
||||
showIcon
|
||||
action={
|
||||
<Button size="small" type="text">
|
||||
UNDO
|
||||
</Button>
|
||||
}
|
||||
closable
|
||||
/>,
|
||||
);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it('support closeIcon', () => {
|
||||
it('custom action', () => {
|
||||
const { container } = render(
|
||||
<Alert
|
||||
message="Success Tips"
|
||||
type="success"
|
||||
showIcon
|
||||
action={
|
||||
<Button size="small" type="text">
|
||||
UNDO
|
||||
</Button>
|
||||
}
|
||||
closable
|
||||
closeIcon={<span>close</span>}
|
||||
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
|
||||
type="warning"
|
||||
/>,
|
||||
);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('data and aria props', () => {
|
||||
it('sets data attributes on input', () => {
|
||||
const { container } = render(<Alert data-test="test-id" data-id="12345" message={null} />);
|
||||
const input = container.querySelector('.ant-alert')!;
|
||||
expect(input.getAttribute('data-test')).toBe('test-id');
|
||||
expect(input.getAttribute('data-id')).toBe('12345');
|
||||
});
|
||||
|
||||
it('sets aria attributes on input', () => {
|
||||
const { container } = render(<Alert aria-describedby="some-label" message={null} />);
|
||||
const input = container.querySelector('.ant-alert')!;
|
||||
expect(input.getAttribute('aria-describedby')).toBe('some-label');
|
||||
});
|
||||
|
||||
it('sets role attribute on input', () => {
|
||||
const { container } = render(<Alert role="status" message={null} />);
|
||||
const input = container.querySelector('.ant-alert')!;
|
||||
expect(input.getAttribute('role')).toBe('status');
|
||||
});
|
||||
it('should sets data attributes on alert when pass attributes to props', () => {
|
||||
render(
|
||||
<Alert data-test="test-id" data-id="12345" aria-describedby="some-label" message={null} />,
|
||||
);
|
||||
const alert = screen.getByRole('alert');
|
||||
expect(alert).toHaveAttribute('data-test', 'test-id');
|
||||
expect(alert).toHaveAttribute('data-id', '12345');
|
||||
expect(alert).toHaveAttribute('aria-describedby', 'some-label');
|
||||
});
|
||||
|
||||
it('ErrorBoundary', () => {
|
||||
it('sets role attribute on input', () => {
|
||||
render(<Alert role="status" message={null} />);
|
||||
|
||||
expect(screen.getByRole('status')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show error as ErrorBoundary when children have error', () => {
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
// eslint-disable-next-line no-console
|
||||
expect(console.error).toHaveBeenCalledTimes(0);
|
||||
// @ts-expect-error
|
||||
// eslint-disable-next-line react/jsx-no-undef
|
||||
const ThrowError = () => <NotExisted />;
|
||||
const { container } = render(
|
||||
render(
|
||||
<ErrorBoundary>
|
||||
<ThrowError />
|
||||
</ErrorBoundary>,
|
||||
);
|
||||
// eslint-disable-next-line jest/no-standalone-expect
|
||||
expect(container.textContent).toContain('ReferenceError: NotExisted is not defined');
|
||||
|
||||
expect(screen.getByRole('alert')).toHaveTextContent(
|
||||
'ReferenceError: NotExisted is not defined',
|
||||
);
|
||||
// eslint-disable-next-line no-console
|
||||
(console.error as any).mockRestore();
|
||||
});
|
||||
|
||||
it('could be used with Tooltip', async () => {
|
||||
const ref = React.createRef<any>();
|
||||
jest.useRealTimers();
|
||||
const { container } = render(
|
||||
<Tooltip title="xxx" mouseEnterDelay={0} ref={ref}>
|
||||
render(
|
||||
<Tooltip title="xxx" mouseEnterDelay={0}>
|
||||
<Alert
|
||||
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
|
||||
type="warning"
|
||||
/>
|
||||
</Tooltip>,
|
||||
);
|
||||
// wrapper.find('.ant-alert').simulate('mouseenter');
|
||||
fireEvent.mouseEnter(container.querySelector('.ant-alert')!);
|
||||
await sleep(0);
|
||||
expect(ref.current.getPopupDomNode()).toBeTruthy();
|
||||
jest.useFakeTimers();
|
||||
|
||||
await userEvent.hover(screen.getByRole('alert'));
|
||||
|
||||
expect(screen.getByRole('tooltip')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('could be used with Popconfirm', async () => {
|
||||
const ref = React.createRef<any>();
|
||||
jest.useRealTimers();
|
||||
const { container } = render(
|
||||
<Popconfirm ref={ref} title="xxx">
|
||||
render(
|
||||
<Popconfirm title="xxx">
|
||||
<Alert
|
||||
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
|
||||
type="warning"
|
||||
/>
|
||||
</Popconfirm>,
|
||||
);
|
||||
fireEvent.click(container.querySelector('.ant-alert')!);
|
||||
await sleep(0);
|
||||
expect(ref.current.getPopupDomNode()).toBeTruthy();
|
||||
jest.useFakeTimers();
|
||||
await userEvent.click(screen.getByRole('alert'));
|
||||
|
||||
expect(screen.getByRole('tooltip')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('could accept none react element icon', () => {
|
||||
const { container } = render(
|
||||
<Alert message="Success Tips" type="success" showIcon icon="icon" />,
|
||||
);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
render(<Alert message="Success Tips" type="success" showIcon icon="icon" />);
|
||||
|
||||
expect(screen.getByRole('alert')).toHaveTextContent(/success tips/i);
|
||||
expect(screen.getByRole('alert')).toHaveTextContent(/icon/i);
|
||||
});
|
||||
|
||||
it('should not render message div when no message', () => {
|
||||
|
@ -150,6 +150,8 @@ class Anchor extends React.Component<InternalAnchorProps, AnchorState, ConfigCon
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { getCurrentAnchor } = this.props;
|
||||
const { activeLink } = this.state;
|
||||
if (this.scrollEvent) {
|
||||
const currentContainer = this.getContainer();
|
||||
if (this.scrollContainer !== currentContainer) {
|
||||
@ -159,6 +161,9 @@ class Anchor extends React.Component<InternalAnchorProps, AnchorState, ConfigCon
|
||||
this.handleScroll();
|
||||
}
|
||||
}
|
||||
if (typeof getCurrentAnchor === 'function') {
|
||||
this.setCurrentActiveLink(getCurrentAnchor(activeLink || ''), false);
|
||||
}
|
||||
this.updateInk();
|
||||
}
|
||||
|
||||
@ -227,7 +232,7 @@ class Anchor extends React.Component<InternalAnchorProps, AnchorState, ConfigCon
|
||||
this.inkNode = node;
|
||||
};
|
||||
|
||||
setCurrentActiveLink = (link: string) => {
|
||||
setCurrentActiveLink = (link: string, triggerChange = true) => {
|
||||
const { activeLink } = this.state;
|
||||
const { onChange, getCurrentAnchor } = this.props;
|
||||
if (activeLink === link) {
|
||||
@ -237,7 +242,9 @@ class Anchor extends React.Component<InternalAnchorProps, AnchorState, ConfigCon
|
||||
this.setState({
|
||||
activeLink: typeof getCurrentAnchor === 'function' ? getCurrentAnchor(link) : link,
|
||||
});
|
||||
onChange?.(link);
|
||||
if (triggerChange) {
|
||||
onChange?.(link);
|
||||
}
|
||||
};
|
||||
|
||||
handleScroll = () => {
|
||||
|
@ -736,5 +736,21 @@ describe('Anchor Render', () => {
|
||||
fireEvent.click(container.querySelector(`a[href="#${hash2}"]`)!);
|
||||
expect(getCurrentAnchor).toHaveBeenCalledWith(`#${hash2}`);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/37627
|
||||
it('should update anchorLink when component is rerender', async () => {
|
||||
const hash1 = getHashUrl();
|
||||
const hash2 = getHashUrl();
|
||||
const Demo: React.FC<{ current: string }> = ({ current }) => (
|
||||
<Anchor getCurrentAnchor={() => `#${current}`}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>
|
||||
);
|
||||
const { container, rerender } = render(<Demo current={hash1} />);
|
||||
expect(container.querySelector(`.ant-anchor-link-title-active`)?.textContent).toBe(hash1);
|
||||
rerender(<Demo current={hash2} />);
|
||||
expect(container.querySelector(`.ant-anchor-link-title-active`)?.textContent).toBe(hash2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -121,11 +121,12 @@ const Card = React.forwardRef((props: CardProps, ref: React.Ref<HTMLDivElement>)
|
||||
{...extraProps}
|
||||
className={`${prefixCls}-head-tabs`}
|
||||
onChange={onTabChange}
|
||||
>
|
||||
{tabList.map(item => (
|
||||
<Tabs.TabPane tab={item.tab} disabled={item.disabled} key={item.key} />
|
||||
))}
|
||||
</Tabs>
|
||||
items={tabList.map(item => ({
|
||||
label: item.tab,
|
||||
key: item.key,
|
||||
disabled: item.disabled ?? false,
|
||||
}))}
|
||||
/>
|
||||
) : null;
|
||||
if (title || extra || tabs) {
|
||||
head = (
|
||||
|
@ -767,6 +767,7 @@ Array [
|
||||
>
|
||||
<div
|
||||
aria-controls="rc-tabs-test-panel-tabtest"
|
||||
aria-disabled="false"
|
||||
aria-selected="true"
|
||||
class="ant-tabs-tab-btn"
|
||||
id="rc-tabs-test-tab-tabtest"
|
||||
@ -781,6 +782,7 @@ Array [
|
||||
>
|
||||
<div
|
||||
aria-controls="rc-tabs-test-panel-tabtest"
|
||||
aria-disabled="false"
|
||||
aria-selected="false"
|
||||
class="ant-tabs-tab-btn"
|
||||
id="rc-tabs-test-tab-tabtest"
|
||||
@ -907,6 +909,7 @@ Array [
|
||||
>
|
||||
<div
|
||||
aria-controls="rc-tabs-test-panel-article"
|
||||
aria-disabled="false"
|
||||
aria-selected="false"
|
||||
class="ant-tabs-tab-btn"
|
||||
id="rc-tabs-test-tab-article"
|
||||
@ -921,6 +924,7 @@ Array [
|
||||
>
|
||||
<div
|
||||
aria-controls="rc-tabs-test-panel-app"
|
||||
aria-disabled="false"
|
||||
aria-selected="true"
|
||||
class="ant-tabs-tab-btn"
|
||||
id="rc-tabs-test-tab-app"
|
||||
@ -935,6 +939,7 @@ Array [
|
||||
>
|
||||
<div
|
||||
aria-controls="rc-tabs-test-panel-project"
|
||||
aria-disabled="false"
|
||||
aria-selected="false"
|
||||
class="ant-tabs-tab-btn"
|
||||
id="rc-tabs-test-tab-project"
|
||||
|
@ -767,6 +767,7 @@ Array [
|
||||
>
|
||||
<div
|
||||
aria-controls="rc-tabs-test-panel-tabtest"
|
||||
aria-disabled="false"
|
||||
aria-selected="true"
|
||||
class="ant-tabs-tab-btn"
|
||||
id="rc-tabs-test-tab-tabtest"
|
||||
@ -781,6 +782,7 @@ Array [
|
||||
>
|
||||
<div
|
||||
aria-controls="rc-tabs-test-panel-tabtest"
|
||||
aria-disabled="false"
|
||||
aria-selected="false"
|
||||
class="ant-tabs-tab-btn"
|
||||
id="rc-tabs-test-tab-tabtest"
|
||||
@ -888,6 +890,7 @@ Array [
|
||||
>
|
||||
<div
|
||||
aria-controls="rc-tabs-test-panel-article"
|
||||
aria-disabled="false"
|
||||
aria-selected="false"
|
||||
class="ant-tabs-tab-btn"
|
||||
id="rc-tabs-test-tab-article"
|
||||
@ -902,6 +905,7 @@ Array [
|
||||
>
|
||||
<div
|
||||
aria-controls="rc-tabs-test-panel-app"
|
||||
aria-disabled="false"
|
||||
aria-selected="true"
|
||||
class="ant-tabs-tab-btn"
|
||||
id="rc-tabs-test-tab-app"
|
||||
@ -916,6 +920,7 @@ Array [
|
||||
>
|
||||
<div
|
||||
aria-controls="rc-tabs-test-panel-project"
|
||||
aria-disabled="false"
|
||||
aria-selected="false"
|
||||
class="ant-tabs-tab-btn"
|
||||
id="rc-tabs-test-tab-project"
|
||||
|
@ -6,8 +6,6 @@ import Button from '../../button/index';
|
||||
import Card from '../index';
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
console.log('fireEvent');
|
||||
|
||||
describe('Card', () => {
|
||||
mountTest(Card);
|
||||
rtlTest(Card);
|
||||
|
@ -12,6 +12,7 @@ describe('Icon', () => {
|
||||
it('should throw Error', () => {
|
||||
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
render(<Icon />);
|
||||
expect(errSpy).toHaveBeenCalled();
|
||||
expect(errSpy).toHaveBeenCalledWith('Warning: [antd: Icon] Empty Icon');
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
@ -31,6 +31,8 @@ const localeValues: Locale = {
|
||||
searchPlaceholder: 'Procurar...',
|
||||
itemUnit: 'item',
|
||||
itemsUnit: 'itens',
|
||||
selectAll: 'Seleccionar Tudo',
|
||||
selectInvert: 'Inverter a página actual',
|
||||
},
|
||||
Upload: {
|
||||
uploading: 'A carregar...',
|
||||
|
@ -132,6 +132,7 @@ const ConfirmDialog = (props: ConfirmDialogProps) => {
|
||||
close,
|
||||
zIndex,
|
||||
afterClose,
|
||||
visible,
|
||||
open,
|
||||
keyboard,
|
||||
centered,
|
||||
@ -149,6 +150,14 @@ const ConfirmDialog = (props: ConfirmDialogProps) => {
|
||||
focusTriggerAfterClose,
|
||||
} = props;
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warning(
|
||||
visible === undefined,
|
||||
'Modal',
|
||||
`\`visible\` is deprecated, please use \`open\` instead.`,
|
||||
);
|
||||
}
|
||||
|
||||
const confirmPrefixCls = `${prefixCls}-confirm`;
|
||||
|
||||
const width = props.width || 416;
|
||||
|
@ -44,14 +44,7 @@ export default function confirm(config: ModalFuncProps) {
|
||||
reactUnmount(container);
|
||||
}
|
||||
|
||||
function render({
|
||||
okText,
|
||||
cancelText,
|
||||
prefixCls: customizePrefixCls,
|
||||
open,
|
||||
visible,
|
||||
...props
|
||||
}: any) {
|
||||
function render({ okText, cancelText, prefixCls: customizePrefixCls, ...props }: any) {
|
||||
/**
|
||||
* https://github.com/ant-design/ant-design/issues/23623
|
||||
*
|
||||
@ -68,7 +61,6 @@ export default function confirm(config: ModalFuncProps) {
|
||||
reactRender(
|
||||
<ConfirmDialog
|
||||
{...props}
|
||||
open={open ?? visible}
|
||||
prefixCls={prefixCls}
|
||||
rootPrefixCls={rootPrefixCls}
|
||||
iconPrefixCls={iconPrefixCls}
|
||||
@ -93,6 +85,12 @@ export default function confirm(config: ModalFuncProps) {
|
||||
destroy.apply(this, args);
|
||||
},
|
||||
};
|
||||
|
||||
// Legacy support
|
||||
if (currentConfig.visible) {
|
||||
delete currentConfig.visible;
|
||||
}
|
||||
|
||||
render(currentConfig);
|
||||
}
|
||||
|
||||
|
@ -311,7 +311,7 @@ function InternalTable<RecordType extends object = any>(
|
||||
);
|
||||
};
|
||||
|
||||
const [transformFilterColumns, filterStates, getFilters] = useFilter<RecordType>({
|
||||
const [transformFilterColumns, filterStates, filters] = useFilter<RecordType>({
|
||||
prefixCls,
|
||||
locale: tableLocale,
|
||||
dropdownPrefixCls,
|
||||
@ -321,16 +321,23 @@ function InternalTable<RecordType extends object = any>(
|
||||
});
|
||||
const mergedData = getFilterData(sortedData, filterStates);
|
||||
|
||||
changeEventInfo.filters = getFilters();
|
||||
changeEventInfo.filters = filters;
|
||||
changeEventInfo.filterStates = filterStates;
|
||||
|
||||
// ============================ Column ============================
|
||||
const columnTitleProps = React.useMemo(
|
||||
() => ({
|
||||
const columnTitleProps = React.useMemo(() => {
|
||||
const mergedFilters: Record<string, FilterValue> = {};
|
||||
Object.keys(filters).forEach(filterKey => {
|
||||
if (filters[filterKey] !== null) {
|
||||
mergedFilters[filterKey] = filters[filterKey]!;
|
||||
}
|
||||
});
|
||||
return {
|
||||
...sorterTitleProps,
|
||||
}),
|
||||
[sorterTitleProps],
|
||||
);
|
||||
filters: mergedFilters,
|
||||
};
|
||||
}, [sorterTitleProps, filters]);
|
||||
|
||||
const [transformTitleColumns] = useTitleColumns(columnTitleProps);
|
||||
|
||||
// ========================== Pagination ==========================
|
||||
|
@ -13,6 +13,7 @@ import type { SelectProps } from '../../select';
|
||||
import type { ColumnGroupType, ColumnType, TableProps } from '..';
|
||||
import type { ColumnFilterItem, FilterDropdownProps, FilterValue } from '../interface';
|
||||
import { resetWarned } from '../../_util/warning';
|
||||
import type { TreeColumnFilterItem } from '../hooks/useFilter/FilterDropdown';
|
||||
|
||||
// https://github.com/Semantic-Org/Semantic-UI-React/blob/72c45080e4f20b531fda2e3e430e384083d6766b/test/specs/modules/Dropdown/Dropdown-test.js#L73
|
||||
const nativeEvent = { nativeEvent: { stopImmediatePropagation: () => {} } };
|
||||
@ -1950,6 +1951,35 @@ describe('Table.filter', () => {
|
||||
expect(container.querySelectorAll('li.ant-dropdown-menu-item').length).toBe(2);
|
||||
});
|
||||
|
||||
it('should supports filterSearch has type of function when filterMode is tree', () => {
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const { container } = render(
|
||||
createTable({
|
||||
columns: [
|
||||
{
|
||||
...column,
|
||||
filterMode: 'tree',
|
||||
filters: [
|
||||
{ text: '节点一', value: 'node1' },
|
||||
{ text: '节点二', value: 'node2' },
|
||||
{ text: '节点三', value: 'node3' },
|
||||
],
|
||||
filterSearch: (input: any, record: TreeColumnFilterItem) =>
|
||||
(record.title as string).includes(input),
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
fireEvent.click(container.querySelector('span.ant-dropdown-trigger')!, nativeEvent);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(container.querySelectorAll('.ant-table-filter-dropdown-tree').length).toBe(1);
|
||||
expect(container.querySelectorAll('.ant-input').length).toBe(1);
|
||||
fireEvent.change(container.querySelector('.ant-input')!, { target: { value: '节点二' } });
|
||||
expect(container.querySelectorAll('.ant-tree-treenode.filter-node').length).toBe(1);
|
||||
});
|
||||
|
||||
it('supports check all items', () => {
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
const { container } = render(
|
||||
@ -2457,4 +2487,26 @@ describe('Table.filter', () => {
|
||||
?.disabled,
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('title render function support `filter`', () => {
|
||||
const title = jest.fn(() => 'RenderTitle');
|
||||
const { container } = render(
|
||||
createTable({
|
||||
columns: [
|
||||
{
|
||||
...column,
|
||||
title,
|
||||
filteredValue: ['boy'],
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(container.querySelector('.ant-table-column-title')?.textContent).toEqual('RenderTitle');
|
||||
expect(title).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
filters: { name: ['boy'] },
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import type { ColumnProps } from '..';
|
||||
import type { TreeColumnFilterItem } from '../hooks/useFilter/FilterDropdown';
|
||||
import Table from '../Table';
|
||||
|
||||
const { Column, ColumnGroup } = Table;
|
||||
@ -48,6 +49,8 @@ describe('Table.typescript types', () => {
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
filterSearch: (input: any, record: TreeColumnFilterItem) =>
|
||||
(record.title as string).includes(input),
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -61,7 +61,7 @@ function renderFilterItems({
|
||||
filteredKeys: Key[];
|
||||
filterMultiple: boolean;
|
||||
searchValue: string;
|
||||
filterSearch: FilterSearchType;
|
||||
filterSearch: FilterSearchType<ColumnFilterItem>;
|
||||
}): Required<MenuProps>['items'] {
|
||||
return filters.map((filter, index) => {
|
||||
const key = String(filter.value);
|
||||
@ -103,6 +103,8 @@ function renderFilterItems({
|
||||
});
|
||||
}
|
||||
|
||||
export type TreeColumnFilterItem = ColumnFilterItem & FilterTreeDataNode;
|
||||
|
||||
export interface FilterDropdownProps<RecordType> {
|
||||
tablePrefixCls: string;
|
||||
prefixCls: string;
|
||||
@ -111,7 +113,7 @@ export interface FilterDropdownProps<RecordType> {
|
||||
filterState?: FilterState<RecordType>;
|
||||
filterMultiple: boolean;
|
||||
filterMode?: 'menu' | 'tree';
|
||||
filterSearch?: FilterSearchType;
|
||||
filterSearch?: FilterSearchType<ColumnFilterItem | TreeColumnFilterItem>;
|
||||
columnKey: Key;
|
||||
children: React.ReactNode;
|
||||
triggerFilter: (filterState: FilterState<RecordType>) => void;
|
||||
@ -312,6 +314,12 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
||||
}
|
||||
return item;
|
||||
});
|
||||
const getFilterData = (node: FilterTreeDataNode): TreeColumnFilterItem => ({
|
||||
...node,
|
||||
text: node.title,
|
||||
value: node.key,
|
||||
children: node.children?.map(item => getFilterData(item)) || [],
|
||||
});
|
||||
|
||||
let dropdownContent: React.ReactNode;
|
||||
|
||||
@ -348,7 +356,7 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
||||
if (filterMode === 'tree') {
|
||||
return (
|
||||
<>
|
||||
<FilterSearch
|
||||
<FilterSearch<TreeColumnFilterItem>
|
||||
filterSearch={filterSearch}
|
||||
value={searchValue}
|
||||
onChange={onSearch}
|
||||
@ -385,7 +393,12 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
|
||||
defaultExpandAll
|
||||
filterTreeNode={
|
||||
searchValue.trim()
|
||||
? node => searchValueMatched(searchValue, node.title)
|
||||
? node => {
|
||||
if (typeof filterSearch === 'function') {
|
||||
return filterSearch(searchValue, getFilterData(node));
|
||||
}
|
||||
return searchValueMatched(searchValue, node.title);
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
|
@ -3,21 +3,21 @@ import * as React from 'react';
|
||||
import Input from '../../../input';
|
||||
import type { FilterSearchType, TableLocale } from '../../interface';
|
||||
|
||||
interface FilterSearchProps {
|
||||
interface FilterSearchProps<RecordType = any> {
|
||||
value: string;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
filterSearch: FilterSearchType;
|
||||
filterSearch: FilterSearchType<RecordType>;
|
||||
tablePrefixCls: string;
|
||||
locale: TableLocale;
|
||||
}
|
||||
|
||||
const FilterSearch: React.FC<FilterSearchProps> = ({
|
||||
function FilterSearch<RecordType>({
|
||||
value,
|
||||
onChange,
|
||||
filterSearch,
|
||||
tablePrefixCls,
|
||||
locale,
|
||||
}) => {
|
||||
}: FilterSearchProps<RecordType>) {
|
||||
if (!filterSearch) {
|
||||
return null;
|
||||
}
|
||||
@ -34,6 +34,6 @@ const FilterSearch: React.FC<FilterSearchProps> = ({
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default FilterSearch;
|
||||
|
@ -203,7 +203,7 @@ function useFilter<RecordType>({
|
||||
}: FilterConfig<RecordType>): [
|
||||
TransformColumns<RecordType>,
|
||||
FilterState<RecordType>[],
|
||||
() => Record<string, FilterValue | null>,
|
||||
Record<string, FilterValue | null>,
|
||||
] {
|
||||
const [filterStates, setFilterStates] = React.useState<FilterState<RecordType>[]>(
|
||||
collectFilterStates(mergedColumns, true),
|
||||
@ -235,10 +235,7 @@ function useFilter<RecordType>({
|
||||
return collectedStates;
|
||||
}, [mergedColumns, filterStates]);
|
||||
|
||||
const getFilters = React.useCallback(
|
||||
() => generateFilterInfo(mergedFilterStates),
|
||||
[mergedFilterStates],
|
||||
);
|
||||
const filters = React.useMemo(() => generateFilterInfo(mergedFilterStates), [mergedFilterStates]);
|
||||
|
||||
const triggerFilter = (filterState: FilterState<RecordType>) => {
|
||||
const newFilterStates = mergedFilterStates.filter(({ key }) => key !== filterState.key);
|
||||
@ -258,7 +255,7 @@ function useFilter<RecordType>({
|
||||
tableLocale,
|
||||
);
|
||||
|
||||
return [transformColumns, mergedFilterStates, getFilters];
|
||||
return [transformColumns, mergedFilterStates, filters];
|
||||
}
|
||||
|
||||
export default useFilter;
|
||||
|
@ -63,7 +63,7 @@ export interface ColumnTitleProps<RecordType> {
|
||||
sortColumn?: ColumnType<RecordType>;
|
||||
sortColumns?: { column: ColumnType<RecordType>; order: SortOrder }[];
|
||||
|
||||
filters?: Record<string, string[]>;
|
||||
filters?: Record<string, FilterValue>;
|
||||
}
|
||||
|
||||
export type ColumnTitle<RecordType> =
|
||||
@ -72,7 +72,9 @@ export type ColumnTitle<RecordType> =
|
||||
|
||||
export type FilterValue = (Key | boolean)[];
|
||||
export type FilterKey = Key[] | null;
|
||||
export type FilterSearchType = boolean | ((input: string, record: {}) => boolean);
|
||||
export type FilterSearchType<RecordType = Record<string, any>> =
|
||||
| boolean
|
||||
| ((input: string, record: RecordType) => boolean);
|
||||
export interface FilterConfirmProps {
|
||||
closeDropdown: boolean;
|
||||
}
|
||||
@ -112,7 +114,7 @@ export interface ColumnType<RecordType> extends Omit<RcColumnType<RecordType>, '
|
||||
defaultFilteredValue?: FilterValue | null;
|
||||
filterIcon?: React.ReactNode | ((filtered: boolean) => React.ReactNode);
|
||||
filterMode?: 'menu' | 'tree';
|
||||
filterSearch?: FilterSearchType;
|
||||
filterSearch?: FilterSearchType<ColumnFilterItem>;
|
||||
onFilter?: (value: string | number | boolean, record: RecordType) => boolean;
|
||||
filterDropdownOpen?: boolean;
|
||||
onFilterDropdownOpenChange?: (visible: boolean) => void;
|
||||
|
@ -14,10 +14,12 @@ describe('Tabs.Animated', () => {
|
||||
it('boolean: true', () => {
|
||||
const { result } = renderHook(() => useAnimateConfig('test', true));
|
||||
|
||||
expect(result.current).toEqual({
|
||||
inkBar: true,
|
||||
tabPane: false,
|
||||
});
|
||||
expect(result.current).toEqual(
|
||||
expect.objectContaining({
|
||||
inkBar: true,
|
||||
tabPane: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('config', () => {
|
||||
|
@ -26,7 +26,7 @@ export default function useAnimateConfig(
|
||||
} else if (animated === true) {
|
||||
mergedAnimated = {
|
||||
inkBar: true,
|
||||
tabPane: false,
|
||||
tabPane: true,
|
||||
};
|
||||
} else {
|
||||
mergedAnimated = {
|
||||
|
@ -845,6 +845,7 @@ const genTabsStyle: GenerateStyle<TabsToken> = (token: TabsToken): CSSObject =>
|
||||
},
|
||||
|
||||
[`${componentCls}-tabpane`]: {
|
||||
outline: 'none',
|
||||
'&-hidden': {
|
||||
display: 'none',
|
||||
},
|
||||
|
@ -538,4 +538,16 @@ describe('Tooltip', () => {
|
||||
jest.useRealTimers();
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('not inject className when children className is not string type', () => {
|
||||
const HOC = ({ className }: { className: Function }) => <span className={className()} />;
|
||||
const { container } = render(
|
||||
<Tooltip open>
|
||||
<HOC className={() => 'bamboo'} />
|
||||
</Tooltip>,
|
||||
);
|
||||
|
||||
expect(container.querySelector('.bamboo')).toBeTruthy();
|
||||
expect(container.querySelector('.ant-tooltip')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -30,7 +30,7 @@ The following APIs are shared by Tooltip, Popconfirm, Popover.
|
||||
| color | The background color | string | - | 4.3.0 |
|
||||
| defaultOpen | Whether the floating tooltip card is open by default | boolean | false | 4.23.0 |
|
||||
| destroyTooltipOnHide | Whether destroy tooltip when hidden, parent container of tooltip will be destroyed when `keepParent` is false | boolean \| { keepParent?: boolean } | false | |
|
||||
| getPopupContainer | The DOM container of the tip, the default behavior is to create a `div` element in `body` | function(triggerNode) | () => document.body | |
|
||||
| getPopupContainer | The DOM container of the tip, the default behavior is to create a `div` element in `body` | (triggerNode: HTMLElement) => HTMLElement | () => document.body | |
|
||||
| mouseEnterDelay | Delay in seconds, before tooltip is shown on mouse enter | number | 0.1 | |
|
||||
| mouseLeaveDelay | Delay in seconds, before tooltip is hidden on mouse leave | number | 0.1 | |
|
||||
| overlayClassName | Class name of the tooltip card | string | - | |
|
||||
@ -40,7 +40,7 @@ The following APIs are shared by Tooltip, Popconfirm, Popover.
|
||||
| trigger | Tooltip trigger mode. Could be multiple by passing an array | `hover` \| `focus` \| `click` \| `contextMenu` \| Array<string> | `hover` | |
|
||||
| open | Whether the floating tooltip card is open or not. Use `visible` under 4.23.0 ([why?](/docs/react/faq#why-open)) | boolean | false | 4.23.0 |
|
||||
| zIndex | Config `z-index` of Tooltip | number | - | |
|
||||
| onOpenChange | Callback executed when visibility of the tooltip card is changed | (open) => void | - | 4.23.0 |
|
||||
| onOpenChange | Callback executed when visibility of the tooltip card is changed | (open: boolean) => void | - | 4.23.0 |
|
||||
|
||||
## Note
|
||||
|
||||
|
@ -276,9 +276,12 @@ const Tooltip = React.forwardRef<unknown, TooltipProps>((props, ref) => {
|
||||
prefixCls,
|
||||
);
|
||||
const childProps = child.props;
|
||||
const childCls = classNames(childProps.className, {
|
||||
[openClassName || `${prefixCls}-open`]: true,
|
||||
});
|
||||
const childCls =
|
||||
!childProps.className || typeof childProps.className === 'string'
|
||||
? classNames(childProps.className, {
|
||||
[openClassName || `${prefixCls}-open`]: true,
|
||||
})
|
||||
: childProps.className;
|
||||
|
||||
// Style
|
||||
const [wrapSSR, hashId] = useStyle(prefixCls, !injectFromPopover);
|
||||
|
@ -32,7 +32,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Vyyeu8jq2/Tooltp.svg
|
||||
| color | 背景颜色 | string | - | 4.3.0 |
|
||||
| defaultOpen | 默认是否显隐 | boolean | false | 4.23.0 |
|
||||
| destroyTooltipOnHide | 关闭后是否销毁 Tooltip,当 `keepParent` 为 `false` 时销毁父容器 | boolean \| { keepParent?: boolean } | false | |
|
||||
| getPopupContainer | 浮层渲染父节点,默认渲染到 body 上 | function(triggerNode) | () => document.body | |
|
||||
| getPopupContainer | 浮层渲染父节点,默认渲染到 body 上 | (triggerNode: HTMLElement) => HTMLElement | () => document.body | |
|
||||
| mouseEnterDelay | 鼠标移入后延时多少才显示 Tooltip,单位:秒 | number | 0.1 | |
|
||||
| mouseLeaveDelay | 鼠标移出后延时多少才隐藏 Tooltip,单位:秒 | number | 0.1 | |
|
||||
| overlayClassName | 卡片类名 | string | - | |
|
||||
@ -42,7 +42,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Vyyeu8jq2/Tooltp.svg
|
||||
| trigger | 触发行为,可选 `hover` \| `focus` \| `click` \| `contextMenu`,可使用数组设置多个触发行为 | string \| string\[] | `hover` | |
|
||||
| open | 用于手动控制浮层显隐,小于 4.23.0 使用 `visible`([为什么?](/docs/react/faq#why-open)) | boolean | false | 4.23.0 |
|
||||
| zIndex | 设置 Tooltip 的 `z-index` | number | - | |
|
||||
| onOpenChange | 显示隐藏的回调 | (open) => void | - | 4.23.0 |
|
||||
| onOpenChange | 显示隐藏的回调 | (open: boolean) => void | - | 4.23.0 |
|
||||
|
||||
## 注意
|
||||
|
||||
|
@ -24,7 +24,7 @@ Basic text writing, including headings, body text, lists, and more.
|
||||
| delete | Deleted line style | boolean | false | |
|
||||
| disabled | Disabled content | boolean | false | |
|
||||
| editable | If editable. Can control edit state when is object | boolean \| [editable](#editable) | false | [editable](#editable) |
|
||||
| ellipsis | Display ellipsis when text overflows,can't configure expandable、rows and onExpand by using object | boolean \| [Omit<ellipsis, 'expandable' \| 'rows' \| 'onExpand'>](#ellipsis) | false | [ellipsis](#ellipsis) |
|
||||
| ellipsis | Display ellipsis when text overflows,can't configure expandable、rows and onExpand by using object. Diff with Typography.Paragraph, Text do not have 100% width style which means it will fix width on the first ellipsis. If you want to have responsive ellipsis, please set width manually | boolean \| [Omit<ellipsis, 'expandable' \| 'rows' \| 'onExpand'>](#ellipsis) | false | [ellipsis](#ellipsis) |
|
||||
| keyboard | Keyboard style | boolean | false | 4.3.0 |
|
||||
| mark | Marked style | boolean | false | |
|
||||
| onClick | Set the handler to handle click event | (event) => void | - | |
|
||||
|
@ -25,7 +25,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/GOM1KQ24O/Typography.svg
|
||||
| delete | 添加删除线样式 | boolean | false | |
|
||||
| disabled | 禁用文本 | boolean | false | |
|
||||
| editable | 是否可编辑,为对象时可对编辑进行控制 | boolean \| [editable](#editable) | false | [editable](#editable) |
|
||||
| ellipsis | 自动溢出省略,为对象时不能设置省略行数、是否可展开、onExpand 展开事件 | boolean \| [Omit<ellipsis, 'expandable' \| 'rows' \| 'onExpand'>](#ellipsis) | false | [ellipsis](#ellipsis) |
|
||||
| ellipsis | 自动溢出省略,为对象时不能设置省略行数、是否可展开、onExpand 展开事件。不同于 Typography.Paragraph,Text 组件自身不带 100% 宽度样式,因而默认情况下初次缩略后宽度便不再变化。如果需要自适应宽度,请手工配置宽度样式 | boolean \| [Omit<ellipsis, 'expandable' \| 'rows' \| 'onExpand'>](#ellipsis) | false | [ellipsis](#ellipsis) |
|
||||
| keyboard | 添加键盘样式 | boolean | false | 4.3.0 |
|
||||
| mark | 添加标记样式 | boolean | false | |
|
||||
| onClick | 点击 Text 时的回调 | (event) => void | - | |
|
||||
|
@ -83,6 +83,10 @@ toc: false
|
||||
- https://mastergo-local-default.oss-cn-beijing.aliyuncs.com/ant-design-mastergo.svg
|
||||
- 可在「MasterGo」在线免费使用的全套组件和模板
|
||||
- https://mastergo.com/community/?utm_source=antdesign&utm_medium=link&utm_campaign=resource&cata_name=AntDesign
|
||||
- Raycast 拓展
|
||||
- https://gw.alipayobjects.com/zos/basement_prod/5edc7f4d-3302-4710-963b-7b6c77ea8d06.svg
|
||||
- mac 用户可使用 Raycast 快速打开 Ant Design 组件
|
||||
- https://www.raycast.com/crazyair/antd-open-browser
|
||||
|
||||
## 文章
|
||||
|
||||
|
@ -111,6 +111,7 @@
|
||||
"@ant-design/react-slick": "~0.29.1",
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@ctrl/tinycolor": "^3.4.0",
|
||||
"@testing-library/user-event": "^14.4.2",
|
||||
"classnames": "^2.2.6",
|
||||
"copy-to-clipboard": "^3.2.0",
|
||||
"dayjs": "^1.11.1",
|
||||
@ -125,7 +126,7 @@
|
||||
"rc-field-form": "~1.27.0",
|
||||
"rc-image": "~5.7.0",
|
||||
"rc-input": "~0.1.2",
|
||||
"rc-input-number": "~7.3.5",
|
||||
"rc-input-number": "~7.3.9",
|
||||
"rc-mentions": "~1.9.1",
|
||||
"rc-menu": "~9.6.3",
|
||||
"rc-motion": "^2.6.1",
|
||||
@ -136,7 +137,7 @@
|
||||
"rc-rate": "~2.9.0",
|
||||
"rc-resize-observer": "^1.2.0",
|
||||
"rc-segmented": "~2.1.0",
|
||||
"rc-select": "~14.1.1",
|
||||
"rc-select": "~14.1.13",
|
||||
"rc-slider": "~10.0.0",
|
||||
"rc-steps": "~4.1.0",
|
||||
"rc-switch": "~4.0.0",
|
||||
@ -230,7 +231,7 @@
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"immer": "^9.0.1",
|
||||
"immutability-helper": "^3.0.0",
|
||||
"inquirer": "^8.0.0",
|
||||
"inquirer": "^9.1.2",
|
||||
"intersection-observer": "^0.12.0",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"jest": "^28.0.3",
|
||||
|
@ -36,6 +36,7 @@ const MAINTAINERS = [
|
||||
'fireairforce',
|
||||
'kerm1it',
|
||||
'madccc',
|
||||
'MadCcc',
|
||||
].map(author => author.toLowerCase());
|
||||
|
||||
const cwd = process.cwd();
|
||||
|
@ -4,14 +4,7 @@ import { FormattedMessage, injectIntl } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { Row, Col, Affix, Tooltip } from 'antd';
|
||||
import { getChildren } from 'jsonml.js/lib/utils';
|
||||
import {
|
||||
CodeFilled,
|
||||
CodeOutlined,
|
||||
BugFilled,
|
||||
BugOutlined,
|
||||
ExperimentOutlined,
|
||||
ExperimentFilled,
|
||||
} from '@ant-design/icons';
|
||||
import { CodeFilled, CodeOutlined, BugFilled, BugOutlined } from '@ant-design/icons';
|
||||
import Demo from './Demo';
|
||||
import EditButton from './EditButton';
|
||||
import { ping, getMetaDescription } from '../utils';
|
||||
@ -25,7 +18,6 @@ class ComponentDoc extends React.Component {
|
||||
expandAll: false,
|
||||
visibleAll: process.env.NODE_ENV !== 'production',
|
||||
showRiddleButton: false,
|
||||
react17Demo: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
@ -108,7 +100,7 @@ class ComponentDoc extends React.Component {
|
||||
} = this.props;
|
||||
const { content, meta } = doc;
|
||||
const demoValues = Object.keys(demos).map(key => demos[key]);
|
||||
const { expandAll, visibleAll, showRiddleButton, react17Demo } = this.state;
|
||||
const { expandAll, visibleAll, showRiddleButton } = this.state;
|
||||
const isSingleCol = meta.cols === 1;
|
||||
const leftChildren = [];
|
||||
const rightChildren = [];
|
||||
@ -131,7 +123,6 @@ class ComponentDoc extends React.Component {
|
||||
location={location}
|
||||
theme={theme}
|
||||
setIframeTheme={setIframeTheme}
|
||||
react18={react17Demo}
|
||||
/>
|
||||
);
|
||||
if (index % 2 === 0 || isSingleCol) {
|
||||
@ -220,27 +211,6 @@ class ComponentDoc extends React.Component {
|
||||
<BugOutlined className={expandTriggerClass} onClick={this.handleVisibleToggle} />
|
||||
)}
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title={
|
||||
<FormattedMessage
|
||||
id={`app.component.examples.${
|
||||
react17Demo ? 'openDemoWithReact18' : 'openDemoNotReact18'
|
||||
}`}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{react17Demo ? (
|
||||
<ExperimentFilled
|
||||
className={expandTriggerClass}
|
||||
onClick={this.handleDemoVersionToggle}
|
||||
/>
|
||||
) : (
|
||||
<ExperimentOutlined
|
||||
className={expandTriggerClass}
|
||||
onClick={this.handleDemoVersionToggle}
|
||||
/>
|
||||
)}
|
||||
</Tooltip>
|
||||
</span>
|
||||
</h2>
|
||||
</section>
|
||||
|
@ -48,14 +48,13 @@ class Demo extends React.Component {
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
const { codeExpand, copied, copyTooltipOpen } = this.state;
|
||||
const { expand, theme, showRiddleButton, react18 } = this.props;
|
||||
const { expand, theme, showRiddleButton } = this.props;
|
||||
return (
|
||||
(codeExpand || expand) !== (nextState.codeExpand || nextProps.expand) ||
|
||||
copied !== nextState.copied ||
|
||||
copyTooltipOpen !== nextState.copyTooltipOpen ||
|
||||
nextProps.theme !== theme ||
|
||||
nextProps.showRiddleButton !== showRiddleButton ||
|
||||
nextProps.react18 !== react18
|
||||
nextProps.showRiddleButton !== showRiddleButton
|
||||
);
|
||||
}
|
||||
|
||||
@ -137,7 +136,6 @@ class Demo extends React.Component {
|
||||
intl: { locale },
|
||||
theme,
|
||||
showRiddleButton,
|
||||
react18,
|
||||
} = props;
|
||||
const { copied, copyTooltipOpen } = state;
|
||||
if (!this.liveDemo) {
|
||||
@ -206,13 +204,13 @@ class Demo extends React.Component {
|
||||
);
|
||||
|
||||
dependencies['@ant-design/icons'] = 'latest';
|
||||
dependencies.react = react18 ? '^18.0.0' : '^17.0.0';
|
||||
dependencies['react-dom'] = react18 ? '^18.0.0' : '^17.0.0';
|
||||
dependencies.react = '^18.0.0';
|
||||
dependencies['react-dom'] = '^18.0.0';
|
||||
|
||||
const codepenPrefillConfig = {
|
||||
title: `${localizedTitle} - antd@${dependencies.antd}`,
|
||||
html,
|
||||
js: `${react18 ? 'const { createRoot } = ReactDOM;\n' : ''}${sourceCode
|
||||
js: `${'const { createRoot } = ReactDOM;\n'}${sourceCode
|
||||
.replace(/import\s+(?:React,\s+)?{(\s+[^}]*\s+)}\s+from\s+'react'/, `const { $1 } = React;`)
|
||||
.replace(/import\s+{(\s+[^}]*\s+)}\s+from\s+'antd';/, 'const { $1 } = antd;')
|
||||
.replace(/import\s+{(\s+[^}]*\s+)}\s+from\s+'@ant-design\/icons';/, 'const { $1 } = icons;')
|
||||
@ -224,20 +222,17 @@ class Demo extends React.Component {
|
||||
'const { $1 } = ReactRouterDOM;',
|
||||
)
|
||||
.replace(/([A-Za-z]*)\s+as\s+([A-Za-z]*)/, '$1:$2')
|
||||
.replace(/export default/, 'const ComponentDemo =')}\n\n${
|
||||
react18
|
||||
? 'createRoot(mountNode).render(<ComponentDemo />)'
|
||||
: 'ReactDOM.render(<ComponentDemo />, mountNode)'
|
||||
};\n`,
|
||||
.replace(
|
||||
/export default/,
|
||||
'const ComponentDemo =',
|
||||
)}\n\ncreateRoot(mountNode).render(<ComponentDemo />);\n`,
|
||||
css: prefillStyle,
|
||||
editors: '001',
|
||||
// eslint-disable-next-line no-undef
|
||||
css_external: `https://unpkg.com/antd@${antdReproduceVersion}/dist/antd.css`,
|
||||
js_external: [
|
||||
react18 ? 'react@18/umd/react.development.js' : 'react@16.x/umd/react.development.js',
|
||||
react18
|
||||
? 'react-dom@18/umd/react-dom.development.js'
|
||||
: 'react-dom@16.x/umd/react-dom.development.js',
|
||||
'react@18/umd/react.development.js',
|
||||
'react-dom@18/umd/react-dom.development.js',
|
||||
// eslint-disable-next-line no-undef
|
||||
`antd@${antdReproduceVersion}/dist/antd-with-locales.js`,
|
||||
`@ant-design/icons/dist/index.umd.js`,
|
||||
@ -253,15 +248,10 @@ class Demo extends React.Component {
|
||||
title: `${localizedTitle} - antd@${dependencies.antd}`,
|
||||
js: `${
|
||||
/import React(\D*)from 'react';/.test(sourceCode) ? '' : `import React from 'react';\n`
|
||||
}${
|
||||
react18
|
||||
? `import { createRoot } from 'react-dom/client';\n`
|
||||
: `import ReactDOM from 'react-dom';\n`
|
||||
}${sourceCode.replace(/export default/, 'const ComponentDemo =')}\n\n${
|
||||
react18
|
||||
? 'createRoot(mountNode).render(<ComponentDemo />)'
|
||||
: 'ReactDOM.render(<ComponentDemo />, mountNode)'
|
||||
};\n`,
|
||||
}import { createRoot } from 'react-dom/client';\n${sourceCode.replace(
|
||||
/export default/,
|
||||
'const ComponentDemo =',
|
||||
)}\n\ncreateRoot(mountNode).render(<ComponentDemo />);\n`,
|
||||
css: prefillStyle,
|
||||
json: JSON.stringify(
|
||||
{
|
||||
@ -296,20 +286,12 @@ ${parsedSourceCode}
|
||||
.replace('</style>', '')
|
||||
.replace('<style>', '');
|
||||
|
||||
const indexJsContent = react18
|
||||
? `
|
||||
const indexJsContent = `
|
||||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import Demo from './demo';
|
||||
|
||||
createRoot(document.getElementById('container')).render(<Demo />);
|
||||
`
|
||||
: `
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Demo from './demo';
|
||||
|
||||
ReactDOM.render(<Demo />, document.getElementById('container'));
|
||||
`;
|
||||
|
||||
const codesandboxPackage = {
|
||||
@ -317,8 +299,8 @@ ReactDOM.render(<Demo />, document.getElementById('container'));
|
||||
main: 'index.js',
|
||||
dependencies: {
|
||||
...dependencies,
|
||||
react: react18 ? '^18.0.0' : '^16.14.0',
|
||||
'react-dom': react18 ? '^18.0.0' : '^16.14.0',
|
||||
react: '^18.0.0',
|
||||
'react-dom': '^18.0.0',
|
||||
'react-scripts': '^4.0.0',
|
||||
},
|
||||
devDependencies: {
|
||||
|
Loading…
Reference in New Issue
Block a user