Commit b6f66433 authored by Jiaan Louw's avatar Jiaan Louw Committed by Nicolò Maria Mezzopera

Refactor compliance dashboard merge requests

Move the merge requests content into its own
directory in preperation for additional content
that will be added.

Hide tabs behind cookie flag.
parent e291de43
<script>
import { GlTabs, GlTab, GlTooltipDirective } from '@gitlab/ui';
import { isEmpty } from 'lodash';
import { sprintf, __, s__ } from '~/locale';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import ApprovalStatus from './approval_status.vue';
import Approvers from './approvers.vue';
import Cookies from 'js-cookie';
import { GlTabs, GlTab } from '@gitlab/ui';
import { __ } from '~/locale';
import MergeRequestsGrid from './merge_requests/grid.vue';
import EmptyState from './empty_state.vue';
import MergeRequest from './merge_request.vue';
import Pagination from './pagination.vue';
import PipelineStatus from './pipeline_status.vue';
import GridColumnHeading from './grid_column_heading.vue';
import { COMPLIANCE_TAB_COOKIE_KEY } from '../constants';
export default {
name: 'ComplianceDashboard',
directives: {
GlTooltip: GlTooltipDirective,
},
components: {
ApprovalStatus,
Approvers,
MergeRequestsGrid,
EmptyState,
GridColumnHeading,
MergeRequest,
Pagination,
PipelineStatus,
GlTab,
GlTabs,
},
mixins: [timeagoMixin],
props: {
emptyStateSvgPath: {
type: String,
......@@ -51,28 +35,13 @@ export default {
},
},
methods: {
key(id, value) {
return `${id}-${value}`;
},
timeAgoString(mergedAt) {
return sprintf(s__('merged %{timeAgo}'), {
timeAgo: this.timeFormatted(mergedAt),
});
},
timeTooltip(mergedAt) {
return this.tooltipTitle(mergedAt);
},
hasStatus(status) {
return !isEmpty(status);
showTabs() {
return Cookies.get(COMPLIANCE_TAB_COOKIE_KEY) === 'true';
},
},
strings: {
heading: __('Compliance Dashboard'),
subheading: __('Here you will find recent merge request activity'),
mergeRequestLabel: __('Merge Request'),
approvalStatusLabel: __('Approval Status'),
pipelineStatusLabel: __('Pipeline'),
updatesLabel: __('Updates'),
mergeRequestsTabLabel: __('Merge Requests'),
},
};
......@@ -84,61 +53,16 @@ export default {
<h4>{{ $options.strings.heading }}</h4>
<p>{{ $options.strings.subheading }}</p>
</header>
<gl-tabs>
<gl-tabs v-if="showTabs()">
<gl-tab>
<template #title>
<span>{{ $options.strings.mergeRequestsTabLabel }}</span>
</template>
<div class="dashboard-grid">
<grid-column-heading :heading="$options.strings.mergeRequestLabel" />
<grid-column-heading
:heading="$options.strings.approvalStatusLabel"
class="gl-text-center"
/>
<grid-column-heading
:heading="$options.strings.pipelineStatusLabel"
class="gl-text-center"
/>
<grid-column-heading :heading="$options.strings.updatesLabel" class="gl-text-right" />
<template v-for="mergeRequest in mergeRequests">
<merge-request :key="key(mergeRequest.id, 'MR')" :merge-request="mergeRequest" />
<div
:key="key(mergeRequest.id, 'approvalStatus')"
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<approval-status
v-if="hasStatus(mergeRequest.approval_status)"
:status="mergeRequest.approval_status"
/>
</div>
<div
:key="key(mergeRequest.id, 'pipeline')"
class="dashboard-pipeline gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<pipeline-status
v-if="hasStatus(mergeRequest.pipeline_status)"
:status="mergeRequest.pipeline_status"
/>
</div>
<div
:key="key(mergeRequest.id, 'updates')"
class="gl-text-right gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5 gl-relative"
>
<approvers :approvers="mergeRequest.approved_by_users" />
<span class="gl-text-gray-700">
<time v-gl-tooltip.bottom="timeTooltip(mergeRequest.merged_at)">{{
timeAgoString(mergeRequest.merged_at)
}}</time>
</span>
</div>
</template>
</div>
<pagination :is-last-page="isLastPage" />
<merge-requests-grid :merge-requests="mergeRequests" :is-last-page="isLastPage" />
</gl-tab>
</gl-tabs>
<merge-requests-grid v-else :merge-requests="mergeRequests" :is-last-page="isLastPage" />
</div>
<empty-state v-else :image-path="emptyStateSvgPath" />
</template>
<script>
import { sprintf, __ } from '~/locale';
import { GlAvatarLink, GlAvatar, GlAvatarsInline, GlTooltipDirective } from '@gitlab/ui';
import { PRESENTABLE_APPROVERS_LIMIT } from '../constants';
import { PRESENTABLE_APPROVERS_LIMIT } from '../../constants';
export default {
directives: {
......
<script>
import { GlTooltipDirective } from '@gitlab/ui';
import { isEmpty } from 'lodash';
import { sprintf, __ } from '~/locale';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import ApprovalStatus from './approval_status.vue';
import Approvers from './approvers.vue';
import MergeRequest from './merge_request.vue';
import PipelineStatus from './pipeline_status.vue';
import GridColumnHeading from '../shared/grid_column_heading.vue';
import Pagination from '../shared/pagination.vue';
export default {
directives: {
GlTooltip: GlTooltipDirective,
},
components: {
ApprovalStatus,
Approvers,
GridColumnHeading,
MergeRequest,
PipelineStatus,
Pagination,
},
mixins: [timeagoMixin],
props: {
mergeRequests: {
type: Array,
required: true,
},
isLastPage: {
type: Boolean,
required: false,
default: false,
},
},
methods: {
key(id, value) {
return `${id}-${value}`;
},
timeAgoString(mergedAt) {
return sprintf(__('merged %{timeAgo}'), {
timeAgo: this.timeFormatted(mergedAt),
});
},
timeTooltip(mergedAt) {
return this.tooltipTitle(mergedAt);
},
hasStatus(status) {
return !isEmpty(status);
},
},
strings: {
mergeRequestLabel: __('Merge Request'),
approvalStatusLabel: __('Approval Status'),
pipelineStatusLabel: __('Pipeline'),
updatesLabel: __('Updates'),
},
keyTypes: {
mergeRequest: 'MR',
approvalStatus: 'approvalStatus',
pipeline: 'pipeline',
updates: 'updates',
},
};
</script>
<template>
<div>
<div class="dashboard-grid">
<grid-column-heading :heading="$options.strings.mergeRequestLabel" />
<grid-column-heading :heading="$options.strings.approvalStatusLabel" class="gl-text-center" />
<grid-column-heading :heading="$options.strings.pipelineStatusLabel" class="gl-text-center" />
<grid-column-heading :heading="$options.strings.updatesLabel" class="gl-text-right" />
<template v-for="mergeRequest in mergeRequests">
<merge-request
:key="key(mergeRequest.id, $options.keyTypes.mergeRequest)"
:merge-request="mergeRequest"
/>
<div
:key="key(mergeRequest.id, $options.keyTypes.approvalStatus)"
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<approval-status
v-if="hasStatus(mergeRequest.approval_status)"
:status="mergeRequest.approval_status"
/>
</div>
<div
:key="key(mergeRequest.id, $options.keyTypes.pipeline)"
class="dashboard-pipeline gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<pipeline-status
v-if="hasStatus(mergeRequest.pipeline_status)"
:status="mergeRequest.pipeline_status"
/>
</div>
<div
:key="key(mergeRequest.id, $options.keyTypes.updates)"
class="gl-text-right gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5 gl-relative"
>
<approvers :approvers="mergeRequest.approved_by_users" />
<span class="gl-text-gray-700">
<time v-gl-tooltip.bottom="timeTooltip(mergeRequest.merged_at)">{{
timeAgoString(mergeRequest.merged_at)
}}</time>
</span>
</div>
</template>
</div>
<pagination class="gl-mt-5" :is-last-page="isLastPage" />
</div>
</template>
export const PRESENTABLE_APPROVERS_LIMIT = 2;
export default {};
export const COMPLIANCE_TAB_COOKIE_KEY = 'compliance_dashboard_tabs';
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ComplianceDashboard component when there are merge requests matches the snapshot 1`] = `
exports[`ComplianceDashboard component when there are merge requests and the show tabs cookie is true matches the snapshot 1`] = `
<div
class="compliance-dashboard"
>
......@@ -26,97 +26,9 @@ exports[`ComplianceDashboard component when there are merge requests matches the
>
<template>
<div
class="dashboard-grid"
>
<grid-column-heading-stub
heading="Merge Request"
/>
<grid-column-heading-stub
class="gl-text-center"
heading="Approval Status"
/>
<grid-column-heading-stub
class="gl-text-center"
heading="Pipeline"
/>
<grid-column-heading-stub
class="gl-text-right"
heading="Updates"
/>
<div
data-testid="merge-request"
>
Merge request 0
</div>
<div
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<!---->
</div>
<div
class="dashboard-pipeline gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<!---->
</div>
<div
class="gl-text-right gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5 gl-relative"
>
<approvers-stub
approvers=""
/>
<span
class="gl-text-gray-700"
>
<time>
merged 2 days ago
</time>
</span>
</div>
<div
data-testid="merge-request"
>
Merge request 1
</div>
<div
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<!---->
</div>
<div
class="dashboard-pipeline gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<!---->
</div>
<div
class="gl-text-right gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5 gl-relative"
>
<approvers-stub
approvers=""
/>
<span
class="gl-text-gray-700"
>
<time>
merged 2 days ago
</time>
</span>
</div>
</div>
<pagination-stub />
<merge-requests-grid-stub
mergerequests="[object Object],[object Object]"
/>
</template>
<template>
<span>
......@@ -128,6 +40,28 @@ exports[`ComplianceDashboard component when there are merge requests matches the
</div>
`;
exports[`ComplianceDashboard component when there are merge requests matches the snapshot 1`] = `
<div
class="compliance-dashboard"
>
<header
class="gl-my-5"
>
<h4>
Compliance Dashboard
</h4>
<p>
Here you will find recent merge request activity
</p>
</header>
<merge-requests-grid-stub
mergerequests="[object Object],[object Object]"
/>
</div>
`;
exports[`ComplianceDashboard component when there are no merge requests matches the snapshot 1`] = `
<empty-state-stub
imagepath="empty.svg"
......
import Cookies from 'js-cookie';
import { shallowMount } from '@vue/test-utils';
import { GlTabs, GlTab } from '@gitlab/ui';
import ComplianceDashboard from 'ee/compliance_dashboard/components/dashboard.vue';
import ApprovalStatus from 'ee/compliance_dashboard/components/approval_status.vue';
import PipelineStatus from 'ee/compliance_dashboard/components/pipeline_status.vue';
import Approvers from 'ee/compliance_dashboard/components/approvers.vue';
import MergeRequestGrid from 'ee/compliance_dashboard/components/merge_requests/grid.vue';
import { COMPLIANCE_TAB_COOKIE_KEY } from 'ee/compliance_dashboard/constants';
import { createMergeRequests } from '../mock_data';
describe('ComplianceDashboard component', () => {
let wrapper;
const findMergeRequests = () => wrapper.findAll('[data-testid="merge-request"]');
const findTime = () => wrapper.find('time');
const findApprovalStatus = () => wrapper.find(ApprovalStatus);
const findPipelineStatus = () => wrapper.find(PipelineStatus);
const findApprovers = () => wrapper.find(Approvers);
const isLastPage = false;
const mergeRequests = createMergeRequests({ count: 2 });
const findMergeRequestsGrid = () => wrapper.find(MergeRequestGrid);
const findDashboardTabs = () => wrapper.find(GlTabs);
const createComponent = (props = {}, options = {}) => {
const createComponent = (props = {}) => {
return shallowMount(ComplianceDashboard, {
propsData: {
mergeRequests: createMergeRequests({ count: 2, options }),
isLastPage: false,
mergeRequests,
isLastPage,
emptyStateSvgPath: 'empty.svg',
...props,
},
stubs: {
GlTab,
MergeRequest: {
props: { mergeRequest: Object },
template: `<div data-testid="merge-request">{{ mergeRequest.title }}</div>`,
},
},
});
};
......@@ -41,50 +36,41 @@ describe('ComplianceDashboard component', () => {
describe('when there are merge requests', () => {
beforeEach(() => {
Cookies.set(COMPLIANCE_TAB_COOKIE_KEY, false);
wrapper = createComponent();
});
afterEach(() => {
Cookies.remove(COMPLIANCE_TAB_COOKIE_KEY);
});
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('renders a list of merge requests', () => {
expect(findMergeRequests().length).toEqual(2);
it('renders the merge requests', () => {
expect(findMergeRequestsGrid().exists()).toBe(true);
});
it('renders the dashboard tabs', () => {
expect(findDashboardTabs().exists()).toEqual(true);
it('sets the MergeRequestGrid properties', () => {
expect(findMergeRequestsGrid().props('mergeRequests')).toBe(mergeRequests);
expect(findMergeRequestsGrid().props('isLastPage')).toBe(isLastPage);
});
describe('approval status', () => {
it('does not render if there is no approval status', () => {
expect(findApprovalStatus().exists()).toBe(false);
describe('and the show tabs cookie is true', () => {
beforeEach(() => {
Cookies.set(COMPLIANCE_TAB_COOKIE_KEY, true);
wrapper = createComponent();
});
it('renders if there is an approval status', () => {
wrapper = createComponent({}, { approvalStatus: 'success' });
expect(findApprovalStatus().exists()).toBe(true);
});
});
describe('pipeline status', () => {
it('does not render if there is no pipeline', () => {
expect(findPipelineStatus().exists()).toBe(false);
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('renders if there is a pipeline', () => {
wrapper = createComponent({}, { addPipeline: true });
expect(findPipelineStatus().exists()).toBe(true);
it('renders the dashboard tabs', () => {
expect(findDashboardTabs().exists()).toBe(true);
});
});
it('renders the approvers list', () => {
expect(findApprovers().exists()).toBe(true);
});
it('renders the "merged at" time', () => {
expect(findTime().text()).toEqual('merged 2 days ago');
});
});
describe('when there are no merge requests', () => {
......@@ -97,11 +83,7 @@ describe('ComplianceDashboard component', () => {
});
it('does not render merge requests', () => {
expect(findMergeRequests().exists()).toEqual(false);
});
it('does not render the dashboard tabs', () => {
expect(findDashboardTabs().exists()).toEqual(false);
expect(findMergeRequestsGrid().exists()).toBe(false);
});
});
});
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`MergeRequestsGrid component when intialized matches the snapshot 1`] = `
<div>
<div
class="dashboard-grid"
>
<grid-column-heading-stub
heading="Merge Request"
/>
<grid-column-heading-stub
class="gl-text-center"
heading="Approval Status"
/>
<grid-column-heading-stub
class="gl-text-center"
heading="Pipeline"
/>
<grid-column-heading-stub
class="gl-text-right"
heading="Updates"
/>
<div
data-testid="merge-request"
>
Merge request 0
</div>
<div
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<!---->
</div>
<div
class="dashboard-pipeline gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<!---->
</div>
<div
class="gl-text-right gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5 gl-relative"
>
<approvers-stub
approvers=""
/>
<span
class="gl-text-gray-700"
>
<time>
merged 2 days ago
</time>
</span>
</div>
<div
data-testid="merge-request"
>
Merge request 1
</div>
<div
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<!---->
</div>
<div
class="dashboard-pipeline gl-display-flex gl-align-items-center gl-justify-content-center gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5"
>
<!---->
</div>
<div
class="gl-text-right gl-border-b-solid gl-border-b-1 gl-border-b-gray-100 gl-p-5 gl-relative"
>
<approvers-stub
approvers=""
/>
<span
class="gl-text-gray-700"
>
<time>
merged 2 days ago
</time>
</span>
</div>
</div>
<pagination-stub
class="gl-mt-5"
/>
</div>
`;
import { shallowMount } from '@vue/test-utils';
import { GlLink } from '@gitlab/ui';
import ApprovalStatus from 'ee/compliance_dashboard/components/approval_status.vue';
import ApprovalStatus from 'ee/compliance_dashboard/components/merge_requests/approval_status.vue';
describe('ApprovalStatus component', () => {
let wrapper;
......
import { shallowMount } from '@vue/test-utils';
import { GlAvatarLink } from '@gitlab/ui';
import Approvers from 'ee/compliance_dashboard/components/approvers.vue';
import Approvers from 'ee/compliance_dashboard/components/merge_requests/approvers.vue';
import { PRESENTABLE_APPROVERS_LIMIT } from 'ee/compliance_dashboard/constants';
import { createApprovers } from '../mock_data';
import { createApprovers } from '../../mock_data';
describe('MergeRequest component', () => {
let wrapper;
......
import { shallowMount } from '@vue/test-utils';
import MergeRequestsGrid from 'ee/compliance_dashboard/components/merge_requests/grid.vue';
import ApprovalStatus from 'ee/compliance_dashboard/components/merge_requests/approval_status.vue';
import PipelineStatus from 'ee/compliance_dashboard/components/merge_requests/pipeline_status.vue';
import Approvers from 'ee/compliance_dashboard/components/merge_requests/approvers.vue';
import { createMergeRequests } from '../../mock_data';
describe('MergeRequestsGrid component', () => {
let wrapper;
const findMergeRequests = () => wrapper.findAll('[data-testid="merge-request"]');
const findTime = () => wrapper.find('time');
const findApprovalStatus = () => wrapper.find(ApprovalStatus);
const findPipelineStatus = () => wrapper.find(PipelineStatus);
const findApprovers = () => wrapper.find(Approvers);
const createComponent = (options = {}) => {
return shallowMount(MergeRequestsGrid, {
propsData: {
mergeRequests: createMergeRequests({ count: 2, options }),
isLastPage: false,
},
stubs: {
MergeRequest: {
props: { mergeRequest: Object },
template: `<div data-testid="merge-request">{{ mergeRequest.title }}</div>`,
},
},
});
};
afterEach(() => {
wrapper.destroy();
});
describe('when intialized', () => {
beforeEach(() => {
wrapper = createComponent();
});
it('matches the snapshot', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('renders a list of merge requests', () => {
expect(findMergeRequests().length).toBe(2);
});
describe('approval status', () => {
it('does not render if there is no approval status', () => {
expect(findApprovalStatus().exists()).toBe(false);
});
it('renders if there is an approval status', () => {
wrapper = createComponent({ approvalStatus: 'success' });
expect(findApprovalStatus().exists()).toBe(true);
});
});
describe('pipeline status', () => {
it('does not render if there is no pipeline', () => {
expect(findPipelineStatus().exists()).toBe(false);
});
it('renders if there is a pipeline', () => {
wrapper = createComponent({ addPipeline: true });
expect(findPipelineStatus().exists()).toBe(true);
});
});
it('renders the approvers list', () => {
expect(findApprovers().exists()).toBe(true);
});
it('renders the "merged at" time', () => {
expect(findTime().text()).toBe('merged 2 days ago');
});
});
});
import { shallowMount } from '@vue/test-utils';
import { GlAvatarLink, GlAvatar } from '@gitlab/ui';
import MergeRequest from 'ee/compliance_dashboard/components/merge_request.vue';
import { createMergeRequest } from '../mock_data';
import MergeRequest from 'ee/compliance_dashboard/components/merge_requests/merge_request.vue';
import { createMergeRequest } from '../../mock_data';
describe('MergeRequest component', () => {
let wrapper;
......
import { shallowMount } from '@vue/test-utils';
import PipelineStatus from 'ee/compliance_dashboard/components/pipeline_status.vue';
import { createPipelineStatus } from '../mock_data';
import PipelineStatus from 'ee/compliance_dashboard/components/merge_requests/pipeline_status.vue';
import { createPipelineStatus } from '../../mock_data';
describe('PipelineStatus component', () => {
let wrapper;
......
import { shallowMount } from '@vue/test-utils';
import GridColumnHeading from 'ee/compliance_dashboard/components/grid_column_heading.vue';
import GridColumnHeading from 'ee/compliance_dashboard/components/shared/grid_column_heading.vue';
describe('GridColumnHeading component', () => {
let wrapper;
......
import { shallowMount } from '@vue/test-utils';
import { GlPagination } from '@gitlab/ui';
import Pagination from 'ee/compliance_dashboard/components/pagination.vue';
import Pagination from 'ee/compliance_dashboard/components/shared/pagination.vue';
describe('MergeRequest component', () => {
describe('Pagination component', () => {
let wrapper;
const findGlPagination = () => wrapper.find(GlPagination);
......
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