diff --git a/README.md b/README.md index 86e1152..0176ed0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,48 @@ -# react-component-playground [![Build Status](https://travis-ci.org/skidding/react-component-playground.svg?branch=master)](https://travis-ci.org/skidding/react-component-playground) [![Coverage Status](https://coveralls.io/repos/skidding/react-component-playground/badge.svg?branch=master)](https://coveralls.io/r/skidding/react-component-playground?branch=master) +# React Component Playground +[![Build Status](https://travis-ci.org/skidding/react-component-playground.svg?branch=master)](https://travis-ci.org/skidding/react-component-playground) [![Coverage Status](https://coveralls.io/repos/skidding/react-component-playground/badge.svg?branch=master)](https://coveralls.io/r/skidding/react-component-playground?branch=master) -Isolated loader for React components. +ComponentPlayground provides a minimal frame for loading and testing React +components in isolation. + +Working with ComponentPlayground improves the component design because it +surfaces any implicit dependencies. It also forces you to define sane inputs +for every component, making them more predictable and easier to debug down +the road. + +Features include: + +- Rendering full-screen components or with the navigation pane on the side. +- Injecting predefined state into components via [ComponentTree](https://github.com/skidding/react-component-tree) +- Real-time editing of props and state with instant feedback + +Before diving deeper, you need to understand what a component _fixture_ looks +like. It's the same thing as a snapshot in the ComponentTree utility. Read more +on the project [README](https://github.com/skidding/react-component-tree#componenttreeserialize). + +`components` is by far the most important of the ComponentPlayground [props](https://github.com/skidding/react-component-playground/blob/master/src/components/component-playground.jsx#L19-L26). +This is an example: + +```js +{ + ComponentOne: { + class: require('./components/ComponentOne.jsx'), + fixtures: { + normal: { + fooProp: 'bar' + }, + paused: { + fooProp: 'bar', + state: { + paused: true + } + } + } + }, + ComponentTwo: { + class: require('./components/ComponentTwo.jsx'), + fixtures: { + //... + } + } +}; +``` diff --git a/package.json b/package.json index 33bbaeb..64a80cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-component-playground", - "version": "0.1.1", + "version": "0.2.0", "description": "Isolated loader for React components", "main": "build/bundle.js", "repository": { diff --git a/src/components/component-playground.jsx b/src/components/component-playground.jsx index a5d358b..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, this.state.fixtureContents); + 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