Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support v5 of AntD #1261 #1373

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@
"@typescript-eslint/require-await": "off",
"@typescript-eslint/restrict-plus-operands": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/prefer-ts-expect-error": "off",
"no-constant-binary-expression": "off",

"consistent-return": "off",
"eslint-comments/require-description": "off",
"import/default": "off",
Expand Down
6,051 changes: 2,493 additions & 3,558 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 19 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,36 @@
"test": "jest"
},
"devDependencies": {
"@ant-design/icons": "^5.5.1",
"@babel/eslint-plugin": "^7.13.16",
"@mui/material": "^6.1.5",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "12.1.5",
"@testing-library/user-event": "14.4.3",
"@types/jest": "26.0.20",
"@types/node": "18.19.50",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^8.11.0",
"antd": "^4.24.16",
"dayjs": "^1.11.13",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-config-vazco": "7.4.0",
"eslint-import-resolver-alias": "1.1.2",
"eslint-import-resolver-typescript": "2.3.0",
"husky": "8.0.1",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-jsx-a11y": "^6.10.1",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react": "^7.27.1",
"eslint-plugin-react-hooks": "^4.2.0",
"husky": "^8.0.1",
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
"lint-staged": "13.0.3",
"moment": "^2.30.1",
"prettier": "^2.8.8",
"react": "^18.3.1",
"rimraf": "3.0.2",
"stylelint": "13.8.0",
"stylelint-config-prettier": "8.0.2",
Expand Down
4 changes: 2 additions & 2 deletions packages/uniforms-antd/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"src/*.tsx"
],
"peerDependencies": {
"@ant-design/icons": "^4.0.0",
"antd": "^4.0.0",
"@ant-design/icons": "<5.0.0",
"antd": "<5.0.0",
"react": "^18.0.0 || ^17.0.0 || ^16.8.0"
},
"dependencies": {
Expand Down
1 change: 1 addition & 0 deletions packages/uniforms-antd/src/NumField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ function Num(props: NumFieldProps) {
}}
placeholder={props.placeholder}
readOnly={props.readOnly}
// @ts-expect-error: `inputRef` is `unknown`
ref={props.inputRef}
step={props.step || (props.decimal ? 0.01 : 1)}
style={{ width: '100%' }}
Expand Down
4 changes: 2 additions & 2 deletions packages/uniforms-antd/src/TextField.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Input, { InputProps } from 'antd/lib/input';
import Input, { InputProps, InputRef } from 'antd/lib/input';
import React, { Ref } from 'react';
import { FieldProps, connectField, filterDOMProps } from 'uniforms';

Expand All @@ -7,7 +7,7 @@ import wrapField from './wrapField';
export type TextFieldProps = FieldProps<
string,
Omit<InputProps, 'onReset'>,
{ inputRef?: Ref<Input> }
{ inputRef?: Ref<InputRef> }
>;

function Text(props: TextFieldProps) {
Expand Down
11 changes: 11 additions & 0 deletions packages/uniforms-antd5/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# uniforms-antd

> Ant Design UI 5components for `uniforms`.

## Install

```sh
$ npm install uniforms-antd5
```

For more in depth documentation see [uniforms.tools](https://uniforms.tools).
74 changes: 74 additions & 0 deletions packages/uniforms-antd5/__tests__/DateField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { screen } from '@testing-library/react';
import userEvent, {
PointerEventsCheckLevel,
} from '@testing-library/user-event';
import moment from 'moment';
import React from 'react';
import { renderWithZod } from 'uniforms/__suites__';
import { DateField } from 'uniforms-antd';
import { z } from 'zod';

const getClosestInput = (text: string) =>
screen.getByText(text).closest('.ant-row')?.querySelector('input');

test('<DateField> - default props override', async () => {
const pickerProps = { showTime: false, style: { background: 'red' } };
renderWithZod({
element: <DateField name="x" {...pickerProps} />,
schema: z.object({ x: z.date() }),
});
const body = screen.getByText('X').closest('body');
const input = body?.querySelector('.ant-picker');
expect(input).toBeInTheDocument();
expect(input).toHaveStyle('background: red');

await userEvent.click(input!);
expect(body?.querySelector('.ant-picker-time-panel')).not.toBeInTheDocument();
});

test('<DateField> - renders a input which correctly reacts on change (empty)', async () => {
const onChange = jest.fn();
renderWithZod({
element: <DateField name="x" value={new Date()} />,
onChange,
schema: z.object({ x: z.date() }),
});

const input = getClosestInput('X');
expect(input).toBeInTheDocument();
await userEvent.click(input!);
const clear = input?.parentElement?.querySelector('.ant-picker-clear');
await userEvent.click(clear!);
expect(onChange).toHaveBeenLastCalledWith('x', undefined);
});

test('<DateField> - renders a input which correctly reacts on change', async () => {
const onChange = jest.fn();
const now = moment('2024-01-01 12:00:00');
renderWithZod({
element: <DateField name="x" />,
onChange,
schema: z.object({ x: z.date() }),
});

const input = getClosestInput('X');
expect(input).toBeInTheDocument();
await userEvent.click(input!);
await userEvent.type(input!, now.format('YYYY-MM-DD HH:mm:ss'));
const ok = screen.getByText('Ok');
await userEvent.click(ok, {
pointerEventsCheck: PointerEventsCheckLevel.Never,
});
expect(onChange).toHaveBeenLastCalledWith('x', now.toDate());
});

test('<DateField> - renders a wrapper with unknown props', () => {
renderWithZod({
element: <DateField name="x" data-x="x" data-y="y" data-z="z" />,
schema: z.object({ x: z.date() }),
});
const input = getClosestInput('X');
expect(input?.getAttribute('data-x')).toBe('x');
expect(input?.getAttribute('data-y')).toBe('y');
expect(input?.getAttribute('data-z')).toBe('z');
});
48 changes: 48 additions & 0 deletions packages/uniforms-antd5/__tests__/SelectField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { fireEvent, screen } from '@testing-library/react';
import React from 'react';
import { renderWithZod } from 'uniforms/__suites__';
import { SelectField } from 'uniforms-antd';
import { z } from 'zod';

test('<SelectField> - renders a select which correctly reacts on change (array)', () => {
const onChange = jest.fn();

renderWithZod({
element: <SelectField name="x" />,
schema: z.object({
x: z.string().uniforms({
fieldType: Array,
options: [
{ label: 'A', value: 'a' },
{ label: 'B', value: 'b' },
],
}),
}),
onChange,
});

fireEvent.mouseDown(screen.getByRole('combobox'));
fireEvent.click(screen.getByText('B'));

expect(onChange).toHaveBeenLastCalledWith('x', ['b']);
});

test('<SelectField> - renders a select (undefined values)', () => {
const { container } = renderWithZod({
element: <SelectField name="x" value={[undefined, 'a', undefined]} />,
schema: z.object({
x: z.string().uniforms({
fieldType: Array,
options: [
{ label: 'A', value: 'a' },
{ label: 'B', value: 'b' },
],
}),
}),
});

expect(
container.getElementsByClassName('ant-select-selection-item-content')
.length,
).toBe(1);
});
38 changes: 38 additions & 0 deletions packages/uniforms-antd5/__tests__/_createContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { SimpleSchemaDefinition } from 'simpl-schema';
import { Context, UnknownObject, randomIds, ChangedMap } from 'uniforms';

import createSchema from './_createSchema';

const randomId = randomIds();

export default function createContext<Model extends UnknownObject>(
schema?: SimpleSchemaDefinition,
context?: Partial<Context<Model>>,
model = {} as Model,
): { context: Context<Model> } {
return {
context: {
changed: false,
changedMap: {} as ChangedMap<Model>,
error: null,
model,
name: [],
onChange() {},
onSubmit() {},
randomId,
submitted: false,
submitting: false,
validating: false,
...context,
schema: createSchema(schema),
state: {
disabled: false,
readOnly: false,
showInlineError: false,
...context?.state,
},
// @ts-expect-error We don't have a true ref in tests.
formRef: null,
},
};
}
6 changes: 6 additions & 0 deletions packages/uniforms-antd5/__tests__/_createSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import SimpleSchema, { SimpleSchemaDefinition } from 'simpl-schema';
import { SimpleSchema2Bridge } from 'uniforms-bridge-simple-schema-2';

export default function createSchema(schema: SimpleSchemaDefinition = {}) {
return new SimpleSchema2Bridge({ schema: new SimpleSchema(schema) });
}
76 changes: 76 additions & 0 deletions packages/uniforms-antd5/__tests__/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as suites from 'uniforms/__suites__';
import * as theme from 'uniforms-antd';

it('exports everything', () => {
expect(theme).toEqual({
AutoFields: expect.any(Function),
AutoField: expect.any(Function),
AutoForm: expect.any(Function),
BaseForm: expect.any(Function),
BoolField: expect.any(Function),
DateField: expect.any(Function),
ErrorField: expect.any(Function),
ErrorsField: expect.any(Function),
HiddenField: expect.any(Function),
ListAddField: expect.any(Function),
ListDelField: expect.any(Function),
ListField: expect.any(Function),
ListItemField: expect.any(Function),
LongTextField: expect.any(Function),
NestField: expect.any(Function),
NumField: expect.any(Function),
QuickForm: expect.any(Function),
RadioField: expect.any(Function),
SelectField: expect.any(Function),
SubmitField: expect.any(Function),
TextField: expect.any(Function),
ValidatedForm: expect.any(Function),
ValidatedQuickForm: expect.any(Function),
wrapField: expect.any(Function),
});
});

describe('@RTL AntD', () => {
suites.testAutoField(theme.AutoField, {
getDateField: screen => screen.getByRole('textbox'),
getSelectField: screen => screen.getByRole('combobox'),
});
suites.testAutoForm(theme.AutoForm);
suites.testBaseForm(theme.BaseForm);
suites.testBoolField(theme.BoolField, { isButton: true, testCheckbox: true });
// FIXME: AntD `DatePicker` is far from the HTML one.
// suites.testDateField(antd.DateField);
suites.testErrorField(theme.ErrorField);
suites.testErrorsField(theme.ErrorsField);
suites.testHiddenField(theme.HiddenField);
suites.testListAddField(theme.ListAddField);
suites.testListDelField(theme.ListDelField);
suites.testListField(theme.ListField, {
getListAddField: screen => screen.getByRole('img', { name: 'plus-square' }),
testTooltip: true,
testStyle: true,
});
suites.testListItemField(theme.ListItemField);
suites.testLongTextField(theme.LongTextField);
suites.testNumField(theme.NumField);
suites.testNestField(theme.NestField);
suites.testQuickForm(theme.QuickForm);
// FIXME: AntD radio.group does not support HTML attributes https://github.com/ant-design/ant-design/issues/8561, added a flag to skip attributes tests.
suites.testRadioField(theme.RadioField, { skipHtmlAttributesTest: true });
// FIXME: AntD has problem with toHaveValue check
suites.testSubmitField(theme.SubmitField, { skipValueTest: true });
// FIXME: AntD select does not work with new RTL test implementation
suites.testSelectField(theme.SelectField, { theme: 'antd' });
suites.testTextField(theme.TextField);
suites.testValidatedForm(theme.ValidatedForm);
suites.testDateField(theme.DateField, {
theme: 'antd',
});
suites.testValidatedQuickForm(theme.ValidatedQuickForm);
suites.testWrapField(theme.wrapField, {
helpPropsName: 'help',
withoutWrapClassName: true,
withoutHelpClassName: true,
withoutLabelClassName: true,
});
});
52 changes: 52 additions & 0 deletions packages/uniforms-antd5/__tests__/wrapField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { screen } from '@testing-library/react';
import React from 'react';
import { renderWithZod } from 'uniforms/__suites__';
import { wrapField } from 'uniforms-antd';
import { z } from 'zod';

describe('wrapField tests', () => {
test('<wrapField> - renders wrapper with extra style', () => {
const { container } = renderWithZod({
element: wrapField(
{ wrapperStyle: { backgroundColor: 'red' } },
<div data-testid="x" />,
),
schema: z.object({}),
});
const element = container.getElementsByClassName('ant-form-item')[0];
expect(element?.getAttribute('style')).toBe('background-color: red;');
});

test('<wrapField> - renders wrapper with label and info', () => {
renderWithZod({
element: wrapField({ label: 'Label', info: 'Info' }, <div />),
schema: z.object({}),
});
expect(screen.getByRole('img').getAttribute('aria-label')).toBe(
'question-circle',
);
});

test('<wrapField> - renders wrapper with extra text', () => {
renderWithZod({
element: wrapField({ extra: 'Extra' }, <div data-testid="x" />),
schema: z.object({}),
});
expect(screen.getByText('Extra')).toBeInTheDocument();
});

test('<wrapField> - renders wrapper with a custom validateStatus', () => {
renderWithZod({
element: wrapField(
{ validateStatus: 'success' },
<div data-testid="x" />,
),
schema: z.object({}),
});
expect(
screen
.getByTestId('x')
.closest('.ant-form-item-has-feedback.ant-form-item-has-success'),
).toBeInTheDocument();
});
});
Loading