You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ga...@apache.org on 2017/04/05 14:54:21 UTC
[couchdb-fauxton] branch master updated: (#883) - Navbar refactor
This is an automated email from the ASF dual-hosted git repository.
garren pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/couchdb-fauxton.git
The following commit(s) were added to refs/heads/master by this push:
new b212dd5 (#883) - Navbar refactor
b212dd5 is described below
commit b212dd5b5b8e9aa2f90f125d8c9d14263fa90b99
Author: Ryan Millay <rm...@us.ibm.com>
AuthorDate: Wed Apr 5 10:54:19 2017 -0400
(#883) - Navbar refactor
* NavBar: Rework NavBar
Clean up of the Navbar up and changed the styling a bit.
Big internal refactor of the codebase.
* the death of the burger + minimized by default
* Removed jquery dependency from navbar and repaired bugs
* dynamic image paths and hardening a stubborn test
---
app/addons/auth/__tests__/base.test.js | 83 ++++++++
app/addons/auth/base.js | 49 ++---
app/addons/auth/routes.js | 5 +-
app/addons/auth/test/baseSpec.js | 100 ---------
app/addons/cluster/routes.js | 2 +
app/addons/config/routes.js | 1 +
.../tests/nightwatch/checkDatabaseTooltip.js | 2 +-
.../tests/nightwatch/selectDocViaTypeahead.js | 2 -
app/addons/fauxton/appwrapper.js | 65 ++++--
app/addons/fauxton/assets/less/fauxton.less | 1 +
app/addons/fauxton/assets/less/navigation.less | 202 ++++++++++++++++++
app/addons/fauxton/base.js | 1 -
.../__tests__/burger-test.js} | 32 ++-
.../navigation/__tests__/login-logout-test.js | 80 ++++++++
.../fauxton/navigation/__tests__/navbar-test.js | 43 ++++
.../navigation-store-test.js} | 109 +++-------
.../fauxton/navigation/__tests__/navlink-test.js | 48 +++++
app/addons/fauxton/navigation/actiontypes.js | 5 +-
app/addons/fauxton/navigation/components.react.jsx | 155 --------------
.../{actiontypes.js => components/Brand.js} | 33 ++-
app/addons/fauxton/navigation/components/Burger.js | 41 ++++
.../{actiontypes.js => components/Footer.js} | 30 ++-
.../fauxton/navigation/components/LoginButton.js | 42 ++++
.../fauxton/navigation/components/LogoutButton.js | 47 +++++
app/addons/fauxton/navigation/components/NavBar.js | 115 +++++++++++
.../fauxton/navigation/components/NavLink.js | 51 +++++
app/addons/fauxton/navigation/container/NavBar.js | 68 ++++++
app/addons/fauxton/navigation/stores.js | 37 +++-
.../navigation/tests/componentsSpec.react.jsx | 61 ------
.../fauxton/tests/nightwatch/highlightsidebar.js | 2 +-
.../fauxton/tests/nightwatch/notificationCenter.js | 1 +
app/app.js | 19 ++
app/constants.js | 4 -
assets/less/fauxton.less | 17 +-
assets/less/templates.less | 228 ---------------------
assets/less/variables.less | 7 +
package.json | 7 +-
settings.json.default.json | 12 +-
webpack.config.dev.js | 10 +-
webpack.config.release.js | 10 +-
40 files changed, 1081 insertions(+), 746 deletions(-)
diff --git a/app/addons/auth/__tests__/base.test.js b/app/addons/auth/__tests__/base.test.js
new file mode 100644
index 0000000..ae56b16
--- /dev/null
+++ b/app/addons/auth/__tests__/base.test.js
@@ -0,0 +1,83 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+import FauxtonAPI from "../../../core/api";
+import Auth from "../../../core/auth";
+import Base from "../base";
+import sinon from "sinon";
+
+
+describe("Auth", function () {
+ FauxtonAPI.auth = new Auth();
+ Base.initialize();
+
+ describe("failed login", function () {
+
+ it("redirects with replace: true set", function () {
+ const navigateSpy = sinon.spy(FauxtonAPI, 'navigate');
+ FauxtonAPI.router.trigger = () => {};
+ FauxtonAPI.session.isLoggedIn = function () { return false; };
+ FauxtonAPI.auth.authDeniedCb();
+ expect(navigateSpy.withArgs('/login?urlback=', {replace: true}).calledOnce).toBeTruthy();
+ FauxtonAPI.navigate.restore();
+ });
+ });
+
+ describe('auth session change', function () {
+
+ afterEach(function () {
+ FauxtonAPI.addHeaderLink.restore && FauxtonAPI.addHeaderLink.restore();
+ FauxtonAPI.session.isLoggedIn.restore && FauxtonAPI.session.isLoggedIn.restore();
+ FauxtonAPI.session.isAdminParty.restore();
+ });
+
+ it('for admin party changes title to admin party', function () {
+ const spy = sinon.spy(FauxtonAPI, 'addHeaderLink');
+ sinon.stub(FauxtonAPI.session, 'isAdminParty').returns(true);
+ FauxtonAPI.session.trigger('change');
+
+ expect(spy.calledOnce).toBeTruthy();
+ const args = spy.getCall(0).args[0];
+ expect(args.title).toMatch("Admin Party!");
+ });
+
+ it('for login changes title to Your Account', function () {
+ var spy = sinon.spy(FauxtonAPI, 'addHeaderLink');
+ sinon.stub(FauxtonAPI.session, 'isAdminParty').returns(false);
+ sinon.stub(FauxtonAPI.session, 'isLoggedIn').returns(true);
+ FauxtonAPI.session.trigger('change');
+
+ expect(spy.calledOnce).toBeTruthy();
+ var args = spy.getCall(0).args[0];
+ expect(args.title).toMatch("Your Account");
+ });
+
+ it('for login adds logout link', function () {
+ var spy = sinon.spy(FauxtonAPI, 'showLogout');
+ sinon.stub(FauxtonAPI.session, 'isAdminParty').returns(false);
+ sinon.stub(FauxtonAPI.session, 'isLoggedIn').returns(true);
+ FauxtonAPI.session.trigger('change');
+
+ expect(spy.calledOnce).toBeTruthy();
+ FauxtonAPI.showLogout.restore();
+ });
+
+ it('for logout, removes logout link', function () {
+ var spy = sinon.spy(FauxtonAPI, 'showLogin');
+ sinon.stub(FauxtonAPI.session, 'isAdminParty').returns(false);
+ sinon.stub(FauxtonAPI.session, 'isLoggedIn').returns(false);
+ FauxtonAPI.session.trigger('change');
+
+ expect(spy.calledOnce).toBeTruthy();
+ FauxtonAPI.showLogin.restore();
+ });
+ });
+});
diff --git a/app/addons/auth/base.js b/app/addons/auth/base.js
index 237a450..c7c32c7 100644
--- a/app/addons/auth/base.js
+++ b/app/addons/auth/base.js
@@ -19,19 +19,16 @@ Auth.session = new Auth.Session();
FauxtonAPI.setSession(Auth.session);
app.session = Auth.session;
-Auth.initialize = function () {
- FauxtonAPI.addHeaderLink({
- id: 'auth',
- title: 'Login',
- href: '#/login',
- icon: 'fonticon-user',
- bottomNav: true
- });
+function cleanupAuthSection () {
+ FauxtonAPI.removeHeaderLink({ id: 'auth', bottomNav: true });
+}
+
+Auth.initialize = function () {
Auth.session.on('change', function () {
- var session = Auth.session;
- var link = {};
+ const session = Auth.session;
+ let link;
if (session.isAdminParty()) {
link = {
@@ -41,36 +38,28 @@ Auth.initialize = function () {
icon: 'fonticon-user',
bottomNav: true
};
+
+ cleanupAuthSection();
+ FauxtonAPI.addHeaderLink(link);
+ FauxtonAPI.hideLogin();
+
} else if (session.isLoggedIn()) {
link = {
id: 'auth',
- title: session.user().name,
+ title: 'Your Account',
href: '#/changePassword',
icon: 'fonticon-user',
bottomNav: true
};
- // ensure the footer link is removed before adding it
- FauxtonAPI.removeHeaderLink({ id: 'logout', footerNav: true });
- FauxtonAPI.addHeaderLink({
- id: 'logout',
- footerNav: true,
- href: '#logout',
- title: 'Logout',
- icon: '',
- className: 'logout'
- });
+ cleanupAuthSection();
+ FauxtonAPI.addHeaderLink(link);
+ FauxtonAPI.showLogout();
} else {
- link = {
- id: 'auth',
- title: 'Login',
- href: '#/login',
- icon: 'fonticon-user',
- bottomNav: true
- };
- FauxtonAPI.removeHeaderLink({ id: 'logout', footerNav: true });
+ cleanupAuthSection();
+ FauxtonAPI.showLogin();
}
- FauxtonAPI.updateHeaderLink(link);
+
});
Auth.session.fetchUser().then(function () {
diff --git a/app/addons/auth/routes.js b/app/addons/auth/routes.js
index b4a0e68..1225619 100644
--- a/app/addons/auth/routes.js
+++ b/app/addons/auth/routes.js
@@ -65,6 +65,7 @@ var AuthRouteObject = FauxtonAPI.RouteObject.extend({
var UserRouteObject = FauxtonAPI.RouteObject.extend({
hideNotificationCenter: true,
hideApiBar: true,
+ selectedHeader: 'Your Account',
routes: {
'changePassword': {
@@ -93,10 +94,6 @@ var UserRouteObject = FauxtonAPI.RouteObject.extend({
ClusterActions.navigateToNodeBasedOnNodeCount('/addAdmin/');
},
- selectedHeader: function () {
- return FauxtonAPI.session.user().name;
- },
-
changePassword: function () {
ClusterActions.fetchNodes();
AuthActions.selectPage('changePassword');
diff --git a/app/addons/auth/test/baseSpec.js b/app/addons/auth/test/baseSpec.js
deleted file mode 100644
index c566287..0000000
--- a/app/addons/auth/test/baseSpec.js
+++ /dev/null
@@ -1,100 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-import FauxtonAPI from "../../../core/api";
-import Base from "../base";
-import Auth from "../../../core/auth";
-import testUtils from "../../../../test/mocha/testUtils";
-import sinon from "sinon";
-var assert = testUtils.assert;
-
-describe("Auth: Login", function () {
-
- describe("failed login", function () {
-
- it("redirects with replace: true set", function () {
- var navigateSpy = sinon.spy(FauxtonAPI, 'navigate');
- FauxtonAPI.auth = new Auth();
- FauxtonAPI.session.isLoggedIn = function () { return false; };
- Base.initialize();
- FauxtonAPI.auth.authDeniedCb();
- assert.ok(navigateSpy.withArgs('/login?urlback=', {replace: true}).calledOnce);
- });
- });
-});
-
-describe('auth session change', function () {
-
- afterEach(function () {
- FauxtonAPI.updateHeaderLink.restore && FauxtonAPI.updateHeaderLink.restore();
- FauxtonAPI.session.isAdminParty.restore && FauxtonAPI.session.isAdminParty.restore();
- });
-
- it('for admin party changes title to admin party', function () {
- var spy = sinon.spy(FauxtonAPI, 'updateHeaderLink');
- var stub = sinon.stub(FauxtonAPI.session, 'isAdminParty').returns(true);
- FauxtonAPI.session.trigger('change');
-
- assert.ok(spy.calledOnce);
- var args = spy.getCall(0).args[0];
-
- assert.ok(args.title.match(/Admin Party/));
- FauxtonAPI.session.isAdminParty.restore();
- });
-
- it('for login changes title to login', function () {
- var spy = sinon.spy(FauxtonAPI, 'updateHeaderLink');
- var stub = sinon.stub(FauxtonAPI.session, 'isAdminParty').returns(false);
- sinon.stub(FauxtonAPI.session, 'user').returns({name: 'test-user'});
- sinon.stub(FauxtonAPI.session, 'isLoggedIn').returns(true);
- FauxtonAPI.session.trigger('change');
-
- assert.ok(spy.calledOnce);
- var args = spy.getCall(0).args[0];
-
- assert.equal(args.title, 'test-user');
- FauxtonAPI.session.isLoggedIn.restore();
- FauxtonAPI.session.user.restore();
- FauxtonAPI.session.isAdminParty.restore();
- });
-
- it('for login adds logout link', function () {
- var spy = sinon.spy(FauxtonAPI, 'addHeaderLink');
- var stub = sinon.stub(FauxtonAPI.session, 'isAdminParty').returns(false);
- sinon.stub(FauxtonAPI.session, 'user').returns({name: 'test-user'});
- sinon.stub(FauxtonAPI.session, 'isLoggedIn').returns(true);
- FauxtonAPI.session.trigger('change');
-
- assert.ok(spy.calledOnce);
- var args = spy.getCall(0).args[0];
-
- assert.equal(args.title, 'Logout');
- FauxtonAPI.session.isLoggedIn.restore();
- FauxtonAPI.session.user.restore();
- FauxtonAPI.session.isAdminParty.restore();
- });
-
- it('for logout, removes logout link', function () {
- var spy = sinon.spy(FauxtonAPI, 'removeHeaderLink');
- var stub = sinon.stub(FauxtonAPI.session, 'isAdminParty').returns(false);
- sinon.stub(FauxtonAPI.session, 'isLoggedIn').returns(false);
- FauxtonAPI.session.trigger('change');
-
- assert.ok(spy.calledOnce);
- var args = spy.getCall(0).args[0];
-
- assert.equal(args.id, 'logout');
- FauxtonAPI.session.isLoggedIn.restore();
- FauxtonAPI.session.isAdminParty.restore();
- });
-
-
-});
diff --git a/app/addons/cluster/routes.js b/app/addons/cluster/routes.js
index 591541f..c4b924d 100644
--- a/app/addons/cluster/routes.js
+++ b/app/addons/cluster/routes.js
@@ -19,6 +19,8 @@ import {OnePaneSimpleLayout} from '../components/layouts';
var ConfigDisabledRouteObject = FauxtonAPI.RouteObject.extend({
+ selectedHeader: 'Configuration',
+
routes: {
'cluster/disabled': 'showDisabledFeatureScreen'
},
diff --git a/app/addons/config/routes.js b/app/addons/config/routes.js
index e1dadb7..370d126 100644
--- a/app/addons/config/routes.js
+++ b/app/addons/config/routes.js
@@ -20,6 +20,7 @@ import Layout from './layout';
var ConfigDisabledRouteObject = FauxtonAPI.RouteObject.extend({
+ selectedHeader: 'Configuration',
routes: {
'_config': 'checkNodes',
diff --git a/app/addons/databases/tests/nightwatch/checkDatabaseTooltip.js b/app/addons/databases/tests/nightwatch/checkDatabaseTooltip.js
index d2c6606..e5b0353 100644
--- a/app/addons/databases/tests/nightwatch/checkDatabaseTooltip.js
+++ b/app/addons/databases/tests/nightwatch/checkDatabaseTooltip.js
@@ -38,7 +38,7 @@ module.exports = {
.checkForStringPresent(newDatabaseName, '"doc_del_count":1')
- .clickWhenVisible('#nav-links a[href="#/_all_dbs"]')
+ .clickWhenVisible('#primary-navbar a[href="#/_all_dbs"]')
// now let's look at the actual UI to confirm the tooltip appears
.waitForElementPresent('.js-db-graveyard', waitTime, false)
.moveToElement('.js-db-graveyard', 1, 1)
diff --git a/app/addons/documents/tests/nightwatch/selectDocViaTypeahead.js b/app/addons/documents/tests/nightwatch/selectDocViaTypeahead.js
index b96940e..0a083f2 100644
--- a/app/addons/documents/tests/nightwatch/selectDocViaTypeahead.js
+++ b/app/addons/documents/tests/nightwatch/selectDocViaTypeahead.js
@@ -27,7 +27,6 @@ module.exports = {
.keys(['\uE00C'])
.waitForElementPresent('.prettyprint', waitTime, false)
.waitForElementPresent('.documents-pagination', waitTime, false)
- .click('.burger')
// we need to explicitly show the doc field because it's hidden on Travis due to screen width
.execute("$('.searchbox-wrapper').show();")
.setValue('.jump-to-doc .Select-input input', ['_des'])
@@ -50,7 +49,6 @@ module.exports = {
.keys(['\uE00C'])
.waitForElementPresent('.prettyprint', waitTime, false)
.waitForElementPresent('.documents-pagination', waitTime, false)
- .click('.burger')
// we need to explicitly show the doc field because it's hidden on Travis due to screen width
.execute("$('.searchbox-wrapper').show();")
.setValue('.jump-to-doc .Select-input input', ['MY_CAP'])
diff --git a/app/addons/fauxton/appwrapper.js b/app/addons/fauxton/appwrapper.js
index f377bbc..573521b 100644
--- a/app/addons/fauxton/appwrapper.js
+++ b/app/addons/fauxton/appwrapper.js
@@ -12,8 +12,12 @@
import React from 'react';
import {NotificationController, PermanentNotification} from "./notifications/notifications.react";
-import {NavBar} from './navigation/components.react';
+import NavBar from './navigation/container/NavBar';
import NavbarActions from './navigation/actions';
+import Stores from './navigation/stores';
+import classNames from 'classnames';
+
+const navBarStore = Stores.navBarStore;
class ContentWrapper extends React.Component {
constructor(props) {
@@ -49,27 +53,52 @@ class ContentWrapper extends React.Component {
}
}
-const App = ({router}) => {
- return (
- <div>
- <PermanentNotification />
- <div id="notifications">
- <NotificationController />
- </div>
- <div role="main" id="main">
- <div id="app-container">
- <div className="wrapper">
- <div className="pusher">
- <ContentWrapper router={router} />
- </div>
- <div id="primary-navbar">
- <NavBar/>
+class App extends React.Component {
+ constructor (props) {
+ super(props);
+ this.state = this.getStoreState();
+ }
+
+ getStoreState () {
+ return {
+ isPrimaryNavMinimized: navBarStore.isMinimized()
+ };
+ }
+
+ componentDidMount () {
+ navBarStore.on('change', this.onChange, this);
+ }
+
+ onChange () {
+ this.setState(this.getStoreState());
+ }
+
+ render () {
+ const mainClass = classNames(
+ {'closeMenu': this.state.isPrimaryNavMinimized}
+ );
+
+ return (
+ <div>
+ <PermanentNotification />
+ <div id="notifications">
+ <NotificationController />
+ </div>
+ <div role="main" id="main" className={mainClass}>
+ <div id="app-container">
+ <div className="wrapper">
+ <div className="pusher">
+ <ContentWrapper router={this.props.router} />
+ </div>
+ <div id="primary-navbar">
+ <NavBar/>
+ </div>
</div>
</div>
</div>
</div>
- </div>
- );
+ );
+ }
};
export default App;
diff --git a/app/addons/fauxton/assets/less/fauxton.less b/app/addons/fauxton/assets/less/fauxton.less
index 255589d..c248f69 100644
--- a/app/addons/fauxton/assets/less/fauxton.less
+++ b/app/addons/fauxton/assets/less/fauxton.less
@@ -1 +1,2 @@
@import "components.less";
+@import "navigation.less";
diff --git a/app/addons/fauxton/assets/less/navigation.less b/app/addons/fauxton/assets/less/navigation.less
new file mode 100644
index 0000000..1493c62
--- /dev/null
+++ b/app/addons/fauxton/assets/less/navigation.less
@@ -0,0 +1,202 @@
+.faux-navbar {
+ margin-top: 64px;
+ background-color: @brandDark2;
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ z-index: 5;
+ overflow: scroll;
+}
+
+.faux-navbar nav {
+ height: 100%;
+}
+
+.faux-navbar__linkcontainer {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ height: 100%;
+}
+
+.faux-navbar__itemarea {
+ .box-sizing(border-box);
+ border-bottom: 1px solid @brandDark2;
+ height: 48px;
+ padding: 10px 20px;
+ line-height: 24px;
+}
+
+.faux-navbar__version-footer {
+ color: @buttonText;
+ font-size: 10px;
+ text-align: center;
+ background-color: @brandDark2;
+ padding-bottom: 10px;
+}
+
+.faux-navbar__burger {
+ background-color: @brandDark2;
+ padding: 19px 0 18px 18px;
+ position: fixed;
+ z-index: 100;
+ top: 0;
+}
+
+.faux-navbar--narrow {
+ width: @collapsedNavWidth;
+}
+
+.faux-navbar--wide {
+ width: @navWidth;
+}
+
+.faux-navbar__burger:hover .faux-navbar__burger__icon {
+ color: @navIconActive;
+}
+
+.faux-navbar__burger__icon {
+ color: @navIconColor;
+ font-size: 27px;
+}
+
+.faux-navbar__burger__icon--flipped:before{
+ -moz-transform: scale(-1, 1);
+ -webkit-transform: scale(-1, 1);
+ -o-transform: scale(-1, 1);
+ -ms-transform: scale(-1, 1);
+ transform: scale(-1, 1);
+}
+
+.faux-navbar__link, .faux-logout__link, .faux-login__link {
+ display: block;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.faux-navbar__link--active {
+ background-color: @brandHighlight;
+ text-decoration: none;
+}
+
+.faux-navbar__link--inactive {
+ background-color: @brandDark1;
+}
+
+.faux-navbar__link:hover, .faux-logout__link:hover, .faux-login__link:hover {
+ background-color: @hoverHighlight;
+ text-decoration: none;
+}
+
+.faux-navbar__link:active {
+ text-decoration: none;
+}
+
+.faux-navbar__link:hover .faux-navbar__icon:before {
+ color: @navIconActive;
+}
+
+.faux-navbar__link--active .faux-navbar__icon:before {
+ color: @navIconActive;
+}
+
+.faux-navbar__icon {
+ margin-right: 14px;
+ color: @navIconColor;
+ font-size: 24px;
+ vertical-align: middle;
+}
+
+.faux-navbar__text {
+ margin: 0;
+ color: @buttonText;
+ vertical-align: middle;
+ font-size: 16px;
+ font-weight: normal;
+ font-family: Helvetica,sans-serif;
+ font-weight: 400;
+}
+
+.faux-navbar__logout__text {
+ font-size: 12px;
+ color: @buttonText;
+}
+
+.faux-navbar__logout__textcontainer {
+ text-align: center;
+ color: @buttonText;
+}
+
+.faux-navbar__logout__textcontainer--narrow {
+ padding-bottom: 4px;
+ padding: 15px 0;
+}
+
+.faux-navbar__logout__textcontainer--wide {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ padding: 15px;
+}
+
+.faux-navbar__login__textcontainer {
+ text-align: center;
+ color: @buttonText;
+}
+
+.faux-navbar__login__textcontainer--narrow {
+ padding-bottom: 4px;
+ padding: 15px 0;
+}
+
+.faux-navbar__login__textcontainer--wide {
+ padding: 15px;
+}
+
+.faux-navbar__brand {
+ margin: 20px 0 20px 0;
+ height: 50px;
+ padding: 10px 10px 10px 10px;
+ float: none;
+ background: @brandDark2;
+}
+
+.faux-navbar__brand-logo {
+ display: block;
+ height: 100%;
+ margin-top: 10px;
+}
+
+.faux-navbar__brand-logo--wide {
+ background: url(@largeLogoPath) no-repeat 23px 0px;
+ background-size: 150px;
+ width: 200px;
+}
+
+.faux-navbar__brand-logo--narrow {
+ width: 43px;
+ height: 40px;
+ background: url(@smallLogoPath) no-repeat 3px 0;
+ background-size: 40px;
+}
+
+.faux-navbar__footer {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+ height: 100%;
+ min-height: 200px;
+ width: 100%;
+}
+
+.faux-navbar__logout__username {
+ color: @buttonText;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 150px;
+ white-space: nowrap;
+ padding-right: 5px;
+ padding-left: 10px;
+ font-size: 12px;
+}
diff --git a/app/addons/fauxton/base.js b/app/addons/fauxton/base.js
index fa83b97..17812d8 100644
--- a/app/addons/fauxton/base.js
+++ b/app/addons/fauxton/base.js
@@ -19,7 +19,6 @@ import "./assets/less/fauxton.less";
const Fauxton = FauxtonAPI.addon();
Fauxton.initialize = () => {
-
const versionInfo = new Fauxton.VersionInfo();
versionInfo.fetch().then(function () {
NavigationActions.setNavbarVersionInfo(versionInfo.get("version"));
diff --git a/app/addons/fauxton/base.js b/app/addons/fauxton/navigation/__tests__/burger-test.js
similarity index 52%
copy from app/addons/fauxton/base.js
copy to app/addons/fauxton/navigation/__tests__/burger-test.js
index fa83b97..5f9640f 100644
--- a/app/addons/fauxton/base.js
+++ b/app/addons/fauxton/navigation/__tests__/burger-test.js
@@ -9,27 +9,21 @@
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
+import Burger from "../components/Burger";
+import React from "react";
+import ReactDOM from "react-dom";
+import sinon from "sinon";
+import {mount} from 'enzyme';
-import app from "../../app";
-import FauxtonAPI from "../../core/api";
-import NavigationActions from "./navigation/actions";
+describe('Navigation Bar', () => {
-import "./assets/less/fauxton.less";
+ describe('Burger', () => {
+ it('dispatch TOGGLE_NAVBAR_MENU on click', () => {
+ const toggleMenu = sinon.spy();
+ const burgerEl = mount(<Burger toggleMenu={toggleMenu} isMinimized={false} />);
+ burgerEl.simulate('click');
+ expect(toggleMenu.calledOnce).toBeTruthy();
+ });
-const Fauxton = FauxtonAPI.addon();
-
-Fauxton.initialize = () => {
-
- const versionInfo = new Fauxton.VersionInfo();
- versionInfo.fetch().then(function () {
- NavigationActions.setNavbarVersionInfo(versionInfo.get("version"));
});
-};
-
-Fauxton.VersionInfo = Backbone.Model.extend({
- url: function () {
- return app.host;
- }
});
-
-export default Fauxton;
diff --git a/app/addons/fauxton/navigation/__tests__/login-logout-test.js b/app/addons/fauxton/navigation/__tests__/login-logout-test.js
new file mode 100644
index 0000000..097e495
--- /dev/null
+++ b/app/addons/fauxton/navigation/__tests__/login-logout-test.js
@@ -0,0 +1,80 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+
+import React from 'react';
+import { mount } from 'enzyme';
+
+import NavBar from '../components/NavBar';
+
+describe('Navigation Bar', () => {
+
+ it('renders with login button when logged out', () => {
+ const props = {
+ activeLink: '',
+ isMinimized: false,
+ version: '42',
+ navLinks: [],
+ bottomNavLinks: [],
+ footerNavLinks: [],
+ isNavBarVisible: true,
+ isLoginSectionVisible: true,
+ isLoginVisibleInsteadOfLogout: true
+ };
+
+ const navBar = mount(<NavBar {...props} />);
+
+ const button = navBar.find('[href="#/login"]');
+ expect(button.text()).toContain('Login');
+ });
+
+ it('renders with logout button when logged in', () => {
+ const props = {
+ activeLink: '',
+ isMinimized: false,
+ version: '42',
+ navLinks: [],
+ bottomNavLinks: [],
+ footerNavLinks: [],
+ username: 'Rocko',
+ isNavBarVisible: true,
+ isLoginSectionVisible: true,
+ isLoginVisibleInsteadOfLogout: false
+ };
+
+ const navBar = mount(<NavBar {...props} />);
+
+ const button = navBar.find('[href="#/logout"]');
+ expect(button.text()).toContain('Log Out');
+ });
+
+ it('Admin Party has no Logout button and no Login button', () => {
+ const props = {
+ activeLink: '',
+ isMinimized: false,
+ version: '42',
+ navLinks: [],
+ bottomNavLinks: [],
+ footerNavLinks: [],
+ username: 'Rocko',
+ isNavBarVisible: true,
+ isLoginSectionVisible: false,
+ isLoginVisibleInsteadOfLogout: false
+ };
+
+ const navBar = mount(<NavBar {...props} />);
+
+ expect(navBar.text()).not.toMatch(/Login/);
+ expect(navBar.text()).not.toMatch(/Log Out/);
+ });
+
+});
diff --git a/app/addons/fauxton/navigation/__tests__/navbar-test.js b/app/addons/fauxton/navigation/__tests__/navbar-test.js
new file mode 100644
index 0000000..7a6331e
--- /dev/null
+++ b/app/addons/fauxton/navigation/__tests__/navbar-test.js
@@ -0,0 +1,43 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+import NavBarContainer from "../container/NavBar";
+import FauxtonAPI from "../../../../core/api";
+import ActionTypes from "../actiontypes";
+import React from "react";
+import ReactDOM from "react-dom";
+import {mount} from 'enzyme';
+
+describe('Navigation Bar', () => {
+ FauxtonAPI.session = {
+ user: () => {}
+ };
+
+ it('is displayed by default', () => {
+ const NavBar = mount(<NavBarContainer />);
+ expect(NavBar.find('.faux-navbar').length).toBe(1);
+ });
+
+ it('is dynamically displayed by isNavBarVisible', () => {
+ const NavBar = mount(<NavBarContainer />);
+
+ FauxtonAPI.dispatch({
+ type: ActionTypes.NAVBAR_HIDE
+ });
+ expect(NavBar.find('.faux-navbar').length).toBe(0);
+
+ FauxtonAPI.dispatch({
+ type: ActionTypes.NAVBAR_SHOW
+ });
+ expect(NavBar.find('.faux-navbar').length).toBe(1);
+ });
+
+});
diff --git a/app/addons/fauxton/navigation/tests/storeSpec.js b/app/addons/fauxton/navigation/__tests__/navigation-store-test.js
similarity index 52%
rename from app/addons/fauxton/navigation/tests/storeSpec.js
rename to app/addons/fauxton/navigation/__tests__/navigation-store-test.js
index 778dac1..c664dce 100644
--- a/app/addons/fauxton/navigation/tests/storeSpec.js
+++ b/app/addons/fauxton/navigation/__tests__/navigation-store-test.js
@@ -9,24 +9,21 @@
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
-import app from "../../../../app";
-import testUtils from "../../../../../test/mocha/testUtils";
import FauxtonAPI from "../../../../core/api";
import Stores from "../stores";
-var assert = testUtils.assert;
-var navBarStore = Stores.navBarStore;
+const navBarStore = Stores.navBarStore;
-describe('NavBarStore', function () {
- beforeEach(function () {
+describe('NavBarStore', () => {
+ beforeEach(() => {
FauxtonAPI.dispatch({
type: 'CLEAR_NAVBAR_LINK',
});
});
- describe('add links', function () {
+ describe('add links', () => {
- it('to nav links', function () {
+ it('to nav links', () => {
var link = {
id: 'mylink'
};
@@ -35,10 +32,10 @@ describe('NavBarStore', function () {
link: link
});
- assert.equal(navBarStore.getNavLinks()[0].id, link.id);
+ expect(navBarStore.getNavLinks()[0].id).toMatch(link.id);
});
- it('to top nav links', function () {
+ it('to top nav links', () => {
var link1 = {
id: 'mylink1'
};
@@ -58,10 +55,10 @@ describe('NavBarStore', function () {
link: link2
});
- assert.equal(navBarStore.getNavLinks()[0].id, link2.id);
+ expect(navBarStore.getNavLinks()[0].id).toMatch(link2.id);
});
- it('to bottom nav', function () {
+ it('to bottom nav', () => {
var link = {
id: 'bottomNav',
bottomNav: true
@@ -71,10 +68,10 @@ describe('NavBarStore', function () {
link: link
});
- assert.equal(navBarStore.getBottomNavLinks()[0].id, link.id);
+ expect(navBarStore.getBottomNavLinks()[0].id).toMatch(link.id);
});
- it('to top of bottom nav', function () {
+ it('to top of bottom nav', () => {
var link = {
id: 'bottomNav',
bottomNav: true,
@@ -85,10 +82,10 @@ describe('NavBarStore', function () {
link: link
});
- assert.equal(navBarStore.getBottomNavLinks()[0].id, link.id);
+ expect(navBarStore.getBottomNavLinks()[0].id).toMatch(link.id);
});
- it('to footer nav', function () {
+ it('to footer nav', () => {
var link = {
id: 'footerNav',
footerNav: true
@@ -98,12 +95,12 @@ describe('NavBarStore', function () {
link: link
});
- assert.equal(navBarStore.getFooterNavLinks()[0].id, link.id);
+ expect(navBarStore.getFooterNavLinks()[0].id).toMatch(link.id);
});
});
- describe('remove link', function () {
- it('from nav links', function () {
+ describe('remove link', () => {
+ it('from nav links', () => {
var link = {
id: 'remove_link',
};
@@ -117,11 +114,11 @@ describe('NavBarStore', function () {
link: link
});
- assert.equal(navBarStore.getNavLinks().length, 0);
+ expect(navBarStore.getNavLinks().length).toBe(0);
});
- it('remove link from list', function () {
- function addLink (id) {
+ it('remove link from list', () => {
+ const addLink = (id) => {
FauxtonAPI.dispatch({
type: 'ADD_NAVBAR_LINK',
link: {
@@ -129,8 +126,8 @@ describe('NavBarStore', function () {
footerNav: true
}
});
- }
- function removeLink () {
+ };
+ const removeLink = () => {
FauxtonAPI.dispatch({
type: 'REMOVE_NAVBAR_LINK',
link: {
@@ -138,7 +135,7 @@ describe('NavBarStore', function () {
footerNav: true
}
});
- }
+ };
addLink('remove_link1');
addLink('remove_link2');
addLink('remove_link3');
@@ -147,10 +144,10 @@ describe('NavBarStore', function () {
removeLink();
removeLink();
- assert.equal(navBarStore.getFooterNavLinks().length, 2);
+ expect(navBarStore.getFooterNavLinks().length).toBe(2);
});
- it('from bottom nav links', function () {
+ it('from bottom nav links', () => {
var link = {
id: 'remove_link',
bottomNav: true
@@ -165,10 +162,10 @@ describe('NavBarStore', function () {
link: link
});
- assert.equal(navBarStore.getBottomNavLinks().length, 0);
+ expect(navBarStore.getBottomNavLinks().length).toBe(0);
});
- it('from footer nav links', function () {
+ it('from footer nav links', () => {
var link = {
id: 'remove_link',
footerNav: true
@@ -183,12 +180,12 @@ describe('NavBarStore', function () {
link: link
});
- assert.equal(navBarStore.getFooterNavLinks().length, 0);
+ expect(navBarStore.getFooterNavLinks().length).toBe(0);
});
});
- describe('update link', function () {
- it('for nav links', function () {
+ describe('update link', () => {
+ it('for nav links', () => {
var link = {
id: 'update-link',
title: 'first'
@@ -205,59 +202,19 @@ describe('NavBarStore', function () {
link: link
});
- assert.equal(navBarStore.getNavLinks()[0].title, 'second');
+ expect(navBarStore.getNavLinks()[0].title).toMatch('second');
});
});
- describe('set version', function () {
- it('stores version number', function () {
+ describe('set version', () => {
+ it('stores version number', () => {
FauxtonAPI.dispatch({
type: 'NAVBAR_SET_VERSION_INFO',
version: 1234
});
- assert.equal(navBarStore.getVersion(), 1234);
- });
-
- });
-
- describe('is Minimized', function () {
-
- it('returns true if localstorage is true', function () {
- app.utils.localStorageSet(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED, true);
- assert.ok(navBarStore.isMinimized());
- });
-
- it('returns false if localstorage is false', function () {
- app.utils.localStorageSet(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED, false);
- assert.notOk(navBarStore.isMinimized(), false);
- });
-
- it('returns false if localstorage is undefined', function () {
- window.localStorage.removeItem(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED);
- assert.notOk(navBarStore.isMinimized(), false);
- });
- });
-
- describe('toggleMenu', function () {
-
- it('that is minimized changes to false', function () {
- app.utils.localStorageSet(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED, true);
- navBarStore.toggleMenu();
- assert.notOk(navBarStore.isMinimized());
- });
-
- it('that is not minimized changes to true', function () {
- app.utils.localStorageSet(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED, false);
- navBarStore.toggleMenu();
- assert.ok(navBarStore.isMinimized());
- });
-
- it('that is undefined changes to true', function () {
- window.localStorage.removeItem(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED);
- navBarStore.toggleMenu();
- assert.ok(navBarStore.isMinimized());
+ expect(navBarStore.getVersion()).toBe(1234);
});
});
diff --git a/app/addons/fauxton/navigation/__tests__/navlink-test.js b/app/addons/fauxton/navigation/__tests__/navlink-test.js
new file mode 100644
index 0000000..8201f4e
--- /dev/null
+++ b/app/addons/fauxton/navigation/__tests__/navlink-test.js
@@ -0,0 +1,48 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+import NavLink from "../components/NavLink";
+import React from "react";
+import ReactDOM from "react-dom";
+import {mount} from 'enzyme';
+
+describe('Navigation Bar', () => {
+ const dbLink = {
+ href: "#/_all_dbs",
+ title: "Databases",
+ icon: "fonticon-database",
+ className: 'databases'
+ };
+
+ describe('Active Link', () => {
+ it('matching title sets active css class', () => {
+ const linkEl = mount(<NavLink link={dbLink} active={"Databases"} isMinimized={false} />);
+ expect(linkEl.find('a.faux-navbar__link--active').length).toBe(1);
+ });
+
+ it('different title sets inactive css class', () => {
+ const linkEl = mount(<NavLink link={dbLink} active={"Replication"} isMinimized={false} />);
+ expect(linkEl.find('a.faux-navbar__link--inactive').length).toBe(1);
+ });
+ });
+
+ describe('Minimized Link', () => {
+ it('shows title when not minimized', () => {
+ const linkEl = mount(<NavLink link={dbLink} active={"Databases"} isMinimized={false} />);
+ expect(linkEl.text()).toMatch("Databases");
+ });
+
+ it('does not show title when minimized', () => {
+ const linkEl = mount(<NavLink link={dbLink} active={"Databases"} isMinimized={true} />);
+ expect(linkEl.find('span.faux-navbar__text').length).toBe(0);
+ });
+ });
+});
diff --git a/app/addons/fauxton/navigation/actiontypes.js b/app/addons/fauxton/navigation/actiontypes.js
index b5515bd..e5f685f 100644
--- a/app/addons/fauxton/navigation/actiontypes.js
+++ b/app/addons/fauxton/navigation/actiontypes.js
@@ -19,5 +19,8 @@ export default {
NAVBAR_SET_VERSION_INFO: 'NAVBAR_SET_VERSION_INFO',
NAVBAR_ACTIVE_LINK: 'NAVBAR_ACTIVE_LINK',
NAVBAR_HIDE: 'NAVBAR_HIDE',
- NAVBAR_SHOW: 'NAVBAR_SHOW'
+ NAVBAR_SHOW: 'NAVBAR_SHOW',
+ NAVBAR_SHOW_HIDE_LOGIN_LOGOUT_SECTION: 'NAVBAR_SHOW_HIDE_LOGIN_LOGOUT_SECTION',
+ NAVBAR_SHOW_LOGIN_BUTTON: 'NAVBAR_SHOW_LOGIN_BUTTON',
+ NAVBAR_SHOW_LOGOUT_BUTTON: 'NAVBAR_SHOW_LOGOUT_BUTTON'
};
diff --git a/app/addons/fauxton/navigation/components.react.jsx b/app/addons/fauxton/navigation/components.react.jsx
deleted file mode 100644
index a269726..0000000
--- a/app/addons/fauxton/navigation/components.react.jsx
+++ /dev/null
@@ -1,155 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-import FauxtonAPI from "../../../core/api";
-import React from "react";
-import ReactDOM from "react-dom";
-import Stores from "./stores";
-import Actions from "./actions";
-const navBarStore = Stores.navBarStore;
-
-const Footer = React.createClass({
- render () {
- const version = this.props.version;
-
- if (!version) { return null; }
- return (
- <div className="version-footer">
- Fauxton on {" "}
- <a href="http://couchdb.apache.org/">Apache CouchDB</a>
- {" "} v. {version}
- </div>
- );
- }
-});
-
-const Burger = React.createClass({
- render () {
- return (
- <div className="burger" onClick={this.props.toggleMenu}>
- <div></div>
- <div></div>
- <div></div>
- </div>
- );
- }
-});
-
-const NavLink = React.createClass({
- render () {
- const link = this.props.link;
- const liClassName = this.props.active === link.title ? 'active' : '';
-
- return (
- <li data-nav-name={link.title} className={liClassName} >
- <a href={link.href} target={link.target ? '_blank' : null} data-bypass={link.target ? 'true' : null}>
- <i className={link.icon + " fonticon "}></i>
- <span dangerouslySetInnerHTML={{__html: link.title }} />
- </a>
- </li>
- );
- }
-});
-
-export const NavBar = React.createClass({
- getStoreState () {
- return {
- navLinks: navBarStore.getNavLinks(),
- bottomNavLinks: navBarStore.getBottomNavLinks(),
- footerNavLinks: navBarStore.getFooterNavLinks(),
- activeLink: navBarStore.getActiveLink(),
- version: navBarStore.getVersion(),
- isMinimized: navBarStore.isMinimized(),
- isNavBarVisible: navBarStore.isNavBarVisible()
- };
- },
-
- getInitialState () {
- return this.getStoreState();
- },
-
- createLinks (links) {
- return _.map(links, function (link, i) {
- return <NavLink key={i} link={link} active={this.state.activeLink} />;
- }, this);
- },
-
- onChange () {
- this.setState(this.getStoreState());
- },
-
- setMenuState () {
- $('body').toggleClass('closeMenu', this.state.isMinimized);
- FauxtonAPI.Events.trigger(FauxtonAPI.constants.EVENTS.NAVBAR_SIZE_CHANGED, this.state.isMinimized);
- },
-
- componentDidMount () {
- navBarStore.on('change', this.onChange, this);
- this.setMenuState();
- },
-
- componentDidUpdate () {
- this.setMenuState();
- },
-
- componentWillUnmount () {
- navBarStore.off('change', this.onChange);
- },
-
- toggleMenu () {
- Actions.toggleNavbarMenu();
- },
-
- render () {
- //YUCK!! but we can only really fix this once we have removed all backbone
- if (!this.state.isNavBarVisible) {
- $('#primary-navbar').hide();
- return null;
- }
-
- $('#primary-navbar').show();
-
- const navLinks = this.createLinks(this.state.navLinks);
- const bottomNavLinks = this.createLinks(this.state.bottomNavLinks);
- const footerNavLinks = this.createLinks(this.state.footerNavLinks);
-
- return (
- <div className="navbar">
- <Burger toggleMenu={this.toggleMenu}/>
- <nav id="main_navigation">
- <ul id="nav-links" className="nav">
- {navLinks}
- {bottomNavLinks}
- </ul>
- </nav>
- <div id="primary-nav-right-shadow"/>
-
- <div className="bottom-container">
- <div className="brand">
- <div className="icon">Apache Fauxton</div>
- </div>
-
- <Footer version={this.state.version}/>
- <div id="footer-links">
- <ul id="footer-nav-links" className="nav">
- {footerNavLinks}
- </ul>
- </div>
- </div>
- </div>
- );
- }
-});
-
-export default {
- NavBar: NavBar,
- Burger: Burger
-};
diff --git a/app/addons/fauxton/navigation/actiontypes.js b/app/addons/fauxton/navigation/components/Brand.js
similarity index 53%
copy from app/addons/fauxton/navigation/actiontypes.js
copy to app/addons/fauxton/navigation/components/Brand.js
index b5515bd..e8751b0 100644
--- a/app/addons/fauxton/navigation/actiontypes.js
+++ b/app/addons/fauxton/navigation/components/Brand.js
@@ -10,14 +10,27 @@
// License for the specific language governing permissions and limitations under
// the License.
-export default {
- ADD_NAVBAR_LINK: 'ADD_NAVBAR_LINK',
- TOGGLE_NAVBAR_MENU: 'TOGGLE_NAVBAR_MENU',
- UPDATE_NAVBAR_LINK: 'UPDATE_NAVBAR_LINK',
- CLEAR_NAVBAR_LINK: 'CLEAR_NAVBAR_LINK',
- REMOVE_NAVBAR_LINK: 'REMOVE_NAVBAR_LINK',
- NAVBAR_SET_VERSION_INFO: 'NAVBAR_SET_VERSION_INFO',
- NAVBAR_ACTIVE_LINK: 'NAVBAR_ACTIVE_LINK',
- NAVBAR_HIDE: 'NAVBAR_HIDE',
- NAVBAR_SHOW: 'NAVBAR_SHOW'
+import React from 'react';
+
+import classNames from 'classnames';
+
+const Brand = ({isMinimized}) => {
+
+ const burgerClasses = classNames(
+ 'faux-navbar__brand-logo',
+ {'faux-navbar__brand-logo--wide': !isMinimized},
+ {'faux-navbar__brand-logo--narrow': isMinimized}
+ );
+
+ return (
+ <div className="faux-navbar__brand">
+ <div className={burgerClasses}></div>
+ </div>
+ );
};
+
+Brand.propTypes = {
+ isMinimized: React.PropTypes.bool.isRequired
+};
+
+export default Brand;
diff --git a/app/addons/fauxton/navigation/components/Burger.js b/app/addons/fauxton/navigation/components/Burger.js
new file mode 100644
index 0000000..fc9c357
--- /dev/null
+++ b/app/addons/fauxton/navigation/components/Burger.js
@@ -0,0 +1,41 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+import React from 'react';
+
+import classNames from 'classnames';
+
+const Burger = ({toggleMenu, isMinimized}) => {
+
+ const burgerClasses = classNames(
+ 'faux-navbar__burger',
+ {'faux-navbar--wide': !isMinimized},
+ {'faux-navbar--narrow': isMinimized}
+ );
+
+ const icon = isMinimized ?
+ 'icon-resize-horizontal' :
+ 'icon-signin faux-navbar__burger__icon--flipped';
+
+ return (
+ <div className={burgerClasses} onClick={toggleMenu}>
+ <i className={"faux-navbar__burger__icon " + icon}></i>
+ </div>
+ );
+};
+
+Burger.propTypes = {
+ toggleMenu: React.PropTypes.func.isRequired,
+ isMinimized: React.PropTypes.bool.isRequired
+};
+
+export default Burger;
diff --git a/app/addons/fauxton/navigation/actiontypes.js b/app/addons/fauxton/navigation/components/Footer.js
similarity index 59%
copy from app/addons/fauxton/navigation/actiontypes.js
copy to app/addons/fauxton/navigation/components/Footer.js
index b5515bd..72ddd01 100644
--- a/app/addons/fauxton/navigation/actiontypes.js
+++ b/app/addons/fauxton/navigation/components/Footer.js
@@ -10,14 +10,24 @@
// License for the specific language governing permissions and limitations under
// the License.
-export default {
- ADD_NAVBAR_LINK: 'ADD_NAVBAR_LINK',
- TOGGLE_NAVBAR_MENU: 'TOGGLE_NAVBAR_MENU',
- UPDATE_NAVBAR_LINK: 'UPDATE_NAVBAR_LINK',
- CLEAR_NAVBAR_LINK: 'CLEAR_NAVBAR_LINK',
- REMOVE_NAVBAR_LINK: 'REMOVE_NAVBAR_LINK',
- NAVBAR_SET_VERSION_INFO: 'NAVBAR_SET_VERSION_INFO',
- NAVBAR_ACTIVE_LINK: 'NAVBAR_ACTIVE_LINK',
- NAVBAR_HIDE: 'NAVBAR_HIDE',
- NAVBAR_SHOW: 'NAVBAR_SHOW'
+import React from 'react';
+
+const Footer = ({version}) => {
+
+ if (!version) { return null; }
+
+ return (
+ <div className="faux-navbar__version-footer">
+ Fauxton on
+ <a href="http://couchdb.apache.org/">Apache CouchDB</a>
+ <div>v. {version}</div>
+ </div>
+ );
+
};
+
+Footer.propTypes = {
+ version: React.PropTypes.string
+};
+
+export default Footer;
diff --git a/app/addons/fauxton/navigation/components/LoginButton.js b/app/addons/fauxton/navigation/components/LoginButton.js
new file mode 100644
index 0000000..6493901
--- /dev/null
+++ b/app/addons/fauxton/navigation/components/LoginButton.js
@@ -0,0 +1,42 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+import React from 'react';
+
+import classNames from 'classnames';
+
+const LoginButton = ({isMinimized}) => {
+
+ const containerClasses = classNames(
+ 'faux-navbar__login__textcontainer',
+ {'faux-navbar__login__textcontainer--narrow': isMinimized},
+ {'faux-navbar__login__textcontainer--wide': !isMinimized}
+ );
+
+ return (
+ <a className="faux-login__link" href="#/login">
+
+ <div className={containerClasses}>
+ <span className="faux-navbar__logout__text">
+ Login
+ </span>
+ </div>
+
+ </a>
+ );
+};
+
+LoginButton.propTypes = {
+ isMinimized: React.PropTypes.bool.isRequired
+};
+
+export default LoginButton;
diff --git a/app/addons/fauxton/navigation/components/LogoutButton.js b/app/addons/fauxton/navigation/components/LogoutButton.js
new file mode 100644
index 0000000..037f172
--- /dev/null
+++ b/app/addons/fauxton/navigation/components/LogoutButton.js
@@ -0,0 +1,47 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+import React from 'react';
+
+import classNames from 'classnames';
+import _ from 'lodash';
+
+const LogoutButton = ({username, isMinimized}) => {
+
+ const containerClasses = classNames(
+ 'faux-navbar__logout__textcontainer',
+ {'faux-navbar__logout__textcontainer--narrow': isMinimized},
+ {'faux-navbar__logout__textcontainer--wide': !isMinimized}
+ );
+
+ return (
+ <a className="faux-logout__link" href="#/logout">
+ <div className={containerClasses}>
+ <span className="faux-navbar__logout__text">
+ Log Out
+ </span>
+
+ {isMinimized ?
+ null :
+ <span className="faux-navbar__logout__username">{_.escape(username)}</span>
+ }
+ </div>
+ </a>
+ );
+};
+
+LogoutButton.propTypes = {
+ username: React.PropTypes.string,
+ isMinimized: React.PropTypes.bool.isRequired
+};
+
+export default LogoutButton;
diff --git a/app/addons/fauxton/navigation/components/NavBar.js b/app/addons/fauxton/navigation/components/NavBar.js
new file mode 100644
index 0000000..3a83a11
--- /dev/null
+++ b/app/addons/fauxton/navigation/components/NavBar.js
@@ -0,0 +1,115 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+import React, { Component } from 'react';
+
+import Footer from './Footer';
+import Burger from './Burger';
+import NavLink from './NavLink';
+import Brand from './Brand';
+import LogoutButton from './LogoutButton';
+import LoginButton from './LoginButton';
+
+import Actions from "../actions";
+
+import classNames from 'classnames';
+
+class NavBar extends Component {
+
+ createLinks (links) {
+ const { activeLink, isMinimized } = this.props;
+
+ return links.map((link, i) => {
+ return <NavLink
+ key={i}
+ link={link}
+ active={activeLink}
+ isMinimized={isMinimized} />;
+ });
+ }
+
+ toggleMenu () {
+ Actions.toggleNavbarMenu();
+ }
+
+ render () {
+ const {
+ isMinimized,
+ version,
+ isLoginSectionVisible,
+ isLoginVisibleInsteadOfLogout,
+ activeLink,
+ username,
+ isNavBarVisible
+ } = this.props;
+
+ if (!isNavBarVisible) {
+ return null;
+ }
+
+ const navLinks = this.createLinks(this.props.navLinks);
+ const bottomNavLinks = this.createLinks(this.props.bottomNavLinks);
+ const footerNavLinks = this.createLinks(this.props.footerNavLinks);
+
+ const navClasses = classNames(
+ 'faux-navbar',
+ {'faux-navbar--wide': !isMinimized},
+ {'faux-navbar--narrow': isMinimized}
+ );
+
+ const loginSection = isLoginVisibleInsteadOfLogout ?
+ <LoginButton active={activeLink} isMinimized={isMinimized} /> :
+ <LogoutButton username={username} isMinimized={isMinimized} />;
+
+ return (
+ <div className={navClasses}>
+ <nav>
+ <Burger isMinimized={isMinimized} toggleMenu={this.toggleMenu}/>
+ <div className="faux-navbar__linkcontainer">
+ <div className="faux-navbar__links">
+ {navLinks}
+ {bottomNavLinks}
+ </div>
+
+ <div className="faux-navbar__footer">
+ <Brand isMinimized={isMinimized} />
+
+ <div>
+ {footerNavLinks}
+ </div>
+
+ <Footer version={version}/>
+
+ {isLoginSectionVisible ? loginSection : null}
+ </div>
+ </div>
+ </nav>
+ <div id="primary-nav-right-shadow"/>
+ </div>
+ );
+ }
+}
+
+NavBar.propTypes = {
+ activeLink: React.PropTypes.string,
+ isMinimized: React.PropTypes.bool.isRequired,
+ version: React.PropTypes.string,
+ username: React.PropTypes.string,
+ navLinks: React.PropTypes.array,
+ bottomNavLinks: React.PropTypes.array,
+ footerNavLinks: React.PropTypes.array,
+ isNavBarVisible: React.PropTypes.bool,
+ isLoginSectionVisible: React.PropTypes.bool.isRequired,
+ isLoginVisibleInsteadOfLogout: React.PropTypes.bool.isRequired
+};
+
+export default NavBar;
diff --git a/app/addons/fauxton/navigation/components/NavLink.js b/app/addons/fauxton/navigation/components/NavLink.js
new file mode 100644
index 0000000..1cb6b7b
--- /dev/null
+++ b/app/addons/fauxton/navigation/components/NavLink.js
@@ -0,0 +1,51 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+import React from 'react';
+import classNames from 'classnames';
+
+
+const NavLink = ({link, active, isMinimized}) => {
+
+ const linkClass = classNames(
+ 'faux-navbar__link',
+ {'faux-navbar__link--active': active === link.title},
+ {'faux-navbar__link--inactive': active !== link.title},
+ {'faux-navbar--wide': !isMinimized},
+ {'faux-navbar--narrow': isMinimized}
+ );
+
+ return (
+ <a className={linkClass} href={link.href} target={link.target ? '_blank' : null} data-bypass={link.target ? 'true' : null}>
+ <div data-nav-name={link.title} className="faux-navbar__itemarea">
+
+ {!!link.icon ?
+ <i className={classNames(link.icon, 'fonticon faux-navbar__icon')}></i> :
+ null
+ }
+ {isMinimized ?
+ null :
+ <span className="faux-navbar__text">{link.title}</span>
+ }
+ </div>
+ </a>
+ );
+};
+
+NavLink.propTypes = {
+ link: React.PropTypes.object.isRequired,
+ active: React.PropTypes.string,
+ isMinimized: React.PropTypes.bool.isRequired,
+};
+
+
+export default NavLink;
diff --git a/app/addons/fauxton/navigation/container/NavBar.js b/app/addons/fauxton/navigation/container/NavBar.js
new file mode 100644
index 0000000..537f0da
--- /dev/null
+++ b/app/addons/fauxton/navigation/container/NavBar.js
@@ -0,0 +1,68 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+import FauxtonAPI from "../../../../core/api";
+import React from "react";
+
+import ReactDOM from "react-dom";
+import Stores from "../stores";
+
+import NavBar from '../components/NavBar';
+
+const navBarStore = Stores.navBarStore;
+
+const NavBarContainer = React.createClass({
+
+ getStoreState () {
+ return {
+ navLinks: navBarStore.getNavLinks(),
+ bottomNavLinks: navBarStore.getBottomNavLinks(),
+ footerNavLinks: navBarStore.getFooterNavLinks(),
+ activeLink: navBarStore.getActiveLink(),
+ version: navBarStore.getVersion(),
+ isMinimized: navBarStore.isMinimized(),
+ isNavBarVisible: navBarStore.isNavBarVisible(),
+
+ isLoginSectionVisible: navBarStore.getIsLoginSectionVisible(),
+ isLoginVisibleInsteadOfLogout: navBarStore.getIsLoginVisibleInsteadOfLogout()
+ };
+ },
+
+ getInitialState () {
+ return this.getStoreState();
+ },
+
+ onChange () {
+ this.setState(this.getStoreState());
+ },
+
+ componentDidMount () {
+ navBarStore.on('change', this.onChange, this);
+ },
+
+ componentWillUnmount () {
+ navBarStore.off('change', this.onChange);
+ },
+
+ render () {
+ const user = FauxtonAPI.session.user();
+
+ const username = user ? user.name : '';
+ return (
+ <NavBar {...this.state} username={username} />
+ );
+ }
+
+});
+
+
+export default NavBarContainer;
diff --git a/app/addons/fauxton/navigation/stores.js b/app/addons/fauxton/navigation/stores.js
index 5e781a8..777f097 100644
--- a/app/addons/fauxton/navigation/stores.js
+++ b/app/addons/fauxton/navigation/stores.js
@@ -10,11 +10,10 @@
// License for the specific language governing permissions and limitations under
// the License.
-import app from "../../../app";
import FauxtonAPI from "../../../core/api";
import ActionTypes from "./actiontypes";
-const Stores = {};
+const Stores = {};
Stores.NavBarStore = FauxtonAPI.Store.extend({
initialize () {
@@ -22,12 +21,24 @@ Stores.NavBarStore = FauxtonAPI.Store.extend({
},
reset () {
+ this._isMinimized = true;
this._activeLink = null;
this._version = null;
this._navLinks = [];
this._footerNavLinks = [];
this._bottomNavLinks = [];
this._navBarVisible = true;
+
+ this._loginSectionVisible = false;
+ this._loginVisibleInsteadOfLogout = true;
+ },
+
+ getIsLoginSectionVisible () {
+ return this._loginSectionVisible;
+ },
+
+ getIsLoginVisibleInsteadOfLogout () {
+ return this._loginVisibleInsteadOfLogout;
},
isNavBarVisible () {
@@ -67,7 +78,7 @@ Stores.NavBarStore = FauxtonAPI.Store.extend({
const links = this.getLinkSection(removeLink);
let indexOf = 0;
- const res = _.filter(links, function (link) {
+ const res = links.filter((link) => {
if (link.id === removeLink.id) {
return true;
}
@@ -94,8 +105,7 @@ Stores.NavBarStore = FauxtonAPI.Store.extend({
},
toggleMenu () {
- app.utils.localStorageSet(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED,
- !this.isMinimized());
+ this._isMinimized = !this._isMinimized;
},
getLinkSection (link) {
@@ -143,8 +153,7 @@ Stores.NavBarStore = FauxtonAPI.Store.extend({
},
isMinimized () {
- const isMinimized = app.utils.localStorageGet(FauxtonAPI.constants.LOCAL_STORAGE.SIDEBAR_MINIMIZED);
- return (_.isUndefined(isMinimized)) ? false : isMinimized;
+ return this._isMinimized;
},
dispatch (action) {
@@ -185,6 +194,20 @@ Stores.NavBarStore = FauxtonAPI.Store.extend({
this.showNavBar();
break;
+ case ActionTypes.NAVBAR_SHOW_HIDE_LOGIN_LOGOUT_SECTION:
+ this._loginSectionVisible = action.visible;
+ break;
+
+ case ActionTypes.NAVBAR_SHOW_LOGIN_BUTTON:
+ this._loginSectionVisible = true;
+ this._loginVisibleInsteadOfLogout = true;
+ break;
+
+ case ActionTypes.NAVBAR_SHOW_LOGOUT_BUTTON:
+ this._loginSectionVisible = true;
+ this._loginVisibleInsteadOfLogout = false;
+ break;
+
default:
return;
// do nothing
diff --git a/app/addons/fauxton/navigation/tests/componentsSpec.react.jsx b/app/addons/fauxton/navigation/tests/componentsSpec.react.jsx
deleted file mode 100644
index ba37364..0000000
--- a/app/addons/fauxton/navigation/tests/componentsSpec.react.jsx
+++ /dev/null
@@ -1,61 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-import FauxtonAPI from "../../../../core/api";
-import Views from "../components.react";
-import Auth from "../../../../core/auth";
-import BaseAuth from "../../../auth/base";
-import utils from "../../../../../test/mocha/testUtils";
-import React from "react";
-import ReactDOM from "react-dom";
-import sinon from "sinon";
-import {mount} from 'enzyme';
-
-var assert = utils.assert;
-
-describe('NavBar', function () {
-
- describe('burger', function () {
- it('dispatch TOGGLE_NAVBAR_MENU on click', function () {
- const toggleMenu = sinon.spy();
- const burgerEl = mount(<Views.Burger toggleMenu={toggleMenu} />);
- burgerEl.simulate('click');
- assert.ok(toggleMenu.calledOnce);
- });
-
- });
-
- it('logout link only ever appears once', function () {
- FauxtonAPI.auth = new Auth();
- sinon.stub(FauxtonAPI.session, 'isLoggedIn').returns(true);
- sinon.stub(FauxtonAPI.session, 'isAdminParty').returns(false);
- sinon.stub(FauxtonAPI.session, 'user').returns({ name: 'test-user' });
- BaseAuth.initialize();
-
- const el = mount(<Views.NavBar />);
-
- FauxtonAPI.session.trigger('change');
-
- // confirm the logout link is present
- let matches = el.text().match(/Logout/);
- assert.equal(matches.length, 1);
-
- // now confirm there's still only a single logout link after publishing multiple
- FauxtonAPI.session.trigger('change');
- FauxtonAPI.session.trigger('change');
- matches = el.text().match(/Logout/);
- assert.equal(matches.length, 1);
-
- FauxtonAPI.session.isLoggedIn.restore();
- FauxtonAPI.session.user.restore();
- FauxtonAPI.session.isAdminParty.restore();
- });
-});
diff --git a/app/addons/fauxton/tests/nightwatch/highlightsidebar.js b/app/addons/fauxton/tests/nightwatch/highlightsidebar.js
index def0e23..daa4c59 100644
--- a/app/addons/fauxton/tests/nightwatch/highlightsidebar.js
+++ b/app/addons/fauxton/tests/nightwatch/highlightsidebar.js
@@ -24,7 +24,7 @@ module.exports = {
.click('a[href="#/replication"]')
.pause(1000)
.waitForElementVisible('.replication__activity_header-btn', waitTime, false)
- .assert.cssClassPresent('li[data-nav-name="Replication"]', 'active')
+ .assert.cssClassPresent('a[href="#/replication"]', 'faux-navbar__link--active')
.end();
}
};
diff --git a/app/addons/fauxton/tests/nightwatch/notificationCenter.js b/app/addons/fauxton/tests/nightwatch/notificationCenter.js
index b81d860..0c192bd 100644
--- a/app/addons/fauxton/tests/nightwatch/notificationCenter.js
+++ b/app/addons/fauxton/tests/nightwatch/notificationCenter.js
@@ -29,6 +29,7 @@ module.exports = {
.assert.cssClassNotPresent('.notification-center-panel', 'visible')
.clickWhenVisible('#notification-center-btn', waitTime, false)
.waitForElementPresent('.notification-center-panel.visible', waitTime, false)
+ .waitForElementPresent('.notification-list div.flex-layout', waitTime, false)
.getText('.notification-center-panel', function (result) {
var content = result.value;
diff --git a/app/app.js b/app/app.js
index 4551a07..cf9be21 100644
--- a/app/app.js
+++ b/app/app.js
@@ -72,6 +72,25 @@ FauxtonAPI.config({
type: 'REMOVE_NAVBAR_LINK',
link: link
});
+ },
+
+ hideLogin: function () {
+ FauxtonAPI.dispatch({
+ type: 'NAVBAR_SHOW_HIDE_LOGIN_LOGOUT_SECTION',
+ visible: false
+ });
+ },
+
+ showLogout: function () {
+ FauxtonAPI.dispatch({
+ type: 'NAVBAR_SHOW_LOGOUT_BUTTON'
+ });
+ },
+
+ showLogin: function () {
+ FauxtonAPI.dispatch({
+ type: 'NAVBAR_SHOW_LOGIN_BUTTON'
+ });
}
});
diff --git a/app/constants.js b/app/constants.js
index a967db4..e937094 100644
--- a/app/constants.js
+++ b/app/constants.js
@@ -47,9 +47,5 @@ export default {
MANGO_INDEX: '/_utils/docs/intro/api.html#documents',
MANGO_SEARCH: '/_utils/docs/intro/api.html#documents',
CHANGES: '/_utils/docs/api/database/changes.html?highlight=changes#post--db-_changes'
- },
-
- LOCAL_STORAGE: {
- SIDEBAR_MINIMIZED: 'sidebar-minimized'
}
};
diff --git a/assets/less/fauxton.less b/assets/less/fauxton.less
index 679857b..31145f0 100644
--- a/assets/less/fauxton.less
+++ b/assets/less/fauxton.less
@@ -60,10 +60,6 @@ body {
h2,h3,h4 {font-weight: 600;}
-a {
- .transition(all .25s linear);
-}
-
// remove blue borders from clicked elements
button:focus, a:focus {
outline: 0;
@@ -77,6 +73,7 @@ a,
a:visited,
a:active {
color: @linkColor;
+ text-decoration: none;
}
a:hover {
@@ -557,7 +554,7 @@ footer.pagination-footer {
// left navigationbar is closed
@media (max-width: 875px) {
- body:not(.closeMenu) {
+ #main:not(.closeMenu) {
.one-pane {
.faux-header__searchboxwrapper {
display: none;
@@ -567,7 +564,7 @@ footer.pagination-footer {
}
@media (max-width: 1285px) {
- body:not(.closeMenu) {
+ #main:not(.closeMenu) {
.with-sidebar {
.faux-header__searchboxwrapper {
display: none;
@@ -584,7 +581,7 @@ body .control-toggle-include-docs span {
}
@media (max-width: 1177px) {
- body.closeMenu {
+ #main.closeMenu {
.with-sidebar {
.two-panel-header {
.control-toggle-include-docs span::before {
@@ -600,7 +597,7 @@ body .control-toggle-include-docs span {
@media (max-width: 1120px) {
- body.closeMenu {
+ #main.closeMenu {
.with-sidebar {
.two-panel-header {
.control-toggle-include-docs span::before {
@@ -627,7 +624,7 @@ body .control-toggle-include-docs span {
}
@media (max-width: 1339px) {
- body:not(.closeMenu) {
+ #main:not(.closeMenu) {
.with-sidebar {
.two-panel-header {
.control-toggle-include-docs span::before {
@@ -642,7 +639,7 @@ body .control-toggle-include-docs span {
}
@media (max-width: 1090px) {
- body:not(.closeMenu) {
+ #main:not(.closeMenu) {
.with-sidebar {
.two-panel-header {
.control-toggle-include-docs span::before {
diff --git a/assets/less/templates.less b/assets/less/templates.less
index d7a7b49..f42de00 100644
--- a/assets/less/templates.less
+++ b/assets/less/templates.less
@@ -50,234 +50,6 @@
}
}
-/* Fixed side navigation */
-#primary-navbar {
- z-index: 5;
- height: 100%;
- left: 0;
- top: 0;
- bottom: 0;
- position: absolute;
- width: @navWidth;
- .closeMenu & {
- width: @collapsedNavWidth;
- overflow-x: hidden;
- }
- background-color: @brandDark2;
- .version-footer {
- color: #fff;
- font-size: 10px;
- text-align: center;
- background-color: @brandDark2;
- }
- .closeMenu & {
- .version-footer {
- display: none;
- }
- }
- #user-create-admin {
- font-size: 12px
- }
- .navbar {
- .closeMenu & {
- width: @collapsedNavWidth;
- }
- .burger {
- padding: 22px 0 22px 18px;
- position: absolute;
- z-index: 100;
- top: 0;
- background-color: @brandDark2;
- width: @navWidth;
- .closeMenu & {
- width: @collapsedNavWidth;
- }
- div {
- height: 4px;
- width: 24px;
- .border-radius(2);
- background-color: @navIconColor;
- margin: 2px 0px;
- }
- &:hover div {
- background-color: @hoverHighlight;
- }
- }
- .bottom-container {
- position: fixed;
- bottom: 10px;
- .brand {
- .box-sizing(content-box);
- .hide-text;
- margin: 20px 0 0px 0;
- width: 199px;
- height: 50px;
- padding: 10px 10px 10px 10px;
- float: none;
- background: @brandDark2;
- .icon {
- .box-sizing(content-box);
- background: url(../img/CouchDB-negative-logo.png) no-repeat 23px 0px;
- background-size: 150px;
- display: block;
- height: 100%;
- width: 100%;
- margin-top: 10px;
- }
- .closeMenu & {
- width: 43px;
-
- .icon {
- background: url(../img/couchdb-logo.png) no-repeat 3px 0;
- background-size: 40px;
-
- }
- }
- }
-
- #footer-nav-links {
- width: 100%;
- margin: 0;
- background: @brandDark2;
-
- li {
- width: 100%;
- text-align: center;
-
- a {
- font-size: 10px;
- color: @navIconColor;
- padding: 0px;
- text-shadow: none;
- width: 100%;
- }
-
- &.active, &:hover {
- a {
- text-decoration: underline;
- color: @hoverHighlight;
- }
- }
- }
- }
- }
- nav {
- margin-top: 64px;
- .nav {
- margin: 0;
- li {
- padding: 0;
- font-size: 16px;
- letter-spacing: 1px;
- line-height: 24px;
- width: @navWidth;
- .closeMenu & {
- width: @collapsedNavWidth;
- }
- font-weight: normal;
- font-family: helvetica;
- .box-sizing(border-box);
- background-color: @brandDark1;
- border-bottom: 1px solid @brandDark2;
- height: 48px;
- min-height: 48px;
- position: relative;
-
- &.active {
- a {
- .box-shadow(none);
- }
- background-color: @brandHighlight;
- //pointer-events: none;
- }
- &:hover {
- a {
- .box-shadow(none);
- }
- background-color: @hoverHighlight;
- }
- &:hover .fonticon:before {
- color: @white;
- }
- &.active .fonticon:before,
- &:hover .fonticon:before,
- {
- text-shadow: @boxShadow;
- color: @navIconActive;
- }
- a {
- padding: 12px 25px 12px 60px;
- background-color: transparent;
- color: #fff;
- text-shadow: @textShadowOff;
- height: 48px;
- display: flex;
-
- .fonticon {
- position: relative;
-
- &:before {
- position: absolute;
- left: -41px;
- font-size: 24px;
- color: @navIconColor;
- text-shadow: @boxShadowOff;
- }
- }
- .closeMenu & {
- color: transparent;
- span {
- display: none;
- }
- }
- }
- }
- }
- ul#footer-nav-links {
- li {
- background-color: @brandDark2;
- border-top: 1px solid @linkColor;
- border-bottom: none;
- &.active, &:hover {
- background-color: @hoverHighlight;
- border-top: 1px solid @linkColor;
- a {
- color: white;
- }
- }
- a {
- color: @linkColor;
- }
- }
-
- }
- ul#bottom-nav-links {
- margin-top: 0;
- padding-bottom: 136px;
- li {
- background-color: @brandDark1;
- &.active {
- background-color:@brandHighlight;
- }
- &:hover {
- background-color: @hoverHighlight;
- }
- a {
- &.fonticon {
- position: relative;
- &:before {
- top: -5px;
- left: -40px;
- font-size: 22px;
- }
- }
- }
- }
- }
- }
- }
-}
-
.bottom-shadow-border {
border-bottom: 1px solid #999;
.box-shadow(0px 6px 0 0 rgba(0, 0, 0, 0.1));
diff --git a/assets/less/variables.less b/assets/less/variables.less
index a33f43f..47d9b76 100644
--- a/assets/less/variables.less
+++ b/assets/less/variables.less
@@ -115,3 +115,10 @@
@infoAlertColor: #329898;
@successAlertColor: #448c11;
@errorAlertColor: #c45b55;
+
+/*
+ -- logo image paths --
+ These are set during webpack bundle through your settings.json file.
+*/
+@largeLogoPath: "";
+@smallLogoPath: "";
diff --git a/package.json b/package.json
index 1bad884..3d4d985 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,7 @@
"fetch-mock": "^5.9.3",
"jest": "^18.1.0",
"less": "^2.7.2",
- "less-loader": "^2.2.3",
+ "less-loader": "^4.0.3",
"mocha": "~3.1.2",
"mocha-loader": "^1.1.0",
"mocha-phantomjs": "git+https://github.com/garrensmith/mocha-phantomjs.git",
@@ -50,6 +50,7 @@
"bluebird": "^3.4.6",
"brace": "^0.7.0",
"chai": "^3.5.0",
+ "classnames": "^2.2.5",
"clean-css": "^4.0.5",
"clipboard": "^1.5.16",
"couchapp": "~0.11.0",
@@ -77,8 +78,8 @@
"imports-loader": "^0.7.0",
"jquery": "^2.2.0",
"jsondiffpatch": "^0.1.41",
- "less": "^2.3.1",
- "less-loader": "^2.2.3",
+ "less": "^2.7.2",
+ "less-loader": "^4.0.3",
"lodash": "^3.10.1",
"mkdirp": "^0.5.1",
"moment": "^2.17.1",
diff --git a/settings.json.default.json b/settings.json.default.json
index fde5dad..d474c1b 100644
--- a/settings.json.default.json
+++ b/settings.json.default.json
@@ -21,7 +21,9 @@
"dest": "dist/debug/index.html",
"variables": {
"title": "Project Fauxton",
- "generationLabel": "Fauxton Dev"
+ "generationLabel": "Fauxton Dev",
+ "largeLogoPath": "../../../../../assets/img/CouchDB-negative-logo.png",
+ "smallLogoPath": "../../../../../assets/img/couchdb-logo.png"
},
"app": {
"root": "/",
@@ -34,7 +36,9 @@
"dest": "dist/debug/index.html",
"variables": {
"title": "Project Fauxton",
- "generationLabel": "Fauxton Release"
+ "generationLabel": "Fauxton Release",
+ "largeLogoPath": "../../../../../assets/img/CouchDB-negative-logo.png",
+ "smallLogoPath": "../../../../../assets/img/couchdb-logo.png"
},
"app": {
"root": "/",
@@ -47,7 +51,9 @@
"dest": "dist/debug/index.html",
"variables": {
"title": "Project Fauxton",
- "generationLabel": "Fauxton Couchapp"
+ "generationLabel": "Fauxton Couchapp",
+ "largeLogoPath": "../../../../../assets/img/CouchDB-negative-logo.png",
+ "smallLogoPath": "../../../../../assets/img/couchdb-logo.png"
},
"app": {
"root": "/",
diff --git a/webpack.config.dev.js b/webpack.config.dev.js
index 89d3dbd..54f09f4 100644
--- a/webpack.config.dev.js
+++ b/webpack.config.dev.js
@@ -74,7 +74,15 @@ module.exports = {
use: [
"style-loader",
"css-loader",
- "less-loader"
+ {
+ loader: "less-loader",
+ options: {
+ modifyVars: {
+ largeLogoPath: "\'" + settings.variables.largeLogoPath + "\'",
+ smallLogoPath: "\'" + settings.variables.smallLogoPath + "\'"
+ }
+ }
+ }
]
},
{
diff --git a/webpack.config.release.js b/webpack.config.release.js
index 99316c9..5220952 100644
--- a/webpack.config.release.js
+++ b/webpack.config.release.js
@@ -109,7 +109,15 @@ module.exports = {
fallback: "style-loader",
use: [
"css-loader",
- "less-loader"
+ {
+ loader: "less-loader",
+ options: {
+ modifyVars: {
+ largeLogoPath: "\'" + settings.variables.largeLogoPath + "\'",
+ smallLogoPath: "\'" + settings.variables.smallLogoPath + "\'"
+ }
+ }
+ }
],
publicPath: '../../'
}),
--
To stop receiving notification emails like this one, please contact
['"commits@couchdb.apache.org" <co...@couchdb.apache.org>'].