Skip to content

Commit 2296c45

Browse files
[O2B-1558] Set filters from url upon page reload (#2144)
Notable changes for users: - When a filters on overview pages are set, the active filters will appear in the url, when reloading the page, these filters will be applied automatically. - Users can now 'undo' filters by pressing the go-back button in the browser Notable changes for developers: - The eorReason filter component has been changed to match its reasonType and Category with its model - A textInputFilter has been replaced by a textFilter as the model that controls it is a TextTokenFilterModel. - This prevented it from being set by the url as their state is different. - A guard has been added in the registerObservablesQcSummaryDependsOn function to prevent continual re-registration - A new getter has been added to filterModel and selectionModel called isInactive that will by default act as an alias for the isEmpty getter - The distinction is that isEmpty is used to determine if the filter should be added to requests or be set to the url while isInactive determines if specific ui interactions should happen (e.g. toggles being considered inactive despite being filled with 'false', thus being added urls, but not enabling the reset-filter button, as you can't reset an already untoggled toggle) - The NumericalComparisonFilter normalized setter has been changed to separate raw value from parsed value. - ToggleFilters will now ALWAYS be filled, meaning that they will always be added to the query parameters. - This was already the case for all toggles except for HasStableBeams, which would only be added if it was true. - The change does not change page behaviour as the back-end checks hasStableBeams for being truethy, and both false and undefined (which it was before this change) rule as falsely.
1 parent 7a03dd1 commit 2296c45

38 files changed

Lines changed: 773 additions & 80 deletions

lib/public/components/Filters/RunsFilter/MultiCompositionFilterModel.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,13 @@ export class MultiCompositionFilterModel extends FilterModel {
7575
return Object.values(this._filters).every((filter) => filter.isEmpty);
7676
}
7777

78+
/**
79+
* @inheritDoc
80+
*/
81+
get isInactive() {
82+
return Object.values(this._filters).every((filter) => filter.isInactive);
83+
}
84+
7885
/**
7986
* @inheritDoc
8087
*/

lib/public/components/Filters/RunsFilter/RunDefinitionFilterModel.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ export class RunDefinitionFilterModel extends SelectionModel {
3131
if (!this.isPhysicsOnly()) {
3232
this.selectedOptions = [];
3333
this.select(RunDefinition.Physics);
34-
this.notify();
3534
}
3635
}
3736

lib/public/components/Filters/common/FilterModel.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,15 @@ export class FilterModel extends Observable {
7777
return this._visualChange$;
7878
}
7979

80+
/**
81+
* States if the filter is active. By default this is equivalent to isEmpty
82+
*
83+
* @return {boolean} true if the filter is active
84+
*/
85+
get isInactive() {
86+
return this.isEmpty;
87+
}
88+
8089
/**
8190
* Utility function to register a filter model as sub-filter model
8291
*

lib/public/components/Filters/common/FilteringModel.js

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export class FilteringModel extends Observable {
6666

6767
if (clearUrl) {
6868
const { params } = this._router;
69-
delete params.filter;
69+
params.filter = this.normalized;
7070
this._router.go(buildUrl('?', params), false, true);
7171
}
7272
}
@@ -93,12 +93,7 @@ export class FilteringModel extends Observable {
9393
* @return {boolean} true if at least one filter is active
9494
*/
9595
isAnyFilterActive() {
96-
for (const model of this._filterModels) {
97-
if (!model.isEmpty) {
98-
return true;
99-
}
100-
}
101-
return false;
96+
return !this._filterModels.every((model) => model.isInactive);
10297
}
10398

10499
/**
@@ -140,6 +135,32 @@ export class FilteringModel extends Observable {
140135
this.notify();
141136
}
142137

138+
/**
139+
* Look for parameters used for filtering in URL and apply them in the layout if it exists
140+
*
141+
* @param {boolean} notify if observers should be notified after setting the filters
142+
* @returns {undefined}
143+
*/
144+
setFilterFromURL(notify = false) {
145+
const { params: { page = '', filter } } = this._router;
146+
147+
if (this._pageIdentifier === page) {
148+
if (filter) {
149+
for (const [key, value] of Object.entries(filter)) {
150+
if (key in this._filters) {
151+
this._filters[key].normalized = value;
152+
}
153+
}
154+
} else {
155+
this.reset();
156+
}
157+
}
158+
159+
if (notify) {
160+
this.notify();
161+
}
162+
}
163+
143164
/**
144165
* Add new filter
145166
*

lib/public/components/Filters/common/filters/NumericalComparisonFilterModel.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,11 @@ export class NumericalComparisonFilterModel extends FilterModel {
9292
* @inheritDoc
9393
*/
9494
set normalized({ operator, limit }) {
95-
const parsedLimit = parseFloat(limit) / this._scale;
95+
const numericLimit = parseFloat(limit);
96+
const scaledLimit = numericLimit / this._scale;
9697

97-
if (!isNaN(parsedLimit)) {
98-
this._operandInputModel.normalized = parsedLimit;
98+
if (!isNaN(numericLimit) || !isNaN(scaledLimit)) {
99+
this._operandInputModel.normalized = { value: numericLimit, raw: scaledLimit };
99100
}
100101

101102
this._operatorSelectionModel.normalized = operator;

lib/public/components/Filters/common/filters/ProcessedTextInputModel.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,14 @@ export class ProcessedTextInputModel extends Observable {
109109
/**
110110
* Sets filters from normalised values.
111111
*
112-
* @param {string} value The value used to set filters
112+
* @param {string} value The value used to set the parsed value
113+
* @param {string} raw The value used to set the raw value
113114
* @return {void}
114115
* @abstract
115116
*/
116-
set normalized(value) {
117+
set normalized({ value, raw }) {
117118
this._value = value;
118-
this._raw = value;
119+
this._raw = raw;
119120
}
120121

121122
/**

lib/public/components/Filters/common/filters/ToggleFilterModel.js

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,17 @@ export class ToggleFilterModel extends SelectionModel {
1919
/**
2020
* Constructor
2121
* @param {boolean} toggledByDefault If the filter should be toggled by default
22-
* @param {boolean} untoggledIsEmpty if true, will treat the untoggled state (false) as empty.
22+
* @param {boolean} defaultIsInactive if true, will treat the untoggled state (false) as empty.
2323
*/
24-
constructor(toggledByDefault = false, untoggledIsEmpty = false) {
24+
constructor(toggledByDefault = false, defaultIsInactive = false) {
2525
super({
2626
availableOptions: [{ value: true }, { value: false }],
2727
defaultSelection: [{ value: toggledByDefault }],
2828
multiple: false,
2929
allowEmpty: false,
3030
});
3131

32-
this._untoggledIsEmpty = untoggledIsEmpty;
32+
this._defaultIsInactive = defaultIsInactive;
3333
}
3434

3535
/**
@@ -51,15 +51,22 @@ export class ToggleFilterModel extends SelectionModel {
5151
}
5252

5353
/**
54-
* Determines whether the current value should be considered empty.
55-
* When this._untoggledIsEmpty is set to 'true', then untoggled states (false) will be treated as an empty filter
54+
* Toggles are always filled, as 'false' / untoggled is also considered a value
5655
*
57-
* @return {boolean} `true` if the current value is considered empty,
58-
* otherwise `false`.
56+
* @return {boolean} `false`
5957
*/
6058
get isEmpty() {
61-
if (this._untoggledIsEmpty) {
62-
return this.isToggled === false;
59+
return false;
60+
}
61+
62+
/**
63+
* Returns if the toggle filter is considered 'inactive'
64+
*
65+
* @return {boolean}
66+
*/
67+
get isInactive() {
68+
if (this._defaultIsInactive) {
69+
return this.hasOnlyDefaultSelection();
6370
}
6471

6572
return false;

lib/public/components/common/selection/SelectionModel.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,15 @@ export class SelectionModel extends Observable {
113113
return selected.length === defaultSelection.length && selected.every((item) => defaultSelection.includes(item));
114114
}
115115

116+
/**
117+
* States if the filter is active. By default this is equivalent to isEmpty
118+
*
119+
* @return {boolean} true if the filter is active
120+
*/
121+
get isInactive() {
122+
return this.isEmpty;
123+
}
124+
116125
/**
117126
* Reset the selection to the default
118127
*
@@ -358,9 +367,19 @@ export class SelectionModel extends Observable {
358367
*/
359368
set normalized(value) {
360369
const options = value.split(',').map((option) => ({ value: option.trim() }));
361-
const postponeSelection = this.options instanceof RemoteData || !this.options?.length;
362-
363-
if (postponeSelection) {
370+
const isRemoteData = this.options instanceof RemoteData;
371+
const noOptions = !this.options?.length;
372+
373+
if (isRemoteData) {
374+
this._availableOptions.match({
375+
Success: (_) => {
376+
this.selectedOptions = options;
377+
},
378+
Other: () => {
379+
this._selectionBacklog = options;
380+
},
381+
});
382+
} else if (noOptions) {
364383
this._selectionBacklog = options;
365384
} else {
366385
this.selectedOptions = options;

lib/public/components/runEorReasons/runEorReasonSelection.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { h } from '/js/src/index.js';
2222
*/
2323
export const eorReasonFilterComponent = (eorReasonFilterModel, eorReasonTypes) => {
2424
const eorReasonsCategories = [...new Set(eorReasonTypes.map(({ category }) => category))];
25+
const { category: currentCategory, title: currentTitle } = eorReasonFilterModel;
2526

2627
return [
2728
h('.flex-row', [
@@ -36,7 +37,7 @@ export const eorReasonFilterComponent = (eorReasonFilterModel, eorReasonTypes) =
3637
h('option', { selected: eorReasonFilterModel.category === '', value: '' }, '-'),
3738
eorReasonsCategories.map((category, index) => h(
3839
`option#eorCategory${index}`,
39-
{ key: category, value: category },
40+
{ key: category, value: category, selected: category === currentCategory },
4041
category,
4142
)),
4243
],
@@ -54,7 +55,7 @@ export const eorReasonFilterComponent = (eorReasonFilterModel, eorReasonTypes) =
5455
.filter((reason) => reason.category === eorReasonFilterModel.category)
5556
.map(({ title }, index) => h(
5657
`option#eorTitle${index}`,
57-
{ key: title, value: title },
58+
{ key: title, value: title, selected: title === currentTitle },
5859
title || '(empty)',
5960
)),
6061
],

lib/public/views/DataPasses/ActiveColumns/dataPassesActiveColumns.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { h } from '/js/src/index.js';
2020
import { formatDataPassName } from '../format/formatDataPassName.js';
2121
import { formatDataPassStatusHistory } from '../format/formatStatusHistory.js';
2222
import { checkboxes } from '../../../components/Filters/common/filters/checkboxFilter.js';
23-
import { textInputFilter } from '../../../components/Filters/common/filters/textInputFilter.js';
23+
import { textFilter } from '../../../components/Filters/common/filters/textFilter.js';
2424

2525
/**
2626
* List of active columns for a generic data passes table
@@ -35,7 +35,7 @@ export const dataPassesActiveColumns = {
3535
visible: true,
3636
sortable: true,
3737
format: (_, dataPass) => formatDataPassName(dataPass),
38-
filter: (filteringModel) => textInputFilter(filteringModel, 'names', 'e.g. LHC22a_apass1, ...', 'w-75'),
38+
filter: (filteringModel) => textFilter(filteringModel.get('names'), { class: 'w-75 mt1', placeholder: 'e.g. LHC22a, lhc23b, ...' }),
3939
balloon: true,
4040
classes: 'w-20',
4141
},

0 commit comments

Comments
 (0)