Merge branch '14307-search-basic-view'
authorJanicki Artur <artur.janicki@contractors.roche.com>
Tue, 9 Oct 2018 13:17:53 +0000 (15:17 +0200)
committerJanicki Artur <artur.janicki@contractors.roche.com>
Tue, 9 Oct 2018 13:17:53 +0000 (15:17 +0200)
refs #14307

Arvados-DCO-1.1-Signed-off-by: Janicki Artur <artur.janicki@contractors.roche.com>

23 files changed:
package.json
src/common/app-info.ts [new file with mode: 0644]
src/components/search-bar/search-bar.tsx [deleted file]
src/components/search-input/search-input.test.tsx
src/index.tsx
src/services/groups-service/groups-service.ts
src/store/app-info/app-info-actions.ts [new file with mode: 0644]
src/store/app-info/app-info-reducer.ts [new file with mode: 0644]
src/store/resources/resources-actions.ts
src/store/search-bar/search-bar-actions.ts [new file with mode: 0644]
src/store/search-bar/search-bar-reducer.ts [new file with mode: 0644]
src/store/store.ts
src/views-components/dialog-forms/file-selection-dialog.ts
src/views-components/main-app-bar/main-app-bar.tsx
src/views-components/search-bar/search-bar-advanced-view.tsx [new file with mode: 0644]
src/views-components/search-bar/search-bar-autocomplete-view.tsx [new file with mode: 0644]
src/views-components/search-bar/search-bar-basic-view.tsx [new file with mode: 0644]
src/views-components/search-bar/search-bar-view.test.tsx [moved from src/components/search-bar/search-bar.test.tsx with 61% similarity]
src/views-components/search-bar/search-bar-view.tsx [new file with mode: 0644]
src/views-components/search-bar/search-bar.tsx [new file with mode: 0644]
src/views/main-panel/main-panel-root.tsx [new file with mode: 0644]
src/views/main-panel/main-panel.tsx
yarn.lock

index af5b28bd1526d069626e99ce63e2fb80821c70c7..f84170adc7330916fab7f1e074f2e3a357533443 100644 (file)
@@ -3,12 +3,13 @@
   "version": "0.1.0",
   "private": true,
   "dependencies": {
-    "@types/js-yaml": "3.11.2",
-    "@material-ui/core": "3.1.1",
+    "@material-ui/core": "3.1.2",
     "@material-ui/icons": "3.0.1",
+    "@types/js-yaml": "3.11.2",
     "@types/lodash": "4.14.116",
     "@types/react-copy-to-clipboard": "4.2.6",
     "@types/react-dropzone": "4.2.2",
+    "@types/react-highlight-words": "0.12.0",
     "@types/redux-form": "7.4.5",
     "@types/shell-quote": "1.6.0",
     "axios": "0.18.0",
     "react-copy-to-clipboard": "5.0.1",
     "react-dom": "16.5.2",
     "react-dropzone": "5.1.1",
+    "react-highlight-words": "0.14.0",
     "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-scripts-ts": "3.1.0",
     "react-splitter-layout": "3.0.1",
     "react-transition-group": "2.5.0",
     "redux": "4.0.0",
@@ -44,7 +46,7 @@
     "@types/classnames": "2.2.6",
     "@types/enzyme": "3.1.14",
     "@types/enzyme-adapter-react-16": "1.0.3",
-    "@types/jest": "23.3.2",
+    "@types/jest": "23.3.3",
     "@types/node": "10.11.2",
     "@types/react": "16.4",
     "@types/react-dom": "16.0.8",
diff --git a/src/common/app-info.ts b/src/common/app-info.ts
new file mode 100644 (file)
index 0000000..cd27483
--- /dev/null
@@ -0,0 +1,9 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+export const getBuildInfo = (): string => {
+    const getBuildNumber = "BN-" + (process.env.REACT_APP_BUILD_NUMBER || "dev");
+    const getGitCommit = "GIT-" + (process.env.REACT_APP_GIT_COMMIT || "latest").substr(0, 7);
+    return getBuildNumber + " / " + getGitCommit;
+};
\ No newline at end of file
diff --git a/src/components/search-bar/search-bar.tsx b/src/components/search-bar/search-bar.tsx
deleted file mode 100644 (file)
index 366d705..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import * as React from 'react';
-import {
-    IconButton,
-    Paper,
-    StyleRulesCallback,
-    withStyles,
-    WithStyles,
-    Tooltip,
-    InputAdornment, Input
-} from '@material-ui/core';
-import SearchIcon from '@material-ui/icons/Search';
-
-type CssRules = 'container' | 'input';
-
-const styles: StyleRulesCallback<CssRules> = theme => {
-    return {
-        container: {
-            position: 'relative',
-            width: '100%'
-        },
-        input: {
-            border: 'none',
-            borderRadius: theme.spacing.unit / 4,
-            padding: `${theme.spacing.unit / 2}px ${theme.spacing.unit}px`
-        }
-    };
-};
-
-interface SearchBarDataProps {
-    value: string;
-}
-
-interface SearchBarActionProps {
-    onSearch: (value: string) => any;
-    debounce?: number;
-}
-
-type SearchBarProps = SearchBarDataProps & SearchBarActionProps & WithStyles<CssRules>;
-
-interface SearchBarState {
-    value: string;
-}
-
-export const DEFAULT_SEARCH_DEBOUNCE = 1000;
-
-export const SearchBar = withStyles(styles)(
-    class extends React.Component<SearchBarProps> {
-        state: SearchBarState = {
-            value: ""
-        };
-
-        timeout: number;
-
-        render() {
-            const { classes } = this.props;
-            return <Paper className={classes.container}>
-                <form onSubmit={this.handleSubmit}>
-                    <Input
-                        className={classes.input}
-                        onChange={this.handleChange}
-                        placeholder="Search"
-                        value={this.state.value}
-                        fullWidth={true}
-                        disableUnderline={true}
-                        endAdornment={
-                            <InputAdornment position="end">
-                                <Tooltip title='Search'>
-                                    <IconButton>
-                                        <SearchIcon />
-                                    </IconButton>
-                                </Tooltip>
-                            </InputAdornment>
-                        }/>
-                </form>
-            </Paper>;
-        }
-
-        componentDidMount() {
-            this.setState({ value: this.props.value });
-        }
-
-        componentWillReceiveProps(nextProps: SearchBarProps) {
-            if (nextProps.value !== this.props.value) {
-                this.setState({ value: nextProps.value });
-            }
-        }
-
-        componentWillUnmount() {
-            clearTimeout(this.timeout);
-        }
-
-        handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
-            event.preventDefault();
-            clearTimeout(this.timeout);
-            this.props.onSearch(this.state.value);
-        }
-
-        handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
-            clearTimeout(this.timeout);
-            this.setState({ value: event.target.value });
-            this.timeout = window.setTimeout(
-                () => this.props.onSearch(this.state.value),
-                this.props.debounce || DEFAULT_SEARCH_DEBOUNCE
-            );
-
-        }
-    }
-);
index a91f9b17a6812408ed2764b6f45f8ee67ef61e32..a983a409cd226151c14fb2bdafaaac423611787f 100644 (file)
@@ -46,7 +46,7 @@ describe("<SearchInput />", () => {
             const searchInput = mount(<SearchInput value="" onSearch={onSearch} debounce={1000} />);
             searchInput.find("input").simulate("change", { target: { value: "current value" } });
             searchInput.find("form").simulate("submit");
-            jest.advanceTimersByTime(1000);
+            jest.runTimersToTime(1000);
             expect(onSearch).toHaveBeenCalledTimes(1);
             expect(onSearch).toBeCalledWith("current value");
         });
@@ -58,37 +58,37 @@ describe("<SearchInput />", () => {
             const searchInput = mount(<SearchInput value="" onSearch={onSearch} />);
             searchInput.find("input").simulate("change", { target: { value: "current value" } });
             expect(onSearch).not.toBeCalled();
-            jest.advanceTimersByTime(DEFAULT_SEARCH_DEBOUNCE);
+            jest.runTimersToTime(DEFAULT_SEARCH_DEBOUNCE);
             expect(onSearch).toBeCalledWith("current value");
         });
 
         it("calls onSearch after the time specified in props has passed", () => {
             const searchInput = mount(<SearchInput value="" onSearch={onSearch} debounce={2000}/>);
             searchInput.find("input").simulate("change", { target: { value: "current value" } });
-            jest.advanceTimersByTime(1000);
+            jest.runTimersToTime(1000);
             expect(onSearch).not.toBeCalled();
-            jest.advanceTimersByTime(1000);
+            jest.runTimersToTime(1000);
             expect(onSearch).toBeCalledWith("current value");
         });
 
         it("calls onSearch only once after no change happened during the specified time", () => {
             const searchInput = mount(<SearchInput value="" onSearch={onSearch} debounce={1000}/>);
             searchInput.find("input").simulate("change", { target: { value: "current value" } });
-            jest.advanceTimersByTime(500);
+            jest.runTimersToTime(500);
             searchInput.find("input").simulate("change", { target: { value: "changed value" } });
-            jest.advanceTimersByTime(1000);
+            jest.runTimersToTime(1000);
             expect(onSearch).toHaveBeenCalledTimes(1);
         });
 
         it("calls onSearch again after the specified time has passed since previous call", () => {
             const searchInput = mount(<SearchInput value="" onSearch={onSearch} debounce={1000}/>);
             searchInput.find("input").simulate("change", { target: { value: "current value" } });
-            jest.advanceTimersByTime(500);
+            jest.runTimersToTime(500);
             searchInput.find("input").simulate("change", { target: { value: "intermediate value" } });
-            jest.advanceTimersByTime(1000);
+            jest.runTimersToTime(1000);
             expect(onSearch).toBeCalledWith("intermediate value");
             searchInput.find("input").simulate("change", { target: { value: "latest value" } });
-            jest.advanceTimersByTime(1000);
+            jest.runTimersToTime(1000);
             expect(onSearch).toBeCalledWith("latest value");
             expect(onSearch).toHaveBeenCalledTimes(2);
 
index a4a5e366a26b1c93713e580c4ce717a1c4e4a117..b3f351e025710729faefdeb4431a9218edbfffea 100644 (file)
@@ -42,14 +42,10 @@ 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';
+import { setBuildInfo } from '~/store/app-info/app-info-actions';
+import { getBuildInfo } from '~/common/app-info';
 
-const getBuildNumber = () => "BN-" + (process.env.REACT_APP_BUILD_NUMBER || "dev");
-const getGitCommit = () => "GIT-" + (process.env.REACT_APP_GIT_COMMIT || "latest").substr(0, 7);
-const getBuildInfo = () => getBuildNumber() + " / " + getGitCommit();
-
-const buildInfo = getBuildInfo();
-
-console.log(`Starting arvados [${buildInfo}]`);
+console.log(`Starting arvados [${getBuildInfo()}]`);
 
 addMenuActionSet(ContextMenuKind.ROOT_PROJECT, rootProjectActionSet);
 addMenuActionSet(ContextMenuKind.PROJECT, projectActionSet);
@@ -80,11 +76,12 @@ fetchConfig()
 
         store.subscribe(initListener(history, store, services, config));
         store.dispatch(initAuth());
+        store.dispatch(setBuildInfo());
         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} />;
+        const MainPanelComponent = (props: any) => <MainPanel {...props} />;
 
         const App = () =>
             <MuiThemeProvider theme={CustomTheme}>
index c4c56f38fc514efaa602d377f2ef9d8a0e70e728..b0c1b56e97fd0d93347299206e0595c36764ee26 100644 (file)
@@ -45,14 +45,16 @@ export class GroupsService<T extends GroupResource = GroupResource> extends Tras
             order: order ? order : undefined
         };
 
+        const pathUrl = uuid ? `${uuid}/contents` : 'contents';
         const response = await CommonResourceService.defaultResponse(
-            this.serverApi
-                .get(this.resourceType + `${uuid}/contents`, {
-                    params: CommonResourceService.mapKeys(_.snakeCase)(params)
-                }),
-            this.actions, 
-            false
-        );
+                this.serverApi
+                    .get(this.resourceType + pathUrl, {
+                        params: CommonResourceService.mapKeys(_.snakeCase)(params)
+                    }),
+                this.actions, 
+                false
+            );
+
         const { items, ...res } = response;
         const mappedItems = items.map((item: GroupContentsResource) => {
             const mappedItem = TrashableResourceService.mapKeys(_.camelCase)(item);
diff --git a/src/store/app-info/app-info-actions.ts b/src/store/app-info/app-info-actions.ts
new file mode 100644 (file)
index 0000000..cd32e03
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { unionize, ofType, UnionOf } from '~/common/unionize';
+import { Dispatch } from 'redux';
+import { RootState } from '~/store/store';
+import { ServiceRepository } from '~/services/services';
+import { getBuildInfo } from '~/common/app-info';
+
+export const appInfoActions = unionize({
+    SET_BUILD_INFO: ofType<string>()
+});
+
+export type AppInfoAction = UnionOf<typeof appInfoActions>;
+
+export const setBuildInfo = () => 
+    (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) =>
+        dispatch(appInfoActions.SET_BUILD_INFO(getBuildInfo()));
+
+
+
diff --git a/src/store/app-info/app-info-reducer.ts b/src/store/app-info/app-info-reducer.ts
new file mode 100644 (file)
index 0000000..e52aa7c
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { appInfoActions, AppInfoAction } from "~/store/app-info/app-info-actions";
+
+export interface AppInfoState {
+    buildInfo: string;
+}
+
+const initialState = {
+    buildInfo: ''
+};
+
+export const appInfoReducer = (state: AppInfoState = initialState, action: AppInfoAction) =>
+    appInfoActions.match(action, {
+        SET_BUILD_INFO: buildInfo => ({ ...state, buildInfo }),
+        default: () => state
+    });
index 0034e7aa5faf8cdea6f22236f971ec14db657e18..0453236a3f797e97e39c2c2027312b5124e68796 100644 (file)
@@ -20,12 +20,14 @@ export const updateResources = (resources: Resource[]) => resourcesActions.SET_R
 
 export const loadResource = (uuid: string) =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
-        const kind = extractUuidKind(uuid);
-        const service = getResourceService(kind)(services);
-        if (service) {
-            const resource = await service.get(uuid);
-            dispatch<any>(updateResources([resource]));
-            return resource;
-        }
+        try {
+            const kind = extractUuidKind(uuid);
+            const service = getResourceService(kind)(services);
+            if (service) {
+                const resource = await service.get(uuid);
+                dispatch<any>(updateResources([resource]));
+                return resource;
+            }
+        } catch {}
         return undefined;
-    };
\ No newline at end of file
+    };
diff --git a/src/store/search-bar/search-bar-actions.ts b/src/store/search-bar/search-bar-actions.ts
new file mode 100644 (file)
index 0000000..2d171e0
--- /dev/null
@@ -0,0 +1,50 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { unionize, ofType, UnionOf } from "~/common/unionize";
+import { GroupContentsResource, GroupContentsResourcePrefix } from '~/services/groups-service/groups-service';
+import { Dispatch } from 'redux';
+import { RootState } from '~/store/store';
+import { ServiceRepository } from '~/services/services';
+import { FilterBuilder } from "~/services/api/filter-builder";
+import { ResourceKind } from '~/models/resource';
+import { GroupClass } from '~/models/group';
+
+export const searchBarActions = unionize({
+    SET_CURRENT_VIEW: ofType<string>(),
+    OPEN_SEARCH_VIEW: ofType<{}>(),
+    CLOSE_SEARCH_VIEW: ofType<{}>(),
+    SET_SEARCH_RESULTS: ofType<GroupContentsResource[]>(),
+    SET_SEARCH_VALUE: ofType<string>()
+});
+
+export type SearchBarActions = UnionOf<typeof searchBarActions>;
+
+export const goToView = (currentView: string) => searchBarActions.SET_CURRENT_VIEW(currentView);
+
+export const searchData = (searchValue: string) => 
+    async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+        dispatch(searchBarActions.SET_SEARCH_VALUE(searchValue));
+        dispatch(searchBarActions.SET_SEARCH_RESULTS([]));
+        if (searchValue) {
+            const filters = getFilters('name', searchValue);
+            const { items } = await services.groupsService.contents('', {
+                filters, 
+                limit: 5,
+                recursive: true
+            });
+            dispatch(searchBarActions.SET_SEARCH_RESULTS(items));
+        }
+    };
+
+
+const getFilters = (filterName: string, searchValue: string): string => {
+    return new FilterBuilder()
+        .addIsA("uuid", [ResourceKind.PROJECT, ResourceKind.COLLECTION, ResourceKind.PROCESS])
+        .addILike(filterName, searchValue, GroupContentsResourcePrefix.COLLECTION)
+        .addILike(filterName, searchValue, GroupContentsResourcePrefix.PROCESS)
+        .addILike(filterName, searchValue, GroupContentsResourcePrefix.PROJECT)
+        .addEqual('groupClass', GroupClass.PROJECT, GroupContentsResourcePrefix.PROJECT)
+        .getFilters();
+};
\ No newline at end of file
diff --git a/src/store/search-bar/search-bar-reducer.ts b/src/store/search-bar/search-bar-reducer.ts
new file mode 100644 (file)
index 0000000..048cfea
--- /dev/null
@@ -0,0 +1,36 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { searchBarActions, SearchBarActions } from '~/store/search-bar/search-bar-actions';
+import { GroupContentsResource } from '~/services/groups-service/groups-service';
+
+interface SearchBar {
+    currentView: string;
+    open: boolean;
+    searchResults: GroupContentsResource[];
+    searchValue: string;
+}
+
+export enum SearchView {
+    BASIC = 'basic',
+    ADVANCED = 'advanced',
+    AUTOCOMPLETE = 'autocomplete'
+}
+
+const initialState: SearchBar = {
+    currentView: SearchView.BASIC,
+    open: false,
+    searchResults: [],
+    searchValue: ''
+};
+
+export const searchBarReducer = (state = initialState, action: SearchBarActions): SearchBar =>
+    searchBarActions.match(action, {
+        SET_CURRENT_VIEW: currentView => ({ ...state, currentView }),
+        OPEN_SEARCH_VIEW: () => ({ ...state, open: true }),
+        CLOSE_SEARCH_VIEW: () => ({ ...state, open: false }),
+        SET_SEARCH_RESULTS: (searchResults) => ({ ...state, searchResults }),
+        SET_SEARCH_VALUE: (searchValue) => ({ ...state, searchValue }),
+        default: () => state
+    });
\ No newline at end of file
index 4c08a39c4b4a394ba1a8cde77249ab90c49bf7f1..60267807c700e0f7b43b69be2dc6498ee9988360 100644 (file)
@@ -39,6 +39,8 @@ import { runProcessPanelReducer } from '~/store/run-process-panel/run-process-pa
 import { WorkflowMiddlewareService } from './workflow-panel/workflow-middleware-service';
 import { WORKFLOW_PANEL_ID } from './workflow-panel/workflow-panel-actions';
 import { fileTreePickerReducer } from './file-tree-picker/file-tree-picker-reducer';
+import { appInfoReducer } from '~/store/app-info/app-info-reducer';
+import { searchBarReducer } from './search-bar/search-bar-reducer';
 
 const composeEnhancers =
     (process.env.NODE_ENV === 'development' &&
@@ -101,5 +103,7 @@ const createRootReducer = (services: ServiceRepository) => combineReducers({
     processPanel: processPanelReducer,
     progressIndicator: progressIndicatorReducer,
     fileTreePicker: fileTreePickerReducer,
-    runProcessPanel: runProcessPanelReducer
+    runProcessPanel: runProcessPanelReducer,
+    appInfo: appInfoReducer,
+    searchBar: searchBarReducer
 });
index 4a3f2afeca29887e676912fdf78531159b412a81..7c883cbf91800e54d7817e4d4e8937982c5aac51 100644 (file)
@@ -14,7 +14,6 @@ export const FileSelectionDialog = compose(
     reduxForm({
         form: FILE_SELECTION,
         onSubmit: (data, dispatch) => {
-            console.log(data);
             dispatch(dialogActions.CLOSE_DIALOG({ id: FILE_SELECTION }));
             return data;
         }
index 1072a56f65c65b50dcfe6a880ec1049a7238a7a6..b936fb14d3fbc05412bc76178efbf466fa5dc496 100644 (file)
@@ -7,11 +7,10 @@ import { AppBar, Toolbar, Typography, Grid } from "@material-ui/core";
 import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
 import { Link } from "react-router-dom";
 import { User } from "~/models/user";
-import { SearchBar } from "~/components/search-bar/search-bar";
+import { SearchBar } from "~/views-components/search-bar/search-bar";
 import { Routes } from '~/routes/routes';
 import { NotificationsMenu } from "~/views-components/main-app-bar/notifications-menu";
 import { AccountMenu } from "~/views-components/main-app-bar/account-menu";
-import { AnonymousMenu } from "~/views-components/main-app-bar/anonymous-menu";
 import { HelpMenu } from './help-menu';
 import { ReactNode } from "react";
 
@@ -28,18 +27,12 @@ const styles: StyleRulesCallback<CssRules> = () => ({
 });
 
 interface MainAppBarDataProps {
-    searchText: string;
-    searchDebounce?: number;
     user?: User;
     buildInfo?: string;
     children?: ReactNode;
 }
 
-export interface MainAppBarActionProps {
-    onSearch: (searchText: string) => void;
-}
-
-export type MainAppBarProps = MainAppBarDataProps & MainAppBarActionProps & WithStyles<CssRules>;
+export type MainAppBarProps = MainAppBarDataProps & WithStyles<CssRules>;
 
 export const MainAppBar = withStyles(styles)(
     (props: MainAppBarProps) => {
@@ -59,11 +52,7 @@ export const MainAppBar = withStyles(styles)(
                         xs={6}
                         container
                         alignItems="center">
-                        {props.user && <SearchBar
-                            value={props.searchText}
-                            onSearch={props.onSearch}
-                            debounce={props.searchDebounce}
-                        />}
+                        {props.user && <SearchBar /> }
                     </Grid>
                     <Grid
                         item
diff --git a/src/views-components/search-bar/search-bar-advanced-view.tsx b/src/views-components/search-bar/search-bar-advanced-view.tsx
new file mode 100644 (file)
index 0000000..356eb33
--- /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 { Paper, StyleRulesCallback, withStyles, WithStyles, List, Button } from '@material-ui/core';
+import { SearchView } from '~/store/search-bar/search-bar-reducer';
+import { RecentQueriesItem } from '~/views-components/search-bar/search-bar-view';
+
+type CssRules = 'list';
+
+const styles: StyleRulesCallback<CssRules> = theme => {
+    return {
+        list: {
+            padding: '0px'
+        }
+    };
+};
+
+interface SearchBarAdvancedViewProps {
+    setView: (currentView: string) => void;
+}
+
+export const SearchBarAdvancedView = withStyles(styles)(
+    ({ classes, setView }: SearchBarAdvancedViewProps & WithStyles<CssRules>) =>
+        <Paper>
+            <List component="nav" className={classes.list}>
+                <RecentQueriesItem text='ADVANCED VIEW' />
+            </List>
+            <Button onClick={() => setView(SearchView.BASIC)}>Back</Button>
+        </Paper>
+);
\ No newline at end of file
diff --git a/src/views-components/search-bar/search-bar-autocomplete-view.tsx b/src/views-components/search-bar/search-bar-autocomplete-view.tsx
new file mode 100644 (file)
index 0000000..c4b2457
--- /dev/null
@@ -0,0 +1,40 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { Paper, StyleRulesCallback, withStyles, WithStyles, List, ListItem, ListItemText } from '@material-ui/core';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { RecentQueriesItem } from '~/views-components/search-bar/search-bar-view';
+import { GroupContentsResource } from '~/services/groups-service/groups-service';
+import Highlighter from "react-highlight-words";
+
+type CssRules = 'list';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    list: {
+        padding: 0
+    }
+});
+
+export interface SearchBarAutocompleteViewDataProps {
+    searchResults?: GroupContentsResource[];
+    searchValue?: string;
+}
+
+type SearchBarAutocompleteViewProps = SearchBarAutocompleteViewDataProps & WithStyles<CssRules>;
+
+export const SearchBarAutocompleteView = withStyles(styles)(
+    ({ classes, searchResults, searchValue }: SearchBarAutocompleteViewProps ) =>
+        <Paper>
+            {searchResults &&  <List component="nav" className={classes.list}>
+                {searchResults.map((item: GroupContentsResource) => {
+                    return <RecentQueriesItem key={item.uuid} text={getFormattedText(item.name, searchValue)} />;
+                })}
+            </List>}
+        </Paper>
+);
+
+const getFormattedText = (textToHighlight: string, searchString = '') => {
+    return <Highlighter searchWords={[searchString]} autoEscape={true} textToHighlight={textToHighlight} />;
+};
\ No newline at end of file
diff --git a/src/views-components/search-bar/search-bar-basic-view.tsx b/src/views-components/search-bar/search-bar-basic-view.tsx
new file mode 100644 (file)
index 0000000..c2bca73
--- /dev/null
@@ -0,0 +1,55 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { Paper, StyleRulesCallback, withStyles, WithStyles, List } from '@material-ui/core';
+import { SearchView } from '~/store/search-bar/search-bar-reducer';
+import { RecentQueriesItem, RenderSavedQueries } from '~/views-components/search-bar/search-bar-view';
+
+type CssRules = 'advanced' | 'searchQueryList' | 'list' | 'searchView';
+
+const styles: StyleRulesCallback<CssRules> = theme => {
+    return {
+        advanced: {
+            display: 'flex',
+            justifyContent: 'flex-end',
+            paddingRight: theme.spacing.unit * 2,
+            paddingBottom: theme.spacing.unit,
+            fontSize: '14px',
+            cursor: 'pointer'
+        },
+        searchQueryList: {
+            padding: `${theme.spacing.unit / 2}px ${theme.spacing.unit}px `,
+            background: '#f2f2f2',
+            fontSize: '14px'
+        },
+        list: {
+            padding: '0px'
+        },
+        searchView: {
+            color: theme.palette.common.black
+        }
+    };
+};
+
+interface SearchBarBasicViewProps {
+    setView: (currentView: string) => void;
+}
+
+export const SearchBarBasicView = withStyles(styles)(
+    ({ classes, setView }: SearchBarBasicViewProps & WithStyles<CssRules>) =>
+        <Paper className={classes.searchView}>
+            <div className={classes.searchQueryList}>Saved search queries</div>
+            <List component="nav" className={classes.list}>
+                <RenderSavedQueries text="Test" />
+                <RenderSavedQueries text="Demo" />
+            </List>
+            <div className={classes.searchQueryList}>Recent search queries</div>
+            <List component="nav" className={classes.list}>
+                <RecentQueriesItem text="cos" />
+                <RecentQueriesItem text="testtest" />
+            </List>
+            <div className={classes.advanced} onClick={() => setView(SearchView.ADVANCED)}>Advanced search</div>
+        </Paper>
+);
\ No newline at end of file
similarity index 61%
rename from src/components/search-bar/search-bar.test.tsx
rename to src/views-components/search-bar/search-bar-view.test.tsx
index 07b5ebf6261dbab0849bad7e07df416ddaa49765..4230aa80a68caba45df435075a3268894cd255bc 100644 (file)
@@ -4,7 +4,7 @@
 
 import * as React from "react";
 import { mount, configure } from "enzyme";
-import { SearchBar, DEFAULT_SEARCH_DEBOUNCE } from "./search-bar";
+import { SearchBarView, DEFAULT_SEARCH_DEBOUNCE } from "./search-bar-view";
 
 import * as Adapter from 'enzyme-adapter-react-16';
 
@@ -22,31 +22,31 @@ describe("<SearchBar />", () => {
 
     describe("on submit", () => {
         it("calls onSearch with initial value passed via props", () => {
-            const searchBar = mount(<SearchBar value="initial value" onSearch={onSearch} />);
+            const searchBar = mount(<SearchBarView value="initial value" onSearch={onSearch} currentView='' open={true} onSetView={jest.fn()} openView={jest.fn()} closeView={jest.fn()} />);
             searchBar.find("form").simulate("submit");
             expect(onSearch).toBeCalledWith("initial value");
         });
 
         it("calls onSearch with current value", () => {
-            const searchBar = mount(<SearchBar value="" onSearch={onSearch} />);
+            const searchBar = mount(<SearchBarView value="" onSearch={onSearch} currentView='' open={true} onSetView={jest.fn()} openView={jest.fn()} closeView={jest.fn()} />);
             searchBar.find("input").simulate("change", { target: { value: "current value" } });
             searchBar.find("form").simulate("submit");
             expect(onSearch).toBeCalledWith("current value");
         });
 
         it("calls onSearch with new value passed via props", () => {
-            const searchBar = mount(<SearchBar value="" onSearch={onSearch} />);
+            const searchBar = mount(<SearchBarView value="" onSearch={onSearch} currentView='' open={true} onSetView={jest.fn()} openView={jest.fn()} closeView={jest.fn()} />);
             searchBar.find("input").simulate("change", { target: { value: "current value" } });
-            searchBar.setProps({value: "new value"});
+            searchBar.setProps({ value: "new value" });
             searchBar.find("form").simulate("submit");
             expect(onSearch).toBeCalledWith("new value");
         });
 
         it("cancels timeout set on input value change", () => {
-            const searchBar = mount(<SearchBar value="" onSearch={onSearch} debounce={1000} />);
+            const searchBar = mount(<SearchBarView value="" onSearch={onSearch} debounce={1000} currentView='' open={true} onSetView={jest.fn()} openView={jest.fn()} closeView={jest.fn()} />);
             searchBar.find("input").simulate("change", { target: { value: "current value" } });
             searchBar.find("form").simulate("submit");
-            jest.advanceTimersByTime(1000);
+            jest.runTimersToTime(1000);
             expect(onSearch).toHaveBeenCalledTimes(1);
             expect(onSearch).toBeCalledWith("current value");
         });
@@ -55,40 +55,40 @@ describe("<SearchBar />", () => {
 
     describe("on input value change", () => {
         it("calls onSearch after default timeout", () => {
-            const searchBar = mount(<SearchBar value="" onSearch={onSearch} />);
+            const searchBar = mount(<SearchBarView value="" onSearch={onSearch} currentView='' open={true} onSetView={jest.fn()} openView={jest.fn()} closeView={jest.fn()} />);
             searchBar.find("input").simulate("change", { target: { value: "current value" } });
             expect(onSearch).not.toBeCalled();
-            jest.advanceTimersByTime(DEFAULT_SEARCH_DEBOUNCE);
+            jest.runTimersToTime(DEFAULT_SEARCH_DEBOUNCE);
             expect(onSearch).toBeCalledWith("current value");
         });
 
         it("calls onSearch after the time specified in props has passed", () => {
-            const searchBar = mount(<SearchBar value="" onSearch={onSearch} debounce={2000}/>);
+            const searchBar = mount(<SearchBarView value="" onSearch={onSearch} debounce={2000} currentView='' open={true} onSetView={jest.fn()} openView={jest.fn()} closeView={jest.fn()} />);
             searchBar.find("input").simulate("change", { target: { value: "current value" } });
-            jest.advanceTimersByTime(1000);
+            jest.runTimersToTime(1000);
             expect(onSearch).not.toBeCalled();
-            jest.advanceTimersByTime(1000);
+            jest.runTimersToTime(1000);
             expect(onSearch).toBeCalledWith("current value");
         });
 
         it("calls onSearch only once after no change happened during the specified time", () => {
-            const searchBar = mount(<SearchBar value="" onSearch={onSearch} debounce={1000}/>);
+            const searchBar = mount(<SearchBarView value="" onSearch={onSearch} debounce={1000} currentView='' open={true} onSetView={jest.fn()} openView={jest.fn()} closeView={jest.fn()} />);
             searchBar.find("input").simulate("change", { target: { value: "current value" } });
-            jest.advanceTimersByTime(500);
+            jest.runTimersToTime(500);
             searchBar.find("input").simulate("change", { target: { value: "changed value" } });
-            jest.advanceTimersByTime(1000);
+            jest.runTimersToTime(1000);
             expect(onSearch).toHaveBeenCalledTimes(1);
         });
 
         it("calls onSearch again after the specified time has passed since previous call", () => {
-            const searchBar = mount(<SearchBar value="" onSearch={onSearch} debounce={1000}/>);
+            const searchBar = mount(<SearchBarView value="" onSearch={onSearch} debounce={1000} currentView='' open={true} onSetView={jest.fn()} openView={jest.fn()} closeView={jest.fn()} />);
             searchBar.find("input").simulate("change", { target: { value: "current value" } });
-            jest.advanceTimersByTime(500);
+            jest.runTimersToTime(500);
             searchBar.find("input").simulate("change", { target: { value: "intermediate value" } });
-            jest.advanceTimersByTime(1000);
+            jest.runTimersToTime(1000);
             expect(onSearch).toBeCalledWith("intermediate value");
             searchBar.find("input").simulate("change", { target: { value: "latest value" } });
-            jest.advanceTimersByTime(1000);
+            jest.runTimersToTime(1000);
             expect(onSearch).toBeCalledWith("latest value");
             expect(onSearch).toHaveBeenCalledTimes(2);
 
diff --git a/src/views-components/search-bar/search-bar-view.tsx b/src/views-components/search-bar/search-bar-view.tsx
new file mode 100644 (file)
index 0000000..f26cb7e
--- /dev/null
@@ -0,0 +1,175 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import {
+    IconButton,
+    Paper,
+    StyleRulesCallback,
+    withStyles,
+    WithStyles,
+    Tooltip,
+    InputAdornment, Input,
+    ListItem, ListItemText, ListItemSecondaryAction,
+    ClickAwayListener
+} from '@material-ui/core';
+import SearchIcon from '@material-ui/icons/Search';
+import { RemoveIcon } from '~/components/icon/icon';
+import { SearchView } from '~/store/search-bar/search-bar-reducer';
+import { SearchBarBasicView } from '~/views-components/search-bar/search-bar-basic-view';
+import { SearchBarAdvancedView } from '~/views-components/search-bar/search-bar-advanced-view';
+import { SearchBarAutocompleteView, SearchBarAutocompleteViewDataProps } from '~/views-components/search-bar/search-bar-autocomplete-view';
+import { ArvadosTheme } from '~/common/custom-theme';
+
+type CssRules = 'container' | 'input' | 'searchBar';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => {
+    return {
+        container: {
+            position: 'relative',
+            width: '100%',
+            borderRadius: '0px'
+        },
+        input: {
+            border: 'none',
+            padding: `0px ${theme.spacing.unit}px`
+        },
+        searchBar: {
+            height: '30px'
+        }
+    };
+};
+
+type SearchBarDataProps = {
+    searchValue: string;
+    currentView: string;
+    open: boolean;
+} & SearchBarAutocompleteViewDataProps;
+
+interface SearchBarActionProps {
+    onSearch: (value: string) => any;
+    debounce?: number;
+    onSetView: (currentView: string) => void;
+    openView: () => void;
+    closeView: () => void;
+}
+
+type SearchBarProps = SearchBarDataProps & SearchBarActionProps & WithStyles<CssRules>;
+
+interface SearchBarState {
+    value: string;
+}
+
+interface RenderQueriesProps {
+    text: string | JSX.Element;
+}
+
+export const RecentQueriesItem = (props: RenderQueriesProps) => {
+    return <ListItem button>
+        <ListItemText secondary={props.text} />
+    </ListItem>;
+};
+
+
+export const RenderSavedQueries = (props: RenderQueriesProps) => {
+    return <ListItem button>
+        <ListItemText secondary={props.text} />
+        <ListItemSecondaryAction>
+            <Tooltip title="Remove">
+                <IconButton aria-label="Remove">
+                    <RemoveIcon />
+                </IconButton>
+            </Tooltip>
+        </ListItemSecondaryAction>
+    </ListItem>;
+};
+
+export const DEFAULT_SEARCH_DEBOUNCE = 1000;
+
+export const SearchBarView = withStyles(styles)(
+    class extends React.Component<SearchBarProps> {
+        state: SearchBarState = {
+            value: ""
+        };
+
+        timeout: number;
+
+        render() {
+            const { classes, currentView, openView, closeView, open } = this.props;
+            return <ClickAwayListener onClickAway={() => closeView()}>
+                <Paper className={classes.container} >
+                    <form onSubmit={this.handleSubmit} className={classes.searchBar}>
+                        <Input
+                            className={classes.input}
+                            onChange={this.handleChange}
+                            placeholder="Search"
+                            value={this.state.value}
+                            fullWidth={true}
+                            disableUnderline={true}
+                            onClick={() => openView()}
+                            endAdornment={
+                                <InputAdornment position="end">
+                                    <Tooltip title='Search'>
+                                        <IconButton>
+                                            <SearchIcon />
+                                        </IconButton>
+                                    </Tooltip>
+                                </InputAdornment>
+                            } />
+                        {open && this.getView(currentView)}
+                    </form>
+                </Paper >
+            </ClickAwayListener>;
+        }
+
+        componentDidMount() {
+            this.setState({ value: this.props.searchValue });
+        }
+
+        componentWillReceiveProps(nextProps: SearchBarProps) {
+            if (nextProps.searchValue !== this.props.searchValue) {
+                this.setState({ value: nextProps.searchValue });
+            }
+        }
+
+        componentWillUnmount() {
+            clearTimeout(this.timeout);
+        }
+
+        getView = (currentView: string) => {
+            switch (currentView) {
+                case SearchView.BASIC:
+                    return <SearchBarBasicView setView={this.props.onSetView} />;
+                case SearchView.ADVANCED:
+                    return <SearchBarAdvancedView setView={this.props.onSetView} />;
+                case SearchView.AUTOCOMPLETE:
+                    return <SearchBarAutocompleteView 
+                                searchResults={this.props.searchResults} 
+                                searchValue={this.props.searchValue} />;
+                default:
+                    return <SearchBarBasicView setView={this.props.onSetView} />;
+            }
+        }
+
+        handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
+            event.preventDefault();
+            clearTimeout(this.timeout);
+            this.props.onSearch(this.state.value);
+        }
+
+        handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+            clearTimeout(this.timeout);
+            this.setState({ value: event.target.value });
+            this.timeout = window.setTimeout(
+                () => this.props.onSearch(this.state.value),
+                this.props.debounce || DEFAULT_SEARCH_DEBOUNCE
+            );
+            if (event.target.value.length > 0) {
+                this.props.onSetView(SearchView.AUTOCOMPLETE);
+            } else {
+                this.props.onSetView(SearchView.BASIC);
+            }
+        }
+    }
+);
diff --git a/src/views-components/search-bar/search-bar.tsx b/src/views-components/search-bar/search-bar.tsx
new file mode 100644 (file)
index 0000000..df8808c
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { connect } from 'react-redux';
+import { RootState } from '~/store/store';
+import { Dispatch } from 'redux';
+import { goToView, searchData, searchBarActions } from '~/store/search-bar/search-bar-actions';
+import { SearchBarView } from '~/views-components/search-bar/search-bar-view';
+
+const mapStateToProps = ({ searchBar }: RootState) => {
+    return {
+        searchValue: searchBar.searchValue,
+        currentView: searchBar.currentView,
+        open: searchBar.open,
+        searchResults: searchBar.searchResults
+    };
+};
+
+const mapDispatchToProps = (dispatch: Dispatch) => ({
+    onSearch: (valueSearch: string) => dispatch<any>(searchData(valueSearch)),
+    onSetView: (currentView: string) => dispatch(goToView(currentView)),
+    openView: () => dispatch<any>(searchBarActions.OPEN_SEARCH_VIEW()),
+    closeView: () => dispatch<any>(searchBarActions.CLOSE_SEARCH_VIEW())
+});
+
+export const SearchBar = connect(mapStateToProps, mapDispatchToProps)(SearchBarView);
\ No newline at end of file
diff --git a/src/views/main-panel/main-panel-root.tsx b/src/views/main-panel/main-panel-root.tsx
new file mode 100644 (file)
index 0000000..15149dc
--- /dev/null
@@ -0,0 +1,47 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { StyleRulesCallback, WithStyles, withStyles, Grid, LinearProgress } from '@material-ui/core';
+import { User } from "~/models/user";
+import { ArvadosTheme } from '~/common/custom-theme';
+import { WorkbenchPanel } from '~/views/workbench/workbench';
+import { LoginPanel } from '~/views/login-panel/login-panel';
+import { WorkbenchLoadingScreen } from '~/views/workbench/workbench-loading-screen';
+import { MainAppBar } from '~/views-components/main-app-bar/main-app-bar';
+
+type CssRules = 'root';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    root: {
+        overflow: 'hidden',
+        width: '100vw',
+        height: '100vh'
+    }
+});
+
+export interface MainPanelRootDataProps {
+    user?: User;
+    working: boolean;
+    loading: boolean;
+    buildInfo: string;
+}
+
+type MainPanelRootProps = MainPanelRootDataProps & WithStyles<CssRules>;
+
+export const MainPanelRoot = withStyles(styles)(
+    ({ classes, loading, working, user, buildInfo }: MainPanelRootProps) =>
+        loading 
+            ? <WorkbenchLoadingScreen />
+            : <>
+                <MainAppBar
+                    user={user}
+                    buildInfo={buildInfo}>
+                    {working ? <LinearProgress color="secondary" /> : null}
+                </MainAppBar>
+                <Grid container direction="column" className={classes.root}>
+                    {user ? <WorkbenchPanel /> : <LoginPanel />}
+                </Grid>
+            </>
+);
\ No newline at end of file
index 297a6214505a53628c3d6ec20d4f79cb455c27fa..7592afb388ec031e06a75faaf66abb58352c43f1 100644 (file)
@@ -2,83 +2,21 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import * as React from 'react';
-import { connect, DispatchProp } from 'react-redux';
-import { push } from 'react-router-redux';
-import { LinearProgress, Grid } from '@material-ui/core';
-import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
-import { ArvadosTheme } from '~/common/custom-theme';
 import { RootState } from '~/store/store';
-import { User } from '~/models/user';
-import { WorkbenchPanel } from '~/views/workbench/workbench';
-import { LoginPanel } from '~/views/login-panel/login-panel';
-import { MainAppBar } from '~/views-components/main-app-bar/main-app-bar';
+import { connect } from 'react-redux';
+import { MainPanelRoot, MainPanelRootDataProps } from '~/views/main-panel/main-panel-root';
 import { isSystemWorking } from '~/store/progress-indicator/progress-indicator-reducer';
-import { isWorkbenchLoading } from '../../store/workbench/workbench-actions';
-import { WorkbenchLoadingScreen } from '~/views/workbench/workbench-loading-screen';
+import { isWorkbenchLoading } from '~/store/workbench/workbench-actions';
 
-type CssRules = 'root';
+const mapStateToProps = (state: RootState): MainPanelRootDataProps => {
+    return {
+        user: state.auth.user,
+        working: isSystemWorking(state.progressIndicator),
+        loading: isWorkbenchLoading(state),
+        buildInfo: state.appInfo.buildInfo
+    };
+};
 
-const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
-    root: {
-        overflow: 'hidden',
-        width: '100vw',
-        height: '100vh'
-    }
-});
+const mapDispatchToProps = null;
 
-interface MainPanelDataProps {
-    user?: User;
-    working: boolean;
-    loading: boolean;
-}
-
-interface MainPanelGeneralProps {
-    buildInfo: string;
-}
-
-interface MainPanelState {
-    searchText: string;
-}
-
-type MainPanelProps = MainPanelDataProps & MainPanelGeneralProps & DispatchProp<any> & WithStyles<CssRules>;
-
-export const MainPanel = withStyles(styles)(
-    connect<MainPanelDataProps>(
-        (state: RootState) => ({
-            user: state.auth.user,
-            working: isSystemWorking(state.progressIndicator),
-            loading: isWorkbenchLoading(state)
-        })
-    )(
-        class extends React.Component<MainPanelProps, MainPanelState> {
-            state = {
-                searchText: "",
-            };
-
-            render() {
-                const { classes, user, buildInfo, working, loading } = this.props;
-                const { searchText } = this.state;
-                return loading
-                    ? <WorkbenchLoadingScreen />
-                    : <>
-                        <MainAppBar
-                            searchText={searchText}
-                            user={user}
-                            onSearch={this.onSearch}
-                            buildInfo={buildInfo}>
-                            {working ? <LinearProgress color="secondary" /> : null}
-                        </MainAppBar>
-                        <Grid container direction="column" className={classes.root}>
-                            {user ? <WorkbenchPanel /> : <LoginPanel />}
-                        </Grid>
-                    </>;
-            }
-
-            onSearch = (searchText: string) => {
-                this.setState({ searchText });
-                this.props.dispatch(push(`/search?q=${searchText}`));
-            }
-        }
-    )
-);
\ No newline at end of file
+export const MainPanel = connect(mapStateToProps, mapDispatchToProps)(MainPanelRoot);
\ No newline at end of file
index 32c9d0e8caae560dd4d975b5ae9305e12e1f6506..e6f3764cdd41ddda18bca3247ee8bc4dd8b319de 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
   dependencies:
     regenerator-runtime "^0.12.0"
 
-"@material-ui/core@3.1.1":
-  version "3.1.1"
-  resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-3.1.1.tgz#377b406fd26b88df6626c1697eef3f488fddb553"
+"@material-ui/core@3.1.2":
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-3.1.2.tgz#0ba7c510320c41be672792e3f3ab73ab79ff100f"
   dependencies:
     "@babel/runtime" "7.0.0"
-    "@types/jss" "^9.5.3"
+    "@types/jss" "^9.5.6"
     "@types/react-transition-group" "^2.0.8"
     brcast "^3.0.1"
     classnames "^2.2.5"
   version "4.7.0"
   resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.0.tgz#2fac51050c68f7d6f96c5aafc631132522f4aa3f"
 
-"@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/jest@23.3.3":
+  version "23.3.3"
+  resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.3.tgz#246ebcc52771d2327bb8e37aa971b412d9dc4237"
 
 "@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":
+"@types/json5@^0.0.29":
+  version "0.0.29"
+  resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
+
+"@types/jss@^9.5.6":
   version "9.5.6"
   resolved "https://registry.yarnpkg.com/@types/jss/-/jss-9.5.6.tgz#96e1d246ddfbccc4867494077c714773cf29acde"
   dependencies:
   dependencies:
     "@types/react" "*"
 
+"@types/react-highlight-words@0.12.0":
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/@types/react-highlight-words/-/react-highlight-words-0.12.0.tgz#1193945cb51e222d8042631223f8a9c6c126ff18"
+  dependencies:
+    "@types/react" "*"
+
 "@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/node" "*"
 
+abab@^1.0.3:
+  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"
@@ -225,6 +235,12 @@ acorn-dynamic-import@^2.0.0:
   dependencies:
     acorn "^4.0.3"
 
+acorn-globals@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf"
+  dependencies:
+    acorn "^4.0.4"
+
 acorn-globals@^4.1.0:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.0.tgz#e3b6f8da3c1552a95ae627571f7dd6923bb54103"
@@ -236,7 +252,7 @@ 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:
+acorn@^4.0.3, acorn@^4.0.4:
   version "4.0.13"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
 
@@ -296,6 +312,10 @@ ansi-align@^2.0.0:
   dependencies:
     string-width "^2.0.0"
 
+ansi-escapes@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
+
 ansi-escapes@^3.0.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30"
@@ -304,7 +324,7 @@ ansi-html@0.0.7:
   version "0.0.7"
   resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e"
 
-ansi-regex@^2.0.0:
+ansi-regex@^2.0.0, ansi-regex@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
 
@@ -316,7 +336,7 @@ ansi-styles@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
 
-ansi-styles@^3.2.0, ansi-styles@^3.2.1:
+ansi-styles@^3.0.0, ansi-styles@^3.2.0, ansi-styles@^3.2.1:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
   dependencies:
@@ -474,10 +494,6 @@ assign-symbols@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
 
-astral-regex@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
-
 async-each@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
@@ -707,16 +723,17 @@ babel-helpers@^6.24.1:
     babel-runtime "^6.22.0"
     babel-template "^6.24.1"
 
-babel-jest@^22.1.0, babel-jest@^22.4.4:
-  version "22.4.4"
-  resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-22.4.4.tgz#977259240420e227444ebe49e226a61e49ea659d"
+babel-jest@20.0.3, babel-jest@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-20.0.3.tgz#e4a03b13dc10389e140fc645d09ffc4ced301671"
   dependencies:
-    babel-plugin-istanbul "^4.1.5"
-    babel-preset-jest "^22.4.4"
+    babel-core "^6.0.0"
+    babel-plugin-istanbul "^4.0.0"
+    babel-preset-jest "^20.0.3"
 
-babel-loader@^7.1.2:
-  version "7.1.5"
-  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.5.tgz#e3ee0cd7394aa557e013b02d3e492bfd07aa6d68"
+babel-loader@7.1.2:
+  version "7.1.2"
+  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.2.tgz#f6cbe122710f1aa2af4d881c6d5b54358ca24126"
   dependencies:
     find-cache-dir "^1.0.0"
     loader-utils "^1.0.2"
@@ -742,7 +759,7 @@ babel-plugin-dynamic-import-node@1.1.0:
     babel-template "^6.26.0"
     babel-types "^6.26.0"
 
-babel-plugin-istanbul@^4.1.4, babel-plugin-istanbul@^4.1.5:
+babel-plugin-istanbul@^4.0.0, babel-plugin-istanbul@^4.1.4:
   version "4.1.6"
   resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz#36c59b2192efce81c5b378321b74175add1c9a45"
   dependencies:
@@ -751,6 +768,10 @@ babel-plugin-istanbul@^4.1.4, babel-plugin-istanbul@^4.1.5:
     istanbul-lib-instrument "^1.10.1"
     test-exclude "^4.2.1"
 
+babel-plugin-jest-hoist@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-20.0.3.tgz#afedc853bd3f8dc3548ea671fbe69d03cc2c1767"
+
 babel-plugin-jest-hoist@^22.4.4:
   version "22.4.4"
   resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.4.4.tgz#b9851906eab34c7bf6f8c895a2b08bea1a844c0b"
@@ -1088,14 +1109,20 @@ babel-preset-flow@^6.23.0:
   dependencies:
     babel-plugin-transform-flow-strip-types "^6.22.0"
 
-babel-preset-jest@^22.0.1, babel-preset-jest@^22.4.4:
+babel-preset-jest@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-20.0.3.tgz#cbacaadecb5d689ca1e1de1360ebfc66862c178a"
+  dependencies:
+    babel-plugin-jest-hoist "^20.0.3"
+
+babel-preset-jest@^22.0.1:
   version "22.4.4"
   resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-22.4.4.tgz#ec9fbd8bcd7dfd24b8b5320e0e688013235b7c39"
   dependencies:
     babel-plugin-jest-hoist "^22.4.4"
     babel-plugin-syntax-object-rest-spread "^6.13.0"
 
-babel-preset-react-app@^3.1.1:
+babel-preset-react-app@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/babel-preset-react-app/-/babel-preset-react-app-3.1.2.tgz#49ba3681b917c4e5c73a5249d3ef4c48fae064e2"
   dependencies:
@@ -1387,6 +1414,12 @@ browserslist@^2.1.2, browserslist@^2.5.1:
     caniuse-lite "^1.0.30000792"
     electron-to-chromium "^1.3.30"
 
+bser@1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/bser/-/bser-1.0.2.tgz#381116970b2a6deea5646dd15dd7278444b56169"
+  dependencies:
+    node-int64 "^0.4.0"
+
 bser@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719"
@@ -1508,12 +1541,6 @@ caniuse-lite@^1.0.30000748, caniuse-lite@^1.0.30000792:
   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"
-  resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-1.2.0.tgz#1c5fcc489fd0ab00d4f1ac7ae1072e3173fbab6f"
-  dependencies:
-    rsvp "^3.3.3"
-
 capture-stack-trace@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d"
@@ -1585,7 +1612,7 @@ chokidar@^1.6.0, chokidar@^1.7.0:
   optionalDependencies:
     fsevents "^1.0.0"
 
-chokidar@^2.0.2:
+chokidar@^2.0.0, chokidar@^2.0.2:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26"
   dependencies:
@@ -1847,6 +1874,10 @@ content-disposition@0.5.2:
   version "0.5.2"
   resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
 
+content-type-parser@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.2.tgz#caabe80623e63638b2502fd4c7f12ff4ce2352e7"
+
 content-type@~1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
@@ -2090,6 +2121,12 @@ 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.2.37 < 0.3.0":
+  version "0.2.37"
+  resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54"
+  dependencies:
+    cssom "0.3.x"
+
 cssstyle@^1.0.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.1.1.tgz#18b038a9c44d65f7a8e428a653b9f6fe42faf5fb"
@@ -2269,10 +2306,6 @@ detect-libc@^1.0.2:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
 
-detect-newline@^2.1.0:
-  version "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.4"
   resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
@@ -2574,7 +2607,7 @@ error-ex@^1.2.0:
   dependencies:
     is-arrayish "^0.2.1"
 
-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:
+es-abstract@^1.10.0, es-abstract@^1.5.0, 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:
@@ -2661,7 +2694,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.1:
+escodegen@^1.6.1, escodegen@^1.9.1:
   version "1.11.0"
   resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.0.tgz#b27a9389481d5bfd5bec76f7bb1eb3f8f4556589"
   dependencies:
@@ -2757,10 +2790,6 @@ execa@^0.7.0:
     signal-exit "^3.0.0"
     strip-eof "^1.0.0"
 
-exit@^0.1.2:
-  version "0.1.2"
-  resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
-
 expand-brackets@^0.1.4:
   version "0.1.5"
   resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b"
@@ -2802,7 +2831,7 @@ expect@^22.4.0:
     jest-message-util "^22.4.3"
     jest-regex-util "^22.4.3"
 
-express@^4.13.3:
+express@^4.16.2:
   version "4.16.3"
   resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53"
   dependencies:
@@ -2926,6 +2955,12 @@ faye-websocket@~0.11.0:
   dependencies:
     websocket-driver ">=0.5.1"
 
+fb-watchman@^1.8.0:
+  version "1.9.2"
+  resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-1.9.2.tgz#a24cf47827f82d38fb59a69ad70b76e3b6ae7383"
+  dependencies:
+    bser "1.0.2"
+
 fb-watchman@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58"
@@ -2950,11 +2985,12 @@ figures@^2.0.0:
   dependencies:
     escape-string-regexp "^1.0.5"
 
-file-loader@0.11.2:
-  version "0.11.2"
-  resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.11.2.tgz#4ff1df28af38719a6098093b88c82c71d1794a34"
+file-loader@1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-1.1.5.tgz#91c25b6b6fbe56dae99f10a425fd64933b5c9daa"
   dependencies:
     loader-utils "^1.0.2"
+    schema-utils "^0.3.0"
 
 filename-regex@^2.0.0:
   version "2.0.1"
@@ -3145,7 +3181,7 @@ fs.realpath@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
 
-fsevents@^1.0.0, fsevents@^1.1.3, fsevents@^1.2.2, fsevents@^1.2.3:
+fsevents@^1.0.0, fsevents@^1.1.3, fsevents@^1.2.2:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426"
   dependencies:
@@ -3417,6 +3453,10 @@ he@1.1.x:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
 
+highlight-words-core@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/highlight-words-core/-/highlight-words-core-1.2.0.tgz#232bec301cbf2a4943d335dc748ce70e9024f3b1"
+
 history@^4.7.2:
   version "4.7.2"
   resolved "https://registry.yarnpkg.com/history/-/history-4.7.2.tgz#22b5c7f31633c5b8021c7f4a8a954ac139ee8d5b"
@@ -3469,7 +3509,7 @@ html-comment-regex@^1.1.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e"
 
-html-encoding-sniffer@^1.0.2:
+html-encoding-sniffer@^1.0.1, html-encoding-sniffer@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8"
   dependencies:
@@ -3633,13 +3673,6 @@ import-lazy@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
 
-import-local@^0.1.1:
-  version "0.1.1"
-  resolved "https://registry.yarnpkg.com/import-local/-/import-local-0.1.1.tgz#b1179572aacdc11c6a91009fb430dbcab5f668a8"
-  dependencies:
-    pkg-dir "^2.0.0"
-    resolve-cwd "^2.0.0"
-
 import-local@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc"
@@ -4061,7 +4094,7 @@ isstream@~0.1.2:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
 
-istanbul-api@^1.1.14:
+istanbul-api@^1.1.1:
   version "1.3.7"
   resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.3.7.tgz#a86c770d2b03e11e3f778cd7aedd82d2722092aa"
   dependencies:
@@ -4077,7 +4110,7 @@ istanbul-api@^1.1.14:
     mkdirp "^0.5.1"
     once "^1.4.0"
 
-istanbul-lib-coverage@^1.1.1, istanbul-lib-coverage@^1.2.1:
+istanbul-lib-coverage@^1.0.1, istanbul-lib-coverage@^1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz#ccf7edcd0a0bb9b8f729feeb0930470f9af664f0"
 
@@ -4087,7 +4120,7 @@ istanbul-lib-hook@^1.2.2:
   dependencies:
     append-transform "^0.4.0"
 
-istanbul-lib-instrument@^1.10.1, istanbul-lib-instrument@^1.10.2, istanbul-lib-instrument@^1.8.0:
+istanbul-lib-instrument@^1.10.1, istanbul-lib-instrument@^1.10.2, istanbul-lib-instrument@^1.4.2:
   version "1.10.2"
   resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz#1f55ed10ac3c47f2bdddd5307935126754d0a9ca"
   dependencies:
@@ -4108,7 +4141,7 @@ istanbul-lib-report@^1.1.5:
     path-parse "^1.0.5"
     supports-color "^3.1.2"
 
-istanbul-lib-source-maps@^1.2.1, istanbul-lib-source-maps@^1.2.6:
+istanbul-lib-source-maps@^1.1.0, 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:
@@ -4124,50 +4157,59 @@ istanbul-reports@^1.5.1:
   dependencies:
     handlebars "^4.0.3"
 
-jest-changed-files@^22.2.0:
-  version "22.4.3"
-  resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-22.4.3.tgz#8882181e022c38bd46a2e4d18d44d19d90a90fb2"
-  dependencies:
-    throat "^4.0.0"
+jest-changed-files@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-20.0.3.tgz#9394d5cc65c438406149bef1bf4d52b68e03e3f8"
 
-jest-cli@^22.4.2:
-  version "22.4.4"
-  resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-22.4.4.tgz#68cd2a2aae983adb1e6638248ca21082fd6d9e90"
+jest-cli@^20.0.4:
+  version "20.0.4"
+  resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-20.0.4.tgz#e532b19d88ae5bc6c417e8b0593a6fe954b1dc93"
   dependencies:
-    ansi-escapes "^3.0.0"
-    chalk "^2.0.1"
-    exit "^0.1.2"
-    glob "^7.1.2"
+    ansi-escapes "^1.4.0"
+    callsites "^2.0.0"
+    chalk "^1.1.3"
     graceful-fs "^4.1.11"
-    import-local "^1.0.0"
     is-ci "^1.0.10"
-    istanbul-api "^1.1.14"
-    istanbul-lib-coverage "^1.1.1"
-    istanbul-lib-instrument "^1.8.0"
-    istanbul-lib-source-maps "^1.2.1"
-    jest-changed-files "^22.2.0"
-    jest-config "^22.4.4"
-    jest-environment-jsdom "^22.4.1"
-    jest-get-type "^22.1.0"
-    jest-haste-map "^22.4.2"
-    jest-message-util "^22.4.0"
-    jest-regex-util "^22.1.0"
-    jest-resolve-dependencies "^22.1.0"
-    jest-runner "^22.4.4"
-    jest-runtime "^22.4.4"
-    jest-snapshot "^22.4.0"
-    jest-util "^22.4.1"
-    jest-validate "^22.4.4"
-    jest-worker "^22.2.2"
+    istanbul-api "^1.1.1"
+    istanbul-lib-coverage "^1.0.1"
+    istanbul-lib-instrument "^1.4.2"
+    istanbul-lib-source-maps "^1.1.0"
+    jest-changed-files "^20.0.3"
+    jest-config "^20.0.4"
+    jest-docblock "^20.0.3"
+    jest-environment-jsdom "^20.0.3"
+    jest-haste-map "^20.0.4"
+    jest-jasmine2 "^20.0.4"
+    jest-message-util "^20.0.3"
+    jest-regex-util "^20.0.3"
+    jest-resolve-dependencies "^20.0.3"
+    jest-runtime "^20.0.4"
+    jest-snapshot "^20.0.3"
+    jest-util "^20.0.3"
     micromatch "^2.3.11"
-    node-notifier "^5.2.1"
-    realpath-native "^1.0.0"
-    rimraf "^2.5.4"
+    node-notifier "^5.0.2"
+    pify "^2.3.0"
     slash "^1.0.0"
-    string-length "^2.0.0"
-    strip-ansi "^4.0.0"
+    string-length "^1.0.1"
+    throat "^3.0.0"
     which "^1.2.12"
-    yargs "^10.0.3"
+    worker-farm "^1.3.1"
+    yargs "^7.0.2"
+
+jest-config@^20.0.4:
+  version "20.0.4"
+  resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-20.0.4.tgz#e37930ab2217c913605eff13e7bd763ec48faeea"
+  dependencies:
+    chalk "^1.1.3"
+    glob "^7.1.1"
+    jest-environment-jsdom "^20.0.3"
+    jest-environment-node "^20.0.3"
+    jest-jasmine2 "^20.0.4"
+    jest-matcher-utils "^20.0.3"
+    jest-regex-util "^20.0.3"
+    jest-resolve "^20.0.4"
+    jest-validate "^20.0.3"
+    pretty-format "^20.0.3"
 
 jest-config@^22.0.1, jest-config@^22.4.4:
   version "22.4.4"
@@ -4185,6 +4227,15 @@ jest-config@^22.0.1, jest-config@^22.4.4:
     jest-validate "^22.4.4"
     pretty-format "^22.4.0"
 
+jest-diff@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-20.0.3.tgz#81f288fd9e675f0fb23c75f1c2b19445fe586617"
+  dependencies:
+    chalk "^1.1.3"
+    diff "^3.2.0"
+    jest-matcher-utils "^20.0.3"
+    pretty-format "^20.0.3"
+
 jest-diff@^22.4.0, jest-diff@^22.4.3:
   version "22.4.3"
   resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-22.4.3.tgz#e18cc3feff0aeef159d02310f2686d4065378030"
@@ -4194,11 +4245,17 @@ jest-diff@^22.4.0, jest-diff@^22.4.3:
     jest-get-type "^22.4.3"
     pretty-format "^22.4.3"
 
-jest-docblock@^22.4.0, jest-docblock@^22.4.3:
-  version "22.4.3"
-  resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-22.4.3.tgz#50886f132b42b280c903c592373bb6e93bb68b19"
+jest-docblock@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-20.0.3.tgz#17bea984342cc33d83c50fbe1545ea0efaa44712"
+
+jest-environment-jsdom@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-20.0.3.tgz#048a8ac12ee225f7190417713834bb999787de99"
   dependencies:
-    detect-newline "^2.1.0"
+    jest-mock "^20.0.3"
+    jest-util "^20.0.3"
+    jsdom "^9.12.0"
 
 jest-environment-jsdom@^22.4.1:
   version "22.4.3"
@@ -4208,6 +4265,13 @@ jest-environment-jsdom@^22.4.1:
     jest-util "^22.4.3"
     jsdom "^11.5.1"
 
+jest-environment-node@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-20.0.3.tgz#d488bc4612af2c246e986e8ae7671a099163d403"
+  dependencies:
+    jest-mock "^20.0.3"
+    jest-util "^20.0.3"
+
 jest-environment-node@^22.4.1:
   version "22.4.3"
   resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-22.4.3.tgz#54c4eaa374c83dd52a9da8759be14ebe1d0b9129"
@@ -4219,17 +4283,30 @@ jest-get-type@^22.1.0, jest-get-type@^22.4.3:
   version "22.4.3"
   resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4"
 
-jest-haste-map@^22.4.2:
-  version "22.4.3"
-  resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-22.4.3.tgz#25842fa2ba350200767ac27f658d58b9d5c2e20b"
+jest-haste-map@^20.0.4:
+  version "20.0.5"
+  resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-20.0.5.tgz#abad74efb1a005974a7b6517e11010709cab9112"
   dependencies:
     fb-watchman "^2.0.0"
     graceful-fs "^4.1.11"
-    jest-docblock "^22.4.3"
-    jest-serializer "^22.4.3"
-    jest-worker "^22.4.3"
+    jest-docblock "^20.0.3"
     micromatch "^2.3.11"
-    sane "^2.0.0"
+    sane "~1.6.0"
+    worker-farm "^1.3.1"
+
+jest-jasmine2@^20.0.4:
+  version "20.0.4"
+  resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-20.0.4.tgz#fcc5b1411780d911d042902ef1859e852e60d5e1"
+  dependencies:
+    chalk "^1.1.3"
+    graceful-fs "^4.1.11"
+    jest-diff "^20.0.3"
+    jest-matcher-utils "^20.0.3"
+    jest-matchers "^20.0.3"
+    jest-message-util "^20.0.3"
+    jest-snapshot "^20.0.3"
+    once "^1.4.0"
+    p-map "^1.1.1"
 
 jest-jasmine2@^22.4.4:
   version "22.4.4"
@@ -4247,16 +4324,17 @@ jest-jasmine2@^22.4.4:
     jest-util "^22.4.1"
     source-map-support "^0.5.0"
 
-jest-leak-detector@^22.4.0:
-  version "22.4.3"
-  resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-22.4.3.tgz#2b7b263103afae8c52b6b91241a2de40117e5b35"
-  dependencies:
-    pretty-format "^22.4.3"
-
 jest-localstorage-mock@2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/jest-localstorage-mock/-/jest-localstorage-mock-2.2.0.tgz#ce9a9de01dfdde2ad8aa08adf73acc7e5cc394cf"
 
+jest-matcher-utils@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-20.0.3.tgz#b3a6b8e37ca577803b0832a98b164f44b7815612"
+  dependencies:
+    chalk "^1.1.3"
+    pretty-format "^20.0.3"
+
 jest-matcher-utils@^22.4.0, jest-matcher-utils@^22.4.3:
   version "22.4.3"
   resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz#4632fe428ebc73ebc194d3c7b65d37b161f710ff"
@@ -4265,6 +4343,23 @@ jest-matcher-utils@^22.4.0, jest-matcher-utils@^22.4.3:
     jest-get-type "^22.4.3"
     pretty-format "^22.4.3"
 
+jest-matchers@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/jest-matchers/-/jest-matchers-20.0.3.tgz#ca69db1c32db5a6f707fa5e0401abb55700dfd60"
+  dependencies:
+    jest-diff "^20.0.3"
+    jest-matcher-utils "^20.0.3"
+    jest-message-util "^20.0.3"
+    jest-regex-util "^20.0.3"
+
+jest-message-util@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-20.0.3.tgz#6aec2844306fcb0e6e74d5796c1006d96fdd831c"
+  dependencies:
+    chalk "^1.1.3"
+    micromatch "^2.3.11"
+    slash "^1.0.0"
+
 jest-message-util@^22.4.0, jest-message-util@^22.4.3:
   version "22.4.3"
   resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-22.4.3.tgz#cf3d38aafe4befddbfc455e57d65d5239e399eb7"
@@ -4275,19 +4370,35 @@ jest-message-util@^22.4.0, jest-message-util@^22.4.3:
     slash "^1.0.0"
     stack-utils "^1.0.1"
 
+jest-mock@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-20.0.3.tgz#8bc070e90414aa155c11a8d64c869a0d5c71da59"
+
 jest-mock@^22.4.3:
   version "22.4.3"
   resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-22.4.3.tgz#f63ba2f07a1511772cdc7979733397df770aabc7"
 
+jest-regex-util@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-20.0.3.tgz#85bbab5d133e44625b19faf8c6aa5122d085d762"
+
 jest-regex-util@^22.1.0, jest-regex-util@^22.4.3:
   version "22.4.3"
   resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-22.4.3.tgz#a826eb191cdf22502198c5401a1fc04de9cef5af"
 
-jest-resolve-dependencies@^22.1.0:
-  version "22.4.3"
-  resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-22.4.3.tgz#e2256a5a846732dc3969cb72f3c9ad7725a8195e"
+jest-resolve-dependencies@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-20.0.3.tgz#6e14a7b717af0f2cb3667c549de40af017b1723a"
   dependencies:
-    jest-regex-util "^22.4.3"
+    jest-regex-util "^20.0.3"
+
+jest-resolve@^20.0.4:
+  version "20.0.4"
+  resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-20.0.4.tgz#9448b3e8b6bafc15479444c6499045b7ffe597a5"
+  dependencies:
+    browser-resolve "^1.11.2"
+    is-builtin-module "^1.0.0"
+    resolve "^1.3.2"
 
 jest-resolve@^22.4.2:
   version "22.4.3"
@@ -4296,50 +4407,36 @@ jest-resolve@^22.4.2:
     browser-resolve "^1.11.2"
     chalk "^2.0.1"
 
-jest-runner@^22.4.4:
-  version "22.4.4"
-  resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-22.4.4.tgz#dfca7b7553e0fa617e7b1291aeb7ce83e540a907"
-  dependencies:
-    exit "^0.1.2"
-    jest-config "^22.4.4"
-    jest-docblock "^22.4.0"
-    jest-haste-map "^22.4.2"
-    jest-jasmine2 "^22.4.4"
-    jest-leak-detector "^22.4.0"
-    jest-message-util "^22.4.0"
-    jest-runtime "^22.4.4"
-    jest-util "^22.4.1"
-    jest-worker "^22.2.2"
-    throat "^4.0.0"
-
-jest-runtime@^22.4.4:
-  version "22.4.4"
-  resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-22.4.4.tgz#9ba7792fc75582a5be0f79af6f8fe8adea314048"
+jest-runtime@^20.0.4:
+  version "20.0.4"
+  resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-20.0.4.tgz#a2c802219c4203f754df1404e490186169d124d8"
   dependencies:
     babel-core "^6.0.0"
-    babel-jest "^22.4.4"
-    babel-plugin-istanbul "^4.1.5"
-    chalk "^2.0.1"
+    babel-jest "^20.0.3"
+    babel-plugin-istanbul "^4.0.0"
+    chalk "^1.1.3"
     convert-source-map "^1.4.0"
-    exit "^0.1.2"
     graceful-fs "^4.1.11"
-    jest-config "^22.4.4"
-    jest-haste-map "^22.4.2"
-    jest-regex-util "^22.1.0"
-    jest-resolve "^22.4.2"
-    jest-util "^22.4.1"
-    jest-validate "^22.4.4"
+    jest-config "^20.0.4"
+    jest-haste-map "^20.0.4"
+    jest-regex-util "^20.0.3"
+    jest-resolve "^20.0.4"
+    jest-util "^20.0.3"
     json-stable-stringify "^1.0.1"
     micromatch "^2.3.11"
-    realpath-native "^1.0.0"
-    slash "^1.0.0"
     strip-bom "3.0.0"
-    write-file-atomic "^2.1.0"
-    yargs "^10.0.3"
+    yargs "^7.0.2"
 
-jest-serializer@^22.4.3:
-  version "22.4.3"
-  resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-22.4.3.tgz#a679b81a7f111e4766235f4f0c46d230ee0f7436"
+jest-snapshot@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-20.0.3.tgz#5b847e1adb1a4d90852a7f9f125086e187c76566"
+  dependencies:
+    chalk "^1.1.3"
+    jest-diff "^20.0.3"
+    jest-matcher-utils "^20.0.3"
+    jest-util "^20.0.3"
+    natural-compare "^1.4.0"
+    pretty-format "^20.0.3"
 
 jest-snapshot@^22.4.0:
   version "22.4.3"
@@ -4352,6 +4449,18 @@ jest-snapshot@^22.4.0:
     natural-compare "^1.4.0"
     pretty-format "^22.4.3"
 
+jest-util@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-20.0.3.tgz#0c07f7d80d82f4e5a67c6f8b9c3fe7f65cfd32ad"
+  dependencies:
+    chalk "^1.1.3"
+    graceful-fs "^4.1.11"
+    jest-message-util "^20.0.3"
+    jest-mock "^20.0.3"
+    jest-validate "^20.0.3"
+    leven "^2.1.0"
+    mkdirp "^0.5.1"
+
 jest-util@^22.4.1, jest-util@^22.4.3:
   version "22.4.3"
   resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-22.4.3.tgz#c70fec8eec487c37b10b0809dc064a7ecf6aafac"
@@ -4364,6 +4473,15 @@ jest-util@^22.4.1, jest-util@^22.4.3:
     mkdirp "^0.5.1"
     source-map "^0.6.0"
 
+jest-validate@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-20.0.3.tgz#d0cfd1de4f579f298484925c280f8f1d94ec3cab"
+  dependencies:
+    chalk "^1.1.3"
+    jest-matcher-utils "^20.0.3"
+    leven "^2.1.0"
+    pretty-format "^20.0.3"
+
 jest-validate@^22.4.4:
   version "22.4.4"
   resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-22.4.4.tgz#1dd0b616ef46c995de61810d85f57119dbbcec4d"
@@ -4374,18 +4492,11 @@ jest-validate@^22.4.4:
     leven "^2.1.0"
     pretty-format "^22.4.0"
 
-jest-worker@^22.2.2, jest-worker@^22.4.3:
-  version "22.4.3"
-  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-22.4.3.tgz#5c421417cba1c0abf64bf56bd5fb7968d79dd40b"
-  dependencies:
-    merge-stream "^1.0.1"
-
-jest@22.4.2:
-  version "22.4.2"
-  resolved "https://registry.yarnpkg.com/jest/-/jest-22.4.2.tgz#34012834a49bf1bdd3bc783850ab44e4499afc20"
+jest@20.0.4:
+  version "20.0.4"
+  resolved "https://registry.yarnpkg.com/jest/-/jest-20.0.4.tgz#3dd260c2989d6dad678b1e9cc4d91944f6d602ac"
   dependencies:
-    import-local "^1.0.0"
-    jest-cli "^22.4.2"
+    jest-cli "^20.0.4"
 
 js-base64@^2.1.9:
   version "2.4.9"
@@ -4448,6 +4559,30 @@ jsdom@^11.5.1:
     ws "^5.2.0"
     xml-name-validator "^3.0.0"
 
+jsdom@^9.12.0:
+  version "9.12.0"
+  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.12.0.tgz#e8c546fffcb06c00d4833ca84410fed7f8a097d4"
+  dependencies:
+    abab "^1.0.3"
+    acorn "^4.0.4"
+    acorn-globals "^3.1.0"
+    array-equal "^1.0.0"
+    content-type-parser "^1.0.1"
+    cssom ">= 0.3.2 < 0.4.0"
+    cssstyle ">= 0.2.37 < 0.3.0"
+    escodegen "^1.6.1"
+    html-encoding-sniffer "^1.0.1"
+    nwmatcher ">= 1.3.9 < 2.0.0"
+    parse5 "^1.5.1"
+    request "^2.79.0"
+    sax "^1.2.1"
+    symbol-tree "^3.2.1"
+    tough-cookie "^2.3.2"
+    webidl-conversions "^4.0.0"
+    whatwg-encoding "^1.0.1"
+    whatwg-url "^4.3.0"
+    xml-name-validator "^2.0.1"
+
 jsesc@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
@@ -4878,6 +5013,10 @@ mem@^1.1.0:
   dependencies:
     mimic-fn "^1.0.0"
 
+memoize-one@^4.0.0:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.0.2.tgz#3fb8db695aa14ab9c0f1644e1585a8806adc1aee"
+
 memory-fs@^0.4.0, memory-fs@~0.4.1:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
@@ -4904,12 +5043,6 @@ merge-descriptors@1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
 
-merge-stream@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1"
-  dependencies:
-    readable-stream "^2.0.1"
-
 merge@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da"
@@ -5189,7 +5322,7 @@ node-libs-browser@^2.0.0:
     util "^0.10.3"
     vm-browserify "0.0.4"
 
-node-notifier@^5.2.1:
+node-notifier@^5.0.2:
   version "5.2.1"
   resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.2.1.tgz#fa313dd08f5517db0e2502e5758d664ac69f9dea"
   dependencies:
@@ -5299,6 +5432,10 @@ 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"
 
+"nwmatcher@>= 1.3.9 < 2.0.0":
+  version "1.4.4"
+  resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.4.tgz#2285631f34a95f0d0395cd900c96ed39b58f346e"
+
 nwsapi@^2.0.7:
   version "2.0.9"
   resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.0.9.tgz#77ac0cdfdcad52b6a1151a84e73254edc33ed016"
@@ -5355,13 +5492,6 @@ object.entries@^1.0.4:
     function-bind "^1.1.0"
     has "^1.0.1"
 
-object.getownpropertydescriptors@^2.0.3:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16"
-  dependencies:
-    define-properties "^1.1.2"
-    es-abstract "^1.5.1"
-
 object.omit@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
@@ -5557,6 +5687,10 @@ parse5@4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608"
 
+parse5@^1.5.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94"
+
 parse5@^3.0.1:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c"
@@ -5643,7 +5777,7 @@ performance-now@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
 
-pify@^2.0.0:
+pify@^2.0.0, pify@^2.3.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
 
@@ -5993,6 +6127,13 @@ pretty-error@^2.0.2:
     renderkid "^2.0.1"
     utila "~0.4"
 
+pretty-format@^20.0.3:
+  version "20.0.3"
+  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-20.0.3.tgz#020e350a560a1fe1a98dc3beb6ccffb386de8b14"
+  dependencies:
+    ansi-regex "^2.1.1"
+    ansi-styles "^3.0.0"
+
 pretty-format@^22.4.0, pretty-format@^22.4.3:
   version "22.4.3"
   resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-22.4.3.tgz#f873d780839a9c02e9664c8a082e9ee79eaac16f"
@@ -6189,7 +6330,7 @@ react-copy-to-clipboard@5.0.1:
     copy-to-clipboard "^3"
     prop-types "^15.5.8"
 
-react-dev-utils@^5.0.1:
+react-dev-utils@^5.0.2:
   version "5.0.2"
   resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-5.0.2.tgz#7bb68d2c4f6ffe7ed1184c5b0124fcad692774d2"
   dependencies:
@@ -6240,6 +6381,14 @@ react-event-listener@^0.6.2:
     prop-types "^15.6.0"
     warning "^4.0.1"
 
+react-highlight-words@0.14.0:
+  version "0.14.0"
+  resolved "https://registry.yarnpkg.com/react-highlight-words/-/react-highlight-words-0.14.0.tgz#a1a40ff0a49ce78e7feb375a4e0a5fd1ca9c9609"
+  dependencies:
+    highlight-words-core "^1.2.0"
+    memoize-one "^4.0.0"
+    prop-types "^15.5.8"
+
 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"
@@ -6315,31 +6464,31 @@ react-rte@0.16.1:
     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"
+react-scripts-ts@3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/react-scripts-ts/-/react-scripts-ts-3.1.0.tgz#3f285c54b242ff6ecbfb91785060db10660ee7b0"
   dependencies:
     autoprefixer "7.1.6"
-    babel-jest "^22.1.0"
-    babel-loader "^7.1.2"
-    babel-preset-react-app "^3.1.1"
+    babel-jest "20.0.3"
+    babel-loader "7.1.2"
+    babel-preset-react-app "^3.1.2"
     case-sensitive-paths-webpack-plugin "2.1.1"
     chalk "1.1.3"
     css-loader "0.28.7"
     dotenv "4.0.0"
     dotenv-expand "4.2.0"
     extract-text-webpack-plugin "3.0.2"
-    file-loader "0.11.2"
+    file-loader "1.1.5"
     fork-ts-checker-webpack-plugin "^0.2.8"
     fs-extra "3.0.1"
     html-webpack-plugin "2.29.0"
-    jest "22.4.2"
+    jest "20.0.4"
     object-assign "4.1.1"
     postcss-flexbugs-fixes "3.2.0"
     postcss-loader "2.0.8"
     promise "8.0.1"
     raf "3.4.0"
-    react-dev-utils "^5.0.1"
+    react-dev-utils "^5.0.2"
     resolve "1.6.0"
     source-map-loader "^0.2.1"
     style-loader "0.19.0"
@@ -6350,10 +6499,10 @@ react-scripts-ts@2.17.0:
     tslint "^5.7.0"
     tslint-config-prettier "^1.10.0"
     tslint-react "^3.2.0"
-    uglifyjs-webpack-plugin "^1.1.8"
+    uglifyjs-webpack-plugin "1.2.5"
     url-loader "0.6.2"
     webpack "3.8.1"
-    webpack-dev-server "2.9.4"
+    webpack-dev-server "2.11.3"
     webpack-manifest-plugin "1.3.2"
     whatwg-fetch "2.0.3"
   optionalDependencies:
@@ -6449,12 +6598,6 @@ readdirp@^2.0.0:
     micromatch "^3.1.10"
     readable-stream "^2.0.2"
 
-realpath-native@^1.0.0:
-  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 - 0.30.0":
   version "0.30.0"
   resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.30.0.tgz#82773641b3927e8c7d24a0d87d65aeeba18aabd0"
@@ -6670,7 +6813,7 @@ request-promise-native@^1.0.5:
     stealthy-require "^1.1.0"
     tough-cookie ">=2.3.3"
 
-request@^2.87.0:
+request@^2.79.0, request@^2.87.0:
   version "2.88.0"
   resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
   dependencies:
@@ -6783,10 +6926,6 @@ rst-selector-parser@^2.2.3:
     lodash.flattendeep "^4.4.0"
     nearley "^2.7.10"
 
-rsvp@^3.3.3:
-  version "3.6.2"
-  resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a"
-
 run-async@^2.2.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
@@ -6827,22 +6966,19 @@ safe-regex@^1.1.0:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
 
-sane@^2.0.0:
-  version "2.5.2"
-  resolved "https://registry.yarnpkg.com/sane/-/sane-2.5.2.tgz#b4dc1861c21b427e929507a3e751e2a2cb8ab3fa"
+sane@~1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/sane/-/sane-1.6.0.tgz#9610c452307a135d29c1fdfe2547034180c46775"
   dependencies:
-    anymatch "^2.0.0"
-    capture-exit "^1.2.0"
+    anymatch "^1.3.0"
     exec-sh "^0.2.0"
-    fb-watchman "^2.0.0"
-    micromatch "^3.1.4"
+    fb-watchman "^1.8.0"
+    minimatch "^3.0.2"
     minimist "^1.1.1"
     walker "~1.0.5"
-    watch "~0.18.0"
-  optionalDependencies:
-    fsevents "^1.2.3"
+    watch "~0.10.0"
 
-sax@^1.2.4, sax@~1.2.1:
+sax@^1.2.1, sax@^1.2.4, sax@~1.2.1:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
 
@@ -7031,17 +7167,6 @@ snapdragon@^0.8.1:
     source-map-resolve "^0.5.0"
     use "^3.1.0"
 
-sockjs-client@1.1.4:
-  version "1.1.4"
-  resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.4.tgz#5babe386b775e4cf14e7520911452654016c8b12"
-  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-client@1.1.5:
   version "1.1.5"
   resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.5.tgz#1bb7c0f7222c40f42adf14f4442cbd1269771a83"
@@ -7053,12 +7178,12 @@ sockjs-client@1.1.5:
     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"
+sockjs@0.3.19:
+  version "0.3.19"
+  resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d"
   dependencies:
     faye-websocket "^0.10.0"
-    uuid "^2.0.2"
+    uuid "^3.0.1"
 
 sort-keys@^1.0.0:
   version "1.1.2"
@@ -7239,12 +7364,11 @@ strict-uri-encode@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
 
-string-length@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
+string-length@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac"
   dependencies:
-    astral-regex "^1.0.0"
-    strip-ansi "^4.0.0"
+    strip-ansi "^3.0.0"
 
 string-width@^1.0.1, string-width@^1.0.2:
   version "1.0.2"
@@ -7344,7 +7468,7 @@ supports-color@^4.2.1:
   dependencies:
     has-flag "^2.0.0"
 
-supports-color@^5.3.0, supports-color@^5.4.0:
+supports-color@^5.1.0, supports-color@^5.3.0, supports-color@^5.4.0:
   version "5.5.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
   dependencies:
@@ -7400,7 +7524,7 @@ symbol-observable@^1.0.2, symbol-observable@^1.0.3, symbol-observable@^1.0.4, sy
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
 
-symbol-tree@^3.2.2:
+symbol-tree@^3.2.1, symbol-tree@^3.2.2:
   version "3.2.2"
   resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
 
@@ -7453,9 +7577,9 @@ theming@^1.3.0:
     is-plain-object "^2.0.1"
     prop-types "^15.5.8"
 
-throat@^4.0.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a"
+throat@^3.0.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/throat/-/throat-3.2.0.tgz#50cb0670edbc40237b9e347d7e1f88e4620af836"
 
 through2@^2.0.0:
   version "2.0.3"
@@ -7534,7 +7658,7 @@ 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.4, tough-cookie@~2.4.3:
+tough-cookie@>=2.3.3, tough-cookie@^2.3.2, 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:
@@ -7547,6 +7671,10 @@ tr46@^1.0.1:
   dependencies:
     punycode "^2.1.0"
 
+tr46@~0.0.3:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
+
 trim-newlines@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
@@ -7699,17 +7827,9 @@ uglify-to-browserify@~1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
 
-uglifyjs-webpack-plugin@^0.4.6:
-  version "0.4.6"
-  resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz#b951f4abb6bd617e66f63eb891498e391763e309"
-  dependencies:
-    source-map "^0.5.6"
-    uglify-js "^2.8.29"
-    webpack-sources "^1.0.1"
-
-uglifyjs-webpack-plugin@^1.1.8:
-  version "1.3.0"
-  resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz#75f548160858163a08643e086d5fefe18a5d67de"
+uglifyjs-webpack-plugin@1.2.5:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.5.tgz#2ef8387c8f1a903ec5e44fa36f9f3cbdcea67641"
   dependencies:
     cacache "^10.0.4"
     find-cache-dir "^1.0.0"
@@ -7720,6 +7840,14 @@ uglifyjs-webpack-plugin@^1.1.8:
     webpack-sources "^1.1.0"
     worker-farm "^1.5.2"
 
+uglifyjs-webpack-plugin@^0.4.6:
+  version "0.4.6"
+  resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz#b951f4abb6bd617e66f63eb891498e391763e309"
+  dependencies:
+    source-map "^0.5.6"
+    uglify-js "^2.8.29"
+    webpack-sources "^1.0.1"
+
 underscore@~1.4.4:
   version "1.4.4"
   resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604"
@@ -7855,13 +7983,6 @@ util-deprecate@~1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
 
-util.promisify@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030"
-  dependencies:
-    define-properties "^1.1.2"
-    object.getownpropertydescriptors "^2.0.3"
-
 util@0.10.3:
   version "0.10.3"
   resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
@@ -7886,14 +8007,10 @@ 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.3.2:
+uuid@3.3.2, uuid@^3.0.1, uuid@^3.3.2:
   version "3.3.2"
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
 
-uuid@^2.0.2:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
-
 validate-npm-package-license@^3.0.1:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
@@ -7951,12 +8068,9 @@ warning@^4.0.1:
   dependencies:
     loose-envify "^1.0.0"
 
-watch@~0.18.0:
-  version "0.18.0"
-  resolved "https://registry.yarnpkg.com/watch/-/watch-0.18.0.tgz#28095476c6df7c90c963138990c0a5423eb4b986"
-  dependencies:
-    exec-sh "^0.2.0"
-    minimist "^1.2.0"
+watch@~0.10.0:
+  version "0.10.0"
+  resolved "https://registry.yarnpkg.com/watch/-/watch-0.10.0.tgz#77798b2da0f9910d595f1ace5b0c2258521f21dc"
 
 watchpack@^1.4.0:
   version "1.6.0"
@@ -7972,11 +8086,15 @@ wbuf@^1.1.0, wbuf@^1.7.2:
   dependencies:
     minimalistic-assert "^1.0.0"
 
-webidl-conversions@^4.0.2:
+webidl-conversions@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
+
+webidl-conversions@^4.0.0, webidl-conversions@^4.0.2:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
 
-webpack-dev-middleware@^1.11.0:
+webpack-dev-middleware@1.12.2:
   version "1.12.2"
   resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz#f8fc1120ce3b4fc5680ceecb43d777966b21105e"
   dependencies:
@@ -7986,22 +8104,22 @@ webpack-dev-middleware@^1.11.0:
     range-parser "^1.0.3"
     time-stamp "^2.0.0"
 
-webpack-dev-server@2.9.4:
-  version "2.9.4"
-  resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.9.4.tgz#7883e61759c6a4b33e9b19ec4037bd4ab61428d1"
+webpack-dev-server@2.11.3:
+  version "2.11.3"
+  resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.11.3.tgz#3fd48a402164a6569d94d3d17f131432631b4873"
   dependencies:
     ansi-html "0.0.7"
     array-includes "^3.0.3"
     bonjour "^3.5.0"
-    chokidar "^1.6.0"
+    chokidar "^2.0.0"
     compression "^1.5.2"
     connect-history-api-fallback "^1.3.0"
     debug "^3.1.0"
     del "^3.0.0"
-    express "^4.13.3"
+    express "^4.16.2"
     html-entities "^1.2.0"
     http-proxy-middleware "~0.17.4"
-    import-local "^0.1.1"
+    import-local "^1.0.0"
     internal-ip "1.2.0"
     ip "^1.1.5"
     killable "^1.0.0"
@@ -8010,13 +8128,13 @@ webpack-dev-server@2.9.4:
     portfinder "^1.0.9"
     selfsigned "^1.9.1"
     serve-index "^1.7.2"
-    sockjs "0.3.18"
-    sockjs-client "1.1.4"
+    sockjs "0.3.19"
+    sockjs-client "1.1.5"
     spdy "^3.4.1"
-    strip-ansi "^3.0.1"
-    supports-color "^4.2.1"
-    webpack-dev-middleware "^1.11.0"
-    yargs "^6.6.0"
+    strip-ansi "^3.0.0"
+    supports-color "^5.1.0"
+    webpack-dev-middleware "1.12.2"
+    yargs "6.6.0"
 
 webpack-manifest-plugin@1.3.2:
   version "1.3.2"
@@ -8084,6 +8202,13 @@ 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@^4.3.0:
+  version "4.8.0"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.8.0.tgz#d2981aa9148c1e00a41c5a6131166ab4683bbcc0"
+  dependencies:
+    tr46 "~0.0.3"
+    webidl-conversions "^3.0.0"
+
 whatwg-url@^6.4.1:
   version "6.5.0"
   resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8"
@@ -8142,7 +8267,7 @@ wordwrap@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
 
-worker-farm@^1.5.2:
+worker-farm@^1.3.1, worker-farm@^1.5.2:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0"
   dependencies:
@@ -8159,7 +8284,7 @@ wrappy@1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
 
-write-file-atomic@^2.0.0, write-file-atomic@^2.1.0:
+write-file-atomic@^2.0.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab"
   dependencies:
@@ -8177,6 +8302,10 @@ xdg-basedir@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
 
+xml-name-validator@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635"
+
 xml-name-validator@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
@@ -8207,6 +8336,12 @@ yargs-parser@^4.2.0:
   dependencies:
     camelcase "^3.0.0"
 
+yargs-parser@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a"
+  dependencies:
+    camelcase "^3.0.0"
+
 yargs-parser@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9"
@@ -8219,6 +8354,24 @@ yargs-parser@^8.1.0:
   dependencies:
     camelcase "^4.1.0"
 
+yargs@6.6.0:
+  version "6.6.0"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208"
+  dependencies:
+    camelcase "^3.0.0"
+    cliui "^3.2.0"
+    decamelize "^1.1.1"
+    get-caller-file "^1.0.1"
+    os-locale "^1.4.0"
+    read-pkg-up "^1.0.1"
+    require-directory "^2.1.1"
+    require-main-filename "^1.0.1"
+    set-blocking "^2.0.0"
+    string-width "^1.0.2"
+    which-module "^1.0.0"
+    y18n "^3.2.1"
+    yargs-parser "^4.2.0"
+
 yargs@^10.0.3:
   version "10.1.2"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.1.2.tgz#454d074c2b16a51a43e2fb7807e4f9de69ccb5c5"
@@ -8236,9 +8389,9 @@ yargs@^10.0.3:
     y18n "^3.2.1"
     yargs-parser "^8.1.0"
 
-yargs@^6.6.0:
-  version "6.6.0"
-  resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208"
+yargs@^7.0.2:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"
   dependencies:
     camelcase "^3.0.0"
     cliui "^3.2.0"
@@ -8252,7 +8405,7 @@ yargs@^6.6.0:
     string-width "^1.0.2"
     which-module "^1.0.0"
     y18n "^3.2.1"
-    yargs-parser "^4.2.0"
+    yargs-parser "^5.0.0"
 
 yargs@^8.0.2:
   version "8.0.2"