Commit 986eff1c authored by Phil Hughes's avatar Phil Hughes

Merge branch 'auto-search-when-state-changed' into 'master'

Perform filtered search when state tab is changed

Closes #27261

See merge request !11917
parents dc766fdb 60d2a7c3
...@@ -77,6 +77,41 @@ class FilteredSearchManager { ...@@ -77,6 +77,41 @@ class FilteredSearchManager {
} }
} }
bindStateEvents() {
this.stateFilters = document.querySelector('.container-fluid .issues-state-filters');
if (this.stateFilters) {
this.searchStateWrapper = this.searchState.bind(this);
this.stateFilters.querySelector('[data-state="opened"]')
.addEventListener('click', this.searchStateWrapper);
this.stateFilters.querySelector('[data-state="closed"]')
.addEventListener('click', this.searchStateWrapper);
this.stateFilters.querySelector('[data-state="all"]')
.addEventListener('click', this.searchStateWrapper);
this.mergedState = this.stateFilters.querySelector('[data-state="merged"]');
if (this.mergedState) {
this.mergedState.addEventListener('click', this.searchStateWrapper);
}
}
}
unbindStateEvents() {
if (this.stateFilters) {
this.stateFilters.querySelector('[data-state="opened"]')
.removeEventListener('click', this.searchStateWrapper);
this.stateFilters.querySelector('[data-state="closed"]')
.removeEventListener('click', this.searchStateWrapper);
this.stateFilters.querySelector('[data-state="all"]')
.removeEventListener('click', this.searchStateWrapper);
if (this.mergedState) {
this.mergedState.removeEventListener('click', this.searchStateWrapper);
}
}
}
bindEvents() { bindEvents() {
this.handleFormSubmit = this.handleFormSubmit.bind(this); this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.setDropdownWrapper = this.dropdownManager.setDropdown.bind(this.dropdownManager); this.setDropdownWrapper = this.dropdownManager.setDropdown.bind(this.dropdownManager);
...@@ -112,6 +147,8 @@ class FilteredSearchManager { ...@@ -112,6 +147,8 @@ class FilteredSearchManager {
document.addEventListener('click', this.removeInputContainerFocusWrapper); document.addEventListener('click', this.removeInputContainerFocusWrapper);
document.addEventListener('keydown', this.removeSelectedTokenKeydownWrapper); document.addEventListener('keydown', this.removeSelectedTokenKeydownWrapper);
eventHub.$on('recentSearchesItemSelected', this.onrecentSearchesItemSelectedWrapper); eventHub.$on('recentSearchesItemSelected', this.onrecentSearchesItemSelectedWrapper);
this.bindStateEvents();
} }
unbindEvents() { unbindEvents() {
...@@ -132,6 +169,8 @@ class FilteredSearchManager { ...@@ -132,6 +169,8 @@ class FilteredSearchManager {
document.removeEventListener('click', this.removeInputContainerFocusWrapper); document.removeEventListener('click', this.removeInputContainerFocusWrapper);
document.removeEventListener('keydown', this.removeSelectedTokenKeydownWrapper); document.removeEventListener('keydown', this.removeSelectedTokenKeydownWrapper);
eventHub.$off('recentSearchesItemSelected', this.onrecentSearchesItemSelectedWrapper); eventHub.$off('recentSearchesItemSelected', this.onrecentSearchesItemSelectedWrapper);
this.unbindStateEvents();
} }
checkForBackspace(e) { checkForBackspace(e) {
...@@ -447,7 +486,19 @@ class FilteredSearchManager { ...@@ -447,7 +486,19 @@ class FilteredSearchManager {
} }
} }
search() { searchState(e) {
const target = e.currentTarget;
// remove focus outline after click
target.blur();
const state = target.dataset && target.dataset.state;
if (state) {
this.search(state);
}
}
search(state = null) {
const paths = []; const paths = [];
const searchQuery = gl.DropdownUtils.getSearchQuery(); const searchQuery = gl.DropdownUtils.getSearchQuery();
...@@ -455,7 +506,7 @@ class FilteredSearchManager { ...@@ -455,7 +506,7 @@ class FilteredSearchManager {
const { tokens, searchToken } const { tokens, searchToken }
= this.tokenizer.processTokens(searchQuery, this.filteredSearchTokenKeys.getKeys()); = this.tokenizer.processTokens(searchQuery, this.filteredSearchTokenKeys.getKeys());
const currentState = gl.utils.getParameterByName('state') || 'opened'; const currentState = state || gl.utils.getParameterByName('state') || 'opened';
paths.push(`state=${currentState}`); paths.push(`state=${currentState}`);
tokens.forEach((token) => { tokens.forEach((token) => {
......
...@@ -45,7 +45,8 @@ ...@@ -45,7 +45,8 @@
li { li {
display: flex; display: flex;
a { a,
.btn-link {
padding: $gl-btn-padding; padding: $gl-btn-padding;
padding-bottom: 11px; padding-bottom: 11px;
font-size: 14px; font-size: 14px;
...@@ -67,7 +68,29 @@ ...@@ -67,7 +68,29 @@
} }
} }
&.active a { .btn-link {
padding-top: 16px;
padding-left: 15px;
padding-right: 15px;
border-left: none;
border-right: none;
border-top: none;
border-radius: 0;
&:hover,
&:active,
&:focus {
background-color: transparent;
}
&:active {
outline: 0;
box-shadow: none;
}
}
&.active a,
&.active .btn-link {
border-bottom: 2px solid $link-underline-blue; border-bottom: 2px solid $link-underline-blue;
color: $black; color: $black;
font-weight: 600; font-weight: 600;
......
- type = local_assigns.fetch(:type, :issues) - type = local_assigns.fetch(:type, :issues)
- page_context_word = type.to_s.humanize(capitalize: false) - page_context_word = type.to_s.humanize(capitalize: false)
- issuables = @issues || @merge_requests - issuables = @issues || @merge_requests
- closed_title = 'Filter by issues that are currently closed.'
%ul.nav-links.issues-state-filters %ul.nav-links.issues-state-filters
%li{ class: active_when(params[:state] == 'opened') }> %li{ class: active_when(params[:state] == 'opened') }>
= link_to page_filter_path(state: 'opened', label: true), id: 'state-opened', title: "Filter by #{page_context_word} that are currently opened." do %button.btn.btn-link{ id: 'state-opened', title: "Filter by #{page_context_word} that are currently opened.", type: 'button', data: { state: 'opened' } }
#{issuables_state_counter_text(type, :opened)} #{issuables_state_counter_text(type, :opened)}
- if type == :merge_requests - if type == :merge_requests
%li{ class: active_when(params[:state] == 'merged') }> %li{ class: active_when(params[:state] == 'merged') }>
= link_to page_filter_path(state: 'merged', label: true), id: 'state-merged', title: 'Filter by merge requests that are currently merged.' do %button.btn.btn-link{ id: 'state-merged', title: 'Filter by merge requests that are currently merged.', type: 'button', data: { state: 'merged' } }
#{issuables_state_counter_text(type, :merged)} #{issuables_state_counter_text(type, :merged)}
%li{ class: active_when(params[:state] == 'closed') }> - closed_title = 'Filter by merge requests that are currently closed and unmerged.'
= link_to page_filter_path(state: 'closed', label: true), id: 'state-closed', title: 'Filter by merge requests that are currently closed and unmerged.' do
#{issuables_state_counter_text(type, :closed)} %li{ class: active_when(params[:state] == 'closed') }>
- else %button.btn.btn-link{ id: 'state-closed', title: closed_title, type: 'button', data: { state: 'closed' } }
%li{ class: active_when(params[:state] == 'closed') }> #{issuables_state_counter_text(type, :closed)}
= link_to page_filter_path(state: 'closed', label: true), id: 'state-all', title: 'Filter by issues that are currently closed.' do
#{issuables_state_counter_text(type, :closed)}
%li{ class: active_when(params[:state] == 'all') }> %li{ class: active_when(params[:state] == 'all') }>
= link_to page_filter_path(state: 'all', label: true), id: 'state-all', title: "Show all #{page_context_word}." do %button.btn.btn-link{ id: 'state-all', title: "Show all #{page_context_word}.", type: 'button', data: { state: 'all' } }
#{issuables_state_counter_text(type, :all)} #{issuables_state_counter_text(type, :all)}
---
title: Perform filtered search when state tab is changed
merge_request:
author:
...@@ -12,11 +12,13 @@ Feature: Project Issues ...@@ -12,11 +12,13 @@ Feature: Project Issues
Given I should see "Release 0.4" in issues Given I should see "Release 0.4" in issues
And I should not see "Release 0.3" in issues And I should not see "Release 0.3" in issues
@javascript
Scenario: I should see closed issues Scenario: I should see closed issues
Given I click link "Closed" Given I click link "Closed"
Then I should see "Release 0.3" in issues Then I should see "Release 0.3" in issues
And I should not see "Release 0.4" in issues And I should not see "Release 0.4" in issues
@javascript
Scenario: I should see all issues Scenario: I should see all issues
Given I click link "All" Given I click link "All"
Then I should see "Release 0.3" in issues Then I should see "Release 0.3" in issues
......
...@@ -38,11 +38,13 @@ Feature: Project Merge Requests ...@@ -38,11 +38,13 @@ Feature: Project Merge Requests
When I visit merge request page "Bug NS-08" When I visit merge request page "Bug NS-08"
Then I should see the diverged commits count Then I should see the diverged commits count
@javascript
Scenario: I should see rejected merge requests Scenario: I should see rejected merge requests
Given I click link "Closed" Given I click link "Closed"
Then I should see "Feature NS-03" in merge requests Then I should see "Feature NS-03" in merge requests
And I should not see "Bug NS-04" in merge requests And I should not see "Bug NS-04" in merge requests
@javascript
Scenario: I should see all merge requests Scenario: I should see all merge requests
Given I click link "All" Given I click link "All"
Then I should see "Feature NS-03" in merge requests Then I should see "Feature NS-03" in merge requests
......
...@@ -28,7 +28,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps ...@@ -28,7 +28,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end end
step 'I click link "Closed"' do step 'I click link "Closed"' do
find('.issues-state-filters a', text: "Closed").click find('.issues-state-filters [data-state="closed"] span', text: 'Closed').click
end end
step 'I click button "Unsubscribe"' do step 'I click button "Unsubscribe"' do
...@@ -44,7 +44,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps ...@@ -44,7 +44,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
end end
step 'I click link "All"' do step 'I click link "All"' do
click_link "All" find('.issues-state-filters [data-state="all"] span', text: 'All').click
# Waits for load # Waits for load
expect(find('.issues-state-filters > .active')).to have_content 'All' expect(find('.issues-state-filters > .active')).to have_content 'All'
end end
......
...@@ -26,7 +26,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ...@@ -26,7 +26,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end end
step 'I click link "All"' do step 'I click link "All"' do
click_link "All" find('.issues-state-filters [data-state="all"] span', text: 'All').click
# Waits for load # Waits for load
expect(find('.issues-state-filters > .active')).to have_content 'All' expect(find('.issues-state-filters > .active')).to have_content 'All'
end end
...@@ -36,9 +36,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ...@@ -36,9 +36,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
end end
step 'I click link "Closed"' do step 'I click link "Closed"' do
page.within('.issues-state-filters') do find('.issues-state-filters [data-state="closed"] span', text: 'Closed').click
click_link "Closed"
end
end end
step 'I should see merge request "Wiki Feature"' do step 'I should see merge request "Wiki Feature"' do
......
...@@ -777,17 +777,17 @@ describe 'Filter issues', js: true, feature: true do ...@@ -777,17 +777,17 @@ describe 'Filter issues', js: true, feature: true do
end end
it 'open state' do it 'open state' do
find('.issues-state-filters a', text: 'Closed').click find('.issues-state-filters [data-state="closed"]').click
wait_for_requests wait_for_requests
find('.issues-state-filters a', text: 'Open').click find('.issues-state-filters [data-state="opened"]').click
wait_for_requests wait_for_requests
expect(page).to have_selector('.issues-list .issue', count: 4) expect(page).to have_selector('.issues-list .issue', count: 4)
end end
it 'closed state' do it 'closed state' do
find('.issues-state-filters a', text: 'Closed').click find('.issues-state-filters [data-state="closed"]').click
wait_for_requests wait_for_requests
expect(page).to have_selector('.issues-list .issue', count: 1) expect(page).to have_selector('.issues-list .issue', count: 1)
...@@ -795,7 +795,7 @@ describe 'Filter issues', js: true, feature: true do ...@@ -795,7 +795,7 @@ describe 'Filter issues', js: true, feature: true do
end end
it 'all state' do it 'all state' do
find('.issues-state-filters a', text: 'All').click find('.issues-state-filters [data-state="all"]').click
wait_for_requests wait_for_requests
expect(page).to have_selector('.issues-list .issue', count: 5) expect(page).to have_selector('.issues-list .issue', count: 5)
......
...@@ -40,13 +40,13 @@ describe 'Filter merge requests', feature: true do ...@@ -40,13 +40,13 @@ describe 'Filter merge requests', feature: true do
end end
it 'does not change when closed link is clicked' do it 'does not change when closed link is clicked' do
find('.issues-state-filters a', text: "Closed").click find('.issues-state-filters [data-state="closed"]').click
expect_assignee_visual_tokens() expect_assignee_visual_tokens()
end end
it 'does not change when all link is clicked' do it 'does not change when all link is clicked' do
find('.issues-state-filters a', text: "All").click find('.issues-state-filters [data-state="all"]').click
expect_assignee_visual_tokens() expect_assignee_visual_tokens()
end end
...@@ -73,13 +73,13 @@ describe 'Filter merge requests', feature: true do ...@@ -73,13 +73,13 @@ describe 'Filter merge requests', feature: true do
end end
it 'does not change when closed link is clicked' do it 'does not change when closed link is clicked' do
find('.issues-state-filters a', text: "Closed").click find('.issues-state-filters [data-state="closed"]').click
expect_milestone_visual_tokens() expect_milestone_visual_tokens()
end end
it 'does not change when all link is clicked' do it 'does not change when all link is clicked' do
find('.issues-state-filters a', text: "All").click find('.issues-state-filters [data-state="all"]').click
expect_milestone_visual_tokens() expect_milestone_visual_tokens()
end end
...@@ -142,11 +142,9 @@ describe 'Filter merge requests', feature: true do ...@@ -142,11 +142,9 @@ describe 'Filter merge requests', feature: true do
expect_tokens([{ name: 'assignee', value: "@#{user.username}" }]) expect_tokens([{ name: 'assignee', value: "@#{user.username}" }])
expect_filtered_search_input_empty expect_filtered_search_input_empty
input_filtered_search_keys("label:~#{label.title} ") input_filtered_search_keys("label:~#{label.title}")
expect_mr_list_count(1) expect_mr_list_count(1)
find("#state-opened[href=\"#{URI.parse(current_url).path}?assignee_username=#{user.username}&label_name%5B%5D=#{label.title}&scope=all&state=opened\"]")
end end
context 'assignee and label', js: true do context 'assignee and label', js: true do
...@@ -163,13 +161,13 @@ describe 'Filter merge requests', feature: true do ...@@ -163,13 +161,13 @@ describe 'Filter merge requests', feature: true do
end end
it 'does not change when closed link is clicked' do it 'does not change when closed link is clicked' do
find('.issues-state-filters a', text: "Closed").click find('.issues-state-filters [data-state="closed"]').click
expect_assignee_label_visual_tokens() expect_assignee_label_visual_tokens()
end end
it 'does not change when all link is clicked' do it 'does not change when all link is clicked' do
find('.issues-state-filters a', text: "All").click find('.issues-state-filters [data-state="all"]').click
expect_assignee_label_visual_tokens() expect_assignee_label_visual_tokens()
end end
......
...@@ -97,6 +97,49 @@ describe('Filtered Search Manager', () => { ...@@ -97,6 +97,49 @@ describe('Filtered Search Manager', () => {
}); });
}); });
describe('searchState', () => {
beforeEach(() => {
spyOn(gl.FilteredSearchManager.prototype, 'search').and.callFake(() => {});
});
it('should blur button', () => {
const e = {
currentTarget: {
blur: () => {},
},
};
spyOn(e.currentTarget, 'blur').and.callThrough();
manager.searchState(e);
expect(e.currentTarget.blur).toHaveBeenCalled();
});
it('should not call search if there is no state', () => {
const e = {
currentTarget: {
blur: () => {},
},
};
manager.searchState(e);
expect(gl.FilteredSearchManager.prototype.search).not.toHaveBeenCalled();
});
it('should call search when there is state', () => {
const e = {
currentTarget: {
blur: () => {},
dataset: {
state: 'opened',
},
},
};
manager.searchState(e);
expect(gl.FilteredSearchManager.prototype.search).toHaveBeenCalledWith('opened');
});
});
describe('search', () => { describe('search', () => {
const defaultParams = '?scope=all&utf8=%E2%9C%93&state=opened'; const defaultParams = '?scope=all&utf8=%E2%9C%93&state=opened';
......
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