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

WIP: Plugable NavTabs implementation candidate #775

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
.DS_Store
*.iml
.idea/*
*.sw[o|p]
~*

# npm
node_modules/
Expand Down
115 changes: 115 additions & 0 deletions src/js/components/AppsAndGroupsListComponent.jsx
Original file line number Diff line number Diff line change
@@ -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 <BreadcrumbComponent groupId={groupId} />;
}

return (
<p className="breadcrumb">
<span>
{`Search results for "${filters[FilterTypes.TEXT]}"`}
</span>
{this.getClearLinkForFilter(FilterTypes.TEXT,
"Clear search",
"clear")}
</p>
);
},

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 (
<div className="app-list-wrapper">
<SidebarComponent groupId={groupId} />
<main>
<div className="contextual-bar">
{this.getContextualBar()}
<ul className="list-unstyled list-inline">
<li>
<Link to={path}
query={newGroupModalQuery}
className="btn btn-default create-group"
activeClassName="create-group-active">
Create Group
</Link>
</li>
<li>
<Link to={path}
query={newAppModalQuery}
className="btn btn-success create-app"
activeClassName="create-app-active">
Create Application
</Link>
</li>
</ul>
</div>
<AppListComponent currentGroup={groupId} />
</main>
</div>
);
}

});

export default AppsAndGroupsListComponent;
8 changes: 4 additions & 4 deletions src/js/components/Marathon.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -298,7 +298,7 @@ var Marathon = React.createClass({
</div>
<NavTabsComponent activeTabId={state.activeTabId}
className="navbar-nav nav-tabs-unbordered"
tabs={tabs} />
tabs={NavTabStore.getTabs()} />
<div className="nav navbar-nav navbar-right">
<AppListFilterComponent />
<HelpMenuComponent />
Expand Down
111 changes: 15 additions & 96 deletions src/js/components/TabPanesComponent.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
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 NavTabStore from "../stores/NavTabStore";
import NavTabEvents from "../events/NavTabEvents";

import QueryParamsMixin from "../mixins/QueryParamsMixin";

Expand All @@ -24,58 +18,24 @@ var TabPanesComponent = React.createClass({

getInitialState: function () {
return {
currentGroup: "/"
tabs: NavTabStore.getTabs()
};
},

componentWillReceiveProps: function () {
this.updateCurrentGroup();
},

componentWillMount: function () {
this.updateCurrentGroup();
NavTabStore.on(NavTabEvents.CHANGE, this.onNavTabChange);
},

getContextualBar: function () {
var state = this.state;
var filters = this.getQueryParamObject();

if (filters[FilterTypes.TEXT] == null ||
Util.isStringAndEmpty(filters[FilterTypes.TEXT])) {
return <BreadcrumbComponent groupId={state.currentGroup} />;
}

return (
<p className="breadcrumb">
<span>
{`Search results for "${filters[FilterTypes.TEXT]}"`}
</span>
{this.getClearLinkForFilter(FilterTypes.TEXT,
"Clear search",
"clear")}
</p>
);
},

updateCurrentGroup: function () {
var {groupId} = this.context.router.getCurrentParams();
if (groupId == null) {
groupId = "/";
}
groupId = decodeURIComponent(groupId);
if (!groupId.endsWith("/")) {
groupId += "/";
}

onNavTabChange: function () {
this.setState({
currentGroup: groupId
tabs: NavTabStore.getTabs()
});
},

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;
Expand All @@ -85,58 +45,17 @@ 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;
}

var renderedNavTabComponents = this.state.tabs.map(tab => {
return (
<TabPaneComponent id={tab.id} key={tab.id}>
{React.createElement(tab.component)}
</TabPaneComponent>
);
});
return (
<TogglableTabsComponent activeTabId={this.getTabId()}
className="container-fluid content">
<TabPaneComponent id={tabs[0].id}>
<div className="app-list-wrapper">
<SidebarComponent groupId={state.currentGroup} />
<main>
<div className="contextual-bar">
{this.getContextualBar()}
<ul className="list-unstyled list-inline">
<li>
<Link to={path}
query={newGroupModalQuery}
className="btn btn-default create-group"
activeClassName="create-group-active">
Create Group
</Link>
</li>
<li>
<Link to={path}
query={newAppModalQuery}
className="btn btn-success create-app"
activeClassName="create-app-active">
Create Application
</Link>
</li>
</ul>
</div>
<AppListComponent currentGroup={state.currentGroup} />
</main>
</div>
</TabPaneComponent>
<TabPaneComponent id={tabs[1].id}>
<DeploymentsListComponent />
</TabPaneComponent>
{renderedNavTabComponents}
</TogglableTabsComponent>
);
}
Expand Down
6 changes: 0 additions & 6 deletions src/js/constants/tabs.js

This file was deleted.

5 changes: 5 additions & 0 deletions src/js/events/NavTabEvents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const NavTabEvents = {
CHANGE: "NAV_TAB_EVENTS_CHANGE"
};

export default Object.freeze(NavTabEvents);
3 changes: 2 additions & 1 deletion src/js/plugin/shared/PluginEvents.js
Original file line number Diff line number Diff line change
Expand Up @@ -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_");
43 changes: 43 additions & 0 deletions src/js/stores/NavTabStore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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 PluginEvents from "../plugin/shared/PluginEvents";

import DeploymentsListComponent
from "../components/DeploymentsListComponent";
import AppsAndGroupsListComponent
from "../components/AppsAndGroupsListComponent";

var tabs = [
{
id: "/apps",
text: "Applications",
component: AppsAndGroupsListComponent
},
{
id: "/deployments",
text: "Deployments",
component: DeploymentsListComponent
}
];

function appendTab(tab) {
tabs.push(tab);
}

var NavTabStore = Util.extendObject(EventEmitter.prototype, {
getTabs: function () {
return tabs;
}
});

PluginDispatcher.register(event => {
if (event.eventType === PluginEvents.APPEND_NAVTAB) {
event.data.map(appendTab);
NavTabStore.emit(NavTabEvents.CHANGE);
}
});

export default NavTabStore;
4 changes: 2 additions & 2 deletions src/test/units/NavTabsComponent.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () {

Expand All @@ -19,7 +19,7 @@ describe("Deployments navigation badge", function () {

var props = {
activeTabId: "/deployments",
tabs: tabs
tabs: NavTabStore.getTabs()
};

DeploymentStore.once(DeploymentEvents.CHANGE, () => {
Expand Down
Loading