From 27a883da2e2c51cd9ad7474dacd324673298e0ac Mon Sep 17 00:00:00 2001 From: Alban Bailly <130582365+abailly-akamai@users.noreply.github.com> Date: Wed, 23 Oct 2024 18:40:42 -0400 Subject: [PATCH 01/66] fix: [M3-8774 ] - Faux bold in Safari with `` & `` tags (#11149) * Increase CSS specifity of strong & b for safari * Added changeset: Faux bold in Safari with & tags * Indentation --- .../.changeset/pr-11149-fixed-1729690586892.md | 5 +++++ packages/manager/src/index.css | 11 +++++------ 2 files changed, 10 insertions(+), 6 deletions(-) create mode 100644 packages/manager/.changeset/pr-11149-fixed-1729690586892.md diff --git a/packages/manager/.changeset/pr-11149-fixed-1729690586892.md b/packages/manager/.changeset/pr-11149-fixed-1729690586892.md new file mode 100644 index 00000000000..fd597d442cd --- /dev/null +++ b/packages/manager/.changeset/pr-11149-fixed-1729690586892.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +Faux bold in Safari with & tags ([#11149](https://github.com/linode/manager/pull/11149)) diff --git a/packages/manager/src/index.css b/packages/manager/src/index.css index 96ad3e87a85..f7349ba654b 100644 --- a/packages/manager/src/index.css +++ b/packages/manager/src/index.css @@ -16,6 +16,11 @@ body { margin: 0; padding: 0 !important; font-family: 'LatoWeb', sans-serif; + + strong, b { + font-family: 'LatoWebBold', sans-serif; + font-weight: normal; + } } body.searchOverlay #main-content { @@ -152,12 +157,6 @@ a.h-u:hover { text-decoration: underline; } -strong, -b { - font-family: 'LatoWebBold', sans-serif; - font-weight: normal; -} - .visually-hidden { position: absolute; height: 1px; From d5d849020fe93de6b883504eeb3057c2686d9c10 Mon Sep 17 00:00:00 2001 From: Harsh Shankar Rao Date: Thu, 24 Oct 2024 11:38:29 +0530 Subject: [PATCH 02/66] change: [M3-8373] - Fix unexpected heading spacing on Account Maintenance page (#11099) * change: [M3-8373] - Fixed unexpected heading spacing on Account Maintenance page * Added changeset: Align table headers in Account Maintenance page * change: M3-8373 - removed unused classes --- .../pr-11099-fixed-1728996803052.md | 5 +++ .../Maintenance/MaintenanceLanding.tsx | 5 ++- .../Account/Maintenance/MaintenanceTable.tsx | 37 +++++++++---------- 3 files changed, 25 insertions(+), 22 deletions(-) create mode 100644 packages/manager/.changeset/pr-11099-fixed-1728996803052.md diff --git a/packages/manager/.changeset/pr-11099-fixed-1728996803052.md b/packages/manager/.changeset/pr-11099-fixed-1728996803052.md new file mode 100644 index 00000000000..d5c87f66fb2 --- /dev/null +++ b/packages/manager/.changeset/pr-11099-fixed-1728996803052.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +Align table headers in Account Maintenance page ([#11099](https://github.com/linode/manager/pull/11099)) diff --git a/packages/manager/src/features/Account/Maintenance/MaintenanceLanding.tsx b/packages/manager/src/features/Account/Maintenance/MaintenanceLanding.tsx index 9cb08f8eba2..634cd91be8d 100644 --- a/packages/manager/src/features/Account/Maintenance/MaintenanceLanding.tsx +++ b/packages/manager/src/features/Account/Maintenance/MaintenanceLanding.tsx @@ -1,16 +1,17 @@ import * as React from 'react'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; +import { Stack } from 'src/components/Stack'; import { MaintenanceTable } from './MaintenanceTable'; const MaintenanceLanding = () => { return ( - <> + - + ); }; diff --git a/packages/manager/src/features/Account/Maintenance/MaintenanceTable.tsx b/packages/manager/src/features/Account/Maintenance/MaintenanceTable.tsx index 0bec6958632..fbdf1c156f9 100644 --- a/packages/manager/src/features/Account/Maintenance/MaintenanceTable.tsx +++ b/packages/manager/src/features/Account/Maintenance/MaintenanceTable.tsx @@ -6,6 +6,7 @@ import { Box } from 'src/components/Box'; import { DownloadCSV } from 'src/components/DownloadCSV/DownloadCSV'; import { Hidden } from 'src/components/Hidden'; import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFooter'; +import { Paper } from 'src/components/Paper'; import { Table } from 'src/components/Table'; import { TableBody } from 'src/components/TableBody'; import { TableCell } from 'src/components/TableCell'; @@ -27,7 +28,6 @@ import { import { MaintenanceTableRow } from './MaintenanceTableRow'; import type { AccountMaintenance, Filter } from '@linode/api-v4'; -import type { Theme } from '@mui/material/styles'; const preferenceKey = 'account-maintenance'; @@ -41,25 +41,17 @@ const headersForCSVDownload = [ { key: 'reason', label: 'Reason' }, ]; -const useStyles = makeStyles()((theme: Theme) => ({ +const useStyles = makeStyles()(() => ({ cell: { width: '12%', }, - headingContainer: { - marginBottom: theme.spacing(1), - marginTop: theme.spacing(1.5), - [theme.breakpoints.down('md')]: { - paddingLeft: theme.spacing(), - paddingRight: theme.spacing(), - }, - }, })); interface Props { type: 'completed' | 'pending'; } -const MaintenanceTable = ({ type }: Props) => { +export const MaintenanceTable = ({ type }: Props) => { const csvRef = React.useRef(); const { classes } = useStyles(); const pagination = usePagination(1, `${preferenceKey}-${type}`, type); @@ -143,11 +135,18 @@ const MaintenanceTable = ({ type }: Props) => { }; return ( - <> - + {type} @@ -161,7 +160,7 @@ const MaintenanceTable = ({ type }: Props) => { onClick={downloadCSV} /> - + @@ -222,8 +221,6 @@ const MaintenanceTable = ({ type }: Props) => { page={pagination.page} pageSize={pagination.pageSize} /> - + ); }; - -export { MaintenanceTable }; From 85edbfc4c0950a11aef0941295c3edd9f08c74a9 Mon Sep 17 00:00:00 2001 From: Connie Liu <139280159+coliu-akamai@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:38:30 -0400 Subject: [PATCH 03/66] test: [M3-8725] - Add cypress tests for creating LKE clusters with ACL (LKE ACL integration part 3) (#11132) * lke ip acl create tests * update to use mocks only * update copies * Added changeset: Added cypress tests for creating LKE clusters with ACL * update error validation test * forgot to remove .only * remove unnecessary comment from lke pr #1 --- .../pr-11132-tests-1729628045654.md | 5 + .../e2e/core/kubernetes/lke-create.spec.ts | 478 +++++++++++++++++- .../src/factories/kubernetesCluster.ts | 1 + .../KubeSummaryPanel.styles.tsx | 1 - 4 files changed, 482 insertions(+), 3 deletions(-) create mode 100644 packages/manager/.changeset/pr-11132-tests-1729628045654.md diff --git a/packages/manager/.changeset/pr-11132-tests-1729628045654.md b/packages/manager/.changeset/pr-11132-tests-1729628045654.md new file mode 100644 index 00000000000..2005408f5ff --- /dev/null +++ b/packages/manager/.changeset/pr-11132-tests-1729628045654.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tests +--- + +Added cypress tests for creating LKE clusters with ACL ([#11132](https://github.com/linode/manager/pull/11132)) diff --git a/packages/manager/cypress/e2e/core/kubernetes/lke-create.spec.ts b/packages/manager/cypress/e2e/core/kubernetes/lke-create.spec.ts index f354607d4a0..b9be678ad32 100644 --- a/packages/manager/cypress/e2e/core/kubernetes/lke-create.spec.ts +++ b/packages/manager/cypress/e2e/core/kubernetes/lke-create.spec.ts @@ -1,7 +1,25 @@ /** * @file LKE creation end-to-end tests. */ - +import { + accountFactory, + kubernetesClusterFactory, + kubernetesControlPlaneACLFactory, + kubernetesControlPlaneACLOptionsFactory, + linodeTypeFactory, + regionFactory, +} from 'src/factories'; +import { + mockCreateCluster, + mockGetCluster, + mockCreateClusterError, + mockGetControlPlaneACL, +} from 'support/intercepts/lke'; +import { mockGetAccount } from 'support/intercepts/account'; +import { + mockGetRegions, + mockGetRegionAvailability, +} from 'support/intercepts/regions'; import { KubernetesCluster } from '@linode/api-v4'; import { LkePlanDescription } from 'support/api/lke'; import { lkeClusterPlans } from 'support/constants/lke'; @@ -83,7 +101,9 @@ describe('LKE Cluster Creation', () => { it('can create an LKE cluster', () => { cy.tag('method:e2e', 'purpose:dcTesting'); const clusterLabel = randomLabel(); - const clusterRegion = chooseRegion(); + const clusterRegion = chooseRegion({ + capabilities: ['Kubernetes'], + }); const clusterVersion = '1.27'; const clusterPlans = new Array(2) .fill(null) @@ -367,3 +387,457 @@ describe('LKE Cluster Creation with DC-specific pricing', () => { }); }); }); + +describe('LKE Cluster Creation with ACL', () => { + /** + * - Confirms ACL flow does not exist if account doesn't have the corresponding capability + */ + it('does not show the ACL flow without the LKE ACL capability', () => { + mockGetAccount( + accountFactory.build({ + capabilities: [], + }) + ).as('getAccount'); + + cy.visitWithLogin('/kubernetes/clusters'); + + ui.button + .findByTitle('Create Cluster') + .should('be.visible') + .should('be.enabled') + .click(); + + cy.url().should('endWith', '/kubernetes/create'); + cy.wait(['@getAccount']); + + cy.contains('Control Plane ACL').should('not.exist'); + cy.contains( + 'Enable an access control list (ACL) on your LKE cluster to restrict access to your cluster’s control plane. When enabled, only the IP addresses and ranges specified by you can connect to the control plane.' + ).should('not.exist'); + cy.contains('Enable Control Plane ACL').should('not.exist'); + cy.contains('IPv4 Addresses or CIDRs').should('not.exist'); + cy.contains('IPv6 Addresses or CIDRs').should('not.exist'); + cy.contains('Add IPv4 Address').should('not.exist'); + cy.contains('Add IPv6 Address').should('not.exist'); + }); + + // setting up mocks + const clusterLabel = randomLabel(); + const mockRegion = regionFactory.build({ + capabilities: ['Linodes', 'Kubernetes'], + id: 'us-east', + label: 'Newark, US', + }); + const mockLinodeTypes = [ + linodeTypeFactory.build({ + id: 'dedicated-1', + label: 'dedicated-1', + class: 'dedicated', + }), + linodeTypeFactory.build({ + id: 'dedicated-2', + label: 'dedicated-2', + class: 'dedicated', + }), + ]; + const clusterVersion = '1.31'; + const clusterPlan = { size: 2, tab: 'Dedicated CPU', type: 'Dedicated' }; + const nodeCount = 1; + const planName = 'dedicated-1'; + const checkoutName = 'dedicated-1 Plan'; + + describe('with LKE IPACL account capability', () => { + beforeEach(() => { + mockGetAccount( + accountFactory.build({ + capabilities: [ + 'LKE HA Control Planes', + 'LKE Network Access Control List (IP ACL)', + ], + }) + ).as('getAccount'); + mockGetRegions([mockRegion]).as('getRegions'); + mockGetLinodeTypes(mockLinodeTypes).as('getLinodeTypes'); + mockGetRegionAvailability(mockRegion.id, []).as('getRegionAvailability'); + }); + + /** + * - Confirms create flow when ACL is toggled off + * - Confirms LKE summary page shows that ACL is not enabled + */ + it('creates an LKE cluster with ACL disabled', () => { + const mockACL = kubernetesControlPlaneACLFactory.build({ + acl: { + enabled: false, + 'revision-id': '', + }, + }); + const mockCluster = kubernetesClusterFactory.build({ + label: clusterLabel, + region: mockRegion.id, + k8s_version: clusterVersion, + control_plane: mockACL, + }); + mockCreateCluster(mockCluster).as('createCluster'); + mockGetCluster(mockCluster).as('getCluster'); + mockGetControlPlaneACL(mockCluster.id, mockACL).as('getControlPlaneACL'); + + cy.visitWithLogin('/kubernetes/clusters'); + + ui.button + .findByTitle('Create Cluster') + .should('be.visible') + .should('be.enabled') + .click(); + + cy.url().should('endWith', '/kubernetes/create'); + cy.wait(['@getAccount', '@getRegions', '@getLinodeTypes']); + + // Fill out LKE creation form label, region, and Kubernetes version fields. + cy.findByLabelText('Cluster Label') + .should('be.visible') + .click() + .type(`${clusterLabel}{enter}`); + + ui.regionSelect.find().click().type(`${mockRegion.label}{enter}`); + cy.wait(['@getRegionAvailability']); + + cy.findByText('Kubernetes Version') + .should('be.visible') + .click() + .type(`${clusterVersion}{enter}`); + + cy.get('[data-testid="ha-radio-button-yes"]') + .should('be.visible') + .click(); + + // Confirm ACL section and disable ACL + cy.contains('Control Plane ACL').should('be.visible'); + cy.contains( + 'Enable an access control list (ACL) on your LKE cluster to restrict access to your cluster’s control plane. When enabled, only the IP addresses and ranges specified by you can connect to the control plane.' + ).should('be.visible'); + cy.contains('Enable Control Plane ACL').should('be.visible'); + cy.contains('IPv4 Addresses or CIDRs').should('be.visible'); + cy.contains('IPv6 Addresses or CIDRs').should('be.visible'); + cy.contains('Add IPv4 Address').should('be.visible'); + cy.contains('Add IPv6 Address').should('be.visible'); + ui.toggle + .find() + .should('have.attr', 'data-qa-toggle', 'true') + .should('be.visible') + .click(); + // IP related fields do not exist when ACL is not enabled + cy.get('IPv4 Addresses or CIDRs').should('not.exist'); + cy.get('IPv6 Addresses or CIDRs').should('not.exist'); + cy.get('Add IPv4 Address').should('not.exist'); + cy.get('Add IPv6 Address').should('not.exist'); + + // Add a node pool + cy.log(`Adding ${nodeCount}x ${getLkePlanName(clusterPlan)} node(s)`); + cy.findByText(clusterPlan.tab).should('be.visible').click(); + cy.findByText(planName) + .should('be.visible') + .closest('tr') + .within(() => { + cy.get('[name="Quantity"]') + .should('be.visible') + .click() + .type(`{selectall}${nodeCount}`); + + ui.button + .findByTitle('Add') + .should('be.visible') + .should('be.enabled') + .click(); + }); + + // Confirm that node pool is shown in the checkout bar. + cy.get('[data-testid="kube-checkout-bar"]') + .should('be.visible') + .within(() => { + cy.findAllByText(checkoutName).first().should('be.visible'); + }); + + // create cluster + cy.get('[data-testid="kube-checkout-bar"]') + .should('be.visible') + .within(() => { + ui.button + .findByTitle('Create Cluster') + .should('be.visible') + .should('be.enabled') + .click(); + }); + + cy.wait('@createCluster').then(() => { + cy.url().should( + 'endWith', + `/kubernetes/clusters/${mockCluster.id}/summary` + ); + }); + + cy.wait(['@getCluster', '@getControlPlaneACL']); + + // Confirms Summary panel displays as expected + cy.contains('Control Plane ACL').should('be.visible'); + ui.button.findByTitle('Enable').should('be.visible').should('be.enabled'); + }); + + /** + * - Confirms create flow when ACL is toggled on + * - Confirms adding IPs + * - Confirms LKE summary page shows that ACL is enabled + */ + it('creates an LKE cluster with ACL enabled', () => { + const mockACLOptions = kubernetesControlPlaneACLOptionsFactory.build({ + 'revision-id': '', + }); + + const mockACL = kubernetesControlPlaneACLFactory.build({ + acl: mockACLOptions, + }); + + const mockCluster = kubernetesClusterFactory.build({ + label: clusterLabel, + region: mockRegion.id, + k8s_version: clusterVersion, + control_plane: mockACL, + }); + mockCreateCluster(mockCluster).as('createCluster'); + mockGetCluster(mockCluster).as('getCluster'); + mockGetControlPlaneACL(mockCluster.id, mockACL).as('getControlPlaneACL'); + + cy.visitWithLogin('/kubernetes/clusters'); + + ui.button + .findByTitle('Create Cluster') + .should('be.visible') + .should('be.enabled') + .click(); + + cy.url().should('endWith', '/kubernetes/create'); + cy.wait(['@getAccount']); + + // Fill out LKE creation form label, region, and Kubernetes version fields. + cy.findByLabelText('Cluster Label') + .should('be.visible') + .click() + .type(`${clusterLabel}{enter}`); + + ui.regionSelect.find().click().type(`${mockRegion.label}{enter}`); + + cy.findByText('Kubernetes Version') + .should('be.visible') + .click() + .type(`${clusterVersion}{enter}`); + + cy.get('[data-testid="ha-radio-button-yes"]') + .should('be.visible') + .click(); + + // Confirm ACL section + cy.contains('Control Plane ACL').should('be.visible'); + cy.contains( + 'Enable an access control list (ACL) on your LKE cluster to restrict access to your cluster’s control plane. When enabled, only the IP addresses and ranges specified by you can connect to the control plane.' + ).should('be.visible'); + cy.contains('Enable Control Plane ACL').should('be.visible'); + cy.contains('IPv4 Addresses or CIDRs').should('be.visible'); + cy.contains('IPv6 Addresses or CIDRs').should('be.visible'); + cy.contains('Add IPv4 Address').should('be.visible'); + cy.contains('Add IPv6 Address').should('be.visible'); + ui.toggle + .find() + .should('have.attr', 'data-qa-toggle', 'true') + .should('be.visible'); + // Add some IPv4s and an IPv6 + cy.findByPlaceholderText('0.0.0.0/0') + .should('be.visible') + .click() + .type('10.0.0.0/24'); + cy.findByText('Add IPv4 Address') + .should('be.visible') + .should('be.enabled') + .click(); + cy.get('[id="domain-transfer-ip-1"]') + .should('be.visible') + .click() + .type('10.0.1.0/24'); + cy.findByPlaceholderText('::/0') + .should('be.visible') + .click() + .type('8e61:f9e9:8d40:6e0a:cbff:c97a:2692:827e'); + cy.findByText('Add IPv6 Address') + .should('be.visible') + .should('be.enabled') + .click(); + + // Add a node pool + cy.log(`Adding ${nodeCount}x ${getLkePlanName(clusterPlan)} node(s)`); + cy.findByText(clusterPlan.tab).should('be.visible').click(); + cy.findByText(planName) + .should('be.visible') + .closest('tr') + .within(() => { + cy.get('[name="Quantity"]') + .should('be.visible') + .click() + .type(`{selectall}${nodeCount}`); + + ui.button + .findByTitle('Add') + .should('be.visible') + .should('be.enabled') + .click(); + }); + + // Confirm that node pool is shown in the checkout bar. + cy.get('[data-testid="kube-checkout-bar"]') + .should('be.visible') + .within(() => { + cy.findAllByText(checkoutName).first().should('be.visible'); + }); + + // create cluster + cy.get('[data-testid="kube-checkout-bar"]') + .should('be.visible') + .within(() => { + ui.button + .findByTitle('Create Cluster') + .should('be.visible') + .should('be.enabled') + .click(); + }); + + cy.wait('@createCluster').then(() => { + cy.url().should( + 'endWith', + `/kubernetes/clusters/${mockCluster.id}/summary` + ); + }); + + cy.wait(['@getCluster', '@getControlPlaneACL']); + + // Confirms Summary panel displays as expected + cy.contains('Control Plane ACL').should('be.visible'); + ui.button + .findByTitle('Enabled (3 IP Addresses)') + .should('be.visible') + .should('be.enabled'); + }); + + /** + * - Confirms IP validation error appears when a bad IP is entered + * - Confirms IP validation error disappears when a valid IP is entered + * - Confirms API error appears as expected and doesn't crash the page + */ + it('can handle validation and API errors', () => { + const mockErrorMessage = 'Control Plane ACL error: request failed'; + mockCreateClusterError(mockErrorMessage, 400).as('createClusterError'); + + cy.visitWithLogin('/kubernetes/clusters'); + + ui.button + .findByTitle('Create Cluster') + .should('be.visible') + .should('be.enabled') + .click(); + + cy.url().should('endWith', '/kubernetes/create'); + cy.wait(['@getAccount']); + + // Fill out LKE creation form label, region, and Kubernetes version fields. + cy.findByLabelText('Cluster Label') + .should('be.visible') + .click() + .type(`${clusterLabel}{enter}`); + + ui.regionSelect.find().click().type(`${mockRegion.label}{enter}`); + + cy.findByText('Kubernetes Version') + .should('be.visible') + .click() + .type(`${clusterVersion}{enter}`); + + cy.get('[data-testid="ha-radio-button-yes"]') + .should('be.visible') + .click(); + + // Confirm ACL IPv4 validation works as expected + cy.findByPlaceholderText('0.0.0.0/0') + .should('be.visible') + .click() + .type('invalid ip'); + // click out of textbox and confirm error is visible + cy.contains('Control Plane ACL').should('be.visible').click(); + cy.contains('Must be a valid IPv4 address.').should('be.visible'); + // enter valid IP + cy.findByPlaceholderText('0.0.0.0/0') + .should('be.visible') + .click() + .clear() + .type('10.0.0.0/24'); + // Click out of textbox and confirm error is gone + cy.contains('Control Plane ACL').should('be.visible').click(); + cy.contains('Must be a valid IPv4 address.').should('not.exist'); + + // Confirm ACL IPv6 validation works as expected + cy.findByPlaceholderText('::/0') + .should('be.visible') + .click() + .type('invalid ip'); + // click out of textbox and confirm error is visible + cy.contains('Control Plane ACL').should('be.visible').click(); + cy.contains('Must be a valid IPv6 address.').should('be.visible'); + // enter valid IP + cy.findByPlaceholderText('::/0') + .should('be.visible') + .click() + .clear() + .type('8e61:f9e9:8d40:6e0a:cbff:c97a:2692:827e'); + // Click out of textbox and confirm error is gone + cy.contains('Control Plane ACL').should('be.visible').click(); + cy.contains('Must be a valid IPv6 address.').should('not.exist'); + + // Add a node pool + cy.log(`Adding ${nodeCount}x ${getLkePlanName(clusterPlan)} node(s)`); + cy.findByText(clusterPlan.tab).should('be.visible').click(); + cy.findByText(planName) + .should('be.visible') + .closest('tr') + .within(() => { + cy.get('[name="Quantity"]') + .should('be.visible') + .click() + .type(`{selectall}${nodeCount}`); + + ui.button + .findByTitle('Add') + .should('be.visible') + .should('be.enabled') + .click(); + }); + + // Confirm that node pool is shown in the checkout bar. + cy.get('[data-testid="kube-checkout-bar"]') + .should('be.visible') + .within(() => { + cy.findAllByText(checkoutName).first().should('be.visible'); + }); + + // Attempt to create cluster + cy.get('[data-testid="kube-checkout-bar"]') + .should('be.visible') + .within(() => { + ui.button + .findByTitle('Create Cluster') + .should('be.visible') + .should('be.enabled') + .click(); + }); + + // Confirm API error displays + cy.wait('@createClusterError'); + cy.contains(mockErrorMessage).should('be.visible'); + }); + }); +}); diff --git a/packages/manager/src/factories/kubernetesCluster.ts b/packages/manager/src/factories/kubernetesCluster.ts index e7b3bdff2ea..9680a530b95 100644 --- a/packages/manager/src/factories/kubernetesCluster.ts +++ b/packages/manager/src/factories/kubernetesCluster.ts @@ -88,6 +88,7 @@ export const kubernetesControlPlaneACLOptionsFactory = Factory.Sync.makeFactory< 'revision-id': '67497a9c5fc8491889a7ef8107493e92', } ); + export const kubernetesControlPlaneACLFactory = Factory.Sync.makeFactory( { acl: { diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeSummaryPanel.styles.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeSummaryPanel.styles.tsx index 3dc8e3799e9..7a48ab8b871 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeSummaryPanel.styles.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeSummaryPanel.styles.tsx @@ -1,4 +1,3 @@ -// This component was built asuming an unmodified MUI
import { styled } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; From 427b55c44e768242fd4a34077b65540179c3e7a8 Mon Sep 17 00:00:00 2001 From: Banks Nussman <115251059+bnussman-akamai@users.noreply.github.com> Date: Thu, 24 Oct 2024 13:34:33 -0400 Subject: [PATCH 04/66] refactor: [M3-8775] - Make the Support Ticket UI look slightly less broken (#11144) * initial work * fix more ui * add changeset * fix padding on preview paper * feedback @mjac0bs * fix top padding @harsh-akamai --------- Co-authored-by: Banks Nussman --- .../pr-11144-changed-1729684811107.md | 5 ++ packages/manager/src/components/Paper.tsx | 4 +- .../TabbedReply/MarkdownReference.tsx | 58 +++++++--------- .../TabbedReply/PreviewReply.tsx | 6 +- .../TabbedReply/ReplyContainer.tsx | 60 +++++------------ .../TabbedReply/TabbedReply.tsx | 66 +++++++------------ .../SupportTickets/SupportTicketDialog.tsx | 63 ++++++------------ 7 files changed, 95 insertions(+), 167 deletions(-) create mode 100644 packages/manager/.changeset/pr-11144-changed-1729684811107.md diff --git a/packages/manager/.changeset/pr-11144-changed-1729684811107.md b/packages/manager/.changeset/pr-11144-changed-1729684811107.md new file mode 100644 index 00000000000..2b69521036d --- /dev/null +++ b/packages/manager/.changeset/pr-11144-changed-1729684811107.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Changed +--- + +Slightly improve styles on support ticket flows ([#11144](https://github.com/linode/manager/pull/11144)) diff --git a/packages/manager/src/components/Paper.tsx b/packages/manager/src/components/Paper.tsx index 61bc8f129c3..5b8355cad18 100644 --- a/packages/manager/src/components/Paper.tsx +++ b/packages/manager/src/components/Paper.tsx @@ -40,11 +40,11 @@ export const Paper = (props: Props) => { const StyledPaper = styled(_Paper, { shouldForwardProp: omittedProps(['error']), })(({ theme, ...props }) => ({ - borderColor: props.error ? theme.color.red : undefined, + borderColor: props.error ? theme.palette.error.dark : undefined, padding: theme.spacing(3), paddingTop: 17, })); const StyledErrorText = styled(FormHelperText)(({ theme }) => ({ - color: theme.color.red, + color: theme.palette.error.dark, })); diff --git a/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/MarkdownReference.tsx b/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/MarkdownReference.tsx index e0c27ecc042..b9b98e2ffe1 100644 --- a/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/MarkdownReference.tsx +++ b/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/MarkdownReference.tsx @@ -1,66 +1,56 @@ import * as React from 'react'; -import { makeStyles } from 'tss-react/mui'; +import { HighlightedMarkdown } from 'src/components/HighlightedMarkdown/HighlightedMarkdown'; import { Link } from 'src/components/Link'; +import { Paper } from 'src/components/Paper'; +import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; -import type { Theme } from '@mui/material/styles'; - -const useStyles = makeStyles()((theme: Theme) => ({ - example: { - backgroundColor: theme.name === 'dark' ? theme.bg.white : theme.bg.offWhite, - margin: `${theme.spacing(2)} 0`, - padding: theme.spacing(2), - }, - header: { - marginBottom: theme.spacing(), - marginTop: theme.spacing(2), - }, -})); - interface Props { isReply?: boolean; - rootClass?: string; } export const MarkdownReference = (props: Props) => { - const { classes } = useStyles(); - return ( -
+ You can use Markdown to format your{' '} - {props.isReply ? 'reply' : 'question'}. For more examples see this{' '} + {props.isReply ? 'reply' : 'question'}. For more examples, see this{' '} Markdown cheatsheet - - Examples - -
+ theme.font.bold}>Examples + ({ + backgroundColor: theme.palette.background.default, + p: 2, + })} + > [I am a link](https://google.com) -
I am a link', }} /> -
-
+ + ({ + backgroundColor: theme.palette.background.default, + p: 2, + })} + > - ``` + ```js
const someCode = 'hello world';
```
- const someCode = "hello world"`, - }} + -
-
+ + ); }; diff --git a/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/PreviewReply.tsx b/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/PreviewReply.tsx index 8fb97b8ceda..d786e28a852 100644 --- a/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/PreviewReply.tsx +++ b/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/PreviewReply.tsx @@ -14,12 +14,12 @@ export const PreviewReply = (props: Props) => { return ( diff --git a/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/ReplyContainer.tsx b/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/ReplyContainer.tsx index 6752630d1c4..383ed47eeb4 100644 --- a/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/ReplyContainer.tsx +++ b/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/ReplyContainer.tsx @@ -1,11 +1,9 @@ -import { SupportReply, uploadAttachment } from '@linode/api-v4/lib/support'; -import { APIError } from '@linode/api-v4/lib/types'; +import { uploadAttachment } from '@linode/api-v4'; import Grid from '@mui/material/Unstable_Grid2'; -import { Theme } from '@mui/material/styles'; -import { makeStyles } from 'tss-react/mui'; import { lensPath, set } from 'ramda'; import * as React from 'react'; import { debounce } from 'throttle-debounce'; +import { makeStyles } from 'tss-react/mui'; import { Accordion } from 'src/components/Accordion'; import { Notice } from 'src/components/Notice/Notice'; @@ -14,37 +12,19 @@ import { getAPIErrorOrDefault, getErrorMap } from 'src/utilities/errorUtils'; import { storage } from 'src/utilities/storage'; import { AttachFileForm } from '../../AttachFileForm'; -import { FileAttachment } from '../../index'; import { MarkdownReference } from './MarkdownReference'; import { ReplyActions } from './ReplyActions'; import { TabbedReply } from './TabbedReply'; +import type { FileAttachment } from '../../index'; +import type { APIError, SupportReply } from '@linode/api-v4'; +import type { Theme } from '@mui/material/styles'; + const useStyles = makeStyles()((theme: Theme) => ({ - expPanelSummary: { - backgroundColor: theme.name === 'dark' ? theme.bg.main : theme.bg.white, - borderTop: `1px solid ${theme.bg.main}`, - paddingTop: theme.spacing(), - }, - reference: { - [theme.breakpoints.down('sm')]: { - padding: `${theme.spacing(2)} !important`, - }, - [theme.breakpoints.up('sm')]: { - marginLeft: 4, - marginRight: 4, - marginTop: theme.spacing(7), - padding: `0 !important`, - }, - }, - referenceRoot: { - '& > p': { - marginBottom: theme.spacing(), - }, - }, replyContainer: { paddingLeft: theme.spacing(6), [theme.breakpoints.down('sm')]: { - paddingLeft: theme.spacing(6), + paddingLeft: theme.spacing(5), }, }, })); @@ -172,23 +152,15 @@ export const ReplyContainer = (props: Props) => { variant="error" /> )} - - - - - - - - + + + + diff --git a/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/TabbedReply.tsx b/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/TabbedReply.tsx index 2b0c91931cb..5a309481537 100644 --- a/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/TabbedReply.tsx +++ b/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/TabbedReply.tsx @@ -1,58 +1,40 @@ -import { Theme } from '@mui/material/styles'; -import { makeStyles } from 'tss-react/mui'; -import * as React from 'react'; +import React from 'react'; -import { Tab, TabbedPanel } from 'src/components/TabbedPanel/TabbedPanel'; +import { SafeTabPanel } from 'src/components/Tabs/SafeTabPanel'; +import { Tab } from 'src/components/Tabs/Tab'; +import { TabList } from 'src/components/Tabs/TabList'; +import { TabPanels } from 'src/components/Tabs/TabPanels'; +import { Tabs } from 'src/components/Tabs/Tabs'; import { PreviewReply } from './PreviewReply'; -import { Props as ReplyProps, TicketReply } from './TicketReply'; +import { TicketReply } from './TicketReply'; + +import type { Props as ReplyProps } from './TicketReply'; interface Props extends ReplyProps { - innerClass?: string; isReply?: boolean; required?: boolean; - rootClass?: string; } -const useStyles = makeStyles()((theme: Theme) => ({ - root: { - '& div[role="tablist"]': { - marginBottom: theme.spacing(), - marginTop: theme.spacing(), - }, - backgroundColor: 'transparent', - padding: 0, - }, -})); - export const TabbedReply = (props: Props) => { - const { classes } = useStyles(); - const { error, innerClass, rootClass, value, ...rest } = props; + const { error, value, ...rest } = props; const title = props.isReply ? 'Reply' : 'Description'; - const tabs: Tab[] = [ - { - render: () => { - return ; - }, - title: props.required ? `${title} (required)` : title, - }, - { - render: () => { - return ; - }, - title: 'Preview', - }, - ]; - return ( - + + + {props.required ? `${title} (required)` : title} + Preview + + + + + + + + + + ); }; diff --git a/packages/manager/src/features/Support/SupportTickets/SupportTicketDialog.tsx b/packages/manager/src/features/Support/SupportTickets/SupportTicketDialog.tsx index b401dd0c7ec..e80beeeda03 100644 --- a/packages/manager/src/features/Support/SupportTickets/SupportTicketDialog.tsx +++ b/packages/manager/src/features/Support/SupportTickets/SupportTicketDialog.tsx @@ -5,11 +5,11 @@ import * as React from 'react'; import { Controller, FormProvider, useForm } from 'react-hook-form'; import { useLocation } from 'react-router-dom'; import { debounce } from 'throttle-debounce'; -import { makeStyles } from 'tss-react/mui'; import { Accordion } from 'src/components/Accordion'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; +import { Box } from 'src/components/Box'; import { Dialog } from 'src/components/Dialog/Dialog'; import { Notice } from 'src/components/Notice/Notice'; import { TextField } from 'src/components/TextField'; @@ -44,29 +44,8 @@ import type { SMTPCustomFields } from './SupportTicketSMTPFields'; import type { CreateKubeClusterPayload } from '@linode/api-v4'; import type { TicketSeverity } from '@linode/api-v4/lib/support'; import type { CreateLinodeRequest } from '@linode/api-v4/src/linodes/types'; -import type { Theme } from '@mui/material/styles'; import type { EntityForTicketDetails } from 'src/components/SupportLink/SupportLink'; -const useStyles = makeStyles()((theme: Theme) => ({ - expPanelSummary: { - backgroundColor: theme.name === 'dark' ? theme.bg.main : theme.bg.white, - borderTop: `1px solid ${theme.bg.main}`, - paddingTop: theme.spacing(1), - }, - innerReply: { - '& div[role="tablist"]': { - marginBottom: theme.spacing(), - marginTop: theme.spacing(), - }, - padding: 0, - }, - rootReply: { - marginBottom: theme.spacing(2), - marginTop: theme.spacing(2), - padding: 0, - }, -})); - interface Accumulator { errors: AttachmentError[]; success: string[]; @@ -212,8 +191,6 @@ export const SupportTicketDialog = (props: SupportTicketDialogProps) => { const [submitting, setSubmitting] = React.useState(false); - const { classes } = useStyles(); - React.useEffect(() => { if (!open) { resetDrawer(); @@ -490,26 +467,28 @@ export const SupportTicketDialog = (props: SupportTicketDialogProps) => { {props.hideProductSelection ? null : ( )} - ( - - )} - control={form.control} - name="description" - /> + + ( + + )} + control={form.control} + name="description" + /> + ({ mt: `${theme.spacing(0.5)} !important` })} // forcefully disable margin when accordion is expanded > From f3d86c040f9818293be5a1d644cfc9a657e522fc Mon Sep 17 00:00:00 2001 From: Hussain Khalil <122488130+hkhalil-akamai@users.noreply.github.com> Date: Thu, 24 Oct 2024 14:10:48 -0400 Subject: [PATCH 05/66] refactor: [M3-8707] - Move `Tooltip` component to UI package (#11125) * Move `Tooltip` component to UI package and update usages * Add deprecation annotation * Move story to ui package and delete original component definition * Fix unit test @dwiley-akamai --- packages/manager/.storybook/main.ts | 1 + packages/manager/.storybook/utils.test.ts | 9 +++++---- packages/manager/.storybook/utils.ts | 14 +++++++------- .../src/components/BackupStatus/BackupStatus.tsx | 5 +++-- .../manager/src/components/Button/Button.tsx | 2 +- .../src/components/CopyTooltip/CopyTooltip.tsx | 4 ++-- .../manager/src/components/DownloadTooltip.tsx | 2 +- .../manager/src/components/GroupByTagToggle.tsx | 2 +- .../src/components/ImageSelect/ImageOption.tsx | 2 +- .../components/ImageSelectv2/ImageOptionv2.tsx | 2 +- .../PlacementGroupSelectOption.tsx | 5 +++-- .../src/components/PrimaryNav/NavItem.tsx | 2 +- .../src/components/RegionSelect/RegionOption.tsx | 2 +- .../components/SelectionCard/SelectionCard.tsx | 5 ++--- .../src/components/TabbedPanel/TabbedPanel.tsx | 4 ++-- packages/manager/src/components/TextField.tsx | 2 +- .../src/components/TextTooltip/TextTooltip.tsx | 2 +- packages/manager/src/components/TooltipIcon.tsx | 4 ++-- .../src/components/Uploaders/FileUpload.tsx | 5 +++-- .../manager/src/dev-tools/ServiceWorkerTool.tsx | 2 +- .../Account/Maintenance/MaintenanceTableRow.tsx | 8 +++++--- .../PaymentDrawer/GooglePayButton.tsx | 5 +++-- .../PaymentDrawer/PayPalButton.tsx | 16 +++++++++------- .../CloudPulse/shared/CloudPulseTooltip.tsx | 3 +-- .../CreateTransferSuccessDialog.tsx | 5 +++-- .../ImageRegions/ImageRegionRow.tsx | 2 +- .../features/Images/ImagesLanding/ImageRow.tsx | 2 +- .../NodePoolsDisplay/NodePool.tsx | 2 +- .../LinodeNetworking/AddIPDrawer.tsx | 2 +- .../LinodesLanding/DisplayGroupedLinodes.tsx | 2 +- .../Linodes/LinodesLanding/DisplayLinodes.tsx | 2 +- .../LinodesLanding/LinodeRow/LinodeRow.tsx | 2 +- .../Linodes/LinodesLanding/SortableTableHead.tsx | 2 +- .../src/features/Managed/Monitors/IssueDay.tsx | 5 +++-- .../src/features/Managed/Monitors/MonitorRow.tsx | 7 ++++--- .../AccessKeyLanding/AccessCell.tsx | 2 +- .../AccessKeyLanding/CopyAllHostnames.tsx | 2 +- .../PlacementGroups/PlacementGroupTypeSelect.tsx | 2 +- .../src/features/TopMenu/TopMenuTooltip.tsx | 3 +-- .../src/features/TopMenu/UserMenu/UserMenu.tsx | 2 +- .../PlansPanel/DisabledPlanSelectionTooltip.tsx | 2 +- .../utilities/noneSingleOrMultipleWithChip.tsx | 2 +- .../src/components/Tooltip.stories.tsx | 2 +- .../{manager => ui}/src/components/Tooltip.tsx | 0 packages/ui/src/components/index.ts | 1 + 45 files changed, 85 insertions(+), 74 deletions(-) rename packages/{manager => ui}/src/components/Tooltip.stories.tsx (91%) rename packages/{manager => ui}/src/components/Tooltip.tsx (100%) diff --git a/packages/manager/.storybook/main.ts b/packages/manager/.storybook/main.ts index c8ca860f561..831c9af0224 100644 --- a/packages/manager/.storybook/main.ts +++ b/packages/manager/.storybook/main.ts @@ -8,6 +8,7 @@ const config: StorybookConfig = { stories: [ '../src/components/**/*.@(mdx|stories.@(js|ts|jsx|tsx))', '../src/features/**/*.@(mdx|stories.@(js|ts|jsx|tsx))', + '../../ui/src/components/**/*.@(mdx|stories.@(js|ts|jsx|tsx))', ], addons: [ '@storybook/addon-docs', diff --git a/packages/manager/.storybook/utils.test.ts b/packages/manager/.storybook/utils.test.ts index 7d87d853c66..f21815e432e 100644 --- a/packages/manager/.storybook/utils.test.ts +++ b/packages/manager/.storybook/utils.test.ts @@ -3,24 +3,25 @@ import { getReactDocgenTSFileGlobs } from './utils'; describe('getReactDocgenTSFileGlobs', () => { const typeScriptFileGlobs = getReactDocgenTSFileGlobs(); it('should return component and feature globs for storybook files', () => { + console.log(typeScriptFileGlobs); expect( typeScriptFileGlobs.some( - (file) => file === 'src/components/Button/**/*.{ts,tsx}' + (file) => file === '../manager/src/components/Button/**/*.{ts,tsx}' ) ).toBe(true); expect( typeScriptFileGlobs.some( - (file) => file === 'src/components/Paper.{ts,tsx}' + (file) => file === '../manager/src/components/Paper.{ts,tsx}' ) ).toBe(true); expect( typeScriptFileGlobs.some( - (file) => file === 'src/features/TopMenu/**/*.{ts,tsx}' + (file) => file === '../manager/src/features/TopMenu/**/*.{ts,tsx}' ) ).toBe(true); expect( typeScriptFileGlobs.some( - (file) => file === 'src/features/Longview/**/*.{ts,tsx}' + (file) => file === '../manager/src/features/Longview/**/*.{ts,tsx}' ) ).toBe(false); }); diff --git a/packages/manager/.storybook/utils.ts b/packages/manager/.storybook/utils.ts index f6a75ea62ee..e0024ef61fe 100644 --- a/packages/manager/.storybook/utils.ts +++ b/packages/manager/.storybook/utils.ts @@ -1,6 +1,6 @@ import globby from 'globby'; -const PATTERN = 'src/**/*.stories.tsx'; +const PATTERN = '../**/src/**/*.stories.tsx'; /** * Find all storybook files, then return the glob containing the parent component/feature. @@ -11,18 +11,18 @@ const PATTERN = 'src/**/*.stories.tsx'; */ export const getReactDocgenTSFileGlobs = () => { const filesWithStories = globby.sync(PATTERN); - const files: string[] = []; + const files = new Set(); filesWithStories.forEach((file) => { - const execArr = /(src\/(components|features)\/[a-zA-Z]*(.|\/))/.exec(file); + const execArr = /^(.*src\/(components|features)\/[a-zA-Z]*(.|\/))/.exec( + file + ); if (execArr) { const isDirectory = execArr[3] === '/'; const fileBlob = `${execArr[0]}${isDirectory ? '**/*.' : ''}{ts,tsx}`; - if (!files.includes(fileBlob)) { - files.push(fileBlob); - } + files.add(fileBlob); } }); - return files; + return Array.from(files); }; diff --git a/packages/manager/src/components/BackupStatus/BackupStatus.tsx b/packages/manager/src/components/BackupStatus/BackupStatus.tsx index 7b6123890b3..f9d34e7e5a8 100644 --- a/packages/manager/src/components/BackupStatus/BackupStatus.tsx +++ b/packages/manager/src/components/BackupStatus/BackupStatus.tsx @@ -1,15 +1,16 @@ +import { Tooltip } from '@linode/ui'; import Backup from '@mui/icons-material/Backup'; -import { Theme } from '@mui/material/styles'; import * as React from 'react'; import { Link as RouterLink } from 'react-router-dom'; import { makeStyles } from 'tss-react/mui'; import { DateTimeDisplay } from 'src/components/DateTimeDisplay'; import { Link } from 'src/components/Link'; -import { Tooltip } from 'src/components/Tooltip'; import { TooltipIcon } from 'src/components/TooltipIcon'; import { Typography } from 'src/components/Typography'; +import type { Theme } from '@mui/material/styles'; + const useStyles = makeStyles()( (theme: Theme, _params, classes) => ({ backupLink: { diff --git a/packages/manager/src/components/Button/Button.tsx b/packages/manager/src/components/Button/Button.tsx index 4dd8d635979..32065827df9 100644 --- a/packages/manager/src/components/Button/Button.tsx +++ b/packages/manager/src/components/Button/Button.tsx @@ -1,10 +1,10 @@ +import { Tooltip } from '@linode/ui'; import HelpOutline from '@mui/icons-material/HelpOutline'; import _Button from '@mui/material/Button'; import { styled } from '@mui/material/styles'; import * as React from 'react'; import Reload from 'src/assets/icons/reload.svg'; -import { Tooltip } from 'src/components/Tooltip'; import { rotate360 } from '../../styles/keyframes'; import { omittedProps } from '../../utilities/omittedProps'; diff --git a/packages/manager/src/components/CopyTooltip/CopyTooltip.tsx b/packages/manager/src/components/CopyTooltip/CopyTooltip.tsx index 1a244e89044..1d263a6fb49 100644 --- a/packages/manager/src/components/CopyTooltip/CopyTooltip.tsx +++ b/packages/manager/src/components/CopyTooltip/CopyTooltip.tsx @@ -1,12 +1,12 @@ +import { Tooltip } from '@linode/ui'; import { styled } from '@mui/material/styles'; import copy from 'copy-to-clipboard'; import * as React from 'react'; import FileCopy from 'src/assets/icons/copy.svg'; -import { Tooltip } from 'src/components/Tooltip'; import { omittedProps } from 'src/utilities/omittedProps'; -import type { TooltipProps } from 'src/components/Tooltip'; +import type { TooltipProps } from '@linode/ui'; export interface CopyTooltipProps { /** diff --git a/packages/manager/src/components/DownloadTooltip.tsx b/packages/manager/src/components/DownloadTooltip.tsx index ec1a8abddf1..b6e697e8cf6 100644 --- a/packages/manager/src/components/DownloadTooltip.tsx +++ b/packages/manager/src/components/DownloadTooltip.tsx @@ -1,8 +1,8 @@ +import { Tooltip } from '@linode/ui'; import * as React from 'react'; import FileDownload from 'src/assets/icons/download.svg'; import { StyledIconButton } from 'src/components/CopyTooltip/CopyTooltip'; -import { Tooltip } from 'src/components/Tooltip'; import { Typography } from 'src/components/Typography'; import { downloadFile } from 'src/utilities/downloadFile'; diff --git a/packages/manager/src/components/GroupByTagToggle.tsx b/packages/manager/src/components/GroupByTagToggle.tsx index 1193bb949ff..1a9fcfdf6e3 100644 --- a/packages/manager/src/components/GroupByTagToggle.tsx +++ b/packages/manager/src/components/GroupByTagToggle.tsx @@ -1,7 +1,7 @@ +import { Tooltip } from '@linode/ui'; import * as React from 'react'; import GroupByTag from 'src/assets/icons/group-by-tag.svg'; -import { Tooltip } from 'src/components/Tooltip'; import { StyledToggleButton } from 'src/features/Linodes/LinodesLanding/DisplayLinodes.styles'; interface GroupByTagToggleProps { diff --git a/packages/manager/src/components/ImageSelect/ImageOption.tsx b/packages/manager/src/components/ImageSelect/ImageOption.tsx index e6b8ecef3d6..907a4839382 100644 --- a/packages/manager/src/components/ImageSelect/ImageOption.tsx +++ b/packages/manager/src/components/ImageSelect/ImageOption.tsx @@ -1,3 +1,4 @@ +import { Tooltip } from '@linode/ui'; import React from 'react'; import { makeStyles } from 'tss-react/mui'; @@ -8,7 +9,6 @@ import { Option } from 'src/components/EnhancedSelect/components/Option'; import { useFlags } from 'src/hooks/useFlags'; import { Stack } from '../Stack'; -import { Tooltip } from '../Tooltip'; import type { ImageItem } from './ImageSelect'; import type { Theme } from '@mui/material/styles'; diff --git a/packages/manager/src/components/ImageSelectv2/ImageOptionv2.tsx b/packages/manager/src/components/ImageSelectv2/ImageOptionv2.tsx index a2f6a2638b7..ba21fb9c391 100644 --- a/packages/manager/src/components/ImageSelectv2/ImageOptionv2.tsx +++ b/packages/manager/src/components/ImageSelectv2/ImageOptionv2.tsx @@ -1,3 +1,4 @@ +import { Tooltip } from '@linode/ui'; import React from 'react'; import CloudInitIcon from 'src/assets/icons/cloud-init.svg'; @@ -7,7 +8,6 @@ import { useFlags } from 'src/hooks/useFlags'; import { SelectedIcon } from '../Autocomplete/Autocomplete.styles'; import { OSIcon } from '../OSIcon'; import { Stack } from '../Stack'; -import { Tooltip } from '../Tooltip'; import { Typography } from '../Typography'; import { isImageDeprecated } from './utilities'; diff --git a/packages/manager/src/components/PlacementGroupsSelect/PlacementGroupSelectOption.tsx b/packages/manager/src/components/PlacementGroupsSelect/PlacementGroupSelectOption.tsx index 3473c7273c7..56b81fabfc4 100644 --- a/packages/manager/src/components/PlacementGroupsSelect/PlacementGroupSelectOption.tsx +++ b/packages/manager/src/components/PlacementGroupsSelect/PlacementGroupSelectOption.tsx @@ -1,10 +1,10 @@ -import { PLACEMENT_GROUP_TYPES, PlacementGroup } from '@linode/api-v4'; +import { PLACEMENT_GROUP_TYPES } from '@linode/api-v4'; +import { Tooltip } from '@linode/ui'; import { visuallyHidden } from '@mui/utils'; import React from 'react'; import { Box } from 'src/components/Box'; import { Stack } from 'src/components/Stack'; -import { Tooltip } from 'src/components/Tooltip'; import { PLACEMENT_GROUP_HAS_NO_CAPACITY } from 'src/features/PlacementGroups/constants'; import { @@ -12,6 +12,7 @@ import { StyledListItem, } from '../RegionSelect/RegionSelect.styles'; +import type { PlacementGroup } from '@linode/api-v4'; import type { ListItemComponentsPropsOverrides } from '@mui/material/ListItem'; interface PlacementGroupSelectOptionProps { diff --git a/packages/manager/src/components/PrimaryNav/NavItem.tsx b/packages/manager/src/components/PrimaryNav/NavItem.tsx index 9ce407f5e30..4e210b5f4ca 100644 --- a/packages/manager/src/components/PrimaryNav/NavItem.tsx +++ b/packages/manager/src/components/PrimaryNav/NavItem.tsx @@ -1,3 +1,4 @@ +import { Tooltip } from '@linode/ui'; import * as React from 'react'; import { Link } from 'react-router-dom'; import { useStyles } from 'tss-react/mui'; @@ -5,7 +6,6 @@ import { useStyles } from 'tss-react/mui'; import { Divider } from 'src/components/Divider'; import { ListItem } from 'src/components/ListItem'; import { ListItemText } from 'src/components/ListItemText'; -import { Tooltip } from 'src/components/Tooltip'; interface Props extends PrimaryLink { closeMenu: () => void; diff --git a/packages/manager/src/components/RegionSelect/RegionOption.tsx b/packages/manager/src/components/RegionSelect/RegionOption.tsx index 14f4fdc66a8..b0c07608158 100644 --- a/packages/manager/src/components/RegionSelect/RegionOption.tsx +++ b/packages/manager/src/components/RegionSelect/RegionOption.tsx @@ -1,3 +1,4 @@ +import { Tooltip } from '@linode/ui'; import { visuallyHidden } from '@mui/utils'; import React from 'react'; @@ -6,7 +7,6 @@ import { Box } from 'src/components/Box'; import { Flag } from 'src/components/Flag'; import { useIsGeckoEnabled } from 'src/components/RegionSelect/RegionSelect.utils'; import { Stack } from 'src/components/Stack'; -import { Tooltip } from 'src/components/Tooltip'; import { TooltipIcon } from 'src/components/TooltipIcon'; import { diff --git a/packages/manager/src/components/SelectionCard/SelectionCard.tsx b/packages/manager/src/components/SelectionCard/SelectionCard.tsx index 4fdcbe58fa1..b82143a66fb 100644 --- a/packages/manager/src/components/SelectionCard/SelectionCard.tsx +++ b/packages/manager/src/components/SelectionCard/SelectionCard.tsx @@ -1,9 +1,8 @@ +import { Tooltip } from '@linode/ui'; import { styled } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; -import { Tooltip } from 'src/components/Tooltip'; - import { CardBase } from './CardBase'; import type { SxProps, Theme } from '@mui/material/styles'; @@ -148,9 +147,9 @@ export const SelectionCard = React.memo((props: SelectionCardProps) => { const cardGrid = ( { {tabs.map((tab, idx) => ( {tab.render(rest.children)} diff --git a/packages/manager/src/components/TextField.tsx b/packages/manager/src/components/TextField.tsx index 824ec03748b..1926202b3bc 100644 --- a/packages/manager/src/components/TextField.tsx +++ b/packages/manager/src/components/TextField.tsx @@ -13,10 +13,10 @@ import { InputLabel } from 'src/components/InputLabel'; import { TooltipIcon } from 'src/components/TooltipIcon'; import { convertToKebabCase } from 'src/utilities/convertToKebobCase'; +import type { TooltipProps } from '@linode/ui'; import type { Theme } from '@mui/material/styles'; import type { StandardTextFieldProps } from '@mui/material/TextField'; import type { BoxProps } from 'src/components/Box'; -import type { TooltipProps } from 'src/components/Tooltip'; const useStyles = makeStyles()((theme: Theme) => ({ absolute: { diff --git a/packages/manager/src/components/TextTooltip/TextTooltip.tsx b/packages/manager/src/components/TextTooltip/TextTooltip.tsx index 8cc4b47b4c2..9a1f1f43979 100644 --- a/packages/manager/src/components/TextTooltip/TextTooltip.tsx +++ b/packages/manager/src/components/TextTooltip/TextTooltip.tsx @@ -1,7 +1,7 @@ +import { Tooltip } from '@linode/ui'; import { styled } from '@mui/material/styles'; import * as React from 'react'; -import { Tooltip } from 'src/components/Tooltip'; import { Typography } from 'src/components/Typography'; import type { SxProps, Theme } from '@mui/material'; diff --git a/packages/manager/src/components/TooltipIcon.tsx b/packages/manager/src/components/TooltipIcon.tsx index 60f960e3aea..d27add81c40 100644 --- a/packages/manager/src/components/TooltipIcon.tsx +++ b/packages/manager/src/components/TooltipIcon.tsx @@ -1,4 +1,5 @@ import styled from '@emotion/styled'; +import { Tooltip, tooltipClasses } from '@linode/ui'; import SuccessOutline from '@mui/icons-material/CheckCircleOutlined'; import ErrorOutline from '@mui/icons-material/ErrorOutline'; import HelpOutline from '@mui/icons-material/HelpOutline'; @@ -8,11 +9,10 @@ import { useTheme } from '@mui/material/styles'; import * as React from 'react'; import { IconButton } from 'src/components/IconButton'; -import { Tooltip, tooltipClasses } from 'src/components/Tooltip'; import { omittedProps } from 'src/utilities/omittedProps'; +import type { TooltipProps } from '@linode/ui'; import type { SxProps, Theme } from '@mui/material/styles'; -import type { TooltipProps } from 'src/components/Tooltip'; type TooltipIconStatus = | 'error' diff --git a/packages/manager/src/components/Uploaders/FileUpload.tsx b/packages/manager/src/components/Uploaders/FileUpload.tsx index 3908ccbbc1a..6152322f7af 100644 --- a/packages/manager/src/components/Uploaders/FileUpload.tsx +++ b/packages/manager/src/components/Uploaders/FileUpload.tsx @@ -1,10 +1,10 @@ +import { Tooltip } from '@linode/ui'; import * as React from 'react'; import CautionIcon from 'src/assets/icons/caution.svg'; import FileUploadComplete from 'src/assets/icons/fileUploadComplete.svg'; import { Button } from 'src/components/Button/Button'; import { LinearProgress } from 'src/components/LinearProgress'; -import { Tooltip } from 'src/components/Tooltip'; import { Typography } from 'src/components/Typography'; import { readableBytes } from 'src/utilities/unitConversions'; @@ -18,7 +18,8 @@ import { StyledUploadPending, useStyles, } from './FileUpload.styles'; -import { ObjectUploaderAction } from './reducer'; + +import type { ObjectUploaderAction } from './reducer'; export interface FileUploadProps { dispatch: React.Dispatch; diff --git a/packages/manager/src/dev-tools/ServiceWorkerTool.tsx b/packages/manager/src/dev-tools/ServiceWorkerTool.tsx index 2094e0f7a54..69a64bc27ef 100644 --- a/packages/manager/src/dev-tools/ServiceWorkerTool.tsx +++ b/packages/manager/src/dev-tools/ServiceWorkerTool.tsx @@ -1,6 +1,6 @@ +import { Tooltip } from '@linode/ui'; import * as React from 'react'; -import { Tooltip } from 'src/components/Tooltip'; import { mswDB } from 'src/mocks/indexedDB'; import { extraMockPresets } from 'src/mocks/presets'; import { dbSeeders } from 'src/mocks/presets/crud/seeds'; diff --git a/packages/manager/src/features/Account/Maintenance/MaintenanceTableRow.tsx b/packages/manager/src/features/Account/Maintenance/MaintenanceTableRow.tsx index 6b40b3ced15..274f222f509 100644 --- a/packages/manager/src/features/Account/Maintenance/MaintenanceTableRow.tsx +++ b/packages/manager/src/features/Account/Maintenance/MaintenanceTableRow.tsx @@ -1,19 +1,21 @@ -import { AccountMaintenance } from '@linode/api-v4/lib/account/types'; +import { Tooltip } from '@linode/ui'; import * as React from 'react'; import { Hidden } from 'src/components/Hidden'; import { HighlightedMarkdown } from 'src/components/HighlightedMarkdown/HighlightedMarkdown'; import { Link } from 'src/components/Link'; -import { Status, StatusIcon } from 'src/components/StatusIcon/StatusIcon'; +import { StatusIcon } from 'src/components/StatusIcon/StatusIcon'; import { TableCell } from 'src/components/TableCell'; import { TableRow } from 'src/components/TableRow'; -import { Tooltip } from 'src/components/Tooltip'; import { useProfile } from 'src/queries/profile/profile'; import { capitalize } from 'src/utilities/capitalize'; import { parseAPIDate } from 'src/utilities/date'; import { formatDate } from 'src/utilities/formatDate'; import { truncate } from 'src/utilities/truncate'; +import type { AccountMaintenance } from '@linode/api-v4/lib/account/types'; +import type { Status } from 'src/components/StatusIcon/StatusIcon'; + const statusTextMap: Record = { completed: 'Completed', pending: 'Scheduled', diff --git a/packages/manager/src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/GooglePayButton.tsx b/packages/manager/src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/GooglePayButton.tsx index 20f3e00a10c..8efdd8da286 100644 --- a/packages/manager/src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/GooglePayButton.tsx +++ b/packages/manager/src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/GooglePayButton.tsx @@ -1,11 +1,11 @@ +import { Tooltip } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; -import { QueryClient, useQueryClient } from '@tanstack/react-query'; +import { useQueryClient } from '@tanstack/react-query'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; import GooglePayIcon from 'src/assets/icons/payment/gPayButton.svg'; import { CircleProgress } from 'src/components/CircleProgress'; -import { Tooltip } from 'src/components/Tooltip'; import { getPaymentLimits } from 'src/features/Billing/billingUtils'; import { gPay, @@ -18,6 +18,7 @@ import { useClientToken } from 'src/queries/account/payment'; import type { SetSuccess } from './types'; import type { APIWarning } from '@linode/api-v4/lib/types'; import type { Theme } from '@mui/material/styles'; +import type { QueryClient } from '@tanstack/react-query'; import type { PaymentMessage } from 'src/features/Billing/BillingPanels/PaymentInfoPanel/AddPaymentMethodDrawer/AddPaymentMethodDrawer'; const useStyles = makeStyles()((theme: Theme) => ({ diff --git a/packages/manager/src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/PayPalButton.tsx b/packages/manager/src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/PayPalButton.tsx index a5427d239f6..af9ecd6aaeb 100644 --- a/packages/manager/src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/PayPalButton.tsx +++ b/packages/manager/src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/PayPalButton.tsx @@ -1,12 +1,9 @@ import { makePayment } from '@linode/api-v4/lib/account/payments'; -import { APIError } from '@linode/api-v4/lib/types'; +import { Tooltip } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import { BraintreePayPalButtons, - CreateOrderBraintreeActions, FUNDING, - OnApproveBraintreeActions, - OnApproveBraintreeData, usePayPalScriptReducer, } from '@paypal/react-paypal-js'; import { useQueryClient } from '@tanstack/react-query'; @@ -14,15 +11,20 @@ import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; import { CircleProgress } from 'src/components/CircleProgress'; -import { Tooltip } from 'src/components/Tooltip'; import { reportException } from 'src/exceptionReporting'; import { getPaymentLimits } from 'src/features/Billing/billingUtils'; import { useAccount } from 'src/queries/account/account'; import { useClientToken } from 'src/queries/account/payment'; +import { accountQueries } from 'src/queries/account/queries'; import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; -import { SetSuccess } from './types'; -import { accountQueries } from 'src/queries/account/queries'; +import type { SetSuccess } from './types'; +import type { APIError } from '@linode/api-v4/lib/types'; +import type { + CreateOrderBraintreeActions, + OnApproveBraintreeActions, + OnApproveBraintreeData, +} from '@paypal/react-paypal-js'; const useStyles = makeStyles()(() => ({ loading: { diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseTooltip.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseTooltip.tsx index 4a056c27509..b0aa7aedf37 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseTooltip.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseTooltip.tsx @@ -1,7 +1,6 @@ +import { Tooltip } from '@linode/ui'; import React from 'react'; -import { Tooltip } from 'src/components/Tooltip'; - import type { TooltipProps } from '@mui/material'; export const CloudPulseTooltip = React.memo((props: TooltipProps) => { diff --git a/packages/manager/src/features/EntityTransfers/EntityTransfersLanding/CreateTransferSuccessDialog.tsx b/packages/manager/src/features/EntityTransfers/EntityTransfersLanding/CreateTransferSuccessDialog.tsx index 76ff9ae2422..1deca5e434d 100644 --- a/packages/manager/src/features/EntityTransfers/EntityTransfersLanding/CreateTransferSuccessDialog.tsx +++ b/packages/manager/src/features/EntityTransfers/EntityTransfersLanding/CreateTransferSuccessDialog.tsx @@ -1,4 +1,4 @@ -import { EntityTransfer } from '@linode/api-v4/lib/entity-transfers/types'; +import { Tooltip } from '@linode/ui'; import copy from 'copy-to-clipboard'; import { DateTime } from 'luxon'; import { update } from 'ramda'; @@ -6,7 +6,6 @@ import * as React from 'react'; import { debounce } from 'throttle-debounce'; import { Button } from 'src/components/Button/Button'; -import { Tooltip } from 'src/components/Tooltip'; import { Typography } from 'src/components/Typography'; import { sendEntityTransferCopyDraftEmailEvent, @@ -23,6 +22,8 @@ import { StyledTypography, } from './CreateTransferSuccessDialog.styles'; +import type { EntityTransfer } from '@linode/api-v4/lib/entity-transfers/types'; + const debouncedSendEntityTransferCopyTokenEvent = debounce( 10 * 1000, true, diff --git a/packages/manager/src/features/Images/ImagesLanding/ImageRegions/ImageRegionRow.tsx b/packages/manager/src/features/Images/ImagesLanding/ImageRegions/ImageRegionRow.tsx index 8b5dc9495d4..d2b314df7c4 100644 --- a/packages/manager/src/features/Images/ImagesLanding/ImageRegions/ImageRegionRow.tsx +++ b/packages/manager/src/features/Images/ImagesLanding/ImageRegions/ImageRegionRow.tsx @@ -1,3 +1,4 @@ +import { Tooltip } from '@linode/ui'; import Close from '@mui/icons-material/Close'; import React from 'react'; @@ -6,7 +7,6 @@ import { Flag } from 'src/components/Flag'; import { IconButton } from 'src/components/IconButton'; import { Stack } from 'src/components/Stack'; import { StatusIcon } from 'src/components/StatusIcon/StatusIcon'; -import { Tooltip } from 'src/components/Tooltip'; import { Typography } from 'src/components/Typography'; import { useRegionsQuery } from 'src/queries/regions/regions'; diff --git a/packages/manager/src/features/Images/ImagesLanding/ImageRow.tsx b/packages/manager/src/features/Images/ImagesLanding/ImageRow.tsx index c986d0cdbc8..4915ce42a5d 100644 --- a/packages/manager/src/features/Images/ImagesLanding/ImageRow.tsx +++ b/packages/manager/src/features/Images/ImagesLanding/ImageRow.tsx @@ -1,3 +1,4 @@ +import { Tooltip } from '@linode/ui'; import React from 'react'; import CloudInitIcon from 'src/assets/icons/cloud-init.svg'; @@ -6,7 +7,6 @@ import { LinkButton } from 'src/components/LinkButton'; import { Stack } from 'src/components/Stack'; import { TableCell } from 'src/components/TableCell'; import { TableRow } from 'src/components/TableRow'; -import { Tooltip } from 'src/components/Tooltip'; import { Typography } from 'src/components/Typography'; import { useFlags } from 'src/hooks/useFlags'; import { useProfile } from 'src/queries/profile/profile'; diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePool.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePool.tsx index 76a4fd9a37d..7969e102be2 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePool.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePool.tsx @@ -1,3 +1,4 @@ +import { Tooltip } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; @@ -5,7 +6,6 @@ import { makeStyles } from 'tss-react/mui'; import { Box } from 'src/components/Box'; import { StyledActionButton } from 'src/components/Button/StyledActionButton'; import { Paper } from 'src/components/Paper'; -import { Tooltip } from 'src/components/Tooltip'; import { Typography } from 'src/components/Typography'; import { NodeTable } from './NodeTable'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/AddIPDrawer.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/AddIPDrawer.tsx index 57642f09997..be95b1f2d86 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/AddIPDrawer.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/AddIPDrawer.tsx @@ -1,3 +1,4 @@ +import { Tooltip } from '@linode/ui'; import { styled } from '@mui/material/styles'; import * as React from 'react'; @@ -11,7 +12,6 @@ import { Notice } from 'src/components/Notice/Notice'; import { Radio } from 'src/components/Radio/Radio'; import { RadioGroup } from 'src/components/RadioGroup'; import { Stack } from 'src/components/Stack'; -import { Tooltip } from 'src/components/Tooltip'; import { Typography } from 'src/components/Typography'; import { useAllocateIPMutation, diff --git a/packages/manager/src/features/Linodes/LinodesLanding/DisplayGroupedLinodes.tsx b/packages/manager/src/features/Linodes/LinodesLanding/DisplayGroupedLinodes.tsx index 7fdb1f9750f..5586742b1e3 100644 --- a/packages/manager/src/features/Linodes/LinodesLanding/DisplayGroupedLinodes.tsx +++ b/packages/manager/src/features/Linodes/LinodesLanding/DisplayGroupedLinodes.tsx @@ -1,3 +1,4 @@ +import { Tooltip } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import { compose } from 'ramda'; import * as React from 'react'; @@ -17,7 +18,6 @@ import { TableBody } from 'src/components/TableBody'; import { TableCell } from 'src/components/TableCell'; import { TableRow } from 'src/components/TableRow'; import { TableRowEmpty } from 'src/components/TableRowEmpty/TableRowEmpty'; -import { Tooltip } from 'src/components/Tooltip'; import { Typography } from 'src/components/Typography'; import { useInfinitePageSize } from 'src/hooks/useInfinitePageSize'; import { groupByTags, sortGroups } from 'src/utilities/groupByTags'; diff --git a/packages/manager/src/features/Linodes/LinodesLanding/DisplayLinodes.tsx b/packages/manager/src/features/Linodes/LinodesLanding/DisplayLinodes.tsx index 6513f6e6a40..57b42f771a0 100644 --- a/packages/manager/src/features/Linodes/LinodesLanding/DisplayLinodes.tsx +++ b/packages/manager/src/features/Linodes/LinodesLanding/DisplayLinodes.tsx @@ -1,3 +1,4 @@ +import { Tooltip } from '@linode/ui'; import { Box } from '@mui/material'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; @@ -11,7 +12,6 @@ import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFoot import { Paper } from 'src/components/Paper'; import { useIsGeckoEnabled } from 'src/components/RegionSelect/RegionSelect.utils'; import { TableBody } from 'src/components/TableBody'; -import { Tooltip } from 'src/components/Tooltip'; import { useInfinitePageSize } from 'src/hooks/useInfinitePageSize'; import { getQueryParamsFromQueryString } from 'src/utilities/queryParams'; diff --git a/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.tsx b/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.tsx index 316ffdfe255..8f696986ce9 100644 --- a/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.tsx +++ b/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.tsx @@ -1,3 +1,4 @@ +import { Tooltip } from '@linode/ui'; import * as React from 'react'; import Flag from 'src/assets/icons/flag.svg'; @@ -7,7 +8,6 @@ import { Link } from 'src/components/Link'; import { StatusIcon } from 'src/components/StatusIcon/StatusIcon'; import { TableCell } from 'src/components/TableCell'; import { TableRow } from 'src/components/TableRow'; -import { Tooltip } from 'src/components/Tooltip'; import { TooltipIcon } from 'src/components/TooltipIcon'; import { Typography } from 'src/components/Typography'; import { LinodeActionMenu } from 'src/features/Linodes/LinodesLanding/LinodeActionMenu/LinodeActionMenu'; diff --git a/packages/manager/src/features/Linodes/LinodesLanding/SortableTableHead.tsx b/packages/manager/src/features/Linodes/LinodesLanding/SortableTableHead.tsx index d9ec9cffbc7..0b2c703de3f 100644 --- a/packages/manager/src/features/Linodes/LinodesLanding/SortableTableHead.tsx +++ b/packages/manager/src/features/Linodes/LinodesLanding/SortableTableHead.tsx @@ -1,3 +1,4 @@ +import { Tooltip } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import * as React from 'react'; @@ -8,7 +9,6 @@ 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 { Tooltip } from 'src/components/Tooltip'; import { StyledToggleButton } from './DisplayLinodes.styles'; diff --git a/packages/manager/src/features/Managed/Monitors/IssueDay.tsx b/packages/manager/src/features/Managed/Monitors/IssueDay.tsx index 0fd09101763..b0cb33e6f06 100644 --- a/packages/manager/src/features/Managed/Monitors/IssueDay.tsx +++ b/packages/manager/src/features/Managed/Monitors/IssueDay.tsx @@ -1,4 +1,4 @@ -import { ManagedIssue } from '@linode/api-v4'; +import { Tooltip } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; @@ -6,7 +6,6 @@ import Bad from 'src/assets/icons/monitor-failed.svg'; import Good from 'src/assets/icons/monitor-ok.svg'; import TicketIcon from 'src/assets/icons/ticket.svg'; import { DateTimeDisplay } from 'src/components/DateTimeDisplay'; -import { Tooltip } from 'src/components/Tooltip'; import { StyledDateTimeDisplay, @@ -14,6 +13,8 @@ import { StyledLink, } from './IssueDay.styles'; +import type { ManagedIssue } from '@linode/api-v4'; + export interface IssueDayProps { day: string; issues: ManagedIssue[]; diff --git a/packages/manager/src/features/Managed/Monitors/MonitorRow.tsx b/packages/manager/src/features/Managed/Monitors/MonitorRow.tsx index a532706d945..7b3730f53a8 100644 --- a/packages/manager/src/features/Managed/Monitors/MonitorRow.tsx +++ b/packages/manager/src/features/Managed/Monitors/MonitorRow.tsx @@ -1,12 +1,10 @@ -import { ManagedServiceMonitor } from '@linode/api-v4/lib/managed'; +import { Tooltip } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import TicketIcon from 'src/assets/icons/ticket.svg'; import { TableCell } from 'src/components/TableCell'; -import { Tooltip } from 'src/components/Tooltip'; import { Typography } from 'src/components/Typography'; -import { ExtendedIssue } from 'src/queries/managed/types'; import ActionMenu from './MonitorActionMenu'; import { statusIconMap, statusTextMap } from './monitorMaps'; @@ -18,6 +16,9 @@ import { StyledTypography, } from './MonitorRow.styles'; +import type { ManagedServiceMonitor } from '@linode/api-v4/lib/managed'; +import type { ExtendedIssue } from 'src/queries/managed/types'; + interface MonitorRowProps { issues: ExtendedIssue[]; monitor: ManagedServiceMonitor; diff --git a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessCell.tsx b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessCell.tsx index 265e90ec4ea..00a7b857da7 100644 --- a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessCell.tsx +++ b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessCell.tsx @@ -1,10 +1,10 @@ /* eslint-disable jsx-a11y/no-noninteractive-tabindex */ +import { Tooltip } from '@linode/ui'; import { styled } from '@mui/material/styles'; import * as React from 'react'; import Check from 'src/assets/icons/monitor-ok.svg'; import { Radio } from 'src/components/Radio/Radio'; -import { Tooltip } from 'src/components/Tooltip'; interface RadioButton extends HTMLInputElement { name: string; diff --git a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/CopyAllHostnames.tsx b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/CopyAllHostnames.tsx index 10353fb94c3..514344fdef1 100644 --- a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/CopyAllHostnames.tsx +++ b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/CopyAllHostnames.tsx @@ -1,3 +1,4 @@ +import { Tooltip } from '@linode/ui'; import { styled } from '@mui/material/styles'; import copy from 'copy-to-clipboard'; import * as React from 'react'; @@ -5,7 +6,6 @@ import * as React from 'react'; import { Box } from 'src/components/Box'; import { StyledLinkButton } from 'src/components/Button/StyledLinkButton'; import { InputLabel } from 'src/components/InputLabel'; -import { Tooltip } from 'src/components/Tooltip'; export interface Props { hideShowAll?: boolean; diff --git a/packages/manager/src/features/PlacementGroups/PlacementGroupTypeSelect.tsx b/packages/manager/src/features/PlacementGroups/PlacementGroupTypeSelect.tsx index b6bc9535536..e9091436fd2 100644 --- a/packages/manager/src/features/PlacementGroups/PlacementGroupTypeSelect.tsx +++ b/packages/manager/src/features/PlacementGroups/PlacementGroupTypeSelect.tsx @@ -1,9 +1,9 @@ +import { Tooltip } from '@linode/ui'; import * as React from 'react'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { Link } from 'src/components/Link'; import { ListItem } from 'src/components/ListItem'; -import { Tooltip } from 'src/components/Tooltip'; import { Typography } from 'src/components/Typography'; import { PLACEMENT_GROUPS_DOCS_LINK } from './constants'; diff --git a/packages/manager/src/features/TopMenu/TopMenuTooltip.tsx b/packages/manager/src/features/TopMenu/TopMenuTooltip.tsx index 35250f5cbfa..43bff6c2967 100644 --- a/packages/manager/src/features/TopMenu/TopMenuTooltip.tsx +++ b/packages/manager/src/features/TopMenu/TopMenuTooltip.tsx @@ -1,7 +1,6 @@ +import { Tooltip } from '@linode/ui'; import * as React from 'react'; -import { Tooltip } from 'src/components/Tooltip'; - import type { Theme } from '@mui/material'; interface Props { diff --git a/packages/manager/src/features/TopMenu/UserMenu/UserMenu.tsx b/packages/manager/src/features/TopMenu/UserMenu/UserMenu.tsx index 1867fdaf3f1..8d2aeffd7e6 100644 --- a/packages/manager/src/features/TopMenu/UserMenu/UserMenu.tsx +++ b/packages/manager/src/features/TopMenu/UserMenu/UserMenu.tsx @@ -1,3 +1,4 @@ +import { Tooltip } from '@linode/ui'; import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown'; import KeyboardArrowUp from '@mui/icons-material/KeyboardArrowUp'; import { styled, useMediaQuery } from '@mui/material'; @@ -14,7 +15,6 @@ import { Divider } from 'src/components/Divider'; import { Hidden } from 'src/components/Hidden'; import { Link } from 'src/components/Link'; import { Stack } from 'src/components/Stack'; -import { Tooltip } from 'src/components/Tooltip'; import { Typography } from 'src/components/Typography'; import { switchAccountSessionContext } from 'src/context/switchAccountSessionContext'; import { SwitchAccountButton } from 'src/features/Account/SwitchAccountButton'; diff --git a/packages/manager/src/features/components/PlansPanel/DisabledPlanSelectionTooltip.tsx b/packages/manager/src/features/components/PlansPanel/DisabledPlanSelectionTooltip.tsx index 2cb7d0cb238..9227cec6247 100644 --- a/packages/manager/src/features/components/PlansPanel/DisabledPlanSelectionTooltip.tsx +++ b/packages/manager/src/features/components/PlansPanel/DisabledPlanSelectionTooltip.tsx @@ -1,8 +1,8 @@ +import { Tooltip } from '@linode/ui'; import HelpOutline from '@mui/icons-material/HelpOutline'; import * as React from 'react'; import { IconButton } from 'src/components/IconButton'; -import { Tooltip } from 'src/components/Tooltip'; interface DisabledPlanSelectionTooltipProps { tooltipCopy: string; diff --git a/packages/manager/src/utilities/noneSingleOrMultipleWithChip.tsx b/packages/manager/src/utilities/noneSingleOrMultipleWithChip.tsx index 783d31a01dd..895f51877e2 100644 --- a/packages/manager/src/utilities/noneSingleOrMultipleWithChip.tsx +++ b/packages/manager/src/utilities/noneSingleOrMultipleWithChip.tsx @@ -1,8 +1,8 @@ +import { Tooltip } from '@linode/ui'; import * as React from 'react'; import { Chip } from 'src/components/Chip'; import { StyledItemWithPlusChip } from 'src/components/RemovableSelectionsList/RemovableSelectionsList.style'; -import { Tooltip } from 'src/components/Tooltip'; export const remainingDataLengthChip = 'remaining-data-length-chip'; diff --git a/packages/manager/src/components/Tooltip.stories.tsx b/packages/ui/src/components/Tooltip.stories.tsx similarity index 91% rename from packages/manager/src/components/Tooltip.stories.tsx rename to packages/ui/src/components/Tooltip.stories.tsx index 57d566beb8c..77cdde63f93 100644 --- a/packages/manager/src/components/Tooltip.stories.tsx +++ b/packages/ui/src/components/Tooltip.stories.tsx @@ -2,7 +2,7 @@ import { Meta, StoryObj } from '@storybook/react'; import React from 'react'; import { Tooltip } from './Tooltip'; -import { Typography } from './Typography'; +import { Typography } from '@mui/material'; const meta: Meta = { component: Tooltip, diff --git a/packages/manager/src/components/Tooltip.tsx b/packages/ui/src/components/Tooltip.tsx similarity index 100% rename from packages/manager/src/components/Tooltip.tsx rename to packages/ui/src/components/Tooltip.tsx diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index ea92b0846b8..5998192f93f 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -1,2 +1,3 @@ export * from './Chip'; export * from './BetaChip'; +export * from './Tooltip'; From 82f3542ba255e82c03a8443856d6e3703a45b7fc Mon Sep 17 00:00:00 2001 From: Alban Bailly <130582365+abailly-akamai@users.noreply.github.com> Date: Fri, 25 Oct 2024 09:59:36 -0400 Subject: [PATCH 06/66] refactor: [M3-8762] - `only-export-components` for Tanstack routes (#11142) * only-export-components fro routes * cleanup * moar cleanup * Added changeset: `only-export-components` for Tanstack routes --- .../pr-11142-tech-stories-1729715589096.md | 5 +++ packages/manager/.eslintrc.cjs | 2 + packages/manager/package.json | 1 + .../src/routes/account/AccountRoute.tsx | 14 ++++++ .../routes/{account.tsx => account/index.ts} | 20 ++------- packages/manager/src/routes/betas.tsx | 43 ------------------- .../manager/src/routes/betas/BetasRoute.tsx | 16 +++++++ packages/manager/src/routes/betas/index.ts | 29 +++++++++++++ packages/manager/src/routes/cloudPulse.tsx | 35 --------------- .../src/routes/cloudPulse/CloudPulseRoute.tsx | 14 ++++++ .../manager/src/routes/cloudPulse/index.ts | 23 ++++++++++ .../src/routes/databases/DatabasesRoute.tsx | 16 +++++++ .../{databases.tsx => databases/index.ts} | 22 ++-------- .../src/routes/domains/DomainsRoute.tsx | 14 ++++++ .../routes/{domains.tsx => domains/index.ts} | 20 ++------- .../manager/src/routes/events/EventsRoute.tsx | 12 ++++++ .../routes/{events.tsx => events/index.ts} | 18 ++------ .../src/routes/firewalls/FirewallsRoute.tsx | 14 ++++++ .../{firewalls.tsx => firewalls/index.ts} | 20 ++------- .../manager/src/routes/images/ImagesRoute.tsx | 14 ++++++ .../routes/{images.tsx => images/index.ts} | 20 ++------- packages/manager/src/routes/index.tsx | 2 +- .../src/routes/kubernetes/KubernetesRoute.tsx | 14 ++++++ .../{kubernetes.tsx => kubernetes/index.ts} | 20 ++------- .../src/routes/linodes/LinodesRoute.tsx | 12 ++++++ .../routes/{linodes.tsx => linodes/index.ts} | 18 ++------ .../src/routes/longview/LongviewRoute.tsx | 14 ++++++ .../{longview.tsx => longview/index.ts} | 20 ++------- .../src/routes/managed/ManagedRoute.tsx | 14 ++++++ .../routes/{managed.tsx => managed/index.ts} | 20 ++------- .../nodeBalancers/NodeBalancersRoute.tsx | 14 ++++++ .../index.ts} | 24 ++--------- .../objectStorage/ObjectStorageRoute.tsx | 14 ++++++ .../index.ts} | 20 ++------- .../placementGroups/PlacementGroupsRoute.tsx | 16 +++++++ .../index.ts} | 22 ++-------- .../src/routes/profile/ProfileRoute.tsx | 12 ++++++ .../routes/{profile.tsx => profile/index.ts} | 17 ++------ packages/manager/src/routes/search.tsx | 33 -------------- .../manager/src/routes/search/SearchRoute.tsx | 14 ++++++ packages/manager/src/routes/search/index.ts | 21 +++++++++ .../routes/stackscripts/StackscriptsRoute.tsx | 14 ++++++ .../index.tsx} | 18 ++------ .../src/routes/support/SupportRoute.tsx | 14 ++++++ .../routes/{support.tsx => support/index.ts} | 20 ++------- .../src/routes/volumes/VolumesRoute.tsx | 14 ++++++ .../routes/{volumes.tsx => volumes/index.ts} | 20 ++------- packages/manager/src/routes/vpcs/VPCRoute.tsx | 16 +++++++ .../src/routes/{vpcs.tsx => vpcs/index.ts} | 22 ++-------- yarn.lock | 36 ++++++++++++++-- 50 files changed, 483 insertions(+), 404 deletions(-) create mode 100644 packages/manager/.changeset/pr-11142-tech-stories-1729715589096.md create mode 100644 packages/manager/src/routes/account/AccountRoute.tsx rename packages/manager/src/routes/{account.tsx => account/index.ts} (89%) delete mode 100644 packages/manager/src/routes/betas.tsx create mode 100644 packages/manager/src/routes/betas/BetasRoute.tsx create mode 100644 packages/manager/src/routes/betas/index.ts delete mode 100644 packages/manager/src/routes/cloudPulse.tsx create mode 100644 packages/manager/src/routes/cloudPulse/CloudPulseRoute.tsx create mode 100644 packages/manager/src/routes/cloudPulse/index.ts create mode 100644 packages/manager/src/routes/databases/DatabasesRoute.tsx rename packages/manager/src/routes/{databases.tsx => databases/index.ts} (76%) create mode 100644 packages/manager/src/routes/domains/DomainsRoute.tsx rename packages/manager/src/routes/{domains.tsx => domains/index.ts} (70%) create mode 100644 packages/manager/src/routes/events/EventsRoute.tsx rename packages/manager/src/routes/{events.tsx => events/index.ts} (50%) create mode 100644 packages/manager/src/routes/firewalls/FirewallsRoute.tsx rename packages/manager/src/routes/{firewalls.tsx => firewalls/index.ts} (76%) create mode 100644 packages/manager/src/routes/images/ImagesRoute.tsx rename packages/manager/src/routes/{images.tsx => images/index.ts} (53%) create mode 100644 packages/manager/src/routes/kubernetes/KubernetesRoute.tsx rename packages/manager/src/routes/{kubernetes.tsx => kubernetes/index.ts} (77%) create mode 100644 packages/manager/src/routes/linodes/LinodesRoute.tsx rename packages/manager/src/routes/{linodes.tsx => linodes/index.ts} (88%) create mode 100644 packages/manager/src/routes/longview/LongviewRoute.tsx rename packages/manager/src/routes/{longview.tsx => longview/index.ts} (83%) create mode 100644 packages/manager/src/routes/managed/ManagedRoute.tsx rename packages/manager/src/routes/{managed.tsx => managed/index.ts} (74%) create mode 100644 packages/manager/src/routes/nodeBalancers/NodeBalancersRoute.tsx rename packages/manager/src/routes/{nodeBalancers.tsx => nodeBalancers/index.ts} (78%) create mode 100644 packages/manager/src/routes/objectStorage/ObjectStorageRoute.tsx rename packages/manager/src/routes/{object-storage.tsx => objectStorage/index.ts} (81%) create mode 100644 packages/manager/src/routes/placementGroups/PlacementGroupsRoute.tsx rename packages/manager/src/routes/{placementGroups.tsx => placementGroups/index.ts} (81%) create mode 100644 packages/manager/src/routes/profile/ProfileRoute.tsx rename packages/manager/src/routes/{profile.tsx => profile/index.ts} (86%) delete mode 100644 packages/manager/src/routes/search.tsx create mode 100644 packages/manager/src/routes/search/SearchRoute.tsx create mode 100644 packages/manager/src/routes/search/index.ts create mode 100644 packages/manager/src/routes/stackscripts/StackscriptsRoute.tsx rename packages/manager/src/routes/{stackscripts.tsx => stackscripts/index.tsx} (80%) create mode 100644 packages/manager/src/routes/support/SupportRoute.tsx rename packages/manager/src/routes/{support.tsx => support/index.ts} (74%) create mode 100644 packages/manager/src/routes/volumes/VolumesRoute.tsx rename packages/manager/src/routes/{volumes.tsx => volumes/index.ts} (53%) create mode 100644 packages/manager/src/routes/vpcs/VPCRoute.tsx rename packages/manager/src/routes/{vpcs.tsx => vpcs/index.ts} (55%) diff --git a/packages/manager/.changeset/pr-11142-tech-stories-1729715589096.md b/packages/manager/.changeset/pr-11142-tech-stories-1729715589096.md new file mode 100644 index 00000000000..c0517590945 --- /dev/null +++ b/packages/manager/.changeset/pr-11142-tech-stories-1729715589096.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tech Stories +--- + +`only-export-components` for Tanstack routes ([#11142](https://github.com/linode/manager/pull/11142)) diff --git a/packages/manager/.eslintrc.cjs b/packages/manager/.eslintrc.cjs index d8262479e86..bc0ad624e4a 100644 --- a/packages/manager/.eslintrc.cjs +++ b/packages/manager/.eslintrc.cjs @@ -155,6 +155,7 @@ module.exports = { 'xss', 'perfectionist', '@linode/eslint-plugin-cloud-manager', + 'react-refresh', ], rules: { '@linode/cloud-manager/deprecate-formik': 'warn', @@ -275,6 +276,7 @@ module.exports = { 'react/prop-types': 'off', 'react-hooks/exhaustive-deps': 'warn', 'react-hooks/rules-of-hooks': 'error', + 'react-refresh/only-export-components': 'warn', 'scanjs-rules/assign_to_hostname': 'warn', 'scanjs-rules/assign_to_href': 'warn', 'scanjs-rules/assign_to_location': 'warn', diff --git a/packages/manager/package.json b/packages/manager/package.json index 21b07e4e746..260a4b3932f 100644 --- a/packages/manager/package.json +++ b/packages/manager/package.json @@ -190,6 +190,7 @@ "eslint-plugin-ramda": "^2.5.1", "eslint-plugin-react": "^7.19.0", "eslint-plugin-react-hooks": "^3.0.0", + "eslint-plugin-react-refresh": "^0.4.13", "eslint-plugin-scanjs-rules": "^0.2.1", "eslint-plugin-sonarjs": "^0.5.0", "eslint-plugin-testing-library": "^3.1.2", diff --git a/packages/manager/src/routes/account/AccountRoute.tsx b/packages/manager/src/routes/account/AccountRoute.tsx new file mode 100644 index 00000000000..f371e3df530 --- /dev/null +++ b/packages/manager/src/routes/account/AccountRoute.tsx @@ -0,0 +1,14 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const AccountRoute = () => { + return ( + }> + + + + ); +}; diff --git a/packages/manager/src/routes/account.tsx b/packages/manager/src/routes/account/index.ts similarity index 89% rename from packages/manager/src/routes/account.tsx rename to packages/manager/src/routes/account/index.ts index d3a17c5e76d..591dce6f2af 100644 --- a/packages/manager/src/routes/account.tsx +++ b/packages/manager/src/routes/account/index.ts @@ -1,22 +1,10 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; +import { createRoute } from '@tanstack/react-router'; -import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const AccountRoutes = () => { - return ( - }> - - - - ); -}; +import { rootRoute } from '../root'; +import { AccountRoute } from './AccountRoute'; const accountRoute = createRoute({ - component: AccountRoutes, + component: AccountRoute, getParentRoute: () => rootRoute, path: 'account', }); diff --git a/packages/manager/src/routes/betas.tsx b/packages/manager/src/routes/betas.tsx deleted file mode 100644 index f3046424564..00000000000 --- a/packages/manager/src/routes/betas.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; - -import { NotFound } from 'src/components/NotFound'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; -import { useFlags } from 'src/hooks/useFlags'; - -import { rootRoute } from './root'; - -export const BetaRoutes = () => { - const flags = useFlags(); - const { selfServeBetas } = flags; - return ( - }> - {selfServeBetas ? : } - - ); -}; - -const betaRoute = createRoute({ - component: BetaRoutes, - getParentRoute: () => rootRoute, - path: 'betas', -}); - -const betaLandingRoute = createRoute({ - getParentRoute: () => betaRoute, - path: '/', -}).lazy(() => - import('src/features/Betas/BetasLanding').then((m) => m.betasLandingLazyRoute) -); - -const betaSignupRoute = createRoute({ - getParentRoute: () => betaRoute, - path: 'signup/$betaId', -}).lazy(() => - import('src/features/Betas/BetaSignup').then((m) => m.betaSignupLazyRoute) -); - -export const betaRouteTree = betaRoute.addChildren([ - betaLandingRoute, - betaSignupRoute, -]); diff --git a/packages/manager/src/routes/betas/BetasRoute.tsx b/packages/manager/src/routes/betas/BetasRoute.tsx new file mode 100644 index 00000000000..866d2ef2a96 --- /dev/null +++ b/packages/manager/src/routes/betas/BetasRoute.tsx @@ -0,0 +1,16 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { NotFound } from 'src/components/NotFound'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; +import { useFlags } from 'src/hooks/useFlags'; + +export const BetasRoute = () => { + const flags = useFlags(); + const { selfServeBetas } = flags; + return ( + }> + {selfServeBetas ? : } + + ); +}; diff --git a/packages/manager/src/routes/betas/index.ts b/packages/manager/src/routes/betas/index.ts new file mode 100644 index 00000000000..48e43ee3d05 --- /dev/null +++ b/packages/manager/src/routes/betas/index.ts @@ -0,0 +1,29 @@ +import { createRoute } from '@tanstack/react-router'; + +import { rootRoute } from '../root'; +import { BetasRoute } from './BetasRoute'; + +const betasRoute = createRoute({ + component: BetasRoute, + getParentRoute: () => rootRoute, + path: 'betas', +}); + +const betaLandingRoute = createRoute({ + getParentRoute: () => betasRoute, + path: '/', +}).lazy(() => + import('src/features/Betas/BetasLanding').then((m) => m.betasLandingLazyRoute) +); + +const betaSignupRoute = createRoute({ + getParentRoute: () => betasRoute, + path: 'signup/$betaId', +}).lazy(() => + import('src/features/Betas/BetaSignup').then((m) => m.betaSignupLazyRoute) +); + +export const betaRouteTree = betasRoute.addChildren([ + betaLandingRoute, + betaSignupRoute, +]); diff --git a/packages/manager/src/routes/cloudPulse.tsx b/packages/manager/src/routes/cloudPulse.tsx deleted file mode 100644 index d044f197413..00000000000 --- a/packages/manager/src/routes/cloudPulse.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; - -import { DocumentTitleSegment } from 'src/components/DocumentTitle'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const CloudPulseRoutes = () => { - return ( - }> - - - - ); -}; - -const cloudPulseRoute = createRoute({ - component: CloudPulseRoutes, - getParentRoute: () => rootRoute, - path: 'monitor/cloudpulse', -}); - -const cloudPulseLandingRoute = createRoute({ - getParentRoute: () => cloudPulseRoute, - path: '/', -}).lazy(() => - import('src/features/CloudPulse/CloudPulseLanding').then( - (m) => m.cloudPulseLandingLazyRoute - ) -); - -export const cloudPulseRouteTree = cloudPulseRoute.addChildren([ - cloudPulseLandingRoute, -]); diff --git a/packages/manager/src/routes/cloudPulse/CloudPulseRoute.tsx b/packages/manager/src/routes/cloudPulse/CloudPulseRoute.tsx new file mode 100644 index 00000000000..380f119f91a --- /dev/null +++ b/packages/manager/src/routes/cloudPulse/CloudPulseRoute.tsx @@ -0,0 +1,14 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { DocumentTitleSegment } from 'src/components/DocumentTitle'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const CloudPulseRoute = () => { + return ( + }> + + + + ); +}; diff --git a/packages/manager/src/routes/cloudPulse/index.ts b/packages/manager/src/routes/cloudPulse/index.ts new file mode 100644 index 00000000000..6f4f3595a7a --- /dev/null +++ b/packages/manager/src/routes/cloudPulse/index.ts @@ -0,0 +1,23 @@ +import { createRoute } from '@tanstack/react-router'; + +import { rootRoute } from '../root'; +import { CloudPulseRoute } from './CloudPulseRoute'; + +const cloudPulseRoute = createRoute({ + component: CloudPulseRoute, + getParentRoute: () => rootRoute, + path: 'monitor/cloudpulse', +}); + +const cloudPulseLandingRoute = createRoute({ + getParentRoute: () => cloudPulseRoute, + path: '/', +}).lazy(() => + import('src/features/CloudPulse/CloudPulseLanding').then( + (m) => m.cloudPulseLandingLazyRoute + ) +); + +export const cloudPulseRouteTree = cloudPulseRoute.addChildren([ + cloudPulseLandingRoute, +]); diff --git a/packages/manager/src/routes/databases/DatabasesRoute.tsx b/packages/manager/src/routes/databases/DatabasesRoute.tsx new file mode 100644 index 00000000000..c6e1f40727e --- /dev/null +++ b/packages/manager/src/routes/databases/DatabasesRoute.tsx @@ -0,0 +1,16 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { DocumentTitleSegment } from 'src/components/DocumentTitle'; +import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const DatabasesRoute = () => { + return ( + }> + + + + + ); +}; diff --git a/packages/manager/src/routes/databases.tsx b/packages/manager/src/routes/databases/index.ts similarity index 76% rename from packages/manager/src/routes/databases.tsx rename to packages/manager/src/routes/databases/index.ts index c6f339ec847..aafe5209b63 100644 --- a/packages/manager/src/routes/databases.tsx +++ b/packages/manager/src/routes/databases/index.ts @@ -1,24 +1,10 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; +import { createRoute } from '@tanstack/react-router'; -import { DocumentTitleSegment } from 'src/components/DocumentTitle'; -import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const DatabasesRoutes = () => { - return ( - }> - - - - - ); -}; +import { rootRoute } from '../root'; +import { DatabasesRoute } from './DatabasesRoute'; const databasesRoute = createRoute({ - component: DatabasesRoutes, + component: DatabasesRoute, getParentRoute: () => rootRoute, path: 'databases', }); diff --git a/packages/manager/src/routes/domains/DomainsRoute.tsx b/packages/manager/src/routes/domains/DomainsRoute.tsx new file mode 100644 index 00000000000..3ac16cd63ac --- /dev/null +++ b/packages/manager/src/routes/domains/DomainsRoute.tsx @@ -0,0 +1,14 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const DomainsRoute = () => { + return ( + }> + + + + ); +}; diff --git a/packages/manager/src/routes/domains.tsx b/packages/manager/src/routes/domains/index.ts similarity index 70% rename from packages/manager/src/routes/domains.tsx rename to packages/manager/src/routes/domains/index.ts index 2387c554d23..b139fe4d8e7 100644 --- a/packages/manager/src/routes/domains.tsx +++ b/packages/manager/src/routes/domains/index.ts @@ -1,22 +1,10 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; +import { createRoute } from '@tanstack/react-router'; -import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const DomainsRoutes = () => { - return ( - }> - - - - ); -}; +import { rootRoute } from '../root'; +import { DomainsRoute } from './DomainsRoute'; const domainsRoute = createRoute({ - component: DomainsRoutes, + component: DomainsRoute, getParentRoute: () => rootRoute, path: 'domains', }); diff --git a/packages/manager/src/routes/events/EventsRoute.tsx b/packages/manager/src/routes/events/EventsRoute.tsx new file mode 100644 index 00000000000..aee80ea08ad --- /dev/null +++ b/packages/manager/src/routes/events/EventsRoute.tsx @@ -0,0 +1,12 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const EventsRoute = () => { + return ( + }> + + + ); +}; diff --git a/packages/manager/src/routes/events.tsx b/packages/manager/src/routes/events/index.ts similarity index 50% rename from packages/manager/src/routes/events.tsx rename to packages/manager/src/routes/events/index.ts index 0289e6f29fe..772ce81e2cc 100644 --- a/packages/manager/src/routes/events.tsx +++ b/packages/manager/src/routes/events/index.ts @@ -1,20 +1,10 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; +import { createRoute } from '@tanstack/react-router'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const EventsRoutes = () => { - return ( - }> - - - ); -}; +import { rootRoute } from '../root'; +import { EventsRoute } from './EventsRoute'; const eventsRoute = createRoute({ - component: EventsRoutes, + component: EventsRoute, getParentRoute: () => rootRoute, path: 'events', }); diff --git a/packages/manager/src/routes/firewalls/FirewallsRoute.tsx b/packages/manager/src/routes/firewalls/FirewallsRoute.tsx new file mode 100644 index 00000000000..45a9f864ccd --- /dev/null +++ b/packages/manager/src/routes/firewalls/FirewallsRoute.tsx @@ -0,0 +1,14 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const FirewallsRoute = () => { + return ( + }> + + + + ); +}; diff --git a/packages/manager/src/routes/firewalls.tsx b/packages/manager/src/routes/firewalls/index.ts similarity index 76% rename from packages/manager/src/routes/firewalls.tsx rename to packages/manager/src/routes/firewalls/index.ts index 79818c7bd46..61a3ef27088 100644 --- a/packages/manager/src/routes/firewalls.tsx +++ b/packages/manager/src/routes/firewalls/index.ts @@ -1,22 +1,10 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; +import { createRoute } from '@tanstack/react-router'; -import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const FirewallsRoutes = () => { - return ( - }> - - - - ); -}; +import { rootRoute } from '../root'; +import { FirewallsRoute } from './FirewallsRoute'; const firewallsRoute = createRoute({ - component: FirewallsRoutes, + component: FirewallsRoute, getParentRoute: () => rootRoute, path: 'firewalls', }); diff --git a/packages/manager/src/routes/images/ImagesRoute.tsx b/packages/manager/src/routes/images/ImagesRoute.tsx new file mode 100644 index 00000000000..5df65fbe1a1 --- /dev/null +++ b/packages/manager/src/routes/images/ImagesRoute.tsx @@ -0,0 +1,14 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const ImagesRoute = () => { + return ( + }> + + + + ); +}; diff --git a/packages/manager/src/routes/images.tsx b/packages/manager/src/routes/images/index.ts similarity index 53% rename from packages/manager/src/routes/images.tsx rename to packages/manager/src/routes/images/index.ts index 881b574151e..2dfa293428a 100644 --- a/packages/manager/src/routes/images.tsx +++ b/packages/manager/src/routes/images/index.ts @@ -1,22 +1,10 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; +import { createRoute } from '@tanstack/react-router'; -import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const ImagesRoutes = () => { - return ( - }> - - - - ); -}; +import { rootRoute } from '../root'; +import { ImagesRoute } from './ImagesRoute'; const imagesRoute = createRoute({ - component: ImagesRoutes, + component: ImagesRoute, getParentRoute: () => rootRoute, path: 'images', }); diff --git a/packages/manager/src/routes/index.tsx b/packages/manager/src/routes/index.tsx index e0381995e9b..b31bfac802c 100644 --- a/packages/manager/src/routes/index.tsx +++ b/packages/manager/src/routes/index.tsx @@ -16,7 +16,7 @@ import { linodesRouteTree } from './linodes'; import { longviewRouteTree } from './longview'; import { managedRouteTree } from './managed'; import { nodeBalancersRouteTree } from './nodeBalancers'; -import { objectStorageRouteTree } from './object-storage'; +import { objectStorageRouteTree } from './objectStorage'; import { placementGroupsRouteTree } from './placementGroups'; import { profileRouteTree } from './profile'; import { migrationRootRoute, rootRoute } from './root'; diff --git a/packages/manager/src/routes/kubernetes/KubernetesRoute.tsx b/packages/manager/src/routes/kubernetes/KubernetesRoute.tsx new file mode 100644 index 00000000000..d8329bf85a1 --- /dev/null +++ b/packages/manager/src/routes/kubernetes/KubernetesRoute.tsx @@ -0,0 +1,14 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const KubernetesRoute = () => { + return ( + }> + + + + ); +}; diff --git a/packages/manager/src/routes/kubernetes.tsx b/packages/manager/src/routes/kubernetes/index.ts similarity index 77% rename from packages/manager/src/routes/kubernetes.tsx rename to packages/manager/src/routes/kubernetes/index.ts index 6d6fc88cb15..4dfa1c52d81 100644 --- a/packages/manager/src/routes/kubernetes.tsx +++ b/packages/manager/src/routes/kubernetes/index.ts @@ -1,19 +1,7 @@ -import { Outlet, createRoute, redirect } from '@tanstack/react-router'; -import React from 'react'; +import { createRoute, redirect } from '@tanstack/react-router'; -import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const KubernetesRoutes = () => { - return ( - }> - - - - ); -}; +import { rootRoute } from '../root'; +import { KubernetesRoute } from './KubernetesRoute'; export const kubernetesRoute = createRoute({ beforeLoad: async ({ location }) => { @@ -23,7 +11,7 @@ export const kubernetesRoute = createRoute({ }); } }, - component: KubernetesRoutes, + component: KubernetesRoute, getParentRoute: () => rootRoute, path: 'kubernetes', }); diff --git a/packages/manager/src/routes/linodes/LinodesRoute.tsx b/packages/manager/src/routes/linodes/LinodesRoute.tsx new file mode 100644 index 00000000000..970c4204e50 --- /dev/null +++ b/packages/manager/src/routes/linodes/LinodesRoute.tsx @@ -0,0 +1,12 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const LinodesRoute = () => { + return ( + }> + + + ); +}; diff --git a/packages/manager/src/routes/linodes.tsx b/packages/manager/src/routes/linodes/index.ts similarity index 88% rename from packages/manager/src/routes/linodes.tsx rename to packages/manager/src/routes/linodes/index.ts index 7574715c46e..573b5d30c93 100644 --- a/packages/manager/src/routes/linodes.tsx +++ b/packages/manager/src/routes/linodes/index.ts @@ -1,20 +1,10 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; +import { createRoute } from '@tanstack/react-router'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const LinodesRoutes = () => { - return ( - }> - - - ); -}; +import { rootRoute } from '../root'; +import { LinodesRoute } from './LinodesRoute'; export const linodesRoute = createRoute({ - component: LinodesRoutes, + component: LinodesRoute, getParentRoute: () => rootRoute, path: 'linodes', }); diff --git a/packages/manager/src/routes/longview/LongviewRoute.tsx b/packages/manager/src/routes/longview/LongviewRoute.tsx new file mode 100644 index 00000000000..57907e7f20d --- /dev/null +++ b/packages/manager/src/routes/longview/LongviewRoute.tsx @@ -0,0 +1,14 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const LongviewRoute = () => { + return ( + }> + + + + ); +}; diff --git a/packages/manager/src/routes/longview.tsx b/packages/manager/src/routes/longview/index.ts similarity index 83% rename from packages/manager/src/routes/longview.tsx rename to packages/manager/src/routes/longview/index.ts index 9e14a97ad36..b684180b509 100644 --- a/packages/manager/src/routes/longview.tsx +++ b/packages/manager/src/routes/longview/index.ts @@ -1,22 +1,10 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; +import { createRoute } from '@tanstack/react-router'; -import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const LongviewRoutes = () => { - return ( - }> - - - - ); -}; +import { rootRoute } from '../root'; +import { LongviewRoute } from './LongviewRoute'; const longviewRoute = createRoute({ - component: LongviewRoutes, + component: LongviewRoute, getParentRoute: () => rootRoute, path: 'longview', }); diff --git a/packages/manager/src/routes/managed/ManagedRoute.tsx b/packages/manager/src/routes/managed/ManagedRoute.tsx new file mode 100644 index 00000000000..24d83aa420b --- /dev/null +++ b/packages/manager/src/routes/managed/ManagedRoute.tsx @@ -0,0 +1,14 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const ManagedRoute = () => { + return ( + }> + + + + ); +}; diff --git a/packages/manager/src/routes/managed.tsx b/packages/manager/src/routes/managed/index.ts similarity index 74% rename from packages/manager/src/routes/managed.tsx rename to packages/manager/src/routes/managed/index.ts index 9b2243c6fa9..637adcde4a6 100644 --- a/packages/manager/src/routes/managed.tsx +++ b/packages/manager/src/routes/managed/index.ts @@ -1,22 +1,10 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; +import { createRoute } from '@tanstack/react-router'; -import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const ManagedRoutes = () => { - return ( - }> - - - - ); -}; +import { rootRoute } from '../root'; +import { ManagedRoute } from './ManagedRoute'; const managedRoute = createRoute({ - component: ManagedRoutes, + component: ManagedRoute, getParentRoute: () => rootRoute, path: 'managed', }); diff --git a/packages/manager/src/routes/nodeBalancers/NodeBalancersRoute.tsx b/packages/manager/src/routes/nodeBalancers/NodeBalancersRoute.tsx new file mode 100644 index 00000000000..c635c9ccce2 --- /dev/null +++ b/packages/manager/src/routes/nodeBalancers/NodeBalancersRoute.tsx @@ -0,0 +1,14 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const NodeBalancersRoute = () => { + return ( + }> + + + + ); +}; diff --git a/packages/manager/src/routes/nodeBalancers.tsx b/packages/manager/src/routes/nodeBalancers/index.ts similarity index 78% rename from packages/manager/src/routes/nodeBalancers.tsx rename to packages/manager/src/routes/nodeBalancers/index.ts index dce8541bd52..7dae79cedcd 100644 --- a/packages/manager/src/routes/nodeBalancers.tsx +++ b/packages/manager/src/routes/nodeBalancers/index.ts @@ -1,26 +1,10 @@ -import { - Outlet, - createRoute, - lazyRouteComponent, -} from '@tanstack/react-router'; -import React from 'react'; +import { createRoute, lazyRouteComponent } from '@tanstack/react-router'; -import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const NodeBalancersRoutes = () => { - return ( - }> - - - - ); -}; +import { rootRoute } from '../root'; +import { NodeBalancersRoute } from './NodeBalancersRoute'; const nodeBalancersRoute = createRoute({ - component: NodeBalancersRoutes, + component: NodeBalancersRoute, getParentRoute: () => rootRoute, path: 'nodebalancers', }); diff --git a/packages/manager/src/routes/objectStorage/ObjectStorageRoute.tsx b/packages/manager/src/routes/objectStorage/ObjectStorageRoute.tsx new file mode 100644 index 00000000000..5b47da7e1e9 --- /dev/null +++ b/packages/manager/src/routes/objectStorage/ObjectStorageRoute.tsx @@ -0,0 +1,14 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const ObjectStorageRoute = () => { + return ( + }> + + + + ); +}; diff --git a/packages/manager/src/routes/object-storage.tsx b/packages/manager/src/routes/objectStorage/index.ts similarity index 81% rename from packages/manager/src/routes/object-storage.tsx rename to packages/manager/src/routes/objectStorage/index.ts index 9a03f21f9db..f2ccbfaaec3 100644 --- a/packages/manager/src/routes/object-storage.tsx +++ b/packages/manager/src/routes/objectStorage/index.ts @@ -1,22 +1,10 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; +import { createRoute } from '@tanstack/react-router'; -import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const ObjectStorageRoutes = () => { - return ( - }> - - - - ); -}; +import { rootRoute } from '../root'; +import { ObjectStorageRoute } from './ObjectStorageRoute'; export const objectStorageRoute = createRoute({ - component: ObjectStorageRoutes, + component: ObjectStorageRoute, getParentRoute: () => rootRoute, path: 'object-storage', }); diff --git a/packages/manager/src/routes/placementGroups/PlacementGroupsRoute.tsx b/packages/manager/src/routes/placementGroups/PlacementGroupsRoute.tsx new file mode 100644 index 00000000000..cd6ab27ee14 --- /dev/null +++ b/packages/manager/src/routes/placementGroups/PlacementGroupsRoute.tsx @@ -0,0 +1,16 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { DocumentTitleSegment } from 'src/components/DocumentTitle'; +import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const PlacementGroupsRoute = () => { + return ( + }> + + + + + ); +}; diff --git a/packages/manager/src/routes/placementGroups.tsx b/packages/manager/src/routes/placementGroups/index.ts similarity index 81% rename from packages/manager/src/routes/placementGroups.tsx rename to packages/manager/src/routes/placementGroups/index.ts index 48ee3d000fc..9088af62b15 100644 --- a/packages/manager/src/routes/placementGroups.tsx +++ b/packages/manager/src/routes/placementGroups/index.ts @@ -1,24 +1,10 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; +import { createRoute } from '@tanstack/react-router'; -import { DocumentTitleSegment } from 'src/components/DocumentTitle'; -import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const AccountRoutes = () => { - return ( - }> - - - - - ); -}; +import { rootRoute } from '../root'; +import { PlacementGroupsRoute } from './PlacementGroupsRoute'; export const placementGroupsRoute = createRoute({ - component: AccountRoutes, + component: PlacementGroupsRoute, getParentRoute: () => rootRoute, path: 'placement-groups', }); diff --git a/packages/manager/src/routes/profile/ProfileRoute.tsx b/packages/manager/src/routes/profile/ProfileRoute.tsx new file mode 100644 index 00000000000..19855a0b965 --- /dev/null +++ b/packages/manager/src/routes/profile/ProfileRoute.tsx @@ -0,0 +1,12 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const ProfileRoute = () => { + return ( + }> + + + ); +}; diff --git a/packages/manager/src/routes/profile.tsx b/packages/manager/src/routes/profile/index.ts similarity index 86% rename from packages/manager/src/routes/profile.tsx rename to packages/manager/src/routes/profile/index.ts index 1b1eec52285..bdeb27f50ae 100644 --- a/packages/manager/src/routes/profile.tsx +++ b/packages/manager/src/routes/profile/index.ts @@ -1,19 +1,10 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; +import { createRoute } from '@tanstack/react-router'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const ProfileRoutes = () => { - return ( - }> - - - ); -}; +import { rootRoute } from '../root'; +import { ProfileRoute } from './ProfileRoute'; const profileRoute = createRoute({ + component: ProfileRoute, getParentRoute: () => rootRoute, path: 'profile', }).lazy(() => diff --git a/packages/manager/src/routes/search.tsx b/packages/manager/src/routes/search.tsx deleted file mode 100644 index 8a013806de5..00000000000 --- a/packages/manager/src/routes/search.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; - -import { SuspenseLoader } from 'src/components/SuspenseLoader'; -import { StatusBanners } from 'src/features/Help/StatusBanners'; - -import { rootRoute } from './root'; - -export const SearchRoutes = () => { - return ( - }> - - - - ); -}; - -const searchRoute = createRoute({ - component: SearchRoutes, - getParentRoute: () => rootRoute, - path: 'search', -}); - -const searchLandingRoute = createRoute({ - getParentRoute: () => searchRoute, - path: '/', -}).lazy(() => - import('src/features/Search/SearchLanding').then( - (m) => m.searchLandingLazyRoute - ) -); - -export const searchRouteTree = searchRoute.addChildren([searchLandingRoute]); diff --git a/packages/manager/src/routes/search/SearchRoute.tsx b/packages/manager/src/routes/search/SearchRoute.tsx new file mode 100644 index 00000000000..b91c38f5d4e --- /dev/null +++ b/packages/manager/src/routes/search/SearchRoute.tsx @@ -0,0 +1,14 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { SuspenseLoader } from 'src/components/SuspenseLoader'; +import { StatusBanners } from 'src/features/Help/StatusBanners'; + +export const SearchRoute = () => { + return ( + }> + + + + ); +}; diff --git a/packages/manager/src/routes/search/index.ts b/packages/manager/src/routes/search/index.ts new file mode 100644 index 00000000000..5e2119ab625 --- /dev/null +++ b/packages/manager/src/routes/search/index.ts @@ -0,0 +1,21 @@ +import { createRoute } from '@tanstack/react-router'; + +import { rootRoute } from '../root'; +import { SearchRoute } from './SearchRoute'; + +const searchRoute = createRoute({ + component: SearchRoute, + getParentRoute: () => rootRoute, + path: 'search', +}); + +const searchLandingRoute = createRoute({ + getParentRoute: () => searchRoute, + path: '/', +}).lazy(() => + import('src/features/Search/SearchLanding').then( + (m) => m.searchLandingLazyRoute + ) +); + +export const searchRouteTree = searchRoute.addChildren([searchLandingRoute]); diff --git a/packages/manager/src/routes/stackscripts/StackscriptsRoute.tsx b/packages/manager/src/routes/stackscripts/StackscriptsRoute.tsx new file mode 100644 index 00000000000..486a60bca87 --- /dev/null +++ b/packages/manager/src/routes/stackscripts/StackscriptsRoute.tsx @@ -0,0 +1,14 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const StackScriptsRoute = () => { + return ( + }> + + + + ); +}; diff --git a/packages/manager/src/routes/stackscripts.tsx b/packages/manager/src/routes/stackscripts/index.tsx similarity index 80% rename from packages/manager/src/routes/stackscripts.tsx rename to packages/manager/src/routes/stackscripts/index.tsx index 9a1def8fdce..83ee8fdfce5 100644 --- a/packages/manager/src/routes/stackscripts.tsx +++ b/packages/manager/src/routes/stackscripts/index.tsx @@ -1,24 +1,14 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; +import { createRoute } from '@tanstack/react-router'; import React from 'react'; -import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; import StackScriptCreate from 'src/features/StackScripts/StackScriptCreate/StackScriptCreate'; import StackScriptDetail from 'src/features/StackScripts/StackScriptsDetail'; -import { rootRoute } from './root'; - -export const StackScriptsRoutes = () => { - return ( - }> - - - - ); -}; +import { rootRoute } from '../root'; +import { StackScriptsRoute } from './StackscriptsRoute'; const stackScriptsRoute = createRoute({ - component: StackScriptsRoutes, + component: StackScriptsRoute, getParentRoute: () => rootRoute, path: 'stackscripts', }); diff --git a/packages/manager/src/routes/support/SupportRoute.tsx b/packages/manager/src/routes/support/SupportRoute.tsx new file mode 100644 index 00000000000..fee1de52d15 --- /dev/null +++ b/packages/manager/src/routes/support/SupportRoute.tsx @@ -0,0 +1,14 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { SuspenseLoader } from 'src/components/SuspenseLoader'; +import { StatusBanners } from 'src/features/Help/StatusBanners'; + +export const SupportTicketsRoute = () => { + return ( + }> + + + + ); +}; diff --git a/packages/manager/src/routes/support.tsx b/packages/manager/src/routes/support/index.ts similarity index 74% rename from packages/manager/src/routes/support.tsx rename to packages/manager/src/routes/support/index.ts index 7082ccd6b37..b82ce14bbd7 100644 --- a/packages/manager/src/routes/support.tsx +++ b/packages/manager/src/routes/support/index.ts @@ -1,23 +1,11 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; +import { createRoute } from '@tanstack/react-router'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; -import { StatusBanners } from 'src/features/Help/StatusBanners'; - -import { rootRoute } from './root'; - -export const SupportTicketsRoutes = () => { - return ( - }> - - - - ); -}; +import { rootRoute } from '../root'; +import { SupportTicketsRoute } from './SupportRoute'; const supportRoute = createRoute({ // TODO: TanStackRouter - got to handle the MainContent.tsx `globalErrors.account_unactivated` logic. - component: SupportTicketsRoutes, + component: SupportTicketsRoute, getParentRoute: () => rootRoute, path: 'support', }); diff --git a/packages/manager/src/routes/volumes/VolumesRoute.tsx b/packages/manager/src/routes/volumes/VolumesRoute.tsx new file mode 100644 index 00000000000..8aa2bf30d58 --- /dev/null +++ b/packages/manager/src/routes/volumes/VolumesRoute.tsx @@ -0,0 +1,14 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const VolumesRoute = () => { + return ( + }> + + + + ); +}; diff --git a/packages/manager/src/routes/volumes.tsx b/packages/manager/src/routes/volumes/index.ts similarity index 53% rename from packages/manager/src/routes/volumes.tsx rename to packages/manager/src/routes/volumes/index.ts index ee9b9971898..9379b004f5b 100644 --- a/packages/manager/src/routes/volumes.tsx +++ b/packages/manager/src/routes/volumes/index.ts @@ -1,22 +1,10 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; +import { createRoute } from '@tanstack/react-router'; -import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const VolumesRoutes = () => { - return ( - }> - - - - ); -}; +import { rootRoute } from '../root'; +import { VolumesRoute } from './VolumesRoute'; const volumesRoute = createRoute({ - component: VolumesRoutes, + component: VolumesRoute, getParentRoute: () => rootRoute, path: 'volumes', }); diff --git a/packages/manager/src/routes/vpcs/VPCRoute.tsx b/packages/manager/src/routes/vpcs/VPCRoute.tsx new file mode 100644 index 00000000000..a8f59364abf --- /dev/null +++ b/packages/manager/src/routes/vpcs/VPCRoute.tsx @@ -0,0 +1,16 @@ +import { Outlet } from '@tanstack/react-router'; +import React from 'react'; + +import { DocumentTitleSegment } from 'src/components/DocumentTitle'; +import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; +import { SuspenseLoader } from 'src/components/SuspenseLoader'; + +export const VPCRoute = () => { + return ( + }> + + + + + ); +}; diff --git a/packages/manager/src/routes/vpcs.tsx b/packages/manager/src/routes/vpcs/index.ts similarity index 55% rename from packages/manager/src/routes/vpcs.tsx rename to packages/manager/src/routes/vpcs/index.ts index d63f4c86ed9..00c9bfc5044 100644 --- a/packages/manager/src/routes/vpcs.tsx +++ b/packages/manager/src/routes/vpcs/index.ts @@ -1,24 +1,10 @@ -import { Outlet, createRoute } from '@tanstack/react-router'; -import React from 'react'; +import { createRoute } from '@tanstack/react-router'; -import { DocumentTitleSegment } from 'src/components/DocumentTitle'; -import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner'; -import { SuspenseLoader } from 'src/components/SuspenseLoader'; - -import { rootRoute } from './root'; - -export const VPCRoutes = () => { - return ( - }> - - - - - ); -}; +import { rootRoute } from '../root'; +import { VPCRoute } from './VPCRoute'; const vpcsRoute = createRoute({ - component: VPCRoutes, + component: VPCRoute, getParentRoute: () => rootRoute, path: 'vpcs', }); diff --git a/yarn.lock b/yarn.lock index 4219a830d0d..bc7e16e4ba3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4818,6 +4818,11 @@ eslint-plugin-react-hooks@^3.0.0: resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-3.0.0.tgz#9e80c71846eb68dd29c3b21d832728aa66e5bd35" integrity sha512-EjxTHxjLKIBWFgDJdhKKzLh5q+vjTFrqNZX36uIxWS4OfyXe5DawqPj3U5qeJ1ngLwatjzQnmR0Lz0J0YH3kxw== +eslint-plugin-react-refresh@^0.4.13: + version "0.4.13" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.13.tgz#ed7330da09b6192e6fa9b1b217ad979afbc898bf" + integrity sha512-f1EppwrpJRWmqDTyvAyomFVDYRtrS7iTEqv3nokETnMiMzs2SSTmKRTACce4O2p4jYyowiSMvpdwC/RLcMFhuQ== + eslint-plugin-react@^7.19.0: version "7.36.1" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.36.1.tgz#f1dabbb11f3d4ebe8b0cf4e54aff4aee81144ee5" @@ -9272,7 +9277,7 @@ string-argv@~0.3.2: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9290,6 +9295,15 @@ string-width@^3.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -9370,7 +9384,7 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9384,6 +9398,13 @@ strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -10334,7 +10355,7 @@ word-wrap@^1.2.5, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -10352,6 +10373,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 52052ce671cc4d5b86c748f0ee6dd4d07baf487b Mon Sep 17 00:00:00 2001 From: Alban Bailly <130582365+abailly-akamai@users.noreply.github.com> Date: Fri, 25 Oct 2024 11:01:00 -0400 Subject: [PATCH 07/66] fix: [M3-8788] - Only run coverage comment job on non-drafts (#11161) * only run is workflow_run is successful * changeset * Update .github/workflows/coverage_comment.yml Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com> --------- Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com> --- .github/workflows/coverage_comment.yml | 1 + packages/manager/.changeset/pr-11161-fixed-1729827835443.md | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 packages/manager/.changeset/pr-11161-fixed-1729827835443.md diff --git a/.github/workflows/coverage_comment.yml b/.github/workflows/coverage_comment.yml index 8ddd29c1ce6..9c395dd244d 100644 --- a/.github/workflows/coverage_comment.yml +++ b/.github/workflows/coverage_comment.yml @@ -11,6 +11,7 @@ permissions: jobs: comment: + if: ${{ github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest steps: diff --git a/packages/manager/.changeset/pr-11161-fixed-1729827835443.md b/packages/manager/.changeset/pr-11161-fixed-1729827835443.md new file mode 100644 index 00000000000..f3b0eacd8aa --- /dev/null +++ b/packages/manager/.changeset/pr-11161-fixed-1729827835443.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +Only run 'Coverage Comment' GHA on non-drafts ([#11161](https://github.com/linode/manager/pull/11161)) From 688f35f5ecb52d6202543b79a1974043fb8f05df Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Fri, 25 Oct 2024 21:11:37 +0530 Subject: [PATCH 08/66] upcoming: [DI-21520] - Added default xFilter for fetching aiven clusters for dbass and fixed label fallback for service-type label (#11150) * Merge pull request #39 from ACLPManager/feature/dbass_aiven_fetch_aclp upcoming: [DI-21520] - Added default xFilter for fetching aiven clusters for dbass * Added changeset: Add default xfilter for DBasS aiven clusters fetch in resource selection component * upcoming: [DI-21520] - Add platform filter for jwe token fetch for aiven clusters in DBasS * upcoming: [DI-21520] - PR Comments * upcoming: [DI-21520] - PR Comments * upcoming: [DI-21520] - PR Comments --------- Co-authored-by: venkatmano-akamai --- .../pr-11150-upcoming-features-1729746495913.md | 5 +++++ .../CloudPulse/Dashboard/CloudPulseDashboard.tsx | 2 +- .../CloudPulse/shared/CloudPulseDashboardSelect.tsx | 4 +--- .../CloudPulse/shared/CloudPulseResourcesSelect.tsx | 13 ++++++++++++- 4 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 packages/manager/.changeset/pr-11150-upcoming-features-1729746495913.md diff --git a/packages/manager/.changeset/pr-11150-upcoming-features-1729746495913.md b/packages/manager/.changeset/pr-11150-upcoming-features-1729746495913.md new file mode 100644 index 00000000000..bbe5b071ce9 --- /dev/null +++ b/packages/manager/.changeset/pr-11150-upcoming-features-1729746495913.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Add default xfilter for DBasS aiven clusters fetch in resource selection component ([#11150](https://github.com/linode/manager/pull/11150)) diff --git a/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboard.tsx b/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboard.tsx index 220c8b2813b..a333a379858 100644 --- a/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboard.tsx +++ b/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboard.tsx @@ -83,7 +83,7 @@ export const CloudPulseDashboard = (props: DashboardProperties) => { Boolean(dashboard?.service_type), dashboard?.service_type, {}, - {} + dashboard?.service_type === 'dbaas' ? { platform: 'rdbms-default' } : {} ); const { diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseDashboardSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseDashboardSelect.tsx index 19828bc4328..cc009df1b56 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseDashboardSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseDashboardSelect.tsx @@ -95,9 +95,7 @@ export const CloudPulseDashboardSelect = React.memo( renderGroup={(params) => ( - {serviceTypeMap.has(params.group) - ? serviceTypeMap.get(params.group) - : params.group} + {serviceTypeMap.get(params.group) || params.group} {params.children} diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx index 9ee85d4cd6e..15c7a6af83e 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx @@ -43,11 +43,22 @@ export const CloudPulseResourcesSelect = React.memo( xFilter, } = props; + const platformFilter = + resourceType === 'dbaas' ? { platform: 'rdbms-default' } : {}; + const { data: resources, isLoading } = useResourcesQuery( disabled !== undefined ? !disabled : Boolean(region && resourceType), resourceType, {}, - xFilter ? xFilter : { region } + xFilter + ? { + ...platformFilter, + ...xFilter, + } + : { + ...platformFilter, + region, + } ); const [selectedResources, setSelectedResources] = React.useState< From 73c3342ec3d6a995849ea3737e9fcc291b52fa8c Mon Sep 17 00:00:00 2001 From: Hussain Khalil <122488130+hkhalil-akamai@users.noreply.github.com> Date: Fri, 25 Oct 2024 15:50:48 -0400 Subject: [PATCH 09/66] refactor: [M3-8707] - Move `IconButton` component to UI package (#11158) * Move `IconButton` to UI package * Added changeset: IconButton component * Added changeset: Tooltip component and story * Update Tooltip changeset --- .../src/components/Autocomplete/Autocomplete.stories.tsx | 4 ++-- .../DebouncedSearchTextField/DebouncedSearchTextField.tsx | 3 +-- .../manager/src/components/DialogTitle/DialogTitle.tsx | 2 +- packages/manager/src/components/Drawer.tsx | 8 +++++--- .../src/components/EditableEntityLabel/EditableInput.tsx | 5 +++-- packages/manager/src/components/MenuItem/MenuItem.tsx | 8 +++++--- .../src/components/PrimaryNav/SideMenu.stories.tsx | 2 +- .../RemovableSelectionsList/RemovableSelectionsList.tsx | 2 +- .../RemovableSelectionsListTable.tsx | 2 +- .../manager/src/components/Snackbar/CloseSnackbar.tsx | 3 +-- packages/manager/src/components/TagCell/TagCell.tsx | 2 +- packages/manager/src/components/TooltipIcon.tsx | 2 +- .../src/features/Billing/InvoiceDetail/InvoiceDetail.tsx | 2 +- .../Images/ImagesLanding/ImageRegions/ImageRegionRow.tsx | 2 +- .../src/features/Images/ImagesLanding/ImagesLanding.tsx | 2 +- .../Kubernetes/KubeCheckoutBar/NodePoolSummary.tsx | 7 ++++--- .../Tabs/StackScripts/StackScriptSelectionList.tsx | 2 +- .../StackScripts/UserDefinedFields/UserDefinedFields.tsx | 2 +- .../src/features/Linodes/LinodeCreate/VPC/VPCRanges.tsx | 2 +- .../Linodes/LinodesLanding/DisplayLinodes.styles.ts | 2 +- .../StackScripts/UserDefinedFieldsPanel/AppInfo.tsx | 2 +- .../manager/src/features/Support/TicketDetailText.tsx | 5 +++-- .../src/features/TopMenu/SearchBar/SearchBar.styles.ts | 3 +-- packages/manager/src/features/TopMenu/TopMenu.tsx | 2 +- packages/manager/src/features/Volumes/VolumesLanding.tsx | 2 +- .../PlansPanel/DisabledPlanSelectionTooltip.tsx | 3 +-- packages/ui/.changeset/pr-11125-added-1729871688954.md | 5 +++++ packages/ui/.changeset/pr-11158-added-1729864026236.md | 5 +++++ packages/{manager => ui}/src/components/IconButton.tsx | 0 packages/ui/src/components/index.ts | 1 + 30 files changed, 53 insertions(+), 39 deletions(-) create mode 100644 packages/ui/.changeset/pr-11125-added-1729871688954.md create mode 100644 packages/ui/.changeset/pr-11158-added-1729864026236.md rename packages/{manager => ui}/src/components/IconButton.tsx (100%) diff --git a/packages/manager/src/components/Autocomplete/Autocomplete.stories.tsx b/packages/manager/src/components/Autocomplete/Autocomplete.stories.tsx index 3ba5f80cd06..2516418fc67 100644 --- a/packages/manager/src/components/Autocomplete/Autocomplete.stories.tsx +++ b/packages/manager/src/components/Autocomplete/Autocomplete.stories.tsx @@ -1,11 +1,10 @@ -import { Linode } from '@linode/api-v4'; +import { IconButton } from '@linode/ui'; import Close from '@mui/icons-material/Close'; import { Stack } from '@mui/material'; import { styled } from '@mui/material/styles'; import { action } from '@storybook/addon-actions'; import React, { useState } from 'react'; -import { IconButton } from 'src/components/IconButton'; import { List } from 'src/components/List'; import { ListItem } from 'src/components/ListItem'; import { linodeFactory } from 'src/factories'; @@ -14,6 +13,7 @@ import { Autocomplete } from './Autocomplete'; import { SelectedIcon } from './Autocomplete.styles'; import type { EnhancedAutocompleteProps } from './Autocomplete'; +import type { Linode } from '@linode/api-v4'; import type { Meta, StoryFn, StoryObj } from '@storybook/react'; const LABEL = 'Select a Linode'; diff --git a/packages/manager/src/components/DebouncedSearchTextField/DebouncedSearchTextField.tsx b/packages/manager/src/components/DebouncedSearchTextField/DebouncedSearchTextField.tsx index 1db938d8756..9069bd8195a 100644 --- a/packages/manager/src/components/DebouncedSearchTextField/DebouncedSearchTextField.tsx +++ b/packages/manager/src/components/DebouncedSearchTextField/DebouncedSearchTextField.tsx @@ -1,3 +1,4 @@ +import { IconButton } from '@linode/ui'; import Clear from '@mui/icons-material/Clear'; import Search from '@mui/icons-material/Search'; import { styled } from '@mui/material/styles'; @@ -8,8 +9,6 @@ import { CircleProgress } from 'src/components/CircleProgress'; import { InputAdornment } from 'src/components/InputAdornment'; import { TextField } from 'src/components/TextField'; -import { IconButton } from '../IconButton'; - import type { TextFieldProps } from 'src/components/TextField'; export interface DebouncedSearchProps extends TextFieldProps { diff --git a/packages/manager/src/components/DialogTitle/DialogTitle.tsx b/packages/manager/src/components/DialogTitle/DialogTitle.tsx index 62d1e7b50ff..711c328c419 100644 --- a/packages/manager/src/components/DialogTitle/DialogTitle.tsx +++ b/packages/manager/src/components/DialogTitle/DialogTitle.tsx @@ -1,10 +1,10 @@ +import { IconButton } from '@linode/ui'; import Close from '@mui/icons-material/Close'; import { Typography } from '@mui/material'; import _DialogTitle from '@mui/material/DialogTitle'; import * as React from 'react'; import { Box } from 'src/components/Box'; -import { IconButton } from 'src/components/IconButton'; import type { SxProps, Theme } from '@mui/material'; diff --git a/packages/manager/src/components/Drawer.tsx b/packages/manager/src/components/Drawer.tsx index 9fbc71c7910..4490a0090e4 100644 --- a/packages/manager/src/components/Drawer.tsx +++ b/packages/manager/src/components/Drawer.tsx @@ -1,14 +1,16 @@ +import { IconButton } from '@linode/ui'; import Close from '@mui/icons-material/Close'; -import _Drawer, { DrawerProps } from '@mui/material/Drawer'; -import { Theme } from '@mui/material/styles'; +import _Drawer from '@mui/material/Drawer'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; -import { IconButton } from 'src/components/IconButton'; import { Typography } from 'src/components/Typography'; import { convertForAria } from 'src/utilities/stringUtils'; +import type { DrawerProps } from '@mui/material/Drawer'; +import type { Theme } from '@mui/material/styles'; + interface Props extends DrawerProps { /** * Callback fired when the drawer closing animation has completed. diff --git a/packages/manager/src/components/EditableEntityLabel/EditableInput.tsx b/packages/manager/src/components/EditableEntityLabel/EditableInput.tsx index eed99a58181..2873c2b8b10 100644 --- a/packages/manager/src/components/EditableEntityLabel/EditableInput.tsx +++ b/packages/manager/src/components/EditableEntityLabel/EditableInput.tsx @@ -1,10 +1,9 @@ +import { IconButton } from '@linode/ui'; import Check from '@mui/icons-material/Check'; import Close from '@mui/icons-material/Close'; import * as React from 'react'; import { ClickAwayListener } from 'src/components/ClickAwayListener'; -import { IconButton } from 'src/components/IconButton'; -import { TextFieldProps } from 'src/components/TextField'; import { StyledButton, @@ -15,6 +14,8 @@ import { StyledTypography, } from './EditableInput.styles'; +import type { TextFieldProps } from 'src/components/TextField'; + export type EditableTextVariant = 'h1' | 'h2' | 'table-cell'; export interface EditableInputProps { diff --git a/packages/manager/src/components/MenuItem/MenuItem.tsx b/packages/manager/src/components/MenuItem/MenuItem.tsx index 3d153a1fda0..8d54587804e 100644 --- a/packages/manager/src/components/MenuItem/MenuItem.tsx +++ b/packages/manager/src/components/MenuItem/MenuItem.tsx @@ -1,10 +1,12 @@ +import { IconButton } from '@linode/ui'; import HelpOutline from '@mui/icons-material/HelpOutline'; -import { Theme } from '@mui/material/styles'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; -import { IconButton } from 'src/components/IconButton'; -import { MenuItem, MenuItemProps } from 'src/components/MenuItem'; +import { MenuItem } from 'src/components/MenuItem'; + +import type { Theme } from '@mui/material/styles'; +import type { MenuItemProps } from 'src/components/MenuItem'; interface WrapperMenuItemProps { isLoading?: boolean; diff --git a/packages/manager/src/components/PrimaryNav/SideMenu.stories.tsx b/packages/manager/src/components/PrimaryNav/SideMenu.stories.tsx index 2bba6ff0fa6..18e333701d1 100644 --- a/packages/manager/src/components/PrimaryNav/SideMenu.stories.tsx +++ b/packages/manager/src/components/PrimaryNav/SideMenu.stories.tsx @@ -1,10 +1,10 @@ +import { IconButton } from '@linode/ui'; import MenuIcon from '@mui/icons-material/Menu'; import { useArgs } from '@storybook/preview-api'; import * as React from 'react'; import { Box } from 'src/components/Box'; import { Hidden } from 'src/components/Hidden'; -import { IconButton } from 'src/components/IconButton'; import { TopMenuTooltip } from 'src/features/TopMenu/TopMenuTooltip'; import { SideMenu } from './SideMenu'; diff --git a/packages/manager/src/components/RemovableSelectionsList/RemovableSelectionsList.tsx b/packages/manager/src/components/RemovableSelectionsList/RemovableSelectionsList.tsx index 34e14dc58a6..0d364358c16 100644 --- a/packages/manager/src/components/RemovableSelectionsList/RemovableSelectionsList.tsx +++ b/packages/manager/src/components/RemovableSelectionsList/RemovableSelectionsList.tsx @@ -1,8 +1,8 @@ +import { IconButton } from '@linode/ui'; import Close from '@mui/icons-material/Close'; import * as React from 'react'; import { Box } from 'src/components/Box'; -import { IconButton } from 'src/components/IconButton'; import { SelectedOptionsHeader, diff --git a/packages/manager/src/components/RemovableSelectionsList/RemovableSelectionsListTable.tsx b/packages/manager/src/components/RemovableSelectionsList/RemovableSelectionsListTable.tsx index 2560401fdfa..dea0c051d07 100644 --- a/packages/manager/src/components/RemovableSelectionsList/RemovableSelectionsListTable.tsx +++ b/packages/manager/src/components/RemovableSelectionsList/RemovableSelectionsListTable.tsx @@ -1,7 +1,7 @@ +import { IconButton } from '@linode/ui'; import Close from '@mui/icons-material/Close'; import * as React from 'react'; -import { IconButton } from 'src/components/IconButton'; import { Table } from 'src/components/Table'; import { TableBody } from 'src/components/TableBody'; import { TableCell } from 'src/components/TableCell'; diff --git a/packages/manager/src/components/Snackbar/CloseSnackbar.tsx b/packages/manager/src/components/Snackbar/CloseSnackbar.tsx index e71673421cc..7a2ae79136f 100644 --- a/packages/manager/src/components/Snackbar/CloseSnackbar.tsx +++ b/packages/manager/src/components/Snackbar/CloseSnackbar.tsx @@ -1,8 +1,7 @@ +import { IconButton } from '@linode/ui'; import Close from '@mui/icons-material/Close'; import * as React from 'react'; -import { IconButton } from 'src/components/IconButton'; - interface Props { onClick: () => void; text: string; diff --git a/packages/manager/src/components/TagCell/TagCell.tsx b/packages/manager/src/components/TagCell/TagCell.tsx index 49b037bf6d4..e3f85c9140f 100644 --- a/packages/manager/src/components/TagCell/TagCell.tsx +++ b/packages/manager/src/components/TagCell/TagCell.tsx @@ -1,9 +1,9 @@ +import { IconButton } from '@linode/ui'; import MoreHoriz from '@mui/icons-material/MoreHoriz'; import { styled } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; -import { IconButton } from 'src/components/IconButton'; import { Tag } from 'src/components/Tag/Tag'; import { useWindowDimensions } from 'src/hooks/useWindowDimensions'; import { omittedProps } from 'src/utilities/omittedProps'; diff --git a/packages/manager/src/components/TooltipIcon.tsx b/packages/manager/src/components/TooltipIcon.tsx index d27add81c40..b187c60ed89 100644 --- a/packages/manager/src/components/TooltipIcon.tsx +++ b/packages/manager/src/components/TooltipIcon.tsx @@ -1,4 +1,5 @@ import styled from '@emotion/styled'; +import { IconButton } from '@linode/ui'; import { Tooltip, tooltipClasses } from '@linode/ui'; import SuccessOutline from '@mui/icons-material/CheckCircleOutlined'; import ErrorOutline from '@mui/icons-material/ErrorOutline'; @@ -8,7 +9,6 @@ import WarningSolid from '@mui/icons-material/Warning'; import { useTheme } from '@mui/material/styles'; import * as React from 'react'; -import { IconButton } from 'src/components/IconButton'; import { omittedProps } from 'src/utilities/omittedProps'; import type { TooltipProps } from '@linode/ui'; diff --git a/packages/manager/src/features/Billing/InvoiceDetail/InvoiceDetail.tsx b/packages/manager/src/features/Billing/InvoiceDetail/InvoiceDetail.tsx index 953207624f6..0d8d022c17b 100644 --- a/packages/manager/src/features/Billing/InvoiceDetail/InvoiceDetail.tsx +++ b/packages/manager/src/features/Billing/InvoiceDetail/InvoiceDetail.tsx @@ -1,4 +1,5 @@ import { getInvoice, getInvoiceItems } from '@linode/api-v4/lib/account'; +import { IconButton } from '@linode/ui'; import KeyboardArrowLeft from '@mui/icons-material/KeyboardArrowLeft'; import { useTheme } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; @@ -10,7 +11,6 @@ import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; import { Currency } from 'src/components/Currency'; import { DownloadCSV } from 'src/components/DownloadCSV/DownloadCSV'; -import { IconButton } from 'src/components/IconButton'; import { LandingHeader } from 'src/components/LandingHeader'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; diff --git a/packages/manager/src/features/Images/ImagesLanding/ImageRegions/ImageRegionRow.tsx b/packages/manager/src/features/Images/ImagesLanding/ImageRegions/ImageRegionRow.tsx index d2b314df7c4..7190630322b 100644 --- a/packages/manager/src/features/Images/ImagesLanding/ImageRegions/ImageRegionRow.tsx +++ b/packages/manager/src/features/Images/ImagesLanding/ImageRegions/ImageRegionRow.tsx @@ -1,10 +1,10 @@ +import { IconButton } from '@linode/ui'; import { Tooltip } from '@linode/ui'; import Close from '@mui/icons-material/Close'; import React from 'react'; import { Box } from 'src/components/Box'; import { Flag } from 'src/components/Flag'; -import { IconButton } from 'src/components/IconButton'; import { Stack } from 'src/components/Stack'; import { StatusIcon } from 'src/components/StatusIcon/StatusIcon'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Images/ImagesLanding/ImagesLanding.tsx b/packages/manager/src/features/Images/ImagesLanding/ImagesLanding.tsx index 1a52b32b1c4..22ad3a3880c 100644 --- a/packages/manager/src/features/Images/ImagesLanding/ImagesLanding.tsx +++ b/packages/manager/src/features/Images/ImagesLanding/ImagesLanding.tsx @@ -1,3 +1,4 @@ +import { IconButton } from '@linode/ui'; import CloseIcon from '@mui/icons-material/Close'; import { useQueryClient } from '@tanstack/react-query'; import { createLazyRoute } from '@tanstack/react-router'; @@ -14,7 +15,6 @@ import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { Drawer } from 'src/components/Drawer'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { Hidden } from 'src/components/Hidden'; -import { IconButton } from 'src/components/IconButton'; import { InputAdornment } from 'src/components/InputAdornment'; import { LandingHeader } from 'src/components/LandingHeader'; import { Notice } from 'src/components/Notice/Notice'; diff --git a/packages/manager/src/features/Kubernetes/KubeCheckoutBar/NodePoolSummary.tsx b/packages/manager/src/features/Kubernetes/KubeCheckoutBar/NodePoolSummary.tsx index a36523911dc..2a8eb496fe3 100644 --- a/packages/manager/src/features/Kubernetes/KubeCheckoutBar/NodePoolSummary.tsx +++ b/packages/manager/src/features/Kubernetes/KubeCheckoutBar/NodePoolSummary.tsx @@ -1,5 +1,5 @@ +import { IconButton } from '@linode/ui'; import Close from '@mui/icons-material/Close'; -import { Theme } from '@mui/material/styles'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; @@ -7,11 +7,12 @@ import { Box } from 'src/components/Box'; import { DisplayPrice } from 'src/components/DisplayPrice'; import { Divider } from 'src/components/Divider'; import { EnhancedNumberInput } from 'src/components/EnhancedNumberInput/EnhancedNumberInput'; -import { IconButton } from 'src/components/IconButton'; import { Typography } from 'src/components/Typography'; -import { ExtendedType } from 'src/utilities/extendType'; import { pluralize } from 'src/utilities/pluralize'; +import type { Theme } from '@mui/material/styles'; +import type { ExtendedType } from 'src/utilities/extendType'; + const useStyles = makeStyles()((theme: Theme) => ({ button: { '&:hover': { diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/StackScriptSelectionList.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/StackScriptSelectionList.tsx index 650ad4cacd1..c6b5a014261 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/StackScriptSelectionList.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/StackScriptSelectionList.tsx @@ -1,4 +1,5 @@ import { getAPIFilterFromQuery } from '@linode/search'; +import { IconButton } from '@linode/ui'; import CloseIcon from '@mui/icons-material/Close'; import { useQueryClient } from '@tanstack/react-query'; import React, { useState } from 'react'; @@ -10,7 +11,6 @@ import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; import { CircleProgress } from 'src/components/CircleProgress'; import { Code } from 'src/components/Code/Code'; -import { IconButton } from 'src/components/IconButton'; import { InputAdornment } from 'src/components/InputAdornment'; import { Stack } from 'src/components/Stack'; import { Table } from 'src/components/Table'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/UserDefinedFields/UserDefinedFields.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/UserDefinedFields/UserDefinedFields.tsx index 8316b8c8e75..79d5550ed13 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/UserDefinedFields/UserDefinedFields.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/UserDefinedFields/UserDefinedFields.tsx @@ -1,9 +1,9 @@ +import { IconButton } from '@linode/ui'; import React from 'react'; import { useFormContext, useWatch } from 'react-hook-form'; import Info from 'src/assets/icons/info.svg'; import { Box } from 'src/components/Box'; -import { IconButton } from 'src/components/IconButton'; import { Notice } from 'src/components/Notice/Notice'; import { Paper } from 'src/components/Paper'; import { ShowMoreExpansion } from 'src/components/ShowMoreExpansion'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/VPC/VPCRanges.tsx b/packages/manager/src/features/Linodes/LinodeCreate/VPC/VPCRanges.tsx index cbcf160789f..b31ff1adbe3 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/VPC/VPCRanges.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/VPC/VPCRanges.tsx @@ -1,9 +1,9 @@ +import { IconButton } from '@linode/ui'; import CloseIcon from '@mui/icons-material/Close'; import React from 'react'; import { Controller, useFieldArray, useFormContext } from 'react-hook-form'; import { Box } from 'src/components/Box'; -import { IconButton } from 'src/components/IconButton'; import { LinkButton } from 'src/components/LinkButton'; import { Stack } from 'src/components/Stack'; import { TextField } from 'src/components/TextField'; diff --git a/packages/manager/src/features/Linodes/LinodesLanding/DisplayLinodes.styles.ts b/packages/manager/src/features/Linodes/LinodesLanding/DisplayLinodes.styles.ts index 56e75d7fe40..a3f6a27cf53 100644 --- a/packages/manager/src/features/Linodes/LinodesLanding/DisplayLinodes.styles.ts +++ b/packages/manager/src/features/Linodes/LinodesLanding/DisplayLinodes.styles.ts @@ -1,6 +1,6 @@ +import { IconButton } from '@linode/ui'; import { styled } from '@mui/material/styles'; -import { IconButton } from 'src/components/IconButton'; import { TableRow } from 'src/components/TableRow'; import { Typography } from 'src/components/Typography'; import { omittedProps } from 'src/utilities/omittedProps'; diff --git a/packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/AppInfo.tsx b/packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/AppInfo.tsx index cbe13db862b..116175721e6 100644 --- a/packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/AppInfo.tsx +++ b/packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/AppInfo.tsx @@ -1,7 +1,7 @@ +import { IconButton } from '@linode/ui'; import * as React from 'react'; import Info from 'src/assets/icons/info.svg'; -import { IconButton } from 'src/components/IconButton'; interface Props { onClick: () => void; diff --git a/packages/manager/src/features/Support/TicketDetailText.tsx b/packages/manager/src/features/Support/TicketDetailText.tsx index a346e5fa356..15aa899a8ca 100644 --- a/packages/manager/src/features/Support/TicketDetailText.tsx +++ b/packages/manager/src/features/Support/TicketDetailText.tsx @@ -1,13 +1,14 @@ +import { IconButton } from '@linode/ui'; import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown'; -import { Theme } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; import { HighlightedMarkdown } from 'src/components/HighlightedMarkdown/HighlightedMarkdown'; -import { IconButton } from 'src/components/IconButton'; import { truncate } from 'src/utilities/truncate'; +import type { Theme } from '@mui/material/styles'; + const useStyles = makeStyles()((theme: Theme) => ({ expButton: { '& svg': { diff --git a/packages/manager/src/features/TopMenu/SearchBar/SearchBar.styles.ts b/packages/manager/src/features/TopMenu/SearchBar/SearchBar.styles.ts index ce2713f1cf1..f23d19daff8 100644 --- a/packages/manager/src/features/TopMenu/SearchBar/SearchBar.styles.ts +++ b/packages/manager/src/features/TopMenu/SearchBar/SearchBar.styles.ts @@ -1,7 +1,6 @@ +import { IconButton } from '@linode/ui'; import { styled } from '@mui/material/styles'; -import { IconButton } from 'src/components/IconButton'; - export const StyledIconButton = styled(IconButton, { label: 'StyledIconButton', })(({ theme }) => ({ diff --git a/packages/manager/src/features/TopMenu/TopMenu.tsx b/packages/manager/src/features/TopMenu/TopMenu.tsx index c44a4249123..5f3a4406f36 100644 --- a/packages/manager/src/features/TopMenu/TopMenu.tsx +++ b/packages/manager/src/features/TopMenu/TopMenu.tsx @@ -1,10 +1,10 @@ +import { IconButton } from '@linode/ui'; import MenuIcon from '@mui/icons-material/Menu'; import * as React from 'react'; import { AppBar } from 'src/components/AppBar'; import { Box } from 'src/components/Box'; import { Hidden } from 'src/components/Hidden'; -import { IconButton } from 'src/components/IconButton'; import { Toolbar } from 'src/components/Toolbar'; import { Typography } from 'src/components/Typography'; import { useAuthentication } from 'src/hooks/useAuthentication'; diff --git a/packages/manager/src/features/Volumes/VolumesLanding.tsx b/packages/manager/src/features/Volumes/VolumesLanding.tsx index 803ef918146..55973edb53b 100644 --- a/packages/manager/src/features/Volumes/VolumesLanding.tsx +++ b/packages/manager/src/features/Volumes/VolumesLanding.tsx @@ -1,3 +1,4 @@ +import { IconButton } from '@linode/ui'; import CloseIcon from '@mui/icons-material/Close'; import { createLazyRoute } from '@tanstack/react-router'; import * as React from 'react'; @@ -8,7 +9,6 @@ import { CircleProgress } from 'src/components/CircleProgress'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { useIsBlockStorageEncryptionFeatureEnabled } from 'src/components/Encryption/utils'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; -import { IconButton } from 'src/components/IconButton'; import { InputAdornment } from 'src/components/InputAdornment'; import { LandingHeader } from 'src/components/LandingHeader'; import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFooter'; diff --git a/packages/manager/src/features/components/PlansPanel/DisabledPlanSelectionTooltip.tsx b/packages/manager/src/features/components/PlansPanel/DisabledPlanSelectionTooltip.tsx index 9227cec6247..9a9bd187c8c 100644 --- a/packages/manager/src/features/components/PlansPanel/DisabledPlanSelectionTooltip.tsx +++ b/packages/manager/src/features/components/PlansPanel/DisabledPlanSelectionTooltip.tsx @@ -1,9 +1,8 @@ +import { IconButton } from '@linode/ui'; import { Tooltip } from '@linode/ui'; import HelpOutline from '@mui/icons-material/HelpOutline'; import * as React from 'react'; -import { IconButton } from 'src/components/IconButton'; - interface DisabledPlanSelectionTooltipProps { tooltipCopy: string; } diff --git a/packages/ui/.changeset/pr-11125-added-1729871688954.md b/packages/ui/.changeset/pr-11125-added-1729871688954.md new file mode 100644 index 00000000000..98553459363 --- /dev/null +++ b/packages/ui/.changeset/pr-11125-added-1729871688954.md @@ -0,0 +1,5 @@ +--- +"@linode/ui": Added +--- + +Tooltip component and story ([#11125](https://github.com/linode/manager/pull/11125)) diff --git a/packages/ui/.changeset/pr-11158-added-1729864026236.md b/packages/ui/.changeset/pr-11158-added-1729864026236.md new file mode 100644 index 00000000000..0f0762718da --- /dev/null +++ b/packages/ui/.changeset/pr-11158-added-1729864026236.md @@ -0,0 +1,5 @@ +--- +"@linode/ui": Added +--- + +IconButton component ([#11158](https://github.com/linode/manager/pull/11158)) diff --git a/packages/manager/src/components/IconButton.tsx b/packages/ui/src/components/IconButton.tsx similarity index 100% rename from packages/manager/src/components/IconButton.tsx rename to packages/ui/src/components/IconButton.tsx diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index 5998192f93f..df5b4a81ed2 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -1,3 +1,4 @@ export * from './Chip'; export * from './BetaChip'; +export * from './IconButton'; export * from './Tooltip'; From b5da866ae2be8714dbcebaa64d44862517b89522 Mon Sep 17 00:00:00 2001 From: zaenab-akamai Date: Mon, 28 Oct 2024 09:48:11 +0530 Subject: [PATCH 10/66] fix: [M3-8590] - Restrict access to Database Create page for restricted users (#11137) * Restrict access to Database Create page for restricted users * updated DatabaseCreate tests * Added notice for restricted users on Database Create page * Fix * Added changeset: Database create page form being enabled for restricted users * Addressed PR comments --- .../pr-11137-fixed-1729745562099.md | 5 + .../DatabaseCreate/DatabaseCreate.test.tsx | 104 ++++++++++++++++++ .../DatabaseCreate/DatabaseCreate.tsx | 27 ++++- .../DatabaseCreateAccessControls.tsx | 27 +++-- 4 files changed, 152 insertions(+), 11 deletions(-) create mode 100644 packages/manager/.changeset/pr-11137-fixed-1729745562099.md diff --git a/packages/manager/.changeset/pr-11137-fixed-1729745562099.md b/packages/manager/.changeset/pr-11137-fixed-1729745562099.md new file mode 100644 index 00000000000..a502521a780 --- /dev/null +++ b/packages/manager/.changeset/pr-11137-fixed-1729745562099.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +Database create page form being enabled for restricted users ([#11137](https://github.com/linode/manager/pull/11137)) diff --git a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.test.tsx b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.test.tsx index 40b0bf1c6ce..1b4c8241343 100644 --- a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.test.tsx +++ b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.test.tsx @@ -12,6 +12,18 @@ import DatabaseCreate from './DatabaseCreate'; const loadingTestId = 'circle-progress'; +const queryMocks = vi.hoisted(() => ({ + useProfile: vi.fn().mockReturnValue({ data: { restricted: false } }), +})); + +vi.mock('src/queries/profile/profile', async () => { + const actual = await vi.importActual('src/queries/profile/profile'); + return { + ...actual, + useProfile: queryMocks.useProfile, + }; +}); + beforeAll(() => mockMatchMedia()); describe('Database Create', () => { @@ -154,4 +166,96 @@ describe('Database Create', () => { expect(nodeRadioBtns).toHaveTextContent('$100/month $0.15/hr'); expect(nodeRadioBtns).toHaveTextContent('$140/month $0.21/hr'); }); + + it('should have the "Create Database Cluster" button disabled for restricted users', async () => { + queryMocks.useProfile.mockReturnValue({ data: { restricted: true } }); + + const { findByText, getByTestId } = renderWithTheme(); + + expect(getByTestId(loadingTestId)).toBeInTheDocument(); + + await waitForElementToBeRemoved(getByTestId(loadingTestId)); + const createClusterButtonSpan = await findByText('Create Database Cluster'); + const createClusterButton = createClusterButtonSpan.closest('button'); + + expect(createClusterButton).toBeInTheDocument(); + expect(createClusterButton).toBeDisabled(); + }); + + it('should disable form inputs for restricted users', async () => { + queryMocks.useProfile.mockReturnValue({ data: { restricted: true } }); + + const { + findAllByRole, + findAllByTestId, + findByPlaceholderText, + getByTestId, + } = renderWithTheme(); + + expect(getByTestId(loadingTestId)).toBeInTheDocument(); + + await waitForElementToBeRemoved(getByTestId(loadingTestId)); + const textInputs = await findAllByTestId('textfield-input'); + textInputs.forEach((input: HTMLInputElement) => { + expect(input).toBeDisabled(); + }); + + const dbEngineSelect = await findByPlaceholderText( + 'Select a Database Engine' + ); + expect(dbEngineSelect).toBeDisabled(); + const regionSelect = await findByPlaceholderText('Select a Region'); + expect(regionSelect).toBeDisabled(); + + const radioButtons = await findAllByRole('radio'); + radioButtons.forEach((radioButton: HTMLElement) => { + expect(radioButton).toBeDisabled(); + }); + }); + + it('should have the "Create Database Cluster" button enabled for users with full access', async () => { + queryMocks.useProfile.mockReturnValue({ data: { restricted: false } }); + + const { findByText, getByTestId } = renderWithTheme(); + + expect(getByTestId(loadingTestId)).toBeInTheDocument(); + + await waitForElementToBeRemoved(getByTestId(loadingTestId)); + const createClusterButtonSpan = await findByText('Create Database Cluster'); + const createClusterButton = createClusterButtonSpan.closest('button'); + + expect(createClusterButton).toBeInTheDocument(); + expect(createClusterButton).toBeEnabled(); + }); + + it('should enable form inputs for users with full access', async () => { + queryMocks.useProfile.mockReturnValue({ data: { restricted: false } }); + + const { + findAllByRole, + findAllByTestId, + findByPlaceholderText, + getByTestId, + } = renderWithTheme(); + + expect(getByTestId(loadingTestId)).toBeInTheDocument(); + + await waitForElementToBeRemoved(getByTestId(loadingTestId)); + const textInputs = await findAllByTestId('textfield-input'); + textInputs.forEach((input: HTMLInputElement) => { + expect(input).toBeEnabled(); + }); + + const dbEngineSelect = await findByPlaceholderText( + 'Select a Database Engine' + ); + expect(dbEngineSelect).toBeEnabled(); + const regionSelect = await findByPlaceholderText('Select a Region'); + expect(regionSelect).toBeEnabled(); + + const radioButtons = await findAllByRole('radio'); + radioButtons.forEach((radioButton: HTMLElement) => { + expect(radioButton).toBeEnabled(); + }); + }); }); diff --git a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx index cb092f26b0e..5ec755d8d25 100644 --- a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx +++ b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx @@ -29,6 +29,7 @@ import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; import { RegionHelperText } from 'src/components/SelectRegionPanel/RegionHelperText'; import { TextField } from 'src/components/TextField'; import { Typography } from 'src/components/Typography'; +import { getRestrictedResourceText } from 'src/features/Account/utils'; import { PlansPanel } from 'src/features/components/PlansPanel/PlansPanel'; import { EngineOption } from 'src/features/Databases/DatabaseCreate/EngineOption'; import { DatabaseLogo } from 'src/features/Databases/DatabaseLanding/DatabaseLogo'; @@ -36,6 +37,7 @@ import { databaseEngineMap } from 'src/features/Databases/DatabaseLanding/Databa import { useIsDatabasesEnabled } from 'src/features/Databases/utilities'; import { enforceIPMasks } from 'src/features/Firewalls/FirewallDetail/Rules/FirewallRuleDrawer.utils'; import { typeLabelDetails } from 'src/features/Linodes/presentation'; +import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck'; import { useCreateDatabaseMutation, useDatabaseEnginesQuery, @@ -48,6 +50,8 @@ import { getSelectedOptionFromGroupedOptions } from 'src/utilities/getSelectedOp import { validateIPs } from 'src/utilities/ipUtils'; import { scrollErrorIntoViewV2 } from 'src/utilities/scrollErrorIntoViewV2'; +import { DatabaseCreateAccessControls } from './DatabaseCreateAccessControls'; + import type { ClusterSize, ComprehensiveReplicationType, @@ -62,7 +66,6 @@ import type { Theme } from '@mui/material/styles'; import type { Item } from 'src/components/EnhancedSelect/Select'; import type { PlanSelectionType } from 'src/features/components/PlansPanel/types'; import type { ExtendedIP } from 'src/utilities/ipUtils'; -import { DatabaseCreateAccessControls } from './DatabaseCreateAccessControls'; const useStyles = makeStyles()((theme: Theme) => ({ btnCtn: { @@ -197,6 +200,9 @@ const DatabaseCreate = () => { const { classes } = useStyles(); const history = useHistory(); const { isDatabasesV2Beta, isDatabasesV2Enabled } = useIsDatabasesEnabled(); + const isRestricted = useRestrictedGlobalGrantCheck({ + globalGrantType: 'add_databases', + }); const { data: regionsData, @@ -510,6 +516,17 @@ const DatabaseCreate = () => { }} title="Create" /> + {isRestricted && ( + + )} {createError && ( @@ -523,6 +540,7 @@ const DatabaseCreate = () => { Name Your Cluster setFieldValue('label', e.target.value)} @@ -544,6 +562,7 @@ const DatabaseCreate = () => { )} className={classes.engineSelect} components={{ Option: EngineOption, SingleValue: _SingleValue }} + disabled={isRestricted} errorText={errors.engine} isClearable={false} label="Database Engine" @@ -555,6 +574,7 @@ const DatabaseCreate = () => { setFieldValue('region', region.id)} regions={regionsData} @@ -570,6 +590,7 @@ const DatabaseCreate = () => { }} className={classes.selectPlanPanel} data-qa-select-plan + disabled={isRestricted} error={errors.type} handleTabChange={handleTabChange} header="Choose a Plan" @@ -599,11 +620,13 @@ const DatabaseCreate = () => { ); }} data-testid="database-nodes" + disabled={isRestricted} > {errors.cluster_size ? ( ) : null} @@ -622,6 +645,7 @@ const DatabaseCreate = () => { {
+
@@ -475,8 +487,7 @@ export const BillingActivityPanel = React.memo((props: Props) => { const StyledBillingAndPaymentHistoryHeader = styled('div', { name: 'BillingAndPaymentHistoryHeader', -})(({ theme }) => ({ - border: theme.name === 'dark' ? `1px solid ${theme.borderColors.divider}` : 0, +})(() => ({ borderBottom: 0, padding: `15px 0px 15px 20px`, })); @@ -488,12 +499,14 @@ interface ActivityFeedItemProps extends ActivityFeedItem { downloadPDF: (id: number) => void; hasError: boolean; isLoading: boolean; + sxRow: SxProps; } export const ActivityFeedItem = React.memo((props: ActivityFeedItemProps) => { const { classes } = useStyles(); const { + sxRow, date, downloadPDF, hasError, @@ -520,7 +533,7 @@ export const ActivityFeedItem = React.memo((props: ActivityFeedItemProps) => { }; return ( - + {type === 'invoice' ? ( {label} From 9c8cd3eaa2393e99d6a44af31b489528db7d9bd0 Mon Sep 17 00:00:00 2001 From: Banks Nussman <115251059+bnussman-akamai@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:17:16 -0400 Subject: [PATCH 13/66] refactor: [M3-8770] - Clean up Profile Display Settings page (#11141) * initial refactor * clean up more * finish clean up * small clean up * small clean up * small clean up * handle case where user does not have a timezone * add changeset and improve commit * add changeset --------- Co-authored-by: Banks Nussman --- .../pr-11141-added-1730133012318.md | 5 + .../pr-11141-tech-stories-1729629238072.md | 5 + .../SingleTextFieldForm.test.tsx | 53 ------ .../SingleTextFieldForm.tsx | 134 --------------- .../DisplaySettings/AvatarForm.test.tsx | 31 ++++ .../Profile/DisplaySettings/AvatarForm.tsx | 55 ++++++ .../DisplaySettings/DisplaySettings.test.tsx | 69 +------- .../DisplaySettings/DisplaySettings.tsx | 157 ++---------------- .../DisplaySettings/EmailForm.test.tsx | 64 +++++++ .../Profile/DisplaySettings/EmailForm.tsx | 97 +++++++++++ .../DisplaySettings/TimezoneForm.test.tsx | 76 ++++----- .../Profile/DisplaySettings/TimezoneForm.tsx | 97 ++++++----- .../DisplaySettings/UsernameForm.test.tsx | 83 +++++++++ .../Profile/DisplaySettings/UsernameForm.tsx | 90 ++++++++++ .../manager/src/queries/profile/profile.ts | 10 +- 15 files changed, 553 insertions(+), 473 deletions(-) create mode 100644 packages/manager/.changeset/pr-11141-added-1730133012318.md create mode 100644 packages/manager/.changeset/pr-11141-tech-stories-1729629238072.md delete mode 100644 packages/manager/src/components/SingleTextFieldForm/SingleTextFieldForm.test.tsx delete mode 100644 packages/manager/src/components/SingleTextFieldForm/SingleTextFieldForm.tsx create mode 100644 packages/manager/src/features/Profile/DisplaySettings/AvatarForm.test.tsx create mode 100644 packages/manager/src/features/Profile/DisplaySettings/AvatarForm.tsx create mode 100644 packages/manager/src/features/Profile/DisplaySettings/EmailForm.test.tsx create mode 100644 packages/manager/src/features/Profile/DisplaySettings/EmailForm.tsx create mode 100644 packages/manager/src/features/Profile/DisplaySettings/UsernameForm.test.tsx create mode 100644 packages/manager/src/features/Profile/DisplaySettings/UsernameForm.tsx diff --git a/packages/manager/.changeset/pr-11141-added-1730133012318.md b/packages/manager/.changeset/pr-11141-added-1730133012318.md new file mode 100644 index 00000000000..9bb2a5137ae --- /dev/null +++ b/packages/manager/.changeset/pr-11141-added-1730133012318.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Added +--- + +Success toasts to profile display settings page (with other minor improvements) ([#11141](https://github.com/linode/manager/pull/11141)) diff --git a/packages/manager/.changeset/pr-11141-tech-stories-1729629238072.md b/packages/manager/.changeset/pr-11141-tech-stories-1729629238072.md new file mode 100644 index 00000000000..28376d598a9 --- /dev/null +++ b/packages/manager/.changeset/pr-11141-tech-stories-1729629238072.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tech Stories +--- + +Clean up Profile Display Settings page ([#11141](https://github.com/linode/manager/pull/11141)) diff --git a/packages/manager/src/components/SingleTextFieldForm/SingleTextFieldForm.test.tsx b/packages/manager/src/components/SingleTextFieldForm/SingleTextFieldForm.test.tsx deleted file mode 100644 index 4a80c6e8d67..00000000000 --- a/packages/manager/src/components/SingleTextFieldForm/SingleTextFieldForm.test.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { fireEvent, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import * as React from 'react'; - -import { renderWithTheme } from 'src/utilities/testHelpers'; - -import { SingleTextFieldForm } from './SingleTextFieldForm'; - -describe('SingleTextFieldForm', () => { - const props = { - initialValue: 'jane-doe', - label: 'Username', - submitForm: vi.fn(() => Promise.resolve()), - }; - - it('Renders a TextField with the given label and initial value', () => { - renderWithTheme(); - expect(screen.getByText('Username')).toBeInTheDocument(); - expect(screen.getByDisplayValue('jane-doe')).toBeInTheDocument(); - }); - - it('Renders a success message on success', async () => { - renderWithTheme(); - await userEvent.type(screen.getByTestId('textfield-input'), 'new-value'); - await userEvent.click(screen.getByText('Update Username')); - await waitFor(() => - expect(screen.getByText(/username updated/i)).toBeInTheDocument() - ); - }); - - it('Renders an error message on error', async () => { - renderWithTheme( - - Promise.reject([{ reason: 'Error updating username.' }]) - )} - /> - ); - await userEvent.type(screen.getByTestId('textfield-input'), 'new-value'); - await userEvent.click(screen.getByText('Update Username')); - await waitFor(() => - expect(screen.getByText(/error updating/i)).toBeInTheDocument() - ); - }); - - it('Calls submitForm function with new value', () => { - renderWithTheme(); - const input = screen.getByTestId('textfield-input'); - fireEvent.change(input, { target: { value: 'my-new-username' } }); - expect(screen.getByDisplayValue('my-new-username')).toBeInTheDocument(); - }); -}); diff --git a/packages/manager/src/components/SingleTextFieldForm/SingleTextFieldForm.tsx b/packages/manager/src/components/SingleTextFieldForm/SingleTextFieldForm.tsx deleted file mode 100644 index 76d40aa6096..00000000000 --- a/packages/manager/src/components/SingleTextFieldForm/SingleTextFieldForm.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import { APIError } from '@linode/api-v4/lib/types'; -import * as React from 'react'; - -import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; -import { Box } from 'src/components/Box'; -import { Notice } from 'src/components/Notice/Notice'; -import { TextField, TextFieldProps } from 'src/components/TextField'; -import { getAPIErrorOrDefault, getErrorMap } from 'src/utilities/errorUtils'; - -interface Props extends TextFieldProps { - errorMessage?: string; - fieldName?: string; - initialValue?: string; - submitForm: (value: string) => Promise; - successCallback?: () => void; - successMessage?: string; -} - -export const SingleTextFieldForm = React.memo((props: Props) => { - const { - disabled, - errorMessage, - fieldName, - initialValue, - label, - submitForm, - successCallback, - successMessage, - tooltipText, - trimmed, - ...textFieldProps - } = props; - - const _fieldName = fieldName ?? label.toLowerCase(); - const _successMessage = successMessage ?? `${label} updated successfully.`; - const _errorMessage = errorMessage ?? `Error updating ${label}.`; - - const [value, setValue] = React.useState(initialValue ?? ''); - const [submitting, setSubmitting] = React.useState(false); - const [errors, setErrors] = React.useState(); - const [success, setSuccess] = React.useState(false); - - const handleSubmit = () => { - setSubmitting(true); - setSuccess(false); - setErrors(undefined); - - submitForm(value) - .then(() => { - setSubmitting(false); - setSuccess(true); - if (successCallback) { - successCallback(); - } - }) - .catch((err) => { - setSubmitting(false); - setErrors(getAPIErrorOrDefault(err, _errorMessage)); - }); - }; - - const errorMap = getErrorMap([_fieldName], errors); - const fieldError = errorMap[_fieldName]; - const generalError = errorMap.none; - - return ( - <> - ({ - [theme.breakpoints.down('md')]: { - alignItems: 'flex-start', - flexDirection: 'column', - }, - })} - alignItems="flex-end" - display="flex" - justifyContent="space-between" - > - setValue(e.target.value)} - onChange={(e) => setValue(e.target.value)} - trimmed={trimmed} - value={value} - /> - - - {success ? ( - - ) : null} - {generalError ? ( - - ) : null} - - ); -}); diff --git a/packages/manager/src/features/Profile/DisplaySettings/AvatarForm.test.tsx b/packages/manager/src/features/Profile/DisplaySettings/AvatarForm.test.tsx new file mode 100644 index 00000000000..7e121bf12c9 --- /dev/null +++ b/packages/manager/src/features/Profile/DisplaySettings/AvatarForm.test.tsx @@ -0,0 +1,31 @@ +import userEvent from '@testing-library/user-event'; +import React from 'react'; + +import { renderWithTheme } from 'src/utilities/testHelpers'; + +import { AvatarForm } from './AvatarForm'; + +describe('AvatarForm', () => { + it('renders a label, button, and avatar', () => { + const { getByRole, getByTestId, getByText } = renderWithTheme( + + ); + + expect(getByText('Avatar')).toBeVisible(); + expect(getByTestId('avatar')).toBeVisible(); + expect(getByRole('button', { name: 'Change Avatar Color' })).toBeVisible(); + }); + + it('opens the avatar color dialog when the button is clicked', async () => { + const { getByRole } = renderWithTheme(); + + const button = getByRole('button', { name: 'Change Avatar Color' }); + + expect(button).toBeVisible(); + expect(button).toBeEnabled(); + + await userEvent.click(button); + + expect(getByRole('heading', { name: 'Change Avatar Color' })).toBeVisible(); + }); +}); diff --git a/packages/manager/src/features/Profile/DisplaySettings/AvatarForm.tsx b/packages/manager/src/features/Profile/DisplaySettings/AvatarForm.tsx new file mode 100644 index 00000000000..f6ad7df75b0 --- /dev/null +++ b/packages/manager/src/features/Profile/DisplaySettings/AvatarForm.tsx @@ -0,0 +1,55 @@ +import { styled } from '@mui/material/styles'; +import React from 'react'; + +import { Avatar } from 'src/components/Avatar/Avatar'; +import { Box } from 'src/components/Box'; +import { Button } from 'src/components/Button/Button'; +import { Typography } from 'src/components/Typography'; + +import { AvatarColorPickerDialog } from './AvatarColorPickerDialog'; + +export const AvatarForm = () => { + const [ + isColorPickerDialogOpen, + setAvatarColorPickerDialogOpen, + ] = React.useState(false); + + return ( + ({ + display: 'flex', + gap: 2, + marginTop: theme.spacing(), + })} + > + +
+ + Avatar + + + Your avatar is automatically generated using the first character of + your username. + + +
+ setAvatarColorPickerDialogOpen(false)} + open={isColorPickerDialogOpen} + /> +
+ ); +}; + +const StyledProfileCopy = styled(Typography, { + label: 'StyledProfileCopy', +})(({ theme }) => ({ + marginBottom: theme.spacing(2), + marginTop: 4, + maxWidth: 360, +})); diff --git a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.test.tsx b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.test.tsx index 2008bf9c339..ac8207f46ef 100644 --- a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.test.tsx +++ b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.test.tsx @@ -1,70 +1,15 @@ -import { screen } from '@testing-library/react'; import React from 'react'; -import { profileFactory } from 'src/factories/profile'; import { DisplaySettings } from 'src/features/Profile/DisplaySettings/DisplaySettings'; import { renderWithTheme } from 'src/utilities/testHelpers'; -const queryMocks = vi.hoisted(() => ({ - useProfile: vi.fn().mockReturnValue({}), -})); +describe('DisplaySettings', () => { + it('renders profile display sections', () => { + const { getByText } = renderWithTheme(); -vi.mock('src/queries/profile/profile', async () => { - const actual = await vi.importActual('src/queries/profile/profile'); - return { - ...actual, - useProfile: queryMocks.useProfile, - }; -}); - -describe('DisplaySettings component', () => { - it('should disable SingleTextFieldForm components based on isProxyUser', () => { - queryMocks.useProfile.mockReturnValue({ - data: profileFactory.build({ user_type: 'proxy' }), - }); - - renderWithTheme(); - - const usernameInput = screen.getByLabelText('Username'); - expect(usernameInput).toHaveAttribute('disabled'); - - const emailInput = screen.getByLabelText('Email'); - expect(emailInput).toHaveAttribute('disabled'); - }); - - it('Should only disable the SingleTextFieldForm components if the user is not a proxy user.', () => { - queryMocks.useProfile.mockReturnValue({ - data: profileFactory.build({ user_type: 'child' }), - }); - - renderWithTheme(); - - const usernameInput = screen.getByLabelText('Username'); - expect(usernameInput).not.toHaveAttribute('disabled'); - - const emailInput = screen.getByLabelText('Email'); - expect(emailInput).not.toHaveAttribute('disabled'); - }); - - it('should disable the "Update Username" and "Update Email" buttons if the user is a proxy user.', async () => { - queryMocks.useProfile.mockReturnValue({ - data: profileFactory.build({ user_type: 'proxy' }), - }); - - renderWithTheme(); - - const updateUsernameButton = screen - .getByText('Update Username') - .closest('button'); - - const updateEmailButton = screen - .getByText('Update Email') - .closest('button'); - - expect(updateUsernameButton).not.toHaveAttribute('disabled'); - expect(updateUsernameButton).toHaveAttribute('aria-disabled', 'true'); - - expect(updateEmailButton).not.toHaveAttribute('disabled'); - expect(updateEmailButton).toHaveAttribute('aria-disabled', 'true'); + expect(getByText('Avatar')).toBeVisible(); + expect(getByText('Username')).toBeVisible(); + expect(getByText('Email')).toBeVisible(); + expect(getByText('Timezone')).toBeVisible(); }); }); diff --git a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx index 5bd869cabef..e15f1448a6b 100644 --- a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx +++ b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx @@ -1,164 +1,33 @@ -import { updateUser } from '@linode/api-v4/lib/account'; -import { styled, useTheme } from '@mui/material/styles'; import { createLazyRoute } from '@tanstack/react-router'; -import * as React from 'react'; -import { useSelector } from 'react-redux'; -import { useLocation } from 'react-router-dom'; -import { v4 } from 'uuid'; +import React from 'react'; -import { Avatar } from 'src/components/Avatar/Avatar'; -import { Box } from 'src/components/Box'; -import { Button } from 'src/components/Button/Button'; import { Divider } from 'src/components/Divider'; import { Paper } from 'src/components/Paper'; -import { SingleTextFieldForm } from 'src/components/SingleTextFieldForm/SingleTextFieldForm'; -import { Typography } from 'src/components/Typography'; -import { RESTRICTED_FIELD_TOOLTIP } from 'src/features/Account/constants'; -import { useNotificationsQuery } from 'src/queries/account/notifications'; -import { useMutateProfile, useProfile } from 'src/queries/profile/profile'; +import { Stack } from 'src/components/Stack'; +import { useProfile } from 'src/queries/profile/profile'; -import { AvatarColorPickerDialog } from './AvatarColorPickerDialog'; +import { AvatarForm } from './AvatarForm'; +import { EmailForm } from './EmailForm'; import { TimezoneForm } from './TimezoneForm'; - -import type { ApplicationState } from 'src/store'; +import { UsernameForm } from './UsernameForm'; export const DisplaySettings = () => { - const theme = useTheme(); - const { mutateAsync: updateProfile } = useMutateProfile(); - const { data: profile, refetch: requestProfile } = useProfile(); - const { data: notifications, refetch } = useNotificationsQuery(); - const loggedInAsCustomer = useSelector( - (state: ApplicationState) => state.authentication.loggedInAsCustomer - ); - const location = useLocation<{ focusEmail: boolean }>(); - const emailRef = React.createRef(); + const { data: profile } = useProfile(); const isProxyUser = profile?.user_type === 'proxy'; - const [ - isColorPickerDialogOpen, - setAvatarColorPickerDialogOpen, - ] = React.useState(false); - - React.useEffect(() => { - if (location.state?.focusEmail && emailRef.current) { - emailRef.current.focus(); - emailRef.current.scrollIntoView(); - } - }, [emailRef, location.state]); - - // Used as React keys to force-rerender forms. - const [emailResetToken, setEmailResetToken] = React.useState(v4()); - const [usernameResetToken, setUsernameResetToken] = React.useState(v4()); - - const updateUsername = (newUsername: string) => { - setEmailResetToken(v4()); - // Default to empty string... but I don't believe this is possible. - return updateUser(profile?.username ?? '', { - username: newUsername, - }); - }; - - const updateEmail = (newEmail: string) => { - setUsernameResetToken(v4()); - return updateProfile({ email: newEmail }); - }; - - const tooltipForDisabledUsernameField = profile?.restricted - ? 'Restricted users cannot update their username. Please contact an account administrator.' - : isProxyUser - ? RESTRICTED_FIELD_TOOLTIP - : undefined; - - const tooltipForDisabledEmailField = isProxyUser - ? RESTRICTED_FIELD_TOOLTIP - : undefined; - return ( - {!isProxyUser && ( - <> - - -
- - Avatar - - - Your avatar is automatically generated using the first character - of your username. - - - -
-
- - - )} - - - - { - // If there's a "user_email_bounce" notification for this user, and - // the user has just updated their email, re-request notifications to - // potentially clear the email bounce notification. - const hasUserEmailBounceNotification = notifications?.find( - (thisNotification) => thisNotification.type === 'user_email_bounce' - ); - if (hasUserEmailBounceNotification) { - refetch(); - } - }} - disabled={isProxyUser} - initialValue={profile?.email} - inputRef={emailRef} - key={emailResetToken} - label="Email" - submitForm={updateEmail} - tooltipText={tooltipForDisabledEmailField} - trimmed - type="email" - /> - - - setAvatarColorPickerDialogOpen(false)} - open={isColorPickerDialogOpen} - /> + } spacing={3}> + {!isProxyUser && } + + + +
); }; -const StyledProfileCopy = styled(Typography, { - label: 'StyledProfileCopy', -})(({ theme }) => ({ - marginBottom: theme.spacing(2), - marginTop: 4, - maxWidth: 360, -})); - export const displaySettingsLazyRoute = createLazyRoute('/profile/display')({ component: DisplaySettings, }); diff --git a/packages/manager/src/features/Profile/DisplaySettings/EmailForm.test.tsx b/packages/manager/src/features/Profile/DisplaySettings/EmailForm.test.tsx new file mode 100644 index 00000000000..f381a503950 --- /dev/null +++ b/packages/manager/src/features/Profile/DisplaySettings/EmailForm.test.tsx @@ -0,0 +1,64 @@ +import { waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; + +import { profileFactory } from 'src/factories'; +import { HttpResponse, http, server } from 'src/mocks/testServer'; +import { renderWithTheme } from 'src/utilities/testHelpers'; + +import { EmailForm } from './EmailForm'; + +describe('EmailForm', () => { + it('renders a label and input', () => { + const { getByLabelText, getByText } = renderWithTheme(); + + expect(getByLabelText('Email')).toBeVisible(); + expect(getByText('Update Email')).toBeVisible(); + }); + + it("populates the form with the current user's email", async () => { + const profile = profileFactory.build({ username: 'myself@linode.com' }); + + server.use(http.get('*/v4/profile', () => HttpResponse.json(profile))); + + const { findByDisplayValue } = renderWithTheme(); + + await findByDisplayValue(profile.email); + }); + + it('disables the input if the user is a proxy user', async () => { + const profile = profileFactory.build({ + user_type: 'proxy', + }); + + server.use(http.get('*/v4/profile', () => HttpResponse.json(profile))); + + const { getByLabelText } = renderWithTheme(); + + await waitFor(() => { + expect(getByLabelText('Email')).toBeDisabled(); + }); + + expect(getByLabelText('This field can’t be modified.')).toBeVisible(); + }); + + it('enables the save button when the user makes a change to the email', async () => { + const profile = profileFactory.build({ email: 'user@linode.com' }); + + server.use(http.get('*/v4/profile', () => HttpResponse.json(profile))); + + const { findByDisplayValue, getByLabelText, getByText } = renderWithTheme( + + ); + + await findByDisplayValue(profile.email); + + const saveButton = getByText('Update Email').closest('button'); + + expect(saveButton).toBeDisabled(); + + await userEvent.type(getByLabelText('Email'), 'user-1@linode.com'); + + expect(saveButton).toBeEnabled(); + }); +}); diff --git a/packages/manager/src/features/Profile/DisplaySettings/EmailForm.tsx b/packages/manager/src/features/Profile/DisplaySettings/EmailForm.tsx new file mode 100644 index 00000000000..039c2c896a0 --- /dev/null +++ b/packages/manager/src/features/Profile/DisplaySettings/EmailForm.tsx @@ -0,0 +1,97 @@ +import { useSnackbar } from 'notistack'; +import React from 'react'; +import { Controller, useForm } from 'react-hook-form'; +import { useLocation } from 'react-router-dom'; + +import { Button } from 'src/components/Button/Button'; +import { TextField } from 'src/components/TextField'; +import { RESTRICTED_FIELD_TOOLTIP } from 'src/features/Account/constants'; +import { useMutateProfile, useProfile } from 'src/queries/profile/profile'; + +import { SingleTextFieldFormContainer } from './TimezoneForm'; + +import type { Profile } from '@linode/api-v4'; + +type Values = Pick; + +export const EmailForm = () => { + const { data: profile } = useProfile(); + const { mutateAsync: updateProfile } = useMutateProfile(); + const { enqueueSnackbar } = useSnackbar(); + + const location = useLocation<{ focusEmail: boolean }>(); + const emailRef = React.createRef(); + + React.useEffect(() => { + if (location.state?.focusEmail && emailRef.current) { + emailRef.current.focus(); + emailRef.current.scrollIntoView(); + } + }, [emailRef, location.state]); + + const values = { email: profile?.email ?? '' }; + + const { + control, + formState: { isDirty, isSubmitting }, + handleSubmit, + setError, + } = useForm({ + defaultValues: values, + values, + }); + + const tooltipForDisabledEmailField = + profile?.user_type === 'proxy' ? RESTRICTED_FIELD_TOOLTIP : undefined; + + const onSubmit = async (values: Values) => { + try { + await updateProfile(values); + enqueueSnackbar({ + message: 'Email updated successfully.', + variant: 'success', + }); + } catch (error) { + setError('email', { message: error[0].reason }); + } + }; + + return ( +
+ + ( + + )} + control={control} + name="email" + /> + + + + ); +}; diff --git a/packages/manager/src/features/Profile/DisplaySettings/TimezoneForm.test.tsx b/packages/manager/src/features/Profile/DisplaySettings/TimezoneForm.test.tsx index eff34420a91..2093cd8887e 100644 --- a/packages/manager/src/features/Profile/DisplaySettings/TimezoneForm.test.tsx +++ b/packages/manager/src/features/Profile/DisplaySettings/TimezoneForm.test.tsx @@ -1,63 +1,65 @@ -import { waitForElementToBeRemoved } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; import * as React from 'react'; import { profileFactory } from 'src/factories'; import { HttpResponse, http, server } from 'src/mocks/testServer'; -import { queryClientFactory } from 'src/queries/base'; import { renderWithTheme } from 'src/utilities/testHelpers'; import { TimezoneForm, getOptionLabel } from './TimezoneForm'; -const queryClient = queryClientFactory(); - describe('Timezone change form', () => { - // Use the MSW to mock a profile with America/New_York as the timezone - // for this specific suite of tests - server.use( - http.get('*/profile', () => { - return HttpResponse.json( - profileFactory.build({ timezone: 'America/New_York' }) - ); - }) - ); - - it('should render input label', async () => { - const { getByTestId, getByText } = renderWithTheme( - , - { queryClient } + beforeEach(() => { + // Use the MSW to mock a profile with America/New_York as the timezone + // for this specific suite of tests + server.use( + http.get('*/profile', () => { + return HttpResponse.json( + profileFactory.build({ timezone: 'America/New_York' }) + ); + }) ); + }); - // This component depends on the /profile to be loaded. Wait for - // loading to finish before we check anything. - await waitForElementToBeRemoved(getByTestId('circle-progress')); + it('should render input label', () => { + const { getByText } = renderWithTheme(); expect(getByText('Timezone')).toBeInTheDocument(); }); - it('should show a message if an admin is logged in as a customer', () => { - const { getByTestId } = renderWithTheme( - , - { queryClient } - ); + it('should show a message if an admin is logged in as a customer', async () => { + const { getByTestId } = renderWithTheme(, { + customStore: { authentication: { loggedInAsCustomer: true } }, + }); expect(getByTestId('admin-notice')).toBeInTheDocument(); }); - it('should not show a message if the user is logged in normally', () => { - const { queryByTestId } = renderWithTheme( - , - { queryClient } - ); + it('should not show a message if the user is logged in normally', async () => { + const { queryByTestId } = renderWithTheme(); expect(queryByTestId('admin-notice')).not.toBeInTheDocument(); }); - it("should include text with the user's current time zone", async () => { - const { queryByTestId } = renderWithTheme( - , - { queryClient } - ); - expect(queryByTestId('admin-notice')).toHaveTextContent('America/New_York'); + it("should include text with the user's current time zone in the admin warning", async () => { + const { queryByTestId } = renderWithTheme(, { + customStore: { authentication: { loggedInAsCustomer: true } }, + }); + + await waitFor(() => { + expect(queryByTestId('admin-notice')).toHaveTextContent( + 'America/New_York' + ); + }); + }); + + it("should show the user's currently selected timezone", async () => { + const { getByLabelText } = renderWithTheme(); + + await waitFor(() => { + expect(getByLabelText('Timezone')).toHaveDisplayValue( + /Eastern Time - New York/ + ); + }); }); }); diff --git a/packages/manager/src/features/Profile/DisplaySettings/TimezoneForm.tsx b/packages/manager/src/features/Profile/DisplaySettings/TimezoneForm.tsx index 7fe803e8c18..43c8e8053ea 100644 --- a/packages/manager/src/features/Profile/DisplaySettings/TimezoneForm.tsx +++ b/packages/manager/src/features/Profile/DisplaySettings/TimezoneForm.tsx @@ -2,17 +2,16 @@ import { styled } from '@mui/material/styles'; import { DateTime } from 'luxon'; import { useSnackbar } from 'notistack'; import * as React from 'react'; +import { Controller, useForm } from 'react-hook-form'; import { timezones } from 'src/assets/timezones/timezones'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { Button } from 'src/components/Button/Button'; -import { CircleProgress } from 'src/components/CircleProgress'; import { Notice } from 'src/components/Notice/Notice'; +import { useAuthentication } from 'src/hooks/useAuthentication'; import { useMutateProfile, useProfile } from 'src/queries/profile/profile'; -interface Props { - loggedInAsCustomer: boolean; -} +import type { Profile } from '@linode/api-v4'; type Timezone = typeof timezones[number]; @@ -40,68 +39,82 @@ const getTimezoneOptions = () => { const timezoneOptions = getTimezoneOptions(); -export const TimezoneForm = (props: Props) => { - const { loggedInAsCustomer } = props; +type Values = Pick; + +export const TimezoneForm = () => { + const { loggedInAsCustomer } = useAuthentication(); const { enqueueSnackbar } = useSnackbar(); const { data: profile } = useProfile(); - const { error, isPending, mutateAsync: updateProfile } = useMutateProfile(); + const { mutateAsync: updateProfile } = useMutateProfile(); - const [timezoneValue, setTimezoneValue] = React.useState(profile?.timezone); + const values = { timezone: profile?.timezone ?? '' }; - const onSubmit = () => { - if (!timezoneValue) { - enqueueSnackbar('Please select a valid timezone.', { variant: 'error' }); - } + const { + control, + formState: { isDirty, isSubmitting }, + handleSubmit, + setError, + } = useForm({ + defaultValues: values, + values, + }); - updateProfile({ timezone: timezoneValue }).then(() => { - enqueueSnackbar('Successfully updated timezone', { variant: 'success' }); - }); + const onSubmit = async (values: Values) => { + try { + await updateProfile(values); + enqueueSnackbar('Successfully updated timezone.', { variant: 'success' }); + } catch (error) { + setError('timezone', { message: error[0].reason }); + } }; - const disabled = !timezoneValue || profile?.timezone === timezoneValue; - - if (!profile) { - return ; - } - return ( - <> +
{loggedInAsCustomer && ( While you are logged in as a customer, all times, dates, and graphs - will be displayed in the user’s timezone ({profile.timezone}). + will be displayed in the user’s timezone ({profile?.timezone}). )} - - option.value === timezoneValue + + ( + option.value === field.value + ) ?? null + } + autoHighlight + disableClearable={profile?.timezone !== undefined} + errorText={fieldState.error?.message} + fullWidth + label="Timezone" + noMarginTop + onChange={(e, option) => field.onChange(option?.value ?? '')} + options={timezoneOptions} + placeholder="Choose a Timezone" + /> )} - autoHighlight - disableClearable - errorText={error?.[0].reason} - fullWidth - label="Timezone" - onChange={(e, option) => setTimezoneValue(option.value)} - options={timezoneOptions} - placeholder="Choose a Timezone" + control={control} + name="timezone" /> - - + + ); }; -const TimezoneFormContainer = styled('div', { - label: 'TimezoneFormContainer', +export const SingleTextFieldFormContainer = styled('div', { + label: 'SingleTextFieldFormContainer', })(({ theme }) => ({ alignItems: 'flex-end', display: 'flex', diff --git a/packages/manager/src/features/Profile/DisplaySettings/UsernameForm.test.tsx b/packages/manager/src/features/Profile/DisplaySettings/UsernameForm.test.tsx new file mode 100644 index 00000000000..d0cd4bd5a8f --- /dev/null +++ b/packages/manager/src/features/Profile/DisplaySettings/UsernameForm.test.tsx @@ -0,0 +1,83 @@ +import { waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; + +import { profileFactory } from 'src/factories'; +import { HttpResponse, http, server } from 'src/mocks/testServer'; +import { renderWithTheme } from 'src/utilities/testHelpers'; + +import { UsernameForm } from './UsernameForm'; + +describe('UsernameForm', () => { + it('renders a label and input', () => { + const { getByLabelText, getByText } = renderWithTheme(); + + expect(getByLabelText('Username')).toBeVisible(); + expect(getByText('Update Username')).toBeVisible(); + }); + + it("populates the form with the current user's username", async () => { + const profile = profileFactory.build({ username: 'my-linode-username' }); + + server.use(http.get('*/v4/profile', () => HttpResponse.json(profile))); + + const { findByDisplayValue } = renderWithTheme(); + + await findByDisplayValue(profile.username); + }); + + it('disables the input if the user is restricted', async () => { + const profile = profileFactory.build({ restricted: true }); + + server.use(http.get('*/v4/profile', () => HttpResponse.json(profile))); + + const { getByLabelText } = renderWithTheme(); + + await waitFor(() => { + expect(getByLabelText('Username')).toBeDisabled(); + }); + + expect( + getByLabelText( + 'Restricted users cannot update their username. Please contact an account administrator.' + ) + ).toBeVisible(); + }); + + it('disables the input if the user is a proxy user', async () => { + const profile = profileFactory.build({ + restricted: false, + user_type: 'proxy', + }); + + server.use(http.get('*/v4/profile', () => HttpResponse.json(profile))); + + const { getByLabelText } = renderWithTheme(); + + await waitFor(() => { + expect(getByLabelText('Username')).toBeDisabled(); + }); + + expect(getByLabelText('This field can’t be modified.')).toBeVisible(); + }); + + it('enables the save button when the user makes a change to the username', async () => { + const profile = profileFactory.build({ username: 'my-linode-username' }); + + server.use(http.get('*/v4/profile', () => HttpResponse.json(profile))); + + const { findByDisplayValue, getByLabelText, getByText } = renderWithTheme( + + ); + + await findByDisplayValue(profile.username); + + const saveButton = getByText('Update Username').closest('button'); + + expect(saveButton).toBeDisabled(); + + await userEvent.type(getByLabelText('Username'), 'my-linode-username-1'); + + expect(saveButton).toBeEnabled(); + }); +}); diff --git a/packages/manager/src/features/Profile/DisplaySettings/UsernameForm.tsx b/packages/manager/src/features/Profile/DisplaySettings/UsernameForm.tsx new file mode 100644 index 00000000000..0fef5ea1adc --- /dev/null +++ b/packages/manager/src/features/Profile/DisplaySettings/UsernameForm.tsx @@ -0,0 +1,90 @@ +import { useSnackbar } from 'notistack'; +import React from 'react'; +import { Controller, useForm } from 'react-hook-form'; + +import { Button } from 'src/components/Button/Button'; +import { TextField } from 'src/components/TextField'; +import { RESTRICTED_FIELD_TOOLTIP } from 'src/features/Account/constants'; +import { useUpdateUserMutation } from 'src/queries/account/users'; +import { useProfile } from 'src/queries/profile/profile'; + +import { SingleTextFieldFormContainer } from './TimezoneForm'; + +import type { Profile } from '@linode/api-v4'; + +type Values = Pick; + +export const UsernameForm = () => { + const { data: profile } = useProfile(); + const { mutateAsync: updateUser } = useUpdateUserMutation( + profile?.username ?? '' + ); + const { enqueueSnackbar } = useSnackbar(); + + const values = { username: profile?.username ?? '' }; + + const { + control, + formState: { isDirty, isSubmitting }, + handleSubmit, + setError, + } = useForm({ + defaultValues: values, + values, + }); + + const tooltipForDisabledUsernameField = profile?.restricted + ? 'Restricted users cannot update their username. Please contact an account administrator.' + : profile?.user_type === 'proxy' + ? RESTRICTED_FIELD_TOOLTIP + : undefined; + + const onSubmit = async (values: Values) => { + try { + await updateUser(values); + enqueueSnackbar({ + message: 'Username updated successfully.', + variant: 'success', + }); + } catch (error) { + setError('username', { message: error[0].reason }); + } + }; + + return ( +
+ + ( + + )} + control={control} + name="username" + /> + + + + ); +}; diff --git a/packages/manager/src/queries/profile/profile.ts b/packages/manager/src/queries/profile/profile.ts index 04a379ef358..f5b2c1d20c1 100644 --- a/packages/manager/src/queries/profile/profile.ts +++ b/packages/manager/src/queries/profile/profile.ts @@ -90,12 +90,20 @@ export const useMutateProfile = () => { const queryClient = useQueryClient(); return useMutation>({ mutationFn: updateProfile, - onSuccess(newData) { + onSuccess(newData, variables) { updateProfileData(newData, queryClient); queryClient.invalidateQueries({ queryKey: accountQueries.users.queryKey, }); + + if (variables.email) { + // If the user updates their email, re-request notifications to + // potentially clear the email bounce notification. + queryClient.invalidateQueries({ + queryKey: accountQueries.notifications.queryKey, + }); + } }, }); }; From 77111ab976319a52fce01619c09a17f25ebed8e6 Mon Sep 17 00:00:00 2001 From: Banks Nussman <115251059+bnussman-akamai@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:18:06 -0400 Subject: [PATCH 14/66] chore: [M3-8786] - Update and clean up `@types/node` package (#11157) * update and clean up node types * small fix and add changesets --------- Co-authored-by: Banks Nussman --- .../.changeset/pr-11157-tech-stories-1729787265807.md | 5 +++++ packages/api-v4/package.json | 1 - .../.changeset/pr-11157-tech-stories-1729787405590.md | 5 +++++ .../support/plugins/discard-passed-test-recordings.ts | 1 - packages/manager/package.json | 2 +- packages/manager/src/hooks/useInterval.tsx | 8 ++++---- .../.changeset/pr-11157-tech-stories-1729787461166.md | 5 +++++ packages/ui/package.json | 1 - .../.changeset/pr-11157-tech-stories-1729787428923.md | 5 +++++ packages/validation/package.json | 1 - yarn.lock | 10 ++++++---- 11 files changed, 31 insertions(+), 13 deletions(-) create mode 100644 packages/api-v4/.changeset/pr-11157-tech-stories-1729787265807.md create mode 100644 packages/manager/.changeset/pr-11157-tech-stories-1729787405590.md create mode 100644 packages/ui/.changeset/pr-11157-tech-stories-1729787461166.md create mode 100644 packages/validation/.changeset/pr-11157-tech-stories-1729787428923.md diff --git a/packages/api-v4/.changeset/pr-11157-tech-stories-1729787265807.md b/packages/api-v4/.changeset/pr-11157-tech-stories-1729787265807.md new file mode 100644 index 00000000000..ecac9463430 --- /dev/null +++ b/packages/api-v4/.changeset/pr-11157-tech-stories-1729787265807.md @@ -0,0 +1,5 @@ +--- +"@linode/api-v4": Tech Stories +--- + +Remove `@types/node` dependency ([#11157](https://github.com/linode/manager/pull/11157)) diff --git a/packages/api-v4/package.json b/packages/api-v4/package.json index d66c2b5743a..af5ed8c861a 100644 --- a/packages/api-v4/package.json +++ b/packages/api-v4/package.json @@ -57,7 +57,6 @@ "lib" ], "devDependencies": { - "@types/node": "^12.7.1", "@types/yup": "^0.29.13", "axios-mock-adapter": "^1.22.0", "concurrently": "^9.0.1", diff --git a/packages/manager/.changeset/pr-11157-tech-stories-1729787405590.md b/packages/manager/.changeset/pr-11157-tech-stories-1729787405590.md new file mode 100644 index 00000000000..04db0ba79dd --- /dev/null +++ b/packages/manager/.changeset/pr-11157-tech-stories-1729787405590.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tech Stories +--- + +Update `@types/node` to `20.17.0` ([#11157](https://github.com/linode/manager/pull/11157)) diff --git a/packages/manager/cypress/support/plugins/discard-passed-test-recordings.ts b/packages/manager/cypress/support/plugins/discard-passed-test-recordings.ts index eca3bca5c2b..b5f124e609d 100644 --- a/packages/manager/cypress/support/plugins/discard-passed-test-recordings.ts +++ b/packages/manager/cypress/support/plugins/discard-passed-test-recordings.ts @@ -1,4 +1,3 @@ -// @ts-expect-error for some reason, @node/types is v12 and it probably doesn't have this. import fs from 'fs/promises'; import { CypressPlugin } from './plugin'; diff --git a/packages/manager/package.json b/packages/manager/package.json index a38fa596162..cea6a44bb2b 100644 --- a/packages/manager/package.json +++ b/packages/manager/package.json @@ -149,7 +149,7 @@ "@types/markdown-it": "^10.0.2", "@types/md5": "^2.1.32", "@types/mocha": "^10.0.2", - "@types/node": "^12.7.1", + "@types/node": "^20.17.0", "@types/qrcode.react": "^0.8.0", "@types/ramda": "0.25.16", "@types/react": "^18.2.55", diff --git a/packages/manager/src/hooks/useInterval.tsx b/packages/manager/src/hooks/useInterval.tsx index 44082110306..223203b2f13 100644 --- a/packages/manager/src/hooks/useInterval.tsx +++ b/packages/manager/src/hooks/useInterval.tsx @@ -35,7 +35,7 @@ interface UseIntervalReturn { /** * Reference to the interval timer. */ - intervalRef: React.MutableRefObject; + intervalRef: React.MutableRefObject; } const useInterval = ({ @@ -46,7 +46,7 @@ const useInterval = ({ startImmediately = false, when = true, }: UseIntervalOptions): UseIntervalReturn => { - const intervalRef = useRef(); + const intervalRef = useRef(); // Save the callback to a ref to ensure it has the most recent version // without needing to reset the interval each time the callback changes. @@ -58,7 +58,7 @@ const useInterval = ({ const clearTimer = useCallback(() => { if (intervalRef.current) { - clearInterval(intervalRef.current); + window.clearInterval(intervalRef.current); // Optionally clear the ref after stopping the interval intervalRef.current = undefined; @@ -84,7 +84,7 @@ const useInterval = ({ tick(); } - intervalRef.current = setInterval(tick, delay); + intervalRef.current = window.setInterval(tick, delay); return clearTimer; } diff --git a/packages/ui/.changeset/pr-11157-tech-stories-1729787461166.md b/packages/ui/.changeset/pr-11157-tech-stories-1729787461166.md new file mode 100644 index 00000000000..92c48e1e383 --- /dev/null +++ b/packages/ui/.changeset/pr-11157-tech-stories-1729787461166.md @@ -0,0 +1,5 @@ +--- +"@linode/ui": Tech Stories +--- + +Remove `@types/node` dependency ([#11157](https://github.com/linode/manager/pull/11157)) diff --git a/packages/ui/package.json b/packages/ui/package.json index 7a4ba2b1d55..787d09ef2b2 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -44,7 +44,6 @@ "@testing-library/dom": "^10.1.0", "@testing-library/jest-dom": "~6.4.2", "@testing-library/react": "~16.0.0", - "@types/node": "^12.7.1", "@types/react": "^18.2.55", "@types/react-dom": "^18.2.18", "@typescript-eslint/eslint-plugin": "^6.21.0", diff --git a/packages/validation/.changeset/pr-11157-tech-stories-1729787428923.md b/packages/validation/.changeset/pr-11157-tech-stories-1729787428923.md new file mode 100644 index 00000000000..c00c591cd64 --- /dev/null +++ b/packages/validation/.changeset/pr-11157-tech-stories-1729787428923.md @@ -0,0 +1,5 @@ +--- +"@linode/validation": Tech Stories +--- + +Remove `@types/node` dependency ([#11157](https://github.com/linode/manager/pull/11157)) diff --git a/packages/validation/package.json b/packages/validation/package.json index 3f3cdc461c5..4aaeb1f109f 100644 --- a/packages/validation/package.json +++ b/packages/validation/package.json @@ -43,7 +43,6 @@ "yup": "^0.32.9" }, "devDependencies": { - "@types/node": "^12.7.1", "concurrently": "^9.0.1", "eslint": "^6.8.0", "eslint-plugin-sonarjs": "^0.5.0", diff --git a/yarn.lock b/yarn.lock index bc7e16e4ba3..17680d9e989 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2368,10 +2368,12 @@ dependencies: undici-types "~6.19.2" -"@types/node@^12.7.1": - version "12.20.55" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" - integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== +"@types/node@^20.17.0": + version "20.17.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.0.tgz#d0620ba0fe4cf2a0f12351c7bdd805fc4e1f036b" + integrity sha512-a7zRo0f0eLo9K5X9Wp5cAqTUNGzuFLDG2R7C4HY2BhcMAsxgSPuRvAC1ZB6QkuUQXf0YZAgfOX2ZyrBa2n4nHQ== + dependencies: + undici-types "~6.19.2" "@types/parse-json@^4.0.0": version "4.0.2" From 1a9d4796639f54cf636330eefa89968236af94c3 Mon Sep 17 00:00:00 2001 From: Connie Liu <139280159+coliu-akamai@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:32:36 -0400 Subject: [PATCH 15/66] test: [M3-8776] - Fix lke-create tests and create unit tests for LKE ACL (#11176) * fix lke-create tests, move copy checks to unit tests * Added changeset: Add unit tests to declutter LKE ACL cypress tests, fix lke-create.spec.ts failures --- .../pr-11176-tests-1730144406169.md | 5 + .../e2e/core/kubernetes/lke-create.spec.ts | 43 +----- .../e2e/core/kubernetes/lke-update.spec.ts | 83 ++--------- .../ControlPlaneACLPane.test.tsx | 79 +++++++++++ .../KubeControlPaneACLDrawer.test.tsx | 133 ++++++++++++++++++ .../KubeControlPaneACLDrawer.tsx | 10 +- 6 files changed, 241 insertions(+), 112 deletions(-) create mode 100644 packages/manager/.changeset/pr-11176-tests-1730144406169.md create mode 100644 packages/manager/src/features/Kubernetes/CreateCluster/ControlPlaneACLPane.test.tsx create mode 100644 packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.test.tsx diff --git a/packages/manager/.changeset/pr-11176-tests-1730144406169.md b/packages/manager/.changeset/pr-11176-tests-1730144406169.md new file mode 100644 index 00000000000..0a1b937db45 --- /dev/null +++ b/packages/manager/.changeset/pr-11176-tests-1730144406169.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tests +--- + +Add unit tests to declutter LKE ACL cypress tests, fix lke-create.spec.ts failures ([#11176](https://github.com/linode/manager/pull/11176)) diff --git a/packages/manager/cypress/e2e/core/kubernetes/lke-create.spec.ts b/packages/manager/cypress/e2e/core/kubernetes/lke-create.spec.ts index b9be678ad32..2398df08e8c 100644 --- a/packages/manager/cypress/e2e/core/kubernetes/lke-create.spec.ts +++ b/packages/manager/cypress/e2e/core/kubernetes/lke-create.spec.ts @@ -411,14 +411,6 @@ describe('LKE Cluster Creation with ACL', () => { cy.wait(['@getAccount']); cy.contains('Control Plane ACL').should('not.exist'); - cy.contains( - 'Enable an access control list (ACL) on your LKE cluster to restrict access to your cluster’s control plane. When enabled, only the IP addresses and ranges specified by you can connect to the control plane.' - ).should('not.exist'); - cy.contains('Enable Control Plane ACL').should('not.exist'); - cy.contains('IPv4 Addresses or CIDRs').should('not.exist'); - cy.contains('IPv6 Addresses or CIDRs').should('not.exist'); - cy.contains('Add IPv4 Address').should('not.exist'); - cy.contains('Add IPv6 Address').should('not.exist'); }); // setting up mocks @@ -511,26 +503,13 @@ describe('LKE Cluster Creation with ACL', () => { .should('be.visible') .click(); - // Confirm ACL section and disable ACL + // Disable ACL cy.contains('Control Plane ACL').should('be.visible'); - cy.contains( - 'Enable an access control list (ACL) on your LKE cluster to restrict access to your cluster’s control plane. When enabled, only the IP addresses and ranges specified by you can connect to the control plane.' - ).should('be.visible'); - cy.contains('Enable Control Plane ACL').should('be.visible'); - cy.contains('IPv4 Addresses or CIDRs').should('be.visible'); - cy.contains('IPv6 Addresses or CIDRs').should('be.visible'); - cy.contains('Add IPv4 Address').should('be.visible'); - cy.contains('Add IPv6 Address').should('be.visible'); ui.toggle .find() .should('have.attr', 'data-qa-toggle', 'true') .should('be.visible') .click(); - // IP related fields do not exist when ACL is not enabled - cy.get('IPv4 Addresses or CIDRs').should('not.exist'); - cy.get('IPv6 Addresses or CIDRs').should('not.exist'); - cy.get('Add IPv4 Address').should('not.exist'); - cy.get('Add IPv6 Address').should('not.exist'); // Add a node pool cy.log(`Adding ${nodeCount}x ${getLkePlanName(clusterPlan)} node(s)`); @@ -637,20 +616,12 @@ describe('LKE Cluster Creation with ACL', () => { // Confirm ACL section cy.contains('Control Plane ACL').should('be.visible'); - cy.contains( - 'Enable an access control list (ACL) on your LKE cluster to restrict access to your cluster’s control plane. When enabled, only the IP addresses and ranges specified by you can connect to the control plane.' - ).should('be.visible'); - cy.contains('Enable Control Plane ACL').should('be.visible'); - cy.contains('IPv4 Addresses or CIDRs').should('be.visible'); - cy.contains('IPv6 Addresses or CIDRs').should('be.visible'); - cy.contains('Add IPv4 Address').should('be.visible'); - cy.contains('Add IPv6 Address').should('be.visible'); ui.toggle .find() .should('have.attr', 'data-qa-toggle', 'true') .should('be.visible'); // Add some IPv4s and an IPv6 - cy.findByPlaceholderText('0.0.0.0/0') + cy.findByLabelText('IPv4 Addresses or CIDRs ip-address-0') .should('be.visible') .click() .type('10.0.0.0/24'); @@ -662,7 +633,7 @@ describe('LKE Cluster Creation with ACL', () => { .should('be.visible') .click() .type('10.0.1.0/24'); - cy.findByPlaceholderText('::/0') + cy.findByLabelText('IPv6 Addresses or CIDRs ip-address-0') .should('be.visible') .click() .type('8e61:f9e9:8d40:6e0a:cbff:c97a:2692:827e'); @@ -763,7 +734,7 @@ describe('LKE Cluster Creation with ACL', () => { .click(); // Confirm ACL IPv4 validation works as expected - cy.findByPlaceholderText('0.0.0.0/0') + cy.findByLabelText('IPv4 Addresses or CIDRs ip-address-0') .should('be.visible') .click() .type('invalid ip'); @@ -771,7 +742,7 @@ describe('LKE Cluster Creation with ACL', () => { cy.contains('Control Plane ACL').should('be.visible').click(); cy.contains('Must be a valid IPv4 address.').should('be.visible'); // enter valid IP - cy.findByPlaceholderText('0.0.0.0/0') + cy.findByLabelText('IPv4 Addresses or CIDRs ip-address-0') .should('be.visible') .click() .clear() @@ -781,7 +752,7 @@ describe('LKE Cluster Creation with ACL', () => { cy.contains('Must be a valid IPv4 address.').should('not.exist'); // Confirm ACL IPv6 validation works as expected - cy.findByPlaceholderText('::/0') + cy.findByLabelText('IPv6 Addresses or CIDRs ip-address-0') .should('be.visible') .click() .type('invalid ip'); @@ -789,7 +760,7 @@ describe('LKE Cluster Creation with ACL', () => { cy.contains('Control Plane ACL').should('be.visible').click(); cy.contains('Must be a valid IPv6 address.').should('be.visible'); // enter valid IP - cy.findByPlaceholderText('::/0') + cy.findByLabelText('IPv6 Addresses or CIDRs ip-address-0') .should('be.visible') .click() .clear() 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 85de03b7b2d..7c20f14834f 100644 --- a/packages/manager/cypress/e2e/core/kubernetes/lke-update.spec.ts +++ b/packages/manager/cypress/e2e/core/kubernetes/lke-update.spec.ts @@ -1436,16 +1436,8 @@ describe('LKE ACL updates', () => { .should('be.visible') .should('not.be.enabled'); - cy.contains( - "Control Plane ACL secures network access to your LKE cluster's control plane. Use this form to enable or disable the ACL on your LKE cluster, update the list of allowed IP addresses, and adjust other settings." - ).should('be.visible'); - - // confirm Activation Status section and toggle on 'Enable' + // Enable ACL cy.contains('Activation Status').should('be.visible'); - cy.contains( - 'Enable or disable the Control Plane ACL. If the ACL is not enabled, any public IP address can be used to access your control plane. Once enabled, all network access is denied except for the IP addresses and CIDR ranges defined on the ACL.' - ).should('be.visible'); - cy.findByText('Enable Control Plane ACL'); ui.toggle .find() .should('have.attr', 'data-qa-toggle', 'false') @@ -1458,39 +1450,19 @@ describe('LKE ACL updates', () => { .should('be.visible') .should('be.enabled'); - // confirm Revision ID section and edit Revision ID - cy.findAllByText('Revision ID').should('have.length', 2); - cy.contains( - 'A unique identifying string for this particular revision to the ACL, used by clients to track events related to ACL update requests and enforcement. This defaults to a randomly generated string but can be edited if you prefer to specify your own string to use for tracking this change.' - ).should('be.visible'); + // Edit Revision ID cy.findByLabelText('Revision ID').should( 'have.value', mockACLOptions['revision-id'] ); cy.findByLabelText('Revision ID').clear().type(mockRevisionId); - // confirm Addresses section - cy.findByText('Addresses').should('be.visible'); - cy.findByText( - "A list of allowed IPv4 and IPv6 addresses and CIDR ranges. This cluster's control plane will only be accessible from IP addresses within this list." - ).should('be.visible'); - cy.findByText('IPv4 Addresses or CIDRs').should('be.visible'); - cy.findByText('Add IPv4 Address') - .should('be.visible') - .should('be.enabled'); - // confirm current IPv4 value and enter new IP + // Addresses section: confirm current IPv4 value and enter new IP cy.findByDisplayValue('10.0.3.0/24') .should('be.visible') .click() .clear() .type('10.0.0.0/24'); - cy.findByText('IPv6 Addresses or CIDRs').should('be.visible'); - cy.findByLabelText('IPv6 Addresses or CIDRs ip-address-0').should( - 'be.visible' - ); - cy.findByText('Add IPv6 Address') - .should('be.visible') - .should('be.enabled'); // submit ui.button @@ -1662,16 +1634,8 @@ describe('LKE ACL updates', () => { .should('be.visible') .should('not.be.enabled'); - cy.contains( - "Control Plane ACL secures network access to your LKE cluster's control plane. Use this form to enable or disable the ACL on your LKE cluster, update the list of allowed IP addresses, and adjust other settings." - ).should('be.visible'); - - // confirm Activation Status section and toggle off 'Enable' + // Activation Status section: toggle off 'Enable' cy.contains('Activation Status').should('be.visible'); - cy.contains( - 'Enable or disable the Control Plane ACL. If the ACL is not enabled, any public IP address can be used to access your control plane. Once enabled, all network access is denied except for the IP addresses and CIDR ranges defined on the ACL.' - ).should('be.visible'); - cy.findByText('Enable Control Plane ACL'); ui.toggle .find() .should('have.attr', 'data-qa-toggle', 'true') @@ -1684,23 +1648,13 @@ describe('LKE ACL updates', () => { .should('be.visible') .should('be.enabled'); - // confirm Revision ID section exists - cy.findAllByText('Revision ID').should('have.length', 2); - cy.contains( - 'A unique identifying string for this particular revision to the ACL, used by clients to track events related to ACL update requests and enforcement. This defaults to a randomly generated string but can be edited if you prefer to specify your own string to use for tracking this change.' - ).should('be.visible'); + // confirm Revision ID section cy.findByLabelText('Revision ID').should( 'have.value', mockACLOptions['revision-id'] ); - // confirm Addresses section - cy.findByText('Addresses').should('be.visible'); - cy.findByText( - "A list of allowed IPv4 and IPv6 addresses and CIDR ranges. This cluster's control plane will only be accessible from IP addresses within this list." - ).should('be.visible'); - cy.findByText('IPv4 Addresses or CIDRs').should('be.visible'); - // update IPv4 + // Addresses Section: update IPv4 cy.findByLabelText('IPv4 Addresses or CIDRs ip-address-0') .should('be.visible') .click() @@ -1709,7 +1663,6 @@ describe('LKE ACL updates', () => { .should('be.visible') .should('be.enabled') .click(); - cy.findByText('IPv6 Addresses or CIDRs').should('be.visible'); // update IPv6 cy.findByLabelText('IPv6 Addresses or CIDRs ip-address-0') .should('be.visible') @@ -1808,35 +1761,24 @@ describe('LKE ACL updates', () => { .findByTitle('Control Plane ACL') .should('be.visible') .within(() => { + // Confirm installation notice is displayed cy.contains( - "Control Plane ACL secures network access to your LKE cluster's control plane. Use this form to enable or disable the ACL on your LKE cluster, update the list of allowed IP addresses, and adjust other settings." + 'Control Plane ACL has not yet been installed on this cluster. During installation, it may take up to 15 minutes for the access control list to be fully enforced.' ).should('be.visible'); // Confirm Activation Status section and Enable ACL cy.contains('Activation Status').should('be.visible'); - cy.contains( - 'Enable or disable the Control Plane ACL. If the ACL is not enabled, any public IP address can be used to access your control plane. Once enabled, all network access is denied except for the IP addresses and CIDR ranges defined on the ACL.' - ).should('be.visible'); ui.toggle .find() .should('have.attr', 'data-qa-toggle', 'false') .should('be.visible') .click(); - // Confirm revision ID section does not exist + // Revision ID section does not exist cy.contains('Revision ID').should('not.exist'); - cy.contains( - 'A unique identifying string for this particular revision to the ACL, used by clients to track events related to ACL update requests and enforcement. This defaults to a randomly generated string but can be edited if you prefer to specify your own string to use for tracking this change.' - ).should('not.exist'); - // Confirm Addresses section and add IP addresses + // Addresses section: add IP addresses cy.findByText('Addresses').should('be.visible'); - cy.findByText( - "A list of allowed IPv4 and IPv6 addresses and CIDR ranges. This cluster's control plane will only be accessible from IP addresses within this list." - ).should('be.visible'); - cy.findByText('IPv4 Addresses or CIDRs').should('be.visible'); - cy.findByText('IPv6 Addresses or CIDRs').should('be.visible'); - cy.findByLabelText('IPv4 Addresses or CIDRs ip-address-0') .should('be.visible') .click() @@ -1847,11 +1789,6 @@ describe('LKE ACL updates', () => { .click() .type('8e61:f9e9:8d40:6e0a:cbff:c97a:2692:827e'); - // Confirm installation notice is displayed - cy.contains( - 'Control Plane ACL has not yet been installed on this cluster. During installation, it may take up to 15 minutes for the access control list to be fully enforced.' - ).should('be.visible'); - // submit ui.button .findByTitle('Update') diff --git a/packages/manager/src/features/Kubernetes/CreateCluster/ControlPlaneACLPane.test.tsx b/packages/manager/src/features/Kubernetes/CreateCluster/ControlPlaneACLPane.test.tsx new file mode 100644 index 00000000000..dc160e53c01 --- /dev/null +++ b/packages/manager/src/features/Kubernetes/CreateCluster/ControlPlaneACLPane.test.tsx @@ -0,0 +1,79 @@ +import userEvent from '@testing-library/user-event'; +import * as React from 'react'; + +import { renderWithTheme } from 'src/utilities/testHelpers'; + +import { ControlPlaneACLPane } from './ControlPlaneACLPane'; + +import type { ControlPlaneACLProps } from './ControlPlaneACLPane'; + +const props: ControlPlaneACLProps = { + enableControlPlaneACL: true, + errorText: undefined, + handleIPv4Change: vi.fn(), + handleIPv6Change: vi.fn(), + ipV4Addr: [{ address: '' }], + ipV6Addr: [{ address: '' }], + setControlPlaneACL: vi.fn(), +}; + +describe('ControlPlaneACLPane', () => { + it('renders all fields when enableControlPlaneACL is true', () => { + const { getByText } = renderWithTheme(); + + expect(getByText('Control Plane ACL')).toBeVisible(); + expect( + getByText( + 'Enable an access control list (ACL) on your LKE cluster to restrict access to your cluster’s control plane. When enabled, only the IP addresses and ranges you specify can connect to the control plane.' + ) + ).toBeVisible(); + expect(getByText('Enable Control Plane ACL')).toBeVisible(); + expect(getByText('IPv4 Addresses or CIDRs')).toBeVisible(); + expect(getByText('Add IPv4 Address')).toBeVisible(); + expect(getByText('IPv6 Addresses or CIDRs')).toBeVisible(); + expect(getByText('Add IPv6 Address')).toBeVisible(); + }); + + it('hides IP fields when enableControlPlaneACL is false', () => { + const { getByText, queryByText } = renderWithTheme( + + ); + + expect(getByText('Control Plane ACL')).toBeVisible(); + expect( + getByText( + 'Enable an access control list (ACL) on your LKE cluster to restrict access to your cluster’s control plane. When enabled, only the IP addresses and ranges you specify can connect to the control plane.' + ) + ).toBeVisible(); + expect(getByText('Enable Control Plane ACL')).toBeVisible(); + expect(queryByText('IPv4 Addresses or CIDRs')).not.toBeInTheDocument(); + expect(queryByText('Add IPv4 Address')).not.toBeInTheDocument(); + expect(queryByText('IPv6 Addresses or CIDRs')).not.toBeInTheDocument(); + expect(queryByText('Add IPv6 Address')).not.toBeInTheDocument(); + }); + + it('calls setControlPlaneACL when clicking the toggle', async () => { + const { getByText } = renderWithTheme(); + + const toggle = getByText('Enable Control Plane ACL'); + await userEvent.click(toggle); + + expect(props.setControlPlaneACL).toHaveBeenCalled(); + }); + + it('handles IP changes', async () => { + const { getByLabelText } = renderWithTheme( + + ); + + const ipv4 = getByLabelText('IPv4 Addresses or CIDRs ip-address-0'); + await userEvent.type(ipv4, 'test'); + + expect(props.handleIPv4Change).toHaveBeenCalled(); + + const ipv6 = getByLabelText('IPv6 Addresses or CIDRs ip-address-0'); + await userEvent.type(ipv6, 'test'); + + expect(props.handleIPv6Change).toHaveBeenCalled(); + }); +}); diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.test.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.test.tsx new file mode 100644 index 00000000000..810b3929640 --- /dev/null +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.test.tsx @@ -0,0 +1,133 @@ +import userEvent from '@testing-library/user-event'; +import * as React from 'react'; + +import { renderWithTheme } from 'src/utilities/testHelpers'; + +import { KubeControlPlaneACLDrawer } from './KubeControlPaneACLDrawer'; + +import type { KubeControlPlaneACLDrawerProps } from './KubeControlPaneACLDrawer'; + +const props: KubeControlPlaneACLDrawerProps = { + closeDrawer: vi.fn(), + clusterId: 1, + clusterLabel: 'Test', + clusterMigrated: true, + open: true, + showControlPlaneACL: true, +}; + +const queryMocks = vi.hoisted(() => ({ + useKubernetesControlPlaneACLQuery: vi.fn().mockReturnValue({ + data: { + acl: { + addresses: { + ipv4: [''], + ipv6: [''], + }, + enabled: false, + 'revision-id': '', + }, + }, + }), +})); + +vi.mock('src/queries/kubernetes', async () => { + const actual = await vi.importActual('src/queries/kubernetes'); + return { + ...actual, + useKubernetesControlPlaneACLQuery: + queryMocks.useKubernetesControlPlaneACLQuery, + }; +}); + +describe('KubeControlPaneACLDrawer', () => { + it('renders the drawer as expected when the cluster is migrated', async () => { + const { getAllByText, getByText, queryByText } = renderWithTheme( + + ); + + expect(getByText('Control Plane ACL')).toBeVisible(); + expect( + getByText( + "Control Plane ACL secures network access to your LKE cluster's control plane. Use this form to enable or disable the ACL on your LKE cluster, update the list of allowed IP addresses, and adjust other settings." + ) + ).toBeVisible(); + + // Activation Status section + expect(getByText('Activation Status')).toBeVisible(); + expect( + getByText( + 'Enable or disable the Control Plane ACL. If the ACL is not enabled, any public IP address can be used to access your control plane. Once enabled, all network access is denied except for the IP addresses and CIDR ranges defined on the ACL.' + ) + ).toBeVisible(); + expect(getByText('Enable Control Plane ACL')).toBeVisible(); + + // Revision ID section + expect(getAllByText('Revision ID').length).toEqual(2); + expect( + getByText( + 'A unique identifying string for this particular revision to the ACL, used by clients to track events related to ACL update requests and enforcement. This defaults to a randomly generated string but can be edited if you prefer to specify your own string to use for tracking this change.' + ) + ).toBeVisible(); + + // Addresses section + expect(getByText('Addresses')).toBeVisible(); + expect( + getByText( + "A list of allowed IPv4 and IPv6 addresses and CIDR ranges. This cluster's control plane will only be accessible from IP addresses within this list." + ) + ).toBeVisible(); + expect(getByText('IPv4 Addresses or CIDRs')).toBeVisible(); + expect(getByText('Add IPv4 Address')).toBeVisible(); + expect(getByText('IPv6 Addresses or CIDRs')).toBeVisible(); + expect(getByText('Add IPv6 Address')).toBeVisible(); + + // Confirm notice does not display + expect( + queryByText( + 'Control Plane ACL has not yet been installed on this cluster. During installation, it may take up to 15 minutes for the access control list to be fully enforced.' + ) + ).not.toBeInTheDocument(); + }); + + it('shows a notice and hides revision ID if cluster is not migrated', () => { + const { getByText, queryByText } = renderWithTheme( + + ); + + // Confirm notice displays + expect( + getByText( + 'Control Plane ACL has not yet been installed on this cluster. During installation, it may take up to 15 minutes for the access control list to be fully enforced.' + ) + ).toBeVisible(); + + // Confirm Revision ID section doesn't display + expect(queryByText('Revision ID')).not.toBeInTheDocument(); + expect( + queryByText( + 'A unique identifying string for this particular revision to the ACL, used by clients to track events related to ACL update requests and enforcement. This defaults to a randomly generated string but can be edited if you prefer to specify your own string to use for tracking this change.' + ) + ).not.toBeInTheDocument(); + + // Confirm other sections still exist + expect(getByText('Activation Status')).toBeVisible(); + expect(getByText('Enable Control Plane ACL')).toBeVisible(); + expect(getByText('Addresses')).toBeVisible(); + expect(getByText('IPv4 Addresses or CIDRs')).toBeVisible(); + expect(getByText('Add IPv4 Address')).toBeVisible(); + expect(getByText('IPv6 Addresses or CIDRs')).toBeVisible(); + expect(getByText('Add IPv6 Address')).toBeVisible(); + }); + + it('closes the drawer', async () => { + const { getByText } = renderWithTheme( + + ); + + const cancel = getByText('Cancel'); + await userEvent.click(cancel); + + expect(props.closeDrawer).toHaveBeenCalled(); + }); +}); diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.tsx index 49522dc4d39..841e1d36ca6 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.tsx @@ -20,11 +20,12 @@ import { useKubernetesControlPlaneACLMutation, useKubernetesControlPlaneACLQuery, } from 'src/queries/kubernetes'; +import { omittedProps } from 'src/utilities/omittedProps'; import { scrollErrorIntoViewV2 } from 'src/utilities/scrollErrorIntoViewV2'; import type { KubernetesControlPlaneACLPayload } from '@linode/api-v4'; -interface Props { +export interface KubeControlPlaneACLDrawerProps { closeDrawer: () => void; clusterId: number; clusterLabel: string; @@ -33,7 +34,9 @@ interface Props { showControlPlaneACL: boolean; } -export const KubeControlPlaneACLDrawer = (props: Props) => { +export const KubeControlPlaneACLDrawer = ( + props: KubeControlPlaneACLDrawerProps +) => { const formContainerRef = React.useRef(null); const { closeDrawer, @@ -241,8 +244,8 @@ export const KubeControlPlaneACLDrawer = (props: Props) => { )} Addresses A list of allowed IPv4 and IPv6 addresses and CIDR ranges. This @@ -307,6 +310,7 @@ export const KubeControlPlaneACLDrawer = (props: Props) => { const StyledTypography = styled(Typography, { label: 'StyledTypography', + shouldForwardProp: omittedProps(['topMargin']), })<{ topMargin?: boolean }>(({ theme, ...props }) => ({ ...(props.topMargin ? { marginTop: theme.spacing(1) } : {}), width: '90%', From 871210c704559bdeeaa4e845f3a307627b8ad127 Mon Sep 17 00:00:00 2001 From: Purvesh Makode Date: Tue, 29 Oct 2024 10:24:22 +0530 Subject: [PATCH 16/66] upcoming: [M3-6700] - Replace one-off hardcoded black and white color values with colorTokens (#11165) * Replace one off hardcoded black and white colors values with colorTokens * Added changeset: Replace one off hardcoded black and white color values with colorTokens * Clean up... --- ...r-11165-upcoming-features-1729872623442.md | 5 +++++ packages/manager/src/GoTo.tsx | 2 +- .../AkamaiBanner/AkamaiBanner.styles.ts | 20 +++++++++++-------- .../components/ColorPalette/ColorPalette.tsx | 2 +- packages/manager/src/components/Drawer.tsx | 2 +- .../EnhancedSelect/Select.styles.ts | 10 +++++----- .../components/ImageSelect/ImageOption.tsx | 7 ++++--- .../src/components/MainContentBanner.tsx | 2 +- .../MultipleIPInput/MultipleIPInput.tsx | 2 +- .../src/components/Notice/Notice.styles.ts | 2 +- .../components/Placeholder/Placeholder.tsx | 10 ++++++++-- .../PrimaryNav/PrimaryNav.styles.ts | 2 +- .../PromotionalOfferCard.tsx | 4 ++-- .../src/components/ShowMore/ShowMore.tsx | 4 ++-- .../manager/src/components/Tag/Tag.styles.ts | 4 ++-- .../PaymentDrawer/GooglePayButton.tsx | 4 +++- .../Databases/DatabaseCreate/EngineOption.tsx | 2 +- .../CreateCluster/ApplicationPlatform.tsx | 5 ++++- .../CreateCluster/HAControlPlane.tsx | 5 ++++- .../Tabs/Marketplace/AppDetailDrawer.tsx | 2 +- .../SearchBar/SearchSuggestion.styles.ts | 2 +- .../manager/src/features/TopMenu/TopMenu.tsx | 5 ++++- packages/ui/src/foundations/themes/light.ts | 14 ++++++------- 23 files changed, 72 insertions(+), 45 deletions(-) create mode 100644 packages/manager/.changeset/pr-11165-upcoming-features-1729872623442.md diff --git a/packages/manager/.changeset/pr-11165-upcoming-features-1729872623442.md b/packages/manager/.changeset/pr-11165-upcoming-features-1729872623442.md new file mode 100644 index 00000000000..5732391876a --- /dev/null +++ b/packages/manager/.changeset/pr-11165-upcoming-features-1729872623442.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Replace one-off hardcoded black and white color values with colorTokens ([#11165](https://github.com/linode/manager/pull/11165)) diff --git a/packages/manager/src/GoTo.tsx b/packages/manager/src/GoTo.tsx index 05cde0ef923..9403a5a0041 100644 --- a/packages/manager/src/GoTo.tsx +++ b/packages/manager/src/GoTo.tsx @@ -42,7 +42,7 @@ const useStyles = makeStyles()((theme: Theme) => ({ }, '& .react-select__option--is-focused': { backgroundColor: theme.palette.primary.main, - color: 'white', + color: theme.colorTokens.Neutrals.White, }, '& .react-select__value-container': { '& p': { diff --git a/packages/manager/src/components/AkamaiBanner/AkamaiBanner.styles.ts b/packages/manager/src/components/AkamaiBanner/AkamaiBanner.styles.ts index 80ce5d2e751..99cbf589211 100644 --- a/packages/manager/src/components/AkamaiBanner/AkamaiBanner.styles.ts +++ b/packages/manager/src/components/AkamaiBanner/AkamaiBanner.styles.ts @@ -9,20 +9,20 @@ import { Stack } from '../Stack'; export const StyledAkamaiLogo = styled(AkamaiLogo, { label: 'StyledAkamaiLogo', -})({ +})(({ theme }) => ({ '& .akamai-logo-icon': { - fill: 'white', + fill: theme.colorTokens.Neutrals.White, }, '& .akamai-logo-name': { display: 'none', }, -}); +})); export const StyledWarningIcon = styled(Warning, { label: 'StyledWarningIcon', -})({ - color: 'black', -}); +})(({ theme }) => ({ + color: theme.colorTokens.Neutrals.Black, +})); export const StyledBanner = styled(Stack, { label: 'StyledBanner', @@ -39,8 +39,12 @@ export const StyledBannerLabel = styled(Box, { label: 'StyledBannerLabel', shouldForwardProp: omittedProps(['warning']), })<{ warning?: boolean }>(({ theme, warning }) => ({ - backgroundColor: warning ? theme.palette.warning.dark : 'black', - color: warning ? 'black' : 'white', + backgroundColor: warning + ? theme.palette.warning.dark + : theme.colorTokens.Neutrals.Black, + color: warning + ? theme.colorTokens.Neutrals.Black + : theme.colorTokens.Neutrals.White, padding: theme.spacing(2.3), [theme.breakpoints.up('sm')]: { textWrap: 'nowrap', diff --git a/packages/manager/src/components/ColorPalette/ColorPalette.tsx b/packages/manager/src/components/ColorPalette/ColorPalette.tsx index f6e27c7c7a5..3a9f2bc1263 100644 --- a/packages/manager/src/components/ColorPalette/ColorPalette.tsx +++ b/packages/manager/src/components/ColorPalette/ColorPalette.tsx @@ -50,7 +50,7 @@ const useStyles = makeStyles()((theme: Theme) => ({ * * If a color does not exist in the current palette and is only used once, consider applying the color conditionally: * - * `theme.name === 'light' ? '#fff' : '#000'` + * `theme.name === 'light' ? theme.colorTokens.Neutrals.White : theme.colorTokens.Neutrals.Black` */ export const ColorPalette = () => { const { classes } = useStyles(); diff --git a/packages/manager/src/components/Drawer.tsx b/packages/manager/src/components/Drawer.tsx index 4490a0090e4..3abb4a91519 100644 --- a/packages/manager/src/components/Drawer.tsx +++ b/packages/manager/src/components/Drawer.tsx @@ -31,7 +31,7 @@ const useStyles = makeStyles()((theme: Theme) => ({ button: { '& :hover, & :focus': { backgroundColor: theme.palette.primary.main, - color: 'white', + color: theme.colorTokens.Neutrals.White, }, '& > span': { padding: 2, diff --git a/packages/manager/src/components/EnhancedSelect/Select.styles.ts b/packages/manager/src/components/EnhancedSelect/Select.styles.ts index 597687971d1..b3a40de671e 100644 --- a/packages/manager/src/components/EnhancedSelect/Select.styles.ts +++ b/packages/manager/src/components/EnhancedSelect/Select.styles.ts @@ -169,7 +169,7 @@ export const useStyles = makeStyles()((theme: Theme) => ({ }, '&:hover': { '& svg': { - color: 'white', + color: theme.colorTokens.Neutrals.White, }, backgroundColor: theme.palette.primary.main, }, @@ -202,7 +202,7 @@ export const useStyles = makeStyles()((theme: Theme) => ({ }, '& .react-select__option--is-focused': { backgroundColor: theme.palette.primary.main, - color: 'white', + color: theme.colorTokens.Neutrals.White, }, '& .react-select__option--is-selected': { '&.react-select__option--is-focused': { @@ -251,7 +251,7 @@ export const useStyles = makeStyles()((theme: Theme) => ({ '& .tag': { '&:hover': { backgroundColor: theme.palette.primary.main, - color: 'white', + color: theme.colorTokens.Neutrals.White, }, backgroundColor: theme.bg.lightBlue1, color: theme.palette.text.primary, @@ -377,7 +377,7 @@ export const reactSelectStyles = (theme: Theme) => ({ }, '&:hover': { '& svg': { - color: 'white', + color: theme.colorTokens.Neutrals.White, }, backgroundColor: theme.palette.primary.main, }, @@ -408,7 +408,7 @@ export const reactSelectStyles = (theme: Theme) => ({ return { ...optionStyles, backgroundColor: theme.palette.primary.main, - color: 'white', + color: theme.colorTokens.Neutrals.White, }; } if (state.isSelected) { diff --git a/packages/manager/src/components/ImageSelect/ImageOption.tsx b/packages/manager/src/components/ImageSelect/ImageOption.tsx index 907a4839382..757f1c58f8e 100644 --- a/packages/manager/src/components/ImageSelect/ImageOption.tsx +++ b/packages/manager/src/components/ImageSelect/ImageOption.tsx @@ -25,17 +25,18 @@ const useStyles = makeStyles()((theme: Theme) => ({ }, focused: { '& g': { - fill: 'white', + fill: theme.colorTokens.Neutrals.White, }, backgroundColor: theme.palette.primary.main, - color: 'white', + color: theme.colorTokens.Neutrals.White, }, root: { '& *': { lineHeight: '1.2em', }, '& g': { - fill: theme.name === 'dark' ? 'white' : '#888f91', + fill: + theme.name === 'dark' ? theme.colorTokens.Neutrals.White : '#888f91', }, display: 'flex !important', flexDirection: 'row', diff --git a/packages/manager/src/components/MainContentBanner.tsx b/packages/manager/src/components/MainContentBanner.tsx index 1746e4107fd..5909c16b2c1 100644 --- a/packages/manager/src/components/MainContentBanner.tsx +++ b/packages/manager/src/components/MainContentBanner.tsx @@ -66,7 +66,7 @@ export const MainContentBanner = React.memo(() => { sx={(theme) => ({ alignItems: 'center', backgroundColor: theme.bg.mainContentBanner, - color: 'white', + color: theme.colorTokens.Neutrals.White, display: 'flex', justifyContent: 'space-between', position: 'sticky', diff --git a/packages/manager/src/components/MultipleIPInput/MultipleIPInput.tsx b/packages/manager/src/components/MultipleIPInput/MultipleIPInput.tsx index 5c52662a742..51c28be7adf 100644 --- a/packages/manager/src/components/MultipleIPInput/MultipleIPInput.tsx +++ b/packages/manager/src/components/MultipleIPInput/MultipleIPInput.tsx @@ -27,7 +27,7 @@ const useStyles = makeStyles()((theme: Theme) => ({ button: { '& :hover, & :focus': { backgroundColor: theme.palette.primary.main, - color: 'white', + color: theme.colorTokens.Neutrals.White, }, '& > span': { padding: 2, diff --git a/packages/manager/src/components/Notice/Notice.styles.ts b/packages/manager/src/components/Notice/Notice.styles.ts index b27f9b9a8a5..39897ece9bb 100644 --- a/packages/manager/src/components/Notice/Notice.styles.ts +++ b/packages/manager/src/components/Notice/Notice.styles.ts @@ -16,7 +16,7 @@ export const useStyles = makeStyles< borderLeft: `5px solid ${theme.palette.error.dark}`, }, icon: { - color: 'white', + color: theme.colorTokens.Neutrals.White, left: -25, // This value must be static regardless of theme selection position: 'absolute', }, diff --git a/packages/manager/src/components/Placeholder/Placeholder.tsx b/packages/manager/src/components/Placeholder/Placeholder.tsx index bdfff2a7e72..11550cec83a 100644 --- a/packages/manager/src/components/Placeholder/Placeholder.tsx +++ b/packages/manager/src/components/Placeholder/Placeholder.tsx @@ -96,14 +96,20 @@ export const Placeholder = (props: PlaceholderProps) => { fill: theme.palette.primary.main, }, '& .circle': { - fill: theme.name === 'light' ? theme.colorTokens.Neutrals.White : '#000', + fill: + theme.name === 'light' + ? theme.colorTokens.Neutrals.White + : theme.colorTokens.Neutrals.Black, }, '& .insidePath path': { opacity: 0, stroke: theme.palette.primary.main, }, '& .outerCircle': { - fill: theme.name === 'light' ? theme.colorTokens.Neutrals.White : '#000', + fill: + theme.name === 'light' + ? theme.colorTokens.Neutrals.White + : theme.colorTokens.Neutrals.Black, stroke: theme.bg.offWhite, }, height: '160px', diff --git a/packages/manager/src/components/PrimaryNav/PrimaryNav.styles.ts b/packages/manager/src/components/PrimaryNav/PrimaryNav.styles.ts index 2455532add4..b17b6479103 100644 --- a/packages/manager/src/components/PrimaryNav/PrimaryNav.styles.ts +++ b/packages/manager/src/components/PrimaryNav/PrimaryNav.styles.ts @@ -69,7 +69,7 @@ const useStyles = makeStyles()( fill: theme.palette.success.dark, }, [`& .${classes.linkItem}`]: { - color: 'white', + color: theme.colorTokens.Neutrals.White, }, backgroundImage: 'linear-gradient(98deg, #38584B 1%, #3A5049 166%)', border: 'red', diff --git a/packages/manager/src/components/PromotionalOfferCard/PromotionalOfferCard.tsx b/packages/manager/src/components/PromotionalOfferCard/PromotionalOfferCard.tsx index 982bf47ec0f..51dbba25cfe 100644 --- a/packages/manager/src/components/PromotionalOfferCard/PromotionalOfferCard.tsx +++ b/packages/manager/src/components/PromotionalOfferCard/PromotionalOfferCard.tsx @@ -18,10 +18,10 @@ const useStyles = makeStyles()((theme: Theme) => ({ button: { '&:hover, &:focus': { backgroundColor: '#3f8a4e', - color: 'white', + color: theme.colorTokens.Neutrals.White, }, backgroundColor: '#4FAD62', - color: 'white', + color: theme.colorTokens.Neutrals.White, marginLeft: theme.spacing(2), marginRight: theme.spacing(2), textAlign: 'center', diff --git a/packages/manager/src/components/ShowMore/ShowMore.tsx b/packages/manager/src/components/ShowMore/ShowMore.tsx index db2ead1512f..c9df4de1053 100644 --- a/packages/manager/src/components/ShowMore/ShowMore.tsx +++ b/packages/manager/src/components/ShowMore/ShowMore.tsx @@ -34,7 +34,7 @@ export const ShowMore = (props: ShowMoreProps) => { anchorEl ? { backgroundColor: theme.palette.primary.main, - color: 'white', + color: theme.colorTokens.Neutrals.White, } : null } @@ -72,7 +72,7 @@ const StyledChip = styled(Chip)(({ theme }) => ({ }, '&:hover': { backgroundColor: theme.palette.primary.main, - color: 'white', + color: theme.colorTokens.Neutrals.White, }, backgroundColor: theme.bg.lightBlue1, fontFamily: theme.font.bold, diff --git a/packages/manager/src/components/Tag/Tag.styles.ts b/packages/manager/src/components/Tag/Tag.styles.ts index 582b8f8dde1..46a7e37e0be 100644 --- a/packages/manager/src/components/Tag/Tag.styles.ts +++ b/packages/manager/src/components/Tag/Tag.styles.ts @@ -48,9 +48,9 @@ export const StyledChip = styled(Chip, { '& > span': { '&:hover, &:focus': { backgroundColor: theme.palette.primary.main, - color: 'white', + color: theme.colorTokens.Neutrals.White, }, - color: 'white', + color: theme.colorTokens.Neutrals.White, }, backgroundColor: theme.palette.primary.main, diff --git a/packages/manager/src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/GooglePayButton.tsx b/packages/manager/src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/GooglePayButton.tsx index 8efdd8da286..d7a1c875fc8 100644 --- a/packages/manager/src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/GooglePayButton.tsx +++ b/packages/manager/src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/GooglePayButton.tsx @@ -34,7 +34,9 @@ const useStyles = makeStyles()((theme: Theme) => ({ }, alignItems: 'center', backgroundColor: - theme.name === 'light' ? '#000' : theme.colorTokens.Neutrals.White, + theme.name === 'light' + ? theme.colorTokens.Neutrals.Black + : theme.colorTokens.Neutrals.White, border: 0, borderRadius: 4, cursor: 'pointer', diff --git a/packages/manager/src/features/Databases/DatabaseCreate/EngineOption.tsx b/packages/manager/src/features/Databases/DatabaseCreate/EngineOption.tsx index c8815efebba..23e81e4373e 100644 --- a/packages/manager/src/features/Databases/DatabaseCreate/EngineOption.tsx +++ b/packages/manager/src/features/Databases/DatabaseCreate/EngineOption.tsx @@ -10,7 +10,7 @@ import type { OptionProps } from 'react-select'; const useStyles = makeStyles()((theme: Theme) => ({ focused: { backgroundColor: theme.palette.primary.main, - color: 'white', + color: theme.colorTokens.Neutrals.White, }, root: { padding: theme.spacing(1), diff --git a/packages/manager/src/features/Kubernetes/CreateCluster/ApplicationPlatform.tsx b/packages/manager/src/features/Kubernetes/CreateCluster/ApplicationPlatform.tsx index f1468a34c2b..484a1388857 100644 --- a/packages/manager/src/features/Kubernetes/CreateCluster/ApplicationPlatform.tsx +++ b/packages/manager/src/features/Kubernetes/CreateCluster/ApplicationPlatform.tsx @@ -38,7 +38,10 @@ export const ApplicationPlatform = (props: APLProps) => { ({ '&&.MuiFormLabel-root.Mui-focused': { - color: theme.name === 'dark' ? 'white' : theme.color.black, + color: + theme.name === 'dark' + ? theme.colorTokens.Neutrals.White + : theme.color.black, }, })} > diff --git a/packages/manager/src/features/Kubernetes/CreateCluster/HAControlPlane.tsx b/packages/manager/src/features/Kubernetes/CreateCluster/HAControlPlane.tsx index a687617bd6d..69a30365bbf 100644 --- a/packages/manager/src/features/Kubernetes/CreateCluster/HAControlPlane.tsx +++ b/packages/manager/src/features/Kubernetes/CreateCluster/HAControlPlane.tsx @@ -61,7 +61,10 @@ export const HAControlPlane = (props: HAControlPlaneProps) => { ({ '&&.MuiFormLabel-root.Mui-focused': { - color: theme.name === 'dark' ? 'white' : theme.color.black, + color: + theme.name === 'dark' + ? theme.colorTokens.Neutrals.White + : theme.color.black, }, })} id="ha-radio-buttons-group-label" diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Marketplace/AppDetailDrawer.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Marketplace/AppDetailDrawer.tsx index 10fa1e88aed..67e8dc2616b 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Marketplace/AppDetailDrawer.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Marketplace/AppDetailDrawer.tsx @@ -23,7 +23,7 @@ const useStyles = makeStyles()((theme: Theme) => ({ textAlign: 'center', }, button: { - color: 'white !important', + color: `${theme.colorTokens.Neutrals.White} !important`, margin: theme.spacing(2), position: 'absolute', }, diff --git a/packages/manager/src/features/TopMenu/SearchBar/SearchSuggestion.styles.ts b/packages/manager/src/features/TopMenu/SearchBar/SearchSuggestion.styles.ts index ae369de459a..0781f6736b6 100644 --- a/packages/manager/src/features/TopMenu/SearchBar/SearchSuggestion.styles.ts +++ b/packages/manager/src/features/TopMenu/SearchBar/SearchSuggestion.styles.ts @@ -24,7 +24,7 @@ export const StyledWrapperDiv = styled('div', { '& .tag': { '&:hover': { backgroundColor: theme.palette.primary.main, - color: 'white', + color: theme.colorTokens.Neutrals.White, }, backgroundColor: theme.bg.lightBlue1, color: theme.palette.text.primary, diff --git a/packages/manager/src/features/TopMenu/TopMenu.tsx b/packages/manager/src/features/TopMenu/TopMenu.tsx index 5f3a4406f36..a5d00f85bce 100644 --- a/packages/manager/src/features/TopMenu/TopMenu.tsx +++ b/packages/manager/src/features/TopMenu/TopMenu.tsx @@ -41,7 +41,10 @@ export const TopMenu = React.memo((props: TopMenuProps) => { {loggedInAsCustomer && ( - + theme.colorTokens.Neutrals.Black} + fontSize="1.2em" + > You are logged in as customer: {username} diff --git a/packages/ui/src/foundations/themes/light.ts b/packages/ui/src/foundations/themes/light.ts index ca316fe95e2..e763a965176 100644 --- a/packages/ui/src/foundations/themes/light.ts +++ b/packages/ui/src/foundations/themes/light.ts @@ -131,7 +131,7 @@ const iconCircleAnimation = { transition: 'fill .2s ease-in-out .2s', }, '& .insidePath *': { - stroke: 'white', + stroke: Color.Neutrals.White, transition: 'fill .2s ease-in-out .2s, stroke .2s ease-in-out .2s', }, '& .outerCircle': { @@ -147,7 +147,7 @@ const iconCircleHoverEffect = { fill: primaryColors.main, }, '& .insidePath *': { - stroke: 'white', + stroke: Color.Neutrals.White, }, }; @@ -1134,7 +1134,7 @@ export const lightTheme: ThemeOptions = { MuiSnackbarContent: { styleOverrides: { root: { - backgroundColor: 'white', + backgroundColor: Color.Neutrals.White, borderLeft: `6px solid transparent`, borderRadius: 4, boxShadow: `0 0 5px ${Color.Neutrals[30]}`, @@ -1156,7 +1156,7 @@ export const lightTheme: ThemeOptions = { root: { '& $checked': { '& .square': { - fill: 'white', + fill: Color.Neutrals.White, }, // color: `${primaryColors.main} !important`, '& input': { @@ -1175,7 +1175,7 @@ export const lightTheme: ThemeOptions = { borderColor: Color.Neutrals[40], }, '& .square': { - fill: 'white', + fill: Color.Neutrals.White, }, }, }, @@ -1188,7 +1188,7 @@ export const lightTheme: ThemeOptions = { width: 16, }, '& .square': { - fill: 'white', + fill: Color.Neutrals.White, transition: 'fill 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms', }, '&:hover, &:focus': { @@ -1406,7 +1406,7 @@ export const lightTheme: ThemeOptions = { opacity: 1, }, tooltip: { - backgroundColor: 'white', + backgroundColor: Color.Neutrals.White, borderRadius: 0, boxShadow: `0 0 5px ${Color.Neutrals[50]}`, // TODO: This was the closest color according to our palette [breakpoints.up('sm')]: { From edf94388c4b9d7483978853f059d132e54e75f9f Mon Sep 17 00:00:00 2001 From: Purvesh Makode Date: Tue, 29 Oct 2024 19:30:52 +0530 Subject: [PATCH 17/66] upcoming: [M3-8755] - Add global `font` and `spacing` tokens to theme and refactor design tokens (#11171) * Add global fontTokens and spacingTokens to theme * Refactor design tokens * Added changeset: Add global `font` and `spacing` tokens to theme and refactor design tokens * Refactor chart and interaction tokens --- ...r-11171-upcoming-features-1730182630692.md | 5 ++++ packages/manager/src/GoTo.tsx | 2 +- packages/manager/src/components/Accordion.tsx | 2 +- .../AkamaiBanner/AkamaiBanner.styles.ts | 10 +++---- .../components/ColorPalette/ColorPalette.tsx | 2 +- .../CopyableTextField/CopyableTextField.tsx | 2 +- packages/manager/src/components/Drawer.tsx | 2 +- .../EnhancedSelect/Select.styles.ts | 10 +++---- .../components/ImageSelect/ImageOption.tsx | 6 ++--- .../src/components/MainContentBanner.tsx | 2 +- .../MultipleIPInput/MultipleIPInput.tsx | 2 +- .../src/components/Notice/Notice.styles.ts | 2 +- .../components/Placeholder/Placeholder.tsx | 8 +++--- .../PrimaryNav/PrimaryNav.styles.ts | 4 +-- .../PromotionalOfferCard.tsx | 4 +-- .../RegionSelect/RegionSelect.styles.ts | 4 +-- .../src/components/ShowMore/ShowMore.tsx | 4 +-- .../manager/src/components/Tag/Tag.styles.ts | 6 ++--- .../src/components/TagCell/TagCell.tsx | 2 +- .../manager/src/dev-tools/Preferences.tsx | 2 +- .../PaymentDrawer/GooglePayButton.tsx | 6 ++--- .../features/CancelLanding/CancelLanding.tsx | 2 +- .../Databases/DatabaseCreate/EngineOption.tsx | 2 +- .../DatabaseSummaryConnectionDetails.style.ts | 2 +- .../CreateCluster/ApplicationPlatform.tsx | 2 +- .../CreateCluster/HAControlPlane.tsx | 2 +- .../Tabs/Marketplace/AppDetailDrawer.tsx | 4 +-- .../Linodes/LinodeEntityDetail.styles.ts | 4 +-- .../TwoFactor/QRCodeForm.tsx | 2 +- .../SearchBar/SearchSuggestion.styles.ts | 2 +- .../manager/src/features/TopMenu/TopMenu.tsx | 2 +- .../VPCs/VPCDetail/VPCDetail.styles.ts | 2 +- .../PlansPanel/PlanSelection.styles.ts | 2 +- packages/ui/src/foundations/breakpoints.ts | 2 +- packages/ui/src/foundations/themes/dark.ts | 5 +++- packages/ui/src/foundations/themes/index.ts | 26 ++++++++++++++----- packages/ui/src/foundations/themes/light.ts | 12 ++++++--- 37 files changed, 94 insertions(+), 66 deletions(-) create mode 100644 packages/manager/.changeset/pr-11171-upcoming-features-1730182630692.md diff --git a/packages/manager/.changeset/pr-11171-upcoming-features-1730182630692.md b/packages/manager/.changeset/pr-11171-upcoming-features-1730182630692.md new file mode 100644 index 00000000000..69f6fb0cfd5 --- /dev/null +++ b/packages/manager/.changeset/pr-11171-upcoming-features-1730182630692.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Add global `font` and `spacing` tokens to theme and refactor design tokens ([#11171](https://github.com/linode/manager/pull/11171)) diff --git a/packages/manager/src/GoTo.tsx b/packages/manager/src/GoTo.tsx index 9403a5a0041..d1d4e34952a 100644 --- a/packages/manager/src/GoTo.tsx +++ b/packages/manager/src/GoTo.tsx @@ -42,7 +42,7 @@ const useStyles = makeStyles()((theme: Theme) => ({ }, '& .react-select__option--is-focused': { backgroundColor: theme.palette.primary.main, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, '& .react-select__value-container': { '& p': { diff --git a/packages/manager/src/components/Accordion.tsx b/packages/manager/src/components/Accordion.tsx index e3c7133630e..6302d388d1a 100644 --- a/packages/manager/src/components/Accordion.tsx +++ b/packages/manager/src/components/Accordion.tsx @@ -23,7 +23,7 @@ const useStyles = makeStyles()((theme: Theme) => ({ alignItems: 'center', backgroundColor: '#2575d0', borderRadius: '50%', - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, display: 'flex', fontFamily: theme.font.bold, fontSize: '0.875rem', diff --git a/packages/manager/src/components/AkamaiBanner/AkamaiBanner.styles.ts b/packages/manager/src/components/AkamaiBanner/AkamaiBanner.styles.ts index 99cbf589211..39dc67c1658 100644 --- a/packages/manager/src/components/AkamaiBanner/AkamaiBanner.styles.ts +++ b/packages/manager/src/components/AkamaiBanner/AkamaiBanner.styles.ts @@ -11,7 +11,7 @@ export const StyledAkamaiLogo = styled(AkamaiLogo, { label: 'StyledAkamaiLogo', })(({ theme }) => ({ '& .akamai-logo-icon': { - fill: theme.colorTokens.Neutrals.White, + fill: theme.tokens.color.Neutrals.White, }, '& .akamai-logo-name': { display: 'none', @@ -21,7 +21,7 @@ export const StyledAkamaiLogo = styled(AkamaiLogo, { export const StyledWarningIcon = styled(Warning, { label: 'StyledWarningIcon', })(({ theme }) => ({ - color: theme.colorTokens.Neutrals.Black, + color: theme.tokens.color.Neutrals.Black, })); export const StyledBanner = styled(Stack, { @@ -41,10 +41,10 @@ export const StyledBannerLabel = styled(Box, { })<{ warning?: boolean }>(({ theme, warning }) => ({ backgroundColor: warning ? theme.palette.warning.dark - : theme.colorTokens.Neutrals.Black, + : theme.tokens.color.Neutrals.Black, color: warning - ? theme.colorTokens.Neutrals.Black - : theme.colorTokens.Neutrals.White, + ? theme.tokens.color.Neutrals.Black + : theme.tokens.color.Neutrals.White, padding: theme.spacing(2.3), [theme.breakpoints.up('sm')]: { textWrap: 'nowrap', diff --git a/packages/manager/src/components/ColorPalette/ColorPalette.tsx b/packages/manager/src/components/ColorPalette/ColorPalette.tsx index 3a9f2bc1263..415b31e10e1 100644 --- a/packages/manager/src/components/ColorPalette/ColorPalette.tsx +++ b/packages/manager/src/components/ColorPalette/ColorPalette.tsx @@ -50,7 +50,7 @@ const useStyles = makeStyles()((theme: Theme) => ({ * * If a color does not exist in the current palette and is only used once, consider applying the color conditionally: * - * `theme.name === 'light' ? theme.colorTokens.Neutrals.White : theme.colorTokens.Neutrals.Black` + * `theme.name === 'light' ? theme.tokens.color.Neutrals.White : theme.tokens.color.Neutrals.Black` */ export const ColorPalette = () => { const { classes } = useStyles(); diff --git a/packages/manager/src/components/CopyableTextField/CopyableTextField.tsx b/packages/manager/src/components/CopyableTextField/CopyableTextField.tsx index 6dea1154ba6..fbccbae4da6 100644 --- a/packages/manager/src/components/CopyableTextField/CopyableTextField.tsx +++ b/packages/manager/src/components/CopyableTextField/CopyableTextField.tsx @@ -65,7 +65,7 @@ const StyledTextField = styled(TextField)(({ theme }) => ({ color: theme.name === 'light' ? `${theme.palette.text.primary} !important` - : `${theme.colorTokens.Neutrals.White} !important`, + : `${theme.tokens.color.Neutrals.White} !important`, opacity: theme.name === 'dark' ? 0.5 : 0.8, }, '&& .MuiInput-root': { diff --git a/packages/manager/src/components/Drawer.tsx b/packages/manager/src/components/Drawer.tsx index 3abb4a91519..07da8da9af5 100644 --- a/packages/manager/src/components/Drawer.tsx +++ b/packages/manager/src/components/Drawer.tsx @@ -31,7 +31,7 @@ const useStyles = makeStyles()((theme: Theme) => ({ button: { '& :hover, & :focus': { backgroundColor: theme.palette.primary.main, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, '& > span': { padding: 2, diff --git a/packages/manager/src/components/EnhancedSelect/Select.styles.ts b/packages/manager/src/components/EnhancedSelect/Select.styles.ts index b3a40de671e..bbd0ba1b9c0 100644 --- a/packages/manager/src/components/EnhancedSelect/Select.styles.ts +++ b/packages/manager/src/components/EnhancedSelect/Select.styles.ts @@ -169,7 +169,7 @@ export const useStyles = makeStyles()((theme: Theme) => ({ }, '&:hover': { '& svg': { - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, backgroundColor: theme.palette.primary.main, }, @@ -202,7 +202,7 @@ export const useStyles = makeStyles()((theme: Theme) => ({ }, '& .react-select__option--is-focused': { backgroundColor: theme.palette.primary.main, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, '& .react-select__option--is-selected': { '&.react-select__option--is-focused': { @@ -251,7 +251,7 @@ export const useStyles = makeStyles()((theme: Theme) => ({ '& .tag': { '&:hover': { backgroundColor: theme.palette.primary.main, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, backgroundColor: theme.bg.lightBlue1, color: theme.palette.text.primary, @@ -377,7 +377,7 @@ export const reactSelectStyles = (theme: Theme) => ({ }, '&:hover': { '& svg': { - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, backgroundColor: theme.palette.primary.main, }, @@ -408,7 +408,7 @@ export const reactSelectStyles = (theme: Theme) => ({ return { ...optionStyles, backgroundColor: theme.palette.primary.main, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }; } if (state.isSelected) { diff --git a/packages/manager/src/components/ImageSelect/ImageOption.tsx b/packages/manager/src/components/ImageSelect/ImageOption.tsx index 757f1c58f8e..f1fe9a3ac76 100644 --- a/packages/manager/src/components/ImageSelect/ImageOption.tsx +++ b/packages/manager/src/components/ImageSelect/ImageOption.tsx @@ -25,10 +25,10 @@ const useStyles = makeStyles()((theme: Theme) => ({ }, focused: { '& g': { - fill: theme.colorTokens.Neutrals.White, + fill: theme.tokens.color.Neutrals.White, }, backgroundColor: theme.palette.primary.main, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, root: { '& *': { @@ -36,7 +36,7 @@ const useStyles = makeStyles()((theme: Theme) => ({ }, '& g': { fill: - theme.name === 'dark' ? theme.colorTokens.Neutrals.White : '#888f91', + theme.name === 'dark' ? theme.tokens.color.Neutrals.White : '#888f91', }, display: 'flex !important', flexDirection: 'row', diff --git a/packages/manager/src/components/MainContentBanner.tsx b/packages/manager/src/components/MainContentBanner.tsx index 5909c16b2c1..dc1cfb4f56b 100644 --- a/packages/manager/src/components/MainContentBanner.tsx +++ b/packages/manager/src/components/MainContentBanner.tsx @@ -66,7 +66,7 @@ export const MainContentBanner = React.memo(() => { sx={(theme) => ({ alignItems: 'center', backgroundColor: theme.bg.mainContentBanner, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, display: 'flex', justifyContent: 'space-between', position: 'sticky', diff --git a/packages/manager/src/components/MultipleIPInput/MultipleIPInput.tsx b/packages/manager/src/components/MultipleIPInput/MultipleIPInput.tsx index 51c28be7adf..fd6eb1a54d7 100644 --- a/packages/manager/src/components/MultipleIPInput/MultipleIPInput.tsx +++ b/packages/manager/src/components/MultipleIPInput/MultipleIPInput.tsx @@ -27,7 +27,7 @@ const useStyles = makeStyles()((theme: Theme) => ({ button: { '& :hover, & :focus': { backgroundColor: theme.palette.primary.main, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, '& > span': { padding: 2, diff --git a/packages/manager/src/components/Notice/Notice.styles.ts b/packages/manager/src/components/Notice/Notice.styles.ts index 39897ece9bb..440ca58a017 100644 --- a/packages/manager/src/components/Notice/Notice.styles.ts +++ b/packages/manager/src/components/Notice/Notice.styles.ts @@ -16,7 +16,7 @@ export const useStyles = makeStyles< borderLeft: `5px solid ${theme.palette.error.dark}`, }, icon: { - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, left: -25, // This value must be static regardless of theme selection position: 'absolute', }, diff --git a/packages/manager/src/components/Placeholder/Placeholder.tsx b/packages/manager/src/components/Placeholder/Placeholder.tsx index 11550cec83a..891cc6dddf6 100644 --- a/packages/manager/src/components/Placeholder/Placeholder.tsx +++ b/packages/manager/src/components/Placeholder/Placeholder.tsx @@ -98,8 +98,8 @@ export const Placeholder = (props: PlaceholderProps) => { '& .circle': { fill: theme.name === 'light' - ? theme.colorTokens.Neutrals.White - : theme.colorTokens.Neutrals.Black, + ? theme.tokens.color.Neutrals.White + : theme.tokens.color.Neutrals.Black, }, '& .insidePath path': { opacity: 0, @@ -108,8 +108,8 @@ export const Placeholder = (props: PlaceholderProps) => { '& .outerCircle': { fill: theme.name === 'light' - ? theme.colorTokens.Neutrals.White - : theme.colorTokens.Neutrals.Black, + ? theme.tokens.color.Neutrals.White + : theme.tokens.color.Neutrals.Black, stroke: theme.bg.offWhite, }, height: '160px', diff --git a/packages/manager/src/components/PrimaryNav/PrimaryNav.styles.ts b/packages/manager/src/components/PrimaryNav/PrimaryNav.styles.ts index b17b6479103..df742cde809 100644 --- a/packages/manager/src/components/PrimaryNav/PrimaryNav.styles.ts +++ b/packages/manager/src/components/PrimaryNav/PrimaryNav.styles.ts @@ -29,7 +29,7 @@ const useStyles = makeStyles()( opacity: 0, }, alignItems: 'center', - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, display: 'flex', fontFamily: 'LatoWebBold', fontSize: '0.875rem', @@ -69,7 +69,7 @@ const useStyles = makeStyles()( fill: theme.palette.success.dark, }, [`& .${classes.linkItem}`]: { - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, backgroundImage: 'linear-gradient(98deg, #38584B 1%, #3A5049 166%)', border: 'red', diff --git a/packages/manager/src/components/PromotionalOfferCard/PromotionalOfferCard.tsx b/packages/manager/src/components/PromotionalOfferCard/PromotionalOfferCard.tsx index 51dbba25cfe..baacd9c737c 100644 --- a/packages/manager/src/components/PromotionalOfferCard/PromotionalOfferCard.tsx +++ b/packages/manager/src/components/PromotionalOfferCard/PromotionalOfferCard.tsx @@ -18,10 +18,10 @@ const useStyles = makeStyles()((theme: Theme) => ({ button: { '&:hover, &:focus': { backgroundColor: '#3f8a4e', - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, backgroundColor: '#4FAD62', - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, marginLeft: theme.spacing(2), marginRight: theme.spacing(2), textAlign: 'center', diff --git a/packages/manager/src/components/RegionSelect/RegionSelect.styles.ts b/packages/manager/src/components/RegionSelect/RegionSelect.styles.ts index 50369c7f418..0b4c18cff01 100644 --- a/packages/manager/src/components/RegionSelect/RegionSelect.styles.ts +++ b/packages/manager/src/components/RegionSelect/RegionSelect.styles.ts @@ -115,10 +115,10 @@ export const StyledChip = styled(Chip)(({ theme }) => ({ }, '& .MuiChip-deleteIcon.MuiSvgIcon-root': { '&:hover': { - backgroundColor: theme.colorTokens.Neutrals.White, + backgroundColor: theme.tokens.color.Neutrals.White, color: '#3683dc', }, backgroundColor: '#3683dc', - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, })); diff --git a/packages/manager/src/components/ShowMore/ShowMore.tsx b/packages/manager/src/components/ShowMore/ShowMore.tsx index c9df4de1053..600a4399a74 100644 --- a/packages/manager/src/components/ShowMore/ShowMore.tsx +++ b/packages/manager/src/components/ShowMore/ShowMore.tsx @@ -34,7 +34,7 @@ export const ShowMore = (props: ShowMoreProps) => { anchorEl ? { backgroundColor: theme.palette.primary.main, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, } : null } @@ -72,7 +72,7 @@ const StyledChip = styled(Chip)(({ theme }) => ({ }, '&:hover': { backgroundColor: theme.palette.primary.main, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, backgroundColor: theme.bg.lightBlue1, fontFamily: theme.font.bold, diff --git a/packages/manager/src/components/Tag/Tag.styles.ts b/packages/manager/src/components/Tag/Tag.styles.ts index 46a7e37e0be..4f61d0f25a7 100644 --- a/packages/manager/src/components/Tag/Tag.styles.ts +++ b/packages/manager/src/components/Tag/Tag.styles.ts @@ -48,9 +48,9 @@ export const StyledChip = styled(Chip, { '& > span': { '&:hover, &:focus': { backgroundColor: theme.palette.primary.main, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, backgroundColor: theme.palette.primary.main, @@ -92,7 +92,7 @@ export const StyledDeleteButton = styled(StyledLinkButton, { }, borderBottomRightRadius: 3, borderLeft: `1px solid ${ - theme.name === 'light' ? theme.colorTokens.Neutrals.White : '#2e3238' + theme.name === 'light' ? theme.tokens.color.Neutrals.White : '#2e3238' }`, borderRadius: 0, borderTopRightRadius: 3, diff --git a/packages/manager/src/components/TagCell/TagCell.tsx b/packages/manager/src/components/TagCell/TagCell.tsx index e3f85c9140f..3700acad19d 100644 --- a/packages/manager/src/components/TagCell/TagCell.tsx +++ b/packages/manager/src/components/TagCell/TagCell.tsx @@ -235,7 +235,7 @@ const StyledTag = styled(Tag, { const StyledIconButton = styled(IconButton)(({ theme }) => ({ '&:hover': { backgroundColor: theme.palette.primary.main, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, backgroundColor: theme.color.tagButtonBg, borderRadius: 0, diff --git a/packages/manager/src/dev-tools/Preferences.tsx b/packages/manager/src/dev-tools/Preferences.tsx index 9fdd92e2928..d0cbd1ba0ca 100644 --- a/packages/manager/src/dev-tools/Preferences.tsx +++ b/packages/manager/src/dev-tools/Preferences.tsx @@ -9,7 +9,7 @@ export const Preferences = () => {

Preferences

Open preference Modal ({ button: { '& svg': { color: - theme.name === 'light' ? theme.colorTokens.Neutrals.White : '#616161', + theme.name === 'light' ? theme.tokens.color.Neutrals.White : '#616161', height: 16, }, '&:hover': { @@ -35,8 +35,8 @@ const useStyles = makeStyles()((theme: Theme) => ({ alignItems: 'center', backgroundColor: theme.name === 'light' - ? theme.colorTokens.Neutrals.Black - : theme.colorTokens.Neutrals.White, + ? theme.tokens.color.Neutrals.Black + : theme.tokens.color.Neutrals.White, border: 0, borderRadius: 4, cursor: 'pointer', diff --git a/packages/manager/src/features/CancelLanding/CancelLanding.tsx b/packages/manager/src/features/CancelLanding/CancelLanding.tsx index 9056ddbde6a..a6538b21842 100644 --- a/packages/manager/src/features/CancelLanding/CancelLanding.tsx +++ b/packages/manager/src/features/CancelLanding/CancelLanding.tsx @@ -16,7 +16,7 @@ const useStyles = makeStyles()((theme: Theme) => ({ root: { '& button': { backgroundColor: '#00b159', - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, marginTop: theme.spacing(8), }, '& h1': { diff --git a/packages/manager/src/features/Databases/DatabaseCreate/EngineOption.tsx b/packages/manager/src/features/Databases/DatabaseCreate/EngineOption.tsx index 23e81e4373e..b989f747fdb 100644 --- a/packages/manager/src/features/Databases/DatabaseCreate/EngineOption.tsx +++ b/packages/manager/src/features/Databases/DatabaseCreate/EngineOption.tsx @@ -10,7 +10,7 @@ import type { OptionProps } from 'react-select'; const useStyles = makeStyles()((theme: Theme) => ({ focused: { backgroundColor: theme.palette.primary.main, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, root: { padding: theme.spacing(1), diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryConnectionDetails.style.ts b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryConnectionDetails.style.ts index 325501935ca..a61db9661f8 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryConnectionDetails.style.ts +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryConnectionDetails.style.ts @@ -46,7 +46,7 @@ export const useStyles = makeStyles()((theme: Theme) => ({ '& span': { fontFamily: theme.font.bold, }, - background: theme.interactionTokens.Background.Secondary, + background: theme.tokens.interaction.Background.Secondary, border: `1px solid ${theme.name === 'light' ? '#ccc' : '#222'}`, padding: `${theme.spacing(1)} 15px`, }, diff --git a/packages/manager/src/features/Kubernetes/CreateCluster/ApplicationPlatform.tsx b/packages/manager/src/features/Kubernetes/CreateCluster/ApplicationPlatform.tsx index 484a1388857..ab910c26119 100644 --- a/packages/manager/src/features/Kubernetes/CreateCluster/ApplicationPlatform.tsx +++ b/packages/manager/src/features/Kubernetes/CreateCluster/ApplicationPlatform.tsx @@ -40,7 +40,7 @@ export const ApplicationPlatform = (props: APLProps) => { '&&.MuiFormLabel-root.Mui-focused': { color: theme.name === 'dark' - ? theme.colorTokens.Neutrals.White + ? theme.tokens.color.Neutrals.White : theme.color.black, }, })} diff --git a/packages/manager/src/features/Kubernetes/CreateCluster/HAControlPlane.tsx b/packages/manager/src/features/Kubernetes/CreateCluster/HAControlPlane.tsx index 69a30365bbf..8b62f016c87 100644 --- a/packages/manager/src/features/Kubernetes/CreateCluster/HAControlPlane.tsx +++ b/packages/manager/src/features/Kubernetes/CreateCluster/HAControlPlane.tsx @@ -63,7 +63,7 @@ export const HAControlPlane = (props: HAControlPlaneProps) => { '&&.MuiFormLabel-root.Mui-focused': { color: theme.name === 'dark' - ? theme.colorTokens.Neutrals.White + ? theme.tokens.color.Neutrals.White : theme.color.black, }, })} diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Marketplace/AppDetailDrawer.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Marketplace/AppDetailDrawer.tsx index 67e8dc2616b..152becd40cb 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Marketplace/AppDetailDrawer.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Marketplace/AppDetailDrawer.tsx @@ -16,14 +16,14 @@ import type { Theme } from '@mui/material/styles'; const useStyles = makeStyles()((theme: Theme) => ({ appName: { - color: `${theme.colorTokens.Neutrals.White} !important`, + color: `${theme.tokens.color.Neutrals.White} !important`, fontFamily: theme.font.bold, fontSize: '2.2rem', lineHeight: '2.5rem', textAlign: 'center', }, button: { - color: `${theme.colorTokens.Neutrals.White} !important`, + color: `${theme.tokens.color.Neutrals.White} !important`, margin: theme.spacing(2), position: 'absolute', }, diff --git a/packages/manager/src/features/Linodes/LinodeEntityDetail.styles.ts b/packages/manager/src/features/Linodes/LinodeEntityDetail.styles.ts index 47f29f03e5b..671c1a515de 100644 --- a/packages/manager/src/features/Linodes/LinodeEntityDetail.styles.ts +++ b/packages/manager/src/features/Linodes/LinodeEntityDetail.styles.ts @@ -158,7 +158,7 @@ export const StyledTableCell = styled(TableCell, { label: 'StyledTableCell' })( fontSize: 15, }, alignItems: 'center', - backgroundColor: theme.interactionTokens.Background.Secondary, + backgroundColor: theme.tokens.interaction.Background.Secondary, color: theme.textColors.tableStatic, display: 'flex', fontFamily: '"UbuntuMono", monospace, sans-serif', @@ -181,7 +181,7 @@ export const StyledCopyTooltip = styled(CopyTooltip, { export const StyledGradientDiv = styled('div', { label: 'StyledGradientDiv' })( ({ theme }) => ({ '&:after': { - backgroundImage: `linear-gradient(to right, ${theme.bg.bgAccessRowTransparentGradient}, ${theme.interactionTokens.Background.Secondary});`, + backgroundImage: `linear-gradient(to right, ${theme.bg.bgAccessRowTransparentGradient}, ${theme.tokens.interaction.Background.Secondary});`, bottom: 0, content: '""', height: '100%', diff --git a/packages/manager/src/features/Profile/AuthenticationSettings/TwoFactor/QRCodeForm.tsx b/packages/manager/src/features/Profile/AuthenticationSettings/TwoFactor/QRCodeForm.tsx index e2d79add7c6..cdd266234ad 100644 --- a/packages/manager/src/features/Profile/AuthenticationSettings/TwoFactor/QRCodeForm.tsx +++ b/packages/manager/src/features/Profile/AuthenticationSettings/TwoFactor/QRCodeForm.tsx @@ -42,7 +42,7 @@ const StyledInstructions = styled(Typography, { const StyledQRCodeContainer = styled('div', { label: 'StyledQRCodeContainer', })(({ theme }) => ({ - border: `5px solid ${theme.colorTokens.Neutrals.White}`, + border: `5px solid ${theme.tokens.color.Neutrals.White}`, display: 'inline-block', margin: `${theme.spacing(2)} 0`, })); diff --git a/packages/manager/src/features/TopMenu/SearchBar/SearchSuggestion.styles.ts b/packages/manager/src/features/TopMenu/SearchBar/SearchSuggestion.styles.ts index 0781f6736b6..eed1bb3881d 100644 --- a/packages/manager/src/features/TopMenu/SearchBar/SearchSuggestion.styles.ts +++ b/packages/manager/src/features/TopMenu/SearchBar/SearchSuggestion.styles.ts @@ -24,7 +24,7 @@ export const StyledWrapperDiv = styled('div', { '& .tag': { '&:hover': { backgroundColor: theme.palette.primary.main, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, backgroundColor: theme.bg.lightBlue1, color: theme.palette.text.primary, diff --git a/packages/manager/src/features/TopMenu/TopMenu.tsx b/packages/manager/src/features/TopMenu/TopMenu.tsx index a5d00f85bce..d494d8c1ff0 100644 --- a/packages/manager/src/features/TopMenu/TopMenu.tsx +++ b/packages/manager/src/features/TopMenu/TopMenu.tsx @@ -42,7 +42,7 @@ export const TopMenu = React.memo((props: TopMenuProps) => { {loggedInAsCustomer && ( theme.colorTokens.Neutrals.Black} + color={(theme) => theme.tokens.color.Neutrals.Black} fontSize="1.2em" > You are logged in as customer: {username} diff --git a/packages/manager/src/features/VPCs/VPCDetail/VPCDetail.styles.ts b/packages/manager/src/features/VPCs/VPCDetail/VPCDetail.styles.ts index 8a9e7640dd2..614b5e7a3a7 100644 --- a/packages/manager/src/features/VPCs/VPCDetail/VPCDetail.styles.ts +++ b/packages/manager/src/features/VPCs/VPCDetail/VPCDetail.styles.ts @@ -9,7 +9,7 @@ export const StyledActionButton = styled(Button, { })(({ theme }) => ({ '&:hover': { backgroundColor: theme.color.blue, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, }, color: theme.textColors.linkActiveLight, fontFamily: theme.font.normal, diff --git a/packages/manager/src/features/components/PlansPanel/PlanSelection.styles.ts b/packages/manager/src/features/components/PlansPanel/PlanSelection.styles.ts index 0a1970408ea..4c8c49d9bbb 100644 --- a/packages/manager/src/features/components/PlansPanel/PlanSelection.styles.ts +++ b/packages/manager/src/features/components/PlansPanel/PlanSelection.styles.ts @@ -6,7 +6,7 @@ import { TableCell } from 'src/components/TableCell'; export const StyledChip = styled(Chip, { label: 'StyledChip' })( ({ theme }) => ({ backgroundColor: theme.color.green, - color: theme.colorTokens.Neutrals.White, + color: theme.tokens.color.Neutrals.White, marginLeft: theme.spacing(), position: 'relative', textTransform: 'uppercase', diff --git a/packages/ui/src/foundations/breakpoints.ts b/packages/ui/src/foundations/breakpoints.ts index aa7273db3f4..be731cc5781 100644 --- a/packages/ui/src/foundations/breakpoints.ts +++ b/packages/ui/src/foundations/breakpoints.ts @@ -13,6 +13,6 @@ export const breakpoints = createTheme({ xs: 0, }, }, - chartTokens: Chart, + tokens: { chart: Chart }, name: 'light', }).breakpoints; diff --git a/packages/ui/src/foundations/themes/dark.ts b/packages/ui/src/foundations/themes/dark.ts index bf667b55cfa..e3b2f2daf9c 100644 --- a/packages/ui/src/foundations/themes/dark.ts +++ b/packages/ui/src/foundations/themes/dark.ts @@ -854,7 +854,6 @@ export const darkTheme: ThemeOptions = { color: Select.Hover.Text, }, }, - interactionTokens: Interaction, name: 'dark', notificationToast, palette: { @@ -875,6 +874,10 @@ export const darkTheme: ThemeOptions = { }, }, textColors: customDarkModeOptions.textColors, + tokens: { + // No need to add global tokens here, as they will be inherited from light.ts + interaction: Interaction, + }, typography: { body1: { color: primaryColors.text, diff --git a/packages/ui/src/foundations/themes/index.ts b/packages/ui/src/foundations/themes/index.ts index 757677dd594..3d8467fb158 100644 --- a/packages/ui/src/foundations/themes/index.ts +++ b/packages/ui/src/foundations/themes/index.ts @@ -8,7 +8,9 @@ import { lightTheme } from './light'; import type { ChartTypes, ColorTypes, + FontTypes, InteractionTypes as InteractionTypesLight, + SpacingTypes, } from '@linode/design-language-system'; import type { InteractionTypes as InteractionTypesDark } from '@linode/design-language-system/themes/dark'; import type { latoWeb } from '../fonts'; @@ -74,17 +76,23 @@ declare module '@mui/material/styles/createTheme' { applyTableHeaderStyles?: any; bg: BgColors; borderColors: BorderColors; - chartTokens: ChartTypes; - colorTokens: ColorTypes; // Global token: theme agnostic color: Colors; font: Fonts; graphs: any; inputMaxWidth: number; inputStyles: any; - interactionTokens: InteractionTypes; name: ThemeName; notificationToast: NotificationToast; textColors: TextColors; + tokens: { + // ---- Global tokens: theme agnostic ---- + color: ColorTypes; + font: FontTypes; + spacing: SpacingTypes; + // ---------------------------------------- + chart: ChartTypes; + interaction: InteractionTypes; + }; visually: any; } @@ -96,17 +104,23 @@ declare module '@mui/material/styles/createTheme' { applyTableHeaderStyles?: any; bg?: DarkModeBgColors | LightModeBgColors; borderColors?: DarkModeBorderColors | LightModeBorderColors; - chartTokens?: ChartTypes; - colorTokens?: ColorTypes; // Global token: theme agnostic color?: DarkModeColors | LightModeColors; font?: Fonts; graphs?: any; inputMaxWidth?: number; inputStyles?: any; - interactionTokens?: InteractionTypes; name: ThemeName; notificationToast?: NotificationToast; textColors?: DarkModeTextColors | LightModeTextColors; + tokens?: { + // ---- Global tokens: theme agnostic ---- + color?: ColorTypes; + font?: FontTypes; + spacing?: SpacingTypes; + // ---------------------------------------- + chart?: ChartTypes; + interaction?: InteractionTypes; + }; visually?: any; } } diff --git a/packages/ui/src/foundations/themes/light.ts b/packages/ui/src/foundations/themes/light.ts index e763a965176..6082f5c2eab 100644 --- a/packages/ui/src/foundations/themes/light.ts +++ b/packages/ui/src/foundations/themes/light.ts @@ -5,9 +5,11 @@ import { Chart, Color, Dropdown, + Font, Interaction, NotificationToast, Select, + Spacing, } from '@linode/design-language-system'; import { breakpoints } from '../breakpoints'; @@ -239,8 +241,6 @@ export const lightTheme: ThemeOptions = { bg, borderColors, breakpoints, - chartTokens: Chart, - colorTokens: Color, color, components: { MuiAccordion: { @@ -1568,7 +1568,6 @@ export const lightTheme: ThemeOptions = { color: Select.Hover.Text, }, }, - interactionTokens: Interaction, name: 'light', // @todo remove this because we leverage pallete.mode now notificationToast, palette: { @@ -1632,6 +1631,13 @@ export const lightTheme: ThemeOptions = { ], spacing, textColors, + tokens: { + color: Color, + font: Font, + spacing: Spacing, + chart: Chart, + interaction: Interaction, + }, typography: { body1: { color: primaryColors.text, From 7a17a78f90c24e7417aae9d1484379bcaf9b1d30 Mon Sep 17 00:00:00 2001 From: Dajahi Wiley <114682940+dwiley-akamai@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:09:08 -0400 Subject: [PATCH 18/66] =?UTF-8?q?refactor:=20[M3-8648]=20=E2=80=93=20Migra?= =?UTF-8?q?te=20several=20components=20to=20`ui`=20package=20prior=20to=20?= =?UTF-8?q?migrating=20`Paper`=20(#11159)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/Autocomplete/Autocomplete.tsx | 2 +- .../DebouncedSearchTextField.tsx | 3 +-- .../src/components/FormLabel.stories.tsx | 5 +++-- .../MultipleIPInput/MultipleIPInput.tsx | 2 +- packages/manager/src/components/Paper.tsx | 7 ++++--- .../src/components/TextField.stories.tsx | 5 +++-- .../manager/src/components/TextField.test.tsx | 2 +- packages/manager/src/components/TextField.tsx | 4 +--- .../PaymentDrawer/PaymentDrawer.tsx | 11 ++++++----- .../DatabaseCreate/DatabaseCreate.tsx | 2 +- .../DatabaseSettings/MaintenanceWindow.tsx | 16 +++++++++------- .../Domains/CreateDomain/CreateDomain.tsx | 2 +- .../SupportSearchLanding.tsx | 2 +- .../Images/ImagesLanding/ImagesLanding.tsx | 3 +-- .../CreateCluster/ApplicationPlatform.tsx | 2 +- .../CreateCluster/ControlPlaneACLPane.tsx | 2 +- .../CreateCluster/HAControlPlane.tsx | 2 +- .../StackScripts/StackScriptSelectionList.tsx | 3 +-- .../UserDefinedFieldInput.tsx | 2 +- .../LinodeBackup/CaptureSnapshot.tsx | 2 +- .../LinodeBackup/RestoreToLinodeDrawer.tsx | 4 ++-- .../LinodeBackup/ScheduleSettings.tsx | 4 ++-- .../LinodeConfigs/LinodeConfigDialog.styles.ts | 2 +- .../LinodeConfigs/LinodeConfigDialog.tsx | 4 ++-- .../LinodeRescue/DeviceSelection.tsx | 2 +- .../LinodeResizeUnifiedMigrationPanel.tsx | 2 +- .../LinodeSettings/AlertSection.tsx | 4 ++-- .../LinodeStorage/CreateDiskDrawer.tsx | 4 ++-- .../LinodeStorage/ResizeDiskDrawer.tsx | 4 ++-- .../Linodes/PowerActionsDialogOrDrawer.tsx | 2 +- .../src/features/Managed/MonitorDrawer.tsx | 18 ++++++++++-------- .../NodeBalancers/NodeBalancerActiveCheck.tsx | 3 +-- .../NodeBalancers/NodeBalancerConfigPanel.tsx | 2 +- .../NodeBalancerSettings.tsx | 5 ++--- .../NodeBalancers/NodeBalancerPassiveCheck.tsx | 2 +- .../AccessKeyLanding/CopyAllHostnames.tsx | 3 +-- .../Profile/APITokens/CreateAPITokenDrawer.tsx | 3 +-- .../PhoneVerification.styles.ts | 2 +- .../PhoneVerification/PhoneVerification.tsx | 2 +- .../SecurityQuestions/Question.tsx | 2 +- .../TwoFactor/TwoFactorToggle.tsx | 2 +- .../Profile/LishSettings/LishSettings.tsx | 5 ++--- .../OAuthClients/CreateOAuthClientDrawer.tsx | 2 +- .../OAuthClients/EditOAuthClientDrawer.tsx | 2 +- .../StackScriptForm/StackScriptForm.tsx | 2 +- .../FieldTypes/UserDefinedSelect.tsx | 5 +++-- .../features/Support/AttachFileListItem.tsx | 8 ++++---- .../SupportTicketProductSelectionFields.tsx | 2 +- .../src/features/VPCs/VPCCreate/SubnetNode.tsx | 7 ++++--- .../VPCDetail/SubnetAssignLinodesDrawer.tsx | 2 +- .../features/Volumes/AttachVolumeDrawer.tsx | 2 +- .../Volumes/VolumeDrawer/ConfigSelect.tsx | 2 +- .../Volumes/VolumeDrawer/SizeField.tsx | 6 +++--- .../src/features/Volumes/VolumesLanding.tsx | 3 +-- .../.changeset/pr-11159-added-1729806253949.md | 5 +++++ packages/ui/src/components/{ => Chip}/Chip.tsx | 0 packages/ui/src/components/Chip/index.ts | 1 + .../FormControl}/FormControl.stories.tsx | 9 +++++---- .../components/FormControl}/FormControl.tsx | 0 .../ui/src/components/FormControl/index.ts | 1 + .../FormHelperText}/FormHelperText.stories.tsx | 6 +++--- .../FormHelperText}/FormHelperText.tsx | 0 .../ui/src/components/FormHelperText/index.ts | 1 + .../src/components/Input}/Input.stories.tsx | 0 .../src/components/Input}/Input.tsx | 0 packages/ui/src/components/Input/index.ts | 1 + .../InputAdornment}/InputAdornment.stories.tsx | 2 +- .../InputAdornment}/InputAdornment.tsx | 0 .../ui/src/components/InputAdornment/index.ts | 1 + .../InputLabel}/InputLabel.stories.tsx | 4 ++-- .../src/components/InputLabel}/InputLabel.tsx | 0 packages/ui/src/components/InputLabel/index.ts | 1 + .../{ => Tooltip}/Tooltip.stories.tsx | 0 .../src/components/{ => Tooltip}/Tooltip.tsx | 0 packages/ui/src/components/Tooltip/index.ts | 1 + packages/ui/src/components/index.ts | 5 +++++ 76 files changed, 129 insertions(+), 112 deletions(-) create mode 100644 packages/ui/.changeset/pr-11159-added-1729806253949.md rename packages/ui/src/components/{ => Chip}/Chip.tsx (100%) create mode 100644 packages/ui/src/components/Chip/index.ts rename packages/{manager/src/components => ui/src/components/FormControl}/FormControl.stories.tsx (78%) rename packages/{manager/src/components => ui/src/components/FormControl}/FormControl.tsx (100%) create mode 100644 packages/ui/src/components/FormControl/index.ts rename packages/{manager/src/components => ui/src/components/FormHelperText}/FormHelperText.stories.tsx (83%) rename packages/{manager/src/components => ui/src/components/FormHelperText}/FormHelperText.tsx (100%) create mode 100644 packages/ui/src/components/FormHelperText/index.ts rename packages/{manager/src/components => ui/src/components/Input}/Input.stories.tsx (100%) rename packages/{manager/src/components => ui/src/components/Input}/Input.tsx (100%) create mode 100644 packages/ui/src/components/Input/index.ts rename packages/{manager/src/components => ui/src/components/InputAdornment}/InputAdornment.stories.tsx (96%) rename packages/{manager/src/components => ui/src/components/InputAdornment}/InputAdornment.tsx (100%) create mode 100644 packages/ui/src/components/InputAdornment/index.ts rename packages/{manager/src/components => ui/src/components/InputLabel}/InputLabel.stories.tsx (86%) rename packages/{manager/src/components => ui/src/components/InputLabel}/InputLabel.tsx (100%) create mode 100644 packages/ui/src/components/InputLabel/index.ts rename packages/ui/src/components/{ => Tooltip}/Tooltip.stories.tsx (100%) rename packages/ui/src/components/{ => Tooltip}/Tooltip.tsx (100%) create mode 100644 packages/ui/src/components/Tooltip/index.ts diff --git a/packages/manager/src/components/Autocomplete/Autocomplete.tsx b/packages/manager/src/components/Autocomplete/Autocomplete.tsx index 081f78b4d91..7e8ac138df6 100644 --- a/packages/manager/src/components/Autocomplete/Autocomplete.tsx +++ b/packages/manager/src/components/Autocomplete/Autocomplete.tsx @@ -1,3 +1,4 @@ +import { InputAdornment } from '@linode/ui'; import CloseIcon from '@mui/icons-material/Close'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import MuiAutocomplete from '@mui/material/Autocomplete'; @@ -7,7 +8,6 @@ import { Box } from 'src/components/Box'; import { TextField } from 'src/components/TextField'; import { CircleProgress } from '../CircleProgress'; -import { InputAdornment } from '../InputAdornment'; import { CustomPopper, SelectedIcon, diff --git a/packages/manager/src/components/DebouncedSearchTextField/DebouncedSearchTextField.tsx b/packages/manager/src/components/DebouncedSearchTextField/DebouncedSearchTextField.tsx index 9069bd8195a..fc62afddd4f 100644 --- a/packages/manager/src/components/DebouncedSearchTextField/DebouncedSearchTextField.tsx +++ b/packages/manager/src/components/DebouncedSearchTextField/DebouncedSearchTextField.tsx @@ -1,4 +1,4 @@ -import { IconButton } from '@linode/ui'; +import { IconButton, InputAdornment } from '@linode/ui'; import Clear from '@mui/icons-material/Clear'; import Search from '@mui/icons-material/Search'; import { styled } from '@mui/material/styles'; @@ -6,7 +6,6 @@ import * as React from 'react'; import { debounce } from 'throttle-debounce'; import { CircleProgress } from 'src/components/CircleProgress'; -import { InputAdornment } from 'src/components/InputAdornment'; import { TextField } from 'src/components/TextField'; import type { TextFieldProps } from 'src/components/TextField'; diff --git a/packages/manager/src/components/FormLabel.stories.tsx b/packages/manager/src/components/FormLabel.stories.tsx index c12e81647d8..b5167d53b5f 100644 --- a/packages/manager/src/components/FormLabel.stories.tsx +++ b/packages/manager/src/components/FormLabel.stories.tsx @@ -1,12 +1,13 @@ -import { Meta, StoryObj } from '@storybook/react'; +import { FormControl } from '@linode/ui'; import React from 'react'; -import { FormControl } from './FormControl'; import { FormControlLabel } from './FormControlLabel'; import { FormLabel } from './FormLabel'; import { Radio } from './Radio/Radio'; import { RadioGroup } from './RadioGroup'; +import type { Meta, StoryObj } from '@storybook/react'; + const meta: Meta = { component: FormLabel, title: 'Components/Form/FormLabel', diff --git a/packages/manager/src/components/MultipleIPInput/MultipleIPInput.tsx b/packages/manager/src/components/MultipleIPInput/MultipleIPInput.tsx index fd6eb1a54d7..e96367d4b1d 100644 --- a/packages/manager/src/components/MultipleIPInput/MultipleIPInput.tsx +++ b/packages/manager/src/components/MultipleIPInput/MultipleIPInput.tsx @@ -1,10 +1,10 @@ +import { InputLabel } from '@linode/ui'; import Close from '@mui/icons-material/Close'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; import { Button } from 'src/components/Button/Button'; -import { InputLabel } from 'src/components/InputLabel'; import { LinkButton } from 'src/components/LinkButton'; import { Notice } from 'src/components/Notice/Notice'; import { StyledLinkButtonBox } from 'src/components/SelectFirewallPanel/SelectFirewallPanel'; diff --git a/packages/manager/src/components/Paper.tsx b/packages/manager/src/components/Paper.tsx index 5b8355cad18..778c766c4f7 100644 --- a/packages/manager/src/components/Paper.tsx +++ b/packages/manager/src/components/Paper.tsx @@ -1,10 +1,11 @@ -import _Paper, { PaperProps } from '@mui/material/Paper'; +import { FormHelperText } from '@linode/ui'; +import _Paper from '@mui/material/Paper'; import { styled } from '@mui/material/styles'; import * as React from 'react'; import { omittedProps } from 'src/utilities/omittedProps'; -import { FormHelperText } from './FormHelperText'; +import type { PaperProps } from '@mui/material/Paper'; interface Props extends PaperProps { /** @@ -29,8 +30,8 @@ export const Paper = (props: Props) => { {props.error && {props.error}} diff --git a/packages/manager/src/components/TextField.stories.tsx b/packages/manager/src/components/TextField.stories.tsx index 58ee3393e74..673ddb67b10 100644 --- a/packages/manager/src/components/TextField.stories.tsx +++ b/packages/manager/src/components/TextField.stories.tsx @@ -1,9 +1,10 @@ -import { Meta, StoryObj } from '@storybook/react'; +import { InputAdornment } from '@linode/ui'; import React from 'react'; -import { InputAdornment } from './InputAdornment'; import { TextField } from './TextField'; +import type { Meta, StoryObj } from '@storybook/react'; + const meta: Meta = { component: TextField, title: 'Foundations/TextField', diff --git a/packages/manager/src/components/TextField.test.tsx b/packages/manager/src/components/TextField.test.tsx index 2c372ada0a2..566e6c0ec9d 100644 --- a/packages/manager/src/components/TextField.test.tsx +++ b/packages/manager/src/components/TextField.test.tsx @@ -1,9 +1,9 @@ +import { InputAdornment } from '@linode/ui'; import { fireEvent, getDefaultNormalizer } from '@testing-library/react'; import * as React from 'react'; import { renderWithTheme } from 'src/utilities/testHelpers'; -import { InputAdornment } from './InputAdornment'; import { TextField } from './TextField'; describe('TextField', () => { diff --git a/packages/manager/src/components/TextField.tsx b/packages/manager/src/components/TextField.tsx index 1926202b3bc..74abf82b7e1 100644 --- a/packages/manager/src/components/TextField.tsx +++ b/packages/manager/src/components/TextField.tsx @@ -1,3 +1,4 @@ +import { FormHelperText, InputAdornment, InputLabel } from '@linode/ui'; import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown'; import { useTheme } from '@mui/material/styles'; import { default as _TextField } from '@mui/material/TextField'; @@ -7,9 +8,6 @@ import { makeStyles } from 'tss-react/mui'; import { Box } from 'src/components/Box'; import { CircleProgress } from 'src/components/CircleProgress'; -import { FormHelperText } from 'src/components/FormHelperText'; -import { InputAdornment } from 'src/components/InputAdornment'; -import { InputLabel } from 'src/components/InputLabel'; import { TooltipIcon } from 'src/components/TooltipIcon'; import { convertToKebabCase } from 'src/utilities/convertToKebobCase'; diff --git a/packages/manager/src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/PaymentDrawer.tsx b/packages/manager/src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/PaymentDrawer.tsx index a3d7587da58..45fa7b3809f 100644 --- a/packages/manager/src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/PaymentDrawer.tsx +++ b/packages/manager/src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/PaymentDrawer.tsx @@ -1,6 +1,5 @@ -import { PaymentMethod } from '@linode/api-v4'; import { makePayment } from '@linode/api-v4/lib/account'; -import { APIWarning } from '@linode/api-v4/lib/types'; +import { InputAdornment } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import { useQueryClient } from '@tanstack/react-query'; import { useSnackbar } from 'notistack'; @@ -12,7 +11,6 @@ import { Currency } from 'src/components/Currency'; import { Divider } from 'src/components/Divider'; import { Drawer } from 'src/components/Drawer'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; -import { InputAdornment } from 'src/components/InputAdornment'; import { LinearProgress } from 'src/components/LinearProgress'; import { Notice } from 'src/components/Notice/Notice'; import { Stack } from 'src/components/Stack'; @@ -23,6 +21,7 @@ import { Typography } from 'src/components/Typography'; import { getRestrictedResourceText } from 'src/features/Account/utils'; import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck'; import { useAccount } from 'src/queries/account/account'; +import { accountQueries } from 'src/queries/account/queries'; import { useProfile } from 'src/queries/profile/profile'; import { isCreditCardExpired } from 'src/utilities/creditCard'; import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; @@ -32,8 +31,10 @@ import GooglePayButton from './GooglePayButton'; import { CreditCardDialog } from './PaymentBits/CreditCardDialog'; import { PaymentMethodCard } from './PaymentMethodCard'; import PayPalButton from './PayPalButton'; -import { SetSuccess } from './types'; -import { accountQueries } from 'src/queries/account/queries'; + +import type { SetSuccess } from './types'; +import type { PaymentMethod } from '@linode/api-v4'; +import type { APIWarning } from '@linode/api-v4/lib/types'; const useStyles = makeStyles()(() => ({ button: { diff --git a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx index 5ec755d8d25..501efb6dcd7 100644 --- a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx +++ b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx @@ -1,3 +1,4 @@ +import { FormControl } from '@linode/ui'; import { BetaChip } from '@linode/ui'; import { createDatabaseSchema } from '@linode/validation/lib/databases.schema'; import Grid from '@mui/material/Unstable_Grid2'; @@ -18,7 +19,6 @@ import { _SingleValue } from 'src/components/EnhancedSelect/components/SingleVal import Select from 'src/components/EnhancedSelect/Select'; import { ErrorMessage } from 'src/components/ErrorMessage'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; -import { FormControl } from 'src/components/FormControl'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { LandingHeader } from 'src/components/LandingHeader'; import { Notice } from 'src/components/Notice/Notice'; diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/MaintenanceWindow.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/MaintenanceWindow.tsx index 753c2959ff5..29303d2d52c 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/MaintenanceWindow.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/MaintenanceWindow.tsx @@ -1,6 +1,4 @@ -import { Database, UpdatesSchedule } from '@linode/api-v4/lib/databases'; -import { APIError } from '@linode/api-v4/lib/types'; -import { Theme } from '@mui/material/styles'; +import { FormControl } from '@linode/ui'; import { useFormik } from 'formik'; import { DateTime } from 'luxon'; import { useSnackbar } from 'notistack'; @@ -9,16 +7,20 @@ import { Link } from 'react-router-dom'; import { makeStyles } from 'tss-react/mui'; import { Button } from 'src/components/Button/Button'; -import Select, { Item } from 'src/components/EnhancedSelect/Select'; +import Select from 'src/components/EnhancedSelect/Select'; +import { FormControlLabel } from 'src/components/FormControlLabel'; import { Notice } from 'src/components/Notice/Notice'; import { Radio } from 'src/components/Radio/Radio'; +import { RadioGroup } from 'src/components/RadioGroup'; import { TooltipIcon } from 'src/components/TooltipIcon'; import { Typography } from 'src/components/Typography'; -import { FormControl } from 'src/components/FormControl'; -import { FormControlLabel } from 'src/components/FormControlLabel'; -import { RadioGroup } from 'src/components/RadioGroup'; import { useDatabaseMutation } from 'src/queries/databases/databases'; +import type { Database, UpdatesSchedule } from '@linode/api-v4/lib/databases'; +import type { APIError } from '@linode/api-v4/lib/types'; +import type { Theme } from '@mui/material/styles'; +import type { Item } from 'src/components/EnhancedSelect/Select'; + const useStyles = makeStyles()((theme: Theme) => ({ formControlDropdown: { '& label': { diff --git a/packages/manager/src/features/Domains/CreateDomain/CreateDomain.tsx b/packages/manager/src/features/Domains/CreateDomain/CreateDomain.tsx index 1802253fe4f..9bfb75e399f 100644 --- a/packages/manager/src/features/Domains/CreateDomain/CreateDomain.tsx +++ b/packages/manager/src/features/Domains/CreateDomain/CreateDomain.tsx @@ -1,3 +1,4 @@ +import { FormHelperText } from '@linode/ui'; import { createDomainSchema } from '@linode/validation/lib/domains.schema'; import { styled } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; @@ -11,7 +12,6 @@ import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { FormControlLabel } from 'src/components/FormControlLabel'; -import { FormHelperText } from 'src/components/FormHelperText'; import { LandingHeader } from 'src/components/LandingHeader'; import { MultipleIPInput } from 'src/components/MultipleIPInput/MultipleIPInput'; import { Notice } from 'src/components/Notice/Notice'; diff --git a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx index 0e549541967..2d653a279a0 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/SupportSearchLanding.tsx @@ -1,3 +1,4 @@ +import { InputAdornment } from '@linode/ui'; import Search from '@mui/icons-material/Search'; import Grid from '@mui/material/Unstable_Grid2'; import { createLazyRoute } from '@tanstack/react-router'; @@ -7,7 +8,6 @@ import { makeStyles } from 'tss-react/mui'; import { Box } from 'src/components/Box'; import { H1Header } from 'src/components/H1Header/H1Header'; -import { InputAdornment } from 'src/components/InputAdornment'; import { Notice } from 'src/components/Notice/Notice'; import { TextField } from 'src/components/TextField'; import { COMMUNITY_SEARCH_URL, DOCS_SEARCH_URL } from 'src/constants'; diff --git a/packages/manager/src/features/Images/ImagesLanding/ImagesLanding.tsx b/packages/manager/src/features/Images/ImagesLanding/ImagesLanding.tsx index 22ad3a3880c..8085e6a3dbd 100644 --- a/packages/manager/src/features/Images/ImagesLanding/ImagesLanding.tsx +++ b/packages/manager/src/features/Images/ImagesLanding/ImagesLanding.tsx @@ -1,4 +1,4 @@ -import { IconButton } from '@linode/ui'; +import { IconButton, InputAdornment } from '@linode/ui'; import CloseIcon from '@mui/icons-material/Close'; import { useQueryClient } from '@tanstack/react-query'; import { createLazyRoute } from '@tanstack/react-router'; @@ -15,7 +15,6 @@ import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { Drawer } from 'src/components/Drawer'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { Hidden } from 'src/components/Hidden'; -import { InputAdornment } from 'src/components/InputAdornment'; import { LandingHeader } from 'src/components/LandingHeader'; import { Notice } from 'src/components/Notice/Notice'; import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFooter'; diff --git a/packages/manager/src/features/Kubernetes/CreateCluster/ApplicationPlatform.tsx b/packages/manager/src/features/Kubernetes/CreateCluster/ApplicationPlatform.tsx index ab910c26119..38a2edd3dbc 100644 --- a/packages/manager/src/features/Kubernetes/CreateCluster/ApplicationPlatform.tsx +++ b/packages/manager/src/features/Kubernetes/CreateCluster/ApplicationPlatform.tsx @@ -1,8 +1,8 @@ +import { FormControl } from '@linode/ui'; import * as React from 'react'; import { Box } from 'src/components/Box'; import { Chip } from 'src/components/Chip'; -import { FormControl } from 'src/components/FormControl'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { FormLabel } from 'src/components/FormLabel'; import { Link } from 'src/components/Link'; diff --git a/packages/manager/src/features/Kubernetes/CreateCluster/ControlPlaneACLPane.tsx b/packages/manager/src/features/Kubernetes/CreateCluster/ControlPlaneACLPane.tsx index 07d13a020a6..884f73c373e 100644 --- a/packages/manager/src/features/Kubernetes/CreateCluster/ControlPlaneACLPane.tsx +++ b/packages/manager/src/features/Kubernetes/CreateCluster/ControlPlaneACLPane.tsx @@ -1,9 +1,9 @@ +import { FormControl } from '@linode/ui'; import { FormLabel } from '@mui/material'; import * as React from 'react'; import { Box } from 'src/components/Box'; import { ErrorMessage } from 'src/components/ErrorMessage'; -import { FormControl } from 'src/components/FormControl'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { MultipleIPInput } from 'src/components/MultipleIPInput/MultipleIPInput'; import { Notice } from 'src/components/Notice/Notice'; diff --git a/packages/manager/src/features/Kubernetes/CreateCluster/HAControlPlane.tsx b/packages/manager/src/features/Kubernetes/CreateCluster/HAControlPlane.tsx index 8b62f016c87..97fbfd1c7d9 100644 --- a/packages/manager/src/features/Kubernetes/CreateCluster/HAControlPlane.tsx +++ b/packages/manager/src/features/Kubernetes/CreateCluster/HAControlPlane.tsx @@ -1,9 +1,9 @@ +import { FormControl } from '@linode/ui'; import { FormLabel } from '@mui/material'; import * as React from 'react'; import { Box } from 'src/components/Box'; import { CircleProgress } from 'src/components/CircleProgress'; -import { FormControl } from 'src/components/FormControl'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/StackScriptSelectionList.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/StackScriptSelectionList.tsx index c6b5a014261..db3312bafdf 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/StackScriptSelectionList.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/StackScriptSelectionList.tsx @@ -1,5 +1,5 @@ import { getAPIFilterFromQuery } from '@linode/search'; -import { IconButton } from '@linode/ui'; +import { IconButton, InputAdornment } from '@linode/ui'; import CloseIcon from '@mui/icons-material/Close'; import { useQueryClient } from '@tanstack/react-query'; import React, { useState } from 'react'; @@ -11,7 +11,6 @@ import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; import { CircleProgress } from 'src/components/CircleProgress'; import { Code } from 'src/components/Code/Code'; -import { InputAdornment } from 'src/components/InputAdornment'; import { Stack } from 'src/components/Stack'; import { Table } from 'src/components/Table'; import { TableBody } from 'src/components/TableBody'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/UserDefinedFields/UserDefinedFieldInput.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/UserDefinedFields/UserDefinedFieldInput.tsx index d83f982b156..070e792bea5 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/UserDefinedFields/UserDefinedFieldInput.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/UserDefinedFields/UserDefinedFieldInput.tsx @@ -1,9 +1,9 @@ +import { FormControl } from '@linode/ui'; import React from 'react'; import { useController, useFormContext } from 'react-hook-form'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { Divider } from 'src/components/Divider'; -import { FormControl } from 'src/components/FormControl'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { FormLabel } from 'src/components/FormLabel'; import { Link } from 'src/components/Link'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/CaptureSnapshot.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/CaptureSnapshot.tsx index 7cc32e768e6..ebdba8be93a 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/CaptureSnapshot.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/CaptureSnapshot.tsx @@ -1,3 +1,4 @@ +import { FormControl } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { useFormik } from 'formik'; import { useSnackbar } from 'notistack'; @@ -5,7 +6,6 @@ import * as React from 'react'; import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; -import { FormControl } from 'src/components/FormControl'; import { Paper } from 'src/components/Paper'; import { TextField } from 'src/components/TextField'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/RestoreToLinodeDrawer.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/RestoreToLinodeDrawer.tsx index 197a2448624..28dd67ad5b5 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/RestoreToLinodeDrawer.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/RestoreToLinodeDrawer.tsx @@ -1,3 +1,5 @@ +import { FormControl } from '@linode/ui'; +import { FormHelperText } from '@linode/ui'; import { useFormik } from 'formik'; import { useSnackbar } from 'notistack'; import * as React from 'react'; @@ -6,9 +8,7 @@ import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { Checkbox } from 'src/components/Checkbox'; import { Drawer } from 'src/components/Drawer'; -import { FormControl } from 'src/components/FormControl'; import { FormControlLabel } from 'src/components/FormControlLabel'; -import { FormHelperText } from 'src/components/FormHelperText'; import { Notice } from 'src/components/Notice/Notice'; import { useEventsPollingActions } from 'src/queries/events/events'; import { useLinodeBackupRestoreMutation } from 'src/queries/linodes/backups'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/ScheduleSettings.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/ScheduleSettings.tsx index bf34e71d935..6f38b003c23 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/ScheduleSettings.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/ScheduleSettings.tsx @@ -1,3 +1,5 @@ +import { FormControl } from '@linode/ui'; +import { FormHelperText } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { useFormik } from 'formik'; import { useSnackbar } from 'notistack'; @@ -5,8 +7,6 @@ import * as React from 'react'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; -import { FormControl } from 'src/components/FormControl'; -import { FormHelperText } from 'src/components/FormHelperText'; import { Notice } from 'src/components/Notice/Notice'; import { Paper } from 'src/components/Paper'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigDialog.styles.ts b/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigDialog.styles.ts index 5891aeca88a..87cc023382b 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigDialog.styles.ts +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigDialog.styles.ts @@ -1,7 +1,7 @@ +import { FormControl } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { Divider } from 'src/components/Divider'; -import { FormControl } from 'src/components/FormControl'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { FormGroup } from 'src/components/FormGroup'; import { RadioGroup } from 'src/components/RadioGroup'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigDialog.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigDialog.tsx index 5c383be5076..99be38d2e7d 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigDialog.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigDialog.tsx @@ -1,3 +1,5 @@ +import { FormControl } from '@linode/ui'; +import { FormHelperText } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import { useQueryClient } from '@tanstack/react-query'; @@ -14,9 +16,7 @@ import { CircleProgress } from 'src/components/CircleProgress'; import { Dialog } from 'src/components/Dialog/Dialog'; import { Divider } from 'src/components/Divider'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; -import { FormControl } from 'src/components/FormControl'; import { FormControlLabel } from 'src/components/FormControlLabel'; -import { FormHelperText } from 'src/components/FormHelperText'; import { FormLabel } from 'src/components/FormLabel'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRescue/DeviceSelection.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRescue/DeviceSelection.tsx index 0e6e7ac7757..b3c6f8d4645 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRescue/DeviceSelection.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRescue/DeviceSelection.tsx @@ -1,8 +1,8 @@ +import { FormControl } from '@linode/ui'; import { defaultTo } from 'ramda'; import * as React from 'react'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; -import { FormControl } from 'src/components/FormControl'; import { titlecase } from 'src/features/Linodes/presentation'; import { getSelectedDeviceOption } from '../utilities'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeResize/LinodeResizeUnifiedMigrationPanel.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeResize/LinodeResizeUnifiedMigrationPanel.tsx index 880e195a084..065f6bb4742 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeResize/LinodeResizeUnifiedMigrationPanel.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeResize/LinodeResizeUnifiedMigrationPanel.tsx @@ -1,9 +1,9 @@ +import { FormControl } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import * as React from 'react'; import { Box } from 'src/components/Box'; import { Divider } from 'src/components/Divider'; -import { FormControl } from 'src/components/FormControl'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { Link } from 'src/components/Link'; import { Radio } from 'src/components/Radio/Radio'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/AlertSection.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/AlertSection.tsx index 6fd1d3baf4b..c01519bb61a 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/AlertSection.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/AlertSection.tsx @@ -1,11 +1,11 @@ -import Grid from '@mui/material/Unstable_Grid2'; +import { InputAdornment } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; +import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { Box } from 'src/components/Box'; import { Divider } from 'src/components/Divider'; import { FormControlLabel } from 'src/components/FormControlLabel'; -import { InputAdornment } from 'src/components/InputAdornment'; import { TextField } from 'src/components/TextField'; import { Toggle } from 'src/components/Toggle/Toggle'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/CreateDiskDrawer.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/CreateDiskDrawer.tsx index 2d0aee0b60a..30d07d62e17 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/CreateDiskDrawer.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/CreateDiskDrawer.tsx @@ -1,3 +1,5 @@ +import { InputAdornment } from '@linode/ui'; +import { FormHelperText } from '@linode/ui'; import { CreateLinodeDiskFromImageSchema, CreateLinodeDiskSchema, @@ -9,8 +11,6 @@ import * as React from 'react'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { Drawer } from 'src/components/Drawer'; -import { FormHelperText } from 'src/components/FormHelperText'; -import { InputAdornment } from 'src/components/InputAdornment'; import { ModeSelect } from 'src/components/ModeSelect/ModeSelect'; import { Notice } from 'src/components/Notice/Notice'; import { TextField } from 'src/components/TextField'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/ResizeDiskDrawer.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/ResizeDiskDrawer.tsx index bc9d7c4856f..94d1c435f1a 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/ResizeDiskDrawer.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/ResizeDiskDrawer.tsx @@ -1,3 +1,5 @@ +import { InputAdornment } from '@linode/ui'; +import { FormHelperText } from '@linode/ui'; import { ResizeLinodeDiskSchema } from '@linode/validation'; import { styled } from '@mui/material/styles'; import { useFormik } from 'formik'; @@ -7,8 +9,6 @@ import * as React from 'react'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Code } from 'src/components/Code/Code'; import { Drawer } from 'src/components/Drawer'; -import { FormHelperText } from 'src/components/FormHelperText'; -import { InputAdornment } from 'src/components/InputAdornment'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; import { TextField } from 'src/components/TextField'; diff --git a/packages/manager/src/features/Linodes/PowerActionsDialogOrDrawer.tsx b/packages/manager/src/features/Linodes/PowerActionsDialogOrDrawer.tsx index c24b49077a3..00dd677f7b8 100644 --- a/packages/manager/src/features/Linodes/PowerActionsDialogOrDrawer.tsx +++ b/packages/manager/src/features/Linodes/PowerActionsDialogOrDrawer.tsx @@ -1,10 +1,10 @@ +import { FormHelperText } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import * as React from 'react'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog'; -import { FormHelperText } from 'src/components/FormHelperText'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Managed/MonitorDrawer.tsx b/packages/manager/src/features/Managed/MonitorDrawer.tsx index b39593d9ffa..e872cb239d6 100644 --- a/packages/manager/src/features/Managed/MonitorDrawer.tsx +++ b/packages/manager/src/features/Managed/MonitorDrawer.tsx @@ -1,9 +1,4 @@ -import { - ManagedCredential, - ManagedServiceMonitor, - ManagedServicePayload, - ServiceType, -} from '@linode/api-v4/lib/managed'; +import { InputAdornment } from '@linode/ui'; import { createServiceMonitorSchema } from '@linode/validation/lib/managed.schema'; import Grid from '@mui/material/Unstable_Grid2'; import { Formik } from 'formik'; @@ -12,10 +7,17 @@ import * as React from 'react'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Drawer } from 'src/components/Drawer'; -import Select, { Item } from 'src/components/EnhancedSelect/Select'; +import Select from 'src/components/EnhancedSelect/Select'; import { Notice } from 'src/components/Notice/Notice'; import { TextField } from 'src/components/TextField'; -import { InputAdornment } from 'src/components/InputAdornment'; + +import type { + ManagedCredential, + ManagedServiceMonitor, + ManagedServicePayload, + ServiceType, +} from '@linode/api-v4/lib/managed'; +import type { Item } from 'src/components/EnhancedSelect/Select'; export interface MonitorDrawerProps { credentials: ManagedCredential[]; diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerActiveCheck.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerActiveCheck.tsx index ec43a2f3ed1..fedfe3909d5 100644 --- a/packages/manager/src/features/NodeBalancers/NodeBalancerActiveCheck.tsx +++ b/packages/manager/src/features/NodeBalancers/NodeBalancerActiveCheck.tsx @@ -1,9 +1,8 @@ +import { FormHelperText, InputAdornment } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; -import { FormHelperText } from 'src/components/FormHelperText'; -import { InputAdornment } from 'src/components/InputAdornment'; import { TextField } from 'src/components/TextField'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerConfigPanel.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerConfigPanel.tsx index 0e4995c1173..0260fd5f049 100644 --- a/packages/manager/src/features/NodeBalancers/NodeBalancerConfigPanel.tsx +++ b/packages/manager/src/features/NodeBalancers/NodeBalancerConfigPanel.tsx @@ -1,3 +1,4 @@ +import { FormHelperText } from '@linode/ui'; import { styled } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; @@ -6,7 +7,6 @@ import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { Button } from 'src/components/Button/Button'; import { Divider } from 'src/components/Divider'; -import { FormHelperText } from 'src/components/FormHelperText'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; import { TextField } from 'src/components/TextField'; diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSettings.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSettings.tsx index 4cb00802471..e633864cb48 100644 --- a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSettings.tsx +++ b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSettings.tsx @@ -1,3 +1,4 @@ +import { FormHelperText, InputAdornment } from '@linode/ui'; import { useTheme } from '@mui/material'; import { createLazyRoute } from '@tanstack/react-router'; import * as React from 'react'; @@ -6,15 +7,13 @@ import { useParams } from 'react-router-dom'; import { Accordion } from 'src/components/Accordion'; import { Button } from 'src/components/Button/Button'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; -import { FormHelperText } from 'src/components/FormHelperText'; -import { InputAdornment } from 'src/components/InputAdornment'; import { TextField } from 'src/components/TextField'; import { useIsResourceRestricted } from 'src/hooks/useIsResourceRestricted'; -import { useNodeBalancersFirewallsQuery } from 'src/queries/nodebalancers'; import { useNodeBalancerQuery, useNodebalancerUpdateMutation, } from 'src/queries/nodebalancers'; +import { useNodeBalancersFirewallsQuery } from 'src/queries/nodebalancers'; import { NodeBalancerDeleteDialog } from '../NodeBalancerDeleteDialog'; import { NodeBalancerFirewalls } from './NodeBalancerFirewalls'; diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerPassiveCheck.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerPassiveCheck.tsx index 7275e5f149b..cd54cac1d4d 100644 --- a/packages/manager/src/features/NodeBalancers/NodeBalancerPassiveCheck.tsx +++ b/packages/manager/src/features/NodeBalancers/NodeBalancerPassiveCheck.tsx @@ -1,8 +1,8 @@ +import { FormHelperText } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { FormControlLabel } from 'src/components/FormControlLabel'; -import { FormHelperText } from 'src/components/FormHelperText'; import { Toggle } from 'src/components/Toggle/Toggle'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/CopyAllHostnames.tsx b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/CopyAllHostnames.tsx index 514344fdef1..695ae8a1bc1 100644 --- a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/CopyAllHostnames.tsx +++ b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/CopyAllHostnames.tsx @@ -1,11 +1,10 @@ -import { Tooltip } from '@linode/ui'; +import { InputLabel, Tooltip } from '@linode/ui'; import { styled } from '@mui/material/styles'; import copy from 'copy-to-clipboard'; import * as React from 'react'; import { Box } from 'src/components/Box'; import { StyledLinkButton } from 'src/components/Button/StyledLinkButton'; -import { InputLabel } from 'src/components/InputLabel'; export interface Props { hideShowAll?: boolean; diff --git a/packages/manager/src/features/Profile/APITokens/CreateAPITokenDrawer.tsx b/packages/manager/src/features/Profile/APITokens/CreateAPITokenDrawer.tsx index 2156c1524fc..58a0b8cc935 100644 --- a/packages/manager/src/features/Profile/APITokens/CreateAPITokenDrawer.tsx +++ b/packages/manager/src/features/Profile/APITokens/CreateAPITokenDrawer.tsx @@ -1,3 +1,4 @@ +import { FormControl, FormHelperText } from '@linode/ui'; import { useFormik } from 'formik'; import { DateTime } from 'luxon'; import * as React from 'react'; @@ -5,8 +6,6 @@ import * as React from 'react'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { Drawer } from 'src/components/Drawer'; -import { FormControl } from 'src/components/FormControl'; -import { FormHelperText } from 'src/components/FormHelperText'; import { Notice } from 'src/components/Notice/Notice'; import { Radio } from 'src/components/Radio/Radio'; import { TableBody } from 'src/components/TableBody'; diff --git a/packages/manager/src/features/Profile/AuthenticationSettings/PhoneVerification/PhoneVerification.styles.ts b/packages/manager/src/features/Profile/AuthenticationSettings/PhoneVerification/PhoneVerification.styles.ts index d6caaa14b50..c4c8ab5226e 100644 --- a/packages/manager/src/features/Profile/AuthenticationSettings/PhoneVerification/PhoneVerification.styles.ts +++ b/packages/manager/src/features/Profile/AuthenticationSettings/PhoneVerification/PhoneVerification.styles.ts @@ -1,8 +1,8 @@ +import { FormHelperText } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { Box } from 'src/components/Box'; -import { FormHelperText } from 'src/components/FormHelperText'; import { TextField } from 'src/components/TextField'; import { Typography } from 'src/components/Typography'; import { omittedProps } from 'src/utilities/omittedProps'; diff --git a/packages/manager/src/features/Profile/AuthenticationSettings/PhoneVerification/PhoneVerification.tsx b/packages/manager/src/features/Profile/AuthenticationSettings/PhoneVerification/PhoneVerification.tsx index c0d5334bc02..baed20e7e5d 100644 --- a/packages/manager/src/features/Profile/AuthenticationSettings/PhoneVerification/PhoneVerification.tsx +++ b/packages/manager/src/features/Profile/AuthenticationSettings/PhoneVerification/PhoneVerification.tsx @@ -1,3 +1,4 @@ +import { InputAdornment } from '@linode/ui'; import { useQueryClient } from '@tanstack/react-query'; import { useFormik } from 'formik'; import { parsePhoneNumber } from 'libphonenumber-js'; @@ -6,7 +7,6 @@ import * as React from 'react'; import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; -import { InputAdornment } from 'src/components/InputAdornment'; import { LinkButton } from 'src/components/LinkButton'; import { TextField } from 'src/components/TextField'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Profile/AuthenticationSettings/SecurityQuestions/Question.tsx b/packages/manager/src/features/Profile/AuthenticationSettings/SecurityQuestions/Question.tsx index a77e0e8b22d..ff8bab2a27b 100644 --- a/packages/manager/src/features/Profile/AuthenticationSettings/SecurityQuestions/Question.tsx +++ b/packages/manager/src/features/Profile/AuthenticationSettings/SecurityQuestions/Question.tsx @@ -1,7 +1,7 @@ +import { InputLabel } from '@linode/ui'; import * as React from 'react'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; -import { InputLabel } from 'src/components/InputLabel'; import { LinkButton } from 'src/components/LinkButton'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Profile/AuthenticationSettings/TwoFactor/TwoFactorToggle.tsx b/packages/manager/src/features/Profile/AuthenticationSettings/TwoFactor/TwoFactorToggle.tsx index 941310500ec..0bfeea24e34 100644 --- a/packages/manager/src/features/Profile/AuthenticationSettings/TwoFactor/TwoFactorToggle.tsx +++ b/packages/manager/src/features/Profile/AuthenticationSettings/TwoFactor/TwoFactorToggle.tsx @@ -1,6 +1,6 @@ +import { FormControl } from '@linode/ui'; import * as React from 'react'; -import { FormControl } from 'src/components/FormControl'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { Toggle } from 'src/components/Toggle/Toggle'; diff --git a/packages/manager/src/features/Profile/LishSettings/LishSettings.tsx b/packages/manager/src/features/Profile/LishSettings/LishSettings.tsx index 952eb980ce0..67c86bc3574 100644 --- a/packages/manager/src/features/Profile/LishSettings/LishSettings.tsx +++ b/packages/manager/src/features/Profile/LishSettings/LishSettings.tsx @@ -1,4 +1,6 @@ +import { FormControl } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; +import { createLazyRoute } from '@tanstack/react-router'; import { equals, lensPath, remove, set } from 'ramda'; import * as React from 'react'; @@ -7,13 +9,10 @@ import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; -import { FormControl } from 'src/components/FormControl'; import { Notice } from 'src/components/Notice/Notice'; import { Paper } from 'src/components/Paper'; import { TextField } from 'src/components/TextField'; import { Typography } from 'src/components/Typography'; -import { createLazyRoute } from '@tanstack/react-router'; - import { useMutateProfile, useProfile } from 'src/queries/profile/profile'; import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; import { getAPIErrorFor } from 'src/utilities/getAPIErrorFor'; diff --git a/packages/manager/src/features/Profile/OAuthClients/CreateOAuthClientDrawer.tsx b/packages/manager/src/features/Profile/OAuthClients/CreateOAuthClientDrawer.tsx index 36a783ae129..5b49daef10d 100644 --- a/packages/manager/src/features/Profile/OAuthClients/CreateOAuthClientDrawer.tsx +++ b/packages/manager/src/features/Profile/OAuthClients/CreateOAuthClientDrawer.tsx @@ -1,10 +1,10 @@ +import { FormControl } from '@linode/ui'; import { useFormik } from 'formik'; import * as React from 'react'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Checkbox } from 'src/components/Checkbox'; import { Drawer } from 'src/components/Drawer'; -import { FormControl } from 'src/components/FormControl'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { Notice } from 'src/components/Notice/Notice'; import { TextField } from 'src/components/TextField'; diff --git a/packages/manager/src/features/Profile/OAuthClients/EditOAuthClientDrawer.tsx b/packages/manager/src/features/Profile/OAuthClients/EditOAuthClientDrawer.tsx index 67cf9149f7d..4f2d4673e55 100644 --- a/packages/manager/src/features/Profile/OAuthClients/EditOAuthClientDrawer.tsx +++ b/packages/manager/src/features/Profile/OAuthClients/EditOAuthClientDrawer.tsx @@ -1,10 +1,10 @@ +import { FormControl } from '@linode/ui'; import { useFormik } from 'formik'; import * as React from 'react'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Checkbox } from 'src/components/Checkbox'; import { Drawer } from 'src/components/Drawer'; -import { FormControl } from 'src/components/FormControl'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { Notice } from 'src/components/Notice/Notice'; import { TextField } from 'src/components/TextField'; diff --git a/packages/manager/src/features/StackScripts/StackScriptForm/StackScriptForm.tsx b/packages/manager/src/features/StackScripts/StackScriptForm/StackScriptForm.tsx index 5a736e413ec..c47c0fdc24a 100644 --- a/packages/manager/src/features/StackScripts/StackScriptForm/StackScriptForm.tsx +++ b/packages/manager/src/features/StackScripts/StackScriptForm/StackScriptForm.tsx @@ -1,7 +1,7 @@ +import { InputAdornment } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; -import { InputAdornment } from 'src/components/InputAdornment'; import { Paper } from 'src/components/Paper'; import { TextField } from 'src/components/TextField'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/FieldTypes/UserDefinedSelect.tsx b/packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/FieldTypes/UserDefinedSelect.tsx index 779e1309cde..2d911c2ae12 100644 --- a/packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/FieldTypes/UserDefinedSelect.tsx +++ b/packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/FieldTypes/UserDefinedSelect.tsx @@ -1,13 +1,14 @@ -import { UserDefinedField } from '@linode/api-v4/lib/stackscripts'; +import { InputLabel } from '@linode/ui'; import { styled } from '@mui/material/styles'; import * as React from 'react'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { FormControlLabel } from 'src/components/FormControlLabel'; -import { InputLabel } from 'src/components/InputLabel'; import { Notice } from 'src/components/Notice/Notice'; import { Radio } from 'src/components/Radio/Radio'; +import type { UserDefinedField } from '@linode/api-v4/lib/stackscripts'; + interface Props { error?: string; field: UserDefinedField; diff --git a/packages/manager/src/features/Support/AttachFileListItem.tsx b/packages/manager/src/features/Support/AttachFileListItem.tsx index 26daa9d028a..58b26d7bf3d 100644 --- a/packages/manager/src/features/Support/AttachFileListItem.tsx +++ b/packages/manager/src/features/Support/AttachFileListItem.tsx @@ -1,15 +1,15 @@ +import { InputAdornment } from '@linode/ui'; import Close from '@mui/icons-material/Close'; import CloudUpload from '@mui/icons-material/CloudUpload'; import Grid from '@mui/material/Unstable_Grid2'; -import { Theme } from '@mui/material/styles'; -import { makeStyles } from 'tss-react/mui'; import * as React from 'react'; +import { makeStyles } from 'tss-react/mui'; -import { InputAdornment } from 'src/components/InputAdornment'; import { LinearProgress } from 'src/components/LinearProgress'; import { TextField } from 'src/components/TextField'; -import { FileAttachment } from './index'; +import type { FileAttachment } from './index'; +import type { Theme } from '@mui/material/styles'; const useStyles = makeStyles()((theme: Theme) => ({ attachmentField: { diff --git a/packages/manager/src/features/Support/SupportTickets/SupportTicketProductSelectionFields.tsx b/packages/manager/src/features/Support/SupportTickets/SupportTicketProductSelectionFields.tsx index 1998e626fc5..65f55f44187 100644 --- a/packages/manager/src/features/Support/SupportTickets/SupportTicketProductSelectionFields.tsx +++ b/packages/manager/src/features/Support/SupportTickets/SupportTicketProductSelectionFields.tsx @@ -1,8 +1,8 @@ +import { FormHelperText } from '@linode/ui'; import React from 'react'; import { Controller, useFormContext } from 'react-hook-form'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; -import { FormHelperText } from 'src/components/FormHelperText'; import { TextField } from 'src/components/TextField'; import { useAllDatabasesQuery } from 'src/queries/databases/databases'; import { useAllDomainsQuery } from 'src/queries/domains'; diff --git a/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.tsx b/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.tsx index ac3eb4b7b11..b47d5fa1b12 100644 --- a/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.tsx +++ b/packages/manager/src/features/VPCs/VPCCreate/SubnetNode.tsx @@ -1,18 +1,19 @@ +import { FormHelperText } from '@linode/ui'; import Close from '@mui/icons-material/Close'; import Stack from '@mui/material/Stack'; -import Grid from '@mui/material/Unstable_Grid2'; import { styled } from '@mui/material/styles'; +import Grid from '@mui/material/Unstable_Grid2'; 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, - SubnetFieldState, calculateAvailableIPv4sRFC1918, } from 'src/utilities/subnets'; +import type { SubnetFieldState } from 'src/utilities/subnets'; + interface Props { disabled?: boolean; // extra props enable SubnetNode to be an independent component or be part of MultipleSubnetInput diff --git a/packages/manager/src/features/VPCs/VPCDetail/SubnetAssignLinodesDrawer.tsx b/packages/manager/src/features/VPCs/VPCDetail/SubnetAssignLinodesDrawer.tsx index 8a532231cc0..946c7f3ae30 100644 --- a/packages/manager/src/features/VPCs/VPCDetail/SubnetAssignLinodesDrawer.tsx +++ b/packages/manager/src/features/VPCs/VPCDetail/SubnetAssignLinodesDrawer.tsx @@ -1,4 +1,5 @@ import { appendConfigInterface } from '@linode/api-v4'; +import { FormHelperText } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import { useFormik } from 'formik'; import * as React from 'react'; @@ -10,7 +11,6 @@ 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 { RemovableSelectionsListTable } from 'src/components/RemovableSelectionsList/RemovableSelectionsListTable'; diff --git a/packages/manager/src/features/Volumes/AttachVolumeDrawer.tsx b/packages/manager/src/features/Volumes/AttachVolumeDrawer.tsx index 9fa0305e7eb..9b53b283243 100644 --- a/packages/manager/src/features/Volumes/AttachVolumeDrawer.tsx +++ b/packages/manager/src/features/Volumes/AttachVolumeDrawer.tsx @@ -1,3 +1,4 @@ +import { FormHelperText } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { useFormik } from 'formik'; import { useSnackbar } from 'notistack'; @@ -10,7 +11,6 @@ import { Checkbox } from 'src/components/Checkbox'; import { Drawer } from 'src/components/Drawer'; import { BLOCK_STORAGE_ENCRYPTION_SETTING_IMMUTABLE_COPY } from 'src/components/Encryption/constants'; import { useIsBlockStorageEncryptionFeatureEnabled } from 'src/components/Encryption/utils'; -import { FormHelperText } from 'src/components/FormHelperText'; import { Notice } from 'src/components/Notice/Notice'; import { LinodeSelect } from 'src/features/Linodes/LinodeSelect/LinodeSelect'; import { useEventsPollingActions } from 'src/queries/events/events'; diff --git a/packages/manager/src/features/Volumes/VolumeDrawer/ConfigSelect.tsx b/packages/manager/src/features/Volumes/VolumeDrawer/ConfigSelect.tsx index a610a59444a..efd115fc434 100644 --- a/packages/manager/src/features/Volumes/VolumeDrawer/ConfigSelect.tsx +++ b/packages/manager/src/features/Volumes/VolumeDrawer/ConfigSelect.tsx @@ -1,7 +1,7 @@ +import { FormControl } from '@linode/ui'; import * as React from 'react'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; -import { FormControl } from 'src/components/FormControl'; import { useAllLinodeConfigsQuery } from 'src/queries/linodes/configs'; interface Props { diff --git a/packages/manager/src/features/Volumes/VolumeDrawer/SizeField.tsx b/packages/manager/src/features/Volumes/VolumeDrawer/SizeField.tsx index 71b8b6f3b29..48b1d04e4a1 100644 --- a/packages/manager/src/features/Volumes/VolumeDrawer/SizeField.tsx +++ b/packages/manager/src/features/Volumes/VolumeDrawer/SizeField.tsx @@ -1,11 +1,9 @@ -import { Theme } from '@mui/material/styles'; +import { FormHelperText, InputAdornment } from '@linode/ui'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; import { Box } from 'src/components/Box'; import { CircleProgress } from 'src/components/CircleProgress'; -import { FormHelperText } from 'src/components/FormHelperText'; -import { InputAdornment } from 'src/components/InputAdornment'; import { TextField } from 'src/components/TextField'; import { Typography } from 'src/components/Typography'; import { MAX_VOLUME_SIZE } from 'src/constants'; @@ -15,6 +13,8 @@ import { getDCSpecificPriceByType } from 'src/utilities/pricing/dynamicPricing'; import { SIZE_FIELD_WIDTH } from '../VolumeCreate'; +import type { Theme } from '@mui/material/styles'; + interface Props { disabled?: boolean; error?: string; diff --git a/packages/manager/src/features/Volumes/VolumesLanding.tsx b/packages/manager/src/features/Volumes/VolumesLanding.tsx index 55973edb53b..972d7d67090 100644 --- a/packages/manager/src/features/Volumes/VolumesLanding.tsx +++ b/packages/manager/src/features/Volumes/VolumesLanding.tsx @@ -1,4 +1,4 @@ -import { IconButton } from '@linode/ui'; +import { IconButton, InputAdornment } from '@linode/ui'; import CloseIcon from '@mui/icons-material/Close'; import { createLazyRoute } from '@tanstack/react-router'; import * as React from 'react'; @@ -9,7 +9,6 @@ import { CircleProgress } from 'src/components/CircleProgress'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { useIsBlockStorageEncryptionFeatureEnabled } from 'src/components/Encryption/utils'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; -import { InputAdornment } from 'src/components/InputAdornment'; import { LandingHeader } from 'src/components/LandingHeader'; import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFooter'; import { Table } from 'src/components/Table'; diff --git a/packages/ui/.changeset/pr-11159-added-1729806253949.md b/packages/ui/.changeset/pr-11159-added-1729806253949.md new file mode 100644 index 00000000000..f6fdef00aaf --- /dev/null +++ b/packages/ui/.changeset/pr-11159-added-1729806253949.md @@ -0,0 +1,5 @@ +--- +"@linode/ui": Added +--- + +Migrate `FormControl`, `FormHelperText`, `Input`, `InputAdornment`, and `InputLabel` from `manager` to `ui` package ([#11159](https://github.com/linode/manager/pull/11159)) diff --git a/packages/ui/src/components/Chip.tsx b/packages/ui/src/components/Chip/Chip.tsx similarity index 100% rename from packages/ui/src/components/Chip.tsx rename to packages/ui/src/components/Chip/Chip.tsx diff --git a/packages/ui/src/components/Chip/index.ts b/packages/ui/src/components/Chip/index.ts new file mode 100644 index 00000000000..99a3b024126 --- /dev/null +++ b/packages/ui/src/components/Chip/index.ts @@ -0,0 +1 @@ +export * from './Chip'; diff --git a/packages/manager/src/components/FormControl.stories.tsx b/packages/ui/src/components/FormControl/FormControl.stories.tsx similarity index 78% rename from packages/manager/src/components/FormControl.stories.tsx rename to packages/ui/src/components/FormControl/FormControl.stories.tsx index 6f01e8b9428..f9e29ea656f 100644 --- a/packages/manager/src/components/FormControl.stories.tsx +++ b/packages/ui/src/components/FormControl/FormControl.stories.tsx @@ -1,10 +1,11 @@ -import { Meta, StoryObj } from '@storybook/react'; +import { FormHelperText } from '@linode/ui'; import React from 'react'; import { FormControl } from './FormControl'; -import { FormHelperText } from './FormHelperText'; -import { Input } from './Input'; -import { InputLabel } from './InputLabel'; +import { Input } from '../Input'; +import { InputLabel } from '../InputLabel'; + +import type { Meta, StoryObj } from '@storybook/react'; const meta: Meta = { component: FormControl, diff --git a/packages/manager/src/components/FormControl.tsx b/packages/ui/src/components/FormControl/FormControl.tsx similarity index 100% rename from packages/manager/src/components/FormControl.tsx rename to packages/ui/src/components/FormControl/FormControl.tsx diff --git a/packages/ui/src/components/FormControl/index.ts b/packages/ui/src/components/FormControl/index.ts new file mode 100644 index 00000000000..639d8536abe --- /dev/null +++ b/packages/ui/src/components/FormControl/index.ts @@ -0,0 +1 @@ +export * from './FormControl'; diff --git a/packages/manager/src/components/FormHelperText.stories.tsx b/packages/ui/src/components/FormHelperText/FormHelperText.stories.tsx similarity index 83% rename from packages/manager/src/components/FormHelperText.stories.tsx rename to packages/ui/src/components/FormHelperText/FormHelperText.stories.tsx index 1af7733fcf0..92f8274d61c 100644 --- a/packages/manager/src/components/FormHelperText.stories.tsx +++ b/packages/ui/src/components/FormHelperText/FormHelperText.stories.tsx @@ -1,10 +1,10 @@ import { Meta, StoryObj } from '@storybook/react'; import React from 'react'; -import { FormControl } from './FormControl'; +import { FormControl } from '../FormControl'; import { FormHelperText } from './FormHelperText'; -import { Input } from './Input'; -import { InputLabel } from './InputLabel'; +import { Input } from '../Input'; +import { InputLabel } from '../InputLabel'; const meta: Meta = { component: FormHelperText, diff --git a/packages/manager/src/components/FormHelperText.tsx b/packages/ui/src/components/FormHelperText/FormHelperText.tsx similarity index 100% rename from packages/manager/src/components/FormHelperText.tsx rename to packages/ui/src/components/FormHelperText/FormHelperText.tsx diff --git a/packages/ui/src/components/FormHelperText/index.ts b/packages/ui/src/components/FormHelperText/index.ts new file mode 100644 index 00000000000..91104b49758 --- /dev/null +++ b/packages/ui/src/components/FormHelperText/index.ts @@ -0,0 +1 @@ +export * from './FormHelperText'; diff --git a/packages/manager/src/components/Input.stories.tsx b/packages/ui/src/components/Input/Input.stories.tsx similarity index 100% rename from packages/manager/src/components/Input.stories.tsx rename to packages/ui/src/components/Input/Input.stories.tsx diff --git a/packages/manager/src/components/Input.tsx b/packages/ui/src/components/Input/Input.tsx similarity index 100% rename from packages/manager/src/components/Input.tsx rename to packages/ui/src/components/Input/Input.tsx diff --git a/packages/ui/src/components/Input/index.ts b/packages/ui/src/components/Input/index.ts new file mode 100644 index 00000000000..ba9fe7ebc6a --- /dev/null +++ b/packages/ui/src/components/Input/index.ts @@ -0,0 +1 @@ +export * from './Input'; diff --git a/packages/manager/src/components/InputAdornment.stories.tsx b/packages/ui/src/components/InputAdornment/InputAdornment.stories.tsx similarity index 96% rename from packages/manager/src/components/InputAdornment.stories.tsx rename to packages/ui/src/components/InputAdornment/InputAdornment.stories.tsx index a7217004d59..85376fd2b0c 100644 --- a/packages/manager/src/components/InputAdornment.stories.tsx +++ b/packages/ui/src/components/InputAdornment/InputAdornment.stories.tsx @@ -1,7 +1,7 @@ import { Meta, StoryObj } from '@storybook/react'; import React from 'react'; -import { Input } from './Input'; +import { Input } from '../Input'; import { InputAdornment } from './InputAdornment'; const meta: Meta = { diff --git a/packages/manager/src/components/InputAdornment.tsx b/packages/ui/src/components/InputAdornment/InputAdornment.tsx similarity index 100% rename from packages/manager/src/components/InputAdornment.tsx rename to packages/ui/src/components/InputAdornment/InputAdornment.tsx diff --git a/packages/ui/src/components/InputAdornment/index.ts b/packages/ui/src/components/InputAdornment/index.ts new file mode 100644 index 00000000000..af4cce2e8c4 --- /dev/null +++ b/packages/ui/src/components/InputAdornment/index.ts @@ -0,0 +1 @@ +export * from './InputAdornment'; diff --git a/packages/manager/src/components/InputLabel.stories.tsx b/packages/ui/src/components/InputLabel/InputLabel.stories.tsx similarity index 86% rename from packages/manager/src/components/InputLabel.stories.tsx rename to packages/ui/src/components/InputLabel/InputLabel.stories.tsx index da74b311479..c892a9207ec 100644 --- a/packages/manager/src/components/InputLabel.stories.tsx +++ b/packages/ui/src/components/InputLabel/InputLabel.stories.tsx @@ -1,8 +1,8 @@ import { Meta, StoryObj } from '@storybook/react'; import React from 'react'; -import { FormControl } from './FormControl'; -import { Input } from './Input'; +import { FormControl } from '../FormControl'; +import { Input } from '../Input'; import { InputLabel } from './InputLabel'; const meta: Meta = { diff --git a/packages/manager/src/components/InputLabel.tsx b/packages/ui/src/components/InputLabel/InputLabel.tsx similarity index 100% rename from packages/manager/src/components/InputLabel.tsx rename to packages/ui/src/components/InputLabel/InputLabel.tsx diff --git a/packages/ui/src/components/InputLabel/index.ts b/packages/ui/src/components/InputLabel/index.ts new file mode 100644 index 00000000000..1a3af1c3b66 --- /dev/null +++ b/packages/ui/src/components/InputLabel/index.ts @@ -0,0 +1 @@ +export * from './InputLabel'; diff --git a/packages/ui/src/components/Tooltip.stories.tsx b/packages/ui/src/components/Tooltip/Tooltip.stories.tsx similarity index 100% rename from packages/ui/src/components/Tooltip.stories.tsx rename to packages/ui/src/components/Tooltip/Tooltip.stories.tsx diff --git a/packages/ui/src/components/Tooltip.tsx b/packages/ui/src/components/Tooltip/Tooltip.tsx similarity index 100% rename from packages/ui/src/components/Tooltip.tsx rename to packages/ui/src/components/Tooltip/Tooltip.tsx diff --git a/packages/ui/src/components/Tooltip/index.ts b/packages/ui/src/components/Tooltip/index.ts new file mode 100644 index 00000000000..7594a8f06c1 --- /dev/null +++ b/packages/ui/src/components/Tooltip/index.ts @@ -0,0 +1 @@ +export * from './Tooltip'; diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index df5b4a81ed2..ec50522e5dc 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -1,4 +1,9 @@ export * from './Chip'; export * from './BetaChip'; +export * from './FormControl'; +export * from './FormHelperText'; export * from './IconButton'; +export * from './Input'; +export * from './InputAdornment'; +export * from './InputLabel'; export * from './Tooltip'; From 0818a7e6aeca54a9bbbe63d02f11ac4e7ca036b7 Mon Sep 17 00:00:00 2001 From: cpathipa <119517080+cpathipa@users.noreply.github.com> Date: Tue, 29 Oct 2024 12:42:00 -0500 Subject: [PATCH 19/66] refactor: [M3-8785] - Remove feature flag, tracking events used for A/B testing in API CLI tools Modal (#11156) * unit test coverage for HostNameTableCell * Revert "unit test coverage for HostNameTableCell" This reverts commit b274baf67e27d79fd4e764607ded7c5aa755ee8b. * chore: [M3-8662] - Update Github Actions actions (#11009) * update actions * add changeset --------- Co-authored-by: Banks Nussman * Remove feature flag, tracking events used for A/B testing in API CLI Tools Modal * Added changeset: Remove feature flag, tracking events used for A/B testing in API CLI tools Modal * PR feedback - @mjac0bs --------- Co-authored-by: Banks Nussman <115251059+bnussman-akamai@users.noreply.github.com> Co-authored-by: Banks Nussman --- .../pr-11156-tech-stories-1729784052355.md | 5 +++ .../create-linode-view-code-snippet.spec.ts | 10 +---- .../src/components/CodeBlock/CodeBlock.tsx | 24 +----------- packages/manager/src/featureFlags.ts | 1 - .../Linodes/LinodeCreate/Actions.test.tsx | 4 +- .../features/Linodes/LinodeCreate/Actions.tsx | 22 ++--------- .../AnsibleIntegrationResources.tsx | 23 ----------- .../ApiAwarenessModal/ApiAwarenessModal.tsx | 32 +--------------- .../ApiAwarenessModal/CurlTabPanel.tsx | 38 ++----------------- .../ApiAwarenessModal/GoSDKResources.tsx | 20 ---------- .../IntegrationsTabPanel.tsx | 6 --- .../ApiAwarenessModal/LinodeCLIPanel.tsx | 33 +--------------- .../ApiAwarenessModal/PythonSDKResources.tsx | 20 ---------- .../ApiAwarenessModal/SDKTabPanel.tsx | 10 +---- .../TerraformIntegrationResources.tsx | 23 ----------- 15 files changed, 20 insertions(+), 251 deletions(-) create mode 100644 packages/manager/.changeset/pr-11156-tech-stories-1729784052355.md diff --git a/packages/manager/.changeset/pr-11156-tech-stories-1729784052355.md b/packages/manager/.changeset/pr-11156-tech-stories-1729784052355.md new file mode 100644 index 00000000000..63b427c420e --- /dev/null +++ b/packages/manager/.changeset/pr-11156-tech-stories-1729784052355.md @@ -0,0 +1,5 @@ +--- +'@linode/manager': Tech Stories +--- + +Remove the feature flag and tracking events used for A/B testing in the API CLI Tools modal, and update the DX Tools modal button copy to 'View Code Snippets ([#11156](https://github.com/linode/manager/pull/11156)) 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 7064f974516..0ae05e81ecc 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,18 +6,12 @@ import { ui } from 'support/ui'; import { randomLabel, randomString } from 'support/util/random'; import { linodeCreatePage } from 'support/ui/pages'; -import { mockAppendFeatureFlags } from 'support/intercepts/feature-flags'; describe('Create Linode flow to validate code snippet modal', () => { /* * tests for create Linode flow to validate code snippet modal. */ - // TODO Delete these mocks once `testdxtoolabexperiment` feature flag is retired. - beforeEach(() => { - mockAppendFeatureFlags({ - testdxtoolabexperiment: 'Create using command line', - }); - }); + it(`view code snippets in create linode flow`, () => { const linodeLabel = randomLabel(); const rootPass = randomString(32); @@ -33,7 +27,7 @@ describe('Create Linode flow to validate code snippet modal', () => { // View Code Snippets and confirm it's provisioned as expected. ui.button - .findByTitle('Create using command line') + .findByTitle('View Code Snippets') .should('be.visible') .should('be.enabled') .click(); diff --git a/packages/manager/src/components/CodeBlock/CodeBlock.tsx b/packages/manager/src/components/CodeBlock/CodeBlock.tsx index d4b97f6e4ec..695bd8e0fe2 100644 --- a/packages/manager/src/components/CodeBlock/CodeBlock.tsx +++ b/packages/manager/src/components/CodeBlock/CodeBlock.tsx @@ -1,8 +1,5 @@ -import { useLDClient } from 'launchdarkly-react-client-sdk'; import React from 'react'; -import { useFlags } from 'src/hooks/useFlags'; -import { useIsAkamaiAccount } from 'src/hooks/useIsAkamaiAccount'; import { sendApiAwarenessClickEvent } from 'src/utilities/analytics/customEventAnalytics'; import { @@ -22,32 +19,13 @@ export interface CodeBlockProps { handleCopyIconClick?: () => void; /** The command language */ language: SupportedLanguage; - ldTrackingKey?: string; } export const CodeBlock = (props: CodeBlockProps) => { - const flags = useFlags(); - const ldClient = useLDClient(); - const { isAkamaiAccount: isInternalAccount } = useIsAkamaiAccount(); - - const { - command, - commandType, - handleCopyIconClick, - language, - ldTrackingKey, - } = props; - - const apicliButtonCopy = flags?.testdxtoolabexperiment; + const { command, commandType, handleCopyIconClick, language } = props; const _handleCopyIconClick = () => { sendApiAwarenessClickEvent('Copy Icon', commandType); - if (ldTrackingKey && !isInternalAccount) { - ldClient?.track(ldTrackingKey, { - variation: apicliButtonCopy, - }); - ldClient?.flush(); - } }; return ( diff --git a/packages/manager/src/featureFlags.ts b/packages/manager/src/featureFlags.ts index 64f07f0ed54..9a92408132d 100644 --- a/packages/manager/src/featureFlags.ts +++ b/packages/manager/src/featureFlags.ts @@ -124,7 +124,6 @@ export interface Flags { taxCollectionBanner: TaxCollectionBanner; taxId: BaseFeatureFlag; taxes: Taxes; - testdxtoolabexperiment: string; tpaProviders: Provider[]; } diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Actions.test.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Actions.test.tsx index ba5d303d69c..f7ff507d752 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Actions.test.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Actions.test.tsx @@ -16,12 +16,12 @@ describe('Actions', () => { expect(button).toHaveAttribute('type', 'submit'); expect(button).toBeEnabled(); }); - it("should render a 'Create using command line' button", () => { + it("should render a ' View Code Snippets' button", () => { const { getByText } = renderWithThemeAndHookFormContext({ component: , }); - const button = getByText('Create Using Command Line').closest('button'); + const button = getByText('View Code Snippets').closest('button'); expect(button).toBeVisible(); expect(button).toBeEnabled(); diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Actions.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Actions.tsx index d24c563fcf3..b1097ff730c 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Actions.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Actions.tsx @@ -1,12 +1,8 @@ -import { useLDClient } from 'launchdarkly-react-client-sdk'; import React, { useState } from 'react'; import { useFormContext } from 'react-hook-form'; import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; -import { LD_DX_TOOLS_METRICS_KEYS } from 'src/constants'; -import { useFlags } from 'src/hooks/useFlags'; -import { useIsAkamaiAccount } from 'src/hooks/useIsAkamaiAccount'; import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck'; import { sendApiAwarenessClickEvent } from 'src/utilities/analytics/customEventAnalytics'; import { sendLinodeCreateFormInputEvent } from 'src/utilities/analytics/formEventAnalytics'; @@ -21,15 +17,10 @@ import { import type { LinodeCreateFormValues } from './utilities'; export const Actions = () => { - const flags = useFlags(); - const ldClient = useLDClient(); const { params } = useLinodeCreateQueryParams(); - const { isAkamaiAccount: isInternalAccount } = useIsAkamaiAccount(); const [isAPIAwarenessModalOpen, setIsAPIAwarenessModalOpen] = useState(false); - const apicliButtonCopy = flags?.testdxtoolabexperiment; - const { formState, getValues, @@ -44,22 +35,15 @@ export const Actions = () => { isLinodeCreateRestricted || 'firewallOverride' in formState.errors; const onOpenAPIAwareness = async () => { - sendApiAwarenessClickEvent('Button', 'Create Using Command Line'); + sendApiAwarenessClickEvent('Button', 'View Code Snippets'); sendLinodeCreateFormInputEvent({ createType: params.type ?? 'OS', interaction: 'click', - label: apicliButtonCopy ?? 'Create Using Command Line', + label: 'View Code Snippets', }); if (await trigger()) { // If validation is successful, we open the dialog. setIsAPIAwarenessModalOpen(true); - if (!isInternalAccount) { - ldClient?.track(LD_DX_TOOLS_METRICS_KEYS.OPEN_MODAL, { - variation: apicliButtonCopy, - }); - } - - ldClient?.flush(); } else { scrollErrorIntoView(undefined, { behavior: 'smooth' }); } @@ -68,7 +52,7 @@ export const Actions = () => { return (
+import { Box } from '@linode/ui'; import { styled } from '@mui/material/styles'; import Table from '@mui/material/Table'; import Grid from '@mui/material/Unstable_Grid2'; import { Link } from 'react-router-dom'; -import { Box } from 'src/components/Box'; import { CopyTooltip } from 'src/components/CopyTooltip/CopyTooltip'; import { TableCell } from 'src/components/TableCell'; import { TableRow } from 'src/components/TableRow'; diff --git a/packages/manager/src/features/Linodes/LinodeEntityDetailBody.tsx b/packages/manager/src/features/Linodes/LinodeEntityDetailBody.tsx index 9eb76ca4d39..70ab5290a57 100644 --- a/packages/manager/src/features/Linodes/LinodeEntityDetailBody.tsx +++ b/packages/manager/src/features/Linodes/LinodeEntityDetailBody.tsx @@ -1,10 +1,10 @@ +import { Box } from '@linode/ui'; import { useMediaQuery } from '@mui/material'; import { useTheme } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { HashLink } from 'react-router-hash-link'; -import { Box } from 'src/components/Box'; import { DISK_ENCRYPTION_NODE_POOL_GUIDANCE_COPY as UNENCRYPTED_LKE_LINODE_GUIDANCE_COPY, UNENCRYPTED_STANDARD_LINODE_GUIDANCE_COPY, diff --git a/packages/manager/src/features/Linodes/LinodeEntityDetailHeader.tsx b/packages/manager/src/features/Linodes/LinodeEntityDetailHeader.tsx index 0aadae894a2..d5c8b4e7fcd 100644 --- a/packages/manager/src/features/Linodes/LinodeEntityDetailHeader.tsx +++ b/packages/manager/src/features/Linodes/LinodeEntityDetailHeader.tsx @@ -1,8 +1,8 @@ +import { Box } from '@linode/ui'; import { Typography } from '@mui/material'; import { useTheme } from '@mui/material/styles'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; import { EntityHeader } from 'src/components/EntityHeader/EntityHeader'; import { Hidden } from 'src/components/Hidden'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/CaptureSnapshot.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/CaptureSnapshot.tsx index ebdba8be93a..b7c2fe6dfe5 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/CaptureSnapshot.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/CaptureSnapshot.tsx @@ -1,10 +1,9 @@ -import { FormControl } from '@linode/ui'; +import { Box, FormControl } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { useFormik } from 'formik'; import { useSnackbar } from 'notistack'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; import { Paper } from 'src/components/Paper'; import { TextField } from 'src/components/TextField'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigActionMenu.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigActionMenu.tsx index 9775fb97382..2595837ce31 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigActionMenu.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigActionMenu.tsx @@ -1,14 +1,17 @@ -import { Config } from '@linode/api-v4/lib/linodes'; -import { Theme, useTheme } from '@mui/material/styles'; +import { Box } from '@linode/ui'; +import { useTheme } from '@mui/material/styles'; import useMediaQuery from '@mui/material/useMediaQuery'; import { splitAt } from 'ramda'; import * as React from 'react'; import { useHistory } from 'react-router-dom'; -import { Action, ActionMenu } from 'src/components/ActionMenu/ActionMenu'; -import { Box } from 'src/components/Box'; +import { ActionMenu } from 'src/components/ActionMenu/ActionMenu'; import { InlineMenuAction } from 'src/components/InlineMenuAction/InlineMenuAction'; +import type { Config } from '@linode/api-v4/lib/linodes'; +import type { Theme } from '@mui/material/styles'; +import type { Action } from 'src/components/ActionMenu/ActionMenu'; + interface Props { config: Config; label: string; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigDialog.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigDialog.tsx index 99be38d2e7d..f0ad6916711 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigDialog.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigDialog.tsx @@ -1,5 +1,4 @@ -import { FormControl } from '@linode/ui'; -import { FormHelperText } from '@linode/ui'; +import { Box, FormControl, FormHelperText } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import { useQueryClient } from '@tanstack/react-query'; @@ -10,7 +9,6 @@ import * as React from 'react'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; -import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; import { CircleProgress } from 'src/components/CircleProgress'; import { Dialog } from 'src/components/Dialog/Dialog'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigs.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigs.tsx index 7dc6b7cd698..9a8a82231ad 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigs.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeConfigs/LinodeConfigs.tsx @@ -1,8 +1,8 @@ +import { Box } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; 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 { DocsLink } from 'src/components/DocsLink/DocsLink'; import OrderBy from 'src/components/OrderBy'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/AddIPDrawer.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/AddIPDrawer.tsx index be95b1f2d86..0b00c30cfa4 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/AddIPDrawer.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/AddIPDrawer.tsx @@ -1,9 +1,9 @@ import { Tooltip } from '@linode/ui'; +import { Box } from '@linode/ui'; import { styled } from '@mui/material/styles'; import * as React from 'react'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; -import { Box } from 'src/components/Box'; import { Divider } from 'src/components/Divider'; import { Drawer } from 'src/components/Drawer'; import { FormControlLabel } from 'src/components/FormControlLabel'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx index 5bb134eabfa..084d385fa5d 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx @@ -1,8 +1,8 @@ +import { Box } from '@linode/ui'; import { useMediaQuery, useTheme } from '@mui/material'; import * as React from 'react'; import { ActionMenu } from 'src/components/ActionMenu/ActionMenu'; -import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; import { CircleProgress } from 'src/components/CircleProgress'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworkingActionMenu.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworkingActionMenu.tsx index 0b425405105..0bb9e1ac7ae 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworkingActionMenu.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeNetworkingActionMenu.tsx @@ -1,10 +1,10 @@ +import { Box } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import useMediaQuery from '@mui/material/useMediaQuery'; import { isEmpty } from 'ramda'; import * as React from 'react'; import { ActionMenu } from 'src/components/ActionMenu/ActionMenu'; -import { Box } from 'src/components/Box'; import { InlineMenuAction } from 'src/components/InlineMenuAction/InlineMenuAction'; import { PUBLIC_IP_ADDRESSES_TOOLTIP_TEXT } from 'src/features/Linodes/PublicIPAddressesTooltip'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/NetworkingSummaryPanel/TransferHistory.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/NetworkingSummaryPanel/TransferHistory.tsx index 28d1d68ea43..5fcdab4f038 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/NetworkingSummaryPanel/TransferHistory.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/NetworkingSummaryPanel/TransferHistory.tsx @@ -1,4 +1,4 @@ -import { Stats } from '@linode/api-v4/lib/linodes'; +import { Box } from '@linode/ui'; import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos'; import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; import { IconButton } from '@mui/material'; @@ -8,8 +8,6 @@ import * as React from 'react'; import PendingIcon from 'src/assets/icons/pending.svg'; import { AreaChart } from 'src/components/AreaChart/AreaChart'; -import { LinodeNetworkTimeData, Point } from 'src/components/AreaChart/types'; -import { Box } from 'src/components/Box'; import { CircleProgress } from 'src/components/CircleProgress'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { Typography } from 'src/components/Typography'; @@ -27,6 +25,12 @@ import { useProfile } from 'src/queries/profile/profile'; import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; import { readableBytes } from 'src/utilities/unitConversions'; +import type { Stats } from '@linode/api-v4/lib/linodes'; +import type { + LinodeNetworkTimeData, + Point, +} from 'src/components/AreaChart/types'; + interface Props { linodeCreated: string; linodeID: number; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/RebuildFromImage.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/RebuildFromImage.tsx index f73eac90fbd..a6e5d9374d1 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/RebuildFromImage.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/RebuildFromImage.tsx @@ -1,4 +1,5 @@ import { rebuildLinode } from '@linode/api-v4'; +import { Box } from '@linode/ui'; import { RebuildLinodeSchema } from '@linode/validation/lib/linodes.schema'; import Grid from '@mui/material/Unstable_Grid2'; import { Formik } from 'formik'; @@ -8,7 +9,6 @@ import * as React from 'react'; import { useLocation } from 'react-router-dom'; import { AccessPanel } from 'src/components/AccessPanel/AccessPanel'; -import { Box } from 'src/components/Box'; import { Checkbox } from 'src/components/Checkbox'; import { Divider } from 'src/components/Divider'; import { ImageSelect } from 'src/components/ImageSelect/ImageSelect'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/UserDataAccordion/UserDataAccordion.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/UserDataAccordion/UserDataAccordion.tsx index 354b1b73693..d20ae58c5c1 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/UserDataAccordion/UserDataAccordion.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/UserDataAccordion/UserDataAccordion.tsx @@ -1,7 +1,7 @@ +import { Box } from '@linode/ui'; import * as React from 'react'; import { Accordion } from 'src/components/Accordion'; -import { Box } from 'src/components/Box'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; import { TextField } from 'src/components/TextField'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/UserDataAccordion/UserDataAccordionHeading.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/UserDataAccordion/UserDataAccordionHeading.tsx index 43ff949ca24..61db1291303 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/UserDataAccordion/UserDataAccordionHeading.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/UserDataAccordion/UserDataAccordionHeading.tsx @@ -1,6 +1,6 @@ +import { Box } from '@linode/ui'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { Link } from 'src/components/Link'; import { TooltipIcon } from 'src/components/TooltipIcon'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeResize/LinodeResize.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeResize/LinodeResize.tsx index f56535503d7..65989e5796d 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeResize/LinodeResize.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeResize/LinodeResize.tsx @@ -1,9 +1,9 @@ +import { Box } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import { useFormik } from 'formik'; import { useSnackbar } from 'notistack'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; import { Checkbox } from 'src/components/Checkbox'; import { CircleProgress } from 'src/components/CircleProgress/CircleProgress'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeResize/LinodeResizeUnifiedMigrationPanel.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeResize/LinodeResizeUnifiedMigrationPanel.tsx index 065f6bb4742..c096b2b343e 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeResize/LinodeResizeUnifiedMigrationPanel.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeResize/LinodeResizeUnifiedMigrationPanel.tsx @@ -1,8 +1,7 @@ -import { FormControl } from '@linode/ui'; +import { Box, FormControl } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { Divider } from 'src/components/Divider'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { Link } from 'src/components/Link'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/AlertSection.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/AlertSection.tsx index c01519bb61a..b84ec5a7fca 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/AlertSection.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/AlertSection.tsx @@ -1,9 +1,8 @@ -import { InputAdornment } from '@linode/ui'; +import { Box, InputAdornment } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { Divider } from 'src/components/Divider'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { TextField } from 'src/components/TextField'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/VPCPanel.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/VPCPanel.tsx index c7c2c8b4fca..4ac18e72bfa 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/VPCPanel.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/VPCPanel.tsx @@ -1,9 +1,9 @@ +import { Box } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import useMediaQuery from '@mui/material/useMediaQuery'; import * as React from 'react'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; -import { Box } from 'src/components/Box'; import { Checkbox } from 'src/components/Checkbox'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { Link } from 'src/components/Link'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeDiskActionMenu.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeDiskActionMenu.tsx index c1962d68618..c42beb7814e 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeDiskActionMenu.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeDiskActionMenu.tsx @@ -1,3 +1,4 @@ +import { Box } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import useMediaQuery from '@mui/material/useMediaQuery'; import { splitAt } from 'ramda'; @@ -5,7 +6,6 @@ import * as React from 'react'; import { useHistory } from 'react-router-dom'; import { ActionMenu } from 'src/components/ActionMenu/ActionMenu'; -import { Box } from 'src/components/Box'; import { InlineMenuAction } from 'src/components/InlineMenuAction/InlineMenuAction'; import { sendEvent } from 'src/utilities/analytics/utils'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeDisks.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeDisks.tsx index 1824a0a5429..a430e689c7a 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeDisks.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeDisks.tsx @@ -1,8 +1,8 @@ +import { Box } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; 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 { Hidden } from 'src/components/Hidden'; import OrderBy from 'src/components/OrderBy'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeVolumes.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeVolumes.tsx index 102df2b3f55..79d5ef0b923 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeVolumes.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeVolumes.tsx @@ -1,7 +1,7 @@ +import { Box } from '@linode/ui'; 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 { useIsBlockStorageEncryptionFeatureEnabled } from 'src/components/Encryption/utils'; import { Hidden } from 'src/components/Hidden'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeSummary/LinodeSummary.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeSummary/LinodeSummary.tsx index 1c913f40341..bdc0602ab39 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeSummary/LinodeSummary.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeSummary/LinodeSummary.tsx @@ -1,3 +1,4 @@ +import { Box } from '@linode/ui'; import { styled, useTheme } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import { DateTime } from 'luxon'; @@ -6,7 +7,6 @@ import { useParams } from 'react-router-dom'; import PendingIcon from 'src/assets/icons/pending.svg'; import { AreaChart } from 'src/components/AreaChart/AreaChart'; -import { Box } from 'src/components/Box'; import Select from 'src/components/EnhancedSelect/Select'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { Paper } from 'src/components/Paper'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeSummary/NetworkGraphs.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeSummary/NetworkGraphs.tsx index 8acff761d91..ba349ebc9c7 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeSummary/NetworkGraphs.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeSummary/NetworkGraphs.tsx @@ -1,21 +1,24 @@ -import { Stats } from '@linode/api-v4/lib/linodes'; -import { Theme, styled, useTheme } from '@mui/material/styles'; +import { Box } from '@linode/ui'; +import { styled, useTheme } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { AreaChart } from 'src/components/AreaChart/AreaChart'; -import { NetworkTimeData } from 'src/components/AreaChart/types'; -import { Box } from 'src/components/Box'; import { - NetworkUnit, convertNetworkToUnit, formatBitsPerSecond, generateNetworkUnits, } from 'src/features/Longview/shared/utilities'; -import { Metrics, getMetrics } from 'src/utilities/statMetrics'; +import { getMetrics } from 'src/utilities/statMetrics'; import { StatsPanel } from './StatsPanel'; +import type { Stats } from '@linode/api-v4/lib/linodes'; +import type { Theme } from '@mui/material/styles'; +import type { NetworkTimeData } from 'src/components/AreaChart/types'; +import type { NetworkUnit } from 'src/features/Longview/shared/utilities'; +import type { Metrics } from 'src/utilities/statMetrics'; + export interface TotalTrafficProps { combinedTraffic: string; inTraffic: string; diff --git a/packages/manager/src/features/Linodes/LinodesLanding/DisplayGroupedLinodes.tsx b/packages/manager/src/features/Linodes/LinodesLanding/DisplayGroupedLinodes.tsx index 5586742b1e3..4624f642627 100644 --- a/packages/manager/src/features/Linodes/LinodesLanding/DisplayGroupedLinodes.tsx +++ b/packages/manager/src/features/Linodes/LinodesLanding/DisplayGroupedLinodes.tsx @@ -1,3 +1,4 @@ +import { Box } from '@linode/ui'; import { Tooltip } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import { compose } from 'ramda'; @@ -5,7 +6,6 @@ import * as React from 'react'; import GridView from 'src/assets/icons/grid-view.svg'; import GroupByTag from 'src/assets/icons/group-by-tag.svg'; -import { Box } from 'src/components/Box'; import Paginate from 'src/components/Paginate'; import { MIN_PAGE_SIZE, diff --git a/packages/manager/src/features/Linodes/LinodesLanding/RegionTypeFilter.tsx b/packages/manager/src/features/Linodes/LinodesLanding/RegionTypeFilter.tsx index 1453568ec1f..a617588a5cb 100644 --- a/packages/manager/src/features/Linodes/LinodesLanding/RegionTypeFilter.tsx +++ b/packages/manager/src/features/Linodes/LinodesLanding/RegionTypeFilter.tsx @@ -1,8 +1,8 @@ +import { Box } from '@linode/ui'; import { Typography } from '@mui/material'; import * as React from 'react'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; -import { Box } from 'src/components/Box'; import { FormLabel } from 'src/components/FormLabel'; import { storage } from 'src/utilities/storage'; diff --git a/packages/manager/src/features/Linodes/MigrateLinode/ConfigureForm.styles.ts b/packages/manager/src/features/Linodes/MigrateLinode/ConfigureForm.styles.ts index c270f1c28ad..c82d327a144 100644 --- a/packages/manager/src/features/Linodes/MigrateLinode/ConfigureForm.styles.ts +++ b/packages/manager/src/features/Linodes/MigrateLinode/ConfigureForm.styles.ts @@ -1,6 +1,6 @@ +import { Box } from '@linode/ui'; import { styled } from '@mui/material/styles'; -import { Box } from 'src/components/Box'; import { Paper } from 'src/components/Paper'; export const StyledPaper = styled(Paper, { label: 'StyledPaper' })( diff --git a/packages/manager/src/features/Linodes/MigrateLinode/MigrateLinode.tsx b/packages/manager/src/features/Linodes/MigrateLinode/MigrateLinode.tsx index a221cd95a7e..cfb21c71dd2 100644 --- a/packages/manager/src/features/Linodes/MigrateLinode/MigrateLinode.tsx +++ b/packages/manager/src/features/Linodes/MigrateLinode/MigrateLinode.tsx @@ -1,8 +1,8 @@ +import { Box } from '@linode/ui'; import { styled, useTheme } from '@mui/material/styles'; import { useSnackbar } from 'notistack'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; import { Dialog } from 'src/components/Dialog/Dialog'; import { ErrorMessage } from 'src/components/ErrorMessage'; diff --git a/packages/manager/src/features/Linodes/MigrateLinode/MigrationPricing.tsx b/packages/manager/src/features/Linodes/MigrateLinode/MigrationPricing.tsx index 7b45196715e..3e6a513f652 100644 --- a/packages/manager/src/features/Linodes/MigrateLinode/MigrationPricing.tsx +++ b/packages/manager/src/features/Linodes/MigrateLinode/MigrationPricing.tsx @@ -1,9 +1,8 @@ -import { PriceObject } from '@linode/api-v4'; +import { Box } from '@linode/ui'; import { useTheme } from '@mui/material'; import { styled } from '@mui/material/styles'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { DisplayPrice } from 'src/components/DisplayPrice'; import { Typography } from 'src/components/Typography'; import { isNumber } from 'src/utilities/isNumber'; @@ -11,6 +10,7 @@ import { isNumber } from 'src/utilities/isNumber'; import { StyledSpan } from './ConfigureForm.styles'; import type { MigratePricePanelType } from './ConfigureForm'; +import type { PriceObject } from '@linode/api-v4'; export interface MigrationPricingProps { backups: 'disabled' | PriceObject | undefined; diff --git a/packages/manager/src/features/Lish/Glish.tsx b/packages/manager/src/features/Lish/Glish.tsx index c20835b0a86..bccf58d5fd4 100644 --- a/packages/manager/src/features/Lish/Glish.tsx +++ b/packages/manager/src/features/Lish/Glish.tsx @@ -1,8 +1,8 @@ /* eslint-disable no-unused-expressions */ +import { Box } from '@linode/ui'; import * as React from 'react'; import { VncScreen } from 'react-vnc'; -import { Box } from 'src/components/Box'; import { CircleProgress } from 'src/components/CircleProgress'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; diff --git a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/LinodeOrIPSelect.tsx b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/LinodeOrIPSelect.tsx index 756cf4be0b6..98a20f5062a 100644 --- a/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/LinodeOrIPSelect.tsx +++ b/packages/manager/src/features/LoadBalancers/LoadBalancerDetail/ServiceTargets/LinodeOrIPSelect.tsx @@ -1,8 +1,8 @@ +import { Box } from '@linode/ui'; import React from 'react'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { SelectedIcon } from 'src/components/Autocomplete/Autocomplete.styles'; -import { Box } from 'src/components/Box'; import { Stack } from 'src/components/Stack'; import { linodeFactory } from 'src/factories'; import { useInfiniteLinodesQuery } from 'src/queries/linodes/linodes'; diff --git a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Apache/Apache.tsx b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Apache/Apache.tsx index 140274354f0..ec95c0c5c73 100644 --- a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Apache/Apache.tsx +++ b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Apache/Apache.tsx @@ -1,22 +1,23 @@ -import { APIError } from '@linode/api-v4/lib/types'; +import { Box } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; import { Typography } from 'src/components/Typography'; import { isToday as _isToday } from 'src/utilities/isToday'; -import { WithStartAndEnd } from '../../../request.types'; -import { useGraphs } from '../OverviewGraphs/useGraphs'; import { StyledTimeRangeSelect, StyledTypography, } from '../CommonStyles.styles'; +import { useGraphs } from '../OverviewGraphs/useGraphs'; import { ApacheGraphs } from './ApacheGraphs'; +import type { WithStartAndEnd } from '../../../request.types'; +import type { APIError } from '@linode/api-v4/lib/types'; + interface Props { clientAPIKey?: string; lastUpdated?: number; diff --git a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Disks/Disks.styles.ts b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Disks/Disks.styles.ts index f2181307748..f575720e54a 100644 --- a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Disks/Disks.styles.ts +++ b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Disks/Disks.styles.ts @@ -1,6 +1,6 @@ +import { Box } from '@linode/ui'; import { styled } from '@mui/material/styles'; -import { Box } from 'src/components/Box'; import { TimeRangeSelect } from '../../../shared/TimeRangeSelect'; export const StyledBox = styled(Box, { label: 'StyledBox' })(({ theme }) => ({ diff --git a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/MySQL/MySQLLanding.tsx b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/MySQL/MySQLLanding.tsx index 3097dae7d3e..730d5db6c62 100644 --- a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/MySQL/MySQLLanding.tsx +++ b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/MySQL/MySQLLanding.tsx @@ -1,15 +1,13 @@ -import { APIError } from '@linode/api-v4/lib/types'; +import { Box } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; import { Typography } from 'src/components/Typography'; import { isToday as _isToday } from 'src/utilities/isToday'; -import { WithStartAndEnd } from '../../../request.types'; import { StyledTimeRangeSelect, StyledTypography, @@ -18,6 +16,9 @@ import { StyledItemGrid } from '../CommonStyles.styles'; import { useGraphs } from '../OverviewGraphs/useGraphs'; import { MySQLGraphs } from './MySQLGraphs'; +import type { WithStartAndEnd } from '../../../request.types'; +import type { APIError } from '@linode/api-v4/lib/types'; + interface Props { clientAPIKey?: string; lastUpdated?: number; diff --git a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/NGINX/NGINX.tsx b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/NGINX/NGINX.tsx index edc56fd31ba..10120cfdf8d 100644 --- a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/NGINX/NGINX.tsx +++ b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/NGINX/NGINX.tsx @@ -1,15 +1,13 @@ -import { APIError } from '@linode/api-v4/lib/types'; +import { Box } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; import { Typography } from 'src/components/Typography'; import { isToday as _isToday } from 'src/utilities/isToday'; -import { WithStartAndEnd } from '../../../request.types'; import { StyledItemGrid, StyledTimeRangeSelect, @@ -18,6 +16,9 @@ import { import { useGraphs } from '../OverviewGraphs/useGraphs'; import { NGINXGraphs } from './NGINXGraphs'; +import type { WithStartAndEnd } from '../../../request.types'; +import type { APIError } from '@linode/api-v4/lib/types'; + interface Props { clientAPIKey?: string; lastUpdated?: number; diff --git a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Processes/ProcessesGraphs.tsx b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Processes/ProcessesGraphs.tsx index bc8ac8949dc..65d964b732b 100644 --- a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Processes/ProcessesGraphs.tsx +++ b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Processes/ProcessesGraphs.tsx @@ -1,14 +1,10 @@ +import { Box } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { LongviewLineGraph } from 'src/components/LongviewLineGraph/LongviewLineGraph'; -import { Typography } from 'src/components/Typography'; import { Paper } from 'src/components/Paper'; -import { - LongviewProcesses, - WithStartAndEnd, -} from 'src/features/Longview/request.types'; +import { Typography } from 'src/components/Typography'; import { convertData, formatMemory, @@ -19,7 +15,11 @@ import { readableBytes, } from 'src/utilities/unitConversions'; -import { Process } from './types'; +import type { Process } from './types'; +import type { + LongviewProcesses, + WithStartAndEnd, +} from 'src/features/Longview/request.types'; interface Props { clientAPIKey: string; diff --git a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Processes/ProcessesLanding.styles.ts b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Processes/ProcessesLanding.styles.ts index c48618efc17..e0ebc591fd7 100644 --- a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Processes/ProcessesLanding.styles.ts +++ b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Processes/ProcessesLanding.styles.ts @@ -1,6 +1,6 @@ +import { Box } from '@linode/ui'; import { styled } from '@mui/material/styles'; -import { Box } from 'src/components/Box'; import { TimeRangeSelect } from '../../../shared/TimeRangeSelect'; export const StyledBox = styled(Box, { label: 'StyledBox' })(({ theme }) => ({ diff --git a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/TopProcesses.tsx b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/TopProcesses.tsx index d34a2585c31..440671c694c 100644 --- a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/TopProcesses.tsx +++ b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/TopProcesses.tsx @@ -1,7 +1,6 @@ -import { APIError } from '@linode/api-v4/lib/types'; +import { Box } from '@linode/ui'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import OrderBy from 'src/components/OrderBy'; import { Table } from 'src/components/Table'; import { TableBody } from 'src/components/TableBody'; @@ -13,16 +12,18 @@ import { TableRowError } from 'src/components/TableRowError/TableRowError'; import { TableRowLoading } from 'src/components/TableRowLoading/TableRowLoading'; import { TableSortCell } from 'src/components/TableSortCell'; import { Typography } from 'src/components/Typography'; -import { - LongviewTopProcesses, - TopProcessStat, -} from 'src/features/Longview/request.types'; import { readableBytes } from 'src/utilities/unitConversions'; import { formatCPU } from '../../shared/formatters'; import { StyledItemGrid } from './CommonStyles.styles'; import { StyledLink } from './TopProcesses.styles'; +import type { APIError } from '@linode/api-v4/lib/types'; +import type { + LongviewTopProcesses, + TopProcessStat, +} from 'src/features/Longview/request.types'; + export interface Props { clientID: number; lastUpdatedError?: APIError[]; diff --git a/packages/manager/src/features/Longview/LongviewLanding/LongviewList.tsx b/packages/manager/src/features/Longview/LongviewLanding/LongviewList.tsx index d42ee3dda4d..7b3053e7622 100644 --- a/packages/manager/src/features/Longview/LongviewLanding/LongviewList.tsx +++ b/packages/manager/src/features/Longview/LongviewLanding/LongviewList.tsx @@ -1,7 +1,6 @@ -import { LongviewClient } from '@linode/api-v4/lib/longview/types'; +import { Box } from '@linode/ui'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { StyledLinkButton } from 'src/components/Button/StyledLinkButton'; import { CircleProgress } from 'src/components/CircleProgress'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; @@ -9,10 +8,12 @@ import Paginate from 'src/components/Paginate'; import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFooter'; import { Paper } from 'src/components/Paper'; import { Typography } from 'src/components/Typography'; -import { Props as LVProps } from 'src/containers/longview.container'; import { LongviewListRows } from './LongviewListRows'; +import type { LongviewClient } from '@linode/api-v4/lib/longview/types'; +import type { Props as LVProps } from 'src/containers/longview.container'; + type LongviewProps = Omit< LVProps, | 'createLongviewClient' diff --git a/packages/manager/src/features/Longview/LongviewPackageDrawer.tsx b/packages/manager/src/features/Longview/LongviewPackageDrawer.tsx index b4210c353b4..894efe65cde 100644 --- a/packages/manager/src/features/Longview/LongviewPackageDrawer.tsx +++ b/packages/manager/src/features/Longview/LongviewPackageDrawer.tsx @@ -1,8 +1,8 @@ +import { Box } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import { pathOr } from 'ramda'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { Drawer } from 'src/components/Drawer'; import { Table } from 'src/components/Table'; import { TableBody } from 'src/components/TableBody'; @@ -10,14 +10,16 @@ import { TableCell } from 'src/components/TableCell'; import { TableHead } from 'src/components/TableHead'; import { TableRow } from 'src/components/TableRow'; import { TableRowEmpty } from 'src/components/TableRowEmpty/TableRowEmpty'; -import withLongviewStats, { +import withLongviewStats from 'src/containers/longview.stats.container'; + +import { LongviewPackageRow } from './LongviewPackageRow'; + +import type { LongviewPackage } from './request.types'; +import type { DispatchProps, LVClientData, } from 'src/containers/longview.stats.container'; -import { LongviewPackageRow } from './LongviewPackageRow'; -import { LongviewPackage } from './request.types'; - interface Props { clientID: number; clientLabel: string; diff --git a/packages/manager/src/features/Longview/LongviewPackageRow.tsx b/packages/manager/src/features/Longview/LongviewPackageRow.tsx index 15d96efb7ee..fc2d38cb665 100644 --- a/packages/manager/src/features/Longview/LongviewPackageRow.tsx +++ b/packages/manager/src/features/Longview/LongviewPackageRow.tsx @@ -1,11 +1,11 @@ +import { Box } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { TableCell } from 'src/components/TableCell'; import { TableRow } from 'src/components/TableRow'; -import { LongviewPackage } from './request.types'; +import type { LongviewPackage } from './request.types'; interface Props { lvPackage: LongviewPackage; diff --git a/packages/manager/src/features/Longview/shared/InstallationInstructions.tsx b/packages/manager/src/features/Longview/shared/InstallationInstructions.tsx index 06e7b7ddf2d..0b35620ad38 100644 --- a/packages/manager/src/features/Longview/shared/InstallationInstructions.tsx +++ b/packages/manager/src/features/Longview/shared/InstallationInstructions.tsx @@ -1,10 +1,11 @@ +import { Box } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { CopyTooltip } from 'src/components/CopyTooltip/CopyTooltip'; import { Link } from 'src/components/Link'; import { Typography } from 'src/components/Typography'; + import { StyledContainerGrid, StyledInstructionGrid, @@ -35,10 +36,10 @@ export const InstallationInstructions = React.memo((props: Props) => {
diff --git a/packages/manager/src/features/Managed/ManagedDashboardCard/ManagedChartPanel.tsx b/packages/manager/src/features/Managed/ManagedDashboardCard/ManagedChartPanel.tsx
index 83bb9e33cba..9b3d4b2bfbc 100644
--- a/packages/manager/src/features/Managed/ManagedDashboardCard/ManagedChartPanel.tsx
+++ b/packages/manager/src/features/Managed/ManagedDashboardCard/ManagedChartPanel.tsx
@@ -1,10 +1,8 @@
-import { DataSeries, ManagedStatsData } from '@linode/api-v4/lib/managed';
+import { Box } from '@linode/ui';
 import { useTheme } from '@mui/material/styles';
-import { Theme } from '@mui/material/styles';
 import * as React from 'react';
 
 import { AreaChart } from 'src/components/AreaChart/AreaChart';
-import { Box } from 'src/components/Box';
 import { CircleProgress } from 'src/components/CircleProgress';
 import { ErrorState } from 'src/components/ErrorState/ErrorState';
 import { TabbedPanel } from 'src/components/TabbedPanel/TabbedPanel';
@@ -23,6 +21,9 @@ import {
   StyledRootDiv,
 } from './ManagedChartPanel.styles';
 
+import type { DataSeries, ManagedStatsData } from '@linode/api-v4/lib/managed';
+import type { Theme } from '@mui/material/styles';
+
 const chartHeight = 300;
 
 interface NetworkTransferProps {
diff --git a/packages/manager/src/features/Managed/SSHAccess/LinodePubKey.tsx b/packages/manager/src/features/Managed/SSHAccess/LinodePubKey.tsx
index 2c1b1f3b631..5cf329f02c9 100644
--- a/packages/manager/src/features/Managed/SSHAccess/LinodePubKey.tsx
+++ b/packages/manager/src/features/Managed/SSHAccess/LinodePubKey.tsx
@@ -1,8 +1,8 @@
+import { Box } from '@linode/ui';
 import Grid from '@mui/material/Unstable_Grid2';
 import copy from 'copy-to-clipboard';
 import * as React from 'react';
 
-import { Box } from 'src/components/Box';
 import { Button } from 'src/components/Button/Button';
 import { ErrorState } from 'src/components/ErrorState/ErrorState';
 import { Link } from 'src/components/Link';
diff --git a/packages/manager/src/features/NodeBalancers/ConfigNodeIPSelect.tsx b/packages/manager/src/features/NodeBalancers/ConfigNodeIPSelect.tsx
index 5b086692862..706cacadcce 100644
--- a/packages/manager/src/features/NodeBalancers/ConfigNodeIPSelect.tsx
+++ b/packages/manager/src/features/NodeBalancers/ConfigNodeIPSelect.tsx
@@ -1,8 +1,8 @@
+import { Box } from '@linode/ui';
 import React from 'react';
 
 import { Autocomplete } from 'src/components/Autocomplete/Autocomplete';
 import { SelectedIcon } from 'src/components/Autocomplete/Autocomplete.styles';
-import { Box } from 'src/components/Box';
 import { Stack } from 'src/components/Stack';
 import { Typography } from 'src/components/Typography';
 import { useAllLinodesQuery } from 'src/queries/linodes/linodes';
diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerConfigNode.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerConfigNode.tsx
index c97f7bc1345..00a6c05c09c 100644
--- a/packages/manager/src/features/NodeBalancers/NodeBalancerConfigNode.tsx
+++ b/packages/manager/src/features/NodeBalancers/NodeBalancerConfigNode.tsx
@@ -1,9 +1,9 @@
+import { Box } from '@linode/ui';
 import { styled } from '@mui/material/styles';
 import Grid from '@mui/material/Unstable_Grid2';
 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 { Chip } from 'src/components/Chip';
 import { Divider } from 'src/components/Divider';
diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerCreate.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerCreate.tsx
index 8957d664cc4..096cc3925ce 100644
--- a/packages/manager/src/features/NodeBalancers/NodeBalancerCreate.tsx
+++ b/packages/manager/src/features/NodeBalancers/NodeBalancerCreate.tsx
@@ -1,3 +1,4 @@
+import { Box } from '@linode/ui';
 import { useTheme } from '@mui/material';
 import useMediaQuery from '@mui/material/useMediaQuery';
 import { createLazyRoute } from '@tanstack/react-router';
@@ -15,7 +16,6 @@ import { useHistory } from 'react-router-dom';
 
 import { Accordion } from 'src/components/Accordion';
 import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel';
-import { Box } from 'src/components/Box';
 import { Button } from 'src/components/Button/Button';
 import { CheckoutSummary } from 'src/components/CheckoutSummary/CheckoutSummary';
 import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog';
@@ -55,8 +55,8 @@ import { sendCreateNodeBalancerEvent } from 'src/utilities/analytics/customEvent
 import { getAPIErrorOrDefault } from 'src/utilities/errorUtils';
 import { getGDPRDetails } from 'src/utilities/formatRegion';
 import { getAPIErrorFor } from 'src/utilities/getAPIErrorFor';
-import { DOCS_LINK_LABEL_DC_PRICING } from 'src/utilities/pricing/constants';
 import { PRICE_ERROR_TOOLTIP_TEXT } from 'src/utilities/pricing/constants';
+import { DOCS_LINK_LABEL_DC_PRICING } from 'src/utilities/pricing/constants';
 import {
   getDCSpecificPriceByType,
   renderMonthlyPriceToCorrectDecimalPlace,
diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerConfigurations.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerConfigurations.tsx
index 66e713df1a8..ad4237c39ee 100644
--- a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerConfigurations.tsx
+++ b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerConfigurations.tsx
@@ -8,6 +8,7 @@ import {
   updateNodeBalancerConfig,
   updateNodeBalancerConfigNode,
 } from '@linode/api-v4/lib/nodebalancers';
+import { Box } from '@linode/ui';
 import { styled } from '@mui/material/styles';
 import {
   append,
@@ -26,7 +27,6 @@ import { compose as composeC } from 'recompose';
 
 import { Accordion } from 'src/components/Accordion';
 import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel';
-import { Box } from 'src/components/Box';
 import { Button } from 'src/components/Button/Button';
 import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog';
 import { DocumentTitleSegment } from 'src/components/DocumentTitle';
diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerFirewalls.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerFirewalls.tsx
index a328bac072e..0ab0801891f 100644
--- a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerFirewalls.tsx
+++ b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerFirewalls.tsx
@@ -1,8 +1,8 @@
 /* eslint-disable jsx-a11y/anchor-is-valid */
+import { Box } from '@linode/ui';
 import { Stack } from '@mui/material';
 import * as React from 'react';
 
-import { Box } from 'src/components/Box';
 import { Link } from 'src/components/Link';
 import { Table } from 'src/components/Table';
 import { TableBody } from 'src/components/TableBody';
diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/TablesPanel.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/TablesPanel.tsx
index f981ac37906..98342107eee 100644
--- a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/TablesPanel.tsx
+++ b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/TablesPanel.tsx
@@ -1,3 +1,4 @@
+import { Box } from '@linode/ui';
 import { useTheme } from '@mui/material/styles';
 import { styled } from '@mui/material/styles';
 import * as React from 'react';
@@ -5,7 +6,6 @@ import { useParams } from 'react-router-dom';
 
 import PendingIcon from 'src/assets/icons/pending.svg';
 import { AreaChart } from 'src/components/AreaChart/AreaChart';
-import { Box } from 'src/components/Box';
 import { CircleProgress } from 'src/components/CircleProgress';
 import { ErrorState } from 'src/components/ErrorState/ErrorState';
 import { Paper } from 'src/components/Paper';
diff --git a/packages/manager/src/features/components/PlansPanel/DistributedRegionPlanTable.tsx b/packages/manager/src/features/components/PlansPanel/DistributedRegionPlanTable.tsx
index 1f56b1ce1b5..0512c10190c 100644
--- a/packages/manager/src/features/components/PlansPanel/DistributedRegionPlanTable.tsx
+++ b/packages/manager/src/features/components/PlansPanel/DistributedRegionPlanTable.tsx
@@ -1,7 +1,7 @@
+import { Box } from '@linode/ui';
 import { styled } from '@mui/material/styles';
 import React from 'react';
 
-import { Box } from 'src/components/Box';
 import { Notice } from 'src/components/Notice/Notice';
 import { Paper } from 'src/components/Paper';
 import { Typography } from 'src/components/Typography';
diff --git a/packages/ui/.changeset/pr-11163-added-1729870035646.md b/packages/ui/.changeset/pr-11163-added-1729870035646.md
new file mode 100644
index 00000000000..715546f8923
--- /dev/null
+++ b/packages/ui/.changeset/pr-11163-added-1729870035646.md
@@ -0,0 +1,5 @@
+---
+"@linode/ui": Added
+---
+
+`Box` component from `manager` to `ui` package, part 1 ([#11163](https://github.com/linode/manager/pull/11163))
diff --git a/packages/manager/src/components/Box.stories.tsx b/packages/ui/src/components/Box/Box.stories.tsx
similarity index 85%
rename from packages/manager/src/components/Box.stories.tsx
rename to packages/ui/src/components/Box/Box.stories.tsx
index 11b944f45cb..bd3c2d899bf 100644
--- a/packages/manager/src/components/Box.stories.tsx
+++ b/packages/ui/src/components/Box/Box.stories.tsx
@@ -1,8 +1,9 @@
-import { Meta, StoryObj } from '@storybook/react';
 import React from 'react';
 
 import { Box } from './Box';
 
+import type { Meta, StoryObj } from '@storybook/react';
+
 const meta: Meta = {
   component: Box,
   title: 'Foundations/Box',
diff --git a/packages/ui/src/components/Box/Box.tsx b/packages/ui/src/components/Box/Box.tsx
new file mode 100644
index 00000000000..b7f8b802eca
--- /dev/null
+++ b/packages/ui/src/components/Box/Box.tsx
@@ -0,0 +1,14 @@
+import { default as _Box } from '@mui/material/Box';
+import React from 'react';
+
+import type { BoxProps } from '@mui/material/Box';
+
+/**
+ * The Box component serves as a wrapper for creating simple layouts or styles.
+ * It uses a `
` unless unless you change it with the `component` prop + */ +export const Box = (props: BoxProps) => { + return <_Box {...props} />; +}; + +export type { BoxProps }; diff --git a/packages/ui/src/components/Box/index.ts b/packages/ui/src/components/Box/index.ts new file mode 100644 index 00000000000..305f81d78bc --- /dev/null +++ b/packages/ui/src/components/Box/index.ts @@ -0,0 +1 @@ +export * from './Box'; diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index ec50522e5dc..d05c00c3913 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -1,5 +1,6 @@ -export * from './Chip'; export * from './BetaChip'; +export * from './Box'; +export * from './Chip'; export * from './FormControl'; export * from './FormHelperText'; export * from './IconButton'; From fef87a9d20e7260916e422b21e50f33553cc0be4 Mon Sep 17 00:00:00 2001 From: Connie Liu <139280159+coliu-akamai@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:51:40 -0400 Subject: [PATCH 22/66] refactor: [M3-8641] - Move Box component to ui package (Part 2) (#11164) * move box to ui package part 2 * consolidate double imports to linode/ui * Added changeset: `Box` component from `manager` to `ui` package, part 2 * missed box import --- .../components/ActionsPanel/ActionsPanel.tsx | 5 +- .../AkamaiBanner/AkamaiBanner.styles.ts | 2 +- .../components/AkamaiBanner/AkamaiBanner.tsx | 2 +- .../components/Autocomplete/Autocomplete.tsx | 3 +- .../manager/src/components/AvatarForProxy.tsx | 2 +- packages/manager/src/components/Box.tsx | 12 ----- .../CircleProgress/CircleProgress.tsx | 2 +- .../CopyableTextField/CopyableTextField.tsx | 2 +- .../DescriptionList/DescriptionList.tsx | 2 +- .../manager/src/components/Dialog/Dialog.tsx | 2 +- .../components/DialogTitle/DialogTitle.tsx | 4 +- .../DismissibleBanner/DismissibleBanner.tsx | 2 +- .../EditableInput.styles.tsx | 7 +-- .../EnhancedNumberInput.tsx | 2 +- .../EnhancedSelect/EnhancedSelect.stories.tsx | 3 +- .../EntityHeader/EntityHeader.stories.tsx | 2 +- packages/manager/src/components/Flag.tsx | 5 +- .../LineGraph/AccessibleGraphData.tsx | 3 +- .../manager/src/components/LinkButton.tsx | 5 +- .../src/components/MainContentBanner.tsx | 3 +- .../src/components/MaintenanceScreen.tsx | 6 ++- packages/manager/src/components/OSIcon.tsx | 5 +- .../PaginationFooter/PaginationFooter.tsx | 4 +- .../PlacementGroupSelectOption.tsx | 3 +- .../src/components/PrimaryNav/PrimaryNav.tsx | 3 +- .../PrimaryNav/SideMenu.stories.tsx | 3 +- .../PromiseLoader/PromiseLoader.test.tsx | 2 +- .../src/components/Radio/Radio.stories.tsx | 3 +- .../RegionMultiSelect.stories.tsx | 2 +- .../components/RegionSelect/RegionOption.tsx | 3 +- .../RegionSelect/RegionSelect.stories.tsx | 2 +- .../RegionSelect/RegionSelect.styles.ts | 2 +- .../RemovableSelectionsList.style.ts | 2 +- .../RemovableSelectionsList.tsx | 4 +- .../SelectFirewallPanel.tsx | 2 +- .../SelectRegionPanel/RegionHelperText.tsx | 5 +- .../manager/src/components/SplashScreen.tsx | 3 +- .../components/StackScript/StackScript.tsx | 8 +-- .../src/components/StatusIcon/StatusIcon.tsx | 3 +- .../components/TabbedPanel/TabbedPanel.tsx | 4 +- .../src/components/TagCell.stories.tsx | 3 +- .../TagsInput/TagsInput.stories.tsx | 5 +- packages/manager/src/components/TextField.tsx | 6 +-- .../TransferDisplay/TransferDisplay.styles.ts | 3 +- .../TransferDisplay/TransferDisplay.tsx | 2 +- .../TransferDisplay/TransferDisplayDialog.tsx | 4 +- .../DatabaseLanding/DatabaseLogo.tsx | 3 +- .../NodePoolsDisplay/NodePool.tsx | 3 +- .../LinodeNetworking/AddIPDrawer.tsx | 3 +- .../LinodesLanding/DisplayGroupedLinodes.tsx | 3 +- .../Events/NotificationCenterEvent.tsx | 2 +- .../NotificationCenter.styles.ts | 2 +- .../NotificationCenterNotificationMessage.tsx | 2 +- ...tificationCenterNotificationsContainer.tsx | 2 +- .../AccessKeyRegions/SelectedRegionsList.tsx | 2 +- .../AccessKeyLanding/CopyAllHostnames.tsx | 3 +- .../AccessKeyLanding/HostNamesDrawer.tsx | 2 +- .../AccessKeyLanding/HostNamesList.tsx | 2 +- .../BucketDetail/BucketDetail.tsx | 5 +- .../BucketDetail/ObjectTableRow.tsx | 4 +- .../BucketLanding/BucketRateLimitTable.tsx | 2 +- .../BucketLanding/OveragePricing.tsx | 2 +- .../PlacementGroupPolicyRadioGroup.tsx | 2 +- .../PlacementGroupsAssignLinodesDrawer.tsx | 4 +- .../PlacementGroupsSummary.tsx | 4 +- .../PlacementGroupsDetailPanel.tsx | 2 +- .../Profile/APITokens/APITokenTable.tsx | 2 +- .../PhoneVerification.styles.ts | 3 +- .../PhoneVerification/PhoneVerification.tsx | 3 +- .../AuthenticationSettings/ResetPassword.tsx | 2 +- .../AuthenticationSettings/SMSMessaging.tsx | 2 +- .../QuestionAndAnswerPair.tsx | 3 +- .../SecurityQuestions/SecurityQuestions.tsx | 2 +- .../AuthenticationSettings/TPAProviders.tsx | 7 +-- .../TwoFactor/ConfirmToken.tsx | 2 +- .../Profile/DisplaySettings/AvatarForm.tsx | 2 +- .../Profile/LishSettings/LishSettings.tsx | 3 +- .../Profile/OAuthClients/OAuthClients.tsx | 2 +- .../SecretTokenDialog/SecretTokenDialog.tsx | 2 +- .../Profile/Settings/PreferenceEditor.tsx | 6 ++- .../SelectStackScriptPanel.tsx | 52 +++++++++---------- .../UserDefinedFieldsPanel.styles.ts | 2 +- .../UserDefinedFieldsPanel.tsx | 2 +- .../manager/src/features/Support/Hively.tsx | 4 +- .../SupportTickets/SupportTicketDialog.tsx | 2 +- .../features/Support/TicketAttachmentRow.tsx | 2 +- .../NotificationMenu/NotificationMenu.tsx | 2 +- .../TopMenu/SearchBar/SearchSuggestion.tsx | 7 +-- .../manager/src/features/TopMenu/TopMenu.tsx | 3 +- .../features/TopMenu/UserMenu/UserMenu.tsx | 3 +- .../src/features/Users/UserPermissions.tsx | 2 +- .../Users/UserPermissionsEntitySection.tsx | 2 +- .../Users/UserProfile/DeleteUserPanel.tsx | 2 +- .../manager/src/features/Users/UserRow.tsx | 2 +- .../src/features/Users/UsersLanding.tsx | 2 +- .../VPCs/VPCCreateDrawer/VPCCreateDrawer.tsx | 2 +- .../VPCs/VPCDetail/AssignIPRanges.tsx | 2 +- .../SubnetAssignLinodesDrawer.styles.ts | 2 +- .../VPCDetail/SubnetAssignLinodesDrawer.tsx | 3 +- .../VPCs/VPCDetail/SubnetLinodeRow.tsx | 2 +- .../VPCDetail/SubnetUnassignLinodesDrawer.tsx | 2 +- .../VPCs/VPCDetail/VPCDetail.styles.ts | 2 +- .../src/features/VPCs/VPCDetail/VPCDetail.tsx | 2 +- .../VPCs/VPCDetail/VPCSubnetsTable.tsx | 2 +- .../features/Volumes/AttachVolumeDrawer.tsx | 3 +- .../features/Volumes/CloneVolumeDrawer.tsx | 2 +- .../src/features/Volumes/EditVolumeDrawer.tsx | 2 +- .../src/features/Volumes/VolumeCreate.tsx | 2 +- .../VolumeDrawer/LinodeVolumeCreateForm.tsx | 2 +- .../Volumes/VolumeDrawer/PricePanel.tsx | 2 +- .../Volumes/VolumeDrawer/SizeField.tsx | 3 +- .../src/features/Volumes/VolumeTableRow.tsx | 2 +- .../pr-11164-added-1729871599808.md | 5 ++ 113 files changed, 177 insertions(+), 211 deletions(-) delete mode 100644 packages/manager/src/components/Box.tsx create mode 100644 packages/ui/.changeset/pr-11164-added-1729871599808.md diff --git a/packages/manager/src/components/ActionsPanel/ActionsPanel.tsx b/packages/manager/src/components/ActionsPanel/ActionsPanel.tsx index d6ccc6a7053..e743777f5a0 100644 --- a/packages/manager/src/components/ActionsPanel/ActionsPanel.tsx +++ b/packages/manager/src/components/ActionsPanel/ActionsPanel.tsx @@ -1,12 +1,11 @@ +import { Box } from '@linode/ui'; import { styled } from '@mui/material/styles'; import * as React from 'react'; import { useStyles } from 'tss-react/mui'; import { Button } from 'src/components/Button/Button'; -import { Box } from '../Box'; - -import type { BoxProps } from '../Box'; +import type { BoxProps } from '@linode/ui'; import type { ButtonProps } from 'src/components/Button/Button'; interface ActionButtonsProps extends ButtonProps { diff --git a/packages/manager/src/components/AkamaiBanner/AkamaiBanner.styles.ts b/packages/manager/src/components/AkamaiBanner/AkamaiBanner.styles.ts index 39dc67c1658..1788f53a67e 100644 --- a/packages/manager/src/components/AkamaiBanner/AkamaiBanner.styles.ts +++ b/packages/manager/src/components/AkamaiBanner/AkamaiBanner.styles.ts @@ -1,10 +1,10 @@ +import { Box } from '@linode/ui'; import { styled } from '@mui/material/styles'; import Warning from 'src/assets/icons/warning.svg'; import AkamaiLogo from 'src/assets/logo/akamai-logo.svg'; import { omittedProps } from 'src/utilities/omittedProps'; -import { Box } from '../Box'; import { Stack } from '../Stack'; export const StyledAkamaiLogo = styled(AkamaiLogo, { diff --git a/packages/manager/src/components/AkamaiBanner/AkamaiBanner.tsx b/packages/manager/src/components/AkamaiBanner/AkamaiBanner.tsx index ea5d2bce76d..1e7421ac180 100644 --- a/packages/manager/src/components/AkamaiBanner/AkamaiBanner.tsx +++ b/packages/manager/src/components/AkamaiBanner/AkamaiBanner.tsx @@ -1,3 +1,4 @@ +import { Box } from '@linode/ui'; import { useMediaQuery, useTheme } from '@mui/material'; import * as React from 'react'; @@ -6,7 +7,6 @@ import { Typography } from 'src/components/Typography'; import { useFlags } from 'src/hooks/useFlags'; import { replaceNewlinesWithLineBreaks } from 'src/utilities/replaceNewlinesWithLineBreaks'; -import { Box } from '../Box'; import { Stack } from '../Stack'; import { StyledAkamaiLogo, diff --git a/packages/manager/src/components/Autocomplete/Autocomplete.tsx b/packages/manager/src/components/Autocomplete/Autocomplete.tsx index 7e8ac138df6..a02b78db4a7 100644 --- a/packages/manager/src/components/Autocomplete/Autocomplete.tsx +++ b/packages/manager/src/components/Autocomplete/Autocomplete.tsx @@ -1,10 +1,9 @@ -import { InputAdornment } from '@linode/ui'; +import { Box, InputAdornment } from '@linode/ui'; import CloseIcon from '@mui/icons-material/Close'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import MuiAutocomplete from '@mui/material/Autocomplete'; import React from 'react'; -import { Box } from 'src/components/Box'; import { TextField } from 'src/components/TextField'; import { CircleProgress } from '../CircleProgress'; diff --git a/packages/manager/src/components/AvatarForProxy.tsx b/packages/manager/src/components/AvatarForProxy.tsx index 16840219df4..d144b083e53 100644 --- a/packages/manager/src/components/AvatarForProxy.tsx +++ b/packages/manager/src/components/AvatarForProxy.tsx @@ -1,8 +1,8 @@ +import { Box } from '@linode/ui'; import { styled } from '@mui/material/styles'; import * as React from 'react'; import ProxyUserIcon from 'src/assets/icons/parent-child.svg'; -import { Box } from 'src/components/Box'; interface Props { height?: number; diff --git a/packages/manager/src/components/Box.tsx b/packages/manager/src/components/Box.tsx deleted file mode 100644 index 3bc8cfdb69e..00000000000 --- a/packages/manager/src/components/Box.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { default as _Box, BoxProps } from '@mui/material/Box'; -import React from 'react'; - -/** - * The Box component serves as a wrapper for creating simple layouts or styles. - * It uses a `
` unless unless you change it with the `component` prop - */ -export const Box = (props: BoxProps) => { - return <_Box {...props} />; -}; - -export type { BoxProps }; diff --git a/packages/manager/src/components/CircleProgress/CircleProgress.tsx b/packages/manager/src/components/CircleProgress/CircleProgress.tsx index 474e6e05c9c..1d177ce85e0 100644 --- a/packages/manager/src/components/CircleProgress/CircleProgress.tsx +++ b/packages/manager/src/components/CircleProgress/CircleProgress.tsx @@ -1,8 +1,8 @@ +import { Box } from '@linode/ui'; import _CircularProgress from '@mui/material/CircularProgress'; import { styled } from '@mui/material/styles'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { omittedProps } from 'src/utilities/omittedProps'; import type { CircularProgressProps } from '@mui/material/CircularProgress'; diff --git a/packages/manager/src/components/CopyableTextField/CopyableTextField.tsx b/packages/manager/src/components/CopyableTextField/CopyableTextField.tsx index fbccbae4da6..b26c74a7267 100644 --- a/packages/manager/src/components/CopyableTextField/CopyableTextField.tsx +++ b/packages/manager/src/components/CopyableTextField/CopyableTextField.tsx @@ -1,7 +1,7 @@ +import { Box } from '@linode/ui'; import { styled } from '@mui/material/styles'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { CopyTooltip } from 'src/components/CopyTooltip/CopyTooltip'; import { TextField } from 'src/components/TextField'; diff --git a/packages/manager/src/components/DescriptionList/DescriptionList.tsx b/packages/manager/src/components/DescriptionList/DescriptionList.tsx index 4711612dd7d..424d53fd90a 100644 --- a/packages/manager/src/components/DescriptionList/DescriptionList.tsx +++ b/packages/manager/src/components/DescriptionList/DescriptionList.tsx @@ -1,7 +1,7 @@ +import { Box } from '@linode/ui'; import useMediaQuery from '@mui/material/useMediaQuery'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { TooltipIcon } from 'src/components/TooltipIcon'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/components/Dialog/Dialog.tsx b/packages/manager/src/components/Dialog/Dialog.tsx index 1ab4366ca12..8a217c008ea 100644 --- a/packages/manager/src/components/Dialog/Dialog.tsx +++ b/packages/manager/src/components/Dialog/Dialog.tsx @@ -1,9 +1,9 @@ +import { Box } from '@linode/ui'; import _Dialog from '@mui/material/Dialog'; import DialogContent from '@mui/material/DialogContent'; import { styled, useTheme } from '@mui/material/styles'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { DialogTitle } from 'src/components/DialogTitle/DialogTitle'; import { Notice } from 'src/components/Notice/Notice'; import { omittedProps } from 'src/utilities/omittedProps'; diff --git a/packages/manager/src/components/DialogTitle/DialogTitle.tsx b/packages/manager/src/components/DialogTitle/DialogTitle.tsx index 711c328c419..ecf01cd0200 100644 --- a/packages/manager/src/components/DialogTitle/DialogTitle.tsx +++ b/packages/manager/src/components/DialogTitle/DialogTitle.tsx @@ -1,11 +1,9 @@ -import { IconButton } from '@linode/ui'; +import { Box, IconButton } from '@linode/ui'; import Close from '@mui/icons-material/Close'; import { Typography } from '@mui/material'; import _DialogTitle from '@mui/material/DialogTitle'; import * as React from 'react'; -import { Box } from 'src/components/Box'; - import type { SxProps, Theme } from '@mui/material'; interface DialogTitleProps { diff --git a/packages/manager/src/components/DismissibleBanner/DismissibleBanner.tsx b/packages/manager/src/components/DismissibleBanner/DismissibleBanner.tsx index 2f1781f9125..0322ed2ffbf 100644 --- a/packages/manager/src/components/DismissibleBanner/DismissibleBanner.tsx +++ b/packages/manager/src/components/DismissibleBanner/DismissibleBanner.tsx @@ -1,8 +1,8 @@ +import { Box } from '@linode/ui'; import Close from '@mui/icons-material/Close'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; -import { Box } from 'src/components/Box'; import { useDismissibleNotifications } from 'src/hooks/useDismissibleNotifications'; import { StyledButton, StyledNotice } from './DismissibleBanner.styles'; diff --git a/packages/manager/src/components/EditableEntityLabel/EditableInput.styles.tsx b/packages/manager/src/components/EditableEntityLabel/EditableInput.styles.tsx index 5532318f7f7..303542ee695 100644 --- a/packages/manager/src/components/EditableEntityLabel/EditableInput.styles.tsx +++ b/packages/manager/src/components/EditableEntityLabel/EditableInput.styles.tsx @@ -1,13 +1,14 @@ +import { Box } from '@linode/ui'; import Edit from '@mui/icons-material/Edit'; import { styled } from '@mui/material/styles'; -import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; -import { TextField, TextFieldProps } from 'src/components/TextField'; +import { TextField } from 'src/components/TextField'; import { Typography } from 'src/components/Typography'; import { fadeIn } from 'src/styles/keyframes'; -import { EditableTextVariant } from './EditableInput'; +import type { EditableTextVariant } from './EditableInput'; +import type { TextFieldProps } from 'src/components/TextField'; export const StyledTypography = styled(Typography, { label: 'EditableInput__StyledTypography', diff --git a/packages/manager/src/components/EnhancedNumberInput/EnhancedNumberInput.tsx b/packages/manager/src/components/EnhancedNumberInput/EnhancedNumberInput.tsx index 6d8d47f0307..cc28261f750 100644 --- a/packages/manager/src/components/EnhancedNumberInput/EnhancedNumberInput.tsx +++ b/packages/manager/src/components/EnhancedNumberInput/EnhancedNumberInput.tsx @@ -1,9 +1,9 @@ +import { Box } from '@linode/ui'; import { styled } from '@mui/material/styles'; import * as React from 'react'; import Minus from 'src/assets/icons/LKEminusSign.svg'; import Plus from 'src/assets/icons/LKEplusSign.svg'; -import { Box } from 'src/components/Box'; import { Button } from 'src/components/Button/Button'; import { TextField } from 'src/components/TextField'; diff --git a/packages/manager/src/components/EnhancedSelect/EnhancedSelect.stories.tsx b/packages/manager/src/components/EnhancedSelect/EnhancedSelect.stories.tsx index dcb03e8fa85..dea546aca84 100644 --- a/packages/manager/src/components/EnhancedSelect/EnhancedSelect.stories.tsx +++ b/packages/manager/src/components/EnhancedSelect/EnhancedSelect.stories.tsx @@ -1,8 +1,7 @@ +import { Box } from '@linode/ui'; import { Typography } from '@mui/material'; import React from 'react'; -import { Box } from 'src/components/Box'; - import Select from './Select'; import type { Item } from './Select'; diff --git a/packages/manager/src/components/EntityHeader/EntityHeader.stories.tsx b/packages/manager/src/components/EntityHeader/EntityHeader.stories.tsx index e1b05f120ad..abc97c5a100 100644 --- a/packages/manager/src/components/EntityHeader/EntityHeader.stories.tsx +++ b/packages/manager/src/components/EntityHeader/EntityHeader.stories.tsx @@ -1,7 +1,7 @@ +import { Box } from '@linode/ui'; import { action } from '@storybook/addon-actions'; import React from 'react'; -import { Box } from 'src/components/Box'; import { EntityHeader } from 'src/components/EntityHeader/EntityHeader'; import { Hidden } from 'src/components/Hidden'; import { LinodeActionMenu } from 'src/features/Linodes/LinodesLanding/LinodeActionMenu/LinodeActionMenu'; diff --git a/packages/manager/src/components/Flag.tsx b/packages/manager/src/components/Flag.tsx index bd01d381754..5653b3d2fa8 100644 --- a/packages/manager/src/components/Flag.tsx +++ b/packages/manager/src/components/Flag.tsx @@ -1,11 +1,10 @@ +import { Box } from '@linode/ui'; import { styled } from '@mui/material/styles'; import 'flag-icons/css/flag-icons.min.css'; import React from 'react'; -import { Box } from './Box'; - -import type { BoxProps } from './Box'; import type { Country } from '@linode/api-v4'; +import type { BoxProps } from '@linode/ui'; const COUNTRY_FLAG_OVERRIDES = { uk: 'gb', diff --git a/packages/manager/src/components/LineGraph/AccessibleGraphData.tsx b/packages/manager/src/components/LineGraph/AccessibleGraphData.tsx index dfca22fd283..054c111feed 100644 --- a/packages/manager/src/components/LineGraph/AccessibleGraphData.tsx +++ b/packages/manager/src/components/LineGraph/AccessibleGraphData.tsx @@ -2,12 +2,11 @@ * ONLY USED IN THE LINE GRAPH COMPONENT (Longview) * Delete when LineGraph is sunsetted */ +import { Box } from '@linode/ui'; import { visuallyHidden } from '@mui/utils'; import { DateTime } from 'luxon'; import * as React from 'react'; -import { Box } from 'src/components/Box'; - import type { ChartData, ChartPoint } from 'chart.js'; export interface GraphTabledDataProps { diff --git a/packages/manager/src/components/LinkButton.tsx b/packages/manager/src/components/LinkButton.tsx index bcb5a83c521..d3d7caa7f09 100644 --- a/packages/manager/src/components/LinkButton.tsx +++ b/packages/manager/src/components/LinkButton.tsx @@ -1,12 +1,13 @@ -import { Theme } from '@mui/material/styles'; +import { Box } from '@linode/ui'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; import { CircleProgress } from 'src/components/CircleProgress'; -import { Box } from './Box'; import { StyledLinkButton } from './Button/StyledLinkButton'; +import type { Theme } from '@mui/material/styles'; + const useStyles = makeStyles()((theme: Theme) => ({ disabled: { color: theme.palette.text.primary, diff --git a/packages/manager/src/components/MainContentBanner.tsx b/packages/manager/src/components/MainContentBanner.tsx index dc1cfb4f56b..a71c8fb0f37 100644 --- a/packages/manager/src/components/MainContentBanner.tsx +++ b/packages/manager/src/components/MainContentBanner.tsx @@ -1,3 +1,4 @@ +import { Box } from '@linode/ui'; import Close from '@mui/icons-material/Close'; import { IconButton } from '@mui/material'; import * as React from 'react'; @@ -10,8 +11,6 @@ import { usePreferences, } from 'src/queries/profile/preferences'; -import { Box } from './Box'; - export const MainContentBanner = React.memo(() => { // Uncomment this to test this banner: // diff --git a/packages/manager/src/components/MaintenanceScreen.tsx b/packages/manager/src/components/MaintenanceScreen.tsx index 7b320806fb4..03d1c85efae 100644 --- a/packages/manager/src/components/MaintenanceScreen.tsx +++ b/packages/manager/src/components/MaintenanceScreen.tsx @@ -1,14 +1,16 @@ +import { Box } from '@linode/ui'; import BuildIcon from '@mui/icons-material/Build'; -import { Theme, useTheme } from '@mui/material/styles'; +import { useTheme } from '@mui/material/styles'; import * as React from 'react'; import Logo from 'src/assets/logo/akamai-logo.svg'; -import { Box } from 'src/components/Box'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { Link } from 'src/components/Link'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; +import type { Theme } from '@mui/material/styles'; + export const MaintenanceScreen = () => { const theme = useTheme(); diff --git a/packages/manager/src/components/OSIcon.tsx b/packages/manager/src/components/OSIcon.tsx index 3a0524da8a5..6ddb6034e40 100644 --- a/packages/manager/src/components/OSIcon.tsx +++ b/packages/manager/src/components/OSIcon.tsx @@ -1,10 +1,9 @@ +import { Box } from '@linode/ui'; import 'font-logos/assets/font-logos.css'; import React from 'react'; -import { Box } from './Box'; - -import type { BoxProps } from './Box'; import type { Image } from '@linode/api-v4'; +import type { BoxProps } from '@linode/ui'; interface Props extends BoxProps { /** diff --git a/packages/manager/src/components/PaginationFooter/PaginationFooter.tsx b/packages/manager/src/components/PaginationFooter/PaginationFooter.tsx index 7bef8e245ca..eefa46d7d30 100644 --- a/packages/manager/src/components/PaginationFooter/PaginationFooter.tsx +++ b/packages/manager/src/components/PaginationFooter/PaginationFooter.tsx @@ -1,9 +1,9 @@ +import { Box } from '@linode/ui'; import { styled, useTheme } from '@mui/material/styles'; import * as React from 'react'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; -import { Box } from '../Box'; import { PaginationControls } from '../PaginationControls/PaginationControls'; export const MIN_PAGE_SIZE = 25; @@ -67,9 +67,9 @@ export const PaginationFooter = (props: Props) => { background: theme.bg.bgPaper, }} alignItems="center" + data-qa-table-pagination display="flex" justifyContent="space-between" - data-qa-table-pagination > {!isShowingAll && (
', + '', + '', + '', + '', + '', + '', + '', + ``, + ``, + ``, + ``, + '', + '
:x: Failing:white_check_mark: Passing:arrow_right_hook: Skipped:clock1: Duration
${runInfo.failing} Failing${runInfo.passing} Passing${runInfo.skipped} Skipped${secondsToTimeString(runInfo.time)}
', + '\n\n', + ].join(''); const extra = metadata.extra ? `${metadata.extra}\n\n` : null; const failedTestSummary = (() => { - const heading = `### Details`; + const heading = `#### Details`; const failedTestHeader = ``; const failedTestRows = results .filter((result: TestResult) => result.failing) @@ -62,7 +83,7 @@ export const githubFormatter: Formatter = ( })(); const rerunNote = (() => { - const heading = `### Debugging`; + const heading = `#### Troubleshooting`; const failingTestFiles = results .filter((result: TestResult) => result.failing) .map((result: TestResult) => result.testFilename); @@ -81,6 +102,7 @@ export const githubFormatter: Formatter = ( })(); return [ + title, headline, '', breakdown, From 42f6cc20cd24e3ecc6275344fa90d1024f09f8a9 Mon Sep 17 00:00:00 2001 From: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com> Date: Fri, 1 Nov 2024 11:37:24 -0700 Subject: [PATCH 36/66] feat: [M3-8719] - Add a 'Mask Sensitive Data' setting to Cloud Manager (#11143) * Add boolean for redacted data to user preferences * Add RedactableText component * Apply RedactableText to Linode landing IP Addresses * Try a styling fix * Fix logic error and rename variables to be less confusing * Redact text on Account Login History tab and Profile Login & Auth tab * Update copy * Create and test util for masked string * Mask text in IPAddress component * Replace instances of RedactableText with MaskableText * Mask billing contact info * Mask user pages * Hide masking toggle icon for IPAddresses when setting off * Mask top menu * Clean up props * Revert top menu * Fix copy icon not copying plaintext value * Rename preference key for consistency * Clean up - typo, linting, comments * Unit test IPAddress component and fix issues * Rename MaskableTextTooltip to VisibilityTooltip * Add test for MaskableText * Add stories * Organize tooltip story with rest of tooltips * Mask fields in AccessTable * Mask IP addresses in Linode Details Network tab * Mask credit card and third party payment details * Fix doubly masked third party payment * Added changeset: Mask Sensitive Data preference to Profile Settings * Clean up of a variable name for consistency in naming * Fix Tooltip import * Update packages/manager/src/utilities/createMaskedText.ts Co-authored-by: Hana Xu <115299789+hana-akamai@users.noreply.github.com> * Address feedback: clean up unnecessary prop; update test * Address feedback: add constant for unmaskedText * Address feedback: Replace displayText with masked prop in CopyTooltip * Add masked visibility toggle to CopyTooltip * Move VisibilityTooltip to ui package * Address feedback: misalignment of AccessTable row components * Fix circular dependency and update IPAddress unit test * Update unit test coverage * Address feedback: tooltip interactivity, placement, hover color * Mask Kube Details page * Mask Firewall rules and lint * Mask Service Transfers * Mask OBJ hostname and access keys * Mask Trusted Devices * Address feedback: update children prop, remove fragments * Remove problematic Storybook story * Address feedback: update MaskableText and util to use consistent lengths * Set the length of masked data fields * Fix console errors and test failures * Fix the unit test that escaped me * Add documentation of feature * Fix a markdown issue in the PR template --------- Co-authored-by: Hana Xu <115299789+hana-akamai@users.noreply.github.com> --- docs/CONTRIBUTING.md | 3 +- docs/PULL_REQUEST_TEMPLATE.md | 4 +- .../02-component-structure.md | 4 + .../pr-11143-added-1729792743385.md | 5 + .../CopyTooltip/CopyTooltip.test.tsx | 88 +++++++++++++ .../components/CopyTooltip/CopyTooltip.tsx | 59 +++++++-- .../MaskableText/MaskableText.test.tsx | 107 ++++++++++++++++ .../components/MaskableText/MaskableText.tsx | 73 +++++++++++ .../PaymentMethodRow/ThirdPartyPayment.tsx | 20 +-- packages/manager/src/factories/preferences.ts | 3 +- .../Account/AccountLoginsTableRow.tsx | 21 ++-- .../PaymentDrawer/CreditCard.tsx | 28 +++-- .../ContactInfoPanel/ContactInformation.tsx | 118 +++++++++++------- .../EntityTransfers/RenderTransferRow.tsx | 12 +- .../Rules/FirewallRuleTable.tsx | 4 +- .../KubeConfigDisplay.tsx | 29 +++-- .../NodePoolsDisplay/NodeRow.tsx | 9 +- .../NodePoolsDisplay/NodeTable.styles.ts | 1 - .../src/features/Linodes/AccessTable.tsx | 5 + .../Linodes/LinodeEntityDetail.styles.ts | 1 + .../Linodes/LinodeEntityDetailBody.tsx | 24 +++- .../LinodeNetworking/LinodeIPAddressRow.tsx | 10 +- .../LinodesLanding/IPAddress.styles.ts | 3 +- .../Linodes/LinodesLanding/IPAddress.test.tsx | 87 +++++++++++++ .../Linodes/LinodesLanding/IPAddress.tsx | 6 +- .../AccessKeyTable/AccessKeyTableRow.tsx | 12 +- .../BucketLanding/BucketDetailsDrawer.tsx | 15 ++- .../BucketLanding/BucketTableRow.tsx | 35 +++--- .../PhoneVerification/PhoneVerification.tsx | 12 +- .../SecurityQuestions/Question.tsx | 12 +- .../AuthenticationSettings/TrustedDevices.tsx | 12 +- .../features/Profile/Settings/Settings.tsx | 23 ++++ .../Users/UserProfile/UserDetailsPanel.tsx | 12 +- .../manager/src/features/Users/UserRow.tsx | 8 +- .../manager/src/types/ManagerPreferences.ts | 1 + .../src/utilities/createMaskedText.test.ts | 35 ++++++ .../manager/src/utilities/createMaskedText.ts | 23 ++++ .../VisibilityTooltip.stories.tsx | 21 ++++ .../VisibilityTooltip/VisibilityTooltip.tsx | 67 ++++++++++ .../src/components/VisibilityTooltip/index.ts | 1 + packages/ui/src/components/index.ts | 1 + 41 files changed, 865 insertions(+), 149 deletions(-) create mode 100644 packages/manager/.changeset/pr-11143-added-1729792743385.md create mode 100644 packages/manager/src/components/CopyTooltip/CopyTooltip.test.tsx create mode 100644 packages/manager/src/components/MaskableText/MaskableText.test.tsx create mode 100644 packages/manager/src/components/MaskableText/MaskableText.tsx create mode 100644 packages/manager/src/utilities/createMaskedText.test.ts create mode 100644 packages/manager/src/utilities/createMaskedText.ts create mode 100644 packages/ui/src/components/VisibilityTooltip/VisibilityTooltip.stories.tsx create mode 100644 packages/ui/src/components/VisibilityTooltip/VisibilityTooltip.tsx create mode 100644 packages/ui/src/components/VisibilityTooltip/index.ts diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index cbf69499d81..c64ee516fe9 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -27,7 +27,8 @@ Feel free to open an issue to report a bug or request a feature. **Example:** `feat: [M3-1234] - Allow user to view their login history` 6. Open a pull request against `develop` and make sure the title follows the same format as the commit message. -7. If needed, create a changeset to populate our changelog. +7. Keep in mind that our repository is public and open source! Before adding screenshots to your PR, we recommend you enable the **Mask Sensitive Data** setting in Cloud Manager [Profile Settings](https://cloud.linode.com/profile/settings). +8. If needed, create a changeset to populate our changelog. - If you don't have the Github CLI installed or need to update it (you need GH CLI 2.21.0 or greater), - install it via `brew`: https://github.com/cli/cli#installation or upgrade with `brew upgrade gh` - Once installed, run `gh repo set-default` and pick `linode/manager` (only > 2.21.0) diff --git a/docs/PULL_REQUEST_TEMPLATE.md b/docs/PULL_REQUEST_TEMPLATE.md index 15e72c8495e..01333bdab73 100644 --- a/docs/PULL_REQUEST_TEMPLATE.md +++ b/docs/PULL_REQUEST_TEMPLATE.md @@ -10,7 +10,9 @@ List any change relevant to the reviewer. Please specify a release date to guarantee timely review of this PR. If exact date is not known, please approximate and update it as needed. ## Preview 📷 -**Include a screenshot or screen recording of the change** +**Include a screenshot or screen recording of the change.** + +:lock: Use the [Mask Sensitive Data](https://cloud.linode.com/profile/settings) setting for security. :bulb: Use `
Failing Tests
SpecTest
- - Label - Access Key + + ({ + [theme.breakpoints.up('md')]: { + minWidth: 120, + }, + })} + > + Label + + Access Key {isObjMultiClusterEnabled && ( - - Regions/S3 Hostnames - + + Regions/S3 Hostnames + )} - - Actions - + @@ -82,6 +85,7 @@ export const AccessKeyTable = (props: AccessKeyTableProps) => { data={data} error={error} isLoading={isLoading} + isObjMultiClusterEnabled={isObjMultiClusterEnabled} isRestrictedUser={isRestrictedUser} openDrawer={openDrawer} openRevokeDialog={openRevokeDialog} diff --git a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyTable/AccessKeyTableBody.tsx b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyTable/AccessKeyTableBody.tsx index 3e193b305f6..34c0c9493a2 100644 --- a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyTable/AccessKeyTableBody.tsx +++ b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyTable/AccessKeyTableBody.tsx @@ -1,73 +1,79 @@ -import { - ObjectStorageKey, - ObjectStorageKeyRegions, -} from '@linode/api-v4/lib/object-storage'; -import { APIError } from '@linode/api-v4/lib/types'; import React from 'react'; import { TableRowEmpty } from 'src/components/TableRowEmpty/TableRowEmpty'; import { TableRowError } from 'src/components/TableRowError/TableRowError'; import { TableRowLoading } from 'src/components/TableRowLoading/TableRowLoading'; -import { OpenAccessDrawer } from '../types'; import { AccessKeyTableRow } from './AccessKeyTableRow'; -type Props = { +import type { OpenAccessDrawer } from '../types'; +import type { + APIError, + ObjectStorageKey, + ObjectStorageKeyRegions, +} from '@linode/api-v4'; + +interface Props { data: ObjectStorageKey[] | undefined; error: APIError[] | null | undefined; isLoading: boolean; + isObjMultiClusterEnabled: boolean; isRestrictedUser: boolean; openDrawer: OpenAccessDrawer; openRevokeDialog: (objectStorageKey: ObjectStorageKey) => void; setHostNames: (hostNames: ObjectStorageKeyRegions[]) => void; setShowHostNamesDrawers: (show: boolean) => void; -}; +} + +export const AccessKeyTableBody = (props: Props) => { + const { + data, + error, + isLoading, + isObjMultiClusterEnabled, + isRestrictedUser, + openDrawer, + openRevokeDialog, + setHostNames, + setShowHostNamesDrawers, + } = props; + + const cols = isObjMultiClusterEnabled ? 4 : 3; -export const AccessKeyTableBody = ({ - data, - error, - isLoading, - isRestrictedUser, - openDrawer, - openRevokeDialog, - setHostNames, - setShowHostNamesDrawers, -}: Props) => { if (isRestrictedUser) { - return ; + return ; } if (isLoading) { - return ; + return ( + + ); } if (error) { return ( ); } - return ( - <> - {data && data.length > 0 ? ( - <> - {data.map((eachKey: ObjectStorageKey, index) => ( - - ))} - - ) : ( - - )} - - ); + if (data?.length === 0) { + return ; + } + + return data?.map((key) => ( + + )); }; diff --git a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyTable/AccessKeyTableRow.tsx b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyTable/AccessKeyTableRow.tsx index e3abe17a7d9..f9755977e65 100644 --- a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyTable/AccessKeyTableRow.tsx +++ b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyTable/AccessKeyTableRow.tsx @@ -2,6 +2,7 @@ import { styled } from '@mui/material/styles'; import React from 'react'; import { CopyTooltip } from 'src/components/CopyTooltip/CopyTooltip'; +import { Hidden } from 'src/components/Hidden'; import { MaskableText } from 'src/components/MaskableText/MaskableText'; import { Stack } from 'src/components/Stack'; import { TableCell } from 'src/components/TableCell'; @@ -12,30 +13,28 @@ import { useFlags } from 'src/hooks/useFlags'; import { isFeatureEnabledV2 } from 'src/utilities/accountCapabilities'; import { AccessKeyActionMenu } from './AccessKeyActionMenu'; -import { StyledLastColumnCell } from './AccessKeyTable.styles'; import { HostNameTableCell } from './HostNameTableCell'; import type { OpenAccessDrawer } from '../types'; -import type { - ObjectStorageKey, - ObjectStorageKeyRegions, -} from '@linode/api-v4/lib/object-storage'; +import type { ObjectStorageKey, ObjectStorageKeyRegions } from '@linode/api-v4'; -type Props = { +interface Props { openDrawer: OpenAccessDrawer; openRevokeDialog: (storageKeyData: ObjectStorageKey) => void; setHostNames: (hostNames: ObjectStorageKeyRegions[]) => void; setShowHostNamesDrawers: (show: boolean) => void; storageKeyData: ObjectStorageKey; -}; +} + +export const AccessKeyTableRow = (props: Props) => { + const { + openDrawer, + openRevokeDialog, + setHostNames, + setShowHostNamesDrawers, + storageKeyData, + } = props; -export const AccessKeyTableRow = ({ - openDrawer, - openRevokeDialog, - setHostNames, - setShowHostNamesDrawers, - storageKeyData, -}: Props) => { const { account } = useAccountManagement(); const flags = useFlags(); @@ -47,37 +46,36 @@ export const AccessKeyTableRow = ({ return ( - - - {storageKeyData.label} - - + {storageKeyData.label} - - {storageKeyData.access_key} - + {storageKeyData.access_key} {isObjMultiClusterEnabled && ( - + + + )} - - + { + setShowHostNamesDrawers(true); + setHostNames(storageKeyData.regions); + }} label={storageKeyData.label} objectStorageKey={storageKeyData} openDrawer={openDrawer} openRevokeDialog={openRevokeDialog} /> - + ); }; diff --git a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyTable/HostNameTableCell.tsx b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyTable/HostNameTableCell.tsx index 14f0595acca..bb67e6c83f8 100644 --- a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyTable/HostNameTableCell.tsx +++ b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/AccessKeyTable/HostNameTableCell.tsx @@ -7,22 +7,17 @@ import { TableCell } from 'src/components/TableCell'; import { useRegionsQuery } from 'src/queries/regions/regions'; import { getRegionsByRegionId } from 'src/utilities/regions'; -import type { - ObjectStorageKey, - ObjectStorageKeyRegions, -} from '@linode/api-v4/lib/object-storage'; +import type { ObjectStorageKey, ObjectStorageKeyRegions } from '@linode/api-v4'; -type Props = { +interface Props { setHostNames: (hostNames: ObjectStorageKeyRegions[]) => void; setShowHostNamesDrawers: (show: boolean) => void; storageKeyData: ObjectStorageKey; -}; +} + +export const HostNameTableCell = (props: Props) => { + const { setHostNames, setShowHostNamesDrawers, storageKeyData } = props; -export const HostNameTableCell = ({ - setHostNames, - setShowHostNamesDrawers, - storageKeyData, -}: Props) => { const { data: regionsData } = useRegionsQuery(); const regionsLookup = regionsData && getRegionsByRegionId(regionsData); diff --git a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/HostNamesDrawer.tsx b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/HostNamesDrawer.tsx index f81a8658735..c7d2147f71b 100644 --- a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/HostNamesDrawer.tsx +++ b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/HostNamesDrawer.tsx @@ -56,13 +56,17 @@ export const HostNamesDrawer = (props: Props) => { return ( ); })} diff --git a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/ViewPermissionsDrawer.tsx b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/ViewPermissionsDrawer.tsx index 73ebb5ec950..f7053031ad7 100644 --- a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/ViewPermissionsDrawer.tsx +++ b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/ViewPermissionsDrawer.tsx @@ -1,4 +1,3 @@ -import { ObjectStorageKey } from '@linode/api-v4/lib/object-storage'; import * as React from 'react'; import { Drawer } from 'src/components/Drawer'; @@ -10,15 +9,15 @@ import { isFeatureEnabledV2 } from 'src/utilities/accountCapabilities'; import { AccessTable } from './AccessTable'; import { BucketPermissionsTable } from './BucketPermissionsTable'; +import type { ObjectStorageKey } from '@linode/api-v4'; + export interface Props { objectStorageKey: ObjectStorageKey | null; onClose: () => void; open: boolean; } -type CombinedProps = Props; - -export const ViewPermissionsDrawer: React.FC = (props) => { +export const ViewPermissionsDrawer = (props: Props) => { const { objectStorageKey, onClose, open } = props; const flags = useFlags(); @@ -30,18 +29,14 @@ export const ViewPermissionsDrawer: React.FC = (props) => { account?.capabilities ?? [] ); - if (objectStorageKey === null) { - return null; - } - return ( - {objectStorageKey.bucket_access === null ? ( + {!objectStorageKey ? null : objectStorageKey.bucket_access === null ? ( This key has unlimited access to all buckets on your account. @@ -70,5 +65,3 @@ export const ViewPermissionsDrawer: React.FC = (props) => { ); }; - -export default React.memo(ViewPermissionsDrawer); diff --git a/packages/manager/src/features/ObjectStorage/BucketLanding/BucketTableRow.styles.ts b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketTableRow.styles.ts index 22dfa151e8e..38006ef18dc 100644 --- a/packages/manager/src/features/ObjectStorage/BucketLanding/BucketTableRow.styles.ts +++ b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketTableRow.styles.ts @@ -1,31 +1,6 @@ import { styled } from '@mui/material/styles'; -import { Link } from 'react-router-dom'; import { TableCell } from 'src/components/TableCell'; -import { TableRow } from 'src/components/TableRow'; - -export const StyledBucketRow = styled(TableRow, { - label: 'StyledBucketRow', -})(({ theme }) => ({ - backgroundColor: theme.bg.white, -})); - -export const StyledBucketNameWrapper = styled('div', { - label: 'StyledBucketNameWrapper', -})(() => ({ - alignItems: 'center', - display: 'flex', - flexFlow: 'row nowrap', - wordBreak: 'break-all', -})); - -export const StyledBucketLabelLink = styled(Link, { - label: 'StyledBucketLabelLink', -})(() => ({ - '&:hover, &:focus': { - textDecoration: 'underline', - }, -})); export const StyledBucketRegionCell = styled(TableCell, { label: 'StyledBucketRegionCell', diff --git a/packages/manager/src/features/ObjectStorage/BucketLanding/BucketTableRow.tsx b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketTableRow.tsx index 2aee34d0013..a84a923baba 100644 --- a/packages/manager/src/features/ObjectStorage/BucketLanding/BucketTableRow.tsx +++ b/packages/manager/src/features/ObjectStorage/BucketLanding/BucketTableRow.tsx @@ -1,10 +1,12 @@ -import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { DateTimeDisplay } from 'src/components/DateTimeDisplay'; import { Hidden } from 'src/components/Hidden'; +import { Link } from 'src/components/Link'; import { MaskableText } from 'src/components/MaskableText/MaskableText'; +import { Stack } from 'src/components/Stack'; import { TableCell } from 'src/components/TableCell'; +import { TableRow } from 'src/components/TableRow'; import { Typography } from 'src/components/Typography'; import { useAccountManagement } from 'src/hooks/useAccountManagement'; import { useFlags } from 'src/hooks/useFlags'; @@ -16,11 +18,8 @@ import { readableBytes } from 'src/utilities/unitConversions'; import { BucketActionMenu } from './BucketActionMenu'; import { - StyledBucketLabelLink, - StyledBucketNameWrapper, StyledBucketObjectsCell, StyledBucketRegionCell, - StyledBucketRow, StyledBucketSizeCell, } from './BucketTableRow.styles'; @@ -69,26 +68,19 @@ export const BucketTableRow = (props: BucketTableRowProps) => { const typeLabel = isLegacy ? 'Legacy' : 'Standard'; return ( - + - - - - - - {label}{' '} - - - - - {hostname} - - + + + {label} + + {hostname} + @@ -137,6 +129,6 @@ export const BucketTableRow = (props: BucketTableRowProps) => { onRemove={onRemove} /> - + ); }; From ae203251e8591a6ae3215dcc173e1e115e0a125f Mon Sep 17 00:00:00 2001 From: Dajahi Wiley <114682940+dwiley-akamai@users.noreply.github.com> Date: Mon, 4 Nov 2024 10:37:01 -0500 Subject: [PATCH 40/66] =?UTF-8?q?refactor:=20[M3-8648]=20=E2=80=93=20Migra?= =?UTF-8?q?te=20`Paper`=20to=20`ui`=20package=20(#11183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../02-component-structure.md | 4 +- packages/manager/.storybook/utils.test.ts | 2 +- .../components/AccessPanel/AccessPanel.tsx | 2 +- .../AkamaiBanner/AkamaiBanner.styles.ts | 3 +- .../src/components/AreaChart/AreaChart.tsx | 3 +- .../Autocomplete/Autocomplete.styles.tsx | 3 +- .../src/components/BarPercent/BarPercent.tsx | 2 +- .../manager/src/components/Button/Button.tsx | 3 +- .../src/components/Button/StyledTagButton.ts | 2 +- .../CheckoutSummary/CheckoutSummary.tsx | 2 +- .../CircleProgress/CircleProgress.tsx | 4 +- .../components/CopyTooltip/CopyTooltip.tsx | 3 +- .../DescriptionList/DescriptionList.styles.ts | 2 +- .../manager/src/components/Dialog/Dialog.tsx | 3 +- packages/manager/src/components/Divider.tsx | 5 +- .../components/EntityDetail/EntityDetail.tsx | 3 +- .../GaugePercent/GaugePercent.styles.ts | 5 +- .../components/LineGraph/LineGraph.styles.ts | 2 +- .../LineGraph/MetricDisplay.styles.ts | 2 +- packages/manager/src/components/Link.tsx | 2 +- .../PaymentMethodRow/PaymentMethodRow.tsx | 3 +- .../PrimaryNav/PrimaryNav.styles.ts | 3 +- .../PromotionalOfferCard.tsx | 9 ++-- .../RemovableSelectionsList.style.ts | 3 +- .../SelectFirewallPanel.tsx | 3 +- .../manager/src/components/Stack.stories.tsx | 2 +- .../src/components/StatusIcon/StatusIcon.tsx | 4 +- .../components/TabbedPanel/TabbedPanel.tsx | 3 +- .../src/components/Table/Table.styles.ts | 3 +- .../components/TableRow/TableRow.styles.ts | 3 +- .../src/components/Tabs/Tabs.stories.tsx | 2 +- .../manager/src/components/Tag/Tag.styles.ts | 2 +- .../src/components/TagCell/TagCell.tsx | 3 +- .../manager/src/components/TooltipIcon.tsx | 5 +- .../ImageUploader/ImageUploader.styles.ts | 2 +- .../VerticalLinearStepper.styles.ts | 3 +- .../Account/Maintenance/MaintenanceTable.tsx | 2 +- .../src/features/Backups/AutoEnroll.tsx | 2 +- .../src/features/Backups/BackupsCTA.styles.ts | 3 +- .../src/features/Betas/BetaDetailsList.tsx | 2 +- .../manager/src/features/Betas/BetaSignup.tsx | 2 +- .../Billing/InvoiceDetail/InvoiceDetail.tsx | 2 +- .../AlertsLanding/AlertsDefinitionLanding.tsx | 2 +- .../Alerts/AlertsLanding/AlertsLanding.tsx | 3 +- .../CloudPulseDashboardWithFilters.tsx | 2 +- .../CloudPulse/Widget/CloudPulseWidget.tsx | 4 +- .../DatabaseCreate/DatabaseCreate.tsx | 4 +- .../DatabaseBackups/DatabaseBackups.tsx | 2 +- .../legacy/DatabaseBackupsLegacy.tsx | 2 +- .../DatabaseResize/DatabaseResize.tsx | 3 +- .../DatabaseSettings/DatabaseSettings.tsx | 2 +- .../DatabaseSummary/DatabaseSummary.tsx | 2 +- .../Domains/CreateDomain/CreateDomain.tsx | 3 +- .../Domains/DomainDetail/DomainDetail.tsx | 2 +- .../Rules/FirewallRuleTable.styles.ts | 3 +- .../src/features/Help/Panels/PopularPosts.tsx | 5 +- .../src/features/Help/Panels/SearchPanel.tsx | 2 +- .../DocumentationResults.tsx | 5 +- .../Images/ImagesCreate/CreateImageTab.tsx | 2 +- .../Images/ImagesCreate/ImageUpload.tsx | 2 +- .../ImageRegions/ManageImageRegionsForm.tsx | 2 +- .../Images/ImagesLanding/ImagesLanding.tsx | 3 +- .../CreateCluster/CreateCluster.tsx | 3 +- .../APLSummaryPanel.tsx | 2 +- .../KubeConfigPanel.tsx | 7 +-- .../KubeControlPaneACLDrawer.tsx | 3 +- .../NodePoolsDisplay/NodePool.tsx | 3 +- .../Linodes/CloneLanding/CloneLanding.tsx | 2 +- .../features/Linodes/CloneLanding/Details.tsx | 2 +- .../Linodes/LinodeCreate/Addons/Addons.tsx | 2 +- .../Linodes/LinodeCreate/Details/Details.tsx | 2 +- .../Linodes/LinodeCreate/EUAgreement.tsx | 2 +- .../features/Linodes/LinodeCreate/Error.tsx | 2 +- .../Linodes/LinodeCreate/Firewall.tsx | 2 +- .../features/Linodes/LinodeCreate/Region.tsx | 2 +- .../Linodes/LinodeCreate/Security.tsx | 2 +- .../Linodes/LinodeCreate/Summary/Summary.tsx | 2 +- .../Tabs/Backups/BackupSelect.tsx | 2 +- .../Tabs/Backups/LinodeSelect.tsx | 2 +- .../Linodes/LinodeCreate/Tabs/Clone/Clone.tsx | 2 +- .../Linodes/LinodeCreate/Tabs/Images.tsx | 2 +- .../Tabs/Marketplace/AppSelect.tsx | 2 +- .../LinodeCreate/Tabs/OperatingSystems.tsx | 2 +- .../Tabs/StackScripts/StackScriptImages.tsx | 2 +- .../StackScripts/StackScriptSelection.tsx | 2 +- .../UserDefinedFields/UserDefinedFields.tsx | 2 +- .../Linodes/LinodeCreate/TwoStepRegion.tsx | 2 +- .../features/Linodes/LinodeCreate/VPC/VPC.tsx | 2 +- .../Linodes/LinodeCreate/utilities.ts | 2 +- .../LinodeBackup/CaptureSnapshot.tsx | 2 +- .../LinodeBackup/LinodeBackups.tsx | 2 +- .../LinodeBackup/ScheduleSettings.tsx | 2 +- .../LinodeFirewalls/LinodeFirewalls.tsx | 2 +- .../LinodeIPAddresses.styles.ts | 2 +- .../LinodeNetworking/LinodeIPAddresses.tsx | 2 +- .../NetworkingSummaryPanel.tsx | 2 +- .../LinodeRebuild/ImageEmptyState.tsx | 2 +- .../LinodeRescue/StandardRescueDialog.tsx | 2 +- .../LinodesDetail/LinodeSettings/VPCPanel.tsx | 3 +- .../LinodeStorage/LinodeDisks.tsx | 2 +- .../LinodeStorage/LinodeVolumes.tsx | 2 +- .../LinodeSummary/LinodeSummary.tsx | 2 +- .../LinodesDetail/VolumesUpgradeBanner.tsx | 2 +- .../LinodesLanding/DisplayGroupedLinodes.tsx | 2 +- .../LinodesLanding/DisplayLinodes.styles.ts | 3 +- .../Linodes/LinodesLanding/DisplayLinodes.tsx | 2 +- .../LinodesLanding/IPAddress.styles.ts | 2 +- .../LinodeRow/LinodeRow.styles.ts | 6 ++- .../MigrateLinode/ConfigureForm.styles.ts | 4 +- .../DetailTabs/CommonStyles.styles.tsx | 2 +- .../DetailTabs/Installation.tsx | 2 +- .../DetailTabs/LongviewDetailOverview.tsx | 2 +- .../OverviewGraphs/OverviewGraphs.tsx | 2 +- .../DetailTabs/Processes/ProcessesGraphs.tsx | 3 +- .../Longview/LongviewDetail/GraphCard.tsx | 2 +- .../LongviewDetail/LongviewDetail.tsx | 2 +- .../LongviewClientInstructions.tsx | 2 +- .../LongviewLanding/LongviewClientRow.tsx | 2 +- .../Longview/LongviewLanding/LongviewList.tsx | 2 +- .../LongviewLanding/LongviewPlans.styles.ts | 2 +- .../LongviewLanding/LongviewPlans.tsx | 2 +- .../Managed/SSHAccess/LinodePubKey.styles.tsx | 2 +- .../NodeBalancers/NodeBalancerCreate.tsx | 3 +- .../NodeBalancerSummary/SummaryPanel.tsx | 2 +- .../NodeBalancerSummary/TablesPanel.tsx | 2 +- .../NotificationCenter.styles.ts | 3 +- .../AccessKeyLanding/HostNamesList.tsx | 3 +- .../BucketDetail/BucketAccess.tsx | 2 +- .../BucketDetail/BucketProperties.styles.ts | 2 +- .../ObjectStorage/BucketDetail/BucketSSL.tsx | 2 +- .../PlacementGroupsSummary.tsx | 2 +- .../Profile/APITokens/APITokenTable.tsx | 3 +- .../AuthenticationSettings.tsx | 2 +- .../PhoneVerification.styles.ts | 3 +- .../TPAProviders.styles.ts | 4 +- .../DisplaySettings/DisplaySettings.tsx | 2 +- .../Profile/LishSettings/LishSettings.tsx | 3 +- .../features/Profile/Referrals/Referrals.tsx | 2 +- .../features/Profile/Settings/Settings.tsx | 2 +- .../Partials/StackScriptTableHead.styles.ts | 5 +- .../SelectStackScriptPanel.styles.ts | 46 +++++++++++-------- .../StackScriptBase/StackScriptBase.styles.ts | 2 +- .../StackScriptForm/StackScriptForm.tsx | 3 +- .../FieldTypes/UserDefinedText.tsx | 25 +++++----- .../UserDefinedFieldsPanel.styles.ts | 5 +- .../TabbedReply/MarkdownReference.tsx | 2 +- .../TabbedReply/PreviewReply.tsx | 2 +- .../SupportTicketDetail/TicketStatus.tsx | 5 +- .../features/Support/TicketAttachmentRow.tsx | 3 +- .../SearchBar/SearchSuggestion.styles.ts | 5 +- .../features/Users/UserPermissions.styles.ts | 2 +- .../Users/UserProfile/DeleteUserPanel.tsx | 3 +- .../Users/UserProfile/UserDetailsPanel.tsx | 2 +- .../Users/UserProfile/UserEmailPanel.tsx | 2 +- .../Users/UserProfile/UsernamePanel.tsx | 2 +- .../FormComponents/VPCCreateForm.styles.ts | 2 +- .../src/features/VPCs/VPCCreate/VPCCreate.tsx | 8 ++-- .../src/features/Volumes/VolumeCreate.tsx | 3 +- .../PlansPanel/DistributedRegionPlanTable.tsx | 3 +- .../PlansPanel/PlanContainer.styles.ts | 6 ++- .../pr-11183-added-1730215173323.md | 5 ++ .../src/components/Paper}/Paper.stories.tsx | 0 .../src/components/Paper}/Paper.tsx | 6 +-- packages/ui/src/components/Paper/index.ts | 1 + packages/ui/src/components/index.ts | 1 + packages/ui/src/index.ts | 1 + packages/ui/src/utilities/index.ts | 1 + .../src/utilities/omittedProps.test.tsx | 5 +- .../src/utilities/omittedProps.ts | 5 +- 169 files changed, 247 insertions(+), 271 deletions(-) create mode 100644 packages/ui/.changeset/pr-11183-added-1730215173323.md rename packages/{manager/src/components => ui/src/components/Paper}/Paper.stories.tsx (100%) rename packages/{manager/src/components => ui/src/components/Paper}/Paper.tsx (89%) create mode 100644 packages/ui/src/components/Paper/index.ts create mode 100644 packages/ui/src/utilities/index.ts rename packages/{manager => ui}/src/utilities/omittedProps.test.tsx (90%) rename packages/{manager => ui}/src/utilities/omittedProps.ts (92%) diff --git a/docs/development-guide/02-component-structure.md b/docs/development-guide/02-component-structure.md index 3cd72cdf54e..1a18bbb39de 100644 --- a/docs/development-guide/02-component-structure.md +++ b/docs/development-guide/02-component-structure.md @@ -15,9 +15,9 @@ The basic structure of a component file should follow: Here is a minimal code example demonstrating the basic structure of a component file: ```tsx -import * as React from "react"; +import { omittedProps } from "@linode/ui"; import { styled } from "@mui/material/styles"; -import { omittedProps } from "src/utilities/omittedProps"; +import * as React from "react"; // If not exported, it can just be named `Props` export interface SayHelloProps { diff --git a/packages/manager/.storybook/utils.test.ts b/packages/manager/.storybook/utils.test.ts index 9dc8db75c2b..405fb57b34c 100644 --- a/packages/manager/.storybook/utils.test.ts +++ b/packages/manager/.storybook/utils.test.ts @@ -10,7 +10,7 @@ describe('getReactDocgenTSFileGlobs', () => { ).toBe(true); expect( typeScriptFileGlobs.some((file) => - file.includes('../manager/src/components/Paper.{ts,tsx}') + file.includes('../ui/src/components/Paper/**/*.{ts,tsx}') ) ).toBe(true); expect( diff --git a/packages/manager/src/components/AccessPanel/AccessPanel.tsx b/packages/manager/src/components/AccessPanel/AccessPanel.tsx index 78b0905f193..7c26208c4e9 100644 --- a/packages/manager/src/components/AccessPanel/AccessPanel.tsx +++ b/packages/manager/src/components/AccessPanel/AccessPanel.tsx @@ -1,3 +1,4 @@ +import { Paper } from '@linode/ui'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; @@ -14,7 +15,6 @@ import { } from 'src/components/Encryption/constants'; import { Encryption } from 'src/components/Encryption/Encryption'; import { useIsDiskEncryptionFeatureEnabled } from 'src/components/Encryption/utils'; -import { Paper } from 'src/components/Paper'; import { getIsDistributedRegion } from 'src/components/RegionSelect/RegionSelect.utils'; import { SuspenseLoader } from 'src/components/SuspenseLoader'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/components/AkamaiBanner/AkamaiBanner.styles.ts b/packages/manager/src/components/AkamaiBanner/AkamaiBanner.styles.ts index 1788f53a67e..04c3ad2a2a0 100644 --- a/packages/manager/src/components/AkamaiBanner/AkamaiBanner.styles.ts +++ b/packages/manager/src/components/AkamaiBanner/AkamaiBanner.styles.ts @@ -1,9 +1,8 @@ -import { Box } from '@linode/ui'; +import { Box, omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import Warning from 'src/assets/icons/warning.svg'; import AkamaiLogo from 'src/assets/logo/akamai-logo.svg'; -import { omittedProps } from 'src/utilities/omittedProps'; import { Stack } from '../Stack'; diff --git a/packages/manager/src/components/AreaChart/AreaChart.tsx b/packages/manager/src/components/AreaChart/AreaChart.tsx index ea6dd7e08b5..70dcabf72b4 100644 --- a/packages/manager/src/components/AreaChart/AreaChart.tsx +++ b/packages/manager/src/components/AreaChart/AreaChart.tsx @@ -1,4 +1,4 @@ -import { Box } from '@linode/ui'; +import { Box, Paper } from '@linode/ui'; import { Typography, useTheme } from '@mui/material'; import { styled } from '@mui/material/styles'; import { DateTime } from 'luxon'; @@ -16,7 +16,6 @@ import { import { AccessibleAreaChart } from 'src/components/AreaChart/AccessibleAreaChart'; import MetricsDisplay from 'src/components/LineGraph/MetricsDisplay'; -import { Paper } from 'src/components/Paper'; import { StyledBottomLegend } from 'src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/TablesPanel'; import { diff --git a/packages/manager/src/components/Autocomplete/Autocomplete.styles.tsx b/packages/manager/src/components/Autocomplete/Autocomplete.styles.tsx index 5e4c9989e51..0d3c4ad5d34 100644 --- a/packages/manager/src/components/Autocomplete/Autocomplete.styles.tsx +++ b/packages/manager/src/components/Autocomplete/Autocomplete.styles.tsx @@ -1,10 +1,9 @@ +import { omittedProps } from '@linode/ui'; import DoneIcon from '@mui/icons-material/Done'; import Popper from '@mui/material/Popper'; import { styled } from '@mui/material/styles'; import React from 'react'; -import { omittedProps } from 'src/utilities/omittedProps'; - import type { PopperProps } from '@mui/material/Popper'; export const StyledListItem = styled('li', { diff --git a/packages/manager/src/components/BarPercent/BarPercent.tsx b/packages/manager/src/components/BarPercent/BarPercent.tsx index 9aeb1e92d86..81a9d6e4ccf 100644 --- a/packages/manager/src/components/BarPercent/BarPercent.tsx +++ b/packages/manager/src/components/BarPercent/BarPercent.tsx @@ -1,8 +1,8 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import * as React from 'react'; import { LinearProgress } from 'src/components/LinearProgress'; -import { omittedProps } from 'src/utilities/omittedProps'; import type { SxProps, Theme } from '@mui/material/styles'; diff --git a/packages/manager/src/components/Button/Button.tsx b/packages/manager/src/components/Button/Button.tsx index 32065827df9..e13a9e1fd86 100644 --- a/packages/manager/src/components/Button/Button.tsx +++ b/packages/manager/src/components/Button/Button.tsx @@ -1,4 +1,4 @@ -import { Tooltip } from '@linode/ui'; +import { Tooltip, omittedProps } from '@linode/ui'; import HelpOutline from '@mui/icons-material/HelpOutline'; import _Button from '@mui/material/Button'; import { styled } from '@mui/material/styles'; @@ -7,7 +7,6 @@ import * as React from 'react'; import Reload from 'src/assets/icons/reload.svg'; import { rotate360 } from '../../styles/keyframes'; -import { omittedProps } from '../../utilities/omittedProps'; import type { ButtonProps as _ButtonProps } from '@mui/material/Button'; import type { SxProps, Theme } from '@mui/material/styles'; diff --git a/packages/manager/src/components/Button/StyledTagButton.ts b/packages/manager/src/components/Button/StyledTagButton.ts index d0dae58b7cd..18355b0ef08 100644 --- a/packages/manager/src/components/Button/StyledTagButton.ts +++ b/packages/manager/src/components/Button/StyledTagButton.ts @@ -1,7 +1,7 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import Plus from 'src/assets/icons/plusSign.svg'; -import { omittedProps } from 'src/utilities/omittedProps'; import { Button } from './Button'; diff --git a/packages/manager/src/components/CheckoutSummary/CheckoutSummary.tsx b/packages/manager/src/components/CheckoutSummary/CheckoutSummary.tsx index a1d7873b0b5..4b26381b8af 100644 --- a/packages/manager/src/components/CheckoutSummary/CheckoutSummary.tsx +++ b/packages/manager/src/components/CheckoutSummary/CheckoutSummary.tsx @@ -1,10 +1,10 @@ +import { Paper } from '@linode/ui'; import { useTheme } from '@mui/material'; import { styled } from '@mui/material/styles'; import Grid2 from '@mui/material/Unstable_Grid2/Grid2'; import useMediaQuery from '@mui/material/useMediaQuery'; import * as React from 'react'; -import { Paper } from '../Paper'; import { Typography } from '../Typography'; import { SummaryItem } from './SummaryItem'; diff --git a/packages/manager/src/components/CircleProgress/CircleProgress.tsx b/packages/manager/src/components/CircleProgress/CircleProgress.tsx index 1d177ce85e0..04270ba551f 100644 --- a/packages/manager/src/components/CircleProgress/CircleProgress.tsx +++ b/packages/manager/src/components/CircleProgress/CircleProgress.tsx @@ -1,10 +1,8 @@ -import { Box } from '@linode/ui'; +import { Box, omittedProps } from '@linode/ui'; import _CircularProgress from '@mui/material/CircularProgress'; import { styled } from '@mui/material/styles'; import * as React from 'react'; -import { omittedProps } from 'src/utilities/omittedProps'; - import type { CircularProgressProps } from '@mui/material/CircularProgress'; import type { SxProps, Theme } from '@mui/material/styles'; diff --git a/packages/manager/src/components/CopyTooltip/CopyTooltip.tsx b/packages/manager/src/components/CopyTooltip/CopyTooltip.tsx index d4cdca2642b..04a31c65424 100644 --- a/packages/manager/src/components/CopyTooltip/CopyTooltip.tsx +++ b/packages/manager/src/components/CopyTooltip/CopyTooltip.tsx @@ -1,11 +1,10 @@ -import { Tooltip, VisibilityTooltip } from '@linode/ui'; +import { Tooltip, VisibilityTooltip, omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import copy from 'copy-to-clipboard'; import * as React from 'react'; import FileCopy from 'src/assets/icons/copy.svg'; import { createMaskedText } from 'src/utilities/createMaskedText'; -import { omittedProps } from 'src/utilities/omittedProps'; import type { MaskableTextLength } from '../MaskableText/MaskableText'; import type { TooltipProps } from '@linode/ui'; diff --git a/packages/manager/src/components/DescriptionList/DescriptionList.styles.ts b/packages/manager/src/components/DescriptionList/DescriptionList.styles.ts index 6b450e9b98d..e65973514ba 100644 --- a/packages/manager/src/components/DescriptionList/DescriptionList.styles.ts +++ b/packages/manager/src/components/DescriptionList/DescriptionList.styles.ts @@ -1,8 +1,8 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import { Typography } from 'src/components/Typography'; -import { omittedProps } from 'src/utilities/omittedProps'; import type { DescriptionListProps } from './DescriptionList'; import type { TypographyProps } from '@mui/material'; diff --git a/packages/manager/src/components/Dialog/Dialog.tsx b/packages/manager/src/components/Dialog/Dialog.tsx index 8a217c008ea..6002e942dfc 100644 --- a/packages/manager/src/components/Dialog/Dialog.tsx +++ b/packages/manager/src/components/Dialog/Dialog.tsx @@ -1,4 +1,4 @@ -import { Box } from '@linode/ui'; +import { Box, omittedProps } from '@linode/ui'; import _Dialog from '@mui/material/Dialog'; import DialogContent from '@mui/material/DialogContent'; import { styled, useTheme } from '@mui/material/styles'; @@ -6,7 +6,6 @@ import * as React from 'react'; import { DialogTitle } from 'src/components/DialogTitle/DialogTitle'; import { Notice } from 'src/components/Notice/Notice'; -import { omittedProps } from 'src/utilities/omittedProps'; import { convertForAria } from 'src/utilities/stringUtils'; import type { DialogProps as _DialogProps } from '@mui/material/Dialog'; diff --git a/packages/manager/src/components/Divider.tsx b/packages/manager/src/components/Divider.tsx index cfd18a7fe5a..6186797e1e3 100644 --- a/packages/manager/src/components/Divider.tsx +++ b/packages/manager/src/components/Divider.tsx @@ -1,8 +1,9 @@ -import _Divider, { DividerProps as _DividerProps } from '@mui/material/Divider'; +import { omittedProps } from '@linode/ui'; +import _Divider from '@mui/material/Divider'; import { styled } from '@mui/material/styles'; import * as React from 'react'; -import { omittedProps } from 'src/utilities/omittedProps'; +import type { DividerProps as _DividerProps } from '@mui/material/Divider'; export interface DividerProps extends _DividerProps { dark?: boolean; diff --git a/packages/manager/src/components/EntityDetail/EntityDetail.tsx b/packages/manager/src/components/EntityDetail/EntityDetail.tsx index f2be5a8c455..d019a34c5ff 100644 --- a/packages/manager/src/components/EntityDetail/EntityDetail.tsx +++ b/packages/manager/src/components/EntityDetail/EntityDetail.tsx @@ -1,9 +1,8 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; -import { omittedProps } from '../../utilities/omittedProps'; - export interface EntityDetailProps { body?: JSX.Element; footer?: JSX.Element; diff --git a/packages/manager/src/components/GaugePercent/GaugePercent.styles.ts b/packages/manager/src/components/GaugePercent/GaugePercent.styles.ts index 2ada9518254..5e844e917b8 100644 --- a/packages/manager/src/components/GaugePercent/GaugePercent.styles.ts +++ b/packages/manager/src/components/GaugePercent/GaugePercent.styles.ts @@ -1,8 +1,7 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; -import { omittedProps } from 'src/utilities/omittedProps'; - -import { GaugePercentProps } from './GaugePercent'; +import type { GaugePercentProps } from './GaugePercent'; type StyledGaugePercentProps = Pick & Required>; diff --git a/packages/manager/src/components/LineGraph/LineGraph.styles.ts b/packages/manager/src/components/LineGraph/LineGraph.styles.ts index c3a8f50b590..75af5f913a9 100644 --- a/packages/manager/src/components/LineGraph/LineGraph.styles.ts +++ b/packages/manager/src/components/LineGraph/LineGraph.styles.ts @@ -1,3 +1,4 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { Button } from 'src/components/Button/Button'; @@ -5,7 +6,6 @@ import { Table } from 'src/components/Table'; import { TableBody } from 'src/components/TableBody'; import { TableCell } from 'src/components/TableCell'; import { TableHead } from 'src/components/TableHead'; -import { omittedProps } from 'src/utilities/omittedProps'; export const StyledWrapper = styled('div')(() => ({ display: 'flex', diff --git a/packages/manager/src/components/LineGraph/MetricDisplay.styles.ts b/packages/manager/src/components/LineGraph/MetricDisplay.styles.ts index 9870bd9bc2e..b0f5ff173ee 100644 --- a/packages/manager/src/components/LineGraph/MetricDisplay.styles.ts +++ b/packages/manager/src/components/LineGraph/MetricDisplay.styles.ts @@ -1,9 +1,9 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { Button } from 'src/components/Button/Button'; import { Table } from 'src/components/Table'; import { TableCell } from 'src/components/TableCell'; -import { omittedProps } from 'src/utilities/omittedProps'; export const StyledTable = styled(Table, { label: 'StyledTable', diff --git a/packages/manager/src/components/Link.tsx b/packages/manager/src/components/Link.tsx index 4bffa1946e4..8242c791db1 100644 --- a/packages/manager/src/components/Link.tsx +++ b/packages/manager/src/components/Link.tsx @@ -1,4 +1,5 @@ import { sanitizeUrl } from '@braintree/sanitize-url'; +import { omitProps } from '@linode/ui'; import * as React from 'react'; import { Link as RouterLink } from 'react-router-dom'; @@ -9,7 +10,6 @@ import { flattenChildrenIntoAriaLabel, opensInNewTab, } from 'src/utilities/link'; -import { omitProps } from 'src/utilities/omittedProps'; import type { LinkProps as TanStackLinkProps } from '@tanstack/react-router'; import type { LinkProps as _LinkProps } from 'react-router-dom'; diff --git a/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.tsx b/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.tsx index 334e530d530..3222eb34b5f 100644 --- a/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.tsx +++ b/packages/manager/src/components/PaymentMethodRow/PaymentMethodRow.tsx @@ -1,4 +1,4 @@ -import { Box } from '@linode/ui'; +import { Box, Paper } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import { useSnackbar } from 'notistack'; import * as React from 'react'; @@ -6,7 +6,6 @@ import { useHistory } from 'react-router-dom'; import { ActionMenu } from 'src/components/ActionMenu/ActionMenu'; import { Chip } from 'src/components/Chip'; -import { Paper } from 'src/components/Paper'; import CreditCard from 'src/features/Billing/BillingPanels/BillingSummary/PaymentDrawer/CreditCard'; import { useMakeDefaultPaymentMethodMutation } from 'src/queries/account/payment'; diff --git a/packages/manager/src/components/PrimaryNav/PrimaryNav.styles.ts b/packages/manager/src/components/PrimaryNav/PrimaryNav.styles.ts index 5d7c0c33ece..69b998cfd4a 100644 --- a/packages/manager/src/components/PrimaryNav/PrimaryNav.styles.ts +++ b/packages/manager/src/components/PrimaryNav/PrimaryNav.styles.ts @@ -1,4 +1,4 @@ -import { Box } from '@linode/ui'; +import { Box, omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import { Link } from 'react-router-dom'; @@ -7,7 +7,6 @@ import AkamaiLogo from 'src/assets/logo/akamai-logo.svg'; import { Accordion } from 'src/components/Accordion'; import { Divider } from 'src/components/Divider'; import { SIDEBAR_WIDTH } from 'src/components/PrimaryNav/SideMenu'; -import { omittedProps } from 'src/utilities/omittedProps'; export const StyledGrid = styled(Grid, { label: 'StyledGrid', diff --git a/packages/manager/src/components/PromotionalOfferCard/PromotionalOfferCard.tsx b/packages/manager/src/components/PromotionalOfferCard/PromotionalOfferCard.tsx index baacd9c737c..47e9dab10a5 100644 --- a/packages/manager/src/components/PromotionalOfferCard/PromotionalOfferCard.tsx +++ b/packages/manager/src/components/PromotionalOfferCard/PromotionalOfferCard.tsx @@ -1,15 +1,16 @@ +import { Paper } from '@linode/ui'; import Button from '@mui/material/Button'; -import { Theme } from '@mui/material/styles'; import * as React from 'react'; import { Link } from 'react-router-dom'; import { makeStyles } from 'tss-react/mui'; import HeavenlyBucketIcon from 'src/assets/icons/promotionalOffers/heavenly-bucket.svg'; -import { Paper } from 'src/components/Paper'; import { Typography } from 'src/components/Typography'; -import { PromotionalOffer } from 'src/featureFlags'; -import { useWindowDimensions } from 'src/hooks/useWindowDimensions'; import { OFFSITE_URL_REGEX, ONSITE_URL_REGEX } from 'src/constants'; +import { useWindowDimensions } from 'src/hooks/useWindowDimensions'; + +import type { Theme } from '@mui/material/styles'; +import type { PromotionalOffer } from 'src/featureFlags'; const useStyles = makeStyles()((theme: Theme) => ({ alignLeft: { diff --git a/packages/manager/src/components/RemovableSelectionsList/RemovableSelectionsList.style.ts b/packages/manager/src/components/RemovableSelectionsList/RemovableSelectionsList.style.ts index bb32350a632..5f715da92d9 100644 --- a/packages/manager/src/components/RemovableSelectionsList/RemovableSelectionsList.style.ts +++ b/packages/manager/src/components/RemovableSelectionsList/RemovableSelectionsList.style.ts @@ -1,9 +1,8 @@ -import { Box } from '@linode/ui'; +import { Box, omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { List } from 'src/components/List'; import { ListItem } from 'src/components/ListItem'; -import { omittedProps } from 'src/utilities/omittedProps'; import { Typography } from '../Typography'; diff --git a/packages/manager/src/components/SelectFirewallPanel/SelectFirewallPanel.tsx b/packages/manager/src/components/SelectFirewallPanel/SelectFirewallPanel.tsx index d001a882a4a..0c8e7a6ac1f 100644 --- a/packages/manager/src/components/SelectFirewallPanel/SelectFirewallPanel.tsx +++ b/packages/manager/src/components/SelectFirewallPanel/SelectFirewallPanel.tsx @@ -1,8 +1,7 @@ -import { Box } from '@linode/ui'; +import { Box, Paper } from '@linode/ui'; import { styled } from '@mui/material/styles'; import * as React from 'react'; -import { Paper } from 'src/components/Paper'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; import { CreateFirewallDrawer } from 'src/features/Firewalls/FirewallLanding/CreateFirewallDrawer'; diff --git a/packages/manager/src/components/Stack.stories.tsx b/packages/manager/src/components/Stack.stories.tsx index 0a08390b6b0..07d01cd91bc 100644 --- a/packages/manager/src/components/Stack.stories.tsx +++ b/packages/manager/src/components/Stack.stories.tsx @@ -1,7 +1,7 @@ +import { Paper } from '@linode/ui'; import React from 'react'; import { Divider } from './Divider'; -import { Paper } from './Paper'; import { Stack } from './Stack'; import type { Meta, StoryObj } from '@storybook/react'; diff --git a/packages/manager/src/components/StatusIcon/StatusIcon.tsx b/packages/manager/src/components/StatusIcon/StatusIcon.tsx index 26a10cb1756..4a82cd814fa 100644 --- a/packages/manager/src/components/StatusIcon/StatusIcon.tsx +++ b/packages/manager/src/components/StatusIcon/StatusIcon.tsx @@ -1,9 +1,7 @@ -import { Box } from '@linode/ui'; +import { Box, omittedProps } from '@linode/ui'; import { styled } from '@mui/material'; import * as React from 'react'; -import { omittedProps } from 'src/utilities/omittedProps'; - import type { BoxProps } from '@linode/ui'; export type Status = 'active' | 'error' | 'inactive' | 'other'; diff --git a/packages/manager/src/components/TabbedPanel/TabbedPanel.tsx b/packages/manager/src/components/TabbedPanel/TabbedPanel.tsx index 668aba1514b..8393e3cbde1 100644 --- a/packages/manager/src/components/TabbedPanel/TabbedPanel.tsx +++ b/packages/manager/src/components/TabbedPanel/TabbedPanel.tsx @@ -1,10 +1,9 @@ -import { Box, Tooltip } from '@linode/ui'; +import { Box, Paper, Tooltip } from '@linode/ui'; import HelpOutline from '@mui/icons-material/HelpOutline'; import { styled } from '@mui/material/styles'; import React, { useEffect, useState } from 'react'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; import { Tab } from 'src/components/Tabs/Tab'; import { TabList } from 'src/components/Tabs/TabList'; import { TabPanel } from 'src/components/Tabs/TabPanel'; diff --git a/packages/manager/src/components/Table/Table.styles.ts b/packages/manager/src/components/Table/Table.styles.ts index 87318bf2aaf..d0a07122ea4 100644 --- a/packages/manager/src/components/Table/Table.styles.ts +++ b/packages/manager/src/components/Table/Table.styles.ts @@ -1,7 +1,6 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; -import { omittedProps } from 'src/utilities/omittedProps'; - import type { TableProps } from './Table'; export const StyledTableWrapper = styled('div', { diff --git a/packages/manager/src/components/TableRow/TableRow.styles.ts b/packages/manager/src/components/TableRow/TableRow.styles.ts index 745ff361b3c..92d357e8fc8 100644 --- a/packages/manager/src/components/TableRow/TableRow.styles.ts +++ b/packages/manager/src/components/TableRow/TableRow.styles.ts @@ -1,8 +1,7 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { default as _TableRow } from '@mui/material/TableRow'; -import { omittedProps } from 'src/utilities/omittedProps'; - import type { TableRowProps } from './TableRow'; export const StyledTableRow = styled(_TableRow, { diff --git a/packages/manager/src/components/Tabs/Tabs.stories.tsx b/packages/manager/src/components/Tabs/Tabs.stories.tsx index 7a279cfc407..89d488081d8 100644 --- a/packages/manager/src/components/Tabs/Tabs.stories.tsx +++ b/packages/manager/src/components/Tabs/Tabs.stories.tsx @@ -1,6 +1,6 @@ +import { Paper } from '@linode/ui'; import * as React from 'react'; -import { Paper } from 'src/components/Paper'; import { SafeTabPanel } from 'src/components/Tabs/SafeTabPanel'; import { TabPanels } from 'src/components/Tabs/TabPanels'; import { Tabs } from 'src/components/Tabs/Tabs'; diff --git a/packages/manager/src/components/Tag/Tag.styles.ts b/packages/manager/src/components/Tag/Tag.styles.ts index 4f61d0f25a7..1912ce37c5b 100644 --- a/packages/manager/src/components/Tag/Tag.styles.ts +++ b/packages/manager/src/components/Tag/Tag.styles.ts @@ -1,7 +1,7 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { Chip } from 'src/components/Chip'; -import { omittedProps } from 'src/utilities/omittedProps'; import { StyledLinkButton } from '../Button/StyledLinkButton'; diff --git a/packages/manager/src/components/TagCell/TagCell.tsx b/packages/manager/src/components/TagCell/TagCell.tsx index 3700acad19d..b9f3dfbfe48 100644 --- a/packages/manager/src/components/TagCell/TagCell.tsx +++ b/packages/manager/src/components/TagCell/TagCell.tsx @@ -1,4 +1,4 @@ -import { IconButton } from '@linode/ui'; +import { IconButton, omittedProps } from '@linode/ui'; import MoreHoriz from '@mui/icons-material/MoreHoriz'; import { styled } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; @@ -6,7 +6,6 @@ import * as React from 'react'; import { Tag } from 'src/components/Tag/Tag'; import { useWindowDimensions } from 'src/hooks/useWindowDimensions'; -import { omittedProps } from 'src/utilities/omittedProps'; import { StyledPlusIcon, StyledTagButton } from '../Button/StyledTagButton'; import { CircleProgress } from '../CircleProgress'; diff --git a/packages/manager/src/components/TooltipIcon.tsx b/packages/manager/src/components/TooltipIcon.tsx index b187c60ed89..b5ad2776429 100644 --- a/packages/manager/src/components/TooltipIcon.tsx +++ b/packages/manager/src/components/TooltipIcon.tsx @@ -1,6 +1,5 @@ import styled from '@emotion/styled'; -import { IconButton } from '@linode/ui'; -import { Tooltip, tooltipClasses } from '@linode/ui'; +import { IconButton, Tooltip, omittedProps, tooltipClasses } from '@linode/ui'; import SuccessOutline from '@mui/icons-material/CheckCircleOutlined'; import ErrorOutline from '@mui/icons-material/ErrorOutline'; import HelpOutline from '@mui/icons-material/HelpOutline'; @@ -9,8 +8,6 @@ import WarningSolid from '@mui/icons-material/Warning'; import { useTheme } from '@mui/material/styles'; import * as React from 'react'; -import { omittedProps } from 'src/utilities/omittedProps'; - import type { TooltipProps } from '@linode/ui'; import type { SxProps, Theme } from '@mui/material/styles'; diff --git a/packages/manager/src/components/Uploaders/ImageUploader/ImageUploader.styles.ts b/packages/manager/src/components/Uploaders/ImageUploader/ImageUploader.styles.ts index 7c9341b25b0..da70cc34ee4 100644 --- a/packages/manager/src/components/Uploaders/ImageUploader/ImageUploader.styles.ts +++ b/packages/manager/src/components/Uploaders/ImageUploader/ImageUploader.styles.ts @@ -1,8 +1,8 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { Button } from 'src/components/Button/Button'; import { Typography } from 'src/components/Typography'; -import { omittedProps } from 'src/utilities/omittedProps'; interface DropZoneClassProps { dropzoneDisabled: boolean; diff --git a/packages/manager/src/components/VerticalLinearStepper/VerticalLinearStepper.styles.ts b/packages/manager/src/components/VerticalLinearStepper/VerticalLinearStepper.styles.ts index 4d8bc9d619c..94da88e268b 100644 --- a/packages/manager/src/components/VerticalLinearStepper/VerticalLinearStepper.styles.ts +++ b/packages/manager/src/components/VerticalLinearStepper/VerticalLinearStepper.styles.ts @@ -1,8 +1,7 @@ +import { omittedProps } from '@linode/ui'; import { StepConnector, StepIcon } from '@mui/material'; import { styled } from '@mui/material/styles'; -import { omittedProps } from 'src/utilities/omittedProps'; - type StyledCircleIconProps = { activeStep: number; index: number; diff --git a/packages/manager/src/features/Account/Maintenance/MaintenanceTable.tsx b/packages/manager/src/features/Account/Maintenance/MaintenanceTable.tsx index 3a5d82f130c..b36a3820932 100644 --- a/packages/manager/src/features/Account/Maintenance/MaintenanceTable.tsx +++ b/packages/manager/src/features/Account/Maintenance/MaintenanceTable.tsx @@ -6,7 +6,7 @@ import { makeStyles } from 'tss-react/mui'; import { DownloadCSV } from 'src/components/DownloadCSV/DownloadCSV'; import { Hidden } from 'src/components/Hidden'; import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFooter'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Table } from 'src/components/Table'; import { TableBody } from 'src/components/TableBody'; import { TableCell } from 'src/components/TableCell'; diff --git a/packages/manager/src/features/Backups/AutoEnroll.tsx b/packages/manager/src/features/Backups/AutoEnroll.tsx index f3b9ee4cca1..9a1a6e4764c 100644 --- a/packages/manager/src/features/Backups/AutoEnroll.tsx +++ b/packages/manager/src/features/Backups/AutoEnroll.tsx @@ -1,10 +1,10 @@ +import { Paper } from '@linode/ui'; import { styled } from '@mui/material/styles'; import * as React from 'react'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; import { Toggle } from 'src/components/Toggle/Toggle'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Backups/BackupsCTA.styles.ts b/packages/manager/src/features/Backups/BackupsCTA.styles.ts index 6e3f4102a08..d01ba8ff10c 100644 --- a/packages/manager/src/features/Backups/BackupsCTA.styles.ts +++ b/packages/manager/src/features/Backups/BackupsCTA.styles.ts @@ -1,7 +1,6 @@ +import { Paper } from '@linode/ui'; import { styled } from '@mui/material/styles'; -import { Paper } from 'src/components/Paper'; - export const StyledPaper = styled(Paper, { label: 'StyledPaper', })(({ theme }) => ({ diff --git a/packages/manager/src/features/Betas/BetaDetailsList.tsx b/packages/manager/src/features/Betas/BetaDetailsList.tsx index c91eea22dd9..54926a1b8ac 100644 --- a/packages/manager/src/features/Betas/BetaDetailsList.tsx +++ b/packages/manager/src/features/Betas/BetaDetailsList.tsx @@ -1,9 +1,9 @@ +import { Paper } from '@linode/ui'; import * as React from 'react'; import { CircleProgress } from 'src/components/CircleProgress'; import { Divider } from 'src/components/Divider'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; -import { Paper } from 'src/components/Paper'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Betas/BetaSignup.tsx b/packages/manager/src/features/Betas/BetaSignup.tsx index 05fe1cbb662..6ea06dbc27f 100644 --- a/packages/manager/src/features/Betas/BetaSignup.tsx +++ b/packages/manager/src/features/Betas/BetaSignup.tsx @@ -1,3 +1,4 @@ +import { Paper } from '@linode/ui'; import { createLazyRoute, useNavigate, @@ -12,7 +13,6 @@ import { CircleProgress } from 'src/components/CircleProgress'; import { HighlightedMarkdown } from 'src/components/HighlightedMarkdown/HighlightedMarkdown'; import { LandingHeader } from 'src/components/LandingHeader/LandingHeader'; import { NotFound } from 'src/components/NotFound'; -import { Paper } from 'src/components/Paper'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; import { useCreateAccountBetaMutation } from 'src/queries/account/betas'; diff --git a/packages/manager/src/features/Billing/InvoiceDetail/InvoiceDetail.tsx b/packages/manager/src/features/Billing/InvoiceDetail/InvoiceDetail.tsx index 897e6d275e9..e88a7a0df46 100644 --- a/packages/manager/src/features/Billing/InvoiceDetail/InvoiceDetail.tsx +++ b/packages/manager/src/features/Billing/InvoiceDetail/InvoiceDetail.tsx @@ -13,7 +13,7 @@ import { DownloadCSV } from 'src/components/DownloadCSV/DownloadCSV'; import { LandingHeader } from 'src/components/LandingHeader'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Typography } from 'src/components/Typography'; import { printInvoice } from 'src/features/Billing/PdfGenerator/PdfGenerator'; import { useFlags } from 'src/hooks/useFlags'; diff --git a/packages/manager/src/features/CloudPulse/Alerts/AlertsLanding/AlertsDefinitionLanding.tsx b/packages/manager/src/features/CloudPulse/Alerts/AlertsLanding/AlertsDefinitionLanding.tsx index 381eb9cf31f..f0c692253ae 100644 --- a/packages/manager/src/features/CloudPulse/Alerts/AlertsLanding/AlertsDefinitionLanding.tsx +++ b/packages/manager/src/features/CloudPulse/Alerts/AlertsLanding/AlertsDefinitionLanding.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Route, Switch } from 'react-router-dom'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Typography } from 'src/components/Typography'; export const AlertDefinitionLanding = () => { diff --git a/packages/manager/src/features/CloudPulse/Alerts/AlertsLanding/AlertsLanding.tsx b/packages/manager/src/features/CloudPulse/Alerts/AlertsLanding/AlertsLanding.tsx index 64223dafd21..e9511a9c42d 100644 --- a/packages/manager/src/features/CloudPulse/Alerts/AlertsLanding/AlertsLanding.tsx +++ b/packages/manager/src/features/CloudPulse/Alerts/AlertsLanding/AlertsLanding.tsx @@ -1,4 +1,4 @@ -import { Box } from '@linode/ui'; +import { Box, Paper } from '@linode/ui'; import * as React from 'react'; import { Redirect, @@ -8,7 +8,6 @@ import { useRouteMatch, } from 'react-router-dom'; -import { Paper } from 'src/components/Paper'; import { TabLinkList } from 'src/components/Tabs/TabLinkList'; import { Tabs } from 'src/components/Tabs/Tabs'; import { useFlags } from 'src/hooks/useFlags'; diff --git a/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboardWithFilters.tsx b/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboardWithFilters.tsx index 4e6d958e87b..cb2ed55152e 100644 --- a/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboardWithFilters.tsx +++ b/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboardWithFilters.tsx @@ -1,9 +1,9 @@ +import { Paper } from '@linode/ui'; import { Grid } from '@mui/material'; import React from 'react'; import { CircleProgress } from 'src/components/CircleProgress'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; -import { Paper } from 'src/components/Paper'; import { useCloudPulseDashboardByIdQuery } from 'src/queries/cloudpulse/dashboards'; import { CloudPulseDashboardFilterBuilder } from '../shared/CloudPulseDashboardFilterBuilder'; diff --git a/packages/manager/src/features/CloudPulse/Widget/CloudPulseWidget.tsx b/packages/manager/src/features/CloudPulse/Widget/CloudPulseWidget.tsx index 44d5dbcdb39..9573682af68 100644 --- a/packages/manager/src/features/CloudPulse/Widget/CloudPulseWidget.tsx +++ b/packages/manager/src/features/CloudPulse/Widget/CloudPulseWidget.tsx @@ -1,8 +1,8 @@ +import { Paper } from '@linode/ui'; import { Box, Grid, Stack, Typography, useTheme } from '@mui/material'; import { DateTime } from 'luxon'; import React from 'react'; -import { Paper } from 'src/components/Paper'; import { useFlags } from 'src/hooks/useFlags'; import { useCloudPulseMetricsQuery } from 'src/queries/cloudpulse/metrics'; import { useProfile } from 'src/queries/profile/profile'; @@ -27,12 +27,12 @@ import { ZoomIcon } from './components/Zoomer'; import type { FilterValueType } from '../Dashboard/CloudPulseDashboardLanding'; import type { CloudPulseResources } from '../shared/CloudPulseResourcesSelect'; +import type { Widgets } from '@linode/api-v4'; import type { AvailableMetrics, TimeDuration, TimeGranularity, } from '@linode/api-v4'; -import type { Widgets } from '@linode/api-v4'; import type { DataSet } from 'src/components/LineGraph/LineGraph'; import type { Metrics } from 'src/utilities/statMetrics'; diff --git a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx index 501efb6dcd7..917e726bb6d 100644 --- a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx +++ b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx @@ -1,5 +1,4 @@ -import { FormControl } from '@linode/ui'; -import { BetaChip } from '@linode/ui'; +import { BetaChip, FormControl, Paper } from '@linode/ui'; import { createDatabaseSchema } from '@linode/validation/lib/databases.schema'; import Grid from '@mui/material/Unstable_Grid2'; import { createLazyRoute } from '@tanstack/react-router'; @@ -22,7 +21,6 @@ import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { LandingHeader } from 'src/components/LandingHeader'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; import { Radio } from 'src/components/Radio/Radio'; import { RadioGroup } from 'src/components/RadioGroup'; import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseBackups/DatabaseBackups.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseBackups/DatabaseBackups.tsx index 41848130675..8d2387c989e 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseBackups/DatabaseBackups.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseBackups/DatabaseBackups.tsx @@ -16,7 +16,7 @@ import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { Button } from 'src/components/Button/Button'; import { Divider } from 'src/components/Divider'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Typography } from 'src/components/Typography'; import { StyledDateCalendar, diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseBackups/legacy/DatabaseBackupsLegacy.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseBackups/legacy/DatabaseBackupsLegacy.tsx index bb260ed996f..318af63572d 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseBackups/legacy/DatabaseBackupsLegacy.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseBackups/legacy/DatabaseBackupsLegacy.tsx @@ -1,7 +1,7 @@ import { APIError } from '@linode/api-v4'; import type { Database, Engine } from '@linode/api-v4/lib/databases'; import * as React from 'react'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Table } from 'src/components/Table'; import { TableBody } from 'src/components/TableBody'; import { TableCell } from 'src/components/TableCell'; diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.tsx index 68672cc50c7..6253d642d44 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.tsx @@ -1,4 +1,4 @@ -import { Box } from '@linode/ui'; +import { Box, Paper } from '@linode/ui'; import { useSnackbar } from 'notistack'; import * as React from 'react'; import { useHistory } from 'react-router-dom'; @@ -9,7 +9,6 @@ import { Divider } from 'src/components/Divider'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { FormControlLabel } from 'src/components/FormControlLabel'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; import { Radio } from 'src/components/Radio/Radio'; import { RadioGroup } from 'src/components/RadioGroup'; import { TypeToConfirmDialog } from 'src/components/TypeToConfirmDialog/TypeToConfirmDialog'; diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx index 5b72c88a7e4..2c7f4bcc22c 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Divider } from 'src/components/Divider'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Typography } from 'src/components/Typography'; import { useProfile } from 'src/queries/profile/profile'; diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummary.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummary.tsx index f93c7f8fc0d..2e2768e6c05 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummary.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummary.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { Divider } from 'src/components/Divider'; import { Link } from 'src/components/Link'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Typography } from 'src/components/Typography'; import AccessControls from 'src/features/Databases/DatabaseDetail/AccessControls'; import ClusterConfiguration from 'src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryClusterConfiguration'; diff --git a/packages/manager/src/features/Domains/CreateDomain/CreateDomain.tsx b/packages/manager/src/features/Domains/CreateDomain/CreateDomain.tsx index 9bfb75e399f..7a4bd2d9362 100644 --- a/packages/manager/src/features/Domains/CreateDomain/CreateDomain.tsx +++ b/packages/manager/src/features/Domains/CreateDomain/CreateDomain.tsx @@ -1,4 +1,4 @@ -import { FormHelperText } from '@linode/ui'; +import { FormHelperText, Paper } from '@linode/ui'; import { createDomainSchema } from '@linode/validation/lib/domains.schema'; import { styled } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; @@ -15,7 +15,6 @@ import { FormControlLabel } from 'src/components/FormControlLabel'; import { LandingHeader } from 'src/components/LandingHeader'; import { MultipleIPInput } from 'src/components/MultipleIPInput/MultipleIPInput'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; import { Radio } from 'src/components/Radio/Radio'; import { RadioGroup } from 'src/components/RadioGroup'; import { TextField } from 'src/components/TextField'; diff --git a/packages/manager/src/features/Domains/DomainDetail/DomainDetail.tsx b/packages/manager/src/features/Domains/DomainDetail/DomainDetail.tsx index 6c6f6cff0cd..4cc6b0f8a6a 100644 --- a/packages/manager/src/features/Domains/DomainDetail/DomainDetail.tsx +++ b/packages/manager/src/features/Domains/DomainDetail/DomainDetail.tsx @@ -7,7 +7,7 @@ import { CircleProgress } from 'src/components/CircleProgress'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { LandingHeader } from 'src/components/LandingHeader'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { TagCell } from 'src/components/TagCell/TagCell'; import { Typography } from 'src/components/Typography'; import { useIsResourceRestricted } from 'src/hooks/useIsResourceRestricted'; diff --git a/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallRuleTable.styles.ts b/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallRuleTable.styles.ts index 282760c26ad..320f254c7d1 100644 --- a/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallRuleTable.styles.ts +++ b/packages/manager/src/features/Firewalls/FirewallDetail/Rules/FirewallRuleTable.styles.ts @@ -1,10 +1,9 @@ -import { Box } from '@linode/ui'; +import { Box, omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import DragIndicator from 'src/assets/icons/drag-indicator.svg'; import { Button } from 'src/components/Button/Button'; import { StyledLinkButton } from 'src/components/Button/StyledLinkButton'; -import { omittedProps } from 'src/utilities/omittedProps'; import type { FirewallRuleTableRowProps } from './FirewallRuleTable'; diff --git a/packages/manager/src/features/Help/Panels/PopularPosts.tsx b/packages/manager/src/features/Help/Panels/PopularPosts.tsx index 8c03c36b706..35a31f84e2b 100644 --- a/packages/manager/src/features/Help/Panels/PopularPosts.tsx +++ b/packages/manager/src/features/Help/Panels/PopularPosts.tsx @@ -1,12 +1,13 @@ -import { Theme } from '@mui/material/styles'; +import { Paper } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; import { Link } from 'src/components/Link'; -import { Paper } from 'src/components/Paper'; import { Typography } from 'src/components/Typography'; +import type { Theme } from '@mui/material/styles'; + const useStyles = makeStyles()((theme: Theme) => ({ post: { marginBottom: theme.spacing(0.5), diff --git a/packages/manager/src/features/Help/Panels/SearchPanel.tsx b/packages/manager/src/features/Help/Panels/SearchPanel.tsx index 49cb8a97d1c..b8f00d29b18 100644 --- a/packages/manager/src/features/Help/Panels/SearchPanel.tsx +++ b/packages/manager/src/features/Help/Panels/SearchPanel.tsx @@ -1,8 +1,8 @@ +import { Paper } from '@linode/ui'; import { styled } from '@mui/material/styles'; import * as React from 'react'; import { H1Header } from 'src/components/H1Header/H1Header'; -import { Paper } from 'src/components/Paper'; import AlgoliaSearchBar from './AlgoliaSearchBar'; diff --git a/packages/manager/src/features/Help/SupportSearchLanding/DocumentationResults.tsx b/packages/manager/src/features/Help/SupportSearchLanding/DocumentationResults.tsx index dc2813cf728..a80672f5f6c 100644 --- a/packages/manager/src/features/Help/SupportSearchLanding/DocumentationResults.tsx +++ b/packages/manager/src/features/Help/SupportSearchLanding/DocumentationResults.tsx @@ -1,12 +1,13 @@ -import { Theme } from '@mui/material/styles'; +import { Paper } from '@linode/ui'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; import { Link } from 'src/components/Link'; import { ListItem } from 'src/components/ListItem'; -import { Paper } from 'src/components/Paper'; import { Typography } from 'src/components/Typography'; +import type { Theme } from '@mui/material/styles'; + const useStyles = makeStyles()((theme: Theme) => ({ header: { marginBottom: theme.spacing(2), diff --git a/packages/manager/src/features/Images/ImagesCreate/CreateImageTab.tsx b/packages/manager/src/features/Images/ImagesCreate/CreateImageTab.tsx index e0673104c5c..968a7b84174 100644 --- a/packages/manager/src/features/Images/ImagesCreate/CreateImageTab.tsx +++ b/packages/manager/src/features/Images/ImagesCreate/CreateImageTab.tsx @@ -13,7 +13,7 @@ import { DISK_ENCRYPTION_IMAGES_CAVEAT_COPY } from 'src/components/Encryption/co import { useIsDiskEncryptionFeatureEnabled } from 'src/components/Encryption/utils'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Stack } from 'src/components/Stack'; import { TagsInput } from 'src/components/TagsInput/TagsInput'; import { TextField } from 'src/components/TextField'; diff --git a/packages/manager/src/features/Images/ImagesCreate/ImageUpload.tsx b/packages/manager/src/features/Images/ImagesCreate/ImageUpload.tsx index 37cf10a3f85..1aaebc172b9 100644 --- a/packages/manager/src/features/Images/ImagesCreate/ImageUpload.tsx +++ b/packages/manager/src/features/Images/ImagesCreate/ImageUpload.tsx @@ -13,7 +13,7 @@ import { Checkbox } from 'src/components/Checkbox'; import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Prompt } from 'src/components/Prompt/Prompt'; import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; import { Stack } from 'src/components/Stack'; diff --git a/packages/manager/src/features/Images/ImagesLanding/ImageRegions/ManageImageRegionsForm.tsx b/packages/manager/src/features/Images/ImagesLanding/ImageRegions/ManageImageRegionsForm.tsx index d2cc55c57ab..85c800b961f 100644 --- a/packages/manager/src/features/Images/ImagesLanding/ImageRegions/ManageImageRegionsForm.tsx +++ b/packages/manager/src/features/Images/ImagesLanding/ImageRegions/ManageImageRegionsForm.tsx @@ -5,7 +5,7 @@ import { useForm } from 'react-hook-form'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { RegionMultiSelect } from 'src/components/RegionSelect/RegionMultiSelect'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Images/ImagesLanding/ImagesLanding.tsx b/packages/manager/src/features/Images/ImagesLanding/ImagesLanding.tsx index 8085e6a3dbd..d002d4c97e7 100644 --- a/packages/manager/src/features/Images/ImagesLanding/ImagesLanding.tsx +++ b/packages/manager/src/features/Images/ImagesLanding/ImagesLanding.tsx @@ -1,4 +1,4 @@ -import { IconButton, InputAdornment } from '@linode/ui'; +import { IconButton, InputAdornment, Paper } from '@linode/ui'; import CloseIcon from '@mui/icons-material/Close'; import { useQueryClient } from '@tanstack/react-query'; import { createLazyRoute } from '@tanstack/react-router'; @@ -18,7 +18,6 @@ import { Hidden } from 'src/components/Hidden'; import { LandingHeader } from 'src/components/LandingHeader'; import { Notice } from 'src/components/Notice/Notice'; import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFooter'; -import { Paper } from 'src/components/Paper'; import { Table } from 'src/components/Table'; import { TableBody } from 'src/components/TableBody'; import { TableCell } from 'src/components/TableCell'; diff --git a/packages/manager/src/features/Kubernetes/CreateCluster/CreateCluster.tsx b/packages/manager/src/features/Kubernetes/CreateCluster/CreateCluster.tsx index 90b594b23e8..48ece304cd3 100644 --- a/packages/manager/src/features/Kubernetes/CreateCluster/CreateCluster.tsx +++ b/packages/manager/src/features/Kubernetes/CreateCluster/CreateCluster.tsx @@ -1,4 +1,4 @@ -import { Box } from '@linode/ui'; +import { Box, Paper } from '@linode/ui'; import { Divider } from '@mui/material'; import Grid from '@mui/material/Unstable_Grid2'; import { createLazyRoute } from '@tanstack/react-router'; @@ -13,7 +13,6 @@ import { ErrorMessage } from 'src/components/ErrorMessage'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { LandingHeader } from 'src/components/LandingHeader'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; import { RegionHelperText } from 'src/components/SelectRegionPanel/RegionHelperText'; import { Stack } from 'src/components/Stack'; diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/APLSummaryPanel.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/APLSummaryPanel.tsx index cf44f3222e3..3822b922a9b 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/APLSummaryPanel.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/APLSummaryPanel.tsx @@ -1,10 +1,10 @@ +import { Paper } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import axios from 'axios'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; import { Link } from 'src/components/Link'; -import { Paper } from 'src/components/Paper'; import { Typography } from 'src/components/Typography'; import type { KubernetesCluster } from '@linode/api-v4/lib/kubernetes'; diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeConfigPanel.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeConfigPanel.tsx index f0b02f2ae18..8715c86719f 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeConfigPanel.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeConfigPanel.tsx @@ -1,19 +1,20 @@ -import { Theme } from '@mui/material/styles'; -import { makeStyles } from 'tss-react/mui'; +import { Paper } from '@linode/ui'; import { useSnackbar } from 'notistack'; import * as React from 'react'; +import { makeStyles } from 'tss-react/mui'; import Download from 'src/assets/icons/download.svg'; import View from 'src/assets/icons/view.svg'; import { Button } from 'src/components/Button/Button'; import { Typography } from 'src/components/Typography'; -import { Paper } from 'src/components/Paper'; import { useKubenetesKubeConfigQuery } from 'src/queries/kubernetes'; import { downloadFile } from 'src/utilities/downloadFile'; import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; import { KubeConfigDrawer } from './KubeConfigDrawer'; +import type { Theme } from '@mui/material/styles'; + const useStyles = makeStyles()((theme: Theme) => ({ button: { display: 'block', diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.tsx index 1b6ec14c881..5daafad7648 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/KubeControlPaneACLDrawer.tsx @@ -1,5 +1,5 @@ import { yupResolver } from '@hookform/resolvers/yup'; -import { Box } from '@linode/ui'; +import { Box, omittedProps } from '@linode/ui'; import { kubernetesControlPlaneACLPayloadSchema } from '@linode/validation'; import { Divider, Stack } from '@mui/material'; import { styled } from '@mui/material/styles'; @@ -20,7 +20,6 @@ import { useKubernetesControlPlaneACLMutation, useKubernetesControlPlaneACLQuery, } from 'src/queries/kubernetes'; -import { omittedProps } from 'src/utilities/omittedProps'; import { scrollErrorIntoViewV2 } from 'src/utilities/scrollErrorIntoViewV2'; import type { KubernetesControlPlaneACLPayload } from '@linode/api-v4'; diff --git a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePool.tsx b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePool.tsx index 25de0fc0093..6f91f150b1c 100644 --- a/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePool.tsx +++ b/packages/manager/src/features/Kubernetes/KubernetesClusterDetail/NodePoolsDisplay/NodePool.tsx @@ -1,10 +1,9 @@ -import { Box, Tooltip } from '@linode/ui'; +import { Box, Paper, Tooltip } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; import { StyledActionButton } from 'src/components/Button/StyledActionButton'; -import { Paper } from 'src/components/Paper'; import { Typography } from 'src/components/Typography'; import { NodeTable } from './NodeTable'; diff --git a/packages/manager/src/features/Linodes/CloneLanding/CloneLanding.tsx b/packages/manager/src/features/Linodes/CloneLanding/CloneLanding.tsx index 90e46290596..1ef66ba6214 100644 --- a/packages/manager/src/features/Linodes/CloneLanding/CloneLanding.tsx +++ b/packages/manager/src/features/Linodes/CloneLanding/CloneLanding.tsx @@ -15,7 +15,7 @@ import { import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { SafeTabPanel } from 'src/components/Tabs/SafeTabPanel'; import { TabLinkList } from 'src/components/Tabs/TabLinkList'; import { TabPanels } from 'src/components/Tabs/TabPanels'; diff --git a/packages/manager/src/features/Linodes/CloneLanding/Details.tsx b/packages/manager/src/features/Linodes/CloneLanding/Details.tsx index 771ce94a07c..6c44321ce49 100644 --- a/packages/manager/src/features/Linodes/CloneLanding/Details.tsx +++ b/packages/manager/src/features/Linodes/CloneLanding/Details.tsx @@ -10,7 +10,7 @@ import { Link } from 'src/components/Link'; import { List } from 'src/components/List'; import { ListItem } from 'src/components/ListItem'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Typography } from 'src/components/Typography'; import { LinodeSelect } from 'src/features/Linodes/LinodeSelect/LinodeSelect'; import { useRegionsQuery } from 'src/queries/regions/regions'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Addons/Addons.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Addons/Addons.tsx index 23cbd33253a..ccf023632dc 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Addons/Addons.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Addons/Addons.tsx @@ -3,7 +3,7 @@ import { useWatch } from 'react-hook-form'; import { Divider } from 'src/components/Divider'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; import { useRegionsQuery } from 'src/queries/regions/regions'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Details/Details.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Details/Details.tsx index 82a2a9d1c95..514f1744675 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Details/Details.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Details/Details.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Controller, useFormContext } from 'react-hook-form'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { TagsInput } from 'src/components/TagsInput/TagsInput'; import { TextField } from 'src/components/TextField'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/EUAgreement.tsx b/packages/manager/src/features/Linodes/LinodeCreate/EUAgreement.tsx index 70e61f78697..7db562deec7 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/EUAgreement.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/EUAgreement.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { useController, useWatch } from 'react-hook-form'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; import { EUAgreementCheckbox } from 'src/features/Account/Agreements/EUAgreementCheckbox'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Error.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Error.tsx index ed95bd34dfc..8b75dc5a0bd 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Error.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Error.tsx @@ -3,7 +3,7 @@ import { useFormContext } from 'react-hook-form'; import { ErrorMessage } from 'src/components/ErrorMessage'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import type { CreateLinodeRequest } from '@linode/api-v4'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Firewall.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Firewall.tsx index 78df102ce43..013ef0e613e 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Firewall.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Firewall.tsx @@ -7,7 +7,7 @@ import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { GenerateFirewallDialog } from 'src/components/GenerateFirewallDialog/GenerateFirewallDialog'; import { Link } from 'src/components/Link'; import { LinkButton } from 'src/components/LinkButton'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; import { FIREWALL_GET_STARTED_LINK } from 'src/constants'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx index 4fc3e9001c0..84d57ffd506 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx @@ -7,7 +7,7 @@ import { DocsLink } from 'src/components/DocsLink/DocsLink'; import { useIsDiskEncryptionFeatureEnabled } from 'src/components/Encryption/utils'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; import { isDistributedRegionSupported, diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Security.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Security.tsx index dcb02f65c7b..bd4a2e76c84 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Security.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Security.tsx @@ -11,7 +11,7 @@ import { } from 'src/components/Encryption/constants'; import { Encryption } from 'src/components/Encryption/Encryption'; import { useIsDiskEncryptionFeatureEnabled } from 'src/components/Encryption/utils'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { getIsDistributedRegion } from 'src/components/RegionSelect/RegionSelect.utils'; import { Skeleton } from 'src/components/Skeleton'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Summary/Summary.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Summary/Summary.tsx index 73277af3bc7..08c1e59efab 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Summary/Summary.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Summary/Summary.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { useFormContext, useWatch } from 'react-hook-form'; import { Divider } from 'src/components/Divider'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; import { useImageQuery } from 'src/queries/images'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Backups/BackupSelect.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Backups/BackupSelect.tsx index 2b6e7de2e0a..20a2aa33e18 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Backups/BackupSelect.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Backups/BackupSelect.tsx @@ -6,7 +6,7 @@ import { useController, useWatch } from 'react-hook-form'; import { DateTimeDisplay } from 'src/components/DateTimeDisplay'; import { LinearProgress } from 'src/components/LinearProgress'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { SelectionCard } from 'src/components/SelectionCard/SelectionCard'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Backups/LinodeSelect.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Backups/LinodeSelect.tsx index dbd0b910e7e..0636a84dddf 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Backups/LinodeSelect.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Backups/LinodeSelect.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Clone/Clone.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Clone/Clone.tsx index 410a863b41d..4c7cfbfd261 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Clone/Clone.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Clone/Clone.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Images.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Images.tsx index a49e908a459..be608ee1192 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Images.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Images.tsx @@ -8,7 +8,7 @@ import ImageIcon from 'src/assets/icons/entityIcons/image.svg'; import { ImageSelect } from 'src/components/ImageSelect/ImageSelect'; import { getAPIFilterForImageSelect } from 'src/components/ImageSelect/utilities'; import { Link } from 'src/components/Link'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Placeholder } from 'src/components/Placeholder/Placeholder'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Marketplace/AppSelect.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Marketplace/AppSelect.tsx index 7b9d43d1e7d..cf463f1ce10 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Marketplace/AppSelect.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Marketplace/AppSelect.tsx @@ -5,7 +5,7 @@ import { useFormContext } from 'react-hook-form'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { DebouncedSearchTextField } from 'src/components/DebouncedSearchTextField'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; import { useMarketplaceAppsQuery } from 'src/queries/stackscripts'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/OperatingSystems.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/OperatingSystems.tsx index 29e5ff0deaa..f40539509f9 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/OperatingSystems.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/OperatingSystems.tsx @@ -1,9 +1,9 @@ +import { Paper } from '@linode/ui'; import { useQueryClient } from '@tanstack/react-query'; import React from 'react'; import { useController, useFormContext } from 'react-hook-form'; import { ImageSelect } from 'src/components/ImageSelect/ImageSelect'; -import { Paper } from 'src/components/Paper'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/StackScriptImages.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/StackScriptImages.tsx index 17c6ec25d5f..9ea42b42068 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/StackScriptImages.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/StackScriptImages.tsx @@ -1,8 +1,8 @@ +import { Paper } from '@linode/ui'; import React from 'react'; import { Controller, useWatch } from 'react-hook-form'; import { ImageSelect } from 'src/components/ImageSelect/ImageSelect'; -import { Paper } from 'src/components/Paper'; import { Typography } from 'src/components/Typography'; import { useStackScriptQuery } from 'src/queries/stackscripts'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/StackScriptSelection.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/StackScriptSelection.tsx index 3edf2b0e66c..f1fe6d0abaa 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/StackScriptSelection.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/StackScriptSelection.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { useFormContext } from 'react-hook-form'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { SafeTabPanel } from 'src/components/Tabs/SafeTabPanel'; import { Tab } from 'src/components/Tabs/Tab'; import { TabList } from 'src/components/Tabs/TabList'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/UserDefinedFields/UserDefinedFields.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/UserDefinedFields/UserDefinedFields.tsx index 69abaeeb32f..ccdfb4c3a71 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/UserDefinedFields/UserDefinedFields.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/StackScripts/UserDefinedFields/UserDefinedFields.tsx @@ -4,7 +4,7 @@ import { useFormContext, useWatch } from 'react-hook-form'; import Info from 'src/assets/icons/info.svg'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { ShowMoreExpansion } from 'src/components/ShowMoreExpansion'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx b/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx index 6c8ba206746..b0b7e8bf696 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { DocsLink } from 'src/components/DocsLink/DocsLink'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; import { RegionHelperText } from 'src/components/SelectRegionPanel/RegionHelperText'; import { SafeTabPanel } from 'src/components/Tabs/SafeTabPanel'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/VPC/VPC.tsx b/packages/manager/src/features/Linodes/LinodeCreate/VPC/VPC.tsx index ef9cfd9d07b..b3bcaa62788 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/VPC/VPC.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/VPC/VPC.tsx @@ -9,7 +9,7 @@ import { FormControlLabel } from 'src/components/FormControlLabel'; import { Link } from 'src/components/Link'; import { LinkButton } from 'src/components/LinkButton'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Stack } from 'src/components/Stack'; import { TextField } from 'src/components/TextField'; import { TooltipIcon } from 'src/components/TooltipIcon'; diff --git a/packages/manager/src/features/Linodes/LinodeCreate/utilities.ts b/packages/manager/src/features/Linodes/LinodeCreate/utilities.ts index 92fd7443e30..b9474de56cb 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/utilities.ts +++ b/packages/manager/src/features/Linodes/LinodeCreate/utilities.ts @@ -1,3 +1,4 @@ +import { omitProps } from '@linode/ui'; import { useCallback } from 'react'; import { useHistory } from 'react-router-dom'; @@ -9,7 +10,6 @@ import { sendLinodeCreateFormErrorEvent } from 'src/utilities/analytics/formEven import { isPrivateIP } from 'src/utilities/ipUtils'; import { utoa } from 'src/utilities/metadata'; import { isNotNullOrUndefined } from 'src/utilities/nullOrUndefined'; -import { omitProps } from 'src/utilities/omittedProps'; import { getQueryParamsFromQueryString } from 'src/utilities/queryParams'; import { getDefaultUDFData } from './Tabs/StackScripts/UserDefinedFields/utilities'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/CaptureSnapshot.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/CaptureSnapshot.tsx index b7c2fe6dfe5..97417dadfa8 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/CaptureSnapshot.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/CaptureSnapshot.tsx @@ -5,7 +5,7 @@ import { useSnackbar } from 'notistack'; import * as React from 'react'; import { Button } from 'src/components/Button/Button'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { TextField } from 'src/components/TextField'; import { Typography } from 'src/components/Typography'; import { useEventsPollingActions } from 'src/queries/events/events'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/LinodeBackups.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/LinodeBackups.tsx index bfd2fb5950a..2f54bf79115 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/LinodeBackups.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/LinodeBackups.tsx @@ -6,7 +6,7 @@ import { useHistory, useParams } from 'react-router-dom'; import { Button } from 'src/components/Button/Button'; import { CircleProgress } from 'src/components/CircleProgress'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { getIsDistributedRegion } from 'src/components/RegionSelect/RegionSelect.utils'; import { Table } from 'src/components/Table'; import { TableBody } from 'src/components/TableBody'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/ScheduleSettings.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/ScheduleSettings.tsx index 6f38b003c23..56fe931b48c 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/ScheduleSettings.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeBackup/ScheduleSettings.tsx @@ -8,7 +8,7 @@ import * as React from 'react'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Typography } from 'src/components/Typography'; import { useLinodeQuery, diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx index 61b0ff3e7d3..b01dae1d22b 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeFirewalls/LinodeFirewalls.tsx @@ -1,7 +1,7 @@ import { Stack } from '@mui/material'; import * as React from 'react'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Table } from 'src/components/Table'; import { TableBody } from 'src/components/TableBody'; import { TableCell } from 'src/components/TableCell'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.styles.ts b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.styles.ts index a3d291b69dd..dd50cbc72d8 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.styles.ts +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.styles.ts @@ -1,7 +1,7 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { CopyTooltip } from 'src/components/CopyTooltip/CopyTooltip'; -import { omittedProps } from 'src/utilities/omittedProps'; type StyledCopyTooltipProps = { isHovered: boolean }; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx index 084d385fa5d..15421aec60d 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/LinodeIPAddresses.tsx @@ -7,7 +7,7 @@ import { Button } from 'src/components/Button/Button'; import { CircleProgress } from 'src/components/CircleProgress'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import OrderBy from 'src/components/OrderBy'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { getIsDistributedRegion } from 'src/components/RegionSelect/RegionSelect.utils'; import { Stack } from 'src/components/Stack'; import { Table } from 'src/components/Table'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/NetworkingSummaryPanel/NetworkingSummaryPanel.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/NetworkingSummaryPanel/NetworkingSummaryPanel.tsx index accb632dbe4..d08415cd49c 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/NetworkingSummaryPanel/NetworkingSummaryPanel.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeNetworking/NetworkingSummaryPanel/NetworkingSummaryPanel.tsx @@ -2,7 +2,7 @@ import { styled, useTheme } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { useIsGeckoEnabled } from 'src/components/RegionSelect/RegionSelect.utils'; import { useLinodeQuery } from 'src/queries/linodes/linodes'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/ImageEmptyState.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/ImageEmptyState.tsx index 6b3270fdfac..960aed6ed8b 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/ImageEmptyState.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/ImageEmptyState.tsx @@ -2,7 +2,7 @@ import { useTheme } from '@mui/material/styles'; import * as React from 'react'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Typography } from 'src/components/Typography'; import type { SxProps, Theme } from '@mui/material/styles'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRescue/StandardRescueDialog.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRescue/StandardRescueDialog.tsx index c7f074f58bd..0c3f4c1969e 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRescue/StandardRescueDialog.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRescue/StandardRescueDialog.tsx @@ -8,7 +8,7 @@ import { Button } from 'src/components/Button/Button'; import { Dialog } from 'src/components/Dialog/Dialog'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { usePrevious } from 'src/hooks/usePrevious'; import { useEventsPollingActions } from 'src/queries/events/events'; import { useAllLinodeDisksQuery } from 'src/queries/linodes/disks'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/VPCPanel.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/VPCPanel.tsx index 0220fb55d70..68c87f6f469 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/VPCPanel.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/VPCPanel.tsx @@ -1,4 +1,4 @@ -import { Box } from '@linode/ui'; +import { Box, Paper } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import useMediaQuery from '@mui/material/useMediaQuery'; import * as React from 'react'; @@ -6,7 +6,6 @@ import * as React from 'react'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { Checkbox } from 'src/components/Checkbox'; import { FormControlLabel } from 'src/components/FormControlLabel'; -import { Paper } from 'src/components/Paper'; import { Stack } from 'src/components/Stack'; import { TextField } from 'src/components/TextField'; import { TooltipIcon } from 'src/components/TooltipIcon'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeDisks.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeDisks.tsx index a430e689c7a..73895ce899a 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeDisks.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeDisks.tsx @@ -8,7 +8,7 @@ import { Hidden } from 'src/components/Hidden'; import OrderBy from 'src/components/OrderBy'; import Paginate from 'src/components/Paginate'; import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFooter'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Stack } from 'src/components/Stack'; import { Table } from 'src/components/Table'; import { TableBody } from 'src/components/TableBody'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeVolumes.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeVolumes.tsx index 79d5ef0b923..73733b1f59c 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeVolumes.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeStorage/LinodeVolumes.tsx @@ -6,7 +6,7 @@ import { Button } from 'src/components/Button/Button'; import { useIsBlockStorageEncryptionFeatureEnabled } from 'src/components/Encryption/utils'; import { Hidden } from 'src/components/Hidden'; import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFooter'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Table } from 'src/components/Table'; import { TableBody } from 'src/components/TableBody'; import { TableCell } from 'src/components/TableCell'; diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeSummary/LinodeSummary.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeSummary/LinodeSummary.tsx index 55a3748120b..6a9d5e4d841 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeSummary/LinodeSummary.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeSummary/LinodeSummary.tsx @@ -9,7 +9,7 @@ import PendingIcon from 'src/assets/icons/pending.svg'; import { AreaChart } from 'src/components/AreaChart/AreaChart'; import Select from 'src/components/EnhancedSelect/Select'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Typography } from 'src/components/Typography'; import { STATS_NOT_READY_API_MESSAGE, diff --git a/packages/manager/src/features/Linodes/LinodesDetail/VolumesUpgradeBanner.tsx b/packages/manager/src/features/Linodes/LinodesDetail/VolumesUpgradeBanner.tsx index 2d1a871b763..0728eb2ada4 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/VolumesUpgradeBanner.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/VolumesUpgradeBanner.tsx @@ -4,7 +4,7 @@ import { useHistory } from 'react-router-dom'; import { Button } from 'src/components/Button/Button'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; import { getUpgradeableVolumeIds } from 'src/features/Volumes/utils'; diff --git a/packages/manager/src/features/Linodes/LinodesLanding/DisplayGroupedLinodes.tsx b/packages/manager/src/features/Linodes/LinodesLanding/DisplayGroupedLinodes.tsx index cd44009569a..ad08c035c02 100644 --- a/packages/manager/src/features/Linodes/LinodesLanding/DisplayGroupedLinodes.tsx +++ b/packages/manager/src/features/Linodes/LinodesLanding/DisplayGroupedLinodes.tsx @@ -11,7 +11,7 @@ import { PaginationFooter, getMinimumPageSizeForNumberOfItems, } from 'src/components/PaginationFooter/PaginationFooter'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { useIsGeckoEnabled } from 'src/components/RegionSelect/RegionSelect.utils'; import { TableBody } from 'src/components/TableBody'; import { TableCell } from 'src/components/TableCell'; diff --git a/packages/manager/src/features/Linodes/LinodesLanding/DisplayLinodes.styles.ts b/packages/manager/src/features/Linodes/LinodesLanding/DisplayLinodes.styles.ts index a3f6a27cf53..48c2972e77f 100644 --- a/packages/manager/src/features/Linodes/LinodesLanding/DisplayLinodes.styles.ts +++ b/packages/manager/src/features/Linodes/LinodesLanding/DisplayLinodes.styles.ts @@ -1,9 +1,8 @@ -import { IconButton } from '@linode/ui'; +import { IconButton, omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { TableRow } from 'src/components/TableRow'; import { Typography } from 'src/components/Typography'; -import { omittedProps } from 'src/utilities/omittedProps'; export const StyledTagHeaderRow = styled(TableRow, { label: 'StyledTagHeaderRow', diff --git a/packages/manager/src/features/Linodes/LinodesLanding/DisplayLinodes.tsx b/packages/manager/src/features/Linodes/LinodesLanding/DisplayLinodes.tsx index 57b42f771a0..0d83541ac4c 100644 --- a/packages/manager/src/features/Linodes/LinodesLanding/DisplayLinodes.tsx +++ b/packages/manager/src/features/Linodes/LinodesLanding/DisplayLinodes.tsx @@ -9,7 +9,7 @@ import GroupByTag from 'src/assets/icons/group-by-tag.svg'; import Paginate from 'src/components/Paginate'; import { getMinimumPageSizeForNumberOfItems } from 'src/components/PaginationFooter/PaginationFooter'; import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFooter'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { useIsGeckoEnabled } from 'src/components/RegionSelect/RegionSelect.utils'; import { TableBody } from 'src/components/TableBody'; import { useInfinitePageSize } from 'src/hooks/useInfinitePageSize'; diff --git a/packages/manager/src/features/Linodes/LinodesLanding/IPAddress.styles.ts b/packages/manager/src/features/Linodes/LinodesLanding/IPAddress.styles.ts index 5f73805886a..00a02287bc3 100644 --- a/packages/manager/src/features/Linodes/LinodesLanding/IPAddress.styles.ts +++ b/packages/manager/src/features/Linodes/LinodesLanding/IPAddress.styles.ts @@ -1,7 +1,7 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { CopyTooltip } from 'src/components/CopyTooltip/CopyTooltip'; -import { omittedProps } from 'src/utilities/omittedProps'; import type { IPAddressProps } from './IPAddress'; diff --git a/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.styles.ts b/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.styles.ts index 2ac9b6061f5..7e0587d82fe 100644 --- a/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.styles.ts +++ b/packages/manager/src/features/Linodes/LinodesLanding/LinodeRow/LinodeRow.styles.ts @@ -1,7 +1,9 @@ -import { Theme, styled } from '@mui/material/styles'; +import { omittedProps } from '@linode/ui'; +import { styled } from '@mui/material/styles'; import { TableCell } from 'src/components/TableCell'; -import { omittedProps } from 'src/utilities/omittedProps'; + +import type { Theme } from '@mui/material/styles'; type StyledMaintenanceCellProps = { maintenance: boolean }; diff --git a/packages/manager/src/features/Linodes/MigrateLinode/ConfigureForm.styles.ts b/packages/manager/src/features/Linodes/MigrateLinode/ConfigureForm.styles.ts index c82d327a144..7fe8de88f62 100644 --- a/packages/manager/src/features/Linodes/MigrateLinode/ConfigureForm.styles.ts +++ b/packages/manager/src/features/Linodes/MigrateLinode/ConfigureForm.styles.ts @@ -1,8 +1,6 @@ -import { Box } from '@linode/ui'; +import { Box, Paper } from '@linode/ui'; import { styled } from '@mui/material/styles'; -import { Paper } from 'src/components/Paper'; - export const StyledPaper = styled(Paper, { label: 'StyledPaper' })( ({ theme }) => ({ '& > p:first-of-type': { diff --git a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/CommonStyles.styles.tsx b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/CommonStyles.styles.tsx index a086c783e9e..e906d1a5df7 100644 --- a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/CommonStyles.styles.tsx +++ b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/CommonStyles.styles.tsx @@ -1,7 +1,7 @@ import { styled } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Typography } from 'src/components/Typography'; import { TimeRangeSelect } from '../../shared/TimeRangeSelect'; diff --git a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Installation.tsx b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Installation.tsx index cc42188a892..87c04c222c7 100644 --- a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Installation.tsx +++ b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Installation.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { InstallationInstructions } from '../../shared/InstallationInstructions'; diff --git a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/LongviewDetailOverview.tsx b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/LongviewDetailOverview.tsx index 7a5f48804d6..2b36364059e 100644 --- a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/LongviewDetailOverview.tsx +++ b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/LongviewDetailOverview.tsx @@ -4,7 +4,7 @@ import { pathOr } from 'ramda'; import * as React from 'react'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Props as LVDataProps } from 'src/containers/longview.stats.container'; import { LongviewPortsResponse, diff --git a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/OverviewGraphs/OverviewGraphs.tsx b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/OverviewGraphs/OverviewGraphs.tsx index a2506cab554..cd056965ae7 100644 --- a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/OverviewGraphs/OverviewGraphs.tsx +++ b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/OverviewGraphs/OverviewGraphs.tsx @@ -2,7 +2,7 @@ import { styled, useTheme } from '@mui/material/styles'; import * as React from 'react'; import Grid from '@mui/material/Unstable_Grid2'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { isToday as _isToday } from 'src/utilities/isToday'; import { WithStartAndEnd } from '../../../request.types'; diff --git a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Processes/ProcessesGraphs.tsx b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Processes/ProcessesGraphs.tsx index 65d964b732b..b99f8548e7e 100644 --- a/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Processes/ProcessesGraphs.tsx +++ b/packages/manager/src/features/Longview/LongviewDetail/DetailTabs/Processes/ProcessesGraphs.tsx @@ -1,9 +1,8 @@ -import { Box } from '@linode/ui'; +import { Box, Paper } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import * as React from 'react'; import { LongviewLineGraph } from 'src/components/LongviewLineGraph/LongviewLineGraph'; -import { Paper } from 'src/components/Paper'; import { Typography } from 'src/components/Typography'; import { convertData, diff --git a/packages/manager/src/features/Longview/LongviewDetail/GraphCard.tsx b/packages/manager/src/features/Longview/LongviewDetail/GraphCard.tsx index 54d65f298a0..8c8c02710c6 100644 --- a/packages/manager/src/features/Longview/LongviewDetail/GraphCard.tsx +++ b/packages/manager/src/features/Longview/LongviewDetail/GraphCard.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { Typography } from 'src/components/Typography'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; interface Props { children?: React.ReactNode; diff --git a/packages/manager/src/features/Longview/LongviewDetail/LongviewDetail.tsx b/packages/manager/src/features/Longview/LongviewDetail/LongviewDetail.tsx index aa059d5817a..3163e954b25 100644 --- a/packages/manager/src/features/Longview/LongviewDetail/LongviewDetail.tsx +++ b/packages/manager/src/features/Longview/LongviewDetail/LongviewDetail.tsx @@ -9,7 +9,7 @@ import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { LandingHeader } from 'src/components/LandingHeader'; import { NotFound } from 'src/components/NotFound'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { SuspenseLoader } from 'src/components/SuspenseLoader'; import { SafeTabPanel } from 'src/components/Tabs/SafeTabPanel'; import { TabLinkList } from 'src/components/Tabs/TabLinkList'; diff --git a/packages/manager/src/features/Longview/LongviewLanding/LongviewClientInstructions.tsx b/packages/manager/src/features/Longview/LongviewLanding/LongviewClientInstructions.tsx index 945e0062d64..c6dc3c1bd19 100644 --- a/packages/manager/src/features/Longview/LongviewLanding/LongviewClientInstructions.tsx +++ b/packages/manager/src/features/Longview/LongviewLanding/LongviewClientInstructions.tsx @@ -3,7 +3,7 @@ import { useTheme } from '@mui/material/styles'; import * as React from 'react'; import { EditableEntityLabel } from 'src/components/EditableEntityLabel/EditableEntityLabel'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { DispatchProps } from 'src/containers/longview.container'; import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; diff --git a/packages/manager/src/features/Longview/LongviewLanding/LongviewClientRow.tsx b/packages/manager/src/features/Longview/LongviewLanding/LongviewClientRow.tsx index c55a084bc15..5c888ea4783 100644 --- a/packages/manager/src/features/Longview/LongviewLanding/LongviewClientRow.tsx +++ b/packages/manager/src/features/Longview/LongviewLanding/LongviewClientRow.tsx @@ -3,7 +3,7 @@ import { default as Grid } from '@mui/material/Unstable_Grid2/Grid2'; import * as React from 'react'; import { compose } from 'recompose'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import withLongviewClients, { DispatchProps, } from 'src/containers/longview.container'; diff --git a/packages/manager/src/features/Longview/LongviewLanding/LongviewList.tsx b/packages/manager/src/features/Longview/LongviewLanding/LongviewList.tsx index 7b3053e7622..4e1359668de 100644 --- a/packages/manager/src/features/Longview/LongviewLanding/LongviewList.tsx +++ b/packages/manager/src/features/Longview/LongviewLanding/LongviewList.tsx @@ -6,7 +6,7 @@ import { CircleProgress } from 'src/components/CircleProgress'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import Paginate from 'src/components/Paginate'; import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFooter'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Typography } from 'src/components/Typography'; import { LongviewListRows } from './LongviewListRows'; diff --git a/packages/manager/src/features/Longview/LongviewLanding/LongviewPlans.styles.ts b/packages/manager/src/features/Longview/LongviewLanding/LongviewPlans.styles.ts index 98c5a17d2a1..2d8f848b9fe 100644 --- a/packages/manager/src/features/Longview/LongviewLanding/LongviewPlans.styles.ts +++ b/packages/manager/src/features/Longview/LongviewLanding/LongviewPlans.styles.ts @@ -1,3 +1,4 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { Chip } from 'src/components/Chip'; @@ -5,7 +6,6 @@ import { Notice } from 'src/components/Notice/Notice'; import { Table } from 'src/components/Table'; import { TableCell } from 'src/components/TableCell'; import { TableRow } from 'src/components/TableRow'; -import { omittedProps } from 'src/utilities/omittedProps'; import type { LongviewSubscriptionRowProps } from './LongviewPlans'; diff --git a/packages/manager/src/features/Longview/LongviewLanding/LongviewPlans.tsx b/packages/manager/src/features/Longview/LongviewLanding/LongviewPlans.tsx index 22edd56966f..0d6304542f3 100644 --- a/packages/manager/src/features/Longview/LongviewLanding/LongviewPlans.tsx +++ b/packages/manager/src/features/Longview/LongviewLanding/LongviewPlans.tsx @@ -11,7 +11,7 @@ import { Button } from 'src/components/Button/Button'; import { CircleProgress } from 'src/components/CircleProgress'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Radio } from 'src/components/Radio/Radio'; import { SupportLink } from 'src/components/SupportLink'; import { TableBody } from 'src/components/TableBody'; diff --git a/packages/manager/src/features/Managed/SSHAccess/LinodePubKey.styles.tsx b/packages/manager/src/features/Managed/SSHAccess/LinodePubKey.styles.tsx index a7bec2af6b9..5c4730043d4 100644 --- a/packages/manager/src/features/Managed/SSHAccess/LinodePubKey.styles.tsx +++ b/packages/manager/src/features/Managed/SSHAccess/LinodePubKey.styles.tsx @@ -3,7 +3,7 @@ import { styled } from '@mui/material/styles'; import SSHKeyIcon from 'src/assets/icons/ssh-key.svg'; import { CircleProgress } from 'src/components/CircleProgress'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Typography } from 'src/components/Typography'; export const StyledCopyToClipboardGrid = styled(Grid, { diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerCreate.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerCreate.tsx index 096cc3925ce..552f0d56928 100644 --- a/packages/manager/src/features/NodeBalancers/NodeBalancerCreate.tsx +++ b/packages/manager/src/features/NodeBalancers/NodeBalancerCreate.tsx @@ -1,4 +1,4 @@ -import { Box } from '@linode/ui'; +import { Box, Paper } from '@linode/ui'; import { useTheme } from '@mui/material'; import useMediaQuery from '@mui/material/useMediaQuery'; import { createLazyRoute } from '@tanstack/react-router'; @@ -25,7 +25,6 @@ import { ErrorMessage } from 'src/components/ErrorMessage'; import { LandingHeader } from 'src/components/LandingHeader'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; import { SelectFirewallPanel } from 'src/components/SelectFirewallPanel/SelectFirewallPanel'; import { RegionHelperText } from 'src/components/SelectRegionPanel/RegionHelperText'; diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.tsx index b79f93fe010..20b9534cf4e 100644 --- a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.tsx +++ b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/SummaryPanel.tsx @@ -2,7 +2,7 @@ import { styled } from '@mui/material/styles'; import * as React from 'react'; import { Link, useParams } from 'react-router-dom'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { TagCell } from 'src/components/TagCell/TagCell'; import { Typography } from 'src/components/Typography'; import { IPAddress } from 'src/features/Linodes/LinodesLanding/IPAddress'; diff --git a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/TablesPanel.tsx b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/TablesPanel.tsx index 1c2e7c8ff4d..b610a54444f 100644 --- a/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/TablesPanel.tsx +++ b/packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerSummary/TablesPanel.tsx @@ -8,7 +8,7 @@ import PendingIcon from 'src/assets/icons/pending.svg'; import { AreaChart } from 'src/components/AreaChart/AreaChart'; import { CircleProgress } from 'src/components/CircleProgress'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Typography } from 'src/components/Typography'; import { formatBitsPerSecond } from 'src/features/Longview/shared/utilities'; import { diff --git a/packages/manager/src/features/NotificationCenter/NotificationCenter.styles.ts b/packages/manager/src/features/NotificationCenter/NotificationCenter.styles.ts index 574587da698..e8e24d268f6 100644 --- a/packages/manager/src/features/NotificationCenter/NotificationCenter.styles.ts +++ b/packages/manager/src/features/NotificationCenter/NotificationCenter.styles.ts @@ -1,4 +1,4 @@ -import { Box } from '@linode/ui'; +import { Box, omittedProps } from '@linode/ui'; import KeyboardArrowDown from '@mui/icons-material/KeyboardArrowDown'; import { styled } from '@mui/material'; import { makeStyles } from 'tss-react/mui'; @@ -6,7 +6,6 @@ import { makeStyles } from 'tss-react/mui'; import { Avatar } from 'src/components/Avatar/Avatar'; import { Link } from 'src/components/Link'; import { Typography } from 'src/components/Typography'; -import { omittedProps } from 'src/utilities/omittedProps'; import type { NotificationCenterNotificationMessageProps } from './types'; import type { Theme } from '@mui/material/styles'; diff --git a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/HostNamesList.tsx b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/HostNamesList.tsx index daca1d84ee5..7229f67a0fd 100644 --- a/packages/manager/src/features/ObjectStorage/AccessKeyLanding/HostNamesList.tsx +++ b/packages/manager/src/features/ObjectStorage/AccessKeyLanding/HostNamesList.tsx @@ -1,11 +1,10 @@ -import { Box } from '@linode/ui'; +import { Box, omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import React, { useRef } from 'react'; import { CopyableTextField } from 'src/components/CopyableTextField/CopyableTextField'; import { List } from 'src/components/List'; import { useRegionsQuery } from 'src/queries/regions/regions'; -import { omittedProps } from 'src/utilities/omittedProps'; import { getRegionsByRegionId } from 'src/utilities/regions'; import type { ObjectStorageKey } from '@linode/api-v4/lib/object-storage'; diff --git a/packages/manager/src/features/ObjectStorage/BucketDetail/BucketAccess.tsx b/packages/manager/src/features/ObjectStorage/BucketDetail/BucketAccess.tsx index 096209ce702..cb499aa8c23 100644 --- a/packages/manager/src/features/ObjectStorage/BucketDetail/BucketAccess.tsx +++ b/packages/manager/src/features/ObjectStorage/BucketDetail/BucketAccess.tsx @@ -1,7 +1,7 @@ +import { Paper } from '@linode/ui'; import { styled } from '@mui/material/styles'; import * as React from 'react'; -import { Paper } from 'src/components/Paper'; import { Typography } from 'src/components/Typography'; import { AccessSelect } from './AccessSelect'; diff --git a/packages/manager/src/features/ObjectStorage/BucketDetail/BucketProperties.styles.ts b/packages/manager/src/features/ObjectStorage/BucketDetail/BucketProperties.styles.ts index 96183503462..32af30ee1d0 100644 --- a/packages/manager/src/features/ObjectStorage/BucketDetail/BucketProperties.styles.ts +++ b/packages/manager/src/features/ObjectStorage/BucketDetail/BucketProperties.styles.ts @@ -1,7 +1,7 @@ +import { Paper } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; -import { Paper } from 'src/components/Paper'; import { Typography } from 'src/components/Typography'; export const StyledText = styled(Typography, { diff --git a/packages/manager/src/features/ObjectStorage/BucketDetail/BucketSSL.tsx b/packages/manager/src/features/ObjectStorage/BucketDetail/BucketSSL.tsx index e3ed6211e24..c410769f1db 100644 --- a/packages/manager/src/features/ObjectStorage/BucketDetail/BucketSSL.tsx +++ b/packages/manager/src/features/ObjectStorage/BucketDetail/BucketSSL.tsx @@ -1,3 +1,4 @@ +import { Paper } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import { useFormik } from 'formik'; @@ -11,7 +12,6 @@ import { ConfirmationDialog } from 'src/components/ConfirmationDialog/Confirmati import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; import { TextField } from 'src/components/TextField'; import { Typography } from 'src/components/Typography'; import { diff --git a/packages/manager/src/features/PlacementGroups/PlacementGroupsDetail/PlacementGroupsSummary/PlacementGroupsSummary.tsx b/packages/manager/src/features/PlacementGroups/PlacementGroupsDetail/PlacementGroupsSummary/PlacementGroupsSummary.tsx index da952cfd61f..d5b3489c74b 100644 --- a/packages/manager/src/features/PlacementGroups/PlacementGroupsDetail/PlacementGroupsSummary/PlacementGroupsSummary.tsx +++ b/packages/manager/src/features/PlacementGroups/PlacementGroupsDetail/PlacementGroupsSummary/PlacementGroupsSummary.tsx @@ -10,7 +10,7 @@ import * as React from 'react'; import { DescriptionList } from 'src/components/DescriptionList/DescriptionList'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; +import { Paper } from '@linode/ui'; import { Typography } from 'src/components/Typography'; import { diff --git a/packages/manager/src/features/Profile/APITokens/APITokenTable.tsx b/packages/manager/src/features/Profile/APITokens/APITokenTable.tsx index 3f2f4c42378..f49ea6b0532 100644 --- a/packages/manager/src/features/Profile/APITokens/APITokenTable.tsx +++ b/packages/manager/src/features/Profile/APITokens/APITokenTable.tsx @@ -1,10 +1,9 @@ -import { Box } from '@linode/ui'; +import { Box, Paper } from '@linode/ui'; import * as React from 'react'; import { Button } from 'src/components/Button/Button'; import { DateTimeDisplay } from 'src/components/DateTimeDisplay'; import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFooter'; -import { Paper } from 'src/components/Paper'; import { Table } from 'src/components/Table'; import { TableBody } from 'src/components/TableBody'; import { TableCell } from 'src/components/TableCell'; diff --git a/packages/manager/src/features/Profile/AuthenticationSettings/AuthenticationSettings.tsx b/packages/manager/src/features/Profile/AuthenticationSettings/AuthenticationSettings.tsx index 72d8c3bdeef..efba74259d0 100644 --- a/packages/manager/src/features/Profile/AuthenticationSettings/AuthenticationSettings.tsx +++ b/packages/manager/src/features/Profile/AuthenticationSettings/AuthenticationSettings.tsx @@ -1,3 +1,4 @@ +import { Paper } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { createLazyRoute } from '@tanstack/react-router'; import * as React from 'react'; @@ -8,7 +9,6 @@ import { Divider } from 'src/components/Divider'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { Link } from 'src/components/Link'; -import { Paper } from 'src/components/Paper'; import { Typography } from 'src/components/Typography'; import { useProfile } from 'src/queries/profile/profile'; diff --git a/packages/manager/src/features/Profile/AuthenticationSettings/PhoneVerification/PhoneVerification.styles.ts b/packages/manager/src/features/Profile/AuthenticationSettings/PhoneVerification/PhoneVerification.styles.ts index 858bc1c1475..cd5b9f75a26 100644 --- a/packages/manager/src/features/Profile/AuthenticationSettings/PhoneVerification/PhoneVerification.styles.ts +++ b/packages/manager/src/features/Profile/AuthenticationSettings/PhoneVerification/PhoneVerification.styles.ts @@ -1,10 +1,9 @@ -import { Box, FormHelperText } from '@linode/ui'; +import { Box, FormHelperText, omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { TextField } from 'src/components/TextField'; import { Typography } from 'src/components/Typography'; -import { omittedProps } from 'src/utilities/omittedProps'; export const StyledCodeSentMessageBox = styled(Box, { label: 'StyledCodeSentMessageBox', diff --git a/packages/manager/src/features/Profile/AuthenticationSettings/TPAProviders.styles.ts b/packages/manager/src/features/Profile/AuthenticationSettings/TPAProviders.styles.ts index 928294c5572..f0cc13c638c 100644 --- a/packages/manager/src/features/Profile/AuthenticationSettings/TPAProviders.styles.ts +++ b/packages/manager/src/features/Profile/AuthenticationSettings/TPAProviders.styles.ts @@ -1,10 +1,10 @@ -import Grid from '@mui/material/Unstable_Grid2'; +import { Paper } from '@linode/ui'; import { styled } from '@mui/material/styles'; +import Grid from '@mui/material/Unstable_Grid2'; import { Button } from 'src/components/Button/Button'; import { Notice } from 'src/components/Notice/Notice'; import { Typography } from 'src/components/Typography'; -import { Paper } from 'src/components/Paper'; export const StyledRootContainer = styled(Paper, { label: 'StyledRootContainer', diff --git a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx index e15f1448a6b..e1812a53fc5 100644 --- a/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx +++ b/packages/manager/src/features/Profile/DisplaySettings/DisplaySettings.tsx @@ -1,8 +1,8 @@ +import { Paper } from '@linode/ui'; import { createLazyRoute } from '@tanstack/react-router'; import React from 'react'; import { Divider } from 'src/components/Divider'; -import { Paper } from 'src/components/Paper'; import { Stack } from 'src/components/Stack'; import { useProfile } from 'src/queries/profile/profile'; diff --git a/packages/manager/src/features/Profile/LishSettings/LishSettings.tsx b/packages/manager/src/features/Profile/LishSettings/LishSettings.tsx index c9889bb5d72..dc6f213bb8e 100644 --- a/packages/manager/src/features/Profile/LishSettings/LishSettings.tsx +++ b/packages/manager/src/features/Profile/LishSettings/LishSettings.tsx @@ -1,4 +1,4 @@ -import { Box, FormControl } from '@linode/ui'; +import { Box, FormControl, Paper } from '@linode/ui'; import { useTheme } from '@mui/material/styles'; import { createLazyRoute } from '@tanstack/react-router'; import { equals, lensPath, remove, set } from 'ramda'; @@ -9,7 +9,6 @@ import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { Button } from 'src/components/Button/Button'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; import { TextField } from 'src/components/TextField'; import { Typography } from 'src/components/Typography'; import { useMutateProfile, useProfile } from 'src/queries/profile/profile'; diff --git a/packages/manager/src/features/Profile/Referrals/Referrals.tsx b/packages/manager/src/features/Profile/Referrals/Referrals.tsx index 79dc5fa98c3..6ea34956725 100644 --- a/packages/manager/src/features/Profile/Referrals/Referrals.tsx +++ b/packages/manager/src/features/Profile/Referrals/Referrals.tsx @@ -1,3 +1,4 @@ +import { Paper } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import { createLazyRoute } from '@tanstack/react-router'; import * as React from 'react'; @@ -10,7 +11,6 @@ import { CopyableTextField } from 'src/components/CopyableTextField/CopyableText import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; import { Typography } from 'src/components/Typography'; import { useProfile } from 'src/queries/profile/profile'; import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; diff --git a/packages/manager/src/features/Profile/Settings/Settings.tsx b/packages/manager/src/features/Profile/Settings/Settings.tsx index 655e3e758ee..d8d9097879f 100644 --- a/packages/manager/src/features/Profile/Settings/Settings.tsx +++ b/packages/manager/src/features/Profile/Settings/Settings.tsx @@ -1,3 +1,4 @@ +import { Paper } from '@linode/ui'; import { createLazyRoute } from '@tanstack/react-router'; import * as React from 'react'; import { useHistory, useLocation } from 'react-router-dom'; @@ -5,7 +6,6 @@ import { useHistory, useLocation } from 'react-router-dom'; import { Code } from 'src/components/Code/Code'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { FormControlLabel } from 'src/components/FormControlLabel'; -import { Paper } from 'src/components/Paper'; import { Radio } from 'src/components/Radio/Radio'; import { RadioGroup } from 'src/components/RadioGroup'; import { Stack } from 'src/components/Stack'; diff --git a/packages/manager/src/features/StackScripts/Partials/StackScriptTableHead.styles.ts b/packages/manager/src/features/StackScripts/Partials/StackScriptTableHead.styles.ts index 0e1b5742e5f..9e2bc3c3523 100644 --- a/packages/manager/src/features/StackScripts/Partials/StackScriptTableHead.styles.ts +++ b/packages/manager/src/features/StackScripts/Partials/StackScriptTableHead.styles.ts @@ -1,10 +1,11 @@ -import { Theme, styled } from '@mui/material/styles'; +import { omittedProps } from '@linode/ui'; +import { styled } from '@mui/material/styles'; import { TableCell } from 'src/components/TableCell'; import { TableHead } from 'src/components/TableHead'; -import { omittedProps } from 'src/utilities/omittedProps'; import type { StackScriptTableHeadProps } from './StackScriptTableHead'; +import type { Theme } from '@mui/material/styles'; const tableHeadStyles = (theme: Theme) => { return { diff --git a/packages/manager/src/features/StackScripts/SelectStackScriptPanel/SelectStackScriptPanel.styles.ts b/packages/manager/src/features/StackScripts/SelectStackScriptPanel/SelectStackScriptPanel.styles.ts index f17330b1a33..bdb98656bf6 100644 --- a/packages/manager/src/features/StackScripts/SelectStackScriptPanel/SelectStackScriptPanel.styles.ts +++ b/packages/manager/src/features/StackScripts/SelectStackScriptPanel/SelectStackScriptPanel.styles.ts @@ -1,31 +1,39 @@ +import { Paper } from '@linode/ui'; import { styled } from '@mui/material/styles'; -import { Paper } from 'src/components/Paper'; import { Table } from 'src/components/Table'; -export const StyledLinkDiv = styled('div', { label: 'StyledLinkDiv' })(({ theme }) => ({ - display: 'block', - marginBottom: 24, - marginTop: theme.spacing(), - textAlign: 'right', -})); +export const StyledLinkDiv = styled('div', { label: 'StyledLinkDiv' })( + ({ theme }) => ({ + display: 'block', + marginBottom: 24, + marginTop: theme.spacing(), + textAlign: 'right', + }) +); -export const StyledPanelPaper = styled(Paper, { label: 'StyledPanelPaper' })(({ theme }) => ({ - backgroundColor: theme.color.white, - flexGrow: 1, - marginBottom: theme.spacing(3), - width: '100%', -})); +export const StyledPanelPaper = styled(Paper, { label: 'StyledPanelPaper' })( + ({ theme }) => ({ + backgroundColor: theme.color.white, + flexGrow: 1, + marginBottom: theme.spacing(3), + width: '100%', + }) +); -export const StyledSelectingPaper = styled(Paper, { label: 'StyledSelectingPaper' })({ +export const StyledSelectingPaper = styled(Paper, { + label: 'StyledSelectingPaper', +})({ maxHeight: '1000px', minHeight: '400px', overflowY: 'scroll', paddingTop: 0, }); -export const StyledTable = styled(Table, { label: 'StyledTable' })(({ theme }) => ({ - backgroundColor: theme.color.white, - flexGrow: 1, - width: '100%', -})); \ No newline at end of file +export const StyledTable = styled(Table, { label: 'StyledTable' })( + ({ theme }) => ({ + backgroundColor: theme.color.white, + flexGrow: 1, + width: '100%', + }) +); diff --git a/packages/manager/src/features/StackScripts/StackScriptBase/StackScriptBase.styles.ts b/packages/manager/src/features/StackScripts/StackScriptBase/StackScriptBase.styles.ts index b4a596d5f8f..69dab3a75fc 100644 --- a/packages/manager/src/features/StackScripts/StackScriptBase/StackScriptBase.styles.ts +++ b/packages/manager/src/features/StackScripts/StackScriptBase/StackScriptBase.styles.ts @@ -1,9 +1,9 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { DebouncedSearchTextField } from 'src/components/DebouncedSearchTextField'; import { Placeholder } from 'src/components/Placeholder/Placeholder'; import { Table } from 'src/components/Table'; -import { omittedProps } from 'src/utilities/omittedProps'; import type { WithStackScriptBaseOptions } from './StackScriptBase'; diff --git a/packages/manager/src/features/StackScripts/StackScriptForm/StackScriptForm.tsx b/packages/manager/src/features/StackScripts/StackScriptForm/StackScriptForm.tsx index 849a0551ab0..41bab8c59ed 100644 --- a/packages/manager/src/features/StackScripts/StackScriptForm/StackScriptForm.tsx +++ b/packages/manager/src/features/StackScripts/StackScriptForm/StackScriptForm.tsx @@ -1,9 +1,8 @@ -import { InputAdornment } from '@linode/ui'; +import { InputAdornment, Paper } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { ImageSelect } from 'src/components/ImageSelect/ImageSelect'; -import { Paper } from 'src/components/Paper'; import { TextField } from 'src/components/TextField'; import { Typography } from 'src/components/Typography'; import { getAPIErrorFor } from 'src/utilities/getAPIErrorFor'; diff --git a/packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/FieldTypes/UserDefinedText.tsx b/packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/FieldTypes/UserDefinedText.tsx index 61d1a9325c9..607a946b355 100644 --- a/packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/FieldTypes/UserDefinedText.tsx +++ b/packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/FieldTypes/UserDefinedText.tsx @@ -1,11 +1,12 @@ -import { UserDefinedField } from '@linode/api-v4/lib/stackscripts'; +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import * as React from 'react'; import { AccessPanel } from 'src/components/AccessPanel/AccessPanel'; import { RenderGuard } from 'src/components/RenderGuard'; import { TextField } from 'src/components/TextField'; -import { omittedProps } from 'src/utilities/omittedProps'; + +import type { UserDefinedField } from '@linode/api-v4/lib/stackscripts'; interface Props { error?: string; @@ -19,16 +20,6 @@ interface Props { } class UserDefinedText extends React.Component { - render() { - return ( -
- {this.props.isPassword - ? this.renderPasswordField() - : this.renderTextField()} -
- ); - } - handleUpdatePassword = (value: string) => { const { field, updateFormState } = this.props; updateFormState(field.name, value); @@ -71,6 +62,16 @@ class UserDefinedText extends React.Component { /> ); }; + + render() { + return ( +
+ {this.props.isPassword + ? this.renderPasswordField() + : this.renderTextField()} +
+ ); + } } type StyledAccessPanelProps = Pick; diff --git a/packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/UserDefinedFieldsPanel.styles.ts b/packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/UserDefinedFieldsPanel.styles.ts index bec7c1fc296..6b14c1c29a4 100644 --- a/packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/UserDefinedFieldsPanel.styles.ts +++ b/packages/manager/src/features/StackScripts/UserDefinedFieldsPanel/UserDefinedFieldsPanel.styles.ts @@ -1,9 +1,6 @@ -import { Box } from '@linode/ui'; +import { Box, Paper, omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; -import { Paper } from 'src/components/Paper'; -import { omittedProps } from 'src/utilities/omittedProps'; - export const StyledBox = styled(Box, { label: 'StyledBox' })(({ theme }) => ({ '& > img': { height: 60, diff --git a/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/MarkdownReference.tsx b/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/MarkdownReference.tsx index b9b98e2ffe1..51c289edd7e 100644 --- a/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/MarkdownReference.tsx +++ b/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/MarkdownReference.tsx @@ -1,8 +1,8 @@ +import { Paper } from '@linode/ui'; import * as React from 'react'; import { HighlightedMarkdown } from 'src/components/HighlightedMarkdown/HighlightedMarkdown'; import { Link } from 'src/components/Link'; -import { Paper } from 'src/components/Paper'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/PreviewReply.tsx b/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/PreviewReply.tsx index d786e28a852..4c526d57a40 100644 --- a/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/PreviewReply.tsx +++ b/packages/manager/src/features/Support/SupportTicketDetail/TabbedReply/PreviewReply.tsx @@ -1,7 +1,7 @@ +import { Paper } from '@linode/ui'; import * as React from 'react'; import { HighlightedMarkdown } from 'src/components/HighlightedMarkdown/HighlightedMarkdown'; -import { Paper } from 'src/components/Paper'; interface Props { error?: string; diff --git a/packages/manager/src/features/Support/SupportTicketDetail/TicketStatus.tsx b/packages/manager/src/features/Support/SupportTicketDetail/TicketStatus.tsx index 79f3d52f439..e056efd29d6 100644 --- a/packages/manager/src/features/Support/SupportTicketDetail/TicketStatus.tsx +++ b/packages/manager/src/features/Support/SupportTicketDetail/TicketStatus.tsx @@ -1,11 +1,10 @@ -import { SupportTicket } from '@linode/api-v4/lib/support/types'; +import { Paper } from '@linode/ui'; import { styled } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import React from 'react'; import { Hidden } from 'src/components/Hidden'; import { Link } from 'src/components/Link'; -import { Paper } from 'src/components/Paper'; import { Stack } from 'src/components/Stack'; import { StatusIcon } from 'src/components/StatusIcon/StatusIcon'; import { Typography } from 'src/components/Typography'; @@ -16,6 +15,8 @@ import { getLinkTargets } from 'src/utilities/getEventsActionLink'; import { SeverityChip } from './SeverityChip'; +import type { SupportTicket } from '@linode/api-v4/lib/support/types'; + type Props = Pick< SupportTicket, 'entity' | 'severity' | 'status' | 'updated' | 'updated_by' diff --git a/packages/manager/src/features/Support/TicketAttachmentRow.tsx b/packages/manager/src/features/Support/TicketAttachmentRow.tsx index 30c3b1a4627..eb978e15480 100644 --- a/packages/manager/src/features/Support/TicketAttachmentRow.tsx +++ b/packages/manager/src/features/Support/TicketAttachmentRow.tsx @@ -1,8 +1,7 @@ -import { Box } from '@linode/ui'; +import { Box, Paper } from '@linode/ui'; import * as React from 'react'; import { Divider } from 'src/components/Divider'; -import { Paper } from 'src/components/Paper'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; diff --git a/packages/manager/src/features/TopMenu/SearchBar/SearchSuggestion.styles.ts b/packages/manager/src/features/TopMenu/SearchBar/SearchSuggestion.styles.ts index eed1bb3881d..ec87141ff35 100644 --- a/packages/manager/src/features/TopMenu/SearchBar/SearchSuggestion.styles.ts +++ b/packages/manager/src/features/TopMenu/SearchBar/SearchSuggestion.styles.ts @@ -1,8 +1,7 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; -import { omittedProps } from 'src/utilities/omittedProps'; - -import { SearchSuggestionProps } from './SearchSuggestion'; +import type { SearchSuggestionProps } from './SearchSuggestion'; export const StyledWrapperDiv = styled('div', { label: 'StyledWrapperDiv', diff --git a/packages/manager/src/features/Users/UserPermissions.styles.ts b/packages/manager/src/features/Users/UserPermissions.styles.ts index fc2a46a9b90..a4dd0c9e512 100644 --- a/packages/manager/src/features/Users/UserPermissions.styles.ts +++ b/packages/manager/src/features/Users/UserPermissions.styles.ts @@ -1,9 +1,9 @@ +import { Paper } from '@linode/ui'; import { styled } from '@mui/material/styles'; import Grid from '@mui/material/Unstable_Grid2'; import { CircleProgress } from 'src/components/CircleProgress'; import Select from 'src/components/EnhancedSelect/Select'; -import { Paper } from 'src/components/Paper'; export const StyledSelect = styled(Select, { label: 'StyledSelect', diff --git a/packages/manager/src/features/Users/UserProfile/DeleteUserPanel.tsx b/packages/manager/src/features/Users/UserProfile/DeleteUserPanel.tsx index b17597a805b..4d941453204 100644 --- a/packages/manager/src/features/Users/UserProfile/DeleteUserPanel.tsx +++ b/packages/manager/src/features/Users/UserProfile/DeleteUserPanel.tsx @@ -1,9 +1,8 @@ -import { Box } from '@linode/ui'; +import { Box, Paper } from '@linode/ui'; import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; import { Button } from 'src/components/Button/Button'; -import { Paper } from 'src/components/Paper'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; import { PARENT_USER } from 'src/features/Account/constants'; diff --git a/packages/manager/src/features/Users/UserProfile/UserDetailsPanel.tsx b/packages/manager/src/features/Users/UserProfile/UserDetailsPanel.tsx index fb902ef61f2..9236aaa831f 100644 --- a/packages/manager/src/features/Users/UserProfile/UserDetailsPanel.tsx +++ b/packages/manager/src/features/Users/UserProfile/UserDetailsPanel.tsx @@ -1,9 +1,9 @@ +import { Paper } from '@linode/ui'; import Grid from '@mui/material/Unstable_Grid2'; import React from 'react'; import { DateTimeDisplay } from 'src/components/DateTimeDisplay'; import { MaskableText } from 'src/components/MaskableText/MaskableText'; -import { Paper } from 'src/components/Paper'; import { Stack } from 'src/components/Stack'; import { StatusIcon } from 'src/components/StatusIcon/StatusIcon'; import { TextTooltip } from 'src/components/TextTooltip'; diff --git a/packages/manager/src/features/Users/UserProfile/UserEmailPanel.tsx b/packages/manager/src/features/Users/UserProfile/UserEmailPanel.tsx index a23049081bf..ae1f97874e2 100644 --- a/packages/manager/src/features/Users/UserProfile/UserEmailPanel.tsx +++ b/packages/manager/src/features/Users/UserProfile/UserEmailPanel.tsx @@ -1,9 +1,9 @@ +import { Paper } from '@linode/ui'; import { useSnackbar } from 'notistack'; import React from 'react'; import { Controller, useForm } from 'react-hook-form'; import { Button } from 'src/components/Button/Button'; -import { Paper } from 'src/components/Paper'; import { TextField } from 'src/components/TextField'; import { RESTRICTED_FIELD_TOOLTIP } from 'src/features/Account/constants'; import { useMutateProfile, useProfile } from 'src/queries/profile/profile'; diff --git a/packages/manager/src/features/Users/UserProfile/UsernamePanel.tsx b/packages/manager/src/features/Users/UserProfile/UsernamePanel.tsx index ac4f7b7a51a..4c97c42a81c 100644 --- a/packages/manager/src/features/Users/UserProfile/UsernamePanel.tsx +++ b/packages/manager/src/features/Users/UserProfile/UsernamePanel.tsx @@ -1,10 +1,10 @@ +import { Paper } from '@linode/ui'; import { useSnackbar } from 'notistack'; import React from 'react'; import { Controller, useForm } from 'react-hook-form'; import { useHistory } from 'react-router-dom'; import { Button } from 'src/components/Button/Button'; -import { Paper } from 'src/components/Paper'; import { TextField } from 'src/components/TextField'; import { RESTRICTED_FIELD_TOOLTIP } from 'src/features/Account/constants'; import { useUpdateUserMutation } from 'src/queries/account/users'; diff --git a/packages/manager/src/features/VPCs/VPCCreate/FormComponents/VPCCreateForm.styles.ts b/packages/manager/src/features/VPCs/VPCCreate/FormComponents/VPCCreateForm.styles.ts index 183f8941b8e..2a3df71e598 100644 --- a/packages/manager/src/features/VPCs/VPCCreate/FormComponents/VPCCreateForm.styles.ts +++ b/packages/manager/src/features/VPCs/VPCCreate/FormComponents/VPCCreateForm.styles.ts @@ -1,7 +1,7 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { Typography } from 'src/components/Typography'; -import { omittedProps } from 'src/utilities/omittedProps'; type StyledVPCFormProps = { isDrawer?: boolean; diff --git a/packages/manager/src/features/VPCs/VPCCreate/VPCCreate.tsx b/packages/manager/src/features/VPCs/VPCCreate/VPCCreate.tsx index bfcba44b36c..8995ec53601 100644 --- a/packages/manager/src/features/VPCs/VPCCreate/VPCCreate.tsx +++ b/packages/manager/src/features/VPCs/VPCCreate/VPCCreate.tsx @@ -1,20 +1,20 @@ -import Grid from '@mui/material/Unstable_Grid2'; +import { Paper } from '@linode/ui'; import { styled } from '@mui/material/styles'; +import Grid from '@mui/material/Unstable_Grid2'; +import { createLazyRoute } from '@tanstack/react-router'; import * as React from 'react'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { LandingHeader } from 'src/components/LandingHeader'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; -import { SubnetContent } from 'src/features/VPCs/VPCCreate/FormComponents/SubnetContent'; import { VPC_GETTING_STARTED_LINK } from 'src/features/VPCs/constants'; +import { SubnetContent } from 'src/features/VPCs/VPCCreate/FormComponents/SubnetContent'; import { useCreateVPC } from 'src/hooks/useCreateVPC'; import { CannotCreateVPCNotice } from './FormComponents/CannotCreateVPCNotice'; import { StyledHeaderTypography } from './FormComponents/VPCCreateForm.styles'; import { VPCTopSectionContent } from './FormComponents/VPCTopSectionContent'; -import { createLazyRoute } from '@tanstack/react-router'; const VPCCreate = () => { const { diff --git a/packages/manager/src/features/Volumes/VolumeCreate.tsx b/packages/manager/src/features/Volumes/VolumeCreate.tsx index dc96018b93f..cc2ac13e4ea 100644 --- a/packages/manager/src/features/Volumes/VolumeCreate.tsx +++ b/packages/manager/src/features/Volumes/VolumeCreate.tsx @@ -1,4 +1,4 @@ -import { Box } from '@linode/ui'; +import { Box, Paper } from '@linode/ui'; import { CreateVolumeSchema } from '@linode/validation/lib/volumes.schema'; import { useTheme } from '@mui/material/styles'; import { createLazyRoute } from '@tanstack/react-router'; @@ -23,7 +23,6 @@ import { useIsBlockStorageEncryptionFeatureEnabled } from 'src/components/Encryp import { ErrorMessage } from 'src/components/ErrorMessage'; import { LandingHeader } from 'src/components/LandingHeader'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; import { Stack } from 'src/components/Stack'; import { TextField } from 'src/components/TextField'; diff --git a/packages/manager/src/features/components/PlansPanel/DistributedRegionPlanTable.tsx b/packages/manager/src/features/components/PlansPanel/DistributedRegionPlanTable.tsx index 0512c10190c..ea927d09792 100644 --- a/packages/manager/src/features/components/PlansPanel/DistributedRegionPlanTable.tsx +++ b/packages/manager/src/features/components/PlansPanel/DistributedRegionPlanTable.tsx @@ -1,9 +1,8 @@ -import { Box } from '@linode/ui'; +import { Box, Paper } from '@linode/ui'; import { styled } from '@mui/material/styles'; import React from 'react'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from 'src/components/Paper'; import { Typography } from 'src/components/Typography'; import type { SxProps, Theme } from '@mui/material/styles'; diff --git a/packages/manager/src/features/components/PlansPanel/PlanContainer.styles.ts b/packages/manager/src/features/components/PlansPanel/PlanContainer.styles.ts index 30cc5351841..6260a155855 100644 --- a/packages/manager/src/features/components/PlansPanel/PlanContainer.styles.ts +++ b/packages/manager/src/features/components/PlansPanel/PlanContainer.styles.ts @@ -1,8 +1,10 @@ +import { omittedProps } from '@linode/ui'; import { styled } from '@mui/material/styles'; import { Table } from 'src/components/Table'; -import { TableCell, TableCellProps } from 'src/components/TableCell'; -import { omittedProps } from 'src/utilities/omittedProps'; +import { TableCell } from 'src/components/TableCell'; + +import type { TableCellProps } from 'src/components/TableCell'; interface StyledTableCellPropsProps extends TableCellProps { isPlanCell?: boolean; diff --git a/packages/ui/.changeset/pr-11183-added-1730215173323.md b/packages/ui/.changeset/pr-11183-added-1730215173323.md new file mode 100644 index 00000000000..c5a6464588a --- /dev/null +++ b/packages/ui/.changeset/pr-11183-added-1730215173323.md @@ -0,0 +1,5 @@ +--- +"@linode/ui": Added +--- + +Migrate `Paper` from `manager` to `ui` package ([#11183](https://github.com/linode/manager/pull/11183)) diff --git a/packages/manager/src/components/Paper.stories.tsx b/packages/ui/src/components/Paper/Paper.stories.tsx similarity index 100% rename from packages/manager/src/components/Paper.stories.tsx rename to packages/ui/src/components/Paper/Paper.stories.tsx diff --git a/packages/manager/src/components/Paper.tsx b/packages/ui/src/components/Paper/Paper.tsx similarity index 89% rename from packages/manager/src/components/Paper.tsx rename to packages/ui/src/components/Paper/Paper.tsx index 778c766c4f7..7badae6d9df 100644 --- a/packages/manager/src/components/Paper.tsx +++ b/packages/ui/src/components/Paper/Paper.tsx @@ -1,10 +1,8 @@ -import { FormHelperText } from '@linode/ui'; +import { FormHelperText } from '../FormHelperText/FormHelperText'; import _Paper from '@mui/material/Paper'; import { styled } from '@mui/material/styles'; import * as React from 'react'; -import { omittedProps } from 'src/utilities/omittedProps'; - import type { PaperProps } from '@mui/material/Paper'; interface Props extends PaperProps { @@ -39,7 +37,7 @@ export const Paper = (props: Props) => { }; const StyledPaper = styled(_Paper, { - shouldForwardProp: omittedProps(['error']), + shouldForwardProp: (prop) => prop !== 'error', })(({ theme, ...props }) => ({ borderColor: props.error ? theme.palette.error.dark : undefined, padding: theme.spacing(3), diff --git a/packages/ui/src/components/Paper/index.ts b/packages/ui/src/components/Paper/index.ts new file mode 100644 index 00000000000..2b9436574cb --- /dev/null +++ b/packages/ui/src/components/Paper/index.ts @@ -0,0 +1 @@ +export * from './Paper'; diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index 0fd364db998..d1887cebe1c 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -7,5 +7,6 @@ export * from './IconButton'; export * from './Input'; export * from './InputAdornment'; export * from './InputLabel'; +export * from './Paper'; export * from './Tooltip'; export * from './VisibilityTooltip'; diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 43f9ba85b57..952d61ff3b1 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -1,2 +1,3 @@ export * from './components'; export * from './foundations'; +export * from './utilities'; diff --git a/packages/ui/src/utilities/index.ts b/packages/ui/src/utilities/index.ts new file mode 100644 index 00000000000..2993dc70047 --- /dev/null +++ b/packages/ui/src/utilities/index.ts @@ -0,0 +1 @@ +export * from './omittedProps'; diff --git a/packages/manager/src/utilities/omittedProps.test.tsx b/packages/ui/src/utilities/omittedProps.test.tsx similarity index 90% rename from packages/manager/src/utilities/omittedProps.test.tsx rename to packages/ui/src/utilities/omittedProps.test.tsx index 56a046e1220..9510870f3da 100644 --- a/packages/manager/src/utilities/omittedProps.test.tsx +++ b/packages/ui/src/utilities/omittedProps.test.tsx @@ -1,6 +1,7 @@ // Styled component using omittedProps import styled from '@emotion/styled'; - +import '@testing-library/jest-dom/vitest'; +import { describe, expect, it } from 'vitest'; import { omitProps, omittedProps } from './omittedProps'; type StyledProps = { @@ -11,7 +12,7 @@ type StyledProps = { const MyStyledComponent = styled('div', { label: 'MyStyledComponent', - shouldForwardProp: omittedProps(['extraProp', 'anotherProp']), + shouldForwardProp: omittedProps(['extraProp', 'anotherProp']), })` color: ${(props) => props.color}; `; diff --git a/packages/manager/src/utilities/omittedProps.ts b/packages/ui/src/utilities/omittedProps.ts similarity index 92% rename from packages/manager/src/utilities/omittedProps.ts rename to packages/ui/src/utilities/omittedProps.ts index 9430071eef7..330b95f7f02 100644 --- a/packages/manager/src/utilities/omittedProps.ts +++ b/packages/ui/src/utilities/omittedProps.ts @@ -24,7 +24,10 @@ export const omittedProps = (props: Array) => ( * @param toRemove Array of props to remove * @returns Object with props removed */ -export const omitProps = ( +export const omitProps = < + Props extends NonNullable, + Keys extends keyof Props & string +>( props: Props, toRemove: Keys[] & string[] ) => From 06f54a510b80cd41d6ac9c2ad366fc44c22cb3f5 Mon Sep 17 00:00:00 2001 From: Banks Nussman <115251059+bnussman-akamai@users.noreply.github.com> Date: Mon, 4 Nov 2024 11:33:52 -0500 Subject: [PATCH 41/66] refactor: [M3-8813] - Remove use of Redux for viewing StackScript details (#11192) * use normal dialog without redux * add changeset * just use one state --------- Co-authored-by: Banks Nussman --- .../pr-11192-tech-stories-1730318704149.md | 5 + .../LinodeRebuild/RebuildFromStackScript.tsx | 20 +++- .../SelectStackScriptPanel.tsx | 7 ++ .../SelectStackScriptPanelContent.tsx | 2 + .../SelectStackScriptsSection.tsx | 11 ++- .../StackScriptSelectionRow.tsx | 42 ++------ .../StackScripts/StackScriptDialog.tsx | 95 ------------------- packages/manager/src/store/index.ts | 7 -- .../src/store/stackScriptDialog/index.ts | 52 ---------- 9 files changed, 49 insertions(+), 192 deletions(-) create mode 100644 packages/manager/.changeset/pr-11192-tech-stories-1730318704149.md delete mode 100644 packages/manager/src/features/StackScripts/StackScriptDialog.tsx delete mode 100644 packages/manager/src/store/stackScriptDialog/index.ts diff --git a/packages/manager/.changeset/pr-11192-tech-stories-1730318704149.md b/packages/manager/.changeset/pr-11192-tech-stories-1730318704149.md new file mode 100644 index 00000000000..15a849acd4c --- /dev/null +++ b/packages/manager/.changeset/pr-11192-tech-stories-1730318704149.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tech Stories +--- + +Remove use of Redux for viewing StackScript details ([#11192](https://github.com/linode/manager/pull/11192)) diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/RebuildFromStackScript.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/RebuildFromStackScript.tsx index df18423158b..b875867d610 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/RebuildFromStackScript.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/RebuildFromStackScript.tsx @@ -12,7 +12,6 @@ import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { ImageSelect } from 'src/components/ImageSelect/ImageSelect'; import { TypeToConfirm } from 'src/components/TypeToConfirm/TypeToConfirm'; import SelectStackScriptPanel from 'src/features/StackScripts/SelectStackScriptPanel/SelectStackScriptPanel'; -import StackScriptDialog from 'src/features/StackScripts/StackScriptDialog'; import { getCommunityStackscripts, getMineAndAccountStackScripts, @@ -32,6 +31,7 @@ import { import { scrollErrorIntoView } from 'src/utilities/scrollErrorIntoView'; import { extendValidationSchema } from 'src/utilities/validatePassword'; +import { StackScriptDetailsDialog } from '../../LinodeCreate/Tabs/StackScripts/StackScriptDetailsDialog'; import { ImageEmptyState } from './ImageEmptyState'; import type { UserDefinedField } from '@linode/api-v4/lib/stackscripts'; @@ -118,6 +118,15 @@ export const RebuildFromStackScript = (props: Props) => { Object.keys(_imagesData).map((eachKey) => _imagesData[eachKey]) ); + const [ + selectedStackScriptIdForDetailsDialog, + setSelectedStackScriptIdForDetailsDialog, + ] = React.useState(); + + const onOpenStackScriptDetailsDialog = (stackscriptId: number) => { + setSelectedStackScriptIdForDetailsDialog(stackscriptId); + }; + // In this component, most errors are handled by Formik. This is not // possible with UDFs, since they are dynamic. Their errors need to // be handled separately. @@ -291,6 +300,7 @@ export const RebuildFromStackScript = (props: Props) => { error={errors.stackscript_id} header="Select StackScript" onSelect={handleSelect} + openStackScriptDetailsDialog={onOpenStackScriptDetailsDialog} publicImages={filterImagesByType(_imagesData, 'public')} resetSelectedStackScript={resetStackScript} selectedId={ss.id} @@ -380,7 +390,13 @@ export const RebuildFromStackScript = (props: Props) => { /> - + + setSelectedStackScriptIdForDetailsDialog(undefined) + } + id={selectedStackScriptIdForDetailsDialog} + open={selectedStackScriptIdForDetailsDialog !== undefined} + /> ); }} diff --git a/packages/manager/src/features/StackScripts/SelectStackScriptPanel/SelectStackScriptPanel.tsx b/packages/manager/src/features/StackScripts/SelectStackScriptPanel/SelectStackScriptPanel.tsx index bf16b9481cf..246c633d453 100644 --- a/packages/manager/src/features/StackScripts/SelectStackScriptPanel/SelectStackScriptPanel.tsx +++ b/packages/manager/src/features/StackScripts/SelectStackScriptPanel/SelectStackScriptPanel.tsx @@ -52,6 +52,7 @@ interface Props extends RenderGuardProps { images: string[], userDefinedFields: UserDefinedField[] ) => void; + openStackScriptDetailsDialog: (stackscriptId: number) => void; publicImages: Record; request: ( username: string, @@ -139,6 +140,9 @@ class SelectStackScriptPanel extends React.Component< />
void; + openStackScriptDetailsDialog: (stackscriptId: number) => void; publicImages: Record; request: StackScriptsRequest; resetStackScriptSelection: () => void; @@ -47,6 +48,7 @@ class SelectStackScriptPanelContent extends React.Component< disabled={this.props.disabled} isSorting={this.props.isSorting} onSelect={this.handleSelectStackScript} + openStackScriptDetailsDialog={this.props.openStackScriptDetailsDialog} publicImages={this.props.publicImages} selectedId={selected} /> diff --git a/packages/manager/src/features/StackScripts/SelectStackScriptPanel/SelectStackScriptsSection.tsx b/packages/manager/src/features/StackScripts/SelectStackScriptPanel/SelectStackScriptsSection.tsx index a207afdc66b..6d3501573a0 100644 --- a/packages/manager/src/features/StackScripts/SelectStackScriptPanel/SelectStackScriptsSection.tsx +++ b/packages/manager/src/features/StackScripts/SelectStackScriptPanel/SelectStackScriptsSection.tsx @@ -18,12 +18,20 @@ interface Props { disabled?: boolean; isSorting: boolean; onSelect: (s: StackScript) => void; + openStackScriptDetailsDialog: (stackscriptId: number) => void; publicImages: Record; selectedId?: number; } export const SelectStackScriptsSection = (props: Props) => { - const { data, disabled, isSorting, onSelect, selectedId } = props; + const { + data, + disabled, + isSorting, + onSelect, + openStackScriptDetailsDialog, + selectedId, + } = props; const { data: profile } = useProfile(); @@ -40,6 +48,7 @@ export const SelectStackScriptsSection = (props: Props) => { key={s.id} label={s.label} onSelect={() => onSelect(s)} + openStackScriptDetailsDialog={openStackScriptDetailsDialog} stackScriptID={s.id} stackScriptUsername={s.username} updateFor={[selectedId === s.id]} diff --git a/packages/manager/src/features/StackScripts/SelectStackScriptPanel/StackScriptSelectionRow.tsx b/packages/manager/src/features/StackScripts/SelectStackScriptPanel/StackScriptSelectionRow.tsx index 8d253c45ca3..e62856f1015 100644 --- a/packages/manager/src/features/StackScripts/SelectStackScriptPanel/StackScriptSelectionRow.tsx +++ b/packages/manager/src/features/StackScripts/SelectStackScriptPanel/StackScriptSelectionRow.tsx @@ -1,13 +1,10 @@ import * as React from 'react'; -import { MapDispatchToProps, connect } from 'react-redux'; -import { compose as recompose } from 'recompose'; import { Radio } from 'src/components/Radio/Radio'; -import { RenderGuard, RenderGuardProps } from 'src/components/RenderGuard'; +import { RenderGuard } from 'src/components/RenderGuard'; import { TableCell } from 'src/components/TableCell'; import { TableRow } from 'src/components/TableRow'; import { Typography } from 'src/components/Typography'; -import { openStackScriptDialog as openStackScriptDialogAction } from 'src/store/stackScriptDialog'; import { StyledDetailsButton, @@ -20,7 +17,7 @@ import { StyledUsernameLabel, } from '../CommonStackScript.styles'; -export interface Props { +interface Props { checked?: boolean; deploymentsActive: number; description: string; @@ -28,24 +25,13 @@ export interface Props { disabledCheckedSelect?: boolean; label: string; onSelect?: (e: React.ChangeEvent, value: boolean) => void; + openStackScriptDetailsDialog: (stackscriptId: number) => void; stackScriptID: number; stackScriptUsername: string; updated: string; } -interface DispatchProps { - openStackScriptDialog: (stackScriptId: number) => void; -} - -export interface StackScriptSelectionRowProps - extends Props, - DispatchProps, - RenderGuardProps {} - -export class StackScriptSelectionRow extends React.Component< - StackScriptSelectionRowProps, - {} -> { +export class StackScriptSelectionRow extends React.Component { render() { const { checked, @@ -54,7 +40,7 @@ export class StackScriptSelectionRow extends React.Component< disabledCheckedSelect, label, onSelect, - openStackScriptDialog, + openStackScriptDetailsDialog, stackScriptID, stackScriptUsername, } = this.props; @@ -62,7 +48,7 @@ export class StackScriptSelectionRow extends React.Component< const renderLabel = () => { const openDialog = (event: React.MouseEvent) => { event.stopPropagation(); - openStackScriptDialog(stackScriptID); + openStackScriptDetailsDialog(stackScriptID); }; return ( @@ -110,18 +96,4 @@ export class StackScriptSelectionRow extends React.Component< } } -const mapDispatchToProps: MapDispatchToProps = ( - dispatch -) => { - return { - openStackScriptDialog: (stackScriptId: number) => - dispatch(openStackScriptDialogAction(stackScriptId)), - }; -}; - -interface ExportProps extends Props, RenderGuardProps {} - -export default recompose( - connect(undefined, mapDispatchToProps), - RenderGuard -)(StackScriptSelectionRow); +export default RenderGuard(StackScriptSelectionRow); diff --git a/packages/manager/src/features/StackScripts/StackScriptDialog.tsx b/packages/manager/src/features/StackScripts/StackScriptDialog.tsx deleted file mode 100644 index 56f1509cba5..00000000000 --- a/packages/manager/src/features/StackScripts/StackScriptDialog.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { StackScript, getStackScript } from '@linode/api-v4/lib/stackscripts'; -import { path, pathOr } from 'ramda'; -import * as React from 'react'; -import { MapDispatchToProps, connect } from 'react-redux'; - -import { CircleProgress } from 'src/components/CircleProgress'; -import { Dialog } from 'src/components/Dialog/Dialog'; -import { Notice } from 'src/components/Notice/Notice'; -import { StackScript as _StackScript } from 'src/components/StackScript/StackScript'; -import { ApplicationState } from 'src/store'; -import { closeStackScriptDialog } from 'src/store/stackScriptDialog'; -import { MapState } from 'src/store/types'; - -interface DispatchProps { - closeDrawer: () => void; -} - -interface Props { - open: boolean; - stackScriptId?: number; -} - -interface StackScriptDialogProps extends DispatchProps, Props {} - -export const StackScriptDialog = (props: StackScriptDialogProps) => { - const { closeDrawer, open, stackScriptId } = props; - - const [stackScript, setStackScript] = React.useState( - undefined - ); - const [loading, setLoading] = React.useState(false); - const [error, setError] = React.useState(true); - - const title = stackScript - ? `${stackScript.username} / ${stackScript.label}` - : 'StackScript'; - - React.useEffect(() => { - if (stackScriptId) { - setLoading(true); - getStackScript(stackScriptId) - .then((stackScript) => { - setStackScript(stackScript); - setLoading(false); - setError(false); - }) - .catch(() => { - setLoading(false); - setError(true); - }); - } - }, [stackScriptId]); - - return ( - - {loading ? ( - - ) : ( - <> - {error && ( - - )} - {stackScript && ( - <_StackScript data={stackScript} userCanModify={false} /> - )} - - )} - - ); -}; - -const mapDispatchToProps: MapDispatchToProps = ( - dispatch -) => { - return { - closeDrawer: () => dispatch(closeStackScriptDialog()), - }; -}; - -const mapStateToProps: MapState = (state: ApplicationState) => ({ - open: pathOr(false, ['stackScriptDialog', 'open'], state), - stackScriptId: path(['stackScriptDialog', 'stackScriptId'], state), -}); - -export default connect(mapStateToProps, mapDispatchToProps)(StackScriptDialog); diff --git a/packages/manager/src/store/index.ts b/packages/manager/src/store/index.ts index 12607a8526b..66b6be90e1a 100644 --- a/packages/manager/src/store/index.ts +++ b/packages/manager/src/store/index.ts @@ -16,10 +16,6 @@ import longviewStats, { State as LongviewStatsState, defaultState as defaultLongviewStatsState, } from 'src/store/longviewStats/longviewStats.reducer'; -import stackScriptDialog, { - State as StackScriptDialogState, - defaultState as stackScriptDialogDefaultState, -} from 'src/store/stackScriptDialog'; import mockFeatureFlags, { MockFeatureFlagState, @@ -38,7 +34,6 @@ export interface ApplicationState { longviewStats: LongviewStatsState; mockFeatureFlags: MockFeatureFlagState; pendingUpload: PendingUploadState; - stackScriptDialog: StackScriptDialogState; } export const defaultState: ApplicationState = { @@ -48,7 +43,6 @@ export const defaultState: ApplicationState = { longviewStats: defaultLongviewStatsState, mockFeatureFlags: defaultMockFeatureFlagState, pendingUpload: pendingUploadState, - stackScriptDialog: stackScriptDialogDefaultState, }; /** @@ -61,7 +55,6 @@ const reducers = combineReducers({ longviewStats, mockFeatureFlags, pendingUpload, - stackScriptDialog, }); export const storeFactory = () => diff --git a/packages/manager/src/store/stackScriptDialog/index.ts b/packages/manager/src/store/stackScriptDialog/index.ts deleted file mode 100644 index 258ea209dd7..00000000000 --- a/packages/manager/src/store/stackScriptDialog/index.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Action } from 'redux'; - -// ACTIONS -export const OPEN = '@manager/stackScript/OPEN'; -export const CLOSE = '@manager/stackScript/CLOSE'; - -export interface State { - open: boolean; - stackScriptId?: number; -} - -interface Open extends Action { - stackScriptId: number; - type: typeof OPEN; -} - -interface Close extends Action { - type: typeof CLOSE; -} - -type ActionCreator = (...args: any[]) => Action; - -// ACTION CREATORS -export const openStackScriptDialog: ActionCreator = ( - stackScriptId: number -): Open => ({ - stackScriptId, - type: OPEN, -}); -export const closeStackScriptDialog: ActionCreator = (): Close => ({ - type: CLOSE, -}); - -// DEFAULT STATE -export const defaultState: State = { - open: false, -}; - -type ActionTypes = Close | Open; - -export default (state: State = defaultState, action: ActionTypes) => { - switch (action.type) { - case OPEN: - return { open: true, stackScriptId: action.stackScriptId }; - - case CLOSE: - return { ...state, open: false }; - - default: - return state; - } -}; From 7c547f63edb31f4ff063c895101f7c47a2060402 Mon Sep 17 00:00:00 2001 From: jdamore-linode <97627410+jdamore-linode@users.noreply.github.com> Date: Mon, 4 Nov 2024 14:39:20 -0500 Subject: [PATCH 42/66] test: [M3-8114] - Clean up Linodes, LKE clusters, and Firewalls after Cypress runs (#11189) * Clean up Linodes, Firewalls, and LKE Clusters after test run, and improve util docs * Added changeset: Delete test Linodes, LKE clusters, and Firewalls after Cypress runs --- .../pr-11189-tests-1730310015248.md | 5 + packages/manager/cypress.config.ts | 2 + packages/manager/cypress/support/api/lke.ts | 6 + .../support/plugins/post-run-cleanup.ts | 174 ++++++++++++++++++ 4 files changed, 187 insertions(+) create mode 100644 packages/manager/.changeset/pr-11189-tests-1730310015248.md create mode 100644 packages/manager/cypress/support/plugins/post-run-cleanup.ts diff --git a/packages/manager/.changeset/pr-11189-tests-1730310015248.md b/packages/manager/.changeset/pr-11189-tests-1730310015248.md new file mode 100644 index 00000000000..0fbe1f93553 --- /dev/null +++ b/packages/manager/.changeset/pr-11189-tests-1730310015248.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tests +--- + +Delete test Linodes, LKE clusters, and Firewalls after Cypress runs ([#11189](https://github.com/linode/manager/pull/11189)) diff --git a/packages/manager/cypress.config.ts b/packages/manager/cypress.config.ts index b51562d9f38..83903ec697c 100644 --- a/packages/manager/cypress.config.ts +++ b/packages/manager/cypress.config.ts @@ -18,6 +18,7 @@ import { generateTestWeights } from './cypress/support/plugins/generate-weights' import { logTestTagInfo } from './cypress/support/plugins/test-tagging-info'; import cypressViteConfig from './cypress/vite.config'; import { featureFlagOverrides } from './cypress/support/plugins/feature-flag-override'; +import { postRunCleanup } from './cypress/support/plugins/post-run-cleanup'; /** * Exports a Cypress configuration object. @@ -97,6 +98,7 @@ export default defineConfig({ splitCypressRun, enableJunitReport(), generateTestWeights, + postRunCleanup, ]); }, }, diff --git a/packages/manager/cypress/support/api/lke.ts b/packages/manager/cypress/support/api/lke.ts index 9551ca55460..14ed8339749 100644 --- a/packages/manager/cypress/support/api/lke.ts +++ b/packages/manager/cypress/support/api/lke.ts @@ -35,6 +35,12 @@ const isPoolReady = (pool: KubeNodePoolResponse): boolean => /** * Delete all LKE clusters whose labels are prefixed with "cy-test-". * + * Sometimes when attempting to delete provisioning LKE clusters, the cluster + * becomes stuck and requires manual intervention to resolve. To reduce the risk + * of this happening, this function will only delete clusters that have finished + * provisioning (i.e. all nodes have `'ready'` status) or which have existed + * for at least an hour. + * * @returns Promise that resolves when test LKE clusters have been deleted. */ export const deleteAllTestLkeClusters = async (): Promise => { diff --git a/packages/manager/cypress/support/plugins/post-run-cleanup.ts b/packages/manager/cypress/support/plugins/post-run-cleanup.ts new file mode 100644 index 00000000000..6be94af0a30 --- /dev/null +++ b/packages/manager/cypress/support/plugins/post-run-cleanup.ts @@ -0,0 +1,174 @@ +import { DateTime } from 'luxon'; +import { depaginate } from '../util/paginate'; +import { CypressPlugin } from './plugin'; + +import { + deleteFirewall, + deleteKubernetesCluster, + deleteLinode, + Firewall, + getFirewalls, + getKubernetesClusters, + getLinodes, + getNodePools, + KubeNodePoolResponse, + KubernetesCluster, + Linode, + PoolNodeResponse, +} from '@linode/api-v4'; + +// TODO Refactor to use utilities after M3-8803. +/* + * Cypress configuration and plugins are executed in Node.js where our + * path aliases `support`, `src`/`@src`, etc., are unavailable. Additionally, + * some Cypress-specific objects like `cy` and `Cypress` are unavailable. + * + * As a result, we cannot import any code which uses aliases, uses `cy`/`Cypress`, + * or imports any code which does (and so on...) from here. Because of this + * limitation, we can't import our existing utilities related to resource clean + * up and sadly must re-implement them here. + * + * M3-8803 seeks to reorganize our utilities to better distinguish which code + * is executed and expected to be available where, and after that point we + * should be able to refactor this plugin to take advantage of existing utilities + * like `deleteAllTestLinodes`, `deleteAllTestFirewalls`, etc. + */ + +// Test resource label/name prefix. +const TEST_TAG_PREFIX = 'cy-test-'; + +// Desired number of items per page of a paginated API request. +const PAGE_SIZE = 500; + +/* + * Determines if the given node pool is ready by checking the status of each node. + */ +const isPoolReady = (pool: KubeNodePoolResponse): boolean => + pool.nodes.every((node: PoolNodeResponse) => node.status === 'ready'); + +/** + * Deletes all test Linodes on the test account. + * + * This is a re-implementation of an existing util, `deleteAllTestLinodes`, in + * `support/api/linodes.ts`. + * + * @returns Promise that resolves when all test Linodes are deleted. + */ +const deleteTestLinodes = async () => { + const allLinodes = await depaginate((page) => + getLinodes({ page, page_size: PAGE_SIZE }) + ); + + const deletePromises = allLinodes + .filter((linode: Linode) => linode.label.startsWith(TEST_TAG_PREFIX)) + .map((linode: Linode) => deleteLinode(linode.id)); + + await Promise.all(deletePromises); +}; + +/** + * Deletes all test Firewalls on the test account. + * + * This is a re-implementation of an existing util, `deleteAllTestFirewalls`, in + * `support/api/firewalls.ts`. + * + * @returns Promise that resolves when all test Firewalls are deleted. + */ +const deleteTestFirewalls = async () => { + const allFirewalls = await depaginate((page) => + getFirewalls({ page, page_size: PAGE_SIZE }) + ); + + const deletePromises = allFirewalls + .filter((firewall: Firewall) => firewall.label.startsWith(TEST_TAG_PREFIX)) + .map((firewall: Firewall) => deleteFirewall(firewall.id)); + + await Promise.all(deletePromises); +}; + +/** + * Deletes all running test LKE clusters on the test account. + * + * Sometimes when attempting to delete provisioning LKE clusters, the cluster + * becomes stuck and requires manual intervention to resolve. To reduce the risk + * of this happening, this function will only delete clusters that have finished + * provisioning (i.e. all nodes have `'ready'` status) or which have existed + * for at least an hour. + * + * This is a re-implementation of an existing util, `deleteAllTestLkeClusters`, in + * `support/api/lke.ts`. + * + * @returns Promise that resolves when all test LKE clusters are deleted. + */ +const deleteTestLkeClusters = async () => { + const allClusters = await depaginate((page) => + getKubernetesClusters({ page, page_size: PAGE_SIZE }) + ); + + const clusterDeletionPromises = allClusters + .filter((cluster: KubernetesCluster) => + cluster.label.startsWith(TEST_TAG_PREFIX) + ) + .map(async (cluster: KubernetesCluster) => { + const clusterCreateTime = DateTime.fromISO(cluster.created, { + zone: 'utc', + }); + const createTimeElapsed = Math.abs( + clusterCreateTime.diffNow('minutes').minutes + ); + + // If the test cluster is older than 1 hour, delete it regardless of + // whether or not all of the Node Pools are ready; this is a safeguard + // to prevent LKE clusters with stuck pools from accumulating. + if (createTimeElapsed >= 60) { + return deleteKubernetesCluster(cluster.id); + } + + // If the cluster is not older than 1 hour, only delete it if all of its + // Node Pools are ready. + const pools = await depaginate((page: number) => + getNodePools(cluster.id, { page, page_size: PAGE_SIZE }) + ); + if (pools.every(isPoolReady)) { + return deleteKubernetesCluster(cluster.id); + } + return; + }); + + await Promise.all(clusterDeletionPromises); +}; + +/* + * Human-friendly string describing the types of resources being deleted, + * and their corresponding deletion function. + */ +const resourceCleanUpItems = [ + { name: 'Linodes', cleanUp: deleteTestLinodes }, + // TODO Remove LKE cluster clean up once M3-8656 is complete because cluster cleanup will no longer be necessary. + { name: 'LKE Clusters', cleanUp: deleteTestLkeClusters }, + { name: 'Firewalls', cleanUp: deleteTestFirewalls }, +]; + +export const postRunCleanup: CypressPlugin = async (on) => { + on('after:run', async () => { + console.log('Performing post-run clean up:\n'); + + for (const resourceCleanUpItem of resourceCleanUpItems) { + console.log(`- Cleaning up test ${resourceCleanUpItem.name}...`); + try { + // Perform clean-up sequentially. + // eslint-disable-next-line no-await-in-loop + await resourceCleanUpItem.cleanUp(); + } catch (e) { + console.error( + `\nAn error occurred while cleaning up test ${resourceCleanUpItem.name}:` + ); + if (e.message) { + console.error(e.message); + } + console.error(e); + } + } + console.log('\nPost-run clean up is complete'); + }); +}; From cf3613f20606829233f337e2528e59831d614db0 Mon Sep 17 00:00:00 2001 From: zaenab-akamai Date: Tue, 5 Nov 2024 01:46:46 +0530 Subject: [PATCH 43/66] upcoming: [M3-8772] - Add global borderRadiusTokens to theme and replace hardcoded values (#11169) * Added borderRadiusTokens * Switched to theme.tokens.* for border radius tokens * Added changeset: Add global border radius token to theme and replace harcoded values where borderRadius = 0 --- .../pr-11169-upcoming-features-1730713096724.md | 5 +++++ .../EnhancedNumberInput/EnhancedNumberInput.tsx | 2 +- .../src/components/EnhancedSelect/Select.styles.ts | 8 ++++---- .../manager/src/components/IconTextLink/IconTextLink.tsx | 2 +- packages/manager/src/components/Tag/Tag.styles.ts | 4 ++-- packages/manager/src/components/TagCell/TagCell.tsx | 2 +- .../StackScriptBase/StackScriptBase.styles.ts | 2 +- packages/ui/src/foundations/themes/index.ts | 3 +++ packages/ui/src/foundations/themes/light.ts | 2 ++ 9 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 packages/manager/.changeset/pr-11169-upcoming-features-1730713096724.md diff --git a/packages/manager/.changeset/pr-11169-upcoming-features-1730713096724.md b/packages/manager/.changeset/pr-11169-upcoming-features-1730713096724.md new file mode 100644 index 00000000000..e35ed345e8f --- /dev/null +++ b/packages/manager/.changeset/pr-11169-upcoming-features-1730713096724.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Add global border radius token to theme and replace harcoded values where borderRadius = 0 ([#11169](https://github.com/linode/manager/pull/11169)) diff --git a/packages/manager/src/components/EnhancedNumberInput/EnhancedNumberInput.tsx b/packages/manager/src/components/EnhancedNumberInput/EnhancedNumberInput.tsx index cc28261f750..f68c63b08a0 100644 --- a/packages/manager/src/components/EnhancedNumberInput/EnhancedNumberInput.tsx +++ b/packages/manager/src/components/EnhancedNumberInput/EnhancedNumberInput.tsx @@ -145,7 +145,7 @@ const StyledButton = styled(Button)(({ theme }) => ({ border: 'none', }, border: 'none', - borderRadius: 0, + borderRadius: theme.tokens.borderRadius.None, height: 34, minHeight: 'fit-content', minWidth: 30, diff --git a/packages/manager/src/components/EnhancedSelect/Select.styles.ts b/packages/manager/src/components/EnhancedSelect/Select.styles.ts index bbd0ba1b9c0..def55c13e51 100644 --- a/packages/manager/src/components/EnhancedSelect/Select.styles.ts +++ b/packages/manager/src/components/EnhancedSelect/Select.styles.ts @@ -94,7 +94,7 @@ export const useStyles = makeStyles()((theme: Theme) => ({ }, backgroundColor: theme.bg.white, border: `1px solid transparent`, - borderRadius: 0, + borderRadius: theme.tokens.borderRadius.None, boxShadow: 'none', minHeight: `calc(${theme.spacing(5)} - 2)`, }, @@ -122,7 +122,7 @@ export const useStyles = makeStyles()((theme: Theme) => ({ }, '& .react-select__menu': { border: `1px solid ${theme.palette.primary.main}`, - borderRadius: 0, + borderRadius: theme.tokens.borderRadius.None, boxShadow: 'none', margin: '-1px 0 0 0', maxWidth: 415, @@ -298,7 +298,7 @@ export const reactSelectStyles = (theme: Theme) => ({ }, backgroundColor: theme.bg.white, border: `1px solid transparent`, - borderRadius: 0, + borderRadius: theme.tokens.borderRadius.None, boxShadow: 'none', minHeight: `calc(${theme.spacing(5)} - 2)`, }), @@ -330,7 +330,7 @@ export const reactSelectStyles = (theme: Theme) => ({ menu: (base: any) => ({ ...base, border: `1px solid ${theme.palette.primary.main}`, - borderRadius: 0, + borderRadius: theme.tokens.borderRadius.None, boxShadow: 'none', left: -1, margin: 0, diff --git a/packages/manager/src/components/IconTextLink/IconTextLink.tsx b/packages/manager/src/components/IconTextLink/IconTextLink.tsx index 78a06edd4d8..8aac572d997 100644 --- a/packages/manager/src/components/IconTextLink/IconTextLink.tsx +++ b/packages/manager/src/components/IconTextLink/IconTextLink.tsx @@ -52,7 +52,7 @@ const useStyles = makeStyles()((theme: Theme) => ({ color: theme.palette.primary.light, }, alignItems: 'flex-start', - borderRadius: 0, + borderRadius: theme.tokens.borderRadius.None, cursor: 'pointer', display: 'flex', margin: `0 ${theme.spacing(1)} 2px 0`, diff --git a/packages/manager/src/components/Tag/Tag.styles.ts b/packages/manager/src/components/Tag/Tag.styles.ts index 1912ce37c5b..77a491cc8cb 100644 --- a/packages/manager/src/components/Tag/Tag.styles.ts +++ b/packages/manager/src/components/Tag/Tag.styles.ts @@ -75,7 +75,7 @@ export const StyledDeleteButton = styled(StyledLinkButton, { label: 'StyledDeleteButton', })(({ theme }) => ({ '& svg': { - borderRadius: 0, + borderRadius: theme.tokens.borderRadius.None, color: theme.color.tagIcon, height: 15, width: 15, @@ -94,7 +94,7 @@ export const StyledDeleteButton = styled(StyledLinkButton, { borderLeft: `1px solid ${ theme.name === 'light' ? theme.tokens.color.Neutrals.White : '#2e3238' }`, - borderRadius: 0, + borderRadius: theme.tokens.borderRadius.None, borderTopRightRadius: 3, height: 30, margin: 0, diff --git a/packages/manager/src/components/TagCell/TagCell.tsx b/packages/manager/src/components/TagCell/TagCell.tsx index b9f3dfbfe48..92d667b0f32 100644 --- a/packages/manager/src/components/TagCell/TagCell.tsx +++ b/packages/manager/src/components/TagCell/TagCell.tsx @@ -237,7 +237,7 @@ const StyledIconButton = styled(IconButton)(({ theme }) => ({ color: theme.tokens.color.Neutrals.White, }, backgroundColor: theme.color.tagButtonBg, - borderRadius: 0, + borderRadius: theme.tokens.borderRadius.None, color: theme.color.tagIcon, height: 30, marginLeft: theme.spacing(0.5), diff --git a/packages/manager/src/features/StackScripts/StackScriptBase/StackScriptBase.styles.ts b/packages/manager/src/features/StackScripts/StackScriptBase/StackScriptBase.styles.ts index 69dab3a75fc..0a93d7cccc2 100644 --- a/packages/manager/src/features/StackScripts/StackScriptBase/StackScriptBase.styles.ts +++ b/packages/manager/src/features/StackScripts/StackScriptBase/StackScriptBase.styles.ts @@ -55,7 +55,7 @@ export const StyledDebouncedSearchTextfield = styled(DebouncedSearchTextField, { '& .input': { backgroundColor: theme.bg.bgPaper, border: `1px solid ${theme.color.grey3}`, - borderRadius: 0, + borderRadius: theme.tokens.borderRadius.None, minHeight: 'auto', minWidth: 415, }, diff --git a/packages/ui/src/foundations/themes/index.ts b/packages/ui/src/foundations/themes/index.ts index 451870f6de2..d1f453303da 100644 --- a/packages/ui/src/foundations/themes/index.ts +++ b/packages/ui/src/foundations/themes/index.ts @@ -10,6 +10,7 @@ import type { ActionTypes as ActionTypesLight, BackgroundTypes as BackgroundTypesLight, BorderTypes as BorderTypesLight, + BorderRadiusTypes, ChartTypes, ColorTypes, ContentTypes as ContentTypesLight, @@ -107,6 +108,7 @@ declare module '@mui/material/styles/createTheme' { notificationToast: NotificationToast; tokens: { // ---- Global tokens: theme agnostic ---- + borderRadius: BorderRadiusTypes; color: ColorTypes; font: FontTypes; spacing: SpacingTypes; @@ -143,6 +145,7 @@ declare module '@mui/material/styles/createTheme' { notificationToast?: NotificationToast; tokens?: { // ---- Global tokens: theme agnostic ---- + borderRadius?: BorderRadiusTypes; color?: ColorTypes; font?: FontTypes; spacing?: SpacingTypes; diff --git a/packages/ui/src/foundations/themes/light.ts b/packages/ui/src/foundations/themes/light.ts index ad4b50a7d64..1bc3375a7ae 100644 --- a/packages/ui/src/foundations/themes/light.ts +++ b/packages/ui/src/foundations/themes/light.ts @@ -3,6 +3,7 @@ import { Action, Background, Border, + BorderRadius, Button, Chart, Color, @@ -1642,6 +1643,7 @@ export const lightTheme: ThemeOptions = { action: Action, background: Background, border: Border, + borderRadius: BorderRadius, color: Color, chart: Chart, content: Content, From 6de478133d0a9dd3ce5f5cfff0847c69db3c19df Mon Sep 17 00:00:00 2001 From: corya-akamai <136115382+corya-akamai@users.noreply.github.com> Date: Mon, 4 Nov 2024 15:53:35 -0500 Subject: [PATCH 44/66] feat: [UIE-8194] - DBaaS major and minor upgrades 3 (#11198) * feat: [UIE-8194] - DBaaS Upgrades and Maintenance 2 * feat: [UIE-8194] - DBaaS major and minor upgrades 3 --- ...r-11198-upcoming-features-1730465952580.md | 5 + .../DatabaseSettingsMaintenance.test.tsx | 145 +++++++++++ .../DatabaseSettingsMaintenance.tsx | 84 +++++++ ...tabaseSettingsReviewUpdatesDialog.test.tsx | 66 +++++ .../DatabaseSettingsReviewUpdatesDialog.tsx | 100 ++++++++ ...abaseSettingsUpgradeVersionDialog.test.tsx | 96 ++++++++ .../DatabaseSettingsUpgradeVersionDialog.tsx | 155 ++++++++++++ .../Databases/DatabaseEngineVersion.test.tsx | 225 ++++++++++++++++++ .../Databases/DatabaseEngineVersion.tsx | 65 +++++ .../src/features/Databases/utilities.test.ts | 128 +++++++++- .../src/features/Databases/utilities.ts | 41 +++- 11 files changed, 1098 insertions(+), 12 deletions(-) create mode 100644 packages/manager/.changeset/pr-11198-upcoming-features-1730465952580.md create mode 100644 packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsMaintenance.test.tsx create mode 100644 packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsMaintenance.tsx create mode 100644 packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsReviewUpdatesDialog.test.tsx create mode 100644 packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsReviewUpdatesDialog.tsx create mode 100644 packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsUpgradeVersionDialog.test.tsx create mode 100644 packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsUpgradeVersionDialog.tsx create mode 100644 packages/manager/src/features/Databases/DatabaseEngineVersion.test.tsx create mode 100644 packages/manager/src/features/Databases/DatabaseEngineVersion.tsx diff --git a/packages/manager/.changeset/pr-11198-upcoming-features-1730465952580.md b/packages/manager/.changeset/pr-11198-upcoming-features-1730465952580.md new file mode 100644 index 00000000000..cd7c2c8e48f --- /dev/null +++ b/packages/manager/.changeset/pr-11198-upcoming-features-1730465952580.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +DBaaS add new Maintenance component, Upgrade version dialog, Review udpates dialog ([#11198](https://github.com/linode/manager/pull/11198)) diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsMaintenance.test.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsMaintenance.test.tsx new file mode 100644 index 00000000000..2672c02b16d --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsMaintenance.test.tsx @@ -0,0 +1,145 @@ +import React from 'react'; + +import { databaseFactory } from 'src/factories'; +import { DatabaseSettingsMaintenance } from 'src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsMaintenance'; +import { renderWithTheme } from 'src/utilities/testHelpers'; + +import type { Engine } from '@linode/api-v4'; + +const UPGRADE_VERSION = 'Upgrade Version'; + +const queryMocks = vi.hoisted(() => ({ + useDatabaseEnginesQuery: vi.fn().mockReturnValue({ + data: [ + { + engine: 'mysql' as Engine, + id: 'mysql/8', + version: '8', + }, + { + engine: 'postgresql' as Engine, + id: 'postgresql/13', + version: '13', + }, + { + engine: 'postgresql' as Engine, + id: 'postgresql/14', + version: '14', + }, + { + engine: 'postgresql' as Engine, + id: 'postgresql/15', + version: '15', + }, + { + engine: 'postgresql' as Engine, + id: 'postgresql/16', + version: '16', + }, + ], + }), +})); + +vi.mock('src/queries/databases/databases', async () => { + const actual = await vi.importActual('src/queries/databases/databases'); + return { + ...actual, + useDatabaseEnginesQuery: queryMocks.useDatabaseEnginesQuery, + }; +}); + +describe('Database Settings Maintenance', () => { + it('should disable upgrade version modal button when there are no upgrades available', async () => { + const database = databaseFactory.build({ + engine: 'mysql', + version: '8.0.30', + }); + + const onReviewUpdates = vi.fn(); + const onUpgradeVersion = vi.fn(); + + const { findByRole } = renderWithTheme( + + ); + + const button = await findByRole('button', { name: UPGRADE_VERSION }); + + expect(button).toBeDisabled(); + }); + + it('should enable upgrade version modal button when there are upgrades available', async () => { + const database = databaseFactory.build({ + engine: 'postgresql', + version: '13', + }); + + const onReviewUpdates = vi.fn(); + const onUpgradeVersion = vi.fn(); + + const { findByRole } = renderWithTheme( + + ); + + const button = await findByRole('button', { name: UPGRADE_VERSION }); + + expect(button).toBeEnabled(); + }); + + it('should show review text and modal button when there are updates', async () => { + const database = databaseFactory.build(); + + const onReviewUpdates = vi.fn(); + const onUpgradeVersion = vi.fn(); + + const { findByRole } = renderWithTheme( + + ); + + const button = await findByRole('button', { name: UPGRADE_VERSION }); + + expect(button).toBeEnabled(); + }); + + it('should not show review text and modal button when there are no updates', async () => { + const database = databaseFactory.build({ + updates: { + pending: [], + }, + }); + + const onReviewUpdates = vi.fn(); + const onUpgradeVersion = vi.fn(); + + const { queryByRole } = renderWithTheme( + + ); + + const button = queryByRole('button', { name: 'Click to review' }); + + expect(button).not.toBeInTheDocument(); + }); +}); diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsMaintenance.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsMaintenance.tsx new file mode 100644 index 00000000000..a128e14dacf --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsMaintenance.tsx @@ -0,0 +1,84 @@ +import { Grid, styled } from '@mui/material'; +import * as React from 'react'; + +import { StyledLinkButton } from 'src/components/Button/StyledLinkButton'; +import { Typography } from 'src/components/Typography'; +import { + getDatabasesDescription, + hasPendingUpdates, + upgradableVersions, +} from 'src/features/Databases/utilities'; +import { useDatabaseEnginesQuery } from 'src/queries/databases/databases'; + +import type { Engine, PendingUpdates } from '@linode/api-v4'; + +interface Props { + databaseEngine: Engine; + databasePendingUpdates?: PendingUpdates[]; + databaseVersion: string; + onReviewUpdates: () => void; + onUpgradeVersion: () => void; +} + +export const DatabaseSettingsMaintenance = (props: Props) => { + const { + databaseEngine: engine, + databasePendingUpdates, + databaseVersion: version, + onReviewUpdates, + onUpgradeVersion, + } = props; + const engineVersion = getDatabasesDescription({ engine, version }); + const { data: engines } = useDatabaseEnginesQuery(true); + const versions = upgradableVersions(engine, version, engines); + const hasUpdates = hasPendingUpdates(databasePendingUpdates); + + return ( + + + Maintenance + Version + {engineVersion} + + Upgrade Version + + + + {/* + TODO Uncomment and provide value when the EOL is returned by the API. + Currently, it is not supported, however they are working on returning it since it has value to the end user + End of life + */} + + + Maintenance updates + {hasUpdates ? ( + + One or more minor version upgrades or patches will be applied during + the next maintenance window.{' '} + + Click to review + + + ) : ( + + There are no minor version upgrades or patches planned for the next + maintenance window.{' '} + + )} + + + ); +}; + +const StyledTypography = styled(Typography)(({ theme }) => ({ + marginBottom: theme.spacing(0.25), +})); + +const BoldTypography = styled(StyledTypography)(({ theme }) => ({ + fontFamily: theme.font.bold, +})); diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsReviewUpdatesDialog.test.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsReviewUpdatesDialog.test.tsx new file mode 100644 index 00000000000..7911e3feb0d --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsReviewUpdatesDialog.test.tsx @@ -0,0 +1,66 @@ +import React from 'react'; + +import { databaseFactory } from 'src/factories/databases'; +import { DatabaseSettingsReviewUpdatesDialog } from 'src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsReviewUpdatesDialog'; +import { renderWithTheme } from 'src/utilities/testHelpers'; + +describe('Database Settings Review Updates Dialog', () => { + it('should list updates', async () => { + const database = databaseFactory.build({ + updates: { + pending: [ + { + deadline: null, + description: 'Update a', + planned_for: '2044-09-15T17:15:12', + }, + { + deadline: null, + description: 'Update b', + planned_for: '2044-09-15T17:15:12', + }, + ], + }, + }); + + const onClose = vi.fn(); + + const { findByText } = renderWithTheme( + + ); + + const a = await findByText('Update a'); + const b = await findByText('Update b'); + + expect(a).toBeDefined(); + expect(b).toBeDefined(); + }); + + it('should enable buttons', async () => { + const database = databaseFactory.build(); + + const onClose = vi.fn(); + + const { findByTestId } = renderWithTheme( + + ); + + const start = await findByTestId('start'); + const close = await findByTestId('close'); + + expect(start).toBeEnabled(); + expect(close).toBeEnabled(); + }); +}); diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsReviewUpdatesDialog.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsReviewUpdatesDialog.tsx new file mode 100644 index 00000000000..e531dc963c7 --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsReviewUpdatesDialog.tsx @@ -0,0 +1,100 @@ +import { useTheme } from '@mui/material'; +import { useSnackbar } from 'notistack'; +import * as React from 'react'; + +import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; +import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog'; +import { Notice } from 'src/components/Notice/Notice'; +import { Typography } from 'src/components/Typography'; +import { usePatchDatabaseMutation } from 'src/queries/databases/databases'; +import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; + +import type { Engine, PendingUpdates } from '@linode/api-v4/lib/databases'; + +interface Props { + databaseEngine: Engine; + databaseID: number; + databasePendingUpdates?: PendingUpdates[]; + onClose: () => void; + open: boolean; +} + +export const DatabaseSettingsReviewUpdatesDialog = (props: Props) => { + const { + databaseEngine, + databaseID, + databasePendingUpdates, + onClose, + open, + } = props; + const theme = useTheme(); + const { enqueueSnackbar } = useSnackbar(); + const { mutateAsync: patchDatabase } = usePatchDatabaseMutation( + databaseEngine, + databaseID + ); + + const [error, setError] = React.useState(''); + const [loading, setIsLoading] = React.useState(false); + + const onStartMaintenance = () => { + setIsLoading(true); + patchDatabase() + .then(() => { + setIsLoading(false); + enqueueSnackbar('Database maintenance started successfully.', { + variant: 'success', + }); + onClose(); + }) + .catch((e) => { + setIsLoading(false); + setError( + getAPIErrorOrDefault(e, 'There was an error starting maintenance.')[0] + .reason + ); + }); + }; + + const renderActions = ( + + ); + + return ( + + {error && } + + During the maintenance there is a brief service interruption. + + {databasePendingUpdates?.length && ( +
    + {databasePendingUpdates.map((update) => ( +
  • {update.description}
  • + ))} +
+ )} +
+ ); +}; diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsUpgradeVersionDialog.test.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsUpgradeVersionDialog.test.tsx new file mode 100644 index 00000000000..f537685046b --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsUpgradeVersionDialog.test.tsx @@ -0,0 +1,96 @@ +import React from 'react'; + +import { databaseFactory } from 'src/factories/databases'; +import { renderWithTheme } from 'src/utilities/testHelpers'; + +import { DatabaseSettingsUpgradeVersionDialog } from './DatabaseSettingsUpgradeVersionDialog'; + +import type { Engine } from '@linode/api-v4'; + +const queryMocks = vi.hoisted(() => ({ + useDatabaseEnginesQuery: vi.fn().mockReturnValue({ + data: [ + { + engine: 'mysql' as Engine, + id: 'mysql/8', + version: '8', + }, + { + engine: 'postgresql' as Engine, + id: 'postgresql/13', + version: '13', + }, + { + engine: 'postgresql' as Engine, + id: 'postgresql/14', + version: '14', + }, + { + engine: 'postgresql' as Engine, + id: 'postgresql/15', + version: '15', + }, + { + engine: 'postgresql' as Engine, + id: 'postgresql/16', + version: '16', + }, + ], + }), +})); + +vi.mock('src/queries/databases/databases', async () => { + const actual = await vi.importActual('src/queries/databases/databases'); + return { + ...actual, + useDatabaseEnginesQuery: queryMocks.useDatabaseEnginesQuery, + }; +}); + +describe('Database Settings Upgrade Version Dialog', () => { + it('should display warning', async () => { + const database = databaseFactory.build(); + + const onClose = vi.fn(); + + const { findByText } = renderWithTheme( + + ); + + const warning = await findByText( + 'Reverting back to the prior version is not possible once the upgrade has been started' + ); + + expect(warning).toBeInTheDocument(); + }); + + it('should disable upgrade button when no selectedVersion', async () => { + const database = databaseFactory.build(); + + const onClose = vi.fn(); + + const { findByTestId } = renderWithTheme( + + ); + + const upgrade = await findByTestId('upgrade'); + const cancel = await findByTestId('cancel'); + + expect(upgrade).toBeDisabled(); + expect(cancel).toBeEnabled(); + }); +}); diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsUpgradeVersionDialog.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsUpgradeVersionDialog.tsx new file mode 100644 index 00000000000..f42d1777e40 --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsUpgradeVersionDialog.tsx @@ -0,0 +1,155 @@ +import { FormControl } from '@linode/ui'; +import { useTheme } from '@mui/material'; +import { useSnackbar } from 'notistack'; +import * as React from 'react'; + +import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; +import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; +import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog'; +import { Notice } from 'src/components/Notice/Notice'; +import { Typography } from 'src/components/Typography'; +import { + DATABASE_ENGINE_MAP, + upgradableVersions, +} from 'src/features/Databases/utilities'; +import { + useDatabaseEnginesQuery, + useDatabaseMutation, +} from 'src/queries/databases/databases'; +import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; + +import type { Engine } from '@linode/api-v4/lib/databases'; + +interface Props { + databaseEngine: Engine; + databaseID: number; + databaseLabel: string; + databaseVersion: string; + onClose: () => void; + open: boolean; +} + +interface VersionOption { + label: string; + value: string; +} + +export const DatabaseSettingsUpgradeVersionDialog = (props: Props) => { + const { + databaseEngine, + databaseID, + databaseLabel, + databaseVersion, + onClose, + open, + } = props; + const theme = useTheme(); + const { enqueueSnackbar } = useSnackbar(); + const { mutateAsync: updateDatabase } = useDatabaseMutation( + databaseEngine, + databaseID + ); + const { data: engines } = useDatabaseEnginesQuery(true); + + const versions = upgradableVersions( + databaseEngine, + databaseVersion, + engines + )?.map((engine) => { + return { + label: `v${engine.version}`, + value: engine.version, + }; + }); + + const [ + selectedVersion, + setSelectedVersion, + ] = React.useState(null); + const [error, setError] = React.useState(''); + const [loading, setIsLoading] = React.useState(false); + + const dialogTitle = `${DATABASE_ENGINE_MAP[databaseEngine]} on ${databaseLabel}`; + const defaultError = 'There was an error upgrading this version.'; + + const onUpgradeVersion = () => { + if (!selectedVersion) { + return; + } + setIsLoading(true); + updateDatabase({ version: selectedVersion.value }) + .then(() => { + setIsLoading(false); + enqueueSnackbar('Database version upgraded successfully.', { + variant: 'success', + }); + onClose(); + }) + .catch((e) => { + setIsLoading(false); + setError(getAPIErrorOrDefault(e, defaultError)[0].reason); + }); + }; + + const renderActions = ( + + ); + + return ( + + {error && } + + Current Version: v{databaseVersion} + + + Please select the new MySQL version. Once you select the new version we + will check it for compatibility with your current version. If it is + compatible you can proceed with the upgrade. + + + + setSelectedVersion(v)} + options={versions ?? []} + placeholder="Select a version" + value={selectedVersion} + /> + + + {loading && ( + + + Checking version upgrade compatibility, then will start upgrade + + {/* Then the text changes to "Starting to upgrade." then closes after 1 second */} + + )} + + + Reverting back to the prior version is not possible once the upgrade + has been started + + + + ); +}; diff --git a/packages/manager/src/features/Databases/DatabaseEngineVersion.test.tsx b/packages/manager/src/features/Databases/DatabaseEngineVersion.test.tsx new file mode 100644 index 00000000000..4af82ffefa6 --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseEngineVersion.test.tsx @@ -0,0 +1,225 @@ +import { waitFor } from '@testing-library/react'; +import React from 'react'; + +import { databaseFactory } from 'src/factories/databases'; +import { renderWithTheme } from 'src/utilities/testHelpers'; + +import { DatabaseEngineVersion } from './DatabaseEngineVersion'; +import * as utils from './utilities'; + +const v1 = () => { + return { + isDatabasesEnabled: true, + isDatabasesV1Enabled: true, + isDatabasesV2Beta: false, + isDatabasesV2Enabled: false, + isDatabasesV2GA: false, + isUserExistingBeta: false, + isUserNewBeta: false, + }; +}; + +const v2Beta = () => { + return { + isDatabasesEnabled: true, + isDatabasesV1Enabled: true, + isDatabasesV2Beta: true, + isDatabasesV2Enabled: true, + isDatabasesV2GA: false, + isUserExistingBeta: false, + isUserNewBeta: true, + }; +}; + +const v2GA = () => ({ + isDatabasesEnabled: true, + isDatabasesV1Enabled: true, + isDatabasesV2Beta: false, + isDatabasesV2Enabled: true, + isDatabasesV2GA: true, + isUserExistingBeta: false, + isUserNewBeta: false, +}); + +const spy = vi.spyOn(utils, 'useIsDatabasesEnabled'); +spy.mockReturnValue(v2GA()); + +describe('Database Engine Version', () => { + it('should render V1 view legacy db', async () => { + spy.mockReturnValue(v1()); + + const database = databaseFactory.build({ + engine: 'postgresql', + platform: 'rdbms-legacy', + version: '14.6', + }); + + const { queryAllByText, queryByTestId } = renderWithTheme( + + ); + + await waitFor(async () => { + expect(queryAllByText('PostgreSQL v14.6')).toHaveLength(1); + expect(queryByTestId('maintenance-link')).toBeNull(); + }); + }); + + it('should render V2 beta view legacy db', async () => { + spy.mockReturnValue(v2Beta()); + + const database = databaseFactory.build({ + engine: 'postgresql', + platform: 'rdbms-legacy', + version: '14.6', + }); + + const { queryAllByText, queryByTestId } = renderWithTheme( + + ); + + await waitFor(async () => { + expect(queryAllByText('PostgreSQL v14.6')).toHaveLength(1); + expect(queryByTestId('maintenance-link')).toBeNull(); + }); + }); + + it('should render V2 beta view default db without pendingUpdates', async () => { + spy.mockReturnValue(v2Beta()); + + const database = databaseFactory.build({ + engine: 'postgresql', + platform: 'rdbms-default', + updates: { + pending: [], + }, + version: '14.6', + }); + + const { queryAllByText, queryByTestId } = renderWithTheme( + + ); + + await waitFor(async () => { + expect(queryAllByText('PostgreSQL v14.6')).toHaveLength(1); + expect(queryByTestId('maintenance-link')).toBeNull(); + }); + }); + + it('should render V2 beta view default db with pendingUpdates', async () => { + spy.mockReturnValue(v2Beta()); + + const database = databaseFactory.build({ + engine: 'postgresql', + platform: 'rdbms-default', + version: '14.6', + }); + + const { queryAllByText, queryByTestId } = renderWithTheme( + + ); + + await waitFor(async () => { + expect(queryAllByText('PostgreSQL v14.6')).toHaveLength(1); + expect(queryByTestId('maintenance-link')).toBeNull(); + }); + }); + + it('should render V2 GA view legacy db', async () => { + spy.mockReturnValue(v2GA()); + + const database = databaseFactory.build({ + engine: 'postgresql', + platform: 'rdbms-legacy', + version: '14.6', + }); + + const { queryAllByText, queryByTestId } = renderWithTheme( + + ); + + await waitFor(async () => { + expect(queryAllByText('PostgreSQL v14.6')).toHaveLength(1); + expect(queryByTestId('maintenance-link')).toBeNull(); + }); + }); + + it('should render V2 GA view default db without pendingUpdates', async () => { + spy.mockReturnValue(v2GA()); + + const database = databaseFactory.build({ + engine: 'mysql', + platform: 'rdbms-default', + updates: { + pending: [], + }, + version: '8.0.30', + }); + + const { queryAllByText, queryByTestId } = renderWithTheme( + + ); + + await waitFor(async () => { + expect(queryAllByText('MySQL v8.0.30')).toHaveLength(1); + expect(queryByTestId('maintenance-link')).toBeNull(); + }); + }); + + it('should render V2 GA view default db with pendingUpdates', async () => { + spy.mockReturnValue(v2GA()); + + const database = databaseFactory.build({ + engine: 'postgresql', + platform: 'rdbms-default', + version: '14.6', + }); + + const { queryAllByText, queryByTestId } = renderWithTheme( + + ); + + await waitFor(async () => { + expect(queryAllByText('PostgreSQL v14.6')).toHaveLength(1); + expect(queryByTestId('maintenance-link')).toBeInTheDocument(); + }); + }); +}); diff --git a/packages/manager/src/features/Databases/DatabaseEngineVersion.tsx b/packages/manager/src/features/Databases/DatabaseEngineVersion.tsx new file mode 100644 index 00000000000..5e613a7e591 --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseEngineVersion.tsx @@ -0,0 +1,65 @@ +import { styled } from '@mui/material'; +import React from 'react'; +import { Link } from 'react-router-dom'; + +import { StatusIcon } from 'src/components/StatusIcon/StatusIcon'; + +import { + getDatabasesDescription, + hasPendingUpdates, + isDefaultDatabase, + useIsDatabasesEnabled, +} from './utilities'; + +import type { + Engine, + PendingUpdates, + Platform, +} from '@linode/api-v4/lib/databases'; + +interface Props { + databaseEngine: Engine; + databaseID: number; + databasePendingUpdates?: PendingUpdates[]; + databasePlatform?: Platform; + databaseVersion: string; +} + +export const DatabaseEngineVersion = (props: Props) => { + const { + databaseEngine: engine, + databaseID, + databasePendingUpdates, + databasePlatform: platform, + databaseVersion: version, + } = props; + const engineVersion = getDatabasesDescription({ engine, version }); + + const { isDatabasesV2GA } = useIsDatabasesEnabled(); + const isDefaultGA = isDatabasesV2GA && isDefaultDatabase({ platform }); + const hasUpdates = hasPendingUpdates(databasePendingUpdates); + + return ( + <> + {engineVersion} + {isDefaultGA && hasUpdates && ( + + + + )} + + ); +}; + +const StyledLink = styled(Link)(({ theme }) => ({ + alignItems: 'center', + display: 'inline-flex', + marginLeft: theme.spacing(0.5), +})); diff --git a/packages/manager/src/features/Databases/utilities.test.ts b/packages/manager/src/features/Databases/utilities.test.ts index 6960014fda6..3d2e93a6c07 100644 --- a/packages/manager/src/features/Databases/utilities.test.ts +++ b/packages/manager/src/features/Databases/utilities.test.ts @@ -1,18 +1,27 @@ import { renderHook, waitFor } from '@testing-library/react'; import { DateTime } from 'luxon'; -import { AccountCapability } from '@linode/api-v4'; -import { accountFactory, databaseTypeFactory } from 'src/factories'; -import { TimeOption } from 'src/features/Databases/DatabaseDetail/DatabaseBackups/DatabaseBackups'; import { + accountFactory, + databaseFactory, + databaseTypeFactory, +} from 'src/factories'; +import { + getDatabasesDescription, isDateOutsideBackup, + isDefaultDatabase, + isLegacyDatabase, isTimeOutsideBackup, toISOString, + upgradableVersions, useIsDatabasesEnabled, } from 'src/features/Databases/utilities'; import { HttpResponse, http, server } from 'src/mocks/testServer'; import { wrapWithTheme } from 'src/utilities/testHelpers'; +import type { AccountCapability, Database, Engine } from '@linode/api-v4'; +import type { TimeOption } from 'src/features/Databases/DatabaseDetail/DatabaseBackups/DatabaseBackups'; + const setup = (capabilities: AccountCapability[], flags: any) => { const account = accountFactory.build({ capabilities }); @@ -376,7 +385,7 @@ describe('isTimeOutsideBackup', () => { describe('toISOString', () => { it('should convert a date and time to ISO string format', () => { - const selectedDate = DateTime.fromObject({ year: 2023, month: 5, day: 15 }); + const selectedDate = DateTime.fromObject({ day: 15, month: 5, year: 2023 }); const selectedTime: TimeOption = { label: '02:00', value: 14 }; const result = toISOString(selectedDate, selectedTime.value); expect(result).toContain('2023-05-15T14:00'); @@ -384,9 +393,9 @@ describe('toISOString', () => { it('should handle midnight correctly', () => { const selectedDate = DateTime.fromObject({ - year: 2023, - month: 12, day: 31, + month: 12, + year: 2023, }); const selectedTime: TimeOption = { label: '12:00 AM', value: 0 }; const result = toISOString(selectedDate, selectedTime.value); @@ -394,9 +403,114 @@ describe('toISOString', () => { }); it('should handle noon correctly', () => { - const selectedDate = DateTime.fromObject({ year: 2024, month: 1, day: 1 }); + const selectedDate = DateTime.fromObject({ day: 1, month: 1, year: 2024 }); const selectedTime: TimeOption = { label: '12:00 PM', value: 12 }; const result = toISOString(selectedDate, selectedTime.value); expect(result).toContain('2024-01-01T12:00'); }); }); + +describe('getDatabasesDescription', () => { + it('should return MySQL', () => { + const result = getDatabasesDescription({ + engine: 'mysql', + version: '8.0.30', + }); + expect(result).toEqual('MySQL v8.0.30'); + }); + + it('should return PostgreSQL', () => { + const db: Database = databaseFactory.build({ + engine: 'postgresql', + version: '14.13', + }); + const result = getDatabasesDescription(db); + expect(result).toEqual('PostgreSQL v14.13'); + }); +}); + +describe('isDefaultDatabase', () => { + it('should return true for default platform database', () => { + const db: Database = databaseFactory.build({ + platform: 'rdbms-default', + }); + const result = isDefaultDatabase(db); + expect(result).toBe(true); + }); + + it('should return false for legacy platform database', () => { + const db: Database = databaseFactory.build({ + platform: 'rdbms-legacy', + }); + const result = isDefaultDatabase(db); + expect(result).toBe(false); + expect(isDefaultDatabase({ platform: undefined })).toBe(false); + }); +}); + +describe('isLegacyDatabase', () => { + it('should return true for legacy databases', () => { + expect(isLegacyDatabase({ platform: 'rdbms-legacy' })).toBe(true); + }); + + it('should return true fro undefined platform', () => { + expect(isLegacyDatabase({ platform: undefined })).toBe(true); + }); + + it('should return false for non-legacy databases', () => { + expect(isLegacyDatabase({ platform: 'rdbms-default' })).toBe(false); + }); +}); + +describe('upgradableVersions', () => { + const mockEngines = [ + { + engine: 'mysql' as Engine, + id: 'mysql/8', + version: '8', + }, + { + engine: 'postgresql' as Engine, + id: 'postgresql/13', + version: '13', + }, + { + engine: 'postgresql' as Engine, + id: 'postgresql/14', + version: '14', + }, + { + engine: 'postgresql' as Engine, + id: 'postgresql/15', + version: '15', + }, + { + engine: 'postgresql' as Engine, + id: 'postgresql/16', + version: '16', + }, + ]; + + it('should return engines with higher versions for the same engine type', () => { + const result = upgradableVersions('postgresql', '14.0.26', mockEngines); + expect(result).toHaveLength(2); + expect(result![0].version).toBe('15'); + }); + + it('should return empty array when no upgrades are available', () => { + const result = upgradableVersions('mysql', '8.0.30', mockEngines); + expect(result).toHaveLength(0); + }); + + it('should only return engines of the same type', () => { + const result = upgradableVersions('postgresql', '14.13.0', mockEngines); + expect(result?.every((engine) => engine.engine === 'postgresql')).toBe( + true + ); + }); + + it('should return undefined when no engines are provided', () => { + const result = upgradableVersions('mysql', '8.0.26', undefined); + expect(result).toBeUndefined(); + }); +}); diff --git a/packages/manager/src/features/Databases/utilities.ts b/packages/manager/src/features/Databases/utilities.ts index 2d3179964c0..4736d94b4fa 100644 --- a/packages/manager/src/features/Databases/utilities.ts +++ b/packages/manager/src/features/Databases/utilities.ts @@ -5,9 +5,12 @@ import { useAccount } from 'src/queries/account/account'; import { useDatabaseTypesQuery } from 'src/queries/databases/databases'; import { isFeatureEnabledV2 } from 'src/utilities/accountCapabilities'; -import { databaseEngineMap } from './DatabaseLanding/DatabaseRow'; - -import type { DatabaseInstance } from '@linode/api-v4'; +import type { + DatabaseEngine, + DatabaseInstance, + Engine, + PendingUpdates, +} from '@linode/api-v4'; import type { DatabaseFork } from '@linode/api-v4'; export interface IsDatabasesEnabled { @@ -213,6 +216,34 @@ export const toDatabaseFork = ( return fork; }; -export const getDatabasesDescription = (database: DatabaseInstance) => { - return `${databaseEngineMap[database.engine]} v${database.version}`; +export const DATABASE_ENGINE_MAP: Record = { + mongodb: 'MongoDB', + mysql: 'MySQL', + postgresql: 'PostgreSQL', + redis: 'Redis', +} as const; + +export const getDatabasesDescription = ( + database: Pick +) => { + return `${DATABASE_ENGINE_MAP[database.engine]} v${database.version}`; }; + +export const hasPendingUpdates = (pendingUpdates?: PendingUpdates[]) => + Boolean( + pendingUpdates?.some((update) => update.deadline || update.planned_for) + ); + +export const isDefaultDatabase = ( + database: Pick +) => database.platform === 'rdbms-default'; + +export const isLegacyDatabase = ( + database: Pick +) => !database.platform || database.platform === 'rdbms-legacy'; + +export const upgradableVersions = ( + engine: Engine, + version: string, + engines?: Pick[] +) => engines?.filter((e) => e.engine === engine && e.version > version); From 8c3c7fdd82ca1f4d1bab2604002bd10136b4b959 Mon Sep 17 00:00:00 2001 From: Connie Liu <139280159+coliu-akamai@users.noreply.github.com> Date: Mon, 4 Nov 2024 16:17:12 -0500 Subject: [PATCH 45/66] refactor: [M3-8814] - Clean up SubnetCreateDrawer and fix animation for VPC subnet drawers (#11195) * new subnet node? * subnet node - i think I like this version more? * update spacing of bottom margins for notices * switch to remove two setValues * Added changeset: Convert from `formik` to `react-hook-form` for `SubnetCreateDrawer` * Update packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx Co-authored-by: Banks Nussman <115251059+bnussman-akamai@users.noreply.github.com> * remove unnecessary grid + update variable name * fix animation for VPC subnet drawers * Added changeset: Animation for VPC subnet drawers * fix cypress tests --------- Co-authored-by: Banks Nussman <115251059+bnussman-akamai@users.noreply.github.com> --- .../pr-11195-fixed-1730745532480.md | 5 + .../pr-11195-tech-stories-1730487840259.md | 5 + .../e2e/core/vpc/vpc-linodes-update.spec.ts | 10 +- .../VPCs/VPCCreate/MultipleSubnetInput.tsx | 3 +- .../VPCs/VPCDetail/SubnetCreateDrawer.tsx | 149 +++++++++++------- .../VPCs/VPCDetail/VPCSubnetsTable.tsx | 94 +++++------ 6 files changed, 147 insertions(+), 119 deletions(-) create mode 100644 packages/manager/.changeset/pr-11195-fixed-1730745532480.md create mode 100644 packages/manager/.changeset/pr-11195-tech-stories-1730487840259.md diff --git a/packages/manager/.changeset/pr-11195-fixed-1730745532480.md b/packages/manager/.changeset/pr-11195-fixed-1730745532480.md new file mode 100644 index 00000000000..9a7e1e0ad9f --- /dev/null +++ b/packages/manager/.changeset/pr-11195-fixed-1730745532480.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +Animation for VPC subnet drawers ([#11195](https://github.com/linode/manager/pull/11195)) diff --git a/packages/manager/.changeset/pr-11195-tech-stories-1730487840259.md b/packages/manager/.changeset/pr-11195-tech-stories-1730487840259.md new file mode 100644 index 00000000000..6f78202aaf5 --- /dev/null +++ b/packages/manager/.changeset/pr-11195-tech-stories-1730487840259.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tech Stories +--- + +Convert from `formik` to `react-hook-form` for `SubnetCreateDrawer` ([#11195](https://github.com/linode/manager/pull/11195)) diff --git a/packages/manager/cypress/e2e/core/vpc/vpc-linodes-update.spec.ts b/packages/manager/cypress/e2e/core/vpc/vpc-linodes-update.spec.ts index a4ac2977d21..aeada2e14d3 100644 --- a/packages/manager/cypress/e2e/core/vpc/vpc-linodes-update.spec.ts +++ b/packages/manager/cypress/e2e/core/vpc/vpc-linodes-update.spec.ts @@ -81,6 +81,7 @@ describe('VPC assign/unassign flows', () => { mockGetVPC(mockVPC).as('getVPC'); mockGetSubnets(mockVPC.id, []).as('getSubnets'); mockCreateSubnet(mockVPC.id).as('createSubnet'); + mockGetLinodes([mockLinode]).as('getLinodes'); cy.visitWithLogin(`/vpcs/${mockVPC.id}`); cy.wait(['@getVPC', '@getSubnets']); @@ -110,7 +111,7 @@ describe('VPC assign/unassign flows', () => { .click(); }); - cy.wait(['@createSubnet', '@getVPC', '@getSubnets']); + cy.wait(['@createSubnet', '@getVPC', '@getSubnets', '@getLinodes']); // confirm that newly created subnet should now appear on VPC's detail page cy.findByText(mockVPC.label).should('be.visible'); @@ -123,12 +124,10 @@ describe('VPC assign/unassign flows', () => { .should('be.visible') .click(); - mockGetLinodes([mockLinode]).as('getLinodes'); ui.actionMenuItem .findByTitle('Assign Linodes') .should('be.visible') .click(); - cy.wait('@getLinodes'); ui.drawer .findByTitle(`Assign Linodes to subnet: ${mockSubnet.label} (0.0.0.0/0)`) @@ -224,9 +223,10 @@ describe('VPC assign/unassign flows', () => { mockGetVPCs(mockVPCs).as('getVPCs'); mockGetVPC(mockVPC).as('getVPC'); mockGetSubnets(mockVPC.id, [mockSubnet]).as('getSubnets'); + mockGetLinodes([mockLinode, mockSecondLinode]).as('getLinodes'); cy.visitWithLogin(`/vpcs/${mockVPC.id}`); - cy.wait(['@getVPC', '@getSubnets']); + cy.wait(['@getVPC', '@getSubnets', '@getLinodes']); // confirm that subnet should get displayed on VPC's detail page cy.findByText(mockVPC.label).should('be.visible'); @@ -239,12 +239,10 @@ describe('VPC assign/unassign flows', () => { .should('be.visible') .click(); - mockGetLinodes([mockLinode, mockSecondLinode]).as('getLinodes'); ui.actionMenuItem .findByTitle('Unassign Linodes') .should('be.visible') .click(); - cy.wait('@getLinodes'); ui.drawer .findByTitle( diff --git a/packages/manager/src/features/VPCs/VPCCreate/MultipleSubnetInput.tsx b/packages/manager/src/features/VPCs/VPCCreate/MultipleSubnetInput.tsx index 23c3f1b42eb..7c5dc9d88e5 100644 --- a/packages/manager/src/features/VPCs/VPCCreate/MultipleSubnetInput.tsx +++ b/packages/manager/src/features/VPCs/VPCCreate/MultipleSubnetInput.tsx @@ -5,12 +5,13 @@ import { Button } from 'src/components/Button/Button'; import { Divider } from 'src/components/Divider'; import { DEFAULT_SUBNET_IPV4_VALUE, - SubnetFieldState, getRecommendedSubnetIPv4, } from 'src/utilities/subnets'; import { SubnetNode } from './SubnetNode'; +import type { SubnetFieldState } from 'src/utilities/subnets'; + interface Props { disabled?: boolean; isDrawer?: boolean; diff --git a/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx b/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx index 0eaf06dca68..d3d1326a69a 100644 --- a/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx +++ b/packages/manager/src/features/VPCs/VPCDetail/SubnetCreateDrawer.tsx @@ -1,21 +1,24 @@ +import { yupResolver } from '@hookform/resolvers/yup'; +import { FormHelperText } from '@linode/ui'; import { createSubnetSchema } from '@linode/validation'; -import { useFormik } from 'formik'; import * as React from 'react'; +import { Controller, useForm } from 'react-hook-form'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Drawer } from 'src/components/Drawer'; import { Notice } from 'src/components/Notice/Notice'; +import { Stack } from 'src/components/Stack'; +import { TextField } from 'src/components/TextField'; import { useGrants, useProfile } from 'src/queries/profile/profile'; import { useCreateSubnetMutation, useVPCQuery } from 'src/queries/vpcs/vpcs'; -import { getErrorMap } from 'src/utilities/errorUtils'; import { DEFAULT_SUBNET_IPV4_VALUE, + RESERVED_IP_NUMBER, + calculateAvailableIPv4sRFC1918, getRecommendedSubnetIPv4, } from 'src/utilities/subnets'; -import { SubnetNode } from '../VPCCreate/SubnetNode'; - -import type { SubnetFieldState } from 'src/utilities/subnets'; +import type { CreateSubnetPayload } from '@linode/api-v4'; interface Props { onClose: () => void; @@ -37,86 +40,114 @@ export const SubnetCreateDrawer = (props: Props) => { vpc?.subnets?.map((subnet) => subnet.ipv4 ?? '') ?? [] ); - const [errorMap, setErrorMap] = React.useState< - Record - >({}); - const { isPending, mutateAsync: createSubnet, - reset, + reset: resetRequest, } = useCreateSubnetMutation(vpcId); - const onCreateSubnet = async () => { + const { + control, + formState: { errors, isDirty, isSubmitting }, + handleSubmit, + reset: resetForm, + setError, + watch, + } = useForm({ + defaultValues: { + ipv4: recommendedIPv4, + label: '', + }, + mode: 'onBlur', + resolver: yupResolver(createSubnetSchema), + }); + + const ipv4 = watch('ipv4'); + const numberOfAvailableIPs = calculateAvailableIPv4sRFC1918(ipv4 ?? ''); + + const onCreateSubnet = async (values: CreateSubnetPayload) => { try { - await createSubnet({ ipv4: values.ip.ipv4, label: values.label }); + await createSubnet(values); onClose(); } catch (errors) { - const newErrors = getErrorMap(['label', 'ipv4'], errors); - setErrorMap(newErrors); - setValues({ - ip: { - ...values.ip, - ipv4Error: newErrors.ipv4, - }, - label: values.label, - labelError: newErrors.label, - }); + for (const error of errors) { + setError(error?.field ?? 'root', { message: error.reason }); + } } }; - const { dirty, handleSubmit, resetForm, setValues, values } = useFormik({ - enableReinitialize: true, - initialValues: { - ip: { - availIPv4s: 256, - ipv4: recommendedIPv4, - }, - // @TODO VPC: add IPv6 when that is supported - label: '', - } as SubnetFieldState, - onSubmit: onCreateSubnet, - validateOnBlur: false, - validateOnChange: false, - validationSchema: createSubnetSchema, - }); - - React.useEffect(() => { - if (open) { - resetForm(); - reset(); - setErrorMap({}); - } - }, [open, reset, resetForm]); - return ( - - {errorMap.none && } + { + resetForm(); + resetRequest(); + }} + onClose={onClose} + open={open} + title={'Create Subnet'} + > + {errors.root?.message && ( + + )} {userCannotAddSubnet && ( )} -
- { - setValues(subnetState); - }} - disabled={userCannotAddSubnet} - subnet={values} - /> + + + ( + + )} + control={control} + name="label" + /> + ( + + )} + control={control} + name="ipv4" + /> + {numberOfAvailableIPs && ( + + Number of Available IP Addresses:{' '} + {numberOfAvailableIPs > RESERVED_IP_NUMBER + ? (numberOfAvailableIPs - RESERVED_IP_NUMBER).toLocaleString() + : 0} + + )} + { Create Subnet - {subnetCreateDrawerOpen && ( - setSubnetCreateDrawerOpen(false)} - open={subnetCreateDrawerOpen} - vpcId={vpcId} - /> - )} + setSubnetCreateDrawerOpen(false)} + open={subnetCreateDrawerOpen} + vpcId={vpcId} + /> @@ -318,52 +316,42 @@ export const VPCSubnetsTable = (props: Props) => { page={pagination.page} pageSize={pagination.pageSize} /> - {subnetUnassignLinodesDrawerOpen && ( - { - setSubnetUnassignLinodesDrawerOpen(false); - setSelectedLinode(undefined); - }} - open={subnetUnassignLinodesDrawerOpen} - singleLinodeToBeUnassigned={selectedLinode} - subnet={selectedSubnet} - vpcId={vpcId} - /> - )} - {subnetAssignLinodesDrawerOpen && ( - setSubnetAssignLinodesDrawerOpen(false)} - open={subnetAssignLinodesDrawerOpen} - subnet={selectedSubnet} - vpcId={vpcId} - vpcRegion={vpcRegion} - /> - )} - {deleteSubnetDialogOpen && ( - setDeleteSubnetDialogOpen(false)} - open={deleteSubnetDialogOpen} - subnet={selectedSubnet} - vpcId={vpcId} - /> - )} - {editSubnetsDrawerOpen && ( - setEditSubnetsDrawerOpen(false)} - open={editSubnetsDrawerOpen} - subnet={selectedSubnet} - vpcId={vpcId} - /> - )} - {powerActionDialogOpen && ( - setPowerActionDialogOpen(false)} - /> - )} + { + setSubnetUnassignLinodesDrawerOpen(false); + setSelectedLinode(undefined); + }} + open={subnetUnassignLinodesDrawerOpen} + singleLinodeToBeUnassigned={selectedLinode} + subnet={selectedSubnet} + vpcId={vpcId} + /> + setSubnetAssignLinodesDrawerOpen(false)} + open={subnetAssignLinodesDrawerOpen} + subnet={selectedSubnet} + vpcId={vpcId} + vpcRegion={vpcRegion} + /> + setDeleteSubnetDialogOpen(false)} + open={deleteSubnetDialogOpen} + subnet={selectedSubnet} + vpcId={vpcId} + /> + setEditSubnetsDrawerOpen(false)} + open={editSubnetsDrawerOpen} + subnet={selectedSubnet} + vpcId={vpcId} + /> + setPowerActionDialogOpen(false)} + /> ); }; From d60510e70510f738a0a32a0fa8d300fabb4052a1 Mon Sep 17 00:00:00 2001 From: mpolotsk-akamai <157619599+mpolotsk-akamai@users.noreply.github.com> Date: Mon, 4 Nov 2024 23:03:06 +0100 Subject: [PATCH 46/66] feat: [UIE-8082] - DBaaS GA Database Create: Summary section, refactoring (#11193) * feat: [UIE-8082] - DBaaS GA Database Create Summary section, refactoring * Added changeset: Added Summary Section for Database Create GA * change: [UIE-8082] - DBaaS GA Database Create GA review fix * Added changeset: Summary Section for Database Create GA --- .../pr-11193-added-1730738415614.md | 5 + .../components/PrimaryNav/PrimaryNav.test.tsx | 2 +- .../DatabaseCreate/DatabaseClusterData.tsx | 126 +++++ .../DatabaseCreate/DatabaseCreate.style.ts | 92 ++++ .../DatabaseCreate/DatabaseCreate.tsx | 519 ++++-------------- .../DatabaseNodeSelector.test.tsx | 45 ++ .../DatabaseCreate/DatabaseNodeSelector.tsx | 209 +++++++ .../DatabaseSummarySection.test.tsx | 68 +++ .../DatabaseCreate/DatabaseSummarySection.tsx | 136 +++++ .../Databases/DatabaseCreate/utilities.tsx | 115 ++++ .../DatabaseResize/DatabaseResize.tsx | 2 +- .../DatabaseSummary/DatabaseSummary.test.tsx | 4 - .../src/features/Databases/utilities.test.ts | 12 +- .../src/features/Databases/utilities.ts | 46 +- 14 files changed, 920 insertions(+), 461 deletions(-) create mode 100644 packages/manager/.changeset/pr-11193-added-1730738415614.md create mode 100644 packages/manager/src/features/Databases/DatabaseCreate/DatabaseClusterData.tsx create mode 100644 packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.style.ts create mode 100644 packages/manager/src/features/Databases/DatabaseCreate/DatabaseNodeSelector.test.tsx create mode 100644 packages/manager/src/features/Databases/DatabaseCreate/DatabaseNodeSelector.tsx create mode 100644 packages/manager/src/features/Databases/DatabaseCreate/DatabaseSummarySection.test.tsx create mode 100644 packages/manager/src/features/Databases/DatabaseCreate/DatabaseSummarySection.tsx create mode 100644 packages/manager/src/features/Databases/DatabaseCreate/utilities.tsx diff --git a/packages/manager/.changeset/pr-11193-added-1730738415614.md b/packages/manager/.changeset/pr-11193-added-1730738415614.md new file mode 100644 index 00000000000..031adeba93d --- /dev/null +++ b/packages/manager/.changeset/pr-11193-added-1730738415614.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Added +--- + +Summary Section for Database Create GA ([#11193](https://github.com/linode/manager/pull/11193)) diff --git a/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx b/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx index 3d7cec0d765..4986554fa27 100644 --- a/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx +++ b/packages/manager/src/components/PrimaryNav/PrimaryNav.test.tsx @@ -118,7 +118,7 @@ describe('PrimaryNav', () => { it('should show Databases menu item if the user has the account capability V2', async () => { const account = accountFactory.build({ - capabilities: ['Managed Databases Beta'], + capabilities: ['Managed Databases'], }); server.use( diff --git a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseClusterData.tsx b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseClusterData.tsx new file mode 100644 index 00000000000..ddb10e80304 --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseClusterData.tsx @@ -0,0 +1,126 @@ +import Grid from '@mui/material/Unstable_Grid2'; +import React from 'react'; + +import { Divider } from 'src/components/Divider'; +import Select from 'src/components/EnhancedSelect'; +import { _SingleValue } from 'src/components/EnhancedSelect/components/SingleValue'; +import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; +import { RegionHelperText } from 'src/components/SelectRegionPanel/RegionHelperText'; +import { Typography } from 'src/components/Typography'; +import { + StyledLabelTooltip, + StyledTextField, +} from 'src/features/Databases/DatabaseCreate/DatabaseCreate.style'; +import { EngineOption } from 'src/features/Databases/DatabaseCreate/EngineOption'; +import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck'; +import { getSelectedOptionFromGroupedOptions } from 'src/utilities/getSelectedOptionFromGroupedOptions'; + +import { getEngineOptions } from './utilities'; + +import type { + ClusterSize, + ComprehensiveReplicationType, + DatabaseEngine, + Engine, + Region, +} from '@linode/api-v4'; +import type { FormikErrors } from 'formik'; +import type { Item } from 'src/components/EnhancedSelect'; +export interface DatabaseCreateValues { + allow_list: { + address: string; + error: string; + }[]; + cluster_size: ClusterSize; + compression_type: undefined; + engine: Engine; + label: string; + region: string; + replication_commit_type: undefined; + replication_type: ComprehensiveReplicationType; + ssl_connection: boolean; + storage_engine: undefined; + type: string; +} + +interface Props { + engines: DatabaseEngine[] | undefined; + errors: FormikErrors; + onChange: (filed: string, value: any) => void; + regionsData: Region[]; + values: DatabaseCreateValues; +} +export const DatabaseClusterData = (props: Props) => { + const { engines, errors, onChange, regionsData, values } = props; + const isRestricted = useRestrictedGlobalGrantCheck({ + globalGrantType: 'add_databases', + }); + + const engineOptions = React.useMemo(() => { + if (!engines) { + return []; + } + return getEngineOptions(engines); + }, [engines]); + + const labelToolTip = ( + + Label must: +
    +
  • Begin with an alpha character
  • +
  • Contain only alpha characters or single hyphens
  • +
  • Be between 3 - 32 characters
  • +
+
+ ); + + return ( + <> + + Name Your Cluster + onChange('label', e.target.value)} + tooltipText={labelToolTip} + value={values.label} + /> + + + + Select Engine and Region + {/* TODO: use Autocomplete instead of Select */} + ) => { - setFieldValue('engine', selected.value); - }} - value={getSelectedOptionFromGroupedOptions( - values.engine, - engineOptions - )} - className={classes.engineSelect} - components={{ Option: EngineOption, SingleValue: _SingleValue }} - disabled={isRestricted} - errorText={errors.engine} - isClearable={false} - label="Database Engine" - options={engineOptions} - placeholder={'Select a Database Engine'} - /> - - - setFieldValue('region', region.id)} - regions={regionsData} - value={values.region} - /> - - + setFieldValue(field, value)} + regionsData={regionsData} + values={values} + /> - { setFieldValue('type', selected); }} - className={classes.selectPlanPanel} data-qa-select-plan disabled={isRestricted} error={errors.type} @@ -601,45 +339,17 @@ const DatabaseCreate = () => { - - Set Number of Nodes{' '} - - - We recommend 3 nodes in a database cluster to avoid downtime during - upgrades and maintenance. - - ) => { - setFieldValue('cluster_size', +e.target.value); - !isDatabasesV2Enabled && - setFieldValue( - 'replication_type', - +e.target.value === 1 ? 'none' : 'semi_synch' - ); + { + handleNodeChange(v); }} - data-testid="database-nodes" - disabled={isRestricted} - > - {errors.cluster_size ? ( - - ) : null} - - {nodeOptions.map((nodeOption) => ( - } - data-qa-radio={nodeOption.label} - key={nodeOption.value} - label={nodeOption.label} - value={nodeOption.value} - /> - ))} - - + displayTypes={displayTypes} + error={errors.cluster_size} + selectedClusterSize={values.cluster_size} + selectedEngine={selectedEngine} + selectedPlan={selectedPlan} + selectedTab={selectedTab} + /> { onChange={(ips: ExtendedIP[]) => setFieldValue('allow_list', ips)} /> - - + {isDatabasesV2GA && ( + + + + )} + + Your database node(s) will take approximately 15-30 minutes to provision. - - - + + {isDatabasesV2Enabled && } ); }; -const determineReplicationType = (clusterSize: number, engine: string) => { - if (Boolean(engine.match(/mongo/))) { - return undefined; - } - - // If engine is a MySQL or Postgres one and it's a standalone DB instance - if (clusterSize === 1) { - return 'none'; - } - - // MySQL engine & cluster = semi_synch. PostgreSQL engine & cluster = asynch. - if (Boolean(engine.match(/mysql/))) { - return 'semi_synch'; - } else { - return 'asynch'; - } -}; - -const determineReplicationCommitType = (engine: string) => { - // 'local' is the default. - if (Boolean(engine.match(/postgres/))) { - return 'local'; - } - - return undefined; -}; - -const determineStorageEngine = (engine: string) => { - // 'wiredtiger' is the default. - if (Boolean(engine.match(/mongo/))) { - return 'wiredtiger'; - } - - return undefined; -}; - -const determineCompressionType = (engine: string) => { - // 'none' is the default. - if (Boolean(engine.match(/mongo/))) { - return 'none'; - } - - return undefined; -}; - export const databaseCreateLazyRoute = createLazyRoute('/databases/create')({ component: DatabaseCreate, }); diff --git a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseNodeSelector.test.tsx b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseNodeSelector.test.tsx new file mode 100644 index 00000000000..07290514b59 --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseNodeSelector.test.tsx @@ -0,0 +1,45 @@ +import { waitForElementToBeRemoved } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import * as React from 'react'; + +import { mockMatchMedia, renderWithTheme } from 'src/utilities/testHelpers'; + +import DatabaseCreate from './DatabaseCreate'; + +const loadingTestId = 'circle-progress'; + +beforeAll(() => mockMatchMedia()); + +describe('database node selector', () => { + const flags = { + dbaasV2: { + beta: false, + enabled: true, + }, + }; + it('should render 3 nodes for dedicated tab', async () => { + const { getByTestId } = renderWithTheme(, { flags }); + expect(getByTestId(loadingTestId)).toBeInTheDocument(); + await waitForElementToBeRemoved(getByTestId(loadingTestId)); + + expect(getByTestId('database-nodes').childNodes.length).equal(3); + expect(getByTestId('database-node-1')).toBeInTheDocument(); + expect(getByTestId('database-node-2')).toBeInTheDocument(); + expect(getByTestId('database-node-3')).toBeInTheDocument(); + }); + + it('should render 2 nodes for shared tab', async () => { + const { getAllByRole, getByTestId } = renderWithTheme(, { + flags, + }); + expect(getByTestId(loadingTestId)).toBeInTheDocument(); + await waitForElementToBeRemoved(getByTestId(loadingTestId)); + + const sharedTab = getAllByRole('tab')[1]; + await userEvent.click(sharedTab); + + expect(getByTestId('database-nodes').childNodes.length).equal(2); + expect(getByTestId('database-node-1')).toBeInTheDocument(); + expect(getByTestId('database-node-3')).toBeInTheDocument(); + }); +}); diff --git a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseNodeSelector.tsx b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseNodeSelector.tsx new file mode 100644 index 00000000000..50ae7d7998f --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseNodeSelector.tsx @@ -0,0 +1,209 @@ +import { FormControl } from '@linode/ui'; +import React from 'react'; + +import { FormControlLabel } from 'src/components/FormControlLabel'; +import { Notice } from 'src/components/Notice/Notice'; +import { Radio } from 'src/components/Radio/Radio'; +import { RadioGroup } from 'src/components/RadioGroup'; +import { Typography } from 'src/components/Typography'; +import { StyledChip } from 'src/features/components/PlansPanel/PlanSelection.styles'; +import { determineInitialPlanCategoryTab } from 'src/features/components/PlansPanel/utils'; +import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck'; + +import { useIsDatabasesEnabled } from '../utilities'; + +import type { + ClusterSize, + DatabaseClusterSizeObject, + DatabasePriceObject, + Engine, +} from '@linode/api-v4/lib/databases/types'; +import type { Theme } from '@mui/material'; +import type { + PlanSelectionType, + PlanSelectionWithDatabaseType, +} from 'src/features/components/PlansPanel/types'; + +export interface NodePricing { + double: DatabasePriceObject | undefined; + multi: DatabasePriceObject | undefined; + single: DatabasePriceObject | undefined; +} +interface Props { + currentClusterSize?: ClusterSize | undefined; + currentPlan?: PlanSelectionWithDatabaseType | undefined; + displayTypes: PlanSelectionType[]; + error?: string; + handleNodeChange: (value: ClusterSize) => void; + selectedClusterSize: ClusterSize | undefined; + selectedEngine: Engine; + selectedPlan: PlanSelectionWithDatabaseType | undefined; + selectedTab: number; +} +const typographyBaseStyles = (theme: Theme) => ({ + color: theme.palette.mode === 'dark' ? theme.color.grey6 : theme.color.grey1, +}); + +export const DatabaseNodeSelector = (props: Props) => { + const { + currentClusterSize, + currentPlan, + displayTypes, + error, + handleNodeChange, + selectedClusterSize, + selectedEngine, + selectedPlan, + selectedTab, + } = props; + + const { isDatabasesV2Enabled } = useIsDatabasesEnabled(); + const isRestricted = useRestrictedGlobalGrantCheck({ + globalGrantType: 'add_databases', + }); + + const nodePricing = { + double: selectedPlan?.engines[selectedEngine]?.find( + (cluster: DatabaseClusterSizeObject) => cluster.quantity === 2 + )?.price, + multi: selectedPlan?.engines[selectedEngine]?.find( + (cluster: DatabaseClusterSizeObject) => cluster.quantity === 3 + )?.price, + single: selectedPlan?.engines[selectedEngine]?.find( + (cluster: DatabaseClusterSizeObject) => cluster.quantity === 1 + )?.price, + }; + + const initialTab = determineInitialPlanCategoryTab( + displayTypes, + currentPlan?.id + ); + + const nodeOptions = React.useMemo(() => { + const hasDedicated = displayTypes.some( + (type) => type.class === 'dedicated' + ); + + const currentChip = currentClusterSize && initialTab === selectedTab && ( + + ); + + const isDisabled = (nodeSize: ClusterSize) => { + return currentClusterSize && nodeSize < currentClusterSize; + }; + + const options = [ + { + label: ( + + 1 Node {` `} + {currentClusterSize === 1 && currentChip} +
+ + {`$${nodePricing?.single?.monthly || 0}/month $${ + nodePricing?.single?.hourly || 0 + }/hr`} + +
+ ), + value: 1, + }, + ]; + + if (hasDedicated && selectedTab === 0 && isDatabasesV2Enabled) { + options.push({ + label: ( + + 2 Nodes - High Availability + {currentClusterSize === 2 && currentChip} +
+ + {`$${nodePricing?.double?.monthly || 0}/month $${ + nodePricing?.double?.hourly || 0 + }/hr`} + +
+ ), + value: 2, + }); + } + + options.push({ + label: ( + + 3 Nodes - High Availability (recommended) + {currentClusterSize === 3 && currentChip} +
+ + {`$${nodePricing?.multi?.monthly || 0}/month $${ + nodePricing?.multi?.hourly || 0 + }/hr`} + +
+ ), + value: 3, + }); + + return options; + }, [ + selectedTab, + nodePricing, + displayTypes, + isDatabasesV2Enabled, + currentClusterSize, + selectedClusterSize, + ]); + + return ( + <> + + Set Number of Nodes + + + We recommend 3 nodes in a database cluster to avoid downtime during + upgrades and maintenance. + + ) => { + handleNodeChange(+e.target.value as ClusterSize); + }} + disabled={isRestricted} + > + {error ? : null} + + {nodeOptions.map((nodeOption) => ( + } + data-qa-radio={nodeOption.label} + data-testid={`database-node-${nodeOption.value}`} + key={nodeOption.value} + label={nodeOption.label} + sx={(theme) => ({ marginBottom: theme.spacing() })} + value={nodeOption.value} + /> + ))} + + + + ); +}; diff --git a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseSummarySection.test.tsx b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseSummarySection.test.tsx new file mode 100644 index 00000000000..e1fc22fff0c --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseSummarySection.test.tsx @@ -0,0 +1,68 @@ +import { waitFor, waitForElementToBeRemoved } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { createMemoryHistory } from 'history'; +import * as React from 'react'; +import { Router } from 'react-router-dom'; + +import { databaseTypeFactory } from 'src/factories'; +import { makeResourcePage } from 'src/mocks/serverHandlers'; +import { HttpResponse, http, server } from 'src/mocks/testServer'; +import { mockMatchMedia, renderWithTheme } from 'src/utilities/testHelpers'; + +import DatabaseCreate from './DatabaseCreate'; + +const loadingTestId = 'circle-progress'; + +beforeAll(() => mockMatchMedia()); + +describe('database node selector', () => { + const flags = { + dbaasV2: { + beta: false, + enabled: true, + }, + }; + + it('should render the correct number of node radio buttons, associated costs, and summary', async () => { + const standardTypes = databaseTypeFactory.buildList(7, { + class: 'standard', + }); + const mockDedicatedTypes = [ + databaseTypeFactory.build({ + class: 'dedicated', + disk: 81920, + id: 'g6-dedicated-2', + label: 'Dedicated 4 GB', + memory: 4096, + }), + ]; + + server.use( + http.get('*/databases/types', () => { + return HttpResponse.json( + makeResourcePage([...mockDedicatedTypes, ...standardTypes]) + ); + }) + ); + const history = createMemoryHistory(); + history.push('databases/create'); + + const { getByTestId } = renderWithTheme( + + + , + { flags } + ); + await waitForElementToBeRemoved(getByTestId(loadingTestId)); + const selectedPlan = await waitFor( + () => document.getElementById('g6-dedicated-2') as HTMLInputElement + ); + await userEvent.click(selectedPlan); + + const summary = getByTestId('currentSummary'); + const selectedPlanText = 'Dedicated 4 GB $60/month'; + expect(summary).toHaveTextContent(selectedPlanText); + const selectedNodesText = '3 Nodes - HA $140/month'; + expect(summary).toHaveTextContent(selectedNodesText); + }); +}); diff --git a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseSummarySection.tsx b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseSummarySection.tsx new file mode 100644 index 00000000000..7d6ee387e92 --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseSummarySection.tsx @@ -0,0 +1,136 @@ +import { Box } from '@linode/ui'; +import React from 'react'; + +import { Typography } from 'src/components/Typography'; + +import { StyledPlanSummarySpan } from '../DatabaseDetail/DatabaseResize/DatabaseResize.style'; +import { useIsDatabasesEnabled } from '../utilities'; +import { StyledSpan } from './DatabaseCreate.style'; + +import type { + ClusterSize, + DatabaseClusterSizeObject, + DatabasePriceObject, + Engine, +} from '@linode/api-v4'; +import type { Theme } from '@mui/material'; +import type { PlanSelectionWithDatabaseType } from 'src/features/components/PlansPanel/types'; + +interface Props { + currentClusterSize: ClusterSize; + currentEngine: Engine; + currentPlan: PlanSelectionWithDatabaseType | undefined; + isResize?: boolean; + label?: string; + platform?: string; + resizeData?: { + basePrice: string; + numberOfNodes: ClusterSize; + plan: string; + price: string; + }; +} + +export const DatabaseSummarySection = (props: Props) => { + const { + currentClusterSize, + currentEngine, + currentPlan, + isResize, + label, + platform, + resizeData, + } = props; + const { isDatabasesV2GA } = useIsDatabasesEnabled(); + + const currentPrice = currentPlan?.engines[currentEngine].find( + (cluster: DatabaseClusterSizeObject) => + cluster.quantity === currentClusterSize + )?.price as DatabasePriceObject; + + const currentBasePrice = currentPlan?.engines[currentEngine][0] + .price as DatabasePriceObject; + + const currentNodePrice = `$${currentPrice?.monthly}/month`; + const currentPlanPrice = `$${currentBasePrice?.monthly}/month`; + + const isNewDatabase = isDatabasesV2GA && platform !== 'rdbms-legacy'; + + const currentSummary = currentPlan ? ( + + + {isResize && 'Current Cluster: '} + {currentPlan?.heading} + {' '} + {isDatabasesV2GA ? ( + {currentPlanPrice} + ) : ( + {currentPlanPrice} + )} + ({ marginRight: theme.spacing(1) })} + > + {currentClusterSize} Node + {currentClusterSize > 1 ? 's - HA ' : ' '} + + {currentNodePrice} + + ) : ( + 'Please specify your cluster configuration' + ); + const resizeSummary = ( + ({ + marginTop: theme.spacing(2), + })} + data-testid="resizeSummary" + > + {resizeData ? ( + <> + + {isNewDatabase + ? 'Resized Cluster: ' + resizeData.plan + : resizeData.plan} + {' '} + {isNewDatabase && {resizeData.basePrice}} + ({ marginRight: theme.spacing(1) }) + : null + } + component="span" + > + {resizeData.numberOfNodes} Node + {resizeData.numberOfNodes > 1 ? 's' : ''} + {!isNewDatabase ? ': ' : ' - HA '} + + {resizeData.price} + + ) : isNewDatabase ? ( + <> + Resized Cluster: Please + select a plan or set the number of nodes. + + ) : ( + 'Please select a plan.' + )} + + ); + + return ( + <> + ({ + marginBottom: isDatabasesV2GA ? theme.spacing(2) : 0, + })} + variant="h2" + > + Summary {isNewDatabase && label} + + {isNewDatabase && currentSummary} + {isResize && resizeSummary} + + ); +}; diff --git a/packages/manager/src/features/Databases/DatabaseCreate/utilities.tsx b/packages/manager/src/features/Databases/DatabaseCreate/utilities.tsx new file mode 100644 index 00000000000..ec405a84c58 --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseCreate/utilities.tsx @@ -0,0 +1,115 @@ +import { groupBy } from 'ramda'; +import React from 'react'; + +import MongoDBIcon from 'src/assets/icons/mongodb.svg'; +import MySQLIcon from 'src/assets/icons/mysql.svg'; +import PostgreSQLIcon from 'src/assets/icons/postgresql.svg'; + +import { databaseEngineMap } from '../DatabaseLanding/DatabaseRow'; + +import type { DatabaseEngine } from '@linode/api-v4'; + +export const determineReplicationType = ( + clusterSize: number, + engine: string +) => { + if (/mongo/.test(engine)) { + return undefined; + } + + // If engine is a MySQL or Postgres one and it's a standalone DB instance + if (clusterSize === 1) { + return 'none'; + } + + // MySQL engine & cluster = semi_synch. PostgreSQL engine & cluster = asynch. + if (/mysql/.test(engine)) { + return 'semi_synch'; + } else { + return 'asynch'; + } +}; + +export const determineReplicationCommitType = (engine: string) => { + // 'local' is the default. + if (/postgres/.test(engine)) { + return 'local'; + } + + return undefined; +}; + +export const determineStorageEngine = (engine: string) => { + // 'wiredtiger' is the default. + if (/mongo/.test(engine)) { + return 'wiredtiger'; + } + + return undefined; +}; + +export const determineCompressionType = (engine: string) => { + // 'none' is the default. + if (/mongo/.test(engine)) { + return 'none'; + } + + return undefined; +}; + +interface EngineIconsProps { + mongodb: React.JSX.Element; + mysql: React.JSX.Element; + postgresql: React.JSX.Element; + redis: null; +} +export const engineIcons: EngineIconsProps = { + mongodb: , + mysql: , + postgresql: , + redis: null, +}; +export const getEngineOptions = (engines: DatabaseEngine[]) => { + const groupedEngines = groupBy((engineObject) => { + if (engineObject.engine.match(/mysql/i)) { + return 'MySQL'; + } + if (engineObject.engine.match(/postgresql/i)) { + return 'PostgreSQL'; + } + if (engineObject.engine.match(/mongodb/i)) { + return 'MongoDB'; + } + if (engineObject.engine.match(/redis/i)) { + return 'Redis'; + } + return 'Other'; + }, engines); + return ['MySQL', 'PostgreSQL', 'MongoDB', 'Redis', 'Other'].reduce( + (accum, thisGroup) => { + if ( + !groupedEngines[thisGroup] || + groupedEngines[thisGroup].length === 0 + ) { + return accum; + } + return [ + ...accum, + { + label: thisGroup, + options: groupedEngines[thisGroup] + .map((engineObject) => ({ + ...engineObject, + flag: engineIcons[engineObject.engine], + label: `${databaseEngineMap[engineObject.engine]} v${ + engineObject.version + }`, + value: `${engineObject.engine}/${engineObject.version}`, + })) + .sort((a, b) => (a.version > b.version ? -1 : 1)), + }, + ]; + }, + [] + ); +}; diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.tsx index 6253d642d44..d18366b16a6 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.tsx @@ -29,7 +29,7 @@ import { } from './DatabaseResize.style'; import { DatabaseResizeCurrentConfiguration } from './DatabaseResizeCurrentConfiguration'; -import type { NodePricing } from '../../DatabaseCreate/DatabaseCreate'; +import type { NodePricing } from '../../DatabaseCreate/DatabaseNodeSelector'; import type { ClusterSize, Database, diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummary.test.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummary.test.tsx index 46b8dac98fd..9d1251d312b 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummary.test.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummary.test.tsx @@ -36,7 +36,6 @@ const BUTTON_ACCESS_CONTROLS = 'button-access-control'; const spy = vi.spyOn(utils, 'useIsDatabasesEnabled'); spy.mockReturnValue({ isDatabasesEnabled: true, - isDatabasesV1Enabled: true, isDatabasesV2Beta: false, isDatabasesV2Enabled: true, isDatabasesV2GA: true, @@ -104,7 +103,6 @@ describe('Database Summary', () => { it('should render Beta view default db', async () => { spy.mockReturnValue({ isDatabasesEnabled: true, - isDatabasesV1Enabled: true, isDatabasesV2Beta: true, isDatabasesV2Enabled: true, isDatabasesV2GA: false, @@ -142,7 +140,6 @@ describe('Database Summary', () => { it('should render Beta view legacy db', async () => { spy.mockReturnValue({ isDatabasesEnabled: true, - isDatabasesV1Enabled: true, isDatabasesV2Beta: true, isDatabasesV2Enabled: true, isDatabasesV2GA: false, @@ -180,7 +177,6 @@ describe('Database Summary', () => { it('should render V1 view legacy db', async () => { spy.mockReturnValue({ isDatabasesEnabled: true, - isDatabasesV1Enabled: true, isDatabasesV2Beta: false, isDatabasesV2Enabled: false, isDatabasesV2GA: false, diff --git a/packages/manager/src/features/Databases/utilities.test.ts b/packages/manager/src/features/Databases/utilities.test.ts index 3d2e93a6c07..468c2702039 100644 --- a/packages/manager/src/features/Databases/utilities.test.ts +++ b/packages/manager/src/features/Databases/utilities.test.ts @@ -49,7 +49,6 @@ describe('useIsDatabasesEnabled', () => { const { result } = setup([], { dbaasV2: { beta: true, enabled: true } }); await waitFor(() => { expect(result.current.isDatabasesEnabled).toBe(false); - expect(result.current.isDatabasesV1Enabled).toBe(false); expect(result.current.isDatabasesV2Enabled).toBe(false); expect(result.current.isDatabasesV2Beta).toBe(false); @@ -67,7 +66,6 @@ describe('useIsDatabasesEnabled', () => { await waitFor(() => { expect(result.current.isDatabasesEnabled).toBe(true); - expect(result.current.isDatabasesV1Enabled).toBe(true); expect(result.current.isDatabasesV2Enabled).toBe(false); expect(result.current.isDatabasesV2Beta).toBe(false); @@ -85,7 +83,6 @@ describe('useIsDatabasesEnabled', () => { await waitFor(() => { expect(result.current.isDatabasesEnabled).toBe(true); - expect(result.current.isDatabasesV1Enabled).toBe(false); expect(result.current.isDatabasesV2Enabled).toBe(true); expect(result.current.isDatabasesV2Beta).toBe(true); @@ -103,7 +100,6 @@ describe('useIsDatabasesEnabled', () => { await waitFor(() => { expect(result.current.isDatabasesEnabled).toBe(false); - expect(result.current.isDatabasesV1Enabled).toBe(false); expect(result.current.isDatabasesV2Enabled).toBe(false); expect(result.current.isDatabasesV2Beta).toBe(false); @@ -121,7 +117,6 @@ describe('useIsDatabasesEnabled', () => { await waitFor(() => { expect(result.current.isDatabasesEnabled).toBe(true); - expect(result.current.isDatabasesV1Enabled).toBe(true); expect(result.current.isDatabasesV2Enabled).toBe(true); expect(result.current.isDatabasesV2Beta).toBe(true); @@ -139,8 +134,7 @@ describe('useIsDatabasesEnabled', () => { await waitFor(() => { expect(result.current.isDatabasesEnabled).toBe(true); - expect(result.current.isDatabasesV1Enabled).toBe(true); - expect(result.current.isDatabasesV2Enabled).toBe(false); + expect(result.current.isDatabasesV2Enabled).toBe(true); expect(result.current.isDatabasesV2Beta).toBe(false); expect(result.current.isUserExistingBeta).toBe(false); @@ -185,7 +179,6 @@ describe('useIsDatabasesEnabled', () => { await waitFor(() => { expect(result.current.isDatabasesEnabled).toBe(true); - expect(result.current.isDatabasesV1Enabled).toBe(true); expect(result.current.isDatabasesV2Enabled).toBe(false); expect(result.current.isDatabasesV2Beta).toBe(false); @@ -231,7 +224,6 @@ describe('useIsDatabasesEnabled', () => { await waitFor(() => { expect(result.current.isDatabasesEnabled).toBe(true); - expect(result.current.isDatabasesV1Enabled).toBe(true); expect(result.current.isDatabasesV2Enabled).toBe(true); expect(result.current.isDatabasesV2Beta).toBe(true); @@ -277,7 +269,6 @@ describe('useIsDatabasesEnabled', () => { await waitFor(() => { expect(result.current.isDatabasesEnabled).toBe(true); - expect(result.current.isDatabasesV1Enabled).toBe(false); expect(result.current.isDatabasesV2Enabled).toBe(true); expect(result.current.isDatabasesV2Beta).toBe(true); @@ -323,7 +314,6 @@ describe('useIsDatabasesEnabled', () => { await waitFor(() => { expect(result.current.isDatabasesEnabled).toBe(true); - expect(result.current.isDatabasesV1Enabled).toBe(false); expect(result.current.isDatabasesV2Enabled).toBe(true); expect(result.current.isDatabasesV2Beta).toBe(false); diff --git a/packages/manager/src/features/Databases/utilities.ts b/packages/manager/src/features/Databases/utilities.ts index 4736d94b4fa..4da443b71e6 100644 --- a/packages/manager/src/features/Databases/utilities.ts +++ b/packages/manager/src/features/Databases/utilities.ts @@ -17,7 +17,6 @@ export interface IsDatabasesEnabled { isDatabasesEnabled: boolean; isDatabasesMonitorBeta?: boolean; isDatabasesMonitorEnabled?: boolean; - isDatabasesV1Enabled: boolean; isDatabasesV2Beta: boolean; isDatabasesV2Enabled: boolean; isDatabasesV2GA: boolean; @@ -71,26 +70,32 @@ export const useIsDatabasesEnabled = (): IsDatabasesEnabled => { account?.capabilities ?? [] ); - const isDatabasesV2Enabled = isFeatureEnabledV2( - 'Managed Databases Beta', - hasV2Flag, - account?.capabilities ?? [] - ); + const isDatabasesV2BetaEnabled = + isFeatureEnabledV2( + 'Managed Databases Beta', + hasV2Flag, + account?.capabilities ?? [] + ) && hasV2BetaFlag; - const isDatabasesV2Beta: boolean = isDatabasesV2Enabled && hasV2BetaFlag; + const isDatabasesV2GAEnabled = + isFeatureEnabledV2( + 'Managed Databases', + hasV2Flag, + account?.capabilities ?? [] + ) && hasV2GAFlag; return { - isDatabasesEnabled: isDatabasesV1Enabled || isDatabasesV2Enabled, - isDatabasesMonitorBeta: !!flags.dbaasV2MonitorMetrics?.beta, - isDatabasesMonitorEnabled: !!flags.dbaasV2MonitorMetrics?.enabled, - isDatabasesV1Enabled, - isDatabasesV2Beta, - isDatabasesV2Enabled, - isDatabasesV2GA: - (isDatabasesV1Enabled || isDatabasesV2Enabled) && hasV2GAFlag, - - isUserExistingBeta: isDatabasesV2Beta && isDatabasesV1Enabled, - isUserNewBeta: isDatabasesV2Beta && !isDatabasesV1Enabled, + isDatabasesEnabled: + isDatabasesV1Enabled || + isDatabasesV2BetaEnabled || + isDatabasesV2GAEnabled, + + isDatabasesV2Beta: isDatabasesV2BetaEnabled, + isDatabasesV2Enabled: isDatabasesV2BetaEnabled || isDatabasesV2GAEnabled, + isDatabasesV2GA: isDatabasesV2GAEnabled, + + isUserExistingBeta: isDatabasesV2BetaEnabled && isDatabasesV1Enabled, + isUserNewBeta: isDatabasesV2BetaEnabled && !isDatabasesV1Enabled, }; } @@ -99,12 +104,11 @@ export const useIsDatabasesEnabled = (): IsDatabasesEnabled => { return { isDatabasesEnabled: hasLegacyTypes || hasDefaultTypes, - isDatabasesMonitorBeta: !!flags.dbaasV2MonitorMetrics?.beta, - isDatabasesMonitorEnabled: !!flags.dbaasV2MonitorMetrics?.enabled, - isDatabasesV1Enabled: hasLegacyTypes, + isDatabasesV2Beta: hasDefaultTypes && hasV2BetaFlag, isDatabasesV2Enabled: hasDefaultTypes, isDatabasesV2GA: (hasLegacyTypes || hasDefaultTypes) && hasV2GAFlag, + isUserExistingBeta: hasLegacyTypes && hasDefaultTypes && hasV2BetaFlag, isUserNewBeta: !hasLegacyTypes && hasDefaultTypes && hasV2BetaFlag, }; From 8e007a791c7103980ca5a161250177dbd86c508d Mon Sep 17 00:00:00 2001 From: mpolotsk-akamai <157619599+mpolotsk-akamai@users.noreply.github.com> Date: Tue, 5 Nov 2024 19:41:58 +0100 Subject: [PATCH 47/66] feat: [UIE-8082] - DBaaS GA Database Resize refactoring (#11180) * feat: [UIE-8082] - DBaaS GA Database Create Summary section, refactoring * Added changeset: Added Summary Section for Database Create GA * change: [UIE-8082] - DBaaS GA Database Resize refactoring * refactoring: [UIE-8082] - DBaaS GA Database Resize review fix * Added changeset: Refactored DatabaseResize to use shared components for node selection and summary section. * flag for monitor tab --- .../pr-11180-changed-1730803608188.md | 5 + .../DatabaseCreate/DatabaseCreate.tsx | 2 +- .../DatabaseResize/DatabaseResize.test.tsx | 101 ++-- .../DatabaseResize/DatabaseResize.tsx | 472 ++++-------------- .../Databases/DatabaseDetail/index.tsx | 15 +- .../DatabaseLanding/DatabaseLanding.tsx | 2 +- .../src/features/Databases/utilities.ts | 2 - 7 files changed, 160 insertions(+), 439 deletions(-) create mode 100644 packages/manager/.changeset/pr-11180-changed-1730803608188.md diff --git a/packages/manager/.changeset/pr-11180-changed-1730803608188.md b/packages/manager/.changeset/pr-11180-changed-1730803608188.md new file mode 100644 index 00000000000..53ae4f23340 --- /dev/null +++ b/packages/manager/.changeset/pr-11180-changed-1730803608188.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Changed +--- + +Refactored DatabaseResize to use shared components for node selection and summary section. ([#11180](https://github.com/linode/manager/pull/11180)) diff --git a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx index d2407183f9a..06bc649e980 100644 --- a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx +++ b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx @@ -138,7 +138,7 @@ const DatabaseCreate = () => { ...values, allow_list: _allow_list, }; - if (isDatabasesV2Enabled || isDatabasesV2GA) { + if (isDatabasesV2Enabled) { delete createPayload.replication_type; } try { diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.test.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.test.tsx index a29ae8a5bfe..b8b17ed60a8 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.test.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.test.tsx @@ -2,6 +2,7 @@ import { queryByAttribute, waitForElementToBeRemoved, } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { createMemoryHistory } from 'history'; import * as React from 'react'; import { Router } from 'react-router-dom'; @@ -16,7 +17,6 @@ import { HttpResponse, http, server } from 'src/mocks/testServer'; import { mockMatchMedia, renderWithTheme } from 'src/utilities/testHelpers'; import { DatabaseResize } from './DatabaseResize'; -import userEvent from '@testing-library/user-event'; const loadingTestId = 'circle-progress'; @@ -169,6 +169,20 @@ describe('database resize', () => { }); describe('on rendering of page and isDatabasesV2GA is true and the Shared CPU tab is preselected ', () => { + const mockDatabase = databaseFactory.build({ + cluster_size: 3, + engine: 'mysql', + platform: 'rdbms-default', + type: 'g6-nanode-1', + }); + + const flags = { + dbaasV2: { + beta: false, + enabled: true, + }, + }; + beforeEach(() => { // Mock database types const standardTypes = [ @@ -206,17 +220,6 @@ describe('database resize', () => { }); it('should render set node section', async () => { - const flags = { - dbaasV2: { - beta: false, - enabled: true, - }, - }; - const mockDatabase = databaseFactory.build({ - cluster_size: 3, - type: 'g6-nanode-1', - engine: 'mysql', - }); const { getByTestId, getByText } = renderWithTheme( , { flags } @@ -232,16 +235,6 @@ describe('database resize', () => { }); it('should render the correct number of node radio buttons, associated costs, and summary', async () => { - const flags = { - dbaasV2: { - beta: false, - enabled: true, - }, - }; - const mockDatabase = databaseFactory.build({ - cluster_size: 3, - type: 'g6-nanode-1', - }); const { getByTestId } = renderWithTheme( , { flags } @@ -252,10 +245,12 @@ describe('database resize', () => { expect(nodeRadioBtns).toHaveTextContent('$60/month $0.09/hr'); expect(nodeRadioBtns).toHaveTextContent('$140/month $0.21/hr'); - const expectedCurrentSummary = - 'Current Cluster: New DBaaS - Nanode 1 GB $60/month 3 Nodes - HA $140/month'; const currentSummary = getByTestId('currentSummary'); - expect(currentSummary).toHaveTextContent(expectedCurrentSummary); + const selectedPlanText = + 'Current Cluster: New DBaaS - Nanode 1 GB $60/month'; + expect(currentSummary).toHaveTextContent(selectedPlanText); + const selectedNodesText = '3 Nodes - HA $140/month'; + expect(currentSummary).toHaveTextContent(selectedNodesText); const expectedResizeSummary = 'Resized Cluster: Please select a plan or set the number of nodes.'; @@ -264,16 +259,6 @@ describe('database resize', () => { }); it('should preselect cluster size in Set Number of Nodes', async () => { - const flags = { - dbaasV2: { - beta: false, - enabled: true, - }, - }; - const mockDatabase = databaseFactory.build({ - cluster_size: 3, - type: 'g6-nanode-1', - }); const { getByTestId } = renderWithTheme( , { flags } @@ -286,16 +271,6 @@ describe('database resize', () => { }); it('should disable visible lower node selections', async () => { - const flags = { - dbaasV2: { - beta: false, - enabled: true, - }, - }; - const mockDatabase = databaseFactory.build({ - cluster_size: 3, - type: 'g6-nanode-1', - }); const { getByTestId } = renderWithTheme( , { flags } @@ -307,14 +282,9 @@ describe('database resize', () => { }); it('should set price, enable resize button, and update resize summary when a new number of nodes is selected', async () => { - const flags = { - dbaasV2: { - beta: false, - enabled: true, - }, - }; const mockDatabase = databaseFactory.build({ cluster_size: 1, + platform: 'rdbms-default', type: 'g6-nanode-1', }); const { getByTestId, getByText } = renderWithTheme( @@ -331,21 +301,18 @@ describe('database resize', () => { ) as HTMLButtonElement; expect(resizeButton.disabled).toBeFalsy(); - const expectedSummaryText = - 'Resized Cluster: New DBaaS - Nanode 1 GB $60/month 3 Nodes - HA $140/month'; const summary = getByTestId('resizeSummary'); - expect(summary).toHaveTextContent(expectedSummaryText); + const selectedPlanText = + 'Resized Cluster: New DBaaS - Nanode 1 GB $60/month'; + expect(summary).toHaveTextContent(selectedPlanText); + const selectedNodesText = '3 Nodes - HA $140/month'; + expect(summary).toHaveTextContent(selectedNodesText); }); it('should disable the resize button if node selection is set back to current', async () => { - const flags = { - dbaasV2: { - beta: false, - enabled: true, - }, - }; const mockDatabase = databaseFactory.build({ cluster_size: 1, + platform: 'rdbms-default', type: 'g6-nanode-1', }); const { getByTestId, getByText } = renderWithTheme( @@ -416,8 +383,9 @@ describe('database resize', () => { it('should render node selection for dedicated tab with default summary', async () => { const mockDatabase = databaseFactory.build({ - type: 'g6-dedicated-2', cluster_size: 3, + platform: 'rdbms-default', + type: 'g6-dedicated-2', }); const flags = { @@ -441,8 +409,9 @@ describe('database resize', () => { it('should disable lower node selections', async () => { const mockDatabase = databaseFactory.build({ - type: 'g6-dedicated-2', cluster_size: 3, + platform: 'rdbms-default', + type: 'g6-dedicated-2', }); const flags = { @@ -452,8 +421,14 @@ describe('database resize', () => { }, }; + // Mock route history so the Plan Selection table displays prices without requiring a region in the DB resize flow. + const history = createMemoryHistory(); + history.push(`databases/${database.engine}/${database.id}/resize`); + const { getByTestId } = renderWithTheme( - , + + + , { flags } ); expect(getByTestId(loadingTestId)).toBeInTheDocument(); diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.tsx index d18366b16a6..89e06beb12f 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResize.tsx @@ -2,34 +2,29 @@ import { Box, Paper } from '@linode/ui'; import { useSnackbar } from 'notistack'; import * as React from 'react'; import { useHistory } from 'react-router-dom'; -import { makeStyles } from 'tss-react/mui'; import { CircleProgress } from 'src/components/CircleProgress'; import { Divider } from 'src/components/Divider'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; -import { FormControlLabel } from 'src/components/FormControlLabel'; import { Notice } from 'src/components/Notice/Notice'; -import { Radio } from 'src/components/Radio/Radio'; -import { RadioGroup } from 'src/components/RadioGroup'; import { TypeToConfirmDialog } from 'src/components/TypeToConfirmDialog/TypeToConfirmDialog'; import { Typography } from 'src/components/Typography'; -import { StyledChip } from 'src/features/components/PlansPanel/PlanSelection.styles'; import { determineInitialPlanCategoryTab } from 'src/features/components/PlansPanel/utils'; +import { DatabaseNodeSelector } from 'src/features/Databases/DatabaseCreate/DatabaseNodeSelector'; +import { DatabaseSummarySection } from 'src/features/Databases/DatabaseCreate/DatabaseSummarySection'; +import { DatabaseResizeCurrentConfiguration } from 'src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResizeCurrentConfiguration'; +import { useIsDatabasesEnabled } from 'src/features/Databases/utilities'; import { typeLabelDetails } from 'src/features/Linodes/presentation'; -import { useDatabaseTypesQuery } from 'src/queries/databases/databases'; import { useDatabaseMutation } from 'src/queries/databases/databases'; +import { useDatabaseTypesQuery } from 'src/queries/databases/databases'; import { formatStorageUnits } from 'src/utilities/formatStorageUnits'; -import { useIsDatabasesEnabled } from '../../utilities'; import { StyledGrid, - StyledPlanSummarySpan, StyledPlansPanel, StyledResizeButton, } from './DatabaseResize.style'; -import { DatabaseResizeCurrentConfiguration } from './DatabaseResizeCurrentConfiguration'; -import type { NodePricing } from '../../DatabaseCreate/DatabaseNodeSelector'; import type { ClusterSize, Database, @@ -39,56 +34,19 @@ import type { Engine, UpdateDatabasePayload, } from '@linode/api-v4'; -import type { Theme } from '@mui/material/styles'; import type { PlanSelectionWithDatabaseType } from 'src/features/components/PlansPanel/types'; -const useStyles = makeStyles()((theme: Theme) => ({ - disabledOptionLabel: { - color: - theme.palette.mode === 'dark' ? theme.color.grey6 : theme.color.grey1, - }, - formControlLabel: { - marginBottom: theme.spacing(), - }, - nodeSpanSpacing: { - marginRight: theme.spacing(1), - }, - summarySpanBorder: { - borderRight: `1px solid ${theme.borderColors.borderTypography}`, - color: theme.textColors.tableStatic, - marginLeft: theme.spacing(1), - marginRight: theme.spacing(1), - paddingRight: theme.spacing(1), - }, -})); - interface Props { database: Database; disabled?: boolean; } export const DatabaseResize = ({ database, disabled = false }: Props) => { - const { classes } = useStyles(); const history = useHistory(); - const [planSelected, setPlanSelected] = React.useState( - database.type - ); - const [summaryText, setSummaryText] = React.useState<{ - basePrice: string; - numberOfNodes: ClusterSize; - plan: string; - price: string; - }>(); - const [nodePricing, setNodePricing] = React.useState< - NodePricing | undefined - >(); - // This will be set to `false` once one of the configuration is selected from available plan. This is used to disable the - // "Resize" button unless there have been changes to the form. - const [ - shouldSubmitBeDisabled, - setShouldSubmitBeDisabled, - ] = React.useState(true); + const [selectedPlanId, setSelectedPlanId] = React.useState< + string | undefined + >(database.type); const [ isResizeConfirmationDialogOpen, @@ -96,7 +54,9 @@ export const DatabaseResize = ({ database, disabled = false }: Props) => { ] = React.useState(false); const [selectedTab, setSelectedTab] = React.useState(0); - const { isDatabasesV2Enabled, isDatabasesV2GA } = useIsDatabasesEnabled(); + const { isDatabasesV2GA } = useIsDatabasesEnabled(); + const isNewDatabaseGA = + isDatabasesV2GA && database.platform !== 'rdbms-legacy'; const [clusterSize, setClusterSize] = React.useState( database.cluster_size ); @@ -122,8 +82,8 @@ export const DatabaseResize = ({ database, disabled = false }: Props) => { payload.cluster_size = clusterSize; } - if (planSelected) { - payload.type = planSelected; + if (selectedPlanId) { + payload.type = selectedPlanId; } updateDatabase(payload).then(() => { @@ -144,49 +104,51 @@ export const DatabaseResize = ({ database, disabled = false }: Props) => { ); - const resizeSummary = ( - ({ - marginTop: theme.spacing(2), - })} - data-testid="resizeSummary" - > - {summaryText ? ( - <> - - {isDatabasesV2GA - ? 'Resized Cluster: ' + summaryText.plan - : summaryText.plan} - {' '} - {isDatabasesV2GA ? ( - - {summaryText.basePrice} - - ) : null} - - {' '} - {summaryText.numberOfNodes} Node - {summaryText.numberOfNodes > 1 ? 's' : ''} - {!isDatabasesV2GA ? ': ' : ' - HA '} - - {summaryText.price} - - ) : isDatabasesV2GA ? ( - <> - Resized Cluster: Please - select a plan or set the number of nodes. - - ) : ( - 'Please select a plan.' - )} - - ); + const selectedEngine = database.engine.split('/')[0] as Engine; + + const summaryText = React.useMemo(() => { + const nodeSelected = clusterSize && clusterSize > database.cluster_size; + + const isSamePlanSelected = selectedPlanId === database.type; + if (!dbTypes) { + return undefined; + } + // Set default message and disable submit when no new selection is made + if (!nodeSelected && (!selectedPlanId || isSamePlanSelected)) { + return undefined; + } + + const selectedPlanType = dbTypes.find( + (type: DatabaseType) => type.id === selectedPlanId + ); + + if (!selectedPlanType || !clusterSize) { + return undefined; + } + + const price = selectedPlanType.engines[selectedEngine].find( + (cluster: DatabaseClusterSizeObject) => cluster.quantity === clusterSize + )?.price as DatabasePriceObject; + const resizeBasePrice = selectedPlanType.engines[selectedEngine][0] + .price as DatabasePriceObject; + const currentPlanPrice = `$${resizeBasePrice?.monthly}/month`; + + return { + basePrice: currentPlanPrice, + numberOfNodes: clusterSize, + plan: formatStorageUnits(selectedPlanType.label), + price: isNewDatabaseGA + ? `$${price?.monthly}/month` + : `$${price?.monthly}/month or $${price?.hourly}/hour`, + }; + }, [selectedPlanId, clusterSize, selectedTab]); const costSummary = ( {`The cost of the resized database is ${summaryText?.price}.`} ); + const confirmationPopUpMessage = database.cluster_size === 1 ? ( <> @@ -204,93 +166,6 @@ export const DatabaseResize = ({ database, disabled = false }: Props) => { ); - const setSummaryAndPrices = ( - databaseTypeId: string, - engine: Engine, - dbTypes: DatabaseType[] - ) => { - const selectedPlanType = dbTypes.find( - (type: DatabaseType) => type.id === databaseTypeId - ); - if (selectedPlanType) { - // When plan is found, set node pricing - const nodePricingDetails = { - double: selectedPlanType.engines[engine]?.find( - (cluster: DatabaseClusterSizeObject) => cluster.quantity === 2 - )?.price, - multi: selectedPlanType.engines[engine]?.find( - (cluster: DatabaseClusterSizeObject) => cluster.quantity === 3 - )?.price, - single: selectedPlanType.engines[engine]?.find( - (cluster: DatabaseClusterSizeObject) => cluster.quantity === 1 - )?.price, - }; - setNodePricing(nodePricingDetails); - } else { - // If plan is not found, clear plan selection - setPlanSelected(undefined); - } - - if (!selectedPlanType || !clusterSize) { - setSummaryText(undefined); - setShouldSubmitBeDisabled(true); - return; - } - - const price = selectedPlanType.engines[engine].find( - (cluster: DatabaseClusterSizeObject) => cluster.quantity === clusterSize - )?.price as DatabasePriceObject; - const resizeBasePrice = selectedPlanType.engines[engine][0] - .price as DatabasePriceObject; - const currentPlanPrice = `$${resizeBasePrice?.monthly}/month`; - - setSummaryText({ - basePrice: currentPlanPrice, - numberOfNodes: clusterSize, - plan: formatStorageUnits(selectedPlanType.label), - price: isDatabasesV2GA - ? `$${price?.monthly}/month` - : `$${price?.monthly}/month or $${price?.hourly}/hour`, - }); - - setShouldSubmitBeDisabled(false); - return; - }; - - React.useEffect(() => { - const nodeSelected = clusterSize && clusterSize > database.cluster_size; - const isSamePlanSelected = planSelected === database.type; - if (!dbTypes) { - return; - } - // Set default message and disable submit when no new selection is made - if (!nodeSelected && (!planSelected || isSamePlanSelected)) { - setShouldSubmitBeDisabled(true); - setSummaryText(undefined); - return; - } - const engineType = database.engine.split('/')[0] as Engine; - // When only a higher node selection is made and plan has not been changed - if (isDatabasesV2GA && nodeSelected && isSamePlanSelected) { - setSummaryAndPrices(database.type, engineType, dbTypes); - } - // No plan selection or plan selection is unchanged - if (!planSelected || isSamePlanSelected) { - return; - } - // When a new plan is selected - setSummaryAndPrices(planSelected, engineType, dbTypes); - }, [ - dbTypes, - database.engine, - database.type, - planSelected, - database.cluster_size, - clusterSize, - ]); - - const selectedEngine = database.engine.split('/')[0] as Engine; - const displayTypes: PlanSelectionWithDatabaseType[] = React.useMemo(() => { if (!dbTypes) { return []; @@ -321,78 +196,36 @@ export const DatabaseResize = ({ database, disabled = false }: Props) => { }, [database.cluster_size, dbTypes, selectedEngine]); const currentPlan = displayTypes?.find((type) => type.id === database.type); - const currentPlanDisk = currentPlan ? currentPlan.disk : 0; - const disabledPlans = displayTypes?.filter((type) => - type.class === 'dedicated' - ? type.disk < currentPlanDisk - : type.disk <= currentPlanDisk - ); - const currentEngine = database.engine.split('/')[0] as Engine; - const currentPrice = currentPlan?.engines[currentEngine].find( - (cluster: DatabaseClusterSizeObject) => - cluster.quantity === database.cluster_size - )?.price as DatabasePriceObject; - const currentBasePrice = currentPlan?.engines[currentEngine][0] - .price as DatabasePriceObject; - const currentNodePrice = `$${currentPrice?.monthly}/month`; - const currentPlanPrice = `$${currentBasePrice?.monthly}/month`; - const currentSummary = ( - - - Current Cluster: {currentPlan?.heading} - {' '} - - {currentPlanPrice} - - - {' '} - {database.cluster_size} Node - {database.cluster_size > 1 ? 's - HA ' : ' '} - - {currentNodePrice} - - ); - - const isDisabledSharedTab = database.cluster_size === 2; React.useEffect(() => { const initialTab = determineInitialPlanCategoryTab( displayTypes, - planSelected, + database.type, currentPlan?.heading ); setSelectedTab(initialTab); + }, [database.type, displayTypes]); - if (isDatabasesV2GA) { - const engineType = database.engine.split('/')[0] as Engine; - const nodePricingDetails = { - double: currentPlan?.engines[engineType]?.find( - (cluster: DatabaseClusterSizeObject) => cluster.quantity === 2 - )?.price, - multi: currentPlan?.engines[engineType]?.find( - (cluster: DatabaseClusterSizeObject) => cluster.quantity === 3 - )?.price, - single: currentPlan?.engines[engineType]?.find( - (cluster: DatabaseClusterSizeObject) => cluster.quantity === 1 - )?.price, - }; - setNodePricing(nodePricingDetails); - } - }, [dbTypes, displayTypes]); + const currentPlanDisk = currentPlan ? currentPlan.disk : 0; + const disabledPlans = displayTypes?.filter((type) => + type.class === 'dedicated' + ? type.disk < currentPlanDisk + : type.disk <= currentPlanDisk + ); + const isDisabledSharedTab = database.cluster_size === 2; + + const shouldSubmitBeDisabled = React.useMemo(() => { + return !summaryText; + }, [summaryText]); - const handleNodeChange = ( - event: React.ChangeEvent - ): void => { - const size = Number(event.currentTarget.value) as ClusterSize; + const handleNodeChange = (size: ClusterSize | undefined): void => { const selectedPlanTab = determineInitialPlanCategoryTab( displayTypes, - planSelected + selectedPlanId ); // If 2 Nodes is selected for an incompatible plan, clear selected plan and related information if (size === 2 && selectedPlanTab !== 0) { - setNodePricing(undefined); - setPlanSelected(undefined); - setSummaryText(undefined); + setSelectedPlanId(undefined); } setClusterSize(size); }; @@ -401,95 +234,24 @@ export const DatabaseResize = ({ database, disabled = false }: Props) => { if (selectedTab === index) { return; } - // Clear plan and related info when when 2 nodes option is selected for incompatible plan. - if (isDatabasesV2GA && selectedTab === 0 && clusterSize === 2) { - setClusterSize(undefined); - setPlanSelected(undefined); - setNodePricing(undefined); - setSummaryText(undefined); - } - setSelectedTab(index); - }; - - const nodeOptions = React.useMemo(() => { - const hasDedicated = displayTypes.some( - (type) => type.class === 'dedicated' - ); - const currentChip = ( - + const initialTab = determineInitialPlanCategoryTab( + displayTypes, + database.type, + currentPlan?.heading ); - const isDisabled = (nodeSize: ClusterSize) => { - return nodeSize < database.cluster_size; - }; - - const options = [ - { - label: ( - - 1 Node {` `} - {database.cluster_size === 1 && currentChip} -
- - {`$${nodePricing?.single?.monthly || 0}/month $${ - nodePricing?.single?.hourly || 0 - }/hr`} - -
- ), - value: 1, - }, - ]; - - if (hasDedicated && selectedTab === 0 && isDatabasesV2Enabled) { - options.push({ - label: ( - - 2 Nodes - High Availability - {database.cluster_size === 2 && currentChip} -
- - {`$${nodePricing?.double?.monthly || 0}/month $${ - nodePricing?.double?.hourly || 0 - }/hr`} - -
- ), - value: 2, - }); + if (isNewDatabaseGA) { + if (initialTab === index) { + setSelectedPlanId(database.type); + setClusterSize(database.cluster_size); + } else { + setClusterSize(undefined); + setSelectedPlanId(undefined); + } } - - options.push({ - label: ( - - 3 Nodes - High Availability (recommended) - {database.cluster_size === 3 && currentChip} -
- - {`$${nodePricing?.multi?.monthly || 0}/month $${ - nodePricing?.multi?.hourly || 0 - }/hr`} - -
- ), - value: 3, - }); - - return options; - }, [selectedTab, nodePricing, displayTypes, isDatabasesV2Enabled]); + setSelectedTab(index); + }; if (typesLoading) { return ; @@ -498,6 +260,7 @@ export const DatabaseResize = ({ database, disabled = false }: Props) => { if (typesError) { return ; } + return ( <> @@ -515,56 +278,41 @@ export const DatabaseResize = ({ database, disabled = false }: Props) => { disabledTabs={isDisabledSharedTab ? ['shared'] : []} handleTabChange={handleTabChange} header="Choose a Plan" - onSelect={(selected: string) => setPlanSelected(selected)} - selectedId={planSelected} - tabDisabledMessage="Resizing a 2-nodes cluster is only allowed with Dedicated plans." + onSelect={(selected: string) => setSelectedPlanId(selected)} + selectedId={selectedPlanId} + tabDisabledMessage="Resizing a 2-node cluster is only allowed with Dedicated plans." types={displayTypes} /> - {isDatabasesV2GA && ( + {isNewDatabaseGA && ( <> - - - Set Number of Nodes{' '} - - - We recommend 3 nodes in a database cluster to avoid downtime - during upgrades and maintenance. - - - - {nodeOptions.map((nodeOption) => ( - } - data-qa-radio={nodeOption.label} - data-testid={`database-node-${nodeOption.value}`} - disabled={nodeOption.value < database.cluster_size} - key={nodeOption.value} - label={nodeOption.label} - value={nodeOption.value} - /> - ))} - + { + handleNodeChange(size); + }} + selectedPlan={displayTypes?.find( + (type) => type.id === selectedPlanId + )} + currentClusterSize={database.cluster_size} + currentPlan={currentPlan} + displayTypes={displayTypes} + selectedClusterSize={clusterSize} + selectedEngine={selectedEngine} + selectedTab={selectedTab} + /> )} - ({ - marginBottom: isDatabasesV2GA ? theme.spacing(2) : 0, - })} - variant="h2" - > - Summary {isDatabasesV2GA ? database.label : ''} - - {isDatabasesV2GA && currentPlan ? currentSummary : null} - {resizeSummary} + import('./DatabaseSummary')); const DatabaseBackups = React.lazy( @@ -72,11 +72,6 @@ export const DatabaseDetail = () => { setEditableLabelError, } = useEditableLabelState(); - const { - isDatabasesMonitorEnabled, - isDatabasesMonitorBeta, - } = useIsDatabasesEnabled(); - if (error) { return ( { } const isDefault = database.platform === 'rdbms-default'; - const isMonitorEnabled = isDefault && isDatabasesMonitorEnabled; + const isMonitorEnabled = isDefault && flags.dbaasV2MonitorMetrics?.enabled; const tabs: Tab[] = [ { @@ -118,9 +113,9 @@ export const DatabaseDetail = () => { if (isMonitorEnabled) { tabs.splice(1, 0, { + chip: flags.dbaasV2MonitorMetrics?.beta ? : null, routeName: `/databases/${engine}/${id}/monitor`, title: 'Monitor', - chip: isDatabasesMonitorBeta ? : null, }); } diff --git a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLanding.tsx b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLanding.tsx index 80c6bb91b6d..d66ca38c4e0 100644 --- a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLanding.tsx +++ b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLanding.tsx @@ -181,7 +181,7 @@ const DatabaseLanding = () => { onButtonClick={() => history.push('/databases/create')} title="Database Clusters" /> - {showTabs && } + {showTabs && !isDatabasesV2GA && } {showTabs ? ( diff --git a/packages/manager/src/features/Databases/utilities.ts b/packages/manager/src/features/Databases/utilities.ts index 4da443b71e6..9f01754f3fa 100644 --- a/packages/manager/src/features/Databases/utilities.ts +++ b/packages/manager/src/features/Databases/utilities.ts @@ -15,8 +15,6 @@ import type { DatabaseFork } from '@linode/api-v4'; export interface IsDatabasesEnabled { isDatabasesEnabled: boolean; - isDatabasesMonitorBeta?: boolean; - isDatabasesMonitorEnabled?: boolean; isDatabasesV2Beta: boolean; isDatabasesV2Enabled: boolean; isDatabasesV2GA: boolean; From f8d33b51bb70604c3ca73b98c0a183bd1e1a108b Mon Sep 17 00:00:00 2001 From: smans-akamai Date: Tue, 5 Nov 2024 15:08:49 -0500 Subject: [PATCH 48/66] feat: [UIE-8090] - DBaaS Settings and Landing Page (#11152) * feat: [UIE-8090] - DBaaS Settings and Landing Page * UIE-8090: Add suspend and resume to Landing Page and details Settings view for GA * feat: [UIE-8090] - DBaaS Settings and Landing Page * UIE-8090: Add suspend and resume to Landing Page and details Settings view for GA * feat: [UIE-8090] - DBaaS Settings and Landing Page * UIE-8090: Add suspend and resume to Landing Page and details Settings view for GA * fix tests that broke as result of merge mistake --------- Co-authored-by: Richard O'Donnell Co-authored-by: corya-akamai <136115382+corya-akamai@users.noreply.github.com> --- .../pr-11152-added-1729713487291.md | 5 + packages/api-v4/src/databases/databases.ts | 30 +++++ .../pr-11152-added-1729713452489.md | 5 + .../DatabaseSettings.test.tsx | 104 +++++++++++++++++- .../DatabaseSettings/DatabaseSettings.tsx | 37 +++++++ ...abaseSettingsSuspendClusterDialog.test.tsx | 86 +++++++++++++++ .../DatabaseSettingsSuspendClusterDialog.tsx | 103 +++++++++++++++++ .../DatabaseLanding/DatabaseActionMenu.tsx | 66 +++++++++-- .../DatabaseLanding/DatabaseLanding.tsx | 2 + .../DatabaseLanding/DatabaseLandingTable.tsx | 20 ++++ .../Databases/DatabaseLanding/DatabaseRow.tsx | 10 +- packages/manager/src/mocks/serverHandlers.ts | 8 ++ .../src/queries/databases/databases.ts | 32 ++++++ 13 files changed, 497 insertions(+), 11 deletions(-) create mode 100644 packages/api-v4/.changeset/pr-11152-added-1729713487291.md create mode 100644 packages/manager/.changeset/pr-11152-added-1729713452489.md create mode 100644 packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsSuspendClusterDialog.test.tsx create mode 100644 packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsSuspendClusterDialog.tsx diff --git a/packages/api-v4/.changeset/pr-11152-added-1729713487291.md b/packages/api-v4/.changeset/pr-11152-added-1729713487291.md new file mode 100644 index 00000000000..2a2770ce30c --- /dev/null +++ b/packages/api-v4/.changeset/pr-11152-added-1729713487291.md @@ -0,0 +1,5 @@ +--- +"@linode/api-v4": Added +--- + +DBaaS Suspend and Resume backend calls ([#11152](https://github.com/linode/manager/pull/11152)) diff --git a/packages/api-v4/src/databases/databases.ts b/packages/api-v4/src/databases/databases.ts index a735955cdd6..374ed3c6519 100644 --- a/packages/api-v4/src/databases/databases.ts +++ b/packages/api-v4/src/databases/databases.ts @@ -316,3 +316,33 @@ export const getSSLFields = (engine: Engine, databaseID: number) => ), setMethod('GET') ); + +/** + * suspendDatabase + * + * Suspend the specified database cluster + */ +export const suspendDatabase = (engine: Engine, databaseID: number) => + Request<{}>( + setURL( + `${API_ROOT}/databases/${encodeURIComponent( + engine + )}/instances/${encodeURIComponent(databaseID)}/suspend` + ), + setMethod('POST') + ); + +/** + * resumeDatabase + * + * Resume the specified database cluster + */ +export const resumeDatabase = (engine: Engine, databaseID: number) => + Request<{}>( + setURL( + `${API_ROOT}/databases/${encodeURIComponent( + engine + )}/instances/${encodeURIComponent(databaseID)}/resume` + ), + setMethod('POST') + ); diff --git a/packages/manager/.changeset/pr-11152-added-1729713452489.md b/packages/manager/.changeset/pr-11152-added-1729713452489.md new file mode 100644 index 00000000000..b16be182d16 --- /dev/null +++ b/packages/manager/.changeset/pr-11152-added-1729713452489.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Added +--- + +DBaaS Suspend and Resume for Database Landing and Details ([#11152](https://github.com/linode/manager/pull/11152)) diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.test.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.test.tsx index 6148b7cff14..1ac650f0371 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.test.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.test.tsx @@ -1,9 +1,12 @@ import * as React from 'react'; import { databaseFactory } from 'src/factories/databases'; -import { renderWithTheme } from 'src/utilities/testHelpers'; +import { mockMatchMedia, renderWithTheme } from 'src/utilities/testHelpers'; import DatabaseSettings from './DatabaseSettings'; +import * as utils from '../../utilities'; + +beforeAll(() => mockMatchMedia()); describe('DatabaseSettings Component', () => { const database = databaseFactory.build(); @@ -70,4 +73,103 @@ describe('DatabaseSettings Component', () => { expect(queryByText('Weekly')).toBeNull(); expect(queryByText('Set a Weekly Maintenance Window')).toBeTruthy(); }); + + it('should render suspend option when isDatabasesV2GA flag is true', async () => { + const flags = { + dbaasV2: { + beta: false, + enabled: true, + }, + }; + const mockNewDatabase = databaseFactory.build({ + platform: 'rdbms-default', + }); + + const spy = vi.spyOn(utils, 'useIsDatabasesEnabled'); + spy.mockReturnValue({ + isDatabasesEnabled: true, + isDatabasesV2Beta: false, + isDatabasesV2Enabled: true, + isDatabasesV2GA: true, + isUserExistingBeta: false, + isUserNewBeta: false, + }); + + const { container, getAllByRole } = renderWithTheme( + , + { flags } + ); + const paper = container.querySelector('.MuiPaper-root'); + expect(paper).not.toBeNull(); + const headings = getAllByRole('heading'); + + expect(headings[0].textContent).toBe('Suspend Cluster'); + expect(headings[1].textContent).toBe('Manage Access'); + expect(headings[2].textContent).toBe('Reset the Root Password'); + expect(headings[3].textContent).toBe('Delete the Cluster'); + }); + + it('should disable suspend when database status is not active', async () => { + const flags = { + dbaasV2: { + beta: false, + enabled: true, + }, + }; + const mockNewDatabase = databaseFactory.build({ + platform: 'rdbms-default', + status: 'resizing', + }); + + const spy = vi.spyOn(utils, 'useIsDatabasesEnabled'); + spy.mockReturnValue({ + isDatabasesEnabled: true, + isDatabasesV2Beta: false, + isDatabasesV2Enabled: true, + isDatabasesV2GA: true, + isUserExistingBeta: false, + isUserNewBeta: false, + }); + + const { getAllByText } = renderWithTheme( + , + { flags } + ); + + const suspendElements = getAllByText(/Suspend Cluster/i); + const suspendButton = suspendElements[1].closest('button'); + expect(suspendButton).toHaveAttribute('aria-disabled', 'true'); + }); + + it('should enable suspend when database status is active', async () => { + const flags = { + dbaasV2: { + beta: false, + enabled: true, + }, + }; + const mockNewDatabase = databaseFactory.build({ + platform: 'rdbms-default', + status: 'active', + }); + + const spy = vi.spyOn(utils, 'useIsDatabasesEnabled'); + spy.mockReturnValue({ + isDatabasesEnabled: true, + isDatabasesV2Beta: false, + isDatabasesV2Enabled: true, + isDatabasesV2GA: true, + isUserExistingBeta: false, + isUserNewBeta: false, + }); + + const { getAllByText } = renderWithTheme( + , + { flags } + ); + + const suspendElements = getAllByText(/Suspend Cluster/i); + const suspendButton = suspendElements[1].closest('button'); + expect(suspendButton).toHaveAttribute('aria-disabled', 'false'); + }); }); diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx index 2c7f4bcc22c..fcf44207c2b 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx @@ -12,6 +12,8 @@ import DatabaseSettingsResetPasswordDialog from './DatabaseSettingsResetPassword import MaintenanceWindow from './MaintenanceWindow'; import type { Database } from '@linode/api-v4/lib/databases/types'; +import { DatabaseSettingsSuspendClusterDialog } from './DatabaseSettingsSuspendClusterDialog'; +import { useIsDatabasesEnabled } from '../../utilities'; interface Props { database: Database; @@ -21,6 +23,7 @@ interface Props { export const DatabaseSettings: React.FC = (props) => { const { database, disabled } = props; const { data: profile } = useProfile(); + const { isDatabasesV2GA } = useIsDatabasesEnabled(); const accessControlCopy = ( @@ -30,6 +33,9 @@ export const DatabaseSettings: React.FC = (props) => { ); const isLegacy = database.platform === 'rdbms-legacy'; + const isDefault = database.platform === 'rdbms-default'; + + const suspendClusterCopy = `Suspend the cluster if you don't use it temporarily to prevent being billed for it.`; const resetRootPasswordCopy = isLegacy ? 'Resetting your root password will automatically generate a new password. You can view the updated password on your database cluster summary page. ' @@ -44,6 +50,10 @@ export const DatabaseSettings: React.FC = (props) => { isResetRootPasswordDialogOpen, setIsResetRootPasswordDialogOpen, ] = React.useState(false); + const [ + isSuspendClusterDialogOpen, + setIsSuspendClusterDialogOpen, + ] = React.useState(false); const onResetRootPassword = () => { setIsResetRootPasswordDialogOpen(true); @@ -53,6 +63,10 @@ export const DatabaseSettings: React.FC = (props) => { setIsDeleteDialogOpen(true); }; + const onSuspendCluster = () => { + setIsSuspendClusterDialogOpen(true); + }; + const onDeleteClusterClose = () => { setIsDeleteDialogOpen(false); }; @@ -61,9 +75,25 @@ export const DatabaseSettings: React.FC = (props) => { setIsResetRootPasswordDialogOpen(false); }; + const onSuspendDialogClose = () => { + setIsSuspendClusterDialogOpen(false); + }; + return ( <> + {isDatabasesV2GA && isDefault && ( + <> + + + + )} = (props) => { onClose={onResetRootPasswordClose} open={isResetRootPasswordDialogOpen} /> + ); }; diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsSuspendClusterDialog.test.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsSuspendClusterDialog.test.tsx new file mode 100644 index 00000000000..6904bdc83b3 --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsSuspendClusterDialog.test.tsx @@ -0,0 +1,86 @@ +import { waitFor } from '@testing-library/react'; +import * as React from 'react'; +import { renderWithTheme } from 'src/utilities/testHelpers'; + +import { + DatabaseSettingsSuspendClusterDialog, + SuspendDialogProps, +} from './DatabaseSettingsSuspendClusterDialog'; +import { Engine } from '@linode/api-v4'; +import userEvent from '@testing-library/user-event'; +import { http, HttpResponse, server } from 'src/mocks/testServer'; + +const mockEngine: Engine = 'mysql'; +const mockLabel = 'database-1'; +const props: SuspendDialogProps = { + databaseEngine: mockEngine, + databaseId: 1234, + databaseLabel: mockLabel, + onClose: vi.fn(), + open: true, +}; + +describe('DatabaseSettingsSuspendClusterDialog', () => { + it('renders the dialog with text', () => { + const { getByTestId, getByText } = renderWithTheme( + + ); + expect(getByText(`Suspend ${mockLabel} cluster?`)).toBeVisible(); + expect(getByText('Suspend Cluster')).toBeVisible(); + expect(getByTestId('CloseIcon')).toBeVisible(); + }); + + it('should initialize with unchecked checkbox and disabled submit button', async () => { + const { getByRole, getByText } = renderWithTheme( + + ); + const confirmationCheckbox = getByRole('checkbox') as HTMLInputElement; + const suspendButton = getByText(/Suspend Cluster/i).closest('button'); + expect(confirmationCheckbox.checked).toBeFalsy(); + expect(suspendButton).toHaveAttribute('aria-disabled', 'true'); + }); + + it('should enable submit button when checkbox is checked', async () => { + const { getByRole, getByText } = renderWithTheme( + + ); + const confirmationCheckbox = getByRole('checkbox') as HTMLInputElement; + const suspendButton = getByText(/Suspend Cluster/i).closest('button'); + await userEvent.click(confirmationCheckbox); + expect(confirmationCheckbox.checked).toBeTruthy(); + expect(suspendButton).toHaveAttribute('aria-disabled', 'false'); + }); + + it('should call onClose after suspend call is successful', async () => { + server.use( + http.post(`*/databases/${encodeURIComponent(mockEngine)}/suspend`, () => { + return HttpResponse.json({}); + }) + ); + const { getByText, getByRole } = renderWithTheme( + + ); + const confirmationCheckbox = getByRole('checkbox') as HTMLInputElement; + const suspendButton = getByText(/Suspend Cluster/i).closest( + 'button' + ) as HTMLButtonElement; + + await userEvent.click(confirmationCheckbox); + await userEvent.click(suspendButton); + await waitFor(() => { + expect(props.onClose).toBeCalled(); + }); + }); + + it('closes the confirmaton dialog if the X button is clicked', async () => { + const { getByTestId } = renderWithTheme( + + ); + + const closeButton = getByTestId('CloseIcon'); + expect(closeButton).toBeVisible(); + + await userEvent.click(closeButton); + expect(props.onClose).toHaveBeenCalled(); + }); +}); diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsSuspendClusterDialog.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsSuspendClusterDialog.tsx new file mode 100644 index 00000000000..9b2e125404b --- /dev/null +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsSuspendClusterDialog.tsx @@ -0,0 +1,103 @@ +import { Engine } from '@linode/api-v4/lib/databases'; +import { useSnackbar } from 'notistack'; +import * as React from 'react'; +import { useHistory } from 'react-router-dom'; +import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; +import { Checkbox } from 'src/components/Checkbox'; +import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog'; +import { Notice } from 'src/components/Notice/Notice'; +import { Typography } from 'src/components/Typography'; +import { useSuspendDatabaseMutation } from 'src/queries/databases/databases'; +import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; + +export interface SuspendDialogProps { + databaseEngine: Engine; + databaseId: number; + databaseLabel: string; + onClose: () => void; + open: boolean; +} + +export const DatabaseSettingsSuspendClusterDialog = ( + props: SuspendDialogProps +) => { + const { databaseEngine, databaseId, databaseLabel, onClose, open } = props; + const { enqueueSnackbar } = useSnackbar(); + const { + mutateAsync: suspendDatabase, + isPending, + error, + reset, + } = useSuspendDatabaseMutation(databaseEngine, databaseId); + + const defaultError = 'There was an error suspending this Database Cluster.'; + const [hasConfirmed, setHasConfirmed] = React.useState(false); + const { push } = useHistory(); + + const onSuspendCluster = async () => { + try { + await suspendDatabase(); + enqueueSnackbar('Database Cluster suspended successfully.', { + variant: 'success', + }); + onClose(); + push('/databases'); + } catch (error) { + enqueueSnackbar('Failed to suspend Database Cluster. Please try again.', { + variant: 'error', + }); + } finally { + setHasConfirmed(false); + } + }; + + const onCancel = () => { + onClose(); + reset(); + setHasConfirmed(false); + }; + + const suspendClusterCopy = `A suspended cluster stops working immediately and you won't be billed for it. + You can resume the clusters work within 180 days from its suspension. + After that time, the cluster will be deleted permanently.`; + + const actions = ( + + ); + + return ( + + + + {suspendClusterCopy} + + + setHasConfirmed((confirmed) => !confirmed)} + text="I understand the effects of this action." + /> + + ); +}; diff --git a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseActionMenu.tsx b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseActionMenu.tsx index c5790e70432..0c23b42fe17 100644 --- a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseActionMenu.tsx +++ b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseActionMenu.tsx @@ -3,36 +3,66 @@ import { useHistory } from 'react-router-dom'; import { ActionMenu } from 'src/components/ActionMenu/ActionMenu'; -import type { Engine } from '@linode/api-v4'; +import type { DatabaseStatus, Engine } from '@linode/api-v4'; import type { Action } from 'src/components/ActionMenu/ActionMenu'; +import { useIsDatabasesEnabled } from '../utilities'; +import { useResumeDatabaseMutation } from 'src/queries/databases/databases'; +import { enqueueSnackbar } from 'notistack'; +import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; interface Props { databaseEngine: Engine; databaseId: number; databaseLabel: string; handlers: ActionHandlers; + databaseStatus: DatabaseStatus; } export interface ActionHandlers { handleDelete: () => void; handleManageAccessControls: () => void; handleResetPassword: () => void; + handleSuspend: () => void; } export const DatabaseActionMenu = (props: Props) => { - const { databaseEngine, databaseId, databaseLabel, handlers } = props; + const { + databaseEngine, + databaseId, + databaseLabel, + handlers, + databaseStatus, + } = props; - const databaseStatus = 'running'; - const isDatabaseNotRunning = databaseStatus !== 'running'; + const { isDatabasesV2GA } = useIsDatabasesEnabled(); + const { mutateAsync: resumeDatabase } = useResumeDatabaseMutation( + databaseEngine, + databaseId + ); + + const status = 'running'; + const isDatabaseNotRunning = status !== 'running'; + const isDatabaseSuspended = + databaseStatus === 'suspended' || databaseStatus === 'suspending'; const history = useHistory(); + const handleResume = async () => { + try { + await resumeDatabase(); + return enqueueSnackbar('Database Cluster resumed successfully.', { + variant: 'success', + }); + } catch (e: any) { + const error = getAPIErrorOrDefault( + e, + 'There was an error resuming this Database Cluster.' + )[0].reason; + return enqueueSnackbar(error, { variant: 'error' }); + } + }; + const actions: Action[] = [ - // TODO: add suspend action menu item once it's ready - // { - // onClick: () => {}, - // title: databaseStatus === 'running' ? 'Suspend' : 'Power On', - // }, { disabled: isDatabaseNotRunning, onClick: handlers.handleManageAccessControls, @@ -59,6 +89,24 @@ export const DatabaseActionMenu = (props: Props) => { }, ]; + if (isDatabasesV2GA) { + actions.unshift({ + disabled: databaseStatus !== 'active', + onClick: () => { + handlers.handleSuspend(); + }, + title: 'Suspend', + }); + + actions.splice(4, 0, { + disabled: !isDatabaseSuspended, + onClick: () => { + handleResume(); + }, + title: 'Resume', + }); + } + return ( { const isV2Enabled = isDatabasesV2Enabled || isDatabasesV2GA; const showTabs = isV2Enabled && !!legacyDatabases?.data.length; const isNewDatabase = isV2Enabled && !!newDatabases?.data.length; + const showSuspend = isDatabasesV2GA && !!newDatabases?.data.length; const legacyTable = () => { return ( @@ -155,6 +156,7 @@ const DatabaseLanding = () => { data={newDatabases?.data} handleOrderChange={newDatabaseHandleOrderChange} isNewDatabase={true} + showSuspend={showSuspend} order={newDatabaseOrder} orderBy={newDatabaseOrderBy} /> diff --git a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLandingTable.tsx b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLandingTable.tsx index 3dbc125ad59..37d8ffa67bc 100644 --- a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLandingTable.tsx +++ b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLandingTable.tsx @@ -20,6 +20,7 @@ import { useInProgressEvents } from 'src/queries/events/events'; import type { DatabaseInstance } from '@linode/api-v4/lib/databases'; import type { Order } from 'src/hooks/useOrder'; +import { DatabaseSettingsSuspendClusterDialog } from '../DatabaseDetail/DatabaseSettings/DatabaseSettingsSuspendClusterDialog'; const preferenceKey = 'databases'; @@ -29,6 +30,7 @@ interface Props { isNewDatabase?: boolean; order: 'asc' | 'desc'; orderBy: string; + showSuspend?: boolean; } const DatabaseLandingTable = ({ data, @@ -36,6 +38,7 @@ const DatabaseLandingTable = ({ isNewDatabase, order, orderBy, + showSuspend, }: Props) => { const { data: events } = useInProgressEvents(); const { isDatabasesV2GA } = useIsDatabasesEnabled(); @@ -56,6 +59,10 @@ const DatabaseLandingTable = ({ isManageAccessControlsDialogOpen, setIsManageAccessControlsDialogOpen, ] = React.useState(false); + const [ + isSuspendClusterDialogOpen, + setIsSuspendClusterDialogOpen, + ] = React.useState(false); const handleManageAccessControls = (database: DatabaseInstance) => { setSelectedDatabase(database); @@ -77,6 +84,11 @@ const DatabaseLandingTable = ({ setIsResetPasswordsDialogOpen(true); }; + const handleSuspend = (database: DatabaseInstance) => { + setSelectedDatabase(database); + setIsSuspendClusterDialogOpen(true); + }; + return ( <>
@@ -157,6 +169,7 @@ const DatabaseLandingTable = ({ handleManageAccessControls: () => handleManageAccessControls(database), handleResetPassword: () => handleResetPassword(database), + handleSuspend: () => handleSuspend(database), }} database={database} events={events} @@ -206,6 +219,13 @@ const DatabaseLandingTable = ({ )} )} + setIsSuspendClusterDialogOpen(false)} + open={isSuspendClusterDialogOpen} + /> t.id === type); const formattedPlan = plan && formatStorageUnits(plan.label); const actualRegion = regions?.find((r) => r.id === region); + const isLinkInactive = + status === 'suspended' || status === 'suspending' || status === 'resuming'; const { isDatabasesV2GA } = useIsDatabasesEnabled(); const configuration = @@ -85,7 +88,11 @@ export const DatabaseRow = ({ return ( - {label} + {isDatabasesV2GA && isLinkInactive ? ( + label + ) : ( + {label} + )} @@ -110,6 +117,7 @@ export const DatabaseRow = ({ {isDatabasesV2GA && isNewDatabase && ( { return HttpResponse.json({}); }), + + http.post('*/databases/:engine/instances/:databaseId/suspend', () => { + return HttpResponse.json({}); + }), + + http.post('*/databases/:engine/instances/:databaseId/resume', () => { + return HttpResponse.json({}); + }), ]; const vpc = [ diff --git a/packages/manager/src/queries/databases/databases.ts b/packages/manager/src/queries/databases/databases.ts index 6e958cd7bd8..ebc176b45c5 100644 --- a/packages/manager/src/queries/databases/databases.ts +++ b/packages/manager/src/queries/databases/databases.ts @@ -9,6 +9,8 @@ import { patchDatabase, resetDatabaseCredentials, restoreWithBackup, + resumeDatabase, + suspendDatabase, updateDatabase, } from '@linode/api-v4/lib/databases'; import { createQueryKeys } from '@lukemorales/query-key-factory'; @@ -188,6 +190,36 @@ export const useDeleteDatabaseMutation = (engine: Engine, id: number) => { }); }; +export const useSuspendDatabaseMutation = (engine: Engine, id: number) => { + const queryClient = useQueryClient(); + return useMutation<{}, APIError[]>({ + mutationFn: () => suspendDatabase(engine, id), + onSuccess() { + queryClient.invalidateQueries({ + queryKey: databaseQueries.databases.queryKey, + }); + queryClient.invalidateQueries({ + queryKey: databaseQueries.database(engine, id).queryKey, + }); + }, + }); +}; + +export const useResumeDatabaseMutation = (engine: Engine, id: number) => { + const queryClient = useQueryClient(); + return useMutation<{}, APIError[]>({ + mutationFn: () => resumeDatabase(engine, id), + onSuccess() { + queryClient.invalidateQueries({ + queryKey: databaseQueries.databases.queryKey, + }); + queryClient.invalidateQueries({ + queryKey: databaseQueries.database(engine, id).queryKey, + }); + }, + }); +}; + export const useDatabaseBackupsQuery = ( engine: Engine, id: number, From 4ec07b0eb4e0465c272deb4f7f4075437933a916 Mon Sep 17 00:00:00 2001 From: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:31:55 -0800 Subject: [PATCH 49/66] fix, test: [M3-8830] - Use unit tested function for Pendo url transformation (#11211) * Use transform function and unit test it * Use singular function name * Added changeset: Use unit tested function for Pendo url transformation * Address feedback @coliu-akamai: add missing test case * Address feedback @hkhalil-akamai: remove unnecessary conditionals --- .../pr-11211-tech-stories-1730836470267.md | 5 + packages/manager/src/hooks/usePendo.test.ts | 102 ++++++++++++++++++ packages/manager/src/hooks/usePendo.ts | 50 +++++---- 3 files changed, 138 insertions(+), 19 deletions(-) create mode 100644 packages/manager/.changeset/pr-11211-tech-stories-1730836470267.md create mode 100644 packages/manager/src/hooks/usePendo.test.ts diff --git a/packages/manager/.changeset/pr-11211-tech-stories-1730836470267.md b/packages/manager/.changeset/pr-11211-tech-stories-1730836470267.md new file mode 100644 index 00000000000..0a46b375401 --- /dev/null +++ b/packages/manager/.changeset/pr-11211-tech-stories-1730836470267.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tech Stories +--- + +Use unit tested function for Pendo url transformation ([#11211](https://github.com/linode/manager/pull/11211)) diff --git a/packages/manager/src/hooks/usePendo.test.ts b/packages/manager/src/hooks/usePendo.test.ts new file mode 100644 index 00000000000..3699979c033 --- /dev/null +++ b/packages/manager/src/hooks/usePendo.test.ts @@ -0,0 +1,102 @@ +import { transformUrl } from './usePendo'; + +const ID_URLS = [ + { + expectedTransform: 'https://cloud.linode.com/nodebalancers/XXXX', + position: 'end', + url: 'https://cloud.linode.com/nodebalancers/123', + }, + { + expectedTransform: + 'https://cloud.linode.com/nodebalancers/XXXX/configurations', + position: 'middle', + url: 'https://cloud.linode.com/nodebalancers/123/configurations', + }, + { + expectedTransform: + 'https://cloud.linode.com/nodebalancers/XXXX/configurations/XXXX', + position: 'multiple', + url: 'https://cloud.linode.com/nodebalancers/123/configurations/456', + }, +]; + +const USERNAME_URLS = [ + { + path: 'my-username', + }, + { + path: 'my-username/profile', + }, + { + path: 'my-username/permissions', + }, + { + path: '123-my-username/profile', + }, +]; + +const OBJ_URLS = [ + { + expectedTransform: + 'http://cloud.linode.com/object-storage/buckets/XXXX/XXXX', + path: 'us-west/abc123', + }, + { + expectedTransform: + 'http://cloud.linode.com/object-storage/buckets/XXXX/XXXX/ssl', + path: 'us-west/abc123/ssl', + }, + { + expectedTransform: + 'http://cloud.linode.com/object-storage/buckets/XXXX/XXXX', + path: 'us-west/123abc', + }, + { + expectedTransform: + 'http://cloud.linode.com/object-storage/buckets/XXXX/XXXX/access', + path: 'us-west/123abc/access', + }, +]; + +describe('transformUrl', () => { + it.each(ID_URLS)( + 'replaces id(s) in $position positions in the url path', + ({ expectedTransform, url }) => { + const actualTransform = transformUrl(url); + expect(actualTransform).toEqual(expectedTransform); + } + ); + + it.each(USERNAME_URLS)( + 'truncates $path from the /users url path', + ({ path }) => { + const baseUrl = 'https://cloud.linode.com/account/users/'; + const actualTransform = transformUrl(`${baseUrl}${path}`); + expect(actualTransform).toEqual(baseUrl); + } + ); + + it.each(OBJ_URLS)( + 'replaces the OBJ region and bucket name in the url path ($path)', + ({ expectedTransform, path }) => { + const baseUrl = 'http://cloud.linode.com/object-storage/buckets/'; + const actualTransform = transformUrl(`${baseUrl}${path}`); + expect(actualTransform).toEqual(expectedTransform); + } + ); + + it('truncates the url after "access_token" in the url path', () => { + const url = + 'https://cloud.linode.com/oauth/callback#access_token=12345&token_type=bearer&expires_in=5678'; + const actualTransform = transformUrl(url); + const expectedTransform = + 'https://cloud.linode.com/oauth/callback#access_token'; + expect(actualTransform).toEqual(expectedTransform); + }); + + it('returns the original url if no transformation is needed', () => { + const url = 'https://cloud.linode.com/linodes/create'; + const actualTransform = transformUrl(url); + expect(actualTransform).toEqual(url); + }); +}); diff --git a/packages/manager/src/hooks/usePendo.ts b/packages/manager/src/hooks/usePendo.ts index 898b5dc8567..d15c4f435f8 100644 --- a/packages/manager/src/hooks/usePendo.ts +++ b/packages/manager/src/hooks/usePendo.ts @@ -29,6 +29,36 @@ const hashUniquePendoId = (id: string | undefined) => { return sha256(id + pendoEnv); }; +/** + * This function uses string matching and replacement to transform the page url into a sanitized url without unwanted data. + * @param url The url of the page. + * @returns A clean, transformed url of the page. + */ +export const transformUrl = (url: string) => { + const idMatchingRegex = /(\/\d+)/g; + const bucketPathMatchingRegex = /(buckets\/[^\/]+\/[^\/]+)/; + const userPathMatchingRegex = /(users\/).*/; + const oauthPathMatchingRegex = /(#access_token).*/; + let transformedUrl = url; + + // Replace any ids with XXXX and keep the rest of the URL intact + transformedUrl = url.replace(idMatchingRegex, '/XXXX'); + + // Replace the region and bucket names with XXXX and keep the rest of the URL intact. + // Object storage file navigation is truncated via the 'clear search' transform. + transformedUrl = transformedUrl.replace( + bucketPathMatchingRegex, + 'buckets/XXXX/XXXX' + ); + + // Remove everything after access_token + transformedUrl = transformedUrl.replace(oauthPathMatchingRegex, '$1'); + + // Remove everything after /users + transformedUrl = transformedUrl.replace(userPathMatchingRegex, '$1'); + return transformedUrl; +}; + /** * Initializes our Pendo analytics script on mount if a valid `PENDO_API_KEY` exists. */ @@ -105,25 +135,7 @@ export const usePendo = () => { action: 'Replace', attr: 'pathname', data(url: string) { - const idMatchingRegex = /(\/\d+)/; - const bucketPathMatchingRegex = /(buckets\/[^\/]+\/[^\/]+)/; - const userPathMatchingRegex = /(users\/).*/; - const oauthPathMatchingRegex = /(#access_token).*/; - - if (idMatchingRegex.test(url)) { - // Replace any ids with XXXX and keep the rest of the URL intact - return url.replace(idMatchingRegex, '/XXXX'); - } else if (bucketPathMatchingRegex.test(url)) { - // Replace the region and bucket names with XXXX and keep the rest of the URL intact - return url.replace(bucketPathMatchingRegex, 'XXXX/XXXX'); - } else if (oauthPathMatchingRegex.test(url)) { - // Remove everything after access_token/ - url.replace(oauthPathMatchingRegex, '$1'); - } else if (userPathMatchingRegex.test(url)) { - // Remove everything after /users - return url.replace(userPathMatchingRegex, '$1'); - } - return url; + return transformUrl(url); }, }, ], From 6a5cc86c0979898e6a511b28476c5fb1746e7266 Mon Sep 17 00:00:00 2001 From: corya-akamai <136115382+corya-akamai@users.noreply.github.com> Date: Wed, 6 Nov 2024 10:34:49 -0500 Subject: [PATCH 50/66] feat: [UIE-8194] - DBaaS major and minor upgrades - 4 (#11199) --- ...r-11199-upcoming-features-1730465428837.md | 5 + .../Databases/DatabaseCreate/utilities.tsx | 10 +- .../DatabaseResizeCurrentConfiguration.tsx | 15 +- .../DatabaseSettings.test.tsx | 149 +++++++++++++++++- .../DatabaseSettings/DatabaseSettings.tsx | 75 +++++++-- .../DatabaseSummaryClusterConfiguration.tsx | 10 +- ...abaseSummaryClusterConfigurationLegacy.tsx | 10 +- .../Databases/DatabaseLanding/DatabaseRow.tsx | 21 +-- 8 files changed, 256 insertions(+), 39 deletions(-) create mode 100644 packages/manager/.changeset/pr-11199-upcoming-features-1730465428837.md diff --git a/packages/manager/.changeset/pr-11199-upcoming-features-1730465428837.md b/packages/manager/.changeset/pr-11199-upcoming-features-1730465428837.md new file mode 100644 index 00000000000..d971b6407b5 --- /dev/null +++ b/packages/manager/.changeset/pr-11199-upcoming-features-1730465428837.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +DBaaS major minor updates integration ([#11199](https://github.com/linode/manager/pull/11199)) diff --git a/packages/manager/src/features/Databases/DatabaseCreate/utilities.tsx b/packages/manager/src/features/Databases/DatabaseCreate/utilities.tsx index ec405a84c58..f7d560d2ced 100644 --- a/packages/manager/src/features/Databases/DatabaseCreate/utilities.tsx +++ b/packages/manager/src/features/Databases/DatabaseCreate/utilities.tsx @@ -4,8 +4,7 @@ import React from 'react'; import MongoDBIcon from 'src/assets/icons/mongodb.svg'; import MySQLIcon from 'src/assets/icons/mysql.svg'; import PostgreSQLIcon from 'src/assets/icons/postgresql.svg'; - -import { databaseEngineMap } from '../DatabaseLanding/DatabaseRow'; +import { getDatabasesDescription } from 'src/features/Databases/utilities'; import type { DatabaseEngine } from '@linode/api-v4'; @@ -101,9 +100,10 @@ export const getEngineOptions = (engines: DatabaseEngine[]) => { .map((engineObject) => ({ ...engineObject, flag: engineIcons[engineObject.engine], - label: `${databaseEngineMap[engineObject.engine]} v${ - engineObject.version - }`, + label: getDatabasesDescription({ + engine: engineObject.engine, + version: engineObject.version, + }), value: `${engineObject.engine}/${engineObject.version}`, })) .sort((a, b) => (a.version > b.version ? -1 : 1)), diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResizeCurrentConfiguration.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResizeCurrentConfiguration.tsx index eba42f15ce6..a45c1814922 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResizeCurrentConfiguration.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseResize/DatabaseResizeCurrentConfiguration.tsx @@ -5,13 +5,13 @@ import * as React from 'react'; import { CircleProgress } from 'src/components/CircleProgress'; import { ErrorState } from 'src/components/ErrorState/ErrorState'; import { TooltipIcon } from 'src/components/TooltipIcon'; +import { DatabaseEngineVersion } from 'src/features/Databases/DatabaseEngineVersion'; import { useDatabaseTypesQuery } from 'src/queries/databases/databases'; import { useInProgressEvents } from 'src/queries/events/events'; import { useRegionsQuery } from 'src/queries/regions/regions'; import { formatStorageUnits } from 'src/utilities/formatStorageUnits'; import { convertMegabytesTo } from 'src/utilities/unitConversions'; -import { databaseEngineMap } from '../../DatabaseLanding/DatabaseRow'; import { DatabaseStatusDisplay } from '../DatabaseStatusDisplay'; import { StyledStatusBox, @@ -24,7 +24,6 @@ import { import type { Region } from '@linode/api-v4'; import type { Database, - DatabaseInstance, DatabaseType, } from '@linode/api-v4/lib/databases/types'; @@ -32,10 +31,6 @@ interface Props { database: Database; } -export const getDatabaseVersionNumber = ( - version: DatabaseInstance['version'] -) => version.split('/')[1]; - export const DatabaseResizeCurrentConfiguration = ({ database }: Props) => { const { data: types, @@ -94,7 +89,13 @@ export const DatabaseResizeCurrentConfiguration = ({ database }: Props) => { Version{' '} - {databaseEngineMap[database.engine]} v{database.version} + Nodes{' '} diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.test.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.test.tsx index 1ac650f0371..6edfcd27efe 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.test.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.test.tsx @@ -3,28 +3,67 @@ import * as React from 'react'; import { databaseFactory } from 'src/factories/databases'; import { mockMatchMedia, renderWithTheme } from 'src/utilities/testHelpers'; -import DatabaseSettings from './DatabaseSettings'; import * as utils from '../../utilities'; +import DatabaseSettings from './DatabaseSettings'; beforeAll(() => mockMatchMedia()); +const v1 = () => { + return { + isDatabasesEnabled: true, + isDatabasesV1Enabled: true, + isDatabasesV2Beta: false, + isDatabasesV2Enabled: false, + isDatabasesV2GA: false, + isUserExistingBeta: false, + isUserNewBeta: false, + }; +}; + +const v2Beta = () => { + return { + isDatabasesEnabled: true, + isDatabasesV1Enabled: true, + isDatabasesV2Beta: true, + isDatabasesV2Enabled: true, + isDatabasesV2GA: false, + isUserExistingBeta: false, + isUserNewBeta: true, + }; +}; + +const v2GA = () => ({ + isDatabasesEnabled: true, + isDatabasesV1Enabled: true, + isDatabasesV2Beta: false, + isDatabasesV2Enabled: true, + isDatabasesV2GA: true, + isUserExistingBeta: false, + isUserNewBeta: false, +}); + +const spy = vi.spyOn(utils, 'useIsDatabasesEnabled'); +spy.mockReturnValue(v2GA()); + describe('DatabaseSettings Component', () => { - const database = databaseFactory.build(); + const database = databaseFactory.build({ platform: 'rdbms-default' }); it('Should exist and be renderable', () => { expect(DatabaseSettings).toBeDefined(); renderWithTheme(); }); it('Should render a Paper component with headers for Manage Access, Reseting the Root password, and Deleting the Cluster', () => { + spy.mockReturnValue(v2GA()); const { container, getAllByRole } = renderWithTheme( ); const paper = container.querySelector('.MuiPaper-root'); expect(paper).not.toBeNull(); const headings = getAllByRole('heading'); - expect(headings[0].textContent).toBe('Manage Access'); - expect(headings[1].textContent).toBe('Reset the Root Password'); - expect(headings[2].textContent).toBe('Delete the Cluster'); + expect(headings[0].textContent).toBe('Suspend Cluster'); + expect(headings[1].textContent).toBe('Manage Access'); + expect(headings[2].textContent).toBe('Reset the Root Password'); + expect(headings[3].textContent).toBe('Delete the Cluster'); }); it.each([ @@ -48,6 +87,106 @@ describe('DatabaseSettings Component', () => { } }); + it('should not render Maintenance for V1 view legacy db', async () => { + spy.mockReturnValue(v1()); + + const database = databaseFactory.build({ + engine: 'postgresql', + platform: 'rdbms-legacy', + version: '14.6', + }); + + const { container } = renderWithTheme( + + ); + + const maintenance = container.querySelector( + '[data-qa-settings-section="Maintenance"]' + ); + + expect(maintenance).not.toBeInTheDocument(); + }); + + it('should not render Maintenance for V2 beta view legacy db', async () => { + spy.mockReturnValue(v2Beta()); + + const database = databaseFactory.build({ + engine: 'postgresql', + platform: 'rdbms-legacy', + version: '14.6', + }); + + const { container } = renderWithTheme( + + ); + + const maintenance = container.querySelector( + '[data-qa-settings-section="Maintenance"]' + ); + + expect(maintenance).not.toBeInTheDocument(); + }); + + it('should not render Maintenance for V2 beta view default db', async () => { + spy.mockReturnValue(v2Beta()); + + const database = databaseFactory.build({ + engine: 'postgresql', + platform: 'rdbms-default', + version: '14.6', + }); + + const { container } = renderWithTheme( + + ); + + const maintenance = container.querySelector( + '[data-qa-settings-section="Maintenance"]' + ); + + expect(maintenance).not.toBeInTheDocument(); + }); + + it('should not render Maintenance for V2 GA view legacy db', async () => { + spy.mockReturnValue(v2GA()); + + const database = databaseFactory.build({ + engine: 'postgresql', + platform: 'rdbms-legacy', + version: '14.6', + }); + + const { container } = renderWithTheme( + + ); + + const maintenance = container.querySelector( + '[data-qa-settings-section="Maintenance"]' + ); + + expect(maintenance).not.toBeInTheDocument(); + }); + + it('should render Maintenance for V2 GA view default db', async () => { + spy.mockReturnValue(v2GA()); + + const database = databaseFactory.build({ + engine: 'postgresql', + platform: 'rdbms-default', + version: '14.6', + }); + + const { container } = renderWithTheme( + + ); + + const maintenance = container.querySelector( + '[data-qa-settings-section="Maintenance"]' + ); + + expect(maintenance).toBeInTheDocument(); + }); + it('Should render Maintenance Window with radio buttons', () => { const database = databaseFactory.build({ platform: 'rdbms-legacy', diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx index fcf44207c2b..57606e222d2 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx @@ -1,19 +1,25 @@ +import { Paper } from '@linode/ui'; import * as React from 'react'; import { Divider } from 'src/components/Divider'; -import { Paper } from '@linode/ui'; import { Typography } from 'src/components/Typography'; +import { DatabaseSettingsReviewUpdatesDialog } from 'src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsReviewUpdatesDialog'; +import { DatabaseSettingsUpgradeVersionDialog } from 'src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsUpgradeVersionDialog'; +import { + isDefaultDatabase, + useIsDatabasesEnabled, +} from 'src/features/Databases/utilities'; import { useProfile } from 'src/queries/profile/profile'; import AccessControls from '../AccessControls'; import DatabaseSettingsDeleteClusterDialog from './DatabaseSettingsDeleteClusterDialog'; +import { DatabaseSettingsMaintenance } from './DatabaseSettingsMaintenance'; import DatabaseSettingsMenuItem from './DatabaseSettingsMenuItem'; import DatabaseSettingsResetPasswordDialog from './DatabaseSettingsResetPasswordDialog'; +import { DatabaseSettingsSuspendClusterDialog } from './DatabaseSettingsSuspendClusterDialog'; import MaintenanceWindow from './MaintenanceWindow'; import type { Database } from '@linode/api-v4/lib/databases/types'; -import { DatabaseSettingsSuspendClusterDialog } from './DatabaseSettingsSuspendClusterDialog'; -import { useIsDatabasesEnabled } from '../../utilities'; interface Props { database: Database; @@ -24,6 +30,7 @@ export const DatabaseSettings: React.FC = (props) => { const { database, disabled } = props; const { data: profile } = useProfile(); const { isDatabasesV2GA } = useIsDatabasesEnabled(); + const isDefaultDB = isDefaultDatabase(database); const accessControlCopy = ( @@ -32,16 +39,13 @@ export const DatabaseSettings: React.FC = (props) => { ); - const isLegacy = database.platform === 'rdbms-legacy'; - const isDefault = database.platform === 'rdbms-default'; - const suspendClusterCopy = `Suspend the cluster if you don't use it temporarily to prevent being billed for it.`; - const resetRootPasswordCopy = isLegacy + const resetRootPasswordCopy = !isDefaultDB ? 'Resetting your root password will automatically generate a new password. You can view the updated password on your database cluster summary page. ' : 'Reset your root password if someone should no longer have access to the root user or if you believe your password may have been compromised. This will automatically generate a new password that you’ll be able to see on your database cluster summary page.'; - const deleteClusterCopy = isLegacy + const deleteClusterCopy = !isDefaultDB ? 'Deleting a database cluster is permanent and cannot be undone.' : 'Permanently remove an unused database cluster.'; @@ -55,6 +59,16 @@ export const DatabaseSettings: React.FC = (props) => { setIsSuspendClusterDialogOpen, ] = React.useState(false); + const [ + isUpgradeVersionDialogOpen, + setIsUpgradeVersionDialogOpen, + ] = React.useState(false); + + const [ + isReviewUpdatesDialogOpen, + setIsReviewUpdatesDialogOpen, + ] = React.useState(false); + const onResetRootPassword = () => { setIsResetRootPasswordDialogOpen(true); }; @@ -79,10 +93,26 @@ export const DatabaseSettings: React.FC = (props) => { setIsSuspendClusterDialogOpen(false); }; + const onUpgradeVersion = () => { + setIsUpgradeVersionDialogOpen(true); + }; + + const onUpgradeVersionClose = () => { + setIsUpgradeVersionDialogOpen(false); + }; + + const onReviewUpdates = () => { + setIsReviewUpdatesDialogOpen(true); + }; + + const onReviewUpdatesClose = () => { + setIsReviewUpdatesDialogOpen(false); + }; + return ( <> - {isDatabasesV2GA && isDefault && ( + {isDatabasesV2GA && isDefaultDB && ( <> = (props) => { onClick={onDeleteCluster} sectionTitle="Delete the Cluster" /> + {isDatabasesV2GA && isDefaultDB && ( + <> + + + + )} = (props) => { onClose={onSuspendDialogClose} open={isSuspendClusterDialogOpen} /> + + ); }; diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryClusterConfiguration.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryClusterConfiguration.tsx index f619078acab..54b733ed209 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryClusterConfiguration.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryClusterConfiguration.tsx @@ -10,7 +10,7 @@ import { StyledLabelTypography, StyledValueGrid, } from 'src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryClusterConfiguration.style'; -import { databaseEngineMap } from 'src/features/Databases/DatabaseLanding/DatabaseRow'; +import { DatabaseEngineVersion } from 'src/features/Databases/DatabaseEngineVersion'; import { useDatabaseTypesQuery } from 'src/queries/databases/databases'; import { useInProgressEvents } from 'src/queries/events/events'; import { useRegionsQuery } from 'src/queries/regions/regions'; @@ -101,7 +101,13 @@ export const DatabaseSummaryClusterConfiguration = (props: Props) => { Engine - {databaseEngineMap[database.engine]} v{database.version} + Region diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/legacy/DatabaseSummaryClusterConfigurationLegacy.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/legacy/DatabaseSummaryClusterConfigurationLegacy.tsx index 8b018175505..dedbefe5070 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/legacy/DatabaseSummaryClusterConfigurationLegacy.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/legacy/DatabaseSummaryClusterConfigurationLegacy.tsx @@ -5,7 +5,7 @@ import { makeStyles } from 'tss-react/mui'; import { TooltipIcon } from 'src/components/TooltipIcon'; import { Typography } from 'src/components/Typography'; import { DatabaseStatusDisplay } from 'src/features/Databases/DatabaseDetail/DatabaseStatusDisplay'; -import { databaseEngineMap } from 'src/features/Databases/DatabaseLanding/DatabaseRow'; +import { DatabaseEngineVersion } from 'src/features/Databases/DatabaseEngineVersion'; import { useDatabaseTypesQuery } from 'src/queries/databases/databases'; import { useInProgressEvents } from 'src/queries/events/events'; import { useRegionsQuery } from 'src/queries/regions/regions'; @@ -94,7 +94,13 @@ export const DatabaseSummaryClusterConfigurationLegacy = (props: Props) => { Version - {databaseEngineMap[database.engine]} v{database.version} + Nodes diff --git a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseRow.tsx b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseRow.tsx index f72696b2319..890247dae03 100644 --- a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseRow.tsx +++ b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseRow.tsx @@ -6,6 +6,7 @@ import { Hidden } from 'src/components/Hidden'; import { TableCell } from 'src/components/TableCell'; import { TableRow } from 'src/components/TableRow'; import { DatabaseStatusDisplay } from 'src/features/Databases/DatabaseDetail/DatabaseStatusDisplay'; +import { DatabaseEngineVersion } from 'src/features/Databases/DatabaseEngineVersion'; import { DatabaseActionMenu } from 'src/features/Databases/DatabaseLanding/DatabaseActionMenu'; import { useIsDatabasesEnabled } from 'src/features/Databases/utilities'; import { useDatabaseTypesQuery } from 'src/queries/databases/databases'; @@ -19,17 +20,9 @@ import type { Event } from '@linode/api-v4'; import type { DatabaseInstance, DatabaseType, - Engine, } from '@linode/api-v4/lib/databases/types'; import type { ActionHandlers } from 'src/features/Databases/DatabaseLanding/DatabaseActionMenu'; -export const databaseEngineMap: Record = { - mongodb: 'MongoDB', - mysql: 'MySQL', - postgresql: 'PostgreSQL', - redis: 'Redis', -}; - interface Props { database: DatabaseInstance; events?: Event[]; @@ -53,9 +46,11 @@ export const DatabaseRow = ({ engine, id, label, + platform, region, status, type, + updates, version, } = database; @@ -101,7 +96,15 @@ export const DatabaseRow = ({ {configuration} - {`${databaseEngineMap[engine]} v${version}`} + + + {actualRegion?.label ?? region} From eeae3a3fd363a25dde71779b24f67b37b19f6e34 Mon Sep 17 00:00:00 2001 From: Alban Bailly <130582365+abailly-akamai@users.noreply.github.com> Date: Wed, 6 Nov 2024 15:16:48 -0500 Subject: [PATCH 51/66] feat: [M3-8831] - New GPUv2 egress transfer display (#11209) * Add new gpuV2 egress transferlogic * light cleanup * adjust e2e s * Added changeset: New GPUv2 egress transfer helpers * feedback @coliu-akamai @hkhalil-akamai * feedback @coliu-akamai @jaalah-akamai * feedback optimization --- .../pr-11209-added-1730788980490.md | 5 +++ .../e2e/core/linodes/plan-selection.spec.ts | 12 +++++-- .../src/components/TableCell/TableCell.tsx | 15 ++++---- packages/manager/src/featureFlags.ts | 1 + .../PlansPanel/PlanContainer.styles.ts | 5 +-- .../components/PlansPanel/PlanContainer.tsx | 6 ++-- .../components/PlansPanel/PlanInformation.tsx | 18 ++++++++++ .../components/PlansPanel/PlanSelection.tsx | 6 +++- .../PlansPanel/PlanSelectionTable.tsx | 34 ++++++++++++++++++- packages/manager/src/mocks/serverHandlers.ts | 14 +++++++- 10 files changed, 101 insertions(+), 15 deletions(-) create mode 100644 packages/manager/.changeset/pr-11209-added-1730788980490.md diff --git a/packages/manager/.changeset/pr-11209-added-1730788980490.md b/packages/manager/.changeset/pr-11209-added-1730788980490.md new file mode 100644 index 00000000000..8973630d4ca --- /dev/null +++ b/packages/manager/.changeset/pr-11209-added-1730788980490.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Added +--- + +New GPUv2 egress transfer helpers ([#11209](https://github.com/linode/manager/pull/11209)) diff --git a/packages/manager/cypress/e2e/core/linodes/plan-selection.spec.ts b/packages/manager/cypress/e2e/core/linodes/plan-selection.spec.ts index 94827d5d9cf..99255a0241c 100644 --- a/packages/manager/cypress/e2e/core/linodes/plan-selection.spec.ts +++ b/packages/manager/cypress/e2e/core/linodes/plan-selection.spec.ts @@ -12,6 +12,7 @@ import { mockGetRegionAvailability, } from 'support/intercepts/regions'; import { mockGetLinodeTypes } from 'support/intercepts/linodes'; +import { mockAppendFeatureFlags } from 'support/intercepts/feature-flags'; const mockRegions = [ regionFactory.build({ @@ -356,11 +357,18 @@ describe('displays specific linode plans for GPU', () => { mockGetRegionAvailability(mockRegions[0].id, mockRegionAvailability).as( 'getRegionAvailability' ); + mockAppendFeatureFlags({ + gpuv2: { + transferBanner: true, + planDivider: true, + egressBanner: true, + }, + }).as('getFeatureFlags'); }); it('Should render divided tables when GPU divider enabled', () => { cy.visitWithLogin('/linodes/create'); - + cy.wait(['@getRegions', '@getLinodeTypes', '@getFeatureFlags']); ui.regionSelect.find().click(); ui.regionSelect.findItemByRegionLabel(mockRegions[0].label).click(); @@ -368,7 +376,7 @@ describe('displays specific linode plans for GPU', () => { // Should display two separate tables cy.findByText('GPU').click(); cy.get(linodePlansPanel).within(() => { - cy.findAllByRole('alert').should('have.length', 2); + cy.findAllByRole('alert').should('have.length', 3); cy.get(notices.unavailable).should('be.visible'); cy.findByRole('table', { diff --git a/packages/manager/src/components/TableCell/TableCell.tsx b/packages/manager/src/components/TableCell/TableCell.tsx index 159b10fcdde..f30c4a116dc 100644 --- a/packages/manager/src/components/TableCell/TableCell.tsx +++ b/packages/manager/src/components/TableCell/TableCell.tsx @@ -1,13 +1,12 @@ -import { - default as _TableCell, - TableCellProps as _TableCellProps, -} from '@mui/material/TableCell'; -import { Theme } from '@mui/material/styles'; +import { default as _TableCell } from '@mui/material/TableCell'; import * as React from 'react'; import { makeStyles } from 'tss-react/mui'; import { TooltipIcon } from 'src/components/TooltipIcon'; +import type { Theme } from '@mui/material/styles'; +import type { TableCellProps as _TableCellProps } from '@mui/material/TableCell'; + const useStyles = makeStyles()((theme: Theme) => ({ actionCell: { textAlign: 'right', @@ -106,7 +105,11 @@ export const TableCell = (props: TableCellProps) => { [classes.root]: true, [classes.sortable]: sortable, // hide the cell at small breakpoints if it's empty with no parent column - emptyCell: !parentColumn && !props.children, + emptyCell: + (!parentColumn && !props.children) || + (!parentColumn && + Array.isArray(props.children) && + !props.children[0]), }, className )} diff --git a/packages/manager/src/featureFlags.ts b/packages/manager/src/featureFlags.ts index 9a92408132d..edbf8292360 100644 --- a/packages/manager/src/featureFlags.ts +++ b/packages/manager/src/featureFlags.ts @@ -72,6 +72,7 @@ export interface CloudPulseResourceTypeMapFlag { interface gpuV2 { egressBanner: boolean; planDivider: boolean; + transferBanner: boolean; } interface DesignUpdatesBannerFlag extends BaseFeatureFlag { diff --git a/packages/manager/src/features/components/PlansPanel/PlanContainer.styles.ts b/packages/manager/src/features/components/PlansPanel/PlanContainer.styles.ts index 6260a155855..dac60cbc026 100644 --- a/packages/manager/src/features/components/PlansPanel/PlanContainer.styles.ts +++ b/packages/manager/src/features/components/PlansPanel/PlanContainer.styles.ts @@ -12,14 +12,15 @@ interface StyledTableCellPropsProps extends TableCellProps { export const StyledTable = styled(Table, { label: 'StyledTable', -})(({ theme }) => ({ +})({ overflowX: 'hidden', -})); +}); export const StyledTableCell = styled(TableCell, { label: 'StyledTableCell', shouldForwardProp: omittedProps(['isPlanCell']), })(({ theme, ...props }) => ({ + ...(props.isPlanCell && { width: '30%' }), '&.emptyCell': { borderRight: 'none', }, diff --git a/packages/manager/src/features/components/PlansPanel/PlanContainer.tsx b/packages/manager/src/features/components/PlansPanel/PlanContainer.tsx index 512bcbb91fb..b1201e0a3d0 100644 --- a/packages/manager/src/features/components/PlansPanel/PlanContainer.tsx +++ b/packages/manager/src/features/components/PlansPanel/PlanContainer.tsx @@ -1,4 +1,3 @@ -import { LinodeTypeClass } from '@linode/api-v4/lib/linodes'; import Grid from '@mui/material/Unstable_Grid2'; import * as React from 'react'; import { useLocation } from 'react-router-dom'; @@ -14,6 +13,7 @@ import { PlanSelectionTable } from './PlanSelectionTable'; import type { PlanWithAvailability } from './types'; import type { Region } from '@linode/api-v4'; +import type { LinodeTypeClass } from '@linode/api-v4/lib/linodes'; export interface PlanContainerProps { allDisabledPlans: PlanWithAvailability[]; @@ -50,7 +50,8 @@ export const PlanContainer = (props: PlanContainerProps) => { // Show the Transfer column if, for any plan, the api returned data and we're not in the Database Create flow const showTransfer = - showLimits && plans.some((plan: PlanWithAvailability) => plan.transfer); + showLimits && + plans.some((plan: PlanWithAvailability) => plan.transfer !== undefined); // Show the Network throughput column if, for any plan, the api returned data (currently Bare Metal does not) const showNetwork = @@ -198,6 +199,7 @@ export const PlanContainer = (props: PlanContainerProps) => { } key={`plan-filter-${idx}`} planFilter={table.planFilter} + plans={plans} showNetwork={showNetwork} showTransfer={showTransfer} /> diff --git a/packages/manager/src/features/components/PlansPanel/PlanInformation.tsx b/packages/manager/src/features/components/PlansPanel/PlanInformation.tsx index 56c159989a0..422fa0f9a19 100644 --- a/packages/manager/src/features/components/PlansPanel/PlanInformation.tsx +++ b/packages/manager/src/features/components/PlansPanel/PlanInformation.tsx @@ -45,6 +45,7 @@ export const PlanInformation = (props: PlanInformationProps) => { return Boolean(disabledClasses?.includes(thisClass)); }; const showGPUEgressBanner = Boolean(useFlags().gpuv2?.egressBanner); + const showTransferBanner = Boolean(useFlags().gpuv2?.transferBanner); return ( <> @@ -68,6 +69,23 @@ export const PlanInformation = (props: PlanInformationProps) => { )} + {showTransferBanner && ( + + theme.font.bold} + fontSize="1rem" + > + Some plans do not include bundled network transfer. If the + transfer allotment is 0, all outbound network transfer is + subject to standard charges. +
+ + Learn more about transfer costs + + . +
+
+ )} {
{showTransfer ? ( - {plan.transfer ? <>{plan.transfer / 1000} TB : ''} + {plan.transfer !== undefined ? ( + <>{plan.transfer / 1000} TB + ) : ( + '' + )} ) : null} {showNetwork ? ( diff --git a/packages/manager/src/features/components/PlansPanel/PlanSelectionTable.tsx b/packages/manager/src/features/components/PlansPanel/PlanSelectionTable.tsx index ee13cbe28b0..4cd06f4a678 100644 --- a/packages/manager/src/features/components/PlansPanel/PlanSelectionTable.tsx +++ b/packages/manager/src/features/components/PlansPanel/PlanSelectionTable.tsx @@ -4,10 +4,13 @@ import { TableBody } from 'src/components/TableBody'; import { TableHead } from 'src/components/TableHead'; import { TableRow } from 'src/components/TableRow'; import { TableRowEmpty } from 'src/components/TableRowEmpty/TableRowEmpty'; +import { TooltipIcon } from 'src/components/TooltipIcon'; +import { useFlags } from 'src/hooks/useFlags'; import { PLAN_SELECTION_NO_REGION_SELECTED_MESSAGE } from 'src/utilities/pricing/constants'; import { StyledTable, StyledTableCell } from './PlanContainer.styles'; -import { PlanWithAvailability } from './types'; + +import type { PlanWithAvailability } from './types'; interface PlanSelectionFilterOptionsTable { header?: string; @@ -17,6 +20,7 @@ interface PlanSelectionFilterOptionsTable { interface PlanSelectionTableProps { filterOptions?: PlanSelectionFilterOptionsTable; planFilter?: (plan: PlanWithAvailability) => boolean; + plans?: PlanWithAvailability[]; renderPlanSelection: ( filterOptions?: PlanSelectionFilterOptionsTable | undefined ) => React.JSX.Element[]; @@ -45,11 +49,26 @@ const tableCells = [ export const PlanSelectionTable = (props: PlanSelectionTableProps) => { const { filterOptions, + plans, renderPlanSelection, shouldDisplayNoRegionSelectedMessage, showNetwork: shouldShowNetwork, showTransfer: shouldShowTransfer, } = props; + const flags = useFlags(); + + const showTransferTooltip = React.useCallback( + (cellName: string) => + plans?.some((plan) => { + return ( + flags.gpuv2?.transferBanner && + plan.class === 'gpu' && + filterOptions?.header?.includes('Ada') && + cellName === 'Transfer' + ); + }), + [plans, filterOptions, flags.gpuv2] + ); return ( { {isPlanCell && filterOptions?.header ? filterOptions?.header : cellName} + {showTransferTooltip(cellName) && ( + + )} ); })} diff --git a/packages/manager/src/mocks/serverHandlers.ts b/packages/manager/src/mocks/serverHandlers.ts index 94d7aaf289b..70e52dc8183 100644 --- a/packages/manager/src/mocks/serverHandlers.ts +++ b/packages/manager/src/mocks/serverHandlers.ts @@ -392,7 +392,17 @@ const nanodeType = linodeTypeFactory.build({ id: 'g6-nanode-1' }); const standardTypes = linodeTypeFactory.buildList(7); const dedicatedTypes = dedicatedTypeFactory.buildList(7); const proDedicatedType = proDedicatedTypeFactory.build(); - +const gpuTypesAda = linodeTypeFactory.buildList(7, { + class: 'gpu', + gpus: 5, + label: 'Ada Lovelace', + transfer: 0, +}); +const gpuTypesRX = linodeTypeFactory.buildList(7, { + class: 'gpu', + gpus: 1, + transfer: 5000, +}); const proxyAccountUser = accountUserFactory.build({ email: 'partner@proxy.com', last_login: null, @@ -586,6 +596,8 @@ export const handlers = [ nanodeType, ...standardTypes, ...dedicatedTypes, + ...gpuTypesAda, + ...gpuTypesRX, proDedicatedType, ]) ); From e04981a1fe862f9fb59a64335c3f72736054a049 Mon Sep 17 00:00:00 2001 From: Hana Xu <115299789+hana-akamai@users.noreply.github.com> Date: Wed, 6 Nov 2024 15:58:35 -0500 Subject: [PATCH 52/66] change: [M3-8806] - Disable unsupported images for distributed regions (#11206) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description 📝 In the Linode Create flow, when a distributed region is selected, images & distros that do not support distributed regions should be disabled Note: It looks like the distributions are now sorted alphabetically. I don't see any issues with this but just wanted to point that out in case anyone did ## Changes 🔄 List any change relevant to the reviewer. - Disable unsupported images/distros for distributed regions in Linode Create - Removed the distributed icon & associated icon text - Removed the word `currently` in the Add-Ons warning notice for distributed regions - Added a new generic `ListItemOption` component and refactored `ImageOption`, `PlacementGroupSelectOption`, and `RegionOption` to use new generic component ## Target release date 🗓️ 11/12 ## How to test 🧪 ### Prerequisites (How to setup test environment) - Ensure your account has the `new-dc-testing`, `new-dc-testing-gecko`, `edge_testing` and `edge_compute` customer tags ### Verification steps (How to verify changes) - Go to the Linode Create page and verify the following on the `OS` tab and `Images` tab: - Select a core region -> No Images/distributions should be disabled - Select a distributed region -> Images/distributions that do not support distributed regions should be disabled - There should be no regressions in the components that were refactored - Ensure unit tests and e2e tests are passing locally/remotely --- .../pr-11206-changed-1730841709351.md | 5 + .../icons/entityIcons/distributed-region.svg | 7 -- .../ImageSelect/ImageOption.test.tsx | 43 ++++--- .../components/ImageSelect/ImageOption.tsx | 64 ++++------ .../components/ImageSelect/ImageSelect.tsx | 18 ++- .../src/components/ImageSelect/utilities.ts | 33 ++++- .../manager/src/components/ListItemOption.tsx | 115 ++++++++++++++++++ .../PlacementGroupSelectOption.tsx | 108 +++++----------- .../PlacementGroupsSelect.test.tsx | 4 +- .../PlacementGroupsSelect.tsx | 10 +- .../RegionSelect/RegionMultiSelect.tsx | 10 +- .../components/RegionSelect/RegionOption.tsx | 102 +++------------- .../RegionSelect/RegionSelect.styles.ts | 65 ---------- .../RegionSelect/RegionSelect.test.tsx | 22 ---- .../components/RegionSelect/RegionSelect.tsx | 63 ++-------- .../RegionSelect/RegionSelect.types.ts | 19 +-- .../ImageRegions/ManageImageRegionsForm.tsx | 6 +- .../LinodeCreate/Addons/Addons.test.tsx | 2 +- .../Linodes/LinodeCreate/Addons/Addons.tsx | 2 +- .../features/Linodes/LinodeCreate/Region.tsx | 14 +-- .../Linodes/LinodeCreate/Region.utils.ts | 4 +- .../Linodes/LinodeCreate/Tabs/Images.test.tsx | 26 ---- .../Linodes/LinodeCreate/Tabs/Images.tsx | 21 +--- .../LinodeCreate/Tabs/OperatingSystems.tsx | 10 +- .../Linodes/LinodeCreate/TwoStepRegion.tsx | 4 +- .../LinodeRebuild/RebuildFromImage.test.tsx | 18 +-- .../LinodeSettings/ImageAndPassword.test.tsx | 20 +-- .../Linodes/MigrateLinode/ConfigureForm.tsx | 14 --- .../PlacementGroupsCreateDrawer.tsx | 4 +- .../StackScriptCreate.test.tsx | 33 ++--- .../StackScriptForm/StackScriptForm.test.tsx | 6 +- 31 files changed, 360 insertions(+), 512 deletions(-) create mode 100644 packages/manager/.changeset/pr-11206-changed-1730841709351.md delete mode 100644 packages/manager/src/assets/icons/entityIcons/distributed-region.svg create mode 100644 packages/manager/src/components/ListItemOption.tsx diff --git a/packages/manager/.changeset/pr-11206-changed-1730841709351.md b/packages/manager/.changeset/pr-11206-changed-1730841709351.md new file mode 100644 index 00000000000..de5dd6f21e4 --- /dev/null +++ b/packages/manager/.changeset/pr-11206-changed-1730841709351.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Changed +--- + +Disable unsupported images for distributed regions ([#11206](https://github.com/linode/manager/pull/11206)) diff --git a/packages/manager/src/assets/icons/entityIcons/distributed-region.svg b/packages/manager/src/assets/icons/entityIcons/distributed-region.svg deleted file mode 100644 index a9e44da4e6d..00000000000 --- a/packages/manager/src/assets/icons/entityIcons/distributed-region.svg +++ /dev/null @@ -1,7 +0,0 @@ - - -distributed-region - - - - diff --git a/packages/manager/src/components/ImageSelect/ImageOption.test.tsx b/packages/manager/src/components/ImageSelect/ImageOption.test.tsx index 8b704581f62..b3380fee7cb 100644 --- a/packages/manager/src/components/ImageSelect/ImageOption.test.tsx +++ b/packages/manager/src/components/ImageSelect/ImageOption.test.tsx @@ -10,7 +10,7 @@ describe('ImageOption', () => { const image = imageFactory.build({ eol: null }); const { getByText } = renderWithTheme( - + ); expect(getByText(image.label)).toBeVisible(); @@ -20,7 +20,7 @@ describe('ImageOption', () => { const image = imageFactory.build(); const { getByTestId } = renderWithTheme( - + ); expect(getByTestId('os-icon')).toBeVisible(); @@ -30,7 +30,7 @@ describe('ImageOption', () => { const image = imageFactory.build({ capabilities: ['cloud-init'] }); const { getByLabelText } = renderWithTheme( - , + , { flags: { metadata: true } } ); @@ -39,25 +39,40 @@ describe('ImageOption', () => { ).toBeVisible(); }); - it('renders a distributed icon if image has the "distributed-sites" capability', () => { - const image = imageFactory.build({ capabilities: ['distributed-sites'] }); + it('disables the image option if the image has a disabled reason', () => { + const image = imageFactory.build({ eol: null }); + const disabledReason = + 'The selected image cannot be deployed to a distributed region.'; - const { getByLabelText } = renderWithTheme( - + const { getByText } = renderWithTheme( + ); + expect( + getByText(image.label).closest('li')?.getAttribute('aria-label') + ).toBe(disabledReason); + }); + it('does not disable the image option if the image does not have a disabled reason', () => { + const image = imageFactory.build({ eol: null }); + + const { getByText } = renderWithTheme( + + ); expect( - getByLabelText( - 'This image is compatible with distributed compute regions.' - ) - ).toBeVisible(); + getByText(image.label).closest('li')?.getAttribute('aria-label') + ).toBe(''); }); it('renders (deprecated) if the image is deprecated', () => { const image = imageFactory.build({ deprecated: true }); const { getByText } = renderWithTheme( - + ); expect(getByText(`${image.label} (deprecated)`)).toBeVisible(); @@ -70,7 +85,7 @@ describe('ImageOption', () => { }); const { getByText } = renderWithTheme( - + ); expect(getByText(`${image.label} (deprecated)`)).toBeVisible(); @@ -80,7 +95,7 @@ describe('ImageOption', () => { const image = imageFactory.build(); const { getByTestId } = renderWithTheme( - + ); expect(getByTestId('DoneIcon')).toBeVisible(); diff --git a/packages/manager/src/components/ImageSelect/ImageOption.tsx b/packages/manager/src/components/ImageSelect/ImageOption.tsx index c1f229ca958..35d73a1e4ff 100644 --- a/packages/manager/src/components/ImageSelect/ImageOption.tsx +++ b/packages/manager/src/components/ImageSelect/ImageOption.tsx @@ -2,60 +2,46 @@ import { Tooltip } from '@linode/ui'; import React from 'react'; import CloudInitIcon from 'src/assets/icons/cloud-init.svg'; -import DistributedRegionIcon from 'src/assets/icons/entityIcons/distributed-region.svg'; +import { ListItemOption } from 'src/components/ListItemOption'; import { useFlags } from 'src/hooks/useFlags'; -import { SelectedIcon } from '../Autocomplete/Autocomplete.styles'; import { OSIcon } from '../OSIcon'; import { Stack } from '../Stack'; import { Typography } from '../Typography'; import { isImageDeprecated } from './utilities'; import type { Image } from '@linode/api-v4'; +import type { ListItemProps } from 'src/components/ListItemOption'; -interface Props { - image: Image; - isSelected: boolean; - listItemProps: Omit, 'key'>; -} - -export const ImageOption = ({ image, isSelected, listItemProps }: Props) => { +export const ImageOption = ({ + disabledOptions, + item, + props, + selected, +}: ListItemProps) => { const flags = useFlags(); return ( -
  • - - {image?.id !== 'any/all' && ( - - )} + + {item?.id !== 'any/all' && } - {image.label} {isImageDeprecated(image) && '(deprecated)'} + {item.label} {isImageDeprecated(item) && '(deprecated)'} - - {image.capabilities.includes('distributed-sites') && ( - -
    - -
    -
    - )} - {flags.metadata && image.capabilities.includes('cloud-init') && ( - - - - - - )} - {isSelected && } -
    -
  • + {flags.metadata && item.capabilities.includes('cloud-init') && ( + + + + + + )} + ); }; diff --git a/packages/manager/src/components/ImageSelect/ImageSelect.tsx b/packages/manager/src/components/ImageSelect/ImageSelect.tsx index c7f2ad94e12..ab07b89494e 100644 --- a/packages/manager/src/components/ImageSelect/ImageSelect.tsx +++ b/packages/manager/src/components/ImageSelect/ImageSelect.tsx @@ -8,10 +8,11 @@ import { OSIcon } from '../OSIcon'; import { ImageOption } from './ImageOption'; import { getAPIFilterForImageSelect, + getDisabledImages, getFilteredImagesForImageSelect, } from './utilities'; -import type { Image } from '@linode/api-v4'; +import type { Image, RegionSite } from '@linode/api-v4'; import type { EnhancedAutocompleteProps } from 'src/components/Autocomplete/Autocomplete'; export type ImageSelectVariant = 'all' | 'private' | 'public'; @@ -27,6 +28,7 @@ interface BaseProps label?: string; placeholder?: string; selectIfOnlyOneOption?: boolean; + siteType?: RegionSite; variant: ImageSelectVariant; } @@ -53,6 +55,7 @@ export const ImageSelect = (props: Props) => { onChange, placeholder, selectIfOnlyOneOption, + siteType, variant, ...rest } = props; @@ -62,6 +65,11 @@ export const ImageSelect = (props: Props) => { getAPIFilterForImageSelect(variant) ); + const disabledImages = getDisabledImages({ + images: images ?? [], + site_type: siteType, + }); + const _options = useMemo(() => { // We can't filter out Kubernetes images using the API so we do it client side const filteredOptions = @@ -144,10 +152,11 @@ export const ImageSelect = (props: Props) => { return ( ); }} @@ -182,6 +191,7 @@ export const ImageSelect = (props: Props) => { : !multiple && !Array.isArray(value) && onChange(value) } errorText={rest.errorText ?? error?.[0].reason} + getOptionDisabled={(option) => Boolean(disabledImages[option.id])} multiple={multiple} value={value} /> diff --git a/packages/manager/src/components/ImageSelect/utilities.ts b/packages/manager/src/components/ImageSelect/utilities.ts index cfd765da017..dcb1babfb1e 100644 --- a/packages/manager/src/components/ImageSelect/utilities.ts +++ b/packages/manager/src/components/ImageSelect/utilities.ts @@ -3,7 +3,8 @@ import { DateTime } from 'luxon'; import { MAX_MONTHS_EOL_FILTER } from 'src/constants'; import type { ImageSelectVariant } from './ImageSelect'; -import type { Image } from '@linode/api-v4'; +import type { Image, RegionSite } from '@linode/api-v4'; +import type { DisableItemOption } from 'src/components/ListItemOption'; /** * Given a Image Select "variant", this PR returns an @@ -88,3 +89,33 @@ export const isImageDeprecated = (image: Image) => { return image.deprecated || isImageEOL; }; + +interface DisabledImageOptions { + images: Image[]; + site_type?: RegionSite; +} + +/** + * Returns images that should be disabled on the Linode Create flow. + * + * @returns key/value pairs for disabled images. the key is the image id and the value is why the image is disabled + */ +export const getDisabledImages = (options: DisabledImageOptions) => { + const { images, site_type } = options; + + // Disable images that do not support distributed sites if the selected region is distributed + if (site_type === 'distributed') { + const disabledImages: Record = {}; + for (const image of images) { + if (!image.capabilities.includes('distributed-sites')) { + disabledImages[image.id] = { + reason: + 'The selected image cannot be deployed to a distributed region.', + }; + } + } + return disabledImages; + } + + return {}; +}; diff --git a/packages/manager/src/components/ListItemOption.tsx b/packages/manager/src/components/ListItemOption.tsx new file mode 100644 index 00000000000..8a8e4a2bb39 --- /dev/null +++ b/packages/manager/src/components/ListItemOption.tsx @@ -0,0 +1,115 @@ +import { Box, Tooltip } from '@linode/ui'; +import { styled } from '@mui/material/styles'; +import { visuallyHidden } from '@mui/utils'; +import React from 'react'; + +import { SelectedIcon } from 'src/components/Autocomplete/Autocomplete.styles'; +import { ListItem } from 'src/components/ListItem'; + +import type { ListItemComponentsPropsOverrides } from '@mui/material/ListItem'; + +export interface ListItemProps { + children?: React.ReactNode; + disabledOptions?: DisableItemOption; + item: T & { id: number | string }; + maxHeight?: number; + props: React.HTMLAttributes; + selected?: boolean; +} + +export interface DisableItemOption { + /** + * The reason the item option is disabled. + * This is shown to the user as a tooltip. + */ + reason: JSX.Element | string; + /** + * An optional minWith applied to the tooltip + * @default 215 + */ + tooltipWidth?: number; +} + +export const ListItemOption = ({ + children, + disabledOptions, + item, + maxHeight, + props, + selected, +}: ListItemProps) => { + const { className, onClick, ...rest } = props; + const isItemOptionDisabled = Boolean(disabledOptions); + const itemOptionDisabledReason = disabledOptions?.reason; + + return ( + + + isItemOptionDisabled + ? e.preventDefault() + : onClick + ? onClick(e) + : null + } + style={{ + display: 'flex', + justifyContent: 'space-between', + maxHeight, + }} + aria-disabled={undefined} + data-qa-disabled-item={isItemOptionDisabled} + > + {children} + {isItemOptionDisabled && ( + {itemOptionDisabledReason} + )} + {selected && } + + + ); +}; + +export const StyledDisabledItem = styled(ListItem, { + label: 'StyledDisabledItem', +})(() => ({ + '&.Mui-disabled': { + cursor: 'not-allowed', + }, + '&.MuiAutocomplete-option': { + minHeight: 'auto !important', + padding: '8px 10px !important', + }, + '&.MuiListItem-root[aria-disabled="true"]:active': { + pointerEvents: 'none !important', + }, +})); diff --git a/packages/manager/src/components/PlacementGroupsSelect/PlacementGroupSelectOption.tsx b/packages/manager/src/components/PlacementGroupsSelect/PlacementGroupSelectOption.tsx index f3e920c75b0..3ba84a95ebe 100644 --- a/packages/manager/src/components/PlacementGroupsSelect/PlacementGroupSelectOption.tsx +++ b/packages/manager/src/components/PlacementGroupsSelect/PlacementGroupSelectOption.tsx @@ -1,89 +1,39 @@ import { PLACEMENT_GROUP_TYPES } from '@linode/api-v4'; -import { Box, Tooltip } from '@linode/ui'; -import { visuallyHidden } from '@mui/utils'; +import { Box } from '@linode/ui'; import React from 'react'; +import { ListItemOption } from 'src/components/ListItemOption'; import { Stack } from 'src/components/Stack'; -import { PLACEMENT_GROUP_HAS_NO_CAPACITY } from 'src/features/PlacementGroups/constants'; - -import { - SelectedIcon, - StyledListItem, -} from '../RegionSelect/RegionSelect.styles'; import type { PlacementGroup } from '@linode/api-v4'; -import type { ListItemComponentsPropsOverrides } from '@mui/material/ListItem'; - -interface PlacementGroupSelectOptionProps { - disabled?: boolean; - label: string; - props: React.HTMLAttributes; - selected?: boolean; - value: PlacementGroup; -} +import type { ListItemProps } from 'src/components/ListItemOption'; export const PlacementGroupSelectOption = ({ - disabled, - label, + disabledOptions, + item, props, selected, - value, -}: PlacementGroupSelectOptionProps) => { - return ( - - - disabled - ? e.preventDefault() - : props.onClick - ? props.onClick(e) - : null - } - aria-disabled={undefined} - > - - - {label} - - - ({PLACEMENT_GROUP_TYPES[value.placement_group_type]}) - - - {disabled && ( - {`Option disabled: ${PLACEMENT_GROUP_HAS_NO_CAPACITY}`} - )} - - {selected && } - - - ); -}; +}: ListItemProps) => ( + + + + {item.label} + + + ({PLACEMENT_GROUP_TYPES[item.placement_group_type]}) + + + + +); diff --git a/packages/manager/src/components/PlacementGroupsSelect/PlacementGroupsSelect.test.tsx b/packages/manager/src/components/PlacementGroupsSelect/PlacementGroupsSelect.test.tsx index e1b25164191..b8c47c37502 100644 --- a/packages/manager/src/components/PlacementGroupsSelect/PlacementGroupsSelect.test.tsx +++ b/packages/manager/src/components/PlacementGroupsSelect/PlacementGroupsSelect.test.tsx @@ -106,8 +106,6 @@ describe('PlacementGroupSelect', () => { const selectedRegionOption = getByText('my-placement-group'); fireEvent.click(selectedRegionOption); - expect( - getByText(`Option disabled: ${PLACEMENT_GROUP_HAS_NO_CAPACITY}`) - ).toBeVisible(); + expect(getByText(PLACEMENT_GROUP_HAS_NO_CAPACITY)).toBeVisible(); }); }); diff --git a/packages/manager/src/components/PlacementGroupsSelect/PlacementGroupsSelect.tsx b/packages/manager/src/components/PlacementGroupsSelect/PlacementGroupsSelect.tsx index 6118c431f1f..9d9b90f96b1 100644 --- a/packages/manager/src/components/PlacementGroupsSelect/PlacementGroupsSelect.tsx +++ b/packages/manager/src/components/PlacementGroupsSelect/PlacementGroupsSelect.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; +import { PLACEMENT_GROUP_HAS_NO_CAPACITY } from 'src/features/PlacementGroups/constants'; import { hasPlacementGroupReachedCapacity } from 'src/features/PlacementGroups/utils'; import { useAllPlacementGroupsQuery } from 'src/queries/placementGroups'; @@ -107,12 +108,15 @@ export const PlacementGroupsSelect = (props: PlacementGroupsSelectProps) => { return ( ); }} diff --git a/packages/manager/src/components/RegionSelect/RegionMultiSelect.tsx b/packages/manager/src/components/RegionSelect/RegionMultiSelect.tsx index 0bdf1469232..0bcfd9a3cea 100644 --- a/packages/manager/src/components/RegionSelect/RegionMultiSelect.tsx +++ b/packages/manager/src/components/RegionSelect/RegionMultiSelect.tsx @@ -16,11 +16,9 @@ import { isRegionOptionUnavailable, } from './RegionSelect.utils'; -import type { - DisableRegionOption, - RegionMultiSelectProps, -} from './RegionSelect.types'; +import type { RegionMultiSelectProps } from './RegionSelect.types'; import type { Region } from '@linode/api-v4'; +import type { DisableItemOption } from 'src/components/ListItemOption'; interface RegionChipLabelProps { region: Region; @@ -71,7 +69,7 @@ export const RegionMultiSelect = React.memo((props: RegionMultiSelectProps) => { }; const disabledRegions = regionOptions.reduce< - Record + Record >((acc, region) => { if (disabledRegionsFromProps?.[region.id]) { acc[region.id] = disabledRegionsFromProps[region.id]; @@ -120,9 +118,9 @@ export const RegionMultiSelect = React.memo((props: RegionMultiSelectProps) => { return ( ); diff --git a/packages/manager/src/components/RegionSelect/RegionOption.tsx b/packages/manager/src/components/RegionSelect/RegionOption.tsx index efbe78c12cb..ae3fe3b182b 100644 --- a/packages/manager/src/components/RegionSelect/RegionOption.tsx +++ b/packages/manager/src/components/RegionSelect/RegionOption.tsx @@ -1,101 +1,35 @@ -import { Box, Tooltip } from '@linode/ui'; -import { visuallyHidden } from '@mui/utils'; +import { Box } from '@linode/ui'; import React from 'react'; -import DistributedRegion from 'src/assets/icons/entityIcons/distributed-region.svg'; import { Flag } from 'src/components/Flag'; +import { ListItemOption } from 'src/components/ListItemOption'; import { useIsGeckoEnabled } from 'src/components/RegionSelect/RegionSelect.utils'; import { Stack } from 'src/components/Stack'; -import { TooltipIcon } from 'src/components/TooltipIcon'; -import { - SelectedIcon, - StyledListItem, - sxDistributedRegionIcon, -} from './RegionSelect.styles'; - -import type { DisableRegionOption } from './RegionSelect.types'; import type { Region } from '@linode/api-v4'; -import type { ListItemComponentsPropsOverrides } from '@mui/material/ListItem'; - -interface Props { - disabledOptions?: DisableRegionOption; - props: React.HTMLAttributes; - region: Region; - selected?: boolean; -} +import type { ListItemProps } from 'src/components/ListItemOption'; export const RegionOption = ({ disabledOptions, + item, props, - region, selected, -}: Props) => { - const { className, onClick } = props; - const isRegionDisabled = Boolean(disabledOptions); - const isRegionDisabledReason = disabledOptions?.reason; - const { isGeckoBetaEnabled, isGeckoLAEnabled } = useIsGeckoEnabled(); - const displayDistributedRegionIcon = - isGeckoBetaEnabled && region.site_type === 'distributed'; +}: ListItemProps) => { + const { isGeckoLAEnabled } = useIsGeckoEnabled(); return ( - - - isRegionDisabled ? e.preventDefault() : onClick ? onClick(e) : null - } - aria-disabled={undefined} - className={isRegionDisabled ? `${className} Mui-disabled` : className} - data-qa-disabled-item={isRegionDisabled} - > - - - {isGeckoLAEnabled ? region.label : `${region.label} (${region.id})`} - {displayDistributedRegionIcon && ( - -  (This region is a distributed region.) - - )} - {isRegionDisabled && isRegionDisabledReason && ( - {isRegionDisabledReason} - )} - - {isGeckoLAEnabled && `(${region.id})`} - {selected && } - {displayDistributedRegionIcon && ( - } - status="other" - sxTooltipIcon={sxDistributedRegionIcon} - text="This region is a distributed region." - /> - )} - - - + + + {isGeckoLAEnabled ? item.label : `${item.label} (${item.id})`} + + {isGeckoLAEnabled && `(${item.id})`} + + ); }; diff --git a/packages/manager/src/components/RegionSelect/RegionSelect.styles.ts b/packages/manager/src/components/RegionSelect/RegionSelect.styles.ts index b6af8c5dffc..46605c20261 100644 --- a/packages/manager/src/components/RegionSelect/RegionSelect.styles.ts +++ b/packages/manager/src/components/RegionSelect/RegionSelect.styles.ts @@ -1,5 +1,4 @@ import { Box } from '@linode/ui'; -import DoneIcon from '@mui/icons-material/Done'; import { styled } from '@mui/material/styles'; import { Chip } from 'src/components/Chip'; @@ -30,44 +29,6 @@ export const StyledAutocompleteContainer = styled(Box, { }, })); -export const sxDistributedRegionIcon = { - '& svg': { - color: 'inherit !important', - height: 21, - width: 24, - }, - '&:hover': { - color: 'inherit', - }, - color: 'inherit', - padding: 0, -}; - -export const StyledDistributedRegionBox = styled(Box, { - label: 'StyledDistributedRegionBox', - shouldForwardProp: (prop) => prop != 'centerChildren', -})<{ centerChildren: boolean }>(({ centerChildren, theme }) => ({ - '& svg': { - height: 21, - marginLeft: 8, - marginRight: 8, - width: 24, - }, - alignSelf: centerChildren ? 'center' : 'end', - color: 'inherit', - display: 'flex', - marginTop: centerChildren ? 21 : 0, - padding: 8, - [theme.breakpoints.down('md')]: { - '& svg': { - marginLeft: 0, - }, - alignSelf: 'start', - marginTop: 0, - paddingLeft: 0, - }, -})); - export const StyledLParentListItem = styled(ListItem, { label: 'RegionSelectParentListItem', })(() => ({ @@ -80,32 +41,6 @@ export const StyledLParentListItem = styled(ListItem, { }, })); -export const StyledListItem = styled(ListItem, { - label: 'RegionSelectListItem', -})(() => ({ - '&.Mui-disabled': { - cursor: 'not-allowed', - }, - '&.MuiAutocomplete-option': { - minHeight: 'auto !important', - padding: '8px 10px !important', - }, - '&.MuiListItem-root[aria-disabled="true"]:active': { - pointerEvents: 'none !important', - }, -})); - -export const SelectedIcon = styled(DoneIcon, { - label: 'RegionSelectSelectedIcon', - shouldForwardProp: (prop) => prop != 'visible', -})<{ visible: boolean }>(({ visible }) => ({ - height: 17, - marginLeft: '-2px', - marginRight: '5px', - visibility: visible ? 'visible' : 'hidden', - width: 17, -})); - export const StyledChip = styled(Chip)(({ theme }) => ({ '& .MuiChip-deleteIcon': { '& svg': { diff --git a/packages/manager/src/components/RegionSelect/RegionSelect.test.tsx b/packages/manager/src/components/RegionSelect/RegionSelect.test.tsx index bd97e2a64a1..3f6721f2ac6 100644 --- a/packages/manager/src/components/RegionSelect/RegionSelect.test.tsx +++ b/packages/manager/src/components/RegionSelect/RegionSelect.test.tsx @@ -64,26 +64,4 @@ describe('RegionSelect', () => { ); expect(getByTestId('textfield-input')).toBeDisabled(); }); - - it('should render a Select component with distributed region text', () => { - const newProps = { - ...props, - showDistributedRegionIconHelperText: true, - }; - const { getByTestId } = renderWithTheme(); - expect( - getByTestId('region-select-distributed-region-text') - ).toBeInTheDocument(); - }); - - it('should render a Select component with no distributed region text', () => { - const newProps = { - ...props, - showDistributedRegionIconHelperText: false, - }; - const { queryByTestId } = renderWithTheme(); - expect( - queryByTestId('region-select-distributed-region-text') - ).not.toBeInTheDocument(); - }); }); diff --git a/packages/manager/src/components/RegionSelect/RegionSelect.tsx b/packages/manager/src/components/RegionSelect/RegionSelect.tsx index 49650ddee1d..b719eb824fd 100644 --- a/packages/manager/src/components/RegionSelect/RegionSelect.tsx +++ b/packages/manager/src/components/RegionSelect/RegionSelect.tsx @@ -1,32 +1,22 @@ -import { Typography } from '@mui/material'; import { createFilterOptions } from '@mui/material/Autocomplete'; import * as React from 'react'; -import DistributedRegion from 'src/assets/icons/entityIcons/distributed-region.svg'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { Flag } from 'src/components/Flag'; -import { Link } from 'src/components/Link'; import { useIsGeckoEnabled } from 'src/components/RegionSelect/RegionSelect.utils'; -import { TooltipIcon } from 'src/components/TooltipIcon'; import { useAllAccountAvailabilitiesQuery } from 'src/queries/account/availability'; import { getRegionCountryGroup } from 'src/utilities/formatRegion'; import { RegionOption } from './RegionOption'; -import { - StyledAutocompleteContainer, - StyledDistributedRegionBox, - sxDistributedRegionIcon, -} from './RegionSelect.styles'; +import { StyledAutocompleteContainer } from './RegionSelect.styles'; import { getRegionOptions, isRegionOptionUnavailable, } from './RegionSelect.utils'; -import type { - DisableRegionOption, - RegionSelectProps, -} from './RegionSelect.types'; +import type { RegionSelectProps } from './RegionSelect.types'; import type { Region } from '@linode/api-v4'; +import type { DisableItemOption } from 'src/components/ListItemOption'; /** * A specific select for regions. @@ -57,13 +47,12 @@ export const RegionSelect = < regionFilter, regions, required, - showDistributedRegionIconHelperText, tooltipText, value, width, } = props; - const { isGeckoBetaEnabled, isGeckoLAEnabled } = useIsGeckoEnabled(); + const { isGeckoLAEnabled } = useIsGeckoEnabled(); const { data: accountAvailability, @@ -81,7 +70,7 @@ export const RegionSelect = < : null; const disabledRegions = regionOptions.reduce< - Record + Record >((acc, region) => { if (disabledRegionsFromProps?.[region.id]) { acc[region.id] = disabledRegionsFromProps[region.id]; @@ -102,24 +91,6 @@ export const RegionSelect = < return acc; }, {}); - const EndAdornment = React.useMemo(() => { - // @TODO Gecko: Remove adornment after LA - if (isGeckoBetaEnabled && selectedRegion?.site_type === 'distributed') { - return ( - } - status="other" - sxTooltipIcon={sxDistributedRegionIcon} - text="This region is a distributed region." - /> - ); - } - if (isGeckoLAEnabled && selectedRegion) { - return `(${selectedRegion?.id})`; - } - return null; - }, [isGeckoBetaEnabled, isGeckoLAEnabled, selectedRegion]); - /* * When Gecko is enabled, allow regions to be searched by ID by passing a * custom stringify function. @@ -142,9 +113,9 @@ export const RegionSelect = < return ( ); }} @@ -156,7 +127,8 @@ export const RegionSelect = < textFieldProps={{ ...props.textFieldProps, InputProps: { - endAdornment: EndAdornment, + endAdornment: + isGeckoLAEnabled && selectedRegion && `(${selectedRegion?.id})`, required, startAdornment: selectedRegion && ( @@ -184,25 +156,6 @@ export const RegionSelect = < placeholder={placeholder ?? 'Select a Region'} value={selectedRegion as Region} /> - {showDistributedRegionIconHelperText && ( // @TODO Gecko Beta: Add docs link - - - - {' '} - Indicates a distributed region.{' '} - - Learn more - - . - - - )} ); }; diff --git a/packages/manager/src/components/RegionSelect/RegionSelect.types.ts b/packages/manager/src/components/RegionSelect/RegionSelect.types.ts index 8c46d66c574..87a9b6b9344 100644 --- a/packages/manager/src/components/RegionSelect/RegionSelect.types.ts +++ b/packages/manager/src/components/RegionSelect/RegionSelect.types.ts @@ -6,19 +6,7 @@ import type { } from '@linode/api-v4'; import type React from 'react'; import type { EnhancedAutocompleteProps } from 'src/components/Autocomplete/Autocomplete'; - -export interface DisableRegionOption { - /** - * The reason the region option is disabled. - * This is shown to the user as a tooltip. - */ - reason: JSX.Element | string; - /** - * An optional minWith applied to the tooltip - * @default 215 - */ - tooltipWidth?: number; -} +import type { DisableItemOption } from 'src/components/ListItemOption'; export type RegionFilterValue = | 'distributed-AF' @@ -49,7 +37,7 @@ export interface RegionSelectProps< /** * A key/value object for disabling regions by their ID. */ - disabledRegions?: Record; + disabledRegions?: Record; helperText?: string; /** * Ignores account availability information when rendering region options @@ -60,7 +48,6 @@ export interface RegionSelectProps< regionFilter?: RegionFilterValue; regions: Region[]; required?: boolean; - showDistributedRegionIconHelperText?: boolean; tooltipText?: string; /** * The ID of the selected region. @@ -79,7 +66,7 @@ export interface RegionMultiSelectProps selectedRegions: Region[]; }>; currentCapability: Capabilities | undefined; - disabledRegions?: Record; + disabledRegions?: Record; helperText?: string; isClearable?: boolean; label?: string; diff --git a/packages/manager/src/features/Images/ImagesLanding/ImageRegions/ManageImageRegionsForm.tsx b/packages/manager/src/features/Images/ImagesLanding/ImageRegions/ManageImageRegionsForm.tsx index 85c800b961f..50d6c634731 100644 --- a/packages/manager/src/features/Images/ImagesLanding/ImageRegions/ManageImageRegionsForm.tsx +++ b/packages/manager/src/features/Images/ImagesLanding/ImageRegions/ManageImageRegionsForm.tsx @@ -1,3 +1,4 @@ +import { Paper } from '@linode/ui'; import { useSnackbar } from 'notistack'; import React from 'react'; import { useForm } from 'react-hook-form'; @@ -5,7 +6,6 @@ import { useForm } from 'react-hook-form'; import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from '@linode/ui'; import { RegionMultiSelect } from 'src/components/RegionSelect/RegionMultiSelect'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; @@ -21,7 +21,7 @@ import type { UpdateImageRegionsPayload, } from '@linode/api-v4'; import type { Resolver } from 'react-hook-form'; -import type { DisableRegionOption } from 'src/components/RegionSelect/RegionSelect.types'; +import type { DisableItemOption } from 'src/components/ListItemOption'; interface Props { image: Image | undefined; @@ -79,7 +79,7 @@ export const ManageImageReplicasForm = (props: Props) => { const values = watch(); - const disabledRegions: Record = {}; + const disabledRegions: Record = {}; const availableRegions = image?.regions.filter( (regionItem) => regionItem.status === 'available' diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Addons/Addons.test.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Addons/Addons.test.tsx index 8395ece6840..2199037af5e 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Addons/Addons.test.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Addons/Addons.test.tsx @@ -34,7 +34,7 @@ describe('Linode Create Addons', () => { }); await findByText( - 'Backups and Private IP are currently not available for distributed regions.' + 'Backups and Private IP are not available for distributed regions.' ); }); }); diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Addons/Addons.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Addons/Addons.tsx index ccf023632dc..6fc8200980e 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Addons/Addons.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Addons/Addons.tsx @@ -32,7 +32,7 @@ export const Addons = () => { Add-ons {isDistributedRegionSelected && ( )} diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx index 84d57ffd506..dcb9d03cdc3 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx @@ -1,3 +1,4 @@ +import { Paper } from '@linode/ui'; import { Box } from '@linode/ui'; import { useQueryClient } from '@tanstack/react-query'; import React from 'react'; @@ -7,7 +8,6 @@ import { DocsLink } from 'src/components/DocsLink/DocsLink'; import { useIsDiskEncryptionFeatureEnabled } from 'src/components/Encryption/utils'; import { Link } from 'src/components/Link'; import { Notice } from 'src/components/Notice/Notice'; -import { Paper } from '@linode/ui'; import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; import { isDistributedRegionSupported, @@ -85,7 +85,7 @@ export const Region = React.memo(() => { const { data: regions } = useRegionsQuery(); - const { isGeckoBetaEnabled, isGeckoLAEnabled } = useIsGeckoEnabled(); + const { isGeckoLAEnabled } = useIsGeckoEnabled(); const showTwoStepRegion = isGeckoLAEnabled && isDistributedRegionSupported(params.type ?? 'OS'); @@ -177,10 +177,6 @@ export const Region = React.memo(() => { !flags.gecko2?.enabled || !isDistributedRegionSupported(params.type ?? 'OS'); - const showDistributedRegionIconHelperText = - isGeckoBetaEnabled && !hideDistributedRegions; - regions?.some((region) => region.site_type === 'distributed'); - const disabledRegions = getDisabledRegions({ regions: regions ?? [], selectedImage: image, @@ -195,9 +191,6 @@ export const Region = React.memo(() => { ? 'core' : undefined } - showDistributedRegionIconHelperText={ - showDistributedRegionIconHelperText - } disabled={isLinodeCreateRestricted} disabledRegions={disabledRegions} errorText={fieldState.error?.message} @@ -241,9 +234,6 @@ export const Region = React.memo(() => { ? 'core' : undefined } - showDistributedRegionIconHelperText={ - showDistributedRegionIconHelperText - } currentCapability="Linodes" disableClearable disabled={isLinodeCreateRestricted} diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Region.utils.ts b/packages/manager/src/features/Linodes/LinodeCreate/Region.utils.ts index cf81a46a69f..52bbf1b35d9 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Region.utils.ts +++ b/packages/manager/src/features/Linodes/LinodeCreate/Region.utils.ts @@ -1,5 +1,5 @@ import type { Image, Region } from '@linode/api-v4'; -import type { DisableRegionOption } from 'src/components/RegionSelect/RegionSelect.types'; +import type { DisableItemOption } from 'src/components/ListItemOption'; interface DisabledRegionOptions { regions: Region[]; @@ -21,7 +21,7 @@ export const getDisabledRegions = (options: DisabledRegionOptions) => { selectedImage && !selectedImage.capabilities.includes('distributed-sites') ) { - const disabledRegions: Record = {}; + const disabledRegions: Record = {}; for (const region of regions) { if (region.site_type === 'distributed') { diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Images.test.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Images.test.tsx index 39f06009405..4ba95eac43a 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Images.test.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Images.test.tsx @@ -3,9 +3,6 @@ import React from 'react'; import { renderWithThemeAndHookFormContext } from 'src/utilities/testHelpers'; import { Images } from './Images'; -import { HttpResponse, http, server } from 'src/mocks/testServer'; -import { imageFactory } from 'src/factories'; -import { makeResourcePage } from 'src/mocks/serverHandlers'; describe('Images', () => { it('renders a header', () => { @@ -30,27 +27,4 @@ describe('Images', () => { expect(getByLabelText('Images')).toBeVisible(); expect(getByPlaceholderText('Choose an image')).toBeVisible(); }); - - it('renders a "Indicates compatibility with distributed compute regions." notice if the user has at least one image with the distributed capability', async () => { - server.use( - http.get('*/v4/images', () => { - const images = [ - imageFactory.build({ capabilities: [] }), - imageFactory.build({ capabilities: ['distributed-sites'] }), - imageFactory.build({ capabilities: [] }), - ]; - return HttpResponse.json(makeResourcePage(images)); - }) - ); - - const { findByText } = renderWithThemeAndHookFormContext({ - component: , - }); - - expect( - await findByText( - 'Indicates compatibility with distributed compute regions.' - ) - ).toBeVisible(); - }); }); diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Images.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Images.tsx index be608ee1192..76b15841eba 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Images.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/Images.tsx @@ -1,14 +1,13 @@ +import { Paper } from '@linode/ui'; import { Box } from '@linode/ui'; import { useQueryClient } from '@tanstack/react-query'; import React from 'react'; import { useController, useFormContext, useWatch } from 'react-hook-form'; -import DistributedRegionIcon from 'src/assets/icons/entityIcons/distributed-region.svg'; import ImageIcon from 'src/assets/icons/entityIcons/image.svg'; import { ImageSelect } from 'src/components/ImageSelect/ImageSelect'; import { getAPIFilterForImageSelect } from 'src/components/ImageSelect/utilities'; import { Link } from 'src/components/Link'; -import { Paper } from '@linode/ui'; import { Placeholder } from 'src/components/Placeholder/Placeholder'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; @@ -45,11 +44,11 @@ export const Images = () => { const { data: regions } = useRegionsQuery(); + const selectedRegion = regions?.find((r) => r.id === regionId); + const onChange = async (image: Image | null) => { field.onChange(image?.id ?? null); - const selectedRegion = regions?.find((r) => r.id === regionId); - // Non-"distributed compatible" Images must only be deployed to core sites. // Clear the region field if the currently selected region is a distributed site and the Image is only core compatible. // @todo: delete this logic when all Images are "distributed compatible" @@ -76,11 +75,6 @@ export const Images = () => { getAPIFilterForImageSelect('private') ); - // @todo: delete this logic when all Images are "distributed compatible" - const showDistributedCapabilityNotice = images?.some((image) => - image.capabilities.includes('distributed-sites') - ); - if (images?.length === 0) { return ( @@ -106,18 +100,11 @@ export const Images = () => { errorText={fieldState.error?.message} onBlur={field.onBlur} onChange={onChange} + siteType={selectedRegion?.site_type} sx={{ width: '416px' }} value={field.value ?? null} variant="private" /> - {showDistributedCapabilityNotice && ( - - - - Indicates compatibility with distributed compute regions. - - - )} diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/OperatingSystems.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/OperatingSystems.tsx index f40539509f9..797401d1fb6 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Tabs/OperatingSystems.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Tabs/OperatingSystems.tsx @@ -1,12 +1,13 @@ import { Paper } from '@linode/ui'; import { useQueryClient } from '@tanstack/react-query'; import React from 'react'; -import { useController, useFormContext } from 'react-hook-form'; +import { useController, useFormContext, useWatch } from 'react-hook-form'; import { ImageSelect } from 'src/components/ImageSelect/ImageSelect'; import { Stack } from 'src/components/Stack'; import { Typography } from 'src/components/Typography'; import { useRestrictedGlobalGrantCheck } from 'src/hooks/useRestrictedGlobalGrantCheck'; +import { useRegionQuery } from 'src/queries/regions/regions'; import { Region } from '../Region'; import { getGeneratedLinodeLabel } from '../utilities'; @@ -29,6 +30,12 @@ export const OperatingSystems = () => { name: 'image', }); + const regionId = useWatch({ + name: 'region', + }); + + const { data: region } = useRegionQuery(regionId ?? -1); + const isCreateLinodeRestricted = useRestrictedGlobalGrantCheck({ globalGrantType: 'add_linodes', }); @@ -58,6 +65,7 @@ export const OperatingSystems = () => { onBlur={field.onBlur} onChange={onChange} placeholder="Choose a Linux distribution" + siteType={region?.site_type} value={field.value} variant="public" /> diff --git a/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx b/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx index b0b7e8bf696..b3fe402b56e 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx @@ -1,9 +1,9 @@ +import { Paper } from '@linode/ui'; import { Box } from '@linode/ui'; import * as React from 'react'; import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { DocsLink } from 'src/components/DocsLink/DocsLink'; -import { Paper } from '@linode/ui'; import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; import { RegionHelperText } from 'src/components/SelectRegionPanel/RegionHelperText'; import { SafeTabPanel } from 'src/components/Tabs/SafeTabPanel'; @@ -116,7 +116,6 @@ export const TwoStepRegion = (props: CombinedProps) => { onChange={(e, region) => onChange(region)} regionFilter="core" regions={regions ?? []} - showDistributedRegionIconHelperText={false} value={value} /> @@ -147,7 +146,6 @@ export const TwoStepRegion = (props: CombinedProps) => { onChange={(e, region) => onChange(region)} regionFilter={regionFilter} regions={regions ?? []} - showDistributedRegionIconHelperText={false} value={value} /> diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/RebuildFromImage.test.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/RebuildFromImage.test.tsx index 3ac60b9e85a..ecdc17ca5f1 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/RebuildFromImage.test.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeRebuild/RebuildFromImage.test.tsx @@ -1,8 +1,8 @@ -import { render } from '@testing-library/react'; import * as React from 'react'; import { reactRouterProps } from 'src/__data__/reactRouterProps'; -import { renderWithTheme, wrapWithTheme } from 'src/utilities/testHelpers'; +import { wrapWithTheme } from 'src/utilities/testHelpers'; +import { renderWithThemeAndHookFormContext } from 'src/utilities/testHelpers'; import { RebuildFromImage } from './RebuildFromImage'; @@ -47,15 +47,17 @@ describe('RebuildFromImage', () => { }); it('renders a SelectImage panel', () => { - const { queryByText } = render( - wrapWithTheme() - ); + const { queryByText } = renderWithThemeAndHookFormContext({ + component: wrapWithTheme(), + }); expect(queryByText('Select Image')).toBeInTheDocument(); }); // @TODO LDE: Remove feature flagging/conditionality once LDE is fully rolled out it('does not render a "Disk Encryption" section when the Disk Encryption feature is disabled', () => { - const { queryByText } = renderWithTheme(); + const { queryByText } = renderWithThemeAndHookFormContext({ + component: , + }); expect(queryByText('Encrypt Disk')).not.toBeInTheDocument(); }); @@ -69,7 +71,9 @@ describe('RebuildFromImage', () => { } ); - const { queryByText } = renderWithTheme(); + const { queryByText } = renderWithThemeAndHookFormContext({ + component: , + }); expect(queryByText('Encrypt Disk')).toBeInTheDocument(); }); diff --git a/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/ImageAndPassword.test.tsx b/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/ImageAndPassword.test.tsx index e52b0bbc9ed..6c0f85f2406 100644 --- a/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/ImageAndPassword.test.tsx +++ b/packages/manager/src/features/Linodes/LinodesDetail/LinodeSettings/ImageAndPassword.test.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { accountUserFactory } from 'src/factories/accountUsers'; import { makeResourcePage } from 'src/mocks/serverHandlers'; import { HttpResponse, http, server } from 'src/mocks/testServer'; -import { renderWithTheme } from 'src/utilities/testHelpers'; +import { renderWithThemeAndHookFormContext } from 'src/utilities/testHelpers'; import { ImageAndPassword } from './ImageAndPassword'; @@ -20,16 +20,18 @@ const props = { describe('ImageAndPassword', () => { it('should render an Image Select', () => { - renderWithTheme(); + renderWithThemeAndHookFormContext({ + component: , + }); expect(screen.getByRole('combobox')); expect(screen.getByRole('combobox')).toBeEnabled(); }); it('should render a password error if defined', async () => { const errorMessage = 'Unable to set password.'; - const { findByText } = renderWithTheme( - - ); + const { findByText } = renderWithThemeAndHookFormContext({ + component: , + }); const passwordError = await findByText(errorMessage, undefined, { timeout: 2500, @@ -37,7 +39,9 @@ describe('ImageAndPassword', () => { expect(passwordError).toBeVisible(); }); it('should render an SSH Keys section', async () => { - const { getByText } = renderWithTheme(); + const { getByText } = renderWithThemeAndHookFormContext({ + component: , + }); expect(getByText('SSH Keys', { selector: 'h2' })).toBeVisible(); }); @@ -50,7 +54,9 @@ describe('ImageAndPassword', () => { }) ); - const { findByText } = renderWithTheme(); + const { findByText } = renderWithThemeAndHookFormContext({ + component: , + }); for (const user of users) { // eslint-disable-next-line no-await-in-loop diff --git a/packages/manager/src/features/Linodes/MigrateLinode/ConfigureForm.tsx b/packages/manager/src/features/Linodes/MigrateLinode/ConfigureForm.tsx index 1964707fa22..c8f1853c14c 100644 --- a/packages/manager/src/features/Linodes/MigrateLinode/ConfigureForm.tsx +++ b/packages/manager/src/features/Linodes/MigrateLinode/ConfigureForm.tsx @@ -1,13 +1,9 @@ import * as React from 'react'; -import DistributedRegion from 'src/assets/icons/entityIcons/distributed-region.svg'; import { Flag } from 'src/components/Flag'; import { Notice } from 'src/components/Notice/Notice'; import { PlacementGroupsSelect } from 'src/components/PlacementGroupsSelect/PlacementGroupsSelect'; import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; -import { sxDistributedRegionIcon } from 'src/components/RegionSelect/RegionSelect.styles'; -import { useIsGeckoEnabled } from 'src/components/RegionSelect/RegionSelect.utils'; -import { TooltipIcon } from 'src/components/TooltipIcon'; import { Typography } from 'src/components/Typography'; import { NO_PLACEMENT_GROUPS_IN_SELECTED_REGION_MESSAGE } from 'src/features/PlacementGroups/constants'; import { useIsPlacementGroupsEnabled } from 'src/features/PlacementGroups/utils'; @@ -148,8 +144,6 @@ export const ConfigureForm = React.memo((props: Props) => { const linodeIsInDistributedRegion = currentActualRegion?.site_type === 'distributed'; - const { isGeckoBetaEnabled } = useIsGeckoEnabled(); - return ( Configure Migration @@ -161,14 +155,6 @@ export const ConfigureForm = React.memo((props: Props) => { {`${getRegionCountryGroup(currentActualRegion)}: ${ currentActualRegion?.label ?? currentRegion }`} - {isGeckoBetaEnabled && linodeIsInDistributedRegion && ( - } - status="other" - sxTooltipIcon={sxDistributedRegionIcon} - text="This region is a distributed region." - /> - )} {shouldDisplayPriceComparison && ( >( + const disabledRegions = regions?.reduce>( (acc, region) => { const isRegionAtCapacity = hasRegionReachedPlacementGroupCapacity({ allPlacementGroups: allPlacementGroupsInRegion, diff --git a/packages/manager/src/features/StackScripts/StackScriptCreate/StackScriptCreate.test.tsx b/packages/manager/src/features/StackScripts/StackScriptCreate/StackScriptCreate.test.tsx index 27e340db290..49012e7b97b 100644 --- a/packages/manager/src/features/StackScripts/StackScriptCreate/StackScriptCreate.test.tsx +++ b/packages/manager/src/features/StackScripts/StackScriptCreate/StackScriptCreate.test.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { reactRouterProps } from 'src/__data__/reactRouterProps'; import { profileFactory } from 'src/factories'; import { queryClientFactory } from 'src/queries/base'; -import { renderWithTheme } from 'src/utilities/testHelpers'; +import { renderWithThemeAndHookFormContext } from 'src/utilities/testHelpers'; import { StackScriptCreate } from './StackScriptCreate'; @@ -15,21 +15,22 @@ const queryClient = queryClientFactory(); describe('StackScriptCreate', () => { it('should render header, inputs, and buttons', () => { - const { getByLabelText, getByText } = renderWithTheme( - - } - grants={{ data: {} } as UseQueryResult} - mode="create" - queryClient={queryClient} - />, - { queryClient } - ); + const { getByLabelText, getByText } = renderWithThemeAndHookFormContext({ + component: ( + + } + grants={{ data: {} } as UseQueryResult} + mode="create" + queryClient={queryClient} + /> + ), + }); expect(getByText('Create')).toBeVisible(); diff --git a/packages/manager/src/features/StackScripts/StackScriptForm/StackScriptForm.test.tsx b/packages/manager/src/features/StackScripts/StackScriptForm/StackScriptForm.test.tsx index 8b91134e9fd..b4be7d1e3eb 100644 --- a/packages/manager/src/features/StackScripts/StackScriptForm/StackScriptForm.test.tsx +++ b/packages/manager/src/features/StackScripts/StackScriptForm/StackScriptForm.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import { renderWithTheme } from 'src/utilities/testHelpers'; +import { renderWithThemeAndHookFormContext } from 'src/utilities/testHelpers'; import { StackScriptForm } from './StackScriptForm'; @@ -34,7 +34,9 @@ const props = { describe('StackScriptCreate', () => { it('should render', () => { - const { getByText } = renderWithTheme(); + const { getByText } = renderWithThemeAndHookFormContext({ + component: , + }); getByText(/stackscript label/i); }); }); From bcdefc920b532c0f165b2c60de292c5c47916435 Mon Sep 17 00:00:00 2001 From: corya-akamai <136115382+corya-akamai@users.noreply.github.com> Date: Wed, 6 Nov 2024 16:23:13 -0500 Subject: [PATCH 53/66] fix: [UIE-8246] - DBaaS provisioning 2 node clusters (#11218) --- .../pr-11218-removed-1730922731323.md | 5 ++ packages/api-v4/src/databases/types.ts | 25 ++++----- .../pr-11218-fixed-1730922753813.md | 5 ++ packages/manager/src/factories/databases.ts | 48 +--------------- .../DatabaseCreate/DatabaseClusterData.tsx | 15 +++-- .../DatabaseCreate/DatabaseCreate.tsx | 56 +++++++++---------- .../src/features/Databases/utilities.ts | 2 - packages/manager/src/mocks/serverHandlers.ts | 31 +++++----- packages/validation/src/databases.schema.ts | 33 +---------- 9 files changed, 75 insertions(+), 145 deletions(-) create mode 100644 packages/api-v4/.changeset/pr-11218-removed-1730922731323.md create mode 100644 packages/manager/.changeset/pr-11218-fixed-1730922753813.md diff --git a/packages/api-v4/.changeset/pr-11218-removed-1730922731323.md b/packages/api-v4/.changeset/pr-11218-removed-1730922731323.md new file mode 100644 index 00000000000..0be878c933c --- /dev/null +++ b/packages/api-v4/.changeset/pr-11218-removed-1730922731323.md @@ -0,0 +1,5 @@ +--- +"@linode/api-v4": Removed +--- + +DBaaS deprecated types including MongoDB and Redis ([#11218](https://github.com/linode/manager/pull/11218)) diff --git a/packages/api-v4/src/databases/types.ts b/packages/api-v4/src/databases/types.ts index a499032962a..87b4458308a 100644 --- a/packages/api-v4/src/databases/types.ts +++ b/packages/api-v4/src/databases/types.ts @@ -20,7 +20,7 @@ export interface DatabaseType extends BaseType { engines: Engines; } -export type Engine = 'mysql' | 'postgresql' | 'mongodb' | 'redis'; +export type Engine = 'mysql' | 'postgresql'; export interface DatabaseEngine { id: string; @@ -103,6 +103,7 @@ export type ClusterSize = 1 | 2 | 3; type ReadonlyCount = 0 | 2; +/** @deprecated TODO (UIE-8214) remove POST GA */ export type MySQLReplicationType = 'none' | 'semi_synch' | 'asynch'; export interface CreateDatabasePayload { @@ -120,7 +121,10 @@ export interface CreateDatabasePayload { type: string; } +/** @deprecated TODO (UIE-8214) remove POST GA */ type DriverTypes = 'jdbc' | 'odbc' | 'php' | 'python' | 'ruby' | 'node.js'; + +/** @deprecated TODO (UIE-8214) remove POST GA */ interface ConnectionStrings { driver: DriverTypes; value: string; @@ -173,20 +177,24 @@ interface BaseDatabase extends DatabaseInstance { used_disk_size_gb?: number; } +/** @deprecated TODO (UIE-8214) remove POST GA */ export interface MySQLDatabase extends BaseDatabase { /** @Deprecated used by rdbms-legacy only */ replication_type?: MySQLReplicationType; } +/** @deprecated TODO (UIE-8214) remove POST GA */ export type PostgresReplicationType = 'none' | 'synch' | 'asynch'; -type ReplicationCommitTypes = +/** @deprecated TODO (UIE-8214) remove POST GA */ +export type ReplicationCommitTypes = | 'on' | 'local' | 'remote_write' | 'remote_apply' | 'off'; +/** @deprecated TODO (UIE-8214) remove POST GA */ export interface PostgresDatabase extends BaseDatabase { /** @Deprecated used by rdbms-legacy only */ replication_type?: PostgresReplicationType; @@ -194,22 +202,13 @@ export interface PostgresDatabase extends BaseDatabase { replication_commit_type?: ReplicationCommitTypes; } -type MongoStorageEngine = 'wiredtiger' | 'mmapv1'; -type MongoCompressionType = 'none' | 'snappy' | 'zlib'; -export interface MongoDatabase extends BaseDatabase { - storage_engine: MongoStorageEngine; - compression_type: MongoCompressionType; - replica_set: string | null; - peers: string[]; -} - +/** @deprecated TODO (UIE-8214) remove POST GA */ export type ComprehensiveReplicationType = MySQLReplicationType & PostgresReplicationType; export type Database = BaseDatabase & Partial & - Partial & - Partial; + Partial; export interface UpdateDatabasePayload { cluster_size?: number; diff --git a/packages/manager/.changeset/pr-11218-fixed-1730922753813.md b/packages/manager/.changeset/pr-11218-fixed-1730922753813.md new file mode 100644 index 00000000000..a7dfe79d811 --- /dev/null +++ b/packages/manager/.changeset/pr-11218-fixed-1730922753813.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +DBaaS enable creation of two node clusters ([#11218](https://github.com/linode/manager/pull/11218)) diff --git a/packages/manager/src/factories/databases.ts b/packages/manager/src/factories/databases.ts index 3877ef2a84a..a8cfd57cd1d 100644 --- a/packages/manager/src/factories/databases.ts +++ b/packages/manager/src/factories/databases.ts @@ -70,29 +70,6 @@ export const databaseTypeFactory = Factory.Sync.makeFactory({ class: 'standard', disk: Factory.each((i) => i * 20480), engines: { - mongodb: [ - { - price: { - hourly: 0.03, - monthly: 50, - }, - quantity: 1, - }, - { - price: { - hourly: 0.08, - monthly: 88, - }, - quantity: 2, - }, - { - price: { - hourly: 0.22, - monthly: 116, - }, - quantity: 3, - }, - ], mysql: [ { price: { @@ -139,29 +116,6 @@ export const databaseTypeFactory = Factory.Sync.makeFactory({ quantity: 3, }, ], - redis: [ - { - price: { - hourly: 0.08, - monthly: 180, - }, - quantity: 1, - }, - { - price: { - hourly: 0.16, - monthly: 360, - }, - quantity: 2, - }, - { - price: { - hourly: 0.32, - monthly: 540, - }, - quantity: 3, - }, - ], }, id: Factory.each((i) => possibleTypes[i % possibleTypes.length]), label: Factory.each((i) => `Linode ${i} GB`), @@ -254,7 +208,7 @@ export const databaseFactory = Factory.Sync.makeFactory({ '2.2.2.2': 'primary', }, oldest_restore_time: '2024-09-15T17:15:12', - platform: pickRandom(['rdbms-legacy', 'rdbms-default']), + platform: Factory.each((i) => (adb10(i) ? 'rdbms-legacy' : 'rdbms-default')), port: 3306, region: 'us-east', ssl_connection: false, diff --git a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseClusterData.tsx b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseClusterData.tsx index ddb10e80304..0bfd67a0b85 100644 --- a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseClusterData.tsx +++ b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseClusterData.tsx @@ -19,10 +19,12 @@ import { getEngineOptions } from './utilities'; import type { ClusterSize, - ComprehensiveReplicationType, DatabaseEngine, Engine, + MySQLReplicationType, + PostgresReplicationType, Region, + ReplicationCommitTypes, } from '@linode/api-v4'; import type { FormikErrors } from 'formik'; import type { Item } from 'src/components/EnhancedSelect'; @@ -32,14 +34,15 @@ export interface DatabaseCreateValues { error: string; }[]; cluster_size: ClusterSize; - compression_type: undefined; engine: Engine; label: string; region: string; - replication_commit_type: undefined; - replication_type: ComprehensiveReplicationType; - ssl_connection: boolean; - storage_engine: undefined; + /** @Deprecated used by rdbms-legacy PostgreSQL only */ + replication_commit_type?: ReplicationCommitTypes; + /** @Deprecated used by rdbms-legacy only */ + replication_type?: MySQLReplicationType | PostgresReplicationType; + /** @Deprecated used by rdbms-legacy only, rdbms-default always uses TLS */ + ssl_connection?: boolean; type: string; } diff --git a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx index 06bc649e980..f1db5c4759b 100644 --- a/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx +++ b/packages/manager/src/features/Databases/DatabaseCreate/DatabaseCreate.tsx @@ -40,10 +40,8 @@ import { scrollErrorIntoViewV2 } from 'src/utilities/scrollErrorIntoViewV2'; import { DatabaseCreateAccessControls } from './DatabaseCreateAccessControls'; import { - determineCompressionType, determineReplicationCommitType, determineReplicationType, - determineStorageEngine, } from './utilities'; import type { @@ -54,6 +52,7 @@ import type { } from '@linode/api-v4/lib/databases/types'; import type { APIError } from '@linode/api-v4/lib/types'; import type { PlanSelectionWithDatabaseType } from 'src/features/components/PlansPanel/types'; +import type { DatabaseCreateValues } from 'src/features/Databases/DatabaseCreate/DatabaseClusterData'; import type { ExtendedIP } from 'src/utilities/ipUtils'; const DatabaseCreate = () => { @@ -138,9 +137,6 @@ const DatabaseCreate = () => { ...values, allow_list: _allow_list, }; - if (isDatabasesV2Enabled) { - delete createPayload.replication_type; - } try { const response = await createDatabase(createPayload); history.push(`/databases/${response.engine}/${response.id}`); @@ -157,6 +153,27 @@ const DatabaseCreate = () => { setSubmitting(false); }; + const initialValues: DatabaseCreateValues = { + allow_list: [ + { + address: '', + error: '', + }, + ], + cluster_size: -1 as ClusterSize, + engine: 'mysql' as Engine, + label: '', + region: '', + type: '', + }; + + if (!isDatabasesV2Enabled) { + // TODO (UIE-8214) remove POST GA + initialValues.replication_commit_type = undefined; // specific to Postgres + initialValues.replication_type = 'none' as ComprehensiveReplicationType; + initialValues.ssl_connection = true; + } + const { errors, handleSubmit, @@ -166,24 +183,7 @@ const DatabaseCreate = () => { setSubmitting, values, } = useFormik({ - initialValues: { - allow_list: [ - { - address: '', - error: '', - }, - ], - cluster_size: -1 as ClusterSize, - compression_type: undefined, // specific to MongoDB - engine: 'mysql' as Engine, - label: '', - region: '', - replication_commit_type: undefined, // specific to Postgres - replication_type: 'none' as ComprehensiveReplicationType, - ssl_connection: true, - storage_engine: undefined, // specific to MongoDB - type: '', - }, + initialValues, onSubmit: submitForm, validate: () => { handleIPValidation(); @@ -200,6 +200,7 @@ const DatabaseCreate = () => { values.cluster_size < 1 ? 3 : values.cluster_size ); if (!isDatabasesV2Enabled) { + // TODO (UIE-8214) remove POST GA setFieldValue( 'replication_type', determineReplicationType(values.cluster_size, values.engine) @@ -209,11 +210,6 @@ const DatabaseCreate = () => { determineReplicationCommitType(values.engine) ); } - setFieldValue('storage_engine', determineStorageEngine(values.engine)); - setFieldValue( - 'compression_type', - determineCompressionType(values.engine) - ); } }, [setFieldValue, values.cluster_size, values.engine, isDatabasesV2Enabled]); @@ -267,8 +263,10 @@ const DatabaseCreate = () => { const handleNodeChange = (size: ClusterSize | undefined) => { setFieldValue('cluster_size', size); - isDatabasesV2Enabled && + if (!isDatabasesV2Enabled) { + // TODO (UIE-8214) remove POST GA setFieldValue('replication_type', size === 1 ? 'none' : 'semi_synch'); + } }; return (
    diff --git a/packages/manager/src/features/Databases/utilities.ts b/packages/manager/src/features/Databases/utilities.ts index 9f01754f3fa..217ab155cda 100644 --- a/packages/manager/src/features/Databases/utilities.ts +++ b/packages/manager/src/features/Databases/utilities.ts @@ -219,10 +219,8 @@ export const toDatabaseFork = ( }; export const DATABASE_ENGINE_MAP: Record = { - mongodb: 'MongoDB', mysql: 'MySQL', postgresql: 'PostgreSQL', - redis: 'Redis', } as const; export const getDatabasesDescription = ( diff --git a/packages/manager/src/mocks/serverHandlers.ts b/packages/manager/src/mocks/serverHandlers.ts index 70e52dc8183..c55a67586e4 100644 --- a/packages/manager/src/mocks/serverHandlers.ts +++ b/packages/manager/src/mocks/serverHandlers.ts @@ -225,11 +225,8 @@ const databases = [ const dedicatedTypes = databaseTypeFactory.buildList(7, { class: 'dedicated', }); - const premiumTypes = databaseTypeFactory.buildList(7, { - class: 'premium', - }); return HttpResponse.json( - makeResourcePage([...standardTypes, ...dedicatedTypes, ...premiumTypes]) + makeResourcePage([...standardTypes, ...dedicatedTypes]) ); }), @@ -238,32 +235,32 @@ const databases = [ const engine2 = databaseEngineFactory.buildList(3, { engine: 'postgresql', }); - const engine3 = databaseEngineFactory.buildList(3, { - engine: 'mongodb', - }); - const combinedList = [...engine1, ...engine2, ...engine3]; + const combinedList = [...engine1, ...engine2]; return HttpResponse.json(makeResourcePage(combinedList)); }), http.get('*/databases/:engine/instances/:id', ({ params }) => { - const database = databaseFactory.build({ - compression_type: params.engine === 'mongodb' ? 'none' : undefined, + const isDefault = Number(params.id) % 2 !== 0; + const db: Record = { engine: params.engine as 'mysql', id: Number(params.id), label: `database-${params.id}`, - replication_commit_type: - params.engine === 'postgresql' ? 'local' : undefined, - replication_type: + platform: isDefault ? 'rdbms-default' : 'rdbms-legacy', + }; + if (!isDefault) { + db.replication_commit_type = + params.engine === 'postgresql' ? 'local' : undefined; + db.replication_type = params.engine === 'mysql' ? pickRandom(possibleMySQLReplicationTypes) : params.engine === 'postgresql' ? pickRandom(possiblePostgresReplicationTypes) - : (undefined as any), - ssl_connection: true, - storage_engine: params.engine === 'mongodb' ? 'wiredtiger' : undefined, - }); + : (undefined as any); + db.ssl_connection = true; + } + const database = databaseFactory.build(db); return HttpResponse.json(database); }), diff --git a/packages/validation/src/databases.schema.ts b/packages/validation/src/databases.schema.ts index 4830427c621..9cc4edd9b7f 100644 --- a/packages/validation/src/databases.schema.ts +++ b/packages/validation/src/databases.schema.ts @@ -14,37 +14,8 @@ export const createDatabaseSchema = object({ cluster_size: number() .oneOf([1, 2, 3], 'Nodes are required') .required('Nodes are required'), - replication_type: string().when('engine', { - is: (engine: string) => Boolean(engine.match(/mysql|postgres/g)), - then: string() - .when('engine', { - is: (engine: string) => Boolean(engine.match(/mysql/)), - then: string().oneOf(['none', 'semi_synch', 'asynch']), - }) - .when('engine', { - is: (engine: string) => Boolean(engine.match(/postgres/)), - then: string().oneOf(['none', 'synch', 'asynch']), - }) - .optional(), - otherwise: string().notRequired().nullable(true), - }), - replication_commit_type: string().when('engine', { - is: (engine: string) => Boolean(engine.match(/postgres/)), - then: string() - .oneOf(['off', 'on', 'local', 'remote_write', 'remote_apply']) - .optional(), - otherwise: string().notRequired().nullable(true), - }), - storage_engine: string().when('engine', { - is: (engine: string) => Boolean(engine.match(/mongodb/)), - then: string().oneOf(['wiredtiger', 'mmapv1']).notRequired(), - otherwise: string().notRequired().nullable(true), - }), - compression_type: string().when('engine', { - is: (engine: string) => Boolean(engine.match(/mongodb/)), - then: string().oneOf(['none', 'snappy', 'zlib']).notRequired(), - otherwise: string().notRequired().nullable(true), - }), + replication_type: string().notRequired().nullable(true), // TODO (UIE-8214) remove POST GA + replication_commit_type: string().notRequired().nullable(true), // TODO (UIE-8214) remove POST GA }); export const updateDatabaseSchema = object({ From 661d3a3eb287bc1b4caf0dbd28441abfc9b49cbc Mon Sep 17 00:00:00 2001 From: Jaalah Ramos Date: Thu, 7 Nov 2024 07:53:43 -0500 Subject: [PATCH 54/66] Initial Changelog --- .../pr-11152-added-1729713487291.md | 5 -- .../pr-11157-tech-stories-1729787265807.md | 5 -- ...r-11196-upcoming-features-1730465602676.md | 5 -- .../pr-11218-removed-1730922731323.md | 5 -- packages/api-v4/CHANGELOG.md | 19 +++++ packages/api-v4/package.json | 2 +- .../pr-11058-tech-stories-1729621320539.md | 5 -- .../pr-11080-changed-1730138781683.md | 5 -- .../pr-11099-fixed-1728996803052.md | 5 -- .../pr-11111-changed-1729082750525.md | 5 -- .../pr-11132-tests-1729628045654.md | 5 -- .../pr-11134-tech-stories-1729561221251.md | 5 -- .../pr-11137-fixed-1729745562099.md | 5 -- .../pr-11141-added-1730133012318.md | 5 -- .../pr-11141-tech-stories-1729629238072.md | 5 -- .../pr-11142-tech-stories-1729715589096.md | 5 -- .../pr-11143-added-1729792743385.md | 5 -- .../pr-11144-changed-1729684811107.md | 5 -- .../pr-11145-tech-stories-1729655521664.md | 5 -- .../pr-11147-changed-1729684531663.md | 5 -- .../pr-11149-fixed-1729690586892.md | 5 -- ...r-11150-upcoming-features-1729746495913.md | 5 -- .../pr-11152-added-1729713452489.md | 5 -- .../pr-11156-tech-stories-1729784052355.md | 5 -- .../pr-11157-tech-stories-1729787405590.md | 5 -- .../pr-11161-fixed-1729827835443.md | 5 -- ...r-11165-upcoming-features-1729872623442.md | 5 -- .../pr-11166-changed-1729892043474.md | 5 -- .../pr-11167-fixed-1730095848948.md | 5 -- ...r-11169-upcoming-features-1730713096724.md | 5 -- ...r-11170-upcoming-features-1730113218650.md | 5 -- ...r-11171-upcoming-features-1730182630692.md | 5 -- .../pr-11176-tests-1730144406169.md | 5 -- .../pr-11177-tech-stories-1730214757061.md | 5 -- .../pr-11177-tech-stories-1730214793474.md | 5 -- .../pr-11180-changed-1730803608188.md | 5 -- .../pr-11184-tests-1730217933343.md | 5 -- .../pr-11187-fixed-1730229958854.md | 5 -- .../pr-11188-added-1730298408749.md | 5 -- .../pr-11189-tests-1730310015248.md | 5 -- .../pr-11190-tests-1730312266172.md | 5 -- .../pr-11192-tech-stories-1730318704149.md | 5 -- .../pr-11193-added-1730738415614.md | 5 -- .../pr-11195-fixed-1730745532480.md | 5 -- .../pr-11195-tech-stories-1730487840259.md | 5 -- ...r-11196-upcoming-features-1730465663102.md | 5 -- ...r-11198-upcoming-features-1730465952580.md | 5 -- ...r-11199-upcoming-features-1730465428837.md | 5 -- .../pr-11200-tests-1730467459621.md | 5 -- .../pr-11202-changed-1730473350553.md | 5 -- .../pr-11206-changed-1730841709351.md | 5 -- .../pr-11209-added-1730788980490.md | 5 -- .../pr-11211-tech-stories-1730836470267.md | 5 -- .../pr-11218-fixed-1730922753813.md | 5 -- packages/manager/CHANGELOG.md | 70 +++++++++++++++++++ packages/manager/package.json | 2 +- .../pr-11125-added-1729871688954.md | 5 -- ...r-11138-upcoming-features-1730101798188.md | 5 -- .../pr-11157-tech-stories-1729787461166.md | 5 -- .../pr-11158-added-1729864026236.md | 5 -- .../pr-11159-added-1729806253949.md | 5 -- .../pr-11163-added-1729870035646.md | 5 -- .../pr-11164-added-1729871599808.md | 5 -- .../pr-11183-added-1730215173323.md | 5 -- packages/ui/CHANGELOG.md | 20 ++++++ packages/ui/package.json | 2 +- .../pr-11157-tech-stories-1729787428923.md | 5 -- packages/validation/CHANGELOG.md | 7 ++ packages/validation/package.json | 2 +- 69 files changed, 120 insertions(+), 309 deletions(-) delete mode 100644 packages/api-v4/.changeset/pr-11152-added-1729713487291.md delete mode 100644 packages/api-v4/.changeset/pr-11157-tech-stories-1729787265807.md delete mode 100644 packages/api-v4/.changeset/pr-11196-upcoming-features-1730465602676.md delete mode 100644 packages/api-v4/.changeset/pr-11218-removed-1730922731323.md delete mode 100644 packages/manager/.changeset/pr-11058-tech-stories-1729621320539.md delete mode 100644 packages/manager/.changeset/pr-11080-changed-1730138781683.md delete mode 100644 packages/manager/.changeset/pr-11099-fixed-1728996803052.md delete mode 100644 packages/manager/.changeset/pr-11111-changed-1729082750525.md delete mode 100644 packages/manager/.changeset/pr-11132-tests-1729628045654.md delete mode 100644 packages/manager/.changeset/pr-11134-tech-stories-1729561221251.md delete mode 100644 packages/manager/.changeset/pr-11137-fixed-1729745562099.md delete mode 100644 packages/manager/.changeset/pr-11141-added-1730133012318.md delete mode 100644 packages/manager/.changeset/pr-11141-tech-stories-1729629238072.md delete mode 100644 packages/manager/.changeset/pr-11142-tech-stories-1729715589096.md delete mode 100644 packages/manager/.changeset/pr-11143-added-1729792743385.md delete mode 100644 packages/manager/.changeset/pr-11144-changed-1729684811107.md delete mode 100644 packages/manager/.changeset/pr-11145-tech-stories-1729655521664.md delete mode 100644 packages/manager/.changeset/pr-11147-changed-1729684531663.md delete mode 100644 packages/manager/.changeset/pr-11149-fixed-1729690586892.md delete mode 100644 packages/manager/.changeset/pr-11150-upcoming-features-1729746495913.md delete mode 100644 packages/manager/.changeset/pr-11152-added-1729713452489.md delete mode 100644 packages/manager/.changeset/pr-11156-tech-stories-1729784052355.md delete mode 100644 packages/manager/.changeset/pr-11157-tech-stories-1729787405590.md delete mode 100644 packages/manager/.changeset/pr-11161-fixed-1729827835443.md delete mode 100644 packages/manager/.changeset/pr-11165-upcoming-features-1729872623442.md delete mode 100644 packages/manager/.changeset/pr-11166-changed-1729892043474.md delete mode 100644 packages/manager/.changeset/pr-11167-fixed-1730095848948.md delete mode 100644 packages/manager/.changeset/pr-11169-upcoming-features-1730713096724.md delete mode 100644 packages/manager/.changeset/pr-11170-upcoming-features-1730113218650.md delete mode 100644 packages/manager/.changeset/pr-11171-upcoming-features-1730182630692.md delete mode 100644 packages/manager/.changeset/pr-11176-tests-1730144406169.md delete mode 100644 packages/manager/.changeset/pr-11177-tech-stories-1730214757061.md delete mode 100644 packages/manager/.changeset/pr-11177-tech-stories-1730214793474.md delete mode 100644 packages/manager/.changeset/pr-11180-changed-1730803608188.md delete mode 100644 packages/manager/.changeset/pr-11184-tests-1730217933343.md delete mode 100644 packages/manager/.changeset/pr-11187-fixed-1730229958854.md delete mode 100644 packages/manager/.changeset/pr-11188-added-1730298408749.md delete mode 100644 packages/manager/.changeset/pr-11189-tests-1730310015248.md delete mode 100644 packages/manager/.changeset/pr-11190-tests-1730312266172.md delete mode 100644 packages/manager/.changeset/pr-11192-tech-stories-1730318704149.md delete mode 100644 packages/manager/.changeset/pr-11193-added-1730738415614.md delete mode 100644 packages/manager/.changeset/pr-11195-fixed-1730745532480.md delete mode 100644 packages/manager/.changeset/pr-11195-tech-stories-1730487840259.md delete mode 100644 packages/manager/.changeset/pr-11196-upcoming-features-1730465663102.md delete mode 100644 packages/manager/.changeset/pr-11198-upcoming-features-1730465952580.md delete mode 100644 packages/manager/.changeset/pr-11199-upcoming-features-1730465428837.md delete mode 100644 packages/manager/.changeset/pr-11200-tests-1730467459621.md delete mode 100644 packages/manager/.changeset/pr-11202-changed-1730473350553.md delete mode 100644 packages/manager/.changeset/pr-11206-changed-1730841709351.md delete mode 100644 packages/manager/.changeset/pr-11209-added-1730788980490.md delete mode 100644 packages/manager/.changeset/pr-11211-tech-stories-1730836470267.md delete mode 100644 packages/manager/.changeset/pr-11218-fixed-1730922753813.md delete mode 100644 packages/ui/.changeset/pr-11125-added-1729871688954.md delete mode 100644 packages/ui/.changeset/pr-11138-upcoming-features-1730101798188.md delete mode 100644 packages/ui/.changeset/pr-11157-tech-stories-1729787461166.md delete mode 100644 packages/ui/.changeset/pr-11158-added-1729864026236.md delete mode 100644 packages/ui/.changeset/pr-11159-added-1729806253949.md delete mode 100644 packages/ui/.changeset/pr-11163-added-1729870035646.md delete mode 100644 packages/ui/.changeset/pr-11164-added-1729871599808.md delete mode 100644 packages/ui/.changeset/pr-11183-added-1730215173323.md delete mode 100644 packages/validation/.changeset/pr-11157-tech-stories-1729787428923.md diff --git a/packages/api-v4/.changeset/pr-11152-added-1729713487291.md b/packages/api-v4/.changeset/pr-11152-added-1729713487291.md deleted file mode 100644 index 2a2770ce30c..00000000000 --- a/packages/api-v4/.changeset/pr-11152-added-1729713487291.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/api-v4": Added ---- - -DBaaS Suspend and Resume backend calls ([#11152](https://github.com/linode/manager/pull/11152)) diff --git a/packages/api-v4/.changeset/pr-11157-tech-stories-1729787265807.md b/packages/api-v4/.changeset/pr-11157-tech-stories-1729787265807.md deleted file mode 100644 index ecac9463430..00000000000 --- a/packages/api-v4/.changeset/pr-11157-tech-stories-1729787265807.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/api-v4": Tech Stories ---- - -Remove `@types/node` dependency ([#11157](https://github.com/linode/manager/pull/11157)) diff --git a/packages/api-v4/.changeset/pr-11196-upcoming-features-1730465602676.md b/packages/api-v4/.changeset/pr-11196-upcoming-features-1730465602676.md deleted file mode 100644 index aef96620c9a..00000000000 --- a/packages/api-v4/.changeset/pr-11196-upcoming-features-1730465602676.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/api-v4": Upcoming Features ---- - -DBaaS modify update payload to include version, add patch API ([#11196](https://github.com/linode/manager/pull/11196)) diff --git a/packages/api-v4/.changeset/pr-11218-removed-1730922731323.md b/packages/api-v4/.changeset/pr-11218-removed-1730922731323.md deleted file mode 100644 index 0be878c933c..00000000000 --- a/packages/api-v4/.changeset/pr-11218-removed-1730922731323.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/api-v4": Removed ---- - -DBaaS deprecated types including MongoDB and Redis ([#11218](https://github.com/linode/manager/pull/11218)) diff --git a/packages/api-v4/CHANGELOG.md b/packages/api-v4/CHANGELOG.md index 6f2f8dc95bf..06268e31c8f 100644 --- a/packages/api-v4/CHANGELOG.md +++ b/packages/api-v4/CHANGELOG.md @@ -1,3 +1,22 @@ +## [2024-11-12] - v0.130.0 + + +### Added: + +- DBaaS: Suspend and Resume backend calls ([#11152](https://github.com/linode/manager/pull/11152)) + +### Removed: + +- DBaaS: Deprecated types including MongoDB and Redis ([#11218](https://github.com/linode/manager/pull/11218)) + +### Tech Stories: + +- Remove `@types/node` dependency ([#11157](https://github.com/linode/manager/pull/11157)) + +### Upcoming Features: + +- DBaaS: Modify update payload to include version, add patch API ([#11196](https://github.com/linode/manager/pull/11196)) + ## [2024-10-28] - v0.129.0 diff --git a/packages/api-v4/package.json b/packages/api-v4/package.json index aab5a0bbb41..2c151782dbc 100644 --- a/packages/api-v4/package.json +++ b/packages/api-v4/package.json @@ -1,6 +1,6 @@ { "name": "@linode/api-v4", - "version": "0.129.0", + "version": "0.130.0", "homepage": "https://github.com/linode/manager/tree/develop/packages/api-v4", "bugs": { "url": "https://github.com/linode/manager/issues" diff --git a/packages/manager/.changeset/pr-11058-tech-stories-1729621320539.md b/packages/manager/.changeset/pr-11058-tech-stories-1729621320539.md deleted file mode 100644 index de8b6082d8f..00000000000 --- a/packages/manager/.changeset/pr-11058-tech-stories-1729621320539.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tech Stories ---- - -Consolidate ImageSelect components ([#11058](https://github.com/linode/manager/pull/11058)) diff --git a/packages/manager/.changeset/pr-11080-changed-1730138781683.md b/packages/manager/.changeset/pr-11080-changed-1730138781683.md deleted file mode 100644 index b33939a9c8e..00000000000 --- a/packages/manager/.changeset/pr-11080-changed-1730138781683.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Changed ---- - -Incorporate Product Family Groups in Side Nav ([#11080](https://github.com/linode/manager/pull/11080)) diff --git a/packages/manager/.changeset/pr-11099-fixed-1728996803052.md b/packages/manager/.changeset/pr-11099-fixed-1728996803052.md deleted file mode 100644 index d5c87f66fb2..00000000000 --- a/packages/manager/.changeset/pr-11099-fixed-1728996803052.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Fixed ---- - -Align table headers in Account Maintenance page ([#11099](https://github.com/linode/manager/pull/11099)) diff --git a/packages/manager/.changeset/pr-11111-changed-1729082750525.md b/packages/manager/.changeset/pr-11111-changed-1729082750525.md deleted file mode 100644 index 2b0ecbe6829..00000000000 --- a/packages/manager/.changeset/pr-11111-changed-1729082750525.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Changed ---- - -Remove Double border on "Billing & Payment History" table with dark theme. ([#11111](https://github.com/linode/manager/pull/11111)) diff --git a/packages/manager/.changeset/pr-11132-tests-1729628045654.md b/packages/manager/.changeset/pr-11132-tests-1729628045654.md deleted file mode 100644 index 2005408f5ff..00000000000 --- a/packages/manager/.changeset/pr-11132-tests-1729628045654.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tests ---- - -Added cypress tests for creating LKE clusters with ACL ([#11132](https://github.com/linode/manager/pull/11132)) diff --git a/packages/manager/.changeset/pr-11134-tech-stories-1729561221251.md b/packages/manager/.changeset/pr-11134-tech-stories-1729561221251.md deleted file mode 100644 index 38ed1150fe9..00000000000 --- a/packages/manager/.changeset/pr-11134-tech-stories-1729561221251.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tech Stories ---- - -Refactor TextField component ([#11134](https://github.com/linode/manager/pull/11134)) diff --git a/packages/manager/.changeset/pr-11137-fixed-1729745562099.md b/packages/manager/.changeset/pr-11137-fixed-1729745562099.md deleted file mode 100644 index a502521a780..00000000000 --- a/packages/manager/.changeset/pr-11137-fixed-1729745562099.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Fixed ---- - -Database create page form being enabled for restricted users ([#11137](https://github.com/linode/manager/pull/11137)) diff --git a/packages/manager/.changeset/pr-11141-added-1730133012318.md b/packages/manager/.changeset/pr-11141-added-1730133012318.md deleted file mode 100644 index 9bb2a5137ae..00000000000 --- a/packages/manager/.changeset/pr-11141-added-1730133012318.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Added ---- - -Success toasts to profile display settings page (with other minor improvements) ([#11141](https://github.com/linode/manager/pull/11141)) diff --git a/packages/manager/.changeset/pr-11141-tech-stories-1729629238072.md b/packages/manager/.changeset/pr-11141-tech-stories-1729629238072.md deleted file mode 100644 index 28376d598a9..00000000000 --- a/packages/manager/.changeset/pr-11141-tech-stories-1729629238072.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tech Stories ---- - -Clean up Profile Display Settings page ([#11141](https://github.com/linode/manager/pull/11141)) diff --git a/packages/manager/.changeset/pr-11142-tech-stories-1729715589096.md b/packages/manager/.changeset/pr-11142-tech-stories-1729715589096.md deleted file mode 100644 index c0517590945..00000000000 --- a/packages/manager/.changeset/pr-11142-tech-stories-1729715589096.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tech Stories ---- - -`only-export-components` for Tanstack routes ([#11142](https://github.com/linode/manager/pull/11142)) diff --git a/packages/manager/.changeset/pr-11143-added-1729792743385.md b/packages/manager/.changeset/pr-11143-added-1729792743385.md deleted file mode 100644 index 1732ea131b2..00000000000 --- a/packages/manager/.changeset/pr-11143-added-1729792743385.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Added ---- - -Mask Sensitive Data preference to Profile Settings ([#11143](https://github.com/linode/manager/pull/11143)) diff --git a/packages/manager/.changeset/pr-11144-changed-1729684811107.md b/packages/manager/.changeset/pr-11144-changed-1729684811107.md deleted file mode 100644 index 2b69521036d..00000000000 --- a/packages/manager/.changeset/pr-11144-changed-1729684811107.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Changed ---- - -Slightly improve styles on support ticket flows ([#11144](https://github.com/linode/manager/pull/11144)) diff --git a/packages/manager/.changeset/pr-11145-tech-stories-1729655521664.md b/packages/manager/.changeset/pr-11145-tech-stories-1729655521664.md deleted file mode 100644 index 62836d869a4..00000000000 --- a/packages/manager/.changeset/pr-11145-tech-stories-1729655521664.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tech Stories ---- - -Add more customization to legends and charts ([#11145](https://github.com/linode/manager/pull/11145)) diff --git a/packages/manager/.changeset/pr-11147-changed-1729684531663.md b/packages/manager/.changeset/pr-11147-changed-1729684531663.md deleted file mode 100644 index cf506d52a3b..00000000000 --- a/packages/manager/.changeset/pr-11147-changed-1729684531663.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Changed ---- - -Improve validation error when a backup is not selected ([#11147](https://github.com/linode/manager/pull/11147)) diff --git a/packages/manager/.changeset/pr-11149-fixed-1729690586892.md b/packages/manager/.changeset/pr-11149-fixed-1729690586892.md deleted file mode 100644 index fd597d442cd..00000000000 --- a/packages/manager/.changeset/pr-11149-fixed-1729690586892.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Fixed ---- - -Faux bold in Safari with & tags ([#11149](https://github.com/linode/manager/pull/11149)) diff --git a/packages/manager/.changeset/pr-11150-upcoming-features-1729746495913.md b/packages/manager/.changeset/pr-11150-upcoming-features-1729746495913.md deleted file mode 100644 index bbe5b071ce9..00000000000 --- a/packages/manager/.changeset/pr-11150-upcoming-features-1729746495913.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Upcoming Features ---- - -Add default xfilter for DBasS aiven clusters fetch in resource selection component ([#11150](https://github.com/linode/manager/pull/11150)) diff --git a/packages/manager/.changeset/pr-11152-added-1729713452489.md b/packages/manager/.changeset/pr-11152-added-1729713452489.md deleted file mode 100644 index b16be182d16..00000000000 --- a/packages/manager/.changeset/pr-11152-added-1729713452489.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Added ---- - -DBaaS Suspend and Resume for Database Landing and Details ([#11152](https://github.com/linode/manager/pull/11152)) diff --git a/packages/manager/.changeset/pr-11156-tech-stories-1729784052355.md b/packages/manager/.changeset/pr-11156-tech-stories-1729784052355.md deleted file mode 100644 index 63b427c420e..00000000000 --- a/packages/manager/.changeset/pr-11156-tech-stories-1729784052355.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@linode/manager': Tech Stories ---- - -Remove the feature flag and tracking events used for A/B testing in the API CLI Tools modal, and update the DX Tools modal button copy to 'View Code Snippets ([#11156](https://github.com/linode/manager/pull/11156)) diff --git a/packages/manager/.changeset/pr-11157-tech-stories-1729787405590.md b/packages/manager/.changeset/pr-11157-tech-stories-1729787405590.md deleted file mode 100644 index 04db0ba79dd..00000000000 --- a/packages/manager/.changeset/pr-11157-tech-stories-1729787405590.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tech Stories ---- - -Update `@types/node` to `20.17.0` ([#11157](https://github.com/linode/manager/pull/11157)) diff --git a/packages/manager/.changeset/pr-11161-fixed-1729827835443.md b/packages/manager/.changeset/pr-11161-fixed-1729827835443.md deleted file mode 100644 index f3b0eacd8aa..00000000000 --- a/packages/manager/.changeset/pr-11161-fixed-1729827835443.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Fixed ---- - -Only run 'Coverage Comment' GHA on non-drafts ([#11161](https://github.com/linode/manager/pull/11161)) diff --git a/packages/manager/.changeset/pr-11165-upcoming-features-1729872623442.md b/packages/manager/.changeset/pr-11165-upcoming-features-1729872623442.md deleted file mode 100644 index 5732391876a..00000000000 --- a/packages/manager/.changeset/pr-11165-upcoming-features-1729872623442.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Upcoming Features ---- - -Replace one-off hardcoded black and white color values with colorTokens ([#11165](https://github.com/linode/manager/pull/11165)) diff --git a/packages/manager/.changeset/pr-11166-changed-1729892043474.md b/packages/manager/.changeset/pr-11166-changed-1729892043474.md deleted file mode 100644 index e177811b75a..00000000000 --- a/packages/manager/.changeset/pr-11166-changed-1729892043474.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Changed ---- - -Database settings text and labels ([#11166](https://github.com/linode/manager/pull/11166)) diff --git a/packages/manager/.changeset/pr-11167-fixed-1730095848948.md b/packages/manager/.changeset/pr-11167-fixed-1730095848948.md deleted file mode 100644 index 1aeb90eeca0..00000000000 --- a/packages/manager/.changeset/pr-11167-fixed-1730095848948.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Fixed ---- - -aria label of action menu button in IP address table row ([#11167](https://github.com/linode/manager/pull/11167)) diff --git a/packages/manager/.changeset/pr-11169-upcoming-features-1730713096724.md b/packages/manager/.changeset/pr-11169-upcoming-features-1730713096724.md deleted file mode 100644 index e35ed345e8f..00000000000 --- a/packages/manager/.changeset/pr-11169-upcoming-features-1730713096724.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Upcoming Features ---- - -Add global border radius token to theme and replace harcoded values where borderRadius = 0 ([#11169](https://github.com/linode/manager/pull/11169)) diff --git a/packages/manager/.changeset/pr-11170-upcoming-features-1730113218650.md b/packages/manager/.changeset/pr-11170-upcoming-features-1730113218650.md deleted file mode 100644 index 7dd7c9e1330..00000000000 --- a/packages/manager/.changeset/pr-11170-upcoming-features-1730113218650.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Upcoming Features ---- - -Handle API errors for global filters and dashboard components ([#11170](https://github.com/linode/manager/pull/11170)) diff --git a/packages/manager/.changeset/pr-11171-upcoming-features-1730182630692.md b/packages/manager/.changeset/pr-11171-upcoming-features-1730182630692.md deleted file mode 100644 index 69f6fb0cfd5..00000000000 --- a/packages/manager/.changeset/pr-11171-upcoming-features-1730182630692.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Upcoming Features ---- - -Add global `font` and `spacing` tokens to theme and refactor design tokens ([#11171](https://github.com/linode/manager/pull/11171)) diff --git a/packages/manager/.changeset/pr-11176-tests-1730144406169.md b/packages/manager/.changeset/pr-11176-tests-1730144406169.md deleted file mode 100644 index 0a1b937db45..00000000000 --- a/packages/manager/.changeset/pr-11176-tests-1730144406169.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tests ---- - -Add unit tests to declutter LKE ACL cypress tests, fix lke-create.spec.ts failures ([#11176](https://github.com/linode/manager/pull/11176)) diff --git a/packages/manager/.changeset/pr-11177-tech-stories-1730214757061.md b/packages/manager/.changeset/pr-11177-tech-stories-1730214757061.md deleted file mode 100644 index 1987e5ba9b7..00000000000 --- a/packages/manager/.changeset/pr-11177-tech-stories-1730214757061.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tech Stories ---- - -Add `cypress_containerized` Docker Compose service ([#11177](https://github.com/linode/manager/pull/11177)) diff --git a/packages/manager/.changeset/pr-11177-tech-stories-1730214793474.md b/packages/manager/.changeset/pr-11177-tech-stories-1730214793474.md deleted file mode 100644 index 0c79ba70e01..00000000000 --- a/packages/manager/.changeset/pr-11177-tech-stories-1730214793474.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tech Stories ---- - -Add `nodejs-cloud-manager` Dockerfile target ([#11177](https://github.com/linode/manager/pull/11177)) diff --git a/packages/manager/.changeset/pr-11180-changed-1730803608188.md b/packages/manager/.changeset/pr-11180-changed-1730803608188.md deleted file mode 100644 index 53ae4f23340..00000000000 --- a/packages/manager/.changeset/pr-11180-changed-1730803608188.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Changed ---- - -Refactored DatabaseResize to use shared components for node selection and summary section. ([#11180](https://github.com/linode/manager/pull/11180)) diff --git a/packages/manager/.changeset/pr-11184-tests-1730217933343.md b/packages/manager/.changeset/pr-11184-tests-1730217933343.md deleted file mode 100644 index 711c71f5d18..00000000000 --- a/packages/manager/.changeset/pr-11184-tests-1730217933343.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tests ---- - -Add vitest workspace configuration ([#11184](https://github.com/linode/manager/pull/11184)) diff --git a/packages/manager/.changeset/pr-11187-fixed-1730229958854.md b/packages/manager/.changeset/pr-11187-fixed-1730229958854.md deleted file mode 100644 index 3fec311e5ee..00000000000 --- a/packages/manager/.changeset/pr-11187-fixed-1730229958854.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Fixed ---- - -Numerous UI bugs on the Object Storage bucket and access key landing pages ([#11187](https://github.com/linode/manager/pull/11187)) diff --git a/packages/manager/.changeset/pr-11188-added-1730298408749.md b/packages/manager/.changeset/pr-11188-added-1730298408749.md deleted file mode 100644 index 94abffd9b01..00000000000 --- a/packages/manager/.changeset/pr-11188-added-1730298408749.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Added ---- - -Pre-select a VPC subnet's on the Linode Create page when the VPC only has one subnet ([#11188](https://github.com/linode/manager/pull/11188)) diff --git a/packages/manager/.changeset/pr-11189-tests-1730310015248.md b/packages/manager/.changeset/pr-11189-tests-1730310015248.md deleted file mode 100644 index 0fbe1f93553..00000000000 --- a/packages/manager/.changeset/pr-11189-tests-1730310015248.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tests ---- - -Delete test Linodes, LKE clusters, and Firewalls after Cypress runs ([#11189](https://github.com/linode/manager/pull/11189)) diff --git a/packages/manager/.changeset/pr-11190-tests-1730312266172.md b/packages/manager/.changeset/pr-11190-tests-1730312266172.md deleted file mode 100644 index 7ca92a9978c..00000000000 --- a/packages/manager/.changeset/pr-11190-tests-1730312266172.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tests ---- - -Allow DBaaS resize test to pass when DBaaS v2 is enabled ([#11190](https://github.com/linode/manager/pull/11190)) diff --git a/packages/manager/.changeset/pr-11192-tech-stories-1730318704149.md b/packages/manager/.changeset/pr-11192-tech-stories-1730318704149.md deleted file mode 100644 index 15a849acd4c..00000000000 --- a/packages/manager/.changeset/pr-11192-tech-stories-1730318704149.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tech Stories ---- - -Remove use of Redux for viewing StackScript details ([#11192](https://github.com/linode/manager/pull/11192)) diff --git a/packages/manager/.changeset/pr-11193-added-1730738415614.md b/packages/manager/.changeset/pr-11193-added-1730738415614.md deleted file mode 100644 index 031adeba93d..00000000000 --- a/packages/manager/.changeset/pr-11193-added-1730738415614.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Added ---- - -Summary Section for Database Create GA ([#11193](https://github.com/linode/manager/pull/11193)) diff --git a/packages/manager/.changeset/pr-11195-fixed-1730745532480.md b/packages/manager/.changeset/pr-11195-fixed-1730745532480.md deleted file mode 100644 index 9a7e1e0ad9f..00000000000 --- a/packages/manager/.changeset/pr-11195-fixed-1730745532480.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Fixed ---- - -Animation for VPC subnet drawers ([#11195](https://github.com/linode/manager/pull/11195)) diff --git a/packages/manager/.changeset/pr-11195-tech-stories-1730487840259.md b/packages/manager/.changeset/pr-11195-tech-stories-1730487840259.md deleted file mode 100644 index 6f78202aaf5..00000000000 --- a/packages/manager/.changeset/pr-11195-tech-stories-1730487840259.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tech Stories ---- - -Convert from `formik` to `react-hook-form` for `SubnetCreateDrawer` ([#11195](https://github.com/linode/manager/pull/11195)) diff --git a/packages/manager/.changeset/pr-11196-upcoming-features-1730465663102.md b/packages/manager/.changeset/pr-11196-upcoming-features-1730465663102.md deleted file mode 100644 index c6d0b96144c..00000000000 --- a/packages/manager/.changeset/pr-11196-upcoming-features-1730465663102.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Upcoming Features ---- - -DBaaS add query to patch API, modify factory to include pendingUpdates ([#11196](https://github.com/linode/manager/pull/11196)) diff --git a/packages/manager/.changeset/pr-11198-upcoming-features-1730465952580.md b/packages/manager/.changeset/pr-11198-upcoming-features-1730465952580.md deleted file mode 100644 index cd7c2c8e48f..00000000000 --- a/packages/manager/.changeset/pr-11198-upcoming-features-1730465952580.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Upcoming Features ---- - -DBaaS add new Maintenance component, Upgrade version dialog, Review udpates dialog ([#11198](https://github.com/linode/manager/pull/11198)) diff --git a/packages/manager/.changeset/pr-11199-upcoming-features-1730465428837.md b/packages/manager/.changeset/pr-11199-upcoming-features-1730465428837.md deleted file mode 100644 index d971b6407b5..00000000000 --- a/packages/manager/.changeset/pr-11199-upcoming-features-1730465428837.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Upcoming Features ---- - -DBaaS major minor updates integration ([#11199](https://github.com/linode/manager/pull/11199)) diff --git a/packages/manager/.changeset/pr-11200-tests-1730467459621.md b/packages/manager/.changeset/pr-11200-tests-1730467459621.md deleted file mode 100644 index 3aa894d7438..00000000000 --- a/packages/manager/.changeset/pr-11200-tests-1730467459621.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tests ---- - -Slight improvements to GitHub test result comment formatting ([#11200](https://github.com/linode/manager/pull/11200)) diff --git a/packages/manager/.changeset/pr-11202-changed-1730473350553.md b/packages/manager/.changeset/pr-11202-changed-1730473350553.md deleted file mode 100644 index 39c64d7c8fe..00000000000 --- a/packages/manager/.changeset/pr-11202-changed-1730473350553.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Changed ---- - -`.env.example` cypress warning ([#11202](https://github.com/linode/manager/pull/11202)) diff --git a/packages/manager/.changeset/pr-11206-changed-1730841709351.md b/packages/manager/.changeset/pr-11206-changed-1730841709351.md deleted file mode 100644 index de5dd6f21e4..00000000000 --- a/packages/manager/.changeset/pr-11206-changed-1730841709351.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Changed ---- - -Disable unsupported images for distributed regions ([#11206](https://github.com/linode/manager/pull/11206)) diff --git a/packages/manager/.changeset/pr-11209-added-1730788980490.md b/packages/manager/.changeset/pr-11209-added-1730788980490.md deleted file mode 100644 index 8973630d4ca..00000000000 --- a/packages/manager/.changeset/pr-11209-added-1730788980490.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Added ---- - -New GPUv2 egress transfer helpers ([#11209](https://github.com/linode/manager/pull/11209)) diff --git a/packages/manager/.changeset/pr-11211-tech-stories-1730836470267.md b/packages/manager/.changeset/pr-11211-tech-stories-1730836470267.md deleted file mode 100644 index 0a46b375401..00000000000 --- a/packages/manager/.changeset/pr-11211-tech-stories-1730836470267.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Tech Stories ---- - -Use unit tested function for Pendo url transformation ([#11211](https://github.com/linode/manager/pull/11211)) diff --git a/packages/manager/.changeset/pr-11218-fixed-1730922753813.md b/packages/manager/.changeset/pr-11218-fixed-1730922753813.md deleted file mode 100644 index a7dfe79d811..00000000000 --- a/packages/manager/.changeset/pr-11218-fixed-1730922753813.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Fixed ---- - -DBaaS enable creation of two node clusters ([#11218](https://github.com/linode/manager/pull/11218)) diff --git a/packages/manager/CHANGELOG.md b/packages/manager/CHANGELOG.md index a7f48e42491..d6cee89dd1d 100644 --- a/packages/manager/CHANGELOG.md +++ b/packages/manager/CHANGELOG.md @@ -4,6 +4,76 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [2024-11-12] - v1.132.0 + + +### Added: + +- Success toasts to profile display settings page (with other minor improvements) ([#11141](https://github.com/linode/manager/pull/11141)) +- Mask Sensitive Data preference to Profile Settings ([#11143](https://github.com/linode/manager/pull/11143)) +- DBaaS Suspend and Resume for Database Landing and Details ([#11152](https://github.com/linode/manager/pull/11152)) +- Pre-selection of a VPC’s subnet on the Linode Create page when the VPC only has one subnet ([#11188](https://github.com/linode/manager/pull/11188)) +- Summary Section for Database Create GA ([#11193](https://github.com/linode/manager/pull/11193)) +- New GPUv2 egress transfer helpers ([#11209](https://github.com/linode/manager/pull/11209)) + +### Changed: + +- Incorporate Product Family Groups in Side Nav ([#11080](https://github.com/linode/manager/pull/11080)) +- Remove Double border on "Billing & Payment History" table with dark theme. ([#11111](https://github.com/linode/manager/pull/11111)) +- Slightly improve styles on support ticket flows ([#11144](https://github.com/linode/manager/pull/11144)) +- Improve validation error when a backup is not selected ([#11147](https://github.com/linode/manager/pull/11147)) +- Database settings text and labels ([#11166](https://github.com/linode/manager/pull/11166)) +- Refactor DatabaseResize to use shared components for node selection and summary section ([#11180](https://github.com/linode/manager/pull/11180)) +- `.env.example` cypress warning ([#11202](https://github.com/linode/manager/pull/11202)) +- Disable unsupported images for distributed regions ([#11206](https://github.com/linode/manager/pull/11206)) + +### Fixed: + +- Misaligned table headers in Account Maintenance page ([#11099](https://github.com/linode/manager/pull/11099)) +- Database create page form being enabled for restricted users ([#11137](https://github.com/linode/manager/pull/11137)) +- Faux bold in Safari with `` & `` tags ([#11149](https://github.com/linode/manager/pull/11149)) +- `Coverage Comment` GHA running on drafts ([#11161](https://github.com/linode/manager/pull/11161)) +- Aria label of action menu button in IP address table row ([#11167](https://github.com/linode/manager/pull/11167)) +- UI bugs on the Object Storage bucket and access key landing pages ([#11187](https://github.com/linode/manager/pull/11187)) +- Animation for VPC subnet drawers ([#11195](https://github.com/linode/manager/pull/11195)) +- DBaaS enable creation of two node clusters ([#11218](https://github.com/linode/manager/pull/11218)) + +### Tech Stories: + +- Consolidate ImageSelect components ([#11058](https://github.com/linode/manager/pull/11058)) +- Refactor TextField component ([#11134](https://github.com/linode/manager/pull/11134)) +- Clean up Profile Display Settings page ([#11141](https://github.com/linode/manager/pull/11141)) +- `only-export-components` for Tanstack routes ([#11142](https://github.com/linode/manager/pull/11142)) +- Add more customization to legends and charts ([#11145](https://github.com/linode/manager/pull/11145)) +- Update `@types/node` to `20.17.0` ([#11157](https://github.com/linode/manager/pull/11157)) +- Add `cypress_containerized` Docker Compose service ([#11177](https://github.com/linode/manager/pull/11177)) +- Add `nodejs-cloud-manager` Dockerfile target ([#11177](https://github.com/linode/manager/pull/11177)) +- Remove use of Redux for viewing StackScript details ([#11192](https://github.com/linode/manager/pull/11192)) +- Convert from `formik` to `react-hook-form` for `SubnetCreateDrawer` ([#11195](https://github.com/linode/manager/pull/11195)) +- Use unit tested function for Pendo url transformation ([#11211](https://github.com/linode/manager/pull/11211)) +- Remove the feature flag and tracking events used for A/B testing in the API CLI Tools modal, and update the DX Tools modal button copy to 'View Code Snippets ([#11156](https://github.com/linode/manager/pull/11156)) + +### Tests: + +- Add cypress tests for creating LKE clusters with ACL ([#11132](https://github.com/linode/manager/pull/11132)) +- Add unit tests to declutter LKE ACL cypress tests and fix `lke-create.spec.ts` failures ([#11176](https://github.com/linode/manager/pull/11176)) +- Add vitest workspace configuration ([#11184](https://github.com/linode/manager/pull/11184)) +- Delete test Linodes, LKE clusters, and Firewalls after Cypress runs ([#11189](https://github.com/linode/manager/pull/11189)) +- Allow DBaaS resize test to pass when DBaaS v2 is enabled ([#11190](https://github.com/linode/manager/pull/11190)) +- Slight improvements to GitHub test result comment formatting ([#11200](https://github.com/linode/manager/pull/11200)) + +### Upcoming Features: + +- Add default x-filter for DBasS Aiven clusters fetch in resource selection component ([#11150](https://github.com/linode/manager/pull/11150)) +- Replace one-off hardcoded black and white color values with colorTokens ([#11165](https://github.com/linode/manager/pull/11165)) +- Add global border radius token to theme and replace hard coded values where `borderRadius = 0` ([#11169](https://github.com/linode/manager/pull/11169)) +- Handle API errors for global filters and dashboard components ([#11170](https://github.com/linode/manager/pull/11170)) +- Add global `font` and `spacing` tokens to theme and refactor design tokens ([#11171](https://github.com/linode/manager/pull/11171)) +- DBaaS: Add query to patch API, modify factory to include pendingUpdates ([#11196](https://github.com/linode/manager/pull/11196)) +- DBaaS: Add new Maintenance, Upgrade Version dialog, and Review Updates dialog components ([#11198](https://github.com/linode/manager/pull/11198)) +- DBaaS: major minor updates integration ([#11199](https://github.com/linode/manager/pull/11199)) + + ## [2024-11-05] - v1.131.2 diff --git a/packages/manager/package.json b/packages/manager/package.json index 8e840354de8..6d199b422df 100644 --- a/packages/manager/package.json +++ b/packages/manager/package.json @@ -2,7 +2,7 @@ "name": "linode-manager", "author": "Linode", "description": "The Linode Manager website", - "version": "1.131.2", + "version": "1.132.0", "private": true, "type": "module", "bugs": { diff --git a/packages/ui/.changeset/pr-11125-added-1729871688954.md b/packages/ui/.changeset/pr-11125-added-1729871688954.md deleted file mode 100644 index 98553459363..00000000000 --- a/packages/ui/.changeset/pr-11125-added-1729871688954.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/ui": Added ---- - -Tooltip component and story ([#11125](https://github.com/linode/manager/pull/11125)) diff --git a/packages/ui/.changeset/pr-11138-upcoming-features-1730101798188.md b/packages/ui/.changeset/pr-11138-upcoming-features-1730101798188.md deleted file mode 100644 index bb0bd1164c4..00000000000 --- a/packages/ui/.changeset/pr-11138-upcoming-features-1730101798188.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/ui": Upcoming Features ---- - -Add Alias tokens to theme ([#11138](https://github.com/linode/manager/pull/11138)) diff --git a/packages/ui/.changeset/pr-11157-tech-stories-1729787461166.md b/packages/ui/.changeset/pr-11157-tech-stories-1729787461166.md deleted file mode 100644 index 92c48e1e383..00000000000 --- a/packages/ui/.changeset/pr-11157-tech-stories-1729787461166.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/ui": Tech Stories ---- - -Remove `@types/node` dependency ([#11157](https://github.com/linode/manager/pull/11157)) diff --git a/packages/ui/.changeset/pr-11158-added-1729864026236.md b/packages/ui/.changeset/pr-11158-added-1729864026236.md deleted file mode 100644 index 0f0762718da..00000000000 --- a/packages/ui/.changeset/pr-11158-added-1729864026236.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/ui": Added ---- - -IconButton component ([#11158](https://github.com/linode/manager/pull/11158)) diff --git a/packages/ui/.changeset/pr-11159-added-1729806253949.md b/packages/ui/.changeset/pr-11159-added-1729806253949.md deleted file mode 100644 index f6fdef00aaf..00000000000 --- a/packages/ui/.changeset/pr-11159-added-1729806253949.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/ui": Added ---- - -Migrate `FormControl`, `FormHelperText`, `Input`, `InputAdornment`, and `InputLabel` from `manager` to `ui` package ([#11159](https://github.com/linode/manager/pull/11159)) diff --git a/packages/ui/.changeset/pr-11163-added-1729870035646.md b/packages/ui/.changeset/pr-11163-added-1729870035646.md deleted file mode 100644 index 715546f8923..00000000000 --- a/packages/ui/.changeset/pr-11163-added-1729870035646.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/ui": Added ---- - -`Box` component from `manager` to `ui` package, part 1 ([#11163](https://github.com/linode/manager/pull/11163)) diff --git a/packages/ui/.changeset/pr-11164-added-1729871599808.md b/packages/ui/.changeset/pr-11164-added-1729871599808.md deleted file mode 100644 index bf250e40f84..00000000000 --- a/packages/ui/.changeset/pr-11164-added-1729871599808.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/ui": Added ---- - -`Box` component from `manager` to `ui` package, part 2 ([#11164](https://github.com/linode/manager/pull/11164)) diff --git a/packages/ui/.changeset/pr-11183-added-1730215173323.md b/packages/ui/.changeset/pr-11183-added-1730215173323.md deleted file mode 100644 index c5a6464588a..00000000000 --- a/packages/ui/.changeset/pr-11183-added-1730215173323.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/ui": Added ---- - -Migrate `Paper` from `manager` to `ui` package ([#11183](https://github.com/linode/manager/pull/11183)) diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index 2cd676b2c6c..95daf390d8c 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -1,3 +1,23 @@ +## [2024-11-12] - v0.3.0 + +### Added: + +- Tooltip component and story ([#11125](https://github.com/linode/manager/pull/11125)) +- IconButton component ([#11158](https://github.com/linode/manager/pull/11158)) +- VisibilityIcon component and story ([#11143](https://github.com/linode/manager/pull/11143)) +- Migrate `FormControl`, `FormHelperText`, `Input`, `InputAdornment`, and `InputLabel` from `manager` to `ui` package ([#11159](https://github.com/linode/manager/pull/11159)) +- `Box` component from `manager` to `ui` package, part 1 ([#11163](https://github.com/linode/manager/pull/11163)) +- `Box` component from `manager` to `ui` package, part 2 ([#11164](https://github.com/linode/manager/pull/11164)) +- Migrate `Paper` from `manager` to `ui` package ([#11183](https://github.com/linode/manager/pull/11183)) + +### Tech Stories: + +- Remove `@types/node` dependency ([#11157](https://github.com/linode/manager/pull/11157)) + +### Upcoming Features: + +- Add Alias tokens to theme ([#11138](https://github.com/linode/manager/pull/11138)) + ## [2024-10-28] - v0.2.0 diff --git a/packages/ui/package.json b/packages/ui/package.json index 11fa556f13e..f4bac6c85eb 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -2,7 +2,7 @@ "name": "@linode/ui", "author": "Linode", "description": "Linode UI component library", - "version": "0.2.0", + "version": "0.3.0", "type": "module", "main": "src/index.ts", "module": "src/index.ts", diff --git a/packages/validation/.changeset/pr-11157-tech-stories-1729787428923.md b/packages/validation/.changeset/pr-11157-tech-stories-1729787428923.md deleted file mode 100644 index c00c591cd64..00000000000 --- a/packages/validation/.changeset/pr-11157-tech-stories-1729787428923.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/validation": Tech Stories ---- - -Remove `@types/node` dependency ([#11157](https://github.com/linode/manager/pull/11157)) diff --git a/packages/validation/CHANGELOG.md b/packages/validation/CHANGELOG.md index f5ca2e4dac9..743ff5202bc 100644 --- a/packages/validation/CHANGELOG.md +++ b/packages/validation/CHANGELOG.md @@ -1,3 +1,10 @@ +## [2024-11-12] - v0.56.0 + + +### Tech Stories: + +- Remove `@types/node` dependency ([#11157](https://github.com/linode/manager/pull/11157)) + ## [2024-10-28] - v0.55.0 diff --git a/packages/validation/package.json b/packages/validation/package.json index 4aaeb1f109f..37f62119cb8 100644 --- a/packages/validation/package.json +++ b/packages/validation/package.json @@ -1,6 +1,6 @@ { "name": "@linode/validation", - "version": "0.55.0", + "version": "0.56.0", "description": "Yup validation schemas for use with the Linode APIv4", "type": "module", "main": "lib/index.cjs", From ea8b44938def368097e8bfc46b175347dbf13aef Mon Sep 17 00:00:00 2001 From: venkatmano-akamai Date: Fri, 8 Nov 2024 00:32:38 +0530 Subject: [PATCH 55/66] upcoming: [DI-21811] - Post processing of missing timestamp data across dimensions in ACLP charts (#11225) * upcoming: [DI-18419] - chart post processing for missing timestamps * upcoming: [DI-21811] - Code corrections and refactoring * upcoming: [DI-21811] - Code corrections and refactoring * upcoming: [DI-21811] - Code corrections and refactoring * upcoming: [DI-21811] - Added changeset * upcoming: [DI-21811] - comment updates * upcoming: [DI-21811] - comment updates * upcoming: [DI-21811] - comment updates * upcoming: [DI-21811] - comment updates * upcoming: [DI-21811] - early returns for empty array --------- Co-authored-by: vmangalr --- ...r-11225-upcoming-features-1730983728586.md | 5 +++ .../CloudPulse/Utils/CloudPulseWidgetUtils.ts | 34 +++++++++++++++++++ .../CloudPulse/Widget/CloudPulseWidget.tsx | 14 ++++++-- 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 packages/manager/.changeset/pr-11225-upcoming-features-1730983728586.md diff --git a/packages/manager/.changeset/pr-11225-upcoming-features-1730983728586.md b/packages/manager/.changeset/pr-11225-upcoming-features-1730983728586.md new file mode 100644 index 00000000000..967dfe47f85 --- /dev/null +++ b/packages/manager/.changeset/pr-11225-upcoming-features-1730983728586.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Add post processing for missing timestamp data across dimensions in ACLP charts ([#11225](https://github.com/linode/manager/pull/11225)) diff --git a/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts b/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts index 7a4e2a5b671..00719db57d0 100644 --- a/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts +++ b/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts @@ -347,3 +347,37 @@ export const getAutocompleteWidgetStyles = (theme: Theme) => ({ width: '90px', }, }); + +/** + * This method handles the existing issue in chart JS, and it will deleted when the recharts migration is completed + * @param arraysToBeFilled The list of dimension data to be filled + * @returns The list of dimension data filled with null values for missing timestamps + */ +// TODO: CloudPulse - delete when recharts migration completed +export const fillMissingTimeStampsAcrossDimensions = (...arraysToBeFilled: [number, number | null][][]): [number, number | null][][] => { + + if (arraysToBeFilled.length === 0) return []; + + // Step 1: Collect all unique keys from all arrays + const allTimestamps = new Set(); + + // Collect timestamps from each array, array[0], contains the number timestamp + arraysToBeFilled.forEach(array => { + array.forEach(([timeStamp]) => allTimestamps.add(timeStamp)); + }); + + // Step 2: Sort the timestamps to maintain chronological order + const sortedTimestamps = Array.from(allTimestamps).sort((a, b) => a - b); + + // Step 3: Synchronize the arrays to have null values for all missing timestamps + return arraysToBeFilled.map(array => { + // Step 3.1: Convert the array into a map for fast lookup + const map = new Map(array.map(([key, value]) => [key, value])); + + // Step 3.2: Build the synchronized array by checking if a key exists + return sortedTimestamps.map(key => { + // If the current array has the key, use its value; otherwise, set it to null, so that the gap is properly visible + return [key, map.get(key) ?? null] as [number, number | null]; + }); + }); +} diff --git a/packages/manager/src/features/CloudPulse/Widget/CloudPulseWidget.tsx b/packages/manager/src/features/CloudPulse/Widget/CloudPulseWidget.tsx index 9573682af68..73401fe86c4 100644 --- a/packages/manager/src/features/CloudPulse/Widget/CloudPulseWidget.tsx +++ b/packages/manager/src/features/CloudPulse/Widget/CloudPulseWidget.tsx @@ -10,6 +10,7 @@ import { useProfile } from 'src/queries/profile/profile'; import { generateGraphData, getCloudPulseMetricRequest, + fillMissingTimeStampsAcrossDimensions, } from '../Utils/CloudPulseWidgetUtils'; import { AGGREGATE_FUNCTION, SIZE, TIME_GRANULARITY } from '../Utils/constants'; import { constructAdditionalRequestFilters } from '../Utils/FilterBuilder'; @@ -256,6 +257,15 @@ export const CloudPulseWidget = (props: CloudPulseWidgetProperties) => { }); data = generatedData.dimensions; + + // add missing timestamps across all the dimensions + const filledArrays = fillMissingTimeStampsAcrossDimensions(...data.map(data => data.data)); + + //update the chart data with updated arrays + filledArrays.forEach((arr, index) => { + data[index].data = arr; + }) + legendRows = generatedData.legendRowsData; today = generatedData.today; scaledWidgetUnit.current = generatedData.unit; // here state doesn't matter, as this is always the latest re-render @@ -323,8 +333,8 @@ export const CloudPulseWidget = (props: CloudPulseWidgetProperties) => { ? metricsApiCallError ?? 'Error while rendering graph' : undefined } - formatData={(data: number) => - convertValueToUnit(data, scaledWidgetUnit.current) + formatData={(data: number | null) => + data === null ? data : convertValueToUnit(data, scaledWidgetUnit.current) } legendRows={ legendRows && legendRows.length > 0 ? legendRows : undefined From 45e60bcc22815bc38f7c9c9f0bcf49fdb3ff6654 Mon Sep 17 00:00:00 2001 From: Jaalah Ramos Date: Thu, 7 Nov 2024 14:11:00 -0500 Subject: [PATCH 56/66] Update changelog --- .../.changeset/pr-11225-upcoming-features-1730983728586.md | 5 ----- packages/manager/CHANGELOG.md | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) delete mode 100644 packages/manager/.changeset/pr-11225-upcoming-features-1730983728586.md diff --git a/packages/manager/.changeset/pr-11225-upcoming-features-1730983728586.md b/packages/manager/.changeset/pr-11225-upcoming-features-1730983728586.md deleted file mode 100644 index 967dfe47f85..00000000000 --- a/packages/manager/.changeset/pr-11225-upcoming-features-1730983728586.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Upcoming Features ---- - -Add post processing for missing timestamp data across dimensions in ACLP charts ([#11225](https://github.com/linode/manager/pull/11225)) diff --git a/packages/manager/CHANGELOG.md b/packages/manager/CHANGELOG.md index d6cee89dd1d..c3f34b424d1 100644 --- a/packages/manager/CHANGELOG.md +++ b/packages/manager/CHANGELOG.md @@ -64,6 +64,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ### Upcoming Features: +- Add post processing for missing timestamp data across dimensions in ACLP charts ([#11225](https://github.com/linode/manager/pull/11225)) - Add default x-filter for DBasS Aiven clusters fetch in resource selection component ([#11150](https://github.com/linode/manager/pull/11150)) - Replace one-off hardcoded black and white color values with colorTokens ([#11165](https://github.com/linode/manager/pull/11165)) - Add global border radius token to theme and replace hard coded values where `borderRadius = 0` ([#11169](https://github.com/linode/manager/pull/11169)) From 57eb19fd3f586dcb60ca7c36c065f692df27eb9e Mon Sep 17 00:00:00 2001 From: ankitaakamai Date: Fri, 8 Nov 2024 20:55:23 +0530 Subject: [PATCH 57/66] upcoming: [DI-21814] - ACLP UI - DBaaS instances order by label (#11226) * upcoming: [DI-21814] - DBaaS instances order by label * upcoming: [DI-21814] - Added changeset * DI-21814: use map for better readability and optimisations --------- Co-authored-by: vmangalr --- .../pr-11226-upcoming-features-1730990421121.md | 5 +++++ .../shared/CloudPulseResourcesSelect.tsx | 17 +++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 packages/manager/.changeset/pr-11226-upcoming-features-1730990421121.md diff --git a/packages/manager/.changeset/pr-11226-upcoming-features-1730990421121.md b/packages/manager/.changeset/pr-11226-upcoming-features-1730990421121.md new file mode 100644 index 00000000000..b7aa2db73c9 --- /dev/null +++ b/packages/manager/.changeset/pr-11226-upcoming-features-1730990421121.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +ACLP UI - DBaaS instances order by label ([#11226](https://github.com/linode/manager/pull/11226)) diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx index 3c6bd803237..ee2ac13dc0d 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx @@ -43,8 +43,9 @@ export const CloudPulseResourcesSelect = React.memo( xFilter, } = props; - const platformFilter = - resourceType === 'dbaas' ? { platform: 'rdbms-default' } : {}; + const resourceFilterMap: Record = { + dbaas: { '+order': 'asc', '+order_by': 'label', platform: 'rdbms-default' }, + }; const { data: resources, isLoading, isError } = useResourcesQuery( disabled !== undefined ? !disabled : Boolean(region && resourceType), @@ -52,13 +53,13 @@ export const CloudPulseResourcesSelect = React.memo( {}, xFilter ? { - ...platformFilter, - ...xFilter, - } + ...(resourceFilterMap[resourceType ?? ''] ?? {}), + ...xFilter, // the usual xFilters + } : { - ...platformFilter, - region, - } + ...(resourceFilterMap[resourceType ?? ''] ?? {}), + region, + } ); const [selectedResources, setSelectedResources] = React.useState< From e4592529566fc0fe61e8d0f85adbe311ce098fe5 Mon Sep 17 00:00:00 2001 From: rodonnel-akamai Date: Fri, 8 Nov 2024 12:13:18 -0500 Subject: [PATCH 58/66] UIE-8254: Add tooltip for ipv6 for new db clusters (#11231) --- .../DatabaseSummaryConnectionDetails.tsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryConnectionDetails.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryConnectionDetails.tsx index 48b122b70ef..9ccfea88542 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryConnectionDetails.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSummary/DatabaseSummaryConnectionDetails.tsx @@ -63,6 +63,16 @@ export const DatabaseSummaryConnectionDetails = (props: Props) => { const password = showCredentials && credentials ? credentials?.password : '••••••••••'; + const hostTooltipComponentProps = { + tooltip: { + style: { + minWidth: 285, + }, + }, + }; + const HOST_TOOLTIP_COPY = + 'Use the IPv6 address (AAAA record) for this hostname to avoid network transfer charges when connecting to this database from Linodes within the same region.'; + const handleShowPasswordClick = () => { setShowPassword((showCredentials) => !showCredentials); }; @@ -226,6 +236,14 @@ export const DatabaseSummaryConnectionDetails = (props: Props) => { className={classes.inlineCopyToolTip} text={database.hosts?.primary} /> + {!isLegacy && ( + + )} ) : ( From 9d9bc073a32b1f055e755c62f747e1a7fdfae46a Mon Sep 17 00:00:00 2001 From: mpolotsk-akamai <157619599+mpolotsk-akamai@users.noreply.github.com> Date: Fri, 8 Nov 2024 18:55:31 +0100 Subject: [PATCH 59/66] feat: [UIE-8193] - Usable Storage Tooltip for Create/Resize Database table (#11232) * feat: [UIE-8193] - Tooltip for Create/Resize Database table * feat: [UIE-8193] - Tooltip context for small screens --- .../src/components/SelectionCard/CardBase.tsx | 18 +++++- .../manager/src/components/TooltipIcon.tsx | 2 +- .../components/PlansPanel/PlanContainer.tsx | 19 +++++- .../PlansPanel/PlanSelectionTable.tsx | 58 ++++++++++++++----- 4 files changed, 81 insertions(+), 16 deletions(-) diff --git a/packages/manager/src/components/SelectionCard/CardBase.tsx b/packages/manager/src/components/SelectionCard/CardBase.tsx index 09bb450ec65..d30d3943694 100644 --- a/packages/manager/src/components/SelectionCard/CardBase.tsx +++ b/packages/manager/src/components/SelectionCard/CardBase.tsx @@ -1,5 +1,7 @@ import * as React from 'react'; +import { useFlags } from 'src/hooks/useFlags'; + import { CardBaseGrid, CardBaseHeading, @@ -36,6 +38,18 @@ export const CardBase = (props: CardBaseProps) => { sxSubheading, } = props; + const flags = useFlags(); + + const isDatabaseCreateFlow = location.pathname.includes('/databases/create'); + const isDatabaseResizeFlow = + location.pathname.match(/\/databases\/.*\/(\d+\/resize)/)?.[0] === + location.pathname; + + const isDatabaseGA = + !flags.dbaasV2?.beta && + flags.dbaasV2?.enabled && + (isDatabaseCreateFlow || isDatabaseResizeFlow); + const renderSubheadings = subheadings.map((subheading, idx) => { const subHeadingIsString = typeof subheading === 'string'; @@ -46,7 +60,9 @@ export const CardBase = (props: CardBaseProps) => { key={idx} sx={sxSubheading} > - {subheading} + {subHeadingIsString && isDatabaseGA + ? subheading?.replace('Storage', 'Usable Storage') + : subheading} ); }); diff --git a/packages/manager/src/components/TooltipIcon.tsx b/packages/manager/src/components/TooltipIcon.tsx index b5ad2776429..24f504d5bb5 100644 --- a/packages/manager/src/components/TooltipIcon.tsx +++ b/packages/manager/src/components/TooltipIcon.tsx @@ -11,7 +11,7 @@ import * as React from 'react'; import type { TooltipProps } from '@linode/ui'; import type { SxProps, Theme } from '@mui/material/styles'; -type TooltipIconStatus = +export type TooltipIconStatus = | 'error' | 'help' | 'info' diff --git a/packages/manager/src/features/components/PlansPanel/PlanContainer.tsx b/packages/manager/src/features/components/PlansPanel/PlanContainer.tsx index b1201e0a3d0..afbe31915f6 100644 --- a/packages/manager/src/features/components/PlansPanel/PlanContainer.tsx +++ b/packages/manager/src/features/components/PlansPanel/PlanContainer.tsx @@ -14,7 +14,7 @@ import { PlanSelectionTable } from './PlanSelectionTable'; import type { PlanWithAvailability } from './types'; import type { Region } from '@linode/api-v4'; import type { LinodeTypeClass } from '@linode/api-v4/lib/linodes'; - +import type { Theme } from '@mui/material/styles'; export interface PlanContainerProps { allDisabledPlans: PlanWithAvailability[]; currentPlanHeading?: string; @@ -65,6 +65,10 @@ export const PlanContainer = (props: PlanContainerProps) => { const shouldDisplayNoRegionSelectedMessage = !selectedRegionId && !isDatabaseCreateFlow && !isDatabaseResizeFlow; + const isDatabaseGA = + !flags.dbaasV2?.beta && + flags.dbaasV2?.enabled && + (isDatabaseCreateFlow || isDatabaseResizeFlow); interface PlanSelectionDividerTable { header?: string; planFilter?: (plan: PlanWithAvailability) => boolean; @@ -142,6 +146,18 @@ export const PlanContainer = (props: PlanContainerProps) => { return ( + {isCreate && isDatabaseGA && ( + ({ + marginBottom: theme.spacing(2), + marginLeft: theme.spacing(1), + marginTop: theme.spacing(1), + })} + > + Usable storage is smaller than the actual plan storage due to the + overhead from the database platform. + + )} {shouldDisplayNoRegionSelectedMessage ? ( { renderPlanSelection={renderPlanSelection} showNetwork={showNetwork} showTransfer={showTransfer} + showUsableStorage={isDatabaseCreateFlow || isDatabaseResizeFlow} /> ) )} diff --git a/packages/manager/src/features/components/PlansPanel/PlanSelectionTable.tsx b/packages/manager/src/features/components/PlansPanel/PlanSelectionTable.tsx index 4cd06f4a678..e76d0cb9e65 100644 --- a/packages/manager/src/features/components/PlansPanel/PlanSelectionTable.tsx +++ b/packages/manager/src/features/components/PlansPanel/PlanSelectionTable.tsx @@ -11,6 +11,7 @@ import { PLAN_SELECTION_NO_REGION_SELECTED_MESSAGE } from 'src/utilities/pricing import { StyledTable, StyledTableCell } from './PlanContainer.styles'; import type { PlanWithAvailability } from './types'; +import type { TooltipIconStatus } from 'src/components/TooltipIcon'; interface PlanSelectionFilterOptionsTable { header?: string; @@ -27,6 +28,7 @@ interface PlanSelectionTableProps { shouldDisplayNoRegionSelectedMessage: boolean; showNetwork?: boolean; showTransfer?: boolean; + showUsableStorage?: boolean; } const tableCells = [ @@ -54,6 +56,7 @@ export const PlanSelectionTable = (props: PlanSelectionTableProps) => { shouldDisplayNoRegionSelectedMessage, showNetwork: shouldShowNetwork, showTransfer: shouldShowTransfer, + showUsableStorage, } = props; const flags = useFlags(); @@ -70,6 +73,29 @@ export const PlanSelectionTable = (props: PlanSelectionTableProps) => { [plans, filterOptions, flags.gpuv2] ); + const showUsableStorageTooltip = (cellName: string) => + cellName === 'Usable Storage'; + + const showTooltip = ( + status: TooltipIconStatus, + text: JSX.Element | string, + width?: number + ) => { + return ( + + ); + }; return ( { ) { return null; } + if ( + showUsableStorage && + !flags.dbaasV2?.beta && + flags.dbaasV2?.enabled && + cellName === 'Storage' + ) { + cellName = 'Usable Storage'; + } return ( { {isPlanCell && filterOptions?.header ? filterOptions?.header : cellName} - {showTransferTooltip(cellName) && ( - - )} + {showTransferTooltip(cellName) && + showTooltip( + 'help', + 'Some plans do not include bundled network transfer. If the transfer allotment is 0, all outbound network transfer is subject to standard charges.' + )} + {showUsableStorageTooltip(cellName) && + showTooltip( + 'help', + 'Usable storage is smaller than the actual plan storage due to the overhead from the database platform.', + 240 + )} ); })} From 7b2b6af33fffcf380e66d11401f63e860e45453a Mon Sep 17 00:00:00 2001 From: Jaalah Ramos Date: Fri, 8 Nov 2024 12:59:33 -0500 Subject: [PATCH 60/66] DBaaS additions --- .../.changeset/pr-11226-upcoming-features-1730990421121.md | 5 ----- packages/manager/CHANGELOG.md | 2 ++ 2 files changed, 2 insertions(+), 5 deletions(-) delete mode 100644 packages/manager/.changeset/pr-11226-upcoming-features-1730990421121.md diff --git a/packages/manager/.changeset/pr-11226-upcoming-features-1730990421121.md b/packages/manager/.changeset/pr-11226-upcoming-features-1730990421121.md deleted file mode 100644 index b7aa2db73c9..00000000000 --- a/packages/manager/.changeset/pr-11226-upcoming-features-1730990421121.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@linode/manager": Upcoming Features ---- - -ACLP UI - DBaaS instances order by label ([#11226](https://github.com/linode/manager/pull/11226)) diff --git a/packages/manager/CHANGELOG.md b/packages/manager/CHANGELOG.md index c3f34b424d1..545b1235e53 100644 --- a/packages/manager/CHANGELOG.md +++ b/packages/manager/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ### Added: +- Tooltip for 'Usable Storage' in Create/Resize Database Table ([#11223](https://github.com/linode/manager/pull/11223)) - Success toasts to profile display settings page (with other minor improvements) ([#11141](https://github.com/linode/manager/pull/11141)) - Mask Sensitive Data preference to Profile Settings ([#11143](https://github.com/linode/manager/pull/11143)) - DBaaS Suspend and Resume for Database Landing and Details ([#11152](https://github.com/linode/manager/pull/11152)) @@ -64,6 +65,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ### Upcoming Features: +- ACLP UI - DBaaS instances order by label ([#11226](https://github.com/linode/manager/pull/11226)) - Add post processing for missing timestamp data across dimensions in ACLP charts ([#11225](https://github.com/linode/manager/pull/11225)) - Add default x-filter for DBasS Aiven clusters fetch in resource selection component ([#11150](https://github.com/linode/manager/pull/11150)) - Replace one-off hardcoded black and white color values with colorTokens ([#11165](https://github.com/linode/manager/pull/11165)) From 49e880237713347242d48cff890facd307b4aa05 Mon Sep 17 00:00:00 2001 From: Alban Bailly <130582365+abailly-akamai@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:47:42 -0500 Subject: [PATCH 61/66] GPU egress transfer copy update (#11235) --- .../src/features/components/PlansPanel/PlanInformation.tsx | 2 +- .../src/features/components/PlansPanel/PlanSelectionTable.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/manager/src/features/components/PlansPanel/PlanInformation.tsx b/packages/manager/src/features/components/PlansPanel/PlanInformation.tsx index 422fa0f9a19..84099c30d8c 100644 --- a/packages/manager/src/features/components/PlansPanel/PlanInformation.tsx +++ b/packages/manager/src/features/components/PlansPanel/PlanInformation.tsx @@ -77,7 +77,7 @@ export const PlanInformation = (props: PlanInformationProps) => { > Some plans do not include bundled network transfer. If the transfer allotment is 0, all outbound network transfer is - subject to standard charges. + subject to charges.
    Learn more about transfer costs diff --git a/packages/manager/src/features/components/PlansPanel/PlanSelectionTable.tsx b/packages/manager/src/features/components/PlansPanel/PlanSelectionTable.tsx index e76d0cb9e65..624a6605045 100644 --- a/packages/manager/src/features/components/PlansPanel/PlanSelectionTable.tsx +++ b/packages/manager/src/features/components/PlansPanel/PlanSelectionTable.tsx @@ -134,7 +134,7 @@ export const PlanSelectionTable = (props: PlanSelectionTableProps) => { {showTransferTooltip(cellName) && showTooltip( 'help', - 'Some plans do not include bundled network transfer. If the transfer allotment is 0, all outbound network transfer is subject to standard charges.' + 'Some plans do not include bundled network transfer. If the transfer allotment is 0, all outbound network transfer is subject to charges.' )} {showUsableStorageTooltip(cellName) && showTooltip( From 91b5c2316c664933fd81d638480b76601de3a11a Mon Sep 17 00:00:00 2001 From: Talmai Oliveira Date: Fri, 8 Nov 2024 16:53:25 -0500 Subject: [PATCH 62/66] default behavior when creating new child clusters should match what existed before we enabled IPACL (in other words: disabled by default) (#11234) Co-authored-by: Talmai Oliveira --- .../src/features/Kubernetes/CreateCluster/CreateCluster.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/manager/src/features/Kubernetes/CreateCluster/CreateCluster.tsx b/packages/manager/src/features/Kubernetes/CreateCluster/CreateCluster.tsx index f77e7250caa..8fccfcf7fd9 100644 --- a/packages/manager/src/features/Kubernetes/CreateCluster/CreateCluster.tsx +++ b/packages/manager/src/features/Kubernetes/CreateCluster/CreateCluster.tsx @@ -79,7 +79,7 @@ export const CreateCluster = () => { const formContainerRef = React.useRef(null); const { mutateAsync: updateAccountAgreements } = useMutateAccountAgreements(); const [highAvailability, setHighAvailability] = React.useState(); - const [controlPlaneACL, setControlPlaneACL] = React.useState(true); + const [controlPlaneACL, setControlPlaneACL] = React.useState(false); const [apl_enabled, setApl_enabled] = React.useState(false); const { data, error: regionsError } = useRegionsQuery(); From 584731168d328d2887592d8ec5eea5879cc570de Mon Sep 17 00:00:00 2001 From: Jaalah Ramos Date: Fri, 8 Nov 2024 17:24:25 -0500 Subject: [PATCH 63/66] Update changelog --- packages/manager/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/manager/CHANGELOG.md b/packages/manager/CHANGELOG.md index 545b1235e53..5c35e32d7e3 100644 --- a/packages/manager/CHANGELOG.md +++ b/packages/manager/CHANGELOG.md @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ### Changed: +- Optimize GPU egress data transfer copy ([#11235](https://github.com/linode/manager/pull/11235)) - Incorporate Product Family Groups in Side Nav ([#11080](https://github.com/linode/manager/pull/11080)) - Remove Double border on "Billing & Payment History" table with dark theme. ([#11111](https://github.com/linode/manager/pull/11111)) - Slightly improve styles on support ticket flows ([#11144](https://github.com/linode/manager/pull/11144)) @@ -30,6 +31,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ### Fixed: +- Preserve default child cluster creation behavior ([#11234](https://github.com/linode/manager/pull/11234)) - Misaligned table headers in Account Maintenance page ([#11099](https://github.com/linode/manager/pull/11099)) - Database create page form being enabled for restricted users ([#11137](https://github.com/linode/manager/pull/11137)) - Faux bold in Safari with `` & `` tags ([#11149](https://github.com/linode/manager/pull/11149)) From 04e36204a8203bb50759b9622473f7500fb48ec3 Mon Sep 17 00:00:00 2001 From: jdamore-linode <97627410+jdamore-linode@users.noreply.github.com> Date: Fri, 8 Nov 2024 19:57:04 -0500 Subject: [PATCH 64/66] Fix LKE create ACL tests (#11237) --- .../e2e/core/kubernetes/lke-create.spec.ts | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/packages/manager/cypress/e2e/core/kubernetes/lke-create.spec.ts b/packages/manager/cypress/e2e/core/kubernetes/lke-create.spec.ts index 2398df08e8c..c220c9e372b 100644 --- a/packages/manager/cypress/e2e/core/kubernetes/lke-create.spec.ts +++ b/packages/manager/cypress/e2e/core/kubernetes/lke-create.spec.ts @@ -503,13 +503,12 @@ describe('LKE Cluster Creation with ACL', () => { .should('be.visible') .click(); - // Disable ACL + // Confirm that ACL is disabled by default. cy.contains('Control Plane ACL').should('be.visible'); ui.toggle .find() - .should('have.attr', 'data-qa-toggle', 'true') - .should('be.visible') - .click(); + .should('have.attr', 'data-qa-toggle', 'false') + .should('be.visible'); // Add a node pool cy.log(`Adding ${nodeCount}x ${getLkePlanName(clusterPlan)} node(s)`); @@ -614,12 +613,16 @@ describe('LKE Cluster Creation with ACL', () => { .should('be.visible') .click(); - // Confirm ACL section + // Confirm ACL is disabled by default, then enable it. cy.contains('Control Plane ACL').should('be.visible'); ui.toggle .find() - .should('have.attr', 'data-qa-toggle', 'true') - .should('be.visible'); + .should('have.attr', 'data-qa-toggle', 'false') + .should('be.visible') + .click(); + + ui.toggle.find().should('have.attr', 'data-qa-toggle', 'true'); + // Add some IPv4s and an IPv6 cy.findByLabelText('IPv4 Addresses or CIDRs ip-address-0') .should('be.visible') @@ -733,11 +736,22 @@ describe('LKE Cluster Creation with ACL', () => { .should('be.visible') .click(); + // Enable ACL + cy.contains('Control Plane ACL').should('be.visible'); + ui.toggle + .find() + .should('have.attr', 'data-qa-toggle', 'false') + .should('be.visible') + .click(); + + ui.toggle.find().should('have.attr', 'data-qa-toggle', 'true'); + // Confirm ACL IPv4 validation works as expected cy.findByLabelText('IPv4 Addresses or CIDRs ip-address-0') .should('be.visible') .click() .type('invalid ip'); + // click out of textbox and confirm error is visible cy.contains('Control Plane ACL').should('be.visible').click(); cy.contains('Must be a valid IPv4 address.').should('be.visible'); From fe172fa97f367400aeaab9b3e03e985eabf7b498 Mon Sep 17 00:00:00 2001 From: Connie Liu <139280159+coliu-akamai@users.noreply.github.com> Date: Tue, 12 Nov 2024 12:40:15 -0500 Subject: [PATCH 65/66] fix: sx styling for Textfield component (#11246) * spread containerProps sx * spread props.sx as well whoops --- packages/manager/src/components/TextField.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/manager/src/components/TextField.tsx b/packages/manager/src/components/TextField.tsx index da82c2b3bbc..41119007223 100644 --- a/packages/manager/src/components/TextField.tsx +++ b/packages/manager/src/components/TextField.tsx @@ -266,6 +266,7 @@ export const TextField = (props: TextFieldProps) => { display: 'flex', flexWrap: 'wrap', }), + ...containerProps?.sx, }} > { ...(Boolean(tooltipText) && { width: '415px', }), + ...props.sx, }} className={className} error={!!error || !!errorText} From 185ece44011bd467183dc5c6222487e380ebe375 Mon Sep 17 00:00:00 2001 From: Banks Nussman <115251059+bnussman-akamai@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:53:27 -0500 Subject: [PATCH 66/66] fix: [M3-8894] - Linode Create crash when selected a Linode with a `type` that is `null` (#11247) * don't fetch when `type` is an empty string * fix and changelog entry --------- Co-authored-by: Banks Nussman --- packages/manager/CHANGELOG.md | 1 + packages/manager/src/features/Linodes/LinodeCreate/Region.tsx | 2 +- packages/manager/src/queries/types.ts | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/manager/CHANGELOG.md b/packages/manager/CHANGELOG.md index 5c35e32d7e3..90a1a2761a8 100644 --- a/packages/manager/CHANGELOG.md +++ b/packages/manager/CHANGELOG.md @@ -40,6 +40,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p - UI bugs on the Object Storage bucket and access key landing pages ([#11187](https://github.com/linode/manager/pull/11187)) - Animation for VPC subnet drawers ([#11195](https://github.com/linode/manager/pull/11195)) - DBaaS enable creation of two node clusters ([#11218](https://github.com/linode/manager/pull/11218)) +- Crash on the Linode Create flow when a Linode with a `type` of `null` is selected ([#11247](https://github.com/linode/manager/pull/11247)) ### Tech Stories: diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx index dcb9d03cdc3..cc0e5470a65 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx @@ -76,7 +76,7 @@ export const Region = React.memo(() => { const { data: type } = useTypeQuery( selectedLinode?.type ?? '', - Boolean(selectedLinode) + Boolean(selectedLinode?.type) ); const isLinodeCreateRestricted = useRestrictedGlobalGrantCheck({ diff --git a/packages/manager/src/queries/types.ts b/packages/manager/src/queries/types.ts index 8e736bfa1af..c40a91db098 100644 --- a/packages/manager/src/queries/types.ts +++ b/packages/manager/src/queries/types.ts @@ -25,7 +25,7 @@ export const useSpecificTypes = (types: string[], enabled = true) => { return useQueries({ queries: types.map>((type) => ({ - enabled, + enabled: enabled && Boolean(type), ...linodeQueries.types._ctx.type(type), ...queryPresets.oneTimeFetch, initialData() {