mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 05:50:35 +00:00
♻️ Use DS buttons in the dashboard team modals (#12537)
* ♻️ Use DS buttons in the dashboard team modals The close buttons of the create/rename team modal, the team permission notice and the two "before you leave" modals become ghost DS icon buttons named "Close"; the class that places them goes on the tooltip wrapper. The cancel and "promote and leave" inputs of the leave modals become DS buttons: secondary, and destructive once a member is picked (primary and disabled before). Two fixtures make the leave and reassign modal testable (the logged-in user owns the second team, which has another member). A visual test covers the create team modal and the leave and reassign modal. AI-assisted-by: claude-opus-5-5 * ♻️ Move dashboard team modal styles closer to the DS The change owner and team form stylesheets resolve their legacy color aliases, spacing and typography mixins to DS tokens and use the DS typography module without a namespace. The legacy uppercase title becomes the DS headline-small typography, which differs only in its size (12px instead of 11px): the "before you leave" title grows 1px and the modal 2px. Unused selectors are commented out with a TODO. refactor/ stays only for the modal placeholders (%modal-*), which move with the modals refactor, and the input placeholders. AI-assisted-by: claude-opus-5-5 * 🔥 Remove unused styles from the dashboard team modals The SCSS refactor of the dashboard team modals 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: .input-wrapper, .cancel-button, &.danger. No visual change. AI-assisted-by: claude-opus-5-5
This commit is contained in:
parent
19fc9ebce8
commit
afd838ea5b
@ -0,0 +1,30 @@
|
||||
[
|
||||
{
|
||||
"~:is-admin": false,
|
||||
"~:email": "bar@example.com",
|
||||
"~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3",
|
||||
"~:name": "Han Solo",
|
||||
"~:fullname": "Han Solo",
|
||||
"~:is-owner": false,
|
||||
"~:modified-at": "~m1713533116365",
|
||||
"~:can-edit": true,
|
||||
"~:is-active": true,
|
||||
"~:id": "~u1e162163-87b7-805b-8005-5fd05514b6d3",
|
||||
"~:profile-id": "~u1e162163-87b7-805b-8005-5fd05514b6d3",
|
||||
"~:created-at": "~m1733324626956"
|
||||
},
|
||||
{
|
||||
"~:is-admin": true,
|
||||
"~:email": "foo@example.com",
|
||||
"~:team-id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3",
|
||||
"~:name": "Princesa Leia",
|
||||
"~:fullname": "Princesa Leia",
|
||||
"~:is-owner": true,
|
||||
"~:modified-at": "~m1713533116365",
|
||||
"~:can-edit": true,
|
||||
"~:is-active": true,
|
||||
"~:id": "~uc7ce0794-0992-8105-8004-38e630f29a9b",
|
||||
"~:profile-id": "~uf56647eb-19a7-8115-8003-b6bc939ecd1b",
|
||||
"~:created-at": "~m1713533116365"
|
||||
}
|
||||
]
|
||||
@ -0,0 +1,48 @@
|
||||
[
|
||||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"layout/grid",
|
||||
"styles/v2",
|
||||
"fdata/pointer-map",
|
||||
"fdata/objects-map",
|
||||
"components/v2",
|
||||
"fdata/shape-data-type"
|
||||
]
|
||||
},
|
||||
"~:permissions": {
|
||||
"~:type": "~:membership",
|
||||
"~:is-owner": true,
|
||||
"~:is-admin": true,
|
||||
"~:can-edit": true
|
||||
},
|
||||
"~:name": "Default",
|
||||
"~:modified-at": "~m1713533116375",
|
||||
"~:id": "~uc7ce0794-0992-8105-8004-38e630f40f6d",
|
||||
"~:created-at": "~m1713533116375",
|
||||
"~:is-default": true
|
||||
},
|
||||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"layout/grid",
|
||||
"styles/v2",
|
||||
"fdata/pointer-map",
|
||||
"fdata/objects-map",
|
||||
"components/v2",
|
||||
"fdata/shape-data-type"
|
||||
]
|
||||
},
|
||||
"~:permissions": {
|
||||
"~:type": "~:membership",
|
||||
"~:is-owner": true,
|
||||
"~:is-admin": true,
|
||||
"~:can-edit": true
|
||||
},
|
||||
"~:name": "Second team",
|
||||
"~:modified-at": "~m1701164272671",
|
||||
"~:id": "~udd33ff88-f4e5-8033-8003-8096cc07bdf3",
|
||||
"~:created-at": "~m1701164272671",
|
||||
"~:is-default": false
|
||||
}
|
||||
]
|
||||
@ -438,3 +438,51 @@ test.describe("Subscription controls", () => {
|
||||
await expect(page).toHaveScreenshot(shot);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Team modals controls", () => {
|
||||
const shot = { maxDiffPixelRatio: 0, threshold: 0 };
|
||||
|
||||
test("Create team modal", async ({ page }) => {
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.goToDashboard();
|
||||
await dashboardPage.teamDropdown.click();
|
||||
await page.getByText("Create new team").click();
|
||||
|
||||
const modal = page.locator("[class*='modal-container']");
|
||||
await expect(modal).toBeVisible();
|
||||
await page.mouse.move(10, 700);
|
||||
await expect(modal).toHaveScreenshot(shot);
|
||||
|
||||
await modal.locator("[class*='modal-header'] button").hover();
|
||||
await expect(modal).toHaveScreenshot(shot);
|
||||
});
|
||||
|
||||
test("Leave and reassign modal", async ({ page }) => {
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
// The logged-in user owns the second team, which has another member.
|
||||
await dashboardPage.mockRPC(
|
||||
"get-teams",
|
||||
"dashboard/get-teams-second-owner.json",
|
||||
);
|
||||
await dashboardPage.mockRPC(
|
||||
"get-team-members?team-id=*",
|
||||
"dashboard/get-team-members-owner.json",
|
||||
);
|
||||
await dashboardPage.goToSecondTeamMembersSection();
|
||||
await expect(page.getByText("role")).toBeVisible();
|
||||
|
||||
const rows = page.locator("[class*='table-rows']");
|
||||
await rows
|
||||
.locator("[class*='menu-btn']")
|
||||
.or(rows.getByRole("button", { name: "Options" }))
|
||||
.first()
|
||||
.click();
|
||||
await rows.getByText("Leave team").click();
|
||||
|
||||
const modal = page.locator("[class*='modal-container']");
|
||||
await expect(modal).toBeVisible();
|
||||
await page.mouse.move(10, 700);
|
||||
await expect(modal).toHaveScreenshot(shot);
|
||||
});
|
||||
});
|
||||
|
||||
@ -12,7 +12,9 @@
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.ui.components.forms :as fm]
|
||||
[app.main.ui.icons :as deprecated-icon]
|
||||
[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 :as i]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
@ -45,8 +47,11 @@
|
||||
[:div {:class (stl/css :modal-container)}
|
||||
[:div {:class (stl/css :modal-header)}
|
||||
[:h2 {:class (stl/css :modal-title)} (tr "modals.leave-and-reassign.title")]
|
||||
[:button {:class (stl/css :modal-close-btn)
|
||||
:on-click modal/hide!} deprecated-icon/close]]
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:tooltip-class (stl/css :modal-close-btn)
|
||||
:aria-label (tr "labels.close")
|
||||
:icon i/close
|
||||
:on-click modal/hide!}]]
|
||||
|
||||
[:div {:class (stl/css :modal-content)}
|
||||
[:p {:class (stl/css :modal-msg)}
|
||||
@ -62,19 +67,16 @@
|
||||
|
||||
[:div {:class (stl/css :modal-footer)}
|
||||
[:div {:class (stl/css :action-buttons)}
|
||||
[:input {:class (stl/css :cancel-button)
|
||||
:type "button"
|
||||
:value (tr "labels.cancel")
|
||||
:on-click modal/hide!}]
|
||||
[:> button* {:variant "secondary"
|
||||
:type "button"
|
||||
:on-click modal/hide!}
|
||||
(tr "labels.cancel")]
|
||||
|
||||
[:input.accept-button
|
||||
{:type "button"
|
||||
:class (stl/css-case :accept-btn true
|
||||
:danger (:valid @form)
|
||||
:global/disabled (not (:valid @form)))
|
||||
:disabled (not (:valid @form))
|
||||
:value (tr "modals.leave-and-reassign.promote-and-leave")
|
||||
:on-click on-accept}]]]]]))
|
||||
[:> button* {:variant (if (:valid @form) "destructive" "primary")
|
||||
:type "button"
|
||||
:disabled (not (:valid @form))
|
||||
:on-click on-accept}
|
||||
(tr "modals.leave-and-reassign.promote-and-leave")]]]]]))
|
||||
|
||||
|
||||
|
||||
@ -149,8 +151,11 @@
|
||||
[:div {:class (stl/css :modal-organization-container)}
|
||||
[:div {:class (stl/css :modal-header)}
|
||||
[:h2 {:class (stl/css :modal-organization-title)} (tr "modals.before-leave-organization.title")]
|
||||
[:button {:class (stl/css :modal-close-btn)
|
||||
:on-click modal/hide!} deprecated-icon/close]]
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:tooltip-class (stl/css :modal-close-btn)
|
||||
:aria-label (tr "labels.close")
|
||||
:icon i/close
|
||||
:on-click modal/hide!}]]
|
||||
|
||||
[:div {:class (stl/css :modal-content)}
|
||||
(if (zero? num-teams-to-delete)
|
||||
@ -169,16 +174,13 @@
|
||||
|
||||
[:div {:class (stl/css :modal-footer)}
|
||||
[:div {:class (stl/css :action-buttons)}
|
||||
[:input {:class (stl/css :cancel-button)
|
||||
:type "button"
|
||||
:value (tr "labels.cancel")
|
||||
:on-click modal/hide!}]
|
||||
[:> button* {:variant "secondary"
|
||||
:type "button"
|
||||
:on-click modal/hide!}
|
||||
(tr "labels.cancel")]
|
||||
|
||||
[:input.accept-button
|
||||
{:type "button"
|
||||
:class (stl/css-case :accept-btn true
|
||||
:danger all-valid?
|
||||
:global/disabled (not all-valid?))
|
||||
:disabled (not all-valid?)
|
||||
:value (tr "modals.leave-and-reassign.promote-and-leave")
|
||||
:on-click on-accept}]]]]]))
|
||||
[:> button* {:variant (if all-valid? "destructive" "primary")
|
||||
:type "button"
|
||||
:disabled (not all-valid?)
|
||||
:on-click on-accept}
|
||||
(tr "modals.leave-and-reassign.promote-and-leave")]]]]]))
|
||||
|
||||
@ -4,8 +4,10 @@
|
||||
//
|
||||
// Copyright (c) KALEIDOS SUBSIDIARY SL
|
||||
|
||||
// refactor/ stays for the modal placeholders (%modal-*, to move with the
|
||||
// modals refactor) and the input placeholder (%input-with-label, with the inputs).
|
||||
@use "refactor/common-refactor.scss" as deprecated;
|
||||
@use "ds/typography.scss" as t;
|
||||
@use "ds/typography.scss" as *;
|
||||
@use "ds/_sizes.scss" as *;
|
||||
@use "ds/z-index.scss" as *;
|
||||
|
||||
@ -20,48 +22,34 @@
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
margin-bottom: deprecated.$s-24;
|
||||
margin-block-end: var(--sp-xxl);
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
@include deprecated.uppercase-title-typography;
|
||||
@include use-typography("headline-small");
|
||||
|
||||
color: var(--modal-title-foreground-color);
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
// Tooltip wrapper of the close button: it places it in the corner.
|
||||
.modal-close-btn {
|
||||
@extend %modal-close-btn-base;
|
||||
position: absolute;
|
||||
inset-block-start: var(--sp-s);
|
||||
inset-inline-end: var(--sp-xs);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
@include deprecated.body-small-typography;
|
||||
@include use-typography("body-small");
|
||||
|
||||
margin-bottom: deprecated.$s-24;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
@extend %input-with-label;
|
||||
@include deprecated.body-small-typography;
|
||||
margin-block-end: var(--sp-xxl);
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
@extend %modal-action-btns;
|
||||
}
|
||||
|
||||
.cancel-button {
|
||||
@extend %modal-cancel-btn;
|
||||
}
|
||||
|
||||
.accept-btn {
|
||||
@extend %modal-accept-btn;
|
||||
|
||||
&.danger {
|
||||
@extend %modal-danger-btn;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-msg {
|
||||
color: var(--modal-text-foreground-color);
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.teams-container {
|
||||
@ -75,34 +63,34 @@
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
inline-size: 100%;
|
||||
}
|
||||
|
||||
.modal-organization-container {
|
||||
@extend %modal-container-base;
|
||||
|
||||
overflow-y: auto;
|
||||
max-height: $sz-512;
|
||||
max-block-size: $sz-512;
|
||||
}
|
||||
|
||||
.modal-organization-title {
|
||||
@include t.use-typography("headline-large");
|
||||
@include use-typography("headline-large");
|
||||
|
||||
color: var(--modal-title-foreground-color);
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
.modal-organization-msg {
|
||||
@include t.use-typography("body-large");
|
||||
@include use-typography("body-large");
|
||||
|
||||
color: var(--modal-text-foreground-color);
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.team-name {
|
||||
@include t.use-typography("body-medium");
|
||||
@include use-typography("body-medium");
|
||||
|
||||
color: var(--modal-text-foreground-color);
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.team-member {
|
||||
@include t.use-typography("body-medium");
|
||||
@include use-typography("body-medium");
|
||||
}
|
||||
|
||||
@ -18,7 +18,8 @@
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.forms :as fm]
|
||||
[app.main.ui.icons :as deprecated-icon]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.keyboard :as kbd]
|
||||
@ -118,8 +119,11 @@
|
||||
[:h2 {:class (stl/css :modal-title)}
|
||||
(tr "labels.create-team")])
|
||||
|
||||
[:button {:class (stl/css :modal-close-btn)
|
||||
:on-click modal/hide!} deprecated-icon/close]]
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:tooltip-class (stl/css :modal-close-btn)
|
||||
:aria-label (tr "labels.close")
|
||||
:icon i/close
|
||||
:on-click modal/hide!}]]
|
||||
|
||||
[:div {:class (stl/css :modal-content)}
|
||||
[:& fm/input {:type "text"
|
||||
@ -162,7 +166,10 @@
|
||||
[:div {:class (stl/css :modal-container)}
|
||||
[:div {:class (stl/css :modal-header)}
|
||||
[:h2 {:class (stl/css :modal-title)} title]
|
||||
[:button {:class (stl/css :modal-close-btn)
|
||||
:on-click modal/hide!} deprecated-icon/close]]
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:tooltip-class (stl/css :modal-close-btn)
|
||||
:aria-label (tr "labels.close")
|
||||
:icon i/close
|
||||
:on-click modal/hide!}]]
|
||||
[:div {:class (stl/css :modal-content)}
|
||||
[:div message]]]]))
|
||||
|
||||
@ -4,9 +4,11 @@
|
||||
//
|
||||
// Copyright (c) KALEIDOS SUBSIDIARY SL
|
||||
|
||||
// refactor/ stays for the modal placeholders (%modal-*, to move with the
|
||||
// modals refactor) and the input placeholder (%input-element-label, with the inputs).
|
||||
@use "refactor/common-refactor.scss" as deprecated;
|
||||
@use "ds/_utils.scss" as *;
|
||||
@use "ds/typography.scss" as t;
|
||||
@use "ds/typography.scss" as *;
|
||||
|
||||
.modal-overlay {
|
||||
@extend %modal-overlay-base;
|
||||
@ -21,20 +23,23 @@
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
@include t.use-typography("headline-large");
|
||||
@include use-typography("headline-large");
|
||||
|
||||
color: var(--modal-title-foreground-color);
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
// Tooltip wrapper of the close button: it places it in the corner.
|
||||
.modal-close-btn {
|
||||
@extend %modal-close-btn-base;
|
||||
position: absolute;
|
||||
inset-block-start: var(--sp-s);
|
||||
inset-inline-end: var(--sp-xs);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
margin-block-end: var(--sp-xxl);
|
||||
color: var(--color-foreground-secondary);
|
||||
|
||||
@include t.use-typography("body-large");
|
||||
@include use-typography("body-large");
|
||||
}
|
||||
|
||||
.team-form {
|
||||
@ -52,24 +57,16 @@
|
||||
flex-direction: column;
|
||||
gap: var(--sp-xs);
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
inline-size: 100%;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
height: 100%;
|
||||
block-size: 100%;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
@extend %modal-action-btns;
|
||||
}
|
||||
|
||||
.cancel-button {
|
||||
@extend %modal-cancel-btn;
|
||||
}
|
||||
|
||||
.accept-btn {
|
||||
@extend %modal-accept-btn;
|
||||
|
||||
&.danger {
|
||||
@extend %modal-danger-btn;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user