diff --git a/web/packages/teleterm/src/ui/TopBar/Connections/Connections.story.tsx b/web/packages/teleterm/src/ui/TopBar/Connections/Connections.story.tsx index 0ac27b0e8372a..2e639260c77c0 100644 --- a/web/packages/teleterm/src/ui/TopBar/Connections/Connections.story.tsx +++ b/web/packages/teleterm/src/ui/TopBar/Connections/Connections.story.tsx @@ -21,6 +21,8 @@ import AppContextProvider from 'teleterm/ui/appContextProvider'; import { MockAppContext } from 'teleterm/ui/fixtures/mocks'; import { ExtendedTrackedConnection } from 'teleterm/ui/services/connectionTracker'; +import { makeRootCluster } from 'teleterm/services/tshd/testHelpers'; + import { Connections } from './Connections'; export default { @@ -34,6 +36,8 @@ export default { ], }; +const rootClusterUri = '/clusters/foo'; + export function Story() { const appContext = new MockAppContext(); prepareAppContext(appContext); @@ -45,6 +49,41 @@ export function Story() { ); } +export function MultipleClusters() { + const appContext = new MockAppContext(); + prepareAppContext(appContext); + appContext.clustersService.setState(draft => { + const rootCluster1 = makeRootCluster({ + uri: rootClusterUri, + name: 'teleport.example.sh', + }); + const rootCluster2 = makeRootCluster({ + uri: '/clusters/bar', + name: 'bar.example.com', + }); + draft.clusters.set(rootCluster1.uri, rootCluster1); + draft.clusters.set(rootCluster2.uri, rootCluster2); + }); + appContext.connectionTracker.getConnections = () => [ + ...makeConnections(), + { + connected: true, + kind: 'connection.server' as const, + title: 'runner-prod', + id: 'ed23ded1', + serverUri: '/clusters/bar/servers/ed23ded1', + login: 'alice', + clusterName: 'bar.example.com', + }, + ]; + + return ( + + + + ); +} + export function WithScroll() { const appContext = new MockAppContext(); prepareAppContext(appContext); diff --git a/web/packages/teleterm/src/ui/TopBar/Connections/ConnectionsFilterableList/ConnectionItem.tsx b/web/packages/teleterm/src/ui/TopBar/Connections/ConnectionsFilterableList/ConnectionItem.tsx index d0b0b3bcec9f6..81d95a98d620f 100644 --- a/web/packages/teleterm/src/ui/TopBar/Connections/ConnectionsFilterableList/ConnectionItem.tsx +++ b/web/packages/teleterm/src/ui/TopBar/Connections/ConnectionsFilterableList/ConnectionItem.tsx @@ -26,18 +26,14 @@ import { useKeyboardArrowsNavigation } from 'teleterm/ui/components/KeyboardArro import { ConnectionStatusIndicator } from './ConnectionStatusIndicator'; -interface ConnectionItemProps { +export function ConnectionItem(props: { index: number; item: ExtendedTrackedConnection; - + showClusterName: boolean; onActivate(): void; - onRemove(): void; - onDisconnect(): void; -} - -export function ConnectionItem(props: ConnectionItemProps) { +}) { const offline = !props.item.connected; const { isActive, scrollIntoViewIfActive } = useKeyboardArrowsNavigation({ index: props.index, @@ -69,8 +65,13 @@ export function ConnectionItem(props: ConnectionItemProps) { onClick={props.onActivate} isActive={isActive} ref={ref} + $showClusterName={props.showClusterName} css={` - padding: 6px 8px; + padding: ${props => props.theme.space[1]}px + ${props => props.theme.space[2]}px; + // Space out items more if there are two lines of text to show inside a single item. + margin-block-start: ${props => + props.$showClusterName ? props.theme.space[1] : 0}px; height: unset; `} > @@ -98,6 +99,7 @@ export function ConnectionItem(props: ConnectionItemProps) { color="text.main" title={props.item.title} css={` + // Needed to condense a single item when the cluster name is displayed. line-height: 16px; `} > @@ -121,13 +123,16 @@ export function ConnectionItem(props: ConnectionItemProps) { {props.item.title} - - {props.item.clusterName} - + + {props.showClusterName && ( + + {props.item.clusterName} + + )} i.clusterName)); + // showClusterNames is based on two values, as there are two cases we need to account for: + // + // 1. If there's only a single cluster a user has access to, they don't care about its name. + // However, the moment there's an extra leaf cluster or just another profile, the user might want + // to know the name of a cluster for the given connection, even if the connection list currently + // shows connections only from a single cluster. + // + // 2. The connection list might include a connection to a leaf cluster resource even after that + // leaf is no longer available and there's only a single cluster in clustersService. As such, we + // have to look at the number of clusters in connections as well. + const showClusterName = + clustersService.getClustersCount() > 1 || clustersInConnections.size > 1; return ( @@ -54,6 +61,7 @@ export function ConnectionsFilterableList( props.onActivateItem(item.id)} onRemove={() => props.onRemoveItem(item.id)} onDisconnect={() => props.onDisconnectItem(item.id)} diff --git a/web/packages/teleterm/src/ui/services/clusters/clustersService.ts b/web/packages/teleterm/src/ui/services/clusters/clustersService.ts index 507165a5776b5..d011ee498884e 100644 --- a/web/packages/teleterm/src/ui/services/clusters/clustersService.ts +++ b/web/packages/teleterm/src/ui/services/clusters/clustersService.ts @@ -486,6 +486,10 @@ export class ClustersService extends ImmutableStore return [...this.state.clusters.values()]; } + getClustersCount() { + return this.state.clusters.size; + } + getRootClusters() { return this.getClusters().filter(c => !c.leaf); }