From 8af1ad09d0018fa211b53ba59cd05a8497535874 Mon Sep 17 00:00:00 2001 From: David Craig Date: Tue, 26 Apr 2016 14:31:37 +1200 Subject: [PATCH] Add loading indicator to save button on scaffolded forms --- .../src/components/FormAction/FormAction.js | 2 +- .../src/components/FormBuilder/FormBuilder.js | 4 +- .../components/{toolbar => Toolbar}/README.md | 0 .../{toolbar => Toolbar}/Toolbar.js | 0 .../{toolbar => Toolbar}/Toolbar.scss | 0 admin/client/src/state/form/FormActions.js | 4 +- admin/client/src/state/form/FormReducer.js | 16 +++++- .../src/state/form/tests/FormsReducer-test.js | 56 +++++++++++++++++++ 8 files changed, 77 insertions(+), 5 deletions(-) rename admin/client/src/components/{toolbar => Toolbar}/README.md (100%) rename admin/client/src/components/{toolbar => Toolbar}/Toolbar.js (100%) rename admin/client/src/components/{toolbar => Toolbar}/Toolbar.scss (100%) diff --git a/admin/client/src/components/FormAction/FormAction.js b/admin/client/src/components/FormAction/FormAction.js index 7ada94c81..b80bb6c84 100644 --- a/admin/client/src/components/FormAction/FormAction.js +++ b/admin/client/src/components/FormAction/FormAction.js @@ -72,7 +72,7 @@ class FormAction extends SilverStripeComponent { * @returns object|null */ getLoadingIcon() { - if (this.props.loading) { + if (this.props.loading === true) { return (
diff --git a/admin/client/src/components/FormBuilder/FormBuilder.js b/admin/client/src/components/FormBuilder/FormBuilder.js index 45e4aeea3..9a28e3757 100644 --- a/admin/client/src/components/FormBuilder/FormBuilder.js +++ b/admin/client/src/components/FormBuilder/FormBuilder.js @@ -316,6 +316,7 @@ export class FormBuilderComponent extends SilverStripeComponent { */ mapActionsToComponents(actions) { const createFn = this.props.createFn; + const form = this.props.form[this.getFormId()]; return actions.map((action, i) => { let props = deepFreeze(action); @@ -327,6 +328,7 @@ export class FormBuilderComponent extends SilverStripeComponent { type: 'submit', label: props.title, icon: 'save', + loading: typeof form !== 'undefined' ? form.submitting : false, bootstrapButtonStyle: 'success', }, props)); break; @@ -401,7 +403,7 @@ export class FormBuilderComponent extends SilverStripeComponent { // If there is structural and state data availabe merge those data for each field. // Otherwise just use the structural data. - const fieldData = formSchema.schema && formState + const fieldData = formSchema.schema && formState && formState.fields ? formSchema.schema.fields.map((f, i) => this.mergeFieldData(f, formState.fields[i])) : formSchema.schema.fields; diff --git a/admin/client/src/components/toolbar/README.md b/admin/client/src/components/Toolbar/README.md similarity index 100% rename from admin/client/src/components/toolbar/README.md rename to admin/client/src/components/Toolbar/README.md diff --git a/admin/client/src/components/toolbar/Toolbar.js b/admin/client/src/components/Toolbar/Toolbar.js similarity index 100% rename from admin/client/src/components/toolbar/Toolbar.js rename to admin/client/src/components/Toolbar/Toolbar.js diff --git a/admin/client/src/components/toolbar/Toolbar.scss b/admin/client/src/components/Toolbar/Toolbar.scss similarity index 100% rename from admin/client/src/components/toolbar/Toolbar.scss rename to admin/client/src/components/Toolbar/Toolbar.scss diff --git a/admin/client/src/state/form/FormActions.js b/admin/client/src/state/form/FormActions.js index dd2034df2..8ef53132e 100644 --- a/admin/client/src/state/form/FormActions.js +++ b/admin/client/src/state/form/FormActions.js @@ -59,7 +59,7 @@ export function submitForm(submitApi, formId, fieldValues) { return (dispatch) => { dispatch({ type: ACTION_TYPES.SUBMIT_FORM_REQUEST, - payload: {}, + payload: { formId }, }); submitApi(Object.assign({ ID: formId }, fieldValues), { 'X-Formschema-Request': 'state' }) @@ -72,7 +72,7 @@ export function submitForm(submitApi, formId, fieldValues) { .catch((error) => { dispatch({ type: ACTION_TYPES.SUBMIT_FORM_FAILURE, - payload: { error }, + payload: { formId, error }, }); }); }; diff --git a/admin/client/src/state/form/FormReducer.js b/admin/client/src/state/form/FormReducer.js index 5d1cffa7f..ef6996fad 100644 --- a/admin/client/src/state/form/FormReducer.js +++ b/admin/client/src/state/form/FormReducer.js @@ -6,6 +6,11 @@ const initialState = deepFreeze({}); function formReducer(state = initialState, action) { switch (action.type) { + case ACTION_TYPES.SUBMIT_FORM_REQUEST: + return deepFreeze(Object.assign({}, state, { + [action.payload.formId]: { submitting: true }, + })); + case ACTION_TYPES.REMOVE_FORM: return deepFreeze(Object.keys(state).reduce((previous, current) => { if (current === action.payload.formId) { @@ -18,7 +23,10 @@ function formReducer(state = initialState, action) { case ACTION_TYPES.ADD_FORM: return deepFreeze(Object.assign({}, state, { - [action.payload.formState.id]: { fields: action.payload.formState.fields }, + [action.payload.formState.id]: { + fields: action.payload.formState.fields, + submitting: false, + }, })); case ACTION_TYPES.UPDATE_FIELD: @@ -38,9 +46,15 @@ function formReducer(state = initialState, action) { [action.payload.response.id]: { fields: action.payload.response.state.fields, messages: action.payload.response.state.messages, + submitting: false, }, })); + case ACTION_TYPES.SUBMIT_FORM_FAILURE: + return deepFreeze(Object.assign({}, state, { + [action.payload.formId]: { submitting: false }, + })); + default: return state; diff --git a/admin/client/src/state/form/tests/FormsReducer-test.js b/admin/client/src/state/form/tests/FormsReducer-test.js index c003c4530..7bf2e814a 100644 --- a/admin/client/src/state/form/tests/FormsReducer-test.js +++ b/admin/client/src/state/form/tests/FormsReducer-test.js @@ -21,6 +21,7 @@ describe('formReducer', () => { value: 'Test', }, ], + submitting: false, }, }); @@ -54,6 +55,7 @@ describe('formReducer', () => { expect(nextState.EditForm.fields[0].messages).toBeDefined(); expect(nextState.EditForm.fields[0].valid).toBe(true); expect(nextState.EditForm.fields[0].value).toBe('Test'); + expect(nextState.EditForm.submitting).toBe(false); }); }); @@ -137,6 +139,7 @@ describe('formReducer', () => { value: 'Test', }, ], + submitting: true, }, }); @@ -172,6 +175,59 @@ describe('formReducer', () => { expect(nextState.DetailEditForm.messages.length).toBe(1); expect(nextState.DetailEditForm.messages[0].type).toBe('good'); expect(nextState.DetailEditForm.messages[0].value).toBe('Saved.'); + expect(nextState.DetailEditForm.submitting).toBe(false); + }); + }); + + describe('SUBMIT_FORM_REQUEST', () => { + it('should set submitting to true', () => { + const initialState = deepFreeze({ + DetailEditForm: { + fields: [ + { + data: [], + id: 'Form_DetailEditForm_Name', + messages: [], + valid: true, + value: 'Test', + }, + ], + submitting: false, + }, + }); + + const nextState = formReducer(initialState, { + type: ACTION_TYPES.SUBMIT_FORM_REQUEST, + payload: { formId: 'DetailEditForm' }, + }); + + expect(nextState.DetailEditForm.submitting).toBe(true); + }); + }); + + describe('SUBMIT_FORM_FAILURE', () => { + it('should set submitting to false', () => { + const initialState = deepFreeze({ + DetailEditForm: { + fields: [ + { + data: [], + id: 'Form_DetailEditForm_Name', + messages: [], + valid: true, + value: 'Test', + }, + ], + submitting: true, + }, + }); + + const nextState = formReducer(initialState, { + type: ACTION_TYPES.SUBMIT_FORM_FAILURE, + payload: { formId: 'DetailEditForm' }, + }); + + expect(nextState.DetailEditForm.submitting).toBe(false); }); }); });