@thecb/components 6.0.0-beta.1 → 6.0.0-beta.10
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/dist/index.cjs.js +134 -78
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +134 -78
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/atoms/country-dropdown/CountryDropdown.js +1 -0
- package/src/components/atoms/dropdown/Dropdown.js +154 -123
- package/src/components/atoms/form-select/FormSelect.js +4 -2
- package/src/components/atoms/state-province-dropdown/StateProvinceDropdown.js +1 -0
package/package.json
CHANGED
|
@@ -72,14 +72,6 @@ const DropdownItemWrapper = styled.div`
|
|
|
72
72
|
}
|
|
73
73
|
`;
|
|
74
74
|
|
|
75
|
-
const SearchInput = styled.input`
|
|
76
|
-
border: none;
|
|
77
|
-
background-color: ${({ themeValues }) =>
|
|
78
|
-
themeValues.hoverColor && themeValues.hoverColor};
|
|
79
|
-
font-size: 16px;
|
|
80
|
-
height: 24px;
|
|
81
|
-
`;
|
|
82
|
-
|
|
83
75
|
const Dropdown = ({
|
|
84
76
|
placeholder,
|
|
85
77
|
options,
|
|
@@ -93,12 +85,17 @@ const Dropdown = ({
|
|
|
93
85
|
maxHeight,
|
|
94
86
|
widthFitOptions = false,
|
|
95
87
|
disabled,
|
|
96
|
-
hasTitles = false
|
|
88
|
+
hasTitles = false,
|
|
89
|
+
autoEraseTypeAhead = true, // legacy behavior as of 05/22
|
|
90
|
+
ariaLabelledby,
|
|
91
|
+
autocompleteValue = "" // autofill item for browsers, like country-name or address-level1 for state
|
|
97
92
|
}) => {
|
|
98
93
|
const [inputValue, setInputValue] = useState("");
|
|
99
94
|
const [optionsState, setOptionsState] = useState([]);
|
|
100
95
|
const [filteredOptions, setFilteredOptions] = useState([]);
|
|
101
96
|
const [optionsChanged, setOptionsChanged] = useState(true);
|
|
97
|
+
const [selectedRef, setSelectedRef] = useState(undefined);
|
|
98
|
+
const [focusedRef, setFocusedRef] = useState(undefined);
|
|
102
99
|
|
|
103
100
|
if (optionsState !== options) {
|
|
104
101
|
setOptionsState(options);
|
|
@@ -118,6 +115,8 @@ const Dropdown = ({
|
|
|
118
115
|
value ? options.find(option => option.value === value)?.text : placeholder;
|
|
119
116
|
|
|
120
117
|
const onKeyDown = e => {
|
|
118
|
+
console.log("key down event is", e.target);
|
|
119
|
+
console.log("key down event value is", e.target.value);
|
|
121
120
|
const { key, keyCode } = e;
|
|
122
121
|
const focus = document.activeElement;
|
|
123
122
|
console.log("dropdown value is", value);
|
|
@@ -161,15 +160,32 @@ const Dropdown = ({
|
|
|
161
160
|
e.preventDefault();
|
|
162
161
|
setInputValue(inputValue.slice(0, -1));
|
|
163
162
|
break;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
163
|
+
case "Home":
|
|
164
|
+
e.preventDefault();
|
|
165
|
+
optionRefs.current[0].current.focus();
|
|
166
|
+
break;
|
|
167
|
+
case "End":
|
|
168
|
+
e.preventDefault();
|
|
169
|
+
optionRefs.current[
|
|
170
|
+
optionRefs?.current?.length ?? 0 - 1
|
|
171
|
+
].current.focus();
|
|
172
|
+
break;
|
|
168
173
|
}
|
|
169
174
|
};
|
|
170
175
|
|
|
171
176
|
useEffect(() => {
|
|
172
|
-
|
|
177
|
+
console.log("option refs in isopen useffect", optionRefs);
|
|
178
|
+
console.log(
|
|
179
|
+
"option ref current in isopen useffect",
|
|
180
|
+
optionRefs.current[0].current
|
|
181
|
+
);
|
|
182
|
+
console.log("selected refs in isopen useffect", selectedRef);
|
|
183
|
+
console.log("value in isopen useffect", value);
|
|
184
|
+
if (isOpen && selectedRef !== undefined && selectedRef.current !== null) {
|
|
185
|
+
// WAI-ARIA requires the selected option to receive focus
|
|
186
|
+
selectedRef.current.focus();
|
|
187
|
+
} else if (isOpen && optionRefs.current[0].current) {
|
|
188
|
+
// If no selected option, first option receives focus
|
|
173
189
|
optionRefs.current[0].current.focus();
|
|
174
190
|
}
|
|
175
191
|
clearTimeout(timer);
|
|
@@ -177,8 +193,10 @@ const Dropdown = ({
|
|
|
177
193
|
}, [isOpen]);
|
|
178
194
|
|
|
179
195
|
useEffect(() => {
|
|
180
|
-
|
|
181
|
-
|
|
196
|
+
if (autoEraseTypeAhead) {
|
|
197
|
+
clearTimeout(timer);
|
|
198
|
+
setTimer(setTimeout(() => setInputValue(""), 3000));
|
|
199
|
+
}
|
|
182
200
|
setFilteredOptions(
|
|
183
201
|
options.filter(
|
|
184
202
|
option =>
|
|
@@ -195,6 +213,8 @@ const Dropdown = ({
|
|
|
195
213
|
!disabledValues.includes(filteredOptions[0].value) &&
|
|
196
214
|
filteredOptions[0].text != placeholder
|
|
197
215
|
) {
|
|
216
|
+
console.log("filtered options are", filteredOptions);
|
|
217
|
+
console.log("option refs are", optionRefs);
|
|
198
218
|
onSelect(filteredOptions[0].value);
|
|
199
219
|
}
|
|
200
220
|
if (optionRefs.current[0].current) {
|
|
@@ -205,107 +225,112 @@ const Dropdown = ({
|
|
|
205
225
|
}, [filteredOptions]);
|
|
206
226
|
|
|
207
227
|
return (
|
|
208
|
-
<
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
: isOpen
|
|
234
|
-
? themeValues.selectedColor
|
|
235
|
-
: GREY_CHATEAU
|
|
236
|
-
}
|
|
237
|
-
borderRadius="2px"
|
|
238
|
-
tabIndex={0}
|
|
239
|
-
dataQa={placeholder}
|
|
240
|
-
extraStyles={`height: 48px;
|
|
241
|
-
${disabled &&
|
|
228
|
+
<Fragment>
|
|
229
|
+
<Stack direction="row" bottomItem={2} extraStyles={`position: relative;`}>
|
|
230
|
+
<Box
|
|
231
|
+
as="input"
|
|
232
|
+
aria-multiline="false"
|
|
233
|
+
aria-autocomplete="list"
|
|
234
|
+
aria-controls={`${ariaLabelledby}_listbox`}
|
|
235
|
+
aria-activedescendant="focused_option"
|
|
236
|
+
aria-owns={`${ariaLabelledby}_listbox`}
|
|
237
|
+
aria-haspopup="listbox"
|
|
238
|
+
aria-labelledby={ariaLabelledby}
|
|
239
|
+
aria-expanded={isOpen}
|
|
240
|
+
autocomplete={autocompleteValue}
|
|
241
|
+
background={isOpen ? themeValues.hoverColor : WHITE}
|
|
242
|
+
borderRadius="2px"
|
|
243
|
+
borderSize="1px"
|
|
244
|
+
borderColor={
|
|
245
|
+
isError
|
|
246
|
+
? ERROR_COLOR
|
|
247
|
+
: isOpen
|
|
248
|
+
? themeValues.selectedColor
|
|
249
|
+
: GREY_CHATEAU
|
|
250
|
+
}
|
|
251
|
+
extraStyles={
|
|
252
|
+
disabled &&
|
|
242
253
|
`color: #6e727e;
|
|
243
|
-
background-color: #f7f7f7;
|
|
244
|
-
pointer-events: none;`}
|
|
245
|
-
`}
|
|
246
|
-
>
|
|
247
|
-
<Stack direction="row" bottomItem={2}>
|
|
248
|
-
{isOpen ? (
|
|
249
|
-
<SearchInput
|
|
250
|
-
aria-label={inputValue || "Dropdown awaiting search value"}
|
|
251
|
-
value={inputValue}
|
|
252
|
-
onChange={noop}
|
|
253
|
-
themeValues={themeValues}
|
|
254
|
-
/>
|
|
255
|
-
) : (
|
|
256
|
-
<Text
|
|
257
|
-
variant="p"
|
|
258
|
-
extraStyles={
|
|
259
|
-
disabled &&
|
|
260
|
-
`color: #6e727e;
|
|
261
254
|
background-color: #f7f7f7;
|
|
262
255
|
pointer-events: none;`
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
256
|
+
}
|
|
257
|
+
hoverStyles={`background-color: ${themeValues.hoverColor};`}
|
|
258
|
+
isOpen={isOpen}
|
|
259
|
+
name={autocompleteValue}
|
|
260
|
+
onKeyDown={onKeyDown}
|
|
261
|
+
onClick={onClick}
|
|
262
|
+
onFocus={onClick}
|
|
263
|
+
onChange={e => {
|
|
264
|
+
console.log("input change event", e.target);
|
|
265
|
+
console.log("input change event value", e.target.value);
|
|
266
|
+
setInputValue(e.target.value);
|
|
267
|
+
}}
|
|
268
|
+
padding="12px"
|
|
269
|
+
placeholder={getSelection()}
|
|
270
|
+
role="combobox"
|
|
271
|
+
themeValues={themeValues}
|
|
272
|
+
title={hasTitles ? getSelection() : null}
|
|
273
|
+
type="text"
|
|
274
|
+
tabIndex={-1}
|
|
275
|
+
value={inputValue}
|
|
276
|
+
width="100%"
|
|
277
|
+
dataQa={placeholder}
|
|
280
278
|
>
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
>
|
|
299
|
-
<Text
|
|
300
|
-
variant="p"
|
|
301
|
-
color={
|
|
302
|
-
choice.value === value
|
|
303
|
-
? WHITE
|
|
304
|
-
: disabledValues.includes(choice.value)
|
|
305
|
-
? STORM_GREY
|
|
306
|
-
: MINESHAFT_GREY
|
|
279
|
+
{isOpen ? (
|
|
280
|
+
<DropdownContentWrapper
|
|
281
|
+
maxHeight={maxHeight}
|
|
282
|
+
open={isOpen}
|
|
283
|
+
ref={dropdownRef}
|
|
284
|
+
widthFitOptions={widthFitOptions}
|
|
285
|
+
tabIndex={0}
|
|
286
|
+
role="listbox"
|
|
287
|
+
id={`${ariaLabelledby}_listbox`}
|
|
288
|
+
>
|
|
289
|
+
<Stack childGap="0">
|
|
290
|
+
{filteredOptions.map((choice, i) => {
|
|
291
|
+
if (
|
|
292
|
+
choice.value === value &&
|
|
293
|
+
selectedRef !== optionRefs.current[i]
|
|
294
|
+
) {
|
|
295
|
+
setSelectedRef(optionRefs.current[i]);
|
|
307
296
|
}
|
|
308
|
-
|
|
297
|
+
return (
|
|
298
|
+
<DropdownItemWrapper
|
|
299
|
+
id={
|
|
300
|
+
focusedRef === optionRefs.current[i]
|
|
301
|
+
? "focused_option"
|
|
302
|
+
: choice.value
|
|
303
|
+
}
|
|
304
|
+
key={choice.value}
|
|
305
|
+
ref={optionRefs.current[i]}
|
|
306
|
+
tabIndex={-1}
|
|
307
|
+
onClick={
|
|
308
|
+
disabledValues.includes(choice.value)
|
|
309
|
+
? evt => evt.preventDefault()
|
|
310
|
+
: () => {
|
|
311
|
+
setSelectedRef(optionRefs.current[i]);
|
|
312
|
+
onSelect(choice.value);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
selected={choice.value === value}
|
|
316
|
+
aria-selected={choice.value === value}
|
|
317
|
+
disabled={disabledValues.includes(choice.value)}
|
|
318
|
+
data-qa={choice.text}
|
|
319
|
+
themeValues={themeValues}
|
|
320
|
+
title={hasTitles ? choice.text : null}
|
|
321
|
+
role="option"
|
|
322
|
+
onFocus={() => setFocusedRef(optionRefs.current[i])}
|
|
323
|
+
>
|
|
324
|
+
<Text
|
|
325
|
+
variant="p"
|
|
326
|
+
color={
|
|
327
|
+
choice.value === value
|
|
328
|
+
? WHITE
|
|
329
|
+
: disabledValues.includes(choice.value)
|
|
330
|
+
? STORM_GREY
|
|
331
|
+
: MINESHAFT_GREY
|
|
332
|
+
}
|
|
333
|
+
extraStyles={`padding-left: 16px;
|
|
309
334
|
cursor: ${
|
|
310
335
|
disabledValues.includes(choice.value)
|
|
311
336
|
? "default"
|
|
@@ -314,17 +339,23 @@ const Dropdown = ({
|
|
|
314
339
|
white-space: nowrap;
|
|
315
340
|
overflow: hidden;
|
|
316
341
|
text-overflow: ellipsis;`}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
342
|
+
>
|
|
343
|
+
{choice.text}
|
|
344
|
+
</Text>
|
|
345
|
+
</DropdownItemWrapper>
|
|
346
|
+
);
|
|
347
|
+
})}
|
|
348
|
+
</Stack>
|
|
349
|
+
</DropdownContentWrapper>
|
|
350
|
+
) : (
|
|
351
|
+
<Fragment />
|
|
352
|
+
)}
|
|
353
|
+
</Box>
|
|
354
|
+
<IconWrapper open={isOpen}>
|
|
355
|
+
<DropdownIcon />
|
|
356
|
+
</IconWrapper>
|
|
357
|
+
</Stack>
|
|
358
|
+
</Fragment>
|
|
328
359
|
);
|
|
329
360
|
};
|
|
330
361
|
|
|
@@ -19,7 +19,8 @@ const FormSelect = ({
|
|
|
19
19
|
disabledValues,
|
|
20
20
|
disabled,
|
|
21
21
|
themeValues,
|
|
22
|
-
hasTitles = false
|
|
22
|
+
hasTitles = false,
|
|
23
|
+
autocompleteValue // autofill item for browsers, like country-name or address-level1 for state
|
|
23
24
|
}) => {
|
|
24
25
|
const [open, setOpen] = useState(false);
|
|
25
26
|
const dropdownRef = useRef(null);
|
|
@@ -58,7 +59,7 @@ const FormSelect = ({
|
|
|
58
59
|
</Cluster>
|
|
59
60
|
</Box>
|
|
60
61
|
<Dropdown
|
|
61
|
-
|
|
62
|
+
ariaLabelledby={labelTextWhenNoError.replace(/\s+/g, "-")}
|
|
62
63
|
maxHeight={dropdownMaxHeight}
|
|
63
64
|
hasTitles={hasTitles}
|
|
64
65
|
placeholder={options[0] ? options[0].text : ""}
|
|
@@ -74,6 +75,7 @@ const FormSelect = ({
|
|
|
74
75
|
}
|
|
75
76
|
onClick={() => setOpen(!open)}
|
|
76
77
|
disabled={disabled}
|
|
78
|
+
autocompleteValue={autocompleteValue}
|
|
77
79
|
/>
|
|
78
80
|
<Stack direction="row" justify="space-between">
|
|
79
81
|
{(field.hasErrors && field.dirty) || (field.hasErrors && showErrors) ? (
|