diff --git a/web/packages/teleterm/src/ui/AccessRequestCheckout/useAssumedRolesBar.test.tsx b/web/packages/teleterm/src/ui/AccessRequestCheckout/useAssumedRolesBar.test.tsx
new file mode 100644
index 0000000000000..9bcef0bc06467
--- /dev/null
+++ b/web/packages/teleterm/src/ui/AccessRequestCheckout/useAssumedRolesBar.test.tsx
@@ -0,0 +1,91 @@
+/**
+ * Teleport
+ * Copyright (C) 2024 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 { useEffect } from 'react';
+import { renderHook, act } from '@testing-library/react';
+
+import { MockAppContextProvider } from 'teleterm/ui/fixtures/MockAppContextProvider';
+import { MockAppContext } from 'teleterm/ui/fixtures/mocks';
+import { makeRootCluster } from 'teleterm/services/tshd/testHelpers';
+import {
+ ResourcesContextProvider,
+ useResourcesContext,
+} from 'teleterm/ui/DocumentCluster/resourcesContext';
+import { RootClusterUri } from 'teleterm/ui/uri';
+
+import { useAssumedRolesBar } from './useAssumedRolesBar';
+
+test('dropping a request refreshes resources', async () => {
+ const appContext = new MockAppContext();
+ const cluster = makeRootCluster();
+ appContext.workspacesService.setState(draftState => {
+ draftState.rootClusterUri = cluster.uri;
+ draftState.workspaces[cluster.uri] = {
+ localClusterUri: cluster.uri,
+ documents: [],
+ location: undefined,
+ accessRequests: undefined,
+ };
+ });
+ jest.spyOn(appContext.clustersService, 'dropRoles');
+ const refreshListener = jest.fn();
+
+ const wrapper = ({ children }) => (
+
+
+
+ {children}
+
+
+ );
+
+ const { result } = renderHook(
+ () =>
+ useAssumedRolesBar({
+ roles: [],
+ id: 'mocked-request-id',
+ expires: new Date(),
+ }),
+ { wrapper }
+ );
+
+ await act(() => result.current.dropRequest());
+ expect(appContext.clustersService.dropRoles).toHaveBeenCalledTimes(1);
+ expect(appContext.clustersService.dropRoles).toHaveBeenCalledWith(
+ cluster.uri,
+ ['mocked-request-id']
+ );
+ expect(refreshListener).toHaveBeenCalledTimes(1);
+});
+
+function RequestRefreshSubscriber(props: {
+ rootClusterUri: RootClusterUri;
+ onResourcesRefreshRequest: () => void;
+}) {
+ const { onResourcesRefreshRequest } = useResourcesContext(
+ props.rootClusterUri
+ );
+ useEffect(() => {
+ onResourcesRefreshRequest(props.onResourcesRefreshRequest);
+ }, [onResourcesRefreshRequest, props.onResourcesRefreshRequest]);
+
+ return null;
+}
diff --git a/web/packages/teleterm/src/ui/AccessRequestCheckout/useAssumedRolesBar.ts b/web/packages/teleterm/src/ui/AccessRequestCheckout/useAssumedRolesBar.ts
index 3bce0793dc8ea..b038ec8d6b283 100644
--- a/web/packages/teleterm/src/ui/AccessRequestCheckout/useAssumedRolesBar.ts
+++ b/web/packages/teleterm/src/ui/AccessRequestCheckout/useAssumedRolesBar.ts
@@ -32,10 +32,12 @@ import { useInterval } from 'shared/hooks';
import { useAppContext } from 'teleterm/ui/appContextProvider';
import { retryWithRelogin } from 'teleterm/ui/utils';
import { AssumedRequest } from 'teleterm/services/tshd/types';
+import { useResourcesContext } from 'teleterm/ui/DocumentCluster/resourcesContext';
export function useAssumedRolesBar(assumedRequest: AssumedRequest) {
const ctx = useAppContext();
const rootClusterUri = ctx.workspacesService?.getRootClusterUri();
+ const { requestResourcesRefresh } = useResourcesContext(rootClusterUri);
const [duration, setDuration] = useState(() =>
getDurationFromNow({
@@ -46,21 +48,18 @@ export function useAssumedRolesBar(assumedRequest: AssumedRequest) {
getRefreshInterval(duration)
);
- const [dropRequestAttempt, dropRequest] = useAsync(() => {
- return retryWithRelogin(
- ctx,
- rootClusterUri,
- () => ctx.clustersService.dropRoles(rootClusterUri, [assumedRequest.id])
- // TODO(gzdunek): We should refresh the resources,
- // the same as after assuming a role in `useAssumeAccess`.
- // Unfortunately, we can't do this because we don't have access to `ResourcesContext`.
- // Consider moving it into `ResourcesService`.
- ).catch(err => {
+ const [dropRequestAttempt, dropRequest] = useAsync(async () => {
+ try {
+ await retryWithRelogin(ctx, rootClusterUri, () =>
+ ctx.clustersService.dropRoles(rootClusterUri, [assumedRequest.id])
+ );
+ requestResourcesRefresh();
+ } catch (err) {
ctx.notificationsService.notifyError({
- title: 'Could not switch back the role',
+ title: 'Could not drop role',
description: err.message,
});
- });
+ }
});
const updateDurationAndInterval = useCallback(() => {
diff --git a/web/packages/teleterm/src/ui/App.tsx b/web/packages/teleterm/src/ui/App.tsx
index 9236ac9222e11..204a3e467ce04 100644
--- a/web/packages/teleterm/src/ui/App.tsx
+++ b/web/packages/teleterm/src/ui/App.tsx
@@ -29,6 +29,7 @@ import AppContext from './appContext';
import { ThemeProvider } from './ThemeProvider';
import { VnetContextProvider } from './Vnet/vnetContext';
import { ConnectionsContextProvider } from './TopBar/Connections/connectionsContext';
+import { ResourcesContextProvider } from './DocumentCluster/resourcesContext';
export const App: React.FC<{ ctx: AppContext }> = ({ ctx }) => {
return (
@@ -36,13 +37,15 @@ export const App: React.FC<{ ctx: AppContext }> = ({ ctx }) => {
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/web/packages/teleterm/src/ui/ConnectMyComputer/DocumentConnectMyComputer/Setup.tsx b/web/packages/teleterm/src/ui/ConnectMyComputer/DocumentConnectMyComputer/Setup.tsx
index 5cc895509d37d..8e8c7ba3c3e33 100644
--- a/web/packages/teleterm/src/ui/ConnectMyComputer/DocumentConnectMyComputer/Setup.tsx
+++ b/web/packages/teleterm/src/ui/ConnectMyComputer/DocumentConnectMyComputer/Setup.tsx
@@ -210,7 +210,7 @@ function AgentSetup() {
setDownloadAgentAttempt,
agentProcessState,
} = useConnectMyComputerContext();
- const { requestResourcesRefresh } = useResourcesContext();
+ const { requestResourcesRefresh } = useResourcesContext(rootClusterUri);
const rootCluster = ctx.clustersService.findCluster(rootClusterUri);
// The verify agent step checks if we can execute the binary. This triggers OS-level checks, such
@@ -279,8 +279,8 @@ function AgentSetup() {
throw error;
}
- // Now that the node has joined the server, let's refresh all open DocumentCluster instances
- // to show the new node.
+ // Now that the node has joined the server, let's refresh open DocumentCluster
+ // instances in the workspace to show the new node.
requestResourcesRefresh();
}, [startAgent, requestResourcesRefresh])
);
diff --git a/web/packages/teleterm/src/ui/ConnectMyComputer/connectMyComputerContext.tsx b/web/packages/teleterm/src/ui/ConnectMyComputer/connectMyComputerContext.tsx
index 723950eb47549..e6c6929ef25fa 100644
--- a/web/packages/teleterm/src/ui/ConnectMyComputer/connectMyComputerContext.tsx
+++ b/web/packages/teleterm/src/ui/ConnectMyComputer/connectMyComputerContext.tsx
@@ -131,7 +131,7 @@ export const ConnectMyComputerContextProvider: FC<
workspacesService,
usageService,
} = ctx;
- const { requestResourcesRefresh } = useResourcesContext();
+ const { requestResourcesRefresh } = useResourcesContext(rootClusterUri);
clustersService.useState();
const [
diff --git a/web/packages/teleterm/src/ui/DocumentAccessRequests/RequestList/RequestList.tsx b/web/packages/teleterm/src/ui/DocumentAccessRequests/RequestList/RequestList.tsx
index 40607a7ee17be..f2c52fb16ff82 100644
--- a/web/packages/teleterm/src/ui/DocumentAccessRequests/RequestList/RequestList.tsx
+++ b/web/packages/teleterm/src/ui/DocumentAccessRequests/RequestList/RequestList.tsx
@@ -167,7 +167,7 @@ const renderActionCell = (
onClick={() => assumeRole(request)}
width="108px"
>
- {flags.isAssumed ? 'assumed' : 'assume roles'}
+ {flags.isAssumed ? 'Assumed' : 'Assume Roles'}
);
} else {
diff --git a/web/packages/teleterm/src/ui/DocumentAccessRequests/useAssumeAccess.ts b/web/packages/teleterm/src/ui/DocumentAccessRequests/useAssumeAccess.ts
index 741d5337e3405..8493052ba7b5b 100644
--- a/web/packages/teleterm/src/ui/DocumentAccessRequests/useAssumeAccess.ts
+++ b/web/packages/teleterm/src/ui/DocumentAccessRequests/useAssumeAccess.ts
@@ -30,12 +30,12 @@ export function useAssumeAccess() {
rootClusterUri,
documentsService,
} = useWorkspaceContext();
- const { requestResourcesRefresh } = useResourcesContext();
+ const { requestResourcesRefresh } = useResourcesContext(rootClusterUri);
const [assumeRoleAttempt, runAssumeRole] = useAsync((requestId: string) =>
retryWithRelogin(ctx, clusterUri, async () => {
await ctx.clustersService.assumeRoles(rootClusterUri, [requestId]);
- // refresh the current resource tabs
+ // Refresh the current resource tabs in the workspace.
requestResourcesRefresh();
})
);
@@ -58,7 +58,7 @@ export function useAssumeAccess() {
return;
}
- // refresh the current resource tabs
+ // Refresh the current resource tabs in the workspace.
requestResourcesRefresh();
// open new cluster tab
diff --git a/web/packages/teleterm/src/ui/DocumentCluster/UnifiedResources.test.tsx b/web/packages/teleterm/src/ui/DocumentCluster/UnifiedResources.test.tsx
index eb537ce8335e5..a69d79f3eb9ea 100644
--- a/web/packages/teleterm/src/ui/DocumentCluster/UnifiedResources.test.tsx
+++ b/web/packages/teleterm/src/ui/DocumentCluster/UnifiedResources.test.tsx
@@ -16,6 +16,7 @@
* along with this program. If not, see .
*/
+import { useImperativeHandle, forwardRef, createRef } from 'react';
import { render, screen } from 'design/utils/testing';
import { mockIntersectionObserver } from 'jsdom-testing-mocks';
import { act } from '@testing-library/react';
@@ -30,7 +31,10 @@ import { ShowResources } from 'gen-proto-ts/teleport/lib/teleterm/v1/cluster_pb'
import { UnifiedResources } from 'teleterm/ui/DocumentCluster/UnifiedResources';
import { MockAppContextProvider } from 'teleterm/ui/fixtures/MockAppContextProvider';
-import { ResourcesContextProvider } from 'teleterm/ui/DocumentCluster/resourcesContext';
+import {
+ ResourcesContextProvider,
+ useResourcesContext,
+} from 'teleterm/ui/DocumentCluster/resourcesContext';
import { ConnectMyComputerContextProvider } from 'teleterm/ui/ConnectMyComputer';
import { MockWorkspaceContextProvider } from 'teleterm/ui/fixtures/MockWorkspaceContextProvider';
import { makeDocumentCluster } from 'teleterm/ui/services/workspacesService/documentsService/testHelpers';
@@ -38,14 +42,15 @@ import { MockAppContext } from 'teleterm/ui/fixtures/mocks';
import {
makeRootCluster,
rootClusterUri,
+ makeServer,
} from 'teleterm/services/tshd/testHelpers';
import { getEmptyPendingAccessRequest } from 'teleterm/ui/services/workspacesService/accessRequestsService';
-
import { MockedUnaryCall } from 'teleterm/services/tshd/cloneableClient';
+import * as uri from 'teleterm/ui/uri';
const mio = mockIntersectionObserver();
-const tests = [
+test.each([
{
name: 'fetches only available resources if cluster does not support access requests',
conditions: {
@@ -166,9 +171,7 @@ const tests = [
includeRequestable: false,
},
},
-];
-
-test.each(tests)('$name', async testCase => {
+])('$name', async testCase => {
const doc = makeDocumentCluster();
const appContext = new MockAppContext({ platform: 'darwin' });
@@ -270,3 +273,119 @@ test.each(tests)('$name', async testCase => {
new AbortController().signal
);
});
+
+test.each([
+ {
+ name: 'refreshes resources when the document cluster URI matches the requested cluster URI',
+ conditions: {
+ documentClusterUri: '/clusters/teleport-local',
+ },
+ expect: {
+ resourcesRefreshed: true,
+ },
+ },
+ {
+ name: 'refreshes resources when the document cluster URI is a leaf of the requested cluster URI',
+ conditions: {
+ documentClusterUri: '/clusters/teleport-local/leaves/leaf',
+ },
+ expect: {
+ resourcesRefreshed: true,
+ },
+ },
+])('$name', async testCase => {
+ const doc = makeDocumentCluster({
+ clusterUri: testCase.conditions.documentClusterUri,
+ });
+ const rootCluster = makeRootCluster({
+ uri: uri.routing.ensureRootClusterUri(doc.clusterUri),
+ });
+ const serverResource = makeServer();
+ const appContext = new MockAppContext();
+ appContext.clustersService.setState(draft => {
+ draft.clusters.set(rootCluster.uri, rootCluster);
+ });
+
+ appContext.workspacesService.setState(draftState => {
+ draftState.rootClusterUri = rootCluster.uri;
+ draftState.workspaces[rootCluster.uri] = {
+ localClusterUri: rootCluster.uri,
+ documents: [doc],
+ location: doc.uri,
+ accessRequests: {
+ pending: getEmptyPendingAccessRequest(),
+ isBarCollapsed: true,
+ },
+ };
+ });
+
+ jest
+ .spyOn(appContext.resourcesService, 'listUnifiedResources')
+ .mockResolvedValue({
+ resources: [
+ {
+ kind: 'server',
+ resource: serverResource,
+ requiresRequest: false,
+ },
+ ],
+ nextKey: '',
+ });
+
+ const ref = createRef<{
+ requestResourcesRefresh: () => void;
+ }>();
+
+ render(
+
+
+
+
+
+
+
+
+
+
+ );
+
+ act(mio.enterAll);
+
+ // Wait for resources to render.
+ await expect(
+ screen.findByText(serverResource.hostname)
+ ).resolves.toBeInTheDocument();
+ expect(
+ appContext.resourcesService.listUnifiedResources
+ ).toHaveBeenCalledTimes(1);
+
+ act(() => ref.current.requestResourcesRefresh());
+
+ // Wait for resources to (potentially) re-render.
+ await expect(
+ screen.findByText(serverResource.hostname)
+ ).resolves.toBeInTheDocument();
+ expect(
+ appContext.resourcesService.listUnifiedResources
+ // When resources are refreshed, we have two calls to the API.
+ ).toHaveBeenCalledTimes(testCase.expect.resourcesRefreshed ? 2 : 1);
+});
+
+const Refresher = forwardRef<
+ {
+ requestResourcesRefresh: () => void;
+ },
+ {
+ rootClusterUri: uri.RootClusterUri;
+ }
+>((props, ref) => {
+ const resourcesContext = useResourcesContext(props.rootClusterUri);
+ useImperativeHandle(ref, () => ({
+ requestResourcesRefresh: resourcesContext.requestResourcesRefresh,
+ }));
+ return null;
+});
diff --git a/web/packages/teleterm/src/ui/DocumentCluster/UnifiedResources.tsx b/web/packages/teleterm/src/ui/DocumentCluster/UnifiedResources.tsx
index 1d2f6e7613701..b3f9de5fe0c73 100644
--- a/web/packages/teleterm/src/ui/DocumentCluster/UnifiedResources.tsx
+++ b/web/packages/teleterm/src/ui/DocumentCluster/UnifiedResources.tsx
@@ -74,7 +74,7 @@ import {
ConnectAppActionButton,
AccessRequestButton,
} from './ActionButtons';
-import { useResourcesContext, ResourcesContext } from './resourcesContext';
+import { useResourcesContext } from './resourcesContext';
import { useUserPreferences } from './useUserPreferences';
export function UnifiedResources(props: {
@@ -102,7 +102,7 @@ export function UnifiedResources(props: {
[rootClusterUri]
)
);
- const { onResourcesRefreshRequest } = useResourcesContext();
+ const { onResourcesRefreshRequest } = useResourcesContext(rootClusterUri);
const loggedInUser = useWorkspaceLoggedInUser();
const { unifiedResourcePreferences } = userPreferences;
@@ -263,7 +263,7 @@ const Resources = memo(
canAddResources: boolean;
canUseConnectMyComputer: boolean;
openConnectMyComputerDocument(): void;
- onResourcesRefreshRequest: ResourcesContext['onResourcesRefreshRequest'];
+ onResourcesRefreshRequest(listener: () => void): { cleanup(): void };
discoverUrl: string;
getAccessRequestButton: (resource: UnifiedResourceResponse) => JSX.Element;
getAddedItemsCount: () => number;
@@ -329,7 +329,7 @@ const Resources = memo(
const { onResourcesRefreshRequest } = props;
useEffect(() => {
const { cleanup } = onResourcesRefreshRequest(() => {
- fetch({ clear: true });
+ void fetch({ clear: true });
});
return cleanup;
}, [onResourcesRefreshRequest, fetch]);
diff --git a/web/packages/teleterm/src/ui/DocumentCluster/resourcesContext.test.tsx b/web/packages/teleterm/src/ui/DocumentCluster/resourcesContext.test.tsx
index 0430156b5c6e9..37d1339db9611 100644
--- a/web/packages/teleterm/src/ui/DocumentCluster/resourcesContext.test.tsx
+++ b/web/packages/teleterm/src/ui/DocumentCluster/resourcesContext.test.tsx
@@ -19,6 +19,8 @@
import React from 'react';
import { renderHook } from '@testing-library/react';
+import { rootClusterUri } from 'teleterm/services/tshd/testHelpers';
+
import {
ResourcesContextProvider,
useResourcesContext,
@@ -29,13 +31,28 @@ describe('requestResourcesRefresh', () => {
const wrapper = ({ children }) => (
{children}
);
- const { result } = renderHook(() => useResourcesContext(), { wrapper });
+ const { result } = renderHook(
+ () => ({
+ clusterUri1: useResourcesContext('/clusters/teleport-local'),
+ clusterUri2: useResourcesContext('/clusters/teleport-remote'),
+ }),
+ {
+ wrapper,
+ }
+ );
- const listener = jest.fn();
- result.current.onResourcesRefreshRequest(listener);
- result.current.requestResourcesRefresh();
+ const listener1 = jest.fn();
+ const listener2 = jest.fn();
+ result.current.clusterUri1.onResourcesRefreshRequest(listener1);
+ result.current.clusterUri2.onResourcesRefreshRequest(listener2);
+
+ result.current.clusterUri1.requestResourcesRefresh();
+ expect(listener1).toHaveBeenCalledTimes(1);
+ expect(listener2).not.toHaveBeenCalled();
- expect(listener).toHaveBeenCalledTimes(1);
+ result.current.clusterUri2.requestResourcesRefresh();
+ expect(listener1).toHaveBeenCalledTimes(1); // it has not been called again
+ expect(listener2).toHaveBeenCalledTimes(1);
});
});
@@ -44,7 +61,9 @@ describe('onResourcesRefreshRequest cleanup function', () => {
const wrapper = ({ children }) => (
{children}
);
- const { result } = renderHook(() => useResourcesContext(), { wrapper });
+ const { result } = renderHook(() => useResourcesContext(rootClusterUri), {
+ wrapper,
+ });
const listener = jest.fn();
const { cleanup } = result.current.onResourcesRefreshRequest(listener);
diff --git a/web/packages/teleterm/src/ui/DocumentCluster/resourcesContext.tsx b/web/packages/teleterm/src/ui/DocumentCluster/resourcesContext.tsx
index f7961f9dfce0f..a8794ba6533d1 100644
--- a/web/packages/teleterm/src/ui/DocumentCluster/resourcesContext.tsx
+++ b/web/packages/teleterm/src/ui/DocumentCluster/resourcesContext.tsx
@@ -18,7 +18,7 @@
import { EventEmitter } from 'events';
-import React, {
+import {
createContext,
FC,
PropsWithChildren,
@@ -27,20 +27,28 @@ import React, {
useRef,
} from 'react';
+import { RootClusterUri } from 'teleterm/ui/uri';
+
export interface ResourcesContext {
/**
- * requestResourcesRefresh makes all DocumentCluster instances within the workspace refresh the
- * resource list with current filters.
+ * requestResourcesRefresh makes all DocumentCluster instances within the workspace
+ * (specified by `rootClusterUri`) refresh the resource list with current filters.
*
- * Its main purpose is to refresh the resource list in existing DocumentCluster tabs after a
- * Connect My Computer node is set up.
+ * Its purpose is to refresh the resource list in existing DocumentCluster tabs after a
+ * Connect My Computer node is set up, or after assuming/dropping an access request.
*/
- requestResourcesRefresh: () => void;
+ requestResourcesRefresh: (rootClusterUri: RootClusterUri) => void;
/**
* onResourcesRefreshRequest registers a listener that will be called any time a refresh is
- * requested. Typically called from useEffect, for this purpose it returns a cleanup function.
+ * requested for a particular rootClusterUri. Typically called from useEffect, for this purpose it
+ * returns a cleanup function.
*/
- onResourcesRefreshRequest: (listener: () => void) => { cleanup: () => void };
+ onResourcesRefreshRequest: (
+ rootClusterUri: RootClusterUri,
+ listener: () => void
+ ) => {
+ cleanup: () => void;
+ };
}
const ResourcesContext = createContext(null);
@@ -51,24 +59,32 @@ export const ResourcesContextProvider: FC = props => {
emitterRef.current = new EventEmitter();
}
- // This function could be expanded to emit a cluster URI so that a request refresh for a root
- // cluster doesn't trigger refreshes of leaf DocumentCluster instances and vice versa.
- // However, the implementation should be good enough for now since it's used only in Connect My
- // Computer setup anyway.
const requestResourcesRefresh = useCallback(
- () => emitterRef.current.emit('refresh'),
+ (rootClusterUri: RootClusterUri) =>
+ emitterRef.current.emit('refresh', rootClusterUri),
[]
);
- const onResourcesRefreshRequest = useCallback(listener => {
- emitterRef.current.addListener('refresh', listener);
+ const onResourcesRefreshRequest = useCallback(
+ (
+ targetRootClusterUri: RootClusterUri,
+ listenerWithoutRootClusterUri: () => void
+ ) => {
+ const listener = (rootClusterUri: RootClusterUri) => {
+ if (rootClusterUri === targetRootClusterUri) {
+ listenerWithoutRootClusterUri();
+ }
+ };
+ emitterRef.current.addListener('refresh', listener);
- return {
- cleanup: () => {
- emitterRef.current.removeListener('refresh', listener);
- },
- };
- }, []);
+ return {
+ cleanup: () => {
+ emitterRef.current.removeListener('refresh', listener);
+ },
+ };
+ },
+ []
+ );
return (
= props => {
);
};
-export const useResourcesContext = () => {
+export const useResourcesContext = (rootClusterUri: RootClusterUri) => {
const context = useContext(ResourcesContext);
if (!context) {
@@ -87,5 +103,20 @@ export const useResourcesContext = () => {
);
}
- return context;
+ const {
+ requestResourcesRefresh: requestResourcesRefreshContext,
+ onResourcesRefreshRequest: onResourcesRefreshRequestContext,
+ } = context;
+
+ return {
+ requestResourcesRefresh: useCallback(
+ () => requestResourcesRefreshContext(rootClusterUri),
+ [requestResourcesRefreshContext, rootClusterUri]
+ ),
+ onResourcesRefreshRequest: useCallback(
+ (listener: () => void) =>
+ onResourcesRefreshRequestContext(rootClusterUri, listener),
+ [onResourcesRefreshRequestContext, rootClusterUri]
+ ),
+ };
};
diff --git a/web/packages/teleterm/src/ui/Documents/DocumentsRenderer.tsx b/web/packages/teleterm/src/ui/Documents/DocumentsRenderer.tsx
index f3cda92bd922f..0be864d2748f6 100644
--- a/web/packages/teleterm/src/ui/Documents/DocumentsRenderer.tsx
+++ b/web/packages/teleterm/src/ui/Documents/DocumentsRenderer.tsx
@@ -45,8 +45,6 @@ import { DocumentGatewayApp } from 'teleterm/ui/DocumentGatewayApp';
import Document from 'teleterm/ui/Document';
import { RootClusterUri, isDatabaseUri, isAppUri } from 'teleterm/ui/uri';
-import { ResourcesContextProvider } from '../DocumentCluster/resourcesContext';
-
import { WorkspaceContextProvider } from './workspaceContext';
import { KeyboardShortcutsPanel } from './KeyboardShortcutsPanel';
@@ -87,25 +85,22 @@ export function DocumentsRenderer(props: {
key={workspace.rootClusterUri}
>
- {/* ConnectMyComputerContext depends on ResourcesContext. */}
-
-
- {workspace.documentsService.getDocuments().length ? (
- renderDocuments(workspace.documentsService)
- ) : (
-
+
+ {workspace.documentsService.getDocuments().length ? (
+ renderDocuments(workspace.documentsService)
+ ) : (
+
+ )}
+ {workspace.rootClusterUri ===
+ workspacesService.getRootClusterUri() &&
+ props.topBarContainerRef.current &&
+ createPortal(
+ ,
+ props.topBarContainerRef.current
)}
- {workspace.rootClusterUri ===
- workspacesService.getRootClusterUri() &&
- props.topBarContainerRef.current &&
- createPortal(
- ,
- props.topBarContainerRef.current
- )}
-
-
+
))}
diff --git a/web/packages/teleterm/src/ui/TabHost/TabHost.test.tsx b/web/packages/teleterm/src/ui/TabHost/TabHost.test.tsx
index 7410532aff073..55b3cfc9abd35 100644
--- a/web/packages/teleterm/src/ui/TabHost/TabHost.test.tsx
+++ b/web/packages/teleterm/src/ui/TabHost/TabHost.test.tsx
@@ -31,6 +31,7 @@ import {
rootClusterUri,
} from 'teleterm/services/tshd/testHelpers';
import { routing } from 'teleterm/ui/uri';
+import { ResourcesContextProvider } from 'teleterm/ui/DocumentCluster/resourcesContext';
function getMockDocuments(): Document[] {
return [
@@ -86,7 +87,9 @@ async function getTestSetup({ documents }: { documents: Document[] }) {
render(
-
+
+
+
);