Return-Path: X-Original-To: apmail-couchdb-commits-archive@www.apache.org Delivered-To: apmail-couchdb-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id EB75818D11 for ; Thu, 18 Feb 2016 01:11:13 +0000 (UTC) Received: (qmail 44972 invoked by uid 500); 18 Feb 2016 01:11:13 -0000 Delivered-To: apmail-couchdb-commits-archive@couchdb.apache.org Received: (qmail 44929 invoked by uid 500); 18 Feb 2016 01:11:13 -0000 Mailing-List: contact commits-help@couchdb.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@couchdb.apache.org Delivered-To: mailing list commits@couchdb.apache.org Received: (qmail 44920 invoked by uid 99); 18 Feb 2016 01:11:13 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 18 Feb 2016 01:11:13 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 62A4DE020D; Thu, 18 Feb 2016 01:11:13 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: benkeen@apache.org To: commits@couchdb.apache.org Message-Id: <5e1e5846aaca4a0da20f3b564ad5a57d@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: fauxton commit: updated refs/heads/master to abc3e69 Date: Thu, 18 Feb 2016 01:11:13 +0000 (UTC) Repository: couchdb-fauxton Updated Branches: refs/heads/master a62af899a -> abc3e697f Misc database sidebar updates This contains various updates to the database sidebar, as per Justin's feedback. - Order and labels of buttons are changed. - a few unclear icons (map reduce, Views) have been removed - properly highlights what page you're on. - Now allows us to select any nav item in the sidebar regardless of nesting to highlight the nav item. This also now works on full page refreshes. Note this feature paves the way for the next thing I'll work on: updating Views so that they retain the sidebar. Once that's changed, it will need to clearly highlight what View you're on in the sidebar. Project: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/commit/abc3e697 Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/abc3e697 Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/abc3e697 Branch: refs/heads/master Commit: abc3e697f3ba5bb76dbb32af8d3068a6d568909b Parents: a62af89 Author: Ben Keen Authored: Fri Feb 5 08:40:19 2016 -0800 Committer: Ben Keen Committed: Wed Feb 17 09:17:18 2016 -0800 ---------------------------------------------------------------------- app/addons/documents/assets/less/sidenav.less | 256 +++++++++---------- app/addons/documents/routes-documents.js | 12 +- app/addons/documents/routes-mango.js | 2 +- app/addons/documents/shared-routes.js | 7 +- app/addons/documents/sidebar/actions.js | 26 +- app/addons/documents/sidebar/actiontypes.js | 2 +- app/addons/documents/sidebar/sidebar.react.jsx | 249 ++++++++++-------- app/addons/documents/sidebar/stores.js | 110 +++++--- .../tests/sidebar.componentsSpec.react.jsx | 30 +++ .../sidebar/tests/sidebar.storesSpec.js | 2 +- assets/less/templates.less | 4 +- 11 files changed, 390 insertions(+), 310 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/abc3e697/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 11df376..9c6fb35 100644 --- a/app/addons/documents/assets/less/sidenav.less +++ b/app/addons/documents/assets/less/sidenav.less @@ -9,6 +9,7 @@ // 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 "../../../../../assets/less/variables.less"; #sidebar-content { .loading-lines { @@ -16,176 +17,147 @@ } } -.sidenav{ - .sidebar-toggler { - top: 5px; - position: absolute; - right: 7px; - z-index: 100; - width: 60px; - height: 35px; - text-align: center; - line-height: 35px; +.sidenav { + a { 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{ + + // selected nav item row styles + .nav-list .active > a { text-shadow: none; - background-color: rgba(0, 0, 0, 0.05); - } - .nav-list > .active > a:hover { - color: white; - } - .nav-list > li > a:hover + div.add-dropdown .dropdown-toggle{ - color: white; + background-color: #f1f0f1; + color: @brandPrimaryDark; + &:hover { + color: white; + } } + .dropdown-toggle:hover { color: @linkColor; } - li{ - position: relative; - a{ - text-shadow: none; + + // ugly! This styles the (+) icon to make it white when a user hovers over a row. Better solution would be to move the + // active class to the
  • instead of the child + .accordion-list-item:hover, .nav-list > li a:hover { + & + div.add-dropdown .dropdown-toggle { + color: white; } } - li.nav-header { + + li { position: relative; - > .accordion-body{ - &.in { - border-bottom: 1px solid #d3d7db; - } + a { + text-shadow: none; } - .accordion-body{ - color: #eee; - margin-left: 0; - li.active > a{ - background-color: rgba(0, 0, 0, 0.055); + } + + .design-doc-section { + border-bottom: 1px solid #d3d7db; + .accordion-list-item:hover { + p, .fonticon-play { + color: white; } - li a{ - font-size: 14px; - background-color: rgba(0, 0, 0, 0.02); - &:hover { - color: #fff; - text-decoration: none; - background-color: @darkRed; - } + p { + background-color: @darkRed; } } - .fonticon-play{ - color: @subListGray; - position: absolute; - display: block; - vertical-align: bottom; - } - .accordion-header:hover .fonticon-play { - color: white; - } - .fonticon-play{ + .accordion-list-item p { .transition(all 0.25s linear); } - > .accordion-header{ - position: relative; - display: block; + .fonticon-play { + font-size: 12px; + top: 12px; + left: 12px; + } + &.down .fonticon-play { + .rotate(90deg); + } + .add-dropdown { + right: 15px; + } + } + + li.nav-header .index-list li a { + padding-left: 46px; + } + + li.nav-header { + margin-top: 0; + position: relative; + + .design-doc-body.in { border-bottom: 1px solid #d3d7db; - .accordion-list-item:hover { - p, .fonticon-play{ - color: white; - } - p{ - background-color: @darkRed; - } - } - .accordion-list-item:hover + div.add-dropdown .dropdown-toggle{ - color: white; - } - .accordion-list-item p{ - .transition(all 0.25s linear); - } - .design-doc-name { - cursor: pointer; - margin: 0; - color: @linkColor; - span { - width: @sidebarWidth - 30px; - display: block; - padding: 10px 13px 10px 36px; - text-overflow: ellipsis; - overflow: hidden; - } - } - .fonticon-play{ - font-size: 12px; - top: 12px; - left: 12px; - } - &.down .fonticon-play { - .rotate(90deg); - } - .add-dropdown{ - right: 15px; - } - .dropdown-toggle:hover{ - color: @linkColor; - } } - > ul.accordion-body{ - > li { - cursor: pointer; - a{ - border-top: none; - &.accordion-header{ - padding: 8px 5px 8px 36px; - } - } - .toggle-view{ - padding: 4px 5px 4px 36px; - } - .fonticon-play{ - font-size: 8px; - top: 11px; - left: 15px; - } - .fonticon:before{ - margin-right: 6px; - font-size: 20px; - top: 3px; - position: relative; - } - } - li { - > a.down .fonticon-play { - .rotate(90deg); - } - } + .accordion-body { + color: #eee; + margin-left: 0; li { + cursor: pointer; + &.active > a { + background-color: #f1f0f1; + } > a.down .fonticon-play { .rotate(90deg); } - - &:hover { - .fonticon-play { - color: white; - + &:hover .fonticon-play { + color: #dddddd; + } + a { + font-size: 14px; + background-color: rgba(0, 0, 0, 0.02); + &:hover { + color: #fff; + text-decoration: none; + background-color: @darkRed; + } + border-top: none; + &.accordion-header{ + padding: 8px 5px 8px 36px; } } - } + } + + .toggle-view { + padding: 4px 5px 4px 36px; + } + .fonticon-play { + font-size: 8px; + top: 11px; + left: 15px; + color: @subListGray; + position: absolute; + display: block; + vertical-align: bottom; + .transition(all 0.25s linear); + } + .fonticon:before { + margin-right: 6px; + font-size: 20px; + top: 3px; + position: relative; + } + .accordion-header:hover .fonticon-play { + color: white; + } + } + .design-doc-name { + cursor: pointer; + margin: 0; + color: #222222; + span { + width: @sidebarWidth - 30px; + display: block; + padding: 10px 13px 10px 36px; + text-overflow: ellipsis; + overflow: hidden; } } + + .index-group-header { + font-weight: bold; + } } http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/abc3e697/app/addons/documents/routes-documents.js ---------------------------------------------------------------------- diff --git a/app/addons/documents/routes-documents.js b/app/addons/documents/routes-documents.js index 3829981..e6bd8b1 100644 --- a/app/addons/documents/routes-documents.js +++ b/app/addons/documents/routes-documents.js @@ -50,7 +50,6 @@ function (app, FauxtonAPI, BaseRoute, Documents, Changes, ChangesActions, Databa roles: ['fx_loggedIn'] }, 'database/:database/_changes': 'changes' - }, events: { @@ -96,7 +95,10 @@ function (app, FauxtonAPI, BaseRoute, Documents, Changes, ChangesActions, Databa }); this.setComponent("#dashboard-lower-content", DesignDocInfoComponents.DesignDocInfo); - SidebarActions.setSelectedTab(app.utils.removeSpecialCharacters(ddoc) + "_metadata"); + SidebarActions.selectNavItem('designDoc', { + designDocName: ddoc, + designDocSection: 'metadata' + }); this.leftheader.updateCrumbs(this.getCrumbs(this.database)); this.rightHeader.hideQueryOptions(); @@ -137,7 +139,7 @@ function (app, FauxtonAPI, BaseRoute, Documents, Changes, ChangesActions, Databa tab = 'design-docs'; } - SidebarActions.setSelectedTab(tab); + SidebarActions.selectNavItem(tab); ComponentsActions.showDeleteDatabaseModal({showDeleteModal: false, dbId: ''}); this.removeComponent('#dashboard-upper-content'); @@ -171,7 +173,7 @@ function (app, FauxtonAPI, BaseRoute, Documents, Changes, ChangesActions, Databa //TODO: REMOVE reloadDesignDocs: function (event) { if (event && event.selectedTab) { - SidebarActions.setSelectedTab(event.selectedTab); + SidebarActions.selectNavItem(event.selectedTab); } }, @@ -187,7 +189,7 @@ function (app, FauxtonAPI, BaseRoute, Documents, Changes, ChangesActions, Databa this.viewEditor && this.viewEditor.remove(); - SidebarActions.setSelectedTab('changes'); + SidebarActions.selectNavItem('changes'); this.leftheader.updateCrumbs(this.getCrumbs(this.database)); this.rightHeader.hideQueryOptions(); http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/abc3e697/app/addons/documents/routes-mango.js ---------------------------------------------------------------------- diff --git a/app/addons/documents/routes-mango.js b/app/addons/documents/routes-mango.js index 379150b..b24b206 100644 --- a/app/addons/documents/routes-mango.js +++ b/app/addons/documents/routes-mango.js @@ -86,7 +86,7 @@ function (app, FauxtonAPI, Helpers, BaseRoute, Databases, } }); - SidebarActions.setSelectedTab('mango-query'); + SidebarActions.selectNavItem('mango-query'); this.setComponent('#react-headerbar', ReactHeader.BulkDocumentHeaderController, {showIncludeAllDocs: false}); this.setComponent('#footer', ReactPagination.Footer); http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/abc3e697/app/addons/documents/shared-routes.js ---------------------------------------------------------------------- diff --git a/app/addons/documents/shared-routes.js b/app/addons/documents/shared-routes.js index 2238cf2..da95edb 100644 --- a/app/addons/documents/shared-routes.js +++ b/app/addons/documents/shared-routes.js @@ -101,14 +101,13 @@ define([ })); }, - addSidebar: function (selectedTab) { + addSidebar: function (selectedNavItem) { var options = { designDocs: this.designDocs, database: this.database }; - - if (selectedTab) { - options.selectedTab = selectedTab; + if (selectedNavItem) { + options.selectedNavItem = selectedNavItem; } SidebarActions.newOptions(options); http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/abc3e697/app/addons/documents/sidebar/actions.js ---------------------------------------------------------------------- diff --git a/app/addons/documents/sidebar/actions.js b/app/addons/documents/sidebar/actions.js index 4dbd83f..3419da0 100644 --- a/app/addons/documents/sidebar/actions.js +++ b/app/addons/documents/sidebar/actions.js @@ -18,6 +18,7 @@ define([ ], function (app, FauxtonAPI, ActionTypes, Stores) { var store = Stores.sidebarStore; + return { newOptions: function (options) { if (options.database.safeID() !== store.getDatabaseName()) { @@ -34,18 +35,33 @@ function (app, FauxtonAPI, ActionTypes, Stores) { }); }, - toggleContent: function (designDoc, index) { + toggleContent: function (designDoc, indexGroup) { FauxtonAPI.dispatch({ type: ActionTypes.SIDEBAR_TOGGLE_CONTENT, designDoc: designDoc, - index: index + indexGroup: indexGroup }); }, - setSelectedTab: function (tab) { + // This selects any item in the sidebar, including nested nav items to ensure the appropriate item is visible + // and highlighted. Params: + // - `navItem`: 'permissions', 'changes', 'all-docs', 'compact', 'mango-query', 'designDoc' (or anything thats been + // extended) + // - `params`: optional object if you passed designDoc as the first param. This lets you specify which sub-page + // should be selected, e.g. + // Actions.selectNavItem('designDoc', { designDocName: 'my-design-doc', section: 'metadata' }); + // Actions.selectNavItem('designDoc', { designDocName: 'my-design-doc', section: 'Views', indexName: 'my-view' }); + selectNavItem: function (navItem, params) { + var settings = $.extend(true, {}, { + designDocName: '', + designDocSection: '', + indexName: '' + }, params); + settings.navItem = navItem; + FauxtonAPI.dispatch({ - type: ActionTypes.SIDEBAR_SET_SELECTED_TAB, - tab: tab + type: ActionTypes.SIDEBAR_SET_SELECTED_NAV_ITEM, + options: settings }); }, http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/abc3e697/app/addons/documents/sidebar/actiontypes.js ---------------------------------------------------------------------- diff --git a/app/addons/documents/sidebar/actiontypes.js b/app/addons/documents/sidebar/actiontypes.js index 157c3fa..70a5547 100644 --- a/app/addons/documents/sidebar/actiontypes.js +++ b/app/addons/documents/sidebar/actiontypes.js @@ -12,7 +12,7 @@ define([], function () { return { - SIDEBAR_SET_SELECTED_TAB: 'SIDEBAR_SET_SELECTED_TAB', + SIDEBAR_SET_SELECTED_NAV_ITEM: 'SIDEBAR_SET_SELECTED_NAV_ITEM', SIDEBAR_NEW_OPTIONS: 'SIDEBAR_NEW_OPTIONS', SIDEBAR_TOGGLE_CONTENT: 'SIDEBAR_TOGGLE_CONTENT', SIDEBAR_FETCHING: 'SIDEBAR_FETCHING', http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/abc3e697/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 471aeb6..326e10b 100644 --- a/app/addons/documents/sidebar/sidebar.react.jsx +++ b/app/addons/documents/sidebar/sidebar.react.jsx @@ -28,7 +28,11 @@ function (app, FauxtonAPI, React, ReactDOM, Stores, Actions, Components, Documen var store = Stores.sidebarStore; var LoadLines = Components.LoadLines; + var MainSidebar = React.createClass({ + propTypes: { + selectedNavItem: React.PropTypes.string.isRequired + }, getNewButtonLinks: function () { // these are links for the sidebar '+' on All Docs and All Design Docs return DocumentHelper.getNewButtonLinks(this.props.databaseName); @@ -36,37 +40,31 @@ function (app, FauxtonAPI, React, ReactDOM, Stores, Actions, Components, Documen buildDocLinks: function () { var base = FauxtonAPI.urls('base', 'app', this.props.databaseName); - var isActive = this.props.isActive; - return FauxtonAPI.getExtensions('docLinks').map(function (link) { return ( -
  • +
  • {link.title}
  • ); - }); + }, this); + }, + + getNavItemClass: function (navItem) { + return (navItem === this.props.selectedNavItem) ? 'active' : ''; }, render: function () { - var isActive = this.props.isActive; var docLinks = this.buildDocLinks(); - var changesUrl = '#' + FauxtonAPI.urls('changes', 'app', this.props.databaseName, ''); + 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 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 (
      -
    • - Permissions -
    • -
    • - Changes -
    • - {docLinks} -
    • +
    • @@ -75,24 +73,28 @@ function (app, FauxtonAPI, React, ReactDOM, Stores, Actions, Components, Documen
      -
    • -
    • +
    • +
    • {runQueryWithMangoText} -
      - -
    • -
    • +
    • + Permissions +
    • +
    • + Changes +
    • + {docLinks} +
    • - All Design Docs + Design Documents
      @@ -108,24 +110,26 @@ function (app, FauxtonAPI, React, ReactDOM, Stores, Actions, Components, Documen 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 + items: React.PropTypes.array.isRequired, + isExpanded: React.PropTypes.bool.isRequired, + selectedIndex: React.PropTypes.string.isRequired }, createItems: function () { return _.map(this.props.items, function (index, key) { var href = FauxtonAPI.urls(this.props.urlNamespace, 'app', this.props.databaseName, this.props.designDocName); + var className = (this.props.selectedIndex === index) ? 'active' : ''; return ( -
    • - - {index} - +
    • + + {index} +
    • ); }, this); @@ -133,7 +137,7 @@ function (app, FauxtonAPI, React, ReactDOM, Stores, Actions, Components, Documen toggle: function (e) { e.preventDefault(); - var newToggleState = !this.props.contentVisible; + var newToggleState = !this.props.isExpanded; var state = newToggleState ? 'show' : 'hide'; $(ReactDOM.findDOMNode(this)).find('.accordion-body').collapse(state); this.props.toggle(this.props.designDocName, this.props.title); @@ -147,22 +151,21 @@ function (app, FauxtonAPI, React, ReactDOM, Stores, Actions, Components, Documen return null; } - var toggleClassNames = 'accordion-header'; - var toggleBodyClassNames = 'accordion-body collapse'; - if (this.props.contentVisible) { + var toggleClassNames = 'accordion-header index-group-header'; + var toggleBodyClassNames = 'index-list accordion-body collapse'; + if (this.props.isExpanded) { toggleClassNames += ' down'; toggleBodyClassNames += ' in'; } var title = this.props.title; - var icon = this.props.icon; var designDocName = this.props.designDocName; var linkId = "nav-design-function-" + designDocName + this.props.selector; + return (
    • - {title}
        @@ -171,13 +174,15 @@ function (app, FauxtonAPI, React, ReactDOM, Stores, Actions, Components, Documen ); } - }); - var DesignDoc = React.createClass({ + var DesignDoc = React.createClass({ propTypes: { - sidebarListTypes: React.PropTypes.array.isRequired + sidebarListTypes: React.PropTypes.array.isRequired, + isExpanded: React.PropTypes.bool.isRequired, + selectedNavInfo: React.PropTypes.object.isRequired, + toggledSections: React.PropTypes.object.isRequired }, getInitialState: function () { @@ -194,32 +199,42 @@ function (app, FauxtonAPI, React, ReactDOM, Stores, Actions, Components, Documen newList.unshift({ selector: 'views', name: 'Views', - icon: 'fonticon-sidenav-map-reduce', urlNamespace: 'view' }); this.setState({ updatedSidebarListTypes: newList }); } }, - createIndexList: function () { + indexList: function () { return _.map(this.state.updatedSidebarListTypes, function (index, key) { - return ; + var expanded = _.has(this.props.toggledSections, index.name) && this.props.toggledSections[index.name]; + + // if an index in this list is selected, pass that down + var selectedIndex = ''; + if (this.props.selectedNavInfo.designDocSection === index.name) { + selectedIndex = this.props.selectedNavInfo.indexName; + } + + return ( + + ); }.bind(this)); }, toggle: function (e) { e.preventDefault(); - var newToggleState = !this.props.contentVisible; + var newToggleState = !this.props.isExpanded; var state = newToggleState ? 'show' : 'hide'; $(ReactDOM.findDOMNode(this)).find('#' + this.props.designDocName).collapse(state); this.props.toggle(this.props.designDocName); @@ -236,7 +251,6 @@ function (app, FauxtonAPI, React, ReactDOM, Stores, Actions, Components, Documen url: '#' + newUrlPrefix + '/' + link.url + '/' + designDocName, icon: 'fonticon-plus-circled' }); - return menuLinks; }, [{ title: 'New View', @@ -252,76 +266,93 @@ function (app, FauxtonAPI, React, ReactDOM, Stores, Actions, Components, Documen render: function () { var buttonLinks = this.getNewButtonLinks(); - var toggleClassNames = 'accordion-header'; - var toggleBodyClassNames = 'accordion-body collapse'; + var toggleClassNames = 'design-doc-section accordion-header'; + var toggleBodyClassNames = 'design-doc-body accordion-body collapse'; - if (this.props.contentVisible) { + if (this.props.isExpanded) { toggleClassNames += ' down'; toggleBodyClassNames += ' in'; } var designDocName = this.props.designDocName; var designDocMetaUrl = FauxtonAPI.urls('designDocs', 'app', this.props.databaseName, designDocName); + var metadataRowClass = (this.props.selectedNavInfo.designDocSection === 'metadata') ? 'active' : ''; + return ( -
      • - -
        -
        -
        -

        - {'_design/' + designDocName} -

        -
        -
        - +
      • +
        +
        +
        +

        + {designDocName} +

        +
        +
        + +
        - - +
      • ); } - }); + var DesignDocList = React.createClass({ componentWillMount: function () { var list = FauxtonAPI.getExtensions('sidebar:list'); this.sidebarListTypes = _.isUndefined(list) ? [] : list; }, - createDesignDocs: function () { + designDocList: function () { return _.map(this.props.designDocs, function (designDoc, key) { - return ; + var ddName = designDoc.safeId; + + // only pass down the selected nav info and toggle info if they're relevant for this particular design doc + var expanded = false, + toggledSections = {}; + if (_.has(this.props.toggledSections, ddName)) { + expanded = this.props.toggledSections[ddName].visible; + toggledSections = this.props.toggledSections[ddName].indexGroups; + } + + var selectedNavInfo = {}; + if (this.props.selectedNav.navItem === 'designDoc' && this.props.selectedNav.designDocName === ddName) { + selectedNavInfo = this.props.selectedNav; + } + + return ( + + ); }.bind(this)); }, render: function () { return (
          - {this.createDesignDocs()} + {this.designDocList()}
        ); } - }); - var DeleteDBModalWrapper = React.createClass({ + var DeleteDBModalWrapper = React.createClass({ componentDidMount: function () { this.dbModal = new DeleteDBModal({ database: this.props.database, @@ -344,29 +375,21 @@ function (app, FauxtonAPI, React, ReactDOM, Stores, Actions, Components, Documen render: function () { return
        ; } - }); + var SidebarController = React.createClass({ getStoreState: function () { return { databaseName: store.getDatabaseName(), - selectedTab: store.getSelectedTab(), + selectedNav: store.getSelected(), designDocs: store.getDesignDocs(), - isVisible: _.bind(store.isVisible, store), + toggledSections: store.getToggledSections(), isLoading: store.isLoading(), database: store.getDatabase() }; }, - isActive: function (id) { - if (id === this.state.selectedTab) { - return 'active'; - } - - return ''; - }, - getInitialState: function () { return this.getStoreState(); }, @@ -380,24 +403,28 @@ function (app, FauxtonAPI, React, ReactDOM, Stores, Actions, Components, Documen }, onChange: function () { - this.setState(this.getStoreState()); + if (this.isMounted()) { + this.setState(this.getStoreState()); + } }, - render: function () { if (this.state.isLoading) { return ; } - return ( ); } http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/abc3e697/app/addons/documents/sidebar/stores.js ---------------------------------------------------------------------- diff --git a/app/addons/documents/sidebar/stores.js b/app/addons/documents/sidebar/stores.js index 8fdbefd..f351fcd 100644 --- a/app/addons/documents/sidebar/stores.js +++ b/app/addons/documents/sidebar/stores.js @@ -22,7 +22,12 @@ function (app, FauxtonAPI, ActionTypes) { Stores.SidebarStore = FauxtonAPI.Store.extend({ initialize: function () { - this._selectedTab = 'all-docs'; + this._selected = { + navItem: 'all-docs', + designDocName: '', + designDocSection: '', // metadata / name of index group ("Views", etc.) + indexName: '' + }; this._loading = true; this._toggledSections = {}; }, @@ -32,8 +37,15 @@ function (app, FauxtonAPI, ActionTypes) { this._designDocs = options.designDocs; this._loading = false; - if (options.selectedTab) { - this.setSelectedTab(options.selectedTab); + // this can be expanded in future as we need. Right now it can only set a top-level nav item ('all docs', + // 'permissions' etc.) and not a nested page + if (options.selectedNavItem) { + this._selected = { + navItem: options.selectedNavItem, + designDocName: '', + designDocSection: '', + indexName: '' + }; } }, @@ -42,62 +54,89 @@ function (app, FauxtonAPI, ActionTypes) { }, getDatabase: function () { - if (this.isLoading()) {return {};} - + if (this.isLoading()) { + return {}; + } return this._database; }, - toggleContent: function (designDoc, index) { + // used to toggle both design docs, and any index groups within them + toggleContent: function (designDoc, indexGroup) { if (!this._toggledSections[designDoc]) { this._toggledSections[designDoc] = { visible: true, - indexes: {} + indexGroups: {} }; return; } - if (index) { - return this.toggleIndex(designDoc, index); + if (indexGroup) { + return this.toggleIndexGroup(designDoc, indexGroup); } this._toggledSections[designDoc].visible = !this._toggledSections[designDoc].visible; }, - toggleIndex: function (designDoc, indexName) { - var index = this._toggledSections[designDoc].indexes[indexName]; + toggleIndexGroup: function (designDoc, indexGroup) { + var expanded = this._toggledSections[designDoc].indexGroups[indexGroup]; - if (_.isUndefined(index)) { - this._toggledSections[designDoc].indexes[indexName] = true; + if (_.isUndefined(expanded)) { + this._toggledSections[designDoc].indexGroups[indexGroup] = true; return; } - this._toggledSections[designDoc].indexes[indexName] = !index; + this._toggledSections[designDoc].indexGroups[indexGroup] = !expanded; }, - isVisible: function (designDoc, index) { + isVisible: function (designDoc, indexGroup) { if (!this._toggledSections[designDoc]) { return false; } - - if (index) { - return this._toggledSections[designDoc].indexes[index]; + if (indexGroup) { + return this._toggledSections[designDoc].indexGroups[indexGroup]; } - return this._toggledSections[designDoc].visible; }, - setSelectedTab: function (tab) { - this._selectedTab = tab; + getSelected: function () { + return this._selected; }, - getDatabaseName: function () { - if (this.isLoading()) { return '';} + setSelected: function (params) { + this._selected = { + navItem: params.navItem, + designDocName: params.designDocName, + designDocSection: params.designDocSection, + indexName: params.indexName + }; + + if (params.designDocName) { + if (!_.has(this._toggledSections, params.designDocName)) { + this._toggledSections[params.designDocName] = { visible: true, indexGroups: {} }; + } + this._toggledSections[params.designDocName].visible = true; + + if (params.designDocSection) { + this._toggledSections[params.designDocName].indexGroups[params.designDocSection] = true; + } + } + }, + + getToggledSections: function () { + return this._toggledSections; + }, + getDatabaseName: function () { + if (this.isLoading()) { + return ''; + } return this._database.safeID(); }, getDesignDocs: function () { - if (this.isLoading()) { return {};} + if (this.isLoading()) { + return {}; + } var docs = this._designDocs.toJSON(); docs = _.filter(docs, function (doc) { @@ -109,47 +148,42 @@ function (app, FauxtonAPI, ActionTypes) { 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(); + case ActionTypes.SIDEBAR_SET_SELECTED_NAV_ITEM: + this.setSelected(action.options); 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(); + this.toggleContent(action.designDoc, action.indexGroup); break; + case ActionTypes.SIDEBAR_FETCHING: this._loading = true; - this.triggerChange(); break; + case ActionTypes.SIDEBAR_REFRESH: - this.triggerChange(); break; default: return; // do nothing } + + this.triggerChange(); } }); 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/abc3e697/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 index a57fc28..68436f8 100644 --- a/app/addons/documents/sidebar/tests/sidebar.componentsSpec.react.jsx +++ b/app/addons/documents/sidebar/tests/sidebar.componentsSpec.react.jsx @@ -24,6 +24,13 @@ define([ describe('DesignDoc', function () { var container; + var selectedNavInfo = { + navItem: 'all-docs', + designDocName: '', + designDocSection: '', + indexName: '' + }; + beforeEach(function () { container = document.createElement('div'); }); @@ -40,6 +47,7 @@ define([ contentVisible={true} isVisible={stub} designDoc={{}} + selectedNavInfo={selectedNavInfo} designDocName="id" databaseName="db-name" />, container); var subOptions = $(ReactDOM.findDOMNode(el)).find('.accordion-body li'); @@ -63,6 +71,7 @@ define([ one: 'something' } }} + selectedNavInfo={selectedNavInfo} designDocName="id" databaseName="db-name" />, container); var subOptions = $(ReactDOM.findDOMNode(el)).find('.accordion-body li'); @@ -81,6 +90,7 @@ define([ }]} contentVisible={true} isVisible={stub} + selectedNavInfo={selectedNavInfo} designDoc={{}} // note that this is empty designDocName="id" databaseName="db-name" />, container); @@ -88,6 +98,26 @@ define([ assert.equal(subOptions.length, 1); }); + it('confirm doc metadata page is highlighted if selected', function () { + var stub = function () { return true; }; + var el = TestUtils.renderIntoDocument(, container); + + assert.equal($(ReactDOM.findDOMNode(el)).find('.accordion-body li.active a').html(), 'Metadata'); + }); + }); }); http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/abc3e697/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 index dbcbee2..3012820 100644 --- a/app/addons/documents/sidebar/tests/sidebar.storesSpec.js +++ b/app/addons/documents/sidebar/tests/sidebar.storesSpec.js @@ -19,7 +19,7 @@ define([ var assert = testUtils.assert; var dispatchToken; var store; - var opts; + describe('Sidebar Store', function () { beforeEach(function () { http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/abc3e697/assets/less/templates.less ---------------------------------------------------------------------- diff --git a/assets/less/templates.less b/assets/less/templates.less index 26098dd..172de0f 100644 --- a/assets/less/templates.less +++ b/assets/less/templates.less @@ -453,10 +453,10 @@ with_tabs_sidebar.html color: #fff; } li.active > a { - color: @linkColorHover; + color: @brandPrimaryDark; } > li > a { - color: @linkColor; + color: #333333; padding: 10px 13px 10px 24px; border-bottom: 1px solid #d3d7db; span {