From ab04bb756475361a0d81dc6248124f918d518e99 Mon Sep 17 00:00:00 2001 From: dqzx <4139931+dqzx@users.noreply.github.com> Date: Sat, 3 Jul 2021 11:42:35 +0100 Subject: [PATCH] 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 --- src/ComposedModal/ComposedModal.svelte | 27 ++++++++++++++++++++++++++ src/Modal/Modal.svelte | 27 +++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/ComposedModal/ComposedModal.svelte b/src/ComposedModal/ComposedModal.svelte index 43c4621e..094eba37 100644 --- a/src/ComposedModal/ComposedModal.svelte +++ b/src/ComposedModal/ComposedModal.svelte @@ -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; diff --git a/src/Modal/Modal.svelte b/src/Modal/Modal.svelte index c167dec4..d4d65d24 100644 --- a/src/Modal/Modal.svelte +++ b/src/Modal/Modal.svelte @@ -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'); } }