diff --git a/packages/data-grid/src/__test__/__snapshots__/Table.test.jsx.snap b/packages/data-grid/src/__test__/__snapshots__/Table.test.jsx.snap
index fe37e7e69..202e017f4 100644
--- a/packages/data-grid/src/__test__/__snapshots__/Table.test.jsx.snap
+++ b/packages/data-grid/src/__test__/__snapshots__/Table.test.jsx.snap
@@ -78,7 +78,7 @@ exports[`Snapshot test Check component matches previous HTML snapshot 1`] = `
fixed={false}
fixedColumn={false}
headerCheckState={false}
- headerIndeterminateState={true}
+ headerIndeterminateState={false}
horizontalScroll={false}
loading={false}
loadingMessage="Loading"
@@ -126,7 +126,7 @@ exports[`Snapshot test Check component matches previous HTML snapshot 1`] = `
},
]
}
- headerIndeterminateState={true}
+ headerIndeterminateState={false}
loading={false}
loadingMessage="Loading"
onSelectAll={[Function]}
@@ -443,7 +443,7 @@ exports[`Snapshot test Check component matches previous HTML snapshot 2`] = `
fixed={false}
fixedColumn={false}
headerCheckState={false}
- headerIndeterminateState={true}
+ headerIndeterminateState={false}
horizontalScroll={false}
loading={false}
loadingMessage="Loading"
@@ -491,7 +491,7 @@ exports[`Snapshot test Check component matches previous HTML snapshot 2`] = `
},
]
}
- headerIndeterminateState={true}
+ headerIndeterminateState={false}
loading={false}
loadingMessage="Loading"
onSelectAll={[Function]}
diff --git a/packages/data-grid/src/table/README.md b/packages/data-grid/src/table/README.md
index 9054cceba..fab39bced 100644
--- a/packages/data-grid/src/table/README.md
+++ b/packages/data-grid/src/table/README.md
@@ -4,20 +4,20 @@ The Data Grid package was developed to aid in the structuring of data. By using
To add the Data Grid package to your library run
-```sh
+```markdown
npm install @puppet/data-grid
```
In your .js file which will be rendering the Data Grid Table component you can reference your node modules instance with the following command:
-```jsx
+```markdown
import { Table } from '@puppet/data-grid';
```
In your app level index.scss add the command below to import the Data Grids styles
-```jsx
-@import '~@puppet/data-grid/dist/index';
+```markdown
+import '~@puppet/data-grid/dist/index';
```
### Basic use
@@ -984,3 +984,246 @@ const columns = [
;
```
+
+### Selection and Pagination
+
+Once we start adding multiple patterns together it is very easy to start confusing the end user. Thats why in the data grid when selecting and paginating together we treat each page header click as page specific and use additional badges in the header for cross pagination selecting and clearing. It's important to remember that a column header checkbox is used to show the state of the page visible. Intermediate and check all states on one page should not be shown on the next.
+
+```jsx
+import { Link, Heading } from '@puppet/react-components';
+import { TablePageSelector, TableFooter } from '../index';
+
+const data = [
+ {
+ eventType: 'Application Control',
+ affectedDevices: 0,
+ detections: 1000,
+ sorted: 'asc',
+ Link: Help to fix,
+ unique: 6,
+ },
+ {
+ eventType: 'Virus/Malware',
+ affectedDevices: 20,
+ detections: 634,
+ unique: 1,
+ Link: Help to fix,
+ },
+ {
+ eventType: 'Spyware/Grayware',
+ affectedDevices: 20,
+ detections: 634,
+ Link: Help to fix,
+ unique: 2,
+ },
+ {
+ eventType: 'URL Filtering',
+ affectedDevices: 16,
+ detections: 599,
+ Link: Help to fix,
+ unique: 3,
+ },
+ {
+ eventType: 'Web Reputation',
+ affectedDevices: 15,
+ detections: 598,
+ Link: Help to fix,
+ unique: 4,
+ },
+ {
+ eventType: 'Network Virus',
+ affectedDevices: 15,
+ detections: 497,
+ Link: Help to fix,
+ unique: 5,
+ },
+
+ {
+ eventType: 'Application Controls',
+ affectedDevices: 0,
+ detections: 0,
+ Link: Help to fix,
+ unique: 7,
+ },
+];
+
+const columns = [
+ {
+ label: 'Event Type1',
+ dataKey: 'eventType',
+ },
+ { label: 'Affected Devices', dataKey: 'affectedDevices' },
+
+ { label: 'Detections', dataKey: 'detections' },
+ { label: 'Linked field', dataKey: 'Link' },
+];
+
+class StatefulParent extends React.Component {
+ constructor() {
+ super();
+ this.state = { CurrentPage: 1, checkAll: false, data, indeterminateState: false, showSelectAllBadge: false, Pages: {arrayOfArrays: []}, nodesPerPage: 5 };
+ this.pageSelectFunc = this.pageSelectFunc.bind(this);
+ this.breakIntoMultiplePages = this.breakIntoMultiplePages.bind(this);
+ this.onRowSelected = this.onRowSelected.bind(this);
+ this.onHeaderSelected = this.onHeaderSelected.bind(this);
+ this.onSelectAllBadgeClick = this.onSelectAllBadgeClick.bind(this);
+ this.onClearAllBadgeClick = this.onClearAllBadgeClick.bind(this);
+ }
+
+ componentWillMount() {
+ const dataToPages = this.breakIntoMultiplePages(data, 5)
+ this.setState({Pages: dataToPages})
+ }
+
+ pageSelectFunc(newPage) {
+ const { CurrentPage, Pages } = this.state;
+ this.checkIfIndeterminateState(Pages)
+ this.setState({ CurrentPage: newPage });
+ }
+
+ breakIntoMultiplePages(originalArray, pageSize) {
+ const arrayOfArrays = [];
+ for (let i = 0; i < originalArray.length; i += pageSize) {
+ arrayOfArrays.push(originalArray.slice(i, i + pageSize));
+ }
+ return { arrayOfArrays };
+ }
+
+ checkIfIndeterminateState(Pages) {
+ const { data, indeterminateState, checkAll, CurrentPage } = this.state;
+
+ let selectedOnCurrentPage = Pages.arrayOfArrays[CurrentPage -1].filter(e => e.selected === true);
+ let currentPageLength = Pages.arrayOfArrays[CurrentPage -1].length
+
+ if (selectedOnCurrentPage.length > 0 && indeterminateState === false && checkAll === false) {
+ this.setState({ indeterminateState: true });
+ } else if (
+ (selectedOnCurrentPage.length === 0 && indeterminateState === true) ||
+ (selectedOnCurrentPage.length === 0 && checkAll === true)
+ ) {
+ this.setState({ indeterminateState: false, checkAll: false });
+ }
+ if (selectedOnCurrentPage.length === currentPageLength && checkAll === false) {
+ this.setState({ indeterminateState: false, checkAll: true });
+ }
+ }
+
+ onHeaderSelected(checked) {
+ const { data: stateData, indeterminateState, show, showSelectAllBadge, CurrentPage, Pages } = this.state;
+
+ this.setState({ indeterminateState: false, checkAll: checked });
+
+ const x = Pages.arrayOfArrays[CurrentPage - 1]
+ let newObj = stateData;
+
+ x.forEach((row) => {
+ const y = stateData.findIndex((stateRow) => stateRow.unique === row.unique )
+ const updatedObj = { ...stateData[y], selected: checked };
+ newObj.splice(y,1, updatedObj);
+ })
+
+ this.setState({data: newObj})
+
+ }
+
+
+ onRowSelected(checked, row) {
+ const { data: stateData, checkAll, Pages } = this.state;
+
+ if (checkAll) {
+ this.setState({ checkAll: false });
+ }
+ // find the index of object from array that you want to update
+ const objIndex = stateData.findIndex(obj => obj.unique === row.unique);
+ // make new object of updated object.
+ const updatedObj = { ...stateData[objIndex], selected: checked };
+ // make final new array of objects by combining updated object.
+ const updatedData = [
+ ...stateData.slice(0, objIndex),
+ updatedObj,
+ ...stateData.slice(objIndex + 1),
+ ];
+
+ this.checkIfIndeterminateState(Pages)
+
+ this.setState({ data: updatedData });
+ }
+
+ onSelectAllBadgeClick(){
+ const { data } = this.state;
+ const x = data
+ for (let i = 0; i < x.length; i += 1) {
+ x[i].selected = true;
+ }
+ this.setState({data: x})
+ }
+
+ onClearAllBadgeClick(){
+ const { data } = this.state;
+ const x = data
+ for (let i = 0; i < x.length; i += 1) {
+ x[i].selected = false;
+ }
+ this.setState({data: x})
+ }
+
+ render() {
+ const {
+ CurrentPage,
+ data:stateData,
+ indeterminateState,
+ checkAll: headerCheckboxState,
+ nodesPerPage
+ } = this.state;
+
+ const Pages = this.breakIntoMultiplePages(stateData, 5)
+ const PageLength = Pages.arrayOfArrays[CurrentPage -1].length
+ const PageCount = Pages.arrayOfArrays.length;
+ const renderPages = CurrentPage - 1;
+ const currentNode = `${nodesPerPage * CurrentPage}`;
+ const tableFooterText = `${currentNode -
+ PageLength +
+ 1} - ${currentNode} of ${stateData.length} nodes`;
+ const selectAllBadgeText = `Select all ${stateData.length} nodes`
+
+ this.checkIfIndeterminateState(Pages);
+ const selectedCount = stateData.filter(obj => obj.selected === true).length
+
+ let rowCountText = `${stateData.length} nodes`
+ if( selectedCount > 0 ){
+ rowCountText = `${selectedCount} of ${stateData.length} nodes selected`
+ }
+
+ return (
+
+ );
+ }
+}
+;
+```
\ No newline at end of file
diff --git a/packages/data-grid/src/table/Table.jsx b/packages/data-grid/src/table/Table.jsx
index 5b81d946a..4b28d6856 100644
--- a/packages/data-grid/src/table/Table.jsx
+++ b/packages/data-grid/src/table/Table.jsx
@@ -106,7 +106,7 @@ const defaultProps = {
onHeaderChecked: () => {},
onRowClick: () => {},
headerCheckState: false,
- headerIndeterminateState: true,
+ headerIndeterminateState: false,
};
const defaultColumnDefs = {
diff --git a/packages/data-grid/src/tableHeader/TableHeader.jsx b/packages/data-grid/src/tableHeader/TableHeader.jsx
index 3d040e65c..7e4e0ebbc 100644
--- a/packages/data-grid/src/tableHeader/TableHeader.jsx
+++ b/packages/data-grid/src/tableHeader/TableHeader.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import { string, arrayOf, shape, node, func, bool } from 'prop-types';
-import { Text, Input } from '@puppet/react-components';
+import { Text, Input, ButtonSelect, Badge } from '@puppet/react-components';
import QuickFilter from '../quickFilter';
import TagBuilder from '../tagBuilder';
import './TableHeader.scss';
@@ -12,7 +12,7 @@ const propTypes = {
selectedRowCountText: string,
/** Allows children to be rendered within the tableheader */
children: node,
- /** Boolean value that determines if the seach box should be rendered */
+ /** Boolean value that determines if the search box should be rendered */
search: bool,
/** String shown within blank input box */
searchPlaceholder: string,
@@ -52,8 +52,35 @@ const propTypes = {
value: string,
}),
),
+ /** Callback function called when the tag close button is clicked */
onRemoveAll: func,
+ /** Callback function called when the tags x button is clicked */
onRemoveTag: func,
+ /** Actions are the possible selections that a user can pick from under a certain field */
+ actions: arrayOf({
+ /** Is the value returned after a users selection for a dataset to be filter by */
+ value: string,
+ /** Should you wish to add an icon to a specific row */
+ icon: string,
+ /** Text which will be displayed for each option */
+ label: string,
+ }),
+ /** String shown as action button select */
+ actionLabel: string,
+ /** Callback function called when an action is selected from the dropdown list */
+ onActionSelect: func,
+ /** Boolean used to conditionally render the showSelectAllBadge */
+ showSelectAllBadge: bool,
+ /** Text shown in the selectAllBadge */
+ selectAllBadgeText: string,
+ /** Callback function called when the selectAllBadge is clicked */
+ onSelectAllBadgeClick: func,
+ /** Boolean used to conditionally render the showClearAllBadge */
+ showClearAllBadge: bool,
+ /** Text shown in the clearAllBadge */
+ clearAllBadgeText: string,
+ /** Callback function called when the clearAllBadgeClick is clicked */
+ onClearAllBadgeClick: func,
};
const defaultProps = {
@@ -69,6 +96,15 @@ const defaultProps = {
activeFilters: [],
onRemoveAll: () => {},
onRemoveTag: () => {},
+ actions: [],
+ actionLabel: 'Actions',
+ onActionSelect: () => {},
+ showSelectAllBadge: false,
+ selectAllBadgeText: 'Select all *** nodes',
+ onSelectAllBadgeClick: () => {},
+ showClearAllBadge: false,
+ clearAllBadgeText: 'Clear selection',
+ onClearAllBadgeClick: () => {},
};
function TableHeader({
@@ -84,6 +120,15 @@ function TableHeader({
onFilterChange,
onRemoveAll,
onRemoveTag,
+ actions,
+ actionLabel,
+ onActionSelect,
+ showSelectAllBadge,
+ selectAllBadgeText,
+ onSelectAllBadgeClick,
+ showClearAllBadge,
+ clearAllBadgeText,
+ onClearAllBadgeClick,
}) {
return (