Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connect My Computer: Move icon to the top bar #31751

Merged
merged 3 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion web/packages/teleterm/src/mainProcess/windowsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class WindowsManager {
width: windowState.width,
height: windowState.height,
backgroundColor: activeTheme.colors.levels.sunken,
minWidth: 400,
minWidth: 490,
minHeight: 300,
show: false,
autoHideMenuBar: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,10 @@

import React from 'react';

import { Flex } from 'design';

import { wait } from 'shared/utils/wait';

import { MockWorkspaceContextProvider } from 'teleterm/ui/fixtures/MockWorkspaceContextProvider';
import { makeRootCluster } from 'teleterm/services/tshd/testHelpers';
import * as types from 'teleterm/ui/services/workspacesService';
import { MockAppContext } from 'teleterm/ui/fixtures/mocks';
import { MockAppContextProvider } from 'teleterm/ui/fixtures/MockAppContextProvider';

Expand Down Expand Up @@ -123,20 +120,6 @@ function ShowState({
const cluster = makeRootCluster({
features: { isUsageBasedBilling: true, advancedAccessWorkflows: false },
});
cluster.loggedInUser.acl.tokens = {
create: true,
use: true,
read: true,
list: true,
edit: true,
pb_delete: true,
};
const doc: types.DocumentConnectMyComputer = {
kind: 'doc.connect_my_computer',
rootClusterUri: cluster.uri,
title: 'Connect My Computer',
uri: '/docs/123',
};
const appContext = new MockAppContext();
appContext.clustersService.state.clusters.set(cluster.uri, cluster);
appContext.configService = createMockConfigService({
Expand All @@ -146,8 +129,8 @@ function ShowState({
draftState.rootClusterUri = cluster.uri;
draftState.workspaces[cluster.uri] = {
localClusterUri: cluster.uri,
documents: [doc],
location: doc.uri,
documents: [],
location: undefined,
accessRequests: undefined,
};
});
Expand All @@ -160,9 +143,7 @@ function ShowState({
<MockAppContextProvider appContext={appContext}>
<MockWorkspaceContextProvider rootClusterUri={cluster.uri}>
<ConnectMyComputerContextProvider rootClusterUri={cluster.uri}>
<Flex justifyContent="flex-end">
<NavigationMenu clusterUri={cluster.uri} />
</Flex>
<NavigationMenu />
</ConnectMyComputerContextProvider>
</MockWorkspaceContextProvider>
</MockAppContextProvider>
Expand Down
89 changes: 41 additions & 48 deletions web/packages/teleterm/src/ui/ConnectMyComputer/NavigationMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ import { Laptop, Warning } from 'design/Icon';

import { Attempt, AttemptStatus } from 'shared/hooks/useAsync';

import { useAppContext } from 'teleterm/ui/appContextProvider';
import { ClusterUri } from 'teleterm/ui/uri';
import { useWorkspaceContext } from 'teleterm/ui/Documents';
import { assertUnreachable } from 'teleterm/ui/utils';

Expand All @@ -31,32 +29,24 @@ import {
useConnectMyComputerContext,
} from './connectMyComputerContext';

interface NavigationMenuProps {
clusterUri: ClusterUri;
}

/**
* IndicatorStatus combines a couple of different states into a single enum which dictates the
* decorative look of NavigationMenu.
*/
type IndicatorStatus = AttemptStatus;

export function NavigationMenu(props: NavigationMenuProps) {
export function NavigationMenu() {
const iconRef = useRef();
const [isMenuOpened, setIsMenuOpened] = useState(false);
const appCtx = useAppContext();
const { documentsService, rootClusterUri } = useWorkspaceContext();
const { isAgentConfiguredAttempt, currentAction, canUse } =
useConnectMyComputerContext();
// DocumentCluster renders this component only if the cluster exists.
const cluster = appCtx.clustersService.findCluster(props.clusterUri);
const indicatorStatus = getIndicatorStatus(
currentAction,
isAgentConfiguredAttempt
);

// Don't show the navigation icon for leaf clusters.
if (cluster.leaf || !canUse) {
if (!canUse) {
return null;
}

Expand Down Expand Up @@ -94,12 +84,11 @@ export function NavigationMenu(props: NavigationMenuProps) {
getContentAnchorEl={null}
open={isMenuOpened}
anchorEl={iconRef.current}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
onClose={() => setIsMenuOpened(false)}
menuListCss={() =>
css`
width: 150px;
display: flex;
flex-direction: column;
`
Expand Down Expand Up @@ -179,41 +168,45 @@ export const MenuIcon = forwardRef<HTMLDivElement, MenuIconProps>(
const indicatorStatusToStyledStatus = (
indicatorStatus: IndicatorStatus
): JSX.Element => {
switch (indicatorStatus) {
case '': {
return null;
}
case 'processing': {
return (
<StyledStatus
bg="success"
css={`
@keyframes blink {
0% {
opacity: 0;
}
50% {
opacity: 100%;
}
100% {
opacity: 0;
}
}

animation: blink 1.4s ease-in-out infinite;
`}
/>
);
}
case 'error': {
return <StyledStatus bg="error.main" />;
}
case 'success': {
return <StyledStatus bg="success" />;
}
}
return (
<StyledStatus
status={indicatorStatus}
css={`
@keyframes blink {
0% {
opacity: 0;
}
50% {
opacity: 100%;
}
100% {
opacity: 0;
}
}

animation: blink 1.4s ease-in-out;
animation-iteration-count: ${props => {
const hasFinished =
props.status === 'success' || props.status === 'error';
return hasFinished ? '0' : 'infinite';
}};
visibility: ${props => (props.status === '' ? 'hidden' : 'visible')};
background: ${props => getIndicatorColor(props.status, props.theme)};
`}
/>
);
};

function getIndicatorColor(status: IndicatorStatus, theme: any): string {
switch (status) {
case 'processing':
case 'success':
return theme.colors.success;
case 'error':
return theme.colors.error.main;
}
}

const StyledButton = styled(Button)`
position: relative;
background: ${props => props.theme.colors.spotBackground[0]};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import styled from 'styled-components';
import { Flex } from 'design';

import { useClusterContext } from 'teleterm/ui/DocumentCluster/clusterContext';
import { ConnectMyComputerNavigationMenu } from 'teleterm/ui/ConnectMyComputer';

import SideNav from './SideNav';
import Servers from './Servers';
Expand All @@ -42,10 +41,7 @@ export default function ClusterResources() {
return (
<StyledMain>
<Flex pb={5} flexDirection="column">
<Flex justifyContent="space-between">
<SideNav mb={2} />
<ConnectMyComputerNavigationMenu clusterUri={clusterCtx.clusterUri} />
</Flex>
<SideNav mb={2} />
<HorizontalSplit>
{clusterCtx.isLocationActive('/resources/servers') && <Servers />}
{clusterCtx.isLocationActive('/resources/databases') && <Databases />}
Expand Down
12 changes: 11 additions & 1 deletion web/packages/teleterm/src/ui/Documents/DocumentsRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import React, { useMemo } from 'react';
import { createPortal } from 'react-dom';

import styled from 'styled-components';
/* eslint-disable @typescript-eslint/ban-ts-comment*/
Expand All @@ -35,6 +36,7 @@ import { DocumentTerminal } from 'teleterm/ui/DocumentTerminal';
import {
ConnectMyComputerContextProvider,
DocumentConnectMyComputer,
ConnectMyComputerNavigationMenu,
} from 'teleterm/ui/ConnectMyComputer';
import { DocumentGatewayKube } from 'teleterm/ui/DocumentGatewayKube';

Expand All @@ -44,7 +46,9 @@ import { RootClusterUri } from 'teleterm/ui/uri';
import { WorkspaceContextProvider } from './workspaceContext';
import { KeyboardShortcutsPanel } from './KeyboardShortcutsPanel';

export function DocumentsRenderer() {
export function DocumentsRenderer(props: {
topBarContainerRef: React.MutableRefObject<HTMLDivElement>;
}) {
const { workspacesService } = useAppContext();

function renderDocuments(documentsService: DocumentsService) {
Expand Down Expand Up @@ -87,6 +91,12 @@ export function DocumentsRenderer() {
) : (
<KeyboardShortcutsPanel />
)}
{workspace.rootClusterUri ===
workspacesService.getRootClusterUri() &&
createPortal(
<ConnectMyComputerNavigationMenu />,
props.topBarContainerRef?.current
)}
</ConnectMyComputerContextProvider>
</WorkspaceContextProvider>
</DocumentsContainer>
Expand Down
8 changes: 5 additions & 3 deletions web/packages/teleterm/src/ui/LayoutManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React, { useRef } from 'react';
import { Flex } from 'design';
/* eslint-disable @typescript-eslint/ban-ts-comment*/
// @ts-ignore
Expand All @@ -25,17 +25,19 @@ import { StatusBar } from 'teleterm/ui/StatusBar';
import { NotificationsHost } from 'teleterm/ui/components/Notifcations';

export function LayoutManager() {
const topBarContainerRef = useRef<HTMLDivElement>();

return (
<Flex flex="1" flexDirection="column" minHeight={0}>
<TopBar />
<TopBar topBarContainerRef={topBarContainerRef} />
<Flex
flex="1"
minHeight={0}
css={`
position: relative;
`}
>
<TabHostContainer />
<TabHostContainer topBarContainerRef={topBarContainerRef} />
<NotificationsHost />
</Flex>
<AccessRequestCheckout />
Expand Down
2 changes: 1 addition & 1 deletion web/packages/teleterm/src/ui/TabHost/TabHost.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ function getTestSetup({ documents }: { documents: Document[] }) {

const utils = render(
<MockAppContextProvider appContext={appContext}>
<TabHost ctx={appContext} />
<TabHost ctx={appContext} topBarContainerRef={undefined} />
</MockAppContextProvider>
);

Expand Down
16 changes: 12 additions & 4 deletions web/packages/teleterm/src/ui/TabHost/TabHost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,26 @@ import { useTabShortcuts } from './useTabShortcuts';
import { useNewTabOpener } from './useNewTabOpener';
import { ClusterConnectPanel } from './ClusterConnectPanel/ClusterConnectPanel';

export function TabHostContainer() {
export function TabHostContainer(props: {
topBarContainerRef: React.MutableRefObject<HTMLDivElement>;
}) {
const ctx = useAppContext();
ctx.workspacesService.useState();
const isRootClusterSelected = !!ctx.workspacesService.getRootClusterUri();

if (isRootClusterSelected) {
return <TabHost ctx={ctx} />;
return <TabHost ctx={ctx} topBarContainerRef={props.topBarContainerRef} />;
}
return <ClusterConnectPanel />;
}

export function TabHost({ ctx }: { ctx: IAppContext }) {
export function TabHost({
ctx,
topBarContainerRef,
}: {
ctx: IAppContext;
topBarContainerRef: React.MutableRefObject<HTMLDivElement>;
}) {
const documentsService =
ctx.workspacesService.getActiveWorkspaceDocumentService();
const activeDocument = documentsService?.getActive();
Expand Down Expand Up @@ -111,7 +119,7 @@ export function TabHost({ ctx }: { ctx: IAppContext }) {
closeTabTooltip={getLabelWithAccelerator('Close', 'closeTab')}
/>
</Flex>
<DocumentsRenderer />
<DocumentsRenderer topBarContainerRef={topBarContainerRef} />
</StyledTabHost>
);
}
Expand Down
6 changes: 5 additions & 1 deletion web/packages/teleterm/src/ui/TopBar/TopBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ import { Clusters } from './Clusters';
import { Identity } from './Identity';
import { AdditionalActions } from './AdditionalActions';

export function TopBar() {
export function TopBar(props: {
topBarContainerRef: React.MutableRefObject<HTMLDivElement>;
}) {
return (
<Grid>
<JustifyLeft>
<Connections />
<div ref={props.topBarContainerRef} />
</JustifyLeft>
<CentralContainer>
<Clusters />
Expand Down Expand Up @@ -61,6 +64,7 @@ const CentralContainer = styled(Flex).attrs({ gap: 3 })`

const JustifyLeft = styled(Flex).attrs({ gap: 3 })`
align-items: center;
min-width: 80px; // reserves space for CMC icon to prevent layout shifting
height: 100%;
`;

Expand Down