From c3d58f0872b87434d051d2b210a4e71d61215fc2 Mon Sep 17 00:00:00 2001 From: MaximKudriavtsev Date: Fri, 2 Mar 2018 09:26:39 +0300 Subject: [PATCH] feat(react-grid-bootstrap4): support the bootstrap4 theme (#681) --- packages/dx-react-demos/package.json | 2 + .../disable-toggling-column-visibility.jsxt | 2 +- .../grid-data-types/bootstrap4/editors.jsx | 112 ++++ .../bootstrap4/demo.jsx | 299 ++++++++++ .../bootstrap4/custom-filter-row.jsx | 93 +++ .../disable-column-filtering.jsxt | 2 +- packages/dx-react-demos/src/theme-registry.js | 24 +- .../bootstrap3/demo-container.jsx | 14 +- .../components/highlighted-cell.jsx | 36 ++ .../bootstrap4/components/loading.css | 29 + .../bootstrap4/components/loading.jsx | 10 + .../components/progress-bar-cell.jsx | 36 ++ .../bootstrap4/demo-container.jsx | 126 +++++ .../bootstrap4/demo-source-data.json | 4 + packages/dx-react-grid-bootstrap3/README.md | 4 +- .../src/templates/column-chooser/overlay.jsx | 4 +- .../column-chooser/toggle-button.jsx | 4 +- packages/dx-react-grid-bootstrap4/.babelrc | 9 + .../dx-react-grid-bootstrap4/.eslintignore | 1 + .../dx-react-grid-bootstrap4/.eslintrc.json | 24 + packages/dx-react-grid-bootstrap4/README.md | 91 +++ packages/dx-react-grid-bootstrap4/css-stub.js | 1 + .../dx-react-grid-bootstrap4/package.json | 99 ++++ .../dx-react-grid-bootstrap4/rollup.config.js | 32 ++ .../dx-react-grid-bootstrap4/setup-enzyme.js | 4 + .../dx-react-grid-bootstrap4/src/grid.jsx | 23 + .../dx-react-grid-bootstrap4/src/index.js | 20 + .../dx-react-grid-bootstrap4/src/main.css | 14 + .../src/plugins/column-chooser.jsx | 21 + .../src/plugins/drag-drop-provider.jsx | 18 + .../src/plugins/grouping-panel.jsx | 43 ++ .../src/plugins/paging-panel.jsx | 40 ++ .../src/plugins/table-column-reordering.jsx | 37 ++ .../src/plugins/table-column-resizing.jsx | 12 + .../src/plugins/table-column-visibility.jsx | 34 ++ .../src/plugins/table-edit-column.jsx | 53 ++ .../src/plugins/table-edit-row.jsx | 19 + .../src/plugins/table-filter-row.jsx | 19 + .../src/plugins/table-group-row.jsx | 20 + .../src/plugins/table-header-row.jsx | 19 + .../src/plugins/table-row-detail.jsx | 23 + .../src/plugins/table-selection.jsx | 23 + .../src/plugins/table.jsx | 53 ++ .../src/plugins/toolbar.jsx | 18 + .../src/plugins/virtual-table.jsx | 70 +++ .../templates/column-chooser/container.jsx | 25 + .../column-chooser/container.test.jsx | 31 + .../src/templates/column-chooser/item.css | 7 + .../src/templates/column-chooser/item.jsx | 55 ++ .../templates/column-chooser/item.test.jsx | 115 ++++ .../src/templates/column-chooser/overlay.jsx | 34 ++ .../templates/column-chooser/overlay.test.jsx | 38 ++ .../column-chooser/toggle-button.jsx | 39 ++ .../column-chooser/toggle-button.test.jsx | 35 ++ .../src/templates/drag-drop.css | 6 + .../src/templates/drag-drop.jsx | 57 ++ .../src/templates/drag-drop.test.jsx | 87 +++ .../src/templates/empty-message.jsx | 21 + .../src/templates/empty-message.test.jsx | 37 ++ .../src/templates/group-panel-container.jsx | 26 + .../templates/group-panel-container.test.jsx | 32 ++ .../templates/group-panel-empty-message.css | 3 + .../templates/group-panel-empty-message.jsx | 22 + .../group-panel-empty-message.test.jsx | 32 ++ .../src/templates/group-panel-item.css | 5 + .../src/templates/group-panel-item.jsx | 114 ++++ .../src/templates/group-panel-item.test.jsx | 158 ++++++ .../src/templates/layout.jsx | 21 + .../src/templates/layout.test.jsx | 16 + .../paging-panel/page-size-selector.jsx | 50 ++ .../paging-panel/page-size-selector.test.jsx | 76 +++ .../src/templates/paging-panel/pager.jsx | 54 ++ .../src/templates/paging-panel/pager.test.jsx | 77 +++ .../src/templates/paging-panel/pagination.jsx | 161 ++++++ .../paging-panel/pagination.test.jsx | 85 +++ .../src/templates/parts/sorting-indicator.css | 4 + .../src/templates/parts/sorting-indicator.jsx | 23 + .../src/templates/table-cell.css | 4 + .../src/templates/table-cell.jsx | 43 ++ .../src/templates/table-cell.test.jsx | 52 ++ .../src/templates/table-container.css | 4 + .../src/templates/table-container.jsx | 19 + .../src/templates/table-detail-cell.jsx | 40 ++ .../src/templates/table-detail-cell.test.jsx | 32 ++ .../templates/table-detail-toggle-cell.css | 4 + .../templates/table-detail-toggle-cell.jsx | 63 +++ .../table-detail-toggle-cell.test.jsx | 88 +++ .../src/templates/table-edit-cell.css | 3 + .../src/templates/table-edit-cell.jsx | 48 ++ .../src/templates/table-edit-cell.test.jsx | 56 ++ .../src/templates/table-edit-command-cell.css | 3 + .../src/templates/table-edit-command-cell.jsx | 91 +++ .../table-edit-command-cell.test.jsx | 71 +++ .../src/templates/table-filter-cell.jsx | 45 ++ .../src/templates/table-filter-cell.test.jsx | 58 ++ .../src/templates/table-group-row-cell.css | 5 + .../src/templates/table-group-row-cell.jsx | 76 +++ .../templates/table-group-row-cell.test.jsx | 90 +++ .../src/templates/table-header-cell.css | 10 + .../src/templates/table-header-cell.jsx | 156 ++++++ .../src/templates/table-header-cell.test.jsx | 268 +++++++++ .../table-header-cell/grouping-control.css | 10 + .../table-header-cell/grouping-control.jsx | 41 ++ .../table-header-cell/resizing-control.css | 16 + .../table-header-cell/resizing-control.jsx | 75 +++ .../resizing-control.test.jsx | 57 ++ .../table-header-cell/sorting-control.css | 3 + .../table-header-cell/sorting-control.jsx | 55 ++ .../sorting-control.test.jsx | 50 ++ .../src/templates/table-layout.jsx | 43 ++ .../src/templates/table-no-data-cell.jsx | 32 ++ .../src/templates/table-no-data-cell.test.jsx | 28 + .../src/templates/table-reordering-cell.jsx | 25 + .../src/templates/table-row.jsx | 27 + .../src/templates/table-row.test.jsx | 14 + .../src/templates/table-select-all-cell.jsx | 65 +++ .../templates/table-select-all-cell.test.jsx | 69 +++ .../src/templates/table-select-cell.jsx | 46 ++ .../src/templates/table-select-cell.test.jsx | 22 + .../src/templates/table-select-row.jsx | 38 ++ .../src/templates/table-select-row.test.jsx | 45 ++ .../src/templates/table-stub-cell.jsx | 27 + .../src/templates/table-stub-cell.test.jsx | 23 + .../src/templates/table-stub-header-cell.jsx | 27 + .../templates/table-stub-header-cell.test.jsx | 23 + .../src/templates/table.css | 7 + .../src/templates/table.jsx | 85 +++ .../src/templates/toolbar/flexible-space.jsx | 4 + .../src/templates/toolbar/toolbar.css | 4 + .../src/templates/toolbar/toolbar.jsx | 33 ++ .../src/templates/toolbar/toolbar.test.jsx | 27 + .../src/templates/virtual-table-layout.jsx | 51 ++ .../src/templates/column-chooser/overlay.jsx | 4 +- .../column-chooser/toggle-button.jsx | 10 +- packages/dx-react-grid/README.md | 22 +- .../docs/guides/getting-started.md | 38 +- .../src/plugins/column-chooser.jsx | 2 + yarn.lock | 528 +++++++----------- 138 files changed, 5818 insertions(+), 374 deletions(-) create mode 100644 packages/dx-react-demos/src/demo-sources/grid-data-types/bootstrap4/editors.jsx create mode 100644 packages/dx-react-demos/src/demo-sources/grid-featured-controlled-mode/bootstrap4/demo.jsx create mode 100644 packages/dx-react-demos/src/demo-sources/grid-filtering/bootstrap4/custom-filter-row.jsx create mode 100644 packages/dx-react-demos/src/theme-sources/bootstrap4/components/highlighted-cell.jsx create mode 100644 packages/dx-react-demos/src/theme-sources/bootstrap4/components/loading.css create mode 100644 packages/dx-react-demos/src/theme-sources/bootstrap4/components/loading.jsx create mode 100644 packages/dx-react-demos/src/theme-sources/bootstrap4/components/progress-bar-cell.jsx create mode 100644 packages/dx-react-demos/src/theme-sources/bootstrap4/demo-container.jsx create mode 100644 packages/dx-react-demos/src/theme-sources/bootstrap4/demo-source-data.json create mode 100644 packages/dx-react-grid-bootstrap4/.babelrc create mode 100644 packages/dx-react-grid-bootstrap4/.eslintignore create mode 100644 packages/dx-react-grid-bootstrap4/.eslintrc.json create mode 100644 packages/dx-react-grid-bootstrap4/README.md create mode 100644 packages/dx-react-grid-bootstrap4/css-stub.js create mode 100644 packages/dx-react-grid-bootstrap4/package.json create mode 100644 packages/dx-react-grid-bootstrap4/rollup.config.js create mode 100644 packages/dx-react-grid-bootstrap4/setup-enzyme.js create mode 100644 packages/dx-react-grid-bootstrap4/src/grid.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/index.js create mode 100644 packages/dx-react-grid-bootstrap4/src/main.css create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/column-chooser.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/drag-drop-provider.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/grouping-panel.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/paging-panel.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/table-column-reordering.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/table-column-resizing.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/table-column-visibility.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/table-edit-column.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/table-edit-row.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/table-filter-row.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/table-group-row.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/table-header-row.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/table-row-detail.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/table-selection.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/table.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/toolbar.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/plugins/virtual-table.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/column-chooser/container.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/column-chooser/container.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/column-chooser/item.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/column-chooser/item.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/column-chooser/item.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/column-chooser/overlay.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/column-chooser/overlay.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/column-chooser/toggle-button.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/column-chooser/toggle-button.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/drag-drop.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/drag-drop.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/drag-drop.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/empty-message.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/empty-message.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/group-panel-container.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/group-panel-container.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/group-panel-empty-message.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/group-panel-empty-message.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/group-panel-empty-message.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/group-panel-item.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/group-panel-item.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/group-panel-item.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/layout.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/layout.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/paging-panel/page-size-selector.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/paging-panel/page-size-selector.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/paging-panel/pager.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/paging-panel/pager.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/paging-panel/pagination.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/paging-panel/pagination.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/parts/sorting-indicator.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/parts/sorting-indicator.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-cell.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-cell.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-cell.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-container.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-container.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-detail-cell.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-detail-cell.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-detail-toggle-cell.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-detail-toggle-cell.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-detail-toggle-cell.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-edit-cell.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-edit-cell.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-edit-cell.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-edit-command-cell.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-edit-command-cell.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-edit-command-cell.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-filter-cell.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-filter-cell.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-group-row-cell.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-group-row-cell.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-group-row-cell.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-header-cell.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-header-cell.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-header-cell.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/grouping-control.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/grouping-control.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/resizing-control.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/resizing-control.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/resizing-control.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/sorting-control.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/sorting-control.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/sorting-control.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-layout.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-no-data-cell.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-no-data-cell.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-reordering-cell.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-row.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-row.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-select-all-cell.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-select-all-cell.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-select-cell.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-select-cell.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-select-row.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-select-row.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-stub-cell.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-stub-cell.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-stub-header-cell.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table-stub-header-cell.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/table.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/toolbar/flexible-space.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/toolbar/toolbar.css create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/toolbar/toolbar.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/toolbar/toolbar.test.jsx create mode 100644 packages/dx-react-grid-bootstrap4/src/templates/virtual-table-layout.jsx diff --git a/packages/dx-react-demos/package.json b/packages/dx-react-demos/package.json index 4a18a01f54..a57fffd832 100644 --- a/packages/dx-react-demos/package.json +++ b/packages/dx-react-demos/package.json @@ -48,6 +48,7 @@ "@devexpress/dx-react-core": "1.1.0-beta.1", "@devexpress/dx-react-grid": "1.1.0-beta.1", "@devexpress/dx-react-grid-bootstrap3": "1.1.0-beta.1", + "@devexpress/dx-react-grid-bootstrap4": "1.1.0-beta.1", "@devexpress/dx-react-grid-material-ui": "1.1.0-beta.1", "@types/react": "^16.0.39", "@types/react-dom": "^16.0.4", @@ -58,6 +59,7 @@ "prop-types": "^15.6.1", "react": "^16.2.0", "react-bootstrap": "^0.32.1", + "reactstrap": "5.0.0-beta", "react-dom": "^16.2.0", "react-frame-component": "^2.0.2", "react-redux": "^5.0.7", diff --git a/packages/dx-react-demos/src/demo-sources/grid-column-chooser/disable-toggling-column-visibility.jsxt b/packages/dx-react-demos/src/demo-sources/grid-column-chooser/disable-toggling-column-visibility.jsxt index c7dda33fac..397601d87f 100644 --- a/packages/dx-react-demos/src/demo-sources/grid-column-chooser/disable-toggling-column-visibility.jsxt +++ b/packages/dx-react-demos/src/demo-sources/grid-column-chooser/disable-toggling-column-visibility.jsxt @@ -1,4 +1,4 @@ -import React from 'react';<%&additionalImports%> +import * as React from 'react';<%&additionalImports%> import { Grid, Table, diff --git a/packages/dx-react-demos/src/demo-sources/grid-data-types/bootstrap4/editors.jsx b/packages/dx-react-demos/src/demo-sources/grid-data-types/bootstrap4/editors.jsx new file mode 100644 index 0000000000..f14998d696 --- /dev/null +++ b/packages/dx-react-demos/src/demo-sources/grid-data-types/bootstrap4/editors.jsx @@ -0,0 +1,112 @@ +import * as React from 'react'; +import { + DataTypeProvider, + EditingState, +} from '@devexpress/dx-react-grid'; +import { + Grid, + Table, + TableHeaderRow, + TableEditRow, + TableEditColumn, +} from '@devexpress/dx-react-grid-bootstrap4'; + +import { + generateRows, + globalSalesValues, +} from '../../../demo-data/generator'; + +const getRowId = row => row.id; + +const BooleanFormatter = ({ value }) => + {value ? 'Yes' : 'No'}; + +const BooleanEditor = ({ value, onValueChange }) => ( + +); + +const BooleanTypeProvider = props => ( + +); + +export default class Demo extends React.PureComponent { + constructor(props) { + super(props); + + this.state = { + columns: [ + { name: 'customer', title: 'Customer' }, + { name: 'product', title: 'Product' }, + { name: 'units', title: 'Units' }, + { name: 'shipped', title: 'Shipped' }, + ], + booleanColumns: ['shipped'], + rows: generateRows({ + columnValues: { id: ({ index }) => index, ...globalSalesValues }, + length: 8, + }), + }; + + this.commitChanges = ({ added, changed, deleted }) => { + let { rows } = this.state; + if (added) { + const startingAddedId = (rows.length - 1) > 0 ? rows[rows.length - 1].id + 1 : 0; + rows = [ + ...rows, + ...added.map((row, index) => ({ + id: startingAddedId + index, + ...row, + })), + ]; + } + if (changed) { + rows = rows.map(row => (changed[row.id] ? { ...row, ...changed[row.id] } : row)); + } + if (deleted) { + const deletedSet = new Set(deleted); + rows = rows.filter(row => !deletedSet.has(row.id)); + } + this.setState({ rows }); + }; + } + render() { + const { rows, columns, booleanColumns } = this.state; + + return ( +
+ + + + + + + + + + ); + } +} diff --git a/packages/dx-react-demos/src/demo-sources/grid-featured-controlled-mode/bootstrap4/demo.jsx b/packages/dx-react-demos/src/demo-sources/grid-featured-controlled-mode/bootstrap4/demo.jsx new file mode 100644 index 0000000000..c10b07e840 --- /dev/null +++ b/packages/dx-react-demos/src/demo-sources/grid-featured-controlled-mode/bootstrap4/demo.jsx @@ -0,0 +1,299 @@ +import * as React from 'react'; +import { + SortingState, EditingState, PagingState, + IntegratedPaging, IntegratedSorting, +} from '@devexpress/dx-react-grid'; +import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Card } from 'reactstrap'; +import { + Grid, + Table, TableHeaderRow, TableEditRow, TableEditColumn, + PagingPanel, DragDropProvider, TableColumnReordering, +} from '@devexpress/dx-react-grid-bootstrap4'; +import { ProgressBarCell } from '../../../theme-sources/bootstrap4/components/progress-bar-cell'; +import { HighlightedCell } from '../../../theme-sources/bootstrap4/components/highlighted-cell'; + +import { generateRows, globalSalesValues } from '../../../demo-data/generator'; + +const CommandButton = ({ + onExecute, icon, text, hint, color, +}) => ( + +); + +const commandComponentProps = { + add: { + icon: 'plus', + hint: 'Create new row', + }, + edit: { + icon: 'pencil', + hint: 'Edit row', + color: 'text-warning', + }, + delete: { + icon: 'trash', + hint: 'Delete row', + color: 'text-danger', + }, + commit: { + icon: 'check', + hint: 'Save changes', + color: 'text-success', + }, + cancel: { + icon: 'x', + hint: 'Cancel changes', + color: 'text-danger', + }, +}; + +const Command = ({ id, onExecute }) => ( + +); + +const availableValues = { + product: globalSalesValues.product, + region: globalSalesValues.region, + customer: globalSalesValues.customer, +}; + +export const LookupEditCell = ({ + column, availableColumnValues, value, onValueChange, +}) => ( + +); + +const Cell = (props) => { + if (props.column.name === 'discount') { + return ; + } + if (props.column.name === 'amount') { + return ; + } + return ; +}; + +const EditCell = (props) => { + const availableColumnValues = availableValues[props.column.name]; + if (availableColumnValues) { + return ; + } + return ; +}; + +const getRowId = row => row.id; + +export default class Demo extends React.PureComponent { + constructor(props) { + super(props); + + this.state = { + columns: [ + { name: 'product', title: 'Product' }, + { name: 'region', title: 'Region' }, + { name: 'amount', title: 'Sale Amount' }, + { name: 'discount', title: 'Discount' }, + { name: 'saleDate', title: 'Sale Date' }, + { name: 'customer', title: 'Customer' }, + ], + tableColumnExtensions: [ + { columnName: 'amount', align: 'right', width: 150 }, + { columnName: 'discount', width: 110 }, + ], + rows: generateRows({ + columnValues: { id: ({ index }) => index, ...globalSalesValues }, + length: 12, + }), + sorting: [], + editingRowIds: [], + addedRows: [], + rowChanges: {}, + currentPage: 0, + deletingRows: [], + pageSize: 0, + pageSizes: [5, 10, 0], + columnOrder: ['product', 'region', 'amount', 'discount', 'saleDate', 'customer'], + }; + + this.changeSorting = sorting => this.setState({ sorting }); + this.changeEditingRowIds = editingRowIds => this.setState({ editingRowIds }); + this.changeAddedRows = addedRows => this.setState({ + addedRows: addedRows.map(row => (Object.keys(row).length ? row : { + amount: 0, + discount: 0, + saleDate: new Date().toISOString().split('T')[0], + product: availableValues.product[0], + region: availableValues.region[0], + customer: availableValues.customer[0], + })), + }); + this.changeRowChanges = rowChanges => this.setState({ rowChanges }); + this.changeCurrentPage = currentPage => this.setState({ currentPage }); + this.changePageSize = pageSize => this.setState({ pageSize }); + this.commitChanges = ({ added, changed, deleted }) => { + let { rows } = this.state; + if (added) { + const startingAddedId = (rows.length - 1) > 0 ? rows[rows.length - 1].id + 1 : 0; + rows = [ + ...rows, + ...added.map((row, index) => ({ + id: startingAddedId + index, + ...row, + })), + ]; + } + if (changed) { + rows = rows.map(row => (changed[row.id] ? { ...row, ...changed[row.id] } : row)); + } + this.setState({ rows, deletingRows: deleted || this.state.deletingRows }); + }; + this.cancelDelete = () => this.setState({ deletingRows: [] }); + this.deleteRows = () => { + const rows = this.state.rows.slice(); + this.state.deletingRows.forEach((rowId) => { + const index = rows.findIndex(row => row.id === rowId); + if (index > -1) { + rows.splice(index, 1); + } + }); + this.setState({ rows, deletingRows: [] }); + }; + this.changeColumnOrder = (order) => { + this.setState({ columnOrder: order }); + }; + } + render() { + const { + rows, + columns, + tableColumnExtensions, + sorting, + editingRowIds, + addedRows, + rowChanges, + currentPage, + deletingRows, + pageSize, + pageSizes, + columnOrder, + } = this.state; + + return ( + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + Delete Row + + +

Are you sure to delete the following row?

+ deletingRows.indexOf(row.id) > -1)} + columns={columns} + > +
+ + + + + + + + + + ); + } +} diff --git a/packages/dx-react-demos/src/demo-sources/grid-filtering/bootstrap4/custom-filter-row.jsx b/packages/dx-react-demos/src/demo-sources/grid-filtering/bootstrap4/custom-filter-row.jsx new file mode 100644 index 0000000000..8fcb9c889f --- /dev/null +++ b/packages/dx-react-demos/src/demo-sources/grid-filtering/bootstrap4/custom-filter-row.jsx @@ -0,0 +1,93 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import { + FilteringState, + IntegratedFiltering, +} from '@devexpress/dx-react-grid'; +import { + Grid, + Table, + TableHeaderRow, + TableFilterRow, +} from '@devexpress/dx-react-grid-bootstrap4'; + +import { + generateRows, + globalSalesValues, +} from '../../../demo-data/generator'; + +const UnitsFilterCell = ({ filter, onFilter }) => ( + +); +UnitsFilterCell.propTypes = { + filter: PropTypes.shape({ + value: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + ]).isRequired, + }), + onFilter: PropTypes.func.isRequired, +}; +UnitsFilterCell.defaultProps = { + filter: null, +}; + +const FilterCell = (props) => { + if (props.column.name === 'units') { + return ; + } + return ; +}; +FilterCell.propTypes = { + column: PropTypes.shape({ name: PropTypes.string }).isRequired, +}; + +export default class Demo extends React.PureComponent { + constructor(props) { + super(props); + + this.state = { + columns: [ + { name: 'product', title: 'Product' }, + { name: 'region', title: 'Region' }, + { name: 'sector', title: 'Sector' }, + { name: 'units', title: 'Quantity' }, + ], + tableColumnExtensions: [ + { columnName: 'units', align: 'right' }, + ], + rows: generateRows({ columnValues: globalSalesValues, length: 8 }), + }; + } + render() { + const { rows, columns, tableColumnExtensions } = this.state; + + return ( +
+ + + +
+ onFilter(e.target.value ? { value: e.target.value } : null)} + /> +
+ + + + + ); + } +} diff --git a/packages/dx-react-demos/src/demo-sources/grid-filtering/disable-column-filtering.jsxt b/packages/dx-react-demos/src/demo-sources/grid-filtering/disable-column-filtering.jsxt index 631aee75ee..9879f3778c 100644 --- a/packages/dx-react-demos/src/demo-sources/grid-filtering/disable-column-filtering.jsxt +++ b/packages/dx-react-demos/src/demo-sources/grid-filtering/disable-column-filtering.jsxt @@ -1,4 +1,4 @@ -import React from 'react';<%&additionalImports%> +import * as React from 'react';<%&additionalImports%> import { FilteringState, IntegratedFiltering, diff --git a/packages/dx-react-demos/src/theme-registry.js b/packages/dx-react-demos/src/theme-registry.js index 4265b9025d..4258fc32dc 100644 --- a/packages/dx-react-demos/src/theme-registry.js +++ b/packages/dx-react-demos/src/theme-registry.js @@ -12,6 +12,22 @@ export const themes = [{ title: 'Material UI Dark', DemoContainer: require('./theme-sources/material-ui/demo-container').Dark, }], +}, { + name: 'bootstrap4', + title: 'Bootstrap 4', + variants: [{ + name: 'default', + title: 'Bootstrap 4', + DemoContainer: require('./theme-sources/bootstrap4/demo-container').default, + }, { + name: 'cyborg', + title: 'Bootstrap 4 (Bootswatch Cyborg)', + DemoContainer: require('./theme-sources/bootstrap4/demo-container').Cyborg, + }, { + name: 'custom', + title: 'Bootstrap 4 (Custom)', + DemoContainer: require('./theme-sources/bootstrap4/demo-container').Custom, + }], }, { name: 'bootstrap3', title: 'Bootstrap 3', @@ -19,18 +35,10 @@ export const themes = [{ name: 'default', title: 'Bootstrap 3', DemoContainer: require('./theme-sources/bootstrap3/demo-container').default, - }, { - name: 'journal', - title: 'Bootstrap 3 (Bootswatch Journal)', - DemoContainer: require('./theme-sources/bootstrap3/demo-container').Journal, }, { name: 'darkly', title: 'Bootstrap 3 (Bootswatch Darkly)', DemoContainer: require('./theme-sources/bootstrap3/demo-container').Darkly, - }, { - name: 'united', - title: 'Bootstrap 3 (Bootswatch United)', - DemoContainer: require('./theme-sources/bootstrap3/demo-container').United, }, { name: 'custom', title: 'Bootstrap 3 (Custom)', diff --git a/packages/dx-react-demos/src/theme-sources/bootstrap3/demo-container.jsx b/packages/dx-react-demos/src/theme-sources/bootstrap3/demo-container.jsx index dbb47dedfe..b7741fd33a 100644 --- a/packages/dx-react-demos/src/theme-sources/bootstrap3/demo-container.jsx +++ b/packages/dx-react-demos/src/theme-sources/bootstrap3/demo-container.jsx @@ -16,16 +16,10 @@ DemoContainer.propTypes = { export default DemoContainer; const THEMES = [{ - name: 'journal', - link: 'https://bootswatch.com/3/journal/bootstrap.min.css', -}, { name: 'darkly', link: 'https://bootswatch.com/3/darkly/bootstrap.min.css', -}, { - name: 'united', - link: 'https://bootswatch.com/3/united/bootstrap.min.css', }]; -const CUSTOM_THEME = 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'; +const CUSTOM_THEME = 'https://bootswatch.com/3/united/bootstrap.min.css'; class DemoFrame extends React.PureComponent { constructor(props, context) { @@ -115,9 +109,9 @@ class DemoFrame extends React.PureComponent { initialContent={this.markup} mountTarget="#mountPoint" > - {themeName === 'custom' && ( + {themeName === 'custom' ? ( - )} + ) : null}
{ this.node = node; }} /> )} @@ -136,7 +130,5 @@ DemoFrame.contextTypes = { embeddedDemoOptions: PropTypes.object.isRequired, }; -export const Journal = props => ; export const Darkly = props => ; -export const United = props => ; export const Custom = props => ; diff --git a/packages/dx-react-demos/src/theme-sources/bootstrap4/components/highlighted-cell.jsx b/packages/dx-react-demos/src/theme-sources/bootstrap4/components/highlighted-cell.jsx new file mode 100644 index 0000000000..e95f6930b3 --- /dev/null +++ b/packages/dx-react-demos/src/theme-sources/bootstrap4/components/highlighted-cell.jsx @@ -0,0 +1,36 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; + +const getColor = (amount) => { + if (amount < 3000) { + return '#fc7a76'; + } + if (amount < 5000) { + return '#ffb294'; + } + if (amount < 8000) { + return '#ffd59f'; + } + return '#c3e2b7'; +}; + +export const HighlightedCell = ({ tableColumn, value, style }) => ( +
+); +HighlightedCell.propTypes = { + value: PropTypes.number.isRequired, + tableColumn: PropTypes.object, + style: PropTypes.object, +}; +HighlightedCell.defaultProps = { + style: {}, + tableColumn: {}, +}; diff --git a/packages/dx-react-demos/src/theme-sources/bootstrap4/components/loading.css b/packages/dx-react-demos/src/theme-sources/bootstrap4/components/loading.css new file mode 100644 index 0000000000..f301661aa8 --- /dev/null +++ b/packages/dx-react-demos/src/theme-sources/bootstrap4/components/loading.css @@ -0,0 +1,29 @@ +.loading-shading { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + + background: rgba(255, 255, 255, .3); +} + +.loading-icon { + position: absolute; + font-size: 25px; + top: calc(45% - 10px); + left: calc(50% - 10px); + + -animation: spin .7s infinite linear; + -webkit-animation: spin2 .7s infinite linear; +} + +@-webkit-keyframes spin2 { + from { -webkit-transform: rotate(360deg); } + to { -webkit-transform: rotate(0deg); } +} + +@keyframes spin { + from { transform: scale(1) rotate(360deg); } + to { transform: scale(1) rotate(0deg); } +} diff --git a/packages/dx-react-demos/src/theme-sources/bootstrap4/components/loading.jsx b/packages/dx-react-demos/src/theme-sources/bootstrap4/components/loading.jsx new file mode 100644 index 0000000000..5cfb7ebe78 --- /dev/null +++ b/packages/dx-react-demos/src/theme-sources/bootstrap4/components/loading.jsx @@ -0,0 +1,10 @@ +import * as React from 'react'; +import './loading.css'; + +export const Loading = () => ( +
+
+ +
+
+); diff --git a/packages/dx-react-demos/src/theme-sources/bootstrap4/components/progress-bar-cell.jsx b/packages/dx-react-demos/src/theme-sources/bootstrap4/components/progress-bar-cell.jsx new file mode 100644 index 0000000000..0712637070 --- /dev/null +++ b/packages/dx-react-demos/src/theme-sources/bootstrap4/components/progress-bar-cell.jsx @@ -0,0 +1,36 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; + +export const ProgressBarCell = ({ value, style }) => { + const percent = value * 100; + return ( +
+ ); +}; +ProgressBarCell.propTypes = { + value: PropTypes.number.isRequired, + style: PropTypes.object, +}; +ProgressBarCell.defaultProps = { + style: {}, +}; diff --git a/packages/dx-react-demos/src/theme-sources/bootstrap4/demo-container.jsx b/packages/dx-react-demos/src/theme-sources/bootstrap4/demo-container.jsx new file mode 100644 index 0000000000..38b1d89d87 --- /dev/null +++ b/packages/dx-react-demos/src/theme-sources/bootstrap4/demo-container.jsx @@ -0,0 +1,126 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import Frame from 'react-frame-component'; +import { FormGroup, ControlLabel, FormControl, InputGroup, Button } from 'react-bootstrap'; +import '@devexpress/dx-react-grid-bootstrap4/dist/dx-react-grid-bootstrap4.css'; + +const CUSTOM_THEME = 'https://bootswatch.com/4/sketchy/bootstrap.min.css'; +const ICONS = 'https://cdnjs.cloudflare.com/ajax/libs/open-iconic/1.1.1/font/css/open-iconic-bootstrap.css'; +const THEMES = [{ + name: 'default', + link: 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css', +}, { + name: 'cyborg', + link: 'https://bootswatch.com/4/cyborg/bootstrap.min.css', +}]; + +class DemoFrame extends React.PureComponent { + constructor(props, context) { + super(props, context); + + this.state = { + customThemeLink: CUSTOM_THEME, + frameHeight: 600, + }; + + const { themeName, url } = this.props; + const { scriptPath } = this.context.embeddedDemoOptions; + const themeLink = themeName !== 'custom' && THEMES.find(({ name }) => name === themeName).link; + this.markup = ` + + + + + + + +
+
+
Loading...
+
+ + + `; + } + componentDidMount() { + this.updateFrameHeight(); + } + updateFrameHeight() { + setTimeout(this.updateFrameHeight.bind(this)); + + if (!this.node) return; + const { height } = this.node.ownerDocument.body.getBoundingClientRect(); + if (height !== this.state.frameHeight) { + this.setState({ frameHeight: height }); + } + } + render() { + const { themeName, children } = this.props; + const { embeddedDemoOptions: { frame } } = this.context; + const { customThemeLink, frameHeight } = this.state; + + return ( +
+ {!frame && themeName === 'custom' && ( +
+ + Custom theme link + + { this.customThemeLinkNode = node; }} + defaultValue={customThemeLink} + /> + + + + + + + )} + + {frame + ? children + : ( + + {themeName === 'custom' ? ( + + ) : null} +
{ this.node = node; }} /> + + )} +
+ ); + } +} + +DemoFrame.propTypes = { + themeName: PropTypes.string.isRequired, + url: PropTypes.string.isRequired, + children: PropTypes.node.isRequired, +}; + +DemoFrame.contextTypes = { + embeddedDemoOptions: PropTypes.object.isRequired, +}; + +export default props => ; +export const Cyborg = props => ; +export const Custom = props => ; diff --git a/packages/dx-react-demos/src/theme-sources/bootstrap4/demo-source-data.json b/packages/dx-react-demos/src/theme-sources/bootstrap4/demo-source-data.json new file mode 100644 index 0000000000..95020bae88 --- /dev/null +++ b/packages/dx-react-demos/src/theme-sources/bootstrap4/demo-source-data.json @@ -0,0 +1,4 @@ +{ + "additionalImports": "\nimport { Card } from 'reactstrap';", + "wrapperTag": "Card" +} diff --git a/packages/dx-react-grid-bootstrap3/README.md b/packages/dx-react-grid-bootstrap3/README.md index 5bb15bcf9a..0b29e31620 100644 --- a/packages/dx-react-grid-bootstrap3/README.md +++ b/packages/dx-react-grid-bootstrap3/README.md @@ -34,11 +34,11 @@ const App = () => ( ); ``` -Make sure that [React-Boostrap](https://react-bootstrap.github.io) dependencies are installed and properly configured. Check the React-Bootstrap's [Getting Started](https://react-bootstrap.github.io/getting-started.html) article for configuration details. +Make sure that [React-Boostrap](https://react-bootstrap.github.io) dependencies are installed and properly configured. Check the React-Bootstrap's [Getting Started](https://react-bootstrap.github.io/getting-started/introduction) article for configuration details. ## Getting started -This package provides components and plugins implementing Bootstrap 3 rendering for the React Grid, which you can use instead of the original React Grid package ones. +This package provides components and plugins that implement Bootstrap 3 rendering for the React Grid, which you can use instead of the original React Grid package ones. See [demos](https://devexpress.github.io/devextreme-reactive/react/grid/demos/) for more information. diff --git a/packages/dx-react-grid-bootstrap3/src/templates/column-chooser/overlay.jsx b/packages/dx-react-grid-bootstrap3/src/templates/column-chooser/overlay.jsx index 89a27475e1..29bee6c43f 100644 --- a/packages/dx-react-grid-bootstrap3/src/templates/column-chooser/overlay.jsx +++ b/packages/dx-react-grid-bootstrap3/src/templates/column-chooser/overlay.jsx @@ -6,7 +6,7 @@ import { Popover } from './popover'; export const Overlay = ({ visible, target, children, onHide, - ...restProps + toggle, ...restProps }) => ( (
+ ${value} + +
+
+
+
+ + +); +``` + +Make sure that [reactstrap](https://reactstrap.github.io/) dependencies are installed and properly configured. Check the reactstrap's [Getting Started](https://reactstrap.github.io/) article for configuration details. + +## Getting started + +The dx-react-grid-bootstrap4 package provides components and plugins that implement Bootstrap 4 rendering for the React Grid. You can use them instead of the ones the React Grid package provides. + +See [demos](https://devexpress.github.io/devextreme-reactive/react/grid/demos/) for more information. + +## Reference + +The package exposes components and plugins with injected template components. + +Components: + +- [Grid](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/grid/) - the Grid component + +Plugins: + +- [DragDropProvider](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/drag-drop-provider/) - implements the drag-and-drop functionality and visualizes columns that are being dragged +- [GroupingPanel](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/grouping-panel/) - renders the Grouping Panel in the Grid’s header +- [ColumnChooser](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/column-chooser/) - allows a user to show and hide grid columns at runtime +- [PagingPanel](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/paging-panel/) - renders the paging panel +- [Table](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/table/) - renders Grid data as a table +- [TableColumnResizing](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/table-column-resizing/) - manages table column widths +- [TableColumnReordering](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/table-column-reordering/) - manages the displayed columns’ order +- [TableColumnVisibility](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/table-column-visibility/) - manages Grid columns’ visibility +- [TableEditColumn](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/table-edit-column/) - renders a command column +- [TableEditRow](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/table-edit-row/) - renders a row being edited +- [TableFilterRow](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/table-filter-row/) - renders a filter row +- [TableGroupRow](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/table-group-row/) - renders group rows and enables them to expand and collapse +- [TableHeaderRow](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/table-header-row/) - renders the table’s header row +- [TableRowDetail](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/table-row-detail/) - renders detail rows +- [TableSelection](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/table-selection/) - visualizes table rows’ selection state by rendering selection checkboxes and highlighting the selected rows +- [Toolbar](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/toolbar/) - renders the Grid toolbar +- [VirtualTable](https://devexpress.github.io/devextreme-reactive/react/grid/docs/reference/virtual-table/) - renders a scrollable table + +## License + +[DevExtreme licensing](https://js.devexpress.com/licensing/). diff --git a/packages/dx-react-grid-bootstrap4/css-stub.js b/packages/dx-react-grid-bootstrap4/css-stub.js new file mode 100644 index 0000000000..f053ebf797 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/css-stub.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/packages/dx-react-grid-bootstrap4/package.json b/packages/dx-react-grid-bootstrap4/package.json new file mode 100644 index 0000000000..7133784aa8 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/package.json @@ -0,0 +1,99 @@ +{ + "name": "@devexpress/dx-react-grid-bootstrap4", + "version": "1.1.0-beta.1", + "description": "Bootstrap 4 templates for DevExtreme React Grid component", + "author": { + "name": "Developer Express Inc.", + "url": "https://www.devexpress.com/" + }, + "bugs": { + "url": "https://github.com/DevExpress/devextreme-reactive/issues" + }, + "homepage": "https://devexpress.github.io/devextreme-reactive/", + "repository": { + "type": "git", + "url": "git+https://github.com/DevExpress/devextreme-reactive.git" + }, + "keywords": [ + "react", + "data", + "grid", + "table", + "bootstrap", + "react-component" + ], + "license": "SEE LICENSE IN README.md", + "publishConfig": { + "access": "public" + }, + "main": "dist/dx-react-grid-bootstrap4.umd.js", + "module": "dist/dx-react-grid-bootstrap4.es.js", + "styles": "dist/dx-react-grid-bootstrap4.css", + "globalName": "DevExpress.DXReactGridBootstrap4", + "files": [ + "dist" + ], + "types": "dist/dx-react-grid-bootstrap4.d.ts", + "scripts": { + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --coverage", + "build": "rollup -c rollup.config.js", + "build:watch": "rollup -c rollup.config.js -w", + "lint": "eslint \"src/**\"", + "lint:fix": "yarn run lint -- --fix" + }, + "jest": { + "moduleNameMapper": { + "\\.css$": "/css-stub.js" + }, + "setupFiles": [ + "/setup-enzyme.js" + ] + }, + "devDependencies": { + "@devexpress/dx-grid-core": "1.1.0-beta.1", + "@devexpress/dx-react-core": "1.1.0-beta.1", + "@devexpress/dx-react-grid": "1.1.0-beta.1", + "@devexpress/dx-testing": "1.1.0-beta.1", + "babel-core": "^6.26.0", + "babel-jest": "^22.4.1", + "babel-plugin-external-helpers": "^6.22.0", + "babel-plugin-transform-object-rest-spread": "^6.26.0", + "babel-plugin-transform-runtime": "^6.23.0", + "babel-preset-es2015": "^6.24.1", + "babel-preset-react": "^6.24.1", + "core-js": "^2.5.3", + "enzyme": "^3.3.0", + "enzyme-adapter-react-16": "^1.1.1", + "eslint": "^4.18.1", + "eslint-config-airbnb": "^16.1.0", + "eslint-config-airbnb-base": "^12.1.0", + "eslint-plugin-filenames": "^1.2.0", + "eslint-plugin-import": "^2.9.0", + "eslint-plugin-jest": "^21.12.2", + "eslint-plugin-jsx-a11y": "^6.0.3", + "eslint-plugin-react": "^7.7.0", + "jest": "^22.4.2", + "react": "^16.2.0", + "react-dom": "^16.2.0", + "react-test-renderer": "^16.2.0", + "rollup": "0.50.0", + "rollup-plugin-babel": "^3.0.3", + "rollup-plugin-css-only": "^0.4.0", + "rollup-plugin-license": "^0.5.0", + "rollup-plugin-node-resolve": "^3.0.2", + "reactstrap": "5.0.0-beta" + }, + "dependencies": { + "classnames": "^2.2.5", + "prop-types": "^15.6.1" + }, + "peerDependencies": { + "@devexpress/dx-grid-core": "1.1.0-beta.1", + "@devexpress/dx-react-core": "1.1.0-beta.1", + "@devexpress/dx-react-grid": "1.1.0-beta.1", + "react": "^16.2.0", + "reactstrap": "5.0.0-beta" + } +} diff --git a/packages/dx-react-grid-bootstrap4/rollup.config.js b/packages/dx-react-grid-bootstrap4/rollup.config.js new file mode 100644 index 0000000000..c2712db51a --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/rollup.config.js @@ -0,0 +1,32 @@ +import babel from 'rollup-plugin-babel'; +import license from 'rollup-plugin-license'; +import resolve from 'rollup-plugin-node-resolve'; +import css from 'rollup-plugin-css-only' +import { banner, external, babelrc, globals } from '../../tools/rollup-utils'; +import pkg from './package.json'; + +export default { + input: 'src/index.js', + sourcemap: true, + output: [ + { file: pkg.main, format: 'umd', name: pkg.globalName }, + { file: pkg.module, format: 'es' }, + ], + globals: globals(), + external: external(__dirname), + plugins: [ + css({ output: pkg.styles }), + resolve({ + main: false, + extensions: ['.js', '.jsx'], + }), + babel(Object.assign({ + babelrc: false, + runtimeHelpers: true, + exclude: 'node_modules/**', + }, babelrc(__dirname))), + license({ + banner, + }), + ], +}; diff --git a/packages/dx-react-grid-bootstrap4/setup-enzyme.js b/packages/dx-react-grid-bootstrap4/setup-enzyme.js new file mode 100644 index 0000000000..135148a7c4 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/setup-enzyme.js @@ -0,0 +1,4 @@ +const Enzyme = require('enzyme'); +const Adapter = require('enzyme-adapter-react-16'); + +Enzyme.configure({ adapter: new Adapter() }); diff --git a/packages/dx-react-grid-bootstrap4/src/grid.jsx b/packages/dx-react-grid-bootstrap4/src/grid.jsx new file mode 100644 index 0000000000..b8825cf9f6 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/grid.jsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import { Grid as GridBase } from '@devexpress/dx-react-grid'; +import { Root } from './templates/layout'; + +export const Grid = ({ children, ...props }) => ( + + {children} + +); + +Grid.Root = Root; + +Grid.propTypes = { + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + ]).isRequired, +}; + diff --git a/packages/dx-react-grid-bootstrap4/src/index.js b/packages/dx-react-grid-bootstrap4/src/index.js new file mode 100644 index 0000000000..7dce8767ef --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/index.js @@ -0,0 +1,20 @@ +import './main.css'; + +export { Grid } from './grid'; +export { ColumnChooser } from './plugins/column-chooser'; +export { DragDropProvider } from './plugins/drag-drop-provider'; +export { PagingPanel } from './plugins/paging-panel'; +export { GroupingPanel } from './plugins/grouping-panel'; +export { TableRowDetail } from './plugins/table-row-detail'; +export { TableGroupRow } from './plugins/table-group-row'; +export { TableSelection } from './plugins/table-selection'; +export { Table } from './plugins/table'; +export { VirtualTable } from './plugins/virtual-table'; +export { TableFilterRow } from './plugins/table-filter-row'; +export { TableHeaderRow } from './plugins/table-header-row'; +export { TableEditRow } from './plugins/table-edit-row'; +export { TableEditColumn } from './plugins/table-edit-column'; +export { TableColumnVisibility } from './plugins/table-column-visibility'; +export { TableColumnReordering } from './plugins/table-column-reordering'; +export { TableColumnResizing } from './plugins/table-column-resizing'; +export { Toolbar } from './plugins/toolbar'; diff --git a/packages/dx-react-grid-bootstrap4/src/main.css b/packages/dx-react-grid-bootstrap4/src/main.css new file mode 100644 index 0000000000..d49914f84a --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/main.css @@ -0,0 +1,14 @@ +.dx-rg-bs4-cursor-pointer { + cursor: pointer; +} +.dx-rg-bs4-user-select-none { + user-select: none; + -moz-user-select: none; + -webkit-user-select: 'none'; +} +.dx-rg-bs4-inactive { + opacity: 0.3; +} +.dx-rg-bs4-overflow-hidden { + overflow: hidden; +} diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/column-chooser.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/column-chooser.jsx new file mode 100644 index 0000000000..6b985c109a --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/column-chooser.jsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { ColumnChooser as ColumnChooserBase } from '@devexpress/dx-react-grid'; +import { Overlay } from '../templates/column-chooser/overlay'; +import { Container } from '../templates/column-chooser/container'; +import { Item } from '../templates/column-chooser/item'; +import { ToggleButton } from '../templates/column-chooser/toggle-button'; + +export const ColumnChooser = props => ( + +); + +ColumnChooser.Container = Container; +ColumnChooser.Item = Item; +ColumnChooser.Overlay = Overlay; +ColumnChooser.ToggleButton = ToggleButton; diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/drag-drop-provider.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/drag-drop-provider.jsx new file mode 100644 index 0000000000..03ca66b68c --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/drag-drop-provider.jsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import { DragDropProvider as DragDropProviderBase } from '@devexpress/dx-react-grid'; +import { Container, Column } from '../templates/drag-drop'; + +export class DragDropProvider extends React.PureComponent { + render() { + return ( + + ); + } +} + +DragDropProvider.Container = Container; +DragDropProvider.Column = Column; diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/grouping-panel.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/grouping-panel.jsx new file mode 100644 index 0000000000..ddc9e3565e --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/grouping-panel.jsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import { GroupingPanel as GroupingPanelBase, GroupPanelLayout } from '@devexpress/dx-react-grid'; +import { GroupPanelContainer } from '../templates/group-panel-container'; +import { GroupPanelItem } from '../templates/group-panel-item'; +import { GroupPanelEmptyMessage } from '../templates/group-panel-empty-message'; + +const defaultMessages = { + groupByColumn: 'Drag a column header here to group by that column', +}; + +export class GroupingPanel extends React.PureComponent { + render() { + const { messages, ...restProps } = this.props; + + return ( + + ); + } +} + +GroupingPanel.Container = GroupPanelContainer; +GroupingPanel.Item = GroupPanelItem; +GroupingPanel.EmptyMessage = GroupPanelEmptyMessage; + +GroupingPanel.propTypes = { + showSortingControls: PropTypes.bool, + messages: PropTypes.shape({ + groupByColumn: PropTypes.string, + }), +}; + +GroupingPanel.defaultProps = { + showSortingControls: false, + messages: {}, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/paging-panel.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/paging-panel.jsx new file mode 100644 index 0000000000..699c49e9fa --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/paging-panel.jsx @@ -0,0 +1,40 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import { PagingPanel as PagingPanelBase } from '@devexpress/dx-react-grid'; +import { Pager } from '../templates/paging-panel/pager'; + +const defaultMessages = { + showAll: 'All', + info: ({ from, to, count }) => + `${from}${from < to ? `-${to}` : ''} of ${count}`, +}; + +export class PagingPanel extends React.PureComponent { + render() { + const { messages, ...restProps } = this.props; + + return ( + + ); + } +} + +PagingPanel.Container = Pager; + +PagingPanel.propTypes = { + messages: PropTypes.shape({ + showAll: PropTypes.string, + info: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.func, + ]), + }), +}; + +PagingPanel.defaultProps = { + messages: {}, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/table-column-reordering.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/table-column-reordering.jsx new file mode 100644 index 0000000000..9a653d38df --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/table-column-reordering.jsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import { DropTarget } from '@devexpress/dx-react-core'; +import { TableColumnReordering as TableColumnReorderingBase } from '@devexpress/dx-react-grid'; +import { TableRow } from '../templates/table-row'; +import { TableReorderingCell } from '../templates/table-reordering-cell'; + +const TableContainer = ({ + onOver, onLeave, onDrop, children, // eslint-disable-line react/prop-types +}) => ( + + {children} + +); + +// eslint-disable-next-line react/prop-types +const ReorderingRow = ({ style, ...restParams }) => ( + +); + +export const TableColumnReordering = props => ( + +); diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/table-column-resizing.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/table-column-resizing.jsx new file mode 100644 index 0000000000..9e9509f2b3 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/table-column-resizing.jsx @@ -0,0 +1,12 @@ +import * as React from 'react'; +import { TableColumnResizing as TableColumnResizingBase } from '@devexpress/dx-react-grid'; + +export class TableColumnResizing extends React.PureComponent { + render() { + return ( + + ); + } +} diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/table-column-visibility.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/table-column-visibility.jsx new file mode 100644 index 0000000000..162a3b9113 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/table-column-visibility.jsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import { TableColumnVisibility as TableColumnVisibilityBase } from '@devexpress/dx-react-grid'; +import { EmptyMessage } from '../templates/empty-message'; + +const defaultMessages = { + noColumns: 'Nothing to show', +}; + +export class TableColumnVisibility extends React.PureComponent { + render() { + const { messages, ...restProps } = this.props; + + return ( + + ); + } +} + +TableColumnVisibility.EmptyMessage = EmptyMessage; + +TableColumnVisibility.propTypes = { + messages: PropTypes.shape({ + noColumns: PropTypes.string, + }), +}; + +TableColumnVisibility.defaultProps = { + messages: {}, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/table-edit-column.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/table-edit-column.jsx new file mode 100644 index 0000000000..9b3bed43c7 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/table-edit-column.jsx @@ -0,0 +1,53 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import { TableEditColumn as TableEditColumnBase } from '@devexpress/dx-react-grid'; +import { + EditCommandHeadingCell, + EditCommandCell, + CommandButton, +} from '../templates/table-edit-command-cell'; + +const defaultMessages = { + addCommand: 'New', + editCommand: 'Edit', + deleteCommand: 'Delete', + commitCommand: 'Save', + cancelCommand: 'Cancel', +}; + +export class TableEditColumn extends React.PureComponent { + render() { + const { + messages, + ...restProps + } = this.props; + + return ( + + ); + } +} + +TableEditColumn.Command = CommandButton; +TableEditColumn.Cell = EditCommandCell; +TableEditColumn.HeaderCell = EditCommandHeadingCell; + +TableEditColumn.propTypes = { + messages: PropTypes.shape({ + addCommand: PropTypes.string, + editCommand: PropTypes.string, + deleteCommand: PropTypes.string, + commitCommand: PropTypes.string, + cancelCommand: PropTypes.string, + }), +}; + +TableEditColumn.defaultProps = { + messages: {}, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/table-edit-row.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/table-edit-row.jsx new file mode 100644 index 0000000000..647a81f071 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/table-edit-row.jsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { TableEditRow as TableEditRowBase } from '@devexpress/dx-react-grid'; +import { EditCell } from '../templates/table-edit-cell'; +import { TableRow } from '../templates/table-row'; + +export class TableEditRow extends React.PureComponent { + render() { + return ( + + ); + } +} + +TableEditRow.Cell = EditCell; +TableEditRow.Row = TableRow; diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/table-filter-row.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/table-filter-row.jsx new file mode 100644 index 0000000000..63dbce5c59 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/table-filter-row.jsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { TableFilterRow as TableFilterRowBase } from '@devexpress/dx-react-grid'; +import { TableFilterCell } from '../templates/table-filter-cell'; +import { TableRow } from '../templates/table-row'; + +export class TableFilterRow extends React.PureComponent { + render() { + return ( + + ); + } +} + +TableFilterRow.Cell = TableFilterCell; +TableFilterRow.Row = TableRow; diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/table-group-row.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/table-group-row.jsx new file mode 100644 index 0000000000..1023fc502f --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/table-group-row.jsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +import { TableGroupRow as TableGroupRowBase } from '@devexpress/dx-react-grid'; +import { TableGroupCell } from '../templates/table-group-row-cell'; +import { TableRow } from '../templates/table-row'; + +export class TableGroupRow extends React.PureComponent { + render() { + return ( + + ); + } +} + +TableGroupRow.Row = TableRow; +TableGroupRow.Cell = TableGroupCell; diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/table-header-row.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/table-header-row.jsx new file mode 100644 index 0000000000..bce1fec30a --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/table-header-row.jsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { TableHeaderRow as TableHeaderRowBase } from '@devexpress/dx-react-grid'; +import { TableHeaderCell } from '../templates/table-header-cell'; +import { TableRow } from '../templates/table-row'; + +export class TableHeaderRow extends React.PureComponent { + render() { + return ( + + ); + } +} + +TableHeaderRow.Cell = TableHeaderCell; +TableHeaderRow.Row = TableRow; diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/table-row-detail.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/table-row-detail.jsx new file mode 100644 index 0000000000..be6e94420b --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/table-row-detail.jsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import { TableRowDetail as TableRowDetailBase } from '@devexpress/dx-react-grid'; +import { TableDetailToggleCell } from '../templates/table-detail-toggle-cell'; +import { TableDetailCell } from '../templates/table-detail-cell'; +import { TableRow } from '../templates/table-row'; + +export class TableRowDetail extends React.PureComponent { + render() { + return ( + + ); + } +} + +TableRowDetail.Cell = TableDetailCell; +TableRowDetail.ToggleCell = TableDetailToggleCell; +TableRowDetail.Row = TableRow; diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/table-selection.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/table-selection.jsx new file mode 100644 index 0000000000..df4ac4eaec --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/table-selection.jsx @@ -0,0 +1,23 @@ +import * as React from 'react'; + +import { TableSelection as TableSelectionBase } from '@devexpress/dx-react-grid'; +import { TableSelectAllCell } from '../templates/table-select-all-cell'; +import { TableSelectCell } from '../templates/table-select-cell'; +import { TableSelectRow } from '../templates/table-select-row'; + +export class TableSelection extends React.PureComponent { + render() { + return ( + + ); + } +} + +TableSelection.Cell = TableSelectCell; +TableSelection.HeaderCell = TableSelectAllCell; diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/table.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/table.jsx new file mode 100644 index 0000000000..5da594eaca --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/table.jsx @@ -0,0 +1,53 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import { Table as TableBase } from '@devexpress/dx-react-grid'; +import { TableLayout } from '../templates/table-layout'; +import { TableCell } from '../templates/table-cell'; +import { TableStubCell } from '../templates/table-stub-cell'; +import { TableStubHeaderCell } from '../templates/table-stub-header-cell'; +import { TableNoDataCell } from '../templates/table-no-data-cell'; +import { TableRow } from '../templates/table-row'; + +const defaultMessages = { + noData: 'No data', +}; + +export class Table extends React.PureComponent { + render() { + const { + messages, + ...restProps + } = this.props; + + return ( + + ); + } +} + +Table.Cell = TableCell; +Table.Row = TableRow; +Table.NoDataCell = TableNoDataCell; +Table.NoDataRow = TableRow; +Table.StubCell = TableStubCell; +Table.StubHeaderCell = TableStubCell; + +Table.propTypes = { + messages: PropTypes.shape({ + noData: PropTypes.string, + }), +}; + +Table.defaultProps = { + messages: {}, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/toolbar.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/toolbar.jsx new file mode 100644 index 0000000000..160e8d5b16 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/toolbar.jsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import { Toolbar as ToolbarBase } from '@devexpress/dx-react-grid'; +import { Toolbar as Root } from '../templates/toolbar/toolbar'; +import { FlexibleSpace } from '../templates/toolbar/flexible-space'; + +export class Toolbar extends React.PureComponent { + render() { + return ( + + ); + } +} + +Toolbar.Root = Root; diff --git a/packages/dx-react-grid-bootstrap4/src/plugins/virtual-table.jsx b/packages/dx-react-grid-bootstrap4/src/plugins/virtual-table.jsx new file mode 100644 index 0000000000..e98157e32b --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/plugins/virtual-table.jsx @@ -0,0 +1,70 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import { createRenderComponent } from '@devexpress/dx-react-core'; +import { Table as TableBase } from '@devexpress/dx-react-grid'; +import { VirtualTableLayout } from '../templates/virtual-table-layout'; +import { TableCell } from '../templates/table-cell'; +import { TableRow } from '../templates/table-row'; +import { TableNoDataCell } from '../templates/table-no-data-cell'; +import { TableStubCell } from '../templates/table-stub-cell'; +import { TableStubHeaderCell } from '../templates/table-stub-header-cell'; + +const defaultMessages = { + noData: 'No data', +}; + +export class VirtualTable extends React.PureComponent { + constructor(props) { + super(props); + + const { height, estimatedRowHeight } = props; + this.layoutRenderComponent = + createRenderComponent(VirtualTableLayout, { height, estimatedRowHeight }); + } + componentWillReceiveProps({ height, estimatedRowHeight }) { + this.layoutRenderComponent.update({ height, estimatedRowHeight }); + } + render() { + const { + height, + estimatedRowHeight, + messages, + ...restProps + } = this.props; + + return ( + + ); + } +} + +VirtualTable.Cell = TableCell; +VirtualTable.Row = TableRow; +VirtualTable.NoDataCell = TableNoDataCell; +VirtualTable.NoDataRow = TableRow; +VirtualTable.StubCell = TableStubCell; +VirtualTable.StubHeaderCell = TableStubCell; + +VirtualTable.propTypes = { + estimatedRowHeight: PropTypes.number, + height: PropTypes.number, + messages: PropTypes.shape({ + noData: PropTypes.string, + }), +}; + +VirtualTable.defaultProps = { + estimatedRowHeight: 37, + height: 530, + messages: {}, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/container.jsx b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/container.jsx new file mode 100644 index 0000000000..726d1837a4 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/container.jsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; + +export const Container = ({ + children, + className, + ...restProps +}) => ( +
+ {children} +
+); + +Container.propTypes = { + children: PropTypes.node.isRequired, + className: PropTypes.string, +}; + +Container.defaultProps = { + className: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/container.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/container.test.jsx new file mode 100644 index 0000000000..f6b89b7029 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/container.test.jsx @@ -0,0 +1,31 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { Container } from './container'; + +describe('Container', () => { + it('should pass the className prop to the root element', () => { + const tree = shallow(( + +
+ + )); + + expect(tree.is('.py-2.custom-class')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + +
+ + )); + + expect(tree.props().data) + .toMatchObject({ a: 1 }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/item.css b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/item.css new file mode 100644 index 0000000000..48807da2a1 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/item.css @@ -0,0 +1,7 @@ +.dx-rg-bs4-column-chooser-item { + font-size: initial; +} + +.dx-rg-bs4-column-chooser-checkbox { + margin-right: 10px; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/item.jsx b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/item.jsx new file mode 100644 index 0000000000..8501fc11b8 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/item.jsx @@ -0,0 +1,55 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; +import './item.css'; + +const handleMouseDown = (e) => { e.currentTarget.style.outline = 'none'; }; +const handleBlur = (e) => { e.currentTarget.style.outline = ''; }; + +export const Item = ({ + item: { column, hidden }, + onToggle, className, + disabled, ...restProps +}) => ( + +); + +Item.propTypes = { + item: PropTypes.shape({ + column: PropTypes.shape({ + name: PropTypes.string, + }), + hidden: PropTypes.bool, + }).isRequired, + onToggle: PropTypes.func, + className: PropTypes.string, + disabled: PropTypes.bool, +}; + +Item.defaultProps = { + onToggle: () => {}, + className: undefined, + disabled: false, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/item.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/item.test.jsx new file mode 100644 index 0000000000..0bf952446c --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/item.test.jsx @@ -0,0 +1,115 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { Item } from './item'; + +const defaultProps = { + item: { + column: { name: 'a', title: 'A' }, + hidden: false, + }, +}; + +describe('Item', () => { + it('should set item checkbox value depending on the "hidden" property', () => { + const tree = shallow(( + + )); + + expect(tree.find('input[type="checkbox"]').prop('checked')) + .toBe(true); + + tree.setProps({ item: { column: { name: 'a', title: 'A' }, hidden: true } }); + + expect(tree.find('input[type="checkbox"]').prop('checked')) + .toBe(false); + }); + + it('should call the "onToggle" on the checkbox "onChange" event', () => { + const toggleHandler = jest.fn(); + const tree = shallow(( + + )); + + tree.find('input[type="checkbox"]') + .first() + .prop('onChange')('a'); + + expect(toggleHandler) + .toHaveBeenCalledTimes(1); + }); + + it('should call the "onToggle" on the list item "onClick" event', () => { + const toggleHandler = jest.fn(); + const tree = shallow(( + + )); + + tree.find('button') + .first() + .prop('onClick')('a'); + + expect(toggleHandler) + .toHaveBeenCalledTimes(1); + }); + + it('should render column title or name in each item', () => { + const tree = shallow(( + + )); + + expect(tree.find('button').text().trim()) + .toBe('A'); + + tree.setProps({ item: { column: { name: 'b' } } }); + + expect(tree.find('button').text().trim()) + .toBe('b'); + }); + + it('should pass the className prop to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.dropdown-item.custom-class.dx-rg-bs4-column-chooser-item.dx-rg-bs4-cursor-pointer')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.props().data) + .toMatchObject({ a: 1 }); + }); + + it('should process the disabled prop', () => { + const tree = shallow(( + + )); + expect(tree.find('button').prop('disabled')) + .toBeTruthy(); + expect(tree.find('input[type="checkbox"]').prop('disabled')) + .toBeTruthy(); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/overlay.jsx b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/overlay.jsx new file mode 100644 index 0000000000..bbdd9cc246 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/overlay.jsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import { Popover } from 'reactstrap'; + +export const Overlay = ({ + visible, children, toggle, target, onHide, ...restProps +}) => ( + target ? ( + + {children} + + ) : null +); + +Overlay.propTypes = { + children: PropTypes.node.isRequired, + toggle: PropTypes.func.isRequired, + visible: PropTypes.bool, + target: PropTypes.object, + onHide: PropTypes.func, +}; + +Overlay.defaultProps = { + visible: false, + target: undefined, + onHide: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/overlay.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/overlay.test.jsx new file mode 100644 index 0000000000..3f0479145b --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/overlay.test.jsx @@ -0,0 +1,38 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { Overlay } from './overlay'; + +describe('Overlay', () => { + const toggle = jest.fn(); + it('should pass the className prop to the root element', () => { + const tree = shallow(( + {}} + toggle={toggle} + target={{}} + > +
+ + )); + + expect(tree.is('.custom-class')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + {}} + toggle={toggle} + target={{}} + > +
+ + )); + + expect(tree.props().data) + .toMatchObject({ a: 1 }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/toggle-button.jsx b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/toggle-button.jsx new file mode 100644 index 0000000000..66428975be --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/toggle-button.jsx @@ -0,0 +1,39 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; + +export const ToggleButton = ({ + onToggle, className, + getMessage, buttonRef, + active, ...restProps +}) => { + const buttonClasses = classNames({ + btn: true, + 'btn-outline-secondary': true, + 'border-0': true, + active, + }, className); + return ( + + ); +}; + +ToggleButton.propTypes = { + onToggle: PropTypes.func.isRequired, + getMessage: PropTypes.func.isRequired, + buttonRef: PropTypes.func.isRequired, + className: PropTypes.string, + active: PropTypes.bool, +}; + +ToggleButton.defaultProps = { + className: undefined, + active: false, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/toggle-button.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/toggle-button.test.jsx new file mode 100644 index 0000000000..ce8bd9c208 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/column-chooser/toggle-button.test.jsx @@ -0,0 +1,35 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { ToggleButton } from './toggle-button'; + +const defaultProps = { + onToggle: () => {}, + getMessage: () => {}, + buttonRef: () => {}, +}; + +describe('ToggleButton', () => { + it('should pass the className prop to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.btn-outline-secondary.custom-class')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.props().data) + .toMatchObject({ a: 1 }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/drag-drop.css b/packages/dx-react-grid-bootstrap4/src/templates/drag-drop.css new file mode 100644 index 0000000000..6e2d5692e9 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/drag-drop.css @@ -0,0 +1,6 @@ +.dx-rg-bs4-drag-drop { + cursor: move; + z-index: 1000; + left: 0; + top: 0; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/drag-drop.jsx b/packages/dx-react-grid-bootstrap4/src/templates/drag-drop.jsx new file mode 100644 index 0000000000..38a008c4c6 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/drag-drop.jsx @@ -0,0 +1,57 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; + +export const Container = ({ + clientOffset, style, className, children, + ...restProps +}) => ( +
    + {children} +
+); + +Container.propTypes = { + clientOffset: PropTypes.shape({ + x: PropTypes.number.isRequired, + y: PropTypes.number.isRequired, + }).isRequired, + style: PropTypes.object, + className: PropTypes.string, + children: PropTypes.oneOfType([ + PropTypes.node, + PropTypes.arrayOf(PropTypes.node), + ]), +}; + +Container.defaultProps = { + style: {}, + className: undefined, + children: undefined, +}; + +export const Column = ({ column, className, ...restProps }) => ( +
  • + {column.title} +
  • +); + +Column.propTypes = { + column: PropTypes.object.isRequired, + className: PropTypes.string, +}; + +Column.defaultProps = { + className: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/drag-drop.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/drag-drop.test.jsx new file mode 100644 index 0000000000..ac6bf9079c --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/drag-drop.test.jsx @@ -0,0 +1,87 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { Container, Column } from './drag-drop'; + +describe('Container', () => { + it('should have correct styles', () => { + const tree = shallow(( + + )); + + expect(tree.find('ul').prop('style')) + .toMatchObject({ + transform: 'translate(calc(10px - 50%), calc(20px - 50%))', + }); + }); + + it('should apply custom styles', () => { + const tree = shallow(( + + )); + + expect(tree.find('ul').prop('style')) + .toMatchObject({ + transform: 'translate(calc(10px - 50%), calc(20px - 50%))', + color: 'red', + }); + }); + + it('should pass the className prop to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.custom-class.list-group.d-inline-block.position-fixed.dx-rg-bs4-drag-drop')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.props().data) + .toMatchObject({ a: 1 }); + }); +}); + +describe('Column', () => { + it('should pass the className prop to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.custom-class.list-group-item')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.props().data) + .toMatchObject({ a: 1 }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/empty-message.jsx b/packages/dx-react-grid-bootstrap4/src/templates/empty-message.jsx new file mode 100644 index 0000000000..62b29d3d10 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/empty-message.jsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; + +export const EmptyMessage = ({ getMessage, className, ...restProps }) => ( +
    + {getMessage('noColumns')} +
    +); + +EmptyMessage.propTypes = { + getMessage: PropTypes.func.isRequired, + className: PropTypes.string, +}; + +EmptyMessage.defaultProps = { + className: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/empty-message.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/empty-message.test.jsx new file mode 100644 index 0000000000..79bf4a6679 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/empty-message.test.jsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { EmptyMessage } from './empty-message'; + +describe('EmptyMessage', () => { + it('should use "noColumns" message key', () => { + const tree = shallow(( + key} /> + )); + + expect(tree.find('.card-body').text()).toBe('noColumns'); + }); + + it('should pass the className prop to the root element', () => { + const tree = shallow(( + key} + className="custom-class" + /> + )); + + expect(tree.is('.card-body.custom-class')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + key} + data={{ a: 1 }} + /> + )); + + expect(tree.props().data) + .toMatchObject({ a: 1 }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/group-panel-container.jsx b/packages/dx-react-grid-bootstrap4/src/templates/group-panel-container.jsx new file mode 100644 index 0000000000..9c84cab839 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/group-panel-container.jsx @@ -0,0 +1,26 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; + +export const GroupPanelContainer = ({ children, className, ...restProps }) => ( +
    + {children} +
    +); + +GroupPanelContainer.propTypes = { + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + ]), + className: PropTypes.string, +}; + +GroupPanelContainer.defaultProps = { + children: null, + className: undefined, +}; + diff --git a/packages/dx-react-grid-bootstrap4/src/templates/group-panel-container.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/group-panel-container.test.jsx new file mode 100644 index 0000000000..0ec7520d79 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/group-panel-container.test.jsx @@ -0,0 +1,32 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { GroupPanelContainer } from './group-panel-container'; + +describe('GroupPanelContainer', () => { + const defaultProps = { + getMessage: () => {}, + }; + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.prop('data')) + .toEqual({ a: 1 }); + }); + + it('should pass custom class to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.w-100.custom-class')) + .toBeTruthy(); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/group-panel-empty-message.css b/packages/dx-react-grid-bootstrap4/src/templates/group-panel-empty-message.css new file mode 100644 index 0000000000..2b5ec6fcad --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/group-panel-empty-message.css @@ -0,0 +1,3 @@ +.dx-rg-bs4-group-panel-empty-message { + padding: 11px 0; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/group-panel-empty-message.jsx b/packages/dx-react-grid-bootstrap4/src/templates/group-panel-empty-message.jsx new file mode 100644 index 0000000000..3f249b63ff --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/group-panel-empty-message.jsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; +import './group-panel-empty-message.css'; + +export const GroupPanelEmptyMessage = ({ getMessage, className, ...restProps }) => ( +
    + {getMessage('groupByColumn')} +
    +); + +GroupPanelEmptyMessage.propTypes = { + getMessage: PropTypes.func.isRequired, + className: PropTypes.string, +}; + +GroupPanelEmptyMessage.defaultProps = { + className: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/group-panel-empty-message.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/group-panel-empty-message.test.jsx new file mode 100644 index 0000000000..817cdf3f3b --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/group-panel-empty-message.test.jsx @@ -0,0 +1,32 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { GroupPanelEmptyMessage } from './group-panel-empty-message'; + +describe('GroupPanelEmptyMessage', () => { + const defaultProps = { + getMessage: () => {}, + }; + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.prop('data')) + .toEqual({ a: 1 }); + }); + + it('should pass custom class to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.dx-rg-bs4-group-panel-empty-message.custom-class')) + .toBeTruthy(); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/group-panel-item.css b/packages/dx-react-grid-bootstrap4/src/templates/group-panel-item.css new file mode 100644 index 0000000000..1a0fa1493d --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/group-panel-item.css @@ -0,0 +1,5 @@ +.dx-rg-bs4-group-panel-item-icon { + top: -2px; + font-size: 11px; + margin-left: -4px; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/group-panel-item.jsx b/packages/dx-react-grid-bootstrap4/src/templates/group-panel-item.jsx new file mode 100644 index 0000000000..94ee046049 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/group-panel-item.jsx @@ -0,0 +1,114 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { SortingIndicator } from './parts/sorting-indicator'; +import './group-panel-item.css'; + +const ENTER_KEY_CODE = 13; +const SPACE_KEY_CODE = 32; + +const isActionKey = keyCode => keyCode === ENTER_KEY_CODE || keyCode === SPACE_KEY_CODE; + +export const GroupPanelItem = ({ + item: { column, draft }, + onGroup, showGroupingControls, showSortingControls, + sortingDirection, onSort, className, groupingEnabled, + sortingEnabled, ...restProps +}) => { + const handleSortingChange = (e) => { + const isActionKeyDown = isActionKey(e.keyCode); + const isMouseClick = e.keyCode === undefined; + + if ((!showSortingControls || !sortingEnabled) || !(isActionKeyDown || isMouseClick)) return; + + const cancelSortingRelatedKey = e.metaKey || e.ctrlKey; + const direction = (isMouseClick || isActionKeyDown) && cancelSortingRelatedKey + ? null + : undefined; + + e.preventDefault(); + onSort({ + direction, + keepOther: cancelSortingRelatedKey, + }); + }; + const handleUngroup = (e) => { + if (!groupingEnabled) return; + const isActionKeyDown = isActionKey(e.keyCode); + const isMouseClick = e.keyCode === undefined; + + if (!isActionKeyDown && !isMouseClick) return; + onGroup(); + }; + return ( +
    + + {column.title || column.name} + {showSortingControls && sortingDirection && ( + +   + + + )} + + + {showGroupingControls && ( + +   + + )} +
    + ); +}; + +GroupPanelItem.propTypes = { + item: PropTypes.shape({ + column: PropTypes.shape({ + title: PropTypes.string, + }).isRequired, + draft: PropTypes.bool, + }).isRequired, + showSortingControls: PropTypes.bool, + sortingDirection: PropTypes.oneOf(['asc', 'desc', null]), + className: PropTypes.string, + onSort: PropTypes.func, + onGroup: PropTypes.func, + showGroupingControls: PropTypes.bool, + groupingEnabled: PropTypes.bool, + sortingEnabled: PropTypes.bool, +}; + +GroupPanelItem.defaultProps = { + showSortingControls: false, + sortingDirection: undefined, + className: undefined, + onSort: undefined, + onGroup: undefined, + showGroupingControls: false, + sortingEnabled: false, + groupingEnabled: false, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/group-panel-item.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/group-panel-item.test.jsx new file mode 100644 index 0000000000..f3c1c71807 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/group-panel-item.test.jsx @@ -0,0 +1,158 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { GroupPanelItem } from './group-panel-item'; + +describe('GroupPanelItem', () => { + const ENTER_KEY_CODE = 13; + const SPACE_KEY_CODE = 32; + it('should use column name if title is not specified', () => { + const tree = shallow(( + + )); + + expect(tree.find('div').text()) + .toBe('test'); + }); + + it('can render the ungroup button', () => { + const tree = shallow(( + + )); + + expect(tree.find('span.oi-x').exists()) + .toBeTruthy(); + }); + + it('does not get focus if sorting is not allowed', () => { + const tree = shallow(( + + )); + + const targetElement = tree.find('span').first(); + expect(targetElement.prop('tabIndex')) + .toBe(undefined); + }); + + it('should handle the "Enter" and "Space" keys down and "Mouse click" for sorting change', () => { + const onSort = jest.fn(); + const tree = shallow(( + + )); + + const targetElement = tree.find('span').first(); + targetElement.simulate('keydown', { preventDefault: jest.fn(), keyCode: ENTER_KEY_CODE }); + expect(onSort) + .toHaveBeenCalled(); + + onSort.mockClear(); + targetElement.simulate('keydown', { preventDefault: jest.fn(), keyCode: SPACE_KEY_CODE }); + expect(onSort) + .toHaveBeenCalled(); + + onSort.mockClear(); + targetElement.simulate('click', { preventDefault: jest.fn() }); + expect(onSort) + .toHaveBeenCalled(); + + onSort.mockClear(); + targetElement.simulate('keydown', { keyCode: 51 }); + expect(onSort) + .not.toHaveBeenCalled(); + }); + + it('should handle the "Mouse click" for ungrouping', () => { + const onGroup = jest.fn(); + const tree = shallow(( + + )); + + const targetElement = tree.find('span').at(1); + targetElement.simulate('click', { preventDefault: jest.fn() }); + expect(onGroup) + .toHaveBeenCalled(); + }); + + it('should cancel sorting on sorting direction change when the "Ctrl" key is pressed', () => { + const onSort = jest.fn(); + const tree = shallow(( + + )); + + const targetElement = tree.find('span').first(); + targetElement.simulate('keydown', { preventDefault: jest.fn(), keyCode: ENTER_KEY_CODE, ctrlKey: true }); + expect(onSort) + .toHaveBeenCalledWith({ keepOther: true, direction: null }); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.prop('data')) + .toEqual({ a: 1 }); + }); + + it('should add the passed className to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.custom-class.btn-group.mb-1.mr-1')) + .toBeTruthy(); + }); + + it('should apply the disabled class if grouping and sorting are not allowed', () => { + const tree = shallow(( + + )); + const buttons = tree.find('.disabled'); + expect(buttons).toHaveLength(2); + }); + + it('should not apply the disabled class if grouping and sorting are not allowed', () => { + const tree = shallow(( + + )); + const buttons = tree.find('.disabled'); + expect(buttons).toHaveLength(0); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/layout.jsx b/packages/dx-react-grid-bootstrap4/src/templates/layout.jsx new file mode 100644 index 0000000000..1fab81468b --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/layout.jsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; + +export const Root = ({ children, ...restProps }) => ( +
    + {children} +
    +); + +Root.propTypes = { + children: PropTypes.oneOfType([ + PropTypes.node, + PropTypes.arrayOf(PropTypes.node), + ]), +}; + +Root.defaultProps = { + children: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/layout.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/layout.test.jsx new file mode 100644 index 0000000000..355033e4a7 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/layout.test.jsx @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { Root } from './layout'; + +describe('Layout', () => { + describe('Root', () => { + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.props().data) + .toMatchObject({ a: 1 }); + }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/page-size-selector.jsx b/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/page-size-selector.jsx new file mode 100644 index 0000000000..ecb2f85f37 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/page-size-selector.jsx @@ -0,0 +1,50 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import { Pagination, PaginationItem, PaginationLink } from 'reactstrap'; + +export const PageSizeSelector = ({ + pageSize, + onPageSizeChange, + pageSizes, + getMessage, +}) => { + const showAll = getMessage('showAll'); + return ( +
    + + + {pageSizes.map(item => ( + + { + e.preventDefault(); + onPageSizeChange(item); + }} + > + {item || showAll} + + + ))} + +
    + ); +}; + +PageSizeSelector.propTypes = { + pageSize: PropTypes.number.isRequired, + onPageSizeChange: PropTypes.func.isRequired, + pageSizes: PropTypes.arrayOf(PropTypes.number).isRequired, + getMessage: PropTypes.func.isRequired, +}; + diff --git a/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/page-size-selector.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/page-size-selector.test.jsx new file mode 100644 index 0000000000..18aa3ca32e --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/page-size-selector.test.jsx @@ -0,0 +1,76 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { Pagination, PaginationItem, PaginationLink } from 'reactstrap'; +import { PageSizeSelector } from './page-size-selector'; + +describe('PageSizeSelector', () => { + describe('#render', () => { + const defaultProps = { + pageSize: 10, + pageSizes: [5, 10], + getMessage: key => key, + onPageSizeChange: () => undefined, + }; + + it('can show info about page sizes', () => { + const tree = shallow(); + + const mobileSelector = tree.find('select'); + const desktopSelector = tree.find(Pagination); + const mobileSelectorItems = mobileSelector.find('option'); + const desktopSelectorItems = desktopSelector.find(PaginationItem); + + expect(mobileSelector).toHaveLength(1); + expect(mobileSelector.prop('value')).toBe(10); + expect(mobileSelectorItems).toHaveLength(2); + expect(mobileSelectorItems.at(0).prop('value')).toBe(5); + expect(mobileSelectorItems.at(0).text()).toBe('5'); + expect(mobileSelectorItems.at(1).prop('value')).toBe(10); + expect(mobileSelectorItems.at(1).text()).toBe('10'); + + expect(desktopSelector).toHaveLength(1); + expect(desktopSelectorItems).toHaveLength(2); + expect(desktopSelectorItems.at(0).prop('active')).toBe(false); + expect(desktopSelectorItems.at(0).render().text()).toBe('5'); + expect(desktopSelectorItems.at(1).prop('active')).toBe(true); + expect(desktopSelectorItems.at(1).render().text()).toBe('10'); + }); + + it('can render the "All" item', () => { + const tree = shallow(); + + const mobileSelector = tree.find('select'); + const mobileSelectorItems = mobileSelector.find('option'); + const desktopSelectorLastItem = tree.find(PaginationLink).last(); + + expect(mobileSelectorItems).toHaveLength(3); + expect(mobileSelectorItems.at(2).prop('value')).toBe(0); + expect(mobileSelectorItems.at(2).text()).toBe('showAll'); + + expect(desktopSelectorLastItem.render().text()).toBe('showAll'); + }); + + it('can handle the \'onPageSizeChange\' event', () => { + const onPageSizeChange = jest.fn(); + const tree = shallow(); + + const mobileSelector = tree.find('select'); + const desktopSelector = tree.find(Pagination); + + mobileSelector.simulate('change', { target: { value: 10 } }); + desktopSelector.find(PaginationLink).at(0).simulate('click', { preventDefault: jest.fn() }); + + expect(onPageSizeChange.mock.calls[0][0]).toBe(10); + expect(onPageSizeChange.mock.calls[1][0]).toBe(5); + }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/pager.jsx b/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/pager.jsx new file mode 100644 index 0000000000..96c81fbb7d --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/pager.jsx @@ -0,0 +1,54 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { PageSizeSelector } from './page-size-selector'; +import { Pagination } from './pagination'; + +export const Pager = ({ + currentPage, + onCurrentPageChange, + totalPages, + pageSize, + onPageSizeChange, + pageSizes, + totalCount, + getMessage, + className, + ...restProps +}) => ( +
    + {!!pageSizes.length && } + onCurrentPageChange(page)} + pageSize={pageSize} + getMessage={getMessage} + /> +
    +); + +Pager.propTypes = { + currentPage: PropTypes.number.isRequired, + onCurrentPageChange: PropTypes.func.isRequired, + totalPages: PropTypes.number.isRequired, + pageSize: PropTypes.number.isRequired, + onPageSizeChange: PropTypes.func.isRequired, + pageSizes: PropTypes.arrayOf(PropTypes.number).isRequired, + totalCount: PropTypes.number.isRequired, + getMessage: PropTypes.func.isRequired, + className: PropTypes.string, +}; + +Pager.defaultProps = { + className: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/pager.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/pager.test.jsx new file mode 100644 index 0000000000..43073c721c --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/pager.test.jsx @@ -0,0 +1,77 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { Pager } from './pager'; +import { Pagination } from './pagination'; + +const defaultProps = { + totalPages: 10, + currentPage: 1, + totalCount: 96, + pageSize: 10, + getMessage: key => key, + pageSizes: [], + onCurrentPageChange: () => {}, + onPageSizeChange: () => {}, +}; + +describe('Pager', () => { + it('renders pagination', () => { + const pager = shallow(( + + )); + const pagination = pager.find(Pagination); + + expect(pagination) + .toHaveLength(1); + }); + + it('renders page size selector', () => { + const pageSizeSelector = shallow(( + + )).find('PageSizeSelector'); + + expect(pageSizeSelector) + .toHaveLength(1); + expect(pageSizeSelector.at(0).prop('getMessage')('showAll')) + .toBe('showAll'); + }); + + it('doesn\'t render page selector if the pageSizes option is not defined ', () => { + const pageSizeSelector = shallow(( + + )).find('PageSizeSelector'); + + expect(pageSizeSelector).toHaveLength(0); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.prop('data')) + .toEqual({ a: 1 }); + }); + + it('should add the passed className to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.clearfix.card-footer.custom-class')) + .toBeTruthy(); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/pagination.jsx b/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/pagination.jsx new file mode 100644 index 0000000000..26307424e2 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/pagination.jsx @@ -0,0 +1,161 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import { Pagination as PaginationBS4, PaginationItem, PaginationLink } from 'reactstrap'; +import { firstRowOnPage, lastRowOnPage } from '@devexpress/dx-grid-core'; + +const calculateStartPage = (currentPage, maxButtonCount, totalPageCount) => Math.max( + Math.min( + currentPage - Math.floor(maxButtonCount / 2, 10), + (totalPageCount - maxButtonCount) + 1, + ), + 1, +); + +const renderPageButtons = ( + currentPage, + totalPageCount, + currentPageChange, +) => { + const pageButtons = []; + const maxButtonCount = 3; + let startPage = 1; + let endPage = totalPageCount || 1; + + if (maxButtonCount < totalPageCount) { + startPage = calculateStartPage(currentPage + 1, maxButtonCount, totalPageCount); + endPage = (startPage + maxButtonCount) - 1; + } + if (startPage > 1) { + pageButtons.push(( + + currentPageChange(e, 0)} + > + {1} + + + )); + + if (startPage > 2) { + pageButtons.push(( + + + {'...'} + + + )); + } + } + + for (let page = startPage; page <= endPage; page += 1) { + pageButtons.push(( + + currentPageChange(e, page - 1)} + > + {page} + + + )); + } + + if (endPage < totalPageCount) { + if (endPage < totalPageCount - 1) { + pageButtons.push(( + + + {'...'} + + + )); + } + + pageButtons.push(( + + currentPageChange(e, totalPageCount - 1)} + > + {totalPageCount} + + + )); + } + + return pageButtons; +}; + +export const Pagination = ({ + totalPages, + currentPage, + onCurrentPageChange, + totalCount, + pageSize, + getMessage, +}) => { + const from = firstRowOnPage(currentPage, pageSize, totalCount); + const to = lastRowOnPage(currentPage, pageSize, totalCount); + const currentPageChange = (e, nextPage) => { + e.preventDefault(); + onCurrentPageChange(nextPage); + }; + return ( + + + + currentPageChange(e, currentPage - 1)} + /> + + {renderPageButtons(currentPage, totalPages, currentPageChange)} + + currentPageChange(e, currentPage + 1)} + /> + + + + + + currentPageChange(e, currentPage - 1)} + /> + +   + + currentPageChange(e, currentPage + 1)} + /> + + + + + {getMessage('info', { from, to, count: totalCount })} + + + + ); +}; + +Pagination.propTypes = { + totalPages: PropTypes.number.isRequired, + currentPage: PropTypes.number.isRequired, + onCurrentPageChange: PropTypes.func.isRequired, + totalCount: PropTypes.number.isRequired, + pageSize: PropTypes.number.isRequired, + getMessage: PropTypes.func.isRequired, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/pagination.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/pagination.test.jsx new file mode 100644 index 0000000000..7ba36584f7 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/paging-panel/pagination.test.jsx @@ -0,0 +1,85 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { Pagination as PaginationBS4, PaginationItem, PaginationLink } from 'reactstrap'; +import { Pagination } from './pagination'; + +const defaultProps = { + totalPages: 10, + currentPage: 1, + totalCount: 96, + pageSize: 10, + getMessage: key => key, + onCurrentPageChange: () => {}, +}; + +describe('Pagination', () => { + it('can show info about rendered pages', () => { + const getMessage = jest.fn(key => key); + const tree = shallow(( + + )); + + expect(getMessage) + .toBeCalledWith('info', { from: 11, to: 20, count: 96 }); + expect(tree.find('span > span').text()) + .toBe('info'); + }); + + it('can render pagination arrows', () => { + const onCurrentPageChange = jest.fn(); + const paginations = shallow(( + + )).find(PaginationBS4); + + const arrows = paginations.at(1).find(PaginationItem); + const prew = arrows.at(0); + const next = arrows.at(1); + + prew.find(PaginationLink).simulate('click', { preventDefault: jest.fn() }); + next.find(PaginationLink).simulate('click', { preventDefault: jest.fn() }); + + expect(arrows).toHaveLength(2); + expect(prew.props('previews')).toBeTruthy(); + expect(next.props('next')).toBeTruthy(); + expect(onCurrentPageChange.mock.calls).toHaveLength(2); + }); + + it('disables the prev arrow if the first page is active', () => { + const paginations = shallow(( + + )).find(PaginationBS4); + + const arrows = paginations.at(1).find(PaginationItem); + const prew = arrows.at(0); + const next = arrows.at(1); + + expect(prew.props().disabled).toBeTruthy(); + expect(next.props().disabled).toBeFalsy(); + }); + + it('disables the next arrow if current page equals to total page count', () => { + const paginations = shallow(( + + )).find(PaginationBS4); + + const arrows = paginations.at(1).find(PaginationItem); + const prew = arrows.at(0); + const next = arrows.at(1); + + expect(prew.props().disabled).toBeFalsy(); + expect(next.props().disabled).toBeTruthy(); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/parts/sorting-indicator.css b/packages/dx-react-grid-bootstrap4/src/templates/parts/sorting-indicator.css new file mode 100644 index 0000000000..6a6eef704f --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/parts/sorting-indicator.css @@ -0,0 +1,4 @@ +.dx-rg-bs4-sorting-indicator { + top: 0; + font-size: 11px; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/parts/sorting-indicator.jsx b/packages/dx-react-grid-bootstrap4/src/templates/parts/sorting-indicator.jsx new file mode 100644 index 0000000000..f194cf9328 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/parts/sorting-indicator.jsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; +import './sorting-indicator.css'; + +export const SortingIndicator = ({ direction }) => ( + +); + +SortingIndicator.propTypes = { + direction: PropTypes.oneOf(['asc', 'desc']), +}; + +SortingIndicator.defaultProps = { + direction: null, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-cell.css b/packages/dx-react-grid-bootstrap4/src/templates/table-cell.css new file mode 100644 index 0000000000..a584f00a73 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-cell.css @@ -0,0 +1,4 @@ +.dx-rg-bs4-table-cell { + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-cell.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-cell.jsx new file mode 100644 index 0000000000..c5141420f9 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-cell.jsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; +import './table-cell.css'; + +export const TableCell = ({ + column, value, children, + tableRow, tableColumn, row, + className, ...restProps +}) => ( +
    +); + +TableCell.propTypes = { + value: PropTypes.any, + column: PropTypes.object, + row: PropTypes.object, + children: PropTypes.oneOfType([ + PropTypes.node, + PropTypes.arrayOf(PropTypes.node), + ]), + tableRow: PropTypes.object, + tableColumn: PropTypes.object, + className: PropTypes.string, +}; + +TableCell.defaultProps = { + value: undefined, + column: undefined, + row: undefined, + children: undefined, + tableRow: undefined, + tableColumn: undefined, + className: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-cell.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-cell.test.jsx new file mode 100644 index 0000000000..b655ecbdd6 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-cell.test.jsx @@ -0,0 +1,52 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { TableCell } from './table-cell'; + +describe('TableCell', () => { + it('should have correct text alignment', () => { + let tree = shallow(); + expect(tree.find('td').is('.text-right')) + .toBeFalsy(); + + tree = shallow(); + expect(tree.find('td').is('.text-right')) + .toBeFalsy(); + + tree = shallow(); + expect(tree.find('td').is('.text-right.text-nowrap.dx-rg-bs4-table-cell')) + .toBeTruthy(); + }); + + it('should have correct text', () => { + const tree = shallow(); + expect(tree.find('td').text()).toBe('text'); + }); + + it('should render children if passed', () => { + const tree = shallow(( + + + + )); + + expect(tree.find('.test').exists()) + .toBeTruthy(); + }); + + it('should pass custom class to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.custom-class.text-nowrap.dx-rg-bs4-table-cell')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + expect(tree.find('td').prop('data')) + .toMatchObject({ a: 1 }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-container.css b/packages/dx-react-grid-bootstrap4/src/templates/table-container.css new file mode 100644 index 0000000000..25cfe3f091 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-container.css @@ -0,0 +1,4 @@ +.dx-rg-bs4-table-container { + overflow: auto; + -webkit-overflow-scrolling: touch; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-container.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-container.jsx new file mode 100644 index 0000000000..c64ba2c9be --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-container.jsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import './table-container.css'; + +export const TableContainer = ({ children, ...restProps }) => ( +
    + {children} +
    +); + +TableContainer.propTypes = { + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + ]).isRequired, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-detail-cell.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-detail-cell.jsx new file mode 100644 index 0000000000..3038c17ae9 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-detail-cell.jsx @@ -0,0 +1,40 @@ +import * as React from 'react'; +import classNames from 'classnames'; +import * as PropTypes from 'prop-types'; + +export const TableDetailCell = ({ + colSpan, children, className, + tableColumn, tableRow, row, + ...restProps +}) => ( +
    +); + +TableDetailCell.propTypes = { + style: PropTypes.object, + colSpan: PropTypes.number, + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + ]), + className: PropTypes.string, + tableColumn: PropTypes.object, + tableRow: PropTypes.object, + row: PropTypes.object, +}; + +TableDetailCell.defaultProps = { + style: null, + colSpan: 1, + className: undefined, + tableColumn: undefined, + tableRow: undefined, + row: undefined, + children: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-detail-cell.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-detail-cell.test.jsx new file mode 100644 index 0000000000..9f8efc8e65 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-detail-cell.test.jsx @@ -0,0 +1,32 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { TableDetailCell } from './table-detail-cell'; + +describe('TableDetailCell', () => { + const defaultProps = { + template: () => (
    ), + }; + it('should pass the className prop to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.table-active.custom-class')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.props().data) + .toMatchObject({ a: 1 }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-detail-toggle-cell.css b/packages/dx-react-grid-bootstrap4/src/templates/table-detail-toggle-cell.css new file mode 100644 index 0000000000..d04fa9405c --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-detail-toggle-cell.css @@ -0,0 +1,4 @@ +.dx-rg-bs4-table-detail-toggle-cell-icon { + font-size: 9px; + top: 0; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-detail-toggle-cell.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-detail-toggle-cell.jsx new file mode 100644 index 0000000000..5ac226952f --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-detail-toggle-cell.jsx @@ -0,0 +1,63 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; +import './table-detail-toggle-cell.css'; + +const ENTER_KEY_CODE = 13; +const SPACE_KEY_CODE = 32; + +const handleMouseDown = (e) => { e.target.style.outline = 'none'; }; +const handleBlur = (e) => { e.target.style.outline = ''; }; + +export const TableDetailToggleCell = ({ + expanded, onToggle, className, + tableColumn, tableRow, row, + ...restProps +}) => { + const handleKeyDown = (e) => { + if (e.keyCode === ENTER_KEY_CODE || e.keyCode === SPACE_KEY_CODE) { + e.preventDefault(); + onToggle(); + } + }; + return ( +
    + ); +}; + +TableDetailToggleCell.propTypes = { + expanded: PropTypes.bool, + onToggle: PropTypes.func, + tableColumn: PropTypes.object, + tableRow: PropTypes.object, + row: PropTypes.object, + className: PropTypes.string, +}; + +TableDetailToggleCell.defaultProps = { + expanded: false, + onToggle: () => {}, + tableColumn: undefined, + tableRow: undefined, + row: undefined, + className: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-detail-toggle-cell.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-detail-toggle-cell.test.jsx new file mode 100644 index 0000000000..cfa65484ad --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-detail-toggle-cell.test.jsx @@ -0,0 +1,88 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { setupConsole } from '@devexpress/dx-testing'; +import { TableDetailToggleCell } from './table-detail-toggle-cell'; + +describe('TableDetailToggleCell', () => { + let resetConsole; + const ENTER_KEY_CODE = 13; + const SPACE_KEY_CODE = 32; + beforeAll(() => { + resetConsole = setupConsole({ ignore: ['validateDOMNesting'] }); + }); + afterAll(() => { + resetConsole(); + }); + + it('should handle click with stopPropagation', () => { + const onToggle = jest.fn(); + const mockEvent = { + stopPropagation: jest.fn(), + }; + const tree = shallow(( + + )); + + const buttonClickHandler = tree.find('td').prop('onClick'); + + buttonClickHandler(mockEvent); + expect(onToggle) + .toHaveBeenCalled(); + expect(mockEvent.stopPropagation) + .toHaveBeenCalled(); + }); + + it('can get focus', () => { + const tree = shallow(( + + )); + + expect(tree.find('span').prop('tabIndex')) + .toBe(0); + }); + + it('should handle the "Enter" and "Space" keys down', () => { + const onToggle = jest.fn(); + const tree = shallow(( + + )); + + const targetElement = tree.find('span'); + targetElement.simulate('keydown', { preventDefault: jest.fn(), keyCode: ENTER_KEY_CODE }); + expect(onToggle) + .toHaveBeenCalled(); + + onToggle.mockClear(); + targetElement.simulate('keydown', { preventDefault: jest.fn(), keyCode: SPACE_KEY_CODE }); + expect(onToggle) + .toHaveBeenCalled(); + + onToggle.mockClear(); + targetElement.simulate('keydown', { preventDefault: jest.fn(), keyCode: 55 }); + expect(onToggle) + .not.toHaveBeenCalled(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.props().data) + .toMatchObject({ a: 1 }); + }); + + it('should pass custom class to the root element', () => { + const tree = shallow(( + + )); + expect(tree.find('td').is('.align-middle.dx-rg-bs4-cursor-pointer.custom-class')) + .toBeTruthy(); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-edit-cell.css b/packages/dx-react-grid-bootstrap4/src/templates/table-edit-cell.css new file mode 100644 index 0000000000..2e3b3a9159 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-edit-cell.css @@ -0,0 +1,3 @@ +.table .dx-rg-bs4-table-edit-cell { + padding: 5px; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-edit-cell.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-edit-cell.jsx new file mode 100644 index 0000000000..93a74dc71c --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-edit-cell.jsx @@ -0,0 +1,48 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; +import './table-edit-cell.css'; + +export const EditCell = ({ + column, value, onValueChange, className, children, + row, tableRow, tableColumn, editingEnabled, ...restProps +}) => ( + +); +EditCell.propTypes = { + column: PropTypes.object, + row: PropTypes.object, + tableColumn: PropTypes.object, + tableRow: PropTypes.object, + value: PropTypes.any, + onValueChange: PropTypes.func.isRequired, + className: PropTypes.string, + editingEnabled: PropTypes.bool, + children: PropTypes.node, +}; +EditCell.defaultProps = { + column: undefined, + row: undefined, + tableColumn: undefined, + tableRow: undefined, + className: undefined, + children: undefined, + editingEnabled: true, + value: '', +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-edit-cell.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-edit-cell.test.jsx new file mode 100644 index 0000000000..6c95bd4232 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-edit-cell.test.jsx @@ -0,0 +1,56 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { EditCell } from './table-edit-cell'; + +describe('EditCell', () => { + const defaultProps = { + onValueChange: () => {}, + }; + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + expect(tree.find('td').prop('data')) + .toEqual({ a: 1 }); + }); + + it('should render children if passed', () => { + const tree = shallow(( + + + + )); + + expect(tree.find('.test').exists()) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.align-middle.custom-class.dx-rg-bs4-table-edit-cell')) + .toBeTruthy(); + }); + + it('should render readonly editor if editing is not allowed', () => { + const tree = shallow(( + + )); + + expect(tree.find('input').prop('readOnly')) + .toBeTruthy(); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-edit-command-cell.css b/packages/dx-react-grid-bootstrap4/src/templates/table-edit-command-cell.css new file mode 100644 index 0000000000..4ba999cf89 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-edit-command-cell.css @@ -0,0 +1,3 @@ +.dx-rg-bs4-table-edit-command-cell { + padding: 11px; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-edit-command-cell.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-edit-command-cell.jsx new file mode 100644 index 0000000000..312d354bb0 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-edit-command-cell.jsx @@ -0,0 +1,91 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; +import './table-edit-command-cell.css'; + +export const CommandButton = ({ + onExecute, text, + className, + ...restProps +}) => ( + +); + +CommandButton.propTypes = { + text: PropTypes.string.isRequired, + onExecute: PropTypes.func.isRequired, + className: PropTypes.string, +}; + +CommandButton.defaultProps = { + className: undefined, +}; + +export const EditCommandHeadingCell = ({ + children, className, + tableColumn, tableRow, + ...restProps +}) => ( + +); + +EditCommandHeadingCell.propTypes = { + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + ]), + tableColumn: PropTypes.object, + tableRow: PropTypes.object, + className: PropTypes.string, +}; + +EditCommandHeadingCell.defaultProps = { + children: undefined, + tableColumn: undefined, + tableRow: undefined, + className: undefined, +}; + +export const EditCommandCell = ({ + children, className, + tableColumn, tableRow, + ...restProps +}) => ( + +); + +EditCommandCell.propTypes = { + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + ]), + tableColumn: PropTypes.object, + tableRow: PropTypes.object, + className: PropTypes.string, +}; + +EditCommandCell.defaultProps = { + children: undefined, + tableColumn: undefined, + tableRow: undefined, + className: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-edit-command-cell.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-edit-command-cell.test.jsx new file mode 100644 index 0000000000..45f2a63f58 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-edit-command-cell.test.jsx @@ -0,0 +1,71 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { EditCommandHeadingCell, EditCommandCell, CommandButton } from './table-edit-command-cell'; + +describe('EditCommandCells', () => { + describe('EditCommandCell', () => { + it('should pass custom class to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.p-0.text-nowrap.text-center.custom-class')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + expect(tree.props().data) + .toMatchObject({ a: 1 }); + }); + }); + + describe('EditCommandHeadingCell', () => { + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.p-0.text-nowrap.text-center.custom-class')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + expect(tree.props().data) + .toMatchObject({ a: 1 }); + }); + }); + + describe('CommandButton', () => { + it('should pass the className prop to the root element', () => { + const tree = shallow(( + {}} + text="" + className="custom-class" + /> + )); + + expect(tree.is('.btn.btn-link.dx-rg-bs4-table-edit-command-cell.custom-class')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + {}} + text="" + data={{ a: 1 }} + /> + )); + + expect(tree.props().data) + .toMatchObject({ a: 1 }); + }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-filter-cell.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-filter-cell.jsx new file mode 100644 index 0000000000..25551837e8 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-filter-cell.jsx @@ -0,0 +1,45 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; + +export const TableFilterCell = ({ + filter, onFilter, children, + column, tableRow, tableColumn, getMessage, + filteringEnabled, ...restProps +}) => ( + +); + +TableFilterCell.propTypes = { + filter: PropTypes.object, + onFilter: PropTypes.func, + children: PropTypes.oneOfType([ + PropTypes.node, + PropTypes.arrayOf(PropTypes.node), + ]), + column: PropTypes.object, + tableRow: PropTypes.object, + tableColumn: PropTypes.object, + getMessage: PropTypes.func, + filteringEnabled: PropTypes.bool, +}; + +TableFilterCell.defaultProps = { + filter: null, + onFilter: () => {}, + children: undefined, + column: undefined, + tableRow: undefined, + tableColumn: undefined, + getMessage: undefined, + filteringEnabled: true, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-filter-cell.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-filter-cell.test.jsx new file mode 100644 index 0000000000..9a7e208b0f --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-filter-cell.test.jsx @@ -0,0 +1,58 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { TableFilterCell } from './table-filter-cell'; + +describe('TableFilterCell', () => { + it('should not set filter with an empty value', () => { + const onFilterMock = jest.fn(); + const tree = shallow(( + + )); + + tree.find('input').simulate('change', { target: { value: '' } }); + expect(onFilterMock.mock.calls[0][0]).toBeNull(); + }); + + it('should render children if passed', () => { + const tree = shallow(( + + + + )); + + expect(tree.find('.test').exists()) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.custom-class')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + expect(tree.find('th').prop('data')) + .toEqual({ a: 1 }); + }); + + it('should render readonly filtering editor if filtering is not allowed', () => { + const tree = shallow(( + key} /> + )); + + expect(tree.find('input').prop('readOnly')) + .toBeTruthy(); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-group-row-cell.css b/packages/dx-react-grid-bootstrap4/src/templates/table-group-row-cell.css new file mode 100644 index 0000000000..dbb4796f66 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-group-row-cell.css @@ -0,0 +1,5 @@ +.dx-rg-bs4-table-group-row-cell { + font-size: 9px; + top: 0; + margin-right: 10px; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-group-row-cell.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-group-row-cell.jsx new file mode 100644 index 0000000000..1cfee10193 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-group-row-cell.jsx @@ -0,0 +1,76 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; +import './table-group-row-cell.css'; + +const ENTER_KEY_CODE = 13; +const SPACE_KEY_CODE = 32; + +const handleMouseDown = (e) => { e.target.style.outline = 'none'; }; +const handleBlur = (e) => { e.target.style.outline = ''; }; + +export const TableGroupCell = ({ + className, colSpan, row, column, + expanded, onToggle, + children, tableRow, tableColumn, + ...restProps +}) => { + const handleClick = () => onToggle(); + const handleKeyDown = (e) => { + const { keyCode } = e; + if (keyCode === ENTER_KEY_CODE || keyCode === SPACE_KEY_CODE) { + e.preventDefault(); + onToggle(); + } + }; + + return ( + + ); +}; + +TableGroupCell.propTypes = { + className: PropTypes.string, + colSpan: PropTypes.number, + row: PropTypes.object, + column: PropTypes.object, + expanded: PropTypes.bool, + onToggle: PropTypes.func, + children: PropTypes.oneOfType([ + PropTypes.node, + PropTypes.arrayOf(PropTypes.node), + ]), + tableRow: PropTypes.object, + tableColumn: PropTypes.object, +}; + +TableGroupCell.defaultProps = { + className: undefined, + colSpan: 1, + row: {}, + column: {}, + expanded: false, + onToggle: () => {}, + children: undefined, + tableRow: undefined, + tableColumn: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-group-row-cell.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-group-row-cell.test.jsx new file mode 100644 index 0000000000..d05cbe3e56 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-group-row-cell.test.jsx @@ -0,0 +1,90 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { setupConsole } from '@devexpress/dx-testing'; +import { TableGroupCell } from './table-group-row-cell'; + +describe('TableGroupRowCell', () => { + let resetConsole; + const ENTER_KEY_CODE = 13; + const SPACE_KEY_CODE = 32; + beforeAll(() => { + resetConsole = setupConsole({ ignore: ['validateDOMNesting'] }); + }); + afterAll(() => { + resetConsole(); + }); + + it('should render column title and value', () => { + const tree = shallow(( + + )); + + expect(tree.text()) + .toMatch(/Title.*Value/); + }); + + it('should render children if passed', () => { + const tree = shallow(( + + + + )); + + expect(tree.find('.test').exists()) + .toBeTruthy(); + }); + + it('can get focus', () => { + const tree = shallow(( + + )); + + expect(tree.find('span').prop('tabIndex')) + .toBe(0); + }); + + it('should handle the "Enter" and "Space" keys down', () => { + const onToggle = jest.fn(); + const tree = shallow(( + + )); + const targetElement = tree.find('span'); + + targetElement.simulate('keydown', { preventDefault: jest.fn(), keyCode: ENTER_KEY_CODE }); + expect(onToggle) + .toHaveBeenCalled(); + + onToggle.mockClear(); + targetElement.simulate('keydown', { preventDefault: jest.fn(), keyCode: SPACE_KEY_CODE }); + expect(onToggle) + .toHaveBeenCalled(); + + onToggle.mockClear(); + targetElement.simulate('keydown', { keyCode: 51 }); + expect(onToggle) + .not.toHaveBeenCalled(); + }); + + it('should pass custom class to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.dx-rg-bs4-cursor-pointer.custom-class')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.prop('data')) + .toEqual({ a: 1 }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell.css b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell.css new file mode 100644 index 0000000000..850588f0b3 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell.css @@ -0,0 +1,10 @@ +.dx-rg-bs4-table-header-cell-wrapper { + overflow: hidden; + text-overflow: ellipsis; +} +.dx-rg-bs4-table-header-cell-left { + margin-right: 14px; +} +.dx-rg-bs4-table-header-cell-right { + margin-left: 14px; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell.jsx new file mode 100644 index 0000000000..97273911df --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell.jsx @@ -0,0 +1,156 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; + +import { DragSource } from '@devexpress/dx-react-core'; + +import { ResizingControl } from './table-header-cell/resizing-control'; +import { GroupingControl } from './table-header-cell/grouping-control'; +import { SortingControl } from './table-header-cell/sorting-control'; + +import './table-header-cell.css'; + +const ENTER_KEY_CODE = 13; +const SPACE_KEY_CODE = 32; + +export class TableHeaderCell extends React.PureComponent { + constructor(props) { + super(props); + + this.state = { + dragging: false, + }; + this.onClick = (e) => { + const { sortingEnabled, showSortingControls, onSort } = this.props; + const isActionKeyDown = e.keyCode === ENTER_KEY_CODE || e.keyCode === SPACE_KEY_CODE; + const isMouseClick = e.keyCode === undefined; + + if ((!showSortingControls || !sortingEnabled) || !(isActionKeyDown || isMouseClick)) return; + + const cancelSortingRelatedKey = e.metaKey || e.ctrlKey; + const direction = (isMouseClick || isActionKeyDown) && cancelSortingRelatedKey + ? null + : undefined; + + e.preventDefault(); + onSort({ + direction, + keepOther: e.shiftKey || cancelSortingRelatedKey, + }); + }; + } + render() { + const { + className, column, tableColumn, + showSortingControls, sortingDirection, sortingEnabled, + showGroupingControls, onGroup, groupingEnabled, + draggingEnabled, onWidthDraftCancel, + resizingEnabled, onWidthChange, onWidthDraft, + tableRow, getMessage, onSort, + ...restProps + } = this.props; + const { dragging } = this.state; + const align = (tableColumn && tableColumn.align) || 'left'; + const columnTitle = column && (column.title || column.name); + const isCellInteractive = (showSortingControls && sortingEnabled) || draggingEnabled; + + const cellLayout = ( + + ); + + return draggingEnabled ? ( + { this.cellRef = element; }} + payload={[{ type: 'column', columnName: column.name }]} + onStart={() => this.setState({ dragging: true })} + onEnd={() => this.cellRef && this.setState({ dragging: false })} + > + {cellLayout} + + ) : cellLayout; + } +} + +TableHeaderCell.propTypes = { + tableColumn: PropTypes.object, + tableRow: PropTypes.object, + column: PropTypes.object, + className: PropTypes.string, + showSortingControls: PropTypes.bool, + sortingEnabled: PropTypes.bool, + sortingDirection: PropTypes.oneOf(['asc', 'desc', null]), + onSort: PropTypes.func, + showGroupingControls: PropTypes.bool, + onGroup: PropTypes.func, + groupingEnabled: PropTypes.bool, + draggingEnabled: PropTypes.bool, + resizingEnabled: PropTypes.bool, + onWidthChange: PropTypes.func, + onWidthDraft: PropTypes.func, + onWidthDraftCancel: PropTypes.func, + getMessage: PropTypes.func, +}; + +TableHeaderCell.defaultProps = { + column: undefined, + tableColumn: undefined, + tableRow: undefined, + className: undefined, + showSortingControls: false, + sortingEnabled: false, + sortingDirection: undefined, + onSort: undefined, + showGroupingControls: false, + onGroup: undefined, + groupingEnabled: false, + draggingEnabled: false, + resizingEnabled: false, + onWidthChange: undefined, + onWidthDraft: undefined, + onWidthDraftCancel: undefined, + getMessage: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell.test.jsx new file mode 100644 index 0000000000..103bcf47aa --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell.test.jsx @@ -0,0 +1,268 @@ +import * as React from 'react'; +import { mount, shallow } from 'enzyme'; +import { DragDropProvider, DragSource } from '@devexpress/dx-react-core'; +import { setupConsole } from '@devexpress/dx-testing'; + +import { TableHeaderCell } from './table-header-cell'; +import { ResizingControl } from './table-header-cell/resizing-control'; +import { GroupingControl } from './table-header-cell/grouping-control'; + +jest.mock('./table-header-cell/grouping-control', () => ({ + GroupingControl: jest.fn(), +})); + +describe('TableHeaderCell', () => { + let resetConsole; + beforeAll(() => { + resetConsole = setupConsole({ ignore: ['validateDOMNesting'] }); + }); + afterAll(() => { + resetConsole(); + }); + beforeEach(() => { + GroupingControl.mockImplementation(() => null); + }); + afterEach(() => { + jest.resetAllMocks(); + }); + + it('should use column name if title is not specified', () => { + const tree = shallow(( + + )); + + expect(tree.find('th > div').text()).toBe('Test'); + }); + + it('should have correct classes when user interaction disallowed', () => { + const tree = shallow(( + + )); + + expect(tree.find('th').is('.dx-rg-bs4-user-select-none.dx-rg-bs4-cursor-pointer')) + .toBeFalsy(); + }); + + it('should have correct classes when sorting is allowed', () => { + const tree = shallow(( + + )); + + expect(tree.find('th').is('.dx-rg-bs4-user-select-none.dx-rg-bs4-cursor-pointer.position-relative')) + .toBeTruthy(); + }); + + it('should have correct classes when dragging is allowed', () => { + const tree = shallow(( + + + + )); + + expect(tree.dive().find('th').is('.dx-rg-bs4-user-select-none.dx-rg-bs4-cursor-pointer.position-relative')) + .toBeTruthy(); + }); + + it('should have correct classes when dragging', () => { + const tree = mount(( + + + + )); + + expect(tree.find('th').is('.dx-rg-bs4-inactive')) + .toBeFalsy(); + + tree.find(DragSource).prop('onStart')(); + tree.update(); + + expect(tree.find('th').is('.dx-rg-bs4-inactive')) + .toBeTruthy(); + + tree.find(DragSource).prop('onEnd')(); + tree.update(); + + expect(tree.find('th').is('.dx-rg-bs4-inactive')) + .toBeFalsy(); + }); + + it('should render resize control if resizing is allowed', () => { + const onWidthChange = () => {}; + const onWidthDraft = () => {}; + const onWidthDraftCancel = () => {}; + + const tree = shallow(( + + )); + + expect(tree.find(ResizingControl).exists()) + .toBeTruthy(); + expect(tree.find(ResizingControl).prop('onWidthChange')) + .toBe(onWidthChange); + expect(tree.find(ResizingControl).prop('onWidthDraft')) + .toBe(onWidthDraft); + expect(tree.find(ResizingControl).prop('onWidthDraftCancel')) + .toBe(onWidthDraftCancel); + }); + + it('should have correct classes when grouping by click is not allowed and column align is left', () => { + const tree = shallow(( + + )); + expect(tree.find('div').is('.text-nowrap.dx-rg-bs4-table-header-cell-wrapper')) + .toBeTruthy(); + expect(tree.find('div').is('.text-right')) + .toBeFalsy(); + }); + + it('should have correct classes when grouping by click is allowed and column align is left', () => { + const tree = shallow(( + + )); + expect(tree.find('div').is('.text-nowrap.dx-rg-bs4-table-header-cell-wrapper.dx-rg-bs4-table-header-cell-left')) + .toBeTruthy(); + expect(tree.find('div').is('.text-right')) + .toBeFalsy(); + }); + + it('should have correct classes when grouping by click is not allowed and column align is right', () => { + const tree = shallow(( + + )); + expect(tree.find('div').is('.text-nowrap.text-right')) + .toBeTruthy(); + expect(tree.find('div').is('.dx-rg-bs4-table-header-cell-right')) + .toBeFalsy(); + }); + + it('should have correct classes when grouping by click is allowed and column align is right', () => { + const tree = shallow(( + + )); + expect(tree.find('div').is('.text-nowrap.text-right.dx-rg-bs4-table-header-cell-right')) + .toBeTruthy(); + }); + + it('should pass custom class to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.find('th').is('.position-relative.custom-class')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + expect(tree.props().data) + .toMatchObject({ a: 1 }); + }); + + describe('with keyboard navigation', () => { + const ENTER_KEY_CODE = 13; + const SPACE_KEY_CODE = 32; + + it('should handle the "Enter" and "Space" keys down', () => { + const onSort = jest.fn(); + const tree = shallow(( + + )); + + const targetElement = tree.find('th'); + targetElement.simulate('click', { keyCode: ENTER_KEY_CODE, preventDefault: jest.fn() }); + expect(onSort) + .toHaveBeenCalled(); + + onSort.mockClear(); + targetElement.simulate('click', { keyCode: SPACE_KEY_CODE, preventDefault: jest.fn() }); + expect(onSort) + .toHaveBeenCalled(); + + onSort.mockClear(); + targetElement.simulate('click', { keyCode: 51 }); + expect(onSort) + .not.toHaveBeenCalled(); + }); + + it('should keep other sorting parameters on sorting change when the "Shift" key is pressed', () => { + const onSort = jest.fn(); + const tree = shallow(( + + )); + + const targetElement = tree.find('th'); + targetElement.simulate('click', { keyCode: ENTER_KEY_CODE, shiftKey: true, preventDefault: jest.fn() }); + expect(onSort) + .toHaveBeenCalledWith({ keepOther: true, direction: undefined }); + }); + + it('should handle the "Ctrl" key with sorting', () => { + const onSort = jest.fn(); + const tree = shallow(( + + )); + + const targetElement = tree.find('th'); + targetElement.simulate('click', { keyCode: ENTER_KEY_CODE, ctrlKey: true, preventDefault: jest.fn() }); + expect(onSort) + .toHaveBeenCalledWith({ keepOther: true, direction: null }); + }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/grouping-control.css b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/grouping-control.css new file mode 100644 index 0000000000..18f0b4d8d7 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/grouping-control.css @@ -0,0 +1,10 @@ +.dx-rg-bs4-grouping-control { + width: 14px; +} + +.dx-rg-bs4-grouping-control-icon { + top: 0; + font-size: 12px; + margin: -5px; + padding: 5px; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/grouping-control.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/grouping-control.jsx new file mode 100644 index 0000000000..d7347c3ebc --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/grouping-control.jsx @@ -0,0 +1,41 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; +import './grouping-control.css'; + +export const GroupingControl = ({ align, disabled, onGroup }) => { + const invertedAlign = align === 'left'; + + return ( +
    { + if (disabled) return; + e.stopPropagation(); + onGroup(); + }} + > + +
    + ); +}; + +GroupingControl.propTypes = { + align: PropTypes.string.isRequired, + onGroup: PropTypes.func.isRequired, + disabled: PropTypes.bool, +}; + +GroupingControl.defaultProps = { + disabled: false, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/resizing-control.css b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/resizing-control.css new file mode 100644 index 0000000000..fdb0767764 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/resizing-control.css @@ -0,0 +1,16 @@ +.dx-rg-bs4-resizing-control { + top: 0; + right: -8px; + width: 16px; + cursor: col-resize; + z-index: 100; +} +.dx-rg-bs4-resizing-control-line { + background-color: #ddd; + width: 1px; + top: 25%; +} +.dx-rg-bs4-resizing-control-wrapper { + top: 0; + left: 0; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/resizing-control.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/resizing-control.jsx new file mode 100644 index 0000000000..5fdd9b8302 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/resizing-control.jsx @@ -0,0 +1,75 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import { Draggable } from '@devexpress/dx-react-core'; +import './resizing-control.css'; + +const ResizingControlLine = ({ resizing, style }) => { + const resizingControlLineBody = resizing && ( +
    + ); + + return ( +
    + {resizingControlLineBody} +
    + ); +}; + +ResizingControlLine.propTypes = { + resizing: PropTypes.bool.isRequired, + style: PropTypes.object.isRequired, +}; + +export class ResizingControl extends React.PureComponent { + constructor(props) { + super(props); + + this.state = { + resizing: false, + }; + + this.onResizeStart = ({ x }) => { + this.resizeStartingX = x; + this.setState({ resizing: true }); + }; + this.onResizeUpdate = ({ x }) => { + const { onWidthDraft } = this.props; + onWidthDraft({ shift: x - this.resizeStartingX }); + }; + this.onResizeEnd = ({ x }) => { + const { onWidthChange, onWidthDraftCancel } = this.props; + onWidthDraftCancel(); + onWidthChange({ shift: x - this.resizeStartingX }); + this.setState({ resizing: false }); + }; + } + render() { + const { resizing } = this.state; + + return ( + +
    + + +
    +
    + ); + } +} + +ResizingControl.propTypes = { + onWidthChange: PropTypes.func.isRequired, + onWidthDraft: PropTypes.func.isRequired, + onWidthDraftCancel: PropTypes.func.isRequired, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/resizing-control.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/resizing-control.test.jsx new file mode 100644 index 0000000000..6b0649e203 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/resizing-control.test.jsx @@ -0,0 +1,57 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { setupConsole } from '@devexpress/dx-testing'; +import { Draggable } from '@devexpress/dx-react-core'; +import { ResizingControl } from './resizing-control'; + +const defaultProps = { + onWidthChange: () => {}, + onWidthDraft: () => {}, + onWidthDraftCancel: () => {}, +}; + +describe('ResizingControl', () => { + let resetConsole; + beforeAll(() => { + resetConsole = setupConsole({ ignore: ['validateDOMNesting'] }); + }); + afterAll(() => { + resetConsole(); + }); + + it('should trigger onWidthChange with correct change on resize end', () => { + const onWidthChange = jest.fn(); + const onWidthDraftCancel = jest.fn(); + const tree = shallow(( + + )); + + tree.find(Draggable).prop('onStart')({ x: 0 }); + + tree.find(Draggable).prop('onEnd')({ x: 10 }); + expect(onWidthDraftCancel) + .toBeCalled(); + expect(onWidthChange) + .toBeCalledWith({ shift: 10 }); + }); + + it('should trigger onWidthDraft with correct change on resize update', () => { + const onWidthDraft = jest.fn(); + const tree = shallow(( + + )); + + tree.find(Draggable).prop('onStart')({ x: 0 }); + + tree.find(Draggable).prop('onUpdate')({ x: 10 }); + expect(onWidthDraft) + .toBeCalledWith({ shift: 10 }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/sorting-control.css b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/sorting-control.css new file mode 100644 index 0000000000..74f386b1b8 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/sorting-control.css @@ -0,0 +1,3 @@ +.dx-rg-bs4-sorting-control { + margin: 2px; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/sorting-control.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/sorting-control.jsx new file mode 100644 index 0000000000..d3e0fbc0cd --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/sorting-control.jsx @@ -0,0 +1,55 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { SortingIndicator } from '../parts/sorting-indicator'; +import './sorting-control.css'; + +const handleMouseDown = (e) => { e.currentTarget.style.outline = 'none'; }; +const handleBlur = (e) => { e.currentTarget.style.outline = ''; }; + +const getProps = (sortingDirection, disabled, onClick) => ({ + className: classNames({ + 'dx-rg-bs4-sorting-control': true, + 'text-primary': sortingDirection, + }), + tabIndex: disabled ? -1 : 0, + onMouseDown: handleMouseDown, + onBlur: handleBlur, + onKeyDown: onClick, +}); + +export const SortingControl = ({ + align, sortingDirection, columnTitle, disabled, onClick, +}) => { + const props = getProps(sortingDirection, disabled, onClick); + return (align === 'right' ? ( + + +   + {columnTitle} + + ) : ( + + {columnTitle} +   + + + )); +}; + +SortingControl.propTypes = { + align: PropTypes.string.isRequired, + sortingDirection: PropTypes.oneOf(['asc', 'desc']), + columnTitle: PropTypes.string.isRequired, + onClick: PropTypes.func.isRequired, + disabled: PropTypes.bool, +}; + +SortingControl.defaultProps = { + sortingDirection: null, + disabled: false, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/sorting-control.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/sorting-control.test.jsx new file mode 100644 index 0000000000..b879f766a7 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-header-cell/sorting-control.test.jsx @@ -0,0 +1,50 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { SortingControl } from './sorting-control'; + +const defaultProps = { + align: '', + columnTitle: 'test', + onClick: jest.fn(), +}; + +describe('TableHeaderCell with keyboard navigation', () => { + it('can get focus', () => { + const tree = shallow(( + + )); + + expect(tree.find('span').prop('tabIndex')) + .toBe(0); + }); + + it('should handle the "Enter" and "Space" keys down', () => { + const onClick = jest.fn(); + const tree = shallow(( + + )); + + tree.simulate('keydown'); + expect(onClick) + .toHaveBeenCalled(); + }); + + it('can not get focus if disabled is true', () => { + const tree = shallow(( + {}} + /> + )); + + expect(tree.find('span').prop('tabIndex')) + .toBe(-1); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-layout.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-layout.jsx new file mode 100644 index 0000000000..97e5b64246 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-layout.jsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import { + TableLayout as TableLayoutCore, + StaticTableLayout, +} from '@devexpress/dx-react-grid'; +import { TableContainer } from './table-container'; +import { Table } from './table'; + +const MINIMAL_COLUMN_WIDTH = 120; + +const TableHead = props =>
    ; +const TableBody = props => ; + +export const TableLayout = ({ + headerRows, + bodyRows, + columns, + cellComponent, + rowComponent, +}) => ( + +); + +TableLayout.propTypes = { + headerRows: PropTypes.array.isRequired, + bodyRows: PropTypes.array.isRequired, + columns: PropTypes.array.isRequired, + cellComponent: PropTypes.func.isRequired, + rowComponent: PropTypes.func.isRequired, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-no-data-cell.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-no-data-cell.jsx new file mode 100644 index 0000000000..f481f11ef2 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-no-data-cell.jsx @@ -0,0 +1,32 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; + +export const TableNoDataCell = ({ + className, colSpan, + getMessage, tableRow, + tableColumn, ...restProps +}) => ( + +); + +TableNoDataCell.propTypes = { + colSpan: PropTypes.number, + getMessage: PropTypes.func.isRequired, + tableRow: PropTypes.object, + tableColumn: PropTypes.object, + className: PropTypes.string, +}; + +TableNoDataCell.defaultProps = { + className: undefined, + colSpan: 1, + tableRow: undefined, + tableColumn: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-no-data-cell.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-no-data-cell.test.jsx new file mode 100644 index 0000000000..c190960e72 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-no-data-cell.test.jsx @@ -0,0 +1,28 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { TableNoDataCell } from './table-no-data-cell'; + +describe('TableNoDataCell', () => { + const defaultProps = { + getMessage: key => key, + }; + it('should use "noData" text if defined', () => { + const tree = shallow(( + + )); + + expect(tree.find('big').text()).toBe('noData'); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.py-5.text-center.custom-class')) + .toBeTruthy(); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-reordering-cell.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-reordering-cell.jsx new file mode 100644 index 0000000000..a07903f9a7 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-reordering-cell.jsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; + +export const TableReorderingCell = ({ style, getCellDimensions }) => { + const refHandler = node => node && getCellDimensions(() => { + const { left, right } = node.getBoundingClientRect(); + return { left, right }; + }); + return ( + + {children} + +); + +TableRow.propTypes = { + children: PropTypes.node, + row: PropTypes.object, + tableColumn: PropTypes.object, + tableRow: PropTypes.object, +}; + +TableRow.defaultProps = { + children: null, + row: undefined, + tableColumn: undefined, + tableRow: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-row.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-row.test.jsx new file mode 100644 index 0000000000..49804cd00d --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-row.test.jsx @@ -0,0 +1,14 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { TableRow } from './table-row'; + +describe('TableRow', () => { + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.prop('data')) + .toEqual({ a: 1 }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-select-all-cell.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-select-all-cell.jsx new file mode 100644 index 0000000000..875d6064f0 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-select-all-cell.jsx @@ -0,0 +1,65 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; + +export const TableSelectAllCell = ({ + allSelected, someSelected, disabled, onToggle, + tableColumn, tableRow, className, + ...restProps +}) => { + const toggle = (e) => { + if (disabled) return; + + e.stopPropagation(); + onToggle(); + }; + + return ( + + ); +}; + +TableSelectAllCell.propTypes = { + className: PropTypes.string, + allSelected: PropTypes.bool, + someSelected: PropTypes.bool, + disabled: PropTypes.bool, + onToggle: PropTypes.func, + tableRow: PropTypes.object, + tableColumn: PropTypes.object, +}; + +TableSelectAllCell.defaultProps = { + className: undefined, + allSelected: false, + someSelected: false, + disabled: false, + onToggle: () => {}, + tableRow: undefined, + tableColumn: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-select-all-cell.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-select-all-cell.test.jsx new file mode 100644 index 0000000000..376d8a1f24 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-select-all-cell.test.jsx @@ -0,0 +1,69 @@ +import * as React from 'react'; +import { mount, shallow } from 'enzyme'; +import { setupConsole } from '@devexpress/dx-testing'; +import { TableSelectAllCell } from './table-select-all-cell'; + +describe('TableSelectAllCell', () => { + let resetConsole; + beforeAll(() => { + resetConsole = setupConsole({ ignore: ['validateDOMNesting'] }); + }); + afterAll(() => { + resetConsole(); + }); + + it('should render indeterminate state checkbox if the `someSelected` property is true', () => { + const tree = mount(( + + )); + + expect(tree.find('input').getDOMNode().indeterminate) + .toBeTruthy(); + }); + + it('should not fire the `onToggle` event on cell click if selection is not available', () => { + const onToggle = jest.fn(); + const tree = shallow(( + + )); + tree.find('input').simulate('change'); + + expect(onToggle) + .not.toHaveBeenCalled(); + }); + + it('should fire the `onToggle` event on cell click if selection is available', () => { + const onToggle = jest.fn(); + const tree = shallow(( + + )); + tree.find('input').simulate('change', { stopPropagation: jest.fn() }); + + expect(onToggle) + .toHaveBeenCalledTimes(1); + }); + + it('should pass custom class to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.align-middle.dx-rg-bs4-cursor-pointer.custom-class')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + expect(tree.find('th').prop('data')) + .toMatchObject({ a: 1 }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-select-cell.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-select-cell.jsx new file mode 100644 index 0000000000..a23dae6bc6 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-select-cell.jsx @@ -0,0 +1,46 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; + +export const TableSelectCell = ({ + className, selected, + onToggle, row, + tableRow, tableColumn, + ...restProps +}) => ( + + +); + +TableSelectCell.propTypes = { + className: PropTypes.string, + selected: PropTypes.bool, + onToggle: PropTypes.func, + row: PropTypes.object, + tableRow: PropTypes.object, + tableColumn: PropTypes.object, +}; + +TableSelectCell.defaultProps = { + className: undefined, + selected: false, + onToggle: () => {}, + row: undefined, + tableRow: undefined, + tableColumn: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-select-cell.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-select-cell.test.jsx new file mode 100644 index 0000000000..ee35efd0ce --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-select-cell.test.jsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { TableSelectCell } from './table-select-cell'; + +describe('TableSelectCell', () => { + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.align-middle.dx-rg-bs4-cursor-pointer.custom-class')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + expect(tree.find('td').prop('data')) + .toEqual({ a: 1 }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-select-row.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-select-row.jsx new file mode 100644 index 0000000000..02b4c42a02 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-select-row.jsx @@ -0,0 +1,38 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; + +export const TableSelectRow = ({ + selected, + children, + style, + onToggle, + selectByRowClick, +}) => ( + { + if (!selectByRowClick) return; + e.stopPropagation(); + onToggle(); + }} + > + {children} + +); + +TableSelectRow.propTypes = { + selected: PropTypes.bool, + children: PropTypes.node, + onToggle: PropTypes.func, + selectByRowClick: PropTypes.bool, + style: PropTypes.object, +}; + +TableSelectRow.defaultProps = { + children: null, + onToggle: () => {}, + selected: false, + selectByRowClick: false, + style: null, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-select-row.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-select-row.test.jsx new file mode 100644 index 0000000000..eb0165a612 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-select-row.test.jsx @@ -0,0 +1,45 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { setupConsole } from '@devexpress/dx-testing'; +import { TableSelectRow } from './table-select-row'; + +describe('Table Select Row', () => { + const defaultProps = { + selected: false, + selectByRowClick: false, + onToggle: () => {}, + }; + let resetConsole; + beforeAll(() => { + resetConsole = setupConsole({ ignore: ['validateDOMNesting'] }); + }); + afterAll(() => { + resetConsole(); + }); + + it('should have correct className', () => { + let tree = shallow(); + expect(tree.find('tr').hasClass('table-active')).toBeFalsy(); + + tree = shallow(); + expect(tree.find('tr').hasClass('table-active')).toBeTruthy(); + }); + + it('should handle row click', () => { + const onToggleMock = jest.fn(); + const event = { stopPropagation: jest.fn() }; + const tree = shallow(); + + tree.find('tr').simulate('click', event); + expect(onToggleMock).toBeCalled(); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-stub-cell.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-stub-cell.jsx new file mode 100644 index 0000000000..2fcee26565 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-stub-cell.jsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; + +export const TableStubCell = ({ + className, + tableRow, + tableColumn, + ...restProps +}) => ( +
    + {children || value} + + {children} + { + e.stopPropagation(); + onToggle(); + }} + {...restProps} + > + + + {children || ( + onValueChange(e.target.value)} + /> + )} + + {children} + + {children} + + {children || ( + onFilter(e.target.value ? { value: e.target.value } : null)} + readOnly={!filteringEnabled} + /> + )} + + + {column.title || column.name}: + {children || row.value} + + {showGroupingControls && ( + + )} +
    + {showSortingControls ? ( + + ) : ( + columnTitle + )} +
    + {resizingEnabled && ( + + )} +
    + {getMessage('noData')} + + ); +}; + +TableReorderingCell.propTypes = { + getCellDimensions: PropTypes.func.isRequired, + style: PropTypes.object, +}; + +TableReorderingCell.defaultProps = { + style: null, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-row.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-row.jsx new file mode 100644 index 0000000000..b2a48c4cbf --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-row.jsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; + +export const TableRow = ({ + children, row, tableRow, tableColumn, + ...restProps +}) => ( +
    + { + if (ref) { + const checkbox = ref; + checkbox.indeterminate = someSelected; + } + }} + onChange={toggle} + onClick={e => e.stopPropagation()} + /> + { + e.stopPropagation(); + onToggle(); + }} + {...restProps} + > + e.stopPropagation()} + /> +
    +); + +TableStubCell.propTypes = { + className: PropTypes.string, + tableRow: PropTypes.object, + tableColumn: PropTypes.object, +}; + +TableStubCell.defaultProps = { + className: undefined, + tableRow: undefined, + tableColumn: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-stub-cell.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-stub-cell.test.jsx new file mode 100644 index 0000000000..0a15bcc682 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-stub-cell.test.jsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { TableStubCell } from './table-stub-cell'; + +describe('TableStubCell', () => { + it('should pass custom class to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.p-0.custom-class')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.prop('data')) + .toEqual({ a: 1 }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-stub-header-cell.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-stub-header-cell.jsx new file mode 100644 index 0000000000..caf8c85f1a --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-stub-header-cell.jsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; + +export const TableStubHeaderCell = ({ + className, + tableRow, + tableColumn, + ...restProps +}) => ( + +); + +TableStubHeaderCell.propTypes = { + className: PropTypes.string, + tableRow: PropTypes.object, + tableColumn: PropTypes.object, +}; + +TableStubHeaderCell.defaultProps = { + className: undefined, + tableRow: undefined, + tableColumn: undefined, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table-stub-header-cell.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table-stub-header-cell.test.jsx new file mode 100644 index 0000000000..ddacdf2407 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table-stub-header-cell.test.jsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { TableStubHeaderCell } from './table-stub-header-cell'; + +describe('TableStubHeaderCell', () => { + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.is('.p-0.custom-class')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + + )); + + expect(tree.prop('data')) + .toEqual({ a: 1 }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table.css b/packages/dx-react-grid-bootstrap4/src/templates/table.css new file mode 100644 index 0000000000..4c6d3973ff --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table.css @@ -0,0 +1,7 @@ +.dx-rg-bs4-table { + table-layout: fixed; +} +.dx-rg-bs4-table-head { + top: 0; + z-index: 1; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/table.jsx b/packages/dx-react-grid-bootstrap4/src/templates/table.jsx new file mode 100644 index 0000000000..fc0449f722 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/table.jsx @@ -0,0 +1,85 @@ +/* globals document:true window:true */ + +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; + +import './table.css'; + +let globalStickyProp; +const testCSSProp = (property, value, noPrefixes) => { + const prop = `${property}:`; + const el = document.createElement('test'); + const mStyle = el.style; + + if (!noPrefixes) { + mStyle.cssText = `${prop + ['-webkit-', '-moz-', '-ms-', '-o-', ''].join(`${value};${prop}`) + value};`; + } else { + mStyle.cssText = prop + value; + } + return mStyle[property]; +}; + +export class Table extends React.Component { + constructor() { + super(); + + this.state = { + stickyProp: globalStickyProp, + backgroundColor: 'white', + }; + } + componentDidMount() { + this.checkStyles(); + } + checkStyles() { + globalStickyProp = testCSSProp('position', 'sticky'); + + const body = document.getElementsByTagName('body')[0]; + const { backgroundColor } = window.getComputedStyle(body); + + if (this.state.backgroundColor !== backgroundColor + || this.state.stickyProp !== globalStickyProp) { + this.setState({ stickyProp: globalStickyProp, backgroundColor }); + } + } + render() { + const { + children, use, style, ...restProps + } = this.props; + const { stickyProp, backgroundColor } = this.state; + return ( + { this.node = node; }} + className={classNames({ + 'table mb-0 dx-rg-bs4-overflow-hidden dx-rg-bs4-table': true, + 'dx-rg-bs4-table-head': use === 'head', + })} + {...restProps} + style={{ + ...style, + ...use === 'head' ? { + position: stickyProp, + backgroundColor, + } : null, + }} + > + {children} +
    + ); + } +} + +Table.propTypes = { + use: PropTypes.oneOf(['head']), + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + ]).isRequired, + style: PropTypes.object, +}; + +Table.defaultProps = { + use: undefined, + style: null, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/toolbar/flexible-space.jsx b/packages/dx-react-grid-bootstrap4/src/templates/toolbar/flexible-space.jsx new file mode 100644 index 0000000000..77457ee9ef --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/toolbar/flexible-space.jsx @@ -0,0 +1,4 @@ +import * as React from 'react'; + +export const FlexibleSpace = () => +
    ; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/toolbar/toolbar.css b/packages/dx-react-grid-bootstrap4/src/templates/toolbar/toolbar.css new file mode 100644 index 0000000000..3b6975ad02 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/toolbar/toolbar.css @@ -0,0 +1,4 @@ +.dx-rg-bs4-toolbar { + align-items: center; + min-height: 55px; +} diff --git a/packages/dx-react-grid-bootstrap4/src/templates/toolbar/toolbar.jsx b/packages/dx-react-grid-bootstrap4/src/templates/toolbar/toolbar.jsx new file mode 100644 index 0000000000..7b9ced1d03 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/toolbar/toolbar.jsx @@ -0,0 +1,33 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import classNames from 'classnames'; +import './toolbar.css'; + +export const Toolbar = ({ + children, + className, + style, + ...restProps +}) => ( +
    + {children} +
    +); + +Toolbar.propTypes = { + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + ]).isRequired, + className: PropTypes.string, + style: PropTypes.object, +}; + +Toolbar.defaultProps = { + className: undefined, + style: null, +}; diff --git a/packages/dx-react-grid-bootstrap4/src/templates/toolbar/toolbar.test.jsx b/packages/dx-react-grid-bootstrap4/src/templates/toolbar/toolbar.test.jsx new file mode 100644 index 0000000000..42ee5f5e6b --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/toolbar/toolbar.test.jsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import { shallow } from 'enzyme'; +import { Toolbar } from './toolbar'; + +describe('Toolbar', () => { + it('should pass custom class to the root element', () => { + const tree = shallow(( + +
    + + )); + + expect(tree.is('.custom-class.card-header.dx-rg-bs4-toolbar.d-flex.position-relative')) + .toBeTruthy(); + }); + + it('should pass rest props to the root element', () => { + const tree = shallow(( + +
    + + )); + + expect(tree.prop('data')) + .toMatchObject({ a: 'a' }); + }); +}); diff --git a/packages/dx-react-grid-bootstrap4/src/templates/virtual-table-layout.jsx b/packages/dx-react-grid-bootstrap4/src/templates/virtual-table-layout.jsx new file mode 100644 index 0000000000..2e4b00ac26 --- /dev/null +++ b/packages/dx-react-grid-bootstrap4/src/templates/virtual-table-layout.jsx @@ -0,0 +1,51 @@ +import * as React from 'react'; +import * as PropTypes from 'prop-types'; +import { + TableLayout, + VirtualTableLayout as VirtualTableLayoutCore, +} from '@devexpress/dx-react-grid'; +import { TableContainer } from './table-container'; +import { Table } from './table'; + +const MINIMAL_COLUMN_WIDTH = 120; + +const HeadTable = props => ; +const TableHead = props => ; +const TableBody = props => ; + +export const VirtualTableLayout = ({ + headerRows, + bodyRows, + columns, + cellComponent, + rowComponent, + height, + estimatedRowHeight, +}) => ( + +); + +VirtualTableLayout.propTypes = { + headerRows: PropTypes.array.isRequired, + bodyRows: PropTypes.array.isRequired, + columns: PropTypes.array.isRequired, + cellComponent: PropTypes.func.isRequired, + rowComponent: PropTypes.func.isRequired, + height: PropTypes.number.isRequired, + estimatedRowHeight: PropTypes.number.isRequired, +}; diff --git a/packages/dx-react-grid-material-ui/src/templates/column-chooser/overlay.jsx b/packages/dx-react-grid-material-ui/src/templates/column-chooser/overlay.jsx index 1c101f60c2..a41bd5f3ad 100644 --- a/packages/dx-react-grid-material-ui/src/templates/column-chooser/overlay.jsx +++ b/packages/dx-react-grid-material-ui/src/templates/column-chooser/overlay.jsx @@ -3,7 +3,7 @@ import * as PropTypes from 'prop-types'; import Popover from 'material-ui/Popover'; export const Overlay = ({ - visible, onHide, + visible, onHide, toggle, children, target, ...restProps }) => ( @@ -24,9 +24,11 @@ Overlay.propTypes = { children: PropTypes.node.isRequired, visible: PropTypes.bool, target: PropTypes.object, + toggle: PropTypes.func, }; Overlay.defaultProps = { visible: false, target: null, + toggle: undefined, }; diff --git a/packages/dx-react-grid-material-ui/src/templates/column-chooser/toggle-button.jsx b/packages/dx-react-grid-material-ui/src/templates/column-chooser/toggle-button.jsx index 0fdbaaae4f..8a08e72674 100644 --- a/packages/dx-react-grid-material-ui/src/templates/column-chooser/toggle-button.jsx +++ b/packages/dx-react-grid-material-ui/src/templates/column-chooser/toggle-button.jsx @@ -5,9 +5,8 @@ import Tooltip from 'material-ui/Tooltip'; import VisibilityOff from 'material-ui-icons/VisibilityOff'; export const ToggleButton = ({ - onToggle, - getMessage, - buttonRef, + onToggle, getMessage, + buttonRef, active, ...restProps }) => ( + ``` + + NOTE: The DevExtreme React Grid does not include Bootstrap CSS. + +- Bootstrap 3 + + ``` + npm i --save @devexpress/dx-react-grid-bootstrap3 + ``` + + Make sure that the [React-Boostrap](https://react-bootstrap.github.io) dependencies are installed and properly configured. Check the React-Bootstrap's [Getting Started](https://react-bootstrap.github.io/getting-started/introduction) article for configuration details. + + NOTE: The DevExtreme React Grid does not include Bootstrap CSS so this needs to be installed as well. ## Supported Browsers @@ -49,7 +67,9 @@ Use the Table plugin to display the data as a simple table: ```jsx import { Grid, Table, TableHeaderRow -} from '@devexpress/dx-react-grid-bootstrap3'/* or '@devexpress/dx-react-grid-material-ui' */; +} from '@devexpress/dx-react-grid-material-ui'; +/*or '@devexpress/dx-react-grid-bootstrap4' + or '@devexpress/dx-react-grid-bootstrap3'*/ const App = () => ( ( Follow the links below to try out the React Grid: -- [CodeSandbox for Bootstrap3](https://codesandbox.io/s/7o46mkowx) - [CodeSandbox for Material UI](https://codesandbox.io/s/13qvz1qqzl) +- [CodeSandbox for Bootstrap3](https://codesandbox.io/s/7o46mkowx) ## License diff --git a/packages/dx-react-grid/src/plugins/column-chooser.jsx b/packages/dx-react-grid/src/plugins/column-chooser.jsx index b7b05f0d6d..f40776caa6 100644 --- a/packages/dx-react-grid/src/plugins/column-chooser.jsx +++ b/packages/dx-react-grid/src/plugins/column-chooser.jsx @@ -57,11 +57,13 @@ export class ColumnChooser extends React.PureComponent { buttonRef={this.buttonRef} onToggle={this.handleToggle} getMessage={getMessage} + active={visible} /> {columnChooserItems(columns, hiddenColumnNames) diff --git a/yarn.lock b/yarn.lock index a120e29ae4..ee05370be0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -41,11 +41,7 @@ dependencies: "@types/react" "*" -"@types/react@*": - version "16.0.38" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.0.38.tgz#76617433ea10274505f60bb86eddfdd0476ffdc2" - -"@types/react@^16.0.39": +"@types/react@*", "@types/react@^16.0.39": version "16.0.39" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.0.39.tgz#6aef052209521cd76f62cd7286abdc8fd787e39d" @@ -65,10 +61,10 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" accepts@~1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.4.tgz#86246758c7dd6d21a6474ff084a4740ec05eb21f" + version "1.3.5" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" dependencies: - mime-types "~2.1.16" + mime-types "~2.1.18" negotiator "0.6.1" acorn-dynamic-import@^2.0.0: @@ -98,18 +94,14 @@ acorn@^4.0.3: resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" acorn@^5.0.0, acorn@^5.3.0, acorn@^5.4.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.4.1.tgz#fdc58d9d17f4a4e98d102ded826a9b9759125102" + version "5.5.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.0.tgz#1abb587fbf051f94e3de20e6b26ef910b1828298" add-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" -ajv-keywords@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" - -ajv-keywords@^3.1.0: +ajv-keywords@^3.0.0, ajv-keywords@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.1.0.tgz#ac2b27939c543e95d2c06e7f7f5c27be4aa543be" @@ -120,7 +112,7 @@ ajv@^4.9.1: co "^4.6.0" json-stable-stringify "^1.0.1" -ajv@^5.1.0, ajv@^5.2.3, ajv@^5.3.0: +ajv@^5.1.0, ajv@^5.3.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" dependencies: @@ -129,9 +121,9 @@ ajv@^5.1.0, ajv@^5.2.3, ajv@^5.3.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" -ajv@^6.1.0: - version "6.1.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.1.1.tgz#978d597fbc2b7d0e5a5c3ddeb149a682f2abfa0e" +ajv@^6.0.1, ajv@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.2.0.tgz#afac295bbaa0152449e522742e4547c1ae9328d2" dependencies: fast-deep-equal "^1.0.0" fast-json-stable-stringify "^2.0.0" @@ -1140,9 +1132,9 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" -braces@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.0.tgz#a46941cb5fb492156b3d6a656e06c35364e3e66e" +braces@^2.3.0, braces@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.1.tgz#7086c913b4e5a08dbe37ac0ee6a2500c4ba691bb" dependencies: arr-flatten "^1.1.0" array-unique "^0.3.2" @@ -1150,6 +1142,7 @@ braces@^2.3.0: extend-shallow "^2.0.1" fill-range "^4.0.0" isobject "^3.0.1" + kind-of "^6.0.2" repeat-element "^1.1.2" snapdragon "^0.8.1" snapdragon-node "^2.0.1" @@ -1340,8 +1333,8 @@ 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.30000808" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000808.tgz#30dfd83009d5704f02dffb37725068ed12a366bb" + version "1.0.30000810" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000810.tgz#bd25830c41efab64339a2e381f49677343c84509" capture-stack-trace@^1.0.0: version "1.0.0" @@ -1421,7 +1414,7 @@ cheerio@^1.0.0-rc.2: lodash "^4.15.0" parse5 "^3.0.1" -chokidar@^1.6.0, chokidar@^1.7.0: +chokidar@^1.6.0: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" dependencies: @@ -1484,7 +1477,7 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classnames@^2.2.5: +classnames@^2.2.3, classnames@^2.2.5: version "2.2.5" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" @@ -1682,19 +1675,19 @@ 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.11: - version "2.0.12" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.12.tgz#c59a5c99db76767e9876500e271ef63b3493bd66" +compressible@~2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.13.tgz#0d1020ab924b2fdb4d6279875c7d6daba6baa7a9" dependencies: - mime-db ">= 1.30.0 < 2" + mime-db ">= 1.33.0 < 2" compression@^1.5.2: - version "1.7.1" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.1.tgz#eff2603efc2e22cf86f35d2eb93589f9875373db" + version "1.7.2" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.2.tgz#aaffbcd6aaf854b44ebb280353d5ad1651f59a69" dependencies: accepts "~1.3.4" bytes "3.0.0" - compressible "~2.0.11" + compressible "~2.0.13" debug "2.6.9" on-headers "~1.0.1" safe-buffer "5.1.1" @@ -1770,13 +1763,6 @@ content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" -conventional-changelog-angular@^1.6.4: - version "1.6.4" - resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-1.6.4.tgz#47debaf92b75b0bd6b39fcba8f9c70dd97552be6" - dependencies: - compare-func "^1.3.1" - q "^1.4.1" - conventional-changelog-angular@^1.6.6: version "1.6.6" resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-1.6.6.tgz#b27f2b315c16d0a1f23eb181309d0e6a4698ea0f" @@ -1784,29 +1770,13 @@ conventional-changelog-angular@^1.6.6: compare-func "^1.3.1" q "^1.5.1" -conventional-changelog-atom@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/conventional-changelog-atom/-/conventional-changelog-atom-0.2.2.tgz#2c7326a8f24686f51500a290ed897d47612be4c3" - dependencies: - q "^1.4.1" - conventional-changelog-atom@^0.2.4: version "0.2.4" resolved "https://registry.yarnpkg.com/conventional-changelog-atom/-/conventional-changelog-atom-0.2.4.tgz#4917759947f4db86073f9d3838a2d54302d5843d" dependencies: q "^1.5.1" -conventional-changelog-cli@^1.3.13: - version "1.3.13" - resolved "https://registry.yarnpkg.com/conventional-changelog-cli/-/conventional-changelog-cli-1.3.13.tgz#8cb5855bc3c684aa8f5dc96e848d1fa5a82eee1e" - dependencies: - add-stream "^1.0.0" - conventional-changelog "^1.1.15" - lodash "^4.1.0" - meow "^3.7.0" - tempfile "^1.1.1" - -conventional-changelog-cli@^1.3.15: +conventional-changelog-cli@^1.3.13, conventional-changelog-cli@^1.3.15: version "1.3.15" resolved "https://registry.yarnpkg.com/conventional-changelog-cli/-/conventional-changelog-cli-1.3.15.tgz#cd7f0e64473f1cecee902ee5ee00548ea91bcc9e" dependencies: @@ -1816,36 +1786,12 @@ conventional-changelog-cli@^1.3.15: meow "^4.0.0" tempfile "^1.1.1" -conventional-changelog-codemirror@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/conventional-changelog-codemirror/-/conventional-changelog-codemirror-0.3.2.tgz#65ef0ab738c40bdf953951edfdb0cb17302606aa" - dependencies: - q "^1.4.1" - conventional-changelog-codemirror@^0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/conventional-changelog-codemirror/-/conventional-changelog-codemirror-0.3.4.tgz#debc43991d487d7964e65087fbbe034044bd51fb" dependencies: q "^1.5.1" -conventional-changelog-core@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-2.0.3.tgz#30797b91d5f510188288d5ff54905e5cf4628e3c" - dependencies: - conventional-changelog-writer "^3.0.2" - conventional-commits-parser "^2.1.3" - dateformat "^1.0.12" - get-pkg-repo "^1.0.0" - git-raw-commits "^1.3.2" - git-remote-origin-url "^2.0.0" - git-semver-tags "^1.3.2" - lodash "^4.0.0" - normalize-package-data "^2.3.5" - q "^1.4.1" - read-pkg "^1.1.0" - read-pkg-up "^1.0.1" - through2 "^2.0.0" - conventional-changelog-core@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-2.0.5.tgz#45b6347c4c6512e1f163f7ff55c9f5bcb88fd990" @@ -1864,36 +1810,18 @@ conventional-changelog-core@^2.0.5: read-pkg-up "^1.0.1" through2 "^2.0.0" -conventional-changelog-ember@^0.3.4: - version "0.3.4" - resolved "https://registry.yarnpkg.com/conventional-changelog-ember/-/conventional-changelog-ember-0.3.4.tgz#76240e769b2f5298e78e85cb4eda69ef2f1358d2" - dependencies: - q "^1.4.1" - conventional-changelog-ember@^0.3.6: version "0.3.6" resolved "https://registry.yarnpkg.com/conventional-changelog-ember/-/conventional-changelog-ember-0.3.6.tgz#f3825d7434168f3d9211b5532dc1d5769532b668" dependencies: q "^1.5.1" -conventional-changelog-eslint@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/conventional-changelog-eslint/-/conventional-changelog-eslint-1.0.2.tgz#3f9e6b0b60f98042f6f4dfc85a611a50b5e79cf9" - dependencies: - q "^1.4.1" - conventional-changelog-eslint@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/conventional-changelog-eslint/-/conventional-changelog-eslint-1.0.4.tgz#333456d1884d96f3a49cc0984a745095e9d56288" dependencies: q "^1.5.1" -conventional-changelog-express@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/conventional-changelog-express/-/conventional-changelog-express-0.3.2.tgz#f5af4770a31f147986db548b49f9952fc55e3eb6" - dependencies: - q "^1.4.1" - conventional-changelog-express@^0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/conventional-changelog-express/-/conventional-changelog-express-0.3.4.tgz#812a9cf778677e12f978ac9c40d85297c0bfcca9" @@ -1912,13 +1840,6 @@ conventional-changelog-jscs@^0.1.0: dependencies: q "^1.4.1" -conventional-changelog-jshint@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/conventional-changelog-jshint/-/conventional-changelog-jshint-0.3.2.tgz#4d45d2601c944687abceabbc1789323719234cbe" - dependencies: - compare-func "^1.3.1" - q "^1.4.1" - conventional-changelog-jshint@^0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/conventional-changelog-jshint/-/conventional-changelog-jshint-0.3.4.tgz#b2de33cd0870d9af804ac6a4fded0ee25b69c9bb" @@ -1926,29 +1847,10 @@ conventional-changelog-jshint@^0.3.4: compare-func "^1.3.1" q "^1.5.1" -conventional-changelog-preset-loader@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-1.1.4.tgz#5096165f2742a18dc0e33ff2ab9ee08dc9d77f08" - conventional-changelog-preset-loader@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-1.1.6.tgz#b29af6332f9313857be36427623c9016bfeeaf33" -conventional-changelog-writer@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-3.0.2.tgz#f3f934028379c0cab90aecfcaf009bf8a187ef14" - dependencies: - compare-func "^1.3.1" - conventional-commits-filter "^1.1.3" - dateformat "^1.0.11" - handlebars "^4.0.2" - json-stringify-safe "^5.0.1" - lodash "^4.0.0" - meow "^3.3.0" - semver "^5.0.1" - split "^1.0.0" - through2 "^2.0.0" - conventional-changelog-writer@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-3.0.4.tgz#705b46a8b8277bd7fd79cad8032095b5d803864c" @@ -1964,22 +1866,6 @@ conventional-changelog-writer@^3.0.4: split "^1.0.0" through2 "^2.0.0" -conventional-changelog@^1.1.15: - version "1.1.15" - resolved "https://registry.yarnpkg.com/conventional-changelog/-/conventional-changelog-1.1.15.tgz#a5c3d281efb40f61c7d21eeffb19e6f6a8429df0" - dependencies: - conventional-changelog-angular "^1.6.4" - conventional-changelog-atom "^0.2.2" - conventional-changelog-codemirror "^0.3.2" - conventional-changelog-core "^2.0.3" - conventional-changelog-ember "^0.3.4" - conventional-changelog-eslint "^1.0.2" - conventional-changelog-express "^0.3.2" - conventional-changelog-jquery "^0.1.0" - conventional-changelog-jscs "^0.1.0" - conventional-changelog-jshint "^0.3.2" - conventional-changelog-preset-loader "^1.1.4" - conventional-changelog@^1.1.17: version "1.1.17" resolved "https://registry.yarnpkg.com/conventional-changelog/-/conventional-changelog-1.1.17.tgz#9019503d51328d8b33b0be6d425ab7f065918332" @@ -1996,33 +1882,14 @@ conventional-changelog@^1.1.17: conventional-changelog-jshint "^0.3.4" conventional-changelog-preset-loader "^1.1.6" -conventional-commits-filter@^1.1.1, conventional-commits-filter@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-1.1.3.tgz#5bf591bc4882fc8c9bd329e5a83ca1fa8721d9fb" - dependencies: - is-subset "^0.1.1" - modify-values "^1.0.0" - -conventional-commits-filter@^1.1.5: +conventional-commits-filter@^1.1.1, conventional-commits-filter@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-1.1.5.tgz#77aac065e3de9c1a74b801e8e25c9affb3184f65" dependencies: is-subset "^0.1.1" modify-values "^1.0.0" -conventional-commits-parser@^2.1.1, conventional-commits-parser@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-2.1.3.tgz#fbbfcfe4901ccbae63bb3834f982325e0b7c663f" - dependencies: - JSONStream "^1.0.4" - is-text-path "^1.0.0" - lodash "^4.2.1" - meow "^3.3.0" - split2 "^2.0.0" - through2 "^2.0.0" - trim-off-newlines "^1.0.0" - -conventional-commits-parser@^2.1.5: +conventional-commits-parser@^2.1.1, conventional-commits-parser@^2.1.5: version "2.1.5" resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-2.1.5.tgz#9ac3a4ab221c0c3c9e9dd2c09ae01e6d1e1dabe0" dependencies: @@ -2320,13 +2187,6 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -dateformat@^1.0.11, dateformat@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" - dependencies: - get-stdin "^4.0.1" - meow "^3.3.0" - dateformat@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" @@ -2413,6 +2273,13 @@ define-property@^1.0.0: dependencies: is-descriptor "^1.0.0" +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" @@ -2637,8 +2504,8 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" electron-to-chromium@^1.2.7: - version "1.3.33" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.33.tgz#bf00703d62a7c65238136578c352d6c5c042a545" + version "1.3.34" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.34.tgz#d93498f40391bb0c16a603d8241b9951404157ed" elliptic@^6.0.0: version "6.4.0" @@ -2830,15 +2697,15 @@ escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" escodegen@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.0.tgz#9811a2f265dc1cd3894420ee3717064b632b8852" + version "1.9.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.1.tgz#dbae17ef96c8e4bedb1356f4504fa4cc2f7cb7e2" dependencies: esprima "^3.1.3" estraverse "^4.2.0" esutils "^2.0.2" optionator "^0.8.1" optionalDependencies: - source-map "~0.5.6" + source-map "~0.6.1" escope@^3.6.0: version "3.6.0" @@ -3007,11 +2874,10 @@ esquery@^1.0.0: estraverse "^4.0.0" esrecurse@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" dependencies: estraverse "^4.1.0" - object-assign "^4.0.1" estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.2.0" @@ -3203,7 +3069,7 @@ extend-shallow@^2.0.1: dependencies: is-extendable "^0.1.0" -extend-shallow@^3.0.0: +extend-shallow@^3.0.0, extend-shallow@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" dependencies: @@ -3228,7 +3094,7 @@ extglob@^0.3.1: dependencies: is-extglob "^1.0.0" -extglob@^2.0.2: +extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" dependencies: @@ -3258,8 +3124,8 @@ fancy-log@^1.1.0, fancy-log@^1.3.2: time-stamp "^1.0.0" fast-deep-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" + version "1.1.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" fast-json-stable-stringify@^2.0.0: version "2.0.0" @@ -3600,17 +3466,7 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -git-raw-commits@^1.3.0, git-raw-commits@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-1.3.2.tgz#0766c14d33566ba0094869697e13b0eb06147c07" - dependencies: - dargs "^4.0.1" - lodash.template "^4.0.2" - meow "^3.3.0" - split2 "^2.0.0" - through2 "^2.0.0" - -git-raw-commits@^1.3.4: +git-raw-commits@^1.3.0, git-raw-commits@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-1.3.4.tgz#442c3df5985b4f5689e9e43597f5194736aac001" dependencies: @@ -3627,14 +3483,7 @@ git-remote-origin-url@^2.0.0: gitconfiglocal "^1.0.0" pify "^2.3.0" -git-semver-tags@^1.3.0, git-semver-tags@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-1.3.2.tgz#94afa43c9070ae527a3ab86b978e59ae207803cc" - dependencies: - meow "^3.3.0" - semver "^5.0.1" - -git-semver-tags@^1.3.4: +git-semver-tags@^1.3.0, git-semver-tags@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-1.3.4.tgz#2ceb2a355c6d7514c123c35e297067d08caf3a92" dependencies: @@ -4070,11 +3919,7 @@ hoek@4.x.x: version "4.2.1" resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" -hoist-non-react-statics@^2.3.0, hoist-non-react-statics@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz#343db84c6018c650778898240135a1420ee22ce0" - -hoist-non-react-statics@^2.5.0: +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" @@ -4316,8 +4161,8 @@ interpret@^1.0.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" invariant@^2.0.0, invariant@^2.1.0, invariant@^2.2.1, invariant@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + version "2.2.3" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.3.tgz#1a827dfde7dcbd7c323f0ca826be8fa7c5e9d688" dependencies: loose-envify "^1.0.0" @@ -4329,9 +4174,9 @@ ip@^1.1.0, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" -ipaddr.js@1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.5.2.tgz#d4b505bde9946987ccf0fc58d9010ff9607e3fa0" +ipaddr.js@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b" is-absolute-url@^2.0.0: version "2.1.0" @@ -4414,7 +4259,7 @@ is-descriptor@^0.1.0: is-data-descriptor "^0.1.4" kind-of "^5.0.0" -is-descriptor@^1.0.0: +is-descriptor@^1.0.0, is-descriptor@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" dependencies: @@ -4531,15 +4376,19 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + is-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" -is-odd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-1.0.0.tgz#3b8a932eb028b3775c39bb09e91767accdb69088" +is-odd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24" dependencies: - is-number "^3.0.0" + is-number "^4.0.0" is-path-cwd@^1.0.0: version "1.0.0" @@ -4645,7 +4494,7 @@ is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" -is-windows@^1.0.1: +is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -5313,7 +5162,7 @@ kind-of@^4.0.0: dependencies: is-buffer "^1.1.5" -kind-of@^5.0.0, kind-of@^5.0.2: +kind-of@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" @@ -5515,10 +5364,6 @@ lodash.camelcase@4.3.0, lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" -lodash.endswith@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/lodash.endswith/-/lodash.endswith-4.2.1.tgz#fed59ac1738ed3e236edd7064ec456448b37bc09" - lodash.escape@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" @@ -5541,9 +5386,9 @@ lodash.isfunction@^3.0.8: version "3.0.9" resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" +lodash.isobject@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d" lodash.kebabcase@4.1.1: version "4.1.1" @@ -5573,10 +5418,6 @@ lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" -lodash.startswith@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/lodash.startswith/-/lodash.startswith-4.2.1.tgz#c598c4adce188a27e53145731cdc6c0e7177600c" - lodash.template@^3.0.0: version "3.6.2" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" @@ -5611,6 +5452,10 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "~3.0.0" +lodash.tonumber@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/lodash.tonumber/-/lodash.tonumber-4.0.3.tgz#0b96b31b35672793eb7f5a63ee791f1b9e9025d9" + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -5623,7 +5468,7 @@ lodash@4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" -lodash@^4.0.0, lodash@^4.1.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1: +lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1: version "4.17.5" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" @@ -5678,8 +5523,8 @@ magic-string@0.22.4: vlq "^0.2.1" make-dir@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.1.0.tgz#19b4369fe48c116f53c2af95ad102c0e39e85d51" + version "1.2.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b" dependencies: pify "^3.0.0" @@ -5791,7 +5636,7 @@ memory-fs@^0.4.0, memory-fs@~0.4.1: errno "^0.1.3" readable-stream "^2.0.1" -meow@^3.3.0, meow@^3.7.0: +meow@^3.3.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" dependencies: @@ -5857,18 +5702,18 @@ micromatch@^2.1.5, micromatch@^2.3.11: regex-cache "^0.4.2" micromatch@^3.0.3, micromatch@^3.0.4, micromatch@^3.1.4: - version "3.1.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.5.tgz#d05e168c206472dfbca985bfef4f57797b4cd4ba" + version "3.1.9" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.9.tgz#15dc93175ae39e52e93087847096effc73efcf89" dependencies: arr-diff "^4.0.0" array-unique "^0.3.2" - braces "^2.3.0" - define-property "^1.0.0" - extend-shallow "^2.0.1" - extglob "^2.0.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" fragment-cache "^0.2.1" - kind-of "^6.0.0" - nanomatch "^1.2.5" + kind-of "^6.0.2" + nanomatch "^1.2.9" object.pick "^1.3.0" regex-not "^1.0.0" snapdragon "^0.8.1" @@ -5881,19 +5726,15 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -"mime-db@>= 1.30.0 < 2": +"mime-db@>= 1.33.0 < 2", mime-db@~1.33.0: version "1.33.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" -mime-db@~1.30.0: - version "1.30.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" - -mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.16, mime-types@~2.1.17, mime-types@~2.1.7: - version "2.1.17" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" +mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.7: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" dependencies: - mime-db "~1.30.0" + mime-db "~1.33.0" mime@1.4.1: version "1.4.1" @@ -6022,20 +5863,21 @@ mute-stream@0.0.7: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" nan@^2.3.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a" + version "2.9.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.9.2.tgz#f564d75f5f8f36a6d9456cca7a6c4fe488ab7866" -nanomatch@^1.2.5: - version "1.2.7" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.7.tgz#53cd4aa109ff68b7f869591fdc9d10daeeea3e79" +nanomatch@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2" dependencies: arr-diff "^4.0.0" array-unique "^0.3.2" - define-property "^1.0.0" - extend-shallow "^2.0.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" fragment-cache "^0.2.1" - is-odd "^1.0.0" - kind-of "^5.0.2" + is-odd "^2.0.0" + is-windows "^1.0.2" + kind-of "^6.0.2" object.pick "^1.3.0" regex-not "^1.0.0" snapdragon "^0.8.1" @@ -6062,6 +5904,10 @@ negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +neo-async@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.0.tgz#76b1c823130cca26acfbaccc8fbaf0a2fa33b18f" + node-fetch@^1.0.1: version "1.7.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" @@ -6699,7 +6545,7 @@ pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" -popper.js@^1.12.9: +popper.js@^1.12.5, popper.js@^1.12.9: version "1.12.9" resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.12.9.tgz#0dfbc2dff96c451bb332edcfcfaaf566d331d5b3" @@ -6954,8 +6800,8 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0 supports-color "^3.2.3" postcss@^6.0.1: - version "6.0.18" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.18.tgz#370f5f44d47f3a205f0eb2f6262bbf202df2a80e" + version "6.0.19" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.19.tgz#76a78386f670b9d9494a655bf23ac012effd1555" dependencies: chalk "^2.3.1" source-map "^0.6.1" @@ -7026,15 +6872,7 @@ prop-types-extra@^1.0.1: dependencies: warning "^3.0.0" -prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.6.0: - version "15.6.0" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856" - dependencies: - fbjs "^0.8.16" - loose-envify "^1.3.1" - object-assign "^4.1.1" - -prop-types@^15.6.1: +prop-types@^15.5.10, prop-types@^15.5.4, 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: @@ -7043,11 +6881,11 @@ prop-types@^15.6.1: object-assign "^4.1.1" proxy-addr@~2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.2.tgz#6571504f47bb988ec8180253f85dd7e14952bdec" + version "2.0.3" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341" dependencies: forwarded "~0.1.2" - ipaddr.js "1.5.2" + ipaddr.js "1.6.0" prr@~1.0.1: version "1.0.1" @@ -7167,8 +7005,8 @@ randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: safe-buffer "^5.1.0" randomfill@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.3.tgz#b96b7df587f01dd91726c418f30553b1418e3d62" + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" dependencies: randombytes "^2.0.5" safe-buffer "^5.1.0" @@ -7255,6 +7093,13 @@ react-overlays@^0.8.0: react-transition-group "^2.2.0" warning "^3.0.0" +react-popper@^0.7.2: + version "0.7.5" + resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-0.7.5.tgz#71c25946f291db381231281f6b95729e8b801596" + dependencies: + popper.js "^1.12.5" + prop-types "^15.5.10" + react-popper@^0.8.0: version "0.8.2" resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-0.8.2.tgz#092095ff13933211d3856d9f325511ec3a42f12c" @@ -7348,6 +7193,18 @@ react@^16.2.0: object-assign "^4.1.1" prop-types "^15.6.0" +reactstrap@5.0.0-beta: + version "5.0.0-beta" + resolved "https://registry.yarnpkg.com/reactstrap/-/reactstrap-5.0.0-beta.tgz#0372acae7665ec3396abe87e5cdddf3077e98995" + dependencies: + classnames "^2.2.3" + lodash.isfunction "^3.0.8" + lodash.isobject "^3.0.2" + lodash.tonumber "^4.0.3" + prop-types "^15.5.8" + react-popper "^0.7.2" + react-transition-group "^2.2.0" + read-cmd-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.1.tgz#2d5d157786a37c055d22077c32c53f8329e91c7b" @@ -7522,11 +7379,12 @@ regex-cache@^0.4.2: dependencies: is-equal-shallow "^0.1.3" -regex-not@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.0.tgz#42f83e39771622df826b02af176525d6a5f157f9" +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" dependencies: - extend-shallow "^2.0.1" + extend-shallow "^3.0.2" + safe-regex "^1.1.0" regexpu-core@^1.0.0: version "1.0.0" @@ -7755,6 +7613,13 @@ rollup-plugin-babel@^3.0.3: dependencies: rollup-pluginutils "^1.5.0" +rollup-plugin-css-only@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-css-only/-/rollup-plugin-css-only-0.4.0.tgz#eaf10c79b17c88dc95712fe91518e3afedfb657a" + dependencies: + mkdirp "^0.5.1" + rollup-pluginutils "^1.5.2" + rollup-plugin-license@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/rollup-plugin-license/-/rollup-plugin-license-0.5.0.tgz#5e707375fb58d29575253a0d28e97e41882682f7" @@ -7765,7 +7630,7 @@ rollup-plugin-license@^0.5.0: mkdirp "0.5.1" moment "2.18.1" -rollup-plugin-node-resolve@^3.0.3: +rollup-plugin-node-resolve@^3.0.2, rollup-plugin-node-resolve@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.0.3.tgz#8f57b253edd00e5b0ad0aed7b7e9cf5982e98fa4" dependencies: @@ -7773,7 +7638,7 @@ rollup-plugin-node-resolve@^3.0.3: is-module "^1.0.0" resolve "^1.1.6" -rollup-pluginutils@^1.5.0: +rollup-pluginutils@^1.5.0, rollup-pluginutils@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz#1e156e778f94b7255bfa1b3d0178be8f5c552408" dependencies: @@ -7833,6 +7698,12 @@ safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, s version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + dependencies: + ret "~0.1.10" + sane@^2.0.0: version "2.4.1" resolved "https://registry.yarnpkg.com/sane/-/sane-2.4.1.tgz#29f991208cf28636720efdc584293e7fd66663a5" @@ -7888,7 +7759,7 @@ semver-diff@^2.0.0: dependencies: semver "^5.0.3" -"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0: +"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" @@ -8144,7 +8015,7 @@ source-map@^0.4.4: dependencies: amdefine ">=0.0.4" -source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1, source-map@~0.5.6: +source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -8160,19 +8031,27 @@ spawn-command@^0.0.2-1: version "0.0.2-1" resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" -spdx-correct@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" +spdx-correct@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" dependencies: - spdx-license-ids "^1.0.2" + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" -spdx-expression-parse@~1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" +spdx-exceptions@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9" -spdx-license-ids@^1.0.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" spdy-transport@^2.0.18: version "2.0.20" @@ -8466,11 +8345,11 @@ symbol-tree@^3.2.2: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" table@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" + version "4.0.3" + resolved "https://registry.yarnpkg.com/table/-/table-4.0.3.tgz#00b5e2b602f1794b9acaf9ca908a76386a7813bc" dependencies: - ajv "^5.2.3" - ajv-keywords "^2.1.0" + ajv "^6.0.1" + ajv-keywords "^3.0.0" chalk "^2.1.0" lodash "^4.17.4" slice-ansi "1.0.0" @@ -8642,12 +8521,13 @@ to-regex-range@^2.1.0: repeat-string "^1.6.1" to-regex@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.1.tgz#15358bee4a2c83bd76377ba1dc049d0f18837aae" + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" dependencies: - define-property "^0.2.5" - extend-shallow "^2.0.1" - regex-not "^1.0.0" + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" touch@^3.1.0: version "3.1.0" @@ -8656,8 +8536,8 @@ touch@^3.1.0: nopt "~1.0.10" tough-cookie@>=2.3.3, tough-cookie@^2.3.3, tough-cookie@~2.3.0, tough-cookie@~2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" + version "2.3.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" dependencies: punycode "^1.4.1" @@ -8722,11 +8602,11 @@ type-check@~0.3.2: prelude-ls "~1.1.2" type-is@~1.6.15: - version "1.6.15" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" + version "1.6.16" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" dependencies: media-typer "0.3.0" - mime-types "~2.1.15" + mime-types "~2.1.18" typedarray@^0.0.6: version "0.0.6" @@ -8765,10 +8645,6 @@ uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" -ultron@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" - unc-path-regex@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" @@ -8842,13 +8718,8 @@ unzip-response@^2.0.1: resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" upath@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.2.tgz#80aaae5395abc5fd402933ae2f58694f0860204c" - dependencies: - lodash.endswith "^4.2.1" - lodash.isfunction "^3.0.8" - lodash.isstring "^4.0.1" - lodash.startswith "^4.2.1" + version "1.0.4" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.4.tgz#ee2321ba0a786c50973db043a50b7bcba822361d" update-notifier@^2.3.0: version "2.3.0" @@ -8943,11 +8814,11 @@ v8flags@^2.0.2: user-home "^1.1.1" validate-npm-package-license@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + version "3.0.3" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338" dependencies: - spdx-correct "~1.0.0" - spdx-expression-parse "~1.0.0" + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" value-equal@^0.4.0: version "0.4.0" @@ -9044,12 +8915,12 @@ watch@~0.18.0: minimist "^1.2.0" watchpack@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.4.0.tgz#4a1472bcbb952bd0a9bb4036801f954dfb39faac" + version "1.5.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.5.0.tgz#231e783af830a22f8966f65c4c4bacc814072eed" dependencies: - async "^2.1.2" - chokidar "^1.7.0" + chokidar "^2.0.2" graceful-fs "^4.1.2" + neo-async "^2.5.0" wbuf@^1.1.0, wbuf@^1.7.2: version "1.7.2" @@ -9273,12 +9144,11 @@ write@^0.2.1: mkdirp "^0.5.1" ws@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-4.0.0.tgz#bfe1da4c08eeb9780b986e0e4d10eccd7345999f" + version "4.1.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-4.1.0.tgz#a979b5d7d4da68bf54efe0408967c324869a7289" dependencies: async-limiter "~1.0.0" safe-buffer "~5.1.0" - ultron "~1.1.0" xdg-basedir@^3.0.0: version "3.0.0"