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 7c08d70dff..eca6f19cc5 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 @@ -345,7 +345,14 @@ exports[`UserMenu > renders a menu without an avatar 1`] = ` role="option" tabindex="0" > - 💬 In a meeting + + 💬 + + + In a meeting +
  • renders a menu without an avatar 1`] = ` role="option" tabindex="0" > - 💡 Focus Time + + 💡 + + + Focus Time +
  • renders a menu without an avatar 1`] = ` role="option" tabindex="0" > - 🚙 On the road + + 🚙 + + + On the road +
  • renders a menu without an avatar 1`] = ` role="option" tabindex="0" > - ☕️ Be right back + + ☕️ + + + Be right back +
  • renders a menu without an avatar 1`] = ` role="option" tabindex="0" > - 🌴 Away + + 🌴 + + + Away +
  • renders a menu without an avatar 1`] = ` role="option" tabindex="0" > - ✍️ Custom… + + ✍️ + + + Custom… +
  • @@ -779,7 +821,14 @@ exports[`UserMenu > renders an open menu 1`] = ` role="option" tabindex="0" > - 💬 In a meeting + + 💬 + + + In a meeting +
  • renders an open menu 1`] = ` role="option" tabindex="0" > - 💡 Focus Time + + 💡 + + + Focus Time +
  • renders an open menu 1`] = ` role="option" tabindex="0" > - 🚙 On the road + + 🚙 + + + On the road +
  • renders an open menu 1`] = ` role="option" tabindex="0" > - ☕️ Be right back + + ☕️ + + + Be right back +
  • renders an open menu 1`] = ` role="option" tabindex="0" > - 🌴 Away + + 🌴 + + + Away +
  • renders an open menu 1`] = ` role="option" tabindex="0" > - ✍️ Custom… + + ✍️ + + + Custom… +
  • diff --git a/packages/shared-components/src/status/SetStatusView.module.css b/packages/shared-components/src/status/SetStatusView.module.css index c7342482b4..7c830a404c 100644 --- a/packages/shared-components/src/status/SetStatusView.module.css +++ b/packages/shared-components/src/status/SetStatusView.module.css @@ -30,3 +30,7 @@ Please see LICENSE files in the repository root for full details. font-weight: var(--cpd-font-weight-medium); font-size: var(--cpd-font-size-body-md); } + +.dropdownEmoji { + margin-right: var(--cpd-space-1-5x); +} diff --git a/packages/shared-components/src/status/SetStatusView.test.tsx b/packages/shared-components/src/status/SetStatusView.test.tsx index 734a2df830..ef98343b36 100644 --- a/packages/shared-components/src/status/SetStatusView.test.tsx +++ b/packages/shared-components/src/status/SetStatusView.test.tsx @@ -24,7 +24,7 @@ class SetStatusViewModel extends MockViewModel implements */ async function openCustomEditor(): Promise { await userEvent.click(screen.getByRole("combobox")); - await userEvent.click(screen.getByRole("option", { name: "✍️ Custom…" })); + await userEvent.click(screen.getByRole("option", { name: "✍️Custom…" })); } describe("SetStatusView", () => { diff --git a/packages/shared-components/src/status/SetStatusView.tsx b/packages/shared-components/src/status/SetStatusView.tsx index 43e690bada..d87b84dc09 100644 --- a/packages/shared-components/src/status/SetStatusView.tsx +++ b/packages/shared-components/src/status/SetStatusView.tsx @@ -5,7 +5,7 @@ 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. */ -import React, { type JSX, useState } from "react"; +import React, { type JSX, useState, useCallback } from "react"; import { Dropdown, type DropdownTriggerProps, Link, Text } from "@vector-im/compound-web"; import { ReactionIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; @@ -15,16 +15,17 @@ import { StatusPillView } from "./StatusPillView"; import { CustomStatusView } from "./CustomStatusView"; 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") }, -]; - -// Sentinel value used to distinguish the "Custom…" dropdown entry from a preset. -const CUSTOM_STATUS_VALUE = "custom"; +const STATUSES = { + in_a_meeting: { emoji: "💬", textKey: _td("status|set_status|in_a_meeting") }, + focus_time: { emoji: "💡", textKey: _td("status|set_status|focus_time") }, + on_the_road: { emoji: "🚙", textKey: _td("status|set_status|on_the_road") }, + be_right_back: { emoji: "☕️", textKey: _td("status|set_status|be_right_back") }, + away: { emoji: "🌴", textKey: _td("status|set_status|away") }, + custom: { emoji: "✍️", textKey: _td("status|set_status|custom") }, +}; +type StatusValue = keyof typeof STATUSES; +// No need to keep recompyuting this, it won't change +const STATUS_KEYS = Object.keys(STATUSES) as StatusValue[]; export interface SetStatusViewSnapshot { /** @@ -58,10 +59,25 @@ export type SetStatusViewProps = { vm: SetStatusViewModel; }; +function StatusOption({ value }: { value: StatusValue }): React.ReactNode { + return ( + <> + {STATUSES[value].emoji} + {_t(STATUSES[value].textKey)} + + ); +} + export function SetStatusView({ vm }: SetStatusViewProps): JSX.Element { const { userStatus } = useViewModel(vm); const [customMode, setCustomMode] = useState(false); + const renderItem = useCallback((value: StatusValue | null): React.ReactNode => { + if (value === null) return null; + + return ; + }, []); + if (userStatus) { return ; } @@ -97,13 +113,13 @@ export function SetStatusView({ vm }: SetStatusViewProps): JSX.Element { return trigger; }; - const onValueChange = (value: string): void => { - if (value === CUSTOM_STATUS_VALUE) { + const onValueChange = (value: StatusValue): void => { + if (value === "custom") { setCustomMode(true); return; } - const status = PRESET_STATUSES.find((s) => s.textKey === value); + const status = STATUSES[value]; if (!status) { return; @@ -115,20 +131,15 @@ export function SetStatusView({ vm }: SetStatusViewProps): JSX.Element { }); }; - const dropdownValues: Array<[string, string]> = [ - ...PRESET_STATUSES.map((s): [string, string] => [s.textKey, `${s.emoji} ${_t(s.textKey)}`]), - [CUSTOM_STATUS_VALUE, `✍️ ${_t("status|set_status|custom")}`], - ]; - return vm.onSetStatusClick ? ( renderTrigger({ onClick: vm.onSetStatusClick }) ) : ( - + values={STATUS_KEYS} label={null} - placeholder={null} trigger={renderTrigger} onValueChange={onValueChange} + renderItem={renderItem} /> ); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1458ba23f5..c1d8f0b4e1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -222,8 +222,8 @@ catalogs: specifier: 10.2.1 version: 10.2.1 '@vector-im/compound-web': - specifier: 9.9.0 - version: 9.9.0 + specifier: 10.0.0 + version: 10.0.0 '@vitejs/plugin-react': specifier: 6.0.5 version: 6.0.5 @@ -557,7 +557,7 @@ importers: version: 10.2.1(@types/react@19.2.18)(react@19.2.8) '@vector-im/compound-web': specifier: 'catalog:' - version: 9.9.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.3.0)(@types/react-dom@19.2.3)(@types/react@19.2.18)(@vector-im/compound-design-tokens@10.2.1)(react-dom@19.2.8)(react@19.2.8) + version: 10.0.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.3.0)(@types/react-dom@19.2.3)(@types/react@19.2.18)(@vector-im/compound-design-tokens@10.2.1)(react-dom@19.2.8)(react@19.2.8) '@vector-im/matrix-wysiwyg': specifier: 2.40.0 version: 2.40.0(patch_hash=7bdf6150f2905bc2f055a6bcaa7b9d78fa7ffde82e800bcc454ac7b0096bd65e)(react@19.2.8) @@ -1501,7 +1501,7 @@ importers: version: typescript@7.0.2 '@vector-im/compound-web': specifier: 'catalog:' - version: 9.9.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.3.0)(@types/react-dom@19.2.3)(@types/react@19.2.18)(@vector-im/compound-design-tokens@10.2.1)(react-dom@19.2.8)(react@19.2.8) + version: 10.0.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.3.0)(@types/react-dom@19.2.3)(@types/react@19.2.18)(@vector-im/compound-design-tokens@10.2.1)(react-dom@19.2.8)(react@19.2.8) '@vitejs/plugin-react': specifier: 'catalog:' version: 6.0.5(vite@8.2.0) @@ -6572,6 +6572,18 @@ packages: react: optional: true + '@vector-im/compound-web@10.0.0': + resolution: {integrity: sha512-Gdxi+MjOTds1QwXw1bFy6NQnTgynNeSCmmOS7Q9c7pogaZQlpEYaEu6mPXww6T6TwBRgK7MNDXvF3k5C8h653Q==} + 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/compound-web@9.9.0': resolution: {integrity: sha512-GjAEhzB/mt9MA7Y1ySe6/AlW26vodDu4hvTi1ThB5rTrt4KAiHMpvk+T56KV5Lwo0VvBxAyHMtm/IY5tkyH12A==} peerDependencies: @@ -19112,6 +19124,28 @@ snapshots: '@types/react': 19.2.18 react: 19.2.8 + '@vector-im/compound-web@10.0.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.3.0)(@types/react-dom@19.2.3)(@types/react@19.2.18)(@vector-im/compound-design-tokens@10.2.1)(react-dom@19.2.8)(react@19.2.8)': + dependencies: + '@floating-ui/react': 0.27.17(react-dom@19.2.8)(react@19.2.8) + '@floating-ui/react-dom': 2.1.9(react-dom@19.2.8)(react@19.2.8) + '@fontsource/inconsolata': 5.2.8 + '@fontsource/inter': 5.3.0 + '@radix-ui/react-context-menu': 2.2.16(@types/react-dom@19.2.3)(@types/react@19.2.18)(react-dom@19.2.8)(react@19.2.8) + '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3)(@types/react@19.2.18)(react-dom@19.2.8)(react@19.2.8) + '@radix-ui/react-form': 0.1.8(@types/react-dom@19.2.3)(@types/react@19.2.18)(react-dom@19.2.8)(react@19.2.8) + '@radix-ui/react-progress': 1.1.8(@types/react-dom@19.2.3)(@types/react@19.2.18)(react-dom@19.2.8)(react@19.2.8) + '@radix-ui/react-separator': 1.1.8(@types/react-dom@19.2.3)(@types/react@19.2.18)(react-dom@19.2.8)(react@19.2.8) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.8) + '@vector-im/compound-design-tokens': 10.2.1(@types/react@19.2.18)(react@19.2.8) + classnames: 2.5.1 + react: 19.2.8 + vaul: 1.1.2(@types/react-dom@19.2.3)(@types/react@19.2.18)(react-dom@19.2.8)(react@19.2.8) + optionalDependencies: + '@types/react': 19.2.18 + transitivePeerDependencies: + - '@types/react-dom' + - react-dom + '@vector-im/compound-web@9.9.0(@fontsource/inconsolata@5.2.8)(@fontsource/inter@5.3.0)(@types/react-dom@19.2.3)(@types/react@19.2.18)(@vector-im/compound-design-tokens@10.2.1)(react-dom@19.2.8)(react@19.2.8)': dependencies: '@floating-ui/react': 0.27.17(react-dom@19.2.8)(react@19.2.8) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4acfe73b56..3d4b5bb72c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -27,7 +27,7 @@ catalog: "playwright-core": 1.61.1 # Compound "@vector-im/compound-design-tokens": 10.2.1 - "@vector-im/compound-web": 9.9.0 + "@vector-im/compound-web": 10.0.0 # i18n matrix-web-i18n: 3.6.0 # emojibase