Skip to content

Commit

Permalink
Moves JSXRenderer to its own file
Browse files Browse the repository at this point in the history
  • Loading branch information
mairatma committed Aug 30, 2016
1 parent 2fda3e4 commit 774ddbc
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 172 deletions.
64 changes: 12 additions & 52 deletions packages/metal-jsx/src/JSXComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,19 @@ import { validators, Config } from 'metal-state';
import Component from 'metal-component';
import IncrementalDomRenderer from 'metal-incremental-dom';
import JSXDataManager from './JSXDataManager';
import JSXRenderer from './JSXRenderer';

/**
* A component that has built-in integration with JSX templates. Example:
*
* <code>
* class MyComponent extends JSXComponent {
* render() {
* return <div>Hello World</div>
* }
* }
* </code>
*/
class JSXComponent extends Component {
/**
* Creates and renders the given function, which can either be a simple
Expand All @@ -22,58 +34,6 @@ class JSXComponent extends Component {
}
}

/**
* Renderer that handles JSX.
*/
class JSXRenderer extends IncrementalDomRenderer {
/**
* @inheritDoc
*/
buildShouldUpdateArgs_() {
return [this.changes_, this.propChanges_];
}

/**
* @inheritDoc
*/
clearChanges_() {
super.clearChanges_();
this.propChanges_ = {};
}

/**
* @inheritDoc
*/
handleDataPropChanged_(data) {
if (data.type === 'props') {
this.propChanges_[data.key] = data;
} else {
super.handleDataPropChanged_(data);
}
}

/**
* @inheritDoc
*/
hasDataChanged_() {
return super.hasDataChanged_() || Object.keys(this.propChanges_).length > 0;
}

/**
* Overrides the original method from `IncrementalDomRenderer` to handle the
* case where developers return a child node directly from the "render"
* function.
* @override
*/
renderIncDom() {
if (this.component_.render) {
iDOMHelpers.renderArbitrary(this.component_.render());
} else {
super.renderIncDom();
}
}
}

JSXComponent.DATA_MANAGER = JSXDataManager;
JSXComponent.RENDERER = JSXRenderer;

Expand Down
57 changes: 57 additions & 0 deletions packages/metal-jsx/src/JSXRenderer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use strict';

import IncrementalDomRenderer from 'metal-incremental-dom';

/**
* Renderer that handles JSX.
*/
class JSXRenderer extends IncrementalDomRenderer {
/**
* @inheritDoc
*/
buildShouldUpdateArgs_() {
return [this.changes_, this.propChanges_];
}

/**
* @inheritDoc
*/
clearChanges_() {
super.clearChanges_();
this.propChanges_ = {};
}

/**
* @inheritDoc
*/
handleDataPropChanged_(data) {
if (data.type === 'props') {
this.propChanges_[data.key] = data;
} else {
super.handleDataPropChanged_(data);
}
}

/**
* @inheritDoc
*/
hasDataChanged_() {
return super.hasDataChanged_() || Object.keys(this.propChanges_).length > 0;
}

/**
* Overrides the original method from `IncrementalDomRenderer` to handle the
* case where developers return a child node directly from the "render"
* function.
* @override
*/
renderIncDom() {
if (this.component_.render) {
iDOMHelpers.renderArbitrary(this.component_.render());
} else {
super.renderIncDom();
}
}
}

export default JSXRenderer;
120 changes: 0 additions & 120 deletions packages/metal-jsx/test/JSXComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,6 @@ describe('JSXComponent', function() {
assert.strictEqual('Hello World', component.element.textContent);
});

it('should render returned contents from variable in "render" function', function() {
class TestComponent extends JSXComponent {
render() {
var jsx = <div class="test">Hello World</div>;
return jsx;
}
}

component = new TestComponent();
assert.strictEqual('DIV', component.element.tagName);
assert.ok(dom.hasClass(component.element, 'test'));
assert.strictEqual('Hello World', component.element.textContent);
});

it('should not throw error if no jsx function is implemented', function() {
class TestComponent extends JSXComponent {
}
Expand Down Expand Up @@ -171,34 +157,6 @@ describe('JSXComponent', function() {
assert.strictEqual('Children Test 2', child.element.textContent);
});

it('should be able to render a child without wrapper element', function() {
class ChildComponent extends JSXComponent {
render() {
return this.props.children[1];
}
}

class TestComponent extends JSXComponent {
render() {
return (
<div class="test">
<ChildComponent ref="child">
<span>Children Test</span>
<span>Children Test 2</span>
<span>Children Test 3</span>
</ChildComponent>
</div>
);
}
}

component = new TestComponent();
var child = component.components.child;
assert.strictEqual('SPAN', child.element.tagName);
assert.strictEqual(1, child.element.childNodes.length);
assert.strictEqual('Children Test 2', child.element.textContent);
});

it('should be able to get the data passed to children', function() {
class ChildComponent extends JSXComponent {
render() {
Expand Down Expand Up @@ -315,50 +273,6 @@ describe('JSXComponent', function() {
assert.strictEqual('defaultFoo', component.state.foo);
});

it('should update if props change', function(done) {
class TestComponent extends JSXComponent {
render() {
return <div>{this.props.foo}</div>
}
}
TestComponent.PROPS = {
foo: {
value: 'defaultFoo'
}
}

component = new TestComponent();
assert.strictEqual('defaultFoo', component.element.textContent);

component.props.foo = 'foo';
component.once('rendered', function() {
assert.strictEqual('foo', component.element.textContent);
done();
});
});

it('should update if state changes', function(done) {
class TestComponent extends JSXComponent {
render() {
return <div>{this.state.foo}</div>
}
}
TestComponent.STATE = {
foo: {
value: 'defaultFoo'
}
}

component = new TestComponent();
assert.strictEqual('defaultFoo', component.element.textContent);

component.state.foo = 'foo';
component.once('rendered', function() {
assert.strictEqual('foo', component.element.textContent);
done();
});
});

it('should call "propsChanged" when new props are passed', function(done) {
class ChildComponent extends JSXComponent {
render() {
Expand Down Expand Up @@ -395,40 +309,6 @@ describe('JSXComponent', function() {
});

describe('shouldUpdate', function() {
it('should pass both state and prop changes to shouldUpdate', function(done) {
class TestComponent extends JSXComponent {
shouldUpdate() {
}
}
TestComponent.PROPS = {
bar: {
}
}
TestComponent.STATE = {
foo: {
}
}

component = new TestComponent();
sinon.stub(component, 'shouldUpdate');
component.props.bar = 'bar';
component.state.foo = 'foo';
component.getDataManager().once('dataChanged', function() {
assert.strictEqual(1, component.shouldUpdate.callCount);

const stateChanges = component.shouldUpdate.args[0][0];
assert.ok(stateChanges.foo);
assert.strictEqual('foo', stateChanges.foo.newVal);
assert.strictEqual(undefined, stateChanges.foo.prevVal);

const propChanges = component.shouldUpdate.args[0][1];
assert.ok(propChanges.bar);
assert.strictEqual('bar', propChanges.bar.newVal);
assert.strictEqual(undefined, propChanges.bar.prevVal);
done();
});
});

it('should not rerender after props change if shouldUpdate returns false', function(done) {
class TestComponent extends JSXComponent {
render() {
Expand Down
Loading

0 comments on commit 774ddbc

Please sign in to comment.