Commit 89b30999 authored by Alexis Reigel's avatar Alexis Reigel

use filtered search bar for admin runners

parent d4387d88
import FilteredSearchTokenKeys from './filtered_search_token_keys';
const tokenKeys = [{
key: 'status',
type: 'string',
param: 'status',
symbol: '',
icon: 'signal',
tag: 'status',
}];
const AdminRunnersFilteredSearchTokenKeys = new FilteredSearchTokenKeys(tokenKeys);
export default AdminRunnersFilteredSearchTokenKeys;
...@@ -7,6 +7,7 @@ import DropdownHint from './dropdown_hint'; ...@@ -7,6 +7,7 @@ import DropdownHint from './dropdown_hint';
import DropdownEmoji from './dropdown_emoji'; import DropdownEmoji from './dropdown_emoji';
import DropdownNonUser from './dropdown_non_user'; import DropdownNonUser from './dropdown_non_user';
import DropdownUser from './dropdown_user'; import DropdownUser from './dropdown_user';
import NullDropdown from './null_dropdown';
import FilteredSearchVisualTokens from './filtered_search_visual_tokens'; import FilteredSearchVisualTokens from './filtered_search_visual_tokens';
export default class FilteredSearchDropdownManager { export default class FilteredSearchDropdownManager {
...@@ -90,6 +91,11 @@ export default class FilteredSearchDropdownManager { ...@@ -90,6 +91,11 @@ export default class FilteredSearchDropdownManager {
gl: DropdownEmoji, gl: DropdownEmoji,
element: this.container.querySelector('#js-dropdown-my-reaction'), element: this.container.querySelector('#js-dropdown-my-reaction'),
}, },
status: {
reference: null,
gl: NullDropdown,
element: this.container.querySelector('#js-dropdown-admin-runner-status'),
},
}; };
supportedTokens.forEach((type) => { supportedTokens.forEach((type) => {
......
import FilteredSearchDropdown from './filtered_search_dropdown';
export default class NullDropdown extends FilteredSearchDropdown {
renderContent(forceShowList = false) {
this.droplab.changeHookList(this.hookId, this.dropdown, [], this.config);
super.renderContent(forceShowList);
}
}
import initFilteredSearch from '~/pages/search/init_filtered_search';
import AdminRunnersFilteredSearchTokenKeys from '~/filtered_search/admin_runners_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants';
document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({
page: FILTERED_SEARCH.ADMIN_RUNNERS,
filteredSearchTokenKeys: AdminRunnersFilteredSearchTokenKeys,
});
});
...@@ -3,4 +3,5 @@ ...@@ -3,4 +3,5 @@
export const FILTERED_SEARCH = { export const FILTERED_SEARCH = {
MERGE_REQUESTS: 'merge_requests', MERGE_REQUESTS: 'merge_requests',
ISSUES: 'issues', ISSUES: 'issues',
ADMIN_RUNNERS: 'admin/runners',
}; };
...@@ -26,8 +26,9 @@ class Admin::RunnersFinder < UnionFinder ...@@ -26,8 +26,9 @@ class Admin::RunnersFinder < UnionFinder
end end
def filter_by_status! def filter_by_status!
if @params[:status].present? && Ci::Runner::AVAILABLE_STATUSES.include?(@params[:status]) status = @params[:status_status]
@runners = @runners.public_send(@params[:status]) # rubocop:disable GitlabSecurity/PublicSend if status.present? && Ci::Runner::AVAILABLE_STATUSES.include?(status)
@runners = @runners.public_send(status) # rubocop:disable GitlabSecurity/PublicSend
end end
end end
......
- active_status = params[:status].presence
- toggle_text = 'Status'
- if active_status
= hidden_field_tag :status, params[:status]
- toggle_text = params[:status].titleize
= dropdown_tag(toggle_text, options: { wrapper_class: 'dropdown-menu-selectable', title: 'Statuses' }) do
%ul
%li= link_to 'Any Status', admin_runners_path(safe_params.slice(:search)), class: ('is-active' unless active_status)
%li.divider
- Ci::Runner::AVAILABLE_STATUSES.each do |status|
%li= link_to status.titleize, admin_runners_path(safe_params.slice(:search).merge(status: status)), class: ('is-active' if active_status == status)
...@@ -41,18 +41,50 @@ ...@@ -41,18 +41,50 @@
= render partial: 'ci/runner/how_to_setup_shared_runner', = render partial: 'ci/runner/how_to_setup_shared_runner',
locals: { registration_token: Gitlab::CurrentSettings.runners_registration_token } locals: { registration_token: Gitlab::CurrentSettings.runners_registration_token }
.append-bottom-20.clearfix .row-content-block.second-block
= form_tag admin_runners_path, id: 'runners-search', method: :get do = form_tag admin_runners_path, id: 'runners-search', method: :get, class: 'filter-form js-filter-form' do
.float-left .filtered-search-wrapper
.form-inline .filtered-search-box
.form-group = dropdown_tag(custom_icon('icon_history'),
= search_field_tag :search, params[:search], class: 'form-control input-short', placeholder: 'Runner description or token', spellcheck: false options: { wrapper_class: "filtered-search-history-dropdown-wrapper",
= submit_tag 'Search', class: 'btn' toggle_class: "filtered-search-history-dropdown-toggle-button",
dropdown_class: "filtered-search-history-dropdown",
.float-left.prepend-left-10 content_class: "filtered-search-history-dropdown-content",
= render 'statuses' title: "Recent searches" }) do
.js-filtered-search-history-dropdown{ data: { full_path: admin_runners_path } }
.float-right.light .filtered-search-box-input-container.droplab-dropdown
.scroll-container
%ul.tokens-container.list-unstyled
%li.input-token
%input.form-control.filtered-search{ { id: 'filtered-search-runners', placeholder: 'Search or filter results...' } }
#js-dropdown-hint.filtered-search-input-dropdown-menu.dropdown-menu.hint-dropdown
%ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { action: 'submit' } }
= button_tag class: %w[btn btn-link] do
= icon('search')
%span
Press Enter or click to search
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
= button_tag class: %w[btn btn-link] do
-# Encapsulate static class name `{{icon}}` inside #{} to bypass
-# haml lint's ClassAttributeWithStaticValue
%i.fa{ class: "#{'{{icon}}'}" }
%span.js-filter-hint
{{hint}}
%span.js-filter-tag.dropdown-light-content
{{tag}}
#js-dropdown-admin-runner-status.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } }
- Ci::Runner::AVAILABLE_STATUSES.each do |status|
%li.filter-dropdown-item{ data: { value: status } }
= button_tag class: %w[btn btn-link] do
= status.titleize
= button_tag class: %w[clear-search hidden] do
= icon('times')
.clearfix
.float-right.light
.prepend-top-10
Runners currently online: #{@active_runners_cnt} Runners currently online: #{@active_runners_cnt}
%br %br
......
require 'spec_helper' require 'spec_helper'
describe "Admin Runners" do describe "Admin Runners", :js do
include StubENV include StubENV
include FilteredSearchHelpers
before do before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
...@@ -30,26 +31,20 @@ describe "Admin Runners" do ...@@ -30,26 +31,20 @@ describe "Admin Runners" do
end end
it 'shows correct runner when description matches' do it 'shows correct runner when description matches' do
within '#runners-search' do input_filtered_search_keys('runner-foo')
fill_in 'search', with: 'runner-foo'
click_button 'Search'
end
expect(page).to have_content("runner-foo") expect(page).to have_content("runner-foo")
expect(page).not_to have_content("runner-bar") expect(page).not_to have_content("runner-bar")
end end
it 'shows no runner when description does not match' do it 'shows no runner when description does not match' do
within '#runners-search' do input_filtered_search_keys('runner-baz')
fill_in 'search', with: 'runner-baz'
click_button 'Search'
end
expect(page).to have_text 'No runners found' expect(page).to have_text 'No runners found'
end end
end end
describe 'filter by status', :js do describe 'filter by status' do
it 'shows correct runner when status matches' do it 'shows correct runner when status matches' do
FactoryBot.create :ci_runner, description: 'runner-active', active: true FactoryBot.create :ci_runner, description: 'runner-active', active: true
FactoryBot.create :ci_runner, description: 'runner-paused', active: false FactoryBot.create :ci_runner, description: 'runner-paused', active: false
...@@ -59,8 +54,7 @@ describe "Admin Runners" do ...@@ -59,8 +54,7 @@ describe "Admin Runners" do
expect(page).to have_content 'runner-active' expect(page).to have_content 'runner-active'
expect(page).to have_content 'runner-paused' expect(page).to have_content 'runner-paused'
click_button 'Status' input_filtered_search_keys('status:active')
click_link 'Active'
expect(page).to have_content 'runner-active' expect(page).to have_content 'runner-active'
expect(page).not_to have_content 'runner-paused' expect(page).not_to have_content 'runner-paused'
end end
...@@ -71,8 +65,7 @@ describe "Admin Runners" do ...@@ -71,8 +65,7 @@ describe "Admin Runners" do
visit admin_runners_path visit admin_runners_path
click_button 'Status' input_filtered_search_keys('status:offline')
click_link 'Offline'
expect(page).not_to have_content 'runner-active' expect(page).not_to have_content 'runner-active'
expect(page).not_to have_content 'runner-paused' expect(page).not_to have_content 'runner-paused'
...@@ -81,48 +74,22 @@ describe "Admin Runners" do ...@@ -81,48 +74,22 @@ describe "Admin Runners" do
end end
end end
describe 'filter by status and enter search term', :js do it 'shows correct runner when status is selected and search term is entered' do
before do FactoryBot.create :ci_runner, description: 'runner-a-1', active: true
FactoryBot.create :ci_runner, description: 'runner-a-1', active: true FactoryBot.create :ci_runner, description: 'runner-a-2', active: false
FactoryBot.create :ci_runner, description: 'runner-a-2', active: false FactoryBot.create :ci_runner, description: 'runner-b-1', active: true
FactoryBot.create :ci_runner, description: 'runner-b-1', active: true
visit admin_runners_path
end
it 'shows correct runner when status is selected first and then search term is entered' do
click_button 'Status'
click_link 'Active'
expect(page).to have_content 'runner-a-1'
expect(page).to have_content 'runner-b-1'
expect(page).not_to have_content 'runner-a-2'
within '#runners-search' do visit admin_runners_path
fill_in 'search', with: 'runner-a'
click_button 'Search'
end
expect(page).to have_content 'runner-a-1'
expect(page).not_to have_content 'runner-b-1'
expect(page).not_to have_content 'runner-a-2'
end
it 'shows correct runner when search term is entered first and then status is selected' do
within '#runners-search' do
fill_in 'search', with: 'runner-a'
click_button 'Search'
end
expect(page).to have_content 'runner-a-1' input_filtered_search_keys('status:active')
expect(page).to have_content 'runner-a-2' expect(page).to have_content 'runner-a-1'
expect(page).not_to have_content 'runner-b-1' expect(page).to have_content 'runner-b-1'
expect(page).not_to have_content 'runner-a-2'
click_button 'Status' input_filtered_search_keys('status:active runner-a')
click_link 'Active' expect(page).to have_content 'runner-a-1'
expect(page).to have_content 'runner-a-1' expect(page).not_to have_content 'runner-b-1'
expect(page).not_to have_content 'runner-b-1' expect(page).not_to have_content 'runner-a-2'
expect(page).not_to have_content 'runner-a-2'
end
end end
end end
......
...@@ -23,7 +23,7 @@ describe Admin::RunnersFinder do ...@@ -23,7 +23,7 @@ describe Admin::RunnersFinder do
it 'calls the corresponding scope on Ci::Runner' do it 'calls the corresponding scope on Ci::Runner' do
expect(Ci::Runner).to receive(:paused).and_call_original expect(Ci::Runner).to receive(:paused).and_call_original
described_class.new(params: { status: 'paused' }).execute described_class.new(params: { status_status: 'paused' }).execute
end end
end end
......
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