Commit 2f84e94d authored by Martin Wortschack's avatar Martin Wortschack

Merge branch '326480-devops-adoption-add-devsecops-tabs-to-table-2' into 'master'

Resolve "[DevOps Adoption] Migrate table to a configuration object"

See merge request gitlab-org/gitlab!61148
parents 1590ddb7 938d55d2
...@@ -18,6 +18,7 @@ import { ...@@ -18,6 +18,7 @@ import {
DEVOPS_ADOPTION_SEGMENT_MODAL_ID, DEVOPS_ADOPTION_SEGMENT_MODAL_ID,
DEFAULT_POLLING_INTERVAL, DEFAULT_POLLING_INTERVAL,
DEVOPS_ADOPTION_GROUP_LEVEL_LABEL, DEVOPS_ADOPTION_GROUP_LEVEL_LABEL,
DEVOPS_ADOPTION_TABLE_CONFIGURATION,
} from '../constants'; } from '../constants';
import bulkFindOrCreateDevopsAdoptionSegmentsMutation from '../graphql/mutations/bulk_find_or_create_devops_adoption_segments.mutation.graphql'; import bulkFindOrCreateDevopsAdoptionSegmentsMutation from '../graphql/mutations/bulk_find_or_create_devops_adoption_segments.mutation.graphql';
import devopsAdoptionSegmentsQuery from '../graphql/queries/devops_adoption_segments.query.graphql'; import devopsAdoptionSegmentsQuery from '../graphql/queries/devops_adoption_segments.query.graphql';
...@@ -57,6 +58,7 @@ export default { ...@@ -57,6 +58,7 @@ export default {
}, },
maxSegments: MAX_SEGMENTS, maxSegments: MAX_SEGMENTS,
devopsSegmentModalId: DEVOPS_ADOPTION_SEGMENT_MODAL_ID, devopsSegmentModalId: DEVOPS_ADOPTION_SEGMENT_MODAL_ID,
devopsAdoptionTableConfiguration: DEVOPS_ADOPTION_TABLE_CONFIGURATION,
data() { data() {
return { return {
isLoadingGroups: false, isLoadingGroups: false,
...@@ -288,6 +290,7 @@ export default { ...@@ -288,6 +290,7 @@ export default {
> >
</div> </div>
<devops-adoption-table <devops-adoption-table
:cols="$options.devopsAdoptionTableConfiguration[0].cols"
:segments="devopsAdoptionSegments.nodes" :segments="devopsAdoptionSegments.nodes"
:selected-segment="selectedSegment" :selected-segment="selectedSegment"
@set-selected-segment="setSelectedSegment" @set-selected-segment="setSelectedSegment"
......
...@@ -16,6 +16,7 @@ import { ...@@ -16,6 +16,7 @@ import {
DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_BY_STORAGE_KEY, DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_BY_STORAGE_KEY,
DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_DESC_STORAGE_KEY, DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_DESC_STORAGE_KEY,
DEVOPS_ADOPTION_TABLE_REMOVE_BUTTON_DISABLED, DEVOPS_ADOPTION_TABLE_REMOVE_BUTTON_DISABLED,
DEVOPS_ADOPTION_GROUP_COL_LABEL,
} from '../constants'; } from '../constants';
import DevopsAdoptionDeleteModal from './devops_adoption_delete_modal.vue'; import DevopsAdoptionDeleteModal from './devops_adoption_delete_modal.vue';
import DevopsAdoptionTableCellFlag from './devops_adoption_table_cell_flag.vue'; import DevopsAdoptionTableCellFlag from './devops_adoption_table_cell_flag.vue';
...@@ -46,17 +47,6 @@ const fieldOptions = { ...@@ -46,17 +47,6 @@ const fieldOptions = {
const { table: i18n } = DEVOPS_ADOPTION_STRINGS; const { table: i18n } = DEVOPS_ADOPTION_STRINGS;
const headers = [
NAME_HEADER,
'issueOpened',
'mergeRequestOpened',
'mergeRequestApproved',
'runnerConfigured',
'pipelineSucceeded',
'deploySucceeded',
'securityScanSucceeded',
].map((key) => ({ key, ...i18n.headers[key], ...fieldOptions }));
export default { export default {
name: 'DevopsAdoptionTable', name: 'DevopsAdoptionTable',
components: { components: {
...@@ -83,15 +73,6 @@ export default { ...@@ -83,15 +73,6 @@ export default {
}, },
devopsSegmentModalId: DEVOPS_ADOPTION_SEGMENT_MODAL_ID, devopsSegmentModalId: DEVOPS_ADOPTION_SEGMENT_MODAL_ID,
devopsSegmentDeleteModalId: DEVOPS_ADOPTION_SEGMENT_DELETE_MODAL_ID, devopsSegmentDeleteModalId: DEVOPS_ADOPTION_SEGMENT_DELETE_MODAL_ID,
tableHeaderFields: [
...headers,
{
key: 'actions',
tdClass: 'actions-cell',
...fieldOptions,
sortable: false,
},
],
testids: DEVOPS_ADOPTION_TABLE_TEST_IDS, testids: DEVOPS_ADOPTION_TABLE_TEST_IDS,
sortByStorageKey: DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_BY_STORAGE_KEY, sortByStorageKey: DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_BY_STORAGE_KEY,
sortDescStorageKey: DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_DESC_STORAGE_KEY, sortDescStorageKey: DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_DESC_STORAGE_KEY,
...@@ -105,6 +86,10 @@ export default { ...@@ -105,6 +86,10 @@ export default {
required: false, required: false,
default: null, default: null,
}, },
cols: {
type: Array,
required: true,
},
}, },
data() { data() {
return { return {
...@@ -112,13 +97,37 @@ export default { ...@@ -112,13 +97,37 @@ export default {
sortDesc: false, sortDesc: false,
}; };
}, },
computed: {
tableHeaderFields() {
return [
{
key: 'name',
label: DEVOPS_ADOPTION_GROUP_COL_LABEL,
...fieldOptions,
},
...this.cols.map((item) => ({
...item,
...fieldOptions,
})),
{
key: 'actions',
tdClass: 'actions-cell',
...fieldOptions,
sortable: false,
},
];
},
},
methods: { methods: {
setSelectedSegment(segment) { setSelectedSegment(segment) {
this.$emit('set-selected-segment', segment); this.$emit('set-selected-segment', segment);
}, },
slotName(key) { headerSlotName(key) {
return `head(${key})`; return `head(${key})`;
}, },
cellSlotName(key) {
return `cell(${key})`;
},
isCurrentGroup(item) { isCurrentGroup(item) {
return item.namespace?.id === this.groupGid; return item.namespace?.id === this.groupGid;
}, },
...@@ -145,14 +154,14 @@ export default { ...@@ -145,14 +154,14 @@ export default {
as-json as-json
/> />
<gl-table <gl-table
:fields="$options.tableHeaderFields" :fields="tableHeaderFields"
:items="segments" :items="segments"
:sort-by.sync="sortBy" :sort-by.sync="sortBy"
:sort-desc.sync="sortDesc" :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"
> >
<template v-for="header in $options.tableHeaderFields" #[slotName(header.key)]> <template v-for="header in tableHeaderFields" #[headerSlotName(header.key)]>
<div :key="header.key" class="gl-display-flex gl-align-items-center"> <div :key="header.key" class="gl-display-flex gl-align-items-center">
<span>{{ header.label }}</span> <span>{{ header.label }}</span>
<gl-icon <gl-icon
...@@ -178,59 +187,12 @@ export default { ...@@ -178,59 +187,12 @@ export default {
</div> </div>
</template> </template>
<template #cell(issueOpened)="{ item }"> <template v-for="col in cols" #[cellSlotName(col.key)]="{ item }">
<devops-adoption-table-cell-flag
v-if="item.latestSnapshot"
:data-testid="$options.testids.ISSUES"
:enabled="item.latestSnapshot.issueOpened"
/>
</template>
<template #cell(mergeRequestOpened)="{ item }">
<devops-adoption-table-cell-flag
v-if="item.latestSnapshot"
:data-testid="$options.testids.MRS"
:enabled="item.latestSnapshot.mergeRequestOpened"
/>
</template>
<template #cell(mergeRequestApproved)="{ item }">
<devops-adoption-table-cell-flag
v-if="item.latestSnapshot"
:data-testid="$options.testids.APPROVALS"
:enabled="item.latestSnapshot.mergeRequestApproved"
/>
</template>
<template #cell(runnerConfigured)="{ item }">
<devops-adoption-table-cell-flag
v-if="item.latestSnapshot"
:data-testid="$options.testids.RUNNERS"
:enabled="item.latestSnapshot.runnerConfigured"
/>
</template>
<template #cell(pipelineSucceeded)="{ item }">
<devops-adoption-table-cell-flag
v-if="item.latestSnapshot"
:data-testid="$options.testids.PIPELINES"
:enabled="item.latestSnapshot.pipelineSucceeded"
/>
</template>
<template #cell(deploySucceeded)="{ item }">
<devops-adoption-table-cell-flag
v-if="item.latestSnapshot"
:data-testid="$options.testids.DEPLOYS"
:enabled="item.latestSnapshot.deploySucceeded"
/>
</template>
<template #cell(securityScanSucceeded)="{ item }">
<devops-adoption-table-cell-flag <devops-adoption-table-cell-flag
v-if="item.latestSnapshot" v-if="item.latestSnapshot"
:data-testid="$options.testids.SCANNING" :key="col.key"
:enabled="item.latestSnapshot.securityScanSucceeded" :data-testid="col.testId"
:enabled="item.latestSnapshot[col.key]"
/> />
</template> </template>
......
...@@ -63,39 +63,6 @@ export const DEVOPS_ADOPTION_STRINGS = { ...@@ -63,39 +63,6 @@ export const DEVOPS_ADOPTION_STRINGS = {
}, },
table: { table: {
removeButton: s__('DevopsAdoption|Remove Group from the table.'), removeButton: s__('DevopsAdoption|Remove Group from the table.'),
headers: {
name: {
label: __('Group'),
},
issueOpened: {
label: s__('DevopsAdoption|Issues'),
tooltip: s__('DevopsAdoption|At least 1 issue opened'),
},
mergeRequestOpened: {
label: s__('DevopsAdoption|MRs'),
tooltip: s__('DevopsAdoption|At least 1 MR opened'),
},
mergeRequestApproved: {
label: s__('DevopsAdoption|Approvals'),
tooltip: s__('DevopsAdoption|At least 1 approval on an MR'),
},
runnerConfigured: {
label: s__('DevopsAdoption|Runners'),
tooltip: s__('DevopsAdoption|Runner configured for project/group'),
},
pipelineSucceeded: {
label: s__('DevopsAdoption|Pipelines'),
tooltip: s__('DevopsAdoption|At least 1 pipeline successfully run'),
},
deploySucceeded: {
label: s__('DevopsAdoption|Deploys'),
tooltip: s__('DevopsAdoption|At least 1 deploy'),
},
securityScanSucceeded: {
label: s__('DevopsAdoption|Scanning'),
tooltip: s__('DevopsAdoption|At least 1 security scan of any type run in pipeline'),
},
},
}, },
deleteModal: { deleteModal: {
title: s__('DevopsAdoption|Confirm remove Group'), title: s__('DevopsAdoption|Confirm remove Group'),
...@@ -115,14 +82,7 @@ export const DEVOPS_ADOPTION_STRINGS = { ...@@ -115,14 +82,7 @@ export const DEVOPS_ADOPTION_STRINGS = {
export const DEVOPS_ADOPTION_TABLE_TEST_IDS = { export const DEVOPS_ADOPTION_TABLE_TEST_IDS = {
TABLE_HEADERS: 'header', TABLE_HEADERS: 'header',
SEGMENT: 'segmentCol', SEGMENT: 'segmentCol',
ISSUES: 'issuesCol',
MRS: 'mrsCol',
APPROVALS: 'approvalsCol',
RUNNERS: 'runnersCol',
PIPELINES: 'pipelinesCol',
DEPLOYS: 'deploysCol',
ACTIONS: 'actionsCol', ACTIONS: 'actionsCol',
SCANNING: 'scanningCol',
LOCAL_STORAGE_SORT_BY: 'localStorageSortBy', LOCAL_STORAGE_SORT_BY: 'localStorageSortBy',
LOCAL_STORAGE_SORT_DESC: 'localStorageSortDesc', LOCAL_STORAGE_SORT_DESC: 'localStorageSortDesc',
}; };
...@@ -132,3 +92,55 @@ export const DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_BY_STORAGE_KEY = ...@@ -132,3 +92,55 @@ export const DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_BY_STORAGE_KEY =
export const DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_DESC_STORAGE_KEY = export const DEVOPS_ADOPTION_SEGMENTS_TABLE_SORT_DESC_STORAGE_KEY =
'devops_adoption_segments_table_sort_desc'; 'devops_adoption_segments_table_sort_desc';
export const DEVOPS_ADOPTION_GROUP_COL_LABEL = __('Group');
export const DEVOPS_ADOPTION_TABLE_CONFIGURATION = [
{
title: s__('DevopsAdoption|Adoption'),
cols: [
{
key: 'issueOpened',
label: s__('DevopsAdoption|Issues'),
tooltip: s__('DevopsAdoption|At least 1 issue opened'),
testId: 'issuesCol',
},
{
key: 'mergeRequestOpened',
label: s__('DevopsAdoption|MRs'),
tooltip: s__('DevopsAdoption|At least 1 MR opened'),
testId: 'mrsCol',
},
{
key: 'mergeRequestApproved',
label: s__('DevopsAdoption|Approvals'),
tooltip: s__('DevopsAdoption|At least 1 approval on an MR'),
testId: 'approvalsCol',
},
{
key: 'runnerConfigured',
label: s__('DevopsAdoption|Runners'),
tooltip: s__('DevopsAdoption|Runner configured for project/group'),
testId: 'runnersCol',
},
{
key: 'pipelineSucceeded',
label: s__('DevopsAdoption|Pipelines'),
tooltip: s__('DevopsAdoption|At least 1 pipeline successfully run'),
testId: 'pipelinesCol',
},
{
key: 'deploySucceeded',
label: s__('DevopsAdoption|Deploys'),
tooltip: s__('DevopsAdoption|At least 1 deploy'),
testId: 'deploysCol',
},
{
key: 'securityScanSucceeded',
label: s__('DevopsAdoption|Scanning'),
tooltip: s__('DevopsAdoption|At least 1 security scan of any type run in pipeline'),
testId: 'scanningCol',
},
],
},
];
...@@ -4,7 +4,10 @@ import { nextTick } from 'vue'; ...@@ -4,7 +4,10 @@ import { nextTick } from 'vue';
import DevopsAdoptionDeleteModal from 'ee/analytics/devops_report/devops_adoption/components/devops_adoption_delete_modal.vue'; import DevopsAdoptionDeleteModal from 'ee/analytics/devops_report/devops_adoption/components/devops_adoption_delete_modal.vue';
import DevopsAdoptionTable from 'ee/analytics/devops_report/devops_adoption/components/devops_adoption_table.vue'; import DevopsAdoptionTable from 'ee/analytics/devops_report/devops_adoption/components/devops_adoption_table.vue';
import DevopsAdoptionTableCellFlag from 'ee/analytics/devops_report/devops_adoption/components/devops_adoption_table_cell_flag.vue'; import DevopsAdoptionTableCellFlag from 'ee/analytics/devops_report/devops_adoption/components/devops_adoption_table_cell_flag.vue';
import { DEVOPS_ADOPTION_TABLE_TEST_IDS as TEST_IDS } from 'ee/analytics/devops_report/devops_adoption/constants'; import {
DEVOPS_ADOPTION_TABLE_TEST_IDS as TEST_IDS,
DEVOPS_ADOPTION_TABLE_CONFIGURATION,
} from 'ee/analytics/devops_report/devops_adoption/constants';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { devopsAdoptionSegmentsData, devopsAdoptionTableHeaders } from '../mock_data'; import { devopsAdoptionSegmentsData, devopsAdoptionTableHeaders } from '../mock_data';
...@@ -17,6 +20,7 @@ describe('DevopsAdoptionTable', () => { ...@@ -17,6 +20,7 @@ describe('DevopsAdoptionTable', () => {
wrapper = mount(DevopsAdoptionTable, { wrapper = mount(DevopsAdoptionTable, {
propsData: { propsData: {
cols: DEVOPS_ADOPTION_TABLE_CONFIGURATION[0].cols,
segments: devopsAdoptionSegmentsData.nodes, segments: devopsAdoptionSegmentsData.nodes,
selectedSegment: devopsAdoptionSegmentsData.nodes[0], selectedSegment: devopsAdoptionSegmentsData.nodes[0],
}, },
...@@ -152,21 +156,14 @@ describe('DevopsAdoptionTable', () => { ...@@ -152,21 +156,14 @@ describe('DevopsAdoptionTable', () => {
}); });
}); });
it.each` const testCols = DEVOPS_ADOPTION_TABLE_CONFIGURATION[0].cols.map((col) => [col.label, col]);
id | field | flag
${TEST_IDS.ISSUES} | ${'issues'} | ${true} it.each(testCols)('displays the cell flag component for %s', (label, { testId }) => {
${TEST_IDS.MRS} | ${'MRs'} | ${true}
${TEST_IDS.APPROVALS} | ${'approvals'} | ${false}
${TEST_IDS.RUNNERS} | ${'runners'} | ${true}
${TEST_IDS.PIPELINES} | ${'pipelines'} | ${false}
${TEST_IDS.DEPLOYS} | ${'deploys'} | ${false}
${TEST_IDS.SCANNING} | ${'scanning'} | ${false}
`('displays the correct $field snapshot value', ({ id, flag }) => {
createComponent(); createComponent();
const booleanFlag = findColSubComponent(id, DevopsAdoptionTableCellFlag); const booleanFlag = findColSubComponent(testId, DevopsAdoptionTableCellFlag);
expect(booleanFlag.props('enabled')).toBe(flag); expect(booleanFlag.exists()).toBe(true);
}); });
describe.each` describe.each`
......
...@@ -11265,6 +11265,9 @@ msgstr "" ...@@ -11265,6 +11265,9 @@ msgstr ""
msgid "DevopsAdoption|Adopted" msgid "DevopsAdoption|Adopted"
msgstr "" msgstr ""
msgid "DevopsAdoption|Adoption"
msgstr ""
msgid "DevopsAdoption|An error occurred while removing the group. Please try again." msgid "DevopsAdoption|An error occurred while removing the group. Please try again."
msgstr "" msgstr ""
......
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