Added Aria compliance to the selectablelist.
This commit is contained in:
parent
9ae4b54155
commit
5188570f26
@ -12,16 +12,13 @@ export const SelectableList = <T,>(
|
|||||||
): JSX.Element => {
|
): JSX.Element => {
|
||||||
const { items, selectedValue, renderLabel, onSelect } = props;
|
const { items, selectedValue, renderLabel, onSelect } = props;
|
||||||
|
|
||||||
// Track focus state of the list itself
|
|
||||||
const listRef = useRef<HTMLUListElement | null>(null);
|
const listRef = useRef<HTMLUListElement | null>(null);
|
||||||
const isFocusedRef = useRef(false);
|
const isFocusedRef = useRef(false);
|
||||||
|
|
||||||
// One ref per item so we can focus + scroll it
|
|
||||||
const itemRefs = useRef<(HTMLLIElement | null)[]>([]);
|
const itemRefs = useRef<(HTMLLIElement | null)[]>([]);
|
||||||
|
|
||||||
const handleKeyDown = useCallback(
|
const handleKeyDown = useCallback(
|
||||||
(e: React.KeyboardEvent<HTMLUListElement>) => {
|
(e: React.KeyboardEvent<HTMLUListElement>) => {
|
||||||
// Ignore keyboard input unless the list itself is focused
|
|
||||||
if (!isFocusedRef.current) return;
|
if (!isFocusedRef.current) return;
|
||||||
if (!items.length) return;
|
if (!items.length) return;
|
||||||
|
|
||||||
@ -44,9 +41,8 @@ export const SelectableList = <T,>(
|
|||||||
[items, selectedValue, onSelect],
|
[items, selectedValue, onSelect],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Only focus the selected item if the list itself already has focus
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isFocusedRef.current) return; // Do NOT steal focus
|
if (!isFocusedRef.current) return;
|
||||||
if (!selectedValue) return;
|
if (!selectedValue) return;
|
||||||
|
|
||||||
const index = items.indexOf(selectedValue);
|
const index = items.indexOf(selectedValue);
|
||||||
@ -64,6 +60,10 @@ export const SelectableList = <T,>(
|
|||||||
ref={listRef}
|
ref={listRef}
|
||||||
className="selectable-list"
|
className="selectable-list"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
|
role="listbox"
|
||||||
|
aria-activedescendant={
|
||||||
|
selectedValue ? `option-${items.indexOf(selectedValue)}` : undefined
|
||||||
|
}
|
||||||
onFocus={() => (isFocusedRef.current = true)}
|
onFocus={() => (isFocusedRef.current = true)}
|
||||||
onBlur={() => (isFocusedRef.current = false)}
|
onBlur={() => (isFocusedRef.current = false)}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
@ -75,8 +75,11 @@ export const SelectableList = <T,>(
|
|||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
key={index}
|
key={index}
|
||||||
|
id={`option-${index}`}
|
||||||
ref={(el) => (itemRefs.current[index] = el)}
|
ref={(el) => (itemRefs.current[index] = el)}
|
||||||
tabIndex={isSelected ? 0 : -1}
|
tabIndex={isSelected ? 0 : -1}
|
||||||
|
role="option"
|
||||||
|
aria-selected={isSelected}
|
||||||
onClick={() => onSelect(item)}
|
onClick={() => onSelect(item)}
|
||||||
className={className}
|
className={className}
|
||||||
>
|
>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user