@thecb/components 9.2.4-beta.2 → 9.2.4-beta.20

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 (25) hide show
  1. package/dist/index.cjs.js +61 -78
  2. package/dist/index.cjs.js.map +1 -1
  3. package/dist/index.esm.js +61 -78
  4. package/dist/index.esm.js.map +1 -1
  5. package/package.json +1 -1
  6. package/src/components/atoms/checkbox/Checkbox.js +1 -3
  7. package/src/components/atoms/checkbox/Checkbox.stories.js +2 -3
  8. package/src/components/atoms/country-dropdown/CountryDropdown.js +2 -2
  9. package/src/components/atoms/country-dropdown/CountryDropdown.stories.js +0 -1
  10. package/src/components/atoms/dropdown/Dropdown.js +7 -8
  11. package/src/components/atoms/form-select/FormSelect.js +9 -20
  12. package/src/components/atoms/form-select/FormSelect.stories.js +2 -2
  13. package/src/components/atoms/radio-button-with-label/RadioButtonWithLabel.js +7 -12
  14. package/src/components/atoms/state-province-dropdown/StateProvinceDropdown.js +0 -1
  15. package/src/components/atoms/state-province-dropdown/StateProvinceDropdown.stories.js +0 -1
  16. package/src/components/molecules/address-form/AddressForm.js +1 -2
  17. package/src/components/molecules/email-form/EmailForm.js +3 -1
  18. package/src/components/molecules/payment-form-ach/PaymentFormACH.js +4 -4
  19. package/src/components/molecules/payment-form-card/PaymentFormCard.js +1 -2
  20. package/src/components/molecules/phone-form/PhoneForm.js +3 -1
  21. package/src/components/molecules/radio-group/RadioGroup.js +2 -5
  22. package/src/components/molecules/radio-group/RadioGroup.stories.js +0 -1
  23. package/src/components/molecules/radio-section/RadioSection.js +9 -3
  24. package/src/components/molecules/radio-section/RadioSection.stories.js +1 -0
  25. package/src/components/molecules/radio-section/radio-button/RadioButton.js +2 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thecb/components",
3
- "version": "9.2.4-beta.2",
3
+ "version": "9.2.4-beta.20",
4
4
  "description": "Common lib for CityBase react components",
5
5
  "main": "dist/index.cjs.js",
6
6
  "typings": "dist/index.d.ts",
@@ -95,8 +95,7 @@ const Checkbox = ({
95
95
  checkboxMargin = "0 16px 0 0",
96
96
  extraStyles,
97
97
  textExtraStyles,
98
- dataQa = null,
99
- isRequired = false
98
+ dataQa = null
100
99
  }) => {
101
100
  const [focused, setFocused] = useState(false);
102
101
 
@@ -143,7 +142,6 @@ const Checkbox = ({
143
142
  disabledStyles={themeValues.disabledStyles}
144
143
  disabledCheckedStyles={themeValues.disabledCheckedStyles}
145
144
  focusedStyles={themeValues.focusedStyles}
146
- aria-required={isRequired}
147
145
  >
148
146
  <CheckboxIcon
149
147
  viewBox="0 0 24 24"
@@ -13,13 +13,12 @@ const groupId = "props";
13
13
  export const checkbox = () => (
14
14
  <Checkbox
15
15
  variant={select(variantsLabel, variants, defaultValue, groupId)}
16
- title={text("title", "Checkbox Title", "props")}
17
- name={text("name", "Checkbox Name", "props")}
16
+ title={text("title", "Checkbox", "props")}
17
+ name={text("name", "Checkbox", "props")}
18
18
  checked={boolean("checked", false, "props")}
19
19
  error={boolean("error", false, "props")}
20
20
  disabled={boolean("disabled", false, "props")}
21
21
  focused={boolean("focused", false, "props")}
22
- isRequired={boolean("isRequired", true, "props")}
23
22
  />
24
23
  );
25
24
 
@@ -10,8 +10,8 @@ const CountryDropdown = ({
10
10
  fieldActions,
11
11
  showErrors,
12
12
  onChange,
13
- dataQa = null,
14
- isRequired = false
13
+ isRequired = false,
14
+ dataQa = null
15
15
  }) => (
16
16
  <FormSelect
17
17
  options={options}
@@ -30,7 +30,6 @@ const CountryFormWrapper = ({ fields, actions }) => (
30
30
  options={options}
31
31
  field={fields.country}
32
32
  fieldActions={actions.fields.country}
33
- isRequired={true}
34
33
  />
35
34
  </Box>
36
35
  );
@@ -136,7 +136,6 @@ const Dropdown = ({
136
136
  ariaInvalid = false,
137
137
  isRequired = false
138
138
  }) => {
139
- const required = options.required || isRequired;
140
139
  const [inputValue, setInputValue] = useState("");
141
140
  const [optionsState, setOptionsState] = useState([]);
142
141
  const [filteredOptions, setFilteredOptions] = useState([]);
@@ -264,13 +263,15 @@ const Dropdown = ({
264
263
  useEffect(() => {
265
264
  if (autoEraseTypeAhead) {
266
265
  clearTimeout(timer);
267
- setTimer(setTimeout(() => setInputValue(""), 3000));
266
+ setTimer(setTimeout(() => setInputValue(""), 20000));
268
267
  }
268
+
269
269
  setFilteredOptions(
270
270
  options.filter(
271
271
  option =>
272
- option.value.toLowerCase().startsWith(inputValue.toLowerCase()) ||
273
- option.text.toLowerCase().startsWith(inputValue.toLowerCase())
272
+ option?.value?.toLowerCase()?.indexOf(inputValue?.toLowerCase()) >=
273
+ 0 ||
274
+ option.text?.toLowerCase()?.indexOf(inputValue?.toLowerCase()) >= 0
274
275
  )
275
276
  );
276
277
  }, [inputValue]);
@@ -326,7 +327,7 @@ const Dropdown = ({
326
327
  aria-labelledby={ariaLabelledby}
327
328
  aria-describedby={ariaDescribedby}
328
329
  aria-expanded={isOpen}
329
- aria-required={required}
330
+ aria-required={options.required}
330
331
  aria-invalid={ariaInvalid}
331
332
  background={isOpen ? themeValues.hoverColor : WHITE}
332
333
  borderRadius="2px"
@@ -358,6 +359,7 @@ const Dropdown = ({
358
359
  }}
359
360
  padding="12px"
360
361
  placeholder={getSelection()}
362
+ required={options.required || isRequired}
361
363
  role="combobox"
362
364
  themeValues={themeValues}
363
365
  title={hasTitles ? getSelection() : null}
@@ -365,8 +367,6 @@ const Dropdown = ({
365
367
  tabIndex={0}
366
368
  value={inputValue}
367
369
  width="100%"
368
- /* Non-semantic elements need the aria-* version of the attribute */
369
- aria-disabled={disabledValues.includes(inputValue)}
370
370
  />
371
371
  <IconWrapper open={isOpen} onClick={onClick}>
372
372
  <DropdownIcon />
@@ -381,7 +381,6 @@ const Dropdown = ({
381
381
  tabIndex={0}
382
382
  role="listbox"
383
383
  id={`${ariaLabelledby}_listbox`}
384
- required={required}
385
384
  >
386
385
  <Stack childGap="0" as="ul">
387
386
  {filteredOptions.map((choice, i) => {
@@ -1,4 +1,4 @@
1
- import React, { useState, useMemo, useRef, useEffect } from "react";
1
+ import React, { useState, useRef, useEffect } from "react";
2
2
  import Dropdown from "../dropdown";
3
3
  import Text from "../text";
4
4
  import { ERROR_COLOR } from "../../../constants/colors";
@@ -29,20 +29,6 @@ const FormSelect = ({
29
29
  }) => {
30
30
  const [open, setOpen] = useState(false);
31
31
  const dropdownRef = useRef(null);
32
- const required = options?.required || isRequired;
33
-
34
- const labelId = useMemo(
35
- () => labelTextWhenNoError => createIdFromString(labelTextWhenNoError),
36
- [labelTextWhenNoError]
37
- );
38
-
39
- const descriptionId = useMemo(
40
- () => (field, labelTextWhenNoError) =>
41
- field.hasErrors && field.dirty
42
- ? labelId(labelTextWhenNoError) + "error-message"
43
- : "",
44
- [field, labelTextWhenNoError]
45
- );
46
32
 
47
33
  const handleClickAway = event => {
48
34
  if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
@@ -60,8 +46,8 @@ const FormSelect = ({
60
46
  return (
61
47
  <SelectContainer
62
48
  ref={dropdownRef}
63
- aria-role="group"
64
49
  disabled={disabled}
50
+ aria-disabled={disabled}
65
51
  data-qa={dataQa}
66
52
  >
67
53
  <Box padding="0" minWidth="100%">
@@ -76,15 +62,18 @@ const FormSelect = ({
76
62
  &::first-letter {
77
63
  text-transform: uppercase;
78
64
  }`}
79
- id={labelId(labelTextWhenNoError)}
65
+ id={createIdFromString(labelTextWhenNoError)}
80
66
  >
81
67
  {labelTextWhenNoError}
82
68
  </Text>
83
69
  </Cluster>
84
70
  </Box>
85
71
  <Dropdown
86
- ariaLabelledby={labelId(labelTextWhenNoError)}
87
- ariaDescribedby={descriptionId(field, labelTextWhenNoError)}
72
+ ariaLabelledby={createIdFromString(labelTextWhenNoError)}
73
+ ariaDescribedby={createIdFromString(
74
+ labelTextWhenNoError,
75
+ "error message"
76
+ )}
88
77
  maxHeight={dropdownMaxHeight}
89
78
  widthFitOptions={widthFitOptions}
90
79
  hasTitles={hasTitles}
@@ -106,7 +95,7 @@ const FormSelect = ({
106
95
  disabled={disabled}
107
96
  autocompleteValue={autocompleteValue}
108
97
  smoothScroll={smoothScroll}
109
- isRequired={required}
98
+ isRequired={isRequired}
110
99
  />
111
100
  <Stack direction="row" justify="space-between">
112
101
  {(field.hasErrors && field.dirty) || (field.hasErrors && showErrors) ? (
@@ -12,7 +12,7 @@ const { mapStateToProps, mapDispatchToProps, reducer } = createFormState({
12
12
  }
13
13
  });
14
14
  const errorMessages = {
15
- [required.error]: "This field is required."
15
+ [required.error]: "thing is required"
16
16
  };
17
17
  const options = [
18
18
  { value: "", text: "choose name" },
@@ -34,7 +34,7 @@ const story = page({
34
34
  const FormWrapper = props => (
35
35
  <FormSelect
36
36
  autocompleteValue={props.autocompleteValue}
37
- labelTextWhenNoError="Form Select Label"
37
+ labelTextWhenNoError="Form Select"
38
38
  errorMessages={errorMessages}
39
39
  options={options}
40
40
  field={props.fields.thing}
@@ -18,7 +18,7 @@ const Circle = styled.div`
18
18
  flex-shrink: 0;
19
19
  margin-right: 8px;
20
20
  width: 1.5rem;
21
- height: 1.5rem;
21
+ height 1.5rem;
22
22
  border: ${({ inactiveBorderColor }) => `1px solid ${inactiveBorderColor}`};
23
23
  border-radius: 50%;
24
24
  box-sizing: border-box;
@@ -30,24 +30,21 @@ const Circle = styled.div`
30
30
  display: block;
31
31
  background: ${({ activeColor }) => activeColor};
32
32
  border-radius: 50%;
33
- transform: scale(0);
34
- }
33
+ transform: scale(0);
34
+ }
35
35
  `;
36
36
 
37
37
  const InputAndLabelContainer = styled(Cluster)`
38
38
  overflow: visible;
39
-
40
39
  ${HiddenRadioInput}:checked + label ${Circle}:after {
41
40
  transform: scale(0.85);
42
41
  transition: transform 0.15s;
43
42
  }
44
-
45
43
  ${HiddenRadioInput}:checked + label ${Circle} {
46
- border: ${({ activeColor }) => "1px solid " + activeColor};
44
+ border: ${({ activeColor }) => `1px solid ${activeColor};`}
47
45
  }
48
-
49
- ${HiddenRadioInput}:focus + label ${Circle} {
50
- box-shadow: ${({ activeColor }) => "0px 0px 2px 1px " + activeColor};
46
+ ${HiddenRadioInput}:focus + label ${Circle} { {
47
+ box-shadow: ${({ activeColor }) => `0px 0px 2px 1px ${activeColor};`}
51
48
  }
52
49
  `;
53
50
 
@@ -62,8 +59,7 @@ const RadioButtonWithLabel = ({
62
59
  index,
63
60
  handleChange = noop, // optional, for custom event handling in ingesting app
64
61
  field,
65
- config,
66
- isRequired = false
62
+ config
67
63
  }) => {
68
64
  const getDefaultChecked = (value, idx) => {
69
65
  const selectionExistsInConfig = config
@@ -95,7 +91,6 @@ const RadioButtonWithLabel = ({
95
91
  handleChange(e);
96
92
  }}
97
93
  defaultChecked={getDefaultChecked(value, index)}
98
- required={isRequired}
99
94
  />
100
95
  <Text
101
96
  as="label"
@@ -15,7 +15,6 @@ const FormStateDropdown = ({
15
15
  const placeholder =
16
16
  countryCode === "US" ? placeHolderOptionUS : placeHolderOption;
17
17
  const options = [placeholder, ...getOptions(countryCode)];
18
-
19
18
  return (
20
19
  <FormSelect
21
20
  options={options}
@@ -39,7 +39,6 @@ const FormWrapper = ({
39
39
  countryCode={countryCode}
40
40
  labelTextWhenNoError={labelTextWhenNoError}
41
41
  showErrors={showErrors}
42
- isRequired={true}
43
42
  />
44
43
  );
45
44
 
@@ -58,7 +58,6 @@ const AddressForm = ({
58
58
  labelTextWhenNoError="Country"
59
59
  errorMessages={countryErrorMessages}
60
60
  field={fields.country}
61
- isRequired={true}
62
61
  onChange={value => {
63
62
  actions.fields.country.set(value);
64
63
  // temporary measure to not dirty fields until
@@ -72,6 +71,7 @@ const AddressForm = ({
72
71
  }}
73
72
  showErrors={showErrors}
74
73
  dataQa="Country"
74
+ isRequired={true}
75
75
  />
76
76
  <FormInput
77
77
  labelTextWhenNoError="Address"
@@ -92,7 +92,6 @@ const AddressForm = ({
92
92
  onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
93
93
  autocompleteValue="address-line2"
94
94
  dataQa="Address Line 2"
95
- isRequired={false}
96
95
  />
97
96
  <FormInput
98
97
  labelTextWhenNoError="City"
@@ -19,7 +19,8 @@ const EmailForm = ({
19
19
  handleSubmit = noop,
20
20
  showWalletCheckbox,
21
21
  saveToWallet,
22
- walletCheckboxMarked
22
+ walletCheckboxMarked,
23
+ isRequired = false
23
24
  }) => {
24
25
  if (clearOnDismount) {
25
26
  useEffect(() => () => actions.form.clear(), []);
@@ -48,6 +49,7 @@ const EmailForm = ({
48
49
  isEmail
49
50
  autocompleteValue="email"
50
51
  dataQa="Email address"
52
+ isRequired={isRequired}
51
53
  />
52
54
  {showWalletCheckbox && (
53
55
  <Checkbox
@@ -108,8 +108,8 @@ const PaymentFormACH = ({
108
108
  fieldActions={actions.fields.confirmRoutingNumber}
109
109
  showErrors={showErrors}
110
110
  onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
111
- isRequired={true}
112
111
  isNum
112
+ isRequired={true}
113
113
  />
114
114
  <FormInput
115
115
  labelTextWhenNoError="Account number"
@@ -118,7 +118,6 @@ const PaymentFormACH = ({
118
118
  field={fields.accountNumber}
119
119
  fieldActions={actions.fields.accountNumber}
120
120
  showErrors={showErrors}
121
- isRequired={true}
122
121
  isNum
123
122
  helperModal={() => (
124
123
  <AccountAndRoutingModal
@@ -132,22 +131,22 @@ const PaymentFormACH = ({
132
131
  />
133
132
  )}
134
133
  onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
134
+ isRequired={true}
135
135
  />
136
136
  <FormInput
137
137
  labelTextWhenNoError="Confirm account number"
138
138
  dataQa="Confirm account number"
139
- isRequired={true}
140
139
  errorMessages={confirmAccountNumberErrors}
141
140
  field={fields.confirmAccountNumber}
142
141
  fieldActions={actions.fields.confirmAccountNumber}
143
142
  showErrors={showErrors}
144
143
  onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
144
+ isRequired={true}
145
145
  isNum
146
146
  />
147
147
  {allowBankAccountType && (
148
148
  <FormSelect
149
149
  labelTextWhenNoError="Account type"
150
- isRequired={true}
151
150
  dataQa="Account type"
152
151
  options={[
153
152
  { text: "Select account type", value: "" },
@@ -158,6 +157,7 @@ const PaymentFormACH = ({
158
157
  showErrors={showErrors}
159
158
  errorMessages={accountTypeErrors}
160
159
  field={fields.accountType}
160
+ isRequired={true}
161
161
  />
162
162
  )}
163
163
  {!hideDefaultPayment && (
@@ -98,7 +98,6 @@ const PaymentFormCard = ({
98
98
  {!hideZipCode && (
99
99
  <CountryDropdown
100
100
  labelTextWhenNoError="Country"
101
- isRequired={true}
102
101
  errorMessages={countryErrorMessages}
103
102
  field={fields.country}
104
103
  onChange={value => {
@@ -125,7 +124,6 @@ const PaymentFormCard = ({
125
124
  isRequired={true}
126
125
  />
127
126
  <FormInput
128
- isRequired={true}
129
127
  labelTextWhenNoError="Credit card number"
130
128
  dataQa="Credit card number"
131
129
  errorMessages={creditCardNumberErrors}
@@ -136,6 +134,7 @@ const PaymentFormCard = ({
136
134
  onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
137
135
  isNum
138
136
  autocompleteValue="cc-number"
137
+ isRequired={true}
139
138
  />
140
139
  <FormInputRow
141
140
  breakpoint={isMobile ? "1000rem" : "21rem"}
@@ -19,7 +19,8 @@ const PhoneForm = ({
19
19
  handleSubmit = noop,
20
20
  showWalletCheckbox,
21
21
  saveToWallet,
22
- walletCheckboxMarked
22
+ walletCheckboxMarked,
23
+ isRequired = false
23
24
  }) => {
24
25
  if (clearOnDismount) {
25
26
  useEffect(() => () => actions.form.clear(), []);
@@ -43,6 +44,7 @@ const PhoneForm = ({
43
44
  autocompleteValue="tel-national"
44
45
  dataQa="Phone number"
45
46
  isNum={true}
47
+ isRequired={isRequired}
46
48
  />
47
49
  {showWalletCheckbox && (
48
50
  <Checkbox
@@ -7,7 +7,7 @@ import { noop } from "../../../util/general";
7
7
 
8
8
  const DefaultHeading = styled.div`
9
9
  font-size: 0.875rem;
10
- color: ${colors.CHARADE_GREY};
10
+ color: ${colors.CHARADE_GREY}
11
11
  margin: 0;
12
12
  padding: 8px 0px;
13
13
  `;
@@ -29,8 +29,7 @@ const RadioGroup = ({
29
29
  handleChange = noop, // optional, for custom event handling in ingesting app
30
30
  // redux-freeform props - this is similar to how FormInput works, duplicated because the radio input is hidden for styling overrides
31
31
  field,
32
- fieldActions,
33
- isRequired = false
32
+ fieldActions
34
33
  }) => {
35
34
  const setValue = value => fieldActions.set(value);
36
35
  return (
@@ -38,7 +37,6 @@ const RadioGroup = ({
38
37
  role="radiogroup"
39
38
  aria-labelledby={`radio-group-${groupName}-heading`}
40
39
  $extraStyles={extraStyles}
41
- required={isRequired}
42
40
  >
43
41
  {Heading !== null && Heading}
44
42
  <Stack childGap="4px">
@@ -53,7 +51,6 @@ const RadioGroup = ({
53
51
  field={field}
54
52
  config={config}
55
53
  aria-invalid={field.dirty && field.hasErrors}
56
- isRequired={isRequired}
57
54
  />
58
55
  ))}
59
56
  </Stack>
@@ -33,7 +33,6 @@ export const radioGroup = () => (
33
33
  fieldActions={{
34
34
  set: () => noop()
35
35
  }}
36
- isRequired={true}
37
36
  />
38
37
  );
39
38
 
@@ -47,7 +47,8 @@ const RadioSection = ({
47
47
  initiallyOpen = true,
48
48
  openHeight = "auto",
49
49
  containerStyles = "",
50
- ariaDescribedBy
50
+ ariaDescribedBy,
51
+ isSectionRequired = false
51
52
  }) => {
52
53
  const handleKeyDown = (id, e) => {
53
54
  if (e?.keyCode === 13 || e?.keyCode === 32) {
@@ -105,7 +106,11 @@ const RadioSection = ({
105
106
  borderRadius="4px"
106
107
  extraStyles={containerStyles}
107
108
  >
108
- <Stack childGap="0" role="radiogroup">
109
+ <Stack
110
+ childGap="0"
111
+ aria-role="radiogroup"
112
+ aria-required={isSectionRequired}
113
+ >
109
114
  {sections
110
115
  .filter(section => !section.hidden)
111
116
  .map(section => (
@@ -124,6 +129,7 @@ const RadioSection = ({
124
129
  role="radio"
125
130
  aria-checked={openSection === section.id}
126
131
  aria-disabled={section.disabled}
132
+ aria-required={section?.required}
127
133
  >
128
134
  <Stack childGap="0">
129
135
  <Box
@@ -177,7 +183,7 @@ const RadioSection = ({
177
183
  : () => toggleOpenSection(section.id)
178
184
  }
179
185
  tabIndex="-1"
180
- required={section?.required}
186
+ isRequired={section?.required}
181
187
  />
182
188
  </Box>
183
189
  )}
@@ -66,6 +66,7 @@ export const radioSection = () => {
66
66
  openSection={openSection}
67
67
  staggeredAnimation={boolean("staggeredAnimation", false, "props")}
68
68
  sections={sections}
69
+ isSectionRequired={true}
69
70
  />
70
71
  );
71
72
  };
@@ -20,7 +20,7 @@ const RadioButton = ({
20
20
  themeValues,
21
21
  ariaLabelledBy = "",
22
22
  ariaLabel = null,
23
- required = false
23
+ isRequired = false
24
24
  }) => {
25
25
  const buttonBorder = {
26
26
  onFocused: {
@@ -91,11 +91,10 @@ const RadioButton = ({
91
91
  type="radio"
92
92
  id={`radio-${name}`}
93
93
  disabled={disabled}
94
- required={required}
95
- aria-required={required}
96
94
  onClick={toggleRadio}
97
95
  aria-describedby={ariaDescribedBy}
98
96
  tabIndex="-1"
97
+ required={isRequired}
99
98
  {...extraProps}
100
99
  />
101
100
  <Motion