Commit 4e034e1b authored by Paul Slaughter's avatar Paul Slaughter

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

Add milestone title token to issues board

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