Commit 983f7d1e authored by Florie Guibert's avatar Florie Guibert

Add Confidential filter to issue boards new filtered search

New filtered search
parent 8e4fed9e
import { sortBy, cloneDeep } from 'lodash'; import { sortBy, cloneDeep } from 'lodash';
import { isGid } from '~/graphql_shared/utils'; import { isGid } from '~/graphql_shared/utils';
import { parseBoolean } from '~/lib/utils/common_utils';
import { ListType, MilestoneIDs, AssigneeFilterType, MilestoneFilterType } from './constants'; import { ListType, MilestoneIDs, AssigneeFilterType, MilestoneFilterType } from './constants';
export function getMilestone() { export function getMilestone() {
...@@ -220,6 +221,10 @@ export const FiltersInfo = { ...@@ -220,6 +221,10 @@ export const FiltersInfo = {
types: { types: {
negatedSupport: true, negatedSupport: true,
}, },
confidential: {
negatedSupport: false,
transform: (val) => parseBoolean(val),
},
search: { search: {
negatedSupport: false, negatedSupport: false,
}, },
......
...@@ -45,6 +45,7 @@ export default { ...@@ -45,6 +45,7 @@ export default {
epicId, epicId,
myReactionEmoji, myReactionEmoji,
releaseTag, releaseTag,
confidential,
} = this.filterParams; } = this.filterParams;
const filteredSearchValue = []; const filteredSearchValue = [];
...@@ -113,6 +114,13 @@ export default { ...@@ -113,6 +114,13 @@ export default {
}); });
} }
if (confidential !== undefined) {
filteredSearchValue.push({
type: 'confidential',
value: { data: confidential },
});
}
if (epicId) { if (epicId) {
filteredSearchValue.push({ filteredSearchValue.push({
type: 'epic', type: 'epic',
...@@ -211,6 +219,7 @@ export default { ...@@ -211,6 +219,7 @@ export default {
myReactionEmoji, myReactionEmoji,
iterationId, iterationId,
releaseTag, releaseTag,
confidential,
} = this.filterParams; } = this.filterParams;
let notParams = {}; let notParams = {};
...@@ -245,6 +254,7 @@ export default { ...@@ -245,6 +254,7 @@ export default {
epic_id: isGid(epicId) ? getIdFromGraphQLId(epicId) : epicId, epic_id: isGid(epicId) ? getIdFromGraphQLId(epicId) : epicId,
my_reaction_emoji: myReactionEmoji, my_reaction_emoji: myReactionEmoji,
release_tag: releaseTag, release_tag: releaseTag,
confidential,
}; };
}, },
}, },
...@@ -311,6 +321,9 @@ export default { ...@@ -311,6 +321,9 @@ export default {
case 'release': case 'release':
filterParams.releaseTag = filter.value.data; filterParams.releaseTag = filter.value.data;
break; break;
case 'confidential':
filterParams.confidential = 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;
......
...@@ -13,6 +13,7 @@ import { __ } from '~/locale'; ...@@ -13,6 +13,7 @@ import { __ } from '~/locale';
import { import {
TOKEN_TITLE_MY_REACTION, TOKEN_TITLE_MY_REACTION,
OPERATOR_IS_AND_IS_NOT, OPERATOR_IS_AND_IS_NOT,
OPERATOR_IS_ONLY,
} from '~/vue_shared/components/filtered_search_bar/constants'; } 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';
...@@ -36,6 +37,7 @@ export default { ...@@ -36,6 +37,7 @@ export default {
issue: __('Issue'), issue: __('Issue'),
milestone: __('Milestone'), milestone: __('Milestone'),
release: __('Release'), release: __('Release'),
confidential: __('Confidential'),
}, },
components: { BoardFilteredSearch }, components: { BoardFilteredSearch },
inject: ['isSignedIn', 'releasesFetchPath'], inject: ['isSignedIn', 'releasesFetchPath'],
...@@ -68,6 +70,7 @@ export default { ...@@ -68,6 +70,7 @@ export default {
type, type,
milestone, milestone,
release, release,
confidential,
} = this.$options.i18n; } = this.$options.i18n;
const { types } = this.$options; const { types } = this.$options;
const { fetchAuthors, fetchLabels } = issueBoardFilters( const { fetchAuthors, fetchLabels } = issueBoardFilters(
...@@ -132,6 +135,18 @@ export default { ...@@ -132,6 +135,18 @@ export default {
}); });
}, },
}, },
{
type: 'confidential',
icon: 'eye-slash',
title: confidential,
unique: true,
token: GlFilteredSearchToken,
operators: OPERATOR_IS_ONLY,
options: [
{ icon: 'eye-slash', value: true, title: __('Yes') },
{ icon: 'eye', value: false, title: __('No') },
],
},
] ]
: []), : []),
{ {
......
...@@ -104,6 +104,7 @@ export const FilterFields = { ...@@ -104,6 +104,7 @@ export const FilterFields = {
'assigneeUsername', 'assigneeUsername',
'assigneeWildcardId', 'assigneeWildcardId',
'authorUsername', 'authorUsername',
'confidential',
'labelName', 'labelName',
'milestoneTitle', 'milestoneTitle',
'milestoneWildcardId', 'milestoneWildcardId',
......
...@@ -448,6 +448,18 @@ export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones, fetchIter ...@@ -448,6 +448,18 @@ export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones, fetchIter
token: EmojiToken, token: EmojiToken,
fetchEmojis: expect.any(Function), fetchEmojis: expect.any(Function),
}, },
{
type: 'confidential',
icon: 'eye-slash',
title: 'Confidential',
unique: true,
token: GlFilteredSearchToken,
operators: [{ value: '=', description: 'is' }],
options: [
{ icon: 'eye-slash', value: true, title: 'Yes' },
{ icon: 'eye', value: false, title: 'No' },
],
},
{ {
icon: 'clock', icon: 'clock',
title: __('Milestone'), title: __('Milestone'),
......
...@@ -12,7 +12,7 @@ RSpec.describe 'Issue board filters', :js do ...@@ -12,7 +12,7 @@ RSpec.describe 'Issue board filters', :js do
let_it_be(:release) { create(:release, tag: 'v1.0', project: project, milestones: [milestone_1]) } let_it_be(:release) { create(:release, tag: 'v1.0', project: project, milestones: [milestone_1]) }
let_it_be(:release_2) { create(:release, tag: 'v2.0', project: project, milestones: [milestone_2]) } let_it_be(:release_2) { create(:release, tag: 'v2.0', project: project, milestones: [milestone_2]) }
let_it_be(:issue_1) { create(:issue, project: project, milestone: milestone_1, author: user) } let_it_be(:issue_1) { create(:issue, project: project, milestone: milestone_1, author: user) }
let_it_be(:issue_2) { create(:labeled_issue, project: project, milestone: milestone_2, assignees: [user], labels: [project_label]) } let_it_be(:issue_2) { create(:labeled_issue, project: project, milestone: milestone_2, assignees: [user], labels: [project_label], confidential: true) }
let_it_be(:award_emoji1) { create(:award_emoji, name: 'thumbsup', user: user, awardable: issue_1) } let_it_be(:award_emoji1) { create(:award_emoji, name: 'thumbsup', user: user, awardable: issue_1) }
let(:filtered_search) { find('[data-testid="issue_1-board-filtered-search"]') } let(:filtered_search) { find('[data-testid="issue_1-board-filtered-search"]') }
...@@ -100,6 +100,25 @@ RSpec.describe 'Issue board filters', :js do ...@@ -100,6 +100,25 @@ RSpec.describe 'Issue board filters', :js do
end end
end end
describe 'filters by confidentiality' do
before do
filter_input.click
filter_input.set("confidential:")
end
it 'loads all the confidentiality options 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, 2)
filter_dropdown.click_on 'Yes'
filter_submit.click
expect(find('.board:nth-child(1)')).to have_selector('.board-card', count: 1)
expect(find('.board-card')).to have_content(issue_2.title)
end
end
describe 'filters by milestone' do describe 'filters by milestone' do
before do before do
set_filter('milestone') set_filter('milestone')
......
...@@ -552,7 +552,20 @@ export const mockEmojiToken = { ...@@ -552,7 +552,20 @@ export const mockEmojiToken = {
fetchEmojis: expect.any(Function), fetchEmojis: expect.any(Function),
}; };
export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones, hasEmoji) => [ export const mockConfidentialToken = {
type: 'confidential',
icon: 'eye-slash',
title: 'Confidential',
unique: true,
token: GlFilteredSearchToken,
operators: [{ value: '=', description: 'is' }],
options: [
{ icon: 'eye-slash', value: true, title: 'Yes' },
{ icon: 'eye', value: false, title: 'No' },
],
};
export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones, isSignedIn) => [
{ {
icon: 'user', icon: 'user',
title: __('Assignee'), title: __('Assignee'),
...@@ -593,7 +606,7 @@ export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones, hasEmoji) ...@@ -593,7 +606,7 @@ export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones, hasEmoji)
symbol: '~', symbol: '~',
fetchLabels, fetchLabels,
}, },
...(hasEmoji ? [mockEmojiToken] : []), ...(isSignedIn ? [mockEmojiToken, mockConfidentialToken] : []),
{ {
icon: 'clock', icon: 'clock',
title: __('Milestone'), title: __('Milestone'),
......
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