Skip to content

Commit 3a6809f

Browse files
authored
Merge pull request #889 from ishpaul777/ishpaul/fix-combobox
Ishpaul/fix-combobox
2 parents 4b6c041 + b86acdd commit 3a6809f

File tree

1 file changed

+47
-18
lines changed

1 file changed

+47
-18
lines changed

lib/components/form/element/combobox.js

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -195,23 +195,39 @@ class Combobox extends React.Component {
195195

196196
// eslint-disable-next-line react/no-unsafe
197197
UNSAFE_componentWillReceiveProps(nextProps) {
198-
if (!nextProps.value || isEqual(nextProps.value, this.state.currentValue)) {
199-
return;
198+
if (nextProps.value && !isEqual(nextProps.value, this.state.currentValue)) {
199+
this.setValue(nextProps.value);
200200
}
201-
this.setValue(nextProps.value);
202201

203202
if (nextProps.options !== undefined) {
204203
// If the options have an id property, we use that to compare them and determine if they changed, if not
205204
// we'll use the whole options array.
206-
if (has(nextProps.options, '0.id')) {
207-
if (
208-
nextProps.options.some((option, index) => !isEqual(option.id, this.props.options[index].id)) ||
209-
nextProps.alreadySelectedOptions.some((alreadySelectedOption, index) => !isEqual(alreadySelectedOption.id, this.props.alreadySelectedOptions[index].id))
210-
) {
205+
const optionsChanged = has(nextProps.options, '0.id')
206+
? nextProps.options.some((option, index) => !isEqual(option.id, this.props.options[index] && this.props.options[index].id))
207+
: !isEqual(nextProps.options, this.props.options);
208+
209+
// Check if alreadySelectedOptions changed - compare by id if available, otherwise by value or full comparison
210+
const hasAlreadySelectedId = nextProps.alreadySelectedOptions && nextProps.alreadySelectedOptions.length > 0 && has(nextProps.alreadySelectedOptions[0], 'id');
211+
212+
const alreadySelectedChanged = hasAlreadySelectedId
213+
? nextProps.alreadySelectedOptions.length !== this.props.alreadySelectedOptions.length ||
214+
nextProps.alreadySelectedOptions.some((alreadySelectedOption, index) => {
215+
const prevOption = this.props.alreadySelectedOptions[index];
216+
return !prevOption || !isEqual(alreadySelectedOption.id, prevOption.id);
217+
})
218+
: !isEqual(nextProps.alreadySelectedOptions, this.props.alreadySelectedOptions);
219+
220+
if (optionsChanged || alreadySelectedChanged) {
221+
// If only alreadySelectedOptions changed and dropdown is open, just update options without resetting
222+
if (!optionsChanged && alreadySelectedChanged && this.state.isDropdownOpen) {
223+
const {currentValue} = this.state;
224+
this.setState(() => ({
225+
options: this.getTruncatedOptions(currentValue, nextProps.alreadySelectedOptions),
226+
}));
227+
} else {
228+
// Options changed or dropdown is closed - do full reset
211229
this.reset(false, nextProps.options, nextProps.alreadySelectedOptions);
212230
}
213-
} else if (!isEqual(nextProps.options, this.props.options) || !isEqual(nextProps.alreadySelectedOptions, this.props.alreadySelectedOptions)) {
214-
this.reset(false, nextProps.options, nextProps.alreadySelectedOptions);
215231
}
216232
}
217233

@@ -484,12 +500,14 @@ class Combobox extends React.Component {
484500
// Get the divider index if we have one
485501
const findDivider = (option) => option.divider;
486502
const dividerIndex = this.options.findIndex(findDivider);
503+
487504
// Split into two arrays everything before and after the divider (if the divider does not exist then we'll return a single array)
488505
const splitOptions = dividerIndex ? [this.options.slice(0, dividerIndex + 1), this.options.slice(dividerIndex + 1)] : [this.options];
489506

490-
const formatOption = (option) => ({
507+
const formatOption = (option, index) => ({
491508
focused: false,
492-
isSelected: option.selected && (isEqual(option.value, currentValue) || !!alreadySelected.find((item) => item.value === option.value)),
509+
isSelected: (option.selected && isEqual(option.value, currentValue)) || !!alreadySelected.find((item) => item.value === option.value),
510+
originalIndex: index, // Added for stable sorting
493511
...option,
494512
});
495513

@@ -498,18 +516,29 @@ class Combobox extends React.Component {
498516
if (o.showLast) {
499517
return 2;
500518
}
501-
502519
return o.isSelected && this.props.alwaysShowSelectedOnTop ? 0 : 1;
503520
};
504521

505522
// Take each array and format it, sort it, and move selected items to top (if applicable)
506-
const formatOptions = (array) =>
507-
array
508-
.map(formatOption)
509-
.sort((a, b) => sortByOption(a) - sortByOption(b))
523+
const formatOptions = (array, arrayStartIndex) => {
524+
return array
525+
.map((option, index) => formatOption(option, arrayStartIndex + index))
526+
.sort((a, b) => {
527+
const sortDiff = sortByOption(a) - sortByOption(b);
528+
// If sort priority is the same, preserve original order using originalIndex
529+
return sortDiff !== 0 ? sortDiff : a.originalIndex - b.originalIndex;
530+
})
510531
.slice(0, this.props.maxItemsToShow);
532+
};
511533

512-
const truncatedOptions = splitOptions.map(formatOptions).flat();
534+
let cumulativeIndex = 0;
535+
const truncatedOptions = splitOptions
536+
.map((array) => {
537+
const result = formatOptions(array, cumulativeIndex);
538+
cumulativeIndex += array.length;
539+
return result;
540+
})
541+
.flat();
513542

514543
if (!truncatedOptions.length) {
515544
truncatedOptions.push({

0 commit comments

Comments
 (0)