@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,55 @@
1
+ import { Pagination } from './Pagination';
2
+ import type { Meta } from '@storybook/react';
3
+ import React from 'react';
4
+ import { useState } from 'react';
5
+
6
+ const meta: Meta<typeof Pagination> = {
7
+ title: 'Components/Pagination',
8
+ component: Pagination,
9
+ };
10
+
11
+ export default meta;
12
+
13
+ export const Default = () => {
14
+ const [activePage, setActivePage] = useState(1);
15
+
16
+ return (
17
+ <>
18
+ <Pagination
19
+ activePage={activePage}
20
+ itemsPerPage={20}
21
+ onChange={setActivePage}
22
+ totalItemsCount={87}
23
+ numberOfPagesDisplayed={0}
24
+ />
25
+ </>
26
+ );
27
+ };
28
+
29
+ export const Compact = () => {
30
+ const [activePage, setActivePage] = useState(1);
31
+ return (
32
+ <Pagination
33
+ activePage={activePage}
34
+ itemsPerPage={20}
35
+ onChange={setActivePage}
36
+ totalItemsCount={100}
37
+ isCompact
38
+ />
39
+ );
40
+ };
41
+
42
+ export const PageNumbers = () => {
43
+ const [activePage, setActivePage] = useState(1);
44
+ return (
45
+ <Pagination
46
+ activePage={activePage}
47
+ itemsPerPage={20}
48
+ onChange={setActivePage}
49
+ totalItemsCount={100}
50
+ isCompact
51
+ arePagesVisible
52
+ numberOfPagesDisplayed={3}
53
+ />
54
+ );
55
+ };
@@ -0,0 +1,225 @@
1
+ import React from 'react';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import { Pagination } from './Pagination';
4
+
5
+ describe('Pagination', () => {
6
+ describe('Default', () => {
7
+ test('It renders a component with a nav tag based on props', () => {
8
+ render(
9
+ <Pagination
10
+ activePage={1}
11
+ totalItemsCount={100}
12
+ itemsPerPage={20}
13
+ onChange={() => 'blerg'}
14
+ />
15
+ );
16
+
17
+ const pagination = screen.getByRole('navigation');
18
+ expect(pagination).toBeInTheDocument();
19
+ });
20
+ });
21
+
22
+ describe('Custom Class', () => {
23
+ test('It renders with a custom class if provided', () => {
24
+ render(
25
+ <Pagination
26
+ activePage={1}
27
+ totalItemsCount={100}
28
+ itemsPerPage={20}
29
+ onChange={() => 'blerg'}
30
+ className="custom-class"
31
+ />
32
+ );
33
+
34
+ const pagination = screen.getByRole('navigation');
35
+ expect(pagination).toHaveClass('custom-class');
36
+ });
37
+ });
38
+
39
+ describe('Custom button labels', () => {
40
+ test('It renders with a custom class if provided', () => {
41
+ render(
42
+ <Pagination
43
+ activePage={1}
44
+ totalItemsCount={100}
45
+ itemsPerPage={20}
46
+ onChange={() => 'blerg'}
47
+ className="custom-class"
48
+ prevPageText="hello"
49
+ nextPageText="goodbye"
50
+ />
51
+ );
52
+
53
+ const prevButton = screen.getByText('hello');
54
+ const nextButton = screen.getByText('goodbye');
55
+ expect(prevButton).toBeInTheDocument();
56
+ expect(nextButton).toBeInTheDocument();
57
+ });
58
+ });
59
+
60
+ describe('With Page numbers', () => {
61
+ test('It renders width default page numbers for 5 pages or records', () => {
62
+ render(
63
+ <Pagination
64
+ activePage={1}
65
+ totalItemsCount={100}
66
+ itemsPerPage={20}
67
+ onChange={() => 'blerg'}
68
+ className="custom-class"
69
+ arePagesVisible
70
+ />
71
+ );
72
+ const expectedPages = ['1', '2', '3', '4', '5'];
73
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
74
+ const pages: any[] = [];
75
+
76
+ expectedPages.forEach((_page, i) => {
77
+ pages.push(screen.getByText(expectedPages[i]));
78
+ });
79
+
80
+ expectedPages.forEach((_page, i) => {
81
+ expect(pages[i]).toBeInTheDocument();
82
+ });
83
+ });
84
+
85
+ test('It does not show excess page numbers if prop passed overshoots the number of records', () => {
86
+ render(
87
+ <Pagination
88
+ activePage={1}
89
+ totalItemsCount={5}
90
+ itemsPerPage={20}
91
+ onChange={() => 'blerg'}
92
+ arePagesVisible
93
+ numberOfPagesDisplayed={4}
94
+ />
95
+ );
96
+ const expectedPageNumbers = ['1'];
97
+ const excludedPageNumbers = ['2', '3', '4'];
98
+
99
+ expectedPageNumbers.forEach((_page, i) => {
100
+ expect(screen.getByText(expectedPageNumbers[i])).toBeInTheDocument();
101
+ });
102
+
103
+ excludedPageNumbers.forEach((_page, i) => {
104
+ expect(screen.queryByText(excludedPageNumbers[i])).toBe(null);
105
+ });
106
+ });
107
+
108
+ test('It shows the correct range of active entries when not on last page', () => {
109
+ render(
110
+ <Pagination
111
+ activePage={2}
112
+ totalItemsCount={1200}
113
+ itemsPerPage={20}
114
+ onChange={() => 'blerg'}
115
+ arePagesVisible
116
+ numberOfPagesDisplayed={4}
117
+ />
118
+ );
119
+
120
+ const range = screen.getByText('Showing 21-40 of 1200');
121
+ expect(range).toBeInTheDocument();
122
+ });
123
+
124
+ test('It shows the correct range of active entries when last page is active', () => {
125
+ render(
126
+ <Pagination
127
+ activePage={12}
128
+ totalItemsCount={1151}
129
+ itemsPerPage={100}
130
+ onChange={() => 'blerg'}
131
+ arePagesVisible
132
+ numberOfPagesDisplayed={4}
133
+ />
134
+ );
135
+
136
+ const range = screen.getByText('Showing 1101-1151 of 1151');
137
+ expect(range).toBeInTheDocument();
138
+ });
139
+
140
+ test('It shows ellipsis buttons where appropriate', () => {
141
+ const { rerender } = render(
142
+ <Pagination
143
+ activePage={12}
144
+ totalItemsCount={1151}
145
+ itemsPerPage={100}
146
+ onChange={() => 'blerg'}
147
+ arePagesVisible
148
+ numberOfPagesDisplayed={4}
149
+ />
150
+ );
151
+
152
+ let ellipsisFound: HTMLElement[] = [];
153
+
154
+ ellipsisFound = screen.queryAllByText('...');
155
+ expect(ellipsisFound.length).toBe(1);
156
+
157
+ rerender(
158
+ <Pagination
159
+ activePage={6}
160
+ totalItemsCount={1151}
161
+ itemsPerPage={100}
162
+ onChange={() => 'blerg'}
163
+ arePagesVisible
164
+ numberOfPagesDisplayed={4}
165
+ />
166
+ );
167
+
168
+ ellipsisFound = screen.queryAllByText('...');
169
+ expect(ellipsisFound.length).toBe(2);
170
+
171
+ const buttonsFound = screen.queryAllByRole('button');
172
+ expect(buttonsFound[2]).toHaveTextContent('...');
173
+ expect(buttonsFound[buttonsFound.length - 3]).toHaveTextContent('...');
174
+ });
175
+ });
176
+
177
+ describe('Compact', () => {
178
+ test('Renders compact pagination if prop is passed', () => {
179
+ render(
180
+ <Pagination
181
+ activePage={1}
182
+ totalItemsCount={100}
183
+ itemsPerPage={20}
184
+ onChange={() => 'blerg'}
185
+ arePagesVisible
186
+ isCompact
187
+ />
188
+ );
189
+
190
+ const buttons = screen.queryAllByRole('button');
191
+
192
+ buttons.forEach((button) => {
193
+ expect(button).toHaveClass('size-sm');
194
+ });
195
+ });
196
+ });
197
+
198
+ describe('Callback Handling', () => {
199
+ test('It fires onChange callback when previous/next buttons are clicked', () => {
200
+ const mockedHandleChange = jest.fn();
201
+
202
+ render(
203
+ <Pagination
204
+ activePage={2}
205
+ totalItemsCount={100}
206
+ itemsPerPage={20}
207
+ onChange={mockedHandleChange}
208
+ arePagesVisible
209
+ />
210
+ );
211
+
212
+ const prevButton = screen.getByText('Previous').closest('button');
213
+ const nextButton = screen.getByText('Next').closest('button');
214
+ const pageFour = screen.getByText('4').closest('button');
215
+ if (prevButton) fireEvent.click(prevButton);
216
+ if (nextButton) fireEvent.click(nextButton);
217
+ if (pageFour) fireEvent.click(pageFour);
218
+
219
+ expect(mockedHandleChange).toBeCalledTimes(3);
220
+ expect(mockedHandleChange.mock.calls[0][0]).toBe(1);
221
+ expect(mockedHandleChange.mock.calls[1][0]).toBe(3);
222
+ expect(mockedHandleChange.mock.calls[2][0]).toBe(4);
223
+ });
224
+ });
225
+ });
@@ -0,0 +1,162 @@
1
+ import React, { FC, ReactNode, useMemo } from 'react';
2
+ import classNames from 'classnames';
3
+ import { Box } from '../Box/Box';
4
+ import { Button } from '../Button/Button';
5
+ import {
6
+ generatePages,
7
+ generatePageRange,
8
+ generatePageTotal,
9
+ generateActiveListRange,
10
+ } from './Pagination.utilities';
11
+
12
+ export interface PaginationProps {
13
+ /**
14
+ * The current page number being displayed.
15
+ */
16
+ activePage: number;
17
+ /**
18
+ * The number of list items contained in a page.
19
+ */
20
+ itemsPerPage: number;
21
+ /**
22
+ * Callback fired when the user clicks a page or prev/next button.
23
+ */
24
+ onChange: (pageNumber: number) => void;
25
+ /**
26
+ * The total number of items in the list
27
+ */
28
+ totalItemsCount: number;
29
+ /**
30
+ * Boolean to determine if individual page buttons (or dropdown are visible). Takes a `ResponsiveProp`
31
+ * if you want to render it differently at different breakpoints
32
+ */
33
+ arePagesVisible?: boolean;
34
+ /**
35
+ * Custom class to pass down to the pagination container.
36
+ */
37
+ className?: string;
38
+ /**
39
+ * Pass true to render a version of Pagination with smaller buttons.
40
+ */
41
+ isCompact?: boolean;
42
+ /**
43
+ * Boolean to determine if the list totals (and current range) are visible.
44
+ * NOTE: these are hidden on mobile regardless of this prop value.
45
+ */
46
+ isTotalVisible?: boolean;
47
+ /**
48
+ * The text (or react node) to pass to the NEXT page button.
49
+ */
50
+ nextPageText?: string | ReactNode;
51
+ /**
52
+ * Number of pages shown in paginator, not including navigation blocks (prev, next), as well as first, last pages.
53
+ * In other words the number of pages displayed 'in the middle', that the user can navigate to.
54
+ */
55
+ numberOfPagesDisplayed?: number;
56
+ /**
57
+ * The text (or react node) to pass to the PREVIOUS page button.
58
+ */
59
+ prevPageText?: string | ReactNode;
60
+ }
61
+
62
+ export const Pagination: FC<PaginationProps> = ({
63
+ activePage,
64
+ itemsPerPage,
65
+ onChange,
66
+ totalItemsCount,
67
+ arePagesVisible = false,
68
+ className = undefined,
69
+ isCompact = false,
70
+ isTotalVisible = true,
71
+ nextPageText = 'Next',
72
+ numberOfPagesDisplayed = 5,
73
+ prevPageText = 'Previous',
74
+ }) => {
75
+ const pageTotal = useMemo(
76
+ () => generatePageTotal(totalItemsCount, itemsPerPage),
77
+ [totalItemsCount, itemsPerPage]
78
+ );
79
+
80
+ const pageRange = useMemo(
81
+ () => generatePageRange(numberOfPagesDisplayed, pageTotal),
82
+ [numberOfPagesDisplayed, pageTotal]
83
+ );
84
+
85
+ const activeListRange = useMemo(
86
+ () => generateActiveListRange(activePage, totalItemsCount, itemsPerPage),
87
+ [activePage, totalItemsCount, itemsPerPage]
88
+ );
89
+
90
+ const pages = useMemo(
91
+ () =>
92
+ generatePages(pageRange, pageTotal, activePage, numberOfPagesDisplayed),
93
+ [pageRange, pageTotal, activePage, numberOfPagesDisplayed]
94
+ );
95
+
96
+ return (
97
+ <Box
98
+ as="nav"
99
+ direction="row"
100
+ alignItems="center"
101
+ justifyContent="space-between"
102
+ padding="lg"
103
+ className={classNames(className)}
104
+ >
105
+ <Box
106
+ direction="row"
107
+ justifyContent={{ base: 'space-between' }}
108
+ flex={{ base: 'auto', tablet: 'none' }}
109
+ gap={isCompact ? 'xs' : 'sm'}
110
+ >
111
+ <Button
112
+ variant="primary"
113
+ size={isCompact ? 'sm' : 'md'}
114
+ isDisabled={activePage === 1}
115
+ onClick={() => onChange(activePage - 1)}
116
+ >
117
+ {prevPageText}
118
+ </Button>
119
+ {arePagesVisible && (
120
+ <Box direction="row" gap="2xs">
121
+ {pages.map(({ pageNumber, isPage }) => {
122
+ console.log(activePage, pageNumber, isPage);
123
+ return (
124
+ <Button
125
+ key={pageNumber}
126
+ onClick={() => onChange(pageNumber)}
127
+ variant={pageNumber === activePage ? 'secondary' : 'tertiary'}
128
+ size={isCompact ? 'sm' : 'md'}
129
+ style={{
130
+ minWidth: isCompact ? '33px' : '42px',
131
+ }}
132
+ className={className}
133
+ >
134
+ {isPage ? pageNumber : '...'}
135
+ </Button>
136
+ );
137
+ })}
138
+ </Box>
139
+ )}
140
+ <Button
141
+ variant="primary"
142
+ size={isCompact ? 'sm' : 'md'}
143
+ isDisabled={activePage === pageTotal}
144
+ onClick={() => onChange(activePage + 1)}
145
+ >
146
+ {nextPageText}
147
+ </Button>
148
+ </Box>
149
+ <Box
150
+ as="p"
151
+ display={{
152
+ base: 'none',
153
+ tablet: 'block',
154
+ }}
155
+ fontSize={isCompact ? 'sm' : 'md'}
156
+ >
157
+ {isTotalVisible &&
158
+ `Showing ${activeListRange.first}-${activeListRange.last} of ${totalItemsCount}`}
159
+ </Box>
160
+ </Box>
161
+ );
162
+ };
@@ -0,0 +1,133 @@
1
+ import {
2
+ generatePages,
3
+ generatePageRange,
4
+ generatePageTotal,
5
+ generateActiveListRange,
6
+ } from './Pagination.utilities';
7
+
8
+ describe('generatePageRange', () => {
9
+ it('returns the number of pages displayed if there are enough total pages', () => {
10
+ const pageRange = generatePageRange(3, 50);
11
+ expect(pageRange).toBe(3);
12
+ });
13
+
14
+ it('returns the page total if it is smaller than the number of pages displayed', () => {
15
+ const pageRange = generatePageRange(3, 2);
16
+ expect(pageRange).toBe(2);
17
+ });
18
+
19
+ it('returns the number of pages displayed if it is the same as total pages', () => {
20
+ const pageRange = generatePageRange(3, 3);
21
+ expect(pageRange).toBe(3);
22
+ });
23
+ });
24
+
25
+ describe('generatePageTotal', () => {
26
+ it('returns correct number of pages for a variety of inputs', () => {
27
+ const pageTotal1 = generatePageTotal(948, 20);
28
+ expect(pageTotal1).toBe(48);
29
+
30
+ const pageTotal2 = generatePageTotal(20, 20);
31
+ expect(pageTotal2).toBe(1);
32
+
33
+ const pageTotal3 = generatePageTotal(15, 20);
34
+ expect(pageTotal3).toBe(1);
35
+ });
36
+ });
37
+
38
+ describe('generateActiveListRange', () => {
39
+ it('returns correct active range for a variety of inputs', () => {
40
+ const activeRange = generateActiveListRange(3, 100, 20);
41
+
42
+ expect(activeRange.first).toBe(41);
43
+ expect(activeRange.last).toBe(60);
44
+
45
+ const activeRange2 = generateActiveListRange(1, 10000, 300);
46
+
47
+ expect(activeRange2.first).toBe(1);
48
+ expect(activeRange2.last).toBe(300);
49
+ });
50
+ });
51
+
52
+ describe('generatePages', () => {
53
+ it('returns correct pages -- scenario 1', () => {
54
+ const pages = generatePages(3, 10, 3, 3);
55
+ expect(pages.length).toBe(5);
56
+
57
+ expect(pages[0].isPage).toBe(true);
58
+ expect(pages[0].pageNumber).toBe(1);
59
+
60
+ expect(pages[1].isPage).toBe(true);
61
+ expect(pages[1].pageNumber).toBe(2);
62
+
63
+ expect(pages[2].isPage).toBe(true);
64
+ expect(pages[2].pageNumber).toBe(3);
65
+
66
+ expect(pages[3].isPage).toBe(false);
67
+ expect(pages[3].pageNumber).toBe(6);
68
+
69
+ expect(pages[4].isPage).toBe(true);
70
+ expect(pages[4].pageNumber).toBe(10);
71
+ });
72
+
73
+ it('returns correct pages -- scenario 2', () => {
74
+ const pages = generatePages(3, 10, 6, 3);
75
+ expect(pages.length).toBe(7);
76
+
77
+ expect(pages[0].isPage).toBe(true);
78
+ expect(pages[0].pageNumber).toBe(1);
79
+
80
+ expect(pages[1].isPage).toBe(false);
81
+ expect(pages[1].pageNumber).toBe(3);
82
+
83
+ expect(pages[2].isPage).toBe(true);
84
+ expect(pages[2].pageNumber).toBe(5);
85
+
86
+ expect(pages[3].isPage).toBe(true);
87
+ expect(pages[3].pageNumber).toBe(6);
88
+
89
+ expect(pages[4].isPage).toBe(true);
90
+ expect(pages[4].pageNumber).toBe(7);
91
+
92
+ expect(pages[5].isPage).toBe(false);
93
+ expect(pages[5].pageNumber).toBe(9);
94
+
95
+ expect(pages[6].isPage).toBe(true);
96
+ expect(pages[6].pageNumber).toBe(10);
97
+ });
98
+
99
+ it('returns correct pages -- scenario 3', () => {
100
+ const pages = generatePages(3, 10, 9, 3);
101
+ expect(pages.length).toBe(5);
102
+
103
+ expect(pages[0].isPage).toBe(true);
104
+ expect(pages[0].pageNumber).toBe(1);
105
+
106
+ expect(pages[1].isPage).toBe(false);
107
+ expect(pages[1].pageNumber).toBe(6);
108
+
109
+ expect(pages[2].isPage).toBe(true);
110
+ expect(pages[2].pageNumber).toBe(8);
111
+
112
+ expect(pages[3].isPage).toBe(true);
113
+ expect(pages[3].pageNumber).toBe(9);
114
+
115
+ expect(pages[4].isPage).toBe(true);
116
+ expect(pages[4].pageNumber).toBe(10);
117
+ });
118
+
119
+ it('returns the correct pages -- one less page range than total', () => {
120
+ const pages = generatePages(2, 3, 1, 2);
121
+
122
+ expect(pages[0].isPage).toBe(true);
123
+ expect(pages[0].pageNumber).toBe(1);
124
+
125
+ expect(pages[1].isPage).toBe(true);
126
+ expect(pages[1].pageNumber).toBe(2);
127
+
128
+ expect(pages[2].isPage).toBe(true);
129
+ expect(pages[2].pageNumber).toBe(3);
130
+
131
+ expect(pages.length).toBe(3);
132
+ });
133
+ });
@@ -0,0 +1,101 @@
1
+ export interface Page {
2
+ isPage: boolean;
3
+ pageNumber: number;
4
+ }
5
+
6
+ export const generatePages = (
7
+ pageRange: number,
8
+ pageTotal: number,
9
+ activePage: number,
10
+ numberOfPagesDisplayed: number
11
+ ): Page[] => {
12
+ const pages: Page[] = [];
13
+ let startingPage = 1;
14
+ let endingPage = pageRange;
15
+
16
+ if (pageTotal <= pageRange) {
17
+ startingPage = 1;
18
+ endingPage = pageRange;
19
+ } else if (activePage + numberOfPagesDisplayed > pageTotal) {
20
+ startingPage = pageTotal - (numberOfPagesDisplayed - 1);
21
+ endingPage = startingPage + (numberOfPagesDisplayed - 1);
22
+ } else if (
23
+ activePage > numberOfPagesDisplayed &&
24
+ activePage + numberOfPagesDisplayed <= pageTotal
25
+ ) {
26
+ startingPage = activePage - Math.floor(numberOfPagesDisplayed / 2);
27
+ endingPage = startingPage + (numberOfPagesDisplayed - 1);
28
+ }
29
+
30
+ for (let i = startingPage; i <= endingPage; i += 1) {
31
+ pages.push({ pageNumber: i, isPage: true });
32
+ }
33
+
34
+ if (pageTotal > pages[pages.length - 1]?.pageNumber) {
35
+ const secondToLastPage =
36
+ pageTotal !== activePage + numberOfPagesDisplayed
37
+ ? activePage + numberOfPagesDisplayed
38
+ : pageTotal - 1;
39
+
40
+ // only add ellipsis if there are more than 0 pages between the final page and the rest of the pages
41
+ if (pageTotal > numberOfPagesDisplayed + 1) {
42
+ pages.push({ pageNumber: secondToLastPage, isPage: false });
43
+ }
44
+
45
+ pages.push({ pageNumber: pageTotal, isPage: true });
46
+ }
47
+
48
+ if (activePage > numberOfPagesDisplayed) {
49
+ const threeDotsPage =
50
+ activePage - numberOfPagesDisplayed > 1
51
+ ? activePage - numberOfPagesDisplayed
52
+ : activePage - numberOfPagesDisplayed + 1;
53
+
54
+ pages.unshift(
55
+ { pageNumber: 1, isPage: true },
56
+ { pageNumber: threeDotsPage, isPage: false }
57
+ );
58
+ }
59
+
60
+ return [...pages];
61
+ };
62
+
63
+ // Return the true page range in cases
64
+ // where number of pages wanted for display is larger than the actual page total.
65
+ export const generatePageRange = (
66
+ numberOfPagesDisplayed: number,
67
+ pageTotal: number
68
+ ): number =>
69
+ numberOfPagesDisplayed > pageTotal ? pageTotal : numberOfPagesDisplayed;
70
+
71
+ export const generatePageTotal = (
72
+ totalItemsCount: number,
73
+ itemsPerPage: number
74
+ ): number => Math.ceil(totalItemsCount / itemsPerPage);
75
+
76
+ // Returns the range of current items displayed based on the specific page.
77
+ // E.G: if the items per page is 20 and we are on page 1, it will return:
78
+ // { first: 1, last: 20 }
79
+ export const generateActiveListRange = (
80
+ activePage: number,
81
+ totalItemsCount: number,
82
+ itemsPerPage: number
83
+ ): { first?: number; last?: number } => {
84
+ const activePageRange: { first?: number; last?: number } = {};
85
+
86
+ const pageTotal = generatePageTotal(totalItemsCount, itemsPerPage);
87
+
88
+ if (activePage === 1) {
89
+ activePageRange.first = 1;
90
+ activePageRange.last =
91
+ totalItemsCount > itemsPerPage ? itemsPerPage : totalItemsCount;
92
+ } else if (activePage < pageTotal) {
93
+ activePageRange.first = activePage * itemsPerPage - (itemsPerPage - 1);
94
+ activePageRange.last = activePage * itemsPerPage;
95
+ } else {
96
+ activePageRange.first = activePage * itemsPerPage - (itemsPerPage - 1);
97
+ activePageRange.last = totalItemsCount;
98
+ }
99
+
100
+ return activePageRange;
101
+ };