Skip to content

Commit

Permalink
Fix SelectNext focus inconsistencies when mixing mouse and keyboard i…
Browse files Browse the repository at this point in the history
…nteraction
  • Loading branch information
acelaya committed Oct 5, 2023
1 parent f3ac2d1 commit c03227d
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 11 deletions.
14 changes: 12 additions & 2 deletions src/components/input/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ type ComponentProps = {
*/
title?: string;

/**
* Whether the focus ring should be applied to `:focus` or `:focus-visible`.
* Defaults to focus-visible.
*/
focusRing?: 'focus' | 'focus-visible';

/** Use `expanded` prop instead */
'aria-expanded'?: never;
/** Use `pressed` prop instead */
Expand Down Expand Up @@ -64,6 +70,7 @@ export default function Button({
size = 'md',
variant = 'secondary',
unstyled = false,
focusRing = 'focus-visible',

role,
...htmlAttributes
Expand Down Expand Up @@ -96,8 +103,11 @@ export default function Button({
{...htmlAttributes}
className={classnames(
{
'focus-visible-ring transition-colors whitespace-nowrap flex items-center':
styled,
'transition-colors whitespace-nowrap flex items-center': styled,
},
styled && {
'focus-visible-ring': focusRing === 'focus-visible',
'focus:ring outline-none': focusRing === 'focus',
},
themed && {
'font-semibold rounded': true,
Expand Down
1 change: 1 addition & 0 deletions src/components/input/SelectNext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ function SelectOption<T>({
return (
<Button
variant="custom"
focusRing="focus"
classes={classnames(
'w-full ring-inset rounded-none !p-0',
'border-t first:border-t-0 bg-transparent',
Expand Down
7 changes: 4 additions & 3 deletions src/hooks/test/use-arrow-key-navigation-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,15 +386,16 @@ describe('useArrowKeyNavigation', () => {
assert.equal(document.activeElement, document.body);

// Once we open the toolbar, the first item will be focused
toggleToolbar();
await act(() => toggleToolbar());
await waitFor(() => document.activeElement === findElementByTestId('bold'));

// If we then focus another toolbar item, then close the toolbar and open it
// again, that same element should be focused again
pressKey('ArrowDown'); // "italic" is focused
pressKey('ArrowDown'); // "underline" is focused
toggleToolbar(); // Close toolbar
toggleToolbar(); // Open toolbar again
await act(() => toggleToolbar()); // Close toolbar
await act(() => toggleToolbar()); // Open toolbar again

await waitFor(
() => document.activeElement === findElementByTestId('underline'),
);
Expand Down
13 changes: 7 additions & 6 deletions src/hooks/use-arrow-key-navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ export function useArrowKeyNavigation(
const lastFocusedItem = useRef<HTMLElement | null>(null);

useEffect(() => {
if (!containerVisible) {
return () => {};
}

if (!containerRef.current) {
throw new Error('Container ref not set');
}
Expand Down Expand Up @@ -196,11 +200,7 @@ export function useArrowKeyNavigation(
const initialIndex = lastFocusedItem.current
? navigableElements.indexOf(lastFocusedItem.current)
: 0;
updateTabIndexes(
navigableElements,
initialIndex,
containerVisible && autofocus,
);
updateTabIndexes(navigableElements, initialIndex, autofocus);

const listeners = new ListenerCollection();

Expand All @@ -215,10 +215,11 @@ export function useArrowKeyNavigation(
lastFocusedItem.current.focus();
return;
}

const elements = getNavigableElements();
const targetIndex = elements.indexOf(event.target as HTMLElement);
if (targetIndex >= 0) {
updateTabIndexes(elements, targetIndex);
updateTabIndexes(elements, targetIndex, autofocus);
}
});

Expand Down

0 comments on commit c03227d

Please sign in to comment.