Repository: linode/manager
Pull request #12256: upcoming: M3-9792 - Add VPC column to the Nodebalancer Landing table
Author: login: harsh-akamai
Labels: name: 🚨 Urgent
name: Approved
description: Multiple approvals and ready to merge!
name: NB-VPC
description: Relating to NodeBalancer-VPC integration
Created: 2025-05-21T08:56:17Z | Merged: 2025-05-28T15:47:17Z

## Changes  🔄

- Add VPC column to the Nodebalancer Landing table

## Target release date 🗓️
6/3

## Preview 📷

| Before  | After   |
| ------- | ------- |
| ![image](https://github.com/user-attachments/assets/67c145ce-ae3e-44b6-872d-d40a170eceac) | ![image](https://github.com/user-attachments/assets/dbe1ecbe-a934-47c9-ae15-342d8bbf8f48) | 

## How to test 🧪

### Prerequisites

- Add the customer tags mentioned in the ticket description 
- Enable the `NodeBalancer-VPC Integration` flag
- Create a Nodebalancer integrated with VPC  


### Verification steps

- [ ] Verify that VPC column is shown in the Nodebalancer landing table

<details>
<summary> Author Checklists </summary>

## As an Author, to speed up the review process, I considered 🤔

👀 Doing a self review
❔ Our [contribution guidelines](https://github.com/linode/manager/blob/develop/docs/CONTRIBUTING.md)
🤏 Splitting feature into small PRs
➕ Adding a [changeset](https://github.com/linode/manager/blob/develop/docs/CONTRIBUTING.md#writing-a-changeset)
🧪 Providing/improving test coverage
 🔐 Removing all sensitive information from the code and PR description
🚩 Using a feature flag to protect the release
👣 Providing comprehensive reproduction steps
📑 Providing or updating our documentation
🕛 Scheduling a pair reviewing session
📱 Providing mobile support
♿  Providing accessibility support

<br/>

- [x] I have read and considered all applicable items listed above.

## As an Author, before moving this PR from Draft to Open, I confirmed ✅

- [x] All unit tests are passing
- [x] TypeScript compilation succeeded without errors
- [x] Code passes all linting rules

</details>


Diff:
diff --git a/packages/manager/.changeset/pr-12256-upcoming-features-1747817841341.md b/packages/manager/.changeset/pr-12256-upcoming-features-1747817841341.md
new file mode 100644
index 00000000000..4cbc46418bf
--- /dev/null
+++ b/packages/manager/.changeset/pr-12256-upcoming-features-1747817841341.md
@@ -0,0 +1,5 @@
+---
+"@linode/manager": Upcoming Features
+---
+
+Add VPC column to the Nodebalancer Landing table ([#12256](https://github.com/linode/manager/pull/12256))
diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerActionMenu.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerActionMenu.tsx
index ef4515b3cd5..b473e8497ed 100644
--- a/packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerActionMenu.tsx
+++ b/packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerActionMenu.tsx
@@ -1,4 +1,3 @@
-import { Hidden } from '@linode/ui';
 import { useTheme } from '@mui/material/styles';
 import useMediaQuery from '@mui/material/useMediaQuery';
 import { useNavigate } from '@tanstack/react-router';
@@ -9,6 +8,8 @@ import { InlineMenuAction } from 'src/components/InlineMenuAction/InlineMenuActi
 import { getRestrictedResourceText } from 'src/features/Account/utils';
 import { useIsResourceRestricted } from 'src/hooks/useIsResourceRestricted';
 
+import { useIsNodebalancerVPCEnabled } from '../utils';
+
 import type { Theme } from '@mui/material/styles';
 import type { Action } from 'src/components/ActionMenu/ActionMenu';
 
@@ -29,6 +30,8 @@ export const NodeBalancerActionMenu = (props: Props) => {
     id: nodeBalancerId,
   });
 
+  const { isNodebalancerVPCEnabled } = useIsNodebalancerVPCEnabled();
+
   const actions: Action[] = [
     {
       onClick: () => {
@@ -72,26 +75,25 @@ export const NodeBalancerActionMenu = (props: Props) => {
     },
   ];
 
-  return (
-    <>
-      {!matchesMdDown &&
-        actions.map((action) => {
-          return (
-            <InlineMenuAction
-              actionText={action.title}
-              disabled={action.disabled}
-              key={action.title}
-              onClick={action.onClick}
-              tooltip={action.tooltip}
-            />
-          );
-        })}
-      <Hidden lgUp>
-        <ActionMenu
-          actionsList={actions}
-          ariaLabel={`Action menu for NodeBalancer ${nodeBalancerId}`}
+  const isInlineMenuEnabled = !matchesMdDown && !isNodebalancerVPCEnabled;
+
+  if (isInlineMenuEnabled) {
+    return actions.map((action) => {
+      return (
+        <InlineMenuAction
+          actionText={action.title}
+          disabled={action.disabled}
+          key={action.title}
+          onClick={action.onClick}
+          tooltip={action.tooltip}
         />
-      </Hidden>
-    </>
+      );
+    });
+  }
+  return (
+    <ActionMenu
+      actionsList={actions}
+      ariaLabel={`Action menu for NodeBalancer ${nodeBalancerId}`}
+    />
   );
 };
diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerTableRow.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerTableRow.tsx
index 9deb553141c..51ee4b7f613 100644
--- a/packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerTableRow.tsx
+++ b/packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerTableRow.tsx
@@ -10,12 +10,15 @@ import { TableRow } from 'src/components/TableRow';
 import { IPAddress } from 'src/features/Linodes/LinodesLanding/IPAddress';
 import { RegionIndicator } from 'src/features/Linodes/LinodesLanding/RegionIndicator';
 
+import { useIsNodebalancerVPCEnabled } from '../utils';
 import { NodeBalancerActionMenu } from './NodeBalancerActionMenu';
+import { NodeBalancerVPC } from './NodeBalancerVPC';
 
 import type { NodeBalancer } from '@linode/api-v4/lib/nodebalancers';
 
 export const NodeBalancerTableRow = (props: NodeBalancer) => {
   const { id, ipv4, label, region, transfer } = props;
+  const { isNodebalancerVPCEnabled } = useIsNodebalancerVPCEnabled();
 
   const { data: configs } = useAllNodeBalancerConfigsQuery(id);
 
@@ -68,6 +71,13 @@ export const NodeBalancerTableRow = (props: NodeBalancer) => {
           <RegionIndicator region={region} />
         </TableCell>
       </Hidden>
+      {isNodebalancerVPCEnabled && (
+        <Hidden lgDown>
+          <TableCell data-qa-vpc>
+            <NodeBalancerVPC nodeBalancerId={id} />
+          </TableCell>
+        </Hidden>
+      )}
       <TableCell actionCell>
         <NodeBalancerActionMenu nodeBalancerId={id} />
       </TableCell>
diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerVPC.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerVPC.tsx
new file mode 100644
index 00000000000..c28242278cc
--- /dev/null
+++ b/packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerVPC.tsx
@@ -0,0 +1,42 @@
+import {
+  useNodeBalancerVPCConfigsBetaQuery,
+  useVPCQuery,
+} from '@linode/queries';
+import React from 'react';
+
+import { Link } from 'src/components/Link';
+import { Skeleton } from 'src/components/Skeleton';
+
+interface Props {
+  nodeBalancerId: number;
+}
+
+export const NodeBalancerVPC = ({ nodeBalancerId }: Props) => {
+  const { data: vpcConfig, isLoading: isVPCConfigLoading } =
+    useNodeBalancerVPCConfigsBetaQuery(nodeBalancerId, Boolean(nodeBalancerId));
+
+  const { data: vpcDetails, isLoading: isVPCDetailsLoading } = useVPCQuery(
+    Number(vpcConfig?.data[0]?.vpc_id),
+    Boolean(vpcConfig?.data[0]?.vpc_id)
+  );
+
+  if (isVPCConfigLoading || isVPCDetailsLoading) {
+    return <Skeleton />;
+  }
+
+  if (vpcConfig?.data?.length === 0) {
+    return 'None';
+  }
+
+  return vpcConfig?.data.map(({ vpc_id: vpcId }, i) => (
+    <React.Fragment key={vpcId}>
+      <Link
+        accessibleAriaLabel={`NodeBalancer Port ${vpcId}`}
+        to={`/vpcs/${vpcId}`}
+      >
+        {vpcDetails?.label}
+      </Link>
+      {i < vpcConfig.data.length - 1 ? ', ' : ''}
+    </React.Fragment>
+  ));
+};
diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancersLanding.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancersLanding.tsx
index c14b28e5aa7..78e3cf26537 100644
--- a/packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancersLanding.tsx
+++ b/packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancersLanding.tsx
@@ -20,6 +20,7 @@ import { usePagination } from 'src/hooks/usePagination';
 import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck';
 
 import { NodeBalancerDeleteDialog } from '../NodeBalancerDeleteDialog';
+import { useIsNodebalancerVPCEnabled } from '../utils';
 import { NodeBalancerLandingEmptyState } from './NodeBalancersLandingEmptyState';
 import { NodeBalancerTableRow } from './NodeBalancerTableRow';
 
@@ -61,6 +62,8 @@ export const NodeBalancersLanding = () => {
     error: selectedNodeBalancerError,
   } = useNodeBalancerQuery(Number(params.id), !!params.id);
 
+  const { isNodebalancerVPCEnabled } = useIsNodebalancerVPCEnabled();
+
   if (error) {
     return (
       <ErrorState
@@ -126,6 +129,11 @@ export const NodeBalancersLanding = () => {
                 Region
               </TableSortCell>
             </Hidden>
+            {isNodebalancerVPCEnabled && (
+              <Hidden lgDown>
+                <TableCell>VPC</TableCell>
+              </Hidden>
+            )}
             <TableCell />
           </TableRow>
         </TableHead>


Reviews:
user: login: hana-akamai
body: How do we create a Nodebalancer integrated VPC?

Errors don't seem to be getting surfaced 🤔 

https://github.com/user-attachments/assets/6779a0a4-ada1-46b6-9ac8-8c50e6b2c062

state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T20:53:40Z
user: login: bnussman-akamai
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-21T22:04:15Z
user: login: harsh-akamai
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T15:18:36Z
user: login: harsh-akamai
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T15:19:13Z
user: login: bnussman-akamai
body: Looks good. 

Left one clean up item and I have some concerns about naming hooks and stuff with "beta"
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-05-22T20:52:24Z
user: login: harsh-akamai
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T13:00:26Z
user: login: harsh-akamai
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T13:14:03Z
user: login: bnussman-akamai
body: Looks good! 
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-28T14:06:06Z
user: login: hana-akamai
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-28T15:17:00Z

Review comments:
diff_hunk: @@ -10,15 +14,25 @@ import { TableRow } from 'src/components/TableRow';
 import { IPAddress } from 'src/features/Linodes/LinodesLanding/IPAddress';
 import { RegionIndicator } from 'src/features/Linodes/LinodesLanding/RegionIndicator';
 
+import { useIsNodebalancerVPCEnabled } from '../utils';
 import { NodeBalancerActionMenu } from './NodeBalancerActionMenu';
 
 import type { NodeBalancer } from '@linode/api-v4/lib/nodebalancers';
 
 export const NodeBalancerTableRow = (props: NodeBalancer) => {
   const { id, ipv4, label, region, transfer } = props;
+  const { isNodebalancerVPCEnabled } = useIsNodebalancerVPCEnabled();
 
   const { data: configs } = useAllNodeBalancerConfigsQuery(id);
 
+  const { data: vpcConfig, isLoading: isVPCConfigLoading } =
+    useNodeBalancerVPCConfigsBetaQuery(id, isNodebalancerVPCEnabled);
+
+  const { data: vpcDetails, isLoading: isVPCDetailsLoading } = useVPCQuery(
+    Number(vpcConfig?.data[0]?.vpc_id) || -1,
path: packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerTableRow.tsx
user: login: hana-akamai
body: We shouldn't need the `-1` if we're already checking if it exists on line 33 before enabling the query right
```suggestion
    Number(vpcConfig?.data[0]?.vpc_id),
```
created_at: 2025-05-21T20:45:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -68,6 +82,25 @@ export const NodeBalancerTableRow = (props: NodeBalancer) => {
           <RegionIndicator region={region} />
         </TableCell>
       </Hidden>
+      {isNodebalancerVPCEnabled && (
+        <Hidden lgDown>
+          <TableCell data-qa-vpc>
+            {isVPCConfigLoading || isVPCDetailsLoading ? <Skeleton /> : null}
+            {vpcConfig?.data?.length === 0 && 'None'}
+            {vpcConfig?.data.map(({ vpc_id: vpcId }, i) => (
+              <React.Fragment key={vpcId}>
+                <Link
+                  accessibleAriaLabel={`NodeBalancer Port ${vpcId}`}
+                  to={`/vpcs/${vpcId}`}
+                >
+                  {vpcDetails?.label}
+                </Link>
+                {i < vpcConfig.data.length - 1 ? ', ' : ''}
+              </React.Fragment>
+            ))}
+          </TableCell>
path: packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerTableRow.tsx
user: login: bnussman-akamai
body: Rather than doing all of this within `NodeBalancerTableRow`, let's make a new component. 

Doing so allows us to avoid having to conditionally enable `useVPCQuery` and it will allow us to use early returns to make the code look super clean.

As an example, we did something like this for new Linode Interfaces

https://github.com/linode/manager/blob/017bb889291ff468663997fa0e550bcf3019d8e1/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/LinodeInterfaceTableRow.tsx#L43-L45

https://github.com/linode/manager/blob/017bb889291ff468663997fa0e550bcf3019d8e1/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeInterfaces/LinodeInterfaceFirewall.tsx#L12-L33


created_at: 2025-05-21T22:04:12Z
author_association: MEMBER
diff_hunk: @@ -10,15 +14,25 @@ import { TableRow } from 'src/components/TableRow';
 import { IPAddress } from 'src/features/Linodes/LinodesLanding/IPAddress';
 import { RegionIndicator } from 'src/features/Linodes/LinodesLanding/RegionIndicator';
 
+import { useIsNodebalancerVPCEnabled } from '../utils';
 import { NodeBalancerActionMenu } from './NodeBalancerActionMenu';
 
 import type { NodeBalancer } from '@linode/api-v4/lib/nodebalancers';
 
 export const NodeBalancerTableRow = (props: NodeBalancer) => {
   const { id, ipv4, label, region, transfer } = props;
+  const { isNodebalancerVPCEnabled } = useIsNodebalancerVPCEnabled();
 
   const { data: configs } = useAllNodeBalancerConfigsQuery(id);
 
+  const { data: vpcConfig, isLoading: isVPCConfigLoading } =
+    useNodeBalancerVPCConfigsBetaQuery(id, isNodebalancerVPCEnabled);
+
+  const { data: vpcDetails, isLoading: isVPCDetailsLoading } = useVPCQuery(
+    Number(vpcConfig?.data[0]?.vpc_id) || -1,
path: packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerTableRow.tsx
user: login: harsh-akamai
body: I missed mentioning to add the customer tags. You could check the ticket/project tracker for the tags. 
I've also addressed the suggestion in 96458c224700f93cdd02310ca594060440d267f2 
created_at: 2025-05-22T15:18:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -68,6 +82,25 @@ export const NodeBalancerTableRow = (props: NodeBalancer) => {
           <RegionIndicator region={region} />
         </TableCell>
       </Hidden>
+      {isNodebalancerVPCEnabled && (
+        <Hidden lgDown>
+          <TableCell data-qa-vpc>
+            {isVPCConfigLoading || isVPCDetailsLoading ? <Skeleton /> : null}
+            {vpcConfig?.data?.length === 0 && 'None'}
+            {vpcConfig?.data.map(({ vpc_id: vpcId }, i) => (
+              <React.Fragment key={vpcId}>
+                <Link
+                  accessibleAriaLabel={`NodeBalancer Port ${vpcId}`}
+                  to={`/vpcs/${vpcId}`}
+                >
+                  {vpcDetails?.label}
+                </Link>
+                {i < vpcConfig.data.length - 1 ? ', ' : ''}
+              </React.Fragment>
+            ))}
+          </TableCell>
path: packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerTableRow.tsx
user: login: harsh-akamai
body: Made the changes in 96458c224700f93cdd02310ca594060440d267f2. Can't deny the code looks clean. Thanks Banks! 
created_at: 2025-05-22T15:19:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,45 @@
+import {
+  useNodeBalancerVPCConfigsBetaQuery,
+  useVPCQuery,
+} from '@linode/queries';
+import React from 'react';
+
+import { Link } from 'src/components/Link';
+import { Skeleton } from 'src/components/Skeleton';
+
+interface Props {
+  nodeBalancerId: number;
+}
+
+export const NodeBalancerVPC = ({ nodeBalancerId }: Props) => {
+  const { data: vpcConfig, isLoading: isVPCConfigLoading } =
+    useNodeBalancerVPCConfigsBetaQuery(
path: packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerVPC.tsx
user: login: bnussman-akamai
body: Rather than naming everything beta, can we just name things normally like `useNodeBalancerVPCsQuery` and  continue to use `BETA_API_ROOT` in the underlying api-v4 function. It will save us from having to rename a ton of stuff later

Also, can we remove the name `configs` from this hook and query key and api-v4 function? I don't know if we need  the word configs if the endpoint is just `/v4/nodebalancer/:id/vpcs`
created_at: 2025-05-22T20:51:30Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,45 @@
+import {
+  useNodeBalancerVPCConfigsBetaQuery,
+  useVPCQuery,
+} from '@linode/queries';
+import React from 'react';
+
+import { Link } from 'src/components/Link';
+import { Skeleton } from 'src/components/Skeleton';
+
+interface Props {
+  nodeBalancerId: number;
+}
+
+export const NodeBalancerVPC = ({ nodeBalancerId }: Props) => {
+  const { data: vpcConfig, isLoading: isVPCConfigLoading } =
+    useNodeBalancerVPCConfigsBetaQuery(
+      nodeBalancerId,
+      nodeBalancerId !== undefined
path: packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerVPC.tsx
user: login: bnussman-akamai
body: We can remove this line because we now know `nodeBalancerId` is defined

```suggestion
```
created_at: 2025-05-22T20:51:49Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,45 @@
+import {
+  useNodeBalancerVPCConfigsBetaQuery,
+  useVPCQuery,
+} from '@linode/queries';
+import React from 'react';
+
+import { Link } from 'src/components/Link';
+import { Skeleton } from 'src/components/Skeleton';
+
+interface Props {
+  nodeBalancerId: number;
+}
+
+export const NodeBalancerVPC = ({ nodeBalancerId }: Props) => {
+  const { data: vpcConfig, isLoading: isVPCConfigLoading } =
+    useNodeBalancerVPCConfigsBetaQuery(
path: packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerVPC.tsx
user: login: harsh-akamai
body: I had named the queries beta since it would act as a distinction between the the /v4 and /v4beta queries. Since there are places we are calling both beta and non-beta queries(like in [NodeBalancerCreate.tsx](https://github.com/linode/manager/blob/develop/packages/manager/src/features/NodeBalancers/NodeBalancerCreate.tsx#L359-L361))

The reason I kept `nodeBalancerVPCConfig` is because the api accepts the vpcConfigId and not the vpcId. `/v4/nodebalancer/:id/vpcs/:vpcConfigId`. I felt keeping the name `nodeBalancerVPCConfig` is more apt than `nodeBalancerVPC`
created_at: 2025-05-26T13:00:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,42 @@
+import {
+  useNodeBalancerVPCConfigsBetaQuery,
+  useVPCQuery,
+} from '@linode/queries';
+import React from 'react';
+
+import { Link } from 'src/components/Link';
+import { Skeleton } from 'src/components/Skeleton';
+
+interface Props {
+  nodeBalancerId: number;
+}
+
+export const NodeBalancerVPC = ({ nodeBalancerId }: Props) => {
+  const { data: vpcConfig, isLoading: isVPCConfigLoading } =
+    useNodeBalancerVPCConfigsBetaQuery(nodeBalancerId, Boolean(nodeBalancerId));
path: packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerVPC.tsx
user: login: harsh-akamai
body: Added the enabled check since we have set the flag to `false` by default. ([PR comment reference](https://github.com/linode/manager/pull/12162#discussion_r2082508448))
created_at: 2025-05-26T13:14:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -72,26 +75,24 @@ export const NodeBalancerActionMenu = (props: Props) => {
     },
   ];
 
-  return (
-    <>
-      {!matchesMdDown &&
-        actions.map((action) => {
-          return (
-            <InlineMenuAction
-              actionText={action.title}
-              disabled={action.disabled}
-              key={action.title}
-              onClick={action.onClick}
-              tooltip={action.tooltip}
-            />
-          );
-        })}
-      <Hidden lgUp>
-        <ActionMenu
-          actionsList={actions}
-          ariaLabel={`Action menu for NodeBalancer ${nodeBalancerId}`}
+  const isInlineMenuEnabled = !matchesMdDown && !isNodebalancerVPCEnabled;
+
+  return isInlineMenuEnabled ? (
path: packages/manager/src/features/NodeBalancers/NodeBalancersLanding/NodeBalancerActionMenu.tsx
user: login: bnussman-akamai
body: nit: we could early return instead of using a ternary to make this slightly cleaner
created_at: 2025-05-28T14:05:26Z
author_association: MEMBER

PR comments:
user: login: linode-gh-bot
created_at: 2025-05-28T15:33:50Z
body: <!-- PULL REQUEST COMMENT: cloud-manager-ci-comment -->
<!-- BEGIN COMMENT SECTION: cloud-manager-ui-tests -->
## Cloud Manager UI test results
### :tada: 605 passing tests on [test run #8 ↗︎](https://baker.linode.com/job/manager/job/manager-test/job/PR-12256/8/display/redirect)

<table><thead><tr><td><strong>:x: Failing</strong></td><td><strong>:white_check_mark: Passing</strong></td><td><strong>:arrow_right_hook: Skipped</strong></td><td><strong>:clock1: Duration</strong></td></tr></thead><tbody><tr><td><code>0 Failing</code></td><td><code>605 Passing</code></td><td><code>4 Skipped</code></td><td><code>108m 38s</code></td></tr></tbody></table>
<!-- END COMMENT SECTION: cloud-manager-ui-tests -->
author_association: NONE