Skip to content

Commit e763c1a

Browse files
MaksimDrobchakstepovat
authored andcommitted
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
1 parent 139cc3b commit e763c1a

19 files changed

+520
-83
lines changed

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class Example extends Component {
108108
| `searchSelectedItemsValue` | `string` | | The value of the search field for destination list. |
109109
| `searchSelectedItemsChanged` | `function` | | Function to handle the change of search field for destination list. Accepts value as a single argument. |
110110
| `selectedItemsFilterFunction` | `function` | based on label | Is the same as filterFunction by default to filter items based on the search query in destination list. |
111-
111+
| `isLocked` | `function` | `item => item.disabled` | Function to be used to define whether item is locked or not|
112112

113113
## Customization
114114

@@ -168,6 +168,21 @@ The `SelectedItem` component receives the following props:
168168

169169
`height` - receives the height defined by the list
170170

171+
172+
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.
173+
174+
Example (selected & disabled):
175+
```javascript
176+
function Exemple(){
177+
const items = [{id:0,label: 'item 0'}, {id:1,label: 'item 1'}];
178+
return (
179+
<MultiSelect
180+
isLocked={item => item.label === 'item 0'}
181+
items={items}
182+
selectedItems={items}
183+
/>
184+
}
185+
```
171186
<br/>
172187
173188
**Search**

src/components/destination_list.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ const DestinationList = ({
2222
noItemsRenderer,
2323
withGrouping,
2424
filteredItems,
25-
children
25+
children,
26+
isLocked
2627
}) => {
2728
const SelectionStatusRenderer = selectionStatusRenderer;
2829
const updatedSelectedItems = withGrouping
@@ -47,6 +48,7 @@ const DestinationList = ({
4748
renderer={selectedItemRenderer}
4849
noItemsRenderer={noItemsRenderer}
4950
noItemsMessage={messages.noItemsMessage}
51+
isLocked={isLocked}
5052
/>
5153
</Column>
5254
);
@@ -64,7 +66,8 @@ DestinationList.propTypes = {
6466
noItemsRenderer: PropTypes.any,
6567
withGrouping: PropTypes.bool,
6668
filteredItems: PropTypes.arrayOf(PropTypes.object),
67-
children: PropTypes.node
69+
children: PropTypes.node,
70+
isLocked: PropTypes.func
6871
};
6972

7073
DestinationList.defaultProps = {

src/components/items/selected_item.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,20 @@ import PropTypes from "prop-types";
33
import CloseIcon from "react-icons/lib/md/close";
44
import IconButton from "@material-ui/core/IconButton";
55
import ItemLabel from "./item_label";
6-
6+
import classnames from "classnames";
77
import styles from "./selected_item.scss";
88

9-
const SelectedItem = ({ item, height, group }) => (
9+
const SelectedItem = ({ item, height, group, disabled }) => (
1010
<div
11-
className={group ? styles.with_grouping : styles.selected_item}
11+
className={classnames({
12+
[styles.with_grouping]: group,
13+
[styles.selected_item]: !group,
14+
[styles.disabled]: disabled
15+
})}
1216
style={{ height }}
1317
>
1418
<ItemLabel label={item.label} />
15-
{!group && (
19+
{!group && !disabled && (
1620
<IconButton>
1721
<CloseIcon />
1822
</IconButton>
@@ -22,7 +26,8 @@ const SelectedItem = ({ item, height, group }) => (
2226

2327
SelectedItem.propTypes = {
2428
item: PropTypes.object,
25-
height: PropTypes.number
29+
height: PropTypes.number,
30+
isLocked: PropTypes.func
2631
};
2732

2833
SelectedItem.defaultProps = {

src/components/items/selected_item.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,9 @@
1818
pointer-events: none;
1919
color: $color-black;
2020
}
21+
22+
.disabled {
23+
color: $disabled-color;
24+
background-color: $selected-background-color;
25+
cursor: default;
26+
}

src/components/list/items_list.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ class ItemsList extends PureComponent {
1616
selectedIds: PropTypes.arrayOf(PropTypes.number),
1717
items: PropTypes.array,
1818
disabled: PropTypes.bool,
19-
disabledItemsTooltip: PropTypes.string
19+
disabledItemsTooltip: PropTypes.string,
20+
isLocked: PropTypes.func
2021
};
2122

2223
static defaultProps = {
@@ -61,7 +62,8 @@ class ItemsList extends PureComponent {
6162
selectedIds,
6263
onClick,
6364
disabled,
64-
disabledItemsTooltip
65+
disabledItemsTooltip,
66+
isLocked
6567
} = this.props;
6668
return (
6769
<AutoSizer>
@@ -84,6 +86,7 @@ class ItemsList extends PureComponent {
8486
selectedIds={selectedIds}
8587
disabled={disabled}
8688
disabledItemsTooltip={disabledItemsTooltip}
89+
isLocked={isLocked}
8790
/>
8891
)}
8992
</AutoSizer>

src/components/list/list.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ class InnerList extends PureComponent {
1818
selectedIds: PropTypes.arrayOf(PropTypes.number),
1919
items: PropTypes.array,
2020
disabled: PropTypes.bool,
21-
disabledItemsTooltip: PropTypes.string
21+
disabledItemsTooltip: PropTypes.string,
22+
isLocked: PropTypes.func
2223
};
2324

2425
static defaultProps = {
@@ -55,12 +56,14 @@ class InnerList extends PureComponent {
5556
onClick,
5657
items,
5758
selectedIds,
58-
disabledItemsTooltip
59+
disabledItemsTooltip,
60+
isLocked
5961
} = this.props;
6062
const Renderer = renderer;
6163
const item = items[index];
6264
const checked = selectedIds.includes(item.id);
63-
const disabled = this.props.disabled || item.disabled;
65+
const disabled = (this.props.disabled && !checked) || isLocked(item);
66+
6467
return (
6568
<div
6669
key={key}
@@ -74,7 +77,7 @@ class InnerList extends PureComponent {
7477
group={item.isGroup}
7578
height={itemHeight}
7679
checked={checked}
77-
disabled={disabled && !checked}
80+
disabled={disabled}
7881
/>
7982
</div>
8083
);

src/components/multi_select.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ export class MultiSelect extends PureComponent {
3939
withGrouping: PropTypes.bool,
4040
listRenderer: PropTypes.func,
4141
generateClassName: PropTypes.func,
42-
showSelectedItemsSearch: PropTypes.bool
42+
showSelectedItemsSearch: PropTypes.bool,
43+
isLocked: PropTypes.func
4344
};
4445

4546
static defaultProps = {
@@ -110,7 +111,8 @@ export class MultiSelect extends PureComponent {
110111
showSelectedItemsSearch,
111112
searchSelectedItemsValue,
112113
filterSelectedItems,
113-
filteredSelectedItems
114+
filteredSelectedItems,
115+
isLocked
114116
} = this.props;
115117
const calculatedHeight = this.calculateHeight();
116118
const selectedIds = selectedItems.map(item => item.id);
@@ -148,6 +150,7 @@ export class MultiSelect extends PureComponent {
148150
selectAllHeight={selectAllHeight}
149151
withGrouping={withGrouping}
150152
listRenderer={listRenderer}
153+
isLocked={isLocked}
151154
/>
152155
)}
153156
{!loading && showSelectedItems && (
@@ -167,6 +170,7 @@ export class MultiSelect extends PureComponent {
167170
filteredItems={filteredSelectedItems}
168171
searchIcon={searchIcon}
169172
filterItems={filterSelectedItems}
173+
isLocked={isLocked}
170174
/>
171175
)}
172176
</div>

src/components/multi_select_state.js

Lines changed: 52 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import React, { PureComponent } from "react";
22
import {
33
getSelectedByAllItems,
4-
filterByIds,
5-
findItem
4+
filterUnselectedByIds,
5+
findItem,
6+
getMinMaxIndexes,
7+
isWithin,
8+
getNewSelectedItems
69
} from "./multi_select_state_utils";
710

811
const withMultiSelectState = WrappedComponent =>
@@ -13,7 +16,8 @@ const withMultiSelectState = WrappedComponent =>
1316
.toLowerCase()
1417
.includes(value.toLowerCase()),
1518
items: [],
16-
selectedItems: []
19+
selectedItems: [],
20+
isLocked: item => item.disabled
1721
};
1822

1923
constructor(props) {
@@ -48,10 +52,8 @@ const withMultiSelectState = WrappedComponent =>
4852
this.setState({ selectedItems: nextProps.selectedItems });
4953
}
5054
if (this.props.items !== nextProps.items) {
51-
this.setState({
52-
items: nextProps.items,
53-
filteredItems: nextProps.items
54-
});
55+
const { items } = nextProps;
56+
this.setState({ items, filteredItems: items });
5557
}
5658
if (searchValue !== nextProps.searchValue) {
5759
this.filterItems({ target: { value: nextProps.searchValue } });
@@ -64,16 +66,16 @@ const withMultiSelectState = WrappedComponent =>
6466
}
6567

6668
handleMultiSelection(index) {
67-
const { items } = this.props;
68-
const { selectedItems, filteredItems } = this.state;
69+
const { items, isLocked } = this.props;
70+
const { filteredItems, firstItemShiftSelected } = this.state;
6971

70-
const { minIndex, maxIndex } = this.getMinMaxIndexes(index);
72+
const interval = getMinMaxIndexes(index, firstItemShiftSelected);
7173
const newSelectedItems = items.filter(
7274
(item, index) =>
73-
(index >= minIndex &&
74-
index <= maxIndex &&
75+
(isWithin(index, interval) &&
76+
!isLocked(item) &&
7577
findItem(item, filteredItems)) ||
76-
findItem(item, selectedItems)
78+
findItem(item, this.state.selectedItems)
7779
);
7880
const newFilteredSelectedItems = this.getNewFilteredSelectedItems(
7981
newSelectedItems
@@ -87,25 +89,6 @@ const withMultiSelectState = WrappedComponent =>
8789
);
8890
}
8991

90-
getMinMaxIndexes(currentIndex) {
91-
const { firstItemShiftSelected } = this.state;
92-
return firstItemShiftSelected > currentIndex
93-
? { minIndex: currentIndex, maxIndex: firstItemShiftSelected }
94-
: { minIndex: firstItemShiftSelected, maxIndex: currentIndex };
95-
}
96-
97-
getNewSelectedItems(itemId) {
98-
const { items } = this.props;
99-
const { selectedItems } = this.state;
100-
const sourceItems = items.filter(
101-
item => item.id === itemId || findItem(item, selectedItems)
102-
);
103-
const destinationItems = selectedItems.filter(
104-
selectedItem => !findItem(selectedItem, items)
105-
);
106-
return [...destinationItems, ...sourceItems];
107-
}
108-
10992
getNewFilteredSelectedItems(items) {
11093
const { searchSelectedItemsValue } = this.props;
11194

@@ -140,27 +123,39 @@ const withMultiSelectState = WrappedComponent =>
140123
const index = items.findIndex(item => item.id === id);
141124
this.setState({ firstItemShiftSelected: index });
142125
}
143-
const newSelectedItems = this.getNewSelectedItems(id);
144-
const newFilteredSelectedItems = this.getNewFilteredSelectedItems(
145-
newSelectedItems
146-
);
147-
this.setState(
148-
{
149-
selectedItems: newSelectedItems,
150-
filteredSelectedItems: newFilteredSelectedItems
151-
},
152-
this.handleChange
153-
);
126+
this.setNewItemsBySelectItem(id, items, selectedItems);
154127
}
155128
} else {
156129
this.unselectItems([id]);
157130
}
158131
}
159132

133+
setNewItemsBySelectItem(id, items, selectedItems) {
134+
const newSelectedItems = getNewSelectedItems(id, items, selectedItems);
135+
const newFilteredSelectedItems = this.getNewFilteredSelectedItems(
136+
newSelectedItems
137+
);
138+
this.setState(
139+
{
140+
selectedItems: newSelectedItems,
141+
filteredSelectedItems: newFilteredSelectedItems
142+
},
143+
this.handleChange
144+
);
145+
}
160146
unselectItems(ids) {
161147
const { selectedItems, filteredSelectedItems } = this.state;
162-
const newSelectedItems = filterByIds(selectedItems, ids);
163-
const newFilteredSelectedItems = filterByIds(filteredSelectedItems, ids);
148+
const { isLocked } = this.props;
149+
const newSelectedItems = filterUnselectedByIds(
150+
selectedItems,
151+
ids,
152+
isLocked
153+
);
154+
const newFilteredSelectedItems = filterUnselectedByIds(
155+
filteredSelectedItems,
156+
ids,
157+
isLocked
158+
);
164159
this.setState(
165160
{
166161
selectedItems: newSelectedItems,
@@ -171,19 +166,21 @@ const withMultiSelectState = WrappedComponent =>
171166
}
172167

173168
clearAll() {
169+
const { selectedItems, isLocked } = this.props;
170+
const lockedItems = selectedItems.filter(isLocked);
174171
this.setState(
175-
{ selectedItems: [], filteredSelectedItems: [] },
172+
{
173+
selectedItems: lockedItems,
174+
filteredSelectedItems: lockedItems
175+
},
176176
this.handleChange
177177
);
178178
}
179179

180180
filterItems(event) {
181181
const { items, filterFunction, searchValueChanged } = this.props;
182182
const { value } = event.target;
183-
this.setState({
184-
filteredItems: items.filter(filterFunction(value))
185-
});
186-
183+
this.setState({ filteredItems: items.filter(filterFunction(value)) });
187184
searchValueChanged && searchValueChanged(value);
188185
}
189186

@@ -225,11 +222,13 @@ const withMultiSelectState = WrappedComponent =>
225222

226223
isAllSelected() {
227224
const { filteredItems, selectedItems } = this.state;
228-
const selectedItemsInFilteredItems = selectedItems.filter(selectedItem =>
229-
findItem(selectedItem, filteredItems)
225+
const { isLocked } = this.props;
226+
const selectedItemsInFilteredItems = selectedItems.filter(
227+
selectedItem =>
228+
!isLocked(selectedItem) && findItem(selectedItem, filteredItems)
230229
);
231230
const selectableFilteredItems = filteredItems.filter(
232-
item => !item.disabled
231+
item => !isLocked(item)
233232
);
234233
return (
235234
selectedItemsInFilteredItems.length ===

0 commit comments

Comments
 (0)