You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ro...@apache.org on 2015/01/28 18:36:21 UTC
[1/2] fauxton commit: updated refs/heads/master to f5f7f4b
Repository: couchdb-fauxton
Updated Branches:
refs/heads/master 16ec9ab45 -> f5f7f4b93
all-docs: New way to bulk-edit documents
adds a bar with controls for interaction with documents to the
upper header which can be toggled on and off
the button for the bar toggles between a header with the api-bar,
query-options-tray and search and the new header for selecting
and deselecting all documents, collapsing and deleting them
Project: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/commit/25dab477
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/25dab477
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/25dab477
Branch: refs/heads/master
Commit: 25dab477415d417019cf863db393470b11e5d350
Parents: 16ec9ab
Author: Robert Kowalski <ro...@apache.org>
Authored: Mon Jan 19 13:58:51 2015 +0100
Committer: Robert Kowalski <ro...@apache.org>
Committed: Wed Jan 28 18:31:02 2015 +0100
----------------------------------------------------------------------
app/addons/documents/assets/less/documents.less | 6 +
app/addons/documents/assets/less/header.less | 53 ++++
app/addons/documents/header/header.actions.js | 61 +++++
.../documents/header/header.actiontypes.js | 22 ++
app/addons/documents/header/header.react.jsx | 245 +++++++++++++++++++
app/addons/documents/header/header.stores.js | 150 ++++++++++++
app/addons/documents/routes-documents.js | 5 +
.../documents/templates/all_docs_list.html | 14 --
app/addons/documents/tests/headerSpec.react.jsx | 146 +++++++++++
.../documents/tests/nightwatch/bulkDelete.js | 16 +-
.../tests/nightwatch/deletesDocument.js | 7 +-
app/addons/documents/tests/viewsSpec.js | 49 +---
app/addons/documents/views.js | 92 ++++---
app/app.js | 1 -
app/constants.js | 2 +-
app/templates/layouts/with_tabs_sidebar.html | 1 +
assets/less/animations.less | 14 ++
assets/less/fauxton.less | 2 +-
assets/less/react-animations.less | 17 ++
19 files changed, 793 insertions(+), 110 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/app/addons/documents/assets/less/documents.less
----------------------------------------------------------------------
diff --git a/app/addons/documents/assets/less/documents.less b/app/addons/documents/assets/less/documents.less
index ed28272..4e53065 100644
--- a/app/addons/documents/assets/less/documents.less
+++ b/app/addons/documents/assets/less/documents.less
@@ -18,6 +18,8 @@
@import "changes.less";
@import "sidenav.less";
+@import "header.less";
+
button.beautify {
margin-top: 20px;
}
@@ -226,3 +228,7 @@ button.string-edit[disabled] {
}
}
}
+
+#react-headerbar {
+ float: left;
+}
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/app/addons/documents/assets/less/header.less
----------------------------------------------------------------------
diff --git a/app/addons/documents/assets/less/header.less b/app/addons/documents/assets/less/header.less
new file mode 100644
index 0000000..0bf0c3a
--- /dev/null
+++ b/app/addons/documents/assets/less/header.less
@@ -0,0 +1,53 @@
+.header-control-box {
+ color: #666;
+ font-size: 13px;
+ line-height: 33px;
+ padding: 12px 20px 12px 20px;
+ height: @collapsedNavWidth;
+ background-color: transparent;
+ border: none;
+ border-right: 1px solid @btnBorder;
+ text-decoration: none;
+ float: left;
+ .icon {
+ font-size: 20px;
+ }
+}
+
+button.header-control-box:focus {
+ outline: 0;
+}
+
+.js-headerbar-togglebutton-selected {
+ .icon, span {
+ color: #e33f3b;
+ }
+}
+
+.control-toggle-alternative-header {
+ .icon {
+ padding: 0px 8px 0px 0px;
+ }
+}
+
+.control-toggle-alternative-header:hover,
+.control-cancel:hover,
+.control-delete:hover {
+ .icon, span {
+ color: #e33f3b;
+ }
+}
+
+.control-cancel {
+ border-right: none;
+}
+
+.header-control-square {
+ height: @collapsedNavWidth;
+ width: @collapsedNavWidth;
+ float: left;
+}
+
+.alternative-header {
+ float: left;
+}
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/app/addons/documents/header/header.actions.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/header/header.actions.js b/app/addons/documents/header/header.actions.js
new file mode 100644
index 0000000..7be2a05
--- /dev/null
+++ b/app/addons/documents/header/header.actions.js
@@ -0,0 +1,61 @@
+// 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/header/header.actiontypes'
+],
+function (app, FauxtonAPI, ActionTypes) {
+
+ return {
+ toggleCollapseDocuments: function () {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.COLLAPSE_DOCUMENTS
+ });
+
+ FauxtonAPI.Events.trigger('headerbar:collapse');
+ },
+
+ toggleSelectAllDocuments: function (on) {
+ FauxtonAPI.Events.trigger('headerbar:selectall', on);
+ },
+
+ updateDocumentCount: function (options) {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.UPDATE_DOCUMENT_COUNT,
+ options: options
+ });
+ },
+
+ deleteSelected: function () {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.DELETE_SELECTED
+ });
+
+ FauxtonAPI.Events.trigger('headerbar:deleteselected');
+ },
+
+ toggleHeaderControls: function () {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.TOGGLE_HEADER_CONTROLS
+ });
+ },
+
+ resetHeaderController: function () {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.RESET_HEADER_BAR
+ });
+ }
+
+ };
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/app/addons/documents/header/header.actiontypes.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/header/header.actiontypes.js b/app/addons/documents/header/header.actiontypes.js
new file mode 100644
index 0000000..180dabb
--- /dev/null
+++ b/app/addons/documents/header/header.actiontypes.js
@@ -0,0 +1,22 @@
+// 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 {
+ UPDATE_DOCUMENT_COUNT: 'UPDATE_DOCUMENT_COUNT',
+ COLLAPSE_DOCUMENTS: 'COLLAPSE_DOCUMENTS',
+ TOGGLE_HEADER_CONTROLS: 'TOGGLE_HEADER_CONTROLS',
+ RESET_HEADER_BAR: 'RESET_HEADER_BAR',
+ DELETE_SELECTED_DOCUMENTS: 'DELETE_SELECTED_DOCUMENTS'
+ };
+});
+
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/app/addons/documents/header/header.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/header/header.react.jsx b/app/addons/documents/header/header.react.jsx
new file mode 100644
index 0000000..faf46bc
--- /dev/null
+++ b/app/addons/documents/header/header.react.jsx
@@ -0,0 +1,245 @@
+// 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/header/header.stores',
+ 'addons/documents/header/header.actions'
+],
+
+function (app, FauxtonAPI, React, Stores, Actions) {
+ var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
+ var headerBarStore = Stores.headerBarStore;
+ var bulkDocumentHeaderStore = Stores.bulkDocumentHeaderStore;
+
+ // this will be a global component
+ var ToggleHeaderButton = React.createClass({
+ render: function () {
+ var iconClasses = 'icon ' + this.props.fonticon + ' ' + this.props.innerClasses,
+ containerClasses = 'button ' + this.props.containerClasses;
+
+ if (this.props.setEnabledClass) {
+ containerClasses = containerClasses + ' js-headerbar-togglebutton-selected';
+ }
+
+ return (
+ <button
+ title={this.props.title}
+ disabled={this.props.disabled}
+ onClick={this.props.toggleCallback}
+ className={containerClasses}
+ >
+ <i className={iconClasses}></i><span>{this.props.text}</span>
+ </button>
+ );
+ }
+ });
+
+ var BulkDocumentHeaderController = React.createClass({
+ getStoreState: function () {
+ return {
+ areDocumentsCollapsed: bulkDocumentHeaderStore.getCollapsedState(),
+ isDeselectPossible: bulkDocumentHeaderStore.getIsDeselectPossible(),
+ isSelectAllPossible: bulkDocumentHeaderStore.getIsSelectAllPossible()
+ };
+ },
+
+ getInitialState: function () {
+ return this.getStoreState();
+ },
+
+ componentDidMount: function () {
+ bulkDocumentHeaderStore.on('change', this.onChange, this);
+ },
+
+ componentWillUnmount: function() {
+ bulkDocumentHeaderStore.off('change', this.onChange);
+ },
+
+ onChange: function () {
+ this.setState(this.getStoreState());
+ },
+
+ render: function () {
+ var baseClass = 'header-control-box header-control-square ',
+ isDeselectPossible = this.state.isDeselectPossible,
+ isSelectAllPossible = this.state.isSelectAllPossible,
+ areDocumentsCollapsed = this.state.areDocumentsCollapsed;
+
+ return (
+ <div className='alternative-header'>
+ <ToggleHeaderButton
+ fonticon={'fonticon-select-all'}
+ toggleCallback={this.selectAllDocuments}
+ innerClasses={''}
+ containerClasses={baseClass + 'control-select-all'}
+ text={''}
+ setEnabledClass={!isSelectAllPossible}
+ disabled={!isSelectAllPossible}
+ title={'Select all Documents'} />
+
+ <ToggleHeaderButton
+ fonticon={'fonticon-deselect-all'}
+ toggleCallback={this.deSelectAllDocuments}
+ innerClasses={''}
+ containerClasses={baseClass + 'control-de-select-all'}
+ text={''}
+ setEnabledClass={!isDeselectPossible}
+ disabled={!isDeselectPossible}
+ title={'Deselect all Documents'} />
+
+ <ToggleHeaderButton
+ fonticon={'fonticon-collapse'}
+ toggleCallback={this.toggleCollapseDocuments}
+ innerClasses={''}
+ containerClasses={baseClass + 'control-collapse'}
+ text={''}
+ setEnabledClass={areDocumentsCollapsed}
+ disabled={areDocumentsCollapsed}
+ title={'Collapse all'} />
+
+ <ToggleHeaderButton
+ fonticon={'fonticon-expand'}
+ toggleCallback={this.toggleCollapseDocuments}
+ innerClasses={''}
+ containerClasses={baseClass + 'control-expand'}
+ text={''}
+ setEnabledClass={!areDocumentsCollapsed}
+ disabled={!areDocumentsCollapsed}
+ title={'Expand all'} />
+
+ <ToggleHeaderButton
+ fonticon={'fonticon-trash'}
+ toggleCallback={this.deleteSelected}
+ innerClasses={''}
+ containerClasses={baseClass + 'control-delete'}
+ text={''}
+ title={'Delete selected'} />
+
+ <ToggleHeaderButton
+ fonticon={''}
+ toggleCallback={this.cancelView}
+ innerClasses={''}
+ containerClasses={baseClass + 'control-cancel'}
+ text={'Cancel'}
+ title={'Switch to other view'} />
+ </div>
+ );
+ },
+
+ toggleCollapseDocuments: function () {
+ Actions.toggleCollapseDocuments();
+ },
+
+ selectAllDocuments: function () {
+ Actions.toggleSelectAllDocuments(false);
+ },
+
+ deSelectAllDocuments: function () {
+ Actions.toggleSelectAllDocuments(true);
+ },
+
+ cancelView: function () {
+ Actions.toggleHeaderControls();
+ },
+
+ deleteSelected: function () {
+ Actions.deleteSelected();
+ }
+ });
+
+ var HeaderBarController = React.createClass({
+ getStoreState: function () {
+ return {
+ isToggled: headerBarStore.getToggleStatus(),
+ toggleClass: headerBarStore.getToggleClass()
+ };
+ },
+
+ getInitialState: function () {
+ return this.getStoreState();
+ },
+
+ onChange: function () {
+ this.setState(this.getStoreState());
+ },
+
+ componentDidMount: function () {
+ headerBarStore.on('change', this.onChange, this);
+ },
+
+ componentWillUnmount: function() {
+ headerBarStore.off('change', this.onChange);
+ },
+
+ toggleCallback: function () {
+ Actions.toggleHeaderControls();
+ },
+
+ componentDidUpdate: function () {
+ // todo reactify right header (api bar, query options)
+ var $oldHeader = $('#api-navbar, #right-header');
+ if (this.state.isToggled) {
+ $oldHeader.hide();
+ return;
+ }
+
+ setTimeout(function () {
+ $oldHeader.velocity('fadeIn', 250);
+ }, 250);
+ },
+
+ render: function () {
+ var containerClasses = 'header-control-box ' +
+ 'control-toggle-alternative-header ' + this.state.toggleClass;
+ var innerClasses = '';
+
+ var headerbar = null;
+ if (this.state.isToggled) {
+ headerbar = (<BulkDocumentHeaderController key={1} />);
+ }
+
+ return (
+ <div>
+ <div>
+ <ToggleHeaderButton
+ fonticon={'fonticon-ok-circled'}
+ toggleCallback={this.toggleCallback}
+ containerClasses={containerClasses}
+ innerClasses={innerClasses}
+ text={'Select'} />
+ <ReactCSSTransitionGroup transitionName={'fade'}>
+ {headerbar}
+ </ReactCSSTransitionGroup>
+ </div>
+ </div>
+ );
+ }
+ });
+
+ var Views = {
+ renderHeaderController: function (el) {
+ React.render(<HeaderBarController/>, el);
+ },
+ removeHeaderController: function (el) {
+ React.unmountComponentAtNode(el);
+ },
+ BulkDocumentHeaderController: BulkDocumentHeaderController,
+ HeaderBarController: HeaderBarController,
+ ToggleHeaderButton: ToggleHeaderButton
+ };
+
+ return Views;
+
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/app/addons/documents/header/header.stores.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/header/header.stores.js b/app/addons/documents/header/header.stores.js
new file mode 100644
index 0000000..c491043
--- /dev/null
+++ b/app/addons/documents/header/header.stores.js
@@ -0,0 +1,150 @@
+// 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/header/header.actiontypes'
+],
+
+function (FauxtonAPI, ActionTypes) {
+ var Stores = {};
+
+ Stores.BulkDocumentHeaderStore = FauxtonAPI.Store.extend({
+ initialize: function () {
+ this.reset();
+ },
+
+ reset: function () {
+ this._collapsedDocuments = false;
+ this._selectedAllDocuments = false;
+
+ this._selectedDocumentsCount = 0;
+ this._documentsOnPageCount = FauxtonAPI.constants.MISC.DEFAULT_PAGE_SIZE;
+ },
+
+ toggleCollapse: function () {
+ this._collapsedDocuments = !this._collapsedDocuments;
+ },
+
+ getCollapsedState: function () {
+ return this._collapsedDocuments;
+ },
+
+ toggleSelectAll: function () {
+ this._selectedAllDocuments = !this._selectedAllDocuments;
+ },
+
+ getSelectedAllState: function () {
+ return this._selectedAllDocuments;
+ },
+
+ getIsDeselectPossible: function () {
+ if (this._selectedDocumentsonPageCount > 0) {
+ return true;
+ }
+ return false;
+ },
+
+ getIsSelectAllPossible: function () {
+ if (this._selectedDocumentsonPageCount < this._documentsOnPageCount) {
+ return true;
+ }
+ return false;
+ },
+
+ setSelectedDocumentCount: function (options) {
+ this._selectedDocumentsonPageCount = options.selectedOnPage;
+ this._documentsOnPageCount = options.documentsOnPageCount;
+ },
+
+ dispatch: function (action) {
+ switch (action.type) {
+
+ case ActionTypes.COLLAPSE_DOCUMENTS:
+ this.toggleCollapse();
+ this.triggerChange();
+ break;
+
+ case ActionTypes.UPDATE_DOCUMENT_COUNT:
+ this.setSelectedDocumentCount(action.options);
+ this.triggerChange();
+ break;
+
+ case ActionTypes.RESET_HEADER_BAR:
+ this.reset();
+ this.triggerChange();
+ break;
+
+ default:
+ return;
+ }
+ }
+
+ });
+
+ Stores.HeaderBarStore = FauxtonAPI.Store.extend({
+ initialize: function (options) {
+ this.reset();
+ },
+
+ reset: function () {
+ this._isToggled = false;
+ },
+
+ toogleStatus: function () {
+ this._isToggled = !this._isToggled;
+ },
+
+ toggleClass: function () {
+ this._toggleClass = '';
+
+ if (this._isToggled) {
+ this._toggleClass = 'js-headerbar-togglebutton-selected';
+ }
+ },
+
+ getToggleStatus: function () {
+ return this._isToggled;
+ },
+
+ getToggleClass: function () {
+ return this._toggleClass;
+ },
+
+ dispatch: function (action) {
+ switch (action.type) {
+ case ActionTypes.TOGGLE_HEADER_CONTROLS:
+ this.toogleStatus();
+ this.toggleClass();
+ this.triggerChange();
+ break;
+
+ case ActionTypes.RESET_HEADER_BAR:
+ this.reset();
+ this.toggleClass();
+ this.triggerChange();
+ break;
+
+ default:
+ return;
+ }
+ }
+ });
+
+ Stores.headerBarStore = new Stores.HeaderBarStore();
+ Stores.headerBarStore.dispatchToken = FauxtonAPI.dispatcher.register(Stores.headerBarStore.dispatch);
+
+ Stores.bulkDocumentHeaderStore = new Stores.BulkDocumentHeaderStore();
+ Stores.bulkDocumentHeaderStore.dispatchToken = FauxtonAPI.dispatcher.register(Stores.bulkDocumentHeaderStore.dispatch);
+
+ return Stores;
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/app/addons/documents/routes-documents.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/routes-documents.js b/app/addons/documents/routes-documents.js
index c776a48..b88ee22 100644
--- a/app/addons/documents/routes-documents.js
+++ b/app/addons/documents/routes-documents.js
@@ -170,6 +170,8 @@ function(app, FauxtonAPI, BaseRoute, Documents, Changes, Index, DocEditor, Datab
return;
}
+ this.reactHeader = this.setView('#react-headerbar', new Documents.Views.ReactHeaderbar());
+
this.footer = this.setView('#footer', new Documents.Views.Footer());
this.leftheader.updateCrumbs(this.getCrumbs(this.database));
@@ -519,6 +521,9 @@ function(app, FauxtonAPI, BaseRoute, Documents, Changes, Index, DocEditor, Datab
},
cleanup: function () {
+ if (this.reactHeader) {
+ this.removeView('#react-headerbar');
+ }
if (this.viewEditor) {
this.removeView('#dashboard-upper-content');
}
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/app/addons/documents/templates/all_docs_list.html
----------------------------------------------------------------------
diff --git a/app/addons/documents/templates/all_docs_list.html b/app/addons/documents/templates/all_docs_list.html
index 7f7a2ca..3a29d96 100644
--- a/app/addons/documents/templates/all_docs_list.html
+++ b/app/addons/documents/templates/all_docs_list.html
@@ -13,20 +13,6 @@
%>
<div class="view">
- <% if (!viewList) { %>
- <div class="row">
- <div class="btn-toolbar span6">
- <button type="button" class="btn btn-small js-all" data-toggle="button">✓ All</button>
- <button class="btn btn-small disabled js-bulk-delete"><i class="icon-trash"></i></button>
- <% if (expandDocs) { %>
- <button id="collapse" class="btn btn-small"><i class="icon-minus"></i> Collapse</button>
- <% } else { %>
- <button id="collapse" class="btn btn-small"><i class="icon-plus"></i> Expand</button>
- <% } %>
- </div>
- </div>
- <% } %>
-
<div id="doc-list"></div>
<% if (endOfResults) { %>
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/app/addons/documents/tests/headerSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/headerSpec.react.jsx b/app/addons/documents/tests/headerSpec.react.jsx
new file mode 100644
index 0000000..874cf84
--- /dev/null
+++ b/app/addons/documents/tests/headerSpec.react.jsx
@@ -0,0 +1,146 @@
+// 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/header/header.react',
+ 'addons/documents/header/header.stores',
+ 'addons/documents/header/header.actions',
+
+ // importing the legacy
+ 'addons/documents/views',
+ 'addons/documents/resources',
+ 'addons/databases/base',
+ 'addons/fauxton/components',
+
+ 'testUtils',
+ 'react'
+], function (FauxtonAPI, Views, Stores, Actions, Documents, Resources, Databases, Components, utils, React) {
+
+ var assert = utils.assert;
+ var TestUtils = React.addons.TestUtils;
+
+ describe('Header Togglebutton', function () {
+ var container, toggleEl, toggleCallback;
+ beforeEach(function () {
+ container = document.createElement('div');
+ toggleCallback = sinon.spy();
+ toggleEl = TestUtils.renderIntoDocument(<Views.ToggleHeaderButton fonticon={'foo'}
+ classString={'bar'} toggleCallback={toggleCallback} />, container);
+ });
+
+ afterEach(function () {
+ React.unmountComponentAtNode(container);
+ });
+
+ it('should call the passed callback', function () {
+ TestUtils.Simulate.click(toggleEl.getDOMNode());
+ assert.ok(toggleCallback.calledOnce);
+ });
+ });
+
+ describe('Header Controller', function () {
+ var container, toggleEl;
+ beforeEach(function () {
+ container = document.createElement('div');
+ Actions.resetHeaderController();
+ });
+
+ afterEach(function () {
+ React.unmountComponentAtNode(container);
+ });
+
+ it('should use the passed classname', function () {
+ toggleEl = TestUtils.renderIntoDocument(<Views.HeaderBarController />, container);
+ var $el = $(toggleEl.getDOMNode()).find('.control-toggle-alternative-header');
+ TestUtils.Simulate.click($el[0]);
+ assert.ok($el.hasClass('js-headerbar-togglebutton-selected'));
+ });
+
+ it('should not render the alternative header if the button is not clicked', function () {
+ toggleEl = TestUtils.renderIntoDocument(<Views.HeaderBarController />, container);
+ var $el = $(toggleEl.getDOMNode()).find('.control-toggle-alternative-header');
+ assert.equal($(toggleEl.getDOMNode()).find('.alternative-header').length, 0);
+ });
+
+ it('should render the alternative header', function () {
+ toggleEl = TestUtils.renderIntoDocument(<Views.HeaderBarController />, container);
+ var $el = $(toggleEl.getDOMNode()).find('.control-toggle-alternative-header');
+ TestUtils.Simulate.click($el[0]);
+ assert.equal($(toggleEl.getDOMNode()).find('.alternative-header').length, 1);
+ });
+ });
+
+ describe('Bulkdocument Headerbar Controller', function () {
+ var container, header, viewSandbox, bulkDeleteDocCollection;
+ beforeEach(function () {
+ // needed for "pressing SelectAll should fill the delete-bulk-docs-collection"
+ var ViewSandbox = utils.ViewSandbox;
+ var database = new Databases.Model({id: 'registry'});
+ bulkDeleteDocCollection = new Resources.BulkDeleteDocCollection([], {databaseId: 'registry'});
+
+
+ database.allDocs = new Resources.AllDocs({_id: "ente"}, {
+ database: database,
+ viewMeta: {update_seq: 1},
+ params: {}
+ });
+
+ var pagination = new Components.IndexPagination({
+ collection: database.allDocs,
+ scrollToSelector: '#dashboard-content',
+ docLimit: 20,
+ perPage: 20
+ });
+
+ var allDocsNumber = new Documents.Views.AllDocsNumber({
+ collection: database.allDocs,
+ pagination: pagination,
+ perPageDefault: 20
+ });
+
+ var view = new Documents.Views.AllDocsList({
+ viewList: false,
+ bulkDeleteDocsCollection: bulkDeleteDocCollection,
+ collection: database.allDocs,
+ pagination: pagination,
+ allDocsNumber: allDocsNumber
+ });
+
+ viewSandbox = new ViewSandbox();
+ viewSandbox.renderView(view);
+
+
+ container = document.createElement('div');
+ });
+
+ afterEach(function () {
+ bulkDeleteDocCollection.off();
+ viewSandbox.remove();
+
+ React.unmountComponentAtNode(container);
+ });
+
+ it('should trigger an event to communicate with the backbone elements', function () {
+ var spy = sinon.spy(FauxtonAPI.Events, 'trigger');
+ header = TestUtils.renderIntoDocument(<Views.BulkDocumentHeaderController />, container);
+ TestUtils.Simulate.click($(header.getDOMNode()).find('.control-collapse')[0]);
+
+ assert.ok(spy.calledOnce);
+ });
+
+ it('pressing SelectAll should fill the delete-bulk-docs-collection', function () {
+ TestUtils.Simulate.click($(header.getDOMNode()).find('.control-select-all')[0]);
+
+ assert.equal(bulkDeleteDocCollection.length, 1);
+ });
+ });
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/app/addons/documents/tests/nightwatch/bulkDelete.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/nightwatch/bulkDelete.js b/app/addons/documents/tests/nightwatch/bulkDelete.js
index 6cb3894..aeb1651 100644
--- a/app/addons/documents/tests/nightwatch/bulkDelete.js
+++ b/app/addons/documents/tests/nightwatch/bulkDelete.js
@@ -23,9 +23,11 @@ module.exports = {
.createDocument(newDocumentName1, newDatabaseName)
.createDocument(newDocumentName2, newDatabaseName)
.url(baseUrl + '/#/database/' + newDatabaseName + '/_all_docs')
- .waitForElementPresent('.js-all', waitTime, false)
- .click('.js-all')
- .click('.js-bulk-delete')
+ .waitForElementPresent('.control-toggle-alternative-header', waitTime, false)
+ .click('.control-toggle-alternative-header')
+ .waitForElementPresent('.control-select-all', waitTime, false)
+ .click('.control-select-all')
+ .click('.control-delete')
.acceptAlert()
.waitForElementVisible('#global-notifications .alert.alert-info', waitTime, false)
.waitForElementNotPresent('[data-id="' + newDocumentName1 + '"]', waitTime, false)
@@ -50,12 +52,14 @@ module.exports = {
.loginToGUI()
.createManyDocuments(25, newDatabaseName)
.url(baseUrl + '/#/database/' + newDatabaseName + '/_all_docs')
- .waitForElementVisible('.js-all', waitTime, false)
- .click('.js-all')
+ .waitForElementPresent('.control-toggle-alternative-header', waitTime, false)
+ .click('.control-toggle-alternative-header')
+ .waitForElementPresent('.control-select-all', waitTime, false)
+ .click('.control-select-all')
.click('#next')
.waitForElementVisible('[data-id="27"]', waitTime, false)
.click('#previous')
- .waitForElementPresent('.js-all.active', waitTime, false)
+ .waitForElementPresent('.control-select-all.js-headerbar-togglebutton-selected', waitTime, false)
.end();
}
};
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/app/addons/documents/tests/nightwatch/deletesDocument.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/nightwatch/deletesDocument.js b/app/addons/documents/tests/nightwatch/deletesDocument.js
index 47b6b94..744933e 100644
--- a/app/addons/documents/tests/nightwatch/deletesDocument.js
+++ b/app/addons/documents/tests/nightwatch/deletesDocument.js
@@ -22,11 +22,12 @@ module.exports = {
.createDocument(newDocumentName, newDatabaseName)
.url(baseUrl)
.waitForElementPresent('#dashboard-content a[href="#/database/' + newDatabaseName + '/_all_docs"]', waitTime, false)
- .click('#dashboard-content a[href="#/database/'+newDatabaseName+'/_all_docs"]')
+ .click('#dashboard-content a[href="#/database/' + newDatabaseName + '/_all_docs"]')
.waitForElementVisible('label[for="checkbox-' + newDocumentName + '"]', waitTime, false)
.click('label[for="checkbox-' + newDocumentName + '"]')
- .waitForElementPresent('.js-bulk-delete:not(.disabled)', waitTime, false)
- .click('button.js-bulk-delete')
+ .click('.control-toggle-alternative-header')
+ .waitForElementPresent('.control-select-all', waitTime, false)
+ .click('.control-delete')
.acceptAlert()
.waitForElementVisible('#global-notifications .alert.alert-info', waitTime, false)
.url(baseUrl+'/'+newDatabaseName+'/_all_docs')
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/app/addons/documents/tests/viewsSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/tests/viewsSpec.js b/app/addons/documents/tests/viewsSpec.js
index 5e416bb..2114ed8 100644
--- a/app/addons/documents/tests/viewsSpec.js
+++ b/app/addons/documents/tests/viewsSpec.js
@@ -20,54 +20,9 @@ define([
ViewSandbox = testUtils.ViewSandbox,
viewSandbox;
- /* describe('AllDocsList', function () {
- var database = new Databases.Model({id: 'registry'}),
- bulkDeleteDocCollection = new Resources.BulkDeleteDocCollection([], {databaseId: 'registry'});
-
- database.allDocs = new Resources.AllDocs({_id: "ente"}, {
- database: database,
- viewMeta: {update_seq: 1},
- params: {}
- });
-
- var pagination = new Components.IndexPagination({
- collection: database.allDocs,
- scrollToSelector: '#dashboard-content',
- docLimit: 20,
- perPage: 20
- });
-
- var allDocsNumber = new Documents.Views.AllDocsNumber({
- collection: database.allDocs,
- pagination: pagination,
- perPageDefault: 20
- });
-
- var view = new Documents.Views.AllDocsList({
- viewList: false,
- bulkDeleteDocsCollection: bulkDeleteDocCollection,
- collection: database.allDocs,
- pagination: pagination,
- allDocsNumber: allDocsNumber
- });
-
- beforeEach(function (done) {
- viewSandbox = new ViewSandbox();
- viewSandbox.renderView(view, done);
- });
-
- afterEach(function () {
- viewSandbox.remove();
- });
-
+ describe('AllDocsList', function () {
it('should load', function () {
assert.equal(typeof Documents.Views.AllDocsList, 'function');
});
-
- it('pressing SelectAll should fill the delete-bulk-docs-collection', function () {
- assert.equal(bulkDeleteDocCollection.length, 0);
- view.$('button.js-all').trigger('click');
- assert.equal(bulkDeleteDocCollection.length, 1);
- });
- });*/
+ });
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/app/addons/documents/views.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/views.js b/app/addons/documents/views.js
index 1e3d039..5f0a1b7 100644
--- a/app/addons/documents/views.js
+++ b/app/addons/documents/views.js
@@ -21,11 +21,16 @@ define([
"addons/documents/shared-views",
"addons/documents/views-queryoptions",
+ // React
+ 'addons/documents/header/header.react',
+ 'addons/documents/header/header.actions',
+
//plugins
"plugins/prettify"
],
-function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions) {
+function (app, FauxtonAPI, Components, Documents,
+ Databases, Views, QueryOptions, ReactHeader, ReactHeaderActions) {
function showError (msg) {
FauxtonAPI.addNotification({
@@ -39,6 +44,21 @@ function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions)
template: "addons/documents/templates/all_docs_footer"
});
+ Views.ReactHeaderbar = FauxtonAPI.View.extend({
+ afterRender: function () {
+ ReactHeader.renderHeaderController(this.el);
+ },
+
+ cleanup: function () {
+ this.disableHeader();
+ ReactHeader.removeHeaderController(this.el);
+ },
+
+ disableHeader: function () {
+ ReactHeaderActions.resetHeaderController();
+ }
+ });
+
Views.RightAllDocsHeader = FauxtonAPI.View.extend({
className: "header-right",
template: "addons/documents/templates/all_docs_header",
@@ -299,15 +319,13 @@ function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions)
},
events: {
- 'click button.js-all': 'selectAll',
- "click button.js-bulk-delete": "bulkDelete",
- "click #collapse": "collapse",
'change input': 'toggleDocument',
"click #js-end-results": "openQueryOptionsTray"
},
initialize: function (options) {
this.rows = {};
+
this.viewList = !!options.viewList;
if (options.ddocInfo) {
@@ -335,6 +353,7 @@ function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions)
msg: 'Successfully deleted your docs',
clear: true
});
+
}.bind(this));
},
@@ -378,28 +397,11 @@ function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions)
}
$row.toggleClass('js-to-delete');
- this.toggleTrash();
- },
-
- toggleTrash: function () {
- var $bulkdDeleteButton = this.$('.js-bulk-delete');
-
- if (this.bulkDeleteDocsCollection && this.bulkDeleteDocsCollection.length > 0) {
- $bulkdDeleteButton.removeClass('disabled');
- } else {
- $bulkdDeleteButton.addClass('disabled');
- }
- },
-
- maybeHighlightAllButton: function () {
- if (this.$('.js-to-delete').length < this.$('.all-docs-item').length) {
- return;
- }
- if (!this.bulkDeleteDocsCollection || this.bulkDeleteDocsCollection.length === 0) {
- return;
- }
- this.$('.js-all').addClass('active');
+ ReactHeaderActions.updateDocumentCount({
+ selectedOnPage: this.$('.js-to-delete').length,
+ documentsOnPageCount: this.perPage()
+ });
},
openQueryOptionsTray: function(e) {
@@ -428,11 +430,14 @@ function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions)
});
},
- selectAll: function (evt) {
+ toggleSelectAll: function (on) {
+ this.selectAllBasedOnBoolean(on);
+ },
+
+ selectAllBasedOnBoolean: function (isActive) {
var $allDocs = this.$('#doc-list'),
$rows = $allDocs.find('.all-docs-item'),
$checkboxes = $rows.find('input:checkbox'),
- isActive = $(evt.target).hasClass('active'),
modelsAffected,
docs;
@@ -440,7 +445,15 @@ function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions)
$rows.toggleClass('js-to-delete', !isActive);
if (isActive) {
- this.bulkDeleteDocsCollection.reset();
+ modelsAffected = _.reduce($rows, function (acc, el) {
+ var docId = $(el).attr('data-id'),
+ model = this.collection.get(docId);
+
+ acc.push(model);
+ return acc;
+ }, [], this);
+
+ this.bulkDeleteDocsCollection.remove(modelsAffected);
} else {
modelsAffected = _.reduce($rows, function (acc, el) {
var docId = $(el).attr('data-id'),
@@ -452,20 +465,19 @@ function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions)
this.bulkDeleteDocsCollection.add(modelsAffected);
}
- this.toggleTrash();
+ ReactHeaderActions.updateDocumentCount({
+ selectedOnPage: this.$('.js-to-delete').length,
+ documentsOnPageCount: this.perPage()
+ });
},
serialize: function() {
return {
- viewList: this.viewList,
- expandDocs: this.expandDocs,
endOfResults: !this.pagination.canShowNextfn()
};
},
- collapse: function (event) {
- event.preventDefault();
-
+ collapse: function () {
if (this.expandDocs) {
this.expandDocs = false;
} else {
@@ -555,15 +567,21 @@ function(app, FauxtonAPI, Components, Documents, Databases, Views, QueryOptions)
prettyPrint();
+ this.stopListening(FauxtonAPI.Events);
+ this.listenTo(FauxtonAPI.Events, 'headerbar:collapse', this.collapse);
+ this.listenTo(FauxtonAPI.Events, 'headerbar:selectall', this.toggleSelectAll);
+ this.listenTo(FauxtonAPI.Events, 'headerbar:deleteselected', this.bulkDelete);
+
if (this.bulkDeleteDocsCollection) {
this.stopListening(this.bulkDeleteDocsCollection);
this.listenTo(this.bulkDeleteDocsCollection, 'error', this.showError);
this.listenTo(this.bulkDeleteDocsCollection, 'removed', this.removeDocuments);
- this.listenTo(this.bulkDeleteDocsCollection, 'updated', this.toggleTrash);
}
- this.toggleTrash();
- this.maybeHighlightAllButton();
+ ReactHeaderActions.updateDocumentCount({
+ selectedOnPage: this.$('.js-to-delete').length,
+ documentsOnPageCount: this.perPage()
+ });
},
perPage: function () {
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/app/app.js
----------------------------------------------------------------------
diff --git a/app/app.js b/app/app.js
index 9064b57..382fd99 100644
--- a/app/app.js
+++ b/app/app.js
@@ -43,7 +43,6 @@ function(app, $, _, Backbone, Bootstrap, Helpers, constants, Utils, FauxtonAPI,
};
}
-
// Provide a global location to place configuration settings and module
// creation also mix in Backbone.Events
_.extend(app, {
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/app/constants.js
----------------------------------------------------------------------
diff --git a/app/constants.js b/app/constants.js
index 7662b5b..5cef015 100644
--- a/app/constants.js
+++ b/app/constants.js
@@ -24,7 +24,7 @@ define([], function () {
DOCUMENT_LIMIT: 100
},
- // events
+ // global events for common used components
EVENTS: {
TRAY_CLOSED: 'tray:closed',
TRAY_OPENED: 'tray:opened',
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/app/templates/layouts/with_tabs_sidebar.html
----------------------------------------------------------------------
diff --git a/app/templates/layouts/with_tabs_sidebar.html b/app/templates/layouts/with_tabs_sidebar.html
index fd4aaa0..e6acfa3 100644
--- a/app/templates/layouts/with_tabs_sidebar.html
+++ b/app/templates/layouts/with_tabs_sidebar.html
@@ -18,6 +18,7 @@ the License.
<div class="header-wrapper">
<div id="breadcrumbs" class="sidebar"></div>
<div class="right-header-wrapper">
+ <div id="react-headerbar"></div>
<div id="api-navbar"></div>
<div id="right-header"></div>
</div>
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/assets/less/animations.less
----------------------------------------------------------------------
diff --git a/assets/less/animations.less b/assets/less/animations.less
index 8de09e7..cca377d 100644
--- a/assets/less/animations.less
+++ b/assets/less/animations.less
@@ -10,3 +10,17 @@
animation: @options;
}
+.fade {
+ transition: opacity .25s ease-in-out;
+ -moz-transition: opacity .25s ease-in-out;
+ -webkit-transition: opacity .25s ease-in-out;
+}
+
+
+.fadeIn {
+ opacity: 1;
+}
+
+.fadeOut {
+ opacity: 0;
+}
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/assets/less/fauxton.less
----------------------------------------------------------------------
diff --git a/assets/less/fauxton.less b/assets/less/fauxton.less
index 36259e7..3eead5d 100644
--- a/assets/less/fauxton.less
+++ b/assets/less/fauxton.less
@@ -28,7 +28,7 @@
@import "trays.less";
@import "mixins.less";
@import "animations.less";
-
+@import "react-animations.less";
/**
* HTML-wide overrides
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/25dab477/assets/less/react-animations.less
----------------------------------------------------------------------
diff --git a/assets/less/react-animations.less b/assets/less/react-animations.less
new file mode 100644
index 0000000..fbdbc44
--- /dev/null
+++ b/assets/less/react-animations.less
@@ -0,0 +1,17 @@
+.fade-enter {
+ opacity: 0;
+ transition: opacity .25s;
+
+ &.fade-enter-active {
+ opacity: 1;
+ }
+}
+
+.fade-leave {
+ opacity: 1;
+ transition: opacity .25s;
+
+ &.fade-leave-active {
+ opacity: 0;
+ }
+}
[2/2] fauxton commit: updated refs/heads/master to f5f7f4b
Posted by ro...@apache.org.
test: boot app in mocha tests again
got removed when we added react, we need it for tests
Project: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/commit/f5f7f4b9
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/f5f7f4b9
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/f5f7f4b9
Branch: refs/heads/master
Commit: f5f7f4b93a2ac9f63b21d9a0a5359285e6f2da38
Parents: 25dab47
Author: Robert Kowalski <ro...@apache.org>
Authored: Wed Jan 28 13:53:15 2015 +0100
Committer: Robert Kowalski <ro...@apache.org>
Committed: Wed Jan 28 18:32:59 2015 +0100
----------------------------------------------------------------------
test/test.config.underscore | 1 +
1 file changed, 1 insertion(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/f5f7f4b9/test/test.config.underscore
----------------------------------------------------------------------
diff --git a/test/test.config.underscore b/test/test.config.underscore
index c6e1d53..171aee1 100644
--- a/test/test.config.underscore
+++ b/test/test.config.underscore
@@ -5,6 +5,7 @@ require.config(
);
require([
+ 'app',
"es5-shim",
<% _.each(testFiles, function (test) {%>
<% if (test[0] === '.') { %>