Commit e4d99980 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Merge branch '322456-add-jira-status-to-sidebar-on-jira-issue-details-page' into 'master'

Add Jira status to sidebar on Jira issue details page

See merge request gitlab-org/gitlab!56065
parents 876f3c35 c6e79e1d
<script>
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { __ } from '~/locale';
export default {
directives: {
GlTooltip: GlTooltipDirective,
},
components: {
GlIcon,
},
props: {
icon: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
value: {
type: String,
required: false,
default: null,
},
},
computed: {
tooltipProps() {
return {
boundary: 'viewport',
placement: 'left',
title: this.value || this.title,
};
},
valueWithFallback() {
return this.value || this.$options.i18n.none;
},
valueClass() {
return {
'no-value': !this.value,
};
},
},
i18n: {
none: __('None'),
},
};
</script>
<template>
<div class="block">
<div v-gl-tooltip="tooltipProps" class="sidebar-collapsed-icon" data-testid="field-collapsed">
<gl-icon :name="icon" />
</div>
<div class="hide-collapsed">
<div class="title" data-testid="field-title">{{ title }}</div>
<div class="value">
<span :class="valueClass" data-testid="field-value">{{ valueWithFallback }}</span>
</div>
</div>
</div>
</template>
<script> <script>
import { labelsFilterParam } from 'ee/integrations/jira/issues_show/constants'; import { labelsFilterParam } from 'ee/integrations/jira/issues_show/constants';
import { __ } from '~/locale';
import LabelsSelect from '~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue'; import LabelsSelect from '~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue';
import Assignee from './assignee.vue'; import Assignee from './assignee.vue';
import IssueDueDate from './issue_due_date.vue'; import IssueDueDate from './issue_due_date.vue';
import IssueField from './issue_field.vue';
import IssueReference from './issue_reference.vue'; import IssueReference from './issue_reference.vue';
export default { export default {
...@@ -11,6 +14,7 @@ export default { ...@@ -11,6 +14,7 @@ export default {
components: { components: {
Assignee, Assignee,
IssueDueDate, IssueDueDate,
IssueField,
IssueReference, IssueReference,
LabelsSelect, LabelsSelect,
}, },
...@@ -39,6 +43,9 @@ export default { ...@@ -39,6 +43,9 @@ export default {
}, },
}, },
labelsFilterParam, labelsFilterParam,
i18n: {
statusTitle: __('Status'),
},
}; };
</script> </script>
...@@ -47,6 +54,7 @@ export default { ...@@ -47,6 +54,7 @@ export default {
<assignee class="block" :assignee="assignee" /> <assignee class="block" :assignee="assignee" />
<issue-due-date :due-date="issue.dueDate" /> <issue-due-date :due-date="issue.dueDate" />
<issue-field icon="progress" :title="$options.i18n.statusTitle" :value="issue.status" />
<labels-select <labels-select
:selected-labels="issue.labels" :selected-labels="issue.labels"
......
import { GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import IssueField from 'ee/integrations/jira/issues_show/components/sidebar/issue_field.vue';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
describe('IssueField', () => {
let wrapper;
const defaultProps = {
icon: 'calendar',
title: 'Field Title',
};
const createComponent = ({ props = {} } = {}) => {
wrapper = extendedWrapper(
shallowMount(IssueField, {
propsData: { ...defaultProps, ...props },
directives: {
GlTooltip: createMockDirective(),
},
}),
);
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const findFieldTitle = () => wrapper.findByTestId('field-title');
const findFieldValue = () => wrapper.findByTestId('field-value');
const findFieldCollapsed = () => wrapper.findByTestId('field-collapsed');
const findFieldCollapsedTooltip = () => getBinding(findFieldCollapsed().element, 'gl-tooltip');
const findGlIcon = () => wrapper.findComponent(GlIcon);
it('renders title', () => {
createComponent();
expect(findFieldTitle().text()).toBe(defaultProps.title);
});
it('renders GlIcon (when collapsed)', () => {
createComponent();
expect(findGlIcon().props('name')).toBe(defaultProps.icon);
});
describe('without value prop', () => {
beforeEach(() => {
createComponent();
});
it('renders fallback value with "no-value" class', () => {
expect(findFieldValue().text()).toBe('None');
});
it('renders tooltip (when collapsed) with "value" = title', () => {
const tooltip = findFieldCollapsedTooltip();
expect(tooltip).toBeDefined();
expect(tooltip.value.title).toBe(defaultProps.title);
});
});
describe('with value prop', () => {
const value = 'field value';
beforeEach(() => {
createComponent({
props: { value },
});
});
it('renders value', () => {
expect(findFieldValue().text()).toBe(value);
});
it('renders tooltip (when collapsed) with "value" = value', () => {
const tooltip = findFieldCollapsedTooltip();
expect(tooltip).toBeDefined();
expect(tooltip.value.title).toBe(value);
});
});
});
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import Assignee from 'ee/integrations/jira/issues_show/components/sidebar/assignee.vue'; import Assignee from 'ee/integrations/jira/issues_show/components/sidebar/assignee.vue';
import IssueDueDate from 'ee/integrations/jira/issues_show/components/sidebar/issue_due_date.vue'; import IssueDueDate from 'ee/integrations/jira/issues_show/components/sidebar/issue_due_date.vue';
import IssueField from 'ee/integrations/jira/issues_show/components/sidebar/issue_field.vue';
import IssueReference from 'ee/integrations/jira/issues_show/components/sidebar/issue_reference.vue'; import IssueReference from 'ee/integrations/jira/issues_show/components/sidebar/issue_reference.vue';
import Sidebar from 'ee/integrations/jira/issues_show/components/sidebar/jira_issues_sidebar_root.vue'; import Sidebar from 'ee/integrations/jira/issues_show/components/sidebar/jira_issues_sidebar_root.vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
...@@ -33,6 +34,7 @@ describe('JiraIssuesSidebar', () => { ...@@ -33,6 +34,7 @@ describe('JiraIssuesSidebar', () => {
const findLabelsSelect = () => wrapper.findComponent(LabelsSelect); const findLabelsSelect = () => wrapper.findComponent(LabelsSelect);
const findAssignee = () => wrapper.findComponent(Assignee); const findAssignee = () => wrapper.findComponent(Assignee);
const findIssueDueDate = () => wrapper.findComponent(IssueDueDate); const findIssueDueDate = () => wrapper.findComponent(IssueDueDate);
const findIssueField = () => wrapper.findComponent(IssueField);
const findIssueReference = () => wrapper.findComponent(IssueReference); const findIssueReference = () => wrapper.findComponent(IssueReference);
it('renders Labels block', () => { it('renders Labels block', () => {
...@@ -55,6 +57,15 @@ describe('JiraIssuesSidebar', () => { ...@@ -55,6 +57,15 @@ describe('JiraIssuesSidebar', () => {
expect(dueDate.props('dueDate')).toBe(mockJiraIssue.dueDate); expect(dueDate.props('dueDate')).toBe(mockJiraIssue.dueDate);
}); });
it('renders IssueField', () => {
createComponent();
const field = findIssueField();
expect(field.props('icon')).toBe('progress');
expect(field.props('title')).toBe('Status');
expect(field.props('value')).toBe(mockJiraIssue.status);
});
describe('when references.relative is null', () => { describe('when references.relative is null', () => {
it('does not render IssueReference', () => { it('does not render IssueReference', () => {
createComponent({ createComponent({
......
...@@ -28,4 +28,5 @@ export const mockJiraIssue = { ...@@ -28,4 +28,5 @@ export const mockJiraIssue = {
relative: 'FE-2', relative: 'FE-2',
}, },
state: 'opened', state: 'opened',
status: 'In Progress',
}; };
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