-
Notifications
You must be signed in to change notification settings - Fork 77
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
refactor(dom): Update getSlotted utility to handle querying for item(s) in default slot #3788
Changes from all commits
e304b8b
a544156
4412bcb
01780f4
79b4cf8
9cb29d1
d9a7b62
ee92c3f
2b51a40
f9f8cdf
9c14d47
124d826
1c547a0
5b8fcba
04828b7
f16e601
6149935
8b9b31d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -101,7 +101,11 @@ describe("dom", () => { | |
} | ||
|
||
connectedCallback(): void { | ||
this.shadowRoot.innerHTML = `<slot name="${testSlotName}"></slot><slot name="${testSlotName2}"></slot>`; | ||
this.shadowRoot.innerHTML = html` | ||
<slot name="${testSlotName}"></slot> | ||
<slot name="${testSlotName2}"></slot> | ||
<slot></slot> | ||
`; | ||
} | ||
} | ||
|
||
|
@@ -119,11 +123,17 @@ describe("dom", () => { | |
</h2> | ||
<h2 slot=${testSlotName}><span>😂</span></h2> | ||
<h3 slot=${testSlotName2}><span>😂</span></h3> | ||
<div id="default-slot-el"><p>🙂</p></div> | ||
</slot-test> | ||
`; | ||
|
||
const assignedSlot = document.querySelector("slot-test").shadowRoot.querySelector(`slot:not([name])`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a workaround for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this is a workaround. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Without this, the assignedNode won't be there. So we would have to run the getSlotted() method in the browser. Which means placing the whole utility in the browser so that all methods can be accessed. |
||
(document.getElementById("default-slot-el") as any).assignedSlot = assignedSlot; | ||
}); | ||
|
||
describe("single slotted", () => { | ||
driskull marked this conversation as resolved.
Show resolved
Hide resolved
|
||
it("returns elements with matching default slot", () => expect(getSlotted(getTestComponent())).toBeTruthy()); | ||
|
||
it("returns elements with matching slot name", () => | ||
expect(getSlotted(getTestComponent(), testSlotName)).toBeTruthy()); | ||
|
||
|
@@ -133,6 +143,9 @@ describe("dom", () => { | |
it("returns null when no results", () => expect(getSlotted(getTestComponent(), "non-existent-slot")).toBeNull()); | ||
|
||
describe("scoped selector", () => { | ||
it("returns element with matching default slot", () => | ||
expect(getSlotted(getTestComponent(), { selector: "p" })).toBeTruthy()); | ||
|
||
it("returns element with matching nested selector", () => | ||
expect(getSlotted(getTestComponent(), testSlotName, { selector: "span" })).toBeTruthy()); | ||
|
||
|
@@ -182,6 +195,9 @@ describe("dom", () => { | |
}); | ||
|
||
describe("multiple slotted", () => { | ||
it("returns element with default slot name", () => | ||
expect(getSlotted(getTestComponent(), { all: true })).toHaveLength(1)); | ||
|
||
it("returns elements with matching slot name", () => | ||
expect(getSlotted(getTestComponent(), testSlotName, { all: true })).toHaveLength(2)); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -162,24 +162,33 @@ interface GetSlottedOptions { | |
selector?: string; | ||
} | ||
|
||
const defaultSlotSelector = "> :not([slot])"; | ||
|
||
export function getSlotted<T extends Element = Element>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be typed like so: export function getSlotted<T extends Element = Element>(element: Element, options: GetSlottedOptions & { all: true }): T[];
export function getSlotted<T extends Element = Element>(element: Element, options?: GetSlottedOptions): T[];
export function getSlotted<T extends Element = Element>(element: Element, slotName: string | string[], options: GetSlottedOptions & { all: true }): T[];
export function getSlotted<T extends Element = Element>(element: Element, slotName: string | string[], options?: GetSlottedOptions): T[];
export function getSlotted<T extends Element = Element>(
element: Element,
slotNameOrOptions: string | string[] | GetSlottedOptions,
optionsOrUndefined?: GetSlottedOptions): (T | null) | T[] { /* ... */ } Can you create a separate refactor issue this one and assign it to me? |
||
element: Element, | ||
slotName: string | string[], | ||
slotName: string | string[] | (GetSlottedOptions & { all: true }), | ||
options: GetSlottedOptions & { all: true } | ||
): T[]; | ||
export function getSlotted<T extends Element = Element>( | ||
element: Element, | ||
slotName: string | string[], | ||
slotName?: string | string[] | GetSlottedOptions, | ||
options?: GetSlottedOptions | ||
): T | null; | ||
export function getSlotted<T extends Element = Element>( | ||
element: Element, | ||
slotName: string | string[], | ||
slotName?: string | string[] | GetSlottedOptions, | ||
options?: GetSlottedOptions | ||
): (T | null) | T[] { | ||
const slotSelector = Array.isArray(slotName) | ||
? slotName.map((name) => `[slot="${name}"]`).join(",") | ||
: `[slot="${slotName}"]`; | ||
if (!Array.isArray(slotName) && typeof slotName !== "string") { | ||
options = slotName; | ||
slotName = null; | ||
} | ||
|
||
const slotSelector = slotName | ||
? Array.isArray(slotName) | ||
? slotName.map((name) => `[slot="${name}"]`).join(",") | ||
: `[slot="${slotName}"]` | ||
: defaultSlotSelector; | ||
|
||
if (options?.all) { | ||
return queryMultiple<T>(element, slotSelector, options); | ||
|
@@ -196,6 +205,10 @@ function queryMultiple<T extends Element = Element>( | |
let matches = Array.from(element.querySelectorAll<T>(slotSelector)); | ||
matches = options && options.direct === false ? matches : matches.filter((el) => el.parentElement === element); | ||
|
||
if (slotSelector === defaultSlotSelector) { | ||
matches = matches.filter((match) => match?.assignedSlot); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jcfranco It seems like this would work but we would need to move the spec tests to e2e for assignedSlot to work I think. |
||
} | ||
|
||
const selector = options?.selector; | ||
return selector | ||
? matches | ||
|
@@ -211,10 +224,15 @@ function querySingle<T extends Element = Element>( | |
options?: GetSlottedOptions | ||
): T | null { | ||
let match = element.querySelector<T>(slotSelector); | ||
|
||
if (slotSelector === defaultSlotSelector) { | ||
match = match?.assignedSlot ? match : null; | ||
} | ||
|
||
match = options && options.direct === false ? match : match?.parentElement === element ? match : null; | ||
|
||
const selector = options?.selector; | ||
return selector ? match.querySelector<T>(selector) : match; | ||
return selector ? match?.querySelector<T>(selector) : match; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the default slot selector will also return children in a custom element without a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think the |
||
} | ||
|
||
export function filterDirectChildren<T extends Element>(el: Element, selector: string): T[] { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I missed this in review :( cc @Elijbet