Skip to content

Commit

Permalink
Remove usage of React.createClass, enforce in eslint
Browse files Browse the repository at this point in the history
  • Loading branch information
tlrobinson committed Mar 10, 2016
1 parent 7951eff commit b400b2c
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 110 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
"new-cap": 0,
"no-fallthrough": 0,
"no-case-declarations": 0,
"react/prop-types": 0,
"react/no-is-mounted": 2,
"react/prefer-es6-class": 2,
"react/prop-types": 0,
"react/no-did-mount-set-state": 0,
"react/no-did-update-set-state": 0,
"react/display-name": 0
Expand Down
117 changes: 59 additions & 58 deletions frontend/src/components/ActionButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,49 @@ import React, { Component, PropTypes } from "react";

import Icon from "metabase/components/Icon.jsx";

import { cancelable } from "metabase/lib/promise";

import cx from "classnames";
import _ from "underscore";

export default React.createClass({
displayName: 'ActionButton',
propTypes: {
actionFn: PropTypes.func.isRequired
},
export default class ActionButton extends Component {
constructor(props, context) {
super(props, context);

getDefaultProps: function() {
return {
normalText: "Save",
activeText: "Saving...",
failedText: "Save failed",
successText: "Saved",
className: 'Button'
};
},

getInitialState: function () {
return {
this.state = {
active: false,
result: null
};
},

componentWillUnmount: function() {
_.bindAll(this, "onClick", "resetStateOnTimeout")
}

static propTypes = {
actionFn: PropTypes.func.isRequired
};

static defaultProps = {
className: "Button",
normalText: "Save",
activeText: "Saving...",
failedText: "Save failed",
successText: "Saved"
};

componentWillUnmount() {
clearTimeout(this.timeout);
},
if (this.actionPromise) {
this.actionPromise.cancel();
}
}

resetStateOnTimeout: function() {
resetStateOnTimeout() {
// clear any previously set timeouts then start a new one
clearTimeout(this.timeout);
this.timeout = setTimeout(() => this.replaceState(this.getInitialState()), 5000);
},
}

onClick: function(event) {
onClick(event) {
event.preventDefault();

// set state to active
Expand All @@ -47,51 +54,45 @@ export default React.createClass({
});

// run the function we want bound to this button
var component = this;
this.props.actionFn().then(function(success) {
component.setState({
this.actionPromise = cancelable(this.props.actionFn());
this.actionPromise.then((success) => {
this.setState({
active: false,
result: "success"
}, component.resetStateOnTimeout);
}, function(error) {
component.setState({
active: false,
result: "failed"
}, component.resetStateOnTimeout);
}, this.resetStateOnTimeout);
}, (error) => {
if (!error.isCanceled) {
this.setState({
active: false,
result: "failed"
}, this.resetStateOnTimeout);
}
});
}

// TODO: timeout on success/failed state to reset back to normal state
},

buttonContent: function() {
if (this.state.active) {
// TODO: loading spinner
return this.props.activeText;
} else if (this.state.result === "success") {
return (
<span>
<Icon name='check' width="12px" height="12px" />
<span className="ml1">{this.props.successText}</span>
</span>
);
} else if (this.state.result === "failed") {
return this.props.failedText;
} else {
return this.props.normalText;
}
},

render: function() {
var buttonStateClasses = cx({
render() {
var buttonStateClasses = cx(this.props.className, {
'Button--waiting': this.state.active,
'Button--success': this.state.result === 'success',
'Button--danger': this.state.result === 'failed'
});

return (
<button className={this.props.className + ' ' + buttonStateClasses} onClick={this.onClick}>
{this.buttonContent()}
<button className={buttonStateClasses} onClick={this.onClick}>
{ this.state.active ?
// TODO: loading spinner
this.props.activeText
: this.state.result === "success" ?
<span>
<Icon name='check' width="12px" height="12px" />
<span className="ml1">{this.props.successText}</span>
</span>
: this.state.result === "failed" ?
this.props.failedText
:
this.props.normalText
}
</button>
);
}
});
}
1 change: 0 additions & 1 deletion frontend/src/components/HistoryModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ export default class HistoryModal extends Component {
}

revisionDescription(revision) {
console.log(revision);
if (revision.is_creation) {
return "First revision.";
} else if (revision.is_reversion) {
Expand Down
103 changes: 53 additions & 50 deletions frontend/src/query_builder/QueryHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,26 @@ import Query from "metabase/lib/query";
import { cancelable } from "metabase/lib/promise";

import cx from "classnames";
import _ from "underscore";

export default class QueryHeader extends Component {
constructor(props, context) {
super(props, context);

export default React.createClass({
displayName: 'QueryHeader',
propTypes: {
this.state = {
recentlySaved: null,
modal: null,
revisions: null
};

_.bindAll(this, "resetStateOnTimeout",
"onCreate", "onSave", "onBeginEditing", "onCancel", "onDelete",
"onFollowBreadcrumb", "onToggleDataReference",
"onFetchRevisions", "onRevertToRevision", "onRevertedRevision"
);
}

static propTypes = {
card: PropTypes.object.isRequired,
originalCard: PropTypes.object,
isEditing: PropTypes.bool.isRequired,
Expand All @@ -37,32 +52,24 @@ export default React.createClass({
toggleDataReferenceFn: PropTypes.func.isRequired,
cardIsNewFn: PropTypes.func.isRequired,
cardIsDirtyFn: PropTypes.func.isRequired
},

getInitialState: function() {
return {
recentlySaved: null,
modal: null,
revisions: null
};
},
}

componentWillUnmount() {
clearTimeout(this.timeout);
if (this.requesetPromise) {
this.requesetPromise.cancel();
}
},
}

resetStateOnTimeout: function() {
resetStateOnTimeout() {
// clear any previously set timeouts then start a new one
clearTimeout(this.timeout);
this.timeout = setTimeout(() =>
this.setState({ recentlySaved: null })
, 5000);
},
}

onCreate: function(card, addToDash) {
onCreate(card, addToDash) {
// TODO: why are we not cleaning the card here?
this.requesetPromise = cancelable(this.props.cardApi.create(card).$promise);
return this.requesetPromise.then(newCard => {
Expand All @@ -73,9 +80,9 @@ export default React.createClass({
modal: addToDash ? "add-to-dashboard" : "saved"
}, this.resetStateOnTimeout);
});
},
}

onSave: async function(card, addToDash) {
onSave(card, addToDash) {
if (card.dataset_query.query) {
Query.cleanQuery(card.dataset_query.query);
}
Expand All @@ -94,57 +101,53 @@ export default React.createClass({
modal: addToDash ? "add-to-dashboard" : null
}, this.resetStateOnTimeout);
});
},
}

onBeginEditing: function() {
onBeginEditing() {
this.props.onBeginEditing();
},
}

onCancel: async function() {
async onCancel() {
if (this.props.fromUrl) {
this.onGoBack();
} else {
this.props.onCancelEditing();
}
},
}

onDelete: async function () {
async onDelete() {
await this.props.cardApi.delete({ 'cardId': this.props.card.id }).$promise;
this.onGoBack();
MetabaseAnalytics.trackEvent("QueryBuilder", "Delete");
},
}

onFollowBreadcrumb: function() {
onFollowBreadcrumb() {
this.props.onRestoreOriginalQuery();
},

setQueryMode: function(mode) {
this.props.setQueryModeFn(mode);
},
}

toggleDataReference: function() {
onToggleDataReference() {
this.props.toggleDataReferenceFn();
},
}

onGoBack: function() {
onGoBack() {
this.props.onChangeLocation(this.props.fromUrl || "/");
},
}

onFetchRevisions: async function({ entity, id }) {
async onFetchRevisions({ entity, id }) {
var revisions = await this.props.revisionApi.list({ entity, id }).$promise;
this.setState({ revisions });
},
}

onRevertToRevision: function({ entity, id, revision_id }) {
onRevertToRevision({ entity, id, revision_id }) {
return this.props.revisionApi.revert({ entity, id, revision_id }).$promise;
},
}

onRevertedRevision: function() {
onRevertedRevision() {
this.props.reloadCardFn();
this.refs.cardHistory.toggle();
},
}

getHeaderButtons: function() {
getHeaderButtons() {
var buttonSections = [];

// NEW card
Expand Down Expand Up @@ -173,7 +176,7 @@ export default React.createClass({
<QueryModeToggle
key="queryModeToggle"
currentQueryMode={this.props.card.dataset_query.type}
setQueryModeFn={this.setQueryMode}
setQueryModeFn={this.props.setQueryModeFn}
/>
]);
}
Expand All @@ -196,7 +199,7 @@ export default React.createClass({
} else {
// edit button
buttonSections.push([
<a key="edit" className="cursor-pointer text-brand-hover" onClick={() => this.onBeginEditing()}>
<a key="edit" className="cursor-pointer text-brand-hover" onClick={this.onBeginEditing}>
<Icon name="pencil" width="16px" height="16px" />
</a>
]);
Expand All @@ -218,7 +221,7 @@ export default React.createClass({

// cancel button
buttonSections.push([
<a key="cancel" className="cursor-pointer text-brand-hover text-grey-4 text-uppercase" onClick={() => this.onCancel()}>
<a key="cancel" className="cursor-pointer text-brand-hover text-grey-4 text-uppercase" onClick={this.onCancel}>
CANCEL
</a>
]);
Expand All @@ -232,7 +235,7 @@ export default React.createClass({
>
<DeleteQuestionModal
card={this.props.card}
deleteCardFn={() => this.onDelete()}
deleteCardFn={this.onDelete}
closeFn={() => this.refs.deleteModal.toggle()}
/>
</ModalWithTrigger>
Expand Down Expand Up @@ -299,16 +302,16 @@ export default React.createClass({
});
buttonSections.push([
<a key="dataReference" className={dataReferenceButtonClasses} title="Get help on what data means">
<Icon name='reference' width="16px" height="16px" onClick={this.toggleDataReference}></Icon>
<Icon name='reference' width="16px" height="16px" onClick={this.onToggleDataReference}></Icon>
</a>
]);

return (
<ButtonBar buttons={buttonSections} className="Header-buttonSection" />
);
},
}

render: function() {
render() {
return (
<div>
<HeaderBar
Expand Down Expand Up @@ -338,4 +341,4 @@ export default React.createClass({
</div>
);
}
});
}

0 comments on commit b400b2c

Please sign in to comment.