From f266bd1d7c0a9fbed613c7faa4091234a5378c88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ovidiu=20Chereche=C8=99?= Date: Thu, 16 Apr 2015 19:57:42 +0300 Subject: [PATCH 1/6] injectState using new ComponentTree API #8 --- src/components/component-playground.jsx | 27 ++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/components/component-playground.jsx b/src/components/component-playground.jsx index a5d358b..ea1abd7 100644 --- a/src/components/component-playground.jsx +++ b/src/components/component-playground.jsx @@ -89,7 +89,7 @@ module.exports = React.createClass({ key: JSON.stringify(this.state.fixtureContents) }; - return _.merge(props, this.state.fixtureContents); + return _.merge(props, _.omit(this.state.fixtureContents, 'state')); } }, @@ -255,6 +255,12 @@ module.exports = React.createClass({ ; }, + componentDidMount: function() { + if (this.refs.preview) { + this._injectPreviewChildState(); + } + }, + componentWillReceiveProps: function(nextProps) { if (nextProps.selectedComponent !== this.props.selectedComponent || nextProps.selectedFixture !== this.props.selectedFixture) { @@ -263,6 +269,17 @@ module.exports = React.createClass({ } }, + componentDidUpdate: function(prevProps, prevState) { + if (this.refs.preview && ( + // Avoid deep comparing the fixture contents when component and/or + // fixture changed, because it's more expensive + this.props.selectedComponent !== prevProps.selectedComponent || + this.props.selectedFixture !== prevProps.selectedFixture || + !_.isEqual(this.state.fixtureContents, prevState.fixtureContents))) { + this._injectPreviewChildState(); + } + }, + onComponentClick: function(componentName, event) { event.preventDefault(); @@ -324,5 +341,13 @@ module.exports = React.createClass({ fixtureName === this.props.selectedFixture; return classNames(classes); + }, + + _injectPreviewChildState: function() { + var state = this.state.fixtureContents.state; + + if (!_.isEmpty(state)) { + ComponentTree.injectState(this.refs.preview, _.cloneDeep(state)); + } } }); From 3219fd7878f9df5cfc7960950fbd97b6210fde9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ovidiu=20Chereche=C8=99?= Date: Thu, 16 Apr 2015 20:00:54 +0300 Subject: [PATCH 2/6] Test state injection #8 --- .../component-playground/children.js | 12 +++++++++++- .../components/component-playground/state.js | 19 +++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/tests/components/component-playground/children.js b/tests/components/component-playground/children.js index 3cdfca4..3febf06 100644 --- a/tests/components/component-playground/children.js +++ b/tests/components/component-playground/children.js @@ -64,7 +64,10 @@ describe('ComponentPlayground component', function() { fixtureContents: { component: 'MyComponent', width: 200, - height: 100 + height: 100, + state: { + paused: true + } } } }); @@ -79,6 +82,13 @@ describe('ComponentPlayground component', function() { expect(childParams.height).to.equal(fixtureContents.height); }); + it('should not send state as prop to preview child', function() { + render(); + + var fixtureContents = component.state.fixtureContents; + expect(childParams.state).to.be.undefined; + }); + it('should use fixture contents as key for preview child', function() { render(); diff --git a/tests/components/component-playground/state.js b/tests/components/component-playground/state.js index ac2ebb0..9c54643 100644 --- a/tests/components/component-playground/state.js +++ b/tests/components/component-playground/state.js @@ -18,14 +18,16 @@ describe('ComponentPlayground component', function() { }; beforeEach(function() { - // Don't render any children - sinon.stub(ComponentTree.loadChild, 'loadChild'); + sinon.stub(ComponentTree, 'injectState'); props = { fixtures: { FirstComponent: { 'blank state': { - myProp: false + myProp: false, + state: { + somethingHappened: true + } } }, SecondComponent: { @@ -41,7 +43,7 @@ describe('ComponentPlayground component', function() { }); afterEach(function() { - ComponentTree.loadChild.loadChild.restore(); + ComponentTree.injectState.restore(); }) describe('state', function() { @@ -84,6 +86,15 @@ describe('ComponentPlayground component', function() { .to.equal(JSON.stringify(fixtureContents, null, 2)); }); + // TODO: Test this on fixture transition as well + it('should inject state to preview child', function() { + render(); + + var args = ComponentTree.injectState.lastCall.args; + expect(args[0]).to.equal(component.refs.preview); + expect(args[1].somethingHappened).to.equal(true); + }); + describe('on fixture transition', function() { beforeEach(function() { render({ From 3b1221c0ee4e9f4540b91a4fd97fea0459ee4a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ovidiu=20Chereche=C8=99?= Date: Thu, 16 Apr 2015 20:21:06 +0300 Subject: [PATCH 3/6] Test state injection after component transition as well #8 --- tests/components/component-playground/state.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/components/component-playground/state.js b/tests/components/component-playground/state.js index 9c54643..b2aeeef 100644 --- a/tests/components/component-playground/state.js +++ b/tests/components/component-playground/state.js @@ -32,7 +32,10 @@ describe('ComponentPlayground component', function() { }, SecondComponent: { 'simple state': { - myProp: true + myProp: true, + state: { + somethingHappened: false + } } } }, @@ -131,6 +134,12 @@ describe('ComponentPlayground component', function() { it('should reset valid user input flag', function() { expect(component.state.isFixtureUserInputValid).to.be.true; }); + + it('should inject new state to preview child', function() { + var args = ComponentTree.injectState.lastCall.args; + expect(args[0]).to.equal(component.refs.preview); + expect(args[1].somethingHappened).to.equal(false); + }); }); }); }); From 1789d4724846f99b33e1793fa8c012d7e9cfeedb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ovidiu=20Chereche=C8=99?= Date: Fri, 17 Apr 2015 19:37:44 +0300 Subject: [PATCH 4/6] Refactor fixtures format to include component class #8 --- src/components/component-playground.jsx | 85 ++++---- .../component-playground/children.js | 60 +++--- .../components/component-playground/events.js | 72 ++++--- .../components/component-playground/render.js | 203 +++++++++--------- .../components/component-playground/state.js | 38 ++-- 5 files changed, 240 insertions(+), 218 deletions(-) diff --git a/src/components/component-playground.jsx b/src/components/component-playground.jsx index ea1abd7..87c37eb 100644 --- a/src/components/component-playground.jsx +++ b/src/components/component-playground.jsx @@ -17,7 +17,7 @@ module.exports = React.createClass({ mixins: [ComponentTree.Mixin], propTypes: { - fixtures: React.PropTypes.object.isRequired, + components: React.PropTypes.object.isRequired, selectedComponent: React.PropTypes.string, selectedFixture: React.PropTypes.string, fixtureEditor: React.PropTypes.bool, @@ -39,35 +39,36 @@ module.exports = React.createClass({ return props.selectedComponent && props.selectedFixture; }, - getSelectedFixtureContents: function(props) { - if (!this.isFixtureSelected(props)) { - return {}; - } - - var fixture = props.fixtures[props.selectedComponent] - [props.selectedFixture]; + getSelectedComponentClass: function(props) { + return props.components[props.selectedComponent].class; + }, - return _.merge({ - component: props.selectedComponent - }, fixture); + getSelectedFixtureContents: function(props) { + return props.components[props.selectedComponent] + .fixtures[props.selectedFixture]; }, getSelectedFixtureUserInput: function(props) { - if (!this.isFixtureSelected(props)) { - return '{}'; - } - return JSON.stringify(this.getSelectedFixtureContents(props), null, 2); }, getFixtureState: function(props, expandedComponents) { - return { - expandedComponents: this.getExpandedComponents(props, - expandedComponents), - fixtureContents: this.getSelectedFixtureContents(props), - fixtureUserInput: this.getSelectedFixtureUserInput(props), + var state = { + expandedComponents: + this.getExpandedComponents(props, expandedComponents), + fixtureContents: {}, + fixtureUserInput: '{}', isFixtureUserInputValid: true }; + + if (this.isFixtureSelected(props)) { + _.assign(state, { + fixtureContents: this.getSelectedFixtureContents(props), + fixtureUserInput: this.getSelectedFixtureUserInput(props) + }); + } + + return state; } }, @@ -84,44 +85,42 @@ module.exports = React.createClass({ children: { preview: function() { - var props = { + var params = { + component: this.constructor.getSelectedComponentClass(this.props), // Child should re-render whenever fixture changes key: JSON.stringify(this.state.fixtureContents) }; - return _.merge(props, _.omit(this.state.fixtureContents, 'state')); + return _.merge(params, _.omit(this.state.fixtureContents, 'state')); } }, render: function() { + var isFixtureSelected = this.constructor.isFixtureSelected(this.props); + var classes = classNames({ 'component-playground': true, 'full-screen': this.props.fullScreen }); - var homeUrlProps = { - fixtureEditor: this.props.fixtureEditor - }; - return (
- {this._renderButtons()} + {isFixtureSelected ? this._renderButtons() : null}

React Component Playground - {_.isEmpty(this.state.fixtureContents) ? this._renderCosmosPlug() - : null} + {!isFixtureSelected ? this._renderCosmosPlug() : null}

{this._renderFixtures()}
- {this._renderContentFrame()} + {isFixtureSelected ? this._renderContentFrame() : null}
); }, @@ -135,7 +134,7 @@ module.exports = React.createClass({ _renderFixtures: function() { return