Commit 974e7753 authored by Robert Hunt's avatar Robert Hunt Committed by Brandon Labuschagne

Add project section to compliance dashboard drawer

parent 98d7e528
<script> <script>
import { GlDrawer } from '@gitlab/ui'; import { GlDrawer } from '@gitlab/ui';
import Project from './drawer_sections/project.vue';
export default { export default {
components: { components: {
GlDrawer, GlDrawer,
Project,
}, },
props: { props: {
mergeRequest: { mergeRequest: {
...@@ -42,5 +44,13 @@ export default { ...@@ -42,5 +44,13 @@ export default {
<template #header> <template #header>
<h4 data-testid="dashboard-drawer-title">{{ mergeRequest.title }}</h4> <h4 data-testid="dashboard-drawer-title">{{ mergeRequest.title }}</h4>
</template> </template>
<template v-if="showDrawer" #default>
<project
:avatar-url="mergeRequest.project.avatar_url"
:compliance-framework="mergeRequest.compliance_management_framework"
:name="mergeRequest.project.name"
:url="mergeRequest.project.web_url"
/>
</template>
</gl-drawer> </gl-drawer>
</template> </template>
<script>
import { GlAvatarLabeled, GlAvatarLink } from '@gitlab/ui';
import ComplianceFrameworkLabel from 'ee/vue_shared/components/compliance_framework_label/compliance_framework_label.vue';
import { __ } from '~/locale';
import DrawerSectionHeader from '../shared/drawer_section_header.vue';
export default {
components: {
ComplianceFrameworkLabel,
DrawerSectionHeader,
GlAvatarLabeled,
GlAvatarLink,
},
props: {
avatarUrl: {
type: String,
required: false,
default: '',
},
complianceFramework: {
type: Object,
required: false,
default: null,
},
name: {
type: String,
required: true,
},
url: {
type: String,
required: true,
},
},
i18n: {
header: __('Project'),
},
};
</script>
<template>
<div>
<drawer-section-header>{{ $options.i18n.header }}</drawer-section-header>
<div class="gl-display-flex gl-align-items-center">
<gl-avatar-link :title="name" :href="url">
<gl-avatar-labeled
:size="16"
:entity-name="name"
label=""
:sub-label="name"
:src="avatarUrl"
/>
</gl-avatar-link>
<compliance-framework-label
v-if="complianceFramework"
class="gl-ml-3"
:name="complianceFramework.name"
:color="complianceFramework.color"
:description="complianceFramework.description"
/>
</div>
</div>
</template>
<script>
export default {};
</script>
<template>
<span class="gl-display-block gl-mb-4 gl-text-gray-900">
<slot></slot>
</span>
</template>
...@@ -20,6 +20,14 @@ class MergeRequestComplianceEntity < Grape::Entity ...@@ -20,6 +20,14 @@ class MergeRequestComplianceEntity < Grape::Entity
merge_request.to_reference(merge_request.project.group) merge_request.to_reference(merge_request.project.group)
end end
expose :project do |merge_request|
{
avatar_url: merge_request.project.avatar_url,
name: merge_request.project.name,
web_url: merge_request.project.web_url
}
end
expose :author, using: API::Entities::UserBasic expose :author, using: API::Entities::UserBasic
expose :approved_by_users, using: API::Entities::UserBasic expose :approved_by_users, using: API::Entities::UserBasic
......
import { GlAvatarLabeled, GlAvatarLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Project from 'ee/compliance_dashboard/components/drawer_sections/project.vue';
import DrawerSectionHeader from 'ee/compliance_dashboard/components/shared/drawer_section_header.vue';
import ComplianceFrameworkLabel from 'ee/vue_shared/components/compliance_framework_label/compliance_framework_label.vue';
import { complianceFramework } from 'ee_jest/vue_shared/components/compliance_framework_label/mock_data';
describe('Project component', () => {
let wrapper;
const projectName = 'Foo project';
const url = 'https://foo.com/project';
const avatarUrl = '/foo/bar.png';
const findSectionHeader = () => wrapper.findComponent(DrawerSectionHeader);
const findAvatarLink = () => wrapper.findComponent(GlAvatarLink);
const findAvatarLabel = () => wrapper.findComponent(GlAvatarLabeled);
const findComplianceFrameworkLabel = () => wrapper.findComponent(ComplianceFrameworkLabel);
const createComponent = (props) => {
return shallowMount(Project, {
propsData: {
name: projectName,
url,
...props,
},
});
};
afterEach(() => {
wrapper.destroy();
});
describe('by default', () => {
beforeEach(() => {
wrapper = createComponent();
});
it('renders the header', () => {
expect(findSectionHeader().text()).toBe('Project');
});
it('renders the avatar with a name and url', () => {
expect(findAvatarLink().attributes()).toStrictEqual({
title: projectName,
href: url,
});
expect(findAvatarLabel().props()).toMatchObject({
subLabel: projectName,
label: '',
});
expect(findAvatarLabel().attributes()).toMatchObject({
'entity-name': projectName,
src: '',
});
});
it('does not render the compliance framework label', () => {
expect(findComplianceFrameworkLabel().exists()).toBe(false);
});
});
describe('when the avatar URL is provided', () => {
beforeEach(() => {
wrapper = createComponent({ avatarUrl });
});
it('renders the avatar with the URL', () => {
expect(findAvatarLabel().props()).toMatchObject({
subLabel: projectName,
label: '',
});
expect(findAvatarLabel().attributes()).toMatchObject({
'entity-name': projectName,
src: avatarUrl,
});
});
});
describe('when the compliance framework is provided', () => {
beforeEach(() => {
wrapper = createComponent({ complianceFramework });
});
it('renders the compliance framework label', () => {
const { color, description, name } = complianceFramework;
expect(findComplianceFrameworkLabel().props()).toStrictEqual({
color,
description,
name,
});
});
});
});
import { GlDrawer } from '@gitlab/ui'; import { GlDrawer } from '@gitlab/ui';
import MergeRequestDrawer from 'ee/compliance_dashboard/components/drawer.vue'; import MergeRequestDrawer from 'ee/compliance_dashboard/components/drawer.vue';
import Project from 'ee/compliance_dashboard/components/drawer_sections/project.vue';
import { complianceFramework } from 'ee_jest/vue_shared/components/compliance_framework_label/mock_data';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { createMergeRequests } from '../mock_data'; import { createMergeRequests } from '../mock_data';
describe('MergeRequestDrawer component', () => { describe('MergeRequestDrawer component', () => {
let wrapper; let wrapper;
const mergeRequest = createMergeRequests({ count: 1 })[0]; const mergeRequest = createMergeRequests({
count: 1,
props: {
compliance_management_framework: complianceFramework,
},
})[0];
const findTitle = () => wrapper.findByTestId('dashboard-drawer-title'); const findTitle = () => wrapper.findByTestId('dashboard-drawer-title');
const findDrawer = () => wrapper.findComponent(GlDrawer); const findDrawer = () => wrapper.findComponent(GlDrawer);
const findProject = () => wrapper.findComponent(Project);
const createComponent = (props) => { const createComponent = (props) => {
return shallowMountExtended(MergeRequestDrawer, { return shallowMountExtended(MergeRequestDrawer, {
...@@ -31,6 +39,10 @@ describe('MergeRequestDrawer component', () => { ...@@ -31,6 +39,10 @@ describe('MergeRequestDrawer component', () => {
it('the drawer is not shown', () => { it('the drawer is not shown', () => {
expect(findDrawer().props('open')).toBe(false); expect(findDrawer().props('open')).toBe(false);
}); });
it('the sections are not mounted', () => {
expect(findProject().exists()).toBe(false);
});
}); });
describe('when open', () => { describe('when open', () => {
...@@ -45,5 +57,14 @@ describe('MergeRequestDrawer component', () => { ...@@ -45,5 +57,14 @@ describe('MergeRequestDrawer component', () => {
it('has the drawer title', () => { it('has the drawer title', () => {
expect(findTitle().text()).toEqual(mergeRequest.title); expect(findTitle().text()).toEqual(mergeRequest.title);
}); });
it('has the project section', () => {
expect(findProject().props()).toStrictEqual({
avatarUrl: mergeRequest.project.avatar_url,
complianceFramework,
name: mergeRequest.project.name,
url: mergeRequest.project.web_url,
});
});
}); });
}); });
import { shallowMount } from '@vue/test-utils';
import DrawerSectionHeader from 'ee/compliance_dashboard/components/shared/drawer_section_header.vue';
describe('DrawerSectionHeader component', () => {
let wrapper;
const headerText = 'Section header';
const createComponent = () => {
return shallowMount(DrawerSectionHeader, {
slots: {
default: headerText,
},
});
};
beforeEach(() => {
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('renders the header text', () => {
expect(wrapper.text()).toBe(headerText);
});
});
...@@ -40,6 +40,11 @@ export const createMergeRequest = ({ id = 1, props } = {}) => { ...@@ -40,6 +40,11 @@ export const createMergeRequest = ({ id = 1, props } = {}) => {
author: createUser(id), author: createUser(id),
pipeline_status: createPipelineStatus('success'), pipeline_status: createPipelineStatus('success'),
approval_status: 'success', approval_status: 'success',
project: {
avatar_url: '/foo/bar.png',
name: 'Foo',
web_url: 'https://foo.com/project',
},
}; };
return { ...mergeRequest, ...props }; return { ...mergeRequest, ...props };
......
...@@ -30,7 +30,8 @@ RSpec.describe MergeRequestComplianceEntity do ...@@ -30,7 +30,8 @@ RSpec.describe MergeRequestComplianceEntity do
:target_branch_uri, :target_branch_uri,
:source_branch, :source_branch,
:source_branch_uri, :source_branch_uri,
:compliance_management_framework :compliance_management_framework,
:project
) )
end end
......
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