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.
This commit is contained in:
David Craig 2016-04-14 14:01:50 +12:00 committed by Damian Mooyman
parent d51e94c035
commit ef6a1f33ee
6 changed files with 64 additions and 57 deletions

View File

@ -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`.

View File

@ -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,
};

View File

@ -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 = (
<FormAction
label={i18n._t('Campaigns.PUBLISHCAMPAIGN')}
style={'success'}
loading={this.props.campaign.isPublishing}
handleClick={this.handlePublish}
icon={'rocket'}
/>
);
} else if (this.props.record.State === 'published') {
// TODO Implement "revert" feature
button = (
<FormAction
label={i18n._t('Campaigns.REVERTCAMPAIGN')}
style={'default'}
icon={'back-in-time'}
disabled
/>
);
}
// TODO Fix indicator positioning
// const itemCountIndicator = (
// <span className="text-muted">
// <span className="label label-warning label--empty">&nbsp;</span>
// &nbsp;{itemSummaryLabel}
// </span>
// );
return (
<div className="btn-toolbar">
{button}
</div>
);
// let itemSummaryLabel;
if (!items) {
return <div className="btn-toolbar"></div>;
}
return <div className="btn-toolbar"></div>;
// 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 = (
// <span className="text-muted">
// <span className="label label-warning label--empty">&nbsp;</span>
// &nbsp;{itemSummaryLabel}
// </span>
// );
return (
<div className="btn-toolbar">
<FormAction {...actionProps} />
</div>
);
}
/**
@ -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,
};
}

View File

@ -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];

View File

@ -3,6 +3,7 @@ import ACTION_TYPES from './action-types';
const initialState = {
campaignId: null,
isPublishing: false,
view: null,
};

View File

@ -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,