Skip to content

Commit 8becfac

Browse files
ssherigar-c-eightfoldSannidhi Sherigar
andauthored
fix(dropddown): accessibility fix for dropdown options via Tab and arrow keys (#1084)
* fix(dropddown): accessibility fix for dropdown options via Tab and arrow keys * fix(dropddown): accessibility fix for dropdown options via Tab and arrow keys * fix(dropddown): accessibility fix for dropdown options via Tab and arrow keys * fix(dropddown): review commets resolved --------- Co-authored-by: Sannidhi Sherigar <ssherigar-c@IND456-JD9DVJCV66-MacBook-Pro.local>
1 parent e1595a4 commit 8becfac

File tree

2 files changed

+63
-7
lines changed

2 files changed

+63
-7
lines changed

src/components/Dropdown/Dropdown.tsx

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export const Dropdown: FC<DropdownProps> = React.memo(
5454
closeOnDropdownClick = true,
5555
closeOnReferenceClick = true,
5656
closeOnOutsideClick = true,
57+
shouldCloseOnTab = false,
5758
disabled,
5859
dropdownClassNames,
5960
dropdownStyle,
@@ -124,6 +125,14 @@ export const Dropdown: FC<DropdownProps> = React.memo(
124125
return focusableElements?.[0];
125126
};
126127

128+
const getFocusableItems = (): HTMLElement[] => {
129+
if (!refs.floating.current) return [];
130+
131+
return Array.from(
132+
refs.floating.current.querySelectorAll<HTMLElement>(SELECTORS)
133+
).filter((el) => focusable(el));
134+
};
135+
127136
const focusFirstElement = (): void => {
128137
const elementToFocus: HTMLElement = firstFocusableElement?.();
129138
clearInterval(intervalRef?.current);
@@ -285,23 +294,65 @@ export const Dropdown: FC<DropdownProps> = React.memo(
285294
event?.preventDefault();
286295
toggle(false)(event);
287296
}
297+
if (event?.key === eventKeys.ESCAPE) {
298+
toggle(false)(event);
299+
}
300+
if (event?.key === eventKeys.TAB && mergedVisible && shouldCloseOnTab) {
301+
toggle(false)(event);
302+
}
288303
if (
289-
event?.key === eventKeys.ESCAPE ||
290-
(event?.key === eventKeys.TAB && mergedVisible) ||
291-
(event?.key === eventKeys.TAB &&
292-
event.shiftKey &&
293-
!(event.target as HTMLElement).matches(':focus-within'))
304+
event?.key === eventKeys.TAB &&
305+
event.shiftKey &&
306+
!(event.target as HTMLElement).matches(':focus-within')
294307
) {
295308
toggle(false)(event);
296309
}
297310
};
298311

299312
const handleFloatingKeyDown = (event: React.KeyboardEvent): void => {
300313
if (
301-
event?.key === eventKeys.ESCAPE ||
302-
(event?.key === eventKeys.TAB && mergedVisible)
314+
!event.defaultPrevented &&
315+
(event.key === eventKeys.ARROWDOWN || event.key === eventKeys.ARROWUP)
303316
) {
317+
const items = getFocusableItems();
318+
const currentIndex = items.indexOf(
319+
document.activeElement as HTMLElement
320+
);
321+
322+
if (event.key === eventKeys.ARROWDOWN) {
323+
event.preventDefault();
324+
const next = items[currentIndex + 1] || items[0];
325+
next?.focus();
326+
return;
327+
}
328+
329+
if (event.key === eventKeys.ARROWUP) {
330+
event.preventDefault();
331+
const prev = items[currentIndex - 1] || items[items.length - 1];
332+
prev?.focus();
333+
return;
334+
}
335+
}
336+
337+
if (event.key === eventKeys.ESCAPE) {
304338
toggle(false)(event);
339+
return;
340+
}
341+
342+
if (event?.key === eventKeys.TAB && mergedVisible && !event.shiftKey) {
343+
if (shouldCloseOnTab) {
344+
toggle(false)(event);
345+
} else {
346+
timeout && clearTimeout(timeout);
347+
timeout = setTimeout(() => {
348+
if (
349+
refs.floating.current &&
350+
!refs.floating.current.contains(document.activeElement)
351+
) {
352+
toggle(false)(event);
353+
}
354+
}, NO_ANIMATION_DURATION);
355+
}
305356
}
306357
if (event?.key === eventKeys.TAB && event.shiftKey && mergedVisible) {
307358
timeout && clearTimeout(timeout);

src/components/Dropdown/Dropdown.types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ export interface DropdownProps {
5252
* @default true
5353
*/
5454
closeOnOutsideClick?: boolean;
55+
/**
56+
* Should close dropdown on tab key
57+
* @default false
58+
*/
59+
shouldCloseOnTab?: boolean;
5560
/**
5661
* If the dropdown is disabled or not
5762
*/

0 commit comments

Comments
 (0)