@thecb/components 6.0.0-beta.2 → 6.0.0-beta.5
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
|
@@ -74,10 +74,11 @@ const DropdownItemWrapper = styled.div`
|
|
|
74
74
|
|
|
75
75
|
const SearchInput = styled.input`
|
|
76
76
|
border: none;
|
|
77
|
-
background-color: ${({ themeValues }) =>
|
|
78
|
-
themeValues.hoverColor && themeValues.hoverColor};
|
|
77
|
+
background-color: ${({ themeValues, isOpen }) =>
|
|
78
|
+
isOpen ? themeValues.hoverColor && themeValues.hoverColor : "WHITE"};
|
|
79
79
|
font-size: 16px;
|
|
80
80
|
height: 24px;
|
|
81
|
+
min-width: 75%;
|
|
81
82
|
`;
|
|
82
83
|
|
|
83
84
|
const Dropdown = ({
|
|
@@ -93,12 +94,15 @@ const Dropdown = ({
|
|
|
93
94
|
maxHeight,
|
|
94
95
|
widthFitOptions = false,
|
|
95
96
|
disabled,
|
|
96
|
-
hasTitles = false
|
|
97
|
+
hasTitles = false,
|
|
98
|
+
autoEraseTypeAhead = true, // legacy behavior as of 05/22
|
|
99
|
+
ariaLabelledby
|
|
97
100
|
}) => {
|
|
98
101
|
const [inputValue, setInputValue] = useState("");
|
|
99
102
|
const [optionsState, setOptionsState] = useState([]);
|
|
100
103
|
const [filteredOptions, setFilteredOptions] = useState([]);
|
|
101
104
|
const [optionsChanged, setOptionsChanged] = useState(true);
|
|
105
|
+
const [selectedRef, setSelectedRef] = useState(undefined);
|
|
102
106
|
|
|
103
107
|
if (optionsState !== options) {
|
|
104
108
|
setOptionsState(options);
|
|
@@ -159,10 +163,18 @@ const Dropdown = ({
|
|
|
159
163
|
break;
|
|
160
164
|
case "Backspace" || "Delete":
|
|
161
165
|
e.preventDefault();
|
|
162
|
-
console.log("input value is", inputValue);
|
|
163
|
-
console.log("new input value will be", inputValue.slice(0, -1));
|
|
164
166
|
setInputValue(inputValue.slice(0, -1));
|
|
165
167
|
break;
|
|
168
|
+
case "Home":
|
|
169
|
+
e.preventDefault();
|
|
170
|
+
optionRefs.current[0].current.focus();
|
|
171
|
+
break;
|
|
172
|
+
case "End":
|
|
173
|
+
e.preventDefault();
|
|
174
|
+
optionRefs.current[
|
|
175
|
+
optionRefs?.current?.length ?? 0 - 1
|
|
176
|
+
].current.focus();
|
|
177
|
+
break;
|
|
166
178
|
}
|
|
167
179
|
if ((keyCode > 64 && keyCode < 91) || keyCode == 32 || keyCode == 189) {
|
|
168
180
|
e.preventDefault();
|
|
@@ -175,8 +187,13 @@ const Dropdown = ({
|
|
|
175
187
|
"option refs in isopen useffect",
|
|
176
188
|
optionRefs.current[0].current
|
|
177
189
|
);
|
|
190
|
+
console.log("selected refs in isopen useffect", selectedRef);
|
|
178
191
|
console.log("value in isopen useffect", value);
|
|
179
|
-
if (isOpen &&
|
|
192
|
+
if (isOpen && selectedRef !== undefined) {
|
|
193
|
+
// WAI-ARIA requires the selected option to receive focus
|
|
194
|
+
selectedRef.current.focus();
|
|
195
|
+
} else if (isOpen && optionRefs.current[0].current) {
|
|
196
|
+
// If no selected option, first option receives focus
|
|
180
197
|
optionRefs.current[0].current.focus();
|
|
181
198
|
}
|
|
182
199
|
clearTimeout(timer);
|
|
@@ -184,8 +201,10 @@ const Dropdown = ({
|
|
|
184
201
|
}, [isOpen]);
|
|
185
202
|
|
|
186
203
|
useEffect(() => {
|
|
187
|
-
|
|
188
|
-
|
|
204
|
+
if (autoEraseTypeAhead) {
|
|
205
|
+
clearTimeout(timer);
|
|
206
|
+
setTimer(setTimeout(() => setInputValue(""), 2000));
|
|
207
|
+
}
|
|
189
208
|
setFilteredOptions(
|
|
190
209
|
options.filter(
|
|
191
210
|
option =>
|
|
@@ -219,6 +238,10 @@ const Dropdown = ({
|
|
|
219
238
|
width="100%"
|
|
220
239
|
hoverStyles={`background-color: ${themeValues.hoverColor};`}
|
|
221
240
|
aria-expanded={isOpen}
|
|
241
|
+
role="combobox"
|
|
242
|
+
aria-owns={`${ariaLabelledby}_listbox`}
|
|
243
|
+
aria-haspopup="listbox"
|
|
244
|
+
aria-labelledby={ariaLabelledby}
|
|
222
245
|
extraStyles={
|
|
223
246
|
disabled &&
|
|
224
247
|
`color: #6e727e;
|
|
@@ -252,26 +275,20 @@ const Dropdown = ({
|
|
|
252
275
|
`}
|
|
253
276
|
>
|
|
254
277
|
<Stack direction="row" bottomItem={2}>
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
pointer-events: none;`
|
|
270
|
-
}
|
|
271
|
-
>
|
|
272
|
-
{getSelection()}
|
|
273
|
-
</Text>
|
|
274
|
-
)}
|
|
278
|
+
<SearchInput
|
|
279
|
+
aria-label={getSelection()}
|
|
280
|
+
placeholder={getSelection()}
|
|
281
|
+
value={inputValue}
|
|
282
|
+
onChange={noop}
|
|
283
|
+
themeValues={themeValues}
|
|
284
|
+
role="searchbox"
|
|
285
|
+
type="text"
|
|
286
|
+
aria-multiline="false"
|
|
287
|
+
aria-autocomplete="list"
|
|
288
|
+
aria-controls={`${ariaLabelledby}_listbox`}
|
|
289
|
+
aria-activedescendant="selected_option"
|
|
290
|
+
isOpen={isOpen}
|
|
291
|
+
/>
|
|
275
292
|
<IconWrapper open={isOpen}>
|
|
276
293
|
<DropdownIcon />
|
|
277
294
|
</IconWrapper>
|
|
@@ -284,35 +301,50 @@ const Dropdown = ({
|
|
|
284
301
|
ref={dropdownRef}
|
|
285
302
|
widthFitOptions={widthFitOptions}
|
|
286
303
|
tabIndex={0}
|
|
304
|
+
role="listbox"
|
|
305
|
+
id={`${ariaLabelledby}_listbox`}
|
|
287
306
|
>
|
|
288
307
|
<Stack childGap="0">
|
|
289
|
-
{filteredOptions.map((choice, i) =>
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
? WHITE
|
|
311
|
-
: disabledValues.includes(choice.value)
|
|
312
|
-
? STORM_GREY
|
|
313
|
-
: MINESHAFT_GREY
|
|
308
|
+
{filteredOptions.map((choice, i) => {
|
|
309
|
+
if (
|
|
310
|
+
choice.value === value &&
|
|
311
|
+
selectedRef !== optionRefs.current[i]
|
|
312
|
+
) {
|
|
313
|
+
setSelectedRef(optionRefs.current[i]);
|
|
314
|
+
}
|
|
315
|
+
return (
|
|
316
|
+
<DropdownItemWrapper
|
|
317
|
+
id={choice.value === value ? "selected_option" : choice.value}
|
|
318
|
+
key={choice.value}
|
|
319
|
+
ref={optionRefs.current[i]}
|
|
320
|
+
as="button"
|
|
321
|
+
tabIndex={-1}
|
|
322
|
+
onClick={
|
|
323
|
+
disabledValues.includes(choice.value)
|
|
324
|
+
? evt => evt.preventDefault()
|
|
325
|
+
: () => {
|
|
326
|
+
setSelectedRef(optionRefs.current[i]);
|
|
327
|
+
onSelect(choice.value);
|
|
328
|
+
}
|
|
314
329
|
}
|
|
315
|
-
|
|
330
|
+
selected={choice.value === value}
|
|
331
|
+
aria-selected={choice.value === value}
|
|
332
|
+
disabled={disabledValues.includes(choice.value)}
|
|
333
|
+
data-qa={choice.text}
|
|
334
|
+
themeValues={themeValues}
|
|
335
|
+
title={hasTitles ? choice.text : null}
|
|
336
|
+
role="option"
|
|
337
|
+
>
|
|
338
|
+
<Text
|
|
339
|
+
variant="p"
|
|
340
|
+
color={
|
|
341
|
+
choice.value === value
|
|
342
|
+
? WHITE
|
|
343
|
+
: disabledValues.includes(choice.value)
|
|
344
|
+
? STORM_GREY
|
|
345
|
+
: MINESHAFT_GREY
|
|
346
|
+
}
|
|
347
|
+
extraStyles={`padding-left: 16px;
|
|
316
348
|
cursor: ${
|
|
317
349
|
disabledValues.includes(choice.value)
|
|
318
350
|
? "default"
|
|
@@ -321,11 +353,12 @@ const Dropdown = ({
|
|
|
321
353
|
white-space: nowrap;
|
|
322
354
|
overflow: hidden;
|
|
323
355
|
text-overflow: ellipsis;`}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
356
|
+
>
|
|
357
|
+
{choice.text}
|
|
358
|
+
</Text>
|
|
359
|
+
</DropdownItemWrapper>
|
|
360
|
+
);
|
|
361
|
+
})}
|
|
329
362
|
</Stack>
|
|
330
363
|
</DropdownContentWrapper>
|
|
331
364
|
) : (
|
|
@@ -58,7 +58,7 @@ const FormSelect = ({
|
|
|
58
58
|
</Cluster>
|
|
59
59
|
</Box>
|
|
60
60
|
<Dropdown
|
|
61
|
-
|
|
61
|
+
ariaLabelledby={labelTextWhenNoError.replace(/\s+/g, "-")}
|
|
62
62
|
maxHeight={dropdownMaxHeight}
|
|
63
63
|
hasTitles={hasTitles}
|
|
64
64
|
placeholder={options[0] ? options[0].text : ""}
|