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 Apr 22, 2024
1 parent cb377a7 commit 0ab6ff5
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();

this._visualChange$ = new Observable();

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

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

/**
* 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;
}
}
return ret;
}

/**
* 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;
}
}
return false;
}

/**
* 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];
}

/**
* 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);
}
}
return ret.join(', ');
}

/**
* 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$;
}
}

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];
}

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

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}]`))
.flat();
default:
return [null];
}
};

/**
* 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();

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

0 comments on commit 0ab6ff5

Please sign in to comment.