Repository: couchdb-fauxton
Updated Branches:
  refs/heads/master f61efde4b -> 8a7a0f9d2


Sidebar component updates

This makes a couple of changes to the DesignDoc and IndexSection
components used in the sidebar.

1. `<IndexSection />` knew too much! It had some hardcoded
references to the structure (prop names) of the design docs and
how it was being called. Now it’s more generic and passed
everything it needs to know via props, so it’s a little bit
dumber + will work for any new type of design doc contents.

2. The design doc item in the sidebar will now only list
Search Indexes or other subsections if they have content. This
won’t impact Fauxton as is, but for script that wrap this
code, it’ll cut down on visual clutter in the UI. [This is
mostly for UX.]


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

Branch: refs/heads/master
Commit: 8a7a0f9d209310a5520cfc48a138a7a3ae357735
Parents: f61efde
Author: Ben Keen <ben.k...@gmail.com>
Authored: Fri Jan 8 17:34:01 2016 -0800
Committer: Ben Keen <ben.k...@gmail.com>
Committed: Tue Jan 12 09:24:28 2016 -0800

----------------------------------------------------------------------
 app/addons/documents/sidebar/sidebar.react.jsx  | 71 ++++++++++-----
 .../tests/sidebar.componentsSpec.react.jsx      | 93 ++++++++++++++++++++
 2 files changed, 142 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/8a7a0f9d/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
index fba0acd..7a7abd7 100644
--- a/app/addons/documents/sidebar/sidebar.react.jsx
+++ b/app/addons/documents/sidebar/sidebar.react.jsx
@@ -142,18 +142,18 @@ function (app, FauxtonAPI, React, Stores, Actions, 
Components, DocumentViews) {
 
   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' }
-        }
-      };
+    propTypes: {
+      urlNamespace: React.PropTypes.string.isRequired,
+      icon: React.PropTypes.string.isRequired,
+      databaseName: React.PropTypes.string.isRequired,
+      designDocName: React.PropTypes.string.isRequired,
+      items: React.PropTypes.array.isRequired
     },
 
     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);
+        var href = FauxtonAPI.urls(this.props.urlNamespace, 'app', 
this.props.databaseName, this.props.designDocName);
+
         return (
           <li key={key}>
           <a
@@ -176,6 +176,13 @@ function (app, FauxtonAPI, React, Stores, Actions, 
Components, DocumentViews) {
     },
 
     render: function () {
+
+      // if this section has no content, omit it to prevent clutter. Otherwise 
it would show a toggle option that
+      // would hide/show nothing
+      if (this.props.items.length === 0) {
+        return null;
+      }
+
       var toggleClassNames = 'accordion-header';
       var toggleBodyClassNames = 'accordion-body collapse';
       if (this.props.contentVisible) {
@@ -184,7 +191,7 @@ function (app, FauxtonAPI, React, Stores, Actions, 
Components, DocumentViews) {
       }
 
       var title = this.props.title;
-      var icon = this.props.indexTypeMap[this.props.selector].icon;
+      var icon = this.props.icon;
       var designDocName = this.props.designDocName;
       var linkId = "nav-design-function-" + designDocName + 
this.props.selector;
       return (
@@ -205,19 +212,36 @@ function (app, FauxtonAPI, React, Stores, Actions, 
Components, DocumentViews) {
 
   var DesignDoc = React.createClass({
 
-    createIndexList: function () {
-      var sidebarListTypes = FauxtonAPI.getExtensions('sidebar:list');
+    propTypes: {
+      sidebarListTypes: React.PropTypes.array.isRequired
+    },
 
-      if (_.isEmpty(sidebarListTypes) ||
-        (_.has(sidebarListTypes[0], 'selector') && 
sidebarListTypes[0].selector !== 'views')) {
-        sidebarListTypes.unshift({
+    getInitialState: function () {
+      return {
+        updatedSidebarListTypes: this.props.sidebarListTypes
+      };
+    },
+
+    componentWillMount: function () {
+      if (_.isEmpty(this.state.updatedSidebarListTypes) ||
+        (_.has(this.state.updatedSidebarListTypes[0], 'selector') && 
this.state.updatedSidebarListTypes[0].selector !== 'views')) {
+
+        var newList = this.state.updatedSidebarListTypes;
+        newList.unshift({
           selector: 'views',
-          name: 'Views'
+          name: 'Views',
+          icon: 'fonticon-sidenav-map-reduce',
+          urlNamespace: 'view'
         });
+        this.setState({ updatedSidebarListTypes: newList });
       }
+    },
 
-      return _.map(sidebarListTypes, function (index, key) {
+    createIndexList: function () {
+      return _.map(this.state.updatedSidebarListTypes, function (index, key) {
         return <IndexSection
+          icon={index.icon}
+          urlNamespace={index.urlNamespace}
           contentVisible={this.props.isVisible(this.props.designDocName, 
index.name)}
           toggle={this.props.toggle}
           databaseName={this.props.databaseName}
@@ -303,10 +327,16 @@ function (app, FauxtonAPI, React, Stores, Actions, 
Components, DocumentViews) {
   });
 
   var DesignDocList = React.createClass({
+    componentWillMount: function () {
+      var list = FauxtonAPI.getExtensions('sidebar:list');
+      this.sidebarListTypes = _.isUndefined(list) ? [] : list;
+    },
+
     createDesignDocs: function () {
       return _.map(this.props.designDocs, function (designDoc, key) {
         return <DesignDoc
           toggle={this.props.toggle}
+          sidebarListTypes={this.sidebarListTypes}
           contentVisible={this.props.isVisible(designDoc.safeId)}
           isVisible={this.props.isVisible}
           key={key}
@@ -317,9 +347,6 @@ function (app, FauxtonAPI, React, Stores, Actions, 
Components, DocumentViews) {
     },
 
     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()}
@@ -412,9 +439,9 @@ function (app, FauxtonAPI, React, Stores, Actions, 
Components, DocumentViews) {
     }
   });
 
-  var Views = {
-    SidebarController: SidebarController
+  return {
+    SidebarController: SidebarController,
+    DesignDoc: DesignDoc
   };
 
-  return Views;
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/8a7a0f9d/app/addons/documents/sidebar/tests/sidebar.componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git 
a/app/addons/documents/sidebar/tests/sidebar.componentsSpec.react.jsx 
b/app/addons/documents/sidebar/tests/sidebar.componentsSpec.react.jsx
new file mode 100644
index 0000000..ec3a046
--- /dev/null
+++ b/app/addons/documents/sidebar/tests/sidebar.componentsSpec.react.jsx
@@ -0,0 +1,93 @@
+// 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',
+  'react',
+  'testUtils',
+  'addons/documents/sidebar/sidebar.react'
+], function (FauxtonAPI, React, utils, Components) {
+  var assert = utils.assert;
+  var TestUtils = React.addons.TestUtils;
+  var DesignDoc = Components.DesignDoc;
+
+
+  describe('DesignDoc', function () {
+    var container;
+
+    beforeEach(function () {
+      container = document.createElement('div');
+    });
+
+    afterEach(function () {
+      React.unmountComponentAtNode(container);
+    });
+
+    it('confirm only single sub-option is shown by default (metadata link)', 
function () {
+      var stub = function () { return true; };
+      var el = TestUtils.renderIntoDocument(<DesignDoc
+        toggle={stub}
+        sidebarListTypes={[]}
+        contentVisible={true}
+        isVisible={stub}
+        designDoc={{}}
+        designDocName="id"
+        databaseName="db-name" />, container);
+      var subOptions = $(React.findDOMNode(el)).find('.accordion-body li');
+      assert.equal(subOptions.length, 1);
+   });
+
+    it('confirm design doc sidebar extensions appear', function () {
+      var stub = function () { return true; };
+      var el = TestUtils.renderIntoDocument(<DesignDoc
+        toggle={stub}
+        contentVisible={true}
+        isVisible={stub}
+        sidebarListTypes={[{
+          selector: 'customProp',
+          name: 'Search Indexes',
+          icon: 'icon-here',
+          urlNamespace: 'whatever'
+        }]}
+        designDoc={{
+          customProp: {
+            one: 'something'
+          }
+        }}
+        designDocName="id"
+        databaseName="db-name" />, container);
+      var subOptions = $(React.findDOMNode(el)).find('.accordion-body li');
+      assert.equal(subOptions.length, 3); // 1 for "Metadata" row, 1 for Type 
List row ("search indexes") and one for the index itself
+    });
+
+    it('confirm design doc sidebar extensions do not appear when they have no 
content', function () {
+      var stub = function () { return true; };
+      var el = TestUtils.renderIntoDocument(<DesignDoc
+        toggle={stub}
+        sidebarListTypes={[{
+          selector: 'customProp',
+          name: 'Search Indexes',
+          icon: 'icon-here',
+          urlNamespace: 'whatever'
+        }]}
+        contentVisible={true}
+        isVisible={stub}
+        designDoc={{}} // note that this is empty
+        designDocName="id"
+        databaseName="db-name" />, container);
+      var subOptions = $(React.findDOMNode(el)).find('.accordion-body li');
+      assert.equal(subOptions.length, 1);
+    });
+
+  });
+
+});
+

Reply via email to