Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,23 @@ import {
css,
ItemActionControls,
cx,
Badge,
BadgeVariant,
Tooltip,
useDarkMode,
Body,
} from '@mongodb-js/compass-components';
import { type Actions, ROW_HEIGHT } from './constants';
import { ExpandButton } from './tree-item';
import { type NavigationItemActions } from './item-actions';
import type {
ConnectedConnectionTreeItem,
NotConnectedConnectionTreeItem,
SidebarTreeItem,
} from './tree-data';

type NavigationBaseItemProps = {
item: SidebarTreeItem;
name: string;
isActive: boolean;
isExpandVisible: boolean;
Expand Down Expand Up @@ -86,7 +97,66 @@ const actionControlsWrapperStyles = css({
gap: spacing[100],
});

const ClusterStateBadge: React.FunctionComponent<{
state: string;
}> = ({ state }) => {
const badgeVariant =
state === 'CREATING'
? BadgeVariant.Blue
: state === 'DELETED'
? BadgeVariant.Red
: BadgeVariant.LightGray;
const badgeText =
state === 'DELETING'
? 'TERMINATING'
: state === 'DELETED'
? 'TERMINATED'
: state;

return (
<Badge variant={badgeVariant} data-testid="navigation-item-state-badge">
{badgeText}
</Badge>
);
};

const ClusterStateBadgeWithTooltip: React.FunctionComponent<{
item: ConnectedConnectionTreeItem | NotConnectedConnectionTreeItem;
}> = ({ item }) => {
const isDarkMode = useDarkMode();

const atlasClusterState = item.connectionInfo.atlasMetadata?.clusterState;
if (atlasClusterState === 'PAUSED') {
return (
<Tooltip
enabled={true}
darkMode={isDarkMode}
trigger={({
children: tooltipChildren,
...tooltipTriggerProps
}: React.HTMLProps<HTMLDivElement>) => (
<div {...tooltipTriggerProps}>
<ClusterStateBadge state={atlasClusterState} />
{tooltipChildren}
</div>
)}
>
<Body>Unpause your cluster to connect to it</Body>
</Tooltip>
);
} else if (
atlasClusterState === 'DELETING' ||
atlasClusterState === 'CREATING' ||
atlasClusterState === 'DELETED'
) {
return <ClusterStateBadge state={atlasClusterState} />;
}

return null;
};

export const NavigationBaseItem: React.FC<NavigationBaseItemProps> = ({
item,
isActive,
actionProps,
name,
Expand All @@ -102,6 +172,7 @@ export const NavigationBaseItem: React.FC<NavigationBaseItemProps> = ({
children,
}) => {
const [hoverProps, isHovered] = useHoverState();

return (
<div
data-testid="base-navigation-item"
Expand All @@ -127,6 +198,9 @@ export const NavigationBaseItem: React.FC<NavigationBaseItemProps> = ({
{icon}
<span title={name}>{name}</span>
</div>
{item.type === 'connection' && (
<ClusterStateBadgeWithTooltip item={item} />
)}
<div className={actionControlsWrapperStyles}>
<ItemActionControls
menuClassName={menuStyles}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback, useMemo } from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
import { getVirtualTreeItems } from './tree-data';
import { getConnectionId, getVirtualTreeItems } from './tree-data';
import { ROW_HEIGHT } from './constants';
import type { Actions } from './constants';
import { VirtualTree } from './virtual-list/virtual-list';
Expand All @@ -18,6 +18,7 @@ import {
spacing,
useId,
} from '@mongodb-js/compass-components';
import { useConnectable } from '@mongodb-js/compass-connections/provider';
import type { WorkspaceTab } from '@mongodb-js/compass-workspaces';
import { usePreference } from 'compass-preferences-model/provider';
import type { NavigationItemActions } from './item-actions';
Expand Down Expand Up @@ -55,8 +56,10 @@ const ConnectionsNavigationTree: React.FunctionComponent<
const isRenameCollectionEnabled = usePreference(
'enableRenameCollectionModal'
);
const showDisabledConnections = !!usePreference('showDisabledConnections');

const id = useId();
const getConnectable = useConnectable();

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

const onDefaultAction: OnDefaultAction<SidebarActionableItem> = useCallback(
(item, evt) => {
if (showDisabledConnections) {
const connectionId = getConnectionId(item);
if (!getConnectable(connectionId)) {
return;
}
}

if (item.type === 'connection') {
if (item.connectionStatus === 'connected') {
onItemAction(item, 'select-connection');
Expand All @@ -83,7 +93,7 @@ const ConnectionsNavigationTree: React.FunctionComponent<
}
}
},
[onItemAction]
[onItemAction, getConnectable, showDisabledConnections]
);

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

const getItemActionsAndConfig = useCallback(
(item: SidebarTreeItem) => {
if (showDisabledConnections) {
const connectionId = getConnectionId(item);
if (!getConnectable(connectionId)) {
return {
actions: [],
};
}
}
switch (item.type) {
case 'placeholder':
return {
Expand Down Expand Up @@ -192,7 +210,12 @@ const ConnectionsNavigationTree: React.FunctionComponent<
};
}
},
[isRenameCollectionEnabled, getCollapseAfterForConnectedItem]
[
isRenameCollectionEnabled,
getCollapseAfterForConnectedItem,
getConnectable,
showDisabledConnections,
]
);

const isTestEnv = process.env.NODE_ENV === 'test';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,22 @@ export const NavigationItemIcon = ({ item }: { item: SidebarTreeItem }) => {
return <Icon glyph="TimeSeries" />;
}
if (item.type === 'connection') {
const atlasClusterState = item.connectionInfo.atlasMetadata?.clusterState;
if (atlasClusterState === 'DELETING' || atlasClusterState === 'CREATING') {
return (
<WithStatusMarker status={'disconnected'}>
<Icon glyph="Refresh" />
</WithStatusMarker>
);
}
if (atlasClusterState === 'PAUSED' || atlasClusterState === 'DELETED') {
return (
<WithStatusMarker status={'disconnected'}>
<ServerIcon />
</WithStatusMarker>
);
}

const isFavorite = item.connectionInfo.savedConnectionType === 'favorite';
if (isFavorite) {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ export function NavigationItem({
<PlaceholderItem level={item.level} />
) : (
<NavigationBaseItem
item={item}
isActive={isActive}
isFocused={isFocused}
isExpanded={!!item.isExpanded}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
DefaultColorCode,
} from '@mongodb-js/connection-form';
import { palette, useDarkMode } from '@mongodb-js/compass-components';
import type { SidebarTreeItem } from './tree-data';
import { getConnectionId, type SidebarTreeItem } from './tree-data';
import { useConnectable } from '@mongodb-js/compass-connections/provider';
import { usePreference } from 'compass-preferences-model/provider';

type AcceptedStyles = {
'--item-bg-color'?: string;
Expand All @@ -21,6 +23,7 @@ export default function StyledNavigationItem({
item: SidebarTreeItem;
children: React.ReactChild;
}): React.ReactElement {
const showDisabledConnections = !!usePreference('showDisabledConnections');
const isDarkMode = useDarkMode();
const { connectionColorToHex, connectionColorToHexActive } =
useConnectionColor();
Expand All @@ -29,9 +32,13 @@ export default function StyledNavigationItem({
() => (isDarkMode ? palette.gray.light1 : palette.gray.dark1),
[isDarkMode]
);
const getConnectable = useConnectable();

const style: React.CSSProperties & AcceptedStyles = useMemo(() => {
const style: AcceptedStyles = {};
const connectionId = getConnectionId(item);
const isConnectable =
!showDisabledConnections || getConnectable(connectionId);
const isDisconnectedConnection =
item.type === 'connection' && item.connectionStatus !== 'connected';
const isNonExistentNamespace =
Expand All @@ -44,19 +51,21 @@ export default function StyledNavigationItem({
style['--item-bg-color-active'] = connectionColorToHexActive(colorCode);
}

if (isDisconnectedConnection || isNonExistentNamespace) {
if (isDisconnectedConnection || isNonExistentNamespace || !isConnectable) {
style['--item-color'] = inactiveColor;
}

// For a non-existent namespace, even if its active, we show it as inactive
if (isNonExistentNamespace) {
// We always show these as inactive
if (isNonExistentNamespace || !isConnectable) {
style['--item-color-active'] = inactiveColor;
}
return style;
}, [
inactiveColor,
item,
colorCode,
getConnectable,
showDisabledConnections,
connectionColorToHex,
connectionColorToHexActive,
]);
Expand Down
10 changes: 10 additions & 0 deletions packages/compass-connections-navigation/src/tree-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,16 @@ export type SidebarActionableItem =

export type SidebarTreeItem = PlaceholderTreeItem | SidebarActionableItem;

export function getConnectionId(item: SidebarTreeItem): string {
if (item.type === 'placeholder') {
return '';
} else if (item.type === 'connection') {
return item.connectionInfo.id;
} else {
return item.connectionId;
}
}

const notConnectedConnectionToItems = ({
connection: { name, connectionInfo, connectionStatus },
connectionIndex,
Expand Down
20 changes: 20 additions & 0 deletions packages/compass-connections/src/hooks/use-connectable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useStore } from '../stores/store-context';
import { useCallback } from 'react';
import { connectable } from '../utils/connection-supports';

export function useConnectable(): (connectionId: string) => boolean {
const store = useStore();
const getConnectable = useCallback(
(connectionId: string) => {
const conn = store.getState().connections.byId[connectionId];
if (!conn) {
return false;
}

return connectable(conn.info);
},
[store]
);

return getConnectable;
}
2 changes: 1 addition & 1 deletion packages/compass-connections/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
ConnectionActionsProvider,
} from './stores/store-context';
export type { ConnectionFeature } from './utils/connection-supports';
export { connectionSupports } from './utils/connection-supports';
export { connectionSupports, connectable } from './utils/connection-supports';

const ConnectionsComponent: React.FunctionComponent<{
/**
Expand Down
2 changes: 2 additions & 0 deletions packages/compass-connections/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export type { ConnectionsService } from './stores/store-context';

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

export { useConnectable } from './hooks/use-connectable';

const ConnectionStatus = {
/**
* @deprecated use a string literal directly
Expand Down
Loading
Loading