From 560a0d09d5c8b8f93c15a6afe2abe8c5ccc2dab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Wed, 21 Jan 2026 15:10:38 +0100 Subject: [PATCH 1/2] :bug: Fix hiding avatar when we have 3 active users --- CHANGES.md | 1 + .../data/workspace/ws-notifications.js | 16 +++++++++++++ .../playwright/ui/specs/workspace.spec.js | 24 ++++++++++++++++++- .../src/app/main/ui/workspace/presence.cljs | 12 ++++++---- .../src/app/main/ui/workspace/presence.scss | 8 +++++-- 5 files changed, 53 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 379c6fe641..fabb3ff34e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -25,6 +25,7 @@ - Fix error message on components doesn't close automatically [Taiga #12012](https://tree.taiga.io/project/penpot/issue/12012) - Fix incorrect default option on tokens import dialog [Github #8051](https://github.com/penpot/penpot/pull/8051) - Fix unhandled exception tokens creation dialog [Github #8110](https://github.com/penpot/penpot/issues/8110) +- Fix displaying a hidden user avatar when there is only one more [Taiga #13058](https://tree.taiga.io/project/penpot/issue/13058) ## 2.13.0 (Unreleased) diff --git a/frontend/playwright/data/workspace/ws-notifications.js b/frontend/playwright/data/workspace/ws-notifications.js index 538b692018..63a8db3b55 100644 --- a/frontend/playwright/data/workspace/ws-notifications.js +++ b/frontend/playwright/data/workspace/ws-notifications.js @@ -5,3 +5,19 @@ export const presenceFixture = { "~:profile-id": "~uc7ce0794-0992-8105-8004-38e630f29a9b", "~:topic": "~uc7ce0794-0992-8105-8004-38f280443849", }; + +export const joinFixture2 = { + "~:type": "~:join-file", + "~:file-id": "~uc7ce0794-0992-8105-8004-38f280443849", + "~:session-id": "~u37730924-d520-80f1-8004-4ae6e5c3942e", + "~:profile-id": "~uc7ce0794-0992-8105-8004-38e630f29a9b", + "~:topic": "~uc7ce0794-0992-8105-8004-38f280443849", +}; + +export const joinFixture3 = { + "~:type": "~:join-file", + "~:file-id": "~uc7ce0794-0992-8105-8004-38f280443849", + "~:session-id": "~u37730924-d520-80f1-8004-4ae6e5c3942f", + "~:profile-id": "~uc7ce0794-0992-8105-8004-38e630f29a9b", + "~:topic": "~uc7ce0794-0992-8105-8004-38f280443849", +}; diff --git a/frontend/playwright/ui/specs/workspace.spec.js b/frontend/playwright/ui/specs/workspace.spec.js index 38e2bb56ca..2755053956 100644 --- a/frontend/playwright/ui/specs/workspace.spec.js +++ b/frontend/playwright/ui/specs/workspace.spec.js @@ -1,6 +1,6 @@ import { test, expect } from "@playwright/test"; import { WorkspacePage } from "../pages/WorkspacePage"; -import { presenceFixture } from "../../data/workspace/ws-notifications"; +import { presenceFixture, joinFixture2, joinFixture3 } from "../../data/workspace/ws-notifications"; test.beforeEach(async ({ page }) => { await WorkspacePage.init(page); @@ -40,6 +40,28 @@ test("User receives presence notifications updates in the workspace", async ({ ).toHaveCount(2); }); +test("BUG 13058 - Presence list shows up to 3 user avatars", async ({ + page, +}) => { + const workspacePage = new WorkspacePage(page); + await workspacePage.setupEmptyFile(); + + await workspacePage.goToWorkspace(); + await workspacePage.sendPresenceMessage(presenceFixture); + await workspacePage.sendPresenceMessage(joinFixture2); + + await expect( + page.getByTestId("active-users-list").getByAltText("Princesa Leia"), + ).toHaveCount(3); + + await workspacePage.sendPresenceMessage(joinFixture3); + await expect( + page.getByTestId("active-users-list").getByAltText("Princesa Leia"), + ).toHaveCount(2); + + await expect(page.getByTestId("active-users-list").getByText("+2")).toBeVisible(); +}); + test("User draws a rect", async ({ page }) => { const workspacePage = new WorkspacePage(page); await workspacePage.setupEmptyFile(); diff --git a/frontend/src/app/main/ui/workspace/presence.cljs b/frontend/src/app/main/ui/workspace/presence.cljs index 398dd8499a..d7bcda046f 100644 --- a/frontend/src/app/main/ui/workspace/presence.cljs +++ b/frontend/src/app/main/ui/workspace/presence.cljs @@ -22,7 +22,7 @@ (let [profile (assoc profile :color color) full-name (:fullname profile)] [:li {:class (stl/css :session-icon) - :style {:z-index (dm/str (+ 1 (* -1 index))) + :style {:z-index (dm/str (+ 2 (* -1 index))) :background-color color} :title full-name} [:img {:alt full-name @@ -37,9 +37,11 @@ sessions (vals presence) num-sessions (count sessions) + max-avatar-count 3 + avatar-count (if (= num-sessions max-avatar-count) max-avatar-count (- max-avatar-count 1)) open* (mf/use-state false) - open? (and ^boolean (deref open*) (> num-sessions 2)) + open? (and ^boolean (deref open*) (> num-sessions max-avatar-count)) on-open (mf/use-fn (fn [] @@ -67,10 +69,10 @@ [:button {:class (stl/css-case :active-users true) :on-click on-open} [:ul {:class (stl/css :active-users-list) :data-testid "active-users-list"} - (when (> num-sessions 2) - [:span {:class (stl/css :users-num)} (dm/str "+" (- num-sessions 2))]) + (when (> num-sessions max-avatar-count) + [:li {:class (stl/css :users-num)} (dm/str "+" (+ 1 (- num-sessions max-avatar-count)))]) - (for [[index session] (d/enumerate (take 2 sessions))] + (for [[index session] (d/enumerate (take avatar-count sessions))] [:& session-widget {:color (:color session) :index index diff --git a/frontend/src/app/main/ui/workspace/presence.scss b/frontend/src/app/main/ui/workspace/presence.scss index 7c54aafba7..4351b5eed2 100644 --- a/frontend/src/app/main/ui/workspace/presence.scss +++ b/frontend/src/app/main/ui/workspace/presence.scss @@ -16,6 +16,7 @@ margin: 0; padding: 0 deprecated.$s-4; border-radius: deprecated.$br-8; + .active-users-list { display: flex; flex-direction: row-reverse; @@ -26,9 +27,10 @@ @extend .user-icon; background-color: var(--user-count-background-color); color: var(--user-count-foreground-color); - z-index: deprecated.$z-index-2; + z-index: deprecated.$z-index-3; border: deprecated.$s-2 solid var(--user-count-foreground-color); } + .session-icon { @extend .user-icon; } @@ -43,11 +45,13 @@ margin: calc(-1 * deprecated.$s-2) calc(-1 * deprecated.$s-4) 0 0; background-color: var(--menu-background-color); z-index: deprecated.$z-index-4; + .active-users-list { gap: deprecated.$s-4; + .users-num, .session-icon { margin-left: 0; } } -} +} \ No newline at end of file From eaf64b6e160b40f58a63fd9284a1636dee552bba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Wed, 21 Jan 2026 16:07:48 +0100 Subject: [PATCH 2/2] :recycle: Make the CSS of presence widgets to adhere to our guidelines --- .../styles/common/refactor/basic-rules.scss | 13 --- .../src/app/main/ui/workspace/presence.scss | 83 +++++++++++-------- 2 files changed, 50 insertions(+), 46 deletions(-) diff --git a/frontend/resources/styles/common/refactor/basic-rules.scss b/frontend/resources/styles/common/refactor/basic-rules.scss index acf775e262..c82907a5b6 100644 --- a/frontend/resources/styles/common/refactor/basic-rules.scss +++ b/frontend/resources/styles/common/refactor/basic-rules.scss @@ -700,19 +700,6 @@ background-color: var(--menu-shortcut-background-color); } -.user-icon { - @include flexCenter; - @include bodySmallTypography; - height: $s-24; - width: $s-24; - border-radius: $br-circle; - margin-left: calc(-1 * $s-4); - img { - border-radius: $br-circle; - border: $s-2 solid var(--user-count-foreground-color); - } -} - .mixed-bar { @include bodySmallTypography; display: flex; diff --git a/frontend/src/app/main/ui/workspace/presence.scss b/frontend/src/app/main/ui/workspace/presence.scss index 4351b5eed2..03f6c8134e 100644 --- a/frontend/src/app/main/ui/workspace/presence.scss +++ b/frontend/src/app/main/ui/workspace/presence.scss @@ -4,54 +4,71 @@ // // Copyright (c) KALEIDOS INC -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/typography.scss" as t; +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; .active-users, .active-users-opened { - @include deprecated.buttonStyle; + background: none; + cursor: pointer; + display: flex; flex-direction: row-reverse; justify-content: flex-end; align-items: center; margin: 0; - padding: 0 deprecated.$s-4; - border-radius: deprecated.$br-8; + padding: 0 var(--sp-xs); + border: none; + border-radius: $br-8; +} - .active-users-list { - display: flex; - flex-direction: row-reverse; - justify-content: flex-end; - margin: 0; +.active-users-list { + display: flex; + flex-direction: row-reverse; + justify-content: flex-end; + margin: 0; + gap: var(--user-list-gap, 0); +} - .users-num { - @extend .user-icon; - background-color: var(--user-count-background-color); - color: var(--user-count-foreground-color); - z-index: deprecated.$z-index-3; - border: deprecated.$s-2 solid var(--user-count-foreground-color); - } +%user-icon { + @include t.use-typography("body-small"); + display: grid; + place-content: center; + height: $sz-24; + width: $sz-24; + border-radius: $br-circle; + margin-inline-start: calc(-1 * var(--sp-xs)); - .session-icon { - @extend .user-icon; - } + img { + border-radius: $br-circle; + border: $b-2 solid var(--user-count-foreground-color); } } +.users-num { + @extend %user-icon; + background-color: var(--user-count-background-color); + color: var(--user-count-foreground-color); + z-index: 3; // FIXME: this is hardcoded because of the way its component uses z-index from cljs + border: $b-2 solid var(--user-count-foreground-color); + margin-inline-start: var(--user-list-inline-margin, calc(-1 * var(--sp-xs))); +} + +.session-icon { + @extend %user-icon; + margin-inline-start: var(--user-list-inline-margin, calc(-1 * var(--sp-xs))); +} + .active-users-opened { position: absolute; - right: calc(-1 * deprecated.$s-2); - top: calc(-1 * deprecated.$s-2); - padding: deprecated.$s-8; - margin: calc(-1 * deprecated.$s-2) calc(-1 * deprecated.$s-4) 0 0; + right: calc(-1 * var(--sp-xxs)); + top: calc(-1 * var(--sp-xxs)); + padding: var(--sp-s); + margin: calc(-1 * var(--sp-xxs)) calc(-1 * var(--sp-xs)) 0 0; background-color: var(--menu-background-color); - z-index: deprecated.$z-index-4; + z-index: 4; // FIXME: this is hardcoded because of the way its component uses z-index from cljs - .active-users-list { - gap: deprecated.$s-4; - - .users-num, - .session-icon { - margin-left: 0; - } - } -} \ No newline at end of file + --user-list-gap: var(--sp-xs); + --user-list-inline-margin: 0; +}