mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
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:
parent
0ca090a391
commit
b4e6d498c9
@ -39,6 +39,17 @@ class CampaignAdmin extends LeftAndMain implements PermissionProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getClientConfig() {
|
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
|
// TODO Hardcoding schema until we can get GridField to generate a schema dynamically
|
||||||
$json = <<<JSON
|
$json = <<<JSON
|
||||||
{
|
{
|
||||||
@ -137,18 +148,15 @@ class CampaignAdmin extends LeftAndMain implements PermissionProvider {
|
|||||||
}
|
}
|
||||||
JSON;
|
JSON;
|
||||||
|
|
||||||
return array_merge(parent::getClientConfig(), [
|
$formName = $request->param('ID');
|
||||||
'forms' => [
|
if($formName == 'EditForm') {
|
||||||
'editForm' => [
|
$response = $this->getResponse();
|
||||||
// TODO Use schemaUrl instead
|
$response->addHeader('Content-Type', 'application/json');
|
||||||
'schema' => json_decode($json)
|
$response->setBody($json);
|
||||||
]
|
return $response;
|
||||||
]
|
} else {
|
||||||
]);
|
return parent::schema($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getEditForm($id = null, $fields = null) {
|
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -6,9 +6,11 @@ import reducerRegister from 'reducer-register';
|
|||||||
|
|
||||||
import * as configActions from '../state/config/actions';
|
import * as configActions from '../state/config/actions';
|
||||||
import ConfigReducer from '../state/config/reducer';
|
import ConfigReducer from '../state/config/reducer';
|
||||||
|
import SchemaReducer from '../state/schema/reducer';
|
||||||
|
|
||||||
function appBoot() {
|
function appBoot() {
|
||||||
reducerRegister.add('config', ConfigReducer);
|
reducerRegister.add('config', ConfigReducer);
|
||||||
|
reducerRegister.add('schemas', SchemaReducer);
|
||||||
|
|
||||||
const initialState = {};
|
const initialState = {};
|
||||||
const rootReducer = combineReducers(reducerRegister.getAll());
|
const rootReducer = combineReducers(reducerRegister.getAll());
|
||||||
|
@ -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.
|
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'.
|
The schema URL where the form will be scaffolded from e.g. '/admin/pages/schema/1'.
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ import * as schemaActions from '../../state/schema/actions';
|
|||||||
import SilverStripeComponent from '../../SilverStripeComponent';
|
import SilverStripeComponent from '../../SilverStripeComponent';
|
||||||
import FormComponent from '../form';
|
import FormComponent from '../form';
|
||||||
import TextField from '../text-field';
|
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.
|
// Using this to map field types to components until we implement dependency injection.
|
||||||
var fakeInjector = {
|
var fakeInjector = {
|
||||||
@ -14,7 +16,9 @@ var fakeInjector = {
|
|||||||
* Components registered with the fake DI container.
|
* Components registered with the fake DI container.
|
||||||
*/
|
*/
|
||||||
components: {
|
components: {
|
||||||
'TextField': TextField
|
'TextField': TextField,
|
||||||
|
'GridField': GridField,
|
||||||
|
'HiddenField': HiddenField
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,7 +46,7 @@ var fakeInjector = {
|
|||||||
case 'String':
|
case 'String':
|
||||||
return this.components.TextField;
|
return this.components.TextField;
|
||||||
case 'Hidden':
|
case 'Hidden':
|
||||||
return this.components.TextField;
|
return this.components.HiddenField;
|
||||||
case 'Text':
|
case 'Text':
|
||||||
// Textarea field (not implemented)
|
// Textarea field (not implemented)
|
||||||
return null;
|
return null;
|
||||||
@ -73,6 +77,8 @@ var fakeInjector = {
|
|||||||
case 'Boolean':
|
case 'Boolean':
|
||||||
// Checkbox field (not implemented)
|
// Checkbox field (not implemented)
|
||||||
return null;
|
return null;
|
||||||
|
case 'Custom':
|
||||||
|
return this.components.GridField;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -117,7 +123,7 @@ export class FormBuilderComponent extends SilverStripeComponent {
|
|||||||
this.formSchemaPromise = $.ajax({
|
this.formSchemaPromise = $.ajax({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: { 'X-FormSchema-Request': headerValues.join() },
|
headers: { 'X-FormSchema-Request': headerValues.join() },
|
||||||
url: this.props.formSchemaUrl
|
url: this.props.schemaUrl
|
||||||
}).done((data, status, xhr) => {
|
}).done((data, status, xhr) => {
|
||||||
this.isFetching = false;
|
this.isFetching = false;
|
||||||
this.props.actions.setSchema(data);
|
this.props.actions.setSchema(data);
|
||||||
@ -128,17 +134,6 @@ export class FormBuilderComponent extends SilverStripeComponent {
|
|||||||
return this.formSchemaPromise;
|
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.
|
* Maps a list of schema fields to their React Component.
|
||||||
* Only top level form fields are handled here, composite fields (TabSets etc),
|
* Only top level form fields are handled here, composite fields (TabSets etc),
|
||||||
@ -189,19 +184,19 @@ export class FormBuilderComponent extends SilverStripeComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const schema = this.props.schemas[this.props.schemaUrl];
|
||||||
|
|
||||||
// If the response from fetching the initial data
|
// If the response from fetching the initial data
|
||||||
// hasn't come back yet, don't render anything.
|
// hasn't come back yet, don't render anything.
|
||||||
if (this.props.schema.forms.length === 0) {
|
if (!schema) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const schema = this.getFormSchema().schema;
|
|
||||||
|
|
||||||
const formProps = {
|
const formProps = {
|
||||||
actions: schema.actions,
|
actions: schema.schema.actions,
|
||||||
attributes: schema.attributes,
|
attributes: schema.schema.attributes,
|
||||||
data: schema.data,
|
data: schema.schema.data,
|
||||||
fields: schema.fields,
|
fields: schema.schema.fields,
|
||||||
mapFieldsToComponents: this.mapFieldsToComponents
|
mapFieldsToComponents: this.mapFieldsToComponents
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -211,13 +206,13 @@ export class FormBuilderComponent extends SilverStripeComponent {
|
|||||||
|
|
||||||
FormBuilderComponent.propTypes = {
|
FormBuilderComponent.propTypes = {
|
||||||
actions: React.PropTypes.object.isRequired,
|
actions: React.PropTypes.object.isRequired,
|
||||||
formSchemaUrl: React.PropTypes.string.isRequired,
|
schemaUrl: React.PropTypes.string.isRequired,
|
||||||
schema: React.PropTypes.object.isRequired
|
schemas: React.PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStateToProps(state) {
|
||||||
return {
|
return {
|
||||||
schema: state.schema
|
schemas: state.schemas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ describe('FormBuilderComponent', () => {
|
|||||||
getState: () => {}
|
getState: () => {}
|
||||||
},
|
},
|
||||||
actions: {},
|
actions: {},
|
||||||
formSchemaUrl: 'admin/assets/schema/1',
|
schemaUrl: 'admin/assets/schema/1',
|
||||||
schema: { forms: [{ schema: { id: '1', schema_url: 'admin/assets/schema/1' } }] }
|
schema: { forms: [{ schema: { id: '1', schema_url: 'admin/assets/schema/1' } }] }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,10 +43,10 @@ class FormComponent extends SilverStripeComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
FormComponent.propTypes = {
|
FormComponent.propTypes = {
|
||||||
actions: React.PropTypes.array.isRequired,
|
actions: React.PropTypes.array,
|
||||||
attributes: React.PropTypes.shape({
|
attributes: React.PropTypes.shape({
|
||||||
action: React.PropTypes.string.isRequired,
|
action: React.PropTypes.string.isRequired,
|
||||||
className: React.PropTypes.string.isRequired,
|
'class': React.PropTypes.string.isRequired,
|
||||||
enctype: React.PropTypes.string.isRequired,
|
enctype: React.PropTypes.string.isRequired,
|
||||||
id: React.PropTypes.string.isRequired,
|
id: React.PropTypes.string.isRequired,
|
||||||
method: React.PropTypes.string.isRequired
|
method: React.PropTypes.string.isRequired
|
||||||
|
@ -5,6 +5,7 @@ import ActionButton from 'action-button';
|
|||||||
import i18n from 'i18n';
|
import i18n from 'i18n';
|
||||||
import NorthHeader from 'north-header';
|
import NorthHeader from 'north-header';
|
||||||
import GridField from 'grid-field';
|
import GridField from 'grid-field';
|
||||||
|
import FormBuilder from '../../components/form-builder';
|
||||||
|
|
||||||
class CampaignAdminContainer extends SilverStripeComponent {
|
class CampaignAdminContainer extends SilverStripeComponent {
|
||||||
|
|
||||||
@ -15,17 +16,17 @@ class CampaignAdminContainer extends SilverStripeComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const schemaUrl = this.props.config.forms.editForm.schemaUrl;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<NorthHeader />
|
<NorthHeader />
|
||||||
|
|
||||||
<ActionButton
|
<ActionButton
|
||||||
text={i18n._t('Campaigns.ADDCAMPAIGN')}
|
text={i18n._t('Campaigns.ADDCAMPAIGN')}
|
||||||
type={'secondary'}
|
type={'secondary'}
|
||||||
icon={'plus-circled'}
|
icon={'plus-circled'}
|
||||||
handleClick={this.addCampaign} />
|
handleClick={this.addCampaign} />
|
||||||
|
<FormBuilder schemaUrl={schemaUrl} />
|
||||||
<GridField />
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import ACTION_TYPES from './action-types';
|
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.
|
* @param string schema - JSON schema for the layout.
|
||||||
*/
|
*/
|
||||||
|
@ -1,30 +1,15 @@
|
|||||||
import deepFreeze from 'deep-freeze';
|
import deepFreeze from 'deep-freeze';
|
||||||
import ACTION_TYPES from './action-types';
|
import ACTION_TYPES from './action-types';
|
||||||
|
|
||||||
const initialState = deepFreeze({
|
const initialState = deepFreeze({});
|
||||||
forms: []
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function schemaReducer(state = initialState, action = null) {
|
export default function schemaReducer(state = initialState, action = null) {
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
|
||||||
case ACTION_TYPES.SET_SCHEMA:
|
case ACTION_TYPES.SET_SCHEMA:
|
||||||
if (state.forms.length === 0) {
|
const id = action.payload.schema.schema_url;
|
||||||
return deepFreeze(Object.assign({}, state, { forms: [action.payload] }));
|
return deepFreeze(Object.assign({}, state, {[id]: 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;
|
|
||||||
})
|
|
||||||
}));
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
|
2
admin/templates/Includes/CampaignAdmin_Content.ss
Normal file
2
admin/templates/Includes/CampaignAdmin_Content.ss
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<div class="cms-content center $BaseCSSClasses" data-layout-type="border" data-pjax-fragment="Content">
|
||||||
|
</div>
|
Loading…
Reference in New Issue
Block a user