diff --git a/.gitignore b/.gitignore
index b947077..5c1bfd3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
node_modules/
dist/
+.idea/
diff --git a/README.md b/README.md
index d3d62d0..a138335 100644
--- a/README.md
+++ b/README.md
@@ -98,13 +98,15 @@ npm i react-to-webcomponent
## API
-`reactToWebComponent(ReactComponent, React, ReactDOM)` takes the following:
+`reactToWebComponent(ReactComponent, React, ReactDOM, options)` takes the following:
- `ReactComponent` - A react component that you want to
convert to a Web Component.
- `React` - A version of React (or [preact-compat](https://preactjs.com/guide/v10/switching-to-preact)) the
component works with.
- `ReactDOM` - A version of ReactDOM (or preact-compat) that the component works with.
+- `options` - An optional set of parameters.
+- `options.shadow` - Use shadow DOM rather than light DOM.
A new class inheriting from `HTMLElement` is
returned. This class can be directly passed to `customElements.define` as follows:
@@ -138,6 +140,19 @@ class WebGreeting extends reactToWebComponent(Greeting, React, ReactDOM)
customElements.define("web-greeting", WebGreeting);
```
+Components can also be implemented using [shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
+
+```js
+const WebGreeting = reactToWebComponent(Greeting, React, ReactDOM, { shadow: true });
+
+customElements.define("web-greeting", WebGreeting);
+
+var myGreeting = new WebGreeting();
+document.body.appendChild(myGreeting);
+
+var shadowContent = myGreeting.shadowRoot.children[0];
+```
+
### How it works
`reactToWebComponent` creates a constructor function whose prototype is a [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). This acts as a trap for any property set on instances of the custom element. When a property is set, the proxy:
diff --git a/react-to-webcomponent-test.js b/react-to-webcomponent-test.js
index 98c17e7..35d001e 100644
--- a/react-to-webcomponent-test.js
+++ b/react-to-webcomponent-test.js
@@ -144,3 +144,38 @@ QUnit.test("works within can-stache and can-stache-bindings (propTypes are writa
assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Bohdi", "can update");
});
+
+
+QUnit.test("works with shadow DOM `options.shadow === true`", function(assert) {
+ class Welcome extends React.Component {
+ render() {
+ return
Hello, {
+ this.props.name
+ }
;
+ }
+ }
+ Welcome.propTypes = {
+ user: PropTypes.string
+ };
+
+ class MyWelcome extends reactToWebComponent(Welcome, React, ReactDOM, { shadow: true }) {}
+
+ customElements.define("my-shadow-welcome", MyWelcome);
+
+ var fixture = document.getElementById("qunit-fixture");
+
+ var myWelcome = new MyWelcome();
+ fixture.appendChild(myWelcome);
+
+ assert.true(myWelcome.shadowRoot !== undefined, "shadow DOM is attached");
+
+ assert.equal(myWelcome.shadowRoot.children.length, 1, "able to render something in shadow DOM")
+
+ var child = myWelcome.shadowRoot.childNodes[0];
+ assert.equal(child.tagName, "H1", "renders the right tag name");
+ assert.equal(child.innerHTML, "Hello, ", "renders the right content");
+
+ myWelcome.name = "Justin";
+ child = myWelcome.shadowRoot.childNodes[0]
+ assert.equal(child.innerHTML, "Hello, Justin", "can update");
+});
diff --git a/react-to-webcomponent.js b/react-to-webcomponent.js
index 8c98256..d9aebf6 100644
--- a/react-to-webcomponent.js
+++ b/react-to-webcomponent.js
@@ -20,12 +20,22 @@ var define = {
}
}
-export default function(ReactComponent, React, ReactDOM) {
+
+/**
+ * Converts a React component into a webcomponent by wrapping it in a Proxy object.
+ * @param {ReactComponent}
+ * @param {React}
+ * @param {ReactDOM}
+ * @param {Object} options - Optional parameters
+ * @param {String?} options.shadow - Use shadow DOM rather than light DOM.
+ */
+export default function(ReactComponent, React, ReactDOM, options= {}) {
var renderAddedProperties = {isConnected: "isConnected" in HTMLElement.prototype};
var rendering = false;
// Create the web component "class"
var WebComponent = function() {
var self = Reflect.construct(HTMLElement, arguments, this.constructor);
+ self.attachShadow({ mode: 'open' });
return self;
};
@@ -43,7 +53,7 @@ export default function(ReactComponent, React, ReactDOM) {
// when any undefined property is set, create a getter/setter that re-renders
set: function(target, key, value, receiver) {
- if(rendering) {
+ if (rendering) {
renderAddedProperties[key] = true;
}
@@ -57,10 +67,10 @@ export default function(ReactComponent, React, ReactDOM) {
// makes sure the property looks writable
getOwnPropertyDescriptor: function(target, key){
var own = Reflect.getOwnPropertyDescriptor(target, key);
- if(own) {
+ if (own) {
return own;
}
- if(key in ReactComponent.propTypes) {
+ if (key in ReactComponent.propTypes) {
return { configurable: true, enumerable: true, writable: true, value: undefined };
}
}
@@ -83,7 +93,10 @@ export default function(ReactComponent, React, ReactDOM) {
}
}, this);
rendering = true;
- this[reactComponentSymbol] = ReactDOM.render(React.createElement(ReactComponent, data), this);
+ // Container is either shadow DOM or light DOM depending on `shadow` option.
+ const container = options.shadow ? this.shadowRoot : this;
+ // Use react to render element in container
+ this[reactComponentSymbol] = ReactDOM.render(React.createElement(ReactComponent, data), container);
rendering = false;
}
};