Skip to content

Commit e78de01

Browse files
authored
Merge pull request #873 from outoftime/containers
Use presentational/container pattern in Output component subtree
2 parents d25c1af + 795d0a5 commit e78de01

30 files changed

Lines changed: 410 additions & 391 deletions

.eslintrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,9 @@
688688
"react/no-unknown-property": [
689689
2
690690
],
691+
"react/no-unused-prop-types": [
692+
2
693+
],
691694
"react/prefer-es6-class": [
692695
2,
693696
"always"

.projections.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
{
22
"src/components/*.jsx": {
33
"type": "component",
4-
"template": "import React from 'react';\n\nclass {camelcase|capitalize} extends React.Component {open}\n render() {open}\n {close}\n{close};\n\nexport default {camelcase|capitalize};"
4+
"template": "import React from 'react';\nimport PropTypes from 'prop-types';\n\nexport default function {}() {open}\n{close}\n\n{}.propTypes = {open}\n{close};"
5+
},
6+
"src/containers/*.js": {
7+
"type": "container",
8+
"template": "import {open}connect{close} from 'react-redux';\nimport {open}{}{close} from '../components';\nimport {open}{close} from '../actions';\n\nfunction mapStateToProps(state) {open}\n return {open}\n {close};\n{close}\n\nfunction mapDispatchToProps(dispatch) {open}\n return {open}\n {close};\n{close}\n\nexport default connect(\n mapStateToProps,\n mapDispatchToProps,\n)({});\n"
59
},
610
"src/store.js": {
711
"type": "store"

src/actions/errors.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import {createAction} from 'redux-actions';
22

3+
export const addRuntimeError = createAction(
4+
'ADD_RUNTIME_ERROR',
5+
(language, error) => ({language, error}),
6+
);
7+
38
export const validatedSource = createAction(
49
'VALIDATED_SOURCE',
510
(language, errors) => ({language, errors}),

src/actions/index.js

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,18 @@ import {
2525
notificationTriggered,
2626
userDismissedNotification,
2727
refreshPreview,
28+
popOutProject,
2829
} from './ui';
2930

31+
import {
32+
addRuntimeError,
33+
} from './errors';
34+
3035
import {
3136
userAuthenticated,
3237
userLoggedOut,
3338
} from './user';
3439

35-
function addRuntimeError(error) {
36-
return {
37-
type: 'RUNTIME_ERROR_ADDED',
38-
payload: {error},
39-
};
40-
}
41-
42-
function clearRuntimeErrors() {
43-
return {
44-
type: 'RUNTIME_ERRORS_CLEARED',
45-
};
46-
}
47-
4840
function toggleDashboard() {
4941
return {type: 'DASHBOARD_TOGGLED'};
5042
}
@@ -61,7 +53,6 @@ export {
6153
userAuthenticated,
6254
userLoggedOut,
6355
addRuntimeError,
64-
clearRuntimeErrors,
6556
hideComponent,
6657
unhideComponent,
6758
toggleDashboard,
@@ -77,6 +68,7 @@ export {
7768
exportGist,
7869
gistExportDisplayed,
7970
gistExportNotDisplayed,
71+
popOutProject,
8072
applicationLoaded,
8173
refreshPreview,
8274
};

src/actions/ui.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ export const stopDragColumnDivider = createAction(
2828
'STOP_DRAG_COLUMN_DIVIDER',
2929
);
3030

31+
export const popOutProject = createAction(
32+
'POP_OUT_PROJECT',
33+
);
34+
3135
export const notificationTriggered = createAction(
3236
'NOTIFICATION_TRIGGERED',
3337
(type, severity = 'error', payload = {}) => ({type, severity, payload}),

src/components/Dashboard.jsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import PropTypes from 'prop-types';
33
import {t} from 'i18next';
44
import partial from 'lodash/partial';
5+
import includes from 'lodash/includes';
56
import isNull from 'lodash/isNull';
67
import classnames from 'classnames';
78
import config from '../config';
@@ -144,14 +145,14 @@ class Dashboard extends React.Component {
144145
);
145146
}
146147

147-
_renderPopSvg(variant, validationState) {
148+
_renderPopSvg(variant, ...validationStates) {
148149
return (
149150
<div
150151
className={classnames(
151152
'dashboard__pop',
152153
{
153154
dashboard__pop_visible:
154-
this.props.validationState === validationState,
155+
includes(validationStates, this.props.validationState),
155156
},
156157
)}
157158
>
@@ -165,7 +166,7 @@ class Dashboard extends React.Component {
165166
<div className="dashboard__pop-container">
166167
{this._renderPopSvg('neutral', 'passed')}
167168
{this._renderPopSvg('thinking', 'validating')}
168-
{this._renderPopSvg('horns', 'failed')}
169+
{this._renderPopSvg('horns', 'validation-error', 'runtime-error')}
169170
</div>
170171
);
171172
}
@@ -202,7 +203,8 @@ class Dashboard extends React.Component {
202203
'u__flex-container_column',
203204
{
204205
dashboard_yellow: this.props.validationState === 'validating',
205-
dashboard_red: this.props.validationState === 'failed',
206+
dashboard_red: this.props.validationState === 'validation-error' ||
207+
this.props.validationState === 'runtime-error',
206208
},
207209
);
208210

src/components/EditorsColumn.jsx

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,6 @@ import {getNodeHeights} from '../util/resize';
1010
import EditorContainer from './EditorContainer';
1111
import Editor from './Editor';
1212

13-
function allErrorsFor(language, errors, runtimeErrors) {
14-
if (language === 'javascript') {
15-
return errors.javascript.items.concat(runtimeErrors);
16-
}
17-
return errors[language].items;
18-
}
19-
2013
export default class EditorsColumn extends React.Component {
2114
constructor(props) {
2215
super(props);
@@ -58,7 +51,6 @@ export default class EditorsColumn extends React.Component {
5851
onEditorInput,
5952
onRef,
6053
onRequestedLineFocused,
61-
runtimeErrors,
6254
style,
6355
ui,
6456
} = this.props;
@@ -78,7 +70,7 @@ export default class EditorsColumn extends React.Component {
7870
onRef={partial(this._storeEditorRef, index)}
7971
>
8072
<Editor
81-
errors={allErrorsFor(language, errors, runtimeErrors)}
73+
errors={errors[language].items}
8274
key={language}
8375
language={language}
8476
percentageOfHeight={1 / languages.length}
@@ -121,7 +113,6 @@ EditorsColumn.propTypes = {
121113
currentProject: PropTypes.object.isRequired,
122114
editorsFlex: PropTypes.array.isRequired,
123115
errors: PropTypes.object.isRequired,
124-
runtimeErrors: PropTypes.array.isRequired,
125116
style: PropTypes.object.isRequired,
126117
ui: PropTypes.shape({
127118
editors: PropTypes.object.isRequired,

src/components/ErrorList.jsx

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,47 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3-
import classnames from 'classnames';
4-
import ErrorSublist from './ErrorSublist';
3+
import map from 'lodash/map';
4+
import partial from 'lodash/partial';
5+
import {t} from 'i18next';
6+
import ErrorItem from './ErrorItem';
57

6-
function ErrorList(props) {
7-
return (
8-
<div
9-
className={classnames(
10-
'error-list',
11-
'output__item',
12-
{'error-list_docked': props.docked, output__item_shrink: props.docked},
8+
function ErrorList({errors, onErrorClick, language}) {
9+
if (errors.state === 'passed') {
10+
return false;
11+
}
12+
13+
const errorItems = map(errors.items, error => (
14+
<ErrorItem
15+
{...error}
16+
key={[error.reason, error.row]}
17+
onClick={partial(
18+
onErrorClick,
19+
language,
1320
)}
14-
>
15-
<ErrorSublist
16-
errors={props.html}
17-
language="html"
18-
onErrorClick={props.onErrorClick}
19-
/>
20-
<ErrorSublist
21-
errors={props.css}
22-
language="css"
23-
onErrorClick={props.onErrorClick}
24-
/>
25-
<ErrorSublist
26-
errors={props.javascript}
27-
language="javascript"
28-
onErrorClick={props.onErrorClick}
29-
/>
21+
/>
22+
));
23+
24+
const errorMessage = t(
25+
'errors.notice',
26+
{count: errors.items.length, language},
27+
);
28+
29+
return (
30+
<div>
31+
<h2 className="error-list__header">
32+
{errorMessage}
33+
</h2>
34+
<ul className="error-list__errors">
35+
{errorItems}
36+
</ul>
3037
</div>
3138
);
3239
}
3340

3441
ErrorList.propTypes = {
35-
css: PropTypes.object.isRequired,
36-
docked: PropTypes.bool,
37-
html: PropTypes.object.isRequired,
38-
javascript: PropTypes.object.isRequired,
42+
errors: PropTypes.object.isRequired,
43+
language: PropTypes.oneOf(['html', 'css', 'javascript']).isRequired,
3944
onErrorClick: PropTypes.func.isRequired,
4045
};
4146

42-
ErrorList.defaultProps = {
43-
docked: false,
44-
};
45-
4647
export default ErrorList;

src/components/ErrorReport.jsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import classnames from 'classnames';
4+
import find from 'lodash/find';
5+
import ErrorList from './ErrorList';
6+
7+
function ErrorReport({errors, isValidating, onErrorClick}) {
8+
if (isValidating) {
9+
return <div className="output__delayed-error-overlay" />;
10+
}
11+
12+
const hasErrors = Boolean(find(errors, list => list.items.length));
13+
if (!hasErrors) {
14+
return null;
15+
}
16+
17+
const isDocked = Boolean(find(errors, {state: 'runtime-error'}));
18+
const {html, css, javascript} = errors;
19+
20+
return (
21+
<div
22+
className={classnames(
23+
'error-list',
24+
'output__item',
25+
{'error-list_docked': isDocked, output__item_shrink: isDocked},
26+
)}
27+
>
28+
<ErrorList
29+
errors={html}
30+
language="html"
31+
onErrorClick={onErrorClick}
32+
/>
33+
<ErrorList
34+
errors={css}
35+
language="css"
36+
onErrorClick={onErrorClick}
37+
/>
38+
<ErrorList
39+
errors={javascript}
40+
language="javascript"
41+
onErrorClick={onErrorClick}
42+
/>
43+
</div>
44+
);
45+
}
46+
47+
ErrorReport.propTypes = {
48+
errors: PropTypes.shape({
49+
css: PropTypes.object.isRequired,
50+
html: PropTypes.object.isRequired,
51+
javascript: PropTypes.object.isRequired,
52+
}).isRequired,
53+
isValidating: PropTypes.bool.isRequired,
54+
onErrorClick: PropTypes.func.isRequired,
55+
};
56+
57+
export default ErrorReport;

src/components/ErrorSublist.jsx

Lines changed: 0 additions & 47 deletions
This file was deleted.

0 commit comments

Comments
 (0)