Fix: trap tab focus within modal (#716)

* Fix: trap tab focus within modal

* Fix: trap tab focus within ComposedModal

* change selector criteria for tabbable elements based on react component logic
This commit is contained in:
dqzx 2021-07-03 11:42:35 +01:00 committed by GitHub
commit ab04bb7564
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 3 deletions

View file

@ -103,6 +103,33 @@
class:is-visible="{open}"
class:bx--modal--danger="{danger}"
{...$$restProps}
on:keydown
on:keydown="{(e) => {
if (open) {
if (e.key === 'Escape') {
open = false;
} else if (e.key === 'Tab') {
// taken from github.com/carbon-design-system/carbon/packages/react/src/internal/keyboard/navigation.js
const selectorTabbable = `
a[href], area[href], input:not([disabled]):not([tabindex='-1']),
button:not([disabled]):not([tabindex='-1']),select:not([disabled]):not([tabindex='-1']),
textarea:not([disabled]):not([tabindex='-1']),
iframe, object, embed, *[tabindex]:not([tabindex='-1']):not([disabled]), *[contenteditable=true]
`;
const tabbable = Array.from(ref.querySelectorAll(selectorTabbable));
let index = tabbable.indexOf(document.activeElement);
if (index === -1 && e.shiftKey) index = 0;
index += tabbable.length + (e.shiftKey ? -1 : 1);
index %= tabbable.length;
tabbable[index].focus();
e.preventDefault();
}
}
}}"
on:click
on:click="{() => {
if (!didClickInnerModal && !preventCloseOnClickOutside) open = false;

View file

@ -138,11 +138,32 @@
class:bx--modal--danger="{danger}"
{...$$restProps}
on:keydown
on:keydown="{({ key }) => {
on:keydown="{(e) => {
if (open) {
if (key === 'Escape') {
if (e.key === 'Escape') {
open = false;
} else if (shouldSubmitOnEnter && key === 'Enter') {
} else if (e.key === 'Tab') {
// trap focus
// taken from github.com/carbon-design-system/carbon/packages/react/src/internal/keyboard/navigation.js
const selectorTabbable = `
a[href], area[href], input:not([disabled]):not([tabindex='-1']),
button:not([disabled]):not([tabindex='-1']),select:not([disabled]):not([tabindex='-1']),
textarea:not([disabled]):not([tabindex='-1']),
iframe, object, embed, *[tabindex]:not([tabindex='-1']):not([disabled]), *[contenteditable=true]
`;
const tabbable = Array.from(ref.querySelectorAll(selectorTabbable));
let index = tabbable.indexOf(document.activeElement);
if (index === -1 && e.shiftKey) index = 0;
index += tabbable.length + (e.shiftKey ? -1 : 1);
index %= tabbable.length;
tabbable[index].focus();
e.preventDefault();
} else if (shouldSubmitOnEnter && e.key === 'Enter') {
dispatch('submit');
}
}