fix(striker-ui): enable add/remove networks

main
Tsu-ba-me 2 years ago
parent 45a0d1a6c8
commit b269770d3a
  1. 152
      striker-ui/components/ManageManifest/AnvilNetworkConfigInputGroup.tsx
  2. 47
      striker-ui/components/ManageManifest/AnvilNetworkInputGroup.tsx
  3. 35
      striker-ui/types/ManageManifest.d.ts

@ -1,9 +1,10 @@
import { ReactElement, useMemo } from 'react'; import { ReactElement, useCallback, useMemo, useState } from 'react';
import { v4 as uuidv4 } from 'uuid';
import NETWORK_TYPES from '../../lib/consts/NETWORK_TYPES';
import AnvilNetworkInputGroup from './AnvilNetworkInputGroup'; import AnvilNetworkInputGroup from './AnvilNetworkInputGroup';
import buildObjectStateSetterCallback from '../../lib/buildObjectStateSetterCallback';
import Grid from '../Grid'; import Grid from '../Grid';
import IconButton from '../IconButton';
import InputWithRef from '../InputWithRef'; import InputWithRef from '../InputWithRef';
import OutlinedInputWithLabel from '../OutlinedInputWithLabel'; import OutlinedInputWithLabel from '../OutlinedInputWithLabel';
import { buildNumberTestBatch } from '../../lib/test_input'; import { buildNumberTestBatch } from '../../lib/test_input';
@ -20,7 +21,7 @@ const INPUT_LABEL_ANVIL_NETWORK_CONFIG_DNS = 'DNS';
const INPUT_LABEL_ANVIL_NETWORK_CONFIG_MTU = 'MTU'; const INPUT_LABEL_ANVIL_NETWORK_CONFIG_MTU = 'MTU';
const INPUT_LABEL_ANVIL_NETWORK_CONFIG_NTP = 'NTP'; const INPUT_LABEL_ANVIL_NETWORK_CONFIG_NTP = 'NTP';
const DEFAULT_NETWORKS: { [networkId: string]: AnvilNetworkConfigNetwork } = { const DEFAULT_NETWORKS: AnvilNetworkConfigNetworkList = {
bcn1: { bcn1: {
networkMinIp: '', networkMinIp: '',
networkNumber: 1, networkNumber: 1,
@ -41,6 +42,8 @@ const DEFAULT_NETWORKS: { [networkId: string]: AnvilNetworkConfigNetwork } = {
}, },
}; };
const isIfn = (type: string) => type === 'ifn';
const AnvilNetworkConfigInputGroup = < const AnvilNetworkConfigInputGroup = <
M extends MapToInputTestID & { M extends MapToInputTestID & {
[K in [K in
@ -53,7 +56,7 @@ const AnvilNetworkConfigInputGroup = <
previous: { previous: {
dnsCsv: previousDnsCsv, dnsCsv: previousDnsCsv,
mtu: previousMtu, mtu: previousMtu,
networks = DEFAULT_NETWORKS, networks: previousNetworks = DEFAULT_NETWORKS,
ntpCsv: previousNtpCsv, ntpCsv: previousNtpCsv,
} = {}, } = {},
}: AnvilNetworkConfigInputGroupProps<M>): ReactElement => { }: AnvilNetworkConfigInputGroupProps<M>): ReactElement => {
@ -63,19 +66,119 @@ const AnvilNetworkConfigInputGroup = <
msgSetters, msgSetters,
} = formUtils; } = formUtils;
const [networkList, setNetworkList] =
useState<AnvilNetworkConfigNetworkList>(previousNetworks);
const networkListArray = useMemo(
() => Object.entries(networkList),
[networkList],
);
const getNetworkNumber = useCallback(
(
type: string,
{
input = networkListArray,
end = networkListArray.length,
}: {
input?: Array<[string, AnvilNetworkConfigNetwork]>;
end?: number;
} = {},
) => {
let netNum = 0;
input.every(([, { networkType }], networkIndex) => {
if (networkType === type) {
netNum += 1;
}
return networkIndex < end;
});
return netNum;
},
[networkListArray],
);
const buildNetwork = useCallback(
({
networkMinIp = '',
networkSubnetMask = '',
networkType = 'ifn',
// Params that depend on others.
networkGateway = isIfn(networkType) ? '' : undefined,
networkNumber = getNetworkNumber(networkType) + 1,
}: Partial<AnvilNetworkConfigNetwork> = {}): {
network: AnvilNetworkConfigNetwork;
networkId: string;
} => ({
network: {
networkGateway,
networkMinIp,
networkNumber,
networkSubnetMask,
networkType,
},
networkId: uuidv4(),
}),
[getNetworkNumber],
);
const setNetwork = useCallback(
(key: string, value?: AnvilNetworkConfigNetwork) =>
setNetworkList(buildObjectStateSetterCallback(key, value)),
[],
);
const removeNetwork = useCallback<AnvilNetworkCloseHandler>(
({ networkId: rmId, networkType: rmType }) => {
let isIdMatch = false;
let networkNumber = 0;
const newList = networkListArray.reduce<AnvilNetworkConfigNetworkList>(
(previous, [networkId, networkValue]) => {
const { networkType } = networkValue;
if (networkId === rmId) {
isIdMatch = true;
} else {
if (networkType === rmType) {
networkNumber += 1;
}
if (isIdMatch) {
previous[networkId] = {
...networkValue,
networkNumber,
};
} else {
previous[networkId] = networkValue;
}
}
return previous;
},
{},
);
setNetworkList(newList);
},
[networkListArray],
);
const networksGridLayout = useMemo<GridLayout>(() => { const networksGridLayout = useMemo<GridLayout>(() => {
let result: GridLayout = {}; let result: GridLayout = {};
result = Object.entries(networks).reduce<GridLayout>( result = networkListArray.reduce<GridLayout>(
( (
previous, previous,
[ [
networkId, networkId,
{ {
networkGateway: previousGateway, networkGateway,
networkMinIp: previousMinIp, networkMinIp,
networkNumber, networkNumber,
networkSubnetMask: previousSubnetMask, networkSubnetMask,
networkType, networkType,
}, },
], ],
@ -89,9 +192,8 @@ const AnvilNetworkConfigInputGroup = <
const inputMinIpId = `${inputIdPrefix}-min-ip`; const inputMinIpId = `${inputIdPrefix}-min-ip`;
const inputSubnetMaskId = `${inputIdPrefix}-subnet-mask`; const inputSubnetMaskId = `${inputIdPrefix}-subnet-mask`;
const networkName = `${NETWORK_TYPES[networkType]} ${networkNumber}`; const isFirstNetwork = networkNumber === 1;
const isShowGateway = isIfn(networkType);
const isShowGateway = networkType === 'ifn';
previous[cellId] = { previous[cellId] = {
children: ( children: (
@ -101,12 +203,16 @@ const AnvilNetworkConfigInputGroup = <
inputGatewayId={inputGatewayId} inputGatewayId={inputGatewayId}
inputMinIpId={inputMinIpId} inputMinIpId={inputMinIpId}
inputSubnetMaskId={inputSubnetMaskId} inputSubnetMaskId={inputSubnetMaskId}
networkName={networkName} networkId={networkId}
networkNumber={networkNumber}
networkType={networkType}
onClose={removeNetwork}
previous={{ previous={{
gateway: previousGateway, gateway: networkGateway,
minIp: previousMinIp, minIp: networkMinIp,
subnetMask: previousSubnetMask, subnetMask: networkSubnetMask,
}} }}
showCloseButton={!isFirstNetwork}
showGateway={isShowGateway} showGateway={isShowGateway}
/> />
), ),
@ -120,13 +226,25 @@ const AnvilNetworkConfigInputGroup = <
); );
return result; return result;
}, [formUtils, networks]); }, [formUtils, networkListArray, removeNetwork]);
return ( return (
<Grid <Grid
columns={{ xs: 1, sm: 2, md: 3 }} columns={{ xs: 1, sm: 2, md: 3 }}
layout={{ layout={{
...networksGridLayout, ...networksGridLayout,
'anvil-network-config-cell-add-network': {
children: (
<IconButton
mapPreset="add"
onClick={() => {
const { network: newNet, networkId: newNetId } = buildNetwork();
setNetwork(newNetId, newNet);
}}
/>
),
},
'anvil-network-config-input-cell-dns': { 'anvil-network-config-input-cell-dns': {
children: ( children: (
<InputWithRef <InputWithRef

@ -1,5 +1,7 @@
import { ReactElement, ReactNode, useEffect, useMemo } from 'react'; import { ReactElement, ReactNode, useEffect, useMemo } from 'react';
import NETWORK_TYPES from '../../lib/consts/NETWORK_TYPES';
import Grid from '../Grid'; import Grid from '../Grid';
import IconButton from '../IconButton'; import IconButton from '../IconButton';
import InputWithRef from '../InputWithRef'; import InputWithRef from '../InputWithRef';
@ -22,14 +24,23 @@ const AnvilNetworkInputGroup = <M extends MapToInputTestID>({
inputMinIpLabel = 'IP address', inputMinIpLabel = 'IP address',
inputSubnetMaskId, inputSubnetMaskId,
inputSubnetMaskLabel = 'Subnet mask', inputSubnetMaskLabel = 'Subnet mask',
networkName, networkId,
networkNumber,
networkType,
onClose,
previous: { previous: {
gateway: previousGateway, gateway: previousGateway,
minIp: previousIpAddress, minIp: previousIpAddress,
subnetMask: previousSubnetMask, subnetMask: previousSubnetMask,
} = {}, } = {},
showCloseButton: isShowCloseButton,
showGateway: isShowGateway, showGateway: isShowGateway,
}: AnvilNetworkInputGroupProps<M>): ReactElement => { }: AnvilNetworkInputGroupProps<M>): ReactElement => {
const networkName = useMemo(
() => `${NETWORK_TYPES[networkType]} ${networkNumber}`,
[networkNumber, networkType],
);
const inputCellGatewayId = useMemo( const inputCellGatewayId = useMemo(
() => `${idPrefix}-input-cell-gateway`, () => `${idPrefix}-input-cell-gateway`,
[idPrefix], [idPrefix],
@ -45,6 +56,26 @@ const AnvilNetworkInputGroup = <M extends MapToInputTestID>({
[isShowGateway], [isShowGateway],
); );
const closeButtonElement = useMemo<ReactNode>(
() =>
isShowCloseButton && (
<IconButton
mapPreset="close"
iconProps={{ fontSize: 'small' }}
onClick={(...args) => {
onClose?.call(null, { networkId, networkType }, ...args);
}}
sx={{
padding: '.2em',
position: 'absolute',
right: '-.6rem',
top: '-.2rem',
}}
/>
),
[isShowCloseButton, networkId, networkType, onClose],
);
const inputGatewayElement = useMemo<ReactNode>(() => { const inputGatewayElement = useMemo<ReactNode>(() => {
let result: ReactNode; let result: ReactNode;
@ -74,6 +105,7 @@ const AnvilNetworkInputGroup = <M extends MapToInputTestID>({
}); });
}, },
)} )}
onFirstRender={buildInputFirstRenderFunction(inputGatewayId)}
required={isShowGateway} required={isShowGateway}
/> />
); );
@ -88,6 +120,7 @@ const AnvilNetworkInputGroup = <M extends MapToInputTestID>({
previousGateway, previousGateway,
networkName, networkName,
buildFinishInputTestBatchFunction, buildFinishInputTestBatchFunction,
buildInputFirstRenderFunction,
msgSetters, msgSetters,
]); ]);
@ -100,18 +133,8 @@ const AnvilNetworkInputGroup = <M extends MapToInputTestID>({
<InnerPanel mv={0}> <InnerPanel mv={0}>
<InnerPanelHeader> <InnerPanelHeader>
<BodyText>{networkName}</BodyText> <BodyText>{networkName}</BodyText>
<IconButton {closeButtonElement}
mapPreset="close"
iconProps={{ fontSize: 'small' }}
sx={{
padding: '.2em',
position: 'absolute',
right: '-.6rem',
top: '-.2rem',
}}
/>
</InnerPanelHeader> </InnerPanelHeader>
<InnerPanelBody> <InnerPanelBody>
<Grid <Grid
layout={{ layout={{

@ -11,16 +11,35 @@ type AnvilIdInputGroupProps<M extends MapToInputTestID> =
formUtils: FormUtils<M>; formUtils: FormUtils<M>;
}; };
type AnvilNetworkConfigNetwork = {
networkGateway?: string;
networkMinIp: string;
networkNumber: number;
networkSubnetMask: string;
networkType: string;
};
type AnvilNetworkConfigNetworkList = {
[networkId: string]: AnvilNetworkConfigNetwork;
};
type AnvilNetworkCloseHandler = (
args: { networkId: string } & Pick<AnvilNetworkConfigNetwork, 'networkType'>,
...handlerArgs: Parameters<IconButtonMouseEventHandler>
) => ReturnType<IconButtonMouseEventHandler>;
type AnvilNetworkInputGroupOptionalProps = { type AnvilNetworkInputGroupOptionalProps = {
inputGatewayId?: string; inputGatewayId?: string;
inputGatewayLabel?: string; inputGatewayLabel?: string;
inputMinIpLabel?: string; inputMinIpLabel?: string;
inputSubnetMaskLabel?: string; inputSubnetMaskLabel?: string;
onClose?: AnvilNetworkCloseHandler;
previous?: { previous?: {
gateway?: string; gateway?: string;
minIp?: string; minIp?: string;
subnetMask?: string; subnetMask?: string;
}; };
showCloseButton?: boolean;
showGateway?: boolean; showGateway?: boolean;
}; };
@ -30,7 +49,9 @@ type AnvilNetworkInputGroupProps<M extends MapToInputTestID> =
idPrefix: string; idPrefix: string;
inputMinIpId: string; inputMinIpId: string;
inputSubnetMaskId: string; inputSubnetMaskId: string;
networkName: string; networkId: string;
networkNumber: number;
networkType: string;
}; };
type AnvilHostInputGroupOptionalProps = { type AnvilHostInputGroupOptionalProps = {
@ -64,22 +85,12 @@ type AnvilHostInputGroupProps<M extends MapToInputTestID> =
idPrefix: string; idPrefix: string;
}; };
type AnvilNetworkConfigNetwork = {
networkGateway?: string;
networkMinIp: string;
networkNumber: number;
networkSubnetMask: string;
networkType: string;
};
type AnvilNetworkConfigInputGroupOptionalProps = { type AnvilNetworkConfigInputGroupOptionalProps = {
previous?: { previous?: {
dnsCsv?: string; dnsCsv?: string;
/** Max Transmission Unit (MTU); unit: bytes */ /** Max Transmission Unit (MTU); unit: bytes */
mtu?: number; mtu?: number;
networks?: { networks?: AnvilNetworkConfigNetworkList;
[networkId: string]: AnvilNetworkConfigNetwork;
};
ntpCsv?: string; ntpCsv?: string;
}; };
}; };

Loading…
Cancel
Save