Skip to content

Commit e3c2427

Browse files
authored
feat(compass-web): add welcome page COMPASS-8657 (#6593)
* welcome page for web * remove single connection when form is not active * tests * connections clean up for single connection * remove more code to fix tests * cluster url
1 parent dce7b3b commit e3c2427

File tree

12 files changed

+468
-331
lines changed

12 files changed

+468
-331
lines changed

packages/compass-connections/src/stores/connections-store-redux.ts

Lines changed: 9 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,7 @@ export type State = {
196196
}
197197
>;
198198

199-
// State related to connection editing form. Can't be null in single
200-
// connection mode, so we always have a default connection set here even if
201-
// nothing is being actively edited. Can be updated when single-connection
202-
// mode is removed from the app
203-
editingConnectionInfoId: ConnectionId;
199+
editingConnectionInfoId: ConnectionId | null;
204200
isEditingConnectionInfoModalOpen: boolean;
205201

206202
// State related to connection favorite fields editing modal form (right now
@@ -487,23 +483,15 @@ export function createDefaultConnectionState(
487483
};
488484
}
489485

490-
// For single connection mode we always have to start with the initial empty
491-
// connection in the state already. This can be removed when single connection
492-
// mode doesn't exist anymore
493-
const INITIAL_CONNECTION_STATE = createDefaultConnectionState();
494-
INITIAL_CONNECTION_STATE.isBeingCreated = true;
495-
496486
const INITIAL_STATE: State = {
497487
connections: {
498-
ids: [INITIAL_CONNECTION_STATE.info.id],
499-
byId: {
500-
[INITIAL_CONNECTION_STATE.info.id]: INITIAL_CONNECTION_STATE,
501-
},
488+
ids: [],
489+
byId: {},
502490
status: 'initial',
503491
error: null,
504492
},
505493
oidcDeviceAuthInfo: {},
506-
editingConnectionInfoId: INITIAL_CONNECTION_STATE.info.id,
494+
editingConnectionInfoId: null,
507495
isEditingConnectionInfoModalOpen: false,
508496
editingConnectionFavoriteInfoId: null,
509497
isEditingConnectionFavoriteInfoModalOpen: false,
@@ -513,13 +501,9 @@ export function getInitialConnectionsStateForConnectionInfos(
513501
connectionInfos: ConnectionInfo[] = []
514502
): State['connections'] {
515503
const byId = Object.fromEntries<ConnectionState>(
516-
[
517-
[INITIAL_CONNECTION_STATE.info.id, INITIAL_CONNECTION_STATE] as const,
518-
].concat(
519-
connectionInfos.map((info) => {
520-
return [info.id, createDefaultConnectionState(info)];
521-
})
522-
)
504+
connectionInfos.map((info) => {
505+
return [info.id, createDefaultConnectionState(info)];
506+
})
523507
);
524508
return {
525509
byId,
@@ -985,28 +969,10 @@ const reducer: Reducer<State, Action> = (state = INITIAL_STATE, action) => {
985969
const newConnection = createDefaultConnectionState();
986970
newConnection.isBeingCreated = true;
987971

988-
let newConnectionsState = state.connections;
989-
990-
// Only relevant for single connections mode: if we're currently editing
991-
// "new connection", clean it up from state before creating state for a new
992-
// one
993-
if (
994-
state.editingConnectionInfoId &&
995-
state.connections.byId[state.editingConnectionInfoId].isBeingCreated
996-
) {
997-
newConnectionsState = {
998-
...newConnectionsState,
999-
byId: {
1000-
...newConnectionsState.byId,
1001-
},
1002-
};
1003-
delete newConnectionsState.byId[state.editingConnectionInfoId];
1004-
}
1005-
1006972
return {
1007973
...state,
1008974
connections: mergeConnectionStateById(
1009-
newConnectionsState,
975+
state.connections,
1010976
newConnection.info.id,
1011977
newConnection
1012978
),
@@ -1078,7 +1044,7 @@ const reducer: Reducer<State, Action> = (state = INITIAL_STATE, action) => {
10781044
}
10791045

10801046
let connections = state.connections;
1081-
let editingConnectionInfoId = state.editingConnectionInfoId;
1047+
const editingConnectionInfoId = state.editingConnectionInfoId;
10821048

10831049
// In cases where connection was never saved or used before, we remove it
10841050
// from the connections state
@@ -1095,20 +1061,6 @@ const reducer: Reducer<State, Action> = (state = INITIAL_STATE, action) => {
10951061
byId: newConnectionsById,
10961062
ids: newIds,
10971063
};
1098-
1099-
// Special case for single connection: after removing connection, we
1100-
// automatically create a new connection and will "select" it for editing.
1101-
// Can go away when single connection mode is removed
1102-
if (state.editingConnectionInfoId === action.connectionId) {
1103-
const newDefaultConnection = createDefaultConnectionState();
1104-
newDefaultConnection.isBeingCreated = true;
1105-
connections = mergeConnectionStateById(
1106-
connections,
1107-
newDefaultConnection.info.id,
1108-
newDefaultConnection
1109-
);
1110-
editingConnectionInfoId = newDefaultConnection.info.id;
1111-
}
11121064
}
11131065

11141066
return {

packages/compass-web/src/entrypoint.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ import type {
5555
} from './logger-and-telemetry';
5656
import { useCompassWebLoggerAndTelemetry } from './logger-and-telemetry';
5757
import { type TelemetryServiceOptions } from '@mongodb-js/compass-telemetry';
58-
import { WorkspaceTab as WelcomeWorkspaceTab } from '@mongodb-js/compass-welcome';
58+
import { WebWorkspaceTab as WelcomeWorkspaceTab } from '@mongodb-js/compass-welcome';
5959
import { useCompassWebPreferences } from './preferences';
6060

6161
const WithAtlasProviders: React.FC = ({ children }) => {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from 'react';
2+
import {
3+
screen,
4+
renderWithConnections,
5+
} from '@mongodb-js/testing-library-compass';
6+
import { expect } from 'chai';
7+
import DesktopWelcomeTab from './desktop-welcome-tab';
8+
9+
const renderDesktopWelcomeTab = (
10+
preferences: {
11+
enableCreatingNewConnections?: boolean;
12+
} = {}
13+
) => {
14+
renderWithConnections(<DesktopWelcomeTab />, {
15+
preferences,
16+
});
17+
};
18+
19+
describe('DesktopWelcomeTab', function () {
20+
it('renders with title', function () {
21+
renderDesktopWelcomeTab();
22+
expect(screen.getByText('Welcome to MongoDB Compass')).to.exist;
23+
});
24+
25+
it('does not render create cluster button when enableCreatingNewConnections is false', function () {
26+
renderDesktopWelcomeTab({ enableCreatingNewConnections: false });
27+
try {
28+
screen.getByTestId('add-new-connection-button');
29+
expect.fail('add-new-connection-button should not be rendered');
30+
} catch (e) {
31+
// noop
32+
}
33+
});
34+
35+
context('when enableCreatingNewConnections is true', function () {
36+
it('renders info text', function () {
37+
renderDesktopWelcomeTab({ enableCreatingNewConnections: true });
38+
expect(
39+
screen.getByText('To get started, connect to an existing server or')
40+
).to.exist;
41+
});
42+
43+
it('renders create cluster button', function () {
44+
renderDesktopWelcomeTab({ enableCreatingNewConnections: true });
45+
expect(screen.getByTestId('add-new-connection-button')).to.exist;
46+
});
47+
48+
it('renders atlas help section', function () {
49+
renderDesktopWelcomeTab({ enableCreatingNewConnections: true });
50+
expect(screen.getByTestId('welcome-tab-atlas-help-section')).to.exist;
51+
});
52+
});
53+
});
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import React from 'react';
2+
3+
import {
4+
Button,
5+
ButtonSize,
6+
ButtonVariant,
7+
Subtitle,
8+
H3,
9+
Body,
10+
Link,
11+
spacing,
12+
palette,
13+
css,
14+
cx,
15+
useDarkMode,
16+
Icon,
17+
} from '@mongodb-js/compass-components';
18+
import { useTelemetry } from '@mongodb-js/compass-telemetry/provider';
19+
import { useConnectionActions } from '@mongodb-js/compass-connections/provider';
20+
import { usePreference } from 'compass-preferences-model/provider';
21+
import { WelcomeTabImage } from './welcome-image';
22+
23+
const sectionContainerStyles = css({
24+
margin: 0,
25+
padding: spacing[4],
26+
paddingBottom: 0,
27+
maxWidth: '450px',
28+
borderRadius: spacing[200],
29+
});
30+
31+
const atlasContainerStyles = css({
32+
backgroundColor: palette.green.light3,
33+
border: `1px solid ${palette.green.light2}`,
34+
paddingBottom: spacing[600],
35+
});
36+
37+
const atlasContainerDarkModeStyles = css({
38+
backgroundColor: palette.green.dark3,
39+
borderColor: palette.green.dark2,
40+
});
41+
42+
const titleStyles = css({
43+
fontSize: '14px',
44+
});
45+
46+
const descriptionStyles = css({
47+
marginTop: spacing[2],
48+
});
49+
50+
const createClusterContainerStyles = css({
51+
marginTop: spacing[2],
52+
});
53+
54+
const createClusterButtonStyles = css({
55+
fontWeight: 'bold',
56+
});
57+
58+
const createClusterButtonLightModeStyles = css({
59+
background: palette.white,
60+
'&:hover': {
61+
background: palette.white,
62+
},
63+
'&:focus': {
64+
background: palette.white,
65+
},
66+
});
67+
68+
function AtlasHelpSection(): React.ReactElement {
69+
const track = useTelemetry();
70+
const darkMode = useDarkMode();
71+
72+
return (
73+
<div
74+
className={cx(
75+
sectionContainerStyles,
76+
atlasContainerStyles,
77+
darkMode && atlasContainerDarkModeStyles
78+
)}
79+
data-testid="welcome-tab-atlas-help-section"
80+
>
81+
<Subtitle className={titleStyles}>
82+
New to Compass and don&apos;t have a cluster?
83+
</Subtitle>
84+
<Body className={descriptionStyles}>
85+
If you don&apos;t already have a cluster, you can create one for free
86+
using{' '}
87+
<Link href="https://www.mongodb.com/atlas/database" target="_blank">
88+
MongoDB Atlas
89+
</Link>
90+
</Body>
91+
<div className={createClusterContainerStyles}>
92+
<Button
93+
data-testid="atlas-cta-link"
94+
className={cx(
95+
createClusterButtonStyles,
96+
!darkMode && createClusterButtonLightModeStyles
97+
)}
98+
onClick={() => track('Atlas Link Clicked', { screen: 'connect' })}
99+
variant={ButtonVariant.PrimaryOutline}
100+
href="https://www.mongodb.com/cloud/atlas/lp/try4?utm_source=compass&utm_medium=product&utm_content=v1"
101+
target="_blank"
102+
size={ButtonSize.Small}
103+
>
104+
CREATE FREE CLUSTER
105+
</Button>
106+
</div>
107+
</div>
108+
);
109+
}
110+
111+
const welcomeTabStyles = css({
112+
display: 'flex',
113+
alignItems: 'center',
114+
justifyContent: 'center',
115+
margin: '0 auto',
116+
gap: spacing[200],
117+
});
118+
119+
const firstConnectionBtnStyles = css({
120+
margin: `${spacing[400]}px 0`,
121+
});
122+
123+
export default function DesktopWelcomeTab() {
124+
const { createNewConnection } = useConnectionActions();
125+
const enableCreatingNewConnections = usePreference(
126+
'enableCreatingNewConnections'
127+
);
128+
129+
return (
130+
<div className={welcomeTabStyles}>
131+
<WelcomeTabImage />
132+
<div>
133+
<H3>Welcome to MongoDB Compass</H3>
134+
{enableCreatingNewConnections && (
135+
<>
136+
<Body>To get started, connect to an existing server or</Body>
137+
<Button
138+
className={firstConnectionBtnStyles}
139+
data-testid="add-new-connection-button"
140+
variant={ButtonVariant.Primary}
141+
leftGlyph={<Icon glyph="Plus" />}
142+
onClick={createNewConnection}
143+
>
144+
Add new connection
145+
</Button>
146+
<AtlasHelpSection />
147+
</>
148+
)}
149+
</div>
150+
</div>
151+
);
152+
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import WelcomeModal from './modal';
2-
import WelcomeTab from './welcome-tab';
2+
import DesktopWelcomeTab from './desktop-welcome-tab';
3+
import WebWelcomeTab from './web-welcome-tab';
34

4-
export { WelcomeModal, WelcomeTab };
5+
export { WelcomeModal, DesktopWelcomeTab, WebWelcomeTab };

packages/compass-welcome/src/components/modal.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from '@mongodb-js/compass-components';
1111
import { withPreferences } from 'compass-preferences-model/provider';
1212

13-
import WelcomeImage from './welcome-image';
13+
import { WelcomeModalImage } from './welcome-image';
1414

1515
const disclaimer = css({
1616
padding: `0 ${spacing[900]}px`,
@@ -69,7 +69,7 @@ export const WelcomeModal: React.FunctionComponent<WelcomeModalProps> = ({
6969
</div>
7070
) : undefined
7171
}
72-
graphic={<WelcomeImage width={156} height={209} />}
72+
graphic={<WelcomeModalImage width={156} height={209} />}
7373
linkText={''}
7474
darkMode={darkMode}
7575
>

0 commit comments

Comments
 (0)