Commit 7256e544 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Correctly display form field errors

Fix correctly initialize edit form data

Ensures we initialize the form with values
when we are editing correctly

Minor fixup mutation specs
parent 6fbee9fa
...@@ -14,7 +14,7 @@ import { ...@@ -14,7 +14,7 @@ import {
getLabelEventsIdentifiers, getLabelEventsIdentifiers,
} from '../utils'; } from '../utils';
const initFields = { const defaultFields = {
id: null, id: null,
name: null, name: null,
startEventIdentifier: null, startEventIdentifier: null,
...@@ -58,18 +58,22 @@ export default { ...@@ -58,18 +58,22 @@ export default {
errors: { errors: {
type: Object, type: Object,
required: false, required: false,
default: null, default: () => {},
}, },
}, },
data() { data() {
return { return {
fields: { labelEvents: getLabelEventsIdentifiers(this.events),
...initFields, fields: {},
...this.initialFields,
},
}; };
}, },
computed: { computed: {
defaultFieldData() {
return {
...defaultFields,
...this.initialFields,
};
},
startEventOptions() { startEventOptions() {
return [ return [
{ value: null, text: s__('CustomCycleAnalytics|Select start event') }, { value: null, text: s__('CustomCycleAnalytics|Select start event') },
...@@ -118,7 +122,7 @@ export default { ...@@ -118,7 +122,7 @@ export default {
); );
}, },
isDirty() { isDirty() {
return !isEqual(this.initialFields, this.fields) && !isEqual(initFields, this.fields); return !isEqual(this.initialFields, this.fields) && !isEqual(defaultFields, this.fields);
}, },
hasValidStartAndEndEventPair() { hasValidStartAndEndEventPair() {
const { const {
...@@ -147,14 +151,26 @@ export default { ...@@ -147,14 +151,26 @@ export default {
}, },
}, },
mounted() { mounted() {
this.labelEvents = getLabelEventsIdentifiers(this.events); this.resetFormFields();
}, },
// updated() {
// this.resetFormFields();
// },
methods: { methods: {
resetFormFields() {
this.fields = this.defaultFieldData;
// console.log('this.fields', this.fields);
// console.log('this.initialFields', this.initialFields);
// console.log('defaultFields', defaultFields);
// Object.entries(this.defaultFieldData).
// for (let [key, value] of Object.entries(this.defaultFieldData)) {
// // console.log('setting', key, value);
// this.$set(this.fields, key, value);
// }
// console.log('this.fields', this.fields);
},
handleCancel() { handleCancel() {
this.fields = { this.resetFormFields();
...initFields,
...this.initialFields,
};
this.$emit('cancel'); this.$emit('cancel');
}, },
handleSave() { handleSave() {
...@@ -245,7 +261,7 @@ export default { ...@@ -245,7 +261,7 @@ export default {
!hasStartEvent ? s__('CustomCycleAnalytics|Please select a start event first') : '' !hasStartEvent ? s__('CustomCycleAnalytics|Please select a start event first') : ''
" "
:state="isValid('endEventIdentifier')" :state="isValid('endEventIdentifier')"
:invalid-feedback="fieldErrors('endEventIdentifier')" :invalid-feedback="fieldErrors('endEventIdentifier') || endEventError"
> >
<!-- :state="hasValidStartAndEndEventPair" <!-- :state="hasValidStartAndEndEventPair"
:invalid-feedback="endEventError" --> :invalid-feedback="endEventError" -->
......
...@@ -66,7 +66,7 @@ export default { ...@@ -66,7 +66,7 @@ export default {
customStageFormErrors: { customStageFormErrors: {
type: Object, type: Object,
required: false, required: false,
default: null, default: () => {},
}, },
labels: { labels: {
type: Array, type: Array,
......
...@@ -241,18 +241,16 @@ export const clearCustomStageFormErrors = ({ commit }) => ...@@ -241,18 +241,16 @@ export const clearCustomStageFormErrors = ({ commit }) =>
export const requestCreateCustomStage = ({ commit }) => commit(types.REQUEST_CREATE_CUSTOM_STAGE); export const requestCreateCustomStage = ({ commit }) => commit(types.REQUEST_CREATE_CUSTOM_STAGE);
export const receiveCreateCustomStageSuccess = ({ commit, dispatch }, { data: { title } }) => { export const receiveCreateCustomStageSuccess = ({ commit, dispatch }, { data: { title } }) => {
commit(types.RECEIVE_CREATE_CUSTOM_STAGE_RESPONSE); commit(types.RECEIVE_CREATE_CUSTOM_STAGE_SUCCESS);
createFlash(__(`Your custom stage '${title}' was created`), 'notice'); createFlash(__(`Your custom stage '${title}' was created`), 'notice');
return dispatch('fetchGroupStagesAndEvents').then(() => dispatch('fetchSummaryData')); return dispatch('fetchGroupStagesAndEvents').then(() => dispatch('fetchSummaryData'));
}; };
export const receiveCreateCustomStageError = ({ commit }, { status, message, errors, data }) => { export const receiveCreateCustomStageError = ({ commit }, { status, message, errors, data }) => {
commit(types.RECEIVE_CREATE_CUSTOM_STAGE_RESPONSE, { status, message, errors, data }); commit(types.RECEIVE_CREATE_CUSTOM_STAGE_ERROR, { status, message, errors, data });
const { name } = data; const { name } = data;
// TODO: check for 403, 422 etc
// Follow up issue to investigate https://gitlab.com/gitlab-org/gitlab/issues/36685
const flashMessage = const flashMessage =
status !== httpStatus.UNPROCESSABLE_ENTITY status !== httpStatus.UNPROCESSABLE_ENTITY
? __(`'${name}' stage already exists'`) ? __(`'${name}' stage already exists'`)
...@@ -322,7 +320,7 @@ export const fetchTasksByTypeData = ({ dispatch, state, getters }) => { ...@@ -322,7 +320,7 @@ 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 }, updatedData) => { export const receiveUpdateStageSuccess = ({ commit, dispatch }, updatedData) => {
commit(types.RECEIVE_UPDATE_STAGE_RESPONSE); commit(types.RECEIVE_UPDATE_STAGE_SUCCESS);
createFlash(__('Stage data updated'), 'notice'); createFlash(__('Stage data updated'), 'notice');
dispatch('fetchGroupStagesAndEvents'); dispatch('fetchGroupStagesAndEvents');
...@@ -333,7 +331,7 @@ export const receiveUpdateStageError = ( ...@@ -333,7 +331,7 @@ export const receiveUpdateStageError = (
{ commit }, { commit },
{ error: { response: { status = 400, data: errorData } = {} } = {}, data }, { error: { response: { status = 400, data: errorData } = {} } = {}, data },
) => { ) => {
commit(types.RECEIVE_UPDATE_STAGE_RESPONSE); commit(types.RECEIVE_UPDATE_STAGE_ERROR);
const ERROR_NAME_RESERVED = 'is reserved'; const ERROR_NAME_RESERVED = 'is reserved';
let message = __('There was a problem saving your custom stage, please try again'); let message = __('There was a problem saving your custom stage, please try again');
......
...@@ -48,7 +48,8 @@ export const RECEIVE_CREATE_CUSTOM_STAGE_SUCCESS = 'RECEIVE_CREATE_CUSTOM_STAGE_ ...@@ -48,7 +48,8 @@ export const RECEIVE_CREATE_CUSTOM_STAGE_SUCCESS = 'RECEIVE_CREATE_CUSTOM_STAGE_
export const RECEIVE_CREATE_CUSTOM_STAGE_ERROR = 'RECEIVE_CREATE_CUSTOM_STAGE_ERROR'; export const RECEIVE_CREATE_CUSTOM_STAGE_ERROR = 'RECEIVE_CREATE_CUSTOM_STAGE_ERROR';
export const REQUEST_UPDATE_STAGE = 'REQUEST_UPDATE_STAGE'; export const REQUEST_UPDATE_STAGE = 'REQUEST_UPDATE_STAGE';
export const RECEIVE_UPDATE_STAGE_RESPONSE = 'RECEIVE_UPDATE_STAGE_RESPONSE'; export const RECEIVE_UPDATE_STAGE_SUCCESS = 'RECEIVE_UPDATE_STAGE_SUCCESS';
export const RECEIVE_UPDATE_STAGE_ERROR = 'RECEIVE_UPDATE_STAGE_ERROR';
export const REQUEST_REMOVE_STAGE = 'REQUEST_REMOVE_STAGE'; export const REQUEST_REMOVE_STAGE = 'REQUEST_REMOVE_STAGE';
export const RECEIVE_REMOVE_STAGE_RESPONSE = 'RECEIVE_REMOVE_STAGE_RESPONSE'; export const RECEIVE_REMOVE_STAGE_RESPONSE = 'RECEIVE_REMOVE_STAGE_RESPONSE';
......
...@@ -96,18 +96,15 @@ export default { ...@@ -96,18 +96,15 @@ export default {
}, },
[types.SHOW_CUSTOM_STAGE_FORM](state) { [types.SHOW_CUSTOM_STAGE_FORM](state) {
state.isCreatingCustomStage = true; state.isCreatingCustomStage = true;
state.customStageFormInitData = {}; state.isEditingCustomStage = false;
}, },
[types.EDIT_CUSTOM_STAGE](state) { [types.EDIT_CUSTOM_STAGE](state) {
state.isEditingCustomStage = true; state.isEditingCustomStage = true;
state.isCreatingCustomStage = false;
}, },
[types.HIDE_CUSTOM_STAGE_FORM](state) { [types.HIDE_CUSTOM_STAGE_FORM](state) {
state.isEditingCustomStage = false; state.isEditingCustomStage = false;
state.isCreatingCustomStage = false; state.isCreatingCustomStage = false;
state.customStageFormInitData = {};
},
[types.SHOW_CUSTOM_STAGE_FORM](state) {
state.isCreatingCustomStage = true;
}, },
[types.CLEAR_CUSTOM_STAGE_FORM_ERRORS](state) { [types.CLEAR_CUSTOM_STAGE_FORM_ERRORS](state) {
state.customStageFormErrors = null; state.customStageFormErrors = null;
...@@ -155,20 +152,31 @@ export default { ...@@ -155,20 +152,31 @@ export default {
}, },
[types.REQUEST_CREATE_CUSTOM_STAGE](state) { [types.REQUEST_CREATE_CUSTOM_STAGE](state) {
state.isSavingCustomStage = true; state.isSavingCustomStage = true;
state.customStageFormErrors = {};
}, },
[types.RECEIVE_CREATE_CUSTOM_STAGE_ERROR](state, { errors = null }) { [types.RECEIVE_CREATE_CUSTOM_STAGE_ERROR](state, { errors = null } = {}) {
state.isSavingCustomStage = false; state.isSavingCustomStage = false;
state.customStageFormErrors = errors; state.customStageFormErrors = convertObjectPropsToCamelCase(errors, { deep: true });
}, },
[types.RECEIVE_CREATE_CUSTOM_STAGE_SUCCESS](state) { [types.RECEIVE_CREATE_CUSTOM_STAGE_SUCCESS](state) {
state.isSavingCustomStage = false; state.isSavingCustomStage = false;
state.customStageFormErrors = {};
}, },
[types.REQUEST_UPDATE_STAGE](state) { [types.REQUEST_UPDATE_STAGE](state) {
state.isLoading = true; state.isLoading = true;
state.isSavingCustomStage = true;
state.customStageFormErrors = {};
},
[types.RECEIVE_UPDATE_STAGE_SUCCESS](state) {
state.isLoading = false;
state.isSavingCustomStage = false;
state.isEditingCustomStage = false;
state.customStageFormErrors = {};
}, },
[types.RECEIVE_UPDATE_STAGE_RESPONSE](state) { [types.RECEIVE_UPDATE_STAGE_ERROR](state, { errors = null } = {}) {
state.isLoading = false; state.isLoading = false;
state.isSavingCustomStage = false; state.isSavingCustomStage = false;
state.customStageFormErrors = convertObjectPropsToCamelCase(errors, { deep: true });
}, },
[types.REQUEST_REMOVE_STAGE](state) { [types.REQUEST_REMOVE_STAGE](state) {
state.isLoading = true; state.isLoading = true;
......
...@@ -31,7 +31,7 @@ export default () => ({ ...@@ -31,7 +31,7 @@ export default () => ({
medians: {}, medians: {},
customStageFormEvents: [], customStageFormEvents: [],
customStageFormErrors: null, customStageFormErrors: {},
tasksByType: { tasksByType: {
subject: TASKS_BY_TYPE_SUBJECT_ISSUE, subject: TASKS_BY_TYPE_SUBJECT_ISSUE,
......
import Vue from 'vue'; import Vue from 'vue';
import { mount } from '@vue/test-utils'; import { shallowMount, mount } from '@vue/test-utils';
import CustomStageForm from 'ee/analytics/cycle_analytics/components/custom_stage_form.vue'; import CustomStageForm from 'ee/analytics/cycle_analytics/components/custom_stage_form.vue';
import { STAGE_ACTIONS } from 'ee/analytics/cycle_analytics/constants'; import { STAGE_ACTIONS } from 'ee/analytics/cycle_analytics/constants';
import { import {
...@@ -21,11 +21,14 @@ const initData = { ...@@ -21,11 +21,14 @@ const initData = {
}; };
describe('CustomStageForm', () => { describe('CustomStageForm', () => {
function createComponent(props) { function createComponent(props, shallow = false) {
return mount(CustomStageForm, { const func = shallow ? shallowMount : mount;
return func(CustomStageForm, {
propsData: { propsData: {
events, events,
labels: groupLabels, labels: groupLabels,
errors: {},
initialFields: {},
...props, ...props,
}, },
}); });
...@@ -248,7 +251,7 @@ describe('CustomStageForm', () => { ...@@ -248,7 +251,7 @@ describe('CustomStageForm', () => {
describe('with a stop event selected and a change to the start event', () => { describe('with a stop event selected and a change to the start event', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({}, false); wrapper = createComponent({});
wrapper.setData({ wrapper.setData({
fields: { fields: {
...@@ -299,7 +302,7 @@ describe('CustomStageForm', () => { ...@@ -299,7 +302,7 @@ describe('CustomStageForm', () => {
describe('Stop event label', () => { describe('Stop event label', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({}, false); wrapper = createComponent({});
}); });
afterEach(() => { afterEach(() => {
...@@ -353,24 +356,32 @@ describe('CustomStageForm', () => { ...@@ -353,24 +356,32 @@ describe('CustomStageForm', () => {
}); });
}); });
describe('Add stage button', () => { describe.only('Add stage button', () => {
beforeEach(() => { beforeEach(done => {
wrapper = createComponent({}, false); wrapper = createComponent({}, true);
selectDropdownOption(wrapper, sel.startEvent, 1); selectDropdownOption(wrapper, sel.startEvent, 1);
return Vue.nextTick(() => { wrapper.vm
selectDropdownOption(wrapper, sel.endEvent, 1); .$nextTick()
return Vue.nextTick(); .then(() => {
}); selectDropdownOption(wrapper, sel.endEvent, 1);
})
.then(wrapper.vm.$nextTick)
.then(done)
.catch(err => {
console.log('ERRRRRRR', err);
done.fail();
});
}); });
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
}); });
it('has text `Add stage`', () => { it.only('has text `Add stage`', () => {
expect(wrapper.find(sel.submit).text('value')).toEqual('Add stage'); const txt = wrapper.find(sel.submit).text();
expect(txt).toEqual('Add stage');
}); });
it('is enabled when all required fields are filled', done => { it('is enabled when all required fields are filled', done => {
...@@ -391,7 +402,7 @@ describe('CustomStageForm', () => { ...@@ -391,7 +402,7 @@ describe('CustomStageForm', () => {
const stopEventIndex = 1; const stopEventIndex = 1;
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({}, false); wrapper = createComponent({});
selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex); selectDropdownOption(wrapper, sel.startEvent, startEventDropdownIndex);
...@@ -452,7 +463,7 @@ describe('CustomStageForm', () => { ...@@ -452,7 +463,7 @@ describe('CustomStageForm', () => {
describe('Cancel button', () => { describe('Cancel button', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({}, false); wrapper = createComponent({});
}); });
afterEach(() => { afterEach(() => {
......
...@@ -54,11 +54,18 @@ describe('Cycle analytics mutations', () => { ...@@ -54,11 +54,18 @@ describe('Cycle analytics mutations', () => {
${types.REQUEST_CREATE_CUSTOM_STAGE} | ${'isSavingCustomStage'} | ${true} ${types.REQUEST_CREATE_CUSTOM_STAGE} | ${'isSavingCustomStage'} | ${true}
${types.RECEIVE_CREATE_CUSTOM_STAGE_SUCCESS} | ${'isSavingCustomStage'} | ${false} ${types.RECEIVE_CREATE_CUSTOM_STAGE_SUCCESS} | ${'isSavingCustomStage'} | ${false}
${types.RECEIVE_CREATE_CUSTOM_STAGE_ERROR} | ${'isSavingCustomStage'} | ${false} ${types.RECEIVE_CREATE_CUSTOM_STAGE_ERROR} | ${'isSavingCustomStage'} | ${false}
${types.RECEIVE_CREATE_CUSTOM_STAGE_ERROR} | ${'customStageFormErrors'} | ${{ errors: [] }} ${types.RECEIVE_CREATE_CUSTOM_STAGE_ERROR} | ${'customStageFormErrors'} | ${{}}
${types.REQUEST_TASKS_BY_TYPE_DATA} | ${'isLoadingTasksByTypeChart'} | ${true} ${types.REQUEST_TASKS_BY_TYPE_DATA} | ${'isLoadingTasksByTypeChart'} | ${true}
${types.RECEIVE_TASKS_BY_TYPE_DATA_ERROR} | ${'isLoadingTasksByTypeChart'} | ${false} ${types.RECEIVE_TASKS_BY_TYPE_DATA_ERROR} | ${'isLoadingTasksByTypeChart'} | ${false}
${types.REQUEST_UPDATE_STAGE} | ${'isLoading'} | ${true} ${types.REQUEST_UPDATE_STAGE} | ${'isLoading'} | ${true}
${types.RECEIVE_UPDATE_STAGE_RESPONSE} | ${'isLoading'} | ${false} ${types.REQUEST_UPDATE_STAGE} | ${'isSavingCustomStage'} | ${true}
${types.REQUEST_UPDATE_STAGE} | ${'customStageFormErrors'} | ${{}}
${types.RECEIVE_UPDATE_STAGE_SUCCESS} | ${'isLoading'} | ${false}
${types.RECEIVE_UPDATE_STAGE_SUCCESS} | ${'isSavingCustomStage'} | ${false}
${types.RECEIVE_UPDATE_STAGE_SUCCESS} | ${'isEditingCustomStage'} | ${false}
${types.RECEIVE_UPDATE_STAGE_SUCCESS} | ${'customStageFormErrors'} | ${{}}
${types.RECEIVE_UPDATE_STAGE_ERROR} | ${'isLoading'} | ${false}
${types.RECEIVE_UPDATE_STAGE_ERROR} | ${'isSavingCustomStage'} | ${false}
${types.REQUEST_REMOVE_STAGE} | ${'isLoading'} | ${true} ${types.REQUEST_REMOVE_STAGE} | ${'isLoading'} | ${true}
${types.RECEIVE_REMOVE_STAGE_RESPONSE} | ${'isLoading'} | ${false} ${types.RECEIVE_REMOVE_STAGE_RESPONSE} | ${'isLoading'} | ${false}
${types.REQUEST_DURATION_DATA} | ${'isLoadingDurationChart'} | ${true} ${types.REQUEST_DURATION_DATA} | ${'isLoadingDurationChart'} | ${true}
...@@ -118,6 +125,18 @@ describe('Cycle analytics mutations', () => { ...@@ -118,6 +125,18 @@ describe('Cycle analytics mutations', () => {
}); });
}); });
describe(`types.RECEIVE_UPDATE_STAGE_ERROR`, () => {
const mockFormError = { errors: { start_identifier: ['Cant be blank'] } };
it('will set customStageFormErrors', () => {
state = {};
mutations[types.RECEIVE_UPDATE_STAGE_ERROR](state, mockFormError);
expect(state.customStageFormErrors).toEqual(
convertObjectPropsToCamelCase(mockFormError.errors),
);
});
});
describe.each` describe.each`
mutation | value mutation | value
${types.REQUEST_GROUP_LABELS} | ${[]} ${types.REQUEST_GROUP_LABELS} | ${[]}
......
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