Commit b76f1d14 authored by Filipa Lacerda's avatar Filipa Lacerda

Fix disabled state while making a request

Provide props down instead of an event
parent 8bd0bb72
......@@ -32,26 +32,38 @@ export default {
required: true,
},
buttonDisabled: {
requestFinishedFor: {
type: String,
required: false,
default: null,
default: '',
},
},
data() {
return {
isDisabled: false,
linkRequested: '',
};
},
computed: {
cssClass() {
const actionIconDash = dasherize(this.actionIcon);
return `${actionIconDash} js-icon-${actionIconDash}`;
},
isDisabled() {
return this.buttonDisabled === this.link;
},
watch: {
requestFinishedFor() {
if (this.requestFinishedFor === this.linkRequested) {
this.isDisabled = false;
}
},
},
methods: {
onClickAction() {
$(this.$el).tooltip('hide');
eventHub.$emit('graphAction', this.link);
this.linkRequested = this.link;
this.isDisabled = true;
},
},
};
......@@ -62,7 +74,8 @@ export default {
@click="onClickAction"
v-tooltip
:title="tooltipText"
class="js-ci-action btn btn-blank btn-transparent ci-action-icon-container ci-action-icon-wrapper"
class="js-ci-action btn btn-blank
btn-transparent ci-action-icon-container ci-action-icon-wrapper"
:class="cssClass"
data-container="body"
:disabled="isDisabled"
......
<script>
import $ from 'jquery';
import JobNameComponent from './job_name_component.vue';
import JobComponent from './job_component.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
import $ from 'jquery';
import JobNameComponent from './job_name_component.vue';
import JobComponent from './job_component.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
/**
/**
* Renders the dropdown for the pipeline graph.
*
* The following object should be provided as `job`:
......@@ -27,7 +27,7 @@
* }
* }
*/
export default {
export default {
directives: {
tooltip,
},
......@@ -42,6 +42,11 @@
type: Object,
required: true,
},
requestFinishedFor: {
type: String,
required: false,
default: '',
},
},
computed: {
......@@ -57,7 +62,8 @@
methods: {
/**
* When the user right clicks or cmd/ctrl + click in the job name or the action icon
* the dropdown should not be closed so we stop propagation of the click event inside the dropdown.
* the dropdown should not be closed so we stop propagation
* of the click event inside the dropdown.
*
* Since this component is rendered multiple times per page we need to guarantee we only
* target the click event of this component.
......@@ -65,13 +71,13 @@
stopDropdownClickPropagation() {
$(
'.js-grouped-pipeline-dropdown button, .js-grouped-pipeline-dropdown a.mini-pipeline-graph-dropdown-item',
this.$el,
).on('click', (e) => {
this.$el
).on('click', e => {
e.stopPropagation();
});
},
},
};
};
</script>
<template>
<div class="ci-job-dropdown-container">
......@@ -102,6 +108,7 @@
<job-component
:job="item"
css-class-job-name="mini-pipeline-graph-dropdown-item"
:request-finished-for="requestFinishedFor"
/>
</li>
</ul>
......
......@@ -18,10 +18,10 @@ export default {
type: Object,
required: true,
},
actionDisabled: {
requestFinishedFor: {
type: String,
required: false,
default: null,
default: '',
},
},
......@@ -105,7 +105,7 @@ export default {
:key="stage.name"
:stage-connector-class="stageConnectorClass(index, stage)"
:is-first-column="isFirstColumn(index)"
:action-disabled="actionDisabled"
:request-finished-for="requestFinishedFor"
:has-triggered-by="hasTriggeredBy"
/>
</ul>
......
......@@ -33,7 +33,6 @@ export default {
ActionComponent,
JobNameComponent,
},
directives: {
tooltip,
},
......@@ -42,20 +41,17 @@ export default {
type: Object,
required: true,
},
cssClassJobName: {
type: String,
required: false,
default: '',
},
actionDisabled: {
requestFinishedFor: {
type: String,
required: false,
default: null,
default: '',
},
},
computed: {
status() {
return this.job && this.job.status ? this.job.status : {};
......@@ -130,7 +126,7 @@ export default {
:tooltip-text="status.action.title"
:link="status.action.path"
:action-icon="status.action.icon"
:button-disabled="actionDisabled"
:request-finished-for="requestFinishedFor"
/>
</div>
</template>
......@@ -30,10 +30,10 @@ export default {
default: '',
},
actionDisabled: {
requestFinishedFor: {
type: String,
required: false,
default: null,
default: '',
},
hasTriggeredBy: {
type: Boolean,
......@@ -81,12 +81,12 @@ export default {
v-if="job.size === 1"
:job="job"
css-class-job-name="build-content"
:action-disabled="actionDisabled"
/>
<dropdown-job-component
v-if="job.size > 1"
:job="job"
:request-finished-for="requestFinishedFor"
/>
</li>
......
......@@ -29,7 +29,7 @@ export default () => {
data() {
return {
mediator,
actionDisabled: null,
requestFinishedFor: null,
};
},
created() {
......@@ -40,15 +40,17 @@ export default () => {
},
methods: {
postAction(action) {
this.actionDisabled = action;
// Click was made, reset this variable
this.requestFinishedFor = null;
this.mediator.service.postAction(action)
this.mediator.service
.postAction(action)
.then(() => {
this.mediator.refreshPipeline();
this.actionDisabled = null;
this.requestFinishedFor = action;
})
.catch(() => {
this.actionDisabled = null;
this.requestFinishedFor = action;
Flash(__('An error occurred while making the request.'));
});
},
......@@ -58,7 +60,7 @@ export default () => {
props: {
isLoading: this.mediator.state.isLoading,
pipeline: this.mediator.store.state.pipeline,
actionDisabled: this.actionDisabled,
requestFinishedFor: this.requestFinishedFor,
},
});
},
......@@ -83,7 +85,8 @@ export default () => {
},
methods: {
postAction(action) {
this.mediator.service.postAction(action.path)
this.mediator.service
.postAction(action.path)
.then(() => this.mediator.refreshPipeline())
.catch(() => Flash(__('An error occurred while making the request.')));
},
......
---
title: Prevent pipeline actions in dropdown to redirct to a new page
merge_request:
author:
type: fixed
......@@ -6,7 +6,7 @@ import mountComponent from '../../helpers/vue_mount_component_helper';
describe('pipeline graph action component', () => {
let component;
beforeEach((done) => {
beforeEach(done => {
const ActionComponent = Vue.extend(actionComponent);
component = mountComponent(ActionComponent, {
tooltipText: 'bar',
......@@ -22,7 +22,7 @@ describe('pipeline graph action component', () => {
});
it('should emit an event with the provided link', () => {
eventHub.$on('graphAction', (link) => {
eventHub.$on('graphAction', link => {
expect(link).toEqual('foo');
});
});
......@@ -31,7 +31,7 @@ describe('pipeline graph action component', () => {
expect(component.$el.getAttribute('data-original-title')).toEqual('bar');
});
it('should update bootstrap tooltip when title changes', (done) => {
it('should update bootstrap tooltip when title changes', done => {
component.tooltipText = 'changed';
setTimeout(() => {
......@@ -44,4 +44,45 @@ describe('pipeline graph action component', () => {
expect(component.$el.querySelector('.ci-action-icon-wrapper')).toBeDefined();
expect(component.$el.querySelector('svg')).toBeDefined();
});
it('disables the button when clicked', done => {
component.$el.click();
component.$nextTick(() => {
expect(component.$el.getAttribute('disabled')).toEqual('disabled');
done();
});
});
it('re-enabled the button when `requestFinishedFor` matches `linkRequested`', done => {
component.$el.click();
component
.$nextTick()
.then(() => {
expect(component.$el.getAttribute('disabled')).toEqual('disabled');
component.requestFinishedFor = 'foo';
})
.then(() => {
expect(component.$el.getAttribute('disabled')).toBeNull();
})
.then(done)
.catch(done.fail);
});
it('does not re-enable the button when `requestFinishedFor` does not matches `linkRequested`', done => {
component.$el.click();
component
.$nextTick()
.then(() => {
expect(component.$el.getAttribute('disabled')).toEqual('disabled');
component.requestFinishedFor = 'bar';
})
.then(() => {
expect(component.$el.getAttribute('disabled')).toEqual('disabled');
})
.then(done)
.catch(done.fail);
});
});
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