-
Notifications
You must be signed in to change notification settings - Fork 58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Shadow DOM support (v5.3.0) #570
Conversation
🦋 Changeset detectedLatest commit: a52dbdb The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codecov Report
@@ Coverage Diff @@
## master #570 +/- ##
==========================================
+ Coverage 97.20% 97.61% +0.41%
==========================================
Files 1 1
Lines 143 210 +67
Branches 62 101 +39
==========================================
+ Hits 139 205 +66
- Misses 4 5 +1
Continue to review full report at Codecov.
|
@idoros @scomea @kee-oth I will be publishing betas of |
54ab754
to
c5bb540
Compare
Firefox claims to have implemented delegatesFocus support in November - https://www.ghacks.net/2021/11/02/firefox-94-0-release-here-is-what-is-new-and-changed/ |
Yay! I plugged it in and got it to work with some FAST stuff on a local branch. Want to poke at it some more though. I had to fiddle some with the options before I landed on this:
Is that the right way? I feel like the basic "just check the shadow dom without special knowledge" option would be easier to understand if this syntax worked as well: ie. 'getShadowRoot' typed as |
Started a draft pr @fast to test it out - microsoft/fast#5606 |
🚀
If you return a falsy value like The purpose of Here's what happens if you return a truthy value, and the difference between a plain shadowRoot === true ? element.children : shadowRoot.children The function is called only if @idoros Please confirm if I'm interpreting this correctly... I wonder if
I think that would result in tabbable only looking at open shadows. Is that what you want? Feels a bit strange to assign a boolean to an option name that really sounds like a function, but I get what you're asking. |
Interpreted correctly. I agree with @scomea that it might be helpful to also accept
Seems that they have. knowing about this might provide improvement for |
😅 Thanks for confirming! I think I can get into
|
@idoros There's a critical nuance I'm not understanding in the implementation, which is the difference between returning // iterate over content
const shadowRoot = element.shadowRoot || options.getShadowRoot(element);
if (shadowRoot) {
// add shadow dom scope
const nestedCandidates = getCandidatesIteratively(
shadowRoot === true ? element.children : shadowRoot.children, <- HERE (1)
true,
options
);
if (options.flatten) {
candidates.push(...nestedCandidates);
} else {
candidates.push({
scope: element,
candidates: nestedCandidates,
});
}
} else {
// add light dom scope
elementsToCheck.unshift(...element.children); <- AND HERE (2)
}
What is the subtle difference here? Either way, it seems we're digging into light DOM children in search of shadows, and if we find an open shadow, we automatically look inside of it. So true or false, as well as no shadow at all (open or closed), we do the same thing in the end. The only difference is when there's a closed shadow and |
Seems like you are correct. To be honest I can't remember, but from my comments in the original PR there is a difference between |
Thanks for pointing that out. Now true vs false is making more sense:
In light of those definitions, then I see how "true" informs the But what happens there if the I think that if needs to become:
WDYT? |
I think I missed this, and when a closed shadow root is passed, a better check can be done |
305b81e
to
31198f7
Compare
So far no "double focus" issues on components that use "delegate focus", so that's good. Still poking around. |
5290547
to
de1637e
Compare
Coming back to this after rebasing the branch (which included 2 fixes from master, one in particular being #604 that caused some conflicts, see de1637e for the changes) and I think I have a better understanding of the nuances going on with And so this request to support That's because for every candidate element we find, we do this test: const shadowRoot = element.shadowRoot || options.getShadowRoot(element); So if there's an open shadow, we don't even look at the If you want to opt-out of shadow DOM support, for whatever reason, then you have to provide So then, do we really need to support Opting out shadow DOM support means tabbable will most certainly not return the full set of nodes (if you use web components with closed shadows). Why would you want that, and would you want that often enough that we should have |
- new development shadow-root-utils - refactor setupFixture to render using new utils - refactor current shadow dom tests to use declarative root - debug page renders shadow root fixtures correctly
- iterate down dom instead of query when getShadowRoot is provided - new candidate list/tree format with scoped lists
- as requested in PR
This goes along with disabling it for `tabbable()` and `focusable()` when the option isn't given.
Note this is the equivalent of `getShadowRoot: () => false` which simply enables shadow DOM support for all open shadows.
…e default if set to some NaN (#610) * fix(index.js) The tabIndex of contentEditable elements was assumed to be zero in any case, not only in the case it was not specifically set. * Simplified and optimized 'getTabIndex'. * Made better use of short-circuit evaluation in 'isNodeMatchingSelectorTabbable', reducing the chances to call the computationally expensive 'isNodeMatchingSelectorFocusable'. * (Re)Added 'isScope' parameter to 'getTabIndex'. This parameter wasn't present in the master branch, so I lost it in the rebase process. * Added tests for a `contenteditable` with negative tab index. * Fixed bug, now the getTabIndex can return 0 not only when the tabindex is not explicitly set, but also when is set to a value that gives NaN when parsed as integer (which would have been resulted in the default browser tabIndex, as if the tabindex wasn't set at all). Also added test for the case an element has a tab index that can't be turned into an integer. * Added changeset, added entry in CHANGELOG.md and wrote more tests. * Be consistent with asterisks * Sync package.json/yarn.lock with beta-530 base branch Co-authored-by: Stefan Cameron <[email protected]>
Well, no time like the present. I haven't heard from anyone for a long time now, so I'm going to assume that "no news is good news" and will merge and publish. Big thanks to everyone who contributed and reviewed! |
Published in v5.3.0 just now. 🎉 |
👉 See #304 for initial PR and discussion history
This PR adds support for shadow dom (as requested in #50). Currently I just placed all tests in a single e2e spec, because it was much easier to handle, I will probably break it down into the 4 apis before we merge. The code is separated into 2 parts:
new options API:
getShadowRoot(element): called for any element without
shadowRoot
property in order to detect shadow dom. (returnshadowRoot
in order to query internally,true
to indicate there is a shadow-root, but it's not available orfalse
for no shadow-root)dev support for fixtures with declarative shadow dom:
setupFixture(content, {**caseId**})
.support isTabbable/isFocusable:
display=none
or is not slotted, then the element will be wrongly considered focusable (only fordisplayCheck: "full"
).support scanning dom via walking instead of query
getShadowRoot
option is supplied then walking is picked over query and shadow-roots and slots are visiteddelegatesFocus
is not implemented here.I played with it, but it only works in chromium (can't get it to work on Firefox, although MDN states differently), and in any case it's marked as deprecatednot implemented in FirefoxisTabbable/isFocusable
. The check try to findshadowRoot.delegatesFocus
(currently exposed on chromium), or ask foroptions.delegatesFocus(node)
and allow whoever uses Tabbable to provide the flag according to available shadow knowledge and browser support.shadowrootdelegatesfocus
in the declarative shadow dom dev utils for nowtypes
documentation - README updated (API changes, instructions, etc.).
make sure source is compatible with the acceptable ECMAScript version
Add support for
getShadowRoot: true
Changeset
Some thoughts:
customElements.define
orElement.prototype.attachShadow
🤔Fixes #50