)(
+ primaryId as number | string,
+ shouldRunQuery
+ );
React.useEffect(() => {
if (enabled && !query.isLoading && !query.data) {
diff --git a/packages/manager/src/routes/index.tsx b/packages/manager/src/routes/index.tsx
index 5fd7ada5b49..7e77a0ee4fd 100644
--- a/packages/manager/src/routes/index.tsx
+++ b/packages/manager/src/routes/index.tsx
@@ -96,6 +96,7 @@ export const migrationRouteTree = migrationRootRoute.addChildren([
placementGroupsRouteTree,
stackScriptsRouteTree,
volumesRouteTree,
+ vpcsRouteTree,
]);
export type MigrationRouteTree = typeof migrationRouteTree;
export const migrationRouter = createRouter({
diff --git a/packages/manager/src/routes/vpcs/index.ts b/packages/manager/src/routes/vpcs/index.ts
index 00c9bfc5044..33ad75531ab 100644
--- a/packages/manager/src/routes/vpcs/index.ts
+++ b/packages/manager/src/routes/vpcs/index.ts
@@ -1,8 +1,35 @@
-import { createRoute } from '@tanstack/react-router';
+import { createRoute, redirect } from '@tanstack/react-router';
import { rootRoute } from '../root';
import { VPCRoute } from './VPCRoute';
+import type { TableSearchParams } from '../types';
+
+export interface SubnetSearchParams extends TableSearchParams {
+ query?: string;
+}
+const vpcAction = {
+ delete: 'delete',
+ edit: 'edit',
+} as const;
+
+const subnetAction = {
+ assign: 'assign',
+ create: 'create',
+ delete: 'delete',
+ edit: 'edit',
+ unassign: 'unassign',
+} as const;
+
+const subnetLinodeAction = {
+ 'power-action': 'power-action',
+ unassign: 'unassign',
+} as const;
+
+export type VPCAction = typeof vpcAction[keyof typeof vpcAction];
+export type SubnetAction = typeof subnetAction[keyof typeof subnetAction];
+export type SubnetLinodeAction = typeof subnetLinodeAction[keyof typeof subnetLinodeAction];
+
const vpcsRoute = createRoute({
component: VPCRoute,
getParentRoute: () => rootRoute,
@@ -12,32 +39,166 @@ const vpcsRoute = createRoute({
const vpcsLandingRoute = createRoute({
getParentRoute: () => vpcsRoute,
path: '/',
-}).lazy(() =>
- import('src/features/VPCs/VPCLanding/VPCLanding').then(
- (m) => m.vpcLandingLazyRoute
- )
-);
+}).lazy(() => import('./vpcsLazyRoutes').then((m) => m.vpcLandingLazyRoute));
+
+type VPCActionRouteParams = {
+ action: VPCAction;
+ vpcId: P;
+};
+
+const vpcActionRouteParams = {
+ params: {
+ parse: ({ action, vpcId }: VPCActionRouteParams) => ({
+ action,
+ vpcId: Number(vpcId),
+ }),
+ stringify: ({ action, vpcId }: VPCActionRouteParams) => ({
+ action,
+ vpcId: String(vpcId),
+ }),
+ },
+};
+
+const vpcActionRoute = createRoute({
+ ...vpcActionRouteParams,
+ beforeLoad: async ({ params }) => {
+ if (!(params.action in vpcAction)) {
+ throw redirect({
+ to: '/vpcs',
+ });
+ }
+ },
+ getParentRoute: () => vpcsLandingRoute,
+ path: '$vpcId/$action',
+}).lazy(() => import('./vpcsLazyRoutes').then((m) => m.vpcLandingLazyRoute));
const vpcsCreateRoute = createRoute({
getParentRoute: () => vpcsRoute,
path: 'create',
-}).lazy(() =>
- import('src/features/VPCs/VPCCreate/VPCCreate').then(
- (m) => m.vpcCreateLazyRoute
- )
-);
+}).lazy(() => import('./vpcsLazyRoutes').then((m) => m.vpcCreateLazyRoute));
const vpcsDetailRoute = createRoute({
getParentRoute: () => vpcsRoute,
+ parseParams: (params) => ({
+ vpcId: Number(params.vpcId),
+ }),
path: '$vpcId',
-}).lazy(() =>
- import('src/features/VPCs/VPCDetail/VPCDetail').then(
- (m) => m.vpcDetailLazyRoute
- )
-);
+ validateSearch: (search: SubnetSearchParams) => search,
+}).lazy(() => import('./vpcsLazyRoutes').then((m) => m.vpcDetailLazyRoute));
+
+/**
+ * We must have different routes for the Edit and Delete modals on the VPC Landing page and the VPC Detail page, or we will get
+ * redirected to the Landing page whenever we try to view a modal on the VPC detail page.
+ *
+ * vpcs/$vpcId/detail/edit (detail page) <==> vpcs/$vpcId/edit (landing page)
+ * vpcs/$vpcId/detail/delete (detail page) <==> vpcs/$vpcId/delete (landing page)
+ */
+const vpcDetailActionRoute = createRoute({
+ ...vpcActionRouteParams,
+ beforeLoad: async ({ params }) => {
+ if (!(params.action in vpcAction)) {
+ throw redirect({
+ params: {
+ vpcId: params.vpcId,
+ },
+ search: () => ({}),
+ to: `/vpcs/$vpcId`,
+ });
+ }
+ },
+ getParentRoute: () => vpcsDetailRoute,
+ path: 'detail/$action',
+}).lazy(() => import('./vpcsLazyRoutes').then((m) => m.vpcDetailLazyRoute));
+
+const subnetCreateRoute = createRoute({
+ getParentRoute: () => vpcsDetailRoute,
+ path: 'subnets/create',
+ validateSearch: (search: SubnetSearchParams) => search,
+}).lazy(() => import('./vpcsLazyRoutes').then((m) => m.vpcDetailLazyRoute));
+
+const subnetDetailRoute = createRoute({
+ getParentRoute: () => vpcsDetailRoute,
+ parseParams: (params) => ({
+ subnetId: Number(params.subnetId),
+ }),
+ path: 'subnets/$subnetId',
+ validateSearch: (search: SubnetSearchParams) => search,
+}).lazy(() => import('./vpcsLazyRoutes').then((m) => m.vpcDetailLazyRoute));
+
+type SubnetActionRouteParams = {
+ subnetAction: SubnetAction;
+ subnetId: P;
+};
+
+const subnetActionRoute = createRoute({
+ beforeLoad: async ({ params }) => {
+ if (!(params.subnetAction in subnetAction)) {
+ throw redirect({
+ params: {
+ vpcId: params.vpcId,
+ },
+ search: () => ({}),
+ to: `/vpcs/$vpcId`,
+ });
+ }
+ },
+ getParentRoute: () => subnetDetailRoute,
+ params: {
+ parse: ({ subnetAction }: SubnetActionRouteParams) => ({
+ subnetAction,
+ }),
+ stringify: ({ subnetAction }: SubnetActionRouteParams) => ({
+ subnetAction,
+ }),
+ },
+ path: '$subnetAction',
+ validateSearch: (search: SubnetSearchParams) => search,
+}).lazy(() => import('./vpcsLazyRoutes').then((m) => m.vpcDetailLazyRoute));
+
+type SubnetLinodeActionRouteParams = {
+ linodeAction: SubnetLinodeAction;
+ linodeId: P;
+};
+
+const subnetLinodeActionRoute = createRoute({
+ beforeLoad: async ({ params }) => {
+ if (!(params.linodeAction in subnetLinodeAction)) {
+ throw redirect({
+ params: {
+ vpcId: params.vpcId,
+ },
+ search: () => ({}),
+ to: `/vpcs/$vpcId`,
+ });
+ }
+ },
+ getParentRoute: () => subnetDetailRoute,
+ params: {
+ parse: ({
+ linodeAction,
+ linodeId,
+ }: SubnetLinodeActionRouteParams) => ({
+ linodeAction,
+ linodeId: Number(linodeId),
+ }),
+ stringify: ({
+ linodeAction,
+ linodeId,
+ }: SubnetLinodeActionRouteParams) => ({
+ linodeAction,
+ linodeId: String(linodeId),
+ }),
+ },
+ path: '/linodes/$linodeId/$linodeAction',
+ validateSearch: (search: SubnetSearchParams) => search,
+}).lazy(() => import('./vpcsLazyRoutes').then((m) => m.vpcDetailLazyRoute));
export const vpcsRouteTree = vpcsRoute.addChildren([
- vpcsLandingRoute,
+ vpcsLandingRoute.addChildren([vpcActionRoute]),
vpcsCreateRoute,
- vpcsDetailRoute,
+ vpcsDetailRoute.addChildren([
+ vpcDetailActionRoute,
+ subnetCreateRoute,
+ subnetDetailRoute.addChildren([subnetActionRoute, subnetLinodeActionRoute]),
+ ]),
]);
diff --git a/packages/manager/src/routes/vpcs/vpcsLazyRoutes.ts b/packages/manager/src/routes/vpcs/vpcsLazyRoutes.ts
new file mode 100644
index 00000000000..59b7f762898
--- /dev/null
+++ b/packages/manager/src/routes/vpcs/vpcsLazyRoutes.ts
@@ -0,0 +1,17 @@
+import { createLazyRoute } from '@tanstack/react-router';
+
+import VPCCreate from 'src/features/VPCs/VPCCreate/VPCCreate';
+import VPCDetail from 'src/features/VPCs/VPCDetail/VPCDetail';
+import VPCLanding from 'src/features/VPCs/VPCLanding/VPCLanding';
+
+export const vpcCreateLazyRoute = createLazyRoute('/vpcs/create')({
+ component: VPCCreate,
+});
+
+export const vpcDetailLazyRoute = createLazyRoute('/vpcs/$vpcId')({
+ component: VPCDetail,
+});
+
+export const vpcLandingLazyRoute = createLazyRoute('/')({
+ component: VPCLanding,
+});
diff --git a/packages/queries/src/vpcs/vpcs.ts b/packages/queries/src/vpcs/vpcs.ts
index 4eac7b31a3f..233c21596ad 100644
--- a/packages/queries/src/vpcs/vpcs.ts
+++ b/packages/queries/src/vpcs/vpcs.ts
@@ -155,6 +155,16 @@ export const useSubnetsQuery = (
placeholderData: keepPreviousData,
});
+export const useSubnetQuery = (
+ vpcId: number,
+ subnetId: number,
+ enabled: boolean = true
+) =>
+ useQuery({
+ ...vpcQueries.vpc(vpcId)._ctx.subnets._ctx.subnet(subnetId),
+ enabled,
+ });
+
export const useCreateSubnetMutation = (vpcId: number) => {
const queryClient = useQueryClient();
return useMutation({
From 3fd8df8ae79afb3ac431ac6a8c54dde455f30e20 Mon Sep 17 00:00:00 2001
From: Hussain Khalil <122488130+hkhalil-akamai@users.noreply.github.com>
Date: Thu, 3 Apr 2025 10:13:22 -0400
Subject: [PATCH 66/84] fix: [M3-9070] - Exclude `child_account` grant from PAT
token calculations when it is hidden (#11935)
* Exclude 'child_account' grant from calculations when it is hidden
* Added changeset: PAT Token drawer logic when Child Account Access is hidden
* Add cy spec for individually selecting all permissions
---
.../pr-11935-fixed-1743110166866.md | 5 ++
.../account/personal-access-tokens.spec.ts | 66 +++++++++++++++++++
.../APITokens/CreateAPITokenDrawer.tsx | 26 ++++++--
.../src/features/Profile/APITokens/utils.ts | 18 +++--
4 files changed, 103 insertions(+), 12 deletions(-)
create mode 100644 packages/manager/.changeset/pr-11935-fixed-1743110166866.md
diff --git a/packages/manager/.changeset/pr-11935-fixed-1743110166866.md b/packages/manager/.changeset/pr-11935-fixed-1743110166866.md
new file mode 100644
index 00000000000..e149756a0d0
--- /dev/null
+++ b/packages/manager/.changeset/pr-11935-fixed-1743110166866.md
@@ -0,0 +1,5 @@
+---
+"@linode/manager": Fixed
+---
+
+PAT Token drawer logic when Child Account Access is hidden ([#11935](https://github.com/linode/manager/pull/11935))
diff --git a/packages/manager/cypress/e2e/core/account/personal-access-tokens.spec.ts b/packages/manager/cypress/e2e/core/account/personal-access-tokens.spec.ts
index 81d1f357097..c133b491d55 100644
--- a/packages/manager/cypress/e2e/core/account/personal-access-tokens.spec.ts
+++ b/packages/manager/cypress/e2e/core/account/personal-access-tokens.spec.ts
@@ -179,6 +179,72 @@ describe('Personal access tokens', () => {
});
});
+ it('sends scope as "*" when all permissions are set to read/write', () => {
+ const token = appTokenFactory.build({
+ label: randomLabel(),
+ token: randomString(64),
+ });
+
+ mockCreatePersonalAccessToken(token).as('createToken');
+
+ cy.visitWithLogin('/profile/tokens');
+
+ // Click create button, fill out and submit PAT create form.
+ ui.button
+ .findByTitle('Create a Personal Access Token')
+ .should('be.visible')
+ .should('be.enabled')
+ .click();
+
+ ui.drawer
+ .findByTitle('Add Personal Access Token')
+ .should('be.visible')
+ .within(() => {
+ // Confirm that the “Child account access” grant is not visible in the list of permissions.
+ cy.findAllByText('Child Account Access').should('not.exist');
+
+ // Confirm submit button is disabled without specifying scopes.
+ ui.buttonGroup.findButtonByTitle('Create Token').scrollIntoView();
+ ui.buttonGroup.findButtonByTitle('Create Token').should('be.disabled');
+
+ // Select "Read/Write" for all scopes.
+ cy.get(
+ '[aria-label="Personal Access Token Permissions"] tr:gt(1)'
+ ).each((row) =>
+ cy.wrap(row).within(() => {
+ cy.get('[type="radio"]').eq(2).click();
+ })
+ );
+
+ // Verify "Select All" radio for "Read/Write" is active
+ cy.get('[data-qa-perm-rw-radio]').should(
+ 'have.attr',
+ 'data-qa-radio',
+ 'true'
+ );
+
+ // Specify a label and submit.
+ cy.findByLabelText('Label').scrollIntoView();
+ cy.findByLabelText('Label')
+ .should('be.visible')
+ .should('be.enabled')
+ .click();
+ cy.findByLabelText('Label').type(token.label);
+
+ ui.buttonGroup.findButtonByTitle('Create Token').scrollIntoView();
+ ui.buttonGroup
+ .findButtonByTitle('Create Token')
+ .should('be.visible')
+ .should('be.enabled')
+ .click();
+ });
+
+ // Confirm that new PAT's scopes are '*'
+ cy.wait('@createToken').then((xhr) => {
+ expect(xhr.request.body.scopes).to.equal('*');
+ });
+ });
+
/*
* - Uses mocked API requests to confirm UI flow when renaming and revoking tokens
* - Confirms that list shows the correct label after renaming a token
diff --git a/packages/manager/src/features/Profile/APITokens/CreateAPITokenDrawer.tsx b/packages/manager/src/features/Profile/APITokens/CreateAPITokenDrawer.tsx
index 225f9b6813a..82da40d92ae 100644
--- a/packages/manager/src/features/Profile/APITokens/CreateAPITokenDrawer.tsx
+++ b/packages/manager/src/features/Profile/APITokens/CreateAPITokenDrawer.tsx
@@ -118,6 +118,10 @@ export const CreateAPITokenDrawer = (props: Props) => {
globalGrantType: 'child_account_access',
});
+ // Visually hide the "Child Account Access" permission even though it's still part of the base perms.
+ const hideChildAccountAccessScope =
+ profile?.user_type !== 'parent' || isChildAccountAccessRestricted;
+
const form = useFormik<{
expiry: string;
label: string;
@@ -128,7 +132,10 @@ export const CreateAPITokenDrawer = (props: Props) => {
const { token } = await createPersonalAccessToken({
expiry: values.expiry,
label: values.label,
- scopes: permTuplesToScopeString(values.scopes),
+ scopes: permTuplesToScopeString(
+ values.scopes,
+ hideChildAccountAccessScope ? ['child_account'] : []
+ ),
});
onClose();
showSecret(token ?? 'Secret not available');
@@ -186,6 +193,19 @@ export const CreateAPITokenDrawer = (props: Props) => {
invalidAccessLevels: [levelMap.read_only],
name: 'vpc',
},
+ ...(hideChildAccountAccessScope
+ ? [
+ {
+ defaultAccessLevel: levelMap.hidden,
+ invalidAccessLevels: [
+ levelMap.read_only,
+ levelMap.read_write,
+ levelMap.none,
+ ],
+ name: 'child_account',
+ },
+ ]
+ : []),
];
const indexOfColumnWhereAllAreSelected = allScopesAreTheSame(
@@ -202,10 +222,6 @@ export const CreateAPITokenDrawer = (props: Props) => {
// Filter permissions for all users except parent user accounts.
const allPermissions = form.values.scopes;
- // Visually hide the "Child Account Access" permission even though it's still part of the base perms.
- const hideChildAccountAccessScope =
- profile?.user_type !== 'parent' || isChildAccountAccessRestricted;
-
return (
{
if (scopeTups.length !== perms.length) {
return false;
}
- return scopeTups.reduce(
- (acc: boolean, [key, value]: Permission) =>
- value === levelMap.read_write && acc,
- true
+ const excludeSet = new Set(exclude);
+ return scopeTups.every(
+ ([key, value]) => value === levelMap.read_write || excludeSet.has(key)
);
};
-export const permTuplesToScopeString = (scopeTups: Permission[]): string => {
- if (allMaxPerm(scopeTups, basePerms)) {
+export const permTuplesToScopeString = (
+ scopeTups: Permission[],
+ exclude: PermissionKey[]
+): string => {
+ if (allMaxPerm(scopeTups, basePerms, exclude)) {
return '*';
}
const joinedTups = scopeTups.reduce((acc, [key, value]) => {
From a374f99c3d7497b464e89bdbd1072beff48a2c6f Mon Sep 17 00:00:00 2001
From: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
Date: Thu, 3 Apr 2025 08:06:33 -0700
Subject: [PATCH 67/84] fix: [M3-9689] - Fix unclearable ACL IP addresses for
LKE clusters (#11947)
* Allow empty array of addresses to be submitted
* Show text fields even if there are no addresses in the array
* Clear and disable other fields if ACL is toggled to disabled
* Improve conditional
* Fix submit button disabled incorrectly; update test coverage
* Added changeset: Unclearable ACL IP addresses for LKE clusters
* Fix test failures
* Remove stray .only
* Address PR feedback
* Address UX feedback: restore form default values on enabled retoggle
* Address feedback: reset IP fields to show textfield on toggle
---
.../pr-11947-fixed-1743523539798.md | 5 ++
.../e2e/core/kubernetes/lke-update.spec.ts | 61 ++++++++++---------
.../KubeControlPaneACLDrawer.test.tsx | 35 +++++++++--
.../KubeControlPaneACLDrawer.tsx | 49 ++++++++++++---
4 files changed, 108 insertions(+), 42 deletions(-)
create mode 100644 packages/manager/.changeset/pr-11947-fixed-1743523539798.md
diff --git a/packages/manager/.changeset/pr-11947-fixed-1743523539798.md b/packages/manager/.changeset/pr-11947-fixed-1743523539798.md
new file mode 100644
index 00000000000..486da047cc3
--- /dev/null
+++ b/packages/manager/.changeset/pr-11947-fixed-1743523539798.md
@@ -0,0 +1,5 @@
+---
+"@linode/manager": Fixed
+---
+
+Unclearable ACL IP addresses for LKE clusters ([#11947](https://github.com/linode/manager/pull/11947))
diff --git a/packages/manager/cypress/e2e/core/kubernetes/lke-update.spec.ts b/packages/manager/cypress/e2e/core/kubernetes/lke-update.spec.ts
index 042570fcc64..4830646d369 100644
--- a/packages/manager/cypress/e2e/core/kubernetes/lke-update.spec.ts
+++ b/packages/manager/cypress/e2e/core/kubernetes/lke-update.spec.ts
@@ -2646,28 +2646,32 @@ describe('LKE ACL updates', () => {
/**
* - Confirms ACL can be disabled from the summary page (for standard tier only)
- * - Confirms both IPv4 and IPv6 can be updated and that drawer updates as a result
*/
- it('can disable ACL on a standard tier cluster and edit IPs', () => {
+ it('can disable ACL on a standard tier cluster', () => {
const mockACLOptions = kubernetesControlPlaneACLOptionsFactory.build({
- addresses: { ipv4: undefined, ipv6: undefined },
+ addresses: {
+ ipv4: [],
+ ipv6: [],
+ },
enabled: true,
});
- const mockUpdatedACLOptions1 = kubernetesControlPlaneACLOptionsFactory.build(
+
+ const mockDisabledACLOptions = kubernetesControlPlaneACLOptionsFactory.build(
{
addresses: {
- ipv4: ['10.0.0.0/24'],
- ipv6: ['8e61:f9e9:8d40:6e0a:cbff:c97a:2692:827e'],
+ ipv4: [''],
+ ipv6: [''],
},
enabled: false,
+ 'revision-id': '',
}
);
const mockControlPaneACL = kubernetesControlPlaneACLFactory.build({
acl: mockACLOptions,
});
- const mockUpdatedControlPlaneACL1 = kubernetesControlPlaneACLFactory.build(
+ const mockUpdatedControlPlaneACL = kubernetesControlPlaneACLFactory.build(
{
- acl: mockUpdatedACLOptions1,
+ acl: mockDisabledACLOptions,
}
);
@@ -2675,7 +2679,7 @@ describe('LKE ACL updates', () => {
mockGetControlPlaneACL(mockCluster.id, mockControlPaneACL).as(
'getControlPlaneACL'
);
- mockUpdateControlPlaneACL(mockCluster.id, mockUpdatedControlPlaneACL1).as(
+ mockUpdateControlPlaneACL(mockCluster.id, mockUpdatedControlPlaneACL).as(
'updateControlPlaneACL'
);
@@ -2719,27 +2723,16 @@ describe('LKE ACL updates', () => {
// confirm Revision ID section
cy.findByLabelText('Revision ID').should(
'have.value',
- mockACLOptions['revision-id']
+ mockDisabledACLOptions['revision-id']
);
- // Addresses Section: update IPv4
+ // confirm IPv4 and IPv6 address sections
cy.findByLabelText('IPv4 Addresses or CIDRs ip-address-0')
.should('be.visible')
- .click();
- cy.focused().type('10.0.0.0/24');
- cy.findByText('Add IPv4 Address')
- .should('be.visible')
- .should('be.enabled')
- .click();
- // update IPv6
+ .should('have.value', mockDisabledACLOptions.addresses?.ipv4?.[0]);
cy.findByLabelText('IPv6 Addresses or CIDRs ip-address-0')
.should('be.visible')
- .click();
- cy.focused().type('8e61:f9e9:8d40:6e0a:cbff:c97a:2692:827e');
- cy.findByText('Add IPv6 Address')
- .should('be.visible')
- .should('be.enabled')
- .click();
+ .should('have.value', mockDisabledACLOptions.addresses?.ipv6?.[0]);
// submit
ui.button
@@ -2754,7 +2747,7 @@ describe('LKE ACL updates', () => {
// confirm summary panel updates
cy.contains('Control Plane ACL').should('be.visible');
- cy.findByText('Enabled (O IP Addresses)').should('not.exist');
+ cy.findByText('Enabled (0 IP Addresses)').should('not.exist');
ui.button
.findByTitle('Enable')
.should('be.visible')
@@ -2772,11 +2765,19 @@ describe('LKE ACL updates', () => {
.should('have.attr', 'data-qa-toggle', 'false')
.should('be.visible');
- // confirm updated IP addresses display
- cy.findByDisplayValue('10.0.0.0/24').should('be.visible');
- cy.findByDisplayValue(
- '8e61:f9e9:8d40:6e0a:cbff:c97a:2692:827e'
- ).should('be.visible');
+ // confirm Revision ID section remains empty
+ cy.findByLabelText('Revision ID').should(
+ 'have.value',
+ mockDisabledACLOptions['revision-id']
+ );
+
+ // confirm IPv4 and IPv6 address sections remain empty
+ cy.findByLabelText('IPv4 Addresses or CIDRs ip-address-0')
+ .should('be.visible')
+ .should('have.value', mockDisabledACLOptions.addresses?.ipv4?.[0]);
+ cy.findByLabelText('IPv6 Addresses or CIDRs ip-address-0')
+ .should('be.visible')
+ .should('have.value', mockDisabledACLOptions.addresses?.ipv6?.[0]);
});
});
diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.test.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.test.tsx
index d169d0cb110..42a4abfb234 100644
--- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.test.tsx
+++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.test.tsx
@@ -28,8 +28,8 @@ const queryMocks = vi.hoisted(() => ({
data: {
acl: {
addresses: {
- ipv4: [''],
- ipv6: [''],
+ ipv4: [],
+ ipv6: [],
},
enabled: false,
'revision-id': '',
@@ -49,9 +49,12 @@ vi.mock('src/queries/kubernetes', async () => {
describe('KubeControlPaneACLDrawer', () => {
it('renders the drawer as expected when the cluster is migrated', async () => {
- const { getAllByText, getByText, queryByText } = renderWithTheme(
-
- );
+ const {
+ getAllByTestId,
+ getAllByText,
+ getByText,
+ queryByText,
+ } = renderWithTheme();
expect(getByText('Control Plane ACL for Test')).toBeVisible();
expect(getByText(ACL_DRAWER_STANDARD_TIER_ACL_COPY)).toBeVisible();
@@ -83,6 +86,13 @@ describe('KubeControlPaneACLDrawer', () => {
expect(getByText('IPv6 Addresses or CIDRs')).toBeVisible();
expect(getByText('Add IPv6 Address')).toBeVisible();
+ // Confirm text input is disabled when ACL is disabled
+ const inputs = getAllByTestId('textfield-input');
+ expect(inputs).toHaveLength(3);
+ inputs.forEach((input) => {
+ expect(input).toBeDisabled();
+ });
+
// Confirm notice does not display
expect(
queryByText(
@@ -91,6 +101,21 @@ describe('KubeControlPaneACLDrawer', () => {
).not.toBeInTheDocument();
});
+ it('confirms the revision ID and IP address fields are enabled when ACL is enabled', async () => {
+ const { getAllByTestId, getByText } = renderWithTheme(
+
+ );
+
+ const toggle = getByText('Enable Control Plane ACL');
+ await userEvent.click(toggle);
+
+ const inputs = getAllByTestId('textfield-input');
+ expect(inputs).toHaveLength(3);
+ inputs.forEach((input) => {
+ expect(input).toBeEnabled();
+ });
+ });
+
it('shows a notice and hides revision ID if cluster is not migrated', () => {
const { getByText, queryByText } = renderWithTheme(
diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.tsx
index 875e16026b8..4e0a40985c9 100644
--- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.tsx
+++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.tsx
@@ -87,6 +87,7 @@ export const KubeControlPlaneACLDrawer = (
handleSubmit,
reset,
setError,
+ setValue,
watch,
} = useForm({
defaultValues: aclData,
@@ -99,8 +100,12 @@ export const KubeControlPlaneACLDrawer = (
values: {
acl: {
addresses: {
- ipv4: aclPayload?.addresses?.ipv4 ?? [''],
- ipv6: aclPayload?.addresses?.ipv6 ?? [''],
+ ipv4: aclPayload?.addresses?.ipv4?.length
+ ? aclPayload?.addresses?.ipv4
+ : [''],
+ ipv6: aclPayload?.addresses?.ipv6?.length
+ ? aclPayload?.addresses?.ipv6
+ : [''],
},
enabled: aclPayload?.enabled ?? false,
'revision-id': aclPayload?.['revision-id'] ?? '',
@@ -150,12 +155,12 @@ export const KubeControlPlaneACLDrawer = (
acl: {
enabled: acl.enabled,
'revision-id': acl['revision-id'],
- ...((ipv4.length > 0 || ipv6.length > 0) && {
+ ...{
addresses: {
- ...(ipv4.length > 0 && { ipv4 }),
- ...(ipv6.length > 0 && { ipv6 }),
+ ipv4,
+ ipv6,
},
- }),
+ },
},
};
@@ -233,10 +238,37 @@ export const KubeControlPlaneACLDrawer = (
checked={
isEnterpriseCluster ? true : field.value ?? false
}
+ onChange={() => {
+ setValue('acl.enabled', !field.value, {
+ shouldDirty: true,
+ });
+ // Disabling ACL should clear the revision-id and any addresses (see LKE-6205).
+ if (!acl.enabled) {
+ setValue('acl.revision-id', '');
+ setValue('acl.addresses.ipv6', ['']);
+ setValue('acl.addresses.ipv4', ['']);
+ } else {
+ setValue(
+ 'acl.revision-id',
+ aclPayload?.['revision-id']
+ );
+ setValue(
+ 'acl.addresses.ipv6',
+ aclPayload?.addresses?.ipv6?.length
+ ? aclPayload?.addresses?.ipv6
+ : ['']
+ );
+ setValue(
+ 'acl.addresses.ipv4',
+ aclPayload?.addresses?.ipv4?.length
+ ? aclPayload?.addresses?.ipv4
+ : ['']
+ );
+ }
+ }}
disabled={isEnterpriseCluster}
name="ipacl-checkbox"
onBlur={field.onBlur}
- onChange={field.onChange}
/>
}
label="Enable Control Plane ACL"
@@ -260,6 +292,7 @@ export const KubeControlPlaneACLDrawer = (
(
(
(
Date: Thu, 3 Apr 2025 11:35:57 -0400
Subject: [PATCH 68/84] test [M3 9562]: remove hardcoded region id (#11948)
* replace region unavailable in devcloud w/ random region
* Added changeset: Fix test thats broken in devcloud
---
.../manager/.changeset/pr-11948-tests-1743523964300.md | 5 +++++
.../core/linodes/create-linode-view-code-snippet.spec.ts | 7 +++++--
2 files changed, 10 insertions(+), 2 deletions(-)
create mode 100644 packages/manager/.changeset/pr-11948-tests-1743523964300.md
diff --git a/packages/manager/.changeset/pr-11948-tests-1743523964300.md b/packages/manager/.changeset/pr-11948-tests-1743523964300.md
new file mode 100644
index 00000000000..bf144dcd6c5
--- /dev/null
+++ b/packages/manager/.changeset/pr-11948-tests-1743523964300.md
@@ -0,0 +1,5 @@
+---
+"@linode/manager": Tests
+---
+
+Fix test thats broken in devcloud ([#11948](https://github.com/linode/manager/pull/11948))
diff --git a/packages/manager/cypress/e2e/core/linodes/create-linode-view-code-snippet.spec.ts b/packages/manager/cypress/e2e/core/linodes/create-linode-view-code-snippet.spec.ts
index 2ec04d6fcc7..b74046af2fc 100644
--- a/packages/manager/cypress/e2e/core/linodes/create-linode-view-code-snippet.spec.ts
+++ b/packages/manager/cypress/e2e/core/linodes/create-linode-view-code-snippet.spec.ts
@@ -6,6 +6,7 @@ import { mockAppendFeatureFlags } from 'support/intercepts/feature-flags';
import { ui } from 'support/ui';
import { linodeCreatePage } from 'support/ui/pages';
import { randomLabel, randomString } from 'support/util/random';
+import { chooseRegion } from 'support/util/regions';
describe('Create Linode flow to validate code snippet modal', () => {
beforeEach(() => {
@@ -20,13 +21,15 @@ describe('Create Linode flow to validate code snippet modal', () => {
it(`view code snippets in create linode flow`, () => {
const linodeLabel = randomLabel();
const rootPass = randomString(32);
-
+ const mockLinodeRegion = chooseRegion({
+ capabilities: ['Linodes'],
+ });
cy.visitWithLogin('/linodes/create');
// Set Linode label, distribution, plan type, password, etc.
linodeCreatePage.setLabel(linodeLabel);
linodeCreatePage.selectImage('Debian 12');
- linodeCreatePage.selectRegionById('us-east');
+ linodeCreatePage.selectRegionById(mockLinodeRegion.id);
linodeCreatePage.selectPlan('Shared CPU', 'Nanode 1 GB');
linodeCreatePage.setRootPassword(rootPass);
From 88135b4ebdadd8149ed31fce288db410a6c58471 Mon Sep 17 00:00:00 2001
From: Jaalah Ramos
Date: Thu, 3 Apr 2025 13:14:18 -0400
Subject: [PATCH 69/84] Update changelogs
---
packages/api-v4/CHANGELOG.md | 2 +-
packages/manager/CHANGELOG.md | 27 ++++++++++++++-------------
packages/queries/CHANGELOG.md | 4 ++--
3 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/packages/api-v4/CHANGELOG.md b/packages/api-v4/CHANGELOG.md
index 7d4497f5d66..ed0cf47d7ce 100644
--- a/packages/api-v4/CHANGELOG.md
+++ b/packages/api-v4/CHANGELOG.md
@@ -3,7 +3,7 @@
### Added:
-- DBaaS Advanced Configurations: added `getDatabaseEngineConfig` request to fetch all advanced configurations and updated types for advanced configs ([#11812](https://github.com/linode/manager/pull/11812))
+- DBaaS Advanced Configurations: Add `getDatabaseEngineConfig` request to fetch all advanced configurations and updated types for advanced configs ([#11812](https://github.com/linode/manager/pull/11812))
### Upcoming Features:
diff --git a/packages/manager/CHANGELOG.md b/packages/manager/CHANGELOG.md
index 02d56fcdc21..1dfa40ee052 100644
--- a/packages/manager/CHANGELOG.md
+++ b/packages/manager/CHANGELOG.md
@@ -13,27 +13,28 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Changed:
-- Updated Breadcrumb component to conform to Akamai Design System specs ([#11841](https://github.com/linode/manager/pull/11841))
+- Update Breadcrumb component to conform to Akamai Design System specs ([#11841](https://github.com/linode/manager/pull/11841))
- Display interface type first in Linode Network IP Addresses table ([#11865](https://github.com/linode/manager/pull/11865))
-- Updated Radio Button component to conform to Akamai Design System specs ([#11878](https://github.com/linode/manager/pull/11878))
+- Update Radio Button component to conform to Akamai Design System specs ([#11878](https://github.com/linode/manager/pull/11878))
- Change `GlobalFilters.tsx` and `Zoomer.tsx` to add color on hover of icon ([#11883](https://github.com/linode/manager/pull/11883))
- Update styles to CDS for profile menu ([#11884](https://github.com/linode/manager/pull/11884))
### Fixed:
-- Disable action menu for `Database` with tooltip text and enable `Delete Cluster` for `read/write` grant users ([#11890](https://github.com/linode/manager/pull/11890))
+- Database action menu incorrectly enabled with `read-only` grant and `Delete Cluster` button incorrectly disabled with `read/write` grant ([#11890](https://github.com/linode/manager/pull/11890))
- Tabs keyboard navigation on some Tanstack rerouted features ([#11894](https://github.com/linode/manager/pull/11894))
### Removed:
-- Ramda from `Utilities` ([#11861](https://github.com/linode/manager/pull/11861))
+
+- Ramda from `Utilities` package ([#11861](https://github.com/linode/manager/pull/11861))
- Move `ListItemOption` from `manager` to `ui` package ([#11790](https://github.com/linode/manager/pull/11790))
- Move `regionsData` from `manager` to `utilities` package ([#11790](https://github.com/linode/manager/pull/11790))
- Move `LinodeCreateType` to `utilities` package ([#11790](https://github.com/linode/manager/pull/11790))
- Move `LinodeSelect` to new `shared` package ([#11844](https://github.com/linode/manager/pull/11844))
- Legacy BetaChip component ([#11872](https://github.com/linode/manager/pull/11872))
- Move `doesRegionSupportFeature` from `manager` to `utilities` package ([#11891](https://github.com/linode/manager/pull/11891))
-- Moved Tags-related queries and dependencies to shares `queries` package ([#11897](https://github.com/linode/manager/pull/11897))
-- Moved Support-related queries and dependencies to shared `queries` package ([#11904](https://github.com/linode/manager/pull/11904))
+- Move Tags-related queries and dependencies to shares `queries` package ([#11897](https://github.com/linode/manager/pull/11897))
+- Move Support-related queries and dependencies to shared `queries` package ([#11904](https://github.com/linode/manager/pull/11904))
- Move `luxon` dependent utils from `manager` to `utilities` package ([#11905](https://github.com/linode/manager/pull/11905))
- Move ramda dependent utils ([#11913](https://github.com/linode/manager/pull/11913))
- Move `useIsGeckoEnabled` hook out of `RegionSelect` to `@linode/shared` package ([#11918](https://github.com/linode/manager/pull/11918))
@@ -51,24 +52,24 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Tests:
-- html presentation for cypress test results ([#11795](https://github.com/linode/manager/pull/11795))
+- Add HTML report generation for Cypress test results ([#11795](https://github.com/linode/manager/pull/11795))
- Add `env:premiumPlans` test tag for tests which require premium plan availability ([#11886](https://github.com/linode/manager/pull/11886))
- Fix Linode create end-to-end test failures against alternative environments ([#11886](https://github.com/linode/manager/pull/11886))
- Delete redundant Linode create SSH key test ([#11886](https://github.com/linode/manager/pull/11886))
- Add test for Add Linode Interface drawer ([#11887](https://github.com/linode/manager/pull/11887))
- Prevent legacy regions from being used by Cypress tests ([#11892](https://github.com/linode/manager/pull/11892))
- Temporarily skip Firewall end-to-end tests ([#11898](https://github.com/linode/manager/pull/11898))
-- Tests for restricted user on database page ([#11912](https://github.com/linode/manager/pull/11912))
+- Add tests for restricted user on database page ([#11912](https://github.com/linode/manager/pull/11912))
### Upcoming Features:
-- DBaaS Advanced Configurations: added UI for existing engine options in the drawer ([#11812](https://github.com/linode/manager/pull/11812))
+- DBaaS Advanced Configurations: Add UI for existing engine options in the drawer ([#11812](https://github.com/linode/manager/pull/11812))
- Add Default Firewalls paper to Account Settings ([#11828](https://github.com/linode/manager/pull/11828))
- Add functionality to support the 'Assign New Roles' drawer for a single user in IAM ([#11834](https://github.com/linode/manager/pull/11834))
- Update Firewall Devices Linode landing table to account for new interface devices ([#11842](https://github.com/linode/manager/pull/11842))
-- Quotas Tab Beta Chip ([#11872](https://github.com/linode/manager/pull/11872))
-- Add AlertListNoticeMessages component for handling multiple API error messages, update AddChannelListing and MetricCriteria components to display these errors, Add handleMultipleError util method for aggregating, mapping the errors to fields. ([#11874](https://github.com/linode/manager/pull/11874))
-- Diable query to get Linode Interface when Interface Delete dialog is closed ([#11881](https://github.com/linode/manager/pull/11881))
+- Add Quotas Tab Beta Chip ([#11872](https://github.com/linode/manager/pull/11872))
+- Add AlertListNoticeMessages component for handling multiple API error messages, update AddChannelListing and MetricCriteria components to display these errors, add handleMultipleError util method for aggregating, mapping the errors to fields ([#11874](https://github.com/linode/manager/pull/11874))
+- Disable query to get Linode Interface when Interface Delete dialog is closed ([#11881](https://github.com/linode/manager/pull/11881))
- Update title for Delete Interface dialog ([#11881](https://github.com/linode/manager/pull/11881))
- Add VPC support to the Add Network Interface Drawer ([#11887](https://github.com/linode/manager/pull/11887))
- Add Interface Details drawer for Linode Interfaces ([#11888](https://github.com/linode/manager/pull/11888))
@@ -77,7 +78,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
- Update success message for create/edit/enable/disable alert at `CreateAlertDefinition.tsx`, `EditAlertDefinition.tsx`, and `AlertListTable.tsx` ([#11903](https://github.com/linode/manager/pull/11903))
- Update Firewall Landing table to account for Linode Interface devices and Default Firewalls ([#11920](https://github.com/linode/manager/pull/11920))
- Add Default Firewall chips to Firewall Detail page ([#11920](https://github.com/linode/manager/pull/11920))
-- remove preselected role from Change Role drawer ([#11926](https://github.com/linode/manager/pull/11926))
+- Remove preselected role from Change Role drawer ([#11926](https://github.com/linode/manager/pull/11926))
- Adjust logic for displaying encryption status on Linode Details page and encryption copy on LKE Create page ([#11930](https://github.com/linode/manager/pull/11930))
## [2025-03-26] - v1.138.1
diff --git a/packages/queries/CHANGELOG.md b/packages/queries/CHANGELOG.md
index 0197b0f782d..62935207291 100644
--- a/packages/queries/CHANGELOG.md
+++ b/packages/queries/CHANGELOG.md
@@ -3,8 +3,8 @@
### Added:
-- Created `tags/` directory and migrated relevant query keys and hooks ([#11897](https://github.com/linode/manager/pull/11897))
-- Created `support/` directory and migrated relevant query keys and hooks ([#11904](https://github.com/linode/manager/pull/11904))
+- `tags/` directory and migrated relevant query keys and hooks ([#11897](https://github.com/linode/manager/pull/11897))
+- `support/` directory and migrated relevant query keys and hooks ([#11904](https://github.com/linode/manager/pull/11904))
### Upcoming Features:
From 0172b47f5a557c5fe5d5adae157a4a8df66906a5 Mon Sep 17 00:00:00 2001
From: Hana Xu <115299789+hana-akamai@users.noreply.github.com>
Date: Thu, 3 Apr 2025 13:57:17 -0400
Subject: [PATCH 70/84] upcoming: [M3-9515] - Update types for VPC IPs (#11938)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Description 📝
Update types for VPC IPs to support IPv6
## How to test 🧪
### Verification steps
(How to verify changes)
- [ ] Check type updates match API spec (linked in parent ticket) for `GET` `/v4/vpcs/ips` and `/v4/vpcs/{vpcId}/ips`
---
.../.changeset/pr-11938-upcoming-features-1743446020892.md | 5 +++++
packages/api-v4/src/vpcs/types.ts | 5 +++++
.../.changeset/pr-11938-upcoming-features-1743446079043.md | 5 +++++
packages/manager/src/factories/vpcs.ts | 7 +++++++
4 files changed, 22 insertions(+)
create mode 100644 packages/api-v4/.changeset/pr-11938-upcoming-features-1743446020892.md
create mode 100644 packages/manager/.changeset/pr-11938-upcoming-features-1743446079043.md
diff --git a/packages/api-v4/.changeset/pr-11938-upcoming-features-1743446020892.md b/packages/api-v4/.changeset/pr-11938-upcoming-features-1743446020892.md
new file mode 100644
index 00000000000..1d822cd32d9
--- /dev/null
+++ b/packages/api-v4/.changeset/pr-11938-upcoming-features-1743446020892.md
@@ -0,0 +1,5 @@
+---
+"@linode/api-v4": Upcoming Features
+---
+
+Add support for IPv6 to `VPCIP` ([#11938](https://github.com/linode/manager/pull/11938))
diff --git a/packages/api-v4/src/vpcs/types.ts b/packages/api-v4/src/vpcs/types.ts
index 025ffd80a6b..2626c8de526 100644
--- a/packages/api-v4/src/vpcs/types.ts
+++ b/packages/api-v4/src/vpcs/types.ts
@@ -66,6 +66,11 @@ export interface VPCIP {
active: boolean;
address: string | null;
address_range: string | null;
+ ipv6_range: string | null;
+ ipv6_is_public: boolean | null;
+ ipv6_addresses: {
+ slaac_address: string;
+ }[];
config_id: number | null;
gateway: string | null;
interface_id: number;
diff --git a/packages/manager/.changeset/pr-11938-upcoming-features-1743446079043.md b/packages/manager/.changeset/pr-11938-upcoming-features-1743446079043.md
new file mode 100644
index 00000000000..48f58ebeb31
--- /dev/null
+++ b/packages/manager/.changeset/pr-11938-upcoming-features-1743446079043.md
@@ -0,0 +1,5 @@
+---
+"@linode/manager": Upcoming Features
+---
+
+Update `vpcIPFactory` to support IPv6 ([#11938](https://github.com/linode/manager/pull/11938))
diff --git a/packages/manager/src/factories/vpcs.ts b/packages/manager/src/factories/vpcs.ts
index 298a2907e84..b475274c1ce 100644
--- a/packages/manager/src/factories/vpcs.ts
+++ b/packages/manager/src/factories/vpcs.ts
@@ -19,6 +19,13 @@ export const vpcIPFactory = Factory.Sync.makeFactory({
config_id: Factory.each((i) => i),
gateway: '192.0.2.1',
interface_id: Factory.each((i) => i),
+ ipv6_addresses: [
+ {
+ slaac_address: '2001:DB8::0000',
+ },
+ ],
+ ipv6_is_public: null,
+ ipv6_range: null,
linode_id: Factory.each((i) => i),
nat_1_1: '192.0.2.97',
prefix: 24,
From 4a7a641066ce8fca4d2e453c0d1a0c7985395729 Mon Sep 17 00:00:00 2001
From: cpathipa <119517080+cpathipa@users.noreply.github.com>
Date: Thu, 3 Apr 2025 13:25:13 -0500
Subject: [PATCH 71/84] feat: [M3-9632] - NewFeatureChip component (#11965)
* Add NewFeatureChip component
* Update styles for BetaChip component
* Update BetaChip usage and component test
* Added changeset: Updated BetaChip styles, its usage and updated BetaChip component tests
* Added changeset: A new `NewFeatureChip` component and updated BetaChip styles
* Delete packages/manager/.changeset/pr-11965-changed-1743702322230.md
* Delete packages/ui/.changeset/pr-11965-added-1743702343146.md
* update comments
* code cleanup
---
.../component/components/beta-chip.spec.tsx | 18 ++---
.../src/components/PrimaryNav/PrimaryLink.tsx | 6 +-
.../src/features/Account/AccountLanding.tsx | 2 +-
.../features/Account/NetworkInterfaceType.tsx | 2 +-
.../Databases/DatabaseDetail/index.tsx | 4 +-
.../DatabaseLanding/DatabaseLogo.tsx | 14 +---
.../components/BetaChip/BetaChip.stories.tsx | 17 +++++
.../src/components/BetaChip/BetaChip.test.tsx | 15 +---
.../ui/src/components/BetaChip/BetaChip.tsx | 72 +++++++------------
.../NewFeatureChip/NewFeatureChip.stories.tsx | 17 +++++
.../NewFeatureChip/NewFeatureChip.test.tsx | 26 +++++++
.../NewFeatureChip/NewFeatureChip.tsx | 57 +++++++++++++++
.../ui/src/components/NewFeatureChip/index.ts | 1 +
packages/ui/src/components/index.ts | 1 +
14 files changed, 157 insertions(+), 95 deletions(-)
create mode 100644 packages/ui/src/components/BetaChip/BetaChip.stories.tsx
create mode 100644 packages/ui/src/components/NewFeatureChip/NewFeatureChip.stories.tsx
create mode 100644 packages/ui/src/components/NewFeatureChip/NewFeatureChip.test.tsx
create mode 100644 packages/ui/src/components/NewFeatureChip/NewFeatureChip.tsx
create mode 100644 packages/ui/src/components/NewFeatureChip/index.ts
diff --git a/packages/manager/cypress/component/components/beta-chip.spec.tsx b/packages/manager/cypress/component/components/beta-chip.spec.tsx
index b05c7b501da..dff3a4aa0f7 100644
--- a/packages/manager/cypress/component/components/beta-chip.spec.tsx
+++ b/packages/manager/cypress/component/components/beta-chip.spec.tsx
@@ -5,23 +5,13 @@ import { componentTests, visualTests } from 'support/util/components';
componentTests('BetaChip', () => {
visualTests((mount) => {
- it('renders "BETA" text indicator with primary color', () => {
- mount();
+ it('renders "BETA" text indicator', () => {
+ mount();
cy.findByText('beta').should('be.visible');
});
- it('renders "BETA" text indicator with default color', () => {
- mount();
- cy.findByText('beta').should('be.visible');
- });
-
- it('passes aXe check with primary color', () => {
- mount();
- checkComponentA11y();
- });
-
- it('passes aXe check with default color', () => {
- mount();
+ it('passes aXe accessibility', () => {
+ mount();
checkComponentA11y();
});
});
diff --git a/packages/manager/src/components/PrimaryNav/PrimaryLink.tsx b/packages/manager/src/components/PrimaryNav/PrimaryLink.tsx
index f2f50d090eb..2a85aec40ac 100644
--- a/packages/manager/src/components/PrimaryNav/PrimaryLink.tsx
+++ b/packages/manager/src/components/PrimaryNav/PrimaryLink.tsx
@@ -61,11 +61,7 @@ const PrimaryLink = React.memo((props: PrimaryLinkProps) => {
>
{display}
{isBeta ? (
-
+
) : null}
diff --git a/packages/manager/src/features/Account/AccountLanding.tsx b/packages/manager/src/features/Account/AccountLanding.tsx
index a047349a024..dd2bf476489 100644
--- a/packages/manager/src/features/Account/AccountLanding.tsx
+++ b/packages/manager/src/features/Account/AccountLanding.tsx
@@ -90,7 +90,7 @@ const AccountLanding = () => {
...(showQuotasTab
? [
{
- chip: ,
+ chip: ,
routeName: '/account/quotas',
title: 'Quotas',
},
diff --git a/packages/manager/src/features/Account/NetworkInterfaceType.tsx b/packages/manager/src/features/Account/NetworkInterfaceType.tsx
index 1010008239d..b6a472432ae 100644
--- a/packages/manager/src/features/Account/NetworkInterfaceType.tsx
+++ b/packages/manager/src/features/Account/NetworkInterfaceType.tsx
@@ -81,7 +81,7 @@ export const NetworkInterfaceType = () => {
}
+ headingChip={}
>