@luscii-healthtech/web-ui 0.1.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 (311) hide show
  1. package/README.md +181 -0
  2. package/dist/components/Acknowledgement/Acknowledgement.d.ts +22 -0
  3. package/dist/components/Avatar/Avatar.d.ts +29 -0
  4. package/dist/components/Badge/Badge.d.ts +7 -0
  5. package/dist/components/Button/Button.d.ts +5 -0
  6. package/dist/components/Button/Button.types.d.ts +32 -0
  7. package/dist/components/Button/ButtonIcon.d.ts +7 -0
  8. package/dist/components/ButtonV2/ButtonV2.d.ts +13 -0
  9. package/dist/components/ButtonV2/PrimaryButton.d.ts +12 -0
  10. package/dist/components/ButtonV2/SecondaryButton.d.ts +8 -0
  11. package/dist/components/ButtonV2/TertiaryButton.d.ts +8 -0
  12. package/dist/components/Carousel/Carousel.d.ts +8 -0
  13. package/dist/components/Carousel/GliderContainer.d.ts +7 -0
  14. package/dist/components/CenteredHero/CenteredHero.d.ts +22 -0
  15. package/dist/components/Checkbox/Checkbox.d.ts +17 -0
  16. package/dist/components/ConfirmationDialog/ConfirmationDialog.d.ts +33 -0
  17. package/dist/components/Datepicker/Datepicker.d.ts +24 -0
  18. package/dist/components/Dropdown/Dropdown.d.ts +47 -0
  19. package/dist/components/EmptyListMessage/EmptyListMessage.d.ts +14 -0
  20. package/dist/components/ErrorBlock/ErrorBlock.d.ts +13 -0
  21. package/dist/components/Icons/AddIcon.d.ts +3 -0
  22. package/dist/components/Icons/AlertsIcon.d.ts +6 -0
  23. package/dist/components/Icons/BellIcon.d.ts +6 -0
  24. package/dist/components/Icons/ChartIcon.d.ts +3 -0
  25. package/dist/components/Icons/ChatBox.d.ts +5 -0
  26. package/dist/components/Icons/CheckIcon.d.ts +2 -0
  27. package/dist/components/Icons/ChevronIcon.d.ts +8 -0
  28. package/dist/components/Icons/CrossIcon.d.ts +7 -0
  29. package/dist/components/Icons/DeleteIcon.d.ts +6 -0
  30. package/dist/components/Icons/DownArrowIcon.d.ts +6 -0
  31. package/dist/components/Icons/DragIcon.d.ts +3 -0
  32. package/dist/components/Icons/EditIcon.d.ts +6 -0
  33. package/dist/components/Icons/EmptyStateDashboardIcon.d.ts +2 -0
  34. package/dist/components/Icons/ExclamationMarkIcon.d.ts +6 -0
  35. package/dist/components/Icons/EyeIcon.d.ts +3 -0
  36. package/dist/components/Icons/GearIcon.d.ts +3 -0
  37. package/dist/components/Icons/GroupIcon.d.ts +3 -0
  38. package/dist/components/Icons/HeartIcon.d.ts +6 -0
  39. package/dist/components/Icons/LeftArrowIcon.d.ts +6 -0
  40. package/dist/components/Icons/LightBulbIcon.d.ts +6 -0
  41. package/dist/components/Icons/LockIcon.d.ts +6 -0
  42. package/dist/components/Icons/MessagesIcon.d.ts +6 -0
  43. package/dist/components/Icons/NotesIcon.d.ts +6 -0
  44. package/dist/components/Icons/PinIcon.d.ts +6 -0
  45. package/dist/components/Icons/PrintIcon.d.ts +3 -0
  46. package/dist/components/Icons/RightArrowIcon.d.ts +6 -0
  47. package/dist/components/Icons/SmallCircleIcon.d.ts +6 -0
  48. package/dist/components/Icons/SmallDiamondIcon.d.ts +6 -0
  49. package/dist/components/Icons/SmallSquareIcon.d.ts +6 -0
  50. package/dist/components/Icons/SpaceRocketIcon.d.ts +6 -0
  51. package/dist/components/Icons/types/IconProps.type.d.ts +3 -0
  52. package/dist/components/InfoBlock/InfoBlock.d.ts +14 -0
  53. package/dist/components/InfoField/InfoField.d.ts +18 -0
  54. package/dist/components/Input/Input.d.ts +44 -0
  55. package/dist/components/Line/Line.d.ts +14 -0
  56. package/dist/components/ListItem/ListItem.d.ts +9 -0
  57. package/dist/components/ListTable/ListTable.d.ts +35 -0
  58. package/dist/components/ListTable/ListTableCell.d.ts +10 -0
  59. package/dist/components/ListTable/ListTableHeader.d.ts +7 -0
  60. package/dist/components/ListTable/ListTableRow.d.ts +9 -0
  61. package/dist/components/LoadingIndicator/LoadingIndicator.d.ts +11 -0
  62. package/dist/components/Menu/Menu.d.ts +14 -0
  63. package/dist/components/Modal/Modal.d.ts +31 -0
  64. package/dist/components/Modal/ModalWithButtons.d.ts +9 -0
  65. package/dist/components/MultiSelect/MultiSelect.d.ts +25 -0
  66. package/dist/components/MultiSelect/MultiSelectUtils.d.ts +1 -0
  67. package/dist/components/NavMenu/NavLayout.d.ts +20 -0
  68. package/dist/components/NavMenu/NavMenu.d.ts +1 -0
  69. package/dist/components/NavMenu/NavMenuContent.d.ts +7 -0
  70. package/dist/components/NavMenu/NavMenuItem.d.ts +15 -0
  71. package/dist/components/NotificationBanner/NotificationBanner.d.ts +22 -0
  72. package/dist/components/Page/CRUDPage.d.ts +43 -0
  73. package/dist/components/Page/Page.d.ts +58 -0
  74. package/dist/components/PaginationMenu/PaginationMenu.d.ts +20 -0
  75. package/dist/components/PaginationMenu/PaginationMenuLarge.d.ts +13 -0
  76. package/dist/components/PaginationMenu/PaginationMenuSmall.d.ts +8 -0
  77. package/dist/components/PreviewPhone/PreviewPhone.d.ts +7 -0
  78. package/dist/components/PreviewPhone/useWindowDimensions.d.ts +4 -0
  79. package/dist/components/Radio/Radio.d.ts +18 -0
  80. package/dist/components/RadioGroup/RadioGroup.d.ts +31 -0
  81. package/dist/components/Section/Section.d.ts +15 -0
  82. package/dist/components/Select/LegacySelect.d.ts +32 -0
  83. package/dist/components/Select/Select.d.ts +6 -0
  84. package/dist/components/Select/options.transformer.d.ts +14 -0
  85. package/dist/components/Select/select.utils.d.ts +7 -0
  86. package/dist/components/SettingsMenuButton/SettingsMenuButton.d.ts +16 -0
  87. package/dist/components/Spinner/Spinner.d.ts +6 -0
  88. package/dist/components/Steps/Step.d.ts +10 -0
  89. package/dist/components/Steps/Steps.d.ts +11 -0
  90. package/dist/components/Switcher/Switcher.d.ts +26 -0
  91. package/dist/components/Switcher/SwitcherItem.d.ts +24 -0
  92. package/dist/components/TabLinks/TabLinks.d.ts +14 -0
  93. package/dist/components/Tabbar/Tabbar.d.ts +10 -0
  94. package/dist/components/Tabbar/TabbarItem.d.ts +15 -0
  95. package/dist/components/Tag/Tag.d.ts +17 -0
  96. package/dist/components/Tag/TagGroup.d.ts +13 -0
  97. package/dist/components/Text/LegacyText.d.ts +37 -0
  98. package/dist/components/Text/Text.d.ts +27 -0
  99. package/dist/components/TextEditor/TextEditor.d.ts +14 -0
  100. package/dist/components/TextEditorV2/TextEditorV2.d.ts +6 -0
  101. package/dist/components/TextLink/TextLink.d.ts +11 -0
  102. package/dist/components/TextListItem/TextListItem.d.ts +12 -0
  103. package/dist/components/Textarea/Textarea.d.ts +22 -0
  104. package/dist/components/Title/LegacyTitle.d.ts +22 -0
  105. package/dist/components/Title/Title.d.ts +14 -0
  106. package/dist/components/ViewItem/ViewItem.d.ts +16 -0
  107. package/dist/index.d.ts +8 -0
  108. package/dist/index.js +8 -0
  109. package/dist/types/general.types.d.ts +7 -0
  110. package/dist/utils/useOutsideClick.d.ts +2 -0
  111. package/dist/web-ui.cjs.development.js +23 -0
  112. package/dist/web-ui.cjs.development.js.map +1 -0
  113. package/dist/web-ui.cjs.production.min.js +2 -0
  114. package/dist/web-ui.cjs.production.min.js.map +1 -0
  115. package/dist/web-ui.esm.js +17 -0
  116. package/dist/web-ui.esm.js.map +1 -0
  117. package/package.json +122 -0
  118. package/src/assets/add.svg +5 -0
  119. package/src/assets/add_hover.svg +4 -0
  120. package/src/assets/big-menu-icon-hover.svg +6 -0
  121. package/src/assets/big-menu-icon.svg +6 -0
  122. package/src/assets/check-cross-icon.svg +7 -0
  123. package/src/assets/check-icon-primary.svg +5 -0
  124. package/src/assets/check-icon.svg +3 -0
  125. package/src/assets/chevron-double.svg +3 -0
  126. package/src/assets/close.svg +3 -0
  127. package/src/assets/color-variant-cross.svg +3 -0
  128. package/src/assets/cross-dark.svg +3 -0
  129. package/src/assets/delete.svg +4 -0
  130. package/src/assets/delete_hover.svg +4 -0
  131. package/src/assets/edit.svg +6 -0
  132. package/src/assets/edit_hover.svg +6 -0
  133. package/src/assets/error-icon.svg +7 -0
  134. package/src/assets/grid-view-icon-active.svg +6 -0
  135. package/src/assets/grid-view-icon.svg +6 -0
  136. package/src/assets/groups.svg +3 -0
  137. package/src/assets/hamburger.svg +5 -0
  138. package/src/assets/happy-star.svg +9 -0
  139. package/src/assets/hcps.svg +3 -0
  140. package/src/assets/info-icon.svg +6 -0
  141. package/src/assets/left-arrow-blue.svg +3 -0
  142. package/src/assets/left-arrow-grey.svg +3 -0
  143. package/src/assets/list-view-icon-active.svg +3 -0
  144. package/src/assets/list-view-icon.svg +3 -0
  145. package/src/assets/loading.svg +16 -0
  146. package/src/assets/modal-close-icon-active.svg +9 -0
  147. package/src/assets/modal-close-icon.svg +9 -0
  148. package/src/assets/no-open-alerts.svg +19 -0
  149. package/src/assets/patients.svg +3 -0
  150. package/src/assets/phone-mockup.svg +9 -0
  151. package/src/assets/programs.svg +3 -0
  152. package/src/assets/right-arrow-blue.svg +3 -0
  153. package/src/assets/right-arrow-grey.svg +3 -0
  154. package/src/assets/search-cancel.svg +3 -0
  155. package/src/assets/search-not-found.svg +70 -0
  156. package/src/assets/search.svg +3 -0
  157. package/src/assets/spinner-gray.svg +6 -0
  158. package/src/assets/spinner.svg +6 -0
  159. package/src/assets/starIcon.svg +3 -0
  160. package/src/assets/success-icon.svg +7 -0
  161. package/src/components/Acknowledgement/Acknowledgement.js +61 -0
  162. package/src/components/Acknowledgement/Acknowledgement.scss +49 -0
  163. package/src/components/Avatar/Avatar.js +81 -0
  164. package/src/components/Avatar/Avatar.scss +153 -0
  165. package/src/components/Badge/Badge.tsx +23 -0
  166. package/src/components/Button/Button.examples.md +46 -0
  167. package/src/components/Button/Button.tsx +200 -0
  168. package/src/components/Button/Button.types.ts +41 -0
  169. package/src/components/Button/ButtonIcon.tsx +42 -0
  170. package/src/components/ButtonV2/ButtonV2.tsx +91 -0
  171. package/src/components/ButtonV2/PrimaryButton.tsx +43 -0
  172. package/src/components/ButtonV2/SecondaryButton.tsx +31 -0
  173. package/src/components/ButtonV2/TertiaryButton.tsx +31 -0
  174. package/src/components/Carousel/Carousel.tsx +52 -0
  175. package/src/components/Carousel/GliderContainer.scss +13 -0
  176. package/src/components/Carousel/GliderContainer.tsx +22 -0
  177. package/src/components/CenteredHero/CenteredHero.js +50 -0
  178. package/src/components/Checkbox/Checkbox.scss +115 -0
  179. package/src/components/Checkbox/Checkbox.tsx +114 -0
  180. package/src/components/ConfirmationDialog/ConfirmationDialog.scss +15 -0
  181. package/src/components/ConfirmationDialog/ConfirmationDialog.tsx +84 -0
  182. package/src/components/Datepicker/Datepicker.js +96 -0
  183. package/src/components/Datepicker/Datepicker.scss +331 -0
  184. package/src/components/Dropdown/Dropdown.js +364 -0
  185. package/src/components/Dropdown/Dropdown.scss +83 -0
  186. package/src/components/EmptyListMessage/EmptyListMessage.tsx +34 -0
  187. package/src/components/ErrorBlock/ErrorBlock.js +24 -0
  188. package/src/components/ErrorBlock/ErrorBlock.scss +20 -0
  189. package/src/components/Icons/AddIcon.tsx +27 -0
  190. package/src/components/Icons/AlertsIcon.tsx +26 -0
  191. package/src/components/Icons/BellIcon.tsx +26 -0
  192. package/src/components/Icons/ChartIcon.tsx +20 -0
  193. package/src/components/Icons/ChatBox.tsx +23 -0
  194. package/src/components/Icons/CheckIcon.tsx +23 -0
  195. package/src/components/Icons/ChevronIcon.tsx +30 -0
  196. package/src/components/Icons/CrossIcon.tsx +26 -0
  197. package/src/components/Icons/DeleteIcon.tsx +23 -0
  198. package/src/components/Icons/DownArrowIcon.tsx +17 -0
  199. package/src/components/Icons/DragIcon.tsx +23 -0
  200. package/src/components/Icons/EditIcon.tsx +23 -0
  201. package/src/components/Icons/EmptyStateDashboardIcon.tsx +130 -0
  202. package/src/components/Icons/ExclamationMarkIcon.tsx +23 -0
  203. package/src/components/Icons/EyeIcon.tsx +21 -0
  204. package/src/components/Icons/GearIcon.tsx +21 -0
  205. package/src/components/Icons/GroupIcon.tsx +21 -0
  206. package/src/components/Icons/HeartIcon.tsx +23 -0
  207. package/src/components/Icons/LeftArrowIcon.tsx +23 -0
  208. package/src/components/Icons/LightBulbIcon.tsx +28 -0
  209. package/src/components/Icons/LockIcon.tsx +23 -0
  210. package/src/components/Icons/MessagesIcon.tsx +23 -0
  211. package/src/components/Icons/NotesIcon.tsx +23 -0
  212. package/src/components/Icons/PinIcon.tsx +23 -0
  213. package/src/components/Icons/PrintIcon.tsx +15 -0
  214. package/src/components/Icons/RightArrowIcon.tsx +23 -0
  215. package/src/components/Icons/SmallCircleIcon.tsx +21 -0
  216. package/src/components/Icons/SmallDiamondIcon.tsx +31 -0
  217. package/src/components/Icons/SmallSquareIcon.tsx +21 -0
  218. package/src/components/Icons/SpaceRocketIcon.tsx +23 -0
  219. package/src/components/Icons/types/IconProps.type.ts +3 -0
  220. package/src/components/InfoBlock/InfoBlock.js +24 -0
  221. package/src/components/InfoBlock/InfoBlock.scss +20 -0
  222. package/src/components/InfoField/InfoField.tsx +86 -0
  223. package/src/components/Input/Input.examples.md +94 -0
  224. package/src/components/Input/Input.js +141 -0
  225. package/src/components/Line/Line.js +38 -0
  226. package/src/components/ListItem/ListItem.scss +20 -0
  227. package/src/components/ListItem/ListItem.tsx +26 -0
  228. package/src/components/ListTable/ListTable.tsx +157 -0
  229. package/src/components/ListTable/ListTableCell.tsx +67 -0
  230. package/src/components/ListTable/ListTableHeader.tsx +33 -0
  231. package/src/components/ListTable/ListTableRow.tsx +46 -0
  232. package/src/components/LoadingIndicator/LoadingIndicator.scss +50 -0
  233. package/src/components/LoadingIndicator/LoadingIndicator.tsx +44 -0
  234. package/src/components/Menu/Menu.js +74 -0
  235. package/src/components/Menu/Menu.scss +27 -0
  236. package/src/components/Modal/Modal.scss +117 -0
  237. package/src/components/Modal/Modal.tsx +104 -0
  238. package/src/components/Modal/ModalWithButtons.tsx +34 -0
  239. package/src/components/MultiSelect/MultiSelect.js +117 -0
  240. package/src/components/MultiSelect/MultiSelect.scss +29 -0
  241. package/src/components/MultiSelect/MultiSelectUtils.js +23 -0
  242. package/src/components/NavMenu/NavLayout.tsx +40 -0
  243. package/src/components/NavMenu/NavMenu.js +35 -0
  244. package/src/components/NavMenu/NavMenuContent.tsx +23 -0
  245. package/src/components/NavMenu/NavMenuItem.tsx +96 -0
  246. package/src/components/NotificationBanner/NotificationBanner.tsx +57 -0
  247. package/src/components/Page/CRUDPage.js +123 -0
  248. package/src/components/Page/CRUDPage.scss +32 -0
  249. package/src/components/Page/Page.js +102 -0
  250. package/src/components/Page/Page.scss +59 -0
  251. package/src/components/PaginationMenu/PaginationMenu.js +31 -0
  252. package/src/components/PaginationMenu/PaginationMenuLarge.tsx +94 -0
  253. package/src/components/PaginationMenu/PaginationMenuSmall.tsx +40 -0
  254. package/src/components/PreviewPhone/PreviewPhone.tsx +53 -0
  255. package/src/components/PreviewPhone/useWindowDimensions.js +26 -0
  256. package/src/components/Radio/Radio.js +99 -0
  257. package/src/components/Radio/Radio.scss +58 -0
  258. package/src/components/RadioGroup/RadioGroup.js +63 -0
  259. package/src/components/RadioGroup/RadioGroup.scss +37 -0
  260. package/src/components/Section/Section.scss +74 -0
  261. package/src/components/Section/Section.tsx +67 -0
  262. package/src/components/Select/LegacySelect.js +114 -0
  263. package/src/components/Select/Select.examples.md +161 -0
  264. package/src/components/Select/Select.tsx +136 -0
  265. package/src/components/Select/options.transformer.ts +36 -0
  266. package/src/components/Select/select.utils.spec.ts +63 -0
  267. package/src/components/Select/select.utils.ts +45 -0
  268. package/src/components/SettingsMenuButton/SettingsMenuButton.tsx +111 -0
  269. package/src/components/Spinner/Spinner.tsx +23 -0
  270. package/src/components/Steps/Step.tsx +22 -0
  271. package/src/components/Steps/Steps.tsx +24 -0
  272. package/src/components/Switcher/Switcher.js +58 -0
  273. package/src/components/Switcher/SwitcherItem.js +61 -0
  274. package/src/components/Switcher/SwitcherItem.scss +67 -0
  275. package/src/components/TabLinks/TabLinks.tsx +63 -0
  276. package/src/components/Tabbar/Tabbar.tsx +29 -0
  277. package/src/components/Tabbar/TabbarItem.tsx +53 -0
  278. package/src/components/Tag/Tag.tsx +39 -0
  279. package/src/components/Tag/TagGroup.tsx +25 -0
  280. package/src/components/Text/LegacyText.js +78 -0
  281. package/src/components/Text/Text.scss +67 -0
  282. package/src/components/Text/Text.tsx +81 -0
  283. package/src/components/TextEditor/TextEditor.js +61 -0
  284. package/src/components/TextEditor/TextEditor.scss +14 -0
  285. package/src/components/TextEditorV2/TextEditorV2.js +58 -0
  286. package/src/components/TextEditorV2/TextEditorV2.scss +110 -0
  287. package/src/components/TextLink/TextLink.tsx +42 -0
  288. package/src/components/TextListItem/TextListItem.js +31 -0
  289. package/src/components/TextListItem/TextListItem.scss +10 -0
  290. package/src/components/Textarea/Textarea.js +108 -0
  291. package/src/components/Textarea/Textarea.scss +56 -0
  292. package/src/components/Title/LegacyTitle.js +64 -0
  293. package/src/components/Title/Title.scss +65 -0
  294. package/src/components/Title/Title.tsx +57 -0
  295. package/src/components/ViewItem/ViewItem.tsx +73 -0
  296. package/src/index.tsx +14 -0
  297. package/src/styles/_colors.scss +59 -0
  298. package/src/styles/_layout.scss +64 -0
  299. package/src/styles/_shadows.scss +19 -0
  300. package/src/styles/_typography.scss +8 -0
  301. package/src/styles/_utils.scss +45 -0
  302. package/src/styles/fonts/avenir/3A0AF8_0_0.eot +0 -0
  303. package/src/styles/fonts/avenir/3A0AF8_0_0.ttf +0 -0
  304. package/src/styles/fonts/avenir/3A0AF8_0_0.woff +0 -0
  305. package/src/styles/fonts/avenir/3A0AF8_0_0.woff2 +0 -0
  306. package/src/styles/fonts/avenir/3A0AF8_1_0.eot +0 -0
  307. package/src/styles/fonts/avenir/3A0AF8_1_0.ttf +0 -0
  308. package/src/styles/fonts/avenir/3A0AF8_1_0.woff +0 -0
  309. package/src/styles/fonts/avenir/3A0AF8_1_0.woff2 +0 -0
  310. package/src/types/general.types.ts +11 -0
  311. package/src/utils/useOutsideClick.js +19 -0
@@ -0,0 +1,67 @@
1
+ import React from "react";
2
+ import classNames from "classnames";
3
+
4
+ import { ButtonProps } from "../Button/Button.types";
5
+ import Button from "../Button/Button";
6
+ import { Title } from "../Title/Title";
7
+ import { LoadingIndicator, LoadingIndicatorProps } from "../LoadingIndicator/LoadingIndicator";
8
+ import { RestPropped } from "../../types/general.types";
9
+
10
+ import "./Section.scss";
11
+
12
+ export interface SectionProps<TitleType, FooterType> extends RestPropped {
13
+ title?: string | TitleType;
14
+ buttons?: ButtonProps[];
15
+ footer?: FooterType;
16
+ className?: string;
17
+ isLoading?: boolean;
18
+ loadingIndicatorProps?: LoadingIndicatorProps;
19
+ }
20
+
21
+ export function Section<TitleType = unknown, FooterType = unknown>({
22
+ title,
23
+ buttons,
24
+ footer,
25
+ children,
26
+ className,
27
+ isLoading = false,
28
+ loadingIndicatorProps,
29
+ ...restProps
30
+ }: React.PropsWithChildren<SectionProps<TitleType, FooterType>>): JSX.Element {
31
+ return (
32
+ <div
33
+ {...restProps}
34
+ className={classNames(
35
+ "cweb-section",
36
+ "flex justify-start flex-col items-start",
37
+ "rounded-lg",
38
+ "mx-0 my-4",
39
+ "bg-white",
40
+ "w-full",
41
+ className
42
+ )}
43
+ >
44
+ {(title || buttons) && (
45
+ <div className="cweb-section-header" data-test-id="section-header">
46
+ {title && (
47
+ <div className="flex flex-row items-center">
48
+ {typeof title === "string" ? <Title text={title} className="mr-3 cweb-section-title" type="sm" /> : title}
49
+ {isLoading && <LoadingIndicator asSpinner={true} {...loadingIndicatorProps} />}
50
+ </div>
51
+ )}
52
+ <div className={classNames("cweb-section-header-buttons-container", "flex flex-row space-x-3")}>
53
+ {buttons &&
54
+ buttons.length > 0 &&
55
+ buttons.map((button) => <Button {...button} key={button.key || button.id || button.text} />)}
56
+ </div>
57
+ </div>
58
+ )}
59
+
60
+ <div className="cweb-section-content w-full">{!isLoading && children}</div>
61
+
62
+ {footer && <div className="cweb-section-footer">{footer}</div>}
63
+ </div>
64
+ );
65
+ }
66
+
67
+ export default Section;
@@ -0,0 +1,114 @@
1
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import PropTypes from "prop-types";
3
+ import { components } from "react-select";
4
+ import classNames from "classnames";
5
+
6
+ import { findValue } from "./select.utils";
7
+ import Select from "./Select";
8
+
9
+ LegacySelect.propTypes = {
10
+ className: PropTypes.string,
11
+ value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
12
+ name: PropTypes.string.isRequired,
13
+ placeholder: PropTypes.string,
14
+ options: PropTypes.arrayOf(
15
+ PropTypes.shape({
16
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
17
+ text: PropTypes.string.isRequired,
18
+ disabled: PropTypes.bool,
19
+ })
20
+ ).isRequired,
21
+ onChange: PropTypes.func.isRequired,
22
+ };
23
+
24
+ /**
25
+ * For legacy purposes this functional component behaves a certain manner that is expected by Forms that still use it.
26
+ * @deprecated use Select instead
27
+ */
28
+ function LegacySelect({
29
+ className,
30
+ value,
31
+ name,
32
+ placeholder = "Placeholder text",
33
+ options,
34
+ onChange,
35
+ isDisabled = false,
36
+ isSearchable = false,
37
+ ...otherProps
38
+ }) {
39
+ const transformedOptions = useMemo(() => options.map((option) => ({ label: option.text, ...option })), [options]);
40
+ const selectRef = useRef(null);
41
+
42
+ const [innerValue, setInnerValue] = useState(findValue(value, transformedOptions));
43
+
44
+ useEffect(() => {
45
+ if (value !== innerValue?.value) {
46
+ // there is a change triggered from the parents
47
+ setInnerValue(findValue(value, transformedOptions));
48
+ }
49
+ }, [value, transformedOptions]);
50
+
51
+ // Why: https://github.com/JedWatson/react-select/issues/3149
52
+ const getDataAttributes = () => {
53
+ const dataProps = {};
54
+
55
+ for (const prop in otherProps) {
56
+ if (prop.includes("data-")) {
57
+ dataProps[prop] = otherProps[prop];
58
+ }
59
+ }
60
+
61
+ return dataProps;
62
+ };
63
+
64
+ const CustomContainer = (commonProps) => (
65
+ <components.SelectContainer
66
+ {...commonProps}
67
+ innerProps={Object.assign({}, commonProps.innerProps, {
68
+ ...getDataAttributes(),
69
+ })}
70
+ />
71
+ );
72
+
73
+ const handleChange = useCallback(
74
+ (changedValue) => {
75
+ setInnerValue(changedValue);
76
+ const { value: extractedValue, ...rest } = changedValue;
77
+ // all components in the app expect this select to behave as
78
+ // a html select so we need to pass a lookalike object in the callback
79
+ onChange &&
80
+ onChange({
81
+ // making sure no legacy implementation breaks
82
+ preventDefault: () => undefined,
83
+ target: { name, value: extractedValue, ...rest },
84
+ currentTarget: { name, value: extractedValue, ...rest },
85
+ });
86
+
87
+ selectRef.current?.blur();
88
+ },
89
+ [setInnerValue, onChange]
90
+ );
91
+
92
+ const containerClassName = classNames("cweb-select h-11", className);
93
+
94
+ // We can easily transform it here, because all forms expect this to be a normal html select.
95
+
96
+ return (
97
+ <Select
98
+ {...otherProps}
99
+ ref={selectRef}
100
+ className={containerClassName}
101
+ name={name}
102
+ isDisabled={isDisabled}
103
+ value={innerValue}
104
+ onChange={handleChange}
105
+ placeholder={placeholder}
106
+ options={transformedOptions}
107
+ isOptionDisabled={(option) => option.disabled}
108
+ components={{ SelectContainer: CustomContainer }}
109
+ isSearchable={isSearchable}
110
+ />
111
+ );
112
+ }
113
+
114
+ export default LegacySelect;
@@ -0,0 +1,161 @@
1
+ ```js
2
+ class SelectExample extends React.Component {
3
+ constructor(props) {
4
+ super(props);
5
+
6
+ this.state = {
7
+ value: null,
8
+ };
9
+
10
+ this.handleChange = this.handleChange.bind(this);
11
+ }
12
+
13
+ handleChange(event) {
14
+ this.setState({
15
+ value: event.target.value,
16
+ });
17
+ }
18
+
19
+ render() {
20
+ return (
21
+ <div>
22
+ <Select
23
+ value={this.state.value}
24
+ name="value"
25
+ options={[
26
+ {
27
+ value: "option1",
28
+ text: "Option1",
29
+ },
30
+ {
31
+ value: "option2",
32
+ text: "Option2",
33
+ },
34
+ {
35
+ value: "option3",
36
+ text: "Option3",
37
+ },
38
+ ]}
39
+ onChange={this.handleChange}
40
+ />
41
+ </div>
42
+ );
43
+ }
44
+ }
45
+ <SelectExample />;
46
+ ```
47
+
48
+ ```js
49
+ class SelectExampleWithPlaceholder extends React.Component {
50
+ constructor(props) {
51
+ super(props);
52
+
53
+ this.state = {
54
+ value: null,
55
+ };
56
+
57
+ this.handleChange = this.handleChange.bind(this);
58
+ }
59
+
60
+ handleChange(event) {
61
+ this.setState({
62
+ value: event.target.value,
63
+ });
64
+ }
65
+
66
+ render() {
67
+ return (
68
+ <div>
69
+ <Select
70
+ value={this.state.value}
71
+ name="value"
72
+ placeholder="Please select an option"
73
+ options={[
74
+ {
75
+ value: "option1",
76
+ text: "Option1",
77
+ },
78
+ {
79
+ value: "option2",
80
+ text: "Option2",
81
+ },
82
+ {
83
+ value: "option3",
84
+ text: "Option3",
85
+ },
86
+ ]}
87
+ onChange={this.handleChange}
88
+ />
89
+ </div>
90
+ );
91
+ }
92
+ }
93
+ <SelectExampleWithPlaceholder />;
94
+ ```
95
+
96
+ ```js
97
+ const inForm = require("../HOCs/form/helpers/inForm").default;
98
+ const SelectInForm = inForm(Select);
99
+
100
+ class SelectExampleInForm extends React.Component {
101
+ constructor(props) {
102
+ super(props);
103
+
104
+ this.state = {
105
+ value: null,
106
+ error: null,
107
+ };
108
+
109
+ this.handleChange = this.handleChange.bind(this);
110
+ this.handleError = this.handleError.bind(this);
111
+ }
112
+
113
+ handleChange(event) {
114
+ this.setState({
115
+ value: event.target.value,
116
+ });
117
+ }
118
+
119
+ handleError() {
120
+ this.setState((prevState) => {
121
+ return {
122
+ error: prevState.error ? null : "This field is required",
123
+ };
124
+ });
125
+ }
126
+
127
+ render() {
128
+ return (
129
+ <div className="styleguide-in-form styleguide-component-block">
130
+ <Title type="tagline" text={"Example Select use in Form"} />
131
+
132
+ <SelectInForm
133
+ label="Field Label"
134
+ error={this.state.error}
135
+ value={this.state.value}
136
+ name="value"
137
+ placeholder="Please select an option"
138
+ options={[
139
+ {
140
+ value: "option1",
141
+ text: "Option1",
142
+ },
143
+ {
144
+ value: "option2",
145
+ text: "Option2",
146
+ },
147
+ {
148
+ value: "option3",
149
+ text: "Option3",
150
+ },
151
+ ]}
152
+ onChange={this.handleChange}
153
+ />
154
+
155
+ <Button text="Toggle ErrorWrapper" className="styleguide-margin-top-10" onClick={this.handleError} />
156
+ </div>
157
+ );
158
+ }
159
+ }
160
+ <SelectExampleInForm />;
161
+ ```
@@ -0,0 +1,136 @@
1
+ import type { Props, StylesConfig, GroupBase } from "react-select";
2
+ import ReactSelect, { mergeStyles, SelectInstance } from "react-select";
3
+ import React from "react";
4
+
5
+ import checkmark from "../../assets/check-icon-primary.svg";
6
+ import doubleChevron from "../../assets/chevron-double.svg";
7
+
8
+ function generateCustomStyles<Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
9
+ hasError: boolean,
10
+ isIE11: boolean
11
+ ): StylesConfig<Option, IsMulti, Group> {
12
+ return {
13
+ option: (baseStyles, state) => {
14
+ return {
15
+ ...baseStyles,
16
+ fontWeight: state.isSelected ? "bold" : "normal",
17
+ fontSize: "14px",
18
+ backgroundColor: "none",
19
+ color: "inherit",
20
+ position: "relative",
21
+ padding: "0.75rem",
22
+ opacity: state.isDisabled ? "0.5" : 1,
23
+ "&:after": {
24
+ position: "absolute",
25
+ content: '""',
26
+ background: `url(${checkmark}) no-repeat center`,
27
+ // This needs to match the proportional
28
+ // padding in this element.
29
+ // 0.75rem -> 12px
30
+ right: 12,
31
+ height: "1rem",
32
+ width: "1rem",
33
+ visibility: state.isSelected ? "visible" : "hidden",
34
+ },
35
+ transition: "background-color 0.3s ease-in-out",
36
+ "&:hover": {
37
+ // tailwind blue-50
38
+ backgroundColor: state.isSelected ? "transparent" : "#F2FAFD",
39
+ },
40
+ pointerEvents: state.isDisabled ? "none" : "auto",
41
+ };
42
+ },
43
+ container: (baseStyles) => {
44
+ return { ...baseStyles, flexGrow: isIE11 ? 0.5 : "initial" };
45
+ },
46
+ control: (baseStyles, state) => {
47
+ const defaultBorderColor = state.isFocused ? "#045baa" : "#D1D5DB";
48
+ const validatedBorderColor = hasError ? "#c53030" : defaultBorderColor;
49
+
50
+ const defaultOutline = hasError ? "rgba(255, 98, 102, 0.3)" : "rgba(0, 159, 227, 0.3)";
51
+ const validatedOutline = `4px solid ${state.isFocused ? defaultOutline : "transparent"}`;
52
+
53
+ return {
54
+ ...baseStyles,
55
+ fontSize: "14px",
56
+ transition: "border 0.3s ease-in-out",
57
+ height: isIE11 ? "50px" : "2.75rem",
58
+
59
+ // primary outline
60
+ outline: validatedOutline,
61
+
62
+ borderColor: validatedBorderColor,
63
+ borderWidth: "1px !important",
64
+ borderStyle: "solid",
65
+ borderRadius: "4px",
66
+
67
+ boxShadow: "0px 1px 2px rgba(0, 0, 0, 0.05)",
68
+
69
+ "&:hover": {
70
+ borderColor: "#9CA3AF",
71
+ // selector for the chevron
72
+ '> [class*="IndicatorsContainer"]': {
73
+ opacity: 1,
74
+ },
75
+ },
76
+ };
77
+ },
78
+ // The placeholder has the following css prop: grid-area: 1/1/2/3;
79
+ // And grid-area doesn't work on IE11, so we need to style it slightly different.
80
+ placeholder: (baseStyles) => ({
81
+ ...baseStyles,
82
+ fontWeight: 100,
83
+ color: "#6B7280",
84
+ paddingTop: isIE11 ? "1.2rem" : undefined,
85
+ }),
86
+ singleValue: (baseStyles) => {
87
+ return {
88
+ ...baseStyles,
89
+ paddingTop: isIE11 ? "1.2rem" : undefined,
90
+ fontSize: "14px",
91
+ };
92
+ },
93
+ indicatorSeparator: () => ({
94
+ display: "none",
95
+ }),
96
+
97
+ indicatorsContainer: (baseStyles) => ({
98
+ ...baseStyles,
99
+ opacity: 0.5,
100
+ transition: "opacity 0.3s ease-in-out",
101
+ background: `url(${doubleChevron}) no-repeat center`,
102
+ // hiding the default indicator
103
+ svg: {
104
+ display: "none",
105
+ },
106
+ marginRight: "0.75rem",
107
+ }),
108
+ menu: (baseStyles) => ({
109
+ ...baseStyles,
110
+ zIndex: 20,
111
+ }),
112
+ };
113
+ }
114
+
115
+ type CustomSelect = <Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
116
+ props: Props<Option, IsMulti, Group>,
117
+ ref: React.RefAttributes<SelectInstance<Option, IsMulti, Group>>
118
+ ) => React.ReactElement;
119
+
120
+ const CustomSelect = React.forwardRef(
121
+ <Option, IsMulti extends boolean, Group extends GroupBase<Option>>(
122
+ props: Props<Option, IsMulti, Group>,
123
+ ref: any
124
+ ) => {
125
+ const { className, styles } = props;
126
+
127
+ const hasError = className?.includes("has-error") || false;
128
+ const isIE11 = "MSInputMethodContext" in window && "documentMode" in document;
129
+ const customStyles = generateCustomStyles<Option, IsMulti, Group>(hasError, isIE11);
130
+ const mergedStyles = mergeStyles<Option, IsMulti, Group>(customStyles, styles);
131
+
132
+ return <ReactSelect<Option, IsMulti, Group> {...props} styles={mergedStyles} />;
133
+ }
134
+ ) as CustomSelect;
135
+
136
+ export default CustomSelect;
@@ -0,0 +1,36 @@
1
+ interface DropdownItem {
2
+ id: string;
3
+ text: string;
4
+ key: string;
5
+ type: string;
6
+ }
7
+
8
+ interface DropdownGroupedItem {
9
+ groupKey: string;
10
+ title: string;
11
+ subItems: DropdownItem[];
12
+ }
13
+
14
+ export const transformSubitemToSelectOption = (subItem: DropdownItem): Record<string, unknown> => {
15
+ return { ...subItem, label: subItem.text, value: subItem.id };
16
+ };
17
+
18
+ export const transformToSelectOptions = (
19
+ items: Array<DropdownGroupedItem | DropdownItem>
20
+ ): Array<Record<string, unknown>> => {
21
+ const groupedItems = items
22
+ .filter((option): option is DropdownGroupedItem => "groupKey" in option)
23
+ .map((option) => ({
24
+ label: option.title,
25
+ options: option.subItems.map(transformSubitemToSelectOption),
26
+ }));
27
+
28
+ const singleItems = items
29
+ .filter((option): option is DropdownItem => !("groupKey" in option))
30
+ .map((option) => ({
31
+ value: option.id,
32
+ label: option.text,
33
+ }));
34
+
35
+ return [...singleItems, ...groupedItems];
36
+ };
@@ -0,0 +1,63 @@
1
+ import { findValue } from "./select.utils";
2
+
3
+ describe("findValue", () => {
4
+ const options: Record<string, string | number>[] = [
5
+ {
6
+ label: "Nausea - 018678f2-f5ab-401d-957e-b94db30347f3",
7
+ text: "Nausea - 018678f2-f5ab-401d-957e-b94db30347f3",
8
+ value: '{"type":"instrument","uuid":"018678f2-f5ab-401d-957e-b94db30347f3"}',
9
+ },
10
+ {
11
+ label: "Pain - 34c30d2e-7e1e-4c13-b066-ebf4897debbf",
12
+ text: "Pain - 34c30d2e-7e1e-4c13-b066-ebf4897debbf",
13
+ value: '{"type":"instrument","uuid":"34c30d2e-7e1e-4c13-b066-ebf4897debbf"}',
14
+ },
15
+ {
16
+ label: "Temperature - 38c85570-cc6e-41d8-aeb1-a84a11de6158",
17
+ text: "Temperature - 38c85570-cc6e-41d8-aeb1-a84a11de6158",
18
+ value: '{"type":"instrument","uuid":"38c85570-cc6e-41d8-aeb1-a84a11de6158"}',
19
+ },
20
+ {
21
+ label: "Body Temperature - f712cc98-fb78-4f3d-b6b5-eb74cb95cb3e",
22
+ text: "Body Temperature - f712cc98-fb78-4f3d-b6b5-eb74cb95cb3e",
23
+ value: '{"type":"instrument","uuid":"f712cc98-fb78-4f3d-b6b5-eb74cb95cb3e"}',
24
+ },
25
+ ];
26
+
27
+ it("can find a filled value", () => {
28
+ const expected = options[0];
29
+
30
+ expect(findValue('{"type":"instrument","uuid":"018678f2-f5ab-401d-957e-b94db30347f3"}', options)).toBe(expected);
31
+ });
32
+
33
+ it("if no value passed, it returns null", () => {
34
+ expect(findValue(null, options)).toEqual(null);
35
+ expect(findValue(undefined, options)).toEqual(null);
36
+ });
37
+
38
+ it("if value passed is empty but options have an empty value, it finds it", () => {
39
+ const optionsWithEmpty = [...options];
40
+ optionsWithEmpty.unshift({
41
+ label: "-- None --",
42
+ text: "-- None --",
43
+ value: "",
44
+ });
45
+
46
+ const expected = optionsWithEmpty[0];
47
+
48
+ expect(findValue("", optionsWithEmpty)).toEqual(expected);
49
+ });
50
+
51
+ it("if value passed is zero but options have a zero, it finds it", () => {
52
+ const optionsWithZero = [...options];
53
+ optionsWithZero.unshift({
54
+ label: "-- None --",
55
+ text: "-- None --",
56
+ value: 0,
57
+ });
58
+
59
+ const expected = optionsWithZero[0];
60
+
61
+ expect(findValue(0, optionsWithZero)).toEqual(expected);
62
+ });
63
+ });
@@ -0,0 +1,45 @@
1
+ /**
2
+ * This is specifically because of the workflow form, that uses a stringified object as a value.
3
+ * @param valueParam The alleged object value
4
+ * @returns true if value is indeed an object
5
+ */
6
+ const isValueObject = (valueParam: string | number) => {
7
+ if (typeof valueParam === "number") {
8
+ return false;
9
+ }
10
+
11
+ try {
12
+ const allegedObject = JSON.parse(valueParam);
13
+ return Object.keys(allegedObject).length > 0;
14
+ } catch {
15
+ // if the parsing breaks, it is probably not an object
16
+ return false;
17
+ }
18
+ };
19
+
20
+ /**
21
+ * react-select needs an exact match of the object we are using in the options in order to know what has been set
22
+ * that's why we have to filter it
23
+ * @param {*} value - The value we need to set in the selector
24
+ * @returns the object having the passed value
25
+ */
26
+ export const findValue = (value: any, options: any[]): any => {
27
+ // There are values that can be found with "" or 0, and they
28
+ // don't need to be evaluated as false in the check below
29
+ if (value === null || value === undefined) {
30
+ return null;
31
+ }
32
+
33
+ let foundValue = null;
34
+
35
+ if (isValueObject(value)) {
36
+ foundValue = options.find((option) => JSON.stringify(option.value) === JSON.stringify(value));
37
+ return foundValue;
38
+ }
39
+
40
+ foundValue = options.find((option: any) => {
41
+ return option.value?.toString() === value?.toString();
42
+ });
43
+
44
+ return foundValue;
45
+ };