diff --git a/src/__tests__/__snapshots__/tests.ts.snap b/src/__tests__/__snapshots__/tests.ts.snap
index 371885a..cd4d07f 100644
--- a/src/__tests__/__snapshots__/tests.ts.snap
+++ b/src/__tests__/__snapshots__/tests.ts.snap
@@ -1,26 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`reactFromMarkupContainer E2E tests Should rehydrate a basic component 1`] = `
-"
-
- rehydrated component
-
"
-`;
-
-exports[`reactFromMarkupContainer E2E tests Should rehydrate valid HTML markup 1`] = `
-"
- "
-`;
+exports[`reactFromHtml E2E tests Should rehydrate a basic component 1`] = `"rehydrated component
"`;
-exports[`reactFromMarkupContainer E2E tests Should work for nested markup containers 1`] = `
+exports[`reactFromHtml E2E tests Should work for nested rehydratables 1`] = `
"
-
-
rehydrated component
-
- rehydrated component
- rehydrated component
-
-
"
+
+ "
`;
diff --git a/src/__tests__/tests.ts b/src/__tests__/tests.ts
index b5f39a6..dad62c6 100644
--- a/src/__tests__/tests.ts
+++ b/src/__tests__/tests.ts
@@ -1,8 +1,8 @@
/* eslint-env jest */
import * as React from "react";
-import reactFromMarkupContainer from "..";
+import reactFromHtml from "..";
-describe("reactFromMarkupContainer E2E tests", async () => {
+describe("reactFromHtml E2E tests", async () => {
it("Should rehydrate a basic component", async () => {
const componentName: string = "myComponent";
@@ -13,63 +13,46 @@ describe("reactFromMarkupContainer E2E tests", async () => {
const rehydrators = { [componentName]: rehydrator };
const documentElement = document.createElement("div");
- documentElement.innerHTML = `
- `;
+ documentElement.innerHTML = ``;
- await reactFromMarkupContainer(documentElement, rehydrators, {
+ await reactFromHtml(documentElement, rehydrators, {
extra: {},
});
expect(documentElement.innerHTML).toMatchSnapshot();
});
- it("Should rehydrate valid HTML markup", async () => {
- const documentElement = document.createElement("div");
-
- documentElement.innerHTML = `
- `;
-
- await reactFromMarkupContainer(documentElement, {}, { extra: {} });
-
- expect(documentElement.innerHTML).toMatchSnapshot();
- });
-
- it("Should work for nested markup containers", async () => {
+ it("Should work for nested rehydratables", async () => {
const componentName: string = "mycomponentName";
const mockCall = jest.fn();
const rehydrators = {
- [componentName]: async () => {
+ [componentName]: async (node: HTMLElement) => {
mockCall();
- return React.createElement("span", {}, "rehydrated component");
+ await reactFromHtml(node, rehydrators, { extra: {} });
+
+ return React.createElement("span", {
+ dangerouslySetInnerHTML: { __html: node.innerHTML },
+ });
},
};
const documentElement = document.createElement("div");
documentElement.innerHTML = `
- `;
-
- await reactFromMarkupContainer(documentElement, rehydrators, {
+
+ `;
+
+ await reactFromHtml(documentElement, rehydrators, {
extra: {},
});
expect(documentElement.innerHTML).toMatchSnapshot();
- expect(mockCall).toBeCalledTimes(3);
+ expect(mockCall).toBeCalledTimes(2);
});
});
diff --git a/src/rehydrator.ts b/src/rehydrator.ts
index 736d40b..f200d3e 100644
--- a/src/rehydrator.ts
+++ b/src/rehydrator.ts
@@ -23,7 +23,8 @@ const rehydratableToReactElement = async (
return rehydrator(
el,
- children => rehydrateChildren(children, rehydrators, options),
+ async children =>
+ (await rehydrateChildren(children, rehydrators, options)).rehydrated,
options.extra
);
};
@@ -44,11 +45,34 @@ const createCustomHandler = (
return false;
};
-const rehydrateChildren = (
+const createReactRoot = (el: Node) => {
+ const container = document.createElement("div");
+
+ if (el.parentNode) {
+ el.parentNode.replaceChild(container, el);
+ }
+
+ container.appendChild(el);
+ container.classList.add("rehydration-root");
+
+ return container;
+};
+
+const rehydrateChildren = async (
el: Node,
rehydrators: IRehydrator,
options: IOptions
-) => domElementToReact(el, createCustomHandler(rehydrators, options));
+) => {
+ const container = createReactRoot(el);
+
+ return {
+ container,
+ rehydrated: await domElementToReact(
+ container,
+ createCustomHandler(rehydrators, options)
+ ),
+ };
+};
const render = ({
rehydrated,
@@ -67,14 +91,23 @@ const render = ({
ReactDOM.render(rehydrated as React.ReactElement, root);
};
+const createQuerySelector = (rehydratableIds: string[]) =>
+ rehydratableIds.reduce(
+ (acc: string, rehydratableId: string) =>
+ `${acc ? `${acc}, ` : ""}[data-rehydratable*="${rehydratableId}"]`,
+ ""
+ );
+
export default async (
container: Element,
rehydrators: IRehydrator,
options: IOptions
) => {
+ const selector = createQuerySelector(Object.keys(rehydrators));
+
const roots = Array.from(
// TODO: allow setting a container identifier so multiple rehydration instances can exist
- container.querySelectorAll("[data-react-from-html-container]")
+ container.querySelectorAll(selector)
).reduce((acc: Element[], root: Element) => {
// filter roots that are contained within other roots
if (!acc.some(r => r.contains(root))) {
@@ -92,13 +125,12 @@ export default async (
if (container.contains(root)) {
renders.push(async () => {
try {
- const rehydrated = await rehydrateChildren(
- root,
- rehydrators,
- options
- );
+ const {
+ container: rootContainer,
+ rehydrated,
+ } = await rehydrateChildren(root, rehydrators, options);
- return { root, rehydrated };
+ return { root: rootContainer, rehydrated };
} catch (e) {
/* tslint:disable-next-line no-console */
console.error("Rehydration failure", e);