Skip to content

Commit

Permalink
Merge pull request #17624 from code-dot-org/react-no-createclass-code…
Browse files Browse the repository at this point in the history
…-studio-components

React: Remove uses of React.createClass in code-studio/components
  • Loading branch information
islemaster committed Sep 8, 2017
2 parents 67c692a + e9be378 commit cb08e42
Show file tree
Hide file tree
Showing 27 changed files with 497 additions and 572 deletions.
41 changes: 21 additions & 20 deletions apps/src/code-studio/components/AdvancedShareOptions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ const style = {
},
};

const AdvancedShareOptions = Radium(React.createClass({
propTypes: {
const AdvancedShareOptions = Radium(class extends React.Component {
static propTypes = {
shareUrl: PropTypes.string.isRequired,
onClickExport: PropTypes.func,
onExpand: PropTypes.func.isRequired,
Expand All @@ -56,18 +56,19 @@ const AdvancedShareOptions = Radium(React.createClass({
iframeHeight: PropTypes.number.isRequired,
iframeWidth: PropTypes.number.isRequired,
}).isRequired,
},
};

getInitialState() {
return {
selectedOption: (this.props.onClickExport && 'export') || 'embed',
constructor(props) {
super(props);
this.state = {
selectedOption: props.onClickExport ? 'export' : 'embed',
exporting: false,
exportError: null,
embedWithoutCode: false,
};
},
}

downloadExport() {
downloadExport = () => {
this.setState({exporting: true});
this.props.onClickExport().then(
this.setState.bind(this, {exporting: false}),
Expand All @@ -78,7 +79,7 @@ const AdvancedShareOptions = Radium(React.createClass({
});
}
);
},
};

renderEmbedTab() {
let url = `${this.props.shareUrl}/embed`;
Expand All @@ -96,7 +97,7 @@ const AdvancedShareOptions = Radium(React.createClass({
) + '?nosource';
}
const {iframeWidth, iframeHeight} = this.props.embedOptions;
var iframeHtml =
const iframeHtml =
`<iframe width="${iframeWidth}" height="${iframeHeight}" style="border: 0px;" src="${url}"></iframe>`;
return (
<div>
Expand All @@ -122,14 +123,14 @@ const AdvancedShareOptions = Radium(React.createClass({
</label>
</div>
);
},
}

renderExportTab() {
var spinner = this.state.exporting ?
const spinner = this.state.exporting ?
<i className="fa fa-spinner fa-spin"></i> :
null;
// TODO: Make this use a nice UI component from somewhere.
var alert = this.state.exportError ? (
const alert = this.state.exportError ? (
<div className="alert fade in">
{this.state.exportError}
</div>
Expand All @@ -149,17 +150,17 @@ const AdvancedShareOptions = Radium(React.createClass({
{alert}
</div>
);
},
}

render() {
if (!this.state.selectedOption) {
// no options are available. Render nothing.
return null;
}
var optionsNav;
var selectedOption;
let optionsNav;
let selectedOption;
if (this.props.expanded) {
var exportTab = null;
let exportTab = null;
if (this.props.onClickExport) {
exportTab = (
<li
Expand All @@ -173,7 +174,7 @@ const AdvancedShareOptions = Radium(React.createClass({
</li>
);
}
var embedTab = (
const embedTab = (
<li
style={[
style.nav.li,
Expand All @@ -198,7 +199,7 @@ const AdvancedShareOptions = Radium(React.createClass({
selectedOption = this.renderEmbedTab();
}
}
var expand = this.props.expanded && this.state.selectedOption ? null :
const expand = this.props.expanded && this.state.selectedOption ? null :
(
<a onClick={this.props.onExpand} style={style.expand}>
{this.props.i18n.t('project.advanced_share')}
Expand All @@ -212,6 +213,6 @@ const AdvancedShareOptions = Radium(React.createClass({
</div>
);
}
}));
});

export default AdvancedShareOptions;
81 changes: 44 additions & 37 deletions apps/src/code-studio/components/AssetManager.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
/* eslint-disable react/no-is-mounted */
import React, {PropTypes} from 'react';
var assetsApi = require('@cdo/apps/clientApi').assets;
var filesApi = require('@cdo/apps/clientApi').files;
import {assets as assetsApi, files as filesApi} from '@cdo/apps/clientApi';

var AssetRow = require('./AssetRow');
var AssetUploader = require('./AssetUploader');
var assetListStore = require('../assets/assetListStore');
import AssetRow from './AssetRow';
import AssetUploader from './AssetUploader';
import assetListStore from '../assets/assetListStore';

var errorMessages = {
const errorMessages = {
403: 'Quota exceeded. Please delete some files and try again.',
413: 'The file is too large.',
415: 'This type of file is not supported.',
500: 'The server responded with an error.',
unknown: 'An unknown error occurred.'
};

var errorUploadDisabled = "This project has been reported for abusive content, " +
const errorUploadDisabled = "This project has been reported for abusive content, " +
"so uploading new assets is disabled.";

function getErrorMessage(status) {
Expand All @@ -33,58 +32,67 @@ const styles = {
/**
* A component for managing hosted assets.
*/
var AssetManager = React.createClass({
propTypes: {
export default class AssetManager extends React.Component {
static propTypes = {
assetChosen: PropTypes.func,
assetsChanged: PropTypes.func,
allowedExtensions: PropTypes.string,
uploadsEnabled: PropTypes.bool.isRequired,
useFilesApi: PropTypes.bool
},
};

getInitialState: function () {
return {
constructor(props) {
super(props);
this.state = {
assets: null,
statusMessage: this.props.uploadsEnabled ? '' : errorUploadDisabled
statusMessage: props.uploadsEnabled ? '' : errorUploadDisabled
};
},
}

componentWillMount: function () {
componentWillMount() {
let api = this.props.useFilesApi ? filesApi : assetsApi;
api.getFiles(this.onAssetListReceived, this.onAssetListFailure);
},
}

componentDidMount() {
this._isMounted = true;
}

componentWillUnmount() {
this._isMounted = false;
}

/**
* Called after the component mounts, when the server responds with the
* current list of assets.
* @param result
*/
onAssetListReceived: function (result) {
onAssetListReceived = (result) => {
assetListStore.reset(result.files);
if (this.isMounted()) {
if (this._isMounted) {
this.setState({assets: assetListStore.list(this.props.allowedExtensions)});
}
},
};

/**
* Called after the component mounts, if the server responds with an error
* when loading the current list of assets.
* @param xhr
*/
onAssetListFailure: function (xhr) {
if (this.isMounted()) {
onAssetListFailure = (xhr) => {
if (this._isMounted) {
this.setState({
statusMessage: 'Error loading asset list: ' + getErrorMessage(xhr.status)
});
}
},
};

onUploadStart: function (data) {
onUploadStart = (data) => {
this.setState({statusMessage: 'Uploading...'});
data.submit();
},
};

onUploadDone: function (result) {
onUploadDone = (result) => {
assetListStore.add(result);
if (this.props.assetsChanged) {
this.props.assetsChanged();
Expand All @@ -93,14 +101,14 @@ var AssetManager = React.createClass({
assets: assetListStore.list(this.props.allowedExtensions),
statusMessage: 'File "' + result.filename + '" successfully uploaded!'
});
},
};

onUploadError: function (status) {
onUploadError = (status) => {
this.setState({statusMessage: 'Error uploading file: ' +
getErrorMessage(status)});
},
};

deleteAssetRow: function (name) {
deleteAssetRow = (name) => {
assetListStore.remove(name);
if (this.props.assetsChanged) {
this.props.assetsChanged();
Expand All @@ -109,10 +117,10 @@ var AssetManager = React.createClass({
assets: assetListStore.list(this.props.allowedExtensions),
statusMessage: 'File "' + name + '" successfully deleted!'
});
},
};

render: function () {
var uploadButton = (<div>
render() {
const uploadButton = (<div>
<AssetUploader
uploadsEnabled={this.props.uploadsEnabled}
allowedExtensions={this.props.allowedExtensions}
Expand All @@ -126,7 +134,7 @@ var AssetManager = React.createClass({
</span>
</div>);

var assetList;
let assetList;
// If `this.state.assets` is null, the asset list is still loading. If it's
// empty, the asset list has loaded and there are no assets in the current
// channel (matching the `allowedExtensions`, if any were provided).
Expand All @@ -152,8 +160,8 @@ var AssetManager = React.createClass({
</div>
);
} else {
var rows = this.state.assets.map(function (asset) {
var choose = this.props.assetChosen && this.props.assetChosen.bind(this,
const rows = this.state.assets.map(function (asset) {
const choose = this.props.assetChosen && this.props.assetChosen.bind(this,
asset.filename);

return (
Expand Down Expand Up @@ -185,5 +193,4 @@ var AssetManager = React.createClass({

return assetList;
}
});
module.exports = AssetManager;
}
44 changes: 20 additions & 24 deletions apps/src/code-studio/components/AssetRow.jsx
Original file line number Diff line number Diff line change
@@ -1,70 +1,67 @@
import React, {PropTypes} from 'react';
var assetsApi = require('@cdo/apps/clientApi').assets;
var filesApi = require('@cdo/apps/clientApi').files;
import {assets as assetsApi, files as filesApi} from '@cdo/apps/clientApi';
import AssetThumbnail from './AssetThumbnail';

/**
* A single row in the AssetManager, describing one asset.
*/
var AssetRow = React.createClass({
propTypes: {
export default class AssetRow extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired,
type: PropTypes.oneOf(['image', 'audio', 'video', 'pdf', 'doc']).isRequired,
size: PropTypes.number,
useFilesApi: PropTypes.bool.isRequired,
onChoose: PropTypes.func,
onDelete: PropTypes.func.isRequired
},
};

getInitialState: function () {
return {
action: 'normal',
actionText: ''
};
},
state = {
action: 'normal',
actionText: ''
};

/**
* Confirm the user actually wants to delete this asset.
*/
confirmDelete: function () {
confirmDelete = () => {
this.setState({action: 'confirming delete', actionText: ''});
},
};

/**
* This user didn't want to delete this asset.
*/
cancelDelete: function () {
cancelDelete = () => {
this.setState({action: 'normal', actionText: ''});
},
};

/**
* Delete this asset and notify the parent to remove this row. If the delete
* fails, flip back to 'confirming delete' and display a message.
*/
handleDelete: function () {
handleDelete = () => {
this.setState({action: 'deleting', actionText: ''});

let api = this.props.useFilesApi ? filesApi : assetsApi;
api.deleteFile(this.props.name, this.props.onDelete, () => {
this.setState({action: 'confirming delete',
actionText: 'Error deleting file.'});
});
},
};

render: function () {
var actions, flex;
render() {
let actions, flex;
// `flex` is the "Choose" button in file-choose mode, or the filesize.
if (this.props.onChoose) {
flex = <button onClick={this.props.onChoose}>Choose</button>;
} else {
var size = (this.props.size / 1000).toFixed(2);
const size = (this.props.size / 1000).toFixed(2);
flex = size + ' kb';
}

const api = this.props.useFilesApi ? filesApi : assetsApi;
const src = api.basePath(this.props.name);
switch (this.state.action) {
case 'normal':
var api = this.props.useFilesApi ? filesApi : assetsApi;
var src = api.basePath(this.props.name);
actions = (
<td width="250" style={{textAlign: 'right'}}>
{flex}
Expand Down Expand Up @@ -122,5 +119,4 @@ var AssetRow = React.createClass({
</tr>
);
}
});
module.exports = AssetRow;
}

0 comments on commit cb08e42

Please sign in to comment.