Commit 2bd69e20 authored by Miguel Rincon's avatar Miguel Rincon

Relocate online runners count in search bar

Use a single stat component to add a global count of online runners
between the breadcrumbs and tabs/filters.

Changelog: changed
parent 05004557
......@@ -3,12 +3,12 @@ import { GlBadge, GlLink } from '@gitlab/ui';
import createFlash from '~/flash';
import { fetchPolicies } from '~/lib/graphql';
import { updateHistory } from '~/lib/utils/url_utility';
import { sprintf, __ } from '~/locale';
import RegistrationDropdown from '../components/registration/registration_dropdown.vue';
import RunnerFilteredSearchBar from '../components/runner_filtered_search_bar.vue';
import RunnerList from '../components/runner_list.vue';
import RunnerName from '../components/runner_name.vue';
import RunnerOnlineStat from '../components/stat/runner_online_stat.vue';
import RunnerPagination from '../components/runner_pagination.vue';
import RunnerTypeTabs from '../components/runner_type_tabs.vue';
......@@ -38,6 +38,7 @@ export default {
RunnerFilteredSearchBar,
RunnerList,
RunnerName,
RunnerOnlineStat,
RunnerPagination,
RunnerTypeTabs,
},
......@@ -110,11 +111,6 @@ export default {
noRunnersFound() {
return !this.runnersLoading && !this.runners.items.length;
},
activeRunnersMessage() {
return sprintf(__('Runners currently online: %{active_runners_count}'), {
active_runners_count: this.activeRunnersCount,
});
},
searchTokens() {
return [
statusTokenConfig,
......@@ -165,6 +161,8 @@ export default {
</script>
<template>
<div>
<runner-online-stat class="gl-py-6 gl-px-5" :value="activeRunnersCount" />
<div
class="gl-display-flex gl-align-items-center gl-flex-direction-column-reverse gl-md-flex-direction-row gl-mt-3 gl-md-mt-0"
>
......@@ -194,11 +192,7 @@ export default {
v-model="search"
:tokens="searchTokens"
:namespace="$options.filteredSearchNamespace"
>
<template #runner-count>
{{ activeRunnersMessage }}
</template>
</runner-filtered-search-bar>
/>
<div v-if="noRunnersFound" class="gl-text-center gl-p-5">
{{ __('No runners found') }}
......
......@@ -76,24 +76,18 @@ export default {
};
</script>
<template>
<div
<filtered-search
class="gl-bg-gray-10 gl-p-5 gl-border-solid gl-border-gray-100 gl-border-0 gl-border-t-1 gl-border-b-1"
>
<filtered-search
v-bind="$attrs"
:namespace="namespace"
recent-searches-storage-key="runners-search"
:sort-options="$options.sortOptions"
:initial-filter-value="initialFilterValue"
:tokens="tokens"
:initial-sort-by="initialSortBy"
:search-input-placeholder="__('Search or filter results...')"
data-testid="runners-filtered-search"
@onFilter="onFilter"
@onSort="onSort"
/>
<div class="gl-text-right" data-testid="runner-count">
<slot name="runner-count"></slot>
</div>
</div>
v-bind="$attrs"
:namespace="namespace"
recent-searches-storage-key="runners-search"
:sort-options="$options.sortOptions"
:initial-filter-value="initialFilterValue"
:tokens="tokens"
:initial-sort-by="initialSortBy"
:search-input-placeholder="__('Search or filter results...')"
data-testid="runners-filtered-search"
@onFilter="onFilter"
@onSort="onSort"
/>
</template>
<script>
import { GlSingleStat } from '@gitlab/ui/dist/charts';
export default {
components: {
GlSingleStat,
},
};
</script>
<template>
<gl-single-stat
v-bind="$attrs"
variant="success"
:title="s__('Runners|Online Runners')"
:meta-text="s__('Runners|online')"
/>
</template>
......@@ -9,6 +9,7 @@ import RegistrationDropdown from '../components/registration/registration_dropdo
import RunnerFilteredSearchBar from '../components/runner_filtered_search_bar.vue';
import RunnerList from '../components/runner_list.vue';
import RunnerName from '../components/runner_name.vue';
import RunnerOnlineStat from '../components/stat/runner_online_stat.vue';
import RunnerPagination from '../components/runner_pagination.vue';
import RunnerTypeTabs from '../components/runner_type_tabs.vue';
......@@ -35,6 +36,7 @@ export default {
RunnerFilteredSearchBar,
RunnerList,
RunnerName,
RunnerOnlineStat,
RunnerPagination,
RunnerTypeTabs,
},
......@@ -145,6 +147,8 @@ export default {
<template>
<div>
<runner-online-stat class="gl-py-6 gl-px-5" :value="groupRunnersCount" />
<div class="gl-display-flex gl-align-items-center">
<runner-type-tabs
v-model="search"
......@@ -164,11 +168,7 @@ export default {
v-model="search"
:tokens="searchTokens"
:namespace="filteredSearchNamespace"
>
<template #runner-count>
{{ runnerCountMessage }}
</template>
</runner-filtered-search-bar>
/>
<div v-if="noRunnersFound" class="gl-text-center gl-p-5">
{{ __('No runners found') }}
......
......@@ -29945,6 +29945,9 @@ msgstr ""
msgid "Runners|Online"
msgstr ""
msgid "Runners|Online Runners"
msgstr ""
msgid "Runners|Paused"
msgstr ""
......
......@@ -26,7 +26,7 @@ RSpec.describe "Admin Runners" do
visit admin_runners_path
expect(page).to have_text "Register an instance runner"
expect(page).to have_text "Runners currently online: 1"
expect(page).to have_text "Online Runners 1"
end
it 'with an instance runner shows an instance badge' do
......@@ -324,7 +324,7 @@ RSpec.describe "Admin Runners" do
it 'has all necessary texts including no runner message' do
expect(page).to have_text "Register an instance runner"
expect(page).to have_text "Runners currently online: 0"
expect(page).to have_text "Online Runners 0"
expect(page).to have_text 'No runners found'
end
end
......
......@@ -155,9 +155,7 @@ describe('AdminRunnersApp', () => {
it('shows the active runner count', () => {
createComponent({ mountFn: mount });
expect(findRunnerFilteredSearchBar().text()).toMatch(
`Runners currently online: ${mockActiveRunnersCount}`,
);
expect(wrapper.text()).toMatch(new RegExp(`Online Runners ${mockActiveRunnersCount}`));
});
describe('when a filter is preselected', () => {
......
......@@ -15,7 +15,6 @@ describe('RunnerList', () => {
const findFilteredSearch = () => wrapper.findComponent(FilteredSearch);
const findGlFilteredSearch = () => wrapper.findComponent(GlFilteredSearch);
const findSortOptions = () => wrapper.findAllComponents(GlDropdownItem);
const findActiveRunnersMessage = () => wrapper.findByTestId('runner-count');
const mockDefaultSort = 'CREATED_DESC';
const mockOtherSort = 'CONTACTED_DESC';
......@@ -23,7 +22,6 @@ describe('RunnerList', () => {
{ type: PARAM_KEY_STATUS, value: { data: STATUS_ACTIVE, operator: '=' } },
{ type: 'filtered-search-term', value: { data: '' } },
];
const mockActiveRunnersCount = 2;
const expectToHaveLastEmittedInput = (value) => {
const inputs = wrapper.emitted('input');
......@@ -43,9 +41,6 @@ describe('RunnerList', () => {
},
...props,
},
slots: {
'runner-count': `Runners currently online: ${mockActiveRunnersCount}`,
},
stubs: {
FilteredSearch,
GlFilteredSearch,
......@@ -69,12 +64,6 @@ describe('RunnerList', () => {
expect(findFilteredSearch().props('namespace')).toBe('runners');
});
it('Displays an active runner count', () => {
expect(findActiveRunnersMessage().text()).toBe(
`Runners currently online: ${mockActiveRunnersCount}`,
);
});
it('sets sorting options', () => {
const SORT_OPTIONS_COUNT = 2;
......
import { GlSingleStat } from '@gitlab/ui/dist/charts';
import { shallowMount, mount } from '@vue/test-utils';
import RunnerOnlineBadge from '~/runner/components/stat/runner_online_stat.vue';
describe('RunnerOnlineBadge', () => {
let wrapper;
const findSingleStat = () => wrapper.findComponent(GlSingleStat);
const createComponent = ({ props = {} } = {}, mountFn = shallowMount) => {
wrapper = mountFn(RunnerOnlineBadge, {
propsData: {
value: '99',
...props,
},
});
};
afterEach(() => {
wrapper.destroy();
});
it('Uses a success appearance', () => {
createComponent({}, shallowMount);
expect(findSingleStat().props('variant')).toBe('success');
});
it('Renders a value', () => {
createComponent({}, mount);
expect(wrapper.text()).toMatch(new RegExp(`Online Runners 99\\s+online`));
});
});
......@@ -130,24 +130,24 @@ describe('GroupRunnersApp', () => {
});
describe('shows the active runner count', () => {
const expectedOnlineCount = (count) => new RegExp(`Online Runners ${count}`);
it('with a regular value', () => {
createComponent({ mountFn: mount });
expect(findRunnerFilteredSearchBar().text()).toMatch(
`Runners in this group: ${mockGroupRunnersLimitedCount}`,
);
expect(wrapper.text()).toMatch(expectedOnlineCount(mockGroupRunnersLimitedCount));
});
it('at the limit', () => {
createComponent({ props: { groupRunnersLimitedCount: 1000 }, mountFn: mount });
expect(findRunnerFilteredSearchBar().text()).toMatch(`Runners in this group: 1,000`);
expect(wrapper.text()).toMatch(expectedOnlineCount('1,000'));
});
it('over the limit', () => {
createComponent({ props: { groupRunnersLimitedCount: 1001 }, mountFn: mount });
expect(findRunnerFilteredSearchBar().text()).toMatch(`Runners in this group: 1,000+`);
expect(wrapper.text()).toMatch(expectedOnlineCount('1,000\\+'));
});
});
......
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