Commit 9439791d authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'winh-timeline-entry-component-ee' into 'master'

Extract shared timeline entry component (EE-port)

See merge request gitlab-org/gitlab-ee!8647
parents 0642a3d4 85f524f9
......@@ -2,6 +2,7 @@
import $ from 'jquery';
import { mapGetters, mapActions } from 'vuex';
import { escape } from 'underscore';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import Flash from '../../flash';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import noteHeader from './note_header.vue';
......@@ -18,6 +19,7 @@ export default {
noteHeader,
noteActions,
noteBody,
TimelineEntryItem,
},
mixins: [noteable, resolvable],
props: {
......@@ -173,68 +175,66 @@ export default {
</script>
<template>
<li
<timeline-entry-item
:id="noteAnchorId"
:class="classNameBindings"
:data-award-url="note.toggle_award_path"
:data-note-id="note.id"
class="note timeline-entry note-wrapper"
class="note note-wrapper"
>
<div class="timeline-entry-inner">
<div v-once class="timeline-icon">
<user-avatar-link
:link-href="author.path"
:img-src="author.avatar_url"
:img-alt="author.name"
:img-size="40"
<div v-once class="timeline-icon">
<user-avatar-link
:link-href="author.path"
:img-src="author.avatar_url"
:img-alt="author.name"
:img-size="40"
>
<slot slot="avatar-badge" name="avatar-badge"> </slot>
</user-avatar-link>
</div>
<div class="timeline-content">
<div class="note-header">
<note-header
v-once
:author="author"
:created-at="note.created_at"
:note-id="note.id"
action-text="commented"
>
<slot slot="avatar-badge" name="avatar-badge"> </slot>
</user-avatar-link>
</div>
<div class="timeline-content">
<div class="note-header">
<note-header
v-once
:author="author"
:created-at="note.created_at"
:note-id="note.id"
action-text="commented"
>
<slot slot="note-header-info" name="note-header-info"> </slot>
</note-header>
<note-actions
:author-id="author.id"
:note-id="note.id"
:note-url="note.noteable_note_url"
:access-level="note.human_access"
:can-edit="note.current_user.can_edit"
:can-award-emoji="note.current_user.can_award_emoji"
:can-delete="note.current_user.can_edit"
:can-report-as-abuse="canReportAsAbuse"
:can-resolve="
note.current_user.can_resolve || (note.isDraft && note.discussion_id !== null)
"
:report-abuse-path="note.report_abuse_path"
:resolvable="note.resolvable || note.isDraft"
:is-resolved="note.resolved || note.resolve_discussion"
:is-resolving="isResolving"
:resolved-by="note.resolved_by"
:discussion-id="note.isDraft && note.discussion_id"
:resolve-discussion="note.isDraft && note.resolve_discussion"
@handleEdit="editHandler"
@handleDelete="deleteHandler"
@handleResolve="resolveHandler"
/>
</div>
<note-body
ref="noteBody"
:note="note"
<slot slot="note-header-info" name="note-header-info"> </slot>
</note-header>
<note-actions
:author-id="author.id"
:note-id="note.id"
:note-url="note.noteable_note_url"
:access-level="note.human_access"
:can-edit="note.current_user.can_edit"
:is-editing="isEditing"
@handleFormUpdate="formUpdateHandler"
@cancelForm="formCancelHandler"
:can-award-emoji="note.current_user.can_award_emoji"
:can-delete="note.current_user.can_edit"
:can-report-as-abuse="canReportAsAbuse"
:can-resolve="
note.current_user.can_resolve || (note.isDraft && note.discussion_id !== null)
"
:report-abuse-path="note.report_abuse_path"
:resolvable="note.resolvable || note.isDraft"
:is-resolved="note.resolved || note.resolve_discussion"
:is-resolving="isResolving"
:resolved-by="note.resolved_by"
:discussion-id="note.isDraft && note.discussion_id"
:resolve-discussion="note.isDraft && note.resolve_discussion"
@handleEdit="editHandler"
@handleDelete="deleteHandler"
@handleResolve="resolveHandler"
/>
</div>
<note-body
ref="noteBody"
:note="note"
:can-edit="note.current_user.can_edit"
:is-editing="isEditing"
@handleFormUpdate="formUpdateHandler"
@cancelForm="formCancelHandler"
/>
</div>
</li>
</timeline-entry-item>
</template>
......@@ -17,12 +17,14 @@
* />
*/
import { mapGetters } from 'vuex';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import userAvatarLink from '../user_avatar/user_avatar_link.vue';
export default {
name: 'PlaceholderNote',
components: {
userAvatarLink,
TimelineEntryItem,
},
props: {
note: {
......@@ -37,30 +39,28 @@ export default {
</script>
<template>
<li class="note being-posted fade-in-half timeline-entry">
<div class="timeline-entry-inner">
<div class="timeline-icon">
<user-avatar-link
:link-href="getUserData.path"
:img-src="getUserData.avatar_url"
:img-size="40"
/>
</div>
<div :class="{ discussion: !note.individual_note }" class="timeline-content">
<div class="note-header">
<div class="note-header-info">
<a :href="getUserData.path">
<span class="d-none d-sm-inline-block">{{ getUserData.name }}</span>
<span class="note-headline-light">@{{ getUserData.username }}</span>
</a>
</div>
<timeline-entry-item class="note being-posted fade-in-half">
<div class="timeline-icon">
<user-avatar-link
:link-href="getUserData.path"
:img-src="getUserData.avatar_url"
:img-size="40"
/>
</div>
<div :class="{ discussion: !note.individual_note }" class="timeline-content">
<div class="note-header">
<div class="note-header-info">
<a :href="getUserData.path">
<span class="d-none d-sm-inline-block">{{ getUserData.name }}</span>
<span class="note-headline-light">@{{ getUserData.username }}</span>
</a>
</div>
<div class="note-body">
<div class="note-text">
<p>{{ note.body }}</p>
</div>
</div>
<div class="note-body">
<div class="note-text">
<p>{{ note.body }}</p>
</div>
</div>
</div>
</li>
</timeline-entry-item>
</template>
<script>
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
/**
* Common component to render a placeholder system note.
*
......@@ -9,6 +11,9 @@
*/
export default {
name: 'PlaceholderSystemNote',
components: {
TimelineEntryItem,
},
props: {
note: {
type: Object,
......@@ -19,11 +24,9 @@ export default {
</script>
<template>
<li class="note system-note timeline-entry being-posted fade-in-half">
<div class="timeline-entry-inner">
<div class="timeline-content">
<em>{{ note.body }}</em>
</div>
<timeline-entry-item class="note system-note being-posted fade-in-half">
<div class="timeline-content">
<em>{{ note.body }}</em>
</div>
</li>
</timeline-entry-item>
</template>
<script>
import { GlSkeletonLoading } from '@gitlab/ui';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
export default {
name: 'SkeletonNote',
components: {
GlSkeletonLoading,
TimelineEntryItem,
},
};
</script>
<template>
<li class="timeline-entry note note-wrapper">
<div class="timeline-entry-inner">
<div class="timeline-icon"></div>
<div class="timeline-content">
<div class="note-header"></div>
<div class="note-body"><gl-skeleton-loading /></div>
</div>
<timeline-entry-item class="note note-wrapper">
<div class="timeline-icon"></div>
<div class="timeline-content">
<div class="note-header"></div>
<div class="note-body"><gl-skeleton-loading /></div>
</div>
</li>
</timeline-entry-item>
</template>
......@@ -20,6 +20,7 @@ import $ from 'jquery';
import { mapGetters } from 'vuex';
import noteHeader from '~/notes/components/note_header.vue';
import Icon from '~/vue_shared/components/icon.vue';
import TimelineEntryItem from './timeline_entry_item.vue';
import { spriteIcon } from '../../../lib/utils/common_utils';
const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
......@@ -29,6 +30,7 @@ export default {
components: {
Icon,
noteHeader,
TimelineEntryItem,
},
props: {
note: {
......@@ -73,36 +75,34 @@ export default {
</script>
<template>
<li
<timeline-entry-item
:id="noteAnchorId"
:class="{ target: isTargetNote }"
class="note system-note timeline-entry note-wrapper"
class="note system-note note-wrapper"
>
<div class="timeline-entry-inner">
<div class="timeline-icon" v-html="iconHtml"></div>
<div class="timeline-content">
<div class="note-header">
<note-header :author="note.author" :created-at="note.created_at" :note-id="note.id">
<span v-html="actionTextHtml"></span>
</note-header>
</div>
<div class="note-body">
<div
:class="{
'system-note-commit-list': hasMoreCommits,
'hide-shade': expanded,
}"
class="note-text"
v-html="note.note_html"
></div>
<div v-if="hasMoreCommits" class="flex-list">
<div class="system-note-commit-list-toggler flex-row" @click="expanded = !expanded;">
<icon :name="toggleIcon" :size="8" class="append-right-5" />
<span>Toggle commit list</span>
</div>
<div class="timeline-icon" v-html="iconHtml"></div>
<div class="timeline-content">
<div class="note-header">
<note-header :author="note.author" :created-at="note.created_at" :note-id="note.id">
<span v-html="actionTextHtml"></span>
</note-header>
</div>
<div class="note-body">
<div
:class="{
'system-note-commit-list': hasMoreCommits,
'hide-shade': expanded,
}"
class="note-text"
v-html="note.note_html"
></div>
<div v-if="hasMoreCommits" class="flex-list">
<div class="system-note-commit-list-toggler flex-row" @click="expanded = !expanded;">
<icon :name="toggleIcon" :size="8" class="append-right-5" />
<span>Toggle commit list</span>
</div>
</div>
</div>
</div>
</li>
</timeline-entry-item>
</template>
<script>
export default {
name: 'TimelineEntryItem',
};
</script>
<template>
<li class="timeline-entry">
<div class="timeline-entry-inner"><slot></slot></div>
</li>
</template>
import { shallowMount, createLocalVue } from '@vue/test-utils';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
describe(TimelineEntryItem.name, () => {
let wrapper;
const factory = (options = {}) => {
const localVue = createLocalVue();
wrapper = shallowMount(TimelineEntryItem, {
localVue,
...options,
});
};
afterEach(() => {
wrapper.destroy();
});
it('renders correctly', () => {
factory();
expect(wrapper.is('.timeline-entry')).toBe(true);
expect(wrapper.contains('.timeline-entry-inner')).toBe(true);
});
it('accepts default slot', () => {
const dummyContent = '<p>some content</p>';
factory({
slots: {
default: dummyContent,
},
});
const content = wrapper.find('.timeline-entry-inner :first-child');
expect(content.html()).toBe(dummyContent);
});
});
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