mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-27 20:49:53 +08:00
commit
f71ee5384f
@ -7,7 +7,7 @@ version: 2.1
|
||||
jobs:
|
||||
test-argos-ci:
|
||||
docker:
|
||||
- image: circleci/node:16-browsers
|
||||
- image: cimg/node:lts-browsers
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
@ -16,9 +16,6 @@ jobs:
|
||||
- run:
|
||||
name: Install argos cli
|
||||
command: npm i fast-glob lodash @argos-ci/core
|
||||
- run:
|
||||
name: Install puppeteer
|
||||
command: node node_modules/puppeteer/install.js
|
||||
- run:
|
||||
name: Build dist file
|
||||
command: npm run dist
|
||||
|
@ -31,7 +31,7 @@ module.exports = {
|
||||
'@typescript-eslint/no-unused-vars': [2, { args: 'none' }],
|
||||
'no-unused-expressions': 'off',
|
||||
'@typescript-eslint/no-unused-expressions': 2,
|
||||
'@typescript-eslint/consistent-type-imports': 2,
|
||||
'@typescript-eslint/consistent-type-imports': [2, { disallowTypeAnnotations: false }],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -3,7 +3,6 @@ import { easeInOutCubic } from '../easings';
|
||||
describe('Test easings', () => {
|
||||
it('easeInOutCubic return value', () => {
|
||||
const nums: number[] = [];
|
||||
// eslint-disable-next-line no-plusplus
|
||||
for (let index = 0; index < 5; index++) {
|
||||
nums.push(easeInOutCubic(index, 1, 5, 4));
|
||||
}
|
||||
|
@ -2,17 +2,14 @@ import { waitFakeTimer } from '../../../tests/utils';
|
||||
import scrollTo from '../scrollTo';
|
||||
|
||||
describe('Test ScrollTo function', () => {
|
||||
let dateNowMock: jest.SpyInstance;
|
||||
const dateNowMock = jest.spyOn(Date, 'now');
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
dateNowMock = jest
|
||||
.spyOn(Date, 'now')
|
||||
.mockImplementationOnce(() => 0)
|
||||
.mockImplementationOnce(() => 1000);
|
||||
dateNowMock.mockReturnValueOnce(0).mockReturnValueOnce(1000);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
@ -21,7 +18,7 @@ describe('Test ScrollTo function', () => {
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllTimers();
|
||||
dateNowMock.mockRestore();
|
||||
dateNowMock.mockClear();
|
||||
});
|
||||
|
||||
it('test scrollTo', async () => {
|
||||
|
@ -4,7 +4,7 @@ import { render, fireEvent } from '../../../tests/utils';
|
||||
|
||||
describe('Table', () => {
|
||||
it('useSyncState', () => {
|
||||
const Test: React.FC = () => {
|
||||
const Test = () => {
|
||||
const [getVal, setVal] = useSyncState('light');
|
||||
return <span onClick={() => setVal('bamboo')}>{getVal()}</span>;
|
||||
};
|
||||
|
@ -236,15 +236,19 @@ describe('Wave component', () => {
|
||||
fakeDoc.appendChild(document.createElement('span'));
|
||||
expect(fakeDoc.childNodes).toHaveLength(2);
|
||||
|
||||
(container.querySelector('.bamboo') as any).getRootNode = () => fakeDoc;
|
||||
const elem = container.querySelector('.bamboo');
|
||||
|
||||
// Click should not throw
|
||||
fireEvent.click(container.querySelector('.bamboo')!);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
if (elem) {
|
||||
elem.getRootNode = () => fakeDoc;
|
||||
|
||||
expect(fakeDoc.querySelector('style')).toBeTruthy();
|
||||
// Click should not throw
|
||||
fireEvent.click(elem);
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect(fakeDoc.querySelector('style')).toBeTruthy();
|
||||
}
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { updateCSS } from 'rc-util/lib/Dom/dynamicCSS';
|
||||
import { composeRef, supportRef } from 'rc-util/lib/ref';
|
||||
import * as React from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
import type { ConfigConsumerProps, CSPConfig } from '../config-provider';
|
||||
import { ConfigConsumer, ConfigContext } from '../config-provider';
|
||||
import raf from './raf';
|
||||
@ -42,7 +41,7 @@ export interface WaveProps {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
class InternalWave extends React.Component<WaveProps> {
|
||||
class Wave extends React.Component<WaveProps> {
|
||||
static contextType = ConfigContext;
|
||||
|
||||
private instance?: {
|
||||
@ -237,8 +236,4 @@ class InternalWave extends React.Component<WaveProps> {
|
||||
}
|
||||
}
|
||||
|
||||
const Wave = forwardRef<InternalWave, WaveProps>((props, ref) => (
|
||||
<InternalWave ref={ref} {...props} />
|
||||
));
|
||||
|
||||
export default Wave;
|
||||
|
@ -29,17 +29,17 @@ const Content = () => {
|
||||
};
|
||||
|
||||
it('Delay loading timer in Button component', () => {
|
||||
const otherTimer: any = 9528;
|
||||
jest.spyOn(window, 'setTimeout').mockReturnValue(otherTimer);
|
||||
const otherTimer = 9528;
|
||||
jest.spyOn<Window, 'setTimeout'>(window, 'setTimeout').mockReturnValue(otherTimer);
|
||||
jest.restoreAllMocks();
|
||||
|
||||
const wrapper = render(<Content />);
|
||||
|
||||
const btnTimer: any = 9527;
|
||||
jest.spyOn(window, 'setTimeout').mockReturnValue(btnTimer);
|
||||
jest.spyOn(window, 'clearTimeout');
|
||||
const setTimeoutMock = window.setTimeout as any as jest.Mock;
|
||||
const clearTimeoutMock = window.clearTimeout as any as jest.Mock;
|
||||
const btnTimer = 9527;
|
||||
const setTimeoutMock = jest
|
||||
.spyOn<Window, 'setTimeout'>(window, 'setTimeout')
|
||||
.mockReturnValue(btnTimer);
|
||||
const clearTimeoutMock = jest.spyOn<Window, 'clearTimeout'>(window, 'clearTimeout');
|
||||
|
||||
// other component may call setTimeout or clearTimeout
|
||||
const setTimeoutCount = () => {
|
||||
@ -58,7 +58,11 @@ it('Delay loading timer in Button component', () => {
|
||||
|
||||
// trigger timer handler
|
||||
act(() => {
|
||||
setTimeoutMock.mock.calls[0][0]();
|
||||
const timerHandler = setTimeoutMock.mock.calls[0][0];
|
||||
|
||||
if (typeof timerHandler === 'function') {
|
||||
timerHandler();
|
||||
}
|
||||
});
|
||||
expect(setTimeoutCount()).toBe(1);
|
||||
expect(clearTimeoutCount()).toBe(0);
|
||||
|
@ -7,7 +7,6 @@ import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import { fireEvent, render, sleep } from '../../../tests/utils';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import type { SizeType } from '../../config-provider/SizeContext';
|
||||
|
||||
describe('Button', () => {
|
||||
mountTest(Button);
|
||||
@ -38,7 +37,8 @@ describe('Button', () => {
|
||||
it('warns if size is wrong', () => {
|
||||
resetWarned();
|
||||
const mockWarn = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
const size = 'who am I' as any as SizeType;
|
||||
const size = 'who am I';
|
||||
// @ts-expect-error: Type '"who am I"' is not assignable to type 'SizeType'.ts(2322)
|
||||
render(<Button.Group size={size} />);
|
||||
expect(mockWarn).toHaveBeenCalledWith('Warning: [antd: Button.Group] Invalid prop `size`.');
|
||||
|
||||
|
@ -1,18 +1,19 @@
|
||||
import React from 'react';
|
||||
import Button from '..';
|
||||
import { fireEvent, render, sleep } from '../../../tests/utils';
|
||||
import { fireEvent, render, sleep, assertsExist } from '../../../tests/utils';
|
||||
|
||||
// Mock Wave ref
|
||||
let waveInstanceMock: any;
|
||||
let waveInstanceMock: InstanceType<typeof import('../../_util/wave').default> | null;
|
||||
jest.mock('../../_util/wave', () => {
|
||||
const Wave = jest.requireActual('../../_util/wave');
|
||||
const Wave: typeof import('../../_util/wave') = jest.requireActual('../../_util/wave');
|
||||
const WaveComponent = Wave.default;
|
||||
|
||||
return {
|
||||
...Wave,
|
||||
__esModule: true,
|
||||
default: (props: any) => (
|
||||
default: (props: import('../../_util/wave').WaveProps) => (
|
||||
<WaveComponent
|
||||
ref={(node: any) => {
|
||||
ref={node => {
|
||||
waveInstanceMock = node;
|
||||
}}
|
||||
{...props}
|
||||
@ -77,12 +78,14 @@ describe('click wave effect', () => {
|
||||
|
||||
it('should run resetEffect in transitionstart', async () => {
|
||||
const wrapper = render(<Button type="primary">button</Button>);
|
||||
assertsExist(waveInstanceMock);
|
||||
const resetEffect = jest.spyOn(waveInstanceMock, 'resetEffect');
|
||||
await clickButton(wrapper);
|
||||
expect(resetEffect).toHaveBeenCalledTimes(1);
|
||||
fireEvent.click(wrapper.container.querySelector('.ant-btn')!);
|
||||
await sleep(10);
|
||||
expect(resetEffect).toHaveBeenCalledTimes(2);
|
||||
// @ts-expect-error: Property 'animationStart' is private and only accessible within class 'Wave'.ts(2341)
|
||||
waveInstanceMock.animationStart = false;
|
||||
fireEvent(wrapper.container.querySelector('.ant-btn')!, new Event('transitionstart'));
|
||||
expect(resetEffect).toHaveBeenCalledTimes(3);
|
||||
@ -91,6 +94,7 @@ describe('click wave effect', () => {
|
||||
|
||||
it('should handle transitionend', async () => {
|
||||
const wrapper = render(<Button type="primary">button</Button>);
|
||||
assertsExist(waveInstanceMock);
|
||||
const resetEffect = jest.spyOn(waveInstanceMock, 'resetEffect');
|
||||
await clickButton(wrapper);
|
||||
expect(resetEffect).toHaveBeenCalledTimes(1);
|
||||
|
@ -13,19 +13,16 @@ import Button from '../../radio/radioButton';
|
||||
import Select from '../../select';
|
||||
import Header, { type CalendarHeaderProps } from '../Header';
|
||||
|
||||
function calendarProps(): PickerPanelProps<any> {
|
||||
return (global as any).calendarProps;
|
||||
}
|
||||
|
||||
function calendarHeaderProps(): CalendarHeaderProps<any> {
|
||||
return (global as any).calendarHeaderProps;
|
||||
}
|
||||
const ref: {
|
||||
calendarProps?: PickerPanelProps<unknown>;
|
||||
calendarHeaderProps?: CalendarHeaderProps<unknown>;
|
||||
} = {};
|
||||
|
||||
jest.mock('../Header', () => {
|
||||
const HeaderModule = jest.requireActual('../Header');
|
||||
const HeaderComponent = HeaderModule.default;
|
||||
return (props: CalendarHeaderProps<any>) => {
|
||||
(global as any).calendarHeaderProps = props;
|
||||
ref.calendarHeaderProps = props;
|
||||
return <HeaderComponent {...props} />;
|
||||
};
|
||||
});
|
||||
@ -35,8 +32,8 @@ jest.mock('rc-picker', () => {
|
||||
const PickerPanelComponent = RcPicker.PickerPanel;
|
||||
return {
|
||||
...RcPicker,
|
||||
PickerPanel: (props: PickerPanelProps<any>) => {
|
||||
(global as any).calendarProps = props;
|
||||
PickerPanel: (props: PickerPanelProps<unknown>) => {
|
||||
ref.calendarProps = props;
|
||||
return <PickerPanelComponent {...props} />;
|
||||
},
|
||||
};
|
||||
@ -152,8 +149,8 @@ describe('Calendar', () => {
|
||||
it('getDateRange should returns a disabledDate function', () => {
|
||||
const validRange: [Moment.Moment, Moment.Moment] = [Moment('2018-02-02'), Moment('2018-05-18')];
|
||||
render(<Calendar validRange={validRange} defaultValue={Moment('2018-02-02')} />);
|
||||
expect(calendarProps().disabledDate?.(Moment('2018-06-02'))).toBe(true);
|
||||
expect(calendarProps().disabledDate?.(Moment('2018-04-02'))).toBe(false);
|
||||
expect(ref.calendarProps?.disabledDate?.(Moment('2018-06-02'))).toBe(true);
|
||||
expect(ref.calendarProps?.disabledDate?.(Moment('2018-04-02'))).toBe(false);
|
||||
});
|
||||
|
||||
it('validRange should work with disabledDate function', () => {
|
||||
@ -162,11 +159,11 @@ describe('Calendar', () => {
|
||||
<Calendar validRange={validRange} disabledDate={data => data.isSame(Moment('2018-02-03'))} />,
|
||||
);
|
||||
|
||||
expect(calendarProps().disabledDate?.(Moment('2018-02-01'))).toBe(true);
|
||||
expect(calendarProps().disabledDate?.(Moment('2018-02-02'))).toBe(false);
|
||||
expect(calendarProps().disabledDate?.(Moment('2018-02-03'))).toBe(true);
|
||||
expect(calendarProps().disabledDate?.(Moment('2018-02-04'))).toBe(false);
|
||||
expect(calendarProps().disabledDate?.(Moment('2018-06-01'))).toBe(true);
|
||||
expect(ref.calendarProps?.disabledDate?.(Moment('2018-02-01'))).toBe(true);
|
||||
expect(ref.calendarProps?.disabledDate?.(Moment('2018-02-02'))).toBe(false);
|
||||
expect(ref.calendarProps?.disabledDate?.(Moment('2018-02-03'))).toBe(true);
|
||||
expect(ref.calendarProps?.disabledDate?.(Moment('2018-02-04'))).toBe(false);
|
||||
expect(ref.calendarProps?.disabledDate?.(Moment('2018-06-01'))).toBe(true);
|
||||
});
|
||||
|
||||
it('Calendar MonthSelect should display correct label', () => {
|
||||
@ -181,9 +178,9 @@ describe('Calendar', () => {
|
||||
const monthMode = 'month';
|
||||
const yearMode = 'year';
|
||||
const wrapper = render(<Calendar />);
|
||||
expect(calendarHeaderProps().mode).toEqual(monthMode);
|
||||
expect(ref.calendarHeaderProps?.mode).toEqual(monthMode);
|
||||
wrapper.rerender(<Calendar mode={yearMode} />);
|
||||
expect(calendarHeaderProps().mode).toEqual(yearMode);
|
||||
expect(ref.calendarHeaderProps?.mode).toEqual(yearMode);
|
||||
});
|
||||
|
||||
it('Calendar should switch mode', () => {
|
||||
@ -191,9 +188,9 @@ describe('Calendar', () => {
|
||||
const yearMode = 'year';
|
||||
const onPanelChangeStub = jest.fn();
|
||||
const wrapper = render(<Calendar mode={yearMode} onPanelChange={onPanelChangeStub} />);
|
||||
expect(calendarHeaderProps().mode).toEqual(yearMode);
|
||||
expect(ref.calendarHeaderProps?.mode).toEqual(yearMode);
|
||||
wrapper.rerender(<Calendar mode={monthMode} onPanelChange={onPanelChangeStub} />);
|
||||
expect(calendarHeaderProps().mode).toEqual(monthMode);
|
||||
expect(ref.calendarHeaderProps?.mode).toEqual(monthMode);
|
||||
expect(onPanelChangeStub).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
@ -234,7 +231,7 @@ describe('Calendar', () => {
|
||||
const date = Moment(new Date(Date.UTC(2017, 7, 9, 8)));
|
||||
const wrapper = render(<Calendar onPanelChange={onPanelChange} value={date} />);
|
||||
|
||||
expect(calendarHeaderProps().mode).toBe('month');
|
||||
expect(ref.calendarHeaderProps?.mode).toBe('month');
|
||||
expect(wrapper.container.querySelectorAll('.ant-picker-date-panel').length).toBe(1);
|
||||
expect(wrapper.container.querySelectorAll('.ant-picker-month-panel').length).toBe(0);
|
||||
fireEvent.click(wrapper.container.querySelector('.ant-radio-button-input[value="year"]')!);
|
||||
|
@ -262,7 +262,7 @@ exports[`renders ./components/carousel/demo/basic.md extend context correctly 1`
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
4
|
||||
</h3>
|
||||
@ -282,7 +282,7 @@ exports[`renders ./components/carousel/demo/basic.md extend context correctly 1`
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
1
|
||||
</h3>
|
||||
@ -302,7 +302,7 @@ exports[`renders ./components/carousel/demo/basic.md extend context correctly 1`
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
2
|
||||
</h3>
|
||||
@ -322,7 +322,7 @@ exports[`renders ./components/carousel/demo/basic.md extend context correctly 1`
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
3
|
||||
</h3>
|
||||
@ -342,7 +342,7 @@ exports[`renders ./components/carousel/demo/basic.md extend context correctly 1`
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
4
|
||||
</h3>
|
||||
@ -362,7 +362,7 @@ exports[`renders ./components/carousel/demo/basic.md extend context correctly 1`
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
1
|
||||
</h3>
|
||||
@ -382,7 +382,7 @@ exports[`renders ./components/carousel/demo/basic.md extend context correctly 1`
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
2
|
||||
</h3>
|
||||
@ -402,7 +402,7 @@ exports[`renders ./components/carousel/demo/basic.md extend context correctly 1`
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
3
|
||||
</h3>
|
||||
@ -422,7 +422,7 @@ exports[`renders ./components/carousel/demo/basic.md extend context correctly 1`
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
4
|
||||
</h3>
|
||||
|
@ -262,7 +262,7 @@ exports[`renders ./components/carousel/demo/basic.md correctly 1`] = `
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
4
|
||||
</h3>
|
||||
@ -282,7 +282,7 @@ exports[`renders ./components/carousel/demo/basic.md correctly 1`] = `
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
1
|
||||
</h3>
|
||||
@ -302,7 +302,7 @@ exports[`renders ./components/carousel/demo/basic.md correctly 1`] = `
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
2
|
||||
</h3>
|
||||
@ -322,7 +322,7 @@ exports[`renders ./components/carousel/demo/basic.md correctly 1`] = `
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
3
|
||||
</h3>
|
||||
@ -342,7 +342,7 @@ exports[`renders ./components/carousel/demo/basic.md correctly 1`] = `
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
4
|
||||
</h3>
|
||||
@ -362,7 +362,7 @@ exports[`renders ./components/carousel/demo/basic.md correctly 1`] = `
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
1
|
||||
</h3>
|
||||
@ -382,7 +382,7 @@ exports[`renders ./components/carousel/demo/basic.md correctly 1`] = `
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
2
|
||||
</h3>
|
||||
@ -402,7 +402,7 @@ exports[`renders ./components/carousel/demo/basic.md correctly 1`] = `
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
3
|
||||
</h3>
|
||||
@ -422,7 +422,7 @@ exports[`renders ./components/carousel/demo/basic.md correctly 1`] = `
|
||||
tabindex="-1"
|
||||
>
|
||||
<h3
|
||||
style="height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
style="margin:0;height:160px;color:#fff;line-height:160px;text-align:center;background:#364d79"
|
||||
>
|
||||
4
|
||||
</h3>
|
||||
|
@ -18,6 +18,7 @@ import { Carousel } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
const contentStyle: React.CSSProperties = {
|
||||
margin: 0,
|
||||
height: '160px',
|
||||
color: '#fff',
|
||||
lineHeight: '160px',
|
||||
|
@ -180,6 +180,7 @@
|
||||
display: flex !important;
|
||||
justify-content: center;
|
||||
margin-right: 15%;
|
||||
margin-bottom: 0;
|
||||
margin-left: 15%;
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
|
@ -242,10 +242,10 @@ describe('CheckboxGroup', () => {
|
||||
const onChange = jest.fn();
|
||||
|
||||
const Demo: React.FC = () => {
|
||||
const [v, setV] = useState<string>('');
|
||||
const [v, setV] = useState('');
|
||||
|
||||
React.useEffect(() => {
|
||||
setTimeout(setV('1') as unknown as TimerHandler, 1000);
|
||||
setV('1');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
@ -1,7 +1,6 @@
|
||||
import type { ChangeEventHandler } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import classNames from 'classnames';
|
||||
import type { ColProps } from 'antd/es/grid';
|
||||
import type { FormInstance } from '..';
|
||||
@ -20,15 +19,7 @@ import Switch from '../../switch';
|
||||
import TreeSelect from '../../tree-select';
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import {
|
||||
fireEvent,
|
||||
render,
|
||||
sleep,
|
||||
act,
|
||||
screen,
|
||||
pureRender,
|
||||
waitFakeTimer,
|
||||
} from '../../../tests/utils';
|
||||
import { fireEvent, render, screen, pureRender, waitFakeTimer } from '../../../tests/utils';
|
||||
import ConfigProvider from '../../config-provider';
|
||||
import Drawer from '../../drawer';
|
||||
import zhCN from '../../locale/zh_CN';
|
||||
@ -51,27 +42,52 @@ describe('Form', () => {
|
||||
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
|
||||
const change = async (
|
||||
container: ReturnType<typeof render>['container'],
|
||||
index: number,
|
||||
value: string,
|
||||
executeMockTimer: boolean,
|
||||
) => {
|
||||
fireEvent.change(container.querySelectorAll('input')?.[index], { target: { value } });
|
||||
await sleep(200);
|
||||
// const change = async (
|
||||
// container: ReturnType<typeof render>['container'],
|
||||
// index: number,
|
||||
// value: string,
|
||||
// executeMockTimer: boolean,
|
||||
// ) => {
|
||||
// fireEvent.change(container.querySelectorAll('input')?.[index], { target: { value } });
|
||||
// await sleep(200);
|
||||
|
||||
if (executeMockTimer) {
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
act(() => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
}
|
||||
await sleep(1);
|
||||
// if (executeMockTimer) {
|
||||
// for (let i = 0; i < 10; i += 1) {
|
||||
// act(() => {
|
||||
// jest.runAllTimers();
|
||||
// });
|
||||
// }
|
||||
// await sleep(1);
|
||||
// }
|
||||
// };
|
||||
|
||||
const changeValue = async (
|
||||
input: HTMLElement | null | number,
|
||||
value: string,
|
||||
advTimer = 1000,
|
||||
) => {
|
||||
let element: HTMLElement;
|
||||
|
||||
if (typeof input === 'number') {
|
||||
element = document.querySelectorAll('input')[input];
|
||||
}
|
||||
|
||||
expect(element!).toBeTruthy();
|
||||
|
||||
fireEvent.change(element!, {
|
||||
target: {
|
||||
value,
|
||||
},
|
||||
});
|
||||
|
||||
if (advTimer) {
|
||||
await waitFakeTimer(advTimer / 20, 20);
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useRealTimers();
|
||||
document.body.innerHTML = '';
|
||||
jest.useFakeTimers();
|
||||
(scrollIntoView as any).mockReset();
|
||||
});
|
||||
|
||||
@ -80,6 +96,8 @@ describe('Form', () => {
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllTimers();
|
||||
jest.useRealTimers();
|
||||
errorSpy.mockRestore();
|
||||
warnSpy.mockRestore();
|
||||
(scrollIntoView as any).mockRestore();
|
||||
@ -100,34 +118,38 @@ describe('Form', () => {
|
||||
);
|
||||
|
||||
// user type something and clear
|
||||
await userEvent.type(screen.getByLabelText('test'), 'test');
|
||||
await userEvent.clear(screen.getByLabelText('test'));
|
||||
await changeValue(0, 'test');
|
||||
await changeValue(0, '');
|
||||
|
||||
// should show alert with correct message and show correct styles
|
||||
await expect(screen.findByRole('alert')).resolves.toHaveTextContent("'test' is required");
|
||||
expect(screen.getByLabelText('test')).toHaveClass('ant-input-status-error');
|
||||
expect(container.querySelectorAll('.ant-form-item-has-error').length).toBeTruthy();
|
||||
expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent(
|
||||
"'test' is required",
|
||||
);
|
||||
expect(container.querySelector('.ant-input-status-error')).toBeTruthy();
|
||||
expect(container.querySelector('.ant-form-item-has-error')).toBeTruthy();
|
||||
|
||||
expect(onChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should clean up', async () => {
|
||||
jest.useFakeTimers();
|
||||
const Demo: React.FC = () => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const onChange = async () => {
|
||||
// Wait a while and then some logic to validate
|
||||
await waitFakeTimer();
|
||||
|
||||
try {
|
||||
await form.validateFields();
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Form form={form} initialValues={{ aaa: '2' }}>
|
||||
<Form.Item name="aaa">
|
||||
<Input
|
||||
onChange={async () => {
|
||||
await sleep(0);
|
||||
try {
|
||||
await form.validateFields();
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Input onChange={onChange} />
|
||||
</Form.Item>
|
||||
<Form.Item shouldUpdate noStyle>
|
||||
{() => {
|
||||
@ -155,14 +177,18 @@ describe('Form', () => {
|
||||
};
|
||||
|
||||
const { container } = render(<Demo />);
|
||||
await change(container, 0, '1', true);
|
||||
expect(screen.getByRole('alert')).toHaveTextContent('aaa');
|
||||
await change(container, 0, '2', true);
|
||||
expect(screen.getByRole('alert')).toHaveTextContent('ccc');
|
||||
await change(container, 0, '1', true);
|
||||
expect(screen.getByRole('alert')).toHaveTextContent('aaa');
|
||||
|
||||
jest.useRealTimers();
|
||||
await changeValue(0, '1');
|
||||
await waitFakeTimer();
|
||||
expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent('aaa');
|
||||
|
||||
await changeValue(0, '2');
|
||||
await waitFakeTimer();
|
||||
expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent('ccc');
|
||||
|
||||
await changeValue(0, '1');
|
||||
await waitFakeTimer();
|
||||
expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent('aaa');
|
||||
});
|
||||
});
|
||||
|
||||
@ -176,6 +202,7 @@ describe('Form', () => {
|
||||
'Warning: [antd: Form.Item] `children` of render props only work with `shouldUpdate` or `dependencies`.',
|
||||
);
|
||||
});
|
||||
|
||||
it("`shouldUpdate` shouldn't work with `dependencies`", () => {
|
||||
render(
|
||||
<Form>
|
||||
@ -255,7 +282,6 @@ describe('Form', () => {
|
||||
});
|
||||
|
||||
it('input element should have the prop aria-describedby pointing to the help id when there are errors', async () => {
|
||||
jest.useFakeTimers();
|
||||
const { container } = pureRender(
|
||||
<Form>
|
||||
<Form.Item name="test" rules={[{ len: 3 }, { type: 'number' }]}>
|
||||
@ -263,15 +289,11 @@ describe('Form', () => {
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'Invalid number' } });
|
||||
|
||||
await waitFakeTimer();
|
||||
await changeValue(0, 'Invalid number');
|
||||
|
||||
expect(container.querySelector('input')?.getAttribute('aria-describedby')).toBe('test_help');
|
||||
expect(container.querySelector('.ant-form-item-explain')?.id).toBe('test_help');
|
||||
|
||||
jest.clearAllTimers();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('input element should have the prop aria-invalid when there are errors', async () => {
|
||||
@ -283,8 +305,7 @@ describe('Form', () => {
|
||||
</Form>,
|
||||
);
|
||||
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: 'Invalid number' } });
|
||||
await sleep(800);
|
||||
await changeValue(0, 'Invalid number');
|
||||
expect(container.querySelector('input')?.getAttribute('aria-invalid')).toBe('true');
|
||||
});
|
||||
|
||||
@ -407,7 +428,7 @@ describe('Form', () => {
|
||||
it('scrollToFirstError', async () => {
|
||||
const onFinishFailed = jest.fn();
|
||||
|
||||
render(
|
||||
const { container } = render(
|
||||
<Form scrollToFirstError={{ block: 'center' }} onFinishFailed={onFinishFailed}>
|
||||
<Form.Item name="test" rules={[{ required: true }]}>
|
||||
<input />
|
||||
@ -419,7 +440,9 @@ describe('Form', () => {
|
||||
);
|
||||
|
||||
expect(scrollIntoView).not.toHaveBeenCalled();
|
||||
await userEvent.click(screen.getByRole('button', { name: /submit/i }));
|
||||
fireEvent.submit(container.querySelector('form')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
const inputNode = document.getElementById('test');
|
||||
expect(scrollIntoView).toHaveBeenCalledWith(inputNode, {
|
||||
block: 'center',
|
||||
@ -452,7 +475,7 @@ describe('Form', () => {
|
||||
});
|
||||
|
||||
it('dynamic change required', async () => {
|
||||
render(
|
||||
const { container } = render(
|
||||
<Form>
|
||||
<Form.Item label="light" name="light" valuePropName="checked">
|
||||
<input type="checkbox" />
|
||||
@ -469,45 +492,24 @@ describe('Form', () => {
|
||||
);
|
||||
|
||||
// should not show alert by default
|
||||
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
|
||||
expect(container.querySelector('.ant-form-item-explain')).toBeFalsy();
|
||||
|
||||
// click to change the light field value to true
|
||||
await userEvent.click(screen.getByLabelText('light'));
|
||||
fireEvent.click(container.querySelector('input')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
// user input something and clear
|
||||
await userEvent.type(screen.getByLabelText('bamboo'), '1');
|
||||
await userEvent.clear(screen.getByLabelText('bamboo'));
|
||||
await changeValue(1, '1');
|
||||
await changeValue(1, '');
|
||||
|
||||
// should show alert says that the field is required
|
||||
await expect(screen.findByRole('alert')).resolves.toHaveTextContent("'bamboo' is required");
|
||||
});
|
||||
|
||||
it('should show alert with string when help is non-empty string', async () => {
|
||||
render(
|
||||
<Form>
|
||||
<Form.Item help="good">
|
||||
<input />
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent(
|
||||
"'bamboo' is required",
|
||||
);
|
||||
|
||||
await expect(screen.findByRole('alert')).resolves.toHaveTextContent('good');
|
||||
});
|
||||
|
||||
it('should show alert with empty string when help is empty string', async () => {
|
||||
render(
|
||||
<Form>
|
||||
<Form.Item help="">
|
||||
<input />
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
|
||||
await expect(screen.findByRole('alert')).resolves.toHaveTextContent('');
|
||||
});
|
||||
|
||||
describe('should show related className when customize help', () => {
|
||||
it('normal', () => {
|
||||
it('normal', async () => {
|
||||
const { container } = render(
|
||||
<Form>
|
||||
<Form.Item help="good">
|
||||
@ -515,10 +517,14 @@ describe('Form', () => {
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(container.querySelector('.ant-form-item-explain')).toHaveTextContent('good');
|
||||
expect(container.querySelector('.ant-form-item-with-help')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('empty string', () => {
|
||||
it('empty string', async () => {
|
||||
const { container } = render(
|
||||
<Form>
|
||||
<Form.Item help="">
|
||||
@ -526,6 +532,10 @@ describe('Form', () => {
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(container.querySelector('.ant-form-item-explain')).toHaveTextContent('');
|
||||
expect(container.querySelector('.ant-form-item-with-help')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -539,8 +549,6 @@ describe('Form', () => {
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/20706
|
||||
it('Error change should work', async () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
const { container } = render(
|
||||
<Form>
|
||||
<Form.Item
|
||||
@ -565,17 +573,16 @@ describe('Form', () => {
|
||||
|
||||
/* eslint-disable no-await-in-loop */
|
||||
for (let i = 0; i < 3; i += 1) {
|
||||
await change(container, 0, 'bamboo', true);
|
||||
await change(container, 0, '', true);
|
||||
await changeValue(0, 'bamboo');
|
||||
await changeValue(0, '');
|
||||
expect(container.querySelector('.ant-form-item-explain')?.textContent).toEqual(
|
||||
"'name' is required",
|
||||
);
|
||||
await change(container, 0, 'p', true);
|
||||
await sleep(100);
|
||||
|
||||
await changeValue(0, 'p');
|
||||
expect(container.querySelector('.ant-form-item-explain')?.textContent).toEqual('not a p');
|
||||
}
|
||||
/* eslint-enable */
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/20813
|
||||
@ -592,15 +599,17 @@ describe('Form', () => {
|
||||
);
|
||||
};
|
||||
|
||||
render(<App />);
|
||||
const { container } = render(<App />);
|
||||
|
||||
// should show initial text
|
||||
await expect(screen.findByRole('alert')).resolves.toHaveTextContent('');
|
||||
await waitFakeTimer();
|
||||
expect(container.querySelector('.ant-form-item-explain')).toHaveTextContent('');
|
||||
|
||||
await userEvent.click(screen.getByRole('button'));
|
||||
fireEvent.click(container.querySelector('button')!);
|
||||
|
||||
// should show bamboo alert without opacity and hide first alert with opacity: 0
|
||||
await expect(screen.findByRole('alert')).resolves.toHaveTextContent('bamboo');
|
||||
await waitFakeTimer();
|
||||
expect(container.querySelector('.ant-form-item-explain')).toHaveTextContent('bamboo');
|
||||
});
|
||||
|
||||
it('warning when use `dependencies` but `name` is empty & children is not a render props', () => {
|
||||
@ -616,8 +625,6 @@ describe('Form', () => {
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/20948
|
||||
it('not repeat render when Form.Item is not a real Field', async () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
const shouldNotRender = jest.fn();
|
||||
const StaticInput: React.FC<React.InputHTMLAttributes<HTMLInputElement>> = ({
|
||||
id,
|
||||
@ -663,9 +670,6 @@ describe('Form', () => {
|
||||
expect(container.querySelector<HTMLInputElement>('#changed')!.value).toEqual('bamboo');
|
||||
expect(shouldNotRender).toHaveBeenCalledTimes(1);
|
||||
expect(shouldRender).toHaveBeenCalledTimes(2);
|
||||
|
||||
jest.clearAllTimers();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('empty help should also render', () => {
|
||||
@ -692,14 +696,12 @@ describe('Form', () => {
|
||||
</Form>,
|
||||
);
|
||||
|
||||
await change(container, 0, '', true);
|
||||
await changeValue(0, '');
|
||||
expect(container.querySelector('.ant-form-item')).toHaveClass('ant-form-item-has-error');
|
||||
expect(container.querySelector('.ant-form-item-explain')!.textContent).toEqual('help');
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('clear validation message when', async () => {
|
||||
jest.useFakeTimers();
|
||||
const { container } = render(
|
||||
<Form>
|
||||
<Form.Item name="test" label="test" rules={[{ required: true, message: 'message' }]}>
|
||||
@ -708,27 +710,26 @@ describe('Form', () => {
|
||||
</Form>,
|
||||
);
|
||||
|
||||
await change(container, 0, '1', true);
|
||||
await changeValue(0, '1');
|
||||
expect(container.querySelectorAll('.ant-form-item-explain').length).toBeFalsy();
|
||||
|
||||
await change(container, 0, '', true);
|
||||
await changeValue(0, '');
|
||||
expect(container.querySelectorAll('.ant-form-item-explain').length).toBeTruthy();
|
||||
|
||||
await change(container, 0, '123', true);
|
||||
await sleep(800);
|
||||
await changeValue(0, '123');
|
||||
expect(container.querySelectorAll('.ant-form-item-explain').length).toBeFalsy();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/21167
|
||||
it('`require` without `name`', () => {
|
||||
render(
|
||||
const { container } = render(
|
||||
<Form.Item label="test" name="test" required>
|
||||
<input />
|
||||
</Form.Item>,
|
||||
);
|
||||
|
||||
expect(screen.getByTitle('test')).toHaveClass('ant-form-item-required');
|
||||
// expect(screen.getByTitle('test')).toHaveClass('ant-form-item-required');
|
||||
expect(container.querySelector('.ant-form-item-required')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('0 is a validate Field', () => {
|
||||
@ -757,7 +758,7 @@ describe('Form', () => {
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/21415
|
||||
it('should not throw error when Component.props.onChange is null', () => {
|
||||
it('should not throw error when Component.props.onChange is null', async () => {
|
||||
const CustomComponent: React.FC = () => (
|
||||
<input onChange={null as unknown as ChangeEventHandler<HTMLInputElement>} />
|
||||
);
|
||||
@ -768,10 +769,8 @@ describe('Form', () => {
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
const handle = async () => {
|
||||
await userEvent.type(screen.getByRole('textbox'), 'aaa');
|
||||
};
|
||||
expect(handle).not.toThrow();
|
||||
|
||||
await changeValue(0, 'aaa');
|
||||
});
|
||||
|
||||
it('change `help` should not warning', async () => {
|
||||
@ -797,91 +796,91 @@ describe('Form', () => {
|
||||
);
|
||||
};
|
||||
|
||||
render(<Demo />);
|
||||
await userEvent.click(screen.getByRole('button'));
|
||||
const { container } = render(<Demo />);
|
||||
fireEvent.click(container.querySelector('button')!);
|
||||
|
||||
expect(errorSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('`label` support template', async () => {
|
||||
render(
|
||||
const { container } = render(
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
<Form validateMessages={{ required: '${label} is good!' }}>
|
||||
<Form.Item name="test" label="Bamboo" rules={[{ required: true }]}>
|
||||
<input />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button htmlType="submit">Submit</Button>
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
|
||||
await userEvent.click(screen.getByRole('button'));
|
||||
fireEvent.submit(container.querySelector('form')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
await expect(screen.findByRole('alert')).resolves.toHaveTextContent('Bamboo is good!');
|
||||
expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent(
|
||||
'Bamboo is good!',
|
||||
);
|
||||
});
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/33691
|
||||
it('should keep upper locale in nested ConfigProvider', async () => {
|
||||
render(
|
||||
const { container } = render(
|
||||
<ConfigProvider locale={zhCN}>
|
||||
<ConfigProvider>
|
||||
<Form>
|
||||
<Form.Item name="test" label="Bamboo" rules={[{ required: true }]}>
|
||||
<input />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button htmlType="submit">Submit</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</ConfigProvider>
|
||||
</ConfigProvider>,
|
||||
);
|
||||
|
||||
await userEvent.click(screen.getByRole('button'));
|
||||
fireEvent.submit(container.querySelector('form')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
await expect(screen.findByRole('alert')).resolves.toHaveTextContent('请输入Bamboo');
|
||||
expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent(
|
||||
'请输入Bamboo',
|
||||
);
|
||||
});
|
||||
|
||||
it('`name` support template when label is not provided', async () => {
|
||||
render(
|
||||
const { container } = render(
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
<Form validateMessages={{ required: '${label} is good!' }}>
|
||||
<Form.Item name="Bamboo" rules={[{ required: true }]}>
|
||||
<input />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button htmlType="submit">Submit</Button>
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
|
||||
await userEvent.click(screen.getByRole('button'));
|
||||
fireEvent.submit(container.querySelector('form')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
await expect(screen.findByRole('alert')).resolves.toHaveTextContent('Bamboo is good!');
|
||||
expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent(
|
||||
'Bamboo is good!',
|
||||
);
|
||||
});
|
||||
|
||||
it('`messageVariables` support validate', async () => {
|
||||
render(
|
||||
const { container } = render(
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
<Form validateMessages={{ required: '${label} is good!' }}>
|
||||
<Form.Item name="test" messageVariables={{ label: 'Bamboo' }} rules={[{ required: true }]}>
|
||||
<input />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button htmlType="submit">Submit</Button>
|
||||
</Form.Item>
|
||||
</Form>,
|
||||
);
|
||||
|
||||
await userEvent.click(screen.getByRole('button'));
|
||||
fireEvent.submit(container.querySelector('form')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
await expect(screen.findByRole('alert')).resolves.toHaveTextContent('Bamboo is good!');
|
||||
expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent(
|
||||
'Bamboo is good!',
|
||||
);
|
||||
});
|
||||
|
||||
it('validation message should has alert role', async () => {
|
||||
// https://github.com/ant-design/ant-design/issues/25711
|
||||
render(
|
||||
const { container } = render(
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
<Form validateMessages={{ required: 'name is good!' }}>
|
||||
<Form.Item name="test" rules={[{ required: true }]}>
|
||||
@ -893,9 +892,12 @@ describe('Form', () => {
|
||||
</Form>,
|
||||
);
|
||||
|
||||
await userEvent.click(screen.getByRole('button'));
|
||||
fireEvent.submit(container.querySelector('form')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
await expect(screen.findByRole('alert')).resolves.toHaveTextContent('name is good!');
|
||||
expect(container.querySelector('.ant-form-item-explain-error')).toHaveTextContent(
|
||||
'name is good!',
|
||||
);
|
||||
});
|
||||
|
||||
it('return same form instance', async () => {
|
||||
@ -917,11 +919,12 @@ describe('Form', () => {
|
||||
);
|
||||
};
|
||||
|
||||
pureRender(<App />);
|
||||
const { container } = pureRender(<App />);
|
||||
|
||||
for (let i = 0; i < 5; i += 1) {
|
||||
fireEvent.click(container.querySelector('button')!);
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await userEvent.click(screen.getByRole('button'));
|
||||
await waitFakeTimer();
|
||||
}
|
||||
|
||||
expect(instances.size).toBe(1);
|
||||
@ -942,12 +945,13 @@ describe('Form', () => {
|
||||
</Form.Item>
|
||||
</Form>
|
||||
);
|
||||
pureRender(<Demo />);
|
||||
const { container } = pureRender(<Demo />);
|
||||
renderTimes = 0;
|
||||
jest.clearAllMocks();
|
||||
fireEvent.change(screen.getByLabelText('username'), { target: { value: 'a' } });
|
||||
|
||||
await changeValue(0, 'a');
|
||||
|
||||
expect(renderTimes).toEqual(1);
|
||||
expect(screen.getByLabelText('username')).toHaveValue('a');
|
||||
expect(container.querySelector('input')).toHaveValue('a');
|
||||
});
|
||||
|
||||
it('should warning with `defaultValue`', () => {
|
||||
@ -979,13 +983,15 @@ describe('Form', () => {
|
||||
</Form>
|
||||
);
|
||||
|
||||
const { rerender } = render(<Demo showA />);
|
||||
const { container, rerender } = render(<Demo showA />);
|
||||
|
||||
await expect(screen.findByRole('alert')).resolves.toBeInTheDocument();
|
||||
await waitFakeTimer();
|
||||
expect(container.querySelector('.ant-form-item-explain')).toBeTruthy();
|
||||
|
||||
rerender(<Demo showA={false} />);
|
||||
|
||||
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
|
||||
await waitFakeTimer();
|
||||
expect(container.querySelector('.ant-form-item-explain')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('no warning of initialValue & getValueProps & preserve', () => {
|
||||
@ -1000,7 +1006,7 @@ describe('Form', () => {
|
||||
});
|
||||
|
||||
it('should customize id when pass with id', () => {
|
||||
render(
|
||||
const { container } = render(
|
||||
<Form>
|
||||
<Form.Item name="light">
|
||||
<Input id="bamboo" />
|
||||
@ -1008,11 +1014,11 @@ describe('Form', () => {
|
||||
</Form>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole('textbox')).toHaveAttribute('id', 'bamboo');
|
||||
expect(container.querySelector('input')!.id).toEqual('bamboo');
|
||||
});
|
||||
|
||||
it('should trigger validate when onBlur when pass validateTrigger onBlur', async () => {
|
||||
render(
|
||||
const { container } = render(
|
||||
<Form validateTrigger="onBlur">
|
||||
<Form.Item name="light" label="light" rules={[{ len: 3 }]}>
|
||||
<Input />
|
||||
@ -1021,14 +1027,14 @@ describe('Form', () => {
|
||||
);
|
||||
|
||||
// type a invalidate value, not trigger validation
|
||||
await userEvent.type(screen.getByRole('textbox'), '7777');
|
||||
|
||||
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
|
||||
await changeValue(0, '7777');
|
||||
expect(container.querySelector('.ant-form-item-explain')).toBeFalsy();
|
||||
|
||||
// tab(onBlur) the input field, trigger and see the alert
|
||||
fireEvent.blur(screen.getByRole('textbox'));
|
||||
fireEvent.blur(container.querySelector('input')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
await expect(screen.findByRole('alert')).resolves.toBeInTheDocument();
|
||||
expect(container.querySelector('.ant-form-item-explain')).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('Form item hidden', () => {
|
||||
@ -1056,7 +1062,7 @@ describe('Form', () => {
|
||||
});
|
||||
|
||||
it('legacy hideRequiredMark', () => {
|
||||
render(
|
||||
const { container } = render(
|
||||
<Form hideRequiredMark role="form">
|
||||
<Form.Item name="light" label="light" required>
|
||||
<Input />
|
||||
@ -1064,7 +1070,7 @@ describe('Form', () => {
|
||||
</Form>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole('form')).toHaveClass('ant-form-hide-required-mark');
|
||||
expect(container.querySelector('form')!).toHaveClass('ant-form-hide-required-mark');
|
||||
});
|
||||
|
||||
it('form should support disabled', () => {
|
||||
@ -1137,7 +1143,7 @@ describe('Form', () => {
|
||||
});
|
||||
|
||||
it('_internalItemRender api test', () => {
|
||||
render(
|
||||
const { container } = render(
|
||||
<Form>
|
||||
<Form.Item
|
||||
name="light"
|
||||
@ -1159,7 +1165,7 @@ describe('Form', () => {
|
||||
</Form>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole('heading')).toHaveTextContent(/warning title/i);
|
||||
expect(container.querySelector('h1')!).toHaveTextContent(/warning title/i);
|
||||
});
|
||||
|
||||
it('Form Item element id will auto add form_item prefix if form name is empty and item name is in the black list', async () => {
|
||||
@ -1198,16 +1204,17 @@ describe('Form', () => {
|
||||
);
|
||||
};
|
||||
|
||||
const { rerender } = render(<Demo />);
|
||||
const { container, rerender } = render(<Demo />);
|
||||
expect(mockFn).toHaveBeenCalled();
|
||||
expect((Util.getFieldId as () => string)()).toBe(itemName);
|
||||
|
||||
// make sure input id is parentNode
|
||||
expect(screen.getByLabelText(itemName)).toHaveAccessibleName(itemName);
|
||||
|
||||
await userEvent.click(screen.getByRole('button'));
|
||||
fireEvent.click(container.querySelector('button')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(screen.getByRole('button')).toHaveTextContent('show');
|
||||
expect(container.querySelector('button')!).toHaveTextContent('show');
|
||||
|
||||
mockFn.mockRestore();
|
||||
|
||||
@ -1217,7 +1224,7 @@ describe('Form', () => {
|
||||
|
||||
describe('tooltip', () => {
|
||||
it('ReactNode', async () => {
|
||||
render(
|
||||
const { container } = render(
|
||||
<Form>
|
||||
<Form.Item label="light" tooltip={<span>Bamboo</span>}>
|
||||
<Input />
|
||||
@ -1225,21 +1232,14 @@ describe('Form', () => {
|
||||
</Form>,
|
||||
);
|
||||
|
||||
await userEvent.hover(screen.getByRole('img', { name: 'question-circle' }));
|
||||
await expect(screen.findByRole('tooltip')).resolves.toMatchInlineSnapshot(`
|
||||
<div
|
||||
class="ant-tooltip-inner"
|
||||
role="tooltip"
|
||||
>
|
||||
<span>
|
||||
Bamboo
|
||||
</span>
|
||||
</div>
|
||||
`);
|
||||
fireEvent.mouseEnter(container.querySelector('.anticon-question-circle')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(container.querySelector('.ant-tooltip-inner')).toHaveTextContent('Bamboo');
|
||||
});
|
||||
|
||||
it('config tooltip should show when hover on icon', async () => {
|
||||
render(
|
||||
const { container } = render(
|
||||
<Form>
|
||||
<Form.Item label="light" tooltip={{ title: 'Bamboo' }}>
|
||||
<Input />
|
||||
@ -1247,9 +1247,10 @@ describe('Form', () => {
|
||||
</Form>,
|
||||
);
|
||||
|
||||
await userEvent.hover(screen.getByRole('img', { name: 'question-circle' }));
|
||||
fireEvent.mouseEnter(container.querySelector('.anticon-question-circle')!);
|
||||
await waitFakeTimer();
|
||||
|
||||
await expect(screen.findByRole('tooltip')).resolves.toHaveTextContent('Bamboo');
|
||||
expect(container.querySelector('.ant-tooltip-inner')).toHaveTextContent('Bamboo');
|
||||
});
|
||||
});
|
||||
|
||||
@ -1269,20 +1270,17 @@ describe('Form', () => {
|
||||
</Form>,
|
||||
);
|
||||
|
||||
await userEvent.type(screen.getByLabelText('test'), 'test');
|
||||
await userEvent.clear(screen.getByLabelText('test'));
|
||||
await changeValue(0, 'test');
|
||||
await changeValue(0, '');
|
||||
|
||||
await sleep(1000);
|
||||
|
||||
expect(container.querySelectorAll('.ant-form-item-with-help').length).toBeTruthy();
|
||||
expect(container.querySelectorAll('.ant-form-item-has-warning').length).toBeTruthy();
|
||||
expect(container.querySelector('.ant-form-item-with-help')).toBeTruthy();
|
||||
expect(container.querySelector('.ant-form-item-has-warning')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('not warning when remove on validate', async () => {
|
||||
jest.useFakeTimers();
|
||||
let rejectFn: (reason?: any) => void = jest.fn();
|
||||
|
||||
const { container, unmount } = render(
|
||||
const { unmount } = render(
|
||||
<Form>
|
||||
<Form.Item>
|
||||
<Form.Item
|
||||
@ -1304,7 +1302,7 @@ describe('Form', () => {
|
||||
</Form>,
|
||||
);
|
||||
|
||||
await change(container, 0, '', true);
|
||||
await changeValue(0, '');
|
||||
|
||||
unmount();
|
||||
|
||||
@ -1312,8 +1310,6 @@ describe('Form', () => {
|
||||
rejectFn(new Error('delay failed'));
|
||||
|
||||
expect(errorSpy).not.toHaveBeenCalled();
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
describe('form colon', () => {
|
||||
@ -1437,8 +1433,6 @@ describe('Form', () => {
|
||||
});
|
||||
|
||||
it('Form.Item.useStatus should work', async () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
const {
|
||||
Item: { useStatus },
|
||||
} = Form;
|
||||
@ -1495,9 +1489,6 @@ describe('Form', () => {
|
||||
expect(container.querySelector('.custom-input-required')?.classList).toContain(
|
||||
'custom-input-status-error',
|
||||
);
|
||||
|
||||
jest.clearAllTimers();
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('item customize margin', async () => {
|
||||
@ -1513,9 +1504,8 @@ describe('Form', () => {
|
||||
</Form>,
|
||||
);
|
||||
|
||||
fireEvent.change(container.querySelector('input')!, { target: { value: '' } });
|
||||
await changeValue(0, '');
|
||||
|
||||
await sleep(0);
|
||||
computeSpy.mockRestore();
|
||||
|
||||
expect(container.querySelector('.ant-form-item-margin-offset')).toHaveStyle({
|
||||
|
@ -5083,6 +5083,34 @@ exports[`renders ./components/input/demo/borderless-debug.md extend context corr
|
||||
RMB
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
style="border:2px solid #000"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -1300,6 +1300,34 @@ exports[`renders ./components/input/demo/borderless-debug.md correctly 1`] = `
|
||||
RMB
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn"
|
||||
style="border:2px solid #000"
|
||||
>
|
||||
<textarea
|
||||
class="ant-input"
|
||||
/>
|
||||
<span
|
||||
aria-label="close-circle"
|
||||
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
|
||||
role="button"
|
||||
tabindex="-1"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="close-circle"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
---
|
||||
order: 98
|
||||
title:
|
||||
zh-CN: Borderless Debug
|
||||
en-US: Borderless Debug
|
||||
zh-CN: Style Debug
|
||||
en-US: Style Debug
|
||||
debug: true
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
Buggy!
|
||||
Buggy! 测试一些踩过的样式坑。
|
||||
|
||||
## en-US
|
||||
|
||||
@ -29,6 +29,7 @@ const App: React.FC = () => (
|
||||
<Input placeholder="Unbordered" bordered={false} allowClear />
|
||||
<Input prefix="¥" suffix="RMB" bordered={false} />
|
||||
<Input prefix="¥" suffix="RMB" disabled bordered={false} />
|
||||
<TextArea allowClear style={{ border: '2px solid #000' }} />
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@ -28,8 +28,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
> input.@{ant-prefix}-input {
|
||||
padding: 0;
|
||||
> .@{ant-prefix}-input {
|
||||
font-size: inherit;
|
||||
border: none;
|
||||
outline: none;
|
||||
@ -37,6 +36,10 @@
|
||||
&:focus {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
&:not(textarea) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&::before {
|
||||
|
@ -2,8 +2,8 @@
|
||||
@input-prefix-cls: ~'@{ant-prefix}-input';
|
||||
|
||||
// ========================= Input =========================
|
||||
.@{iconfont-css-prefix}.@{ant-prefix}-input-clear-icon,
|
||||
.@{ant-prefix}-input-clear-icon {
|
||||
.@{iconfont-css-prefix}.@{input-prefix-cls}-clear-icon,
|
||||
.@{input-prefix-cls}-clear-icon {
|
||||
margin: 0;
|
||||
color: @disabled-color;
|
||||
font-size: @font-size-sm;
|
||||
@ -31,11 +31,10 @@
|
||||
}
|
||||
|
||||
// ======================= TextArea ========================
|
||||
.@{ant-prefix}-input-affix-wrapper-textarea-with-clear-btn {
|
||||
padding: 0 !important;
|
||||
border: 0 !important;
|
||||
.@{input-prefix-cls}-affix-wrapper.@{input-prefix-cls}-affix-wrapper-textarea-with-clear-btn {
|
||||
padding: 0;
|
||||
|
||||
.@{ant-prefix}-input-clear-icon {
|
||||
.@{input-prefix-cls}-clear-icon {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
|
@ -1,9 +1,12 @@
|
||||
/* eslint-disable no-template-curly-in-string */
|
||||
import Pagination from 'rc-pagination/lib/locale/it_IT';
|
||||
import Calendar from '../calendar/locale/it_IT';
|
||||
import DatePicker from '../date-picker/locale/it_IT';
|
||||
import type { Locale } from '../locale-provider';
|
||||
import TimePicker from '../time-picker/locale/it_IT';
|
||||
|
||||
const typeTemplate = ' ${label} non è un ${type} valido';
|
||||
|
||||
const localeValues: Locale = {
|
||||
locale: 'it',
|
||||
Pagination,
|
||||
@ -17,11 +20,17 @@ const localeValues: Locale = {
|
||||
filterTitle: 'Menù Filtro',
|
||||
filterConfirm: 'OK',
|
||||
filterReset: 'Reset',
|
||||
selectNone: 'Deseleziona tutto',
|
||||
selectionAll: 'Seleziona tutto',
|
||||
filterEmptyText: 'Senza filtri',
|
||||
filterCheckall: 'Seleziona tutti',
|
||||
filterSearchPlaceholder: 'Cerca nei filtri',
|
||||
emptyText: 'Senza dati',
|
||||
selectAll: 'Seleziona pagina corrente',
|
||||
selectInvert: 'Inverti selezione nella pagina corrente',
|
||||
selectNone: 'Deseleziona tutto',
|
||||
selectionAll: 'Seleziona tutto',
|
||||
sortTitle: 'Ordina',
|
||||
expand: 'Espandi riga',
|
||||
collapse: 'Comprimi riga ',
|
||||
triggerDesc: 'Clicca per ordinare in modo discendente',
|
||||
triggerAsc: 'Clicca per ordinare in modo ascendente',
|
||||
cancelSort: "Clicca per eliminare l'ordinamento",
|
||||
@ -36,16 +45,23 @@ const localeValues: Locale = {
|
||||
cancelText: 'Annulla',
|
||||
},
|
||||
Transfer: {
|
||||
titles: ['', ''],
|
||||
searchPlaceholder: 'Cerca qui',
|
||||
itemUnit: 'elemento',
|
||||
itemsUnit: 'elementi',
|
||||
remove: 'Elimina',
|
||||
selectCurrent: 'Seleziona la pagina corrente',
|
||||
removeCurrent: 'Rimuovi la pagina corrente',
|
||||
selectAll: 'Seleziona tutti i dati',
|
||||
removeAll: 'Rimuovi tutti i dati',
|
||||
selectInvert: 'Inverti la pagina corrente',
|
||||
},
|
||||
Upload: {
|
||||
uploading: 'Caricamento...',
|
||||
removeFile: 'Rimuovi il file',
|
||||
uploadError: 'Errore di caricamento',
|
||||
previewFile: 'Anteprima file',
|
||||
downloadFile: 'Download file',
|
||||
downloadFile: 'Scarica file',
|
||||
},
|
||||
Empty: {
|
||||
description: 'Nessun dato',
|
||||
@ -59,6 +75,62 @@ const localeValues: Locale = {
|
||||
copied: 'copia effettuata',
|
||||
expand: 'espandi',
|
||||
},
|
||||
PageHeader: {
|
||||
back: 'Torna',
|
||||
},
|
||||
Form: {
|
||||
optional: '(opzionale)',
|
||||
defaultValidateMessages: {
|
||||
default: 'Errore di convalida del campo ${label}',
|
||||
required: 'Si prega di inserire ${label}',
|
||||
enum: '${label} deve essere uno di [${enum}]',
|
||||
whitespace: '${label} non può essere un carattere vuoto',
|
||||
date: {
|
||||
format: 'Il formato della data ${label} non è valido',
|
||||
parse: '${label} non può essere convertito in una data',
|
||||
invalid: '${label} non è una data valida',
|
||||
},
|
||||
types: {
|
||||
string: typeTemplate,
|
||||
method: typeTemplate,
|
||||
array: typeTemplate,
|
||||
object: typeTemplate,
|
||||
number: typeTemplate,
|
||||
date: typeTemplate,
|
||||
boolean: typeTemplate,
|
||||
integer: typeTemplate,
|
||||
float: typeTemplate,
|
||||
regexp: typeTemplate,
|
||||
email: typeTemplate,
|
||||
url: typeTemplate,
|
||||
hex: typeTemplate,
|
||||
},
|
||||
string: {
|
||||
len: '${label} deve avere ${len} caratteri',
|
||||
min: '${label} deve contenere almeno ${min} caratteri',
|
||||
max: '${label} deve contenere fino a ${max} caratteri',
|
||||
range: '${label} deve contenere tra ${min}-${max} caratteri',
|
||||
},
|
||||
number: {
|
||||
len: '${label} deve essere uguale a ${len}',
|
||||
min: '${label} valore minimo è ${min}',
|
||||
max: '${label} valor e massimo è ${max}',
|
||||
range: '${label} deve essere compreso tra ${min}-${max}',
|
||||
},
|
||||
array: {
|
||||
len: 'Deve essere ${len} ${label}',
|
||||
min: 'Almeno ${min} ${label}',
|
||||
max: 'Massimo ${max} ${label}',
|
||||
range: 'Il totale di ${label} deve essere compreso tra ${min}-${max}',
|
||||
},
|
||||
pattern: {
|
||||
mismatch: '${label} non corrisponde al modello ${pattern}',
|
||||
},
|
||||
},
|
||||
},
|
||||
Image: {
|
||||
preview: 'Anteprima',
|
||||
},
|
||||
};
|
||||
|
||||
export default localeValues;
|
||||
|
@ -1,6 +1,9 @@
|
||||
import classNames from 'classnames';
|
||||
import RcMentions from 'rc-mentions';
|
||||
import type { MentionsProps as RcMentionsProps } from 'rc-mentions/lib/Mentions';
|
||||
import type {
|
||||
MentionsProps as RcMentionsProps,
|
||||
MentionsRef as RcMentionsRef,
|
||||
} from 'rc-mentions/lib/Mentions';
|
||||
import { composeRef } from 'rc-util/lib/ref';
|
||||
import * as React from 'react';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
@ -29,6 +32,8 @@ export interface MentionProps extends RcMentionsProps {
|
||||
status?: InputStatus;
|
||||
}
|
||||
|
||||
export interface MentionsRef extends RcMentionsRef {}
|
||||
|
||||
export interface MentionState {
|
||||
focused: boolean;
|
||||
}
|
||||
@ -44,12 +49,12 @@ interface MentionsEntity {
|
||||
}
|
||||
|
||||
interface CompoundedComponent
|
||||
extends React.ForwardRefExoticComponent<MentionProps & React.RefAttributes<HTMLElement>> {
|
||||
extends React.ForwardRefExoticComponent<MentionProps & React.RefAttributes<MentionsRef>> {
|
||||
Option: typeof Option;
|
||||
getMentions: (value: string, config?: MentionsConfig) => MentionsEntity[];
|
||||
}
|
||||
|
||||
const InternalMentions: React.ForwardRefRenderFunction<unknown, MentionProps> = (
|
||||
const InternalMentions: React.ForwardRefRenderFunction<MentionsRef, MentionProps> = (
|
||||
{
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
@ -64,7 +69,7 @@ const InternalMentions: React.ForwardRefRenderFunction<unknown, MentionProps> =
|
||||
ref,
|
||||
) => {
|
||||
const [focused, setFocused] = React.useState(false);
|
||||
const innerRef = React.useRef<HTMLElement>();
|
||||
const innerRef = React.useRef<MentionsRef>();
|
||||
const mergedRef = composeRef(ref, innerRef);
|
||||
const { getPrefixCls, renderEmpty, direction } = React.useContext(ConfigContext);
|
||||
const {
|
||||
@ -163,7 +168,9 @@ const InternalMentions: React.ForwardRefRenderFunction<unknown, MentionProps> =
|
||||
return mentions;
|
||||
};
|
||||
|
||||
const Mentions = React.forwardRef<unknown, MentionProps>(InternalMentions) as CompoundedComponent;
|
||||
const Mentions = React.forwardRef<MentionsRef, MentionProps>(
|
||||
InternalMentions,
|
||||
) as CompoundedComponent;
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Mentions.displayName = 'Mentions';
|
||||
}
|
||||
|
@ -98,7 +98,7 @@
|
||||
"test-node": "jest --config .jest.node.js --cache=false",
|
||||
"tsc": "tsc --noEmit",
|
||||
"site:test": "jest --config .jest.site.js --cache=false --force-exit",
|
||||
"test-image": "npm run dist && jest --config .jest.image.js --no-cache -i -u",
|
||||
"test-image": "npm run dist && jest --config .jest.image.js -i -u",
|
||||
"argos": "node ./scripts/argos-upload.js",
|
||||
"version": "node ./scripts/generate-version",
|
||||
"install-react-16": "npm i --no-save --legacy-peer-deps react@16 react-dom@16",
|
||||
|
@ -6,6 +6,11 @@ import { render, act } from '@testing-library/react';
|
||||
import { _rs as onLibResize } from 'rc-resize-observer/lib/utils/observerUtil';
|
||||
import { _rs as onEsResize } from 'rc-resize-observer/es/utils/observerUtil';
|
||||
|
||||
export function assertsExist<T>(item: T | null | undefined): asserts item is T {
|
||||
expect(item).not.toBeUndefined();
|
||||
expect(item).not.toBeNull();
|
||||
}
|
||||
|
||||
export function setMockDate(dateString = '2017-09-18T03:30:07.795') {
|
||||
MockDate.set(dateString);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user