@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,280 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { Toast, ExtendedToastOptions, ToastType } from './Toast.types';
3
+
4
+ const TOAST_LIMIT = 20;
5
+
6
+ // eslint-disable-next-line no-shadow
7
+ export enum ToastStoreActionType {
8
+ ADD_TOAST = 'TOAST/ADD_TOAST',
9
+ UPDATE_TOAST = 'TOAST/UPDATE_TOAST',
10
+ UPSERT_TOAST = 'TOAST/UPSERT_TOAST',
11
+ DISMISS_TOAST = 'TOAST/DISMISS_TOAST',
12
+ REMOVE_TOAST = 'TOAST/REMOVE_TOAST',
13
+ START_PAUSE = 'TOAST/START_PAUSE',
14
+ END_PAUSE = 'TOAST/END_PAUSE',
15
+ }
16
+
17
+ type GenericAction<
18
+ TActionType extends PropertyKey,
19
+ TPayload extends Record<string, unknown>
20
+ > = {
21
+ type: TActionType;
22
+ payload: TPayload;
23
+ };
24
+
25
+ type ToastStoreAction =
26
+ | GenericAction<ToastStoreActionType.ADD_TOAST, { toast: Toast }>
27
+ | GenericAction<ToastStoreActionType.UPSERT_TOAST, { toast: Toast }>
28
+ | GenericAction<ToastStoreActionType.UPDATE_TOAST, { toast: Partial<Toast> }>
29
+ | GenericAction<ToastStoreActionType.DISMISS_TOAST, { toastId?: string }>
30
+ | GenericAction<ToastStoreActionType.REMOVE_TOAST, { toastId?: string }>
31
+ | GenericAction<ToastStoreActionType.START_PAUSE, { time: number }>
32
+ | GenericAction<ToastStoreActionType.END_PAUSE, { time: number }>;
33
+
34
+ interface ToastState {
35
+ toasts: Toast[];
36
+ pausedAt: number | undefined;
37
+ }
38
+
39
+ const toastTimeouts = new Map<Toast['id'], ReturnType<typeof setTimeout>>();
40
+
41
+ const addToDismissedQueue = (toastId: string) => {
42
+ if (toastTimeouts.has(toastId)) {
43
+ return;
44
+ }
45
+
46
+ const timeout = setTimeout(() => {
47
+ toastTimeouts.delete(toastId);
48
+ dispatch({
49
+ // eslint-disable-line
50
+ type: ToastStoreActionType.REMOVE_TOAST,
51
+ payload: { toastId },
52
+ });
53
+ }, 1000);
54
+
55
+ toastTimeouts.set(toastId, timeout);
56
+ };
57
+
58
+ const clearFromDismissedQueue = (toastId: string) => {
59
+ const timeout = toastTimeouts.get(toastId);
60
+ if (timeout) {
61
+ clearTimeout(timeout);
62
+ }
63
+ };
64
+
65
+ type ReducerCallback<TState, TAction> = (
66
+ state: TState,
67
+ action: TAction
68
+ ) => TState;
69
+ type HandlerMap<
70
+ TActionTypes extends PropertyKey,
71
+ TAction extends GenericAction<TActionTypes, Record<string, unknown>>,
72
+ TState
73
+ > = {
74
+ [action in TActionTypes]: ReducerCallback<TState, TAction>;
75
+ };
76
+ type ToastStoreHandlerMap = HandlerMap<
77
+ ToastStoreActionType,
78
+ ToastStoreAction,
79
+ ToastState
80
+ >;
81
+
82
+ const createReducer =
83
+ <
84
+ TActionTypes extends PropertyKey,
85
+ TAction extends GenericAction<TActionTypes, Record<string, unknown>>,
86
+ TState,
87
+ THandlers extends HandlerMap<TActionTypes, TAction, TState>
88
+ >(
89
+ initialState: TState,
90
+ handlers: THandlers
91
+ ) =>
92
+ (state: TState = initialState, action: TAction) => {
93
+ if (handlers.hasOwnProperty(action.type)) {
94
+ // eslint-disable-line
95
+ return handlers[action.type](state, action);
96
+ }
97
+
98
+ return state;
99
+ };
100
+
101
+ type ToastStoreHandler = ReducerCallback<ToastState, ToastStoreAction>;
102
+
103
+ const handleAddToast: ToastStoreHandler = (state, action) => {
104
+ if (!('toast' in action.payload)) return state;
105
+
106
+ const { toast } = action.payload;
107
+
108
+ return {
109
+ ...state,
110
+ toasts: [toast as Toast, ...state.toasts].slice(0, TOAST_LIMIT),
111
+ };
112
+ };
113
+
114
+ const handleUpdateToast: ToastStoreHandler = (state, action) => {
115
+ const { toast } = action.payload as { toast: Toast };
116
+
117
+ // @TODO -- Side effects
118
+ if (toast.id) {
119
+ clearFromDismissedQueue(toast.id);
120
+ }
121
+
122
+ return {
123
+ ...state,
124
+ toasts: state.toasts.map((t) =>
125
+ t.id === toast.id ? { ...t, ...toast } : t
126
+ ), // eslint-disable-line
127
+ };
128
+ };
129
+
130
+ const handleUpsertToast: ToastStoreHandler = (state, action) => {
131
+ const { toast } = action.payload as { toast: Toast };
132
+
133
+ // @TODO -- refactor to avoid using recursive function before 'reducer is declared'
134
+ return state.toasts.find((t) => t.id === toast.id)
135
+ ? reducer(state, {
136
+ type: ToastStoreActionType.UPDATE_TOAST,
137
+ payload: { toast },
138
+ }) // eslint-disable-line
139
+ : reducer(state, {
140
+ type: ToastStoreActionType.ADD_TOAST,
141
+ payload: { toast } as { toast: Toast },
142
+ }); // eslint-disable-line
143
+ };
144
+
145
+ const handleDismissToast: ToastStoreHandler = (state, action) => {
146
+ const { toastId } = action.payload as { toastId: string };
147
+
148
+ if (toastId) {
149
+ addToDismissedQueue(toastId);
150
+ } else {
151
+ state.toasts.forEach((toast) => {
152
+ addToDismissedQueue(toast.id);
153
+ });
154
+ }
155
+
156
+ return {
157
+ ...state,
158
+ // eslint-disable-next-line no-confusing-arrow
159
+ toasts: state.toasts.map((t) =>
160
+ t.id === toastId || toastId === undefined ? { ...t, visible: false } : t
161
+ ),
162
+ };
163
+ };
164
+
165
+ const handleRemoveToast: ToastStoreHandler = (state, action) => {
166
+ const { toastId } = action.payload as { toastId: string };
167
+
168
+ if (toastId === undefined) {
169
+ return {
170
+ ...state,
171
+ toasts: [],
172
+ };
173
+ }
174
+
175
+ return {
176
+ ...state,
177
+ toasts: state.toasts.filter((t) => t.id !== toastId),
178
+ };
179
+ };
180
+
181
+ const handleStartPause: ToastStoreHandler = (state, action) => {
182
+ const { time } = action.payload as { time: number };
183
+
184
+ return {
185
+ ...state,
186
+ pausedAt: time,
187
+ };
188
+ };
189
+
190
+ const handleEndPause: ToastStoreHandler = (state, action) => {
191
+ const { time } = action.payload as { time: number };
192
+
193
+ const diff = time - (state.pausedAt || 0);
194
+
195
+ return {
196
+ ...state,
197
+ pausedAt: undefined,
198
+ toasts: state.toasts.map((t) => ({
199
+ ...t,
200
+ pauseDuration: t.pauseDuration + diff,
201
+ })),
202
+ };
203
+ };
204
+
205
+ const actionHandlers: ToastStoreHandlerMap = {
206
+ [ToastStoreActionType.ADD_TOAST]: handleAddToast,
207
+ [ToastStoreActionType.UPDATE_TOAST]: handleUpdateToast,
208
+ [ToastStoreActionType.UPSERT_TOAST]: handleUpsertToast,
209
+ [ToastStoreActionType.DISMISS_TOAST]: handleDismissToast,
210
+ [ToastStoreActionType.REMOVE_TOAST]: handleRemoveToast,
211
+ [ToastStoreActionType.START_PAUSE]: handleStartPause,
212
+ [ToastStoreActionType.END_PAUSE]: handleEndPause,
213
+ };
214
+
215
+ const toastReducer = createReducer<
216
+ ToastStoreActionType,
217
+ ToastStoreAction,
218
+ ToastState,
219
+ ToastStoreHandlerMap
220
+ >({ toasts: [], pausedAt: undefined }, actionHandlers);
221
+
222
+ const reducer = (state: ToastState, action: ToastStoreAction) =>
223
+ toastReducer(state, action);
224
+
225
+ const listeners: Array<(state: ToastState) => void> = [];
226
+
227
+ let memoryState: ToastState = { toasts: [], pausedAt: undefined };
228
+
229
+ export const dispatch = (action: ToastStoreAction): void => {
230
+ memoryState = reducer(memoryState, action);
231
+ listeners.forEach((listener) => {
232
+ listener(memoryState);
233
+ });
234
+ };
235
+
236
+ const defaultTimeouts: {
237
+ [key in ToastType]: number;
238
+ } = {
239
+ blank: 4000,
240
+ error: 4000,
241
+ success: 2000,
242
+ loading: Infinity,
243
+ custom: 4000,
244
+ };
245
+
246
+ export const useToastStore = (
247
+ toastOptions: ExtendedToastOptions = {}
248
+ ): ToastState => {
249
+ const [state, setState] = useState<ToastState>(memoryState);
250
+ useEffect(() => {
251
+ listeners.push(setState);
252
+ return () => {
253
+ const index = listeners.indexOf(setState);
254
+ if (index > -1) {
255
+ listeners.splice(index, 1);
256
+ }
257
+ };
258
+ }, [state]);
259
+
260
+ const mergedToasts = state.toasts.map((t) => ({
261
+ ...toastOptions,
262
+ ...toastOptions[t.type],
263
+ ...t,
264
+ duration:
265
+ t.duration ||
266
+ toastOptions[t.type]?.duration ||
267
+ toastOptions?.duration ||
268
+ defaultTimeouts[t.type],
269
+ style: {
270
+ ...toastOptions.style,
271
+ ...toastOptions[t.type]?.style,
272
+ ...t.style,
273
+ },
274
+ }));
275
+
276
+ return {
277
+ ...state,
278
+ toasts: mergedToasts,
279
+ };
280
+ };
@@ -0,0 +1,283 @@
1
+ import type { Meta } from '@storybook/react';
2
+ import React, { useState } from 'react';
3
+ import { ToastContainer, ToastPosition, toast } from './';
4
+ import { Button } from '../Button/Button';
5
+ import { Table } from '../Table/Table';
6
+ import { Box } from '../Box/Box';
7
+
8
+ const meta: Meta<typeof ToastContainer> = {
9
+ title: 'Components/Toast',
10
+ component: ToastContainer,
11
+ parameters: {
12
+ controls: { hideNoControlsWarning: true },
13
+ },
14
+ decorators: [
15
+ (Story, { args, viewMode }) => (
16
+ <>
17
+ {viewMode === 'story' && <ToastContainer />}
18
+ <Story args={{ ...args }} />
19
+ </>
20
+ ),
21
+ ],
22
+ };
23
+
24
+ export default meta;
25
+
26
+ export const Example = () =>
27
+ (() => {
28
+ const handleClick = () => toast.success('File saved', { duration: 5000 });
29
+ return <Button onClick={handleClick}>Show toast</Button>;
30
+ })();
31
+
32
+ export const Column = () =>
33
+ (() => {
34
+ const codePreviewStyle = {
35
+ padding: '3px 5px',
36
+ borderRadius: '3px',
37
+ border: '1px solid #EEEEEE',
38
+ color: 'rgba(51,51,51,0.9)',
39
+ backgroundColor: '#F8F8F8',
40
+ };
41
+ const toastInterfaceRows = [
42
+ {
43
+ name: 'id',
44
+ type: 'string',
45
+ description:
46
+ 'Unique id of toast. This is autogenerated if not included, but can be included to update an existing toast.',
47
+ },
48
+ {
49
+ name: 'duration',
50
+ type: 'number',
51
+ description:
52
+ 'Duration (in milliseconds) that toast remains on screen before auto-dismissing.',
53
+ },
54
+ {
55
+ name: 'ariaProps',
56
+ type: "{ role: 'status' | 'alert', 'aria-live': 'assertive' | 'off' | 'polite' }",
57
+ description: 'Accessibility props to be passed down to toast.',
58
+ },
59
+ {
60
+ name: 'className',
61
+ type: 'string',
62
+ description: 'Custom class.',
63
+ },
64
+ {
65
+ name: 'style',
66
+ type: 'CSS.StyleObject',
67
+ description: 'Custom styles',
68
+ },
69
+ {
70
+ name: 'position',
71
+ type: " 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';",
72
+ description:
73
+ 'Toast position (overrides global container toast position)',
74
+ },
75
+ {
76
+ name: 'canDismiss',
77
+ type: 'boolean',
78
+ description:
79
+ 'Whether toast includes the close/dismiss icon allowing user to close it (defaults to true)',
80
+ },
81
+ {
82
+ name: 'isCompact',
83
+ type: 'boolean',
84
+ description: 'Whether to render a compact toast (smaller padding)',
85
+ },
86
+ ];
87
+ const columnConfig = [
88
+ { heading: 'Name', dataKey: 'name' },
89
+ {
90
+ heading: 'Type',
91
+ dataKey: 'type',
92
+ render: (cell: any) => <code style={codePreviewStyle}>{cell}</code>,
93
+ },
94
+ { heading: 'Description', dataKey: 'description' },
95
+ ];
96
+ return (
97
+ <Table rowKey="name" columns={columnConfig} rows={toastInterfaceRows} />
98
+ );
99
+ })();
100
+
101
+ export const Position = () =>
102
+ (() => {
103
+ const handleClick = (position: ToastPosition) =>
104
+ toast('Hey there', { position });
105
+ return (
106
+ <Box gap="md" direction="row">
107
+ <Button onClick={() => handleClick('top-right')}>top right</Button>
108
+ <Button onClick={() => handleClick('top-center')}>top center</Button>
109
+ <Button onClick={() => handleClick('top-left')}>top left</Button>
110
+ <Button onClick={() => handleClick('bottom-right')}>
111
+ bottom right
112
+ </Button>
113
+ <Button onClick={() => handleClick('bottom-center')}>
114
+ bottom center
115
+ </Button>
116
+ <Button onClick={() => handleClick('bottom-left')}>bottom left</Button>
117
+ </Box>
118
+ );
119
+ })();
120
+
121
+ export const Duration = () =>
122
+ (() => {
123
+ const handleClick = () => {
124
+ toast(`toast will close in 6 seconds`, { duration: 6000 });
125
+ };
126
+ return (
127
+ <Box gap="md" direction="row">
128
+ <Button onClick={handleClick}>Open toast with custom duration</Button>
129
+ </Box>
130
+ );
131
+ })();
132
+
133
+ export const NotDismissible = () =>
134
+ (() => {
135
+ const handleClick = () => {
136
+ toast(`This toast can't be dismissed`, {
137
+ duration: 5000,
138
+ canDismiss: false,
139
+ });
140
+ };
141
+ return (
142
+ <Box gap="md" direction="row">
143
+ <Button onClick={handleClick}>Open non-dismissible toast</Button>
144
+ </Box>
145
+ );
146
+ })();
147
+
148
+ export const ProgrammaticDismiss = () => {
149
+ const [toastId, setToastId] = useState('');
150
+ const handleClick = () => {
151
+ if (!toastId) {
152
+ const newToastId = toast(`Dismiss programmatically`, {
153
+ duration: 30000,
154
+ canDismiss: false,
155
+ });
156
+ setToastId(newToastId);
157
+ }
158
+ };
159
+ const handleDismiss = () => {
160
+ if (toastId) {
161
+ toast.dismiss(toastId);
162
+ setToastId('');
163
+ }
164
+ };
165
+ return (
166
+ <>
167
+ <Box gap="md" direction="row">
168
+ <Button onClick={handleClick} isDisabled={!!toastId}>
169
+ Show Toast
170
+ </Button>
171
+ <Button
172
+ variant="tertiary"
173
+ isDisabled={!toastId}
174
+ onClick={handleDismiss}
175
+ >
176
+ Dismiss toast
177
+ </Button>
178
+ </Box>
179
+ </>
180
+ );
181
+ };
182
+
183
+ export const Compact = () =>
184
+ (() => {
185
+ const handleClick = () => {
186
+ toast('This is compact', { isCompact: true });
187
+ };
188
+ return (
189
+ <Box gap="md" direction="row">
190
+ <Button onClick={handleClick}>Open compact toast</Button>
191
+ </Box>
192
+ );
193
+ })();
194
+
195
+ export const BasicTypes = () =>
196
+ (() => {
197
+ const handleClick = (type: 'blank' | 'error' | 'success' | 'loading') => {
198
+ if (type === 'success') {
199
+ toast.success('Proposal saved');
200
+ } else if (type === 'error') {
201
+ toast.error('Unable to save');
202
+ } else if (type === 'loading') {
203
+ toast.loading('Loading...', { canDismiss: false });
204
+ } else {
205
+ toast('Toast without icon');
206
+ }
207
+ };
208
+ return (
209
+ <>
210
+ <Box fontSize="lg" fontWeight="bold" margin="0 0 md 0">
211
+ Basic Types
212
+ </Box>
213
+ <Box gap="md" direction="row">
214
+ <Button onClick={() => handleClick('blank')} variant="primary">
215
+ Default
216
+ </Button>
217
+ <Button onClick={() => handleClick('error')} variant="primary">
218
+ Error
219
+ </Button>
220
+ <Button onClick={() => handleClick('success')} variant="primary">
221
+ Success
222
+ </Button>
223
+ <Button onClick={() => handleClick('loading')} variant="primary">
224
+ Loading
225
+ </Button>
226
+ </Box>
227
+ </>
228
+ );
229
+ })();
230
+
231
+ export const AdvancedTypes = () =>
232
+ (() => {
233
+ const handleClick = (type: 'async' | 'async-data') => {
234
+ if (type.includes('async')) {
235
+ const promise = new Promise((resolve, reject) => {
236
+ const randomNum = Math.floor(Math.random() * 2);
237
+ setTimeout(() => {
238
+ if (randomNum % 2 === 0) {
239
+ resolve(', data loaded');
240
+ } else {
241
+ reject(', bad request');
242
+ }
243
+ }, 2000);
244
+ });
245
+ toast.async(promise, {
246
+ loading: 'loading',
247
+ success: (data) => `Success${type.includes('data') ? data : ''}`,
248
+ error: (err) => `Error${type.includes('data') ? err : ''}`,
249
+ });
250
+ }
251
+ };
252
+ return (
253
+ <>
254
+ <Box fontSize="lg" fontWeight="bold" margin="0 0 md 0">
255
+ Advanced Types
256
+ </Box>
257
+ <Box display="block" childGap="md">
258
+ <Box display="block" childGap="sm">
259
+ <p>
260
+ With an async toast, you can pass the three states of an async op
261
+ (loading, success, error) to a corresponding toast state. By
262
+ passing the promise itself to the toast component, will will
263
+ handle displaying different toast variations based on the state of
264
+ the passed promise.
265
+ </p>
266
+ <Button onClick={() => handleClick('async')} variant="primary">
267
+ async
268
+ </Button>
269
+ </Box>
270
+ <Box display="block" childGap="sm">
271
+ <p>
272
+ If you need to include data or an error returned from the promise
273
+ you can pass a function to the toasts in question, see code
274
+ example.
275
+ </p>
276
+ <Button onClick={() => handleClick('async-data')} variant="primary">
277
+ async (including promise data)
278
+ </Button>
279
+ </Box>
280
+ </Box>
281
+ </>
282
+ );
283
+ })();