From ef6a1f33ee4961d1beadcef8644d0f2c3f5e8d73 Mon Sep 17 00:00:00 2001 From: David Craig Date: Thu, 14 Apr 2016 14:01:50 +1200 Subject: [PATCH] Minor updates and fixes for campaign admin front-end - Updated FormActionComponent prop from 'style' to 'bootstrapButtonStyle because 'style' is a built in React prop. - Linted and added missing propTypes to campaign admin List component. - Added missing initial state key to campaign reducer. - Forced REST verb to lowercase in silverstripe-backend to make comparisions more robust. --- .../src/components/form-action/README.md | 2 +- .../src/components/form-action/index.js | 12 ++- .../src/sections/campaign-admin/list.js | 100 +++++++++--------- admin/javascript/src/silverstripe-backend.js | 4 +- .../javascript/src/state/campaign/reducer.js | 1 + admin/javascript/src/state/records/reducer.js | 2 +- 6 files changed, 64 insertions(+), 57 deletions(-) diff --git a/admin/javascript/src/components/form-action/README.md b/admin/javascript/src/components/form-action/README.md index 9022c5f0b..0db385d6d 100644 --- a/admin/javascript/src/components/form-action/README.md +++ b/admin/javascript/src/components/form-action/README.md @@ -24,7 +24,7 @@ The html id attribute. Used for the button's `type` attribute. Defaults to `button` -### style (string) +### bootstrapButtonStyle (string) The style of button to be shown, adds a class `btn-{style}` to the button. Defaults to `secondary`. diff --git a/admin/javascript/src/components/form-action/index.js b/admin/javascript/src/components/form-action/index.js index be3439a00..c3dd2c7bd 100644 --- a/admin/javascript/src/components/form-action/index.js +++ b/admin/javascript/src/components/form-action/index.js @@ -37,7 +37,7 @@ class FormActionComponent extends SilverStripeComponent { const buttonClasses = ['btn']; // Add 'type' class - buttonClasses.push(`btn-${this.props.style}`); + buttonClasses.push(`btn-${this.props.bootstrapButtonStyle}`); // If there is no text if (typeof this.props.label === 'undefined') { @@ -91,9 +91,13 @@ class FormActionComponent extends SilverStripeComponent { * Event handler triggered when a user clicks the button. * * @param object event - * @returns null + * @return undefined */ handleClick(event) { + if (typeof this.props.handleClick === 'undefined') { + return; + } + this.props.handleClick(event); } @@ -107,13 +111,13 @@ FormActionComponent.propTypes = { loading: React.PropTypes.bool, icon: React.PropTypes.string, disabled: React.PropTypes.bool, - style: React.PropTypes.string, + bootstrapButtonStyle: React.PropTypes.string, extraClass: React.PropTypes.string, }; FormActionComponent.defaultProps = { type: 'button', - style: 'secondary', + bootstrapButtonStyle: 'secondary', disabled: false, }; diff --git a/admin/javascript/src/sections/campaign-admin/list.js b/admin/javascript/src/sections/campaign-admin/list.js index a618a626b..2aece7150 100644 --- a/admin/javascript/src/sections/campaign-admin/list.js +++ b/admin/javascript/src/sections/campaign-admin/list.js @@ -99,56 +99,53 @@ class CampaignListContainer extends SilverStripeComponent { } renderButtonToolbar() { - const items = this.getItems(this.props.campaignId); + const items = this.getItems(); - let itemSummaryLabel; - if (items) { - itemSummaryLabel = i18n.sprintf( - (items.length === 1) ? - i18n._t('Campaigns.ITEM_SUMMARY_SINGULAR') - : i18n._t('Campaigns.ITEM_SUMMARY_PLURAL'), - items.length - ); - - let button; - if (this.props.record.State === 'open') { - button = ( - - ); - } else if (this.props.record.State === 'published') { - // TODO Implement "revert" feature - button = ( - - ); - } - - // TODO Fix indicator positioning - // const itemCountIndicator = ( - // - //   - //  {itemSummaryLabel} - // - // ); - - return ( -
- {button} -
- ); + // let itemSummaryLabel; + if (!items) { + return
; } - return
; + // let itemSummaryLabel = i18n.sprintf( + // items.length === 1 + // ? i18n._t('Campaigns.ITEM_SUMMARY_SINGULAR') + // : i18n._t('Campaigns.ITEM_SUMMARY_PLURAL'), + // items.length + // ); + + let actionProps = {}; + + if (this.props.record.State === 'open') { + actionProps = Object.assign(actionProps, { + label: i18n._t('Campaigns.PUBLISHCAMPAIGN'), + bootstrapButtonStyle: 'success', + loading: this.props.campaign.isPublishing, + handleClick: this.handlePublish, + icon: 'rocket', + }); + } else if (this.props.record.State === 'published') { + // TODO Implement "revert" feature + actionProps = Object.assign(actionProps, { + label: i18n._t('Campaigns.REVERTCAMPAIGN'), + bootstrapButtonStyle: 'default', + icon: 'back-in-time', + disabled: true, + }); + } + + // TODO Fix indicator positioning + // const itemCountIndicator = ( + // + //   + //  {itemSummaryLabel} + // + // ); + + return ( +
+ +
+ ); } /** @@ -219,8 +216,13 @@ class CampaignListContainer extends SilverStripeComponent { } CampaignListContainer.propTypes = { + campaign: React.PropTypes.shape({ + isPublishing: React.PropTypes.bool.isRequired, + }), + campaignActions: React.PropTypes.object.isRequired, publishApi: React.PropTypes.func.isRequired, - isPublishing: React.PropTypes.bool, + record: React.PropTypes.object.isRequired, + recordActions: React.PropTypes.object.isRequired, }; function mapStateToProps(state, ownProps) { @@ -230,7 +232,7 @@ function mapStateToProps(state, ownProps) { record = state.records.ChangeSet[parseInt(ownProps.campaignId, 10)]; } return { - record: record || [], + record: record || {}, campaign: state.campaign, }; } diff --git a/admin/javascript/src/silverstripe-backend.js b/admin/javascript/src/silverstripe-backend.js index 27bae9d10..d11093b37 100644 --- a/admin/javascript/src/silverstripe-backend.js +++ b/admin/javascript/src/silverstripe-backend.js @@ -285,7 +285,7 @@ class SilverStripeBackend { // Always add full payload data to GET requests. // GET requests with a HTTP body are technically legal, // but throw an error in the WHATWG fetch() implementation. - { setFromData: (refinedSpec.method === 'get') } + { setFromData: (refinedSpec.method.toLowerCase() === 'get') } ); const encodedData = encode( @@ -295,7 +295,7 @@ class SilverStripeBackend { applySchemaToData(refinedSpec.payloadSchema, data) ); - const args = refinedSpec.method === 'get' + const args = refinedSpec.method.toLowerCase() === 'get' ? [url, headers] : [url, encodedData, headers]; diff --git a/admin/javascript/src/state/campaign/reducer.js b/admin/javascript/src/state/campaign/reducer.js index 51d8f1013..05635ba91 100644 --- a/admin/javascript/src/state/campaign/reducer.js +++ b/admin/javascript/src/state/campaign/reducer.js @@ -3,6 +3,7 @@ import ACTION_TYPES from './action-types'; const initialState = { campaignId: null, + isPublishing: false, view: null, }; diff --git a/admin/javascript/src/state/records/reducer.js b/admin/javascript/src/state/records/reducer.js index 3c2e06fdd..3ffe882ef 100644 --- a/admin/javascript/src/state/records/reducer.js +++ b/admin/javascript/src/state/records/reducer.js @@ -28,7 +28,7 @@ function recordsReducer(state = initialState, action) { case ACTION_TYPES.FETCH_RECORDS_SUCCESS: recordType = action.payload.recordType; // TODO Automatic pluralisation from recordType - records = action.payload.data._embedded[`${recordType}s`] || []; + records = action.payload.data._embedded[`${recordType}s`] || {}; records = records.reduce((prev, val) => Object.assign({}, prev, { [val.id]: val }), {}); return deepFreeze(Object.assign({}, state, { [recordType]: records,