fix(modal): skip showModal if enabled flipped false before mount
Re-check props.enabled inside the dialogRef watcher. The watcher fires once Vue mounts the <dialog>, but the caller may have flipped enabled back to false between the openDialog() call and the mount flush. In that case the prop state is disabled and we must not open the dialog. Addresses augmentcode review on #2604.
This commit is contained in:
parent
e01a599418
commit
113b77e92f
|
|
@ -154,6 +154,34 @@ describe('Modal.vue — open race condition (#2590)', () => {
|
|||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('does not open the dialog if enabled flips back to false before mount', async () => {
|
||||
// Regression guard: the dialogRef watcher fires once the <dialog>
|
||||
// element mounts. If props.enabled has flipped back to false by the
|
||||
// time the mount happens, the watcher must not call showModal().
|
||||
const wrapper = mount(Modal, {
|
||||
attachTo: document.body,
|
||||
props: {enabled: false},
|
||||
slots: {default: '<p class="test-body">hi</p>'},
|
||||
})
|
||||
|
||||
// Flip enabled true then false within the same tick, before the mount
|
||||
// flush can complete.
|
||||
wrapper.setProps({enabled: true})
|
||||
wrapper.setProps({enabled: false})
|
||||
await flushPromises()
|
||||
await nextTick()
|
||||
await new Promise(resolve => setTimeout(resolve, 200))
|
||||
await flushPromises()
|
||||
await nextTick()
|
||||
|
||||
// showModal must not have been called — the final prop state is
|
||||
// disabled.
|
||||
expect(showModalSpy).not.toHaveBeenCalled()
|
||||
expect(document.querySelector('dialog.modal-dialog')).toBeNull()
|
||||
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('clears data-closing when re-opened mid-close transition', async () => {
|
||||
// Regression guard: if the user toggles enabled back to true while the
|
||||
// 150ms close transition is still in flight, the <dialog> is still
|
||||
|
|
|
|||
|
|
@ -147,9 +147,12 @@ watch(
|
|||
// Actually call showModal() the moment the <dialog> element is mounted.
|
||||
// `dialogRef` is populated by Vue during the render flush after
|
||||
// `showDialog.value = true`, so this fires deterministically, no matter
|
||||
// how many flushes the renderer needs (see #2590).
|
||||
// how many flushes the renderer needs (see #2590). We re-check
|
||||
// `props.enabled` here because the prop can flip back to `false` between
|
||||
// `openDialog()` and the mount flush, in which case we must not open.
|
||||
watch(dialogRef, (dialog) => {
|
||||
if (!dialog) return
|
||||
if (!props.enabled) return
|
||||
delete dialog.dataset.closing
|
||||
dialog.showModal()
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue