Commit 04d1569c authored by Tom Quirk's avatar Tom Quirk

Add GlAlert with link to Jira issue

- adds test coverage for various properties of issueble-show-root
- fall back to title when titleHtml doesn't exist
- adds issue status icon (for small screen sizes)
parent 8f792928
......@@ -54,7 +54,7 @@ export default {
<template>
<div>
<div class="title-container">
<h2 v-safe-html="issuable.titleHtml" class="title qa-title" dir="auto"></h2>
<h2 v-safe-html="issuable.titleHtml || issuable.title" class="title qa-title" dir="auto"></h2>
<gl-button
v-if="enableEdit"
v-gl-tooltip.bottom
......
<script>
import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import { fetchIssue } from 'ee/integrations/jira/issues_show/api';
import { issueStates, issueStateLabels } from 'ee/integrations/jira/issues_show/constants';
import Sidebar from 'ee/integrations/jira/issues_show/components/sidebar.vue';
......@@ -9,6 +10,9 @@ import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
export default {
name: 'JiraIssuesShow',
components: {
GlAlert,
GlSprintf,
GlLink,
IssuableShow,
Sidebar,
},
......@@ -33,6 +37,9 @@ export default {
statusBadgeText() {
return issueStateLabels[this.issue.state];
},
statusIcon() {
return this.isIssueOpen ? 'issue-open-m' : 'mobile-issue-close';
},
},
async mounted() {
this.issue = convertObjectPropsToCamelCase(await fetchIssue(this.issuesShowPath), {
......@@ -44,12 +51,32 @@ export default {
</script>
<template>
<div>
<div class="gl-mt-5">
<gl-alert
variant="info"
:dismissible="false"
:title="s__('JiraService|This issue is synchronized with Jira')"
class="gl-mb-2"
>
<gl-sprintf
:message="
s__(
`JiraService|Not all data may be displayed here. To view more details or make changes to this issue, go to %{linkStart}Jira%{linkEnd}.`,
)
"
>
<template #link="{ content }">
<gl-link :href="issue.webUrl" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</gl-alert>
<issuable-show
v-if="!isLoading"
:issuable="issue"
:enable-edit="false"
:status-badge-class="statusBadgeClass"
:status-icon="statusIcon"
>
<template #status-badge>{{ statusBadgeText }}</template>
......
import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import waitForPromises from 'helpers/wait_for_promises';
import JiraIssuesShow from 'ee/integrations/jira/issues_show/components/jira_issues_show_root.vue';
import { issueStates } from 'ee/integrations/jira/issues_show/constants';
import IssuableShow from '~/issuable_show/components/issuable_show_root.vue';
import IssuableHeader from '~/issuable_show/components/issuable_header.vue';
import { mockJiraIssue } from '../mock_data';
jest.mock('ee/integrations/jira/issues_show/api', () => ({
fetchIssue: jest.fn().mockImplementation(() => mockJiraIssue),
}));
const mockJiraIssuesShowPath = 'jira_issues_show_path';
describe('JiraIssuesShow', () => {
let wrapper;
let mockAxios;
const findIssuableShow = () => wrapper.findComponent(IssuableShow);
const findIssuableShowStatusBadge = () =>
wrapper.findComponent(IssuableHeader).find('[data-testid="status"]');
const createComponent = () => {
wrapper = shallowMount(JiraIssuesShow, {
stubs: {
IssuableShow,
IssuableHeader,
},
provide: {
issuesShowPath: mockJiraIssuesShowPath,
},
});
};
beforeEach(() => {
mockAxios = new MockAdapter(axios);
});
afterEach(() => {
mockAxios.restore();
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
});
const findIssuableShow = () => wrapper.findComponent(IssuableShow);
it('renders IssuableShow', async () => {
mockAxios.onGet(mockJiraIssuesShowPath).replyOnce(200, mockJiraIssue);
createComponent();
await waitForPromises();
await wrapper.vm.$nextTick();
expect(findIssuableShow().exists()).toBe(true);
});
describe.each`
state | statusIcon | statusBadgeClass | badgeText
${issueStates.OPENED} | ${'issue-open-m'} | ${'status-box-open'} | ${'Open'}
${issueStates.CLOSED} | ${'mobile-issue-close'} | ${'status-box-issue-closed'} | ${'Closed'}
`('when issue state is `$state`', async ({ state, statusIcon, statusBadgeClass, badgeText }) => {
beforeEach(async () => {
mockAxios.onGet(mockJiraIssuesShowPath).replyOnce(200, { ...mockJiraIssue, state });
createComponent();
await waitForPromises();
await wrapper.vm.$nextTick();
});
it('sets `statusIcon` prop correctly', () => {
expect(findIssuableShow().props('statusIcon')).toBe(statusIcon);
});
it('sets `statusBadgeClass` prop correctly', () => {
expect(findIssuableShow().props('statusBadgeClass')).toBe(statusBadgeClass);
});
it('renders correct status badge text', () => {
expect(findIssuableShowStatusBadge().text()).toBe(badgeText);
});
});
});
......@@ -16676,6 +16676,9 @@ msgstr ""
msgid "JiraService|Jira workflow transition IDs"
msgstr ""
msgid "JiraService|Not all data may be displayed here. To view more details or make changes to this issue, go to %{linkStart}Jira%{linkEnd}."
msgstr ""
msgid "JiraService|Open Jira"
msgstr ""
......@@ -16700,6 +16703,9 @@ msgstr ""
msgid "JiraService|This feature requires a Premium plan."
msgstr ""
msgid "JiraService|This issue is synchronized with Jira"
msgstr ""
msgid "JiraService|Use a password for server version and an API token for cloud version"
msgstr ""
......
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