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

Cluster table click away #351

Merged
merged 6 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
27 changes: 0 additions & 27 deletions components/clusterTable/clusterTable.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import React, { FunctionComponent } from 'react';
import { Meta, StoryObj } from '@storybook/react';

import { noop } from '../../utils/noop';
import { mapClusterFromRaw } from '../../utils/mapClustersFromRaw';
import { mockClusterResponse } from '../../tests/mocks/mockClusterResponse';
import { useAppDispatch, useAppSelector } from '../../redux/store';
import { setPresentedClusterId } from '../../redux/slices/api.slice';

import { ClusterTable } from './clusterTable';

Expand All @@ -17,33 +14,9 @@ const meta: Meta<typeof ClusterTable> = {

export default meta;

const ClusterTableWithHooks: FunctionComponent = () => {
const { presentedClusterId, managementCluster, clusterMap } = useAppSelector(({ api }) => api);

const dispatch = useAppDispatch();

return managementCluster ? (
<ClusterTable
clusters={clusterMap}
managementCluster={managementCluster}
presentedClusterId={presentedClusterId}
onMenuButtonClick={(presentedClusterId) =>
dispatch(setPresentedClusterId(presentedClusterId))
}
onDeleteCluster={noop}
/>
) : (
<div>No management cluster</div>
);
};

export const Default: StoryObj<typeof ClusterTable> = {
args: {
onDeleteCluster: noop,
managementCluster,
},
};

export const WithSelectableRows: StoryObj<typeof ClusterTable> = {
render: () => <ClusterTableWithHooks />,
};
78 changes: 35 additions & 43 deletions components/clusterTable/clusterTable.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useState, FunctionComponent, useMemo, ComponentPropsWithRef } from 'react';
import { ClickAwayListener } from '@mui/material';
import TableHead from '@mui/material/TableHead';
import IconButton from '@mui/material/IconButton';
import List from '@mui/material/List';
Expand Down Expand Up @@ -46,6 +47,8 @@ import {
StyledTable,
} from './clusterTable.styled';

import useToggle from '@/hooks/useToggle';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const CLOUD_LOGO_OPTIONS: Record<InstallationType, any> = {
[InstallationType.LOCAL]: k3dLogo,
Expand All @@ -67,9 +70,7 @@ type ClusterRowProps = {
expanded?: boolean;
showExpandButton?: boolean;
onExpanseClick?: () => void;
onDeleteCluster: () => void;
presentedClusterId?: string;
onMenuButtonClick?: (clusterId: Cluster['clusterId']) => void;
onDeleteCluster: (clusterId: string) => void;
};

const ClusterRow: FunctionComponent<ClusterRowProps> = ({
Expand All @@ -78,9 +79,9 @@ const ClusterRow: FunctionComponent<ClusterRowProps> = ({
showExpandButton,
onExpanseClick = noop,
onDeleteCluster,
onMenuButtonClick = noop,
presentedClusterId,
}) => {
const { isOpen, close, toggle } = useToggle();

const {
clusterId,
clusterName,
Expand All @@ -98,12 +99,10 @@ const ClusterRow: FunctionComponent<ClusterRowProps> = ({
const { iconLabel, iconType, bgColor } = CLUSTER_TAG_CONFIG[status ?? ClusterStatus.PROVISIONED];
const { nameLabel, typeLabel } = FORMATTED_CLUSTER_TYPE[type ?? ClusterType.MANAGEMENT];

const selected = clusterId === presentedClusterId;

return (
<>
<StyledTableRow selected={selected}>
<StyledTableCell align="right" style={{ width: '50px' }} selected={selected}>
<StyledTableRow selected={isOpen}>
<StyledTableCell align="right" style={{ width: '50px' }} selected={isOpen}>
{type === ClusterType.MANAGEMENT && showExpandButton && (
<StyledIconButton
aria-label="expand row"
Expand All @@ -115,64 +114,66 @@ const ClusterRow: FunctionComponent<ClusterRowProps> = ({
</StyledIconButton>
)}
</StyledTableCell>
<StyledTableCell scope="row" selected={selected}>
<StyledTableCell scope="row" selected={isOpen}>
<StyledCellText variant="body2" style={{ fontWeight: 500 }}>
{clusterName}
</StyledCellText>
<StyledCellText variant="body2" style={{ color: DODGER_BLUE }}>
{nameLabel}
</StyledCellText>
</StyledTableCell>
<StyledTableCell selected={selected}>
<StyledTableCell selected={isOpen}>
<StyledCellText variant="body2">{typeLabel}</StyledCellText>
</StyledTableCell>
<StyledTableCell selected={selected}>
<StyledTableCell selected={isOpen}>
<StyledCellText variant="body2">
{environment && <Tag text={environment.name ?? ''} bgColor={environment.color} />}
</StyledCellText>
</StyledTableCell>
<StyledTableCell align="left" selected={selected}>
<StyledTableCell align="left" selected={isOpen}>
<Image src={cloudLogoSrc} height={18} width={30} alt={cloudProvider ?? ''} />
</StyledTableCell>
<StyledTableCell selected={selected}>
<StyledTableCell selected={isOpen}>
<StyledCellText variant="body2">{cloudRegion}</StyledCellText>
</StyledTableCell>
<StyledTableCell align="center" selected={selected}>
<StyledTableCell align="center" selected={isOpen}>
<StyledCellText variant="body2">{nodeCount}</StyledCellText>
</StyledTableCell>
<StyledTableCell selected={selected}>
<StyledTableCell selected={isOpen}>
{creationDate && (
<StyledCellText variant="body2">
{moment(+creationDate).format('DD MMM YYYY')}
</StyledCellText>
)}
</StyledTableCell>
<StyledTableCell selected={selected}>
<StyledTableCell selected={isOpen}>
<StyledCellText variant="body2">{gitUser}</StyledCellText>
</StyledTableCell>
<StyledTableCell selected={selected}>
<StyledTableCell selected={isOpen}>
<StyledTag text={iconLabel} bgColor={bgColor} icon={iconType} />
</StyledTableCell>
<StyledTableCell style={{ position: 'relative' }} selected={selected}>
<StyledTableCell style={{ position: 'relative' }} selected={isOpen}>
<IconButton
aria-label="more info"
onClick={() => onMenuButtonClick(clusterId)}
onClick={toggle}
disabled={status === ClusterStatus.DELETED}
>
<MoreHorizIcon />
</IconButton>
{selected && (
<Menu>
<List>
<ListItem disablePadding>
<ListItemButton onClick={onDeleteCluster}>
<Typography variant="body2" style={{ color: `${FIRE_BRICK}` }}>
Delete cluster
</Typography>
</ListItemButton>
</ListItem>
</List>
</Menu>
{isOpen && (
<ClickAwayListener onClickAway={close}>
<Menu>
<List>
<ListItem disablePadding>
<ListItemButton onClick={() => onDeleteCluster(clusterId)}>
<Typography variant="body2" style={{ color: `${FIRE_BRICK}` }}>
Delete cluster
</Typography>
</ListItemButton>
</ListItem>
</List>
</Menu>
</ClickAwayListener>
)}
</StyledTableCell>
</StyledTableRow>
Expand Down Expand Up @@ -256,19 +257,14 @@ const ClusterTableHead: FunctionComponent<ClusterTableHeadProps> = ({ orderBy, o
interface ClusterTableProps extends Omit<ComponentPropsWithRef<'tbody'>, 'key'> {
managementCluster: ManagementCluster;
clusters: ClusterCache;
onDeleteCluster: () => void;
onMenuButtonClick?: (clusterId: Cluster['clusterId']) => void;
presentedClusterId?: string;
onDeleteCluster: (clusterId: string) => void;
customRef?: React.Ref<HTMLTableSectionElement>;
}

export const ClusterTable: FunctionComponent<ClusterTableProps> = ({
managementCluster,
clusters,
onDeleteCluster,
onMenuButtonClick,
presentedClusterId,
customRef,
...rest
}) => {
const [expanded, setExpanded] = useState(true);
Expand Down Expand Up @@ -298,15 +294,13 @@ export const ClusterTable: FunctionComponent<ClusterTableProps> = ({
<StyledTableContainer {...rest}>
<StyledTable aria-label="collapsible table">
<ClusterTableHead onSort={handleRequestSort} order={order} orderBy={orderBy} />
<StyledTableBody ref={customRef}>
<StyledTableBody>
<ClusterRow
cluster={managementCluster}
onDeleteCluster={onDeleteCluster}
onMenuButtonClick={onMenuButtonClick}
expanded={expanded}
showExpandButton={!!filteredWorkloadClusters.length}
onExpanseClick={() => setExpanded(!expanded)}
presentedClusterId={presentedClusterId}
/>

{expanded &&
Expand All @@ -315,8 +309,6 @@ export const ClusterTable: FunctionComponent<ClusterTableProps> = ({
key={cluster.clusterName}
cluster={cluster}
onDeleteCluster={onDeleteCluster}
onMenuButtonClick={onMenuButtonClick}
presentedClusterId={presentedClusterId}
/>
))}
</StyledTableBody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ const ControlledNumberInputWithHooks = () => {
});
return (
<form onSubmit={() => action('onSubmit')}>
<ControlledNumberInput label="Number of nodes" name="numberOfNodes" control={control} />
<ControlledNumberInput
numberInputProps={{ defaultValue: 3 }}
label="Number of nodes"
name="numberOfNodes"
control={control}
/>
<button type="submit">Submit</button>
</form>
);
Expand Down
2 changes: 1 addition & 1 deletion components/deleteCluster/deleteCluster.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const DeleteClusterWithHooks: FunctionComponent<DeleteClusterProps> = (props) =>
const [open, setOpen] = useState(true);
return (
<>
<DeleteCluster {...props} isOpen={open} onClose={() => setOpen(false)} onDelete={noop} />
<DeleteCluster {...props} isOpen={open} onCloseModal={() => setOpen(false)} onDelete={noop} />
<Button variant="contained" color="primary" onClick={() => setOpen(!open)}>
{open ? 'Close' : 'Open'}
</Button>
Expand Down
19 changes: 6 additions & 13 deletions components/deleteCluster/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,26 @@ import LaunchOutlinedIcon from '@mui/icons-material/LaunchOutlined';

import Typography from '../typography';
import TextFieldWithRef from '../textField';
import Modal from '../modal';
import Modal, { IModalProps } from '../modal';
import { LAUGHING_ORANGE } from '../../constants/colors';
import Button from '../button';
import { Cluster, ClusterType, DraftCluster } from '../../types/provision';
import CopyText from '../copyText';

import { Content, CopyTextContainer, Footer, Header, NextLink } from './deleteCluster.styled';

export interface DeleteClusterProps {
export interface DeleteClusterProps extends Omit<IModalProps, 'children'> {
cluster: Cluster | DraftCluster;
isOpen: boolean;
onClose: () => void;
onDelete: () => void;
}

const DeleteCluster: FunctionComponent<DeleteClusterProps> = ({
cluster,
isOpen,
onClose,
onDelete,
}) => {
const DeleteCluster: FunctionComponent<DeleteClusterProps> = ({ cluster, onDelete, ...rest }) => {
const [matchingClusterName, setMatchingClusterName] = useState('');

const isManagementCluster = cluster.type === ClusterType.MANAGEMENT;

return (
<Modal isOpen={isOpen}>
<Modal {...rest}>
<Box sx={{ width: '500px', backgroundColor: 'white' }}>
<Header>
<ErrorOutlineIcon htmlColor={LAUGHING_ORANGE} />
Expand Down Expand Up @@ -70,12 +63,12 @@ const DeleteCluster: FunctionComponent<DeleteClusterProps> = ({
)}
</Content>
<Footer>
<Button variant="text" onClick={onClose} color="info">
<Button variant="text" onClick={rest.onCloseModal} color="info">
Cancel
</Button>
<Button
variant="contained"
onClick={isManagementCluster ? onClose : onDelete}
onClick={isManagementCluster ? rest.onCloseModal : onDelete}
color={isManagementCluster ? 'primary' : 'error'}
disabled={cluster.clusterName !== matchingClusterName && !isManagementCluster}
>
Expand Down
2 changes: 1 addition & 1 deletion components/numberInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const NumberInput = forwardRef<HTMLLabelElement | null, NumberInputProps>(functi
{ label, onChange = noop, inputProps = {}, ...rest },
ref,
) {
const [value, setValue] = useState(Number(inputProps.min ?? 0));
const [value, setValue] = useState(Number(inputProps.defaultValue ?? 0));
return (
<Root {...rest} ref={ref}>
{label && (
Expand Down
15 changes: 15 additions & 0 deletions constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { TagColor, TagIconOption } from '../components/tag';
import { ClusterStatus, ClusterType } from '../types/provision';

import { InstallationType } from '@/types/redux';

export const AWS_REGIONS = [
{ label: 'US East (Ohio) (us-east-2)', value: 'us-east-2' },
{ label: 'US East (N. Virginia) (us-east-1)', value: 'us-east-1' },
Expand Down Expand Up @@ -52,8 +54,21 @@ export const CLUSTER_TAG_CONFIG: Record<
};

export const MIN_NODE_COUNT = 1;
export const SUGGESTED_WORKLOAD_NODE_COUNT = 2;

export const WORKLOAD_CLUSTER_OPTIONS = [
{ label: 'Physical', value: ClusterType.WORKLOAD },
{ label: 'Virtual', value: ClusterType.WORKLOAD_V_CLUSTER },
];

export const DEFAULT_CLOUD_INSTANCE_SIZES: Record<
InstallationType,
{ instanceSize: string; nodeCount: number }
> = {
[InstallationType.AWS]: { instanceSize: 'm5.large', nodeCount: 6 },
[InstallationType.CIVO]: { instanceSize: 'Medium - Standard', nodeCount: 6 },
[InstallationType.DIGITAL_OCEAN]: { instanceSize: 's-4vcpu-8gb', nodeCount: 4 },
[InstallationType.GOOGLE]: { instanceSize: 'e2-medium', nodeCount: 3 },
[InstallationType.VULTR]: { instanceSize: 'vc2-4c-8gb', nodeCount: 5 },
[InstallationType.LOCAL]: { instanceSize: '', nodeCount: 3 },
};
7 changes: 5 additions & 2 deletions containers/clusterForms/shared/setupForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,15 @@ const SetupForm: FunctionComponent = () => {
const {
control,
setValue,
getValues,
formState: { errors },
watch,
} = useFormContext<InstallValues>();

const [domainName, subDomain] = watch(['domainName', 'subDomain']);

const { instanceSize, nodeCount } = getValues();

const subDomainHelperText = !subDomain ? '' : `${subDomain}.${domainName}`;

const {
Expand Down Expand Up @@ -181,14 +184,14 @@ const SetupForm: FunctionComponent = () => {
label: instanceSize,
value: instanceSize,
}))}
defaultValue={values?.instanceSize}
defaultValue={instanceSize}
/>
<Box sx={{ width: 136 }}>
<ControlledNumberInput
label="Number of nodes"
control={control}
name="nodeCount"
numberInputProps={{ min: MIN_NODE_COUNT }}
numberInputProps={{ min: MIN_NODE_COUNT, defaultValue: nodeCount }}
/>
</Box>
<ControlledAutocomplete
Expand Down
Loading
Loading