@toptal/picasso-forms 6.0.4 → 6.0.5

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.
Files changed (236) hide show
  1. package/CHANGELOG.md +657 -0
  2. package/{Autocomplete → dist-package/Autocomplete}/Autocomplete.d.ts +0 -0
  3. package/{Autocomplete → dist-package/Autocomplete}/Autocomplete.js +0 -0
  4. package/{Autocomplete → dist-package/Autocomplete}/Autocomplete.js.map +0 -0
  5. package/{Autocomplete → dist-package/Autocomplete}/index.d.ts +0 -0
  6. package/{Autocomplete → dist-package/Autocomplete}/index.js +0 -0
  7. package/{Autocomplete → dist-package/Autocomplete}/index.js.map +0 -0
  8. package/{ButtonCheckbox → dist-package/ButtonCheckbox}/ButtonCheckbox.d.ts +0 -0
  9. package/{ButtonCheckbox → dist-package/ButtonCheckbox}/ButtonCheckbox.js +0 -0
  10. package/{ButtonCheckbox → dist-package/ButtonCheckbox}/ButtonCheckbox.js.map +0 -0
  11. package/{ButtonCheckbox → dist-package/ButtonCheckbox}/index.d.ts +0 -0
  12. package/{ButtonCheckbox → dist-package/ButtonCheckbox}/index.js +0 -0
  13. package/{ButtonCheckbox → dist-package/ButtonCheckbox}/index.js.map +0 -0
  14. package/{ButtonRadio → dist-package/ButtonRadio}/ButtonRadio.d.ts +0 -0
  15. package/{ButtonRadio → dist-package/ButtonRadio}/ButtonRadio.js +0 -0
  16. package/{ButtonRadio → dist-package/ButtonRadio}/ButtonRadio.js.map +0 -0
  17. package/{ButtonRadio → dist-package/ButtonRadio}/index.d.ts +0 -0
  18. package/{ButtonRadio → dist-package/ButtonRadio}/index.js +0 -0
  19. package/{ButtonRadio → dist-package/ButtonRadio}/index.js.map +0 -0
  20. package/{Checkbox → dist-package/Checkbox}/Checkbox.d.ts +0 -0
  21. package/{Checkbox → dist-package/Checkbox}/Checkbox.js +0 -0
  22. package/{Checkbox → dist-package/Checkbox}/Checkbox.js.map +0 -0
  23. package/{Checkbox → dist-package/Checkbox}/index.d.ts +0 -0
  24. package/{Checkbox → dist-package/Checkbox}/index.js +0 -0
  25. package/{Checkbox → dist-package/Checkbox}/index.js.map +0 -0
  26. package/{CheckboxGroup → dist-package/CheckboxGroup}/CheckboxGroup.d.ts +0 -0
  27. package/{CheckboxGroup → dist-package/CheckboxGroup}/CheckboxGroup.js +0 -0
  28. package/{CheckboxGroup → dist-package/CheckboxGroup}/CheckboxGroup.js.map +0 -0
  29. package/{CheckboxGroup → dist-package/CheckboxGroup}/CheckboxGroupContext.d.ts +0 -0
  30. package/{CheckboxGroup → dist-package/CheckboxGroup}/CheckboxGroupContext.js +0 -0
  31. package/{CheckboxGroup → dist-package/CheckboxGroup}/CheckboxGroupContext.js.map +0 -0
  32. package/{CheckboxGroup → dist-package/CheckboxGroup}/index.d.ts +0 -0
  33. package/{CheckboxGroup → dist-package/CheckboxGroup}/index.js +0 -0
  34. package/{CheckboxGroup → dist-package/CheckboxGroup}/index.js.map +0 -0
  35. package/{DatePicker → dist-package/DatePicker}/DatePicker.d.ts +0 -0
  36. package/{DatePicker → dist-package/DatePicker}/DatePicker.js +0 -0
  37. package/{DatePicker → dist-package/DatePicker}/DatePicker.js.map +0 -0
  38. package/{DatePicker → dist-package/DatePicker}/index.d.ts +0 -0
  39. package/{DatePicker → dist-package/DatePicker}/index.js +0 -0
  40. package/{DatePicker → dist-package/DatePicker}/index.js.map +0 -0
  41. package/{FieldWrapper → dist-package/FieldWrapper}/FieldWrapper.d.ts +0 -0
  42. package/{FieldWrapper → dist-package/FieldWrapper}/FieldWrapper.js +0 -0
  43. package/{FieldWrapper → dist-package/FieldWrapper}/FieldWrapper.js.map +0 -0
  44. package/{FieldWrapper → dist-package/FieldWrapper}/index.d.ts +0 -0
  45. package/{FieldWrapper → dist-package/FieldWrapper}/index.js +0 -0
  46. package/{FieldWrapper → dist-package/FieldWrapper}/index.js.map +0 -0
  47. package/{FileInput → dist-package/FileInput}/FileInput.d.ts +0 -0
  48. package/{FileInput → dist-package/FileInput}/FileInput.js +0 -0
  49. package/{FileInput → dist-package/FileInput}/FileInput.js.map +0 -0
  50. package/{FileInput → dist-package/FileInput}/index.d.ts +0 -0
  51. package/{FileInput → dist-package/FileInput}/index.js +0 -0
  52. package/{FileInput → dist-package/FileInput}/index.js.map +0 -0
  53. package/{Form → dist-package/Form}/Form.d.ts +0 -0
  54. package/{Form → dist-package/Form}/Form.js +0 -0
  55. package/{Form → dist-package/Form}/Form.js.map +0 -0
  56. package/{Form → dist-package/Form}/FormContext.d.ts +0 -0
  57. package/{Form → dist-package/Form}/FormContext.js +0 -0
  58. package/{Form → dist-package/Form}/FormContext.js.map +0 -0
  59. package/{Form → dist-package/Form}/index.d.ts +0 -0
  60. package/{Form → dist-package/Form}/index.js +0 -0
  61. package/{Form → dist-package/Form}/index.js.map +0 -0
  62. package/{FormConfig → dist-package/FormConfig}/FormConfig.d.ts +0 -0
  63. package/{FormConfig → dist-package/FormConfig}/FormConfig.js +0 -0
  64. package/{FormConfig → dist-package/FormConfig}/FormConfig.js.map +0 -0
  65. package/{FormConfig → dist-package/FormConfig}/index.d.ts +0 -0
  66. package/{FormConfig → dist-package/FormConfig}/index.js +0 -0
  67. package/{FormConfig → dist-package/FormConfig}/index.js.map +0 -0
  68. package/{Input → dist-package/Input}/Input.d.ts +0 -0
  69. package/{Input → dist-package/Input}/Input.js +0 -0
  70. package/{Input → dist-package/Input}/Input.js.map +0 -0
  71. package/{Input → dist-package/Input}/index.d.ts +0 -0
  72. package/{Input → dist-package/Input}/index.js +0 -0
  73. package/{Input → dist-package/Input}/index.js.map +0 -0
  74. package/{Input → dist-package/Input}/utils/get-input-name.d.ts +0 -0
  75. package/{Input → dist-package/Input}/utils/get-input-name.js +0 -0
  76. package/{Input → dist-package/Input}/utils/get-input-name.js.map +0 -0
  77. package/{Input → dist-package/Input}/utils/get-input-name.test.d.ts +0 -0
  78. package/{Input → dist-package/Input}/utils/get-input-name.test.js +0 -0
  79. package/{Input → dist-package/Input}/utils/get-input-name.test.js.map +0 -0
  80. package/{NumberInput → dist-package/NumberInput}/NumberInput.d.ts +0 -0
  81. package/{NumberInput → dist-package/NumberInput}/NumberInput.js +0 -0
  82. package/{NumberInput → dist-package/NumberInput}/NumberInput.js.map +0 -0
  83. package/{NumberInput → dist-package/NumberInput}/index.d.ts +0 -0
  84. package/{NumberInput → dist-package/NumberInput}/index.js +0 -0
  85. package/{NumberInput → dist-package/NumberInput}/index.js.map +0 -0
  86. package/dist-package/README.md +29 -0
  87. package/{Radio → dist-package/Radio}/Radio.d.ts +0 -0
  88. package/{Radio → dist-package/Radio}/Radio.js +0 -0
  89. package/{Radio → dist-package/Radio}/Radio.js.map +0 -0
  90. package/{Radio → dist-package/Radio}/index.d.ts +0 -0
  91. package/{Radio → dist-package/Radio}/index.js +0 -0
  92. package/{Radio → dist-package/Radio}/index.js.map +0 -0
  93. package/{RadioGroup → dist-package/RadioGroup}/RadioGroup.d.ts +0 -0
  94. package/{RadioGroup → dist-package/RadioGroup}/RadioGroup.js +0 -0
  95. package/{RadioGroup → dist-package/RadioGroup}/RadioGroup.js.map +0 -0
  96. package/{RadioGroup → dist-package/RadioGroup}/RadioGroupContext.d.ts +0 -0
  97. package/{RadioGroup → dist-package/RadioGroup}/RadioGroupContext.js +0 -0
  98. package/{RadioGroup → dist-package/RadioGroup}/RadioGroupContext.js.map +0 -0
  99. package/{RadioGroup → dist-package/RadioGroup}/index.d.ts +0 -0
  100. package/{RadioGroup → dist-package/RadioGroup}/index.js +0 -0
  101. package/{RadioGroup → dist-package/RadioGroup}/index.js.map +0 -0
  102. package/{Rating → dist-package/Rating}/Rating.d.ts +0 -0
  103. package/{Rating → dist-package/Rating}/Rating.js +0 -0
  104. package/{Rating → dist-package/Rating}/Rating.js.map +0 -0
  105. package/{Rating → dist-package/Rating}/index.d.ts +0 -0
  106. package/{Rating → dist-package/Rating}/index.js +0 -0
  107. package/{Rating → dist-package/Rating}/index.js.map +0 -0
  108. package/{Select → dist-package/Select}/Select.d.ts +0 -0
  109. package/{Select → dist-package/Select}/Select.js +0 -0
  110. package/{Select → dist-package/Select}/Select.js.map +0 -0
  111. package/{Select → dist-package/Select}/index.d.ts +0 -0
  112. package/{Select → dist-package/Select}/index.js +0 -0
  113. package/{Select → dist-package/Select}/index.js.map +0 -0
  114. package/{SubmitButton → dist-package/SubmitButton}/SubmitButton.d.ts +0 -0
  115. package/{SubmitButton → dist-package/SubmitButton}/SubmitButton.js +0 -0
  116. package/{SubmitButton → dist-package/SubmitButton}/SubmitButton.js.map +0 -0
  117. package/{SubmitButton → dist-package/SubmitButton}/index.d.ts +0 -0
  118. package/{SubmitButton → dist-package/SubmitButton}/index.js +0 -0
  119. package/{SubmitButton → dist-package/SubmitButton}/index.js.map +0 -0
  120. package/{Switch → dist-package/Switch}/Switch.d.ts +0 -0
  121. package/{Switch → dist-package/Switch}/Switch.js +0 -0
  122. package/{Switch → dist-package/Switch}/Switch.js.map +0 -0
  123. package/{Switch → dist-package/Switch}/index.d.ts +0 -0
  124. package/{Switch → dist-package/Switch}/index.js +0 -0
  125. package/{Switch → dist-package/Switch}/index.js.map +0 -0
  126. package/{TagSelector → dist-package/TagSelector}/TagSelector.d.ts +0 -0
  127. package/{TagSelector → dist-package/TagSelector}/TagSelector.js +0 -0
  128. package/{TagSelector → dist-package/TagSelector}/TagSelector.js.map +0 -0
  129. package/{TagSelector → dist-package/TagSelector}/index.d.ts +0 -0
  130. package/{TagSelector → dist-package/TagSelector}/index.js +0 -0
  131. package/{TagSelector → dist-package/TagSelector}/index.js.map +0 -0
  132. package/{TimePicker → dist-package/TimePicker}/TimePicker.d.ts +0 -0
  133. package/{TimePicker → dist-package/TimePicker}/TimePicker.js +0 -0
  134. package/{TimePicker → dist-package/TimePicker}/TimePicker.js.map +0 -0
  135. package/{TimePicker → dist-package/TimePicker}/index.d.ts +0 -0
  136. package/{TimePicker → dist-package/TimePicker}/index.js +0 -0
  137. package/{TimePicker → dist-package/TimePicker}/index.js.map +0 -0
  138. package/{index.d.ts → dist-package/index.d.ts} +2 -1
  139. package/{index.js → dist-package/index.js} +0 -1
  140. package/dist-package/index.js.map +1 -0
  141. package/dist-package/package.json +44 -0
  142. package/{utils → dist-package/utils}/flat-map.d.ts +0 -0
  143. package/{utils → dist-package/utils}/flat-map.js +0 -0
  144. package/{utils → dist-package/utils}/flat-map.js.map +0 -0
  145. package/{utils → dist-package/utils}/index.d.ts +0 -0
  146. package/{utils → dist-package/utils}/index.js +0 -0
  147. package/{utils → dist-package/utils}/index.js.map +0 -0
  148. package/{utils → dist-package/utils}/scroll-to-error-decorator.d.ts +0 -0
  149. package/{utils → dist-package/utils}/scroll-to-error-decorator.js +0 -0
  150. package/{utils → dist-package/utils}/scroll-to-error-decorator.js.map +0 -0
  151. package/{utils → dist-package/utils}/validators.d.ts +0 -0
  152. package/{utils → dist-package/utils}/validators.js +0 -0
  153. package/{utils → dist-package/utils}/validators.js.map +0 -0
  154. package/package.json +4 -5
  155. package/src/Autocomplete/Autocomplete.tsx +21 -0
  156. package/src/Autocomplete/index.ts +1 -0
  157. package/src/ButtonCheckbox/ButtonCheckbox.tsx +57 -0
  158. package/src/ButtonCheckbox/index.ts +1 -0
  159. package/src/ButtonRadio/ButtonRadio.tsx +24 -0
  160. package/src/ButtonRadio/index.ts +1 -0
  161. package/src/Checkbox/Checkbox.tsx +73 -0
  162. package/src/Checkbox/__snapshots__/test.tsx.snap +254 -0
  163. package/src/Checkbox/index.ts +1 -0
  164. package/src/Checkbox/test.tsx +91 -0
  165. package/src/CheckboxGroup/CheckboxGroup.tsx +30 -0
  166. package/src/CheckboxGroup/CheckboxGroupContext.ts +3 -0
  167. package/src/CheckboxGroup/index.ts +3 -0
  168. package/src/CheckboxGroup/test.tsx +35 -0
  169. package/src/DatePicker/DatePicker.tsx +26 -0
  170. package/src/DatePicker/index.ts +1 -0
  171. package/src/FieldWrapper/FieldWrapper.tsx +287 -0
  172. package/src/FieldWrapper/index.ts +2 -0
  173. package/src/FieldWrapper/story/index.jsx +137 -0
  174. package/src/FileInput/FileInput.tsx +66 -0
  175. package/src/FileInput/index.ts +1 -0
  176. package/src/Form/Form.tsx +181 -0
  177. package/src/Form/FormContext.ts +38 -0
  178. package/src/Form/__image_snapshots__/form-default-snap.png +0 -0
  179. package/src/Form/__image_snapshots__/form-form-level-configurations-snap.png +0 -0
  180. package/src/Form/__snapshots__/test.tsx.snap +61 -0
  181. package/src/Form/index.ts +1 -0
  182. package/src/Form/story/BackendCommunication.example.tsx +139 -0
  183. package/src/Form/story/CustomFormLevelConfiguration.example.tsx +26 -0
  184. package/src/Form/story/CustomValidator.example.tsx +45 -0
  185. package/src/Form/story/Default.example.tsx +177 -0
  186. package/src/Form/story/FileInput.example.tsx +42 -0
  187. package/src/Form/story/ParseInput.example.tsx +28 -0
  188. package/src/Form/story/TitleCase.example.tsx +167 -0
  189. package/src/Form/story/ValidateOnSubmit.example.tsx +85 -0
  190. package/src/Form/story/index.jsx +203 -0
  191. package/src/Form/test.tsx +27 -0
  192. package/src/FormConfig/FormConfig.ts +12 -0
  193. package/src/FormConfig/index.ts +1 -0
  194. package/src/FormConfig/test.tsx +44 -0
  195. package/src/Input/Input.tsx +27 -0
  196. package/src/Input/index.ts +1 -0
  197. package/src/Input/test.tsx +34 -0
  198. package/src/Input/utils/get-input-name.test.ts +16 -0
  199. package/src/Input/utils/get-input-name.ts +11 -0
  200. package/src/NumberInput/NumberInput.tsx +45 -0
  201. package/src/NumberInput/index.ts +1 -0
  202. package/src/Radio/Radio.tsx +24 -0
  203. package/src/Radio/__snapshots__/test.tsx.snap +231 -0
  204. package/src/Radio/index.ts +1 -0
  205. package/src/Radio/test.tsx +49 -0
  206. package/src/RadioGroup/RadioGroup.tsx +39 -0
  207. package/src/RadioGroup/RadioGroupContext.ts +3 -0
  208. package/src/RadioGroup/index.ts +3 -0
  209. package/src/RadioGroup/test.tsx +35 -0
  210. package/src/Rating/Rating.tsx +22 -0
  211. package/src/Rating/index.ts +1 -0
  212. package/src/Select/Select.tsx +47 -0
  213. package/src/Select/index.ts +1 -0
  214. package/src/SubmitButton/SubmitButton.tsx +70 -0
  215. package/src/SubmitButton/__image_snapshots__/submitbutton-button-types-snap.png +0 -0
  216. package/src/SubmitButton/__image_snapshots__/submitbutton-default-snap.png +0 -0
  217. package/src/SubmitButton/index.ts +6 -0
  218. package/src/SubmitButton/story/ButtonTypes.example.tsx +46 -0
  219. package/src/SubmitButton/story/Default.example.tsx +15 -0
  220. package/src/SubmitButton/story/index.jsx +32 -0
  221. package/src/Switch/Switch.tsx +23 -0
  222. package/src/Switch/index.ts +1 -0
  223. package/src/TagSelector/TagSelector.tsx +25 -0
  224. package/src/TagSelector/index.ts +1 -0
  225. package/src/TimePicker/TimePicker.tsx +24 -0
  226. package/src/TimePicker/index.ts +1 -0
  227. package/src/index.ts +16 -0
  228. package/src/story/Deserialization.example.tsx +34 -0
  229. package/src/story/FormSpy.example.tsx +42 -0
  230. package/src/story/index.jsx +37 -0
  231. package/src/utils/flat-map.ts +4 -0
  232. package/src/utils/index.ts +3 -0
  233. package/src/utils/scroll-to-error-decorator.ts +78 -0
  234. package/src/utils/validators.ts +18 -0
  235. package/tsconfig.build.json +7 -0
  236. package/index.js.map +0 -1
@@ -0,0 +1,61 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Form renders 1`] = `
4
+ <div>
5
+ <div
6
+ class="Picasso-root"
7
+ >
8
+ <div
9
+ class=""
10
+ >
11
+ <form>
12
+ <div
13
+ class="PicassoFormField-root"
14
+ data-field-has-error="false"
15
+ >
16
+ <div
17
+ class="MuiInputBase-root MuiOutlinedInput-root PicassoOutlinedInput-root PicassoInput-root PicassoOutlinedInput-rootAuto PicassoOutlinedInput-rootMedium MuiInputBase-adornedStart MuiOutlinedInput-adornedStart MuiInputBase-adornedEnd MuiOutlinedInput-adornedEnd"
18
+ >
19
+ <input
20
+ aria-invalid="false"
21
+ autocomplete="none"
22
+ class="MuiInputBase-input MuiOutlinedInput-input PicassoOutlinedInput-input PicassoOutlinedInput-inputMedium MuiInputBase-inputAdornedStart MuiOutlinedInput-inputAdornedStart MuiInputBase-inputAdornedEnd MuiOutlinedInput-inputAdornedEnd"
23
+ id="test"
24
+ name="test"
25
+ placeholder="test input"
26
+ type="text"
27
+ value=""
28
+ />
29
+ <fieldset
30
+ aria-hidden="true"
31
+ class="PrivateNotchedOutline-root MuiOutlinedInput-notchedOutline PicassoOutlinedInput-notchedOutline"
32
+ style="padding-left: 8px;"
33
+ >
34
+ <legend
35
+ class="PrivateNotchedOutline-legend"
36
+ style="width: 0.01px;"
37
+ >
38
+ <span>
39
+
40
+ </span>
41
+ </legend>
42
+ </fieldset>
43
+ </div>
44
+ </div>
45
+ <button
46
+ class="MuiButtonBase-root PicassoButton-medium PicassoButton-primary PicassoButton-root"
47
+ data-component-type="button"
48
+ tabindex="0"
49
+ type="submit"
50
+ >
51
+ <span
52
+ class="PicassoContainer-centerAlignItems PicassoContainer-flex PicassoContainer-inline PicassoButton-content"
53
+ >
54
+ Submit
55
+ </span>
56
+ </button>
57
+ </form>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ `;
@@ -0,0 +1 @@
1
+ export { default } from './Form'
@@ -0,0 +1,139 @@
1
+ import React, { useCallback } from 'react'
2
+ import { Container } from '@toptal/picasso'
3
+ import { Form } from '@toptal/picasso-forms'
4
+
5
+ const BackendCommunicationExample = () => {
6
+ const handleSuccessSubmit = useCallback(
7
+ (values: any) => api.successSubmit(values),
8
+ []
9
+ )
10
+ const handleSubmitWithInlineError = useCallback(
11
+ (values: any) => api.submitWithInlineError(values),
12
+ []
13
+ )
14
+ const handleSubmitWithCustomNotificationError = useCallback(
15
+ (values: any) => api.submitWithCustomNotificationError(values),
16
+ []
17
+ )
18
+
19
+ return (
20
+ <Container
21
+ style={{
22
+ display: 'grid',
23
+ gap: '2rem',
24
+ gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))'
25
+ }}
26
+ >
27
+ <Form
28
+ onSubmit={handleSuccessSubmit}
29
+ successSubmitMessage='Login successful!'
30
+ >
31
+ <Form.Input
32
+ required
33
+ name='successName'
34
+ label='First name'
35
+ placeholder='e.g. Bruce'
36
+ width='full'
37
+ />
38
+ <Form.Input
39
+ required
40
+ name='successSurname'
41
+ label='Last name'
42
+ placeholder='e.g. Wayne'
43
+ width='full'
44
+ />
45
+
46
+ <Container top='small'>
47
+ <Form.SubmitButton
48
+ variant='positive'
49
+ data-testid='success-submit-button'
50
+ >
51
+ Login Success
52
+ </Form.SubmitButton>
53
+ </Container>
54
+ </Form>
55
+
56
+ <Form
57
+ onSubmit={handleSubmitWithInlineError}
58
+ failedSubmitMessage='Login failed! Please try another combination of first and last names.'
59
+ >
60
+ <Form.Input
61
+ required
62
+ name='inlineErrorName'
63
+ label='First name'
64
+ placeholder='e.g. Bruce'
65
+ width='full'
66
+ />
67
+ <Form.Input
68
+ required
69
+ name='inlineErrorSurname'
70
+ label='Last name'
71
+ placeholder='e.g. Wayne'
72
+ width='full'
73
+ />
74
+
75
+ <Container top='small'>
76
+ <Form.SubmitButton
77
+ variant='negative'
78
+ data-testid='submit-with-inline-error-button'
79
+ >
80
+ Login with Inline Error
81
+ </Form.SubmitButton>
82
+ </Container>
83
+ </Form>
84
+
85
+ <Form onSubmit={handleSubmitWithCustomNotificationError}>
86
+ <Form.Input
87
+ required
88
+ name='customNotificationErrorName'
89
+ label='First name'
90
+ placeholder='e.g. Bruce'
91
+ width='full'
92
+ />
93
+ <Form.Input
94
+ required
95
+ name='customNotificationErrorSurname'
96
+ label='Last name'
97
+ placeholder='e.g. Wayne'
98
+ width='full'
99
+ />
100
+
101
+ <Container top='small'>
102
+ <Form.SubmitButton
103
+ variant='negative'
104
+ data-testid='submit-with-custom-notification-button'
105
+ >
106
+ Login with Custom Notification Error
107
+ </Form.SubmitButton>
108
+ </Container>
109
+ </Form>
110
+ </Container>
111
+ )
112
+ }
113
+
114
+ // the emulation of the api call
115
+ const responseWithDelay = async (response: any) =>
116
+ new Promise(resolve => setTimeout(() => resolve(response), 2000))
117
+
118
+ const api = {
119
+ successSubmit: (values: any) => {
120
+ // do something with received values
121
+ console.log('Success submit. Form values:', values)
122
+
123
+ return responseWithDelay(undefined)
124
+ },
125
+ submitWithInlineError: (values: any) => {
126
+ console.log('Submit with Inline Errors. Form values:', values)
127
+
128
+ return responseWithDelay({
129
+ inlineErrorName: 'Unknown first name'
130
+ })
131
+ },
132
+ submitWithCustomNotificationError: (values: any) => {
133
+ console.log('Submit with Custom Notification Errors. Form values:', values)
134
+
135
+ return responseWithDelay('Custom Notification Message!')
136
+ }
137
+ }
138
+
139
+ export default BackendCommunicationExample
@@ -0,0 +1,26 @@
1
+ import React from 'react'
2
+ import { Container } from '@toptal/picasso'
3
+ import { Form, FormConfigProps } from '@toptal/picasso-forms'
4
+
5
+ const formConfig: FormConfigProps = {
6
+ requiredVariant: 'asterisk'
7
+ }
8
+
9
+ const Example = () => (
10
+ <Form.ConfigProvider value={formConfig}>
11
+ <Form onSubmit={() => {}}>
12
+ <Form.Input
13
+ required
14
+ name='formConfig.firstName'
15
+ label='First name'
16
+ placeholder='e.g. Bruce'
17
+ />
18
+
19
+ <Container top='small'>
20
+ <Form.SubmitButton>Submit</Form.SubmitButton>
21
+ </Container>
22
+ </Form>
23
+ </Form.ConfigProvider>
24
+ )
25
+
26
+ export default Example
@@ -0,0 +1,45 @@
1
+ import React from 'react'
2
+ import { Container } from '@toptal/picasso'
3
+ import { Form } from '@toptal/picasso-forms'
4
+
5
+ const minMaxValidator = (value?: string | number) => {
6
+ if (value === undefined) {
7
+ return undefined
8
+ }
9
+
10
+ const number = Number(value)
11
+
12
+ if (number < 0) {
13
+ return "Age can't be negative"
14
+ }
15
+
16
+ if (number > 120) {
17
+ return "Age can't have value more than 120 years"
18
+ }
19
+
20
+ return undefined
21
+ }
22
+
23
+ const CustomValidatorExample = () => (
24
+ <Form onSubmit={values => window.alert(values)}>
25
+ <Form.Input
26
+ required
27
+ name='userName'
28
+ label='First name'
29
+ placeholder='e.g. Bruce'
30
+ />
31
+ <Form.NumberInput
32
+ required
33
+ validate={minMaxValidator}
34
+ name='userAge'
35
+ label="What's your age?"
36
+ placeholder='e.g. 25'
37
+ />
38
+
39
+ <Container top='small'>
40
+ <Form.SubmitButton>Submit</Form.SubmitButton>
41
+ </Container>
42
+ </Form>
43
+ )
44
+
45
+ export default CustomValidatorExample
@@ -0,0 +1,177 @@
1
+ import React, { useState } from 'react'
2
+ import { Container } from '@toptal/picasso'
3
+ import { Item } from '@toptal/picasso/Autocomplete'
4
+ import { isSubstring } from '@toptal/picasso/utils'
5
+ import { Form } from '@toptal/picasso-forms'
6
+
7
+ const countries = [
8
+ { value: 'Afghanistan', text: 'Afghanistan' },
9
+ { value: 'Albania', text: 'Albania' },
10
+ { value: 'Algeria', text: 'Algeria' },
11
+ { value: 'Belarus', text: 'Belarus' },
12
+ { value: 'Croatia', text: 'Croatia' },
13
+ { value: 'Lithuania', text: 'Lithuania' },
14
+ { value: 'Slovakia', text: 'Slovakia' },
15
+ { value: 'Spain', text: 'Spain' },
16
+ { value: 'Ukraine', text: 'Ukraine' }
17
+ ]
18
+
19
+ const skills = [
20
+ { value: 0, text: 'HTML' },
21
+ { value: 1, text: 'CSS' },
22
+ { value: 2, text: 'Javascript' }
23
+ ]
24
+
25
+ const EMPTY_INPUT_VALUE = ''
26
+ const getAutocompleteDisplayValue = (item: Item | null) =>
27
+ item?.text || EMPTY_INPUT_VALUE
28
+
29
+ const filterOptions = (str = '', options: Item[] = []): Item[] | null => {
30
+ if (!str) {
31
+ return options
32
+ }
33
+ const result = options.filter(option =>
34
+ option?.text ? isSubstring(str, option.text) : false
35
+ )
36
+
37
+ return result.length > 0 ? result : null
38
+ }
39
+
40
+ const Example = () => {
41
+ const [skillInputValue, setSkillInputValue] = useState<string>(
42
+ EMPTY_INPUT_VALUE
43
+ )
44
+ const skillOptions = filterOptions(skillInputValue, skills)
45
+
46
+ const [autocompleteValue, setAutocompleteValue] = useState<string>(
47
+ EMPTY_INPUT_VALUE
48
+ )
49
+ const [autocompleteOptions, setAutocompleteOptions] = useState<Item[] | null>(
50
+ countries
51
+ )
52
+
53
+ return (
54
+ <Form
55
+ autoComplete='off'
56
+ onSubmit={values => window.alert(values)}
57
+ initialValues={{ gender: 'female' }}
58
+ >
59
+ <Form.Input
60
+ enableReset
61
+ onResetClick={(set: (value: string) => void) => {
62
+ set('')
63
+ }}
64
+ required
65
+ name='firstName'
66
+ label='First name'
67
+ placeholder='e.g. Bruce'
68
+ />
69
+ <Form.Input
70
+ required
71
+ name='lastName'
72
+ label='Last name'
73
+ placeholder='e.g. Wayne'
74
+ />
75
+ <Form.NumberInput
76
+ enableReset
77
+ required
78
+ name='age'
79
+ label="What's your age?"
80
+ placeholder='e.g. 25'
81
+ />
82
+ <Form.RadioGroup name='gender' label='Gender'>
83
+ <Form.Radio label='Male' value='male' />
84
+ <Form.Radio label='Female' value='female' />
85
+ </Form.RadioGroup>
86
+ <Form.RadioGroup name='gender' label='Gender' horizontal spacing={8}>
87
+ <Form.ButtonRadio value='male'>Male</Form.ButtonRadio>
88
+ <Form.ButtonRadio value='female'>Female</Form.ButtonRadio>
89
+ </Form.RadioGroup>
90
+ <Form.DatePicker name='dateOfBirth' label='Date of birth' />
91
+ <Form.TimePicker name='timeOfBirth' label='Time of birth' />
92
+ <Form.TagSelector
93
+ name='skills'
94
+ label='Skills'
95
+ inputValue={skillInputValue}
96
+ options={skillOptions}
97
+ onInputChange={setSkillInputValue}
98
+ />
99
+ <Form.CheckboxGroup name='hobbies' label='Hobbies'>
100
+ <Form.Checkbox label='Skiing' value='skiing' />
101
+ <Form.Checkbox label='Free diving' value='freeDiving' />
102
+ <Form.Checkbox label='Dancing' value='dancing' />
103
+ </Form.CheckboxGroup>
104
+ <Form.CheckboxGroup name='hobbies' label='Hobbies' horizontal spacing={8}>
105
+ <Form.ButtonCheckbox value='skiing'>Skiing</Form.ButtonCheckbox>
106
+ <Form.ButtonCheckbox value='freeDiving'>
107
+ Free diving
108
+ </Form.ButtonCheckbox>
109
+ <Form.ButtonCheckbox value='dancing'>Dancing</Form.ButtonCheckbox>
110
+ </Form.CheckboxGroup>
111
+ <Form.Select
112
+ enableReset
113
+ required
114
+ name='businessType'
115
+ label='Business type'
116
+ width='auto'
117
+ options={[
118
+ { value: 0, text: 'Company' },
119
+ { value: 1, text: 'Individual' }
120
+ ]}
121
+ />
122
+ <Form.Select
123
+ name='origin_country'
124
+ label='Origin country'
125
+ width='auto'
126
+ options={countries}
127
+ />
128
+ <Form.Autocomplete
129
+ name='current_country'
130
+ label='Current country'
131
+ placeholder='Start typing country...'
132
+ width='auto'
133
+ value={autocompleteValue}
134
+ options={autocompleteOptions}
135
+ onSelect={(item: Item) => {
136
+ console.log('onSelect returns item object:', item)
137
+
138
+ const itemValue = getAutocompleteDisplayValue(item)
139
+
140
+ if (autocompleteValue !== itemValue) {
141
+ setAutocompleteValue(itemValue)
142
+ }
143
+ }}
144
+ onChange={(newValue: string) => {
145
+ console.log('onChange returns just item value:', newValue)
146
+
147
+ setAutocompleteOptions(filterOptions(newValue, countries))
148
+ setAutocompleteValue(newValue)
149
+ }}
150
+ getDisplayValue={getAutocompleteDisplayValue}
151
+ />
152
+ <Form.Rating
153
+ name='rating'
154
+ label='How much do you love Picasso?'
155
+ required
156
+ />
157
+ <Form.FileInput
158
+ required
159
+ name='avatar'
160
+ label='Avatar'
161
+ status='No file selected.'
162
+ />
163
+ <Form.Checkbox
164
+ required
165
+ name='legal'
166
+ label='I confirm that I have legal permission from the client to feature this project.'
167
+ />
168
+ <Form.Switch name='publicProfile' label='Public Profile' width='auto' />
169
+
170
+ <Container top='small'>
171
+ <Form.SubmitButton>Submit</Form.SubmitButton>
172
+ </Container>
173
+ </Form>
174
+ )
175
+ }
176
+
177
+ export default Example
@@ -0,0 +1,42 @@
1
+ import React from 'react'
2
+ import { Container } from '@toptal/picasso'
3
+ import { Form } from '@toptal/picasso-forms'
4
+ import { FileUpload } from '@toptal/picasso/FileInput'
5
+
6
+ type FormType = {
7
+ attachments: FileUpload[]
8
+ }
9
+
10
+ const Example = () => {
11
+ const MAX_SIZE = 2
12
+ const initialAttachments = [
13
+ { file: new File(['image.png'], 'image.png') },
14
+ { file: new File(['resume.pdf'], 'resume.pdf') }
15
+ ]
16
+
17
+ const handleSubmit = ({ attachments }: FormType) => {
18
+ window.alert(
19
+ `Uploading: ${attachments.map(({ file }) => file.name).join(', ')}`
20
+ )
21
+ }
22
+
23
+ return (
24
+ <Form<FormType>
25
+ autoComplete='off'
26
+ onSubmit={handleSubmit}
27
+ initialValues={{
28
+ attachments: initialAttachments
29
+ }}
30
+ >
31
+ <Form.FileInput
32
+ name='attachments'
33
+ hint={`Max file size: ${MAX_SIZE}MB.`}
34
+ />
35
+ <Container top='small'>
36
+ <Form.SubmitButton>Submit</Form.SubmitButton>
37
+ </Container>
38
+ </Form>
39
+ )
40
+ }
41
+
42
+ export default Example
@@ -0,0 +1,28 @@
1
+ import React from 'react'
2
+ import { Container, Typography } from '@toptal/picasso'
3
+ import { Form } from '@toptal/picasso-forms'
4
+
5
+ const ParseInputExample = () => (
6
+ <Form onSubmit={values => window.alert(values)}>
7
+ <Container bottom='small'>
8
+ <Typography size='medium'>
9
+ I want to trim my first name from the empty spaces:
10
+ </Typography>
11
+ </Container>
12
+ <Container flex alignItems='flex-end'>
13
+ <Form.Input
14
+ name='parseInput.firstName'
15
+ label='First name'
16
+ placeholder='e.g. Bruce'
17
+ parse={(value: string) => value.trim()}
18
+ limit={24}
19
+ />
20
+
21
+ <Container left='small'>
22
+ <Form.SubmitButton>Submit</Form.SubmitButton>
23
+ </Container>
24
+ </Container>
25
+ </Form>
26
+ )
27
+
28
+ export default ParseInputExample
@@ -0,0 +1,167 @@
1
+ import React, { useState } from 'react'
2
+ import { Container } from '@toptal/picasso'
3
+ import { Item } from '@toptal/picasso/Autocomplete'
4
+ import { isSubstring } from '@toptal/picasso/utils'
5
+ import { Form } from '@toptal/picasso-forms'
6
+
7
+ const countries = [
8
+ { value: 'Afghanistan', text: 'Afghanistan' },
9
+ { value: 'Albania', text: 'Albania' },
10
+ { value: 'Algeria', text: 'Algeria' },
11
+ { value: 'Belarus', text: 'Belarus' },
12
+ { value: 'Croatia', text: 'Croatia' },
13
+ { value: 'Lithuania', text: 'Lithuania' },
14
+ { value: 'Slovakia', text: 'Slovakia' },
15
+ { value: 'Spain', text: 'Spain' },
16
+ { value: 'Ukraine', text: 'Ukraine' }
17
+ ]
18
+
19
+ const skills = [
20
+ { value: 0, text: 'HTML' },
21
+ { value: 1, text: 'CSS' },
22
+ { value: 2, text: 'Javascript' }
23
+ ]
24
+
25
+ const EMPTY_INPUT_VALUE = ''
26
+ const getAutocompleteDisplayValue = (item: Item | null) =>
27
+ item?.text || EMPTY_INPUT_VALUE
28
+
29
+ const filterOptions = (str = '', options: Item[] = []): Item[] | null => {
30
+ if (!str) {
31
+ return options
32
+ }
33
+ const result = options.filter(option =>
34
+ option?.text ? isSubstring(str, option.text) : false
35
+ )
36
+
37
+ return result.length > 0 ? result : null
38
+ }
39
+
40
+ const Example = () => {
41
+ const [skillInputValue, setSkillInputValue] = useState<string>(
42
+ EMPTY_INPUT_VALUE
43
+ )
44
+ const skillOptions = filterOptions(skillInputValue, skills)
45
+
46
+ const [autocompleteValue, setAutocompleteValue] = useState<string>(
47
+ EMPTY_INPUT_VALUE
48
+ )
49
+ const [autocompleteOptions, setAutocompleteOptions] = useState<Item[] | null>(
50
+ countries
51
+ )
52
+
53
+ return (
54
+ <Form
55
+ autoComplete='off'
56
+ onSubmit={values => window.alert(values)}
57
+ initialValues={{ gender: 'female' }}
58
+ >
59
+ <Form.Input
60
+ titleCase
61
+ enableReset
62
+ onResetClick={(set: (value: string) => void) => {
63
+ set('')
64
+ }}
65
+ required
66
+ name='firstName'
67
+ label='First name'
68
+ placeholder='e.g. Bruce'
69
+ />
70
+ <Form.NumberInput
71
+ titleCase
72
+ enableReset
73
+ required
74
+ name='age'
75
+ label="What's your age?"
76
+ placeholder='e.g. 25'
77
+ />
78
+ <Form.RadioGroup titleCase name='gender' label='Select gender'>
79
+ <Form.Radio label='Male' value='male' />
80
+ <Form.Radio label='Female' value='female' />
81
+ </Form.RadioGroup>
82
+ <Form.DatePicker titleCase name='dateOfBirth' label='Date of birth' />
83
+ <Form.TimePicker titleCase name='timeOfBirth' label='Time of birth' />
84
+ <Form.TagSelector
85
+ titleCase
86
+ name='skills'
87
+ label='Your skills'
88
+ inputValue={skillInputValue}
89
+ options={skillOptions}
90
+ onInputChange={setSkillInputValue}
91
+ />
92
+ <Form.CheckboxGroup titleCase name='hobbies' label='Your hobbies'>
93
+ <Form.Checkbox label='Skiing' value='skiing' />
94
+ <Form.Checkbox titleCase label='Free diving' value='freeDiving' />
95
+ <Form.Checkbox label='Dancing' value='dancing' />
96
+ </Form.CheckboxGroup>
97
+ <Form.Select
98
+ titleCase
99
+ enableReset
100
+ required
101
+ name='businessType'
102
+ label='Business type'
103
+ width='auto'
104
+ options={[
105
+ { value: 0, text: 'Company' },
106
+ { value: 1, text: 'Individual' }
107
+ ]}
108
+ />
109
+ <Form.Autocomplete
110
+ titleCase
111
+ name='current_country'
112
+ label='Current country'
113
+ placeholder='Start typing country...'
114
+ width='auto'
115
+ value={autocompleteValue}
116
+ options={autocompleteOptions}
117
+ onSelect={(item: Item) => {
118
+ console.log('onSelect returns item object:', item)
119
+
120
+ const itemValue = getAutocompleteDisplayValue(item)
121
+
122
+ if (autocompleteValue !== itemValue) {
123
+ setAutocompleteValue(itemValue)
124
+ }
125
+ }}
126
+ onChange={(newValue: string) => {
127
+ console.log('onChange returns just item value:', newValue)
128
+
129
+ setAutocompleteOptions(filterOptions(newValue, countries))
130
+ setAutocompleteValue(newValue)
131
+ }}
132
+ getDisplayValue={getAutocompleteDisplayValue}
133
+ />
134
+ <Form.Rating
135
+ titleCase
136
+ name='rating'
137
+ label='How much do you love Picasso?'
138
+ required
139
+ />
140
+ <Form.FileInput
141
+ titleCase
142
+ required
143
+ name='avatar'
144
+ label='Your avatar'
145
+ status='No file selected.'
146
+ />
147
+ <Form.Checkbox
148
+ titleCase
149
+ required
150
+ name='legal'
151
+ label='I confirm that I have legal permission from the client to feature this project.'
152
+ />
153
+ <Form.Switch
154
+ titleCase
155
+ name='publicProfile'
156
+ label='Public profile'
157
+ width='auto'
158
+ />
159
+
160
+ <Container top='small'>
161
+ <Form.SubmitButton>Submit</Form.SubmitButton>
162
+ </Container>
163
+ </Form>
164
+ )
165
+ }
166
+
167
+ export default Example