Commit 1a7e6c58 authored by Scott Stern's avatar Scott Stern Committed by Paul Slaughter

Add milestone title token to issues board

https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67460
parent d982b664
...@@ -27,7 +27,13 @@ export default { ...@@ -27,7 +27,13 @@ export default {
}, },
computed: { computed: {
urlParams() { urlParams() {
const { authorUsername, labelName, assigneeUsername, search } = this.filterParams; const {
authorUsername,
labelName,
assigneeUsername,
search,
milestoneTitle,
} = this.filterParams;
let notParams = {}; let notParams = {};
if (Object.prototype.hasOwnProperty.call(this.filterParams, 'not')) { if (Object.prototype.hasOwnProperty.call(this.filterParams, 'not')) {
...@@ -36,6 +42,7 @@ export default { ...@@ -36,6 +42,7 @@ export default {
'not[label_name][]': this.filterParams.not.labelName, 'not[label_name][]': this.filterParams.not.labelName,
'not[author_username]': this.filterParams.not.authorUsername, 'not[author_username]': this.filterParams.not.authorUsername,
'not[assignee_username]': this.filterParams.not.assigneeUsername, 'not[assignee_username]': this.filterParams.not.assigneeUsername,
'not[milestone_title]': this.filterParams.not.milestoneTitle,
}, },
undefined, undefined,
); );
...@@ -46,6 +53,7 @@ export default { ...@@ -46,6 +53,7 @@ export default {
author_username: authorUsername, author_username: authorUsername,
'label_name[]': labelName, 'label_name[]': labelName,
assignee_username: assigneeUsername, assignee_username: assigneeUsername,
milestone_title: milestoneTitle,
search, search,
}; };
}, },
...@@ -64,7 +72,13 @@ export default { ...@@ -64,7 +72,13 @@ export default {
this.performSearch(); this.performSearch();
}, },
getFilteredSearchValue() { getFilteredSearchValue() {
const { authorUsername, labelName, assigneeUsername, search } = this.filterParams; const {
authorUsername,
labelName,
assigneeUsername,
search,
milestoneTitle,
} = this.filterParams;
const filteredSearchValue = []; const filteredSearchValue = [];
if (authorUsername) { if (authorUsername) {
...@@ -90,6 +104,13 @@ export default { ...@@ -90,6 +104,13 @@ export default {
); );
} }
if (milestoneTitle) {
filteredSearchValue.push({
type: 'milestone_title',
value: { data: milestoneTitle, operator: '=' },
});
}
if (this.filterParams['not[authorUsername]']) { if (this.filterParams['not[authorUsername]']) {
filteredSearchValue.push({ filteredSearchValue.push({
type: 'author_username', type: 'author_username',
...@@ -97,6 +118,13 @@ export default { ...@@ -97,6 +118,13 @@ export default {
}); });
} }
if (this.filterParams['not[milestoneTitle]']) {
filteredSearchValue.push({
type: 'milestone_title',
value: { data: this.filterParams['not[milestoneTitle]'], operator: '!=' },
});
}
if (this.filterParams['not[assigneeUsername]']) { if (this.filterParams['not[assigneeUsername]']) {
filteredSearchValue.push({ filteredSearchValue.push({
type: 'assignee_username', type: 'assignee_username',
...@@ -143,6 +171,9 @@ export default { ...@@ -143,6 +171,9 @@ export default {
case 'label_name': case 'label_name':
labels.push(filter.value.data); labels.push(filter.value.data);
break; break;
case 'milestone_title':
filterParams.milestoneTitle = filter.value.data;
break;
case 'filtered-search-term': case 'filtered-search-term':
if (filter.value.data) plainText.push(filter.value.data); if (filter.value.data) plainText.push(filter.value.data);
break; break;
......
<script> <script>
import { mapActions } from 'vuex';
import BoardFilteredSearch from '~/boards/components/board_filtered_search.vue'; import BoardFilteredSearch from '~/boards/components/board_filtered_search.vue';
import issueBoardFilters from '~/boards/issue_board_filters'; import issueBoardFilters from '~/boards/issue_board_filters';
import { TYPE_USER } from '~/graphql_shared/constants'; import { TYPE_USER } from '~/graphql_shared/constants';
...@@ -6,6 +7,7 @@ import { convertToGraphQLId } from '~/graphql_shared/utils'; ...@@ -6,6 +7,7 @@ import { convertToGraphQLId } from '~/graphql_shared/utils';
import { __ } from '~/locale'; import { __ } from '~/locale';
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.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 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';
export default { export default {
i18n: { i18n: {
...@@ -13,6 +15,7 @@ export default { ...@@ -13,6 +15,7 @@ export default {
label: __('Label'), label: __('Label'),
author: __('Author'), author: __('Author'),
assignee: __('Assignee'), assignee: __('Assignee'),
milestone: __('Milestone'),
is: __('is'), is: __('is'),
isNot: __('is not'), isNot: __('is not'),
}, },
...@@ -29,7 +32,7 @@ export default { ...@@ -29,7 +32,7 @@ export default {
}, },
computed: { computed: {
tokens() { tokens() {
const { label, is, isNot, author, assignee } = this.$options.i18n; const { label, is, isNot, author, assignee, milestone } = this.$options.i18n;
const { fetchAuthors, fetchLabels } = issueBoardFilters( const { fetchAuthors, fetchLabels } = issueBoardFilters(
this.$apollo, this.$apollo,
this.fullPath, this.fullPath,
...@@ -77,10 +80,21 @@ export default { ...@@ -77,10 +80,21 @@ export default {
fetchAuthors, fetchAuthors,
preloadedAuthors: this.preloadedAuthors(), preloadedAuthors: this.preloadedAuthors(),
}, },
{
type: 'milestone_title',
title: milestone,
icon: 'clock',
symbol: '%',
token: MilestoneToken,
unique: true,
defaultMilestones: [], // todo: https://gitlab.com/gitlab-org/gitlab/-/issues/337044#note_640010094
fetchMilestones: this.fetchMilestones,
},
]; ];
}, },
}, },
methods: { methods: {
...mapActions(['fetchMilestones']),
preloadedAuthors() { preloadedAuthors() {
return gon?.current_user_id return gon?.current_user_id
? [ ? [
......
...@@ -275,7 +275,6 @@ export default { ...@@ -275,7 +275,6 @@ export default {
avatar_url: gon.current_user_avatar_url, avatar_url: gon.current_user_avatar_url,
}); });
} }
const tokens = [ const tokens = [
{ {
type: TOKEN_TYPE_AUTHOR, type: TOKEN_TYPE_AUTHOR,
......
...@@ -115,6 +115,7 @@ describe('BoardFilteredSearch', () => { ...@@ -115,6 +115,7 @@ describe('BoardFilteredSearch', () => {
{ type: 'author_username', value: { data: 'root', operator: '=' } }, { type: 'author_username', value: { data: 'root', operator: '=' } },
{ type: 'label_name', value: { data: 'label', operator: '=' } }, { type: 'label_name', value: { data: 'label', operator: '=' } },
{ type: 'label_name', value: { data: 'label2', operator: '=' } }, { type: 'label_name', value: { data: 'label2', operator: '=' } },
{ type: 'milestone_title', value: { data: 'New Milestone', operator: '=' } },
]; ];
jest.spyOn(urlUtility, 'updateHistory'); jest.spyOn(urlUtility, 'updateHistory');
findFilteredSearch().vm.$emit('onFilter', mockFilters); findFilteredSearch().vm.$emit('onFilter', mockFilters);
...@@ -122,7 +123,8 @@ describe('BoardFilteredSearch', () => { ...@@ -122,7 +123,8 @@ describe('BoardFilteredSearch', () => {
expect(urlUtility.updateHistory).toHaveBeenCalledWith({ expect(urlUtility.updateHistory).toHaveBeenCalledWith({
title: '', title: '',
replace: true, replace: true,
url: 'http://test.host/?author_username=root&label_name[]=label&label_name[]=label2', url:
'http://test.host/?author_username=root&label_name[]=label&label_name[]=label2&milestone_title=New+Milestone',
}); });
}); });
}); });
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import BoardFilteredSearch from '~/boards/components/board_filtered_search.vue'; import BoardFilteredSearch from '~/boards/components/board_filtered_search.vue';
import IssueBoardFilteredSpec from '~/boards/components/issue_board_filtered_search.vue'; import IssueBoardFilteredSpec from '~/boards/components/issue_board_filtered_search.vue';
import { BoardType } from '~/boards/constants';
import issueBoardFilters from '~/boards/issue_board_filters'; import issueBoardFilters from '~/boards/issue_board_filters';
import { mockTokens } from '../mock_data'; import { mockTokens } from '../mock_data';
jest.mock('~/boards/issue_board_filters');
describe('IssueBoardFilter', () => { describe('IssueBoardFilter', () => {
let wrapper; let wrapper;
const createComponent = ({ initialFilterParams = {} } = {}) => { const createComponent = () => {
wrapper = shallowMount(IssueBoardFilteredSpec, { wrapper = shallowMount(IssueBoardFilteredSpec, {
provide: { initialFilterParams },
props: { fullPath: '', boardType: '' }, props: { fullPath: '', boardType: '' },
}); });
}; };
...@@ -20,7 +20,17 @@ describe('IssueBoardFilter', () => { ...@@ -20,7 +20,17 @@ describe('IssueBoardFilter', () => {
}); });
describe('default', () => { describe('default', () => {
let fetchAuthorsSpy;
let fetchLabelsSpy;
beforeEach(() => { beforeEach(() => {
fetchAuthorsSpy = jest.fn();
fetchLabelsSpy = jest.fn();
issueBoardFilters.mockReturnValue({
fetchAuthors: fetchAuthorsSpy,
fetchLabels: fetchLabelsSpy,
});
createComponent(); createComponent();
}); });
...@@ -28,17 +38,10 @@ describe('IssueBoardFilter', () => { ...@@ -28,17 +38,10 @@ describe('IssueBoardFilter', () => {
expect(wrapper.find(BoardFilteredSearch).exists()).toBe(true); expect(wrapper.find(BoardFilteredSearch).exists()).toBe(true);
}); });
it.each([[BoardType.group], [BoardType.project]])( it('passes the correct tokens to BoardFilteredSearch', () => {
'when boardType is %s we pass the correct tokens to BoardFilteredSearch', const tokens = mockTokens(fetchLabelsSpy, fetchAuthorsSpy, wrapper.vm.fetchMilestones);
(boardType) => {
const { fetchAuthors, fetchLabels } = issueBoardFilters({}, '', boardType);
const tokens = mockTokens(fetchLabels, fetchAuthors); expect(wrapper.find(BoardFilteredSearch).props('tokens')).toEqual(tokens);
});
expect(wrapper.find(BoardFilteredSearch).props('tokens').toString()).toBe(
tokens.toString(),
);
},
);
}); });
}); });
...@@ -8,6 +8,7 @@ import boardsStore from '~/boards/stores/boards_store'; ...@@ -8,6 +8,7 @@ import boardsStore from '~/boards/stores/boards_store';
import { __ } from '~/locale'; import { __ } from '~/locale';
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.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 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';
export const boardObj = { export const boardObj = {
id: 1, id: 1,
...@@ -542,7 +543,7 @@ export const mockMoveData = { ...@@ -542,7 +543,7 @@ export const mockMoveData = {
...mockMoveIssueParams, ...mockMoveIssueParams,
}; };
export const mockTokens = (fetchLabels, fetchAuthors) => [ export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones) => [
{ {
icon: 'labels', icon: 'labels',
title: __('Label'), title: __('Label'),
...@@ -568,6 +569,7 @@ export const mockTokens = (fetchLabels, fetchAuthors) => [ ...@@ -568,6 +569,7 @@ export const mockTokens = (fetchLabels, fetchAuthors) => [
token: AuthorToken, token: AuthorToken,
unique: true, unique: true,
fetchAuthors, fetchAuthors,
preloadedAuthors: [],
}, },
{ {
icon: 'user', icon: 'user',
...@@ -580,5 +582,16 @@ export const mockTokens = (fetchLabels, fetchAuthors) => [ ...@@ -580,5 +582,16 @@ export const mockTokens = (fetchLabels, fetchAuthors) => [
token: AuthorToken, token: AuthorToken,
unique: true, unique: true,
fetchAuthors, fetchAuthors,
preloadedAuthors: [],
},
{
icon: 'clock',
title: __('Milestone'),
symbol: '%',
type: 'milestone_title',
token: MilestoneToken,
unique: true,
defaultMilestones: [],
fetchMilestones,
}, },
]; ];
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