Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1078,7 +1062,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 +1079,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
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';
import {
screen,
renderWithConnections,
} from '@mongodb-js/testing-library-compass';
import { expect } from 'chai';
import WebWelcomeTab from './web-welcome-tab';
import type { ConnectionInfo } from '@mongodb-js/compass-connections/provider';

const CONNECTION_ITEM = {
id: '1',
connectionOptions: { connectionString: 'mongodb://localhost:27017' },
};

const renderWebWelcomeTab = (connections: ConnectionInfo[] = []) => {
renderWithConnections(<WebWelcomeTab />, {
connections,
});
};

describe('WebWelcomeTab', function () {
it('renders with title', function () {
renderWebWelcomeTab();
expect(screen.getByText('Welcome! Explore')).to.exist;
});

context('with no connections', function () {
it('renders info text', function () {
renderWebWelcomeTab();
expect(
screen.getByText('To get started, create your first MongoDB Cluster.')
).to.exist;
});
it('renders with create cluster button', function () {
renderWebWelcomeTab();
expect(screen.getByTestId('add-new-atlas-cluster-button')).to.exist;
});
it('renders help text', function () {
renderWebWelcomeTab();
expect(screen.getByText('Need more help?')).to.exist;
expect(screen.getByText('View documentation')).to.exist;
});
});

context('with at least one connection', function () {
it('renders info text', function () {
renderWebWelcomeTab([CONNECTION_ITEM]);
expect(
screen.getByText('To get started, connect to an existing cluster.')
).to.exist;
});
it('does not render create cluster button', function () {
renderWebWelcomeTab([CONNECTION_ITEM]);
try {
screen.getByTestId('add-new-atlas-cluster-button');
expect.fail('add-new-atlas-cluster-button should not be rendered');
} catch (e) {
// noop
}
});
});
});
Loading
Loading