Spike Results: Enable native dialog for d2l-dialog-confirm#6760
Spike Results: Enable native dialog for d2l-dialog-confirm#6760
Conversation
|
Thanks for the PR! 🎉 We've deployed an automatic preview for this PR - you can see your changes here:
Note The build needs to finish before your changes are deployed. |
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
|
I initially ran the |
| // starting in Chrome 102, all elements inside <dialog>s that are inside shadow roots have null offsetParent | ||
| // https://bugs.chromium.org/p/chromium/issues/detail?id=1331803 | ||
| window.D2L.DialogMixin.hasNative = false; | ||
| window.D2L.DialogMixin.hasNative = (window.HTMLDialogElement !== undefined); |
There was a problem hiding this comment.
There look to be at least 2 cases that use DialogMixin and don't look to be setting preferNative so they would end up using native dialogs. We at least probably want to flag this change separately. We can evaluate those cases for if native dialogs can be enabled or if we should be setting preferNative to false for those cases.
There was a problem hiding this comment.
With having this be a property this is no longer an issue. We can opt-in for those 2 places and test them out at that time.
Is there somewhere I can find demo 11? Is this just calling I think ideally the content of the confirm gets announced immediately. Not sure about general dialog, but ideally the role and title are announced upon opening, and then the user can navigate the content how they want - virtual cursor or tabbing. |
Yup - it was in this blog. In terms of announcing the content immediately: The only way to announce the content immediately in Safari + VoiceOver seems to be having the initial focus be the content (which would be the native dialog default behaviour due to the tabindex); it doesn't read the In Safari + VoiceOver we also aren't currently announcing the content immediately (though ideally we probably would): |
| /** | ||
| * @ignore | ||
| */ | ||
| preferNative: { type: Boolean, attribute: 'prefer-native', reflect: true }, |
There was a problem hiding this comment.
Does anything need to set, or do we rely on the prefer-native attribute? Just wondering if we want/need to keep this as much under wraps as possible.
There was a problem hiding this comment.
We probably want to keep it under wraps
| async updated(changedProperties) { | ||
| super.updated(changedProperties); | ||
|
|
||
| if (changedProperties.has('preferNative')) { |
There was a problem hiding this comment.
Are there any case where we would expect preferNative to change after the dialog constructor runs?
Aside from that, maybe this logic could go in willUpdate(changedProperties) { ... } so that useNative is set before render?
There was a problem hiding this comment.
Likely not, it's more there as a just in case. willUpdate would make more sense. I'm also good to just remove this and can add back if needed.
There was a problem hiding this comment.
Ok, I am good either way, but would move to willUpdate if we keep. It'd be really weird and potentially disruptive if it ever changed while open. I suppose if we draw the line now, then later on we won't be wondering if it ever changes once set.
| _overflowBottom: { state: true }, | ||
| _overflowTop: { state: true }, | ||
| _parentDialog: { state: true }, | ||
| _preferNative: { state: true }, |
There was a problem hiding this comment.
Just raising in case it affects how you'd like to define this - the LMS fullscreen dialog appears to set preferNative = false. I think it probably does this to support launching LMS general (non-top-layer) dialogs nested within the fullscreen dialog. i.e. needed to ensure that nested non-top-layer LMS dialog stacks on top. I think it will still need to quietly opt out of native.
There was a problem hiding this comment.
Ah interesting. With the current code in this PR this would still be fine, but if we ever set _preferNative to true by default in dialog-fullscreen.js then that would break. Perhaps then it should be declared as an attribute but ignored from the docs so that we can easily set it within the LMS.
There was a problem hiding this comment.
Yeah. Via the attribute or setting it directly on the element in JavaScript. But yeah, wouldn't really want anyone else setting it.
|
|
||
| firstUpdated(changedProperties) { | ||
| super.firstUpdated(changedProperties); | ||
| this._useNative = (window.D2L.DialogMixin.hasNative && this.preferNative); |
There was a problem hiding this comment.
I'm guessing that this is here (instead of the constructor) so that a consumer can:
const dialog = document.createElement('d2l-dialog');
dialog.preferNative = ...;The unfortunate thing about firstUpdated() is that it runs after render(), so assigning to _useNative causes a second iteration render lifecycle.
We could move this to willUpdate() to avoid that. If we want to actively prevent someone from changing it after it's already assigned, we could add a check to not allow the update.
| * @ignore | ||
| * Do NOT use this | ||
| */ | ||
| preferNative: { type: Boolean, attribute: 'prefer-native' }, |
There was a problem hiding this comment.
OK so just a question, if we do not want this attribute to be used, but we still expose it, why not making this a "protected" instance variable that is initialized in this mixin as needed, and overridden by web components that extends from it as well ?
That way we do not expose that attribute and also (as long as I understand the changes) it is initialized only in the constructor. Maybe I am missing more code context, but sounds like that is what we want to achieve maybe.
There was a problem hiding this comment.
We will be setting it from the LMS code as well (context here). I had had it as this._preferNative and not an attribute prior to that, and I think in the LMS we technically could set that in the javascript, though to me that just feels like not a correct usage pattern (but I'm not totally opposed to doing that). I do think the risk of others setting this is likely low.
There was a problem hiding this comment.
Oh I see, well in that case and IMHO what I would do is to remove the property, make it a private instance variable, and expose a setter for that code you and Dave were discussing about. That is only to be consistent with the intention of really not having to expose something we really don't want to as an HTML attribute. But if this results to be more cumbersome then, I guess adding the @ignore should do the trick. Just worried about people going through the code a noticing there is a property that they can set as an html attribute.



Spike ticket: GAUD-7808
tldr it's quirky but possible; there are differences in how focus behaves between chrome vs others and potential screen reader issues but the situations can be handled with a recommended implementation.
Investigation notes:
Why we disabled it in the first place:
isFocusableoffsetParentfrom isFocusable here Fix dialog initial focus behaviour for Safari. #2963 so this particular issue isn't as much of a concern anymore (at least for confirm dialogs)Focus issues that now apply:
tabindex="0"so by its calculations that's where to focusInvestigation notes:
autofocus. However, currently only Chrome actually will useautofocuson a slotted elementautofocuson thedialogitself is more ideal (and perhaps we should do this for our general dialogs when we get here)What a great compromise, right? We'll just put
autofocuson thedialogand all our problems will be solved.ha.
PROBLEM: Chrome behaves differently when
autofocusis on adialogand there are interactive elements within the dialog (see this blog post which has great comparisons; Demo 6 is the one applicable here). It puts focus on the content with tabindex 0. If we remove that remove that tabindex then it puts focus on the "yes" button (worse).What I have done:
I have used the method in demo 11 which achieves the most consistency (all focus on the dialog initially, screen reader behaviour (VoiceOver so far, will test more) seems good. We do need to keep the tabindex=0 on the content as otherwise Safari doesn't actually read it as a description (though perhaps a more expert screen reader user than I could still navigate to it).
Option 2:
Put
autofocuson the dialog (for Firefox and Safari) and also on the secondary button (for chrome). This would have inconsistencies between browsers and also is a bit hacky to do.TODO:
Screen reader experience: