@ttoss/forms 0.5.10 → 0.5.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/index.js CHANGED
@@ -84,11 +84,107 @@ var FormFieldInput = ({
84
84
  });
85
85
  };
86
86
 
87
+ // src/FormFieldRadio.tsx
88
+ import { Box as Box3, Label as Label2, Radio } from "@ttoss/ui";
89
+ import { useController as useController2 } from "react-hook-form";
90
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
91
+ var FormFieldRadio = ({
92
+ label,
93
+ name,
94
+ options
95
+ }) => {
96
+ const {
97
+ field: { onChange, onBlur, value, ref }
98
+ } = useController2({
99
+ name,
100
+ defaultValue: ""
101
+ });
102
+ return /* @__PURE__ */ jsxs2(Box3, {
103
+ children: [
104
+ label && /* @__PURE__ */ jsx4(Label2, {
105
+ children: label
106
+ }),
107
+ /* @__PURE__ */ jsx4(Box3, {
108
+ variant: "form.radios",
109
+ children: options.map((option) => {
110
+ const id = `form-field-radio-${name}-${option.value}`;
111
+ return /* @__PURE__ */ jsxs2(Label2, {
112
+ htmlFor: id,
113
+ children: [
114
+ /* @__PURE__ */ jsx4(Radio, {
115
+ ref,
116
+ onChange,
117
+ onBlur,
118
+ value: option.value,
119
+ defaultChecked: value === option.value,
120
+ name,
121
+ id
122
+ }),
123
+ option.label
124
+ ]
125
+ }, id);
126
+ })
127
+ }),
128
+ /* @__PURE__ */ jsx4(ErrorMessage, {
129
+ name
130
+ })
131
+ ]
132
+ });
133
+ };
134
+
135
+ // src/FormFieldSelect.tsx
136
+ import { Box as Box4, Label as Label3, Select } from "@ttoss/ui";
137
+ import { useController as useController3 } from "react-hook-form";
138
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
139
+ var FormFieldSelect = ({
140
+ label,
141
+ name,
142
+ options,
143
+ arrow
144
+ }) => {
145
+ const {
146
+ field: { onChange, onBlur, value, ref }
147
+ } = useController3({
148
+ name,
149
+ defaultValue: ""
150
+ });
151
+ const id = `form-field-select-${name}`;
152
+ return /* @__PURE__ */ jsxs3(Box4, {
153
+ children: [
154
+ label && /* @__PURE__ */ jsx5(Label3, {
155
+ htmlFor: id,
156
+ children: label
157
+ }),
158
+ /* @__PURE__ */ jsx5(Select, {
159
+ ref,
160
+ name,
161
+ onChange,
162
+ onBlur,
163
+ value,
164
+ arrow,
165
+ variant: "form.select",
166
+ children: options.map((option) => {
167
+ return /* @__PURE__ */ jsx5("option", {
168
+ value: option.value,
169
+ id,
170
+ children: option.label
171
+ }, id);
172
+ })
173
+ }),
174
+ /* @__PURE__ */ jsx5(ErrorMessage, {
175
+ name
176
+ })
177
+ ]
178
+ });
179
+ };
180
+
87
181
  // src/FormField.tsx
88
182
  var FormField = () => {
89
183
  return null;
90
184
  };
91
185
  FormField.Input = FormFieldInput;
186
+ FormField.Radio = FormFieldRadio;
187
+ FormField.Select = FormFieldSelect;
92
188
  export {
93
189
  Form,
94
190
  FormField
package/dist/index.d.ts CHANGED
@@ -17,6 +17,23 @@ declare const FormField: {
17
17
  label?: string | undefined;
18
18
  name: react_hook_form.Path<TFieldValues>;
19
19
  }) => JSX.Element;
20
+ Radio: <TFieldValues_1 extends react_hook_form.FieldValues = react_hook_form.FieldValues>({ label, name, options, }: {
21
+ label?: string | undefined;
22
+ name: react_hook_form.Path<TFieldValues_1>;
23
+ options: {
24
+ value: string | number;
25
+ label: string;
26
+ }[];
27
+ }) => JSX.Element;
28
+ Select: <TFieldValues_2 extends react_hook_form.FieldValues = react_hook_form.FieldValues>({ label, name, options, arrow, }: {
29
+ label?: string | undefined;
30
+ name: react_hook_form.Path<TFieldValues_2>;
31
+ options: {
32
+ value: string | number;
33
+ label: string;
34
+ }[];
35
+ arrow?: React.ReactElement<any, string | React.JSXElementConstructor<any>> | undefined;
36
+ }) => JSX.Element;
20
37
  };
21
38
 
22
39
  export { Form, FormField };
package/dist/index.js CHANGED
@@ -116,11 +116,107 @@ var FormFieldInput = ({
116
116
  });
117
117
  };
118
118
 
119
+ // src/FormFieldRadio.tsx
120
+ var import_ui4 = require("@ttoss/ui");
121
+ var import_react_hook_form4 = require("react-hook-form");
122
+ var import_jsx_runtime4 = require("react/jsx-runtime");
123
+ var FormFieldRadio = ({
124
+ label,
125
+ name,
126
+ options
127
+ }) => {
128
+ const {
129
+ field: { onChange, onBlur, value, ref }
130
+ } = (0, import_react_hook_form4.useController)({
131
+ name,
132
+ defaultValue: ""
133
+ });
134
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ui4.Box, {
135
+ children: [
136
+ label && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui4.Label, {
137
+ children: label
138
+ }),
139
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui4.Box, {
140
+ variant: "form.radios",
141
+ children: options.map((option) => {
142
+ const id = `form-field-radio-${name}-${option.value}`;
143
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ui4.Label, {
144
+ htmlFor: id,
145
+ children: [
146
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui4.Radio, {
147
+ ref,
148
+ onChange,
149
+ onBlur,
150
+ value: option.value,
151
+ defaultChecked: value === option.value,
152
+ name,
153
+ id
154
+ }),
155
+ option.label
156
+ ]
157
+ }, id);
158
+ })
159
+ }),
160
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ErrorMessage, {
161
+ name
162
+ })
163
+ ]
164
+ });
165
+ };
166
+
167
+ // src/FormFieldSelect.tsx
168
+ var import_ui5 = require("@ttoss/ui");
169
+ var import_react_hook_form5 = require("react-hook-form");
170
+ var import_jsx_runtime5 = require("react/jsx-runtime");
171
+ var FormFieldSelect = ({
172
+ label,
173
+ name,
174
+ options,
175
+ arrow
176
+ }) => {
177
+ const {
178
+ field: { onChange, onBlur, value, ref }
179
+ } = (0, import_react_hook_form5.useController)({
180
+ name,
181
+ defaultValue: ""
182
+ });
183
+ const id = `form-field-select-${name}`;
184
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui5.Box, {
185
+ children: [
186
+ label && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Label, {
187
+ htmlFor: id,
188
+ children: label
189
+ }),
190
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui5.Select, {
191
+ ref,
192
+ name,
193
+ onChange,
194
+ onBlur,
195
+ value,
196
+ arrow,
197
+ variant: "form.select",
198
+ children: options.map((option) => {
199
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", {
200
+ value: option.value,
201
+ id,
202
+ children: option.label
203
+ }, id);
204
+ })
205
+ }),
206
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ErrorMessage, {
207
+ name
208
+ })
209
+ ]
210
+ });
211
+ };
212
+
119
213
  // src/FormField.tsx
120
214
  var FormField = () => {
121
215
  return null;
122
216
  };
123
217
  FormField.Input = FormFieldInput;
218
+ FormField.Radio = FormFieldRadio;
219
+ FormField.Select = FormFieldSelect;
124
220
  // Annotate the CommonJS export names for ESM import in node:
125
221
  0 && (module.exports = {
126
222
  Form,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttoss/forms",
3
- "version": "0.5.10",
3
+ "version": "0.5.11",
4
4
  "license": "UNLICENSED",
5
5
  "author": "ttoss",
6
6
  "contributors": [
@@ -42,5 +42,5 @@
42
42
  "publishConfig": {
43
43
  "access": "public"
44
44
  },
45
- "gitHead": "e2b509ee8717f07f7365191b651dcbb5f080e05a"
45
+ "gitHead": "84c4bd0244196f787a338ebdbcc869caac61a0ff"
46
46
  }
package/src/FormField.tsx CHANGED
@@ -1,4 +1,6 @@
1
1
  import { FormFieldInput } from './FormFieldInput';
2
+ import { FormFieldRadio } from './FormFieldRadio';
3
+ import { FormFieldSelect } from './FormFieldSelect';
2
4
 
3
5
  const FormField = () => {
4
6
  // to be implemented
@@ -6,5 +8,7 @@ const FormField = () => {
6
8
  };
7
9
 
8
10
  FormField.Input = FormFieldInput;
11
+ FormField.Radio = FormFieldRadio;
12
+ FormField.Select = FormFieldSelect;
9
13
 
10
14
  export { FormField };
@@ -0,0 +1,69 @@
1
+ import * as yup from 'yup';
2
+ import { Button } from '@ttoss/ui';
3
+ import { Form } from './Form';
4
+ import { FormFieldRadio } from './FormFieldRadio';
5
+ import { render, screen, userEvent } from '@ttoss/test-utils';
6
+ import { useForm } from 'react-hook-form';
7
+ import { yupResolver } from '@hookform/resolvers/yup';
8
+
9
+ const RADIO_OPTIONS = [
10
+ { value: 'Ferrari', label: 'Ferrari' },
11
+ { value: 'Mercedes', label: 'Mercedes' },
12
+ { value: 'BMW', label: 'BMW' },
13
+ ];
14
+
15
+ test('call onSubmit with correct data', async () => {
16
+ const user = userEvent.setup({ delay: null });
17
+
18
+ const onSubmit = jest.fn();
19
+
20
+ const RenderForm = () => {
21
+ const formMethods = useForm();
22
+
23
+ return (
24
+ <Form {...formMethods} onSubmit={onSubmit}>
25
+ <FormFieldRadio name="car" label="Cars" options={RADIO_OPTIONS} />
26
+ <Button type="submit">Submit</Button>
27
+ </Form>
28
+ );
29
+ };
30
+
31
+ render(<RenderForm />);
32
+
33
+ await user.click(screen.getByLabelText('BMW'));
34
+
35
+ await user.click(screen.getByText('Submit'));
36
+
37
+ expect(onSubmit).toHaveBeenCalledWith({ car: 'BMW' });
38
+ });
39
+
40
+ test('should display error messages', async () => {
41
+ const user = userEvent.setup({ delay: null });
42
+
43
+ const onSubmit = jest.fn();
44
+
45
+ const schema = yup.object({
46
+ car: yup.string().required('Car is required'),
47
+ });
48
+
49
+ const RenderForm = () => {
50
+ const formMethods = useForm({
51
+ mode: 'all',
52
+ resolver: yupResolver(schema),
53
+ });
54
+
55
+ return (
56
+ <Form {...formMethods} onSubmit={onSubmit}>
57
+ <FormFieldRadio name="car" label="Cars" options={RADIO_OPTIONS} />
58
+
59
+ <Button type="submit">Submit</Button>
60
+ </Form>
61
+ );
62
+ };
63
+
64
+ render(<RenderForm />);
65
+
66
+ await user.click(screen.getByText('Submit'));
67
+
68
+ expect(await screen.findByText('Car is required')).toBeInTheDocument();
69
+ });
@@ -0,0 +1,53 @@
1
+ import { Box, Label, Radio } from '@ttoss/ui';
2
+ import { ErrorMessage } from './ErrorMessage';
3
+ import { FieldValues, Path, useController } from 'react-hook-form';
4
+
5
+ type FormRadioOption = {
6
+ value: string | number;
7
+ label: string;
8
+ };
9
+
10
+ export const FormFieldRadio = <TFieldValues extends FieldValues = FieldValues>({
11
+ label,
12
+ name,
13
+ options,
14
+ }: {
15
+ label?: string;
16
+ name: Path<TFieldValues>;
17
+ options: FormRadioOption[];
18
+ }) => {
19
+ const {
20
+ field: { onChange, onBlur, value, ref },
21
+ } = useController<any>({
22
+ name,
23
+ defaultValue: '',
24
+ });
25
+
26
+ return (
27
+ <Box>
28
+ {label && <Label>{label}</Label>}
29
+ <Box variant="form.radios">
30
+ {options.map((option) => {
31
+ const id = `form-field-radio-${name}-${option.value}`;
32
+
33
+ return (
34
+ <Label key={id} htmlFor={id}>
35
+ <Radio
36
+ ref={ref}
37
+ onChange={onChange}
38
+ onBlur={onBlur}
39
+ value={option.value}
40
+ defaultChecked={value === option.value}
41
+ name={name}
42
+ id={id}
43
+ />
44
+ {option.label}
45
+ </Label>
46
+ );
47
+ })}
48
+ </Box>
49
+
50
+ <ErrorMessage name={name} />
51
+ </Box>
52
+ );
53
+ };
@@ -0,0 +1,73 @@
1
+ import * as yup from 'yup';
2
+ import { Button } from '@ttoss/ui';
3
+ import { Form } from './Form';
4
+ import { FormFieldSelect } from './FormFieldSelect';
5
+ import { render, screen, userEvent } from '@ttoss/test-utils';
6
+ import { useForm } from 'react-hook-form';
7
+ import { yupResolver } from '@hookform/resolvers/yup';
8
+
9
+ const RADIO_OPTIONS = [
10
+ { value: '', label: 'Select a car' },
11
+ { value: 'Ferrari', label: 'Ferrari' },
12
+ { value: 'Mercedes', label: 'Mercedes' },
13
+ { value: 'BMW', label: 'BMW' },
14
+ ];
15
+
16
+ test('call onSubmit with correct data', async () => {
17
+ const user = userEvent.setup({ delay: null });
18
+
19
+ const onSubmit = jest.fn();
20
+
21
+ const RenderForm = () => {
22
+ const formMethods = useForm();
23
+
24
+ return (
25
+ <Form {...formMethods} onSubmit={onSubmit}>
26
+ <FormFieldSelect name="car" label="Cars" options={RADIO_OPTIONS} />
27
+ <Button type="submit">Submit</Button>
28
+ </Form>
29
+ );
30
+ };
31
+
32
+ render(<RenderForm />);
33
+
34
+ await user.selectOptions(
35
+ screen.getByRole('combobox'),
36
+ screen.getByText('BMW')
37
+ );
38
+
39
+ await user.click(screen.getByText('Submit'));
40
+
41
+ expect(onSubmit).toHaveBeenCalledWith({ car: 'BMW' });
42
+ });
43
+
44
+ test('should display error messages', async () => {
45
+ const user = userEvent.setup({ delay: null });
46
+
47
+ const onSubmit = jest.fn();
48
+
49
+ const schema = yup.object({
50
+ car: yup.string().required('Car is required'),
51
+ });
52
+
53
+ const RenderForm = () => {
54
+ const formMethods = useForm({
55
+ mode: 'all',
56
+ resolver: yupResolver(schema),
57
+ });
58
+
59
+ return (
60
+ <Form {...formMethods} onSubmit={onSubmit}>
61
+ <FormFieldSelect name="car" label="Cars" options={RADIO_OPTIONS} />
62
+
63
+ <Button type="submit">Submit</Button>
64
+ </Form>
65
+ );
66
+ };
67
+
68
+ render(<RenderForm />);
69
+
70
+ await user.click(screen.getByText('Submit'));
71
+
72
+ expect(await screen.findByText('Car is required')).toBeInTheDocument();
73
+ });
@@ -0,0 +1,56 @@
1
+ import { Box, Label, Select, type SelectProps } from '@ttoss/ui';
2
+ import { ErrorMessage } from './ErrorMessage';
3
+ import { FieldValues, Path, useController } from 'react-hook-form';
4
+
5
+ type FormRadioOption = {
6
+ value: string | number;
7
+ label: string;
8
+ };
9
+
10
+ export const FormFieldSelect = <
11
+ TFieldValues extends FieldValues = FieldValues
12
+ >({
13
+ label,
14
+ name,
15
+ options,
16
+ arrow,
17
+ }: {
18
+ label?: string;
19
+ name: Path<TFieldValues>;
20
+ options: FormRadioOption[];
21
+ arrow?: SelectProps['arrow'];
22
+ }) => {
23
+ const {
24
+ field: { onChange, onBlur, value, ref },
25
+ } = useController<any>({
26
+ name,
27
+ defaultValue: '',
28
+ });
29
+
30
+ const id = `form-field-select-${name}`;
31
+
32
+ return (
33
+ <Box>
34
+ {label && <Label htmlFor={id}>{label}</Label>}
35
+ <Select
36
+ ref={ref}
37
+ name={name}
38
+ onChange={onChange}
39
+ onBlur={onBlur}
40
+ value={value}
41
+ arrow={arrow}
42
+ variant="form.select"
43
+ >
44
+ {options.map((option) => {
45
+ return (
46
+ <option key={id} value={option.value} id={id}>
47
+ {option.label}
48
+ </option>
49
+ );
50
+ })}
51
+ </Select>
52
+
53
+ <ErrorMessage name={name} />
54
+ </Box>
55
+ );
56
+ };