@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,213 @@
1
+ import React, {
2
+ ChangeEvent,
3
+ forwardRef,
4
+ FocusEvent,
5
+ ForwardRefExoticComponent,
6
+ ReactNode,
7
+ HTMLProps,
8
+ InputHTMLAttributes,
9
+ } from 'react';
10
+ import classNames from 'classnames';
11
+ import { ResponsiveProp } from '../../types';
12
+ import { generateResponsiveClasses } from '../../lib/generateResponsiveClasses';
13
+
14
+ import { Box, BoxProps } from '../Box/Box';
15
+ import { HelpText } from '../HelpText/HelpText';
16
+ import { getAutoCompleteValue } from '../../lib/getAutoCompleteValue';
17
+ import styles from './TextareaInputInset.module.scss';
18
+ import { InputValidationMessage } from '../InputValidationMessage/InputValidationMessage';
19
+
20
+ export type TextareaInputInsetSize = 'md' | 'lg';
21
+ export interface TextareaInputInsetProps {
22
+ /**
23
+ * The input's id attribute. Used to programmatically tie the input with its label.
24
+ */
25
+ id: string;
26
+ /**
27
+ * Custom content to be displayed above the input. If the label is hidden, will be used to set aria-label attribute.
28
+ */
29
+ label: string;
30
+ /**
31
+ * Callback function to call on change event.
32
+ */
33
+ onChange: (event: ChangeEvent<HTMLInputElement>) => void;
34
+ /**
35
+ * The text value of the input. Required since our Input is a controlled component.
36
+ */
37
+ value: InputHTMLAttributes<HTMLInputElement>['value'];
38
+ /**
39
+ * Automatically focus the input when the page is loaded.
40
+ */
41
+ autoFocus?: boolean;
42
+ /**
43
+ * Custom class to be added to standard input classes.
44
+ */
45
+ className?: string;
46
+ /**
47
+ * Mark the input field as invalid and display a validation message.
48
+ * Pass a string or node to render a validation message below the input.
49
+ */
50
+ error?: ReactNode;
51
+ /**
52
+ * Additional clarifying text to help describe the input
53
+ */
54
+ helpText?: ReactNode;
55
+ /**
56
+ * Props passed directly to the input element of the component
57
+ */
58
+ inputProps?: BoxProps & HTMLProps<HTMLInputElement>;
59
+ /**
60
+ * The input's disabled attribute
61
+ */
62
+ isDisabled?: boolean;
63
+ /**
64
+ * The required and aria-required attributes on the input
65
+ */
66
+ isRequired?: boolean;
67
+ /**
68
+ * The input's 'maxlength' attribute.
69
+ * NOTE: initializing the input with a value longer than the desired maxlength will not trim this value.
70
+ */
71
+ maxLength?: number;
72
+ /**
73
+ * The input's 'name' attribute.
74
+ */
75
+ name?: string;
76
+ /**
77
+ * Callback function to call on blur event.
78
+ */
79
+ onBlur?: (event: FocusEvent<HTMLInputElement>) => void;
80
+ /**
81
+ * Callback function to call on focus event.
82
+ */
83
+ onFocus?: (event: FocusEvent<HTMLInputElement>) => void;
84
+ /**
85
+ * The input placeholder attribute.
86
+ */
87
+ placeholder?: string;
88
+ /**
89
+ * An input helper rendered before the input field value
90
+ */
91
+ prefix?: ReactNode;
92
+ /**
93
+ * Visual indicator that the field is required, that gets appended to the label
94
+ */
95
+ requiredIndicator?: ReactNode;
96
+ /**
97
+ * Textarea resize behavior
98
+ */
99
+ resize?: 'vertical' | 'horizontal' | 'none' | 'both';
100
+ /**
101
+ * number of visible text lines for the control.
102
+ */
103
+ rows?: number;
104
+ /**
105
+ * The size of the text input.
106
+ */
107
+ size?: TextareaInputInsetSize | ResponsiveProp<TextareaInputInsetSize>;
108
+ /**
109
+ * An input helper rendered after the input field value
110
+ */
111
+ suffix?: ReactNode;
112
+ /**
113
+ * The input 'type' value. Defaults to type 'text'.
114
+ */
115
+ type?: InputHTMLAttributes<HTMLInputElement>['type'];
116
+ /**
117
+ * Additional props to be spread to rendered element
118
+ */
119
+ [x: string]: any; // eslint-disable-line
120
+ }
121
+
122
+ export const TextareaInputInset: ForwardRefExoticComponent<TextareaInputInsetProps> =
123
+ forwardRef<HTMLDivElement, TextareaInputInsetProps>(
124
+ (
125
+ {
126
+ id,
127
+ label,
128
+ onChange,
129
+ value,
130
+ autoComplete = false,
131
+ autoFocus = false,
132
+ className,
133
+ error = false,
134
+ helpText,
135
+ inputProps = {},
136
+ isDisabled = false,
137
+ isRequired = false,
138
+ maxLength = undefined,
139
+ name = '',
140
+ onBlur = undefined,
141
+ onFocus = undefined,
142
+ placeholder = ' ',
143
+ requiredIndicator = ' *',
144
+ resize = 'vertical',
145
+ rows = 5,
146
+ size = 'md',
147
+ type = 'text',
148
+ },
149
+ ref
150
+ ) => {
151
+ const responsiveClasses = generateResponsiveClasses('size', size);
152
+
153
+ const inputWrapperClasses = classNames(
154
+ 'hyphen-components__variables__form-control',
155
+ styles['text-input-wrapper'],
156
+ ...responsiveClasses.map((c) => styles[c]),
157
+ {
158
+ [styles.disabled]: isDisabled,
159
+ }
160
+ );
161
+
162
+ const computedInputProps: TextareaInputInsetProps['inputProps'] = {
163
+ ...inputProps, // These are spread first so that we don't have top level props overwritten by the user.
164
+ 'aria-required': isRequired,
165
+ 'aria-invalid': !!error,
166
+ 'aria-label': label,
167
+ 'aria-labelledby': label ? `${id}Label` : undefined,
168
+ autoComplete: getAutoCompleteValue(autoComplete),
169
+ autoFocus,
170
+ className: classNames(styles[`textarea-resize-${resize}`], {
171
+ [styles.error]: error,
172
+ }),
173
+ disabled: isDisabled,
174
+ id,
175
+ maxLength,
176
+ name,
177
+ onBlur,
178
+ onChange,
179
+ onFocus,
180
+ placeholder,
181
+ required: isRequired,
182
+ rows,
183
+ type,
184
+ value,
185
+ };
186
+
187
+ return (
188
+ <Box width="100" ref={ref} className={className}>
189
+ <Box
190
+ display="block"
191
+ position="relative"
192
+ className={inputWrapperClasses}
193
+ >
194
+ <Box as="textarea" {...computedInputProps} />
195
+ <label
196
+ htmlFor={id}
197
+ className={styles['text-input-label']}
198
+ id={`${id}Label`}
199
+ >
200
+ {label}
201
+ {isRequired && requiredIndicator && (
202
+ <span>{requiredIndicator}</span>
203
+ )}
204
+ </label>
205
+ </Box>
206
+ {helpText && <HelpText>{helpText}</HelpText>}
207
+ {error && error !== true && (
208
+ <InputValidationMessage>{error}</InputValidationMessage>
209
+ )}
210
+ </Box>
211
+ );
212
+ }
213
+ );
@@ -0,0 +1,11 @@
1
+ import { Canvas, Meta, ArgTypes } from '@storybook/blocks';
2
+ import { ThemeProvider } from './ThemeProvider';
3
+ import * as Stories from './ThemeProvider.stories';
4
+
5
+ <Meta of={Stories} />
6
+
7
+ # ThemeProvider
8
+
9
+ Allow users to set the theme for the entire application by adding the `ThemeProvider` to the root of the application.
10
+
11
+ <Canvas of={Stories.BasicUsage} />
@@ -0,0 +1,56 @@
1
+ import React from 'react';
2
+ import { ThemeProvider } from './ThemeProvider';
3
+ import type { Meta } from '@storybook/react';
4
+ import { Box } from '../Box/Box';
5
+ import { RadioGroup } from '../RadioGroup/RadioGroup';
6
+ import { useTheme, Theme } from './ThemeProvider';
7
+
8
+ const meta: Meta<typeof ThemeProvider> = {
9
+ title: 'Providers/ThemeProvider',
10
+ component: ThemeProvider,
11
+ };
12
+
13
+ export default meta;
14
+
15
+ export const BasicUsage = () =>
16
+ (() => {
17
+ const App = () => {
18
+ const { theme, setTheme } = useTheme();
19
+
20
+ const options = [
21
+ {
22
+ id: 'light',
23
+ value: 'light',
24
+ label: 'Light',
25
+ },
26
+ {
27
+ id: 'dark',
28
+ value: 'dark',
29
+ label: 'Dark',
30
+ },
31
+ {
32
+ id: 'system',
33
+ value: 'system',
34
+ label: 'System',
35
+ },
36
+ ];
37
+
38
+ return (
39
+ <Box gap="md" background="secondary" padding="xl" color="base">
40
+ <p>Theme: {theme}</p>
41
+
42
+ <RadioGroup
43
+ name="SelectTheme"
44
+ value={theme}
45
+ onChange={(event) => setTheme(event.target.value as Theme)}
46
+ options={options}
47
+ />
48
+ </Box>
49
+ );
50
+ };
51
+ return (
52
+ <ThemeProvider defaultTheme="light" storageKey="hyphen-ui-theme">
53
+ <App />
54
+ </ThemeProvider>
55
+ );
56
+ })();
@@ -0,0 +1,75 @@
1
+ import React, { createContext, useState, useEffect, useContext } from 'react';
2
+
3
+ export type Theme = 'dark' | 'light' | 'system';
4
+
5
+ type ThemeProviderProps = {
6
+ children: React.ReactNode;
7
+ defaultTheme?: Theme;
8
+ storageKey?: string;
9
+ };
10
+
11
+ type ThemeProviderState = {
12
+ theme: Theme;
13
+ setTheme: (theme: Theme) => void;
14
+ };
15
+
16
+ const initialState: ThemeProviderState = {
17
+ theme: 'system',
18
+ setTheme: () => null,
19
+ };
20
+
21
+ const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
22
+
23
+ export function ThemeProvider({
24
+ children,
25
+ defaultTheme = 'system',
26
+ storageKey = 'hyphen-ui-theme',
27
+ ...props
28
+ }: ThemeProviderProps) {
29
+ const [theme, setTheme] = useState<Theme>(
30
+ () => (localStorage.getItem(storageKey) as Theme) || defaultTheme
31
+ );
32
+
33
+ useEffect(() => {
34
+ const root = window.document.documentElement;
35
+
36
+ root.classList.remove('light', 'dark');
37
+
38
+ if (theme === 'system') {
39
+ const systemTheme = window.matchMedia('(prefers-color-scheme: dark)')
40
+ .matches
41
+ ? 'dark'
42
+ : 'light';
43
+
44
+ root.classList.add(systemTheme);
45
+ return;
46
+ }
47
+
48
+ root.classList.add(theme);
49
+ }, [theme]);
50
+
51
+ const value = {
52
+ theme,
53
+ setTheme: (theme: Theme) => {
54
+ localStorage.setItem(storageKey, theme);
55
+ setTheme(theme);
56
+ },
57
+ };
58
+
59
+ return (
60
+ <ThemeProviderContext.Provider {...props} value={value}>
61
+ {children}
62
+ </ThemeProviderContext.Provider>
63
+ );
64
+ }
65
+
66
+ export const useTheme = () => {
67
+ const context = useContext(ThemeProviderContext);
68
+
69
+ if (context === undefined)
70
+ throw new Error(
71
+ 'useTheme must be used within a ThemeProvider. Be sure your App is wrapped in ThemeProvider.'
72
+ );
73
+
74
+ return context;
75
+ };
@@ -0,0 +1,75 @@
1
+ import { Canvas, Meta, ArgTypes } from '@storybook/blocks';
2
+ import { TimePicker } from './TimePicker';
3
+ import * as Stories from './TimePicker.stories';
4
+
5
+ <Meta of={Stories} />
6
+
7
+ # TimePicker
8
+
9
+ Use a TimePicker when you want users to select from a list of a available times agnostic of date.
10
+
11
+ _NOTE:_ This component is abstracted from the [SelectInput](?path=/docs/components-selectinput-overview--default-story)
12
+ and as such includes the same underlying props interface with an added method for generating the time options automatically.
13
+
14
+ _ABOUT TIME FORMAT:_ While the `value` prop technically returns `{ label: string; value: string; }` the value returned will always
15
+ be a JS ISO date string, like this: `2016-07-13T18:46:01.933Z`.
16
+
17
+ ## Props
18
+
19
+ <ArgTypes of={TimePicker} />
20
+
21
+ ## Default
22
+
23
+ <Canvas of={Stories.Default} />
24
+
25
+ ## With A Specific Interval
26
+
27
+ Pass an `interval` (seconds) value to determine how many times get generated as options.
28
+ The example below uses `3600` --> 1 Hour Intervals
29
+
30
+ <Canvas of={Stories.WithASpecificInterval} />
31
+
32
+ ## With Min and Max Times
33
+
34
+ You can determine the range of times to be shown by capping the start and end times.
35
+ Use `startTime` and `endTime` for this. The expected values for `hour` and `minute` properties
36
+ are `0-24` and `0-60` respectively.
37
+
38
+ IMPORTANT: `startTime` is inclusive, and `endTime` is exclusive. This is to avoid
39
+ the options showing the startTime twice when using a 24-hour cycle.
40
+
41
+ See example below where we want to show 15 minute increments starting at 9:00AM and ending at 3:30PM.
42
+
43
+ <Canvas of={Stories.WithMinAndMaxTimes} />
44
+
45
+ ## With Custom Date Display
46
+
47
+ The labels for the dropdwon options are governed by JS `toLocaleTimeString`, and as such
48
+ options are available to customize these.
49
+
50
+ NOTE: that the option values are always returned as ISO strings regardless of the label display option.
51
+
52
+ The example below is shown in military (24-hour) time.
53
+
54
+ <Canvas of={Stories.WithCustomDateDisplay} />
55
+
56
+ ## With Open Menu
57
+
58
+ The menu can be rendered open with the `menuIsOpen` prop. We do this here to confirm option generation
59
+ via our UI visual snapshot testing (Chromatic).
60
+
61
+ <Canvas of={Stories.WithOpenMenu} />
62
+
63
+ ## Help Text
64
+
65
+ <Canvas of={Stories.HelpText} />
66
+
67
+ ## Sizes
68
+
69
+ Set the size of the input to `sm`, `md` or `lg`. `md` is the default size.
70
+
71
+ <Canvas of={Stories.Sizes} />
72
+
73
+ ## Component Design Tokens
74
+
75
+ This component shares component design tokens with all form controls. For a complete list of tokens, see the [Theming Form Controls documentation](/docs/theming-form-controls--custom-theme-form).
@@ -0,0 +1,149 @@
1
+ import type { Meta } from '@storybook/react';
2
+ import React, { ChangeEvent, useState } from 'react';
3
+ import { TimePicker } from './TimePicker';
4
+ import { Box } from '../Box/Box';
5
+
6
+ const meta: Meta<typeof TimePicker> = {
7
+ title: 'Components/TimePicker',
8
+ component: TimePicker,
9
+ parameters: {
10
+ controls: { hideNoControlsWarning: true },
11
+ },
12
+ };
13
+
14
+ export default meta;
15
+
16
+ export const Default = () => {
17
+ const [value, setValue] = useState<string | null>(null);
18
+ return (
19
+ <Box height="360px">
20
+ <TimePicker
21
+ id="defaultTimePicker"
22
+ name="defaultTimePicker"
23
+ onChange={(event: ChangeEvent<HTMLInputElement>) => {
24
+ setValue(event.target.value);
25
+ }}
26
+ label="Pick a Time"
27
+ value={value}
28
+ />
29
+ </Box>
30
+ );
31
+ };
32
+
33
+ export const WithASpecificInterval = () => (
34
+ <Box height="360px">
35
+ <TimePicker
36
+ id="intervalTimePicker"
37
+ name="intervalTimePicker"
38
+ onChange={() => {}}
39
+ label="Pick a Time"
40
+ interval={3600}
41
+ />
42
+ </Box>
43
+ );
44
+
45
+ export const WithMinAndMaxTimes = () => (
46
+ <Box height="360px">
47
+ <TimePicker
48
+ id="startEnd"
49
+ name="startEnd"
50
+ onChange={() => {}}
51
+ label="Pick a Time"
52
+ startTime={{ hour: 9, minute: 0 }}
53
+ endTime={{ hour: 15, minute: 31 }}
54
+ />
55
+ </Box>
56
+ );
57
+
58
+ export const WithCustomDateDisplay = () => (
59
+ <Box height="360px">
60
+ <TimePicker
61
+ id="customDate"
62
+ name="customDate"
63
+ onChange={() => {}}
64
+ label="Pick a Time"
65
+ startTime={{ hour: 9, minute: 0 }}
66
+ endTime={{ hour: 15, minute: 31 }}
67
+ dateDisplayOptions={{ hour12: false, hour: '2-digit', minute: '2-digit' }}
68
+ />
69
+ </Box>
70
+ );
71
+
72
+ export const WithOpenMenu = () => {
73
+ const [value, setValue] = useState<string | null>(null);
74
+ return (
75
+ <Box height="260px">
76
+ <TimePicker
77
+ id="openMenu"
78
+ name="openMenu"
79
+ onChange={(event: ChangeEvent<HTMLInputElement>) => {
80
+ setValue(event.target.value);
81
+ }}
82
+ label="Pick a Time"
83
+ value={value}
84
+ menuIsOpen
85
+ interval={3600}
86
+ startTime={{ hour: 9, minute: 0 }}
87
+ endTime={{ hour: 13, minute: 1 }}
88
+ />
89
+ </Box>
90
+ );
91
+ };
92
+
93
+ export const HelpText = () => {
94
+ const [value, setValue] = useState<string>('');
95
+ return (
96
+ <Box height="360px">
97
+ <TimePicker
98
+ id="helpText"
99
+ name="helpText"
100
+ onChange={(event: ChangeEvent<HTMLInputElement>) => {
101
+ setValue(event.target.value);
102
+ }}
103
+ label="Pick a Time"
104
+ value={value}
105
+ helpText="pick a time, any time..."
106
+ />
107
+ </Box>
108
+ );
109
+ };
110
+
111
+ export const Sizes = () => {
112
+ const [value, setValue] = useState<string>();
113
+ const [value1, setValue1] = useState<string>();
114
+ const [value2, setValue2] = useState<string>();
115
+ return (
116
+ <Box gap="md" height="360px">
117
+ <TimePicker
118
+ id="smTimePicker"
119
+ name="smTimePicker"
120
+ onChange={(event: ChangeEvent<HTMLInputElement>) => {
121
+ setValue(event.target.value);
122
+ }}
123
+ label="Small"
124
+ value={value}
125
+ size="sm"
126
+ />
127
+ <TimePicker
128
+ id="mdTimePicker"
129
+ name="mdTimePicker"
130
+ onChange={(event: ChangeEvent<HTMLInputElement>) => {
131
+ setValue1(event.target.value);
132
+ }}
133
+ label="Medium"
134
+ value={value1}
135
+ size="md"
136
+ />
137
+ <TimePicker
138
+ id="lgTimePicker"
139
+ name="lgTimePicker"
140
+ onChange={(event: ChangeEvent<HTMLInputElement>) => {
141
+ setValue2(event.target.value);
142
+ }}
143
+ label="Large"
144
+ value={value2}
145
+ size="lg"
146
+ />
147
+ </Box>
148
+ );
149
+ };
@@ -0,0 +1,97 @@
1
+ import React from 'react';
2
+ import { screen, render } from '@testing-library/react';
3
+ import { TimePicker } from './TimePicker';
4
+
5
+ describe('TimePicker', () => {
6
+ describe('Default', () => {
7
+ it('Renders a TimePicker (select) with default props', () => {
8
+ render(
9
+ <TimePicker
10
+ name="timePicker"
11
+ id="timePicker"
12
+ onChange={() => null}
13
+ value={null}
14
+ label="Select Time"
15
+ menuIsOpen
16
+ maxMenuHeight={2000}
17
+ />
18
+ );
19
+
20
+ const timePicker = screen.getByLabelText('Select Time');
21
+
22
+ const expectedTimes = [
23
+ '12:00 AM',
24
+ '12:15 AM',
25
+ '12:30 AM',
26
+ '12:45 AM',
27
+ '01:00 AM',
28
+ '01:15 AM',
29
+ ];
30
+ expect(timePicker).toBeInTheDocument();
31
+ expectedTimes.forEach((time) => {
32
+ expect(screen.queryByText(time)).toBeInTheDocument();
33
+ });
34
+ });
35
+ });
36
+
37
+ describe('Min/Max & Interval', () => {
38
+ it('Renders correct options based on interval and start end times.', () => {
39
+ render(
40
+ <TimePicker
41
+ name="timePicker"
42
+ id="timePicker"
43
+ onChange={() => null}
44
+ value={null}
45
+ label="Select Time"
46
+ menuIsOpen
47
+ maxMenuHeight={2000}
48
+ interval={3600}
49
+ startTime={{ hour: 9, minute: 0 }}
50
+ endTime={{ hour: 12, minute: 0 }}
51
+ />
52
+ );
53
+
54
+ const timePicker = screen.getByLabelText('Select Time');
55
+
56
+ const expectedTimes = ['09:00 AM', '10:00 AM', '11:00 AM'];
57
+
58
+ const notExpected = ['12:00 PM', '12:45 AM', '01:00 AM', '01:15 AM'];
59
+ expect(timePicker).toBeInTheDocument();
60
+ expectedTimes.forEach((time) => {
61
+ expect(screen.queryByText(time)).toBeInTheDocument();
62
+ });
63
+
64
+ notExpected.forEach((time) => {
65
+ expect(screen.queryByText(time)).toBe(null);
66
+ });
67
+ });
68
+ });
69
+
70
+ describe('Custom Date Display', () => {
71
+ it('renders the times based on the options provided', () => {
72
+ render(
73
+ <TimePicker
74
+ name="timePicker"
75
+ id="timePicker"
76
+ onChange={() => null}
77
+ value={null}
78
+ label="Select Time"
79
+ menuIsOpen
80
+ maxMenuHeight={2000}
81
+ dateDisplayOptions={{ hour12: false }}
82
+ startTime={{ hour: 13, minute: 0 }}
83
+ endTime={{ hour: 15, minute: 1 }}
84
+ interval={3600}
85
+ />
86
+ );
87
+
88
+ const timePicker = screen.getByLabelText('Select Time');
89
+
90
+ const expectedTimes = ['13:00:00', '14:00:00', '15:00:00'];
91
+ expect(timePicker).toBeInTheDocument();
92
+ expectedTimes.forEach((time) => {
93
+ expect(screen.queryByText(time)).toBeInTheDocument();
94
+ });
95
+ });
96
+ });
97
+ });