Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-08-04 15:06:55 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-08-04 15:06:55 +0300
commit30a8e054751fe9020a9ed526434db83af35b4718 (patch)
tree705dc26a080a7110a653f72a3200f8c718858892 /app/assets/javascripts/vue_shared/components
parentf797f00ec769812339968010af178b3d51a4c460 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/vue_shared/components')
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_utils.js117
1 files changed, 85 insertions, 32 deletions
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_utils.js b/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_utils.js
index 96a0ca9eb01..d33c0bb4708 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_utils.js
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_utils.js
@@ -62,17 +62,30 @@ export function prepareTokens(filters = {}) {
}, []);
}
+/**
+ * This function takes a token array and translates it into a filter object
+ * @param filters
+ * @returns A Filter Object
+ */
export function processFilters(filters) {
return filters.reduce((acc, token) => {
- const { type, value } = token;
- const { operator } = value;
- const tokenValue = value.data;
+ let type;
+ let value;
+ let operator;
+ if (typeof token === 'string') {
+ type = FILTERED_SEARCH_TERM;
+ value = token;
+ } else {
+ type = token.type;
+ operator = token.value.operator;
+ value = token.value.data;
+ }
if (!acc[type]) {
acc[type] = [];
}
- acc[type].push({ value: tokenValue, operator });
+ acc[type].push({ value, operator });
return acc;
}, {});
}
@@ -89,59 +102,93 @@ function filteredSearchQueryParam(filter) {
* { myFilterName: { value: 'foo', operator: '=' }, search: [{ value: 'my' }, { value: 'search' }] }
* gets translated into:
* { myFilterName: 'foo', 'not[myFilterName]': null, search: 'my search' }
+ * By default it supports '=' and '!=' operators. This can be extended by providing the `customOperators` option
* @param {Object} filters
* @param {Object} filters.myFilterName a single filter value or an array of filters
* @param {Object} options
* @param {Object} [options.filteredSearchTermKey] if set, 'filtered-search-term' filters are assigned to this key, 'search' is suggested
+ * @param {Object} [options.customOperators] Allows to extend the supported operators, e.g.
+ *
+ * filterToQueryObject({foo: [{ value: '100', operator: '>' }]}, {customOperators: {operator: '>',prefix: 'gt'}})
+ * returns {gt[foo]: '100'}
+ * It's also possible to restrict custom operators to a given key by setting `applyOnlyToKey` string attribute.
+ *
* @return {Object} query object with both filter name and not-name with values
*/
export function filterToQueryObject(filters = {}, options = {}) {
- const { filteredSearchTermKey } = options;
+ const { filteredSearchTermKey, customOperators } = options;
return Object.keys(filters).reduce((memo, key) => {
const filter = filters[key];
- if (typeof filteredSearchTermKey === 'string' && key === FILTERED_SEARCH_TERM) {
+ if (typeof filteredSearchTermKey === 'string' && key === FILTERED_SEARCH_TERM && filter) {
return { ...memo, [filteredSearchTermKey]: filteredSearchQueryParam(filter) };
}
- let selected;
- let unselected;
+ const operators = [
+ { operator: '=' },
+ { operator: '!=', prefix: 'not' },
+ ...(customOperators ?? []),
+ ];
- if (Array.isArray(filter)) {
- selected = filter.filter((item) => item.operator === '=').map((item) => item.value);
- unselected = filter.filter((item) => item.operator === '!=').map((item) => item.value);
- } else {
- selected = filter?.operator === '=' ? filter.value : null;
- unselected = filter?.operator === '!=' ? filter.value : null;
- }
+ const result = {};
- if (isEmpty(selected)) {
- selected = null;
- }
- if (isEmpty(unselected)) {
- unselected = null;
+ for (const op of operators) {
+ const { operator, prefix, applyOnlyToKey } = op;
+
+ if (!applyOnlyToKey || applyOnlyToKey === key) {
+ let value;
+ if (Array.isArray(filter)) {
+ value = filter.filter((item) => item.operator === operator).map((item) => item.value);
+ } else {
+ value = filter?.operator === operator ? filter.value : null;
+ }
+ if (isEmpty(value)) {
+ value = null;
+ }
+ if (prefix) {
+ result[`${prefix}[${key}]`] = value;
+ } else {
+ result[key] = value;
+ }
+ }
}
- return { ...memo, [key]: selected, [`not[${key}]`]: unselected };
+ return { ...memo, ...result };
}, {});
}
/**
- * Extracts filter name from url name, e.g. `not[my_filter]` => `my_filter`
- * and returns the operator with it depending on the filter name
+ * Extracts filter name from url name and operator, e.g.
+ * e.g. input: not[my_filter]` output: {filterName: `my_filter`, operator: '!='}`
+ *
+ * By default it supports filter with the format `my_filter=foo` and `not[my_filter]=bar`. This can be extended with the `customOperators` option.
* @param {String} filterName from url
+ * @param {Object.customOperators} It allows to extend the supported parameter, e.g.
+ * input: 'gt[filter]', { customOperators: [{ operator: '>', prefix: 'gt' }]})
+ * output: '{filterName: 'filter', operator: '>'}
* @return {Object}
* @return {Object.filterName} extracted filter name
* @return {Object.operator} `=` or `!=`
*/
-function extractNameAndOperator(filterName) {
- // eslint-disable-next-line @gitlab/require-i18n-strings
- if (filterName.startsWith('not[') && filterName.endsWith(']')) {
- return { filterName: filterName.slice(4, -1), operator: '!=' };
- }
+function extractNameAndOperator(filterName, customOperators) {
+ const ops = [
+ {
+ prefix: 'not',
+ operator: '!=',
+ },
+ ...(customOperators ?? []),
+ ];
+
+ const operator = ops.find(
+ ({ prefix }) => filterName.startsWith(`${prefix}[`) && filterName.endsWith(']'),
+ );
- return { filterName, operator: '=' };
+ if (!operator) {
+ return { filterName, operator: '=' };
+ }
+ const { prefix } = operator;
+ return { filterName: filterName.slice(prefix.length + 1, -1), operator: operator.operator };
}
/**
@@ -159,14 +206,20 @@ function filteredSearchTermValue(value) {
* '?myFilterName=foo'
* gets translated into:
* { myFilterName: { value: 'foo', operator: '=' } }
+ * By default it only support '=' and '!=' operators. This can be extended with the customOperator option.
* @param {String|Object} query URL query string or object, e.g. from `window.location.search` or `this.$route.query`
* @param {Object} options
- * @param {Object} options
* @param {String} [options.filteredSearchTermKey] if set, a FILTERED_SEARCH_TERM filter is created to this parameter. `'search'` is suggested
* @param {String[]} [options.filterNamesAllowList] if set, only this list of filters names is mapped
+ * @param {Object} [options.customOperator] It allows to extend the supported parameter, e.g.
+ * input: 'gt[myFilter]=100', { customOperators: [{ operator: '>', prefix: 'gt' }]})
+ * output: '{ myFilter: {value: '100', operator: '>'}}
* @return {Object} filter object with filter names and their values
*/
-export function urlQueryToFilter(query = '', { filteredSearchTermKey, filterNamesAllowList } = {}) {
+export function urlQueryToFilter(
+ query = '',
+ { filteredSearchTermKey, filterNamesAllowList, customOperators } = {},
+) {
const filters = isString(query) ? queryToObject(query, { gatherArrays: true }) : query;
return Object.keys(filters).reduce((memo, key) => {
const value = filters[key];
@@ -180,7 +233,7 @@ export function urlQueryToFilter(query = '', { filteredSearchTermKey, filterName
};
}
- const { filterName, operator } = extractNameAndOperator(key);
+ const { filterName, operator } = extractNameAndOperator(key, customOperators);
if (filterNamesAllowList && !filterNamesAllowList.includes(filterName)) {
return memo;
}