Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(connect): can not select files on the connection form VSCODE-658 #898

Merged
merged 13 commits into from
Dec 10, 2024
Merged
231 changes: 117 additions & 114 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1209,9 +1209,9 @@
"@babel/core": "^7.25.8",
"@babel/parser": "^7.25.8",
"@babel/traverse": "^7.25.7",
"@mongodb-js/compass-components": "^1.30.1",
"@mongodb-js/connection-form": "^1.42.0",
"@mongodb-js/connection-info": "^0.9.1",
"@mongodb-js/compass-components": "^1.32.1",
"@mongodb-js/connection-form": "^1.45.1",
"@mongodb-js/connection-info": "^0.9.5",
"@mongodb-js/mongodb-constants": "^0.10.3",
"@mongosh/browser-runtime-electron": "^2.3.3",
"@mongosh/i18n": "^2.3.3",
Expand Down
4 changes: 2 additions & 2 deletions src/test/suite/views/webview-app/overview-page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ describe('OverviewPage test suite', function () {
.getCalls()
.filter(
(call) =>
call.args[0].command === MESSAGE_TYPES.EDIT_AND_CONNECT_CONNECTION
call.args[0].command === MESSAGE_TYPES.EDIT_CONNECTION_AND_CONNECT
);
};
expect(getConnectMessages()).to.have.length(0);
Expand All @@ -165,7 +165,7 @@ describe('OverviewPage test suite', function () {
expect(connectMessages).to.have.length(1);

expect(connectMessages[0].args[0]).to.deep.equal({
command: 'EDIT_AND_CONNECT_CONNECTION',
command: 'EDIT_CONNECTION_AND_CONNECT',
connectionInfo: {
id: 'pear',
connectionOptions: {
Expand Down
12 changes: 6 additions & 6 deletions src/test/suite/views/webviewController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ suite('Webview Test Suite', () => {
const extensionPath = mdbTestExtension.extensionContextStub.extensionPath;
const htmlString = getWebviewContent({
extensionPath,
telemetryUserId: 'MOCK_ANONYMOU_ID',
telemetryUserId: 'MOCK_ANONYMOUS_ID',
webview: {
asWebviewUri: (jsUri) => {
return jsUri;
Expand All @@ -133,7 +133,7 @@ suite('Webview Test Suite', () => {
});

expect(htmlString).to.include(
">window['VSCODE_EXTENSION_SEGMENT_ANONYMOUS_ID'] = 'MOCK_ANONYMOU_ID';"
">window['VSCODE_EXTENSION_SEGMENT_ANONYMOUS_ID'] = 'MOCK_ANONYMOUS_ID';"
);
});

Expand All @@ -143,7 +143,7 @@ suite('Webview Test Suite', () => {
extensionPath,
telemetryUserId: 'test',
webview: {
asWebviewUri: (jsUri) => {
asWebviewUri: (jsUri: vscode.Uri) => {
return jsUri;
},
} as unknown as vscode.Webview,
Expand Down Expand Up @@ -177,7 +177,7 @@ suite('Webview Test Suite', () => {
extensionPath,
telemetryUserId: 'test',
webview: {
asWebviewUri: (jsUri) => {
asWebviewUri: (jsUri: vscode.Uri) => {
return jsUri;
},
} as unknown as vscode.Webview,
Expand Down Expand Up @@ -368,7 +368,7 @@ suite('Webview Test Suite', () => {
onDidReceiveMessage: (callback): void => {
messageReceived = callback;
},
asWebviewUri: () => '',
asWebviewUri: (): string => '',
};
const fakeVSCodeExecuteCommand = sandbox
.stub(vscode.commands, 'executeCommand')
Expand Down Expand Up @@ -538,7 +538,7 @@ suite('Webview Test Suite', () => {

// Mock a connection status request call.
messageReceived({
command: MESSAGE_TYPES.EDIT_AND_CONNECT_CONNECTION,
command: MESSAGE_TYPES.EDIT_CONNECTION_AND_CONNECT,
connectionInfo: {
id: 'pineapple',
connectionOptions: {
Expand Down
2 changes: 1 addition & 1 deletion src/views/webview-app/connection-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const ConnectionForm: React.FunctionComponent<
// Warning: This property may be removed in future
// modal releases.
contentClassName={modalContentStyles}
setOpen={() => onClose()}
setOpen={(): void => onClose()}
open={open}
size="large"
>
Expand Down
35 changes: 28 additions & 7 deletions src/views/webview-app/extension-app-message-constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ConnectionOptions } from 'mongodb-data-service';
import type { FileChooserOptions } from './use-connection-form';

export enum CONNECTION_STATUS {
LOADING = 'LOADING', // When the connection status has not yet been shared from the extension.
Expand All @@ -19,9 +20,11 @@ export enum MESSAGE_TYPES {
CANCEL_CONNECT = 'CANCEL_CONNECT',
CONNECT_RESULT = 'CONNECT_RESULT',
CONNECTION_FORM_OPENED = 'CONNECTION_FORM_OPENED',
OPEN_FILE_CHOOSER = 'OPEN_FILE_CHOOSER',
OPEN_FILE_CHOOSER_RESULT = 'OPEN_FILE_CHOOSER_RESULT',
CONNECTION_STATUS_MESSAGE = 'CONNECTION_STATUS_MESSAGE',
OPEN_EDIT_CONNECTION = 'OPEN_EDIT_CONNECTION',
EDIT_AND_CONNECT_CONNECTION = 'EDIT_AND_CONNECT_CONNECTION',
EDIT_CONNECTION_AND_CONNECT = 'EDIT_CONNECTION_AND_CONNECT',
EXTENSION_LINK_CLICKED = 'EXTENSION_LINK_CLICKED',
CREATE_NEW_PLAYGROUND = 'CREATE_NEW_PLAYGROUND',
GET_CONNECTION_STATUS = 'GET_CONNECTION_STATUS',
Expand Down Expand Up @@ -58,14 +61,20 @@ export interface OpenEditConnectionMessage extends BasicWebviewMessage {
};
}

export interface EditAndConnectConnection extends BasicWebviewMessage {
command: MESSAGE_TYPES.EDIT_AND_CONNECT_CONNECTION;
export interface EditConnectionAndConnectMessage extends BasicWebviewMessage {
command: MESSAGE_TYPES.EDIT_CONNECTION_AND_CONNECT;
connectionInfo: {
id: string;
connectionOptions: ConnectionOptions;
};
}

export interface OpenFileChooserMessage extends BasicWebviewMessage {
command: MESSAGE_TYPES.OPEN_FILE_CHOOSER;
fileChooserOptions: FileChooserOptions;
requestId: string;
}

export interface ConnectMessage extends BasicWebviewMessage {
command: MESSAGE_TYPES.CONNECT;
connectionInfo: {
Expand All @@ -85,6 +94,16 @@ export interface ConnectResultsMessage extends BasicWebviewMessage {
connectionId: string;
}

export type FileChooserResult =
| { canceled: false; filePaths: string[] }
| { canceled: false; filePath?: string };

export interface OpenFileChooserResultMessage extends BasicWebviewMessage {
command: MESSAGE_TYPES.OPEN_FILE_CHOOSER_RESULT;
fileChooserResult: FileChooserResult;
requestId: string;
}

export interface GetConnectionStatusMessage extends BasicWebviewMessage {
command: MESSAGE_TYPES.GET_CONNECTION_STATUS;
}
Expand Down Expand Up @@ -113,7 +132,7 @@ export interface ThemeChangedMessage extends BasicWebviewMessage {
darkMode: boolean;
}

export type MESSAGE_FROM_WEBVIEW_TO_EXTENSION =
export type MessageFromWebviewToExtension =
| ConnectMessage
| CancelConnectMessage
| ConnectionFormOpenedMessage
Expand All @@ -123,10 +142,12 @@ export type MESSAGE_FROM_WEBVIEW_TO_EXTENSION =
| OpenConnectionStringInputMessage
| OpenTrustedLinkMessage
| RenameConnectionMessage
| EditAndConnectConnection;
| EditConnectionAndConnectMessage
| OpenFileChooserMessage;

export type MESSAGE_FROM_EXTENSION_TO_WEBVIEW =
export type MessageFromExtensionToWebview =
| ConnectResultsMessage
| ConnectionStatusMessage
| ThemeChangedMessage
| OpenEditConnectionMessage;
| OpenEditConnectionMessage
| OpenFileChooserResultMessage;
106 changes: 89 additions & 17 deletions src/views/webview-app/overview-page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
import React, { useCallback, useLayoutEffect, useState } from 'react';
import type { ElectronShowFileDialogProvider } from '@mongodb-js/compass-components';
import {
HorizontalRule,
css,
resetGlobalCSS,
spacing,
FileInputBackendProvider,
createElectronFileInputBackend,
type ElectronFileDialogOptions,
} from '@mongodb-js/compass-components';
import type { ConnectionOptions } from 'mongodb-data-service';

import OverviewHeader from './overview-header';
import ConnectionStatus from './connection-status';
import ConnectHelper from './connect-helper';
import AtlasCta from './atlas-cta';
import ResourcesPanel from './resources-panel/panel';
import { ConnectionForm } from './connection-form';
import useConnectionForm from './use-connection-form';
import useConnectionForm, {
FILE_CHOOSER_MODE,
type FileChooserOptions,
} from './use-connection-form';
import type { MessageFromExtensionToWebview } from './extension-app-message-constants';
import { MESSAGE_TYPES } from './extension-app-message-constants';

const pageStyles = css({
width: '90%',
Expand All @@ -39,6 +49,7 @@ const OverviewPage: React.FC = () => {
handleCancelConnectClicked,
handleSaveConnectionClicked,
handleConnectClicked,
handleOpenFileChooser,
} = useConnectionForm();
const handleResourcesPanelClose = useCallback(
() => setShowResourcesPanel(false),
Expand All @@ -55,30 +66,91 @@ const OverviewPage: React.FC = () => {
resetGlobalCSS();
}, []);

function handleOpenFileChooserResult<T>(
options: FileChooserOptions
): Promise<T> {
const requestId = handleOpenFileChooser(options);
return new Promise((resolve) => {
const messageHandler = (
event: MessageEvent<MessageFromExtensionToWebview>
): void => {
const message = event.data;
if (
message.command === MESSAGE_TYPES.OPEN_FILE_CHOOSER_RESULT &&
message.requestId === requestId
) {
window.removeEventListener('message', messageHandler);
resolve(message.fileChooserResult as T);
}
};
window.addEventListener('message', messageHandler);
});
}

// Electron 32.0 removed support for the `path` property of the Web File object in favor of the webUtils.getPathForFile method.
// https://github.com/electron/electron/blob/83d704009687956fb4b69cb13ab03664d7950118/docs/breaking-changes.md%23removed-filepath
// We can not import `dialog` and `webUtils` from 'electron' in the sandboxed webview.
// To work around this, we use a custom dialog provider that uses webview APIs
// to send a message to the extension process to open the electron file dialog
// and listen for the response to get the file path and send them to the electron file input backend.
const dialogProvider: ElectronShowFileDialogProvider<void> = {
getCurrentWindow(): void {},
dialog: {
async showSaveDialog(
window: void,
electronFileDialogOptions: Partial<ElectronFileDialogOptions>
): Promise<{ canceled: boolean; filePath?: string }> {
return handleOpenFileChooserResult({
electronFileDialogOptions,
mode: FILE_CHOOSER_MODE.SAVE,
});
},
async showOpenDialog(
window: void,
electronFileDialogOptions: Partial<ElectronFileDialogOptions>
): Promise<{ canceled: boolean; filePaths: string[] }> {
return handleOpenFileChooserResult({
electronFileDialogOptions,
mode: FILE_CHOOSER_MODE.OPEN,
});
},
},
};

return (
<div data-testid="overview-page" className={pageStyles}>
{showResourcesPanel && (
<ResourcesPanel onClose={handleResourcesPanelClose} />
)}
{isConnectionFormOpen && (
<ConnectionForm
isConnecting={isConnecting}
initialConnectionInfo={initialConnectionInfo}
onSaveAndConnectClicked={({ id, connectionOptions }) => {
void handleSaveConnectionClicked({
id,
connectionOptions,
});
handleConnectClicked({
<FileInputBackendProvider
createFileInputBackend={createElectronFileInputBackend(
dialogProvider,
null
)}
>
<ConnectionForm
isConnecting={isConnecting}
initialConnectionInfo={initialConnectionInfo}
onSaveAndConnectClicked={({
id,
connectionOptions,
});
}}
onCancelConnectClicked={handleCancelConnectClicked}
onClose={closeConnectionForm}
open={isConnectionFormOpen}
connectionErrorMessage={connectionErrorMessage}
/>
}: {
id: string;
connectionOptions: ConnectionOptions;
}): void => {
void handleSaveConnectionClicked();
handleConnectClicked({
id,
connectionOptions,
});
}}
onCancelConnectClicked={handleCancelConnectClicked}
onClose={closeConnectionForm}
open={isConnectionFormOpen}
connectionErrorMessage={connectionErrorMessage}
/>
</FileInputBackendProvider>
)}
<OverviewHeader onResourcesClick={handleResourcesClick} />
<HorizontalRule />
Expand Down
Loading
Loading