@hyphen/hyphen-components 2.9.0

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 (319) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/package.json +138 -0
  4. package/src/components/Alert/Alert.constants.ts +19 -0
  5. package/src/components/Alert/Alert.mdx +29 -0
  6. package/src/components/Alert/Alert.module.scss +74 -0
  7. package/src/components/Alert/Alert.stories.tsx +102 -0
  8. package/src/components/Alert/Alert.test.tsx +187 -0
  9. package/src/components/Alert/Alert.tsx +157 -0
  10. package/src/components/Alert/Alert.types.ts +14 -0
  11. package/src/components/Badge/Badge.mdx +28 -0
  12. package/src/components/Badge/Badge.module.scss +155 -0
  13. package/src/components/Badge/Badge.stories.tsx +52 -0
  14. package/src/components/Badge/Badge.test.tsx +74 -0
  15. package/src/components/Badge/Badge.tsx +70 -0
  16. package/src/components/Box/Box.mdx +259 -0
  17. package/src/components/Box/Box.module.scss +16 -0
  18. package/src/components/Box/Box.stories.tsx +1679 -0
  19. package/src/components/Box/Box.test.tsx +478 -0
  20. package/src/components/Box/Box.tsx +636 -0
  21. package/src/components/Button/Button.constants.ts +10 -0
  22. package/src/components/Button/Button.mdx +71 -0
  23. package/src/components/Button/Button.module.scss +312 -0
  24. package/src/components/Button/Button.stories.tsx +117 -0
  25. package/src/components/Button/Button.test.tsx +460 -0
  26. package/src/components/Button/Button.tsx +241 -0
  27. package/src/components/Card/Card.mdx +46 -0
  28. package/src/components/Card/Card.module.scss +6 -0
  29. package/src/components/Card/Card.stories.tsx +101 -0
  30. package/src/components/Card/Card.test.tsx +11 -0
  31. package/src/components/Card/Card.tsx +61 -0
  32. package/src/components/Card/components/CardFooter/CardFooter.test.tsx +11 -0
  33. package/src/components/Card/components/CardFooter/CardFooter.tsx +35 -0
  34. package/src/components/Card/components/CardHeader/CardHeader.test.tsx +23 -0
  35. package/src/components/Card/components/CardHeader/CardHeader.tsx +54 -0
  36. package/src/components/Card/components/CardSection/CardSection.test.tsx +28 -0
  37. package/src/components/Card/components/CardSection/CardSection.tsx +102 -0
  38. package/src/components/Card/components/index.ts +3 -0
  39. package/src/components/CheckboxInput/CheckboxInput.mdx +98 -0
  40. package/src/components/CheckboxInput/CheckboxInput.stories.tsx +254 -0
  41. package/src/components/CheckboxInput/CheckboxInput.test.tsx +436 -0
  42. package/src/components/CheckboxInput/CheckboxInput.tsx +171 -0
  43. package/src/components/CheckboxInput/components/Checkbox.module.scss +174 -0
  44. package/src/components/CheckboxInput/components/Checkbox.test.tsx +201 -0
  45. package/src/components/CheckboxInput/components/Checkbox.tsx +176 -0
  46. package/src/components/CheckboxInput/components/CheckboxIcon.tsx +71 -0
  47. package/src/components/DateInput/DateInput.mdx +61 -0
  48. package/src/components/DateInput/DateInput.stories.tsx +168 -0
  49. package/src/components/DateInput/DateInput.test.tsx +258 -0
  50. package/src/components/DateInput/DateInput.tsx +189 -0
  51. package/src/components/DatePicker/DatePicker.mdx +52 -0
  52. package/src/components/DatePicker/DatePicker.module.scss +603 -0
  53. package/src/components/DatePicker/DatePicker.stories.tsx +199 -0
  54. package/src/components/DatePicker/DatePicker.test.tsx +26 -0
  55. package/src/components/DatePicker/DatePicker.tsx +138 -0
  56. package/src/components/Details/Details.mdx +30 -0
  57. package/src/components/Details/Details.module.scss +32 -0
  58. package/src/components/Details/Details.stories.tsx +38 -0
  59. package/src/components/Details/Details.test.tsx +189 -0
  60. package/src/components/Details/Details.tsx +51 -0
  61. package/src/components/Details/DetailsSummary.tsx +65 -0
  62. package/src/components/Drawer/Drawer.mdx +117 -0
  63. package/src/components/Drawer/Drawer.module.scss +96 -0
  64. package/src/components/Drawer/Drawer.stories.tsx +380 -0
  65. package/src/components/Drawer/Drawer.test.tsx +90 -0
  66. package/src/components/Drawer/Drawer.tsx +249 -0
  67. package/src/components/FormControl/FormControl.tsx +78 -0
  68. package/src/components/FormLabel/FormLabel.mdx +24 -0
  69. package/src/components/FormLabel/FormLabel.module.scss +19 -0
  70. package/src/components/FormLabel/FormLabel.stories.tsx +20 -0
  71. package/src/components/FormLabel/FormLabel.test.tsx +35 -0
  72. package/src/components/FormLabel/FormLabel.tsx +96 -0
  73. package/src/components/Formik/Formik.mdx +10 -0
  74. package/src/components/Formik/Formik.stories.tsx +307 -0
  75. package/src/components/Formik/FormikCheckboxInput/FormikCheckboxInput.test.tsx +172 -0
  76. package/src/components/Formik/FormikCheckboxInput/FormikCheckboxInput.tsx +41 -0
  77. package/src/components/Formik/FormikRadioGroup/FormikRadioGroup.test.tsx +205 -0
  78. package/src/components/Formik/FormikRadioGroup/FormikRadioGroup.tsx +37 -0
  79. package/src/components/Formik/FormikSelectInput/FormikSelectInput.test.tsx +210 -0
  80. package/src/components/Formik/FormikSelectInput/FormikSelectInput.tsx +41 -0
  81. package/src/components/Formik/FormikSelectInputInset/FormikSelectInputInset.test.tsx +153 -0
  82. package/src/components/Formik/FormikSelectInputInset/FormikSelectInputInset.tsx +44 -0
  83. package/src/components/Formik/FormikSelectInputNative/FormikSelectInputNative.test.tsx +161 -0
  84. package/src/components/Formik/FormikSelectInputNative/FormikSelectInputNative.tsx +46 -0
  85. package/src/components/Formik/FormikTextInput/FormikTextInput.test.tsx +176 -0
  86. package/src/components/Formik/FormikTextInput/FormikTextInput.tsx +38 -0
  87. package/src/components/Formik/FormikTextInputInset/FormikTextInputInset.test.tsx +170 -0
  88. package/src/components/Formik/FormikTextInputInset/FormikTextInputInset.tsx +42 -0
  89. package/src/components/Formik/FormikTextareaInput/FormikTextareaInput.test.tsx +186 -0
  90. package/src/components/Formik/FormikTextareaInput/FormikTextareaInput.tsx +42 -0
  91. package/src/components/Formik/FormikTextareaInputInset/FormikTextareaInputInset.test.tsx +179 -0
  92. package/src/components/Formik/FormikTextareaInputInset/FormikTextareaInputInset.tsx +42 -0
  93. package/src/components/Formik/FormikTimePicker/FormikTimePicker.test.tsx +224 -0
  94. package/src/components/Formik/FormikTimePicker/FormikTimePicker.tsx +37 -0
  95. package/src/components/Formik/FormikTimePickerNative/FormikTimePickerNative.test.tsx +175 -0
  96. package/src/components/Formik/FormikTimePickerNative/FormikTimePickerNative.tsx +38 -0
  97. package/src/components/Formik/FormikToggle/FormikToggle.test.tsx +172 -0
  98. package/src/components/Formik/FormikToggle/FormikToggle.tsx +38 -0
  99. package/src/components/Heading/Heading.constants.ts +19 -0
  100. package/src/components/Heading/Heading.mdx +35 -0
  101. package/src/components/Heading/Heading.module.scss +5 -0
  102. package/src/components/Heading/Heading.stories.tsx +90 -0
  103. package/src/components/Heading/Heading.test.tsx +67 -0
  104. package/src/components/Heading/Heading.tsx +67 -0
  105. package/src/components/HelpText/HelpText.module.scss +14 -0
  106. package/src/components/HelpText/HelpText.tsx +33 -0
  107. package/src/components/Icon/Icon.mdx +40 -0
  108. package/src/components/Icon/Icon.stories.tsx +72 -0
  109. package/src/components/Icon/Icon.test.tsx +30 -0
  110. package/src/components/Icon/Icon.tsx +61 -0
  111. package/src/components/InputValidationMessage/InputValidationMessage.module.scss +3 -0
  112. package/src/components/InputValidationMessage/InputValidationMessage.tsx +27 -0
  113. package/src/components/Modal/Modal.mdx +60 -0
  114. package/src/components/Modal/Modal.module.scss +135 -0
  115. package/src/components/Modal/Modal.stories.tsx +194 -0
  116. package/src/components/Modal/Modal.test.tsx +81 -0
  117. package/src/components/Modal/Modal.tsx +174 -0
  118. package/src/components/Modal/components/ModalBody/ModalBody.test.tsx +20 -0
  119. package/src/components/Modal/components/ModalBody/ModalBody.tsx +24 -0
  120. package/src/components/Modal/components/ModalFooter/ModalFooter.test.tsx +32 -0
  121. package/src/components/Modal/components/ModalFooter/ModalFooter.tsx +37 -0
  122. package/src/components/Modal/components/ModalHeader/ModalHeader.test.tsx +29 -0
  123. package/src/components/Modal/components/ModalHeader/ModalHeader.tsx +58 -0
  124. package/src/components/Modal/components/index.ts +5 -0
  125. package/src/components/Pagination/Pagination.mdx +26 -0
  126. package/src/components/Pagination/Pagination.stories.tsx +55 -0
  127. package/src/components/Pagination/Pagination.test.tsx +225 -0
  128. package/src/components/Pagination/Pagination.tsx +162 -0
  129. package/src/components/Pagination/Pagination.utilities.test.ts +133 -0
  130. package/src/components/Pagination/Pagination.utilities.ts +101 -0
  131. package/src/components/Popover/Popover.mdx +104 -0
  132. package/src/components/Popover/Popover.module.scss +74 -0
  133. package/src/components/Popover/Popover.stories.tsx +471 -0
  134. package/src/components/Popover/Popover.test.tsx +128 -0
  135. package/src/components/Popover/Popover.tsx +277 -0
  136. package/src/components/RadioGroup/RadioGroup.mdx +81 -0
  137. package/src/components/RadioGroup/RadioGroup.module.scss +23 -0
  138. package/src/components/RadioGroup/RadioGroup.stories.tsx +375 -0
  139. package/src/components/RadioGroup/RadioGroup.test.tsx +282 -0
  140. package/src/components/RadioGroup/RadioGroup.tsx +145 -0
  141. package/src/components/RadioGroup/RadioInput/RadioInput.module.scss +114 -0
  142. package/src/components/RadioGroup/RadioInput/RadioInput.test.tsx +156 -0
  143. package/src/components/RadioGroup/RadioInput/RadioInput.tsx +148 -0
  144. package/src/components/RadioGroup/RadioInput/RadioInputIcon.tsx +59 -0
  145. package/src/components/ResponsiveProvider/ResponsiveProvider.mdx +36 -0
  146. package/src/components/ResponsiveProvider/ResponsiveProvider.stories.tsx +54 -0
  147. package/src/components/ResponsiveProvider/ResponsiveProvider.test.tsx +70 -0
  148. package/src/components/ResponsiveProvider/ResponsiveProvider.tsx +73 -0
  149. package/src/components/SelectInput/SelectInput.mdx +115 -0
  150. package/src/components/SelectInput/SelectInput.module.scss +357 -0
  151. package/src/components/SelectInput/SelectInput.stories.tsx +373 -0
  152. package/src/components/SelectInput/SelectInput.test.tsx +403 -0
  153. package/src/components/SelectInput/SelectInput.tsx +245 -0
  154. package/src/components/SelectInputInset/SelectInputInset.mdx +56 -0
  155. package/src/components/SelectInputInset/SelectInputInset.module.scss +397 -0
  156. package/src/components/SelectInputInset/SelectInputInset.stories.tsx +189 -0
  157. package/src/components/SelectInputInset/SelectInputInset.test.tsx +305 -0
  158. package/src/components/SelectInputInset/SelectInputInset.tsx +235 -0
  159. package/src/components/SelectInputNative/SelectInputNative.mdx +87 -0
  160. package/src/components/SelectInputNative/SelectInputNative.module.scss +356 -0
  161. package/src/components/SelectInputNative/SelectInputNative.stories.tsx +282 -0
  162. package/src/components/SelectInputNative/SelectInputNative.test.tsx +341 -0
  163. package/src/components/SelectInputNative/SelectInputNative.tsx +121 -0
  164. package/src/components/Spinner/Spinner.mdx +29 -0
  165. package/src/components/Spinner/Spinner.module.scss +16 -0
  166. package/src/components/Spinner/Spinner.stories.tsx +48 -0
  167. package/src/components/Spinner/Spinner.test.tsx +47 -0
  168. package/src/components/Spinner/Spinner.tsx +116 -0
  169. package/src/components/Table/Table.mdx +216 -0
  170. package/src/components/Table/Table.module.scss +61 -0
  171. package/src/components/Table/Table.stories.tsx +884 -0
  172. package/src/components/Table/Table.test.tsx +437 -0
  173. package/src/components/Table/Table.tsx +171 -0
  174. package/src/components/Table/TableBody/TableBody.module.scss +19 -0
  175. package/src/components/Table/TableBody/TableBody.test.tsx +38 -0
  176. package/src/components/Table/TableBody/TableBody.tsx +96 -0
  177. package/src/components/Table/TableBody/TableBodyCell/TableBodyCell.module.scss +47 -0
  178. package/src/components/Table/TableBody/TableBodyCell/TableBodyCell.test.tsx +81 -0
  179. package/src/components/Table/TableBody/TableBodyCell/TableBodyCell.tsx +94 -0
  180. package/src/components/Table/TableHead/TableHead.test.tsx +20 -0
  181. package/src/components/Table/TableHead/TableHead.tsx +78 -0
  182. package/src/components/Table/TableHead/TableHeaderCell/TableHeaderCell.module.scss +72 -0
  183. package/src/components/Table/TableHead/TableHeaderCell/TableHeaderCell.test.tsx +187 -0
  184. package/src/components/Table/TableHead/TableHeaderCell/TableHeaderCell.tsx +192 -0
  185. package/src/components/Table/common/TableRow/TableRow.module.scss +5 -0
  186. package/src/components/Table/common/TableRow/TableRow.test.tsx +52 -0
  187. package/src/components/Table/common/TableRow/TableRow.tsx +155 -0
  188. package/src/components/TextInput/TextInput.mdx +96 -0
  189. package/src/components/TextInput/TextInput.module.scss +405 -0
  190. package/src/components/TextInput/TextInput.stories.tsx +268 -0
  191. package/src/components/TextInput/TextInput.test.tsx +231 -0
  192. package/src/components/TextInput/TextInput.tsx +263 -0
  193. package/src/components/TextInputInset/TextInputInset.mdx +62 -0
  194. package/src/components/TextInputInset/TextInputInset.module.scss +418 -0
  195. package/src/components/TextInputInset/TextInputInset.stories.tsx +213 -0
  196. package/src/components/TextInputInset/TextInputInset.test.tsx +222 -0
  197. package/src/components/TextInputInset/TextInputInset.tsx +261 -0
  198. package/src/components/TextareaInput/TextareaInput.mdx +117 -0
  199. package/src/components/TextareaInput/TextareaInput.module.scss +275 -0
  200. package/src/components/TextareaInput/TextareaInput.stories.tsx +293 -0
  201. package/src/components/TextareaInput/TextareaInput.test.tsx +195 -0
  202. package/src/components/TextareaInput/TextareaInput.tsx +182 -0
  203. package/src/components/TextareaInputInset/TextareaInputInset.mdx +55 -0
  204. package/src/components/TextareaInputInset/TextareaInputInset.module.scss +337 -0
  205. package/src/components/TextareaInputInset/TextareaInputInset.stories.tsx +160 -0
  206. package/src/components/TextareaInputInset/TextareaInputInset.test.tsx +199 -0
  207. package/src/components/TextareaInputInset/TextareaInputInset.tsx +213 -0
  208. package/src/components/ThemeProvider/ThemeProvider.mdx +11 -0
  209. package/src/components/ThemeProvider/ThemeProvider.stories.tsx +56 -0
  210. package/src/components/ThemeProvider/ThemeProvider.tsx +75 -0
  211. package/src/components/TimePicker/TimePicker.mdx +75 -0
  212. package/src/components/TimePicker/TimePicker.stories.tsx +149 -0
  213. package/src/components/TimePicker/TimePicker.test.tsx +97 -0
  214. package/src/components/TimePicker/TimePicker.tsx +83 -0
  215. package/src/components/TimePickerNative/TimePickerNative.mdx +67 -0
  216. package/src/components/TimePickerNative/TimePickerNative.stories.tsx +151 -0
  217. package/src/components/TimePickerNative/TimePickerNative.test.tsx +117 -0
  218. package/src/components/TimePickerNative/TimePickerNative.tsx +93 -0
  219. package/src/components/Toast/Toast.mdx +134 -0
  220. package/src/components/Toast/Toast.store.ts +280 -0
  221. package/src/components/Toast/Toast.stories.tsx +283 -0
  222. package/src/components/Toast/Toast.test.tsx +784 -0
  223. package/src/components/Toast/Toast.types.ts +98 -0
  224. package/src/components/Toast/ToastContainer.tsx +170 -0
  225. package/src/components/Toast/ToastNotification.module.scss +63 -0
  226. package/src/components/Toast/ToastNotification.tsx +176 -0
  227. package/src/components/Toast/index.ts +4 -0
  228. package/src/components/Toast/toast.ts +102 -0
  229. package/src/components/Toast/useToasts.ts +102 -0
  230. package/src/components/Toggle/Toggle.mdx +51 -0
  231. package/src/components/Toggle/Toggle.module.scss +294 -0
  232. package/src/components/Toggle/Toggle.stories.tsx +128 -0
  233. package/src/components/Toggle/Toggle.test.tsx +308 -0
  234. package/src/components/Toggle/Toggle.tsx +184 -0
  235. package/src/constants/keyCodes.ts +2 -0
  236. package/src/docs/Brands.mdx +153 -0
  237. package/src/docs/Colors.mdx +158 -0
  238. package/src/docs/DesignTokens.mdx +415 -0
  239. package/src/docs/GetStarted.mdx +47 -0
  240. package/src/docs/intro.mdx +12 -0
  241. package/src/fonts/AvenirBold.otf +0 -0
  242. package/src/fonts/AvenirBold.woff +0 -0
  243. package/src/fonts/AvenirBold.woff2 +0 -0
  244. package/src/fonts/AvenirLight.otf +0 -0
  245. package/src/fonts/AvenirLight.woff +0 -0
  246. package/src/fonts/AvenirLight.woff2 +0 -0
  247. package/src/fonts/AvenirRegular.otf +0 -0
  248. package/src/fonts/AvenirRegular.woff +0 -0
  249. package/src/fonts/AvenirRegular.woff2 +0 -0
  250. package/src/fonts/Geist-Bold.otf +0 -0
  251. package/src/fonts/Geist-Bold.woff +0 -0
  252. package/src/fonts/Geist-Bold.woff2 +0 -0
  253. package/src/fonts/Geist-Medium.otf +0 -0
  254. package/src/fonts/Geist-Medium.woff +0 -0
  255. package/src/fonts/Geist-Medium.woff2 +0 -0
  256. package/src/fonts/Geist-Regular.otf +0 -0
  257. package/src/fonts/Geist-Regular.woff +0 -0
  258. package/src/fonts/Geist-Regular.woff2 +0 -0
  259. package/src/fonts/Geist-SemiBold.otf +0 -0
  260. package/src/fonts/Geist-SemiBold.woff +0 -0
  261. package/src/fonts/Geist-SemiBold.woff2 +0 -0
  262. package/src/fonts/GeistMono-Regular.otf +0 -0
  263. package/src/fonts/GeistMono-Regular.woff +0 -0
  264. package/src/fonts/GeistMono-Regular.woff2 +0 -0
  265. package/src/hooks/index.ts +4 -0
  266. package/src/hooks/useBreakpoint/useBreakpoint.mdx +26 -0
  267. package/src/hooks/useBreakpoint/useBreakpoint.stories.tsx +30 -0
  268. package/src/hooks/useBreakpoint/useBreakpoint.test.tsx +19 -0
  269. package/src/hooks/useBreakpoint/useBreakpoint.ts +50 -0
  270. package/src/hooks/useIsomorphicLayoutEffect/useIsomorphicLayouEffect.ts +5 -0
  271. package/src/hooks/useOpenClose/useOpenClose.mdx +15 -0
  272. package/src/hooks/useOpenClose/useOpenClose.stories.tsx +41 -0
  273. package/src/hooks/useOpenClose/useOpenClose.test.tsx +119 -0
  274. package/src/hooks/useOpenClose/useOpenClose.tsx +95 -0
  275. package/src/hooks/useWindowSize/useWindowSize.mdx +25 -0
  276. package/src/hooks/useWindowSize/useWindowSize.stories.tsx +35 -0
  277. package/src/hooks/useWindowSize/useWindowSize.ts +24 -0
  278. package/src/index.ts +48 -0
  279. package/src/lib/cssShorthandToClasses.test.ts +149 -0
  280. package/src/lib/cssShorthandToClasses.ts +133 -0
  281. package/src/lib/doesStringIncludeCssUnit.ts +6 -0
  282. package/src/lib/generateResponsiveClasses.test.ts +24 -0
  283. package/src/lib/generateResponsiveClasses.ts +30 -0
  284. package/src/lib/getAutoCompleteValue.test.ts +27 -0
  285. package/src/lib/getAutoCompleteValue.ts +12 -0
  286. package/src/lib/getColumnKeys.ts +27 -0
  287. package/src/lib/getDimensionCss.test.ts +148 -0
  288. package/src/lib/getDimensionCss.ts +73 -0
  289. package/src/lib/getElementType.test.tsx +42 -0
  290. package/src/lib/getElementType.ts +42 -0
  291. package/src/lib/getFlexCss.test.ts +122 -0
  292. package/src/lib/getFlexCss.ts +67 -0
  293. package/src/lib/index.ts +15 -0
  294. package/src/lib/isFunction.ts +6 -0
  295. package/src/lib/mergeRefs.ts +15 -0
  296. package/src/lib/prefersReducedMotion.ts +12 -0
  297. package/src/lib/react-children-utilities/filter.ts +12 -0
  298. package/src/lib/react-children-utilities/index.ts +1 -0
  299. package/src/lib/reactRouterClickHandler.ts +37 -0
  300. package/src/lib/resolveValue.ts +7 -0
  301. package/src/lib/tokens.ts +139 -0
  302. package/src/modes.ts +8 -0
  303. package/src/styles/animation.scss +152 -0
  304. package/src/styles/cursor.scss +43 -0
  305. package/src/styles/display.scss +119 -0
  306. package/src/styles/flex.scss +453 -0
  307. package/src/styles/fonts.scss +44 -0
  308. package/src/styles/globals/utilities.scss +4 -0
  309. package/src/styles/mixins.scss +14 -0
  310. package/src/styles/overflow.scss +41 -0
  311. package/src/styles/position.scss +45 -0
  312. package/src/styles/reset.scss +108 -0
  313. package/src/styles/text-align.scss +21 -0
  314. package/src/styles/utilities.scss +9 -0
  315. package/src/styles/variables/forms.scss +71 -0
  316. package/src/styles/variables/index.scss +3 -0
  317. package/src/styles/white-space.scss +21 -0
  318. package/src/types/index.ts +201 -0
  319. package/src/types/lib.types.ts +3 -0
@@ -0,0 +1,161 @@
1
+ import React from 'react';
2
+ import { render, fireEvent, screen, waitFor } from '@testing-library/react';
3
+ import { Formik, Form, Field, getIn, setIn, FormikValues } from 'formik';
4
+ import { FormikSelectInputNative } from './FormikSelectInputNative';
5
+
6
+ const testLabelName = 'test select';
7
+
8
+ const selectOptions = [
9
+ { value: 'chocolate', label: 'Chocolate' },
10
+ { value: 'strawberry', label: 'Strawberry' },
11
+ { value: 'vanilla', label: 'Vanilla' },
12
+ ];
13
+
14
+ const handleValidation = (testValueKey: string) => (values: FormikValues) =>
15
+ getIn(values, testValueKey)?.length > 1
16
+ ? {}
17
+ : setIn({}, testValueKey, 'input is required');
18
+
19
+ const renderForm = (
20
+ initialValue: any,
21
+ props: any,
22
+ testValueKey = testLabelName
23
+ ) => (
24
+ <Formik
25
+ initialValues={{
26
+ [testLabelName]: initialValue,
27
+ }}
28
+ validate={props.isRequired ? handleValidation(testValueKey) : undefined}
29
+ onSubmit={() => {}}
30
+ >
31
+ {() => (
32
+ <Form data-testid="form">
33
+ <Field
34
+ label={testValueKey}
35
+ name={testValueKey}
36
+ id={testValueKey}
37
+ options={selectOptions}
38
+ component={FormikSelectInputNative}
39
+ {...props}
40
+ />
41
+ <button type="submit">submit</button>
42
+ </Form>
43
+ )}
44
+ </Formik>
45
+ );
46
+
47
+ describe('SelectInputNative', () => {
48
+ describe('States', () => {
49
+ describe('Hidden label, with a placeholder', () => {
50
+ test('it renders input without a visual label, and with a placeholder', () => {
51
+ render(
52
+ renderForm(null, { placeholder: 'Test Placeholder', hideLabel: true })
53
+ );
54
+ expect(screen.queryByText(testLabelName)).toBeNull();
55
+ expect(screen.getByText('Test Placeholder')).toBeInTheDocument();
56
+ });
57
+ });
58
+
59
+ describe('No Aria-labelledby', () => {
60
+ test('does not assign "aria-labelledby" attribute when a label is hidden', () => {
61
+ render(renderForm(null, { hideLabel: true }));
62
+ const inputElement = screen.getByLabelText(testLabelName);
63
+ expect(inputElement).not.toHaveAttribute('aria-labelledby');
64
+ });
65
+ });
66
+
67
+ describe('With a label, and no custom placeholder', () => {
68
+ test('it renders input with a label, and with a default placeholder', () => {
69
+ render(renderForm(null, {}));
70
+
71
+ expect(screen.getByLabelText(testLabelName)).toBeInTheDocument();
72
+ expect(screen.getByText('Select...')).toBeInTheDocument();
73
+ });
74
+
75
+ test('assigns the "aria-labelledby" attribute and renders label with correct id, when label is provided', () => {
76
+ render(renderForm(null, {}));
77
+ const inputElement = screen.getByLabelText(testLabelName);
78
+ expect(inputElement).toHaveAttribute(
79
+ 'aria-labelledby',
80
+ `${testLabelName}Label`
81
+ );
82
+ expect(
83
+ document.getElementById(`${testLabelName}Label`)
84
+ ).toBeInTheDocument();
85
+ });
86
+ });
87
+
88
+ describe('Single select, pre-selected', () => {
89
+ test('it renders with value pre-selected', () => {
90
+ render(renderForm(selectOptions[2].value, {}));
91
+
92
+ expect(screen.getByText('Vanilla')).toBeInTheDocument();
93
+ });
94
+ });
95
+
96
+ describe('Is Required', () => {
97
+ test('it sets aria-required on the input', () => {
98
+ render(renderForm(undefined, { isRequired: true }));
99
+ const inputElement = screen.getByLabelText(testLabelName);
100
+ expect(inputElement).toHaveAttribute('aria-required', 'true');
101
+ });
102
+ });
103
+
104
+ describe('Is Disabled', () => {
105
+ test('it disables the input', () => {
106
+ render(renderForm(null, { isDisabled: true }));
107
+
108
+ expect(screen.getByLabelText('test select')).toBeDisabled();
109
+ });
110
+ });
111
+
112
+ describe('Is Invalid, with a helpful message', () => {
113
+ test('it renders the helpful message', async () => {
114
+ render(renderForm(null, { isRequired: true }));
115
+ const form = screen.getByTestId('form');
116
+ // const submitButton = getByText('submit').closest('button');
117
+
118
+ fireEvent.submit(form);
119
+ await waitFor(() =>
120
+ expect(screen.getByText('input is required')).toBeInTheDocument()
121
+ );
122
+ });
123
+
124
+ test('it renders the error message from nested object', async () => {
125
+ render(
126
+ renderForm(
127
+ { outer: { nested: null } },
128
+ { isRequired: true },
129
+ `${testLabelName}.outer.nested`
130
+ )
131
+ );
132
+ const form = screen.getByTestId('form');
133
+ // const submitButton = getByText('submit').closest('button');
134
+
135
+ fireEvent.submit(form);
136
+ await waitFor(() =>
137
+ expect(screen.getByText('input is required')).toBeInTheDocument()
138
+ );
139
+ });
140
+ });
141
+ });
142
+
143
+ describe('Callback Handling', () => {
144
+ describe('onChange', () => {
145
+ test("Custom onChange event fires callback function, overwriting Formik's onChange", async () => {
146
+ let value = null;
147
+ const mockedHandleChange = jest.fn((event) => {
148
+ value = event.target.value;
149
+ });
150
+
151
+ const { getByLabelText } = render(
152
+ renderForm(value, { onChange: mockedHandleChange })
153
+ );
154
+ const selectInput = getByLabelText(testLabelName);
155
+
156
+ fireEvent.change(selectInput);
157
+ expect(mockedHandleChange).toHaveBeenCalledTimes(1);
158
+ });
159
+ });
160
+ });
161
+ });
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import {
3
+ FormikTouched,
4
+ FormikErrors,
5
+ FieldAttributes,
6
+ FormikValues,
7
+ getIn,
8
+ } from 'formik';
9
+ import {
10
+ SelectInputNative,
11
+ SelectInputNativeProps,
12
+ } from '../../SelectInputNative/SelectInputNative';
13
+
14
+ export interface FormikSelectInputNativeProps
15
+ extends Omit<SelectInputNativeProps, 'onChange'> {
16
+ field: FieldAttributes<HTMLSelectElement>;
17
+ form: {
18
+ touched: FormikTouched<FormikValues>;
19
+ errors: FormikErrors<FormikValues>;
20
+ };
21
+ onChange?: SelectInputNativeProps['onChange'];
22
+ }
23
+
24
+ export const FormikSelectInputNative: React.FC<
25
+ FormikSelectInputNativeProps
26
+ > = ({
27
+ field: { name, onBlur, onChange: formikOnChange, value },
28
+ form: { touched, errors },
29
+ onChange,
30
+ options,
31
+ id,
32
+ label,
33
+ ...props
34
+ }) => (
35
+ <SelectInputNative
36
+ {...props}
37
+ options={options}
38
+ id={id}
39
+ label={label}
40
+ onChange={onChange ?? formikOnChange}
41
+ name={name}
42
+ onBlur={onBlur}
43
+ value={value}
44
+ error={getIn(touched, name) && getIn(errors, name)}
45
+ />
46
+ );
@@ -0,0 +1,176 @@
1
+ import React from 'react';
2
+ import { render, fireEvent, screen, waitFor } from '@testing-library/react';
3
+ import { Formik, Field, Form, setIn, getIn, FormikValues } from 'formik';
4
+ import { FormikTextInput } from './FormikTextInput';
5
+
6
+ const testLabelName = 'textInput';
7
+
8
+ const handleValidation = (testValueKey: string) => (values: FormikValues) =>
9
+ getIn(values, testValueKey)
10
+ ? {}
11
+ : setIn({}, testValueKey, 'input is required');
12
+
13
+ const renderForm = (
14
+ initialValue: any,
15
+ props: any,
16
+ testValueKey = testLabelName
17
+ ) => (
18
+ <Formik
19
+ initialValues={{
20
+ [testLabelName]: initialValue,
21
+ }}
22
+ validate={props.isRequired ? handleValidation(testValueKey) : undefined} // eslint-disable-line
23
+ onSubmit={() => {}}
24
+ >
25
+ {() => (
26
+ <Form noValidate>
27
+ <Field
28
+ label={testValueKey}
29
+ name={testValueKey}
30
+ id={testValueKey}
31
+ component={FormikTextInput}
32
+ {...props}
33
+ />
34
+ <button type="submit">submit</button>
35
+ </Form>
36
+ )}
37
+ </Formik>
38
+ );
39
+
40
+ describe('FormikTextInput', () => {
41
+ describe('States', () => {
42
+ describe('Autofocused', () => {
43
+ test('Input autofocuses if "autoFocus" prop is set to true', () => {
44
+ const { getByDisplayValue } = render(
45
+ renderForm('hello', { autoFocus: true })
46
+ );
47
+ const inputElement = getByDisplayValue('hello');
48
+ expect(document.activeElement).toEqual(inputElement);
49
+ });
50
+
51
+ test('Input correctly assigns autocomplete value of "on" when bool true is provided', () => {
52
+ const { getByDisplayValue } = render(
53
+ renderForm('hello', { autoComplete: true })
54
+ );
55
+ const inputElement = getByDisplayValue('hello');
56
+ expect(inputElement).toHaveAttribute('autocomplete', 'on');
57
+ });
58
+ });
59
+
60
+ describe('With Autocomplete', () => {
61
+ test('Input correctly assigns autocomplete value of "off" when bool false is provided', () => {
62
+ const { getByDisplayValue } = render(
63
+ renderForm('hello', { autoComplete: false })
64
+ );
65
+ const inputElement = getByDisplayValue('hello');
66
+ expect(inputElement).toHaveAttribute('autocomplete', 'off');
67
+ });
68
+
69
+ test('Input correctly assigns autocomplete value of "off" when incorrect type is provided', () => {
70
+ const { getByDisplayValue } = render(
71
+ renderForm('hello', { autoComplete: ['a', 'random', 'array'] })
72
+ );
73
+ const inputElement = getByDisplayValue('hello');
74
+ expect(inputElement).toHaveAttribute('autocomplete', 'off');
75
+ });
76
+ });
77
+
78
+ describe('Required', () => {
79
+ test('Input correctly assigns the "aria-required" attribute when "isRequired" prop is true', () => {
80
+ const { getByDisplayValue } = render(
81
+ renderForm('hello', { isRequired: true })
82
+ );
83
+ const inputElement = getByDisplayValue('hello');
84
+ expect(inputElement).toHaveAttribute('aria-required', 'true');
85
+ });
86
+ });
87
+
88
+ describe('With Error', () => {
89
+ test('Input correctly displays error message if provided', async () => {
90
+ const { getByText } = render(renderForm('', { isRequired: true }));
91
+ const submitButton = getByText('submit');
92
+
93
+ fireEvent.click(submitButton);
94
+ await waitFor(() =>
95
+ expect(screen.getByText('input is required')).toBeInTheDocument()
96
+ );
97
+ });
98
+
99
+ test('Input correctly displays error message in nested object', async () => {
100
+ const { getByText } = render(
101
+ renderForm(
102
+ { outer: { nested: '' } },
103
+ { isRequired: true },
104
+ `${testLabelName}.outer.nested`
105
+ )
106
+ );
107
+ const submitButton = getByText('submit');
108
+
109
+ fireEvent.click(submitButton);
110
+ await waitFor(() =>
111
+ expect(screen.getByText('input is required')).toBeInTheDocument()
112
+ );
113
+ });
114
+ });
115
+
116
+ describe('With Max Length', () => {
117
+ test('Input correctly passes maxlength property if prop is passed', async () => {
118
+ const { getByLabelText } = render(renderForm('', { maxLength: '3' }));
119
+ const inputElement = getByLabelText(testLabelName);
120
+ expect(inputElement).toHaveAttribute('maxlength');
121
+ expect(inputElement.getAttribute('maxlength')).toBe('3');
122
+ });
123
+ });
124
+
125
+ describe('Aria-labelledby', () => {
126
+ test('assigns the "aria-labelledby" attribute and renders label with correct id, when label is provided', () => {
127
+ const { getByLabelText } = render(renderForm('', {}));
128
+ const inputElement = getByLabelText(testLabelName);
129
+ expect(inputElement).toHaveAttribute(
130
+ 'aria-labelledby',
131
+ `${testLabelName}Label`
132
+ );
133
+ expect(
134
+ document.getElementById(`${testLabelName}Label`)
135
+ ).toBeInTheDocument();
136
+ });
137
+
138
+ test('does not assign "aria-labelledby" attribute when a label is hidden', () => {
139
+ render(renderForm('', { hideLabel: true }));
140
+ const inputElement = screen.getByLabelText(testLabelName);
141
+ expect(inputElement).not.toHaveAttribute('aria-labelledby');
142
+ });
143
+ });
144
+ });
145
+
146
+ describe('Callback Handling', () => {
147
+ describe('onChange', () => {
148
+ test("Custom onChange event fires callback function, overwriting Formik's onChange", () => {
149
+ let value = '';
150
+ const mockedHandleChange = jest.fn((event) => {
151
+ value = event.target.value;
152
+ });
153
+
154
+ const { getByLabelText } = render(
155
+ renderForm(value, { onChange: mockedHandleChange })
156
+ );
157
+ const input = getByLabelText(testLabelName);
158
+
159
+ fireEvent.change(input, { target: { value: 'hello' } });
160
+
161
+ expect(mockedHandleChange).toHaveBeenCalledTimes(1);
162
+ expect(value).toBe('hello');
163
+ });
164
+ });
165
+ });
166
+
167
+ describe('Children props', () => {
168
+ describe('Form Label', () => {
169
+ test('Input correctly passes props to dependency label component', async () => {
170
+ const { getByText } = render(renderForm('', { isRequired: true }));
171
+ const labelElement = getByText(`${testLabelName}`);
172
+ expect(labelElement).toHaveAttribute('for', testLabelName);
173
+ });
174
+ });
175
+ });
176
+ });
@@ -0,0 +1,38 @@
1
+ import React from 'react';
2
+ import {
3
+ FormikTouched,
4
+ FormikErrors,
5
+ FieldAttributes,
6
+ FormikValues,
7
+ getIn,
8
+ } from 'formik';
9
+ import { TextInput, TextInputProps } from '../../TextInput/TextInput';
10
+
11
+ export interface FormikTextInputProps extends Omit<TextInputProps, 'onChange'> {
12
+ field: FieldAttributes<HTMLInputElement>;
13
+ form: {
14
+ touched: FormikTouched<FormikValues>;
15
+ errors: FormikErrors<FormikValues>;
16
+ };
17
+ onChange?: TextInputProps['onChange'];
18
+ }
19
+
20
+ export const FormikTextInput: React.FC<FormikTextInputProps> = ({
21
+ field: { name, onBlur, onChange: formikOnChange, value },
22
+ form: { touched, errors },
23
+ onChange,
24
+ id,
25
+ label,
26
+ ...props
27
+ }) => (
28
+ <TextInput
29
+ {...props}
30
+ id={id}
31
+ label={label}
32
+ name={name}
33
+ onBlur={onBlur}
34
+ onChange={onChange ?? formikOnChange}
35
+ value={value}
36
+ error={getIn(touched, name) && getIn(errors, name)}
37
+ />
38
+ );
@@ -0,0 +1,170 @@
1
+ import React from 'react';
2
+ import { render, fireEvent, screen, waitFor } from '@testing-library/react';
3
+ import { Formik, Field, Form, setIn, getIn, FormikValues } from 'formik';
4
+ import { FormikTextInputInset } from './FormikTextInputInset';
5
+
6
+ const testLabelName = 'textInput';
7
+
8
+ const handleValidation = (testValueKey: string) => (values: FormikValues) =>
9
+ getIn(values, testValueKey)
10
+ ? {}
11
+ : setIn({}, testValueKey, 'input is required');
12
+
13
+ const renderForm = (
14
+ initialValue: any,
15
+ props: any,
16
+ testValueKey = testLabelName
17
+ ) => (
18
+ <Formik
19
+ initialValues={{
20
+ [testLabelName]: initialValue,
21
+ }}
22
+ validate={props.isRequired ? handleValidation(testValueKey) : undefined} // eslint-disable-line
23
+ onSubmit={() => {}}
24
+ >
25
+ {() => (
26
+ <Form noValidate>
27
+ <Field
28
+ label={testValueKey}
29
+ name={testValueKey}
30
+ id={testValueKey}
31
+ component={FormikTextInputInset}
32
+ {...props}
33
+ />
34
+ <button type="submit">submit</button>
35
+ </Form>
36
+ )}
37
+ </Formik>
38
+ );
39
+
40
+ describe('FormikTextInputInset', () => {
41
+ describe('States', () => {
42
+ describe('Autofocused', () => {
43
+ test('Input autofocuses if "autoFocus" prop is set to true', () => {
44
+ const { getByDisplayValue } = render(
45
+ renderForm('hello', { autoFocus: true })
46
+ );
47
+ const inputElement = getByDisplayValue('hello');
48
+ expect(document.activeElement).toEqual(inputElement);
49
+ });
50
+
51
+ test('Input correctly assigns autocomplete value of "on" when bool true is provided', () => {
52
+ const { getByDisplayValue } = render(
53
+ renderForm('hello', { autoComplete: true })
54
+ );
55
+ const inputElement = getByDisplayValue('hello');
56
+ expect(inputElement).toHaveAttribute('autocomplete', 'on');
57
+ });
58
+ });
59
+
60
+ describe('With Autocomplete', () => {
61
+ test('Input correctly assigns autocomplete value of "off" when bool false is provided', () => {
62
+ const { getByDisplayValue } = render(
63
+ renderForm('hello', { autoComplete: false })
64
+ );
65
+ const inputElement = getByDisplayValue('hello');
66
+ expect(inputElement).toHaveAttribute('autocomplete', 'off');
67
+ });
68
+
69
+ test('Input correctly assigns autocomplete value of "off" when incorrect type is provided', () => {
70
+ const { getByDisplayValue } = render(
71
+ renderForm('hello', { autoComplete: ['a', 'random', 'array'] })
72
+ );
73
+ const inputElement = getByDisplayValue('hello');
74
+ expect(inputElement).toHaveAttribute('autocomplete', 'off');
75
+ });
76
+ });
77
+
78
+ describe('Required', () => {
79
+ test('Input correctly assigns the "aria-required" attribute when "isRequired" prop is true', () => {
80
+ const { getByDisplayValue } = render(
81
+ renderForm('hello', { isRequired: true })
82
+ );
83
+ const inputElement = getByDisplayValue('hello');
84
+ expect(inputElement).toHaveAttribute('aria-required', 'true');
85
+ });
86
+ });
87
+
88
+ describe('With Error', () => {
89
+ test('Input correctly displays error message if provided', async () => {
90
+ const { getByText } = render(renderForm('', { isRequired: true }));
91
+ const submitButton = getByText('submit');
92
+
93
+ fireEvent.click(submitButton);
94
+ await waitFor(() =>
95
+ expect(screen.getByText('input is required')).toBeInTheDocument()
96
+ );
97
+ });
98
+
99
+ test('Input correctly displays error message in nested object', async () => {
100
+ const { getByText } = render(
101
+ renderForm(
102
+ { outer: { nested: '' } },
103
+ { isRequired: true },
104
+ `${testLabelName}.outer.nested`
105
+ )
106
+ );
107
+ const submitButton = getByText('submit');
108
+
109
+ fireEvent.click(submitButton);
110
+ await waitFor(() =>
111
+ expect(screen.getByText('input is required')).toBeInTheDocument()
112
+ );
113
+ });
114
+ });
115
+
116
+ describe('With Max Length', () => {
117
+ test('Input correctly passes maxlength property if prop is passed', async () => {
118
+ const { getByLabelText } = render(renderForm('', { maxLength: '3' }));
119
+ const inputElement = getByLabelText(testLabelName);
120
+ expect(inputElement).toHaveAttribute('maxlength');
121
+ expect(inputElement.getAttribute('maxlength')).toBe('3');
122
+ });
123
+ });
124
+
125
+ describe('Aria-labelledby', () => {
126
+ test('assigns the "aria-labelledby" attribute and renders label with correct id, when label is provided', () => {
127
+ const { getByLabelText } = render(renderForm('', {}));
128
+ const inputElement = getByLabelText(testLabelName);
129
+ expect(inputElement).toHaveAttribute(
130
+ 'aria-labelledby',
131
+ `${testLabelName}Label`
132
+ );
133
+ expect(
134
+ document.getElementById(`${testLabelName}Label`)
135
+ ).toBeInTheDocument();
136
+ });
137
+ });
138
+ });
139
+
140
+ describe('Callback Handling', () => {
141
+ describe('onChange', () => {
142
+ test("Custom onChange event fires callback function, overwriting Formik's onChange", () => {
143
+ let value = '';
144
+ const mockedHandleChange = jest.fn((event) => {
145
+ value = event.target.value;
146
+ });
147
+
148
+ const { getByLabelText } = render(
149
+ renderForm(value, { onChange: mockedHandleChange })
150
+ );
151
+ const input = getByLabelText(testLabelName);
152
+
153
+ fireEvent.change(input, { target: { value: 'hello' } });
154
+
155
+ expect(mockedHandleChange).toHaveBeenCalledTimes(1);
156
+ expect(value).toBe('hello');
157
+ });
158
+ });
159
+ });
160
+
161
+ describe('Children props', () => {
162
+ describe('Form Label', () => {
163
+ test('Input correctly passes props to dependency label component', async () => {
164
+ const { getByText } = render(renderForm('', { isRequired: true }));
165
+ const labelElement = getByText(`${testLabelName}`);
166
+ expect(labelElement).toHaveAttribute('for', testLabelName);
167
+ });
168
+ });
169
+ });
170
+ });
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import {
3
+ FormikTouched,
4
+ FormikErrors,
5
+ FieldAttributes,
6
+ FormikValues,
7
+ getIn,
8
+ } from 'formik';
9
+ import {
10
+ TextInputInset,
11
+ TextInputInsetProps,
12
+ } from '../../TextInputInset/TextInputInset';
13
+
14
+ export interface FormikTextInputInsetProps
15
+ extends Omit<TextInputInsetProps, 'onChange'> {
16
+ field: FieldAttributes<HTMLInputElement>;
17
+ form: {
18
+ touched: FormikTouched<FormikValues>;
19
+ errors: FormikErrors<FormikValues>;
20
+ };
21
+ onChange?: TextInputInsetProps['onChange'];
22
+ }
23
+
24
+ export const FormikTextInputInset: React.FC<FormikTextInputInsetProps> = ({
25
+ field: { name, onBlur, onChange: formikOnChange, value },
26
+ form: { touched, errors },
27
+ onChange,
28
+ id,
29
+ label,
30
+ ...props
31
+ }) => (
32
+ <TextInputInset
33
+ {...props}
34
+ id={id}
35
+ label={label}
36
+ name={name}
37
+ onBlur={onBlur}
38
+ onChange={onChange ?? formikOnChange}
39
+ value={value}
40
+ error={getIn(touched, name) && getIn(errors, name)}
41
+ />
42
+ );