diff --git a/packages/manager/.changeset/pr-13234-changed-1767172149210.md b/packages/manager/.changeset/pr-13234-changed-1767172149210.md new file mode 100644 index 00000000000..770e04ff88d --- /dev/null +++ b/packages/manager/.changeset/pr-13234-changed-1767172149210.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Changed +--- + +Update Generational Plans default sort to show newest (G8) -> oldest (G6) ([#13234](https://github.com/linode/manager/pull/13234)) 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 07bd5f6edd6..4623ef33e52 100644 --- a/packages/manager/cypress/e2e/core/kubernetes/lke-update.spec.ts +++ b/packages/manager/cypress/e2e/core/kubernetes/lke-update.spec.ts @@ -1405,6 +1405,13 @@ describe('LKE cluster updates', () => { .findByTitle(`Add a Node Pool: ${mockCluster.label}`) .should('be.visible') .within(() => { + // For the "Dedicated 4 GB", use filter to select G6 Dedicated instead of relying on pagination + ui.autocomplete.findByLabel('Dedicated Plans').click(); + + ui.autocompletePopper.find().within(() => { + cy.findByText('G6 Dedicated').should('be.visible').click(); + }); + cy.findByText('Dedicated 4 GB') .should('be.visible') .closest('tr') diff --git a/packages/manager/cypress/e2e/core/linodes/alerts-create.spec.ts b/packages/manager/cypress/e2e/core/linodes/alerts-create.spec.ts index dcb02fd5f66..43325319275 100644 --- a/packages/manager/cypress/e2e/core/linodes/alerts-create.spec.ts +++ b/packages/manager/cypress/e2e/core/linodes/alerts-create.spec.ts @@ -123,6 +123,12 @@ describe('Create flow when beta alerts enabled by region and feature flag', func cy.get('[data-qa-tp="Linode Plan"]') .should('be.visible') .within(() => { + // For the Dedicated 8 GB, Use filter to select G6 Dedicated instead of relying on pagination + ui.autocomplete.findByLabel('Dedicated Plans').click(); + ui.autocompletePopper.find().within(() => { + cy.findByText('G6 Dedicated').should('be.visible').click(); + }); + cy.get('[data-qa-plan-row="Dedicated 8 GB"]').click(); }); cy.get('[type="password"]').should('be.visible').scrollIntoView(); @@ -283,6 +289,12 @@ describe('Create flow when beta alerts enabled by region and feature flag', func cy.get('[data-qa-tp="Linode Plan"]') .should('be.visible') .within(() => { + // For the Dedicated 8 GB, Use filter to select G6 Dedicated instead of relying on pagination + ui.autocomplete.findByLabel('Dedicated Plans').click(); + ui.autocompletePopper.find().within(() => { + cy.findByText('G6 Dedicated').should('be.visible').click(); + }); + cy.get('[data-qa-plan-row="Dedicated 8 GB"]').click(); }); cy.get('[type="password"]').should('be.visible').scrollIntoView(); @@ -432,6 +444,12 @@ describe('Create flow when beta alerts enabled by region and feature flag', func cy.get('[data-qa-tp="Linode Plan"]') .should('be.visible') .within(() => { + // For the Dedicated 8 GB, Use filter to select G6 Dedicated instead of relying on pagination + ui.autocomplete.findByLabel('Dedicated Plans').click(); + ui.autocompletePopper.find().within(() => { + cy.findByText('G6 Dedicated').should('be.visible').click(); + }); + cy.get('[data-qa-plan-row="Dedicated 8 GB"]').click(); }); cy.get('[type="password"]').should('be.visible').scrollIntoView(); diff --git a/packages/manager/cypress/e2e/core/linodes/create-linode.spec.ts b/packages/manager/cypress/e2e/core/linodes/create-linode.spec.ts index ef7213c5fae..eb6eec87f92 100644 --- a/packages/manager/cypress/e2e/core/linodes/create-linode.spec.ts +++ b/packages/manager/cypress/e2e/core/linodes/create-linode.spec.ts @@ -93,6 +93,16 @@ describe('Create Linode', () => { linodeCreatePage.setLabel(linodeLabel); linodeCreatePage.selectImage('Debian 12'); linodeCreatePage.selectRegionById(linodeRegion.id); + + // For the "Dedicated 4 GB" plan under the "Dedicated CPU" plan type, use filter to select G6 Dedicated instead of relying on pagination + if (planConfig.planType === 'Dedicated CPU') { + ui.autocomplete.findByLabel('Dedicated Plans').click(); + + ui.autocompletePopper.find().within(() => { + cy.findByText('G6 Dedicated').should('be.visible').click(); + }); + } + linodeCreatePage.selectPlan( planConfig.planType, planConfig.planLabel diff --git a/packages/manager/cypress/e2e/core/oneClickApps/one-click-apps.spec.ts b/packages/manager/cypress/e2e/core/oneClickApps/one-click-apps.spec.ts index bd5f13f4f28..8c1e15ab9bd 100644 --- a/packages/manager/cypress/e2e/core/oneClickApps/one-click-apps.spec.ts +++ b/packages/manager/cypress/e2e/core/oneClickApps/one-click-apps.spec.ts @@ -221,6 +221,11 @@ describe('OneClick Apps (OCA)', () => { cy.focused().type(`${region.id}{enter}`); // Choose a Linode plan + // For the Dedicated 8 GB, Use filter to select G6 Dedicated instead of relying on pagination + ui.autocomplete.findByLabel('Dedicated Plans').click(); + ui.autocompletePopper.find().within(() => { + cy.findByText('G6 Dedicated').should('be.visible').click(); + }); cy.get('[data-qa-plan-row="Dedicated 8 GB"]') .closest('tr') .within(() => { diff --git a/packages/manager/cypress/e2e/core/stackscripts/create-stackscripts.spec.ts b/packages/manager/cypress/e2e/core/stackscripts/create-stackscripts.spec.ts index 01a7dbe891e..22048718330 100644 --- a/packages/manager/cypress/e2e/core/stackscripts/create-stackscripts.spec.ts +++ b/packages/manager/cypress/e2e/core/stackscripts/create-stackscripts.spec.ts @@ -114,6 +114,13 @@ const fillOutLinodeForm = (label: string, regionName: string) => { cy.focused().type(label); cy.findByText('Dedicated CPU').should('be.visible').click(); + + // Use filter to select G6 Dedicated instead of relying on pagination + ui.autocomplete.findByLabel('Dedicated Plans').click(); + ui.autocompletePopper.find().within(() => { + cy.findByText('G6 Dedicated').should('be.visible').click(); + }); + cy.get('[id="g6-dedicated-2"]').click(); cy.findByLabelText('Root Password').should('be.visible').type(password); }; diff --git a/packages/manager/cypress/e2e/core/stackscripts/smoke-community-stackscripts.spec.ts b/packages/manager/cypress/e2e/core/stackscripts/smoke-community-stackscripts.spec.ts index 26b17237855..7e137192517 100644 --- a/packages/manager/cypress/e2e/core/stackscripts/smoke-community-stackscripts.spec.ts +++ b/packages/manager/cypress/e2e/core/stackscripts/smoke-community-stackscripts.spec.ts @@ -379,6 +379,13 @@ describe('Community Stackscripts integration tests', () => { // An error message shows up when no region is selected cy.contains('Plan is required.').should('be.visible'); + + // For the Dedicated 8 GB, Use filter to select G6 Dedicated instead of relying on pagination + ui.autocomplete.findByLabel('Dedicated Plans').click(); + ui.autocompletePopper.find().within(() => { + cy.findByText('G6 Dedicated').should('be.visible').click(); + }); + cy.get('[data-qa-plan-row="Dedicated 8 GB"]') .closest('tr') .within(() => { diff --git a/packages/manager/src/features/components/PlansPanel/utils/planFilters.test.ts b/packages/manager/src/features/components/PlansPanel/utils/planFilters.test.ts index 1c4e99cf5c5..4196e192eb0 100644 --- a/packages/manager/src/features/components/PlansPanel/utils/planFilters.test.ts +++ b/packages/manager/src/features/components/PlansPanel/utils/planFilters.test.ts @@ -11,6 +11,7 @@ import { filterPlansByGpuType, filterPlansByType, getAvailableTypes, + getGenerationRank, supportsTypeFiltering, } from './planFilters'; @@ -240,4 +241,32 @@ describe('planFilters utilities', () => { expect(result).toEqual(gpuPlans); }); }); + + describe('getGenerationRank', () => { + it('returns higher rank for newer generations', () => { + expect(getGenerationRank('g8-dedicated-4-2')).toBeGreaterThan( + getGenerationRank('g7-dedicated-4-2') + ); + expect(getGenerationRank('g7-dedicated-4-2')).toBeGreaterThan( + getGenerationRank('g6-dedicated-2') + ); + }); + + it('handles multi-digit generations correctly', () => { + expect(getGenerationRank('g10-dedicated-2')).toBeGreaterThan( + getGenerationRank('g8-dedicated-4-2') + ); + expect(getGenerationRank('g11-dedicated-2')).toBeGreaterThan( + getGenerationRank('g10-dedicated-2') + ); + }); + + it('returns 0 for unknown generations', () => { + expect(getGenerationRank('legacy-plan')).toBe(0); + expect(getGenerationRank('gg-xyz')).toBe(0); + expect(getGenerationRank('g-pqr')).toBe(0); + expect(getGenerationRank('x123')).toBe(0); + expect(getGenerationRank('')).toBe(0); + }); + }); }); diff --git a/packages/manager/src/features/components/PlansPanel/utils/planFilters.ts b/packages/manager/src/features/components/PlansPanel/utils/planFilters.ts index 9dd66d55a8d..89b8ca003f9 100644 --- a/packages/manager/src/features/components/PlansPanel/utils/planFilters.ts +++ b/packages/manager/src/features/components/PlansPanel/utils/planFilters.ts @@ -68,6 +68,26 @@ export const filterPlansByGeneration = ( // Type Filtering // ============================================================================ +/** + * Returns the numeric generation of a plan based on its ID. + * Higher generation number = newer plan (shown first). + * + * Example: + * - "g8-dedicated-4-2" -> 8 + * - "g1-accelerated-netint-vpu" -> 1 + * - "legacy-plan" -> 0 + */ +export const getGenerationRank = (planId: string): number => { + const generation = planId.split('-')[0]; // eg., "g8" or "legacy" + + // Safe fallback: must start with "g" + if (!generation.startsWith('g')) return 0; + + const num = Number(generation.slice(1)); + + return Number.isFinite(num) ? num : 0; +}; + /** * Filter plans by type within a generation * @@ -88,9 +108,11 @@ export const filterPlansByType = ( generation: PlanFilterGeneration, type: PlanFilterType ): PlanWithAvailability[] => { - // "All" returns all plans unchanged + // "All" returns all plans, sorted from newest to oldest generations if (type === PLAN_FILTER_ALL) { - return plans; + return [...plans].sort( + (a, b) => getGenerationRank(b.id) - getGenerationRank(a.id) + ); } // G7, G6, and "All" generation only have "All" option (no sub-types)