From e763c1a8544ef50a34cbf741abba8b21165ce194 Mon Sep 17 00:00:00 2001 From: MaximDrobchak <47354306+MaksimDrobchak@users.noreply.github.com> Date: Sun, 19 May 2019 18:10:22 +0300 Subject: [PATCH] Locked_items_right_section (#148) * support locked items in destination list * use locked function instead of item.disabled * added README & story * migration to isLocked * migration to isLocked * migration to isLocked * added test coverage * corected climatcode * corected climatcode * corected climatcode --- README.md | 17 ++- src/components/destination_list.js | 7 +- src/components/items/selected_item.js | 15 +- src/components/items/selected_item.scss | 6 + src/components/list/items_list.js | 7 +- src/components/list/list.js | 11 +- src/components/multi_select.js | 8 +- src/components/multi_select_state.js | 105 +++++++------ src/components/multi_select_state_utils.js | 25 +++- src/components/source_list.js | 7 +- stories/multi-select.stories.js | 50 +++++++ .../destination_list.spec.js.snap | 11 ++ .../__snapshots__/multi_select.spec.js.snap | 72 +++++++++ .../__snapshots__/source_list.spec.js.snap | 18 +++ .../__snapshots__/selected_item.spec.js.snap | 18 +++ tests/components/items/selected_item.spec.js | 8 + tests/components/list/list.spec.js | 73 ++++++++- tests/components/multi_select_state.spec.js | 140 ++++++++++++++++++ .../multi_select_state_utils.spec.js | 5 +- 19 files changed, 520 insertions(+), 83 deletions(-) diff --git a/README.md b/README.md index 90448f9..17564e5 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ class Example extends Component { | `searchSelectedItemsValue` | `string` | | The value of the search field for destination list. | | `searchSelectedItemsChanged` | `function` | | Function to handle the change of search field for destination list. Accepts value as a single argument. | | `selectedItemsFilterFunction` | `function` | based on label | Is the same as filterFunction by default to filter items based on the search query in destination list. | - +| `isLocked` | `function` | `item => item.disabled` | Function to be used to define whether item is locked or not| ## Customization @@ -168,6 +168,21 @@ The `SelectedItem` component receives the following props: `height` - receives the height defined by the list + +You can disable specific selected items by passing `item.disabled: true` or pass `isLocked` function which will be used to define whether the item is disabled. + +Example (selected & disabled): +```javascript +function Exemple(){ + const items = [{id:0,label: 'item 0'}, {id:1,label: 'item 1'}]; + return ( + item.label === 'item 0'} + items={items} + selectedItems={items} + /> +} +```
**Search** diff --git a/src/components/destination_list.js b/src/components/destination_list.js index aac34be..5649f71 100644 --- a/src/components/destination_list.js +++ b/src/components/destination_list.js @@ -22,7 +22,8 @@ const DestinationList = ({ noItemsRenderer, withGrouping, filteredItems, - children + children, + isLocked }) => { const SelectionStatusRenderer = selectionStatusRenderer; const updatedSelectedItems = withGrouping @@ -47,6 +48,7 @@ const DestinationList = ({ renderer={selectedItemRenderer} noItemsRenderer={noItemsRenderer} noItemsMessage={messages.noItemsMessage} + isLocked={isLocked} /> ); @@ -64,7 +66,8 @@ DestinationList.propTypes = { noItemsRenderer: PropTypes.any, withGrouping: PropTypes.bool, filteredItems: PropTypes.arrayOf(PropTypes.object), - children: PropTypes.node + children: PropTypes.node, + isLocked: PropTypes.func }; DestinationList.defaultProps = { diff --git a/src/components/items/selected_item.js b/src/components/items/selected_item.js index 9d586e8..ac1ed1c 100644 --- a/src/components/items/selected_item.js +++ b/src/components/items/selected_item.js @@ -3,16 +3,20 @@ import PropTypes from "prop-types"; import CloseIcon from "react-icons/lib/md/close"; import IconButton from "@material-ui/core/IconButton"; import ItemLabel from "./item_label"; - +import classnames from "classnames"; import styles from "./selected_item.scss"; -const SelectedItem = ({ item, height, group }) => ( +const SelectedItem = ({ item, height, group, disabled }) => (
- {!group && ( + {!group && !disabled && ( @@ -22,7 +26,8 @@ const SelectedItem = ({ item, height, group }) => ( SelectedItem.propTypes = { item: PropTypes.object, - height: PropTypes.number + height: PropTypes.number, + isLocked: PropTypes.func }; SelectedItem.defaultProps = { diff --git a/src/components/items/selected_item.scss b/src/components/items/selected_item.scss index 008de28..a40667f 100644 --- a/src/components/items/selected_item.scss +++ b/src/components/items/selected_item.scss @@ -18,3 +18,9 @@ pointer-events: none; color: $color-black; } + +.disabled { + color: $disabled-color; + background-color: $selected-background-color; + cursor: default; +} diff --git a/src/components/list/items_list.js b/src/components/list/items_list.js index 708d5c3..9596647 100644 --- a/src/components/list/items_list.js +++ b/src/components/list/items_list.js @@ -16,7 +16,8 @@ class ItemsList extends PureComponent { selectedIds: PropTypes.arrayOf(PropTypes.number), items: PropTypes.array, disabled: PropTypes.bool, - disabledItemsTooltip: PropTypes.string + disabledItemsTooltip: PropTypes.string, + isLocked: PropTypes.func }; static defaultProps = { @@ -61,7 +62,8 @@ class ItemsList extends PureComponent { selectedIds, onClick, disabled, - disabledItemsTooltip + disabledItemsTooltip, + isLocked } = this.props; return ( @@ -84,6 +86,7 @@ class ItemsList extends PureComponent { selectedIds={selectedIds} disabled={disabled} disabledItemsTooltip={disabledItemsTooltip} + isLocked={isLocked} /> )} diff --git a/src/components/list/list.js b/src/components/list/list.js index d28c5bd..6660594 100644 --- a/src/components/list/list.js +++ b/src/components/list/list.js @@ -18,7 +18,8 @@ class InnerList extends PureComponent { selectedIds: PropTypes.arrayOf(PropTypes.number), items: PropTypes.array, disabled: PropTypes.bool, - disabledItemsTooltip: PropTypes.string + disabledItemsTooltip: PropTypes.string, + isLocked: PropTypes.func }; static defaultProps = { @@ -55,12 +56,14 @@ class InnerList extends PureComponent { onClick, items, selectedIds, - disabledItemsTooltip + disabledItemsTooltip, + isLocked } = this.props; const Renderer = renderer; const item = items[index]; const checked = selectedIds.includes(item.id); - const disabled = this.props.disabled || item.disabled; + const disabled = (this.props.disabled && !checked) || isLocked(item); + return (
); diff --git a/src/components/multi_select.js b/src/components/multi_select.js index c89b584..de411c6 100644 --- a/src/components/multi_select.js +++ b/src/components/multi_select.js @@ -39,7 +39,8 @@ export class MultiSelect extends PureComponent { withGrouping: PropTypes.bool, listRenderer: PropTypes.func, generateClassName: PropTypes.func, - showSelectedItemsSearch: PropTypes.bool + showSelectedItemsSearch: PropTypes.bool, + isLocked: PropTypes.func }; static defaultProps = { @@ -110,7 +111,8 @@ export class MultiSelect extends PureComponent { showSelectedItemsSearch, searchSelectedItemsValue, filterSelectedItems, - filteredSelectedItems + filteredSelectedItems, + isLocked } = this.props; const calculatedHeight = this.calculateHeight(); const selectedIds = selectedItems.map(item => item.id); @@ -148,6 +150,7 @@ export class MultiSelect extends PureComponent { selectAllHeight={selectAllHeight} withGrouping={withGrouping} listRenderer={listRenderer} + isLocked={isLocked} /> )} {!loading && showSelectedItems && ( @@ -167,6 +170,7 @@ export class MultiSelect extends PureComponent { filteredItems={filteredSelectedItems} searchIcon={searchIcon} filterItems={filterSelectedItems} + isLocked={isLocked} /> )}
diff --git a/src/components/multi_select_state.js b/src/components/multi_select_state.js index 6bb33d5..5b8d513 100644 --- a/src/components/multi_select_state.js +++ b/src/components/multi_select_state.js @@ -1,8 +1,11 @@ import React, { PureComponent } from "react"; import { getSelectedByAllItems, - filterByIds, - findItem + filterUnselectedByIds, + findItem, + getMinMaxIndexes, + isWithin, + getNewSelectedItems } from "./multi_select_state_utils"; const withMultiSelectState = WrappedComponent => @@ -13,7 +16,8 @@ const withMultiSelectState = WrappedComponent => .toLowerCase() .includes(value.toLowerCase()), items: [], - selectedItems: [] + selectedItems: [], + isLocked: item => item.disabled }; constructor(props) { @@ -48,10 +52,8 @@ const withMultiSelectState = WrappedComponent => this.setState({ selectedItems: nextProps.selectedItems }); } if (this.props.items !== nextProps.items) { - this.setState({ - items: nextProps.items, - filteredItems: nextProps.items - }); + const { items } = nextProps; + this.setState({ items, filteredItems: items }); } if (searchValue !== nextProps.searchValue) { this.filterItems({ target: { value: nextProps.searchValue } }); @@ -64,16 +66,16 @@ const withMultiSelectState = WrappedComponent => } handleMultiSelection(index) { - const { items } = this.props; - const { selectedItems, filteredItems } = this.state; + const { items, isLocked } = this.props; + const { filteredItems, firstItemShiftSelected } = this.state; - const { minIndex, maxIndex } = this.getMinMaxIndexes(index); + const interval = getMinMaxIndexes(index, firstItemShiftSelected); const newSelectedItems = items.filter( (item, index) => - (index >= minIndex && - index <= maxIndex && + (isWithin(index, interval) && + !isLocked(item) && findItem(item, filteredItems)) || - findItem(item, selectedItems) + findItem(item, this.state.selectedItems) ); const newFilteredSelectedItems = this.getNewFilteredSelectedItems( newSelectedItems @@ -87,25 +89,6 @@ const withMultiSelectState = WrappedComponent => ); } - getMinMaxIndexes(currentIndex) { - const { firstItemShiftSelected } = this.state; - return firstItemShiftSelected > currentIndex - ? { minIndex: currentIndex, maxIndex: firstItemShiftSelected } - : { minIndex: firstItemShiftSelected, maxIndex: currentIndex }; - } - - getNewSelectedItems(itemId) { - const { items } = this.props; - const { selectedItems } = this.state; - const sourceItems = items.filter( - item => item.id === itemId || findItem(item, selectedItems) - ); - const destinationItems = selectedItems.filter( - selectedItem => !findItem(selectedItem, items) - ); - return [...destinationItems, ...sourceItems]; - } - getNewFilteredSelectedItems(items) { const { searchSelectedItemsValue } = this.props; @@ -140,27 +123,39 @@ const withMultiSelectState = WrappedComponent => const index = items.findIndex(item => item.id === id); this.setState({ firstItemShiftSelected: index }); } - const newSelectedItems = this.getNewSelectedItems(id); - const newFilteredSelectedItems = this.getNewFilteredSelectedItems( - newSelectedItems - ); - this.setState( - { - selectedItems: newSelectedItems, - filteredSelectedItems: newFilteredSelectedItems - }, - this.handleChange - ); + this.setNewItemsBySelectItem(id, items, selectedItems); } } else { this.unselectItems([id]); } } + setNewItemsBySelectItem(id, items, selectedItems) { + const newSelectedItems = getNewSelectedItems(id, items, selectedItems); + const newFilteredSelectedItems = this.getNewFilteredSelectedItems( + newSelectedItems + ); + this.setState( + { + selectedItems: newSelectedItems, + filteredSelectedItems: newFilteredSelectedItems + }, + this.handleChange + ); + } unselectItems(ids) { const { selectedItems, filteredSelectedItems } = this.state; - const newSelectedItems = filterByIds(selectedItems, ids); - const newFilteredSelectedItems = filterByIds(filteredSelectedItems, ids); + const { isLocked } = this.props; + const newSelectedItems = filterUnselectedByIds( + selectedItems, + ids, + isLocked + ); + const newFilteredSelectedItems = filterUnselectedByIds( + filteredSelectedItems, + ids, + isLocked + ); this.setState( { selectedItems: newSelectedItems, @@ -171,8 +166,13 @@ const withMultiSelectState = WrappedComponent => } clearAll() { + const { selectedItems, isLocked } = this.props; + const lockedItems = selectedItems.filter(isLocked); this.setState( - { selectedItems: [], filteredSelectedItems: [] }, + { + selectedItems: lockedItems, + filteredSelectedItems: lockedItems + }, this.handleChange ); } @@ -180,10 +180,7 @@ const withMultiSelectState = WrappedComponent => filterItems(event) { const { items, filterFunction, searchValueChanged } = this.props; const { value } = event.target; - this.setState({ - filteredItems: items.filter(filterFunction(value)) - }); - + this.setState({ filteredItems: items.filter(filterFunction(value)) }); searchValueChanged && searchValueChanged(value); } @@ -225,11 +222,13 @@ const withMultiSelectState = WrappedComponent => isAllSelected() { const { filteredItems, selectedItems } = this.state; - const selectedItemsInFilteredItems = selectedItems.filter(selectedItem => - findItem(selectedItem, filteredItems) + const { isLocked } = this.props; + const selectedItemsInFilteredItems = selectedItems.filter( + selectedItem => + !isLocked(selectedItem) && findItem(selectedItem, filteredItems) ); const selectableFilteredItems = filteredItems.filter( - item => !item.disabled + item => !isLocked(item) ); return ( selectedItemsInFilteredItems.length === diff --git a/src/components/multi_select_state_utils.js b/src/components/multi_select_state_utils.js index f2e85ad..56f9f2d 100644 --- a/src/components/multi_select_state_utils.js +++ b/src/components/multi_select_state_utils.js @@ -15,10 +15,11 @@ const getDestinationItems = (itemsToSelect, selectedItems, items) => !findItem(selectedItem, itemsToSelect) && !findItem(selectedItem, items) ); -const containsId = (ids, item) => ids.find(id => id === item.id) === undefined; +const notContainsId = (ids, item) => + ids.find(id => id === item.id) === undefined; -export const filterByIds = (items, ids) => - items.filter(item => containsId(ids, item)); +export const filterUnselectedByIds = (items, ids, isLocked) => + items.filter(item => notContainsId(ids, item) || isLocked(item)); export const getSelectedByAllItems = (itemsToSelect, selectedItems, items) => { const sourceItems = getSourceItems(itemsToSelect, selectedItems, items); @@ -30,3 +31,21 @@ export const getSelectedByAllItems = (itemsToSelect, selectedItems, items) => { return [...destinationItems, ...sourceItems]; }; + +export const getMinMaxIndexes = (currentIndex, firstItemShiftSelected) => + firstItemShiftSelected > currentIndex + ? { minIndex: currentIndex, maxIndex: firstItemShiftSelected } + : { minIndex: firstItemShiftSelected, maxIndex: currentIndex }; + +export const isWithin = (index, { minIndex, maxIndex }) => + index >= minIndex && index <= maxIndex; + +export const getNewSelectedItems = (itemId, items, selectedItems) => { + const sourceItems = items.filter( + item => item.id === itemId || findItem(item, selectedItems) + ); + const destinationItems = selectedItems.filter( + selectedItem => !findItem(selectedItem, items) + ); + return [...destinationItems, ...sourceItems]; +}; diff --git a/src/components/source_list.js b/src/components/source_list.js index 525dbb0..a3f7468 100644 --- a/src/components/source_list.js +++ b/src/components/source_list.js @@ -28,7 +28,8 @@ const SourceList = ({ disabled, withGrouping, listRenderer, - children + children, + isLocked }) => { const SelectAllRenderer = selectAllRenderer; const updatedFilteredItems = withGrouping @@ -61,6 +62,7 @@ const SourceList = ({ noItemsMessage={messages.noItemsMessage} disabled={disabled} disabledItemsTooltip={messages.disabledItemsTooltip} + isLocked={isLocked} /> ); @@ -85,7 +87,8 @@ SourceList.propTypes = { selectItem: PropTypes.func, disabled: PropTypes.bool, withGrouping: PropTypes.bool, - listRenderer: PropTypes.func + listRenderer: PropTypes.func, + isLocked: PropTypes.func }; SourceList.defaultProps = { diff --git a/stories/multi-select.stories.js b/stories/multi-select.stories.js index 94dbfd2..9d67544 100644 --- a/stories/multi-select.stories.js +++ b/stories/multi-select.stories.js @@ -307,4 +307,54 @@ storiesOf("React Multi Select", module) return ; }) + ) + .add( + "With some of the locked items", + withReadme(Readme, () => { + const disabledItem = [ + { id: 0, label: "item 0", disabled: true }, + { id: 5, label: "item 5", disabled: true } + ]; + + return ( + item.disabled} + loading={boolean("Loading", false)} + onChange={action("onChange")} + showSearch={boolean("Show search", true)} + showSelectAll={boolean("Show select all", true)} + messages={{ disabledItemsTooltip: "You can select up to 4 items" }} + /> + ); + }) + ) + .add( + "With some of the locked items selected & unselected", + withReadme(Readme, () => { + const disabledItem = [ + { id: 2, label: "item 2", disabled: true }, + { id: 5, label: "item 5", disabled: true } + ]; + const items = [ + { id: 1, label: "item 1" }, + ...disabledItem, + { id: 3, label: "item 3" } + ]; + return ( + item.disabled} + loading={boolean("Loading", false)} + onChange={action("onChange")} + showSearch={boolean("Show search", true)} + showSelectAll={boolean("Show select all", true)} + messages={{ disabledItemsTooltip: "You can select up to 4 items" }} + /> + ); + }) ); diff --git a/tests/components/__snapshots__/destination_list.spec.js.snap b/tests/components/__snapshots__/destination_list.spec.js.snap index b7abac6..450303c 100644 --- a/tests/components/__snapshots__/destination_list.spec.js.snap +++ b/tests/components/__snapshots__/destination_list.spec.js.snap @@ -61,6 +61,7 @@ exports[`DestinationList custom messages 1`] = ` @@ -4373,6 +4421,7 @@ exports[`MultiSelect Snapshots without responsiveHeight 1`] = ` <_class filterFunction={[Function]} height={400} + isLocked={[Function]} items={Array []} selectedItems={Array []} /> @@ -4387,6 +4436,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4415,6 +4465,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4443,6 +4494,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4471,6 +4523,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [MockFunction mockedComponent], "listRenderer": [Function], @@ -4499,6 +4552,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [MockFunction mockedComponent], @@ -4527,6 +4581,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4562,6 +4617,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4590,6 +4646,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4618,6 +4675,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4646,6 +4704,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4674,6 +4733,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4705,6 +4765,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4733,6 +4794,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4761,6 +4823,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4789,6 +4852,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4817,6 +4881,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4845,6 +4910,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4876,6 +4942,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4904,6 +4971,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -4935,6 +5003,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 60, "itemRenderer": [Function], "listRenderer": [Function], @@ -4963,6 +5032,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 60, "itemRenderer": [Function], "listRenderer": [Function], @@ -4991,6 +5061,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], @@ -5019,6 +5090,7 @@ Object { "filteredItems": Array [], "getList": undefined, "isAllSelected": false, + "isLocked": undefined, "itemHeight": 40, "itemRenderer": [Function], "listRenderer": [Function], diff --git a/tests/components/__snapshots__/source_list.spec.js.snap b/tests/components/__snapshots__/source_list.spec.js.snap index d8fcbb1..5dcdd4c 100644 --- a/tests/components/__snapshots__/source_list.spec.js.snap +++ b/tests/components/__snapshots__/source_list.spec.js.snap @@ -14,6 +14,7 @@ exports[`SourceList can remove search 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={40} items={Array []} listRenderer={[Function]} @@ -33,6 +34,7 @@ exports[`SourceList can remove select all 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={40} items={Array []} listRenderer={[Function]} @@ -60,6 +62,7 @@ exports[`SourceList custom itemRenderer 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={40} items={Array []} listRenderer={[Function]} @@ -87,6 +90,7 @@ exports[`SourceList custom messages 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={40} items={Array []} listRenderer={[Function]} @@ -114,6 +118,7 @@ exports[`SourceList custom noItemsRenderer 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={40} items={Array []} listRenderer={[Function]} @@ -141,6 +146,7 @@ exports[`SourceList custom searchRenderer 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={40} items={Array []} listRenderer={[Function]} @@ -168,6 +174,7 @@ exports[`SourceList custom selectAllRenderer 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={40} items={Array []} listRenderer={[Function]} @@ -195,6 +202,7 @@ exports[`SourceList default snapshot 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={40} items={Array []} listRenderer={[Function]} @@ -222,6 +230,7 @@ exports[`SourceList passed filterItems 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={40} items={Array []} listRenderer={[Function]} @@ -249,6 +258,7 @@ exports[`SourceList passed filteredItems with group 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={40} items={ Array [ @@ -306,6 +316,7 @@ exports[`SourceList passed selectAllItems 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={40} items={Array []} listRenderer={[Function]} @@ -333,6 +344,7 @@ exports[`SourceList passed selectItem 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={40} items={Array []} listRenderer={[Function]} @@ -365,6 +377,7 @@ exports[`SourceList passed selectedIds 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={40} items={Array []} listRenderer={[Function]} @@ -397,6 +410,7 @@ exports[`SourceList passed selectedItems 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={40} items={Array []} listRenderer={[Function]} @@ -477,6 +491,7 @@ exports[`SourceList show max selection tooltip 1`] = ` disabled={false} disabledItemsTooltip="You can select up to 4 items" height={400} + isLocked={undefined} itemHeight={40} items={Array []} listRenderer={[Function]} @@ -504,6 +519,7 @@ exports[`SourceList will pass itemHeight 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={10} items={Array []} listRenderer={[Function]} @@ -531,6 +547,7 @@ exports[`SourceList will pass selectAllHeight 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={10} items={Array []} listRenderer={[Function]} @@ -558,6 +575,7 @@ exports[`SourceList will pass selectAllHeight without itemHeight 1`] = ` disabled={false} disabledItemsTooltip={undefined} height={400} + isLocked={undefined} itemHeight={40} items={Array []} listRenderer={[Function]} diff --git a/tests/components/items/__snapshots__/selected_item.spec.js.snap b/tests/components/items/__snapshots__/selected_item.spec.js.snap index 78fe494..97fb7d5 100644 --- a/tests/components/items/__snapshots__/selected_item.spec.js.snap +++ b/tests/components/items/__snapshots__/selected_item.spec.js.snap @@ -18,6 +18,24 @@ exports[`SelectedItem default snapshot 1`] = ` `; +exports[`SelectedItem snapshot with disabled item 1`] = ` +
+ + + + +
+`; + exports[`SelectedItem snapshot with group item 1`] = `
{ const tree = renderer.render(); expect(tree).toMatchSnapshot(); }); + + test("snapshot with disabled item", () => { + const renderer = new ShallowRenderer(); + const tree = renderer.render( + + ); + expect(tree).toMatchSnapshot(); + }); }); diff --git a/tests/components/list/list.spec.js b/tests/components/list/list.spec.js index 9105348..3a0733a 100644 --- a/tests/components/list/list.spec.js +++ b/tests/components/list/list.spec.js @@ -13,6 +13,7 @@ const itemsWithDisabled = [ ]; const CUSTOM_MESSAGE = "custom message"; const CustomComponent = () =>
Custom Component
; +const isLocked = item => item.disabled; describe("List", () => { test("default snapshot", () => { @@ -66,20 +67,24 @@ describe("List", () => { }); test("does not shows NoItems if items are present", () => { - const wrapper = mount(); + const wrapper = mount( + + ); const noItems = wrapper.find(NoItems); expect(noItems.length).toBe(0); }); test("shows Items if items are present", () => { - const wrapper = mount(); + const wrapper = mount( + + ); const itemsWrapper = wrapper.find(Item); expect(itemsWrapper.length).toBe(2); }); test("shows checked Items if items are present", () => { const wrapper = mount( - + ); const itemsWrapper = wrapper.find(Item); expect(itemsWrapper.at(0).prop("checked")).toBe(false); @@ -88,7 +93,12 @@ describe("List", () => { test("shows Custom Item Component", () => { const wrapper = mount( - + ); const itemsWrapper = wrapper.find(CustomComponent); expect(itemsWrapper.length).toBe(2); @@ -96,7 +106,9 @@ describe("List", () => { test("click will trigger onClick", () => { const onClick = jest.fn(); - const wrapper = mount(); + const wrapper = mount( + + ); const itemsWrapper = wrapper.find(Item); itemsWrapper.at(0).simulate("click"); expect(onClick).toHaveBeenCalledTimes(1); @@ -104,7 +116,9 @@ describe("List", () => { test("click will trigger onClick with id param", () => { const onClick = jest.fn(); - const wrapper = mount(); + const wrapper = mount( + + ); const itemsWrapper = wrapper.find(Item); itemsWrapper.at(0).simulate("click"); expect(onClick).toHaveBeenCalledWith(expect.anything(), 5); @@ -123,7 +137,12 @@ describe("List", () => { test("click will not trigger onClick for disabled item", () => { const onClick = jest.fn(); const wrapper = mount( - + ); const itemsWrapper = wrapper.find(Item); itemsWrapper.at(0).simulate("click"); @@ -139,6 +158,7 @@ describe("List", () => { selectedIds={[5]} onClick={onClick} disabled={true} + isLocked={isLocked} /> ); const itemsWrapper = wrapper.find(Item); @@ -166,9 +186,48 @@ describe("List", () => { items={items.slice(0, 1)} disabledItemsTooltip={"You can select up to 4 items"} disabled={false} + isLocked={isLocked} /> ); const row = wrapper.find(".list_item"); expect(row.prop("title")).toBe(undefined); }); + test("show Item is checked & disabled", () => { + const wrapper = mount( + + ); + + const itemsWrapper = wrapper.find(Item); + expect(itemsWrapper.at(0).prop("disabled")).toBe(true); + expect(itemsWrapper.at(0).prop("checked")).toBe(true); + }); + + test("show Item is disabled but dose'nt checked", () => { + const wrapper = mount( + + ); + + const itemsWrapper = wrapper.find(Item); + expect(itemsWrapper.at(0).prop("disabled")).toBe(true); + expect(itemsWrapper.at(0).prop("checked")).toBe(false); + }); + test("show Item dose'nt disabled & dose'nt checked", () => { + const wrapper = mount( + + ); + + const itemsWrapper = wrapper.find(Item); + expect(itemsWrapper.at(0).prop("disabled")).toBe(false); + expect(itemsWrapper.at(0).prop("checked")).toBe(false); + }); }); diff --git a/tests/components/multi_select_state.spec.js b/tests/components/multi_select_state.spec.js index 2e696e3..a9a18fc 100644 --- a/tests/components/multi_select_state.spec.js +++ b/tests/components/multi_select_state.spec.js @@ -575,4 +575,144 @@ describe("withMultiSelectState", () => { wrapper.update(); expect(wrapper.state("filteredSelectedItems")).toEqual([ITEM_1]); }); + + test("select all for locked items", () => { + const ConditionalComponent = withMultiSelectState(CustomComponent); + const items = [ITEM_1, DISABLED_ITEM_23]; + const wrapper = shallow( + + ); + wrapper.props().selectAllItems(); + expect(wrapper.state("filteredSelectedItems")).toEqual([ + ITEM_1, + DISABLED_ITEM_23 + ]); + }); + + test("unselect locked items", () => { + const ConditionalComponent = withMultiSelectState(CustomComponent); + const items = [ITEM_1, DISABLED_ITEM_23]; + + const wrapper = shallow( + + ); + + wrapper.props().selectAllItems(); + expect(wrapper.state("filteredSelectedItems")).toEqual([ + ITEM_1, + DISABLED_ITEM_23 + ]); + + wrapper.props().unselectItems([ITEM_1.id, DISABLED_ITEM_23.id]); + expect(wrapper.state("selectedItems")).toEqual([DISABLED_ITEM_23]); + }); + + test("unselect all items for locked items", () => { + const ConditionalComponent = withMultiSelectState(CustomComponent); + const items = [ITEM_1, ITEM_2, ITEM_3, ITEM_4, DISABLED_ITEM_23]; + + const wrapper = shallow( + + ); + + wrapper.props().selectAllItems(); + expect(wrapper.state("selectedItems")).toEqual([DISABLED_ITEM_23]); + }); + + test("filter locked items in source list", () => { + const ConditionalComponent = withMultiSelectState(CustomComponent); + const items = [ITEM_1, ITEM_2, ITEM_3, ITEM_4, DISABLED_ITEM_23]; + + const wrapper = shallow( + + ); + + wrapper.props().filterItems({ target: { value: DISABLED_ITEM_23.label } }); + wrapper.update(); + expect(wrapper.state("filteredItems")).toEqual([DISABLED_ITEM_23]); + }); + + test("unselect locked items", () => { + const ConditionalComponent = withMultiSelectState(CustomComponent); + const items = [ITEM_1, DISABLED_ITEM_23]; + + const wrapper = shallow( + + ); + + wrapper.props().unselectItems([DISABLED_ITEM_23.id]); + expect(wrapper.state("selectedItems")).toEqual([DISABLED_ITEM_23]); + }); + + test("filter in destination list locked items", () => { + const ConditionalComponent = withMultiSelectState(CustomComponent); + const items = [ITEM_1, ITEM_2, ITEM_3, ITEM_4, DISABLED_ITEM_23]; + + const wrapper = shallow( + + ); + + wrapper + .props() + .filterSelectedItems({ target: { value: DISABLED_ITEM_23.label } }); + wrapper.update(); + expect(wrapper.state("filteredSelectedItems")).toEqual([DISABLED_ITEM_23]); + }); + + test("case select with shift items for (selected & disabled) and disabled", () => { + const ConditionalComponent = withMultiSelectState(CustomComponent); + const items = [ + ITEM_1, + { id: 1, label: "item 1", disabled: true }, + ITEM_3, + { id: 3, label: "item 3", disabled: true }, + { id: 4, label: "item 4" } + ]; + + const selectedItems = [{ id: 1, label: "item 1", disabled: true }]; + + const wrapper = shallow( + + ); + wrapper.props().selectItem(EVENT_WITH_SHIFT, ITEM_1.id); + wrapper.update(); + wrapper.props().selectItem(EVENT_WITH_SHIFT, 4); + wrapper.update(); + expect(wrapper.prop("selectedItems")).toEqual([ + items[0], + items[1], + items[2], + items[4] + ]); + }); }); diff --git a/tests/components/multi_select_state_utils.spec.js b/tests/components/multi_select_state_utils.spec.js index 4fec8c7..66f6905 100644 --- a/tests/components/multi_select_state_utils.spec.js +++ b/tests/components/multi_select_state_utils.spec.js @@ -1,5 +1,5 @@ import { - filterByIds, + filterUnselectedByIds, getSelectedByAllItems } from "../../src/components/multi_select_state_utils"; @@ -26,7 +26,8 @@ const disabledItems = [ describe("testing utils for multi select state", () => { test("filter items by id", () => { - const filterItems = filterByIds(items, ids); + const isLocked = item => item.disabled; + const filterItems = filterUnselectedByIds(items, ids, isLocked); expect(filterItems).toEqual([items[2], items[3]]); });