Skip to content

Commit

Permalink
feat: Add ant.design support
Browse files Browse the repository at this point in the history
Issue #14
  • Loading branch information
italoiz committed Feb 25, 2020
1 parent 79e2d50 commit c7b9bf5
Show file tree
Hide file tree
Showing 12 changed files with 1,190 additions and 9 deletions.
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@typescript-eslint/parser": "2.15.0",
"@unform/core": "^2.0.0",
"@unform/web": "^2.0.0",
"antd": "^3.26.12",
"commitlint-config-rocketseat": "^0.0.1",
"coveralls": "^3.0.9",
"eslint": "^6.8.0",
Expand Down Expand Up @@ -67,5 +68,14 @@
"repository": {
"type": "git",
"url": "https://github.com/italoiz/unform-community-packages.git"
},
"changelog": {
"labels": {
"feature": "🚀 New Features",
"enhancement": "💅🏻 Enhancements",
"bug": "🐛 Bug Fix",
"breaking": "💥 Breaking Change",
"documentation": "📝 Documentation"
}
}
}
11 changes: 11 additions & 0 deletions packages/antd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# `antd`

> TODO: description
## Usage

```
const antd = require('antd');
// TODO: DEMONSTRATE API
```
155 changes: 155 additions & 0 deletions packages/antd/__tests__/components/Select.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import '@testing-library/jest-dom/extend-expect';
import React, { RefObject } from 'react';

import { fireEvent, wait, act } from '@testing-library/react';
import { FormHandles } from '@unform/core';

import { Select } from '../../lib';
import render from '../utils/RenderTest';

describe('<Select /> Component', () => {
it('should render correctly', () => {
const { getByRole } = render(<Select name="country" />);

expect(!!getByRole('combobox')).toBe(true);
});

it('should render with options', () => {
const { getByRole, baseElement } = render(
<Select name="country">
<Select.Option value="br">Brazil</Select.Option>
<Select.Option value="us">United State</Select.Option>
</Select>,
);

fireEvent.click(getByRole('combobox'));

wait(() => {
expect(baseElement.querySelectorAll('[role=option]').length).toBe(2);
});
});

it('should render with <label /> element when exists `label` property', () => {
const { getByText } = render(
<Select name="country" label="Select a country...">
<Select.Option value="br">Brazil</Select.Option>
<Select.Option value="us">United State</Select.Option>
</Select>,
);

expect(!!getByText('Select a country...')).toBe(true);
});

it('should return form data on submit form', async () => {
const submitMock = jest.fn();

const { getByRole, getByTestId, baseElement } = render(
<Select name="country">
<Select.Option value="br">Brazil</Select.Option>
<Select.Option value="us">United State</Select.Option>
</Select>,
{
onSubmit: submitMock,
},
);

fireEvent.click(getByRole('combobox'));

await wait(() => {
const option = baseElement.querySelectorAll('[role=option]')[0];
fireEvent.click(option);
});

fireEvent.submit(getByTestId('form'));

expect(submitMock).toHaveBeenCalledWith(
{ country: 'br' },
expect.any(Object),
);
});

it('should render with initial data when `initialData` property exists', () => {
const submitMock = jest.fn();

const { getByTestId } = render(
<Select name="country">
<Select.Option value="br">Brazil</Select.Option>
<Select.Option value="us">United State</Select.Option>
</Select>,
{
onSubmit: submitMock,
initialData: { country: 'br' },
},
);

fireEvent.submit(getByTestId('form'));

expect(submitMock).toHaveBeenCalledWith(
{ country: 'br' },
expect.any(Object),
);
});

it('should work with `mode` property equal `multiple` or `tags`', async () => {
const submitMock = jest.fn();

const { getByRole, getByTestId, baseElement } = render(
<Select name="country" mode="multiple">
<Select.Option value="br">Brazil</Select.Option>
<Select.Option value="us">United State</Select.Option>
</Select>,
{
onSubmit: submitMock,
},
);

fireEvent.click(getByRole('combobox'));

await wait(() => {
fireEvent.click(baseElement.querySelectorAll('[role=option]')[0]);
fireEvent.click(baseElement.querySelectorAll('[role=option]')[1]);
});

fireEvent.submit(getByTestId('form'));

expect(submitMock).toHaveBeenCalledWith(
{ country: expect.arrayContaining(['br', 'us']) },
expect.any(Object),
);
});

it('should display the error when the field error exists.', () => {
const formRef: RefObject<FormHandles> = { current: null };

const { getByText } = render(
<Select name="country">
<Select.Option value="br">Brazil</Select.Option>
<Select.Option value="us">United State</Select.Option>
</Select>,
{
ref: formRef,
},
);

act(() => {
if (formRef.current) {
formRef.current.setFieldError('country', 'Country is required');
}
});

expect(!!getByText('Country is required')).toBe(true);
});

it('should throw an error when `name` property not passed', () => {
console.error = jest.fn(); // eslint-disable-line no-console

expect(() => {
const props = {} as any;
render(<Select {...props} />);
}).toThrow(
'Select component must have a `name` property for correctly working.',
);

expect(console.error).toHaveBeenCalled(); // eslint-disable-line no-console
});
});
16 changes: 16 additions & 0 deletions packages/antd/__tests__/utils/RenderTest.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';

import { render as rtlRender } from '@testing-library/react';
import { Form } from '@unform/web';

export default function RenderTest(
children: React.ReactNode,
props: object = {},
) {
const mockFunction = jest.fn();
return rtlRender(
<Form data-testid="form" onSubmit={mockFunction} {...props}>
{children}
</Form>,
);
}
13 changes: 13 additions & 0 deletions packages/antd/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const { join } = require('path');

const baseConfig = require('../../jest.config');
const pkg = require('./package.json');

delete baseConfig.projects;

module.exports = {
...baseConfig,
displayName: pkg.name,
testMatch: [join(__dirname, '__tests__/**/*.spec.{ts,tsx}')],
coverageDirectory: join(__dirname, '__tests__/coverage'),
};
83 changes: 83 additions & 0 deletions packages/antd/lib/components/Select/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { useRef, useEffect, useMemo } from 'react';

import { Select as BaseSelect, Form } from 'antd';

import { useField } from '@unform/core';

import { printWarning } from '../../debug';
import { SelectProps, SelectComponent } from './types';

const Select: SelectComponent<SelectProps> = ({
children,
label,
FormItemProps,
name,
mode,
...restProps
}) => {
if (!name) {
printWarning(
'Select component must have a `name` property for correctly working.',
);
}

const ref = useRef(null);
const { fieldName, defaultValue, error, registerField } = useField(name);

useEffect(() => {
if (fieldName) {
registerField({
name: fieldName,
ref: ref.current,
path: 'rcSelect.state.value',
getValue(fieldRef) {
const { value } = fieldRef.rcSelect.state;

if (['multiple', 'tags'].includes(mode as string)) {
return value || undefined;
}

return value[0] || undefined;
},
});
}
}, [fieldName, registerField, mode]);

const validateStatusProp = useMemo(() => {
if (error) return 'error';

return FormItemProps?.validateStatus || undefined;
}, [error, FormItemProps]);

const helpProp = useMemo(() => {
if (error) return error;

return FormItemProps?.help || undefined;
}, [error, FormItemProps]);

return (
<Form.Item
label={label}
{...FormItemProps}
validateStatus={validateStatusProp as 'error'}
help={helpProp}
>
<BaseSelect
{...restProps}
ref={ref}
defaultValue={defaultValue}
mode={mode}
>
{children}
</BaseSelect>
</Form.Item>
);
};

/* re-export <Option /> component */
Select.Option = BaseSelect.Option;

/* re-export <OptGroup /> component */
Select.OptGroup = BaseSelect.OptGroup;

export default Select;
17 changes: 17 additions & 0 deletions packages/antd/lib/components/Select/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { FormItemProps } from 'antd/lib/form/FormItem';
import {
SelectProps as BaseSelectProps,
OptionProps,
OptGroupProps,
} from 'antd/lib/select';

export interface SelectProps extends Omit<BaseSelectProps, 'name'> {
name: string;
label?: string;
FormItemProps?: FormItemProps;
}

export interface SelectComponent<T> extends React.FC<T> {
Option: React.ClassicComponentClass<OptionProps>;
OptGroup: React.ClassicComponentClass<OptGroupProps>;
}
9 changes: 9 additions & 0 deletions packages/antd/lib/debug.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function printWarning(message: string) {
if (process.env.NODE_ENV !== 'production') {
if (typeof console !== 'undefined') {
console.error(`Warning: ${message}`); // eslint-disable-line
}

throw new Error(`Warning: ${message}`);
}
}
1 change: 1 addition & 0 deletions packages/antd/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Select } from './components/Select';
32 changes: 32 additions & 0 deletions packages/antd/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "unform-antd",
"version": "0.1.0",
"author": "Italo Izaac <[email protected]>",
"license": "MIT",
"main": "dist/index.js",
"module": "dist/index.es.js",
"types": "typings/index.d.ts",
"files": [
"dist",
"typings"
],
"repository": {
"type": "git",
"url": "https://github.com/italoiz/unform-community-packages.git",
"directory": "packages/antd"
},
"scripts": {
"test": "jest"
},
"peerDependencies": {
"antd": "^3.25.12",
"@unform/core": "*",
"react": "^16.8.0",
"react-dom": "^16.8.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.1.1",
"@testing-library/react": "^9.4.1",
"@unform/web": "^2.0.0"
}
}
2 changes: 1 addition & 1 deletion tsconfig.eslint.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"compilerOptions": { "strict": true },
"include": ["packages/**/*"]
"include": ["packages/*/**/*"]
}
Loading

0 comments on commit c7b9bf5

Please sign in to comment.