From a4f6f26e83b54ba8ca9e5fccfacb2814ac58d902 Mon Sep 17 00:00:00 2001 From: Hana Xu Date: Mon, 25 Aug 2025 14:02:37 -0400 Subject: [PATCH 1/5] wip --- .../features/Linodes/LinodeCreate/Region.tsx | 21 ++++++++-------- .../LinodeCreate/TwoStepRegion.test.tsx | 25 +++++++++++++++++++ .../Linodes/LinodeCreate/TwoStepRegion.tsx | 14 +++++++++-- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx index 427812f51d7..9e877b51685 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx @@ -82,10 +82,9 @@ export const Region = React.memo(() => { const showTwoStepRegion = isGeckoLAEnabled && isDistributedRegionSupported(createType ?? 'OS'); - const onChange = async (region: RegionType) => { + const onChange = async (region?: RegionType) => { const values = getValues(); - - field.onChange(region.id); + field.onChange(region?.id); if (values.hasSignedEUAgreement) { // Reset the EU agreement checkbox if they checked it so they have to re-agree when they change regions @@ -114,14 +113,14 @@ export const Region = React.memo(() => { if ( values.metadata?.user_data && - !region.capabilities.includes('Metadata') + !region?.capabilities.includes('Metadata') ) { // Clear metadata only if the new region does not support it setValue('metadata.user_data', null); } // Handle maintenance policy based on region capabilities - if (region.capabilities.includes('Maintenance Policy')) { + if (region?.capabilities.includes('Maintenance Policy')) { // If the region supports maintenance policy, set it to the default value // or keep the current value if it's already set if (!values.maintenance_policy) { @@ -140,20 +139,20 @@ export const Region = React.memo(() => { // Because distributed regions do not support some features, // we must disable those features here. Keep in mind, we should // prevent the user from enabling these features in their respective components. - if (region.site_type === 'distributed') { + if (region?.site_type === 'distributed') { setValue('backups_enabled', false); setValue('private_ip', false); } if (isDiskEncryptionFeatureEnabled) { - if (region.site_type === 'distributed') { + if (region?.site_type === 'distributed') { // If a distributed region is selected, make sure we don't send disk_encryption in the payload. setValue('disk_encryption', undefined); } else { // Enable disk encryption by default if the region supports it const defaultDiskEncryptionValue = - region.capabilities.includes('Disk Encryption') || - region.capabilities.includes('LA Disk Encryption') + region?.capabilities.includes('Disk Encryption') || + region?.capabilities.includes('LA Disk Encryption') ? 'enabled' : undefined; @@ -161,12 +160,12 @@ export const Region = React.memo(() => { } } - if (!isLabelFieldDirty) { + if (!isLabelFieldDirty && region) { // Auto-generate the Linode label because the region is included in the generated label const label = await getGeneratedLinodeLabel({ queryClient, tab: createType ?? 'OS', - values: { ...values, region: region.id }, + values: { ...values, region: region?.id }, }); setValue('label', label); diff --git a/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.test.tsx b/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.test.tsx index 87dc7b09080..64859a118ad 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.test.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.test.tsx @@ -115,4 +115,29 @@ describe('TwoStepRegion', () => { const geographicalAreaSelect2 = screen.getByLabelText('Geographical Area'); expect(geographicalAreaSelect2).toHaveAttribute('value', 'North America'); }); + + it.only('should clear the selected region when switching between the Core and Distributed tabs', async () => { + renderWithThemeAndHookFormContext({ + component: , + }); + + // Select a core region + const regionSelect = screen.getByLabelText('Region'); + await userEvent.click(regionSelect); + + const coreRegionOption = screen.getByText(`US, Atlanta, GA (us-southeast)`); + await userEvent.click(coreRegionOption); + expect(regionSelect).toHaveAttribute('value', 'US, Atlanta, GA (us-southeast)'); + + // Switch tabs + const [coreTab, distributedTab] = screen.getAllByRole('tab'); + await userEvent.click(distributedTab); + await userEvent.click(coreTab); + + // Previous Region value should be cleared + // const select2 = screen.getByLabelText('Region'); + // expect(select2).toHaveAttribute('value', 'undefined'); + + expect(regionSelect).toHaveAttribute('value', 'US, Atlanta, GA (us-southeast)'); + }); }); diff --git a/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx b/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx index 1b7eaa011c7..d55085706bc 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx @@ -61,7 +61,7 @@ const GEOGRAPHICAL_AREA_OPTIONS: GeographicalAreaOption[] = [ ]; interface Props { - onChange: (region: RegionType) => void; + onChange: (region?: RegionType) => void; } type CombinedProps = Props & Omit, 'onChange'>; @@ -69,6 +69,8 @@ type CombinedProps = Props & Omit, 'onChange'>; export const TwoStepRegion = (props: CombinedProps) => { const { disabled, disabledRegions, errorText, onChange, value } = props; + const [tabIndex, setTabIndex] = React.useState(0); + const [regionFilter, setRegionFilter] = React.useState('distributed'); @@ -97,7 +99,15 @@ export const TwoStepRegion = (props: CombinedProps) => { } /> - + { + // if (index !== tabIndex) { + // setTabIndex(index); + // // Reset region selection when switching between site types + // // onChange(undefined); + // } + // }} + > Core Distributed From e7f1d3033dd23dc0c551e664eb1f6a38fa335994 Mon Sep 17 00:00:00 2001 From: Hana Xu Date: Mon, 25 Aug 2025 14:38:55 -0400 Subject: [PATCH 2/5] wip 2 --- .../Linodes/LinodeCreate/Region.test.tsx | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Region.test.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Region.test.tsx index 2b3e5b92d37..e4271ef05a5 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Region.test.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Region.test.tsx @@ -3,6 +3,7 @@ import { linodeTypeFactory, regionFactory, } from '@linode/utilities'; +import { screen } from '@testing-library/react'; import { userEvent } from '@testing-library/user-event'; import React from 'react'; @@ -248,4 +249,62 @@ describe('Region', () => { 'The selected image cannot be deployed to a distributed region.' ); }); + + it.only('test', async () => { + const distributedRegion = regionFactory.build({ + capabilities: ['Linodes', 'Distributed Plans'], + site_type: 'distributed', + }); + const coreRegion = regionFactory.build({ + capabilities: ['Linodes'], + site_type: 'core', + }); + + server.use( + http.get('*/v4*/regions', () => { + return HttpResponse.json( + makeResourcePage([coreRegion, distributedRegion]) + ); + }) + ); + + await renderWithThemeAndHookFormContext({ + component: , + options: { + flags: { + gecko2: { + enabled: true, + la: true, + ga: false, + }, + }, + }, + }); + + // Select a core region + const regionSelect = screen.getByLabelText('Region'); + await userEvent.click(regionSelect); + const coreRegionOption = screen.getByText( + `US, ${coreRegion.label} (${coreRegion.id})` + ); + await userEvent.click(coreRegionOption); + expect(regionSelect).toHaveAttribute( + 'value', + `US, ${coreRegion.label} (${coreRegion.id})` + ); + + // Switch tabs + const [coreTab, distributedTab] = screen.getAllByRole('tab'); + await userEvent.click(distributedTab); + await userEvent.click(coreTab); + + // Previous Region value should be cleared + // const select2 = screen.getByLabelText('Region'); + // expect(select2).toHaveAttribute('value', 'undefined'); + + expect(regionSelect).toHaveAttribute( + 'value', + `US, ${coreRegion.label} (${coreRegion.id})` + ); + }); }); From c46f3005752a0ad3d911bfd47d65628f057a8a79 Mon Sep 17 00:00:00 2001 From: Hana Xu Date: Tue, 26 Aug 2025 12:58:34 -0400 Subject: [PATCH 3/5] clean up --- .../Linodes/LinodeCreate/Region.test.tsx | 59 ------------------- .../LinodeCreate/TwoStepRegion.test.tsx | 25 -------- .../Linodes/LinodeCreate/TwoStepRegion.tsx | 14 ++--- 3 files changed, 7 insertions(+), 91 deletions(-) diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Region.test.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Region.test.tsx index e4271ef05a5..2b3e5b92d37 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Region.test.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Region.test.tsx @@ -3,7 +3,6 @@ import { linodeTypeFactory, regionFactory, } from '@linode/utilities'; -import { screen } from '@testing-library/react'; import { userEvent } from '@testing-library/user-event'; import React from 'react'; @@ -249,62 +248,4 @@ describe('Region', () => { 'The selected image cannot be deployed to a distributed region.' ); }); - - it.only('test', async () => { - const distributedRegion = regionFactory.build({ - capabilities: ['Linodes', 'Distributed Plans'], - site_type: 'distributed', - }); - const coreRegion = regionFactory.build({ - capabilities: ['Linodes'], - site_type: 'core', - }); - - server.use( - http.get('*/v4*/regions', () => { - return HttpResponse.json( - makeResourcePage([coreRegion, distributedRegion]) - ); - }) - ); - - await renderWithThemeAndHookFormContext({ - component: , - options: { - flags: { - gecko2: { - enabled: true, - la: true, - ga: false, - }, - }, - }, - }); - - // Select a core region - const regionSelect = screen.getByLabelText('Region'); - await userEvent.click(regionSelect); - const coreRegionOption = screen.getByText( - `US, ${coreRegion.label} (${coreRegion.id})` - ); - await userEvent.click(coreRegionOption); - expect(regionSelect).toHaveAttribute( - 'value', - `US, ${coreRegion.label} (${coreRegion.id})` - ); - - // Switch tabs - const [coreTab, distributedTab] = screen.getAllByRole('tab'); - await userEvent.click(distributedTab); - await userEvent.click(coreTab); - - // Previous Region value should be cleared - // const select2 = screen.getByLabelText('Region'); - // expect(select2).toHaveAttribute('value', 'undefined'); - - expect(regionSelect).toHaveAttribute( - 'value', - `US, ${coreRegion.label} (${coreRegion.id})` - ); - }); }); diff --git a/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.test.tsx b/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.test.tsx index 64859a118ad..87dc7b09080 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.test.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.test.tsx @@ -115,29 +115,4 @@ describe('TwoStepRegion', () => { const geographicalAreaSelect2 = screen.getByLabelText('Geographical Area'); expect(geographicalAreaSelect2).toHaveAttribute('value', 'North America'); }); - - it.only('should clear the selected region when switching between the Core and Distributed tabs', async () => { - renderWithThemeAndHookFormContext({ - component: , - }); - - // Select a core region - const regionSelect = screen.getByLabelText('Region'); - await userEvent.click(regionSelect); - - const coreRegionOption = screen.getByText(`US, Atlanta, GA (us-southeast)`); - await userEvent.click(coreRegionOption); - expect(regionSelect).toHaveAttribute('value', 'US, Atlanta, GA (us-southeast)'); - - // Switch tabs - const [coreTab, distributedTab] = screen.getAllByRole('tab'); - await userEvent.click(distributedTab); - await userEvent.click(coreTab); - - // Previous Region value should be cleared - // const select2 = screen.getByLabelText('Region'); - // expect(select2).toHaveAttribute('value', 'undefined'); - - expect(regionSelect).toHaveAttribute('value', 'US, Atlanta, GA (us-southeast)'); - }); }); diff --git a/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx b/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx index d55085706bc..3de75e84167 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx @@ -100,13 +100,13 @@ export const TwoStepRegion = (props: CombinedProps) => { /> { - // if (index !== tabIndex) { - // setTabIndex(index); - // // Reset region selection when switching between site types - // // onChange(undefined); - // } - // }} + onChange={(index: number) => { + if (index !== tabIndex) { + setTabIndex(index); + // M3-9469: Reset region selection when switching between site types + onChange(undefined); + } + }} > Core From e0bd117d45bf266012e87c1ebf154db425bf7a36 Mon Sep 17 00:00:00 2001 From: Hana Xu Date: Tue, 26 Aug 2025 12:59:42 -0400 Subject: [PATCH 4/5] Added changeset: Reset selected region when switching between Core and Distributed tabs --- packages/manager/.changeset/pr-12767-fixed-1756227582572.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/manager/.changeset/pr-12767-fixed-1756227582572.md diff --git a/packages/manager/.changeset/pr-12767-fixed-1756227582572.md b/packages/manager/.changeset/pr-12767-fixed-1756227582572.md new file mode 100644 index 00000000000..b2b49b81636 --- /dev/null +++ b/packages/manager/.changeset/pr-12767-fixed-1756227582572.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +Reset selected region when switching between Core and Distributed tabs ([#12767](https://github.com/linode/manager/pull/12767)) From 4c6abaa8148ceafa7e9b1cd9753f68f35a048e7b Mon Sep 17 00:00:00 2001 From: Hana Xu Date: Wed, 27 Aug 2025 15:31:54 -0400 Subject: [PATCH 5/5] feedback --- .../manager/src/features/Linodes/LinodeCreate/Region.tsx | 4 ++-- .../src/features/Linodes/LinodeCreate/TwoStepRegion.tsx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx b/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx index 9e877b51685..fe1ad519646 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/Region.tsx @@ -82,7 +82,7 @@ export const Region = React.memo(() => { const showTwoStepRegion = isGeckoLAEnabled && isDistributedRegionSupported(createType ?? 'OS'); - const onChange = async (region?: RegionType) => { + const onChange = async (region: null | RegionType) => { const values = getValues(); field.onChange(region?.id); @@ -165,7 +165,7 @@ export const Region = React.memo(() => { const label = await getGeneratedLinodeLabel({ queryClient, tab: createType ?? 'OS', - values: { ...values, region: region?.id }, + values: { ...values, region: region.id }, }); setValue('label', label); diff --git a/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx b/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx index 3de75e84167..1533b87d5a7 100644 --- a/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx +++ b/packages/manager/src/features/Linodes/LinodeCreate/TwoStepRegion.tsx @@ -61,7 +61,7 @@ const GEOGRAPHICAL_AREA_OPTIONS: GeographicalAreaOption[] = [ ]; interface Props { - onChange: (region?: RegionType) => void; + onChange: (region: null | RegionType) => void; } type CombinedProps = Props & Omit, 'onChange'>; @@ -100,11 +100,11 @@ export const TwoStepRegion = (props: CombinedProps) => { /> { + onChange={(index) => { if (index !== tabIndex) { setTabIndex(index); // M3-9469: Reset region selection when switching between site types - onChange(undefined); + onChange(null); } }} >