Skip to content

Commit 8089cae

Browse files
committed
Make userSelections return label, value, and index
Resolves #230.
1 parent eec035c commit 8089cae

File tree

5 files changed

+33
-26
lines changed

5 files changed

+33
-26
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## 6.0.0 (TBA)
44

5-
_This new version includes minor breaking changes. Please review them before upgrading._
5+
_This new version includes a variety of breaking changes. Please review them before upgrading._
66

77
### Added
88

@@ -11,6 +11,7 @@ _This new version includes minor breaking changes. Please review them before upg
1111

1212
### Changed
1313

14+
* **Breaking:** `onChange` property: the second argument now returns objects with `label`, `index`, and `value`, instead of just values (#230)
1415
* **Breaking:** `icons` property:
1516
* Will now merge any missing keys with the default icons
1617
* Rename `moveLeft`, `moveAllLeft`, `moveRight`, and `moveAllRight` to `moveToAvailable`, `moveAllToAvailable`, `moveToSelected`, and `moveAllToSelected`
@@ -20,7 +21,7 @@ _This new version includes minor breaking changes. Please review them before upg
2021
* Will now merge any missing keys with the default language
2122
* Rename `moveLeft`, `moveAllLeft`, `moveRight`, and `moveAllRight` to `moveToAvailable`, `moveAllToAvailable`, `moveToSelected`, and `moveAllToSelected`
2223
* Split `filterPlaceholder` into `availableFilterPlaceholder` and `selectedFilterPlaceholder`
23-
* **Breaking:** `options` property: no longer has PropTypes validation for `label` and `value`
24+
* **Breaking:** `options` property: no longer has PropTypes validation for `label` and `value` (#208)
2425
* **Breaking:** Rename `*-right` and `*-left` classes to `*-to-selected` and `*-to-available`
2526
* Improve accessibility of required error
2627
* Change filter input to `type="search"` (#247)

src/js/components/DualListBox.jsx

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,14 @@ function DualListBox(props) {
145145
}
146146

147147
return Array.from(element.options)
148-
.filter(({ selected: isSelected }) => isSelected)
149-
.map(({ dataset: { order, value } }) => ({
150-
index: parseInt(order, 10),
151-
value: JSON.parse(value),
152-
}));
148+
.map(({ dataset, label, selected: isSelected }, index) => ({
149+
index,
150+
isSelected,
151+
label,
152+
order: parseInt(dataset.order, 10),
153+
value: JSON.parse(dataset.value),
154+
}))
155+
.filter(({ isSelected }) => isSelected);
153156
}
154157

155158
/**
@@ -331,20 +334,20 @@ function DualListBox(props) {
331334
// If all of the marked options are already as high as they can get, ignore the
332335
// re-arrangement request because they will end of swapping their order amongst
333336
// themselves.
334-
if (markedOptions[markedOptions.length - 1].index > markedOptions.length - 1) {
335-
markedOptions.forEach(({ index }) => {
336-
if (index > 0) {
337-
newOrder = swapOptions(index, index - 1)(newOrder);
337+
if (markedOptions[markedOptions.length - 1].order > markedOptions.length - 1) {
338+
markedOptions.forEach(({ order }) => {
339+
if (order > 0) {
340+
newOrder = swapOptions(order, order - 1)(newOrder);
338341
}
339342
});
340343
}
341344
} else if (direction === 'down') {
342345
// Similar to the above, if all of the marked options are already as low as they can
343346
// get, ignore the re-arrangement request.
344-
if (markedOptions[0].index < selected.length - markedOptions.length) {
345-
markedOptions.reverse().forEach(({ index }) => {
346-
if (index < selected.length - 1) {
347-
newOrder = swapOptions(index, index + 1)(newOrder);
347+
if (markedOptions[0].order < selected.length - markedOptions.length) {
348+
markedOptions.reverse().forEach(({ order }) => {
349+
if (order < selected.length - 1) {
350+
newOrder = swapOptions(order, order + 1)(newOrder);
348351
}
349352
});
350353
}
@@ -365,13 +368,13 @@ function DualListBox(props) {
365368
let unmarked = [...selected];
366369

367370
// Filter out marked options
368-
markedOptions.forEach(({ index }) => {
369-
unmarked[index] = null;
371+
markedOptions.forEach(({ order }) => {
372+
unmarked[order] = null;
370373
});
371374
unmarked = unmarked.filter((v) => v !== null);
372375

373376
// Condense marked options raw values
374-
const marked = markedOptions.map(({ index }) => selected[index]);
377+
const marked = markedOptions.map(({ order }) => selected[order]);
375378

376379
if (direction === 'top') {
377380
return [...marked, ...unmarked];
@@ -478,15 +481,15 @@ function DualListBox(props) {
478481
const toggleItemsMap = { ...selectedItems };
479482

480483
// Add/remove the individual items based on previous state
481-
toggleItems.forEach(({ value, index }) => {
484+
toggleItems.forEach(({ value, order }) => {
482485
const inSelectedOptions = selectedItems.indexOf(value) > -1;
483486

484487
if (inSelectedOptions && (!allowDuplicates || controlKey === 'selected')) {
485488
// Toggled items that were previously selected are removed unless `allowDuplicates`
486489
// is set to true or the option was sourced from the selected ListBox. We use an
487490
// object mapping such that we can remove the exact index of the selected items
488491
// without the array re-arranging itself.
489-
delete toggleItemsMap[index];
492+
delete toggleItemsMap[order];
490493
} else {
491494
selectedItems.push(value);
492495
}
@@ -510,7 +513,7 @@ function DualListBox(props) {
510513
*/
511514
function onChange(newSelected, selection, controlKey, isRearrange = false) {
512515
const { onChange: onChangeProp } = props;
513-
const userSelection = selection.map(({ value }) => value);
516+
const userSelection = selection.map(({ index, label, value }) => ({ index, label, value }));
514517

515518
onChangeProp(newSelected, userSelection, controlKey);
516519

src/js/components/ObjectValueWrapper.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ function ObjectValueWrapper(props) {
4343
const simpleSelected = flattenOptions(selected, getOptionValue);
4444

4545
const simpleOnChange = useCallback((newSelected, userSelection, controlKey) => {
46-
const sourceValues = { selected: newSelected, userSelection };
47-
const complexValues = { selected: [], userSelection: [] };
46+
const sourceValues = { selected: newSelected };
47+
const complexValues = { selected: [] };
4848

4949
// Reconstruct option objects for both the selected values and user selection
5050
Object.keys(sourceValues).forEach((key) => {
@@ -78,7 +78,7 @@ function ObjectValueWrapper(props) {
7878
});
7979
});
8080

81-
onChange(complexValues.selected, complexValues.userSelection, controlKey);
81+
onChange(complexValues.selected, userSelection, controlKey);
8282
}, [getOptionValue, options]);
8383

8484
/* eslint-disable react/jsx-props-no-spreading */

test/DualListBox.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1376,7 +1376,10 @@ describe('<DualListBox />', async () => {
13761376
await user.click(screen.getByLabelText('Move to available'));
13771377

13781378
assert.deepEqual(actualSelected, []);
1379-
assert.deepEqual(actualSelection, ['luna', 'phobos']);
1379+
assert.deepEqual(actualSelection, [
1380+
{ index: 0, label: 'Moon', value: 'luna' },
1381+
{ index: 1, label: 'Phobos', value: 'phobos' },
1382+
]);
13801383
});
13811384

13821385
it('should identify the control responsible for the changes', async () => {

test/ObjectValueWrapper.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ describe('ObjectValueWrapper', () => {
136136
await user.click(screen.getByLabelText('Move to selected'));
137137

138138
assert.deepEqual(actualSelection, [
139-
{ label: 'Phobos', value: 'phobos' },
139+
{ index: 1, label: 'Phobos', value: 'phobos' },
140140
]);
141141
});
142142
});

0 commit comments

Comments
 (0)