From: Michal Klobukowski Date: Mon, 18 Jun 2018 11:03:22 +0000 (+0200) Subject: Merge branch 'master' X-Git-Tag: 1.2.0~74^2~1 X-Git-Url: https://git.arvados.org/arvados-workbench2.git/commitdiff_plain/4bbaf2a0cede89ea50d63d210c6631adc1970620?hp=fbe620454ddcba2c96b7a0f6049e05e3d12e5422 Merge branch 'master' Feature #13628 Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski --- diff --git a/package.json b/package.json index 967faf77..fda2ead6 100644 --- a/package.json +++ b/package.json @@ -3,16 +3,16 @@ "version": "0.1.0", "private": true, "dependencies": { - "@material-ui/core": "1.2.0", - "@material-ui/icons": "^1.1.0", - "@types/lodash": "^4.14.109", + "@material-ui/core": "1.2.1", + "@material-ui/icons": "1.1.0", + "@types/lodash": "4.14.109", "axios": "0.18.0", "lodash": "4.17.10", - "react": "16.4.0", - "react-dom": "16.4.0", + "react": "16.4.1", + "react-dom": "16.4.1", "react-redux": "5.0.7", - "react-router": "4.2.0", - "react-router-dom": "4.2.2", + "react-router": "4.3.1", + "react-router-dom": "4.3.1", "react-router-redux": "5.0.0-alpha.9", "react-scripts-ts": "2.16.0", "redux": "4.0.0", @@ -27,13 +27,13 @@ "lint": "tslint src/** -t verbose" }, "devDependencies": { - "@types/enzyme": "^3.1.10", - "@types/enzyme-adapter-react-16": "^1.0.2", - "@types/jest": "23.0.0", - "@types/node": "10.3.0", - "@types/react": "16.3.16", - "@types/react-dom": "16.0.5", - "@types/react-redux": "6.0.1", + "@types/enzyme": "3.1.10", + "@types/enzyme-adapter-react-16": "1.0.2", + "@types/jest": "23.1.0", + "@types/node": "10.3.3", + "@types/react": "16.3.18", + "@types/react-dom": "16.0.6", + "@types/react-redux": "6.0.2", "@types/react-router": "4.0.26", "@types/react-router-dom": "4.2.7", "@types/react-router-redux": "5.0.15", @@ -42,7 +42,7 @@ "enzyme-adapter-react-16": "^1.1.1", "jest-localstorage-mock": "2.2.0", "redux-devtools": "3.4.1", - "typescript": "2.9.1" + "typescript": "2.9.2" }, "moduleNameMapper": { "^~/(.*)$": "/src/$1" diff --git a/src/common/formatters.ts b/src/common/formatters.ts new file mode 100644 index 00000000..1d9a5201 --- /dev/null +++ b/src/common/formatters.ts @@ -0,0 +1,42 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +export const formatDate = (isoDate: string) => { + const date = new Date(isoDate); + return date.toLocaleString(); +}; + +export const formatFileSize = (size?: number) => { + if (typeof size === "number") { + for (const { base, unit } of fileSizes) { + if (size >= base) { + return `${(size / base).toFixed()} ${unit}`; + } + } + } + return ""; +}; + +const fileSizes = [ + { + base: 1000000000000, + unit: "TB" + }, + { + base: 1000000000, + unit: "GB" + }, + { + base: 1000000, + unit: "MB" + }, + { + base: 1000, + unit: "KB" + }, + { + base: 1, + unit: "B" + } +]; \ No newline at end of file diff --git a/src/components/data-explorer/data-explorer.tsx b/src/components/data-explorer/data-explorer.tsx new file mode 100644 index 00000000..9aeb28a7 --- /dev/null +++ b/src/components/data-explorer/data-explorer.tsx @@ -0,0 +1,191 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import * as React from 'react'; +import { DataTable, DataTableProps, DataColumn, ColumnSelector } from "../../components/data-table"; +import { Typography, Grid, ListItem, Divider, List, ListItemIcon, ListItemText, Paper, Toolbar } from '@material-ui/core'; +import IconButton, { IconButtonProps } from '@material-ui/core/IconButton'; +import MoreVertIcon from "@material-ui/icons/MoreVert"; +import Popover from '../popover/popover'; +import { formatFileSize, formatDate } from '../../common/formatters'; +import { DataItem } from './data-item'; + +interface DataExplorerProps { + items: DataItem[]; + onItemClick: (item: DataItem) => void; +} + +type DataExplorerState = Pick, "columns">; + +class DataExplorer extends React.Component { + state: DataExplorerState = { + columns: [ + { + name: "Name", + selected: true, + render: item => this.renderName(item) + }, + { + name: "Status", + selected: true, + render: item => renderStatus(item.status) + }, + { + name: "Type", + selected: true, + render: item => renderType(item.type) + }, + { + name: "Owner", + selected: true, + render: item => renderOwner(item.owner) + }, + { + name: "File size", + selected: true, + render: (item) => renderFileSize(item.fileSize) + }, + { + name: "Last modified", + selected: true, + render: item => renderDate(item.lastModified) + }, + { + name: "Actions", + selected: true, + configurable: false, + renderHeader: () => null, + render: renderItemActions + } + ] + }; + + render() { + return + + + + + + + + ; + } + + toggleColumn = (column: DataColumn) => { + const index = this.state.columns.indexOf(column); + const columns = this.state.columns.slice(0); + columns.splice(index, 1, { ...column, selected: !column.selected }); + this.setState({ columns }); + } + + renderName = (item: DataItem) => + this.props.onItemClick(item)}> + + {renderIcon(item)} + + + + {item.name} + + + + +} + +const renderIcon = (dataItem: DataItem) => { + switch (dataItem.type) { + case "arvados#group": + return ; + case "arvados#groupList": + return ; + default: + return ; + } +}; + +const renderDate = (date: string) => + + {formatDate(date)} + ; + +const renderFileSize = (fileSize?: number) => + + {formatFileSize(fileSize)} + ; + +const renderOwner = (owner: string) => + + {owner} + ; + +const renderType = (type: string) => + + {type} + ; + +const renderStatus = (status?: string) => + + {status || "-"} + ; + +const renderItemActions = () => + + + + {[{ + icon: "fas fa-users", + label: "Share" + }, + { + icon: "fas fa-sign-out-alt", + label: "Move to" + }, + { + icon: "fas fa-star", + label: "Add to favourite" + }, + { + icon: "fas fa-edit", + label: "Rename" + }, + { + icon: "fas fa-copy", + label: "Make a copy" + }, + { + icon: "fas fa-download", + label: "Download" + }].map(renderAction)} + < Divider /> + {renderAction({ icon: "fas fa-trash-alt", label: "Remove" })} + + + ; + +const renderAction = (action: { label: string, icon: string }, index?: number) => + + + + + + {action.label} + + ; + +const ItemActionsTrigger: React.SFC = (props) => + + + ; + +export default DataExplorer; diff --git a/src/components/data-explorer/data-item.ts b/src/components/data-explorer/data-item.ts new file mode 100644 index 00000000..3a809924 --- /dev/null +++ b/src/components/data-explorer/data-item.ts @@ -0,0 +1,12 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +export interface DataItem { + name: string; + type: string; + owner: string; + lastModified: string; + fileSize?: number; + status?: string; +} \ No newline at end of file diff --git a/src/components/data-explorer/index.ts b/src/components/data-explorer/index.ts new file mode 100644 index 00000000..bde402d8 --- /dev/null +++ b/src/components/data-explorer/index.ts @@ -0,0 +1,6 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +export { default as DataExplorer } from "./data-explorer"; +export * from "./data-item"; \ No newline at end of file diff --git a/src/components/data-table/column-selector/column-selector.test.tsx b/src/components/data-table/column-selector/column-selector.test.tsx new file mode 100644 index 00000000..26c16a1b --- /dev/null +++ b/src/components/data-table/column-selector/column-selector.test.tsx @@ -0,0 +1,79 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import * as React from "react"; +import { mount, configure } from "enzyme"; +import * as Adapter from "enzyme-adapter-react-16"; +import ColumnSelector, { ColumnSelectorProps, ColumnSelectorTrigger } from "./column-selector"; +import { DataColumn } from "../data-column"; +import { ListItem, Checkbox } from "@material-ui/core"; + +configure({ adapter: new Adapter() }); + +describe("", () => { + it("shows only configurable columns", () => { + const columns: Array> = [ + { + name: "Column 1", + render: () => , + selected: true + }, + { + name: "Column 2", + render: () => , + selected: true, + configurable: true, + }, + { + name: "Column 3", + render: () => , + selected: true, + configurable: false + } + ]; + const columnsConfigurator = mount(); + columnsConfigurator.find(ColumnSelectorTrigger).simulate("click"); + expect(columnsConfigurator.find(ListItem)).toHaveLength(2); + }); + + it("renders checked checkboxes next to selected columns", () => { + const columns: Array> = [ + { + name: "Column 1", + render: () => , + selected: true + }, + { + name: "Column 2", + render: () => , + selected: false + }, + { + name: "Column 3", + render: () => , + selected: true + } + ]; + const columnsConfigurator = mount(); + columnsConfigurator.find(ColumnSelectorTrigger).simulate("click"); + expect(columnsConfigurator.find(Checkbox).at(0).prop("checked")).toBe(true); + expect(columnsConfigurator.find(Checkbox).at(1).prop("checked")).toBe(false); + expect(columnsConfigurator.find(Checkbox).at(2).prop("checked")).toBe(true); + }); + + it("calls onColumnToggle with clicked column", () => { + const columns: Array> = [ + { + name: "Column 1", + render: () => , + selected: true + } + ]; + const onColumnToggle = jest.fn(); + const columnsConfigurator = mount(); + columnsConfigurator.find(ColumnSelectorTrigger).simulate("click"); + columnsConfigurator.find(ListItem).simulate("click"); + expect(onColumnToggle).toHaveBeenCalledWith(columns[0]); + }); +}); \ No newline at end of file diff --git a/src/components/data-table/column-selector/column-selector.tsx b/src/components/data-table/column-selector/column-selector.tsx new file mode 100644 index 00000000..87d3e8db --- /dev/null +++ b/src/components/data-table/column-selector/column-selector.tsx @@ -0,0 +1,56 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import * as React from 'react'; +import { WithStyles, StyleRulesCallback, Theme, withStyles, IconButton, Paper, List, Checkbox, ListItemText, ListItem } from '@material-ui/core'; +import MenuIcon from "@material-ui/icons/Menu"; +import { DataColumn, isColumnConfigurable } from '../data-column'; +import Popover from "../../popover/popover"; +import { IconButtonProps } from '@material-ui/core/IconButton'; + +export interface ColumnSelectorProps { + columns: Array>; + onColumnToggle: (column: DataColumn) => void; +} + +const ColumnSelector: React.SFC> = ({ columns, onColumnToggle, classes }) => + + + + {columns + .filter(isColumnConfigurable) + .map((column, index) => ( + onColumnToggle(column)}> + + + {column.name} + + + ))} + + + ; + +export const ColumnSelectorTrigger: React.SFC = (props) => + + + ; + +type CssRules = "checkbox"; + +const styles: StyleRulesCallback = (theme: Theme) => ({ + checkbox: { + width: 24, + height: 24 + } +}); + +export default withStyles(styles)(ColumnSelector); diff --git a/src/components/data-table/data-column.ts b/src/components/data-table/data-column.ts new file mode 100644 index 00000000..d3b14736 --- /dev/null +++ b/src/components/data-table/data-column.ts @@ -0,0 +1,16 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +export interface DataColumn { + name: string; + selected: boolean; + configurable?: boolean; + key?: React.Key; + render: (item: T) => React.ReactElement; + renderHeader?: () => React.ReactElement | null; +} + +export const isColumnConfigurable = (column: DataColumn) => { + return column.configurable === undefined || column.configurable; +}; \ No newline at end of file diff --git a/src/components/data-table/data-table.test.tsx b/src/components/data-table/data-table.test.tsx new file mode 100644 index 00000000..4a34a6b7 --- /dev/null +++ b/src/components/data-table/data-table.test.tsx @@ -0,0 +1,107 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import * as React from "react"; +import { mount, configure } from "enzyme"; +import * as Adapter from "enzyme-adapter-react-16"; +import DataTable from "./data-table"; +import { DataColumn } from "./data-column"; +import { TableHead, TableCell, Typography, TableBody, Button } from "@material-ui/core"; + +configure({ adapter: new Adapter() }); + +describe("", () => { + it("shows only selected columns", () => { + const columns: Array> = [ + { + name: "Column 1", + render: () => , + selected: true + }, + { + name: "Column 2", + render: () => , + selected: true + }, + { + name: "Column 3", + render: () => , + selected: false + } + ]; + const dataTable = mount(); + expect(dataTable.find(TableHead).find(TableCell)).toHaveLength(2); + }); + + it("renders column name", () => { + const columns: Array> = [ + { + name: "Column 1", + render: () => , + selected: true + } + ]; + const dataTable = mount(); + expect(dataTable.find(TableHead).find(TableCell).text()).toBe("Column 1"); + }); + + it("uses renderHeader instead of name prop", () => { + const columns: Array> = [ + { + name: "Column 1", + renderHeader: () => Column Header, + render: () => , + selected: true + } + ]; + const dataTable = mount(); + expect(dataTable.find(TableHead).find(TableCell).text()).toBe("Column Header"); + }); + + it("passes column key prop to corresponding cells", () => { + const columns: Array> = [ + { + name: "Column 1", + key: "column-1-key", + render: () => , + selected: true + } + ]; + const dataTable = mount(); + expect(dataTable.find(TableHead).find(TableCell).key()).toBe("column-1-key"); + expect(dataTable.find(TableBody).find(TableCell).key()).toBe("column-1-key"); + }); + + it("shows information that items array is empty", () => { + const columns: Array> = [ + { + name: "Column 1", + render: () => , + selected: true + } + ]; + const dataTable = mount(); + expect(dataTable.find(Typography).text()).toBe("No items"); + }); + + it("renders items", () => { + const columns: Array> = [ + { + name: "Column 1", + render: (item) => {item}, + selected: true + }, + { + name: "Column 2", + render: (item) => , + selected: true + } + ]; + const dataTable = mount(); + expect(dataTable.find(TableBody).find(Typography).text()).toBe("item 1"); + expect(dataTable.find(TableBody).find(Button).text()).toBe("item 1"); + }); + + +}); \ No newline at end of file diff --git a/src/components/data-table/data-table.tsx b/src/components/data-table/data-table.tsx new file mode 100644 index 00000000..e7ce03ad --- /dev/null +++ b/src/components/data-table/data-table.tsx @@ -0,0 +1,74 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import * as React from 'react'; +import { Table, TableBody, TableRow, TableCell, TableHead, StyleRulesCallback, Theme, WithStyles, withStyles, Typography } from '@material-ui/core'; +import { DataColumn } from './data-column'; + +export interface DataTableProps { + items: T[]; + columns: Array>; + onItemClick?: (item: T) => void; +} + +class DataTable extends React.Component & WithStyles> { + render() { + const { items, columns, classes, onItemClick } = this.props; + return
+ {items.length > 0 ? + + + + {columns + .filter(column => column.selected) + .map(({ name, renderHeader, key }, index) => + + {renderHeader ? renderHeader() : name} + + )} + + + + {items + .map((item, index) => + onItemClick && onItemClick(item)}> + {columns + .filter(column => column.selected) + .map((column, index) => ( + + {column.render(item)} + + ))} + + )} + +
: + No items + } +
; + } +} + +type CssRules = "tableBody" | "tableContainer" | "noItemsInfo"; + +const styles: StyleRulesCallback = (theme: Theme) => ({ + tableContainer: { + overflowX: 'auto' + }, + tableBody: { + background: theme.palette.background.paper + }, + noItemsInfo: { + textAlign: "center", + padding: theme.spacing.unit + } +}); + +export default withStyles(styles)(DataTable); diff --git a/src/components/data-table/index.ts b/src/components/data-table/index.ts new file mode 100644 index 00000000..f35754b1 --- /dev/null +++ b/src/components/data-table/index.ts @@ -0,0 +1,9 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +export * from "./data-column"; +export * from "./column-selector/column-selector"; +export { default as ColumnSelector } from "./column-selector/column-selector"; +export * from "./data-table"; +export { default as DataTable } from "./data-table"; \ No newline at end of file diff --git a/src/components/popover/popover.test.tsx b/src/components/popover/popover.test.tsx new file mode 100644 index 00000000..fa24c0cd --- /dev/null +++ b/src/components/popover/popover.test.tsx @@ -0,0 +1,69 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import * as React from "react"; +import { mount, configure } from "enzyme"; +import * as Adapter from "enzyme-adapter-react-16"; + +import Popover, { DefaultTrigger } from "./popover"; +import Button, { ButtonProps } from "@material-ui/core/Button"; + +configure({ adapter: new Adapter() }); + +describe("", () => { + it("opens on default trigger click", () => { + const popover = mount(); + popover.find(DefaultTrigger).simulate("click"); + expect(popover.state().anchorEl).toBeDefined(); + }); + + it("renders custom trigger", () => { + const popover = mount(); + expect(popover.find(Button).text()).toBe("Open popover"); + }); + + it("opens on custom trigger click", () => { + const popover = mount(); + popover.find(CustomTrigger).simulate("click"); + expect(popover.state().anchorEl).toBeDefined(); + }); + + it("renders children when opened", () => { + const popover = mount( + + + + ); + popover.find(DefaultTrigger).simulate("click"); + expect(popover.find(CustomTrigger)).toHaveLength(1); + }); + + it("does not close if closeOnContentClick is not set", () => { + const popover = mount( + + + + ); + popover.find(DefaultTrigger).simulate("click"); + popover.find(CustomTrigger).simulate("click"); + expect(popover.state().anchorEl).toBeDefined(); + }); + it("closes on content click if closeOnContentClick is set", () => { + const popover = mount( + + + + ); + popover.find(DefaultTrigger).simulate("click"); + popover.find(CustomTrigger).simulate("click"); + expect(popover.state().anchorEl).toBeUndefined(); + }); + +}); + +const CustomTrigger: React.SFC = (props) => ( + +); \ No newline at end of file diff --git a/src/components/popover/popover.tsx b/src/components/popover/popover.tsx new file mode 100644 index 00000000..c8d40338 --- /dev/null +++ b/src/components/popover/popover.tsx @@ -0,0 +1,69 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import * as React from 'react'; +import { Popover as MaterialPopover } from '@material-ui/core'; + +import { PopoverOrigin } from '@material-ui/core/Popover'; +import IconButton, { IconButtonProps } from '@material-ui/core/IconButton'; + +export interface PopoverProps { + triggerComponent?: React.ComponentType<{ onClick: (event: React.MouseEvent) => void }>; + closeOnContentClick?: boolean; +} + + +class Popover extends React.Component { + + state = { + anchorEl: undefined + }; + + transformOrigin: PopoverOrigin = { + vertical: "top", + horizontal: "right", + }; + + render() { + const Trigger = this.props.triggerComponent || DefaultTrigger; + return ( + <> + + + {this.props.children} + + + ); + } + + handleClose = () => { + this.setState({ anchorEl: undefined }); + } + + handleTriggerClick = (event: React.MouseEvent) => { + this.setState({ anchorEl: event.currentTarget }); + } + + handleSelfClick = () => { + if (this.props.closeOnContentClick) { + this.handleClose(); + } + } + +} + +export const DefaultTrigger: React.SFC = (props) => ( + + + +); + +export default Popover; diff --git a/src/components/project-tree/project-tree.test.tsx b/src/components/project-tree/project-tree.test.tsx index d42df088..932a29cc 100644 --- a/src/components/project-tree/project-tree.test.tsx +++ b/src/components/project-tree/project-tree.test.tsx @@ -8,6 +8,7 @@ import * as Enzyme from 'enzyme'; import * as Adapter from 'enzyme-adapter-react-16'; import ListItemIcon from '@material-ui/core/ListItemIcon'; import { Collapse } from '@material-ui/core'; +import CircularProgress from '@material-ui/core/CircularProgress'; import ProjectTree from './project-tree'; import { TreeItem } from '../tree/tree'; @@ -16,7 +17,7 @@ Enzyme.configure({ adapter: new Adapter() }); describe("ProjectTree component", () => { - it("checks is there ListItemIcon in the ProjectTree component", () => { + it("should render ListItemIcon", () => { const project: TreeItem = { data: { name: "sample name", @@ -28,14 +29,15 @@ describe("ProjectTree component", () => { }, id: "3", open: true, - active: true + active: true, + status: 1 }; const wrapper = mount( { }} />); - expect(wrapper.find(ListItemIcon).length).toEqual(1); + expect(wrapper.find(ListItemIcon)).toHaveLength(1); }); - it("checks are there two ListItemIcon's in the ProjectTree component", () => { + it("should render 2 ListItemIcons", () => { const project: Array> = [ { data: { @@ -48,7 +50,8 @@ describe("ProjectTree component", () => { }, id: "3", open: false, - active: true + active: true, + status: 1 }, { data: { @@ -61,15 +64,54 @@ describe("ProjectTree component", () => { }, id: "3", open: false, - active: true + active: true, + status: 1 } ]; const wrapper = mount( { }} />); - expect(wrapper.find(ListItemIcon).length).toEqual(2); + expect(wrapper.find(ListItemIcon)).toHaveLength(2); }); - it("check ProjectTree, when open is changed", () => { + it("should render Collapse", () => { + const project: Array> = [ + { + data: { + name: "sample name", + createdAt: "2018-06-12", + modifiedAt: "2018-06-13", + uuid: "uuid", + ownerUuid: "ownerUuid", + href: "href", + }, + id: "3", + open: true, + active: true, + status: 2, + items: [ + { + data: { + name: "sample name", + createdAt: "2018-06-12", + modifiedAt: "2018-06-13", + uuid: "uuid", + ownerUuid: "ownerUuid", + href: "href", + }, + id: "3", + open: true, + active: true, + status: 1 + } + ] + } + ]; + const wrapper = mount( { }} />); + + expect(wrapper.find(Collapse)).toHaveLength(1); + }); + + it("should render CircularProgress", () => { const project: TreeItem = { data: { name: "sample name", @@ -80,27 +122,12 @@ describe("ProjectTree component", () => { href: "href", }, id: "3", - open: true, + open: false, active: true, - items: [ - { - data: { - name: "sample name", - createdAt: "2018-06-12", - modifiedAt: "2018-06-13", - uuid: "uuid", - ownerUuid: "ownerUuid", - href: "href", - }, - id: "4", - open: false, - active: true - } - ] + status: 1 }; const wrapper = mount( { }} />); - wrapper.setState({open: true }); - expect(wrapper.find(Collapse).length).toEqual(1); + expect(wrapper.find(CircularProgress)).toHaveLength(1); }); }); diff --git a/src/components/project-tree/project-tree.tsx b/src/components/project-tree/project-tree.tsx index 43ad312a..275805ff 100644 --- a/src/components/project-tree/project-tree.tsx +++ b/src/components/project-tree/project-tree.tsx @@ -9,7 +9,7 @@ import ListItemText from "@material-ui/core/ListItemText/ListItemText"; import ListItemIcon from '@material-ui/core/ListItemIcon'; import Typography from '@material-ui/core/Typography'; -import Tree, { TreeItem } from '../tree/tree'; +import Tree, { TreeItem, TreeItemStatus } from '../tree/tree'; import { Project } from '../../models/project'; type CssRules = 'active' | 'listItemText' | 'row' | 'treeContainer'; @@ -27,9 +27,9 @@ const styles: StyleRulesCallback = (theme: Theme) => ({ marginLeft: '20px', }, treeContainer: { - position: 'absolute', + marginTop: '37px', overflowX: 'visible', - marginTop: '80px', + overflowY: 'auto', minWidth: '240px', whiteSpace: 'nowrap', } @@ -37,7 +37,7 @@ const styles: StyleRulesCallback = (theme: Theme) => ({ export interface ProjectTreeProps { projects: Array>; - toggleProjectTreeItem: (id: string) => void; + toggleProjectTreeItem: (id: string, status: TreeItemStatus) => void; } class ProjectTree extends React.Component> { diff --git a/src/components/tree/tree.test.tsx b/src/components/tree/tree.test.tsx index ffdc74f9..bb0499f8 100644 --- a/src/components/tree/tree.test.tsx +++ b/src/components/tree/tree.test.tsx @@ -1,7 +1,55 @@ // Copyright (C) The Arvados Authors. All rights reserved. // // SPDX-License-Identifier: AGPL-3.0 +import * as React from 'react'; +import { mount } from 'enzyme'; +import * as Enzyme from 'enzyme'; +import * as Adapter from 'enzyme-adapter-react-16'; +import { Collapse } from '@material-ui/core'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import ListItem from "@material-ui/core/ListItem/ListItem"; -it("should render the tree", () => { - expect(true).toBe(true); -}); \ No newline at end of file +import Tree, {TreeItem} from './tree'; +import { Project } from '../../models/project'; +Enzyme.configure({ adapter: new Adapter() }); + +describe("Tree component", () => { + + it("should render ListItem", () => { + const project: TreeItem = { + data: { + name: "sample name", + createdAt: "2018-06-12", + modifiedAt: "2018-06-13", + uuid: "uuid", + ownerUuid: "ownerUuid", + href: "href", + }, + id: "3", + open: true, + active: true, + status: 1, + }; + const wrapper = mount(
} toggleItem={() => { }} items={[project]}/>) + expect(wrapper.find(ListItem)).toHaveLength(1); + }); + + it("should render arrow", () => { + const project: TreeItem = { + data: { + name: "sample name", + createdAt: "2018-06-12", + modifiedAt: "2018-06-13", + uuid: "uuid", + ownerUuid: "ownerUuid", + href: "href", + }, + id: "3", + open: true, + active: true, + status: 1, + }; + const wrapper = mount(
} toggleItem={() => { }} items={[project]}/>) + expect(wrapper.find('i')).toHaveLength(1); + }); +}); diff --git a/src/components/tree/tree.tsx b/src/components/tree/tree.tsx index fb0df58e..6731950c 100644 --- a/src/components/tree/tree.tsx +++ b/src/components/tree/tree.tsx @@ -8,8 +8,10 @@ import ListItem from "@material-ui/core/ListItem/ListItem"; import { StyleRulesCallback, Theme, withStyles, WithStyles } from '@material-ui/core/styles'; import { ReactElement } from "react"; import Collapse from "@material-ui/core/Collapse/Collapse"; +import CircularProgress from '@material-ui/core/CircularProgress'; +import { inherits } from 'util'; -type CssRules = 'list' | 'activeArrow' | 'arrow' | 'arrowRotate'; +type CssRules = 'list' | 'activeArrow' | 'inactiveArrow' | 'arrowRotate' | 'arrowTransition' | 'loader' | 'arrowVisibility'; const styles: StyleRulesCallback = (theme: Theme) => ({ list: { @@ -20,53 +22,79 @@ const styles: StyleRulesCallback = (theme: Theme) => ({ color: '#4285F6', position: 'absolute', }, - arrow: { + inactiveArrow: { position: 'absolute', }, + arrowTransition: { + transition: 'all 0.1s ease', + }, arrowRotate: { + transition: 'all 0.1s ease', transform: 'rotate(-90deg)', + }, + arrowVisibility: { + opacity: 0, + }, + loader: { + position: 'absolute', + transform: 'translate(0px)', + top: '3px' } }); +export enum TreeItemStatus { + Initial, + Pending, + Loaded +} + export interface TreeItem { data: T; id: string; open: boolean; active: boolean; + status: TreeItemStatus; + toggled?: boolean; items?: Array>; } interface TreeProps { items?: Array>; render: (item: TreeItem, level?: number) => ReactElement<{}>; - toggleItem: (id: string) => any; + toggleItem: (id: string, status: TreeItemStatus) => any; level?: number; } class Tree extends React.Component & WithStyles, {}> { - renderArrow (items: boolean, arrowClass: string, open: boolean){ - return ; + renderArrow(status: TreeItemStatus, arrowClass: string, open: boolean, id: string) { + return this.props.toggleItem(id, status)} + className={` + ${arrowClass} + ${status === TreeItemStatus.Pending ? this.props.classes.arrowVisibility : ''} + ${open ? `fas fa-caret-down ${this.props.classes.arrowTransition}` : `fas fa-caret-down ${this.props.classes.arrowRotate}`}`} />; } render(): ReactElement { const level = this.props.level ? this.props.level : 0; - const {classes, render, toggleItem, items} = this.props; - const {list, arrow, activeArrow} = classes; + const { classes, render, toggleItem, items } = this.props; + const { list, inactiveArrow, activeArrow, loader } = classes; return {items && items.map((it: TreeItem, idx: number) => -
- toggleItem(it.id)} className={list} style={{paddingLeft: (level + 1) * 20}}> - {this.renderArrow(true, it.active ? activeArrow : arrow, it.open)} - {render(it, level)} - - {it.items && it.items.length > 0 && - - - } -
)} +
+ + {it.status === TreeItemStatus.Pending ? : null} + {it.toggled && it.items && it.items.length === 0 ? null : this.renderArrow(it.status, it.active ? activeArrow : inactiveArrow, it.open, it.id)} + {render(it, level)} + + {it.items && it.items.length > 0 && + + + } +
)}
; } } diff --git a/src/models/project.ts b/src/models/project.ts index 83fb59bd..830621b4 100644 --- a/src/models/project.ts +++ b/src/models/project.ts @@ -9,4 +9,5 @@ export interface Project { uuid: string; ownerUuid: string; href: string; + kind: string; } diff --git a/src/services/project-service/project-service.ts b/src/services/project-service/project-service.ts index dbc0f927..119cfece 100644 --- a/src/services/project-service/project-service.ts +++ b/src/services/project-service/project-service.ts @@ -34,7 +34,7 @@ interface GroupsResponse { export default class ProjectService { public getProjectList = (parentUuid?: string) => (dispatch: Dispatch): Promise => { - dispatch(actions.PROJECTS_REQUEST()); + dispatch(actions.PROJECTS_REQUEST(parentUuid)); if (parentUuid) { const fb = new FilterBuilder(); fb.addLike(FilterField.OWNER_UUID, parentUuid); @@ -47,7 +47,8 @@ export default class ProjectService { modifiedAt: g.modified_at, href: g.href, uuid: g.uuid, - ownerUuid: g.owner_uuid + ownerUuid: g.owner_uuid, + kind: g.kind } as Project)); dispatch(actions.PROJECTS_SUCCESS({projects, parentItemId: parentUuid})); return projects; diff --git a/src/store/project/project-action.ts b/src/store/project/project-action.ts index 87ecbda9..2856de66 100644 --- a/src/store/project/project-action.ts +++ b/src/store/project/project-action.ts @@ -8,7 +8,7 @@ import { default as unionize, ofType, UnionOf } from "unionize"; const actions = unionize({ CREATE_PROJECT: ofType(), REMOVE_PROJECT: ofType(), - PROJECTS_REQUEST: {}, + PROJECTS_REQUEST: ofType(), PROJECTS_SUCCESS: ofType<{ projects: Project[], parentItemId?: string }>(), TOGGLE_PROJECT_TREE_ITEM: ofType() }, { diff --git a/src/store/project/project-reducer.test.ts b/src/store/project/project-reducer.test.ts index 3e828830..311ec9d1 100644 --- a/src/store/project/project-reducer.test.ts +++ b/src/store/project/project-reducer.test.ts @@ -4,7 +4,7 @@ import projectsReducer, { findTreeBranch } from "./project-reducer"; import actions from "./project-action"; -import { TreeItem } from "../../components/tree/tree"; +import { TreeItem, TreeItemStatus } from "../../components/tree/tree"; describe('project-reducer', () => { it('should add new project to the list', () => { @@ -15,7 +15,8 @@ describe('project-reducer', () => { createdAt: '2018-01-01', modifiedAt: '2018-01-01', ownerUuid: 'owner-test123', - uuid: 'test123' + uuid: 'test123', + kind: "" }; const state = projectsReducer(initialState, actions.CREATE_PROJECT(project)); @@ -30,24 +31,27 @@ describe('project-reducer', () => { createdAt: '2018-01-01', modifiedAt: '2018-01-01', ownerUuid: 'owner-test123', - uuid: 'test123' + uuid: 'test123', + kind: "" }; const projects = [project, project]; const state = projectsReducer(initialState, actions.PROJECTS_SUCCESS({ projects, parentItemId: undefined })); expect(state).toEqual([{ - active: false, - open: false, - id: "test123", - items: [], - data: project - }, { - active: false, - open: false, - id: "test123", - items: [], - data: project - } + active: false, + open: false, + id: "test123", + items: [], + data: project, + status: 0 + }, { + active: false, + open: false, + id: "test123", + items: [], + data: project, + status: 0 + } ]); }); }); @@ -60,6 +64,7 @@ describe("findTreeBranch", () => { active: false, data: "", open: false, + status: TreeItemStatus.Initial }); it("should return an array that matches path to the given item", () => { diff --git a/src/store/project/project-reducer.ts b/src/store/project/project-reducer.ts index ac6d4b73..8770391a 100644 --- a/src/store/project/project-reducer.ts +++ b/src/store/project/project-reducer.ts @@ -4,12 +4,12 @@ import { Project } from "../../models/project"; import actions, { ProjectAction } from "./project-action"; -import { TreeItem } from "../../components/tree/tree"; +import { TreeItem, TreeItemStatus } from "../../components/tree/tree"; import * as _ from "lodash"; export type ProjectState = Array>; -function findTreeItem(tree: Array>, itemId: string): TreeItem | undefined { +export function findTreeItem(tree: Array>, itemId: string): TreeItem | undefined { let item; for (const t of tree) { item = t.id === itemId @@ -47,11 +47,15 @@ function updateProjectTree(tree: Array>, projects: Project[], let treeItem; if (parentItemId) { treeItem = findTreeItem(tree, parentItemId); + if (treeItem) { + treeItem.status = TreeItemStatus.Loaded; + } } const items = projects.map((p, idx) => ({ id: p.uuid, open: false, active: false, + status: TreeItemStatus.Initial, data: p, items: [] } as TreeItem)); @@ -64,12 +68,18 @@ function updateProjectTree(tree: Array>, projects: Project[], return items; } - const projectsReducer = (state: ProjectState = [], action: ProjectAction) => { return actions.match(action, { CREATE_PROJECT: project => [...state, project], REMOVE_PROJECT: () => state, - PROJECTS_REQUEST: () => state, + PROJECTS_REQUEST: itemId => { + const tree = _.cloneDeep(state); + const item = findTreeItem(tree, itemId); + if (item) { + item.status = TreeItemStatus.Pending; + } + return tree; + }, PROJECTS_SUCCESS: ({ projects, parentItemId }) => { return updateProjectTree(state, projects, parentItemId); }, @@ -80,6 +90,7 @@ const projectsReducer = (state: ProjectState = [], action: ProjectAction) => { if (item) { item.open = !item.open; item.active = true; + item.toggled = true; } return tree; }, diff --git a/src/views/data-explorer/data-explorer.tsx b/src/views/data-explorer/data-explorer.tsx new file mode 100644 index 00000000..5f17b638 --- /dev/null +++ b/src/views/data-explorer/data-explorer.tsx @@ -0,0 +1,62 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import * as React from 'react'; +import { DataTableProps } from "../../components/data-table"; +import { RouteComponentProps } from 'react-router'; +import { Project } from '../../models/project'; +import { ProjectState, findTreeItem } from '../../store/project/project-reducer'; +import { RootState } from '../../store/store'; +import { connect, DispatchProp } from 'react-redux'; +import { push } from 'react-router-redux'; +import projectActions from "../../store/project/project-action"; +import { DataExplorer, DataItem } from '../../components/data-explorer'; +import { TreeItem } from '../../components/tree/tree'; + +interface DataExplorerViewDataProps { + projects: ProjectState; +} + +type DataExplorerViewProps = DataExplorerViewDataProps & RouteComponentProps<{ name: string }> & DispatchProp; + +type DataExplorerViewState = Pick, "columns">; + +interface MappedProjectItem extends DataItem { + uuid: string; +} + +class DataExplorerView extends React.Component { + + render() { + const project = findTreeItem(this.props.projects, this.props.match.params.name); + const projectItems = project && project.items || []; + return ( + + ); + } + + goToProject = (project: MappedProjectItem) => { + this.props.dispatch(push(`/project/${project.uuid}`)); + this.props.dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM(project.uuid)); + } + +} + +const mapTreeItem = (item: TreeItem): MappedProjectItem => ({ + name: item.data.name, + type: item.data.kind, + owner: item.data.ownerUuid, + lastModified: item.data.modifiedAt, + uuid: item.data.uuid +}); + + +export default connect( + (state: RootState) => ({ + projects: state.projects + }) +)(DataExplorerView); diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx index cb691869..0aecc0d2 100644 --- a/src/views/workbench/workbench.tsx +++ b/src/views/workbench/workbench.tsx @@ -6,31 +6,21 @@ import * as React from 'react'; import { StyleRulesCallback, Theme, WithStyles, withStyles } from '@material-ui/core/styles'; import Drawer from '@material-ui/core/Drawer'; -import AppBar from '@material-ui/core/AppBar'; -import Toolbar from '@material-ui/core/Toolbar'; -import Typography from '@material-ui/core/Typography'; import { connect, DispatchProp } from "react-redux"; -import ProjectList from "../../components/project-list/project-list"; import { Route, Switch } from "react-router"; -import { Link } from "react-router-dom"; -import Button from "@material-ui/core/Button/Button"; import authActions from "../../store/auth/auth-action"; -import IconButton from "@material-ui/core/IconButton/IconButton"; -import Menu from "@material-ui/core/Menu/Menu"; -import MenuItem from "@material-ui/core/MenuItem/MenuItem"; -import { AccountCircle } from "@material-ui/icons"; import { User } from "../../models/user"; -import Grid from "@material-ui/core/Grid/Grid"; import { RootState } from "../../store/store"; -import MainAppBar, { MainAppBarActionProps, MainAppBarMenuItems, MainAppBarMenuItem } from '../../components/main-app-bar/main-app-bar'; +import MainAppBar, { MainAppBarActionProps, MainAppBarMenuItem } from '../../components/main-app-bar/main-app-bar'; import { Breadcrumb } from '../../components/breadcrumbs/breadcrumbs'; import { push } from 'react-router-redux'; import projectActions from "../../store/project/project-action"; import ProjectTree from '../../components/project-tree/project-tree'; -import { TreeItem } from "../../components/tree/tree"; +import { TreeItem, TreeItemStatus } from "../../components/tree/tree"; import { Project } from "../../models/project"; import { projectService } from '../../services/services'; import { findTreeBranch } from '../../store/project/project-reducer'; +import DataExplorer from '../data-explorer/data-explorer'; const drawerWidth = 240; @@ -78,6 +68,7 @@ type WorkbenchProps = WorkbenchDataProps & WorkbenchActionProps & DispatchProp & interface NavBreadcrumb extends Breadcrumb { itemId: string; + status: TreeItemStatus; } interface NavMenuItem extends MainAppBarMenuItem { @@ -128,8 +119,8 @@ class Workbench extends React.Component { mainAppBarActions: MainAppBarActionProps = { - onBreadcrumbClick: ({ itemId }: NavBreadcrumb) => { - this.toggleProjectTreeItem(itemId); + onBreadcrumbClick: ({ itemId, status }: NavBreadcrumb) => { + this.toggleProjectTreeItem(itemId, status); }, onSearch: searchText => { this.setState({ searchText }); @@ -138,17 +129,25 @@ class Workbench extends React.Component { onMenuItemClick: (menuItem: NavMenuItem) => menuItem.action() }; - toggleProjectTreeItem = (itemId: string) => { + toggleProjectTreeItem = (itemId: string, status: TreeItemStatus) => { + if (status === TreeItemStatus.Loaded) { + this.openProjectItem(itemId); + } else { + this.props.dispatch(projectService.getProjectList(itemId)).then(() => this.openProjectItem(itemId)); + } + } + + openProjectItem = (itemId: string) => { const branch = findTreeBranch(this.props.projects, itemId); this.setState({ breadcrumbs: branch.map(item => ({ label: item.data.name, - itemId: item.data.uuid + itemId: item.data.uuid, + status: item.status })) }); - this.props.dispatch(projectService.getProjectList(itemId)).then(() => { - this.props.dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM(itemId)); - }); + this.props.dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM(itemId)); + this.props.dispatch(push(`/project/${itemId}`)); } render() { @@ -179,7 +178,7 @@ class Workbench extends React.Component {
- +
diff --git a/yarn.lock b/yarn.lock index 309773f0..eee6c860 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,29 +3,29 @@ "@babel/code-frame@^7.0.0-beta.35": - version "7.0.0-beta.49" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.49.tgz#becd805482734440c9d137e46d77340e64d7f51b" + version "7.0.0-beta.51" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.51.tgz#bd71d9b192af978df915829d39d4094456439a0c" dependencies: - "@babel/highlight" "7.0.0-beta.49" + "@babel/highlight" "7.0.0-beta.51" -"@babel/highlight@7.0.0-beta.49": - version "7.0.0-beta.49" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.49.tgz#96bdc6b43e13482012ba6691b1018492d39622cc" +"@babel/highlight@7.0.0-beta.51": + version "7.0.0-beta.51" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.51.tgz#e8844ae25a1595ccfd42b89623b4376ca06d225d" dependencies: chalk "^2.0.0" esutils "^2.0.2" js-tokens "^3.0.0" "@babel/runtime@^7.0.0-beta.42": - version "7.0.0-beta.49" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.49.tgz#03b3bf07eb982072c8e851dd2ddd5110282e61bf" + version "7.0.0-beta.51" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.51.tgz#48b8ed18307034c6620f643514650ca2ccc0165a" dependencies: - core-js "^2.5.6" + core-js "^2.5.7" regenerator-runtime "^0.11.1" -"@material-ui/core@1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-1.2.0.tgz#ec372fd44f949faa604c4ccd4b7ee0bc5e08ac8c" +"@material-ui/core@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-1.2.1.tgz#f8c73da10b875762b37be7167ec2ac79b027499f" dependencies: "@babel/runtime" "^7.0.0-beta.42" "@types/jss" "^9.5.3" @@ -55,7 +55,7 @@ scroll "^2.0.3" warning "^4.0.1" -"@material-ui/icons@^1.1.0": +"@material-ui/icons@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-1.1.0.tgz#4d025df7b0ba6ace8d6710079ed76013a4d26595" dependencies: @@ -65,13 +65,13 @@ version "0.22.7" resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.7.tgz#4a92eafedfb2b9f4437d3a4410006d81114c66ce" -"@types/enzyme-adapter-react-16@^1.0.2": +"@types/enzyme-adapter-react-16@1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.2.tgz#15ae37c64d6221a6f4b3a4aacc357cf773859de4" dependencies: "@types/enzyme" "*" -"@types/enzyme@*", "@types/enzyme@^3.1.10": +"@types/enzyme@*", "@types/enzyme@3.1.10": version "3.1.10" resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.1.10.tgz#28108a9864e65699751469551a803a35d2e26160" dependencies: @@ -82,9 +82,9 @@ version "4.6.2" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.6.2.tgz#12cfaba693ba20f114ed5765467ff25fdf67ddb0" -"@types/jest@23.0.0": - version "23.0.0" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.0.0.tgz#760cac74f00bb9c3075587716d2b3b4435663bc0" +"@types/jest@23.1.0": + version "23.1.0" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.1.0.tgz#8054dd838ba23dc331794d26456b86c7e50bf0f6" "@types/jss@^9.5.3": version "9.5.3" @@ -93,24 +93,24 @@ csstype "^2.0.0" indefinite-observable "^1.0.1" -"@types/lodash@^4.14.109": +"@types/lodash@4.14.109": version "4.14.109" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.109.tgz#b1c4442239730bf35cabaf493c772b18c045886d" -"@types/node@*", "@types/node@10.3.0": - version "10.3.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.3.0.tgz#078516315a84d56216b5d4fed8f75d59d3b16cac" +"@types/node@*", "@types/node@10.3.3": + version "10.3.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.3.3.tgz#8798d9e39af2fa604f715ee6a6b19796528e46c3" -"@types/react-dom@16.0.5": - version "16.0.5" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.5.tgz#a757457662e3819409229e8f86795ff37b371f96" +"@types/react-dom@16.0.6": + version "16.0.6" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.6.tgz#f1a65a4e7be8ed5d123f8b3b9eacc913e35a1a3c" dependencies: "@types/node" "*" "@types/react" "*" -"@types/react-redux@6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-6.0.1.tgz#bb8f6cc19d00a999f9d932ab796212ad3921994b" +"@types/react-redux@6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-6.0.2.tgz#10069b53db8e0920fd8656e068dcf10c53c9ad2a" dependencies: "@types/react" "*" redux "^4.0.0" @@ -145,9 +145,9 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@16.3.16": - version "16.3.16" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.3.16.tgz#78fc44a90b45701f50c8a7008f733680ba51fc86" +"@types/react@*", "@types/react@16.3.18": + version "16.3.18" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.3.18.tgz#bf195aed4d77dc86f06e4c9bb760214a3b822b8d" dependencies: csstype "^2.2.0" @@ -190,8 +190,8 @@ acorn@^4.0.3: resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" acorn@^5.0.0, acorn@^5.3.0: - version "5.6.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.6.1.tgz#c9e50c3e3717cf897f1b071ceadbb543bbc0a8d4" + version "5.7.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" address@1.0.3, address@^1.0.1: version "1.0.3" @@ -215,12 +215,12 @@ ajv@^5.0.0, ajv@^5.1.0, ajv@^5.1.5: json-schema-traverse "^0.3.0" ajv@^6.1.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.0.tgz#4c8affdf80887d8f132c9c52ab8a2dc4d0b7b24c" + version "6.5.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.1.tgz#88ebc1263c7133937d108b80c5572e64e1d9322d" dependencies: fast-deep-equal "^2.0.1" fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" + json-schema-traverse "^0.4.1" uri-js "^4.2.1" align-text@^0.1.1, align-text@^0.1.3: @@ -285,11 +285,11 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -append-transform@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" +append-transform@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" dependencies: - default-require-extensions "^1.0.0" + default-require-extensions "^2.0.0" aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" @@ -1427,12 +1427,12 @@ caniuse-api@^1.5.2: lodash.uniq "^4.5.0" caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30000848" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000848.tgz#e149c981c72aa20439e3bc12c7cf8b3f7e1237c6" + version "1.0.30000856" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000856.tgz#fbebb99abe15a5654fc7747ebb5315bdfde3358f" caniuse-lite@^1.0.30000748, caniuse-lite@^1.0.30000792: - version "1.0.30000848" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000848.tgz#ec9c0a72ec8f9ef812e4f4b8628625af9c85ade0" + version "1.0.30000856" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000856.tgz#ecc16978135a6f219b138991eb62009d25ee8daa" capture-exit@^1.2.0: version "1.2.0" @@ -1560,8 +1560,8 @@ class-utils@^0.3.5: static-extend "^0.1.1" classnames@^2.2.5: - version "2.2.5" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" + version "2.2.6" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" clean-css@4.1.x: version "4.1.11" @@ -1633,12 +1633,16 @@ collection-visit@^1.0.0: object-visit "^1.0.0" color-convert@^1.3.0, color-convert@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" + version "1.9.2" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" dependencies: - color-name "^1.1.1" + color-name "1.1.1" + +color-name@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" -color-name@^1.0.0, color-name@^1.1.1: +color-name@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" @@ -1691,18 +1695,18 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" compare-versions@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.2.1.tgz#a49eb7689d4caaf0b6db5220173fd279614000f7" + version "3.3.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.3.0.tgz#af93ea705a96943f622ab309578b9b90586f39c3" component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" compressible@~2.0.13: - version "2.0.13" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.13.tgz#0d1020ab924b2fdb4d6279875c7d6daba6baa7a9" + version "2.0.14" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.14.tgz#326c5f507fbb055f54116782b969a81b67a29da7" dependencies: - mime-db ">= 1.33.0 < 2" + mime-db ">= 1.34.0 < 2" compression@^1.5.2: version "1.7.2" @@ -1797,7 +1801,7 @@ core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" -core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.6: +core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.7: version "2.5.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" @@ -2081,11 +2085,11 @@ deepmerge@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.1.1.tgz#e862b4e45ea0555072bf51e7fd0d9845170ae768" -default-require-extensions@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" +default-require-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-2.0.0.tgz#f5f8fbb18a7d6d50b21f641f649ebb522cfe24f7" dependencies: - strip-bom "^2.0.0" + strip-bom "^3.0.0" define-properties@^1.1.2: version "1.1.2" @@ -2529,8 +2533,8 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" escodegen@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.1.tgz#dbae17ef96c8e4bedb1356f4504fa4cc2f7cb7e2" + version "1.10.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.10.0.tgz#f647395de22519fbd0d928ffcf1d17e0dec2603e" dependencies: esprima "^3.1.3" estraverse "^4.2.0" @@ -2804,8 +2808,8 @@ fb-watchman@^2.0.0: bser "^2.0.0" fbjs@^0.8.1, fbjs@^0.8.16: - version "0.8.16" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" + version "0.8.17" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd" dependencies: core-js "^1.0.0" isomorphic-fetch "^2.1.1" @@ -2813,7 +2817,7 @@ fbjs@^0.8.1, fbjs@^0.8.16: object-assign "^4.1.0" promise "^7.1.1" setimmediate "^1.0.5" - ua-parser-js "^0.7.9" + ua-parser-js "^0.7.18" figures@^2.0.0: version "2.0.0" @@ -3289,8 +3293,8 @@ hash-base@^3.0.0: safe-buffer "^5.0.1" hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + version "1.1.4" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.4.tgz#8b50e1f35d51bd01e5ed9ece4dbe3549ccfa0a3c" dependencies: inherits "^2.0.3" minimalistic-assert "^1.0.0" @@ -3317,9 +3321,9 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^2.3.0, hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40" +hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0: + version "2.5.4" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.4.tgz#fc3b1ac05d2ae3abedec84eba846511b0d4fcc4f" home-or-tmp@^2.0.0: version "2.0.0" @@ -3484,8 +3488,8 @@ icss-utils@^2.1.0: postcss "^6.0.1" ieee754@^1.1.4: - version "1.1.11" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.11.tgz#c16384ffe00f5b7835824e67b6f2bd44a5229455" + version "1.1.12" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" iferr@^0.1.5: version "0.1.5" @@ -3587,7 +3591,7 @@ interpret@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" -invariant@^2.0.0, invariant@^2.2.1, invariant@^2.2.2: +invariant@^2.0.0, invariant@^2.2.1, invariant@^2.2.2, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" dependencies: @@ -3955,10 +3959,10 @@ istanbul-lib-coverage@^1.1.1, istanbul-lib-coverage@^1.1.2, istanbul-lib-coverag resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz#f7d8f2e42b97e37fe796114cb0f9d68b5e3a4341" istanbul-lib-hook@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.2.0.tgz#ae556fd5a41a6e8efa0b1002b1e416dfeaf9816c" + version "1.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.2.1.tgz#f614ec45287b2a8fc4f07f5660af787575601805" dependencies: - append-transform "^0.4.0" + append-transform "^1.0.0" istanbul-lib-instrument@^1.10.1, istanbul-lib-instrument@^1.8.0: version "1.10.1" @@ -4343,6 +4347,10 @@ json-schema-traverse@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -4464,8 +4472,8 @@ jss-vendor-prefixer@^7.0.0: css-vendor "^0.3.8" jss@^9.3.3, jss@^9.7.0: - version "9.8.2" - resolved "https://registry.yarnpkg.com/jss/-/jss-9.8.2.tgz#09cabdfba831545bf094e399cfa45a1743daf4a6" + version "9.8.3" + resolved "https://registry.yarnpkg.com/jss/-/jss-9.8.3.tgz#399da571c4b2c8f4cf418ca7e8627e44fc287fc8" dependencies: is-in-browser "^1.1.3" symbol-observable "^1.1.0" @@ -4818,7 +4826,7 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -"mime-db@>= 1.33.0 < 2": +"mime-db@>= 1.34.0 < 2": version "1.34.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.34.0.tgz#452d0ecff5c30346a6dc1e64b1eaee0d3719ff9a" @@ -5167,8 +5175,8 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" nwsapi@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.0.1.tgz#a50d59a2dcb14b6931401171713ced2d0eb3468f" + version "2.0.3" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.0.3.tgz#3f4010d6c943f34018d3dfb5f2fbc0de90476959" oauth-sign@~0.8.2: version "0.8.2" @@ -5351,8 +5359,8 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" p-limit@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.2.0.tgz#0e92b6bedcb59f022c13d0f1949dc82d15909f1c" + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" dependencies: p-try "^1.0.0" @@ -5905,7 +5913,7 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1: +prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1: version "15.6.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" dependencies: @@ -5928,6 +5936,10 @@ pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" +psl@^1.1.24: + version "1.1.28" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.28.tgz#4fb6ceb08a1e2214d4fd4de0ca22dae13740bc7b" + public-encrypt@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.2.tgz#46eb9107206bf73489f8b85b69d91334c6610994" @@ -6085,9 +6097,9 @@ react-dev-utils@^5.0.1: strip-ansi "3.0.1" text-table "0.2.0" -react-dom@16.4.0: - version "16.4.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.0.tgz#099f067dd5827ce36a29eaf9a6cdc7cbf6216b1e" +react-dom@16.4.1: + version "16.4.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.1.tgz#7f8b0223b3a5fbe205116c56deb85de32685dad6" dependencies: fbjs "^0.8.16" loose-envify "^1.1.0" @@ -6099,22 +6111,22 @@ react-error-overlay@^4.0.0: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4" react-event-listener@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.6.0.tgz#f8cf2821f5ca1844e0df1dac1c7b9a3ecb686fd7" + version "0.6.1" + resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.6.1.tgz#41c7a80a66b398c27dd511e22712b02f3d4eccca" dependencies: "@babel/runtime" "^7.0.0-beta.42" prop-types "^15.6.0" - warning "^3.0.0" + warning "^4.0.1" -react-is@^16.4.0: - version "16.4.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.0.tgz#cc9fdc855ac34d2e7d9d2eb7059bbc240d35ffcf" +react-is@^16.4.1: + version "16.4.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.1.tgz#d624c4650d2c65dbd52c72622bbf389435d9776e" react-jss@^8.1.0: - version "8.4.0" - resolved "https://registry.yarnpkg.com/react-jss/-/react-jss-8.4.0.tgz#7cb43d85dea56afafc8f0fd072ae27fcc0518950" + version "8.5.1" + resolved "https://registry.yarnpkg.com/react-jss/-/react-jss-8.5.1.tgz#f97c72f6a1c86aa6408932a2a2836ce40c0ab9fc" dependencies: - hoist-non-react-statics "^2.3.1" + hoist-non-react-statics "^2.5.0" jss "^9.7.0" jss-preset-default "^4.3.0" prop-types "^15.6.0" @@ -6151,16 +6163,16 @@ react-redux@5.0.7: loose-envify "^1.1.0" prop-types "^15.6.0" -react-router-dom@4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.2.2.tgz#c8a81df3adc58bba8a76782e946cbd4eae649b8d" +react-router-dom@4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.3.1.tgz#4c2619fc24c4fa87c9fd18f4fb4a43fe63fbd5c6" dependencies: history "^4.7.2" - invariant "^2.2.2" + invariant "^2.2.4" loose-envify "^1.3.1" - prop-types "^15.5.4" - react-router "^4.2.0" - warning "^3.0.0" + prop-types "^15.6.1" + react-router "^4.3.1" + warning "^4.0.1" react-router-redux@5.0.0-alpha.9: version "5.0.0-alpha.9" @@ -6170,17 +6182,17 @@ react-router-redux@5.0.0-alpha.9: prop-types "^15.6.0" react-router "^4.2.0" -react-router@4.2.0, react-router@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-4.2.0.tgz#61f7b3e3770daeb24062dae3eedef1b054155986" +react-router@4.3.1, react-router@^4.2.0, react-router@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-4.3.1.tgz#aada4aef14c809cb2e686b05cee4742234506c4e" dependencies: history "^4.7.2" - hoist-non-react-statics "^2.3.0" - invariant "^2.2.2" + hoist-non-react-statics "^2.5.0" + invariant "^2.2.4" loose-envify "^1.3.1" path-to-regexp "^1.7.0" - prop-types "^15.5.4" - warning "^3.0.0" + prop-types "^15.6.1" + warning "^4.0.1" react-scripts-ts@2.16.0: version "2.16.0" @@ -6227,13 +6239,13 @@ react-scripts-ts@2.16.0: fsevents "^1.1.3" react-test-renderer@^16.0.0-0: - version "16.4.0" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.4.0.tgz#0dbe0e24263e94e1830c7afb1f403707fad313a3" + version "16.4.1" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.4.1.tgz#f2fb30c2c7b517db6e5b10ed20bb6b0a7ccd8d70" dependencies: fbjs "^0.8.16" object-assign "^4.1.1" prop-types "^15.6.0" - react-is "^16.4.0" + react-is "^16.4.1" react-transition-group@^2.2.1: version "2.3.1" @@ -6243,9 +6255,9 @@ react-transition-group@^2.2.1: loose-envify "^1.3.1" prop-types "^15.6.1" -react@16.4.0: - version "16.4.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.4.0.tgz#402c2db83335336fba1962c08b98c6272617d585" +react@16.4.1: + version "16.4.1" + resolved "https://registry.yarnpkg.com/react/-/react-16.4.1.tgz#de51ba5764b5dbcd1f9079037b862bd26b82fe32" dependencies: fbjs "^0.8.16" loose-envify "^1.1.0" @@ -6582,8 +6594,8 @@ resolve@1.6.0: path-parse "^1.0.5" resolve@^1.1.7, resolve@^1.3.2: - version "1.7.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3" + version "1.8.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.0.tgz#a7f2ac27b78480ecc09c83782741d9f26e4f0c3e" dependencies: path-parse "^1.0.5" @@ -6664,7 +6676,7 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3": +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -7009,13 +7021,14 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" sshpk@^1.7.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.1.tgz#130f5975eddad963f1d56f92b9ac6c51fa9f83eb" + version "1.14.2" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98" dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" dashdash "^1.12.0" getpass "^0.1.1" + safer-buffer "^2.0.2" optionalDependencies: bcrypt-pbkdf "^1.0.0" ecc-jsbn "~0.1.1" @@ -7362,7 +7375,14 @@ toposort@^1.0.0: version "1.0.7" resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029" -tough-cookie@>=2.3.3, tough-cookie@^2.3.3, tough-cookie@~2.3.3: +tough-cookie@>=2.3.3, tough-cookie@^2.3.3: + version "2.4.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.2.tgz#aa9133154518b494efab98a58247bfc38818c00c" + dependencies: + psl "^1.1.24" + punycode "^1.4.1" + +tough-cookie@~2.3.3: version "2.3.4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" dependencies: @@ -7414,8 +7434,8 @@ tsconfig-paths-webpack-plugin@^2.0.0: tsconfig-paths "^3.1.1" tsconfig-paths@^3.1.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.3.2.tgz#bb48b845e221a44387be0f9968ee6c37c2a37c4d" + version "3.4.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.4.0.tgz#d19fe80c5b245f99d17363471971eab54e65a8a7" dependencies: deepmerge "^2.0.1" minimist "^1.2.0" @@ -7490,11 +7510,11 @@ typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" -typescript@2.9.1: - version "2.9.1" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.1.tgz#fdb19d2c67a15d11995fd15640e373e09ab09961" +typescript@2.9.2: + version "2.9.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" -ua-parser-js@^0.7.9: +ua-parser-js@^0.7.18: version "0.7.18" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed" @@ -7667,8 +7687,8 @@ url-parse-lax@^1.0.0: prepend-http "^1.0.1" url-parse@^1.1.8, url-parse@~1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.0.tgz#6bfdaad60098c7fe06f623e42b22de62de0d3d75" + version "1.4.1" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.1.tgz#4dec9dad3dc8585f862fed461d2e19bbf623df30" dependencies: querystringify "^2.0.0" requires-port "^1.0.0" @@ -7924,8 +7944,8 @@ whatwg-mimetype@^2.0.0, whatwg-mimetype@^2.1.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz#f0f21d76cbba72362eb609dbed2a30cd17fcc7d4" whatwg-url@^6.4.0, whatwg-url@^6.4.1: - version "6.4.1" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.4.1.tgz#fdb94b440fd4ad836202c16e9737d511f012fd67" + version "6.5.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" dependencies: lodash.sortby "^4.7.0" tr46 "^1.0.1"