Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React: Remove uses of React.createClass in code-studio/components #17624

Merged
merged 31 commits into from
Sep 8, 2017
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
8453ff4
ES6ify AbuseError
islemaster Sep 8, 2017
cd3121c
ES6ify AbuseExclamation
islemaster Sep 8, 2017
fec2388
ES6ify AdvancedShareOptions
islemaster Sep 8, 2017
be30ca2
ES6ify AssetManager
islemaster Sep 8, 2017
68f6d56
ES6ify AssetRow
islemaster Sep 8, 2017
cd5c75d
ES6ify AssetThumbnail
islemaster Sep 8, 2017
bd63b0d
ES6ify AssetUploader
islemaster Sep 8, 2017
6a91ab8
ES6ify Attachments
islemaster Sep 8, 2017
d6b556f
ES6ify Grid
islemaster Sep 8, 2017
5fd33df
ES6ify GridEditor
islemaster Sep 8, 2017
2e9b513
ES6ify HiddenUploader
islemaster Sep 8, 2017
c851660
ES6ify Icon
islemaster Sep 8, 2017
928b083
ES6ify IconLibrary
islemaster Sep 8, 2017
9d331d7
ES6ify IconList
islemaster Sep 8, 2017
e6cf0d1
ES6ify IconListEntry
islemaster Sep 8, 2017
b594d14
ES6ify ImagePicker
islemaster Sep 8, 2017
45e8b95
ES6ify ReportAbuseForm
islemaster Sep 8, 2017
bd482a1
ES6ify SendToPhone
islemaster Sep 8, 2017
936ef84
ES6ify ShareDialog
islemaster Sep 8, 2017
07964bd
ES6ify SmallFooter
islemaster Sep 8, 2017
9a2e070
ES6ify SoundCategory
islemaster Sep 8, 2017
8c9d781
ES6ify SoundLibrary
islemaster Sep 8, 2017
2a1350c
ES6ify SoundList
islemaster Sep 8, 2017
2c7e18d
ES6ify SoundListEntry
islemaster Sep 8, 2017
4db38da
ES6ify SoundPicker
islemaster Sep 8, 2017
efecf9f
ES6ify TeacherContentToggle
islemaster Sep 8, 2017
8837649
ES6ify TeacherPanel
islemaster Sep 8, 2017
c7154b0
Fixup to AdvancedShareOptions.jsx
islemaster Sep 8, 2017
02ef047
Fixup to Grid.jsx
islemaster Sep 8, 2017
4aa632d
Fixup in GridEditor.jsx
islemaster Sep 8, 2017
e9be378
Remove deprecated isMounted call
islemaster Sep 8, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope for this PR, but have we considered adding decorator support and using @Radium to add Radium to our components?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be awesome. I loved using annotations in AS3.

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',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems better expressed as props.onClickExport ? 'export' : 'embed', but I guess that's out of scope for this change.

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;
69 changes: 34 additions & 35 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,59 @@ 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);
},
}

/**
* 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()) {
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) {
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 +93,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 +109,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 +126,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 +152,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 +185,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;
}