Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to React 16 #263

Merged
merged 16 commits into from
Jan 28, 2018
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
38 changes: 20 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"eslint 'src/app/**/*.ts{,x}' --config src/app/.eslintrc.json --no-eslintrc",
"lint-ts/tslint": "tslint './*.ts' 'src/**/*.ts{,x}' --project .",
"lint-ts": "run-p --aggregate-output 'lint-ts/*'",
"app/webpack/build": "NODE_ENV=production NO_PERF_CHECKS=1 node ./build.js",
"app/webpack/build":
"REACT=1 NODE_ENV=production NO_PERF_CHECKS=1 node ./build.js",
"bundlesize": "bundlesize",
"app/build":
"API_ENDPOINT=${API_ENDPOINT:-https://api.hollowverse.com/graphql} run-s -l app/graphql/build app/webpack/build bundlesize",
Expand All @@ -26,7 +27,7 @@
"nodemon -q --watch src/app --ext graphql,gql --exec 'run-s app/graphql/schema app/graphql/types'",
"app/graphql/build": "run-s -l app/graphql/schema app/graphql/types",
"server/app-server/dev":
"APP_SERVER_PORT=${APP_SERVER_PORT:-3001} HOT=1 NODE_ENV=development node --inspect=9231 -r 'source-map-support/register' -r 'ts-node/register' src/appServer.ts",
"REACT=1 APP_SERVER_PORT=${APP_SERVER_PORT:-3001} HOT=1 NODE_ENV=development node --inspect=9231 -r 'source-map-support/register' -r 'ts-node/register' src/appServer.ts",
"server/app-server/start": "node dist/appServer.js",
"server/main-server/dev":
"PORT=${PORT:-8081} API_ENDPOINT=${API_ENDPOINT:-https://api.hollowverse.com/graphql} nodemon -q --watch src --ignore src/app --ignore src/webpack --ext ts,tsx,json --exec 'node --inspect=0.0.0.0:${SERVER_DEBUG_PORT:-9232} -r 'ts-node/register' src/mainServer.ts'",
Expand All @@ -51,7 +52,7 @@
},
{
"path": "./dist/client/**/vendor.*.js",
"maxSize": "100 kB"
"maxSize": "130 kB"
},
{
"path": "./dist/client/**/*.module.*.css",
Expand Down Expand Up @@ -98,13 +99,13 @@
"ios_saf >= 9.3",
"safari >= 9.1"
],
"resolutions": {
"@types/react": "^16.0.35"
},
"dependencies": {
"@babel/polyfill": "^7.0.0-beta.36",
"@babel/runtime": "^7.0.0-beta.36",
"@hollowverse/common": "hollowverse/common",
"@types/react-redux": "^5.0.14",
"@types/react-router-redux": "5",
"@types/whatwg-fetch": "^0.0.33",
"algoliasearch": "^3.24.8",
"babel-plugin-universal-import": "^1.4.0",
"bluebird": "^3.5.1",
Expand All @@ -129,16 +130,16 @@
"npm-run-all": "^4.0.2",
"preact": "^8.2.7",
"preact-compat": "^3.17.0",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-flip-move": "^2.10.0",
"react-intersection-observer": "^2.1.2",
"react-intersection-observer": "^3.0.2",
"react-redux": "^5.0.6",
"react-redux-epic": "^1.0.0",
"react-router": "^4.0.0",
"react-router-dom": "^4.0.0",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-router-redux": "^5.0.0-alpha.9",
"react-universal-component": "^2.6.0",
"react-universal-component": "^2.8.1",
"redux": "^3.7.2",
"redux-observable": "^0.17.0",
"reselect": "^3.0.1",
Expand Down Expand Up @@ -175,17 +176,19 @@
"@types/node": "^8.5.2",
"@types/node-fetch": "^1.6.7",
"@types/query-string": "^4.3.1",
"@types/react": "^15.0.34",
"@types/react-dom": "^15.5.1",
"@types/react-loadable": "^4.0.4",
"@types/react-router": "^4.0.12",
"@types/react-router-dom": "^4.0.5",
"@types/react": "^16.0.35",
"@types/react-dom": "^16.0.3",
"@types/react-redux": "^5.0.14",
"@types/react-router": "^4.0.21",
"@types/react-router-dom": "^4.2.3",
"@types/react-router-redux": "^5.0.11",
"@types/serialize-javascript": "^1.3.2",
"@types/shelljs": "^0.7.2",
"@types/shrink-ray": "^0.1.0",
"@types/stylelint": "^7.10.0",
"@types/sumo-logger": "^1.0.0",
"@types/webpack": "^3.8.1",
"@types/whatwg-fetch": "^0.0.33",
"apollo-codegen": "^0.17.1",
"autoprefixer": "^7.2.4",
"babel-eslint": "^7.2.3",
Expand All @@ -200,7 +203,6 @@
"babel-plugin-transform-remove-console": "^6.8.4",
"babel-plugin-transform-remove-debugger": "^6.8.4",
"babel-preset-minify": "^0.2.0",
"babel-preset-react-optimize": "^1.0.1",
"bundlesize": "^0.15.3",
"circular-dependency-plugin": "^3.0.0",
"css-loader": "^0.28.4",
Expand Down
4 changes: 2 additions & 2 deletions src/app/client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';
import { Provider } from 'react-redux';
import { ConnectedRouter as Router } from 'react-router-redux';
import domready from 'domready';
import { render } from 'react-dom';
import { hydrate } from 'react-dom';
import createBrowserHistory from 'history/createBrowserHistory';

import { App } from 'components/App/App';
Expand All @@ -18,7 +18,7 @@ const history = createBrowserHistory();
const { store } = createConfiguredStore(history, __INITIAL_STATE__);

const renderApp = (NewApp: typeof App = App) => {
render(
hydrate(
<Provider store={store}>
<Router history={history}>
<NewApp />
Expand Down
18 changes: 16 additions & 2 deletions src/app/components/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,27 @@ import { LoadableAbout } from 'pages/About/LoadableAbout';
import { LoadablePrivacyPolicy } from 'pages/PrivacyPolicy/LoadablePrivacyPolicy';
import { LoadableHome } from 'pages/Home/LoadableHome';

type State = {
hasMounted: boolean;
};

/**
* Main app component
*/
export const App = class extends React.Component {
export const App = class extends React.Component<{}, State> {
state: State = {
hasMounted: false,
};

componentDidMount() {
this.setState({ hasMounted: true });
}

render() {
const { hasMounted } = this.state;

return (
<div className={cc([classes.root, { 'no-js': __IS_SERVER__ }])}>
<div className={cc([classes.root, { 'no-js': hasMounted }])}>
<Route>
{props => <ConnectedNavBar {...props} title="Hollowverse" />}
</Route>
Expand Down
22 changes: 5 additions & 17 deletions src/app/components/Collapsable/Collapsable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ import { SvgIcon } from 'components/SvgIcon/SvgIcon';

import dropdownIcon from 'icons/dropdown.svg';

import { generateRandomString } from 'helpers/generateRandomString';

type Props = {
id?: string;
id: string;
label: React.ReactNode;
isOpen?: boolean;
onChange?(isOpen: boolean): void;
Expand Down Expand Up @@ -46,11 +44,11 @@ export class Collapsable extends React.PureComponent<Props, State> {
handleChange = () => {
const onChange = this.props.onChange || this.defaultOnChange;

onChange(!this.props.isOpen);
onChange(!this.state.isOpen);
};

render() {
const { label, children, id = generateRandomString() } = this.props;
const { id, label, children } = this.props;
const { isOpen } = this.state;

return (
Expand All @@ -72,19 +70,9 @@ export class Collapsable extends React.PureComponent<Props, State> {
id={id}
onChange={this.handleChange}
checked={isOpen}
aria-checked={String(isOpen)}
aria-checked={isOpen}
/>
<label
className={classes.label}
// There seems to be an issue in Preact server side rendering where
// the `htmlFor` attribute is passed as-is because when JS is disabled,
// the generated server markup has `htmlfor` instead of `for` and the
// collapsable component does not toggle when the label is clicked.
//
// Using `{...{ for: id }}` tricks TypeScript into ignoring the
// "unknown" property `for`, since TS expects `htmlFor` instead.
{...{ for: id }}
>
<label className={classes.label} htmlFor={id}>
{label}
<SvgIcon size={8} className={classes.icon} {...dropdownIcon} />
</label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const DispatchOnLifecycleEvent = connect<{}, DispatchProps, OwnProps>(
}

render() {
return this.props.children as any;
return this.props.children || null;
}
},
);
36 changes: 24 additions & 12 deletions src/app/components/EditorialSummary/EditorialSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type Node = {
};

type Props = {
id: string;
author: string;
lastUpdatedOn: string | null;
nodes: Node[];
Expand Down Expand Up @@ -55,7 +56,7 @@ const Block = (props: BlockProps): JSX.Element => {
const { node, nodes, referencesMap, onSourceClick } = props;
const children = findChildren(node, nodes).map(child => {
if (isBlockNode(child)) {
return <Block {...props} node={child} />;
return <Block key={child.id} {...props} node={child} />;
} else if (child.type === 'link' && child.sourceUrl) {
return (
<a
Expand Down Expand Up @@ -129,24 +130,30 @@ export class EditorialSummary extends React.PureComponent<Props, State> {
references = new Map<Node, Source>();

state: State = {
// The server markup should have the sources shown by default
// so that users can click on a reference in the content to go the source.
// even if they have disabled JavaScript in the browser.
//
// When JS is executed, Preact calls render
// and sees that, in the browser build, `shouldShowSources` is now `false`,
// so it hides them.
shouldShowSources: __IS_SERVER__,
shouldShowSources: true,
};

constructor({ nodes }: Props) {
super();
constructor(props: Props, context: any) {
super(props, context);

const { nodes } = props;

// Collect references in nodes to create the Sources section at the end
// of the editorial summary.
nodes.filter(isRootBlock).forEach(findRefs(nodes, this.references));
}

componentDidMount() {
// The server markup should have the sources shown by default (see default `state` above)
// so that users can click on a reference in the content to go the source.
// even if they have disabled JavaScript in the browser.
//
// When JS is executed on the client, React calls `componentDidMount`
// and sees that, `shouldShowSources` is now `false`,
// so it hides them.
this.setState({ shouldShowSources: false });
}

onSourceClick: BlockProps['onSourceClick'] = () => {
this.setState({ shouldShowSources: true });
};
Expand All @@ -163,14 +170,19 @@ export class EditorialSummary extends React.PureComponent<Props, State> {
.filter(isRootBlock)
.map(node => (
<Block
key={node.id}
node={node}
nodes={nodes}
referencesMap={this.references}
onSourceClick={this.onSourceClick}
/>
))}
<hr />
<Collapsable isOpen={shouldShowSources} label={<h3>Sources</h3>}>
<Collapsable
id="sources"
isOpen={shouldShowSources}
label={<h3>Sources</h3>}
>
<small>
<ol className={classes.sourceList}>
{Array.from(this.references.values()).map(ref => {
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/FbComments/FbComments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export class FbComments extends React.PureComponent<Props> {
>
<div
// A unique `key` is required to prevent
// Preact from re-using the DOM Node for
// React from re-using the DOM Node for
// other pages, which confuses Facebook SDK
// and causes the component to be stuck
// at "Loading..."
Expand Down
Loading