From 50bb3143c7acd2e0776095444beb513e5c4ab60e Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Fri, 13 Jan 2017 16:10:35 +1100 Subject: [PATCH 01/28] Sidebar: Remove DB icon Implementing: https://mongodb.invisionapp.com/share/6E9XJUCKW#/screens/213327458 The arrow needs to be moved to the left side of the menu to replace it in a future commit. --- .../sidebar/lib/components/sidebar-database.jsx | 1 - src/internal-packages/sidebar/styles/index.less | 11 ----------- 2 files changed, 12 deletions(-) diff --git a/src/internal-packages/sidebar/lib/components/sidebar-database.jsx b/src/internal-packages/sidebar/lib/components/sidebar-database.jsx index e05cb1419ae..8feabcde4e3 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-database.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-database.jsx @@ -62,7 +62,6 @@ class SidebarDatabase extends React.Component { return (
-
Date: Fri, 13 Jan 2017 16:20:18 +1100 Subject: [PATCH 02/28] Sidebar: Move clickable arrow to left side --- src/internal-packages/sidebar/styles/index.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal-packages/sidebar/styles/index.less b/src/internal-packages/sidebar/styles/index.less index fd17a4ef41d..a8162bac337 100644 --- a/src/internal-packages/sidebar/styles/index.less +++ b/src/internal-packages/sidebar/styles/index.less @@ -113,7 +113,7 @@ &-expand-icon { position: absolute; top: 8px; - right: 9px; + left: 9px; transition: all 180ms ease-out; color: #9DA4AA; } From 4961dc8e935a58a5136bca1300e076c283f03cba Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Sat, 14 Jan 2017 16:15:57 +1100 Subject: [PATCH 03/28] Add most new sidebar + and trash icons Designers can style pass later, e.g. not sure how to do the fade gradient in the top right instance area, the active parent child thing more cleanly or the specific circled plus icon inside another circle from: https://mongodb.invisionapp.com/share/6E9XJUCKW#/screens/213327444 --- .../lib/components/sidebar-collection.jsx | 8 +++ .../lib/components/sidebar-database.jsx | 18 +++++- .../sidebar-instance-properties.jsx | 10 +++- .../sidebar/styles/index.less | 55 ++++++++++++++++++- 4 files changed, 88 insertions(+), 3 deletions(-) diff --git a/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx b/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx index b4a62a00689..71302cef7d6 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx @@ -28,6 +28,10 @@ class SidebarCollection extends React.Component { } } + handleDropCollectionClick() { + console.log('Do drop collection'); + } + renderReadonly() { if (this.props.readonly) { return ( @@ -44,6 +48,10 @@ class SidebarCollection extends React.Component { } return (
+
- + + +
- + +
diff --git a/src/internal-packages/sidebar/styles/index.less b/src/internal-packages/sidebar/styles/index.less index a8162bac337..43a047be550 100644 --- a/src/internal-packages/sidebar/styles/index.less +++ b/src/internal-packages/sidebar/styles/index.less @@ -1,3 +1,6 @@ +@compass-sidebar-base-background-color: #4c5259; +@compass-sidebar-active-background-color: #6D7984; + .compass-sidebar { margin: 0; background: #4c5259; @@ -61,7 +64,7 @@ &-is-active, &-is-active:hover { - background-color: #6D7984; + background-color: @compass-sidebar-active-background-color; } } @@ -122,6 +125,48 @@ color: #fff; } + &-icon { + color: @compass-sidebar-base-background-color; + font-size: 17px; + position: absolute; + top: 0; + transition: all 180ms ease-out; + + &-create-database { + right: 0; + } + + &-create-collection { + right: 22px; + } + + &-create-database, + &-create-collection { + padding: 7px 9px 8px 3px; + } + + // database and collection sidebar rows have slightly different heights + &-drop-database { + padding: 7px 9px 8px 3px; + } + &-drop-collection { + padding: 6px 9px 6px 3px; + } + + &-drop-database, + &-drop-collection { + right: 0; + } + + &-is-active:hover { + background: @compass-sidebar-active-background-color; + } + + &:hover { + color: #fff; + } + } + &-item-content { .compass { &-sidebar-item { @@ -236,6 +281,14 @@ .compass-sidebar-instance-version { color: #fff; } + + .compass-sidebar-icon-create-database { + color: @compass-sidebar-active-background-color; + + &:hover { + color: #fff; + } + } } } From c1b8b089835c2a1598f3c3d1f2dad40b9b118e26 Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Mon, 16 Jan 2017 16:13:38 +1100 Subject: [PATCH 04/28] Add create/drop tooltips --- .../sidebar/lib/components/constants.js | 10 ++++++++ .../lib/components/sidebar-collection.jsx | 12 ++++++++++ .../lib/components/sidebar-database.jsx | 23 ++++++++++++++++++- .../sidebar-instance-properties.jsx | 13 +++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 src/internal-packages/sidebar/lib/components/constants.js diff --git a/src/internal-packages/sidebar/lib/components/constants.js b/src/internal-packages/sidebar/lib/components/constants.js new file mode 100644 index 00000000000..0a46f617196 --- /dev/null +++ b/src/internal-packages/sidebar/lib/components/constants.js @@ -0,0 +1,10 @@ +const TOOLTIP_IDS = Object.freeze({ + CREATE_COLLECTION: 'create-collection', + CREATE_DATABASE: 'create-database', + DROP_COLLECTION: 'drop-collection', + DROP_DATABASE: 'drop-database' +}); + +module.exports = { + TOOLTIP_IDS +}; diff --git a/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx b/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx index 71302cef7d6..925e0ed86cd 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx @@ -1,9 +1,12 @@ const app = require('ampersand-app'); const React = require('react'); +const ReactTooltip = require('react-tooltip'); const ipc = require('hadron-ipc'); const { NamespaceStore } = require('hadron-reflux-store'); +const { TOOLTIP_IDS } = require('./constants'); + class SidebarCollection extends React.Component { constructor() { super(); @@ -42,6 +45,13 @@ class SidebarCollection extends React.Component { render() { const collectionName = this.getCollectionName(); + const tooltipText = `Drop ${this.props.database}.${collectionName} collection`; + const tooltipOptions = { + 'data-for': TOOLTIP_IDS.DROP_COLLECTION, + 'data-effect': 'solid', + 'data-offset': "{'bottom': 18, 'left': 3}", + 'data-tip': tooltipText + }; let className = 'compass-sidebar-title compass-sidebar-title-is-actionable'; if (this.props.activeNamespace === this.props._id) { className += ' compass-sidebar-title-is-active'; @@ -51,7 +61,9 @@ class SidebarCollection extends React.Component { +
+ +
+
From 94bf01b3dcb8ca691902abe3c9a36e7800da0391 Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Mon, 16 Jan 2017 17:23:50 +1100 Subject: [PATCH 05/28] Implement handleCreateDatabaseClick --- .../sidebar/lib/components/sidebar-instance-properties.jsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/internal-packages/sidebar/lib/components/sidebar-instance-properties.jsx b/src/internal-packages/sidebar/lib/components/sidebar-instance-properties.jsx index da3a6e7f2e6..9b5fea7dce2 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-instance-properties.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-instance-properties.jsx @@ -6,6 +6,11 @@ const { NamespaceStore } = require('hadron-reflux-store'); const { TOOLTIP_IDS } = require('./constants'); class SidebarInstanceProperties extends React.Component { + constructor(props) { + super(props); + this.DatabaseDDLActions = app.appRegistry.getAction('DatabaseDDL.Actions'); + } + getHostnameAndPort() { const connection = this.props.connection; if (!connection.hostname) { @@ -56,7 +61,7 @@ class SidebarInstanceProperties extends React.Component { } handleCreateDatabaseClick() { - console.log('Do create database'); + this.DatabaseDDLActions.openCreateDatabaseDialog(); } render() { From 88f5b84499c0d23b5b38072c79e2b4c29d6fbe46 Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Tue, 24 Jan 2017 16:32:07 +1100 Subject: [PATCH 06/28] Resolve import vs run time issue by using beforeEach Took me an hour with console.log() and console.trace() to finally realise what was really going on here. --- test/enzyme/sidebar-instance.test.js | 99 ++++++++++++++-------------- 1 file changed, 51 insertions(+), 48 deletions(-) diff --git a/test/enzyme/sidebar-instance.test.js b/test/enzyme/sidebar-instance.test.js index 99c841f24ea..efd95564a93 100644 --- a/test/enzyme/sidebar-instance.test.js +++ b/test/enzyme/sidebar-instance.test.js @@ -11,71 +11,74 @@ chai.use(chaiEnzyme()); describe('', () => { context('when rendering with no SSH Tunnel', () => { - let connection = { - hostname: 'ip-1-2-3-4-mongod.com', - port: 27000, - ssh_tunnel: 'NONE' - }; - let instance = { - build: { - enterprise_module: true, - version: '3.4.0-rc3' - }, - collections: [], - databases: [] - }; - const component = shallow( + beforeEach(function() { + const connection = { + hostname: 'ip-1-2-3-4-mongod.com', + port: 27000, + ssh_tunnel: 'NONE' + }; + const instance = { + build: { + enterprise_module: true, + version: '3.4.0-rc3' + }, + collections: [], + databases: [] + }; + this.component = shallow( ); - - it('renders the endpoint host name and port as text', () => { - const element = component.find('.compass-sidebar-instance-hostname'); + }); + it('renders the endpoint host name and port as text', function() { + const element = this.component.find('.compass-sidebar-instance-hostname'); expect(element.text()).to.be.equal('ip-1-2-3-4-mongod.com:27000'); }); - it('does not render any ssh-tunnel section', () => { - const element = component.find('.compass-sidebar-instance-ssh-tunnel'); + it('does not render any ssh-tunnel section', function() { + const element = this.component.find('.compass-sidebar-instance-ssh-tunnel'); expect(element).to.not.exist; }); - it('renders instance build version', () => { - const element = component.find('.compass-sidebar-instance-version'); + it('renders instance build version', function() { + const element = this.component.find('.compass-sidebar-instance-version'); expect(element.text()).to.be.equal('Enterprise version 3.4.0-rc3'); }); }); - context('when rendering with an SSH Tunnel', () => { - let connection = { - hostname: 'ip-1-2-3-4-secret-mongod.com', - port: 27017, - ssh_tunnel: 'IDENTITY_FILE', - ssh_tunnel_options: { - host: 'my-jump-box.com', - port: '2222' - } - }; - let instance = { - build: {version: '3.2.10'}, - collections: [], - databases: [] - }; - const component = shallow( - ); - it('renders the endpoint host name and port as text', () => { - const element = component.find('.compass-sidebar-instance-hostname'); + context('when rendering with an SSH Tunnel', () => { + beforeEach(function() { + const connection = { + hostname: 'ip-1-2-3-4-secret-mongod.com', + port: 27017, + ssh_tunnel: 'IDENTITY_FILE', + ssh_tunnel_options: { + host: 'my-jump-box.com', + port: '2222' + } + }; + const instance = { + build: {version: '3.2.10'}, + collections: [], + databases: [] + }; + this.component = shallow( + ); + }); + it('renders the endpoint host name and port as text', function() { + const element = this.component.find('.compass-sidebar-instance-hostname'); expect(element.text()).to.be.equal('ip-1-2-3-4-secret-mongod.com:27017'); }); - it('renders the SSH tunnel host name and port text', () => { - const element = component.find('.compass-sidebar-instance-ssh-tunnel'); + it('renders the SSH tunnel host name and port text', function() { + const element = this.component.find('.compass-sidebar-instance-ssh-tunnel'); expect(element.text()).to.be.equal('via SSH tunnel my-jump-box.com:2222'); }); - it('renders instance build version', () => { - const element = component.find('.compass-sidebar-instance-version'); + it('renders instance build version', function() { + const element = this.component.find('.compass-sidebar-instance-version'); expect(element.text()).to.be.equal('Community version 3.2.10'); }); }); From 3b3dd3c3cbb12a7b733a232cb247b04e879d5523 Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Tue, 24 Jan 2017 16:39:38 +1100 Subject: [PATCH 07/28] Fix failing enzyme tests by mocking the AppRegistry --- test/enzyme/sidebar-instance.test.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/test/enzyme/sidebar-instance.test.js b/test/enzyme/sidebar-instance.test.js index efd95564a93..9088b84bc8e 100644 --- a/test/enzyme/sidebar-instance.test.js +++ b/test/enzyme/sidebar-instance.test.js @@ -1,15 +1,27 @@ /* eslint no-unused-vars: 0, no-unused-expressions: 0 */ +const app = require('ampersand-app'); const chai = require('chai'); const chaiEnzyme = require('chai-enzyme'); const expect = chai.expect; const React = require('react'); - -const shallow = require('enzyme').shallow; +const sinon = require('sinon'); +const AppRegistry = require('hadron-app-registry'); +const { shallow } = require('enzyme'); const SidebarInstanceProperties = require('../../src/internal-packages/sidebar/lib/components/sidebar-instance-properties'); chai.use(chaiEnzyme()); +const appRegistry = app.appRegistry; + describe('', () => { + beforeEach(() => { + app.appRegistry = new AppRegistry(); + app.appRegistry.registerAction('DatabaseDDL.Actions', sinon.spy()); + }); + afterEach(() => { + app.appRegistry = appRegistry; + }); + context('when rendering with no SSH Tunnel', () => { beforeEach(function() { const connection = { From de759b482f088d6294e04db803d0a34330cc4a2a Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Mon, 16 Jan 2017 17:31:41 +1100 Subject: [PATCH 08/28] Implement handleCreateCollectionClick --- src/internal-packages/database/index.js | 3 +++ .../sidebar/lib/components/sidebar-database.jsx | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/internal-packages/database/index.js b/src/internal-packages/database/index.js index bdb190c42de..b78bd81ca0d 100644 --- a/src/internal-packages/database/index.js +++ b/src/internal-packages/database/index.js @@ -1,4 +1,5 @@ const app = require('ampersand-app'); +const CollectionsAction = require('./lib/actions/collections-actions'); const CollectionsTable = require('./lib/components'); const CreateCollectionCheckbox = require('./lib/components/create-collection-checkbox'); const CreateCollectionInput = require('./lib/components/create-collection-input'); @@ -10,6 +11,7 @@ const DropCollectionDialog = require('./lib/components/drop-collection-dialog'); * Activate all the components in the Schema package. */ function activate() { + app.appRegistry.registerAction('Database.CollectionsActions', CollectionsAction); app.appRegistry.registerComponent('Database.CollectionsTable', CollectionsTable); app.appRegistry.registerComponent('Database.CreateCollectionCheckbox', CreateCollectionCheckbox); app.appRegistry.registerComponent('Database.CreateCollectionInput', CreateCollectionInput); @@ -22,6 +24,7 @@ function activate() { * Deactivate all the components in the Schema package. */ function deactivate() { + app.appRegistry.deregisterAction('Database.CollectionsActions'); app.appRegistry.deregisterComponent('Database.CollectionsTable'); app.appRegistry.deregisterComponent('Database.CreateCollectionCheckbox'); app.appRegistry.deregisterComponent('Database.CreateCollectionInput'); diff --git a/src/internal-packages/sidebar/lib/components/sidebar-database.jsx b/src/internal-packages/sidebar/lib/components/sidebar-database.jsx index d5801e05d9a..3e08c55f021 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-database.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-database.jsx @@ -12,6 +12,7 @@ class SidebarDatabase extends React.Component { constructor(props) { super(props); this.state = { expanded: props.expanded }; + this.CollectionsActions = app.appRegistry.getAction('Database.CollectionsActions'); this.CollectionStore = app.appRegistry.getStore('App.CollectionStore'); } @@ -58,7 +59,8 @@ class SidebarDatabase extends React.Component { } handleCreateCollectionClick() { - console.log('Do create collection'); + const databaseName = this.props._id; + this.CollectionsActions.openCreateCollectionDialog(databaseName); } handleDropDBClick() { From e4a2fa57a26d68df5b9ad7656d9ee7a6118a9f34 Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Mon, 16 Jan 2017 17:32:13 +1100 Subject: [PATCH 09/28] Implement handleDropDBClick --- .../sidebar/lib/components/sidebar-database.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/internal-packages/sidebar/lib/components/sidebar-database.jsx b/src/internal-packages/sidebar/lib/components/sidebar-database.jsx index 3e08c55f021..19f7e39a17e 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-database.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-database.jsx @@ -13,6 +13,7 @@ class SidebarDatabase extends React.Component { super(props); this.state = { expanded: props.expanded }; this.CollectionsActions = app.appRegistry.getAction('Database.CollectionsActions'); + this.DatabaseDDLActions = app.appRegistry.getAction('DatabaseDDL.Actions'); this.CollectionStore = app.appRegistry.getStore('App.CollectionStore'); } @@ -64,7 +65,8 @@ class SidebarDatabase extends React.Component { } handleDropDBClick() { - console.log('Do drop database'); + const databaseName = this.props._id; + this.DatabaseDDLActions.openDropDatabaseDialog(databaseName); } render() { From fa4f4154e66b4b501cf7936215d884057bb4bd1f Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Mon, 16 Jan 2017 17:57:39 +1100 Subject: [PATCH 10/28] Implement handleDropCollectionClick --- .../sidebar/lib/components/sidebar-collection.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx b/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx index 925e0ed86cd..1878669d283 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx @@ -14,6 +14,7 @@ class SidebarCollection extends React.Component { active: false }; this.handleClick = this.handleClick.bind(this); + this.CollectionsActions = app.appRegistry.getAction('Database.CollectionsActions'); this.CollectionStore = app.appRegistry.getStore('App.CollectionStore'); } @@ -32,7 +33,9 @@ class SidebarCollection extends React.Component { } handleDropCollectionClick() { - console.log('Do drop collection'); + const databaseName = this.props.database; + const collectionName = this.getCollectionName(); + this.CollectionsActions.openDropCollectionDialog(databaseName, collectionName); } renderReadonly() { From 8dc748b6ead9e80805c6086a02a7a3759a38ae9e Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Mon, 23 Jan 2017 16:53:38 +1100 Subject: [PATCH 11/28] Add CSS hacks to handle collapsible sidebar --- src/internal-packages/sidebar/styles/index.less | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/internal-packages/sidebar/styles/index.less b/src/internal-packages/sidebar/styles/index.less index 43a047be550..e397bf92727 100644 --- a/src/internal-packages/sidebar/styles/index.less +++ b/src/internal-packages/sidebar/styles/index.less @@ -133,7 +133,10 @@ transition: all 180ms ease-out; &-create-database { - right: 0; + // Quick hack so the icon doesn't end up on the right of the entire page + // with the new collapsible sidebar. Designers feel welcome to improve :) + left: 222px; + top: 12px; } &-create-collection { @@ -338,7 +341,8 @@ .compass-sidebar-content, .compass-sidebar-property-column, .compass-sidebar-instance-hostname, - .compass-sidebar-instance-version { + .compass-sidebar-instance-version, + .compass-sidebar-icon-create-database { display: none; } .compass-sidebar-instance { From 273832215efbed4d3e1fe1a1f28f1849087e053d Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Mon, 23 Jan 2017 19:02:35 +1100 Subject: [PATCH 12/28] Sidebar: Add Create Database button --- .../sidebar/lib/components/sidebar.jsx | 14 +++++++++ .../sidebar/styles/index.less | 30 +++++++++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/internal-packages/sidebar/lib/components/sidebar.jsx b/src/internal-packages/sidebar/lib/components/sidebar.jsx index c60feb979a2..3ede3dd509d 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar.jsx @@ -13,6 +13,7 @@ const SidebarInstanceProperties = require('./sidebar-instance-properties'); class Sidebar extends React.Component { constructor(props) { super(props); + this.DatabaseDDLActions = app.appRegistry.getAction('DatabaseDDL.Actions'); this.state = { collapsed: false }; } @@ -44,6 +45,10 @@ class Sidebar extends React.Component { SidebarActions.filterDatabases(re); } + handleCreateDatabaseClick() { + this.DatabaseDDLActions.openCreateDatabaseDialog(); + } + render() { return (
@@ -77,6 +82,15 @@ class Sidebar extends React.Component { ); }) } +
); diff --git a/src/internal-packages/sidebar/styles/index.less b/src/internal-packages/sidebar/styles/index.less index e397bf92727..4158cbaa3bf 100644 --- a/src/internal-packages/sidebar/styles/index.less +++ b/src/internal-packages/sidebar/styles/index.less @@ -1,9 +1,10 @@ @compass-sidebar-base-background-color: #4c5259; @compass-sidebar-active-background-color: #6D7984; +@compass-sidebar-active-hover-background-color: #7D8994; .compass-sidebar { margin: 0; - background: #4c5259; + background: @compass-sidebar-base-background-color; color: #bfbfbe; flex-grow: 0; flex-shrink: 0; @@ -125,6 +126,28 @@ color: #fff; } + &-button { + &-create-database { + background-color: @compass-sidebar-active-background-color; + border-radius: 12px; + border: grey; + color: #fff; + font-size: 11px; + font-weight: bold; + margin: 4px 10px 16px 10px; + padding: 5px; + text-transform: uppercase; + width: 230px; + + .fa-plus { + width: 20px; + } + } + &-create-database:hover { + background-color: @compass-sidebar-active-hover-background-color; + } + } + &-icon { color: @compass-sidebar-base-background-color; font-size: 17px; @@ -278,7 +301,7 @@ &-is-active, &-is-active:hover { - background-color: #6D7984; + background-color: @compass-sidebar-active-background-color; //there's got to be a better way to handle styles on the child when parent -is-active .compass-sidebar-instance-version { @@ -342,7 +365,8 @@ .compass-sidebar-property-column, .compass-sidebar-instance-hostname, .compass-sidebar-instance-version, - .compass-sidebar-icon-create-database { + .compass-sidebar-icon-create-database, + .compass-sidebar-button-create-database { display: none; } .compass-sidebar-instance { From 3a8e9f5f1edec374e6997aa57d562a926b183644 Mon Sep 17 00:00:00 2001 From: KeyboardTsundoku Date: Tue, 24 Jan 2017 11:44:48 +1100 Subject: [PATCH 13/28] switching home content on create database --- .../database-ddl/lib/component/create-database-dialog.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/internal-packages/database-ddl/lib/component/create-database-dialog.jsx b/src/internal-packages/database-ddl/lib/component/create-database-dialog.jsx index 7679f59dfdb..8544ea7e674 100644 --- a/src/internal-packages/database-ddl/lib/component/create-database-dialog.jsx +++ b/src/internal-packages/database-ddl/lib/component/create-database-dialog.jsx @@ -33,6 +33,7 @@ class CreateDatabaseDialog extends React.Component { this.CreateCollectionInput = app.appRegistry.getComponent('Database.CreateCollectionInput'); this.CreateCollectionSizeInput = app.appRegistry.getComponent('Database.CreateCollectionSizeInput'); this.CreateCollectionCheckbox = app.appRegistry.getComponent('Database.CreateCollectionCheckbox'); + this.HomeActions = app.appRegistry.getAction('Home.Actions'); } /** @@ -88,6 +89,7 @@ class CreateDatabaseDialog extends React.Component { this.state.capped, this.state.maxSize ); + this.HomeActions.switchContent(''); } /** From 17fa4855e2a8765910cf79b16cf6fdfa4296dffd Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Tue, 24 Jan 2017 14:05:25 +1100 Subject: [PATCH 14/28] CreateDB: Reuse the NamespaceStore over HomeActions So we get the sidebar to update too :) --- .../database-ddl/lib/component/create-database-dialog.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/internal-packages/database-ddl/lib/component/create-database-dialog.jsx b/src/internal-packages/database-ddl/lib/component/create-database-dialog.jsx index 8544ea7e674..1cba01d5879 100644 --- a/src/internal-packages/database-ddl/lib/component/create-database-dialog.jsx +++ b/src/internal-packages/database-ddl/lib/component/create-database-dialog.jsx @@ -5,6 +5,7 @@ const Modal = require('react-bootstrap').Modal; const { TextButton } = require('hadron-react-buttons'); const Actions = require('../action'); const CreateDatabaseStore = require('../store/create-database-store'); +const { NamespaceStore } = require('hadron-reflux-store'); /** * The more information url. @@ -33,7 +34,6 @@ class CreateDatabaseDialog extends React.Component { this.CreateCollectionInput = app.appRegistry.getComponent('Database.CreateCollectionInput'); this.CreateCollectionSizeInput = app.appRegistry.getComponent('Database.CreateCollectionSizeInput'); this.CreateCollectionCheckbox = app.appRegistry.getComponent('Database.CreateCollectionCheckbox'); - this.HomeActions = app.appRegistry.getAction('Home.Actions'); } /** @@ -89,7 +89,7 @@ class CreateDatabaseDialog extends React.Component { this.state.capped, this.state.maxSize ); - this.HomeActions.switchContent(''); + NamespaceStore.ns = ''; } /** From 39b3233dbc42dd8012943cf9c32003181244f7f1 Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Tue, 24 Jan 2017 14:14:37 +1100 Subject: [PATCH 15/28] CreateCollection: Change to Collections list onCreateCollectionButtonClicked --- .../database/lib/components/create-collection-dialog.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/internal-packages/database/lib/components/create-collection-dialog.jsx b/src/internal-packages/database/lib/components/create-collection-dialog.jsx index dd93ea8f635..5554930f945 100644 --- a/src/internal-packages/database/lib/components/create-collection-dialog.jsx +++ b/src/internal-packages/database/lib/components/create-collection-dialog.jsx @@ -2,6 +2,7 @@ const app = require('ampersand-app'); const shell = require('electron').shell; const React = require('react'); const Modal = require('react-bootstrap').Modal; +const { NamespaceStore } = require('hadron-reflux-store'); const { TextButton } = require('hadron-react-buttons'); const Actions = require('../actions/collections-actions'); const CreateCollectionStore = require('../stores/create-collection-store'); @@ -80,12 +81,14 @@ class CreateCollectionDialog extends React.Component { evt.stopPropagation(); this.setState({ inProgress: true, error: false, errorMessage: '' }); + const databaseName = this.state.databaseName; Actions.createCollection( - this.state.databaseName, + databaseName, this.state.collectionName, this.state.capped, this.state.maxSize ); + NamespaceStore.ns = databaseName; } /** From d015b3e3dbc17d6538a2fe420fd6e072ec76ce64 Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Tue, 24 Jan 2017 14:19:27 +1100 Subject: [PATCH 16/28] DropCollection: Change to Collections list onDropCollectionButtonClicked --- .../database/lib/components/drop-collection-dialog.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/internal-packages/database/lib/components/drop-collection-dialog.jsx b/src/internal-packages/database/lib/components/drop-collection-dialog.jsx index 4e05301ff93..974f19142fd 100644 --- a/src/internal-packages/database/lib/components/drop-collection-dialog.jsx +++ b/src/internal-packages/database/lib/components/drop-collection-dialog.jsx @@ -1,6 +1,7 @@ const app = require('ampersand-app'); const React = require('react'); const Modal = require('react-bootstrap').Modal; +const { NamespaceStore } = require('hadron-reflux-store'); const { TextButton } = require('hadron-react-buttons'); const Actions = require('../actions/collections-actions'); const DropCollectionStore = require('../stores/drop-collection-store'); @@ -74,6 +75,7 @@ class DropCollectionDialog extends React.Component { this.setState({ inProgress: true, error: false, errorMessage: '' }); Actions.dropCollection(this.state.databaseName, this.state.collectionName); + NamespaceStore.ns = this.state.databaseName; } /** From 259805a81b3e4940868445151f6af207f9351784 Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Tue, 24 Jan 2017 14:21:07 +1100 Subject: [PATCH 17/28] DropDatabase: Change to Databases list onDropDatabaseButtonClicked --- .../database-ddl/lib/component/drop-database-dialog.jsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/internal-packages/database-ddl/lib/component/drop-database-dialog.jsx b/src/internal-packages/database-ddl/lib/component/drop-database-dialog.jsx index 9b5688df585..549f5ea4bc1 100644 --- a/src/internal-packages/database-ddl/lib/component/drop-database-dialog.jsx +++ b/src/internal-packages/database-ddl/lib/component/drop-database-dialog.jsx @@ -1,6 +1,7 @@ const app = require('ampersand-app'); const React = require('react'); const Modal = require('react-bootstrap').Modal; +const { NamespaceStore } = require('hadron-reflux-store'); const { TextButton } = require('hadron-react-buttons'); const Actions = require('../action'); const DropDatabaseStore = require('../store/drop-database-store'); @@ -62,11 +63,13 @@ class DropDatabaseDialog extends React.Component { evt.stopPropagation(); // prevent drop database if names don't match - if (this.state.confirmName !== this.state.name) { + const databaseName = this.state.name; + if (this.state.confirmName !== databaseName) { return; } this.setState({ inProgress: true, error: false, errorMessage: '' }); - Actions.dropDatabase(this.state.name); + Actions.dropDatabase(databaseName); + NamespaceStore.ns = ''; } /** From b20e12be3efca1862d4abeb351ec5798fd412273 Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Tue, 24 Jan 2017 17:06:21 +1100 Subject: [PATCH 18/28] Update SidebarInstance tests to detect dataservice not writable --- ...js => sidebar-instance-properties.test.js} | 67 ++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) rename test/enzyme/{sidebar-instance.test.js => sidebar-instance-properties.test.js} (59%) diff --git a/test/enzyme/sidebar-instance.test.js b/test/enzyme/sidebar-instance-properties.test.js similarity index 59% rename from test/enzyme/sidebar-instance.test.js rename to test/enzyme/sidebar-instance-properties.test.js index 9088b84bc8e..28ccada540d 100644 --- a/test/enzyme/sidebar-instance.test.js +++ b/test/enzyme/sidebar-instance-properties.test.js @@ -12,16 +12,66 @@ const SidebarInstanceProperties = require('../../src/internal-packages/sidebar/l chai.use(chaiEnzyme()); const appRegistry = app.appRegistry; +const dataService = app.dataService; describe('', () => { - beforeEach(() => { + beforeEach(function() { app.appRegistry = new AppRegistry(); - app.appRegistry.registerAction('DatabaseDDL.Actions', sinon.spy()); + this.DatabaseDDLActionSpy = sinon.spy(); + app.appRegistry.registerAction( + 'DatabaseDDL.Actions', + {openCreateDatabaseDialog: this.DatabaseDDLActionSpy} + ); + app.dataService = { + isWritable: () => { + return true; + } + }; }); afterEach(() => { + app.dataService = dataService; app.appRegistry = appRegistry; }); + context('when no SSH Tunnel and dataService is not writable', function() { + beforeEach(function() { + const connection = { + hostname: 'ip-1-2-3-4-mongod.com', + port: 27000, + ssh_tunnel: 'NONE' + }; + const instance = { + build: { + enterprise_module: true, + version: '3.4.0-rc3' + }, + collections: [], + databases: [] + }; + app.dataService = { + isWritable: () => { + return false; + } + }; + this.component = shallow( + ); + }); + it('warns the create database icon does not work on secondaries', function() { + const expected = 'Create database is not available on a secondary node'; + const element = this.component.find('.compass-sidebar-icon-create-database'); + expect(element.prop('data-tip')).to.be.equal(expected); + }); + it('the create database icon triggers no action', function() { + const element = this.component.find('.compass-sidebar-icon-create-database'); + element.simulate('click'); + expect(this.DatabaseDDLActionSpy.called).to.be.false; + }); + }); + context('when rendering with no SSH Tunnel', () => { beforeEach(function() { const connection = { @@ -44,6 +94,19 @@ describe('', () => { activeNamespace={''} />); }); + context('when dataService is writable', function() { + it('renders a create database icon with tooltip', function() { + const expected = 'Create database'; + const element = this.component.find('.compass-sidebar-icon-create-database'); + expect(element.prop('data-tip')).to.be.equal(expected); + }); + it('clicking the create database icon triggers an action', function() { + const element = this.component.find('.compass-sidebar-icon-create-database'); + element.simulate('click'); + expect(this.DatabaseDDLActionSpy.calledOnce).to.be.true; + }); + }); + it('renders the endpoint host name and port as text', function() { const element = this.component.find('.compass-sidebar-instance-hostname'); expect(element.text()).to.be.equal('ip-1-2-3-4-mongod.com:27000'); From 41c610ef46a3cd04b857eb4432e0b6c860bfc74e Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Tue, 24 Jan 2017 22:28:56 +1100 Subject: [PATCH 19/28] Handle SidebarInstance isWritable --- .../sidebar/lib/components/constants.js | 2 +- .../components/sidebar-instance-properties.jsx | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/internal-packages/sidebar/lib/components/constants.js b/src/internal-packages/sidebar/lib/components/constants.js index 0a46f617196..cc8deb6460f 100644 --- a/src/internal-packages/sidebar/lib/components/constants.js +++ b/src/internal-packages/sidebar/lib/components/constants.js @@ -1,6 +1,6 @@ const TOOLTIP_IDS = Object.freeze({ CREATE_COLLECTION: 'create-collection', - CREATE_DATABASE: 'create-database', + CREATE_DATABASE_ICON: 'create-database-icon', DROP_COLLECTION: 'drop-collection', DROP_DATABASE: 'drop-database' }); diff --git a/src/internal-packages/sidebar/lib/components/sidebar-instance-properties.jsx b/src/internal-packages/sidebar/lib/components/sidebar-instance-properties.jsx index 9b5fea7dce2..7023a9f70e9 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-instance-properties.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-instance-properties.jsx @@ -60,20 +60,25 @@ class SidebarInstanceProperties extends React.Component { ipc.call('window:hide-collection-submenu'); } - handleCreateDatabaseClick() { - this.DatabaseDDLActions.openCreateDatabaseDialog(); + handleCreateDatabaseClick(isWritable) { + if (isWritable) { + this.DatabaseDDLActions.openCreateDatabaseDialog(); + } } render() { const instance = this.props.instance; + const isWritable = app.dataService.isWritable(); const numDbs = instance.databases.length; const numCollections = instance.collections.length; const hostnameAndPort = this.getHostnameAndPort(); const sshTunnelViaPort = this.getSshTunnelViaPort(); const versionName = this.getVersionName(); - const tooltipText = 'Create database'; + const tooltipText = isWritable ? + 'Create database' : + 'Create database is not available on a secondary node'; // TODO: Arbiter/recovering/etc const tooltipOptions = { - 'data-for': TOOLTIP_IDS.CREATE_DATABASE, + 'data-for': TOOLTIP_IDS.CREATE_DATABASE_ICON, 'data-class': 'compass-sidebar-tooltip-should-be-visible', 'data-effect': 'solid', 'data-place': 'right', @@ -91,10 +96,10 @@ class SidebarInstanceProperties extends React.Component { - +
From 1d9a4c0e98bda41c837eda4b776ab8b3d80179e5 Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Tue, 24 Jan 2017 23:51:29 +1100 Subject: [PATCH 20/28] CreateDBButton: Add tooltip and handle secondaries/arbiters + tests --- .../sidebar/lib/components/constants.js | 1 + .../sidebar/lib/components/sidebar.jsx | 54 ++++++++---- .../sidebar/styles/index.less | 8 +- test/enzyme/sidebar.test.js | 84 +++++++++++++++++++ 4 files changed, 128 insertions(+), 19 deletions(-) create mode 100644 test/enzyme/sidebar.test.js diff --git a/src/internal-packages/sidebar/lib/components/constants.js b/src/internal-packages/sidebar/lib/components/constants.js index cc8deb6460f..9490c49571f 100644 --- a/src/internal-packages/sidebar/lib/components/constants.js +++ b/src/internal-packages/sidebar/lib/components/constants.js @@ -1,5 +1,6 @@ const TOOLTIP_IDS = Object.freeze({ CREATE_COLLECTION: 'create-collection', + CREATE_DATABASE_BUTTON: 'create-database-button', CREATE_DATABASE_ICON: 'create-database-icon', DROP_COLLECTION: 'drop-collection', DROP_DATABASE: 'drop-database' diff --git a/src/internal-packages/sidebar/lib/components/sidebar.jsx b/src/internal-packages/sidebar/lib/components/sidebar.jsx index 3ede3dd509d..ae2d0295c75 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar.jsx @@ -1,11 +1,10 @@ const React = require('react'); - +const ReactTooltip = require('react-tooltip'); const app = require('ampersand-app'); -const StoreConnector = app.appRegistry.getComponent('App.StoreConnector'); -const InstanceStore = app.appRegistry.getStore('App.InstanceStore'); const SidebarActions = require('../actions'); const SidebarDatabase = require('./sidebar-database'); const SidebarInstanceProperties = require('./sidebar-instance-properties'); +const { TOOLTIP_IDS } = require('./constants'); // const debug = require('debug')('mongodb-compass:sidebar:sidebar'); @@ -14,6 +13,8 @@ class Sidebar extends React.Component { constructor(props) { super(props); this.DatabaseDDLActions = app.appRegistry.getAction('DatabaseDDL.Actions'); + this.InstanceStore = app.appRegistry.getStore('App.InstanceStore'); + this.StoreConnector = app.appRegistry.getComponent('App.StoreConnector'); this.state = { collapsed: false }; } @@ -45,8 +46,37 @@ class Sidebar extends React.Component { SidebarActions.filterDatabases(re); } - handleCreateDatabaseClick() { - this.DatabaseDDLActions.openCreateDatabaseDialog(); + handleCreateDatabaseClick(isWritable) { + if (isWritable) { + this.DatabaseDDLActions.openCreateDatabaseDialog(); + } + } + + renderCreateDatabaseButton() { + const isWritable = app.dataService.isWritable(); + const tooltipText = isWritable ? + 'Create database' : + 'Not available on a secondary node'; // TODO: Arbiter/recovering/etc + const tooltipOptions = { + 'data-for': TOOLTIP_IDS.CREATE_DATABASE_BUTTON, + 'data-class': 'compass-sidebar-tooltip-should-be-visible', + 'data-effect': 'solid', + 'data-place': 'top', + 'data-tip': tooltipText + }; + return ( + + ); } render() { @@ -58,12 +88,12 @@ class Sidebar extends React.Component { >
- + - +
@@ -82,15 +112,7 @@ class Sidebar extends React.Component { ); }) } - + {this.renderCreateDatabaseButton()}
); diff --git a/src/internal-packages/sidebar/styles/index.less b/src/internal-packages/sidebar/styles/index.less index 4158cbaa3bf..d3b0a546aaf 100644 --- a/src/internal-packages/sidebar/styles/index.less +++ b/src/internal-packages/sidebar/styles/index.less @@ -132,16 +132,18 @@ border-radius: 12px; border: grey; color: #fff; - font-size: 11px; - font-weight: bold; margin: 4px 10px 16px 10px; padding: 5px; - text-transform: uppercase; width: 230px; .fa-plus { width: 20px; } + .plus-button { + font-size: 11px; + font-weight: bold; + text-transform: uppercase; + } } &-create-database:hover { background-color: @compass-sidebar-active-hover-background-color; diff --git a/test/enzyme/sidebar.test.js b/test/enzyme/sidebar.test.js new file mode 100644 index 00000000000..9950fdb1c32 --- /dev/null +++ b/test/enzyme/sidebar.test.js @@ -0,0 +1,84 @@ +// /* eslint no-unused-vars: 0, no-unused-expressions: 0 */ +const app = require('ampersand-app'); +const chai = require('chai'); +const chaiEnzyme = require('chai-enzyme'); +const expect = chai.expect; +const React = require('react'); +const sinon = require('sinon'); +const AppRegistry = require('hadron-app-registry'); +const { shallow } = require('enzyme'); +const Sidebar = require('../../src/internal-packages/sidebar/lib/components/sidebar'); +const InstanceStore = require('../../src/internal-packages/app/lib/stores/instance-store'); +const StoreConnector = require('../../src/internal-packages/app/lib/components/store-connector'); + +chai.use(chaiEnzyme()); + +const appDataService = app.dataService; +const appRegistry = app.appRegistry; + +describe('', () => { + beforeEach(function() { + app.appRegistry = new AppRegistry(); + this.DatabaseDDLActionSpy = sinon.spy(); + app.appRegistry.registerAction( + 'DatabaseDDL.Actions', + {openCreateDatabaseDialog: this.DatabaseDDLActionSpy} + ); + app.appRegistry.registerComponent('App.StoreConnector', StoreConnector); + app.appRegistry.registerStore('App.InstanceStore', InstanceStore); + app.dataService = { + isWritable: () => { + return true; + } + }; + }); + afterEach(() => { + app.dataService = appDataService; + app.appRegistry = appRegistry; + }); + + context('when dataService is not writable', function() { + beforeEach(function() { + app.dataService = { + isWritable: () => { + return false; + } + }; + this.component = shallow( + ); + }); + it('warns the create database button is not available on secondaries', function() { + const expected = 'Not available on a secondary node'; + const element = this.component.find('.compass-sidebar-button-create-database'); + expect(element.prop('data-tip')).to.be.equal(expected); + }); + it('the create database button triggers no action', function() { + const element = this.component.find('.compass-sidebar-button-create-database'); + element.simulate('click'); + expect(this.DatabaseDDLActionSpy.called).to.be.false; + }); + }); + + context('when dataService is writable', () => { + beforeEach(function() { + this.component = shallow( + ); + }); + it('renders a create database button with tooltip', function() { + const expected = 'Create database'; + const element = this.component.find('.compass-sidebar-button-create-database'); + expect(element.prop('data-tip')).to.be.equal(expected); + }); + it('clicking the create database button triggers an action', function() { + const element = this.component.find('.compass-sidebar-button-create-database'); + element.simulate('click'); + expect(this.DatabaseDDLActionSpy.calledOnce).to.be.true; + }); + }); +}); From b71989db564aadedb850465b7c127630468607ff Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Wed, 25 Jan 2017 00:05:47 +1100 Subject: [PATCH 21/28] DropCollectionIcon: Handle secondaries/arbiters + tests --- .../lib/components/sidebar-collection.jsx | 17 ++-- test/enzyme/sidebar-collection.test.js | 82 +++++++++++++++++++ 2 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 test/enzyme/sidebar-collection.test.js diff --git a/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx b/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx index 1878669d283..1fa388fcd3b 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx @@ -32,10 +32,12 @@ class SidebarCollection extends React.Component { } } - handleDropCollectionClick() { - const databaseName = this.props.database; - const collectionName = this.getCollectionName(); - this.CollectionsActions.openDropCollectionDialog(databaseName, collectionName); + handleDropCollectionClick(isWritable) { + if (isWritable) { + const databaseName = this.props.database; + const collectionName = this.getCollectionName(); + this.CollectionsActions.openDropCollectionDialog(databaseName, collectionName); + } } renderReadonly() { @@ -48,7 +50,10 @@ class SidebarCollection extends React.Component { render() { const collectionName = this.getCollectionName(); - const tooltipText = `Drop ${this.props.database}.${collectionName} collection`; + const isWritable = app.dataService.isWritable(); + const tooltipText = isWritable ? + `Drop ${this.props.database}.${collectionName} collection` : + 'Drop collection is not available on a secondary node'; // TODO: Arbiter/recovering/etc const tooltipOptions = { 'data-for': TOOLTIP_IDS.DROP_COLLECTION, 'data-effect': 'solid', @@ -63,7 +68,7 @@ class SidebarCollection extends React.Component {
diff --git a/test/enzyme/sidebar-collection.test.js b/test/enzyme/sidebar-collection.test.js new file mode 100644 index 00000000000..b16ebc12c29 --- /dev/null +++ b/test/enzyme/sidebar-collection.test.js @@ -0,0 +1,82 @@ +// /* eslint no-unused-vars: 0, no-unused-expressions: 0 */ +const app = require('ampersand-app'); +const chai = require('chai'); +const chaiEnzyme = require('chai-enzyme'); +const expect = chai.expect; +const React = require('react'); +const sinon = require('sinon'); +const AppRegistry = require('hadron-app-registry'); +const { shallow } = require('enzyme'); +const SidebarCollection = require('../../src/internal-packages/sidebar/lib/components/sidebar-collection'); + +chai.use(chaiEnzyme()); + +const appDataService = app.dataService; +const appRegistry = app.appRegistry; + +describe('', () => { + beforeEach(function() { + app.appRegistry = new AppRegistry(); + this.DatabaseDDLActionSpy = sinon.spy(); + app.appRegistry.registerAction( + 'Database.CollectionsActions', + {openDropCollectionDialog: this.DatabaseDDLActionSpy} + ); + app.dataService = { + isWritable: () => { + return true; + } + }; + }); + afterEach(() => { + app.dataService = appDataService; + app.appRegistry = appRegistry; + }); + + context('when dataService is not writable', function() { + beforeEach(function() { + app.dataService = { + isWritable: () => { + return false; + } + }; + this.component = shallow( + ); + }); + it('warns the drop collection icon does not work on secondaries', function() { + const expected = 'Drop collection is not available on a secondary node'; + const element = this.component.find('.compass-sidebar-icon-drop-collection'); + expect(element.prop('data-tip')).to.be.equal(expected); + }); + it('the drop collection icon triggers no action', function() { + const element = this.component.find('.compass-sidebar-icon-drop-collection'); + element.simulate('click'); + expect(this.DatabaseDDLActionSpy.called).to.be.false; + }); + }); + + context('when dataService is writable', () => { + beforeEach(function() { + this.component = shallow( + ); + }); + it('renders a drop collection icon with tooltip', function() { + const expected = 'Drop foo.bar collection'; + const element = this.component.find('.compass-sidebar-icon-drop-collection'); + expect(element.prop('data-tip')).to.be.equal(expected); + }); + it('clicking the drop collection icon triggers an action', function() { + const element = this.component.find('.compass-sidebar-icon-drop-collection'); + element.simulate('click'); + expect(this.DatabaseDDLActionSpy.calledOnce).to.be.true; + }); + }); +}); From 5da030eb14cb5282ec0105ab818bd6214b1302c1 Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Wed, 25 Jan 2017 00:23:00 +1100 Subject: [PATCH 22/28] AddCollectionIcon & DropDatabaseIcon: Handle secondaries/arbiters + tests --- .../lib/components/sidebar-database.jsx | 29 +++-- test/enzyme/sidebar-database.test.js | 107 ++++++++++++++++++ 2 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 test/enzyme/sidebar-database.test.js diff --git a/src/internal-packages/sidebar/lib/components/sidebar-database.jsx b/src/internal-packages/sidebar/lib/components/sidebar-database.jsx index 19f7e39a17e..2ebc9c3135a 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-database.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-database.jsx @@ -59,25 +59,34 @@ class SidebarDatabase extends React.Component { this.setState({ expanded: !this.state.expanded }); } - handleCreateCollectionClick() { - const databaseName = this.props._id; - this.CollectionsActions.openCreateCollectionDialog(databaseName); + handleCreateCollectionClick(isWritable) { + if (isWritable) { + const databaseName = this.props._id; + this.CollectionsActions.openCreateCollectionDialog(databaseName); + } } - handleDropDBClick() { - const databaseName = this.props._id; - this.DatabaseDDLActions.openDropDatabaseDialog(databaseName); + handleDropDBClick(isWritable) { + if (isWritable) { + const databaseName = this.props._id; + this.DatabaseDDLActions.openDropDatabaseDialog(databaseName); + } } render() { - const createTooltipText = 'Create collection'; + const isWritable = app.dataService.isWritable(); + const createTooltipText = isWritable ? + 'Create collection' : + 'Create collection is not available on a secondary node'; // TODO: Arbiter/recovering/etc const createTooltipOptions = { 'data-for': TOOLTIP_IDS.CREATE_COLLECTION, 'data-effect': 'solid', 'data-offset': "{'bottom': 18, 'left': 3}", 'data-tip': createTooltipText }; - const dropTooltipText = `Drop ${this.props._id} database`; + const dropTooltipText = isWritable ? + `Drop ${this.props._id} database` : + 'Drop database is not available on a secondary node'; // TODO: Arbiter/recovering/etc const dropTooltipOptions = { 'data-for': TOOLTIP_IDS.DROP_DATABASE, 'data-effect': 'solid', @@ -94,13 +103,13 @@ class SidebarDatabase extends React.Component { diff --git a/test/enzyme/sidebar-database.test.js b/test/enzyme/sidebar-database.test.js new file mode 100644 index 00000000000..f5cc172f762 --- /dev/null +++ b/test/enzyme/sidebar-database.test.js @@ -0,0 +1,107 @@ +// /* eslint no-unused-vars: 0, no-unused-expressions: 0 */ +const app = require('ampersand-app'); +const chai = require('chai'); +const chaiEnzyme = require('chai-enzyme'); +const expect = chai.expect; +const React = require('react'); +const sinon = require('sinon'); +const AppRegistry = require('hadron-app-registry'); +const { shallow } = require('enzyme'); +const SidebarDatabase = require('../../src/internal-packages/sidebar/lib/components/sidebar-database'); + +chai.use(chaiEnzyme()); + +const appDataService = app.dataService; +const appRegistry = app.appRegistry; + +describe('', () => { + beforeEach(function() { + app.appRegistry = new AppRegistry(); + this.DatabaseDDLActionSpyCreate = sinon.spy(); + this.DatabaseDDLActionSpyDrop = sinon.spy(); + app.appRegistry.registerAction( + 'Database.CollectionsActions', + {openCreateCollectionDialog: this.DatabaseDDLActionSpyCreate} + ); + app.appRegistry.registerAction( + 'DatabaseDDL.Actions', + {openDropDatabaseDialog: this.DatabaseDDLActionSpyDrop} + ); + app.dataService = { + isWritable: () => { + return true; + } + }; + }); + afterEach(() => { + app.dataService = appDataService; + app.appRegistry = appRegistry; + }); + + context('when dataService is not writable', function() { + beforeEach(function() { + app.dataService = { + isWritable: () => { + return false; + } + }; + this.component = shallow( + ); + }); + it('warns the create collection icon does not work on secondaries', function() { + const expected = 'Create collection is not available on a secondary node'; + const element = this.component.find('.compass-sidebar-icon-create-collection'); + expect(element.prop('data-tip')).to.be.equal(expected); + }); + it('the create collection icon triggers no action', function() { + const element = this.component.find('.compass-sidebar-icon-create-collection'); + element.simulate('click'); + expect(this.DatabaseDDLActionSpyCreate.called).to.be.false; + }); + + it('warns the drop database icon does not work on secondaries', function() { + const expected = 'Drop database is not available on a secondary node'; + const element = this.component.find('.compass-sidebar-icon-drop-database'); + expect(element.prop('data-tip')).to.be.equal(expected); + }); + it('the drop database icon triggers no action', function() { + const element = this.component.find('.compass-sidebar-icon-drop-database'); + element.simulate('click'); + expect(this.DatabaseDDLActionSpyDrop.called).to.be.false; + }); + }); + + context('when dataService is writable', () => { + beforeEach(function() { + this.component = shallow( + ); + }); + it('renders a create collection icon with tooltip', function() { + const expected = 'Create collection'; + const element = this.component.find('.compass-sidebar-icon-create-collection'); + expect(element.prop('data-tip')).to.be.equal(expected); + }); + it('clicking the create collection icon triggers an action', function() { + const element = this.component.find('.compass-sidebar-icon-create-collection'); + element.simulate('click'); + expect(this.DatabaseDDLActionSpyCreate.calledOnce).to.be.true; + }); + + it('renders a drop database icon with tooltip', function() { + const expected = 'Drop foo database'; + const element = this.component.find('.compass-sidebar-icon-drop-database'); + expect(element.prop('data-tip')).to.be.equal(expected); + }); + it('clicking the drop database icon triggers an action', function() { + const element = this.component.find('.compass-sidebar-icon-drop-database'); + element.simulate('click'); + expect(this.DatabaseDDLActionSpyDrop.calledOnce).to.be.true; + }); + }); +}); From 2325d8d4958dd66d1b3979c0eebc7a6e3450279f Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Wed, 25 Jan 2017 11:45:39 +1100 Subject: [PATCH 23/28] Remove db/collection names from tooltips --- .../sidebar/lib/components/sidebar-collection.jsx | 2 +- .../sidebar/lib/components/sidebar-database.jsx | 2 +- test/enzyme/sidebar-collection.test.js | 2 +- test/enzyme/sidebar-database.test.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx b/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx index 1fa388fcd3b..57b9855f70e 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx @@ -52,7 +52,7 @@ class SidebarCollection extends React.Component { const collectionName = this.getCollectionName(); const isWritable = app.dataService.isWritable(); const tooltipText = isWritable ? - `Drop ${this.props.database}.${collectionName} collection` : + `Drop collection` : 'Drop collection is not available on a secondary node'; // TODO: Arbiter/recovering/etc const tooltipOptions = { 'data-for': TOOLTIP_IDS.DROP_COLLECTION, diff --git a/src/internal-packages/sidebar/lib/components/sidebar-database.jsx b/src/internal-packages/sidebar/lib/components/sidebar-database.jsx index 2ebc9c3135a..257b7c255fc 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-database.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-database.jsx @@ -85,7 +85,7 @@ class SidebarDatabase extends React.Component { 'data-tip': createTooltipText }; const dropTooltipText = isWritable ? - `Drop ${this.props._id} database` : + `Drop database` : 'Drop database is not available on a secondary node'; // TODO: Arbiter/recovering/etc const dropTooltipOptions = { 'data-for': TOOLTIP_IDS.DROP_DATABASE, diff --git a/test/enzyme/sidebar-collection.test.js b/test/enzyme/sidebar-collection.test.js index b16ebc12c29..55dc098fc3a 100644 --- a/test/enzyme/sidebar-collection.test.js +++ b/test/enzyme/sidebar-collection.test.js @@ -69,7 +69,7 @@ describe('', () => { />); }); it('renders a drop collection icon with tooltip', function() { - const expected = 'Drop foo.bar collection'; + const expected = 'Drop collection'; const element = this.component.find('.compass-sidebar-icon-drop-collection'); expect(element.prop('data-tip')).to.be.equal(expected); }); diff --git a/test/enzyme/sidebar-database.test.js b/test/enzyme/sidebar-database.test.js index f5cc172f762..4f36798a399 100644 --- a/test/enzyme/sidebar-database.test.js +++ b/test/enzyme/sidebar-database.test.js @@ -94,7 +94,7 @@ describe('', () => { }); it('renders a drop database icon with tooltip', function() { - const expected = 'Drop foo database'; + const expected = 'Drop database'; const element = this.component.find('.compass-sidebar-icon-drop-database'); expect(element.prop('data-tip')).to.be.equal(expected); }); From 5588c88280efb6081433c641f5c907c94c761b52 Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Wed, 25 Jan 2017 11:57:34 +1100 Subject: [PATCH 24/28] Only show Create Database Button tooltip when connected to a secondary --- src/internal-packages/sidebar/lib/components/sidebar.jsx | 7 +++---- test/enzyme/sidebar.test.js | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/internal-packages/sidebar/lib/components/sidebar.jsx b/src/internal-packages/sidebar/lib/components/sidebar.jsx index ae2d0295c75..4af31955bb8 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar.jsx @@ -54,10 +54,9 @@ class Sidebar extends React.Component { renderCreateDatabaseButton() { const isWritable = app.dataService.isWritable(); - const tooltipText = isWritable ? - 'Create database' : - 'Not available on a secondary node'; // TODO: Arbiter/recovering/etc - const tooltipOptions = { + const tooltipText = 'Not available on a secondary node'; // TODO: Arbiter/recovering/etc + // Only show this tooltip on a secondary + const tooltipOptions = isWritable ? {} : { 'data-for': TOOLTIP_IDS.CREATE_DATABASE_BUTTON, 'data-class': 'compass-sidebar-tooltip-should-be-visible', 'data-effect': 'solid', diff --git a/test/enzyme/sidebar.test.js b/test/enzyme/sidebar.test.js index 9950fdb1c32..cbbad33e934 100644 --- a/test/enzyme/sidebar.test.js +++ b/test/enzyme/sidebar.test.js @@ -70,10 +70,9 @@ describe('', () => { activeNamespace={''} />); }); - it('renders a create database button with tooltip', function() { - const expected = 'Create database'; + it('renders a create database button with no tooltip', function() { const element = this.component.find('.compass-sidebar-button-create-database'); - expect(element.prop('data-tip')).to.be.equal(expected); + expect(element.prop('data-tip')).to.be.undefined; }); it('clicking the create database button triggers an action', function() { const element = this.component.find('.compass-sidebar-button-create-database'); From 0c25d4760f72e26f5dc63f6ca31de936e1116879 Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Wed, 25 Jan 2017 12:22:37 +1100 Subject: [PATCH 25/28] Add compass-sidebar-icon-is-disabled CSS classes for secondary tooltips Also disambiguate other tooltip parameters. --- .../lib/components/sidebar-collection.jsx | 12 ++++++++---- .../lib/components/sidebar-database.jsx | 18 +++++++++++++----- .../components/sidebar-instance-properties.jsx | 12 ++++++++---- test/enzyme/sidebar-collection.test.js | 4 ++++ test/enzyme/sidebar-database.test.js | 8 ++++++++ .../enzyme/sidebar-instance-properties.test.js | 4 ++++ 6 files changed, 45 insertions(+), 13 deletions(-) diff --git a/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx b/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx index 57b9855f70e..f06a6512a82 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-collection.jsx @@ -60,21 +60,25 @@ class SidebarCollection extends React.Component { 'data-offset': "{'bottom': 18, 'left': 3}", 'data-tip': tooltipText }; - let className = 'compass-sidebar-title compass-sidebar-title-is-actionable'; + let titleClassName = 'compass-sidebar-title compass-sidebar-title-is-actionable'; if (this.props.activeNamespace === this.props._id) { - className += ' compass-sidebar-title-is-active'; + titleClassName += ' compass-sidebar-title-is-active'; + } + let dropClassName = 'compass-sidebar-icon compass-sidebar-icon-drop-collection fa fa-trash-o'; + if (!isWritable) { + dropClassName += ' compass-sidebar-icon-is-disabled'; } return (
{collectionName}  diff --git a/src/internal-packages/sidebar/lib/components/sidebar-database.jsx b/src/internal-packages/sidebar/lib/components/sidebar-database.jsx index 257b7c255fc..fa080fc7cbf 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-database.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-database.jsx @@ -93,22 +93,30 @@ class SidebarDatabase extends React.Component { 'data-offset': "{'bottom': 18, 'left': 3}", 'data-tip': dropTooltipText }; - let className = 'compass-sidebar-item-header compass-sidebar-item-header-is-expandable compass-sidebar-item-header-is-actionable'; + let headerClassName = 'compass-sidebar-item-header compass-sidebar-item-header-is-expandable compass-sidebar-item-header-is-actionable'; if (this.props.activeNamespace === this.props._id) { - className += ' compass-sidebar-item-header-is-active'; + headerClassName += ' compass-sidebar-item-header-is-active'; + } + let createClassName = 'compass-sidebar-icon compass-sidebar-icon-create-collection fa fa-plus-circle'; + if (!isWritable) { + createClassName += ' compass-sidebar-icon-is-disabled'; + } + let dropClassName = 'compass-sidebar-icon compass-sidebar-icon-drop-database fa fa-trash-o'; + if (!isWritable) { + dropClassName += ' compass-sidebar-icon-is-disabled'; } return (
-
+
diff --git a/src/internal-packages/sidebar/lib/components/sidebar-instance-properties.jsx b/src/internal-packages/sidebar/lib/components/sidebar-instance-properties.jsx index 7023a9f70e9..fb843a34d20 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar-instance-properties.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar-instance-properties.jsx @@ -85,17 +85,21 @@ class SidebarInstanceProperties extends React.Component { 'data-offset': "{'top': 1, 'left': 18}", 'data-tip': tooltipText }; - let className = 'compass-sidebar-instance'; + let instanceClassName = 'compass-sidebar-instance'; // empty string for active namespace means instance level if (this.props.activeNamespace === '') { - className += ' compass-sidebar-instance-is-active'; + instanceClassName += ' compass-sidebar-instance-is-active'; + } + let createClassName = 'compass-sidebar-icon compass-sidebar-icon-create-database fa fa-plus-circle'; + if (!isWritable) { + createClassName += ' compass-sidebar-icon-is-disabled'; } return (
-
+
diff --git a/test/enzyme/sidebar-collection.test.js b/test/enzyme/sidebar-collection.test.js index 55dc098fc3a..d3fd801c203 100644 --- a/test/enzyme/sidebar-collection.test.js +++ b/test/enzyme/sidebar-collection.test.js @@ -47,6 +47,10 @@ describe('', () => { activeNamespace={''} />); }); + it('drop collection contains a disabled BEM modifer class', function() { + const element = this.component.find('.compass-sidebar-icon-drop-collection'); + expect(element.hasClass('compass-sidebar-icon-is-disabled')).to.be.true; + }); it('warns the drop collection icon does not work on secondaries', function() { const expected = 'Drop collection is not available on a secondary node'; const element = this.component.find('.compass-sidebar-icon-drop-collection'); diff --git a/test/enzyme/sidebar-database.test.js b/test/enzyme/sidebar-database.test.js index 4f36798a399..d96cc1759e4 100644 --- a/test/enzyme/sidebar-database.test.js +++ b/test/enzyme/sidebar-database.test.js @@ -51,6 +51,10 @@ describe('', () => { activeNamespace={''} />); }); + it('create collection contains a disabled BEM modifer class', function() { + const element = this.component.find('.compass-sidebar-icon-create-collection'); + expect(element.hasClass('compass-sidebar-icon-is-disabled')).to.be.true; + }); it('warns the create collection icon does not work on secondaries', function() { const expected = 'Create collection is not available on a secondary node'; const element = this.component.find('.compass-sidebar-icon-create-collection'); @@ -62,6 +66,10 @@ describe('', () => { expect(this.DatabaseDDLActionSpyCreate.called).to.be.false; }); + it('drop database contains a disabled BEM modifer class', function() { + const element = this.component.find('.compass-sidebar-icon-drop-database'); + expect(element.hasClass('compass-sidebar-icon-is-disabled')).to.be.true; + }); it('warns the drop database icon does not work on secondaries', function() { const expected = 'Drop database is not available on a secondary node'; const element = this.component.find('.compass-sidebar-icon-drop-database'); diff --git a/test/enzyme/sidebar-instance-properties.test.js b/test/enzyme/sidebar-instance-properties.test.js index 28ccada540d..59f41143a3f 100644 --- a/test/enzyme/sidebar-instance-properties.test.js +++ b/test/enzyme/sidebar-instance-properties.test.js @@ -60,6 +60,10 @@ describe('', () => { activeNamespace={''} />); }); + it('create database icon contains a disabled BEM modifer class', function() { + const element = this.component.find('.compass-sidebar-icon-create-database'); + expect(element.hasClass('compass-sidebar-icon-is-disabled')).to.be.true; + }); it('warns the create database icon does not work on secondaries', function() { const expected = 'Create database is not available on a secondary node'; const element = this.component.find('.compass-sidebar-icon-create-database'); From a6d02235a7174621d983db6685b419a48cbcfaa6 Mon Sep 17 00:00:00 2001 From: Peter Schmidt Date: Wed, 25 Jan 2017 12:22:50 +1100 Subject: [PATCH 26/28] Add compass-sidebar-button-is-disabled CSS classes for secondary tooltips --- src/internal-packages/sidebar/lib/components/sidebar.jsx | 6 +++++- test/enzyme/sidebar.test.js | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/internal-packages/sidebar/lib/components/sidebar.jsx b/src/internal-packages/sidebar/lib/components/sidebar.jsx index 4af31955bb8..f3e8e48107d 100644 --- a/src/internal-packages/sidebar/lib/components/sidebar.jsx +++ b/src/internal-packages/sidebar/lib/components/sidebar.jsx @@ -63,9 +63,13 @@ class Sidebar extends React.Component { 'data-place': 'top', 'data-tip': tooltipText }; + let className = 'compass-sidebar-button-create-database'; + if (!isWritable) { + className += ' compass-sidebar-button-is-disabled'; + } return (