Merge branch '13863-workflow-inputs-processing'
authorMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Tue, 2 Oct 2018 13:09:26 +0000 (15:09 +0200)
committerMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Tue, 2 Oct 2018 13:09:26 +0000 (15:09 +0200)
refs #13863

Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski@contractors.roche.com>

76 files changed:
.env
package.json
src/common/custom-theme.ts
src/components/confirmation-dialog/confirmation-dialog.tsx
src/components/data-explorer/data-explorer.tsx
src/components/icon/icon.tsx
src/components/rich-text-editor-link/rich-text-editor-link.tsx [new file with mode: 0644]
src/components/search-bar/search-bar.tsx
src/components/text-field/text-field.tsx
src/components/workflow-inputs-form/validators.ts [new file with mode: 0644]
src/components/workflow-inputs-form/workflow-input.tsx [new file with mode: 0644]
src/index.tsx
src/models/container-request.ts
src/models/mount-types.ts
src/models/process.ts
src/models/resource.ts
src/models/runtime-constraints.ts
src/models/workflow.ts
src/routes/route-change-handlers.ts
src/routes/routes.ts
src/services/common-service/common-resource-service.ts
src/services/container-request-service/container-request-service.ts
src/services/services.ts
src/services/workflow-service/workflow-service.ts [new file with mode: 0644]
src/store/collection-panel/collection-panel-action.ts
src/store/collection-panel/collection-panel-reducer.ts
src/store/dialog/with-dialog.ts
src/store/navigation/navigation-action.ts
src/store/projects/project-update-actions.ts
src/store/rich-text-editor-dialog/rich-text-editor-dialog-actions.tsx [new file with mode: 0644]
src/store/run-process-panel/run-process-panel-actions.ts [new file with mode: 0644]
src/store/run-process-panel/run-process-panel-reducer.ts [new file with mode: 0644]
src/store/shared-with-me-panel/shared-with-me-middleware-service.ts
src/store/side-panel-tree/side-panel-tree-actions.ts
src/store/side-panel/side-panel-action.ts
src/store/store.ts
src/store/workbench/workbench-actions.ts
src/store/workflow-panel/workflow-middleware-service.ts [new file with mode: 0644]
src/store/workflow-panel/workflow-panel-actions.ts [new file with mode: 0644]
src/validators/is-float.tsx [new file with mode: 0644]
src/validators/is-integer.tsx [new file with mode: 0644]
src/validators/is-number.tsx [new file with mode: 0644]
src/validators/validators.tsx
src/views-components/current-token-dialog/current-token-dialog.tsx
src/views-components/data-explorer/data-explorer.tsx
src/views-components/data-explorer/renderers.tsx
src/views-components/details-panel/project-details.tsx
src/views-components/file-remove-dialog/file-remove-dialog.ts
src/views-components/form-fields/project-form-fields.tsx
src/views-components/main-content-bar/main-content-bar.tsx
src/views-components/rich-text-editor-dialog/rich-text-editor-dialog.tsx [new file with mode: 0644]
src/views-components/side-panel-button/side-panel-button.tsx
src/views-components/side-panel/side-panel.tsx
src/views/login-panel/login-panel.tsx
src/views/process-panel/process-information-card.tsx
src/views/process-panel/process-subprocesses-card.tsx
src/views/run-process-panel/inputs/boolean-input.tsx [new file with mode: 0644]
src/views/run-process-panel/inputs/enum-input.tsx [new file with mode: 0644]
src/views/run-process-panel/inputs/file-input.tsx [new file with mode: 0644]
src/views/run-process-panel/inputs/float-input.tsx [new file with mode: 0644]
src/views/run-process-panel/inputs/generic-input.tsx [new file with mode: 0644]
src/views/run-process-panel/inputs/int-input.tsx [new file with mode: 0644]
src/views/run-process-panel/inputs/string-input.tsx [new file with mode: 0644]
src/views/run-process-panel/run-process-advanced-form.tsx [new file with mode: 0644]
src/views/run-process-panel/run-process-basic-form.tsx [new file with mode: 0644]
src/views/run-process-panel/run-process-first-step.tsx [new file with mode: 0644]
src/views/run-process-panel/run-process-inputs-form.tsx [new file with mode: 0644]
src/views/run-process-panel/run-process-panel-root.tsx [new file with mode: 0644]
src/views/run-process-panel/run-process-panel.tsx [new file with mode: 0644]
src/views/run-process-panel/run-process-second-step.tsx [new file with mode: 0644]
src/views/workbench/workbench.tsx
src/views/workflow-panel/workflow-description-card.tsx [new file with mode: 0644]
src/views/workflow-panel/workflow-panel-view.tsx [new file with mode: 0644]
src/views/workflow-panel/workflow-panel.tsx [new file with mode: 0644]
typings/global.d.ts
yarn.lock

diff --git a/.env b/.env
index ed397c5cc0952badd931be8ccfdd7ec60d95a6d1..bd410081ebecca5b03e42a2695e9e842ee013401 100644 (file)
--- a/.env
+++ b/.env
@@ -3,5 +3,5 @@
 # SPDX-License-Identifier: AGPL-3.0
 
 REACT_APP_ARVADOS_CONFIG_URL=/config.json
-REACT_APP_ARVADOS_API_HOST=qr1hi.arvadosapi.com
+REACT_APP_ARVADOS_API_HOST=c97qk.arvadosapi.com
 HTTPS=true
\ No newline at end of file
index 811e4a07ae5dc53eeb56ed4105e03cbae6f05649..af5b28bd1526d069626e99ce63e2fb80821c70c7 100644 (file)
@@ -3,8 +3,9 @@
   "version": "0.1.0",
   "private": true,
   "dependencies": {
-    "@material-ui/core": "1.5.0",
-    "@material-ui/icons": "2.0.2",
+    "@types/js-yaml": "3.11.2",
+    "@material-ui/core": "3.1.1",
+    "@material-ui/icons": "3.0.1",
     "@types/lodash": "4.14.116",
     "@types/react-copy-to-clipboard": "4.2.6",
     "@types/react-dropzone": "4.2.2",
     "@types/shell-quote": "1.6.0",
     "axios": "0.18.0",
     "classnames": "2.2.6",
-    "lodash": "4.17.10",
-    "react": "16.4.2",
+    "js-yaml": "3.12.0",
+    "lodash": "4.17.11",
+    "react": "16.5.2",
     "react-copy-to-clipboard": "5.0.1",
-    "react-dom": "16.4.2",
-    "react-dropzone": "5.0.1",
+    "react-dom": "16.5.2",
+    "react-dropzone": "5.1.1",
     "react-redux": "5.0.7",
     "react-router": "4.3.1",
     "react-router-dom": "4.3.1",
     "react-router-redux": "5.0.0-alpha.9",
+    "react-rte": "0.16.1",
     "react-scripts-ts": "2.17.0",
     "react-splitter-layout": "3.0.1",
-    "react-transition-group": "2.4.0",
+    "react-transition-group": "2.5.0",
     "redux": "4.0.0",
     "redux-thunk": "2.3.0",
     "shell-quote": "1.6.1",
     "lint": "tslint src/** -t verbose"
   },
   "devDependencies": {
-    "@types/classnames": "^2.2.4",
-    "@types/enzyme": "3.1.13",
+    "@types/classnames": "2.2.6",
+    "@types/enzyme": "3.1.14",
     "@types/enzyme-adapter-react-16": "1.0.3",
-    "@types/jest": "23.3.1",
-    "@types/node": "10.7.1",
+    "@types/jest": "23.3.2",
+    "@types/node": "10.11.2",
     "@types/react": "16.4",
-    "@types/react-dom": "16.0.7",
-    "@types/react-redux": "6.0.6",
-    "@types/react-router": "4.0.29",
-    "@types/react-router-dom": "4.3.0",
-    "@types/react-router-redux": "5.0.15",
+    "@types/react-dom": "16.0.8",
+    "@types/react-redux": "6.0.9",
+    "@types/react-router": "4.0.31",
+    "@types/react-router-dom": "4.3.1",
+    "@types/react-router-redux": "5.0.16",
     "@types/redux-devtools": "3.0.44",
-    "@types/redux-form": "7.4.5",
+    "@types/redux-form": "7.4.8",
     "@types/uuid": "3.4.4",
     "axios-mock-adapter": "1.15.0",
-    "enzyme": "3.4.4",
-    "enzyme-adapter-react-16": "1.2.0",
+    "enzyme": "3.6.0",
+    "enzyme-adapter-react-16": "1.5.0",
     "jest-localstorage-mock": "2.2.0",
     "redux-devtools": "3.4.1",
     "redux-form": "7.4.2",
-    "typescript": "3.0.1"
+    "typescript": "3.1.1"
   },
   "jest": {
     "moduleNameMapper": {
index 8f3497947dc48f87dacdfeefe897383b76c4a2d6..98380b968e8f8b0a70d8f877a50cc48b8b968b67 100644 (file)
@@ -123,6 +123,16 @@ export const themeOptions: ArvadosThemeOptions = {
                     color: arvadosPurple
                 }
             }
+        },
+        MuiStepIcon: {
+            root: {
+                '&$active': {
+                    color: arvadosPurple
+                },
+                '&$completed': {
+                    color: 'inherited'
+                },
+            }
         }
     },
     mixins: {
index 9ca3454a61e7c304ef703f32f84ef734662837f6..13714a7d3577f65a53bceeea3d0adfac0cb4ca80 100644 (file)
@@ -4,7 +4,7 @@
 
 import * as React from "react";
 import { Dialog, DialogTitle, DialogContent, DialogActions, Button, DialogContentText } from "@material-ui/core";
-import { WithDialogProps } from "../../store/dialog/with-dialog";
+import { WithDialogProps } from "~/store/dialog/with-dialog";
 
 export interface ConfirmationDialogDataProps {
     title: string;
index 59f4dbebb47832ff4ec5a827c44eb1a434db61ac..e808351b5257267e340d1498501902c1698b43c9 100644 (file)
@@ -12,7 +12,7 @@ import { DataTableFilterItem } from '../data-table-filters/data-table-filters';
 import { SearchInput } from '../search-input/search-input';
 import { ArvadosTheme } from "~/common/custom-theme";
 
-type CssRules = 'searchBox' | "toolbar";
+type CssRules = 'searchBox' | "toolbar" | "footer" | "root";
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     searchBox: {
@@ -21,6 +21,12 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     toolbar: {
         paddingTop: theme.spacing.unit * 2
     },
+    footer: {
+        overflow: 'auto'
+    },
+    root: {
+        height: '100%'
+    }
 });
 
 interface DataExplorerDataProps<T> {
@@ -66,7 +72,7 @@ export const DataExplorer = withStyles(styles)(
                 items, itemsAvailable, onRowClick, onRowDoubleClick, classes,
                 dataTableDefaultView
             } = this.props;
-            return <Paper>
+            return <Paper className={classes.root}>
                 <Toolbar className={classes.toolbar}>
                     <Grid container justify="space-between" wrap="nowrap" alignItems="center">
                         <div className={classes.searchBox}>
@@ -91,7 +97,7 @@ export const DataExplorer = withStyles(styles)(
                     working={working}
                     defaultView={dataTableDefaultView}
                 />
-                <Toolbar>
+                <Toolbar className={classes.footer}>
                     <Grid container justify="flex-end">
                         <TablePagination
                             count={itemsAvailable}
index 06a56172ffb52077d48e6c05734b5a627dd4b697..5d99aea93b76b12b17b2367ad570f01a4439c6ce 100644 (file)
@@ -20,6 +20,7 @@ import CreateNewFolder from '@material-ui/icons/CreateNewFolder';
 import Delete from '@material-ui/icons/Delete';
 import DeviceHub from '@material-ui/icons/DeviceHub';
 import Edit from '@material-ui/icons/Edit';
+import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
 import Folder from '@material-ui/icons/Folder';
 import GetApp from '@material-ui/icons/GetApp';
 import Help from '@material-ui/icons/Help';
@@ -62,6 +63,7 @@ export const CloudUploadIcon: IconType = (props) => <CloudUpload {...props} />;
 export const DefaultIcon: IconType = (props) => <RateReview {...props} />;
 export const DetailsIcon: IconType = (props) => <Info {...props} />;
 export const DownloadIcon: IconType = (props) => <GetApp {...props} />;
+export const ExpandIcon: IconType = (props) => <ExpandMoreIcon {...props} />;
 export const FavoriteIcon: IconType = (props) => <Star {...props} />;
 export const HelpIcon: IconType = (props) => <Help {...props} />;
 export const HelpOutlineIcon: IconType = (props) => <HelpOutline {...props} />;
diff --git a/src/components/rich-text-editor-link/rich-text-editor-link.tsx b/src/components/rich-text-editor-link/rich-text-editor-link.tsx
new file mode 100644 (file)
index 0000000..2d6a5b4
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { Dispatch } from 'redux';
+import { connect } from 'react-redux';
+import { withStyles, StyleRulesCallback, WithStyles, Typography } from '@material-ui/core';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { openRichTextEditorDialog } from '~/store/rich-text-editor-dialog/rich-text-editor-dialog-actions';
+
+type CssRules = "root";
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    root: {
+        color: theme.palette.primary.main,
+        cursor: 'pointer'
+    }
+});
+
+interface RichTextEditorLinkData {
+    title: string;
+    label: string;
+    content: string;
+}
+
+interface RichTextEditorLinkActions {
+    onClick: (title: string, content: string) => void;
+}
+
+type RichTextEditorLinkProps = RichTextEditorLinkData & RichTextEditorLinkActions & WithStyles<CssRules>;
+
+const mapDispatchToProps = (dispatch: Dispatch) => ({
+    onClick: (title: string, content: string) => dispatch<any>(openRichTextEditorDialog(title, content))
+});
+
+export const RichTextEditorLink = connect(undefined, mapDispatchToProps)(
+    withStyles(styles)(({ classes, title, content, label, onClick }: RichTextEditorLinkProps) =>
+        <Typography component='span' className={classes.root}
+            onClick={() => onClick(title, content) }>
+            {label}
+        </Typography>
+    ));
\ No newline at end of file
index f01b5692f204ca3a72683f71cdecdbaf194a4108..366d70563e3b8060af35b764cd4486d9c75ea697 100644 (file)
@@ -3,10 +3,18 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { IconButton, Paper, StyleRulesCallback, withStyles, WithStyles, Tooltip } from '@material-ui/core';
+import {
+    IconButton,
+    Paper,
+    StyleRulesCallback,
+    withStyles,
+    WithStyles,
+    Tooltip,
+    InputAdornment, Input
+} from '@material-ui/core';
 import SearchIcon from '@material-ui/icons/Search';
 
-type CssRules = 'container' | 'input' | 'button';
+type CssRules = 'container' | 'input';
 
 const styles: StyleRulesCallback<CssRules> = theme => {
     return {
@@ -17,17 +25,7 @@ const styles: StyleRulesCallback<CssRules> = theme => {
         input: {
             border: 'none',
             borderRadius: theme.spacing.unit / 4,
-            boxSizing: 'border-box',
-            padding: theme.spacing.unit,
-            paddingRight: theme.spacing.unit * 4,
-            width: '100%',
-        },
-        button: {
-            position: 'absolute',
-            top: theme.spacing.unit / 2,
-            right: theme.spacing.unit / 2,
-            width: theme.spacing.unit * 3,
-            height: theme.spacing.unit * 3
+            padding: `${theme.spacing.unit / 2}px ${theme.spacing.unit}px`
         }
     };
 };
@@ -61,17 +59,22 @@ export const SearchBar = withStyles(styles)(
             const { classes } = this.props;
             return <Paper className={classes.container}>
                 <form onSubmit={this.handleSubmit}>
-                    <input
+                    <Input
                         className={classes.input}
                         onChange={this.handleChange}
                         placeholder="Search"
                         value={this.state.value}
-                    />
-                    <Tooltip title='Search'>
-                        <IconButton className={classes.button}>
-                            <SearchIcon />
-                        </IconButton>
-                    </Tooltip>
+                        fullWidth={true}
+                        disableUnderline={true}
+                        endAdornment={
+                            <InputAdornment position="end">
+                                <Tooltip title='Search'>
+                                    <IconButton>
+                                        <SearchIcon />
+                                    </IconButton>
+                                </Tooltip>
+                            </InputAdornment>
+                        }/>
                 </form>
             </Paper>;
         }
index b5671dbb08c962b8bae3458738fdf634b57bc382..4d8c012f9edec158ada5549b9b3cfde0dde3b368 100644 (file)
@@ -6,6 +6,7 @@ import * as React from 'react';
 import { WrappedFieldProps } from 'redux-form';
 import { ArvadosTheme } from '~/common/custom-theme';
 import { TextField as MaterialTextField, StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core';
+import RichTextEditor from 'react-rte';
 
 type CssRules = 'textField';
 
@@ -15,7 +16,7 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     },
 });
 
-export const TextField = withStyles(styles)((props: WrappedFieldProps & WithStyles<CssRules> & { label?: string, autoFocus?: boolean }) =>
+export const TextField = withStyles(styles)((props: WrappedFieldProps & WithStyles<CssRules> & { label?: string, autoFocus?: boolean, required?: boolean }) =>
     <MaterialTextField
         helperText={props.meta.touched && props.meta.error}
         className={props.classes.textField}
@@ -25,5 +26,33 @@ export const TextField = withStyles(styles)((props: WrappedFieldProps & WithStyl
         autoComplete='off'
         autoFocus={props.autoFocus}
         fullWidth={true}
+        required={props.required}
         {...props.input}
     />);
+
+
+interface RichEditorTextFieldData {
+    label?: string;
+}
+
+type RichEditorTextFieldProps = RichEditorTextFieldData & WrappedFieldProps & WithStyles<CssRules>;
+
+export const RichEditorTextField = withStyles(styles)(
+    class RichEditorTextField extends React.Component<RichEditorTextFieldProps> {
+        state = {
+            value: RichTextEditor.createValueFromString(this.props.input.value, 'html')
+        };
+
+        onChange = (value: any) => {
+            this.setState({ value });
+            this.props.input.onChange(value.toString('html'));
+        }
+
+        render() {
+            return <RichTextEditor 
+                value={this.state.value}
+                onChange={this.onChange}
+                placeholder={this.props.label} />;
+        }
+    }
+);
\ No newline at end of file
diff --git a/src/components/workflow-inputs-form/validators.ts b/src/components/workflow-inputs-form/validators.ts
new file mode 100644 (file)
index 0000000..9e1e24e
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { CommandInputParameter } from '~/models/workflow';
+import { require } from '~/validators/require';
+import { CWLType } from '../../models/workflow';
+
+
+const alwaysValid = () => undefined;
+
+export const required = ({ type }: CommandInputParameter) => {
+    if (type instanceof Array) {
+        for (const t of type) {
+            if (t === CWLType.NULL) {
+                return alwaysValid;
+            }
+        }
+    }
+    return require;
+};
diff --git a/src/components/workflow-inputs-form/workflow-input.tsx b/src/components/workflow-inputs-form/workflow-input.tsx
new file mode 100644 (file)
index 0000000..3a15d15
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { CommandInputParameter } from '~/models/workflow';
+import { TextField } from '@material-ui/core';
+import { required } from '~/components/workflow-inputs-form/validators';
+
+export interface WorkflowInputProps {
+    input: CommandInputParameter;
+}
+export const WorkflowInput = ({ input }: WorkflowInputProps) =>
+    <TextField
+        label={`${input.label || input.id}${required(input)() ? '*' : ''}`}
+        name={input.id}
+        helperText={input.doc}
+        fullWidth />;
\ No newline at end of file
index 52852847355b79bce73b8307481166176991470e..a4a5e366a26b1c93713e580c4ce717a1c4e4a117 100644 (file)
@@ -38,7 +38,10 @@ import { addRouteChangeHandlers } from './routes/route-change-handlers';
 import { setCurrentTokenDialogApiHost } from '~/store/current-token-dialog/current-token-dialog-actions';
 import { processResourceActionSet } from '~/views-components/context-menu/action-sets/process-resource-action-set';
 import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions';
+import { setUuidPrefix } from '~/store/workflow-panel/workflow-panel-actions';
 import { trashedCollectionActionSet } from '~/views-components/context-menu/action-sets/trashed-collection-action-set';
+import { ContainerRequestState } from '~/models/container-request';
+import { MountKind } from './models/mount-types';
 
 const getBuildNumber = () => "BN-" + (process.env.REACT_APP_BUILD_NUMBER || "dev");
 const getGitCommit = () => "GIT-" + (process.env.REACT_APP_GIT_COMMIT || "latest").substr(0, 7);
@@ -78,6 +81,7 @@ fetchConfig()
         store.subscribe(initListener(history, store, services, config));
         store.dispatch(initAuth());
         store.dispatch(setCurrentTokenDialogApiHost(apiHost));
+        store.dispatch(setUuidPrefix(config.uuidPrefix));
 
         const TokenComponent = (props: any) => <ApiToken authService={services.authService} {...props} />;
         const MainPanelComponent = (props: any) => <MainPanel buildInfo={buildInfo} {...props} />;
@@ -109,8 +113,145 @@ const initListener = (history: History, store: RootStore, services: ServiceRepos
             initWebSocket(config, services.authService, store);
             await store.dispatch(loadWorkbench());
             addRouteChangeHandlers(history, store);
+            // createEnumCollectorWorkflow(services);
         }
     };
 };
 
+const createPrimitivesCollectorWorkflow = ({workflowService}:ServiceRepository) => {
+    workflowService.create({
+            name: 'Primitive values collector',
+            description: 'Workflow for collecting primitive values',
+            definition: "cwlVersion: v1.0\n$graph:\n- class: CommandLineTool\n  requirements:\n  - listing:\n    - entryname: input_collector.log\n      entry: |\n        \"flag\":\n          $(inputs.example_flag)\n        \"string\":\n          $(inputs.example_string)\n        \"int\":\n          $(inputs.example_int)\n        \"long\":\n          $(inputs.example_long)\n        \"float\":\n          $(inputs.example_float)\n        \"double\":\n          $(inputs.example_double)\n    class: InitialWorkDirRequirement\n  inputs:\n  - type: double\n    id: '#input_collector.cwl/example_double'\n  - type: boolean\n    id: '#input_collector.cwl/example_flag'\n  - type: float\n    id: '#input_collector.cwl/example_float'\n  - type: int\n    id: '#input_collector.cwl/example_int'\n  - type: long\n    id: '#input_collector.cwl/example_long'\n  - type: string\n    id: '#input_collector.cwl/example_string'\n  outputs:\n  - type: File\n    outputBinding:\n      glob: '*'\n    id: '#input_collector.cwl/output'\n  baseCommand: [echo]\n  id: '#input_collector.cwl'\n- class: Workflow\n  doc: Workflw for collecting primitive values\n  inputs:\n  - type: double\n    label: Double value\n    doc: This should allow for entering a decimal number (64-bit).\n    id: '#main/example_double'\n    default: 0.3333333333333333\n  - type: boolean\n    label: Boolean Flag\n    doc: This should render as in checkbox.\n    id: '#main/example_flag'\n    default: true\n  - type: float\n    label: Float value\n    doc: This should allow for entering a decimal number (32-bit).\n    id: '#main/example_float'\n    default: 0.15625\n  - type: int\n    label: Integer Number\n    doc: This should allow for entering a number (32-bit signed).\n    id: '#main/example_int'\n    default: 2147483647\n  - type: long\n    label: Long Number\n    doc: This should allow for entering a number (64-bit signed).\n    id: '#main/example_long'\n    default: 9223372036854775807\n  - type: string\n    label: Freetext\n    doc: This should allow for entering an arbitrary char sequence.\n    id: '#main/example_string'\n    default: This is a string\n  outputs:\n  - type: File\n    outputSource: '#main/input_collector/output'\n    id: '#main/log_file'\n  steps:\n  - run: '#input_collector.cwl'\n    in:\n    - source: '#main/example_double'\n      id: '#main/input_collector/example_double'\n    - source: '#main/example_flag'\n      id: '#main/input_collector/example_flag'\n    - source: '#main/example_float'\n      id: '#main/input_collector/example_float'\n    - source: '#main/example_int'\n      id: '#main/input_collector/example_int'\n    - source: '#main/example_long'\n      id: '#main/input_collector/example_long'\n    - source: '#main/example_string'\n      id: '#main/input_collector/example_string'\n    out: ['#main/input_collector/output']\n    id: '#main/input_collector'\n  id: '#main'\n",
+        });
+};
+
+const createEnumCollectorWorkflow = ({workflowService}:ServiceRepository) => {
+    workflowService.create({
+            name: 'Enum values collector',
+            description: 'Workflow for collecting enum values',
+            definition: "cwlVersion: v1.0\n$graph:\n- class: CommandLineTool\n  requirements:\n  - listing:\n    - entryname: input_collector.log\n      entry: |\n        \"enum_type\":\n          $(inputs.enum_type)\n\n    class: InitialWorkDirRequirement\n  inputs:\n  - type:\n      type: enum\n      symbols: ['#input_collector.cwl/enum_type/OTU table', '#input_collector.cwl/enum_type/Pathway\n          table', '#input_collector.cwl/enum_type/Function table', '#input_collector.cwl/enum_type/Ortholog\n          table']\n    id: '#input_collector.cwl/enum_type'\n  outputs:\n  - type: File\n    outputBinding:\n      glob: '*'\n    id: '#input_collector.cwl/output'\n  baseCommand: [echo]\n  id: '#input_collector.cwl'\n- class: Workflow\n  doc: This is the description of the workflow\n  inputs:\n  - type:\n      type: enum\n      symbols: ['#main/enum_type/OTU table', '#main/enum_type/Pathway table', '#main/enum_type/Function\n          table', '#main/enum_type/Ortholog table']\n      name: '#enum_typef4179c7f-45f9-482d-a5db-1abb86698384'\n    label: Enumeration Type\n    doc: This should render as a drop-down menu.\n    id: '#main/enum_type'\n    default: OTU table\n  outputs:\n  - type: File\n    outputSource: '#main/input_collector/output'\n    id: '#main/log_file'\n  steps:\n  - run: '#input_collector.cwl'\n    in:\n    - source: '#main/enum_type'\n      id: '#main/input_collector/enum_type'\n    out: ['#main/input_collector/output']\n    id: '#main/input_collector'\n  id: '#main'\n",
+        });
+};
+
+const createSampleProcess = ({ containerRequestService }: ServiceRepository) => {
+    containerRequestService.create({
+        ownerUuid: 'c97qk-j7d0g-s3ngc1z0748hsmf',
+        name: 'Simple process 7',
+        state: ContainerRequestState.COMMITTED,
+        mounts: {
+            '/var/spool/cwl': {
+                kind: MountKind.COLLECTION,
+                writable: true,
+            },
+            'stdout': {
+                kind: MountKind.MOUNTED_FILE,
+                path: '/var/spool/cwl/cwl.output.json'
+            },
+            '/var/lib/cwl/workflow.json': {
+                kind: MountKind.JSON,
+                content: {
+                    "cwlVersion": "v1.0",
+                    "$graph": [
+                        {
+                            "class": "CommandLineTool",
+                            "requirements": [
+                                {
+                                  "listing": [
+                                    {
+                                      "entryname": "input_collector.log",
+                                      "entry": "$(inputs.single_file.basename)\n"
+                                    }
+                                  ],
+                                  "class": "InitialWorkDirRequirement"
+                                }
+                              ],
+                            "inputs": [
+                                {
+                                    "type": "File",
+                                    "id": "#input_collector.cwl/single_file"
+                                }
+                            ],
+                            "outputs": [
+                                {
+                                    "type": "File",
+                                    "outputBinding": {
+                                        "glob": "*"
+                                    },
+                                    "id": "#input_collector.cwl/output"
+                                }
+                            ],
+                            "baseCommand": [
+                                "echo"
+                            ],
+                            "id": "#input_collector.cwl"
+                        },
+                        {
+                            "class": "Workflow",
+                            "doc": "This is the description of the workflow",
+                            "inputs": [
+                                {
+                                    "type": "File",
+                                    "label": "Single File",
+                                    "doc": "This should allow for single File selection only.",
+                                    "id": "#main/single_file"
+                                }
+                            ],
+                            "outputs": [
+                                {
+                                    "type": "File",
+                                    "outputSource": "#main/input_collector/output",
+                                    "id": "#main/log_file"
+                                }
+                            ],
+                            "steps": [
+                                {
+                                    "run": "#input_collector.cwl",
+                                    "in": [
+                                        {
+                                            "source": "#main/single_file",
+                                            "id": "#main/input_collector/single_file"
+                                        }
+                                    ],
+                                    "out": [
+                                        "#main/input_collector/output"
+                                    ],
+                                    "id": "#main/input_collector"
+                                }
+                            ],
+                            "id": "#main"
+                        }
+                    ]
+                },
+            },
+            '/var/lib/cwl/cwl.input.json': {
+                kind: MountKind.JSON,
+                content: {
+                    "single_file": {
+                        "class": "File",
+                        "location": "keep:233454526794c0a2d56a305baeff3d30+145/1.txt",
+                        "basename": "fileA"
+                      }
+                },
+            }
+        },
+        runtimeConstraints: {
+            API: true,
+            vcpus: 1,
+            ram: 1073741824,
+        },
+        containerImage: 'arvados/jobs:1.1.4.20180618144723',
+        cwd: '/var/spool/cwl',
+        command: [
+            'arvados-cwl-runner',
+            '--local',
+            '--api=containers',
+            "--project-uuid=c97qk-j7d0g-s3ngc1z0748hsmf",
+            '/var/lib/cwl/workflow.json#main',
+            '/var/lib/cwl/cwl.input.json'
+        ],
+        outputPath: '/var/spool/cwl',
+        priority: 1,
+    });
+};
 
index 78891c7bdcd8fc23918ff369052548dd02b725db..e65bed9fd3b729af13a0ee546efebfc250749215 100644 (file)
@@ -22,7 +22,7 @@ export interface ContainerRequestResource extends Resource {
     requestingContainerUuid: string | null;
     containerUuid: string | null;
     containerCountMax: number;
-    mounts: MountType[];
+    mounts: {[path: string]: MountType};
     runtimeConstraints: RuntimeConstraints;
     schedulingParameters: SchedulingParameters;
     containerImage: string;
index ec48c852450df5a239a546c3bc4c11c64d72806f..52b29499bbc6f91e3752ffc137b4e3aadd4ea4c3 100644 (file)
@@ -8,7 +8,7 @@ export enum MountKind {
     TEMPORARY_DIRECTORY = 'tmp',
     KEEP = 'keep',
     MOUNTED_FILE = 'file',
-    JSON = 'JSON'
+    JSON = 'json'
 }
 
 export type MountType =
@@ -16,7 +16,8 @@ export type MountType =
     GitTreeMount |
     TemporaryDirectoryMount |
     KeepMount |
-    JSONMount;
+    JSONMount |
+    FileMount;
 
 export interface CollectionMount {
     kind: MountKind.COLLECTION;
@@ -52,5 +53,10 @@ export interface KeepMount {
 
 export interface JSONMount {
     kind: MountKind.JSON;
-    content: string;
+    content: any;
+}
+
+export interface FileMount {
+    kind: MountKind.MOUNTED_FILE;
+    path: string;
 }
index 1e04cb10f395de5284765fbb50244d2e45d4ea1d..9762d50411d45c40be0bb9efbc389e10e3e2e35e 100644 (file)
@@ -3,5 +3,29 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import { ContainerRequestResource } from "./container-request";
+import { MountType, MountKind } from '~/models/mount-types';
+import { WorkflowResource, parseWorkflowDefinition } from '~/models/workflow';
+import { WorkflowInputsData } from './workflow';
 
 export type ProcessResource = ContainerRequestResource;
+
+export const createWorkflowMounts = (workflow: WorkflowResource, inputs: WorkflowInputsData): { [path: string]: MountType } => {
+    return {
+        '/var/spool/cwl': {
+            kind: MountKind.COLLECTION,
+            writable: true,
+        },
+        'stdout': {
+            kind: MountKind.MOUNTED_FILE,
+            path: '/var/spool/cwl/cwl.output.json',
+        },
+        '/var/lib/cwl/workflow.json': {
+            kind: MountKind.JSON,
+            content: parseWorkflowDefinition(workflow)
+        },
+        '/var/lib/cwl/cwl.input.json': {
+            kind: MountKind.JSON,
+            content: inputs,
+        }
+    };
+};
index 698bcf73188ec21c375a349d3f2605953bbd2074..b8156cf2d12c0c7990e5f8aa8e869626f8bf17bc 100644 (file)
@@ -40,6 +40,7 @@ export enum ResourceObjectType {
     GROUP = 'j7d0g',
     LOG = '57u5n',
     USER = 'tpzed',
+    WORKFLOW = '7fd4e',
 }
 
 export const RESOURCE_UUID_PATTERN = '.{5}-.{5}-.{15}';
@@ -70,6 +71,8 @@ export const extractUuidKind = (uuid: string = '') => {
             return ResourceKind.CONTAINER;
         case ResourceObjectType.LOG:
             return ResourceKind.LOG;
+        case ResourceObjectType.WORKFLOW:
+            return ResourceKind.WORKFLOW;
         default:
             return undefined;
     }
index ba905378108f1a39a6456f0c9f774317a2ba8acb..a780fd3575c71e93efc1c267681a3b896a8d42a4 100644 (file)
@@ -5,6 +5,6 @@
 export interface RuntimeConstraints {
     ram: number;
     vcpus: number;
-    keepCacheRam: number;
+    keepCacheRam?: number;
     API: boolean;
 }
index d644b7909047b0c28db876f3dc1dbe9a96cd0c43..d7d97c4c962401bfc7bd5ff655ebe88fdaf4d92d 100644 (file)
 // SPDX-License-Identifier: AGPL-3.0
 
 import { Resource, ResourceKind } from "./resource";
+import { safeLoad } from 'js-yaml';
 
 export interface WorkflowResource extends Resource {
     kind: ResourceKind.WORKFLOW;
     name: string;
     description: string;
     definition: string;
-}
\ No newline at end of file
+}
+export interface WorkflowResoruceDefinition {
+    cwlVersion: string;
+    $graph: Array<Workflow | CommandLineTool>;
+}
+export interface Workflow {
+    class: 'Workflow';
+    doc?: string;
+    id?: string;
+    inputs: CommandInputParameter[];
+    outputs: any[];
+    steps: any[];
+}
+
+export interface CommandLineTool {
+    class: 'CommandLineTool';
+    id: string;
+    inputs: CommandInputParameter[];
+    outputs: any[];
+}
+
+export type CommandInputParameter =
+    BooleanCommandInputParameter |
+    IntCommandInputParameter |
+    LongCommandInputParameter |
+    FloatCommandInputParameter |
+    DoubleCommandInputParameter |
+    StringCommandInputParameter |
+    FileCommandInputParameter |
+    DirectoryCommandInputParameter |
+    StringArrayCommandInputParameter |
+    FileArrayCommandInputParameter |
+    DirectoryArrayCommandInputParameter |
+    EnumCommandInputParameter;
+
+export enum CWLType {
+    NULL = 'null',
+    BOOLEAN = 'boolean',
+    INT = 'int',
+    LONG = 'long',
+    FLOAT = 'float',
+    DOUBLE = 'double',
+    STRING = 'string',
+    FILE = 'File',
+    DIRECTORY = 'Directory',
+}
+
+export interface CommandInputEnumSchema {
+    symbols: string[];
+    type: 'enum';
+    label?: string;
+    name?: string;
+}
+
+export interface CommandInputArraySchema<ItemType> {
+    items: ItemType;
+    type: 'array';
+    label?: string;
+}
+
+export interface File {
+    class: CWLType.FILE;
+    location?: string;
+    path?: string;
+    basename?: string;
+}
+
+export interface Directory {
+    class: CWLType.DIRECTORY;
+    location?: string;
+    path?: string;
+    basename?: string;
+}
+
+export interface GenericCommandInputParameter<Type, Value> {
+    id: string;
+    label?: string;
+    doc?: string | string[];
+    default?: Value;
+    type?: Type | Array<Type | CWLType.NULL>;
+}
+export type GenericArrayCommandInputParameter<Type, Value> = GenericCommandInputParameter<CommandInputArraySchema<Type>, Value[]>;
+
+export type BooleanCommandInputParameter = GenericCommandInputParameter<CWLType.BOOLEAN, boolean>;
+export type IntCommandInputParameter = GenericCommandInputParameter<CWLType.INT, number>;
+export type LongCommandInputParameter = GenericCommandInputParameter<CWLType.LONG, number>;
+export type FloatCommandInputParameter = GenericCommandInputParameter<CWLType.FLOAT, number>;
+export type DoubleCommandInputParameter = GenericCommandInputParameter<CWLType.DOUBLE, number>;
+export type StringCommandInputParameter = GenericCommandInputParameter<CWLType.STRING, string>;
+export type FileCommandInputParameter = GenericCommandInputParameter<CWLType.FILE, File>;
+export type DirectoryCommandInputParameter = GenericCommandInputParameter<CWLType.DIRECTORY, Directory>;
+export type EnumCommandInputParameter = GenericCommandInputParameter<CommandInputEnumSchema, string>;
+
+export type StringArrayCommandInputParameter = GenericArrayCommandInputParameter<CWLType.STRING, string>;
+export type FileArrayCommandInputParameter = GenericArrayCommandInputParameter<CWLType.FILE, File>;
+export type DirectoryArrayCommandInputParameter = GenericArrayCommandInputParameter<CWLType.DIRECTORY, Directory>;
+
+export type WorkflowInputsData = {
+    [key: string]: boolean | number | string | File | Directory;
+};
+export const parseWorkflowDefinition = (workflow: WorkflowResource): WorkflowResoruceDefinition => {
+    const definition = safeLoad(workflow.definition);
+    return definition;
+};
+
+export const getWorkflowInputs = (workflowDefinition: WorkflowResoruceDefinition) => {
+    const mainWorkflow = workflowDefinition.$graph.find(item => item.class === 'Workflow' && item.id === '#main');
+    return mainWorkflow
+        ? mainWorkflow.inputs
+        : undefined;
+};
+export const getInputLabel = (input: CommandInputParameter) => {
+    return `${input.label || input.id}`;
+};
+
+export const isRequiredInput = ({ type }: CommandInputParameter) => {
+    if (type instanceof Array) {
+        for (const t of type) {
+            if (t === CWLType.NULL) {
+                return false;
+            }
+        }
+    }
+    return true;
+};
+
+export const isPrimitiveOfType = (input: GenericCommandInputParameter<any, any>, type: CWLType) =>
+    input.type instanceof Array
+        ? input.type.indexOf(type) > -1
+        : input.type === type;
+
+export const stringifyInputType = ({ type }: CommandInputParameter) => {
+    if (typeof type === 'string') {
+        return type;
+    } else if (type instanceof Array) {
+        return type.join(' | ');
+    } else if (typeof type === 'object') {
+        if (type.type === 'enum') {
+            return 'enum';
+        } else if (type.type === 'array') {
+            return `${type.items}[]`;
+        } else {
+            return 'unknown';
+        }
+    } else {
+        return 'unknown';
+    }
+};
index 33e0bef753c6f5535a18a02e94b7e8e3012052ab..af3bdab4b092ca61781747eb24a91733fdd20dd0 100644 (file)
@@ -4,11 +4,10 @@
 
 import { History, Location } from 'history';
 import { RootStore } from '~/store/store';
-import { matchProcessRoute, matchProcessLogRoute, matchProjectRoute, matchCollectionRoute, matchFavoritesRoute, matchTrashRoute, matchRootRoute, matchSharedWithMeRoute } from './routes';
+import { matchProcessRoute, matchProcessLogRoute, matchProjectRoute, matchCollectionRoute, matchFavoritesRoute, matchTrashRoute, matchRootRoute, matchSharedWithMeRoute, matchRunProcessRoute, matchWorkflowRoute } from './routes';
 import { loadProject, loadCollection, loadFavorites, loadTrash, loadProcess, loadProcessLog } from '~/store/workbench/workbench-actions';
 import { navigateToRootProject } from '~/store/navigation/navigation-action';
-import { navigateToSharedWithMe } from '../store/navigation/navigation-action';
-import { loadSharedWithMe } from '../store/workbench/workbench-actions';
+import { loadSharedWithMe, loadRunProcess, loadWorkflow } from '../store/workbench/workbench-actions';
 
 export const addRouteChangeHandlers = (history: History, store: RootStore) => {
     const handler = handleLocationChange(store);
@@ -25,6 +24,8 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
     const processMatch = matchProcessRoute(pathname);
     const processLogMatch = matchProcessLogRoute(pathname);
     const sharedWithMeMatch = matchSharedWithMeRoute(pathname);
+    const runProcessMatch = matchRunProcessRoute(pathname);
+    const workflowMatch = matchWorkflowRoute(pathname);
 
     if (projectMatch) {
         store.dispatch(loadProject(projectMatch.params.id));
@@ -42,5 +43,9 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
         store.dispatch(navigateToRootProject);
     } else if (sharedWithMeMatch) {
         store.dispatch(loadSharedWithMe);
+    } else if (runProcessMatch) {
+        store.dispatch(loadRunProcess);
+    } else if (workflowMatch) {
+        store.dispatch(loadWorkflow);
     }
 };
index fb28bd05bee5ff360c17cedd01bf2487d7cf4f22..432cf750cb2f28f24e75d1986fe8064503f3b21e 100644 (file)
@@ -17,6 +17,8 @@ export const Routes = {
     TRASH: '/trash',
     PROCESS_LOGS: `/process-logs/:id(${RESOURCE_UUID_PATTERN})`,
     SHARED_WITH_ME: '/shared-with-me',
+    RUN_PROCESS: '/run-process',
+    WORKFLOWS: '/workflows'
 };
 
 export const getResourceUrl = (uuid: string) => {
@@ -64,3 +66,9 @@ export const matchProcessLogRoute = (route: string) =>
 
 export const matchSharedWithMeRoute = (route: string) =>
     matchPath(route, { path: Routes.SHARED_WITH_ME });
+
+export const matchRunProcessRoute = (route: string) =>
+    matchPath(route, { path: Routes.RUN_PROCESS });
+    
+export const matchWorkflowRoute = (route: string) =>
+    matchPath<ResourceRouteParams>(route, { path: Routes.WORKFLOWS });
index f6810c0453b183a1db0847fe127a8dd607004d41..0c99e7d4be10ce56c4b453e01448ad56254069fe 100644 (file)
@@ -89,7 +89,7 @@ export class CommonResourceService<T extends Resource> {
         this.actions = actions;
     }
 
-    create(data?: Partial<T> | any) {
+    create(data?: Partial<T>) {
         return CommonResourceService.defaultResponse(
             this.serverApi
                 .post<T>(this.resourceType, data && CommonResourceService.mapKeys(_.snakeCase)(data)),
index e035ed5328fbecfef416212daa1183cb5d51b748..2e2ccd1c851bdc031d0f3f0aba8b9badb5873b33 100644 (file)
@@ -2,6 +2,7 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
+import { snakeCase } from 'lodash';
 import { CommonResourceService } from "~/services/common-service/common-resource-service";
 import { AxiosInstance } from "axios";
 import { ContainerRequestResource } from '~/models/container-request';
@@ -11,4 +12,25 @@ export class ContainerRequestService extends CommonResourceService<ContainerRequ
     constructor(serverApi: AxiosInstance, actions: ApiActions) {
         super(serverApi, "container_requests", actions);
     }
+
+    create(data?: Partial<ContainerRequestResource>) {
+        if (data) {
+            const { mounts } = data;
+            if (mounts) {
+                const mappedData = {
+                    ...CommonResourceService.mapKeys(snakeCase)(data),
+                    mounts,
+                };
+                return CommonResourceService
+                    .defaultResponse(
+                        this.serverApi.post<ContainerRequestResource>(this.resourceType, mappedData),
+                        this.actions);
+            }
+        }
+        return CommonResourceService
+            .defaultResponse(
+                this.serverApi
+                    .post<ContainerRequestResource>(this.resourceType, data && CommonResourceService.mapKeys(snakeCase)(data)),
+                this.actions);
+    }
 }
index 9c764b0910a2b2338347951b56f427cfcfe9cfa3..d39a68b91c20df7d4ce896ae62f826d74bd53249 100644 (file)
@@ -21,6 +21,7 @@ import { ContainerRequestService } from './container-request-service/container-r
 import { ContainerService } from './container-service/container-service';
 import { LogService } from './log-service/log-service';
 import { ApiActions } from "~/services/api/api-actions";
+import { WorkflowService } from "~/services/workflow-service/workflow-service";
 
 export type ServiceRepository = ReturnType<typeof createServices>;
 
@@ -39,6 +40,7 @@ export const createServices = (config: Config, actions: ApiActions) => {
     const logService = new LogService(apiClient, actions);
     const projectService = new ProjectService(apiClient, actions);
     const userService = new UserService(apiClient, actions);
+    const workflowService = new WorkflowService(apiClient, actions);
 
     const ancestorsService = new AncestorService(groupsService, userService);
     const authService = new AuthService(apiClient, config.rootUrl, actions);
@@ -64,6 +66,7 @@ export const createServices = (config: Config, actions: ApiActions) => {
         tagService,
         userService,
         webdavClient,
+        workflowService,
     };
 };
 
diff --git a/src/services/workflow-service/workflow-service.ts b/src/services/workflow-service/workflow-service.ts
new file mode 100644 (file)
index 0000000..57ad5fa
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { AxiosInstance } from "axios";
+import { CommonResourceService } from "~/services/common-service/common-resource-service";
+import { WorkflowResource } from '~/models/workflow';
+import { ApiActions } from '~/services/api/api-actions';
+
+export class WorkflowService extends CommonResourceService<WorkflowResource> {
+    constructor(serverApi: AxiosInstance, actions: ApiActions) {
+        super(serverApi, "workflows", actions);
+    }
+}
index 5e991619e77e3a405722ae3b30115b555c3e62af..265b5cf8310f3a752ba16302852241ea90fd879b 100644 (file)
@@ -15,6 +15,7 @@ import { resourcesActions } from "~/store/resources/resources-actions";
 import { unionize, ofType, UnionOf } from '~/common/unionize';
 
 export const collectionPanelActions = unionize({
+    SET_COLLECTION: ofType<CollectionResource>(),
     LOAD_COLLECTION: ofType<{ uuid: string }>(),
     LOAD_COLLECTION_SUCCESS: ofType<{ item: CollectionResource }>()
 });
@@ -34,7 +35,7 @@ export const loadCollectionPanel = (uuid: string) =>
         return collection;
     };
 
-export const createCollectionTag = (data: TagProperty) => 
+export const createCollectionTag = (data: TagProperty) =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
         const item = getState().collectionPanel.item;
         const uuid = item ? item.uuid : '';
index b4951b81826b14426b72e813eda7e93d14c2f8f6..55829cb5d094b8e4b9dbddd313dffa256f78eb6f 100644 (file)
@@ -16,5 +16,6 @@ const initialState = {
 export const collectionPanelReducer = (state: CollectionPanelState = initialState, action: CollectionPanelAction) =>
     collectionPanelActions.match(action, {
         default: () => state,
+        SET_COLLECTION: (item) => ({ ...state, item }),
         LOAD_COLLECTION_SUCCESS: ({ item }) => ({ ...state, item }),
     });
index d53a79d6efe19f5ccde771f05d1590bd77199f0e..41dcd559c6a47033d779a339bd82f5953d05835e 100644 (file)
@@ -18,9 +18,9 @@ export type WithDialogDispatchProps = {
 };
 
 export type WithDialogProps<T> = WithDialogStateProps<T> & WithDialogDispatchProps;
-
 export const withDialog = (id: string) =>
-    <T, P>(component: React.ComponentType<WithDialogProps<T> & P>) =>
+    // TODO: How to make compiler happy with & P instead of & any?
+    <T, P>(component: React.ComponentType<WithDialogProps<T> & any>) =>
         connect(mapStateToProps(id), mapDispatchToProps(id))(component);
 
 export const mapStateToProps = (id: string) => <T>(state: { dialog: DialogState }): WithDialogStateProps<T> => {
@@ -32,4 +32,4 @@ export const mapDispatchToProps = (id: string) => (dispatch: Dispatch): WithDial
     closeDialog: () => {
         dispatch(dialogActions.CLOSE_DIALOG({ id }));
     }
-});
\ No newline at end of file
+});
index 943f38ce98b4eb633b6d369c2bcc2313d85d932f..48bd606e41b146d1f73849d27e3957a84ab9fee8 100644 (file)
@@ -26,6 +26,8 @@ export const navigateTo = (uuid: string) =>
             dispatch<any>(navigateToFavorites);
         } else if (uuid === SidePanelTreeCategory.SHARED_WITH_ME) {
             dispatch(navigateToSharedWithMe);
+        } else if (uuid === SidePanelTreeCategory.WORKFLOWS) {
+            dispatch(navigateToWorkflows);
         } else if (uuid === SidePanelTreeCategory.TRASH) {
             dispatch(navigateToTrash);
         }
@@ -35,6 +37,8 @@ export const navigateToFavorites = push(Routes.FAVORITES);
 
 export const navigateToTrash = push(Routes.TRASH);
 
+export const navigateToWorkflows = push(Routes.WORKFLOWS);
+
 export const navigateToProject = compose(push, getProjectUrl);
 
 export const navigateToCollection = compose(push, getCollectionUrl);
@@ -51,3 +55,5 @@ export const navigateToRootProject = (dispatch: Dispatch, getState: () => RootSt
 };
 
 export const navigateToSharedWithMe = push(Routes.SHARED_WITH_ME);
+
+export const navigateToRunProcess = push(Routes.RUN_PROCESS);
\ No newline at end of file
index afa2e35e8d7c6555055ba053c0bfaeadb6145d84..34ea42f59bd93796bb355da70d3291cc91133906 100644 (file)
@@ -10,6 +10,7 @@ import { getCommonResourceServiceError, CommonResourceServiceError } from "~/ser
 import { ServiceRepository } from "~/services/services";
 import { ProjectResource } from '~/models/project';
 import { ContextMenuResource } from "~/store/context-menu/context-menu-actions";
+import { getResource } from '~/store/resources/resources';
 
 export interface ProjectUpdateFormDialogData {
     uuid: string;
@@ -20,8 +21,9 @@ export interface ProjectUpdateFormDialogData {
 export const PROJECT_UPDATE_FORM_NAME = 'projectUpdateFormName';
 
 export const openProjectUpdateDialog = (resource: ContextMenuResource) =>
-    (dispatch: Dispatch) => {
-        dispatch(initialize(PROJECT_UPDATE_FORM_NAME, resource));
+    (dispatch: Dispatch, getState: () => RootState) => {
+        const project = getResource(resource.uuid)(getState().resources);
+        dispatch(initialize(PROJECT_UPDATE_FORM_NAME, project));
         dispatch(dialogActions.OPEN_DIALOG({ id: PROJECT_UPDATE_FORM_NAME, data: {} }));
     };
 
diff --git a/src/store/rich-text-editor-dialog/rich-text-editor-dialog-actions.tsx b/src/store/rich-text-editor-dialog/rich-text-editor-dialog-actions.tsx
new file mode 100644 (file)
index 0000000..f2a1c4b
--- /dev/null
@@ -0,0 +1,10 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from "redux";
+import { dialogActions } from "~/store/dialog/dialog-actions";
+
+export const RICH_TEXT_EDITOR_DIALOG_NAME = 'richTextEditorDialogName';
+export const openRichTextEditorDialog = (title: string, text: string) => 
+    dialogActions.OPEN_DIALOG({ id: RICH_TEXT_EDITOR_DIALOG_NAME, data: { title, text } });
\ No newline at end of file
diff --git a/src/store/run-process-panel/run-process-panel-actions.ts b/src/store/run-process-panel/run-process-panel-actions.ts
new file mode 100644 (file)
index 0000000..7112f71
--- /dev/null
@@ -0,0 +1,95 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from 'redux';
+import { unionize, ofType, UnionOf } from "~/common/unionize";
+import { ServiceRepository } from "~/services/services";
+import { RootState } from '~/store/store';
+import { WorkflowResource } from '~/models/workflow';
+import { getFormValues } from 'redux-form';
+import { RUN_PROCESS_BASIC_FORM, RunProcessBasicFormData } from '~/views/run-process-panel/run-process-basic-form';
+import { RUN_PROCESS_INPUTS_FORM } from '~/views/run-process-panel/run-process-inputs-form';
+import { WorkflowInputsData } from '~/models/workflow';
+import { createWorkflowMounts } from '~/models/process';
+import { ContainerRequestState } from '~/models/container-request';
+import { navigateToProcess } from '../navigation/navigation-action';
+import { RunProcessAdvancedFormData, RUN_PROCESS_ADVANCED_FORM } from '~/views/run-process-panel/run-process-advanced-form';
+
+export const runProcessPanelActions = unionize({
+    SET_PROCESS_OWNER_UUID: ofType<string>(),
+    SET_CURRENT_STEP: ofType<number>(),
+    SET_WORKFLOWS: ofType<WorkflowResource[]>(),
+    SET_SELECTED_WORKFLOW: ofType<WorkflowResource>(),
+    SEARCH_WORKFLOWS: ofType<string>()
+});
+
+export interface RunProcessSecondStepDataFormProps {
+    name: string;
+    description: string;
+}
+
+export const RUN_PROCESS_SECOND_STEP_FORM_NAME = 'runProcessSecondStepFormName';
+
+export type RunProcessPanelAction = UnionOf<typeof runProcessPanelActions>;
+
+export const loadRunProcessPanel = () =>
+    async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+        try {
+            const response = await services.workflowService.list();
+            dispatch(runProcessPanelActions.SET_WORKFLOWS(response.items));
+        } catch (e) {
+            return;
+        }
+    };
+
+export const setWorkflow = (workflow: WorkflowResource) =>
+    async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+        dispatch(runProcessPanelActions.SET_SELECTED_WORKFLOW(workflow));
+    };
+
+export const goToStep = (step: number) => runProcessPanelActions.SET_CURRENT_STEP(step);
+
+export const runProcess = async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+    const state = getState();
+    const basicForm = getFormValues(RUN_PROCESS_BASIC_FORM)(state) as RunProcessBasicFormData;
+    const inputsForm = getFormValues(RUN_PROCESS_INPUTS_FORM)(state) as WorkflowInputsData;
+    const advancedForm = getFormValues(RUN_PROCESS_ADVANCED_FORM)(state) as RunProcessAdvancedFormData;
+    const { processOwnerUuid, selectedWorkflow } = state.runProcessPanel;
+    if (selectedWorkflow) {
+        const newProcessData = {
+            ownerUuid: processOwnerUuid,
+            name: basicForm.name,
+            description: basicForm.description,
+            state: ContainerRequestState.COMMITTED,
+            mounts: createWorkflowMounts(selectedWorkflow, normalizeInputKeys(inputsForm)),
+            runtimeConstraints: {
+                API: true,
+                vcpus: 1,
+                ram: 1073741824,
+            },
+            containerImage: 'arvados/jobs:1.1.4.20180618144723',
+            cwd: '/var/spool/cwl',
+            command: [
+                'arvados-cwl-runner',
+                '--local',
+                '--api=containers',
+                `--project-uuid=${processOwnerUuid}`,
+                '/var/lib/cwl/workflow.json#main',
+                '/var/lib/cwl/cwl.input.json'
+            ],
+            outputPath: '/var/spool/cwl',
+            priority: 1,
+            outputName: advancedForm && advancedForm.output ? advancedForm.output : undefined,
+        };
+        const newProcess = await services.containerRequestService.create(newProcessData);
+        dispatch(navigateToProcess(newProcess.uuid));
+    }
+};
+
+const normalizeInputKeys = (inputs: WorkflowInputsData): WorkflowInputsData =>
+    Object.keys(inputs).reduce((normalizedInputs, key) => ({
+        ...normalizedInputs,
+        [key.split('/').slice(1).join('/')]: inputs[key],
+    }), {});
+export const searchWorkflows = (term: string) => runProcessPanelActions.SEARCH_WORKFLOWS(term);
diff --git a/src/store/run-process-panel/run-process-panel-reducer.ts b/src/store/run-process-panel/run-process-panel-reducer.ts
new file mode 100644 (file)
index 0000000..0ad06be
--- /dev/null
@@ -0,0 +1,38 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { RunProcessPanelAction, runProcessPanelActions } from '~/store/run-process-panel/run-process-panel-actions';
+import { WorkflowResource, CommandInputParameter, getWorkflowInputs, parseWorkflowDefinition } from '~/models/workflow';
+
+interface RunProcessPanel {
+    processOwnerUuid: string;
+    currentStep: number;
+    workflows: WorkflowResource[];
+    searchWorkflows: WorkflowResource[];
+    selectedWorkflow: WorkflowResource | undefined;
+    inputs: CommandInputParameter[];
+}
+
+const initialState: RunProcessPanel = {
+    processOwnerUuid: '',
+    currentStep: 0,
+    workflows: [],
+    selectedWorkflow: undefined,
+    inputs: [],
+    searchWorkflows: [],
+};
+
+export const runProcessPanelReducer = (state = initialState, action: RunProcessPanelAction): RunProcessPanel =>
+    runProcessPanelActions.match(action, {
+        SET_PROCESS_OWNER_UUID: processOwnerUuid => ({ ...state, processOwnerUuid }),
+        SET_CURRENT_STEP: currentStep => ({ ...state, currentStep }),
+        SET_SELECTED_WORKFLOW: selectedWorkflow => ({
+            ...state,
+            selectedWorkflow,
+            inputs: getWorkflowInputs(parseWorkflowDefinition(selectedWorkflow)) || [],
+        }),
+        SET_WORKFLOWS: workflows => ({ ...state, workflows, searchWorkflows: workflows }), 
+        SEARCH_WORKFLOWS: term => ({ ...state, searchWorkflows: state.workflows.filter(workflow => workflow.name.includes(term)) }),
+        default: () => state
+    });
\ No newline at end of file
index 1ebb13ecb05053cfb06336f9a5c5bf9ca65dd687..c26a7a5aa7eb7cb846393bd5a67af79f2b016eb1 100644 (file)
@@ -37,7 +37,6 @@ export class SharedWithMeMiddlewareService extends DataExplorerMiddlewareService
         } catch (e) {
             api.dispatch(couldNotFetchSharedItems());
         }
-
     }
 }
 
index 3fd2d68af6930946c933657a8d59aec8dfdcddc6..22a83dda8c9f71b130e5b8251b3e8d8c321bb714 100644 (file)
@@ -13,7 +13,6 @@ import { getTreePicker, TreePicker } from '../tree-picker/tree-picker';
 import { TreeItemStatus } from "~/components/tree/tree";
 import { getNodeAncestors, getNodeValue, getNodeAncestorsIds, getNode } from '~/models/tree';
 import { ProjectResource } from '~/models/project';
-import { progressIndicatorActions } from '../progress-indicator/progress-indicator-actions';
 import { OrderBuilder } from '../../services/api/order-builder';
 
 export enum SidePanelTreeCategory {
index 2a5fdd0c2d17c1aae6c3455034fd303ddb523a36..fd08ee1319d5b1550bd278591df4997d7d7f247a 100644 (file)
@@ -4,7 +4,7 @@
 
 import { Dispatch } from 'redux';
 import { isSidePanelTreeCategory, SidePanelTreeCategory } from '~/store/side-panel-tree/side-panel-tree-actions';
-import { navigateToFavorites, navigateTo, navigateToTrash, navigateToSharedWithMe } from '../navigation/navigation-action';
+import { navigateToFavorites, navigateTo, navigateToTrash, navigateToSharedWithMe, navigateToWorkflows } from '../navigation/navigation-action';
 import { snackbarActions } from '~/store/snackbar/snackbar-actions';
 
 export const navigateFromSidePanel = (id: string) =>
@@ -24,6 +24,8 @@ const getSidePanelTreeCategoryAction = (id: string) => {
             return navigateToTrash;
         case SidePanelTreeCategory.SHARED_WITH_ME:
             return navigateToSharedWithMe;
+        case SidePanelTreeCategory.WORKFLOWS:
+            return navigateToWorkflows;
         default:
             return sidePanelTreeCategoryNotAvailable(id);
     }
index 012b747425b72e714472a5b2a0cfe89d03dc2546..21e5029a0338bd1e56512e4c0d977db3a72ec552 100644 (file)
@@ -35,6 +35,9 @@ import { processPanelReducer } from '~/store/process-panel/process-panel-reducer
 import { SHARED_WITH_ME_PANEL_ID } from '~/store/shared-with-me-panel/shared-with-me-panel-actions';
 import { SharedWithMeMiddlewareService } from './shared-with-me-panel/shared-with-me-middleware-service';
 import { progressIndicatorReducer } from './progress-indicator/progress-indicator-reducer';
+import { runProcessPanelReducer } from '~/store/run-process-panel/run-process-panel-reducer';
+import { WorkflowMiddlewareService } from './workflow-panel/workflow-middleware-service';
+import { WORKFLOW_PANEL_ID } from './workflow-panel/workflow-panel-actions';
 
 const composeEnhancers =
     (process.env.NODE_ENV === 'development' &&
@@ -60,6 +63,9 @@ export function configureStore(history: History, services: ServiceRepository): R
     const sharedWithMePanelMiddleware = dataExplorerMiddleware(
         new SharedWithMeMiddlewareService(services, SHARED_WITH_ME_PANEL_ID)
     );
+    const workflowPanelMiddleware = dataExplorerMiddleware(
+        new WorkflowMiddlewareService(services, WORKFLOW_PANEL_ID)
+    );
 
     const middlewares: Middleware[] = [
         routerMiddleware(history),
@@ -68,6 +74,7 @@ export function configureStore(history: History, services: ServiceRepository): R
         favoritePanelMiddleware,
         trashPanelMiddleware,
         sharedWithMePanelMiddleware,
+        workflowPanelMiddleware
     ];
     const enhancer = composeEnhancers(applyMiddleware(...middlewares));
     return createStore(rootReducer, enhancer);
@@ -91,5 +98,6 @@ const createRootReducer = (services: ServiceRepository) => combineReducers({
     treePicker: treePickerReducer,
     fileUploader: fileUploaderReducer,
     processPanel: processPanelReducer,
-    progressIndicator: progressIndicatorReducer
+    progressIndicator: progressIndicatorReducer,
+    runProcessPanel: runProcessPanelReducer
 });
index 94b4b4f5bb7602f811b257edc08ab48289522618..56e3401d5ae0e56af72463992689e9bd17323bd5 100644 (file)
@@ -38,12 +38,18 @@ import { loadProcessPanel } from '~/store/process-panel/process-panel-actions';
 import { sharedWithMePanelActions } from '~/store/shared-with-me-panel/shared-with-me-panel-actions';
 import { loadSharedWithMePanel } from '../shared-with-me-panel/shared-with-me-panel-actions';
 import { CopyFormDialogData } from '~/store/copy-dialog/copy-dialog';
+import { loadWorkflowPanel, workflowPanelActions } from '~/store/workflow-panel/workflow-panel-actions';
+import { workflowPanelColumns } from '~/views/workflow-panel/workflow-panel-view';
 import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions';
 import { getProgressIndicator } from '../progress-indicator/progress-indicator-reducer';
 import { ResourceKind, extractUuidKind } from '~/models/resource';
 import { FilterBuilder } from '~/services/api/filter-builder';
 import { GroupContentsResource } from '~/services/groups-service/groups-service';
 import { unionize, ofType, UnionOf, MatchCases } from '~/common/unionize';
+import { loadRunProcessPanel } from '~/store/run-process-panel/run-process-panel-actions';
+import { loadCollectionFiles } from '~/store/collection-panel/collection-panel-files/collection-panel-files-actions';
+import { collectionPanelActions } from "~/store/collection-panel/collection-panel-action";
+import { CollectionResource } from "~/models/collection";
 
 export const WORKBENCH_LOADING_SCREEN = 'workbenchLoadingScreen';
 
@@ -76,6 +82,7 @@ export const loadWorkbench = () =>
                 dispatch(favoritePanelActions.SET_COLUMNS({ columns: favoritePanelColumns }));
                 dispatch(trashPanelActions.SET_COLUMNS({ columns: trashPanelColumns }));
                 dispatch(sharedWithMePanelActions.SET_COLUMNS({ columns: projectPanelColumns }));
+                dispatch(workflowPanelActions.SET_COLUMNS({ columns: workflowPanelColumns }));
                 dispatch<any>(initSidePanelTree());
                 if (router.location) {
                     const match = matchRootRoute(router.location.pathname);
@@ -191,19 +198,25 @@ export const loadCollection = (uuid: string) =>
                 const match = await loadGroupContentsResource({ uuid, userUuid, services });
                 match({
                     OWNED: async collection => {
+                        dispatch(collectionPanelActions.SET_COLLECTION(collection as CollectionResource));
                         dispatch(updateResources([collection]));
                         await dispatch(activateSidePanelTreeItem(collection.ownerUuid));
                         dispatch(setSidePanelBreadcrumbs(collection.ownerUuid));
+                        dispatch(loadCollectionFiles(collection.uuid));
                     },
                     SHARED: collection => {
+                        dispatch(collectionPanelActions.SET_COLLECTION(collection as CollectionResource));
                         dispatch(updateResources([collection]));
                         dispatch<any>(setSharedWithMeBreadcrumbs(collection.ownerUuid));
                         dispatch(activateSidePanelTreeItem(SidePanelTreeCategory.SHARED_WITH_ME));
+                        dispatch(loadCollectionFiles(collection.uuid));
                     },
                     TRASHED: collection => {
+                        dispatch(collectionPanelActions.SET_COLLECTION(collection as CollectionResource));
                         dispatch(updateResources([collection]));
                         dispatch(setTrashBreadcrumbs(''));
                         dispatch(activateSidePanelTreeItem(SidePanelTreeCategory.TRASH));
+                        dispatch(loadCollectionFiles(collection.uuid));
                     },
 
                 });
@@ -347,6 +360,18 @@ export const loadSharedWithMe = handleFirstTimeLoad(async (dispatch: Dispatch) =
     await dispatch<any>(setSidePanelBreadcrumbs(SidePanelTreeCategory.SHARED_WITH_ME));
 });
 
+export const loadRunProcess = handleFirstTimeLoad(
+    async (dispatch: Dispatch) => {
+        await dispatch<any>(loadRunProcessPanel());
+    }
+);
+
+export const loadWorkflow = handleFirstTimeLoad(async (dispatch: Dispatch<any>) => {
+    dispatch(activateSidePanelTreeItem(SidePanelTreeCategory.WORKFLOWS));
+    await dispatch(loadWorkflowPanel());
+    dispatch(setSidePanelBreadcrumbs(SidePanelTreeCategory.WORKFLOWS));
+});
+
 const finishLoadingProject = (project: GroupContentsResource | string) =>
     async (dispatch: Dispatch<any>) => {
         const uuid = typeof project === 'string' ? project : project.uuid;
diff --git a/src/store/workflow-panel/workflow-middleware-service.ts b/src/store/workflow-panel/workflow-middleware-service.ts
new file mode 100644 (file)
index 0000000..1002979
--- /dev/null
@@ -0,0 +1,77 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { ServiceRepository } from '~/services/services';
+import { MiddlewareAPI, Dispatch } from 'redux';
+import { DataExplorerMiddlewareService, dataExplorerToListParams, listResultsToDataExplorerItemsMeta } from '~/store/data-explorer/data-explorer-middleware-service';
+import { RootState } from '~/store/store';
+import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
+import { DataExplorer, getDataExplorer } from '~/store/data-explorer/data-explorer-reducer';
+import { updateResources } from '~/store/resources/resources-actions';
+import { FilterBuilder } from '~/services/api/filter-builder';
+import { SortDirection } from '~/components/data-table/data-column';
+import { WorkflowPanelColumnNames } from '~/views/workflow-panel/workflow-panel-view';
+import { OrderDirection, OrderBuilder } from '~/services/api/order-builder';
+import { WorkflowResource } from '~/models/workflow';
+import { ListResults } from '~/services/common-service/common-resource-service';
+import { workflowPanelActions } from './workflow-panel-actions';
+
+export class WorkflowMiddlewareService extends DataExplorerMiddlewareService {
+    constructor(private services: ServiceRepository, id: string) {
+        super(id);
+    }
+
+    async requestItems(api: MiddlewareAPI<Dispatch, RootState>) {
+        const state = api.getState();
+        const dataExplorer = getDataExplorer(state.dataExplorer, this.getId());
+        try {
+            const response = await this.services.workflowService.list(getParams(dataExplorer));
+            api.dispatch(updateResources(response.items));
+            api.dispatch(setItems(response));
+        } catch {
+            api.dispatch(couldNotFetchWorkflows());
+        }
+    }
+}
+
+export const getParams = (dataExplorer: DataExplorer) => ({
+    ...dataExplorerToListParams(dataExplorer),
+    order: getOrder(dataExplorer),
+    filters: getFilters(dataExplorer)
+});
+
+export const getFilters = (dataExplorer: DataExplorer) => {
+    const filters = new FilterBuilder()
+        .addILike("name", dataExplorer.searchValue)
+        .getFilters();
+    return filters;
+};
+
+export const getOrder = (dataExplorer: DataExplorer) => {
+    const sortColumn = dataExplorer.columns.find(c => c.sortDirection !== SortDirection.NONE);
+    const order = new OrderBuilder<WorkflowResource>();
+    if (sortColumn) {
+        const sortDirection = sortColumn && sortColumn.sortDirection === SortDirection.ASC
+            ? OrderDirection.ASC
+            : OrderDirection.DESC;
+        const columnName = sortColumn && sortColumn.name === WorkflowPanelColumnNames.NAME ? "name" : "modifiedAt";
+        return order
+            .addOrder(sortDirection, columnName)
+            .getOrder();
+    } else {
+        return order.getOrder();
+    }
+};
+
+export const setItems = (listResults: ListResults<WorkflowResource>) =>
+    workflowPanelActions.SET_ITEMS({
+        ...listResultsToDataExplorerItemsMeta(listResults),
+        items: listResults.items.map(resource => resource.uuid),
+    });
+
+const couldNotFetchWorkflows = () =>
+    snackbarActions.OPEN_SNACKBAR({
+        message: 'Could not fetch workflows.',
+        kind: SnackbarKind.ERROR
+    });
\ No newline at end of file
diff --git a/src/store/workflow-panel/workflow-panel-actions.ts b/src/store/workflow-panel/workflow-panel-actions.ts
new file mode 100644 (file)
index 0000000..ca72e5a
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from 'redux';
+import { RootState } from '~/store/store';
+import { ServiceRepository } from '~/services/services';
+import { bindDataExplorerActions } from '~/store/data-explorer/data-explorer-action';
+import { propertiesActions } from '~/store/properties/properties-actions';
+import { getResource } from '../resources/resources';
+import { getProperty } from '~/store/properties/properties';
+import { WorkflowResource } from '../../models/workflow';
+
+export const WORKFLOW_PANEL_ID = "workflowPanel";
+const UUID_PREFIX_PROPERTY_NAME = 'uuidPrefix';
+const WORKFLOW_PANEL_DETAILS_UUID = 'workflowPanelDetailsUuid';
+export const workflowPanelActions = bindDataExplorerActions(WORKFLOW_PANEL_ID);
+
+export const loadWorkflowPanel = () =>
+    (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        dispatch(workflowPanelActions.REQUEST_ITEMS());
+    };
+
+export const setUuidPrefix = (uuidPrefix: string) =>
+    propertiesActions.SET_PROPERTY({ key: UUID_PREFIX_PROPERTY_NAME, value: uuidPrefix });
+
+export const getUuidPrefix = (state: RootState) => {
+    return state.properties.uuidPrefix;
+};
+
+export const showWorkflowDetails = (uuid: string) =>
+    propertiesActions.SET_PROPERTY({ key: WORKFLOW_PANEL_DETAILS_UUID, value: uuid });
+
+export const getWorkflowDetails = (state: RootState) => {
+    const uuid = getProperty<string>(WORKFLOW_PANEL_DETAILS_UUID)(state.properties);
+    return uuid ? getResource<WorkflowResource>(uuid)(state.resources) : undefined;
+};
diff --git a/src/validators/is-float.tsx b/src/validators/is-float.tsx
new file mode 100644 (file)
index 0000000..9bde5f9
--- /dev/null
@@ -0,0 +1,11 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { isInteger, isNumber } from 'lodash';
+
+const ERROR_MESSAGE = 'This field must be a float';
+
+export const isFloat = (value: any) => {
+    return isNumber(value) ? undefined : ERROR_MESSAGE;
+};
diff --git a/src/validators/is-integer.tsx b/src/validators/is-integer.tsx
new file mode 100644 (file)
index 0000000..fbfe8fb
--- /dev/null
@@ -0,0 +1,11 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { isInteger as isInt } from 'lodash';
+
+const ERROR_MESSAGE = 'This field must be an integer';
+
+export const isInteger = (value: any) => {
+    return isInt(value) ? undefined : ERROR_MESSAGE;
+};
diff --git a/src/validators/is-number.tsx b/src/validators/is-number.tsx
new file mode 100644 (file)
index 0000000..152c647
--- /dev/null
@@ -0,0 +1,10 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { isNumber as isNum } from 'lodash';
+const ERROR_MESSAGE = 'This field must be a number';
+
+export const isNumber = (value: any) => {
+    return !isNaN(value) && isNum(value) ? undefined : ERROR_MESSAGE;
+};
index 755cd7f7b4c7be4b7a1650653c3b7783406c1179..edc472657eb3178036fb0d8b3917dd0600e39ce5 100644 (file)
@@ -9,7 +9,6 @@ export const TAG_KEY_VALIDATION = [require, maxLength(255)];
 export const TAG_VALUE_VALIDATION = [require, maxLength(255)];
 
 export const PROJECT_NAME_VALIDATION = [require, maxLength(255)];
-export const PROJECT_DESCRIPTION_VALIDATION = [maxLength(255)];
 
 export const COLLECTION_NAME_VALIDATION = [require, maxLength(255)];
 export const COLLECTION_DESCRIPTION_VALIDATION = [maxLength(255)];
index ba6c3258f428a3fe882fa47463b623e798f25451..503206a6f5be37d0c166d5b0ceaa3df7e14ec015 100644 (file)
@@ -7,10 +7,8 @@ import { Dialog, DialogActions, DialogTitle, DialogContent, WithStyles, withStyl
 import { ArvadosTheme } from '~/common/custom-theme';
 import { withDialog } from '~/store/dialog/with-dialog';
 import { WithDialogProps } from '~/store/dialog/with-dialog';
-import { compose } from 'redux';
 import { connect } from 'react-redux';
 import { CurrentTokenDialogData, getCurrentTokenDialogData } from '~/store/current-token-dialog/current-token-dialog-actions';
-import { RootState } from '~/store/store';
 import { DefaultCodeSnippet } from '~/components/default-code-snippet/default-code-snippet';
 
 type CssRules = 'link' | 'paper' | 'button';
@@ -35,46 +33,46 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
 
 type CurrentTokenProps = CurrentTokenDialogData & WithDialogProps<{}> & WithStyles<CssRules>;
 
-export const CurrentTokenDialog = compose(
-    withStyles(styles),
-    connect(getCurrentTokenDialogData),
-    withDialog('currentTokenDialog')
-)(class extends React.Component<CurrentTokenProps> {
-    render() {
-        const { classes, open, closeDialog, ...data } = this.props;
-        return <Dialog
-            open={open}
-            onClose={closeDialog}
-            fullWidth={true}
-            maxWidth='md'>
-            <DialogTitle>Current Token</DialogTitle>
-            <DialogContent>
-                <Typography variant='body1' paragraph={true}>
-                    The Arvados API token is a secret key that enables the Arvados SDKs to access Arvados with the proper permissions.
-                            <Typography component='p'>
-                        For more information see
-                                <a href='http://doc.arvados.org/user/reference/api-tokens.html' target='blank' className={classes.link}>
-                            Getting an API token.
-                                </a>
-                    </Typography>
-                </Typography>
-                <Typography variant='body1' paragraph={true}>
-                    Paste the following lines at a shell prompt to set up the necessary environment for Arvados SDKs to authenticate to your klingenc account.
-                        </Typography>
-                <DefaultCodeSnippet lines={[getSnippet(data)]} />
-                <Typography variant='body1'>
-                    Arvados
-                            <a href='http://doc.arvados.org/user/reference/api-tokens.html' target='blank' className={classes.link}>virtual machines</a>
-                    do this for you automatically. This setup is needed only when you use the API remotely (e.g., from your own workstation).
+export const CurrentTokenDialog =
+    withStyles(styles)(
+    connect(getCurrentTokenDialogData)(
+    withDialog('currentTokenDialog')(
+    class extends React.Component<CurrentTokenProps> {
+        render() {
+            const { classes, open, closeDialog, ...data } = this.props;
+            return <Dialog
+                open={open}
+                onClose={closeDialog}
+                fullWidth={true}
+                maxWidth='md'>
+                <DialogTitle>Current Token</DialogTitle>
+                <DialogContent>
+                    <Typography variant='body1' paragraph={true}>
+                        The Arvados API token is a secret key that enables the Arvados SDKs to access Arvados with the proper permissions.
+                                <Typography component='p'>
+                            For more information see
+                                    <a href='http://doc.arvados.org/user/reference/api-tokens.html' target='blank' className={classes.link}>
+                                Getting an API token.
+                                    </a>
                         </Typography>
-            </DialogContent>
-            <DialogActions>
-                <Button onClick={closeDialog} className={classes.button} color="primary">CLOSE</Button>
-            </DialogActions>
-        </Dialog>;
+                    </Typography>
+                    <Typography variant='body1' paragraph={true}>
+                        Paste the following lines at a shell prompt to set up the necessary environment for Arvados SDKs to authenticate to your klingenc account.
+                            </Typography>
+                    <DefaultCodeSnippet lines={[getSnippet(data)]} />
+                    <Typography variant='body1'>
+                        Arvados
+                                <a href='http://doc.arvados.org/user/reference/api-tokens.html' target='blank' className={classes.link}>virtual machines</a>
+                        do this for you automatically. This setup is needed only when you use the API remotely (e.g., from your own workstation).
+                            </Typography>
+                </DialogContent>
+                <DialogActions>
+                    <Button onClick={closeDialog} className={classes.button} color="primary">CLOSE</Button>
+                </DialogActions>
+            </Dialog>;
+        }
     }
-}
-);
+)));
 
 const getSnippet = ({ apiHost, currentToken }: CurrentTokenDialogData) =>
 `HISTIGNORE=$HISTIGNORE:'export ARVADOS_API_TOKEN=*'
index 74c3e64aaacf1139d5a90cff0c5ada69f40b14b2..17f2c77b0a44fdd3b235b1625c9d0ddf39ba430f 100644 (file)
@@ -15,7 +15,7 @@ import { DataColumns } from "~/components/data-table/data-table";
 interface Props {
     id: string;
     onRowClick: (item: any) => void;
-    onContextMenu: (event: React.MouseEvent<HTMLElement>, item: any) => void;
+    onContextMenu?: (event: React.MouseEvent<HTMLElement>, item: any) => void;
     onRowDoubleClick: (item: any) => void;
     extractKey?: (item: any) => React.Key;
 }
index b9cc63c30475edc89797a07de66963806deea154..12e1be7805c61c6f6674e282af0456cd78ce6670 100644 (file)
@@ -3,10 +3,10 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { Grid, Typography, withStyles } from '@material-ui/core';
+import { Grid, Typography, withStyles, Tooltip, IconButton } from '@material-ui/core';
 import { FavoriteStar } from '../favorite-star/favorite-star';
 import { ResourceKind, TrashableResource } from '~/models/resource';
-import { ProjectIcon, CollectionIcon, ProcessIcon, DefaultIcon } from '~/components/icon/icon';
+import { ProjectIcon, CollectionIcon, ProcessIcon, DefaultIcon, WorkflowIcon, ShareIcon } from '~/components/icon/icon';
 import { formatDate, formatFileSize } from '~/common/formatters';
 import { resourceLabel } from '~/common/labels';
 import { connect } from 'react-redux';
@@ -16,6 +16,9 @@ import { GroupContentsResource } from '~/services/groups-service/groups-service'
 import { getProcess, Process, getProcessStatus, getProcessStatusColor } from '~/store/processes/process';
 import { ArvadosTheme } from '~/common/custom-theme';
 import { compose } from 'redux';
+import { WorkflowResource } from '~/models/workflow';
+import { ResourceStatus } from '~/views/workflow-panel/workflow-panel-view';
+import { getUuidPrefix } from '~/store/workflow-panel/workflow-panel-actions';
 
 export const renderName = (item: { name: string; uuid: string, kind: string }) =>
     <Grid container alignItems="center" wrap="nowrap" spacing={16}>
@@ -48,6 +51,8 @@ export const renderIcon = (item: { kind: string }) => {
             return <CollectionIcon />;
         case ResourceKind.PROCESS:
             return <ProcessIcon />;
+        case ResourceKind.WORKFLOW:
+            return <WorkflowIcon />;
         default:
             return <DefaultIcon />;
     }
@@ -57,6 +62,68 @@ export const renderDate = (date?: string) => {
     return <Typography noWrap style={{ minWidth: '100px' }}>{formatDate(date)}</Typography>;
 };
 
+export const renderWorkflowName = (item: { name: string; uuid: string, kind: string, ownerUuid: string }) =>
+    <Grid container alignItems="center" wrap="nowrap" spacing={16}>
+        <Grid item>
+            {renderIcon(item)}
+        </Grid>
+        <Grid item>
+            <Typography color="primary" style={{ width: '100px' }}>
+                {item.name}
+            </Typography>
+        </Grid>
+    </Grid>;
+
+export const RosurceWorkflowName = connect(
+    (state: RootState, props: { uuid: string }) => {
+        const resource = getResource<WorkflowResource>(props.uuid)(state.resources);
+        return resource || { name: '', uuid: '', kind: '', ownerUuid: '' };
+    })(renderWorkflowName);
+
+const getPublicUuid = (uuidPrefix: string) => {
+    return `${uuidPrefix}-tpzed-anonymouspublic`;
+};
+
+// do share onClick
+export const resourceShare = (uuidPrefix: string, ownerUuid?: string) => {
+    return <Tooltip title="Share">
+        <IconButton onClick={() => undefined}>
+            {ownerUuid === getPublicUuid(uuidPrefix) ? <ShareIcon /> : null}
+        </IconButton>
+    </Tooltip>;
+};
+
+export const ResourceShare = connect(
+    (state: RootState, props: { uuid: string }) => {
+        const resource = getResource<WorkflowResource>(props.uuid)(state.resources);
+        const uuidPrefix = getUuidPrefix(state);
+        return {
+            ownerUuid: resource ? resource.ownerUuid : '',
+            uuidPrefix
+        };
+    })((props: { ownerUuid?: string, uuidPrefix: string }) => resourceShare(props.uuidPrefix, props.ownerUuid));
+
+export const renderWorkflowStatus = (uuidPrefix: string, ownerUuid?: string) => {
+    if (ownerUuid === getPublicUuid(uuidPrefix)) {
+        return renderStatus(ResourceStatus.PUBLIC);
+    } else {
+        return renderStatus(ResourceStatus.PRIVATE);
+    }
+};
+
+const renderStatus = (status: string) =>
+    <Typography noWrap style={{ width: '60px' }}>{status}</Typography>;
+
+export const ResourceWorkflowStatus = connect(
+    (state: RootState, props: { uuid: string }) => {
+        const resource = getResource<WorkflowResource>(props.uuid)(state.resources);
+        const uuidPrefix = getUuidPrefix(state);
+        return {
+            ownerUuid: resource ? resource.ownerUuid : '',
+            uuidPrefix
+        };
+    })((props: { ownerUuid?: string, uuidPrefix: string }) => renderWorkflowStatus(props.uuidPrefix, props.ownerUuid));
+
 export const ResourceLastModifiedDate = connect(
     (state: RootState, props: { uuid: string }) => {
         const resource = getResource<GroupContentsResource>(props.uuid)(state.resources);
index 1e65ec834bf8e48f75230bea8505d843eda82a63..331952f259e7ff357cd48a2e088007064e5a2a8d 100644 (file)
@@ -10,6 +10,7 @@ import { ResourceKind } from '~/models/resource';
 import { resourceLabel } from '~/common/labels';
 import { DetailsData } from "./details-data";
 import { DetailsAttribute } from "~/components/details-attribute/details-attribute";
+import { RichTextEditorLink } from '~/components/rich-text-editor-link/rich-text-editor-link';
 
 export class ProjectDetails extends DetailsData<ProjectResource> {
 
@@ -27,7 +28,15 @@ export class ProjectDetails extends DetailsData<ProjectResource> {
             <DetailsAttribute label='Created at' value={formatDate(this.item.createdAt)} />
             {/* Missing attr */}
             <DetailsAttribute label='File size' value='1.4 GB' />
-            <DetailsAttribute label='Description' value={this.item.description} />
+            <DetailsAttribute label='Description'>
+                {this.item.description ? 
+                    <RichTextEditorLink
+                        title={`Description of ${this.item.name}`}
+                        content={this.item.description} 
+                        label='Show full description' />
+                    : '---'
+                }
+            </DetailsAttribute>
         </div>;
     }
 }
index c83181c2823bd502b0b4209f1a285f9a3319528f..f20d398824482d6447029d8e731b406a04dfe749 100644 (file)
@@ -28,6 +28,7 @@ const mergeProps = (
         ...props
     });
 
+// TODO: Remove as any
 export const [FileRemoveDialog] = [ConfirmationDialog]
-    .map(connect(mapStateToProps, mapDispatchToProps, mergeProps))
+    .map(connect(mapStateToProps, mapDispatchToProps, mergeProps) as any)
     .map(withDialog(FILE_REMOVE_DIALOG));
index 6446c763a81300850c026ed3889ed4d86872837a..8243cfe364d45cee3d05fde0b3637a7b13a3838f 100644 (file)
@@ -4,8 +4,8 @@
 
 import * as React from "react";
 import { Field } from "redux-form";
-import { TextField } from "~/components/text-field/text-field";
-import { PROJECT_NAME_VALIDATION, PROJECT_DESCRIPTION_VALIDATION } from "~/validators/validators";
+import { TextField, RichEditorTextField } from "~/components/text-field/text-field";
+import { PROJECT_NAME_VALIDATION } from "~/validators/validators";
 
 export const ProjectNameField = () =>
     <Field
@@ -18,6 +18,5 @@ export const ProjectNameField = () =>
 export const ProjectDescriptionField = () =>
     <Field
         name='description'
-        component={TextField}
-        validate={PROJECT_DESCRIPTION_VALIDATION}
+        component={RichEditorTextField}
         label="Description - optional" />;
index 071b986ab57352050ef411e5cd6db7ecc3bd61ea..6fb419e36710aa187e0f28a79b46fba82ed5f0d7 100644 (file)
@@ -8,25 +8,36 @@ import { DetailsIcon } from "~/components/icon/icon";
 import { Breadcrumbs } from "~/views-components/breadcrumbs/breadcrumbs";
 import { detailsPanelActions } from "~/store/details-panel/details-panel-action";
 import { connect } from 'react-redux';
+import { RootState } from '~/store/store';
+import { matchWorkflowRoute } from '~/routes/routes';
 
 interface MainContentBarProps {
     onDetailsPanelToggle: () => void;
+    buttonVisible: boolean;
 }
 
-export const MainContentBar = connect(undefined, {
-    onDetailsPanelToggle: detailsPanelActions.TOGGLE_DETAILS_PANEL
-})((props: MainContentBarProps) =>
-    <Toolbar>
-        <Grid container>
-            <Grid container item xs alignItems="center">
-                <Breadcrumbs />
-            </Grid>
-            <Grid item>
-                <Tooltip title="Additional Info">
-                    <IconButton color="inherit" onClick={props.onDetailsPanelToggle}>
-                        <DetailsIcon />
-                    </IconButton>
-                </Tooltip>
+const isWorkflowPath = ({ router }: RootState) => {
+    const pathname = router.location ? router.location.pathname : '';
+    const match = matchWorkflowRoute(pathname);
+    return !!match;
+};
+
+export const MainContentBar = connect((state: RootState) => ({
+    buttonVisible: !isWorkflowPath(state)
+}), {
+        onDetailsPanelToggle: detailsPanelActions.TOGGLE_DETAILS_PANEL
+    })((props: MainContentBarProps) =>
+        <Toolbar>
+            <Grid container>
+                <Grid container item xs alignItems="center">
+                    <Breadcrumbs />
+                </Grid>
+                <Grid item>
+                    {props.buttonVisible ? <Tooltip title="Additional Info">
+                        <IconButton color="inherit" onClick={props.onDetailsPanelToggle}>
+                            <DetailsIcon />
+                        </IconButton>
+                    </Tooltip> : null}
+                </Grid>
             </Grid>
-        </Grid>
-    </Toolbar>);
+        </Toolbar>);
diff --git a/src/views-components/rich-text-editor-dialog/rich-text-editor-dialog.tsx b/src/views-components/rich-text-editor-dialog/rich-text-editor-dialog.tsx
new file mode 100644 (file)
index 0000000..a997355
--- /dev/null
@@ -0,0 +1,38 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from "react";
+import { Dialog, DialogTitle, DialogContent, DialogActions, Button, DialogContentText } from "@material-ui/core";
+import { WithDialogProps } from "../../store/dialog/with-dialog";
+import { withDialog } from '~/store/dialog/with-dialog';
+import { RICH_TEXT_EDITOR_DIALOG_NAME } from "~/store/rich-text-editor-dialog/rich-text-editor-dialog-actions";
+import RichTextEditor from 'react-rte';
+
+export interface RichTextEditorDialogDataProps {
+    title: string;
+    text: string;
+}
+
+export const RichTextEditorDialog = withDialog(RICH_TEXT_EDITOR_DIALOG_NAME)(
+    (props: WithDialogProps<RichTextEditorDialogDataProps>) =>
+        <Dialog open={props.open}
+            onClose={props.closeDialog}
+            fullWidth
+            maxWidth='sm'>
+            <DialogTitle>{props.data.title}</DialogTitle>
+            <DialogContent>
+                <RichTextEditor 
+                    value={RichTextEditor.createValueFromString(props.data.text, 'html')}
+                    readOnly={true} />
+            </DialogContent>
+            <DialogActions>
+                <Button
+                    variant='flat'
+                    color='primary'
+                    onClick={props.closeDialog}>
+                    Close
+                </Button>
+            </DialogActions>
+        </Dialog>
+);
\ No newline at end of file
index 89c3400b55cb00bb14bb93901d74e0b7b93a89c7..9ba23267dd3a1025a6f2f96fec956c86e2090a9f 100644 (file)
@@ -14,6 +14,8 @@ import { AddIcon, CollectionIcon, ProcessIcon, ProjectIcon } from '~/components/
 import { openProjectCreateDialog } from '~/store/projects/project-create-actions';
 import { openCollectionCreateDialog } from '~/store/collections/collection-create-actions';
 import { matchProjectRoute } from '~/routes/routes';
+import { navigateToRunProcess } from '~/store/navigation/navigation-action';
+import { runProcessPanelActions } from '~/store/run-process-panel/run-process-panel-actions';
 
 type CssRules = 'button' | 'menuItem' | 'icon';
 
@@ -66,10 +68,10 @@ export const SidePanelButton = withStyles(styles)(
             };
 
             render() {
-                const { classes, buttonVisible  } = this.props;
+                const { classes, buttonVisible } = this.props;
                 const { anchorEl } = this.state;
                 return <Toolbar>
-                    {buttonVisible  && <Grid container>
+                    {buttonVisible && <Grid container>
                         <Grid container item xs alignItems="center" justify="flex-start">
                             <Button variant="contained" color="primary" size="small" className={classes.button}
                                 aria-owns={anchorEl ? 'aside-menu-list' : undefined}
@@ -88,7 +90,7 @@ export const SidePanelButton = withStyles(styles)(
                                 <MenuItem className={classes.menuItem} onClick={this.handleNewCollectionClick}>
                                     <CollectionIcon className={classes.icon} /> New collection
                                 </MenuItem>
-                                <MenuItem className={classes.menuItem}>
+                                <MenuItem className={classes.menuItem} onClick={this.handleRunProcessClick}>
                                     <ProcessIcon className={classes.icon} /> Run a process
                                 </MenuItem>
                                 <MenuItem className={classes.menuItem} onClick={this.handleNewProjectClick}>
@@ -96,7 +98,7 @@ export const SidePanelButton = withStyles(styles)(
                                 </MenuItem>
                             </Menu>
                         </Grid>
-                    </Grid> }
+                    </Grid>}
                 </Toolbar>;
             }
 
@@ -104,6 +106,11 @@ export const SidePanelButton = withStyles(styles)(
                 this.props.dispatch<any>(openProjectCreateDialog(this.props.currentItemId));
             }
 
+            handleRunProcessClick = () => {
+                this.props.dispatch(runProcessPanelActions.SET_PROCESS_OWNER_UUID(this.props.currentItemId));
+                this.props.dispatch<any>(navigateToRunProcess);
+            }
+
             handleNewCollectionClick = () => {
                 this.props.dispatch<any>(openCollectionCreateDialog(this.props.currentItemId));
             }
index 739e9eac1139ee62b3b38eeb3b8988cea5cc3bb2..12e82dfb102f9ade1df6cd928a19c1957b7ebe3b 100644 (file)
@@ -36,11 +36,11 @@ const mapDispatchToProps = (dispatch: Dispatch): SidePanelTreeProps => ({
 const mapStateToProps = (state: RootState) => ({
 });
 
-export const SidePanel = compose(
-    withStyles(styles),
-    connect(mapStateToProps, mapDispatchToProps)
-)(({ classes, ...props }: WithStyles<CssRules> & SidePanelTreeProps) =>
+export const SidePanel = withStyles(styles)(
+    connect(mapStateToProps, mapDispatchToProps)(
+    ({ classes, ...props }: WithStyles<CssRules> & SidePanelTreeProps) =>
     <Grid item xs>
         <SidePanelButton />
         <SidePanelTree {...props} />
-    </Grid>);
+    </Grid>
+));
index 377c58bbd2674b4cfa4b86e8c361d485e526bfec..2928a94ef00265a0dc9c0c3391bb73778be3a4f0 100644 (file)
@@ -3,7 +3,6 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { compose } from 'redux';
 import { connect, DispatchProp } from 'react-redux';
 import { Grid, Typography, Button } from '@material-ui/core';
 import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
@@ -51,17 +50,15 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
 
 type LoginPanelProps = DispatchProp<any> & WithStyles<CssRules>;
 
-export const LoginPanel = compose(
-    withStyles(styles),
-    connect()
-)(({ classes, dispatch }: LoginPanelProps) => 
+export const LoginPanel = withStyles(styles)(connect()(
+    ({ classes, dispatch }: LoginPanelProps) =>
     <Grid container direction="column" item xs alignItems="center" justify="center" className={classes.root}>
         <Grid item className={classes.container}>
             <Typography variant="title" align="center" className={classes.title}>
                 Welcome to the Arvados Wrokbench
             </Typography>
             <Typography variant="body1" className={classes.content}>
-                The "Log in" button below will show you a Google sign-in page. 
+                The "Log in" button below will show you a Google sign-in page.
                 After you assure Google that you want to log in here with your Google account, you will be redirected back here to Arvados Workbench.
             </Typography>
             <Typography variant="body1" className={classes.content}>
@@ -80,4 +77,4 @@ export const LoginPanel = compose(
             </Typography>
         </Grid>
     </Grid>
-);
\ No newline at end of file
+));
index 40fd8b229681ff38abd14125450e812e1767d564..08131266fe69d0af6cf2032f0876c17e8dbda98a 100644 (file)
@@ -70,7 +70,7 @@ export interface ProcessInformationCardDataProps {
     onContextMenu: (event: React.MouseEvent<HTMLElement>) => void;
 }
 
-type ProcessInformationCardProps = ProcessInformationCardDataProps & WithStyles<CssRules>;
+type ProcessInformationCardProps = ProcessInformationCardDataProps & WithStyles<CssRules, true>;
 
 export const ProcessInformationCard = withStyles(styles, { withTheme: true })(
     ({ classes, process, onContextMenu, theme }: ProcessInformationCardProps) =>
index 54e0206e3624c8b512b8b730e318ffe9bd04228b..f05254531041809ffa759db6b2b526bae6aa2984 100644 (file)
@@ -69,7 +69,7 @@ export interface ProcessSubprocessesCardDataProps {
     subprocess: Process;
 }
 
-type ProcessSubprocessesCardProps = ProcessSubprocessesCardDataProps & WithStyles<CssRules>;
+type ProcessSubprocessesCardProps = ProcessSubprocessesCardDataProps & WithStyles<CssRules, true>;
 
 export const ProcessSubprocessesCard = withStyles(styles, { withTheme: true })(
     ({ classes, onContextMenu, subprocess, theme }: ProcessSubprocessesCardProps) => {
@@ -105,4 +105,4 @@ export const ProcessSubprocessesCard = withStyles(styles, { withTheme: true })(
                     label="Runtime" value={formatTime(getProcessRuntime(subprocess))} />
             </CardContent>
         </Card>;
-    });
\ No newline at end of file
+    });
diff --git a/src/views/run-process-panel/inputs/boolean-input.tsx b/src/views/run-process-panel/inputs/boolean-input.tsx
new file mode 100644 (file)
index 0000000..e66ec3d
--- /dev/null
@@ -0,0 +1,31 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { BooleanCommandInputParameter } from '~/models/workflow';
+import { Field } from 'redux-form';
+import { Switch } from '@material-ui/core';
+import { GenericInputProps, GenericInput } from './generic-input';
+
+export interface BooleanInputProps {
+    input: BooleanCommandInputParameter;
+}
+export const BooleanInput = ({ input }: BooleanInputProps) =>
+    <Field
+        name={input.id}
+        commandInput={input}
+        component={BooleanInputComponent}
+        normalize={(value, prevValue) => !prevValue}
+    />;
+
+const BooleanInputComponent = (props: GenericInputProps) =>
+    <GenericInput
+        component={Input}
+        {...props} />;
+
+const Input = (props: GenericInputProps) =>
+    <Switch
+        color='primary'
+        checked={props.input.value}
+        onChange={() => props.input.onChange(props.input.value)} />;
\ No newline at end of file
diff --git a/src/views/run-process-panel/inputs/enum-input.tsx b/src/views/run-process-panel/inputs/enum-input.tsx
new file mode 100644 (file)
index 0000000..967d8fc
--- /dev/null
@@ -0,0 +1,36 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { EnumCommandInputParameter, CommandInputEnumSchema } from '~/models/workflow';
+import { Field } from 'redux-form';
+import { Select, MenuItem } from '@material-ui/core';
+import { GenericInputProps, GenericInput } from './generic-input';
+
+export interface EnumInputProps {
+    input: EnumCommandInputParameter;
+}
+export const EnumInput = ({ input }: EnumInputProps) =>
+    <Field
+        name={input.id}
+        commandInput={input}
+        component={EnumInputComponent}
+    />;
+
+const EnumInputComponent = (props: GenericInputProps) =>
+    <GenericInput
+        component={Input}
+        {...props} />;
+
+const Input = (props: GenericInputProps) => {
+    const type = props.commandInput.type as CommandInputEnumSchema;
+    return <Select
+        value={props.input.value}
+        onChange={props.input.onChange}>
+        {type.symbols.map(symbol =>
+            <MenuItem key={symbol} value={symbol.split('/').pop()}>
+                {symbol.split('/').pop()}
+            </MenuItem>)}
+    </Select>;
+};
\ No newline at end of file
diff --git a/src/views/run-process-panel/inputs/file-input.tsx b/src/views/run-process-panel/inputs/file-input.tsx
new file mode 100644 (file)
index 0000000..7c4402f
--- /dev/null
@@ -0,0 +1,42 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import {
+    getInputLabel,
+    isRequiredInput,
+    FileCommandInputParameter,
+    File,
+    CWLType
+} from '~/models/workflow';
+import { Field } from 'redux-form';
+import { require } from '~/validators/require';
+import { Input } from '@material-ui/core';
+import { GenericInputProps, GenericInput } from './generic-input';
+
+export interface FileInputProps {
+    input: FileCommandInputParameter;
+}
+export const FileInput = ({ input }: FileInputProps) =>
+    <Field
+        name={input.id}
+        commandInput={input}        
+        component={FileInputComponent}
+        format={(value?: File) => value ? value.location : ''}
+        parse={(value: string): File => ({
+            class: CWLType.FILE,
+            location: value,
+            basename: value.split('/').slice(1).join('/')
+        })}
+        validate={[
+            isRequiredInput(input)
+                ? require
+                : () => undefined,
+        ]} />;
+
+const FileInputComponent = (props: GenericInputProps) =>
+    <GenericInput
+        component={props =>
+            <Input readOnly fullWidth value={props.input.value} error={props.meta.touched && !!props.meta.error}/>}
+        {...props} />;
diff --git a/src/views/run-process-panel/inputs/float-input.tsx b/src/views/run-process-panel/inputs/float-input.tsx
new file mode 100644 (file)
index 0000000..aeaf6cd
--- /dev/null
@@ -0,0 +1,54 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { FloatCommandInputParameter, isRequiredInput } from '~/models/workflow';
+import { Field } from 'redux-form';
+import { isNumber } from '~/validators/is-number';
+import { GenericInput } from '~/views/run-process-panel/inputs/generic-input';
+import { Input as MaterialInput } from '@material-ui/core';
+import { GenericInputProps } from './generic-input';
+export interface FloatInputProps {
+    input: FloatCommandInputParameter;
+}
+export const FloatInput = ({ input }: FloatInputProps) =>
+    <Field
+        name={input.id}
+        commandInput={input}
+        component={FloatInputComponent}
+        parse={parseFloat}
+        format={value => isNaN(value) ? '' : JSON.stringify(value)}
+        validate={[
+            isRequiredInput(input)
+                ? isNumber
+                : () => undefined,]} />;
+
+class FloatInputComponent extends React.Component<GenericInputProps> {
+    state = {
+        endsWithDecimalSeparator: false,
+    };
+
+    handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+        const [base, fraction] = event.target.value.split('.');
+        this.setState({ endsWithDecimalSeparator: fraction === '' });
+        this.props.input.onChange(event);
+    }
+
+    render() {
+        const props = {
+            ...this.props,
+            input: {
+                ...this.props.input,
+                value: this.props.input.value + (this.state.endsWithDecimalSeparator ? '.' : ''),
+                onChange: this.handleChange,
+            },
+        };
+        return <GenericInput
+            component={Input}
+            {...props} />;
+    }
+}
+
+const Input = (props: GenericInputProps) =>
+    <MaterialInput fullWidth {...props.input} error={props.meta.touched && !!props.meta.error} />;
diff --git a/src/views/run-process-panel/inputs/generic-input.tsx b/src/views/run-process-panel/inputs/generic-input.tsx
new file mode 100644 (file)
index 0000000..a449c65
--- /dev/null
@@ -0,0 +1,34 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { WrappedFieldProps } from 'redux-form';
+import { FormGroup, FormLabel, Input, FormHelperText, FormControl } from '@material-ui/core';
+import { GenericCommandInputParameter, getInputLabel, isRequiredInput } from '../../../models/workflow';
+
+export type GenericInputProps = WrappedFieldProps & {
+    commandInput: GenericCommandInputParameter<any, any>;
+};
+
+type GenericInputContainerProps = GenericInputProps & {
+    component: React.ComponentType<GenericInputProps>;
+};
+export const GenericInput = ({ component: Component, ...props }: GenericInputContainerProps) => {
+    return <FormGroup>
+        <FormLabel
+            focused={props.meta.active}
+            required={isRequiredInput(props.commandInput)}
+            error={props.meta.touched && !!props.meta.error}>
+            {getInputLabel(props.commandInput)}
+        </FormLabel>
+        <Component {...props} />
+        <FormHelperText error={props.meta.touched && !!props.meta.error}>
+            {
+                props.meta.touched && props.meta.error
+                    ? props.meta.error
+                    : props.commandInput.doc
+            }
+        </FormHelperText>
+    </FormGroup>;
+};
\ No newline at end of file
diff --git a/src/views/run-process-panel/inputs/int-input.tsx b/src/views/run-process-panel/inputs/int-input.tsx
new file mode 100644 (file)
index 0000000..193de26
--- /dev/null
@@ -0,0 +1,36 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { IntCommandInputParameter, getInputLabel, isRequiredInput } from '~/models/workflow';
+import { Field } from 'redux-form';
+import { isInteger } from '~/validators/is-integer';
+import { GenericInputProps, GenericInput } from '~/views/run-process-panel/inputs/generic-input';
+import { Input as MaterialInput } from '@material-ui/core';
+
+export interface IntInputProps {
+    input: IntCommandInputParameter;
+}
+export const IntInput = ({ input }: IntInputProps) =>
+    <Field
+        name={input.id}
+        commandInput={input}
+        component={IntInputComponent}
+        parse={value => parseInt(value, 10)}
+        format={value => isNaN(value) ? '' : JSON.stringify(value)}
+        validate={[
+            isRequiredInput(input)
+                ? isInteger
+                : () => undefined,
+        ]} />;
+
+const IntInputComponent = (props: GenericInputProps) =>
+    <GenericInput
+        component={Input}
+        {...props} />;
+
+
+const Input = (props: GenericInputProps) =>
+    <MaterialInput fullWidth type='number' {...props.input} error={props.meta.touched && !!props.meta.error} />;
+
diff --git a/src/views/run-process-panel/inputs/string-input.tsx b/src/views/run-process-panel/inputs/string-input.tsx
new file mode 100644 (file)
index 0000000..7f02e1d
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { isRequiredInput, StringCommandInputParameter } from '~/models/workflow';
+import { Field } from 'redux-form';
+import { require } from '~/validators/require';
+import { GenericInputProps, GenericInput } from '~/views/run-process-panel/inputs/generic-input';
+import { Input as MaterialInput } from '@material-ui/core';
+
+export interface StringInputProps {
+    input: StringCommandInputParameter;
+}
+export const StringInput = ({ input }: StringInputProps) =>
+    <Field
+        name={input.id}
+        commandInput={input}
+        component={StringInputComponent}
+        validate={[
+            isRequiredInput(input)
+                ? require
+                : () => undefined,
+        ]} />;
+
+const StringInputComponent = (props: GenericInputProps) =>
+    <GenericInput
+        component={Input}
+        {...props} />;
+
+const Input = (props: GenericInputProps) =>
+    <MaterialInput fullWidth {...props.input} error={props.meta.touched && !!props.meta.error} />;
\ No newline at end of file
diff --git a/src/views/run-process-panel/run-process-advanced-form.tsx b/src/views/run-process-panel/run-process-advanced-form.tsx
new file mode 100644 (file)
index 0000000..19beab6
--- /dev/null
@@ -0,0 +1,45 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { ExpansionPanel, ExpansionPanelDetails, ExpansionPanelSummary } from '@material-ui/core';
+import { reduxForm, Field } from 'redux-form';
+import { Grid } from '@material-ui/core';
+import { TextField } from '~/components/text-field/text-field';
+import { ExpandIcon } from '~/components/icon/icon';
+
+export const RUN_PROCESS_ADVANCED_FORM = 'runProcessAdvancedForm';
+
+export interface RunProcessAdvancedFormData {
+    output: string;
+    runtime: string;
+}
+
+export const RunProcessAdvancedForm =
+    reduxForm<RunProcessAdvancedFormData>({
+        form: RUN_PROCESS_ADVANCED_FORM
+    })(() =>
+        <form>
+            <ExpansionPanel elevation={0}>
+                <ExpansionPanelSummary style={{ padding: 0 }} expandIcon={<ExpandIcon />}>
+                    Advanced
+                </ExpansionPanelSummary>
+                <ExpansionPanelDetails style={{ padding: 0 }}>
+                    <Grid container spacing={32}>
+                        <Grid item xs={12} md={6}>
+                            <Field
+                                name='output'
+                                component={TextField}
+                                label="Output name" />
+                        </Grid>
+                        <Grid item xs={12} md={6}>
+                            <Field
+                                name='runtime'
+                                component={TextField}
+                                label="Runtime limit (hh)" />
+                        </Grid>
+                    </Grid>
+                </ExpansionPanelDetails>
+            </ExpansionPanel>
+        </form >);
diff --git a/src/views/run-process-panel/run-process-basic-form.tsx b/src/views/run-process-panel/run-process-basic-form.tsx
new file mode 100644 (file)
index 0000000..2739824
--- /dev/null
@@ -0,0 +1,38 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { reduxForm, Field } from 'redux-form';
+import { Grid } from '@material-ui/core';
+import { TextField } from '~/components/text-field/text-field';
+import { PROCESS_NAME_VALIDATION } from '~/validators/validators';
+
+export const RUN_PROCESS_BASIC_FORM = 'runProcessBasicForm';
+
+export interface RunProcessBasicFormData {
+    name: string;
+    description: string;
+}
+export const RunProcessBasicForm =
+    reduxForm<RunProcessBasicFormData>({
+        form: RUN_PROCESS_BASIC_FORM
+    })(() =>
+        <form>
+            <Grid container spacing={32}>
+                <Grid item xs={12} md={6}>
+                    <Field
+                        name='name'
+                        component={TextField}
+                        label="Enter a new name for run process"
+                        required
+                        validate={PROCESS_NAME_VALIDATION} />
+                </Grid>
+                <Grid item xs={12} md={6}>
+                    <Field
+                        name='description'
+                        component={TextField}
+                        label="Enter a description for run process" />
+                </Grid>
+            </Grid>
+        </form>);
diff --git a/src/views/run-process-panel/run-process-first-step.tsx b/src/views/run-process-panel/run-process-first-step.tsx
new file mode 100644 (file)
index 0000000..fe93ef8
--- /dev/null
@@ -0,0 +1,91 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { StyleRulesCallback, withStyles, Grid, Button, WithStyles, List, ListItem, ListItemText, ListItemIcon, Tabs, Tab } from '@material-ui/core';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { WorkflowResource } from '~/models/workflow';
+import { WorkflowIcon } from '~/components/icon/icon';
+import { WorkflowDetailsCard } from '../workflow-panel/workflow-description-card';
+import { SearchInput } from '~/components/search-input/search-input';
+
+type CssRules = 'root' | 'searchGrid' | 'workflowDetailsGrid' | 'list' | 'listItem' | 'itemSelected' | 'listItemText' | 'listItemIcon';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    root: {
+        alignSelf: 'flex-start'
+    },
+    searchGrid: {
+        marginBottom: theme.spacing.unit * 2
+    },
+    workflowDetailsGrid: {
+        borderLeft: `1px solid ${theme.palette.grey["300"]}`
+    },
+    list: {
+        maxHeight: 300,
+        position: 'relative',
+        overflow: 'auto'
+    },
+    listItem: {
+        padding: theme.spacing.unit,
+    },
+    itemSelected: {
+        backgroundColor: 'rgba(3, 190, 171, 0.3) !important'
+    },
+    listItemText: {
+        fontSize: '0.875rem'
+    },
+    listItemIcon: {
+        color: theme.customs.colors.red900
+    }
+});
+
+export interface RunProcessFirstStepDataProps {
+    workflows: WorkflowResource[];
+    selectedWorkflow: WorkflowResource | undefined;
+}
+
+export interface RunProcessFirstStepActionProps {
+    onSearch: (term: string) => void;
+    onSetStep: (step: number) => void;
+    onSetWorkflow: (workflow: WorkflowResource) => void;
+}
+
+type RunProcessFirstStepProps = RunProcessFirstStepDataProps & RunProcessFirstStepActionProps & WithStyles<CssRules>;
+
+export const RunProcessFirstStep = withStyles(styles)(
+    ({ onSearch, onSetStep, onSetWorkflow, workflows, selectedWorkflow, classes }: RunProcessFirstStepProps) =>
+        <Grid container spacing={16}>
+            <Grid container item xs={6} className={classes.root}>
+                <Grid item xs={12} className={classes.searchGrid}>
+                    <SearchInput value='' onSearch={onSearch} />
+                </Grid>
+                <Grid item xs={12}>
+                    <List className={classes.list}>
+                        {workflows.map(workflow => (
+                            <ListItem key={workflow.uuid} button
+                                classes={{ root: classes.listItem, selected: classes.itemSelected}}
+                                selected={selectedWorkflow && (selectedWorkflow.uuid === workflow.uuid)}
+                                onClick={() => onSetWorkflow(workflow)}>
+                                <ListItemIcon>
+                                    <WorkflowIcon className={classes.listItemIcon}/>
+                                </ListItemIcon>
+                                <ListItemText className={classes.listItemText} primary={workflow.name} disableTypography={true} />
+                            </ListItem>
+                        ))}
+                    </List>
+                </Grid>
+            </Grid>
+            <Grid item xs={6} className={classes.workflowDetailsGrid}>
+                <WorkflowDetailsCard workflow={selectedWorkflow}/>
+            </Grid>
+            <Grid item xs={12}>
+                <Button variant="contained" color="primary" 
+                    disabled={!(!!selectedWorkflow)}
+                    onClick={() => onSetStep(1)}>
+                    Next
+                </Button>
+            </Grid>
+        </Grid>
+);
diff --git a/src/views/run-process-panel/run-process-inputs-form.tsx b/src/views/run-process-panel/run-process-inputs-form.tsx
new file mode 100644 (file)
index 0000000..b0d5d5f
--- /dev/null
@@ -0,0 +1,83 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { reduxForm, InjectedFormProps } from 'redux-form';
+import { CommandInputParameter, CWLType, IntCommandInputParameter, BooleanCommandInputParameter, FileCommandInputParameter } from '~/models/workflow';
+import { IntInput } from '~/views/run-process-panel/inputs/int-input';
+import { StringInput } from '~/views/run-process-panel/inputs/string-input';
+import { StringCommandInputParameter, FloatCommandInputParameter, isPrimitiveOfType, File, Directory, WorkflowInputsData, EnumCommandInputParameter } from '../../models/workflow';
+import { FloatInput } from '~/views/run-process-panel/inputs/float-input';
+import { BooleanInput } from './inputs/boolean-input';
+import { FileInput } from './inputs/file-input';
+import { connect } from 'react-redux';
+import { compose } from 'redux';
+import { Grid, StyleRulesCallback, withStyles, WithStyles } from '@material-ui/core';
+import { EnumInput } from './inputs/enum-input';
+
+export const RUN_PROCESS_INPUTS_FORM = 'runProcessInputsForm';
+
+export interface RunProcessInputFormProps {
+    inputs: CommandInputParameter[];
+}
+
+export const RunProcessInputsForm = compose(
+    connect((_: any, props: RunProcessInputFormProps) => ({
+        initialValues: props.inputs.reduce(
+            (values, input) => ({ ...values, [input.id]: input.default }),
+            {}),
+    })),
+    reduxForm<WorkflowInputsData, RunProcessInputFormProps>({
+        form: RUN_PROCESS_INPUTS_FORM
+    }))(
+        (props: InjectedFormProps & RunProcessInputFormProps) =>
+            <form>
+                <Grid container spacing={32}>
+                    {props.inputs.map(input =>
+                        <InputItem input={input} key={input.id} />)}
+                </Grid>
+            </form>);
+
+type CssRules = 'inputItem';
+
+const styles: StyleRulesCallback<CssRules> = theme => ({
+    inputItem: {
+        marginBottom: theme.spacing.unit * 2,
+    }
+});
+
+const InputItem = withStyles(styles)(
+    (props: WithStyles<CssRules> & { input: CommandInputParameter }) =>
+        <Grid item xs={12} md={6} className={props.classes.inputItem}>
+            {getInputComponent(props.input)}
+        </Grid>);
+
+const getInputComponent = (input: CommandInputParameter) => {
+    switch (true) {
+        case isPrimitiveOfType(input, CWLType.BOOLEAN):
+            return <BooleanInput input={input as BooleanCommandInputParameter} />;
+
+        case isPrimitiveOfType(input, CWLType.INT):
+        case isPrimitiveOfType(input, CWLType.LONG):
+            return <IntInput input={input as IntCommandInputParameter} />;
+
+        case isPrimitiveOfType(input, CWLType.FLOAT):
+        case isPrimitiveOfType(input, CWLType.DOUBLE):
+            return <FloatInput input={input as FloatCommandInputParameter} />;
+
+        case isPrimitiveOfType(input, CWLType.STRING):
+            return <StringInput input={input as StringCommandInputParameter} />;
+
+        case isPrimitiveOfType(input, CWLType.FILE):
+            return <FileInput input={input as FileCommandInputParameter} />;
+
+        case typeof input.type === 'object' &&
+            !(input.type instanceof Array) &&
+            input.type.type === 'enum':
+            return <EnumInput input={input as EnumCommandInputParameter} />;
+
+        default:
+            return null;
+    }
+};
diff --git a/src/views/run-process-panel/run-process-panel-root.tsx b/src/views/run-process-panel/run-process-panel-root.tsx
new file mode 100644 (file)
index 0000000..1489527
--- /dev/null
@@ -0,0 +1,41 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { Stepper, Step, StepLabel, StepContent } from '@material-ui/core';
+import { RunProcessFirstStepDataProps, RunProcessFirstStepActionProps, RunProcessFirstStep } from '~/views/run-process-panel/run-process-first-step';
+import { RunProcessSecondStepForm } from './run-process-second-step';
+
+export type RunProcessPanelRootDataProps = {
+    currentStep: number;
+} & RunProcessFirstStepDataProps;
+
+export type RunProcessPanelRootActionProps = RunProcessFirstStepActionProps & {
+    runProcess: () => void;
+};
+
+type RunProcessPanelRootProps = RunProcessPanelRootDataProps & RunProcessPanelRootActionProps;
+
+export const RunProcessPanelRoot = ({ runProcess, currentStep, onSearch, onSetStep, onSetWorkflow, workflows, selectedWorkflow }: RunProcessPanelRootProps) =>
+    <Stepper activeStep={currentStep} orientation="vertical" elevation={2}>
+        <Step>
+            <StepLabel>Choose a workflow</StepLabel>
+            <StepContent>
+                <RunProcessFirstStep
+                    workflows={workflows}
+                    selectedWorkflow={selectedWorkflow}
+                    onSearch={onSearch}
+                    onSetStep={onSetStep} 
+                    onSetWorkflow={onSetWorkflow} />
+            </StepContent>
+        </Step>
+        <Step>
+            <StepLabel>Select inputs</StepLabel>
+            <StepContent>
+                <RunProcessSecondStepForm
+                    goBack={() => onSetStep(0)}
+                    runProcess={runProcess} />
+            </StepContent>
+        </Step>
+    </Stepper>;
\ No newline at end of file
diff --git a/src/views/run-process-panel/run-process-panel.tsx b/src/views/run-process-panel/run-process-panel.tsx
new file mode 100644 (file)
index 0000000..42324ab
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from 'redux';
+import { connect } from 'react-redux';
+import { RootState } from '~/store/store';
+import { RunProcessPanelRootDataProps, RunProcessPanelRootActionProps, RunProcessPanelRoot } from '~/views/run-process-panel/run-process-panel-root';
+import { goToStep, setWorkflow, runProcess, searchWorkflows } from '~/store/run-process-panel/run-process-panel-actions';
+import { WorkflowResource } from '~/models/workflow';
+
+const mapStateToProps = ({ runProcessPanel }: RootState): RunProcessPanelRootDataProps => {
+    return {
+        workflows: runProcessPanel.searchWorkflows,
+        currentStep: runProcessPanel.currentStep,
+        selectedWorkflow: runProcessPanel.selectedWorkflow
+    };
+};
+
+const mapDispatchToProps = (dispatch: Dispatch): RunProcessPanelRootActionProps => ({
+    onSetStep: (step: number) => {
+        dispatch<any>(goToStep(step));
+    },
+    onSetWorkflow: (workflow: WorkflowResource) => {
+        dispatch<any>(setWorkflow(workflow));
+    },
+    runProcess: () => {
+        dispatch<any>(runProcess);
+    },
+    onSearch: (term: string) => {
+        dispatch<any>(searchWorkflows(term));
+    }
+});
+
+export const RunProcessPanel = connect(mapStateToProps, mapDispatchToProps)(RunProcessPanelRoot);
\ No newline at end of file
diff --git a/src/views/run-process-panel/run-process-second-step.tsx b/src/views/run-process-panel/run-process-second-step.tsx
new file mode 100644 (file)
index 0000000..2585136
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { Grid, Button } from '@material-ui/core';
+import { RunProcessBasicForm, RUN_PROCESS_BASIC_FORM } from './run-process-basic-form';
+import { RunProcessInputsForm } from '~/views/run-process-panel/run-process-inputs-form';
+import { CommandInputParameter } from '~/models/workflow';
+import { connect } from 'react-redux';
+import { RootState } from '~/store/store';
+import { isValid } from 'redux-form';
+import { RUN_PROCESS_INPUTS_FORM } from './run-process-inputs-form';
+import { RunProcessAdvancedForm } from './run-process-advanced-form';
+
+export interface RunProcessSecondStepFormDataProps {
+    inputs: CommandInputParameter[];
+    valid: boolean;
+}
+
+export interface RunProcessSecondStepFormActionProps {
+    goBack: () => void;
+    runProcess: () => void;
+}
+
+const mapStateToProps = (state: RootState): RunProcessSecondStepFormDataProps => ({
+    inputs: state.runProcessPanel.inputs,
+    valid: isValid(RUN_PROCESS_BASIC_FORM)(state) &&
+        isValid(RUN_PROCESS_INPUTS_FORM)(state),
+});
+
+export type RunProcessSecondStepFormProps = RunProcessSecondStepFormDataProps & RunProcessSecondStepFormActionProps;
+export const RunProcessSecondStepForm = connect(mapStateToProps)(
+    ({ inputs, valid, goBack, runProcess }: RunProcessSecondStepFormProps) =>
+        <Grid container spacing={16}>
+            <Grid item xs={12}>
+                <RunProcessBasicForm />
+                <RunProcessInputsForm inputs={inputs} />
+                <RunProcessAdvancedForm />
+            </Grid>
+            <Grid item xs={12}>
+                <Button color="primary" onClick={goBack}>
+                    Back
+                </Button>
+                <Button disabled={!valid} variant="contained" color="primary" onClick={runProcess}>
+                    Run Process
+                </Button>
+            </Grid>
+        </Grid>);
index 78918be0c65c687d5230fa622c215c9e04ca847b..fe09564392027bf76feadbaedeab48b9aac3891c 100644 (file)
@@ -11,6 +11,7 @@ import { ArvadosTheme } from '~/common/custom-theme';
 import { ContextMenu } from "~/views-components/context-menu/context-menu";
 import { FavoritePanel } from "../favorite-panel/favorite-panel";
 import { CurrentTokenDialog } from '~/views-components/current-token-dialog/current-token-dialog';
+import { RichTextEditorDialog } from '~/views-components/rich-text-editor-dialog/rich-text-editor-dialog';
 import { Snackbar } from '~/views-components/snackbar/snackbar';
 import { CollectionPanel } from '../collection-panel/collection-panel';
 import { RenameFileDialog } from '~/views-components/rename-file-dialog/rename-file-dialog';
@@ -32,12 +33,14 @@ import { MoveProjectDialog } from '~/views-components/dialog-forms/move-project-
 import { MoveCollectionDialog } from '~/views-components/dialog-forms/move-collection-dialog';
 import { FilesUploadCollectionDialog } from '~/views-components/dialog-forms/files-upload-collection-dialog';
 import { PartialCopyCollectionDialog } from '~/views-components/dialog-forms/partial-copy-collection-dialog';
-import { TrashPanel } from "~/views/trash-panel/trash-panel";
+import { ProcessCommandDialog } from '~/views-components/process-command-dialog/process-command-dialog';
 import { MainContentBar } from '~/views-components/main-content-bar/main-content-bar';
 import { Grid } from '@material-ui/core';
-import { SharedWithMePanel } from '../shared-with-me-panel/shared-with-me-panel';
+import { TrashPanel } from "~/views/trash-panel/trash-panel";
+import { SharedWithMePanel } from '~/views/shared-with-me-panel/shared-with-me-panel';
+import { RunProcessPanel } from '~/views/run-process-panel/run-process-panel';
 import SplitterLayout from 'react-splitter-layout';
-import { ProcessCommandDialog } from '~/views-components/process-command-dialog/process-command-dialog';
+import { WorkflowPanel } from '~/views/workflow-panel/workflow-panel';
 
 type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
 
@@ -76,7 +79,7 @@ export const WorkbenchPanel =
         <Grid container item xs className={classes.root}>
             <Grid container item xs className={classes.container}>
                 <SplitterLayout customClassName={classes.splitter} percentage={true}
-                    primaryIndex={0} primaryMinSize={20} secondaryInitialSize={80} secondaryMinSize={40}>
+                    primaryIndex={0} primaryMinSize={15} secondaryInitialSize={85} secondaryMinSize={40}>
                     <Grid container item xs component='aside' direction='column' className={classes.asidePanel}>
                         <SidePanel />
                     </Grid>
@@ -93,6 +96,8 @@ export const WorkbenchPanel =
                                 <Route path={Routes.TRASH} component={TrashPanel} />
                                 <Route path={Routes.PROCESS_LOGS} component={ProcessLogPanel} />
                                 <Route path={Routes.SHARED_WITH_ME} component={SharedWithMePanel} />
+                                <Route path={Routes.RUN_PROCESS} component={RunProcessPanel} />
+                                <Route path={Routes.WORKFLOWS} component={WorkflowPanel} />
                             </Switch>
                         </Grid>
                     </Grid>
@@ -117,6 +122,7 @@ export const WorkbenchPanel =
             <PartialCopyCollectionDialog />
             <ProcessCommandDialog />
             <RenameFileDialog />
+            <RichTextEditorDialog />
             <Snackbar />
             <UpdateCollectionDialog />
             <UpdateProcessDialog />
diff --git a/src/views/workflow-panel/workflow-description-card.tsx b/src/views/workflow-panel/workflow-description-card.tsx
new file mode 100644 (file)
index 0000000..a983564
--- /dev/null
@@ -0,0 +1,96 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { StyleRulesCallback, WithStyles, withStyles, CardContent, Tab, Tabs, Typography, List, ListItem, Table, TableHead, TableCell, TableBody, TableRow } from '@material-ui/core';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { WorkflowIcon } from '~/components/icon/icon';
+import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
+import { WorkflowResource, parseWorkflowDefinition, getWorkflowInputs, getInputLabel, stringifyInputType } from '~/models/workflow';
+
+export type CssRules = 'root' | 'tab';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    root: {
+        height: '100%',
+    },
+    tab: {
+        minWidth: '50%'
+    }
+});
+
+interface WorkflowDetailsCardDataProps {
+    workflow?: WorkflowResource;
+}
+
+type WorkflowDetailsCardProps = WorkflowDetailsCardDataProps & WithStyles<CssRules>;
+
+export const WorkflowDetailsCard = withStyles(styles)(
+    class extends React.Component<WorkflowDetailsCardProps> {
+        state = {
+            value: 0,
+        };
+
+        handleChange = (event: React.MouseEvent<HTMLElement>, value: number) => {
+            this.setState({ value });
+        }
+
+        render() {
+            const { classes, workflow } = this.props;
+            const { value } = this.state;
+            return <div className={classes.root}>
+                <Tabs value={value} onChange={this.handleChange} centered={true}>
+                    <Tab className={classes.tab} label="Description" />
+                    <Tab className={classes.tab} label="Inputs" />
+                </Tabs>
+                {value === 0 && <CardContent>
+                    {workflow ? (
+                        workflow.description
+                    ) : (
+                            <DataTableDefaultView
+                                icon={WorkflowIcon}
+                                messages={['Please select a workflow to see its description.']} />
+                        )}
+                </CardContent>}
+                {value === 1 && <CardContent>
+                    {workflow
+                        ? this.renderInputsTable()
+                        : <DataTableDefaultView
+                            icon={WorkflowIcon}
+                            messages={['Please select a workflow to see its inputs.']} />
+                    }
+                </CardContent>}
+            </div>;
+        }
+
+        get inputs() {
+            if (this.props.workflow) {
+                const definition = parseWorkflowDefinition(this.props.workflow);
+                if (definition) {
+                    return getWorkflowInputs(definition);
+                }
+            }
+            return;
+        }
+
+        renderInputsTable() {
+            return <Table>
+                <TableHead>
+                    <TableRow>
+                        <TableCell>Label</TableCell>
+                        <TableCell>Type</TableCell>
+                        <TableCell>Description</TableCell>
+                    </TableRow>
+                </TableHead>
+                <TableBody>
+                    {this.inputs && this.inputs.map(input =>
+                        <TableRow key={input.id}>
+                            <TableCell>{getInputLabel(input)}</TableCell>
+                            <TableCell>{stringifyInputType(input)}</TableCell>
+                            <TableCell>{input.doc}</TableCell>
+                        </TableRow>)}
+                </TableBody>
+            </Table>;
+        }
+    });
\ No newline at end of file
diff --git a/src/views/workflow-panel/workflow-panel-view.tsx b/src/views/workflow-panel/workflow-panel-view.tsx
new file mode 100644 (file)
index 0000000..c49ec88
--- /dev/null
@@ -0,0 +1,129 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { DataExplorer } from "~/views-components/data-explorer/data-explorer";
+import { WorkflowIcon } from '~/components/icon/icon';
+import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
+import { WORKFLOW_PANEL_ID, workflowPanelActions } from '~/store/workflow-panel/workflow-panel-actions';
+import {
+    ResourceLastModifiedDate,
+    RosurceWorkflowName,
+    ResourceWorkflowStatus,
+    ResourceShare
+} from "~/views-components/data-explorer/renderers";
+import { SortDirection } from '~/components/data-table/data-column';
+import { DataColumns } from '~/components/data-table/data-table';
+import { DataTableFilterItem } from '~/components/data-table-filters/data-table-filters';
+import { Grid, Paper } from '@material-ui/core';
+import { WorkflowDetailsCard } from './workflow-description-card';
+import { WorkflowResource } from '../../models/workflow';
+
+export enum WorkflowPanelColumnNames {
+    NAME = "Name",
+    AUTHORISATION = "Authorisation",
+    LAST_MODIFIED = "Last modified",
+    SHARE = 'Share'
+}
+
+export interface WorkflowPanelFilter extends DataTableFilterItem {
+    type: ResourceStatus;
+}
+
+export interface WorkflowPanelDataProps {
+    workflow?: WorkflowResource;
+}
+
+export interface WorfklowPanelActionProps {
+    handleRowDoubleClick: (workflowUuid: string) => void;
+    handleRowClick: (workflowUuid: string) => void;
+}
+
+export type WorkflowPanelProps = WorkflowPanelDataProps & WorfklowPanelActionProps;
+
+export enum ResourceStatus {
+    PUBLIC = "Public",
+    PRIVATE = "Private",
+    SHARED = "Shared"
+}
+
+const resourceStatus = (type: string) => {
+    switch (type) {
+        case ResourceStatus.PUBLIC:
+            return "Public";
+        case ResourceStatus.PRIVATE:
+            return "Private";
+        case ResourceStatus.SHARED:
+            return "Shared";
+        default:
+            return "Unknown";
+    }
+};
+
+export const workflowPanelColumns: DataColumns<string, WorkflowPanelFilter> = [
+    {
+        name: WorkflowPanelColumnNames.NAME,
+        selected: true,
+        configurable: true,
+        sortDirection: SortDirection.ASC,
+        filters: [],
+        render: (uuid: string) => <RosurceWorkflowName uuid={uuid} />
+    },
+    {
+        name: WorkflowPanelColumnNames.AUTHORISATION,
+        selected: true,
+        configurable: true,
+        filters: [
+            {
+                name: resourceStatus(ResourceStatus.PUBLIC),
+                selected: true,
+                type: ResourceStatus.PUBLIC
+            },
+            {
+                name: resourceStatus(ResourceStatus.PRIVATE),
+                selected: true,
+                type: ResourceStatus.PRIVATE
+            },
+            {
+                name: resourceStatus(ResourceStatus.SHARED),
+                selected: true,
+                type: ResourceStatus.SHARED
+            }
+        ],
+        render: (uuid: string) => <ResourceWorkflowStatus uuid={uuid} />,
+    },
+    {
+        name: WorkflowPanelColumnNames.LAST_MODIFIED,
+        selected: true,
+        configurable: true,
+        sortDirection: SortDirection.NONE,
+        filters: [],
+        render: (uuid: string) => <ResourceLastModifiedDate uuid={uuid} />
+    },
+    {
+        name: '',
+        selected: true,
+        configurable: false,
+        filters: [],
+        render: (uuid: string) => <ResourceShare uuid={uuid} />
+    }
+];
+
+export const WorkflowPanelView = (props: WorkflowPanelProps) => {
+    return <Grid container spacing={16}>
+        <Grid item xs={6}>
+            <DataExplorer
+                id={WORKFLOW_PANEL_ID}
+                onRowClick={props.handleRowClick}
+                onRowDoubleClick={props.handleRowDoubleClick}
+                contextMenuColumn={false}
+                dataTableDefaultView={<DataTableDefaultView icon={WorkflowIcon} />} />
+        </Grid>
+        <Grid item xs={6}>
+            <Paper>
+                <WorkflowDetailsCard workflow={props.workflow} />
+            </Paper>
+        </Grid>
+    </Grid>;
+};
\ No newline at end of file
diff --git a/src/views/workflow-panel/workflow-panel.tsx b/src/views/workflow-panel/workflow-panel.tsx
new file mode 100644 (file)
index 0000000..0dbf918
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from "redux";
+import { connect } from "react-redux";
+import { navigateTo } from '~/store/navigation/navigation-action';
+import { WorkflowPanelView } from '~/views/workflow-panel/workflow-panel-view';
+import { WorfklowPanelActionProps, WorkflowPanelDataProps } from './workflow-panel-view';
+import { showWorkflowDetails, getWorkflowDetails } from '../../store/workflow-panel/workflow-panel-actions';
+import { RootState } from '~/store/store';
+
+const mapStateToProps = (state: RootState): WorkflowPanelDataProps => ({ 
+    workflow: getWorkflowDetails(state)
+});
+
+const mapDispatchToProps = (dispatch: Dispatch): WorfklowPanelActionProps => ({
+
+    handleRowDoubleClick: (uuid: string) => {
+        dispatch<any>(navigateTo(uuid));
+    },
+
+    handleRowClick: (uuid: string) => {
+        dispatch(showWorkflowDetails(uuid));
+    }
+});
+
+export const WorkflowPanel = connect(mapStateToProps, mapDispatchToProps)(WorkflowPanelView);
\ No newline at end of file
index da8e4415d1f6c2d863eb47cb8586ab9214a5ba32..b9f1cc627770e4061f08cccbf10db29e40fefbac 100644 (file)
@@ -12,4 +12,5 @@ declare interface System {
 }
 declare var System: System;
 
-declare module 'react-splitter-layout';
\ No newline at end of file
+declare module 'react-splitter-layout';
+declare module 'react-rte';
\ No newline at end of file
index c8148433ef7250b47fb7b54896da373b806b40aa..32c9d0e8caae560dd4d975b5ae9305e12e1f6506 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
@@ -3,37 +3,30 @@
 
 
 "@babel/code-frame@^7.0.0-beta.35":
-  version "7.0.0-beta.54"
-  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.54.tgz#0024f96fdf7028a21d68e273afd4e953214a1ead"
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8"
   dependencies:
-    "@babel/highlight" "7.0.0-beta.54"
+    "@babel/highlight" "^7.0.0"
 
-"@babel/highlight@7.0.0-beta.54":
-  version "7.0.0-beta.54"
-  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.54.tgz#155d507358329b8e7068970017c3fd74a9b08584"
+"@babel/highlight@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4"
   dependencies:
     chalk "^2.0.0"
     esutils "^2.0.2"
-    js-tokens "^3.0.0"
+    js-tokens "^4.0.0"
 
-"@babel/runtime@7.0.0-beta.42":
-  version "7.0.0-beta.42"
-  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.42.tgz#352e40c92e0460d3e82f49bd7e79f6cda76f919f"
-  dependencies:
-    core-js "^2.5.3"
-    regenerator-runtime "^0.11.1"
-
-"@babel/runtime@7.0.0-beta.56":
-  version "7.0.0-beta.56"
-  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.56.tgz#cda612dffd5b1719a7b8e91e3040bd6ae64de8b0"
+"@babel/runtime@7.0.0", "@babel/runtime@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0.tgz#adeb78fedfc855aa05bc041640f3f6f98e85424c"
   dependencies:
     regenerator-runtime "^0.12.0"
 
-"@material-ui/core@1.5.0":
-  version "1.5.0"
-  resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-1.5.0.tgz#00884bb4139d98786d05a97803d19426d4afa55d"
+"@material-ui/core@3.1.1":
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-3.1.1.tgz#377b406fd26b88df6626c1697eef3f488fddb553"
   dependencies:
-    "@babel/runtime" "7.0.0-beta.42"
+    "@babel/runtime" "7.0.0"
     "@types/jss" "^9.5.3"
     "@types/react-transition-group" "^2.0.8"
     brcast "^3.0.1"
     react-event-listener "^0.6.2"
     react-jss "^8.1.0"
     react-transition-group "^2.2.1"
-    recompose "^0.28.0"
+    recompose "0.28.0 - 0.30.0"
     warning "^4.0.1"
 
-"@material-ui/icons@2.0.2":
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-2.0.2.tgz#0150c38cda089ef284e9b4a730dfe6e88a0b5de6"
+"@material-ui/icons@3.0.1":
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-3.0.1.tgz#671fb3d04dcaf9351dbbd2bf82ae2ae72e3d93cd"
   dependencies:
-    "@babel/runtime" "7.0.0-beta.42"
-    recompose "^0.28.0"
+    "@babel/runtime" "7.0.0"
+    recompose "^0.29.0"
 
 "@types/cheerio@*":
-  version "0.22.8"
-  resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.8.tgz#5702f74f78b73e13f1eb1bd435c2c9de61a250d4"
+  version "0.22.9"
+  resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.9.tgz#b5990152604c2ada749b7f88cab3476f21f39d7b"
 
-"@types/classnames@^2.2.4":
+"@types/classnames@2.2.6":
   version "2.2.6"
   resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.6.tgz#dbe8a666156d556ed018e15a4c65f08937c3f628"
 
   dependencies:
     "@types/enzyme" "*"
 
-"@types/enzyme@*", "@types/enzyme@3.1.13":
-  version "3.1.13"
-  resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.1.13.tgz#4bbc5c81fa40c9fc7efee25c4a23cb37119a33ea"
+"@types/enzyme@*", "@types/enzyme@3.1.14":
+  version "3.1.14"
+  resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.1.14.tgz#379c26205f6e0e272f3a51d6bbdd50071a9d03a6"
   dependencies:
     "@types/cheerio" "*"
     "@types/react" "*"
 
 "@types/history@*":
-  version "4.6.2"
-  resolved "https://registry.yarnpkg.com/@types/history/-/history-4.6.2.tgz#12cfaba693ba20f114ed5765467ff25fdf67ddb0"
+  version "4.7.0"
+  resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.0.tgz#2fac51050c68f7d6f96c5aafc631132522f4aa3f"
 
-"@types/jest@23.3.1":
-  version "23.3.1"
-  resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.1.tgz#a4319aedb071d478e6f407d1c4578ec8156829cf"
+"@types/jest@23.3.2":
+  version "23.3.2"
+  resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.2.tgz#07b90f6adf75d42c34230c026a2529e56c249dbb"
+
+"@types/json5@^0.0.29":
+  version "0.0.29"
+  resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
+
+"@types/js-yaml@3.11.2":
+  version "3.11.2"
+  resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.11.2.tgz#699ad86054cc20043c30d66a6fcde30bbf5d3d5e"
 
 "@types/jss@^9.5.3":
-  version "9.5.4"
-  resolved "https://registry.yarnpkg.com/@types/jss/-/jss-9.5.4.tgz#89a4ee32a14a8d2937187b1a7f443750e964a74d"
+  version "9.5.6"
+  resolved "https://registry.yarnpkg.com/@types/jss/-/jss-9.5.6.tgz#96e1d246ddfbccc4867494077c714773cf29acde"
   dependencies:
     csstype "^2.0.0"
     indefinite-observable "^1.0.1"
   version "4.14.116"
   resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.116.tgz#5ccf215653e3e8c786a58390751033a9adca0eb9"
 
-"@types/node@*", "@types/node@10.7.1":
-  version "10.7.1"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-10.7.1.tgz#b704d7c259aa40ee052eec678758a68d07132a2e"
+"@types/node@*", "@types/node@10.11.2":
+  version "10.11.2"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-10.11.2.tgz#59508b88ce90fe2742f7b8414c6f5db3e359570d"
+
+"@types/prop-types@*":
+  version "15.5.6"
+  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.6.tgz#9c03d3fed70a8d517c191b7734da2879b50ca26c"
 
 "@types/react-copy-to-clipboard@4.2.6":
   version "4.2.6"
   dependencies:
     "@types/react" "*"
 
-"@types/react-dom@16.0.7":
-  version "16.0.7"
-  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.7.tgz#54d0f867a76b90597e8432030d297982f25c20ba"
+"@types/react-dom@16.0.8":
+  version "16.0.8"
+  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.8.tgz#6e1366ed629cadf55860cbfcc25db533f5d2fa7d"
   dependencies:
     "@types/node" "*"
     "@types/react" "*"
   dependencies:
     "@types/react" "*"
 
-"@types/react-redux@6.0.6":
-  version "6.0.6"
-  resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-6.0.6.tgz#87f1d0a6ea901b93fcaf95fa57641ff64079d277"
+"@types/react-redux@6.0.9":
+  version "6.0.9"
+  resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-6.0.9.tgz#96aa7f5b0716bcc3bfb36ceaa1223118d509f79a"
   dependencies:
     "@types/react" "*"
     redux "^4.0.0"
 
-"@types/react-router-dom@4.3.0":
-  version "4.3.0"
-  resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.3.0.tgz#c91796d02deb3a5b24bc1c5db4a255df0d18b8b5"
+"@types/react-router-dom@4.3.1":
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-4.3.1.tgz#71fe2918f8f60474a891520def40a63997dafe04"
   dependencies:
     "@types/history" "*"
     "@types/react" "*"
     "@types/react-router" "*"
 
-"@types/react-router-redux@5.0.15":
-  version "5.0.15"
-  resolved "https://registry.yarnpkg.com/@types/react-router-redux/-/react-router-redux-5.0.15.tgz#aebc593bd3426adb6ae2eba6ac8c919ee232ce7b"
+"@types/react-router-redux@5.0.16":
+  version "5.0.16"
+  resolved "https://registry.yarnpkg.com/@types/react-router-redux/-/react-router-redux-5.0.16.tgz#3db2a8f0410fe9d821e6ace87b774c8bdecb39ca"
   dependencies:
     "@types/history" "*"
     "@types/react" "*"
     "@types/react-router" "*"
     redux ">= 3.7.2"
 
-"@types/react-router@*", "@types/react-router@4.0.29":
-  version "4.0.29"
-  resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-4.0.29.tgz#1a906dd99abf21297a5b7cf003d1fd36e7a92069"
+"@types/react-router@*", "@types/react-router@4.0.31":
+  version "4.0.31"
+  resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-4.0.31.tgz#416bac49d746800810886c7b8582a622ed9604fc"
   dependencies:
     "@types/history" "*"
     "@types/react" "*"
 
 "@types/react-transition-group@^2.0.8":
-  version "2.0.11"
-  resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-2.0.11.tgz#feb274676a39383fffaa0dff710958d2251abefb"
+  version "2.0.14"
+  resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-2.0.14.tgz#afd0cd785a97f070b55765e9f9d76ff568269001"
   dependencies:
     "@types/react" "*"
 
 "@types/react@*", "@types/react@16.4":
-  version "16.4.7"
-  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.4.7.tgz#f33f6d759a7e1833befa15224d68942d178a5a3f"
+  version "16.4.14"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.4.14.tgz#47c604c8e46ed674bbdf4aabf82b34b9041c6a04"
   dependencies:
+    "@types/prop-types" "*"
     csstype "^2.2.0"
 
 "@types/redux-devtools@3.0.44":
   dependencies:
     "@types/node" "*"
 
-abab@^1.0.4:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e"
+abab@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f"
 
 abbrev@1:
   version "1.1.1"
@@ -220,18 +226,27 @@ acorn-dynamic-import@^2.0.0:
     acorn "^4.0.3"
 
 acorn-globals@^4.1.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.1.0.tgz#ab716025dbe17c54d3ef81d32ece2b2d99fe2538"
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.0.tgz#e3b6f8da3c1552a95ae627571f7dd6923bb54103"
   dependencies:
-    acorn "^5.0.0"
+    acorn "^6.0.1"
+    acorn-walk "^6.0.1"
+
+acorn-walk@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.0.1.tgz#c7827bdbb8e21aa97b609adfa225400d9ae348ba"
 
 acorn@^4.0.3:
   version "4.0.13"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
 
-acorn@^5.0.0, acorn@^5.3.0:
-  version "5.7.1"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8"
+acorn@^5.0.0, acorn@^5.5.3:
+  version "5.7.3"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279"
+
+acorn@^6.0.1:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.2.tgz#6a459041c320ab17592c6317abbfdf4bbaa98ca4"
 
 address@1.0.3, address@^1.0.1:
   version "1.0.3"
@@ -245,7 +260,7 @@ ajv-keywords@^3.1.0:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a"
 
-ajv@^5.0.0, ajv@^5.1.0, ajv@^5.1.5:
+ajv@^5.0.0, ajv@^5.1.5, ajv@^5.3.0:
   version "5.5.2"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
   dependencies:
@@ -255,13 +270,13 @@ ajv@^5.0.0, ajv@^5.1.0, ajv@^5.1.5:
     json-schema-traverse "^0.3.0"
 
 ajv@^6.1.0:
-  version "6.5.2"
-  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.2.tgz#678495f9b82f7cca6be248dd92f59bff5e1f4360"
+  version "6.5.4"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.4.tgz#247d5274110db653706b550fcc2b797ca28cfc59"
   dependencies:
     fast-deep-equal "^2.0.1"
     fast-json-stable-stringify "^2.0.0"
     json-schema-traverse "^0.4.1"
-    uri-js "^4.2.1"
+    uri-js "^4.2.2"
 
 align-text@^0.1.1, align-text@^0.1.3:
   version "0.1.4"
@@ -275,10 +290,6 @@ alphanum-sort@^1.0.1, alphanum-sort@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
 
-amdefine@>=0.0.4:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
-
 ansi-align@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f"
@@ -325,11 +336,11 @@ anymatch@^2.0.0:
     micromatch "^3.1.4"
     normalize-path "^2.1.1"
 
-append-transform@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab"
+append-transform@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991"
   dependencies:
-    default-require-extensions "^2.0.0"
+    default-require-extensions "^1.0.0"
 
 aproba@^1.0.3, aproba@^1.1.1:
   version "1.2.0"
@@ -444,8 +455,10 @@ asn1.js@^4.0.0:
     minimalistic-assert "^1.0.0"
 
 asn1@~0.2.3:
-  version "0.2.3"
-  resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
+  version "0.2.4"
+  resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
+  dependencies:
+    safer-buffer "~2.1.0"
 
 assert-plus@1.0.0, assert-plus@^1.0.0:
   version "1.0.0"
@@ -473,7 +486,7 @@ async-limiter@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
 
-async@^1.4.0, async@^1.5.2:
+async@^1.5.2:
   version "1.5.2"
   resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
 
@@ -488,8 +501,8 @@ asynckit@^0.4.0:
   resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
 
 atob@^2.1.1:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a"
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
 
 attr-accept@^1.1.3:
   version "1.1.3"
@@ -523,9 +536,9 @@ aws-sign2@~0.7.0:
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
 
-aws4@^1.6.0:
-  version "1.7.0"
-  resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289"
+aws4@^1.8.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
 
 axios-mock-adapter@1.15.0:
   version "1.15.0"
@@ -1123,7 +1136,7 @@ babel-register@^6.26.0:
     mkdirp "^0.5.1"
     source-map-support "^0.4.15"
 
-babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0, babel-runtime@^6.9.2:
+babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0, babel-runtime@^6.9.2:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
   dependencies:
@@ -1206,12 +1219,12 @@ big.js@^3.1.3:
   resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e"
 
 binary-extensions@^1.0.0:
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205"
+  version "1.12.0"
+  resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14"
 
 bluebird@^3.4.7, bluebird@^3.5.1:
-  version "3.5.1"
-  resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
+  version "3.5.2"
+  resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.2.tgz#1be0908e054a751754549c270489c1505d4ab15a"
 
 bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
   version "4.11.8"
@@ -1298,8 +1311,8 @@ brorand@^1.0.1:
   resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
 
 browser-process-hrtime@^0.1.2:
-  version "0.1.2"
-  resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz#425d68a58d3447f02a04aa894187fce8af8b7b8e"
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4"
 
 browser-resolve@^1.11.2:
   version "1.11.3"
@@ -1381,8 +1394,8 @@ bser@^2.0.0:
     node-int64 "^0.4.0"
 
 buffer-from@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04"
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
 
 buffer-indexof@^1.0.0:
   version "1.1.1"
@@ -1488,12 +1501,12 @@ caniuse-api@^1.5.2:
     lodash.uniq "^4.5.0"
 
 caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
-  version "1.0.30000870"
-  resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000870.tgz#f397cd64922c24f85d0ce7803c9bd5c5a1571b16"
+  version "1.0.30000887"
+  resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000887.tgz#9abf538610e3349870ed525f7062de649cc3c570"
 
 caniuse-lite@^1.0.30000748, caniuse-lite@^1.0.30000792:
-  version "1.0.30000865"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000865.tgz#70026616e8afe6e1442f8bb4e1092987d81a2f25"
+  version "1.0.30000887"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000887.tgz#1769458c27bbdcf61b0cb6b5072bb6cd11fd9c23"
 
 capture-exit@^1.2.0:
   version "1.2.0"
@@ -1502,8 +1515,8 @@ capture-exit@^1.2.0:
     rsvp "^3.3.3"
 
 capture-stack-trace@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d"
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d"
 
 case-sensitive-paths-webpack-plugin@2.1.1:
   version "2.1.1"
@@ -1592,12 +1605,12 @@ chokidar@^2.0.2:
     fsevents "^1.2.2"
 
 chownr@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181"
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494"
 
-ci-info@^1.0.0:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.3.tgz#710193264bb05c77b8c90d02f5aaf22216a667b2"
+ci-info@^1.5.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497"
 
 cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
   version "1.0.4"
@@ -1612,6 +1625,10 @@ clap@^1.0.9:
   dependencies:
     chalk "^1.1.3"
 
+class-autobind@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/class-autobind/-/class-autobind-0.1.4.tgz#34516c49167cf8d3f639ddc186bcfa2268afff34"
+
 class-utils@^0.3.5:
   version "0.3.6"
   resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
@@ -1625,11 +1642,11 @@ classnames@2.2.6, classnames@^2.2.5:
   version "2.2.6"
   resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
 
-clean-css@4.1.x:
-  version "4.1.11"
-  resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.11.tgz#2ecdf145aba38f54740f26cefd0ff3e03e125d6a"
+clean-css@4.2.x:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17"
   dependencies:
-    source-map "0.5.x"
+    source-map "~0.6.0"
 
 cli-boxes@^1.0.0:
   version "1.0.0"
@@ -1695,14 +1712,14 @@ collection-visit@^1.0.0:
     object-visit "^1.0.0"
 
 color-convert@^1.3.0, color-convert@^1.9.0:
-  version "1.9.2"
-  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147"
+  version "1.9.3"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
   dependencies:
-    color-name "1.1.1"
+    color-name "1.1.3"
 
-color-name@1.1.1, color-name@^1.0.0:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689"
+color-name@1.1.3, color-name@^1.0.0:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
 
 color-string@^0.3.0:
   version "0.3.0"
@@ -1734,15 +1751,25 @@ colors@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
 
-combined-stream@1.0.6, combined-stream@~1.0.5:
+combined-stream@1.0.6:
   version "1.0.6"
   resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818"
   dependencies:
     delayed-stream "~1.0.0"
 
-commander@2.16.x, commander@^2.12.1, commander@~2.16.0:
-  version "2.16.0"
-  resolved "https://registry.yarnpkg.com/commander/-/commander-2.16.0.tgz#f16390593996ceb4f3eeb020b31d78528f7f8a50"
+combined-stream@~1.0.6:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828"
+  dependencies:
+    delayed-stream "~1.0.0"
+
+commander@2.17.x, commander@~2.17.1:
+  version "2.17.1"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
+
+commander@^2.12.1:
+  version "2.18.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970"
 
 commander@~2.13.0:
   version "2.13.0"
@@ -1752,19 +1779,15 @@ commondir@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
 
-compare-versions@^3.1.0:
-  version "3.3.0"
-  resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.3.0.tgz#af93ea705a96943f622ab309578b9b90586f39c3"
-
 component-emitter@^1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
 
 compressible@~2.0.14:
-  version "2.0.14"
-  resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.14.tgz#326c5f507fbb055f54116782b969a81b67a29da7"
+  version "2.0.15"
+  resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.15.tgz#857a9ab0a7e5a07d8d837ed43fe2defff64fe212"
   dependencies:
-    mime-db ">= 1.34.0 < 2"
+    mime-db ">= 1.36.0 < 2"
 
 compression@^1.5.2:
   version "1.7.3"
@@ -1829,8 +1852,10 @@ content-type@~1.0.4:
   resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
 
 convert-source-map@^1.4.0, convert-source-map@^1.5.1:
-  version "1.5.1"
-  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20"
+  dependencies:
+    safe-buffer "~5.1.1"
 
 cookie-signature@1.0.6:
   version "1.0.6"
@@ -1865,7 +1890,7 @@ core-js@^1.0.0:
   version "1.2.7"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
 
-core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.3:
+core-js@^2.4.0, core-js@^2.5.0:
   version "2.5.7"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
 
@@ -2065,15 +2090,15 @@ cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
   version "0.3.4"
   resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.4.tgz#8cd52e8a3acfd68d3aed38ee0a640177d2f9d797"
 
-"cssstyle@>= 0.3.1 < 0.4.0":
-  version "0.3.1"
-  resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.3.1.tgz#6da9b4cff1bc5d716e6e5fe8e04fcb1b50a49adf"
+cssstyle@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.1.1.tgz#18b038a9c44d65f7a8e428a653b9f6fe42faf5fb"
   dependencies:
     cssom "0.3.x"
 
 csstype@^2.0.0, csstype@^2.2.0, csstype@^2.5.2:
-  version "2.5.6"
-  resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.5.6.tgz#2ae1db2319642d8b80a668d2d025c6196071e788"
+  version "2.5.7"
+  resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.5.7.tgz#bf9235d5872141eccfb2d16d82993c6b149179ff"
 
 currently-unhandled@^0.4.1:
   version "0.4.1"
@@ -2098,20 +2123,20 @@ dashdash@^1.12.0:
     assert-plus "^1.0.0"
 
 data-urls@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.0.0.tgz#24802de4e81c298ea8a9388bb0d8e461c774684f"
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.0.1.tgz#d416ac3896918f29ca84d81085bc3705834da579"
   dependencies:
-    abab "^1.0.4"
-    whatwg-mimetype "^2.0.0"
-    whatwg-url "^6.4.0"
+    abab "^2.0.0"
+    whatwg-mimetype "^2.1.0"
+    whatwg-url "^7.0.0"
 
 date-now@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
 
 debounce@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.1.0.tgz#6a1a4ee2a9dc4b7c24bb012558dbcdb05b37f408"
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131"
 
 debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9:
   version "2.6.9"
@@ -2119,7 +2144,7 @@ debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.
   dependencies:
     ms "2.0.0"
 
-debug@^3.1.0:
+debug@=3.1.0, debug@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
   dependencies:
@@ -2149,18 +2174,17 @@ deepmerge@^2.0.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.1.1.tgz#e862b4e45ea0555072bf51e7fd0d9845170ae768"
 
-default-require-extensions@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-2.0.0.tgz#f5f8fbb18a7d6d50b21f641f649ebb522cfe24f7"
+default-require-extensions@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8"
   dependencies:
-    strip-bom "^3.0.0"
+    strip-bom "^2.0.0"
 
 define-properties@^1.1.2:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94"
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
   dependencies:
-    foreach "^2.0.5"
-    object-keys "^1.0.8"
+    object-keys "^1.0.12"
 
 define-property@^0.2.5:
   version "0.2.5"
@@ -2250,8 +2274,8 @@ detect-newline@^2.1.0:
   resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2"
 
 detect-node@^2.0.3:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127"
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
 
 detect-port-alt@1.1.6:
   version "1.1.6"
@@ -2328,7 +2352,7 @@ domelementtype@~1.1.1:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
 
-domexception@^1.0.0:
+domexception@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
   dependencies:
@@ -2373,6 +2397,50 @@ dotenv@4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d"
 
+draft-js-export-html@>=0.6.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/draft-js-export-html/-/draft-js-export-html-1.2.0.tgz#1cbe2b78e1fed74fc29c7cdcbfd7540468eca209"
+  dependencies:
+    draft-js-utils "^1.2.0"
+
+draft-js-export-markdown@>=0.3.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/draft-js-export-markdown/-/draft-js-export-markdown-1.2.0.tgz#c423d67389e1c70ddd13e956afe82fd1b72feea5"
+  dependencies:
+    draft-js-utils "^1.2.0"
+
+draft-js-import-element@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/draft-js-import-element/-/draft-js-import-element-1.2.1.tgz#9a6a56d74690d48d35d8d089564e6d710b4926eb"
+  dependencies:
+    draft-js-utils "^1.2.0"
+    synthetic-dom "^1.2.0"
+
+draft-js-import-html@>=0.4.0:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/draft-js-import-html/-/draft-js-import-html-1.2.1.tgz#88adb8ce5dbe1a5a777663b1893cee6a35239eaa"
+  dependencies:
+    draft-js-import-element "^1.2.1"
+
+draft-js-import-markdown@>=0.3.0:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/draft-js-import-markdown/-/draft-js-import-markdown-1.2.1.tgz#ec18eb15008bab13d9878d65db50e181dd64a5ce"
+  dependencies:
+    draft-js-import-element "^1.2.1"
+    synthetic-dom "^1.2.0"
+
+draft-js-utils@>=0.2.0, draft-js-utils@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/draft-js-utils/-/draft-js-utils-1.2.0.tgz#f5cb23eb167325ffed3d79882fdc317721d2fd12"
+
+draft-js@>=0.10.0:
+  version "0.10.5"
+  resolved "https://registry.yarnpkg.com/draft-js/-/draft-js-0.10.5.tgz#bfa9beb018fe0533dbb08d6675c371a6b08fa742"
+  dependencies:
+    fbjs "^0.8.15"
+    immutable "~3.7.4"
+    object-assign "^4.1.0"
+
 duplexer3@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
@@ -2391,22 +2459,23 @@ duplexify@^3.4.2, duplexify@^3.6.0:
     stream-shift "^1.0.0"
 
 ecc-jsbn@~0.1.1:
-  version "0.1.1"
-  resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
   dependencies:
     jsbn "~0.1.0"
+    safer-buffer "^2.1.0"
 
 ee-first@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
 
 electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.30:
-  version "1.3.52"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.52.tgz#d2d9f1270ba4a3b967b831c40ef71fb4d9ab5ce0"
+  version "1.3.71"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.71.tgz#baecb282e8b27247bbfcf2f3e0254d6fe9a76789"
 
 elliptic@^6.0.0:
-  version "6.4.0"
-  resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df"
+  version "6.4.1"
+  resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.1.tgz#c2d0b7776911b86722c632c3c06c60f2f819939a"
   dependencies:
     bn.js "^4.4.0"
     brorand "^1.0.1"
@@ -2449,30 +2518,29 @@ entities@^1.1.1, entities@~1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
 
-enzyme-adapter-react-16@1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.2.0.tgz#c6e80f334e0a817873262d7d01ee9e4747e3c97e"
+enzyme-adapter-react-16@1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.5.0.tgz#50af8d76a45fe0915de932bd95d34cdca75c0be3"
   dependencies:
-    enzyme-adapter-utils "^1.5.0"
+    enzyme-adapter-utils "^1.8.0"
     function.prototype.name "^1.1.0"
     object.assign "^4.1.0"
     object.values "^1.0.4"
     prop-types "^15.6.2"
     react-is "^16.4.2"
-    react-reconciler "^0.7.0"
     react-test-renderer "^16.0.0-0"
 
-enzyme-adapter-utils@^1.5.0:
-  version "1.5.0"
-  resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.5.0.tgz#a020ab3ae79bb1c85e1d51f48f35e995e0eed810"
+enzyme-adapter-utils@^1.8.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.8.0.tgz#ee9f07250663a985f1f2caaf297720787da559f1"
   dependencies:
     function.prototype.name "^1.1.0"
     object.assign "^4.1.0"
     prop-types "^15.6.2"
 
-enzyme@3.4.4:
-  version "3.4.4"
-  resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.4.4.tgz#92c7c6b9e59d4ef0c3d36a75dccc0e41a5c14d21"
+enzyme@3.6.0:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.6.0.tgz#d213f280a258f61e901bc663d4cc2d6fd9a9dec8"
   dependencies:
     array.prototype.flat "^1.2.1"
     cheerio "^1.0.0-rc.2"
@@ -2483,7 +2551,8 @@ enzyme@3.4.4:
     is-number-object "^1.0.3"
     is-string "^1.0.4"
     is-subset "^0.1.1"
-    lodash "^4.17.4"
+    lodash.escape "^4.0.1"
+    lodash.isequal "^4.5.0"
     object-inspect "^1.6.0"
     object-is "^1.0.1"
     object.assign "^4.1.0"
@@ -2491,6 +2560,7 @@ enzyme@3.4.4:
     object.values "^1.0.4"
     raf "^3.4.0"
     rst-selector-parser "^2.2.3"
+    string.prototype.trim "^1.1.2"
 
 errno@^0.1.3, errno@~0.1.7:
   version "0.1.7"
@@ -2504,7 +2574,7 @@ error-ex@^1.2.0:
   dependencies:
     is-arrayish "^0.2.1"
 
-es-abstract@^1.10.0, es-abstract@^1.5.1, es-abstract@^1.6.1, es-abstract@^1.7.0:
+es-abstract@^1.10.0, es-abstract@^1.5.0, es-abstract@^1.5.1, es-abstract@^1.6.1, es-abstract@^1.7.0:
   version "1.12.0"
   resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165"
   dependencies:
@@ -2515,16 +2585,16 @@ es-abstract@^1.10.0, es-abstract@^1.5.1, es-abstract@^1.6.1, es-abstract@^1.7.0:
     is-regex "^1.0.4"
 
 es-to-primitive@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d"
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377"
   dependencies:
-    is-callable "^1.1.1"
+    is-callable "^1.1.4"
     is-date-object "^1.0.1"
-    is-symbol "^1.0.1"
+    is-symbol "^1.0.2"
 
 es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14:
-  version "0.10.45"
-  resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.45.tgz#0bfdf7b473da5919d5adf3bd25ceb754fccc3653"
+  version "0.10.46"
+  resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.46.tgz#efd99f67c5a7ec789baa3daa7f79870388f7f572"
   dependencies:
     es6-iterator "~2.0.3"
     es6-symbol "~3.1.1"
@@ -2554,8 +2624,8 @@ es6-map@^0.1.3:
     event-emitter "~0.3.5"
 
 es6-promise@^4.0.5:
-  version "4.2.4"
-  resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29"
+  version "4.2.5"
+  resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.5.tgz#da6d0d5692efb461e082c14817fe2427d8f5d054"
 
 es6-set@~0.1.5:
   version "0.1.5"
@@ -2591,7 +2661,7 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
 
-escodegen@^1.9.0:
+escodegen@^1.9.1:
   version "1.11.0"
   resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.0.tgz#b27a9389481d5bfd5bec76f7bb1eb3f8f4556589"
   dependencies:
@@ -2780,7 +2850,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
     assign-symbols "^1.0.0"
     is-extendable "^1.0.1"
 
-extend@~3.0.1:
+extend@~3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
 
@@ -2862,7 +2932,7 @@ fb-watchman@^2.0.0:
   dependencies:
     bser "^2.0.0"
 
-fbjs@^0.8.1, fbjs@^0.8.16:
+fbjs@^0.8.1, fbjs@^0.8.15:
   version "0.8.17"
   resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
   dependencies:
@@ -2969,10 +3039,10 @@ flush-write-stream@^1.0.0:
     readable-stream "^2.0.4"
 
 follow-redirects@^1.0.0, follow-redirects@^1.3.0:
-  version "1.5.1"
-  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.1.tgz#67a8f14f5a1f67f962c2c46469c79eaec0a90291"
+  version "1.5.8"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.8.tgz#1dbfe13e45ad969f813e86c00e5296f525c885a1"
   dependencies:
-    debug "^3.1.0"
+    debug "=3.1.0"
 
 for-in@^1.0.1, for-in@^1.0.2:
   version "1.0.2"
@@ -2984,10 +3054,6 @@ for-own@^0.1.4:
   dependencies:
     for-in "^1.0.1"
 
-foreach@^2.0.5:
-  version "2.0.5"
-  resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
-
 forever-agent@~0.6.1:
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
@@ -3005,7 +3071,7 @@ fork-ts-checker-webpack-plugin@^0.2.8:
     lodash.startswith "^4.2.1"
     minimatch "^3.0.4"
 
-form-data@~2.3.1:
+form-data@~2.3.2:
   version "2.3.2"
   resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099"
   dependencies:
@@ -3086,7 +3152,7 @@ fsevents@^1.0.0, fsevents@^1.1.3, fsevents@^1.2.2, fsevents@^1.2.3:
     nan "^2.9.2"
     node-pre-gyp "^0.10.0"
 
-function-bind@^1.1.0, function-bind@^1.1.1:
+function-bind@^1.0.2, function-bind@^1.1.0, function-bind@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
 
@@ -3160,8 +3226,8 @@ glob2base@^0.0.12:
     find-index "^0.1.1"
 
 glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2:
-  version "7.1.2"
-  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
+  version "7.1.3"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
   dependencies:
     fs.realpath "^1.0.0"
     inflight "^1.0.4"
@@ -3254,24 +3320,24 @@ handle-thing@^1.2.5:
   resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4"
 
 handlebars@^4.0.3:
-  version "4.0.11"
-  resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc"
+  version "4.0.12"
+  resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.12.tgz#2c15c8a96d46da5e266700518ba8cb8d919d5bc5"
   dependencies:
-    async "^1.4.0"
+    async "^2.5.0"
     optimist "^0.6.1"
-    source-map "^0.4.4"
+    source-map "^0.6.1"
   optionalDependencies:
-    uglify-js "^2.6"
+    uglify-js "^3.1.4"
 
 har-schema@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
 
-har-validator@~5.0.3:
-  version "5.0.3"
-  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd"
+har-validator@~5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.0.tgz#44657f5688a22cfd4b72486e81b3a3fb11742c29"
   dependencies:
-    ajv "^5.1.0"
+    ajv "^5.3.0"
     har-schema "^2.0.0"
 
 has-ansi@^2.0.0:
@@ -3414,12 +3480,12 @@ html-entities@^1.2.0:
   resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f"
 
 html-minifier@^3.2.3:
-  version "3.5.19"
-  resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.19.tgz#ed53c4b7326fe507bc3a1adbcc3bbb56660a2ebd"
+  version "3.5.20"
+  resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.20.tgz#7b19fd3caa0cb79f7cde5ee5c3abdf8ecaa6bb14"
   dependencies:
     camel-case "3.0.x"
-    clean-css "4.1.x"
-    commander "2.16.x"
+    clean-css "4.2.x"
+    commander "2.17.x"
     he "1.1.x"
     param-case "2.1.x"
     relateurl "0.2.x"
@@ -3519,12 +3585,18 @@ iconv-lite@0.4.19:
   version "0.4.19"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
 
-iconv-lite@^0.4.17, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
+iconv-lite@0.4.23:
   version "0.4.23"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
   dependencies:
     safer-buffer ">= 2.1.2 < 3"
 
+iconv-lite@^0.4.17, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
+  version "0.4.24"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3"
+
 icss-replace-symbols@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
@@ -3549,6 +3621,14 @@ ignore-walk@^3.0.1:
   dependencies:
     minimatch "^3.0.4"
 
+immutable@^3.8.1:
+  version "3.8.2"
+  resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
+
+immutable@~3.7.4:
+  version "3.7.6"
+  resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b"
+
 import-lazy@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
@@ -3653,9 +3733,9 @@ ip@^1.1.0, ip@^1.1.5:
   version "1.1.5"
   resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
 
-ipaddr.js@1.6.0:
-  version "1.6.0"
-  resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b"
+ipaddr.js@1.8.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e"
 
 is-absolute-url@^2.0.0:
   version "2.1.0"
@@ -3697,15 +3777,15 @@ is-builtin-module@^1.0.0:
   dependencies:
     builtin-modules "^1.0.0"
 
-is-callable@^1.1.1, is-callable@^1.1.3, is-callable@^1.1.4:
+is-callable@^1.1.3, is-callable@^1.1.4:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
 
 is-ci@^1.0.10:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.1.0.tgz#247e4162e7860cebbdaf30b774d6b0ac7dcfe7a5"
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c"
   dependencies:
-    ci-info "^1.0.0"
+    ci-info "^1.5.0"
 
 is-data-descriptor@^0.1.4:
   version "0.1.4"
@@ -3926,9 +4006,11 @@ is-svg@^2.0.0:
   dependencies:
     html-comment-regex "^1.1.0"
 
-is-symbol@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572"
+is-symbol@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38"
+  dependencies:
+    has-symbols "^1.0.0"
 
 is-typedarray@~1.0.0:
   version "1.0.0"
@@ -3980,76 +4062,65 @@ isstream@~0.1.2:
   resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
 
 istanbul-api@^1.1.14:
-  version "1.3.1"
-  resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.3.1.tgz#4c3b05d18c0016d1022e079b98dc82c40f488954"
+  version "1.3.7"
+  resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.3.7.tgz#a86c770d2b03e11e3f778cd7aedd82d2722092aa"
   dependencies:
     async "^2.1.4"
-    compare-versions "^3.1.0"
     fileset "^2.0.2"
-    istanbul-lib-coverage "^1.2.0"
-    istanbul-lib-hook "^1.2.0"
-    istanbul-lib-instrument "^1.10.1"
-    istanbul-lib-report "^1.1.4"
-    istanbul-lib-source-maps "^1.2.4"
-    istanbul-reports "^1.3.0"
+    istanbul-lib-coverage "^1.2.1"
+    istanbul-lib-hook "^1.2.2"
+    istanbul-lib-instrument "^1.10.2"
+    istanbul-lib-report "^1.1.5"
+    istanbul-lib-source-maps "^1.2.6"
+    istanbul-reports "^1.5.1"
     js-yaml "^3.7.0"
     mkdirp "^0.5.1"
     once "^1.4.0"
 
-istanbul-lib-coverage@^1.1.1, istanbul-lib-coverage@^1.1.2, istanbul-lib-coverage@^1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz#f7d8f2e42b97e37fe796114cb0f9d68b5e3a4341"
-
-istanbul-lib-hook@^1.2.0:
+istanbul-lib-coverage@^1.1.1, istanbul-lib-coverage@^1.2.1:
   version "1.2.1"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.2.1.tgz#f614ec45287b2a8fc4f07f5660af787575601805"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz#ccf7edcd0a0bb9b8f729feeb0930470f9af664f0"
+
+istanbul-lib-hook@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.2.2.tgz#bc6bf07f12a641fbf1c85391d0daa8f0aea6bf86"
   dependencies:
-    append-transform "^1.0.0"
+    append-transform "^0.4.0"
 
-istanbul-lib-instrument@^1.10.1, istanbul-lib-instrument@^1.8.0:
-  version "1.10.1"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz#724b4b6caceba8692d3f1f9d0727e279c401af7b"
+istanbul-lib-instrument@^1.10.1, istanbul-lib-instrument@^1.10.2, istanbul-lib-instrument@^1.8.0:
+  version "1.10.2"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz#1f55ed10ac3c47f2bdddd5307935126754d0a9ca"
   dependencies:
     babel-generator "^6.18.0"
     babel-template "^6.16.0"
     babel-traverse "^6.18.0"
     babel-types "^6.18.0"
     babylon "^6.18.0"
-    istanbul-lib-coverage "^1.2.0"
+    istanbul-lib-coverage "^1.2.1"
     semver "^5.3.0"
 
-istanbul-lib-report@^1.1.4:
-  version "1.1.4"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.4.tgz#e886cdf505c4ebbd8e099e4396a90d0a28e2acb5"
+istanbul-lib-report@^1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz#f2a657fc6282f96170aaf281eb30a458f7f4170c"
   dependencies:
-    istanbul-lib-coverage "^1.2.0"
+    istanbul-lib-coverage "^1.2.1"
     mkdirp "^0.5.1"
     path-parse "^1.0.5"
     supports-color "^3.1.2"
 
-istanbul-lib-source-maps@^1.2.1:
-  version "1.2.3"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.3.tgz#20fb54b14e14b3fb6edb6aca3571fd2143db44e6"
+istanbul-lib-source-maps@^1.2.1, istanbul-lib-source-maps@^1.2.6:
+  version "1.2.6"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.6.tgz#37b9ff661580f8fca11232752ee42e08c6675d8f"
   dependencies:
     debug "^3.1.0"
-    istanbul-lib-coverage "^1.1.2"
+    istanbul-lib-coverage "^1.2.1"
     mkdirp "^0.5.1"
     rimraf "^2.6.1"
     source-map "^0.5.3"
 
-istanbul-lib-source-maps@^1.2.4:
-  version "1.2.5"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.5.tgz#ffe6be4e7ab86d3603e4290d54990b14506fc9b1"
-  dependencies:
-    debug "^3.1.0"
-    istanbul-lib-coverage "^1.2.0"
-    mkdirp "^0.5.1"
-    rimraf "^2.6.1"
-    source-map "^0.5.3"
-
-istanbul-reports@^1.3.0:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.3.0.tgz#2f322e81e1d9520767597dca3c20a0cce89a3554"
+istanbul-reports@^1.5.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.5.1.tgz#97e4dbf3b515e8c484caea15d6524eebd3ff4e1a"
   dependencies:
     handlebars "^4.0.3"
 
@@ -4317,18 +4388,18 @@ jest@22.4.2:
     jest-cli "^22.4.2"
 
 js-base64@^2.1.9:
-  version "2.4.8"
-  resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.8.tgz#57a9b130888f956834aa40c5b165ba59c758f033"
-
-js-tokens@^3.0.0, js-tokens@^3.0.2:
-  version "3.0.2"
-  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
+  version "2.4.9"
+  resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.9.tgz#748911fb04f48a60c4771b375cac45a80df11c03"
 
-"js-tokens@^3.0.0 || ^4.0.0":
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
 
-js-yaml@^3.4.3, js-yaml@^3.7.0:
+js-tokens@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
+
+js-yaml@3.12.0, js-yaml@^3.4.3, js-yaml@^3.7.0:
   version "3.12.0"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1"
   dependencies:
@@ -4347,34 +4418,34 @@ jsbn@~0.1.0:
   resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
 
 jsdom@^11.5.1:
-  version "11.11.0"
-  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.11.0.tgz#df486efad41aee96c59ad7a190e2449c7eb1110e"
+  version "11.12.0"
+  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8"
   dependencies:
-    abab "^1.0.4"
-    acorn "^5.3.0"
+    abab "^2.0.0"
+    acorn "^5.5.3"
     acorn-globals "^4.1.0"
     array-equal "^1.0.0"
     cssom ">= 0.3.2 < 0.4.0"
-    cssstyle ">= 0.3.1 < 0.4.0"
+    cssstyle "^1.0.0"
     data-urls "^1.0.0"
-    domexception "^1.0.0"
-    escodegen "^1.9.0"
+    domexception "^1.0.1"
+    escodegen "^1.9.1"
     html-encoding-sniffer "^1.0.2"
-    left-pad "^1.2.0"
-    nwsapi "^2.0.0"
+    left-pad "^1.3.0"
+    nwsapi "^2.0.7"
     parse5 "4.0.0"
     pn "^1.1.0"
-    request "^2.83.0"
+    request "^2.87.0"
     request-promise-native "^1.0.5"
     sax "^1.2.4"
     symbol-tree "^3.2.2"
-    tough-cookie "^2.3.3"
+    tough-cookie "^2.3.4"
     w3c-hr-time "^1.0.1"
     webidl-conversions "^4.0.2"
     whatwg-encoding "^1.0.3"
     whatwg-mimetype "^2.1.0"
     whatwg-url "^6.4.1"
-    ws "^4.0.0"
+    ws "^5.2.0"
     xml-name-validator "^3.0.0"
 
 jsesc@^1.3.0:
@@ -4419,6 +4490,12 @@ json5@^0.5.0, json5@^0.5.1:
   version "0.5.1"
   resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
 
+json5@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
+  dependencies:
+    minimist "^1.2.0"
+
 jsonfile@^2.1.0:
   version "2.4.0"
   resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8"
@@ -4530,8 +4607,8 @@ keycode@^2.1.9:
   resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04"
 
 killable@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.0.tgz#da8b84bd47de5395878f95d64d02f2449fe05e6b"
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
 
 kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
   version "3.2.2"
@@ -4575,7 +4652,7 @@ lcid@^1.0.0:
   dependencies:
     invert-kv "^1.0.0"
 
-left-pad@^1.2.0:
+left-pad@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e"
 
@@ -4610,10 +4687,10 @@ load-json-file@^2.0.0:
     strip-bom "^3.0.0"
 
 loader-runner@^2.3.0:
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2"
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.1.tgz#026f12fe7c3115992896ac02ba022ba92971b979"
 
-loader-utils@^0.2.16, loader-utils@~0.2.2:
+loader-utils@^0.2.16:
   version "0.2.17"
   resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348"
   dependencies:
@@ -4638,8 +4715,8 @@ locate-path@^2.0.0:
     path-exists "^3.0.0"
 
 lodash-es@^4.17.10, lodash-es@^4.17.5, lodash-es@^4.2.1:
-  version "4.17.10"
-  resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.10.tgz#62cd7104cdf5dd87f235a837f0ede0e8e5117e05"
+  version "4.17.11"
+  resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.11.tgz#145ab4a7ac5c5e52a3531fb4f310255a152b4be0"
 
 lodash._reinterpolate@~3.0.0:
   version "3.0.0"
@@ -4661,10 +4738,18 @@ lodash.endswith@^4.2.1:
   version "4.2.1"
   resolved "https://registry.yarnpkg.com/lodash.endswith/-/lodash.endswith-4.2.1.tgz#fed59ac1738ed3e236edd7064ec456448b37bc09"
 
+lodash.escape@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98"
+
 lodash.flattendeep@^4.4.0:
   version "4.4.0"
   resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
 
+lodash.isequal@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
+
 lodash.isfunction@^3.0.8:
   version "3.0.9"
   resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051"
@@ -4702,9 +4787,9 @@ lodash.uniq@^4.5.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
 
-lodash@4.17.10, "lodash@>=3.5 <5", lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0:
-  version "4.17.10"
-  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
+lodash@4.17.11, "lodash@>=3.5 <5", lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0:
+  version "4.17.11"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
 
 loglevel@^1.4.1:
   version "1.6.1"
@@ -4714,7 +4799,7 @@ longest@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
 
-loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1:
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
   dependencies:
@@ -4851,7 +4936,7 @@ micromatch@^2.1.5, micromatch@^2.3.11:
     parse-glob "^3.0.4"
     regex-cache "^0.4.2"
 
-micromatch@^3.1.4, micromatch@^3.1.8:
+micromatch@^3.1.10, micromatch@^3.1.4:
   version "3.1.10"
   resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
   dependencies:
@@ -4876,15 +4961,15 @@ miller-rabin@^4.0.0:
     bn.js "^4.0.0"
     brorand "^1.0.1"
 
-"mime-db@>= 1.34.0 < 2", mime-db@~1.35.0:
-  version "1.35.0"
-  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.35.0.tgz#0569d657466491283709663ad379a99b90d9ab47"
+"mime-db@>= 1.36.0 < 2", mime-db@~1.36.0:
+  version "1.36.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397"
 
-mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.18:
-  version "2.1.19"
-  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.19.tgz#71e464537a7ef81c15f2db9d97e913fc0ff606f0"
+mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.19:
+  version "2.1.20"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19"
   dependencies:
-    mime-db "~1.35.0"
+    mime-db "~1.36.0"
 
 mime@1.4.1:
   version "1.4.1"
@@ -4927,8 +5012,8 @@ minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0:
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
 
 minipass@^2.2.1, minipass@^2.3.3:
-  version "2.3.3"
-  resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233"
+  version "2.3.4"
+  resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.4.tgz#4768d7605ed6194d6d576169b9e12ef71e9d9957"
   dependencies:
     safe-buffer "^5.1.2"
     yallist "^3.0.0"
@@ -5002,8 +5087,8 @@ mute-stream@0.0.7:
   resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
 
 nan@^2.9.2:
-  version "2.10.0"
-  resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
+  version "2.11.0"
+  resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.0.tgz#574e360e4d954ab16966ec102c0c049fd961a099"
 
 nanomatch@^1.2.9:
   version "1.2.13"
@@ -5026,8 +5111,8 @@ natural-compare@^1.4.0:
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
 
 nearley@^2.7.10:
-  version "2.15.0"
-  resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.15.0.tgz#d1ff5406a58064615fe6eafb429cf06fbb1b7eab"
+  version "2.15.1"
+  resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.15.1.tgz#965e4e6ec9ed6b80fc81453e161efbcebb36d247"
   dependencies:
     moo "^0.4.3"
     nomnom "~1.6.2"
@@ -5036,8 +5121,8 @@ nearley@^2.7.10:
     semver "^5.4.1"
 
 needle@^2.2.1:
-  version "2.2.1"
-  resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d"
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.4.tgz#51931bff82533b1928b7d1d69e01f1b00ffd2a4e"
   dependencies:
     debug "^2.1.2"
     iconv-lite "^0.4.4"
@@ -5048,8 +5133,8 @@ negotiator@0.6.1:
   resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
 
 neo-async@^2.5.0:
-  version "2.5.1"
-  resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee"
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.2.tgz#489105ce7bc54e709d736b195f82135048c50fcc"
 
 next-tick@1:
   version "1.0.0"
@@ -5175,12 +5260,12 @@ normalize-url@^1.4.0:
     sort-keys "^1.0.0"
 
 npm-bundled@^1.0.1:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308"
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.5.tgz#3c1732b7ba936b3a10325aef616467c0ccbcc979"
 
 npm-packlist@^1.1.6:
-  version "1.1.10"
-  resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.10.tgz#1039db9e985727e464df066f4cf0ab6ef85c398a"
+  version "1.1.11"
+  resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.11.tgz#84e8c683cbe7867d34b1d357d893ce29e28a02de"
   dependencies:
     ignore-walk "^3.0.1"
     npm-bundled "^1.0.1"
@@ -5214,13 +5299,13 @@ number-is-nan@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
 
-nwsapi@^2.0.0:
-  version "2.0.7"
-  resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.0.7.tgz#6fc54c254621f10cac5225b76e81c74120139b78"
+nwsapi@^2.0.7:
+  version "2.0.9"
+  resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.0.9.tgz#77ac0cdfdcad52b6a1151a84e73254edc33ed016"
 
-oauth-sign@~0.8.2:
-  version "0.8.2"
-  resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
+oauth-sign@~0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
 
 object-assign@4.1.1, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
   version "4.1.1"
@@ -5242,7 +5327,7 @@ object-is@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6"
 
-object-keys@^1.0.11, object-keys@^1.0.8:
+object-keys@^1.0.11, object-keys@^1.0.12:
   version "1.0.12"
   resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2"
 
@@ -5350,10 +5435,10 @@ optionator@^0.8.1:
     wordwrap "~1.0.0"
 
 original@>=0.0.5:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/original/-/original-1.0.1.tgz#b0a53ff42ba997a8c9cd1fb5daaeb42b9d693190"
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f"
   dependencies:
-    url-parse "~1.4.0"
+    url-parse "^1.4.3"
 
 os-browserify@^0.3.0:
   version "0.3.0"
@@ -5517,8 +5602,8 @@ path-key@^2.0.0:
   resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
 
 path-parse@^1.0.5:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
 
 path-to-regexp@0.1.7:
   version "0.1.7"
@@ -5545,8 +5630,8 @@ path-type@^2.0.0:
     pify "^2.0.0"
 
 pbkdf2@^3.0.3:
-  version "3.0.16"
-  resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.16.tgz#7404208ec6b01b62d85bf83853a8064f8d9c2a5c"
+  version "3.0.17"
+  resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
   dependencies:
     create-hash "^1.1.2"
     create-hmac "^1.1.4"
@@ -5591,8 +5676,8 @@ popper.js@^1.14.1:
   resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.4.tgz#8eec1d8ff02a5a3a152dd43414a15c7b79fd69b6"
 
 portfinder@^1.0.9:
-  version "1.0.13"
-  resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9"
+  version "1.0.17"
+  resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.17.tgz#a8a1691143e46c4735edefcf4fbcccedad26456a"
   dependencies:
     async "^1.5.2"
     debug "^2.2.0"
@@ -5951,11 +6036,11 @@ prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1,
     object-assign "^4.1.1"
 
 proxy-addr@~2.0.3:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341"
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93"
   dependencies:
     forwarded "~0.1.2"
-    ipaddr.js "1.6.0"
+    ipaddr.js "1.8.0"
 
 prr@~1.0.1:
   version "1.0.1"
@@ -5966,8 +6051,8 @@ pseudomap@^1.0.2:
   resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
 
 psl@^1.1.24:
-  version "1.1.28"
-  resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.28.tgz#4fb6ceb08a1e2214d4fd4de0ca22dae13740bc7b"
+  version "1.1.29"
+  resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.29.tgz#60f580d360170bb722a797cc704411e6da850c67"
 
 public-encrypt@^4.0.0:
   version "4.0.2"
@@ -6010,10 +6095,14 @@ q@^1.1.2:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
 
-qs@6.5.1, qs@~6.5.1:
+qs@6.5.1:
   version "6.5.1"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
 
+qs@~6.5.2:
+  version "6.5.2"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
+
 query-string@^4.1.0:
   version "4.3.4"
   resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb"
@@ -6051,8 +6140,8 @@ randexp@0.4.6:
     ret "~0.1.10"
 
 randomatic@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.0.0.tgz#d35490030eb4f7578de292ce6dfb04a91a128923"
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.0.tgz#36f2ca708e9e567f5ed2ec01949026d50aa10116"
   dependencies:
     is-number "^4.0.0"
     kind-of "^6.0.0"
@@ -6101,8 +6190,8 @@ react-copy-to-clipboard@5.0.1:
     prop-types "^15.5.8"
 
 react-dev-utils@^5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-5.0.1.tgz#1f396e161fe44b595db1b186a40067289bf06613"
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-5.0.2.tgz#7bb68d2c4f6ffe7ed1184c5b0124fcad692774d2"
   dependencies:
     address "1.0.3"
     babel-code-frame "6.26.0"
@@ -6116,44 +6205,44 @@ react-dev-utils@^5.0.1:
     inquirer "3.3.0"
     is-root "1.0.0"
     opn "5.2.0"
-    react-error-overlay "^4.0.0"
+    react-error-overlay "^4.0.1"
     recursive-readdir "2.2.1"
     shell-quote "1.6.1"
-    sockjs-client "1.1.4"
+    sockjs-client "1.1.5"
     strip-ansi "3.0.1"
     text-table "0.2.0"
 
-react-dom@16.4.2:
-  version "16.4.2"
-  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.2.tgz#4afed569689f2c561d2b8da0b819669c38a0bda4"
+react-dom@16.5.2:
+  version "16.5.2"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.5.2.tgz#b69ee47aa20bab5327b2b9d7c1fe2a30f2cfa9d7"
   dependencies:
-    fbjs "^0.8.16"
     loose-envify "^1.1.0"
     object-assign "^4.1.1"
-    prop-types "^15.6.0"
+    prop-types "^15.6.2"
+    schedule "^0.5.0"
 
-react-dropzone@5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-5.0.1.tgz#3ed201215794c0f650c6f25a8311a9d96d35ebb6"
+react-dropzone@5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-5.1.1.tgz#b05613ea22f1ab71aa1f7cf5367df7b19468a2f3"
   dependencies:
     attr-accept "^1.1.3"
-    prop-types "^15.5.7"
+    prop-types "^15.6.2"
 
-react-error-overlay@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4"
+react-error-overlay@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.1.tgz#417addb0814a90f3a7082eacba7cee588d00da89"
 
 react-event-listener@^0.6.2:
-  version "0.6.2"
-  resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.6.2.tgz#df405e9578be052b77a76e4c3914686637caecff"
+  version "0.6.4"
+  resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.6.4.tgz#d0ea5ed897da1a796616c44b5a8758898140f203"
   dependencies:
-    "@babel/runtime" "7.0.0-beta.42"
+    "@babel/runtime" "7.0.0"
     prop-types "^15.6.0"
     warning "^4.0.1"
 
-react-is@^16.4.1, react-is@^16.4.2:
-  version "16.4.2"
-  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.2.tgz#84891b56c2b6d9efdee577cc83501dfc5ecead88"
+react-is@^16.4.2, react-is@^16.5.2:
+  version "16.5.2"
+  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.5.2.tgz#e2a7b7c3f5d48062eb769fcb123505eb928722e3"
 
 react-jss@^8.1.0:
   version "8.6.1"
@@ -6169,15 +6258,6 @@ react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
 
-react-reconciler@^0.7.0:
-  version "0.7.0"
-  resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.7.0.tgz#9614894103e5f138deeeb5eabaf3ee80eb1d026d"
-  dependencies:
-    fbjs "^0.8.16"
-    loose-envify "^1.1.0"
-    object-assign "^4.1.1"
-    prop-types "^15.6.0"
-
 react-redux@5.0.7:
   version "5.0.7"
   resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.7.tgz#0dc1076d9afb4670f993ffaef44b8f8c1155a4c8"
@@ -6220,6 +6300,21 @@ react-router@4.3.1, react-router@^4.2.0, react-router@^4.3.1:
     prop-types "^15.6.1"
     warning "^4.0.1"
 
+react-rte@0.16.1:
+  version "0.16.1"
+  resolved "https://registry.yarnpkg.com/react-rte/-/react-rte-0.16.1.tgz#e345664c87e992d15ec053b406f51ffac6e86622"
+  dependencies:
+    babel-runtime "^6.23.0"
+    class-autobind "^0.1.4"
+    classnames "^2.2.5"
+    draft-js ">=0.10.0"
+    draft-js-export-html ">=0.6.0"
+    draft-js-export-markdown ">=0.3.0"
+    draft-js-import-html ">=0.4.0"
+    draft-js-import-markdown ">=0.3.0"
+    draft-js-utils ">=0.2.0"
+    immutable "^3.8.1"
+
 react-scripts-ts@2.17.0:
   version "2.17.0"
   resolved "https://registry.yarnpkg.com/react-scripts-ts/-/react-scripts-ts-2.17.0.tgz#398bae19a30c9b39b3dfe0720ebb40c60c2f6574"
@@ -6269,31 +6364,31 @@ react-splitter-layout@3.0.1:
   resolved "https://registry.yarnpkg.com/react-splitter-layout/-/react-splitter-layout-3.0.1.tgz#c2e00e69b35d240ab7a44f395d41803c5f4b70ef"
 
 react-test-renderer@^16.0.0-0:
-  version "16.4.1"
-  resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.4.1.tgz#f2fb30c2c7b517db6e5b10ed20bb6b0a7ccd8d70"
+  version "16.5.2"
+  resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.5.2.tgz#92e9d2c6f763b9821b2e0b22f994ee675068b5ae"
   dependencies:
-    fbjs "^0.8.16"
     object-assign "^4.1.1"
-    prop-types "^15.6.0"
-    react-is "^16.4.1"
+    prop-types "^15.6.2"
+    react-is "^16.5.2"
+    schedule "^0.5.0"
 
-react-transition-group@2.4.0, react-transition-group@^2.2.1:
-  version "2.4.0"
-  resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.4.0.tgz#1d9391fabfd82e016f26fabd1eec329dbd922b5a"
+react-transition-group@2.5.0, react-transition-group@^2.2.1:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.5.0.tgz#70bca0e3546102c4dc5cf3f5f57f73447cce6874"
   dependencies:
     dom-helpers "^3.3.1"
-    loose-envify "^1.3.1"
+    loose-envify "^1.4.0"
     prop-types "^15.6.2"
     react-lifecycles-compat "^3.0.4"
 
-react@16.4.2:
-  version "16.4.2"
-  resolved "https://registry.yarnpkg.com/react/-/react-16.4.2.tgz#2cd90154e3a9d9dd8da2991149fdca3c260e129f"
+react@16.5.2:
+  version "16.5.2"
+  resolved "https://registry.yarnpkg.com/react/-/react-16.5.2.tgz#19f6b444ed139baa45609eee6dc3d318b3895d42"
   dependencies:
-    fbjs "^0.8.16"
     loose-envify "^1.1.0"
     object-assign "^4.1.1"
-    prop-types "^15.6.0"
+    prop-types "^15.6.2"
+    schedule "^0.5.0"
 
 read-pkg-up@^1.0.1:
   version "1.0.1"
@@ -6347,25 +6442,35 @@ readable-stream@1.0:
     string_decoder "~0.10.x"
 
 readdirp@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78"
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525"
   dependencies:
-    graceful-fs "^4.1.2"
-    minimatch "^3.0.2"
+    graceful-fs "^4.1.11"
+    micromatch "^3.1.10"
     readable-stream "^2.0.2"
-    set-immediate-shim "^1.0.1"
 
 realpath-native@^1.0.0:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.0.1.tgz#07f40a0cce8f8261e2e8b7ebebf5c95965d7b633"
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.0.2.tgz#cd51ce089b513b45cf9b1516c82989b51ccc6560"
   dependencies:
     util.promisify "^1.0.0"
 
-recompose@^0.28.0:
-  version "0.28.2"
-  resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.28.2.tgz#19e679227bdf979e0d31b73ffe7ae38c9194f4a7"
+"recompose@0.28.0 - 0.30.0":
+  version "0.30.0"
+  resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.30.0.tgz#82773641b3927e8c7d24a0d87d65aeeba18aabd0"
   dependencies:
-    "@babel/runtime" "7.0.0-beta.56"
+    "@babel/runtime" "^7.0.0"
+    change-emitter "^0.1.2"
+    fbjs "^0.8.1"
+    hoist-non-react-statics "^2.3.1"
+    react-lifecycles-compat "^3.0.2"
+    symbol-observable "^1.0.4"
+
+recompose@^0.29.0:
+  version "0.29.0"
+  resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.29.0.tgz#f1a4e20d5f24d6ef1440f83924e821de0b1bccef"
+  dependencies:
+    "@babel/runtime" "^7.0.0"
     change-emitter "^0.1.2"
     fbjs "^0.8.1"
     hoist-non-react-statics "^2.3.1"
@@ -6451,13 +6556,13 @@ regenerate@^1.2.1:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
 
-regenerator-runtime@^0.11.0, regenerator-runtime@^0.11.1:
+regenerator-runtime@^0.11.0:
   version "0.11.1"
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
 
 regenerator-runtime@^0.12.0:
-  version "0.12.0"
-  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.0.tgz#8052ac952d85b10f3425192cd0c53f45cf65c6cb"
+  version "0.12.1"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
 
 regenerator-transform@^0.10.0:
   version "0.10.1"
@@ -6538,8 +6643,8 @@ renderkid@^2.0.1:
     utila "~0.3"
 
 repeat-element@^1.1.2:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a"
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
 
 repeat-string@^1.5.2, repeat-string@^1.6.1:
   version "1.6.1"
@@ -6565,30 +6670,30 @@ request-promise-native@^1.0.5:
     stealthy-require "^1.1.0"
     tough-cookie ">=2.3.3"
 
-request@^2.83.0:
-  version "2.87.0"
-  resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e"
+request@^2.87.0:
+  version "2.88.0"
+  resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
   dependencies:
     aws-sign2 "~0.7.0"
-    aws4 "^1.6.0"
+    aws4 "^1.8.0"
     caseless "~0.12.0"
-    combined-stream "~1.0.5"
-    extend "~3.0.1"
+    combined-stream "~1.0.6"
+    extend "~3.0.2"
     forever-agent "~0.6.1"
-    form-data "~2.3.1"
-    har-validator "~5.0.3"
+    form-data "~2.3.2"
+    har-validator "~5.1.0"
     http-signature "~1.2.0"
     is-typedarray "~1.0.0"
     isstream "~0.1.2"
     json-stringify-safe "~5.0.1"
-    mime-types "~2.1.17"
-    oauth-sign "~0.8.2"
+    mime-types "~2.1.19"
+    oauth-sign "~0.9.0"
     performance-now "^2.1.0"
-    qs "~6.5.1"
-    safe-buffer "^5.1.1"
-    tough-cookie "~2.3.3"
+    qs "~6.5.2"
+    safe-buffer "^5.1.2"
+    tough-cookie "~2.4.3"
     tunnel-agent "^0.6.0"
-    uuid "^3.1.0"
+    uuid "^3.3.2"
 
 require-directory@^2.1.1:
   version "2.1.1"
@@ -6718,7 +6823,7 @@ safe-regex@^1.1.0:
   dependencies:
     ret "~0.1.10"
 
-"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2:
+"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
 
@@ -6741,6 +6846,12 @@ sax@^1.2.4, sax@~1.2.1:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
 
+schedule@^0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/schedule/-/schedule-0.5.0.tgz#c128fffa0b402488b08b55ae74bb9df55cc29cc8"
+  dependencies:
+    object-assign "^4.1.1"
+
 schema-utils@^0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf"
@@ -6748,8 +6859,8 @@ schema-utils@^0.3.0:
     ajv "^5.0.0"
 
 schema-utils@^0.4.5:
-  version "0.4.5"
-  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e"
+  version "0.4.7"
+  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187"
   dependencies:
     ajv "^6.1.0"
     ajv-keywords "^3.1.0"
@@ -6771,8 +6882,8 @@ semver-diff@^2.0.0:
     semver "^5.0.3"
 
 "semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1:
-  version "5.5.0"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
+  version "5.5.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477"
 
 send@0.16.2:
   version "0.16.2"
@@ -6825,10 +6936,6 @@ set-blocking@^2.0.0, set-blocking@~2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
 
-set-immediate-shim@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
-
 set-value@^0.4.3:
   version "0.4.3"
   resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1"
@@ -6935,6 +7042,17 @@ sockjs-client@1.1.4:
     json3 "^3.3.2"
     url-parse "^1.1.8"
 
+sockjs-client@1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.5.tgz#1bb7c0f7222c40f42adf14f4442cbd1269771a83"
+  dependencies:
+    debug "^2.6.6"
+    eventsource "0.1.6"
+    faye-websocket "~0.11.0"
+    inherits "^2.0.1"
+    json3 "^3.3.2"
+    url-parse "^1.1.8"
+
 sockjs@0.3.18:
   version "0.3.18"
   resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.18.tgz#d9b289316ca7df77595ef299e075f0f937eb4207"
@@ -6953,12 +7071,11 @@ source-list-map@^2.0.0:
   resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085"
 
 source-map-loader@^0.2.1:
-  version "0.2.3"
-  resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-0.2.3.tgz#d4b0c8cd47d54edce3e6bfa0f523f452b5b0e521"
+  version "0.2.4"
+  resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-0.2.4.tgz#c18b0dc6e23bf66f6792437557c569a11e072271"
   dependencies:
     async "^2.5.0"
-    loader-utils "~0.2.2"
-    source-map "~0.6.1"
+    loader-utils "^1.1.0"
 
 source-map-resolve@^0.5.0:
   version "0.5.2"
@@ -6977,8 +7094,8 @@ source-map-support@^0.4.15:
     source-map "^0.5.6"
 
 source-map-support@^0.5.0:
-  version "0.5.6"
-  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.6.tgz#4435cee46b1aab62b8e8610ce60f788091c51c13"
+  version "0.5.9"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f"
   dependencies:
     buffer-from "^1.0.0"
     source-map "^0.6.0"
@@ -6987,23 +7104,17 @@ source-map-url@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
 
-source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1:
+source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1:
   version "0.5.7"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
 
-source-map@^0.4.4:
-  version "0.4.4"
-  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
-  dependencies:
-    amdefine ">=0.0.4"
-
-source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
+source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
 
 spdx-correct@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82"
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.1.tgz#434434ff9d1726b4d9f4219d1004813d80639e30"
   dependencies:
     spdx-expression-parse "^3.0.0"
     spdx-license-ids "^3.0.0"
@@ -7020,8 +7131,8 @@ spdx-expression-parse@^3.0.0:
     spdx-license-ids "^3.0.0"
 
 spdx-license-ids@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87"
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz#e2a303236cac54b04031fa7a5a79c7e701df852f"
 
 spdy-transport@^2.0.18:
   version "2.1.0"
@@ -7104,8 +7215,8 @@ stream-browserify@^2.0.1:
     readable-stream "^2.0.2"
 
 stream-each@^1.1.0:
-  version "1.2.2"
-  resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.2.tgz#8e8c463f91da8991778765873fe4d960d8f616bd"
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae"
   dependencies:
     end-of-stream "^1.1.0"
     stream-shift "^1.0.0"
@@ -7150,6 +7261,14 @@ string-width@^1.0.1, string-width@^1.0.2:
     is-fullwidth-code-point "^2.0.0"
     strip-ansi "^4.0.0"
 
+string.prototype.trim@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea"
+  dependencies:
+    define-properties "^1.1.2"
+    es-abstract "^1.5.0"
+    function-bind "^1.0.2"
+
 string_decoder@^1.0.0, string_decoder@~1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
@@ -7192,7 +7311,7 @@ strip-indent@^1.0.1:
   dependencies:
     get-stdin "^4.0.1"
 
-strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
+strip-json-comments@~2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
 
@@ -7226,8 +7345,8 @@ supports-color@^4.2.1:
     has-flag "^2.0.0"
 
 supports-color@^5.3.0, supports-color@^5.4.0:
-  version "5.4.0"
-  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
   dependencies:
     has-flag "^3.0.0"
 
@@ -7285,13 +7404,17 @@ symbol-tree@^3.2.2:
   version "3.2.2"
   resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
 
+synthetic-dom@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/synthetic-dom/-/synthetic-dom-1.2.0.tgz#f3589aafe2b5e299f337bb32973a9be42dd5625e"
+
 tapable@^0.2.7:
   version "0.2.8"
   resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22"
 
 tar@^4:
-  version "4.4.4"
-  resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.4.tgz#ec8409fae9f665a4355cc3b4087d0820232bb8cd"
+  version "4.4.6"
+  resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.6.tgz#63110f09c00b4e60ac8bcfe1bf3c8660235fbc9b"
   dependencies:
     chownr "^1.0.1"
     fs-minipass "^1.2.5"
@@ -7308,11 +7431,11 @@ term-size@^1.2.0:
     execa "^0.7.0"
 
 test-exclude@^4.2.1:
-  version "4.2.1"
-  resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.1.tgz#dfa222f03480bca69207ca728b37d74b45f724fa"
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.3.tgz#a9a5e64474e4398339245a0a769ad7c2f4a97c20"
   dependencies:
     arrify "^1.0.1"
-    micromatch "^3.1.8"
+    micromatch "^2.3.11"
     object-assign "^4.1.0"
     read-pkg-up "^1.0.1"
     require-main-filename "^1.0.1"
@@ -7350,8 +7473,8 @@ thunky@^1.0.2:
   resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.0.2.tgz#a862e018e3fb1ea2ec3fce5d55605cf57f247371"
 
 time-stamp@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.0.0.tgz#95c6a44530e15ba8d6f4a3ecb8c3a3fac46da357"
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.1.0.tgz#6c5c0b2bc835a244616abcfddf81ce13a1975c9f"
 
 timed-out@^4.0.0:
   version "4.0.1"
@@ -7411,19 +7534,13 @@ toposort@^1.0.0:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029"
 
-tough-cookie@>=2.3.3, tough-cookie@^2.3.3:
+tough-cookie@>=2.3.3, tough-cookie@^2.3.4, tough-cookie@~2.4.3:
   version "2.4.3"
   resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
   dependencies:
     psl "^1.1.24"
     punycode "^1.4.1"
 
-tough-cookie@~2.3.3:
-  version "2.3.4"
-  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655"
-  dependencies:
-    punycode "^1.4.1"
-
 tr46@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
@@ -7470,21 +7587,22 @@ tsconfig-paths-webpack-plugin@^2.0.0:
     tsconfig-paths "^3.1.1"
 
 tsconfig-paths@^3.1.1:
-  version "3.4.2"
-  resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.4.2.tgz#4640bffaeee3fc0ab986607edae203859156a8c3"
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.6.0.tgz#f14078630d9d6e8b1dc690c1fc0cfb9cd0663891"
   dependencies:
+    "@types/json5" "^0.0.29"
     deepmerge "^2.0.1"
+    json5 "^1.0.1"
     minimist "^1.2.0"
     strip-bom "^3.0.0"
-    strip-json-comments "^2.0.1"
 
 tslib@^1.8.0, tslib@^1.8.1:
   version "1.9.3"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
 
 tslint-config-prettier@^1.10.0:
-  version "1.14.0"
-  resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.14.0.tgz#860b36634e53f4c70c64c51ff3ef7fd9bbab7676"
+  version "1.15.0"
+  resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.15.0.tgz#76b9714399004ab6831fdcf76d89b73691c812cf"
 
 tslint-react@^3.2.0:
   version "3.6.0"
@@ -7510,8 +7628,8 @@ tslint@^5.7.0:
     tsutils "^2.27.2"
 
 tsutils@^2.13.1, tsutils@^2.27.2:
-  version "2.28.0"
-  resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.28.0.tgz#6bd71e160828f9d019b6f4e844742228f85169a1"
+  version "2.29.0"
+  resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99"
   dependencies:
     tslib "^1.8.1"
 
@@ -7546,9 +7664,9 @@ typedarray@^0.0.6:
   version "0.0.6"
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
 
-typescript@3.0.1:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.1.tgz#43738f29585d3a87575520a4b93ab6026ef11fdb"
+typescript@3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.1.tgz#3362ba9dd1e482ebb2355b02dfe8bcd19a2c7c96"
 
 ua-parser-js@^0.7.18:
   version "0.7.18"
@@ -7561,14 +7679,14 @@ uglify-es@^3.3.4:
     commander "~2.13.0"
     source-map "~0.6.1"
 
-uglify-js@3.4.x, uglify-js@^3.0.13:
-  version "3.4.5"
-  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.5.tgz#650889c0766cf0f6fd5346cea09cd212f544be69"
+uglify-js@3.4.x, uglify-js@^3.0.13, uglify-js@^3.1.4:
+  version "3.4.9"
+  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"
   dependencies:
-    commander "~2.16.0"
+    commander "~2.17.1"
     source-map "~0.6.1"
 
-uglify-js@^2.6, uglify-js@^2.8.29:
+uglify-js@^2.8.29:
   version "2.8.29"
   resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
   dependencies:
@@ -7590,8 +7708,8 @@ uglifyjs-webpack-plugin@^0.4.6:
     webpack-sources "^1.0.1"
 
 uglifyjs-webpack-plugin@^1.1.8:
-  version "1.2.7"
-  resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.7.tgz#57638dd99c853a1ebfe9d97b42160a8a507f9d00"
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz#75f548160858163a08643e086d5fefe18a5d67de"
   dependencies:
     cacache "^10.0.4"
     find-cache-dir "^1.0.0"
@@ -7628,14 +7746,14 @@ uniqs@^2.0.0:
   resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
 
 unique-filename@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3"
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230"
   dependencies:
     unique-slug "^2.0.0"
 
 unique-slug@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.0.tgz#db6676e7c7cc0629878ff196097c78855ae9f4ab"
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.1.tgz#5e9edc6d1ce8fb264db18a507ef9bd8544451ca6"
   dependencies:
     imurmurhash "^0.1.4"
 
@@ -7687,7 +7805,7 @@ upper-case@^1.1.1:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
 
-uri-js@^4.2.1:
+uri-js@^4.2.2:
   version "4.2.2"
   resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
   dependencies:
@@ -7715,9 +7833,9 @@ url-parse-lax@^1.0.0:
   dependencies:
     prepend-http "^1.0.1"
 
-url-parse@^1.1.8, url-parse@~1.4.0:
-  version "1.4.1"
-  resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.1.tgz#4dec9dad3dc8585f862fed461d2e19bbf623df30"
+url-parse@^1.1.8, url-parse@^1.4.3:
+  version "1.4.3"
+  resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.3.tgz#bfaee455c889023219d757e045fa6a684ec36c15"
   dependencies:
     querystringify "^2.0.0"
     requires-port "^1.0.0"
@@ -7768,7 +7886,7 @@ utils-merge@1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
 
-uuid@3.3.2, uuid@^3.1.0:
+uuid@3.3.2, uuid@^3.3.2:
   version "3.3.2"
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
 
@@ -7777,8 +7895,8 @@ uuid@^2.0.2:
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
 
 validate-npm-package-license@^3.0.1:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338"
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
   dependencies:
     spdx-correct "^3.0.0"
     spdx-expression-parse "^3.0.0"
@@ -7828,8 +7946,8 @@ warning@^3.0.0:
     loose-envify "^1.0.0"
 
 warning@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.1.tgz#66ce376b7fbfe8a887c22bdf0e7349d73d397745"
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.2.tgz#aa6876480872116fa3e11d434b0d0d8d91e44607"
   dependencies:
     loose-envify "^1.0.0"
 
@@ -7908,8 +8026,8 @@ webpack-manifest-plugin@1.3.2:
     lodash ">=3.5 <5"
 
 webpack-sources@^1.0.1, webpack-sources@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54"
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85"
   dependencies:
     source-list-map "^2.0.0"
     source-map "~0.6.1"
@@ -7953,20 +8071,20 @@ websocket-extensions@>=0.1.1:
   resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
 
 whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz#57c235bc8657e914d24e1a397d3c82daee0a6ba3"
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.4.tgz#63fb016b7435b795d9025632c086a5209dbd2621"
   dependencies:
-    iconv-lite "0.4.19"
+    iconv-lite "0.4.23"
 
 whatwg-fetch@2.0.3, whatwg-fetch@>=0.10.0:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84"
 
-whatwg-mimetype@^2.0.0, whatwg-mimetype@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz#f0f21d76cbba72362eb609dbed2a30cd17fcc7d4"
+whatwg-mimetype@^2.1.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz#a3d58ef10b76009b042d03e25591ece89b88d171"
 
-whatwg-url@^6.4.0, whatwg-url@^6.4.1:
+whatwg-url@^6.4.1:
   version "6.5.0"
   resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8"
   dependencies:
@@ -7974,6 +8092,14 @@ whatwg-url@^6.4.0, whatwg-url@^6.4.1:
     tr46 "^1.0.1"
     webidl-conversions "^4.0.2"
 
+whatwg-url@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.0.0.tgz#fde926fa54a599f3adf82dff25a9f7be02dc6edd"
+  dependencies:
+    lodash.sortby "^4.7.0"
+    tr46 "^1.0.1"
+    webidl-conversions "^4.0.2"
+
 whet.extend@~0.9.9:
   version "0.9.9"
   resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1"
@@ -8041,12 +8167,11 @@ write-file-atomic@^2.0.0, write-file-atomic@^2.1.0:
     imurmurhash "^0.1.4"
     signal-exit "^3.0.2"
 
-ws@^4.0.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/ws/-/ws-4.1.0.tgz#a979b5d7d4da68bf54efe0408967c324869a7289"
+ws@^5.2.0:
+  version "5.2.2"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f"
   dependencies:
     async-limiter "~1.0.0"
-    safe-buffer "~5.1.0"
 
 xdg-basedir@^3.0.0:
   version "3.0.0"