diff --git a/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js b/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js index 8d5f9fca74..2e3b7fe082 100644 --- a/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js +++ b/frontend/playwright/ui/visual-specs/visual-dashboard.spec.js @@ -559,6 +559,43 @@ test.describe("Team modals controls", () => { }); }); +test.describe("Settings subscription controls", () => { + const shot = { maxDiffPixelRatio: 0, threshold: 0 }; + + test("Plans page and management dialog", async ({ page }) => { + await DashboardPage.mockConfigFlags( + page, + ["enable-subscriptions", "disable-onboarding"], + { penpotIsSaas: true }, + ); + await DashboardPage.mockRPC( + page, + "get-subscription-usage", + "subscription/get-subscription-usage.json", + ); + await page.goto("/?screen=settings-subscription"); + await expect( + page.getByRole("heading", { name: "Subscription", level: 2 }), + ).toBeVisible(); + + const trial = page + .getByRole("button", { name: "Try it free for 14 days" }) + .first(); + await expect(trial).toBeVisible(); + await page.mouse.move(10, 700); + await expect(page).toHaveScreenshot(shot); + + await trial.click(); + const dialog = page.locator("[class*='modal-dialog']"); + await expect(dialog).toBeVisible(); + await page.mouse.move(10, 700); + await expect(dialog).toHaveScreenshot(shot); + + await dialog.locator("button").first().hover(); + await expect(dialog).toHaveScreenshot(shot); + }); +}); + test.describe("Dashboard sidebar controls", () => { const shot = { maxDiffPixelRatio: 0, threshold: 0 }; diff --git a/frontend/src/app/main/ui/settings/subscription.cljs b/frontend/src/app/main/ui/settings/subscription.cljs index d1db4cab5f..3b879563a9 100644 --- a/frontend/src/app/main/ui/settings/subscription.cljs +++ b/frontend/src/app/main/ui/settings/subscription.cljs @@ -16,6 +16,7 @@ [app.main.ui.components.forms :as fm] [app.main.ui.dashboard.subscription :refer [get-subscription-type]] [app.main.ui.ds.buttons.button :refer [button*]] + [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] [app.main.ui.ds.foundations.assets.raw-svg :refer [raw-svg*]] [app.main.ui.nitrate.nitrate-activation-success-modal] @@ -93,6 +94,7 @@ [:button {:class (stl/css :cta-button :more-info) :on-click cta-link-with-icon} cta-text-with-icon [:> icon* {:icon-id "open-link" + :class (stl/css :cta-icon) :size "s"}]]) (when has-cta-link [:button {:class (stl/css-case :cta-button true @@ -137,7 +139,7 @@ [:button {:class (stl/css :cta-button :show-editors-button) :on-click handle-click} (tr "subscription.settings.management.dialog.editors") [:> icon* {:icon-id (if show-editors-list i/arrow-up i/arrow-down) - :class (stl/css :icon-dropdown) + :class (stl/css :cta-icon) :size "s"}]] (when show-editors-list [:* @@ -262,9 +264,11 @@ [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-dialog)} - [:button {:class (stl/css :close-btn) :on-click handle-close-dialog} - [:> icon* {:icon-id "close" - :size "m"}]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click handle-close-dialog}] [:div {:class (stl/css :modal-title :subscription-title)} (if (= unlimited-modal-step 2) (tr "subscription.settings.management-dialog.step-2-title") @@ -382,12 +386,15 @@ [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-dialog :subscription-success)} - [:button {:class (stl/css :close-btn) :on-click handle-close-dialog} - [:> icon* {:icon-id "close" - :size "m"}]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click handle-close-dialog}] [:div {:class (stl/css :modal-success-content)} [:div {:class (stl/css :modal-start)} - [:> raw-svg* {:id (if (= "light" (:theme profile)) "logo-subscription-light" "logo-subscription")}]] + [:> raw-svg* {:id (if (= "light" (:theme profile)) "logo-subscription-light" "logo-subscription") + :class (stl/css :modal-start-logo)}]] [:div {:class (stl/css :modal-end)} [:div {:class (stl/css :modal-title)} @@ -835,9 +842,11 @@ [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-dialog)} - [:button {:class (stl/css :close-btn) :on-click handle-close-dialog} - [:> icon* {:icon-id "close" - :size "m"}]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click handle-close-dialog}] [:div {:class (stl/css :modal-title :subscription-title :nitrate-subscription)} (tr "nitrate.form.title")] @@ -889,9 +898,11 @@ [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-dialog)} - [:button {:class (stl/css :close-btn) :on-click handle-close-dialog} - [:> icon* {:icon-id "close" - :size "m"}]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click handle-close-dialog}] [:div {:class (stl/css :modal-title :subscription-title)} (tr "nitrate.contact-sales.title" subscription-type)] [:div {:class (stl/css :modal-content)} @@ -942,9 +953,11 @@ [:div {:class (stl/css :modal-overlay)} [:div {:class (stl/css :modal-dialog)} - [:button {:class (stl/css :close-btn) :on-click handle-close-dialog} - [:> icon* {:icon-id "close" - :size "m"}]] + [:> icon-button* {:variant "ghost" + :tooltip-class (stl/css :close-btn) + :aria-label (tr "labels.close") + :icon i/close + :on-click handle-close-dialog}] [:div {:class (stl/css :modal-title :subscription-title)} (tr "nitrate.subscription.settings.manual-cancel")] diff --git a/frontend/src/app/main/ui/settings/subscription.scss b/frontend/src/app/main/ui/settings/subscription.scss index ff7885506d..b1fd03d2b6 100644 --- a/frontend/src/app/main/ui/settings/subscription.scss +++ b/frontend/src/app/main/ui/settings/subscription.scss @@ -4,8 +4,10 @@ // // Copyright (c) KALEIDOS SUBSIDIARY SL +// refactor/ stays for the modal placeholders (%modal-*), which move with +// the modals refactor. @use "refactor/common-refactor.scss" as deprecated; -@use "ds/typography.scss" as t; +@use "ds/typography.scss" as *; @use "ds/_borders.scss" as *; @use "ds/spacing.scss" as *; @use "ds/_sizes.scss" as *; @@ -39,34 +41,29 @@ } .membership-date { - @include t.use-typography("body-small"); + @include use-typography("body-small"); color: var(--color-foreground-secondary); margin-inline-start: var(--sp-s); } -.subscription-member, .penpot-member { - @extend %button-icon; -} - -.penpot-member { - stroke: var(--icon-foreground); + color: var(--color-foreground-secondary); } .subscription-member { - stroke: var(--color-badge-premium); + color: var(--color-badge-premium); } .title-section { - @include t.use-typography("title-large"); + @include use-typography("title-large"); color: var(--color-foreground-primary); margin-block-end: var(--sp-xxl); } .plan-section-title { - @include t.use-typography("headline-small"); + @include use-typography("headline-small"); color: var(--color-foreground-primary); } @@ -100,29 +97,26 @@ } .plan-title-icon { - @extend %button-icon; - + display: flex; + justify-content: center; + align-items: center; + color: var(--color-foreground-primary); block-size: var(--sp-xl); inline-size: var(--sp-xl); - border-radius: 6px; - border: 1.75px solid var(--color-foreground-primary); -} - -.plan-title-icon svg { - stroke: var(--color-foreground-primary); - stroke-width: 2.25px; + border-radius: $br-6; + border: $b-2 solid var(--color-foreground-primary); } .plan-card-title, .plan-price-value, .price-value { - @include t.use-typography("title-medium"); + @include use-typography("title-medium"); color: var(--color-foreground-primary); } .plan-editors { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); align-self: end; color: var(--color-foreground-primary); @@ -130,7 +124,7 @@ } .plan-price-period { - @include t.use-typography("body-small"); + @include use-typography("body-small"); color: var(--color-foreground-primary); } @@ -144,7 +138,7 @@ } .plan-cancel-date { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); color: var(--color-foreground-primary); } @@ -158,7 +152,7 @@ .benefits-title, .benefit { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); color: var(--color-foreground-secondary); } @@ -172,29 +166,24 @@ } .cta-button { - @include t.use-typography("body-medium"); - @include deprecated.button-style; + @include use-typography("body-medium"); + border: none; + background: none; + cursor: pointer; align-items: center; color: var(--color-accent-primary); display: flex; margin-block-start: var(--sp-m); - text-align: left; + text-align: start; } -.cta-button svg { - @extend %button-icon; - +.cta-icon { block-size: var(--sp-l); inline-size: var(--sp-l); - stroke: var(--color-accent-primary); margin-inline-start: var(--sp-xs); } -.icon-dropdown svg { - transform: rotate(90deg); -} - .bottom-link { margin-block-start: var(--sp-xs); } @@ -204,7 +193,7 @@ } .inline-error { - @include t.use-typography("body-small"); + @include use-typography("body-small"); color: var(--color-foreground-error); margin-block: var(--sp-s) 0; @@ -225,15 +214,18 @@ min-inline-size: px2rem(648); } +// Tooltip wrapper of the close buttons: it places them in the corner. .close-btn { - @extend %modal-close-btn-base; + position: absolute; + inset-block-start: var(--sp-s); + inset-inline-end: var(--sp-xs); } .modal-title { - @include t.use-typography("title-large"); + @include use-typography("title-large"); margin-block-end: var(--sp-xxxl); - color: var(--modal-title-foreground-color); + color: var(--color-foreground-primary); display: flex; gap: var(--sp-m); } @@ -243,7 +235,7 @@ } .modal-text-medium { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); } .modal-text-cap { @@ -257,7 +249,7 @@ .modal-text-medium strong, .text-strikethrough strong, .modal-text-cap strong { - font-weight: deprecated.$fw700; + font-weight: 700; } .modal-content, @@ -309,13 +301,13 @@ } } -.modal-start svg { +.modal-start-logo { inline-size: 100%; block-size: auto; } .editors-text { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); margin: 0; } @@ -326,7 +318,7 @@ } .editors-list { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); list-style-position: inside; list-style-type: none; @@ -342,7 +334,7 @@ } .error-message { - @include t.use-typography("body-small"); + @include use-typography("body-small"); color: var(--color-foreground-error); margin-block-start: var(--sp-s); @@ -361,7 +353,7 @@ } .unlimited-capped-warning { - @include t.use-typography("body-small"); + @include use-typography("body-small"); background-color: var(--color-background-tertiary); border-radius: var(--sp-s); @@ -374,28 +366,12 @@ padding-inline: 0; } -.radio-btns { - display: flex; - flex-direction: column; - padding-block-end: var(--sp-xl); - gap: var(--sp-s); -} - -.radio-btns label { - @include t.use-typography("body-large"); - - padding: 0; - display: flex; - align-items: center; - color: var(--color-foreground-secondary); -} - .modal-contact-content { gap: var(--sp-xl); } .downgrade-warning { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); background-color: var(--color-background-tertiary); border-radius: var(--sp-s); @@ -412,13 +388,13 @@ } .downgrade-item { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); margin-block-end: var(--sp-l); } .cta-link { - @include t.use-typography("body-medium"); + @include use-typography("body-medium"); align-items: center; color: var(--color-accent-primary);