You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ga...@apache.org on 2015/03/26 11:49:53 UTC
[3/3] fauxton commit: updated refs/heads/master to 96d31bc
Create Index Results
This converts the AllDocsList backbone view to React.
Project: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/commit/96d31bc7
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/tree/96d31bc7
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/diff/96d31bc7
Branch: refs/heads/master
Commit: 96d31bc7d0e7101c5a364fbaab2524d1e626e3d5
Parents: 2bc72f2
Author: Garren Smith <ga...@gmail.com>
Authored: Thu Mar 5 15:58:18 2015 +0200
Committer: Garren Smith <ga...@gmail.com>
Committed: Thu Mar 26 12:39:57 2015 +0200
----------------------------------------------------------------------
.travis.yml | 2 +-
.../components/react-components.react.jsx | 17 +-
app/addons/components/tests/docSpec.react.jsx | 8 +-
.../tests/nightwatch/checkDatabaseTooltip.js | 2 +-
app/addons/documents/header/header.actions.js | 26 +-
.../documents/header/header.actiontypes.js | 6 +-
app/addons/documents/header/header.react.jsx | 62 +--
app/addons/documents/header/header.stores.js | 75 +---
app/addons/documents/index-editor/actions.js | 7 +-
app/addons/documents/index-results/actions.js | 151 ++++++++
.../documents/index-results/actiontypes.js | 21 ++
.../index-results.components.react.jsx | 120 +++++-
app/addons/documents/index-results/stores.js | 270 +++++++++++++
.../tests/index-results.actionsSpec.js | 232 ++++++++++++
.../index-results.componentsSpec.react.jsx | 31 ++
.../tests/index-results.storesSpec.js | 372 ++++++++++++++++++
app/addons/documents/pagination/actions.js | 32 +-
app/addons/documents/pagination/actiontypes.js | 2 -
app/addons/documents/pagination/stores.js | 22 +-
.../pagination/tests/pagination.actionsSpec.js | 75 ++--
.../pagination/tests/paginationStoreSpec.js | 68 +++-
app/addons/documents/resources.js | 16 +-
app/addons/documents/routes-documents.js | 375 +++++++++----------
app/addons/documents/routes-index-editor.js | 35 +-
app/addons/documents/shared-routes.js | 71 ----
app/addons/documents/tests/actionsSpec.js | 14 +-
app/addons/documents/tests/headerSpec.react.jsx | 39 +-
.../tests/nightwatch/deletesDocument.js | 3 +-
.../documents/tests/nightwatch/paginateView.js | 14 +-
app/addons/documents/tests/resourcesSpec.js | 136 ++++---
app/addons/documents/tests/routeSpec.js | 11 -
app/addons/documents/tests/viewsSpec.js | 29 --
app/addons/documents/views.js | 260 +------------
assets/less/animations.less | 1 -
assets/less/react-animations.less | 18 +
test/mocha/testUtils.js | 9 +-
36 files changed, 1781 insertions(+), 851 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/.travis.yml
----------------------------------------------------------------------
diff --git a/.travis.yml b/.travis.yml
index 21e7bbe..0bab741 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,7 +18,7 @@ before_script:
- npm install -g grunt-cli
- grunt test
- grunt dev &
- - sleep 5
+ - sleep 10
script:
- grunt nightwatch
notifications:
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/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 b5727f9..a21c794 100644
--- a/app/addons/components/react-components.react.jsx
+++ b/app/addons/components/react-components.react.jsx
@@ -191,7 +191,13 @@ function (app, FauxtonAPI, React, Components, beautifyHelper) {
var Document = React.createClass({
propTypes: {
- docIdentifier: React.PropTypes.string.isRequired
+ docIdentifier: React.PropTypes.string.isRequired,
+ docChecked: React.PropTypes.func.isRequired
+ },
+
+ onChange: function (e) {
+ e.preventDefault();
+ this.props.docChecked(this.props.docIdentifier, this.props.doc, e);
},
getUrlFragment: function () {
@@ -213,19 +219,22 @@ function (app, FauxtonAPI, React, Components, beautifyHelper) {
id={'checkbox-' + this.props.docIdentifier}
checked={this.props.checked ? 'checked="checked"': null}
type="checkbox"
- onChange={this.props.onChange}
+ onChange={this.onChange}
className="js-row-select" />
- <label
+ <label onClick={this.onChange}
className="label-checkbox-doclist"
htmlFor={'checkbox-' + this.props.docIdentifier} />
</div>
);
},
+ onDoubleClick: function (e) {
+ this.props.onDoubleClick(this.props.docIdentifier, this.props.doc, e);
+ },
render: function () {
return (
- <div onDoubleClick={this.props.onDoubleClick} className="doc-row">
+ <div data-id={this.props.docIdentifier} onDoubleClick={this.onDoubleClick} className="doc-row">
<div className="custom-inputs">
{this.getCheckbox()}
</div>
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/app/addons/components/tests/docSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/components/tests/docSpec.react.jsx b/app/addons/components/tests/docSpec.react.jsx
index ce3a94c..119abe6 100644
--- a/app/addons/components/tests/docSpec.react.jsx
+++ b/app/addons/components/tests/docSpec.react.jsx
@@ -54,7 +54,7 @@ define([
<ReactComponents.Document checked={true} docIdentifier="foo" />,
container
);
- assert.equal($(el.getDOMNode()).find('#checkbox-foo').attr('checked'), 'checked');
+ assert.equal($(el.getDOMNode()).find('input[type="checkbox"]').attr('checked'), 'checked');
});
it('you can uncheck it', function () {
@@ -62,17 +62,17 @@ define([
<ReactComponents.Document docIdentifier="foo" />,
container
);
- assert.equal($(el.getDOMNode()).find('#checkbox-foo').attr('checked'), undefined);
+ assert.equal($(el.getDOMNode()).find('input[type="checkbox"]').attr('checked'), undefined);
});
it('it calls an onchange callback', function () {
var spy = sinon.spy();
el = TestUtils.renderIntoDocument(
- <ReactComponents.Document onChange={spy} docIdentifier="foo" />,
+ <ReactComponents.Document docChecked={spy} docIdentifier="foo" />,
container
);
- var testEl = $(el.getDOMNode()).find('#checkbox-foo')[0];
+ var testEl = $(el.getDOMNode()).find('input[type="checkbox"]')[0];
React.addons.TestUtils.Simulate.change(testEl, {target: {value: 'Hello, world'}});
assert.ok(spy.calledOnce);
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/app/addons/databases/tests/nightwatch/checkDatabaseTooltip.js
----------------------------------------------------------------------
diff --git a/app/addons/databases/tests/nightwatch/checkDatabaseTooltip.js b/app/addons/databases/tests/nightwatch/checkDatabaseTooltip.js
index 3808338..dc25f6a 100644
--- a/app/addons/databases/tests/nightwatch/checkDatabaseTooltip.js
+++ b/app/addons/databases/tests/nightwatch/checkDatabaseTooltip.js
@@ -34,7 +34,7 @@ module.exports = {
.waitForElementPresent('.control-select-all', waitTime, false)
.click('.control-delete')
.acceptAlert()
- .waitForElementVisible('#global-notifications .alert.alert-info', waitTime, false)
+ .waitForElementVisible('.alert.alert-info', waitTime, false)
.click('#nav-links a[href="#/_all_dbs"]')
// now let's look at the actual UI to confirm the tooltip appears
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/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
index 7be2a05..5a6dfe0 100644
--- a/app/addons/documents/header/header.actions.js
+++ b/app/addons/documents/header/header.actions.js
@@ -18,31 +18,37 @@ define([
function (app, FauxtonAPI, ActionTypes) {
return {
- toggleCollapseDocuments: function () {
+ collapseDocuments: function () {
FauxtonAPI.dispatch({
type: ActionTypes.COLLAPSE_DOCUMENTS
});
- FauxtonAPI.Events.trigger('headerbar:collapse');
},
- toggleSelectAllDocuments: function (on) {
- FauxtonAPI.Events.trigger('headerbar:selectall', on);
+ unCollapseDocuments: function () {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.EXPAND_DOCUMENTS
+ });
+
},
- updateDocumentCount: function (options) {
+ selectAllDocuments: function () {
FauxtonAPI.dispatch({
- type: ActionTypes.UPDATE_DOCUMENT_COUNT,
- options: options
+ type: ActionTypes.SELECT_ALL_DOCUMENTS
});
},
- deleteSelected: function () {
+ deSelectAllDocuments: function () {
FauxtonAPI.dispatch({
- type: ActionTypes.DELETE_SELECTED
+ type: ActionTypes.DESELECT_ALL_DOCUMENTS
});
+ },
- FauxtonAPI.Events.trigger('headerbar:deleteselected');
+ updateDocumentCount: function (options) {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.UPDATE_DOCUMENT_COUNT,
+ options: options
+ });
},
toggleHeaderControls: function () {
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/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
index 180dabb..18cc41f 100644
--- a/app/addons/documents/header/header.actiontypes.js
+++ b/app/addons/documents/header/header.actiontypes.js
@@ -14,9 +14,11 @@ define([], function () {
return {
UPDATE_DOCUMENT_COUNT: 'UPDATE_DOCUMENT_COUNT',
COLLAPSE_DOCUMENTS: 'COLLAPSE_DOCUMENTS',
+ EXPAND_DOCUMENTS: 'UNCOLLAPSE_DOCUMENTS',
TOGGLE_HEADER_CONTROLS: 'TOGGLE_HEADER_CONTROLS',
RESET_HEADER_BAR: 'RESET_HEADER_BAR',
- DELETE_SELECTED_DOCUMENTS: 'DELETE_SELECTED_DOCUMENTS'
+ DELETE_SELECTED_DOCUMENTS: 'DELETE_SELECTED_DOCUMENTS',
+ SELECT_ALL_DOCUMENTS: 'SELECT_ALL_DOCUMENTS',
+ DESELECT_ALL_DOCUMENTS: 'DESELECT_ALL_DOCUMENTS'
};
});
-
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/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
index 5906974..a5a1f5d 100644
--- a/app/addons/documents/header/header.react.jsx
+++ b/app/addons/documents/header/header.react.jsx
@@ -17,20 +17,24 @@ define([
'addons/documents/header/header.stores',
'addons/documents/header/header.actions',
'addons/components/react-components.react',
+ 'addons/documents/index-results/stores',
+ 'addons/documents/index-results/actions',
],
-function (app, FauxtonAPI, React, Stores, Actions, ReactComponents) {
+function (app, FauxtonAPI, React, Stores, Actions, ReactComponents, IndexResultsStore, IndexResultsActions) {
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
var headerBarStore = Stores.headerBarStore;
var bulkDocumentHeaderStore = Stores.bulkDocumentHeaderStore;
+ var indexResultsStore = IndexResultsStore.indexResultsStore;
var ToggleHeaderButton = ReactComponents.ToggleHeaderButton;
var BulkDocumentHeaderController = React.createClass({
getStoreState: function () {
return {
- areDocumentsCollapsed: bulkDocumentHeaderStore.getCollapsedState(),
- isDeselectPossible: bulkDocumentHeaderStore.getIsDeselectPossible(),
- isSelectAllPossible: bulkDocumentHeaderStore.getIsSelectAllPossible()
+ canCollapseDocs: indexResultsStore.canCollapseDocs(),
+ canUncollapseDocs: indexResultsStore.canUncollapseDocs(),
+ canDeselectAll: indexResultsStore.canDeselectAll(),
+ canSelectAll: indexResultsStore.canSelectAll()
};
},
@@ -39,11 +43,11 @@ function (app, FauxtonAPI, React, Stores, Actions, ReactComponents) {
},
componentDidMount: function () {
- bulkDocumentHeaderStore.on('change', this.onChange, this);
+ indexResultsStore.on('change', this.onChange, this);
},
componentWillUnmount: function () {
- bulkDocumentHeaderStore.off('change', this.onChange);
+ indexResultsStore.off('change', this.onChange);
},
onChange: function () {
@@ -52,9 +56,10 @@ function (app, FauxtonAPI, React, Stores, Actions, ReactComponents) {
render: function () {
var baseClass = 'header-control-box header-control-square ',
- isDeselectPossible = this.state.isDeselectPossible,
- isSelectAllPossible = this.state.isSelectAllPossible,
- areDocumentsCollapsed = this.state.areDocumentsCollapsed;
+ canDeselectAll = this.state.canDeselectAll,
+ canSelectAll = this.state.canSelectAll,
+ canCollapseDocs = this.state.canCollapseDocs,
+ canUncollapseDocs = this.state.canUncollapseDocs;
return (
<div className='alternative-header'>
@@ -64,8 +69,8 @@ function (app, FauxtonAPI, React, Stores, Actions, ReactComponents) {
innerClasses={''}
containerClasses={baseClass + 'control-select-all'}
text={''}
- setEnabledClass={!isSelectAllPossible}
- disabled={!isSelectAllPossible}
+ setEnabledClass={!canSelectAll}
+ disabled={!canSelectAll}
title={'Select all Documents'} />
<ToggleHeaderButton
@@ -74,29 +79,30 @@ function (app, FauxtonAPI, React, Stores, Actions, ReactComponents) {
innerClasses={''}
containerClasses={baseClass + 'control-de-select-all'}
text={''}
- setEnabledClass={!isDeselectPossible}
- disabled={!isDeselectPossible}
+ setEnabledClass={!canDeselectAll}
+ disabled={!canDeselectAll}
title={'Deselect all Documents'} />
+
<ToggleHeaderButton
fonticon={'fonticon-collapse'}
- toggleCallback={this.toggleCollapseDocuments}
+ toggleCallback={this.collapseDocuments}
innerClasses={''}
containerClasses={baseClass + 'control-collapse'}
text={''}
- setEnabledClass={areDocumentsCollapsed}
- disabled={areDocumentsCollapsed}
- title={'Collapse all'} />
+ setEnabledClass={!canCollapseDocs}
+ disabled={!canCollapseDocs}
+ title={'Collapse Selected'} />
<ToggleHeaderButton
fonticon={'fonticon-expand'}
- toggleCallback={this.toggleCollapseDocuments}
+ toggleCallback={this.unCollapseDocuments}
innerClasses={''}
containerClasses={baseClass + 'control-expand'}
text={''}
- setEnabledClass={!areDocumentsCollapsed}
- disabled={!areDocumentsCollapsed}
- title={'Expand all'} />
+ setEnabledClass={!canUncollapseDocs}
+ disabled={!canUncollapseDocs}
+ title={'Expand Selected'} />
<ToggleHeaderButton
fonticon={'fonticon-trash'}
@@ -117,16 +123,20 @@ function (app, FauxtonAPI, React, Stores, Actions, ReactComponents) {
);
},
- toggleCollapseDocuments: function () {
- Actions.toggleCollapseDocuments();
+ collapseDocuments: function () {
+ Actions.collapseDocuments();
+ },
+
+ unCollapseDocuments: function () {
+ Actions.unCollapseDocuments();
},
selectAllDocuments: function () {
- Actions.toggleSelectAllDocuments(false);
+ Actions.selectAllDocuments();
},
deSelectAllDocuments: function () {
- Actions.toggleSelectAllDocuments(true);
+ Actions.deSelectAllDocuments();
},
cancelView: function () {
@@ -134,7 +144,7 @@ function (app, FauxtonAPI, React, Stores, Actions, ReactComponents) {
},
deleteSelected: function () {
- Actions.deleteSelected();
+ IndexResultsActions.deleteSelected();
}
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/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
index 4085b0e..bc57a34 100644
--- a/app/addons/documents/header/header.stores.js
+++ b/app/addons/documents/header/header.stores.js
@@ -12,79 +12,13 @@
define([
'api',
- 'addons/documents/header/header.actiontypes'
+ 'addons/documents/header/header.actiontypes',
+ 'addons/documents/index-results/actiontypes'
],
-function (FauxtonAPI, ActionTypes) {
+function (FauxtonAPI, ActionTypes, IndexResultsActions) {
var Stores = {};
- Stores.BulkDocumentHeaderStore = FauxtonAPI.Store.extend({
- initialize: function () {
- this.reset();
- },
-
- reset: function () {
- this._collapsedDocuments = false;
- this._selectedDocumentsCount = 0;
- this._documentsOnPageCount = FauxtonAPI.constants.MISC.DEFAULT_PAGE_SIZE;
- },
-
- toggleCollapse: function () {
- this._collapsedDocuments = !this._collapsedDocuments;
- },
-
- getCollapsedState: function () {
- return this._collapsedDocuments;
- },
-
- 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();
@@ -137,8 +71,5 @@ function (FauxtonAPI, ActionTypes) {
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/96d31bc7/app/addons/documents/index-editor/actions.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-editor/actions.js b/app/addons/documents/index-editor/actions.js
index 263849b..84bf535 100644
--- a/app/addons/documents/index-editor/actions.js
+++ b/app/addons/documents/index-editor/actions.js
@@ -14,9 +14,10 @@ define([
'app',
'api',
'addons/documents/resources',
- 'addons/documents/index-editor/actiontypes'
+ 'addons/documents/index-editor/actiontypes',
+ 'addons/documents/index-results/actions'
],
-function (app, FauxtonAPI, Documents, ActionTypes) {
+function (app, FauxtonAPI, Documents, ActionTypes, IndexResultsActions) {
var ActionHelpers = {
createNewDesignDoc: function (id, database) {
var designDoc = {
@@ -125,7 +126,7 @@ function (app, FauxtonAPI, Documents, ActionTypes) {
FauxtonAPI.navigate(fragment, {trigger: true});
}
- FauxtonAPI.triggerRouteEvent('updateAllDocs', {ddoc: designDoc.id, view: viewInfo.viewName});
+ IndexResultsActions.reloadResultsList();
});
}
},
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/app/addons/documents/index-results/actions.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/actions.js b/app/addons/documents/index-results/actions.js
new file mode 100644
index 0000000..3e882eb
--- /dev/null
+++ b/app/addons/documents/index-results/actions.js
@@ -0,0 +1,151 @@
+// 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/index-results/actiontypes',
+ 'addons/documents/index-results/stores',
+ 'addons/documents/header/header.stores',
+ 'addons/documents/header/header.actions',
+ 'addons/documents/resources'
+],
+function (app, FauxtonAPI, ActionTypes, Stores, HeaderStores, HeaderActions, Documents) {
+ var indexResultsStore = Stores.indexResultsStore;
+ var headerBarStore = HeaderStores.headerBarStore;
+
+ var errorMessage = function (ids) {
+ var msg = 'Failed to delete your document!';
+
+ if (ids) {
+ msg = 'Failed to delete: ' + ids.join(', ');
+ }
+
+ FauxtonAPI.addNotification({
+ msg: msg,
+ type: 'error',
+ clear: true
+ });
+ };
+
+ return {
+ newResultsList: function (options) {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.INDEX_RESULTS_NEW_RESULTS,
+ options: options
+ });
+
+ if (!options.collection.fetch) { return; }
+
+ return options.collection.fetch({reset: true}).then(function () {
+ this.resultsListReset();
+ }.bind(this), function (xhr) {
+ // TODO: handle error requests that slip through
+ // This should just throw a notification, not break the page
+ var errorMsg = 'Bad Request';
+
+ try {
+ var responseText = JSON.parse(xhr.responseText);
+ if (responseText.reason) {
+ errorMsg = responseText.reason;
+ }
+ } catch (e) {
+ console.log(e);
+ }
+
+ FauxtonAPI.addNotification({
+ msg: errorMsg,
+ type: "error",
+ clear: true
+ });
+ });
+ },
+
+ reloadResultsList: function () {
+ return this.newResultsList({
+ collection: indexResultsStore.getCollection(),
+ deleteable: indexResultsStore.isDeleteable()
+ });
+ },
+
+ resultsListReset: function () {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.INDEX_RESULTS_RESET
+ });
+ },
+
+ selectDoc: function (id) {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.INDEX_RESULTS_SELECT_DOC,
+ id: id
+ });
+
+ //show menu
+ if (!headerBarStore.getToggleStatus()) {
+ HeaderActions.toggleHeaderControls();
+ return;
+ }
+
+ //hide menu
+ if (headerBarStore.getToggleStatus() && indexResultsStore.getSelectedItemsLength() === 0) {
+ HeaderActions.toggleHeaderControls();
+ }
+ },
+
+ selectListOfDocs: function (ids) {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.INDEX_RESULTS_SELECT_LIST_OF_DOCS,
+ ids: ids
+ });
+ },
+
+ clearResults: function () {
+ FauxtonAPI.dispatch({
+ type: ActionTypes.INDEX_RESULTS_CLEAR_RESULTS
+ });
+ },
+
+ deleteSelected: function () {
+ var itemsLength = indexResultsStore.getSelectedItemsLength();
+ var msg = "Are you sure you want to delete these " + itemsLength + " docs?";
+
+ if (itemsLength === 0 || !window.confirm(msg)) {
+ return false;
+ }
+
+ var reloadResultsList = _.bind(this.reloadResultsList, this);
+ var selectListOfDocs = _.bind(this.selectListOfDocs, this);
+ var selectedIds = [];
+
+ indexResultsStore.createBulkDeleteFromSelected().bulkDelete()
+ .then(function (ids) {
+ FauxtonAPI.addNotification({
+ msg: 'Successfully deleted your docs',
+ clear: true
+ });
+
+ if (!_.isEmpty(ids.errorIds)) {
+ errorMessage(ids.errorIds);
+ selectedIds = ids.errorIds;
+ }
+ }, function (ids) {
+ errorMessage(ids);
+ selectedIds = ids;
+ })
+ .always(function () {
+ reloadResultsList().then(function () {
+ selectListOfDocs(selectedIds);
+ });
+ });
+ }
+ };
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/app/addons/documents/index-results/actiontypes.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/actiontypes.js b/app/addons/documents/index-results/actiontypes.js
new file mode 100644
index 0000000..72157c7
--- /dev/null
+++ b/app/addons/documents/index-results/actiontypes.js
@@ -0,0 +1,21 @@
+// 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 {
+ INDEX_RESULTS_NEW_RESULTS: 'INDEX_RESULTS_NEW_RESULTS',
+ INDEX_RESULTS_RESET: 'INDEX_RESULTS_RESET',
+ INDEX_RESULTS_SELECT_DOC: 'INDEX_RESULTS_SELECT_DOC',
+ INDEX_RESULTS_SELECT_LIST_OF_DOCS: 'INDEX_RESULTS_SELECT_LIST_OF_DOCS',
+ INDEX_RESULTS_CLEAR_RESULTS: 'INDEX_RESULTS_CLEAR_RESULTS'
+ };
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/app/addons/documents/index-results/index-results.components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/index-results.components.react.jsx b/app/addons/documents/index-results/index-results.components.react.jsx
index 8c77739..92fc018 100644
--- a/app/addons/documents/index-results/index-results.components.react.jsx
+++ b/app/addons/documents/index-results/index-results.components.react.jsx
@@ -13,10 +13,18 @@
define([
'app',
'api',
- 'react'
+ 'react',
+ 'addons/documents/index-results/stores',
+ 'addons/documents/index-results/actions',
+ 'addons/components/react-components.react',
+ 'addons/documents/resources',
+
+ "plugins/prettify"
],
-function (app, FauxtonAPI, React) {
+function (app, FauxtonAPI, React, Stores, Actions, Components, Documents) {
+ var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
+ var store = Stores.indexResultsStore;
var NoResultScreen = React.createClass({
render: function () {
@@ -28,10 +36,118 @@ function (app, FauxtonAPI, React) {
}
});
+ var ResultsScreen = React.createClass({
+
+ onDoubleClick: function (id, doc) {
+ FauxtonAPI.navigate(doc.url);
+ },
+
+ getUrlFragment: function (url) {
+ if (this.props.hasReduce) {
+ return null;
+ }
+
+
+ return (
+ <a href={url}>
+ <i className="fonticon-pencil"></i>
+ </a>);
+ },
+
+ getDocumentList: function () {
+ return _.map(this.props.results, function (doc) {
+ return (
+ <Components.Document
+ key={doc.id}
+ doc={doc}
+ onDoubleClick={this.onDoubleClick}
+ keylabel={doc.keylabel}
+ docContent={doc.content}
+ checked={this.props.isSelected(doc.id)}
+ docChecked={this.props.docChecked}
+ docIdentifier={doc.id} >
+ {this.getUrlFragment('#' + doc.url)}
+ </Components.Document>
+ );
+ }, this);
+ },
+
+ render: function () {
+ var classNames = 'view';
+
+ if (this.props.isDeleteable) {
+ classNames += ' show-select';
+ }
+
+ return (
+ <div className={classNames}>
+ <div id="doc-list">
+ <ReactCSSTransitionGroup transitionName={'slow-fade'}>
+ {this.getDocumentList()}
+ </ReactCSSTransitionGroup>
+ </div>
+ </div>
+ );
+ },
+
+ componentDidMount: function () {
+ prettyPrint();
+ },
+
+ componentDidUpdate: function () {
+ prettyPrint();
+ },
+
+ });
+
var ViewResultListController = React.createClass({
+ getStoreState: function () {
+ return {
+ hasResults: store.hasResults(),
+ results: store.getResults(),
+ isDeleteable: store.isDeleteable(),
+ isSelected: store.isSelected,
+ hasReduce: store.hasReduce()
+ };
+ },
+
+ isSelected: function (id) {
+ return this.state.isSelected(id);
+ },
+
+ 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());
+ },
+
+ docChecked: function (id) {
+ Actions.selectDoc(id);
+ },
+
render: function () {
var view = <NoResultScreen />;
+ if (this.state.hasResults) {
+ view = <ResultsScreen
+ isCollapsed={this.isCollapsed}
+ isSelected={this.isSelected}
+ hasReduce={this.state.hasReduce}
+ isDeleteable={this.state.isDeleteable}
+ docChecked={this.docChecked}
+ results={this.state.results} />;
+ }
+
return (
view
);
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/app/addons/documents/index-results/stores.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/stores.js b/app/addons/documents/index-results/stores.js
new file mode 100644
index 0000000..1f2382d
--- /dev/null
+++ b/app/addons/documents/index-results/stores.js
@@ -0,0 +1,270 @@
+// 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/index-results/actiontypes',
+ 'addons/documents/header/header.actiontypes',
+ "addons/documents/resources"
+],
+
+function (FauxtonAPI, ActionTypes, HeaderActionTypes, Documents) {
+ var Stores = {};
+
+ /*TODO:
+ remove header code, add delete, clean up pagination tests
+ */
+
+ Stores.IndexResultsStore = FauxtonAPI.Store.extend({
+
+ initialize: function () {
+ this._deleteable = false;
+ this._collection = [];
+ this.clearSelectedItems();
+ this.clearCollapsedDocs();
+ this._isLoading = false;
+ },
+
+ clearSelectedItems: function () {
+ this._selectedItems = {};
+ },
+
+ clearCollapsedDocs: function () {
+ this._collapsedDocs = {};
+ },
+
+ newResults: function (options) {
+ this._collection = options.collection;
+ this._deleteable = options.deleteable;
+ this.clearSelectedItems();
+ this.clearCollapsedDocs();
+ },
+
+ hasReduce: function () {
+ if (!this._collection || !this._collection.params) {
+ return false;
+ }
+ return this._collection.params.reduce;
+ },
+
+ getCollection: function () {
+ return this._collection;
+ },
+
+ getDocContent: function (originalDoc) {
+ var doc = originalDoc.toJSON();
+
+ if (this.isCollapsed(doc._id)) {
+ doc = {
+ _id: _.isUndefined(doc._id),
+ _rev: doc._rev
+ };
+ }
+
+ return JSON.stringify(doc, null, " ");
+ },
+
+ getDocId: function (doc) {
+
+ if (!_.isUndefined(doc.id)) {
+ return doc.id;
+ }
+
+ if (!_.isNull(doc.get('key'))) {
+ return doc.get('key').toString();
+ }
+
+ return '';
+ },
+
+ getResults: function () {
+ return this._collection.map(function (doc) {
+ return {
+ content: this.getDocContent(doc),
+ id: this.getDocId(doc),
+ keylabel: doc.isFromView() ? 'key' : 'id',
+ url: doc.isFromView() ? doc.url('app') : doc.url('web-index')
+ };
+ }, this);
+ },
+
+ hasResults: function () {
+ if (this._isLoading) { return this._isLoading; }
+ return this._collection.length > 0;
+ },
+
+ isDeleteable: function () {
+ return this._deleteable;
+ },
+
+ selectDoc: function (id) {
+ if (!this._selectedItems[id]) {
+ this._selectedItems[id] = true;
+ } else {
+ delete this._selectedItems[id];
+ }
+ },
+
+ selectListOfDocs: function (ids) {
+ this.clearSelectedItems();
+ _.each(ids, function (id) {
+ this.selectDoc(id);
+ }, this);
+ },
+
+ selectAllDocuments: function () {
+ this.clearSelectedItems();
+ this._collection.each(function (doc) {
+ this.selectDoc(doc.id);
+ }, this);
+ },
+
+ deSelectAllDocuments: function () {
+ this.clearSelectedItems();
+ },
+
+ getSelectedItemsLength: function () {
+ return _.keys(this._selectedItems).length;
+ },
+
+ getCollapsedDocsLength: function () {
+ return _.keys(this._collapsedDocs).length;
+ },
+
+ getCollapsedDocs: function () {
+ return this._collapsedDocs;
+ },
+
+ getDatabase: function () {
+ return this._collection.database;
+ },
+
+ createBulkDeleteFromSelected: function () {
+ var items = _.map(_.keys(this._selectedItems), function (id) {
+ var doc = this._collection.get(id);
+
+ return {
+ _id: doc.id,
+ _rev: doc.get('_rev'),
+ _deleted: true
+ };
+ }, this);
+
+ var bulkDelete = new Documents.BulkDeleteDocCollection(items, {
+ databaseId: this.getDatabase().safeID()
+ });
+
+ return bulkDelete;
+ },
+
+ canSelectAll: function () {
+ return this._collection.length > this.getSelectedItemsLength();
+ },
+
+ canDeselectAll: function () {
+ return this.getSelectedItemsLength() > 0;
+ },
+
+ getSelectedItems: function () {
+ return this._selectedItems;
+ },
+
+ canCollapseDocs: function () {
+ return this._collection.length > this.getCollapsedDocsLength();
+ },
+
+ canUncollapseDocs: function () {
+ return this.getCollapsedDocsLength() > 0;
+ },
+
+ isSelected: function (id) {
+ return !!this._selectedItems[id];
+ },
+
+ isCollapsed: function (id) {
+ return !!this._collapsedDocs[id];
+ },
+
+ collapseSelectedDocs: function () {
+ _.each(this._selectedItems, function (val, key) {
+ this._collapsedDocs[key] = true;
+ }, this);
+ },
+
+ unCollapseSelectedDocs: function () {
+ _.each(this._selectedItems, function (val, key) {
+ delete this._collapsedDocs[key];
+ }, this);
+ },
+
+ clearResultsBeforeFetch: function () {
+ this.getCollection().reset();
+ this._isLoading = true;
+ },
+
+ resultsResetFromFetch: function () {
+ this._isLoading = false;
+ },
+
+ dispatch: function (action) {
+ switch (action.type) {
+ case ActionTypes.INDEX_RESULTS_NEW_RESULTS:
+ this.newResults(action.options);
+ this.triggerChange();
+ break;
+ case ActionTypes.INDEX_RESULTS_RESET:
+ this.resultsResetFromFetch();
+ this.triggerChange();
+ break;
+ case ActionTypes.INDEX_RESULTS_SELECT_DOC:
+ this.selectDoc(action.id);
+ this.triggerChange();
+ break;
+ case ActionTypes.INDEX_RESULTS_SELECT_LIST_OF_DOCS:
+ this.selectListOfDocs(action.ids);
+ this.triggerChange();
+ break;
+ case ActionTypes.INDEX_RESULTS_CLEAR_RESULTS:
+ this.clearResultsBeforeFetch();
+ this.triggerChange();
+ break;
+ case HeaderActionTypes.SELECT_ALL_DOCUMENTS:
+ this.selectAllDocuments();
+ this.triggerChange();
+ break;
+ case HeaderActionTypes.DESELECT_ALL_DOCUMENTS:
+ this.deSelectAllDocuments();
+ this.triggerChange();
+ break;
+ case HeaderActionTypes.COLLAPSE_DOCUMENTS:
+ this.collapseSelectedDocs();
+ this.triggerChange();
+ break;
+ case HeaderActionTypes.EXPAND_DOCUMENTS:
+ this.unCollapseSelectedDocs();
+ this.triggerChange();
+ break;
+ default:
+ return;
+ // do nothing
+ }
+ }
+
+ });
+
+ Stores.indexResultsStore = new Stores.IndexResultsStore();
+
+ Stores.indexResultsStore.dispatchToken = FauxtonAPI.dispatcher.register(Stores.indexResultsStore.dispatch);
+
+ return Stores;
+
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/app/addons/documents/index-results/tests/index-results.actionsSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/tests/index-results.actionsSpec.js b/app/addons/documents/index-results/tests/index-results.actionsSpec.js
new file mode 100644
index 0000000..5ec9aab
--- /dev/null
+++ b/app/addons/documents/index-results/tests/index-results.actionsSpec.js
@@ -0,0 +1,232 @@
+// 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/index-results/actions',
+ 'addons/documents/index-results/stores',
+ 'addons/documents/header/header.stores',
+ 'addons/documents/header/header.actions',
+ 'addons/documents/resources',
+ 'testUtils',
+], function (FauxtonAPI, Actions, Stores, HeaderStores, HeaderActions, Documents, testUtils) {
+ var assert = testUtils.assert;
+ var restore = testUtils.restore;
+ var store = Stores.indexResultsStore;
+
+ FauxtonAPI.router = new FauxtonAPI.Router([]);
+
+ describe('Index Results Actions', function () {
+
+ describe('#newResultsList', function () {
+
+ it('sends results list reset', function () {
+ var collection = {
+ fetch: function () {
+ var promise = $.Deferred();
+ promise.resolve();
+ return promise;
+ }
+ };
+
+ var spy = sinon.spy(Actions, 'resultsListReset');
+
+ Actions.newResultsList({collection: collection});
+ assert.ok(spy.calledOnce);
+ });
+
+ });
+
+ });
+
+ describe('#selectDoc', function () {
+ afterEach(function () {
+ restore(HeaderStores.headerBarStore.getToggleStatus);
+ restore(HeaderActions.toggleHeaderControls);
+ });
+
+ it('toggles header controls if not active', function () {
+ var stub = sinon.stub(HeaderStores.headerBarStore, 'getToggleStatus');
+ stub.returns(false);
+
+ var spy = sinon.spy(HeaderActions, 'toggleHeaderControls');
+
+ Actions.selectDoc('id');
+ assert.ok(spy.calledOnce);
+ });
+
+ it('does not toggles header controls if active', function () {
+ store.clearSelectedItems();
+ var stub = sinon.stub(HeaderStores.headerBarStore, 'getToggleStatus');
+ stub.returns(true);
+
+ var spy = sinon.spy(HeaderActions, 'toggleHeaderControls');
+
+ Actions.selectDoc('id');
+ assert.notOk(spy.calledOnce);
+ });
+
+ it('hides header control if active and no items selected', function () {
+ var stub = sinon.stub(HeaderStores.headerBarStore, 'getToggleStatus');
+ stub.returns(true);
+ store._selectedItems = {'id': true};
+
+ var spy = sinon.spy(HeaderActions, 'toggleHeaderControls');
+
+ Actions.selectDoc('id');
+ assert.ok(spy.calledOnce);
+
+ });
+
+ });
+
+ describe('#deleteSelected', function () {
+ var confirmStub;
+
+ beforeEach(function () {
+ store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ store._selectedItems = {
+ 'testId1': true,
+ 'testId2': true
+ };
+
+ confirmStub = sinon.stub(window, 'confirm');
+ confirmStub.returns(true);
+
+ });
+
+ afterEach(function () {
+ restore(window.confirm);
+ restore(store.createBulkDeleteFromSelected);
+ restore(FauxtonAPI.addNotification);
+ restore(Actions.reloadResultsList);
+ restore(Actions.selectListOfDocs);
+ });
+
+ it('doesn\'t delete if user denies confirmation', function () {
+ window.confirm.restore();
+
+ var stub = sinon.stub(window, 'confirm');
+ stub.returns(false);
+
+ var spy = sinon.spy(store, 'createBulkDeleteFromSelected');
+
+ Actions.deleteSelected();
+
+ assert.notOk(spy.calledOnce);
+ });
+
+ it('creates bulk delete', function () {
+ var spy = sinon.spy(store, 'createBulkDeleteFromSelected');
+
+ Actions.deleteSelected();
+
+ assert.ok(spy.calledOnce);
+ });
+
+ it('on success notifies all deleted', function () {
+ var spy = sinon.spy(FauxtonAPI, 'addNotification');
+ var promise = FauxtonAPI.Deferred();
+ var ids = {
+ errorIds: []
+ };
+ var bulkDelete = {
+ bulkDelete: function () {
+ promise.resolve(ids);
+ return promise;
+ }
+ };
+ var stub = sinon.stub(store, 'createBulkDeleteFromSelected');
+ stub.returns(bulkDelete);
+
+ Actions.deleteSelected();
+
+ assert.ok(spy.calledOnce);
+ });
+
+ it('on success with some failed ids, re-selects failed', function () {
+ var spy = sinon.spy(Actions, 'selectListOfDocs');
+
+ var reloadResultsListStub = sinon.stub(Actions, 'reloadResultsList');
+ var stubPromise = FauxtonAPI.Deferred();
+ stubPromise.resolve();
+ reloadResultsListStub.returns(stubPromise);
+
+ var promise = FauxtonAPI.Deferred();
+ var ids = {
+ errorIds: ['1']
+ };
+ var bulkDelete = {
+ bulkDelete: function () {
+ promise.resolve(ids);
+ return promise;
+ }
+ };
+
+ var stub = sinon.stub(store, 'createBulkDeleteFromSelected');
+ stub.returns(bulkDelete);
+
+ Actions.deleteSelected();
+ assert.ok(spy.calledWith(ids.errorIds));
+ });
+
+ it('on failure notifies failed', function () {
+ var spy = sinon.spy(FauxtonAPI, 'addNotification');
+ var promise = FauxtonAPI.Deferred();
+ var bulkDelete = {
+ bulkDelete: function () {
+ promise.reject();
+ return promise;
+ }
+ };
+ var stub = sinon.stub(store, 'createBulkDeleteFromSelected');
+ stub.returns(bulkDelete);
+
+ Actions.deleteSelected();
+
+ assert.ok(spy.calledOnce);
+ });
+
+ it('on failure re-selects docs', function () {
+ var spy = sinon.spy(Actions, 'selectListOfDocs');
+
+ var reloadResultsListStub = sinon.stub(Actions, 'reloadResultsList');
+ var stubPromise = FauxtonAPI.Deferred();
+ stubPromise.resolve();
+ reloadResultsListStub.returns(stubPromise);
+
+ var promise = FauxtonAPI.Deferred();
+ var errorIds = ['1'];
+
+ var bulkDelete = {
+ bulkDelete: function () {
+ promise.reject(errorIds);
+ return promise;
+ }
+ };
+
+ var stub = sinon.stub(store, 'createBulkDeleteFromSelected');
+ stub.returns(bulkDelete);
+
+ Actions.deleteSelected();
+ assert.ok(spy.calledWith(errorIds));
+ });
+
+ });
+
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/app/addons/documents/index-results/tests/index-results.componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/tests/index-results.componentsSpec.react.jsx b/app/addons/documents/index-results/tests/index-results.componentsSpec.react.jsx
new file mode 100644
index 0000000..4502921
--- /dev/null
+++ b/app/addons/documents/index-results/tests/index-results.componentsSpec.react.jsx
@@ -0,0 +1,31 @@
+// 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/index-results/index-results.components.react',
+ 'testUtils',
+ "react"
+], function (FauxtonAPI, Views, utils, React) {
+ FauxtonAPI.router = new FauxtonAPI.Router([]);
+
+ var assert = utils.assert;
+ var TestUtils = React.addons.TestUtils;
+
+ describe('Index Results', function () {
+ var container;
+
+ afterEach(function () {
+ React.unmountComponentAtNode(container);
+ });
+
+ });
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/app/addons/documents/index-results/tests/index-results.storesSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/index-results/tests/index-results.storesSpec.js b/app/addons/documents/index-results/tests/index-results.storesSpec.js
new file mode 100644
index 0000000..574082f
--- /dev/null
+++ b/app/addons/documents/index-results/tests/index-results.storesSpec.js
@@ -0,0 +1,372 @@
+// 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/index-results/stores',
+ 'addons/documents/index-results/actiontypes',
+ 'addons/documents/shared-resources',
+ 'testUtils'
+], function (FauxtonAPI, Stores, ActionTypes, Documents, testUtils) {
+ var assert = testUtils.assert;
+ var dispatchToken;
+ var store;
+
+ describe('Index Results Store', function () {
+
+ beforeEach(function () {
+ store = new Stores.IndexResultsStore();
+ dispatchToken = FauxtonAPI.dispatcher.register(store.dispatch);
+ });
+
+ describe('#hasResults', function () {
+
+ it('returns true for collection', function () {
+ store._collection = [1, 2, 3];
+
+ assert.ok(store.hasResults());
+ });
+
+ it('returns false for empty collection', function () {
+ store._collection = [];
+
+ assert.notOk(store.hasResults());
+ });
+
+ });
+
+ describe('#getResults', function () {
+
+ it('has correct doc format', function () {
+ store._collection = new Documents.AllDocs([{_id: 'testId'}], {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ var doc = store.getResults()[0];
+ assert.equal(doc.id, 'testId');
+ assert.equal(doc.keylabel, 'id');
+ });
+
+ });
+
+ afterEach(function () {
+ FauxtonAPI.dispatcher.unregister(dispatchToken);
+ });
+ });
+
+ describe('canSelectAll', function () {
+
+ it('returns true for selected docs less than collection', function () {
+ store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ store._selectedItems = {'testId1': true};
+ assert.ok(store.canSelectAll());
+ });
+
+ it('returns false for selected docs same as collection', function () {
+ store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ store._selectedItems = {
+ 'testId1': true,
+ 'testId2': true
+ };
+
+ assert.notOk(store.canSelectAll());
+ });
+
+ });
+
+ describe('canDeselectAll', function () {
+
+ it('returns true for selected docs', function () {
+ store._selectedItems = {'testId1': true};
+ assert.ok(store.canDeselectAll());
+ });
+
+ it('returns false for no selected docs', function () {
+ store._selectedItems = {};
+
+ assert.notOk(store.canDeselectAll());
+ });
+
+ });
+
+ describe('canCollapseDocs', function () {
+
+ it('returns true for no collapsed docs', function () {
+ store._collapsedDocs = {};
+ assert.ok(store.canCollapseDocs());
+ });
+
+ it('returns false for all collapsed docs', function () {
+ store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ store._collapsedDocs = {
+ 'testId1': true,
+ 'testId2': true
+ };
+
+ assert.notOk(store.canCollapseDocs());
+ });
+
+ });
+
+ describe('canUncollapseDocs', function () {
+
+ it('returns true for collapsed docs', function () {
+ store._collapsedDocs = {'testId1': true};
+ assert.ok(store.canUncollapseDocs());
+ });
+
+ it('returns false for no collapsed docs', function () {
+ store.clearCollapsedDocs();
+
+ assert.notOk(store.canUncollapseDocs());
+ });
+
+ });
+
+ describe('getDocContent', function () {
+
+ it('returns full doc if not collapsed', function () {
+ store._collection = new Documents.AllDocs([{_id: 'testId1', 'value': 'one'}] , {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ var doc = store._collection.first();
+ var result = store.getDocContent(doc);
+
+ assert.equal(JSON.parse(result).value, 'one');
+ });
+
+ it('returns collapsed doc if collapsed', function () {
+ store._collection = new Documents.AllDocs([{_id: 'testId1', 'value': 'one'}] , {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ var doc = store._collection.first();
+ store._collapsedDocs = {'testId1': true};
+ var result = store.getDocContent(doc);
+
+ assert.ok(_.isUndefined(JSON.parse(result).value));
+ });
+
+ });
+
+ describe('#selectDoc', function () {
+
+ it('selects doc if not already selected', function () {
+ store._selectedItems = {};
+ store.selectDoc('id');
+ assert.equal(store.getSelectedItemsLength(), 1);
+ });
+
+ it('deselects doc if already selected', function () {
+ store._selectedItems = {'id': true};
+ store.selectDoc('id');
+ assert.equal(store.getSelectedItemsLength(), 0);
+ });
+ });
+
+ describe('#selectAllDocuments', function () {
+
+ it('selects all documents', function () {
+ store._collection = new Documents.AllDocs([{_id: 'testId1', 'value': 'one'}] , {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ store.selectAllDocuments();
+ assert.ok(store.getSelectedItems().testId1);
+ });
+
+ });
+
+ describe('#deSelectAllDocuments', function () {
+
+ it('deselects all documents', function () {
+ store._collection = new Documents.AllDocs([{_id: 'testId1', 'value': 'one'}] , {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ store.selectAllDocuments();
+ assert.ok(store.getSelectedItems().testId1);
+ store.deSelectAllDocuments();
+ assert.equal(store.getSelectedItemsLength(), 0);
+ });
+ });
+
+ describe('#collapseSelectedDocs', function () {
+
+ it('collapses all selected docs', function () {
+ store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ store.clearCollapsedDocs();
+
+ store._selectedItems = {
+ 'testId1': true,
+ 'testId2': true
+ };
+
+ store.collapseSelectedDocs();
+ assert.equal(store.getCollapsedDocsLength(), 2);
+ });
+
+ });
+
+ describe('#unCollapseSelectedDocs', function () {
+
+ it('uncollapses all selected docs', function () {
+ store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ store.clearCollapsedDocs();
+
+ store._selectedItems = {
+ 'testId1': true,
+ 'testId2': true
+ };
+
+ store.collapseSelectedDocs();
+ assert.equal(store.getCollapsedDocsLength(), 2);
+ store.unCollapseSelectedDocs();
+ assert.equal(store.getCollapsedDocsLength(), 0);
+ });
+ });
+
+ describe('#createBulkDeleteFromSelected', function () {
+
+ it('correctly creates BulkDeleteDocCollection', function () {
+ store._collection = new Documents.AllDocs([{_id: 'testId1'}, {_id: 'testId2'}], {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ store._selectedItems = {
+ 'testId1': true,
+ 'testId2': true
+ };
+
+ var bulkDelete = store.createBulkDeleteFromSelected();
+
+ assert.equal(bulkDelete.length, 2);
+ assert.ok(bulkDelete.at(0).get('_deleted'));
+ });
+
+ });
+
+ describe('#getDocId', function () {
+
+ it('returns id if it exists', function () {
+ var doc = new Documents.Doc({
+ _id: 'doc-id'
+ }, {
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ assert.equal(store.getDocId(doc), 'doc-id');
+
+ });
+
+ it('returns key if it exists', function () {
+ var doc = new Documents.Doc({
+ key: 'doc-key'
+ }, {
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ assert.equal(store.getDocId(doc), 'doc-key');
+
+ });
+
+ it('returns empty string if no key or id exists', function () {
+ var doc = new Documents.Doc({
+ key: null,
+ value: 'the-value'
+ }, {
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ assert.equal(store.getDocId(doc), '');
+
+ });
+ });
+
+ describe('hasReduce', function () {
+
+ it('returns false for no collection', function () {
+ store._collection = null;
+ assert.notOk(store.hasReduce());
+ });
+
+ it('returns false for no params', function () {
+ store._collection = [];
+ assert.notOk(store.hasReduce());
+ });
+
+ it('returns true for reduce param', function () {
+ store._collection = [];
+ store._collection.param = {
+ reduce: true
+ };
+ assert.notOk(store.hasReduce());
+
+ });
+
+ });
+});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/app/addons/documents/pagination/actions.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/pagination/actions.js b/app/addons/documents/pagination/actions.js
index c69087a..5705d90 100644
--- a/app/addons/documents/pagination/actions.js
+++ b/app/addons/documents/pagination/actions.js
@@ -14,9 +14,10 @@ define([
'app',
'api',
'addons/documents/pagination/actiontypes',
- 'addons/documents/pagination/stores'
+ 'addons/documents/pagination/stores',
+ 'addons/documents/index-results/actions'
],
-function (app, FauxtonAPI, ActionTypes, Stores) {
+function (app, FauxtonAPI, ActionTypes, Stores, IndexResultsActions) {
var store = Stores.indexPaginationStore;
@@ -27,13 +28,10 @@ function (app, FauxtonAPI, ActionTypes, Stores) {
perPage: perPage
});
- FauxtonAPI.triggerRouteEvent('perPageChange', store.documentsLeftToFetch());
- },
+ IndexResultsActions.clearResults();
- newPagination: function (collection) {
- FauxtonAPI.dispatch({
- type: ActionTypes.NEW_PAGINATION,
- collection: collection
+ store.getCollection().fetch().then(function () {
+ IndexResultsActions.resultsListReset();
});
},
@@ -44,21 +42,13 @@ function (app, FauxtonAPI, ActionTypes, Stores) {
});
},
- collectionReset: function () {
- FauxtonAPI.dispatch({
- type: ActionTypes.PAGINATION_COLLECTION_RESET,
- });
- },
-
paginateNext: function () {
FauxtonAPI.dispatch({
type: ActionTypes.PAGINATE_NEXT,
});
- FauxtonAPI.triggerRouteEvent('paginate', {
- direction: 'next',
- perPage: store.documentsLeftToFetch(),
- currentPage: store.getCurrentPage()
+ store.getCollection().next().then(function () {
+ IndexResultsActions.resultsListReset();
});
},
@@ -67,10 +57,8 @@ function (app, FauxtonAPI, ActionTypes, Stores) {
type: ActionTypes.PAGINATE_PREVIOUS,
});
- FauxtonAPI.triggerRouteEvent('paginate', {
- direction: 'previous',
- perPage: store.getPerPage(),
- currentPage: store.getCurrentPage()
+ store.getCollection().previous().then(function () {
+ IndexResultsActions.resultsListReset();
});
},
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/app/addons/documents/pagination/actiontypes.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/pagination/actiontypes.js b/app/addons/documents/pagination/actiontypes.js
index fe2fe93..199131e 100644
--- a/app/addons/documents/pagination/actiontypes.js
+++ b/app/addons/documents/pagination/actiontypes.js
@@ -14,8 +14,6 @@ define([], function () {
return {
COLLECTION_CHANGED: 'COLLECTION_CHANGED',
PER_PAGE_CHANGE: 'PER_PAGE_CHANGE',
- NEW_PAGINATION: 'NEW_PAGINATION',
- COLLECTION_RESET: 'PAGINATION_COLLECTION_RESET',
PAGINATE_NEXT: 'PAGINATE_NEXT',
PAGINATE_PREVIOUS: 'PAGINATE_PREVIOUS',
SET_PAGINATION_DOCUMENT_LIMIT: 'SET_PAGINATION_DOCUMENT_LIMIT'
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/app/addons/documents/pagination/stores.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/pagination/stores.js b/app/addons/documents/pagination/stores.js
index f63f766..731614a 100644
--- a/app/addons/documents/pagination/stores.js
+++ b/app/addons/documents/pagination/stores.js
@@ -13,8 +13,9 @@
define([
'app',
'api',
- 'addons/documents/pagination/actiontypes'
-], function (app, FauxtonAPI, ActionTypes) {
+ 'addons/documents/pagination/actiontypes',
+ 'addons/documents/index-results/actiontypes'
+], function (app, FauxtonAPI, ActionTypes, IndexResultsActionTypes) {
var Stores = {};
var maxDocLimit = 10000;
@@ -49,6 +50,10 @@ define([
this.reset();
},
+ getCollection: function () {
+ return this._collection;
+ },
+
canShowPrevious: function () {
if (!this._enabled) { return false; }
return this._collection.hasPrevious();
@@ -67,6 +72,7 @@ define([
paginateNext: function () {
this._currentPage += 1;
this._pageStart += this.getPerPage();
+ this._collection.paging.pageSize = this.documentsLeftToFetch();
},
paginatePrevious: function () {
@@ -76,6 +82,8 @@ define([
if (this._pageStart < 1) {
this._pageStart = 1;
}
+
+ this._collection.paging.pageSize = this.getPerPage();
},
getCurrentPage: function () {
@@ -121,6 +129,10 @@ define([
setPerPage: function (perPage) {
this._perPage = perPage;
app.utils.localStorageSet('fauxton:perpage', perPage);
+
+ if (this._collection && this._collection.pageSizeReset) {
+ this._collection.pageSizeReset(perPage, {fetch: false});
+ }
},
getTotalRows: function () {
@@ -142,15 +154,15 @@ define([
dispatch: function (action) {
switch (action.type) {
- case ActionTypes.NEW_PAGINATION:
- this.newPagination(action.collection);
+ case IndexResultsActionTypes.INDEX_RESULTS_NEW_RESULTS:
+ this.newPagination(action.options.collection);
this.triggerChange();
break;
case ActionTypes.SET_PAGINATION_DOCUMENT_LIMIT:
this.setDocumentLimit(action.docLimit);
this.triggerChange();
break;
- case ActionTypes.PAGINATION_COLLECTION_RESET:
+ case IndexResultsActionTypes.INDEX_RESULTS_RESET:
this.triggerChange();
break;
case ActionTypes.PAGINATE_NEXT:
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/app/addons/documents/pagination/tests/pagination.actionsSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/pagination/tests/pagination.actionsSpec.js b/app/addons/documents/pagination/tests/pagination.actionsSpec.js
index 8eab4b2..edeff05 100644
--- a/app/addons/documents/pagination/tests/pagination.actionsSpec.js
+++ b/app/addons/documents/pagination/tests/pagination.actionsSpec.js
@@ -14,8 +14,10 @@ define([
'api',
'addons/documents/pagination/actions',
'addons/documents/pagination/stores',
+ 'addons/documents/index-results/actions',
+ 'addons/documents/shared-resources',
'testUtils',
-], function (FauxtonAPI, Actions, Stores, testUtils) {
+], function (FauxtonAPI, Actions, Stores, IndexResultsActions, Documents, testUtils) {
var assert = testUtils.assert;
FauxtonAPI.router = new FauxtonAPI.Router([]);
@@ -26,47 +28,74 @@ define([
Stores.indexPaginationStore.documentsLeftToFetch.restore && Stores.indexPaginationStore.documentsLeftToFetch.restore();
Stores.indexPaginationStore.getCurrentPage.restore && Stores.indexPaginationStore.getCurrentPage.restore();
Stores.indexPaginationStore.getPerPage.restore && Stores.indexPaginationStore.getPerPage.restore();
- FauxtonAPI.triggerRouteEvent.restore();
+
+ IndexResultsActions.resultsListReset.restore && IndexResultsActions.resultsListReset.restore();
+ Stores.indexPaginationStore.getCollection.restore && Stores.indexPaginationStore.getCollection.restore();
});
describe('updatePerPage', function () {
- it('triggers routeEvent', function () {
- var stub = sinon.stub(Stores.indexPaginationStore, 'documentsLeftToFetch');
- stub.returns(30);
- var spy = sinon.spy(FauxtonAPI, 'triggerRouteEvent');
+ beforeEach(function () {
+ Stores.indexPaginationStore._collection = new Documents.AllDocs([{id:1}, {id: 2}], {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ });
+
+ it('fetches collection', function () {
+ var spy = sinon.spy(Stores.indexPaginationStore, 'getCollection');
Actions.updatePerPage(30);
- assert.ok(spy.calledWith('perPageChange', 30));
+ assert.ok(spy.calledOnce);
+ });
+
+ it('sends results list reset', function () {
+ var promise = $.Deferred();
+ promise.resolve();
+ var stub = sinon.stub(Stores.indexPaginationStore, 'getCollection');
+ var spy = sinon.spy(IndexResultsActions, 'resultsListReset');
+ stub.returns({
+ fetch: function () { return promise; }
+ });
+
+ Actions.updatePerPage(30);
+ assert.ok(spy.calledOnce);
});
});
describe('paginateNext', function () {
- it('triggers routeEvent', function () {
- var spyEvent = sinon.spy(FauxtonAPI, 'triggerRouteEvent');
- var spyDocuments = sinon.spy(Stores.indexPaginationStore, 'documentsLeftToFetch');
- var spyPage = sinon.spy(Stores.indexPaginationStore, 'getCurrentPage');
- Actions.paginateNext();
+ it('sends results list reset', function () {
+ var promise = $.Deferred();
+ promise.resolve();
+ var stub = sinon.stub(Stores.indexPaginationStore, 'getCollection');
+ var spy = sinon.spy(IndexResultsActions, 'resultsListReset');
+ stub.returns({
+ next: function () { return promise; }
+ });
- assert.ok(spyEvent.calledOnce);
- assert.ok(spyDocuments.calledOnce);
- assert.ok(spyPage.calledOnce);
+ Actions.paginateNext();
+ assert.ok(spy.calledOnce);
});
});
describe('paginatePrevious', function () {
- it('triggers routeEvent', function () {
- var spyEvent = sinon.spy(FauxtonAPI, 'triggerRouteEvent');
- var spyPerPage = sinon.spy(Stores.indexPaginationStore, 'getPerPage');
- var spyPage = sinon.spy(Stores.indexPaginationStore, 'getCurrentPage');
- Actions.paginatePrevious();
+ it('sends results list reset', function () {
+ var promise = $.Deferred();
+ promise.resolve();
+ var stub = sinon.stub(Stores.indexPaginationStore, 'getCollection');
+ var spy = sinon.spy(IndexResultsActions, 'resultsListReset');
+ stub.returns({
+ previous: function () { return promise; }
+ });
- assert.ok(spyEvent.calledOnce);
- assert.ok(spyPerPage.called);
- assert.ok(spyPage.calledOnce);
+ Actions.paginatePrevious();
+ assert.ok(spy.calledOnce);
});
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/app/addons/documents/pagination/tests/paginationStoreSpec.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/pagination/tests/paginationStoreSpec.js b/app/addons/documents/pagination/tests/paginationStoreSpec.js
index 2230af5..f278c3d 100644
--- a/app/addons/documents/pagination/tests/paginationStoreSpec.js
+++ b/app/addons/documents/pagination/tests/paginationStoreSpec.js
@@ -14,8 +14,9 @@ define([
'api',
'addons/documents/pagination/stores',
'addons/documents/pagination/actiontypes',
+ 'addons/documents/shared-resources',
'testUtils'
-], function (FauxtonAPI, Stores, ActionTypes, testUtils) {
+], function (FauxtonAPI, Stores, ActionTypes, Documents, testUtils) {
var assert = testUtils.assert;
var dispatchToken;
var store;
@@ -34,7 +35,12 @@ define([
describe('#collectionChanged', function () {
var collection;
beforeEach(function () {
- collection = new Backbone.Collection([{id:1}, {id: 2}]);
+ collection = new Documents.AllDocs([{id:1}, {id: 2}], {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
collection.updateSeq = function () { return 'updateSeq';};
store.reset();
});
@@ -93,6 +99,12 @@ define([
describe('paginateNext', function () {
beforeEach(function () {
store.setPerPage(20);
+ store._collection = new Documents.AllDocs(null, {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
});
it('should increment page number', function () {
@@ -112,18 +124,30 @@ define([
});
it('should set correct page end', function () {
- store._collection = new Backbone.Collection();
store._collection.length = 20;
store.reset();
store.paginateNext();
assert.equal(store.getPageEnd(), 40);
});
+
+ it('should set collection pageSize', function () {
+ store.reset();
+ store.paginateNext();
+
+ assert.equal(store.getCollection().paging.pageSize, 20);
+ });
});
describe('paginatePrevious', function () {
beforeEach(function () {
store.reset();
+ store._collection = new Documents.AllDocs(null, {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
});
it('should decrement page number', function () {
@@ -141,7 +165,6 @@ define([
});
it('should decrement page end', function () {
- store._collection = new Backbone.Collection();
store._collection.length = 20;
store.paginateNext();
store.paginatePrevious();
@@ -149,6 +172,14 @@ define([
assert.equal(store.getPageEnd(), 20);
});
+ it('should set collection pageSize', function () {
+ store.reset();
+ store.paginateNext();
+ store.paginatePrevious();
+
+ assert.equal(store.getCollection().paging.pageSize, 20);
+ });
+
});
describe('totalDocsViewed', function () {
@@ -226,7 +257,36 @@ define([
store.setDocumentLimit(NaN);
assert.equal(store._docLimit, 10000);
});
+ });
+
+ describe('#setPerPage', function () {
+ beforeEach(function () {
+ store.reset();
+ store._collection = new Documents.AllDocs(null, {
+ params: {},
+ database: {
+ safeID: function () { return '1';}
+ }
+ });
+
+ });
+ it('stores per page in local storage', function () {
+ var testPerPage = 111;
+ store.setPerPage(testPerPage);
+ var perPage = window.localStorage.getItem('fauxton:perpage');
+ assert.equal(perPage, testPerPage );
+ });
+
+ it('sets collections perPage', function () {
+ var spy = sinon.spy(store._collection, 'pageSizeReset');
+ var testPerPage = 110;
+
+ store.setPerPage(testPerPage);
+ assert.equal(spy.getCall(0).args[0], testPerPage);
+
+
+ });
});
});
});
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/app/addons/documents/resources.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/resources.js b/app/addons/documents/resources.js
index 880cce1..e6ee676 100644
--- a/app/addons/documents/resources.js
+++ b/app/addons/documents/resources.js
@@ -106,6 +106,7 @@ function (app, FauxtonAPI, Documents, PagingCollection) {
bulkDelete: function () {
var payload = this.createPayload(this.toJSON()),
+ promise = FauxtonAPI.Deferred(),
that = this;
$.ajax({
@@ -116,7 +117,7 @@ function (app, FauxtonAPI, Documents, PagingCollection) {
data: JSON.stringify(payload),
})
.then(function (res) {
- that.handleResponse(res);
+ that.handleResponse(res, promise);
})
.fail(function () {
var ids = _.reduce(that.toArray(), function (acc, doc) {
@@ -124,10 +125,13 @@ function (app, FauxtonAPI, Documents, PagingCollection) {
return acc;
}, []);
that.trigger('error', ids);
+ promise.reject(ids);
});
+
+ return promise;
},
- handleResponse: function (res) {
+ handleResponse: function (res, promise) {
var ids = _.reduce(res, function (ids, doc) {
if (doc.error) {
ids.errorIds.push(doc.id);
@@ -146,6 +150,14 @@ function (app, FauxtonAPI, Documents, PagingCollection) {
this.trigger('error', ids.errorIds);
}
+ // This is kind of tricky. If there are no documents deleted then rejects
+ // otherwise resolve with list of successful and failed documents
+ if (!_.isEmpty(ids.successIds)) {
+ promise.resolve(ids);
+ } else {
+ promise.reject(ids.errorIds);
+ }
+
this.trigger('updated');
},
http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/96d31bc7/app/addons/documents/routes-documents.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/routes-documents.js b/app/addons/documents/routes-documents.js
index 0b607de..ff57dac 100644
--- a/app/addons/documents/routes-documents.js
+++ b/app/addons/documents/routes-documents.js
@@ -24,225 +24,220 @@ define([
'addons/databases/base',
'addons/documents/resources',
'addons/fauxton/components',
- 'addons/documents/pagination/actions',
- 'addons/documents/pagination/stores'
+ 'addons/documents/pagination/stores',
+ 'addons/documents/index-results/actions'
],
function (app, FauxtonAPI, BaseRoute, Documents, Changes, Index, DocEditor,
- Databases, Resources, Components, PaginationActions, PaginationStores) {
+ Databases, Resources, Components, PaginationStores, IndexResultsActions) {
+
+
+ var DocumentsRouteObject = BaseRoute.extend({
+ layout: "with_tabs_sidebar",
+ routes: {
+ "database/:database/_all_docs(:extra)": {
+ route: "allDocs",
+ roles: ["fx_loggedIn"]
+ },
+ "database/:database/_design/:ddoc/_info": {
+ route: "designDocMetadata",
+ roles: ['fx_loggedIn']
+ },
+ 'database/:database/_changes': 'changes'
+ },
+ events: {
+ "route:reloadDesignDocs": "reloadDesignDocs"
+ },
- var DocumentsRouteObject = BaseRoute.extend({
- layout: "with_tabs_sidebar",
- routes: {
- "database/:database/_all_docs(:extra)": {
- route: "allDocs",
- roles: ["fx_loggedIn"]
+ initialize: function (route, masterLayout, options) {
+ this.initViews(options[0]);
+ this.listenToLookaheadTray();
},
- "database/:database/_design/:ddoc/_info": {
- route: "designDocMetadata",
- roles: ['fx_loggedIn']
+
+ establish: function () {
+ return [
+ this.designDocs.fetch({reset: true}),
+ this.allDatabases.fetchOnce()
+ ];
},
- 'database/:database/_changes': 'changes'
- },
-
- events: {
- "route:reloadDesignDocs": "reloadDesignDocs",
- 'route:updateAllDocs': 'updateAllDocsFromView',
- 'route:paginate': 'paginate',
- 'route:perPageChange': 'perPageChange',
- },
-
- initialize: function (route, masterLayout, options) {
- this.initViews(options[0]);
- this.listenToLookaheadTray();
- },
-
- establish: function () {
- return [
- this.designDocs.fetch({reset: true}),
- this.allDatabases.fetchOnce()
- ];
- },
-
- initViews: function (dbName) {
- this.databaseName = dbName;
- this.database = new Databases.Model({id: this.databaseName});
- this.allDatabases = this.getAllDatabases();
-
- this.createDesignDocsCollection();
-
- this.rightHeader = this.setView("#right-header", new Documents.Views.RightAllDocsHeader({
- database: this.database
- }));
-
- this.addLeftHeader();
- this.addSidebar();
- },
-
- getAllDatabases: function () {
- return new Databases.List(); //getAllDatabases() can be overwritten instead of hard coded into initViews
- },
-
- // this safely assumes the db name is valid
- onSelectDatabase: function (dbName) {
- this.cleanup();
- this.initViews(dbName);
-
- var url = FauxtonAPI.urls('allDocs', 'app', app.utils.safeURLName(dbName), '');
- FauxtonAPI.navigate(url, {
- trigger: true
- });
-
- // we need to start listening again because cleanup() removed the listener, but in this case
- // initialize() doesn't fire to re-set up the listener
- this.listenToLookaheadTray();
- },
-
- listenToLookaheadTray: function () {
- this.listenTo(FauxtonAPI.Events, 'lookaheadTray:update', this.onSelectDatabase);
- },
-
- designDocMetadata: function (database, ddoc) {
- this.footer && this.footer.remove();
- this.toolsView && this.toolsView.remove();
- this.viewEditor && this.viewEditor.remove();
-
- var designDocInfo = new Resources.DdocInfo({ _id: "_design/" + ddoc }, { database: this.database });
- this.setView("#dashboard-lower-content", new Documents.Views.DdocInfo({
- ddocName: ddoc,
- model: designDocInfo
- }));
-
- this.sidebar.setSelectedTab(app.utils.removeSpecialCharacters(ddoc) + "_metadata");
- this.leftheader.updateCrumbs(this.getCrumbs(this.database));
- this.rightHeader.hideQueryOptions();
-
- this.apiUrl = [designDocInfo.url('apiurl'), designDocInfo.documentation()];
- },
-
- /*
- * docParams are the options collection uses to fetch from the server
- * urlParams are what are shown in the url and to the user
- * They are not the same when paginating
- */
- allDocs: function (databaseName, options) {
- var params = this.createParams(options),
- urlParams = params.urlParams,
- docParams = params.docParams,
- collection;
-
- if (this.eventAllDocs) {
- this.eventAllDocs = false;
- return;
- }
- this.reactHeader = this.setView('#react-headerbar', new Documents.Views.ReactHeaderbar());
+ initViews: function (dbName) {
+ this.databaseName = dbName;
+ this.database = new Databases.Model({id: this.databaseName});
+ this.allDatabases = this.getAllDatabases();
+
+ this.createDesignDocsCollection();
- this.footer = this.setView('#footer', new Documents.Views.Footer());
+ this.rightHeader = this.setView("#right-header", new Documents.Views.RightAllDocsHeader({
+ database: this.database
+ }));
- this.leftheader.updateCrumbs(this.getCrumbs(this.database));
+ this.addLeftHeader();
+ this.addSidebar();
+ },
- this.database.buildAllDocs(docParams);
- collection = this.database.allDocs;
+ getAllDatabases: function () {
+ return new Databases.List(); //getAllDatabases() can be overwritten instead of hard coded into initViews
+ },
- if (docParams.startkey && docParams.startkey.indexOf("_design") > -1) {
- this.sidebar.setSelectedTab("design-docs");
- } else {
- this.sidebar.setSelectedTab("all-docs");
- }
+ // this safely assumes the db name is valid
+ onSelectDatabase: function (dbName) {
+ this.cleanup();
+ this.initViews(dbName);
- this.viewEditor && this.viewEditor.remove();
- this.headerView && this.headerView.remove();
+ var url = FauxtonAPI.urls('allDocs', 'app', app.utils.safeURLName(dbName), '');
+ FauxtonAPI.navigate(url, {
+ trigger: true
+ });
+ // we need to start listening again because cleanup() removed the listener, but in this case
+ // initialize() doesn't fire to re-set up the listener
+ this.listenToLookaheadTray();
+ },
- if (!docParams) {
- docParams = {};
- }
+ listenToLookaheadTray: function () {
+ this.listenTo(FauxtonAPI.Events, 'lookaheadTray:update', this.onSelectDatabase);
+ },
- PaginationActions.newPagination(collection);
- this.database.allDocs.paging.pageSize = PaginationStores.indexPaginationStore.getPerPage();
+ designDocMetadata: function (database, ddoc) {
+ this.footer && this.footer.remove();
+ this.toolsView && this.toolsView.remove();
+ this.viewEditor && this.viewEditor.remove();
- // documentsView will populate the collection
- this.documentsView = this.setView("#dashboard-lower-content", new Documents.Views.AllDocsList({
- database: this.database,
- collection: collection,
- docParams: docParams,
- bulkDeleteDocsCollection: new Documents.BulkDeleteDocCollection([], {databaseId: this.database.get('id')})
- }));
+ var designDocInfo = new Resources.DdocInfo({ _id: "_design/" + ddoc }, { database: this.database });
+ this.setView("#dashboard-lower-content", new Documents.Views.DdocInfo({
+ ddocName: ddoc,
+ model: designDocInfo
+ }));
- // this used to be a function that returned the object, but be warned: it caused a closure with a reference to
- // the initial this.database object which can change
- this.apiUrl = [this.database.allDocs.urlRef("apiurl", urlParams), this.database.allDocs.documentation()];
+ this.sidebar.setSelectedTab(app.utils.removeSpecialCharacters(ddoc) + "_metadata");
+ this.leftheader.updateCrumbs(this.getCrumbs(this.database));
+ this.rightHeader.hideQueryOptions();
- // update the rightHeader with the latest & greatest info
- this.rightHeader.resetQueryOptions({ queryParams: urlParams });
- this.rightHeader.showQueryOptions();
- },
+ this.apiUrl = [designDocInfo.url('apiurl'), designDocInfo.documentation()];
+ },
- reloadDesignDocs: function (event) {
- this.sidebar.forceRender();
+ /*
+ * docParams are the options collection uses to fetch from the server
+ * urlParams are what are shown in the url and to the user
+ * They are not the same when paginating
+ */
+ allDocs: function (databaseName, options) {
+ var params = this.createParams(options),
+ urlParams = params.urlParams,
+ docParams = params.docParams,
+ collection;
- if (event && event.selectedTab) {
- this.sidebar.setSelectedTab(event.selectedTab);
- }
- },
+ if (this.eventAllDocs) {
+ this.eventAllDocs = false;
+ return;
+ }
- changes: function () {
- var docParams = app.getParams();
- this.database.buildChanges(docParams);
+ this.reactHeader = this.setView('#react-headerbar', new Documents.Views.ReactHeaderbar());
- this.changesView = this.setView("#dashboard-lower-content", new Changes.ChangesReactWrapper({
- model: this.database
- }));
+ this.footer = this.setView('#footer', new Documents.Views.Footer());
- this.headerView = this.setView('#dashboard-upper-content', new Changes.ChangesHeaderReactWrapper());
+ this.leftheader.updateCrumbs(this.getCrumbs(this.database));
- this.footer && this.footer.remove();
- this.toolsView && this.toolsView.remove();
- this.viewEditor && this.viewEditor.remove();
- this.reactHeader && this.reactHeader.remove();
+ this.database.buildAllDocs(docParams);
+ collection = this.database.allDocs;
- this.sidebar.setSelectedTab('changes');
- this.leftheader.updateCrumbs(this.getCrumbs(this.database));
- this.rightHeader.hideQueryOptions();
+ if (docParams.startkey && docParams.startkey.indexOf("_design") > -1) {
+ this.sidebar.setSelectedTab("design-docs");
+ } else {
+ this.sidebar.setSelectedTab("all-docs");
+ }
- this.apiUrl = function () {
- return [FauxtonAPI.urls('changes', 'apiurl', this.database.id, ''), this.database.documentation()];
- };
- },
+ this.viewEditor && this.viewEditor.remove();
+ this.headerView && this.headerView.remove();
- cleanup: function () {
- if (this.reactHeader) {
- this.removeView('#react-headerbar');
- }
- if (this.viewEditor) {
- this.removeView('#dashboard-upper-content');
- }
- if (this.documentsView) {
- this.removeView('#dashboard-lower-content');
- }
- if (this.rightHeader) {
- this.removeView('#right-header');
- }
- if (this.leftheader) {
- this.removeView('#breadcrumbs');
- }
- if (this.sidebar) {
- this.removeView('#sidebar');
- }
- if (this.footer) {
- this.removeView('#footer');
- }
- if (this.headerView) {
- this.removeView('#dashboard-upper-content');
+
+ if (!docParams) {
+ docParams = {};
+ }
+
+ IndexResultsActions.newResultsList({
+ collection: collection,
+ deleteable: true
+ });
+
+ this.database.allDocs.paging.pageSize = PaginationStores.indexPaginationStore.getPerPage();
+
+ this.resultList = this.setView('#dashboard-lower-content', new Index.ViewResultListReact({}));
+
+ // this used to be a function that returned the object, but be warned: it caused a closure with a reference to
+ // the initial this.database object which can change
+ this.apiUrl = [this.database.allDocs.urlRef("apiurl", urlParams), this.database.allDocs.documentation()];
+
+ // update the rightHeader with the latest & greatest info
+ this.rightHeader.resetQueryOptions({ queryParams: urlParams });
+ this.rightHeader.showQueryOptions();
+ },
+
+ reloadDesignDocs: function (event) {
+ this.sidebar.forceRender();
+
+ if (event && event.selectedTab) {
+ this.sidebar.setSelectedTab(event.selectedTab);
+ }
+ },
+
+ changes: function () {
+ var docParams = app.getParams();
+ this.database.buildChanges(docParams);
+
+ this.changesView = this.setView("#dashboard-lower-content", new Changes.ChangesReactWrapper({
+ model: this.database
+ }));
+
+ this.headerView = this.setView('#dashboard-upper-content', new Changes.ChangesHeaderReactWrapper());
+
+ this.footer && this.footer.remove();
+ this.toolsView && this.toolsView.remove();
+ this.viewEditor && this.viewEditor.remove();
+ this.reactHeader && this.reactHeader.remove();
+
+ this.sidebar.setSelectedTab('changes');
+ this.leftheader.updateCrumbs(this.getCrumbs(this.database));
+ this.rightHeader.hideQueryOptions();
+
+ this.apiUrl = function () {
+ return [FauxtonAPI.urls('changes', 'apiurl', this.database.id, ''), this.database.documentation()];
+ };
+ },
+
+ cleanup: function () {
+ if (this.reactHeader) {
+ this.removeView('#react-headerbar');
+ }
+ if (this.viewEditor) {
+ this.removeView('#dashboard-upper-content');
+ }
+ if (this.documentsView) {
+ this.removeView('#dashboard-lower-content');
+ }
+ if (this.rightHeader) {
+ this.removeView('#right-header');
+ }
+ if (this.leftheader) {
+ this.removeView('#breadcrumbs');
+ }
+ if (this.sidebar) {
+ this.removeView('#sidebar');
+ }
+ if (this.footer) {
+ this.removeView('#footer');
+ }
+ if (this.headerView) {
+ this.removeView('#dashboard-upper-content');
+ }
+
+ // we're no longer interested in listening to the lookahead tray event on this route object
+ this.stopListening(FauxtonAPI.Events, 'lookaheadTray:update', this.onSelectDatabase);
}
- // we're no longer interested in listening to the lookahead tray event on this route object
- this.stopListening(FauxtonAPI.Events, 'lookaheadTray:update', this.onSelectDatabase);
- }
+ });
+ return DocumentsRouteObject;
});
-
- return DocumentsRouteObject;
-});