Commit 79614a08 authored by Dhiraj Bodicherla's avatar Dhiraj Bodicherla Committed by Natalia Tepluhina

Added search box to dashboards dropdown

Dashboards list can get longer and the
current dropdown does not have a way to search
and filter. This MR adds a search input
parent 579b7c46
...@@ -402,7 +402,9 @@ export default { ...@@ -402,7 +402,9 @@ export default {
:text="currentEnvironmentName" :text="currentEnvironmentName"
> >
<div class="d-flex flex-column overflow-hidden"> <div class="d-flex flex-column overflow-hidden">
<gl-dropdown-header class="text-center">{{ __('Environment') }}</gl-dropdown-header> <gl-dropdown-header class="monitor-environment-dropdown-header text-center">{{
__('Environment')
}}</gl-dropdown-header>
<gl-dropdown-divider /> <gl-dropdown-divider />
<gl-search-box-by-type <gl-search-box-by-type
v-if="shouldRenderSearchableEnvironmentsDropdown" v-if="shouldRenderSearchableEnvironmentsDropdown"
......
...@@ -4,11 +4,14 @@ import { ...@@ -4,11 +4,14 @@ import {
GlAlert, GlAlert,
GlDropdown, GlDropdown,
GlDropdownItem, GlDropdownItem,
GlDropdownHeader,
GlDropdownDivider, GlDropdownDivider,
GlSearchBoxByType,
GlModal, GlModal,
GlLoadingIcon, GlLoadingIcon,
GlModalDirective, GlModalDirective,
} from '@gitlab/ui'; } from '@gitlab/ui';
import { s__ } from '~/locale';
import DuplicateDashboardForm from './duplicate_dashboard_form.vue'; import DuplicateDashboardForm from './duplicate_dashboard_form.vue';
const events = { const events = {
...@@ -20,7 +23,9 @@ export default { ...@@ -20,7 +23,9 @@ export default {
GlAlert, GlAlert,
GlDropdown, GlDropdown,
GlDropdownItem, GlDropdownItem,
GlDropdownHeader,
GlDropdownDivider, GlDropdownDivider,
GlSearchBoxByType,
GlModal, GlModal,
GlLoadingIcon, GlLoadingIcon,
DuplicateDashboardForm, DuplicateDashboardForm,
...@@ -44,6 +49,7 @@ export default { ...@@ -44,6 +49,7 @@ export default {
alert: null, alert: null,
loading: false, loading: false,
form: {}, form: {},
searchTerm: '',
}; };
}, },
computed: { computed: {
...@@ -54,6 +60,17 @@ export default { ...@@ -54,6 +60,17 @@ export default {
selectedDashboardText() { selectedDashboardText() {
return this.selectedDashboard.display_name; return this.selectedDashboard.display_name;
}, },
filteredDashboards() {
return this.allDashboards.filter(({ display_name }) =>
display_name.toLowerCase().includes(this.searchTerm.toLowerCase()),
);
},
shouldShowNoMsgContainer() {
return this.filteredDashboards.length === 0;
},
okButtonText() {
return this.loading ? s__('Metrics|Duplicating...') : s__('Metrics|Duplicate');
},
}, },
methods: { methods: {
...mapActions('monitoringDashboard', ['duplicateSystemDashboard']), ...mapActions('monitoringDashboard', ['duplicateSystemDashboard']),
...@@ -95,45 +112,70 @@ export default { ...@@ -95,45 +112,70 @@ export default {
}; };
</script> </script>
<template> <template>
<gl-dropdown toggle-class="dropdown-menu-toggle" :text="selectedDashboardText"> <gl-dropdown
<gl-dropdown-item toggle-class="dropdown-menu-toggle"
v-for="dashboard in allDashboards" menu-class="monitor-dashboard-dropdown-menu"
:key="dashboard.path" :text="selectedDashboardText"
:active="dashboard.path === selectedDashboard.path" >
active-class="is-active" <div class="d-flex flex-column overflow-hidden">
@click="selectDashboard(dashboard)" <gl-dropdown-header class="monitor-dashboard-dropdown-header text-center">{{
> __('Dashboard')
{{ dashboard.display_name || dashboard.path }} }}</gl-dropdown-header>
</gl-dropdown-item>
<template v-if="isSystemDashboard">
<gl-dropdown-divider /> <gl-dropdown-divider />
<gl-search-box-by-type
ref="monitorDashboardsDropdownSearch"
v-model="searchTerm"
class="m-2"
/>
<div class="flex-fill overflow-auto">
<gl-dropdown-item
v-for="dashboard in filteredDashboards"
:key="dashboard.path"
:active="dashboard.path === selectedDashboard.path"
active-class="is-active"
@click="selectDashboard(dashboard)"
>
{{ dashboard.display_name || dashboard.path }}
</gl-dropdown-item>
</div>
<gl-modal <div
ref="duplicateDashboardModal" v-show="shouldShowNoMsgContainer"
modal-id="duplicateDashboardModal" ref="monitorDashboardsDropdownMsg"
:title="s__('Metrics|Duplicate dashboard')" class="text-secondary no-matches-message"
ok-variant="success"
@ok="ok"
@hide="hide"
> >
<gl-alert v-if="alert" class="mb-3" variant="danger" @dismiss="alert = null"> {{ __('No matching results') }}
{{ alert }} </div>
</gl-alert>
<duplicate-dashboard-form <template v-if="isSystemDashboard">
:dashboard="selectedDashboard" <gl-dropdown-divider />
:default-branch="defaultBranch"
@change="formChange" <gl-modal
/> ref="duplicateDashboardModal"
<template #modal-ok> modal-id="duplicateDashboardModal"
<gl-loading-icon v-if="loading" inline color="light" /> :title="s__('Metrics|Duplicate dashboard')"
{{ loading ? s__('Metrics|Duplicating...') : s__('Metrics|Duplicate') }} ok-variant="success"
</template> @ok="ok"
</gl-modal> @hide="hide"
>
<gl-alert v-if="alert" class="mb-3" variant="danger" @dismiss="alert = null">
{{ alert }}
</gl-alert>
<duplicate-dashboard-form
:dashboard="selectedDashboard"
:default-branch="defaultBranch"
@change="formChange"
/>
<template #modal-ok>
<gl-loading-icon v-if="loading" inline color="light" />
{{ okButtonText }}
</template>
</gl-modal>
<gl-dropdown-item ref="duplicateDashboardItem" v-gl-modal="'duplicateDashboardModal'"> <gl-dropdown-item ref="duplicateDashboardItem" v-gl-modal="'duplicateDashboardModal'">
{{ s__('Metrics|Duplicate dashboard') }} {{ s__('Metrics|Duplicate dashboard') }}
</gl-dropdown-item> </gl-dropdown-item>
</template> </template>
</div>
</gl-dropdown> </gl-dropdown>
</template> </template>
...@@ -47,7 +47,12 @@ ...@@ -47,7 +47,12 @@
} }
.prometheus-graphs-header { .prometheus-graphs-header {
.monitor-environment-dropdown-menu { .monitor-dashboard-dropdown-header header {
font-size: $gl-font-size;
}
.monitor-environment-dropdown-menu,
.monitor-dashboard-dropdown-menu {
&.show { &.show {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
......
---
title: Added search box to dashboards dropdown in monitoring dashboard
merge_request: 23906
author:
type: added
...@@ -44,7 +44,7 @@ exports[`Dashboard template matches the default snapshot 1`] = ` ...@@ -44,7 +44,7 @@ exports[`Dashboard template matches the default snapshot 1`] = `
class="d-flex flex-column overflow-hidden" class="d-flex flex-column overflow-hidden"
> >
<gl-dropdown-header-stub <gl-dropdown-header-stub
class="text-center" class="monitor-environment-dropdown-header text-center"
> >
Environment Environment
</gl-dropdown-header-stub> </gl-dropdown-header-stub>
......
...@@ -35,13 +35,17 @@ describe('DashboardsDropdown', () => { ...@@ -35,13 +35,17 @@ describe('DashboardsDropdown', () => {
const findItems = () => wrapper.findAll(GlDropdownItem); const findItems = () => wrapper.findAll(GlDropdownItem);
const findItemAt = i => wrapper.findAll(GlDropdownItem).at(i); const findItemAt = i => wrapper.findAll(GlDropdownItem).at(i);
const findSearchInput = () => wrapper.find({ ref: 'monitorDashboardsDropdownSearch' });
const findNoItemsMsg = () => wrapper.find({ ref: 'monitorDashboardsDropdownMsg' });
const setSearchTerm = searchTerm => wrapper.setData({ searchTerm });
describe('when it receives dashboards data', () => { describe('when it receives dashboards data', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent(); wrapper = createComponent();
}); });
it('displays an item for each dashboard', () => { it('displays an item for each dashboard', () => {
expect(wrapper.findAll(GlDropdownItem).length).toEqual(dashboardGitResponse.length); expect(findItems().length).toEqual(dashboardGitResponse.length);
}); });
it('displays items with the dashboard display name', () => { it('displays items with the dashboard display name', () => {
...@@ -49,6 +53,32 @@ describe('DashboardsDropdown', () => { ...@@ -49,6 +53,32 @@ describe('DashboardsDropdown', () => {
expect(findItemAt(1).text()).toBe(dashboardGitResponse[1].display_name); expect(findItemAt(1).text()).toBe(dashboardGitResponse[1].display_name);
expect(findItemAt(2).text()).toBe(dashboardGitResponse[2].display_name); expect(findItemAt(2).text()).toBe(dashboardGitResponse[2].display_name);
}); });
it('displays a search input', () => {
expect(findSearchInput().isVisible()).toBe(true);
});
it('hides no message text by default', () => {
expect(findNoItemsMsg().isVisible()).toBe(false);
});
it('filters dropdown items when searched for item exists in the list', () => {
const searchTerm = 'Default';
setSearchTerm(searchTerm);
return wrapper.vm.$nextTick(() => {
expect(findItems()).toHaveLength(1);
});
});
it('shows no items found message when searched for item does not exists in the list', () => {
const searchTerm = 'does-not-exist';
setSearchTerm(searchTerm);
return wrapper.vm.$nextTick(() => {
expect(findNoItemsMsg().isVisible()).toBe(true);
});
});
}); });
describe('when a system dashboard is selected', () => { describe('when a system dashboard is selected', () => {
...@@ -224,7 +254,7 @@ describe('DashboardsDropdown', () => { ...@@ -224,7 +254,7 @@ describe('DashboardsDropdown', () => {
it('displays an item for each dashboard', () => { it('displays an item for each dashboard', () => {
const item = wrapper.findAll({ ref: 'duplicateDashboardItem' }); const item = wrapper.findAll({ ref: 'duplicateDashboardItem' });
expect(findItems().length).toEqual(dashboardGitResponse.length); expect(findItems()).toHaveLength(dashboardGitResponse.length);
expect(item.length).toBe(0); expect(item.length).toBe(0);
}); });
......
...@@ -518,6 +518,15 @@ export const metricsDashboardPayload = { ...@@ -518,6 +518,15 @@ export const metricsDashboardPayload = {
], ],
}; };
const customDashboardsData = new Array(30).fill(null).map((_, idx) => ({
default: false,
display_name: `Custom Dashboard ${idx}`,
can_edit: true,
system_dashboard: false,
project_blob_path: `${mockProjectDir}/blob/master/dashboards/.gitlab/dashboards/dashboard_${idx}.yml`,
path: `.gitlab/dashboards/dashboard_${idx}.yml`,
}));
export const dashboardGitResponse = [ export const dashboardGitResponse = [
{ {
default: true, default: true,
...@@ -527,22 +536,7 @@ export const dashboardGitResponse = [ ...@@ -527,22 +536,7 @@ export const dashboardGitResponse = [
project_blob_path: null, project_blob_path: null,
path: 'config/prometheus/common_metrics.yml', path: 'config/prometheus/common_metrics.yml',
}, },
{ ...customDashboardsData,
default: false,
display_name: 'Custom Dashboard 1',
can_edit: true,
system_dashboard: false,
project_blob_path: `${mockProjectDir}/blob/master/dashboards/.gitlab/dashboards/dashboard_1.yml`,
path: '.gitlab/dashboards/dashboard_1.yml',
},
{
default: false,
display_name: 'Custom Dashboard 2',
can_edit: true,
system_dashboard: false,
project_blob_path: `${mockProjectDir}/blob/master/dashboards/.gitlab/dashboards/dashboard_2.yml`,
path: '.gitlab/dashboards/dashboard_2.yml',
},
]; ];
export const graphDataPrometheusQuery = { export const graphDataPrometheusQuery = {
......
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