Render tree last commit widget with Vue

Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/62766
This commit is contained in:
Phil Hughes 2019-06-19 11:32:33 +01:00
parent 8716eb0cef
commit bf6b70662b
No known key found for this signature in database
GPG key ID: 32245528C52E0F9F
6 changed files with 397 additions and 0 deletions

View file

@ -0,0 +1,145 @@
<script>
import { GlTooltipDirective, GlLink, GlButton } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
import Icon from '../../vue_shared/components/icon.vue';
import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import TimeagoTooltip from '../../vue_shared/components/time_ago_tooltip.vue';
import CommitPipelineStatus from '../../projects/tree/components/commit_pipeline_status_component.vue';
import CiIcon from '../../vue_shared/components/ci_icon.vue';
import ClipboardButton from '../../vue_shared/components/clipboard_button.vue';
import getRefMixin from '../mixins/get_ref';
import getProjectPath from '../queries/getProjectPath.graphql';
import pathLastCommit from '../queries/pathLastCommit.query.graphql';
export default {
components: {
Icon,
UserAvatarLink,
TimeagoTooltip,
CommitPipelineStatus,
ClipboardButton,
CiIcon,
GlLink,
GlButton,
},
directives: {
GlTooltip: GlTooltipDirective,
},
mixins: [getRefMixin],
apollo: {
projectPath: {
query: getProjectPath,
},
commit: {
query: pathLastCommit,
variables() {
return {
projectPath: this.projectPath,
ref: this.ref,
path: this.currentPath.replace(/^\//, ''),
};
},
update: data => data.project.repository.tree.commit,
},
},
props: {
currentPath: {
type: String,
required: false,
default: '',
},
},
data() {
return {
projectPath: '',
commit: {},
showDescription: false,
};
},
computed: {
statusTitle() {
return sprintf(s__('Commits|Commit: %{commitText}'), {
commitText: this.commit.pipeline.detailedStatus.text,
});
},
isLoading() {
return this.$apollo.queries.commit.loading;
},
showCommitId() {
return this.commit.id.substr(0, 8);
},
},
methods: {
toggleShowDescription() {
this.showDescription = !this.showDescription;
},
},
};
</script>
<template>
<div v-if="!isLoading" class="info-well d-none d-sm-flex project-last-commit commit p-3">
<user-avatar-link
v-if="commit.author"
:link-href="commit.author.webUrl"
:img-src="commit.author.avatarUrl"
:img-size="40"
class="avatar-cell"
/>
<div class="commit-detail flex-list">
<div class="commit-content qa-commit-content">
<gl-link :href="commit.webUrl" class="commit-row-message item-title">
{{ commit.title }}
</gl-link>
<gl-button
v-if="commit.description"
:class="{ open: showDescription }"
:aria-label="__('Show commit description')"
class="text-expander"
@click="toggleShowDescription"
>
<icon name="ellipsis_h" />
</gl-button>
<div class="committer">
<gl-link
v-if="commit.author"
:href="commit.author.webUrl"
class="commit-author-link js-user-link"
>
{{ commit.author.name }}
</gl-link>
authored
<timeago-tooltip :time="commit.authoredDate" tooltip-placement="bottom" />
</div>
<pre
v-if="commit.description"
v-show="showDescription"
class="commit-row-description append-bottom-8"
>
{{ commit.description }}
</pre>
</div>
<div class="commit-actions flex-row">
<gl-link
v-if="commit.pipeline"
v-gl-tooltip
:href="commit.pipeline.detailedStatus.detailsPath"
:title="statusTitle"
class="js-commit-pipeline"
>
<ci-icon :status="commit.pipeline.detailedStatus" :size="24" :aria-label="statusTitle" />
</gl-link>
<div class="commit-sha-group d-flex">
<div class="label label-monospace monospace">
{{ showCommitId }}
</div>
<clipboard-button
:text="commit.id"
:title="__('Copy commit SHA to clipboard')"
tooltip-placement="bottom"
/>
</div>
</div>
</div>
</div>
</template>

View file

@ -2,6 +2,7 @@ import Vue from 'vue';
import createRouter from './router';
import App from './components/app.vue';
import Breadcrumbs from './components/breadcrumbs.vue';
import LastCommit from './components/last_commit.vue';
import apolloProvider from './graphql';
import { setTitle } from './utils/title';
@ -48,6 +49,24 @@ export default function setupVueRepositoryList() {
},
});
const commitEl = document.getElementById('js-last-commit');
if (commitEl) {
// eslint-disable-next-line no-new
new Vue({
el: commitEl,
router,
apolloProvider,
render(h) {
return h(LastCommit, {
props: {
currentPath: this.$route.params.pathMatch,
},
});
},
});
}
return new Vue({
el,
router,

View file

@ -0,0 +1,29 @@
query pathLastCommit($projectPath: ID!, $path: String, $ref: String!) {
project(fullPath: $projectPath) {
repository {
tree(path: $path, ref: $ref) {
commit {
id
title
message
webUrl
authoredDate
author {
name
avatarUrl
webUrl
}
pipeline {
detailedStatus {
detailsPath
icon
tooltip
text
group
}
}
}
}
}
}
}

View file

@ -9140,6 +9140,9 @@ msgstr ""
msgid "Show comments only"
msgstr ""
msgid "Show commit description"
msgstr ""
msgid "Show complete raw log"
msgstr ""

View file

@ -0,0 +1,98 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Repository last commit component renders commit widget 1`] = `
<div
class="info-well d-none d-sm-flex project-last-commit commit p-3"
>
<useravatarlink-stub
class="avatar-cell"
imgalt=""
imgcssclasses=""
imgsize="40"
imgsrc="https://test.com"
linkhref="https://test.com/test"
tooltipplacement="top"
tooltiptext=""
username=""
/>
<div
class="commit-detail flex-list"
>
<div
class="commit-content qa-commit-content"
>
<gllink-stub
class="commit-row-message item-title"
href="https://test.com/commit/123"
>
Commit title
</gllink-stub>
<!---->
<div
class="committer"
>
<gllink-stub
class="commit-author-link js-user-link"
href="https://test.com/test"
>
Test
</gllink-stub>
authored
<timeagotooltip-stub
cssclass=""
time="2019-01-01"
tooltipplacement="bottom"
/>
</div>
<!---->
</div>
<div
class="commit-actions flex-row"
>
<gllink-stub
class="js-commit-pipeline"
data-original-title="Commit: failed"
href="https://test.com/pipeline"
title=""
>
<ciicon-stub
aria-label="Commit: failed"
cssclasses=""
size="24"
status="[object Object]"
/>
</gllink-stub>
<div
class="commit-sha-group d-flex"
>
<div
class="label label-monospace monospace"
>
12345678
</div>
<clipboardbutton-stub
cssclass="btn-default"
text="123456789"
title="Copy commit SHA to clipboard"
tooltipplacement="bottom"
/>
</div>
</div>
</div>
</div>
`;

View file

@ -0,0 +1,103 @@
import { shallowMount } from '@vue/test-utils';
import LastCommit from '~/repository/components/last_commit.vue';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
let vm;
function createCommitData(data = {}) {
return {
id: '123456789',
title: 'Commit title',
message: 'Commit message',
webUrl: 'https://test.com/commit/123',
authoredDate: '2019-01-01',
author: {
name: 'Test',
avatarUrl: 'https://test.com',
webUrl: 'https://test.com/test',
},
pipeline: {
detailedStatus: {
detailsPath: 'https://test.com/pipeline',
icon: 'failed',
tooltip: 'failed',
text: 'failed',
group: {},
},
},
...data,
};
}
function factory(commit = createCommitData(), loading = false) {
vm = shallowMount(LastCommit, {
mocks: {
$apollo: {
queries: {
commit: {
loading: true,
},
},
},
},
});
vm.setData({ commit });
vm.vm.$apollo.queries.commit.loading = loading;
}
describe('Repository last commit component', () => {
afterEach(() => {
vm.destroy();
});
it.each`
loading | label
${true} | ${'hides'}
${false} | ${'shows'}
`('$label when $loading is true', ({ loading }) => {
factory(createCommitData(), loading);
expect(vm.isEmpty()).toBe(loading);
});
it('renders commit widget', () => {
factory();
expect(vm.element).toMatchSnapshot();
});
it('renders short commit ID', () => {
factory();
expect(vm.find('.label-monospace').text()).toEqual('12345678');
});
it('hides pipeline components when pipeline does not exist', () => {
factory(createCommitData({ pipeline: null }));
expect(vm.find('.js-commit-pipeline').exists()).toBe(false);
});
it('hides author component when author does not exist', () => {
factory(createCommitData({ author: null }));
expect(vm.find('.js-user-link').exists()).toBe(false);
expect(vm.find(UserAvatarLink).exists()).toBe(false);
});
it('does not render description expander when description is null', () => {
factory(createCommitData({ description: null }));
expect(vm.find('.text-expander').exists()).toBe(false);
expect(vm.find('.commit-row-description').exists()).toBe(false);
});
it('expands commit description when clicking expander', () => {
factory(createCommitData({ description: 'Test description' }));
vm.find('.text-expander').vm.$emit('click');
expect(vm.find('.commit-row-description').isVisible()).toBe(true);
expect(vm.find('.text-expander').classes('open')).toBe(true);
});
});