@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,90 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import { Drawer, DrawerProps } from './Drawer';
4
+
5
+ const renderDrawer = ({
6
+ isOpen,
7
+ title,
8
+ ariaLabel,
9
+ onDismiss,
10
+ closeButton,
11
+ }: DrawerProps) => (
12
+ <Drawer
13
+ isOpen={isOpen}
14
+ title={title}
15
+ ariaLabel={ariaLabel}
16
+ onDismiss={onDismiss}
17
+ closeButton={closeButton}
18
+ />
19
+ );
20
+
21
+ describe('Drawer', () => {
22
+ test('renders its children', () => {
23
+ const { getByText } = render(
24
+ renderDrawer({
25
+ ariaLabel: 'Right Drawer',
26
+ isOpen: true,
27
+ title: 'Right Drawer',
28
+ onDismiss: () => null,
29
+ })
30
+ );
31
+ expect(getByText('Right Drawer')).toBeInTheDocument();
32
+ });
33
+
34
+ test('it applies the aria label', () => {
35
+ const { getByLabelText } = render(
36
+ renderDrawer({
37
+ ariaLabel: 'Right Drawer',
38
+ isOpen: true,
39
+ title: 'Right Drawer',
40
+ onDismiss: () => null,
41
+ })
42
+ );
43
+ expect(getByLabelText('Right Drawer')).toBeInTheDocument();
44
+ });
45
+
46
+ test('it renders a close button and title', () => {
47
+ const { getByText, getByLabelText } = render(
48
+ renderDrawer({
49
+ ariaLabel: 'Right Drawer',
50
+ isOpen: true,
51
+ title: 'Right Drawer',
52
+ onDismiss: () => null,
53
+ })
54
+ );
55
+ expect(getByLabelText('close')).toBeInTheDocument();
56
+ expect(getByText('Right Drawer')).toBeInTheDocument();
57
+ });
58
+
59
+ test('it renders a close button without title', () => {
60
+ const { getByLabelText } = render(
61
+ renderDrawer({
62
+ ariaLabel: 'Right Drawer',
63
+ isOpen: true,
64
+ onDismiss: () => null,
65
+ closeButton: true,
66
+ })
67
+ );
68
+ expect(getByLabelText('close')).toBeInTheDocument();
69
+ });
70
+
71
+ test('it open and closes based on isOpen prop', () => {
72
+ const { queryByLabelText, getByLabelText, rerender } = render(
73
+ renderDrawer({
74
+ ariaLabel: 'Right Drawer',
75
+ isOpen: false,
76
+ })
77
+ );
78
+
79
+ expect(queryByLabelText('Right Drawer')).toBe(null);
80
+
81
+ rerender(
82
+ renderDrawer({
83
+ ariaLabel: 'Right Drawer',
84
+ isOpen: true,
85
+ })
86
+ );
87
+
88
+ expect(getByLabelText('Right Drawer')).toBeInTheDocument();
89
+ });
90
+ });
@@ -0,0 +1,249 @@
1
+ import React, {
2
+ CSSProperties,
3
+ RefObject,
4
+ forwardRef,
5
+ useCallback,
6
+ } from 'react';
7
+ import ReactModal from 'react-modal';
8
+ import FocusLock from 'react-focus-lock';
9
+ import { RemoveScroll } from 'react-remove-scroll';
10
+ import classNames from 'classnames';
11
+ import { DimensionSize, CssDimensionValue } from '../../types';
12
+ import { Box } from '../Box/Box';
13
+ import styles from './Drawer.module.scss';
14
+ import { Button } from '../Button/Button';
15
+
16
+ export type DrawerPlacementType = 'left' | 'right' | 'top' | 'bottom';
17
+ export interface DrawerProps {
18
+ /**
19
+ * If the drawer is open
20
+ */
21
+ isOpen: boolean;
22
+ /**
23
+ * Handle zoom/pinch gestures on iOS devices when scroll locking is enabled.
24
+ */
25
+ allowPinchZoom?: boolean;
26
+ /**
27
+ * A drawer needs to be properly labeled to provide context for users with
28
+ * assistive technology such as screen readers. If a drawer is announced to
29
+ * the user without a label, it can be confusing and difficult to navigate.
30
+ */
31
+ ariaLabel?: string;
32
+ /**
33
+ * The id of the element that should be used as the drawer's label by assistive technologies like screen readers.
34
+ */
35
+ ariaLabelledBy?: string;
36
+ /**
37
+ * Contents of the dialog.
38
+ */
39
+ children?: React.ReactNode;
40
+ /**
41
+ * Additional class names to add to the drawer content.
42
+ */
43
+ className?: string;
44
+ /**
45
+ * Whether the drawer has a visible close button.
46
+ * If a title is defined, then a close button will be rendered
47
+ */
48
+ closeButton?: boolean;
49
+ /**
50
+ * If true, the drawer will close when the overlay is clicked
51
+ */
52
+ closeOnOverlayClick?: boolean;
53
+ /**
54
+ * The ref of the container where the drawer will be inserted into the DOM.
55
+ * By default, drawers are inserted in the document.body, but if need be they can
56
+ * be scoped as necessary.
57
+ */
58
+ containerRef?: React.RefObject<Node>;
59
+ /**
60
+ * By default, focus is trapped within the drawer.
61
+ * If true, focus will not be trapped within the contents of the drawer.
62
+ */
63
+ dangerouslyBypassFocusLock?: boolean;
64
+ /**
65
+ * By default, the drawer locks scrolling on the body, but in some cases you may want to allow for scrolling.
66
+ * If true, this will allow the body to scroll while the drawer is open.
67
+ */
68
+ dangerouslyBypassScrollLock?: boolean;
69
+ /**
70
+ * If true, the overlay will not be rendered, scrolling for the entire page will remain enabled,
71
+ * and focus will not be locked to the contents of the drawer
72
+ */
73
+ hideOverlay?: boolean;
74
+ /**
75
+ * By default the first focusable element will receive focus when the dialog
76
+ * opens but you can provide a ref to focus instead.
77
+ *
78
+ * @see Docs https://reach.tech/dialog#dialog-initialfocusref
79
+ */
80
+ initialFocusRef?: RefObject<HTMLDivElement>;
81
+ /**
82
+ * Which edge of the viewport should the drawer appear from
83
+ */
84
+ placement?: DrawerPlacementType;
85
+ /**
86
+ * Function that is called whenever the user either hits
87
+ * the "Escape" key, clicks the close button icon, or clicks the overlay.
88
+ */
89
+ onDismiss?: (event?: React.SyntheticEvent) => void;
90
+ /**
91
+ * Title to be displayed at the top of the Drawer.
92
+ * A close button will be rendered automatically if this prop is defined.
93
+ */
94
+ title?: string;
95
+ /**
96
+ * The width of the Drawer when opened. Can be given a standard css value (px, rem, em, %),
97
+ * or a [width token](/?path=/story/design-tokens-design-tokens--page#width)
98
+ */
99
+ width?: DimensionSize | CssDimensionValue;
100
+ }
101
+ export const Drawer: React.FC<DrawerProps> = forwardRef<
102
+ HTMLDivElement,
103
+ DrawerProps
104
+ >(
105
+ (
106
+ {
107
+ ariaLabel = undefined,
108
+ ariaLabelledBy = undefined,
109
+ allowPinchZoom = false,
110
+ children = undefined,
111
+ className = undefined,
112
+ closeButton = false,
113
+ closeOnOverlayClick = true,
114
+ containerRef = undefined,
115
+ dangerouslyBypassFocusLock = false,
116
+ dangerouslyBypassScrollLock = false,
117
+ hideOverlay = false,
118
+ initialFocusRef = undefined,
119
+ isOpen,
120
+ onDismiss = undefined,
121
+ placement = 'right',
122
+ title = undefined,
123
+ width = undefined,
124
+ },
125
+ ref
126
+ ) => {
127
+ const activateFocusLock = useCallback(() => {
128
+ setTimeout(() => {
129
+ if (initialFocusRef && initialFocusRef.current) {
130
+ initialFocusRef.current.focus();
131
+ }
132
+ }, 100);
133
+ }, [initialFocusRef]);
134
+
135
+ const dynamicWidth = width;
136
+
137
+ const dynamicStyle: CSSProperties = {
138
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
139
+ ['--w' as any]: dynamicWidth,
140
+ };
141
+
142
+ const overlayClassnames = classNames(styles.overlay, styles.drawer, {
143
+ [styles['hide-overlay']]: hideOverlay,
144
+ [styles[`hide-overlay-${placement}`]]: hideOverlay,
145
+ 'position-fixed': containerRef === undefined,
146
+ 'position-absolute': containerRef !== undefined,
147
+ });
148
+
149
+ const contentClassnames = classNames(
150
+ styles['drawer-content'],
151
+ styles[placement],
152
+ {
153
+ [styles['hide-overlay']]: hideOverlay,
154
+ 'overflow-auto': !closeButton && !title,
155
+ className,
156
+ }
157
+ );
158
+
159
+ const renderHeader = () => {
160
+ if (closeButton && onDismiss && !title) {
161
+ return (
162
+ <Box alignItems="flex-end" justifyContent="center" padding="md lg">
163
+ <Button
164
+ variant="tertiary"
165
+ onClick={onDismiss}
166
+ aria-label="close"
167
+ type="button"
168
+ iconPrefix="remove"
169
+ />
170
+ </Box>
171
+ );
172
+ }
173
+ if (title) {
174
+ return (
175
+ <Box
176
+ direction="row"
177
+ justifyContent="space-between"
178
+ alignItems="center"
179
+ padding="2xl"
180
+ >
181
+ <Box className={styles.title} fontWeight="bold">
182
+ {title}
183
+ </Box>
184
+ {onDismiss && (
185
+ <Button
186
+ variant="tertiary"
187
+ onClick={onDismiss}
188
+ aria-label="close"
189
+ type="button"
190
+ iconPrefix="remove"
191
+ />
192
+ )}
193
+ </Box>
194
+ );
195
+ }
196
+ return null;
197
+ };
198
+
199
+ const content =
200
+ title || closeButton ? (
201
+ <Box flex="auto" overflow="auto">
202
+ {children}
203
+ </Box>
204
+ ) : (
205
+ children
206
+ );
207
+
208
+ const parentElement = containerRef?.current
209
+ ? (containerRef.current as HTMLElement)
210
+ : document.body;
211
+
212
+ const isDisabledFocusLock = hideOverlay || dangerouslyBypassFocusLock;
213
+ const isDisabledRemoveScroll = hideOverlay || dangerouslyBypassScrollLock;
214
+
215
+ return (
216
+ <FocusLock
217
+ autoFocus
218
+ returnFocus
219
+ disabled={isDisabledFocusLock || !isOpen}
220
+ onActivation={activateFocusLock}
221
+ >
222
+ <RemoveScroll
223
+ allowPinchZoom={allowPinchZoom}
224
+ enabled={!isDisabledRemoveScroll && isOpen}
225
+ >
226
+ <Box ref={ref}>
227
+ <ReactModal
228
+ isOpen={isOpen}
229
+ overlayClassName={overlayClassnames}
230
+ className={contentClassnames}
231
+ onRequestClose={closeOnOverlayClick ? onDismiss : undefined}
232
+ ariaHideApp={false}
233
+ style={{
234
+ content: dynamicStyle,
235
+ overlay: dynamicStyle,
236
+ }}
237
+ parentSelector={() => parentElement}
238
+ >
239
+ <Box aria-label={ariaLabel} aria-labelledby={ariaLabelledBy}>
240
+ {renderHeader()}
241
+ {content}
242
+ </Box>
243
+ </ReactModal>
244
+ </Box>
245
+ </RemoveScroll>
246
+ </FocusLock>
247
+ );
248
+ }
249
+ );
@@ -0,0 +1,78 @@
1
+ import React, { ReactNode } from 'react';
2
+ import { Box, BoxProps } from '../Box/Box';
3
+ import { FormLabel } from '../FormLabel/FormLabel';
4
+ import { InputValidationMessage } from '../InputValidationMessage/InputValidationMessage';
5
+
6
+ export interface FormControlProps extends BoxProps {
7
+ /**
8
+ * The input's id attribute. Used to programmatically tie the input with its label.
9
+ */
10
+ id: string;
11
+ /**
12
+ * Custom content to be displayed above the input. If the label is hidden, will be used to set aria-label attribute.
13
+ */
14
+ label: string;
15
+ /**
16
+ * Mark the input field as invalid and display a validation message.
17
+ * Pass a string or node to render a validation message below the input.
18
+ */
19
+ error?: ReactNode;
20
+ /**
21
+ * Visually hide the label.
22
+ */
23
+ hideLabel?: boolean;
24
+ /**
25
+ * Additional clarifying text to help describe the input
26
+ */
27
+ helpText?: ReactNode;
28
+ /**
29
+ * The input's disabled attribute
30
+ */
31
+ isDisabled?: boolean;
32
+ /**
33
+ * The required and aria-required attributes on the input
34
+ */
35
+ isRequired?: boolean;
36
+ /**
37
+ * Visual indicator that the field is required, that gets appended to the label
38
+ */
39
+ requiredIndicator?: ReactNode;
40
+ }
41
+
42
+ export const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
43
+ (
44
+ {
45
+ label,
46
+ hideLabel,
47
+ children,
48
+ error,
49
+ id,
50
+ isRequired,
51
+ helpText,
52
+ isDisabled,
53
+ requiredIndicator,
54
+ width = '100',
55
+ ...restProps
56
+ },
57
+ ref
58
+ ) => {
59
+ const labelProps = {
60
+ inputId: id,
61
+ helpText,
62
+ margin: '0 0 xs 0',
63
+ isDisabled,
64
+ isFieldRequired: isRequired,
65
+ requiredIndicator,
66
+ };
67
+
68
+ return (
69
+ <Box width={width} ref={ref} {...restProps}>
70
+ {label && !hideLabel && <FormLabel {...labelProps}>{label}</FormLabel>}
71
+ {children}
72
+ {error && error !== true && (
73
+ <InputValidationMessage>{error}</InputValidationMessage>
74
+ )}
75
+ </Box>
76
+ );
77
+ }
78
+ );
@@ -0,0 +1,24 @@
1
+ import { Canvas, Meta, ArgTypes } from '@storybook/blocks';
2
+ import { FormLabel } from './FormLabel';
3
+ import * as Stories from './FormLabel.stories';
4
+
5
+ <Meta of={Stories} />
6
+
7
+ # FormLabel
8
+
9
+ FormLabel is a sub component of many of our inputs, and is meant to be paired with a form input. In most cases, the label will have already been built into another component as a prop. However, you can use it on its own or with another component.
10
+
11
+ <Canvas of={Stories.BasicUsage} />
12
+
13
+ ## Accessibility
14
+
15
+ - Each label requires the `inputId` prop that equals the id of the associated input so that the `htmlFor` property can be set.
16
+ - Don't use placeholder text as a replacement for labels. It can be used to provide an example to users, but will disappear from the field when a user enters text. It's also not broadly supported by assistive technologies and won't display in older browsers.
17
+ - Use one of 3 ways to label a form field in order of most preferred:
18
+ 1. Visible label with Label
19
+ 2. Visible label that's associated to the input with aria-labelledby
20
+ 3. Label directly using aria-label
21
+
22
+ ## Props
23
+
24
+ <ArgTypes of={FormLabel} />
@@ -0,0 +1,19 @@
1
+ .label {
2
+ font-family: var(--INTERNAL_form-control-font-family);
3
+ color: var(--color-font-base);
4
+ font-size: var(--INTERNAL_form-control-size-md-label-size);
5
+ font-weight: var(--INTERNAL_form-control-label-font-weight);
6
+
7
+ &.radio-input-label {
8
+ font-weight: 400;
9
+ }
10
+
11
+ &.disabled {
12
+ color: var(--color-base-grey-300);
13
+ }
14
+
15
+ .help-text {
16
+ margin-top: var(--INTERNAL_form-control-help-margin);
17
+ font-weight: var(--INTERNAL_form-control-help-font-weight);
18
+ }
19
+ }
@@ -0,0 +1,20 @@
1
+ import { FormLabel } from './FormLabel';
2
+
3
+ import type { Meta } from '@storybook/react';
4
+ import React from 'react';
5
+
6
+ const meta: Meta<typeof FormLabel> = {
7
+ title: 'Components/FormLabel',
8
+ component: FormLabel,
9
+ parameters: {
10
+ controls: { hideNoControlsWarning: true },
11
+ },
12
+ };
13
+
14
+ export default meta;
15
+
16
+ export const BasicUsage = () => (
17
+ <FormLabel inputId="inputId" helpText="More helpful text about the input">
18
+ Default Label
19
+ </FormLabel>
20
+ );
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import { FormLabel } from './FormLabel';
4
+
5
+ beforeEach(() => {
6
+ console.error = jest.fn(); // eslint-disable-line no-console
7
+ });
8
+
9
+ afterEach(() => {
10
+ (console.error as jest.Mock).mockRestore(); // eslint-disable-line no-console
11
+ });
12
+
13
+ describe('FormLabel', () => {
14
+ test('Label correctly renders with base props', () => {
15
+ render(<FormLabel inputId="myId">my label</FormLabel>);
16
+ const labelElement = screen.getByText('my label');
17
+ expect(labelElement).toHaveAttribute('for', 'myId');
18
+ expect(labelElement).toHaveTextContent('my label');
19
+ });
20
+
21
+ test('correctly assigns an id when given an inputId', () => {
22
+ render(<FormLabel inputId="myId">my label</FormLabel>);
23
+ const labelElement = screen.getByText('my label');
24
+ expect(labelElement).toHaveAttribute('id', 'myIdLabel');
25
+ });
26
+
27
+ test('renders help text if provided', () => {
28
+ const { getByText } = render(
29
+ <FormLabel inputId="myId" helpText="i am help text">
30
+ my label
31
+ </FormLabel>
32
+ );
33
+ expect(getByText('i am help text')).toBeDefined();
34
+ });
35
+ });
@@ -0,0 +1,96 @@
1
+ import React, { FC, ReactNode } from 'react';
2
+ import classNames from 'classnames';
3
+ import { Box, BoxProps } from '../Box/Box';
4
+ import styles from './FormLabel.module.scss';
5
+
6
+ export interface FormLabelProps extends BoxProps {
7
+ /**
8
+ * Content to be rendered inside the label.
9
+ */
10
+ children: ReactNode;
11
+ /**
12
+ * The id of the form control that the label is labeling
13
+ */
14
+ inputId: string;
15
+ /**
16
+ * Custom class to pass to label element.
17
+ */
18
+ className?: string;
19
+ /**
20
+ * Additional clarifying text to that helps describe the field
21
+ */
22
+ helpText?: ReactNode;
23
+ /**
24
+ * Mark the label has disabled
25
+ */
26
+ isDisabled?: boolean;
27
+ /**
28
+ * prop deprecated: no longer in use and will be remove in next major release.
29
+ */
30
+ isFieldRequired?: boolean;
31
+ /**
32
+ * Apply custom styling to labels for a radio input
33
+ */
34
+ isRadioInputLabel?: boolean;
35
+ /**
36
+ * Visual indicator that the field is required, that gets appended to the label
37
+ */
38
+ requiredIndicator?: ReactNode;
39
+ /**
40
+ * Additional props to be spread to rendered element
41
+ */
42
+ [x: string]: any; // eslint-disable-line
43
+ }
44
+
45
+ export const FormLabel: FC<FormLabelProps> = ({
46
+ children,
47
+ inputId,
48
+ className = '',
49
+ display = 'block',
50
+ helpText,
51
+ isDisabled = false,
52
+ isFieldRequired = false,
53
+ isRadioInputLabel = false,
54
+ requiredIndicator = ' *',
55
+ margin = '0',
56
+ padding = '0',
57
+ ...restProps
58
+ }) => {
59
+ const labelClasses = classNames(
60
+ 'hyphen-components__variables__form-control',
61
+ styles.label,
62
+ className,
63
+ {
64
+ [styles.disabled]: isDisabled,
65
+ [styles.disabled]: isDisabled,
66
+ [styles['radio-input-label']]: isRadioInputLabel,
67
+ }
68
+ );
69
+
70
+ return (
71
+ <Box
72
+ as="label"
73
+ id={`${inputId}Label`}
74
+ className={labelClasses}
75
+ display={display}
76
+ margin={margin}
77
+ padding={padding}
78
+ htmlFor={inputId}
79
+ {...restProps}
80
+ >
81
+ {children}
82
+ {isFieldRequired && requiredIndicator && <span>{requiredIndicator}</span>}
83
+ {helpText && (
84
+ <Box
85
+ as="p"
86
+ display="block"
87
+ fontSize="sm"
88
+ color="secondary"
89
+ className={styles['help-text']}
90
+ >
91
+ {helpText}
92
+ </Box>
93
+ )}
94
+ </Box>
95
+ );
96
+ };
@@ -0,0 +1,10 @@
1
+ import { Meta, Canvas } from '@storybook/addon-docs';
2
+ import * as Stories from './Formik.stories';
3
+
4
+ <Meta of={Stories} />
5
+
6
+ # Formik Form
7
+
8
+ Formik Form demonstrates a pattern for integrating the input elements found in Hyphen Components with [Formik](https://formik.org/). The form is fully functional and even mock submits. Manipulate the inputs in the form and watch the state object below it update with the new values in real-time. Attempt to submit the form without filling out required inputs, and observe the error states for these inputs.
9
+
10
+ <Canvas of={Stories.FormikForm} />