feat: improve/refactor focus handling (#17796)

* feat: improve focus

* test

* lint

* use modulus in loop
This commit is contained in:
Min Idzelis
2025-04-30 00:19:38 -04:00
committed by GitHub
parent 2e8a286540
commit 4b1ced439b
11 changed files with 92 additions and 129 deletions

View File

@@ -1,8 +1,11 @@
import FocusTrapTest from '$lib/actions/__test__/focus-trap-test.svelte';
import { setDefaultTabbleOptions } from '$lib/utils/focus-util';
import { render, screen } from '@testing-library/svelte';
import userEvent from '@testing-library/user-event';
import { tick } from 'svelte';
setDefaultTabbleOptions({ displayCheck: 'none' });
describe('focusTrap action', () => {
const user = userEvent.setup();
@@ -38,6 +41,7 @@ describe('focusTrap action', () => {
const openButton = screen.getByText('Open');
await user.click(openButton);
await tick();
expect(document.activeElement).toEqual(screen.getByTestId('one'));
screen.getByText('Close').click();

View File

@@ -1,5 +1,5 @@
import { shortcuts } from '$lib/actions/shortcut';
import { getFocusable } from '$lib/utils/focus-util';
import { getTabbable } from '$lib/utils/focus-util';
import { tick } from 'svelte';
interface Options {
@@ -18,18 +18,21 @@ export function focusTrap(container: HTMLElement, options?: Options) {
};
};
const setInitialFocus = () => {
const focusableElement = getFocusable(container)[0];
// Use tick() to ensure focus trap works correctly inside <Portal />
void tick().then(() => focusableElement?.focus());
const setInitialFocus = async () => {
const focusableElement = getTabbable(container, false)[0];
if (focusableElement) {
// Use tick() to ensure focus trap works correctly inside <Portal />
await tick();
focusableElement?.focus();
}
};
if (withDefaults(options).active) {
setInitialFocus();
void setInitialFocus();
}
const getFocusableElements = () => {
const focusableElements = getFocusable(container);
const focusableElements = getTabbable(container);
return [
focusableElements.at(0), //
focusableElements.at(-1),
@@ -67,7 +70,7 @@ export function focusTrap(container: HTMLElement, options?: Options) {
update(newOptions?: Options) {
options = newOptions;
if (withDefaults(options).active) {
setInitialFocus();
void setInitialFocus();
}
},
destroy() {