Skip to content
Permalink
Browse files

API Add PopoverField for extra-actions popup in react

  • Loading branch information...
Damian Mooyman
Damian Mooyman committed Jul 6, 2016
1 parent 9e1b12a commit 5cb4ab4a82993d5b16fa0316e008f547f38d8e98

Large diffs are not rendered by default.

Oops, something went wrong.

Large diffs are not rendered by default.

Oops, something went wrong.

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -15,6 +15,8 @@ import BreadcrumbsReducer from 'state/breadcrumbs/BreadcrumbsReducer';
import TextField from 'components/TextField/TextField';
import HiddenField from 'components/HiddenField/HiddenField';
import GridField from 'components/GridField/GridField';
import FormAction from 'components/FormAction/FormAction';
import PopoverField from 'components/PopoverField/PopoverField';
import { routerReducer } from 'react-router-redux';

// Sections
@@ -33,6 +35,8 @@ function appBoot() {
injector.register('TextField', TextField);
injector.register('HiddenField', HiddenField);
injector.register('GridField', GridField);
injector.register('PopoverField', PopoverField);
injector.register('FormAction', FormAction);

const initialState = {};
const rootReducer = combineReducers(reducerRegister.getAll());
@@ -9,25 +9,32 @@ class FormAction extends SilverStripeComponent {
}

render() {
const props = {
type: this.props.type,
className: this.getButtonClasses(),
disabled: this.props.disabled,
onClick: this.handleClick,
};

if (typeof this.props.id !== 'undefined') {
props.id = this.props.id;
}

return (
<button {...props}>
<button {...this.getButtonProps()}>
{this.getLoadingIcon()}
{this.props.label}
{this.props.title}
</button>
);
}

/**
* Get props for the button
*
* @returns {Object}
*/
getButtonProps() {
// Merge attributes
return Object.assign({},
typeof this.props.attributes === 'undefined' ? {} : this.props.attributes,
{
id: this.props.id,
className: this.getButtonClasses(),
disabled: this.props.disabled,
onClick: this.handleClick,
}
);
}

/**
* Returns the necessary button classes based on the given props
*
@@ -37,16 +44,20 @@ class FormAction extends SilverStripeComponent {
const buttonClasses = ['btn'];

// Add 'type' class
buttonClasses.push(`btn-${this.props.bootstrapButtonStyle}`);
const bootstrapStyle = this.getBootstrapButtonStyle();
if (bootstrapStyle) {
buttonClasses.push(`btn-${bootstrapStyle}`);
}

// If there is no text
if (typeof this.props.label === 'undefined') {
if (typeof this.props.title === 'undefined') {
buttonClasses.push('btn--no-text');
}

// Add icon class
if (typeof this.props.icon !== 'undefined') {
buttonClasses.push(`font-icon-${this.props.icon}`);
const icon = this.getIcon();
if (icon) {
buttonClasses.push(`font-icon-${icon}`);
}

// Add loading class
@@ -66,6 +77,34 @@ class FormAction extends SilverStripeComponent {
return buttonClasses.join(' ');
}

/**
* Gets the bootstrap classname for this action
*
* @return {String}
*/
getBootstrapButtonStyle() {
// Add 'type' class
if (typeof this.props.bootstrapButtonStyle !== 'undefined') {
return this.props.bootstrapButtonStyle;
}

if (this.props.name === 'action_save') {
return 'primary';
}

return 'secondary';
}

/**
* Get icon name
*
* @returns {String}
*/
getIcon() {
// In case this is specified directly
return this.props.icon || this.props.data.icon || null;
}

/**
* Returns markup for the loading icon
*
@@ -75,9 +114,9 @@ class FormAction extends SilverStripeComponent {
if (this.props.loading === true) {
return (
<div className="btn__loading-icon" >
<span className="btn__circle btn__circle--1" ></span>
<span className="btn__circle btn__circle--2" ></span>
<span className="btn__circle btn__circle--3" ></span>
<span className="btn__circle btn__circle--1" />
<span className="btn__circle btn__circle--2" />
<span className="btn__circle btn__circle--3" />
</div>
);
}
@@ -88,7 +127,7 @@ class FormAction extends SilverStripeComponent {
/**
* Event handler triggered when a user clicks the button.
*
* @param object event
* @param {Object} event
* @return undefined
*/
handleClick(event) {
@@ -104,18 +143,21 @@ class FormAction extends SilverStripeComponent {
FormAction.propTypes = {
id: React.PropTypes.string,
handleClick: React.PropTypes.func,
label: React.PropTypes.string,
title: React.PropTypes.string,
type: React.PropTypes.string,
loading: React.PropTypes.bool,
icon: React.PropTypes.string,
disabled: React.PropTypes.bool,
bootstrapButtonStyle: React.PropTypes.string,
extraClass: React.PropTypes.string,
attributes: React.PropTypes.object,
};

FormAction.defaultProps = {
type: 'button',
bootstrapButtonStyle: 'secondary',
title: '',
icon: '',
attributes: {},
data: {},
disabled: false,
};

@@ -12,6 +12,7 @@
height: 32px;
margin-right: $spacer-x * .5; // 8px
position: relative;
box-shadow: none;
}

// Button icons
@@ -192,3 +193,9 @@
}
}
}

// Remove focus from these actions
// For things like popovers which have other indications they are focused
.btn--no-focus:focus {
outline: none;
}
@@ -5,9 +5,7 @@ import * as formActions from 'state/form/FormActions';
import * as schemaActions from 'state/schema/SchemaActions';
import SilverStripeComponent from 'lib/SilverStripeComponent';
import Form from 'components/Form/Form';
import FormAction from 'components/FormAction/FormAction';
import fetch from 'isomorphic-fetch';
import deepFreeze from 'deep-freeze-strict';
import backend from 'lib/Backend';
import injector from 'lib/Injector';
import merge from 'merge';
@@ -44,7 +42,7 @@ export class FormBuilderComponent extends SilverStripeComponent {
/**
* Gets the ID for this form
*
* @returns {string}
* @returns {String}
*/
getFormId() {
const schema = this.getFormSchema();
@@ -62,10 +60,10 @@ export class FormBuilderComponent extends SilverStripeComponent {
* Fetches data used to generate a form. This can be form schema and or form state data.
* When the response comes back the data is saved to state.
*
* @param boolean schema - If form schema data should be returned in the response.
* @param boolean state - If form state data should be returned in the response.
* @param {Boolean} schema If form schema data should be returned in the response.
* @param {Boolean} state If form state data should be returned in the response.
*
* @return object - Promise from the AJAX request.
* @return {Object} Promise from the AJAX request.
*/
fetch(schema = true, state = true) {
const headerValues = [];
@@ -227,15 +225,14 @@ export class FormBuilderComponent extends SilverStripeComponent {
* Only top level form fields are handled here, composite fields (TabSets etc),
* are responsible for mapping and rendering their children.
*
* @param array fields
*
* @return array
* @param {Array} fields
* @return {Array}
*/
mapFieldsToComponents(fields) {
const createFn = this.props.createFn;
const handleFieldUpdate = this.handleFieldUpdate;

return fields.map((field, i) => {
return fields.map((field) => {
const Component = field.component !== null
? injector.getComponentByName(field.component)
: injector.getComponentByDataType(field.type);
@@ -244,62 +241,37 @@ export class FormBuilderComponent extends SilverStripeComponent {
return null;
}

// Events
const extraProps = { onChange: handleFieldUpdate };

// Build child nodes
if (field.children) {
extraProps.children = this.mapFieldsToComponents(field.children);
}

// 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, { onChange: handleFieldUpdate });
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={i} {...props} />;
return <Component key={props.id} {...props} />;
});
}

/**
* Maps a list of form actions to their React Component.
*
* @param array actions
*
* @return array
* @param {Array} actions
* @return {Array}
*/
mapActionsToComponents(actions) {
const createFn = this.props.createFn;
const form = this.props.form[this.getFormId()];

return actions.map((action, i) => {
let props = deepFreeze(action);

// Add sensible defaults for common actions.
switch (props.name) {
case 'action_save':
props = deepFreeze(Object.assign({}, {
type: 'submit',
label: props.title,
icon: 'save',
loading: typeof form !== 'undefined' ? form.submitting : false,
bootstrapButtonStyle: 'primary',
}, props));
break;
case 'action_cancel':
props = deepFreeze(Object.assign({}, {
type: 'button',
label: props.title,
}, props));
break;
default:
break;
}

if (typeof createFn === 'function') {
return createFn(FormAction, props);
}

return <FormAction key={i} {...props} />;
});
return this.mapFieldsToComponents(actions);
}

/**
@@ -38,7 +38,6 @@ class HiddenField extends SilverStripeComponent {
}

HiddenField.propTypes = {
label: React.PropTypes.string,
extraClass: React.PropTypes.string,
name: React.PropTypes.string.isRequired,
onChange: React.PropTypes.func,
Oops, something went wrong.

0 comments on commit 5cb4ab4

Please sign in to comment.
You can’t perform that action at this time.