Merge branch '27174-filter-filters' into 'master'

prevent filtering Issues by multiple milestones, authors, or assignees

Closes #27174

See merge request !9406
This commit is contained in:
Clement Ho 2017-03-15 17:20:09 +00:00
commit 53d4cad1c9
5 changed files with 52 additions and 15 deletions

View file

@ -57,13 +57,15 @@ require('./filtered_search_dropdown');
const dropdownData = []; const dropdownData = [];
[].forEach.call(this.input.closest('.filtered-search-input-container').querySelectorAll('.dropdown-menu'), (dropdownMenu) => { [].forEach.call(this.input.closest('.filtered-search-input-container').querySelectorAll('.dropdown-menu'), (dropdownMenu) => {
const { icon, hint, tag } = dropdownMenu.dataset; const { icon, hint, tag, type } = dropdownMenu.dataset;
if (icon && hint && tag) { if (icon && hint && tag) {
dropdownData.push({ dropdownData.push(
icon: `fa-${icon}`, Object.assign({
hint, icon: `fa-${icon}`,
tag: `<${tag}>`, hint,
}); tag: `<${tag}>`,
}, type && { type }),
);
} }
}); });

View file

@ -51,14 +51,18 @@
static filterHint(input, item) { static filterHint(input, item) {
const updatedItem = item; const updatedItem = item;
const searchInput = gl.DropdownUtils.getSearchInput(input); const searchInput = gl.DropdownUtils.getSearchQuery(input);
let { lastToken } = gl.FilteredSearchTokenizer.processTokens(searchInput); const { lastToken, tokens } = gl.FilteredSearchTokenizer.processTokens(searchInput);
lastToken = lastToken.key || lastToken || ''; const lastKey = lastToken.key || lastToken || '';
const allowMultiple = item.type === 'array';
const itemInExistingTokens = tokens.some(t => t.key === item.hint);
if (!lastToken || searchInput.split('').last() === ' ') { if (!allowMultiple && itemInExistingTokens) {
updatedItem.droplab_hidden = true;
} else if (!lastKey || searchInput.split('').last() === ' ') {
updatedItem.droplab_hidden = false; updatedItem.droplab_hidden = false;
} else if (lastToken) { } else if (lastKey) {
const split = lastToken.split(':'); const split = lastKey.split(':');
const tokenName = split[0].split(' ').last(); const tokenName = split[0].split(' ').last();
const match = updatedItem.hint.indexOf(tokenName.toLowerCase()) === -1; const match = updatedItem.hint.indexOf(tokenName.toLowerCase()) === -1;

View file

@ -73,7 +73,7 @@
%li.filter-dropdown-item %li.filter-dropdown-item
%button.btn.btn-link.js-data-value %button.btn.btn-link.js-data-value
{{title}} {{title}}
#js-dropdown-label.dropdown-menu{ data: { icon: 'tag', hint: 'label', tag: '~label' } } #js-dropdown-label.dropdown-menu{ data: { icon: 'tag', hint: 'label', tag: '~label', type: 'array' } }
%ul{ data: { dropdown: true } } %ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } } %li.filter-dropdown-item{ data: { value: 'none' } }
%button.btn.btn-link %button.btn.btn-link

View file

@ -0,0 +1,4 @@
---
title: Prevent filtering issues by multiple Milestones or Authors
merge_request:
author:

View file

@ -126,7 +126,11 @@ require('~/filtered_search/filtered_search_dropdown_manager');
beforeEach(() => { beforeEach(() => {
setFixtures(` setFixtures(`
<input type="text" id="test" /> <ul class="tokens-container">
<li class="input-token">
<input class="filtered-search" type="text" id="test" />
</li>
</ul>
`); `);
input = document.getElementById('test'); input = document.getElementById('test');
@ -142,7 +146,7 @@ require('~/filtered_search/filtered_search_dropdown_manager');
input.value = 'o'; input.value = 'o';
updatedItem = gl.DropdownUtils.filterHint(input, { updatedItem = gl.DropdownUtils.filterHint(input, {
hint: 'label', hint: 'label',
}, 'o'); });
expect(updatedItem.droplab_hidden).toBe(true); expect(updatedItem.droplab_hidden).toBe(true);
}); });
@ -150,6 +154,29 @@ require('~/filtered_search/filtered_search_dropdown_manager');
const updatedItem = gl.DropdownUtils.filterHint(input, {}, ''); const updatedItem = gl.DropdownUtils.filterHint(input, {}, '');
expect(updatedItem.droplab_hidden).toBe(false); expect(updatedItem.droplab_hidden).toBe(false);
}); });
it('should allow multiple if item.type is array', () => {
input.value = 'label:~first la';
const updatedItem = gl.DropdownUtils.filterHint(input, {
hint: 'label',
type: 'array',
});
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should prevent multiple if item.type is not array', () => {
input.value = 'milestone:~first mile';
let updatedItem = gl.DropdownUtils.filterHint(input, {
hint: 'milestone',
});
expect(updatedItem.droplab_hidden).toBe(true);
updatedItem = gl.DropdownUtils.filterHint(input, {
hint: 'milestone',
type: 'string',
});
expect(updatedItem.droplab_hidden).toBe(true);
});
}); });
describe('setDataValueIfSelected', () => { describe('setDataValueIfSelected', () => {