From 5deb9826caa709fb403b6f09c8c02aa882178bd1 Mon Sep 17 00:00:00 2001 From: Brandon Dail Date: Tue, 3 Jan 2017 17:28:00 -0600 Subject: [PATCH] Use create-react-app for fixtures application This moves the current fixture architecture to one based around create-react-app. There are a few important things to note: * The react-loader.js script is always loaded before the bundle and will populate React and ReactDOM on the window. It is then read from the window by all components. * The UI for the "React Sandbox" or "React Fixtures App" is also rendered with whatever version of React the user has selected. This means we dont have to deal with iframes or worry about multiple versions of React potentially interferring. But it also means that all components must be written using createClass to be fully backwards compatable. I tested back to 0.13.1 and it works fine. --- fixtures/.gitignore | 15 + fixtures/package.json | 19 + fixtures/public/favicon.ico | Bin 0 -> 24838 bytes fixtures/public/index.html | 32 + fixtures/{ => public}/react-loader.js | 8 +- fixtures/range-inputs/index.html | 82 - fixtures/selects/index.html | 84 - fixtures/src/components/App.js | 19 + fixtures/src/components/Header.js | 68 + fixtures/src/components/RangeInputs.js | 28 + fixtures/src/components/TextInputs.js | 67 + fixtures/src/components/fixtures/index.js | 28 + .../fixtures}/range-inputs/README.md | 0 .../components/fixtures/range-inputs/index.js | 28 + .../components/fixtures}/selects/README.md | 0 .../src/components/fixtures/selects/index.js | 37 + .../fixtures}/text-inputs/README.md | 0 .../components/fixtures/text-inputs/index.js | 67 + .../components/fixtures}/textareas/README.md | 0 .../components/fixtures/textareas/index.js | 33 + fixtures/src/index.js | 8 + fixtures/src/styles/App.css | 94 + fixtures/src/styles/TextInputs.css | 36 + fixtures/test.html | 182 - fixtures/text-inputs/index.html | 120 - fixtures/textareas/index.html | 89 - fixtures/yarn.lock | 5283 +++++++++++++++++ 27 files changed, 5867 insertions(+), 560 deletions(-) create mode 100644 fixtures/.gitignore create mode 100644 fixtures/package.json create mode 100644 fixtures/public/favicon.ico create mode 100644 fixtures/public/index.html rename fixtures/{ => public}/react-loader.js (89%) delete mode 100644 fixtures/range-inputs/index.html delete mode 100644 fixtures/selects/index.html create mode 100644 fixtures/src/components/App.js create mode 100644 fixtures/src/components/Header.js create mode 100644 fixtures/src/components/RangeInputs.js create mode 100644 fixtures/src/components/TextInputs.js create mode 100644 fixtures/src/components/fixtures/index.js rename fixtures/{ => src/components/fixtures}/range-inputs/README.md (100%) create mode 100644 fixtures/src/components/fixtures/range-inputs/index.js rename fixtures/{ => src/components/fixtures}/selects/README.md (100%) create mode 100644 fixtures/src/components/fixtures/selects/index.js rename fixtures/{ => src/components/fixtures}/text-inputs/README.md (100%) create mode 100644 fixtures/src/components/fixtures/text-inputs/index.js rename fixtures/{ => src/components/fixtures}/textareas/README.md (100%) create mode 100644 fixtures/src/components/fixtures/textareas/index.js create mode 100644 fixtures/src/index.js create mode 100644 fixtures/src/styles/App.css create mode 100644 fixtures/src/styles/TextInputs.css delete mode 100644 fixtures/test.html delete mode 100644 fixtures/text-inputs/index.html delete mode 100644 fixtures/textareas/index.html create mode 100644 fixtures/yarn.lock diff --git a/fixtures/.gitignore b/fixtures/.gitignore new file mode 100644 index 0000000000..6c96c5cff1 --- /dev/null +++ b/fixtures/.gitignore @@ -0,0 +1,15 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# dependencies +node_modules + +# testing +coverage + +# production +build + +# misc +.DS_Store +.env +npm-debug.log diff --git a/fixtures/package.json b/fixtures/package.json new file mode 100644 index 0000000000..d8154e046e --- /dev/null +++ b/fixtures/package.json @@ -0,0 +1,19 @@ +{ + "name": "_fixtures", + "version": "0.1.0", + "private": true, + "devDependencies": { + "react-scripts": "0.8.4" + }, + "dependencies": { + "query-string": "^4.2.3", + "react": "^15.4.1", + "react-dom": "^15.4.1" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test --env=jsdom", + "eject": "react-scripts eject" + } +} diff --git a/fixtures/public/favicon.ico b/fixtures/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5c125de5d897c1ff5692a656485b3216123dcd89 GIT binary patch literal 24838 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 + + + + + + + React App + + + +
+ + + diff --git a/fixtures/react-loader.js b/fixtures/public/react-loader.js similarity index 89% rename from fixtures/react-loader.js rename to fixtures/public/react-loader.js index 3f47f3ff21..aadc7e6843 100644 --- a/fixtures/react-loader.js +++ b/fixtures/public/react-loader.js @@ -7,8 +7,8 @@ * (Loads React 15.4.1) */ -var REACT_PATH = '../../build/react.js'; -var DOM_PATH = '../../build/react-dom.js'; +var REACT_PATH = 'react.js'; +var DOM_PATH = 'react-dom.js'; var BABEL_PATH = 'https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.19.0/babel.js'; function parseQuery(qstr) { @@ -25,9 +25,11 @@ function parseQuery(qstr) { return query; } -var query = parseQuery(window.location.search.slice(1)); +var query = parseQuery(window.location.search); var version = query.version || 'local'; +console.log(query) + if (version !== 'local') { REACT_PATH = 'https://unpkg.com/react@' + version + '/dist/react.min.js'; DOM_PATH = 'https://unpkg.com/react-dom@' + version + '/dist/react-dom.min.js'; diff --git a/fixtures/range-inputs/index.html b/fixtures/range-inputs/index.html deleted file mode 100644 index f8bd4c8588..0000000000 --- a/fixtures/range-inputs/index.html +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - Range Inputs - - - - -
Loading...
- - - - - - diff --git a/fixtures/selects/index.html b/fixtures/selects/index.html deleted file mode 100644 index 4f2812ebf7..0000000000 --- a/fixtures/selects/index.html +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - Selects - - - - -
Loading...
- - - - - - diff --git a/fixtures/src/components/App.js b/fixtures/src/components/App.js new file mode 100644 index 0000000000..7d6f04b143 --- /dev/null +++ b/fixtures/src/components/App.js @@ -0,0 +1,19 @@ +const React = window.React; +import Header from './Header'; +import Fixtures from './fixtures'; +import '../styles/App.css'; + +const App = React.createClass({ + render() { + return ( +
+
+
+ +
+
+ ); + }, +}); + +export default App; diff --git a/fixtures/src/components/Header.js b/fixtures/src/components/Header.js new file mode 100644 index 0000000000..c93a515072 --- /dev/null +++ b/fixtures/src/components/Header.js @@ -0,0 +1,68 @@ +import { parse, stringify } from 'query-string'; +const React = window.React; + +const Header = React.createClass({ + getInitialState() { + const query = parse(window.location.search); + const version = query.version || 'local'; + const versions = [version]; + return { version, versions }; + }, + componentWillMount() { + fetch('http://api.github.com/repos/facebook/react/tags') + .then(res => res.json()) + .then(tags => { + let versions = tags.map(tag => tag.name.slice(1)); + versions = ['local', ...versions]; + this.setState({ versions }); + }); + }, + handleVersionChange(event) { + const query = parse(window.location.search); + query.version = event.target.value; + if (query.version === 'local') { + delete query.version; + } + window.location.search = stringify(query); + }, + handleFixtureChange(event) { + window.location.pathname = event.target.value; + }, + render() { + return ( +
+
+ + + React Sandbox (v{React.version}) + + +
+ + +
+
+
+ ); + }, +}); + +export default Header; diff --git a/fixtures/src/components/RangeInputs.js b/fixtures/src/components/RangeInputs.js new file mode 100644 index 0000000000..eea1f996d6 --- /dev/null +++ b/fixtures/src/components/RangeInputs.js @@ -0,0 +1,28 @@ +const React = window.React; + +const RangeInputs = React.createClass({ + getInitialState() { + return { value: 0.5 }; + }, + onChange(event) { + this.setState({ value: event.target.value }); + }, + render() { + return ( +
+
+ Controlled + + Value: {this.state.value} +
+ +
+ Uncontrolled + +
+
+ ); + }, +}); + +export default RangeInputs; diff --git a/fixtures/src/components/TextInputs.js b/fixtures/src/components/TextInputs.js new file mode 100644 index 0000000000..62d16187c9 --- /dev/null +++ b/fixtures/src/components/TextInputs.js @@ -0,0 +1,67 @@ +const React = window.React; +import '../styles/TextInputs.css'; + +const TextInputFixtures = React.createClass({ + getInitialState() { + return { + color: '#ffaaee', + }; + }, + + renderControlled(type) { + let id = `controlled_${type}`; + + let onChange = e => { + let value = e.target.value; + if (type === 'number') { + value = value === '' ? '' : parseFloat(value, 10) || 0 + } + this.setState({ + [type] : value, + }); + }; + + let state = this.state[type] || ''; + + return ( +
+ + +   → {JSON.stringify(state)} +
+ ); + }, + + renderUncontrolled(type) { + let id = `uncontrolled_${type}`; + return ( +
+ + +
+ ); + }, + + render() { + // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input + let types = [ + 'text', 'email', 'number', 'url', 'tel', + 'color', 'date', 'datetime-local', + 'time', 'month', 'week', 'range', 'password', + ]; + return ( +
event.preventDefault()}> +
+ Controlled + {types.map(this.renderControlled)} +
+
+ Uncontrolled + {types.map(this.renderUncontrolled)} +
+
+ ); + }, +}); + +module.exports = TextInputFixtures; diff --git a/fixtures/src/components/fixtures/index.js b/fixtures/src/components/fixtures/index.js new file mode 100644 index 0000000000..763939d28f --- /dev/null +++ b/fixtures/src/components/fixtures/index.js @@ -0,0 +1,28 @@ +const React = window.React; +import RangeInputFixtures from './range-inputs'; +import TextInputFixtures from './text-inputs'; +import SelectFixtures from './selects'; +import TextAreaFixtures from './textareas/'; + +/** + * A simple routing component that renders the appropriate + * fixture based on the location pathname. + */ +const FixturesPage = React.createClass({ + render() { + switch (window.location.pathname) { + case '/text-inputs': + return ; + case '/range-inputs': + return ; + case '/selects': + return ; + case '/textareas': + return ; + default: + return ; + } + }, +}); + +module.exports = FixturesPage; diff --git a/fixtures/range-inputs/README.md b/fixtures/src/components/fixtures/range-inputs/README.md similarity index 100% rename from fixtures/range-inputs/README.md rename to fixtures/src/components/fixtures/range-inputs/README.md diff --git a/fixtures/src/components/fixtures/range-inputs/index.js b/fixtures/src/components/fixtures/range-inputs/index.js new file mode 100644 index 0000000000..eea1f996d6 --- /dev/null +++ b/fixtures/src/components/fixtures/range-inputs/index.js @@ -0,0 +1,28 @@ +const React = window.React; + +const RangeInputs = React.createClass({ + getInitialState() { + return { value: 0.5 }; + }, + onChange(event) { + this.setState({ value: event.target.value }); + }, + render() { + return ( +
+
+ Controlled + + Value: {this.state.value} +
+ +
+ Uncontrolled + +
+
+ ); + }, +}); + +export default RangeInputs; diff --git a/fixtures/selects/README.md b/fixtures/src/components/fixtures/selects/README.md similarity index 100% rename from fixtures/selects/README.md rename to fixtures/src/components/fixtures/selects/README.md diff --git a/fixtures/src/components/fixtures/selects/index.js b/fixtures/src/components/fixtures/selects/index.js new file mode 100644 index 0000000000..3b170b24f5 --- /dev/null +++ b/fixtures/src/components/fixtures/selects/index.js @@ -0,0 +1,37 @@ +const React = window.React; + +const SelectFixture = React.createClass({ + getInitialState() { + return { value: '' }; + }, + onChange(event) { + this.setState({ value: event.target.value }) + }, + render() { + return ( +
+
+ Controlled + + Value: {this.state.value} +
+
+ Uncontrolled + +
+
+ ); + }, +}); + +export default SelectFixture; diff --git a/fixtures/text-inputs/README.md b/fixtures/src/components/fixtures/text-inputs/README.md similarity index 100% rename from fixtures/text-inputs/README.md rename to fixtures/src/components/fixtures/text-inputs/README.md diff --git a/fixtures/src/components/fixtures/text-inputs/index.js b/fixtures/src/components/fixtures/text-inputs/index.js new file mode 100644 index 0000000000..904a8bdc3b --- /dev/null +++ b/fixtures/src/components/fixtures/text-inputs/index.js @@ -0,0 +1,67 @@ +const React = window.React; +import '../../../styles/TextInputs.css'; + +const TextInputFixtures = React.createClass({ + getInitialState() { + return { + color: '#ffaaee', + }; + }, + + renderControlled(type) { + let id = `controlled_${type}`; + + let onChange = e => { + let value = e.target.value; + if (type === 'number') { + value = value === '' ? '' : parseFloat(value, 10) || 0 + } + this.setState({ + [type] : value, + }); + }; + + let state = this.state[type] || ''; + + return ( +
+ + +   → {JSON.stringify(state)} +
+ ); + }, + + renderUncontrolled(type) { + let id = `uncontrolled_${type}`; + return ( +
+ + +
+ ); + }, + + render() { + // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input + let types = [ + 'text', 'email', 'number', 'url', 'tel', + 'color', 'date', 'datetime-local', + 'time', 'month', 'week', 'range', 'password', + ]; + return ( +
event.preventDefault()}> +
+ Controlled + {types.map(this.renderControlled)} +
+
+ Uncontrolled + {types.map(this.renderUncontrolled)} +
+
+ ); + }, +}); + +module.exports = TextInputFixtures; diff --git a/fixtures/textareas/README.md b/fixtures/src/components/fixtures/textareas/README.md similarity index 100% rename from fixtures/textareas/README.md rename to fixtures/src/components/fixtures/textareas/README.md diff --git a/fixtures/src/components/fixtures/textareas/index.js b/fixtures/src/components/fixtures/textareas/index.js new file mode 100644 index 0000000000..f500e679da --- /dev/null +++ b/fixtures/src/components/fixtures/textareas/index.js @@ -0,0 +1,33 @@ +const React = window.React; + +const TextAreaFixtures = React.createClass({ + getInitialState() { + return { value: '' }; + }, + onChange(event) { + this.setState({ value: event.target.value }) + }, + render() { + return ( +
+
+
+ Controlled +