Skip to content

Commit

Permalink
basic multiroot test
Browse files Browse the repository at this point in the history
  • Loading branch information
lifeart committed Jan 16, 2025
1 parent 659378b commit fabe97d
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 33 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ import { renderComponent } from "@lifeart/gxt";
import App from "./App.gts";

const Instance = renderComponent(
new App().template(),
App,
document.getElementById("app"),
);
```
Expand Down
27 changes: 0 additions & 27 deletions src/components/Application.gts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@ import { PageThree } from './pages/PageThree.gts';
import { Benchmark } from './pages/Benchmark.gts';
import { NestedRouter } from './pages/NestedRouter.gts';
import { router } from './../services/router';
import { $context, addToTree } from '@/utils/shared';
import { cleanupFastContext, initDOM } from '@/utils/context';

export class Application extends Component {
router = router;
@tracked
now = Date.now();
rootNode!: HTMLElement;
components = {
pageOne: PageOne,
pageTwo: PageTwo,
Expand All @@ -27,30 +24,6 @@ export class Application extends Component {
};
async destroy() {
await Promise.all(runDestructors(this));
this.rootNode.innerHTML = '';
this.rootNode = null!;
}

constructor(
rootNode: HTMLElement | DocumentFragment,
root?: Root,
router?: unknown,
) {
super({});
if (root) {
addToTree(root, this);
}
cleanupFastContext();
if (root) {
initDOM(root);
this.args[$context] = root;
}
if (router) {
this.router = router;
}
this.rootNode = rootNode;
// @ts-ignore
renderComponent(this, this.rootNode, root);
}
<template>
{{#if IS_GLIMMER_COMPAT_MODE}}
Expand Down
76 changes: 76 additions & 0 deletions src/tests/integration/multiroot-test.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { module, test } from 'qunit';
import { find, render, rerender } from '@lifeart/gxt/test-utils';
import { Component, renderComponent, cell } from '@lifeart/gxt';
import { ROOT_CONTEXT, getContext } from '@/utils/context';

module('Integration | multiroot', function () {
test('could render one component to different places', async function (assert) {
const name = cell('Hello');
class AppOne extends Component {
<template>
<button type='button' data-test-button>{{name}}</button>
</template>
}
await render(
<template>
<div id='app-1'></div>
<div id='app-2'></div>
<iframe id='iframe-1'></iframe>
</template>,
);

const r1 = find('#app-1');
const r2 = find('#app-2');
// @ts-expect-error possible null
const r3 = (find('#iframe-1') as HTMLIFrameElement).contentWindow.document
.body;

assert.dom(r1).exists('app one node exists');
assert.dom(r2).exists('app two node exists');
assert.ok(r3, 'app three node exists');

const appOneInstance = renderComponent(AppOne, r1);
const appTwoInstance = renderComponent(AppOne, r2);

const appThreeInstance = renderComponent(AppOne, r3);

function qButton(r: Element) {
return r.querySelector('[data-test-button]') as HTMLButtonElement;
}

assert.dom(qButton(r1)).exists('button in app one exists');
assert.dom(qButton(r1)).hasText(name.value, 'button in app one has text');

assert.dom(qButton(r2)).exists('button in app two exists');
assert.dom(qButton(r2)).hasText(name.value, 'button in app two has text');

assert.dom(qButton(r3)).exists('button in app three exists');
assert.dom(qButton(r3)).hasText(name.value, 'button in app three has text');

assert.ok(appOneInstance, 'app one instance exists');
assert.ok(appTwoInstance, 'app two instance exists');
assert.ok(appThreeInstance, 'app three instance exists');

name.update('Foo');

await rerender();

assert
.dom(qButton(r1))
.hasText(name.value, 'button in app one has updated text');
assert
.dom(qButton(r2))
.hasText(name.value, 'button in app two has updated text');
assert
.dom(qButton(r3))
.hasText(name.value, 'button in app three has updated text');

const appOneRoot = getContext(appOneInstance.ctx!, ROOT_CONTEXT);
const appTwoRoot = getContext(appTwoInstance.ctx!, ROOT_CONTEXT);
const appThreeRoot = getContext(appThreeInstance.ctx!, ROOT_CONTEXT);

assert.notEqual(appOneRoot, appTwoRoot, `a1 & a2 roots differ`);
assert.notEqual(appTwoRoot, appThreeRoot, `a2 & a3 roots differ`);
assert.notEqual(appThreeRoot, appOneRoot, `a3 & a1 roots differ`);
});
});
5 changes: 3 additions & 2 deletions src/utils/benchmark/benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { measureRender } from '@/utils/benchmark/measure-render';
import { setResolveRender } from '@/utils/runtime';
import { Root } from '@/utils/dom';
import { cleanupFastContext } from '../context';
import { renderComponent } from '../component';

export function createBenchmark(doc: Document) {
return {
Expand All @@ -21,12 +22,12 @@ export function createBenchmark(doc: Document) {
console.error('Rehydration failed, fallback to normal render', e);
const fragment = doc.createDocumentFragment();
cleanupFastContext();
new Application(fragment, new Root(doc));
renderComponent(Application, fragment, new Root(doc));
root.innerHTML = '';
root.appendChild(fragment);
}
} else {
new Application(root);
renderComponent(Application, root);
}
});

Expand Down
25 changes: 23 additions & 2 deletions src/utils/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,13 @@ import {
PARENT,
$context,
} from './shared';
import { resolveRenderable, Root } from './dom';
import { provideContext, initDOM, RENDERING_CONTEXT } from './context';
import { resolveRenderable, Root, $_c } from './dom';
import {
provideContext,
initDOM,
RENDERING_CONTEXT,
cleanupFastContext,
} from './context';
import { cellToText, createRoot, MergedCell } from '.';

export type ComponentRenderTarget =
Expand Down Expand Up @@ -103,6 +108,8 @@ export function renderElement(
}
}

const OBJECT_PROTO = Object.getPrototypeOf({});

export function renderComponent(
component: ComponentReturnType | Component<any>,
target: ComponentRenderTarget,
Expand All @@ -114,6 +121,7 @@ export function renderComponent(
throw new Error(`Trying to render undefined`);
}
}
cleanupFastContext();
const targetElement = targetFor(target);

if (!skipRoot) {
Expand All @@ -127,6 +135,19 @@ export function renderComponent(
}
}

if (
typeof component === 'function' &&
Object.getPrototypeOf(component) !== OBJECT_PROTO
) {
component = $_c(
component,
{
[$context]: appRoot,
},
appRoot,
);
}

if (TRY_CATCH_ERROR_HANDLING) {
try {
if ($template in component && isFn(component[$template])) {
Expand Down
3 changes: 2 additions & 1 deletion src/utils/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -807,10 +807,11 @@ export const $_maybeHelper = (
return value;
};


function component(
comp: ComponentReturnType | Component,
args: Record<string, unknown>,
ctx: Component<any>,
ctx: Component<any> | Root,
) {
let label = IS_DEV_MODE
? `${
Expand Down

0 comments on commit fabe97d

Please sign in to comment.