Commit b6e70c8a authored by Kushal Pandya's avatar Kushal Pandya

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

Extract shared timeline entry component

See merge request gitlab-org/gitlab-ce!23447
parents df027849 e3bddb62
...@@ -4,6 +4,7 @@ import { mapActions, mapGetters, mapState } from 'vuex'; ...@@ -4,6 +4,7 @@ import { mapActions, mapGetters, mapState } from 'vuex';
import _ from 'underscore'; import _ from 'underscore';
import Autosize from 'autosize'; import Autosize from 'autosize';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import Flash from '../../flash'; import Flash from '../../flash';
import Autosave from '../../autosave'; import Autosave from '../../autosave';
import { import {
...@@ -30,6 +31,7 @@ export default { ...@@ -30,6 +31,7 @@ export default {
markdownField, markdownField,
userAvatarLink, userAvatarLink,
loadingButton, loadingButton,
TimelineEntryItem,
}, },
mixins: [issuableStateMixin], mixins: [issuableStateMixin],
props: { props: {
...@@ -309,9 +311,8 @@ Please check your network connection and try again.`; ...@@ -309,9 +311,8 @@ Please check your network connection and try again.`;
<div> <div>
<note-signed-out-widget v-if="!isLoggedIn" /> <note-signed-out-widget v-if="!isLoggedIn" />
<discussion-locked-widget v-else-if="!canCreateNote" :issuable-type="issuableTypeTitle" /> <discussion-locked-widget v-else-if="!canCreateNote" :issuable-type="issuableTypeTitle" />
<div v-else-if="canCreateNote" class="notes notes-form timeline"> <ul v-else-if="canCreateNote" class="notes notes-form timeline">
<div class="timeline-entry note-form"> <timeline-entry-item class="note-form">
<div class="timeline-entry-inner">
<div class="flash-container error-alert timeline-content"></div> <div class="flash-container error-alert timeline-content"></div>
<div class="timeline-icon d-none d-sm-none d-md-block"> <div class="timeline-icon d-none d-sm-none d-md-block">
<user-avatar-link <user-avatar-link
...@@ -438,8 +439,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown" ...@@ -438,8 +439,7 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
</div> </div>
</form> </form>
</div> </div>
</div> </timeline-entry-item>
</div> </ul>
</div>
</div> </div>
</template> </template>
...@@ -6,6 +6,7 @@ import { truncateSha } from '~/lib/utils/text_utility'; ...@@ -6,6 +6,7 @@ import { truncateSha } from '~/lib/utils/text_utility';
import { s__, __, sprintf } from '~/locale'; import { s__, __, sprintf } from '~/locale';
import systemNote from '~/vue_shared/components/notes/system_note.vue'; import systemNote from '~/vue_shared/components/notes/system_note.vue';
import icon from '~/vue_shared/components/icon.vue'; import icon from '~/vue_shared/components/icon.vue';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import Flash from '../../flash'; import Flash from '../../flash';
import { SYSTEM_NOTE } from '../constants'; import { SYSTEM_NOTE } from '../constants';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
...@@ -37,6 +38,7 @@ export default { ...@@ -37,6 +38,7 @@ export default {
placeholderNote, placeholderNote,
placeholderSystemNote, placeholderSystemNote,
systemNote, systemNote,
TimelineEntryItem,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
...@@ -301,8 +303,7 @@ Please check your network connection and try again.`; ...@@ -301,8 +303,7 @@ Please check your network connection and try again.`;
</script> </script>
<template> <template>
<li class="note note-discussion timeline-entry" :class="componentClassName"> <timeline-entry-item class="note note-discussion" :class="componentClassName">
<div class="timeline-entry-inner">
<div class="timeline-content"> <div class="timeline-content">
<div :data-discussion-id="discussion.id" class="discussion js-discussion-container"> <div :data-discussion-id="discussion.id" class="discussion js-discussion-container">
<div v-if="shouldRenderDiffs" class="discussion-header note-wrapper"> <div v-if="shouldRenderDiffs" class="discussion-header note-wrapper">
...@@ -405,11 +406,7 @@ Please check your network connection and try again.`; ...@@ -405,11 +406,7 @@ Please check your network connection and try again.`;
class="btn btn-default mr-sm-2" class="btn btn-default mr-sm-2"
@click="resolveHandler();" @click="resolveHandler();"
> >
<i <i v-if="isResolving" aria-hidden="true" class="fa fa-spinner fa-spin"></i>
v-if="isResolving"
aria-hidden="true"
class="fa fa-spinner fa-spin"
></i>
{{ resolveButtonTitle }} {{ resolveButtonTitle }}
</button> </button>
</div> </div>
...@@ -457,6 +454,5 @@ Please check your network connection and try again.`; ...@@ -457,6 +454,5 @@ Please check your network connection and try again.`;
</div> </div>
</div> </div>
</div> </div>
</div> </timeline-entry-item>
</li>
</template> </template>
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import $ from 'jquery'; import $ from 'jquery';
import { mapGetters, mapActions } from 'vuex'; import { mapGetters, mapActions } from 'vuex';
import { escape } from 'underscore'; import { escape } from 'underscore';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import Flash from '../../flash'; import Flash from '../../flash';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import noteHeader from './note_header.vue'; import noteHeader from './note_header.vue';
...@@ -18,6 +19,7 @@ export default { ...@@ -18,6 +19,7 @@ export default {
noteHeader, noteHeader,
noteActions, noteActions,
noteBody, noteBody,
TimelineEntryItem,
}, },
mixins: [noteable, resolvable], mixins: [noteable, resolvable],
props: { props: {
...@@ -169,14 +171,13 @@ export default { ...@@ -169,14 +171,13 @@ export default {
</script> </script>
<template> <template>
<li <timeline-entry-item
:id="noteAnchorId" :id="noteAnchorId"
:class="classNameBindings" :class="classNameBindings"
:data-award-url="note.toggle_award_path" :data-award-url="note.toggle_award_path"
:data-note-id="note.id" :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"> <div v-once class="timeline-icon">
<user-avatar-link <user-avatar-link
:link-href="author.path" :link-href="author.path"
...@@ -225,6 +226,5 @@ export default { ...@@ -225,6 +226,5 @@ export default {
@cancelForm="formCancelHandler" @cancelForm="formCancelHandler"
/> />
</div> </div>
</div> </timeline-entry-item>
</li>
</template> </template>
...@@ -17,12 +17,14 @@ ...@@ -17,12 +17,14 @@
* /> * />
*/ */
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import userAvatarLink from '../user_avatar/user_avatar_link.vue'; import userAvatarLink from '../user_avatar/user_avatar_link.vue';
export default { export default {
name: 'PlaceholderNote', name: 'PlaceholderNote',
components: { components: {
userAvatarLink, userAvatarLink,
TimelineEntryItem,
}, },
props: { props: {
note: { note: {
...@@ -37,8 +39,7 @@ export default { ...@@ -37,8 +39,7 @@ export default {
</script> </script>
<template> <template>
<li class="note being-posted fade-in-half timeline-entry"> <timeline-entry-item class="note being-posted fade-in-half">
<div class="timeline-entry-inner">
<div class="timeline-icon"> <div class="timeline-icon">
<user-avatar-link <user-avatar-link
:link-href="getUserData.path" :link-href="getUserData.path"
...@@ -61,6 +62,5 @@ export default { ...@@ -61,6 +62,5 @@ export default {
</div> </div>
</div> </div>
</div> </div>
</div> </timeline-entry-item>
</li>
</template> </template>
<script> <script>
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
/** /**
* Common component to render a placeholder system note. * Common component to render a placeholder system note.
* *
...@@ -9,6 +11,9 @@ ...@@ -9,6 +11,9 @@
*/ */
export default { export default {
name: 'PlaceholderSystemNote', name: 'PlaceholderSystemNote',
components: {
TimelineEntryItem,
},
props: { props: {
note: { note: {
type: Object, type: Object,
...@@ -19,11 +24,9 @@ export default { ...@@ -19,11 +24,9 @@ export default {
</script> </script>
<template> <template>
<li class="note system-note timeline-entry being-posted fade-in-half"> <timeline-entry-item class="note system-note being-posted fade-in-half">
<div class="timeline-entry-inner">
<div class="timeline-content"> <div class="timeline-content">
<em>{{ note.body }}</em> <em>{{ note.body }}</em>
</div> </div>
</div> </timeline-entry-item>
</li>
</template> </template>
<script> <script>
import { GlSkeletonLoading } from '@gitlab/ui'; import { GlSkeletonLoading } from '@gitlab/ui';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
export default { export default {
name: 'SkeletonNote', name: 'SkeletonNote',
components: { components: {
GlSkeletonLoading, GlSkeletonLoading,
TimelineEntryItem,
}, },
}; };
</script> </script>
<template> <template>
<li class="timeline-entry note note-wrapper"> <timeline-entry-item class="note note-wrapper">
<div class="timeline-entry-inner">
<div class="timeline-icon"></div> <div class="timeline-icon"></div>
<div class="timeline-content"> <div class="timeline-content">
<div class="note-header"></div> <div class="note-header"></div>
<div class="note-body"><gl-skeleton-loading /></div> <div class="note-body"><gl-skeleton-loading /></div>
</div> </div>
</div> </timeline-entry-item>
</li>
</template> </template>
...@@ -20,6 +20,7 @@ import $ from 'jquery'; ...@@ -20,6 +20,7 @@ import $ from 'jquery';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import noteHeader from '~/notes/components/note_header.vue'; import noteHeader from '~/notes/components/note_header.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import TimelineEntryItem from './timeline_entry_item.vue';
import { spriteIcon } from '../../../lib/utils/common_utils'; import { spriteIcon } from '../../../lib/utils/common_utils';
const MAX_VISIBLE_COMMIT_LIST_COUNT = 3; const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
...@@ -29,6 +30,7 @@ export default { ...@@ -29,6 +30,7 @@ export default {
components: { components: {
Icon, Icon,
noteHeader, noteHeader,
TimelineEntryItem,
}, },
props: { props: {
note: { note: {
...@@ -73,12 +75,11 @@ export default { ...@@ -73,12 +75,11 @@ export default {
</script> </script>
<template> <template>
<li <timeline-entry-item
:id="noteAnchorId" :id="noteAnchorId"
:class="{ target: isTargetNote }" :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-icon" v-html="iconHtml"></div>
<div class="timeline-content"> <div class="timeline-content">
<div class="note-header"> <div class="note-header">
...@@ -103,6 +104,5 @@ export default { ...@@ -103,6 +104,5 @@ export default {
</div> </div>
</div> </div>
</div> </div>
</div> </timeline-entry-item>
</li>
</template> </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