Skip to content

Commit

Permalink
feat(compass-web): add welcome page COMPASS-8657 (#6593)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
mabaasit authored Jan 9, 2025
1 parent dce7b3b commit e3c2427
Show file tree
Hide file tree
Showing 12 changed files with 468 additions and 331 deletions.
66 changes: 9 additions & 57 deletions packages/compass-connections/src/stores/connections-store-redux.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,11 +196,7 @@ export type State = {
}
>;

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

// State related to connection favorite fields editing modal form (right now
Expand Down Expand Up @@ -487,23 +483,15 @@ export function createDefaultConnectionState(
};
}

// For single connection mode we always have to start with the initial empty
// connection in the state already. This can be removed when single connection
// mode doesn't exist anymore
const INITIAL_CONNECTION_STATE = createDefaultConnectionState();
INITIAL_CONNECTION_STATE.isBeingCreated = true;

const INITIAL_STATE: State = {
connections: {
ids: [INITIAL_CONNECTION_STATE.info.id],
byId: {
[INITIAL_CONNECTION_STATE.info.id]: INITIAL_CONNECTION_STATE,
},
ids: [],
byId: {},
status: 'initial',
error: null,
},
oidcDeviceAuthInfo: {},
editingConnectionInfoId: INITIAL_CONNECTION_STATE.info.id,
editingConnectionInfoId: null,
isEditingConnectionInfoModalOpen: false,
editingConnectionFavoriteInfoId: null,
isEditingConnectionFavoriteInfoModalOpen: false,
Expand All @@ -513,13 +501,9 @@ export function getInitialConnectionsStateForConnectionInfos(
connectionInfos: ConnectionInfo[] = []
): State['connections'] {
const byId = Object.fromEntries<ConnectionState>(
[
[INITIAL_CONNECTION_STATE.info.id, INITIAL_CONNECTION_STATE] as const,
].concat(
connectionInfos.map((info) => {
return [info.id, createDefaultConnectionState(info)];
})
)
connectionInfos.map((info) => {
return [info.id, createDefaultConnectionState(info)];
})
);
return {
byId,
Expand Down Expand Up @@ -985,28 +969,10 @@ const reducer: Reducer<State, Action> = (state = INITIAL_STATE, action) => {
const newConnection = createDefaultConnectionState();
newConnection.isBeingCreated = true;

let newConnectionsState = state.connections;

// Only relevant for single connections mode: if we're currently editing
// "new connection", clean it up from state before creating state for a new
// one
if (
state.editingConnectionInfoId &&
state.connections.byId[state.editingConnectionInfoId].isBeingCreated
) {
newConnectionsState = {
...newConnectionsState,
byId: {
...newConnectionsState.byId,
},
};
delete newConnectionsState.byId[state.editingConnectionInfoId];
}

return {
...state,
connections: mergeConnectionStateById(
newConnectionsState,
state.connections,
newConnection.info.id,
newConnection
),
Expand Down Expand Up @@ -1078,7 +1044,7 @@ const reducer: Reducer<State, Action> = (state = INITIAL_STATE, action) => {
}

let connections = state.connections;
let editingConnectionInfoId = state.editingConnectionInfoId;
const editingConnectionInfoId = state.editingConnectionInfoId;

// In cases where connection was never saved or used before, we remove it
// from the connections state
Expand All @@ -1095,20 +1061,6 @@ const reducer: Reducer<State, Action> = (state = INITIAL_STATE, action) => {
byId: newConnectionsById,
ids: newIds,
};

// Special case for single connection: after removing connection, we
// automatically create a new connection and will "select" it for editing.
// Can go away when single connection mode is removed
if (state.editingConnectionInfoId === action.connectionId) {
const newDefaultConnection = createDefaultConnectionState();
newDefaultConnection.isBeingCreated = true;
connections = mergeConnectionStateById(
connections,
newDefaultConnection.info.id,
newDefaultConnection
);
editingConnectionInfoId = newDefaultConnection.info.id;
}
}

return {
Expand Down
2 changes: 1 addition & 1 deletion packages/compass-web/src/entrypoint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ import type {
} from './logger-and-telemetry';
import { useCompassWebLoggerAndTelemetry } from './logger-and-telemetry';
import { type TelemetryServiceOptions } from '@mongodb-js/compass-telemetry';
import { WorkspaceTab as WelcomeWorkspaceTab } from '@mongodb-js/compass-welcome';
import { WebWorkspaceTab as WelcomeWorkspaceTab } from '@mongodb-js/compass-welcome';
import { useCompassWebPreferences } from './preferences';

const WithAtlasProviders: React.FC = ({ children }) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';
import {
screen,
renderWithConnections,
} from '@mongodb-js/testing-library-compass';
import { expect } from 'chai';
import DesktopWelcomeTab from './desktop-welcome-tab';

const renderDesktopWelcomeTab = (
preferences: {
enableCreatingNewConnections?: boolean;
} = {}
) => {
renderWithConnections(<DesktopWelcomeTab />, {
preferences,
});
};

describe('DesktopWelcomeTab', function () {
it('renders with title', function () {
renderDesktopWelcomeTab();
expect(screen.getByText('Welcome to MongoDB Compass')).to.exist;
});

it('does not render create cluster button when enableCreatingNewConnections is false', function () {
renderDesktopWelcomeTab({ enableCreatingNewConnections: false });
try {
screen.getByTestId('add-new-connection-button');
expect.fail('add-new-connection-button should not be rendered');
} catch (e) {
// noop
}
});

context('when enableCreatingNewConnections is true', function () {
it('renders info text', function () {
renderDesktopWelcomeTab({ enableCreatingNewConnections: true });
expect(
screen.getByText('To get started, connect to an existing server or')
).to.exist;
});

it('renders create cluster button', function () {
renderDesktopWelcomeTab({ enableCreatingNewConnections: true });
expect(screen.getByTestId('add-new-connection-button')).to.exist;
});

it('renders atlas help section', function () {
renderDesktopWelcomeTab({ enableCreatingNewConnections: true });
expect(screen.getByTestId('welcome-tab-atlas-help-section')).to.exist;
});
});
});
152 changes: 152 additions & 0 deletions packages/compass-welcome/src/components/desktop-welcome-tab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import React from 'react';

import {
Button,
ButtonSize,
ButtonVariant,
Subtitle,
H3,
Body,
Link,
spacing,
palette,
css,
cx,
useDarkMode,
Icon,
} from '@mongodb-js/compass-components';
import { useTelemetry } from '@mongodb-js/compass-telemetry/provider';
import { useConnectionActions } from '@mongodb-js/compass-connections/provider';
import { usePreference } from 'compass-preferences-model/provider';
import { WelcomeTabImage } from './welcome-image';

const sectionContainerStyles = css({
margin: 0,
padding: spacing[4],
paddingBottom: 0,
maxWidth: '450px',
borderRadius: spacing[200],
});

const atlasContainerStyles = css({
backgroundColor: palette.green.light3,
border: `1px solid ${palette.green.light2}`,
paddingBottom: spacing[600],
});

const atlasContainerDarkModeStyles = css({
backgroundColor: palette.green.dark3,
borderColor: palette.green.dark2,
});

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,
},
});

function AtlasHelpSection(): React.ReactElement {
const track = useTelemetry();
const darkMode = useDarkMode();

return (
<div
className={cx(
sectionContainerStyles,
atlasContainerStyles,
darkMode && atlasContainerDarkModeStyles
)}
data-testid="welcome-tab-atlas-help-section"
>
<Subtitle className={titleStyles}>
New to Compass and don&apos;t have a cluster?
</Subtitle>
<Body className={descriptionStyles}>
If you don&apos;t already have a cluster, you can create one for free
using{' '}
<Link href="https://www.mongodb.com/atlas/database" target="_blank">
MongoDB Atlas
</Link>
</Body>
<div className={createClusterContainerStyles}>
<Button
data-testid="atlas-cta-link"
className={cx(
createClusterButtonStyles,
!darkMode && createClusterButtonLightModeStyles
)}
onClick={() => track('Atlas Link Clicked', { screen: 'connect' })}
variant={ButtonVariant.PrimaryOutline}
href="https://www.mongodb.com/cloud/atlas/lp/try4?utm_source=compass&utm_medium=product&utm_content=v1"
target="_blank"
size={ButtonSize.Small}
>
CREATE FREE CLUSTER
</Button>
</div>
</div>
);
}

const welcomeTabStyles = css({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
margin: '0 auto',
gap: spacing[200],
});

const firstConnectionBtnStyles = css({
margin: `${spacing[400]}px 0`,
});

export default function DesktopWelcomeTab() {
const { createNewConnection } = useConnectionActions();
const enableCreatingNewConnections = usePreference(
'enableCreatingNewConnections'
);

return (
<div className={welcomeTabStyles}>
<WelcomeTabImage />
<div>
<H3>Welcome to MongoDB Compass</H3>
{enableCreatingNewConnections && (
<>
<Body>To get started, connect to an existing server or</Body>
<Button
className={firstConnectionBtnStyles}
data-testid="add-new-connection-button"
variant={ButtonVariant.Primary}
leftGlyph={<Icon glyph="Plus" />}
onClick={createNewConnection}
>
Add new connection
</Button>
<AtlasHelpSection />
</>
)}
</div>
</div>
);
}
5 changes: 3 additions & 2 deletions packages/compass-welcome/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import WelcomeModal from './modal';
import WelcomeTab from './welcome-tab';
import DesktopWelcomeTab from './desktop-welcome-tab';
import WebWelcomeTab from './web-welcome-tab';

export { WelcomeModal, WelcomeTab };
export { WelcomeModal, DesktopWelcomeTab, WebWelcomeTab };
4 changes: 2 additions & 2 deletions packages/compass-welcome/src/components/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from '@mongodb-js/compass-components';
import { withPreferences } from 'compass-preferences-model/provider';

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

const disclaimer = css({
padding: `0 ${spacing[900]}px`,
Expand Down Expand Up @@ -69,7 +69,7 @@ export const WelcomeModal: React.FunctionComponent<WelcomeModalProps> = ({
</div>
) : undefined
}
graphic={<WelcomeImage width={156} height={209} />}
graphic={<WelcomeModalImage width={156} height={209} />}
linkText={''}
darkMode={darkMode}
>
Expand Down
Loading

0 comments on commit e3c2427

Please sign in to comment.