From d1862f3d4d788a5da1d53425bc351a2a820e15a5 Mon Sep 17 00:00:00 2001 From: Grzegorz Zdunek Date: Wed, 22 Jan 2025 18:16:45 +0100 Subject: [PATCH] Show active tab name in status bar (#51108) * Show active document in the status bar * Update reading breadcrumbs value in tests * Show db name first and then username * Add helpers for making documents * Show document icon in tabs and adjust styling * Update gateway title from the document title * Fix document helpers * Put breadcrumbs in title * Simplify breadcrumb key * Optimize `useActiveDocumentClusterBreadcrumbs` * Deprecate `isDocumentTshNodeWithLoginHost` and `isDocumentTshNodeWithServerId` * Remove `aria-label="breadcrumbs"` * Replace id with class * Reuse `makeAppGateway`, `makeDatabaseGateway` and `makeKubeGateway` helpers in docs helpers * Add a comment about updating gateway title * Re-update the format of db gateways title * Render entire TabHost instead of only tabs * Make comment more specific --- .../teleterm/src/services/tshd/testHelpers.ts | 6 +- .../ui/AppInitializer/AppInitializer.test.tsx | 6 +- .../ui/DocumentGateway/DocumentGateway.tsx | 2 +- .../StatusBar/ShareFeedback/ShareFeedback.tsx | 2 +- .../teleterm/src/ui/StatusBar/StatusBar.tsx | 46 ++++- .../useActiveDocumentClusterBreadcrumbs.ts | 69 ++++--- .../teleterm/src/ui/TabHost/TabHost.story.tsx | 73 ++++++++ web/packages/teleterm/src/ui/Tabs/TabItem.tsx | 45 ++--- web/packages/teleterm/src/ui/Tabs/Tabs.tsx | 6 +- .../teleterm/src/ui/fixtures/mocks.ts | 15 +- .../connectionTrackerService.ts | 5 + .../documentsService/documentsUtils.ts | 93 +++++++++- .../documentsService/testHelpers.ts | 172 +++++++++++++++++- .../documentsService/types.ts | 8 + 14 files changed, 466 insertions(+), 82 deletions(-) create mode 100644 web/packages/teleterm/src/ui/TabHost/TabHost.story.tsx diff --git a/web/packages/teleterm/src/services/tshd/testHelpers.ts b/web/packages/teleterm/src/services/tshd/testHelpers.ts index 8cb15ec3e3701..34ccb2a3e7ef2 100644 --- a/web/packages/teleterm/src/services/tshd/testHelpers.ts +++ b/web/packages/teleterm/src/services/tshd/testHelpers.ts @@ -245,7 +245,7 @@ export const makeLoggedInUser = ( export const makeDatabaseGateway = ( props: Partial = {} ): tsh.Gateway => ({ - uri: '/gateways/foo', + uri: '/gateways/db', targetName: 'sales-production', targetUri: databaseUri, targetUser: 'alice', @@ -265,7 +265,7 @@ export const makeDatabaseGateway = ( export const makeKubeGateway = ( props: Partial = {} ): tsh.Gateway => ({ - uri: '/gateways/foo', + uri: '/gateways/kube', targetName: 'foo', targetUri: kubeUri, targetUser: '', @@ -285,7 +285,7 @@ export const makeKubeGateway = ( export const makeAppGateway = ( props: Partial = {} ): tsh.Gateway => ({ - uri: '/gateways/bar', + uri: '/gateways/app', targetName: 'sales-production', targetUri: appUri, localAddress: 'localhost', diff --git a/web/packages/teleterm/src/ui/AppInitializer/AppInitializer.test.tsx b/web/packages/teleterm/src/ui/AppInitializer/AppInitializer.test.tsx index 102a9d4d23e32..63d35c68a0b3a 100644 --- a/web/packages/teleterm/src/ui/AppInitializer/AppInitializer.test.tsx +++ b/web/packages/teleterm/src/ui/AppInitializer/AppInitializer.test.tsx @@ -147,10 +147,8 @@ test('activating a workspace via deep link overrides the previously active works await userEvent.click(dialogSuccessButton); // Check if the first activated workspace is the one from the deep link. - expect(await screen.findByTitle(/Current cluster:/)).toBeVisible(); - expect( - screen.queryByTitle(`Current cluster: ${deepLinkCluster.name}`) - ).toBeVisible(); + const el = await screen.findByTitle(/Open Profiles/); + expect(el.title).toContain(deepLinkCluster.name); }); test.each<{ diff --git a/web/packages/teleterm/src/ui/DocumentGateway/DocumentGateway.tsx b/web/packages/teleterm/src/ui/DocumentGateway/DocumentGateway.tsx index cb884bff8a924..ede152605fc3d 100644 --- a/web/packages/teleterm/src/ui/DocumentGateway/DocumentGateway.tsx +++ b/web/packages/teleterm/src/ui/DocumentGateway/DocumentGateway.tsx @@ -54,7 +54,7 @@ export function DocumentGateway(props: { const runCliCommand = () => { const command = getCliCommandArgv0(gateway.gatewayCliCommand); - const title = `${command} · ${doc.targetUser}@${doc.targetName}`; + const title = `${command} · ${doc.targetName} (${doc.targetUser})`; const cliDoc = documentsService.createGatewayCliDocument({ title, diff --git a/web/packages/teleterm/src/ui/StatusBar/ShareFeedback/ShareFeedback.tsx b/web/packages/teleterm/src/ui/StatusBar/ShareFeedback/ShareFeedback.tsx index 436a4d280c299..ed0d32f2957fd 100644 --- a/web/packages/teleterm/src/ui/StatusBar/ShareFeedback/ShareFeedback.tsx +++ b/web/packages/teleterm/src/ui/StatusBar/ShareFeedback/ShareFeedback.tsx @@ -50,7 +50,7 @@ export function ShareFeedback() { onClick={openShareFeedback} > {!hasBeenShareFeedbackOpened && } - + . */ +import { Fragment } from 'react'; + import { Flex, Text } from 'design'; import { AccessRequestCheckoutButton } from './AccessRequestCheckoutButton'; @@ -23,7 +25,7 @@ import { ShareFeedback } from './ShareFeedback'; import { useActiveDocumentClusterBreadcrumbs } from './useActiveDocumentClusterBreadcrumbs'; export function StatusBar() { - const clusterBreadcrumbs = useActiveDocumentClusterBreadcrumbs(); + const breadcrumbs = useActiveDocumentClusterBreadcrumbs(); return ( - - {clusterBreadcrumbs} - + {breadcrumbs && ( + name).join(' → ')} + > + {breadcrumbs.map((breadcrumb, index) => ( + + {breadcrumb.Icon && ( + + )} + {breadcrumb.name} + {index !== breadcrumbs.length - 1 && ( + + )} + + ))} + + )} + + diff --git a/web/packages/teleterm/src/ui/StatusBar/useActiveDocumentClusterBreadcrumbs.ts b/web/packages/teleterm/src/ui/StatusBar/useActiveDocumentClusterBreadcrumbs.ts index bd785cf51d72a..9d82b6dce151e 100644 --- a/web/packages/teleterm/src/ui/StatusBar/useActiveDocumentClusterBreadcrumbs.ts +++ b/web/packages/teleterm/src/ui/StatusBar/useActiveDocumentClusterBreadcrumbs.ts @@ -16,42 +16,57 @@ * along with this program. If not, see . */ -import { useAppContext } from 'teleterm/ui/appContextProvider'; +import { ComponentType, useCallback } from 'react'; + +import { IconProps } from 'design/Icon/Icon'; + import { getResourceUri, - useWorkspaceServiceState, + getStaticNameAndIcon, } from 'teleterm/ui/services/workspacesService'; import { routing } from 'teleterm/ui/uri'; -export function useActiveDocumentClusterBreadcrumbs(): string { - const ctx = useAppContext(); - useWorkspaceServiceState(); - ctx.clustersService.useState(); +import { useStoreSelector } from '../hooks/useStoreSelector'; - const activeDocument = ctx.workspacesService - .getActiveWorkspaceDocumentService() - ?.getActive(); +interface Breadcrumb { + name: string; + Icon?: ComponentType; +} - if (!activeDocument) { - return; - } +export function useActiveDocumentClusterBreadcrumbs(): Breadcrumb[] { + const activeDocument = useStoreSelector( + 'workspacesService', + useCallback(state => { + const workspace = state.workspaces[state.rootClusterUri]; + return workspace?.documents.find(d => d.uri === workspace?.location); + }, []) + ); + const resourceUri = activeDocument && getResourceUri(activeDocument); + const staticNameAndIcon = + activeDocument && getStaticNameAndIcon(activeDocument); + const clusterUri = resourceUri && routing.ensureClusterUri(resourceUri); + const rootClusterUri = + resourceUri && routing.ensureRootClusterUri(resourceUri); + + const cluster = useStoreSelector( + 'clustersService', + useCallback(state => state.clusters.get(clusterUri), [clusterUri]) + ); + const rootCluster = useStoreSelector( + 'clustersService', + useCallback(state => state.clusters.get(rootClusterUri), [rootClusterUri]) + ); - const resourceUri = getResourceUri(activeDocument); - if (!resourceUri) { + if (!cluster || !rootCluster || !staticNameAndIcon) { return; } - const clusterUri = routing.ensureClusterUri(resourceUri); - const rootClusterUri = routing.ensureRootClusterUri(resourceUri); - - const rootCluster = ctx.clustersService.findCluster(rootClusterUri); - const leafCluster = - clusterUri === rootClusterUri - ? undefined - : ctx.clustersService.findCluster(clusterUri); - - return [rootCluster, leafCluster] - .filter(Boolean) - .map(c => c.name) - .join(' > '); + return [ + { name: rootCluster.name }, + clusterUri !== rootClusterUri && { name: cluster.name }, + { + name: staticNameAndIcon.name, + Icon: staticNameAndIcon.Icon, + }, + ].filter(Boolean); } diff --git a/web/packages/teleterm/src/ui/TabHost/TabHost.story.tsx b/web/packages/teleterm/src/ui/TabHost/TabHost.story.tsx new file mode 100644 index 0000000000000..07b12cd25e69a --- /dev/null +++ b/web/packages/teleterm/src/ui/TabHost/TabHost.story.tsx @@ -0,0 +1,73 @@ +/** + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { Meta } from '@storybook/react'; +import { createRef } from 'react'; + +import { makeRootCluster } from 'teleterm/services/tshd/testHelpers'; +import { ResourcesContextProvider } from 'teleterm/ui/DocumentCluster/resourcesContext'; +import { MockAppContextProvider } from 'teleterm/ui/fixtures/MockAppContextProvider'; +import { MockAppContext } from 'teleterm/ui/fixtures/mocks'; +import { Document } from 'teleterm/ui/services/workspacesService'; +import { + makeDocumentAccessRequests, + makeDocumentAuthorizeWebSession, + makeDocumentCluster, + makeDocumentConnectMyComputer, + makeDocumentGatewayApp, + makeDocumentGatewayCliClient, + makeDocumentGatewayDatabase, + makeDocumentGatewayKube, + makeDocumentPtySession, + makeDocumentTshNode, +} from 'teleterm/ui/services/workspacesService/documentsService/testHelpers'; + +import { TabHostContainer } from './TabHost'; + +const meta: Meta = { + title: 'Teleterm/TabHost', +}; + +export default meta; + +const allDocuments: Document[] = [ + makeDocumentCluster(), + makeDocumentTshNode(), + makeDocumentConnectMyComputer(), + makeDocumentGatewayDatabase(), + makeDocumentGatewayApp(), + makeDocumentGatewayCliClient(), + makeDocumentGatewayKube(), + makeDocumentAccessRequests(), + makeDocumentPtySession(), + makeDocumentAuthorizeWebSession(), +]; + +const cluster = makeRootCluster(); + +export function Story() { + const ctx = new MockAppContext(); + ctx.addRootClusterWithDoc(cluster, allDocuments); + return ( + + + + + + ); +} diff --git a/web/packages/teleterm/src/ui/Tabs/TabItem.tsx b/web/packages/teleterm/src/ui/Tabs/TabItem.tsx index c69f5535c1658..43aad682a49c1 100644 --- a/web/packages/teleterm/src/ui/Tabs/TabItem.tsx +++ b/web/packages/teleterm/src/ui/Tabs/TabItem.tsx @@ -16,11 +16,12 @@ * along with this program. If not, see . */ -import { useRef } from 'react'; +import { ComponentType, useRef } from 'react'; import styled from 'styled-components'; import { ButtonIcon, Text } from 'design'; import * as Icons from 'design/Icon'; +import { IconProps } from 'design/Icon/Icon'; import { LinearProgress } from 'teleterm/ui/components/LinearProgress'; @@ -29,6 +30,7 @@ import { useTabDnD } from './useTabDnD'; type TabItemProps = { index?: number; name?: string; + Icon?: ComponentType; active?: boolean; nextActive?: boolean; closeTabTooltip?: string; @@ -75,24 +77,21 @@ export function TabItem(props: TabItemProps) { min-width: 0; `} > - - + <TabContent ref={ref} active={active} dragging={isDragging} title={name}> + {props.Icon && <props.Icon size="small" pr={1} />} + <Title color="inherit" fontWeight={500} fontSize="12px"> {name} {isLoading && active && } {onClose && ( (props.active ? 'flex' : 'none')}; `} onClick={handleClose} > @@ -115,13 +114,7 @@ export function NewTabItem(props: NewTabItemProps) { return ( - + @@ -141,8 +134,6 @@ const RelativeContainer = styled.div` const TabContent = styled.div<{ dragging?: boolean; active?: boolean; - // TODO(bl-nero): is this really used? Perhaps remove it. - canDrag?: boolean; }>` display: flex; z-index: 1; // covers shadow from the top @@ -150,6 +141,8 @@ const TabContent = styled.div<{ min-width: 0; width: 100%; height: 100%; + cursor: pointer; + padding-inline: 6px 4px; border-radius: 8px 8px 0 0; position: relative; opacity: ${props => (props.dragging ? 0 : 1)}; @@ -168,21 +161,15 @@ const TabContent = styled.div<{ &:focus { color: ${props => props.theme.colors.text.main}; transition: color 0.3s; + + > .close { + display: flex; + } } `; const Title = styled(Text)` - display: block; - cursor: pointer; - outline: none; - color: inherit; - font-family: inherit; - line-height: 32px; - background-color: transparent; white-space: nowrap; - padding-left: 12px; - border: none; - min-width: 0; width: 100%; `; diff --git a/web/packages/teleterm/src/ui/Tabs/Tabs.tsx b/web/packages/teleterm/src/ui/Tabs/Tabs.tsx index 509324cb7d192..a5ca3eb832ec2 100644 --- a/web/packages/teleterm/src/ui/Tabs/Tabs.tsx +++ b/web/packages/teleterm/src/ui/Tabs/Tabs.tsx @@ -22,7 +22,10 @@ import { Box } from 'design'; import { typography } from 'design/system'; import { TypographyProps } from 'design/system/typography'; -import { Document } from 'teleterm/ui/services/workspacesService'; +import { + Document, + getStaticNameAndIcon, +} from 'teleterm/ui/services/workspacesService'; import { NewTabItem, TabItem } from './TabItem'; @@ -50,6 +53,7 @@ export function Tabs(props: Props) { index={index} name={item.title} active={active} + Icon={getStaticNameAndIcon(item)?.Icon} nextActive={nextActive} onClick={() => onSelect(item)} onClose={() => onClose(item)} diff --git a/web/packages/teleterm/src/ui/fixtures/mocks.ts b/web/packages/teleterm/src/ui/fixtures/mocks.ts index 08a15915d9b1a..f746eead52eb5 100644 --- a/web/packages/teleterm/src/ui/fixtures/mocks.ts +++ b/web/packages/teleterm/src/ui/fixtures/mocks.ts @@ -45,17 +45,24 @@ export class MockAppContext extends AppContext { }); } - addRootClusterWithDoc(cluster: Cluster, doc: Document | undefined) { + addRootClusterWithDoc( + cluster: Cluster, + doc: Document[] | Document | undefined + ) { this.clustersService.setState(draftState => { draftState.clusters.set(cluster.uri, cluster); }); + const docs = Array.isArray(doc) ? doc : [doc]; this.workspacesService.setState(draftState => { draftState.rootClusterUri = cluster.uri; draftState.workspaces[cluster.uri] = { - documents: [doc].filter(Boolean), - location: doc?.uri, + documents: docs.filter(Boolean), + location: docs[0]?.uri, localClusterUri: cluster.uri, - accessRequests: undefined, + accessRequests: { + isBarCollapsed: true, + pending: { kind: 'role', roles: new Set() }, + }, }; }); } diff --git a/web/packages/teleterm/src/ui/services/connectionTracker/connectionTrackerService.ts b/web/packages/teleterm/src/ui/services/connectionTracker/connectionTrackerService.ts index e444834106036..af1d53feca6fd 100644 --- a/web/packages/teleterm/src/ui/services/connectionTracker/connectionTrackerService.ts +++ b/web/packages/teleterm/src/ui/services/connectionTracker/connectionTrackerService.ts @@ -265,6 +265,11 @@ export class ConnectionTrackerService extends ImmutableStore. */ +import { ComponentType } from 'react'; + +import { + Application, + Database, + Kubernetes, + Laptop, + ListAddCheck, + Server, + ShieldCheck, + Table, + Terminal, +} from 'design/Icon'; +import { IconProps } from 'design/Icon/Icon'; + import { ClusterOrResourceUri, isAppUri, @@ -100,7 +115,7 @@ export function getDocumentGatewayTitle(doc: DocumentGateway): string { switch (targetKind) { case 'db': { - return targetUser ? `${targetUser}@${targetName}` : targetName; + return targetUser ? `${targetName} (${targetUser})` : targetName; } case 'app': { return targetSubresourceName @@ -112,3 +127,79 @@ export function getDocumentGatewayTitle(doc: DocumentGateway): string { } } } + +/** + * Returns a name and icon of the document. + * If possible, the name is the title of a document, except for cases + * when it contains some additional values like cwd, or a shell name. + * At the moment, the name is used only in the status bar. + * The icon is used both in the status bar and the tabs. + */ +export function getStaticNameAndIcon( + document: Document +): { name: string; Icon: ComponentType } | undefined { + switch (document.kind) { + case 'doc.cluster': + return { + name: 'Resources', + Icon: Table, + }; + case 'doc.gateway_cli_client': + return { + name: document.title, + Icon: Database, + }; + case 'doc.gateway': + if (isDatabaseUri(document.targetUri)) { + return { + name: document.title, + Icon: Database, + }; + } + if (isAppUri(document.targetUri)) { + return { + name: document.title, + Icon: Application, + }; + } + return; + case 'doc.gateway_kube': + return { + name: routing.parseKubeUri(document.targetUri).params.kubeId, + Icon: Kubernetes, + }; + case 'doc.terminal_tsh_node': + return isDocumentTshNodeWithServerId(document) + ? { + name: document.title, + Icon: Server, + } + : undefined; + case 'doc.access_requests': + return { + name: document.title, + Icon: ListAddCheck, + }; + case 'doc.terminal_shell': + return { + name: 'Terminal', + Icon: Terminal, + }; + case 'doc.connect_my_computer': + return { + name: document.title, + Icon: Laptop, + }; + case 'doc.authorize_web_session': + return { + name: document.title, + Icon: ShieldCheck, + }; + case 'doc.blank': + case 'doc.terminal_tsh_kube': + return undefined; + default: + document satisfies never; + return undefined; + } +} diff --git a/web/packages/teleterm/src/ui/services/workspacesService/documentsService/testHelpers.ts b/web/packages/teleterm/src/ui/services/workspacesService/documentsService/testHelpers.ts index c27a5acad517a..87e4a76e8c5ac 100644 --- a/web/packages/teleterm/src/ui/services/workspacesService/documentsService/testHelpers.ts +++ b/web/packages/teleterm/src/ui/services/workspacesService/documentsService/testHelpers.ts @@ -16,16 +16,32 @@ * along with this program. If not, see . */ -import { makeRootCluster } from 'teleterm/services/tshd/testHelpers'; +import { + makeAppGateway, + makeDatabaseGateway, + makeKubeGateway, + makeRootCluster, + makeServer, +} from 'teleterm/services/tshd/testHelpers'; -import { DocumentCluster } from './types'; +import { + DocumentAccessRequests, + DocumentAuthorizeWebSession, + DocumentCluster, + DocumentConnectMyComputer, + DocumentGateway, + DocumentGatewayCliClient, + DocumentGatewayKube, + DocumentPtySession, + DocumentTshNodeWithServerId, +} from './types'; export function makeDocumentCluster( props?: Partial ): DocumentCluster { return { kind: 'doc.cluster', - uri: '/docs/unique-uri', + uri: '/docs/cluster', title: 'teleport-ent.asteroid.earth', clusterUri: makeRootCluster().uri, queryParams: { @@ -40,3 +56,153 @@ export function makeDocumentCluster( ...props, }; } + +export function makeDocumentGatewayDatabase( + props?: Partial +): DocumentGateway { + const gw = makeDatabaseGateway(); + return { + kind: 'doc.gateway', + uri: '/docs/gateway_database', + gatewayUri: '/gateways/db-gateway', + title: 'aurora (sre)', + targetUri: gw.targetUri, + port: gw.localPort, + targetName: gw.targetName, + targetUser: gw.targetUser, + status: '', + targetSubresourceName: gw.targetSubresourceName, + origin: 'connection_list', + ...props, + }; +} + +export function makeDocumentGatewayApp( + props?: Partial +): DocumentGateway { + const gw = makeAppGateway(); + return { + kind: 'doc.gateway', + uri: '/docs/gateway_app', + title: 'grafana', + targetUri: gw.targetUri, + gatewayUri: gw.uri, + port: gw.localPort, + targetName: gw.targetName, + targetUser: gw.targetUser, + status: '', + targetSubresourceName: gw.targetSubresourceName, + origin: 'connection_list', + ...props, + }; +} + +export function makeDocumentPtySession( + props?: Partial +): DocumentPtySession { + return { + kind: 'doc.terminal_shell', + uri: '/docs/terminal_shell', + title: '/Users/alice/Documents', + rootClusterId: 'teleport-local', + ...props, + }; +} + +export function makeDocumentTshNode( + props?: Partial +): DocumentTshNodeWithServerId { + return { + kind: 'doc.terminal_tsh_node', + uri: '/docs/terminal_tsh_node', + title: 'alice@node', + serverUri: makeServer().uri, + status: '', + rootClusterId: 'teleport-local', + leafClusterId: '', + origin: 'connection_list', + serverId: '1234abcd-1234-abcd-1234-abcd1234abcd', + ...props, + }; +} + +export function makeDocumentGatewayCliClient( + props?: Partial +): DocumentGatewayCliClient { + const gw = makeDatabaseGateway(); + return { + kind: 'doc.gateway_cli_client', + uri: '/docs/gateway_cli_client', + title: 'psql · aurora (sre)', + rootClusterId: 'teleport-local', + leafClusterId: '', + targetProtocol: gw.protocol, + targetUri: gw.targetUri, + targetName: gw.targetName, + targetUser: gw.targetUser, + status: '', + ...props, + }; +} + +export function makeDocumentGatewayKube( + props?: Partial +): DocumentGatewayKube { + const gw = makeKubeGateway(); + return { + kind: 'doc.gateway_kube', + uri: '/docs/gateway_kube', + title: 'cookie', + rootClusterId: 'teleport-local', + leafClusterId: '', + targetUri: gw.targetUri, + status: '', + origin: 'connection_list', + ...props, + }; +} + +export function makeDocumentAccessRequests( + props?: Partial +): DocumentAccessRequests { + return { + kind: 'doc.access_requests', + uri: '/docs/access_requests', + title: 'Access Requests', + clusterUri: makeRootCluster().uri, + state: 'browsing', + requestId: '1231', + ...props, + }; +} + +export function makeDocumentConnectMyComputer( + props?: Partial +): DocumentConnectMyComputer { + return { + kind: 'doc.connect_my_computer', + uri: '/docs/connect-my-computer', + rootClusterUri: makeRootCluster().uri, + status: '', + title: 'Connect My Computer', + ...props, + }; +} + +export function makeDocumentAuthorizeWebSession( + props?: Partial +): DocumentAuthorizeWebSession { + return { + kind: 'doc.authorize_web_session', + uri: '/docs/authorize-web-session', + title: 'Authorize Web Session', + rootClusterUri: makeRootCluster().uri, + webSessionRequest: { + id: '123', + username: 'alice', + token: 'secret-token', + redirectUri: '', + }, + ...props, + }; +} diff --git a/web/packages/teleterm/src/ui/services/workspacesService/documentsService/types.ts b/web/packages/teleterm/src/ui/services/workspacesService/documentsService/types.ts index d39f12167515d..46162821b5721 100644 --- a/web/packages/teleterm/src/ui/services/workspacesService/documentsService/types.ts +++ b/web/packages/teleterm/src/ui/services/workspacesService/documentsService/types.ts @@ -288,6 +288,10 @@ export type Document = | DocumentConnectMyComputer | DocumentAuthorizeWebSession; +/** + * @deprecated DocumentTshNode is supposed to be simplified to just DocumentTshNodeWithServerId. + * See the comment for DocumentTshNodeWithLoginHost for more details. + */ export function isDocumentTshNodeWithLoginHost( doc: Document ): doc is DocumentTshNodeWithLoginHost { @@ -296,6 +300,10 @@ export function isDocumentTshNodeWithLoginHost( return doc.kind === 'doc.terminal_tsh_node' && !('serverId' in doc); } +/** + * @deprecated DocumentTshNode is supposed to be simplified to just DocumentTshNodeWithServerId. + * See the comment for DocumentTshNodeWithLoginHost for more details. + */ export function isDocumentTshNodeWithServerId( doc: Document ): doc is DocumentTshNodeWithServerId {