From b4e6d498c9dc3d3de09aedf62d73c0b7ab6e1814 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Wed, 23 Mar 2016 23:16:03 +1300 Subject: [PATCH] Simplified form schema structure and mocks Keyed by URL instead of anonymous object maps which need to be iterated on Removed the 'schema.forms' namespace, unnecessary since all 'schema' items should be forms --- admin/code/CampaignAdmin.php | 32 ++++++++------ admin/javascript/src/boot/index.js | 2 + .../src/components/form-builder/README.md | 2 +- .../src/components/form-builder/index.js | 43 ++++++++----------- .../form-builder/tests/form-builder-test.js | 2 +- admin/javascript/src/components/form/index.js | 4 +- .../src/sections/campaign-admin/controller.js | 13 +++--- admin/javascript/src/state/schema/actions.js | 2 +- admin/javascript/src/state/schema/reducer.js | 21 ++------- .../Includes/CampaignAdmin_Content.ss | 2 + 10 files changed, 58 insertions(+), 65 deletions(-) create mode 100644 admin/templates/Includes/CampaignAdmin_Content.ss diff --git a/admin/code/CampaignAdmin.php b/admin/code/CampaignAdmin.php index 37b7e0897..572b3d7ad 100644 --- a/admin/code/CampaignAdmin.php +++ b/admin/code/CampaignAdmin.php @@ -39,6 +39,17 @@ class CampaignAdmin extends LeftAndMain implements PermissionProvider { } public function getClientConfig() { + return array_merge(parent::getClientConfig(), [ + 'forms' => [ + // TODO Use schemaUrl instead + 'editForm' => [ + 'schemaUrl' => $this->Link('schema/EditForm') + ] + ] + ]); + } + + public function schema($request) { // TODO Hardcoding schema until we can get GridField to generate a schema dynamically $json = << [ - 'editForm' => [ - // TODO Use schemaUrl instead - 'schema' => json_decode($json) - ] - ] - ]); - } - - public function getEditForm($id = null, $fields = null) { - return ''; + $formName = $request->param('ID'); + if($formName == 'EditForm') { + $response = $this->getResponse(); + $response->addHeader('Content-Type', 'application/json'); + $response->setBody($json); + return $response; + } else { + return parent::schema($request); + } } /** diff --git a/admin/javascript/src/boot/index.js b/admin/javascript/src/boot/index.js index 49afbb79e..529088079 100644 --- a/admin/javascript/src/boot/index.js +++ b/admin/javascript/src/boot/index.js @@ -6,9 +6,11 @@ import reducerRegister from 'reducer-register'; import * as configActions from '../state/config/actions'; import ConfigReducer from '../state/config/reducer'; +import SchemaReducer from '../state/schema/reducer'; function appBoot() { reducerRegister.add('config', ConfigReducer); + reducerRegister.add('schemas', SchemaReducer); const initialState = {}; const rootReducer = combineReducers(reducerRegister.getAll()); diff --git a/admin/javascript/src/components/form-builder/README.md b/admin/javascript/src/components/form-builder/README.md index 0d793d617..f91c2237e 100644 --- a/admin/javascript/src/components/form-builder/README.md +++ b/admin/javascript/src/components/form-builder/README.md @@ -14,7 +14,7 @@ Actions the component can dispatch. This should include but is not limited to: An action to call when the response from fetching schema data is returned. This would normally be a simple action to set the store's `schema` key to the returned data. -### formSchemaUrl +### schemaUrl The schema URL where the form will be scaffolded from e.g. '/admin/pages/schema/1'. diff --git a/admin/javascript/src/components/form-builder/index.js b/admin/javascript/src/components/form-builder/index.js index 1e15a5bfb..980c4fbde 100644 --- a/admin/javascript/src/components/form-builder/index.js +++ b/admin/javascript/src/components/form-builder/index.js @@ -6,6 +6,8 @@ import * as schemaActions from '../../state/schema/actions'; import SilverStripeComponent from '../../SilverStripeComponent'; import FormComponent from '../form'; import TextField from '../text-field'; +import HiddenField from '../hidden-field'; +import GridField from '../../sections/grid-field'; // Using this to map field types to components until we implement dependency injection. var fakeInjector = { @@ -14,7 +16,9 @@ var fakeInjector = { * Components registered with the fake DI container. */ components: { - 'TextField': TextField + 'TextField': TextField, + 'GridField': GridField, + 'HiddenField': HiddenField }, /** @@ -42,7 +46,7 @@ var fakeInjector = { case 'String': return this.components.TextField; case 'Hidden': - return this.components.TextField; + return this.components.HiddenField; case 'Text': // Textarea field (not implemented) return null; @@ -73,6 +77,8 @@ var fakeInjector = { case 'Boolean': // Checkbox field (not implemented) return null; + case 'Custom': + return this.components.GridField; default: return null; } @@ -117,7 +123,7 @@ export class FormBuilderComponent extends SilverStripeComponent { this.formSchemaPromise = $.ajax({ method: 'GET', headers: { 'X-FormSchema-Request': headerValues.join() }, - url: this.props.formSchemaUrl + url: this.props.schemaUrl }).done((data, status, xhr) => { this.isFetching = false; this.props.actions.setSchema(data); @@ -128,17 +134,6 @@ export class FormBuilderComponent extends SilverStripeComponent { return this.formSchemaPromise; } - /** - * Gets form schema for the FormBuilder. - * - * @return object|undefined - */ - getFormSchema() { - return this.props.schema.forms.find(function (form) { - return form.schema.schema_url === this.props.formSchemaUrl; - }.bind(this)); - } - /** * Maps a list of schema fields to their React Component. * Only top level form fields are handled here, composite fields (TabSets etc), @@ -189,19 +184,19 @@ export class FormBuilderComponent extends SilverStripeComponent { } render() { + const schema = this.props.schemas[this.props.schemaUrl]; + // If the response from fetching the initial data // hasn't come back yet, don't render anything. - if (this.props.schema.forms.length === 0) { + if (!schema) { return null; } - const schema = this.getFormSchema().schema; - const formProps = { - actions: schema.actions, - attributes: schema.attributes, - data: schema.data, - fields: schema.fields, + actions: schema.schema.actions, + attributes: schema.schema.attributes, + data: schema.schema.data, + fields: schema.schema.fields, mapFieldsToComponents: this.mapFieldsToComponents }; @@ -211,13 +206,13 @@ export class FormBuilderComponent extends SilverStripeComponent { FormBuilderComponent.propTypes = { actions: React.PropTypes.object.isRequired, - formSchemaUrl: React.PropTypes.string.isRequired, - schema: React.PropTypes.object.isRequired + schemaUrl: React.PropTypes.string.isRequired, + schemas: React.PropTypes.object.isRequired }; function mapStateToProps(state) { return { - schema: state.schema + schemas: state.schemas } } diff --git a/admin/javascript/src/components/form-builder/tests/form-builder-test.js b/admin/javascript/src/components/form-builder/tests/form-builder-test.js index 908a3155f..782c5e9a0 100644 --- a/admin/javascript/src/components/form-builder/tests/form-builder-test.js +++ b/admin/javascript/src/components/form-builder/tests/form-builder-test.js @@ -15,7 +15,7 @@ describe('FormBuilderComponent', () => { getState: () => {} }, actions: {}, - formSchemaUrl: 'admin/assets/schema/1', + schemaUrl: 'admin/assets/schema/1', schema: { forms: [{ schema: { id: '1', schema_url: 'admin/assets/schema/1' } }] } }; diff --git a/admin/javascript/src/components/form/index.js b/admin/javascript/src/components/form/index.js index 36c5a15bc..5903d7d21 100644 --- a/admin/javascript/src/components/form/index.js +++ b/admin/javascript/src/components/form/index.js @@ -43,10 +43,10 @@ class FormComponent extends SilverStripeComponent { } FormComponent.propTypes = { - actions: React.PropTypes.array.isRequired, + actions: React.PropTypes.array, attributes: React.PropTypes.shape({ action: React.PropTypes.string.isRequired, - className: React.PropTypes.string.isRequired, + 'class': React.PropTypes.string.isRequired, enctype: React.PropTypes.string.isRequired, id: React.PropTypes.string.isRequired, method: React.PropTypes.string.isRequired diff --git a/admin/javascript/src/sections/campaign-admin/controller.js b/admin/javascript/src/sections/campaign-admin/controller.js index 284fcf542..5c827c233 100644 --- a/admin/javascript/src/sections/campaign-admin/controller.js +++ b/admin/javascript/src/sections/campaign-admin/controller.js @@ -5,6 +5,7 @@ import ActionButton from 'action-button'; import i18n from 'i18n'; import NorthHeader from 'north-header'; import GridField from 'grid-field'; +import FormBuilder from '../../components/form-builder'; class CampaignAdminContainer extends SilverStripeComponent { @@ -15,17 +16,17 @@ class CampaignAdminContainer extends SilverStripeComponent { } render() { + const schemaUrl = this.props.config.forms.editForm.schemaUrl; + return (
- - - +
); } diff --git a/admin/javascript/src/state/schema/actions.js b/admin/javascript/src/state/schema/actions.js index 011fb7135..3f8cc2058 100644 --- a/admin/javascript/src/state/schema/actions.js +++ b/admin/javascript/src/state/schema/actions.js @@ -1,7 +1,7 @@ import ACTION_TYPES from './action-types'; /** - * Sets the schema being used to generate the curent layout. + * Sets the schema being used to generate the current form layout. * * @param string schema - JSON schema for the layout. */ diff --git a/admin/javascript/src/state/schema/reducer.js b/admin/javascript/src/state/schema/reducer.js index 6e846ded5..29e25faa0 100644 --- a/admin/javascript/src/state/schema/reducer.js +++ b/admin/javascript/src/state/schema/reducer.js @@ -1,30 +1,15 @@ import deepFreeze from 'deep-freeze'; import ACTION_TYPES from './action-types'; -const initialState = deepFreeze({ - forms: [] -}); +const initialState = deepFreeze({}); export default function schemaReducer(state = initialState, action = null) { switch (action.type) { case ACTION_TYPES.SET_SCHEMA: - if (state.forms.length === 0) { - return deepFreeze(Object.assign({}, state, { forms: [action.payload] })); - } - - // Replace the form which has a matching `schema.id` property. - return deepFreeze(Object.assign({}, state, { - forms: state.forms.map((form) => { - if (form.schema.id === action.payload.schema.id) { - // Only replace the `schema` key incase other actions have updated other keys. - return Object.assign({}, form, action.payload); - } - - return form; - }) - })); + const id = action.payload.schema.schema_url; + return deepFreeze(Object.assign({}, state, {[id]: action.payload})); default: return state; diff --git a/admin/templates/Includes/CampaignAdmin_Content.ss b/admin/templates/Includes/CampaignAdmin_Content.ss new file mode 100644 index 000000000..a6b8bf9ed --- /dev/null +++ b/admin/templates/Includes/CampaignAdmin_Content.ss @@ -0,0 +1,2 @@ +
+