diff --git a/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-custom-sections.spec.ts b/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-custom-sections.spec.ts index 742b611faf..e40b1a6c0d 100644 --- a/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-custom-sections.spec.ts +++ b/apps/web/playwright/e2e/left-panel/room-list-panel/room-list-custom-sections.spec.ts @@ -258,6 +258,47 @@ test.describe("Room list custom sections", () => { }); }); + test.describe("Collapse and expand all sections", () => { + test("should collapse all sections when 'Collapse all sections' button is clicked", async ({ page, app }) => { + await app.client.createRoom({ name: "my room" }); + await createCustomSection(page, "Work"); + + const roomList = getRoomList(page); + const header = getRoomListHeader(page); + + await expect(getSectionHeader(page, "Chats")).toBeVisible(); + await expect(getSectionHeader(page, "Work")).toBeVisible(); + + const collapseButton = header.getByRole("button", { name: "Collapse all sections" }); + await expect(collapseButton).toBeVisible(); + + await expect(roomList.getByRole("row", { name: "Open room my room" })).toBeVisible(); + + await collapseButton.click(); + + await expect(getSectionHeader(page, "Chats")).toHaveAttribute("aria-expanded", "false"); + await expect(getSectionHeader(page, "Work")).toHaveAttribute("aria-expanded", "false"); + }); + + test("should expand all sections when 'Expand all sections' button is clicked", async ({ page, app }) => { + await app.client.createRoom({ name: "my room" }); + await createCustomSection(page, "Work"); + + const roomList = getRoomList(page); + const header = getRoomListHeader(page); + + await expect(getSectionHeader(page, "Chats")).toBeVisible(); + + await header.getByRole("button", { name: "Collapse all sections" }).click(); + await expect(roomList.getByRole("row", { name: "Open room my room" })).not.toBeVisible(); + + await header.getByRole("button", { name: "Expand all sections" }).click(); + + await expect(getSectionHeader(page, "Chats")).toHaveAttribute("aria-expanded", "true"); + await expect(getSectionHeader(page, "Work")).toHaveAttribute("aria-expanded", "true"); + }); + }); + test.describe("Adding a room to a custom section", () => { test("should add a room to a custom section via the More Options menu", async ({ page, app }) => { await app.client.createRoom({ name: "my room" }); diff --git a/apps/web/src/dispatcher/actions.ts b/apps/web/src/dispatcher/actions.ts index 49a3ce8868..ad19d1c7d8 100644 --- a/apps/web/src/dispatcher/actions.ts +++ b/apps/web/src/dispatcher/actions.ts @@ -403,4 +403,20 @@ export enum Action { * or keyboard event). */ UserActivity = "user_activity", + + /** + * Fired to request collapsing all room list sections. + */ + RoomListCollapseAllSections = "room_list_collapse_all_sections", + + /** + * Fired to request expanding all room list sections. + */ + RoomListExpandAllSections = "room_list_expand_all_sections", + + /** + * Fired to report the collapse state of a given room list section. + * Payload: {@link RoomListSectionsCollapseStateChangedPayload} + */ + RoomListSectionsCollapseStateChanged = "room_list_sections_collapse_state_changed", } diff --git a/apps/web/src/dispatcher/payloads/RoomListSectionsCollapseStateChangedPayload.ts b/apps/web/src/dispatcher/payloads/RoomListSectionsCollapseStateChangedPayload.ts new file mode 100644 index 0000000000..d3dbe21cf4 --- /dev/null +++ b/apps/web/src/dispatcher/payloads/RoomListSectionsCollapseStateChangedPayload.ts @@ -0,0 +1,20 @@ +/* + * 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 CollapseSectionsOption } from "@element-hq/web-shared-components"; + +import { type ActionPayload } from "../payloads"; +import { type Action } from "../actions"; + +export interface RoomListSectionsCollapseStateChangedPayload extends ActionPayload { + action: Action.RoomListSectionsCollapseStateChanged; + /** + * The new collapse state for the room list sections. + * If undefined, the feature is disabled. + */ + collapseSections?: CollapseSectionsOption; +} diff --git a/apps/web/src/viewmodels/room-list/RoomListHeaderViewModel.ts b/apps/web/src/viewmodels/room-list/RoomListHeaderViewModel.ts index 55626cb252..2e48532078 100644 --- a/apps/web/src/viewmodels/room-list/RoomListHeaderViewModel.ts +++ b/apps/web/src/viewmodels/room-list/RoomListHeaderViewModel.ts @@ -26,6 +26,7 @@ import { showSpaceSettings, } from "../../utils/space"; import type { ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload"; +import type { RoomListSectionsCollapseStateChangedPayload } from "../../dispatcher/payloads/RoomListSectionsCollapseStateChangedPayload"; import SettingsStore from "../../settings/SettingsStore"; import RoomListStoreV3 from "../../stores/room-list-v3/RoomListStoreV3"; import { SortingAlgorithm } from "../../stores/room-list-v3/skip-list/sorters"; @@ -77,6 +78,10 @@ export class RoomListHeaderViewModel if (this.activeSpace) { this.disposables.trackListener(this.activeSpace, RoomEvent.Name, this.onSpaceNameChange); } + + // Listen for section collapse state changes from RoomListViewModel + const dispatcherRef = defaultDispatcher.register(this.onDispatch); + this.disposables.track(() => defaultDispatcher.unregister(dispatcherRef)); } /** @@ -203,6 +208,23 @@ export class RoomListHeaderViewModel public createSection = (): void => { RoomListStoreV3.instance.createSection(); }; + + public collapseOrExpandSections = (): void => { + const action = + this.snapshot.current.collapseSections === "expand" + ? Action.RoomListExpandAllSections + : Action.RoomListCollapseAllSections; + defaultDispatcher.fire(action); + }; + + private readonly onDispatch = (payload: { action: string }): void => { + if (payload.action === Action.RoomListSectionsCollapseStateChanged) { + const { collapseSections } = payload as RoomListSectionsCollapseStateChangedPayload; + this.snapshot.merge({ + collapseSections: collapseSections && (collapseSections === "collapse" ? "expand" : "collapse"), + }); + } + }; } /** * Get the initial snapshot for the RoomListHeaderViewModel. diff --git a/apps/web/src/viewmodels/room-list/RoomListViewModel.ts b/apps/web/src/viewmodels/room-list/RoomListViewModel.ts index e711e340b0..6e28e61ed7 100644 --- a/apps/web/src/viewmodels/room-list/RoomListViewModel.ts +++ b/apps/web/src/viewmodels/room-list/RoomListViewModel.ts @@ -21,6 +21,7 @@ import { Action } from "../../dispatcher/actions"; import dispatcher from "../../dispatcher/dispatcher"; import { type ViewRoomDeltaPayload } from "../../dispatcher/payloads/ViewRoomDeltaPayload"; import { type ViewRoomPayload } from "../../dispatcher/payloads/ViewRoomPayload"; +import { type RoomListSectionsCollapseStateChangedPayload } from "../../dispatcher/payloads/RoomListSectionsCollapseStateChangedPayload"; import SpaceStore from "../../stores/spaces/SpaceStore"; import RoomListStoreV3, { CHATS_TAG, @@ -325,9 +326,24 @@ export class RoomListViewModel // Handle keyboard navigation shortcuts (Alt+ArrowUp/Down) // This was previously handled by useRoomListNavigation hook this.handleViewRoomDelta(payload as ViewRoomDeltaPayload); + } else if (payload.action === Action.RoomListCollapseAllSections) { + this.onCollapseAllSections(false); + } else if (payload.action === Action.RoomListExpandAllSections) { + this.onCollapseAllSections(true); } }; + /** + * Handles the collapse or expansion of all sections in the room list. + * @param expand - Whether to expand or collapse all sections + */ + private onCollapseAllSections(expand: boolean): void { + for (const sectionHeaderVM of this.roomSectionHeaderViewModels.values()) { + sectionHeaderVM.isExpanded = expand; + } + this.updateRoomListData(); + } + /** * Handle keyboard navigation shortcuts (Alt+ArrowUp/Down) to move between rooms. * Supports both regular navigation and unread-only navigation. @@ -581,6 +597,32 @@ export class RoomListViewModel sections: keepIfSame(previousSections, viewSections), isFlatList, }); + + this.notifyCollapseState(isFlatList); + } + + /** + * Notify the dispatcher about the current collapse state of the room list sections. + * @param isFlatList - Whether the room list is currently displayed as a flat list + */ + private notifyCollapseState(isFlatList: boolean): void { + // Hide collapse/expand all button if sections are disabled or if it's a flat list + if (!SettingsStore.getValue("feature_room_list_sections") || isFlatList) { + dispatcher.dispatch({ + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: undefined, + }); + return; + } + + // Determine if all sections are currently collapsed + const allCollapsed = this.snapshot.current.sections.every( + ({ id }) => !(this.roomSectionHeaderViewModels.get(id)?.isExpanded ?? true), + ); + dispatcher.dispatch({ + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: allCollapsed ? "collapse" : "expand", + }); } public createChatRoom = (): void => { diff --git a/apps/web/test/viewmodels/room-list/RoomListHeaderViewModel-test.ts b/apps/web/test/viewmodels/room-list/RoomListHeaderViewModel-test.ts index 53f76d884f..e4afd6e7b8 100644 --- a/apps/web/test/viewmodels/room-list/RoomListHeaderViewModel-test.ts +++ b/apps/web/test/viewmodels/room-list/RoomListHeaderViewModel-test.ts @@ -323,6 +323,89 @@ describe("RoomListHeaderViewModel", () => { expect(createSectionSpy).toHaveBeenCalled(); }); + describe("collapseOrExpandSections", () => { + it("should dispatch RoomListCollapseAllSections when collapseSections is not 'expand'", () => { + const fireSpy = jest.spyOn(defaultDispatcher, "fire"); + vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance }); + + vm.collapseOrExpandSections(); + + expect(fireSpy).toHaveBeenCalledWith(Action.RoomListCollapseAllSections); + }); + + it("should dispatch RoomListExpandAllSections when collapseSections is 'expand'", () => { + const fireSpy = jest.spyOn(defaultDispatcher, "fire"); + vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance }); + + // Drive the VM into the "expand" state by simulating all sections collapsed + defaultDispatcher.dispatch( + { + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: "collapse", + }, + true, + ); + expect(vm.getSnapshot().collapseSections).toBe("expand"); + vm.collapseOrExpandSections(); + + expect(fireSpy).toHaveBeenCalledWith(Action.RoomListExpandAllSections); + }); + }); + + describe("RoomListSectionsCollapseStateChanged handling", () => { + it("should set collapseSections to 'expand' when collapseSections is collapse", () => { + vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance }); + + defaultDispatcher.dispatch( + { + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: "collapse", + }, + true, + ); + + expect(vm.getSnapshot().collapseSections).toBe("expand"); + }); + + it("should set collapseSections to 'collapse' when collapseSections is expand", () => { + vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance }); + + defaultDispatcher.dispatch( + { + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: "expand", + }, + true, + ); + + expect(vm.getSnapshot().collapseSections).toBe("collapse"); + }); + + it("should set collapseSections to undefined when collapseSections is undefined", () => { + vm = new RoomListHeaderViewModel({ matrixClient, spaceStore: SpaceStore.instance }); + + // First drive it into a non-undefined state + defaultDispatcher.dispatch( + { + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: "collapse", + }, + true, + ); + expect(vm.getSnapshot().collapseSections).toBe("expand"); + + defaultDispatcher.dispatch( + { + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: undefined, + }, + true, + ); + + expect(vm.getSnapshot().collapseSections).toBeUndefined(); + }); + }); + it("should toggle message preview from enabled to disabled", () => { jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName: string) => { if (settingName === "RoomList.showMessagePreview") return true; diff --git a/apps/web/test/viewmodels/room-list/RoomListViewModel-test.tsx b/apps/web/test/viewmodels/room-list/RoomListViewModel-test.tsx index c0eacbdf35..c3fc431646 100644 --- a/apps/web/test/viewmodels/room-list/RoomListViewModel-test.tsx +++ b/apps/web/test/viewmodels/room-list/RoomListViewModel-test.tsx @@ -465,6 +465,20 @@ describe("RoomListViewModel", () => { }); }); + describe("notifyCollapseState", () => { + it("should dispatch collapseSections=undefined when feature_room_list_sections is disabled", () => { + viewModel = new RoomListViewModel({ client: matrixClient }); + + const dispatchSpy = jest.spyOn(dispatcher, "dispatch"); + RoomListStoreV3.instance.emit(RoomListStoreV3Event.ListsUpdate); + + expect(dispatchSpy).toHaveBeenCalledWith({ + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: undefined, + }); + }); + }); + describe("Keyboard navigation (ViewRoomDelta)", () => { beforeEach(() => { // stubClient sets up MatrixClientPeg which is needed when ViewRoom action is dispatched @@ -971,6 +985,96 @@ describe("RoomListViewModel", () => { expect(favSection!.roomIds).toEqual(["!fav1:server"]); }); + describe("Collapse/expand all sections", () => { + it("should collapse all sections when Action.RoomListCollapseAllSections is dispatched", async () => { + viewModel = new RoomListViewModel({ client: matrixClient }); + + const favHeader = viewModel.getSectionHeaderViewModel(DefaultTagID.Favourite); + const chatsHeader = viewModel.getSectionHeaderViewModel(CHATS_TAG); + expect(favHeader.isExpanded).toBe(true); + + dispatcher.dispatch({ action: Action.RoomListCollapseAllSections }); + await flushPromisesWithFakeTimers(); + + expect(favHeader.isExpanded).toBe(false); + expect(chatsHeader.isExpanded).toBe(false); + + const snapshot = viewModel.getSnapshot(); + expect(snapshot.sections.find((s) => s.id === DefaultTagID.Favourite)!.roomIds).toEqual([]); + expect(snapshot.sections.find((s) => s.id === CHATS_TAG)!.roomIds).toEqual([]); + }); + + it("should expand all sections when Action.RoomListExpandAllSections is dispatched", async () => { + viewModel = new RoomListViewModel({ client: matrixClient }); + + // Collapse first + const favHeader = viewModel.getSectionHeaderViewModel(DefaultTagID.Favourite); + favHeader.onClick(); + expect(favHeader.isExpanded).toBe(false); + + dispatcher.dispatch({ action: Action.RoomListExpandAllSections }); + await flushPromisesWithFakeTimers(); + + expect(favHeader.isExpanded).toBe(true); + const snapshot = viewModel.getSnapshot(); + expect(snapshot.sections.find((s) => s.id === DefaultTagID.Favourite)!.roomIds).toEqual([ + "!fav1:server", + "!fav2:server", + ]); + }); + }); + + describe("notifyCollapseState", () => { + it("should dispatch collapseSections=expand when all sections are expanded (default)", () => { + viewModel = new RoomListViewModel({ client: matrixClient }); + + const dispatchSpy = jest.spyOn(dispatcher, "dispatch"); + RoomListStoreV3.instance.emit(RoomListStoreV3Event.ListsUpdate); + + expect(dispatchSpy).toHaveBeenCalledWith({ + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: "expand", + }); + }); + + it("should dispatch collapseSection=collapse when all sections are collapsed", () => { + viewModel = new RoomListViewModel({ client: matrixClient }); + + // Collapse all sections + viewModel.getSectionHeaderViewModel(DefaultTagID.Favourite).isExpanded = false; + viewModel.getSectionHeaderViewModel(CHATS_TAG).isExpanded = false; + viewModel.getSectionHeaderViewModel(DefaultTagID.LowPriority).isExpanded = false; + + const dispatchSpy = jest.spyOn(dispatcher, "dispatch"); + RoomListStoreV3.instance.emit(RoomListStoreV3Event.ListsUpdate); + + expect(dispatchSpy).toHaveBeenCalledWith({ + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: "collapse", + }); + }); + + it("should dispatch collapseSection=undefined when it is a flat list", () => { + jest.spyOn(RoomListStoreV3.instance, "getSortedRoomsInActiveSpace").mockReturnValue({ + spaceId: "home", + sections: [ + { tag: DefaultTagID.Favourite, rooms: [] }, + { tag: CHATS_TAG, rooms: [regularRoom1] }, + { tag: DefaultTagID.LowPriority, rooms: [] }, + ], + }); + viewModel = new RoomListViewModel({ client: matrixClient }); + + const dispatchSpy = jest.spyOn(dispatcher, "dispatch"); + RoomListStoreV3.instance.emit(RoomListStoreV3Event.ListsUpdate); + + expect(dispatchSpy).toHaveBeenCalledWith({ + action: Action.RoomListSectionsCollapseStateChanged, + collapseSections: undefined, + }); + }); + }); + it("should apply sticky room within the correct section", async () => { stubClient(); viewModel = new RoomListViewModel({ client: matrixClient }); diff --git a/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListHeaderView/RoomListHeaderView.stories.tsx/collapse-sections-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListHeaderView/RoomListHeaderView.stories.tsx/collapse-sections-auto.png new file mode 100644 index 0000000000..333171f50e Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room-list/RoomListHeaderView/RoomListHeaderView.stories.tsx/collapse-sections-auto.png differ diff --git a/packages/shared-components/src/i18n/strings/en_EN.json b/packages/shared-components/src/i18n/strings/en_EN.json index d747318b37..fe6d4e0865 100644 --- a/packages/shared-components/src/i18n/strings/en_EN.json +++ b/packages/shared-components/src/i18n/strings/en_EN.json @@ -100,6 +100,7 @@ }, "appearance": "Appearance", "chat_moved": "Chat moved", + "collapse_all_sections": "Collapse all sections", "collapse_filters": "Collapse filter list", "empty": { "no_chats": "No chats yet", @@ -118,6 +119,7 @@ "show_activity": "See all activity", "show_chats": "Show all chats" }, + "expand_all_sections": "Expand all sections", "expand_filters": "Expand filter list", "filters": { "favourite": "Favourites", diff --git a/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.stories.tsx b/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.stories.tsx index 83551bc21b..d622503c1d 100644 --- a/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.stories.tsx +++ b/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.stories.tsx @@ -31,6 +31,7 @@ const RoomListHeaderViewWrapperImpl = ({ sort, toggleMessagePreview, createSection, + collapseOrExpandSections, ...rest }: RoomListHeaderProps): JSX.Element => { const vm = useMockedViewModel(rest, { @@ -44,6 +45,7 @@ const RoomListHeaderViewWrapperImpl = ({ openSpacePreferences, toggleMessagePreview, createSection, + collapseOrExpandSections, }); return ; }; @@ -65,6 +67,7 @@ const meta = { openSpacePreferences: fn(), toggleMessagePreview: fn(), createSection: fn(), + collapseOrExpandSections: fn(), }, parameters: { design: { @@ -109,3 +112,9 @@ export const PlusIcon: Story = { useComposeIcon: false, }, }; + +export const CollapseSections: Story = { + args: { + collapseSections: "collapse", + }, +}; diff --git a/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.test.tsx b/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.test.tsx index 48904171cd..d6ce1e9e3f 100644 --- a/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.test.tsx +++ b/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.test.tsx @@ -12,7 +12,7 @@ import React from "react"; import * as stories from "./RoomListHeaderView.stories"; -const { Default, NoComposeMenu, NoSpaceMenu } = composeStories(stories); +const { Default, NoComposeMenu, NoSpaceMenu, CollapseSections } = composeStories(stories); describe("RoomListHeaderView", () => { it("renders the default state", () => { @@ -29,4 +29,11 @@ describe("RoomListHeaderView", () => { const { container } = render(); expect(container).toMatchSnapshot(); }); + + it("should bind the collapse all sections action", () => { + const { getByRole } = render(); + const collapseButton = getByRole("button", { name: "Collapse all sections" }); + collapseButton.click(); + expect(CollapseSections.args?.collapseOrExpandSections).toHaveBeenCalled(); + }); }); diff --git a/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.tsx b/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.tsx index 05254899ea..d68fd25b0e 100644 --- a/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.tsx +++ b/packages/shared-components/src/room-list/RoomListHeaderView/RoomListHeaderView.tsx @@ -9,6 +9,7 @@ import React, { type JSX } from "react"; import { IconButton, H1 } from "@vector-im/compound-web"; import ComposeIcon from "@vector-im/compound-design-tokens/assets/web/icons/compose"; import PlusIcon from "@vector-im/compound-design-tokens/assets/web/icons/plus"; +import CollapseAllIcon from "@vector-im/compound-design-tokens/assets/web/icons/collapse-all"; import { type ViewModel, useViewModel } from "../../core/viewmodel"; import { Flex } from "../../core/utils/Flex"; @@ -21,6 +22,11 @@ import styles from "./RoomListHeaderView.module.css"; */ export type SortOption = "recent" | "alphabetical" | "unread-first"; +/** + * The available options for collapsing sections in the room list. + */ +export type CollapseSectionsOption = "collapse" | "expand"; + export interface RoomListHeaderViewSnapshot { /** * The title of the room list @@ -68,6 +74,12 @@ export interface RoomListHeaderViewSnapshot { * Whether to use the compose icon instead of the create icon. */ useComposeIcon: boolean; + /** + * If "collapse", an icon to collapse all sections is shown. + * If "expand", an icon to expand all sections is shown. + * If undefined, no icon are shown. + */ + collapseSections?: CollapseSectionsOption; } export interface RoomListHeaderViewActions { @@ -111,6 +123,10 @@ export interface RoomListHeaderViewActions { * Create a new section in the room list. */ createSection: () => void; + /** + * Collapse or expand all sections in the room list depending on the current state. + */ + collapseOrExpandSections: () => void; } /** @@ -136,7 +152,7 @@ interface RoomListHeaderViewProps { */ export function RoomListHeaderView({ vm }: Readonly): JSX.Element { const { translate: _t } = useI18n(); - const { title, displaySpaceMenu, displayComposeMenu, useComposeIcon } = useViewModel(vm); + const { title, displaySpaceMenu, displayComposeMenu, useComposeIcon, collapseSections } = useViewModel(vm); return ( ): J + {collapseSections && ( + vm.collapseOrExpandSections()} + tooltip={ + collapseSections === "collapse" + ? _t("room_list|collapse_all_sections") + : _t("room_list|expand_all_sections") + } + > + + + )} {/* If we don't display the compose menu, it means that the user can only send DM */} {displayComposeMenu ? ( diff --git a/packages/shared-components/src/room-list/RoomListHeaderView/index.ts b/packages/shared-components/src/room-list/RoomListHeaderView/index.ts index a0b6edee11..9e7bf46c2b 100644 --- a/packages/shared-components/src/room-list/RoomListHeaderView/index.ts +++ b/packages/shared-components/src/room-list/RoomListHeaderView/index.ts @@ -10,5 +10,6 @@ export type { RoomListHeaderViewSnapshot, RoomListHeaderViewActions, SortOption, + CollapseSectionsOption, } from "./RoomListHeaderView"; export { RoomListHeaderView } from "./RoomListHeaderView"; diff --git a/packages/shared-components/src/room-list/RoomListHeaderView/test-utils.ts b/packages/shared-components/src/room-list/RoomListHeaderView/test-utils.ts index 37b53af6f3..70c51c45c8 100644 --- a/packages/shared-components/src/room-list/RoomListHeaderView/test-utils.ts +++ b/packages/shared-components/src/room-list/RoomListHeaderView/test-utils.ts @@ -24,6 +24,7 @@ export class MockedViewModel extends MockViewModel i public openSpacePreferences = vi.fn<() => void>(); public toggleMessagePreview = vi.fn<() => void>(); public createSection = vi.fn<() => void>(); + public collapseOrExpandSections = vi.fn<() => void>(); } export { defaultSnapshot } from "./default-snapshot"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 92878b13f0..841534b316 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,8 +13,8 @@ catalogs: specifier: 1.59.1 version: 1.59.1 '@vector-im/compound-design-tokens': - specifier: 10.1.0 - version: 10.1.0 + specifier: 10.1.1 + version: 10.1.1 '@vector-im/compound-web': specifier: 9.2.1 version: 9.2.1 @@ -374,10 +374,10 @@ importers: version: 1.0.2 '@vector-im/compound-design-tokens': specifier: 'catalog:' - version: 10.1.0(@types/react@19.2.14)(react@19.2.5) + version: 10.1.1(@types/react@19.2.14)(react@19.2.5) '@vector-im/compound-web': specifier: 'catalog:' - version: 9.2.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.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 9.2.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.1.1(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@vector-im/matrix-wysiwyg': specifier: 2.40.0 version: 2.40.0(patch_hash=7bdf6150f2905bc2f055a6bcaa7b9d78fa7ffde82e800bcc454ac7b0096bd65e)(react@19.2.5) @@ -1043,7 +1043,7 @@ importers: version: 1.16.0 '@vector-im/compound-design-tokens': specifier: 'catalog:' - version: 10.1.0(@types/react@19.2.14)(react@19.2.5) + version: 10.1.1(@types/react@19.2.14)(react@19.2.5) classnames: specifier: ^2.5.1 version: 2.5.1 @@ -1158,7 +1158,7 @@ importers: version: 8.58.2(eslint@8.57.1)(typescript@6.0.3) '@vector-im/compound-web': specifier: 'catalog:' - version: 9.2.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.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 9.2.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.1.1(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@vitest/browser-playwright': specifier: ^4.0.17 version: 4.1.5(playwright@1.59.1)(vite@8.0.10(@types/node@18.19.130)(esbuild@0.27.4)(jiti@2.6.1)(sugarss@5.0.1(postcss@8.5.10))(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.5) @@ -5904,8 +5904,8 @@ packages: '@upsetjs/venn.js@2.0.0': resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} - '@vector-im/compound-design-tokens@10.1.0': - resolution: {integrity: sha512-o+7DGx+NygpT2NPE1Jo//7NZDuyjzRH06eRchS0ZlkJicKx/impEmShmHU/XiE4P84BIFOo9eZ1Ws+rAym6Tuw==} + '@vector-im/compound-design-tokens@10.1.1': + resolution: {integrity: sha512-f2rdTilbPeOjrX7Mh9iTPcp5VergY7JLLWzKVjwMvpT0wtoFKwn59D1hwX2QInpiG70QTCxEdQFYLxQKvJQ74Q==} peerDependencies: '@types/react': ^19.2.10 react: ^17 || ^18 || ^19.0.0 @@ -18677,12 +18677,12 @@ snapshots: d3-selection: 3.0.0 d3-transition: 3.0.1(d3-selection@3.0.0) - '@vector-im/compound-design-tokens@10.1.0(@types/react@19.2.14)(react@19.2.5)': + '@vector-im/compound-design-tokens@10.1.1(@types/react@19.2.14)(react@19.2.5)': optionalDependencies: '@types/react': 19.2.14 react: 19.2.5 - '@vector-im/compound-web@9.2.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.1.0(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@vector-im/compound-web@9.2.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.1.1(@types/react@19.2.14)(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@floating-ui/react': 0.27.17(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@fontsource/inconsolata': 5.2.8 @@ -18693,7 +18693,7 @@ snapshots: '@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.5(react@19.2.5))(react@19.2.5) '@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.5(react@19.2.5))(react@19.2.5) '@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@19.2.5) - '@vector-im/compound-design-tokens': 10.1.0(@types/react@19.2.14)(react@19.2.5) + '@vector-im/compound-design-tokens': 10.1.1(@types/react@19.2.14)(react@19.2.5) classnames: 2.5.1 react: 19.2.5 vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c2e8a9c23a..6da463bce7 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -16,7 +16,7 @@ catalog: "@playwright/test": 1.59.1 "playwright-core": 1.59.1 # Compound - "@vector-im/compound-design-tokens": 10.1.0 + "@vector-im/compound-design-tokens": 10.1.1 "@vector-im/compound-web": 9.2.1 # i18n matrix-web-i18n: 3.6.0