Bug 1418250 - JSON Viewer to ES6 Classes, prop-types and react-dom-factories r?honza draft
authorMichael Ratcliffe <mratcliffe@mozilla.com>
Fri, 17 Nov 2017 10:28:07 +0000
changeset 699627 d29134c2cfa53f8ac401f89d7d67d0d4e4d49f87
parent 699096 a3f183201f7f183c263d554bfb15fbf0b0ed2ea4
child 740672 ef57b628e25bae5d7e31c6b68990f249786cf56f
push id89618
push userbmo:mratcliffe@mozilla.com
push dateFri, 17 Nov 2017 10:37:32 +0000
reviewershonza
bugs1418250
milestone59.0a1
Bug 1418250 - JSON Viewer to ES6 Classes, prop-types and react-dom-factories r?honza MozReview-Commit-ID: Cy6Tp71zTwk
devtools/client/jsonview/components/Headers.js
devtools/client/jsonview/components/HeadersPanel.js
devtools/client/jsonview/components/JsonPanel.js
devtools/client/jsonview/components/MainTabbedArea.js
devtools/client/jsonview/components/SearchBox.js
devtools/client/jsonview/components/TextPanel.js
devtools/client/jsonview/components/reps/Toolbar.js
--- a/devtools/client/jsonview/components/Headers.js
+++ b/devtools/client/jsonview/components/Headers.js
@@ -2,83 +2,88 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 define(function (require, exports, module) {
-  const { DOM: dom, createFactory, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
+  const { createFactory, Component } = require("devtools/client/shared/vendor/react");
+  const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+  const dom = require("devtools/client/shared/vendor/react-dom-factories");
 
   const { div, span, table, tbody, tr, td, } = dom;
 
   /**
    * This template is responsible for rendering basic layout
    * of the 'Headers' panel. It displays HTTP headers groups such as
    * received or response headers.
    */
-  let Headers = createClass({
-    displayName: "Headers",
+  class Headers extends Component {
+    static get propTypes() {
+      return {
+        data: PropTypes.object,
+      };
+    }
 
-    propTypes: {
-      data: PropTypes.object,
-    },
+    constructor(props) {
+      super(props);
+      this.state = {};
+    }
 
-    getInitialState: function () {
-      return {};
-    },
-
-    render: function () {
+    render() {
       let data = this.props.data;
 
       return (
         div({className: "netInfoHeadersTable"},
           div({className: "netHeadersGroup"},
             div({className: "netInfoHeadersGroup"},
               JSONView.Locale.$STR("jsonViewer.responseHeaders")
             ),
             table({cellPadding: 0, cellSpacing: 0},
-              HeaderList({headers: data.response})
+              HeaderListFactory({headers: data.response})
             )
           ),
           div({className: "netHeadersGroup"},
             div({className: "netInfoHeadersGroup"},
               JSONView.Locale.$STR("jsonViewer.requestHeaders")
             ),
             table({cellPadding: 0, cellSpacing: 0},
-              HeaderList({headers: data.request})
+              HeaderListFactory({headers: data.request})
             )
           )
         )
       );
     }
-  });
+  }
 
   /**
    * This template renders headers list,
    * name + value pairs.
    */
-  let HeaderList = createFactory(createClass({
-    displayName: "HeaderList",
+  class HeaderList extends Component {
+    static get propTypes() {
+      return {
+        headers: PropTypes.arrayOf(PropTypes.shape({
+          name: PropTypes.string,
+          value: PropTypes.string
+        }))
+      };
+    }
 
-    propTypes: {
-      headers: PropTypes.arrayOf(PropTypes.shape({
-        name: PropTypes.string,
-        value: PropTypes.string
-      }))
-    },
+    constructor(props) {
+      super(props);
 
-    getInitialState: function () {
-      return {
+      this.state = {
         headers: []
       };
-    },
+    }
 
-    render: function () {
+    render() {
       let headers = this.props.headers;
 
       headers.sort(function (a, b) {
         return a.name > b.name ? 1 : -1;
       });
 
       let rows = [];
       headers.forEach(header => {
@@ -93,13 +98,15 @@ define(function (require, exports, modul
       });
 
       return (
         tbody({},
           rows
         )
       );
     }
-  }));
+  }
+
+  let HeaderListFactory = createFactory(HeaderList);
 
   // Exports from this module
   exports.Headers = Headers;
 });
--- a/devtools/client/jsonview/components/HeadersPanel.js
+++ b/devtools/client/jsonview/components/HeadersPanel.js
@@ -2,80 +2,91 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 define(function (require, exports, module) {
-  const { DOM: dom, createFactory, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
+  const { createFactory, Component } = require("devtools/client/shared/vendor/react");
+  const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+  const dom = require("devtools/client/shared/vendor/react-dom-factories");
 
   const { createFactories } = require("devtools/client/shared/react-utils");
 
   const { Headers } = createFactories(require("./Headers"));
   const { Toolbar, ToolbarButton } = createFactories(require("./reps/Toolbar"));
 
   const { div } = dom;
 
   /**
    * This template represents the 'Headers' panel
    * s responsible for rendering its content.
    */
-  let HeadersPanel = createClass({
-    displayName: "HeadersPanel",
+  class HeadersPanel extends Component {
+    static get propTypes() {
+      return {
+        actions: PropTypes.object,
+        data: PropTypes.object,
+      };
+    }
 
-    propTypes: {
-      actions: PropTypes.object,
-      data: PropTypes.object,
-    },
+    constructor(props) {
+      super(props);
 
-    getInitialState: function () {
-      return {
+      this.state = {
         data: {}
       };
-    },
+    }
 
-    render: function () {
+    render() {
       let data = this.props.data;
 
       return (
         div({className: "headersPanelBox tab-panel-inner"},
-          HeadersToolbar({actions: this.props.actions}),
+          HeadersToolbarFactory({actions: this.props.actions}),
           div({className: "panelContent"},
             Headers({data: data})
           )
         )
       );
     }
-  });
+  }
 
   /**
    * This template is responsible for rendering a toolbar
    * within the 'Headers' panel.
    */
-  let HeadersToolbar = createFactory(createClass({
-    displayName: "HeadersToolbar",
+  class HeadersToolbar extends Component {
+    static get propTypes() {
+      return {
+        actions: PropTypes.object,
+      };
+    }
 
-    propTypes: {
-      actions: PropTypes.object,
-    },
+    constructor(props) {
+      super(props);
+      this.onCopy = this.onCopy.bind(this);
+    }
 
     // Commands
 
-    onCopy: function (event) {
+    onCopy(event) {
       this.props.actions.onCopyHeaders();
-    },
+    }
 
-    render: function () {
+    render() {
       return (
         Toolbar({},
           ToolbarButton({className: "btn copy", onClick: this.onCopy},
             JSONView.Locale.$STR("jsonViewer.Copy")
           )
         )
       );
-    },
-  }));
+    }
+  }
+
+  let HeadersToolbarFactory = createFactory(HeadersToolbar);
 
   // Exports from this module
   exports.HeadersPanel = HeadersPanel;
 });
--- a/devtools/client/jsonview/components/JsonPanel.js
+++ b/devtools/client/jsonview/components/JsonPanel.js
@@ -2,18 +2,21 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 define(function (require, exports, module) {
-  const { DOM: dom, createFactory, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
-  const TreeView = createFactory(require("devtools/client/shared/components/tree/TreeView"));
+  const { createFactory, Component } = require("devtools/client/shared/vendor/react");
+  const dom = require("devtools/client/shared/vendor/react-dom-factories");
+  const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+  const TreeView =
+    createFactory(require("devtools/client/shared/components/tree/TreeView"));
 
   const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
   const { createFactories } = require("devtools/client/shared/react-utils");
   const { Rep } = REPS;
 
   const { SearchBox } = createFactories(require("./SearchBox"));
   const { Toolbar, ToolbarButton } = createFactories(require("./reps/Toolbar"));
 
@@ -23,93 +26,98 @@ define(function (require, exports, modul
     return Object(value) === value;
   }
 
   /**
    * This template represents the 'JSON' panel. The panel is
    * responsible for rendering an expandable tree that allows simple
    * inspection of JSON structure.
    */
-  let JsonPanel = createClass({
-    displayName: "JsonPanel",
-
-    propTypes: {
-      data: PropTypes.oneOfType([
-        PropTypes.string,
-        PropTypes.array,
-        PropTypes.object,
-        PropTypes.bool,
-        PropTypes.number
-      ]),
-      expandedNodes: PropTypes.instanceOf(Set),
-      searchFilter: PropTypes.string,
-      actions: PropTypes.object,
-    },
+  class JsonPanel extends Component {
+    static get propTypes() {
+      return {
+        data: PropTypes.oneOfType([
+          PropTypes.string,
+          PropTypes.array,
+          PropTypes.object,
+          PropTypes.bool,
+          PropTypes.number
+        ]),
+        expandedNodes: PropTypes.instanceOf(Set),
+        searchFilter: PropTypes.string,
+        actions: PropTypes.object,
+      };
+    }
 
-    getInitialState: function () {
-      return {};
-    },
-
-    componentDidMount: function () {
-      document.addEventListener("keypress", this.onKeyPress, true);
-    },
+    constructor(props) {
+      super(props);
+      this.state = {};
+      this.onKeyPress = this.onKeyPress.bind(this);
+      this.onFilter = this.onFilter.bind(this);
+      this.renderValue = this.renderValue.bind(this);
+      this.renderTree = this.renderTree.bind(this);
+    }
 
-    componentWillUnmount: function () {
-      document.removeEventListener("keypress", this.onKeyPress, true);
-    },
+    componentDidMount() {
+      document.addEventListener("keypress", this.onKeyPress, true);
+    }
 
-    onKeyPress: function (e) {
+    componentWillUnmount() {
+      document.removeEventListener("keypress", this.onKeyPress, true);
+    }
+
+    onKeyPress(e) {
       // XXX shortcut for focusing the Filter field (see Bug 1178771).
-    },
+    }
 
-    onFilter: function (object) {
+    onFilter(object) {
       if (!this.props.searchFilter) {
         return true;
       }
 
       let json = object.name + JSON.stringify(object.value);
       return json.toLowerCase().indexOf(this.props.searchFilter.toLowerCase()) >= 0;
-    },
+    }
 
-    renderValue: props => {
+    renderValue(props) {
       let member = props.member;
 
       // Hide object summary when non-empty object is expanded (bug 1244912).
       if (isObject(member.value) && member.hasChildren && member.open) {
         return null;
       }
 
       // Render the value (summary) using Reps library.
       return Rep(Object.assign({}, props, {
         cropLimit: 50,
         noGrip: true,
         omitLinkHref: false,
       }));
-    },
+    }
 
-    renderTree: function () {
+    renderTree() {
       // Append custom column for displaying values. This column
       // Take all available horizontal space.
       let columns = [{
         id: "value",
         width: "100%"
       }];
 
       // Render tree component.
       return TreeView({
         object: this.props.data,
         mode: MODE.TINY,
         onFilter: this.onFilter,
         columns: columns,
         renderValue: this.renderValue,
         expandedNodes: this.props.expandedNodes,
       });
-    },
+    }
 
-    render: function () {
+    render() {
       let content;
       let data = this.props.data;
 
       if (!isObject(data)) {
         content = div({className: "jsonPrimitiveValue"}, Rep({
           object: data
         }));
       } else if (data instanceof Error) {
@@ -117,57 +125,65 @@ define(function (require, exports, modul
           data + ""
         );
       } else {
         content = this.renderTree();
       }
 
       return (
         div({className: "jsonPanelBox tab-panel-inner"},
-          JsonToolbar({actions: this.props.actions}),
+          JsonToolbarFactory({actions: this.props.actions}),
           div({className: "panelContent"},
             content
           )
         )
       );
     }
-  });
+  }
 
   /**
    * This template represents a toolbar within the 'JSON' panel.
    */
-  let JsonToolbar = createFactory(createClass({
-    displayName: "JsonToolbar",
+  class JsonToolbar extends Component {
+    static get propTypes() {
+      return {
+        actions: PropTypes.object,
+      };
+    }
 
-    propTypes: {
-      actions: PropTypes.object,
-    },
+    constructor(props) {
+      super(props);
+      this.onSave = this.onSave.bind(this);
+      this.onCopy = this.onCopy.bind(this);
+    }
 
     // Commands
 
-    onSave: function (event) {
+    onSave(event) {
       this.props.actions.onSaveJson();
-    },
+    }
 
-    onCopy: function (event) {
+    onCopy(event) {
       this.props.actions.onCopyJson();
-    },
+    }
 
-    render: function () {
+    render() {
       return (
         Toolbar({},
           ToolbarButton({className: "btn save", onClick: this.onSave},
             JSONView.Locale.$STR("jsonViewer.Save")
           ),
           ToolbarButton({className: "btn copy", onClick: this.onCopy},
             JSONView.Locale.$STR("jsonViewer.Copy")
           ),
           SearchBox({
             actions: this.props.actions
           })
         )
       );
-    },
-  }));
+    }
+  }
+
+  let JsonToolbarFactory = createFactory(JsonToolbar);
 
   // Exports from this module
   exports.JsonPanel = JsonPanel;
 });
--- a/devtools/client/jsonview/components/MainTabbedArea.js
+++ b/devtools/client/jsonview/components/MainTabbedArea.js
@@ -2,61 +2,65 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 define(function (require, exports, module) {
-  const { createClass, PropTypes } = require("devtools/client/shared/vendor/react");
-
+  const { Component } = require("devtools/client/shared/vendor/react");
+  const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
   const { createFactories } = require("devtools/client/shared/react-utils");
   const { JsonPanel } = createFactories(require("./JsonPanel"));
   const { TextPanel } = createFactories(require("./TextPanel"));
   const { HeadersPanel } = createFactories(require("./HeadersPanel"));
   const { Tabs, TabPanel } = createFactories(require("devtools/client/shared/components/tabs/Tabs"));
 
   /**
    * This object represents the root application template
    * responsible for rendering the basic tab layout.
    */
-  let MainTabbedArea = createClass({
-    displayName: "MainTabbedArea",
+  class MainTabbedArea extends Component {
+    static get propTypes() {
+      return {
+        jsonText: PropTypes.string,
+        tabActive: PropTypes.number,
+        actions: PropTypes.object,
+        headers: PropTypes.object,
+        searchFilter: PropTypes.string,
+        json: PropTypes.oneOfType([
+          PropTypes.string,
+          PropTypes.object,
+          PropTypes.array,
+          PropTypes.bool,
+          PropTypes.number
+        ]),
+        expandedNodes: PropTypes.instanceOf(Set),
+      };
+    }
 
-    propTypes: {
-      jsonText: PropTypes.string,
-      tabActive: PropTypes.number,
-      actions: PropTypes.object,
-      headers: PropTypes.object,
-      searchFilter: PropTypes.string,
-      json: PropTypes.oneOfType([
-        PropTypes.string,
-        PropTypes.object,
-        PropTypes.array,
-        PropTypes.bool,
-        PropTypes.number
-      ]),
-      expandedNodes: PropTypes.instanceOf(Set),
-    },
+    constructor(props) {
+      super(props);
 
-    getInitialState: function () {
-      return {
+      this.state = {
         json: {},
         headers: {},
-        jsonText: this.props.jsonText,
-        tabActive: this.props.tabActive
+        jsonText: props.jsonText,
+        tabActive: props.tabActive
       };
-    },
 
-    onTabChanged: function (index) {
+      this.onTabChanged = this.onTabChanged.bind(this);
+    }
+
+    onTabChanged(index) {
       this.setState({tabActive: index});
-    },
+    }
 
-    render: function () {
+    render() {
       return (
         Tabs({
           tabActive: this.state.tabActive,
           onAfterChange: this.onTabChanged},
           TabPanel({
             className: "json",
             title: JSONView.Locale.$STR("jsonViewer.tab.JSON")},
             JsonPanel({
@@ -82,13 +86,13 @@ define(function (require, exports, modul
               data: this.props.headers,
               actions: this.props.actions,
               searchFilter: this.props.searchFilter
             })
           )
         )
       );
     }
-  });
+  }
 
   // Exports from this module
   exports.MainTabbedArea = MainTabbedArea;
 });
--- a/devtools/client/jsonview/components/SearchBox.js
+++ b/devtools/client/jsonview/components/SearchBox.js
@@ -2,54 +2,62 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 define(function (require, exports, module) {
-  const { DOM: dom, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
+  const { Component } = require("devtools/client/shared/vendor/react");
+  const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+  const dom = require("devtools/client/shared/vendor/react-dom-factories");
 
   const { input } = dom;
 
   // For smooth incremental searching (in case the user is typing quickly).
   const searchDelay = 250;
 
   /**
    * This object represents a search box located at the
    * top right corner of the application.
    */
-  let SearchBox = createClass({
-    displayName: "SearchBox",
+  class SearchBox extends Component {
+    static get propTypes() {
+      return {
+        actions: PropTypes.object,
+      };
+    }
 
-    propTypes: {
-      actions: PropTypes.object,
-    },
+    constructor(props) {
+      super(props);
+      this.onSearch = this.onSearch.bind(this);
+      this.doSearch = this.doSearch.bind(this);
+    }
 
-    onSearch: function (event) {
+    onSearch(event) {
       let searchBox = event.target;
       let win = searchBox.ownerDocument.defaultView;
 
       if (this.searchTimeout) {
         win.clearTimeout(this.searchTimeout);
       }
 
       let callback = this.doSearch.bind(this, searchBox);
       this.searchTimeout = win.setTimeout(callback, searchDelay);
-    },
+    }
 
-    doSearch: function (searchBox) {
+    doSearch(searchBox) {
       this.props.actions.onSearch(searchBox.value);
-    },
+    }
 
-    render: function () {
+    render() {
       return (
         input({className: "searchBox devtools-filterinput",
                placeholder: JSONView.Locale.$STR("jsonViewer.filterJSON"),
                onChange: this.onSearch})
       );
-    },
-  });
+    }
+  }
 
   // Exports from this module
   exports.SearchBox = SearchBox;
 });
--- a/devtools/client/jsonview/components/TextPanel.js
+++ b/devtools/client/jsonview/components/TextPanel.js
@@ -2,83 +2,92 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 define(function (require, exports, module) {
-  const { DOM: dom, createFactory, createClass, PropTypes } = require("devtools/client/shared/vendor/react");
-
+  const { createFactory, Component } = require("devtools/client/shared/vendor/react");
+  const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+  const dom = require("devtools/client/shared/vendor/react-dom-factories");
   const { createFactories } = require("devtools/client/shared/react-utils");
   const { Toolbar, ToolbarButton } = createFactories(require("./reps/Toolbar"));
   const { div, pre } = dom;
 
   /**
    * This template represents the 'Raw Data' panel displaying
    * JSON as a text received from the server.
    */
-  let TextPanel = createClass({
-    displayName: "TextPanel",
+  class TextPanel extends Component {
+    static get propTypes() {
+      return {
+        isValidJson: PropTypes.bool,
+        actions: PropTypes.object,
+        data: PropTypes.string
+      };
+    }
 
-    propTypes: {
-      isValidJson: PropTypes.bool,
-      actions: PropTypes.object,
-      data: PropTypes.string
-    },
+    constructor(props) {
+      super(props);
+      this.state = {};
+    }
 
-    getInitialState: function () {
-      return {};
-    },
-
-    render: function () {
+    render() {
       return (
         div({className: "textPanelBox tab-panel-inner"},
-          TextToolbar({
+          TextToolbarFactory({
             actions: this.props.actions,
             isValidJson: this.props.isValidJson
           }),
           div({className: "panelContent"},
             pre({className: "data"},
               this.props.data
             )
           )
         )
       );
     }
-  });
+  }
 
   /**
    * This object represents a toolbar displayed within the
    * 'Raw Data' panel.
    */
-  let TextToolbar = createFactory(createClass({
-    displayName: "TextToolbar",
+  class TextToolbar extends Component {
+    static get propTypes() {
+      return {
+        actions: PropTypes.object,
+        isValidJson: PropTypes.bool
+      };
+    }
 
-    propTypes: {
-      actions: PropTypes.object,
-      isValidJson: PropTypes.bool
-    },
+    constructor(props) {
+      super(props);
+      this.onPrettify = this.onPrettify.bind(this);
+      this.onSave = this.onSave.bind(this);
+      this.onCopy = this.onCopy.bind(this);
+    }
 
     // Commands
 
-    onPrettify: function (event) {
+    onPrettify(event) {
       this.props.actions.onPrettify();
-    },
+    }
 
-    onSave: function (event) {
+    onSave(event) {
       this.props.actions.onSaveJson();
-    },
+    }
 
-    onCopy: function (event) {
+    onCopy(event) {
       this.props.actions.onCopyJson();
-    },
+    }
 
-    render: function () {
+    render() {
       return (
         Toolbar({},
           ToolbarButton({
             className: "btn save",
             onClick: this.onSave},
             JSONView.Locale.$STR("jsonViewer.Save")
           ),
           ToolbarButton({
@@ -90,14 +99,16 @@ define(function (require, exports, modul
             ToolbarButton({
               className: "btn prettyprint",
               onClick: this.onPrettify},
               JSONView.Locale.$STR("jsonViewer.PrettyPrint")
             ) :
             null
         )
       );
-    },
-  }));
+    }
+  }
+
+  let TextToolbarFactory = createFactory(TextToolbar);
 
   // Exports from this module
   exports.TextPanel = TextPanel;
 });
--- a/devtools/client/jsonview/components/reps/Toolbar.js
+++ b/devtools/client/jsonview/components/reps/Toolbar.js
@@ -2,57 +2,58 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 define(function (require, exports, module) {
-  const React = require("devtools/client/shared/vendor/react");
-  const DOM = React.DOM;
+  const { Component } = require("devtools/client/shared/vendor/react");
+  const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+  const dom = require("devtools/client/shared/vendor/react-dom-factories");
 
   /**
    * Renders a simple toolbar.
    */
-  let Toolbar = React.createClass({
-    displayName: "Toolbar",
+  class Toolbar extends Component {
+    static get propTypes() {
+      return {
+        children: PropTypes.oneOfType([
+          PropTypes.array,
+          PropTypes.element
+        ])
+      };
+    }
 
-    propTypes: {
-      children: React.PropTypes.oneOfType([
-        React.PropTypes.array,
-        React.PropTypes.element
-      ])
-    },
-
-    render: function () {
+    render() {
       return (
-        DOM.div({className: "toolbar"},
+        dom.div({className: "toolbar"},
           this.props.children
         )
       );
     }
-  });
+  }
 
   /**
    * Renders a simple toolbar button.
    */
-  let ToolbarButton = React.createClass({
-    displayName: "ToolbarButton",
+  class ToolbarButton extends Component {
+    static get propTypes() {
+      return {
+        active: PropTypes.bool,
+        disabled: PropTypes.bool,
+        children: PropTypes.string,
+      };
+    }
 
-    propTypes: {
-      active: React.PropTypes.bool,
-      disabled: React.PropTypes.bool,
-      children: React.PropTypes.string,
-    },
-
-    render: function () {
+    render() {
       let props = Object.assign({className: "btn"}, this.props);
       return (
-        DOM.button(props, this.props.children)
+        dom.button(props, this.props.children)
       );
-    },
-  });
+    }
+  }
 
   // Exports from this module
   exports.Toolbar = Toolbar;
   exports.ToolbarButton = ToolbarButton;
 });