Commit 93af1114 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Wire create and edit form actions

Allows us to reuse the custom_stage_form
for both editing and creating a custom cycle
analytics stage.

Dispatch updateStage action

Added `id` field to edit form initial data

Display updated stage data after successful action
parent b4758cfa
...@@ -125,6 +125,7 @@ export default { ...@@ -125,6 +125,7 @@ export default {
'removeStage', 'removeStage',
'setFeatureFlags', 'setFeatureFlags',
'editCustomStage', 'editCustomStage',
'updateStage',
]), ]),
onGroupSelect(group) { onGroupSelect(group) {
this.setSelectedGroup(group); this.setSelectedGroup(group);
...@@ -154,7 +155,7 @@ export default { ...@@ -154,7 +155,7 @@ export default {
onCreateCustomStage(data) { onCreateCustomStage(data) {
this.createCustomStage(data); this.createCustomStage(data);
}, },
onUpdateStage(data) { onUpdateCustomStage(data) {
this.updateStage(data); this.updateStage(data);
}, },
onRemoveStage(id) { onRemoveStage(id) {
...@@ -262,9 +263,10 @@ export default { ...@@ -262,9 +263,10 @@ export default {
@selectStage="onStageSelect" @selectStage="onStageSelect"
@editStage="onShowEditStageForm" @editStage="onShowEditStageForm"
@showAddStageForm="onShowAddStageForm" @showAddStageForm="onShowAddStageForm"
@submit="onCreateCustomStage" @hideStage="onUpdateCustomStage"
@hideStage="onUpdateStage"
@removeStage="onRemoveStage" @removeStage="onRemoveStage"
@createStage="onCreateCustomStage"
@updateStage="onUpdateCustomStage"
/> />
</div> </div>
</div> </div>
......
...@@ -15,6 +15,7 @@ import { ...@@ -15,6 +15,7 @@ import {
} from '../utils'; } from '../utils';
const initFields = { const initFields = {
id: null,
name: null, name: null,
startEventIdentifier: null, startEventIdentifier: null,
startEventLabelId: null, startEventLabelId: null,
...@@ -132,7 +133,7 @@ export default { ...@@ -132,7 +133,7 @@ export default {
}, },
saveStageText() { saveStageText() {
return this.isEditingCustomStage return this.isEditingCustomStage
? s__('CustomCycleAnalytics|Edit stage') ? s__('CustomCycleAnalytics|Update stage')
: s__('CustomCycleAnalytics|Add stage'); : s__('CustomCycleAnalytics|Add stage');
}, },
formTitle() { formTitle() {
...@@ -150,10 +151,13 @@ export default { ...@@ -150,10 +151,13 @@ export default {
this.$emit('cancel'); this.$emit('cancel');
}, },
handleSave() { handleSave() {
this.$emit( const data = snakeFields(this.fields);
this.isEditingCustomStage ? STAGE_ACTIONS.EDIT : STAGE_ACTIONS.SAVE, if (this.isEditingCustomStage) {
snakeFields(this.fields), const { id } = this.initialFields;
); this.$emit(STAGE_ACTIONS.UPDATE, { ...data, id });
} else {
this.$emit(STAGE_ACTIONS.CREATE, data);
}
}, },
handleSelectLabel(key, labelId = null) { handleSelectLabel(key, labelId = null) {
this.fields[key] = labelId; this.fields[key] = labelId;
...@@ -184,7 +188,7 @@ export default { ...@@ -184,7 +188,7 @@ export default {
<gl-form-group :label="s__('CustomCycleAnalytics|Start event')"> <gl-form-group :label="s__('CustomCycleAnalytics|Start event')">
<gl-form-select <gl-form-select
v-model="fields.startEventIdentifier" v-model="fields.startEventIdentifier"
name="add-stage-start-event" name="custom-stage-start-event"
:required="true" :required="true"
:options="startEventOptions" :options="startEventOptions"
/> />
...@@ -195,7 +199,7 @@ export default { ...@@ -195,7 +199,7 @@ export default {
<labels-selector <labels-selector
:labels="labels" :labels="labels"
:selected-label-id="fields.startEventLabelId" :selected-label-id="fields.startEventLabelId"
name="add-stage-start-event-label" name="custom-stage-start-event-label"
@selectLabel="labelId => handleSelectLabel('startEventLabelId', labelId)" @selectLabel="labelId => handleSelectLabel('startEventLabelId', labelId)"
@clearLabel="handleClearLabel('startEventLabelId')" @clearLabel="handleClearLabel('startEventLabelId')"
/> />
...@@ -214,7 +218,7 @@ export default { ...@@ -214,7 +218,7 @@ export default {
> >
<gl-form-select <gl-form-select
v-model="fields.endEventIdentifier" v-model="fields.endEventIdentifier"
name="add-stage-stop-event" name="custom-stage-stop-event"
:options="endEventOptions" :options="endEventOptions"
:required="true" :required="true"
:disabled="!hasStartEvent" :disabled="!hasStartEvent"
...@@ -226,7 +230,7 @@ export default { ...@@ -226,7 +230,7 @@ export default {
<labels-selector <labels-selector
:labels="labels" :labels="labels"
:selected-label-id="fields.endEventLabelId" :selected-label-id="fields.endEventLabelId"
name="add-stage-stop-event-label" name="custom-stage-stop-event-label"
@selectLabel="labelId => handleSelectLabel('endEventLabelId', labelId)" @selectLabel="labelId => handleSelectLabel('endEventLabelId', labelId)"
@clearLabel="handleClearLabel('endEventLabelId')" @clearLabel="handleClearLabel('endEventLabelId')"
/> />
......
...@@ -91,6 +91,9 @@ export default { ...@@ -91,6 +91,9 @@ export default {
const { currentStageEvents = [], isLoading, isEmptyStage } = this; const { currentStageEvents = [], isLoading, isEmptyStage } = this;
return currentStageEvents.length && !isLoading && !isEmptyStage; return currentStageEvents.length && !isLoading && !isEmptyStage;
}, },
customStageFormActive() {
return this.isCreatingCustomStage;
},
stageHeaders() { stageHeaders() {
return [ return [
{ {
...@@ -109,13 +112,13 @@ export default { ...@@ -109,13 +112,13 @@ export default {
title: this.stageName, title: this.stageName,
description: __('The collection of events added to the data gathered for that stage.'), description: __('The collection of events added to the data gathered for that stage.'),
classes: 'event-header pl-3', classes: 'event-header pl-3',
displayHeader: !this.isAddingCustomStage, displayHeader: !this.customStageFormActive,
}, },
{ {
title: __('Total Time'), title: __('Total Time'),
description: __('The time taken by each data entry gathered by that stage.'), description: __('The time taken by each data entry gathered by that stage.'),
classes: 'total-time-header pr-5 text-right', classes: 'total-time-header pr-5 text-right',
displayHeader: !this.isAddingCustomStage, displayHeader: !this.customStageFormActive,
}, },
]; ];
}, },
...@@ -129,6 +132,7 @@ export default { ...@@ -129,6 +132,7 @@ export default {
this.$emit(STAGE_ACTIONS.REMOVE, stageId); this.$emit(STAGE_ACTIONS.REMOVE, stageId);
}, },
}, },
STAGE_ACTIONS,
}; };
</script> </script>
<template> <template>
...@@ -159,14 +163,14 @@ export default { ...@@ -159,14 +163,14 @@ export default {
:is-active="!isCreatingCustomStage && stage.id === currentStage.id" :is-active="!isCreatingCustomStage && stage.id === currentStage.id"
:can-edit="canEditStages" :can-edit="canEditStages"
:is-default-stage="!stage.custom" :is-default-stage="!stage.custom"
@select="$emit('selectStage', stage)"
@remove="removeStage(stage.id)" @remove="removeStage(stage.id)"
@hide="hideStage(stage.id)" @hide="hideStage(stage.id)"
@edit="$emit(STAGE_ACTIONS.EDIT, stageData)" @select="$emit($options.STAGE_ACTIONS.SELECT, stage)"
@edit="$emit($options.STAGE_ACTIONS.EDIT, stage)"
/> />
<add-stage-button <add-stage-button
v-if="canEditStages" v-if="canEditStages"
:active="isAddingCustomStage" :active="customStageFormActive"
@showform="$emit('showAddStageForm')" @showform="$emit('showAddStageForm')"
/> />
</ul> </ul>
...@@ -181,7 +185,8 @@ export default { ...@@ -181,7 +185,8 @@ export default {
:initial-fields="customStageFormInitData" :initial-fields="customStageFormInitData"
:is-editing-custom-stage="isEditingCustomStage" :is-editing-custom-stage="isEditingCustomStage"
@submit="$emit('submit', $event)" @submit="$emit('submit', $event)"
@saveStage="saveStage" @createStage="$emit($options.STAGE_ACTIONS.CREATE, $event)"
@updateStage="$emit($options.STAGE_ACTIONS.UPDATE, $event)"
/> />
<template v-else> <template v-else>
<stage-event-list <stage-event-list
......
...@@ -37,6 +37,7 @@ export const STAGE_ACTIONS = { ...@@ -37,6 +37,7 @@ export const STAGE_ACTIONS = {
SELECT: 'selectStage', SELECT: 'selectStage',
EDIT: 'editStage', EDIT: 'editStage',
REMOVE: 'removeStage', REMOVE: 'removeStage',
SAVE: 'saveStage',
HIDE: 'hideStage', HIDE: 'hideStage',
CREATE: 'createStage',
UPDATE: 'updateStage',
}; };
...@@ -86,10 +86,12 @@ export const fetchCycleAnalyticsData = ({ dispatch }) => { ...@@ -86,10 +86,12 @@ export const fetchCycleAnalyticsData = ({ dispatch }) => {
export const hideCustomStageForm = ({ commit }) => commit(types.HIDE_CUSTOM_STAGE_FORM); export const hideCustomStageForm = ({ commit }) => commit(types.HIDE_CUSTOM_STAGE_FORM);
export const showCustomStageForm = ({ commit }) => commit(types.SHOW_CUSTOM_STAGE_FORM); export const showCustomStageForm = ({ commit }) => commit(types.SHOW_CUSTOM_STAGE_FORM);
export const editCustomStage = ({ commit, dispatch }, initData = {}) => { export const editCustomStage = ({ commit, dispatch }, selectedStage = {}) => {
commit(types.EDIT_CUSTOM_STAGE, initData); commit(types.EDIT_CUSTOM_STAGE);
if (initData.id) { commit(types.SET_SELECTED_STAGE_DATA, selectedStage);
dispatch('setSelectedStageId', initData.id);
if (selectedStage.id) {
dispatch('setSelectedStageId', selectedStage.id);
} }
}; };
...@@ -251,11 +253,12 @@ export const fetchTasksByTypeData = ({ dispatch, state, getters }) => { ...@@ -251,11 +253,12 @@ export const fetchTasksByTypeData = ({ dispatch, state, getters }) => {
}; };
export const requestUpdateStage = ({ commit }) => commit(types.REQUEST_UPDATE_STAGE); export const requestUpdateStage = ({ commit }) => commit(types.REQUEST_UPDATE_STAGE);
export const receiveUpdateStageSuccess = ({ commit, dispatch }) => { export const receiveUpdateStageSuccess = ({ commit, dispatch }, updatedData) => {
commit(types.RECEIVE_UPDATE_STAGE_RESPONSE); commit(types.RECEIVE_UPDATE_STAGE_RESPONSE);
createFlash(__(`Stage data updated`), 'notice'); createFlash(__(`Stage data updated`), 'notice');
dispatch('fetchGroupStagesAndEvents'); dispatch('fetchGroupStagesAndEvents');
commit(types.SET_SELECTED_STAGE_DATA, updatedData);
}; };
export const receiveUpdateStageError = ({ commit }) => { export const receiveUpdateStageError = ({ commit }) => {
......
...@@ -19,6 +19,7 @@ export const RECEIVE_STAGE_DATA_ERROR = 'RECEIVE_STAGE_DATA_ERROR'; ...@@ -19,6 +19,7 @@ export const RECEIVE_STAGE_DATA_ERROR = 'RECEIVE_STAGE_DATA_ERROR';
export const HIDE_CUSTOM_STAGE_FORM = 'HIDE_CUSTOM_STAGE_FORM'; export const HIDE_CUSTOM_STAGE_FORM = 'HIDE_CUSTOM_STAGE_FORM';
export const SHOW_CUSTOM_STAGE_FORM = 'SHOW_CUSTOM_STAGE_FORM'; export const SHOW_CUSTOM_STAGE_FORM = 'SHOW_CUSTOM_STAGE_FORM';
export const EDIT_CUSTOM_STAGE = 'EDIT_CUSTOM_STAGE'; export const EDIT_CUSTOM_STAGE = 'EDIT_CUSTOM_STAGE';
export const SET_SELECTED_STAGE_DATA = 'SET_SELECTED_STAGE_DATA';
export const REQUEST_GROUP_LABELS = 'REQUEST_GROUP_LABELS'; export const REQUEST_GROUP_LABELS = 'REQUEST_GROUP_LABELS';
export const RECEIVE_GROUP_LABELS_SUCCESS = 'RECEIVE_GROUP_LABELS_SUCCESS'; export const RECEIVE_GROUP_LABELS_SUCCESS = 'RECEIVE_GROUP_LABELS_SUCCESS';
......
...@@ -80,19 +80,32 @@ export default { ...@@ -80,19 +80,32 @@ export default {
state.isCreatingCustomStage = true; state.isCreatingCustomStage = true;
state.customStageFormInitData = {}; state.customStageFormInitData = {};
}, },
[types.EDIT_CUSTOM_STAGE](state, initData) { [types.EDIT_CUSTOM_STAGE](state) {
console.log('EDIT_CUSTOM_STAGE::initData', initData); state.isEditingCustomStage = true;
},
[types.HIDE_CUSTOM_STAGE_FORM](state) {
state.isEditingCustomStage = false;
state.isCreatingCustomStage = false;
state.customStageFormInitData = {};
},
[types.SHOW_CUSTOM_STAGE_FORM](state) {
state.isCreatingCustomStage = true;
},
[types.SET_SELECTED_STAGE_DATA](state, initData) {
const data = convertObjectPropsToCamelCase(initData);
const { const {
title: name, id,
title: name, // TODO: do we still need to do this?
startEventIdentifier, startEventIdentifier,
endEventIdentifier, endEventIdentifier,
startEventLabelId, startEventLabelId,
endEventLabelId, endEventLabelId,
} = initData; } = data;
state.isEditingCustomStage = true; // TODO: remove setSelectedStageId action in favour of passing the id into this + a getter
state.customStageFormInitData = { state.customStageFormInitData = {
...state.customStageFormInitData, ...state.customStageFormInitData,
id,
name, name,
startEventIdentifier, startEventIdentifier,
endEventIdentifier, endEventIdentifier,
...@@ -100,14 +113,6 @@ export default { ...@@ -100,14 +113,6 @@ export default {
endEventLabelId, endEventLabelId,
}; };
}, },
[types.HIDE_CUSTOM_STAGE_FORM](state) {
state.isEditingCustomStage = false;
state.isCreatingCustomStage = false;
state.customStageFormInitData = {};
},
[types.SHOW_CUSTOM_STAGE_FORM](state) {
state.isAddingCustomStage = true;
},
[types.RECEIVE_SUMMARY_DATA_ERROR](state) { [types.RECEIVE_SUMMARY_DATA_ERROR](state) {
state.summary = []; state.summary = [];
}, },
...@@ -179,6 +184,7 @@ export default { ...@@ -179,6 +184,7 @@ export default {
}, },
[types.RECEIVE_UPDATE_STAGE_RESPONSE](state) { [types.RECEIVE_UPDATE_STAGE_RESPONSE](state) {
state.isLoading = false; state.isLoading = false;
state.isSavingCustomStage = false;
}, },
[types.REQUEST_REMOVE_STAGE](state) { [types.REQUEST_REMOVE_STAGE](state) {
state.isLoading = true; state.isLoading = true;
......
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CustomStageForm does not have a loading icon 1`] = ` exports[`CustomStageForm Editing a custom stage isSavingCustomStage=true displays a loading icon 1`] = `
"<button type=\\"button\\" class=\\"js-custom-stage-form-submit btn btn-success\\"><!----> "<button disabled=\\"disabled\\" type=\\"button\\" class=\\"js-save-stage btn btn-success\\"><span class=\\"gl-spinner-container\\"><span aria-label=\\"Loading\\" aria-hidden=\\"true\\" class=\\"gl-spinner gl-spinner-orange gl-spinner-sm\\"></span></span>
Add stage Update stage
</button>" </button>"
`; `;
exports[`CustomStageForm isSavingCustomStage=true displays a loading icon 1`] = ` exports[`CustomStageForm Empty form isSavingCustomStage=true displays a loading icon 1`] = `
"<button disabled=\\"disabled\\" type=\\"button\\" class=\\"js-custom-stage-form-submit btn btn-success\\"><span class=\\"gl-spinner-container\\"><span aria-label=\\"Loading\\" aria-hidden=\\"true\\" class=\\"gl-spinner gl-spinner-orange gl-spinner-sm\\"></span></span> "<button disabled=\\"disabled\\" type=\\"button\\" class=\\"js-save-stage btn btn-success\\"><span class=\\"gl-spinner-container\\"><span aria-label=\\"Loading\\" aria-hidden=\\"true\\" class=\\"gl-spinner gl-spinner-orange gl-spinner-sm\\"></span></span>
Add stage Add stage
</button>" </button>"
`; `;
...@@ -12,6 +12,7 @@ import { ...@@ -12,6 +12,7 @@ import {
} from '../mock_data'; } from '../mock_data';
const initData = { const initData = {
id: 74,
name: 'Cool stage pre', name: 'Cool stage pre',
startEventIdentifier: labelStartEvent.identifier, startEventIdentifier: labelStartEvent.identifier,
startEventLabelId: groupLabels[0].id, startEventLabelId: groupLabels[0].id,
...@@ -35,11 +36,11 @@ describe('CustomStageForm', () => { ...@@ -35,11 +36,11 @@ describe('CustomStageForm', () => {
const findEvent = ev => wrapper.emitted()[ev]; const findEvent = ev => wrapper.emitted()[ev];
const sel = { const sel = {
name: '[name="add-stage-name"]', name: '[name="custom-stage-name"]',
startEvent: '[name="add-stage-start-event"]', startEvent: '[name="custom-stage-start-event"]',
startEventLabel: '[name="add-stage-start-event-label"]', startEventLabel: '[name="custom-stage-start-event-label"]',
endEvent: '[name="add-stage-stop-event"]', endEvent: '[name="custom-stage-stop-event"]',
endEventLabel: '[name="add-stage-stop-event-label"]', endEventLabel: '[name="custom-stage-stop-event-label"]',
submit: '.js-save-stage', submit: '.js-save-stage',
cancel: '.js-save-stage-cancel', cancel: '.js-save-stage-cancel',
invalidFeedback: '.invalid-feedback', invalidFeedback: '.invalid-feedback',
...@@ -360,7 +361,7 @@ describe('CustomStageForm', () => { ...@@ -360,7 +361,7 @@ describe('CustomStageForm', () => {
wrapper.destroy(); wrapper.destroy();
}); });
it('has text `Edit stage`', () => { it('has text `Add stage`', () => {
expect(wrapper.find(sel.submit).text('value')).toEqual('Add stage'); expect(wrapper.find(sel.submit).text('value')).toEqual('Add stage');
}); });
...@@ -395,21 +396,25 @@ describe('CustomStageForm', () => { ...@@ -395,21 +396,25 @@ describe('CustomStageForm', () => {
wrapper.destroy(); wrapper.destroy();
}); });
it(`emits a ${STAGE_ACTIONS.SAVE} event when clicked`, () => { it(`emits a ${STAGE_ACTIONS.CREATE} event when clicked`, done => {
let event = findEvent(STAGE_ACTIONS.SAVE); let event = findEvent(STAGE_ACTIONS.CREATE);
expect(event).toBeUndefined(); expect(event).toBeUndefined();
wrapper.find(sel.submit).trigger('click'); wrapper.find(sel.submit).trigger('click');
event = findEvent(STAGE_ACTIONS.SAVE);
expect(event).toBeTruthy(); Vue.nextTick(() => {
expect(event.length).toEqual(1); event = findEvent(STAGE_ACTIONS.CREATE);
expect(event).toBeTruthy();
expect(event.length).toEqual(1);
done();
});
}); });
it('`submit` event receives the latest data', () => { it(`${STAGE_ACTIONS.CREATE} event receives the latest data`, () => {
const startEv = startEvents[startEventIndex]; const startEv = startEvents[startEventIndex];
const selectedStopEvent = getDropdownOption(wrapper, sel.stopEvent, stopEventIndex); const selectedStopEvent = getDropdownOption(wrapper, sel.stopEvent, stopEventIndex);
let event = findEvent(STAGE_ACTIONS.SAVE); let event = findEvent(STAGE_ACTIONS.CREATE);
expect(event).toBeUndefined(); expect(event).toBeUndefined();
const res = [ const res = [
...@@ -423,7 +428,7 @@ describe('CustomStageForm', () => { ...@@ -423,7 +428,7 @@ describe('CustomStageForm', () => {
]; ];
wrapper.find(sel.submit).trigger('click'); wrapper.find(sel.submit).trigger('click');
event = findEvent(STAGE_ACTIONS.SAVE); event = findEvent(STAGE_ACTIONS.CREATE);
expect(event[0]).toEqual(res); expect(event[0]).toEqual(res);
}); });
}); });
...@@ -464,6 +469,7 @@ describe('CustomStageForm', () => { ...@@ -464,6 +469,7 @@ describe('CustomStageForm', () => {
Vue.nextTick(() => { Vue.nextTick(() => {
expect(wrapper.vm.fields).toEqual({ expect(wrapper.vm.fields).toEqual({
id: null,
name: null, name: null,
startEventIdentifier: null, startEventIdentifier: null,
startEventLabelId: null, startEventLabelId: null,
...@@ -497,6 +503,21 @@ describe('CustomStageForm', () => { ...@@ -497,6 +503,21 @@ describe('CustomStageForm', () => {
}); });
}); });
}); });
describe('isSavingCustomStage=true', () => {
beforeEach(() => {
wrapper = createComponent(
{
isSavingCustomStage: true,
},
false,
);
});
it('displays a loading icon', () => {
expect(wrapper.find(sel.submit).html()).toMatchSnapshot();
});
});
}); });
describe('Editing a custom stage', () => { describe('Editing a custom stage', () => {
...@@ -547,9 +568,9 @@ describe('CustomStageForm', () => { ...@@ -547,9 +568,9 @@ describe('CustomStageForm', () => {
}); });
}); });
describe('Edit stage button', () => { describe('Update stage button', () => {
it('has text `Edit stage`', () => { it('has text `Update stage`', () => {
expect(wrapper.find(sel.submit).text('value')).toEqual('Edit stage'); expect(wrapper.find(sel.submit).text('value')).toEqual('Update stage');
}); });
it('is disabled by default', () => { it('is disabled by default', () => {
...@@ -632,24 +653,24 @@ describe('CustomStageForm', () => { ...@@ -632,24 +653,24 @@ describe('CustomStageForm', () => {
}); });
}); });
}); });
});
it('does not have a loading icon', () => {
expect(wrapper.find(sel.submit).html()).toMatchSnapshot();
});
describe('isSavingCustomStage=true', () => { describe('isSavingCustomStage=true', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent( wrapper = createComponent(
{ {
isSavingCustomStage: true, isEditingCustomStage: true,
}, initialFields: {
false, ...initData,
); },
}); isSavingCustomStage: true,
},
false,
);
});
it('displays a loading icon', () => { it('displays a loading icon', () => {
expect(wrapper.find(sel.submit).html()).toMatchSnapshot(); expect(wrapper.find(sel.submit).html()).toMatchSnapshot();
});
}); });
}); });
}); });
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