Commit 549beacc authored by Eric Eastwood's avatar Eric Eastwood
parent 9e00708f
<script>
import eventHub from '../event_hub';
import issueToken from './issue_token.vue';
import addIssuableForm from './add_issuable_form.vue';
export default {
name: 'RelatedIssuesBlock',
props: {
relatedIssues: {
type: Array,
required: false,
default: () => [],
},
canAddRelatedIssues: {
type: Boolean,
required: false,
default: false,
},
isFormVisible: {
type: Boolean,
required: false,
default: false,
},
pendingRelatedIssues: {
type: Array,
required: false,
default: () => [],
},
inputValue: {
type: String,
required: false,
default: '',
},
helpPath: {
type: String,
required: false,
default: '',
},
},
components: {
addIssuableForm,
issueToken,
},
computed: {
hasRelatedIssues() {
return this.relatedIssues.length > 0;
},
relatedIssueCount() {
return this.relatedIssues.length;
},
hasHelpPath() {
return this.helpPath.length > 0;
},
},
methods: {
showAddRelatedIssuesForm() {
eventHub.$emit('showAddRelatedIssuesForm');
},
},
updated() {
const addIssueButton = this.$refs.issueCountBadgeAddButton;
if (addIssueButton) {
$(addIssueButton).tooltip('fixTitle');
}
},
};
</script>
<template>
<div class="related-issues-block">
<div
class="panel-slim panel-default">
<div
class="panel-heading"
:class="{ 'panel-empty-heading': !this.hasRelatedIssues }">
<h3 class="panel-title">
Related issues
<a
v-if="hasHelpPath"
:href="helpPath">
<i
class="related-issues-header-help-icon fa fa-question-circle"
aria-label="Read more about related issues">
</i>
</a>
<div class="related-issues-header-issue-count issue-count-badge">
<span
class="issue-count-badge-count"
:class="{ 'has-btn': this.canAddRelatedIssues }">
{{ relatedIssueCount }}
</span>
<button
ref="issueCountBadgeAddButton"
v-if="canAddRelatedIssues"
type="button"
class="issue-count-badge-add-button btn btn-small btn-default"
title="Add an issue"
aria-label="Add an issue"
data-toggle="tooltip"
data-placement="top"
@click="showAddRelatedIssuesForm">
<i
class="fa fa-plus"
aria-hidden="true">
</i>
</button>
</div>
</h3>
</div>
<div
v-if="isFormVisible"
class="js-add-related-issues-form-area related-issues-add-related-issues-form panel-body">
<add-issuable-form
:input-value="inputValue"
:pending-issuables="pendingRelatedIssues"
add-button-label="Add related issues" />
</div>
<div
v-if="hasRelatedIssues"
class="panel-body">
<ul
class="related-issues-token-body">
<li
:key="issue.reference"
v-for="issue in relatedIssues"
class="js-related-issues-token-list-item related-issues-token-list-item">
<issue-token
event-namespace="relatedIssue"
:reference="issue.reference"
:display-reference="issue.displayReference"
:title="issue.title"
:path="issue.path"
:state="issue.state"
:fetch-status="issue.fetchStatus"
:can-remove="issue.canRemove" />
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
......@@ -32,6 +32,10 @@
}
}
.panel-empty-heading {
border-bottom: 0;
}
.panel-body {
padding: $gl-padding;
......
@import "./issues/issue_count_badge";
[v-cloak] {
display: none;
}
......@@ -415,30 +417,6 @@
margin: 5px;
}
.board-issue-count-holder {
margin-top: -3px;
.btn {
line-height: 12px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}
.board-issue-count {
padding-right: 10px;
padding-left: 10px;
line-height: 21px;
border-radius: $border-radius-base;
border: 1px solid $border-color;
&.has-btn {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-width: 1px 0 1px 1px;
}
}
.boards-title-holder {
padding: 25px 13px $gl-padding;
......
@import "./issues/issue_count_badge";
@import "./issues/related_issues";
.issues-list {
.issue {
padding: 10px 0 10px $gl-padding;
......
.issue-count-badge {
display: inline-flex;
align-items: stretch;
height: 24px;
}
.issue-count-badge-count {
display: flex;
align-items: center;
padding-right: 10px;
padding-left: 10px;
border: 1px solid $border-color;
border-radius: $border-radius-base;
line-height: 1;
&.has-btn {
border-right: 0;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}
.issue-count-badge-add-button {
display: flex;
align-items: center;
border: 1px solid $border-color;
border-radius: 0 $border-radius-base $border-radius-base 0;
line-height: 1;
}
.related-issues-block {
margin-top: $gl-vert-padding;
}
.related-issues-header-help-icon {
margin-left: 0.25em;
color: $gl-text-color-secondary;
}
.related-issues-header-issue-count {
margin-left: 0.5em;
}
.related-issues-add-related-issues-form {
border-bottom: 1px solid $border-color;
}
.related-issues-token-body {
display: flex;
flex-wrap: wrap;
margin-bottom: 0;
padding-left: 0;
list-style: none;
}
.related-issues-token-list-item {
margin-bottom: 0.5em;
margin-right: 1em;
}
......@@ -6,11 +6,11 @@
%span.has-tooltip{ ":title" => '(list.label ? list.label.description : "")',
data: { container: "body", placement: "bottom" } }
{{ list.title }}
.board-issue-count-holder.pull-right.clearfix{ "v-if" => 'list.type !== "blank"' }
%span.board-issue-count.pull-left{ ":class" => '{ "has-btn": list.type !== "closed" && !disabled }' }
.issue-count-badge.pull-right.clearfix{ "v-if" => 'list.type !== "blank"' }
%span.issue-count-badge-count{ ":class" => '{ "has-btn": list.type !== "closed" && !disabled }' }
{{ list.issuesSize }}
- if can?(current_user, :admin_issue, @project)
%button.btn.btn-small.btn-default.pull-right.has-tooltip{ type: "button",
%button.issue-count-badge-add-button.btn.btn-small.btn-default.has-tooltip{ type: "button",
"@click" => "showNewIssueForm",
"v-if" => 'list.type !== "closed"',
"aria-label" => "New issue",
......
# Related issues
> [Introduced][ee-1797] in GitLab 9.2.
Related issues are a bi-directional relationship between any two issues.
Related issues will appear in a block below the issue description.
# Adding related issues
You can relate one issue to the other by clicking the issue count badge "+" button
in the header of the related issue block. Then use the input that will appear
where you can type in the issue reference.
Valid references will be added to a temporary list that you can review.
When ready, click the green "Add related issues" button to submit.
![Adding a related issue](img/related_issues_add.png)
# Removing a related issue
In the related issues block, click the "x" icon on the right-side of every issue
token. Because of the bi-directional relationship, it will no longer appear in
either issue.
![Removing a related issue](img/related_issues_remove.png)
[ee-1797]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1797
......@@ -19,18 +19,18 @@ describe 'Issue Boards new issue', feature: true, js: true do
end
it 'displays new issue button' do
expect(page).to have_selector('.board-issue-count-holder .btn', count: 1)
expect(page).to have_selector('.issue-count-badge-add-button', count: 1)
end
it 'does not display new issue button in closed list' do
page.within('.board:nth-child(2)') do
expect(page).not_to have_selector('.board-issue-count-holder .btn')
expect(page).not_to have_selector('.issue-count-badge-add-button')
end
end
it 'shows form when clicking button' do
page.within(first('.board')) do
find('.board-issue-count-holder .btn').click
find('.issue-count-badge-add-button').click
expect(page).to have_selector('.board-new-issue-form')
end
......@@ -38,7 +38,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
it 'hides form when clicking cancel' do
page.within(first('.board')) do
find('.board-issue-count-holder .btn').click
find('.issue-count-badge-add-button').click
expect(page).to have_selector('.board-new-issue-form')
......@@ -50,7 +50,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
it 'creates new issue' do
page.within(first('.board')) do
find('.board-issue-count-holder .btn').click
find('.issue-count-badge-add-button').click
end
page.within(first('.board-new-issue-form')) do
......@@ -60,14 +60,14 @@ describe 'Issue Boards new issue', feature: true, js: true do
wait_for_requests
page.within(first('.board .board-issue-count')) do
page.within(first('.board .issue-count-badge-count')) do
expect(page).to have_content('1')
end
end
it 'shows sidebar when creating new issue' do
page.within(first('.board')) do
find('.board-issue-count-holder .btn').click
find('.issue-count-badge-add-button').click
end
page.within(first('.board-new-issue-form')) do
......@@ -88,7 +88,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
end
it 'does not display new issue button' do
expect(page).to have_selector('.board-issue-count-holder .btn', count: 0)
expect(page).to have_selector('.issue-count-badge-add-button', count: 0)
end
end
end
import Vue from 'vue';
import eventHub from '~/issuable/related_issues/event_hub';
import relatedIssuesBlock from '~/issuable/related_issues/components/related_issues_block.vue';
const issuable1 = {
reference: 'foo/bar#123',
displayReference: '#123',
title: 'some title',
path: '/foo/bar/issues/123',
state: 'opened',
};
const issuable2 = {
reference: 'foo/bar#124',
displayReference: '#124',
title: 'some other thing',
path: '/foo/bar/issues/124',
state: 'opened',
};
describe('RelatedIssuesBlock', () => {
let RelatedIssuesBlock;
let vm;
beforeEach(() => {
RelatedIssuesBlock = Vue.extend(relatedIssuesBlock);
});
afterEach(() => {
if (vm) {
vm.$destroy();
}
});
describe('with defaults', () => {
beforeEach(() => {
vm = new RelatedIssuesBlock().$mount();
});
it('unable to add new related issues', () => {
expect(vm.$refs.issueCountBadgeAddButton).toBeUndefined();
});
it('add related issues form is hidden', () => {
expect(vm.$el.querySelector('.js-add-related-issues-form-area')).toBeNull();
});
});
describe('with canAddRelatedIssues=true', () => {
beforeEach(() => {
vm = new RelatedIssuesBlock({
propsData: {
canAddRelatedIssues: true,
},
}).$mount();
});
it('can add new related issues', () => {
expect(vm.$refs.issueCountBadgeAddButton).toBeDefined();
});
});
describe('with isFormVisible=true', () => {
beforeEach(() => {
vm = new RelatedIssuesBlock({
propsData: {
isFormVisible: true,
},
}).$mount();
});
it('shows add related issues form', () => {
expect(vm.$el.querySelector('.js-add-related-issues-form-area')).toBeDefined();
});
});
describe('with relatedIssues', () => {
beforeEach(() => {
vm = new RelatedIssuesBlock({
propsData: {
relatedIssues: [
issuable1,
issuable2,
],
},
}).$mount();
});
it('should render issue tokens items', () => {
expect(vm.$el.querySelectorAll('.js-related-issues-token-list-item').length).toEqual(2);
});
});
describe('methods', () => {
let showAddRelatedIssuesFormSpy;
beforeEach(() => {
vm = new RelatedIssuesBlock({
propsData: {
relatedIssues: [
issuable1,
],
},
}).$mount();
showAddRelatedIssuesFormSpy = jasmine.createSpy('spy');
eventHub.$on('showAddRelatedIssuesForm', showAddRelatedIssuesFormSpy);
});
afterEach(() => {
eventHub.$off('showAddRelatedIssuesForm', showAddRelatedIssuesFormSpy);
});
it('when expanding add related issue form', () => {
expect(showAddRelatedIssuesFormSpy).not.toHaveBeenCalled();
vm.showAddRelatedIssuesForm();
expect(showAddRelatedIssuesFormSpy).toHaveBeenCalled();
});
});
});
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