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
This commit is contained in:
Ingo Schommer 2016-03-23 23:16:03 +13:00
parent 0ca090a391
commit b4e6d498c9
10 changed files with 58 additions and 65 deletions

View File

@ -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 = <<<JSON
{
@ -137,18 +148,15 @@ class CampaignAdmin extends LeftAndMain implements PermissionProvider {
}
JSON;
return array_merge(parent::getClientConfig(), [
'forms' => [
'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);
}
}
/**

View File

@ -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());

View File

@ -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'.

View File

@ -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
}
}

View File

@ -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' } }] }
};

View File

@ -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

View File

@ -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 (
<div>
<NorthHeader />
<ActionButton
text={i18n._t('Campaigns.ADDCAMPAIGN')}
type={'secondary'}
icon={'plus-circled'}
text={i18n._t('Campaigns.ADDCAMPAIGN')}
type={'secondary'}
icon={'plus-circled'}
handleClick={this.addCampaign} />
<GridField />
<FormBuilder schemaUrl={schemaUrl} />
</div>
);
}

View File

@ -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.
*/

View File

@ -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;

View File

@ -0,0 +1,2 @@
<div class="cms-content center $BaseCSSClasses" data-layout-type="border" data-pjax-fragment="Content">
</div>