-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add dialog light dismiss behavior (the actual click outside part) 5/5
This adds to the prior CLs to actually allow "click outside" to function as a light dismiss trigger. See spec PR for details: whatwg/html#10737 Bug: 376516550 Change-Id: I62158e092de9acac182777f2ad9864e818128907 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6013845 Commit-Queue: David Baron <[email protected]> Reviewed-by: David Baron <[email protected]> Auto-Submit: Mason Freed <[email protected]> Cr-Commit-Position: refs/heads/main@{#1383792}
- Loading branch information
1 parent
9cccb7c
commit 5420c40
Showing
2 changed files
with
246 additions
and
0 deletions.
There are no files selected for viewing
108 changes: 108 additions & 0 deletions
108
...cs/interactive-elements/the-dialog-element/dialog-popover-closedby-complex.tentative.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
<!DOCTYPE html> | ||
<meta charset="utf-8"> | ||
<link rel="author" href="mailto:[email protected]"> | ||
<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#dialog-light-dismiss"> | ||
<script src="/resources/testharness.js"></script> | ||
<script src="/resources/testharnessreport.js"></script> | ||
<script src="/resources/testdriver.js"></script> | ||
<script src="/resources/testdriver-actions.js"></script> | ||
<script src="/resources/testdriver-vendor.js"></script> | ||
<script src="../../popovers/resources/popover-utils.js"></script> | ||
|
||
<div id=unrelated>Unrelated</div> | ||
<dialog id=dialogA closedby=any>Dialog 1 | ||
<div id=popoverA popover>Popover 1 | ||
<dialog id=dialogB closedby=any>Dialog 2 | ||
<div id=popoverB popover>Popover 2</div> | ||
</dialog> | ||
</div> | ||
</dialog> | ||
|
||
<style> | ||
#dialogA { top: 100px; bottom: auto; padding:0; } | ||
#popoverA { top: 150px; bottom: auto; padding:0; } | ||
#dialogB { top: 200px; bottom: auto; padding:0; } | ||
#popoverB { top: 250px; bottom: auto; padding:0; } | ||
</style> | ||
|
||
<script> | ||
function openDialog(dialog,modal) { | ||
assert_false(dialog.open); | ||
if (modal) { | ||
dialog.showModal(); | ||
} else { | ||
dialog.show(); | ||
} | ||
assert_true(dialog.open); | ||
assert_equals(dialog.matches(':modal'),modal); | ||
} | ||
function assertStates(dialogAExpected,popoverAExpected, | ||
dialogBExpected,popoverBExpected) { | ||
assert_equals(dialogA.open,dialogAExpected, | ||
`First dialog should be ${dialogAExpected ? 'open' : 'closed'}`); | ||
assert_equals(popoverA.matches(':popover-open'),popoverAExpected, | ||
`First popover should be ${popoverAExpected ? 'open' : 'closed'}`); | ||
assert_equals(dialogB.open,dialogBExpected, | ||
`Second dialog should be ${dialogBExpected ? 'open' : 'closed'}`); | ||
assert_equals(popoverB.matches(':popover-open'),popoverBExpected, | ||
`Second popover should be ${popoverBExpected ? 'open' : 'closed'}`); | ||
} | ||
function openDialogPopoverStack(t,modalA,modalB) { | ||
t.add_cleanup(() => { | ||
dialogA.close(); | ||
popoverA.hidePopover(); | ||
dialogB.close(); | ||
popoverB.hidePopover(); | ||
}); | ||
openDialog(dialogA,modalA); | ||
popoverA.showPopover(); | ||
openDialog(dialogB,modalB); | ||
popoverB.showPopover(); | ||
assertStates(true,true,true,true); | ||
} | ||
|
||
[false,true].forEach(modalA => { | ||
[false,true].forEach(modalB => { | ||
const modalAString = modalA ? 'modal dialogA' : 'modeless dialogA'; | ||
const modalBString = modalB ? 'modal dialogB' : 'modeless dialogB'; | ||
promise_test(async (t) => { | ||
openDialogPopoverStack(t,modalA,modalB); | ||
await clickOn(unrelated); | ||
// Clicking outside all is actually a click on a dialog backdrop. | ||
// If dialogB is modal, it'll be dialogB, which is nested inside popoverA. | ||
assertStates(false,modalB,false,false); | ||
},`clicking outside all with ${modalAString} and ${modalBString}`); | ||
|
||
promise_test(async (t) => { | ||
openDialogPopoverStack(t,modalA,modalB); | ||
await clickOn(popoverB); | ||
// Clicking popoverB will keep both popovers plus the intervening dialogB | ||
// open, because they're a stack. | ||
assertStates(false,true,true,true); | ||
},`clicking popoverB with ${modalAString} and ${modalBString}`); | ||
|
||
promise_test(async (t) => { | ||
openDialogPopoverStack(t,modalA,modalB); | ||
await clickOn(dialogB); | ||
// dialogB is nested inside popoverA. | ||
assertStates(false,true,true,false); | ||
},`clicking dialogB with ${modalAString} and ${modalBString}`); | ||
|
||
promise_test(async (t) => { | ||
openDialogPopoverStack(t,modalA,modalB); | ||
await clickOn(popoverA); | ||
// If dialogB is modal, then clicking popoverA is actually a backdrop | ||
// click on dialogB, which will close it. PopoverA stays open because | ||
// dialogB is nested inside popoverA. | ||
assertStates(false,true,!modalB,false); | ||
},`clicking popoverA with ${modalAString} and ${modalBString}`); | ||
|
||
promise_test(async (t) => { | ||
openDialogPopoverStack(t,modalA,modalB); | ||
await clickOn(dialogB); | ||
// Again, this is a backdrop click on dialogB. | ||
assertStates(false,true,true,false); | ||
},`clicking dialogA with ${modalAString} and ${modalBString}`); | ||
}); | ||
}); | ||
</script> |
138 changes: 138 additions & 0 deletions
138
...ics/interactive-elements/the-dialog-element/dialog-popover-closedby-simple.tentative.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
<!DOCTYPE html> | ||
<meta charset="utf-8"> | ||
<link rel="author" href="mailto:[email protected]"> | ||
<link rel=help href="https://html.spec.whatwg.org/multipage/interactive-elements.html#dialog-light-dismiss"> | ||
<script src="/resources/testharness.js"></script> | ||
<script src="/resources/testharnessreport.js"></script> | ||
<script src="/resources/testdriver.js"></script> | ||
<script src="/resources/testdriver-actions.js"></script> | ||
<script src="/resources/testdriver-vendor.js"></script> | ||
<script src="../../popovers/resources/popover-utils.js"></script> | ||
|
||
<div id=unrelated>Unrelated</div> | ||
<dialog id=dialog_outer>Dialog outer | ||
<div id=popover_inner popover>Popover inner</div> | ||
</dialog> | ||
<div id=popover_outer popover>Popover outer | ||
<dialog id=dialog_inner>Dialog inner</dialog> | ||
</div> | ||
|
||
<style> | ||
dialog { top: 50px; bottom: auto; padding:0; } | ||
[popover] { top: 100px; bottom: auto; padding:0; } | ||
</style> | ||
|
||
<script> | ||
function resetDialogOuterTest(dialog,popover) { | ||
popover.hidePopover(); | ||
dialog.close(); | ||
dialog.showModal(); | ||
popover.showPopover(); | ||
assert_true(dialog.open && popover.matches(':popover-open'),'setup'); | ||
} | ||
async function runDialogOuterTest(t,dialog,popover) { | ||
t.add_cleanup(() => { | ||
dialog.removeAttribute('closedby'); | ||
popover.hidePopover(); | ||
dialog.close(); | ||
}); | ||
resetDialogOuterTest(dialog,popover); | ||
await clickOn(popover); | ||
assert_true(popover.matches(':popover-open'), | ||
'clicking on popover should always leave everything open'); | ||
assert_true(dialog.open,'dialog should stay open'); | ||
resetDialogOuterTest(dialog,popover); | ||
await clickOn(dialog); | ||
assert_false(popover.matches(':popover-open'),'popover should close'); | ||
assert_true(dialog.open,'dialog should stay open'); | ||
resetDialogOuterTest(dialog,popover); | ||
await clickOn(unrelated); | ||
assert_false(popover.matches(':popover-open'),'popover should always close'); | ||
assert_equals(dialog.open,dialog.closedBy !== 'any', | ||
'dialog should close if closedby=any'); | ||
resetDialogOuterTest(dialog,popover); | ||
const ESC = '\uE00C'; | ||
await new test_driver.send_keys(document.documentElement,ESC); | ||
assert_false(popover.matches(':popover-open'), | ||
'popover should close after first ESC'); | ||
assert_true(dialog.open,'dialog should stay open for first ESC'); | ||
await new test_driver.send_keys(document.documentElement,ESC); | ||
assert_equals(dialog.open,dialog.closedBy === 'none', | ||
'dialog should close on second ESC, if closedby is not none'); | ||
} | ||
promise_test(async (t) => { | ||
dialog_outer.setAttribute('closedby','any'); | ||
await runDialogOuterTest(t,dialog_outer,popover_inner); | ||
},'Dialog closedby=any parent, popover child'); | ||
promise_test(async (t) => { | ||
dialog_outer.setAttribute('closedby','closerequest'); | ||
await runDialogOuterTest(t,dialog_outer,popover_inner); | ||
},'Dialog closedby=closerequest parent, popover child'); | ||
promise_test(async (t) => { | ||
dialog_outer.setAttribute('closedby','none'); | ||
await runDialogOuterTest(t,dialog_outer,popover_inner); | ||
},'Dialog closedby=none parent, popover child'); | ||
|
||
|
||
function resetPopoverOuterTest(dialog,popover) { | ||
dialog.close(); | ||
popover.hidePopover(); | ||
popover.showPopover(); | ||
dialog.showModal(); | ||
assert_true(dialog.open && popover.matches(':popover-open'),'setup'); | ||
} | ||
async function runPopoverOuterTest(t,dialog,popover) { | ||
t.add_cleanup(() => { | ||
dialog.removeAttribute('closedby'); | ||
dialog.close(); | ||
popover.hidePopover(); | ||
}); | ||
resetPopoverOuterTest(dialog,popover); | ||
await clickOn(dialog); | ||
assert_true(dialog.open,'clicking on dialog should always leave everything open'); | ||
assert_true(popover.matches(':popover-open'),'popover should stay open'); | ||
resetPopoverOuterTest(dialog,popover); | ||
await clickOn(popover); | ||
assert_equals(dialog.open,dialog.closedBy !== 'any', | ||
'dialog should close if closedby=any'); | ||
// Note that "clicking on" popover really means clicking on dialog's | ||
// ::backdrop, and the dialog is a child of the popover. So by popover's light | ||
// dismiss logic, it will *not* close. That's semi-expected here, but not in | ||
// the next case. | ||
assert_true(popover.matches(':popover-open'),'popover should stay open'); | ||
resetPopoverOuterTest(dialog,popover); | ||
await clickOn(unrelated); | ||
assert_equals(dialog.open,dialog.closedBy !== 'any', | ||
'dialog should close if closedby=any'); | ||
// See note above. | ||
assert_true(popover.matches(':popover-open'),'popover should stay open'); | ||
if (!dialog.open) { | ||
// If we light dismissed the dialog, check that the popover responds to a | ||
// second click. | ||
await clickOn(unrelated); | ||
assert_false(popover.matches(':popover-open'),'popover should stay open'); | ||
} | ||
resetPopoverOuterTest(dialog,popover); | ||
const ESC = '\uE00C'; | ||
await new test_driver.send_keys(document.documentElement,ESC); | ||
assert_equals(dialog.open,dialog.closedBy === 'none', | ||
'dialog should close after first ESC, if closedby!=none'); | ||
assert_true(popover.matches(':popover-open'), | ||
'popover should stay open for first ESC'); | ||
await new test_driver.send_keys(document.documentElement,ESC); | ||
assert_equals(popover.matches(':popover-open'),dialog.closedBy === 'none', | ||
'popover should close on second ESC, unless inner dialog prevents with closedby==none'); | ||
} | ||
promise_test(async (t) => { | ||
dialog_inner.setAttribute('closedby','any'); | ||
await runPopoverOuterTest(t,dialog_inner,popover_outer); | ||
},'Popover parent, dialog closedby=any child'); | ||
promise_test(async (t) => { | ||
dialog_inner.setAttribute('closedby','closerequest'); | ||
await runPopoverOuterTest(t,dialog_inner,popover_outer); | ||
},'Popover parent, dialog closedby=closerequest child'); | ||
promise_test(async (t) => { | ||
dialog_inner.setAttribute('closedby','none'); | ||
await runPopoverOuterTest(t,dialog_inner,popover_outer); | ||
},'Popover parent, dialog closedby=none child'); | ||
</script> |