Skip to content

Commit

Permalink
Fix queryAssignedElements decorator legacy browser compatibility (#2392)
Browse files Browse the repository at this point in the history
Fix `@queryAssignedElements` decorator so it is compatible with legacy browsers.
Uses `HTMLSlotElement.assignedElements` if available with a graceful fallback
on `HTMLSlotElement.assignedNodes` which is supported by polyfills.

Co-authored-by: Andrew Jakubowicz <ajakubowicz@google.com>
  • Loading branch information
AndrewJakubowicz and AndrewJakubowicz committed Jan 12, 2022
1 parent 4a5369c commit dc3301c
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 11 deletions.
7 changes: 7 additions & 0 deletions .changeset/stupid-rockets-deliver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@lit/reactive-element': patch
---

Fix `@queryAssignedElements` decorator so it is compatible with legacy browsers.
Uses `HTMLSlotElement.assignedElements` if available with a graceful fallback
on `HTMLSlotElement.assignedNodes` which is supported by polyfills.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@ import {decorateProperty} from './base.js';
import type {ReactiveElement} from '../reactive-element.js';
import type {QueryAssignedNodesOptions} from './query-assigned-nodes.js';

/**
* A tiny module scoped polyfill for HTMLSlotElement.assignedElements.
*/
const slotAssignedElements =
window.HTMLSlotElement?.prototype.assignedElements != null
? (slot: HTMLSlotElement, opts?: AssignedNodesOptions) =>
slot.assignedElements(opts)
: (slot: HTMLSlotElement, opts?: AssignedNodesOptions) =>
slot
.assignedNodes(opts)
.filter(
(node): node is Element => node.nodeType === Node.ELEMENT_NODE
);

/**
* Options for the [[`queryAssignedElements`]] decorator. Extends the options
* that can be passed into
Expand Down Expand Up @@ -67,7 +81,8 @@ export function queryAssignedElements(options?: QueryAssignedElementsOptions) {
const slotSelector = `slot${slot ? `[name=${slot}]` : ':not([name])'}`;
const slotEl =
this.renderRoot?.querySelector<HTMLSlotElement>(slotSelector);
const elements = slotEl?.assignedElements(options) ?? [];
const elements =
slotEl != null ? slotAssignedElements(slotEl, options) : [];
if (selector) {
return elements.filter((node) => node.matches(selector));
}
Expand Down
22 changes: 12 additions & 10 deletions packages/reactive-element/src/decorators/query-assigned-nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/

import {decorateProperty} from './base.js';
import {queryAssignedElements} from './query-assigned-elements.js';

import type {ReactiveElement} from '../reactive-element.js';

Expand Down Expand Up @@ -114,23 +115,24 @@ export function queryAssignedNodes(
} else {
assignedNodesOptions = {flatten};
}
selector ??= '';

// For backwards compatibility, queryAssignedNodes with a selector behaves
// exactly like queryAssignedElements with a selector.
if (selector) {
return queryAssignedElements({
slot: slot as string,
flatten,
selector,
});
}

return decorateProperty({
descriptor: (_name: PropertyKey) => ({
get(this: ReactiveElement) {
const slotSelector = `slot${slot ? `[name=${slot}]` : ':not([name])'}`;
const slotEl =
this.renderRoot?.querySelector<HTMLSlotElement>(slotSelector);
let nodes = slotEl?.assignedNodes(assignedNodesOptions) ?? [];
if (selector) {
nodes = nodes.filter(
(node) =>
node.nodeType === Node.ELEMENT_NODE &&
(node as Element).matches(selector as string)
);
}
return nodes;
return slotEl?.assignedNodes(assignedNodesOptions) ?? [];
},
enumerable: true,
configurable: true,
Expand Down

0 comments on commit dc3301c

Please sign in to comment.