Commit 0cc65a26 authored by charlie ablett's avatar charlie ablett

Merge branch 'justin_ho-jira-issues-show-add-mock-data' into 'master'

Add Jira issues show mock endpoint and add basic frontend structure

See merge request gitlab-org/gitlab!52792
parents 26043c95 fb4ff4c0
......@@ -35,11 +35,13 @@ export default {
},
allowLabelEdit: {
type: Boolean,
required: true,
required: false,
default: false,
},
allowLabelCreate: {
type: Boolean,
required: true,
required: false,
default: false,
},
allowMultiselect: {
type: Boolean,
......@@ -48,7 +50,8 @@ export default {
},
allowScopedLabels: {
type: Boolean,
required: true,
required: false,
default: false,
},
variant: {
type: String,
......
import axios from '~/lib/utils/axios_utils';
export const fetchIssue = async (issuePath) => {
return axios.get(issuePath).then(({ data }) => {
return data;
});
};
<script>
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';
import IssuableShow from '~/issuable_show/components/issuable_show_root.vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
export default {
name: 'JiraIssuesShow',
components: {
IssuableShow,
Sidebar,
},
inject: {
issuesShowPath: {
default: '',
},
},
data() {
return {
isLoading: true,
issue: {},
};
},
computed: {
isIssueOpen() {
return this.issue.state === issueStates.OPENED;
},
statusBadgeClass() {
return this.isIssueOpen ? 'status-box-open' : 'status-box-issue-closed';
},
statusBadgeText() {
return issueStateLabels[this.issue.state];
},
},
async mounted() {
this.issue = convertObjectPropsToCamelCase(await fetchIssue(this.issuesShowPath), {
deep: true,
});
this.isLoading = false;
},
};
</script>
<template>
<div></div>
<div>
<issuable-show
v-if="!isLoading"
:issuable="issue"
:enable-edit="false"
:status-badge-class="statusBadgeClass"
>
<template #status-badge>{{ statusBadgeText }}</template>
<template #right-sidebar-items="{ sidebarExpanded }">
<sidebar :sidebar-expanded="sidebarExpanded" :selected-labels="issue.labels" />
</template>
</issuable-show>
</div>
</template>
<script>
import LabelsSelect from '~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue';
export default {
components: {
LabelsSelect,
},
props: {
sidebarExpanded: {
type: Boolean,
required: true,
},
selectedLabels: {
type: Array,
required: true,
},
},
};
</script>
<template>
<labels-select
:selected-labels="selectedLabels"
variant="sidebar"
class="block labels js-labels-block"
>{{ __('None') }}</labels-select
>
</template>
import { __ } from '~/locale';
export const issueStates = {
OPENED: 'opened',
CLOSED: 'closed',
};
export const issueStateLabels = {
[issueStates.OPENED]: __('Open'),
[issueStates.CLOSED]: __('Closed'),
};
......@@ -9,8 +9,13 @@ export default function initJiraIssueShow({ mountPointSelector }) {
return null;
}
const { issuesShowPath } = mountPointEl.dataset;
return new Vue({
el: mountPointEl,
provide: {
issuesShowPath,
},
render: (createElement) => createElement(JiraIssuesShowApp),
});
}
......@@ -32,6 +32,15 @@ module Projects
end
end
def show
respond_to do |format|
format.html
format.json do
render json: issue_json
end
end
end
private
def issues_json
......@@ -46,6 +55,30 @@ module Projects
.represent(jira_issues, project: project)
end
def issue_json
{
title_html: '<a href="https://jira.reali.sh:8080/projects/FE/issues/FE-2">FE-2</a> The second FE issue on Jira',
description_html: '<a href="https://jira.reali.sh:8080/projects/FE/issues/FE-2">FE-2</a> The second FE issue on Jira',
created_at: 2.hours.ago,
author: {
id: 2,
username: 'justin_ho',
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'
},
labels: [
{
title: 'In Progress',
description: 'Work that is still in progress',
color: '#EBECF0',
text_color: '#283856'
}
],
state: 'opened'
}
end
def finder
@finder ||= ::Projects::Integrations::Jira::IssuesFinder.new(project, finder_options)
end
......
......@@ -50,5 +50,11 @@ module EE
def escaped_form_authenticity_token
CGI.escape(form_authenticity_token)
end
def jira_issues_show_data
{
issues_show_path: project_integrations_jira_issue_path(@project, params[:id], format: :json)
}
end
end
end
.js-jira-issues-show-app
- add_to_breadcrumbs _('Jira Issues'), project_integrations_jira_issues_path(@project)
- page_title 'Jira issue'
.js-jira-issues-show-app{ data: jira_issues_show_data }
......@@ -194,8 +194,21 @@ RSpec.describe Projects::Integrations::Jira::IssuesController do
it 'renders `show` template' do
get :show, params: { namespace_id: project.namespace, project_id: project, id: 1 }
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:show)
end
it 'returns JSON response' do
get :show, params: { namespace_id: project.namespace, project_id: project, id: 1, format: :json }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to include(
'title_html',
'description_html',
'author',
'labels'
)
end
end
end
end
import { shallowMount } from '@vue/test-utils';
import JiraIssuesShow from 'ee/integrations/jira/issues_show/components/jira_issues_show_root.vue';
import IssuableShow from '~/issuable_show/components/issuable_show_root.vue';
import { mockJiraIssue } from '../mock_data';
jest.mock('ee/integrations/jira/issues_show/api', () => ({
fetchIssue: jest.fn().mockImplementation(() => mockJiraIssue),
}));
describe('JiraIssuesShow', () => {
let wrapper;
const createComponent = () => {
wrapper = shallowMount(JiraIssuesShow, {
stubs: {
IssuableShow,
},
});
};
afterEach(() => {
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
});
const findIssuableShow = () => wrapper.findComponent(IssuableShow);
it('renders IssuableShow', async () => {
createComponent();
await wrapper.vm.$nextTick();
expect(findIssuableShow().exists()).toBe(true);
});
});
import { shallowMount } from '@vue/test-utils';
import Sidebar from 'ee/integrations/jira/issues_show/components/sidebar.vue';
import LabelsSelect from '~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue';
import { mockJiraIssue } from '../mock_data';
describe('Sidebar', () => {
let wrapper;
const defaultProps = {
sidebarExpanded: false,
selectedLabels: mockJiraIssue.labels,
};
const createComponent = () => {
wrapper = shallowMount(Sidebar, {
propsData: defaultProps,
});
};
afterEach(() => {
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
});
const findLabelsSelect = () => wrapper.findComponent(LabelsSelect);
it('renders LabelsSelect', async () => {
createComponent();
await wrapper.vm.$nextTick();
expect(findLabelsSelect().exists()).toBe(true);
});
});
export const mockJiraIssue = {
title_html:
'<a href="https://jira.reali.sh:8080/projects/FE/issues/FE-2">FE-2</a> The second FE issue on Jira',
description_html:
'<a href="https://jira.reali.sh:8080/projects/FE/issues/FE-2">FE-2</a> The second FE issue on Jira',
created_at: '"2021-02-01T04:04:40.833Z"',
author: {
id: 2,
username: 'justin_ho',
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',
},
labels: [
{
title: 'In Progress',
description: 'Work that is still in progress',
color: '#EBECF0',
text_color: '#283856',
},
],
state: 'opened',
};
......@@ -87,4 +87,17 @@ RSpec.describe EE::ServicesHelper do
expect(slack_link).to include('redirect_uri=http://some-path/project/1&state=a+token')
end
end
describe '#jira_issues_show_data' do
subject { helper.jira_issues_show_data }
before do
allow(helper).to receive(:params).and_return({ id: 'FE-1' })
assign(:project, project)
end
it 'includes Jira issues show data' do
is_expected.to include(:issues_show_path)
end
end
end
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