diff --git a/packages/manager/.changeset/pr-11217-fixed-1730913381751.md b/packages/manager/.changeset/pr-11217-fixed-1730913381751.md new file mode 100644 index 00000000000..8763a003d48 --- /dev/null +++ b/packages/manager/.changeset/pr-11217-fixed-1730913381751.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +Kubernetes details page UI issues ([#11217](https://github.com/linode/manager/pull/11217)) diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeClusterSpecs.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeClusterSpecs.tsx index d7f2407acb3..ac02162e06e 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeClusterSpecs.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeClusterSpecs.tsx @@ -32,7 +32,6 @@ interface Props { const useStyles = makeStyles()((theme: Theme) => ({ iconTextOuter: { - flexBasis: '72%', minWidth: 115, }, item: { @@ -45,16 +44,6 @@ const useStyles = makeStyles()((theme: Theme) => ({ paddingBottom: theme.spacing(1), paddingTop: theme.spacing(1), }, - mainGridContainer: { - position: 'relative', - [theme.breakpoints.up('lg')]: { - justifyContent: 'space-between', - }, - }, - root: { - marginBottom: theme.spacing(3), - padding: `${theme.spacing(2.5)} ${theme.spacing(2.5)} ${theme.spacing(3)}`, - }, tooltip: { '& .MuiTooltip-tooltip': { minWidth: 320, @@ -147,7 +136,7 @@ export const KubeClusterSpecs = React.memo((props: Props) => { }; return ( - + {kubeSpecsLeft.map(kubeSpecItem)} {kubeSpecsRight.map(kubeSpecItem)} diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeConfigDisplay.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeConfigDisplay.tsx index 810a4c92446..cb48ac45077 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeConfigDisplay.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeConfigDisplay.tsx @@ -1,5 +1,4 @@ import { Box } from '@linode/ui'; -import Grid from '@mui/material/Unstable_Grid2'; import { useSnackbar } from 'notistack'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; @@ -8,6 +7,7 @@ import DetailsIcon from 'src/assets/icons/code-file.svg'; import DownloadIcon from 'src/assets/icons/lke-download.svg'; import ResetIcon from 'src/assets/icons/reset.svg'; import { MaskableText } from 'src/components/MaskableText/MaskableText'; +import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; import { useAllKubernetesClusterAPIEndpointsQuery, @@ -135,8 +135,8 @@ export const KubeConfigDisplay = (props: Props) => { }; return ( - <> - + + Kubernetes API Endpoint: @@ -147,8 +147,8 @@ export const KubeConfigDisplay = (props: Props) => { endpointsLoading, endpointsError?.[0].reason )} - - + + Kubeconfig: @@ -189,7 +189,7 @@ export const KubeConfigDisplay = (props: Props) => { - - > + + ); }; diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeSummaryPanel.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeSummaryPanel.tsx index 6b4cf458e50..6e6c9d40042 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeSummaryPanel.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeSummaryPanel.tsx @@ -1,7 +1,6 @@ import { Box } from '@linode/ui'; import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import { useTheme } from '@mui/material/styles'; -import Grid from '@mui/material/Unstable_Grid2'; import { useSnackbar } from 'notistack'; import * as React from 'react'; @@ -29,9 +28,10 @@ import { KubeConfigDisplay } from './KubeConfigDisplay'; import { KubeConfigDrawer } from './KubeConfigDrawer'; import { KubeControlPlaneACLDrawer } from './KubeControlPaneACLDrawer'; import { KubeEntityDetailFooter } from './KubeEntityDetailFooter'; -import { StyledActionRowGrid } from './KubeSummaryPanel.styles'; import type { KubernetesCluster } from '@linode/api-v4/lib/kubernetes'; +import { Hidden } from 'src/components/Hidden'; +import { ActionMenu } from 'src/components/ActionMenu/ActionMenu'; interface Props { cluster: KubernetesCluster; @@ -95,55 +95,32 @@ export const KubeSummaryPanel = React.memo((props: Props) => { setDrawerOpen(true); }; - const sxSpacing = { - paddingLeft: theme.spacing(3), - paddingRight: theme.spacing(1), - }; - - const sxMainGridContainer = { - paddingBottom: theme.spacing(2.5), - paddingTop: theme.spacing(2), - position: 'relative', - }; - return ( - + + - - + {cluster.control_plane.high_availability && ( + ({ + borderColor: theme.color.green, + position: 'absolute', + right: theme.spacing(3), + })} + label="HA CLUSTER" + size="small" + variant="outlined" /> - - - - {cluster.control_plane.high_availability && ( - ({ borderColor: theme.color.green })} - variant="outlined" - /> - )} - - - + )} + } footer={ { Summary - - { - window.open(dashboard?.url, '_blank'); - }} - sx={{ - '& svg': { - height: '14px', - marginLeft: '4px', - }, - alignItems: 'center', - display: 'flex', - }} - disabled={Boolean(dashboardError) || !dashboard} - > - Kubernetes Dashboard - - - setIsDeleteDialogOpen(true)} - > - Delete Cluster - + + + window.open(dashboard?.url, '_blank'), + title: 'Kubernetes Dashboard', + }, + { + onClick: () => setIsDeleteDialogOpen(true), + title: 'Delete Cluster', + }, + ]} + ariaLabel={`Action menu for Kubernetes Cluster ${cluster.label}`} + /> + + + } + onClick={() => window.open(dashboard?.url, '_blank')} + > + Kubernetes Dashboard + + setIsDeleteDialogOpen(true)}> + Delete Cluster + + } - noBodyBottomBorder /> { will no longer be able to access this cluster via your previous Kubeconfig file. This action cannot be undone. - + ); }); diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubernetesClusterDetail.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubernetesClusterDetail.tsx index e495d7db708..db046030fbe 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubernetesClusterDetail.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubernetesClusterDetail.tsx @@ -1,4 +1,4 @@ -import Grid from '@mui/material/Unstable_Grid2'; +import { Box } from '@linode/ui'; import { createLazyRoute } from '@tanstack/react-router'; import * as React from 'react'; import { useLocation, useParams } from 'react-router-dom'; @@ -7,6 +7,7 @@ import { CircleProgress } from 'src/components/CircleProgress'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { LandingHeader } from 'src/components/LandingHeader'; +import { Stack } from 'src/components/Stack'; import { getKubeHighAvailability } from 'src/features/Kubernetes/kubeUtils'; import { useAPLAvailability } from 'src/features/Kubernetes/kubeUtils'; import { useAccount } from 'src/queries/account/account'; @@ -78,16 +79,13 @@ export const KubernetesClusterDetail = () => { }; return ( - <> + - - - - + { docsLink="https://techdocs.akamai.com/cloud-computing/docs/getting-started-with-lke-linode-kubernetes-engine" title="Kubernetes Cluster Details" /> - + - - {showAPL && cluster.apl_enabled && ( - <> - - + {showAPL && cluster.apl_enabled && ( + + - - > - )} - + + )} - + setIsUpgradeToHAOpen(false)} open={isUpgradeToHAOpen} regionID={cluster.region} /> - > + ); }; diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePool.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePool.tsx index 6f91f150b1c..dcd5bc3beb9 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePool.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePool.tsx @@ -1,9 +1,8 @@ import { Box, Paper, Tooltip } from '@linode/ui'; -import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; -import { makeStyles } from 'tss-react/mui'; import { StyledActionButton } from 'src/components/Button/StyledActionButton'; +import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; import { NodeTable } from './NodeTable'; @@ -13,7 +12,8 @@ import type { PoolNodeResponse, } from '@linode/api-v4/lib/kubernetes'; import type { EncryptionStatus } from '@linode/api-v4/lib/linodes/types'; -import type { Theme } from '@mui/material/styles'; +import { Hidden } from 'src/components/Hidden'; +import { ActionMenu } from 'src/components/ActionMenu/ActionMenu'; interface Props { autoscaler: AutoscaleSettings; @@ -29,19 +29,6 @@ interface Props { typeLabel: string; } -const useStyles = makeStyles()((theme: Theme) => ({ - autoscaleText: { - alignSelf: 'center', - paddingRight: theme.spacing(2), - }, - button: { - paddingRight: 8, - }, - deletePoolBtn: { - paddingRight: 8, - }, -})); - export const NodePool = (props: Props) => { const { autoscaler, @@ -57,10 +44,8 @@ export const NodePool = (props: Props) => { typeLabel, } = props; - const { classes } = useStyles(); - return ( - + { py: 0, }} > - - {typeLabel} - - - openAutoscalePoolDialog(poolId)} - > - Autoscale Pool - - {autoscaler.enabled ? ( - - {`(Min ${autoscaler.min} / Max ${autoscaler.max})`} - - ) : null} - handleClickResize(poolId)} - > - Resize Pool - - openRecycleAllNodesDialog(poolId)} - > - Recycle Pool Nodes - - - - openDeletePoolDialog(poolId)} - > - Delete Pool - - - - + {typeLabel} + + openAutoscalePoolDialog(poolId), + title: 'Autoscale Pool', + }, + { + onClick: () => handleClickResize(poolId), + title: 'Resize Pool', + }, + { + onClick: () => openRecycleAllNodesDialog(poolId), + title: 'Recycle Pool Nodes', + }, + { + disabled: isOnlyNodePool, + onClick: () => openDeletePoolDialog(poolId), + title: 'Delete Pool', + tooltip: isOnlyNodePool + ? 'Clusters must contain at least one node pool.' + : undefined, + }, + ]} + ariaLabel={`Action menu for Node Pool ${poolId}`} + /> + + + + openAutoscalePoolDialog(poolId)} + > + Autoscale Pool + + {autoscaler.enabled && ( + + (Min {autoscaler.min} / Max {autoscaler.max}) + + )} + handleClickResize(poolId)} + > + Resize Pool + + openRecycleAllNodesDialog(poolId)} + > + Recycle Pool Nodes + + + + openDeletePoolDialog(poolId)} + > + Delete Pool + + + + + { poolId={poolId} typeLabel={typeLabel} /> - + ); }; diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePoolsDisplay.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePoolsDisplay.tsx index 99a5957085d..3976191515b 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePoolsDisplay.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePoolsDisplay.tsx @@ -1,7 +1,5 @@ -import Grid from '@mui/material/Unstable_Grid2'; import React, { useState } from 'react'; import { Waypoint } from 'react-waypoint'; -import { makeStyles } from 'tss-react/mui'; import { Button } from 'src/components/Button/Button'; import { CircleProgress } from 'src/components/CircleProgress'; @@ -22,37 +20,6 @@ import { RecycleNodeDialog } from './RecycleNodeDialog'; import { ResizeNodePoolDrawer } from './ResizeNodePoolDrawer'; import type { Region } from '@linode/api-v4'; -import type { Theme } from '@mui/material/styles'; - -const useStyles = makeStyles()((theme: Theme) => ({ - button: { - marginBottom: theme.spacing(), - marginLeft: theme.spacing(), - }, - displayTable: { - '& > div': { - marginBottom: theme.spacing(3), - }, - '& > div:last-child': { - marginBottom: 0, - }, - padding: '8px 8px 0px', - width: '100%', - }, - nodePoolHeader: { - marginBottom: theme.spacing(), - [theme.breakpoints.only('sm')]: { - marginLeft: theme.spacing(), - }, - [theme.breakpoints.only('xs')]: { - marginLeft: theme.spacing(), - }, - }, - nodePoolHeaderOuter: { - alignItems: 'center', - display: 'flex', - }, -})); export interface Props { clusterID: number; @@ -63,7 +30,6 @@ export interface Props { export const NodePoolsDisplay = (props: Props) => { const { clusterID, clusterLabel, clusterRegionId, regionsData } = props; - const { classes, cx } = useStyles(); const { data: pools, @@ -117,138 +83,118 @@ export const NodePoolsDisplay = (props: Props) => { return ( <> - - - - Node Pools - - - + Node Pools + setIsRecycleClusterOpen(true)} > Recycle All Nodes - + Add a Node Pool - - - - {poolsError ? ( - - ) : ( - - - {_pools?.map((thisPool) => { - const { disk_encryption, id, nodes } = thisPool; - - const thisPoolType = types?.find( - (thisType) => thisType.id === thisPool.type - ); - - const typeLabel = - thisPoolType?.formattedLabel ?? 'Unknown type'; - - return ( - ({ paddingBottom: theme.spacing(2) })} - > - { - setSelectedPoolId(poolId); - setIsAutoscaleDialogOpen(true); - }} - openDeletePoolDialog={(id) => { - setSelectedPoolId(id); - setIsDeleteNodePoolOpen(true); - }} - openRecycleAllNodesDialog={(id) => { - setSelectedPoolId(id); - setIsRecycleAllPoolNodesOpen(true); - }} - openRecycleNodeDialog={(nodeId, linodeLabel) => { - setSelectedNodeId(nodeId); - setIsRecycleNodeOpen(true); - }} - autoscaler={thisPool.autoscaler} - encryptionStatus={disk_encryption} - handleClickResize={handleOpenResizeDrawer} - isOnlyNodePool={pools?.length === 1} - nodes={nodes ?? []} - poolId={thisPool.id} - typeLabel={typeLabel} - /> - - ); - })} - {pools?.length > numPoolsToDisplay && ( - - - - )} - - - setAddDrawerOpen(false)} - open={addDrawerOpen} - regionsData={regionsData} - /> - setIsResizeDrawerOpen(false)} - open={isResizeDrawerOpen} - /> - setIsDeleteNodePoolOpen(false)} - open={isDeleteNodePoolOpen} - /> - setIsAutoscaleDialogOpen(false)} - open={isAutoscaleDialogOpen} - /> - setIsRecycleNodeOpen(false)} - open={isRecycleNodeOpen} - /> - setIsRecycleAllPoolNodesOpen(false)} - open={isRecycleAllPoolNodesOpen} - /> - setIsRecycleClusterOpen(false)} - open={isRecycleClusterOpen} + + + {poolsError && } + + {_pools?.map((thisPool) => { + const { disk_encryption, id, nodes } = thisPool; + + const thisPoolType = types?.find( + (thisType) => thisType.id === thisPool.type + ); + + const typeLabel = thisPoolType?.formattedLabel ?? 'Unknown type'; + + return ( + { + setSelectedPoolId(poolId); + setIsAutoscaleDialogOpen(true); + }} + openDeletePoolDialog={(id) => { + setSelectedPoolId(id); + setIsDeleteNodePoolOpen(true); + }} + openRecycleAllNodesDialog={(id) => { + setSelectedPoolId(id); + setIsRecycleAllPoolNodesOpen(true); + }} + openRecycleNodeDialog={(nodeId, linodeLabel) => { + setSelectedNodeId(nodeId); + setIsRecycleNodeOpen(true); + }} + autoscaler={thisPool.autoscaler} + encryptionStatus={disk_encryption} + handleClickResize={handleOpenResizeDrawer} + isOnlyNodePool={pools?.length === 1} + key={id} + nodes={nodes ?? []} + poolId={thisPool.id} + typeLabel={typeLabel} /> - - )} + ); + })} + {pools?.length > numPoolsToDisplay && ( + + + + )} + setAddDrawerOpen(false)} + open={addDrawerOpen} + regionsData={regionsData} + /> + setIsResizeDrawerOpen(false)} + open={isResizeDrawerOpen} + /> + setIsDeleteNodePoolOpen(false)} + open={isDeleteNodePoolOpen} + /> + setIsAutoscaleDialogOpen(false)} + open={isAutoscaleDialogOpen} + /> + setIsRecycleNodeOpen(false)} + open={isRecycleNodeOpen} + /> + setIsRecycleAllPoolNodesOpen(false)} + open={isRecycleAllPoolNodesOpen} + /> + setIsRecycleClusterOpen(false)} + open={isRecycleClusterOpen} + /> > ); }; diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeRow.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeRow.tsx index 2aba5674d8a..738c9249a60 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeRow.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeRow.tsx @@ -1,3 +1,4 @@ +import { Box } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { Link } from 'react-router-dom'; @@ -5,13 +6,13 @@ import { Link } from 'react-router-dom'; import { CopyTooltip } from 'src/components/CopyTooltip/CopyTooltip'; import { StatusIcon } from 'src/components/StatusIcon/StatusIcon'; import { TableCell } from 'src/components/TableCell'; +import { TableRow } from 'src/components/TableRow'; import { Typography } from 'src/components/Typography'; import { transitionText } from 'src/features/Linodes/transitions'; import { useInProgressEvents } from 'src/queries/events/events'; import { usePreferences } from 'src/queries/profile/preferences'; import NodeActionMenu from './NodeActionMenu'; -import { StyledCopyTooltip, StyledTableRow } from './NodeTable.styles'; import type { APIError } from '@linode/api-v4/lib/types'; @@ -73,7 +74,7 @@ export const NodeRow = React.memo((props: NodeRowProps) => { const displayIP = ip ?? ''; return ( - + @@ -113,15 +114,22 @@ export const NodeRow = React.memo((props: NodeRowProps) => { Error retrieving IP ) : displayIP.length > 0 ? ( - <> + - - > + + ) : null} @@ -131,6 +139,6 @@ export const NodeRow = React.memo((props: NodeRowProps) => { openRecycleNodeDialog={openRecycleNodeDialog} /> - + ); }); diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable.styles.ts b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable.styles.ts index 97675764fff..b00eacbf634 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable.styles.ts +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable.styles.ts @@ -2,32 +2,8 @@ import { styled } from '@mui/material/styles'; import VerticalDivider from 'src/assets/icons/divider-vertical.svg'; import { CopyTooltip } from 'src/components/CopyTooltip/CopyTooltip'; -import { Table } from 'src/components/Table'; -import { TableRow } from 'src/components/TableRow'; import { Typography } from 'src/components/Typography'; -export const StyledTableRow = styled(TableRow, { - label: 'TableRow', -})(({ theme }) => ({ - '& svg': { - height: `12px`, - width: `12px`, - }, - '&:hover': { - backgroundColor: theme.bg.lightBlue1, - }, - [`&:hover .copy-tooltip > svg, & .copy-tooltip:focus > svg`]: { - opacity: 1, - }, - marginLeft: 4, -})); - -export const StyledTable = styled(Table, { - label: 'Table', -})(({ theme }) => ({ - borderLeft: `1px solid ${theme.borderColors.borderTable}`, - borderRight: `1px solid ${theme.borderColors.borderTable}`, -})); export const StyledCopyTooltip = styled(CopyTooltip, { label: 'CopyTooltip', diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable.tsx index c65f67dde39..ec38258918f 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodeTable.tsx @@ -8,6 +8,7 @@ import { useIsDiskEncryptionFeatureEnabled } from 'src/components/Encryption/uti import OrderBy from 'src/components/OrderBy'; import Paginate from 'src/components/Paginate'; import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFooter'; +import { Table } from 'src/components/Table'; import { TableBody } from 'src/components/TableBody'; import { TableCell } from 'src/components/TableCell'; import { TableContentWrapper } from 'src/components/TableContentWrapper/TableContentWrapper'; @@ -20,11 +21,7 @@ import { Typography } from 'src/components/Typography'; import { useAllLinodesQuery } from 'src/queries/linodes/linodes'; import { NodeRow as _NodeRow } from './NodeRow'; -import { - StyledTable, - StyledTypography, - StyledVerticalDivider, -} from './NodeTable.styles'; +import { StyledTypography, StyledVerticalDivider } from './NodeTable.styles'; import type { NodeRow } from './NodeRow'; import type { PoolNodeResponse } from '@linode/api-v4/lib/kubernetes'; @@ -70,7 +67,7 @@ export const NodeTable = React.memo((props: Props) => { pageSize, }) => ( <> - + { - +