diff --git a/src/index.js b/src/index.js
index 4916451..287e6b7 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,4 +1,4 @@
-import { h, cloneElement, render, hydrate } from 'preact';
+import { h, cloneElement, render, hydrate, Fragment } from 'preact';
/**
* @typedef {import('./index.d.ts').PreactCustomElement} PreactCustomElement
@@ -26,7 +26,9 @@ export default function register(Component, tagName, propNames, options) {
}
PreactElement.prototype = Object.create(HTMLElement.prototype);
PreactElement.prototype.constructor = PreactElement;
- PreactElement.prototype.connectedCallback = connectedCallback;
+ PreactElement.prototype.connectedCallback = function () {
+ connectedCallback.call(this, options);
+ };
PreactElement.prototype.attributeChangedCallback = attributeChangedCallback;
PreactElement.prototype.disconnectedCallback = disconnectedCallback;
@@ -89,7 +91,7 @@ function ContextProvider(props) {
/**
* @this {PreactCustomElement}
*/
-function connectedCallback() {
+function connectedCallback(options) {
// Obtain a reference to the previous context by pinging the nearest
// higher up node that was rendered with Preact. If one Preact component
// higher up receives our ping, it will set the `detail` property of
@@ -106,7 +108,7 @@ function connectedCallback() {
this._vdom = h(
ContextProvider,
{ ...this._props, context },
- toVdom(this, this._vdomComponent)
+ toVdom(this, this._vdomComponent, options)
);
(this.hasAttribute('hydrate') ? hydrate : render)(this._vdom, this._root);
}
@@ -170,10 +172,11 @@ function Slot(props, context) {
}
}
};
- return h('slot', { ...props, ref });
+ const { useFragment, ...rest } = props;
+ return h(useFragment ? Fragment : 'slot', { ...rest, ref });
}
-function toVdom(element, nodeName) {
+function toVdom(element, nodeName, options) {
if (element.nodeType === 3) return element.data;
if (element.nodeType !== 1) return null;
let children = [],
@@ -189,7 +192,7 @@ function toVdom(element, nodeName) {
}
for (i = cn.length; i--; ) {
- const vnode = toVdom(cn[i], null);
+ const vnode = toVdom(cn[i], null, options);
// Move slots correctly
const name = cn[i].slot;
if (name) {
@@ -199,7 +202,15 @@ function toVdom(element, nodeName) {
}
}
+ const shadow = !!(options && options.shadow);
+
// Only wrap the topmost node with a slot
- const wrappedChildren = nodeName ? h(Slot, null, children) : children;
+ const wrappedChildren = nodeName
+ ? h(Slot, { useFragment: !shadow }, children)
+ : children;
+
+ if (!shadow && nodeName) {
+ element.innerHTML = '';
+ }
return h(nodeName || element.nodeName.toLowerCase(), props, wrappedChildren);
}
diff --git a/src/index.test.jsx b/src/index.test.jsx
index 9609a08..fbbe185 100644
--- a/src/index.test.jsx
+++ b/src/index.test.jsx
@@ -1,9 +1,21 @@
import { assert } from '@open-wc/testing';
-import { h, createContext, Component } from 'preact';
+import { h, createContext, Component, Fragment } from 'preact';
import { useContext } from 'preact/hooks';
import { act } from 'preact/test-utils';
import registerElement from './index';
+/** @param {string} name */
+function createTestElement(name) {
+ const el = document.createElement(name);
+ const child1 = document.createElement('p');
+ child1.textContent = 'Child 1';
+ const child2 = document.createElement('p');
+ child2.textContent = 'Child 2';
+ el.appendChild(child1);
+ el.appendChild(child2);
+ return el;
+}
+
describe('web components', () => {
/** @type {HTMLDivElement} */
let root;
@@ -359,4 +371,54 @@ describe('web components', () => {
const style = getComputedStyle(child);
assert.equal(style.color, 'rgb(255, 0, 0)');
});
+
+ it('supports controlling light DOM children', async () => {
+ function LightDomChildren({ children }) {
+ return (
+ Light DOM Children
+
Child 1
Child 2
Child 1
Child 2
Child 1
Child 2