diff --git a/packages/manager/.changeset/pr-9832-upcoming-features-1698162070035.md b/packages/manager/.changeset/pr-9832-upcoming-features-1698162070035.md new file mode 100644 index 00000000000..204313ac97b --- /dev/null +++ b/packages/manager/.changeset/pr-9832-upcoming-features-1698162070035.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +VPC UX feedback ([#9832](https://github.com/linode/manager/pull/9832)) diff --git a/packages/manager/cypress/e2e/core/vpc/vpc-details-page.spec.ts b/packages/manager/cypress/e2e/core/vpc/vpc-details-page.spec.ts index d91f38034de..cc8341ef251 100644 --- a/packages/manager/cypress/e2e/core/vpc/vpc-details-page.spec.ts +++ b/packages/manager/cypress/e2e/core/vpc/vpc-details-page.spec.ts @@ -117,7 +117,7 @@ describe('VPC details page', () => { // Confirm that user is redirected to VPC landing page. cy.url().should('endWith', '/vpcs'); - cy.findByText('Create a private and isolated network.'); + cy.findByText('Create a private and isolated network'); }); /** diff --git a/packages/manager/cypress/e2e/core/vpc/vpc-landing-page.spec.ts b/packages/manager/cypress/e2e/core/vpc/vpc-landing-page.spec.ts index 40639e974f7..855eb7ff1b5 100644 --- a/packages/manager/cypress/e2e/core/vpc/vpc-landing-page.spec.ts +++ b/packages/manager/cypress/e2e/core/vpc/vpc-landing-page.spec.ts @@ -12,6 +12,7 @@ import { vpcFactory } from '@src/factories'; import { ui } from 'support/ui'; import { randomLabel, randomPhrase } from 'support/util/random'; import { chooseRegion, getRegionById } from 'support/util/regions'; +import { VPC_LABEL } from 'src/features/VPCs/constants'; // TODO Remove feature flag mocks when feature flag is removed from codebase. describe('VPC landing page', () => { @@ -65,10 +66,8 @@ describe('VPC landing page', () => { cy.wait(['@getFeatureFlags', '@getClientStream', '@getVPCs']); // Confirm that empty state is shown and that each section is present. - cy.findByText('VPCs').should('be.visible'); - cy.findByText('Create a private and isolated network.').should( - 'be.visible' - ); + cy.findByText(VPC_LABEL).should('be.visible'); + cy.findByText('Create a private and isolated network').should('be.visible'); cy.findByText('Getting Started Guides').should('be.visible'); cy.findByText('Video Playlist').should('be.visible'); @@ -268,9 +267,7 @@ describe('VPC landing page', () => { cy.wait(['@deleteVPC', '@getVPCs']); ui.toast.assertMessage('VPC deleted successfully.'); cy.findByText(mockVPCs[1].label).should('not.exist'); - cy.findByText('Create a private and isolated network.').should( - 'be.visible' - ); + cy.findByText('Create a private and isolated network').should('be.visible'); }); /* diff --git a/packages/manager/src/assets/icons/entityIcons/vpc.svg b/packages/manager/src/assets/icons/entityIcons/vpc.svg index 14ca73da488..c58f06cd668 100644 --- a/packages/manager/src/assets/icons/entityIcons/vpc.svg +++ b/packages/manager/src/assets/icons/entityIcons/vpc.svg @@ -1,5 +1,4 @@ - - - - + + + diff --git a/packages/manager/src/features/Linodes/LinodesCreate/VPCPanel.tsx b/packages/manager/src/features/Linodes/LinodesCreate/VPCPanel.tsx index e3837b7f1a4..4feaba4a8ef 100644 --- a/packages/manager/src/features/Linodes/LinodesCreate/VPCPanel.tsx +++ b/packages/manager/src/features/Linodes/LinodesCreate/VPCPanel.tsx @@ -1,4 +1,3 @@ -import { Stack } from 'src/components/Stack'; import { useTheme } from '@mui/material/styles'; import useMediaQuery from '@mui/material/useMediaQuery'; import * as React from 'react'; @@ -9,10 +8,12 @@ import Select, { Item } from 'src/components/EnhancedSelect'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { Link } from 'src/components/Link'; import { Paper } from 'src/components/Paper'; +import { Stack } from 'src/components/Stack'; import { TextField } from 'src/components/TextField'; import { TooltipIcon } from 'src/components/TooltipIcon'; import { Typography } from 'src/components/Typography'; import { APP_ROOT } from 'src/constants'; +import { VPC_AUTO_ASSIGN_IPV4_TOOLTIP } from 'src/features/VPCs/constants'; import { useAccountManagement } from 'src/hooks/useAccountManagement'; import { useFlags } from 'src/hooks/useFlags'; import { useRegionsQuery } from 'src/queries/regions'; @@ -255,10 +256,8 @@ export const VPCPanel = (props: VPCPanelProps) => { Auto-assign a VPC IPv4 address for this Linode in the VPC } diff --git a/packages/manager/src/features/OneClickApps/oneClickApps.ts b/packages/manager/src/features/OneClickApps/oneClickApps.ts index 08345a499a2..9536dd263dd 100644 --- a/packages/manager/src/features/OneClickApps/oneClickApps.ts +++ b/packages/manager/src/features/OneClickApps/oneClickApps.ts @@ -1137,11 +1137,11 @@ export const oneClickApps: OCA[] = [ { href: 'https://www.linode.com/docs/products/tools/marketplace/guides/mcffmpegplugins/', - title: 'Deploy MainConcept FFmpeg Plugins through the Linode Marketplace', + title: + 'Deploy MainConcept FFmpeg Plugins through the Linode Marketplace', }, ], - summary: - 'MainConcept FFmpeg Plugins are advanced video encoding tools.', + summary: 'MainConcept FFmpeg Plugins are advanced video encoding tools.', website: 'https://www.mainconcept.com/ffmpeg', }, { @@ -1162,8 +1162,7 @@ export const oneClickApps: OCA[] = [ title: 'Deploy MainConcept Live Encoder through the Linode Marketplace', }, ], - summary: - 'MainConcept Live Encoder is a real time video encoding engine.', + summary: 'MainConcept Live Encoder is a real time video encoding engine.', website: 'https://www.mainconcept.com/live-encoder', }, { @@ -1181,7 +1180,8 @@ export const oneClickApps: OCA[] = [ { href: 'https://www.linode.com/docs/products/tools/marketplace/guides/mcp2avcultratranscoder/', - title: 'Deploy MainConcept P2 AVC Ultra Transcoder through the Linode Marketplace', + title: + 'Deploy MainConcept P2 AVC Ultra Transcoder through the Linode Marketplace', }, ], summary: @@ -1203,7 +1203,8 @@ export const oneClickApps: OCA[] = [ { href: 'https://www.linode.com/docs/products/tools/marketplace/guides/mcp2xavc/', - title: 'Deploy MainConcept XAVC Transcoder through the Linode Marketplace', + title: + 'Deploy MainConcept XAVC Transcoder through the Linode Marketplace', }, ], summary: @@ -1225,7 +1226,8 @@ export const oneClickApps: OCA[] = [ { href: 'https://www.linode.com/docs/products/tools/marketplace/guides/mcp2xdcam/', - title: 'Deploy MainConcept XDCAM Transcoder through the Linode Marketplace', + title: + 'Deploy MainConcept XDCAM Transcoder through the Linode Marketplace', }, ], summary: @@ -2250,8 +2252,7 @@ export const oneClickApps: OCA[] = [ website: 'https://docs.splunk.com/Documentation/Splunk', }, { - alt_description: - 'A private by design messaging platform.', + alt_description: 'A private by design messaging platform.', alt_name: 'Anonymous messaging platform.', categories: ['Productivity'], colors: { diff --git a/packages/manager/src/features/VPCs/VPCDetail/SubnetAssignLinodesDrawer.tsx b/packages/manager/src/features/VPCs/VPCDetail/SubnetAssignLinodesDrawer.tsx index 6b19eb9c872..35bd7fd5c15 100644 --- a/packages/manager/src/features/VPCs/VPCDetail/SubnetAssignLinodesDrawer.tsx +++ b/packages/manager/src/features/VPCs/VPCDetail/SubnetAssignLinodesDrawer.tsx @@ -3,15 +3,20 @@ import { useFormik } from 'formik'; import * as React from 'react'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; +import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; import { Checkbox } from 'src/components/Checkbox'; import { DownloadCSV } from 'src/components/DownloadCSV/DownloadCSV'; import { Drawer } from 'src/components/Drawer'; +import { FormControlLabel } from 'src/components/FormControlLabel'; import { FormHelperText } from 'src/components/FormHelperText'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; import { RemovableSelectionsList } from 'src/components/RemovableSelectionsList/RemovableSelectionsList'; import { TextField } from 'src/components/TextField'; +import { TooltipIcon } from 'src/components/TooltipIcon'; +import { Typography } from 'src/components/Typography'; +import { VPC_AUTO_ASSIGN_IPV4_TOOLTIP } from 'src/features/VPCs/constants'; import { useFormattedDate } from 'src/hooks/useFormattedDate'; import { useUnassignLinode } from 'src/hooks/useUnassignLinode'; import { useAllLinodesQuery } from 'src/queries/linodes/linodes'; @@ -371,16 +376,25 @@ export const SubnetAssignLinodesDrawer = ( sx={{ marginBottom: '8px' }} value={values.selectedLinode || null} /> - + + + } + label={ + + Auto-assign a VPC IPv4 address for this Linode + + } + data-testid="vpc-ipv4-checkbox" + disabled={userCannotAssignLinodes} + sx={{ marginRight: 0 }} + /> + + {!autoAssignIPv4 && ( { @@ -450,22 +464,24 @@ export const SubnetAssignLinodesDrawer = ( preferredDataLabel="linodeConfigLabel" selectionData={assignedLinodesAndConfigData} /> - + {assignedLinodesAndConfigData.length > 0 && ( + + )} {col[0].label} {col[0].value} + + {col[0].label} + {' '} + {col[0].value} - {col[1].label} {col[1].value} + + {col[1].label} + {' '} + {col[1].value} ); @@ -149,7 +156,9 @@ const VPCDetail = () => { {vpc.description.length > 0 && ( - Description{' '} + + Description + {' '} {description}{' '} @@ -184,7 +193,9 @@ const VPCDetail = () => { })} padding={`${theme.spacing(2)} ${theme.spacing()}`} > - Subnets ({vpc.subnets.length}) + + Subnets ({vpc.subnets.length}) + {numLinodes > 0 && ( { const { push } = useHistory(); diff --git a/packages/manager/src/features/VPCs/VPCLanding/VPCEmptyStateData.tsx b/packages/manager/src/features/VPCs/VPCLanding/VPCEmptyStateData.tsx index a71bcfcb903..911f9da165d 100644 --- a/packages/manager/src/features/VPCs/VPCLanding/VPCEmptyStateData.tsx +++ b/packages/manager/src/features/VPCs/VPCLanding/VPCEmptyStateData.tsx @@ -1,12 +1,15 @@ +import { VPC_LABEL } from 'src/features/VPCs/constants'; + import type { ResourcesHeaders, ResourcesLinks, } from 'src/components/EmptyLandingPageResources/ResourcesLinksTypes'; export const headers: ResourcesHeaders = { - description: '', - subtitle: 'Create a private and isolated network.', - title: 'VPCs', + description: + 'Enable cloud resources to privately and securely communicate with each other, the internet, and other private networks.', + subtitle: 'Create a private and isolated network', + title: VPC_LABEL, }; export const linkAnalyticsEvent: ResourcesLinks['linkAnalyticsEvent'] = { diff --git a/packages/manager/src/features/VPCs/VPCLanding/VPCLanding.test.tsx b/packages/manager/src/features/VPCs/VPCLanding/VPCLanding.test.tsx index f2b9f355c0e..682d7b7b40b 100644 --- a/packages/manager/src/features/VPCs/VPCLanding/VPCLanding.test.tsx +++ b/packages/manager/src/features/VPCs/VPCLanding/VPCLanding.test.tsx @@ -61,7 +61,7 @@ describe('VPC Landing Table', () => { await waitForElementToBeRemoved(getByTestId(loadingTestId)); expect( - getByText('Create a private and isolated network.') + getByText('Create a private and isolated network') ).toBeInTheDocument(); }); }); diff --git a/packages/manager/src/features/VPCs/VPCLanding/VPCLanding.tsx b/packages/manager/src/features/VPCs/VPCLanding/VPCLanding.tsx index 822a5085ac4..480a60a2a89 100644 --- a/packages/manager/src/features/VPCs/VPCLanding/VPCLanding.tsx +++ b/packages/manager/src/features/VPCs/VPCLanding/VPCLanding.tsx @@ -13,12 +13,13 @@ import { TableCell } from 'src/components/TableCell'; import { TableHead } from 'src/components/TableHead'; import { TableRow } from 'src/components/TableRow'; import { TableSortCell } from 'src/components/TableSortCell'; -import { VPCDeleteDialog } from './VPCDeleteDialog'; +import { VPC_LABEL } from 'src/features/VPCs/constants'; import { useOrder } from 'src/hooks/useOrder'; import { usePagination } from 'src/hooks/usePagination'; import { useVPCsQuery } from 'src/queries/vpcs'; import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; +import { VPCDeleteDialog } from './VPCDeleteDialog'; import { VPCEditDrawer } from './VPCEditDrawer'; import { VPCEmptyState } from './VPCEmptyState'; import { VPCRow } from './VPCRow'; @@ -94,7 +95,7 @@ const VPCLanding = () => { createButtonText="Create VPC" docsLink="#" // @TODO VPC: Add docs link onButtonClick={createVPC} - title="Virtual Private Cloud (VPC)" + title={VPC_LABEL} /> diff --git a/packages/manager/src/features/VPCs/constants.ts b/packages/manager/src/features/VPCs/constants.ts index 6fdb4afb1d9..411e7de937a 100644 --- a/packages/manager/src/features/VPCs/constants.ts +++ b/packages/manager/src/features/VPCs/constants.ts @@ -12,3 +12,8 @@ export const REBOOT_LINODE_WARNING_VPCDETAILS = 'Assigned or unassigned Linodes will not take affect until the Linodes are rebooted.'; export const SUBNET_UNASSIGN_LINODES_WARNING = `Unassigning Linodes from a subnet requires you to reboot the Linodes to update its configuration.`; + +export const VPC_LABEL = 'Virtual Private Cloud (VPC)'; + +export const VPC_AUTO_ASSIGN_IPV4_TOOLTIP = + 'A VPC IPv4 is the private IP address for this Linode in the VPC.';