Skip to content

Commit

Permalink
[O2B-532] Create a reusable filtering system
Browse files Browse the repository at this point in the history
  • Loading branch information
martinboulais committed Nov 26, 2024
1 parent 78e987e commit c6e2d5b
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 35 deletions.
3 changes: 2 additions & 1 deletion lib/public/components/Filters/common/FilterModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Observable } from '/js/src/index.js';

/**
* Model storing the state of a given filter
*
* @abstract
*/
export class FilterModel extends Observable {
Expand Down Expand Up @@ -49,7 +50,7 @@ export class FilterModel extends Observable {
/**
* Returns the normalized value of the filter, that can be used as URL parameter
*
* @return {string|number|object|array|null} the normalized value
* @return {string|number|object|string[]|number[]|null} the normalized value
* @abstract
*/
get normalized() {
Expand Down
117 changes: 117 additions & 0 deletions lib/public/components/Filters/common/FilteringModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* @license
* Copyright CERN and copyright holders of ALICE O2. This software is
* distributed under the terms of the GNU General Public License v3 (GPL
* Version 3), copied verbatim in the file "COPYING".
*
* See http://alice-o2.web.cern.ch/license for full licensing information.
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/

import { Observable } from '/js/src/index.js';

/**
* @typedef FilteringItem
* @property {FilterModel} model the model of the filter
* @property {string} label the label of the filter
*/

/**
* Model representing a filtering system, including filter inputs visibility, filters values and so on
*/
export class FilteringModel extends Observable {
/**
* Constructor
*
* @param {Object<string, FilteringItem>} filters the filters with their label and model
*/
constructor(filters) {
super();

Check warning on line 32 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L31-L32

Added lines #L31 - L32 were not covered by tests

this._visualChange$ = new Observable();

Check warning on line 34 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

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

Added line #L34 was not covered by tests

for (const { model } of filters) {
model.bubbleTo(this);
model.visualChange$?.bubbleTo(this._visualChange$);

Check warning on line 38 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L36-L38

Added lines #L36 - L38 were not covered by tests
}
this._filters = filters;

Check warning on line 40 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

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

Added line #L40 was not covered by tests
}

/**
* Reset the filters
*
* @return {void}
*/
reset() {
for (const { model } of this._filters) {
model.reset();

Check warning on line 50 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L48-L50

Added lines #L48 - L50 were not covered by tests
}
}

/**
* Returns the normalized value of all the filters, without null values
*
* @return {object} the normalized values
*/
get normalized() {
const ret = {};
for (const filterKey in this._filters) {
const filter = this.getFilter(filterKey);
if (filter && !filter.isEmpty) {
ret[filterKey] = filter.normalized;

Check warning on line 64 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L59-L64

Added lines #L59 - L64 were not covered by tests
}
}
return ret;

Check warning on line 67 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

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

Added line #L67 was not covered by tests
}

/**
* States if there is currently at least one filter active
*
* @return {boolean} true if at least one filter is active
*/
isAnyFilterActive() {
for (const { model } of this._filters) {
if (!model.isEmpty) {
return true;

Check warning on line 78 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L75-L78

Added lines #L75 - L78 were not covered by tests
}
}
return false;

Check warning on line 81 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

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

Added line #L81 was not covered by tests
}

/**
* Returns the object storing all the filters models
*
* @param {string} key the key of the model
* @return {FilterModel} the filters store
*/
getFilter(key) {
return this.filters[key];

Check warning on line 91 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L90-L91

Added lines #L90 - L91 were not covered by tests
}

/**
* Returns the list of human-readable names of currently active filters
*
* @return {string} the active filters names
*/
get activeFiltersLabels() {
const ret = [];
for (const { model, label } of this._filters) {
if (!model.isEmpty) {
ret.push(label);

Check warning on line 103 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L99-L103

Added lines #L99 - L103 were not covered by tests
}
}
return ret.join(', ');

Check warning on line 106 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

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

Added line #L106 was not covered by tests
}

/**
* Returns the observable notified any time there is a visual change which has no impact on the actual filtering
*
* @return {Observable} the filters visibility observable
*/
get visualChange$() {
return this._visualChange$;

Check warning on line 115 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L114-L115

Added lines #L114 - L115 were not covered by tests
}
}

This file was deleted.

69 changes: 69 additions & 0 deletions lib/public/utilities/serializeQueryParameters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* @license
* Copyright CERN and copyright holders of ALICE O2. This software is
* distributed under the terms of the GNU General Public License v3 (GPL
* Version 3), copied verbatim in the file "COPYING".
*
* See http://alice-o2.web.cern.ch/license for full licensing information.
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/

/**
* Given a value and a query param prefix, returns a list of key => values representing the corresponding query parameters (null or undefined
* values are dropped)
*
* for example [1, 3] with prefix 'myPrefix' will result in [{key: 'myPrefix[]', value: 1}, {key: 'myPrefix[]', value: 3]
* for example {foo: 1, bar: 3} with prefix 'myPrefix' will result in [{key: 'myPrefix[foo]', value: 1}, {key: 'myPrefix[bar]', value: 3]
*
* @param {string|boolean|number|null|array|object} parameters the parameter to convert to query param
* @param {string} key the query parameter's key
* @return {({key: string, value: (string|number)}|null)[]} the query parameters definition
*/
export const serializeQueryParameters = (parameters, key) => {
if (parameters === null || parameters === undefined) {
return [null];

Check warning on line 27 in lib/public/utilities/serializeQueryParameters.js

View check run for this annotation

Codecov / codecov/patch

lib/public/utilities/serializeQueryParameters.js#L25-L27

Added lines #L25 - L27 were not covered by tests
}

if (Array.isArray(parameters)) {
return parameters.map((parameter) => serializeQueryParameters(parameter, `${key}[]`)).flat();

Check warning on line 31 in lib/public/utilities/serializeQueryParameters.js

View check run for this annotation

Codecov / codecov/patch

lib/public/utilities/serializeQueryParameters.js#L30-L31

Added lines #L30 - L31 were not covered by tests
}

switch (typeof parameters) {
case 'boolean':
return [{ key, value: parameters ? 'true' : 'false' }];
case 'number':
case 'string':
return [{ key, value: parameters }];
case 'object':
return Object.entries(parameters)
.map(([parameterKey, parameter]) => serializeQueryParameters(parameter, `${key}[${parameterKey}]`))

Check warning on line 42 in lib/public/utilities/serializeQueryParameters.js

View check run for this annotation

Codecov / codecov/patch

lib/public/utilities/serializeQueryParameters.js#L34-L42

Added lines #L34 - L42 were not covered by tests
.flat();
default:
return [null];

Check warning on line 45 in lib/public/utilities/serializeQueryParameters.js

View check run for this annotation

Codecov / codecov/patch

lib/public/utilities/serializeQueryParameters.js#L44-L45

Added lines #L44 - L45 were not covered by tests
}
};

/**
* Generate a {URLSearchParams} from an object representing the query parameters
*
* Parameters can be nested ({foo: {bar: 23}}) and values can be an array ({foo: ['bar', 'baz']})
*
* @param {Object} parameters the query parameters
* @return {URLSearchParams} the generated search params
*/
export const generateURLSearchParams = (parameters) => {
const ret = new URLSearchParams();

Check warning on line 58 in lib/public/utilities/serializeQueryParameters.js

View check run for this annotation

Codecov / codecov/patch

lib/public/utilities/serializeQueryParameters.js#L57-L58

Added lines #L57 - L58 were not covered by tests

for (const mainKey in parameters) {
const serializedQueryParameters = serializeQueryParameters(parameters[mainKey], mainKey);
for (const serializedQueryParameter of serializedQueryParameters) {
if (serializedQueryParameter) {
ret.append(serializedQueryParameter.key, serializedQueryParameter.value);

Check warning on line 64 in lib/public/utilities/serializeQueryParameters.js

View check run for this annotation

Codecov / codecov/patch

lib/public/utilities/serializeQueryParameters.js#L60-L64

Added lines #L60 - L64 were not covered by tests
}
}
}
return ret;

Check warning on line 68 in lib/public/utilities/serializeQueryParameters.js

View check run for this annotation

Codecov / codecov/patch

lib/public/utilities/serializeQueryParameters.js#L68

Added line #L68 was not covered by tests
};

0 comments on commit c6e2d5b

Please sign in to comment.