From 8c1622ab1ad8d255ad725178580687d636c7bcec Mon Sep 17 00:00:00 2001 From: coliu-akamai Date: Thu, 7 Sep 2023 11:09:47 -0400 Subject: [PATCH 01/14] subnet create drawer initial drawer --- .../features/VPCs/VPCCreate/SubnetNode.tsx | 4 + .../VPCs/VPCDetail/SubnetCreateDrawer.tsx | 100 ++++++++++++++++++ .../src/features/VPCs/VPCDetail/VPCDetail.tsx | 16 +++ 3 files changed, 120 insertions(+) create mode 100644 packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx diff --git a/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.tsx b/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.tsx index 45587b47933..5714063c2b1 100644 --- a/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.tsx +++ b/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.tsx @@ -13,6 +13,10 @@ import { calculateAvailableIPv4s } from 'src/utilities/subnets'; interface Props { disabled?: boolean; // extra props enable SubnetNode to be an independent component or be part of MultipleSubnetInput + // Note: when I first created this component, I wanted to make it reusable as an independent component for the Create subnet drawer, + // edit subnet drawer, etc. However, now that I'm working on the Create Subnet drawer, imo it's easier to not use this component + // If I've time, may look into getting rid of the props that enabled this to be an independent component + // actually wait tbd maybe it's not so bad to use this... i'm thinking lol (do i have brain cells rn????) // potential refactor - isRemoveable, and subnetIdx & remove in onChange prop idx?: number; isRemovable?: boolean; diff --git a/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx b/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx new file mode 100644 index 00000000000..c057e2158e1 --- /dev/null +++ b/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx @@ -0,0 +1,100 @@ +import { CreateSubnetPayload, Subnet } from '@linode/api-v4'; +import { useFormik } from 'formik'; +import * as React from 'react'; + +import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; +import { Drawer } from 'src/components/Drawer'; +import { Notice } from 'src/components/Notice/Notice'; +import { TextField } from 'src/components/TextField'; +import { useGrants, useProfile } from 'src/queries/profile'; +import { useCreateSubnetMutation } from 'src/queries/vpcs'; +import { getErrorMap } from 'src/utilities/errorUtils'; + +import { SubnetNode } from '../VPCCreate/SubnetNode'; + +interface Props { + onClose: () => void; + open: boolean; + vpcId: number; +} + +export const SubnetCreateDrawer = (props: Props) => { + const { onClose, open, vpcId } = props; + + const { data: profile } = useProfile(); + const { data: grants } = useGrants(); + + const userCannotAddSubnet = profile?.restricted && !grants?.global.add_vpcs; + + const { + error, + isLoading, + mutateAsync: createSubnet, + reset, + } = useCreateSubnetMutation(vpcId); + + const form = useFormik({ + enableReinitialize: true, + initialValues: { + // TODO VPC - add IPv6 when that is supported + label: '', + ipv4: '', + }, + async onSubmit(values) { + // may need to change this if we want to use subnet node ... lol + await createSubnet(values); + onClose(); + }, + }); + + React.useEffect(() => { + if (open) { + form.resetForm(); + reset(); + } + }, [open]); + + const errorMap = getErrorMap(['label', 'ipv4'], error); + + return ( + + {errorMap.none && } + {userCannotAddSubnet && ( + + )} +
+ + + + +
+ ); +}; diff --git a/packages/manager/src/features/VPCs/VPCDetail/VPCDetail.tsx b/packages/manager/src/features/VPCs/VPCDetail/VPCDetail.tsx index f49f046374d..53e859bfaaf 100644 --- a/packages/manager/src/features/VPCs/VPCDetail/VPCDetail.tsx +++ b/packages/manager/src/features/VPCs/VPCDetail/VPCDetail.tsx @@ -4,6 +4,7 @@ import * as React from 'react'; import { useParams } from 'react-router-dom'; import { Box } from 'src/components/Box'; +import { Button } from 'src/components/Button/Button'; import { CircleProgress } from 'src/components/CircleProgress/CircleProgress'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { EntityHeader } from 'src/components/EntityHeader/EntityHeader'; @@ -16,6 +17,7 @@ import { truncate } from 'src/utilities/truncate'; import { VPCDeleteDialog } from '../VPCLanding/VPCDeleteDialog'; import { VPCEditDrawer } from '../VPCLanding/VPCEditDrawer'; import { getUniqueLinodesFromSubnets } from '../utils'; +import { SubnetCreateDrawer } from './SubnetCreateDrawer'; import { StyledActionButton, StyledDescriptionBox, @@ -33,6 +35,9 @@ const VPCDetail = () => { const { data: regions } = useRegionsQuery(); const [editVPCDrawerOpen, setEditVPCDrawerOpen] = React.useState(false); + const [subnetCreateDrawerOpen, setSubnetCreateDrawerOpen] = React.useState( + false + ); const [deleteVPCDialogOpen, setDeleteVPCDialogOpen] = React.useState(false); const [showFullDescription, setShowFullDescription] = React.useState(false); @@ -184,6 +189,17 @@ const VPCDetail = () => { > Subnets + + {/* todo: search bar can maybe go here too? */} + + + setSubnetCreateDrawerOpen(false)} + open={subnetCreateDrawerOpen} + vpcId={vpc.id} + /> ); From 825f32a4a2cb54096c3e6d679d34984fe7910c4d Mon Sep 17 00:00:00 2001 From: coliu-akamai Date: Thu, 7 Sep 2023 22:07:15 -0400 Subject: [PATCH 02/14] tests, cleanup --- .../features/VPCs/VPCCreate/SubnetNode.tsx | 15 ++--- .../VPCDetail/SubnetCreateDrawer.test.tsx | 49 +++++++++++++++ .../VPCs/VPCDetail/SubnetCreateDrawer.tsx | 59 ++++++++++++++----- .../src/features/VPCs/VPCDetail/VPCDetail.tsx | 11 +++- packages/manager/src/utilities/subnets.ts | 2 +- packages/validation/src/vpcs.schema.ts | 2 +- 6 files changed, 112 insertions(+), 26 deletions(-) create mode 100644 packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.test.tsx diff --git a/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.tsx b/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.tsx index 5714063c2b1..4974ec482c8 100644 --- a/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.tsx +++ b/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.tsx @@ -6,17 +6,18 @@ import * as React from 'react'; import { Button } from 'src/components/Button/Button'; import { FormHelperText } from 'src/components/FormHelperText'; import { TextField } from 'src/components/TextField'; -import { RESERVED_IP_NUMBER } from 'src/utilities/subnets'; -import { SubnetFieldState } from 'src/utilities/subnets'; -import { calculateAvailableIPv4s } from 'src/utilities/subnets'; +import { + calculateAvailableIPv4s, + SubnetFieldState, + RESERVED_IP_NUMBER, +} from 'src/utilities/subnets'; interface Props { disabled?: boolean; // extra props enable SubnetNode to be an independent component or be part of MultipleSubnetInput - // Note: when I first created this component, I wanted to make it reusable as an independent component for the Create subnet drawer, - // edit subnet drawer, etc. However, now that I'm working on the Create Subnet drawer, imo it's easier to not use this component - // If I've time, may look into getting rid of the props that enabled this to be an independent component - // actually wait tbd maybe it's not so bad to use this... i'm thinking lol (do i have brain cells rn????) + // Note: when I first created this component, I wanted to make it possible to use it as an independent component for the Create subnet drawer, + // edit subnet drawer, etc. However, now that I'm working on the Create Subnet drawer, imo it's a lot easier to not use this component + // If I've time, will look into simplifying it/combining it with the MultipleSubnetInput // potential refactor - isRemoveable, and subnetIdx & remove in onChange prop idx?: number; isRemovable?: boolean; diff --git a/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.test.tsx b/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.test.tsx new file mode 100644 index 00000000000..2a8df2ad2a1 --- /dev/null +++ b/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.test.tsx @@ -0,0 +1,49 @@ +import * as React from 'react'; + +import { renderWithTheme } from 'src/utilities/testHelpers'; + +import { SubnetCreateDrawer } from './SubnetCreateDrawer'; +import { fireEvent } from '@testing-library/react'; + +const props = { + onClose: jest.fn(), + open: true, + vpcId: 1, +}; + +describe('Create Subnet Drawer', () => { + it('should render title, label, ipv4 input, ipv4 availability, and action buttons', () => { + const { getByTestId, getByText } = renderWithTheme( + + ); + + const drawerTitle = getByText('Create Subnet'); + expect(drawerTitle).toBeVisible(); + + const label = getByText('Subnet label'); + expect(label).toBeVisible(); + expect(label).toBeEnabled(); + + const ipv4Input = getByText('Subnet IP Address Range'); + expect(ipv4Input).toBeVisible(); + expect(ipv4Input).toBeEnabled(); + + const saveButton = getByTestId('create-subnet-button'); + expect(saveButton).toBeVisible(); + + const cancelBtn = getByText(/Cancel/); + expect(cancelBtn).toBeEnabled(); + expect(cancelBtn).toBeVisible(); + }); + + it('should close the drawer if the close cancel button is clicked', () => { + const { getByText } = renderWithTheme(); + + const cancelBtn = getByText(/Cancel/); + expect(cancelBtn).toBeEnabled(); + expect(cancelBtn).toBeVisible(); + + fireEvent.click(cancelBtn); + expect(props.onClose).toHaveBeenCalled(); + }); +}); diff --git a/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx b/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx index c057e2158e1..23d13b0b557 100644 --- a/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx +++ b/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx @@ -1,16 +1,20 @@ -import { CreateSubnetPayload, Subnet } from '@linode/api-v4'; +import { CreateSubnetPayload } from '@linode/api-v4'; import { useFormik } from 'formik'; import * as React from 'react'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Drawer } from 'src/components/Drawer'; +import { FormHelperText } from 'src/components/FormHelperText'; import { Notice } from 'src/components/Notice/Notice'; import { TextField } from 'src/components/TextField'; import { useGrants, useProfile } from 'src/queries/profile'; import { useCreateSubnetMutation } from 'src/queries/vpcs'; import { getErrorMap } from 'src/utilities/errorUtils'; - -import { SubnetNode } from '../VPCCreate/SubnetNode'; +import { + calculateAvailableIPv4s, + DEFAULT_SUBNET_IPV4_VALUE, + RESERVED_IP_NUMBER, +} from 'src/utilities/subnets'; interface Props { onClose: () => void; @@ -33,23 +37,34 @@ export const SubnetCreateDrawer = (props: Props) => { reset, } = useCreateSubnetMutation(vpcId); - const form = useFormik({ + const [availIPv4s, setAvailIPv4s] = React.useState(256); + + const { + dirty, + handleSubmit, + resetForm, + setFieldValue, + values, + } = useFormik({ enableReinitialize: true, initialValues: { // TODO VPC - add IPv6 when that is supported label: '', - ipv4: '', + ipv4: DEFAULT_SUBNET_IPV4_VALUE, }, async onSubmit(values) { - // may need to change this if we want to use subnet node ... lol - await createSubnet(values); - onClose(); + try { + await createSubnet(values); + onClose(); + } catch (errors) { + // will have warnings for uncaught errors without a try/catch - is that ok? + } }, }); React.useEffect(() => { if (open) { - form.resetForm(); + resetForm(); reset(); } }, [open]); @@ -69,25 +84,39 @@ export const SubnetCreateDrawer = (props: Props) => { variant="error" /> )} -
+ ) => + setFieldValue('label', e.target.value) + } + placeholder="Enter a subnet label" + value={values.label} /> ) => { + setFieldValue('ipv4', e.target.value); + const availIPAddresses = calculateAvailableIPv4s(e.target.value); + setAvailIPv4s(availIPAddresses); + }} rows={1} - value={form.values.ipv4} + value={values.ipv4} /> + {availIPv4s && ( + + Available IP Addresses:{' '} + {availIPv4s > 4 ? availIPv4s - RESERVED_IP_NUMBER : 0} + + )} { > Subnets - + {/* todo: search bar can maybe go here too? */} - diff --git a/packages/manager/src/utilities/subnets.ts b/packages/manager/src/utilities/subnets.ts index 6eaa6a79bfa..836564bad7c 100644 --- a/packages/manager/src/utilities/subnets.ts +++ b/packages/manager/src/utilities/subnets.ts @@ -1,6 +1,6 @@ import { determineIPType, vpcsValidateIP } from '@linode/validation'; -export const DEFAULT_SUBNET_IPV4_VALUE = '10.0.0.0/24'; +export const DEFAULT_SUBNET_IPV4_VALUE = '10.0.4.0/24'; export const RESERVED_IP_NUMBER = 4; // VPC: TODO - added ipv6 related fields here, but they will not be used until VPCs support ipv6 diff --git a/packages/validation/src/vpcs.schema.ts b/packages/validation/src/vpcs.schema.ts index ffdefed6164..9cbc4665472 100644 --- a/packages/validation/src/vpcs.schema.ts +++ b/packages/validation/src/vpcs.schema.ts @@ -1,7 +1,7 @@ import ipaddr from 'ipaddr.js'; import { array, lazy, object, string } from 'yup'; -const LABEL_MESSAGE = 'VPC label must be between 1 and 64 characters.'; +const LABEL_MESSAGE = 'Label must be between 1 and 64 characters.'; const LABEL_REQUIRED = 'Label is required'; const LABEL_REQUIREMENTS = 'Must include only ASCII letters, numbers, and dashes'; From 65ab7391d7d89a58827332cd6f6c03a25adf113f Mon Sep 17 00:00:00 2001 From: coliu-akamai Date: Fri, 8 Sep 2023 10:41:02 -0400 Subject: [PATCH 03/14] validation --- .../VPCs/VPCDetail/SubnetCreateDrawer.tsx | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx b/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx index 23d13b0b557..a4db50b4ec6 100644 --- a/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx +++ b/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx @@ -1,4 +1,5 @@ import { CreateSubnetPayload } from '@linode/api-v4'; +import { createSubnetSchema } from '@linode/validation'; import { useFormik } from 'formik'; import * as React from 'react'; @@ -39,6 +40,15 @@ export const SubnetCreateDrawer = (props: Props) => { const [availIPv4s, setAvailIPv4s] = React.useState(256); + const onCreateSubnet = async () => { + try { + await createSubnet(values); + onClose(); + } catch (errors) { + // will get an warning for uncaught error messages without a try/catch - is that ok? + } + }; + const { dirty, handleSubmit, @@ -52,14 +62,10 @@ export const SubnetCreateDrawer = (props: Props) => { label: '', ipv4: DEFAULT_SUBNET_IPV4_VALUE, }, - async onSubmit(values) { - try { - await createSubnet(values); - onClose(); - } catch (errors) { - // will have warnings for uncaught errors without a try/catch - is that ok? - } - }, + onSubmit: onCreateSubnet, + validateOnBlur: false, + validateOnChange: false, + validationSchema: createSubnetSchema, }); React.useEffect(() => { @@ -89,6 +95,7 @@ export const SubnetCreateDrawer = (props: Props) => { disabled={userCannotAddSubnet} errorText={errorMap.label} label="Subnet label" + name="label" onChange={(e: React.ChangeEvent) => setFieldValue('label', e.target.value) } @@ -99,6 +106,7 @@ export const SubnetCreateDrawer = (props: Props) => { disabled={userCannotAddSubnet} errorText={errorMap.ipv4} label="Subnet IP Address Range" + name="ipv4" onChange={(e: React.ChangeEvent) => { setFieldValue('ipv4', e.target.value); const availIPAddresses = calculateAvailableIPv4s(e.target.value); @@ -120,6 +128,7 @@ export const SubnetCreateDrawer = (props: Props) => { label: 'Create subnet', loading: isLoading, type: 'submit', + onClick: onCreateSubnet, }} secondaryButtonProps={{ label: 'Cancel', onClick: onClose }} /> From 822a4abb453f378f6d10fef1e5e1c3e856c17933 Mon Sep 17 00:00:00 2001 From: coliu-akamai Date: Fri, 8 Sep 2023 10:56:59 -0400 Subject: [PATCH 04/14] changeset --- .../.changeset/pr-9652-upcoming-features-1694185012011.md | 5 +++++ packages/manager/src/mocks/serverHandlers.ts | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 packages/manager/.changeset/pr-9652-upcoming-features-1694185012011.md diff --git a/packages/manager/.changeset/pr-9652-upcoming-features-1694185012011.md b/packages/manager/.changeset/pr-9652-upcoming-features-1694185012011.md new file mode 100644 index 00000000000..c08c4863058 --- /dev/null +++ b/packages/manager/.changeset/pr-9652-upcoming-features-1694185012011.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Subnet Create Drawer ([#9652](https://github.com/linode/manager/pull/9652)) diff --git a/packages/manager/src/mocks/serverHandlers.ts b/packages/manager/src/mocks/serverHandlers.ts index 7f598ebf949..b6b71ed8f56 100644 --- a/packages/manager/src/mocks/serverHandlers.ts +++ b/packages/manager/src/mocks/serverHandlers.ts @@ -454,6 +454,10 @@ const vpc = [ const vpc = vpcFactory.build({ ...(req.body as any) }); return res(ctx.json(vpc)); }), + rest.post('*/vpcs/:vpcId/subnets', (req, res, ctx) => { + const subnet = subnetFactory.build({ ...(req.body as any) }); + return res(ctx.json(subnet)); + }), ]; const nanodeType = linodeTypeFactory.build({ id: 'g6-nanode-1' }); From eb251af612144e6600eb5c06fc28c186045f9983 Mon Sep 17 00:00:00 2001 From: coliu-akamai Date: Fri, 8 Sep 2023 11:30:38 -0400 Subject: [PATCH 05/14] using subnet node --- .../features/VPCs/VPCCreate/SubnetNode.tsx | 8 +- .../VPCs/VPCDetail/SubnetCreateDrawer.tsx | 77 +++++++------------ 2 files changed, 32 insertions(+), 53 deletions(-) diff --git a/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.tsx b/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.tsx index 4974ec482c8..92d0c1dbed9 100644 --- a/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.tsx +++ b/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.tsx @@ -15,9 +15,6 @@ import { interface Props { disabled?: boolean; // extra props enable SubnetNode to be an independent component or be part of MultipleSubnetInput - // Note: when I first created this component, I wanted to make it possible to use it as an independent component for the Create subnet drawer, - // edit subnet drawer, etc. However, now that I'm working on the Create Subnet drawer, imo it's a lot easier to not use this component - // If I've time, will look into simplifying it/combining it with the MultipleSubnetInput // potential refactor - isRemoveable, and subnetIdx & remove in onChange prop idx?: number; isRemovable?: boolean; @@ -58,7 +55,10 @@ export const SubnetNode = (props: Props) => { return ( - + void; open: boolean; @@ -31,37 +29,44 @@ export const SubnetCreateDrawer = (props: Props) => { const userCannotAddSubnet = profile?.restricted && !grants?.global.add_vpcs; + const [errorMap, setErrorMap] = React.useState< + Record + >({}); + const { - error, isLoading, mutateAsync: createSubnet, reset, } = useCreateSubnetMutation(vpcId); - const [availIPv4s, setAvailIPv4s] = React.useState(256); - const onCreateSubnet = async () => { try { - await createSubnet(values); + await createSubnet({ label: values.label, ipv4: values.ip.ipv4 }); onClose(); } catch (errors) { - // will get an warning for uncaught error messages without a try/catch - is that ok? + const newErrors = getErrorMap(['label', 'ipv4'], errors); + setErrorMap(newErrors); + setValues({ + label: values.label, + labelError: newErrors.label, + ip: { + ...values.ip, + ipv4Error: newErrors.ipv4, + }, + }); } }; - const { - dirty, - handleSubmit, - resetForm, - setFieldValue, - values, - } = useFormik({ + const { dirty, handleSubmit, resetForm, setValues, values } = useFormik({ enableReinitialize: true, initialValues: { // TODO VPC - add IPv6 when that is supported label: '', - ipv4: DEFAULT_SUBNET_IPV4_VALUE, - }, + ip: { + ipv4: DEFAULT_SUBNET_IPV4_VALUE, + availIPv4s: 256, + }, + } as SubnetFieldState, onSubmit: onCreateSubnet, validateOnBlur: false, validateOnChange: false, @@ -75,8 +80,6 @@ export const SubnetCreateDrawer = (props: Props) => { } }, [open]); - const errorMap = getErrorMap(['label', 'ipv4'], error); - return ( {errorMap.none && } @@ -91,36 +94,12 @@ export const SubnetCreateDrawer = (props: Props) => { /> )} - ) => - setFieldValue('label', e.target.value) - } - placeholder="Enter a subnet label" - value={values.label} - /> - ) => { - setFieldValue('ipv4', e.target.value); - const availIPAddresses = calculateAvailableIPv4s(e.target.value); - setAvailIPv4s(availIPAddresses); + { + setValues(subnetState); }} - rows={1} - value={values.ipv4} + subnet={values} /> - {availIPv4s && ( - - Available IP Addresses:{' '} - {availIPv4s > 4 ? availIPv4s - RESERVED_IP_NUMBER : 0} - - )} Date: Fri, 8 Sep 2023 11:41:23 -0400 Subject: [PATCH 06/14] more thorough tests for subnet node --- .../VPCCreate/MultipleSubnetInput.test.tsx | 2 +- .../VPCs/VPCCreate/SubnetNode.test.tsx | 45 +++++++++++++++++++ .../VPCs/VPCCreate/VPCCreate.test.tsx | 2 +- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/packages/manager/src/features/VPCs/VPCCreate/MultipleSubnetInput.test.tsx b/packages/manager/src/features/VPCs/VPCCreate/MultipleSubnetInput.test.tsx index ca3f6f05420..bc11a749549 100644 --- a/packages/manager/src/features/VPCs/VPCCreate/MultipleSubnetInput.test.tsx +++ b/packages/manager/src/features/VPCs/VPCCreate/MultipleSubnetInput.test.tsx @@ -46,7 +46,7 @@ describe('MultipleSubnetInput', () => { expect(props.onChange).toHaveBeenCalledWith([ ...props.subnets, { - ip: { ipv4: '10.0.0.0/24', ipv4Error: '', availIPv4s: 256 }, + ip: { ipv4: '10.0.4.0/24', ipv4Error: '', availIPv4s: 256 }, label: '', labelError: '', }, diff --git a/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.test.tsx b/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.test.tsx index ab0ece97a39..436f79966e2 100644 --- a/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.test.tsx +++ b/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.test.tsx @@ -44,4 +44,49 @@ describe('SubnetNode', () => { const availIps = screen.queryByText('Available IP Addresses:'); expect(availIps).not.toBeInTheDocument(); }); + + it('should show a label and ip textfield inputs at minimum', () => { + renderWithTheme( + {}} + subnet={{ ip: { ipv4: '' }, label: '' }} + /> + ); + + const label = screen.getByText('Subnet label'); + expect(label).toBeInTheDocument(); + const ipAddress = screen.getByText('Subnet IP Address Range'); + expect(ipAddress).toBeInTheDocument(); + }); + + it('should show a removeable button if isRemovable is true and subnet idx exists and is > 0', () => { + renderWithTheme( + {}} + subnet={{ ip: { ipv4: '' }, label: '' }} + /> + ); + + const removeableButton = screen.getByTestId('delete-subnet-1'); + expect(removeableButton).toBeInTheDocument(); + }); + + it('should not show a removeable button for a subnet with idx 0', () => { + renderWithTheme( + {}} + subnet={{ ip: { ipv4: '' }, label: '' }} + /> + ); + + const removeableButton = screen.queryByTestId('delete-subnet-0'); + expect(removeableButton).not.toBeInTheDocument(); + }); }); diff --git a/packages/manager/src/features/VPCs/VPCCreate/VPCCreate.test.tsx b/packages/manager/src/features/VPCs/VPCCreate/VPCCreate.test.tsx index b7586ed15bf..2308c4e2991 100644 --- a/packages/manager/src/features/VPCs/VPCCreate/VPCCreate.test.tsx +++ b/packages/manager/src/features/VPCs/VPCCreate/VPCCreate.test.tsx @@ -106,6 +106,6 @@ describe('VPC create page', () => { const { getAllByTestId } = renderWithTheme(); const subnetIP = getAllByTestId('textfield-input'); expect(subnetIP[3]).toBeInTheDocument(); - expect(subnetIP[3]).toHaveValue('10.0.0.0/24'); + expect(subnetIP[3]).toHaveValue('10.0.4.0/24'); }); }); From efea5583dfab521b5ce990cd069b8ee4f2ace8d8 Mon Sep 17 00:00:00 2001 From: coliu-akamai Date: Fri, 8 Sep 2023 15:46:34 -0400 Subject: [PATCH 07/14] update tests and create subnet button --- .../src/features/VPCs/VPCDetail/VPCDetail.tsx | 23 --------- .../VPCs/VPCDetail/VPCSubnetsTable.test.tsx | 6 +-- .../VPCs/VPCDetail/VPCSubnetsTable.tsx | 47 +++++++++++++++---- 3 files changed, 41 insertions(+), 35 deletions(-) diff --git a/packages/manager/src/features/VPCs/VPCDetail/VPCDetail.tsx b/packages/manager/src/features/VPCs/VPCDetail/VPCDetail.tsx index 3a0ce88cfc2..496ed72bedf 100644 --- a/packages/manager/src/features/VPCs/VPCDetail/VPCDetail.tsx +++ b/packages/manager/src/features/VPCs/VPCDetail/VPCDetail.tsx @@ -4,7 +4,6 @@ import * as React from 'react'; import { useParams } from 'react-router-dom'; import { Box } from 'src/components/Box'; -import { Button } from 'src/components/Button/Button'; import { CircleProgress } from 'src/components/CircleProgress/CircleProgress'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { EntityHeader } from 'src/components/EntityHeader/EntityHeader'; @@ -17,7 +16,6 @@ import { truncate } from 'src/utilities/truncate'; import { VPCDeleteDialog } from '../VPCLanding/VPCDeleteDialog'; import { VPCEditDrawer } from '../VPCLanding/VPCEditDrawer'; import { getUniqueLinodesFromSubnets } from '../utils'; -import { SubnetCreateDrawer } from './SubnetCreateDrawer'; import { StyledActionButton, StyledDescriptionBox, @@ -35,9 +33,6 @@ const VPCDetail = () => { const { data: regions } = useRegionsQuery(); const [editVPCDrawerOpen, setEditVPCDrawerOpen] = React.useState(false); - const [subnetCreateDrawerOpen, setSubnetCreateDrawerOpen] = React.useState( - false - ); const [deleteVPCDialogOpen, setDeleteVPCDialogOpen] = React.useState(false); const [showFullDescription, setShowFullDescription] = React.useState(false); @@ -189,24 +184,6 @@ const VPCDetail = () => { > Subnets ({vpc.subnets.length}) - - {/* todo: search bar can maybe go here too? */} - - - setSubnetCreateDrawerOpen(false)} - open={subnetCreateDrawerOpen} - vpcId={vpc.id} - /> ); diff --git a/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.test.tsx b/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.test.tsx index b0ef0a21950..20f31c48966 100644 --- a/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.test.tsx +++ b/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.test.tsx @@ -50,7 +50,7 @@ describe('VPC Subnets table', () => { getByText('Linodes'); getByText(subnet.linodes.length); - const actionMenuButton = getAllByRole('button')[3]; + const actionMenuButton = getAllByRole('button')[4]; fireEvent.click(actionMenuButton); getByText('Assign Linode'); @@ -73,7 +73,7 @@ describe('VPC Subnets table', () => { await waitForElementToBeRemoved(getByTestId(loadingTestId)); - const expandTableButton = getAllByRole('button')[2]; + const expandTableButton = getAllByRole('button')[3]; fireEvent.click(expandTableButton); getByText('No Linodes'); }); @@ -91,7 +91,7 @@ describe('VPC Subnets table', () => { await waitForElementToBeRemoved(getByTestId(loadingTestId)); - const expandTableButton = getAllByRole('button')[2]; + const expandTableButton = getAllByRole('button')[3]; fireEvent.click(expandTableButton); getByText('Linode Label'); diff --git a/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.tsx b/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.tsx index d1b6871d53f..7d9dc314638 100644 --- a/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.tsx +++ b/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.tsx @@ -1,6 +1,8 @@ -import { styled } from '@mui/material/styles'; +import { styled, useTheme } from '@mui/material/styles'; import * as React from 'react'; +import { Box } from 'src/components/Box'; +import { Button } from 'src/components/Button/Button'; import { CircleProgress } from 'src/components/CircleProgress/CircleProgress'; import { CollapsibleTable, @@ -22,6 +24,7 @@ import { useOrder } from 'src/hooks/useOrder'; import { usePagination } from 'src/hooks/usePagination'; import { useSubnetsQuery } from 'src/queries/vpcs'; +import { SubnetCreateDrawer } from './SubnetCreateDrawer'; import { SubnetLinodeRow, SubnetLinodeTableRowHead } from './SubnetLinodeRow'; interface Props { @@ -31,7 +34,11 @@ interface Props { const preferenceKey = 'vpc-subnets'; export const VPCSubnetsTable = ({ vpcId }: Props) => { + const theme = useTheme(); const [subnetsFilterText, setSubnetsFilterText] = React.useState(''); + const [subnetCreateDrawerOpen, setSubnetCreateDrawerOpen] = React.useState( + false + ); const pagination = usePagination(1, preferenceKey); @@ -170,14 +177,36 @@ export const VPCSubnetsTable = ({ vpcId }: Props) => { return ( <> - + + + + setSubnetCreateDrawerOpen(false)} + open={subnetCreateDrawerOpen} + vpcId={vpcId} /> Date: Fri, 8 Sep 2023 15:51:03 -0400 Subject: [PATCH 08/14] remove styling that has no effect --- packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.tsx b/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.tsx index 7d9dc314638..6308bd4b257 100644 --- a/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.tsx +++ b/packages/manager/src/features/VPCs/VPCDetail/VPCSubnetsTable.tsx @@ -193,7 +193,6 @@ export const VPCSubnetsTable = ({ vpcId }: Props) => { [theme.breakpoints.up('sm')]: { width: '416px', }, - width: '100%', }} />