Commit 7e242b28 authored by Phil Hughes's avatar Phil Hughes

Merge branch '337688-on-graphql-boards-add-support-for-milestone-wildcard-search' into 'master'

Add support for milestoneWildCardId for boards

See merge request gitlab-org/gitlab!74573
parents 574144e1 8ee44515
import { sortBy, cloneDeep } from 'lodash'; import { sortBy, cloneDeep } from 'lodash';
import { isGid } from '~/graphql_shared/utils'; import { isGid } from '~/graphql_shared/utils';
import { ListType, MilestoneIDs } from './constants'; import { ListType, MilestoneIDs, AssigneeFilterType, MilestoneFilterType } from './constants';
export function getMilestone() { export function getMilestone() {
return null; return null;
...@@ -186,6 +186,7 @@ export function isListDraggable(list) { ...@@ -186,6 +186,7 @@ export function isListDraggable(list) {
export const FiltersInfo = { export const FiltersInfo = {
assigneeUsername: { assigneeUsername: {
negatedSupport: true, negatedSupport: true,
remap: (k, v) => (v === AssigneeFilterType.any ? 'assigneeWildcardId' : k),
}, },
assigneeId: { assigneeId: {
// assigneeId should be renamed to assigneeWildcardId. // assigneeId should be renamed to assigneeWildcardId.
...@@ -204,6 +205,11 @@ export const FiltersInfo = { ...@@ -204,6 +205,11 @@ export const FiltersInfo = {
}, },
milestoneTitle: { milestoneTitle: {
negatedSupport: true, negatedSupport: true,
remap: (k, v) => (Object.values(MilestoneFilterType).includes(v) ? 'milestoneWildcardId' : k),
},
milestoneWildcardId: {
negatedSupport: true,
transform: (val) => val.toUpperCase(),
}, },
myReactionEmoji: { myReactionEmoji: {
negatedSupport: true, negatedSupport: true,
......
<script> <script>
import { pickBy, isEmpty } from 'lodash'; import { pickBy, isEmpty } from 'lodash';
import { mapActions } from 'vuex'; import { mapActions } from 'vuex';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId, isGid } from '~/graphql_shared/utils';
import { updateHistory, setUrlParams } from '~/lib/utils/url_utility'; import { updateHistory, setUrlParams } from '~/lib/utils/url_utility';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants'; import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
...@@ -80,7 +80,7 @@ export default { ...@@ -80,7 +80,7 @@ export default {
if (milestoneTitle) { if (milestoneTitle) {
filteredSearchValue.push({ filteredSearchValue.push({
type: 'milestone_title', type: 'milestone',
value: { data: milestoneTitle, operator: '=' }, value: { data: milestoneTitle, operator: '=' },
}); });
} }
...@@ -129,7 +129,7 @@ export default { ...@@ -129,7 +129,7 @@ export default {
if (this.filterParams['not[milestoneTitle]']) { if (this.filterParams['not[milestoneTitle]']) {
filteredSearchValue.push({ filteredSearchValue.push({
type: 'milestone_title', type: 'milestone',
value: { data: this.filterParams['not[milestoneTitle]'], operator: '!=' }, value: { data: this.filterParams['not[milestoneTitle]'], operator: '!=' },
}); });
} }
...@@ -242,7 +242,7 @@ export default { ...@@ -242,7 +242,7 @@ export default {
search, search,
types, types,
weight, weight,
epic_id: getIdFromGraphQLId(epicId), epic_id: isGid(epicId) ? getIdFromGraphQLId(epicId) : epicId,
my_reaction_emoji: myReactionEmoji, my_reaction_emoji: myReactionEmoji,
release_tag: releaseTag, release_tag: releaseTag,
}; };
...@@ -293,7 +293,7 @@ export default { ...@@ -293,7 +293,7 @@ export default {
case 'label_name': case 'label_name':
labels.push(filter.value.data); labels.push(filter.value.data);
break; break;
case 'milestone_title': case 'milestone':
filterParams.milestoneTitle = filter.value.data; filterParams.milestoneTitle = filter.value.data;
break; break;
case 'iteration': case 'iteration':
...@@ -326,6 +326,7 @@ export default { ...@@ -326,6 +326,7 @@ export default {
if (plainText.length) { if (plainText.length) {
filterParams.search = plainText.join(' '); filterParams.search = plainText.join(' ');
} }
return filterParams; return filterParams;
}, },
}, },
......
...@@ -11,7 +11,6 @@ import { TYPE_USER } from '~/graphql_shared/constants'; ...@@ -11,7 +11,6 @@ import { TYPE_USER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils'; import { convertToGraphQLId } from '~/graphql_shared/utils';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { import {
DEFAULT_MILESTONES_GRAPHQL,
TOKEN_TITLE_MY_REACTION, TOKEN_TITLE_MY_REACTION,
OPERATOR_IS_AND_IS_NOT, OPERATOR_IS_AND_IS_NOT,
} from '~/vue_shared/components/filtered_search_bar/constants'; } from '~/vue_shared/components/filtered_search_bar/constants';
...@@ -136,13 +135,12 @@ export default { ...@@ -136,13 +135,12 @@ export default {
] ]
: []), : []),
{ {
type: 'milestone_title', type: 'milestone',
title: milestone, title: milestone,
icon: 'clock', icon: 'clock',
symbol: '%', symbol: '%',
token: MilestoneToken, token: MilestoneToken,
unique: true, unique: true,
defaultMilestones: DEFAULT_MILESTONES_GRAPHQL,
fetchMilestones: this.fetchMilestones, fetchMilestones: this.fetchMilestones,
}, },
{ {
......
...@@ -106,6 +106,7 @@ export const FilterFields = { ...@@ -106,6 +106,7 @@ export const FilterFields = {
'authorUsername', 'authorUsername',
'labelName', 'labelName',
'milestoneTitle', 'milestoneTitle',
'milestoneWildcardId',
'myReactionEmoji', 'myReactionEmoji',
'releaseTag', 'releaseTag',
'search', 'search',
...@@ -114,6 +115,18 @@ export const FilterFields = { ...@@ -114,6 +115,18 @@ export const FilterFields = {
], ],
}; };
/* eslint-disable @gitlab/require-i18n-strings */
export const AssigneeFilterType = {
any: 'Any',
};
export const MilestoneFilterType = {
any: 'Any',
none: 'None',
started: 'Started',
upcoming: 'Upcoming',
};
export const DraggableItemTypes = { export const DraggableItemTypes = {
card: 'card', card: 'card',
list: 'list', list: 'list',
......
...@@ -28,13 +28,6 @@ export const DEFAULT_MILESTONES = DEFAULT_NONE_ANY.concat([ ...@@ -28,13 +28,6 @@ export const DEFAULT_MILESTONES = DEFAULT_NONE_ANY.concat([
{ value: FILTER_STARTED, text: __('Started'), title: __('Started') }, { value: FILTER_STARTED, text: __('Started'), title: __('Started') },
]); ]);
export const DEFAULT_MILESTONES_GRAPHQL = [
{ value: 'any', text: __('Any'), title: __('Any') },
{ value: 'none', text: __('None'), title: __('None') },
{ value: '#upcoming', text: __('Upcoming'), title: __('Upcoming') },
{ value: '#started', text: __('Started'), title: __('Started') },
];
export const SortDirection = { export const SortDirection = {
descending: 'descending', descending: 'descending',
ascending: 'ascending', ascending: 'ascending',
......
import { GlFilteredSearchToken } from '@gitlab/ui'; import { GlFilteredSearchToken } from '@gitlab/ui';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { DEFAULT_MILESTONES_GRAPHQL } from '~/vue_shared/components/filtered_search_bar/constants';
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 EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue'; import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_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';
...@@ -453,10 +452,9 @@ export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones, fetchIter ...@@ -453,10 +452,9 @@ export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones, fetchIter
icon: 'clock', icon: 'clock',
title: __('Milestone'), title: __('Milestone'),
symbol: '%', symbol: '%',
type: 'milestone_title', type: 'milestone',
token: MilestoneToken, token: MilestoneToken,
unique: true, unique: true,
defaultMilestones: DEFAULT_MILESTONES_GRAPHQL,
fetchMilestones, fetchMilestones,
}, },
{ {
......
...@@ -30,9 +30,7 @@ RSpec.describe 'Issue board filters', :js do ...@@ -30,9 +30,7 @@ RSpec.describe 'Issue board filters', :js do
describe 'filters by releases' do describe 'filters by releases' do
before do before do
filter_input.click set_filter('release')
filter_input.set('release:')
filter_first_suggestion.click # Select `=` operator
end end
it 'loads all the releases when opened and submit one as filter', :aggregate_failures do it 'loads all the releases when opened and submit one as filter', :aggregate_failures do
...@@ -47,6 +45,35 @@ RSpec.describe 'Issue board filters', :js do ...@@ -47,6 +45,35 @@ RSpec.describe 'Issue board filters', :js do
end end
end end
describe 'filters by milestone' do
before do
set_filter('milestone')
end
it 'loads all the milestones when opened and submit one as filter', :aggregate_failures do
expect(find('.board:nth-child(1)')).to have_selector('.board-card', count: 2)
expect_filtered_search_dropdown_results(filter_dropdown, 6)
expect(filter_dropdown).to have_content('None')
expect(filter_dropdown).to have_content('Any')
expect(filter_dropdown).to have_content('Started')
expect(filter_dropdown).to have_content('Upcoming')
expect(filter_dropdown).to have_content(milestone_1.title)
expect(filter_dropdown).to have_content(milestone_2.title)
click_on milestone_1.title
filter_submit.click
expect(find('.board:nth-child(1)')).to have_selector('.board-card', count: 1)
end
end
def set_filter(filter)
filter_input.click
filter_input.set("#{filter}:")
filter_first_suggestion.click # Select `=` operator
end
def expect_filtered_search_dropdown_results(filter_dropdown, count) def expect_filtered_search_dropdown_results(filter_dropdown, count)
expect(filter_dropdown).to have_selector('.gl-new-dropdown-item', count: count) expect(filter_dropdown).to have_selector('.gl-new-dropdown-item', count: count)
end end
......
...@@ -120,7 +120,7 @@ describe('BoardFilteredSearch', () => { ...@@ -120,7 +120,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: '=' } }, { type: 'milestone', value: { data: 'New Milestone', operator: '=' } },
{ type: 'types', value: { data: 'INCIDENT', operator: '=' } }, { type: 'types', value: { data: 'INCIDENT', operator: '=' } },
{ type: 'weight', value: { data: '2', operator: '=' } }, { type: 'weight', value: { data: '2', operator: '=' } },
{ type: 'iteration', value: { data: '3341', operator: '=' } }, { type: 'iteration', value: { data: '3341', operator: '=' } },
......
...@@ -2,7 +2,6 @@ import { GlFilteredSearchToken } from '@gitlab/ui'; ...@@ -2,7 +2,6 @@ import { GlFilteredSearchToken } from '@gitlab/ui';
import { keyBy } from 'lodash'; import { keyBy } from 'lodash';
import { ListType } from '~/boards/constants'; import { ListType } from '~/boards/constants';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { DEFAULT_MILESTONES_GRAPHQL } from '~/vue_shared/components/filtered_search_bar/constants';
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 EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue'; import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_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';
...@@ -599,10 +598,9 @@ export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones, hasEmoji) ...@@ -599,10 +598,9 @@ export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones, hasEmoji)
icon: 'clock', icon: 'clock',
title: __('Milestone'), title: __('Milestone'),
symbol: '%', symbol: '%',
type: 'milestone_title', type: 'milestone',
token: MilestoneToken, token: MilestoneToken,
unique: true, unique: true,
defaultMilestones: DEFAULT_MILESTONES_GRAPHQL,
fetchMilestones, fetchMilestones,
}, },
{ {
......
...@@ -11,10 +11,7 @@ import createFlash from '~/flash'; ...@@ -11,10 +11,7 @@ import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { sortMilestonesByDueDate } from '~/milestones/milestone_utils'; import { sortMilestonesByDueDate } from '~/milestones/milestone_utils';
import { import { DEFAULT_MILESTONES } from '~/vue_shared/components/filtered_search_bar/constants';
DEFAULT_MILESTONES,
DEFAULT_MILESTONES_GRAPHQL,
} from '~/vue_shared/components/filtered_search_bar/constants';
import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue'; import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
import { mockMilestoneToken, mockMilestones, mockRegularMilestone } from '../mock_data'; import { mockMilestoneToken, mockMilestones, mockRegularMilestone } from '../mock_data';
...@@ -199,12 +196,12 @@ describe('MilestoneToken', () => { ...@@ -199,12 +196,12 @@ describe('MilestoneToken', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ wrapper = createComponent({
active: true, active: true,
config: { ...mockMilestoneToken, defaultMilestones: DEFAULT_MILESTONES_GRAPHQL }, config: { ...mockMilestoneToken, defaultMilestones: DEFAULT_MILESTONES },
}); });
}); });
it('finds the correct value from the activeToken', () => { it('finds the correct value from the activeToken', () => {
DEFAULT_MILESTONES_GRAPHQL.forEach(({ value, title }) => { DEFAULT_MILESTONES.forEach(({ value, title }) => {
const activeToken = wrapper.vm.getActiveMilestone([], value); const activeToken = wrapper.vm.getActiveMilestone([], value);
expect(activeToken.title).toEqual(title); expect(activeToken.title).toEqual(title);
......
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