@thecb/components 10.12.1 → 10.12.3-beta.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 (215) hide show
  1. package/README.md +4 -0
  2. package/dist/index.cjs.js +1413 -2314
  3. package/dist/index.cjs.js.map +1 -1
  4. package/dist/index.d.ts +6 -4
  5. package/dist/index.esm.js +1413 -2315
  6. package/dist/index.esm.js.map +1 -1
  7. package/package.json +25 -13
  8. package/src/components/atoms/.DS_Store +0 -0
  9. package/src/components/atoms/alert/Alert.mdx +19 -0
  10. package/src/components/atoms/alert/Alert.stories.js +148 -26
  11. package/src/components/atoms/badge/Badge.js +2 -2
  12. package/src/components/atoms/badge/Badge.mdx +27 -0
  13. package/src/components/atoms/badge/Badge.stories.js +143 -29
  14. package/src/components/atoms/breadcrumb/Breadcrumb.mdx +21 -0
  15. package/src/components/atoms/breadcrumb/Breadcrumb.stories.js +38 -29
  16. package/src/components/atoms/button-with-action/ButtonWithAction.stories.js +108 -55
  17. package/src/components/atoms/button-with-link/ButtonWithLink.mdx +21 -0
  18. package/src/components/atoms/button-with-link/ButtonWithLink.stories.js +160 -31
  19. package/src/components/atoms/card/Card.js +13 -1
  20. package/src/components/atoms/card/Card.mdx +41 -0
  21. package/src/components/atoms/card/Card.stories.js +360 -0
  22. package/src/components/atoms/card/Card.theme.js +2 -0
  23. package/src/components/atoms/card/index.d.ts +1 -0
  24. package/src/components/atoms/checkbox/Checkbox.mdx +15 -0
  25. package/src/components/atoms/checkbox/Checkbox.oldstories.js +30 -0
  26. package/src/components/atoms/checkbox/Checkbox.stories.js +148 -29
  27. package/src/components/atoms/country-dropdown/CountryDropdown.mdx +36 -0
  28. package/src/components/atoms/country-dropdown/CountryDropdown.stories.js +61 -27
  29. package/src/components/atoms/detail/Detail.js +0 -26
  30. package/src/components/atoms/detail/Detail.mdx +32 -0
  31. package/src/components/atoms/detail/Detail.stories.js +156 -0
  32. package/src/components/atoms/display-box/DisplayBox.mdx +11 -0
  33. package/src/components/atoms/display-box/DisplayBox.stories.js +65 -21
  34. package/src/components/atoms/display-card/DisplayCard.mdx +13 -0
  35. package/src/components/atoms/display-card/DisplayCard.stories.js +163 -22
  36. package/src/components/atoms/dropdown/Dropdown.mdx +65 -0
  37. package/src/components/atoms/dropdown/Dropdown.stories.js +91 -10
  38. package/src/components/atoms/form-layouts/FormInput.mdx +38 -0
  39. package/src/components/atoms/form-layouts/FormInput.stories.js +212 -26
  40. package/src/components/atoms/form-select/FormSelect.mdx +42 -0
  41. package/src/components/atoms/form-select/FormSelect.stories.js +55 -29
  42. package/src/components/atoms/formatted-address/FormattedAddress.mdx +13 -0
  43. package/src/components/atoms/formatted-address/FormattedAddress.stories.js +133 -27
  44. package/src/components/atoms/formatted-bank-account/FormattedBankAccount.mdx +17 -0
  45. package/src/components/atoms/formatted-bank-account/FormattedBankAccount.stories.js +57 -0
  46. package/src/components/atoms/formatted-credit-card/FormattedCreditCard.mdx +40 -0
  47. package/src/components/atoms/formatted-credit-card/FormattedCreditCard.stories.js +74 -0
  48. package/src/components/atoms/icons/Icons.mdx +40 -0
  49. package/src/components/atoms/icons/Icons.stories.js +325 -0
  50. package/src/components/atoms/labeled-amount/LabeledAmount.mdx +23 -0
  51. package/src/components/atoms/labeled-amount/LabeledAmount.stories.js +110 -34
  52. package/src/components/atoms/line-item/LineItem.mdx +28 -0
  53. package/src/components/atoms/line-item/LineItem.stories.js +89 -22
  54. package/src/components/atoms/link/Link.mdx +19 -0
  55. package/src/components/atoms/link/Link.stories.js +155 -49
  56. package/src/components/atoms/loading/Loading.mdx +13 -0
  57. package/src/components/atoms/loading/Loading.stories.js +22 -0
  58. package/src/components/atoms/loading-line/LoadingLine.js +14 -10
  59. package/src/components/atoms/loading-line/LoadingLine.mdx +15 -0
  60. package/src/components/atoms/loading-line/LoadingLine.stories.js +132 -28
  61. package/src/components/atoms/nav-footer/NavFooter.mdx +15 -0
  62. package/src/components/atoms/nav-footer/NavFooter.stories.js +235 -22
  63. package/src/components/atoms/nav-header/NavHeader.mdx +13 -0
  64. package/src/components/atoms/nav-header/NavHeader.stories.js +122 -21
  65. package/src/components/atoms/nav-tabs/NavTabs.mdx +30 -0
  66. package/src/components/atoms/nav-tabs/NavTabs.stories.js +49 -0
  67. package/src/components/atoms/password-requirements/PasswordRequirements.mdx +39 -0
  68. package/src/components/atoms/password-requirements/PasswordRequirements.stories.js +108 -44
  69. package/src/components/atoms/placeholder/Placeholder.mdx +19 -0
  70. package/src/components/atoms/placeholder/Placeholder.stories.js +164 -36
  71. package/src/components/atoms/searchable-select/SearchableSelect.mdx +44 -0
  72. package/src/components/atoms/searchable-select/SearchableSelect.stories.js +103 -28
  73. package/src/components/atoms/state-province-dropdown/StateProvinceDropdown.mdx +36 -0
  74. package/src/components/atoms/state-province-dropdown/StateProvinceDropdown.stories.js +65 -40
  75. package/src/components/atoms/table/Table.mdx +71 -0
  76. package/src/components/atoms/table/Table.oldstories.js +84 -0
  77. package/src/components/atoms/table/Table.stories.js +59 -75
  78. package/src/components/atoms/table/TableRow.js +1 -0
  79. package/src/components/atoms/title/Title.js +0 -23
  80. package/src/components/atoms/title/Title.mdx +26 -0
  81. package/src/components/atoms/title/Title.stories.js +144 -0
  82. package/src/components/atoms/toggle-switch/ToggleSwitch.mdx +17 -0
  83. package/src/components/atoms/toggle-switch/ToggleSwitch.stories.js +103 -20
  84. package/src/components/atoms/toggle-switch/ToggleSwitch.theme.js +8 -5
  85. package/src/components/atoms/typeahead-input/TypeaheadInput.mdx +13 -0
  86. package/src/components/atoms/typeahead-input/TypeaheadInput.stories.js +63 -0
  87. package/src/components/molecules/address-form/AddressForm.mdx +18 -0
  88. package/src/components/molecules/address-form/AddressForm.stories.js +223 -20
  89. package/src/components/molecules/banner/Banner.mdx +23 -0
  90. package/src/components/molecules/banner/Banner.stories.js +122 -26
  91. package/src/components/molecules/change-password-form/ChangePasswordForm.mdx +15 -0
  92. package/src/components/molecules/change-password-form/ChangePasswordForm.stories.js +203 -19
  93. package/src/components/molecules/collapsible-section/CollapsibleSection.mdx +15 -0
  94. package/src/components/molecules/collapsible-section/CollapsibleSection.stories.js +210 -61
  95. package/src/components/molecules/edit-name-form/EditNameForm.mdx +13 -0
  96. package/src/components/molecules/edit-name-form/EditNameForm.stories.js +117 -0
  97. package/src/components/molecules/idle-modal/IdleModal.js +101 -0
  98. package/src/components/molecules/idle-modal/IdleModal.mdx +17 -0
  99. package/src/components/molecules/idle-modal/IdleModal.stories.js +180 -0
  100. package/src/components/molecules/idle-modal/index.d.ts +16 -0
  101. package/src/components/molecules/idle-modal/index.js +3 -0
  102. package/src/components/molecules/index.js +1 -0
  103. package/src/components/molecules/link-card/LinkCard.mdx +17 -0
  104. package/src/components/molecules/link-card/LinkCard.stories.js +287 -72
  105. package/src/components/molecules/login-form/LoginForm.mdx +16 -0
  106. package/src/components/molecules/login-form/LoginForm.stories.js +117 -21
  107. package/src/components/molecules/modal/Modal.mdx +17 -0
  108. package/src/components/molecules/modal/Modal.stories.js +342 -128
  109. package/src/components/molecules/module/Module.mdx +17 -0
  110. package/src/components/molecules/module/Module.stories.js +267 -25
  111. package/src/components/molecules/multiple-select-filter/MultipleSelectFilter.js +295 -61
  112. package/src/components/molecules/multiple-select-filter/{MultipleSelectFilter.stories.js → MultipleSelectFilter.oldstories.js} +1 -1
  113. package/src/components/molecules/multiple-select-filter/MultipleSelectFilter.styled.js +4 -4
  114. package/src/components/molecules/obligation/Obligation.mdx +23 -0
  115. package/src/components/molecules/obligation/Obligation.stories.js +460 -0
  116. package/src/components/molecules/obligation/icons/PropertyPersonalIcon.js +1 -1
  117. package/src/components/molecules/pagination/Pagination.mdx +15 -0
  118. package/src/components/molecules/pagination/Pagination.stories.js +177 -28
  119. package/src/components/molecules/popover/Popover.mdx +15 -0
  120. package/src/components/molecules/popover/Popover.stories.js +220 -0
  121. package/src/components/molecules/tabs/Tabs.mdx +17 -0
  122. package/src/components/molecules/tabs/Tabs.stories.js +135 -227
  123. package/src/components/molecules/toast-notification/Toast.mdx +15 -0
  124. package/src/components/molecules/toast-notification/Toast.stories.js +183 -0
  125. package/src/hooks/use-outside-click/index.js +5 -4
  126. package/src/stories/Button.stories.ts +53 -0
  127. package/src/stories/Button.tsx +48 -0
  128. package/src/stories/Configure.mdx +364 -0
  129. package/src/stories/Header.stories.ts +33 -0
  130. package/src/stories/Header.tsx +56 -0
  131. package/src/stories/Page.stories.ts +32 -0
  132. package/src/stories/Page.tsx +73 -0
  133. package/src/stories/assets/accessibility.png +0 -0
  134. package/src/stories/assets/accessibility.svg +5 -0
  135. package/src/stories/assets/addon-library.png +0 -0
  136. package/src/stories/assets/assets.png +0 -0
  137. package/src/stories/assets/avif-test-image.avif +0 -0
  138. package/src/stories/assets/context.png +0 -0
  139. package/src/stories/assets/discord.svg +15 -0
  140. package/src/stories/assets/docs.png +0 -0
  141. package/src/stories/assets/figma-plugin.png +0 -0
  142. package/src/stories/assets/github.svg +3 -0
  143. package/src/stories/assets/share.png +0 -0
  144. package/src/stories/assets/styling.png +0 -0
  145. package/src/stories/assets/testing.png +0 -0
  146. package/src/stories/assets/theming.png +0 -0
  147. package/src/stories/assets/tutorials.svg +12 -0
  148. package/src/stories/assets/youtube.svg +4 -0
  149. package/src/stories/button.css +30 -0
  150. package/src/stories/header.css +32 -0
  151. package/src/stories/page.css +69 -0
  152. package/src/util/idleTimerUtils.js +36 -0
  153. package/src/util/index.js +3 -1
  154. package/src/components/atoms/icons/icons.stories.js +0 -116
  155. package/src/components/molecules/edit-name-form/EdidNameForm.stories.js +0 -24
  156. package/src/components/molecules/multiple-select-filter/__private__/ActionLinkButton.js +0 -24
  157. package/src/components/molecules/multiple-select-filter/__private__/FilterButton.js +0 -85
  158. package/src/components/molecules/multiple-select-filter/__private__/FilterDropdown.js +0 -23
  159. package/src/components/molecules/multiple-select-filter/__private__/FilterableList.js +0 -144
  160. package/src/components/molecules/multiple-select-filter/__private__/FilterableListItem.js +0 -67
  161. package/src/components/molecules/multiple-select-filter/__private__/SearchBox.js +0 -38
  162. package/src/components/molecules/multiple-select-filter/__private__/useKeyboardNavigation.js +0 -84
  163. package/src/components/molecules/multiple-select-filter/__private__/util.js +0 -31
  164. package/src/components/molecules/toast-notification/ToastNotification.stories.js +0 -105
  165. /package/src/components/atoms/add-obligation/{AddObligation.stories.js → AddObligation.oldstories.js} +0 -0
  166. /package/src/components/atoms/amount-callout/{AmountCallout.stories.js → AmountCallout.oldstories.js} +0 -0
  167. /package/src/components/atoms/checkbox-list/{CheckboxList.stories.js → CheckboxList.oldstories.js} +0 -0
  168. /package/src/components/atoms/form-layouts/{FormLayouts.stories.js → FormLayouts.oldstories.js} +0 -0
  169. /package/src/components/atoms/hamburger-button/{HamburgerButton.stories.js → HamburgerButton.oldstories.js} +0 -0
  170. /package/src/components/atoms/heading/{Heading.stories.js → Heading.oldstories.js} +0 -0
  171. /package/src/components/atoms/layouts/examples/box-example/{BoxExample.stories.js → BoxExample.oldstories.js} +0 -0
  172. /package/src/components/atoms/layouts/examples/center-example/{CenterExample.stories.js → CenterExample.oldstories.js} +0 -0
  173. /package/src/components/atoms/layouts/examples/cluster-example/{ClusterExample.stories.js → ClusterExample.oldstories.js} +0 -0
  174. /package/src/components/atoms/layouts/examples/cover-example/{CoverExample.stories.js → CoverExample.oldstories.js} +0 -0
  175. /package/src/components/atoms/layouts/examples/frame-example/{FrameExample.stories.js → FrameExample.oldstories.js} +0 -0
  176. /package/src/components/atoms/layouts/examples/grid-example/{GridExample.stories.js → GridExample.oldstories.js} +0 -0
  177. /package/src/components/atoms/layouts/examples/imposter-example/{ImposterExample.stories.js → ImposterExample.oldstories.js} +0 -0
  178. /package/src/components/atoms/layouts/examples/motion-example/{MotionExample.stories.js → MotionExample.oldstories.js} +0 -0
  179. /package/src/components/atoms/layouts/examples/reel-example/{ReelExample.stories.js → ReelExample.oldstories.js} +0 -0
  180. /package/src/components/atoms/layouts/examples/sidebar-example/{SidebarExample.stories.js → SidebarExample.oldstories.js} +0 -0
  181. /package/src/components/atoms/layouts/examples/stack-example/{StackExample.stories.js → StackExample.oldstories.js} +0 -0
  182. /package/src/components/atoms/layouts/examples/switcher-example/{SwitcherExample.stories.js → SwitcherExample.oldstories.js} +0 -0
  183. /package/src/components/atoms/paragraph/{Paragraph.stories.js → Paragraph.oldstories.js} +0 -0
  184. /package/src/components/atoms/processing-fee/{ProcessingFee.stories.js → ProcessingFee.oldstories.js} +0 -0
  185. /package/src/components/atoms/search/{Search.stories.js → Search.oldstories.js} +0 -0
  186. /package/src/components/atoms/solid-divider/{SolidDivider.stories.js → SolidDivider.oldstories.js} +0 -0
  187. /package/src/components/atoms/sortable-table-heading/{SortableTableHeading.stories.js → SortableTableHeading.oldstories.js} +0 -0
  188. /package/src/components/atoms/spinner/{Spinner.stories.js → Spinner.oldstories.js} +0 -0
  189. /package/src/components/atoms/tab/{Tab.stories.js → Tab.oldstories.js} +0 -0
  190. /package/src/components/atoms/text/{Text.stories.js → Text.oldstories.js} +0 -0
  191. /package/src/components/atoms/typeahead-input/{TypeaheadIinput.stories.js → TypeaheadIinput.oldstories.js} +0 -0
  192. /package/src/components/atoms/wallet-name/{WalletName.stories.js → WalletName.oldstories.js} +0 -0
  193. /package/src/components/molecules/account-and-routing-modal/{AccountAndRoutingModal.stories.js → AccountAndRoutingModal.oldstories.js} +0 -0
  194. /package/src/components/molecules/editable-list/{EditableList.stories.js → EditableList.oldstories.js} +0 -0
  195. /package/src/components/molecules/email-form/{EmailForm.stories.js → EmailForm.oldstories.js} +0 -0
  196. /package/src/components/molecules/forgot-password-form/{ForgotPasswordForm.stories.js → ForgotPasswordForm.oldstories.js} +0 -0
  197. /package/src/components/molecules/highlight-tab-row/{HighlightTabRow.stories.js → HighlightTabRow.oldstories.js} +0 -0
  198. /package/src/components/molecules/obligation/modules/{AmountModule.stories.js → AmountModule.oldstories.js} +0 -0
  199. /package/src/components/molecules/payment-button-bar/{PaymentButtonBar.stories.js → PaymentButtonBar.oldstories.js} +0 -0
  200. /package/src/components/molecules/payment-details/{PaymentDetails.stories.js → PaymentDetails.oldstories.js} +0 -0
  201. /package/src/components/molecules/payment-form-ach/{PaymentFormACH.stories.js → PaymentFormACH.oldstories.js} +0 -0
  202. /package/src/components/molecules/payment-form-card/{PaymentFormCard.stories.js → PaymentFormCard.oldstories.js} +0 -0
  203. /package/src/components/molecules/periscope-dashboard-iframe/{PeriscopeDashBoardIframe.stories.js → PeriscopeDashBoardIframe.oldstories.js} +0 -0
  204. /package/src/components/molecules/phone-form/{PhoneForm.stories.js → PhoneForm.oldstories.js} +0 -0
  205. /package/src/components/molecules/popup-menu/{PopupMenu.stories.js → PopupMenu.oldstories.js} +0 -0
  206. /package/src/components/molecules/radio-group/{RadioGroup.stories.js → RadioGroup.oldstories.js} +0 -0
  207. /package/src/components/molecules/radio-section/{RadioSection.stories.js → RadioSection.oldstories.js} +0 -0
  208. /package/src/components/molecules/registration-form/{RegistrationForm.stories.js → RegistrationForm.oldstories.js} +0 -0
  209. /package/src/components/molecules/reset-confirmation-form/{ResetConfirmationForm.stories.js → ResetConfirmationForm.oldstories.js} +0 -0
  210. /package/src/components/molecules/reset-password-form/{ResetPasswordForm.stories.js → ResetPasswordForm.oldstories.js} +0 -0
  211. /package/src/components/molecules/reset-password-success/{ResetPasswordSuccess.stories.js → ResetPasswordSuccess.oldstories.js} +0 -0
  212. /package/src/components/molecules/tab-sidebar/{TabSidebar.stories.js → TabSidebar.oldstories.js} +0 -0
  213. /package/src/components/molecules/terms-and-conditions/{TermsAndConditions.stories.js → TermsAndConditions.oldstories.js} +0 -0
  214. /package/src/components/molecules/terms-and-conditions-modal/{TermsAndConditionsModal.stories.js → TermsAndConditionsModal.oldstories.js} +0 -0
  215. /package/src/components/molecules/workflow-tile/{WorkflowTile.stories.js → WorkflowTile.oldstories.js} +0 -0
@@ -1,16 +1,146 @@
1
- import React, { useState, useEffect, useRef } from "react";
1
+ import React, { useState, useEffect, useRef, forwardRef } from "react";
2
2
  import { fallbackValues } from "./MultipleSelectFilter.theme";
3
3
  import { themeComponent } from "../../../util/themeUtils";
4
- import { Box } from "../../atoms/layouts";
5
- import { GHOST_GREY, WHITE, CHARADE_GREY } from "../../../constants/colors";
4
+ import { Box, Center } from "../../atoms/layouts";
5
+ import { FormInput } from "../../atoms/form-layouts";
6
+ import ButtonWithAction from "../../atoms/button-with-action";
7
+ import Checkbox from "../../atoms/checkbox";
8
+ import { GHOST_GREY, WHITE } from "../../../constants/colors";
9
+ import { Text } from "../../atoms";
10
+ import DropdownIconV2 from "../../atoms/dropdown/DropdownIconV2";
11
+ import { FONT_WEIGHT_REGULAR } from "../../../constants/style_constants";
6
12
  import { noop } from "../../../util/general";
7
- import { FilterContainer } from "./MultipleSelectFilter.styled";
8
- import ActionLinkButton from "./__private__/ActionLinkButton";
9
- import FilterButton from "./__private__/FilterButton";
10
- import FilterDropdown from "./__private__/FilterDropdown";
11
- import SearchBox from "./__private__/SearchBox";
12
- import FilterableList from "./__private__/FilterableList";
13
- import useOutsideClickHook from "../../../hooks/use-outside-click";
13
+ import {
14
+ FilterContainer,
15
+ FilterButton,
16
+ FilterDropdown
17
+ } from "./MultipleSelectFilter.styled";
18
+
19
+ const ScrollableOptionsList = ({
20
+ id,
21
+ optionsList,
22
+ selectedOptions,
23
+ themeValues,
24
+ selectOption,
25
+ maxSelections,
26
+ name
27
+ }) => {
28
+ const [focusedIndex, setFocusedIndex] = useState(-1);
29
+ const checkboxRefs = useRef([]);
30
+ const isMaxSelectionReached =
31
+ maxSelections && maxSelections === selectedOptions?.length;
32
+ const isChecked = option =>
33
+ selectedOptions?.some(
34
+ selectedOption => selectedOption?.name === option?.name
35
+ );
36
+
37
+ useEffect(() => {
38
+ if (
39
+ focusedIndex !== -1 &&
40
+ checkboxRefs.current &&
41
+ checkboxRefs.current[focusedIndex]
42
+ ) {
43
+ checkboxRefs.current[focusedIndex].focus(); // move focus to the active option
44
+ }
45
+ }, [focusedIndex]);
46
+
47
+ const handleKeyDown = event => {
48
+ if (event.key === "ArrowDown") {
49
+ event.preventDefault();
50
+ setFocusedIndex(prevIndex =>
51
+ prevIndex < optionsList.length - 1 ? prevIndex + 1 : 0
52
+ );
53
+ } else if (event.key === "ArrowUp") {
54
+ event.preventDefault();
55
+ setFocusedIndex(prevIndex =>
56
+ prevIndex > 0 ? prevIndex - 1 : optionsList.length - 1
57
+ );
58
+ } else if (event.key === " ") {
59
+ event.preventDefault();
60
+ // Select option on spacebar press if the maximum selection hasn't been reached.
61
+ const validFocusedIndex = focusedIndex < 0 ? 0 : focusedIndex;
62
+ if (
63
+ !isMaxSelectionReached ||
64
+ (focusedIndex !== -1 && isChecked(optionsList[validFocusedIndex]))
65
+ ) {
66
+ selectOption(optionsList[validFocusedIndex]);
67
+ }
68
+ } else if (event.key === "Tab") {
69
+ // Reset focus when tabbing out of the list.
70
+ setTimeout(() => {
71
+ setFocusedIndex(-1);
72
+ }, 500);
73
+ }
74
+ };
75
+
76
+ return (
77
+ <Box
78
+ id={id}
79
+ role="listbox"
80
+ padding="0"
81
+ extraStyles={`
82
+ overflow-y: auto;
83
+ max-height: 250px;
84
+ display: flex;
85
+ flex-flow: column;
86
+ `}
87
+ onKeyDown={handleKeyDown}
88
+ >
89
+ {optionsList.map((option, index) => {
90
+ const checked = isChecked(option);
91
+ const isDisabled = isMaxSelectionReached && !checked;
92
+ const tabIndex =
93
+ index === focusedIndex || (index === 0 && focusedIndex === -1)
94
+ ? "0"
95
+ : "-1";
96
+ return (
97
+ <Box
98
+ padding="0"
99
+ key={index}
100
+ extraStyles={`
101
+ :hover,
102
+ :active,
103
+ :focus {
104
+ background-color: ${themeValues.primaryColor};
105
+ }
106
+ `}
107
+ >
108
+ <Checkbox
109
+ ref={el => (checkboxRefs.current[index] = el)}
110
+ title={option.name}
111
+ name={option.name}
112
+ checked={checked}
113
+ onChange={() => (isDisabled ? noop : selectOption(option))}
114
+ textExtraStyles={`font-size: 0.875rem; margin: 0;`}
115
+ disabled={isDisabled}
116
+ extraStyles={`
117
+ padding: 0.075rem 0.325rem;
118
+ margin: 0;
119
+ :hover,
120
+ :active,
121
+ :focus {
122
+ background-color: ${themeValues.primaryColor};
123
+ }
124
+ `}
125
+ checkboxMargin="0.3rem"
126
+ role="option"
127
+ checkboxExtraStyles={`
128
+ width: 1.375rem;
129
+ height: 1.375rem;
130
+ ${
131
+ checked && !isDisabled
132
+ ? `background: ` + themeValues.secondaryColor + `;`
133
+ : ""
134
+ }`}
135
+ tabIndex={tabIndex}
136
+ dataQa={`${name}-option-${index}`}
137
+ />
138
+ </Box>
139
+ );
140
+ })}
141
+ </Box>
142
+ );
143
+ };
14
144
 
15
145
  const MultipleSelectFilter = ({
16
146
  actions,
@@ -34,27 +164,53 @@ const MultipleSelectFilter = ({
34
164
  themeValues,
35
165
  truncateBtnTextWidth = "15rem"
36
166
  }) => {
167
+ const [itemList, setItemList] = useState([]);
37
168
  const [opened, setOpened] = useState(false);
38
- const [appliedOptions, setAppliedOptions] = useState([]);
39
-
40
- const handleClickOutsideContainer = () => {
41
- setOpened(false);
42
- actions.fields.searchTerm.set("");
43
- onApply(selectedOptions);
44
- };
45
- const containerRef = useOutsideClickHook(() => handleClickOutsideContainer());
169
+ const containerRef = useRef(null);
46
170
  const dropdownRef = useRef(null);
47
171
  const filterButtonRef = useRef(null);
48
172
  const applyFilterButtonRef = useRef(null);
49
173
  const filterDropdownID = `${name}-filter-dropdown`;
50
174
  const checkboxListID = `${name}-checkbox-list`;
51
175
 
176
+ const backgroundColor = opened
177
+ ? themeValues.primaryColor
178
+ : selectedOptions?.length
179
+ ? themeValues.secondaryColor
180
+ : WHITE;
181
+ const contentColor = !opened && selectedOptions?.length ? WHITE : "#292A33";
182
+
183
+ const completeOptionsList = itemList
184
+ .slice()
185
+ .sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
186
+ const selectValues = items => items.map(item => item.value);
187
+ const selectOption = selectedOption => {
188
+ if (selectValues(selectedOptions).includes(selectedOption.value)) {
189
+ const fewerOptions = selectedOptions.filter(
190
+ option => option.value !== selectedOption.value
191
+ );
192
+ setSelectedOptions(fewerOptions);
193
+ } else {
194
+ const moreOptions = selectedOptions.concat(selectedOption);
195
+ setSelectedOptions(moreOptions);
196
+ }
197
+ };
198
+
199
+ useEffect(() => setItemList(options), [options]);
200
+
52
201
  useEffect(() => {
53
- if (!opened) {
54
- onApply(selectedOptions);
55
- setAppliedOptions(selectedOptions);
202
+ const filteredItems = options.filter(item =>
203
+ item?.name
204
+ ?.toLowerCase()
205
+ .includes(fields?.searchTerm?.rawValue?.toLowerCase())
206
+ );
207
+ // If no items are filtered, display the entire list of options.
208
+ if (filteredItems?.length) {
209
+ setItemList(filteredItems);
210
+ } else {
211
+ setItemList(options);
56
212
  }
57
- }, [opened]);
213
+ }, [fields.searchTerm.rawValue]);
58
214
 
59
215
  useEffect(() => {
60
216
  const handleKeyDown = event => {
@@ -82,8 +238,22 @@ const MultipleSelectFilter = ({
82
238
  onApply(selectedOptions);
83
239
  }
84
240
  };
241
+ const handleClickOutside = event => {
242
+ if (
243
+ containerRef.current &&
244
+ !containerRef.current.contains(event.target) &&
245
+ dropdownRef.current &&
246
+ !dropdownRef.current.contains(event.target)
247
+ ) {
248
+ setOpened(false);
249
+ actions.fields.searchTerm.set("");
250
+ onApply(selectedOptions);
251
+ }
252
+ };
253
+ document.addEventListener("mousedown", handleClickOutside);
85
254
  document.addEventListener("keydown", handleKeyDown);
86
255
  return () => {
256
+ document.addEventListener("mousedown", handleClickOutside);
87
257
  document.removeEventListener("keydown", handleKeyDown);
88
258
  };
89
259
  }, []);
@@ -92,53 +262,101 @@ const MultipleSelectFilter = ({
92
262
  <FilterContainer ref={containerRef} extraStyles={`${extraStyles}`}>
93
263
  <FilterButton
94
264
  ref={filterButtonRef}
95
- btnContentOverride={btnContentOverride}
265
+ variant="tertiary"
96
266
  action={() => {
97
267
  actions.fields.searchTerm.set("");
98
268
  setOpened(!opened);
99
269
  }}
100
- opened={opened}
101
- backgroundColor={
102
- opened
103
- ? themeValues.primaryColor
104
- : selectedOptions?.length
105
- ? themeValues.secondaryColor
106
- : WHITE
107
- }
108
- contentColor={!opened && selectedOptions?.length ? WHITE : CHARADE_GREY}
109
- name={name}
110
- filterDropdownID={filterDropdownID}
111
- hasIcon={hasIcon}
112
- icon={Icon}
113
- truncateBtnTextWidth={truncateBtnTextWidth}
114
- filterLabel={filterLabel}
115
- selectedOptions={selectedOptions}
116
- ></FilterButton>
270
+ aria-haspopup="listbox"
271
+ aria-expanded={opened}
272
+ aria-controls={filterDropdownID}
273
+ backgroundColor={backgroundColor}
274
+ dataQa={`${name}-filter-button`}
275
+ contentOverride
276
+ >
277
+ {btnContentOverride ? (
278
+ btnContentOverride
279
+ ) : (
280
+ <Center
281
+ as="span"
282
+ style={{ display: "flex", flexDirection: "row" }}
283
+ intrinsic
284
+ >
285
+ {hasIcon && <Icon color={contentColor} />}
286
+ <Center
287
+ as="span"
288
+ style={{
289
+ display: "flex",
290
+ flexDirection: "row",
291
+ padding: "0 0.5rem 0 0.25rem"
292
+ }}
293
+ intrinsic
294
+ >
295
+ <Text
296
+ variant="pS"
297
+ color={contentColor}
298
+ extraStyles={`
299
+ white-space: nowrap;
300
+ overflow: hidden;
301
+ text-overflow: ellipsis;
302
+ ${truncateBtnTextWidth && `max-width:` + truncateBtnTextWidth}
303
+ `}
304
+ >
305
+ {selectedOptions?.length
306
+ ? `${filterLabel ? filterLabel + ": " : ""}${
307
+ selectedOptions[0].name
308
+ }`
309
+ : `${filterLabel ? filterLabel : ""}`}
310
+ </Text>
311
+ <Text color={contentColor} variant="pS">
312
+ {selectedOptions?.length && selectedOptions?.length > 1
313
+ ? `, +${selectedOptions?.length - 1} More`
314
+ : ""}
315
+ </Text>
316
+ </Center>
317
+ <DropdownIconV2 color={contentColor} />
318
+ </Center>
319
+ )}
320
+ </FilterButton>
117
321
  <FilterDropdown
118
322
  id={filterDropdownID}
119
323
  ref={dropdownRef}
120
- ariaOwns={checkboxListID}
121
- opened={opened}
324
+ hidden={!opened}
325
+ role="combobox"
326
+ aria-expanded={opened}
327
+ aria-haspopup="listbox"
328
+ aria-owns={checkboxListID}
122
329
  >
123
- <SearchBox
124
- showSearchBox={searchable && options?.length > 8}
125
- autocompleteValue={autocompleteValue}
126
- fields={fields}
127
- actions={actions}
128
- placeholder={placeholder}
129
- disabled={disabled}
130
- ></SearchBox>
131
- <FilterableList
330
+ <Box padding="0 0 0.5rem">
331
+ {searchable && options?.length > 8 && (
332
+ <FormInput
333
+ autocompleteValue={autocompleteValue}
334
+ showFieldErrorRow={false}
335
+ errorMessages={{}}
336
+ field={fields.searchTerm}
337
+ fieldActions={actions.fields.searchTerm}
338
+ placeholder={placeholder}
339
+ disabled={disabled}
340
+ extraStyles={`
341
+ height: 2.875rem;
342
+ border: 0;
343
+ border-radius: 0;
344
+ padding: 0.45rem;
345
+ font-size: 0.875rem;
346
+ border-bottom: 1px solid ${GHOST_GREY};
347
+ `}
348
+ />
349
+ )}
350
+ </Box>
351
+ <ScrollableOptionsList
132
352
  id={checkboxListID}
133
- options={options}
134
- appliedOptions={appliedOptions}
135
- themeValues={themeValues}
353
+ optionsList={completeOptionsList}
136
354
  selectedOptions={selectedOptions}
355
+ themeValues={themeValues}
356
+ selectOption={selectOption}
137
357
  maxSelections={maxSelections}
138
358
  name={name}
139
- setSelectedOptions={setSelectedOptions}
140
- searchTerm={fields?.searchTerm}
141
- ></FilterableList>
359
+ ></ScrollableOptionsList>
142
360
  <Box
143
361
  padding="0 0.5rem 0.0625rem 0.5rem"
144
362
  extraStyles={`
@@ -149,26 +367,42 @@ const MultipleSelectFilter = ({
149
367
  border-top: 1px solid ${GHOST_GREY};
150
368
  `}
151
369
  >
152
- <ActionLinkButton
370
+ <ButtonWithAction
153
371
  action={() => {
154
372
  setOpened(false);
155
373
  setSelectedOptions([]);
156
374
  actions.fields.searchTerm.set("");
157
375
  onClear();
158
376
  }}
377
+ variant="tertiary"
378
+ extraStyles={`
379
+ padding: 0.2rem;
380
+ margin: 0.5rem;
381
+ min-height: auto;
382
+ min-width: auto;
383
+ `}
384
+ textExtraStyles={`font-weight: ${FONT_WEIGHT_REGULAR};`}
159
385
  text="Clear"
160
386
  dataQa={`${name}-clear-filters`}
161
- ></ActionLinkButton>
162
- <ActionLinkButton
387
+ ></ButtonWithAction>
388
+ <ButtonWithAction
163
389
  ref={applyFilterButtonRef}
164
390
  action={() => {
165
391
  setOpened(false);
166
392
  actions.fields.searchTerm.set("");
167
393
  onApply(selectedOptions);
168
394
  }}
395
+ variant="tertiary"
396
+ extraStyles={`
397
+ padding: 0.2rem;
398
+ margin: 0.5rem;
399
+ min-height: auto;
400
+ min-width: auto;
401
+ `}
402
+ textExtraStyles={`font-weight: ${FONT_WEIGHT_REGULAR};`}
169
403
  text="Apply"
170
404
  dataQa={`${name}-apply-filters`}
171
- ></ActionLinkButton>
405
+ ></ButtonWithAction>
172
406
  </Box>
173
407
  </FilterDropdown>
174
408
  </FilterContainer>
@@ -41,7 +41,7 @@ const items = [
41
41
  ];
42
42
 
43
43
  const FormWrapper = props => {
44
- const [selectedItems, setSelectedItems] = useState([]);
44
+ const [selectedItems, setSelectedItems] = useState(props.selectedItems || []);
45
45
 
46
46
  return (
47
47
  <MultipleSelectFilter
@@ -28,11 +28,11 @@ const StyledFilterDropdown = styled(Box)`
28
28
  max-width: 18.625rem;
29
29
  `;
30
30
 
31
- export const FilterDropdownContainer = forwardRef((props, ref) => (
31
+ export const FilterDropdown = forwardRef((props, ref) => (
32
32
  <StyledFilterDropdown ref={ref} {...props} />
33
33
  ));
34
34
 
35
- const FilterButton = styled(ButtonWithAction)`
35
+ const StyledFilterButton = styled(ButtonWithAction)`
36
36
  min-width: auto;
37
37
  min-height: 2.3125rem;
38
38
  margin: 0;
@@ -52,6 +52,6 @@ const FilterButton = styled(ButtonWithAction)`
52
52
  `}
53
53
  `;
54
54
 
55
- export const StyledFilterButton = forwardRef((props, ref) => (
56
- <FilterButton ref={ref} {...props} />
55
+ export const FilterButton = forwardRef((props, ref) => (
56
+ <StyledFilterButton ref={ref} {...props} />
57
57
  ));
@@ -0,0 +1,23 @@
1
+ import { Canvas, Meta, Title, Story, Controls } from '@storybook/blocks';
2
+
3
+ import * as ObligationStories from './Obligation.stories.js';
4
+
5
+ <Meta of={ObligationStories} />
6
+
7
+ <Title />
8
+
9
+ The Obligation is a purpose built component to display user accounts on NFE's User Profile. The component consumers configuration provided by FCS to dictate its icon and type. NFE uses this configuration to assemble the description and subDescription from the obligation's custom attributes. A full detail of the structure of the configuration object is beyond the scope of this documentation, but an example can be found in the story code, and further detail is available on Confluence within the implementation guides used by solutions engineers.
10
+
11
+ The component currently has two states. An active obligation uses data from the user lookup and FCS to provide account details and amount due as well as controls to interact with the obligation, such as for viewing account details or enabling/disabling autopay.
12
+
13
+ An inactive obligation is one whose data is not fully returned from lookup (this happens when our systems contain record of the account because of its association with a user, but the source system or source file ingested by FLS no longer provides that information, such as when a debt gets paid off). Inactive obligations look different and take some props to display a small amount of information to the user about what account used to be represented there.
14
+
15
+ If the current user is a customer management admin accessing the profile via the RevM impersonation feature, then most of the interaction controls with the Obligation will be disabled.
16
+
17
+ The Obligation was designed to be extensible to meet product needs in the future. New "modules" can be added to the modules directory that feature different collections of buttons or information display. Some work will need to be done to the main component code to support such additions.
18
+
19
+ <Controls />
20
+
21
+ <div style={{ marginBottom: "2em"}}>
22
+ <Story />
23
+ </div>