You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by mi...@apache.org on 2015/06/12 00:05:28 UTC

fauxton commit: updated refs/heads/master to 9d7cd44

Repository: couchdb-fauxton
Updated Branches:
  refs/heads/master b1e63328b -> 9d7cd4421


Convert Sidebar to React

Converts Sidebar to React. Fixes COUCHDB-2703


Project: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/commit/9d7cd442
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/9d7cd442
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/9d7cd442

Branch: refs/heads/master
Commit: 9d7cd44213a56a0a84486048c105b5c9c62c04ee
Parents: b1e6332
Author: Garren Smith <ga...@gmail.com>
Authored: Mon May 25 17:16:33 2015 +0200
Committer: michellephung@gmail.com <mi...@gmail.com>
Committed: Thu Jun 11 18:02:11 2015 -0400

----------------------------------------------------------------------
 app/addons/compaction/routes.js                 |   2 +-
 .../nightwatch/compactAndCleanIsPresent.js      |   2 +-
 .../components/react-components.react.jsx       |  59 ++-
 app/addons/cors/assets/less/cors.less           |   4 -
 .../databases/tests/nightwatch/zeroclipboard.js |   1 +
 app/addons/documents/assets/less/sidenav.less   |  35 +-
 app/addons/documents/changes/actions.js         |   4 +-
 app/addons/documents/routes-documents.js        |  20 +-
 app/addons/documents/routes-mango.js            |  10 +-
 app/addons/documents/shared-routes.js           |  18 +-
 app/addons/documents/shared-views.js            | 320 -------------
 app/addons/documents/sidebar/actions.js         |  52 +++
 app/addons/documents/sidebar/actiontypes.js     |  20 +
 app/addons/documents/sidebar/sidebar.react.jsx  | 448 +++++++++++++++++++
 app/addons/documents/sidebar/stores.js          | 153 +++++++
 .../sidebar/tests/sidebar.storesSpec.js         |  79 ++++
 .../documents/templates/design_doc_menu.html    |  32 --
 .../documents/templates/index_menu_item.html    |  31 --
 app/addons/documents/templates/sidebar.html     |  50 ---
 .../switchDatabaseViaLookaheadTray.js           |   3 +-
 .../documents/tests/nightwatch/viewCreate.js    |  11 +-
 app/addons/documents/views.js                   |   5 +-
 assets/less/fauxton.less                        |   3 +-
 assets/less/templates.less                      |   5 +-
 24 files changed, 890 insertions(+), 477 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/compaction/routes.js
----------------------------------------------------------------------
diff --git a/app/addons/compaction/routes.js b/app/addons/compaction/routes.js
index ea6d54f..f098768 100644
--- a/app/addons/compaction/routes.js
+++ b/app/addons/compaction/routes.js
@@ -39,7 +39,7 @@ function (app, FauxtonAPI, Compaction, Actions, Databases, BaseRoute) {
 
       this.createDesignDocsCollection();
       this.addLeftHeader();
-      this.addSidebar('docLink_compact');
+      this.addSidebar('compact');
     },
 
     onSelectDatabase: function (dbName) {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/compaction/tests/nightwatch/compactAndCleanIsPresent.js
----------------------------------------------------------------------
diff --git a/app/addons/compaction/tests/nightwatch/compactAndCleanIsPresent.js b/app/addons/compaction/tests/nightwatch/compactAndCleanIsPresent.js
index a9fb368..12f8c16 100644
--- a/app/addons/compaction/tests/nightwatch/compactAndCleanIsPresent.js
+++ b/app/addons/compaction/tests/nightwatch/compactAndCleanIsPresent.js
@@ -22,7 +22,7 @@ module.exports = {
 
       //navigate to 'Compact & Clean' view
       .clickWhenVisible('#dashboard-content a[href="#/database/' + newDatabaseName + '/_all_docs"]')
-      .clickWhenVisible('#docLink_compact')
+      .clickWhenVisible('#compact')
       .waitForElementVisible('#compact-db', waitTime, false)
       .waitForElementVisible('#cleanup-views', waitTime, false)
       .end();

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/components/react-components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/components/react-components.react.jsx b/app/addons/components/react-components.react.jsx
index 9458b51..d543a09 100644
--- a/app/addons/components/react-components.react.jsx
+++ b/app/addons/components/react-components.react.jsx
@@ -620,6 +620,62 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
     }
   });
 
+  var MenuDropDown = React.createClass({
+
+    getDefaultProps: function () {
+      return {
+        icon: 'fonticon-plus-circled'
+      };
+    },
+
+    createSectionLinks: function (links) {
+      if (!links) { return null; }
+      return links.map(function (link, key) {
+        return (
+          <li key={key}>
+            <a className={link.icon ? 'icon ' + link.icon : ''}
+              data-bypass={link.external ? 'true' : ''}
+              href={link.url}
+              target={link.external ? '_blank' : ''}>
+              {link.title}
+            </a>
+          </li>
+        );
+      });
+    },
+
+    createSectionTitle: function (title) {
+      if (!title) {
+        return null;
+      }
+
+      return (
+        <li className="header-label">{title}</li>
+      );
+    },
+
+    createSection: function () {
+      return this.props.links.map(function (linkSection, key) {
+        return (
+          <ul className="dropdown-menu arrow" key={key} role="menu" aria-labelledby="dLabel">
+            {this.createSectionTitle(linkSection.title)}
+            {this.createSectionLinks(linkSection.links)}
+        </ul>
+      );
+      }.bind(this));
+    },
+
+    render: function () {
+      return (
+        <div className="dropdown">
+          <a className={"dropdown-toggle icon " + this.props.icon} data-toggle="dropdown" href="#" data-bypass="true"></a>
+            {this.createSection()}
+        </div>
+      );
+    }
+
+  });
+
   var ReactComponents = {
     ConfirmButton: ConfirmButton,
     ToggleHeaderButton: ToggleHeaderButton,
@@ -629,7 +685,8 @@ function (app, FauxtonAPI, React, Components, ace, beautifyHelper) {
     Beautify: Beautify,
     PaddedBorderedBox: PaddedBorderedBox,
     Document: Document,
-    LoadLines: LoadLines
+    LoadLines: LoadLines,
+    MenuDropDown: MenuDropDown
   };
 
   return ReactComponents;

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/cors/assets/less/cors.less
----------------------------------------------------------------------
diff --git a/app/addons/cors/assets/less/cors.less b/app/addons/cors/assets/less/cors.less
index 259e247..59b5dda 100644
--- a/app/addons/cors/assets/less/cors.less
+++ b/app/addons/cors/assets/less/cors.less
@@ -14,10 +14,6 @@
 @import "../../../../../assets/less/variables.less";
 @import "../../../../../assets/less/bootstrap/mixins.less";
 
-#sidebar-content .nav-list li a span {
-  display: none;
-}
-
 #cors-header {
   margin: 18px 30px 0 30px;
 }

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/databases/tests/nightwatch/zeroclipboard.js
----------------------------------------------------------------------
diff --git a/app/addons/databases/tests/nightwatch/zeroclipboard.js b/app/addons/databases/tests/nightwatch/zeroclipboard.js
index 8c0c282..bdfa1d3 100644
--- a/app/addons/databases/tests/nightwatch/zeroclipboard.js
+++ b/app/addons/databases/tests/nightwatch/zeroclipboard.js
@@ -36,6 +36,7 @@ module.exports = {
       .mouseButtonDown('left')
       .mouseButtonUp('left')
       .closeNotification()
+      .clickWhenVisible('.search-autocomplete', waitTime, false)
       .setValue('.search-autocomplete', '')
       .keys([controlOrCommandKey, 'v'])
       .assert.value('.search-autocomplete', 'http://localhost:8000/_all_dbs')

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/assets/less/sidenav.less
----------------------------------------------------------------------
diff --git a/app/addons/documents/assets/less/sidenav.less b/app/addons/documents/assets/less/sidenav.less
index e75a7f6..27aa406 100644
--- a/app/addons/documents/assets/less/sidenav.less
+++ b/app/addons/documents/assets/less/sidenav.less
@@ -10,7 +10,38 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
+#sidebar-content {
+  .loading-lines {
+    margin-top: 20px;
+  }  
+}
+
 .sidenav{
+  .sidebar-toggler {
+    top: 5px;
+    position: absolute;
+    right: 7px;
+    z-index: 100;
+    width: 60px;
+    height: 35px;
+    text-align: center;
+    line-height: 35px;
+    text-decoration: none;
+    .icon {
+      padding-right: 3px;
+    }
+
+    &.sidebar-hidden {
+      &:hover {
+        background-color: rgba(153, 153, 153, 0.1);
+      }
+      width: 120px;
+      right: 210px;
+    }
+  }
+  .nav li + .nav-header {
+    margin-top: 0px;
+  }
   .nav-list > .active > a{
     text-shadow: none;
     background-color: rgba(0, 0, 0, 0.05);
@@ -159,7 +190,3 @@
     }
   }
 }
-
-
-
-

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/changes/actions.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/changes/actions.js b/app/addons/documents/changes/actions.js
index 447833c..167f13c 100644
--- a/app/addons/documents/changes/actions.js
+++ b/app/addons/documents/changes/actions.js
@@ -68,7 +68,9 @@ function (app, FauxtonAPI, ActionTypes, Stores, Helpers) {
 
       var query = $.param(params);
       var db = app.utils.safeURLName(changesStore.getDatabaseName());
-      currentRequest = $.get('/' + db + '/_changes?' + query);
+
+      var endpoint = FauxtonAPI.urls('changes', 'apiurl', db, '');
+      currentRequest = $.get(endpoint);
       currentRequest.then(_.bind(this.updateChanges, this));
     },
 

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/routes-documents.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/routes-documents.js b/app/addons/documents/routes-documents.js
index 3231c89..4d78aed 100644
--- a/app/addons/documents/routes-documents.js
+++ b/app/addons/documents/routes-documents.js
@@ -30,13 +30,14 @@ define([
   'addons/documents/pagination/pagination.react',
   'addons/documents/header/header.react',
   'addons/documents/header/header.actions',
+  'addons/documents/sidebar/actions',
   'addons/documents/designdocinfo/actions',
   'addons/documents/designdocinfo/components.react'
 ],
 
 function (app, FauxtonAPI, BaseRoute, Documents, Changes, ChangesActions, DocEditor,
   Databases, Resources, Components, PaginationStores, IndexResultsActions,
-  IndexResultsComponents, ReactPagination, ReactHeader, ReactActions,
+  IndexResultsComponents, ReactPagination, ReactHeader, ReactActions, SidebarActions,
   DesignDocInfoActions, DesignDocInfoComponents) {
 
     var DocumentsRouteObject = BaseRoute.extend({
@@ -97,7 +98,8 @@ function (app, FauxtonAPI, BaseRoute, Documents, Changes, ChangesActions, DocEdi
         });
         this.setComponent("#dashboard-lower-content", DesignDocInfoComponents.DesignDocInfo);
 
-        this.sidebar.setSelectedTab(app.utils.removeSpecialCharacters(ddoc) + "_metadata");
+        SidebarActions.setSelectedTab(app.utils.removeSpecialCharacters(ddoc) + "_metadata");
+
         this.leftheader.updateCrumbs(this.getCrumbs(this.database));
         this.rightHeader.hideQueryOptions();
 
@@ -125,12 +127,13 @@ function (app, FauxtonAPI, BaseRoute, Documents, Changes, ChangesActions, DocEdi
         this.database.buildAllDocs(docParams);
         collection = this.database.allDocs;
 
+        var tab = 'all-docs';
         if (docParams.startkey && docParams.startkey.indexOf("_design") > -1) {
-          this.sidebar.setSelectedTab("design-docs");
-        } else {
-          this.sidebar.setSelectedTab("all-docs");
+          tab = 'design-docs';
         }
 
+        SidebarActions.setSelectedTab(tab);
+
         this.removeComponent('#dashboard-upper-content');
 
         if (!docParams) {
@@ -157,11 +160,10 @@ function (app, FauxtonAPI, BaseRoute, Documents, Changes, ChangesActions, DocEdi
         this.rightHeader.showQueryOptions();
       },
 
+      //TODO: REMOVE
       reloadDesignDocs: function (event) {
-        this.sidebar.forceRender();
-
         if (event && event.selectedTab) {
-          this.sidebar.setSelectedTab(event.selectedTab);
+          SidebarActions.setSelectedTab(event.selectedTab);
         }
       },
 
@@ -177,7 +179,7 @@ function (app, FauxtonAPI, BaseRoute, Documents, Changes, ChangesActions, DocEdi
 
         this.viewEditor && this.viewEditor.remove();
 
-        this.sidebar.setSelectedTab('changes');
+        SidebarActions.setSelectedTab('changes');
         this.leftheader.updateCrumbs(this.getCrumbs(this.database));
         this.rightHeader.hideQueryOptions();
 

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/routes-mango.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/routes-mango.js b/app/addons/documents/routes-mango.js
index a8301ab..e2d827e 100644
--- a/app/addons/documents/routes-mango.js
+++ b/app/addons/documents/routes-mango.js
@@ -31,15 +31,15 @@ define([
   'addons/documents/mango/mango.components.react',
   'addons/documents/mango/mango.actions',
   'addons/documents/mango/mango.stores',
-  'addons/documents/index-results/index-results.components.react'
-
+  'addons/documents/index-results/index-results.components.react',
+  'addons/documents/sidebar/actions',
 ],
 
 
 function (app, FauxtonAPI, Helpers, BaseRoute, Databases,
   Components, Resources, Documents, IndexResultsActions, PaginationStores,
   ReactHeader, ReactActions, ReactPagination,
-  MangoComponents, MangoActions, MangoStores, IndexResultsComponents) {
+  MangoComponents, MangoActions, MangoStores, IndexResultsComponents, SidebarActions) {
 
   var MangoIndexEditorAndQueryEditor = BaseRoute.extend({
     layout: 'two_pane',
@@ -63,7 +63,6 @@ function (app, FauxtonAPI, Helpers, BaseRoute, Databases,
       this.allDatabases = this.getAllDatabases();
       this.createDesignDocsCollection();
       this.addLeftHeader();
-      this.addSidebar();
 
       MangoActions.setDatabase({
         database: this.database
@@ -89,8 +88,7 @@ function (app, FauxtonAPI, Helpers, BaseRoute, Databases,
 
       ReactActions.resetHeaderController();
 
-      // magic method
-      this.sidebar.setSelectedTab('mango-query');
+      SidebarActions.setSelectedTab('mango-query');
       this.setComponent('#react-headerbar', ReactHeader.HeaderBarController);
       this.setComponent('#footer', ReactPagination.Footer);
 

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/shared-routes.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/shared-routes.js b/app/addons/documents/shared-routes.js
index 9a59afd..2bf52b4 100644
--- a/app/addons/documents/shared-routes.js
+++ b/app/addons/documents/shared-routes.js
@@ -17,8 +17,11 @@ define([
   'addons/databases/base',
   'addons/fauxton/components',
   'addons/documents/pagination/actions',
-  'addons/documents/pagination/stores'
-], function (app, FauxtonAPI, Documents, Databases, Components, PaginationActions, PaginationStores ) {
+  'addons/documents/pagination/stores',
+  'addons/documents/sidebar/sidebar.react',
+  'addons/documents/sidebar/actions'
+], function (app, FauxtonAPI, Documents, Databases, Components, PaginationActions, PaginationStores,
+  SidebarComponents, SidebarActions) {
 
 
   // The Documents section is built up a lot of different route object which share code. This contains
@@ -99,14 +102,17 @@ define([
     },
 
     addSidebar: function (selectedTab) {
-      var params = {
-        collection: this.designDocs,
+      var options = {
+        designDocs: this.designDocs,
         database: this.database
       };
+
       if (selectedTab) {
-        params.selectedTab = selectedTab;
+        options.selectedTab = selectedTab;
       }
-      this.sidebar = this.setView("#sidebar-content", new Documents.Views.Sidebar(params));
+
+      SidebarActions.newOptions(options);
+      this.setComponent("#sidebar-content", SidebarComponents.SidebarController);
     },
 
     getCrumbs: function (database) {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/shared-views.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/shared-views.js b/app/addons/documents/shared-views.js
deleted file mode 100644
index fa14731..0000000
--- a/app/addons/documents/shared-views.js
+++ /dev/null
@@ -1,320 +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.
-
-define([
-       "app",
-
-       "api",
-       "addons/fauxton/components",
-
-       "addons/documents/resources",
-       "addons/databases/resources",
-       "css.escape"
-],
-
-function (app, FauxtonAPI, Components, Documents, Databases) {
-  var Views = {};
-
-  Views.Sidebar = FauxtonAPI.View.extend({
-    template: "addons/documents/templates/sidebar",
-    className: "sidenav",
-    tagName: "nav",
-
-    initialize: function (options) {
-      this.database = options.database;
-
-      if (options.ddocInfo) {
-        this.ddocID = options.ddocInfo.id;
-        this.currView = options.ddocInfo.currView;
-      }
-
-      this.designDocList = [];
-    },
-
-    serialize: function () {
-      var docLinks = FauxtonAPI.getExtensions('docLinks'),
-          extensionList = FauxtonAPI.getExtensions('sidebar:list'),
-          safeDatabaseName = this.database.safeID(),
-          changesLink = '#' + FauxtonAPI.urls('changes', 'app', safeDatabaseName, ''),
-          permissionsLink = '#' + FauxtonAPI.urls('permissions', 'app', safeDatabaseName),
-          databaseUrl = FauxtonAPI.urls('allDocs', 'app', safeDatabaseName, ''),
-          db_url = FauxtonAPI.urls('allDocs', 'server', safeDatabaseName, ''),
-          base = '#' + FauxtonAPI.urls('base', 'app', safeDatabaseName);
-
-      return {
-        changesUrl: changesLink,
-        permissionsUrl: permissionsLink,
-        docLinks: docLinks,
-        databaseUrl: databaseUrl,
-        base: base,
-        mangoQueryUrl: FauxtonAPI.urls('mango', 'query-app', safeDatabaseName),
-        runQueryWithMangoText: app.i18n.en_US['run-query-with-mango']
-      };
-    },
-
-    getNewButtonLinks: function () {  //these are links for the sidebar '+' on All Docs and All Design Docs
-      var database = this.collection.database,
-          addLinks = FauxtonAPI.getExtensions('sidebar:links'),
-          databaseName = database.id,
-          newUrlPrefix = '#' + FauxtonAPI.urls('databaseBaseURL', 'app', databaseName);
-
-      return _.reduce(addLinks, function (menuLinks, link) {
-
-        menuLinks.push({
-          title: link.title,
-          url: newUrlPrefix + '/' + link.url,
-          icon: 'fonticon-plus-circled'
-        });
-
-        return menuLinks;
-      }, [{
-        title: 'New Doc',
-        url: newUrlPrefix + '/new',
-        icon: 'fonticon-plus-circled'
-      }, {
-        title: 'New View',
-        url: newUrlPrefix + '/new_view',
-        icon: 'fonticon-plus-circled'
-      }, this.getMangoLink()]);
-    },
-
-    getMangoLink: function () {
-      var database = this.collection.database,
-          databaseName = database.id,
-          newUrlPrefix = '#' + FauxtonAPI.urls('databaseBaseURL', 'app', databaseName);
-
-      return {
-        title: app.i18n.en_US['new-mango-index'],
-        url: newUrlPrefix + '/_index',
-        icon: 'fonticon-plus-circled'
-      };
-    },
-
-    beforeRender: function (manage) {
-      this.deleteDBModal = this.setView(
-        '#delete-db-modal',
-        new Views.DeleteDBModal({
-          database: this.database,
-          isSystemDatabase: this.database.isSystemDatabase()
-        })
-      );
-
-      var newLinks = [{
-        title: 'Add new',
-        links: this.getNewButtonLinks()
-      }];
-
-      [
-        '#new-all-docs-button',
-        '#new-design-docs-button'
-      ].forEach(function (id) {
-        this.setView(id, new Components.MenuDropDown({
-          links: newLinks,
-        }));
-      }.bind(this));
-
-      this.setView('#mango-query-button', new Components.MenuDropDown({
-        links: [{
-          title: 'Add new',
-          links: [this.getMangoLink()]
-        }]
-      }));
-
-
-      _.each(this.designDocList, function (view) { view.remove(); view = undefined;});
-      this.designDocList = [];
-
-      this.collection.each(function (design) {
-
-        if (design.get('doc').language === 'query') {
-          return;
-        }
-
-        if (design.has('doc')) {
-          design.collection = this.collection;
-          var view = this.insertView(new Views.DdocSidenav({
-            model: design,
-            collection: this.collection
-          }));
-
-          this.designDocList.push(view);
-        }
-      }, this);
-    },
-
-    afterRender: function () {
-      if (this.selectedTab) {
-        this.setSelectedTab(this.selectedTab);
-      }
-    },
-
-    setSelectedTab: function (selectedTab) {
-      this.selectedTab = selectedTab;
-      var $selectedTab = this.$('#' + selectedTab);
-
-      this.$('li').removeClass('active');
-      $selectedTab.parent().addClass('active');
-
-      if ($selectedTab.parents(".accordion-body").length !== 0) {
-        $selectedTab
-        .parents(".accordion-body")
-        .addClass("in")
-        .parents(".nav-header")
-        .find(">.js-collapse-toggle").addClass("down");
-
-        this.$('.js-toggle-' + $selectedTab.data('ddoctype')).addClass("down");
-      }
-    }
-  });
-
-  Views.DdocSidenav = FauxtonAPI.View.extend({
-    tagName: "ul",
-    className:  "nav nav-list",
-    template: "addons/documents/templates/design_doc_menu",
-    events: {
-      "click .js-collapse-toggle": "toggleArrow"
-    },
-
-    toggleArrow: function (e) {
-      var escapedId = window.CSS.escape(e.currentTarget.id),
-          $toggleElement = this.$('#' + escapedId).next();
-
-      this.$('#' + escapedId).toggleClass('down');
-      $toggleElement.collapse('toggle');
-    },
-
-    buildIndexList: function (designDocs, info) {
-      var design = this.model.id.replace(/^_design\//, "");
-      var databaseId = this.model.database.id;
-
-      if (_.isUndefined(designDocs[info.selector])) { return; }
-
-      this.insertView(".accordion-body", new Views.IndexItem({
-        selector: info.selector,
-        ddoc: design,
-        collection: designDocs[info.selector],
-        name: info.name,
-        database: databaseId
-      }));
-    },
-
-    serialize: function () {
-      var ddocName = this.model.id.replace(/^_design\//, ""),
-          escapedId = window.CSS.escape(ddocName),
-          docSafe = app.utils.safeURLName(ddocName),
-          databaseName = this.collection.database.safeID();
-
-      return {
-        designDocMetaUrl: FauxtonAPI.urls('designDocs', 'app', databaseName, docSafe),
-        designDoc: ddocName,
-        escapedId: escapedId
-      };
-    },
-
-    getSidebarLinks: function () {  //these are for each Design doc '+' links
-      var ddocName = this.model.id.replace(/^_design\//, ""),
-          docSafe = app.utils.safeURLName(ddocName),
-          databaseName = this.collection.database.id,
-          newUrlPrefix = FauxtonAPI.urls('databaseBaseURL', 'app', databaseName);
-
-
-      return _.reduce(FauxtonAPI.getExtensions('sidebar:links'), function (menuLinks, link) {
-        menuLinks.push({
-          title: link.title,
-          url: '#' + newUrlPrefix + '/' + link.url + '/' + docSafe,
-          icon: 'fonticon-plus-circled'
-        });
-
-        return menuLinks;
-      }, [{
-        title: 'New View',
-        url: '#' + FauxtonAPI.urls('new', 'addView', databaseName, docSafe),
-        icon: 'fonticon-plus-circled'
-      }]);
-
-    },
-
-    renderIndexLists: function () {
-      var ddocDocs = this.model.get("doc"),
-          sidebarListTypes = FauxtonAPI.getExtensions('sidebar:list');
-
-      if (!ddocDocs) { return; }
-
-      this.buildIndexList(ddocDocs, {
-        selector: "views",
-        name: 'Views'
-      });
-
-      _.each(sidebarListTypes, function (info) {
-        this.buildIndexList(ddocDocs, info);
-      }, this);
-
-    },
-
-    beforeRender: function (manage) {
-      var sideBarMenuLinks = [];
-
-      sideBarMenuLinks.push({
-        title: 'Add new',
-        links: this.getSidebarLinks()
-      });
-
-      this.renderIndexLists();
-      this.setView(".new-button", new Components.MenuDropDown({
-        links: sideBarMenuLinks,
-      }));
-    }
-  });
-
-  Views.IndexItem = FauxtonAPI.View.extend({
-    template: "addons/documents/templates/index_menu_item",
-    tagName: 'li',
-
-    initialize: function (options) {
-      this.index = options.index;
-      this.ddoc = options.ddoc;
-      this.database = options.database;
-      this.selected = !! options.selected;
-      this.selector = options.selector;
-      this.name = options.name;
-
-      this.indexTypeMap = {
-        views:   { icon: 'fonticon-sidenav-map-reduce', urlFolder: '_view', type: 'view' },
-        indexes: { icon: 'fonticon-sidenav-search', urlFolder: '_indexes', type: 'search' }
-      };
-    },
-
-    serialize: function () {
-      return {
-        icon: this.indexTypeMap[this.selector].icon,
-        urlFolder: this.indexTypeMap[this.selector].urlFolder,
-        ddocType:  this.selector,
-        name: this.name,
-        index: this.index,
-        ddoc: this.ddoc,
-        database: this.database,
-        selected: this.selected,
-        collection: this.collection,
-        href: FauxtonAPI.urls(this.indexTypeMap[this.selector].type, 'app', this.database, this.ddoc)
-      };
-    },
-
-    afterRender: function () {
-      if (this.selected) {
-        $(".sidenav ul.nav-list li").removeClass("active");
-        this.$el.addClass("active");
-      }
-    }
-  });
-
-  return Views;
-});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/sidebar/actions.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/sidebar/actions.js b/app/addons/documents/sidebar/actions.js
new file mode 100644
index 0000000..ca84585
--- /dev/null
+++ b/app/addons/documents/sidebar/actions.js
@@ -0,0 +1,52 @@
+// 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.
+
+define([
+  'app',
+  'api',
+  'addons/documents/sidebar/actiontypes',
+  'addons/documents/sidebar/stores'
+],
+function (app, FauxtonAPI, ActionTypes, Stores) {
+  var store = Stores.sidebarStore;
+  return {
+    newOptions: function (options) {
+      if (options.database.safeID() !== store.getDatabaseName()) {
+        FauxtonAPI.dispatch({
+          type: ActionTypes.SIDEBAR_FETCHING
+        });
+      }
+
+      options.designDocs.fetch().then(function () {
+        FauxtonAPI.dispatch({
+          type: ActionTypes.SIDEBAR_NEW_OPTIONS,
+          options: options
+        });
+      });
+    },
+
+    toggleContent: function (designDoc, index) {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.SIDEBAR_TOGGLE_CONTENT,
+        designDoc: designDoc,
+        index: index
+      });
+    },
+
+    setSelectedTab: function (tab) {
+      FauxtonAPI.dispatch({
+        type: ActionTypes.SIDEBAR_SET_SELECTED_TAB,
+        tab: tab
+      });
+    }
+  };
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/sidebar/actiontypes.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/sidebar/actiontypes.js b/app/addons/documents/sidebar/actiontypes.js
new file mode 100644
index 0000000..c1d6af5
--- /dev/null
+++ b/app/addons/documents/sidebar/actiontypes.js
@@ -0,0 +1,20 @@
+// 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.
+
+define([], function () {
+  return {
+    SIDEBAR_SET_SELECTED_TAB: 'SIDEBAR_SET_SELECTED_TAB',
+    SIDEBAR_NEW_OPTIONS: 'SIDEBAR_NEW_OPTIONS',
+    SIDEBAR_TOGGLE_DESIGN_DOC: 'SIDEBAR_TOGGLE_CONTENT',
+    SIDEBAR_FETCHING: 'SIDEBAR_FETCHING'
+  };
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/sidebar/sidebar.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/sidebar/sidebar.react.jsx b/app/addons/documents/sidebar/sidebar.react.jsx
new file mode 100644
index 0000000..70e4f53
--- /dev/null
+++ b/app/addons/documents/sidebar/sidebar.react.jsx
@@ -0,0 +1,448 @@
+// 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.
+
+define([
+  'app',
+  'api',
+  'react',
+  'addons/documents/sidebar/stores',
+  'addons/documents/sidebar/actions',
+  'addons/components/react-components.react',
+  'addons/documents/views',
+
+  'plugins/prettify'
+],
+
+function (app, FauxtonAPI, React, Stores, Actions, Components, DocumentViews) {
+  var DeleteDBModal = DocumentViews.Views.DeleteDBModal;
+  var store = Stores.sidebarStore;
+  var LoadLines = Components.LoadLines;
+
+  var SidebarToggle = React.createClass({
+    getInitialState: function () {
+      return {
+        hidden: false
+      };
+    },
+
+    toggle: function (e) {
+      e.preventDefault();
+      var newHiddenState = !this.state.hidden;
+      this.setState({hidden: newHiddenState});
+      var $dashboard = $('#dashboard-content');
+
+      if (newHiddenState) {
+        $dashboard.animate({left: '210px'}, 300);
+      } else {
+        $dashboard.animate({left: '550px'}, 300);
+      }
+
+    },
+
+    render: function () {
+      return null;
+    }
+
+  });
+
+  var MainSidebar = React.createClass({
+
+    getNewButtonLinks: function () {  //these are links for the sidebar '+' on All Docs and All Design Docs
+      var addLinks = FauxtonAPI.getExtensions('sidebar:links');
+      var databaseName = this.props.databaseName;
+      var newUrlPrefix = '#' + FauxtonAPI.urls('databaseBaseURL', 'app', databaseName);
+
+      var addNewLinks = _.reduce(addLinks, function (menuLinks, link) {
+        menuLinks.push({
+          title: link.title,
+          url: newUrlPrefix + '/' + link.url,
+          icon: 'fonticon-plus-circled'
+        });
+
+        return menuLinks;
+      }, [{
+        title: 'New Doc',
+        url: newUrlPrefix + '/new',
+        icon: 'fonticon-plus-circled'
+      }, {
+        title: 'New View',
+        url: newUrlPrefix + '/new_view',
+        icon: 'fonticon-plus-circled'
+      }, this.getMangoLink()]);
+
+      return [{
+        title: 'Add new',
+        links: addNewLinks
+      }];
+    },
+
+    getMangoLink: function () {
+      var databaseName = this.props.databaseName;
+      var newUrlPrefix = '#' + FauxtonAPI.urls('databaseBaseURL', 'app', databaseName);
+
+      return {
+        title: app.i18n.en_US['new-mango-index'],
+        url: newUrlPrefix + '/_index',
+        icon: 'fonticon-plus-circled'
+      };
+    },
+
+    buildDocLinks: function () {
+      var base = FauxtonAPI.urls('base', 'app', this.props.databaseName);
+      var isActive = this.props.isActive;
+
+      return FauxtonAPI.getExtensions('docLinks').map(function (link) {
+        return (
+          <li key={link.url} className={isActive(link.url)}>
+            <a id={link.url} href={base + link.url}>{link.title}</a>
+          </li>
+        );
+
+      });
+    },
+
+    render: function () {
+      var isActive = this.props.isActive;
+      var docLinks = this.buildDocLinks();
+      var changesUrl = '#' + FauxtonAPI.urls('changes', 'app', this.props.databaseName, '');
+      var permissionsUrl = '#' + FauxtonAPI.urls('permissions', 'app', this.props.databaseName);
+      var databaseUrl = FauxtonAPI.urls('allDocs', 'app', this.props.databaseName, '');
+      var mangoQueryUrl = FauxtonAPI.urls('mango', 'query-app', this.props.databaseName);
+      var runQueryWithMangoText = app.i18n.en_US['run-query-with-mango'];
+      var buttonLinks = this.getNewButtonLinks();
+
+      return (
+        <ul className="nav nav-list">
+          <li className={isActive('permissions')}>
+            <a id="permissions" href={permissionsUrl}>Permissions</a>
+          </li>
+          <li className={isActive('changes')}>
+            <a id="changes" href={changesUrl}>Changes</a>
+          </li>
+          {docLinks}
+          <li className={isActive('all-docs')}>
+            <a id="all-docs"
+              href={"#/" + databaseUrl}
+              className="toggle-view">
+              All Documents
+            </a>
+            <div id="new-all-docs-button" className="add-dropdown">
+              <Components.MenuDropDown links={buttonLinks} />
+            </div>
+           </li>
+          <li className={isActive('mango-query')}>
+            <a
+              id="mango-query"
+              href={'#/' + mangoQueryUrl}
+              className="toggle-view">
+              {runQueryWithMangoText}
+            </a>
+            <div id="mango-query-button" className="add-dropdown">
+              <Components.MenuDropDown links={buttonLinks} />
+            </div>
+          </li>
+          <li className={isActive('design-docs')}>
+            <a
+              id="design-docs"
+              href={"#/" + databaseUrl + '?startkey="_design"&endkey="_design0"'}
+              className="toggle-view">
+              All Design Docs
+            </a>
+            <div id="new-design-docs-button" className="add-dropdown">
+              <Components.MenuDropDown links={buttonLinks} />
+            </div>
+          </li>
+        </ul>
+      );
+    }
+
+  });
+
+  var IndexSection = React.createClass({
+
+    getDefaultProps: function () {
+      return {
+        indexTypeMap: {
+          views:   { icon: 'fonticon-sidenav-map-reduce', urlFolder: '_view', type: 'view' },
+          indexes: { icon: 'fonticon-sidenav-search', urlFolder: '_indexes', type: 'search' }
+        }
+      };
+    },
+
+    createItems: function () {
+      return _.map(this.props.items, function (index, key) {
+        var href = FauxtonAPI.urls(this.props.indexTypeMap[this.props.selector].type, 'app', this.props.databaseName, this.props.designDocName);
+        return (
+          <li key={key}>
+          <a
+            id={this.props.designDocName + '_' + index}
+            href={"#/" + href + index}
+            className="toggle-view">
+            {index}
+          </a>
+          </li>
+        );
+      }, this);
+    },
+
+    toggle: function (e) {
+      e.preventDefault();
+      var newToggleState = !this.props.contentVisible;
+      var state = newToggleState ? 'show' : 'hide';
+      $(this.getDOMNode()).find('.accordion-body').collapse(state);
+      this.props.toggle(this.props.designDocName, this.props.title);
+    },
+
+    render: function () {
+      var toggleClassNames = 'accordion-header';
+      var toggleBodyClassNames = 'accordion-body collapse';
+      if (this.props.contentVisible) {
+        toggleClassNames += ' down';
+        toggleBodyClassNames += ' in';
+      }
+
+      var title = this.props.title;
+      var icon = this.props.indexTypeMap[this.props.selector].icon;
+      var designDocName = this.props.designDocName;
+      var linkId = "nav-design-function-" + designDocName + this.props.selector;
+      return (
+        <li id={linkId} onClick={this.toggle}>
+          <a className={toggleClassNames} data-toggle="collapse">
+            <div className="fonticon-play"></div>
+            <span className={icon + " fonticon"}></span>
+            {title}
+            </a>
+            <ul className={toggleBodyClassNames}>
+              {this.createItems()}
+          </ul>
+        </li>
+      );
+    }
+
+  });
+
+  var DesignDoc = React.createClass({
+
+    createIndexList: function () {
+      var sidebarListTypes = FauxtonAPI.getExtensions('sidebar:list');
+
+      if (_.isEmpty(sidebarListTypes) ||
+        (_.has(sidebarListTypes[0], 'selector') && sidebarListTypes[0].selector !== 'views')) {
+        sidebarListTypes.unshift({
+          selector: 'views',
+          name: 'Views'
+        });
+      }
+
+      return _.map(sidebarListTypes, function (index, key) {
+        return <IndexSection
+          contentVisible={this.props.isVisible(this.props.designDocName, index.name)}
+          toggle={this.props.toggle}
+          databaseName={this.props.databaseName}
+          designDocName={this.props.designDocName}
+          key={key}
+          title={index.name}
+          selector={index.selector}
+          items={_.keys(this.props.designDoc[index.selector])} />;
+      }.bind(this));
+    },
+
+    toggle: function (e) {
+      e.preventDefault();
+      var newToggleState = !this.props.contentVisible;
+      var state = newToggleState ? 'show' : 'hide';
+      $(this.getDOMNode()).find('#' + this.props.designDocName).collapse(state);
+      this.props.toggle(this.props.designDocName);
+    },
+
+    getNewButtonLinks: function () {
+      var databaseName = this.props.databaseName;
+      var newUrlPrefix = FauxtonAPI.urls('databaseBaseURL', 'app', databaseName);
+      var designDocName = this.props.designDocName;
+
+      var addNewLinks = _.reduce(FauxtonAPI.getExtensions('sidebar:links'), function (menuLinks, link) {
+        menuLinks.push({
+          title: link.title,
+          url: '#' + newUrlPrefix + '/' + link.url + '/' + designDocName,
+          icon: 'fonticon-plus-circled'
+        });
+
+        return menuLinks;
+      }, [{
+        title: 'New View',
+        url: '#' + FauxtonAPI.urls('new', 'addView', databaseName, designDocName),
+        icon: 'fonticon-plus-circled'
+      }]);
+
+      return [{
+        title: 'Add new',
+        links: addNewLinks
+      }];
+    },
+
+    render: function () {
+      var buttonLinks = this.getNewButtonLinks();
+      var toggleClassNames = 'accordion-header';
+      var toggleBodyClassNames = 'accordion-body collapse';
+
+      if (this.props.contentVisible) {
+        toggleClassNames += ' down';
+        toggleBodyClassNames += ' in';
+      }
+      var designDocName = this.props.designDocName;
+      var designDocMetaUrl = FauxtonAPI.urls('designDocs', 'app', this.props.databaseName, designDocName);
+      return (
+        <li  className="nav-header">
+
+        <div id={"sidebar-tab-" + designDocName} className={toggleClassNames}>
+          <div id={"nav-header-" + designDocName} onClick={this.toggle} className='accordion-list-item'>
+            <div className='fonticon-play'></div>
+            <p className='design-doc-name'>
+              <span title={'_design/' + designDocName}>{'_design/' + designDocName}</span>
+            </p>
+          </div>
+          <div className='new-button add-dropdown'>
+            <Components.MenuDropDown links={buttonLinks} />
+          </div>
+        </div>
+        <ul className={toggleBodyClassNames} id={this.props.designDocName}>
+          <li>
+            <a href={"#/" + designDocMetaUrl} className="toggle-view accordion-header">
+              <span className="fonticon-sidenav-info fonticon"></span>
+              Design Doc Metadata
+            </a>
+          </li>
+          {this.createIndexList()}
+        </ul>
+        </li>
+      );
+    }
+
+  });
+
+  var DesignDocList = React.createClass({
+    createDesignDocs: function () {
+      return _.map(this.props.designDocs, function (designDoc, key) {
+        return <DesignDoc
+          toggle={this.props.toggle}
+          contentVisible={this.props.isVisible(designDoc.safeId)}
+          isVisible={this.props.isVisible}
+          key={key}
+          designDoc={designDoc}
+          designDocName={designDoc.safeId}
+          databaseName={this.props.databaseName} />;
+      }.bind(this));
+    },
+
+    render: function () {
+      var designDocName = this.props.designDocName;
+      var designDocMetaUrl = FauxtonAPI.urls('designDocs', 'app', this.props.databaseName, designDocName);
+
+      return (
+        <ul className="nav nav-list">
+          {this.createDesignDocs()}
+        </ul>
+      );
+    }
+
+  });
+
+  var DeleteDBModalWrapper = React.createClass({
+
+    componentDidMount: function () {
+      this.dbModal = new DeleteDBModal({
+        database: this.props.database,
+        isSystemDatabase: (/^_/).test(this.props.database.id),
+        el: this.getDOMNode()
+      });
+
+      this.dbModal.render();
+    },
+
+    componentWillUnmount: function () {
+      this.dbModal.remove();
+    },
+
+    componentWillReceiveProps: function (newProps) {
+      this.dbModal.database = newProps.database;
+      this.dbModal.isSystemDatabase = newProps.database.isSystemDatabase();
+    },
+
+    render: function () {
+      return <div id="delete-db-modal"> </div>;
+    }
+
+  });
+
+  var SidebarController = React.createClass({
+    getStoreState: function () {
+      return {
+        databaseName: store.getDatabaseName(),
+        selectedTab: store.getSelectedTab(),
+        designDocs: store.getDesignDocs(),
+        isVisible: _.bind(store.isVisible, store),
+        isLoading: store.isLoading(),
+        database: store.getDatabase()
+      };
+    },
+
+    isActive: function (id) {
+      if (id === this.state.selectedTab) {
+        return 'active';
+      }
+
+      return '';
+    },
+
+    getInitialState: function () {
+      return this.getStoreState();
+    },
+
+    componentDidMount: function () {
+      store.on('change', this.onChange, this);
+    },
+
+    componentWillUnmount: function () {
+      store.off('change', this.onChange);
+    },
+
+    onChange: function () {
+      this.setState(this.getStoreState());
+    },
+
+
+    render: function () {
+      if (this.state.isLoading) {
+        return <LoadLines />;
+      }
+
+      return (
+        <nav className="sidenav">
+          <SidebarToggle />
+          <MainSidebar isActive={this.isActive} databaseName={this.state.databaseName} />
+          <DesignDocList
+            toggle={Actions.toggleContent}
+            isVisible={this.state.isVisible}
+            designDocs={this.state.designDocs}
+            databaseName={this.state.databaseName} />
+          <DeleteDBModalWrapper database={this.state.database}/>
+        </nav>
+      );
+    }
+  });
+
+  var Views = {
+    SidebarController: SidebarController
+  };
+
+  return Views;
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/sidebar/stores.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/sidebar/stores.js b/app/addons/documents/sidebar/stores.js
new file mode 100644
index 0000000..0208257
--- /dev/null
+++ b/app/addons/documents/sidebar/stores.js
@@ -0,0 +1,153 @@
+// 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.
+
+define([
+  'app',
+  'api',
+  'addons/documents/sidebar/actiontypes'
+],
+
+function (app, FauxtonAPI, ActionTypes) {
+  var Stores = {};
+
+  Stores.SidebarStore = FauxtonAPI.Store.extend({
+
+    initialize: function () {
+      this._selectedTab = 'all-docs';
+      this._loading = true;
+      this._toggledSections = {};
+    },
+
+    newOptions: function (options) {
+      this._database = options.database;
+      this._designDocs = options.designDocs;
+      this._loading = false;
+
+      if (options.selectedTab) {
+        this.setSelectedTab(options.selectedTab);
+      }
+    },
+
+    isLoading: function () {
+      return this._loading;
+    },
+
+    getDatabase: function () {
+      if (this.isLoading()) {return {};}
+
+      return this._database;
+    },
+
+    toggleContent: function (designDoc, index) {
+      if (!this._toggledSections[designDoc]) {
+        this._toggledSections[designDoc] = {
+          visible: true,
+          indexes: {}
+        };
+        return;
+      }
+
+      if (index) {
+        return this.toggleIndex(designDoc, index);
+      }
+
+      this._toggledSections[designDoc].visible = !this._toggledSections[designDoc].visible;
+    },
+
+    toggleIndex: function (designDoc, indexName) {
+      var index = this._toggledSections[designDoc].indexes[indexName];
+
+      if (_.isUndefined(index)) {
+        this._toggledSections[designDoc].indexes[indexName] = true;
+        return;
+      }
+
+      this._toggledSections[designDoc].indexes[indexName] = !index;
+    },
+
+    isVisible: function (designDoc, index) {
+      if (!this._toggledSections[designDoc]) {
+        return false;
+      }
+
+      if (index) {
+        return this._toggledSections[designDoc].indexes[index];
+      }
+
+      return this._toggledSections[designDoc].visible;
+    },
+
+    setSelectedTab: function (tab) {
+      this._selectedTab = tab;
+    },
+
+    getDatabaseName: function () {
+      if (this.isLoading()) { return '';}
+
+      return this._database.safeID();
+    },
+
+    getDesignDocs: function () {
+      if (this.isLoading()) { return {};}
+      var docs = this._designDocs.toJSON();
+
+      docs = _.filter(docs, function (doc) {
+        if (_.has(doc.doc, 'language')) {
+          return doc.doc.language !== 'query';
+        }
+        return true;
+      });
+
+      return docs.map(function (doc) {
+        doc.safeId = app.utils.safeURLName(doc._id.replace(/^_design\//, ""));
+
+        return _.extend(doc, doc.doc);
+      });
+    },
+
+    getSelectedTab: function () {
+      return this._selectedTab;
+    },
+
+    dispatch: function (action) {
+      switch (action.type) {
+        case ActionTypes.SIDEBAR_SET_SELECTED_TAB:
+          this.setSelectedTab(action.tab);
+          this.triggerChange();
+        break;
+        case ActionTypes.SIDEBAR_NEW_OPTIONS:
+          this.newOptions(action.options);
+          this.triggerChange();
+        break;
+        case ActionTypes.SIDEBAR_TOGGLE_CONTENT:
+          this.toggleContent(action.designDoc, action.index);
+          this.triggerChange();
+        break;
+        case ActionTypes.SIDEBAR_FETCHING:
+          this._loading = true;
+          this.triggerChange();
+        break;
+        default:
+        return;
+        // do nothing
+      }
+    }
+
+  });
+
+  Stores.sidebarStore = new Stores.SidebarStore();
+
+  Stores.sidebarStore.dispatchToken = FauxtonAPI.dispatcher.register(Stores.sidebarStore.dispatch);
+
+  return Stores;
+
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/sidebar/tests/sidebar.storesSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/sidebar/tests/sidebar.storesSpec.js b/app/addons/documents/sidebar/tests/sidebar.storesSpec.js
new file mode 100644
index 0000000..dbcbee2
--- /dev/null
+++ b/app/addons/documents/sidebar/tests/sidebar.storesSpec.js
@@ -0,0 +1,79 @@
+// 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.
+
+define([
+  'api',
+  'addons/documents/sidebar/stores',
+  'addons/documents/sidebar/actiontypes',
+  'testUtils'
+], function (FauxtonAPI, Stores, ActionTypes, testUtils) {
+  var assert = testUtils.assert;
+  var dispatchToken;
+  var store;
+  var opts;
+
+  describe('Sidebar Store', function () {
+    beforeEach(function () {
+      store = new Stores.SidebarStore();
+      dispatchToken = FauxtonAPI.dispatcher.register(store.dispatch);
+    });
+
+    afterEach(function () {
+      FauxtonAPI.dispatcher.unregister(dispatchToken);
+    });
+
+    describe('toggle state', function () {
+
+      it('should not be visible if never toggled', function () {
+        assert.notOk(store.isVisible('designDoc'));
+      });
+
+      it('should be visible after being toggled', function () {
+        var designDoc = 'designDoc';
+        store.toggleContent(designDoc);
+        assert.ok(store.isVisible(designDoc));
+      });
+
+      it('should not be visible after being toggled twice', function () {
+        var designDoc = 'designDoc';
+        store.toggleContent(designDoc);
+        store.toggleContent(designDoc);
+        assert.notOk(store.isVisible(designDoc));
+      });
+
+    });
+
+    describe('toggle state for index', function () {
+      var designDoc = 'design-doc';
+
+      beforeEach(function () {
+        store.toggleContent(designDoc);
+      });
+
+      it('should be hidden if never toggled', function () {
+        assert.notOk(store.isVisible(designDoc, 'index'));
+      });
+
+      it('should be if toggled', function () {
+        store.toggleContent(designDoc, 'index');
+        assert.ok(store.isVisible(designDoc, 'index'));
+      });
+
+      it('should be hidden after being toggled twice', function () {
+        store.toggleContent(designDoc, 'index');
+        store.toggleContent(designDoc, 'index');
+        assert.notOk(store.isVisible(designDoc, 'index'));
+      });
+
+    });
+  });
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/templates/design_doc_menu.html
----------------------------------------------------------------------
diff --git a/app/addons/documents/templates/design_doc_menu.html b/app/addons/documents/templates/design_doc_menu.html
deleted file mode 100644
index 4a0b6a9..0000000
--- a/app/addons/documents/templates/design_doc_menu.html
+++ /dev/null
@@ -1,32 +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.
-*/%>
-<li class="nav-header">
-
-<div class="js-collapse-toggle accordion-header" id="nav-header-<%- escapedId %>" >
-  <div class="accordion-list-item">
-    <div class="fonticon-play"></div>
-    <p class="design-doc-name"><span title="_design/<%- designDoc%>">_design/<%- designDoc%></span></p>
-  </div>
-  <div class="new-button add-dropdown"></div>
-</div>
-<ul class="accordion-body collapse <%- escapedId %>" id="<%- escapedId %>">
-  <li>
-  <a id="<%- escapedId %>_metadata" href="#/<%- designDocMetaUrl %>" class="toggle-view accordion-header">
-    <span class="fonticon-sidenav-info fonticon"></span>
-    Design Doc Metadata
-  </a>
-  </li>
-
-</ul>
-</li>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/templates/index_menu_item.html
----------------------------------------------------------------------
diff --git a/app/addons/documents/templates/index_menu_item.html b/app/addons/documents/templates/index_menu_item.html
deleted file mode 100644
index da055b8..0000000
--- a/app/addons/documents/templates/index_menu_item.html
+++ /dev/null
@@ -1,31 +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.
-*/%>
-<a class="js-toggle-<%-ddocType%> accordion-header js-collapse-toggle" data-toggle="collapse"  data-target="#<%- removeSpecialCharacters(ddoc) + ddocType %>" id="nav-design-function-<%= removeSpecialCharacters(ddoc) + ddocType %>">
-  <div class="fonticon-play"></div>
-  <span class="<%- icon %> fonticon"></span>
-  <%- name %>
-  </a>
-  <ul class="accordion-body collapse" id="<%- removeSpecialCharacters(ddoc) + ddocType %>">
-  <% _.each(collection, function (item, index) { %>
-  <li>
-  <a
-    data-ddoctype="<%- ddocType %>"
-    id="<%- removeSpecialCharacters(ddoc) %>_<%- removeSpecialCharacters(index) %>"
-    href="#/<%- href%><%- index%>"
-    class="toggle-view">
-    <%- index %>
-  </a>
-  </li>
-  <% }); %>
-</ul>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/templates/sidebar.html
----------------------------------------------------------------------
diff --git a/app/addons/documents/templates/sidebar.html b/app/addons/documents/templates/sidebar.html
deleted file mode 100644
index 6d6a388..0000000
--- a/app/addons/documents/templates/sidebar.html
+++ /dev/null
@@ -1,50 +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.
-*/%>
-
-<ul class="nav nav-list">
-  <li><a id="permissions" href="<%- permissionsUrl %>">Permissions</a><li>
-  <li><a id="changes" href="<%- changesUrl %>">Changes</a><li>
-  <% _.each(docLinks, function (link) { %>
-    <li><a id="docLink_<%-link.url%>" href="<%- base + link.url %>"><%- link.title %></a></li>
-  <% }); %>
-  <li class="active"> 
-    <a
-      id="all-docs"
-      href="#/<%- databaseUrl %>"
-      class="toggle-view">
-      All Documents
-    </a>
-    <div id="new-all-docs-button" class="add-dropdown"> </div>
-   </li>
-  <li>
-    <a
-      id="mango-query"
-      href='#/<%- mangoQueryUrl %>'
-      class="toggle-view">
-      <%- runQueryWithMangoText %>
-    </a>
-    <div id="mango-query-button" class="add-dropdown"> </div>
-  </li>
-  <li>
-    <a
-      id="design-docs"
-      href='#/<%- databaseUrl %>?startkey="_design"&endkey="_design0"'
-      class="toggle-view">
-      All Design Docs
-    </a>
-    <div id="new-design-docs-button" class="add-dropdown"> </div>
-  </li>
-</ul>
-
-<div id="delete-db-modal"> </div>

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/tests/nightwatch/switchDatabaseViaLookaheadTray.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/nightwatch/switchDatabaseViaLookaheadTray.js b/app/addons/documents/tests/nightwatch/switchDatabaseViaLookaheadTray.js
index da297cf..5aa49fe 100644
--- a/app/addons/documents/tests/nightwatch/switchDatabaseViaLookaheadTray.js
+++ b/app/addons/documents/tests/nightwatch/switchDatabaseViaLookaheadTray.js
@@ -27,10 +27,11 @@ module.exports = {
       .url(baseUrl + '/#/database/' + newDatabaseName + '/_all_docs')
       .clickWhenVisible('#breadcrumbs .lookahead-tray-link')
       .setValue('#breadcrumbs .search-autocomplete', [secondDatabaseName, client.Keys.ENTER])
+      .waitForElementVisible('#breadcrumbs .lookahead-tray-link', waitTime, false)
       .getText('body', function (result) {
 
         // check the breadcrumb title is now the second database name. That indicates a successful redirect
-        client.assert.containsText("#breadcrumbs .lookahead-tray-link", secondDatabaseName);
+        client.assert.containsText('#breadcrumbs .lookahead-tray-link', secondDatabaseName);
       })
 
       .deleteDatabase(secondDatabaseName)

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/tests/nightwatch/viewCreate.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/nightwatch/viewCreate.js b/app/addons/documents/tests/nightwatch/viewCreate.js
index 2433e7c..bc6094e 100644
--- a/app/addons/documents/tests/nightwatch/viewCreate.js
+++ b/app/addons/documents/tests/nightwatch/viewCreate.js
@@ -93,7 +93,12 @@ module.exports = {
     var newDatabaseName = client.globals.testDatabaseName;
     /*jshint multistr: true */
 
-    openDifferentDropdownsAndClick(client, '#nav-header-testdesigndoc')
+    client
+      .loginToGUI()
+      .populateDatabase(newDatabaseName)
+      .url(baseUrl + '/#/database/' + newDatabaseName + '/_all_docs')
+      .clickWhenVisible('#sidebar-tab-testdesigndoc a.dropdown-toggle.icon.fonticon-plus-circled', waitTime, false)
+      .clickWhenVisible('#sidebar-tab-testdesigndoc a[href*="new_view"]', waitTime, false)
       .waitForElementVisible('#index-name', waitTime, false)
       .clearValue('#index-name')
       .setValue('#index-name', 'test-new-view')
@@ -111,8 +116,8 @@ module.exports = {
       //go back to all docs
       .url(baseUrl + '/#/database/' + newDatabaseName + '/_all_docs')
       .clickWhenVisible('#nav-header-testdesigndoc', waitTime, false)
-      .clickWhenVisible('[data-target="#testdesigndocviews"]', waitTime, false)
-      .clickWhenVisible('#testdesigndoc_testnewview', waitTime, false)
+      .clickWhenVisible('#nav-design-function-testdesigndocviews', waitTime, false)
+      .clickWhenVisible('#testdesigndoc_test-new-view', waitTime, false)
       .waitForElementPresent('.prettyprint', waitTime, false)
       .waitForElementNotPresent('.loading-lines', waitTime, false)
       .assert.containsText('.prettyprint', 'enteente')

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/app/addons/documents/views.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/views.js b/app/addons/documents/views.js
index d0cdb5b..13aaeca 100644
--- a/app/addons/documents/views.js
+++ b/app/addons/documents/views.js
@@ -18,7 +18,6 @@ define([
   "addons/databases/resources",
 
   // Views
-  "addons/documents/shared-views",
   "addons/documents/views-queryoptions",
 
   //plugins
@@ -26,7 +25,9 @@ define([
 ],
 
 function (app, FauxtonAPI, Components, Documents,
-  Databases, Views, QueryOptions) {
+  Databases, QueryOptions) {
+
+  var Views = {};
 
   function showError (msg) {
     FauxtonAPI.addNotification({

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/assets/less/fauxton.less
----------------------------------------------------------------------
diff --git a/assets/less/fauxton.less b/assets/less/fauxton.less
index 4121f16..a51b783 100644
--- a/assets/less/fauxton.less
+++ b/assets/less/fauxton.less
@@ -231,8 +231,7 @@ table.databases {
     .divider {
       border: none;
     }
-    > li > a:hover,
-    > li > a:focus{
+    > li > a:hover{
       background-color: @linkColorHover;
       color: #fff;
     }

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/9d7cd442/assets/less/templates.less
----------------------------------------------------------------------
diff --git a/assets/less/templates.less b/assets/less/templates.less
index 98f295f..7b3e887 100644
--- a/assets/less/templates.less
+++ b/assets/less/templates.less
@@ -320,7 +320,7 @@ with_tabs_sidebar.html
     display: block;
   }
   margin-top: 6px;
-  
+
   .nav-list:last-child {
     margin-bottom: 30px;
   }
@@ -453,8 +453,7 @@ with_tabs_sidebar.html
     .divider {
       border: none;
     }
-    > li > a:hover,
-    > li > a:focus {
+    > li > a:hover{
       background-color: @linkColorHover;
       color: #fff;
     }