couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From robertkowal...@apache.org
Subject fauxton commit: updated refs/heads/master to 6379614
Date Fri, 12 Feb 2016 15:32:11 GMT
Repository: couchdb-fauxton
Updated Branches:
  refs/heads/master 0c9fcb4c9 -> 63796145e


make dbs deletable from db list

offer a modal for comfortable deletion from the database list.

additionally factor out `isSystemDatabase` and test it.

PR: #632
PR-URL: https://github.com/apache/couchdb-fauxton/pull/632
Reviewed-By: Benjamin Keen <ben.keen@gmail.com>


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

Branch: refs/heads/master
Commit: 63796145e1a9abac8b5d7ace8e721b379d9e6431
Parents: 0c9fcb4
Author: Robert Kowalski <robertkowalski@apache.org>
Authored: Tue Jan 26 18:24:09 2016 +0100
Committer: Robert Kowalski <robertkowalski@apache.org>
Committed: Fri Feb 12 15:31:54 2016 +0000

----------------------------------------------------------------------
 app/addons/components/actions.js                |  36 +++++++
 app/addons/components/actiontypes.js            |   3 +-
 .../components/react-components.react.jsx       | 103 ++++++++++++++++++-
 app/addons/components/stores.js                 |  38 +++++++
 .../tests/deleteDatabaseModalSpec.react.jsx     |  78 ++++++++++++++
 app/addons/databases/assets/less/databases.less |   1 +
 app/addons/databases/components.react.jsx       |  49 +++++++--
 app/addons/databases/resources.js               |   2 +-
 app/addons/databases/stores.js                  |  11 +-
 .../databases/tests/componentsSpec.react.jsx    |  22 +++-
 .../tests/nightwatch/deletesDatabase.js         |  21 ++++
 app/addons/documents/routes-documents.js        |   6 +-
 app/addons/documents/sidebar/sidebar.react.jsx  |   4 +-
 app/core/tests/utilsSpec.js                     |  11 ++
 app/core/utils.js                               |   4 +
 15 files changed, 362 insertions(+), 27 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/63796145/app/addons/components/actions.js
----------------------------------------------------------------------
diff --git a/app/addons/components/actions.js b/app/addons/components/actions.js
index 390877c..ca12b5d 100644
--- a/app/addons/components/actions.js
+++ b/app/addons/components/actions.js
@@ -36,7 +36,43 @@ function (FauxtonAPI, ActionTypes) {
     });
   }
 
+  function showDeleteDatabaseModal (options) {
+    FauxtonAPI.dispatch({
+      type: ActionTypes.COMPONENTS_DATABASES_SHOWDELETE_MODAL,
+      options: options
+    });
+  }
+
+  function deleteDatabase (dbId) {
+    var url = FauxtonAPI.urls('databaseBaseURL', 'server', dbId, '');
+
+    $.ajax({
+      url: url,
+      dataType: 'json',
+      type: 'DELETE'
+    }).then(function () {
+      this.showDeleteDatabaseModal({ showModal: true });
+
+      FauxtonAPI.addNotification({
+        msg: 'The database <code>' + _.escape(dbId) + '</code> has been deleted.',
+        clear: true,
+        escape: false // beware of possible XSS when the message changes
+      });
+
+      Backbone.history.loadUrl(FauxtonAPI.urls('allDBs', 'app'));
+
+    }.bind(this)).fail(function (rsp, error, msg) {
+      FauxtonAPI.addNotification({
+        msg: 'Could not delete the database, reason ' + msg + '.',
+        type: 'error',
+        clear: true
+      });
+    });
+  }
+
   return {
+    deleteDatabase: deleteDatabase,
+    showDeleteDatabaseModal: showDeleteDatabaseModal,
     showAPIBar: showAPIBar,
     hideAPIBar: hideAPIBar,
     updateAPIBar: updateAPIBar

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/63796145/app/addons/components/actiontypes.js
----------------------------------------------------------------------
diff --git a/app/addons/components/actiontypes.js b/app/addons/components/actiontypes.js
index c8d8f80..28e608d 100644
--- a/app/addons/components/actiontypes.js
+++ b/app/addons/components/actiontypes.js
@@ -15,7 +15,8 @@ define([],  function () {
   return {
     SHOW_API_BAR: 'SHOW_API_BAR',
     HIDE_API_BAR: 'HIDE_API_BAR',
-    UPDATE_API_BAR: 'UPDATE_API_BAR'
+    UPDATE_API_BAR: 'UPDATE_API_BAR',
+    COMPONENTS_DATABASES_SHOWDELETE_MODAL: 'COMPONENTS_DATABASES_SHOWDELETE_MODAL'
   };
 
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/63796145/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 fac7570..c5e19cd 100644
--- a/app/addons/components/react-components.react.jsx
+++ b/app/addons/components/react-components.react.jsx
@@ -16,6 +16,8 @@ define([
   'react',
   'react-dom',
   'addons/components/stores',
+  'addons/components/actions',
+
   'addons/fauxton/components.react',
   'addons/documents/helpers',
   'ace/ace',
@@ -23,7 +25,8 @@ define([
   'libs/react-bootstrap'
 ],
 
-function (app, FauxtonAPI, React, ReactDOM, Stores, FauxtonComponents, Helpers, ace, beautifyHelper,
ReactBootstrap) {
+function (app, FauxtonAPI, React, ReactDOM, Stores, Actions,
+  FauxtonComponents, Helpers, ace, beautifyHelper, ReactBootstrap) {
 
   var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
   var componentStore = Stores.componentStore;
@@ -1366,6 +1369,101 @@ function (app, FauxtonAPI, React, ReactDOM, Stores, FauxtonComponents,
Helpers,
     }
   });
 
+  var DeleteDatabaseModal = React.createClass({
+
+    getInitialState: function () {
+      return {
+        inputValue: '',
+        disableSubmit: true
+      };
+    },
+
+    propTypes: {
+      dbId: React.PropTypes.string,
+      showHide: React.PropTypes.func.isRequired
+    },
+
+    close: function () {
+      this.setState({
+        inputValue: '',
+        disableSubmit: true
+      });
+
+      this.props.showHide({showModal: false});
+    },
+
+    open: function () {
+      this.props.showHide({showModal: true});
+    },
+
+    onInputChange: function (e) {
+      this.setState({
+        inputValue: e.target.value
+      });
+
+      this.setState({
+        disableSubmit: e.target.value.trim() !== this.props.showModal.dbId.trim()
+      });
+    },
+
+    onDeleteClick: function (e) {
+      e.preventDefault();
+
+      Actions.deleteDatabase(this.props.showModal.dbId.trim());
+    },
+
+    onInputKeypress: function (e) {
+      if (e.keyCode === 13 && this.state.disableSubmit !== true) {
+        Actions.deleteDatabase(this.props.showModal.dbId.trim());
+      }
+    },
+
+    render: function () {
+      var isSystemDatabase = this.props.showModal.isSystemDatabase;
+      var showDeleteModal = this.props.showModal.showDeleteModal;
+      var dbId = this.props.showModal.dbId;
+      var errorMessage = this.state.errorMessage;
+
+      var warning = isSystemDatabase ? (
+        <p className="warning"><b>You are about to delete a system database,
be careful!</b></p>
+      ) : null;
+
+      return (
+        <Modal dialogClassName="delete-db-modal" show={showDeleteModal} onHide={this.close}>
+          <Modal.Header closeButton={true}>
+            <Modal.Title>Delete Database</Modal.Title>
+          </Modal.Header>
+          <Modal.Body>
+            {warning}
+            <p>
+            Warning: This action will permanently delete <code>{dbId}</code>.
+            To confirm the deletion of the database and all of the
+            database's documents, you must enter the database's name.
+            </p>
+            <input
+              type="text"
+              className="input-block-level"
+              onKeyUp={this.onInputKeypress}
+              onChange={this.onInputChange} />
+
+          </Modal.Body>
+          <Modal.Footer>
+            <button onClick={this.close} className="btn cancel-button">
+              <i className="icon fonticon-cancel-circled"></i> Cancel
+            </button>
+            <button
+              disabled={this.state.disableSubmit}
+              onClick={this.onDeleteClick}
+              className="btn btn-danger delete"
+            >
+              <i className="icon fonticon-ok-circled"></i> Delete
+            </button>
+          </Modal.Footer>
+        </Modal>
+      );
+    }
+  });
+
   return {
     BadgeList: BadgeList,
     Badge: Badge,
@@ -1385,14 +1483,13 @@ function (app, FauxtonAPI, React, ReactDOM, Stores, FauxtonComponents,
Helpers,
     Tray: Tray,
     TrayContents: TrayContents,
     ApiBarController: ApiBarController,
-
     renderMenuDropDown: function (el, opts) {
       ReactDOM.render(<MenuDropDown icon="fonticon-cog" links={opts.links} />, el);
     },
-
     removeMenuDropDown: function (el) {
       ReactDOM.unmountComponentAtNode(el);
     },
+    DeleteDatabaseModal: DeleteDatabaseModal
   };
 
 });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/63796145/app/addons/components/stores.js
----------------------------------------------------------------------
diff --git a/app/addons/components/stores.js b/app/addons/components/stores.js
index d5ec7d5..eccb002 100644
--- a/app/addons/components/stores.js
+++ b/app/addons/components/stores.js
@@ -79,6 +79,44 @@ function (FauxtonAPI, app, ActionTypes) {
     }
   });
 
+  Stores.DeleteDbModalStore = FauxtonAPI.Store.extend({
+    initialize: function () {
+      this.reset();
+    },
+
+    reset: function () {
+      this._deleteModal = {showDeleteModal: false, dbId: '', isSystemDatabase: false};
+    },
+
+    setDeleteModal: function (options) {
+      options.isSystemDatabase = app.utils.isSystemDatabase(options.dbId);
+      this._deleteModal = options;
+    },
+
+    getShowDeleteDatabaseModal: function () {
+      return this._deleteModal;
+    },
+
+    dispatch: function (action) {
+      switch (action.type) {
+        case ActionTypes.COMPONENTS_DATABASES_SHOWDELETE_MODAL:
+          this.setDeleteModal(action.options);
+        break;
+
+        default:
+        return;
+      }
+
+      this.triggerChange();
+    }
+  });
+
+
+
+
+  Stores.deleteDbModalStore = new Stores.DeleteDbModalStore();
+  Stores.deleteDbModalStore.dispatchToken = FauxtonAPI.dispatcher.register(Stores.deleteDbModalStore.dispatch);
+
   Stores.componentStore = new Stores.ComponentStore();
   Stores.componentStore.dispatchToken = FauxtonAPI.dispatcher.register(Stores.componentStore.dispatch);
 

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/63796145/app/addons/components/tests/deleteDatabaseModalSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/components/tests/deleteDatabaseModalSpec.react.jsx b/app/addons/components/tests/deleteDatabaseModalSpec.react.jsx
new file mode 100644
index 0000000..6b4d62a
--- /dev/null
+++ b/app/addons/components/tests/deleteDatabaseModalSpec.react.jsx
@@ -0,0 +1,78 @@
+// 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/components/react-components.react',
+
+  'testUtils',
+  'libs/react-bootstrap',
+  'react'
+], function (FauxtonAPI, ReactComponents, utils, ReactBootstrap, React) {
+
+  var assert = utils.assert;
+  var TestUtils = React.addons.TestUtils;
+  var Modal = ReactBootstrap.Modal;
+
+  function noop () {}
+
+  describe('DeleteDatabaseModal', function () {
+    var container, instance;
+    beforeEach(function () {
+      container = document.createElement('div');
+    });
+
+    afterEach(function () {
+      React.unmountComponentAtNode(container);
+    });
+
+    it('submitting is disabled when initially rendered', function () {
+      instance = TestUtils.renderIntoDocument(
+        <ReactComponents.DeleteDatabaseModal
+          showHide={noop}
+          showModal={{isSystemDatabase: false, showDeleteModal: true, dbId: 'fooo'}} />,
+        container
+      );
+
+      assert.ok($('body').find('.modal').find('button.delete').prop('disabled'));
+    });
+
+    it('submitting is disabled when garbage entered', function () {
+      instance = TestUtils.renderIntoDocument(
+        <ReactComponents.DeleteDatabaseModal
+          showHide={noop}
+          showModal={{isSystemDatabase: false, showDeleteModal: true, dbId: 'fooo'}} />,
+        container
+      );
+
+      var input = $('body').find('.modal').find('input')[0];
+
+      TestUtils.Simulate.change(input, {target: {value: 'Hello, world'}});
+      assert.ok($('body').find('.modal').find('button.delete').prop('disabled'));
+    });
+
+    it('submitting is enabled when same db name entered', function () {
+      instance = TestUtils.renderIntoDocument(
+        <ReactComponents.DeleteDatabaseModal
+          showHide={noop}
+          showModal={{isSystemDatabase: false, showDeleteModal: true, dbId: 'fooo'}} />,
+        container
+      );
+
+      var input = $('body').find('.modal').find('input')[0];
+
+      TestUtils.Simulate.change(input, {target: {value: 'fooo'}});
+      assert.notOk($('body').find('.modal').find('button.delete').prop('disabled'));
+    });
+
+
+  });
+});

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/63796145/app/addons/databases/assets/less/databases.less
----------------------------------------------------------------------
diff --git a/app/addons/databases/assets/less/databases.less b/app/addons/databases/assets/less/databases.less
index 58763a5..fbc1ca2 100644
--- a/app/addons/databases/assets/less/databases.less
+++ b/app/addons/databases/assets/less/databases.less
@@ -27,6 +27,7 @@
     .border-radius(6px);
     text-decoration: none;
     font-size: 19px;
+    margin-left: 10px;
   }
   td {
     vertical-align: middle;

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/63796145/app/addons/databases/components.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/databases/components.react.jsx b/app/addons/databases/components.react.jsx
index 05e4b45..0ff6923 100644
--- a/app/addons/databases/components.react.jsx
+++ b/app/addons/databases/components.react.jsx
@@ -16,22 +16,30 @@ define([
   'react',
   'react-dom',
   'addons/components/react-components.react',
+  'addons/components/stores',
+  'addons/components/actions',
   'addons/fauxton/components.react',
+
   'addons/databases/stores',
   'addons/databases/resources',
   'addons/databases/actions',
   'helpers'
-], function (app, FauxtonAPI, React, ReactDOM, Components, ComponentsReact, Stores, Resources,
Actions, Helpers) {
+], function (app, FauxtonAPI, React, ReactDOM, Components, ComponentsStore, ComponentsActions,
FauxtonComponentsReact,
+  Stores, Resources, Actions, Helpers) {
 
   var ToggleHeaderButton = Components.ToggleHeaderButton;
   var databasesStore = Stores.databasesStore;
+  var deleteDbModalStore = ComponentsStore.deleteDbModalStore;
+  var DeleteDatabaseModal = Components.DeleteDatabaseModal;
+
 
   var DatabasesController = React.createClass({
 
     getStoreState: function () {
       return {
         collection: databasesStore.getCollection(),
-        loading: databasesStore.isLoading()
+        loading: databasesStore.isLoading(),
+        showDeleteDatabaseModal: deleteDbModalStore.getShowDeleteDatabaseModal()
       };
     },
 
@@ -41,10 +49,12 @@ define([
 
     componentDidMount: function () {
       databasesStore.on('change', this.onChange, this);
+      deleteDbModalStore.on('change', this.onChange, this);
     },
 
     componentWillUnmount: function () {
       databasesStore.off('change', this.onChange, this);
+      deleteDbModalStore.off('change', this.onChange, this);
     },
 
     onChange: function () {
@@ -55,7 +65,10 @@ define([
       var collection = this.state.collection;
       var loading = this.state.loading;
       return (
-        <DatabaseTable body={collection} loading={loading} />
+        <DatabaseTable
+          showDeleteDatabaseModal={this.state.showDeleteDatabaseModal}
+          body={collection}
+          loading={loading} />
       );
     }
   });
@@ -65,7 +78,9 @@ define([
     createRows: function () {
       return _.map(this.props.body, function (item, iteration) {
         return (
-          <DatabaseRow row={item} key={iteration} />
+          <DatabaseRow
+            row={item}
+            key={iteration} />
         );
       });
     },
@@ -77,6 +92,12 @@ define([
       });
     },
 
+    showDeleteDatabaseModal: function (name) {
+      ComponentsActions.showDeleteDatabaseModal({
+        showDeleteModal: !this.props.showDeleteDatabaseModal.showDeleteModal
+      });
+    },
+
     render: function () {
       if (this.props.loading) {
         return (
@@ -85,9 +106,13 @@ define([
           </div>
         );
       }
+
       var rows = this.createRows();
       return (
         <div className="view">
+          <DeleteDatabaseModal
+            showHide={this.showDeleteDatabaseModal}
+            showModal={this.props.showDeleteDatabaseModal} />
           <table className="databases table table-striped">
             <thead>
               <tr>
@@ -131,6 +156,10 @@ define([
       });
     },
 
+    showDeleteDatabaseModal: function (name) {
+      ComponentsActions.showDeleteDatabaseModal({showDeleteModal: true, dbId: name});
+    },
+
     render: function () {
       var row = this.props.row;
       var name = row.get("name");
@@ -160,10 +189,14 @@ define([
             <a
               className="db-actions btn fonticon-replicate set-replication-start"
               title={"Replicate " + name}
-              href={"#/replication/" + encoded}></a>&#160;
+              href={"#/replication/" + encoded}></a>
             <a
               className="db-actions btn icon-lock set-permissions"
               title={"Set permissions for " + name} href={"#/database/" + encoded + "/permissions"}></a>
+            <a
+              className="db-actions btn icon-trash"
+              onClick={this.showDeleteDatabaseModal.bind(this, name)}
+              title={'Delete ' + name} data-bypass="true"></a>
           </td>
         </tr>
       );
@@ -241,11 +274,11 @@ define([
             title="Create Database"
             fonticon="fonticon-new-database"
             text="Create Database" />
-          <ComponentsReact.Tray ref="newDbTray" className="new-database-tray">
+          <FauxtonComponentsReact.Tray ref="newDbTray" className="new-database-tray">
             <span className="add-on">Create Database</span>
             <input id="js-new-database-name" type="text" onKeyUp={this.onKeyUpInInput}
ref="newDbName" className="input-xxlarge" placeholder="Name of database" />
             <a className="btn" id="js-create-database" onClick={this.onAddDatabase}>Create</a>
-          </ComponentsReact.Tray>
+          </FauxtonComponentsReact.Tray>
         </div>
       );
     }
@@ -349,7 +382,7 @@ define([
       return (
         <footer className="all-db-footer pagination-footer">
           <div id="database-pagination">
-            <ComponentsReact.Pagination page={page} total={total} urlPrefix={urlPrefix}
/>
+            <FauxtonComponentsReact.Pagination page={page} total={total} urlPrefix={urlPrefix}
/>
           </div>
         </footer>
       );

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/63796145/app/addons/databases/resources.js
----------------------------------------------------------------------
diff --git a/app/addons/databases/resources.js b/app/addons/databases/resources.js
index 577f3f4..2af0a25 100644
--- a/app/addons/databases/resources.js
+++ b/app/addons/databases/resources.js
@@ -50,7 +50,7 @@ function (app, FauxtonAPI, Documents) {
     },
 
     isSystemDatabase: function () {
-      return (/^_/).test(this.id);
+      return app.utils.isSystemDatabase(this.id);
     },
 
     url: function (context) {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/63796145/app/addons/databases/stores.js
----------------------------------------------------------------------
diff --git a/app/addons/databases/stores.js b/app/addons/databases/stores.js
index cdae6df..0f73b52 100644
--- a/app/addons/databases/stores.js
+++ b/app/addons/databases/stores.js
@@ -94,32 +94,33 @@ define([
           this.init(action.options.collection, action.options.backboneCollection);
           this.setPage(action.options.page);
           this.setLoading(false);
-          this.triggerChange();
         break;
 
         case ActionTypes.DATABASES_SETPAGE:
           this.setPage(action.options.page);
-          this.triggerChange();
         break;
 
         case ActionTypes.DATABASES_SET_PROMPT_VISIBLE:
           this.setPromptVisible(action.options.visible);
-          this.triggerChange();
         break;
 
         case ActionTypes.DATABASES_STARTLOADING:
           this.setLoading(true);
-          this.triggerChange();
         break;
 
         case ActionTypes.DATABASES_LOADCOMPLETE:
           this.setLoading(false);
-          this.triggerChange();
+        break;
+
+        case ActionTypes.DATABASES_SHOWDELETE_MODAL:
+          this.setDeleteModal(action.options);
         break;
 
         default:
         return;
       }
+
+      this.triggerChange();
     }
   });
 

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/63796145/app/addons/databases/tests/componentsSpec.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/databases/tests/componentsSpec.react.jsx b/app/addons/databases/tests/componentsSpec.react.jsx
index f57c5c2..d6540c1 100644
--- a/app/addons/databases/tests/componentsSpec.react.jsx
+++ b/app/addons/databases/tests/componentsSpec.react.jsx
@@ -100,7 +100,7 @@ define([
       assert.equal("2.0 MB", dbEl.getDOMNode().getElementsByTagName('tr')[1].getElementsByTagName('td')[1].innerText.trim());
       assert.equal("88", dbEl.getDOMNode().getElementsByTagName('tr')[1].getElementsByTagName('td')[2].innerText.trim());
       assert.equal(0, dbEl.getDOMNode().getElementsByTagName('tr')[1].getElementsByTagName('td')[2].getElementsByTagName("i").length);
-      assert.equal(2, dbEl.getDOMNode().getElementsByTagName('tr')[1].getElementsByTagName('td')[4].getElementsByTagName("a").length);
+      assert.equal(3, dbEl.getDOMNode().getElementsByTagName('tr')[1].getElementsByTagName('td')[4].getElementsByTagName("a").length);
       assert.equal("db2", dbEl.getDOMNode().getElementsByTagName('tr')[2].getElementsByTagName('td')[0].innerText.trim());
       assert.equal(1, dbEl.getDOMNode().getElementsByTagName('tr')[2].getElementsByTagName('td')[2].getElementsByTagName("i").length);
     });
@@ -255,7 +255,10 @@ define([
       FauxtonAPI.registerExtension('DatabaseTable:head', ColHeader2);
       FauxtonAPI.registerExtension('DatabaseTable:head', ColHeader3);
 
-      var table = TestUtils.renderIntoDocument(<Views.DatabaseTable loading={false} body={[]}
/>, container);
+      var table = TestUtils.renderIntoDocument(
+        <Views.DatabaseTable showDeleteDatabaseModal={{showModal: false}} loading={false}
body={[]} />,
+        container
+      );
       var cols = $(ReactDOM.findDOMNode(table)).find('th');
 
       // (default # of rows is 5)
@@ -280,7 +283,10 @@ define([
         isGraveYard: function () { return false; }
       };
 
-      var databaseRow = TestUtils.renderIntoDocument(<Views.DatabaseTable body={[row]}
/>, container);
+      var databaseRow = TestUtils.renderIntoDocument(
+        <Views.DatabaseTable showDeleteDatabaseModal={{showModal: false}} body={[row]}
/>,
+        container
+      );
       var links = $(ReactDOM.findDOMNode(databaseRow)).find('td');
 
       // (default # of rows is 5)
@@ -301,7 +307,10 @@ define([
         isGraveYard: function () { return false; }
       };
 
-      var databaseRow = TestUtils.renderIntoDocument(<Views.DatabaseTable body={[row]}
/>, container);
+      var databaseRow = TestUtils.renderIntoDocument(
+        <Views.DatabaseTable showDeleteDatabaseModal={{showModal: false}} body={[row]}
/>,
+        container
+      );
       assert.equal($(ReactDOM.findDOMNode(databaseRow)).find('.database-load-fail').length,
1);
     });
 
@@ -315,7 +324,10 @@ define([
         isGraveYard: function () { return false; }
       };
 
-      var databaseRow = TestUtils.renderIntoDocument(<Views.DatabaseTable body={[row]}
/>, container);
+      var databaseRow = TestUtils.renderIntoDocument(
+        <Views.DatabaseTable showDeleteDatabaseModal={{showModal: false}} body={[row]}
/>,
+        container
+      );
 
       assert.equal($(ReactDOM.findDOMNode(databaseRow)).find('.database-load-fail').length,
0);
     });

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/63796145/app/addons/databases/tests/nightwatch/deletesDatabase.js
----------------------------------------------------------------------
diff --git a/app/addons/databases/tests/nightwatch/deletesDatabase.js b/app/addons/databases/tests/nightwatch/deletesDatabase.js
index 4d05410..d3f7eea 100644
--- a/app/addons/databases/tests/nightwatch/deletesDatabase.js
+++ b/app/addons/databases/tests/nightwatch/deletesDatabase.js
@@ -31,5 +31,26 @@ module.exports = {
       .checkForDatabaseDeleted(newDatabaseName, waitTime)
 
     .end();
+  },
+
+  'Deletes a database from the list': function (client) {
+    var waitTime = client.globals.maxWaitTime,
+        newDatabaseName = client.globals.testDatabaseName,
+        baseUrl = client.globals.test_settings.launch_url;
+
+    client
+      .createDatabase(newDatabaseName)
+      .loginToGUI()
+      .url(baseUrl + '/#/_all_dbs/')
+
+      .waitForElementPresent('a[href="#/database/' + newDatabaseName + '/_all_docs"]', waitTime,
false)
+      .assert.elementPresent('a[href="#/database/' + newDatabaseName + '/_all_docs"]')
+      .clickWhenVisible('[title="Delete ' + newDatabaseName + '"]', waitTime, false)
+      .setValue('.delete-db-modal input[type="text"]', [newDatabaseName, client.Keys.ENTER])
+      .checkForDatabaseDeleted(newDatabaseName, waitTime)
+      .assert.elementNotPresent('a[href="#/database/' + newDatabaseName + '/_all_docs"]')
+
+
+    .end();
   }
 };

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/63796145/app/addons/documents/routes-documents.js
----------------------------------------------------------------------
diff --git a/app/addons/documents/routes-documents.js b/app/addons/documents/routes-documents.js
index 17afb3e..3829981 100644
--- a/app/addons/documents/routes-documents.js
+++ b/app/addons/documents/routes-documents.js
@@ -30,12 +30,13 @@ define([
   'addons/documents/header/header.actions',
   'addons/documents/sidebar/actions',
   'addons/documents/designdocinfo/actions',
-  'addons/documents/designdocinfo/components.react'
+  'addons/documents/designdocinfo/components.react',
+  'addons/components/actions',
 ],
 
 function (app, FauxtonAPI, BaseRoute, Documents, Changes, ChangesActions, Databases, Resources,
Components,
   IndexResultStores, IndexResultsActions, IndexResultsComponents, ReactPagination, ReactHeader,
ReactActions,
-  SidebarActions, DesignDocInfoActions, DesignDocInfoComponents) {
+  SidebarActions, DesignDocInfoActions, DesignDocInfoComponents, ComponentsActions) {
 
     var DocumentsRouteObject = BaseRoute.extend({
       layout: "with_tabs_sidebar",
@@ -137,6 +138,7 @@ function (app, FauxtonAPI, BaseRoute, Documents, Changes, ChangesActions,
Databa
         }
 
         SidebarActions.setSelectedTab(tab);
+        ComponentsActions.showDeleteDatabaseModal({showDeleteModal: false, dbId: ''});
 
         this.removeComponent('#dashboard-upper-content');
 

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/63796145/app/addons/documents/sidebar/sidebar.react.jsx
----------------------------------------------------------------------
diff --git a/app/addons/documents/sidebar/sidebar.react.jsx b/app/addons/documents/sidebar/sidebar.react.jsx
index dd5abc2..471aeb6 100644
--- a/app/addons/documents/sidebar/sidebar.react.jsx
+++ b/app/addons/documents/sidebar/sidebar.react.jsx
@@ -325,8 +325,8 @@ function (app, FauxtonAPI, React, ReactDOM, Stores, Actions, Components,
Documen
     componentDidMount: function () {
       this.dbModal = new DeleteDBModal({
         database: this.props.database,
-        isSystemDatabase: (/^_/).test(this.props.database.id),
-        el: ReactDOM.findDOMNode(this)
+        el: ReactDOM.findDOMNode(this),
+        isSystemDatabase: app.utils.isSystemDatabase(this.props.database.id)
       });
 
       this.dbModal.render();

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/63796145/app/core/tests/utilsSpec.js
----------------------------------------------------------------------
diff --git a/app/core/tests/utilsSpec.js b/app/core/tests/utilsSpec.js
index c6e09af..a52cea5 100644
--- a/app/core/tests/utilsSpec.js
+++ b/app/core/tests/utilsSpec.js
@@ -49,6 +49,17 @@ define([
       });
     });
 
+    describe('isSystemDatabase', function () {
+
+      it('detects system databases', function () {
+        assert.ok(utils.isSystemDatabase('_replicator'));
+      });
+
+      it('ignores other dbs', function () {
+        assert.notOk(utils.isSystemDatabase('foo'));
+      });
+    });
+
     describe('localStorage', function () {
 
       it('Should get undefined when getting a non-existent key', function () {

http://git-wip-us.apache.org/repos/asf/couchdb-fauxton/blob/63796145/app/core/utils.js
----------------------------------------------------------------------
diff --git a/app/core/utils.js b/app/core/utils.js
index 6948526..fe93802 100644
--- a/app/core/utils.js
+++ b/app/core/utils.js
@@ -84,6 +84,10 @@ function ($, _) {
       return 'doc';
     },
 
+    isSystemDatabase: function (id) {
+      return (/^_/).test(id);
+    },
+
     // Need this to work around backbone router thinking _design/foo
     // is a separate route. Alternatively, maybe these should be
     // treated separately. For instance, we could default into the


Mime
View raw message