+4
-2
src/__tests__/useModalFocus.test.tsx
+4
-2
src/__tests__/useModalFocus.test.tsx
···
25
25
</main>
26
26
);
27
27
28
-
// starts out with first element available
29
-
cy.focused().contains('Focus 1');
28
+
// starts out with first modal element available
29
+
cy.focused().should('have.attr', 'aria-modal', 'true')
30
30
31
31
// cycles through the modal's focusable targets only
32
+
cy.realPress('Tab');
33
+
cy.focused().contains('Focus 1');
32
34
cy.realPress('Tab');
33
35
cy.focused().contains('Focus 2');
34
36
cy.realPress('Tab');
+10
-6
src/useModalFocus.ts
+10
-6
src/useModalFocus.ts
···
3
3
snapshotSelection,
4
4
restoreSelection,
5
5
} from './utils/selection';
6
-
import { getFirstFocusTarget, getFocusTargets } from './utils/focus';
6
+
7
+
import {
8
+
getAutofocusTarget,
9
+
getFirstFocusTarget,
10
+
getFocusTargets,
11
+
} from './utils/focus';
12
+
7
13
import { useLayoutEffect } from './utils/react';
8
14
import { contains } from './utils/element';
9
15
import { makePriorityHook } from './usePriority';
···
30
36
!document.activeElement ||
31
37
!ref.current.contains(document.activeElement)
32
38
) {
33
-
const newTarget = getFirstFocusTarget(ref.current);
34
-
if (newTarget) {
35
-
selection = snapshotSelection(ref.current);
36
-
newTarget.focus();
37
-
}
39
+
const newTarget = getAutofocusTarget(ref.current);
40
+
selection = snapshotSelection(ref.current);
41
+
newTarget.focus();
38
42
}
39
43
40
44
function onBlur(event: FocusEvent) {
+13
-3
src/utils/focus.ts
+13
-3
src/utils/focus.ts
···
60
60
};
61
61
62
62
/** Returns the first focus target that should be focused automatically. */
63
-
export const getFirstFocusTarget = (node: HTMLElement): HTMLElement | null => {
64
-
const targets = getFocusTargets(node);
65
-
return targets.find(x => x.matches('[autofocus]')) || targets[0] || null;
63
+
export const getFirstFocusTarget = (node: HTMLElement): HTMLElement | null =>
64
+
getFocusTargets(node)[0] || null;
65
+
66
+
/** Returns the first focus target that should be focused automatically in a modal/dialog. */
67
+
export const getAutofocusTarget = (node: HTMLElement): HTMLElement => {
68
+
const elements = node.querySelectorAll(focusableSelectors);
69
+
for (let i = 0, l = elements.length; i < l; i++) {
70
+
const element = elements[i] as HTMLElement;
71
+
if (isVisible(element) && element.matches('[autofocus]')) return element;
72
+
}
73
+
74
+
node.setAttribute('tabindex', '-1');
75
+
return node;
66
76
};
67
77
68
78
/** Returns the next (optionally in reverse) focus target given a target node. */