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