@thecb/components 12.1.0-beta.15 → 12.1.0-beta.16

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": "12.1.0-beta.15",
3
+ "version": "12.1.0-beta.16",
4
4
  "description": "Common lib for CityBase react components",
5
5
  "main": "dist/index.cjs.js",
6
6
  "typings": "dist/index.d.ts",
@@ -27,10 +27,7 @@ const Alert = ({
27
27
  contentFullHeight = false,
28
28
  ariaRole = null,
29
29
  ariaAtomic = true,
30
- ariaLive = "polite",
31
- ariaLabelledBy = null,
32
- headingAs = null,
33
- headingId = null
30
+ ariaLive = "polite"
34
31
  }) => {
35
32
  const Icon = AlertIcons[variant];
36
33
  let contentPadding = maxContentWidth
@@ -63,8 +60,6 @@ const Alert = ({
63
60
  variant={enableSmallText ? "pS" : "p"}
64
61
  color={themeValues.text}
65
62
  weight="600"
66
- {...(headingAs ? { as: headingAs } : {})}
67
- {...(headingId ? { id: headingId } : {})}
68
63
  >
69
64
  {heading}
70
65
  </Text>
@@ -112,9 +107,6 @@ const Alert = ({
112
107
  aria-live={ariaLive}
113
108
  {...(ariaAtomic !== null ? { "aria-atomic": ariaAtomic } : {})}
114
109
  {...(ariaRole !== null ? { role: ariaRole } : {})}
115
- {...(ariaLabelledBy !== null
116
- ? { "aria-labelledby": ariaLabelledBy }
117
- : {})}
118
110
  >
119
111
  {maxContentWidth ? (
120
112
  <Center maxWidth={maxContentWidth}>{content}</Center>
@@ -19,12 +19,6 @@ export interface AlertProps {
19
19
  innerContentPadding?: string;
20
20
  iconPadding?: string;
21
21
  contentFullHeight?: boolean;
22
- ariaRole?: string;
23
- ariaAtomic?: boolean;
24
- ariaLive?: string;
25
- ariaLabelledBy?: string;
26
- headingAs?: string;
27
- headingId?: string;
28
22
  }
29
23
 
30
24
  export const Alert: React.FC<Expand<AlertProps> &
@@ -2,7 +2,7 @@ import React, { useState, forwardRef } from "react";
2
2
  import styled, { css } from "styled-components";
3
3
  import { fallbackValues } from "./Checkbox.theme";
4
4
  import { noop } from "../../../util/general";
5
- import { ENTER } from "../../../constants/keyboard";
5
+ import { SPACE_KEY } from "../../../constants/keyboard";
6
6
  import Box from "../layouts/Box";
7
7
  import Text from "../text";
8
8
  import { themeComponent } from "../../../util/themeUtils";
@@ -110,6 +110,7 @@ const Checkbox = forwardRef(
110
110
  checkboxExtraStyles,
111
111
  hasIconOverride = false,
112
112
  icon: Icon,
113
+ customAriaLabel,
113
114
  ...rest
114
115
  },
115
116
  ref
@@ -117,14 +118,22 @@ const Checkbox = forwardRef(
117
118
  const [focused, setFocused] = useState(false);
118
119
 
119
120
  const handleClick = (e, func) => {
120
- if (e.keyCode === ENTER) {
121
+ if (e.key === SPACE_KEY) {
122
+ e.preventDefault();
121
123
  func();
122
124
  }
123
125
  };
124
126
 
125
- const titleId = title ? `checkboxlabel-${name}` : undefined;
126
- const ariaLabelledById = labelledById ?? titleId;
127
- const ariaLabel = ariaLabelledById ? undefined : name;
127
+ const normalizeName = name ? name.replace(/\s+/g, "-") : "";
128
+ const checkboxId = `checkbox-${normalizeName}`;
129
+ const titleId = title ? `checkboxlabel-${normalizeName}` : undefined;
130
+ const ariaLabelledById = customAriaLabel
131
+ ? undefined
132
+ : labelledById ?? titleId;
133
+ const ariaLabel = ariaLabelledById ? undefined : customAriaLabel ?? name;
134
+ const errorMessageNormalized = error
135
+ ? `${normalizeName}-error-message`
136
+ : undefined;
128
137
 
129
138
  return (
130
139
  <Box
@@ -137,7 +146,7 @@ const Checkbox = forwardRef(
137
146
  aria-invalid={error}
138
147
  aria-label={ariaLabel}
139
148
  aria-labelledby={ariaLabelledById}
140
- aria-describedby={error ? `${name}-error-message` : undefined}
149
+ aria-describedby={errorMessageNormalized}
141
150
  onFocus={() => setFocused(true)}
142
151
  onBlur={() => setFocused(false)}
143
152
  onKeyDown={e => handleClick(e, onChange)}
@@ -155,7 +164,7 @@ const Checkbox = forwardRef(
155
164
  <CheckboxLabelContainer data-qa={dataQa}>
156
165
  <CheckboxContainer data-qa="Checkbox">
157
166
  <HiddenCheckbox
158
- id={`checkbox-${name}`}
167
+ id={checkboxId}
159
168
  disabled={disabled}
160
169
  name={name}
161
170
  checked={checked}
@@ -21,6 +21,7 @@ const meta = {
21
21
  extraStyles: undefined,
22
22
  textExtraStyles: undefined,
23
23
  labelledById: undefined,
24
+ customAriaLabel: undefined,
24
25
  dataQa: null
25
26
  },
26
27
  argTypes: {
@@ -52,6 +53,13 @@ const meta = {
52
53
  type: { summary: "string" }
53
54
  }
54
55
  },
56
+ customAriaLabel: {
57
+ table: {
58
+ description:
59
+ "Overrides the default aria-label derived from `name` or `labelledById`",
60
+ type: { summary: "string" }
61
+ }
62
+ },
55
63
  extraStyles: {
56
64
  table: {
57
65
  type: { summary: "string" }
@@ -147,3 +155,20 @@ export const Hidden = {
147
155
  },
148
156
  render: args => <CheckboxWithHooks {...args} />
149
157
  };
158
+
159
+ export const CustomAriaLabel = {
160
+ args: {
161
+ title: "Save to wallet (optional).",
162
+ customAriaLabel: "Save to wallet (optional).",
163
+ name: "save to wallet"
164
+ },
165
+ render: args => <CheckboxWithHooks {...args} />
166
+ };
167
+
168
+ export const SpacesInName = {
169
+ args: {
170
+ title: "Accept terms and conditions",
171
+ name: "accept terms and conditions"
172
+ },
173
+ render: args => <RequiredCheckbox {...args} />
174
+ };
@@ -14,7 +14,6 @@ import {
14
14
  } from "../../atoms/form-layouts";
15
15
  import AccountAndRoutingModal from "../account-and-routing-modal";
16
16
  import { noop } from "../../../util/general";
17
- import { Cluster, Cover } from "../../atoms/layouts";
18
17
  import TermsAndConditions from "../terms-and-conditions";
19
18
 
20
19
  const PaymentFormACH = ({
@@ -172,29 +171,42 @@ const PaymentFormACH = ({
172
171
  hidden={hideDefaultPayment}
173
172
  />
174
173
  )}
175
- {(showWalletCheckbox || showTerms) && (
176
- <Cluster childGap={"4px"} align="center">
177
- {showWalletCheckbox && (
178
- <Checkbox
179
- name="bank checkbox"
180
- dataQa="Save checking account to wallet"
181
- title="Save checking account to wallet."
182
- checked={walletCheckboxMarked}
183
- onChange={saveToWallet}
184
- />
185
- )}
186
- {showTerms && (
187
- <Cover singleChild>
188
- <TermsAndConditions
189
- version="v2"
190
- showCheckbox={false}
191
- description="View"
192
- terms={termsContent}
193
- initialFocusSelector={".modal-close-button"}
194
- />
195
- </Cover>
196
- )}
197
- </Cluster>
174
+ {showWalletCheckbox && (
175
+ <Checkbox
176
+ name="bank checkbox"
177
+ dataQa="Save checking account to wallet"
178
+ customAriaLabel="Save checking account to wallet (optional)."
179
+ title={
180
+ <>
181
+ Save checking account to wallet (optional).{" "}
182
+ {showTerms && (
183
+ <span
184
+ onClick={e => e.stopPropagation()}
185
+ style={{ display: "inline" }}
186
+ >
187
+ <TermsAndConditions
188
+ version="v2"
189
+ showCheckbox={false}
190
+ description="View "
191
+ terms={termsContent}
192
+ initialFocusSelector={".modal-close-button"}
193
+ />
194
+ </span>
195
+ )}
196
+ </>
197
+ }
198
+ checked={walletCheckboxMarked}
199
+ onChange={saveToWallet}
200
+ />
201
+ )}
202
+ {!showWalletCheckbox && showTerms && (
203
+ <TermsAndConditions
204
+ version="v2"
205
+ showCheckbox={false}
206
+ description="View "
207
+ terms={termsContent}
208
+ initialFocusSelector=".modal-close-button"
209
+ />
198
210
  )}
199
211
  </FormInputColumn>
200
212
  </FormContainer>
@@ -196,29 +196,42 @@ const PaymentFormCard = ({
196
196
  />
197
197
  </Box>
198
198
  )}
199
- {(showWalletCheckbox || showTerms) && (
200
- <Cluster childGap={"4px"} align="center">
201
- {showWalletCheckbox && (
202
- <Checkbox
203
- name="credit card checkbox"
204
- dataQa="Save credit card to wallet"
205
- title="Save credit card to wallet."
206
- checked={walletCheckboxMarked}
207
- onChange={saveToWallet}
208
- />
209
- )}
210
- {showTerms && (
211
- <Cover singleChild>
212
- <TermsAndConditions
213
- version="v2"
214
- showCheckbox={false}
215
- description="View"
216
- terms={termsContent}
217
- initialFocusSelector={".modal-close-button"}
218
- />
219
- </Cover>
220
- )}
221
- </Cluster>
199
+ {showWalletCheckbox && (
200
+ <Checkbox
201
+ name="credit card checkbox"
202
+ dataQa="Save credit card to wallet"
203
+ customAriaLabel="Save credit card to wallet (optional)."
204
+ title={
205
+ <>
206
+ Save credit card to wallet (optional).{" "}
207
+ {showTerms && (
208
+ <span
209
+ onClick={e => e.stopPropagation()}
210
+ style={{ display: "inline" }}
211
+ >
212
+ <TermsAndConditions
213
+ version="v2"
214
+ showCheckbox={false}
215
+ description="View "
216
+ terms={termsContent}
217
+ initialFocusSelector={".modal-close-button"}
218
+ />
219
+ </span>
220
+ )}
221
+ </>
222
+ }
223
+ checked={walletCheckboxMarked}
224
+ onChange={saveToWallet}
225
+ />
226
+ )}
227
+ {!showWalletCheckbox && showTerms && (
228
+ <TermsAndConditions
229
+ version="v2"
230
+ showCheckbox={false}
231
+ description="View "
232
+ terms={termsContent}
233
+ initialFocusSelector=".modal-close-button"
234
+ />
222
235
  )}
223
236
  </FormInputColumn>
224
237
  </FormContainer>
@@ -1,4 +1,5 @@
1
1
  import React, { useState } from "react";
2
+ import styled from "styled-components";
2
3
  import Checkbox from "../../atoms/checkbox";
3
4
  import { Box, Stack, Cluster } from "../../atoms/layouts";
4
5
  import Text from "../../atoms/text";
@@ -12,6 +13,15 @@ import { generateShadows } from "../../../util/generateShadows";
12
13
  import { useScrollTo } from "../../../hooks";
13
14
 
14
15
  const TermsAndConditionsTitleDivId = "terms-and-conditions-title";
16
+ const InlineTermsWrapper = styled.span`
17
+ display: inline;
18
+ > div {
19
+ display: inline;
20
+ }
21
+ .modal-trigger {
22
+ display: inline !important;
23
+ }
24
+ `;
15
25
 
16
26
  const TermsAndConditionsControlV2 = ({
17
27
  showCheckbox = true,
@@ -42,6 +52,25 @@ const TermsAndConditionsControlV2 = ({
42
52
  }
43
53
  };
44
54
 
55
+ if (!showCheckbox && displayInline) {
56
+ return (
57
+ <InlineTermsWrapper id={TermsAndConditionsTitleDivId}>
58
+ {description && <Text color={CHARADE_GREY}>{description}</Text>}
59
+ {terms && (
60
+ <TermsAndConditionsModal
61
+ link={linkText}
62
+ terms={terms}
63
+ isOpen={showTerms}
64
+ toggleOpen={toggleTerms}
65
+ linkVariant={modalVariant}
66
+ title={modalTitle}
67
+ initialFocusSelector={initialFocusSelector}
68
+ />
69
+ )}
70
+ </InlineTermsWrapper>
71
+ );
72
+ }
73
+
45
74
  return (
46
75
  <Box
47
76
  padding={displayInline ? "0" : "1.5rem"}
@@ -0,0 +1,172 @@
1
+ import TermsAndConditionsControlV2 from "./TermsAndConditionsControlV2";
2
+ import React, { useState } from "react";
3
+
4
+ const sampleTerms =
5
+ "By using this service, you agree to the terms and conditions set forth by the provider. All payments are subject to review.";
6
+
7
+ const meta = {
8
+ title: "Molecules/TermsAndConditionsControlV2",
9
+ component: TermsAndConditionsControlV2,
10
+ parameters: {
11
+ layout: "centered"
12
+ },
13
+ tags: ["!autodocs"],
14
+ args: {
15
+ showCheckbox: true,
16
+ isChecked: false,
17
+ hasError: false,
18
+ errorMessage: "Please accept Terms and Conditions",
19
+ description: "I agree to the",
20
+ linkText: "Terms and Conditions",
21
+ terms: sampleTerms,
22
+ id: "terms-and-conditions",
23
+ displayInline: true,
24
+ modalVariant: "default",
25
+ checkboxMargin: "4px 8px 4px 4px",
26
+ modalTitle: "Terms and Conditions",
27
+ initialFocusSelector: ".modal-close-button",
28
+ isRequired: false
29
+ },
30
+ argTypes: {
31
+ showCheckbox: {
32
+ description: "Whether to show the checkbox",
33
+ table: {
34
+ type: { summary: "boolean" },
35
+ defaultValue: { summary: true }
36
+ }
37
+ },
38
+ isChecked: {
39
+ table: {
40
+ type: { summary: "boolean" },
41
+ defaultValue: { summary: false }
42
+ }
43
+ },
44
+ hasError: {
45
+ description: "Whether the checkbox is in an error state",
46
+ table: {
47
+ type: { summary: "boolean" },
48
+ defaultValue: { summary: false }
49
+ }
50
+ },
51
+ errorMessage: {
52
+ table: {
53
+ type: { summary: "string" },
54
+ defaultValue: { summary: "Please accept Terms and Conditions" }
55
+ }
56
+ },
57
+ description: {
58
+ description: "Text displayed before the terms link",
59
+ table: {
60
+ type: { summary: "string" },
61
+ defaultValue: { summary: '""' }
62
+ }
63
+ },
64
+ linkText: {
65
+ description: "Text for the modal trigger link",
66
+ table: {
67
+ type: { summary: "string" },
68
+ defaultValue: { summary: "Terms and Conditions" }
69
+ }
70
+ },
71
+ terms: {
72
+ description: "Terms content displayed inside the modal",
73
+ table: {
74
+ type: { summary: "string" }
75
+ }
76
+ },
77
+ displayInline: {
78
+ description:
79
+ "When true with showCheckbox=false, renders inline terms using InlineTermsWrapper",
80
+ table: {
81
+ type: { summary: "boolean" },
82
+ defaultValue: { summary: true }
83
+ }
84
+ },
85
+ modalVariant: {
86
+ table: {
87
+ type: { summary: "string" },
88
+ defaultValue: { summary: "default" }
89
+ }
90
+ },
91
+ id: {
92
+ table: {
93
+ type: { summary: "string" },
94
+ defaultValue: { summary: "terms-and-conditions" }
95
+ }
96
+ },
97
+ checkboxMargin: {
98
+ table: {
99
+ type: { summary: "string" },
100
+ defaultValue: { summary: "4px 8px 4px 4px" }
101
+ }
102
+ },
103
+ modalTitle: {
104
+ table: {
105
+ type: { summary: "string" },
106
+ defaultValue: { summary: "Terms and Conditions" }
107
+ }
108
+ },
109
+ initialFocusSelector: {
110
+ table: {
111
+ type: { summary: "string" },
112
+ defaultValue: { summary: '""' }
113
+ }
114
+ },
115
+ isRequired: {
116
+ table: {
117
+ type: { summary: "boolean" },
118
+ defaultValue: { summary: false }
119
+ }
120
+ }
121
+ }
122
+ };
123
+
124
+ const TermsWithCheckbox = props => {
125
+ const [isChecked, setIsChecked] = useState(false);
126
+
127
+ return (
128
+ <TermsAndConditionsControlV2
129
+ {...props}
130
+ isChecked={isChecked}
131
+ onCheck={() => setIsChecked(!isChecked)}
132
+ />
133
+ );
134
+ };
135
+
136
+ export default meta;
137
+
138
+ export const Default = {
139
+ render: args => <TermsWithCheckbox {...args} />
140
+ };
141
+
142
+ export const InlineWithoutCheckbox = {
143
+ args: {
144
+ showCheckbox: false,
145
+ displayInline: true,
146
+ description: "View "
147
+ },
148
+ render: args => <TermsAndConditionsControlV2 {...args} />
149
+ };
150
+
151
+ export const NonInlineWithoutCheckbox = {
152
+ args: {
153
+ showCheckbox: false,
154
+ displayInline: false,
155
+ description: "View "
156
+ },
157
+ render: args => (
158
+ <>
159
+ <span>Non-inline variants have a gray background by default.</span>
160
+ <TermsAndConditionsControlV2 {...args} />
161
+ <span>And they do not render inline with the surrounding text.</span>
162
+ </>
163
+ )
164
+ };
165
+
166
+ export const WithError = {
167
+ args: {
168
+ hasError: true,
169
+ isRequired: true
170
+ },
171
+ render: args => <TermsWithCheckbox {...args} hasError={!args.isChecked} />
172
+ };
@@ -5,3 +5,4 @@ export const ARROW_DOWN = 40;
5
5
  export const ENTER = 13;
6
6
  export const ESCAPE = 27;
7
7
  export const SPACEBAR = 32;
8
+ export const SPACE_KEY = " ";