Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 79 additions & 19 deletions fixtures/dom/public/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,30 @@
var renders = 0;
var failed = false;

var needsReactDOM = getBooleanQueryParam('needsReactDOM');
var needsCreateElement = getBooleanQueryParam('needsCreateElement');

function unmountComponent(node) {
// ReactDOM was moved into a separate package in 0.14
if (needsReactDOM) {
ReactDOM.unmountComponentAtNode(node);
} else if (React.unmountComponentAtNode) {
React.unmountComponentAtNode(node);
} else {
// Unmounting for React 0.4 and lower
React.unmountAndReleaseReactRootNode(node);
}
}

function createElement(value) {
// React.createElement replaced function invocation in 0.12
if (needsCreateElement) {
return React.createElement(value);
} else {
return value();
}
}

function getQueryParam(key) {
var pattern = new RegExp(key + '=([^&]+)(&|$)');
var matches = window.location.search.match(pattern);
Expand All @@ -35,20 +59,56 @@
function prerender() {
setStatus('Generating markup');

output.innerHTML = ReactDOMServer.renderToString(
React.createElement(Fixture)
);
return Promise.resolve()
.then(function() {
const element = createElement(Fixture);

// Server rendering moved to a separate package along with ReactDOM
// in 0.14.0
if (needsReactDOM) {
return ReactDOMServer.renderToString(element);
}

// React.renderComponentToString was renamed in 0.12
if (React.renderToString) {
return React.renderToString(element);
}

setStatus('Markup only (No React)');
// React.renderComponentToString became synchronous in React 0.9.0
if (React.renderComponentToString.length === 1) {
return React.renderComponentToString(element);
}

// Finally, React 0.4 and lower emits markup in a callback
return new Promise(function(resolve) {
React.renderComponentToString(element, resolve);
});
})
.then(function(string) {
output.innerHTML = string;
setStatus('Markup only (No React)');
})
.catch(handleError);
}

function render() {
setStatus('Hydrating');

if (ReactDOM.hydrate) {
ReactDOM.hydrate(React.createElement(Fixture), output);
var element = createElement(Fixture);

// ReactDOM was split out into another package in 0.14
if (needsReactDOM) {
// Hydration changed to a separate method in React 16
if (ReactDOM.hydrate) {
ReactDOM.hydrate(element, output);
} else {
ReactDOM.render(element, output);
}
} else if (React.render) {
// React.renderComponent was renamed in 0.12
React.render(element, output);
} else {
ReactDOM.render(React.createElement(Fixture), output);
React.renderComponent(element, output);
}

setStatus(renders > 0 ? 'Re-rendered (' + renders + 'x)' : 'Hydrated');
Expand Down Expand Up @@ -85,17 +145,17 @@
setStatus('Failed');
output.innerHTML = 'Please name your root component "Fixture"';
} else {
prerender();

if (getBooleanQueryParam('hydrate')) {
render();
}
prerender().then(function() {
if (getBooleanQueryParam('hydrate')) {
render();
}
});
}
}

function reloadFixture(code) {
renders = 0;
ReactDOM.unmountComponentAtNode(output);
unmountComponent(output);
injectFixture(code);
}

Expand All @@ -109,12 +169,12 @@

loadScript(getQueryParam('reactPath'))
.then(function() {
return getBooleanQueryParam('needsReactDOM')
? loadScript(getQueryParam('reactDOMPath'))
: null;
})
.then(function() {
return loadScript(getQueryParam('reactDOMServerPath'));
if (needsReactDOM) {
return Promise.all([
loadScript(getQueryParam('reactDOMPath')),
loadScript(getQueryParam('reactDOMServerPath')),
]);
}
})
.then(function() {
if (failed) {
Expand Down
16 changes: 9 additions & 7 deletions fixtures/dom/src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import '../style.css';

const React = window.React;

function App() {
return (
<div>
<Header />
<Fixtures />
</div>
);
class App extends React.Component {
render() {
return (
<div>
<Header />
<Fixtures />
</div>
);
}
}

export default App;
37 changes: 16 additions & 21 deletions fixtures/dom/src/components/Header.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {parse, stringify} from 'query-string';
import getVersionTags from '../tags';
import VersionPicker from './VersionPicker';

const React = window.React;

class Header extends React.Component {
Expand All @@ -9,18 +10,12 @@ class Header extends React.Component {
const version = query.version || 'local';
const production = query.production || false;
const versions = [version];

this.state = {version, versions, production};
}
componentWillMount() {
getVersionTags().then(tags => {
let versions = tags.map(tag => tag.name.slice(1));
versions = [`local`, ...versions];
this.setState({versions});
});
}
handleVersionChange(event) {
handleVersionChange(version) {
const query = parse(window.location.search);
query.version = event.target.value;
query.version = version;
if (query.version === 'local') {
delete query.version;
}
Expand Down Expand Up @@ -48,7 +43,10 @@ class Header extends React.Component {
width="20"
height="20"
/>
<a href="/">DOM Test Fixtures (v{React.version})</a>
<a href="/">
DOM Test Fixtures (v
{React.version})
</a>
</span>

<div className="header-controls">
Expand Down Expand Up @@ -90,17 +88,14 @@ class Header extends React.Component {
<option value="/suspense">Suspense</option>
</select>
</label>
<label htmlFor="react_version">
<label htmlFor="global_version">
<span className="sr-only">Select a version to test</span>
<select
value={this.state.version}
onChange={this.handleVersionChange}>
{this.state.versions.map(version => (
<option key={version} value={version}>
{version}
</option>
))}
</select>
<VersionPicker
id="global_version"
name="global_version"
version={this.state.version}
onChange={this.handleVersionChange}
/>
</label>
</div>
</div>
Expand Down
41 changes: 41 additions & 0 deletions fixtures/dom/src/components/VersionPicker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import getVersionTags from '../tags';

const React = window.React;

class VersionPicker extends React.Component {
constructor(props, context) {
super(props, context);
const version = props.version || 'local';
const versions = [version];
this.state = {versions};
}

componentWillMount() {
getVersionTags().then(tags => {
let versions = tags.map(tag => tag.name.slice(1));
versions = [`local`, ...versions];
this.setState({versions});
});
}

onChange = event => {
this.props.onChange(event.target.value);
};

render() {
const {version, id, name} = this.props;
const {versions} = this.state;

return (
<select id={id} name={name} value={version} onChange={this.onChange}>
{versions.map(version => (
<option key={version} value={version}>
{version}
</option>
))}
</select>
);
}
}

export default VersionPicker;
9 changes: 8 additions & 1 deletion fixtures/dom/src/components/fixtures/hydration/Code.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {findDOMNode} from '../../../find-dom-node';

const React = window.React;

export class CodeEditor extends React.Component {
Expand All @@ -6,6 +8,8 @@ export class CodeEditor extends React.Component {
}

componentDidMount() {
this.textarea = findDOMNode(this);

// Important: CodeMirror incorrectly lays out the editor
// if it executes before CSS has loaded
// https://github.com/graphql/graphiql/issues/33#issuecomment-318188555
Expand Down Expand Up @@ -44,7 +48,6 @@ export class CodeEditor extends React.Component {
render() {
return (
<textarea
ref={ref => (this.textarea = ref)}
defaultValue={this.props.code}
autoComplete="off"
hidden={true}
Expand Down Expand Up @@ -72,6 +75,10 @@ export class CodeError extends React.Component {
if (supportsDetails) {
const [summary, ...body] = error.message.split(/\n+/g);

if (body.length >= 0) {
return <div className={className}>{summary}</div>;
}

return (
<details className={className}>
<summary>{summary}</summary>
Expand Down
6 changes: 6 additions & 0 deletions fixtures/dom/src/components/fixtures/hydration/hydration.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

.hydration-options label {
font-size: 13px;
margin-right: 10px;
}

.hydration-options input[type=checkbox] {
Expand All @@ -30,6 +31,11 @@
vertical-align: middle;
}

.hydration-options select {
margin-left: 10px;
max-width: 100px;
}

.hydration .CodeMirror {
font-size: 13px;
padding-top: 8px;
Expand Down
25 changes: 23 additions & 2 deletions fixtures/dom/src/components/fixtures/hydration/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import './hydration.css';
import VersionPicker from '../../VersionPicker';
import {SAMPLE_CODE} from './data';
import {CodeEditor, CodeError} from './Code';
import {compile} from './code-transformer';
import {reactPaths} from '../../../react-loader';
import qs from 'query-string';

const React = window.React;
// The Hydration fixture can render at a different version than the parent
// app. This allows rendering for versions of React older than the DOM
// test fixtures can support.
const initialVersion = qs.parse(window.location.search).version || 'local';

class Hydration extends React.Component {
state = {
error: null,
code: SAMPLE_CODE,
hydrate: true,
version: initialVersion,
};

ready = false;
Expand Down Expand Up @@ -72,9 +78,14 @@ class Hydration extends React.Component {
});
};

setVersion = version => {
this.setState({version});
};

render() {
const {code, error, hydrate} = this.state;
const src = '/renderer.html?' + qs.stringify({hydrate, ...reactPaths()});
const {code, error, hydrate, version} = this.state;
const src =
'/renderer.html?' + qs.stringify({hydrate, ...reactPaths(version)});

return (
<div className="hydration">
Expand All @@ -89,6 +100,16 @@ class Hydration extends React.Component {
/>
Auto-Hydrate
</label>

<label htmlFor="hydration_version">
Version:
<VersionPicker
id="hydration_version"
name="hyration_version"
version={version}
onChange={this.setVersion}
/>
</label>
</header>

<CodeEditor code={code} onChange={this.setCode} />
Expand Down
Loading