Commit 03eeb688 authored by Brandon Labuschagne's avatar Brandon Labuschagne

Merge branch 'ek-add-extract-filter-params-util' into 'master'

Extract helpers for filter and pagination params

See merge request gitlab-org/gitlab!71917
parents 58ee9026 378790eb
import dateFormat from 'dateformat';
import { urlQueryToFilter } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
import { dateFormats } from './constants';
export const filterBySearchTerm = (data = [], searchTerm = '', filterByKey = 'name') => {
......@@ -7,3 +8,45 @@ export const filterBySearchTerm = (data = [], searchTerm = '', filterByKey = 'na
};
export const toYmd = (date) => dateFormat(date, dateFormats.isoDate);
/**
* Takes a url and extracts query parameters used for the shared
* filter bar
*
* @param {string} url The URL to extract query parameters from
* @returns {Object}
*/
export const extractFilterQueryParameters = (url = '') => {
const {
source_branch_name = null,
target_branch_name = null,
author_username = null,
milestone_title = null,
assignee_username = [],
label_name = [],
} = urlQueryToFilter(url);
return {
selectedSourceBranch: source_branch_name,
selectedTargetBranch: target_branch_name,
selectedAuthor: author_username,
selectedMilestone: milestone_title,
selectedAssigneeList: assignee_username,
selectedLabelList: label_name,
};
};
/**
* Takes a url and extracts sorting and pagination query parameters into an object
*
* @param {string} url The URL to extract query parameters from
* @returns {Object}
*/
export const extractPaginationQueryParameters = (url = '') => {
const { sort, direction, page } = urlQueryToFilter(url);
return {
sort: sort?.value || null,
direction: direction?.value || null,
page: page?.value || null,
};
};
import Vue from 'vue';
import { urlQueryToFilter } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
import { extractFilterQueryParameters } from '~/analytics/shared/utils';
import CodeAnalyticsApp from './components/app.vue';
import store from './store';
......@@ -20,11 +20,11 @@ export default () => {
labelsEndpoint: labelsPath,
projectEndpoint: projectPath,
});
const { milestone_title = null, label_name = [] } = urlQueryToFilter(window.location.search);
store.dispatch('filters/initialize', {
selectedMilestone: milestone_title,
selectedLabelList: label_name,
});
const { selectedMilestone, selectedLabelList } = extractFilterQueryParameters(
window.location.search,
);
store.dispatch('filters/initialize', { selectedMilestone, selectedLabelList });
// eslint-disable-next-line no-new
new Vue({
......
......@@ -2,7 +2,10 @@ import { GlToast } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import { urlQueryToFilter } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
import {
extractFilterQueryParameters,
extractPaginationQueryParameters,
} from '~/analytics/shared/utils';
import { buildCycleAnalyticsInitialData } from '../shared/utils';
import CycleAnalytics from './components/base.vue';
import createStore from './store';
......@@ -20,27 +23,21 @@ export default () => {
const initialData = buildCycleAnalyticsInitialData(el.dataset);
const store = createStore();
const pagination = extractPaginationQueryParameters(window.location.search);
const {
author_username = null,
milestone_title = null,
assignee_username = [],
label_name = [],
sort,
direction,
page,
} = urlQueryToFilter(window.location.search);
selectedAuthor,
selectedMilestone,
selectedAssigneeList,
selectedLabelList,
} = extractFilterQueryParameters(window.location.search);
store.dispatch('initializeCycleAnalytics', {
...initialData,
selectedAuthor: author_username,
selectedMilestone: milestone_title,
selectedAssigneeList: assignee_username,
selectedLabelList: label_name,
pagination: {
sort: sort?.value || null,
direction: direction?.value || null,
page: page?.value || null,
},
selectedAuthor,
selectedMilestone,
selectedAssigneeList,
selectedLabelList,
pagination,
});
return new Vue({
......
......@@ -3,7 +3,7 @@ import VueApollo from 'vue-apollo';
import { ITEM_TYPE } from '~/groups/constants';
import createDefaultClient from '~/lib/graphql';
import { getParameterValues } from '~/lib/utils/url_utility';
import { urlQueryToFilter } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
import { extractFilterQueryParameters } from '~/analytics/shared/utils';
import MergeRequestAnalyticsApp from './components/app.vue';
import createStore from './store';
import { parseAndValidateDates } from './utils';
......@@ -28,21 +28,23 @@ export default () => {
groupEndpoint: type === ITEM_TYPE.GROUP ? fullPath : null,
projectEndpoint: type === ITEM_TYPE.PROJECT ? fullPath : null,
});
const {
source_branch_name = null,
target_branch_name = null,
assignee_username = null,
author_username = null,
milestone_title = null,
label_name = [],
} = urlQueryToFilter(window.location.search);
selectedSourceBranch,
selectedTargetBranch,
selectedAssignee,
selectedAuthor,
selectedMilestone,
selectedLabelList,
} = extractFilterQueryParameters(window.location.search);
store.dispatch('filters/initialize', {
selectedSourceBranch: source_branch_name,
selectedTargetBranch: target_branch_name,
selectedAssignee: assignee_username,
selectedAuthor: author_username,
selectedMilestone: milestone_title,
selectedLabelList: label_name,
selectedSourceBranch,
selectedTargetBranch,
selectedAssignee,
selectedAuthor,
selectedMilestone,
selectedLabelList,
});
const { startDate, endDate } = parseAndValidateDates(
......
import { filterBySearchTerm } from '~/analytics/shared/utils';
import {
filterBySearchTerm,
extractFilterQueryParameters,
extractPaginationQueryParameters,
} from '~/analytics/shared/utils';
import { objectToQuery } from '~/lib/utils/url_utility';
describe('filterBySearchTerm', () => {
const data = [
......@@ -22,3 +27,102 @@ describe('filterBySearchTerm', () => {
expect(filterBySearchTerm(data, 'ne', 'title')).toEqual([data[0]]);
});
});
describe('extractFilterQueryParameters', () => {
const selectedAuthor = 'Author 1';
const selectedMilestone = 'Milestone 1.0';
const selectedSourceBranch = 'main';
const selectedTargetBranch = 'feature-1';
const selectedAssigneeList = ['Alice', 'Bob'];
const selectedLabelList = ['Label 1', 'Label 2'];
const queryParamsString = objectToQuery({
source_branch_name: selectedSourceBranch,
target_branch_name: selectedTargetBranch,
author_username: selectedAuthor,
milestone_title: selectedMilestone,
assignee_username: selectedAssigneeList,
label_name: selectedLabelList,
});
it('extracts the correct filter parameters from a url', () => {
const result = extractFilterQueryParameters(queryParamsString);
const operator = '=';
const expectedFilters = {
selectedAssigneeList: { operator, value: selectedAssigneeList.join(',') },
selectedLabelList: { operator, value: selectedLabelList.join(',') },
selectedAuthor: { operator, value: selectedAuthor },
selectedMilestone: { operator, value: selectedMilestone },
selectedSourceBranch: { operator, value: selectedSourceBranch },
selectedTargetBranch: { operator, value: selectedTargetBranch },
};
expect(result).toMatchObject(expectedFilters);
});
it('returns null for missing parameters', () => {
const result = extractFilterQueryParameters('');
const expectedFilters = {
selectedAuthor: null,
selectedMilestone: null,
selectedSourceBranch: null,
selectedTargetBranch: null,
};
expect(result).toMatchObject(expectedFilters);
});
it('only returns the parameters we expect', () => {
const result = extractFilterQueryParameters('foo="one"&bar="two"');
const resultKeys = Object.keys(result);
['foo', 'bar'].forEach((key) => {
expect(resultKeys).not.toContain(key);
});
[
'selectedAuthor',
'selectedMilestone',
'selectedSourceBranch',
'selectedTargetBranch',
'selectedAssigneeList',
'selectedLabelList',
].forEach((key) => {
expect(resultKeys).toContain(key);
});
});
it('returns an empty array for missing list parameters', () => {
const result = extractFilterQueryParameters('');
const expectedFilters = { selectedAssigneeList: [], selectedLabelList: [] };
expect(result).toMatchObject(expectedFilters);
});
});
describe('extractPaginationQueryParameters', () => {
const sort = 'title';
const direction = 'asc';
const page = '1';
const queryParamsString = objectToQuery({ sort, direction, page });
it('extracts the correct filter parameters from a url', () => {
const result = extractPaginationQueryParameters(queryParamsString);
const expectedFilters = { sort, page, direction };
expect(result).toMatchObject(expectedFilters);
});
it('returns null for missing parameters', () => {
const result = extractPaginationQueryParameters('');
const expectedFilters = { sort: null, direction: null, page: null };
expect(result).toMatchObject(expectedFilters);
});
it('only returns the parameters we expect', () => {
const result = extractPaginationQueryParameters('foo="one"&bar="two"&qux="three"');
const resultKeys = Object.keys(result);
['foo', 'bar', 'qux'].forEach((key) => {
expect(resultKeys).not.toContain(key);
});
['sort', 'page', 'direction'].forEach((key) => {
expect(resultKeys).toContain(key);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment