Commit e012f293 authored by Tom Quirk's avatar Tom Quirk

Add Jira issue Note component

Adds a barebones, readonly, gitlab-ui-powered version
of `noteable_note.vue`. The only current usecase is
Jira issue comments.
parent aa8384f1
<template>
<div class="timeline-icon"><slot></slot></div>
</template>
<script>
import { GlAvatarLink, GlAvatar, GlLink, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import TimelineIcon from '~/vue_shared/components/notes/timeline_icon.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
export default {
components: {
GlAvatarLink,
GlAvatar,
GlLink,
TimelineEntryItem,
TimelineIcon,
TimeAgoTooltip,
},
directives: {
SafeHtml,
},
props: {
authorAvatarUrl: {
type: String,
required: true,
},
authorWebUrl: {
type: String,
required: true,
},
authorName: {
type: String,
required: true,
},
noteBodyHtml: {
type: String,
required: true,
},
noteCreatedAt: {
type: String,
required: true,
},
authorUsername: {
type: String,
required: false,
default: undefined,
},
},
computed: {
noteAnchor() {
return `#${this.$attrs.id || ''}`;
},
},
};
</script>
<template>
<timeline-entry-item class="gl-p-5">
<timeline-icon class="gl-mr-5 gl-ml-2">
<gl-avatar-link target="_blank" :href="authorWebUrl">
<gl-avatar :size="32" :src="authorAvatarUrl" :alt="authorName" />
</gl-avatar-link>
</timeline-icon>
<div>
<div class="gl-display-flex gl-justify-content-space-between">
<div class="gl-display-flex gl-align-items-center gl-mb-3">
<gl-link
:href="authorWebUrl"
class="gl-text-black-normal gl-font-weight-bold gl-white-space-nowrap gl-mr-2"
>
{{ authorName }}
</gl-link>
<gl-link
v-if="authorUsername"
:href="authorWebUrl"
class="gl-text-gray-500 gl-mr-2 gl-white-space-nowrap"
data-testid="author-username"
>
@{{ authorUsername }}
</gl-link>
<span class="gl-text-gray-500 gl-mr-2">·</span>
<gl-link class="gl-text-gray-500" :href="noteAnchor" data-testid="time-ago-link">
<time-ago-tooltip :time="noteCreatedAt" tooltip-placement="bottom" />
</gl-link>
</div>
<div data-testid="badges-container">
<slot name="badges"></slot>
</div>
</div>
<div>
<div class="gl-overflow-x-auto gl-overflow-y-hidden">
<div v-safe-html="noteBodyHtml" class="md"></div>
</div>
</div>
</div>
</timeline-entry-item>
</template>
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`JiraIssuesNote template renders note 1`] = `
"<timeline-entry-item-stub class=\\"gl-p-5\\">
<timeline-icon-stub class=\\"gl-mr-5 gl-ml-2\\">
<gl-avatar-link-stub target=\\"_blank\\" href=\\"http://127.0.0.1:3000/root\\">
<gl-avatar-stub entityid=\\"0\\" entityname=\\"\\" src=\\"http://127.0.0.1:3000/uploads/-/system/user/avatar/1/avatar.png?width=90\\" alt=\\"Justin Ho\\" size=\\"32\\" shape=\\"circle\\"></gl-avatar-stub>
</gl-avatar-link-stub>
</timeline-icon-stub>
<div>
<div class=\\"gl-display-flex gl-justify-content-space-between\\">
<div class=\\"gl-display-flex gl-align-items-center gl-mb-3\\">
<gl-link-stub href=\\"http://127.0.0.1:3000/root\\" class=\\"gl-text-black-normal gl-font-weight-bold gl-white-space-nowrap gl-mr-2\\">
Justin Ho
</gl-link-stub>
<!----> <span class=\\"gl-text-gray-500 gl-mr-2\\">·</span>
<gl-link-stub href=\\"#\\" data-testid=\\"time-ago-link\\" class=\\"gl-text-gray-500\\">
<time-ago-tooltip-stub time=\\"&quot;2021-02-01T04:04:40.833Z&quot;\\" tooltipplacement=\\"bottom\\" cssclass=\\"\\"></time-ago-tooltip-stub>
</gl-link-stub>
</div>
<div data-testid=\\"badges-container\\"></div>
</div>
<div>
<div class=\\"gl-overflow-x-auto gl-overflow-y-hidden\\">
<div class=\\"md\\">
<p>hi</p>
</div>
</div>
</div>
</div>
</timeline-entry-item-stub>"
`;
import { shallowMount } from '@vue/test-utils';
import JiraIssueNote from 'ee/integrations/jira/issues_show/components/note.vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { mockJiraIssueComment } from '../mock_data';
describe('JiraIssuesNote', () => {
let wrapper;
const findTimeAgoLink = () => wrapper.findByTestId('time-ago-link');
const findBadgesContainer = () => wrapper.findByTestId('badges-container');
const findAuthorUsernameLink = () => wrapper.findByTestId('author-username');
const createComponent = ({ props, slots } = {}) => {
wrapper = extendedWrapper(
shallowMount(JiraIssueNote, {
propsData: {
authorName: mockJiraIssueComment.author.name,
authorWebUrl: mockJiraIssueComment.author.web_url,
authorAvatarUrl: mockJiraIssueComment.author.avatar_url,
noteCreatedAt: mockJiraIssueComment.created_at,
noteBodyHtml: mockJiraIssueComment.body_html,
...props,
},
slots,
}),
);
};
afterEach(() => {
wrapper.destroy();
});
describe('template', () => {
it('renders note', () => {
createComponent();
expect(wrapper.html()).toMatchSnapshot();
});
it.each`
id | expectedTimeAgoHref
${undefined} | ${'#'}
${'1234'} | ${'#1234'}
`(
'sets "time ago" link to $expectedTimeAgoHref when id is $id',
({ id, expectedTimeAgoHref }) => {
createComponent({ props: { id } });
expect(findTimeAgoLink().attributes('href')).toBe(expectedTimeAgoHref);
},
);
describe('with badge slot', () => {
it('renders slot content', () => {
createComponent({ slots: { badges: 'testing badges content' } });
expect(findBadgesContainer().html()).toContain('testing badges content');
});
});
describe('with author username', () => {
it('renders slot content', () => {
createComponent({ props: { authorUsername: 'testuser' } });
expect(findAuthorUsernameLink().html()).toContain('testuser');
});
});
});
});
......@@ -29,3 +29,14 @@ export const mockJiraIssue = {
},
state: 'opened',
};
export const mockJiraIssueComment = {
body_html: '<p>hi</p>',
created_at: '"2021-02-01T04:04:40.833Z"',
author: {
name: 'Justin Ho',
web_url: 'http://127.0.0.1:3000/root',
avatar_url: 'http://127.0.0.1:3000/uploads/-/system/user/avatar/1/avatar.png?width=90',
},
id: 10000,
};
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