From b6355dcd2d4c02e976e0c7a7e90a7285a584e556 Mon Sep 17 00:00:00 2001 From: Sergey Petushkov Date: Fri, 22 Nov 2024 13:49:08 +0100 Subject: [PATCH] chore(connections): remove single connection ui and hooks COMPASS-8469 (#6518) * chore(connections): remove single connection ui and hooks * chore(connections): rename prop; add comments; more cleanup * chore: fix check --- package-lock.json | 2 - .../sc-connections-navigation-tree.spec.tsx | 549 ---------------- packages/compass-connections/package.json | 1 - .../src/components/atlas-help/atlas-help.tsx | 98 --- .../connecting/connecting-animation.spec.tsx | 47 -- .../connecting/connecting-animation.tsx | 159 ----- .../connecting/connecting-background.tsx | 71 -- .../connecting/connecting-illustration.tsx | 613 ------------------ .../components/connecting/connecting.spec.tsx | 108 --- .../src/components/connecting/connecting.tsx | 130 ---- .../connection-list/connection-icon.tsx | 57 -- .../connection-list/connection-list.spec.tsx | 351 ---------- .../connection-list/connection-list.tsx | 336 ---------- .../connection-list/connection.spec.tsx | 289 --------- .../components/connection-list/connection.tsx | 332 ---------- .../connection-list/connections-title.tsx | 58 -- .../src/components/form-help/form-help.tsx | 75 --- .../components/legacy-connections.spec.tsx | 341 ---------- .../src/components/legacy-connections.tsx | 217 ------- .../compass-connections/src/index.spec.tsx | 44 +- packages/compass-connections/src/index.tsx | 34 +- packages/compass-connections/src/provider.ts | 42 +- ...c.tsx => connections-store-redux.spec.tsx} | 355 ++++------ .../src/stores/connections-store-redux.ts | 47 +- .../src/stores/connections-store.tsx | 84 --- .../src/stores/store-context.tsx | 14 - packages/compass/src/app/components/home.tsx | 114 +--- .../compass/src/app/components/workspace.tsx | 6 +- packages/compass/src/app/index.tsx | 5 - .../compass/src/main/auto-connect.spec.ts | 24 - packages/compass/src/main/auto-connect.ts | 9 - packages/compass/src/main/menu.spec.ts | 218 +------ packages/compass/src/main/menu.ts | 19 - packages/compass/src/main/window-manager.ts | 5 - 34 files changed, 245 insertions(+), 4609 deletions(-) delete mode 100644 packages/compass-connections-navigation/src/sc-connections-navigation-tree.spec.tsx delete mode 100644 packages/compass-connections/src/components/atlas-help/atlas-help.tsx delete mode 100644 packages/compass-connections/src/components/connecting/connecting-animation.spec.tsx delete mode 100644 packages/compass-connections/src/components/connecting/connecting-animation.tsx delete mode 100644 packages/compass-connections/src/components/connecting/connecting-background.tsx delete mode 100644 packages/compass-connections/src/components/connecting/connecting-illustration.tsx delete mode 100644 packages/compass-connections/src/components/connecting/connecting.spec.tsx delete mode 100644 packages/compass-connections/src/components/connecting/connecting.tsx delete mode 100644 packages/compass-connections/src/components/connection-list/connection-icon.tsx delete mode 100644 packages/compass-connections/src/components/connection-list/connection-list.spec.tsx delete mode 100644 packages/compass-connections/src/components/connection-list/connection-list.tsx delete mode 100644 packages/compass-connections/src/components/connection-list/connection.spec.tsx delete mode 100644 packages/compass-connections/src/components/connection-list/connection.tsx delete mode 100644 packages/compass-connections/src/components/connection-list/connections-title.tsx delete mode 100644 packages/compass-connections/src/components/form-help/form-help.tsx delete mode 100644 packages/compass-connections/src/components/legacy-connections.spec.tsx delete mode 100644 packages/compass-connections/src/components/legacy-connections.tsx rename packages/compass-connections/src/stores/{connections-store.spec.tsx => connections-store-redux.spec.tsx} (53%) delete mode 100644 packages/compass-connections/src/stores/connections-store.tsx diff --git a/package-lock.json b/package-lock.json index 3555c19b673..56cf8ad607f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43749,7 +43749,6 @@ "dependencies": { "@mongodb-js/compass-components": "^1.31.1", "@mongodb-js/compass-logging": "^1.4.10", - "@mongodb-js/compass-maybe-protect-connection-string": "^0.28.0", "@mongodb-js/compass-telemetry": "^1.2.3", "@mongodb-js/compass-utils": "^0.6.14", "@mongodb-js/connection-form": "^1.44.0", @@ -55372,7 +55371,6 @@ "requires": { "@mongodb-js/compass-components": "^1.31.1", "@mongodb-js/compass-logging": "^1.4.10", - "@mongodb-js/compass-maybe-protect-connection-string": "^0.28.0", "@mongodb-js/compass-telemetry": "^1.2.3", "@mongodb-js/compass-utils": "^0.6.14", "@mongodb-js/connection-form": "^1.44.0", diff --git a/packages/compass-connections-navigation/src/sc-connections-navigation-tree.spec.tsx b/packages/compass-connections-navigation/src/sc-connections-navigation-tree.spec.tsx deleted file mode 100644 index 4685d5f50d5..00000000000 --- a/packages/compass-connections-navigation/src/sc-connections-navigation-tree.spec.tsx +++ /dev/null @@ -1,549 +0,0 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ -import React from 'react'; -import { - render, - screen, - cleanup, - within, - waitFor, - userEvent, -} from '@mongodb-js/testing-library-compass'; -import { expect } from 'chai'; -import Sinon from 'sinon'; -import { ConnectionsNavigationTree } from './connections-navigation-tree'; -import type { ConnectedConnection } from './tree-data'; -import type { PreferencesAccess } from 'compass-preferences-model'; -import { createSandboxFromDefaultPreferences } from 'compass-preferences-model'; -import { PreferencesProvider } from 'compass-preferences-model/provider'; -import type { WorkspaceTab } from '@mongodb-js/compass-workspaces'; -import { ConnectionStatus } from '@mongodb-js/compass-connections/provider'; - -const connections: ConnectedConnection[] = [ - { - connectionInfo: { id: 'connectionId' } as any, - connectionStatus: ConnectionStatus.Connected, - databases: [ - { - _id: 'foo', - name: 'foo', - collectionsStatus: 'initial', - collectionsLength: 5, - collections: [], - }, - { - _id: 'bar', - name: 'bar', - collectionsStatus: 'ready', - collectionsLength: 3, - collections: [ - { _id: 'bar.meow', name: 'meow', type: 'collection' }, - { _id: 'bar.woof', name: 'woof', type: 'timeseries' }, - { _id: 'bar.bwok', name: 'bwok', type: 'view' }, - ], - }, - ], - databasesLength: 2, - databasesStatus: 'ready', - isDataLake: false, - isReady: true, - isWritable: true, - name: 'test', - isPerformanceTabAvailable: true, - isPerformanceTabSupported: false, - }, -]; - -const TEST_VIRTUAL_PROPS = { - __TEST_REACT_AUTOSIZER_DEFAULT_WIDTH: 1024, - __TEST_REACT_AUTOSIZER_DEFAULT_HEIGHT: 768, - __TEST_REACT_WINDOW_OVERSCAN: Infinity, -}; - -const activeWorkspace = { - connectionId: 'connectionId', - namespace: 'bar.meow', - type: 'Collection', -}; - -const dummyPreferences = { - getPreferences() { - return { - enableMultipleConnectionSystem: false, - }; - }, - onPreferenceValueChanged() {}, -} as unknown as PreferencesAccess; - -function renderComponent( - props: Partial> = {}, - preferences: PreferencesAccess = dummyPreferences -) { - cleanup(); - return render( - - {}} - onItemExpand={() => {}} - {...TEST_VIRTUAL_PROPS} - {...props} - /> - - ); -} - -// TODO(COMPASS-7906): remove -describe.skip('ConnectionsNavigationTree -- Single connection usage', function () { - let preferences: PreferencesAccess; - afterEach(cleanup); - - beforeEach(async function () { - preferences = await createSandboxFromDefaultPreferences(); - renderComponent({}); - }); - - context('when the rename collection feature flag is enabled', () => { - beforeEach(async function () { - await preferences.savePreferences({ - enableRenameCollectionModal: true, - enableMultipleConnectionSystem: false, - }); - - renderComponent( - { - expanded: { connectionId: { bar: true } }, - activeWorkspace: activeWorkspace as WorkspaceTab, - }, - preferences - ); - }); - - it('shows the Rename Collection action', function () { - const collection = screen.getByTestId('connectionId.bar.meow'); - const showActionsButton = within(collection).getByTitle('Show actions'); - - expect(within(collection).getByTitle('Show actions')).to.exist; - - userEvent.click(showActionsButton); - - expect(screen.getByText('Rename collection')).to.exist; - }); - - it('should activate callback with `rename-collection` when corresponding action is clicked', function () { - const spy = Sinon.spy(); - - renderComponent( - { - expanded: { connectionId: { bar: true } }, - activeWorkspace: activeWorkspace as WorkspaceTab, - onItemAction: spy, - }, - preferences - ); - - const collection = screen.getByTestId('connectionId.bar.meow'); - - userEvent.click(within(collection).getByTitle('Show actions')); - userEvent.click(screen.getByText('Rename collection')); - - expect(spy).to.be.calledOnce; - const [[item, action]] = spy.args; - expect(item.type).to.equal('collection'); - expect(item.connectionId).to.equal('connectionId'); - expect(item.namespace).to.equal('bar.meow'); - expect(action).to.equal('rename-collection'); - }); - }); - - it('should render databases', function () { - expect(screen.getByText('foo')).to.exist; - expect(screen.getByText('bar')).to.exist; - }); - - it('should render collections when database is expanded', function () { - renderComponent({ - expanded: { connectionId: { bar: true } }, - }); - - expect(screen.getByText('meow')).to.exist; - expect(screen.getByText('woof')).to.exist; - expect(screen.getByText('bwok')).to.exist; - }); - - it('should render collection placeholders when database is expanded but collections are not ready', function () { - renderComponent({ - expanded: { connectionId: { foo: true } }, - }); - - expect(screen.getAllByTestId('placeholder')).to.have.lengthOf(5); - }); - - it('should make current active namespace tabbable', async function () { - renderComponent({ - activeWorkspace: { - ...activeWorkspace, - namespace: 'bar', - type: 'Collections', - } as WorkspaceTab, - }); - - userEvent.tab(); - - await waitFor(() => { - // Virtual list will be the one to grab the focus first, but will - // immediately forward it to the element and mocking raf here breaks - // virtual list implementatin, waitFor is to accomodate for that - expect(document.querySelector('[data-id="connectionId.bar"]')).to.eq( - document.activeElement - ); - return true; - }); - }); - - describe('when connection is writable', function () { - it('should show all database actions on hover', function () { - userEvent.hover(screen.getByText('foo')); - - const database = screen.getByTestId('connectionId.foo'); - - expect(within(database).getByTitle('Create collection')).to.exist; - expect(within(database).getByTitle('Drop database')).to.exist; - }); - - it('should show all database actions for active namespace', function () { - renderComponent({ - activeWorkspace: { - ...activeWorkspace, - namespace: 'bar', - type: 'Collections', - } as WorkspaceTab, - }); - - const database = screen.getByTestId('connectionId.bar'); - - expect(within(database).getByTitle('Create collection')).to.exist; - expect(within(database).getByTitle('Drop database')).to.exist; - }); - - it('should show all collection actions', function () { - renderComponent({ - expanded: { connectionId: { bar: true } }, - activeWorkspace: activeWorkspace as WorkspaceTab, - }); - - const collection = screen.getByTestId('connectionId.bar.meow'); - const showActionsButton = within(collection).getByTitle('Show actions'); - - expect(within(collection).getByTitle('Show actions')).to.exist; - - userEvent.click(showActionsButton); - - expect(screen.getByText('Open in new tab')).to.exist; - expect(() => screen.getByText('Rename collection')).to.throw; - expect(screen.getByText('Drop collection')).to.exist; - }); - - it('should show all view actions', function () { - renderComponent({ - expanded: { connectionId: { bar: true } }, - activeWorkspace: { - ...activeWorkspace, - namespace: 'bar.bwok', - } as WorkspaceTab, - }); - - const collection = screen.getByTestId('connectionId.bar.bwok'); - const showActionsButton = within(collection).getByTitle('Show actions'); - - expect(within(collection).getByTitle('Show actions')).to.exist; - - userEvent.click(showActionsButton); - - expect(screen.getByText('Open in new tab')).to.exist; - expect(screen.getByText('Drop view')).to.exist; - expect(screen.getByText('Duplicate view')).to.exist; - expect(screen.getByText('Modify view')).to.exist; - - // views cannot be renamed - expect(() => screen.getByText('Rename collection')).to.throw; - }); - }); - - [ - { - name: 'when connection is not writable', - // eslint-disable-next-line @typescript-eslint/require-await - async renderReadonlyComponent( - props: Partial< - React.ComponentProps - > = {} - ) { - const readonlyConnections: ConnectedConnection[] = [ - { - ...connections[0], - ...(props.connections as ConnectedConnection[])?.[0], - isWritable: false, - }, - ]; - renderComponent({ - ...props, - connections: readonlyConnections, - }); - }, - }, - { - name: 'when connection is datalake', - // eslint-disable-next-line @typescript-eslint/require-await - async renderReadonlyComponent( - props: Partial< - React.ComponentProps - > = {} - ) { - const readonlyConnections: ConnectedConnection[] = [ - { - ...connections[0], - ...(props.connections as ConnectedConnection[])?.[0], - isDataLake: true, - }, - ]; - renderComponent({ - ...props, - connections: readonlyConnections, - }); - }, - }, - { - name: 'when preferences is readonly', - async renderReadonlyComponent( - props: Partial< - React.ComponentProps - > = {} - ) { - await preferences.savePreferences({ - readOnly: true, - }); - const readonlyConnections: ConnectedConnection[] = [ - { - ...connections[0], - ...(props.connections as ConnectedConnection[])?.[0], - }, - ]; - renderComponent( - { - ...props, - connections: readonlyConnections, - }, - preferences - ); - }, - }, - ].forEach(function ({ name, renderReadonlyComponent }) { - describe(name, function () { - it('should not show database actions', async function () { - await renderReadonlyComponent({ - activeWorkspace: { - ...activeWorkspace, - namespace: 'bar', - type: 'Collections', - } as WorkspaceTab, - }); - - const database = screen.getByTestId('connectionId.bar'); - - expect(() => within(database).getByTitle('Create collection')).to.throw; - expect(() => within(database).getByTitle('Drop database')).to.throw; - }); - - it('should show only one collection action', async function () { - await renderReadonlyComponent({ - expanded: { connectionId: { bar: true } }, - activeWorkspace: { - ...activeWorkspace, - namespace: 'bar.bwok', - } as WorkspaceTab, - }); - - const collection = screen.getByTestId('connectionId.bar.bwok'); - - await waitFor(() => { - expect(within(collection).getByTitle('Open in new tab')).to.exist; - }); - }); - }); - }); - - describe('onItemAction', function () { - it('should activate callback with `select-database` when database is clicked', function () { - const spy = Sinon.spy(); - renderComponent({ - onItemAction: spy, - }); - - userEvent.click(screen.getByText('foo')); - - expect(spy).to.be.calledOnce; - const [[item, action]] = spy.args; - expect(item.type).to.equal('database'); - expect(item.connectionId).to.equal('connectionId'); - expect(item.dbName).to.equal('foo'); - expect(action).to.equal('select-database'); - }); - - it('should activate callback with `select-collection` when collection is clicked', function () { - const spy = Sinon.spy(); - renderComponent({ - onItemAction: spy, - expanded: { connectionId: { bar: true } }, - }); - - userEvent.click(screen.getByText('meow')); - - expect(spy).to.be.calledOnce; - const [[item, action]] = spy.args; - expect(item.type).to.equal('collection'); - expect(item.connectionId).to.equal('connectionId'); - expect(item.namespace).to.equal('bar.meow'); - expect(action).to.equal('select-collection'); - }); - - describe('database actions', function () { - it('should activate callback with `drop-database` when corresponding action is clicked', function () { - const spy = Sinon.spy(); - renderComponent({ - onItemAction: spy, - activeWorkspace: { - ...activeWorkspace, - namespace: 'foo', - type: 'Collections', - } as WorkspaceTab, - }); - - userEvent.click(screen.getByTitle('Drop database')); - - expect(spy).to.be.calledOnce; - const [[item, action]] = spy.args; - expect(item.type).to.equal('database'); - expect(item.connectionId).to.equal('connectionId'); - expect(item.dbName).to.equal('foo'); - expect(action).to.equal('drop-database'); - }); - - it('should activate callback with `create-collection` when corresponding action is clicked', function () { - const spy = Sinon.spy(); - renderComponent({ - onItemAction: spy, - activeWorkspace: { - ...activeWorkspace, - namespace: 'foo', - type: 'Collections', - } as WorkspaceTab, - }); - - userEvent.click(screen.getByTitle('Create collection')); - - expect(spy).to.be.calledOnce; - const [[item, action]] = spy.args; - expect(item.type).to.equal('database'); - expect(item.connectionId).to.equal('connectionId'); - expect(item.dbName).to.equal('foo'); - expect(action).to.equal('create-collection'); - }); - }); - - describe('collection actions', function () { - it('should activate callback with `open-in-new-tab` when corresponding action is clicked', function () { - const spy = Sinon.spy(); - renderComponent({ - onItemAction: spy, - expanded: { connectionId: { bar: true } }, - activeWorkspace: activeWorkspace as WorkspaceTab, - }); - - const collection = screen.getByTestId('connectionId.bar.meow'); - - userEvent.click(within(collection).getByTitle('Show actions')); - userEvent.click(screen.getByText('Open in new tab')); - - expect(spy).to.be.calledOnce; - const [[item, action]] = spy.args; - expect(item.type).to.equal('collection'); - expect(item.connectionId).to.equal('connectionId'); - expect(item.namespace).to.equal('bar.meow'); - expect(action).to.equal('open-in-new-tab'); - }); - - it('should activate callback with `drop-collection` when corresponding action is clicked', function () { - const spy = Sinon.spy(); - renderComponent({ - onItemAction: spy, - expanded: { connectionId: { bar: true } }, - activeWorkspace: activeWorkspace as WorkspaceTab, - }); - - const collection = screen.getByTestId('connectionId.bar.meow'); - - userEvent.click(within(collection).getByTitle('Show actions')); - userEvent.click(screen.getByText('Drop collection')); - - expect(spy).to.be.calledOnce; - const [[item, action]] = spy.args; - expect(item.type).to.equal('collection'); - expect(item.connectionId).to.equal('connectionId'); - expect(item.namespace).to.equal('bar.meow'); - expect(action).to.equal('drop-collection'); - }); - }); - - describe('view actions', function () { - it('should activate callback with `duplicate-view` when corresponding action is clicked', function () { - const spy = Sinon.spy(); - renderComponent({ - expanded: { connectionId: { bar: true } }, - activeWorkspace: { - ...activeWorkspace, - namespace: 'bar.bwok', - } as WorkspaceTab, - onItemAction: spy, - }); - - const view = screen.getByTestId('connectionId.bar.bwok'); - - userEvent.click(within(view).getByTitle('Show actions')); - userEvent.click(screen.getByText('Duplicate view')); - - expect(spy).to.be.calledOnce; - const [[item, action]] = spy.args; - expect(item.type).to.equal('view'); - expect(item.connectionId).to.equal('connectionId'); - expect(item.namespace).to.equal('bar.bwok'); - expect(action).to.equal('duplicate-view'); - }); - - it('should activate callback with `modify-view` when corresponding action is clicked', function () { - const spy = Sinon.spy(); - renderComponent({ - expanded: { connectionId: { bar: true } }, - activeWorkspace: { - ...activeWorkspace, - namespace: 'bar.bwok', - } as WorkspaceTab, - onItemAction: spy, - }); - - const view = screen.getByTestId('connectionId.bar.bwok'); - - userEvent.click(within(view).getByTitle('Show actions')); - userEvent.click(screen.getByText('Modify view')); - - expect(spy).to.be.calledOnce; - const [[item, action]] = spy.args; - expect(item.type).to.equal('view'); - expect(item.connectionId).to.equal('connectionId'); - expect(item.namespace).to.equal('bar.bwok'); - expect(action).to.equal('modify-view'); - }); - }); - }); -}); diff --git a/packages/compass-connections/package.json b/packages/compass-connections/package.json index 5f078107074..3bff9ee3880 100644 --- a/packages/compass-connections/package.json +++ b/packages/compass-connections/package.json @@ -53,7 +53,6 @@ "dependencies": { "@mongodb-js/compass-components": "^1.31.1", "@mongodb-js/compass-logging": "^1.4.10", - "@mongodb-js/compass-maybe-protect-connection-string": "^0.28.0", "@mongodb-js/compass-telemetry": "^1.2.3", "@mongodb-js/compass-utils": "^0.6.14", "@mongodb-js/connection-form": "^1.44.0", diff --git a/packages/compass-connections/src/components/atlas-help/atlas-help.tsx b/packages/compass-connections/src/components/atlas-help/atlas-help.tsx deleted file mode 100644 index 5c265e0f095..00000000000 --- a/packages/compass-connections/src/components/atlas-help/atlas-help.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import React from 'react'; -import { - Button, - ButtonSize, - ButtonVariant, - Subtitle, - Body, - Link, - spacing, - palette, - css, - cx, - useDarkMode, -} from '@mongodb-js/compass-components'; -import { useTelemetry } from '@mongodb-js/compass-telemetry/provider'; - -const sectionContainerStyles = css({ - margin: 0, - padding: spacing[4], - paddingBottom: 0, -}); - -const atlasContainerStyles = css({ - backgroundColor: palette.green.light3, - paddingBottom: spacing[4], -}); - -const atlasContainerDarkModeStyles = css({ - backgroundColor: palette.green.dark3, -}); - -const titleStyles = css({ - fontSize: '14px', -}); - -const descriptionStyles = css({ - marginTop: spacing[2], -}); - -const createClusterContainerStyles = css({ - marginTop: spacing[2], -}); - -const createClusterButtonStyles = css({ - fontWeight: 'bold', -}); - -const createClusterButtonLightModeStyles = css({ - background: palette.white, - '&:hover': { - background: palette.white, - }, - '&:focus': { - background: palette.white, - }, -}); - -export function AtlasHelpSection(): React.ReactElement { - const track = useTelemetry(); - const darkMode = useDarkMode(); - - return ( -
- - New to Compass and don't have a cluster? - - - If you don't already have a cluster, you can create one for free - using{' '} - - MongoDB Atlas - - -
- -
-
- ); -} diff --git a/packages/compass-connections/src/components/connecting/connecting-animation.spec.tsx b/packages/compass-connections/src/components/connecting/connecting-animation.spec.tsx deleted file mode 100644 index bbd3d75a470..00000000000 --- a/packages/compass-connections/src/components/connecting/connecting-animation.spec.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react'; -import { render } from '@mongodb-js/testing-library-compass'; -import { expect } from 'chai'; -import sinon from 'sinon'; - -import ConnectingAnimation from './connecting-animation'; - -describe('ConnectingAnimation Component', function () { - describe('when rendered', function () { - let unmountComponent; - let cancelAnimationFrameSpy; - let requestAnimationFrameSpy; - - beforeEach(function () { - requestAnimationFrameSpy = sinon.spy(window.requestAnimationFrame); - cancelAnimationFrameSpy = sinon.spy(window.cancelAnimationFrame); - - sinon.replace(window, 'requestAnimationFrame', requestAnimationFrameSpy); - sinon.replace(window, 'cancelAnimationFrame', cancelAnimationFrameSpy); - - const { unmount } = render(); - unmountComponent = unmount; - }); - afterEach(function () { - unmountComponent = null; - sinon.restore(); - }); - - it('calls to request an animation frame', function () { - expect(requestAnimationFrameSpy.called).to.equal(true); - }); - - it('does not call to cancelAnimationFrame', function () { - expect(cancelAnimationFrameSpy.called).to.equal(false); - }); - - describe('when unmounted', function () { - beforeEach(function () { - unmountComponent(); - }); - - it('calls to cancels the request animation frame', function () { - expect(cancelAnimationFrameSpy.called).to.equal(true); - }); - }); - }); -}); diff --git a/packages/compass-connections/src/components/connecting/connecting-animation.tsx b/packages/compass-connections/src/components/connecting/connecting-animation.tsx deleted file mode 100644 index acecf9dae40..00000000000 --- a/packages/compass-connections/src/components/connecting/connecting-animation.tsx +++ /dev/null @@ -1,159 +0,0 @@ -import React, { useEffect, useRef } from 'react'; -import { spacing, css, palette, rgba } from '@mongodb-js/compass-components'; - -const animationContainerStyles = css({ - marginTop: spacing[3], - textAlign: 'center', -}); - -const animationSvgStyles = css({ - width: 70, - height: 'auto', -}); - -const shadowStyles = css({ - fill: palette.green.dark2, - opacity: 0.12, -}); - -const ringCircleStyles = css({ - stroke: palette.green.dark2, - strokeLinecap: 'round', - strokeLinejoin: 'round', - fill: 'none', -}); - -const ringShadowStyles = css(ringCircleStyles, { - opacity: 0.12, -}); - -const innerCircleStyles = css({ - fill: palette.yellow.light3, - opacity: 0.85, -}); - -const outerCircleStyles = css({ - fill: palette.red.light2, -}); - -const redArrowStyles = css({ - fill: palette.red.light1, -}); - -const arrowStyles = css({ - fill: rgba(palette.green.dark2, 0.3), -}); - -// This function returns the speed at which the needle shoots off in -// a direction. The farther from 0 the number, the farther/faster it goes. -const getNewRotationVelocity = () => { - return ( - (Math.PI / (170 + Math.random() * 100)) * (Math.random() > 0.5 ? 1 : -1) - ); -}; - -// How fast the needle returns to the center mark. -const rotationAcceleration = Math.PI / 90000; -// Closer to 0 the more friction/slowdown overtime there is (1 is no friction). -const friction = 0.974; - -/** - * Animated compass shown when attempting to connect. - */ -function ConnectingAnimation(): React.ReactElement { - const requestAnimationRef = - useRef>(); - const lastFrame = useRef(Date.now()); - const currentRotation = useRef(0); - const rotationVelocity = useRef(getNewRotationVelocity()); - - const connectingArrow1Ref = useRef(null); - const connectingArrow2Ref = useRef(null); - - useEffect(() => { - function updateAnimation() { - if (Date.now() - lastFrame.current > 20) { - // When the user returns from an unfocused view we disregard - // that last frame time for a frame. - lastFrame.current = Date.now(); - } - - const deltaTime = Date.now() - lastFrame.current; - - const arrow1 = connectingArrow1Ref.current; - const rotation = currentRotation.current * (180 / Math.PI); - arrow1?.setAttribute('transform', `rotate(${rotation}, 24.39, 39.2)`); - const arrow2 = connectingArrow2Ref.current; - arrow2?.setAttribute('transform', `rotate(${rotation}, 24.39, 39.2)`); - - currentRotation.current += rotationVelocity.current * deltaTime; - rotationVelocity.current += - rotationAcceleration * - (currentRotation.current > 0 ? -1 : 1) * - deltaTime; - rotationVelocity.current *= friction; - - if ( - Math.abs(rotationVelocity.current) < Math.PI / 1100 && - Math.abs(currentRotation.current) < Math.PI / 1100 - ) { - // When the Compass hands are settled we apply a force so - // it starts to rotate again. - rotationVelocity.current = getNewRotationVelocity(); - } - - lastFrame.current = Date.now(); - - requestAnimationRef.current = - window.requestAnimationFrame(updateAnimation); - } - - requestAnimationRef.current = window.requestAnimationFrame(updateAnimation); - return () => { - if (requestAnimationRef.current !== undefined) { - window.cancelAnimationFrame(requestAnimationRef.current); - } - }; - }, []); - - return ( -
- - - - - - - - - - - - -
- ); -} - -export default ConnectingAnimation; diff --git a/packages/compass-connections/src/components/connecting/connecting-background.tsx b/packages/compass-connections/src/components/connecting/connecting-background.tsx deleted file mode 100644 index 98209ec33b9..00000000000 --- a/packages/compass-connections/src/components/connecting/connecting-background.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import { css, keyframes } from '@mongodb-js/compass-components'; - -const connectingBackgroundSvgStyles = css({ - position: 'fixed', - zIndex: 500, - top: 0, - left: 0, - bottom: 0, - right: 0, -}); - -const opacityFadeInKeyframes = keyframes({ - '0%': { - opacity: 0, - }, - '100%': { - opacity: 1, - }, -}); - -const connectingBackgroundGradientStyles = css({ - opacity: 0.9, - animation: `${opacityFadeInKeyframes} 500ms ease-out`, -}); - -function ConnectingBackground(): React.ReactElement { - return ( - - - - - - - - - - - - - - - ); -} - -export default ConnectingBackground; diff --git a/packages/compass-connections/src/components/connecting/connecting-illustration.tsx b/packages/compass-connections/src/components/connecting/connecting-illustration.tsx deleted file mode 100644 index b06ba822a43..00000000000 --- a/packages/compass-connections/src/components/connecting/connecting-illustration.tsx +++ /dev/null @@ -1,613 +0,0 @@ -import React from 'react'; -import { css } from '@mongodb-js/compass-components'; - -const illustrationStyles = css({ - maxHeight: '40vh', -}); - -function ConnectingIllustration(): React.ReactElement { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -} - -export default ConnectingIllustration; diff --git a/packages/compass-connections/src/components/connecting/connecting.spec.tsx b/packages/compass-connections/src/components/connecting/connecting.spec.tsx deleted file mode 100644 index be9644c22a1..00000000000 --- a/packages/compass-connections/src/components/connecting/connecting.spec.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import React from 'react'; -import { - cleanup, - render, - screen, - fireEvent, - waitFor, -} from '@mongodb-js/testing-library-compass'; -import { expect } from 'chai'; -import sinon from 'sinon'; - -import Connecting from './connecting'; - -const oidcUrlText = 'Visit the following URL to complete authentication'; - -describe('Connecting Component', function () { - let onCancelConnectionClickedSpy; - - beforeEach(function () { - onCancelConnectionClickedSpy = sinon.spy(); - }); - - before(function () { - sinon.replace(window, 'requestAnimationFrame', () => 0); - sinon.replace(window, 'cancelAnimationFrame', () => 0); - }); - - afterEach(function () { - // Modals can have delays and transitions so it's best to cleanup. - cleanup(); - }); - - after(function () { - sinon.restore(); - }); - - describe('when there is a connection attempt in progress', function () { - beforeEach(function () { - render( - - ); - }); - - it('shows the connecting background overlay', function () { - expect(screen.queryByTestId('connecting-background-svg')).to.be.visible; - }); - - it('does not show the connecting modal yet', function () { - expect(screen.queryByText('Cancel')).to.not.exist; - }); - - describe('after a slight delay', function () { - beforeEach(async function () { - await waitFor(() => { - return screen.getByText('Cancel'); - }); - }); - - it('shows the connecting modal', function () { - expect(screen.getByText('Cancel')).to.be.visible; - expect( - screen.getByTestId('connecting-modal-content').textContent - ).to.not.include(oidcUrlText); - }); - - describe('when the cancel button is clicked', function () { - beforeEach(function () { - expect(onCancelConnectionClickedSpy.called).to.equal(false); - - const cancelButton = screen.getByText('Cancel'); - fireEvent.click(cancelButton); - }); - - it('calls onCancelConnectionClicked', function () { - expect(onCancelConnectionClickedSpy.called).to.equal(true); - }); - }); - }); - }); - - describe('when there is an oidc device auth code and url', function () { - beforeEach(async function () { - render( - - ); - await waitFor(() => { - return screen.getByText('Cancel'); - }); - }); - - it('shows the url', function () { - expect( - screen.getByTestId('connecting-modal-content').textContent - ).to.include(oidcUrlText); - expect( - screen.getByTestId('connecting-modal-content').textContent - ).to.include('https://localhost:27097'); - }); - }); -}); diff --git a/packages/compass-connections/src/components/connecting/connecting.tsx b/packages/compass-connections/src/components/connecting/connecting.tsx deleted file mode 100644 index dbd6ab93302..00000000000 --- a/packages/compass-connections/src/components/connecting/connecting.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { - Body, - Code, - H3, - Link, - Modal, - spacing, - css, -} from '@mongodb-js/compass-components'; - -import ConnectingAnimation from './connecting-animation'; -import ConnectingIllustration from './connecting-illustration'; -import ConnectingBackground from './connecting-background'; - -// We delay showing the modal for this amount of time to avoid flashing. -const showModalDelayMS = 250; - -const modalContentStyles = css({ - textAlign: 'center', - padding: spacing[3], - paddingBottom: spacing[5] + spacing[2], -}); - -const connectingStatusStyles = css({ - marginTop: spacing[3], - fontWeight: 'bold', - maxHeight: 100, - overflow: 'hidden', - textOverflow: 'ellipsis', -}); - -const cancelButtonStyles = css({ - border: 'none', - background: 'none', - padding: 0, - margin: 0, - marginTop: spacing[4], -}); - -const textContentStyles = css({ - marginTop: spacing[3], -}); - -const oidcContainerStyles = css({ - padding: `0px ${spacing[4]}px`, -}); - -/** - * Modal shown when attempting to connect. - */ -function Connecting({ - connectingStatusText, - onCancelConnectionClicked, - oidcDeviceAuthVerificationUrl, - oidcDeviceAuthUserCode, -}: { - connectingStatusText: string; - onCancelConnectionClicked: () => void; - oidcDeviceAuthVerificationUrl: string | null; - oidcDeviceAuthUserCode: string | null; -}): React.ReactElement { - const [showModal, setShowModal] = useState(false); - const showModalDebounceTimeout = useRef | null>( - null - ); - - useEffect(() => { - if (showModalDebounceTimeout.current === null && !showModal) { - showModalDebounceTimeout.current = setTimeout(() => { - setShowModal(true); - showModalDebounceTimeout.current = null; - }, showModalDelayMS); - } - - return () => { - if (showModalDebounceTimeout.current) { - clearTimeout(showModalDebounceTimeout.current); - showModalDebounceTimeout.current = null; - } - }; - }, [showModal]); - - return ( - - - onCancelConnectionClicked()}> -
- - {oidcDeviceAuthVerificationUrl && oidcDeviceAuthUserCode ? ( -
- - Visit the following URL to complete authentication: - - - {oidcDeviceAuthVerificationUrl} - - - Enter the following code on that page: - - - {oidcDeviceAuthUserCode} - -
- ) : ( - <> -

{connectingStatusText}

- - - )} - - Cancel - -
-
-
- ); -} - -export default Connecting; diff --git a/packages/compass-connections/src/components/connection-list/connection-icon.tsx b/packages/compass-connections/src/components/connection-list/connection-icon.tsx deleted file mode 100644 index 3b2246346fa..00000000000 --- a/packages/compass-connections/src/components/connection-list/connection-icon.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import { - Icon, - spacing, - MongoDBLogoMark, - css, - cx, -} from '@mongodb-js/compass-components'; -import { isLocalhost, isAtlas } from 'mongodb-build-info'; - -const connectionIconStyles = css({ - borderRadius: '50%', - width: spacing[3], - height: spacing[3], - flexShrink: 0, - marginTop: spacing[1] / 2, - marginRight: spacing[2], - gridArea: 'icon', -}); - -function ConnectionIcon({ - connectionString, - color, -}: { - connectionString: string; - color: string; -}): React.ReactElement { - const testId = 'connection-icon'; - - if (isAtlas(connectionString)) { - return ( - - ); - } - - const glyph = isLocalhost(connectionString) ? 'Laptop' : 'Cloud'; - return ( - - ); -} - -export default ConnectionIcon; diff --git a/packages/compass-connections/src/components/connection-list/connection-list.spec.tsx b/packages/compass-connections/src/components/connection-list/connection-list.spec.tsx deleted file mode 100644 index e2d01bac0b0..00000000000 --- a/packages/compass-connections/src/components/connection-list/connection-list.spec.tsx +++ /dev/null @@ -1,351 +0,0 @@ -import React from 'react'; -import { - render, - screen, - fireEvent, - waitFor, - cleanup, - userEvent, -} from '@mongodb-js/testing-library-compass'; -import { expect } from 'chai'; -import sinon from 'sinon'; -import type { ConnectionInfo } from '@mongodb-js/connection-storage/renderer'; - -import ConnectionList from './connection-list'; - -const mockRecents: ConnectionInfo[] = []; -for (let i = 0; i < 5; i++) { - mockRecents.push({ - id: `mock-connection-${i}`, - connectionOptions: { - connectionString: `mongodb://localhost:2${5000 + i}`, - }, - lastUsed: new Date(1647022100487 - i), - }); -} - -const mockFavorites: ConnectionInfo[] = [ - { - id: 'mock-connection-atlas', - connectionOptions: { - connectionString: - 'mongodb+srv://testUserForTesting:notMyRealPassword@test.mongodb.net/test?authSource=admin&replicaSet=art-dev-shard-0&readPreference=primary&ssl=true', - }, - favorite: { - name: 'Atlas test', - color: '#d4366e', - }, - savedConnectionType: 'favorite', - lastUsed: new Date(), - }, - { - id: 'mock-connection-empty-connection', - connectionOptions: { - connectionString: '', - }, - favorite: { - name: 'super long favorite name - super long favorite name - super long favorite name - super long favorite name', - color: '#5fc86e', - }, - savedConnectionType: 'favorite', - lastUsed: new Date(), - }, - { - id: 'mock-connection-local', - connectionOptions: { - connectionString: 'mongodb://localhost:27019', - }, - favorite: { - name: 'favorite', - color: '#5fc86e', - }, - savedConnectionType: 'favorite', - lastUsed: new Date(), - }, - { - id: 'mock-connection-invalid string', - connectionOptions: { - connectionString: 'invalid connection string', - }, - savedConnectionType: 'recent', - lastUsed: new Date(), - }, -]; - -describe('ConnectionList Component', function () { - let setActiveConnectionIdSpy; - let createNewConnectionSpy; - beforeEach(function () { - setActiveConnectionIdSpy = sinon.spy(); - createNewConnectionSpy = sinon.spy(); - }); - afterEach(cleanup); - describe('when rendered', function () { - beforeEach(function () { - render( - true} - onDoubleClick={() => true} - /> - ); - }); - - it('shows two lists', function () { - const listItems = screen.getAllByRole('list'); - expect(listItems.length).to.equal(2); - }); - - it('renders all of the connections in the lists', function () { - const listItems = screen.getAllByRole('listitem'); - expect(listItems.length).to.equal( - mockFavorites.length + mockRecents.length - ); - }); - - it('renders the favorite connections in a list', function () { - const listItems = screen.getAllByTestId('favorite-connection-title'); - expect(listItems[0].textContent).to.equal(mockFavorites[0].favorite.name); - expect(listItems[1].textContent).to.equal(mockFavorites[1].favorite.name); - expect(listItems[2].textContent).to.equal(mockFavorites[2].favorite.name); - }); - - it('renders the recent connections in a list', function () { - const listItems = screen.getAllByTestId('recent-connection'); - expect(listItems.length).to.equal(mockRecents.length); - }); - - it('does not show the saved connections filter input', function () { - const filter = screen.queryByTestId( - 'sidebar-filter-saved-connections-input' - ); - expect(filter).to.not.exist; - }); - - it('does not show connection import export option when there is no openConnectionImportExportModal prop', function () { - const connectionsHeader = screen.getByTestId( - 'favorite-connections-list-header' - ); - userEvent.hover(connectionsHeader); - expect(() => screen.getByTestId('favorites-menu-show-actions')).to.throw; - }); - - it('shows connection import export option when there is a openConnectionImportExportModal prop', function () { - cleanup(); - render( - true} - onDoubleClick={() => true} - openConnectionImportExportModal={() => {}} - /> - ); - const connectionsHeader = screen.getByTestId( - 'favorite-connections-list-header' - ); - userEvent.hover(connectionsHeader); - expect(screen.getByTestId('favorites-menu-show-actions')).to.be.visible; - }); - }); - - describe('with more than 10 favorite connections', function () { - beforeEach(function () { - const favorites = [ - ...mockFavorites, - ...mockFavorites.map((favorite) => ({ - ...favorite, - id: favorite.id + '__1', - })), - ...mockFavorites.map((favorite) => ({ - ...favorite, - id: favorite.id + '__2', - })), - ...mockFavorites.map((favorite) => ({ - ...favorite, - id: favorite.id + '__3', - })), - ]; - render( - true} - onDoubleClick={() => true} - /> - ); - }); - - it('shows the saved connections filter input', function () { - expect(screen.getByTestId('sidebar-filter-saved-connections-input')).to.be - .visible; - expect( - screen.getAllByTestId('favorite-connection-title').length - ).to.equal(12); - }); - - describe('when the saved connections filter input is updated', function () { - beforeEach(function () { - const textInput = screen.getByTestId( - 'sidebar-filter-saved-connections-input' - ); - - userEvent.type(textInput, 'super'); - }); - - it('only shows the partial favorite connections', function () { - expect( - screen.getAllByTestId('favorite-connection-title').length - ).to.equal(4); - }); - }); - }); - - describe('when new connection is clicked', function () { - beforeEach(function () { - render( - true} - onDoubleClick={() => true} - /> - ); - - expect(setActiveConnectionIdSpy.called).to.equal(false); - - const button = screen.getByText('New connection').closest('button'); - fireEvent( - button, - new MouseEvent('click', { - bubbles: true, - cancelable: true, - }) - ); - }); - - it('calls create new connection', function () { - expect(createNewConnectionSpy.called).to.equal(true); - expect(setActiveConnectionIdSpy.called).to.equal(false); - }); - }); - - describe('when a favorite connection is clicked', function () { - beforeEach(function () { - render( - true} - onDoubleClick={() => true} - /> - ); - - expect(setActiveConnectionIdSpy.called).to.equal(false); - - const button = screen - .getByText(mockFavorites[1].favorite.name) - .closest('button'); - fireEvent( - button, - new MouseEvent('click', { - bubbles: true, - cancelable: true, - }) - ); - }); - - it('calls changed active connection id to the clicked connection', function () { - expect(setActiveConnectionIdSpy.called).to.equal(true); - expect(setActiveConnectionIdSpy.firstCall.args[0]).to.equal( - 'mock-connection-empty-connection' - ); - }); - }); - - describe('when a recent connection is clicked', function () { - beforeEach(function () { - render( - true} - onDoubleClick={() => true} - /> - ); - - expect(setActiveConnectionIdSpy.called).to.equal(false); - - const button = screen - .getByText(mockRecents[3].connectionOptions.connectionString.substr(10)) - .closest('button'); - fireEvent( - button, - new MouseEvent('click', { - bubbles: true, - cancelable: true, - }) - ); - }); - - it('calls changed active connection id to the clicked connection', function () { - expect(setActiveConnectionIdSpy.called).to.equal(true); - expect(setActiveConnectionIdSpy.firstCall.args[0]).to.equal( - 'mock-connection-3' - ); - }); - }); - describe('when "clear all" button is clicked', function () { - let removeAllRecentsConnectionsSpy; - beforeEach(async function () { - removeAllRecentsConnectionsSpy = sinon.spy(); - render( - true} - removeAllRecentsConnections={removeAllRecentsConnectionsSpy} - onDoubleClick={() => true} - /> - ); - - expect(removeAllRecentsConnectionsSpy.called).to.equal(false); - - fireEvent.mouseOver(screen.getByText('Recents')); - await waitFor(() => screen.getByText('Clear All')); - const button = screen.getByText('Clear All'); - fireEvent( - button, - new MouseEvent('click', { - bubbles: true, - cancelable: true, - }) - ); - }); - - it('calls function to remove all recents connections', function () { - expect(removeAllRecentsConnectionsSpy.called).to.equal(true); - }); - }); -}); diff --git a/packages/compass-connections/src/components/connection-list/connection-list.tsx b/packages/compass-connections/src/components/connection-list/connection-list.tsx deleted file mode 100644 index 1d4712e82b1..00000000000 --- a/packages/compass-connections/src/components/connection-list/connection-list.tsx +++ /dev/null @@ -1,336 +0,0 @@ -import React, { Fragment, useMemo, useState } from 'react'; -import { - Button, - FavoriteIcon, - H3, - Icon, - palette, - spacing, - css, - cx, - useDarkMode, - useHoverState, - ItemActionControls, -} from '@mongodb-js/compass-components'; -import type { ItemAction } from '@mongodb-js/compass-components'; -import type { ConnectionInfo } from '@mongodb-js/connection-storage/provider'; -import type AppRegistry from 'hadron-app-registry'; - -import Connection from './connection'; -import ConnectionsTitle from './connections-title'; -import { TextInput } from '@mongodb-js/compass-components'; - -const newConnectionButtonContainerStyles = css({ - padding: spacing[3], -}); - -const savedConnectionsFilter = css({ - margin: `${spacing[2]}px ${spacing[3]}px`, -}); - -const newConnectionButtonStyles = css({ - width: '100%', - justifyContent: 'center', - fontWeight: 'bold', - '> div': { - width: 'auto', - }, -}); - -const newConnectionButtonStylesLight = css({ - backgroundColor: palette.white, -}); -const newConnectionButtonStylesDark = css({ - backgroundColor: palette.gray.dark2, -}); - -const sectionHeaderStyles = css({ - marginTop: spacing[4], - marginBottom: spacing[3], - paddingLeft: spacing[3], - paddingRight: spacing[2], - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - ':hover': {}, -}); - -const recentHeaderStyles = css({ - marginTop: spacing[4], -}); - -const sectionHeaderTitleStyles = css({ - flexGrow: 1, - fontSize: '16px', - lineHeight: '24px', - fontWeight: 700, -}); - -const sectionHeaderTitleStylesLight = css({ - color: palette.gray.dark3, -}); - -const sectionHeaderTitleStylesDark = css({ - color: 'white', -}); - -const sectionHeaderIconStyles = css({ - fontSize: spacing[3], - margin: 0, - marginRight: spacing[2], - padding: 0, - display: 'flex', -}); - -const connectionListSectionStyles = css({ - overflowY: 'auto', - padding: 0, - paddingBottom: spacing[3], -}); - -const connectionListStyles = css({ - listStyleType: 'none', - margin: 0, - padding: 0, -}); - -const MIN_FAV_CONNECTIONS_TO_SHOW_FILTER = 10; - -function RecentIcon() { - const darkMode = useDarkMode(); - - const color = darkMode ? 'white' : palette.gray.dark3; - - return ( - - - - - - ); -} - -export type ConnectionInfoFavorite = ConnectionInfo & - Required>; - -type FavoriteAction = 'import-saved-connections' | 'export-saved-connections'; - -const favoriteActions: ItemAction[] = [ - { - action: 'import-saved-connections', - label: 'Import saved connections', - icon: 'Download', - }, - { - action: 'export-saved-connections', - label: 'Export saved connections', - icon: 'Export', - }, -]; - -type ConnectionListProps = { - activeConnectionId?: string; - appRegistry: AppRegistry; - recentConnections: ConnectionInfo[]; - favoriteConnections: ConnectionInfo[]; - createNewConnection: () => void; - setActiveConnectionId: (connectionId: string) => void; - onDoubleClick: (connectionInfo: ConnectionInfo) => void; - removeAllRecentsConnections: () => void; - duplicateConnection: (connectionInfo: ConnectionInfo) => void; - removeConnection: (connectionInfo: ConnectionInfo) => void; - openConnectionImportExportModal?: (action: FavoriteAction) => void; -}; - -function ConnectionList({ - activeConnectionId, - appRegistry, - recentConnections, - favoriteConnections, - createNewConnection, - setActiveConnectionId, - onDoubleClick, - removeAllRecentsConnections, - duplicateConnection, - removeConnection, - openConnectionImportExportModal, -}: ConnectionListProps): React.ReactElement { - const darkMode = useDarkMode(); - const [recentHoverProps, recentHeaderHover] = useHoverState(); - const [favoriteHoverProps, favoriteHeaderHover] = useHoverState(); - - const [favoriteConnectionsFilter, setSavedConnectionsFilter] = useState(''); - - const filteredSavedConnections = useMemo(() => { - if (!favoriteConnectionsFilter) { - return favoriteConnections; - } - - return favoriteConnections.filter((connection) => - connection.favorite?.name - ?.toLowerCase() - .includes(favoriteConnectionsFilter.toLowerCase()) - ); - }, [favoriteConnections, favoriteConnectionsFilter]); - const showFilteredSavedConnections = - favoriteConnections.length > MIN_FAV_CONNECTIONS_TO_SHOW_FILTER; - - return ( - - appRegistry.emit(actionName)} - /> -
- -
-
-
-
- -
-

- Saved connections -

- {openConnectionImportExportModal && ( - - data-testid="favorites-menu" - onAction={openConnectionImportExportModal} - iconSize="small" - actions={favoriteActions} - isVisible={favoriteHeaderHover} - > - )} -
- {showFilteredSavedConnections && ( - ) => - setSavedConnectionsFilter(e.target.value) - } - className={savedConnectionsFilter} - /> - )} -
    - {(showFilteredSavedConnections - ? filteredSavedConnections - : favoriteConnections - ).map((connectionInfo, index) => ( -
  • - setActiveConnectionId(connectionInfo.id)} - onDoubleClick={onDoubleClick} - removeConnection={removeConnection} - duplicateConnection={duplicateConnection} - /> -
  • - ))} -
-
-
- -
-

- Recents -

- {recentHeaderHover && ( - - )} -
-
    - {recentConnections.map((connectionInfo, index) => ( -
  • - setActiveConnectionId(connectionInfo.id)} - onDoubleClick={onDoubleClick} - removeConnection={removeConnection} - duplicateConnection={duplicateConnection} - /> -
  • - ))} -
-
-
- ); -} - -export default ConnectionList; diff --git a/packages/compass-connections/src/components/connection-list/connection.spec.tsx b/packages/compass-connections/src/components/connection-list/connection.spec.tsx deleted file mode 100644 index f9da0135919..00000000000 --- a/packages/compass-connections/src/components/connection-list/connection.spec.tsx +++ /dev/null @@ -1,289 +0,0 @@ -import React from 'react'; -import { render, screen, fireEvent } from '@mongodb-js/testing-library-compass'; -import { expect } from 'chai'; -import sinon from 'sinon'; - -import Connection from './connection'; - -describe('Connection Component', function () { - let onClickSpy: sinon.SinonSpy; - let onDoubleClickSpy: sinon.SinonSpy; - let duplicateConnectionSpy: sinon.SinonSpy; - let removeConnectionSpy: sinon.SinonSpy; - - beforeEach(function () { - onClickSpy = sinon.spy(); - onDoubleClickSpy = sinon.spy(); - duplicateConnectionSpy = sinon.spy(); - removeConnectionSpy = sinon.spy(); - }); - - describe('when it has a lastUsed date', function () { - it('shows the date as a string', function () { - const lastUsed = new Date('Dec 17, 1995, 12:00 AM'); - const stub = sinon.stub(lastUsed, 'toLocaleString').returns('Dec, 17'); - - render( - - ); - - const dateStringElement = screen.getByText('Dec, 17'); - expect(dateStringElement).to.not.equal(null); - expect(stub.getCall(0).args).to.deep.equal([ - 'default', - { - day: 'numeric', - hour: 'numeric', - minute: 'numeric', - month: 'short', - year: 'numeric', - }, - ]); - }); - }); - - describe('when it is a favorite', function () { - describe('when it has a name', function () { - beforeEach(function () { - render( - - ); - }); - - it('shows the name', function () { - const connectionTitle = screen.getByRole('heading'); - expect(connectionTitle.textContent).to.equal('aaa'); - }); - }); - - describe('when there is not a favorite color', function () { - beforeEach(function () { - render( - - ); - }); - - it('it uses 33, 49, 60 by default', function () { - const favoriteIndicator = screen.getByTestId('connection-icon'); - expect( - getComputedStyle(favoriteIndicator).getPropertyValue('color') - ).to.equal('rgb(28, 45, 56)'); - }); - }); - }); - - describe('when it is not a favorite', function () { - beforeEach(function () { - render( - - ); - }); - - it('there is an icon and its 33, 49, 60', function () { - const favoriteIndicator = screen.queryByTestId('connection-icon'); - expect(favoriteIndicator).to.not.equal(null); - expect( - getComputedStyle(favoriteIndicator).getPropertyValue('color') - ).to.equal('rgb(28, 45, 56)'); - }); - - it('it shows the connection title as the name', function () { - const connectionTitle = screen.getByRole('heading'); - expect(connectionTitle.textContent).to.equal('outerspace:27019'); - }); - - it('the connection title has a title', function () { - const connectionTitle = screen.getByRole('heading'); - expect(connectionTitle.title).to.equal('outerspace:27019'); - }); - }); - - describe('when it is not a favorite and has an invalid connection string to get a title from', function () { - beforeEach(function () { - render( - - ); - }); - - it('it shows a connection string title', function () { - const connectionTitle = screen.getByRole('heading'); - expect(connectionTitle.textContent).to.equal('invalid connection string'); - }); - }); - - describe('when it has no connection string or favorite name', function () { - beforeEach(function () { - render( - - ); - }); - - it('it shows a default connection title', function () { - const connectionTitle = screen.getByRole('heading'); - expect(connectionTitle.textContent).to.equal('Connection'); - }); - }); - - describe('when clicked', function () { - beforeEach(function () { - render( - - ); - const button = screen.getByText('123').closest('button'); - - expect(onClickSpy.called).to.equal(false); - - fireEvent( - button, - new MouseEvent('click', { - bubbles: true, - cancelable: true, - }) - ); - }); - - it('calls the onclick handler', function () { - expect(onClickSpy.called).to.equal(true); - }); - }); - - describe('when double clicked', function () { - beforeEach(function () { - render( - - ); - const button = screen.getByText('double-click').closest('button'); - expect(onDoubleClickSpy.called).to.equal(false); - fireEvent( - button, - new MouseEvent('dblclick', { - bubbles: true, - cancelable: true, - }) - ); - }); - - it('calls the ondoubleclick handler', function () { - expect(onDoubleClickSpy.called).to.equal(true); - const [connectionInfo] = onDoubleClickSpy.getCall(0).args; - expect(connectionInfo).to.deep.equal({ - id: '0000-0000-0000-0000', - connectionOptions: { - connectionString: 'double-click.com', - }, - favorite: { - name: 'double-click', - }, - }); - }); - }); -}); diff --git a/packages/compass-connections/src/components/connection-list/connection.tsx b/packages/compass-connections/src/components/connection-list/connection.tsx deleted file mode 100644 index 973a8bcc086..00000000000 --- a/packages/compass-connections/src/components/connection-list/connection.tsx +++ /dev/null @@ -1,332 +0,0 @@ -import React, { useCallback } from 'react'; -import { - H3, - Description, - spacing, - palette, - css, - cx, - useDarkMode, - ItemActionControls, - useHoverState, - useToast, -} from '@mongodb-js/compass-components'; - -import type { ItemAction } from '@mongodb-js/compass-components'; -import type { ConnectionInfo } from '@mongodb-js/connection-storage/provider'; -import { getConnectionTitle } from '@mongodb-js/connection-info'; - -import ConnectionIcon from './connection-icon'; -import { useConnectionColor } from '@mongodb-js/connection-form'; -import { useMaybeProtectConnectionString } from '@mongodb-js/compass-maybe-protect-connection-string'; - -const TOAST_TIMEOUT_MS = 5000; // 5 seconds. - -type Action = - | 'copy-connection-string' - | 'duplicate-connection' - | 'remove-connection'; - -const itemActionControls = css({ - position: 'absolute', - right: spacing[1], - top: spacing[2] + spacing[1], - margin: 'auto 0', - bottom: 0, -}); - -const connectionButtonContainerStyles = css({ - position: 'relative', - width: '100%', - '&:hover': { - '&::after': { - opacity: 1, - width: spacing[1], - }, - }, - '&:focus': { - '&::after': { - opacity: 1, - width: spacing[1], - }, - }, - '&:focus-within': { - '&::after': { - opacity: 1, - width: spacing[1], - }, - }, -}); - -const connectionButtonStyles = css({ - margin: 0, - paddingTop: spacing[1], - paddingRight: spacing[3], - paddingBottom: spacing[1], - paddingLeft: spacing[2], - width: '100%', - display: 'grid', - gridTemplateAreas: `'color icon title' 'color . description'`, - gridTemplateColumns: 'auto auto 1fr', - gridTemplateRows: '1fr 1fr', - alignItems: 'center', - justifyItems: 'start', - border: 'none', - borderRadius: 0, - background: 'none', - '&:hover': { - cursor: 'pointer', - border: 'none', - }, - '&:focus': { - border: 'none', - }, -}); - -const connectionButtonStylesLight = css({ - '&:hover': { - background: palette.gray.light2, - }, -}); - -const connectionButtonStylesDark = css({ - '&:hover': { - background: palette.gray.dark2, - }, -}); - -const connectionTitleStyles = css({ - fontSize: '14px', - fontWeight: 'normal', - lineHeight: '20px', - margin: 0, - flexGrow: 1, - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - gridArea: 'title', - width: 'calc(100% - 20px)', - textAlign: 'left', -}); - -const connectionDescriptionStyles = css({ - fontSize: '12px', - lineHeight: '20px', - margin: 0, - padding: 0, - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - gridArea: 'description', -}); - -// Creates a date string formatted as `Oct 27, 3000, 2:06 PM`. -const dateConfig: Intl.DateTimeFormatOptions = { - month: 'short', - day: 'numeric', - year: 'numeric', - hour: 'numeric', - minute: 'numeric', -}; - -const colorIndicatorStyles = css({ - height: 'calc(100% - 4px)', - width: spacing[2], - borderRadius: spacing[2], - margin: '2px 0', - marginRight: spacing[2], - gridArea: 'color', -}); - -function FavoriteColorIndicator({ - color, - className, -}: { - color?: string; - className?: string; -}): React.ReactElement { - const { connectionColorToHex } = useConnectionColor(); - const favoriteColorHex = connectionColorToHex(color); - - return ( -
- ); -} - -const actions: ItemAction[] = [ - { - action: 'copy-connection-string', - label: 'Copy connection string', - icon: 'Copy', - }, - - { - action: 'duplicate-connection', - label: 'Duplicate', - icon: 'Clone', - }, - - { - action: 'remove-connection', - label: 'Remove', - icon: 'Trash', - }, -]; - -function Connection({ - isActive, - connectionInfo, - onClick, - onDoubleClick, - duplicateConnection, - removeConnection, -}: { - isActive: boolean; - connectionInfo: ConnectionInfo; - onClick: () => void; - onDoubleClick: (connectionInfo: ConnectionInfo) => void; - duplicateConnection: (connectionInfo: ConnectionInfo) => void; - removeConnection: (connectionInfo: ConnectionInfo) => void; -}): React.ReactElement { - const connectionTitle = getConnectionTitle(connectionInfo); - const { - connectionOptions: { connectionString }, - savedConnectionType, - favorite, - lastUsed, - } = connectionInfo; - - const darkMode = useDarkMode(); - - const { connectionColorToHex } = useConnectionColor(); - const favoriteColorHex = connectionColorToHex(favorite?.color) ?? ''; - - const hasColoredBackground = isActive && favoriteColorHex; - const normalTitleColor = darkMode ? palette.white : palette.gray.dark3; - const titleColor = hasColoredBackground ? palette.black : normalTitleColor; - const backgroundColor = hasColoredBackground ? favoriteColorHex : 'none'; - - const normalDescriptionColor = darkMode - ? palette.gray.light1 - : palette.gray.base; - const descriptionColor = hasColoredBackground - ? palette.gray.dark3 - : normalDescriptionColor; - - const normalConnectionMenuColor = darkMode - ? palette.white - : palette.gray.base; - const connectionMenuColor = hasColoredBackground - ? palette.gray.dark3 - : normalConnectionMenuColor; - - const { openToast } = useToast('compass-connections'); - const maybeProtectConnectionString = useMaybeProtectConnectionString(); - - const onAction = useCallback( - (action: Action) => { - async function copyConnectionString(connectionString: string) { - try { - await navigator.clipboard.writeText(connectionString); - openToast('copy-to-clipboard', { - title: 'Success', - description: 'Copied to clipboard.', - variant: 'success', - timeout: TOAST_TIMEOUT_MS, - }); - } catch (err) { - openToast('copy-to-clipboard', { - title: 'Error', - description: - 'An error occurred when copying to clipboard. Please try again.', - variant: 'warning', - timeout: TOAST_TIMEOUT_MS, - }); - } - } - - if (action === 'copy-connection-string') { - void copyConnectionString( - maybeProtectConnectionString( - connectionInfo.connectionOptions.connectionString - ) - ); - return; - } - - if (action === 'duplicate-connection') { - duplicateConnection(connectionInfo); - return; - } - - if (action === 'remove-connection') { - removeConnection(connectionInfo); - return; - } - }, - [ - connectionInfo, - duplicateConnection, - openToast, - removeConnection, - maybeProtectConnectionString, - ] - ); - - const [hoverProps, isHovered] = useHoverState(); - - return ( -
- -
- - data-testid="connection-menu" - onAction={onAction} - iconSize="small" - actions={actions} - isVisible={isHovered} - iconStyle={{ color: connectionMenuColor }} - > -
-
- ); -} - -export default Connection; diff --git a/packages/compass-connections/src/components/connection-list/connections-title.tsx b/packages/compass-connections/src/components/connection-list/connections-title.tsx deleted file mode 100644 index c58f1998064..00000000000 --- a/packages/compass-connections/src/components/connection-list/connections-title.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; - -import { - ItemActionControls, - Subtitle, - css, - spacing, -} from '@mongodb-js/compass-components'; -import type { ItemAction } from '@mongodb-js/compass-components'; - -const containerStyles = css({ - display: 'flex', - alignItems: 'center', - backgroundColor: 'var(--title-bg-color)', - - height: spacing[6], - padding: spacing[3] + spacing[1], -}); - -const connectionsTitleStyles = css({ - color: 'var(--title-color)', -}); - -const iconStyles = css({ - color: 'var(--title-color)', - '&:hover': { - color: 'var(--title-color-hover)', - }, -}); - -type Action = 'open-compass-settings'; - -const actions: ItemAction[] = [ - { - action: 'open-compass-settings', - label: 'Compass Settings', - icon: 'Settings', - }, -]; - -export default function ConnectionsTitle({ - onAction, -}: { - onAction(actionName: Action, ...rest: any[]): void; -}) { - return ( -
- Compass - - onAction={onAction} - iconSize="small" - actions={actions} - data-testid="connections-sidebar-title-actions" - iconClassName={iconStyles} - > -
- ); -} diff --git a/packages/compass-connections/src/components/form-help/form-help.tsx b/packages/compass-connections/src/components/form-help/form-help.tsx deleted file mode 100644 index 41ac1646722..00000000000 --- a/packages/compass-connections/src/components/form-help/form-help.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import React from 'react'; -import { - Subtitle, - Body, - Link, - spacing, - css, -} from '@mongodb-js/compass-components'; -import { AtlasHelpSection } from '../atlas-help/atlas-help'; - -const formHelpContainerStyles = css({ - position: 'relative', - margin: 0, - width: spacing[5] * 10, - display: 'inline-block', -}); - -const sectionContainerStyles = css({ - margin: 0, - padding: spacing[4], - paddingBottom: 0, -}); - -const titleStyles = css({ - fontSize: '14px', -}); - -const descriptionStyles = css({ - marginTop: spacing[2], -}); - -function FormHelp({ - isMultiConnectionEnabled, -}: { - isMultiConnectionEnabled: boolean; -}): React.ReactElement { - return ( -
- - {!isMultiConnectionEnabled && ( - <> -
- - How do I find my connection string in Atlas? - - - If you have an Atlas cluster, go to the Cluster view. Click the - 'Connect' button for the cluster to which you wish to - connect. - - - See example - -
-
- - How do I format my connection string? - - - See example - -
- - )} -
- ); -} - -export default FormHelp; diff --git a/packages/compass-connections/src/components/legacy-connections.spec.tsx b/packages/compass-connections/src/components/legacy-connections.spec.tsx deleted file mode 100644 index 6a09b91114c..00000000000 --- a/packages/compass-connections/src/components/legacy-connections.spec.tsx +++ /dev/null @@ -1,341 +0,0 @@ -import React from 'react'; -import { expect } from 'chai'; -import { UUID } from 'bson'; -import sinon from 'sinon'; -import Connections from './legacy-connections'; -import type { ConnectionInfo } from '../connection-info-provider'; -import { - renderWithConnections, - screen, - userEvent, - waitFor, - cleanup, -} from '@mongodb-js/testing-library-compass'; - -async function loadSavedConnectionAndConnect(connectionInfo: ConnectionInfo) { - const savedConnectionButton = screen.getByTestId( - `saved-connection-button-${connectionInfo.id}` - ); - userEvent.click(savedConnectionButton); - - // Wait for the connection to load in the form. - await waitFor(() => - expect(screen.queryByTestId('connectionString')?.textContent).to.equal( - connectionInfo.connectionOptions.connectionString - ) - ); - - const connectButton = screen.getByRole('button', { name: 'Save & Connect' }); - userEvent.click(connectButton); - - // Wait for the connecting... modal to hide. - await waitFor(() => expect(screen.queryByText('Cancel')).to.not.exist); -} - -// TODO(COMPASS-7906): remove -describe.skip('Connections Component', function () { - afterEach(function () { - sinon.restore(); - cleanup(); - }); - - context('when rendered', function () { - beforeEach(function () { - renderWithConnections(); - }); - - it('renders the connect button from the connect-form', function () { - const button = screen.queryByText('Connect')?.closest('button'); - expect(button).to.not.equal(null); - }); - - it('renders atlas cta button', function () { - const button = screen.getByTestId('atlas-cta-link'); - expect(button.getAttribute('href')).to.equal( - 'https://www.mongodb.com/cloud/atlas/lp/try4?utm_source=compass&utm_medium=product&utm_content=v1' - ); - }); - - it('shows two connections lists', function () { - const listItems = screen.getAllByRole('list'); - expect(listItems.length).to.equal(2); - }); - - it('should not show any banners', function () { - expect(screen.queryByRole('alert')).to.not.exist; - }); - - it('should load an empty connections list with no connections', function () { - const listItems = screen.queryAllByRole('listitem'); - expect(listItems.length).to.equal(0); - - const favorites = screen.queryAllByTestId('favorite-connection'); - expect(favorites.length).to.equal(0); - - const recents = screen.queryAllByTestId('recent-connection'); - expect(recents.length).to.equal(0); - }); - - it('should include the help panels', function () { - expect(screen.queryByText(/How do I find my/)).to.be.visible; - expect(screen.queryByText(/How do I format my/)).to.be.visible; - }); - }); - - context('when rendered with saved connections in storage', function () { - let savedConnectionId: string; - let savedConnectionWithAppNameId: string; - let connections: ConnectionInfo[]; - let connectSpyFn: sinon.SinonSpy; - let saveConnectionSpy: sinon.SinonSpy; - let getState; - - beforeEach(async function () { - savedConnectionId = new UUID().toString(); - savedConnectionWithAppNameId = new UUID().toString(); - - connections = [ - { - id: savedConnectionId, - connectionOptions: { - connectionString: - 'mongodb://localhost:27018/?readPreference=primary&ssl=false', - }, - }, - { - id: savedConnectionWithAppNameId, - connectionOptions: { - connectionString: - 'mongodb://localhost:27019/?appName=Some+App+Name', - }, - }, - ]; - - connectSpyFn = sinon.stub().returns({}); - - const { connectionsStore, connectionStorage } = renderWithConnections( - , - { - connections, - connectFn: connectSpyFn, - } - ); - - saveConnectionSpy = sinon.spy(connectionStorage, 'save'); - getState = connectionsStore.getState; - - await waitFor(() => { - expect(screen.queryAllByRole('listitem')).to.exist; - }); - }); - - it('should render the saved connections', function () { - const listItems = screen.getAllByRole('listitem'); - expect(listItems.length).to.equal(2); - - const favorites = screen.queryAllByTestId('favorite-connection'); - expect(favorites.length).to.equal(0); - - const recents = screen.getAllByTestId('recent-connection'); - expect(recents.length).to.equal(2); - }); - - it('renders the title of the saved connection', function () { - expect(screen.getByText('localhost:27018')).to.be.visible; - }); - - context( - 'when a saved connection is clicked on and connected to', - function () { - beforeEach(async function () { - await loadSavedConnectionAndConnect( - connections.find(({ id }) => id === savedConnectionId)! - ); - }); - - it('should call the connect function with the connection options to connect', function () { - expect(connectSpyFn.callCount).to.equal(1); - expect(connectSpyFn.firstCall.args[0]).to.have.property( - 'connectionString', - 'mongodb://localhost:27018/?readPreference=primary&ssl=false&appName=TEST' - ); - }); - - it('should call to save the connection', function () { - expect(saveConnectionSpy.callCount).to.equal(1); - }); - - it('should update the connection with a new lastUsed time', function () { - expect( - getState().connections.byId[savedConnectionId].info - ).to.have.property('lastUsed'); - }); - } - ); - - context( - 'when a saved connection with appName is clicked on and connected to', - function () { - beforeEach(async function () { - await loadSavedConnectionAndConnect( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - connections.find(({ id }) => id === savedConnectionWithAppNameId)! - ); - }); - - it('should call the connect function without replacing appName', function () { - expect(connectSpyFn.callCount).to.equal(1); - expect(connectSpyFn.firstCall.args[0]).to.have.property( - 'connectionString', - 'mongodb://localhost:27019/?appName=Some+App+Name' - ); - }); - } - ); - }); - - context( - 'when connecting to a connection that is not succeeding', - function () { - let savedConnectableId: string; - let savedUnconnectableId: string; - let connections: ConnectionInfo[]; - let connectSpyFn: sinon.SinonSpy; - let saveConnectionSpy: sinon.SinonSpy; - - beforeEach(async function () { - savedConnectableId = new UUID().toString(); - savedUnconnectableId = new UUID().toString(); - connections = [ - { - id: savedConnectableId, - connectionOptions: { - connectionString: - 'mongodb://localhost:27018/?readPreference=primary&ssl=false', - }, - }, - { - id: savedUnconnectableId, - connectionOptions: { - connectionString: - 'mongodb://localhost:27099/?connectTimeoutMS=5000&serverSelectionTimeoutMS=5000', - }, - }, - ]; - - connectSpyFn = sinon - .stub() - // On first call we cancel it, so just never resolve to give UI time - // to render the connecting... state - .onFirstCall() - .callsFake(() => { - return new Promise(() => {}); - }) - // On second call connect successfully without blocking - .onSecondCall() - .callsFake(() => { - return {}; - }); - - const { connectionStorage } = renderWithConnections( - , - { connections, connectFn: connectSpyFn } - ); - - saveConnectionSpy = sinon.spy(connectionStorage, 'save'); - - await waitFor( - () => - expect( - screen.queryByTestId( - `saved-connection-button-${savedUnconnectableId}` - ) - ).to.exist - ); - - const savedConnectionButton = screen.getByTestId( - `saved-connection-button-${savedUnconnectableId}` - ); - userEvent.click(savedConnectionButton); - - // Wait for the connection to load in the form. - await waitFor(() => - expect( - screen.queryByTestId('connectionString')?.textContent - ).to.equal( - 'mongodb://localhost:27099/?connectTimeoutMS=5000&serverSelectionTimeoutMS=5000' - ) - ); - - const connectButton = screen.getByRole('button', { - name: 'Save & Connect', - }); - userEvent.click(connectButton); - - // Wait for the connecting... modal to be shown. - await waitFor(() => { - expect(screen.queryByText('Cancel')).to.be.visible; - }); - }); - - context('when the connection attempt is cancelled', function () { - beforeEach(async function () { - const cancelButton = screen.getByRole('button', { name: 'Cancel' }); - userEvent.click(cancelButton); - - // Wait for the connecting... modal to hide. - await waitFor(() => { - expect(screen.queryByText('Cancel')).to.not.exist; - }); - }); - - it('should enable the connect button', function () { - const connectButton = screen.getByRole('button', { - name: 'Save & Connect', - }); - expect(connectButton).to.not.match('disabled'); - }); - - it('should not call to save the connection', function () { - expect(saveConnectionSpy.callCount).to.equal(0); - }); - - it('should have the connections-wrapper test id', function () { - expect(screen.getByTestId('connections-wrapper')).to.be.visible; - }); - - it('should call the connect function with the connection options to connect', function () { - expect(connectSpyFn.callCount).to.equal(1); - expect(connectSpyFn.firstCall.args[0]).to.have.property( - 'connectionString', - 'mongodb://localhost:27099/?connectTimeoutMS=5000&serverSelectionTimeoutMS=5000&appName=TEST' - ); - }); - - context( - 'connecting to a successful connection after cancelling a connect', - function () { - beforeEach(async function () { - await loadSavedConnectionAndConnect( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - connections.find(({ id }) => id === savedConnectableId)! - ); - }); - - it('should call to save the connection once', function () { - expect(saveConnectionSpy.callCount).to.equal(1); - }); - - it('should call the connect function with the connection options to connect', function () { - expect(connectSpyFn.callCount).to.equal(2); - expect(connectSpyFn.secondCall.args[0]).to.have.property( - 'connectionString', - 'mongodb://localhost:27018/?readPreference=primary&ssl=false&appName=TEST' - ); - }); - } - ); - }); - } - ); -}); diff --git a/packages/compass-connections/src/components/legacy-connections.tsx b/packages/compass-connections/src/components/legacy-connections.tsx deleted file mode 100644 index 5da0cd144c2..00000000000 --- a/packages/compass-connections/src/components/legacy-connections.tsx +++ /dev/null @@ -1,217 +0,0 @@ -import { - Card, - ErrorBoundary, - ResizableSidebar, - css, - cx, - palette, - spacing, - useDarkMode, -} from '@mongodb-js/compass-components'; -import { useLogger } from '@mongodb-js/compass-logging/provider'; -import ConnectionForm from '@mongodb-js/connection-form'; -import type AppRegistry from 'hadron-app-registry'; -import React, { useCallback } from 'react'; -import { usePreference } from 'compass-preferences-model/provider'; -import type { ConnectionInfo } from '../provider'; -import { useConnectionRepository } from '../hooks/use-connection-repository'; -import Connecting from './connecting/connecting'; -import ConnectionList from './connection-list/connection-list'; -import FormHelp from './form-help/form-help'; -import { useConnectionInfoStatus } from '../hooks/use-connections-with-status'; -import { useConnections } from '../stores/connections-store'; -import { - getConnectingStatusText, - getConnectionErrorMessage, -} from './connection-status-notifications'; -import { useConnectionFormPreferences } from '../hooks/use-connection-form-preferences'; - -const connectStyles = css({ - position: 'absolute', - left: 0, - right: 0, - bottom: 0, - top: 0, - display: 'flex', - flexDirection: 'row', -}); - -const formContainerStyles = css({ - position: 'relative', - flexGrow: 1, - display: 'flex', - padding: spacing[4], - margin: 0, - paddingBottom: spacing[3], - flexDirection: 'row', - flexWrap: 'wrap', - gap: spacing[4], - overflow: 'auto', - height: '100%', -}); - -const connectFormContainerStyles = css({ - margin: 0, - padding: 0, - height: 'fit-content', - width: spacing[6] * 12, - position: 'relative', - display: 'inline-block', -}); - -const connectFormCardStyles = css({ - margin: 0, - height: 'fit-content', - width: '100%', - position: 'relative', - display: 'flex', - flexFlow: 'column nowrap', - maxHeight: '95vh', -}); - -const formCardDarkThemeStyles = css({ - background: palette.black, -}); - -const formCardLightThemeStyles = css({ - background: palette.white, -}); - -function Connections({ - appRegistry, - openConnectionImportExportModal, -}: { - appRegistry: AppRegistry; - openConnectionImportExportModal?: ( - action: 'import-saved-connections' | 'export-saved-connections' - ) => void; -}): React.ReactElement { - const { log, mongoLogId } = useLogger('COMPASS-CONNECTIONS'); - - const { - state: { - editingConnectionInfo: activeConnectionInfo, - connectionErrors, - oidcDeviceAuthState, - }, - connect, - disconnect, - createNewConnection, - editConnection, - duplicateConnection, - removeAllRecentConnections, - removeConnection, - saveEditedConnection, - } = useConnections(); - - const activeConnectionStatus = useConnectionInfoStatus( - activeConnectionInfo.id - ); - - const { favoriteConnections, nonFavoriteConnections: recentConnections } = - useConnectionRepository(); - - const darkMode = useDarkMode(); - const connectionFormPreferences = useConnectionFormPreferences(); - const isMultiConnectionEnabled = usePreference( - 'enableMultipleConnectionSystem' - ); - - const onConnectClick = (connectionInfo: ConnectionInfo) => { - void connect(connectionInfo); - }; - - const connectionErrorMessage = getConnectionErrorMessage( - connectionErrors[activeConnectionInfo.id] - ); - - const { title, description } = getConnectingStatusText(activeConnectionInfo); - const connectingStatusText = `${title}${ - description ? `. ${description}` : '' - }`; - - const activeConnectionOidcAuthState = - oidcDeviceAuthState[activeConnectionInfo.id]; - - const openSettingsModal = useCallback( - (tab?: string) => appRegistry.emit('open-compass-settings', tab), - [appRegistry] - ); - - return ( -
- - { - editConnection(id); - }} - onDoubleClick={onConnectClick} - removeAllRecentsConnections={() => { - void removeAllRecentConnections(); - }} - removeConnection={({ id }) => { - void removeConnection(id); - }} - duplicateConnection={({ id }) => { - void duplicateConnection(id, { autoDuplicate: true }); - }} - openConnectionImportExportModal={openConnectionImportExportModal} - /> - -
-
- { - log.error( - mongoLogId(1001000108), - 'Connect Form', - 'Rendering connect form failed', - { error: error.message, errorInfo } - ); - }} - > -
- - - -
-
- -
-
- {activeConnectionStatus === 'connecting' && ( - - void disconnect(activeConnectionInfo.id) - } - /> - )} -
- ); -} - -export default Connections; diff --git a/packages/compass-connections/src/index.spec.tsx b/packages/compass-connections/src/index.spec.tsx index 5110e320161..ddeab15f4be 100644 --- a/packages/compass-connections/src/index.spec.tsx +++ b/packages/compass-connections/src/index.spec.tsx @@ -1,21 +1,53 @@ import React from 'react'; +import type { RenderConnectionsOptions } from '@mongodb-js/testing-library-compass'; import { createDefaultConnectionInfo, render, + waitFor, } from '@mongodb-js/testing-library-compass'; import { expect } from 'chai'; +function renderCompassConnections(opts?: RenderConnectionsOptions) { + return render( +
+ {/* it's a bit weird, but testing-library-compass already renders CompassConnections for us */} +
, + opts + ); +} + describe('CompassConnections', function () { + it('autoconnects and does not save autoconnect info when mounted', async function () { + const { connectionsStore, connectionStorage } = renderCompassConnections({ + onAutoconnectInfoRequest() { + return Promise.resolve({ + id: 'autoconnect', + connectionOptions: { + connectionString: 'mongodb://autoconnect', + }, + }); + }, + }); + + await waitFor(() => { + expect(connectionsStore.getState().connections.byId) + .to.have.property('autoconnect') + .have.property('status', 'connected'); + }); + + const storedConnection = await connectionStorage.load({ + id: 'autoconnect', + }); + + // autoconnect info should never be saved + expect(storedConnection).to.eq(undefined); + }); + it('cleans-up connections when unmounted', async function () { const conn1 = createDefaultConnectionInfo(); const conn2 = createDefaultConnectionInfo(); - const result = render( -
- {/* it's a bit weird, but testing-library-compass already renders CompassConnections for us */} -
, - { connections: [conn1, conn2] } - ); + const result = renderCompassConnections({ connections: [conn1, conn2] }); await result.connectionsStore.actions.connect(conn1); diff --git a/packages/compass-connections/src/index.tsx b/packages/compass-connections/src/index.tsx index ebb03bee83a..b1a6073d68a 100644 --- a/packages/compass-connections/src/index.tsx +++ b/packages/compass-connections/src/index.tsx @@ -11,7 +11,6 @@ import type { import { telemetryLocator } from '@mongodb-js/compass-telemetry/provider'; import type { ExtraConnectionData as ExtraConnectionDataForTelemetry } from '@mongodb-js/compass-telemetry'; import { ConnectedConnectionModal } from './components/connection-modal'; -export { default as SingleConnectionForm } from './components/legacy-connections'; export { LegacyConnectionsModal } from './components/legacy-connections-modal'; import { autoconnectCheck, @@ -27,14 +26,42 @@ export type { ConnectionFeature } from './utils/connection-supports'; export { connectionSupports } from './utils/connection-supports'; const ConnectionsComponent: React.FunctionComponent<{ + /** + * Application name, will be passed to the driver during connection + */ appName: string; + /** + * Callback prop that should resolve with any extra connection information to + * be added to the connection tracking + */ onExtraConnectionDataRequest: ( connectionInfo: ConnectionInfo ) => Promise<[ExtraConnectionDataForTelemetry, string | null]>; + /** + * Callback prop that might optionally resolve with the connectionInfo object + * to be automatically connected to as soon as plugin is activated. + * ConnectionStorage argument can be used to pick connectionInfo from the list + * of existing connections + */ onAutoconnectInfoRequest?: ( connectionStorage: ConnectionStorage ) => Promise; + /** + * By default any connection returned by `onAutoconnectInfoRequest` will be + * automatically connected. This property can be used to disable "reconnect" + * if connection with the matching id was explicitly disconnected by the user + * in the UI before in the same session. Currently this is only behavior of + * Compass desktop. + */ + doNotReconnectDisconnectedAutoconnectInfo?: boolean; + /** + * Can be used to override default connection function + */ connectFn?: typeof devtoolsConnect | undefined; + /** + * Can be used to provide preloaded connections instead of triggering loading + * connections on plugin activate + */ preloadStorageConnectionInfos?: ConnectionInfo[]; }> = ({ children }) => { return ( @@ -69,7 +96,10 @@ const CompassConnectionsPlugin = registerHadronPlugin( void store.dispatch(loadConnections()); if (initialProps.onAutoconnectInfoRequest) { void store.dispatch( - autoconnectCheck(initialProps.onAutoconnectInfoRequest) + autoconnectCheck( + initialProps.onAutoconnectInfoRequest, + initialProps.doNotReconnectDisconnectedAutoconnectInfo + ) ); } }); diff --git a/packages/compass-connections/src/provider.ts b/packages/compass-connections/src/provider.ts index 96c74721ddf..467621afb6c 100644 --- a/packages/compass-connections/src/provider.ts +++ b/packages/compass-connections/src/provider.ts @@ -1,13 +1,7 @@ import { createServiceLocator } from 'hadron-app-registry'; import { useConnectionInfo } from './connection-info-provider'; import type { DataService } from 'mongodb-data-service'; -import { - useConnectionActions, - useConnectionForId, - useConnectionIds, - useConnections, -} from './stores/store-context'; -import { useConnections as useConnectionsStore } from './stores/connections-store'; +import { useConnections } from './stores/store-context'; export type { DataService }; export { useConnectionsWithStatus } from './hooks/use-connections-with-status'; @@ -97,7 +91,6 @@ export { connectionsLocator, } from './stores/store-context'; -export { useConnectionsStore as useConnections }; export { useConnectionSupports } from './hooks/use-connection-supports'; const ConnectionStatus = { @@ -116,36 +109,3 @@ const ConnectionStatus = { } as const; export { ConnectionStatus }; - -/** - * @deprecated compatibility for single connection mode: in single connection - * mode the first "connected" connection is the current application connection - */ -export function useSingleConnectionModeConnectionInfoStatus() { - const [connectionId = '-1'] = useConnectionIds((connection) => { - return ( - connection.status === 'connected' || - connection.status === 'connecting' || - connection.status === 'failed' - ); - }); - const connectionState = useConnectionForId(connectionId); - const { disconnect } = useConnectionActions(); - return connectionState && connectionState.status === 'connected' - ? { - isConnected: true as const, - connectionInfo: connectionState.info, - connectionError: null, - disconnect: () => { - disconnect(connectionState.info.id); - return undefined; - }, - } - : { - isConnected: false as const, - connectionInfo: connectionState?.info ?? null, - connectionError: - connectionState?.status === 'failed' ? connectionState.error : null, - disconnect: () => undefined, - }; -} diff --git a/packages/compass-connections/src/stores/connections-store.spec.tsx b/packages/compass-connections/src/stores/connections-store-redux.spec.tsx similarity index 53% rename from packages/compass-connections/src/stores/connections-store.spec.tsx rename to packages/compass-connections/src/stores/connections-store-redux.spec.tsx index bad3d139d91..d6f6cc36492 100644 --- a/packages/compass-connections/src/stores/connections-store.spec.tsx +++ b/packages/compass-connections/src/stores/connections-store-redux.spec.tsx @@ -1,13 +1,12 @@ import { expect } from 'chai'; import sinon from 'sinon'; -import { useConnections } from './connections-store'; +import type { RenderConnectionsOptions } from '@mongodb-js/testing-library-compass'; import { - cleanup, - renderWithConnections, waitFor, screen, createDefaultConnectionInfo, wait, + render, } from '@mongodb-js/testing-library-compass'; import React from 'react'; @@ -34,76 +33,30 @@ const mockConnections = [ }, ]; -const defaultPreferences = { - enableMultipleConnectionSystem: true, - maximumNumberOfActiveConnections: undefined, -}; - -// A bit of a special case, testing-library doesn't allow to test hooks that -// have UI side-effects, but we're doing it in these connection hooks -function renderHookWithConnections( - cb: () => T, - options: Parameters[1] -) { - const hookResult = { current: null } as { current: T }; - const HookGetter = () => { - hookResult.current = cb(); - return null; - }; - const result = renderWithConnections(, options); - return { ...result, result: hookResult }; +function renderCompassConnections(opts?: RenderConnectionsOptions) { + return render( +
+ {/* it's a bit weird, but testing-library-compass already renders CompassConnections for us */} +
, + opts + ); } -describe('useConnections', function () { +describe('CompassConnections store', function () { afterEach(() => { - cleanup(); sinon.resetHistory(); sinon.restore(); }); - it('autoconnects on mount and does not save autoconnect info', async function () { - const { connectionsStore, connectionStorage } = renderHookWithConnections( - useConnections, - { - preferences: defaultPreferences, - connections: mockConnections, - onAutoconnectInfoRequest() { - return Promise.resolve({ - id: 'autoconnect', - connectionOptions: { - connectionString: 'mongodb://autoconnect', - }, - }); - }, - } - ); - - await waitFor(() => { - expect(connectionsStore.getState().connections.byId) - .to.have.property('autoconnect') - .have.property('status', 'connected'); - }); - - const storedConnection = await connectionStorage.load({ - id: 'autoconnect', - }); - - // autoconnect info should never be saved - expect(storedConnection).to.eq(undefined); - }); - describe('#connect', function () { it('should show notifications throughout connection flow and save connection to persistent store', async function () { - const { result, connectionStorage, track } = renderHookWithConnections( - useConnections, - { - preferences: defaultPreferences, + const { connectionsStore, connectionStorage, track } = + renderCompassConnections({ connectFn: async () => { await wait(100); return {}; }, - } - ); + }); const connectionInfo = createDefaultConnectionInfo(); @@ -113,7 +66,7 @@ describe('useConnections', function () { // Verifying it's not in storage expect(storedConnectionBeforeConnect).to.eq(undefined); - const connectPromise = result.current.connect(connectionInfo); + const connectPromise = connectionsStore.actions.connect(connectionInfo); await waitFor(() => { expect(track).to.have.been.calledWith('Connection Attempt'); @@ -139,8 +92,7 @@ describe('useConnections', function () { }); it('should show error toast if connection failed', async function () { - const { result } = renderHookWithConnections(useConnections, { - preferences: defaultPreferences, + const { connectionsStore } = renderCompassConnections({ connectFn: sinon .stub() .rejects(new Error('Failed to connect to cluster')), @@ -148,7 +100,7 @@ describe('useConnections', function () { const connectionInfo = createDefaultConnectionInfo(); - const connectPromise = result.current.connect(connectionInfo); + const connectPromise = connectionsStore.actions.connect(connectionInfo); await waitFor(() => { expect(screen.getByText('Failed to connect to cluster')).to.exist; @@ -164,11 +116,9 @@ describe('useConnections', function () { }); it('should show non-genuine modal at the end of connection if non genuine mongodb detected', async function () { - const { result } = renderHookWithConnections(useConnections, { - preferences: defaultPreferences, - }); + const { connectionsStore } = renderCompassConnections({}); - await result.current.connect({ + await connectionsStore.actions.connect({ id: '123', connectionOptions: { connectionString: @@ -183,14 +133,13 @@ describe('useConnections', function () { }); it('should show max connections toast if maximum connections number reached', async function () { - const { result } = renderHookWithConnections(useConnections, { + const { connectionsStore } = renderCompassConnections({ preferences: { - ...defaultPreferences, maximumNumberOfActiveConnections: 0, }, }); - const connectPromise = result.current.connect( + const connectPromise = connectionsStore.actions.connect( createDefaultConnectionInfo() ); @@ -211,12 +160,11 @@ describe('useConnections', function () { }); }); - const { result } = renderHookWithConnections(useConnections, { - preferences: defaultPreferences, + const { connectionsStore } = renderCompassConnections({ connectFn, }); - const connectPromise = result.current.connect( + const connectPromise = connectionsStore.actions.connect( createDefaultConnectionInfo() ); @@ -254,91 +202,68 @@ describe('useConnections', function () { }); }); - for (const multipleConnectionsEnabled of [true, false]) { - describe(`when multiple connections ${ - multipleConnectionsEnabled ? 'enabled' : 'disabled' - }`, function () { - it('should only update favorite info for existing connection with new props when existing connection is successfull', async function () { - const { result, connectionStorage } = renderHookWithConnections( - useConnections, - { - connections: mockConnections, - preferences: { - ...defaultPreferences, - enableMultipleConnectionSystem: multipleConnectionsEnabled, - }, - } - ); + it('should only update favorite info for existing connection with new props when existing connection is successfull', async function () { + const { connectionsStore, connectionStorage } = renderCompassConnections({ + connections: mockConnections, + }); - await result.current.connect({ - ...mockConnections[0], - connectionOptions: { - ...mockConnections[0].connectionOptions, - connectionString: 'mongodb://foobar', - }, - favorite: { name: 'foobar' }, - }); + await connectionsStore.actions.connect({ + ...mockConnections[0], + connectionOptions: { + ...mockConnections[0].connectionOptions, + connectionString: 'mongodb://foobar', + }, + favorite: { name: 'foobar' }, + }); - const storedConnection = await connectionStorage.load({ - id: mockConnections[0].id, - }); + const storedConnection = await connectionStorage.load({ + id: mockConnections[0].id, + }); - // Connection string in the storage wasn't updated - expect(storedConnection).to.have.nested.property( - 'connectionOptions.connectionString', - 'mongodb://turtle' - ); - - // Connection favorite name was updated - expect(storedConnection).to.have.nested.property( - 'favorite.name', - 'foobar' - ); - }); + // Connection string in the storage wasn't updated + expect(storedConnection).to.have.nested.property( + 'connectionOptions.connectionString', + 'mongodb://turtle' + ); - it('should not update existing connection if connection failed', async function () { - const { result, connectionStorage } = renderHookWithConnections( - useConnections, - { - connections: mockConnections, - preferences: { - ...defaultPreferences, - enableMultipleConnectionSystem: multipleConnectionsEnabled, - }, - connectFn: sinon.stub().rejects(new Error('Failed to connect')), - } - ); + // Connection favorite name was updated + expect(storedConnection).to.have.nested.property( + 'favorite.name', + 'foobar' + ); + }); - await result.current.connect({ - ...mockConnections[0], - favorite: { name: 'foobar' }, - }); + it('should not update existing connection if connection failed', async function () { + const { connectionsStore, connectionStorage } = renderCompassConnections({ + connections: mockConnections, + connectFn: sinon.stub().rejects(new Error('Failed to connect')), + }); - // Connection in the storage wasn't updated - expect( - await connectionStorage.load({ id: mockConnections[0].id }) - ).to.have.nested.property('favorite.name', 'turtles'); - }); + await connectionsStore.actions.connect({ + ...mockConnections[0], + favorite: { name: 'foobar' }, }); - } + + // Connection in the storage wasn't updated + expect( + await connectionStorage.load({ id: mockConnections[0].id }) + ).to.have.nested.property('favorite.name', 'turtles'); + }); }); describe('#saveAndConnect', function () { it('saves the connection options before connecting', async function () { - const { result, track, connectionStorage } = renderHookWithConnections( - useConnections, - { + const { connectionsStore, track, connectionStorage } = + renderCompassConnections({ connections: mockConnections, - preferences: defaultPreferences, - } - ); + }); const updatedConnection = { ...mockConnections[0], savedConnectionType: 'recent' as const, }; - await result.current.saveAndConnect(updatedConnection); + await connectionsStore.actions.saveAndConnect(updatedConnection); await waitFor(() => { expect(track.getCall(1).firstArg).to.eq('New Connection'); @@ -356,8 +281,7 @@ describe('useConnections', function () { describe('#disconnect', function () { it('disconnect even if connection is in progress cleaning up progress toasts', async function () { - const { result, track } = renderHookWithConnections(useConnections, { - preferences: defaultPreferences, + const { connectionsStore, track } = renderCompassConnections({ connectFn() { return new Promise(() => { // going to cancel this one @@ -366,13 +290,13 @@ describe('useConnections', function () { }); const connectionInfo = createDefaultConnectionInfo(); - const connectPromise = result.current.connect(connectionInfo); + const connectPromise = connectionsStore.actions.connect(connectionInfo); await waitFor(() => { expect(screen.getByText(/Connecting to/)).to.exist; }); - result.current.disconnect(connectionInfo.id); + connectionsStore.actions.disconnect(connectionInfo.id); await connectPromise; expect(track).to.have.been.calledWith('Connection Disconnected'); @@ -380,58 +304,31 @@ describe('useConnections', function () { }); }); - describe('#createNewConnection', function () { - it('in single connection mode should "open" connection form create new connection info for editing every time', function () { - const { result } = renderHookWithConnections(useConnections, { - preferences: { enableMultipleConnectionSystem: false }, - }); - - expect(result.current.state.isEditingConnectionInfoModalOpen).to.eq( - false - ); - - result.current.createNewConnection(); - const conn1 = result.current.state.editingConnectionInfo; - - expect(result.current.state.isEditingConnectionInfoModalOpen).to.eq(true); - - result.current.createNewConnection(); - const conn2 = result.current.state.editingConnectionInfo; - - expect(result.current.state.isEditingConnectionInfoModalOpen).to.eq(true); - - result.current.createNewConnection(); - const conn3 = result.current.state.editingConnectionInfo; - - expect(result.current.state.isEditingConnectionInfoModalOpen).to.eq(true); - - expect(conn1).to.not.deep.eq(conn2); - expect(conn1).to.not.deep.eq(conn3); - }); - }); + describe('#createNewConnection', function () {}); describe('#saveEditedConnection', function () { it('new connection: should call save and track the creation', async function () { - const { result, track, connectionStorage } = renderHookWithConnections( - useConnections, - { - preferences: defaultPreferences, - } - ); + const { connectionsStore, track, connectionStorage } = + renderCompassConnections({}); // We can't save non-existent connections, create new one before // proceeding - result.current.createNewConnection(); + connectionsStore.actions.createNewConnection(); + + const editingConnection = + connectionsStore.getState().connections.byId[ + connectionsStore.getState().editingConnectionInfoId + ]; const newConnection = { - ...result.current.state.editingConnectionInfo, + ...editingConnection.info, favorite: { name: 'peaches (50) peaches', }, savedConnectionType: 'favorite' as const, }; - await result.current.saveEditedConnection(newConnection); + await connectionsStore.actions.saveEditedConnection(newConnection); expect(track).to.have.been.calledWith('Connection Created'); @@ -439,20 +336,17 @@ describe('useConnections', function () { }); it('existing connection: should call save and should not track the creation', async function () { - const { result, track, connectionStorage } = renderHookWithConnections( - useConnections, - { + const { connectionsStore, track, connectionStorage } = + renderCompassConnections({ connections: mockConnections, - preferences: defaultPreferences, - } - ); + }); const updatedConnection = { ...mockConnections[0], savedConnectionType: 'recent' as const, }; - await result.current.saveEditedConnection(updatedConnection); + await connectionsStore.actions.saveEditedConnection(updatedConnection); expect(track).not.to.have.been.called; @@ -464,15 +358,14 @@ describe('useConnections', function () { describe('#removeConnection', function () { it('should disconnect and call delete and track the deletion', async function () { - const { result, connectionsStore, connectionStorage, track } = - renderHookWithConnections(useConnections, { + const { connectionsStore, connectionStorage, track } = + renderCompassConnections({ connections: mockConnections, - preferences: defaultPreferences, }); await connectionsStore.actions.connect(mockConnections[0]); - result.current.removeConnection(mockConnections[0].id); + connectionsStore.actions.removeConnection(mockConnections[0].id); await waitFor(() => { expect(track).to.have.been.calledWith('Connection Removed'); @@ -489,41 +382,36 @@ describe('useConnections', function () { describe('#editConnection', function () { it('should only allow to edit existing connections', function () { - const { result } = renderHookWithConnections(useConnections, { + const { connectionsStore } = renderCompassConnections({ connections: mockConnections, - preferences: defaultPreferences, }); - result.current.editConnection('123'); - expect(result.current.state).to.have.property( + connectionsStore.actions.editConnection('123'); + expect(connectionsStore.getState()).to.have.property( 'isEditingConnectionInfoModalOpen', false ); - result.current.editConnection(mockConnections[0].id); - expect(result.current.state).to.have.property( + connectionsStore.actions.editConnection(mockConnections[0].id); + expect(connectionsStore.getState()).to.have.property( 'isEditingConnectionInfoModalOpen', true ); - expect(result.current.state).to.have.property( - 'editingConnectionInfo', - mockConnections[0] + expect(connectionsStore.getState()).to.have.property( + 'editingConnectionInfoId', + mockConnections[0].id ); }); }); describe('#duplicateConnection', function () { it('should copy connection and add a copy number at the end', function () { - const { result, connectionsStore } = renderHookWithConnections( - useConnections, - { - connections: mockConnections, - preferences: defaultPreferences, - } - ); + const { connectionsStore } = renderCompassConnections({ + connections: mockConnections, + }); for (let i = 0; i <= 30; i++) { - result.current.duplicateConnection(mockConnections[1].id, { + connectionsStore.actions.duplicateConnection(mockConnections[1].id, { autoDuplicate: true, }); } @@ -538,27 +426,23 @@ describe('useConnections', function () { }); it('should create a name if there is none', async function () { - const { result, connectionsStore } = renderHookWithConnections( - useConnections, - { - connections: [ - { - id: '123', - connectionOptions: { - connectionString: 'mongodb://localhost:27017', - }, - favorite: { - name: '', - color: 'color2', - }, - savedConnectionType: 'recent' as const, + const { connectionsStore } = renderCompassConnections({ + connections: [ + { + id: '123', + connectionOptions: { + connectionString: 'mongodb://localhost:27017', }, - ], - preferences: defaultPreferences, - } - ); + favorite: { + name: '', + color: 'color2', + }, + savedConnectionType: 'recent' as const, + }, + ], + }); - result.current.duplicateConnection('123', { + connectionsStore.actions.duplicateConnection('123', { autoDuplicate: true, }); @@ -582,14 +466,11 @@ describe('useConnections', function () { savedConnectionType: 'favorite' as const, }; - const { result, connectionsStore } = renderHookWithConnections( - useConnections, - { - connections: [newConnection], - } - ); + const { connectionsStore } = renderCompassConnections({ + connections: [newConnection], + }); - result.current.duplicateConnection(newConnection.id, { + connectionsStore.actions.duplicateConnection(newConnection.id, { autoDuplicate: true, }); diff --git a/packages/compass-connections/src/stores/connections-store-redux.ts b/packages/compass-connections/src/stores/connections-store-redux.ts index 1d3dcb99ba9..80a84e2b2b5 100644 --- a/packages/compass-connections/src/stores/connections-store-redux.ts +++ b/packages/compass-connections/src/stores/connections-store-redux.ts @@ -43,6 +43,39 @@ export type ConnectionsEventMap = { ) => void; }; +/** + * Returns status of the autoconnect connection preserved for the duration of + * the session. If connection was ever disconnected, this value will be used to + * not connect again + */ +export function getSessionConnectionStatus(connectionId = '-1') { + try { + return window.sessionStorage.getItem(`CONNECTION_STATUS:${connectionId}`); + } catch { + return null; + } +} + +/** + * Allows to store connection status for the duration of the current session + * (while the browser tab / window exists). Useful to preserve + * connection state between page reloads. Currently only used by Compass desktop + * to prevent autoconnecting to trigger on hard page refresh. + */ +export function setSessionConnectionStatus( + connectionId: string, + status: 'disconnected' +) { + try { + return window.sessionStorage.setItem( + `CONNECTION_STATUS:${connectionId}`, + status + ); + } catch { + return false; + } +} + export interface ConnectionsEventEmitter { emit( this: void, @@ -1369,7 +1402,8 @@ const connectionAttemptError = ( export const autoconnectCheck = ( getAutoconnectInfo: ( connectionStorage: ConnectionStorage - ) => Promise + ) => Promise, + doNotReconnectDisconnectedAutoconnectInfo = false ): ConnectionsThunkAction< Promise, ConnectionAutoconnectCheckAction | ConnectionAttemptErrorAction @@ -1386,6 +1420,12 @@ export const autoconnectCheck = ( 'Performing automatic connection attempt' ); const connectionInfo = await getAutoconnectInfo(connectionStorage); + if ( + doNotReconnectDisconnectedAutoconnectInfo && + getSessionConnectionStatus(connectionInfo?.id) === 'disconnected' + ) { + return; + } dispatch({ type: ActionTypes.ConnectionAutoconnectCheck, connectionInfo: connectionInfo, @@ -2029,9 +2069,10 @@ export const disconnect = ( ): ConnectionsThunkAction => { return (dispatch, getState, { logger: { debug } }) => { debug('closing connection with connectionId', connectionId); - + if (getState().connections.byId[connectionId]?.isAutoconnectInfo) { + setSessionConnectionStatus(connectionId, 'disconnected'); + } dispatch(cleanupConnection(connectionId)); - dispatch({ type: ActionTypes.Disconnect, connectionId }); }; }; diff --git a/packages/compass-connections/src/stores/connections-store.tsx b/packages/compass-connections/src/stores/connections-store.tsx deleted file mode 100644 index 523fa5c7c48..00000000000 --- a/packages/compass-connections/src/stores/connections-store.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import type { ConnectionInfo } from '@mongodb-js/connection-storage/provider'; -import { useConnectionActions, useConnectionsState } from './store-context'; - -type State = { - connectionErrors: Record; - editingConnectionInfo: ConnectionInfo; - isEditingConnectionInfoModalOpen: boolean; - isEditingNewConnection: boolean; - oidcDeviceAuthState: Record; -}; - -/** - * @deprecated use connections-store hooks instead - */ -export function useConnections(): { - state: State; - - connect: (connectionInfo: ConnectionInfo) => Promise; - saveAndConnect: (connectionInfo: ConnectionInfo) => Promise; - disconnect: (connectionId: string) => void; - - createNewConnection: () => void; - editConnection: (connectionId: string) => void; - saveEditedConnection: (connectionInfo: ConnectionInfo) => Promise; - cancelEditConnection: (connectionId: string) => void; - duplicateConnection: ( - connectionId: string, - options?: { autoDuplicate: boolean } - ) => void; - toggleConnectionFavoritedStatus: (connectionId: string) => void; - removeConnection: (connectionId: string) => void; - - removeAllRecentConnections: () => void; - showNonGenuineMongoDBWarningModal: (connectionId: string) => void; -} { - const connectionsState = useConnectionsState(); - const editingConnection = - connectionsState.connections.byId[connectionsState.editingConnectionInfoId]; - const state = { - connectionErrors: Object.fromEntries( - Object.entries(connectionsState.connections.byId).map(([k, v]) => { - return [k, v.error ?? null]; - }) - ), - editingConnectionInfo: editingConnection.info, - isEditingNewConnection: !!editingConnection.isBeingCreated, - isEditingConnectionInfoModalOpen: - connectionsState.isEditingConnectionInfoModalOpen, - oidcDeviceAuthState: Object.fromEntries( - Object.entries(connectionsState.oidcDeviceAuthInfo).map(([k, v]) => { - return [k, { url: v.verificationUrl, code: v.userCode }]; - }) - ), - }; - const { - connect, - saveAndConnect, - disconnect, - createNewConnection, - editConnection, - saveEditedConnection, - cancelEditConnection, - duplicateConnection, - toggleFavoritedConnectionStatus, - removeConnection, - removeAllRecentConnections, - showNonGenuineMongoDBWarningModal, - } = useConnectionActions(); - return { - state, - connect, - saveAndConnect, - disconnect, - createNewConnection, - editConnection, - saveEditedConnection, - cancelEditConnection, - duplicateConnection, - toggleConnectionFavoritedStatus: toggleFavoritedConnectionStatus, - removeConnection, - removeAllRecentConnections, - showNonGenuineMongoDBWarningModal, - }; -} diff --git a/packages/compass-connections/src/stores/store-context.tsx b/packages/compass-connections/src/stores/store-context.tsx index 80a66aa7978..03d4d15e9f2 100644 --- a/packages/compass-connections/src/stores/store-context.tsx +++ b/packages/compass-connections/src/stores/store-context.tsx @@ -326,20 +326,6 @@ export function useConnectionInfoRefForId(connectionId: ConnectionId): { return ref; } -/** - * @deprecated exposed for compat with old connections store interface, should - * never be used anywhere else - */ -export function useConnectionsState() { - return useSelector((state) => state, { - // These warnings are very noisy. We know this selector is bad, but there is - // no way to reimplement old connections store without it and we're going to - // remove it soon anyway - stabilityCheck: 'never', - noopCheck: 'never', - }); -} - export function useConnectionsColorList(): { id: ConnectionId; color: string | undefined; diff --git a/packages/compass/src/app/components/home.tsx b/packages/compass/src/app/components/home.tsx index 65741de8f80..5cf88e8b73e 100644 --- a/packages/compass/src/app/components/home.tsx +++ b/packages/compass/src/app/components/home.tsx @@ -8,22 +8,17 @@ import { getScrollbarStyles, palette, resetGlobalCSS, - useEffectOnChange, } from '@mongodb-js/compass-components'; import CompassConnections, { - SingleConnectionForm, LegacyConnectionsModal, } from '@mongodb-js/compass-connections'; import { CompassFindInPagePlugin } from '@mongodb-js/compass-find-in-page'; import type { SettingsTabId } from '@mongodb-js/compass-settings'; import { CompassSettingsPlugin } from '@mongodb-js/compass-settings'; import { WelcomeModal } from '@mongodb-js/compass-welcome'; -import * as hadronIpc from 'hadron-ipc'; import { type ConnectionStorage } from '@mongodb-js/connection-storage/provider'; -import { AppRegistryProvider, useLocalAppRegistry } from 'hadron-app-registry'; -import type AppRegistry from 'hadron-app-registry'; -import { useSingleConnectionModeConnectionInfoStatus } from '@mongodb-js/compass-connections/provider'; -import React, { useCallback, useEffect, useState } from 'react'; +import { AppRegistryProvider } from 'hadron-app-registry'; +import React, { useCallback, useState } from 'react'; import Workspace from './workspace'; import { getExtraConnectionData } from '../utils/telemetry'; // The only place where the app-stores plugin can be used as a plugin and not a @@ -35,26 +30,11 @@ import { AtlasAuthPlugin } from '@mongodb-js/atlas-service/renderer'; import { CompassGenerativeAIPlugin } from '@mongodb-js/compass-generative-ai'; import type { WorkspaceTab } from '@mongodb-js/compass-workspaces'; import { ConnectionStorageProvider } from '@mongodb-js/connection-storage/provider'; -import { - ConnectionImportExportProvider, - useOpenConnectionImportExportModal, -} from '@mongodb-js/compass-connection-import-export'; -import { usePreference } from 'compass-preferences-model/provider'; +import { ConnectionImportExportProvider } from '@mongodb-js/compass-connection-import-export'; import { useTelemetry } from '@mongodb-js/compass-telemetry/provider'; -import { ConnectionInfoProvider } from '@mongodb-js/compass-connections/provider'; -import { CompassShellPlugin } from '@mongodb-js/compass-shell'; resetGlobalCSS(); -const homePageStyles = css({ - display: 'flex', - flexDirection: 'row', - alignItems: 'stretch', - flex: 1, - overflow: 'auto', - height: '100%', -}); - const homeContainerStyles = css({ height: '100vh', width: '100vw', @@ -78,31 +58,11 @@ const globalDarkThemeStyles = css({ export type HomeProps = { appName: string; showWelcomeModal?: boolean; - onDisconnect: () => void; showCollectionSubMenu: (args: { isReadOnly: boolean }) => void; hideCollectionSubMenu: () => void; showSettings: (tab?: SettingsTabId) => void; }; -function SingleConnectionFormWithConnectionImportExport({ - appRegistry, -}: { - appRegistry: AppRegistry; -}) { - const { supportsConnectionImportExport, openConnectionImportExportModal } = - useOpenConnectionImportExportModal({ context: 'connectionsList' }); - return ( - - ); -} - const verticalSplitStyles = css({ width: '100vw', height: '100vh', @@ -112,35 +72,13 @@ const verticalSplitStyles = css({ overflow: 'hidden', }); -const shellContainerStyles = css({ - zIndex: 5, -}); - function Home({ appName, showWelcomeModal = false, - onDisconnect, showCollectionSubMenu, hideCollectionSubMenu, showSettings, }: HomeProps): React.ReactElement | null { - const appRegistry = useLocalAppRegistry(); - const { connectionInfo, isConnected, disconnect } = - useSingleConnectionModeConnectionInfoStatus(); - - useEffect(() => { - function onDisconnect() { - void disconnect(); - } - - hadronIpc.ipcRenderer?.on('app:disconnect', onDisconnect); - - return () => { - // Clean up the ipc listener. - hadronIpc.ipcRenderer?.removeListener('app:disconnect', onDisconnect); - }; - }, [disconnect]); - const onWorkspaceChange = useCallback( (ws: WorkspaceTab | null, collectionInfo) => { if (ws?.type === 'Collection') { @@ -152,13 +90,6 @@ function Home({ [showCollectionSubMenu, hideCollectionSubMenu] ); - useEffectOnChange(() => { - if (!isConnected) { - hideCollectionSubMenu(); - onDisconnect(); - } - }, [isConnected, onDisconnect, hideCollectionSubMenu]); - const [isWelcomeOpen, setIsWelcomeOpen] = useState(showWelcomeModal); const closeWelcomeModal = useCallback( @@ -171,43 +102,17 @@ function Home({ [setIsWelcomeOpen, showSettings] ); - const multiConnectionsEnabled = usePreference( - 'enableMultipleConnectionSystem' - ); - return (
- {multiConnectionsEnabled && ( - - - - )} - {!multiConnectionsEnabled && - (isConnected ? ( - - - -
- -
-
-
- ) : ( -
- -
- ))} + + +
@@ -243,6 +148,7 @@ function HomeWithConnections({ appName={props.appName} onExtraConnectionDataRequest={getExtraConnectionData} onAutoconnectInfoRequest={onAutoconnectInfoRequest} + doNotReconnectDisconnectedAutoconnectInfo > diff --git a/packages/compass/src/app/components/workspace.tsx b/packages/compass/src/app/components/workspace.tsx index 7804ee984c8..16c6d6335a5 100644 --- a/packages/compass/src/app/components/workspace.tsx +++ b/packages/compass/src/app/components/workspace.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useCallback } from 'react'; +import React, { useCallback } from 'react'; import { WorkspaceTab as ShellWorkspace } from '@mongodb-js/compass-shell'; import { WorkspaceTab as CollectionWorkspace, @@ -78,10 +78,6 @@ export default function Workspace({ [appName, getConnectionInfoById, onActiveWorkspaceTabChange] ); - useEffect(() => { - updateTitle(appName); - }, [appName]); - return ( ', - positionalArguments: [''], - passphrase: '', - username: '', - password: '', - }; - onCompassDisconnect({ id: 1 }); - expect( - await getWindowAutoConnectPreferences({ id: 1 }, preferences) - ).to.deep.equal({ - shouldAutoConnect: false, - }); - registerMongoDbUrlForBrowserWindow({ id: 2 }, 'mongodb://foo'); - onCompassDisconnect({ id: 2 }); - expect( - await getWindowAutoConnectPreferences({ id: 2 }, preferences) - ).to.deep.equal({ - shouldAutoConnect: false, - }); - }); - it('should ignore about: urls', async function () { fakePreferences = { positionalArguments: ['about:blank'], diff --git a/packages/compass/src/main/auto-connect.ts b/packages/compass/src/main/auto-connect.ts index 47ba394f47f..becfdd587d9 100644 --- a/packages/compass/src/main/auto-connect.ts +++ b/packages/compass/src/main/auto-connect.ts @@ -178,12 +178,3 @@ export async function getWindowAutoConnectPreferences( shouldAutoConnect: true, }; } - -export function onCompassDisconnect( - bw: Pick | undefined | null -): void { - if (!bw) { - return; - } - browserWindowStates.set(bw.id, { disconnected: true }); -} diff --git a/packages/compass/src/main/menu.spec.ts b/packages/compass/src/main/menu.spec.ts index 0dd004ba711..c216cc3199f 100644 --- a/packages/compass/src/main/menu.spec.ts +++ b/packages/compass/src/main/menu.spec.ts @@ -287,92 +287,7 @@ describe('CompassMenu', function () { } }); - // TODO(COMPASS-7906): remove - it.skip('[single-connection] should generate a menu template for darwin', function () { - sinon.stub(process, 'platform').value('darwin'); - expect(serializable(CompassMenu.getTemplate(0))).to.deep.equal([ - { - label: app.getName(), - submenu: [ - { label: `About ${app.getName()}`, role: 'about' }, - { label: 'Check for updates…' }, - { type: 'separator' }, - { label: '&Settings', accelerator: 'CmdOrCtrl+,' }, - { type: 'separator' }, - { label: 'Hide', accelerator: 'Command+H', role: 'hide' }, - { - label: 'Hide Others', - accelerator: 'Command+Shift+H', - role: 'hideOthers', - }, - { label: 'Show All', role: 'unhide' }, - { type: 'separator' }, - { label: 'Quit', accelerator: 'CmdOrCtrl+Q' }, - ], - }, - { - label: '&Connections', - submenu: [ - { label: 'New &Window', accelerator: 'CmdOrCtrl+N' }, - { label: '&Disconnect' }, - { type: 'separator' }, - { label: '&Import Saved Connections' }, - { label: '&Export Saved Connections' }, - ], - }, - { - label: 'Edit', - submenu: [ - { label: 'Undo', accelerator: 'Command+Z', role: 'undo' }, - { label: 'Redo', accelerator: 'Shift+Command+Z', role: 'redo' }, - { type: 'separator' }, - { label: 'Cut', accelerator: 'Command+X', role: 'cut' }, - { label: 'Copy', accelerator: 'Command+C', role: 'copy' }, - { label: 'Paste', accelerator: 'Command+V', role: 'paste' }, - { - label: 'Select All', - accelerator: 'Command+A', - role: 'selectAll', - }, - { type: 'separator' }, - { label: 'Find', accelerator: 'CmdOrCtrl+F' }, - ], - }, - { - label: '&View', - submenu: [ - { label: '&Reload', accelerator: 'CmdOrCtrl+Shift+R' }, - { label: '&Reload Data', accelerator: 'CmdOrCtrl+R' }, - { type: 'separator' }, - { label: 'Actual Size', accelerator: 'CmdOrCtrl+0' }, - { label: 'Zoom In', accelerator: 'CmdOrCtrl+=' }, - { label: 'Zoom Out', accelerator: 'CmdOrCtrl+-' }, - ], - }, - { - label: 'Window', - submenu: [ - { label: 'Minimize', accelerator: 'Command+M', role: 'minimize' }, - { label: 'Close', accelerator: 'Command+Shift+W', role: 'close' }, - { type: 'separator' }, - { label: 'Bring All to Front', role: 'front' }, - ], - }, - { - label: '&Help', - submenu: [ - { label: `&Online ${app.getName()} Help`, accelerator: 'F1' }, - { label: '&License' }, - { label: `&View Source Code on GitHub` }, - { label: `&Suggest a Feature` }, - { label: `&Report a Bug` }, - { label: '&Open Log File' }, - ], - }, - ]); - }); - - it('[multiple-connection] should generate a menu template for darwin', async function () { + it('should generate a menu template for darwin', async function () { await App.preferences.savePreferences({ enableMultipleConnectionSystem: true, }); @@ -457,137 +372,6 @@ describe('CompassMenu', function () { ]); }); - ['win32', 'linux'].forEach((platform) => { - // TODO(COMPASS-7906): remove - it.skip(`[single-connection] should generate a menu template for ${platform}`, function () { - sinon.stub(process, 'platform').value(platform); - - expect(serializable(CompassMenu.getTemplate(0))).to.deep.equal([ - { - label: '&Connections', - submenu: [ - { label: 'New &Window', accelerator: 'CmdOrCtrl+N' }, - { label: '&Disconnect' }, - { type: 'separator' }, - { label: '&Import Saved Connections' }, - { label: '&Export Saved Connections' }, - { type: 'separator' }, - { label: 'E&xit', accelerator: 'CmdOrCtrl+Q' }, - ], - }, - { - label: 'Edit', - submenu: [ - { label: 'Undo', accelerator: 'Command+Z', role: 'undo' }, - { label: 'Redo', accelerator: 'Shift+Command+Z', role: 'redo' }, - { type: 'separator' }, - { label: 'Cut', accelerator: 'Command+X', role: 'cut' }, - { label: 'Copy', accelerator: 'Command+C', role: 'copy' }, - { label: 'Paste', accelerator: 'Command+V', role: 'paste' }, - { - label: 'Select All', - accelerator: 'Command+A', - role: 'selectAll', - }, - { type: 'separator' }, - { label: 'Find', accelerator: 'CmdOrCtrl+F' }, - { type: 'separator' }, - { label: '&Settings', accelerator: 'CmdOrCtrl+,' }, - ], - }, - { - label: '&View', - submenu: [ - { label: '&Reload', accelerator: 'CmdOrCtrl+Shift+R' }, - { label: '&Reload Data', accelerator: 'CmdOrCtrl+R' }, - { type: 'separator' }, - { label: 'Actual Size', accelerator: 'CmdOrCtrl+0' }, - { label: 'Zoom In', accelerator: 'CmdOrCtrl+=' }, - { label: 'Zoom Out', accelerator: 'CmdOrCtrl+-' }, - ], - }, - { - label: '&Help', - submenu: [ - { label: `&Online ${app.getName()} Help`, accelerator: 'F1' }, - { label: '&License' }, - { label: `&View Source Code on GitHub` }, - { label: `&Suggest a Feature` }, - { label: `&Report a Bug` }, - { label: '&Open Log File' }, - { type: 'separator' }, - { label: `&About ${app.getName()}` }, - { label: 'Check for updates…' }, - ], - }, - ]); - }); - - it(`[multiple-connection] should generate a menu template for ${platform}`, async function () { - await App.preferences.savePreferences({ - enableMultipleConnectionSystem: true, - }); - sinon.stub(process, 'platform').value(platform); - - expect(serializable(CompassMenu.getTemplate(0))).to.deep.equal([ - { - label: '&Connections', - submenu: [ - { label: '&Import Saved Connections' }, - { label: '&Export Saved Connections' }, - { type: 'separator' }, - { label: 'E&xit', accelerator: 'CmdOrCtrl+Q' }, - ], - }, - { - label: 'Edit', - submenu: [ - { label: 'Undo', accelerator: 'Command+Z', role: 'undo' }, - { label: 'Redo', accelerator: 'Shift+Command+Z', role: 'redo' }, - { type: 'separator' }, - { label: 'Cut', accelerator: 'Command+X', role: 'cut' }, - { label: 'Copy', accelerator: 'Command+C', role: 'copy' }, - { label: 'Paste', accelerator: 'Command+V', role: 'paste' }, - { - label: 'Select All', - accelerator: 'Command+A', - role: 'selectAll', - }, - { type: 'separator' }, - { label: 'Find', accelerator: 'CmdOrCtrl+F' }, - { type: 'separator' }, - { label: '&Settings', accelerator: 'CmdOrCtrl+,' }, - ], - }, - { - label: '&View', - submenu: [ - { label: '&Reload', accelerator: 'CmdOrCtrl+Shift+R' }, - { label: '&Reload Data', accelerator: 'CmdOrCtrl+R' }, - { type: 'separator' }, - { label: 'Actual Size', accelerator: 'CmdOrCtrl+0' }, - { label: 'Zoom In', accelerator: 'CmdOrCtrl+=' }, - { label: 'Zoom Out', accelerator: 'CmdOrCtrl+-' }, - ], - }, - { - label: '&Help', - submenu: [ - { label: `&Online ${app.getName()} Help`, accelerator: 'F1' }, - { label: '&License' }, - { label: `&View Source Code on GitHub` }, - { label: `&Suggest a Feature` }, - { label: `&Report a Bug` }, - { label: '&Open Log File' }, - { type: 'separator' }, - { label: `&About ${app.getName()}` }, - { label: 'Check for updates…' }, - ], - }, - ]); - }); - }); - it('does not crash when rendering menu item with an accelerator', () => { const window = new BrowserWindow({ show: false }); const template = CompassMenu.getTemplate(window.id); diff --git a/packages/compass/src/main/menu.ts b/packages/compass/src/main/menu.ts index 809a0ea12df..88e4563f5d7 100644 --- a/packages/compass/src/main/menu.ts +++ b/packages/compass/src/main/menu.ts @@ -144,30 +144,11 @@ function newWindowItem( }; } -function disconnectItem(): MenuItemConstructorOptions { - return { - label: '&Disconnect', - click() { - ipcMain?.broadcastFocused('app:disconnect'); - }, - }; -} - function connectSubMenu( nonDarwin: boolean, app: typeof CompassApplication ): MenuItemConstructorOptions { - const { enableMultipleConnectionSystem: isMultiConnectionsEnabled } = - app.preferences.getPreferences(); - - const singleConnectionItems: MenuTemplate = [ - newWindowItem(app), - disconnectItem(), - separator(), - ]; - const subMenu: MenuTemplate = [ - ...(!isMultiConnectionsEnabled ? singleConnectionItems : []), { label: '&Import Saved Connections', click() { diff --git a/packages/compass/src/main/window-manager.ts b/packages/compass/src/main/window-manager.ts index c5d999630fb..fe66f91cbca 100644 --- a/packages/compass/src/main/window-manager.ts +++ b/packages/compass/src/main/window-manager.ts @@ -19,7 +19,6 @@ import COMPASS_ICON from './icon'; import type { CompassApplication } from './application'; import { getWindowAutoConnectPreferences, - onCompassDisconnect, registerMongoDbUrlForBrowserWindow, } from './auto-connect'; @@ -261,10 +260,6 @@ class CompassWindowManager { 'compass:log'(_evt, meta) { ipcMain?.broadcast('compass:log', meta); }, - 'compass:disconnected': (evt) => { - const bw = BrowserWindow.fromWebContents(evt.sender); - return onCompassDisconnect(bw); - }, 'compass:get-window-auto-connect-preferences': (evt) => { const bw = BrowserWindow.fromWebContents(evt.sender); return getWindowAutoConnectPreferences(bw, compassApp.preferences);