mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Remove duplicate props for PopoverField, added FormBuilder->handleAction and flattened state data
This commit is contained in:
parent
6a4b29d703
commit
d7663e850e
@ -6,6 +6,7 @@ if (typeof(ss) === 'undefined' || typeof(ss.i18n) === 'undefined') {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ss.i18n.addDictionary('en', {
|
ss.i18n.addDictionary('en', {
|
||||||
|
"Boolean.ANY": "Any",
|
||||||
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nAre you sure you want to archive these pages?\n\nThese pages and all of their children pages will be unpublished and sent to the archive.",
|
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nAre you sure you want to archive these pages?\n\nThese pages and all of their children pages will be unpublished and sent to the archive.",
|
||||||
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
|
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
|
||||||
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
|
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
|
||||||
|
@ -13,11 +13,6 @@ class AddToCampaignModal extends SilverStripeComponent {
|
|||||||
|
|
||||||
handleSubmit(event, fieldValues, submitFn) {
|
handleSubmit(event, fieldValues, submitFn) {
|
||||||
|
|
||||||
if (!fieldValues.Campaign && fieldValues.Campaign !== 0) {
|
|
||||||
event.preventDefault();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof this.props.handleSubmit === 'function') {
|
if (typeof this.props.handleSubmit === 'function') {
|
||||||
this.props.handleSubmit(event, fieldValues, submitFn);
|
this.props.handleSubmit(event, fieldValues, submitFn);
|
||||||
return;
|
return;
|
||||||
|
@ -131,17 +131,17 @@ class FormAction extends SilverStripeComponent {
|
|||||||
* @return undefined
|
* @return undefined
|
||||||
*/
|
*/
|
||||||
handleClick(event) {
|
handleClick(event) {
|
||||||
if (typeof this.props.handleClick === 'undefined') {
|
if (typeof this.props.handleClick === 'function') {
|
||||||
return;
|
this.props.handleClick(event, this.props.name || this.props.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.handleClick(event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FormAction.propTypes = {
|
FormAction.propTypes = {
|
||||||
id: React.PropTypes.string,
|
id: React.PropTypes.string,
|
||||||
|
name: React.PropTypes.string,
|
||||||
handleClick: React.PropTypes.func,
|
handleClick: React.PropTypes.func,
|
||||||
title: React.PropTypes.string,
|
title: React.PropTypes.string,
|
||||||
type: React.PropTypes.string,
|
type: React.PropTypes.string,
|
||||||
|
@ -25,6 +25,7 @@ export class FormBuilderComponent extends SilverStripeComponent {
|
|||||||
this.mapFieldsToComponents = this.mapFieldsToComponents.bind(this);
|
this.mapFieldsToComponents = this.mapFieldsToComponents.bind(this);
|
||||||
this.handleFieldUpdate = this.handleFieldUpdate.bind(this);
|
this.handleFieldUpdate = this.handleFieldUpdate.bind(this);
|
||||||
this.handleSubmit = this.handleSubmit.bind(this);
|
this.handleSubmit = this.handleSubmit.bind(this);
|
||||||
|
this.handleAction = this.handleAction.bind(this);
|
||||||
this.removeForm = this.removeForm.bind(this);
|
this.removeForm = this.removeForm.bind(this);
|
||||||
this.getFormId = this.getFormId.bind(this);
|
this.getFormId = this.getFormId.bind(this);
|
||||||
this.getFormSchema = this.getFormSchema.bind(this);
|
this.getFormSchema = this.getFormSchema.bind(this);
|
||||||
@ -171,6 +172,12 @@ export class FormBuilderComponent extends SilverStripeComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleAction(event, name) {
|
||||||
|
if (typeof this.props.handleAction === 'function') {
|
||||||
|
this.props.handleAction(event, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form submission handler passed to the Form Component as a prop.
|
* Form submission handler passed to the Form Component as a prop.
|
||||||
* Provides a hook for controllers to access for state and provide custom functionality.
|
* Provides a hook for controllers to access for state and provide custom functionality.
|
||||||
@ -228,6 +235,30 @@ export class FormBuilderComponent extends SilverStripeComponent {
|
|||||||
submitFn();
|
submitFn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildComponent(field, extraProps = {}) {
|
||||||
|
const Component = field.component !== null
|
||||||
|
? injector.getComponentByName(field.component)
|
||||||
|
: injector.getComponentByDataType(field.type);
|
||||||
|
|
||||||
|
if (Component === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Props which every form field receives.
|
||||||
|
// Leave it up to the schema and component to determine
|
||||||
|
// which props are required.
|
||||||
|
const props = Object.assign({}, field, extraProps);
|
||||||
|
|
||||||
|
// Provides container components a place to hook in
|
||||||
|
// and apply customisations to scaffolded components.
|
||||||
|
const createFn = this.props.createFn;
|
||||||
|
if (typeof createFn === 'function') {
|
||||||
|
return createFn(Component, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Component key={props.id} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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),
|
||||||
@ -237,38 +268,16 @@ export class FormBuilderComponent extends SilverStripeComponent {
|
|||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
mapFieldsToComponents(fields) {
|
mapFieldsToComponents(fields) {
|
||||||
const createFn = this.props.createFn;
|
|
||||||
const handleFieldUpdate = this.handleFieldUpdate;
|
|
||||||
|
|
||||||
return fields.map((field) => {
|
return fields.map((field) => {
|
||||||
const Component = field.component !== null
|
|
||||||
? injector.getComponentByName(field.component)
|
|
||||||
: injector.getComponentByDataType(field.type);
|
|
||||||
|
|
||||||
if (Component === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
const extraProps = { onChange: handleFieldUpdate };
|
const extraProps = { onChange: this.handleFieldUpdate };
|
||||||
|
|
||||||
// Build child nodes
|
// Build child nodes
|
||||||
if (field.children) {
|
if (field.children) {
|
||||||
extraProps.children = this.mapFieldsToComponents(field.children);
|
extraProps.children = this.mapFieldsToComponents(field.children);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Props which every form field receives.
|
return this.buildComponent(field, extraProps);
|
||||||
// Leave it up to the schema and component to determine
|
|
||||||
// which props are required.
|
|
||||||
const props = Object.assign({}, field, extraProps);
|
|
||||||
|
|
||||||
// Provides container components a place to hook in
|
|
||||||
// and apply customisations to scaffolded components.
|
|
||||||
if (typeof createFn === 'function') {
|
|
||||||
return createFn(Component, props);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Component key={props.id} {...props} />;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,7 +288,17 @@ export class FormBuilderComponent extends SilverStripeComponent {
|
|||||||
* @return {Array}
|
* @return {Array}
|
||||||
*/
|
*/
|
||||||
mapActionsToComponents(actions) {
|
mapActionsToComponents(actions) {
|
||||||
return this.mapFieldsToComponents(actions);
|
return actions.map((action) => {
|
||||||
|
// Events
|
||||||
|
const extraProps = { handleClick: this.handleAction };
|
||||||
|
|
||||||
|
// Build child nodes
|
||||||
|
if (action.children) {
|
||||||
|
extraProps.children = this.mapActionsToComponents(action.children);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.buildComponent(action, extraProps);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -297,7 +316,8 @@ export class FormBuilderComponent extends SilverStripeComponent {
|
|||||||
return structure;
|
return structure;
|
||||||
}
|
}
|
||||||
return merge.recursive(true, structure, {
|
return merge.recursive(true, structure, {
|
||||||
data: state.data,
|
data: Object.assign({}, structure.data, state.data),
|
||||||
|
source: state.source,
|
||||||
messages: state.messages,
|
messages: state.messages,
|
||||||
valid: state.valid,
|
valid: state.valid,
|
||||||
value: state.value,
|
value: state.value,
|
||||||
@ -368,6 +388,7 @@ FormBuilderComponent.propTypes = {
|
|||||||
form: React.PropTypes.object.isRequired,
|
form: React.PropTypes.object.isRequired,
|
||||||
formActions: React.PropTypes.object.isRequired,
|
formActions: React.PropTypes.object.isRequired,
|
||||||
handleSubmit: React.PropTypes.func,
|
handleSubmit: React.PropTypes.func,
|
||||||
|
handleAction: React.PropTypes.func,
|
||||||
schemas: React.PropTypes.object.isRequired,
|
schemas: React.PropTypes.object.isRequired,
|
||||||
schemaActions: React.PropTypes.object.isRequired,
|
schemaActions: React.PropTypes.object.isRequired,
|
||||||
schemaUrl: React.PropTypes.string.isRequired,
|
schemaUrl: React.PropTypes.string.isRequired,
|
||||||
|
@ -28,7 +28,7 @@ HeaderField.propTypes = {
|
|||||||
React.PropTypes.array,
|
React.PropTypes.array,
|
||||||
React.PropTypes.shape({
|
React.PropTypes.shape({
|
||||||
headingLevel: React.PropTypes.number.isRequired,
|
headingLevel: React.PropTypes.number.isRequired,
|
||||||
title: React.PropTypes.string.isRequired,
|
title: React.PropTypes.string,
|
||||||
}),
|
}),
|
||||||
]).isRequired,
|
]).isRequired,
|
||||||
};
|
};
|
||||||
|
@ -8,7 +8,7 @@ class PopoverField extends SilverStripeComponent {
|
|||||||
const placement = this.getPlacement();
|
const placement = this.getPlacement();
|
||||||
const overlay = (
|
const overlay = (
|
||||||
<Popover id={`${this.props.id}_Popover`} className={`fade in popover-${placement}`}
|
<Popover id={`${this.props.id}_Popover`} className={`fade in popover-${placement}`}
|
||||||
title={this.getPopoverTitle()}
|
title={this.props.data.popoverTitle}
|
||||||
>
|
>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</Popover>
|
</Popover>
|
||||||
@ -35,52 +35,21 @@ class PopoverField extends SilverStripeComponent {
|
|||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
getPlacement() {
|
getPlacement() {
|
||||||
const placement = this.getDataProperty('placement');
|
const placement = this.props.data.placement;
|
||||||
return placement || 'bottom';
|
return placement || 'bottom';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets title of popup box
|
|
||||||
*
|
|
||||||
* @return {String} Return the string to use.
|
|
||||||
*/
|
|
||||||
getPopoverTitle() {
|
|
||||||
const title = this.getDataProperty('popoverTitle');
|
|
||||||
return title || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search for a given property either passed in to data or as a direct prop
|
|
||||||
*
|
|
||||||
* @param {String} name
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
getDataProperty(name) {
|
|
||||||
if (typeof this.props[name] !== 'undefined') {
|
|
||||||
return this.props[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case this is nested in the form schema data prop
|
|
||||||
if (
|
|
||||||
typeof this.props.data !== 'undefined'
|
|
||||||
&& typeof this.props.data[name] !== 'undefined'
|
|
||||||
) {
|
|
||||||
return this.props.data[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PopoverField.propTypes = {
|
PopoverField.propTypes = {
|
||||||
id: React.PropTypes.string,
|
id: React.PropTypes.string,
|
||||||
title: React.PropTypes.string,
|
title: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.bool]),
|
||||||
popoverTitle: React.PropTypes.string,
|
data: React.PropTypes.oneOfType([
|
||||||
placement: React.PropTypes.oneOf(['top', 'right', 'bottom', 'left']),
|
React.PropTypes.array,
|
||||||
data: React.PropTypes.shape({
|
React.PropTypes.shape({
|
||||||
popoverTitle: React.PropTypes.string,
|
popoverTitle: React.PropTypes.string,
|
||||||
placement: React.PropTypes.oneOf(['top', 'right', 'bottom', 'left']),
|
placement: React.PropTypes.oneOf(['top', 'right', 'bottom', 'left']),
|
||||||
}),
|
}),
|
||||||
|
]),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PopoverField;
|
export default PopoverField;
|
||||||
|
@ -60,12 +60,7 @@ class SingleSelectField extends SilverStripeComponent {
|
|||||||
* @returns ReactComponent
|
* @returns ReactComponent
|
||||||
*/
|
*/
|
||||||
getSelectField() {
|
getSelectField() {
|
||||||
const options = this.props.source.map((item) => {
|
const options = this.props.source || [];
|
||||||
return Object.assign({},
|
|
||||||
item,
|
|
||||||
{disabled: this.props.data.disabled.indexOf(item.value) > -1}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.props.hasEmptyDefault) {
|
if (this.props.hasEmptyDefault) {
|
||||||
options.unshift({
|
options.unshift({
|
||||||
@ -124,21 +119,15 @@ SingleSelectField.propTypes = {
|
|||||||
source: React.PropTypes.arrayOf(React.PropTypes.shape({
|
source: React.PropTypes.arrayOf(React.PropTypes.shape({
|
||||||
value: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]),
|
value: React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number]),
|
||||||
title: React.PropTypes.string,
|
title: React.PropTypes.string,
|
||||||
})).isRequired,
|
disabled: React.PropTypes.bool,
|
||||||
data: React.PropTypes.shape({
|
})),
|
||||||
disabled: React.PropTypes.arrayOf(
|
|
||||||
React.PropTypes.oneOfType([React.PropTypes.string, React.PropTypes.number])
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
hasEmptyDefault: React.PropTypes.bool,
|
hasEmptyDefault: React.PropTypes.bool,
|
||||||
emptyString: React.PropTypes.string,
|
emptyString: React.PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
SingleSelectField.defaultProps = {
|
SingleSelectField.defaultProps = {
|
||||||
data: {
|
source: [],
|
||||||
disabled: [],
|
emptyString: i18n._t('Boolean.ANY', 'Any'),
|
||||||
},
|
|
||||||
emptyString: '',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SingleSelectField;
|
export default SingleSelectField;
|
||||||
|
@ -79,6 +79,7 @@ export function submitForm(submitApi, formId, fieldValues) {
|
|||||||
type: ACTION_TYPES.SUBMIT_FORM_FAILURE,
|
type: ACTION_TYPES.SUBMIT_FORM_FAILURE,
|
||||||
payload: { formId, error },
|
payload: { formId, error },
|
||||||
});
|
});
|
||||||
|
return error;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ namespace SilverStripe\Forms\Schema;
|
|||||||
|
|
||||||
use Form;
|
use Form;
|
||||||
use FormField;
|
use FormField;
|
||||||
|
use CompositeField;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class FormSchema
|
* Class FormSchema
|
||||||
@ -63,11 +64,8 @@ class FormSchema {
|
|||||||
'messages' => []
|
'messages' => []
|
||||||
];
|
];
|
||||||
|
|
||||||
// @todo - Flatten all nested fields for returning state. At the moment, only top
|
// flattened nested fields are returned, rather than only top level fields.
|
||||||
// level fields are returned.
|
$state['fields'] = $this->getFieldStates($form->Fields());
|
||||||
foreach ($form->Fields() as $field) {
|
|
||||||
$state['fields'][] = $field->getSchemaState();
|
|
||||||
}
|
|
||||||
|
|
||||||
if($form->Message()) {
|
if($form->Message()) {
|
||||||
$state['messages'][] = [
|
$state['messages'][] = [
|
||||||
@ -78,4 +76,17 @@ class FormSchema {
|
|||||||
|
|
||||||
return $state;
|
return $state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getFieldStates($fields) {
|
||||||
|
$states = [];
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
$states[] = $field->getSchemaState();
|
||||||
|
|
||||||
|
if ($field instanceof CompositeField) {
|
||||||
|
$subFields = $field->FieldList();
|
||||||
|
array_merge($states, $this->getFieldStates($subFields));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $states;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,13 @@ class PopoverField extends FieldGroup
|
|||||||
*/
|
*/
|
||||||
protected $popoverTitle = null;
|
protected $popoverTitle = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Placement of the popup box
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $placement = 'bottom';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get popup title
|
* Get popup title
|
||||||
*
|
*
|
||||||
@ -47,19 +54,33 @@ class PopoverField extends FieldGroup
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get popup placement
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getPlacement()
|
||||||
|
{
|
||||||
|
return $this->placement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPlacement($placement)
|
||||||
|
{
|
||||||
|
$valid = ['top', 'right', 'bottom', 'left'];
|
||||||
|
|
||||||
|
if (in_array($placement, $valid)) {
|
||||||
|
$this->placement = $placement;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function getSchemaDataDefaults()
|
public function getSchemaDataDefaults()
|
||||||
{
|
{
|
||||||
$schema = parent::getSchemaDataDefaults();
|
$schema = parent::getSchemaDataDefaults();
|
||||||
if($this->getPopoverTitle()) {
|
|
||||||
$data = [
|
$schema['data']['popoverTitle'] = $this->getPopoverTitle();
|
||||||
'popoverTitle' => $this->getPopoverTitle()
|
$schema['data']['placement'] = $this->getPlacement();
|
||||||
];
|
|
||||||
if(isset($schema['data'])) {
|
|
||||||
$schema['data'] = array_merge($schema['data'], $data);
|
|
||||||
} else {
|
|
||||||
$schema['data'] = $data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $schema;
|
return $schema;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,20 +39,21 @@ abstract class SelectField extends FormField {
|
|||||||
parent::__construct($name, $title, $value);
|
parent::__construct($name, $title, $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSchemaDataDefaults() {
|
public function getSchemaStateDefaults() {
|
||||||
$data = parent::getSchemaDataDefaults();
|
$data = parent::getSchemaStateDefaults();
|
||||||
|
$disabled = $this->getDisabledItems();
|
||||||
|
|
||||||
// Add options to 'data'
|
// Add options to 'data'
|
||||||
$source = $this->getSource();
|
$source = $this->getSource();
|
||||||
$data['source'] = (is_array($source))
|
$data['source'] = (is_array($source))
|
||||||
? array_map(function ($value, $title) {
|
? array_map(function ($value, $title) use ($disabled) {
|
||||||
return [
|
return [
|
||||||
'value' => $value,
|
'value' => $value,
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
|
'disabled' => in_array($value, $disabled),
|
||||||
];
|
];
|
||||||
}, array_keys($source), $source)
|
}, array_keys($source), $source)
|
||||||
: [];
|
: [];
|
||||||
$data['data']['disabled'] = $this->getDisabledItems();
|
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
@ -271,7 +271,10 @@ class FormSchemaTest extends SapphireTest {
|
|||||||
'disabled' => false,
|
'disabled' => false,
|
||||||
'customValidationMessage' => '',
|
'customValidationMessage' => '',
|
||||||
'attributes' => [],
|
'attributes' => [],
|
||||||
'data' => [],
|
'data' => [
|
||||||
|
'popoverTitle' => null,
|
||||||
|
'placement' => 'bottom',
|
||||||
|
],
|
||||||
'children' => [
|
'children' => [
|
||||||
[
|
[
|
||||||
'id' => 'Form_TestForm_action_publish',
|
'id' => 'Form_TestForm_action_publish',
|
||||||
|
Loading…
Reference in New Issue
Block a user