Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Sep 22, 2022. It is now read-only.

Trap focus when connected in <details open> #90

Merged
merged 2 commits into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 22 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,27 +95,35 @@ function onSummaryClick(event: Event): void {
}
}

function trapFocus(dialog: DetailsDialogElement, details: Element): void {
const root = 'getRootNode' in dialog ? (dialog.getRootNode() as Document | ShadowRoot) : document
if (root.activeElement instanceof HTMLElement) {
initialized.set(dialog, {details, activeElement: root.activeElement})
}

autofocus(dialog)
;(details as HTMLElement).addEventListener('keydown', keydown)
}

function releaseFocus(dialog: DetailsDialogElement, details: Element): void {
for (const form of dialog.querySelectorAll('form')) {
form.reset()
}
const focusElement = findFocusElement(details, dialog)
if (focusElement) focusElement.focus()
;(details as HTMLElement).removeEventListener('keydown', keydown)
}

function toggle(event: Event): void {
const details = event.currentTarget
if (!(details instanceof Element)) return
const dialog = details.querySelector('details-dialog')
if (!(dialog instanceof DetailsDialogElement)) return

if (details.hasAttribute('open')) {
const root = 'getRootNode' in dialog ? (dialog.getRootNode() as Document | ShadowRoot) : document
if (root.activeElement instanceof HTMLElement) {
initialized.set(dialog, {details, activeElement: root.activeElement})
}

autofocus(dialog)
;(details as HTMLElement).addEventListener('keydown', keydown)
trapFocus(dialog, details)
} else {
for (const form of dialog.querySelectorAll('form')) {
form.reset()
}
const focusElement = findFocusElement(details, dialog)
if (focusElement) focusElement.focus()
;(details as HTMLElement).removeEventListener('keydown', keydown)
releaseFocus(dialog, details)
}
}

Expand Down Expand Up @@ -300,6 +308,7 @@ class DetailsDialogElement extends HTMLElement {

details.addEventListener('toggle', toggle)
state.details = details
if (details.hasAttribute('open')) trapFocus(this, details)

updateIncludeFragmentEventListeners(details, this.src, this.preload)
}
Expand Down
42 changes: 42 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,48 @@ describe('details-dialog-element', function() {
})
})

describe('connected as a child of an already [open] <details> element', function () {
let details
let dialog
let summary
let close

beforeEach(function() {
const container = document.createElement('div')
container.innerHTML = `
<details open>
<summary>Click</summary>
<details-dialog>
<button data-button>Button</button>
<button ${CLOSE_ATTR}>Goodbye</button>
</details-dialog>
</details>
`
document.body.append(container)

details = document.querySelector('details')
dialog = details.querySelector('details-dialog')
summary = details.querySelector('summary')
close = dialog.querySelector(CLOSE_SELECTOR)
})

afterEach(function() {
document.body.innerHTML = ''
})

it('manages focus', async function() {
assert.equal(document.activeElement, dialog)
triggerKeydownEvent(document.activeElement, 'Tab', true)
assert.equal(document.activeElement, document.querySelector(`[${CLOSE_ATTR}]`))
triggerKeydownEvent(document.activeElement, 'Tab')
assert.equal(document.activeElement, document.querySelector(`[data-button]`))
triggerKeydownEvent(document.activeElement, 'Tab')
assert.equal(document.activeElement, document.querySelector(`[${CLOSE_ATTR}]`))
triggerKeydownEvent(document.activeElement, 'Tab')
assert.equal(document.activeElement, document.querySelector(`[data-button]`))
})
})

describe('shadow DOM context', function() {
let shadowRoot, details, summary, dialog
beforeEach(function() {
Expand Down