Render tree last commit widget with Vue
Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/62766
This commit is contained in:
parent
8716eb0cef
commit
bf6b70662b
6 changed files with 397 additions and 0 deletions
145
app/assets/javascripts/repository/components/last_commit.vue
Normal file
145
app/assets/javascripts/repository/components/last_commit.vue
Normal 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>
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9140,6 +9140,9 @@ msgstr ""
|
|||
msgid "Show comments only"
|
||||
msgstr ""
|
||||
|
||||
msgid "Show commit description"
|
||||
msgstr ""
|
||||
|
||||
msgid "Show complete raw log"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -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>
|
||||
`;
|
103
spec/frontend/repository/components/last_commit_spec.js
Normal file
103
spec/frontend/repository/components/last_commit_spec.js
Normal 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);
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue