@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,186 @@
1
+ import React from 'react';
2
+ import { render, fireEvent, screen, waitFor } from '@testing-library/react';
3
+ import { Formik, Field, Form, FormikValues, getIn, setIn } from 'formik';
4
+ import { FormikTextareaInput } from './FormikTextareaInput';
5
+
6
+ const testLabelName = 'textInput';
7
+
8
+ const handleValidation = (testValueKey: string) => (values: FormikValues) =>
9
+ getIn(values, testValueKey)?.length > 1
10
+ ? {}
11
+ : setIn({}, testValueKey, 'input is required');
12
+
13
+ type FormProps = {
14
+ isRequired?: boolean;
15
+ maxLength?: string;
16
+ autoComplete?: boolean | string | string[];
17
+ hideLabel?: boolean;
18
+ onChange?: jest.Mock<void, [any]>; // eslint-disable-line
19
+ autoFocus?: boolean;
20
+ };
21
+
22
+ // eslint-disable-next-line
23
+ const renderForm = (
24
+ initialValue: any,
25
+ props: FormProps,
26
+ testValueKey = testLabelName
27
+ ) => (
28
+ <Formik
29
+ initialValues={{
30
+ [testLabelName]: initialValue,
31
+ }}
32
+ validate={props.isRequired ? handleValidation(testValueKey) : undefined} // eslint-disable-line
33
+ onSubmit={() => {}} // eslint-disable-line
34
+ >
35
+ {() => (
36
+ <Form noValidate>
37
+ <Field
38
+ label={testValueKey}
39
+ name={testValueKey}
40
+ id={testValueKey}
41
+ component={FormikTextareaInput}
42
+ {...props}
43
+ />
44
+ <button type="submit">submit</button>
45
+ </Form>
46
+ )}
47
+ </Formik>
48
+ );
49
+
50
+ describe('FormikTextareaInput', () => {
51
+ describe('States', () => {
52
+ describe('Autofocused', () => {
53
+ test('Input autofocuses if "autoFocus" prop is set to true', () => {
54
+ const { getByDisplayValue } = render(
55
+ renderForm('hello', { autoFocus: true })
56
+ );
57
+ const inputElement = getByDisplayValue('hello');
58
+ expect(document.activeElement).toEqual(inputElement);
59
+ });
60
+
61
+ test('Input correctly assigns autocomplete value of "on" when bool true is provided', () => {
62
+ const { getByDisplayValue } = render(
63
+ renderForm('hello', { autoComplete: true })
64
+ );
65
+ const inputElement = getByDisplayValue('hello');
66
+ expect(inputElement).toHaveAttribute('autocomplete', 'on');
67
+ });
68
+ });
69
+
70
+ describe('With Autocomplete', () => {
71
+ test('Input correctly assigns autocomplete value of "off" when bool false is provided', () => {
72
+ const { getByDisplayValue } = render(
73
+ renderForm('hello', { autoComplete: false })
74
+ );
75
+ const inputElement = getByDisplayValue('hello');
76
+ expect(inputElement).toHaveAttribute('autocomplete', 'off');
77
+ });
78
+
79
+ test('Input correctly assigns autocomplete value of "off" when incorrect type is provided', () => {
80
+ const { getByDisplayValue } = render(
81
+ renderForm('hello', { autoComplete: ['a', 'random', 'array'] })
82
+ );
83
+ const inputElement = getByDisplayValue('hello');
84
+ expect(inputElement).toHaveAttribute('autocomplete', 'off');
85
+ });
86
+ });
87
+
88
+ describe('Required', () => {
89
+ test('Input correctly assigns the "aria-required" attribute when "isRequired" prop is true', () => {
90
+ const { getByDisplayValue } = render(
91
+ renderForm('hello', { isRequired: true })
92
+ );
93
+ const inputElement = getByDisplayValue('hello');
94
+ expect(inputElement).toHaveAttribute('aria-required', 'true');
95
+ });
96
+ });
97
+
98
+ describe('With Error', () => {
99
+ test('Input correctly displays error message if provided', async () => {
100
+ const { getByText } = render(renderForm('', { isRequired: true }));
101
+ const submitButton = getByText('submit');
102
+
103
+ fireEvent.click(submitButton);
104
+ await waitFor(() =>
105
+ expect(screen.getByText('input is required')).toBeInTheDocument()
106
+ );
107
+ });
108
+
109
+ test('Input correctly displays error message from nested object', async () => {
110
+ const { getByText } = render(
111
+ renderForm(
112
+ { outer: { nested: '' } },
113
+ { isRequired: true },
114
+ `${testLabelName}.outer.nested`
115
+ )
116
+ );
117
+ const submitButton = getByText('submit');
118
+
119
+ fireEvent.click(submitButton);
120
+ await waitFor(() =>
121
+ expect(screen.getByText('input is required')).toBeInTheDocument()
122
+ );
123
+ });
124
+ });
125
+
126
+ describe('With Max Length', () => {
127
+ test('Input correctly passes maxlength property if prop is passed', async () => {
128
+ const { getByLabelText } = render(renderForm('', { maxLength: '3' }));
129
+ const inputElement = getByLabelText(testLabelName);
130
+ expect(inputElement).toHaveAttribute('maxlength');
131
+ expect(inputElement.getAttribute('maxlength')).toBe('3');
132
+ });
133
+ });
134
+
135
+ describe('Aria-labelledby', () => {
136
+ test('assigns the "aria-labelledby" attribute and renders label with correct id, when label is provided', () => {
137
+ const { getByLabelText } = render(renderForm('', {}));
138
+ const inputElement = getByLabelText(testLabelName);
139
+ expect(inputElement).toHaveAttribute(
140
+ 'aria-labelledby',
141
+ `${testLabelName}Label`
142
+ );
143
+ expect(
144
+ document.getElementById(`${testLabelName}Label`)
145
+ ).toBeInTheDocument();
146
+ });
147
+
148
+ test('does not assign "aria-labelledby" attribute when a label is hidden', () => {
149
+ render(renderForm('', { hideLabel: true }));
150
+ const inputElement = screen.getByLabelText(testLabelName);
151
+ expect(inputElement).not.toHaveAttribute('aria-labelledby');
152
+ });
153
+ });
154
+ });
155
+
156
+ describe('Callback Handling', () => {
157
+ describe('onChange', () => {
158
+ test("Custom onChange event fires callback function, overwriting Formik's onChange", () => {
159
+ let value = '';
160
+ const mockedHandleChange = jest.fn((event) => {
161
+ value = event.target.value;
162
+ });
163
+
164
+ const { getByLabelText } = render(
165
+ renderForm(value, { onChange: mockedHandleChange })
166
+ );
167
+ const input = getByLabelText(testLabelName);
168
+
169
+ fireEvent.change(input, { target: { value: 'hello' } });
170
+
171
+ expect(mockedHandleChange).toHaveBeenCalledTimes(1);
172
+ expect(value).toBe('hello');
173
+ });
174
+ });
175
+ });
176
+
177
+ describe('Children props', () => {
178
+ describe('Form Label', () => {
179
+ test('Input correctly passes props to dependency label component', async () => {
180
+ const { getByText } = render(renderForm('', { isRequired: true }));
181
+ const labelElement = getByText(`${testLabelName}`);
182
+ expect(labelElement).toHaveAttribute('for', testLabelName);
183
+ });
184
+ });
185
+ });
186
+ });
@@ -0,0 +1,42 @@
1
+ import React, { FC } from 'react';
2
+ import {
3
+ FormikTouched,
4
+ FormikErrors,
5
+ FormikValues,
6
+ FieldAttributes,
7
+ getIn,
8
+ } from 'formik';
9
+ import {
10
+ TextareaInput,
11
+ TextareaInputProps,
12
+ } from '../../TextareaInput/TextareaInput';
13
+
14
+ export interface FormikTextareaInputProps
15
+ extends Omit<TextareaInputProps, 'onChange'> {
16
+ field: FieldAttributes<HTMLTextAreaElement>;
17
+ form: {
18
+ touched: FormikTouched<FormikValues>;
19
+ errors: FormikErrors<FormikValues>;
20
+ };
21
+ onChange?: TextareaInputProps['onChange'];
22
+ }
23
+
24
+ export const FormikTextareaInput: FC<FormikTextareaInputProps> = ({
25
+ field: { name, onBlur, onChange: formikOnChange, value },
26
+ form: { touched, errors },
27
+ onChange,
28
+ id,
29
+ label,
30
+ ...props
31
+ }) => (
32
+ <TextareaInput
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
+ );
@@ -0,0 +1,179 @@
1
+ import React from 'react';
2
+ import { render, fireEvent, screen, waitFor } from '@testing-library/react';
3
+ import { Formik, Field, Form, FormikValues, getIn, setIn } from 'formik';
4
+ import { FormikTextareaInputInset } from './FormikTextareaInputInset';
5
+
6
+ const testLabelName = 'textInput';
7
+
8
+ const handleValidation = (testValueKey: string) => (values: FormikValues) =>
9
+ getIn(values, testValueKey)?.length > 1
10
+ ? {}
11
+ : setIn({}, testValueKey, 'input is required');
12
+
13
+ type FormProps = {
14
+ isRequired?: boolean;
15
+ maxLength?: string;
16
+ autoComplete?: boolean | string | string[];
17
+ onChange?: jest.Mock<void, [any]>; // eslint-disable-line
18
+ autoFocus?: boolean;
19
+ };
20
+
21
+ // eslint-disable-next-line
22
+ const renderForm = (
23
+ initialValue: any,
24
+ props: FormProps,
25
+ testValueKey = testLabelName
26
+ ) => (
27
+ <Formik
28
+ initialValues={{
29
+ [testLabelName]: initialValue,
30
+ }}
31
+ validate={props.isRequired ? handleValidation(testValueKey) : undefined} // eslint-disable-line
32
+ onSubmit={() => {}} // eslint-disable-line
33
+ >
34
+ {() => (
35
+ <Form noValidate>
36
+ <Field
37
+ label={testValueKey}
38
+ name={testValueKey}
39
+ id={testValueKey}
40
+ component={FormikTextareaInputInset}
41
+ {...props}
42
+ />
43
+ <button type="submit">submit</button>
44
+ </Form>
45
+ )}
46
+ </Formik>
47
+ );
48
+
49
+ describe('FormikTextareaInputInset', () => {
50
+ describe('States', () => {
51
+ describe('Autofocused', () => {
52
+ test('Input autofocuses if "autoFocus" prop is set to true', () => {
53
+ const { getByDisplayValue } = render(
54
+ renderForm('hello', { autoFocus: true })
55
+ );
56
+ const inputElement = getByDisplayValue('hello');
57
+ expect(document.activeElement).toEqual(inputElement);
58
+ });
59
+
60
+ test('Input correctly assigns autocomplete value of "on" when bool true is provided', () => {
61
+ const { getByDisplayValue } = render(
62
+ renderForm('hello', { autoComplete: true })
63
+ );
64
+ const inputElement = getByDisplayValue('hello');
65
+ expect(inputElement).toHaveAttribute('autocomplete', 'on');
66
+ });
67
+ });
68
+
69
+ describe('With Autocomplete', () => {
70
+ test('Input correctly assigns autocomplete value of "off" when bool false is provided', () => {
71
+ const { getByDisplayValue } = render(
72
+ renderForm('hello', { autoComplete: false })
73
+ );
74
+ const inputElement = getByDisplayValue('hello');
75
+ expect(inputElement).toHaveAttribute('autocomplete', 'off');
76
+ });
77
+
78
+ test('Input correctly assigns autocomplete value of "off" when incorrect type is provided', () => {
79
+ const { getByDisplayValue } = render(
80
+ renderForm('hello', { autoComplete: ['a', 'random', 'array'] })
81
+ );
82
+ const inputElement = getByDisplayValue('hello');
83
+ expect(inputElement).toHaveAttribute('autocomplete', 'off');
84
+ });
85
+ });
86
+
87
+ describe('Required', () => {
88
+ test('Input correctly assigns the "aria-required" attribute when "isRequired" prop is true', () => {
89
+ const { getByDisplayValue } = render(
90
+ renderForm('hello', { isRequired: true })
91
+ );
92
+ const inputElement = getByDisplayValue('hello');
93
+ expect(inputElement).toHaveAttribute('aria-required', 'true');
94
+ });
95
+ });
96
+
97
+ describe('With Error', () => {
98
+ test('Input correctly displays error message if provided', async () => {
99
+ const { getByText } = render(renderForm('', { isRequired: true }));
100
+ const submitButton = getByText('submit');
101
+
102
+ fireEvent.click(submitButton);
103
+ await waitFor(() =>
104
+ expect(screen.getByText('input is required')).toBeInTheDocument()
105
+ );
106
+ });
107
+
108
+ test('Input correctly displays error message from nested object', async () => {
109
+ const { getByText } = render(
110
+ renderForm(
111
+ { outer: { nested: '' } },
112
+ { isRequired: true },
113
+ `${testLabelName}.outer.nested`
114
+ )
115
+ );
116
+ const submitButton = getByText('submit');
117
+
118
+ fireEvent.click(submitButton);
119
+ await waitFor(() =>
120
+ expect(screen.getByText('input is required')).toBeInTheDocument()
121
+ );
122
+ });
123
+ });
124
+
125
+ describe('With Max Length', () => {
126
+ test('Input correctly passes maxlength property if prop is passed', async () => {
127
+ const { getByLabelText } = render(renderForm('', { maxLength: '3' }));
128
+ const inputElement = getByLabelText(testLabelName);
129
+ expect(inputElement).toHaveAttribute('maxlength');
130
+ expect(inputElement.getAttribute('maxlength')).toBe('3');
131
+ });
132
+ });
133
+
134
+ describe('Aria-labelledby', () => {
135
+ test('assigns the "aria-labelledby" attribute and renders label with correct id, when label is provided', () => {
136
+ const { getByLabelText } = render(renderForm('', {}));
137
+ const inputElement = getByLabelText(testLabelName);
138
+ expect(inputElement).toHaveAttribute(
139
+ 'aria-labelledby',
140
+ `${testLabelName}Label`
141
+ );
142
+ expect(
143
+ document.getElementById(`${testLabelName}Label`)
144
+ ).toBeInTheDocument();
145
+ });
146
+ });
147
+ });
148
+
149
+ describe('Callback Handling', () => {
150
+ describe('onChange', () => {
151
+ test("Custom onChange event fires callback function, overwriting Formik's onChange", () => {
152
+ let value = '';
153
+ const mockedHandleChange = jest.fn((event) => {
154
+ value = event.target.value;
155
+ });
156
+
157
+ const { getByLabelText } = render(
158
+ renderForm(value, { onChange: mockedHandleChange })
159
+ );
160
+ const input = getByLabelText(testLabelName);
161
+
162
+ fireEvent.change(input, { target: { value: 'hello' } });
163
+
164
+ expect(mockedHandleChange).toHaveBeenCalledTimes(1);
165
+ expect(value).toBe('hello');
166
+ });
167
+ });
168
+ });
169
+
170
+ describe('Children props', () => {
171
+ describe('Form Label', () => {
172
+ test('Input correctly passes props to dependency label component', async () => {
173
+ const { getByText } = render(renderForm('', { isRequired: true }));
174
+ const labelElement = getByText(`${testLabelName}`);
175
+ expect(labelElement).toHaveAttribute('for', testLabelName);
176
+ });
177
+ });
178
+ });
179
+ });
@@ -0,0 +1,42 @@
1
+ import React, { FC } from 'react';
2
+ import {
3
+ FormikTouched,
4
+ FormikErrors,
5
+ FormikValues,
6
+ FieldAttributes,
7
+ getIn,
8
+ } from 'formik';
9
+ import {
10
+ TextareaInputInset,
11
+ TextareaInputInsetProps,
12
+ } from '../../TextareaInputInset/TextareaInputInset';
13
+
14
+ export interface FormikTextareaInputInsetProps
15
+ extends Omit<TextareaInputInsetProps, 'onChange'> {
16
+ field: FieldAttributes<HTMLTextAreaElement>;
17
+ form: {
18
+ touched: FormikTouched<FormikValues>;
19
+ errors: FormikErrors<FormikValues>;
20
+ };
21
+ onChange?: TextareaInputInsetProps['onChange'];
22
+ }
23
+
24
+ export const FormikTextareaInputInset: FC<FormikTextareaInputInsetProps> = ({
25
+ field: { name, onBlur, onChange: formikOnChange, value },
26
+ form: { touched, errors },
27
+ onChange,
28
+ id,
29
+ label,
30
+ ...props
31
+ }) => (
32
+ <TextareaInputInset
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
+ );
@@ -0,0 +1,224 @@
1
+ import React from 'react';
2
+ import { render, fireEvent, screen, waitFor } from '@testing-library/react';
3
+ import selectEvent from 'react-select-event';
4
+ import { Formik, Form, Field, FormikValues, getIn, setIn } from 'formik';
5
+ import { FormikTimePicker } from './FormikTimePicker';
6
+
7
+ const testLabelName = 'test select';
8
+
9
+ type Option = { value: string; label: string };
10
+ const handleValidation = (testValueKey: string) => (values: FormikValues) =>
11
+ getIn(values, testValueKey)?.length > 1
12
+ ? {}
13
+ : setIn({}, testValueKey, 'input is required');
14
+
15
+ const renderForm = (
16
+ initialValue: any, // eslint-disable-line
17
+ props: {
18
+ placeholder?: string;
19
+ hideLabel?: boolean;
20
+ isMulti?: boolean;
21
+ isRequired?: unknown;
22
+ isDisabled?: boolean;
23
+ onChange?: jest.Mock<void, [any]>; // eslint-disable-line
24
+ interval?: number;
25
+ },
26
+ testValueKey = testLabelName
27
+ ) => (
28
+ <Formik
29
+ initialValues={{
30
+ [testLabelName]: initialValue,
31
+ }}
32
+ validate={props.isRequired ? handleValidation(testValueKey) : undefined} // eslint-disable-line
33
+ onSubmit={() => {}} // eslint-disable-line
34
+ >
35
+ {() => (
36
+ <Form>
37
+ <Field
38
+ label={testValueKey}
39
+ name={testValueKey}
40
+ id={testValueKey}
41
+ component={FormikTimePicker}
42
+ {...props}
43
+ />
44
+ <button type="submit">submit</button>
45
+ </Form>
46
+ )}
47
+ </Formik>
48
+ );
49
+
50
+ describe('FormikTimePicker', () => {
51
+ describe('States', () => {
52
+ describe('Hidden label, with a placeholder', () => {
53
+ test('it renders input without a visual label, and with a placeholder', () => {
54
+ render(
55
+ renderForm(undefined, {
56
+ placeholder: 'Test Placeholder',
57
+ hideLabel: true,
58
+ })
59
+ );
60
+ expect(screen.queryByText(testLabelName)).toBeNull();
61
+ expect(screen.getByText('Test Placeholder')).toBeInTheDocument();
62
+ });
63
+ });
64
+
65
+ describe('No Aria-labelledby', () => {
66
+ test('does not assign "aria-labelledby" attribute when a label is hidden', () => {
67
+ render(renderForm(undefined, { hideLabel: true }));
68
+ const inputElement = screen.getByLabelText(testLabelName);
69
+ expect(inputElement).not.toHaveAttribute('aria-labelledby');
70
+ });
71
+ });
72
+
73
+ describe('With a label, and no custom placeholder', () => {
74
+ test('it renders input with a label, and with a default placeholder', () => {
75
+ render(renderForm(undefined, {}));
76
+
77
+ expect(screen.getByLabelText(testLabelName)).toBeInTheDocument();
78
+ expect(screen.getByText('HH:MM')).toBeInTheDocument();
79
+ });
80
+
81
+ test('assigns the "aria-labelledby" attribute and renders label with correct id, when label is provided', () => {
82
+ render(renderForm(undefined, {}));
83
+ const inputElement = screen.getByLabelText(testLabelName);
84
+ expect(inputElement).toHaveAttribute(
85
+ 'aria-labelledby',
86
+ `${testLabelName}Label`
87
+ );
88
+ expect(
89
+ document.getElementById(`${testLabelName}Label`)
90
+ ).toBeInTheDocument();
91
+ });
92
+ });
93
+
94
+ describe('Single select, pre-selected', () => {
95
+ test('it renders with value pre-selected', () => {
96
+ render(
97
+ renderForm(
98
+ { label: '12:00 AM', value: '2020-10-23T04:30:00.120Z' },
99
+ {}
100
+ )
101
+ );
102
+
103
+ expect(screen.getByText('12:00 AM')).toBeInTheDocument();
104
+ });
105
+ });
106
+
107
+ describe('Multi select, no selection', () => {
108
+ test('it renders input with a label, and with a default placeholder', () => {
109
+ render(renderForm(undefined, { isMulti: true }));
110
+
111
+ expect(screen.getByLabelText(testLabelName)).toBeInTheDocument();
112
+ expect(screen.getByText('HH:MM')).toBeInTheDocument();
113
+ });
114
+ });
115
+
116
+ describe('Multi select, with multiple items selected', () => {
117
+ test('it renders input with a label, and with two items selected', () => {
118
+ render(
119
+ renderForm(
120
+ [
121
+ { label: '12:00 AM', value: '2020-10-23T04:30:00.120Z' },
122
+ { label: '12:15 AM', value: '2020-10-23T04:45:00.120Z' },
123
+ ],
124
+ { isMulti: true }
125
+ )
126
+ );
127
+
128
+ expect(screen.getByLabelText(testLabelName)).toBeInTheDocument();
129
+ expect(screen.queryByText('HH:MM')).toBeNull();
130
+ expect(screen.getByText('12:00 AM')).toBeInTheDocument();
131
+ expect(screen.getByText('12:15 AM')).toBeInTheDocument();
132
+ });
133
+ });
134
+
135
+ describe('Is Disabled', () => {
136
+ test('it disables the input', () => {
137
+ const { container } = render(
138
+ renderForm(undefined, { isDisabled: true })
139
+ );
140
+
141
+ const disabledInput = container.querySelector(
142
+ '.react-select__control[aria-disabled="true"]'
143
+ );
144
+
145
+ expect(disabledInput).toBeInTheDocument();
146
+ });
147
+ });
148
+
149
+ describe('Is Invalid, with a helpful message', () => {
150
+ test('it renders the helpful message', async () => {
151
+ const { getByText } = render(renderForm([], { isRequired: true }));
152
+ const submitButton = getByText('submit');
153
+
154
+ fireEvent.click(submitButton);
155
+ await waitFor(() =>
156
+ expect(screen.getByText('input is required')).toBeInTheDocument()
157
+ );
158
+ });
159
+
160
+ test('it renders the error message from nested object', async () => {
161
+ const { getByText } = render(
162
+ renderForm(
163
+ { outer: { nested: [] } },
164
+ { isRequired: true },
165
+ `${testLabelName}.outer.nested`
166
+ )
167
+ );
168
+ const submitButton = getByText('submit');
169
+
170
+ fireEvent.click(submitButton);
171
+ await waitFor(() =>
172
+ expect(screen.getByText('input is required')).toBeInTheDocument()
173
+ );
174
+ });
175
+ });
176
+ });
177
+
178
+ describe('Callback Handling', () => {
179
+ describe('onChange', () => {
180
+ test("Custom onChange event fires callback function, overwriting Formik's onChange", async () => {
181
+ let value: Option | undefined;
182
+ const mockedHandleChange = jest.fn((event) => {
183
+ value = event.target.value;
184
+ });
185
+
186
+ const { getByLabelText, container, getByText } = render(
187
+ renderForm(value, { onChange: mockedHandleChange })
188
+ );
189
+ const selectInput = getByLabelText(testLabelName);
190
+ /**
191
+ * This class is specific to react-select, combined with our custom classNamePrefix prop.
192
+ * While this is an implementation detail there appears to be
193
+ * no clearer path to test our own component which depends on react-select
194
+ */
195
+ const selectInputWrapper = container.querySelector(
196
+ '.react-select__control'
197
+ );
198
+
199
+ fireEvent.focus(selectInput);
200
+ if (selectInputWrapper) {
201
+ fireEvent.mouseDown(selectInputWrapper);
202
+ }
203
+ const option = await waitFor(() => getByText('12:00 AM'), {
204
+ container,
205
+ });
206
+ fireEvent.click(option);
207
+ expect(mockedHandleChange).toHaveBeenCalledTimes(1);
208
+ expect(value?.label).toEqual('12:00 AM');
209
+ });
210
+
211
+ test('it fires onChange callback on change', async () => {
212
+ const mockedHandleChange = jest.fn();
213
+
214
+ const { getByLabelText } = render(
215
+ renderForm(undefined, { onChange: mockedHandleChange })
216
+ );
217
+
218
+ await selectEvent.select(getByLabelText(testLabelName), '12:00 AM');
219
+
220
+ expect(mockedHandleChange).toBeCalledTimes(1);
221
+ });
222
+ });
223
+ });
224
+ });