From a7859c339a0aa6c1a18b11af57f16c174ac880b7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 12 Jun 2026 15:35:45 +0100 Subject: [PATCH 01/25] First stage 1. Make avatar BIGGER! 2. Move status button out to its own component so we can reuse it --- .../css/views/settings/_AvatarSetting.pcss | 10 ++--- .../views/settings/AvatarSetting.tsx | 2 +- .../src/menus/UserMenu/UserMenu.module.css | 27 ------------- .../src/menus/UserMenu/UserMenu.tsx | 28 ++----------- .../src/status/StatusButtonView.module.css | 33 +++++++++++++++ .../src/status/StatusButtonView.tsx | 40 +++++++++++++++++++ 6 files changed, 82 insertions(+), 58 deletions(-) create mode 100644 packages/shared-components/src/status/StatusButtonView.module.css create mode 100644 packages/shared-components/src/status/StatusButtonView.tsx diff --git a/apps/web/res/css/views/settings/_AvatarSetting.pcss b/apps/web/res/css/views/settings/_AvatarSetting.pcss index b928522820e..fc87bf422aa 100644 --- a/apps/web/res/css/views/settings/_AvatarSetting.pcss +++ b/apps/web/res/css/views/settings/_AvatarSetting.pcss @@ -7,9 +7,9 @@ Please see LICENSE files in the repository root for full details. */ .mx_AvatarSetting_avatar { - width: 90px; - min-width: 90px; /* so it doesn't get crushed by the flexbox in languages with longer words */ - height: 90px; + width: 120px; + min-width: 120px; /* so it doesn't get crushed by the flexbox in languages with longer words */ + height: 120px; margin-top: 8px; position: relative; @@ -31,9 +31,9 @@ Please see LICENSE files in the repository root for full details. & > img { display: block; - height: 90px; + height: 120px; width: inherit; - border-radius: 90px; + border-radius: 120px; cursor: pointer; object-fit: cover; } diff --git a/apps/web/src/components/views/settings/AvatarSetting.tsx b/apps/web/src/components/views/settings/AvatarSetting.tsx index 1c27efa75c4..7407f64b56e 100644 --- a/apps/web/src/components/views/settings/AvatarSetting.tsx +++ b/apps/web/src/components/views/settings/AvatarSetting.tsx @@ -181,7 +181,7 @@ const AvatarSetting: React.FC = ({ diff --git a/packages/shared-components/src/menus/UserMenu/UserMenu.module.css b/packages/shared-components/src/menus/UserMenu/UserMenu.module.css index 1aa0eccf473..4b521f4cd4d 100644 --- a/packages/shared-components/src/menus/UserMenu/UserMenu.module.css +++ b/packages/shared-components/src/menus/UserMenu/UserMenu.module.css @@ -77,33 +77,6 @@ background-color: var(--cpd-color-bg-canvas-default); } -.statusButton { - border-radius: 30px; - background: var(--cpd-color-bg-subtle-secondary); - display: flex; - align-items: center; - padding-inline-start: var(--cpd-space-2x); - gap: 6px; - white-space: nowrap; - overflow: hidden; - max-width: 100%; - - .menuStatusEmoji { - flex-shrink: 0; - } - - .menuStatusText { - overflow: hidden; - min-width: 0; - text-overflow: ellipsis; - } - - span { - font: var(--cpd-font-body-md-medium); - color: var(--cpd-color-text-secondary); - } -} - .triggerButton { border: none; background: none; diff --git a/packages/shared-components/src/menus/UserMenu/UserMenu.tsx b/packages/shared-components/src/menus/UserMenu/UserMenu.tsx index 31301a3c5b6..1566e3bc29c 100644 --- a/packages/shared-components/src/menus/UserMenu/UserMenu.tsx +++ b/packages/shared-components/src/menus/UserMenu/UserMenu.tsx @@ -6,7 +6,7 @@ */ import React, { type JSX } from "react"; -import { Avatar, Button, IconButton, Link, Menu, MenuItem, Separator, Text } from "@vector-im/compound-web"; +import { Avatar, Button, Link, Menu, MenuItem, Separator, Text } from "@vector-im/compound-web"; import { ChatProblemIcon, DevicesIcon, @@ -14,7 +14,6 @@ import { LockIcon, PopOutIcon, SettingsIcon, - CloseIcon, } from "@vector-im/compound-design-tokens/assets/web/icons"; import classNames from "classnames"; @@ -22,7 +21,7 @@ import styles from "./UserMenu.module.css"; import { useViewModel, type ViewModel } from "../../core/viewmodel"; import { useI18n } from "../../core/i18n/i18nContext"; import { type UserStatus } from "../../core/userStatus"; -import { _t } from "../.."; +import { StatusButtonView } from "../../status/StatusButtonView"; export interface UserMenuViewSnapshot { /** @@ -118,27 +117,6 @@ export type UserMenuViewProps = { className?: string; }; -function StatusButton({ status, clearStatus }: { status: UserStatus; clearStatus: () => void }): JSX.Element { - return ( -
- - {status.emoji} - - - {status.text} - - - - -
- ); -} - export function UserMenuView({ vm, className }: UserMenuViewProps): JSX.Element { const { userId, displayName, avatarUrl, expanded, open, manageAccountHref, actions, showAvatar, userStatus } = useViewModel(vm); @@ -175,7 +153,7 @@ export function UserMenuView({ vm, className }: UserMenuViewProps): JSX.Element {displayName} - {userStatus && } + {userStatus && } {userId} diff --git a/packages/shared-components/src/status/StatusButtonView.module.css b/packages/shared-components/src/status/StatusButtonView.module.css new file mode 100644 index 00000000000..6e18842622d --- /dev/null +++ b/packages/shared-components/src/status/StatusButtonView.module.css @@ -0,0 +1,33 @@ +/* + * Copyright 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +.statusButton { + border-radius: 30px; + background: var(--cpd-color-bg-subtle-secondary); + display: flex; + align-items: center; + padding-inline-start: var(--cpd-space-2x); + gap: 6px; + white-space: nowrap; + overflow: hidden; + max-width: 100%; + + .menuStatusEmoji { + flex-shrink: 0; + } + + .menuStatusText { + overflow: hidden; + min-width: 0; + text-overflow: ellipsis; + } + + span { + font: var(--cpd-font-body-md-medium); + color: var(--cpd-color-text-secondary); + } +} diff --git a/packages/shared-components/src/status/StatusButtonView.tsx b/packages/shared-components/src/status/StatusButtonView.tsx new file mode 100644 index 00000000000..e8a5f564592 --- /dev/null +++ b/packages/shared-components/src/status/StatusButtonView.tsx @@ -0,0 +1,40 @@ +/* + * Copyright 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +import React, { type JSX } from "react"; +import { IconButton, Text } from "@vector-im/compound-web"; +import { CloseIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; + +import { _t, type UserStatus } from ".."; +import styles from "./StatusButtonView.module.css"; + +export function StatusButtonView({ + status, + clearStatus, +}: { + status: UserStatus; + clearStatus: () => void; +}): JSX.Element { + return ( +
+ + {status.emoji} + + + {status.text} + + + + +
+ ); +} From 2e2792f72819f1c7516ed1020d55b992465117e2 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 16 Jun 2026 12:57:18 +0100 Subject: [PATCH 02/25] Working version but using context menu rather than dropdown menu which almost looks right but not quite. --- .../views/settings/_UserProfileSettings.pcss | 3 ++ .../views/settings/UserProfileSettings.tsx | 41 +++++++++++-------- .../src/i18n/strings/en_EN.json | 10 +++++ packages/shared-components/src/index.ts | 1 + .../src/status/StatusButtonView.module.css | 2 +- .../src/status/StatusButtonView.tsx | 18 ++++---- 6 files changed, 49 insertions(+), 26 deletions(-) diff --git a/apps/web/res/css/views/settings/_UserProfileSettings.pcss b/apps/web/res/css/views/settings/_UserProfileSettings.pcss index 845aa52c1c8..9f43037e921 100644 --- a/apps/web/res/css/views/settings/_UserProfileSettings.pcss +++ b/apps/web/res/css/views/settings/_UserProfileSettings.pcss @@ -23,6 +23,9 @@ Please see LICENSE files in the repository root for full details. width: 100%; gap: 0; } + .mx_UserProfileSettings_profile_nameAndStatus { + width: 100%; + } } .mx_UserProfileSettings_profile_controls { diff --git a/apps/web/src/components/views/settings/UserProfileSettings.tsx b/apps/web/src/components/views/settings/UserProfileSettings.tsx index 0ecd5d3dfaf..b5dfff14995 100644 --- a/apps/web/src/components/views/settings/UserProfileSettings.tsx +++ b/apps/web/src/components/views/settings/UserProfileSettings.tsx @@ -11,7 +11,7 @@ import { logger } from "matrix-js-sdk/src/logger"; import { EditInPlace, Alert, ErrorMessage } from "@vector-im/compound-web"; import PopOutIcon from "@vector-im/compound-design-tokens/assets/web/icons/pop-out"; import SignOutIcon from "@vector-im/compound-design-tokens/assets/web/icons/sign-out"; -import { Flex } from "@element-hq/web-shared-components"; +import { Flex, SetStatusView, useCreateAutoDisposedViewModel } from "@element-hq/web-shared-components"; import { _t } from "../../../languageHandler"; import { OwnProfileStore } from "../../../stores/OwnProfileStore"; @@ -28,6 +28,7 @@ import LogoutDialog, { shouldShowLogoutDialog } from "../dialogs/LogoutDialog"; import Modal from "../../../Modal"; import defaultDispatcher from "../../../dispatcher/dispatcher"; import { SettingsSection } from "./shared/SettingsSection.tsx"; +import { SetStatusViewModel } from "../../../viewmodels/status/SetStatusViewModel.ts"; const SpinnerToast: React.FC<{ children?: ReactNode }> = ({ children }) => ( <> @@ -132,6 +133,8 @@ const UserProfileSettings: React.FC = ({ })(); }, [client]); + const setStatusVM = useCreateAutoDisposedViewModel(() => new SetStatusViewModel({ client })); + const onAvatarRemove = useCallback(async () => { const removeToast = toastRack.displayToast( {_t("settings|general|avatar_remove_progress")}, @@ -213,21 +216,27 @@ const UserProfileSettings: React.FC = ({ placeholderId={client.getUserId() ?? ""} disabled={!canSetAvatar} /> - - {displayNameError && {_t("settings|general|display_name_error")}} - + + + + {displayNameError && ( + {_t("settings|general|display_name_error")} + )} + + + {avatarError && ( diff --git a/packages/shared-components/src/i18n/strings/en_EN.json b/packages/shared-components/src/i18n/strings/en_EN.json index 80bed47b08d..5d839ddb14f 100644 --- a/packages/shared-components/src/i18n/strings/en_EN.json +++ b/packages/shared-components/src/i18n/strings/en_EN.json @@ -181,6 +181,16 @@ "space_settings": "Space settings" } }, + "status": { + "set_status": { + "away": "Away", + "be_right_back": "Be right back", + "focus_time": "Focus Time", + "in_a_meeting": "In a meeting", + "on_the_road": "On the road", + "set_status_prompt": "What's your status?" + } + }, "terms": { "tac_button": "Review terms and conditions" }, diff --git a/packages/shared-components/src/index.ts b/packages/shared-components/src/index.ts index d8ada8a735c..89bb7882ec7 100644 --- a/packages/shared-components/src/index.ts +++ b/packages/shared-components/src/index.ts @@ -89,5 +89,6 @@ export * from "./core/utils/FormattingUtils"; export * from "./core/i18n/I18nApi"; export * from "./core/utils/linkify"; export type * from "./core/userStatus.ts"; +export * from "./status/SetStatusView"; // MVVM export * from "./core/viewmodel"; diff --git a/packages/shared-components/src/status/StatusButtonView.module.css b/packages/shared-components/src/status/StatusButtonView.module.css index 6e18842622d..a52bae20874 100644 --- a/packages/shared-components/src/status/StatusButtonView.module.css +++ b/packages/shared-components/src/status/StatusButtonView.module.css @@ -8,7 +8,7 @@ .statusButton { border-radius: 30px; background: var(--cpd-color-bg-subtle-secondary); - display: flex; + display: inline-flex; align-items: center; padding-inline-start: var(--cpd-space-2x); gap: 6px; diff --git a/packages/shared-components/src/status/StatusButtonView.tsx b/packages/shared-components/src/status/StatusButtonView.tsx index e8a5f564592..42753aac8b1 100644 --- a/packages/shared-components/src/status/StatusButtonView.tsx +++ b/packages/shared-components/src/status/StatusButtonView.tsx @@ -12,15 +12,15 @@ import { CloseIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; import { _t, type UserStatus } from ".."; import styles from "./StatusButtonView.module.css"; -export function StatusButtonView({ - status, - clearStatus, -}: { - status: UserStatus; - clearStatus: () => void; -}): JSX.Element { +export const StatusButtonView = React.forwardRef< + HTMLDivElement, + { + status: UserStatus; + clearStatus: () => void; + } & React.HTMLAttributes +>(function StatusButtonView({ status, clearStatus, ...props }, ref): JSX.Element { return ( -
+
{status.emoji} @@ -37,4 +37,4 @@ export function StatusButtonView({
); -} +}); From 0bc1bcc0bfbfe906c6b59744b45007ed80839df1 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 16 Jun 2026 13:07:23 +0100 Subject: [PATCH 03/25] Commit the set status view component --- .../src/status/SetStatusView.module.css | 29 ++++++ .../src/status/SetStatusView.stories.tsx | 81 ++++++++++++++++ .../src/status/SetStatusView.tsx | 96 +++++++++++++++++++ 3 files changed, 206 insertions(+) create mode 100644 packages/shared-components/src/status/SetStatusView.module.css create mode 100644 packages/shared-components/src/status/SetStatusView.stories.tsx create mode 100644 packages/shared-components/src/status/SetStatusView.tsx diff --git a/packages/shared-components/src/status/SetStatusView.module.css b/packages/shared-components/src/status/SetStatusView.module.css new file mode 100644 index 00000000000..4796fbb0807 --- /dev/null +++ b/packages/shared-components/src/status/SetStatusView.module.css @@ -0,0 +1,29 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +.setStatusTrigger { + background: none; + border: none; + cursor: pointer; + text-decoration: underline solid; + display: flex; + align-items: center; + + svg { + width: 20px; + height: 20px; + margin-right: 1.5px; + } +} + +.menuItem { + place-items: start; + gap: var(--cpd-space-1x); + color: var(--cpd-color-text-secondary); + font-weight: var(--cpd-font-weight-medium); + font-size: var(--cpd-font-size-body-md); +} diff --git a/packages/shared-components/src/status/SetStatusView.stories.tsx b/packages/shared-components/src/status/SetStatusView.stories.tsx new file mode 100644 index 00000000000..4df59cf1c39 --- /dev/null +++ b/packages/shared-components/src/status/SetStatusView.stories.tsx @@ -0,0 +1,81 @@ +/* + * Copyright 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +import { type Meta, type StoryObj } from "@storybook/react-vite"; +import React, { type JSX } from "react"; +import { fn } from "storybook/test"; + +import { SetStatusView, type SetStatusViewActions, type SetStatusViewSnapshot } from "./SetStatusView"; +import { useMockedViewModel } from "../core/viewmodel"; +import { withViewDocs } from "../../.storybook/withViewDocs"; + +const SetStatusViewWrapperImpl = ({ + setStatus, + clearStatus, + onOpenChange, + ...snapshot +}: SetStatusViewSnapshot & SetStatusViewActions): JSX.Element => { + const vm = useMockedViewModel(snapshot, { + setStatus, + clearStatus, + onOpenChange, + }); + return ; +}; + +const SetStatusViewWrapper = withViewDocs(SetStatusViewWrapperImpl, SetStatusView); + +const meta = { + title: "Status/SetStatusView", + component: SetStatusViewWrapper, + tags: ["autodocs"], + args: { + setStatus: fn(), + clearStatus: fn(), + onOpenChange: fn(), + open: false, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const NoStatus: Story = {}; + +export const WithStatus: Story = { + args: { + userStatus: { emoji: "🦩", text: "Flamboyant" }, + }, +}; + +// Rules needed for any story where the menu is open. +const MENU_OPEN_A11Y_RULES = [ + { + // Menu contains a header which is invalid + id: "aria-required-children", + enabled: false, + }, + { + // Menu pops open by default + id: "aria-hidden-focus", + enabled: false, + }, +]; + +export const Open: Story = { + args: { + open: true, + }, + parameters: { + a11y: { + config: { + rules: MENU_OPEN_A11Y_RULES, + }, + }, + }, + tags: ["!dev", "!autodocs"], +}; diff --git a/packages/shared-components/src/status/SetStatusView.tsx b/packages/shared-components/src/status/SetStatusView.tsx new file mode 100644 index 00000000000..9ef2ef5bdde --- /dev/null +++ b/packages/shared-components/src/status/SetStatusView.tsx @@ -0,0 +1,96 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + +import React, { type JSX } from "react"; +import { Menu, MenuItem, Text } from "@vector-im/compound-web"; +import { ReactionIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; + +import { _t, _td, type UserStatus } from ".."; +import { useViewModel, type ViewModel } from "../core/viewmodel"; +import { StatusButtonView } from "./StatusButtonView"; +import styles from "./SetStatusView.module.css"; + +const PRESET_STATUSES = [ + { emoji: "💬", textKey: _td("status|set_status|in_a_meeting") }, + { emoji: "💡", textKey: _td("status|set_status|focus_time") }, + { emoji: "🚙", textKey: _td("status|set_status|on_the_road") }, + { emoji: "☕️", textKey: _td("status|set_status|be_right_back") }, + { emoji: "🌴", textKey: _td("status|set_status|away") }, +]; + +export interface SetStatusViewSnapshot { + /** + * The current user status, or undefined if no status is set. + */ + userStatus?: UserStatus; + + /** + * Whether the status picker dropdown is open. + */ + open: boolean; +} + +export interface SetStatusViewActions { + /** + * When the user clicks the trigger to open or close the menu + * @param open Whether the menu should be open or closed + */ + onOpenChange: (open: boolean) => void; + + /** + * Called when the user selects a preset status from the dropdown. + */ + setStatus: (status: UserStatus) => void; + + /** + * Called when the user clears their current status. + */ + clearStatus: () => void; +} + +export type SetStatusViewModel = ViewModel; + +export type SetStatusViewProps = { + vm: SetStatusViewModel; +}; + +export function SetStatusView({ vm }: SetStatusViewProps): JSX.Element { + const { userStatus, open } = useViewModel(vm); + + const trigger = userStatus ? ( + + ) : ( + + ); + + return ( + + {PRESET_STATUSES.map((status) => ( + vm.setStatus({ emoji: status.emoji, text: _t(status.textKey) })} + hideChevron={true} + className={styles.menuItem} + > + {status.emoji} {_t(status.textKey)} + + ))} + + ); +} From c736ca3351d95d74dcfb8f156f58dde84ccf9046 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 18 Jun 2026 16:02:20 +0100 Subject: [PATCH 04/25] Change to use dropdown with custom trigger --- .../src/status/SetStatusView.module.css | 4 ++ .../src/status/SetStatusView.stories.tsx | 7 -- .../src/status/SetStatusView.tsx | 65 +++++++------------ .../src/status/StatusButtonView.module.css | 3 + .../src/status/StatusButtonView.tsx | 8 +-- 5 files changed, 34 insertions(+), 53 deletions(-) diff --git a/packages/shared-components/src/status/SetStatusView.module.css b/packages/shared-components/src/status/SetStatusView.module.css index 4796fbb0807..5573e3f6d04 100644 --- a/packages/shared-components/src/status/SetStatusView.module.css +++ b/packages/shared-components/src/status/SetStatusView.module.css @@ -5,6 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ +.setStatusDropdown { + width: 200px; +} + .setStatusTrigger { background: none; border: none; diff --git a/packages/shared-components/src/status/SetStatusView.stories.tsx b/packages/shared-components/src/status/SetStatusView.stories.tsx index 4df59cf1c39..f063da4d393 100644 --- a/packages/shared-components/src/status/SetStatusView.stories.tsx +++ b/packages/shared-components/src/status/SetStatusView.stories.tsx @@ -16,13 +16,11 @@ import { withViewDocs } from "../../.storybook/withViewDocs"; const SetStatusViewWrapperImpl = ({ setStatus, clearStatus, - onOpenChange, ...snapshot }: SetStatusViewSnapshot & SetStatusViewActions): JSX.Element => { const vm = useMockedViewModel(snapshot, { setStatus, clearStatus, - onOpenChange, }); return ; }; @@ -36,8 +34,6 @@ const meta = { args: { setStatus: fn(), clearStatus: fn(), - onOpenChange: fn(), - open: false, }, } satisfies Meta; @@ -67,9 +63,6 @@ const MENU_OPEN_A11Y_RULES = [ ]; export const Open: Story = { - args: { - open: true, - }, parameters: { a11y: { config: { diff --git a/packages/shared-components/src/status/SetStatusView.tsx b/packages/shared-components/src/status/SetStatusView.tsx index 9ef2ef5bdde..7147777e175 100644 --- a/packages/shared-components/src/status/SetStatusView.tsx +++ b/packages/shared-components/src/status/SetStatusView.tsx @@ -6,7 +6,7 @@ Please see LICENSE files in the repository root for full details. */ import React, { type JSX } from "react"; -import { Menu, MenuItem, Text } from "@vector-im/compound-web"; +import { Dropdown, type DropdownTriggerProps, Text } from "@vector-im/compound-web"; import { ReactionIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; import { _t, _td, type UserStatus } from ".."; @@ -27,20 +27,9 @@ export interface SetStatusViewSnapshot { * The current user status, or undefined if no status is set. */ userStatus?: UserStatus; - - /** - * Whether the status picker dropdown is open. - */ - open: boolean; } export interface SetStatusViewActions { - /** - * When the user clicks the trigger to open or close the menu - * @param open Whether the menu should be open or closed - */ - onOpenChange: (open: boolean) => void; - /** * Called when the user selects a preset status from the dropdown. */ @@ -59,38 +48,30 @@ export type SetStatusViewProps = { }; export function SetStatusView({ vm }: SetStatusViewProps): JSX.Element { - const { userStatus, open } = useViewModel(vm); + const { userStatus } = useViewModel(vm); - const trigger = userStatus ? ( - - ) : ( - - ); + const renderTrigger = (props: DropdownTriggerProps): JSX.Element => { + const trigger = userStatus ? ( + + ) : ( + + ); + + return trigger; + }; return ( - - {PRESET_STATUSES.map((status) => ( - vm.setStatus({ emoji: status.emoji, text: _t(status.textKey) })} - hideChevron={true} - className={styles.menuItem} - > - {status.emoji} {_t(status.textKey)} - - ))} - + [s.textKey, `${s.emoji} ${_t(s.textKey)}`])} + label={null} + placeholder={null} + trigger={renderTrigger} + /> ); } diff --git a/packages/shared-components/src/status/StatusButtonView.module.css b/packages/shared-components/src/status/StatusButtonView.module.css index a52bae20874..c6b2f550798 100644 --- a/packages/shared-components/src/status/StatusButtonView.module.css +++ b/packages/shared-components/src/status/StatusButtonView.module.css @@ -7,8 +7,11 @@ .statusButton { border-radius: 30px; + border: 0px; background: var(--cpd-color-bg-subtle-secondary); display: inline-flex; + /* Disables filling to available width if we're in a flex container */ + align-self: flex-start; align-items: center; padding-inline-start: var(--cpd-space-2x); gap: 6px; diff --git a/packages/shared-components/src/status/StatusButtonView.tsx b/packages/shared-components/src/status/StatusButtonView.tsx index 42753aac8b1..d00a1502c0e 100644 --- a/packages/shared-components/src/status/StatusButtonView.tsx +++ b/packages/shared-components/src/status/StatusButtonView.tsx @@ -13,14 +13,14 @@ import { _t, type UserStatus } from ".."; import styles from "./StatusButtonView.module.css"; export const StatusButtonView = React.forwardRef< - HTMLDivElement, + HTMLButtonElement, { status: UserStatus; clearStatus: () => void; - } & React.HTMLAttributes + } & React.HTMLAttributes >(function StatusButtonView({ status, clearStatus, ...props }, ref): JSX.Element { return ( -
+
+ ); }); From c9bbcb4b796d11aeebce81209772992d91749657 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 23 Jun 2026 16:45:27 +0100 Subject: [PATCH 05/25] Show full set status component in user menu and styling tweaks --- .../components/views/spaces/SpacePanel.tsx | 2 +- .../src/viewmodels/menus/UserMenuViewModel.ts | 4 ++ .../src/menus/UserMenu/UserMenu.module.css | 2 +- .../src/menus/UserMenu/UserMenu.tsx | 68 +++++++++++++++---- .../src/status/SetStatusView.module.css | 6 +- .../src/status/SetStatusView.tsx | 9 +-- 6 files changed, 69 insertions(+), 22 deletions(-) diff --git a/apps/web/src/components/views/spaces/SpacePanel.tsx b/apps/web/src/components/views/spaces/SpacePanel.tsx index 58ec07cd988..1ba1fcb6246 100644 --- a/apps/web/src/components/views/spaces/SpacePanel.tsx +++ b/apps/web/src/components/views/spaces/SpacePanel.tsx @@ -465,7 +465,7 @@ const SpacePanel: React.FC = () => { ref={ref} aria-label={_t("common|spaces")} > - + implements UserMenuViewActions { + public readonly setStatusVm: SetStatusViewModel; private static computeSnapshot( client: MatrixClient, isPanelCollapsed: boolean, @@ -64,11 +66,13 @@ export class UserMenuViewModel extends BaseViewModel void; /** - * Called when the user clicks the button to clear theirt status. + * Called when the user clicks the button to clear their status. */ clearStatus: () => void; } export type UserMenuViewProps = { vm: ViewModel; + setStatusVm: SetStatusViewModel; /** * Class name for the wrapper */ className?: string; }; -export function UserMenuView({ vm, className }: UserMenuViewProps): JSX.Element { - const { userId, displayName, avatarUrl, expanded, open, manageAccountHref, actions, showAvatar, userStatus } = - useViewModel(vm); +export function UserMenuView({ vm, setStatusVm, className }: UserMenuViewProps): JSX.Element { + const { + userId, + displayName, + avatarUrl, + expanded, + open, + manageAccountHref, + actions, + showAvatar, + userStatus, + } = useViewModel(vm); const { translate: _t } = useI18n(); const trigger = ( )} @@ -185,7 +215,11 @@ export function UserMenuView({ vm, className }: UserMenuViewProps): JSX.Element
{actions.openHomePage && ( - + )} {actions.linkNewDevice && ( )} {actions.openSecurity && ( - + )} {actions.openFeedback && ( {expanded && ( - + {displayName} )} diff --git a/packages/shared-components/src/status/SetStatusView.module.css b/packages/shared-components/src/status/SetStatusView.module.css index 5573e3f6d04..4883e33e3eb 100644 --- a/packages/shared-components/src/status/SetStatusView.module.css +++ b/packages/shared-components/src/status/SetStatusView.module.css @@ -5,17 +5,15 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -.setStatusDropdown { - width: 200px; -} - .setStatusTrigger { background: none; border: none; + padding: 0; cursor: pointer; text-decoration: underline solid; display: flex; align-items: center; + width: fit-content; svg { width: 20px; diff --git a/packages/shared-components/src/status/SetStatusView.tsx b/packages/shared-components/src/status/SetStatusView.tsx index 7147777e175..8e4084d3f5f 100644 --- a/packages/shared-components/src/status/SetStatusView.tsx +++ b/packages/shared-components/src/status/SetStatusView.tsx @@ -50,10 +50,12 @@ export type SetStatusViewProps = { export function SetStatusView({ vm }: SetStatusViewProps): JSX.Element { const { userStatus } = useViewModel(vm); + if (userStatus) { + return ; + } + const renderTrigger = (props: DropdownTriggerProps): JSX.Element => { - const trigger = userStatus ? ( - - ) : ( + const trigger = ( )} @@ -215,11 +186,7 @@ export function UserMenuView({ vm, setStatusVm, className }: UserMenuViewProps):
{actions.openHomePage && ( - + )} {actions.linkNewDevice && ( )} {actions.openSecurity && ( - + )} {actions.openFeedback && ( {expanded && ( - + {displayName} )} From fb102139174cb12140bbcd9e30e914ea51750bd1 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 24 Jun 2026 10:49:35 +0100 Subject: [PATCH 09/25] Fix type import --- apps/web/src/viewmodels/menus/UserMenuViewModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/viewmodels/menus/UserMenuViewModel.ts b/apps/web/src/viewmodels/menus/UserMenuViewModel.ts index eaa7bdd3436..ae6e3c57af4 100644 --- a/apps/web/src/viewmodels/menus/UserMenuViewModel.ts +++ b/apps/web/src/viewmodels/menus/UserMenuViewModel.ts @@ -20,7 +20,7 @@ import { getHomePageUrl } from "../../utils/pages"; import SdkConfig from "../../SdkConfig"; import type { MatrixClient } from "matrix-js-sdk/src/matrix"; import { clearUserStatus } from "../../utils/userStatus"; -import { SetStatusViewModel, UserMenuSetStatusViewModel } from "../status/SetStatusViewModel"; +import { type SetStatusViewModel, UserMenuSetStatusViewModel } from "../status/SetStatusViewModel"; // Matches maximum size of an avatar in the UserMenu const AVATAR_PX = 88; From 32faa0a671d906cbc3ff2960709cb3811d7d6146 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 24 Jun 2026 10:58:27 +0100 Subject: [PATCH 10/25] Pass set status viewmodel in story --- .../src/menus/UserMenu/UserMenu.stories.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/shared-components/src/menus/UserMenu/UserMenu.stories.tsx b/packages/shared-components/src/menus/UserMenu/UserMenu.stories.tsx index 0e1900d0e02..0e9472a8b00 100644 --- a/packages/shared-components/src/menus/UserMenu/UserMenu.stories.tsx +++ b/packages/shared-components/src/menus/UserMenu/UserMenu.stories.tsx @@ -13,6 +13,7 @@ import { UserMenuView, type UserMenuViewSnapshot, type UserMenuViewActions } fro import avatarUrl from "../../../static/element.png"; import { useMockedViewModel } from "../../core/viewmodel"; import { withViewDocs } from "../../../.storybook/withViewDocs"; +import { type SetStatusViewActions, type SetStatusViewSnapshot } from "../.."; const UserMenuWrapperImpl = (snapshot: UserMenuViewSnapshot): JSX.Element => { const vm = useMockedViewModel(snapshot, { @@ -26,7 +27,16 @@ const UserMenuWrapperImpl = (snapshot: UserMenuViewSnapshot): JSX.Element => { openSettings: fn(), clearStatus: fn(), }); - return ; + + const setStatusVm = useMockedViewModel( + {}, + { + setStatus: fn(), + clearStatus: fn(), + }, + ); + + return ; }; const UserMenuWrapper = withViewDocs(UserMenuWrapperImpl, UserMenuView); From 0a26480d709abc04583021d6bebe8e48f7abbb1b Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 24 Jun 2026 13:53:01 +0100 Subject: [PATCH 11/25] Bump compound-web for customisable dropdown --- pnpm-lock.yaml | 41 +++++++++++++++++++++++++++++++++++++---- pnpm-workspace.yaml | 2 +- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ccfc1731035..fe61ae6b75a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -215,8 +215,8 @@ catalogs: specifier: 10.2.1 version: 10.2.1 '@vector-im/compound-web': - specifier: 9.4.1 - version: 9.4.1 + specifier: 9.6.0 + version: 9.6.0 '@vitejs/plugin-react': specifier: 6.0.2 version: 6.0.2 @@ -542,7 +542,7 @@ importers: version: 10.2.1(@types/react@19.2.14)(react@19.2.7) '@vector-im/compound-web': specifier: 'catalog:' - version: 9.4.1(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.2.1(@types/react@19.2.14)(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.6.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.2.1(@types/react@19.2.14)(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@vector-im/matrix-wysiwyg': specifier: 2.40.0 version: 2.40.0(patch_hash=7bdf6150f2905bc2f055a6bcaa7b9d78fa7ffde82e800bcc454ac7b0096bd65e)(react@19.2.7) @@ -1546,7 +1546,7 @@ importers: version: 8.61.0(eslint@8.57.1)(typescript@6.0.3) '@vector-im/compound-web': specifier: 'catalog:' - version: 9.4.1(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.2.1(@types/react@19.2.14)(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 9.6.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.2.1(@types/react@19.2.14)(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@vitejs/plugin-react': specifier: 'catalog:' version: 6.0.2(vite@8.0.16(@types/node@25.9.3)(esbuild@0.27.4)(jiti@2.7.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.8.4)) @@ -6548,6 +6548,18 @@ packages: '@types/react': optional: true + '@vector-im/compound-web@9.6.0': + resolution: {integrity: sha512-J0bf99N3+C3/BasdBXNj1m3HZoGt7ttV5BcSmeNIPni5aONbrFVAYkjOmKGeEJViskghDDNcAXZQooI2SSsB+g==} + peerDependencies: + '@fontsource/inconsolata': ^5 + '@fontsource/inter': ^5 + '@types/react': ^19.2.10 + '@vector-im/compound-design-tokens': '>=1.6.1 <11.0.0' + react: ^18 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@vector-im/matrix-wysiwyg@2.40.0': resolution: {integrity: sha512-8LRFLs5PEKYs4lOL7aJ4lL/hGCrvEvOYkCR3JggXYXDVMtX4LmfdlKYucSAe98pCmqAAbLRvlRcR1bTOYvM8ug==} peerDependencies: @@ -19533,6 +19545,27 @@ snapshots: - '@types/react-dom' - react-dom + '@vector-im/compound-web@9.6.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.2.8)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(@vector-im/compound-design-tokens@10.2.1(@types/react@19.2.14)(react@19.2.7))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@floating-ui/react': 0.27.17(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@fontsource/inconsolata': 5.2.8 + '@fontsource/inter': 5.2.8 + '@radix-ui/react-context-menu': 2.2.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-form': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-progress': 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-separator': 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@19.2.7) + '@vector-im/compound-design-tokens': 10.2.1(@types/react@19.2.14)(react@19.2.7) + classnames: 2.5.1 + react: 19.2.7 + vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.14 + transitivePeerDependencies: + - '@types/react-dom' + - react-dom + '@vector-im/matrix-wysiwyg@2.40.0(patch_hash=7bdf6150f2905bc2f055a6bcaa7b9d78fa7ffde82e800bcc454ac7b0096bd65e)(react@19.2.7)': dependencies: react: 19.2.7 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b27a8d7e38f..92ec1afdb45 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -23,7 +23,7 @@ catalog: "playwright-core": 1.60.0 # Compound "@vector-im/compound-design-tokens": 10.2.1 - "@vector-im/compound-web": 9.4.1 + "@vector-im/compound-web": 9.6.0 # i18n matrix-web-i18n: 3.6.0 # fonts From 968267acdb5397a53303c67ca6e5d98e69696713 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 24 Jun 2026 14:51:40 +0100 Subject: [PATCH 12/25] Snapshot --- .../__snapshots__/UserMenu.test.tsx.snap | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) diff --git a/packages/shared-components/src/menus/UserMenu/__snapshots__/UserMenu.test.tsx.snap b/packages/shared-components/src/menus/UserMenu/__snapshots__/UserMenu.test.tsx.snap index c23fe6619b4..dd3a89745d9 100644 --- a/packages/shared-components/src/menus/UserMenu/__snapshots__/UserMenu.test.tsx.snap +++ b/packages/shared-components/src/menus/UserMenu/__snapshots__/UserMenu.test.tsx.snap @@ -293,6 +293,88 @@ exports[`UserMenu > renders a menu without an avatar 1`] = ` > Sally Sanderson +
+ +
+
    +
  • + 💬 In a meeting + +
  • +
  • + 💡 Focus Time + +
  • +
  • + 🚙 On the road + +
  • +
  • + ☕️ Be right back + +
  • +
  • + 🌴 Away + +
  • +
+
+
renders an open menu 1`] = ` > Sally Sanderson with a longer name +
+ +
+
    +
  • + 💬 In a meeting + +
  • +
  • + 💡 Focus Time + +
  • +
  • + 🚙 On the road + +
  • +
  • + ☕️ Be right back + +
  • +
  • + 🌴 Away + +
  • +
+
+
Date: Wed, 24 Jun 2026 15:01:25 +0100 Subject: [PATCH 13/25] Add explicit aria label as combobox role things don't just inherit from their inner text --- packages/shared-components/src/status/SetStatusView.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/shared-components/src/status/SetStatusView.tsx b/packages/shared-components/src/status/SetStatusView.tsx index 6c48b18fa0f..ad1e6949183 100644 --- a/packages/shared-components/src/status/SetStatusView.tsx +++ b/packages/shared-components/src/status/SetStatusView.tsx @@ -63,7 +63,11 @@ export function SetStatusView({ vm }: SetStatusViewProps): JSX.Element { const renderTrigger = (props: DropdownTriggerProps): JSX.Element => { const trigger = ( - +
); }); From eb6d5a2f1ba51e25e182be8e8f71e8dc5c44790c Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 24 Jun 2026 17:15:57 +0100 Subject: [PATCH 17/25] Screenshots --- .../no-status-auto.png | Bin 0 -> 19439 bytes .../SetStatusView.stories.tsx/open-auto.png | Bin 0 -> 19439 bytes .../with-status-auto.png | Bin 0 -> 19488 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 packages/shared-components/__vis__/linux/__baselines__/status/SetStatusView.stories.tsx/no-status-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/status/SetStatusView.stories.tsx/open-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/status/SetStatusView.stories.tsx/with-status-auto.png diff --git a/packages/shared-components/__vis__/linux/__baselines__/status/SetStatusView.stories.tsx/no-status-auto.png b/packages/shared-components/__vis__/linux/__baselines__/status/SetStatusView.stories.tsx/no-status-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..36e5baf191577697160cc9719681f615d85e932f GIT binary patch literal 19439 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 E1D&1nyZ)c|^qvgiQAqX;hsolaA2r@#5 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 literal 0 HcmV?d00001 From 113e5d7b965d06efd440ba1dc88d676fe91fef8d Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 25 Jun 2026 13:29:27 +0100 Subject: [PATCH 18/25] Disable in settings if feature flag off --- .../web/src/components/views/settings/UserProfileSettings.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/views/settings/UserProfileSettings.tsx b/apps/web/src/components/views/settings/UserProfileSettings.tsx index b5dfff14995..230b17e2e2c 100644 --- a/apps/web/src/components/views/settings/UserProfileSettings.tsx +++ b/apps/web/src/components/views/settings/UserProfileSettings.tsx @@ -29,6 +29,7 @@ import Modal from "../../../Modal"; import defaultDispatcher from "../../../dispatcher/dispatcher"; import { SettingsSection } from "./shared/SettingsSection.tsx"; import { SetStatusViewModel } from "../../../viewmodels/status/SetStatusViewModel.ts"; +import SettingsStore from "../../../settings/SettingsStore.ts"; const SpinnerToast: React.FC<{ children?: ReactNode }> = ({ children }) => ( <> @@ -133,6 +134,7 @@ const UserProfileSettings: React.FC = ({ })(); }, [client]); + const userStatusEnabled = SettingsStore.getValue("feature_user_status"); const setStatusVM = useCreateAutoDisposedViewModel(() => new SetStatusViewModel({ client })); const onAvatarRemove = useCallback(async () => { @@ -235,7 +237,7 @@ const UserProfileSettings: React.FC = ({ {_t("settings|general|display_name_error")} )} - + {userStatusEnabled && } {avatarError && ( From 79f70d65c8ab57b5a62c4d152cf91d970b71f905 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 25 Jun 2026 17:57:45 +0100 Subject: [PATCH 19/25] Snapshot --- .../menus/__snapshots__/UserMenuViewModel-test.ts.snap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/web/test/viewmodels/menus/__snapshots__/UserMenuViewModel-test.ts.snap b/apps/web/test/viewmodels/menus/__snapshots__/UserMenuViewModel-test.ts.snap index 979c0c59946..b7f34866147 100644 --- a/apps/web/test/viewmodels/menus/__snapshots__/UserMenuViewModel-test.ts.snap +++ b/apps/web/test/viewmodels/menus/__snapshots__/UserMenuViewModel-test.ts.snap @@ -17,6 +17,7 @@ exports[`UserMenuViewModel should generate a menu options for a guest 1`] = ` "manageAccountHref": undefined, "open": true, "showAvatar": false, + "showUserStatus": false, "userId": "@alice:domain", "userStatus": undefined, } @@ -39,6 +40,7 @@ exports[`UserMenuViewModel should generate a menu options for a logged in client "manageAccountHref": undefined, "open": true, "showAvatar": true, + "showUserStatus": false, "userId": "@alice:domain", "userStatus": undefined, } From 244b4eced299454a63a58cf9ddc6edae2a740b7a Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 25 Jun 2026 18:37:58 +0100 Subject: [PATCH 20/25] Snapshot again --- .../views/spaces/__snapshots__/SpacePanel-test.tsx.snap | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/test/unit-tests/components/views/spaces/__snapshots__/SpacePanel-test.tsx.snap b/apps/web/test/unit-tests/components/views/spaces/__snapshots__/SpacePanel-test.tsx.snap index 058e5a85de9..1e336bfa163 100644 --- a/apps/web/test/unit-tests/components/views/spaces/__snapshots__/SpacePanel-test.tsx.snap +++ b/apps/web/test/unit-tests/components/views/spaces/__snapshots__/SpacePanel-test.tsx.snap @@ -7,19 +7,19 @@ exports[` should show all activated MetaSpaces in the correct orde class="mx_SpacePanel collapsed newUi" >