Stop redirecting the page in graph main actions

This commit is contained in:
Filipa Lacerda 2018-04-06 15:42:19 +00:00 committed by Phil Hughes
parent 0fff9db5ea
commit 377c8c7846
9 changed files with 300 additions and 234 deletions

View File

@ -1,14 +1,15 @@
<script>
import $ from 'jquery';
import tooltip from '../../../vue_shared/directives/tooltip';
import icon from '../../../vue_shared/components/icon.vue';
import Icon from '../../../vue_shared/components/icon.vue';
import { dasherize } from '../../../lib/utils/text_utility';
import eventHub from '../../event_hub';
/**
* Renders either a cancel, retry or play icon pointing to the given path.
* TODO: Remove UJS from here and use an async request instead.
*/
export default {
components: {
icon,
Icon,
},
directives: {
@ -26,35 +27,46 @@
required: true,
},
actionMethod: {
type: String,
required: true,
},
actionIcon: {
type: String,
required: true,
},
},
buttonDisabled: {
type: String,
required: false,
default: null,
},
},
computed: {
cssClass() {
const actionIconDash = dasherize(this.actionIcon);
return `${actionIconDash} js-icon-${actionIconDash}`;
},
isDisabled() {
return this.buttonDisabled === this.link;
},
},
methods: {
onClickAction() {
$(this.$el).tooltip('hide');
eventHub.$emit('graphAction', this.link);
},
},
};
</script>
<template>
<a
<button
type="button"
@click="onClickAction"
v-tooltip
:data-method="actionMethod"
:title="tooltipText"
:href="link"
class="ci-action-icon-container ci-action-icon-wrapper"
class="btn btn-blank btn-transparent ci-action-icon-container ci-action-icon-wrapper"
:class="cssClass"
data-container="body"
:disabled="isDisabled"
>
<icon :name="actionIcon" />
</a>
</button>
</template>

View File

@ -1,11 +1,11 @@
<script>
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import stageColumnComponent from './stage_column_component.vue';
import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import StageColumnComponent from './stage_column_component.vue';
export default {
components: {
stageColumnComponent,
loadingIcon,
StageColumnComponent,
LoadingIcon,
},
props: {
@ -17,6 +17,11 @@
type: Object,
required: true,
},
actionDisabled: {
type: String,
required: false,
default: null,
},
},
computed: {
@ -70,6 +75,7 @@
:key="stage.name"
:stage-connector-class="stageConnectorClass(index, stage)"
:is-first-column="isFirstColumn(index)"
:action-disabled="actionDisabled"
/>
</ul>
</div>

View File

@ -1,7 +1,7 @@
<script>
import actionComponent from './action_component.vue';
import dropdownActionComponent from './dropdown_action_component.vue';
import jobNameComponent from './job_name_component.vue';
import ActionComponent from './action_component.vue';
import DropdownActionComponent from './dropdown_action_component.vue';
import JobNameComponent from './job_name_component.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
/**
@ -31,9 +31,9 @@
export default {
components: {
actionComponent,
dropdownActionComponent,
jobNameComponent,
ActionComponent,
DropdownActionComponent,
JobNameComponent,
},
directives: {
@ -56,6 +56,12 @@
required: false,
default: false,
},
actionDisabled: {
type: String,
required: false,
default: null,
},
},
computed: {
@ -132,7 +138,7 @@
:tooltip-text="status.action.title"
:link="status.action.path"
:action-icon="status.action.icon"
:action-method="status.action.method"
:button-disabled="actionDisabled"
/>
<dropdown-action-component

View File

@ -1,11 +1,11 @@
<script>
import jobComponent from './job_component.vue';
import dropdownJobComponent from './dropdown_job_component.vue';
import JobComponent from './job_component.vue';
import DropdownJobComponent from './dropdown_job_component.vue';
export default {
components: {
jobComponent,
dropdownJobComponent,
JobComponent,
DropdownJobComponent,
},
props: {
title: {
@ -29,6 +29,11 @@
required: false,
default: '',
},
actionDisabled: {
type: String,
required: false,
default: null,
},
},
methods: {
@ -69,6 +74,7 @@
v-if="job.size === 1"
:job="job"
css-class-job-name="build-content"
:action-disabled="actionDisabled"
/>
<dropdown-job-component

View File

@ -25,13 +25,36 @@ export default () => {
data() {
return {
mediator,
actionDisabled: null,
};
},
created() {
eventHub.$on('graphAction', this.postAction);
},
beforeDestroy() {
eventHub.$off('graphAction', this.postAction);
},
methods: {
postAction(action) {
this.actionDisabled = action;
this.mediator.service.postAction(action)
.then(() => {
this.mediator.refreshPipeline();
this.actionDisabled = null;
})
.catch(() => {
this.actionDisabled = null;
Flash(__('An error occurred while making the request.'));
});
},
},
render(createElement) {
return createElement('pipeline-graph', {
props: {
isLoading: this.mediator.state.isLoading,
pipeline: this.mediator.store.state.pipeline,
actionDisabled: this.actionDisabled,
},
});
},

View File

@ -52,8 +52,11 @@ export default class pipelinesMediator {
}
refreshPipeline() {
this.service.getPipeline()
this.poll.stop();
return this.service.getPipeline()
.then(response => this.successCallback(response))
.catch(() => this.errorCallback());
.catch(() => this.errorCallback())
.finally(() => this.poll.restart());
}
}

View File

@ -495,17 +495,17 @@
svg {
fill: $gl-text-color-secondary;
position: relative;
left: 5px;
top: 2px;
width: 18px;
height: 18px;
left: 1px;
top: -1px;
width: 16px;
height: 16px;
}
&.play {
svg {
width: #{$ci-action-icon-size - 8};
height: #{$ci-action-icon-size - 8};
left: 8px;
width: 16px;
height: 16px;
left: 3px;
}
}
}

View File

@ -0,0 +1,5 @@
---
title: Stop redirecting the page in pipeline main actions
merge_request:
author:
type: fixed

View File

@ -1,25 +1,30 @@
import Vue from 'vue';
import actionComponent from '~/pipelines/components/graph/action_component.vue';
import eventHub from '~/pipelines/event_hub';
import mountComponent from '../../helpers/vue_mount_component_helper';
describe('pipeline graph action component', () => {
let component;
beforeEach((done) => {
const ActionComponent = Vue.extend(actionComponent);
component = new ActionComponent({
propsData: {
component = mountComponent(ActionComponent, {
tooltipText: 'bar',
link: 'foo',
actionMethod: 'post',
actionIcon: 'cancel',
},
}).$mount();
});
Vue.nextTick(done);
});
it('should render a link', () => {
expect(component.$el.getAttribute('href')).toEqual('foo');
afterEach(() => {
component.$destroy();
});
it('should emit an event with the provided link', () => {
eventHub.$on('graphAction', (link) => {
expect(link).toEqual('foo');
});
});
it('should render the provided title as a bootstrap tooltip', () => {