From 9854f8e54f125fb0c60a6782ae0ab69100ff7406 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Tue, 27 Oct 2026 10:23:55 +0100 Subject: [PATCH] :recycle: Move dashboard subscription styles off the deprecated styles (#12538) * :recycle: Move dashboard subscription styles off the deprecated styles The subscription stylesheet inlines the deprecated button-style and flex-center mixins, resolves its legacy color aliases and sizes to DS tokens, uses logical properties and the DS typography module without a namespace. Three rules that match nothing (the arrow svg, .cta-link and .open-arrow) are commented out with a TODO. The subscription controls need no DS change: the plan card, the current plan card and the manage subscription link have no DS equivalent, and they use no legacy icons. refactor/ stays only for the power up menu item placeholder and the shared dashboard header styles. A visual test covers the sidebar plan card (closed and open), the team settings plan and the members seats warnings. No visual change. AI-assisted-by: claude-opus-5-5 * :fire: Remove unused styles from the dashboard subscription The SCSS refactor of the dashboard subscription left rules commented out with a TODO because the module does not use them, or because they target an svg inside a DS icon that is the svg itself. CSS modules scope the classes to this module and no other source uses them, so they go: .icon-dropdown svg, .cta-link, .open-arrow. No visual change. AI-assisted-by: claude-opus-5-5 --- .../ui/visual-specs/visual-dashboard.spec.js | 101 +++++++++++++++++ .../app/main/ui/dashboard/subscription.scss | 104 ++++++++---------- 2 files changed, 149 insertions(+), 56 deletions(-) diff --git a/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js b/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js index 1b2ce0d483..ee79bdfc75 100644 --- a/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js +++ b/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js @@ -337,3 +337,104 @@ test.describe("Delete account modal", () => { await expect(modal).toHaveScreenshot(shot); }); }); + +test.describe("Subscription controls", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + // An unlimited trial that the logged-in user owns, as in + // specs/subscriptions-dashboard.spec.js. + const setupSubscription = async ( + page, + members, + secondTeam = true, + usage = "subscription/get-subscription-usage.json", + ) => { + await DashboardPage.mockConfigFlags(page, [ + "enable-subscriptions", + "disable-onboarding", + ]); + await DashboardPage.mockRPC( + page, + "get-profile", + "subscription/get-profile-unlimited-subscription.json", + ); + await DashboardPage.mockRPC(page, "get-subscription-usage", usage); + await DashboardPage.mockRPC( + page, + "get-team-info", + "subscription/get-team-info-subscriptions.json", + ); + const dashboardPage = new DashboardPage(page); + await dashboardPage.setupDashboardFull(); + await DashboardPage.mockRPC( + page, + "get-teams", + "subscription/get-teams-unlimited-subscription-owner.json", + ); + if (secondTeam) { + await DashboardPage.mockRPC( + page, + "get-projects?team-id=*", + "dashboard/get-projects-second-team.json", + ); + } + await DashboardPage.mockRPC(page, "get-team-members?team-id=*", members); + await DashboardPage.mockRPC( + page, + "get-team-stats?team-id=*", + "dashboard/get-team-stats.json", + ); + await dashboardPage.mockRPC( + "push-audit-events", + "workspace/audit-event-empty.json", + ); + return dashboardPage; + }; + + test("Sidebar plan card", async ({ page }) => { + const dashboardPage = await setupSubscription( + page, + "subscription/get-team-members-subscription-owner.json", + false, + "subscription/get-subscription-usage-one-editor.json", + ); + await dashboardPage.goToDashboard(); + + const name = page.getByTestId("subscription-name"); + await expect(name).toHaveText("Unlimited plan (trial)"); + const card = page.locator("[class*='cta-power-up']").first(); + await page.mouse.move(640, 300); + await expect(card).toHaveScreenshot(shot); + + await name.click(); + await page.mouse.move(640, 300); + await expect(card).toHaveScreenshot(shot); + }); + + test("Team settings plan", async ({ page }) => { + const dashboardPage = await setupSubscription( + page, + "subscription/get-team-members-subscription-owner.json", + ); + await dashboardPage.goToSecondTeamSettingsSection(); + + await expect( + page.getByRole("button", { name: "Manage your subscription" }), + ).toBeVisible(); + await expect( + page.locator("[class$='subscription__team']"), + ).toHaveScreenshot(shot); + }); + + test("Members seats warning", async ({ page }) => { + const dashboardPage = await setupSubscription( + page, + "subscription/get-team-members-subscription-eight-member.json", + ); + await dashboardPage.goToSecondTeamMembersSection(); + + await expect(page.getByTestId("cta")).toHaveCount(2); + await page.mouse.move(640, 700); + await expect(page).toHaveScreenshot(shot); + }); +}); diff --git a/frontend/src/app/main/ui/dashboard/subscription.scss b/frontend/src/app/main/ui/dashboard/subscription.scss index 43a6d9419b..6cfe4c2e9a 100644 --- a/frontend/src/app/main/ui/dashboard/subscription.scss +++ b/frontend/src/app/main/ui/dashboard/subscription.scss @@ -4,10 +4,16 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// refactor/ stays for the menu item placeholder (%menu-item-base) of the +// power up submenu. @use "refactor/common-refactor.scss" as deprecated; + +// The shared dashboard header styles move in their own change: they are +// used by ten dashboard modules. @use "common/refactor/common-dashboard"; -@use "ds/typography.scss" as t; +@use "ds/typography.scss" as *; @use "ds/_borders.scss" as *; +@use "ds/_utils.scss" as *; @use "ds/spacing.scss" as *; .cta-power-up { @@ -26,8 +32,9 @@ } .cta-top-section { - @include deprecated.button-style; - + border: none; + background: none; + cursor: pointer; display: grid; color: var(--color-foreground-secondary); grid-template-columns: 1fr auto; @@ -44,17 +51,11 @@ } .icon-dropdown { - @include deprecated.flex-center; - - height: 100%; - width: var(--sp-l); -} - -.icon-dropdown svg { - @extend %button-icon-small; - - stroke: var(--icon-foreground); - transform: rotate(90deg); + display: flex; + justify-content: center; + align-items: center; + block-size: 100%; + inline-size: var(--sp-l); } .cta-bottom-section { @@ -69,12 +70,14 @@ } .cta-bottom-section .content { - @include t.use-typography("body-medium"); - @include deprecated.button-style; + @include use-typography("body-medium"); + border: none; + background: none; + cursor: pointer; color: var(--color-foreground-secondary); display: inline-block; - text-align: left; + text-align: start; } .cta-bottom-button { @@ -87,72 +90,67 @@ .cta-text, .cta-title { - text-align: left; + text-align: start; } .cta-title { - @include t.use-typography("body-small"); + @include use-typography("body-small"); margin-block-end: var(--sp-xs); } .highlighted .cta-title { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); margin-block-end: 0; } .cta-text { - @include t.use-typography("title-small"); + @include use-typography("title-small"); } .highlighted .cta-text { - @include t.use-typography("body-large"); + @include use-typography("body-large"); color: var(--color-foreground-primary); } .cta-bottom-section .content a { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); color: var(--color-accent-tertiary); margin-inline-start: var(--sp-xs); } -.cta-link { - @include deprecated.button-style; - - align-self: end; - margin-inline-start: var(--sp-xs); -} - .team { display: grid; grid-auto-rows: min-content; gap: var(--sp-s); - max-width: deprecated.$s-1000; - width: 100%; + max-inline-size: px2rem(1000); + inline-size: 100%; } .team-label { - @include t.use-typography("headline-small"); + @include use-typography("headline-small"); - color: var(--title-foreground-color); + color: var(--color-foreground-secondary); } .team-text { - @include t.use-typography("title-medium"); + @include use-typography("title-medium"); color: var(--color-foreground-primary); } .manage-subscription-link { - @include deprecated.button-style; - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); + border: none; + background: none; + cursor: pointer; color: var(--color-accent-tertiary); display: flex; - margin-block-start: -8px; + margin-block-start: calc(-1 * var(--sp-s)); padding: 0; } @@ -178,21 +176,15 @@ cursor: pointer; &:hover { - color: var(--menu-foreground-color-hover); - - .open-arrow { - svg { - stroke: var(--menu-foreground-color-hover); - } - } + color: var(--color-foreground-primary); } } .members-cta { - height: fit-content; + block-size: fit-content; margin-block-start: var(--sp-s); - margin-inline-start: deprecated.$s-52; - max-width: deprecated.$s-220; + margin-inline-start: px2rem(52); + max-inline-size: px2rem(220); .cta-title { line-height: 1.2; @@ -200,7 +192,7 @@ } .dashboard-cta { - height: fit-content; + block-size: fit-content; margin: var(--sp-l); .cta-title { @@ -209,7 +201,7 @@ } .cta-message { - @include t.use-typography("body-small"); + @include use-typography("body-small"); color: var(--color-foreground-secondary); line-height: 1; @@ -231,13 +223,13 @@ } .nitrate-title { - @include t.use-typography("body-large"); + @include use-typography("body-large"); color: var(--color-foreground-primary); } .nitrate-info { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); color: var(--color-foreground-secondary); margin-block: var(--sp-s) var(--sp-xxl); @@ -249,8 +241,8 @@ } .nitrate-bottom-button { - width: fit-content; - height: fit-content; + inline-size: fit-content; + block-size: fit-content; padding: var(--sp-s) var(--sp-m); } @@ -271,14 +263,14 @@ } .nitrate-current-plan-label { - @include t.use-typography("body-small"); + @include use-typography("body-small"); padding-block-end: var(--sp-xs); color: var(--color-foreground-secondary); } .nitrate-current-plan-text { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); color: var(--color-foreground-primary); }