Commit a7190223 authored by Miguel Rincon's avatar Miguel Rincon

Display a star next to `starred` dashboards

When dashboards are fetched with a new `starred` property,
they are displayed with an icon indicating they have been starred.
parent 071e8dac
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { mapState, mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
import { import {
GlAlert, GlAlert,
GlIcon,
GlDropdown, GlDropdown,
GlDropdownItem, GlDropdownItem,
GlDropdownHeader, GlDropdownHeader,
...@@ -21,6 +22,7 @@ const events = { ...@@ -21,6 +22,7 @@ const events = {
export default { export default {
components: { components: {
GlAlert, GlAlert,
GlIcon,
GlDropdown, GlDropdown,
GlDropdownItem, GlDropdownItem,
GlDropdownHeader, GlDropdownHeader,
...@@ -60,20 +62,31 @@ export default { ...@@ -60,20 +62,31 @@ export default {
selectedDashboardText() { selectedDashboardText() {
return this.selectedDashboard.display_name; return this.selectedDashboard.display_name;
}, },
filteredDashboards() { filteredDashboards() {
return this.allDashboards.filter(({ display_name }) => return this.allDashboards.filter(({ display_name = '' }) =>
display_name.toLowerCase().includes(this.searchTerm.toLowerCase()), display_name.toLowerCase().includes(this.searchTerm.toLowerCase()),
); );
}, },
shouldShowNoMsgContainer() { shouldShowNoMsgContainer() {
return this.filteredDashboards.length === 0; return this.filteredDashboards.length === 0;
}, },
starredDashboards() {
return this.filteredDashboards.filter(({ starred }) => starred);
},
nonStarredDashboards() {
return this.filteredDashboards.filter(({ starred }) => !starred);
},
okButtonText() { okButtonText() {
return this.loading ? s__('Metrics|Duplicating...') : s__('Metrics|Duplicate'); return this.loading ? s__('Metrics|Duplicating...') : s__('Metrics|Duplicate');
}, },
}, },
methods: { methods: {
...mapActions('monitoringDashboard', ['duplicateSystemDashboard']), ...mapActions('monitoringDashboard', ['duplicateSystemDashboard']),
dashboardDisplayName(dashboard) {
return dashboard.display_name || dashboard.path || '';
},
selectDashboard(dashboard) { selectDashboard(dashboard) {
this.$emit(events.selectDashboard, dashboard); this.$emit(events.selectDashboard, dashboard);
}, },
...@@ -127,15 +140,34 @@ export default { ...@@ -127,15 +140,34 @@ export default {
v-model="searchTerm" v-model="searchTerm"
class="m-2" class="m-2"
/> />
<div class="flex-fill overflow-auto"> <div class="flex-fill overflow-auto">
<gl-dropdown-item <gl-dropdown-item
v-for="dashboard in filteredDashboards" v-for="dashboard in starredDashboards"
:key="dashboard.path"
:active="dashboard.path === selectedDashboard.path"
active-class="is-active"
@click="selectDashboard(dashboard)"
>
<div class="d-flex">
{{ dashboardDisplayName(dashboard) }}
<gl-icon class="text-muted ml-auto" name="star" />
</div>
</gl-dropdown-item>
<gl-dropdown-divider
v-if="starredDashboards.length && nonStarredDashboards.length"
ref="starredListDivider"
/>
<gl-dropdown-item
v-for="dashboard in nonStarredDashboards"
:key="dashboard.path" :key="dashboard.path"
:active="dashboard.path === selectedDashboard.path" :active="dashboard.path === selectedDashboard.path"
active-class="is-active" active-class="is-active"
@click="selectDashboard(dashboard)" @click="selectDashboard(dashboard)"
> >
{{ dashboard.display_name || dashboard.path }} {{ dashboardDisplayName(dashboard) }}
</gl-dropdown-item> </gl-dropdown-item>
</div> </div>
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { GlDropdownItem, GlModal, GlLoadingIcon, GlAlert } from '@gitlab/ui'; import { GlDropdownItem, GlModal, GlLoadingIcon, GlAlert, GlIcon } from '@gitlab/ui';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import DashboardsDropdown from '~/monitoring/components/dashboards_dropdown.vue'; import DashboardsDropdown from '~/monitoring/components/dashboards_dropdown.vue';
...@@ -9,36 +9,45 @@ import { dashboardGitResponse } from '../mock_data'; ...@@ -9,36 +9,45 @@ import { dashboardGitResponse } from '../mock_data';
const defaultBranch = 'master'; const defaultBranch = 'master';
function createComponent(props, opts = {}) { const starredDashboards = dashboardGitResponse.filter(({ starred }) => starred);
const storeOpts = { const notStarredDashboards = dashboardGitResponse.filter(({ starred }) => !starred);
methods: {
duplicateSystemDashboard: jest.fn(),
},
computed: {
allDashboards: () => dashboardGitResponse,
},
};
return shallowMount(DashboardsDropdown, {
propsData: {
...props,
defaultBranch,
},
sync: false,
...storeOpts,
...opts,
});
}
describe('DashboardsDropdown', () => { describe('DashboardsDropdown', () => {
let wrapper; let wrapper;
let mockDashboards;
function createComponent(props, opts = {}) {
const storeOpts = {
methods: {
duplicateSystemDashboard: jest.fn(),
},
computed: {
allDashboards: () => mockDashboards,
},
};
return shallowMount(DashboardsDropdown, {
propsData: {
...props,
defaultBranch,
},
sync: false,
...storeOpts,
...opts,
});
}
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 findSearchInput = () => wrapper.find({ ref: 'monitorDashboardsDropdownSearch' });
const findNoItemsMsg = () => wrapper.find({ ref: 'monitorDashboardsDropdownMsg' }); const findNoItemsMsg = () => wrapper.find({ ref: 'monitorDashboardsDropdownMsg' });
const findStarredListDivider = () => wrapper.find({ ref: 'starredListDivider' });
const setSearchTerm = searchTerm => wrapper.setData({ searchTerm }); const setSearchTerm = searchTerm => wrapper.setData({ searchTerm });
beforeEach(() => {
mockDashboards = dashboardGitResponse;
});
describe('when it receives dashboards data', () => { describe('when it receives dashboards data', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent(); wrapper = createComponent();
...@@ -48,10 +57,14 @@ describe('DashboardsDropdown', () => { ...@@ -48,10 +57,14 @@ describe('DashboardsDropdown', () => {
expect(findItems().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, with starred dashboards first', () => {
expect(findItemAt(0).text()).toBe(dashboardGitResponse[0].display_name); expect(findItemAt(0).text()).toBe(starredDashboards[0].display_name);
expect(findItemAt(1).text()).toBe(dashboardGitResponse[1].display_name); expect(findItemAt(1).text()).toBe(notStarredDashboards[0].display_name);
expect(findItemAt(2).text()).toBe(dashboardGitResponse[2].display_name); expect(findItemAt(2).text()).toBe(notStarredDashboards[1].display_name);
});
it('displays separator between starred and not starred dashboards', () => {
expect(findStarredListDivider().exists()).toBe(true);
}); });
it('displays a search input', () => { it('displays a search input', () => {
...@@ -81,6 +94,60 @@ describe('DashboardsDropdown', () => { ...@@ -81,6 +94,60 @@ describe('DashboardsDropdown', () => {
}); });
}); });
describe('when the dashboard is missing a display name', () => {
beforeEach(() => {
mockDashboards = dashboardGitResponse.map(d => ({ ...d, display_name: undefined }));
wrapper = createComponent();
});
it('displays items with the dashboard path, with starred dashboards first', () => {
expect(findItemAt(0).text()).toBe(starredDashboards[0].path);
expect(findItemAt(1).text()).toBe(notStarredDashboards[0].path);
expect(findItemAt(2).text()).toBe(notStarredDashboards[1].path);
});
});
describe('when it receives starred dashboards', () => {
beforeEach(() => {
mockDashboards = starredDashboards;
wrapper = createComponent();
});
it('displays an item for each dashboard', () => {
expect(findItems().length).toEqual(starredDashboards.length);
});
it('displays a star icon', () => {
const star = findItemAt(0).find(GlIcon);
expect(star.exists()).toBe(true);
expect(star.attributes('name')).toBe('star');
});
it('displays no separator between starred and not starred dashboards', () => {
expect(findStarredListDivider().exists()).toBe(false);
});
});
describe('when it receives only not-starred dashboards', () => {
beforeEach(() => {
mockDashboards = notStarredDashboards;
wrapper = createComponent();
});
it('displays an item for each dashboard', () => {
expect(findItems().length).toEqual(notStarredDashboards.length);
});
it('displays no star icon', () => {
const star = findItemAt(0).find(GlIcon);
expect(star.exists()).toBe(false);
});
it('displays no separator between starred and not starred dashboards', () => {
expect(findStarredListDivider().exists()).toBe(false);
});
});
describe('when a system dashboard is selected', () => { describe('when a system dashboard is selected', () => {
let duplicateDashboardAction; let duplicateDashboardAction;
let modalDirective; let modalDirective;
...@@ -260,7 +327,7 @@ describe('DashboardsDropdown', () => { ...@@ -260,7 +327,7 @@ describe('DashboardsDropdown', () => {
expect(wrapper.emitted().selectDashboard).toBeTruthy(); expect(wrapper.emitted().selectDashboard).toBeTruthy();
}); });
it('emits a "selectDashboard" event with dashboard information', () => { it('emits a "selectDashboard" event with dashboard information', () => {
expect(wrapper.emitted().selectDashboard[0]).toEqual([dashboardGitResponse[1]]); expect(wrapper.emitted().selectDashboard[0]).toEqual([dashboardGitResponse[0]]);
}); });
}); });
}); });
...@@ -34,6 +34,7 @@ const customDashboardsData = new Array(30).fill(null).map((_, idx) => ({ ...@@ -34,6 +34,7 @@ const customDashboardsData = new Array(30).fill(null).map((_, idx) => ({
system_dashboard: false, system_dashboard: false,
project_blob_path: `${mockProjectDir}/blob/master/dashboards/.gitlab/dashboards/dashboard_${idx}.yml`, project_blob_path: `${mockProjectDir}/blob/master/dashboards/.gitlab/dashboards/dashboard_${idx}.yml`,
path: `.gitlab/dashboards/dashboard_${idx}.yml`, path: `.gitlab/dashboards/dashboard_${idx}.yml`,
starred: false,
})); }));
export const mockDashboardsErrorResponse = { export const mockDashboardsErrorResponse = {
...@@ -323,6 +324,16 @@ export const dashboardGitResponse = [ ...@@ -323,6 +324,16 @@ export const dashboardGitResponse = [
system_dashboard: true, system_dashboard: true,
project_blob_path: null, project_blob_path: null,
path: 'config/prometheus/common_metrics.yml', path: 'config/prometheus/common_metrics.yml',
starred: false,
},
{
default: false,
display_name: 'dashboard.yml',
can_edit: true,
system_dashboard: false,
project_blob_path: `${mockProjectDir}/-/blob/master/.gitlab/dashboards/dashboard.yml`,
path: '.gitlab/dashboards/dashboard.yml',
starred: true,
}, },
...customDashboardsData, ...customDashboardsData,
]; ];
......
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