Commit 6b5b7b4e authored by Kushal Pandya's avatar Kushal Pandya

Add milestone filtering support in Roadmap

Adds Milestone token support in filtered search for
Roadmap.
parent 75ce65df
......@@ -16,6 +16,7 @@ import { visitUrl, mergeUrlParams, updateHistory, setUrlParams } from '~/lib/uti
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
import { EPICS_STATES, PRESET_TYPES } from '../constants';
......@@ -59,6 +60,7 @@ export default {
'sortedBy',
'fullPath',
'groupLabelsEndpoint',
'groupMilestonesEndpoint',
'filterParams',
]),
selectedEpicStateTitle() {
......@@ -108,10 +110,32 @@ export default {
});
},
},
{
type: 'milestone_title',
icon: 'clock',
title: __('Milestone'),
unique: true,
symbol: '%',
token: MilestoneToken,
operators: [{ value: '=', description: __('is'), default: 'true' }],
fetchMilestones: (search = '') => {
return axios.get(this.groupMilestonesEndpoint).then(({ data }) => {
// TODO: Remove below condition check once either of the following is supported.
// a) Milestones Private API supports search param.
// b) Milestones Public API supports including child projects' milestones.
if (search) {
return {
data: data.filter(m => m.title.toLowerCase().includes(search.toLowerCase())),
};
}
return { data };
});
},
},
];
},
getFilteredSearchValue() {
const { authorUsername, labelName, search } = this.filterParams || {};
const { authorUsername, labelName, milestoneTitle, search } = this.filterParams || {};
const filteredSearchValue = [];
if (authorUsername) {
......@@ -121,6 +145,13 @@ export default {
});
}
if (milestoneTitle) {
filteredSearchValue.push({
type: 'milestone_title',
value: { data: milestoneTitle },
});
}
if (labelName?.length) {
filteredSearchValue.push(
...labelName.map(label => ({
......@@ -138,7 +169,7 @@ export default {
},
updateUrl() {
const queryParams = urlParamsToObject(window.location.search);
const { authorUsername, labelName, search } = this.filterParams || {};
const { authorUsername, labelName, milestoneTitle, search } = this.filterParams || {};
queryParams.state = this.epicsState;
queryParams.sort = this.sortedBy;
......@@ -149,6 +180,12 @@ export default {
delete queryParams.author_username;
}
if (milestoneTitle) {
queryParams.milestone_title = milestoneTitle;
} else {
delete queryParams.milestone_title;
}
delete queryParams.label_name;
if (labelName?.length) {
queryParams['label_name[]'] = labelName;
......@@ -182,10 +219,18 @@ export default {
filters.forEach(filter => {
if (typeof filter === 'object') {
if (filter.type === 'author_username') {
filterParams.authorUsername = filter.value.data;
} else if (filter.type === 'label_name') {
labels.push(filter.value.data);
switch (filter.type) {
case 'author_username':
filterParams.authorUsername = filter.value.data;
break;
case 'milestone_title':
filterParams.milestoneTitle = filter.value.data;
break;
case 'label_name':
labels.push(filter.value.data);
break;
default:
break;
}
} else {
filterParams.search = filter;
......
......@@ -8,6 +8,7 @@ query groupEpics(
$dueDate: Time
$labelName: [String!] = []
$authorUsername: String = ""
$milestoneTitle: String = ""
$search: String = ""
) {
group(fullPath: $fullPath) {
......@@ -20,6 +21,7 @@ query groupEpics(
endDate: $dueDate
labelName: $labelName
authorUsername: $authorUsername
milestoneTitle: $milestoneTitle
search: $search
) {
edges {
......
......@@ -87,6 +87,7 @@ export default () => {
epicIid: dataset.iid,
newEpicEndpoint: dataset.newEpicEndpoint,
groupLabelsEndpoint: dataset.groupLabelsEndpoint,
groupMilestonesEndpoint: dataset.groupMilestonesEndpoint,
epicsState: dataset.epicsState,
sortedBy: dataset.sortedBy,
filterQueryString,
......@@ -110,6 +111,7 @@ export default () => {
filterParams: this.filterParams,
initialEpicsPath: this.initialEpicsPath,
groupLabelsEndpoint: this.groupLabelsEndpoint,
groupMilestonesEndpoint: this.groupMilestonesEndpoint,
defaultInnerHeight: this.defaultInnerHeight,
isChildEpics: this.isChildEpics,
hasFiltersApplied: this.hasFiltersApplied,
......
......@@ -6,6 +6,7 @@ export default () => ({
initialEpicsPath: '',
filterParams: null,
groupLabelsEndpoint: '',
groupMilestonesEndpoint: '',
// Data
epicIid: '',
......
......@@ -32,6 +32,7 @@
has_filters_applied: "#{has_filters_applied}",
new_epic_endpoint: group_epics_path(@group),
group_labels_endpoint: group_labels_path(@group, format: :json),
group_milestones_endpoint: group_milestones_path(@group, format: :json),
preset_type: roadmap_layout,
epics_state: @epics_state,
sorted_by: @sort,
......
......@@ -9,6 +9,7 @@ import {
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
import { visitUrl, mergeUrlParams, updateHistory } from '~/lib/utils/url_utility';
......@@ -93,6 +94,7 @@ describe('RoadmapFilters', () => {
wrapper.vm.$store.dispatch('setFilterParams', {
authorUsername: 'root',
labelName: ['Bug'],
milestoneTitle: '4.0',
});
wrapper.vm.$store.dispatch('setSortedBy', 'end_date_asc');
......@@ -101,7 +103,7 @@ describe('RoadmapFilters', () => {
wrapper.vm.updateUrl();
expect(global.window.location.href).toBe(
`${TEST_HOST}/?state=${EPICS_STATES.CLOSED}&sort=end_date_asc&author_username=root&label_name%5B%5D=Bug`,
`${TEST_HOST}/?state=${EPICS_STATES.CLOSED}&sort=end_date_asc&author_username=root&milestone_title=4.0&label_name%5B%5D=Bug`,
);
});
});
......@@ -186,6 +188,16 @@ describe('RoadmapFilters', () => {
operators: [{ value: '=', description: 'is', default: 'true' }],
fetchLabels: expect.any(Function),
},
{
type: 'milestone_title',
icon: 'clock',
title: 'Milestone',
unique: true,
symbol: '%',
token: MilestoneToken,
operators: [{ value: '=', description: 'is', default: 'true' }],
fetchMilestones: expect.any(Function),
},
]);
});
......
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