From 6e7e9e80e0a89c2c295ccaa4b8469b5ed33acd27 Mon Sep 17 00:00:00 2001 From: Simon Knox Date: Tue, 21 Feb 2017 16:32:08 +1100 Subject: prevent filtering Issues by multiple milestones, authors, or assignees --- .../javascripts/filtered_search/dropdown_hint.js | 14 +++++----- .../javascripts/filtered_search/dropdown_utils.js | 18 ++++++++----- app/views/shared/issuable/_search_bar.html.haml | 2 +- changelogs/unreleased/27174-filter-filters.yml | 4 +++ .../filtered_search/dropdown_utils_spec.js | 31 ++++++++++++++++++++-- 5 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 changelogs/unreleased/27174-filter-filters.yml diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js index 38ff3fb7158..28e5e3232cb 100644 --- a/app/assets/javascripts/filtered_search/dropdown_hint.js +++ b/app/assets/javascripts/filtered_search/dropdown_hint.js @@ -57,13 +57,15 @@ require('./filtered_search_dropdown'); const dropdownData = []; [].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) { - dropdownData.push({ - icon: `fa-${icon}`, - hint, - tag: `<${tag}>`, - }); + dropdownData.push( + Object.assign({ + icon: `fa-${icon}`, + hint, + tag: `<${tag}>`, + }, type && { type }), + ); } }); diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js index a5a6b56a0d3..77bf191f343 100644 --- a/app/assets/javascripts/filtered_search/dropdown_utils.js +++ b/app/assets/javascripts/filtered_search/dropdown_utils.js @@ -51,14 +51,18 @@ static filterHint(input, item) { const updatedItem = item; - const searchInput = gl.DropdownUtils.getSearchInput(input); - let { lastToken } = gl.FilteredSearchTokenizer.processTokens(searchInput); - lastToken = lastToken.key || lastToken || ''; - - if (!lastToken || searchInput.split('').last() === ' ') { + const searchInput = gl.DropdownUtils.getSearchQuery(input); + const { lastToken, tokens } = gl.FilteredSearchTokenizer.processTokens(searchInput); + const lastKey = lastToken.key || lastToken || ''; + const allowMultiple = item.type === 'array'; + const itemInExistingTokens = tokens.some(t => t.key === item.hint); + + if (!allowMultiple && itemInExistingTokens) { + updatedItem.droplab_hidden = true; + } else if (!lastKey || searchInput.split('').last() === ' ') { updatedItem.droplab_hidden = false; - } else if (lastToken) { - const split = lastToken.split(':'); + } else if (lastKey) { + const split = lastKey.split(':'); const tokenName = split[0].split(' ').last(); const match = updatedItem.hint.indexOf(tokenName.toLowerCase()) === -1; diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index f8123846596..46e8c259a84 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -73,7 +73,7 @@ %li.filter-dropdown-item %button.btn.btn-link.js-data-value {{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 } } %li.filter-dropdown-item{ data: { value: 'none' } } %button.btn.btn-link diff --git a/changelogs/unreleased/27174-filter-filters.yml b/changelogs/unreleased/27174-filter-filters.yml new file mode 100644 index 00000000000..0da1e4d5d3b --- /dev/null +++ b/changelogs/unreleased/27174-filter-filters.yml @@ -0,0 +1,4 @@ +--- +title: Prevent filtering issues by multiple Milestones or Authors +merge_request: +author: diff --git a/spec/javascripts/filtered_search/dropdown_utils_spec.js b/spec/javascripts/filtered_search/dropdown_utils_spec.js index 5c65903701b..e6538020896 100644 --- a/spec/javascripts/filtered_search/dropdown_utils_spec.js +++ b/spec/javascripts/filtered_search/dropdown_utils_spec.js @@ -126,7 +126,11 @@ require('~/filtered_search/filtered_search_dropdown_manager'); beforeEach(() => { setFixtures(` - + `); input = document.getElementById('test'); @@ -142,7 +146,7 @@ require('~/filtered_search/filtered_search_dropdown_manager'); input.value = 'o'; updatedItem = gl.DropdownUtils.filterHint(input, { hint: 'label', - }, 'o'); + }); expect(updatedItem.droplab_hidden).toBe(true); }); @@ -150,6 +154,29 @@ require('~/filtered_search/filtered_search_dropdown_manager'); const updatedItem = gl.DropdownUtils.filterHint(input, {}, ''); 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', () => { -- cgit v1.2.1