@thecb/components 11.1.11-beta.1 → 11.1.12-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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thecb/components",
3
- "version": "11.1.11-beta.1",
3
+ "version": "11.1.12-beta.0",
4
4
  "description": "Common lib for CityBase react components",
5
5
  "main": "dist/index.cjs.js",
6
6
  "typings": "dist/index.d.ts",
@@ -99,6 +99,7 @@ const FormattedInputField = styled(({ showErrors, themeValues, ...props }) => (
99
99
 
100
100
  const FormInput = ({
101
101
  type = "text",
102
+ labelDisplayOverride = null,
102
103
  labelTextWhenNoError = "",
103
104
  errorMessages,
104
105
  isNum = false,
@@ -147,39 +148,47 @@ const FormInput = ({
147
148
  <Box padding="0">
148
149
  {helperModal ? (
149
150
  <Cluster justify="space-between" align="center">
150
- <Text
151
- as="label"
152
- color={themeValues.labelColor}
153
- variant={labelTextVariant}
154
- weight={themeValues.fontWeight}
155
- extraStyles={`word-break: break-word;
151
+ {labelDisplayOverride ? (
152
+ labelDisplayOverride
153
+ ) : (
154
+ <Text
155
+ as="label"
156
+ color={themeValues.labelColor}
157
+ variant={labelTextVariant}
158
+ weight={themeValues.fontWeight}
159
+ extraStyles={`word-break: break-word;
156
160
  font-family: Public Sans;
157
161
  &::first-letter {
158
162
  text-transform: uppercase;
159
163
  }`}
160
- id={createIdFromString(labelTextWhenNoError)}
161
- >
162
- {labelTextWhenNoError}
163
- </Text>
164
+ id={createIdFromString(labelTextWhenNoError)}
165
+ >
166
+ {labelTextWhenNoError}
167
+ </Text>
168
+ )}
164
169
  {helperModal()}
165
170
  </Cluster>
166
171
  ) : (
167
172
  <Box padding="0" minWidth="100%">
168
173
  <Cluster justify="space-between" align="center">
169
- <Text
170
- as="label"
171
- color={themeValues.labelColor}
172
- variant={labelTextVariant}
173
- fontWeight={themeValues.fontWeight}
174
- extraStyles={`word-break: break-word;
174
+ {labelDisplayOverride ? (
175
+ labelDisplayOverride
176
+ ) : (
177
+ <Text
178
+ as="label"
179
+ color={themeValues.labelColor}
180
+ variant={labelTextVariant}
181
+ fontWeight={themeValues.fontWeight}
182
+ extraStyles={`word-break: break-word;
175
183
  font-family: Public Sans;
176
184
  &::first-letter {
177
185
  text-transform: uppercase;
178
186
  }`}
179
- id={createIdFromString(labelTextWhenNoError)}
180
- >
181
- {labelTextWhenNoError}
182
- </Text>
187
+ id={createIdFromString(labelTextWhenNoError)}
188
+ >
189
+ {labelTextWhenNoError}
190
+ </Text>
191
+ )}
183
192
  {type === "password" && (
184
193
  <Text
185
194
  variant={labelTextVariant}
@@ -11,6 +11,7 @@ import FilterDropdown from "./__private__/FilterDropdown";
11
11
  import SearchBox from "./__private__/SearchBox";
12
12
  import FilterableList from "./__private__/FilterableList";
13
13
  import useOutsideClickHook from "../../../hooks/use-outside-click";
14
+ import { mergeOptions } from "./__private__/util";
14
15
 
15
16
  const MultipleSelectFilter = ({
16
17
  actions,
@@ -32,34 +33,71 @@ const MultipleSelectFilter = ({
32
33
  placeholder = "Search",
33
34
  searchable = true,
34
35
  themeValues,
35
- truncateBtnTextWidth = "15rem"
36
+ truncateBtnTextWidth = "15rem",
37
+ activeAppliedOptions
36
38
  }) => {
39
+ // State to manage whether the dropdown is open or closed
37
40
  const [opened, setOpened] = useState(false);
41
+
42
+ // State to manage the currently selected options
38
43
  const [selectedOptions, setSelectedOptions] = useState([]);
39
- const [appliedOptions, setAppliedOptions] = useState([]);
44
+
45
+ // State to manage the applied options, initialized with activeAppliedOptions or an empty array
46
+ const [appliedOptions, setAppliedOptions] = useState(
47
+ activeAppliedOptions || []
48
+ );
49
+
50
+ // State to track whether the user has interacted with the component
51
+ const [hasInteracted, setHasInteracted] = useState(false);
52
+
53
+ // Reference to keep track of the opened state across renders without causing re-renders
40
54
  const openedRef = useRef(opened);
41
55
 
42
- const handleOnClose = () => {
43
- if (openedRef.current) {
44
- setOpened(false);
45
- actions.fields.searchTerm.set("");
46
- }
47
- };
56
+ // Hook to detect clicks outside the component and close the dropdown
48
57
  const containerRef = useOutsideClickHook(() => handleOnClose());
58
+
59
+ // References to various elements within the component
49
60
  const dropdownRef = useRef(null);
50
61
  const filterButtonRef = useRef(null);
51
62
  const applyFilterButtonRef = useRef(null);
63
+
64
+ // IDs for accessibility and identification purposes
52
65
  const filterDropdownID = `${name}-filter-dropdown`;
53
66
  const listGroupID = `${name}-list-group`;
54
67
 
68
+ const handleOnClose = () => {
69
+ if (openedRef.current) {
70
+ setOpened(false);
71
+ actions.fields.searchTerm.set("");
72
+ }
73
+ };
74
+
55
75
  useEffect(() => {
56
76
  openedRef.current = opened;
57
77
  if (!opened) {
58
- onApply(selectedOptions);
59
- setAppliedOptions(selectedOptions);
78
+ if (hasInteracted) {
79
+ onApply(selectedOptions);
80
+ setAppliedOptions(selectedOptions);
81
+ }
82
+ } else {
83
+ setHasInteracted(true);
60
84
  }
61
85
  }, [opened]);
62
86
 
87
+ useEffect(() => {
88
+ // Update the applied options state with the current active applied options,
89
+ // or an empty array if activeAppliedOptions is undefined or null.
90
+ // This ensures that the current applied options are in sync with the parent component.
91
+ setAppliedOptions(activeAppliedOptions || []);
92
+
93
+ // Merge the selected options with the active applied options.
94
+ const mergedSelections = mergeOptions(
95
+ selectedOptions,
96
+ activeAppliedOptions
97
+ );
98
+ setSelectedOptions(mergedSelections);
99
+ }, [activeAppliedOptions]);
100
+
63
101
  useEffect(() => {
64
102
  const handleKeyDown = event => {
65
103
  if (event.key === "Escape") {
@@ -122,6 +160,7 @@ const MultipleSelectFilter = ({
122
160
  filterLabel={filterLabel}
123
161
  selectedOptions={selectedOptions}
124
162
  extraStyles={btnExtraStyles}
163
+ dataAppliedOptions={appliedOptions?.length}
125
164
  ></FilterButton>
126
165
  <FilterDropdown
127
166
  id={filterDropdownID}
@@ -21,7 +21,8 @@ const FilterButton = forwardRef(
21
21
  truncateBtnTextWidth,
22
22
  filterLabel,
23
23
  selectedOptions,
24
- extraStyles
24
+ extraStyles,
25
+ dataAppliedOptions
25
26
  },
26
27
  ref
27
28
  ) => {
@@ -48,6 +49,7 @@ const FilterButton = forwardRef(
48
49
  dataQa={`${name}-filter-button`}
49
50
  extraStyles={extraStyles}
50
51
  aria-label={`${filterLabel} Filter: ${btnTextFilterDescription} ${btnTextItemsDescription}`}
52
+ data-applied-options={dataAppliedOptions}
51
53
  contentOverride
52
54
  >
53
55
  {btnContentOverride ? (
@@ -29,3 +29,16 @@ export const selectOption = (option, selectedOptions, setSelectedOptions) => {
29
29
  setSelectedOptions(moreOptions);
30
30
  }
31
31
  };
32
+
33
+ export const mergeOptions = (selectedOptions, activeOptions) => {
34
+ if (!activeOptions.length) return selectedOptions;
35
+ if (!selectedOptions.length) return activeOptions;
36
+
37
+ const mergedOptions = [...selectedOptions];
38
+ activeOptions.forEach(activeOption => {
39
+ if (!mergedOptions.some(option => option.name === activeOption.name)) {
40
+ mergedOptions.push(activeOption);
41
+ }
42
+ });
43
+ return mergedOptions;
44
+ };
@@ -20,7 +20,7 @@ import { fallbackValues } from "./Obligation.theme";
20
20
 
21
21
  const Obligation = ({
22
22
  config,
23
- obligations,
23
+ obligations = [],
24
24
  actions,
25
25
  autoPayEnabled,
26
26
  autoPayAvailable,
@@ -121,6 +121,7 @@ const Obligation = ({
121
121
  {!isMobile && (
122
122
  <PaymentDetailsActions
123
123
  obligations={obligations}
124
+ obligationAssocID={obligationAssocID}
124
125
  autoPayEnabled={autoPayEnabled}
125
126
  autoPayAvailable={autoPayAvailable}
126
127
  handleAutopayAction={handleAutopayAction}
@@ -146,6 +147,7 @@ const Obligation = ({
146
147
  {isMobile && (
147
148
  <PaymentDetailsActions
148
149
  obligations={obligations}
150
+ obligationAssocID={obligationAssocID}
149
151
  autoPayEnabled={autoPayEnabled}
150
152
  autoPayAvailable={autoPayAvailable}
151
153
  handleAutopayAction={handleAutopayAction}
@@ -33,8 +33,8 @@ const AutopayModalModule = ({
33
33
  onKeyPress
34
34
  }) => {
35
35
  const generateMethodNeededText = (planText, allowedPaymentInstruments) => {
36
- const allowsCard = allowedPaymentInstruments.includes(CC_METHOD);
37
- const allowsACH = allowedPaymentInstruments.includes(ACH_METHOD);
36
+ const allowsCard = allowedPaymentInstruments?.includes(CC_METHOD);
37
+ const allowsACH = allowedPaymentInstruments?.includes(ACH_METHOD);
38
38
  const methodRequired =
39
39
  allowsCard && !allowsACH
40
40
  ? "debit or credit card payment method"
@@ -2,18 +2,34 @@ import React from "react";
2
2
  import { equals } from "ramda";
3
3
  import { FormInput } from "../../atoms/form-layouts";
4
4
  import { displayCurrency } from "../../../util/general";
5
- import Text from "../../atoms/text";
6
-
5
+ import { Stack, Detail, Text } from "../../atoms";
6
+ import { themeComponent } from "../../../util/themeUtils";
7
+ import { fallbackValues } from "./PartialAmountField.theme";
7
8
  const PartialAmountField = ({
8
9
  lineItem,
9
10
  field,
10
11
  showErrors,
11
12
  errorMessages,
12
13
  moneyFormat,
13
- fieldActions
14
+ fieldActions,
15
+ themeValues
14
16
  }) => (
15
17
  <FormInput
16
- labelTextWhenNoError={lineItem.description}
18
+ labelDisplayOverride={
19
+ <Stack childGap="0px">
20
+ <Detail
21
+ as="h3"
22
+ variant={themeValues.detailVariant}
23
+ weight={themeValues.weightTitle}
24
+ >
25
+ <span>{lineItem.description}</span>
26
+ </Detail>
27
+ <Detail as="p" variant={themeValues.detailVariant} weight="400">
28
+ {lineItem.subDescription}
29
+ </Detail>
30
+ </Stack>
31
+ }
32
+ labelTextWhenNoError={`${lineItem.description} | ${lineItem.subDescription}`}
17
33
  key={lineItem.id}
18
34
  field={field}
19
35
  fieldActions={fieldActions}
@@ -46,4 +62,12 @@ function arePropsEqual(prevProps, nextProps) {
46
62
  );
47
63
  }
48
64
 
49
- export default React.memo(PartialAmountField, arePropsEqual);
65
+ export default React.memo(
66
+ themeComponent(
67
+ PartialAmountField,
68
+ "PartialAmountField",
69
+ fallbackValues,
70
+ "default"
71
+ ),
72
+ arePropsEqual
73
+ );
@@ -0,0 +1,7 @@
1
+ const weightTitle = { default: "600", small: "400" };
2
+ const detailVariant = { default: "large", small: "small" };
3
+
4
+ export const fallbackValues = {
5
+ weightTitle,
6
+ detailVariant
7
+ };