Skip to content

Commit 9a4c39f

Browse files
committed
Issue #149 - It is possible to select more than maxSelectedItems when using Shift
1 parent e763c1a commit 9a4c39f

File tree

4 files changed

+153
-5
lines changed

4 files changed

+153
-5
lines changed

src/components/multi_select_state.js

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import React, { PureComponent } from "react";
22
import {
33
getSelectedByAllItems,
4+
getSelectedItemsOutsideInterval,
5+
getAvailableIntervalForSelection,
46
filterUnselectedByIds,
57
findItem,
68
getMinMaxIndexes,
@@ -66,16 +68,32 @@ const withMultiSelectState = WrappedComponent =>
6668
}
6769

6870
handleMultiSelection(index) {
69-
const { items, isLocked } = this.props;
70-
const { filteredItems, firstItemShiftSelected } = this.state;
71+
const { items, isLocked, maxSelectedItems } = this.props;
72+
const {
73+
filteredItems,
74+
firstItemShiftSelected,
75+
selectedItems
76+
} = this.state;
77+
78+
const initialInterval = getMinMaxIndexes(index, firstItemShiftSelected);
79+
const outsideIntervalSelectedItems = getSelectedItemsOutsideInterval(
80+
items,
81+
selectedItems,
82+
initialInterval
83+
);
84+
const interval = getAvailableIntervalForSelection(
85+
initialInterval,
86+
outsideIntervalSelectedItems.length,
87+
maxSelectedItems,
88+
firstItemShiftSelected
89+
);
7190

72-
const interval = getMinMaxIndexes(index, firstItemShiftSelected);
7391
const newSelectedItems = items.filter(
7492
(item, index) =>
7593
(isWithin(index, interval) &&
7694
!isLocked(item) &&
7795
findItem(item, filteredItems)) ||
78-
findItem(item, this.state.selectedItems)
96+
findItem(item, outsideIntervalSelectedItems)
7997
);
8098
const newFilteredSelectedItems = this.getNewFilteredSelectedItems(
8199
newSelectedItems

src/components/multi_select_state_utils.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,50 @@ export const getMinMaxIndexes = (currentIndex, firstItemShiftSelected) =>
3737
? { minIndex: currentIndex, maxIndex: firstItemShiftSelected }
3838
: { minIndex: firstItemShiftSelected, maxIndex: currentIndex };
3939

40+
export const getAvailableIntervalForSelection = (
41+
interval,
42+
outsideSelectedItems,
43+
maxSelectedItems,
44+
firstItemShiftSelected
45+
) => {
46+
const { minIndex, maxIndex } = interval;
47+
const shouldBeSelect = outsideSelectedItems + (maxIndex - minIndex);
48+
return maxSelectedItems > 0 && maxSelectedItems <= shouldBeSelect
49+
? getFixedInterval(
50+
interval,
51+
outsideSelectedItems,
52+
maxSelectedItems,
53+
firstItemShiftSelected
54+
)
55+
: interval;
56+
};
57+
58+
const getFixedInterval = (
59+
{ minIndex, maxIndex },
60+
outsideSelectedItems,
61+
maxSelectedItems,
62+
firstItemShiftSelected
63+
) => ({
64+
minIndex:
65+
maxIndex === firstItemShiftSelected
66+
? maxIndex - (maxSelectedItems - outsideSelectedItems - 1)
67+
: minIndex,
68+
maxIndex:
69+
minIndex === firstItemShiftSelected
70+
? minIndex + (maxSelectedItems - outsideSelectedItems - 1)
71+
: maxIndex
72+
});
73+
74+
export const getSelectedItemsOutsideInterval = (
75+
sourceItems,
76+
selectedItems,
77+
interval
78+
) =>
79+
selectedItems.filter(selectedItem => {
80+
const index = sourceItems.findIndex(item => item.id === selectedItem.id);
81+
return !isWithin(index, interval) || selectedItem.disabled;
82+
});
83+
4084
export const isWithin = (index, { minIndex, maxIndex }) =>
4185
index >= minIndex && index <= maxIndex;
4286

tests/components/multi_select_state.spec.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,4 +715,40 @@ describe("withMultiSelectState", () => {
715715
items[4]
716716
]);
717717
});
718+
719+
test("case select with shift items where shouldBeSelect > maxSelectedItems", () => {
720+
const ConditionalComponent = withMultiSelectState(CustomComponent);
721+
const items = [ITEM_1, ITEM_2, ITEM_3, ITEM_4, ITEM_12];
722+
723+
const wrapper = shallow(
724+
<ConditionalComponent
725+
items={items}
726+
selectedItems={[ITEM_1]}
727+
maxSelectedItems={3}
728+
/>
729+
);
730+
wrapper.props().selectItem(EVENT_WITH_SHIFT, ITEM_12.id);
731+
wrapper.update();
732+
wrapper.props().selectItem(EVENT_WITH_SHIFT, ITEM_3.id);
733+
wrapper.update();
734+
expect(wrapper.prop("selectedItems")).toEqual([ITEM_1, ITEM_4, ITEM_12]);
735+
});
736+
737+
test("case select with shift items where shouldBeSelect < maxSelectedItems", () => {
738+
const ConditionalComponent = withMultiSelectState(CustomComponent);
739+
const items = [ITEM_1, ITEM_2, ITEM_3, ITEM_4];
740+
741+
const wrapper = shallow(
742+
<ConditionalComponent
743+
items={items}
744+
selectedItems={[]}
745+
maxSelectedItems={4}
746+
/>
747+
);
748+
wrapper.props().selectItem(EVENT_WITH_SHIFT, ITEM_1.id);
749+
wrapper.update();
750+
wrapper.props().selectItem(EVENT_WITH_SHIFT, ITEM_3.id);
751+
wrapper.update();
752+
expect(wrapper.prop("selectedItems")).toEqual([ITEM_1, ITEM_2, ITEM_3]);
753+
});
718754
});

tests/components/multi_select_state_utils.spec.js

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {
22
filterUnselectedByIds,
3-
getSelectedByAllItems
3+
getSelectedByAllItems,
4+
getSelectedItemsOutsideInterval,
5+
getAvailableIntervalForSelection
46
} from "../../src/components/multi_select_state_utils";
57

68
const items = [
@@ -76,4 +78,52 @@ describe("testing utils for multi select state", () => {
7678
);
7779
expect(allSelectedItems).toEqual([]);
7880
});
81+
82+
test("filter selectedItems that not in interval", () => {
83+
const selectedItemsOutsideInterval = getSelectedItemsOutsideInterval(
84+
items,
85+
selectedItems,
86+
{ minIndex: 1, maxIndex: 3 }
87+
);
88+
expect(selectedItemsOutsideInterval.length).toEqual(1);
89+
expect(selectedItemsOutsideInterval[0]).toEqual(selectedItems[0]);
90+
});
91+
92+
test("filter itemsToSelect that not in interval", () => {
93+
const selectedItemsOutsideInterval = getSelectedItemsOutsideInterval(
94+
items,
95+
itemsToSelect,
96+
{ minIndex: 0, maxIndex: 3 }
97+
);
98+
expect(selectedItemsOutsideInterval.length).toEqual(0);
99+
});
100+
101+
test("available interval when minIndex == firstItemShiftSelected", () => {
102+
const initialInterval = { minIndex: 0, maxIndex: 3 };
103+
const maxSelectedItems = 3;
104+
const selectedItemsOutsideInterval = 0;
105+
const firstItemShiftSelected = 0;
106+
const availableInterval = getAvailableIntervalForSelection(
107+
initialInterval,
108+
selectedItemsOutsideInterval,
109+
maxSelectedItems,
110+
firstItemShiftSelected
111+
);
112+
expect(availableInterval).toEqual({ minIndex: 0, maxIndex: 2 });
113+
});
114+
115+
test("available interval when maxIndex == firstItemShiftSelected", () => {
116+
const initialInterval = { minIndex: 1, maxIndex: 3 };
117+
const maxSelectedItems = 3;
118+
const selectedItemsOutsideInterval = 1;
119+
const firstItemShiftSelected = 3;
120+
121+
const availableInterval = getAvailableIntervalForSelection(
122+
initialInterval,
123+
selectedItemsOutsideInterval,
124+
maxSelectedItems,
125+
firstItemShiftSelected
126+
);
127+
expect(availableInterval).toEqual({ minIndex: 2, maxIndex: 3 });
128+
});
79129
});

0 commit comments

Comments
 (0)