Bug 1479416 - Add Connect sidebar item to aboutdebugging-new;r=daisuke
authorJulian Descottes <jdescottes@mozilla.com>
Fri, 03 Aug 2018 07:23:33 +0000
changeset 429963 42a9a104150d73d6f00f9e64ca081bcffc576618
parent 429962 ec5ee2990e219b1cd1c95053a21878d69ddad52c
child 429964 a9582879cc3ebaafc22abe46f952f1cc1051829b
push id67307
push userjdescottes@mozilla.com
push dateFri, 03 Aug 2018 07:24:12 +0000
treeherderautoland@42a9a104150d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdaisuke
bugs1479416
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1479416 - Add Connect sidebar item to aboutdebugging-new;r=daisuke This patch adds: - a new action actions/ui:selectPage - takes a page id in argument - returns type PAGE_SELECTED, with selected page id as 'page' - a new reducer reducers/ui-state - on PAGE_SELECTED, updates the state by setting 'page' in 'selectedPage' The state of the application after this patch looks as follows: { ui: { selectedPage: String } } As we add more devices the way we identify the selected page might change (maybe use an index instead of an id) but that is good enough for now. The "dispatch" helper is forwarded to the App component via connect. From that point, components are responsible for forwarding the dispatch property to all components who need to dispatch actions. Differential Revision: https://phabricator.services.mozilla.com/D2549
devtools/client/aboutdebugging-new/aboutdebugging.js
devtools/client/aboutdebugging-new/src/actions/index.js
devtools/client/aboutdebugging-new/src/actions/moz.build
devtools/client/aboutdebugging-new/src/actions/ui.js
devtools/client/aboutdebugging-new/src/components/App.js
devtools/client/aboutdebugging-new/src/components/Sidebar.js
devtools/client/aboutdebugging-new/src/components/SidebarItem.js
devtools/client/aboutdebugging-new/src/constants.js
devtools/client/aboutdebugging-new/src/create-store.js
devtools/client/aboutdebugging-new/src/moz.build
devtools/client/aboutdebugging-new/src/reducers/index.js
devtools/client/aboutdebugging-new/src/reducers/moz.build
devtools/client/aboutdebugging-new/src/reducers/ui-state.js
--- a/devtools/client/aboutdebugging-new/aboutdebugging.js
+++ b/devtools/client/aboutdebugging-new/aboutdebugging.js
@@ -7,32 +7,41 @@
 const { BrowserLoader } =
   ChromeUtils.import("resource://devtools/client/shared/browser-loader.js", {});
 const { require } = BrowserLoader({
   baseURI: "resource://devtools/client/aboutdebugging-new/",
   window,
 });
 const Services = require("Services");
 
+const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
 const { createFactory } =
   require("devtools/client/shared/vendor/react");
 const { render, unmountComponentAtNode } =
   require("devtools/client/shared/vendor/react-dom");
+const Provider =
+  createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
+
+const actions = require("./src/actions/index");
+const { configureStore } = require("./src/create-store");
 
 const App = createFactory(require("./src/components/App"));
 
 const AboutDebugging = {
   init() {
     if (!Services.prefs.getBoolPref("devtools.enabled", true)) {
       // If DevTools are disabled, navigate to about:devtools.
       window.location = "about:devtools?reason=AboutDebugging";
       return;
     }
 
-    render(App(), this.mount);
+    this.store = configureStore();
+    this.actions = bindActionCreators(actions, this.store.dispatch);
+
+    render(Provider({ store: this.store }, App()), this.mount);
   },
 
   destroy() {
     unmountComponentAtNode(this.mount);
   },
 
   get mount() {
     return document.getElementById("mount");
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/actions/index.js
@@ -0,0 +1,9 @@
+/* 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";
+
+const ui = require("./ui");
+
+Object.assign(exports, ui);
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/actions/moz.build
@@ -0,0 +1,8 @@
+# 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/.
+
+DevToolsModules(
+    'index.js',
+    'ui.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/actions/ui.js
@@ -0,0 +1,20 @@
+/* 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";
+
+const {
+  PAGE_SELECTED,
+} = require("../constants");
+
+function selectPage(page) {
+  return {
+    type: PAGE_SELECTED,
+    page,
+  };
+}
+
+module.exports = {
+  selectPage,
+};
--- a/devtools/client/aboutdebugging-new/src/components/App.js
+++ b/devtools/client/aboutdebugging-new/src/components/App.js
@@ -1,25 +1,61 @@
 /* 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";
 
+const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { createFactory, PureComponent } = 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 { PAGES } = require("../constants");
 
 const RuntimePage = createFactory(require("./RuntimePage"));
 const Sidebar = createFactory(require("./Sidebar"));
 
 class App extends PureComponent {
+  static get propTypes() {
+    return {
+      // The "dispatch" helper is forwarded to the App component via connect.
+      // From that point, components are responsible for forwarding the dispatch
+      // property to all components who need to dispatch actions.
+      dispatch: PropTypes.func.isRequired,
+      selectedPage: PropTypes.string.isRequired,
+    };
+  }
+
+  getSelectedPageComponent() {
+    switch (this.props.selectedPage) {
+      case PAGES.THIS_FIREFOX:
+        return RuntimePage();
+      default:
+        // Invalid page, blank.
+        return null;
+    }
+  }
+
   render() {
+    const { dispatch, selectedPage } = this.props;
+
     return dom.div(
       {
         className: "app",
       },
-      Sidebar(),
-      RuntimePage(),
+      Sidebar({ dispatch, selectedPage }),
+      this.getSelectedPageComponent(),
     );
   }
 }
 
-module.exports = App;
+const mapStateToProps = state => {
+  return {
+    selectedPage: state.ui.selectedPage,
+  };
+};
+
+const mapDispatchToProps = dispatch => ({
+  dispatch,
+});
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(App);
--- a/devtools/client/aboutdebugging-new/src/components/Sidebar.js
+++ b/devtools/client/aboutdebugging-new/src/components/Sidebar.js
@@ -1,32 +1,53 @@
 /* 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";
 
 const { createFactory, PureComponent } = 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 { PAGES } = require("../constants");
 
 const SidebarItem = createFactory(require("./SidebarItem"));
-
 const FIREFOX_ICON = "chrome://devtools/skin/images/firefox-logo-glyph.svg";
+const MOBILE_ICON = "chrome://devtools/skin/images/firefox-logo-glyph.svg";
 
 class Sidebar extends PureComponent {
+  static get propTypes() {
+    return {
+      dispatch: PropTypes.func.isRequired,
+      selectedPage: PropTypes.string.isRequired,
+    };
+  }
+
   render() {
-    return dom.section(
+    const { dispatch, selectedPage } = this.props;
+
+    return dom.aside(
       {
         className: "sidebar",
       },
       dom.ul(
         {},
         SidebarItem({
+          id: PAGES.THIS_FIREFOX,
+          dispatch,
           icon: FIREFOX_ICON,
-          isSelected: true,
+          isSelected: PAGES.THIS_FIREFOX === selectedPage,
           name: "This Firefox",
+        }),
+        SidebarItem({
+          id: PAGES.CONNECT,
+          dispatch,
+          icon: MOBILE_ICON,
+          isSelected: PAGES.CONNECT === selectedPage,
+          name: "Connect",
         })
       )
     );
   }
 }
 
 module.exports = Sidebar;
--- a/devtools/client/aboutdebugging-new/src/components/SidebarItem.js
+++ b/devtools/client/aboutdebugging-new/src/components/SidebarItem.js
@@ -3,34 +3,43 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { PureComponent } = 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 Actions = require("../actions/index");
+
 /**
  * This component displays an item of the Sidebar component.
  */
 class SidebarItem extends PureComponent {
   static get propTypes() {
     return {
+      dispatch: PropTypes.func.isRequired,
       icon: PropTypes.string.isRequired,
+      id: PropTypes.string.isRequired,
       isSelected: PropTypes.bool.isRequired,
       name: PropTypes.string.isRequired,
     };
   }
 
+  onItemClick() {
+    this.props.dispatch(Actions.selectPage(this.props.id));
+  }
+
   render() {
     const { icon, isSelected, name } = this.props;
 
     return dom.li(
       {
         className: "sidebar-item" + (isSelected ? " sidebar-item--selected" : ""),
+        onClick: () => this.onItemClick()
       },
       dom.img({
         className: "sidebar-item__icon" +
                    (isSelected ? " sidebar-item__icon--selected" : ""),
         src: icon,
       }),
       name
     );
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/constants.js
@@ -0,0 +1,17 @@
+/* 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";
+
+const actionTypes = {
+  PAGE_SELECTED: "PAGE_SELECTED",
+};
+
+const PAGES = {
+  THIS_FIREFOX: "this-firefox",
+  CONNECT: "connect",
+};
+
+// flatten constants
+module.exports = Object.assign({}, { PAGES }, actionTypes);
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/create-store.js
@@ -0,0 +1,18 @@
+/* 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";
+
+const { createStore } = require("devtools/client/shared/vendor/redux");
+
+const rootReducer = require("./reducers/index");
+const { UiState } = require("./reducers/ui-state");
+
+exports.configureStore = function() {
+  const initialState = {
+    ui: new UiState()
+  };
+
+  return createStore(rootReducer, initialState);
+};
--- a/devtools/client/aboutdebugging-new/src/moz.build
+++ b/devtools/client/aboutdebugging-new/src/moz.build
@@ -1,7 +1,14 @@
 # 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/.
 
 DIRS += [
+    'actions',
     'components',
+    'reducers',
 ]
+
+DevToolsModules(
+    'constants.js',
+    'create-store.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/reducers/index.js
@@ -0,0 +1,12 @@
+/* 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";
+
+const { combineReducers } = require("devtools/client/shared/vendor/redux");
+const { uiReducer } = require("./ui-state");
+
+module.exports = combineReducers({
+  ui: uiReducer
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/reducers/moz.build
@@ -0,0 +1,8 @@
+# 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/.
+
+DevToolsModules(
+    'index.js',
+    'ui-state.js',
+)
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/src/reducers/ui-state.js
@@ -0,0 +1,33 @@
+/* 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";
+
+const {
+  PAGE_SELECTED,
+  PAGES
+} = require("../constants");
+
+function UiState() {
+  return {
+    selectedPage: PAGES.THIS_FIREFOX
+  };
+}
+
+function uiReducer(state = UiState(), action) {
+  switch (action.type) {
+    case PAGE_SELECTED: {
+      const { page } = action;
+      return { selectedPage: page };
+    }
+
+    default:
+      return state;
+  }
+}
+
+module.exports = {
+  UiState,
+  uiReducer,
+};