@sqrzro/admin 1.0.0-beta.1

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 (476) hide show
  1. package/.eslintrc +4 -0
  2. package/.gitattributes +1 -0
  3. package/.prettierrc +9 -0
  4. package/.storybook/main.js +40 -0
  5. package/.storybook/manager.js +4 -0
  6. package/.storybook/postcss.config.js +3 -0
  7. package/.storybook/preview.js +11 -0
  8. package/.storybook/theme.js +7 -0
  9. package/.stylelintrc +9 -0
  10. package/LICENSE +5 -0
  11. package/README.md +9 -0
  12. package/__mocks__/squarezero-utility.ts +40 -0
  13. package/assets/logo-horizontal-dark.svg +21 -0
  14. package/assets/logo-horizontal-white.svg +21 -0
  15. package/assets/logo-vertical-dark.svg +21 -0
  16. package/assets/logo-vertical-white.svg +21 -0
  17. package/dist/index.css +3554 -0
  18. package/dist/index.js +5099 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/types/components/Alert/index.d.ts +10 -0
  21. package/dist/types/components/Allow/index.d.ts +13 -0
  22. package/dist/types/components/App/index.d.ts +19 -0
  23. package/dist/types/components/AutoSuggest/index.d.ts +60 -0
  24. package/dist/types/components/Button/index.d.ts +36 -0
  25. package/dist/types/components/CalendarInput/index.d.ts +10 -0
  26. package/dist/types/components/CheckboxList/index.d.ts +4 -0
  27. package/dist/types/components/ConnectedDropdown/index.d.ts +8 -0
  28. package/dist/types/components/ConnectedList/index.d.ts +4 -0
  29. package/dist/types/components/ConnectedScene/index.d.ts +11 -0
  30. package/dist/types/components/ConnectedTable/index.d.ts +4 -0
  31. package/dist/types/components/ContentBlock/index.d.ts +6 -0
  32. package/dist/types/components/DateInput/index.d.ts +7 -0
  33. package/dist/types/components/Document/index.d.ts +6 -0
  34. package/dist/types/components/Dropdown/index.d.ts +14 -0
  35. package/dist/types/components/Fieldset/index.d.ts +11 -0
  36. package/dist/types/components/FileInput/index.d.ts +4 -0
  37. package/dist/types/components/FilterLink/index.d.ts +7 -0
  38. package/dist/types/components/Form/index.d.ts +11 -0
  39. package/dist/types/components/FormActions/index.d.ts +9 -0
  40. package/dist/types/components/FormField/index.d.ts +27 -0
  41. package/dist/types/components/FormRepeater/index.d.ts +8 -0
  42. package/dist/types/components/ImageInput/index.d.ts +4 -0
  43. package/dist/types/components/InfoPanel/index.d.ts +8 -0
  44. package/dist/types/components/Link/__mocks__/index.d.ts +6 -0
  45. package/dist/types/components/Link/index.d.ts +13 -0
  46. package/dist/types/components/List/index.d.ts +23 -0
  47. package/dist/types/components/ListActions/index.d.ts +9 -0
  48. package/dist/types/components/LoginForm/index.d.ts +3 -0
  49. package/dist/types/components/Message/index.d.ts +7 -0
  50. package/dist/types/components/ModalActions/index.d.ts +11 -0
  51. package/dist/types/components/MoneyInput/index.d.ts +10 -0
  52. package/dist/types/components/PasswordForm/index.d.ts +3 -0
  53. package/dist/types/components/PasswordInput/index.d.ts +8 -0
  54. package/dist/types/components/RadioList/index.d.ts +4 -0
  55. package/dist/types/components/Scene/index.d.ts +10 -0
  56. package/dist/types/components/SummaryList/index.d.ts +8 -0
  57. package/dist/types/components/Switch/index.d.ts +9 -0
  58. package/dist/types/components/Table/index.d.ts +13 -0
  59. package/dist/types/components/Tag/index.d.ts +8 -0
  60. package/dist/types/components/TextArea/index.d.ts +4 -0
  61. package/dist/types/components/TextInput/index.d.ts +17 -0
  62. package/dist/types/components/WebsiteInput/index.d.ts +10 -0
  63. package/dist/types/components/WeekCalendar/index.d.ts +12 -0
  64. package/dist/types/core/AppHeader/index.d.ts +3 -0
  65. package/dist/types/core/AppLogo/index.d.ts +6 -0
  66. package/dist/types/core/AppLogoImage/index.d.ts +3 -0
  67. package/dist/types/core/AppLogoPlaceholder/index.d.ts +3 -0
  68. package/dist/types/core/Assistive/index.d.ts +6 -0
  69. package/dist/types/core/Banner/index.d.ts +3 -0
  70. package/dist/types/core/BuildModal/index.d.ts +3 -0
  71. package/dist/types/core/Calendar/index.d.ts +11 -0
  72. package/dist/types/core/CalendarDay/index.d.ts +16 -0
  73. package/dist/types/core/CalendarMonth/index.d.ts +12 -0
  74. package/dist/types/core/CalendarNavigation/index.d.ts +9 -0
  75. package/dist/types/core/CalendarWeek/index.d.ts +14 -0
  76. package/dist/types/core/ChkRad/index.d.ts +10 -0
  77. package/dist/types/core/ChkRadIcon/index.d.ts +7 -0
  78. package/dist/types/core/ChkRadList/index.d.ts +15 -0
  79. package/dist/types/core/CloseButton/index.d.ts +9 -0
  80. package/dist/types/core/ConfirmModal/index.d.ts +3 -0
  81. package/dist/types/core/ConnectedRepeater/index.d.ts +26 -0
  82. package/dist/types/core/ConnectedRepeaterComponent/index.d.ts +24 -0
  83. package/dist/types/core/Container/index.d.ts +7 -0
  84. package/dist/types/core/DropdownPanel/index.d.ts +18 -0
  85. package/dist/types/core/EmptyMessage/index.d.ts +12 -0
  86. package/dist/types/core/ErrorBoundary/index.d.ts +15 -0
  87. package/dist/types/core/ErrorMessage/index.d.ts +8 -0
  88. package/dist/types/core/FilterItem/index.d.ts +10 -0
  89. package/dist/types/core/Filters/index.d.ts +11 -0
  90. package/dist/types/core/FixedActions/index.d.ts +12 -0
  91. package/dist/types/core/FormError/index.d.ts +6 -0
  92. package/dist/types/core/FormLabel/index.d.ts +8 -0
  93. package/dist/types/core/FormLegend/index.d.ts +6 -0
  94. package/dist/types/core/Header/index.d.ts +8 -0
  95. package/dist/types/core/Icon/index.d.ts +7 -0
  96. package/dist/types/core/IconButton/Icon/index.d.ts +7 -0
  97. package/dist/types/core/IconButton/index.d.ts +11 -0
  98. package/dist/types/core/InputPanel/index.d.ts +8 -0
  99. package/dist/types/core/ListItem/index.d.ts +16 -0
  100. package/dist/types/core/ListItemAction/index.d.ts +4 -0
  101. package/dist/types/core/ListItemActions/index.d.ts +8 -0
  102. package/dist/types/core/ListItemMetaItem/index.d.ts +10 -0
  103. package/dist/types/core/ListItemTitle/index.d.ts +9 -0
  104. package/dist/types/core/Loader/index.d.ts +6 -0
  105. package/dist/types/core/MeActions/index.d.ts +3 -0
  106. package/dist/types/core/MePanel/index.d.ts +9 -0
  107. package/dist/types/core/Modal/index.d.ts +12 -0
  108. package/dist/types/core/Navigation/index.d.ts +3 -0
  109. package/dist/types/core/NavigationDivider/index.d.ts +3 -0
  110. package/dist/types/core/NavigationItem/index.d.ts +7 -0
  111. package/dist/types/core/Pagination/index.d.ts +7 -0
  112. package/dist/types/core/PaginationItem/index.d.ts +7 -0
  113. package/dist/types/core/Panel/index.d.ts +8 -0
  114. package/dist/types/core/RadialProgress/index.d.ts +8 -0
  115. package/dist/types/core/StaticTextInput/index.d.ts +12 -0
  116. package/dist/types/core/Styled/index.d.ts +6 -0
  117. package/dist/types/core/SummaryListItem/index.d.ts +15 -0
  118. package/dist/types/core/TableActionsCell/index.d.ts +8 -0
  119. package/dist/types/core/TableCell/index.d.ts +8 -0
  120. package/dist/types/core/TableHead/index.d.ts +8 -0
  121. package/dist/types/core/TableRow/index.d.ts +13 -0
  122. package/dist/types/core/Tabs/index.d.ts +11 -0
  123. package/dist/types/core/TextInputAncillary/index.d.ts +11 -0
  124. package/dist/types/core/Toast/index.d.ts +3 -0
  125. package/dist/types/core/WeekCalendarDay/index.d.ts +14 -0
  126. package/dist/types/filters/BooleanFilter/index.d.ts +5 -0
  127. package/dist/types/filters/DateFilter/index.d.ts +5 -0
  128. package/dist/types/filters/DropdownFilter/index.d.ts +5 -0
  129. package/dist/types/filters/QuickDateFilter/index.d.ts +5 -0
  130. package/dist/types/filters/interfaces.d.ts +36 -0
  131. package/dist/types/hooks/useAccessiblePanel.d.ts +13 -0
  132. package/dist/types/hooks/useAppContext.d.ts +37 -0
  133. package/dist/types/hooks/useClickOutside.d.ts +3 -0
  134. package/dist/types/hooks/useConnectedList.d.ts +7 -0
  135. package/dist/types/hooks/useFilters.d.ts +3 -0
  136. package/dist/types/hooks/useForm.d.ts +26 -0
  137. package/dist/types/hooks/useFormData.d.ts +9 -0
  138. package/dist/types/hooks/useLayout.d.ts +6 -0
  139. package/dist/types/hooks/useModal.d.ts +12 -0
  140. package/dist/types/hooks/useVariant.d.ts +6 -0
  141. package/dist/types/index.d.ts +187 -0
  142. package/dist/types/scenes/Error404Scene.d.ts +3 -0
  143. package/dist/types/services/BuildService.d.ts +5 -0
  144. package/dist/types/services/ConfirmService.d.ts +12 -0
  145. package/dist/types/services/DateService.d.ts +17 -0
  146. package/dist/types/services/DownloadService.d.ts +5 -0
  147. package/dist/types/services/FilterComponentService.d.ts +8 -0
  148. package/dist/types/services/FilterService.d.ts +13 -0
  149. package/dist/types/services/FormatService.d.ts +7 -0
  150. package/dist/types/services/ToastService.d.ts +5 -0
  151. package/dist/types/utility/MockRouter/index.d.ts +2 -0
  152. package/dist/types/utility/StorybookPanel/index.d.ts +7 -0
  153. package/dist/types/utility/TestChangeHandler/index.d.ts +8 -0
  154. package/dist/types/utility/create-app.d.ts +4 -0
  155. package/dist/types/utility/interfaces.d.ts +72 -0
  156. package/dist/types/utility/prop-types.d.ts +40 -0
  157. package/docs/guides/01-installation.story.mdx +33 -0
  158. package/docs/guides/02-configuration.story.mdx +80 -0
  159. package/docs/guides/03-pages.story.mdx +18 -0
  160. package/docs/guides/04-integration.story.mdx +33 -0
  161. package/docs/guides/a11y.story.mdx +7 -0
  162. package/docs/guides/theming.story.mdx +7 -0
  163. package/docs/introduction.story.mdx +17 -0
  164. package/jest.config.js +19 -0
  165. package/package.json +101 -0
  166. package/postcss.config.js +3 -0
  167. package/rollup.config.js +36 -0
  168. package/scripts/create-exports.js +74 -0
  169. package/scripts/update-license.js +8 -0
  170. package/squarezero.config.js +5 -0
  171. package/src/components/Alert/Alert.module.css +50 -0
  172. package/src/components/Alert/Alert.spec.tsx +63 -0
  173. package/src/components/Alert/Alert.story.tsx +23 -0
  174. package/src/components/Alert/index.tsx +67 -0
  175. package/src/components/Allow/Allow.spec.tsx +37 -0
  176. package/src/components/Allow/index.tsx +42 -0
  177. package/src/components/App/App.module.css +22 -0
  178. package/src/components/App/App.spec.tsx +128 -0
  179. package/src/components/App/index.tsx +108 -0
  180. package/src/components/AutoSuggest/AutoSuggest.module.css +8 -0
  181. package/src/components/AutoSuggest/AutoSuggest.spec.tsx +211 -0
  182. package/src/components/AutoSuggest/AutoSuggest.story.tsx +30 -0
  183. package/src/components/AutoSuggest/index.tsx +216 -0
  184. package/src/components/Button/Button.module.css +95 -0
  185. package/src/components/Button/Button.spec.tsx +69 -0
  186. package/src/components/Button/Button.story.tsx +39 -0
  187. package/src/components/Button/index.tsx +99 -0
  188. package/src/components/CalendarInput/CalendarInput.module.css +8 -0
  189. package/src/components/CalendarInput/CalendarInput.spec.tsx +206 -0
  190. package/src/components/CalendarInput/CalendarInput.story.tsx +62 -0
  191. package/src/components/CalendarInput/index.tsx +122 -0
  192. package/src/components/CheckboxList/CheckboxList.spec.tsx +83 -0
  193. package/src/components/CheckboxList/index.tsx +53 -0
  194. package/src/components/ConnectedDropdown/index.tsx +56 -0
  195. package/src/components/ConnectedList/index.tsx +15 -0
  196. package/src/components/ConnectedScene/index.tsx +95 -0
  197. package/src/components/ConnectedTable/index.tsx +17 -0
  198. package/src/components/ContentBlock/ContentBlock.module.css +8 -0
  199. package/src/components/ContentBlock/ContentBlock.spec.tsx +15 -0
  200. package/src/components/ContentBlock/index.tsx +16 -0
  201. package/src/components/DateInput/DateInput.module.css +15 -0
  202. package/src/components/DateInput/DateInput.spec.tsx +91 -0
  203. package/src/components/DateInput/index.tsx +149 -0
  204. package/src/components/Document/index.tsx +20 -0
  205. package/src/components/Dropdown/Dropdown.module.css +8 -0
  206. package/src/components/Dropdown/Dropdown.spec.tsx +286 -0
  207. package/src/components/Dropdown/Dropdown.story.tsx +53 -0
  208. package/src/components/Dropdown/index.tsx +224 -0
  209. package/src/components/Fieldset/Fieldset.spec.tsx +22 -0
  210. package/src/components/Fieldset/Fieldset.story.tsx +21 -0
  211. package/src/components/Fieldset/index.tsx +30 -0
  212. package/src/components/FileInput/FileInput.module.css +18 -0
  213. package/src/components/FileInput/FileInput.spec.tsx +42 -0
  214. package/src/components/FileInput/FileInput.story.tsx +6 -0
  215. package/src/components/FileInput/index.tsx +72 -0
  216. package/src/components/FilterLink/FilterLink.module.css +9 -0
  217. package/src/components/FilterLink/FilterLink.spec.tsx +20 -0
  218. package/src/components/FilterLink/index.tsx +31 -0
  219. package/src/components/Form/Form.spec.tsx +68 -0
  220. package/src/components/Form/index.tsx +87 -0
  221. package/src/components/FormActions/FormActions.spec.tsx +10 -0
  222. package/src/components/FormActions/FormActions.story.tsx +23 -0
  223. package/src/components/FormActions/index.tsx +31 -0
  224. package/src/components/FormField/FormField.module.css +71 -0
  225. package/src/components/FormField/FormField.spec.tsx +137 -0
  226. package/src/components/FormField/FormField.story.tsx +22 -0
  227. package/src/components/FormField/index.tsx +162 -0
  228. package/src/components/FormRepeater/FormRepeater.module.css +3 -0
  229. package/src/components/FormRepeater/FormRepeater.spec.tsx +127 -0
  230. package/src/components/FormRepeater/index.tsx +118 -0
  231. package/src/components/ImageInput/ImageInput.module.css +74 -0
  232. package/src/components/ImageInput/ImageInput.spec.tsx +45 -0
  233. package/src/components/ImageInput/ImageInput.story.tsx +6 -0
  234. package/src/components/ImageInput/index.tsx +91 -0
  235. package/src/components/InfoPanel/InfoPanel.module.css +18 -0
  236. package/src/components/InfoPanel/InfoPanel.spec.tsx +15 -0
  237. package/src/components/InfoPanel/InfoPanel.story.tsx +32 -0
  238. package/src/components/InfoPanel/index.tsx +25 -0
  239. package/src/components/Link/Link.spec.tsx +62 -0
  240. package/src/components/Link/__mocks__/index.tsx +24 -0
  241. package/src/components/Link/index.tsx +51 -0
  242. package/src/components/List/List.module.css +9 -0
  243. package/src/components/List/List.spec.tsx +311 -0
  244. package/src/components/List/List.story.tsx +92 -0
  245. package/src/components/List/index.tsx +79 -0
  246. package/src/components/ListActions/ListActions.spec.tsx +10 -0
  247. package/src/components/ListActions/ListActions.story.tsx +6 -0
  248. package/src/components/ListActions/index.tsx +31 -0
  249. package/src/components/LoginForm/LoginForm.module.css +21 -0
  250. package/src/components/LoginForm/LoginForm.spec.tsx +63 -0
  251. package/src/components/LoginForm/index.tsx +102 -0
  252. package/src/components/Message/Message.module.css +14 -0
  253. package/src/components/Message/Message.spec.tsx +20 -0
  254. package/src/components/Message/index.tsx +27 -0
  255. package/src/components/ModalActions/ModalActions.module.css +5 -0
  256. package/src/components/ModalActions/ModalActions.spec.tsx +53 -0
  257. package/src/components/ModalActions/ModalActions.story.tsx +6 -0
  258. package/src/components/ModalActions/index.tsx +47 -0
  259. package/src/components/MoneyInput/MoneyInput.spec.tsx +17 -0
  260. package/src/components/MoneyInput/MoneyInput.story.tsx +6 -0
  261. package/src/components/MoneyInput/index.tsx +16 -0
  262. package/src/components/PasswordForm/PasswordForm.module.css +21 -0
  263. package/src/components/PasswordForm/index.tsx +94 -0
  264. package/src/components/PasswordInput/PasswordInput.module.css +23 -0
  265. package/src/components/PasswordInput/PasswordInput.spec.tsx +31 -0
  266. package/src/components/PasswordInput/PasswordInput.story.tsx +16 -0
  267. package/src/components/PasswordInput/index.tsx +27 -0
  268. package/src/components/RadioList/RadioList.spec.tsx +45 -0
  269. package/src/components/RadioList/index.tsx +10 -0
  270. package/src/components/Scene/Scene.spec.tsx +123 -0
  271. package/src/components/Scene/Scene.story.tsx +6 -0
  272. package/src/components/Scene/index.tsx +58 -0
  273. package/src/components/SummaryList/SummaryList.module.css +16 -0
  274. package/src/components/SummaryList/SummaryList.spec.tsx +77 -0
  275. package/src/components/SummaryList/SummaryList.story.tsx +11 -0
  276. package/src/components/SummaryList/index.tsx +37 -0
  277. package/src/components/Switch/Switch.module.css +86 -0
  278. package/src/components/Switch/Switch.spec.tsx +64 -0
  279. package/src/components/Switch/index.tsx +85 -0
  280. package/src/components/Table/Table.module.css +14 -0
  281. package/src/components/Table/Table.spec.tsx +128 -0
  282. package/src/components/Table/Table.story.tsx +101 -0
  283. package/src/components/Table/index.tsx +114 -0
  284. package/src/components/Tag/Tag.module.css +37 -0
  285. package/src/components/Tag/Tag.spec.tsx +10 -0
  286. package/src/components/Tag/Tag.story.tsx +55 -0
  287. package/src/components/Tag/index.tsx +27 -0
  288. package/src/components/TextArea/TextArea.module.css +23 -0
  289. package/src/components/TextArea/TextArea.spec.tsx +77 -0
  290. package/src/components/TextArea/index.tsx +59 -0
  291. package/src/components/TextInput/TextInput.module.css +57 -0
  292. package/src/components/TextInput/TextInput.spec.tsx +134 -0
  293. package/src/components/TextInput/TextInput.story.tsx +24 -0
  294. package/src/components/TextInput/index.tsx +146 -0
  295. package/src/components/WebsiteInput/WebsiteInput.spec.tsx +17 -0
  296. package/src/components/WebsiteInput/index.tsx +16 -0
  297. package/src/components/WeekCalendar/WeekCalendar.module.css +19 -0
  298. package/src/components/WeekCalendar/index.tsx +80 -0
  299. package/src/core/AppHeader/index.tsx +23 -0
  300. package/src/core/AppLogo/AppLogo.module.css +19 -0
  301. package/src/core/AppLogo/index.tsx +33 -0
  302. package/src/core/AppLogoImage/index.tsx +19 -0
  303. package/src/core/AppLogoPlaceholder/index.tsx +26 -0
  304. package/src/core/Assistive/Assistive.module.css +10 -0
  305. package/src/core/Assistive/index.tsx +18 -0
  306. package/src/core/Banner/Banner.module.css +36 -0
  307. package/src/core/Banner/index.tsx +25 -0
  308. package/src/core/BuildModal/index.tsx +119 -0
  309. package/src/core/Calendar/index.tsx +116 -0
  310. package/src/core/CalendarDay/CalendarDay.module.css +61 -0
  311. package/src/core/CalendarDay/index.tsx +82 -0
  312. package/src/core/CalendarMonth/CalendarMonth.module.css +13 -0
  313. package/src/core/CalendarMonth/index.tsx +101 -0
  314. package/src/core/CalendarNavigation/CalendarNavigation.module.css +26 -0
  315. package/src/core/CalendarNavigation/index.tsx +56 -0
  316. package/src/core/CalendarWeek/index.tsx +95 -0
  317. package/src/core/ChkRad/ChkRad.module.css +31 -0
  318. package/src/core/ChkRad/ChkRad.spec.tsx +72 -0
  319. package/src/core/ChkRad/index.tsx +80 -0
  320. package/src/core/ChkRadIcon/ChkRadIcon.module.css +28 -0
  321. package/src/core/ChkRadIcon/index.tsx +33 -0
  322. package/src/core/ChkRadList/ChkRadList.module.css +10 -0
  323. package/src/core/ChkRadList/index.tsx +85 -0
  324. package/src/core/CloseButton/CloseButton.spec.tsx +36 -0
  325. package/src/core/CloseButton/index.tsx +31 -0
  326. package/src/core/ConfirmModal/ConfirmModal.spec.tsx +88 -0
  327. package/src/core/ConfirmModal/index.tsx +51 -0
  328. package/src/core/ConnectedRepeater/index.tsx +248 -0
  329. package/src/core/ConnectedRepeaterComponent/index.tsx +66 -0
  330. package/src/core/Container/Container.module.css +12 -0
  331. package/src/core/Container/index.tsx +35 -0
  332. package/src/core/DropdownPanel/DropdownPanel.module.css +68 -0
  333. package/src/core/DropdownPanel/index.tsx +92 -0
  334. package/src/core/EmptyMessage/EmptyMessage.module.css +18 -0
  335. package/src/core/EmptyMessage/index.tsx +31 -0
  336. package/src/core/ErrorBoundary/ErrorBoundary.spec.tsx +33 -0
  337. package/src/core/ErrorBoundary/index.tsx +42 -0
  338. package/src/core/ErrorMessage/ErrorMessage.module.css +25 -0
  339. package/src/core/ErrorMessage/ErrorMessage.spec.tsx +31 -0
  340. package/src/core/ErrorMessage/index.tsx +37 -0
  341. package/src/core/FilterItem/FilterItem.module.css +104 -0
  342. package/src/core/FilterItem/index.tsx +164 -0
  343. package/src/core/Filters/Filters.module.css +86 -0
  344. package/src/core/Filters/Filters.spec.tsx +533 -0
  345. package/src/core/Filters/index.tsx +158 -0
  346. package/src/core/FixedActions/FixedActions.module.css +24 -0
  347. package/src/core/FixedActions/index.tsx +52 -0
  348. package/src/core/FormError/FormError.module.css +7 -0
  349. package/src/core/FormError/index.tsx +18 -0
  350. package/src/core/FormLabel/FormLabel.module.css +15 -0
  351. package/src/core/FormLabel/index.tsx +25 -0
  352. package/src/core/FormLegend/FormLegend.module.css +13 -0
  353. package/src/core/FormLegend/index.tsx +18 -0
  354. package/src/core/Header/Header.module.css +35 -0
  355. package/src/core/Header/index.tsx +37 -0
  356. package/src/core/Icon/Icon.spec.tsx +31 -0
  357. package/src/core/Icon/index.tsx +64 -0
  358. package/src/core/IconButton/Icon/Icon.spec.tsx +31 -0
  359. package/src/core/IconButton/Icon/index.tsx +64 -0
  360. package/src/core/IconButton/IconButton.module.css +32 -0
  361. package/src/core/IconButton/index.tsx +55 -0
  362. package/src/core/InputPanel/InputPanel.module.css +70 -0
  363. package/src/core/InputPanel/index.tsx +28 -0
  364. package/src/core/ListItem/ListItem.module.css +28 -0
  365. package/src/core/ListItem/ListItem.story.tsx +28 -0
  366. package/src/core/ListItem/index.tsx +99 -0
  367. package/src/core/ListItemAction/ListItemAction.module.css +28 -0
  368. package/src/core/ListItemAction/index.tsx +30 -0
  369. package/src/core/ListItemActions/ListItemActions.module.css +94 -0
  370. package/src/core/ListItemActions/index.tsx +55 -0
  371. package/src/core/ListItemMetaItem/ListItemMetaItem.module.css +18 -0
  372. package/src/core/ListItemMetaItem/index.tsx +54 -0
  373. package/src/core/ListItemTitle/ListItemTitle.module.css +14 -0
  374. package/src/core/ListItemTitle/ListItemTitle.spec.tsx +33 -0
  375. package/src/core/ListItemTitle/index.tsx +72 -0
  376. package/src/core/Loader/Loader.module.css +57 -0
  377. package/src/core/Loader/Loader.story.tsx +8 -0
  378. package/src/core/Loader/index.tsx +27 -0
  379. package/src/core/MeActions/MeActions.module.css +45 -0
  380. package/src/core/MeActions/index.tsx +46 -0
  381. package/src/core/MePanel/MePanel.module.css +75 -0
  382. package/src/core/MePanel/index.tsx +45 -0
  383. package/src/core/Modal/Modal.module.css +84 -0
  384. package/src/core/Modal/Modal.spec.tsx +51 -0
  385. package/src/core/Modal/index.tsx +66 -0
  386. package/src/core/Navigation/Navigation.module.css +83 -0
  387. package/src/core/Navigation/Navigation.spec.tsx +101 -0
  388. package/src/core/Navigation/index.tsx +109 -0
  389. package/src/core/NavigationDivider/NavigationDivider.module.css +18 -0
  390. package/src/core/NavigationDivider/index.tsx +13 -0
  391. package/src/core/NavigationItem/NavigationItem.module.css +51 -0
  392. package/src/core/NavigationItem/index.tsx +59 -0
  393. package/src/core/Pagination/Pagination.module.css +15 -0
  394. package/src/core/Pagination/index.tsx +100 -0
  395. package/src/core/PaginationItem/PaginationItem.module.css +33 -0
  396. package/src/core/PaginationItem/index.tsx +46 -0
  397. package/src/core/Panel/Panel.module.css +15 -0
  398. package/src/core/Panel/index.tsx +31 -0
  399. package/src/core/RadialProgress/RadialProgress.module.css +78 -0
  400. package/src/core/RadialProgress/index.tsx +52 -0
  401. package/src/core/StaticTextInput/StaticTextInput.module.css +79 -0
  402. package/src/core/StaticTextInput/StaticTextInput.spec.tsx +133 -0
  403. package/src/core/StaticTextInput/index.tsx +114 -0
  404. package/src/core/Styled/Styled.spec.tsx +38 -0
  405. package/src/core/Styled/Styled.story.tsx +10 -0
  406. package/src/core/Styled/index.tsx +62 -0
  407. package/src/core/SummaryListItem/SummaryListItem.module.css +28 -0
  408. package/src/core/SummaryListItem/index.tsx +60 -0
  409. package/src/core/TableActionsCell/TableActionsCell.module.css +3 -0
  410. package/src/core/TableActionsCell/index.tsx +37 -0
  411. package/src/core/TableCell/TableCell.module.css +16 -0
  412. package/src/core/TableCell/index.tsx +39 -0
  413. package/src/core/TableHead/TableHead.module.css +12 -0
  414. package/src/core/TableHead/index.tsx +48 -0
  415. package/src/core/TableRow/index.tsx +64 -0
  416. package/src/core/Tabs/Tabs.module.css +50 -0
  417. package/src/core/Tabs/index.tsx +94 -0
  418. package/src/core/TextInputAncillary/TextInputAncillary.module.css +64 -0
  419. package/src/core/TextInputAncillary/index.tsx +72 -0
  420. package/src/core/Toast/Toast.module.css +55 -0
  421. package/src/core/Toast/Toast.spec.tsx +78 -0
  422. package/src/core/Toast/index.tsx +81 -0
  423. package/src/core/WeekCalendarDay/WeekCalendarDay.module.css +32 -0
  424. package/src/core/WeekCalendarDay/index.tsx +68 -0
  425. package/src/filters/BooleanFilter/index.tsx +46 -0
  426. package/src/filters/DateFilter/index.tsx +36 -0
  427. package/src/filters/DropdownFilter/index.tsx +53 -0
  428. package/src/filters/QuickDateFilter/index.tsx +74 -0
  429. package/src/filters/interfaces.ts +40 -0
  430. package/src/hooks/useAccessiblePanel.spec.tsx +88 -0
  431. package/src/hooks/useAccessiblePanel.ts +74 -0
  432. package/src/hooks/useAppContext.ts +77 -0
  433. package/src/hooks/useClickOutside.ts +49 -0
  434. package/src/hooks/useConnectedList.ts +26 -0
  435. package/src/hooks/useFilters.ts +26 -0
  436. package/src/hooks/useForm.spec.ts +126 -0
  437. package/src/hooks/useForm.ts +99 -0
  438. package/src/hooks/useFormData.spec.ts +40 -0
  439. package/src/hooks/useFormData.ts +27 -0
  440. package/src/hooks/useLayout.spec.ts +55 -0
  441. package/src/hooks/useLayout.ts +29 -0
  442. package/src/hooks/useModal.spec.tsx +63 -0
  443. package/src/hooks/useModal.ts +60 -0
  444. package/src/hooks/useVariant.spec.ts +14 -0
  445. package/src/hooks/useVariant.ts +34 -0
  446. package/src/index.ts +194 -0
  447. package/src/scenes/Error404Scene.tsx +5 -0
  448. package/src/services/BuildService.spec.ts +56 -0
  449. package/src/services/BuildService.ts +34 -0
  450. package/src/services/ConfirmService.ts +21 -0
  451. package/src/services/DateService.spec.ts +87 -0
  452. package/src/services/DateService.ts +137 -0
  453. package/src/services/DownloadService.spec.ts +29 -0
  454. package/src/services/DownloadService.ts +14 -0
  455. package/src/services/FilterComponentService.tsx +46 -0
  456. package/src/services/FilterService.spec.ts +87 -0
  457. package/src/services/FilterService.ts +58 -0
  458. package/src/services/FormatService.spec.ts +83 -0
  459. package/src/services/FormatService.ts +55 -0
  460. package/src/services/ToastService.spec.ts +44 -0
  461. package/src/services/ToastService.ts +19 -0
  462. package/src/styles/layout.css +64 -0
  463. package/src/styles/variables.css +85 -0
  464. package/src/typings.d.ts +4 -0
  465. package/src/utility/MockRouter/index.ts +45 -0
  466. package/src/utility/StorybookPanel/StorybookPanel.module.css +9 -0
  467. package/src/utility/StorybookPanel/index.tsx +25 -0
  468. package/src/utility/TestChangeHandler/TestChangeHandler.module.css +7 -0
  469. package/src/utility/TestChangeHandler/TestChangeHandler.spec.tsx +24 -0
  470. package/src/utility/TestChangeHandler/index.tsx +54 -0
  471. package/src/utility/create-app.tsx +29 -0
  472. package/src/utility/interfaces.ts +119 -0
  473. package/src/utility/prop-types.ts +67 -0
  474. package/tests/setupFilesAfterEnv.js +10 -0
  475. package/tsconfig.json +26 -0
  476. package/tsconfig.types.json +9 -0
@@ -0,0 +1,533 @@
1
+ /* eslint-disable max-lines */
2
+
3
+ import React from 'react';
4
+ import { render, screen, waitFor } from '@testing-library/react';
5
+ import MockDate from 'mockdate';
6
+ import userEvent from '@testing-library/user-event';
7
+ import fetch from 'jest-fetch-mock';
8
+
9
+ import FilterComponentService from '@services/FilterComponentService';
10
+
11
+ import Filters from './index';
12
+
13
+ const mockRouterPush = jest.fn();
14
+
15
+ jest.mock('next/router', () => ({
16
+ useRouter: (): { push: jest.Mock } => ({ push: mockRouterPush }),
17
+ }));
18
+
19
+ describe('Filters', () => {
20
+ const { location } = window;
21
+
22
+ beforeEach(() => {
23
+ MockDate.reset();
24
+ fetch.resetMocks();
25
+ mockRouterPush.mockClear();
26
+ });
27
+
28
+ afterEach(() => {
29
+ window.location = location;
30
+ });
31
+
32
+ it('should render', () => {
33
+ render(<Filters />);
34
+ expect(screen.getByTestId('filters-root')).toBeInTheDocument();
35
+ });
36
+
37
+ it('should open and close', () => {
38
+ render(<Filters />);
39
+
40
+ userEvent.click(screen.getByText('Filters...'));
41
+ expect(screen.getByTestId('filters-content')).toHaveClass('contentIsOpen');
42
+
43
+ userEvent.click(screen.getByText('Filters...'));
44
+ expect(screen.getByTestId('filters-content')).not.toHaveClass('contentIsOpen');
45
+ });
46
+
47
+ it('should have filters', () => {
48
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
49
+ delete window.location;
50
+ window.location = { ...location, search: '?lorem=ipsum' };
51
+
52
+ render(<Filters />);
53
+ expect(screen.getAllByRole('button')[0]).toHaveTextContent(/^Filters \(1\)$/u);
54
+ });
55
+
56
+ it('should have search', () => {
57
+ render(<Filters hasSearch />);
58
+ expect(screen.getByRole('textbox')).toBeInTheDocument();
59
+ });
60
+
61
+ it('should handle search', () => {
62
+ render(<Filters hasSearch />);
63
+
64
+ userEvent.type(screen.getByRole('textbox'), 'lorem');
65
+ expect(screen.getByRole('textbox')).toHaveValue('lorem');
66
+
67
+ userEvent.type(screen.getByRole('textbox'), '{enter}');
68
+ expect(mockRouterPush).toHaveBeenCalledTimes(1);
69
+ expect(mockRouterPush).toHaveBeenLastCalledWith('/?search=lorem');
70
+ });
71
+
72
+ it('should handle search clear', () => {
73
+ render(<Filters hasSearch />);
74
+
75
+ userEvent.type(screen.getByRole('textbox'), 'lorem');
76
+ expect(screen.getByRole('textbox')).toHaveValue('lorem');
77
+
78
+ userEvent.click(screen.getByText('Clear'));
79
+ expect(screen.getByRole('textbox')).toHaveValue('');
80
+ expect(mockRouterPush).toHaveBeenCalledTimes(1);
81
+ expect(mockRouterPush).toHaveBeenLastCalledWith('/?search=');
82
+ });
83
+
84
+ it('should handle empty search', () => {
85
+ render(<Filters hasSearch />);
86
+
87
+ userEvent.type(screen.getByRole('textbox'), 'a{enter}{backspace}{enter}');
88
+ expect(screen.getByRole('textbox')).toHaveValue('');
89
+
90
+ expect(mockRouterPush).toHaveBeenCalledTimes(2);
91
+ expect(mockRouterPush).toHaveBeenLastCalledWith('/?search=');
92
+ });
93
+
94
+ it('should set search from the url', () => {
95
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
96
+ delete window.location;
97
+ window.location = { ...location, search: '?search=lorem' };
98
+
99
+ render(<Filters hasSearch />);
100
+ expect(screen.getByRole('textbox')).toHaveValue('lorem');
101
+ });
102
+
103
+ it('should render boolean field', () => {
104
+ const fields = [
105
+ FilterComponentService.boolean({
106
+ label: 'Boolean',
107
+ name: 'boolean',
108
+ data: ['Yes', 'No'],
109
+ }),
110
+ ];
111
+ render(<Filters fields={fields} />);
112
+ expect(screen.getByText('Boolean')).toBeInTheDocument();
113
+
114
+ userEvent.click(screen.getByText('Open'));
115
+ expect(screen.queryAllByRole('option')).toHaveLength(2);
116
+ expect(screen.queryAllByRole('option')[0]).toHaveTextContent('Yes');
117
+ });
118
+
119
+ it('should render date field', () => {
120
+ const fields = [FilterComponentService.date({ label: 'Date', name: 'date' })];
121
+ render(<Filters fields={fields} />);
122
+ expect(screen.getByText('Date')).toBeInTheDocument();
123
+
124
+ userEvent.click(screen.getByText('Open'));
125
+ expect(screen.queryAllByRole('table')).toHaveLength(1);
126
+ });
127
+
128
+ it('should render date summary (non-string)', () => {
129
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
130
+ delete window.location;
131
+ window.location = { ...location, search: '?date[0]=1&date[1]=2' };
132
+
133
+ const fields = [FilterComponentService.date({ label: 'Date', name: 'date' })];
134
+ render(<Filters fields={fields} />);
135
+
136
+ expect(screen.getByTestId('filter-item-label')).toHaveTextContent('Date');
137
+ });
138
+
139
+ it('should render date summary (incorrect format)', () => {
140
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
141
+ delete window.location;
142
+ window.location = { ...location, search: '?date=lorem' };
143
+
144
+ const fields = [FilterComponentService.date({ label: 'Date', name: 'date' })];
145
+ render(<Filters fields={fields} />);
146
+
147
+ expect(screen.getByTestId('filter-item-label')).toHaveTextContent('Date');
148
+ });
149
+
150
+ it('should render date summary (single date)', () => {
151
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
152
+ delete window.location;
153
+ window.location = { ...location, search: '?date=2020-01-01,2020-01-01' };
154
+
155
+ const fields = [FilterComponentService.date({ label: 'Date', name: 'date' })];
156
+ render(<Filters fields={fields} />);
157
+
158
+ expect(screen.getByTestId('filter-item-label')).toHaveTextContent('01/01/2020');
159
+ });
160
+
161
+ it('should render date summary (same year)', () => {
162
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
163
+ delete window.location;
164
+ window.location = { ...location, search: '?date=2020-01-14,2020-03-01' };
165
+
166
+ const fields = [FilterComponentService.date({ label: 'Date', name: 'date' })];
167
+ render(<Filters fields={fields} />);
168
+
169
+ expect(screen.getByTestId('filter-item-label')).toHaveTextContent('14/01 - 01/03');
170
+ });
171
+
172
+ it('should render date summary (different year)', () => {
173
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
174
+ delete window.location;
175
+ window.location = { ...location, search: '?date=2019-01-14,2020-03-01' };
176
+
177
+ const fields = [FilterComponentService.date({ label: 'Date', name: 'date' })];
178
+ render(<Filters fields={fields} />);
179
+
180
+ expect(screen.getByTestId('filter-item-label')).toHaveTextContent(
181
+ '14/01/2019 - 01/03/2020'
182
+ );
183
+ });
184
+
185
+ it('should render dropdown field', () => {
186
+ const fields = [
187
+ FilterComponentService.dropdown({
188
+ label: 'Dropdown',
189
+ name: 'dropdown',
190
+ data: [
191
+ { id: 1, name: 'Lorem' },
192
+ { id: 2, name: 'Ipsum' },
193
+ ],
194
+ }),
195
+ ];
196
+ render(<Filters fields={fields} />);
197
+ expect(screen.getByText('Dropdown')).toBeInTheDocument();
198
+
199
+ userEvent.click(screen.getByText('Open'));
200
+ expect(screen.queryAllByRole('option')).toHaveLength(2);
201
+ expect(screen.queryAllByRole('option')[0]).toHaveTextContent('Lorem');
202
+ });
203
+
204
+ it('should render dropdown field (incorrect data format)', () => {
205
+ const fields = [
206
+ FilterComponentService.dropdown({
207
+ label: 'Dropdown',
208
+ name: 'dropdown',
209
+ data: ['lorem', 'ipsum'],
210
+ }),
211
+ ];
212
+ render(<Filters fields={fields} />);
213
+ expect(screen.getByText('Dropdown')).toBeInTheDocument();
214
+
215
+ userEvent.click(screen.getByText('Open'));
216
+ expect(screen.queryAllByRole('option')).toHaveLength(0);
217
+ });
218
+
219
+ it('should render dropdown summary', () => {
220
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
221
+ delete window.location;
222
+ window.location = { ...location, search: '?dropdown=1' };
223
+
224
+ const fields = [
225
+ FilterComponentService.dropdown({
226
+ label: 'Dropdown',
227
+ name: 'dropdown',
228
+ data: [
229
+ { id: 1, name: 'Lorem' },
230
+ { id: 2, name: 'Ipsum' },
231
+ ],
232
+ }),
233
+ ];
234
+ render(<Filters fields={fields} />);
235
+
236
+ expect(screen.getByTestId('filter-item-label')).toHaveTextContent('Lorem');
237
+ });
238
+
239
+ it('should render dropdown summary (incorrect data format)', () => {
240
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
241
+ delete window.location;
242
+ window.location = { ...location, search: '?dropdown=1' };
243
+
244
+ const fields = [
245
+ FilterComponentService.dropdown({
246
+ label: 'Dropdown',
247
+ name: 'dropdown',
248
+ data: ['lorem', 'ipsum'],
249
+ }),
250
+ ];
251
+ render(<Filters fields={fields} />);
252
+
253
+ expect(screen.getByTestId('filter-item-label')).toHaveTextContent('Dropdown');
254
+ });
255
+
256
+ it('should render quickDate field', () => {
257
+ const fields = [
258
+ FilterComponentService.quickDate({ label: 'Quick Date', name: 'quickDate' }),
259
+ ];
260
+ render(<Filters fields={fields} />);
261
+ expect(screen.getByText('Quick Date')).toBeInTheDocument();
262
+
263
+ userEvent.click(screen.getByText('Open'));
264
+ expect(screen.queryAllByRole('option')).toHaveLength(7);
265
+ expect(screen.queryAllByRole('option')[0]).toHaveTextContent('Today');
266
+ });
267
+
268
+ it('should handle change on quickDate field (defined)', () => {
269
+ MockDate.set('2020-09-19');
270
+
271
+ const fields = [
272
+ FilterComponentService.quickDate({ label: 'Quick Date', name: 'quickDate' }),
273
+ ];
274
+ render(<Filters fields={fields} />);
275
+
276
+ userEvent.click(screen.getByText('Open'));
277
+
278
+ userEvent.click(screen.getByTestId('dropdown-panel-control-2020-09-19,2020-09-19'));
279
+
280
+ expect(mockRouterPush).toHaveBeenCalledTimes(1);
281
+ expect(mockRouterPush).toHaveBeenLastCalledWith('/?quickDate=2020-09-19%2C2020-09-19');
282
+ });
283
+
284
+ it('should handle change on quickDate field (custom)', () => {
285
+ const fields = [
286
+ FilterComponentService.quickDate({ label: 'Quick Date', name: 'quickDate' }),
287
+ ];
288
+ render(<Filters fields={fields} />);
289
+
290
+ userEvent.click(screen.getByText('Open'));
291
+
292
+ expect(screen.getByTestId('quick-date-filter-dropdown')).toHaveStyle('display: block');
293
+ expect(screen.getByTestId('quick-date-filter-date-filter')).toHaveStyle('display: none');
294
+
295
+ userEvent.click(screen.getByTestId('dropdown-panel-control-[internal]'));
296
+
297
+ expect(screen.getByTestId('quick-date-filter-dropdown')).toHaveStyle('display: none');
298
+ expect(screen.getByTestId('quick-date-filter-date-filter')).toHaveStyle('display: block');
299
+ });
300
+
301
+ it('should render quickDate summary (non-string)', () => {
302
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
303
+ delete window.location;
304
+ window.location = { ...location, search: '?quickDate[0]=1&quickDate[1]=2' };
305
+
306
+ const fields = [
307
+ FilterComponentService.quickDate({ label: 'Quick Date', name: 'quickDate' }),
308
+ ];
309
+ render(<Filters fields={fields} />);
310
+
311
+ expect(screen.getByTestId('filter-item-label')).toHaveTextContent('Quick Date');
312
+ });
313
+
314
+ it('should render quickDate summary (existing definition)', () => {
315
+ MockDate.set('2020-09-19');
316
+
317
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
318
+ delete window.location;
319
+ window.location = { ...location, search: '?quickDate=2020-09-19,2020-09-19' };
320
+
321
+ const fields = [
322
+ FilterComponentService.quickDate({ label: 'Quick Date', name: 'quickDate' }),
323
+ ];
324
+ render(<Filters fields={fields} />);
325
+
326
+ expect(screen.getByTestId('filter-item-label')).toHaveTextContent('Today');
327
+ });
328
+
329
+ it('should render quickDate summary (non-existing definition)', () => {
330
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
331
+ delete window.location;
332
+ window.location = { ...location, search: '?quickDate=2019-09-19,2019-09-24' };
333
+
334
+ const fields = [
335
+ FilterComponentService.quickDate({ label: 'Quick Date', name: 'quickDate' }),
336
+ ];
337
+ render(<Filters fields={fields} />);
338
+
339
+ expect(screen.getByTestId('filter-item-label')).toHaveTextContent('19/09 - 24/09');
340
+ });
341
+
342
+ it('should render remote', async () => {
343
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
344
+ delete window.location;
345
+ window.location = { ...location, search: '?dropdown=1' };
346
+
347
+ const mockFn = fetch.mockResponseOnce(JSON.stringify({ data: [{ id: 1, name: 'Lorem' }] }));
348
+
349
+ const fields = [
350
+ FilterComponentService.dropdown({
351
+ label: 'Dropdown',
352
+ name: 'dropdown',
353
+ remote: 'lorem',
354
+ }),
355
+ ];
356
+
357
+ render(<Filters fields={fields} />);
358
+
359
+ expect(mockFn).toHaveBeenCalledTimes(1);
360
+ expect(mockFn).toHaveBeenLastCalledWith(
361
+ '/lorem?simpleview=1',
362
+ expect.objectContaining({ body: null })
363
+ );
364
+
365
+ expect(screen.getByTestId('filter-item-root')).toHaveClass('rootIsDisabled');
366
+ expect(screen.getByText('Dropdown')).toBeInTheDocument();
367
+
368
+ await waitFor(() => {
369
+ expect(screen.getByTestId('filter-item-root')).not.toHaveClass('rootIsDisabled');
370
+ });
371
+
372
+ expect(screen.getByText('Lorem')).toBeInTheDocument();
373
+ });
374
+
375
+ it('should render remote with existing params', async () => {
376
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
377
+ delete window.location;
378
+ window.location = { ...location, search: '?dropdown=1' };
379
+
380
+ const mockFn = fetch.mockResponseOnce(JSON.stringify({ data: [{ id: 1, name: 'Lorem' }] }));
381
+
382
+ const fields = [
383
+ FilterComponentService.dropdown({
384
+ label: 'Dropdown',
385
+ name: 'dropdown',
386
+ remote: ['lorem', { ipsum: 'ipsum' }],
387
+ }),
388
+ ];
389
+
390
+ render(<Filters fields={fields} />);
391
+
392
+ await waitFor(() => {
393
+ expect(mockFn).toHaveBeenCalledTimes(1);
394
+ });
395
+
396
+ expect(mockFn).toHaveBeenLastCalledWith(
397
+ '/lorem?ipsum=ipsum&simpleview=1',
398
+ expect.objectContaining({ body: null })
399
+ );
400
+ });
401
+
402
+ it('should render custom filter', () => {
403
+ const fields = [
404
+ {
405
+ label: 'Custom',
406
+ name: 'custom',
407
+ renderField: (): React.ReactNode => <div>Lorem</div>,
408
+ },
409
+ ];
410
+
411
+ render(<Filters fields={fields} />);
412
+
413
+ expect(screen.getByText('Custom')).toBeInTheDocument();
414
+
415
+ userEvent.click(screen.getByText('Open'));
416
+ expect(screen.getByText('Lorem')).toBeInTheDocument();
417
+ });
418
+
419
+ it('should render custom filter with value', () => {
420
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
421
+ delete window.location;
422
+ window.location = { ...location, search: '?custom=ipsum' };
423
+
424
+ const fields = [
425
+ {
426
+ label: 'Custom',
427
+ name: 'custom',
428
+ renderField: (): React.ReactNode => <div>Lorem</div>,
429
+ },
430
+ ];
431
+
432
+ render(<Filters fields={fields} />);
433
+
434
+ expect(screen.getByText('ipsum')).toBeInTheDocument();
435
+ });
436
+
437
+ it('should handle filter change', () => {
438
+ const fields = [
439
+ FilterComponentService.boolean({
440
+ label: 'Boolean',
441
+ name: 'boolean',
442
+ data: ['Yes', 'No'],
443
+ }),
444
+ ];
445
+ render(<Filters fields={fields} />);
446
+
447
+ userEvent.click(screen.getByText('Open'));
448
+ userEvent.click(screen.getAllByText('Select')[0]);
449
+
450
+ expect(mockRouterPush).toHaveBeenCalledTimes(1);
451
+ expect(mockRouterPush).toHaveBeenLastCalledWith('/?boolean=1');
452
+ });
453
+
454
+ it('should handle filter cancel', () => {
455
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
456
+ delete window.location;
457
+ window.location = { ...location, search: '?boolean=1' };
458
+
459
+ const fields = [
460
+ FilterComponentService.boolean({
461
+ label: 'Boolean',
462
+ name: 'boolean',
463
+ data: ['Yes', 'No'],
464
+ }),
465
+ ];
466
+ render(<Filters fields={fields} />);
467
+
468
+ userEvent.click(screen.getByText('Cancel'));
469
+
470
+ expect(mockRouterPush).toHaveBeenCalledTimes(1);
471
+ expect(mockRouterPush).toHaveBeenLastCalledWith('/?');
472
+ });
473
+
474
+ it('should handle array values', () => {
475
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
476
+ delete window.location;
477
+ window.location = { ...location, search: '?dropdown[]=1' };
478
+
479
+ const fields = [
480
+ FilterComponentService.dropdown({
481
+ label: 'Dropdown',
482
+ name: 'dropdown',
483
+ data: [
484
+ { id: 1, name: 'Lorem' },
485
+ { id: 2, name: 'Ipsum' },
486
+ ],
487
+ props: { isMultiple: true },
488
+ }),
489
+ ];
490
+ render(<Filters fields={fields} />);
491
+
492
+ userEvent.click(screen.getByText('Open'));
493
+ userEvent.click(screen.getAllByText('Select')[1]);
494
+
495
+ expect(mockRouterPush).toHaveBeenCalledTimes(1);
496
+ expect(mockRouterPush).toHaveBeenLastCalledWith(encodeURI('/?dropdown[0]=1&dropdown[1]=2'));
497
+ });
498
+
499
+ it('should render array values count', () => {
500
+ // @ts-expect-error The operand of a 'delete' operator must be optional.
501
+ delete window.location;
502
+ window.location = { ...location, search: '?custom[0]=1&custom[1]=2' };
503
+
504
+ const fields = [
505
+ {
506
+ label: 'Custom',
507
+ name: 'custom',
508
+ renderField: (): React.ReactNode => <div>Lorem</div>,
509
+ },
510
+ ];
511
+
512
+ render(<Filters fields={fields} />);
513
+ expect(screen.getByText('2')).toBeInTheDocument();
514
+ });
515
+
516
+ it('should render divider', () => {
517
+ const fields = [FilterComponentService.boolean({ label: 'Boolean', name: 'boolean' })];
518
+ render(<Filters fields={fields} hasSearch />);
519
+
520
+ expect(screen.getByTestId('filters-divider')).toBeInTheDocument();
521
+ });
522
+
523
+ it('should render actions', () => {
524
+ const mockFn = jest.fn();
525
+ const actions = [{ label: 'Lorem', onClick: mockFn }];
526
+
527
+ render(<Filters actions={actions} />);
528
+ expect(screen.getByText('Lorem')).toBeInTheDocument();
529
+
530
+ userEvent.click(screen.getByText('Lorem'));
531
+ expect(mockFn).toHaveBeenCalledTimes(1);
532
+ });
533
+ });
@@ -0,0 +1,158 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import classNames from 'classnames';
3
+ import PropTypes from 'prop-types';
4
+
5
+ import Button from '@components/Button';
6
+ import TextInput from '@components/TextInput';
7
+
8
+ import FilterItem from '@core/FilterItem';
9
+
10
+ import useFilters from '@hooks/useFilters';
11
+
12
+ import type { FilterField } from '@filters/interfaces';
13
+ import type { SimpleAction, SimpleEvent } from '@utility/interfaces';
14
+
15
+ import styles from './Filters.module.css';
16
+
17
+ interface FiltersProps {
18
+ actions?: SimpleAction[];
19
+ fields?: FilterField[];
20
+ hasSearch?: boolean;
21
+ isDisabled?: boolean;
22
+ }
23
+
24
+ const Filters: React.FunctionComponent<FiltersProps> = ({
25
+ actions,
26
+ fields,
27
+ hasSearch,
28
+ isDisabled,
29
+ }) => {
30
+ const [filters, setFilters] = useFilters({ shouldFilterPage: true });
31
+ const [pendingSearch, setPendingSearch] = useState('');
32
+
33
+ const [isOpen, setIsOpen] = useState(false);
34
+
35
+ const toggleIsOpen = (): void => setIsOpen(!isOpen);
36
+
37
+ useEffect(() => {
38
+ setPendingSearch(filters.get('search')?.toString() || '');
39
+ }, [filters.get('search')]);
40
+
41
+ const handleCancel = (event: SimpleEvent): void => {
42
+ const newFilters = new Map(filters);
43
+
44
+ newFilters.delete(event.target.value as string);
45
+ setFilters(newFilters);
46
+ };
47
+
48
+ const handleChange = (event: SimpleEvent): void => {
49
+ const newFilters = new Map(filters);
50
+
51
+ /*
52
+ * `event.target.value` can be null, so the optional check (?.) is required for typescript.
53
+ * However, no null value can be passed from a filter, so this can be ignored by test
54
+ * coverage.
55
+ */
56
+ /* istanbul ignore next */
57
+ const value = Array.isArray(event.target.value)
58
+ ? event.target.value.map((item) => item?.toString() || '')
59
+ : event.target.value?.toString() || '';
60
+
61
+ newFilters.set(event.target.name, value);
62
+ setFilters(newFilters);
63
+ };
64
+
65
+ const handleSearch = (event: React.KeyboardEvent): void => {
66
+ if (event.key === 'Enter') {
67
+ handleChange({ target: { name: 'search', value: pendingSearch } });
68
+ }
69
+ };
70
+
71
+ const handleSearchClear = (): void => {
72
+ handleChange({ target: { name: 'search', value: '' } });
73
+ setPendingSearch('');
74
+ };
75
+
76
+ return (
77
+ <aside className={styles.root} data-testid="filters-root">
78
+ <div className={classNames(styles.control, isOpen && styles.controlIsOpen)}>
79
+ <Button onClick={toggleIsOpen} variant="bordered">
80
+ Filters{filters.size ? ` (${filters.size})` : '...'}
81
+ </Button>
82
+ </div>
83
+ <div
84
+ className={classNames(styles.content, isOpen && styles.contentIsOpen)}
85
+ data-testid="filters-content"
86
+ >
87
+ <div className={styles.section}>
88
+ {hasSearch && (
89
+ <div className={styles.search}>
90
+ <TextInput
91
+ name="search"
92
+ onChange={(event): void =>
93
+ setPendingSearch(event.target.value as string)
94
+ }
95
+ onClear={handleSearchClear}
96
+ onKeyDown={handleSearch}
97
+ placeholder="Search..."
98
+ value={pendingSearch}
99
+ isClearable
100
+ />
101
+ </div>
102
+ )}
103
+ {hasSearch && fields?.length && (
104
+ <i className={styles.divider} data-testid="filters-divider" />
105
+ )}
106
+ {fields?.map((item) => (
107
+ <FilterItem
108
+ key={item.label}
109
+ onCancel={handleCancel}
110
+ onChange={handleChange}
111
+ value={filters.get(item.name)}
112
+ {...item}
113
+ />
114
+ ))}
115
+ </div>
116
+ {actions?.length && (
117
+ <div className={styles.actions}>
118
+ {actions.map((item) => (
119
+ <Button
120
+ key={item.label}
121
+ isDisabled={isDisabled || item.isDisabled}
122
+ onClick={item.onClick}
123
+ variant={item.variant || 'bordered'}
124
+ >
125
+ {item.label}
126
+ </Button>
127
+ ))}
128
+ </div>
129
+ )}
130
+ </div>
131
+ <div className={classNames(styles.close, isOpen && styles.closeIsOpen)}>
132
+ <Button onClick={toggleIsOpen} variant="link">
133
+ Close Filters
134
+ </Button>
135
+ </div>
136
+ </aside>
137
+ );
138
+ };
139
+
140
+ Filters.propTypes = {
141
+ actions: PropTypes.arrayOf(
142
+ PropTypes.shape({
143
+ label: PropTypes.string.isRequired,
144
+ onClick: PropTypes.func.isRequired,
145
+ }).isRequired
146
+ ),
147
+ fields: PropTypes.arrayOf(
148
+ PropTypes.shape({
149
+ label: PropTypes.string.isRequired,
150
+ name: PropTypes.string.isRequired,
151
+ renderField: PropTypes.func.isRequired,
152
+ }).isRequired
153
+ ),
154
+ hasSearch: PropTypes.bool,
155
+ isDisabled: PropTypes.bool,
156
+ };
157
+
158
+ export default Filters;