Commit 96a64f64 authored by Brandon Labuschagne's avatar Brandon Labuschagne

Merge branch '291136-sort-columns-in-devops-adoption-table' into 'master'

Add sorting by column in DevOps Adoption table

See merge request gitlab-org/gitlab!50743
parents d4389de7 58e7dce6
...@@ -7,6 +7,7 @@ import { ...@@ -7,6 +7,7 @@ import {
GlTooltipDirective, GlTooltipDirective,
GlIcon, GlIcon,
} from '@gitlab/ui'; } from '@gitlab/ui';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import DevopsAdoptionTableCellFlag from './devops_adoption_table_cell_flag.vue'; import DevopsAdoptionTableCellFlag from './devops_adoption_table_cell_flag.vue';
import DevopsAdoptionDeleteModal from './devops_adoption_delete_modal.vue'; import DevopsAdoptionDeleteModal from './devops_adoption_delete_modal.vue';
import { import {
...@@ -14,17 +15,38 @@ import { ...@@ -14,17 +15,38 @@ import {
DEVOPS_ADOPTION_STRINGS, DEVOPS_ADOPTION_STRINGS,
DEVOPS_ADOPTION_SEGMENT_MODAL_ID, DEVOPS_ADOPTION_SEGMENT_MODAL_ID,
DEVOPS_ADOPTION_SEGMENT_DELETE_MODAL_ID, DEVOPS_ADOPTION_SEGMENT_DELETE_MODAL_ID,
DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_BY_STORAGE_KEY,
DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_DESC_STORAGE_KEY,
} from '../constants'; } from '../constants';
const NAME_HEADER = 'name';
const formatter = (value, key, item) => {
if (key === NAME_HEADER) {
return value;
}
if (item.latestSnapshot && item.latestSnapshot[key] === false) {
return 1;
} else if (item.latestSnapshot && item.latestSnapshot[key]) {
return 2;
}
return 0;
};
const fieldOptions = { const fieldOptions = {
thClass: 'gl-bg-white! gl-text-gray-400', thClass: 'gl-bg-white! gl-text-gray-400',
thAttr: { 'data-testid': DEVOPS_ADOPTION_TABLE_TEST_IDS.TABLE_HEADERS }, thAttr: { 'data-testid': DEVOPS_ADOPTION_TABLE_TEST_IDS.TABLE_HEADERS },
formatter,
sortable: true,
sortByFormatted: true,
}; };
const { table: i18n } = DEVOPS_ADOPTION_STRINGS; const { table: i18n } = DEVOPS_ADOPTION_STRINGS;
const headers = [ const headers = [
'name', NAME_HEADER,
'issueOpened', 'issueOpened',
'mergeRequestOpened', 'mergeRequestOpened',
'mergeRequestApproved', 'mergeRequestApproved',
...@@ -41,6 +63,7 @@ export default { ...@@ -41,6 +63,7 @@ export default {
DevopsAdoptionTableCellFlag, DevopsAdoptionTableCellFlag,
GlButton, GlButton,
GlPopover, GlPopover,
LocalStorageSync,
DevopsAdoptionDeleteModal, DevopsAdoptionDeleteModal,
GlIcon, GlIcon,
}, },
...@@ -57,9 +80,12 @@ export default { ...@@ -57,9 +80,12 @@ export default {
key: 'actions', key: 'actions',
tdClass: 'actions-cell', tdClass: 'actions-cell',
...fieldOptions, ...fieldOptions,
sortable: false,
}, },
], ],
testids: DEVOPS_ADOPTION_TABLE_TEST_IDS, testids: DEVOPS_ADOPTION_TABLE_TEST_IDS,
sortByStorageKey: DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_BY_STORAGE_KEY,
sortDescStorageKey: DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_DESC_STORAGE_KEY,
props: { props: {
segments: { segments: {
type: Array, type: Array,
...@@ -71,6 +97,12 @@ export default { ...@@ -71,6 +97,12 @@ export default {
default: null, default: null,
}, },
}, },
data() {
return {
sortBy: NAME_HEADER,
sortDesc: false,
};
},
methods: { methods: {
popoverContainerId(name) { popoverContainerId(name) {
return `popover_container_id_for_${name}`; return `popover_container_id_for_${name}`;
...@@ -89,9 +121,23 @@ export default { ...@@ -89,9 +121,23 @@ export default {
</script> </script>
<template> <template>
<div> <div>
<local-storage-sync
v-model="sortBy"
:storage-key="$options.sortByStorageKey"
:data-testid="$options.testids.LOCAL_STORAGE_SORT_BY"
as-json
/>
<local-storage-sync
v-model="sortDesc"
:storage-key="$options.sortDescStorageKey"
:data-testid="$options.testids.LOCAL_STORAGE_SORT_DESC"
as-json
/>
<gl-table <gl-table
:fields="$options.tableHeaderFields" :fields="$options.tableHeaderFields"
:items="segments" :items="segments"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
thead-class="gl-border-t-0 gl-border-b-solid gl-border-b-1 gl-border-b-gray-100" thead-class="gl-border-t-0 gl-border-b-solid gl-border-b-1 gl-border-b-gray-100"
stacked="sm" stacked="sm"
> >
......
...@@ -116,4 +116,12 @@ export const DEVOPS_ADOPTION_TABLE_TEST_IDS = { ...@@ -116,4 +116,12 @@ export const DEVOPS_ADOPTION_TABLE_TEST_IDS = {
DEPLOYS: 'deploysCol', DEPLOYS: 'deploysCol',
ACTIONS: 'actionsCol', ACTIONS: 'actionsCol',
SCANNING: 'scanningCol', SCANNING: 'scanningCol',
LOCAL_STORAGE_SORT_BY: 'localStorageSortBy',
LOCAL_STORAGE_SORT_DESC: 'localStorageSortDesc',
}; };
export const DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_BY_STORAGE_KEY =
'devops_adoption_segments_table_sort_by';
export const DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_DESC_STORAGE_KEY =
'devops_adoption_segments_table_sort_desc';
---
title: Add sorting by column in DevOps Adoption table
merge_request: 50743
author:
type: added
import { GlTable, GlButton, GlIcon } from '@gitlab/ui'; import { GlTable, GlButton, GlIcon } from '@gitlab/ui';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import DevopsAdoptionTable from 'ee/admin/dev_ops_report/components/devops_adoption_table.vue'; import DevopsAdoptionTable from 'ee/admin/dev_ops_report/components/devops_adoption_table.vue';
import DevopsAdoptionTableCellFlag from 'ee/admin/dev_ops_report/components/devops_adoption_table_cell_flag.vue'; import DevopsAdoptionTableCellFlag from 'ee/admin/dev_ops_report/components/devops_adoption_table_cell_flag.vue';
import { DEVOPS_ADOPTION_TABLE_TEST_IDS as TEST_IDS } from 'ee/admin/dev_ops_report/constants'; import { DEVOPS_ADOPTION_TABLE_TEST_IDS as TEST_IDS } from 'ee/admin/dev_ops_report/constants';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { devopsAdoptionSegmentsData, devopsAdoptionTableHeaders } from '../mock_data'; import { devopsAdoptionSegmentsData, devopsAdoptionTableHeaders } from '../mock_data';
describe('DevopsAdoptionTable', () => { describe('DevopsAdoptionTable', () => {
...@@ -21,6 +23,7 @@ describe('DevopsAdoptionTable', () => { ...@@ -21,6 +23,7 @@ describe('DevopsAdoptionTable', () => {
}; };
beforeEach(() => { beforeEach(() => {
localStorage.clear();
createComponent(); createComponent();
}); });
...@@ -39,6 +42,9 @@ describe('DevopsAdoptionTable', () => { ...@@ -39,6 +42,9 @@ describe('DevopsAdoptionTable', () => {
const findColSubComponent = (colTestId, childComponent) => const findColSubComponent = (colTestId, childComponent) =>
findCol(colTestId).find(childComponent); findCol(colTestId).find(childComponent);
const findSortByLocalStorageSync = () => wrapper.findAll(LocalStorageSync).at(0);
const findSortDescLocalStorageSync = () => wrapper.findAll(LocalStorageSync).at(1);
describe('table headings', () => { describe('table headings', () => {
let headers; let headers;
...@@ -60,7 +66,7 @@ describe('DevopsAdoptionTable', () => { ...@@ -60,7 +66,7 @@ describe('DevopsAdoptionTable', () => {
}); });
it(`displays the correct table heading text for "${label}"`, () => { it(`displays the correct table heading text for "${label}"`, () => {
expect(headerWrapper.text()).toBe(label); expect(headerWrapper.text()).toContain(label);
}); });
describe(`helper information for "${label}"`, () => { describe(`helper information for "${label}"`, () => {
...@@ -142,4 +148,42 @@ describe('DevopsAdoptionTable', () => { ...@@ -142,4 +148,42 @@ describe('DevopsAdoptionTable', () => {
expect(button.props('category')).toBe('tertiary'); expect(button.props('category')).toBe('tertiary');
}); });
}); });
describe('sorting', () => {
let headers;
beforeEach(() => {
headers = findTable().findAll(`[data-testid="${TEST_IDS.TABLE_HEADERS}"]`);
});
it('sorts the segments by name', async () => {
expect(findCol(TEST_IDS.SEGMENT).text()).toBe('Segment 1');
headers.at(0).trigger('click');
await nextTick();
expect(findCol(TEST_IDS.SEGMENT).text()).toBe('Segment 2');
});
it('should update local storage when the sort column changes', async () => {
expect(findSortByLocalStorageSync().props('value')).toBe('name');
headers.at(1).trigger('click');
await nextTick();
expect(findSortByLocalStorageSync().props('value')).toBe('issueOpened');
});
it('should update local storage when the sort direction changes', async () => {
expect(findSortDescLocalStorageSync().props('value')).toBe(false);
headers.at(0).trigger('click');
await nextTick();
expect(findSortDescLocalStorageSync().props('value')).toBe(true);
});
});
}); });
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