about summary refs log tree commit diff
path: root/app/assets/javascripts/components/features/ui
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/components/features/ui')
-rw-r--r--app/assets/javascripts/components/features/ui/components/boost_modal.jsx37
-rw-r--r--app/assets/javascripts/components/features/ui/components/column.jsx32
-rw-r--r--app/assets/javascripts/components/features/ui/components/column_header.jsx29
-rw-r--r--app/assets/javascripts/components/features/ui/components/column_link.jsx13
-rw-r--r--app/assets/javascripts/components/features/ui/components/columns_area.jsx16
-rw-r--r--app/assets/javascripts/components/features/ui/components/media_modal.jsx44
-rw-r--r--app/assets/javascripts/components/features/ui/components/modal_root.jsx33
-rw-r--r--app/assets/javascripts/components/features/ui/components/onboarding_modal.jsx50
-rw-r--r--app/assets/javascripts/components/features/ui/components/tabs_bar.jsx4
-rw-r--r--app/assets/javascripts/components/features/ui/components/upload_area.jsx16
-rw-r--r--app/assets/javascripts/components/features/ui/components/video_modal.jsx22
-rw-r--r--app/assets/javascripts/components/features/ui/index.jsx47
12 files changed, 181 insertions, 162 deletions
diff --git a/app/assets/javascripts/components/features/ui/components/boost_modal.jsx b/app/assets/javascripts/components/features/ui/components/boost_modal.jsx
index b54768631..e33239be7 100644
--- a/app/assets/javascripts/components/features/ui/components/boost_modal.jsx
+++ b/app/assets/javascripts/components/features/ui/components/boost_modal.jsx
@@ -1,5 +1,5 @@
-import PureRenderMixin from 'react-addons-pure-render-mixin';
 import ImmutablePropTypes from 'react-immutable-proptypes';
+import PropTypes from 'prop-types';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import IconButton from '../../../components/icon_button';
 import Button from '../../../components/button';
@@ -12,24 +12,18 @@ const messages = defineMessages({
   reblog: { id: 'status.reblog', defaultMessage: 'Boost' }
 });
 
-const BoostModal = React.createClass({
-  contextTypes: {
-    router: React.PropTypes.object
-  },
+class BoostModal extends React.PureComponent {
 
-  propTypes: {
-    status: ImmutablePropTypes.map.isRequired,
-    onReblog: React.PropTypes.func.isRequired,
-    onClose: React.PropTypes.func.isRequired,
-    intl: React.PropTypes.object.isRequired
-  },
-
-  mixins: [PureRenderMixin],
+  constructor (props, context) {
+    super(props, context);
+    this.handleReblog = this.handleReblog.bind(this);
+    this.handleAccountClick = this.handleAccountClick.bind(this);
+  }
 
   handleReblog() {
     this.props.onReblog(this.props.status);
     this.props.onClose();
-  },
+  }
 
   handleAccountClick (e) {
     if (e.button === 0) {
@@ -37,7 +31,7 @@ const BoostModal = React.createClass({
       this.props.onClose();
       this.context.router.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
     }
-  },
+  }
 
   render () {
     const { status, intl, onClose } = this.props;
@@ -72,6 +66,17 @@ const BoostModal = React.createClass({
     );
   }
 
-});
+}
+
+BoostModal.contextTypes = {
+  router: PropTypes.object
+};
+
+BoostModal.propTypes = {
+  status: ImmutablePropTypes.map.isRequired,
+  onReblog: PropTypes.func.isRequired,
+  onClose: PropTypes.func.isRequired,
+  intl: PropTypes.object.isRequired
+};
 
 export default injectIntl(BoostModal);
diff --git a/app/assets/javascripts/components/features/ui/components/column.jsx b/app/assets/javascripts/components/features/ui/components/column.jsx
index 977ea6059..4dad7f721 100644
--- a/app/assets/javascripts/components/features/ui/components/column.jsx
+++ b/app/assets/javascripts/components/features/ui/components/column.jsx
@@ -1,5 +1,5 @@
 import ColumnHeader from './column_header';
-import PureRenderMixin from 'react-addons-pure-render-mixin';
+import PropTypes from 'prop-types';
 
 const easingOutQuint = (x, t, b, c, d) => c*((t=t/d-1)*t*t*t*t + 1) + b;
 
@@ -29,17 +29,13 @@ const scrollTop = (node) => {
   };
 };
 
-const Column = React.createClass({
+class Column extends React.PureComponent {
 
-  propTypes: {
-    heading: React.PropTypes.string,
-    icon: React.PropTypes.string,
-    children: React.PropTypes.node,
-    active: React.PropTypes.bool,
-    hideHeadingOnMobile: React.PropTypes.bool
-  },
-
-  mixins: [PureRenderMixin],
+  constructor (props, context) {
+    super(props, context);
+    this.handleHeaderClick = this.handleHeaderClick.bind(this);
+    this.handleWheel = this.handleWheel.bind(this);
+  }
 
   handleHeaderClick () {
     const scrollable = ReactDOM.findDOMNode(this).querySelector('.scrollable');
@@ -47,13 +43,13 @@ const Column = React.createClass({
       return;
     }
     this._interruptScrollAnimation = scrollTop(scrollable);
-  },
+  }
 
   handleWheel () {
     if (typeof this._interruptScrollAnimation !== 'undefined') {
       this._interruptScrollAnimation();
     }
-  },
+  }
 
   render () {
     const { heading, icon, children, active, hideHeadingOnMobile } = this.props;
@@ -72,6 +68,14 @@ const Column = React.createClass({
     );
   }
 
-});
+}
+
+Column.propTypes = {
+  heading: PropTypes.string,
+  icon: PropTypes.string,
+  children: PropTypes.node,
+  active: PropTypes.bool,
+  hideHeadingOnMobile: PropTypes.bool
+};
 
 export default Column;
diff --git a/app/assets/javascripts/components/features/ui/components/column_header.jsx b/app/assets/javascripts/components/features/ui/components/column_header.jsx
index 232db1e14..454cce9c2 100644
--- a/app/assets/javascripts/components/features/ui/components/column_header.jsx
+++ b/app/assets/javascripts/components/features/ui/components/column_header.jsx
@@ -1,20 +1,15 @@
-import PureRenderMixin from 'react-addons-pure-render-mixin';
+import PropTypes from 'prop-types'
 
-const ColumnHeader = React.createClass({
+class ColumnHeader extends React.PureComponent {
 
-  propTypes: {
-    icon: React.PropTypes.string,
-    type: React.PropTypes.string,
-    active: React.PropTypes.bool,
-    onClick: React.PropTypes.func,
-    hideOnMobile: React.PropTypes.bool
-  },
-
-  mixins: [PureRenderMixin],
+  constructor (props, context) {
+    super(props, context);
+    this.handleClick = this.handleClick.bind(this);
+  }
 
   handleClick () {
     this.props.onClick();
-  },
+  }
 
   render () {
     const { type, active, hideOnMobile } = this.props;
@@ -33,6 +28,14 @@ const ColumnHeader = React.createClass({
     );
   }
 
-});
+}
+
+ColumnHeader.propTypes = {
+  icon: PropTypes.string,
+  type: PropTypes.string,
+  active: PropTypes.bool,
+  onClick: PropTypes.func,
+  hideOnMobile: PropTypes.bool
+};
 
 export default ColumnHeader;
diff --git a/app/assets/javascripts/components/features/ui/components/column_link.jsx b/app/assets/javascripts/components/features/ui/components/column_link.jsx
index 9ed34e85f..32fd329d4 100644
--- a/app/assets/javascripts/components/features/ui/components/column_link.jsx
+++ b/app/assets/javascripts/components/features/ui/components/column_link.jsx
@@ -1,3 +1,4 @@
+import PropTypes from 'prop-types';
 import { Link } from 'react-router';
 
 const outerStyle = {
@@ -30,12 +31,12 @@ const ColumnLink = ({ icon, text, to, href, method, hideOnMobile }) => {
 };
 
 ColumnLink.propTypes = {
-  icon: React.PropTypes.string.isRequired,
-  text: React.PropTypes.string.isRequired,
-  to: React.PropTypes.string,
-  href: React.PropTypes.string,
-  method: React.PropTypes.string,
-  hideOnMobile: React.PropTypes.bool
+  icon: PropTypes.string.isRequired,
+  text: PropTypes.string.isRequired,
+  to: PropTypes.string,
+  href: PropTypes.string,
+  method: PropTypes.string,
+  hideOnMobile: PropTypes.bool
 };
 
 export default ColumnLink;
diff --git a/app/assets/javascripts/components/features/ui/components/columns_area.jsx b/app/assets/javascripts/components/features/ui/components/columns_area.jsx
index dd771900d..06f6427ab 100644
--- a/app/assets/javascripts/components/features/ui/components/columns_area.jsx
+++ b/app/assets/javascripts/components/features/ui/components/columns_area.jsx
@@ -1,4 +1,4 @@
-import PureRenderMixin from 'react-addons-pure-render-mixin';
+import PropTypes from 'prop-types';
 
 const style = {
   display: 'flex',
@@ -6,13 +6,7 @@ const style = {
   overflowX: 'auto'
 };
 
-const ColumnsArea = React.createClass({
-
-  propTypes: {
-    children: React.PropTypes.node
-  },
-
-  mixins: [PureRenderMixin],
+class ColumnsArea extends React.PureComponent {
 
   render () {
     return (
@@ -22,6 +16,10 @@ const ColumnsArea = React.createClass({
     );
   }
 
-});
+}
+
+ColumnsArea.propTypes = {
+  children: PropTypes.node
+};
 
 export default ColumnsArea;
diff --git a/app/assets/javascripts/components/features/ui/components/media_modal.jsx b/app/assets/javascripts/components/features/ui/components/media_modal.jsx
index 786b08152..8ed6afa6c 100644
--- a/app/assets/javascripts/components/features/ui/components/media_modal.jsx
+++ b/app/assets/javascripts/components/features/ui/components/media_modal.jsx
@@ -1,6 +1,6 @@
 import LoadingIndicator from '../../../components/loading_indicator';
-import PureRenderMixin from 'react-addons-pure-render-mixin';
 import ImmutablePropTypes from 'react-immutable-proptypes';
+import PropTypes from 'prop-types';
 import ExtendedVideoPlayer from '../../../components/extended_video_player';
 import ImageLoader from 'react-imageloader';
 import { defineMessages, injectIntl } from 'react-intl';
@@ -44,30 +44,25 @@ const closeStyle = {
   right: '4px'
 };
 
-const MediaModal = React.createClass({
+class MediaModal extends React.PureComponent {
 
-  propTypes: {
-    media: ImmutablePropTypes.list.isRequired,
-    index: React.PropTypes.number.isRequired,
-    onClose: React.PropTypes.func.isRequired,
-    intl: React.PropTypes.object.isRequired
-  },
-
-  getInitialState () {
-    return {
+  constructor (props, context) {
+    super(props, context);
+    this.state = {
       index: null
     };
-  },
-
-  mixins: [PureRenderMixin],
+    this.handleNextClick = this.handleNextClick.bind(this);
+    this.handlePrevClick = this.handlePrevClick.bind(this);
+    this.handleKeyUp = this.handleKeyUp.bind(this);
+  }
 
   handleNextClick () {
     this.setState({ index: (this.getIndex() + 1) % this.props.media.size});
-  },
+  }
 
   handlePrevClick () {
     this.setState({ index: (this.getIndex() - 1) % this.props.media.size});
-  },
+  }
 
   handleKeyUp (e) {
     switch(e.key) {
@@ -78,19 +73,19 @@ const MediaModal = React.createClass({
       this.handleNextClick();
       break;
     }
-  },
+  }
 
   componentDidMount () {
     window.addEventListener('keyup', this.handleKeyUp, false);
-  },
+  }
 
   componentWillUnmount () {
     window.removeEventListener('keyup', this.handleKeyUp);
-  },
+  }
 
   getIndex () {
     return this.state.index !== null ? this.state.index : this.props.index;
-  },
+  }
 
   render () {
     const { media, intl, onClose } = this.props;
@@ -128,6 +123,13 @@ const MediaModal = React.createClass({
     );
   }
 
-});
+}
+
+MediaModal.propTypes = {
+  media: ImmutablePropTypes.list.isRequired,
+  index: PropTypes.number.isRequired,
+  onClose: PropTypes.func.isRequired,
+  intl: PropTypes.object.isRequired
+};
 
 export default injectIntl(MediaModal);
diff --git a/app/assets/javascripts/components/features/ui/components/modal_root.jsx b/app/assets/javascripts/components/features/ui/components/modal_root.jsx
index ace3e085f..7b84ef3c8 100644
--- a/app/assets/javascripts/components/features/ui/components/modal_root.jsx
+++ b/app/assets/javascripts/components/features/ui/components/modal_root.jsx
@@ -1,4 +1,4 @@
-import PureRenderMixin from 'react-addons-pure-render-mixin';
+import PropTypes from 'prop-types';
 import MediaModal from './media_modal';
 import OnboardingModal from './onboarding_modal';
 import VideoModal from './video_modal';
@@ -12,37 +12,34 @@ const MODAL_COMPONENTS = {
   'BOOST': BoostModal
 };
 
-const ModalRoot = React.createClass({
+class ModalRoot extends React.PureComponent {
 
-  propTypes: {
-    type: React.PropTypes.string,
-    props: React.PropTypes.object,
-    onClose: React.PropTypes.func.isRequired
-  },
-
-  mixins: [PureRenderMixin],
+  constructor (props, context) {
+    super(props, context);
+    this.handleKeyUp = this.handleKeyUp.bind(this);
+  }
 
   handleKeyUp (e) {
     if (e.key === 'Escape' && !!this.props.type) {
       this.props.onClose();
     }
-  },
+  }
 
   componentDidMount () {
     window.addEventListener('keyup', this.handleKeyUp, false);
-  },
+  }
 
   componentWillUnmount () {
     window.removeEventListener('keyup', this.handleKeyUp);
-  },
+  }
 
   willEnter () {
     return { opacity: 0, scale: 0.98 };
-  },
+  }
 
   willLeave () {
     return { opacity: spring(0), scale: spring(0.98) };
-  },
+  }
 
   render () {
     const { type, props, onClose } = this.props;
@@ -81,6 +78,12 @@ const ModalRoot = React.createClass({
     );
   }
 
-});
+}
+
+ModalRoot.propTypes = {
+  type: PropTypes.string,
+  props: PropTypes.object,
+  onClose: PropTypes.func.isRequired
+};
 
 export default ModalRoot;
diff --git a/app/assets/javascripts/components/features/ui/components/onboarding_modal.jsx b/app/assets/javascripts/components/features/ui/components/onboarding_modal.jsx
index 36e0d0c8a..e39eaf8de 100644
--- a/app/assets/javascripts/components/features/ui/components/onboarding_modal.jsx
+++ b/app/assets/javascripts/components/features/ui/components/onboarding_modal.jsx
@@ -1,5 +1,5 @@
 import { connect } from 'react-redux';
-import PureRenderMixin from 'react-addons-pure-render-mixin';
+import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import Permalink from '../../../components/permalink';
@@ -32,8 +32,8 @@ const PageOne = ({ acct, domain }) => (
 );
 
 PageOne.propTypes = {
-  acct: React.PropTypes.string.isRequired,
-  domain: React.PropTypes.string.isRequired
+  acct: PropTypes.string.isRequired,
+  domain: PropTypes.string.isRequired
 };
 
 const PageTwo = () => (
@@ -85,7 +85,7 @@ const PageThree = ({ me, domain }) => (
 
 PageThree.propTypes = {
   me: ImmutablePropTypes.map.isRequired,
-  domain: React.PropTypes.string.isRequired
+  domain: PropTypes.string.isRequired
 };
 
 const PageFour = ({ domain, intl }) => (
@@ -119,8 +119,8 @@ const PageFour = ({ domain, intl }) => (
 );
 
 PageFour.propTypes = {
-  domain: React.PropTypes.string.isRequired,
-  intl: React.PropTypes.object.isRequired
+  domain: PropTypes.string.isRequired,
+  intl: PropTypes.object.isRequired
 };
 
 const PageSix = ({ admin }) => {
@@ -157,33 +157,27 @@ const mapStateToProps = state => ({
   domain: state.getIn(['meta', 'domain'])
 });
 
-const OnboardingModal = React.createClass({
+class OnboardingModal extends React.PureComponent {
 
-  propTypes: {
-    onClose: React.PropTypes.func.isRequired,
-    intl: React.PropTypes.object.isRequired,
-    me: ImmutablePropTypes.map.isRequired,
-    domain: React.PropTypes.string.isRequired,
-    admin: ImmutablePropTypes.map
-  },
-
-  getInitialState () {
-    return {
+  constructor (props, context) {
+    super(props, context);
+    this.state = {
       currentIndex: 0
     };
-  },
-
-  mixins: [PureRenderMixin],
+    this.handleSkip = this.handleSkip.bind(this);
+    this.handleDot = this.handleDot.bind(this);
+    this.handleNext = this.handleNext.bind(this);
+  }
 
   handleSkip (e) {
     e.preventDefault();
     this.props.onClose();
-  },
+  }
 
   handleDot (i, e) {
     e.preventDefault();
     this.setState({ currentIndex: i });
-  },
+  }
 
   handleNext (maxNum, e) {
     e.preventDefault();
@@ -193,7 +187,7 @@ const OnboardingModal = React.createClass({
     } else {
       this.props.onClose();
     }
-  },
+  }
 
   render () {
     const { me, admin, domain, intl } = this.props;
@@ -251,6 +245,14 @@ const OnboardingModal = React.createClass({
     );
   }
 
-});
+}
+
+OnboardingModal.propTypes = {
+  onClose: PropTypes.func.isRequired,
+  intl: PropTypes.object.isRequired,
+  me: ImmutablePropTypes.map.isRequired,
+  domain: PropTypes.string.isRequired,
+  admin: ImmutablePropTypes.map
+}
 
 export default connect(mapStateToProps)(injectIntl(OnboardingModal));
diff --git a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx b/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx
index 68002840f..93c7441de 100644
--- a/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx
+++ b/app/assets/javascripts/components/features/ui/components/tabs_bar.jsx
@@ -1,7 +1,7 @@
 import { Link } from 'react-router';
 import { FormattedMessage } from 'react-intl';
 
-const TabsBar = React.createClass({
+class TabsBar extends React.PureComponent {
 
   render () {
     return (
@@ -18,6 +18,6 @@ const TabsBar = React.createClass({
     );
   }
 
-});
+}
 
 export default TabsBar;
diff --git a/app/assets/javascripts/components/features/ui/components/upload_area.jsx b/app/assets/javascripts/components/features/ui/components/upload_area.jsx
index 70b687019..38c2ad904 100644
--- a/app/assets/javascripts/components/features/ui/components/upload_area.jsx
+++ b/app/assets/javascripts/components/features/ui/components/upload_area.jsx
@@ -1,14 +1,8 @@
-import PureRenderMixin from 'react-addons-pure-render-mixin';
+import PropTypes from 'prop-types';
 import { Motion, spring } from 'react-motion';
 import { FormattedMessage } from 'react-intl';
 
-const UploadArea = React.createClass({
-
-  propTypes: {
-    active: React.PropTypes.bool
-  },
-
-  mixins: [PureRenderMixin],
+class UploadArea extends React.PureComponent {
 
   render () {
     const { active } = this.props;
@@ -27,6 +21,10 @@ const UploadArea = React.createClass({
     );
   }
 
-});
+}
+
+UploadArea.propTypes = {
+  active: PropTypes.bool
+};
 
 export default UploadArea;
diff --git a/app/assets/javascripts/components/features/ui/components/video_modal.jsx b/app/assets/javascripts/components/features/ui/components/video_modal.jsx
index 1c3519bd3..adbab0494 100644
--- a/app/assets/javascripts/components/features/ui/components/video_modal.jsx
+++ b/app/assets/javascripts/components/features/ui/components/video_modal.jsx
@@ -1,6 +1,6 @@
 import LoadingIndicator from '../../../components/loading_indicator';
-import PureRenderMixin from 'react-addons-pure-render-mixin';
 import ImmutablePropTypes from 'react-immutable-proptypes';
+import PropTypes from 'prop-types';
 import ExtendedVideoPlayer from '../../../components/extended_video_player';
 import { defineMessages, injectIntl } from 'react-intl';
 import IconButton from '../../../components/icon_button';
@@ -16,16 +16,7 @@ const closeStyle = {
   right: '4px'
 };
 
-const VideoModal = React.createClass({
-
-  propTypes: {
-    media: ImmutablePropTypes.map.isRequired,
-    time: React.PropTypes.number,
-    onClose: React.PropTypes.func.isRequired,
-    intl: React.PropTypes.object.isRequired
-  },
-
-  mixins: [PureRenderMixin],
+class VideoModal extends React.PureComponent {
 
   render () {
     const { media, intl, time, onClose } = this.props;
@@ -42,6 +33,13 @@ const VideoModal = React.createClass({
     );
   }
 
-});
+}
+
+VideoModal.propTypes = {
+  media: ImmutablePropTypes.map.isRequired,
+  time: PropTypes.number,
+  onClose: PropTypes.func.isRequired,
+  intl: PropTypes.object.isRequired
+};
 
 export default injectIntl(VideoModal);
diff --git a/app/assets/javascripts/components/features/ui/index.jsx b/app/assets/javascripts/components/features/ui/index.jsx
index d3090ae9b..1f35842d9 100644
--- a/app/assets/javascripts/components/features/ui/index.jsx
+++ b/app/assets/javascripts/components/features/ui/index.jsx
@@ -1,6 +1,6 @@
 import ColumnsArea from './components/columns_area';
 import NotificationsContainer from './containers/notifications_container';
-import PureRenderMixin from 'react-addons-pure-render-mixin';
+import PropTypes from 'prop-types';
 import LoadingBarContainer from './containers/loading_bar_container';
 import HomeTimeline from '../home_timeline';
 import Compose from '../compose';
@@ -15,26 +15,26 @@ import { refreshTimeline } from '../../actions/timelines';
 import { refreshNotifications } from '../../actions/notifications';
 import UploadArea from './components/upload_area';
 
-const UI = React.createClass({
+class UI extends React.PureComponent {
 
-  propTypes: {
-    dispatch: React.PropTypes.func.isRequired,
-    children: React.PropTypes.node
-  },
-
-  getInitialState () {
-    return {
+  constructor (props, context) {
+    super(props, context);
+    this.state = {
       width: window.innerWidth,
       draggingOver: false
     };
-  },
-
-  mixins: [PureRenderMixin],
+    this.handleResize = this.handleResize.bind(this);
+    this.handleDragEnter = this.handleDragEnter.bind(this);
+    this.handleDragOver = this.handleDragOver.bind(this);
+    this.handleDrop = this.handleDrop.bind(this);
+    this.handleDragLeave = this.handleDragLeave.bind(this);
+    this.setRef = this.setRef.bind(this);
+  }
 
   @debounce(500)
   handleResize () {
     this.setState({ width: window.innerWidth });
-  },
+  }
 
   handleDragEnter (e) {
     e.preventDefault();
@@ -50,7 +50,7 @@ const UI = React.createClass({
     if (e.dataTransfer && e.dataTransfer.items.length > 0) {
       this.setState({ draggingOver: true });
     }
-  },
+  }
 
   handleDragOver (e) {
     e.preventDefault();
@@ -63,7 +63,7 @@ const UI = React.createClass({
     }
 
     return false;
-  },
+  }
 
   handleDrop (e) {
     e.preventDefault();
@@ -73,7 +73,7 @@ const UI = React.createClass({
     if (e.dataTransfer && e.dataTransfer.files.length === 1) {
       this.props.dispatch(uploadCompose(e.dataTransfer.files));
     }
-  },
+  }
 
   handleDragLeave (e) {
     e.preventDefault();
@@ -86,7 +86,7 @@ const UI = React.createClass({
     }
 
     this.setState({ draggingOver: false });
-  },
+  }
 
   componentWillMount () {
     window.addEventListener('resize', this.handleResize, { passive: true });
@@ -97,7 +97,7 @@ const UI = React.createClass({
 
     this.props.dispatch(refreshTimeline('home'));
     this.props.dispatch(refreshNotifications());
-  },
+  }
 
   componentWillUnmount () {
     window.removeEventListener('resize', this.handleResize);
@@ -105,11 +105,11 @@ const UI = React.createClass({
     document.removeEventListener('dragover', this.handleDragOver);
     document.removeEventListener('drop', this.handleDrop);
     document.removeEventListener('dragleave', this.handleDragLeave);
-  },
+  }
 
   setRef (c) {
     this.node = c;
-  },
+  }
 
   render () {
     const { width, draggingOver } = this.state;
@@ -148,6 +148,11 @@ const UI = React.createClass({
     );
   }
 
-});
+}
+
+UI.propTypes = {
+  dispatch: PropTypes.func.isRequired,
+  children: PropTypes.node
+};
 
 export default connect()(UI);