Commit de6da188 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Merge branch 'add-devops-adoption-table-component' into 'master'

Add devops adoption table component

See merge request gitlab-org/gitlab!47491
parents 19ee559f eeb6dee2
<script>
import { GlTable, GlButton } from '@gitlab/ui';
import { s__ } from '~/locale';
import DevopsAdoptionTableCellFlag from './devops_adoption_table_cell_flag.vue';
import { DEVOPS_ADOPTION_TABLE_TEST_IDS } from '../constants';
const fieldOptions = {
thClass: 'gl-bg-white! gl-text-gray-400',
thAttr: { 'data-testid': DEVOPS_ADOPTION_TABLE_TEST_IDS.TABLE_HEADERS },
};
export default {
name: 'DevopsAdoptionTable',
components: { GlTable, DevopsAdoptionTableCellFlag, GlButton },
tableHeaderFields: [
{
key: 'name',
label: s__('DevopsAdoption|Segment'),
...fieldOptions,
},
{
key: 'issueOpened',
label: s__('DevopsAdoption|Issues'),
...fieldOptions,
},
{
key: 'mergeRequestOpened',
label: s__('DevopsAdoption|MRs'),
...fieldOptions,
},
{
key: 'mergeRequestApproved',
label: s__('DevopsAdoption|Approvals'),
...fieldOptions,
},
{
key: 'runnerConfigured',
label: s__('DevopsAdoption|Runners'),
...fieldOptions,
},
{
key: 'pipelineSucceeded',
label: s__('DevopsAdoption|Pipelines'),
...fieldOptions,
},
{
key: 'deploySucceeded',
label: s__('DevopsAdoption|Deploys'),
...fieldOptions,
},
{
key: 'securityScanSucceeded',
label: s__('DevopsAdoption|Scanning'),
...fieldOptions,
},
{
key: 'actions',
label: '',
tdClass: 'actions-cell',
...fieldOptions,
},
],
testids: DEVOPS_ADOPTION_TABLE_TEST_IDS,
props: {
segments: {
type: Array,
required: true,
},
},
};
</script>
<template>
<gl-table
:fields="$options.tableHeaderFields"
:items="segments"
thead-class="gl-border-t-0 gl-border-b-solid gl-border-b-1 gl-border-b-gray-100"
stacked="sm"
>
<template #cell(name)="{ item }">
<div :data-testid="$options.testids.SEGMENT">
<strong>{{ item.name }}</strong>
</div>
</template>
<template #cell(issueOpened)="{ item }">
<devops-adoption-table-cell-flag
:data-testid="$options.testids.ISSUES"
:enabled="item.latestSnapshot.issueOpened"
/>
</template>
<template #cell(mergeRequestOpened)="{ item }">
<devops-adoption-table-cell-flag
:data-testid="$options.testids.MRS"
:enabled="item.latestSnapshot.mergeRequestOpened"
/>
</template>
<template #cell(mergeRequestApproved)="{ item }">
<devops-adoption-table-cell-flag
:data-testid="$options.testids.APPROVALS"
:enabled="item.latestSnapshot.mergeRequestApproved"
/>
</template>
<template #cell(runnerConfigured)="{ item }">
<devops-adoption-table-cell-flag
:data-testid="$options.testids.RUNNERS"
:enabled="item.latestSnapshot.runnerConfigured"
/>
</template>
<template #cell(pipelineSucceeded)="{ item }">
<devops-adoption-table-cell-flag
:data-testid="$options.testids.PIPELINES"
:enabled="item.latestSnapshot.pipelineSucceeded"
/>
</template>
<template #cell(deploySucceeded)="{ item }">
<devops-adoption-table-cell-flag
:data-testid="$options.testids.DEPLOYS"
:enabled="item.latestSnapshot.deploySucceeded"
/>
</template>
<template #cell(securityScanSucceeded)="{ item }">
<devops-adoption-table-cell-flag
:data-testid="$options.testids.SCANNING"
:enabled="item.latestSnapshot.securityScanSucceeded"
/>
</template>
<template #cell(actions)>
<div :data-testid="$options.testids.ACTIONS">
<gl-button category="tertiary" icon="ellipsis_h" />
</div>
</template>
</gl-table>
</template>
...@@ -9,3 +9,16 @@ export const DEVOPS_ADOPTION_STRINGS = { ...@@ -9,3 +9,16 @@ export const DEVOPS_ADOPTION_STRINGS = {
button: s__('DevopsAdoption|Add new segment'), button: s__('DevopsAdoption|Add new segment'),
}, },
}; };
export const DEVOPS_ADOPTION_TABLE_TEST_IDS = {
TABLE_HEADERS: 'header',
SEGMENT: 'segmentCol',
ISSUES: 'issuesCol',
MRS: 'mrsCol',
APPROVALS: 'approvalsCol',
RUNNERS: 'runnersCol',
PIPELINES: 'pipelinesCol',
DEPLOYS: 'deploysCol',
ACTIONS: 'actionsCol',
SCANNING: 'scanningCol',
};
...@@ -12,3 +12,12 @@ ...@@ -12,3 +12,12 @@
background-color: var(--indigo-600, $indigo-600); background-color: var(--indigo-600, $indigo-600);
} }
} }
@include media-breakpoint-down(sm) {
.actions-cell {
div {
width: 100% !important;
text-align: center !important;
}
}
}
import { mount } from '@vue/test-utils';
import { GlTable, GlButton } from '@gitlab/ui';
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 { DEVOPS_ADOPTION_TABLE_TEST_IDS as TEST_IDS } from 'ee/admin/dev_ops_report/constants';
import { devopsAdoptionSegmentsData, devopsAdoptionTableHeaders } from '../mock_data';
describe('DevopsAdoptionTable', () => {
let wrapper;
const createComponent = () => {
wrapper = mount(DevopsAdoptionTable, {
propsData: {
segments: devopsAdoptionSegmentsData.nodes,
},
});
};
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const findTable = () => wrapper.find(GlTable);
const findCol = testId => findTable().find(`[data-testid="${testId}"]`);
const findColSubComponent = (colTestId, childComponent) =>
findCol(colTestId).find(childComponent);
it('displays the correct table headers', () => {
const headers = findTable().findAll(`[data-testid="${TEST_IDS.TABLE_HEADERS}"]`);
expect(headers).toHaveLength(devopsAdoptionTableHeaders.length);
devopsAdoptionTableHeaders.forEach((headerText, i) =>
expect(headers.at(i).text()).toEqual(headerText),
);
});
describe('table fields', () => {
it('displays the correct segment name', () => {
expect(findCol(TEST_IDS.SEGMENT).text()).toBe('Segment 1');
});
it.each`
id | field | flag
${TEST_IDS.ISSUES} | ${'issues'} | ${true}
${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 }) => {
const booleanFlag = findColSubComponent(id, DevopsAdoptionTableCellFlag);
expect(booleanFlag.props('enabled')).toBe(flag);
});
it('displays the actions icon', () => {
const button = findColSubComponent(TEST_IDS.ACTIONS, GlButton);
expect(button.exists()).toBe(true);
expect(button.props('icon')).toBe('ellipsis_h');
expect(button.props('category')).toBe('tertiary');
});
});
});
export const devopsAdoptionSegmentsData = {
nodes: [
{
name: 'Segment 1',
latestSnapshot: {
issueOpened: true,
mergeRequestOpened: true,
mergeRequestApproved: false,
runnerConfigured: true,
pipelineSucceeded: false,
deploySucceeded: false,
securityScanSucceeded: false,
recordedAt: '2020-10-31T23:59:59Z',
__typename: 'latestSnapshot',
},
__typename: 'devopsSegment',
},
],
__typename: 'devopsAdoptionSegments',
};
export const devopsAdoptionTableHeaders = [
'Segment',
'Issues',
'MRs',
'Approvals',
'Runners',
'Pipelines',
'Deploys',
'Scanning',
'',
];
...@@ -9457,9 +9457,33 @@ msgstr "" ...@@ -9457,9 +9457,33 @@ msgstr ""
msgid "DevopsAdoption|Add new segment" msgid "DevopsAdoption|Add new segment"
msgstr "" msgstr ""
msgid "DevopsAdoption|Approvals"
msgstr ""
msgid "DevopsAdoption|Deploys"
msgstr ""
msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team." msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team."
msgstr "" msgstr ""
msgid "DevopsAdoption|Issues"
msgstr ""
msgid "DevopsAdoption|MRs"
msgstr ""
msgid "DevopsAdoption|Pipelines"
msgstr ""
msgid "DevopsAdoption|Runners"
msgstr ""
msgid "DevopsAdoption|Scanning"
msgstr ""
msgid "DevopsAdoption|Segment"
msgstr ""
msgid "DevopsReport|Adoption" msgid "DevopsReport|Adoption"
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