Skip to content
This repository has been archived by the owner on Mar 5, 2018. It is now read-only.

Commit

Permalink
Merge pull request #40 from robbiepitts/render-component-refactor
Browse files Browse the repository at this point in the history
Test and refactor rendering
  • Loading branch information
tomdale committed Apr 19, 2017
2 parents 708b114 + 1c637d0 commit c085b46
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 24 deletions.
68 changes: 50 additions & 18 deletions src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
import {
Simple,
templateFactory,
RenderResult,
ComponentDefinition,
Component
} from '@glimmer/runtime';
Expand All @@ -25,6 +24,8 @@ import DynamicScope from './dynamic-scope';
import Environment from './environment';
import mainTemplate from './templates/main';

function NOOP() {}

export interface ApplicationOptions {
rootName: string;
resolver: Resolver;
Expand All @@ -50,17 +51,20 @@ export default class Application implements Owner {
private _rootsIndex: number = 0;
private _registry: Registry;
private _container: Container;
private _renderResult: RenderResult;
/** Whether the initial render has completed. */
private _rendered: boolean;
/** Whether a re-render has been scheduled. */
private _scheduled: boolean;
private _initializers: Initializer[] = [];
private _initialized = false;
private _rendered = false;
private _scheduled = false;
private _rerender: () => void = NOOP;
private _afterRender: () => void = NOOP;
private _renderPromise: Option<Promise<void>>;

constructor(options: ApplicationOptions) {
this.rootName = options.rootName;
this.resolver = options.resolver;
this._renderPromise = new Promise<void>(resolve => {
this._afterRender = resolve;
});
}

/** @hidden */
Expand Down Expand Up @@ -136,30 +140,58 @@ export default class Application implements Owner {

this.env.commit();

let renderResult = result.value;

this._rerender = () => {
this.env.begin();
renderResult.rerender();
this.env.commit();
this._didRender();
};

this._didRender();
}

_didRender(): void {
let { _afterRender } = this;

this._afterRender = NOOP;
this._renderPromise = null;
this._rendered = true;
this._renderResult = result.value;

_afterRender();
}

renderComponent(component: string | ComponentDefinition<Component>, parent: Simple.Node, nextSibling: Option<Simple.Node>): void {
renderComponent(
component: string | ComponentDefinition<Component>,
parent: Simple.Node,
nextSibling: Option<Simple.Node> = null
): Promise<void> {
this._roots.push({ id: this._rootsIndex++, component, parent, nextSibling });
this.scheduleRerender();
return this.scheduleRerender();
}

/** @hidden */
rerender(): void {
this.env.begin();
this._renderResult.rerender();
this.env.commit();
scheduleRerender(): Promise<void> {
let { _renderPromise } = this;

if (_renderPromise === null) {
_renderPromise = this._renderPromise = new Promise<void>(resolve => {
this._afterRender = resolve;
});

this._scheduleRerender();
}

return _renderPromise;
}

/** @hidden */
scheduleRerender(): void {
if (this._scheduled || !this._rendered) { return; }
_scheduleRerender(): void {
if (this._scheduled || !this._rendered) return;

this._scheduled = true;
requestAnimationFrame(() => {
this._scheduled = false;
this.rerender();
this._rerender();
});
}

Expand Down
2 changes: 1 addition & 1 deletion test/action-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ test('can curry arguments to actions', function(assert) {
passedEvent = null;

helloWorldComponent.name = "cruel world";
app.rerender();
app.scheduleRerender();

h1 = app.rootElement.querySelector('h1');
h1.onclick(fakeEvent);
Expand Down
6 changes: 3 additions & 3 deletions test/environment-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ test('can render a component with the component helper', function(assert) {

assert.equal(app.rootElement.innerText, 'Hello Glimmer!');

app.rerender();
app.scheduleRerender();

assert.equal(app.rootElement.innerText, 'Hello Glimmer!');
});
Expand Down Expand Up @@ -95,7 +95,7 @@ test('can render a custom helper', function(assert) {

assert.equal(app.rootElement.innerText, 'Hello Glimmer!');

app.rerender();
app.scheduleRerender();

assert.equal(app.rootElement.innerText, 'Hello Glimmer!');
});
Expand All @@ -114,7 +114,7 @@ test('can render a custom helper that takes args', function(assert) {

assert.equal(app.rootElement.innerText, 'Hello Tom Dale!');

app.rerender();
app.scheduleRerender();

assert.equal(app.rootElement.innerText, 'Hello Tom Dale!');
});
Expand Down
122 changes: 122 additions & 0 deletions test/render-component-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import buildApp from './test-helpers/test-app';

const { module, test } = QUnit;

module('renderComponent');

test('renders a component', function(assert) {
assert.expect(1);

let containerElement = document.createElement('div');

let app = buildApp()
.template('hello-world', `<h1>Hello Glimmer!</h1>`)
.boot();

return app.renderComponent('hello-world', containerElement).then(() => {
assert.equal(containerElement.innerHTML, '<h1>Hello Glimmer!</h1>');
});
});

test('renders a component without affecting existing content', function(assert) {
assert.expect(2);

let containerElement = document.createElement('div');
let previousSibling = document.createElement('p');

previousSibling.appendChild(document.createTextNode('foo'));
containerElement.appendChild(previousSibling);
containerElement.appendChild(document.createTextNode('bar'));

let app = buildApp()
.template('hello-world', `<h1>Hello Glimmer!</h1>`)
.boot();

assert.equal(containerElement.innerHTML, '<p>foo</p>bar');

return app.renderComponent('hello-world', containerElement).then(() => {
assert.equal(containerElement.innerHTML, '<p>foo</p>bar<h1>Hello Glimmer!</h1>');
});
});

test('renders a component before a given sibling', function(assert) {
assert.expect(2);

let containerElement = document.createElement('div');
let previousSibling = document.createElement('p');
let nextSibling = document.createElement('aside');

containerElement.appendChild(previousSibling);
containerElement.appendChild(nextSibling);

let app = buildApp()
.template('hello-world', `<h1>Hello Glimmer!</h1>`)
.boot();

assert.equal(containerElement.innerHTML, '<p></p><aside></aside>');

return app.renderComponent('hello-world', containerElement, nextSibling).then(() => {
assert.equal(containerElement.innerHTML, '<p></p><h1>Hello Glimmer!</h1><aside></aside>');
});
});

test('renders multiple components in different places', function(assert) {
assert.expect(2);

let firstContainerElement = document.createElement('div');
let secondContainerElement = document.createElement('div');

let app = buildApp()
.template('hello-world', `<h1>Hello Glimmer!</h1>`)
.template('hello-robbie', `<h1>Hello Robbie!</h1>`)
.boot();

return Promise.all([
app.renderComponent('hello-world', firstContainerElement),
app.renderComponent('hello-robbie', secondContainerElement)
]).then(() => {
assert.equal(firstContainerElement.innerHTML, '<h1>Hello Glimmer!</h1>');
assert.equal(secondContainerElement.innerHTML, '<h1>Hello Robbie!</h1>');
});
});

test('renders multiple components in the same container', function(assert) {
assert.expect(1);

let containerElement = document.createElement('div');

let app = buildApp()
.template('hello-world', `<h1>Hello Glimmer!</h1>`)
.template('hello-robbie', `<h1>Hello Robbie!</h1>`)
.boot();

return Promise.all([
app.renderComponent('hello-world', containerElement),
app.renderComponent('hello-robbie', containerElement)
]).then(() => {
assert.equal(containerElement.innerHTML, '<h1>Hello Glimmer!</h1><h1>Hello Robbie!</h1>');
});
});

test('renders multiple components in the same container in particular places', function(assert) {
assert.expect(2);

let containerElement = document.createElement('div');
let nextSibling = document.createElement('aside');

containerElement.appendChild(nextSibling);

let app = buildApp()
.template('hello-world', `<h1>Hello Glimmer!</h1>`)
.template('hello-robbie', `<h1>Hello Robbie!</h1>`)
.boot();

assert.equal(containerElement.innerHTML, '<aside></aside>');

return Promise.all([
app.renderComponent('hello-world', containerElement),
app.renderComponent('hello-robbie', containerElement, nextSibling)
]).then(() => {
assert.equal(containerElement.innerHTML, '<h1>Hello Robbie!</h1><aside></aside><h1>Hello Glimmer!</h1>');
});
});
4 changes: 2 additions & 2 deletions test/test-helpers/test-app.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import Application from '../../src/application';
import Resolver, { BasicModuleRegistry } from '@glimmer/resolver';
import { Factory } from '@glimmer/di';

import { TestComponent, TestComponentManager } from './components';
import { precompile } from './compiler';

interface ComponentFactory {
export interface ComponentFactory {
create(injections: object): TestComponent;
}

Expand Down Expand Up @@ -52,6 +51,7 @@ export class AppBuilder {
constructor(name: string) {
this.rootName = name;
this.modules[`component-manager:/${this.rootName}/component-managers/main`] = TestComponentManager;
this.template('main', '<div />');
}

template(name: string, template: string) {
Expand Down

0 comments on commit c085b46

Please sign in to comment.