Commit 356730c6 authored by Peter Hegman's avatar Peter Hegman

Merge branch '337666-devops-sorting-for-table-on-devops-adoption-overview-tab' into 'master'

[DevOps] Sorting for table on DevOps adoption Overview tab

See merge request gitlab-org/gitlab!70937
parents 2b719bdf 87145f1c
......@@ -9,6 +9,7 @@ import {
GlProgressBar,
} from '@gitlab/ui';
import { uniqueId } from 'lodash';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { formatNumber } from '~/locale';
import {
TABLE_TEST_IDS_HEADERS,
......@@ -20,14 +21,28 @@ import {
TABLE_TEST_IDS_ACTIONS,
TABLE_TEST_IDS_NAMESPACE,
DEVOPS_ADOPTION_TABLE_CONFIGURATION,
OVERVIEW_TABLE_SORT_BY_STORAGE_KEY,
OVERVIEW_TABLE_SORT_DESC_STORAGE_KEY,
OVERVIEW_TABLE_NAME_KEY,
} from '../constants';
import DevopsAdoptionDeleteModal from './devops_adoption_delete_modal.vue';
const thClass = ['gl-bg-white!', 'gl-text-gray-400'];
const formatter = (value, key, item) => {
if (key === OVERVIEW_TABLE_NAME_KEY) {
return item.group?.namespace?.fullName;
} else if (item.adoption[key]) return item.adoption[key].adopted;
return 0;
};
const fieldOptions = {
thClass,
thAttr: { 'data-testid': TABLE_TEST_IDS_HEADERS },
sortable: true,
sortByFormatted: true,
formatter,
};
export default {
......@@ -39,6 +54,7 @@ export default {
GlBadge,
GlProgressBar,
DevopsAdoptionDeleteModal,
LocalStorageSync,
},
directives: {
GlTooltip: GlTooltipDirective,
......@@ -54,6 +70,8 @@ export default {
NAMESPACE: TABLE_TEST_IDS_NAMESPACE,
},
cols: DEVOPS_ADOPTION_TABLE_CONFIGURATION,
sortByStorageKey: OVERVIEW_TABLE_SORT_BY_STORAGE_KEY,
sortDescStorageKey: OVERVIEW_TABLE_SORT_DESC_STORAGE_KEY,
props: {
data: {
type: Object,
......@@ -63,6 +81,8 @@ export default {
},
data() {
return {
sortBy: OVERVIEW_TABLE_NAME_KEY,
sortDesc: false,
selectedNamespace: null,
deleteModalId: uniqueId('delete-modal-'),
};
......@@ -74,7 +94,7 @@ export default {
tableHeaderFields() {
return [
{
key: 'name',
key: OVERVIEW_TABLE_NAME_KEY,
label: I18N_GROUP_COL_LABEL,
...fieldOptions,
thClass: ['gl-w-grid-size-30', ...thClass],
......@@ -90,6 +110,7 @@ export default {
key: 'actions',
tdClass: 'actions-cell',
...fieldOptions,
sortable: false,
},
];
},
......@@ -137,12 +158,16 @@ export default {
</script>
<template>
<div>
<local-storage-sync v-model="sortBy" :storage-key="$options.sortByStorageKey" as-json />
<local-storage-sync v-model="sortDesc" :storage-key="$options.sortDescStorageKey" as-json />
<h4>{{ tableHeader }}</h4>
<gl-table
:fields="tableHeaderFields"
:items="formattedData"
thead-class="gl-border-t-0 gl-border-b-solid gl-border-b-1 gl-border-b-gray-100"
stacked="md"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
>
<template v-for="header in tableHeaderFields" #[headerSlotName(header.key)]>
{{ header.label }}
......
......@@ -6,6 +6,7 @@ export const DEBOUNCE_DELAY = 500;
export const PROGRESS_BAR_HEIGHT = '8px';
export const DATE_TIME_FORMAT = 'yyyy-mm-dd HH:MM';
export const OVERVIEW_TABLE_NAME_KEY = 'name';
export const TABLE_TEST_IDS_HEADERS = 'header';
export const TABLE_TEST_IDS_NAMESPACE = 'namespaceCol';
export const TABLE_TEST_IDS_ACTIONS = 'actionsCol';
......@@ -15,6 +16,9 @@ export const TABLE_TEST_IDS_LOCAL_STORAGE_SORT_DESC = 'localStorageSortDesc';
export const TABLE_SORT_BY_STORAGE_KEY = 'devops_adoption_table_sort_by';
export const TABLE_SORT_DESC_STORAGE_KEY = 'devops_adoption_table_sort_desc';
export const OVERVIEW_TABLE_SORT_BY_STORAGE_KEY = 'devops_adoption_overview_table_sort_by';
export const OVERVIEW_TABLE_SORT_DESC_STORAGE_KEY = 'devops_adoption_overview_table_sort_desc';
export const TRACK_ADOPTION_TAB_CLICK_EVENT = 'i_analytics_dev_ops_adoption';
export const TRACK_DEVOPS_SCORE_TAB_CLICK_EVENT = 'i_analytics_dev_ops_score';
......
......@@ -9,6 +9,7 @@ import {
} from 'ee/analytics/devops_report/devops_adoption/constants';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { devopsAdoptionNamespaceData } from '../mock_data';
const DELETE_MODAL_ID = 'delete-modal-test-unique-id';
......@@ -33,6 +34,10 @@ describe('DevopsAdoptionOverviewTable', () => {
});
};
beforeEach(() => {
localStorage.clear();
});
afterEach(() => {
wrapper.destroy();
});
......@@ -46,6 +51,9 @@ describe('DevopsAdoptionOverviewTable', () => {
const findDeleteModal = () => wrapper.findComponent(DevopsAdoptionDeleteModal);
const findSortByLocalStorageSync = () => wrapper.findAll(LocalStorageSync).at(0);
const findSortDescLocalStorageSync = () => wrapper.findAll(LocalStorageSync).at(1);
describe('table headings', () => {
beforeEach(() => {
createComponent();
......@@ -54,9 +62,11 @@ describe('DevopsAdoptionOverviewTable', () => {
it('displays the table headings', () => {
const headerTexts = wrapper
.findAllByTestId(TABLE_TEST_IDS_HEADERS)
.wrappers.map((x) => x.text());
.wrappers.map((x) => x.text().split('\n')[0]);
expect(headerTexts).toEqual(['Group', 'Dev', 'Sec', 'Ops', '']);
headerTexts.pop(); // Remove the blank entry at the end used for the actions
expect(headerTexts).toEqual(['Group', 'Dev', 'Sec', 'Ops']);
});
});
......@@ -183,4 +193,45 @@ describe('DevopsAdoptionOverviewTable', () => {
},
);
});
describe('sorting', () => {
let headers;
beforeEach(() => {
createComponent();
headers = wrapper.findAllByTestId(TABLE_TEST_IDS_HEADERS);
});
it.each`
column | index
${'name'} | ${0}
${'dev'} | ${1}
`('sorts correctly $column column', async ({ index }) => {
expect(findCol(TABLE_TEST_IDS_NAMESPACE).text()).toBe(
devopsAdoptionNamespaceData.nodes[0].namespace.fullName,
);
await headers.at(index).trigger('click');
expect(findCol(TABLE_TEST_IDS_NAMESPACE).text()).toBe(
devopsAdoptionNamespaceData.nodes[1].namespace.fullName,
);
});
it('should update local storage when the sort column changes', async () => {
expect(findSortByLocalStorageSync().props('value')).toBe('name');
await headers.at(1).trigger('click');
expect(findSortByLocalStorageSync().props('value')).toBe('dev');
});
it('should update local storage when the sort direction changes', async () => {
expect(findSortDescLocalStorageSync().props('value')).toBe(false);
await headers.at(0).trigger('click');
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