From 6d127141632fb86d0209c35f0713c8c3d7b08b15 Mon Sep 17 00:00:00 2001 From: Joel Armendariz Date: Thu, 20 Aug 2020 12:51:21 -0500 Subject: [PATCH 1/5] feat(table): add multiselect option for filtering --- src/components/Table/Table.jsx | 7 +- src/components/Table/Table.story.jsx | 46 + .../FilterHeaderRow/FilterHeaderRow.jsx | 105 +- .../FilterHeaderRow/_filter-header-row.scss | 9 + src/components/Table/TableHead/TableHead.jsx | 8 +- src/components/Table/TablePropTypes.js | 1 + .../TableViewDropdown.story.storyshot | 16 +- .../Table/__snapshots__/Table.story.storyshot | 4774 ++++++++++++++++- src/components/Table/tableReducer.js | 14 +- src/components/Table/tableReducer.test.jsx | 7 + .../__snapshots__/TableCard.story.storyshot | 180 +- .../TimeSeriesCard.story.storyshot | 10 +- .../__snapshots__/publicAPI.test.js.snap | 25 + 13 files changed, 4849 insertions(+), 353 deletions(-) diff --git a/src/components/Table/Table.jsx b/src/components/Table/Table.jsx index d25d7a3fc4..aa5c6609e5 100644 --- a/src/components/Table/Table.jsx +++ b/src/components/Table/Table.jsx @@ -85,7 +85,12 @@ const propTypes = { filters: PropTypes.arrayOf( PropTypes.shape({ columnId: PropTypes.string.isRequired, - value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]).isRequired, + value: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + PropTypes.bool, + PropTypes.arrayOf(PropTypes.string), + ]).isRequired, }) ), toolbar: PropTypes.shape({ diff --git a/src/components/Table/Table.story.jsx b/src/components/Table/Table.story.jsx index 2dab8a32a7..8d0fc15772 100644 --- a/src/components/Table/Table.story.jsx +++ b/src/components/Table/Table.story.jsx @@ -680,6 +680,52 @@ storiesOf('Watson IoT/Table', module) }, } ) + .add( + 'Stateful Example with multiselect filtering', + () => ( + + { + if (column.filter) { + return { + ...column, + filter: { ...column.filter, isMultiselect: !!column.filter?.options }, + }; + } + return column; + })} + view={{ + ...initialState.view, + pagination: { + ...initialState.view.pagination, + maxPages: 5, + }, + toolbar: { + activeBar: 'filter', + }, + }} + secondaryTitle={text('Secondary Title', `Row count: ${initialState.data.length}`)} + actions={actions} + isSortable + lightweight={boolean('lightweight', false)} + options={{ + ...initialState.options, + hasFilter: select('hasFilter', ['onKeyPress', 'onEnterAndBlur'], 'onKeyPress'), + wrapCellText: select('wrapCellText', selectTextWrapping, 'always'), + hasSingleRowEdit: true, + }} + /> + + ), + { + info: { + text: `This table has a multiselect filter. To support multiselect filtering, make sure to pass isMultiselect: true to the filter prop on the table.`, + propTables: [Table], + propTablesExclude: [StatefulTable], + }, + } + ) .add( 'Stateful Example with row nesting and fixed columns', () => , diff --git a/src/components/Table/TableHead/FilterHeaderRow/FilterHeaderRow.jsx b/src/components/Table/TableHead/FilterHeaderRow/FilterHeaderRow.jsx index 114aeb8513..dd43bc454c 100644 --- a/src/components/Table/TableHead/FilterHeaderRow/FilterHeaderRow.jsx +++ b/src/components/Table/TableHead/FilterHeaderRow/FilterHeaderRow.jsx @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { ComboBox, DataTable, FormItem, TextInput } from 'carbon-components-react'; +import { ComboBox, DataTable, FormItem, TextInput, MultiSelect } from 'carbon-components-react'; import { Close16 } from '@carbon/icons-react'; import memoize from 'lodash/memoize'; import classnames from 'classnames'; @@ -30,6 +30,8 @@ class FilterHeaderRow extends Component { text: PropTypes.string.isRequired, }) ), + /** if isMultiselect and isFilterable are true, the table is filtered based on a multiselect */ + isMultiselect: PropTypes.bool, }) ).isRequired, /** internationalized string */ @@ -48,7 +50,12 @@ class FilterHeaderRow extends Component { filters: PropTypes.arrayOf( PropTypes.shape({ columnId: PropTypes.string.isRequired, - value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]).isRequired, + value: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + PropTypes.bool, + PropTypes.arrayOf(PropTypes.string), + ]).isRequired, }) ), /** Callback when filter is applied sends object of keys and values with the filter values */ @@ -192,35 +199,71 @@ class FilterHeaderRow extends Component { column.isFilterable !== undefined && !column.isFilterable ? (
) : column.options ? ( - (item ? item.text : '')} - initialSelectedItem={{ - id: columnStateValue, - text: ( - column.options.find(option => option.id === columnStateValue) || { text: '' } - ).text, // eslint-disable-line react/destructuring-assignment - }} - placeholder={column.placeholderText || 'Choose an option'} - onChange={evt => { - this.setState( - state => ({ - filterValues: { - ...state.filterValues, - [column.id]: evt.selectedItem === null ? '' : evt.selectedItem.id, - }, - }), - this.handleApplyFilter - ); - }} - light={lightweight} - disabled={isDisabled} - /> + column.isMultiselect ? ( + (item ? item.text : '')} + initialSelectedItems={ + Array.isArray(columnStateValue) + ? columnStateValue.map(value => + typeof value !== 'object' ? { id: value, text: value } : value + ) + : [{ id: columnStateValue, text: columnStateValue }] + } + placeholder={column.placeholderText || 'Choose an option'} + onChange={evt => { + this.setState( + state => ({ + filterValues: { + ...state.filterValues, + [column.id]: evt.selectedItems.map(item => item.text), + }, + }), + this.handleApplyFilter + ); + }} + light + disabled={isDisabled} + /> + ) : ( + (item ? item.text : '')} + initialSelectedItem={{ + id: columnStateValue, + text: ( + column.options.find(option => option.id === columnStateValue) || { + text: '', + } + ).text, // eslint-disable-line react/destructuring-assignment + }} + placeholder={column.placeholderText || 'Choose an option'} + onChange={evt => { + this.setState( + state => ({ + filterValues: { + ...state.filterValues, + [column.id]: evt.selectedItem === null ? '' : evt.selectedItem.id, + }, + }), + this.handleApplyFilter + ); + }} + light={lightweight} + disabled={isDisabled} + /> + ) ) : ( svg { + fill: #fff; + } + } + .#{$prefix}--tag--filter { background-color: transparent; diff --git a/src/components/Table/TableHead/TableHead.jsx b/src/components/Table/TableHead/TableHead.jsx index f102232628..36f84ebd34 100644 --- a/src/components/Table/TableHead/TableHead.jsx +++ b/src/components/Table/TableHead/TableHead.jsx @@ -86,7 +86,12 @@ const propTypes = { filters: PropTypes.arrayOf( PropTypes.shape({ columnId: PropTypes.string.isRequired, - value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]).isRequired, + value: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.number, + PropTypes.bool, + PropTypes.arrayOf(PropTypes.string), + ]).isRequired, }) ), }).isRequired, @@ -379,6 +384,7 @@ const TableHead = ({ ...column.filter, id: column.id, isFilterable: !isNil(column.filter), + isMultiselect: column.filter?.isMultiselect, width: column.width, }))} hasFastFilter={hasFastFilter} diff --git a/src/components/Table/TablePropTypes.js b/src/components/Table/TablePropTypes.js index 937e9c4348..6e72aebd94 100644 --- a/src/components/Table/TablePropTypes.js +++ b/src/components/Table/TablePropTypes.js @@ -112,6 +112,7 @@ export const TableColumnsPropTypes = PropTypes.arrayOf( filter: PropTypes.shape({ /** I18N text for the filter */ placeholderText: PropTypes.string, + isMultiselect: PropTypes.bool, options: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]).isRequired, diff --git a/src/components/Table/TableViewDropdown/__snapshots__/TableViewDropdown.story.storyshot b/src/components/Table/TableViewDropdown/__snapshots__/TableViewDropdown.story.storyshot index 81ff3450c0..9f1d25db27 100644 --- a/src/components/Table/TableViewDropdown/__snapshots__/TableViewDropdown.story.storyshot +++ b/src/components/Table/TableViewDropdown/__snapshots__/TableViewDropdown.story.storyshot @@ -47,7 +47,7 @@ exports[`Storybook Snapshot tests and console checks Storyshots Watson IoT/Table >
@@ -2673,7 +2673,7 @@ exports[`Storybook Snapshot tests and console checks Storyshots Watson IoT/Table > @@ -3456,7 +3456,7 @@ exports[`Storybook Snapshot tests and console checks Storyshots Watson IoT/Table >
@@ -5440,7 +5440,7 @@ exports[`Storybook Snapshot tests and console checks Storyshots Watson IoT/Table > @@ -91836,13 +91836,4371 @@ exports[`Storybook Snapshot tests and console checks Storyshots Watson IoT/Table + + + Date + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+
+ + + +
+
+
+ + + +
+
+ +
+ +
+
+
+
+ + + +
+
+
+ +
+ + + +
+
+ + + + Open menu + + +
+
+
+
+
+ option-A +
+
+
+
+ option-B +
+
+
+
+ option-C +
+
+
+
+
+
+ + + +
+ + + + +
+
+ +
+ +
+
+
+
+ + + +
+
+ +
+ +
+
+
+
+ + + +
+ + + + + + + + + + + + + + +
+ + +
+
+ + + + + helping whiteboard as 1 + + + + + + + 1973-03-14T23:33:20.000Z + + + + + + + option-B + + + + + + + + + + + + + + + + 1 + + + + + + + false + + + + + + + + + + + +
+
+ + +
+
+ + + + + + + + +
+ + +
+
+ + + + + can pinocchio whiteboard 4 + + + + + + + 1973-09-04T14:13:20.000Z + + + + + + + option-B + + + + + + + + + + + + + + + + 16 + + + + + + + true + + + + + + + + + + + +
+
+ +
+
+ + + + + + + + +
+ + +
+
+ + + + + eat whiteboard pinocchio 16 + + + + + + + 1981-04-13T08:53:20.000Z + + + + + + + option-B + + + + + + + + + + + + + + + + 256 + + + + + + + true + + + + + + + + + + + +
+
+ +
+
+ + + + + + + + +
+ + +
+
+ + + + + whiteboard can eat 22 + + + + + + + 1988-07-04T06:13:20.000Z + + + + + + + option-B + + + + + + + + + + + + + + + + 484 + + + + + + + true + + + + + + + + + + + +
+
+ + +
+
+ + + + + + + + +
+ + +
+
+ + + + + helping whiteboard as 31 + + + + + + + 2003-08-16T02:13:20.000Z + + + + + + + option-B + + + + + + + + + + + + + + + + 961 + + + + + + + false + + + + + + + + + + + +
+
+ + +
+
+ + + + + + + + +
+ + +
+
+ + + + + can pinocchio whiteboard 34 + + + + + + + 2009-10-20T00:53:20.000Z + + + + + + + option-B + + + + + + + + + + + + + + + + 1156 + + + + + + + true + + + + + + + + + + + +
+
+ + +
+
+ + + + + + + + +
+ + +
+
+ + + + + eat whiteboard pinocchio 46 + + + + + + + 2040-03-22T03:33:20.000Z + + + + + + + option-B + + + + + + + + + + + + + + + + 2116 + + + + + + + true + + + + + + + + + + + +
+
+ + +
+
+ + + + + + + + +
+ + +
+
+ + + + + whiteboard can eat 52 + + + + + + + 2058-11-08T16:53:20.000Z + + + + + + + option-B + + + + + + + + + + + + + + + + 2704 + + + + + + + true + + + + + + + + + + + +
+
+ +
+
+ + + + + + + + +
+ + +
+
+ + + + + helping whiteboard as 61 + + + + + + + 2091-01-30T12:53:20.000Z + + + + + + + option-B + + + + + + + + + + + + + + + + 3721 + + + + + + + false + + + + + + + + + + + +
+
+ + +
+
+ + + + + + + + +
+ + +
+
+ + + + + can pinocchio whiteboard 64 + + + + + + + 2102-12-19T19:33:20.000Z + + + + + + + option-B + + + + + + + + + + + + + + + + 4096 + + + + + + + true + + + + + + + + + + + +
+
+ +
+
+ + + + +
+
+
+ +
+
+
+
+ + + + +
+
+
+
+ + 1–10 of 14 items + +
+
+
+
+ +
+
+ + + + +
+
+
+
+ + 1 of 2 pages + + + +
+
+
+
+
+ +
+`; + +exports[`Storybook Snapshot tests and console checks Storyshots Watson IoT/Table Stateful Example with multiselect filtering 1`] = ` +
+
+
+
+
+
+
+ +
+
+

+ + 0 item selected + +

+
+
+ +
+
+
+ + + + + + +
+
+ + + + +
+
+
+ + + + + +
+ + +
+ + +
+
+
+ + @@ -91855,32 +96213,21 @@ exports[`Storybook Snapshot tests and console checks Storyshots Watson IoT/Table > Date -