From 34d40bed5ff82d93f51a45b7874bebda55779448 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Thu, 31 Mar 2016 10:45:54 +1300 Subject: [PATCH] Add ESLint support See https://github.com/silverstripe/silverstripe-framework/pull/5108 --- .editorconfig | 4 + .eslintignore | 37 ++ .eslintrc | 3 + .../src/LeftAndMain.FieldDescriptionToggle.js | 56 +- admin/javascript/src/LeftAndMain.Menu.js | 26 +- admin/javascript/src/LeftAndMain.Preview.js | 50 +- admin/javascript/src/boot/index.js | 29 +- .../src/components/form-action/index.js | 156 +++--- .../src/components/form-builder/index.js | 381 +++++++------ .../form-builder/tests/form-builder-test.js | 26 +- admin/javascript/src/components/form/index.js | 92 ++-- .../src/components/grid-field/action.js | 34 +- .../src/components/grid-field/cell.js | 14 +- .../src/components/grid-field/header-cell.js | 14 +- .../src/components/grid-field/header.js | 10 +- .../src/components/grid-field/index.js | 165 +++--- .../src/components/grid-field/row.js | 10 +- .../src/components/grid-field/table.js | 90 ++-- .../grid-field/tests/grid-field-test.js | 104 ++-- .../src/components/hidden-field/index.js | 68 +-- .../north-header-breadcrumbs/index.js | 58 +- .../tests/north-header-breadcrumbs-test.js | 58 +- .../src/components/north-header/index.js | 38 +- .../src/components/text-field/index.js | 78 +-- .../tests/text-field-component-test.js | 40 +- admin/javascript/src/config.js | 56 +- admin/javascript/src/mocks/jQuery.js | 16 +- .../src/mocks/silverstripe-component.js | 14 +- admin/javascript/src/reducer-register.js | 83 ++- .../src/sections/campaign-admin/controller.js | 61 +-- .../src/sections/campaign-admin/index.js | 30 +- admin/javascript/src/silverstripe-backend.js | 108 ++-- .../javascript/src/silverstripe-component.js | 125 +++-- admin/javascript/src/sspath.js | 64 +-- .../src/state/config/action-types.js | 4 +- admin/javascript/src/state/config/actions.js | 11 +- admin/javascript/src/state/config/reducer.js | 14 +- .../src/state/records/action-types.js | 18 +- admin/javascript/src/state/records/actions.js | 71 ++- admin/javascript/src/state/records/reducer.js | 75 ++- .../src/state/records/tests/reducer-test.js | 48 +- .../src/state/schema/action-types.js | 2 +- admin/javascript/src/state/schema/actions.js | 11 +- admin/javascript/src/state/schema/reducer.js | 15 +- .../src/state/schema/tests/reducer-test.js | 20 +- .../src/tests/reducer-register-test.js | 52 +- .../src/tests/silverstripe-backend-test.js | 146 ++--- gulpfile.js | 499 +++++++++--------- javascript/src/HtmlEditorField.js | 10 +- javascript/src/UploadField.js | 2 +- javascript/src/router.js | 25 +- package.json | 6 +- 52 files changed, 1675 insertions(+), 1552 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc diff --git a/.editorconfig b/.editorconfig index a133be8b2..66e2e1452 100644 --- a/.editorconfig +++ b/.editorconfig @@ -17,6 +17,10 @@ trim_trailing_whitespace = false indent_size = 2 indent_style = space +[*.js] +indent_size = 2 +indent_style = space + # Don't perform any clean-up on thirdparty files [thirdparty/**] diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..366bc945f --- /dev/null +++ b/.eslintignore @@ -0,0 +1,37 @@ +# Ignore dist files +javascript/dist/ + +# Ignore legacy files +javascript/src/AssetUploadField.js +javascript/src/ConfirmedPasswordField.js +javascript/src/DateField.js +javascript/src/GridField.js +javascript/src/HtmlEditorField.js +javascript/src/InlineFormAction.js +javascript/src/PermissionCheckboxSetField.js +javascript/src/SelectionGroup.js +javascript/src/TabSet.js +javascript/src/TinyMCE_SSPlugin.js +javascript/src/ToggleCompositeField.js +javascript/src/ToggleField.js +javascript/src/TreeDropdownField.js +javascript/src/UploadField.js +javascript/src/UploadField_downloadtemplate.js +javascript/src/UploadField_select.js +javascript/src/UploadField_uploadtemplate.js +javascript/src/i18n.js +javascript/src/i18nx.js +javascript/src/jQuery.js +admin/javascript/src/LeftAndMain.js +admin/javascript/src/LeftAndMain.*.js +admin/javascript/src/CMSSecurity.js +admin/javascript/src/MemberDatetimeOptionsetField.js +admin/javascript/src/MemberImportForm.js +admin/javascript/src/ModelAdmin.js +admin/javascript/src/SecurityAdmin.js +admin/javascript/src/leaktools.js +admin/javascript/src/sspath.js +admin/javascript/src/ssui.core.js + +# Ignore tests +admin/javascript/**/tests/ diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..b0c0c8bda --- /dev/null +++ b/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "airbnb" +} diff --git a/admin/javascript/src/LeftAndMain.FieldDescriptionToggle.js b/admin/javascript/src/LeftAndMain.FieldDescriptionToggle.js index 6a446e6c6..5a7f66097 100644 --- a/admin/javascript/src/LeftAndMain.FieldDescriptionToggle.js +++ b/admin/javascript/src/LeftAndMain.FieldDescriptionToggle.js @@ -6,38 +6,38 @@ import $ from 'jQuery'; $.entwine('ss', function ($) { - $('.cms-description-toggle').entwine({ - onadd: function () { - var shown = false, // Current state of the description. - fieldId = this.prop('id').substr(0, this.prop('id').indexOf('_Holder')), - $trigger = this.find('.cms-description-trigger'), // Click target for toggling the description. - $description = this.find('.description'); + $('.cms-description-toggle').entwine({ + onadd: function () { + var shown = false, // Current state of the description. + fieldId = this.prop('id').substr(0, this.prop('id').indexOf('_Holder')), + $trigger = this.find('.cms-description-trigger'), // Click target for toggling the description. + $description = this.find('.description'); - // Prevent multiple events being added. - if (this.hasClass('description-toggle-enabled')) { - return; - } + // Prevent multiple events being added. + if (this.hasClass('description-toggle-enabled')) { + return; + } - // If a custom trigger han't been supplied use a sensible default. - if ($trigger.length === 0) { - $trigger = this - .find('.middleColumn') - .first() // Get the first middleColumn so we don't add multiple triggers on composite field types. - .after('') - .next(); - } + // If a custom trigger han't been supplied use a sensible default. + if ($trigger.length === 0) { + $trigger = this + .find('.middleColumn') + .first() // Get the first middleColumn so we don't add multiple triggers on composite field types. + .after('') + .next(); + } - this.addClass('description-toggle-enabled'); + this.addClass('description-toggle-enabled'); - // Toggle next description when button is clicked. - $trigger.on('click', function() { - $description[shown ? 'hide' : 'show'](); - shown = !shown; - }); + // Toggle next description when button is clicked. + $trigger.on('click', function() { + $description[shown ? 'hide' : 'show'](); + shown = !shown; + }); - // Hide next description by default. - $description.hide(); - } - }); + // Hide next description by default. + $description.hide(); + } + }); }); diff --git a/admin/javascript/src/LeftAndMain.Menu.js b/admin/javascript/src/LeftAndMain.Menu.js index 205088383..401463167 100644 --- a/admin/javascript/src/LeftAndMain.Menu.js +++ b/admin/javascript/src/LeftAndMain.Menu.js @@ -15,11 +15,11 @@ $.entwine('ss', function($){ * * @@ -38,7 +38,7 @@ $.entwine('ss', function($){ $(this).addClass('collapse'); } }); - } else { //collapse + } else { //collapse $(this).children('ul').each(function() { $(this).addClass('collapsed-flyout'); $(this).hasClass('collapse'); @@ -59,7 +59,7 @@ $.entwine('ss', function($){ //hide all the flyout-indicator $('.cms-menu-list').find('.child-flyout-indicator').hide(); - } else { //collapse + } else { //collapse //hide the flyout only if it is not the current section $('.collapsed-flyout').find('li').each(function() { //if (!$(this).hasClass('current')) @@ -110,11 +110,11 @@ $.entwine('ss', function($){ * @func getEvaluatedCollapsedState * @return {boolean} - Returns true if the menu should be collapsed, false if expanded. * @desc Evaluate whether the menu should be collapsed. - * The basic rule is "If the SiteTree (middle column) is present, collapse the menu, otherwise expand the menu". - * This reason behind this is to give the content area more real estate when the SiteTree is present. - * The user may wish to override this automatic behaviour and have the menu expanded or collapsed at all times. - * So unlike manually toggling the menu, the automatic behaviour never updates the menu's cookie value. - * Here we use the manually set state and the automatic behaviour to evaluate what the collapsed state should be. + * The basic rule is "If the SiteTree (middle column) is present, collapse the menu, otherwise expand the menu". + * This reason behind this is to give the content area more real estate when the SiteTree is present. + * The user may wish to override this automatic behaviour and have the menu expanded or collapsed at all times. + * So unlike manually toggling the menu, the automatic behaviour never updates the menu's cookie value. + * Here we use the manually set state and the automatic behaviour to evaluate what the collapsed state should be. */ getEvaluatedCollapsedState: function () { var shouldCollapse, @@ -269,7 +269,7 @@ $.entwine('ss', function($){ $('.collapsed-flyout').show(); fly.addClass('opened'); fly.children('ul').find('li').fadeIn('fast'); - } else { //collapse + } else { //collapse if(li) { li.remove(); } diff --git a/admin/javascript/src/LeftAndMain.Preview.js b/admin/javascript/src/LeftAndMain.Preview.js index c22b24d1f..d2381b817 100644 --- a/admin/javascript/src/LeftAndMain.Preview.js +++ b/admin/javascript/src/LeftAndMain.Preview.js @@ -501,17 +501,17 @@ $.entwine('ss.preview', function($){ _loadCurrentPage: function() { if (!this.getIsPreviewEnabled()) return; - var doc, - containerEl = $('.cms-container'); - try { - doc = this.find('iframe')[0].contentDocument; - } catch (e) { - // iframe can't be accessed - might be secure? - console.warn('Unable to access iframe, possible https mis-match'); - } - if (!doc) { - return; - } + var doc, + containerEl = $('.cms-container'); + try { + doc = this.find('iframe')[0].contentDocument; + } catch (e) { + // iframe can't be accessed - might be secure? + console.warn('Unable to access iframe, possible https mis-match'); + } + if (!doc) { + return; + } // Load this page in the admin interface if appropriate var id = $(doc).find('meta[name=x-page-id]').attr('content'); @@ -529,21 +529,21 @@ $.entwine('ss.preview', function($){ * Prepare the iframe content for preview. */ _adjustIframeForPreview: function() { - var iframe = this.find('iframe')[0], - doc; - if(!iframe){ - return; - } + var iframe = this.find('iframe')[0], + doc; + if(!iframe){ + return; + } - try { - doc = iframe.contentDocument; - } catch (e) { - // iframe can't be accessed - might be secure? - console.warn('Unable to access iframe, possible https mis-match'); - } - if(!doc) { - return; - } + try { + doc = iframe.contentDocument; + } catch (e) { + // iframe can't be accessed - might be secure? + console.warn('Unable to access iframe, possible https mis-match'); + } + if(!doc) { + return; + } // Open external links in new window to avoid "escaping" the internal page context in the preview // iframe, which is important to stay in for the CMS logic. diff --git a/admin/javascript/src/boot/index.js b/admin/javascript/src/boot/index.js index 9e258836e..863bdb58a 100644 --- a/admin/javascript/src/boot/index.js +++ b/admin/javascript/src/boot/index.js @@ -10,24 +10,27 @@ import SchemaReducer from 'state/schema/reducer'; import RecordsReducer from 'state/records/reducer'; // Sections +// eslint-disable-next-line no-unused-vars import CampaignAdmin from 'sections/campaign-admin/index'; function appBoot() { - reducerRegister.add('config', ConfigReducer); - reducerRegister.add('schemas', SchemaReducer); - reducerRegister.add('records', RecordsReducer); + reducerRegister.add('config', ConfigReducer); + reducerRegister.add('schemas', SchemaReducer); + reducerRegister.add('records', RecordsReducer); - const initialState = {}; - const rootReducer = combineReducers(reducerRegister.getAll()); - const createStoreWithMiddleware = applyMiddleware(thunkMiddleware, createLogger())(createStore); + const initialState = {}; + const rootReducer = combineReducers(reducerRegister.getAll()); + const createStoreWithMiddleware = applyMiddleware(thunkMiddleware, createLogger())(createStore); - // TODO: The store needs to be passed into route callbacks on the route context. - window.store = createStoreWithMiddleware(rootReducer, initialState); + // TODO: The store needs to be passed into route callbacks on the route context. + window.store = createStoreWithMiddleware(rootReducer, initialState); - // Set the initial config state. - configActions.setConfig(window.ss.config)(window.store.dispatch); + // Set the initial config state. + configActions.setConfig(window.ss.config)(window.store.dispatch); } -// TODO: This should be using `window.onload` but isn't because Entwine hooks are being used to set up the . -// `window.onload` happens AFTER these Entwine hooks which means the store is undefined when the is constructed. -$('body').entwine({ onadd: function () { appBoot(); } }); +// TODO: This should be using `window.onload` but isn't because +// Entwine hooks are being used to set up the . +// `window.onload` happens AFTER these Entwine hooks which means +// the store is undefined when the is constructed. +$('body').entwine({ onadd: () => { appBoot(); } }); diff --git a/admin/javascript/src/components/form-action/index.js b/admin/javascript/src/components/form-action/index.js index c0ade7191..7e3927efe 100644 --- a/admin/javascript/src/components/form-action/index.js +++ b/admin/javascript/src/components/form-action/index.js @@ -2,101 +2,101 @@ import React from 'react'; import SilverStripeComponent from 'silverstripe-component'; class FormActionComponent extends SilverStripeComponent { - constructor(props) { - super(props); + constructor(props) { + super(props); - this.handleClick = this.handleClick.bind(this); + this.handleClick = this.handleClick.bind(this); + } + + render() { + return ( + + ); + } + + /** + * Returns the necessary button classes based on the given props + * + * @returns string + */ + getButtonClasses() { + let buttonClasses = 'btn'; + + // Add 'type' class + buttonClasses += ` btn-${this.props.style}`; + + // If there is no text + if (typeof this.props.label === 'undefined') { + buttonClasses += ' no-text'; } - render() { - return ( - - ); + // Add icon class + if (typeof this.props.icon !== 'undefined') { + buttonClasses += ` font-icon-${this.props.icon}`; } - /** - * Returns the necessary button classes based on the given props - * - * @returns string - */ - getButtonClasses() { - var buttonClasses = 'btn'; - - // Add 'type' class - buttonClasses += ` btn-${this.props.style}`; - - // If there is no text - if (typeof this.props.label === 'undefined') { - buttonClasses += ' no-text'; - } - - // Add icon class - if (typeof this.props.icon !== 'undefined') { - buttonClasses += ` font-icon-${this.props.icon}`; - } - - // Add loading class - if (this.props.loading === true) { - buttonClasses += ' btn--loading'; - } - - // Add disabled class - if (this.props.disabled === true) { - buttonClasses += ' disabled'; - } - - return buttonClasses; + // Add loading class + if (this.props.loading === true) { + buttonClasses += ' btn--loading'; } - /** - * Returns markup for the loading icon - * - * @returns object|null - */ - getLoadingIcon() { - if (this.props.loading) { - return ( -
- - - - - -
- ); - } - - return null; + // Add disabled class + if (this.props.disabled === true) { + buttonClasses += ' disabled'; } - /** - * Event handler triggered when a user clicks the button. - * - * @param object event - * @returns null - */ - handleClick(event) { - this.props.handleClick(event); + return buttonClasses; + } + + /** + * Returns markup for the loading icon + * + * @returns object|null + */ + getLoadingIcon() { + if (this.props.loading) { + return ( +
+ + + + + +
+ ); } + return null; + } + + /** + * Event handler triggered when a user clicks the button. + * + * @param object event + * @returns null + */ + handleClick(event) { + this.props.handleClick(event); + } + } FormActionComponent.propTypes = { - handleClick: React.PropTypes.func.isRequired, - label: React.PropTypes.string, - type: React.PropTypes.string, - loading: React.PropTypes.bool, - icon: React.PropTypes.string, - disabled: React.PropTypes.bool, - style: React.PropTypes.string + handleClick: React.PropTypes.func.isRequired, + label: React.PropTypes.string, + type: React.PropTypes.string, + loading: React.PropTypes.bool, + icon: React.PropTypes.string, + disabled: React.PropTypes.bool, + style: React.PropTypes.string, }; FormActionComponent.defaultProps = { - type: 'button', - style: 'secondary' + type: 'button', + style: 'secondary', }; export default FormActionComponent; diff --git a/admin/javascript/src/components/form-builder/index.js b/admin/javascript/src/components/form-builder/index.js index a9790b866..a250f7d68 100644 --- a/admin/javascript/src/components/form-builder/index.js +++ b/admin/javascript/src/components/form-builder/index.js @@ -1,7 +1,6 @@ import React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import $ from 'jQuery'; import * as schemaActions from 'state/schema/actions'; import SilverStripeComponent from 'silverstripe-component'; import FormComponent from 'components/form/index'; @@ -14,219 +13,217 @@ import es6promise from 'es6-promise'; es6promise.polyfill(); // Using this to map field types to components until we implement dependency injection. -var fakeInjector = { +const fakeInjector = { - /** - * Components registered with the fake DI container. - */ - components: { - 'TextField': TextField, - 'GridField': GridField, - 'HiddenField': HiddenField - }, + /** + * Components registered with the fake DI container. + */ + components: { + TextField, + GridField, + HiddenField, + }, - /** - * Gets the component matching the passed component name. - * Used when a component type is provided bt the form schema. - * - * @param string componentName - The name of the component to get from the injector. - * - * @return object|null - */ - getComponentByName: function (componentName) { - return this.components[componentName]; - }, + /** + * Gets the component matching the passed component name. + * Used when a component type is provided bt the form schema. + * + * @param string componentName - The name of the component to get from the injector. + * + * @return object|null + */ + getComponentByName(componentName) { + return this.components[componentName]; + }, - /** - * Default data type to component mappings. - * Used as a fallback when no component type is provided in the form schema. - * - * @param string dataType - The data type provided by the form schema. - * - * @return object|null - */ - getComponentByDataType: function (dataType) { - switch (dataType) { - case 'String': - return this.components.TextField; - case 'Hidden': - return this.components.HiddenField; - case 'Text': - // Textarea field (not implemented) - return null; - case 'HTML': - // HTML editor field (not implemented) - return null; - case 'Integer': - // Numeric field (not implemented) - return null; - case 'Decimal': - // Numeric field (not implemented) - return null; - case 'MultiSelect': - // Radio field (not implemented) - return null; - case 'SingleSelect': - // Dropdown field (not implemented) - return null; - case 'Date': - // DateTime field (not implemented) - return null; - case 'DateTime': - // DateTime field (not implemented) - return null; - case 'Time': - // DateTime field (not implemented) - return null; - case 'Boolean': - // Checkbox field (not implemented) - return null; - case 'Custom': - return this.components.GridField; - default: - return null; - } + /** + * Default data type to component mappings. + * Used as a fallback when no component type is provided in the form schema. + * + * @param string dataType - The data type provided by the form schema. + * + * @return object|null + */ + getComponentByDataType(dataType) { + switch (dataType) { + case 'String': + return this.components.TextField; + case 'Hidden': + return this.components.HiddenField; + case 'Text': + // Textarea field (not implemented) + return null; + case 'HTML': + // HTML editor field (not implemented) + return null; + case 'Integer': + // Numeric field (not implemented) + return null; + case 'Decimal': + // Numeric field (not implemented) + return null; + case 'MultiSelect': + // Radio field (not implemented) + return null; + case 'SingleSelect': + // Dropdown field (not implemented) + return null; + case 'Date': + // DateTime field (not implemented) + return null; + case 'DateTime': + // DateTime field (not implemented) + return null; + case 'Time': + // DateTime field (not implemented) + return null; + case 'Boolean': + // Checkbox field (not implemented) + return null; + case 'Custom': + return this.components.GridField; + default: + return null; } -} + }, +}; export class FormBuilderComponent extends SilverStripeComponent { - constructor(props) { - super(props); + constructor(props) { + super(props); - this.formSchemaPromise = null; + this.formSchemaPromise = null; + this.isFetching = false; + + this.fetch(); + } + + /** + * 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. + * + * @return object - Promise from the AJAX request. + */ + fetch(schema = true, state = false) { + const headerValues = []; + + if (this.isFetching === true) { + return this.formSchemaPromise; + } + + if (schema === true) { + headerValues.push('schema'); + } + + if (state === true) { + headerValues.push('state'); + } + + this.formSchemaPromise = fetch(this.props.schemaUrl, { + headers: { 'X-FormSchema-Request': headerValues.join() }, + credentials: 'same-origin', + }) + .then(response => response.json()) + .then(json => { this.isFetching = false; + this.props.actions.setSchema(json); + }); - this.fetch(); + this.isFetching = true; + + return this.formSchemaPromise; + } + + /** + * Maps a list of schema fields to their React Component. + * 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 + */ + mapFieldsToComponents(fields) { + return fields.map((field, i) => { + const Component = field.component !== null + ? fakeInjector.getComponentByName(field.component) + : fakeInjector.getComponentByDataType(field.type); + + if (Component === null) { + return null; + } + + // Props which every form field receives. + const props = { + attributes: field.attributes, + data: field.data, + description: field.description, + extraClass: field.extraClass, + fields: field.children, + id: field.id, + name: field.name, + }; + + // Structural fields (like TabSets) are not posted back to + // the server and don't receive some props. + if (field.type !== 'Structural') { + props.rightTitle = field.rightTitle; + props.leftTitle = field.leftTitle; + props.readOnly = field.readOnly; + props.disabled = field.disabled; + props.customValidationMessage = field.customValidationMessage; + } + + // Dropdown and Radio fields need some options... + if (field.type === 'MultiSelect' || field.type === 'SingleSelect') { + props.source = field.source; + } + + return ; + }); + } + + 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 (!schema) { + return null; } - /** - * 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. - * - * @return object - Promise from the AJAX request. - */ - fetch(schema = true, state = false) { - var headerValues = []; + const formProps = { + actions: schema.schema.actions, + attributes: schema.schema.attributes, + data: schema.schema.data, + fields: schema.schema.fields, + mapFieldsToComponents: this.mapFieldsToComponents, + }; - if (this.isFetching === true) { - return this.formSchemaPromise; - } - - if (schema === true) { - headerValues.push('schema'); - } - - if (state === true) { - headerValues.push('state'); - } - - this.formSchemaPromise = fetch(this.props.schemaUrl, { - headers: { 'X-FormSchema-Request': headerValues.join() }, - credentials: 'same-origin' - }) - .then(response => { - return response.json(); - }) - .then(json => { - this.isFetching = false; - this.props.actions.setSchema(json); - }); - - this.isFetching = true; - - return this.formSchemaPromise; - } - - /** - * Maps a list of schema fields to their React Component. - * 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 - */ - mapFieldsToComponents(fields) { - return fields.map((field, i) => { - - const Component = field.component !== null - ? fakeInjector.getComponentByName(field.component) - : fakeInjector.getComponentByDataType(field.type); - - if (Component === null) { - return null; - } - - // Props which every form field receives. - let props = { - attributes: field.attributes, - data: field.data, - description: field.description, - extraClass: field.extraClass, - fields: field.children, - id: field.id, - name: field.name - }; - - // Structural fields (like TabSets) are not posted back to the server and don't receive some props. - if (field.type !== 'Structural') { - props.rightTitle = field.rightTitle; - props.leftTitle = field.leftTitle; - props.readOnly = field.readOnly; - props.disabled = field.disabled; - props.customValidationMessage = field.customValidationMessage; - } - - // Dropdown and Radio fields need some options... - if (field.type === 'MultiSelect' || field.type === 'SingleSelect') { - props.source = field.source; - } - - return - }); - } - - 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 (!schema) { - return null; - } - - const formProps = { - actions: schema.schema.actions, - attributes: schema.schema.attributes, - data: schema.schema.data, - fields: schema.schema.fields, - mapFieldsToComponents: this.mapFieldsToComponents - }; - - return - } + return ; + } } FormBuilderComponent.propTypes = { - actions: React.PropTypes.object.isRequired, - schemaUrl: React.PropTypes.string.isRequired, - schemas: React.PropTypes.object.isRequired + actions: React.PropTypes.object.isRequired, + schemaUrl: React.PropTypes.string.isRequired, + schemas: React.PropTypes.object.isRequired, }; function mapStateToProps(state) { - return { - schemas: state.schemas - } + return { + schemas: state.schemas, + }; } function mapDispatchToProps(dispatch) { - return { - actions: bindActionCreators(schemaActions, dispatch) - } + return { + actions: bindActionCreators(schemaActions, dispatch), + }; } export default connect(mapStateToProps, mapDispatchToProps)(FormBuilderComponent); 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 977b7d7c5..d5702b462 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 @@ -5,22 +5,22 @@ import { FormBuilderComponent } from '../'; describe('FormBuilderComponent', () => { - describe('getFormSchema()', () => { + describe('getFormSchema()', () => { - var formBuilder; + var formBuilder; - beforeEach(() => { - const props = { - store: { - getState: () => {} - }, - actions: {}, - schemaUrl: 'admin/assets/schema/1', - schema: { forms: [{ schema: { id: '1', schema_url: 'admin/assets/schema/1' } }] } - }; + beforeEach(() => { + const props = { + store: { + getState: () => {} + }, + actions: {}, + schemaUrl: 'admin/assets/schema/1', + schema: { forms: [{ schema: { id: '1', schema_url: 'admin/assets/schema/1' } }] } + }; - formBuilder = new FormBuilderComponent(props); - }); + formBuilder = new FormBuilderComponent(props); }); + }); }); diff --git a/admin/javascript/src/components/form/index.js b/admin/javascript/src/components/form/index.js index 47f3d4893..6ee80e5d9 100644 --- a/admin/javascript/src/components/form/index.js +++ b/admin/javascript/src/components/form/index.js @@ -4,56 +4,62 @@ import FormActionComponent from 'components/form-action/index'; class FormComponent extends SilverStripeComponent { - /** - * Gets the components responsible for perfoming actions on the form. - * For example form submission. - * - * @return array|null - */ - getFormActionComponents() { - return this.props.actions.map((action) => { - return ; - }); - } + /** + * Gets the components responsible for perfoming actions on the form. + * For example form submission. + * + * @return array|null + */ + getFormActionComponents() { + return this.props.actions.map((action) => + + ); + } - render() { - const attr = this.props.attributes; - const fields = this.props.mapFieldsToComponents(this.props.fields); - const actions = this.getFormActionComponents(); + render() { + const attr = this.props.attributes; + const fields = this.props.mapFieldsToComponents(this.props.fields); + const actions = this.getFormActionComponents(); - return ( -
- {fields && -
- {fields} -
- } + return ( + + {fields && +
+ {fields} +
+ } - {actions && -
-
- {actions} -
-
- } -
- ); - } + {actions && +
+
+ {actions} +
+
+ } + + ); + } } FormComponent.propTypes = { - actions: React.PropTypes.array, - attributes: React.PropTypes.shape({ - action: React.PropTypes.string.isRequired, - 'class': React.PropTypes.string.isRequired, - enctype: React.PropTypes.string.isRequired, - id: React.PropTypes.string.isRequired, - method: React.PropTypes.string.isRequired - }), - data: React.PropTypes.array, - fields: React.PropTypes.array.isRequired, - mapFieldsToComponents: React.PropTypes.func.isRequired + actions: React.PropTypes.array, + attributes: React.PropTypes.shape({ + action: React.PropTypes.string.isRequired, + class: React.PropTypes.string.isRequired, + enctype: React.PropTypes.string.isRequired, + id: React.PropTypes.string.isRequired, + method: React.PropTypes.string.isRequired, + }), + data: React.PropTypes.array, + fields: React.PropTypes.array.isRequired, + mapFieldsToComponents: React.PropTypes.func.isRequired, }; export default FormComponent; diff --git a/admin/javascript/src/components/grid-field/action.js b/admin/javascript/src/components/grid-field/action.js index a5c6be5f5..185cbd12b 100644 --- a/admin/javascript/src/components/grid-field/action.js +++ b/admin/javascript/src/components/grid-field/action.js @@ -2,27 +2,27 @@ import React from 'react'; import SilverStripeComponent from 'silverstripe-component'; class GridFieldActionComponent extends SilverStripeComponent { - constructor(props) { - super(props); - - this.handleClick = this.handleClick.bind(this); - } + constructor(props) { + super(props); + this.handleClick = this.handleClick.bind(this); + } - render() { - return ( -