зеркало из
1
0
Форкнуть 0

Merge pull request #22 from github/will-close-event

Add cancellable details-dialog:will-close event
This commit is contained in:
Mu-An Chiou 2018-09-13 14:38:36 -04:00 коммит произвёл GitHub
Родитель cf27b407bc 6b0e0d8f4a
Коммит 1f7086a9d7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 98 добавлений и 13 удалений

Просмотреть файл

@ -24,6 +24,25 @@ import 'details-dialog-element'
</details>
```
## Events
### `details-dialog:will-close`
A `details-dialog:will-close` event is fired when a request to close the dialog
is made either by pressing escape, clicking a `data-close-dialog` element,
clicking on the `<summary>` element, or when `.toggle(false)` is called on an
open dialog.
This event can be cancelled to keep the dialog open.
```js
document.addEventListener('details-dialog:will-close', function(event) {
if (!confirm('Are you sure?')) {
event.preventDefault()
}
})
```
## Browser support
Browsers without native [custom element support][support] require a [polyfill][].

Просмотреть файл

@ -59,6 +59,30 @@ function restrictTabBehavior(event: KeyboardEvent): void {
elements[targetIndex].focus()
}
function allowClosingDialog(details: Element): boolean {
const dialog = details.querySelector('details-dialog')
if (!(dialog instanceof DetailsDialogElement)) return true
return dialog.dispatchEvent(
new CustomEvent('details-dialog:will-close', {
bubbles: true,
cancelable: true
})
)
}
function onSummaryClick(event: Event): void {
if (!(event.currentTarget instanceof Element)) return
const details = event.currentTarget.closest('details[open]')
if (!details) return
// Prevent summary click events if details-dialog:will-close was cancelled
if (!allowClosingDialog(details)) {
event.preventDefault()
event.stopPropagation()
}
}
function toggle(event: Event): void {
const details = event.currentTarget
if (!(details instanceof Element)) return
@ -95,13 +119,10 @@ function toggleDetails(details: Element, open: boolean) {
// Don't update unless state is changing
if (open === details.hasAttribute('open')) return
const summary = details.querySelector('summary')
if (summary) {
// Toggle via clicking summary so it can be canceled by listeners wanting
// to prevent the toggle
summary.click()
} else {
open ? details.setAttribute('open', 'open') : details.removeAttribute('open')
if (open) {
details.setAttribute('open', '')
} else if (allowClosingDialog(details)) {
details.removeAttribute('open')
}
}
@ -143,7 +164,10 @@ class DetailsDialogElement extends HTMLElement {
if (!details) return
const summary = details.querySelector('summary')
if (summary) summary.setAttribute('aria-haspopup', 'dialog')
if (summary) {
summary.setAttribute('aria-haspopup', 'dialog')
summary.addEventListener('click', onSummaryClick, {capture: true})
}
details.addEventListener('toggle', toggle)
state.details = details
@ -151,8 +175,14 @@ class DetailsDialogElement extends HTMLElement {
disconnectedCallback() {
const state = initialized.get(this)
if (!state || !state.details) return
state.details.removeEventListener('toggle', toggle)
if (!state) return
const {details} = state
if (!details) return
details.removeEventListener('toggle', toggle)
const summary = details.querySelector('summary')
if (summary) {
summary.removeEventListener('click', onSummaryClick, {capture: true})
}
state.details = null
}

Просмотреть файл

@ -75,15 +75,15 @@ describe('details-dialog-element', function() {
assert(!details.open)
})
it('supports canceling requests to close the dialog when a summary element is present', async function() {
it('supports a cancellable details-dialog:will-close event when a summary element is present', async function() {
dialog.toggle(true)
await waitForToggleEvent(details)
assert(details.open)
let closeRequestCount = 0
let allowCloseToHappen = false
summary.addEventListener(
'click',
dialog.addEventListener(
'details-dialog:will-close',
function(event) {
closeRequestCount++
if (!allowCloseToHappen) {
@ -120,6 +120,42 @@ describe('details-dialog-element', function() {
summary.remove()
})
it('supports a cancellable details-dialog:will-close event', async function() {
dialog.toggle(true)
await waitForToggleEvent(details)
assert(details.open)
let closeRequestCount = 0
let allowCloseToHappen = false
dialog.addEventListener(
'details-dialog:will-close',
function(event) {
closeRequestCount++
if (!allowCloseToHappen) {
event.preventDefault()
event.stopPropagation()
}
},
{capture: true}
)
close.click()
assert(details.open)
assert.equal(closeRequestCount, 1)
pressEscape(details)
assert(details.open)
assert.equal(closeRequestCount, 2)
dialog.toggle(false)
assert(details.open)
assert.equal(closeRequestCount, 3)
allowCloseToHappen = true
close.click()
assert(!details.open)
})
it('toggles open', function() {
assert(!details.open)
dialog.toggle(true)