Skip to content

Commit 4956ea4

Browse files
committed
save, connect, save & onnect
1 parent 529a71d commit 4956ea4

File tree

7 files changed

+129
-62
lines changed

7 files changed

+129
-62
lines changed

packages/compass-connections/src/stores/connections-store.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ type State = {
55
connectionErrors: Record<string, Error | null>;
66
editingConnectionInfo: ConnectionInfo;
77
isEditingConnectionInfoModalOpen: boolean;
8+
isEditingNewConnection: boolean;
89
oidcDeviceAuthState: Record<string, { url: string; code: string }>;
910
};
1011

@@ -32,16 +33,16 @@ export function useConnections(): {
3233
showNonGenuineMongoDBWarningModal: (connectionId: string) => void;
3334
} {
3435
const connectionsState = useConnectionsState();
36+
const editingConnection =
37+
connectionsState.connections.byId[connectionsState.editingConnectionInfoId];
3538
const state = {
3639
connectionErrors: Object.fromEntries(
3740
Object.entries(connectionsState.connections.byId).map(([k, v]) => {
3841
return [k, v.error ?? null];
3942
})
4043
),
41-
editingConnectionInfo:
42-
connectionsState.connections.byId[
43-
connectionsState.editingConnectionInfoId
44-
].info,
44+
editingConnectionInfo: editingConnection.info,
45+
isEditingNewConnection: !!editingConnection.isBeingCreated,
4546
isEditingConnectionInfoModalOpen:
4647
connectionsState.isEditingConnectionInfoModalOpen,
4748
oidcDeviceAuthState: Object.fromEntries(

packages/compass-sidebar/src/components/multiple-connections/sidebar.tsx

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export function MultipleConnectionSidebar({
120120
showNonGenuineMongoDBWarningModal,
121121
state: {
122122
editingConnectionInfo,
123+
isEditingNewConnection,
123124
isEditingConnectionInfoModalOpen,
124125
connectionErrors,
125126
},
@@ -184,6 +185,10 @@ export function MultipleConnectionSidebar({
184185
[globalAppRegistry]
185186
);
186187

188+
const disableEditingConnectedConnection = !!findActiveConnection(
189+
editingConnectionInfo.id
190+
);
191+
187192
return (
188193
<ResizableSidebar data-testid="navigation-sidebar" useNewTheme={true}>
189194
<aside className={sidebarStyles}>
@@ -229,7 +234,7 @@ export function MultipleConnectionSidebar({
229234
{editingConnectionInfo && (
230235
<ConnectionFormModal
231236
disableEditingConnectedConnection={
232-
!!findActiveConnection(editingConnectionInfo.id)
237+
disableEditingConnectedConnection
233238
}
234239
onDisconnectClicked={() => disconnect(editingConnectionInfo.id)}
235240
isOpen={isEditingConnectionInfoModalOpen}
@@ -240,20 +245,32 @@ export function MultipleConnectionSidebar({
240245
}
241246
}}
242247
initialConnectionInfo={editingConnectionInfo}
243-
onSaveAndConnectClicked={(connectionInfo) => {
244-
void connect(connectionInfo);
245-
}}
246-
onSaveClicked={(connectionInfo) => {
247-
return saveEditedConnection(connectionInfo);
248-
}}
249-
onCancel={() => {
250-
cancelEditConnection(editingConnectionInfo.id);
251-
}}
252248
connectionErrorMessage={
253249
connectionErrors[editingConnectionInfo.id]?.message
254250
}
255251
openSettingsModal={openSettingsModal}
256252
{...formPreferences}
253+
onCancel={() => {
254+
cancelEditConnection(editingConnectionInfo.id);
255+
}}
256+
onSaveClicked={(connectionInfo) => {
257+
return saveEditedConnection(connectionInfo);
258+
}}
259+
onConnectClicked={
260+
isEditingNewConnection || disableEditingConnectedConnection
261+
? undefined
262+
: (connectionInfo) => {
263+
void connect(connectionInfo);
264+
}
265+
}
266+
onSaveAndConnectClicked={
267+
disableEditingConnectedConnection
268+
? undefined
269+
: (connectionInfo) => {
270+
void saveEditedConnection(connectionInfo);
271+
void connect(connectionInfo);
272+
}
273+
}
257274
/>
258275
)}
259276
<MappedCsfleModal

packages/connection-form/src/components/connection-form-actions.spec.tsx renamed to packages/connection-form/src/components/connection-form-modal-actions.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { render, screen, userEvent } from '@mongodb-js/testing-library-compass';
33
import { expect } from 'chai';
44
import sinon from 'sinon';
55

6-
import { ConnectionFormModalActions } from './connection-form-actions';
6+
import { ConnectionFormModalActions } from './connection-form-modal-actions';
77

88
describe('<ConnectionFormModalActions />', function () {
99
it('should show warnings', function () {

packages/connection-form/src/components/connection-form-actions.tsx renamed to packages/connection-form/src/components/connection-form-modal-actions.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export type ConnectionFormModalActionsProps = {
4545
onCancel?(): void;
4646
onSave?(): void;
4747
onSaveAndConnect?(): void;
48+
onConnect?(): void;
4849
};
4950

5051
export function ConnectionFormModalActions({
@@ -53,6 +54,7 @@ export function ConnectionFormModalActions({
5354
onCancel,
5455
onSave,
5556
onSaveAndConnect,
57+
onConnect
5658
}: ConnectionFormModalActionsProps): React.ReactElement {
5759
const saveAndConnectLabel = useConnectionFormSetting('saveAndConnectLabel');
5860
return (
@@ -102,9 +104,19 @@ export function ConnectionFormModalActions({
102104
</div>
103105
)}
104106

107+
{onConnect && (
108+
<Button
109+
data-testid={'connect-button'}
110+
variant={ButtonVariant.PrimaryOutline}
111+
onClick={onConnect}
112+
>
113+
Connect
114+
</Button>
115+
)}
116+
105117
{onSaveAndConnect && (
106118
<Button
107-
data-testid="connect-button"
119+
data-testid={onConnect ? 'save-and-connect-button' : 'connect-button'}
108120
variant={ButtonVariant.Primary}
109121
onClick={onSaveAndConnect}
110122
>

packages/connection-form/src/components/connection-form.spec.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,14 @@ describe('ConnectionForm Component', function () {
8181
it('renders a banner, disables the connection string and removes advanced connection options + connect button', function () {
8282
const onDisconnectClicked = Sinon.spy();
8383
const onSaveClicked = Sinon.spy();
84-
const onSaveAndConnectClicked = Sinon.spy();
84+
const onConnectClicked = undefined;
85+
const onSaveAndConnectClicked = undefined;
8586

8687
renderForm({
8788
disableEditingConnectedConnection: true,
8889
onDisconnectClicked,
8990
onSaveClicked,
91+
onConnectClicked,
9092
onSaveAndConnectClicked,
9193
});
9294

@@ -105,7 +107,6 @@ describe('ConnectionForm Component', function () {
105107
// pressing enter calls onSubmit which saves
106108
fireEvent.submit(screen.getByRole('form'));
107109
expect(onSaveClicked.callCount).to.equal(1);
108-
expect(onSaveAndConnectClicked.callCount).to.equal(0);
109110

110111
fireEvent.click(screen.getByRole('button', { name: 'Disconnect' }));
111112
expect(onDisconnectClicked.callCount).to.equal(1);
@@ -116,12 +117,14 @@ describe('ConnectionForm Component', function () {
116117
it('leaves the connection string, advanced connection options and connect button intact, does not render a banner', function () {
117118
const onDisconnectClicked = Sinon.spy();
118119
const onSaveClicked = Sinon.spy();
120+
const onConnectClicked = Sinon.spy();
119121
const onSaveAndConnectClicked = Sinon.spy();
120122

121123
renderForm({
122124
disableEditingConnectedConnection: false,
123125
onDisconnectClicked,
124126
onSaveClicked,
127+
onConnectClicked,
125128
onSaveAndConnectClicked,
126129
});
127130

packages/connection-form/src/components/connection-form.tsx

Lines changed: 77 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
import { cloneDeep } from 'lodash';
2626
import ConnectionStringInput from './connection-string-input';
2727
import AdvancedConnectionOptions from './advanced-connection-options';
28-
import { ConnectionFormModalActions } from './connection-form-actions';
28+
import { ConnectionFormModalActions } from './connection-form-modal-actions';
2929
import {
3030
useConnectForm,
3131
type ConnectionPersonalizationOptions,
@@ -316,13 +316,27 @@ type ConnectionFormPropsWithoutSettings = {
316316
initialConnectionInfo: ConnectionInfo;
317317
connectionErrorMessage?: string | null;
318318
disableEditingConnectedConnection?: boolean;
319-
onCancel?: () => void;
320-
onConnectClicked?: (connectionInfo: ConnectionInfo) => void;
321-
onSaveAndConnectClicked?: (connectionInfo: ConnectionInfo) => void;
322-
onSaveClicked?: (connectionInfo: ConnectionInfo) => Promise<void>;
323319
onDisconnectClicked?: () => void;
324320
onAdvancedOptionsToggle?: (newState: boolean) => void;
325321
openSettingsModal?: (tab?: string) => void;
322+
323+
// form action buttons:
324+
325+
onCancel?: () => void;
326+
327+
// If onSaveClicked is specified, a Save button will show up. It will be the
328+
// primary button if there is no onSaveAndConnectClick.
329+
onSaveClicked?: (connectionInfo: ConnectionInfo) => Promise<void>;
330+
331+
// If onConnectClicked is specified, a Connect button will show up.
332+
onConnectClicked?: (connectionInfo: ConnectionInfo) => void;
333+
334+
// If onSaveAndConnectClicked is specified, a button will show up with
335+
// its label set to the value of the setting saveAndConnectLabel. It will be
336+
// the primary button if specified. If onConnectClicked is specified, the test
337+
// id will be save-and-connect-button, otherwise it will be connect-button.
338+
// For historical/backwards compatible reasons.
339+
onSaveAndConnectClicked?: (connectionInfo: ConnectionInfo) => void;
326340
};
327341

328342
export type ConnectionFormProps = ConnectionFormPropsWithoutSettings &
@@ -334,6 +348,7 @@ function ConnectionForm({
334348
disableEditingConnectedConnection = false,
335349
onSaveAndConnectClicked,
336350
onSaveClicked,
351+
onConnectClicked,
337352
onDisconnectClicked,
338353
onCancel,
339354
onAdvancedOptionsToggle,
@@ -422,39 +437,52 @@ function ConnectionForm({
422437
},
423438
[onSaveClicked, setErrors]
424439
);
425-
const onSubmitForm = useCallback(() => {
426-
const updatedConnectionOptions = cloneDeep(connectionOptions);
427-
// TODO: this method throws on malformed connection strings instead of
428-
// returning errors
429-
const formErrors = validateConnectionOptionsErrors(
430-
updatedConnectionOptions
431-
);
432-
if (formErrors.length) {
433-
setErrors(formErrors);
434-
return;
435-
}
436-
if (disableEditingConnectedConnection) {
437-
// there isn't a connect button in this case, so the default action should
438-
// be to save, not to save and connect
439-
void callOnSaveConnectionClickedAndStoreErrors?.(
440-
getConnectionInfoToSave()
440+
const onSubmitForm = useCallback(
441+
(intendedAction?: 'connect' | 'save-and-connect') => {
442+
const updatedConnectionOptions = cloneDeep(connectionOptions);
443+
// TODO: this method throws on malformed connection strings instead of
444+
// returning errors
445+
const formErrors = validateConnectionOptionsErrors(
446+
updatedConnectionOptions
441447
);
442-
} else {
443-
onSaveAndConnectClicked?.({
444-
...initialConnectionInfo,
445-
...getConnectionInfoToSave(),
446-
connectionOptions: updatedConnectionOptions,
447-
});
448-
}
449-
}, [
450-
connectionOptions,
451-
disableEditingConnectedConnection,
452-
setErrors,
453-
callOnSaveConnectionClickedAndStoreErrors,
454-
getConnectionInfoToSave,
455-
onSaveAndConnectClicked,
456-
initialConnectionInfo,
457-
]);
448+
if (formErrors.length) {
449+
setErrors(formErrors);
450+
return;
451+
}
452+
if (intendedAction === 'connect') {
453+
// The user explicitly clicked connect, so we use that.
454+
onConnectClicked?.({
455+
...initialConnectionInfo,
456+
...getConnectionInfoToSave(),
457+
connectionOptions: updatedConnectionOptions,
458+
});
459+
} else {
460+
if (onSaveAndConnectClicked) {
461+
// If there is a save&connect button, we use that action by default.
462+
onSaveAndConnectClicked?.({
463+
...initialConnectionInfo,
464+
...getConnectionInfoToSave(),
465+
connectionOptions: updatedConnectionOptions,
466+
});
467+
} else {
468+
// If there isn't a save&connect button then the next default action
469+
// should be to save.
470+
void callOnSaveConnectionClickedAndStoreErrors?.(
471+
getConnectionInfoToSave()
472+
);
473+
}
474+
}
475+
},
476+
[
477+
connectionOptions,
478+
setErrors,
479+
onConnectClicked,
480+
initialConnectionInfo,
481+
getConnectionInfoToSave,
482+
callOnSaveConnectionClickedAndStoreErrors,
483+
onSaveAndConnectClicked,
484+
]
485+
);
458486

459487
const showPersonalisationForm = useConnectionFormSetting(
460488
'showPersonalisationForm'
@@ -588,14 +616,20 @@ function ConnectionForm({
588616
warnings={connectionStringInvalidError ? [] : warnings}
589617
onCancel={onCancel}
590618
onSave={
591-
onSaveClicked &&
592-
(() =>
593-
void callOnSaveConnectionClickedAndStoreErrors?.(
594-
getConnectionInfoToSave()
595-
))
619+
onSaveClicked
620+
? () =>
621+
void callOnSaveConnectionClickedAndStoreErrors?.(
622+
getConnectionInfoToSave()
623+
)
624+
: undefined
625+
}
626+
onConnect={
627+
onConnectClicked ? () => onSubmitForm('connect') : undefined
596628
}
597629
onSaveAndConnect={
598-
disableEditingConnectedConnection ? undefined : onSubmitForm
630+
onSaveAndConnectClicked
631+
? () => onSubmitForm('save-and-connect')
632+
: undefined
599633
}
600634
/>
601635
</div>

packages/connection-form/src/hooks/use-connect-form-settings.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const defaultSettings = {
3333
showKerberosAuth: true,
3434
showCSFLE: true,
3535
showProxySettings: true,
36-
saveAndConnectLabel: 'Connect',
36+
saveAndConnectLabel: 'Save & Connect',
3737
};
3838

3939
export const ConnectionFormSettingsContext = createContext<

0 commit comments

Comments
 (0)