Commit 9ca3de45 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'ss/add-iteration-token' into 'master'

Add iteration token to the board issues filter

See merge request gitlab-org/gitlab!72341
parents d50cba37 31192e2f
......@@ -39,6 +39,7 @@ export default {
assigneeUsername,
search,
milestoneTitle,
iterationId,
types,
weight,
epicId,
......@@ -83,6 +84,13 @@ export default {
});
}
if (iterationId) {
filteredSearchValue.push({
type: 'iteration',
value: { data: iterationId, operator: '=' },
});
}
if (weight) {
filteredSearchValue.push({
type: 'weight',
......@@ -118,6 +126,13 @@ export default {
});
}
if (this.filterParams['not[iteration_id]']) {
filteredSearchValue.push({
type: 'iteration_id',
value: { data: this.filterParams['not[iteration_id]'], operator: '!=' },
});
}
if (this.filterParams['not[weight]']) {
filteredSearchValue.push({
type: 'weight',
......@@ -179,8 +194,8 @@ export default {
weight,
epicId,
myReactionEmoji,
iterationId,
} = this.filterParams;
let notParams = {};
if (Object.prototype.hasOwnProperty.call(this.filterParams, 'not')) {
......@@ -194,6 +209,7 @@ export default {
'not[weight]': this.filterParams.not.weight,
'not[epic_id]': this.filterParams.not.epicId,
'not[my_reaction_emoji]': this.filterParams.not.myReactionEmoji,
'not[iteration_id]': this.filterParams.not.iterationId,
},
undefined,
);
......@@ -205,6 +221,7 @@ export default {
'label_name[]': labelName,
assignee_username: assigneeUsername,
milestone_title: milestoneTitle,
iteration_id: iterationId,
search,
types,
weight,
......@@ -261,6 +278,9 @@ export default {
case 'milestone_title':
filterParams.milestoneTitle = filter.value.data;
break;
case 'iteration':
filterParams.iterationId = filter.value.data;
break;
case 'weight':
filterParams.weight = filter.value.data;
break;
......
......@@ -12,6 +12,7 @@ import { __ } from '~/locale';
import {
DEFAULT_MILESTONES_GRAPHQL,
TOKEN_TITLE_MY_REACTION,
OPERATOR_IS_AND_IS_NOT,
} from '~/vue_shared/components/filtered_search_bar/constants';
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue';
......@@ -35,8 +36,6 @@ export default {
issue: __('Issue'),
milestone: __('Milestone'),
weight: __('Weight'),
is: __('is'),
isNot: __('is not'),
},
components: { BoardFilteredSearch },
inject: ['isSignedIn'],
......@@ -62,8 +61,6 @@ export default {
tokensCE() {
const {
label,
is,
isNot,
author,
assignee,
issue,
......@@ -84,10 +81,7 @@ export default {
icon: 'user',
title: assignee,
type: 'assignee_username',
operators: [
{ value: '=', description: is },
{ value: '!=', description: isNot },
],
operators: OPERATOR_IS_AND_IS_NOT,
token: AuthorToken,
unique: true,
fetchAuthors,
......@@ -97,10 +91,7 @@ export default {
icon: 'pencil',
title: author,
type: 'author_username',
operators: [
{ value: '=', description: is },
{ value: '!=', description: isNot },
],
operators: OPERATOR_IS_AND_IS_NOT,
symbol: '@',
token: AuthorToken,
unique: true,
......@@ -111,10 +102,7 @@ export default {
icon: 'labels',
title: label,
type: 'label_name',
operators: [
{ value: '=', description: is },
{ value: '!=', description: isNot },
],
operators: OPERATOR_IS_AND_IS_NOT,
token: LabelToken,
unique: false,
symbol: '~',
......
......@@ -373,7 +373,6 @@ export default {
commit(types.REQUEST_ITEMS_FOR_LIST, { listId, fetchNext });
const { fullPath, fullBoardId, boardType, filterParams } = state;
const variables = {
fullPath,
boardId: fullBoardId,
......
<script>
import { GlDropdownDivider, GlDropdownSectionHeader, GlFilteredSearchSuggestion } from '@gitlab/ui';
import createFlash from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { __ } from '~/locale';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
import { formatDate } from '~/lib/utils/datetime_utility';
......@@ -43,7 +42,7 @@ export default {
},
methods: {
getActiveIteration(iterations, data) {
return iterations.find((iteration) => this.getValue(iteration) === data);
return iterations.find((iteration) => iteration.id === data);
},
groupIterationsByCadence(iterations) {
const cadences = [];
......@@ -80,9 +79,6 @@ export default {
this.loading = false;
});
},
getValue(iteration) {
return String(getIdFromGraphQLId(iteration.id));
},
/**
* TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/344619
* This method also exists as a utility function in ee/../iterations/utils.js
......@@ -125,7 +121,7 @@ export default {
<gl-filtered-search-suggestion
v-for="iteration in cadence.iterations"
:key="iteration.id"
:value="getValue(iteration)"
:value="iteration.id"
>
{{ iteration.title }}
<div v-if="glFeatures.iterationCadences" class="gl-text-gray-400">
......
......@@ -2,17 +2,23 @@
// This is a false violation of @gitlab/no-runtime-template-compiler, since it
// extends a valid Vue single file component.
/* eslint-disable @gitlab/no-runtime-template-compiler */
import { mapActions } from 'vuex';
import IssueBoardFilteredSearchFoss from '~/boards/components/issue_board_filtered_search.vue';
import { BoardType } from '~/boards/constants';
import { __ } from '~/locale';
import { OPERATOR_IS_AND_IS_NOT } from '~/vue_shared/components/filtered_search_bar/constants';
import EpicToken from '~/vue_shared/components/filtered_search_bar/tokens/epic_token.vue';
import IterationToken from '~/vue_shared/components/filtered_search_bar/tokens/iteration_token.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
extends: IssueBoardFilteredSearchFoss,
i18n: {
...IssueBoardFilteredSearchFoss.i18n,
epic: __('Epic'),
iteration: __('Iteration'),
},
mixins: [glFeatureFlagMixin()],
computed: {
isGroupBoard() {
return this.boardType === BoardType.group;
......@@ -23,7 +29,7 @@ export default {
: this.fullPath.slice(0, this.fullPath.lastIndexOf('/'));
},
tokens() {
const { epic } = this.$options.i18n;
const { epic, iteration } = this.$options.i18n;
return [
...this.tokensCE,
......@@ -38,8 +44,24 @@ export default {
useIdValue: true,
fullPath: this.epicsGroupPath,
},
...(this.glFeatures.iterationCadences
? [
{
icon: 'iteration',
title: iteration,
type: 'iteration',
operators: OPERATOR_IS_AND_IS_NOT,
token: IterationToken,
unique: true,
fetchIterations: this.fetchIterations,
},
]
: []),
];
},
},
methods: {
...mapActions(['fetchIterations']),
},
};
</script>
#import "../../sidebar/queries/iteration.fragment.graphql"
query GroupBoardIterations($fullPath: ID!, $title: String) {
group(fullPath: $fullPath) {
iterations(includeAncestors: true, title: $title) {
nodes {
id
title
...IterationFragment
}
}
}
......
#import "../../sidebar/queries/iteration.fragment.graphql"
query ProjectBoardIterations($fullPath: ID!, $title: String) {
project(fullPath: $fullPath) {
iterations(includeAncestors: true, title: $title) {
nodes {
id
title
...IterationFragment
}
}
}
......
......@@ -301,6 +301,7 @@ export default {
commit(types.REQUEST_ITEMS_FOR_LIST, { listId, fetchNext });
const { epicId, ...filterParams } = state.filterParams;
if (noEpicIssues && epicId !== undefined) {
return null;
}
......
......@@ -12,7 +12,12 @@ describe('IssueBoardFilter', () => {
const createComponent = () => {
wrapper = shallowMount(IssueBoardFilteredSpec, {
propsData: { fullPath: 'gitlab-org', boardType: 'group' },
provide: { isSignedIn: true },
provide: {
isSignedIn: true,
glFeatures: {
iterationCadences: true,
},
},
});
};
......@@ -42,7 +47,12 @@ describe('IssueBoardFilter', () => {
});
it('passes the correct tokens to BoardFilteredSearch including epics', () => {
const tokens = mockTokens(fetchLabelsSpy, fetchAuthorsSpy, wrapper.vm.fetchMilestones);
const tokens = mockTokens(
fetchLabelsSpy,
fetchAuthorsSpy,
wrapper.vm.fetchMilestones,
wrapper.vm.fetchIterations,
);
expect(wrapper.find(BoardFilteredSearch).props('tokens')).toEqual(tokens);
});
......
......@@ -7,6 +7,7 @@ import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji
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 WeightToken from '~/vue_shared/components/filtered_search_bar/tokens/weight_token.vue';
import IterationToken from '~/vue_shared/components/filtered_search_bar/tokens/iteration_token.vue';
export const mockEpicBoardResponse = {
data: {
......@@ -398,7 +399,7 @@ export const mockGroup2 = {
export const mockSubGroups = [mockGroup0, mockGroup1, mockGroup2];
export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones) => [
export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones, fetchIterations) => [
{
icon: 'user',
title: __('Assignee'),
......@@ -486,4 +487,16 @@ export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones) => [
useIdValue: true,
fullPath: 'gitlab-org',
},
{
type: 'iteration',
icon: 'iteration',
title: 'Iteration',
operators: [
{ value: '=', description: 'is' },
{ value: '!=', description: 'is not' },
],
unique: true,
fetchIterations,
token: IterationToken,
},
];
......@@ -123,6 +123,7 @@ describe('BoardFilteredSearch', () => {
{ type: 'milestone_title', value: { data: 'New Milestone', operator: '=' } },
{ type: 'types', value: { data: 'INCIDENT', operator: '=' } },
{ type: 'weight', value: { data: '2', operator: '=' } },
{ type: 'iteration', value: { data: '3341', operator: '=' } },
];
jest.spyOn(urlUtility, 'updateHistory');
findFilteredSearch().vm.$emit('onFilter', mockFilters);
......@@ -131,7 +132,7 @@ describe('BoardFilteredSearch', () => {
title: '',
replace: true,
url:
'http://test.host/?author_username=root&label_name[]=label&label_name[]=label2&milestone_title=New+Milestone&types=INCIDENT&weight=2',
'http://test.host/?author_username=root&label_name[]=label&label_name[]=label2&milestone_title=New+Milestone&iteration_id=3341&types=INCIDENT&weight=2',
});
});
});
......
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