Merge branch 'issue-filter-click-to-search' into 'master'
Allow issue filter submission using mouse only See merge request !8681
This commit is contained in:
commit
32f6f5b9aa
12 changed files with 62 additions and 36 deletions
|
@ -20,6 +20,9 @@
|
|||
if (selected.tagName === 'LI') {
|
||||
if (selected.hasAttribute('data-value')) {
|
||||
this.dismissDropdown();
|
||||
} else if (selected.getAttribute('data-action') === 'submit') {
|
||||
this.dismissDropdown();
|
||||
this.dispatchFormSubmitEvent();
|
||||
} else {
|
||||
const token = selected.querySelector('.js-filter-hint').innerText.trim();
|
||||
const tag = selected.querySelector('.js-filter-tag').innerText.trim();
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
}
|
||||
|
||||
this.dismissDropdown();
|
||||
this.dispatchInputEvent();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,6 +85,12 @@
|
|||
}));
|
||||
}
|
||||
|
||||
dispatchFormSubmitEvent() {
|
||||
// dispatchEvent() is necessary as form.submit() does not
|
||||
// trigger event handlers
|
||||
this.input.form.dispatchEvent(new Event('submit'));
|
||||
}
|
||||
|
||||
hideDropdown() {
|
||||
this.getCurrentHook().list.hide();
|
||||
}
|
||||
|
|
|
@ -61,11 +61,19 @@
|
|||
const word = `${tokenName}:${tokenValue}`;
|
||||
|
||||
// Get the string to replace
|
||||
const selectionStart = input.selectionStart;
|
||||
let newCaretPosition = input.selectionStart;
|
||||
const { left, right } = gl.DropdownUtils.getInputSelectionPosition(input);
|
||||
|
||||
input.value = `${inputValue.substr(0, left)}${word}${inputValue.substr(right)}`;
|
||||
gl.FilteredSearchDropdownManager.updateInputCaretPosition(selectionStart, input);
|
||||
|
||||
// If we have added a tokenValue at the end of the input,
|
||||
// add a space and set selection to the end
|
||||
if (right >= inputValue.length && tokenValue !== '') {
|
||||
input.value += ' ';
|
||||
newCaretPosition = input.value.length;
|
||||
}
|
||||
|
||||
gl.FilteredSearchDropdownManager.updateInputCaretPosition(newCaretPosition, input);
|
||||
}
|
||||
|
||||
static updateInputCaretPosition(selectionStart, input) {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
}
|
||||
|
||||
bindEvents() {
|
||||
this.handleFormSubmit = this.handleFormSubmit.bind(this);
|
||||
this.setDropdownWrapper = this.dropdownManager.setDropdown.bind(this.dropdownManager);
|
||||
this.toggleClearSearchButtonWrapper = this.toggleClearSearchButton.bind(this);
|
||||
this.checkForEnterWrapper = this.checkForEnter.bind(this);
|
||||
|
@ -32,6 +33,7 @@
|
|||
this.checkForBackspaceWrapper = this.checkForBackspace.bind(this);
|
||||
this.tokenChange = this.tokenChange.bind(this);
|
||||
|
||||
this.filteredSearchInput.form.addEventListener('submit', this.handleFormSubmit);
|
||||
this.filteredSearchInput.addEventListener('input', this.setDropdownWrapper);
|
||||
this.filteredSearchInput.addEventListener('input', this.toggleClearSearchButtonWrapper);
|
||||
this.filteredSearchInput.addEventListener('keydown', this.checkForEnterWrapper);
|
||||
|
@ -42,6 +44,7 @@
|
|||
}
|
||||
|
||||
unbindEvents() {
|
||||
this.filteredSearchInput.form.removeEventListener('submit', this.handleFormSubmit);
|
||||
this.filteredSearchInput.removeEventListener('input', this.setDropdownWrapper);
|
||||
this.filteredSearchInput.removeEventListener('input', this.toggleClearSearchButtonWrapper);
|
||||
this.filteredSearchInput.removeEventListener('keydown', this.checkForEnterWrapper);
|
||||
|
@ -88,6 +91,11 @@
|
|||
this.dropdownManager.resetDropdowns();
|
||||
}
|
||||
|
||||
handleFormSubmit(e) {
|
||||
e.preventDefault();
|
||||
this.search();
|
||||
}
|
||||
|
||||
loadSearchParamsFromURL() {
|
||||
const params = gl.utils.getUrlParamsArray();
|
||||
const usernameParams = this.getUsernameParams();
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
= icon('times')
|
||||
#js-dropdown-hint.dropdown-menu.hint-dropdown
|
||||
%ul{ 'data-dropdown' => true }
|
||||
%li.filter-dropdown-item{ 'data-value' => '' }
|
||||
%li.filter-dropdown-item{ 'data-action' => 'submit' }
|
||||
%button.btn.btn-link
|
||||
= icon('search')
|
||||
%span
|
||||
|
@ -125,10 +125,6 @@
|
|||
new MilestoneSelect();
|
||||
new IssueStatusSelect();
|
||||
new SubscriptionSelect();
|
||||
$('form.filter-form').on('submit', function (event) {
|
||||
event.preventDefault();
|
||||
Turbolinks.visit(this.action + '&' + $(this).serialize());
|
||||
});
|
||||
|
||||
$(document).off('page:restore').on('page:restore', function (event) {
|
||||
if (gl.FilteredSearchManager) {
|
||||
|
|
4
changelogs/unreleased/issue-filter-click-to-search.yml
Normal file
4
changelogs/unreleased/issue-filter-click-to-search.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: allow issue filter bar to be operated with mouse only
|
||||
merge_request: 8681
|
||||
author:
|
|
@ -134,14 +134,14 @@ describe 'Dropdown assignee', js: true, feature: true do
|
|||
click_button 'Assigned to me'
|
||||
end
|
||||
|
||||
expect(filtered_search.value).to eq("assignee:#{user.to_reference}")
|
||||
expect(filtered_search.value).to eq("assignee:#{user.to_reference} ")
|
||||
end
|
||||
|
||||
it 'fills in the assignee username when the assignee has not been filtered' do
|
||||
click_assignee(user_jacob.name)
|
||||
|
||||
expect(page).to have_css(js_dropdown_assignee, visible: false)
|
||||
expect(filtered_search.value).to eq("assignee:@#{user_jacob.username}")
|
||||
expect(filtered_search.value).to eq("assignee:@#{user_jacob.username} ")
|
||||
end
|
||||
|
||||
it 'fills in the assignee username when the assignee has been filtered' do
|
||||
|
@ -149,14 +149,14 @@ describe 'Dropdown assignee', js: true, feature: true do
|
|||
click_assignee(user.name)
|
||||
|
||||
expect(page).to have_css(js_dropdown_assignee, visible: false)
|
||||
expect(filtered_search.value).to eq("assignee:@#{user.username}")
|
||||
expect(filtered_search.value).to eq("assignee:@#{user.username} ")
|
||||
end
|
||||
|
||||
it 'selects `no assignee`' do
|
||||
find('#js-dropdown-assignee .filter-dropdown-item', text: 'No Assignee').click
|
||||
|
||||
expect(page).to have_css(js_dropdown_assignee, visible: false)
|
||||
expect(filtered_search.value).to eq("assignee:none")
|
||||
expect(filtered_search.value).to eq("assignee:none ")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -121,14 +121,14 @@ describe 'Dropdown author', js: true, feature: true do
|
|||
click_author(user_jacob.name)
|
||||
|
||||
expect(page).to have_css(js_dropdown_author, visible: false)
|
||||
expect(filtered_search.value).to eq("author:@#{user_jacob.username}")
|
||||
expect(filtered_search.value).to eq("author:@#{user_jacob.username} ")
|
||||
end
|
||||
|
||||
it 'fills in the author username when the author has been filtered' do
|
||||
click_author(user.name)
|
||||
|
||||
expect(page).to have_css(js_dropdown_author, visible: false)
|
||||
expect(filtered_search.value).to eq("author:@#{user.username}")
|
||||
expect(filtered_search.value).to eq("author:@#{user.username} ")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ describe 'Dropdown label', js: true, feature: true do
|
|||
click_label(bug_label.title)
|
||||
|
||||
expect(page).to have_css(js_dropdown_label, visible: false)
|
||||
expect(filtered_search.value).to eq("label:~#{bug_label.title}")
|
||||
expect(filtered_search.value).to eq("label:~#{bug_label.title} ")
|
||||
end
|
||||
|
||||
it 'fills in the label name when the label is partially filled' do
|
||||
|
@ -167,49 +167,49 @@ describe 'Dropdown label', js: true, feature: true do
|
|||
click_label(bug_label.title)
|
||||
|
||||
expect(page).to have_css(js_dropdown_label, visible: false)
|
||||
expect(filtered_search.value).to eq("label:~#{bug_label.title}")
|
||||
expect(filtered_search.value).to eq("label:~#{bug_label.title} ")
|
||||
end
|
||||
|
||||
it 'fills in the label name that contains multiple words' do
|
||||
click_label(two_words_label.title)
|
||||
|
||||
expect(page).to have_css(js_dropdown_label, visible: false)
|
||||
expect(filtered_search.value).to eq("label:~\"#{two_words_label.title}\"")
|
||||
expect(filtered_search.value).to eq("label:~\"#{two_words_label.title}\" ")
|
||||
end
|
||||
|
||||
it 'fills in the label name that contains multiple words and is very long' do
|
||||
click_label(long_label.title)
|
||||
|
||||
expect(page).to have_css(js_dropdown_label, visible: false)
|
||||
expect(filtered_search.value).to eq("label:~\"#{long_label.title}\"")
|
||||
expect(filtered_search.value).to eq("label:~\"#{long_label.title}\" ")
|
||||
end
|
||||
|
||||
it 'fills in the label name that contains double quotes' do
|
||||
click_label(wont_fix_label.title)
|
||||
|
||||
expect(page).to have_css(js_dropdown_label, visible: false)
|
||||
expect(filtered_search.value).to eq("label:~'#{wont_fix_label.title}'")
|
||||
expect(filtered_search.value).to eq("label:~'#{wont_fix_label.title}' ")
|
||||
end
|
||||
|
||||
it 'fills in the label name with the correct capitalization' do
|
||||
click_label(uppercase_label.title)
|
||||
|
||||
expect(page).to have_css(js_dropdown_label, visible: false)
|
||||
expect(filtered_search.value).to eq("label:~#{uppercase_label.title}")
|
||||
expect(filtered_search.value).to eq("label:~#{uppercase_label.title} ")
|
||||
end
|
||||
|
||||
it 'fills in the label name with special characters' do
|
||||
click_label(special_label.title)
|
||||
|
||||
expect(page).to have_css(js_dropdown_label, visible: false)
|
||||
expect(filtered_search.value).to eq("label:~#{special_label.title}")
|
||||
expect(filtered_search.value).to eq("label:~#{special_label.title} ")
|
||||
end
|
||||
|
||||
it 'selects `no label`' do
|
||||
find('#js-dropdown-label .filter-dropdown-item', text: 'No Label').click
|
||||
|
||||
expect(page).to have_css(js_dropdown_label, visible: false)
|
||||
expect(filtered_search.value).to eq("label:none")
|
||||
expect(filtered_search.value).to eq("label:none ")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ describe 'Dropdown milestone', js: true, feature: true do
|
|||
click_milestone(milestone.title)
|
||||
|
||||
expect(page).to have_css(js_dropdown_milestone, visible: false)
|
||||
expect(filtered_search.value).to eq("milestone:%#{milestone.title}")
|
||||
expect(filtered_search.value).to eq("milestone:%#{milestone.title} ")
|
||||
end
|
||||
|
||||
it 'fills in the milestone name when the milestone is partially filled' do
|
||||
|
@ -135,56 +135,56 @@ describe 'Dropdown milestone', js: true, feature: true do
|
|||
click_milestone(milestone.title)
|
||||
|
||||
expect(page).to have_css(js_dropdown_milestone, visible: false)
|
||||
expect(filtered_search.value).to eq("milestone:%#{milestone.title}")
|
||||
expect(filtered_search.value).to eq("milestone:%#{milestone.title} ")
|
||||
end
|
||||
|
||||
it 'fills in the milestone name that contains multiple words' do
|
||||
click_milestone(two_words_milestone.title)
|
||||
|
||||
expect(page).to have_css(js_dropdown_milestone, visible: false)
|
||||
expect(filtered_search.value).to eq("milestone:%\"#{two_words_milestone.title}\"")
|
||||
expect(filtered_search.value).to eq("milestone:%\"#{two_words_milestone.title}\" ")
|
||||
end
|
||||
|
||||
it 'fills in the milestone name that contains multiple words and is very long' do
|
||||
click_milestone(long_milestone.title)
|
||||
|
||||
expect(page).to have_css(js_dropdown_milestone, visible: false)
|
||||
expect(filtered_search.value).to eq("milestone:%\"#{long_milestone.title}\"")
|
||||
expect(filtered_search.value).to eq("milestone:%\"#{long_milestone.title}\" ")
|
||||
end
|
||||
|
||||
it 'fills in the milestone name that contains double quotes' do
|
||||
click_milestone(wont_fix_milestone.title)
|
||||
|
||||
expect(page).to have_css(js_dropdown_milestone, visible: false)
|
||||
expect(filtered_search.value).to eq("milestone:%'#{wont_fix_milestone.title}'")
|
||||
expect(filtered_search.value).to eq("milestone:%'#{wont_fix_milestone.title}' ")
|
||||
end
|
||||
|
||||
it 'fills in the milestone name with the correct capitalization' do
|
||||
click_milestone(uppercase_milestone.title)
|
||||
|
||||
expect(page).to have_css(js_dropdown_milestone, visible: false)
|
||||
expect(filtered_search.value).to eq("milestone:%#{uppercase_milestone.title}")
|
||||
expect(filtered_search.value).to eq("milestone:%#{uppercase_milestone.title} ")
|
||||
end
|
||||
|
||||
it 'fills in the milestone name with special characters' do
|
||||
click_milestone(special_milestone.title)
|
||||
|
||||
expect(page).to have_css(js_dropdown_milestone, visible: false)
|
||||
expect(filtered_search.value).to eq("milestone:%#{special_milestone.title}")
|
||||
expect(filtered_search.value).to eq("milestone:%#{special_milestone.title} ")
|
||||
end
|
||||
|
||||
it 'selects `no milestone`' do
|
||||
click_static_milestone('No Milestone')
|
||||
|
||||
expect(page).to have_css(js_dropdown_milestone, visible: false)
|
||||
expect(filtered_search.value).to eq("milestone:none")
|
||||
expect(filtered_search.value).to eq("milestone:none ")
|
||||
end
|
||||
|
||||
it 'selects `upcoming milestone`' do
|
||||
click_static_milestone('Upcoming')
|
||||
|
||||
expect(page).to have_css(js_dropdown_milestone, visible: false)
|
||||
expect(filtered_search.value).to eq("milestone:upcoming")
|
||||
expect(filtered_search.value).to eq("milestone:upcoming ")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -539,7 +539,7 @@ describe 'Filter issues', js: true, feature: true do
|
|||
click_button user2.username
|
||||
end
|
||||
|
||||
expect(filtered_search.value).to eq("author:@#{user2.username}")
|
||||
expect(filtered_search.value).to eq("author:@#{user2.username} ")
|
||||
end
|
||||
|
||||
it 'changes label' do
|
||||
|
@ -551,7 +551,7 @@ describe 'Filter issues', js: true, feature: true do
|
|||
click_button label.name
|
||||
end
|
||||
|
||||
expect(filtered_search.value).to eq("author:@#{user.username} label:~#{label.name}")
|
||||
expect(filtered_search.value).to eq("author:@#{user.username} label:~#{label.name} ")
|
||||
end
|
||||
|
||||
it 'changes label correctly space is in previous label' do
|
||||
|
@ -563,7 +563,7 @@ describe 'Filter issues', js: true, feature: true do
|
|||
click_button label.name
|
||||
end
|
||||
|
||||
expect(filtered_search.value).to eq("label:~#{label.name}")
|
||||
expect(filtered_search.value).to eq("label:~#{label.name} ")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
it('should add tokenName and tokenValue', () => {
|
||||
gl.FilteredSearchDropdownManager.addWordToInput('label', 'none');
|
||||
expect(getInputValue()).toBe('label:none');
|
||||
expect(getInputValue()).toBe('label:none ');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -45,13 +45,13 @@
|
|||
it('should replace tokenValue', () => {
|
||||
setInputValue('author:roo');
|
||||
gl.FilteredSearchDropdownManager.addWordToInput('author', '@root');
|
||||
expect(getInputValue()).toBe('author:@root');
|
||||
expect(getInputValue()).toBe('author:@root ');
|
||||
});
|
||||
|
||||
it('should add tokenValues containing spaces', () => {
|
||||
setInputValue('label:~"test');
|
||||
gl.FilteredSearchDropdownManager.addWordToInput('label', '~\'"test me"\'');
|
||||
expect(getInputValue()).toBe('label:~\'"test me"\'');
|
||||
expect(getInputValue()).toBe('label:~\'"test me"\' ');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue