(null);
12 | const { loading, loadNewPerson } = useRandomPerson();
13 | const [data, setData] = React.useState({});
14 |
15 | const loadData = React.useCallback(async () => {
16 | const person = await loadNewPerson();
17 | formRef.current?.setData({ ...person, yesno: 1, truefalse: true });
18 | }, [loadNewPerson]);
19 |
20 | return (
21 | <>
22 |
75 |
76 |
85 |
96 |
106 |
107 |
108 | {JSON.stringify(data, null, 3)}
109 |
110 | >
111 | );
112 | };
113 |
114 | export default FormWrapper;
115 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | This Code of Conduct outlines our expectations for participants within the Rocketseat community as well as steps to reporting unacceptable behavior. Our goal is to make explicit what we expect from participants in this community as well as its leaders. We are committed to providing a welcoming and inspiring community for all and expect our code of conduct to be honored. Anyone who violates this code of conduct may be banned from the community.
4 |
5 | Our community strives to:
6 |
7 | - **Be welcoming, friendly and patient**.
8 | - **Be considerate**: Your work will be used by other people, and you in turn will depend on the work of others. Any decision you take will affect users and colleagues, and you should take those consequences into account when making decisions. Remember that you might not be communicating in someone else’s primary language.
9 | - **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a productive one.
10 | - **Be understandable**: Disagreements, both social and technical, happen all the time and Rocketseat is no exception. It is important that we resolve disagreements and differing views constructively. Remember that we're different. The strength of Rocketseat comes from its varied community, people from a wide range of backgrounds. Different people have different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn't mean that they're wrong. Don't forget that it is human to err and blaming each other doesn't get us anywhere, rather offer to help resolving issues and to help learn from mistakes.
11 |
12 | This code serves to distill our common understanding of a collaborative, shared environment, and goals. We expect it to be followed in spirit as much as in the letter.
13 |
14 | ### Diversity Statement
15 |
16 | We encourage everyone to participate and are committed to building a community for all. Although we may not be able to satisfy everyone, we all agree that everyone is equal. Whenever a participant has made a mistake, we expect them to take responsibility for it. If someone has been harmed or offended, it is our responsibility to listen carefully and respectfully, and do our best to right the wrong.
17 |
18 | Although this list cannot be exhaustive, we explicitly honor diversity in age, gender, gender identity or expression, culture, ethnicity, language, national origin, political beliefs, profession, race, religion, sexual orientation, socioeconomic status, and technical ability. We will not tolerate discrimination based on any of the protected characteristics above, including participants with disabilities.
19 |
20 | ### Reporting Issues
21 |
22 | If you experience or witness unacceptable behavior—or have any other concerns—please report it by contacting us via italoiz.dev@gmail.com. All reports will be handled with discretion. In your report please include:
23 |
24 | - Your contact information.
25 | - Names (real, nicknames, or pseudonyms) of any individuals involved. If there are additional witnesses, please include them as well. Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record, please include a link.
26 | - Any additional information that may be helpful.
27 |
28 | After filing a report, a representative will contact you personally. If the person who is harassing you is part of the response team, they will recuse themselves from handling your incident. A representative will then review the incident, follow up with any additional questions, and make a decision as to how to respond. We will respect confidentiality requests for the purpose of protecting victims of abuse.
29 |
30 | Anyone asked to stop unacceptable behavior is expected to comply immediately. If an individual engages in unacceptable behavior, the representative may take any action they deem appropriate, up to and including a permanent ban from our community without warning.
31 |
32 | _This Code Of Conduct follows the template established by [TODO Group](https://todogroup.org)_.
33 |
--------------------------------------------------------------------------------
/packages/material-ui/lib/Select/index.tsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | useEffect,
3 | useRef,
4 | useState,
5 | useMemo,
6 | useCallback,
7 | } from 'react';
8 |
9 | import {
10 | Select as BaseSelect,
11 | FormControl,
12 | InputLabel,
13 | FormHelperText,
14 | } from '@material-ui/core';
15 | import { useField } from '@unform/core';
16 |
17 | import { printWarning } from '../debug';
18 | import { SelectProps } from './types';
19 |
20 | function getOptionsCollectionArray(
21 | options: HTMLOptionsCollection,
22 | ): HTMLOptionElement[] {
23 | const arr: HTMLOptionElement[] = [];
24 | for (let i = 0; i < options.length; i += 1) {
25 | arr.push(options[i]);
26 | }
27 | return arr;
28 | }
29 |
30 | function isValidValue(value: string | number | boolean): boolean {
31 | return typeof value !== 'undefined' && value !== '';
32 | }
33 |
34 | const Select: React.FC = ({
35 | name,
36 | label,
37 | style,
38 | className,
39 | defaultValue,
40 | children,
41 | native,
42 | onChange,
43 | value: valueProp,
44 | multiple,
45 | variant,
46 | ...restProps
47 | }) => {
48 | if (!name) {
49 | printWarning(
50 | 'Select component must have a `name` property for correctly working.',
51 | );
52 | }
53 |
54 | const {
55 | fieldName,
56 | registerField,
57 | defaultValue: defaultFieldValue,
58 | error,
59 | } = useField(name);
60 |
61 | const inputRef = useRef(null);
62 | const defaultInputValue = useMemo(() => {
63 | if (multiple) {
64 | return defaultFieldValue || defaultValue || [];
65 | }
66 | return defaultFieldValue || defaultValue || '';
67 | }, [defaultFieldValue, defaultValue, multiple]);
68 | const [inputValue, setInputValue] = useState(defaultInputValue);
69 |
70 | const _handleChange = useCallback(
71 | e => {
72 | const el = e.target;
73 | let value: number | number[] | string | string[];
74 |
75 | if (native && multiple) {
76 | value = getOptionsCollectionArray(el.options)
77 | .filter(opt => opt.selected)
78 | .map(opt => opt.value);
79 | } else {
80 | value = el.value;
81 | }
82 |
83 | if (valueProp === undefined && onChange === undefined) {
84 | setInputValue(() => value);
85 | }
86 |
87 | if (valueProp === undefined && typeof onChange === 'function') {
88 | setInputValue(() => value);
89 | onChange(e, null);
90 | }
91 |
92 | if (valueProp !== undefined && typeof onChange === 'function') {
93 | onChange(e, null);
94 | }
95 | },
96 | [valueProp, onChange, setInputValue, multiple, native],
97 | );
98 |
99 | useEffect(() => {
100 | if (fieldName) {
101 | registerField({
102 | name: fieldName,
103 | ref: inputRef.current,
104 | getValue() {
105 | return valueProp || inputValue;
106 | },
107 | setValue(_, newValue: string | string[] | number[]) {
108 | _handleChange({
109 | target: { value: newValue },
110 | });
111 | },
112 | });
113 | }
114 | }, [fieldName, registerField, _handleChange, native, valueProp, inputValue]);
115 |
116 | const baseSelectProps: SelectProps = useMemo(
117 | () => ({
118 | value: !valueProp ? inputValue : valueProp,
119 | inputProps: {
120 | ...restProps.inputProps,
121 | ref: inputRef,
122 | },
123 | defaultValue: defaultInputValue || inputValue,
124 | onChange: _handleChange,
125 | name,
126 | multiple,
127 | label,
128 | ...restProps,
129 | }),
130 | [
131 | inputValue,
132 | defaultInputValue,
133 | name,
134 | restProps,
135 | _handleChange,
136 | valueProp,
137 | multiple,
138 | label,
139 | ],
140 | );
141 |
142 | const shrink = useMemo(() => {
143 | if (native) {
144 | return true;
145 | }
146 |
147 | if (multiple) {
148 | return !!(valueProp || inputValue).length;
149 | }
150 |
151 | return isValidValue(valueProp as any) || !!isValidValue(inputValue);
152 | }, [native, multiple, inputValue, valueProp]);
153 |
154 | return (
155 |
161 | {!!label && (
162 |
163 | {label}
164 |
165 | )}
166 |
167 |
168 | {children}
169 |
170 |
171 | {!!error && {error}}
172 |
173 | );
174 | };
175 |
176 | export default React.memo(Select);
177 |
--------------------------------------------------------------------------------
/packages/material-ui/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 
4 |
5 |
6 |
7 |
8 |
9 | [](https://www.npmjs.com/package/unform-material-ui)
10 | [](https://travis-ci.org/italoiz/unform-material-ui)
11 | [](https://coveralls.io/github/italoiz/unform-material-ui?branch=master)
12 |
13 |
14 |
15 | ## Overview
16 |
17 | This library uses [Unform](https://github.com/Rocketseat/unform) + [Material UI](https://material-ui.com) styles to create super beautiful forms easily.
18 |
19 | ## Table of contents
20 |
21 | - [Overview](#overview)
22 | - [Table of contents](#table-of-contents)
23 | - [Roadmap](#roadmap)
24 | - [Installation](#installation)
25 | - [Guides](#guides)
26 | - [Basics](#basics)
27 | - [Components](#components)
28 | - [`` Component](#textfield--component)
29 | - [`` Component](#select--component)
30 | - [`` Component](#checkbox--component)
31 | - [`` Component](#switch--component)
32 | - [Contributing](#contributing)
33 | - [Contribution Guidelines](#contribution-guidelines)
34 | - [Code of Conduct](#code-of-conduct)
35 | - [License](#license)
36 |
37 | ## Roadmap
38 |
39 | - Support all [Material UI](https://material-ui.com) components;
40 | - Better docs;
41 |
42 | ## Installation
43 |
44 | Just add unform to your project:
45 |
46 | ```
47 | yarn add @unform/core @unform/web @material-ui/core unform-material-ui
48 | ```
49 |
50 | ## Guides
51 |
52 | ### Basics
53 |
54 | > ⚠️ This is a library that depends on **Unform** and **Material UI** to work, both must be installed in your project.
55 |
56 | - Here's how to use the [Unform library here](https://unform.dev/guides/basic-form)
57 | - Here's how to se up the [Material UI here](https://material-ui.com/getting-started/installation/)
58 |
59 | ### Components
60 |
61 | > ️️⚠️ For now, all components of the **Material UI** are not yet supported. Below are just the supported components.
62 |
63 | #### `` Component
64 |
65 | The `` component, is similar to the default component ``. See the component documentation [here](https://material-ui.com/api/text-field/) for more information.
66 |
67 | ```jsx
68 | import React from 'react';
69 | import { Form } from '@unform/web';
70 | import { TextField } from 'unform-material-ui';
71 |
72 | function App() {
73 | function handleSubmit(data) {}
74 |
75 | return (
76 |
82 | );
83 | }
84 | ```
85 |
86 | #### `` Component
87 |
88 | The `` component, is similar to the default component ``. See the component documentation [here](https://material-ui.com/api/select/) for more information.
89 |
90 | ```jsx
91 | import React from 'react';
92 | import { Form } from '@unform/web';
93 | import { Select } from 'unform-material-ui';
94 | import { MenuItem } from '@material-ui/core';
95 |
96 | function App() {
97 | function handleSubmit(data) {
98 | /**
99 | * {
100 | * "tech": ['node'],
101 | * "country": "br"
102 | * }
103 | */
104 | }
105 |
106 | return (
107 |
120 | );
121 | }
122 | ```
123 |
124 | #### `` Component
125 |
126 | The `` component, is similar to the default component ``. See the component documentation [here](https://material-ui.com/api/checkboxes/) for more information.
127 |
128 | ```jsx
129 | import React from 'react';
130 | import { Form } from '@rocketseat/unform';
131 | import { Checkbox } from 'unform-material-ui';
132 |
133 | function App() {
134 | function handleSubmit(data) {
135 | /**
136 | * {
137 | * "terms": true,
138 | * "acceptEmails": true
139 | * }
140 | */
141 | }
142 |
143 | return (
144 |
153 | );
154 | }
155 | ```
156 |
157 | #### `` Component
158 |
159 | The `` component, is similar to the default component ``. See the component documentation [here](https://material-ui.com/api/switch/) for more information.
160 |
161 | ```jsx
162 | import React from 'react';
163 | import { Form } from '@rocketseat/unform';
164 | import { Switch } from 'unform-material-ui';
165 |
166 | function App() {
167 | function handleSubmit(data) {
168 | /**
169 | * {
170 | * "terms": true,
171 | * "acceptEmails": true
172 | * }
173 | */
174 | }
175 |
176 | return (
177 |
186 | );
187 | }
188 | ```
189 |
190 | ## Contributing
191 |
192 | Thanks for being interested on making this package better. We encourage everyone to help improving this project with some new features, bug fixes and performance issues. Please take a little bit of your time to read our guides, so this process can be faster and easier.
193 |
194 | ### Contribution Guidelines
195 |
196 | Take a moment to read about our [Contribution Guidelines](/.github/CONTRIBUTING.md) so you can understand how to submit an issue, commit and create pull requests.
197 |
198 | ### Code of Conduct
199 |
200 | We expect you to follow our [Code of Conduct](/.github/CODE_OF_CONDUCT.md). You can read it to understand what kind of behaviour will and will not be tolerated.
201 |
202 | ## License
203 |
204 | MIT © [Italo Izaac](https://github.com/italoiz)
205 |
--------------------------------------------------------------------------------
/packages/material-ui/__tests__/components/TextField.spec.tsx:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom/extend-expect';
2 | import React, { RefObject } from 'react';
3 |
4 | import { fireEvent, act } from '@testing-library/react';
5 | import { FormHandles } from '@unform/core';
6 |
7 | import { TextField } from '../../lib';
8 | import render from '../utils/RenderTest';
9 |
10 | describe(' Component', () => {
11 | it('should render correctly', () => {
12 | const { container } = render();
13 |
14 | expect(!!container.querySelector('input[name=name]')).toBe(true);
15 | });
16 |
17 | it('should render with `defaultValue` property when exists', () => {
18 | const { container } = render(
19 | ,
20 | );
21 |
22 | expect(container.querySelector('input[name=name]')).toHaveAttribute(
23 | 'value',
24 | 'foo bar',
25 | );
26 | });
27 |
28 | it('should render with initial data when `initialData` property exists', () => {
29 | const { container } = render(, {
30 | initialData: { name: 'foo bar' },
31 | });
32 |
33 | expect(container.querySelector('input[name=name]')).toHaveAttribute(
34 | 'value',
35 | 'foo bar',
36 | );
37 | });
38 |
39 | it('should return form data on submit form', () => {
40 | const submitMock = jest.fn();
41 |
42 | const { container, getByTestId } = render(, {
43 | onSubmit: submitMock,
44 | });
45 |
46 | const input: any = container.querySelector('input[name=name]');
47 |
48 | fireEvent.change(input, { target: { value: 'foo bar' } });
49 | fireEvent.submit(getByTestId('form'));
50 |
51 | expect(submitMock).toHaveBeenCalledWith(
52 | { name: 'foo bar' },
53 | expect.any(Object),
54 | expect.any(Object),
55 | );
56 | });
57 |
58 | it('should display the error when the field error exists.', () => {
59 | const formRef: RefObject = { current: null };
60 |
61 | const { getByText } = render(, {
62 | ref: formRef,
63 | });
64 |
65 | act(() => {
66 | if (formRef.current) {
67 | formRef.current.setFieldError('name', 'Name is required');
68 | }
69 | });
70 |
71 | expect(!!getByText('Name is required')).toBe(true);
72 | });
73 |
74 | it('should throw an error when `name` property not passed', () => {
75 | console.error = jest.fn(); // eslint-disable-line no-console
76 |
77 | expect(() => {
78 | const props = {} as any;
79 | render();
80 | }).toThrow(
81 | 'TextField component must have a `name` property for correctly working.',
82 | );
83 |
84 | expect(console.error).toHaveBeenCalled(); // eslint-disable-line no-console
85 | });
86 |
87 | it('should shrink label when focus on input', () => {
88 | const { getByTestId } = render(
89 | ,
95 | );
96 |
97 | const input = getByTestId('input');
98 | const label = getByTestId('input-label');
99 |
100 | act(() => {
101 | fireEvent.focus(input);
102 | });
103 | expect(label).toHaveAttribute('data-shrink', 'true');
104 |
105 | act(() => {
106 | fireEvent.blur(input);
107 | });
108 | expect(label).toHaveAttribute('data-shrink', 'false');
109 | });
110 |
111 | it('should shrink label when value is change via unform api', () => {
112 | const formRef: RefObject = { current: null };
113 |
114 | const { getByTestId } = render(
115 | ,
120 | {
121 | ref: formRef,
122 | },
123 | );
124 |
125 | const label = getByTestId('input-label');
126 |
127 | act(() => {
128 | if (formRef.current) {
129 | formRef.current.setData({ name: 'foo bar' });
130 | }
131 | });
132 |
133 | expect(label).toHaveAttribute('data-shrink', 'true');
134 | });
135 |
136 | it('should not shrink label when value is empty', () => {
137 | const formRef: RefObject = { current: null };
138 |
139 | const { getByTestId } = render(
140 | ,
145 | {
146 | ref: formRef,
147 | },
148 | );
149 |
150 | const label = getByTestId('input-label');
151 |
152 | act(() => {
153 | if (formRef.current) {
154 | formRef.current.setData({ name: '' });
155 | }
156 | });
157 |
158 | expect(label).toHaveAttribute('data-shrink', 'false');
159 | });
160 |
161 | it('should not shrink label on reset input value', () => {
162 | const formRef: RefObject = { current: null };
163 |
164 | const { getByTestId } = render(
165 | ,
170 | {
171 | ref: formRef,
172 | },
173 | );
174 |
175 | const label = getByTestId('input-label');
176 |
177 | act(() => {
178 | if (formRef.current) {
179 | formRef.current.reset();
180 | }
181 | });
182 |
183 | expect(label).toHaveAttribute('data-shrink', 'false');
184 | });
185 |
186 | it('should put reset value on input when data is passed to the reset function', () => {
187 | const formRef: RefObject = { current: null };
188 | const submitMock = jest.fn();
189 |
190 | const { getByTestId } = render(
191 | ,
197 | {
198 | ref: formRef,
199 | onSubmit: submitMock,
200 | },
201 | );
202 |
203 | act(() => {
204 | if (formRef.current) {
205 | formRef.current.reset({ name: 'foo bar reseted' });
206 | }
207 | });
208 |
209 | fireEvent.submit(getByTestId('form'));
210 | expect(submitMock).toHaveBeenCalledWith(
211 | { name: 'foo bar reseted' },
212 | expect.any(Object),
213 | expect.any(Object),
214 | );
215 | });
216 |
217 | it('should use prop `shrink` instead of default shrink behavior when property exists', () => {
218 | const { getByTestId } = render(
219 | ,
224 | );
225 |
226 | const label = getByTestId('input-label');
227 | expect(label).toHaveAttribute('data-shrink', 'true');
228 | });
229 |
230 | it('should use empty value instead of `undefined` when use the `setData` function with inexistent property value', () => {
231 | const formRef: RefObject = { current: null };
232 | const submitMock = jest.fn();
233 | const { getByTestId } = render(, {
234 | ref: formRef,
235 | onSubmit: submitMock,
236 | });
237 |
238 | act(() => {
239 | if (formRef.current) {
240 | formRef.current.setData({
241 | email: 'foo@bar.com',
242 | });
243 | }
244 | });
245 |
246 | fireEvent.submit(getByTestId('form'));
247 |
248 | expect(submitMock).toHaveBeenCalledWith(
249 | { name: '' },
250 | expect.any(Object),
251 | expect.any(Object),
252 | );
253 | });
254 | });
255 |
--------------------------------------------------------------------------------
/packages/material-ui/__tests__/components/Select.spec.tsx:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom/extend-expect';
2 | import React, { RefObject } from 'react';
3 |
4 | import { MenuItem } from '@material-ui/core';
5 | import { fireEvent, act, screen } from '@testing-library/react';
6 | import userEvent from '@testing-library/user-event';
7 | import { FormHandles } from '@unform/core';
8 |
9 | import { Select } from '../../lib';
10 | import render from '../utils/RenderTest';
11 |
12 | describe(' Component', () => {
13 | it('should render correctly', () => {
14 | const { container } = render();
15 |
16 | const input = container.querySelector('input[name=country]');
17 |
18 | expect(!!input).toBe(true);
19 | });
20 |
21 | it('should render select options correctly', () => {
22 | const { getByRole, getAllByRole } = render(
23 | ,
27 | );
28 |
29 | fireEvent.mouseDown(getByRole('button'));
30 | const options = getAllByRole('option');
31 | expect(options.length).toBe(2);
32 | });
33 |
34 | it('should render with `defaultValue` property when exists', () => {
35 | const { container, getByRole } = render(
36 | ,
40 | );
41 |
42 | const input: any = container.querySelector('input[name=country]');
43 | const button = getByRole('button');
44 | expect(input.value).toBe('us');
45 | expect(button.textContent).toBe('United State');
46 | });
47 |
48 | it('should render with element when exists `label` property', () => {
49 | const { getByText } = render(
50 | ,
54 | );
55 | expect(!!getByText('Select a country...')).toBe(true);
56 | });
57 |
58 | it('should return form data on submit form', () => {
59 | const submitMock = jest.fn();
60 |
61 | const { getByTestId } = render(
62 | ,
66 | {
67 | onSubmit: submitMock,
68 | },
69 | );
70 |
71 | fireEvent.submit(getByTestId('form'));
72 |
73 | expect(submitMock).toHaveBeenCalledWith(
74 | { country: 'us' },
75 | expect.any(Object),
76 | expect.any(Object),
77 | );
78 | });
79 |
80 | it('should render with initial data when `initialData` property exists', () => {
81 | const { container } = render(
82 | ,
86 | {
87 | initialData: { country: 'us' },
88 | },
89 | );
90 |
91 | expect(container.querySelector('input[name=country]')).toHaveAttribute(
92 | 'value',
93 | 'us',
94 | );
95 | });
96 |
97 | it('should display the error when the field error exists.', () => {
98 | const formRef: RefObject = { current: null };
99 |
100 | const { getByText } = render(
101 | ,
105 | {
106 | ref: formRef,
107 | },
108 | );
109 |
110 | act(() => {
111 | if (formRef.current) {
112 | formRef.current.setFieldError('country', 'Country is required');
113 | }
114 | });
115 |
116 | expect(!!getByText('Country is required')).toBe(true);
117 | });
118 |
119 | it('should throw an error when `name` property not passed', () => {
120 | console.error = jest.fn(); // eslint-disable-line no-console
121 |
122 | expect(() => {
123 | const props = {} as any;
124 | render();
125 | }).toThrow(
126 | 'Select component must have a `name` property for correctly working.',
127 | );
128 |
129 | expect(console.error).toHaveBeenCalled(); // eslint-disable-line no-console
130 | });
131 |
132 | it('should call `onChange` on select an option', () => {
133 | const onChange = jest.fn();
134 |
135 | const { getByRole, getAllByRole } = render(
136 | ,
140 | );
141 |
142 | fireEvent.mouseDown(getByRole('button'));
143 | const option = getAllByRole('option')[0];
144 | fireEvent.click(option);
145 | expect(onChange).toHaveBeenCalled();
146 | });
147 |
148 | it('should call `onChange` on select an option via unform api', () => {
149 | const onChange = jest.fn();
150 | const ref: RefObject = { current: null };
151 |
152 | render(
153 | ,
157 | {
158 | ref,
159 | },
160 | );
161 |
162 | act(() => {
163 | if (ref.current) {
164 | ref.current.setFieldValue('country', 'br');
165 | ref.current.setData({ country: 'us' });
166 | }
167 | expect(onChange).toHaveBeenCalledTimes(2);
168 | });
169 | });
170 |
171 | it('should update internal state on select an option via unform api', () => {
172 | const ref: RefObject = { current: null };
173 | const submitMock = jest.fn();
174 |
175 | const { getByTestId } = render(
176 | ,
180 | {
181 | ref,
182 | onSubmit: submitMock,
183 | },
184 | );
185 |
186 | act(() => {
187 | if (ref.current) {
188 | ref.current.setFieldValue('country', 'br');
189 | }
190 | });
191 |
192 | fireEvent.submit(getByTestId('form'));
193 | expect(submitMock).toHaveBeenCalledWith(
194 | {
195 | country: 'br',
196 | },
197 | expect.any(Object),
198 | expect.any(Object),
199 | );
200 | });
201 |
202 | it('should not update internal state when `onChange` and `value` properties is provided', async () => {
203 | const instance = { called: false, value: 'br' };
204 | const onChange = jest.fn().mockImplementation(() => {
205 | instance.called = true;
206 | });
207 |
208 | const { getByRole, getAllByRole } = render(
209 | ,
213 | );
214 |
215 | fireEvent.mouseDown(getByRole('button'));
216 | const options = getAllByRole('option');
217 | fireEvent.click(options[1]);
218 |
219 | expect(onChange).toHaveBeenCalled();
220 | expect(instance).toEqual({ called: true, value: 'br' });
221 | });
222 |
223 | it('should shrink label when is native type', () => {
224 | const { getByTestId } = render(
225 | ,
229 | );
230 |
231 | const label = getByTestId('select-label');
232 | expect(label).toHaveAttribute('data-shrink', 'true');
233 | });
234 |
235 | it('should shrink label when is multiple type and is not empty', () => {
236 | const { getByTestId } = render(
237 | ,
241 | {
242 | initialData: {
243 | country: ['br'],
244 | },
245 | },
246 | );
247 |
248 | const label = getByTestId('select-label');
249 | expect(label).toHaveAttribute('data-shrink', 'true');
250 | });
251 |
252 | it('should return array values on submit form and is multiple type', () => {
253 | const onSubmit = jest.fn();
254 |
255 | const { getByTestId } = render(
256 | ,
260 | {
261 | initialData: {
262 | country: ['br'],
263 | },
264 | onSubmit,
265 | },
266 | );
267 |
268 | fireEvent.submit(getByTestId('form'));
269 | expect(onSubmit).toHaveBeenCalledWith(
270 | {
271 | country: ['br'],
272 | },
273 | expect.any(Object),
274 | expect.any(Object),
275 | );
276 | });
277 |
278 | it('should return array values on submit form and is multiple and native type', () => {
279 | const onSubmit = jest.fn();
280 |
281 | const { getByTestId } = render(
282 | ,
292 | {
293 | onSubmit,
294 | },
295 | );
296 |
297 | userEvent.selectOptions(screen.getByTestId('select-input'), ['br', 'us']);
298 | fireEvent.submit(getByTestId('form'));
299 |
300 | expect(onSubmit).toHaveBeenCalledWith(
301 | {
302 | country: ['br', 'us'],
303 | },
304 | expect.any(Object),
305 | expect.any(Object),
306 | );
307 | });
308 | });
309 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribution guidelines
2 |
3 | ## Table of Contents
4 |
5 | - [Getting started](#getting-started)
6 | - [Language](#language)
7 | - [For native English speakers](#for-native-english-speakers)
8 | - [Code of Conduct](#code-of-conduct)
9 | - [How can I help?](#how-can-i-help)
10 | - [Documentation](#documentation)
11 | - [Issues](#issues)
12 | - [Submitting an issue](#submitting-an-issue)
13 | - [Feedback](#feedback)
14 | - [Code](#code)
15 | - [Dev environment](#dev-environment)
16 | - [Commiting](#commiting)
17 | - [Skipping building process](#skipping-building-process)
18 | - [Why all these rules?](#why-all-these-rules)
19 | - [Submitting a pull request](#submitting-a-pull-request)
20 |
21 | ## Getting started
22 |
23 | First off, we would like to thank you for taking the time to contribute and make this a better project!
24 |
25 | Here we have a set of instructions and guidelines to reduce misunderstandings and make the process of contributing to `unform` as smooth as possible.
26 |
27 | We hope this guide makes the contribution process clear and answers any questions you may have.
28 |
29 | ### Language
30 |
31 | Please, while contributing or interacting in any way in this project, refrain from using any language other than **English**.
32 |
33 | #### For native English speakers
34 |
35 | Try to use simple words and sentences. Don't make fun of non-native English speakers if you find something wrong about the way they express themselves.
36 |
37 | Try to encourage newcomers to express their opinions, and make them comfortable enough to do so.
38 |
39 | ### Code of Conduct
40 |
41 | We expect that project participants to adhere to our Code of Conduct. You can check the [full text](CODE_OF_CONDUCT.md) so that you may understand the kind of conduct we are expecting and what actions will and will not be tolerated.
42 |
43 | By participating in this project, you agree to abide by its terms.
44 |
45 | ## How can I help?
46 |
47 | Here are some ways you can help along with some guidelines.
48 |
49 | ### Documentation
50 |
51 | As a user of `unform`, you're the perfect candidate to help us improve our documentation!
52 |
53 | Typos, errors, lack of examples and/or explanation and so on, are just some examples of things that could be fixed and/or improved.
54 |
55 | You could even make improvements to this guide! :)
56 |
57 | While documenting, try to keep things simple and clear.
58 |
59 | ### Issues
60 |
61 | Some issues are created with missing information, without a template, not reproducible, or plain
62 | invalid.
63 |
64 | You can make them easier to understand and resolve.
65 |
66 | #### Submitting an issue
67 |
68 | - Please search for similar issues before opening a new one;
69 | - Use one of the corresponding issue templates;
70 | - Use a clear and descriptive title;
71 | - Include as much information as possible by filling out the provided issue
72 | template;
73 | - Most of the time, the best way to report an issue is a failing test proving it.
74 |
75 | ### Feedback
76 |
77 | The more feedback the better! We're always looking for more suggestions and opinions on discussions. That's a good opportunity to influence the future direction of this tool.
78 |
79 | This includes submitting an enhancement suggestion, including completely new features and minor improvements to existing functionality.
80 |
81 | The [`question`](https://github.com/Rocketseat/unform/labels/question)
82 | and
83 | [`rfc`](https://github.com/Rocketseat/unform/labels/rfc)
84 | labels are a good place to find ongoing discussions.
85 |
86 | ### Code
87 |
88 | You can use issue labels to discover issues you could help out with:
89 |
90 | - [`bug` issues](https://github.com/Rocketseat/unform/labels/bug)
91 | are known bugs we'd like to fix;
92 | - [`enhancement` issues](https://github.com/Rocketseat/unform/labels/enhancement)
93 | are features we're open to include.
94 |
95 | The
96 | [`help wanted`](https://github.com/Rocketseat/unform/labels/help%20wanted)
97 | and
98 | [`good first issue`](https://github.com/Rocketseat/unform/labels/good%20first%20issue)
99 | labels are especially useful.
100 |
101 | When you see an issue that is already assigned, please check to see if there isn't someone working on it already (maybe try asking in the issue). This is to prevent unnecessary work for everyone involved.
102 |
103 | #### Dev environment
104 |
105 | When developing, prefer using **Node** ≥ 8 and **yarn**. Writing code with the latest stable Node versions allows us to use newer developer tools.
106 |
107 | After [cloning the repository](https://help.github.com/articles/cloning-a-repository/), run `yarn` to install dependencies.
108 |
109 | A summary of the scripts:
110 |
111 | - To run the example use `yarn dev:example`;
112 | - The `yarn dev:start` command will build the lib and watch files in bundle and rebuild on changes;
113 | - Running `yarn dev` will run both `dev:example` and `dev:start`;
114 | - Use `yarn test` to run the test suite (powered by [Jest](https://facebook.github.io/jest/));
115 | - `yarn coveralls` can't be used locally, this is only used to provide test coverage statistics to [Coveralls](https://coveralls.io);
116 | - For code coverage locally, you can run `yarn test --coverage`.
117 | - `yarn build` will build the lib using [Rollup](https://rollupjs.org/guide/en);
118 |
119 | This project uses [Prettier](http://prettier.io/) for code formatting. Consider installing an [editor plugin](https://prettier.io/docs/en/editors.html) for the best experience, but code will also be formatted with a precommit script (using [lint-staged](https://github.com/okonet/lint-staged)).
120 |
121 | ## Commiting
122 |
123 | A commit message can consists of a **header**, **body** and **footer**. The header is the only mandatory part and consists of a type and a subject. The body is used to fully describe the change. The footer is the place to reference any issues or pull requests related to the commit. That said, we end with a template like this:
124 |
125 | ```
126 | :
127 |
128 | [optional body]
129 |
130 | [optional footer]
131 | ```
132 |
133 | To ensure that a commit is valid, easy to read, and changelog-ready, we have a hook that lints the commit message before allowing a commit to pass. This linter verifies the following:
134 |
135 | - The header (first line) is the only mandatory part of the commit message;
136 | - The body and footer are both optional but its use is highly encouraged;
137 | - The header should contains:
138 | - A type:
139 | - Must be lowercase;
140 | - Must be one of:
141 | - **chore**: A change that neither fix a bug nor adds a feature;
142 | - **ci**: A CI change;
143 | - **docs**: A documentation change or fix;
144 | - **feat**: A new feature;
145 | - **fix**: A bug fix;
146 | - **test**: A test-related change.
147 | - A subject:
148 | - Must be capitalized;
149 | - Must be limited to 50 characters or less;
150 | - Must omit any trailing punctuation.
151 | - The body:
152 | - Must have a leading blank line;
153 | - Each line must be limited to 72 characters or less.
154 | - The footer:
155 | - Must have a leading blank line;
156 | - Each line must be limited to 72 characters or less;
157 | - If your commit is about documentation or meta files, please add the tag **[skip ci]** to skip the building process.
158 | - If needed, reference to issues and pull requests must be made here in the last line.
159 |
160 | You also should follow these general guidelines when committing:
161 |
162 | - Use the present tense ("Add feature" not "Added feature");
163 | - Use the imperative mood ("Move cursor to..." not "Moves cursor to...");
164 | - Try to answer the following questions:
165 | - Why is this change necessary?
166 | - How does it address the issue?
167 | - What side effects (if any) does this change may have?
168 |
169 | Example of a commit message:
170 |
171 | ```
172 | type: Commit message style guide for Git
173 |
174 | The first line of a commit message serves as a summary. When displayed
175 | on the web, it's often styled as a heading, and in emails, it's
176 | typically used as the subject. As such, you should specify a "type" and
177 | a "subject". The type must be lowercase and one of: chore, ci, docs,
178 | feat, fix, test. For the subject you'll need capitalize it and
179 | omit any trailing punctuation. Aim for about 50 characters, give or
180 | take, otherwise it may be painfully truncated in some contexts. Write
181 | it, along with the rest of your message, in the present tense and
182 | imperative mood: "Fix bug" and not "Fixed bug" or "Fixes bug".
183 | Consistent wording makes it easier to mentally process a list of
184 | commits.
185 |
186 | Oftentimes a subject by itself is sufficient. When it's not, add a
187 | blank line (this is important) followed by one or more paragraphs hard
188 | wrapped to 72 characters. Git is strongly opinionated that the author
189 | is responsible for line breaks; if you omit them, command line tooling
190 | will show it as one extremely long unwrapped line. Fortunately, most
191 | text editors are capable of automating this.
192 |
193 | Issues and pull request can be referenced on the footer: #3 #12
194 | ```
195 |
196 | ### Skipping building process
197 |
198 | By default, Travis CI automatically runs the building process whenever you push changes. If your commit is about documentation or meta files, you can override this behavior by adding a **[skip ci]** tag anywhere in a commit’s **footer**. This not only skips the marked commit, but also **all other commits** in the push.
199 |
200 | ### Why all these rules?
201 |
202 | We try to enforce these rules for the following reasons:
203 |
204 | - Automatically generating changelog;
205 | - Communicating in a better way the nature of changes;
206 | - Triggering build and publish processes;
207 | - Automatically determining a semantic version bump (based on the types of commits);
208 | - Making it easier for people to contribute, by allowing them to explore a more structured commit history.
209 |
210 | ## Submitting a pull request
211 |
212 | Before submitting a pull request, please make sure the following is done:
213 |
214 | - [Fork](https://help.github.com/en/articles/fork-a-repo) the repository and create your branch from `master`.
215 | - Example: `feature/my-awesome-feature` or `fix/annoying-bug`;
216 | - Run `yarn` in the repository root;
217 | - If you’ve fixed a bug or added code that should be tested, **add tests**;
218 | - Ensure the test suite passes;
219 | - Ensure your commit is validated;
220 |
--------------------------------------------------------------------------------