From 2cc11969b7f26c405dafee63d44f4d50dbc937cc Mon Sep 17 00:00:00 2001 From: Dalton Barreto Date: Wed, 3 Aug 2016 14:16:24 -0300 Subject: [PATCH 1/6] Ignoring Vim's temp files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index edaacf371..5042be051 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ .DS_Store *.iml .idea/* +*.sw[o|p] +~* # npm node_modules/ From a906864d863ef254ad0b2dd6760dd0412e0264dd Mon Sep 17 00:00:00 2001 From: Dalton Barreto Date: Sat, 13 Aug 2016 09:21:33 -0300 Subject: [PATCH 2/6] Extracting navtabs as separated React Components --- .../components/AppsAndGroupsListComponent.jsx | 115 ++++++++++++++++++ src/js/components/TabPanesComponent.jsx | 104 +--------------- src/js/constants/tabs.js | 18 ++- 3 files changed, 133 insertions(+), 104 deletions(-) create mode 100644 src/js/components/AppsAndGroupsListComponent.jsx diff --git a/src/js/components/AppsAndGroupsListComponent.jsx b/src/js/components/AppsAndGroupsListComponent.jsx new file mode 100644 index 000000000..57d5ed509 --- /dev/null +++ b/src/js/components/AppsAndGroupsListComponent.jsx @@ -0,0 +1,115 @@ +import React from "react/addons"; + +import QueryParamsMixin from "../mixins/QueryParamsMixin"; +import {Link} from "react-router"; +import SidebarComponent from "../components/SidebarComponent"; +import AppListComponent from "../components/AppListComponent"; +import BreadcrumbComponent from "../components/BreadcrumbComponent"; +import FilterTypes from "../constants/FilterTypes"; +import Util from "../helpers/Util"; + +var AppsAndGroupsListComponent = React.createClass({ + displayName: "AppsAndGroupsListComponent", + + mixins: [QueryParamsMixin], + + getInitialState: function () { + return { + currentGroup: "/" + }; + }, + + componentWillReceiveProps: function () { + this.updateCurrentGroup(); + }, + + componentWillMount: function () { + this.updateCurrentGroup(); + }, + + updateCurrentGroup: function () { + var {groupId} = this.context.router.getCurrentParams(); + if (groupId == null) { + groupId = "/"; + } + groupId = decodeURIComponent(groupId); + if (!groupId.endsWith("/")) { + groupId += "/"; + } + + this.setState({ + currentGroup: groupId + }); + }, + + getContextualBar: function () { + var groupId = "/"; + var filters = this.getQueryParamObject(); + + if (filters[FilterTypes.TEXT] == null || + Util.isStringAndEmpty(filters[FilterTypes.TEXT])) { + return ; + } + + return ( +

+ + {`Search results for "${filters[FilterTypes.TEXT]}"`} + + {this.getClearLinkForFilter(FilterTypes.TEXT, + "Clear search", + "clear")} +

+ ); + }, + + render: function () { + var path = this.getCurrentPathname(); + var groupId = this.state.currentGroup; + + var newAppModalQuery = { + modal: "new-app" + }; + + var newGroupModalQuery = { + modal: "new-group" + }; + + if (groupId != null && groupId !== "/") { + newAppModalQuery.groupId = groupId; + newGroupModalQuery.groupId = groupId; + } + return ( +
+ +
+
+ {this.getContextualBar()} +
    +
  • + + Create Group + +
  • +
  • + + Create Application + +
  • +
+
+ +
+
+ ); + } + +}); + +export default AppsAndGroupsListComponent; diff --git a/src/js/components/TabPanesComponent.jsx b/src/js/components/TabPanesComponent.jsx index fcdcf4aba..74216df85 100644 --- a/src/js/components/TabPanesComponent.jsx +++ b/src/js/components/TabPanesComponent.jsx @@ -1,14 +1,7 @@ -import {Link} from "react-router"; import React from "react/addons"; -import AppListComponent from "../components/AppListComponent"; -import BreadcrumbComponent from "../components/BreadcrumbComponent"; -import DeploymentsListComponent from "../components/DeploymentsListComponent"; -import FilterTypes from "../constants/FilterTypes"; -import SidebarComponent from "../components/SidebarComponent"; import TabPaneComponent from "../components/TabPaneComponent"; import TogglableTabsComponent from "../components/TogglableTabsComponent"; -import Util from "../helpers/Util"; import tabs from "../constants/tabs"; import QueryParamsMixin from "../mixins/QueryParamsMixin"; @@ -22,56 +15,6 @@ var TabPanesComponent = React.createClass({ router: React.PropTypes.func }, - getInitialState: function () { - return { - currentGroup: "/" - }; - }, - - componentWillReceiveProps: function () { - this.updateCurrentGroup(); - }, - - componentWillMount: function () { - this.updateCurrentGroup(); - }, - - getContextualBar: function () { - var state = this.state; - var filters = this.getQueryParamObject(); - - if (filters[FilterTypes.TEXT] == null || - Util.isStringAndEmpty(filters[FilterTypes.TEXT])) { - return ; - } - - return ( -

- - {`Search results for "${filters[FilterTypes.TEXT]}"`} - - {this.getClearLinkForFilter(FilterTypes.TEXT, - "Clear search", - "clear")} -

- ); - }, - - updateCurrentGroup: function () { - var {groupId} = this.context.router.getCurrentParams(); - if (groupId == null) { - groupId = "/"; - } - groupId = decodeURIComponent(groupId); - if (!groupId.endsWith("/")) { - groupId += "/"; - } - - this.setState({ - currentGroup: groupId - }); - }, - getTabId: function () { var path = this.getCurrentPathname(); @@ -85,57 +28,14 @@ var TabPanesComponent = React.createClass({ }, render: function () { - var path = this.getCurrentPathname(); - var state = this.state; - var groupId = state.currentGroup; - - var newAppModalQuery = { - modal: "new-app" - }; - - var newGroupModalQuery = { - modal: "new-group" - }; - - if (groupId != null && groupId !== "/") { - newAppModalQuery.groupId = groupId; - newGroupModalQuery.groupId = groupId; - } - return ( -
- -
-
- {this.getContextualBar()} -
    -
  • - - Create Group - -
  • -
  • - - Create Application - -
  • -
-
- -
-
+ {React.createElement(tabs[0].component)}
- + {React.createElement(tabs[1].component)}
); diff --git a/src/js/constants/tabs.js b/src/js/constants/tabs.js index 48af08a4a..00b4f2fef 100644 --- a/src/js/constants/tabs.js +++ b/src/js/constants/tabs.js @@ -1,6 +1,20 @@ +import React from "react/addons"; +import DeploymentsListComponent + from "../components/DeploymentsListComponent"; +import AppsAndGroupsListComponent + from "../components/AppsAndGroupsListComponent"; + var tabs = [ - {id: "/apps", text: "Applications"}, - {id: "/deployments", text: "Deployments"} + { + id: "/apps", + text: "Applications", + component: AppsAndGroupsListComponent + }, + { + id: "/deployments", + text: "Deployments", + component: DeploymentsListComponent + } ]; export default tabs; From 91ad57b53f135798f9eb79dfaa3bcfc4c9b4a711 Mon Sep 17 00:00:00 2001 From: Dalton Barreto Date: Sat, 13 Aug 2016 09:51:11 -0300 Subject: [PATCH 3/6] Iterate over tabs array to render Nav Tabs --- src/js/components/TabPanesComponent.jsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/js/components/TabPanesComponent.jsx b/src/js/components/TabPanesComponent.jsx index 74216df85..f49047b8c 100644 --- a/src/js/components/TabPanesComponent.jsx +++ b/src/js/components/TabPanesComponent.jsx @@ -28,15 +28,17 @@ var TabPanesComponent = React.createClass({ }, render: function () { + var renderedNavTabComponents = tabs.map(tab => { + return ( + + {React.createElement(tab.component)} + + ); + }); return ( - - {React.createElement(tabs[0].component)} - - - {React.createElement(tabs[1].component)} - + {renderedNavTabComponents} ); } From 575193d0232c07ff4a45b0773080af8753610ca4 Mon Sep 17 00:00:00 2001 From: Dalton Barreto Date: Sun, 14 Aug 2016 16:27:20 -0300 Subject: [PATCH 4/6] Replacing constants/tabs.js with a NavTabStore --- src/js/components/Marathon.jsx | 8 ++++---- src/js/components/TabPanesComponent.jsx | 6 +++--- src/js/{constants/tabs.js => stores/NavTabStore.js} | 11 ++++++++++- src/test/units/NavTabsComponent.test.js | 4 ++-- 4 files changed, 19 insertions(+), 10 deletions(-) rename src/js/{constants/tabs.js => stores/NavTabStore.js} (65%) diff --git a/src/js/components/Marathon.jsx b/src/js/components/Marathon.jsx index b320df7df..ac80e4582 100644 --- a/src/js/components/Marathon.jsx +++ b/src/js/components/Marathon.jsx @@ -27,7 +27,7 @@ import PluginStore from "../stores/PluginStore"; import PluginEvents from "../events/PluginEvents"; import PluginMountPoints from "../plugin/shared/PluginMountPoints"; -import tabs from "../constants/tabs"; +import NavTabStore from "../stores/NavTabStore"; var Marathon = React.createClass({ displayName: "Marathon", @@ -105,10 +105,10 @@ var Marathon = React.createClass({ } var activeTabId = !this.context.router.isActive("404") - ? tabs[0].id + ? NavTabStore.getTabs()[0].id : null; - if (tabs.find(function (tab) { + if (NavTabStore.getTabs().find(function (tab) { return tab.id === path; })) { activeTabId = path; @@ -298,7 +298,7 @@ var Marathon = React.createClass({ + tabs={NavTabStore.getTabs()} />
diff --git a/src/js/components/TabPanesComponent.jsx b/src/js/components/TabPanesComponent.jsx index f49047b8c..b809cb905 100644 --- a/src/js/components/TabPanesComponent.jsx +++ b/src/js/components/TabPanesComponent.jsx @@ -2,7 +2,7 @@ import React from "react/addons"; import TabPaneComponent from "../components/TabPaneComponent"; import TogglableTabsComponent from "../components/TogglableTabsComponent"; -import tabs from "../constants/tabs"; +import NavTabStore from "../stores/NavTabStore"; import QueryParamsMixin from "../mixins/QueryParamsMixin"; @@ -18,7 +18,7 @@ var TabPanesComponent = React.createClass({ getTabId: function () { var path = this.getCurrentPathname(); - var hasTab = tabs.find(tab => tab.id === path); + var hasTab = NavTabStore.getTabs().find(tab => tab.id === path); if (hasTab) { return path; @@ -28,7 +28,7 @@ var TabPanesComponent = React.createClass({ }, render: function () { - var renderedNavTabComponents = tabs.map(tab => { + var renderedNavTabComponents = NavTabStore.getTabs().map(tab => { return ( {React.createElement(tab.component)} diff --git a/src/js/constants/tabs.js b/src/js/stores/NavTabStore.js similarity index 65% rename from src/js/constants/tabs.js rename to src/js/stores/NavTabStore.js index 00b4f2fef..e45200fb4 100644 --- a/src/js/constants/tabs.js +++ b/src/js/stores/NavTabStore.js @@ -1,4 +1,7 @@ +import {EventEmitter} from "events"; import React from "react/addons"; +import Util from "../helpers/Util"; + import DeploymentsListComponent from "../components/DeploymentsListComponent"; import AppsAndGroupsListComponent @@ -17,4 +20,10 @@ var tabs = [ } ]; -export default tabs; +var NavTabStore = Util.extendObject(EventEmitter.prototype, { + getTabs: function () { + return tabs; + } +}); + +export default NavTabStore; diff --git a/src/test/units/NavTabsComponent.test.js b/src/test/units/NavTabsComponent.test.js index d7cffb3e2..6e5224bd0 100644 --- a/src/test/units/NavTabsComponent.test.js +++ b/src/test/units/NavTabsComponent.test.js @@ -7,7 +7,7 @@ import NavTabsComponent from "../../js/components/NavTabsComponent"; import DeploymentEvents from "../../js/events/DeploymentEvents"; import DeploymentStore from "../../js/stores/DeploymentStore"; -import tabs from "../../js/constants/tabs"; +import NavTabStore from "../../js/stores/NavTabStore"; describe("Deployments navigation badge", function () { @@ -19,7 +19,7 @@ describe("Deployments navigation badge", function () { var props = { activeTabId: "/deployments", - tabs: tabs + tabs: NavTabStore.getTabs() }; DeploymentStore.once(DeploymentEvents.CHANGE, () => { From aaca770e987a9a604ce1b4a189f168fa5b2fd02d Mon Sep 17 00:00:00 2001 From: Dalton Barreto Date: Mon, 15 Aug 2016 17:12:31 -0300 Subject: [PATCH 5/6] Adding tests to NavTabStore events --- src/js/components/TabPanesComponent.jsx | 2 +- src/js/events/NavTabEvents.js | 6 +++ src/js/stores/NavTabStore.js | 13 ++++++ src/test/units/NavTabsStore.test.js | 53 +++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 src/js/events/NavTabEvents.js create mode 100644 src/test/units/NavTabsStore.test.js diff --git a/src/js/components/TabPanesComponent.jsx b/src/js/components/TabPanesComponent.jsx index b809cb905..5af81f36f 100644 --- a/src/js/components/TabPanesComponent.jsx +++ b/src/js/components/TabPanesComponent.jsx @@ -30,7 +30,7 @@ var TabPanesComponent = React.createClass({ render: function () { var renderedNavTabComponents = NavTabStore.getTabs().map(tab => { return ( - + {React.createElement(tab.component)} ); diff --git a/src/js/events/NavTabEvents.js b/src/js/events/NavTabEvents.js new file mode 100644 index 000000000..6078ba767 --- /dev/null +++ b/src/js/events/NavTabEvents.js @@ -0,0 +1,6 @@ +const NavTabEvents = { + CHANGE: "NAV_TAB_EVENTS_CHANGE", + APPEND_NAVTAB: "NAV_TAB_EVENTS_APPEND_NAVTAB" +}; + +export default Object.freeze(NavTabEvents); diff --git a/src/js/stores/NavTabStore.js b/src/js/stores/NavTabStore.js index e45200fb4..cdd455c52 100644 --- a/src/js/stores/NavTabStore.js +++ b/src/js/stores/NavTabStore.js @@ -1,6 +1,8 @@ import {EventEmitter} from "events"; import React from "react/addons"; +import PluginDispatcher from "../plugin/shared/PluginDispatcher"; import Util from "../helpers/Util"; +import NavTabEvents from "../events/NavTabEvents"; import DeploymentsListComponent from "../components/DeploymentsListComponent"; @@ -20,10 +22,21 @@ var tabs = [ } ]; +function appendTab(tab) { + tabs.push(tab); +} + var NavTabStore = Util.extendObject(EventEmitter.prototype, { getTabs: function () { return tabs; } }); +PluginDispatcher.register(event => { + if (event.eventType === NavTabEvents.APPEND_NAVTAB) { + event.data.map(appendTab); + NavTabStore.emit(NavTabEvents.CHANGE); + } +}); + export default NavTabStore; diff --git a/src/test/units/NavTabsStore.test.js b/src/test/units/NavTabsStore.test.js new file mode 100644 index 000000000..078d425dd --- /dev/null +++ b/src/test/units/NavTabsStore.test.js @@ -0,0 +1,53 @@ +import {expect} from "chai"; + +import NavTabStore from "../../js/stores/NavTabStore"; +import NavTabEvents from "../../js/events/NavTabEvents"; +import PluginDispatcher from "../../js/plugin/shared/PluginDispatcher"; + +describe("NavTabStore", function () { + + before(function (done) { + PluginDispatcher.dispatch({ + eventType: NavTabEvents.APPEND_NAVTAB, + data: [ + { + id: "/mesos", + text: "Mesos", + component: "" + }, + { + id: "/another-tab", + text: "Another Tab", + component: "" + } + ] + }); + + + + done(); + }); + + it("successfully appends multiple tabs to TabStore internal array", function () { + expect(NavTabStore.getTabs().length).to.equal(4); + }); + + it("successfully fires NavTabEvents.CHANGE event", function () { + var eventFired = false; + + function onChange() { + eventFired = true; + }; + + NavTabStore.on(NavTabEvents.CHANGE, onChange); + PluginDispatcher.dispatch({ + eventType: NavTabEvents.APPEND_NAVTAB, + data: [] + }); + NavTabStore.removeListener(NavTabEvents.CHANGE, onChange); + expect(eventFired).to.equal(true); + }); + + + +}); From e83b9a88e63ee7c8d329d3ac40db8ce77fdbf2de Mon Sep 17 00:00:00 2001 From: Dalton Barreto Date: Sat, 17 Sep 2016 11:14:32 -0300 Subject: [PATCH 6/6] Listening to the NavTabStore.CHANGE event Moving APPEND_NAVTAB to plugin/shared/PluginEvents.js since this is what is exposed to plugins. --- src/js/components/TabPanesComponent.jsx | 19 ++++++++++++++++++- src/js/events/NavTabEvents.js | 3 +-- src/js/plugin/shared/PluginEvents.js | 3 ++- src/js/stores/NavTabStore.js | 3 ++- src/test/units/NavTabsStore.test.js | 5 +++-- 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/js/components/TabPanesComponent.jsx b/src/js/components/TabPanesComponent.jsx index 5af81f36f..80fd894a6 100644 --- a/src/js/components/TabPanesComponent.jsx +++ b/src/js/components/TabPanesComponent.jsx @@ -3,6 +3,7 @@ import React from "react/addons"; import TabPaneComponent from "../components/TabPaneComponent"; import TogglableTabsComponent from "../components/TogglableTabsComponent"; import NavTabStore from "../stores/NavTabStore"; +import NavTabEvents from "../events/NavTabEvents"; import QueryParamsMixin from "../mixins/QueryParamsMixin"; @@ -15,6 +16,22 @@ var TabPanesComponent = React.createClass({ router: React.PropTypes.func }, + getInitialState: function () { + return { + tabs: NavTabStore.getTabs() + }; + }, + + componentWillMount: function () { + NavTabStore.on(NavTabEvents.CHANGE, this.onNavTabChange); + }, + + onNavTabChange: function () { + this.setState({ + tabs: NavTabStore.getTabs() + }); + }, + getTabId: function () { var path = this.getCurrentPathname(); @@ -28,7 +45,7 @@ var TabPanesComponent = React.createClass({ }, render: function () { - var renderedNavTabComponents = NavTabStore.getTabs().map(tab => { + var renderedNavTabComponents = this.state.tabs.map(tab => { return ( {React.createElement(tab.component)} diff --git a/src/js/events/NavTabEvents.js b/src/js/events/NavTabEvents.js index 6078ba767..d42ff5240 100644 --- a/src/js/events/NavTabEvents.js +++ b/src/js/events/NavTabEvents.js @@ -1,6 +1,5 @@ const NavTabEvents = { - CHANGE: "NAV_TAB_EVENTS_CHANGE", - APPEND_NAVTAB: "NAV_TAB_EVENTS_APPEND_NAVTAB" + CHANGE: "NAV_TAB_EVENTS_CHANGE" }; export default Object.freeze(NavTabEvents); diff --git a/src/js/plugin/shared/PluginEvents.js b/src/js/plugin/shared/PluginEvents.js index 5a79342bf..7ff681fd1 100644 --- a/src/js/plugin/shared/PluginEvents.js +++ b/src/js/plugin/shared/PluginEvents.js @@ -5,7 +5,8 @@ const PluginEvents = { "INJECT_COMPONENT": "INJECT_COMPONENT", "APPS_STORE_CHANGE": "APPS_STORE_CHANGE", "APP_FORM_STORE_CHANGE": "APP_FORM_STORE_CHANGE", - "APP_FORM_STORE_VALIDATION_ERROR": "APP_FORM_STORE_VALIDATION_ERROR" + "APP_FORM_STORE_VALIDATION_ERROR": "APP_FORM_STORE_VALIDATION_ERROR", + "APPEND_NAVTAB": "NAV_TAB_EVENTS_APPEND_NAVTAB" }; export default Util.fixObject(PluginEvents, "PLUGIN_EVENTS_"); diff --git a/src/js/stores/NavTabStore.js b/src/js/stores/NavTabStore.js index cdd455c52..f3b810b80 100644 --- a/src/js/stores/NavTabStore.js +++ b/src/js/stores/NavTabStore.js @@ -3,6 +3,7 @@ import React from "react/addons"; import PluginDispatcher from "../plugin/shared/PluginDispatcher"; import Util from "../helpers/Util"; import NavTabEvents from "../events/NavTabEvents"; +import PluginEvents from "../plugin/shared/PluginEvents"; import DeploymentsListComponent from "../components/DeploymentsListComponent"; @@ -33,7 +34,7 @@ var NavTabStore = Util.extendObject(EventEmitter.prototype, { }); PluginDispatcher.register(event => { - if (event.eventType === NavTabEvents.APPEND_NAVTAB) { + if (event.eventType === PluginEvents.APPEND_NAVTAB) { event.data.map(appendTab); NavTabStore.emit(NavTabEvents.CHANGE); } diff --git a/src/test/units/NavTabsStore.test.js b/src/test/units/NavTabsStore.test.js index 078d425dd..c15f667bb 100644 --- a/src/test/units/NavTabsStore.test.js +++ b/src/test/units/NavTabsStore.test.js @@ -1,6 +1,7 @@ import {expect} from "chai"; import NavTabStore from "../../js/stores/NavTabStore"; +import PluginEvents from "../../js/plugin/shared/PluginEvents"; import NavTabEvents from "../../js/events/NavTabEvents"; import PluginDispatcher from "../../js/plugin/shared/PluginDispatcher"; @@ -8,7 +9,7 @@ describe("NavTabStore", function () { before(function (done) { PluginDispatcher.dispatch({ - eventType: NavTabEvents.APPEND_NAVTAB, + eventType: PluginEvents.APPEND_NAVTAB, data: [ { id: "/mesos", @@ -41,7 +42,7 @@ describe("NavTabStore", function () { NavTabStore.on(NavTabEvents.CHANGE, onChange); PluginDispatcher.dispatch({ - eventType: NavTabEvents.APPEND_NAVTAB, + eventType: PluginEvents.APPEND_NAVTAB, data: [] }); NavTabStore.removeListener(NavTabEvents.CHANGE, onChange);