openui / open-ui

Maintain an open standard for UI and promote its adherence and adoption.
https://open-ui.org
Other
3.59k stars 191 forks source link

What should "nested" dialog light dismiss look like? #1128

Open mfreed7 opened 2 days ago

mfreed7 commented 2 days ago

I have a spec PR open to add dialog "light dismiss" capabilities, via a new closedby attribute, here: https://github.com/whatwg/html/issues/9373. One question came up (see discussion starting roughly here) about what the behavior should be for "nested" dialogs. I keep putting quotes around "nested" because dialogs don't have any current notion of being "nested", in the same way that popovers explicitly do (see the big Note here: https://html.spec.whatwg.org/#topmost-popover-ancestor). So any behavior we add here is essentially inventing some concept of nested dialogs.

So the question is, what are typical expectations for users or developers when multiple modal dialogs are open, and the user clicks outside one of them. I'd like to side-step the question of modeless dialogs, at least for now, since I consider modals the more pressing, and common, use case.

Here's a demo to work with (borrowed and tweaked from @domenic):

Screenshot 2024-11-22 at 10 15 25 AM

Some things to note:

There are three positions that a user might try to click (labeled in red in the demo above):

  1. Within inner's border box.
  2. Within the visible portion of outer's border box.
  3. Outside both dialogs' border boxes.

Note, again, that #2 and #3 are both actually clicks on the backdrop of inner.

In terms of potential outcomes, for each click position:

  1. Clicking on inner should always leave inner visible. It could either close outer or leave it open. It seems like it makes the most sense to leave outer open, at least to a user for this use case.
  2. This is a click outside inner, so inner should clearly close. And because it's an attempted click on outer, it seems like outer should stay open.
  3. Clearly, inner should close, since it is topmost. The question is whether the user expects this click to just close inner and leave outer open (i.e. just pop one dialog off the stack), or whether both should close. My assumption is that in all design systems today that implement "light dismiss dialogs", outer stays open, just due to implementation. But I'd love confirmation.

The point of this issue is a) to make sure I haven't missed something, and b) to check expectations for behavior. I'd love to discuss, so Agenda+.

keithamus commented 2 days ago
  1. Clicking on inner should always leave inner visible. It could either close outer or leave it open. It seems like it makes the most sense to leave outer open, at least to a user for this use case.

Yes, it's important that clicking on inner should not close any lower dialogs. It should leave outer open.

  1. This is a click outside inner, so inner should clearly close. And because it's an attempted click on outer, it seems like outer should stay open.

Right. Only one dialog should close at a time. Closing many may be an unpredictable behaviour, and would sow distrust for the user as to what they can anticipate.

  1. Clearly, inner should close, since it is topmost. The question is whether the user expects this click to just close inner and leave outer open (i.e. just pop one dialog off the stack), or whether both should close. My assumption is that in all design systems today that implement "light dismiss dialogs", outer stays open, just due to implementation. But I'd love confirmation.

We have some nested dialogs where outer stays open, and it is a carefully considered design choice. It is important to us that the implied behaviour is "just pop one off the stack" because it allows for a very predictable consistency, whether or not the developer made them nested or not, and regardless of the "type" (hint, popover, dialog, etc).

a screenshot of the "Create new issue" dialog in GitHub, where some changes have been made, and the user has tried to dismiss the dialog. A nested dialog lays over the top with the heading "Discard changes?"

In the above image, there is actually a more complex case: the Discard changes? dialog can be light dismissed, which acts like the Keep editing button. Attempting to light dismiss dialog "Create new issue" will invoke the "Discard changes?" dialog, in order to intercept the user before they are able to dismiss this dialog and potentially reset their work. For us it is important that clicking the backdrop always does something; either light dismiss the current top dialog, or present a new dialog (or some kind of visual confirmation) showing that the light dismiss has been prevented.

mfreed7 commented 2 days ago

We have some nested dialogs where outer stays open, and it is a carefully considered design choice. It is important to us that the implied behaviour is "just pop one off the stack" because it allows for a very predictable consistency, whether or not the developer made them nested or not, and regardless of the "type" (hint, popover, dialog, etc).

This is very helpful - thanks.

In the above image, there is actually a more complex case: the Discard changes? dialog can be light dismissed, which acts like the Keep editing button. Attempting to light dismiss dialog "Create new issue" will invoke the "Discard changes?" dialog, in order to intercept the user before they are able to dismiss this dialog and potentially reset their work. For us it is important that clicking the backdrop always does something; either light dismiss the current top dialog, or present a new dialog (or some kind of visual confirmation) showing that the light dismiss has been prevented.

Thanks also for this use case. I believe this should be easy to implement, within the currently proposed behavior (https://github.com/whatwg/html/issues/9373), by adding a listener for the outer dialog's cancel event, and preventDefaulting it as needed. Does that seem correct?

keithamus commented 1 day ago

Thanks also for this use case. I believe this should be easy to implement, within the currently proposed behavior (whatwg/html#9373), by adding a listener for the outer dialog's cancel event, and preventDefaulting it as needed. Does that seem correct?

Yes absolutely. No concerns on that particular behaviour.