Skip to content
11 changes: 11 additions & 0 deletions lib/public/components/Filters/common/FilterModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ export class FilterModel extends Observable {
throw new Error('Abstract function call');
}

/**
* Sets filters from normalised values to submodels in needed.
*
* @param {string|number|object|string[]|number[]|null} _value The value used to set filters
* @return {void} the normalized value
* @abstract
*/
set normalized(_value) {
throw new Error('Abstract function call');
}

/**
* Returns the observable notified any time there is a visual change which has no impact on the actual filter value
*
Expand Down
42 changes: 39 additions & 3 deletions lib/public/components/common/selection/SelectionModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ export class SelectionModel extends Observable {
super();
const { availableOptions = [], defaultSelection = [], multiple = true, allowEmpty = true } = configuration || {};

/**
* @type {SelectionOption[]}
* @protected
*/
this._selectionBacklog = [];

/**
* @type {RemoteData<SelectionOption[]>|SelectionOption[]}
* @protected
Expand Down Expand Up @@ -243,14 +249,20 @@ export class SelectionModel extends Observable {
}

/**
* Defines the list of available options
* Defines the list of available options and if there is a selection backlog, these will be applied
*
* @param {RemoteData<SelectionOption[], *>|SelectionOption[]} availableOptions the new available options
* @return {void}
*/
setAvailableOptions(availableOptions) {
this._availableOptions = availableOptions;
this.visualChange$.notify();

if (this._selectionBacklog.length) {
Comment thread
isaachilly marked this conversation as resolved.
this.selectedOptions = this._selectionBacklog;
this._selectionBacklog = [];
this.notify();
}
}

/**
Expand Down Expand Up @@ -315,12 +327,19 @@ export class SelectionModel extends Observable {
}

/**
* Define (overrides) the list of currently selected options
* Define (overrides) the list of currently selected options.
* Invalid selection options are excluded
*
* @param {SelectionOption[]} selected the list of selected options
*/
set selectedOptions(selected) {
this._selectedOptions = selected;
let { options } = this;

if (this.options instanceof RemoteData) {
options = options.isSuccess() ? options.payload : [];
}

this._selectedOptions = options.filter((option) => selected.some(({ value }) => String(value) === String(option.value)));;
}

/**
Expand All @@ -332,6 +351,23 @@ export class SelectionModel extends Observable {
return this._defaultSelection;
}

/**
* Sets selected options based on a comma-seperated string.
* Accounts for the options being either RemoteData or an array.
*
* @return {string}
Comment thread
isaachilly marked this conversation as resolved.
Outdated
*/
set normalized(value) {
Copy link
Copy Markdown
Collaborator

@isaachilly isaachilly May 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if value is null or undefined? Could value ever be null or undefined? Maybe not in UI but maybe if passed in URL? What do you think? Goes for all the setters.

Copy link
Copy Markdown
Collaborator Author

@NarrowsProjects NarrowsProjects May 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since queryRouter omits any values that are undefined when computing its params field, the normalized values derived from params will always be usable.

As a result, if a value is passed via URL (which is why the setters exist), neither value nor its equivalent can end up being undefined.

Copy link
Copy Markdown
Collaborator Author

@NarrowsProjects NarrowsProjects May 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, while undefined can't really reach the setters, empty strings can

const options = value.split(',').map((option) => ({ value: option }));
Comment thread
isaachilly marked this conversation as resolved.
Outdated
const postponeSelection = this.options instanceof RemoteData || ! this.options?.length;

if (postponeSelection) {
this._selectionBacklog = options;
} else {
this.selectedOptions = options;
}
}

/**
* Returns the normalized value of the selection
*
Expand Down