Room list: add expand all icon to room list header (#33732)

* feat: add expand all icon to room list header

* test: add screenshot

* test: add snapshot tests
This commit is contained in:
Florian Duros
2026-06-03 17:38:24 +02:00
committed by GitHub
parent cb37bfcf36
commit 96549b1edc
5 changed files with 345 additions and 3 deletions
@@ -118,3 +118,9 @@ export const CollapseSections: Story = {
collapseSections: "collapse",
},
};
export const ExpandSections: Story = {
args: {
collapseSections: "expand",
},
};
@@ -12,7 +12,7 @@ import React from "react";
import * as stories from "./RoomListHeaderView.stories";
const { Default, NoComposeMenu, NoSpaceMenu, CollapseSections } = composeStories(stories);
const { Default, NoComposeMenu, NoSpaceMenu, CollapseSections, ExpandSections } = composeStories(stories);
describe("RoomListHeaderView", () => {
it("renders the default state", () => {
@@ -30,6 +30,16 @@ describe("RoomListHeaderView", () => {
expect(container).toMatchSnapshot();
});
it("renders collapse button", () => {
const { container } = render(<CollapseSections />);
expect(container).toMatchSnapshot();
});
it("renders expand button", () => {
const { container } = render(<ExpandSections />);
expect(container).toMatchSnapshot();
});
it("should bind the collapse all sections action", () => {
const { getByRole } = render(<CollapseSections />);
const collapseButton = getByRole("button", { name: "Collapse all sections" });
@@ -9,7 +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 { CollapseAllIcon, ExpandAllIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { type ViewModel, useViewModel } from "../../core/viewmodel";
import { Flex } from "../../core/utils/Flex";
@@ -182,7 +182,11 @@ export function RoomListHeaderView({ vm }: Readonly<RoomListHeaderViewProps>): J
: _t("room_list|expand_all_sections")
}
>
<CollapseAllIcon color="var(--cpd-color-icon-secondary)" aria-hidden />
{collapseSections === "collapse" ? (
<CollapseAllIcon color="var(--cpd-color-icon-secondary)" aria-hidden />
) : (
<ExpandAllIcon color="var(--cpd-color-icon-secondary)" aria-hidden />
)}
</IconButton>
)}
@@ -1,5 +1,327 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`RoomListHeaderView > renders collapse button 1`] = `
<div>
<header
aria-label="Room options"
class="Flex-module_flex RoomListHeaderView-module_header"
data-testid="room-list-header"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: end; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
>
<div
class="Flex-module_flex RoomListHeaderView-module_container"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
>
<div
class="Flex-module_flex RoomListHeaderView-module_title"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-1x); --mx-flex-wrap: nowrap;"
>
<h1
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
title="Rooms"
>
Rooms
</h1>
<button
aria-disabled="false"
aria-expanded="false"
aria-haspopup="menu"
aria-label="Open space menu"
class="_icon-button_1215g_8 SpaceMenuView-module_button"
data-kind="primary"
data-state="closed"
id="radix-react-use-id-1"
role="button"
style="--cpd-icon-button-size: 24px; padding: 2px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 14.95q-.2 0-.375-.062a.9.9 0 0 1-.325-.213l-4.6-4.6a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l3.9 3.9 3.9-3.9a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7l-4.6 4.6q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
/>
</svg>
</div>
</button>
</div>
<div
class="Flex-module_flex"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: nowrap;"
>
<button
aria-disabled="false"
aria-expanded="false"
aria-haspopup="menu"
aria-label="Room Options"
aria-labelledby="react-use-id-2"
class="_icon-button_1215g_8"
data-kind="primary"
data-state="closed"
id="radix-react-use-id-3"
role="button"
style="--cpd-icon-button-size: 28px; padding: 4px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 14q-.824 0-1.412-.588A1.93 1.93 0 0 1 4 12q0-.825.588-1.412A1.93 1.93 0 0 1 6 10q.824 0 1.412.588Q8 11.175 8 12t-.588 1.412A1.93 1.93 0 0 1 6 14m6 0q-.825 0-1.412-.588A1.93 1.93 0 0 1 10 12q0-.825.588-1.412A1.93 1.93 0 0 1 12 10q.825 0 1.412.588Q14 11.175 14 12t-.588 1.412A1.93 1.93 0 0 1 12 14m6 0q-.824 0-1.413-.588A1.93 1.93 0 0 1 16 12q0-.825.587-1.412A1.93 1.93 0 0 1 18 10q.824 0 1.413.588Q20 11.175 20 12t-.587 1.412A1.93 1.93 0 0 1 18 14"
/>
</svg>
</div>
</button>
<button
aria-labelledby="react-use-id-4"
class="_icon-button_1215g_8"
data-kind="primary"
role="button"
style="--cpd-icon-button-size: 28px; padding: 4px;"
tabindex="0"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-hidden="true"
color="var(--cpd-color-icon-secondary)"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m12 16.975-3.9 3.9a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275.95.95 0 0 1-.275-.7q0-.425.275-.7l3.875-3.875q.575-.575 1.425-.575t1.425.575l3.875 3.875a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275zm0-9.8 3.9-3.9A.95.95 0 0 1 16.6 3q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7L13.425 8.55q-.575.575-1.425.575t-1.425-.575L6.7 4.675a.95.95 0 0 1-.275-.7q0-.425.275-.7A.95.95 0 0 1 7.4 3q.425 0 .7.275z"
/>
</svg>
</div>
</button>
<button
aria-disabled="false"
aria-expanded="false"
aria-haspopup="menu"
aria-labelledby="react-use-id-5"
class="_icon-button_1215g_8"
data-kind="primary"
data-state="closed"
id="radix-react-use-id-6"
role="button"
style="--cpd-icon-button-size: 28px; padding: 4px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-hidden="true"
color="var(--cpd-color-icon-secondary)"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M16.937 2.82a2 2 0 0 1 2.828 0l1.415 1.414a2 2 0 0 1 0 2.829l-7.071 7.07c-.195.196-.42.342-.66.44a1 1 0 0 1-.168.072l-3.993 1.331a1 1 0 0 1-1.265-1.265l1.331-3.992q.03-.09.073-.168m10.338-4.903-6.717 6.718-1.414-1.414 6.717-6.718z"
fill-rule="evenodd"
/>
<path
d="M3 5a2 2 0 0 1 2-2h6a1 1 0 1 1 0 2H5v14h14v-6a1 1 0 1 1 2 0v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
/>
</svg>
</div>
</button>
</div>
</div>
</header>
</div>
`;
exports[`RoomListHeaderView > renders expand button 1`] = `
<div>
<header
aria-label="Room options"
class="Flex-module_flex RoomListHeaderView-module_header"
data-testid="room-list-header"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: end; --mx-flex-justify: start; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
>
<div
class="Flex-module_flex RoomListHeaderView-module_container"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: space-between; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
>
<div
class="Flex-module_flex RoomListHeaderView-module_title"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-1x); --mx-flex-wrap: nowrap;"
>
<h1
class="_typography_6v6n8_153 _font-heading-sm-semibold_6v6n8_93"
title="Rooms"
>
Rooms
</h1>
<button
aria-disabled="false"
aria-expanded="false"
aria-haspopup="menu"
aria-label="Open space menu"
class="_icon-button_1215g_8 SpaceMenuView-module_button"
data-kind="primary"
data-state="closed"
id="radix-react-use-id-1"
role="button"
style="--cpd-icon-button-size: 24px; padding: 2px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 14.95q-.2 0-.375-.062a.9.9 0 0 1-.325-.213l-4.6-4.6a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275l3.9 3.9 3.9-3.9a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7l-4.6 4.6q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
/>
</svg>
</div>
</button>
</div>
<div
class="Flex-module_flex"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x); --mx-flex-wrap: nowrap;"
>
<button
aria-disabled="false"
aria-expanded="false"
aria-haspopup="menu"
aria-label="Room Options"
aria-labelledby="react-use-id-2"
class="_icon-button_1215g_8"
data-kind="primary"
data-state="closed"
id="radix-react-use-id-3"
role="button"
style="--cpd-icon-button-size: 28px; padding: 4px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 14q-.824 0-1.412-.588A1.93 1.93 0 0 1 4 12q0-.825.588-1.412A1.93 1.93 0 0 1 6 10q.824 0 1.412.588Q8 11.175 8 12t-.588 1.412A1.93 1.93 0 0 1 6 14m6 0q-.825 0-1.412-.588A1.93 1.93 0 0 1 10 12q0-.825.588-1.412A1.93 1.93 0 0 1 12 10q.825 0 1.412.588Q14 11.175 14 12t-.588 1.412A1.93 1.93 0 0 1 12 14m6 0q-.824 0-1.413-.588A1.93 1.93 0 0 1 16 12q0-.825.587-1.412A1.93 1.93 0 0 1 18 10q.824 0 1.413.588Q20 11.175 20 12t-.587 1.412A1.93 1.93 0 0 1 18 14"
/>
</svg>
</div>
</button>
<button
aria-labelledby="react-use-id-4"
class="_icon-button_1215g_8"
data-kind="primary"
role="button"
style="--cpd-icon-button-size: 28px; padding: 4px;"
tabindex="0"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-hidden="true"
color="var(--cpd-color-icon-secondary)"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m12 19.2 3.9-3.9a.95.95 0 0 1 .7-.275q.425 0 .7.275a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7l-3.875 3.875q-.575.575-1.425.575t-1.425-.575L6.7 16.7a.95.95 0 0 1-.275-.7q0-.425.275-.7a.95.95 0 0 1 .7-.275q.425 0 .7.275zm0-14.25-3.9 3.9a.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275.95.95 0 0 1-.275-.7q0-.425.275-.7l3.875-3.875Q11.15 3 12 3t1.425.575L17.3 7.45a.95.95 0 0 1 .275.7.95.95 0 0 1-.275.7.95.95 0 0 1-.7.275.95.95 0 0 1-.7-.275z"
/>
</svg>
</div>
</button>
<button
aria-disabled="false"
aria-expanded="false"
aria-haspopup="menu"
aria-labelledby="react-use-id-5"
class="_icon-button_1215g_8"
data-kind="primary"
data-state="closed"
id="radix-react-use-id-6"
role="button"
style="--cpd-icon-button-size: 28px; padding: 4px;"
tabindex="0"
type="button"
>
<div
class="_indicator-icon_147l5_17"
style="--cpd-icon-button-size: 100%;"
>
<svg
aria-hidden="true"
color="var(--cpd-color-icon-secondary)"
fill="currentColor"
height="1em"
viewBox="0 0 24 24"
width="1em"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M16.937 2.82a2 2 0 0 1 2.828 0l1.415 1.414a2 2 0 0 1 0 2.829l-7.071 7.07c-.195.196-.42.342-.66.44a1 1 0 0 1-.168.072l-3.993 1.331a1 1 0 0 1-1.265-1.265l1.331-3.992q.03-.09.073-.168m10.338-4.903-6.717 6.718-1.414-1.414 6.717-6.718z"
fill-rule="evenodd"
/>
<path
d="M3 5a2 2 0 0 1 2-2h6a1 1 0 1 1 0 2H5v14h14v-6a1 1 0 1 1 2 0v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"
/>
</svg>
</div>
</button>
</div>
</div>
</header>
</div>
`;
exports[`RoomListHeaderView > renders the default state 1`] = `
<div>
<header