Commit ad21d826 authored by Eric Eastwood's avatar Eric Eastwood Committed by Phil Hughes

Related Issues UX improvements - loading

parent 0b86b67a
<script> <script>
import GfmAutoComplete from '~/gfm_auto_complete'; import GfmAutoComplete from '~/gfm_auto_complete';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import IssueToken from './issue_token.vue'; import issueToken from './issue_token.vue';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
export default { export default {
name: 'AddIssuableForm', name: 'AddIssuableForm',
...@@ -40,7 +41,8 @@ export default { ...@@ -40,7 +41,8 @@ export default {
}, },
components: { components: {
issueToken: IssueToken, issueToken,
loadingIcon,
}, },
computed: { computed: {
...@@ -139,6 +141,11 @@ export default { ...@@ -139,6 +141,11 @@ export default {
@click="onFormSubmit" @click="onFormSubmit"
:disabled="isSubmitButtonDisabled"> :disabled="isSubmitButtonDisabled">
{{ addButtonLabel }} {{ addButtonLabel }}
<loadingIcon
ref="loadingIcon"
v-if="isSubmitting"
:inline="true"
label="Submitting related issues" />
</button> </button>
<button <button
type="button" type="button"
......
...@@ -70,6 +70,12 @@ export default { ...@@ -70,6 +70,12 @@ export default {
hasRelatedIssues() { hasRelatedIssues() {
return this.relatedIssues.length > 0; return this.relatedIssues.length > 0;
}, },
shouldShowTokenBody() {
return this.hasRelatedIssues || this.isFetching;
},
hasBody() {
return this.isFormVisible || this.shouldShowTokenBody;
},
relatedIssueCount() { relatedIssueCount() {
return this.relatedIssues.length; return this.relatedIssues.length;
}, },
...@@ -92,9 +98,8 @@ export default { ...@@ -92,9 +98,8 @@ export default {
class="panel-slim panel-default"> class="panel-slim panel-default">
<div <div
class="panel-heading" class="panel-heading"
:class="{ 'panel-empty-heading': !this.hasRelatedIssues }"> :class="{ 'panel-empty-heading': !this.hasBody }">
<h3 class="panel-title related-issues-panel-title"> <h3 class="panel-title">
<div>
Related issues Related issues
<a <a
v-if="hasHelpPath" v-if="hasHelpPath"
...@@ -126,13 +131,6 @@ export default { ...@@ -126,13 +131,6 @@ export default {
</i> </i>
</button> </button>
</div> </div>
</div>
<div>
<loadingIcon
ref="loadingIcon"
v-if="isFetching"
label="Fetching related issues" />
</div>
</h3> </h3>
</div> </div>
<div <div
...@@ -149,8 +147,17 @@ export default { ...@@ -149,8 +147,17 @@ export default {
:auto-complete-sources="autoCompleteSources" /> :auto-complete-sources="autoCompleteSources" />
</div> </div>
<div <div
v-if="hasRelatedIssues" class="related-issues-token-body panel-body"
class="related-issues-token-body panel-body"> :class="{
'collapsed': !shouldShowTokenBody
}">
<div
v-if="isFetching"
class="related-issues-loading-icon">
<loadingIcon
ref="loadingIcon"
label="Fetching related issues" />
</div>
<ul <ul
class="related-issues-token-list"> class="related-issues-token-list">
<li <li
......
...@@ -12,9 +12,18 @@ ...@@ -12,9 +12,18 @@
required: false, required: false,
default: '1', default: '1',
}, },
inline: {
type: Boolean,
required: false,
default: false,
},
}, },
computed: { computed: {
rootElementType() {
return this.inline ? 'span' : 'div';
},
cssClass() { cssClass() {
return `fa-${this.size}x`; return `fa-${this.size}x`;
}, },
...@@ -22,12 +31,14 @@ ...@@ -22,12 +31,14 @@
}; };
</script> </script>
<template> <template>
<div class="text-center"> <component
:is="this.rootElementType"
class="text-center">
<i <i
class="fa fa-spin fa-spinner" class="fa fa-spin fa-spinner"
:class="cssClass" :class="cssClass"
aria-hidden="true" aria-hidden="true"
:aria-label="label"> :aria-label="label">
</i> </i>
</div> </component>
</template> </template>
...@@ -4,11 +4,6 @@ $token_spacing_bottom: 0.5em; ...@@ -4,11 +4,6 @@ $token_spacing_bottom: 0.5em;
margin-top: 3 * $gl-vert-padding; margin-top: 3 * $gl-vert-padding;
} }
.related-issues-panel-title {
display: flex;
justify-content: space-between;
}
.related-issues-header-help-icon { .related-issues-header-help-icon {
margin-left: 0.25em; margin-left: 0.25em;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
...@@ -24,6 +19,22 @@ $token_spacing_bottom: 0.5em; ...@@ -24,6 +19,22 @@ $token_spacing_bottom: 0.5em;
.related-issues-token-body { .related-issues-token-body {
padding-bottom: calc(#{$gl-padding} - #{$token_spacing_bottom}); padding-bottom: calc(#{$gl-padding} - #{$token_spacing_bottom});
transition-property: max-height, padding, opacity;
transition-duration: $general-hover-transition-duration;
transition-timing-function: $general-hover-transition-curve;
&.collapsed {
overflow: hidden;
max-height: 0;
padding-top: 0;
padding-bottom: 0;
opacity: 0;
}
}
.related-issues-loading-icon {
margin-bottom: $token_spacing_bottom;
line-height: 1.75;
} }
.related-issues-token-list { .related-issues-token-list {
......
...@@ -72,6 +72,11 @@ ...@@ -72,6 +72,11 @@
.js-related-issues-root{ data: { endpoint: namespace_project_issue_links_path(@project.namespace, @project, @issue), .js-related-issues-root{ data: { endpoint: namespace_project_issue_links_path(@project.namespace, @project, @issue),
can_add_related_issues: "#{can?(current_user, :update_issue, @issue)}", can_add_related_issues: "#{can?(current_user, :update_issue, @issue)}",
help_path: help_page_path('user/project/issues/related_issues') } } help_path: help_page_path('user/project/issues/related_issues') } }
.related-issues-block
.panel-slim.panel-default
.panel-heading.panel-empty-heading
%h3.panel-title
Related issues
#merge-requests{ data: { url: referenced_merge_requests_namespace_project_issue_url(@project.namespace, @project, @issue) } } #merge-requests{ data: { url: referenced_merge_requests_namespace_project_issue_url(@project.namespace, @project, @issue) } }
// This element is filled in using JavaScript. // This element is filled in using JavaScript.
......
...@@ -39,6 +39,24 @@ describe('AddIssuableForm', () => { ...@@ -39,6 +39,24 @@ describe('AddIssuableForm', () => {
}); });
describe('with data', () => { describe('with data', () => {
describe('without references', () => {
beforeEach(() => {
vm = new AddIssuableForm({
propsData: {
inputValue: '',
addButtonLabel: 'Submit',
pendingReferences: [],
},
}).$mount();
});
it('should have disabled submit button', () => {
expect(vm.$refs.addButton.disabled).toBe(true);
expect(vm.$refs.loadingIcon).toBeUndefined();
});
});
describe('with references', () => {
const inputValue = 'foo #123'; const inputValue = 'foo #123';
const addButtonLabel = 'Add issuable'; const addButtonLabel = 'Add issuable';
...@@ -68,6 +86,28 @@ describe('AddIssuableForm', () => { ...@@ -68,6 +86,28 @@ describe('AddIssuableForm', () => {
}); });
}); });
describe('when submitting', () => {
beforeEach(() => {
vm = new AddIssuableForm({
propsData: {
inputValue: '',
addButtonLabel: 'Submit',
pendingReferences: [
issuable1.reference,
issuable2.reference,
],
isSubmitting: true,
},
}).$mount();
});
it('should have disabled submit button with loading icon', () => {
expect(vm.$refs.addButton.disabled).toBe(true);
expect(vm.$refs.loadingIcon).toBeDefined();
});
});
});
describe('methods', () => { describe('methods', () => {
let addIssuableFormInputSpy; let addIssuableFormInputSpy;
let addIssuableFormBlurSpy; let addIssuableFormBlurSpy;
......
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