Skip to content

Commit b5940fc

Browse files
authored
feat: update atlas cluster states COMPASS-8228 (#6884)
* init * refactor * Update base-navigation-item.tsx * Update connections-store-redux.ts * Refactor * Update connection-storage.tsx * refactor * Update base-navigation-item.tsx * remove unused' * Revert "remove unused'" This reverts commit 2a14569. * comments * Update connections-store-redux.ts * Update connections-store-redux.ts * fix tests
1 parent bd6a3dd commit b5940fc

File tree

15 files changed

+229
-16
lines changed

15 files changed

+229
-16
lines changed

packages/compass-connections-navigation/src/base-navigation-item.tsx

+74
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,23 @@ import {
55
css,
66
ItemActionControls,
77
cx,
8+
Badge,
9+
BadgeVariant,
10+
Tooltip,
11+
useDarkMode,
12+
Body,
813
} from '@mongodb-js/compass-components';
914
import { type Actions, ROW_HEIGHT } from './constants';
1015
import { ExpandButton } from './tree-item';
1116
import { type NavigationItemActions } from './item-actions';
17+
import type {
18+
ConnectedConnectionTreeItem,
19+
NotConnectedConnectionTreeItem,
20+
SidebarTreeItem,
21+
} from './tree-data';
1222

1323
type NavigationBaseItemProps = {
24+
item: SidebarTreeItem;
1425
name: string;
1526
isActive: boolean;
1627
isExpandVisible: boolean;
@@ -86,7 +97,66 @@ const actionControlsWrapperStyles = css({
8697
gap: spacing[100],
8798
});
8899

100+
const ClusterStateBadge: React.FunctionComponent<{
101+
state: string;
102+
}> = ({ state }) => {
103+
const badgeVariant =
104+
state === 'CREATING'
105+
? BadgeVariant.Blue
106+
: state === 'DELETED'
107+
? BadgeVariant.Red
108+
: BadgeVariant.LightGray;
109+
const badgeText =
110+
state === 'DELETING'
111+
? 'TERMINATING'
112+
: state === 'DELETED'
113+
? 'TERMINATED'
114+
: state;
115+
116+
return (
117+
<Badge variant={badgeVariant} data-testid="navigation-item-state-badge">
118+
{badgeText}
119+
</Badge>
120+
);
121+
};
122+
123+
const ClusterStateBadgeWithTooltip: React.FunctionComponent<{
124+
item: ConnectedConnectionTreeItem | NotConnectedConnectionTreeItem;
125+
}> = ({ item }) => {
126+
const isDarkMode = useDarkMode();
127+
128+
const atlasClusterState = item.connectionInfo.atlasMetadata?.clusterState;
129+
if (atlasClusterState === 'PAUSED') {
130+
return (
131+
<Tooltip
132+
enabled={true}
133+
darkMode={isDarkMode}
134+
trigger={({
135+
children: tooltipChildren,
136+
...tooltipTriggerProps
137+
}: React.HTMLProps<HTMLDivElement>) => (
138+
<div {...tooltipTriggerProps}>
139+
<ClusterStateBadge state={atlasClusterState} />
140+
{tooltipChildren}
141+
</div>
142+
)}
143+
>
144+
<Body>Unpause your cluster to connect to it</Body>
145+
</Tooltip>
146+
);
147+
} else if (
148+
atlasClusterState === 'DELETING' ||
149+
atlasClusterState === 'CREATING' ||
150+
atlasClusterState === 'DELETED'
151+
) {
152+
return <ClusterStateBadge state={atlasClusterState} />;
153+
}
154+
155+
return null;
156+
};
157+
89158
export const NavigationBaseItem: React.FC<NavigationBaseItemProps> = ({
159+
item,
90160
isActive,
91161
actionProps,
92162
name,
@@ -102,6 +172,7 @@ export const NavigationBaseItem: React.FC<NavigationBaseItemProps> = ({
102172
children,
103173
}) => {
104174
const [hoverProps, isHovered] = useHoverState();
175+
105176
return (
106177
<div
107178
data-testid="base-navigation-item"
@@ -127,6 +198,9 @@ export const NavigationBaseItem: React.FC<NavigationBaseItemProps> = ({
127198
{icon}
128199
<span title={name}>{name}</span>
129200
</div>
201+
{item.type === 'connection' && (
202+
<ClusterStateBadgeWithTooltip item={item} />
203+
)}
130204
<div className={actionControlsWrapperStyles}>
131205
<ItemActionControls
132206
menuClassName={menuStyles}

packages/compass-connections-navigation/src/connections-navigation-tree.tsx

+26-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useCallback, useMemo } from 'react';
22
import AutoSizer from 'react-virtualized-auto-sizer';
3-
import { getVirtualTreeItems } from './tree-data';
3+
import { getConnectionId, getVirtualTreeItems } from './tree-data';
44
import { ROW_HEIGHT } from './constants';
55
import type { Actions } from './constants';
66
import { VirtualTree } from './virtual-list/virtual-list';
@@ -18,6 +18,7 @@ import {
1818
spacing,
1919
useId,
2020
} from '@mongodb-js/compass-components';
21+
import { useConnectable } from '@mongodb-js/compass-connections/provider';
2122
import type { WorkspaceTab } from '@mongodb-js/compass-workspaces';
2223
import { usePreference } from 'compass-preferences-model/provider';
2324
import type { NavigationItemActions } from './item-actions';
@@ -55,8 +56,10 @@ const ConnectionsNavigationTree: React.FunctionComponent<
5556
const isRenameCollectionEnabled = usePreference(
5657
'enableRenameCollectionModal'
5758
);
59+
const showDisabledConnections = !!usePreference('showDisabledConnections');
5860

5961
const id = useId();
62+
const getConnectable = useConnectable();
6063

6164
const treeData = useMemo(() => {
6265
return getVirtualTreeItems({
@@ -69,6 +72,13 @@ const ConnectionsNavigationTree: React.FunctionComponent<
6972

7073
const onDefaultAction: OnDefaultAction<SidebarActionableItem> = useCallback(
7174
(item, evt) => {
75+
if (showDisabledConnections) {
76+
const connectionId = getConnectionId(item);
77+
if (!getConnectable(connectionId)) {
78+
return;
79+
}
80+
}
81+
7282
if (item.type === 'connection') {
7383
if (item.connectionStatus === 'connected') {
7484
onItemAction(item, 'select-connection');
@@ -83,7 +93,7 @@ const ConnectionsNavigationTree: React.FunctionComponent<
8393
}
8494
}
8595
},
86-
[onItemAction]
96+
[onItemAction, getConnectable, showDisabledConnections]
8797
);
8898

8999
const activeItemId = useMemo(() => {
@@ -144,6 +154,14 @@ const ConnectionsNavigationTree: React.FunctionComponent<
144154

145155
const getItemActionsAndConfig = useCallback(
146156
(item: SidebarTreeItem) => {
157+
if (showDisabledConnections) {
158+
const connectionId = getConnectionId(item);
159+
if (!getConnectable(connectionId)) {
160+
return {
161+
actions: [],
162+
};
163+
}
164+
}
147165
switch (item.type) {
148166
case 'placeholder':
149167
return {
@@ -192,7 +210,12 @@ const ConnectionsNavigationTree: React.FunctionComponent<
192210
};
193211
}
194212
},
195-
[isRenameCollectionEnabled, getCollapseAfterForConnectedItem]
213+
[
214+
isRenameCollectionEnabled,
215+
getCollapseAfterForConnectedItem,
216+
getConnectable,
217+
showDisabledConnections,
218+
]
196219
);
197220

198221
const isTestEnv = process.env.NODE_ENV === 'test';

packages/compass-connections-navigation/src/navigation-item-icon.tsx

+16
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,22 @@ export const NavigationItemIcon = ({ item }: { item: SidebarTreeItem }) => {
6363
return <Icon glyph="TimeSeries" />;
6464
}
6565
if (item.type === 'connection') {
66+
const atlasClusterState = item.connectionInfo.atlasMetadata?.clusterState;
67+
if (atlasClusterState === 'DELETING' || atlasClusterState === 'CREATING') {
68+
return (
69+
<WithStatusMarker status={'disconnected'}>
70+
<Icon glyph="Refresh" />
71+
</WithStatusMarker>
72+
);
73+
}
74+
if (atlasClusterState === 'PAUSED' || atlasClusterState === 'DELETED') {
75+
return (
76+
<WithStatusMarker status={'disconnected'}>
77+
<ServerIcon />
78+
</WithStatusMarker>
79+
);
80+
}
81+
6682
const isFavorite = item.connectionInfo.savedConnectionType === 'favorite';
6783
if (isFavorite) {
6884
return (

packages/compass-connections-navigation/src/navigation-item.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ export function NavigationItem({
212212
<PlaceholderItem level={item.level} />
213213
) : (
214214
<NavigationBaseItem
215+
item={item}
215216
isActive={isActive}
216217
isFocused={isFocused}
217218
isExpanded={!!item.isExpanded}

packages/compass-connections-navigation/src/styled-navigation-item.tsx

+13-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import {
44
DefaultColorCode,
55
} from '@mongodb-js/connection-form';
66
import { palette, useDarkMode } from '@mongodb-js/compass-components';
7-
import type { SidebarTreeItem } from './tree-data';
7+
import { getConnectionId, type SidebarTreeItem } from './tree-data';
8+
import { useConnectable } from '@mongodb-js/compass-connections/provider';
9+
import { usePreference } from 'compass-preferences-model/provider';
810

911
type AcceptedStyles = {
1012
'--item-bg-color'?: string;
@@ -21,6 +23,7 @@ export default function StyledNavigationItem({
2123
item: SidebarTreeItem;
2224
children: React.ReactChild;
2325
}): React.ReactElement {
26+
const showDisabledConnections = !!usePreference('showDisabledConnections');
2427
const isDarkMode = useDarkMode();
2528
const { connectionColorToHex, connectionColorToHexActive } =
2629
useConnectionColor();
@@ -29,9 +32,13 @@ export default function StyledNavigationItem({
2932
() => (isDarkMode ? palette.gray.light1 : palette.gray.dark1),
3033
[isDarkMode]
3134
);
35+
const getConnectable = useConnectable();
3236

3337
const style: React.CSSProperties & AcceptedStyles = useMemo(() => {
3438
const style: AcceptedStyles = {};
39+
const connectionId = getConnectionId(item);
40+
const isConnectable =
41+
!showDisabledConnections || getConnectable(connectionId);
3542
const isDisconnectedConnection =
3643
item.type === 'connection' && item.connectionStatus !== 'connected';
3744
const isNonExistentNamespace =
@@ -44,19 +51,21 @@ export default function StyledNavigationItem({
4451
style['--item-bg-color-active'] = connectionColorToHexActive(colorCode);
4552
}
4653

47-
if (isDisconnectedConnection || isNonExistentNamespace) {
54+
if (isDisconnectedConnection || isNonExistentNamespace || !isConnectable) {
4855
style['--item-color'] = inactiveColor;
4956
}
5057

51-
// For a non-existent namespace, even if its active, we show it as inactive
52-
if (isNonExistentNamespace) {
58+
// We always show these as inactive
59+
if (isNonExistentNamespace || !isConnectable) {
5360
style['--item-color-active'] = inactiveColor;
5461
}
5562
return style;
5663
}, [
5764
inactiveColor,
5865
item,
5966
colorCode,
67+
getConnectable,
68+
showDisabledConnections,
6069
connectionColorToHex,
6170
connectionColorToHexActive,
6271
]);

packages/compass-connections-navigation/src/tree-data.ts

+10
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,16 @@ export type SidebarActionableItem =
124124

125125
export type SidebarTreeItem = PlaceholderTreeItem | SidebarActionableItem;
126126

127+
export function getConnectionId(item: SidebarTreeItem): string {
128+
if (item.type === 'placeholder') {
129+
return '';
130+
} else if (item.type === 'connection') {
131+
return item.connectionInfo.id;
132+
} else {
133+
return item.connectionId;
134+
}
135+
}
136+
127137
const notConnectedConnectionToItems = ({
128138
connection: { name, connectionInfo, connectionStatus },
129139
connectionIndex,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { useStore } from '../stores/store-context';
2+
import { useCallback } from 'react';
3+
import { connectable } from '../utils/connection-supports';
4+
5+
export function useConnectable(): (connectionId: string) => boolean {
6+
const store = useStore();
7+
const getConnectable = useCallback(
8+
(connectionId: string) => {
9+
const conn = store.getState().connections.byId[connectionId];
10+
if (!conn) {
11+
return false;
12+
}
13+
14+
return connectable(conn.info);
15+
},
16+
[store]
17+
);
18+
19+
return getConnectable;
20+
}

packages/compass-connections/src/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
ConnectionActionsProvider,
2424
} from './stores/store-context';
2525
export type { ConnectionFeature } from './utils/connection-supports';
26-
export { connectionSupports } from './utils/connection-supports';
26+
export { connectionSupports, connectable } from './utils/connection-supports';
2727

2828
const ConnectionsComponent: React.FunctionComponent<{
2929
/**

packages/compass-connections/src/provider.ts

+2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ export type { ConnectionsService } from './stores/store-context';
6969

7070
export { useConnectionSupports } from './hooks/use-connection-supports';
7171

72+
export { useConnectable } from './hooks/use-connectable';
73+
7274
const ConnectionStatus = {
7375
/**
7476
* @deprecated use a string literal directly

0 commit comments

Comments
 (0)