mirror of
https://github.com/Novaloop-AG/healthchat-web.git
synced 2026-11-07 15:08:24 +00:00
Room list: add custom section creation in SC (#33138)
* feat: add way to create custom section in SC * feat: disable custom section in vms * chore: include design tokens icons in vitest config * test: update screenshot * chore: use one react import
This commit is contained in:
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
@@ -199,8 +199,11 @@ export class RoomListHeaderViewModel
|
|||||||
SettingsStore.setValue("RoomList.showMessagePreview", null, SettingLevel.DEVICE, isMessagePreviewEnabled);
|
SettingsStore.setValue("RoomList.showMessagePreview", null, SettingLevel.DEVICE, isMessagePreviewEnabled);
|
||||||
this.snapshot.merge({ isMessagePreviewEnabled });
|
this.snapshot.merge({ isMessagePreviewEnabled });
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
|
public createSection = (): void => {
|
||||||
|
// To be implemented when custom section creation is added in vms
|
||||||
|
};
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Get the initial snapshot for the RoomListHeaderViewModel.
|
* Get the initial snapshot for the RoomListHeaderViewModel.
|
||||||
* @param spaceStore - The space store instance.
|
* @param spaceStore - The space store instance.
|
||||||
@@ -280,5 +283,8 @@ function computeHeaderSpaceState(
|
|||||||
displaySpaceMenu,
|
displaySpaceMenu,
|
||||||
canInviteInSpace,
|
canInviteInSpace,
|
||||||
canAccessSpaceSettings,
|
canAccessSpaceSettings,
|
||||||
|
// To be implemented when custom section creation is added in vms
|
||||||
|
canCreateSection: false,
|
||||||
|
useComposeIcon: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -303,6 +303,8 @@ export class RoomListItemViewModel
|
|||||||
canMarkAsRead,
|
canMarkAsRead,
|
||||||
canMarkAsUnread,
|
canMarkAsUnread,
|
||||||
roomNotifState,
|
roomNotifState,
|
||||||
|
// To be implemented when custom section creation is added in vms
|
||||||
|
canMoveToSection: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,4 +383,8 @@ export class RoomListItemViewModel
|
|||||||
const echoChamber = EchoChamber.forRoom(this.props.room);
|
const echoChamber = EchoChamber.forRoom(this.props.room);
|
||||||
echoChamber.notificationVolume = elementNotifState;
|
echoChamber.notificationVolume = elementNotifState;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public onCreateSection = (): void => {
|
||||||
|
// To be implemented when custom section creation is added in vms
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -572,6 +572,12 @@ export class RoomListViewModel
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public closeToast: () => void = () => {
|
||||||
|
this.snapshot.merge({
|
||||||
|
toast: undefined,
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
@@ -5,6 +5,7 @@
|
|||||||
"action": {
|
"action": {
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
"click": "Click",
|
"click": "Click",
|
||||||
|
"close": "Close",
|
||||||
"collapse": "Collapse",
|
"collapse": "Collapse",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"dismiss": "Dismiss",
|
"dismiss": "Dismiss",
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
"invite": "Invite",
|
"invite": "Invite",
|
||||||
"new_conversation": "New conversation",
|
"new_conversation": "New conversation",
|
||||||
"new_room": "New room",
|
"new_room": "New room",
|
||||||
|
"new_section": "New section",
|
||||||
"new_video_room": "New video room",
|
"new_video_room": "New video room",
|
||||||
"open_menu": "Open menu",
|
"open_menu": "Open menu",
|
||||||
"pause": "Pause",
|
"pause": "Pause",
|
||||||
@@ -132,7 +134,8 @@
|
|||||||
"leave_room": "Leave room",
|
"leave_room": "Leave room",
|
||||||
"low_priority": "Low priority",
|
"low_priority": "Low priority",
|
||||||
"mark_read": "Mark as read",
|
"mark_read": "Mark as read",
|
||||||
"mark_unread": "Mark as unread"
|
"mark_unread": "Mark as unread",
|
||||||
|
"move_to_section": "Move to"
|
||||||
},
|
},
|
||||||
"notification_options": "Notification options",
|
"notification_options": "Notification options",
|
||||||
"open_space_menu": "Open space menu",
|
"open_space_menu": "Open space menu",
|
||||||
@@ -141,6 +144,7 @@
|
|||||||
"more_options": "More Options"
|
"more_options": "More Options"
|
||||||
},
|
},
|
||||||
"room_options": "Room Options",
|
"room_options": "Room Options",
|
||||||
|
"section_created": "Section created",
|
||||||
"section_header": {
|
"section_header": {
|
||||||
"toggle": "Toggle %(section)s section",
|
"toggle": "Toggle %(section)s section",
|
||||||
"toggle_unread": "Toggle %(section)s section with unread room(s)"
|
"toggle_unread": "Toggle %(section)s section with unread room(s)"
|
||||||
|
|||||||
+9
@@ -30,6 +30,7 @@ const RoomListHeaderViewWrapperImpl = ({
|
|||||||
openSpacePreferences,
|
openSpacePreferences,
|
||||||
sort,
|
sort,
|
||||||
toggleMessagePreview,
|
toggleMessagePreview,
|
||||||
|
createSection,
|
||||||
...rest
|
...rest
|
||||||
}: RoomListHeaderProps): JSX.Element => {
|
}: RoomListHeaderProps): JSX.Element => {
|
||||||
const vm = useMockedViewModel(rest, {
|
const vm = useMockedViewModel(rest, {
|
||||||
@@ -42,6 +43,7 @@ const RoomListHeaderViewWrapperImpl = ({
|
|||||||
sort,
|
sort,
|
||||||
openSpacePreferences,
|
openSpacePreferences,
|
||||||
toggleMessagePreview,
|
toggleMessagePreview,
|
||||||
|
createSection,
|
||||||
});
|
});
|
||||||
return <RoomListHeaderView vm={vm} />;
|
return <RoomListHeaderView vm={vm} />;
|
||||||
};
|
};
|
||||||
@@ -62,6 +64,7 @@ const meta = {
|
|||||||
sort: fn(),
|
sort: fn(),
|
||||||
openSpacePreferences: fn(),
|
openSpacePreferences: fn(),
|
||||||
toggleMessagePreview: fn(),
|
toggleMessagePreview: fn(),
|
||||||
|
createSection: fn(),
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
design: {
|
design: {
|
||||||
@@ -100,3 +103,9 @@ export const LongTitle: Story = {
|
|||||||
title: "Loooooooooooooooooooooooooooooooooooooong title",
|
title: "Loooooooooooooooooooooooooooooooooooooong title",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const PlusIcon: Story = {
|
||||||
|
args: {
|
||||||
|
useComposeIcon: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
import React, { type JSX } from "react";
|
import React, { type JSX } from "react";
|
||||||
import { IconButton, H1 } from "@vector-im/compound-web";
|
import { IconButton, H1 } from "@vector-im/compound-web";
|
||||||
import ComposeIcon from "@vector-im/compound-design-tokens/assets/web/icons/compose";
|
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 { type ViewModel, useViewModel } from "../../core/viewmodel";
|
import { type ViewModel, useViewModel } from "../../core/viewmodel";
|
||||||
import { Flex } from "../../core/utils/Flex";
|
import { Flex } from "../../core/utils/Flex";
|
||||||
@@ -59,6 +60,14 @@ export interface RoomListHeaderViewSnapshot {
|
|||||||
* Whether message previews are enabled in the room list.
|
* Whether message previews are enabled in the room list.
|
||||||
*/
|
*/
|
||||||
isMessagePreviewEnabled: boolean;
|
isMessagePreviewEnabled: boolean;
|
||||||
|
/**
|
||||||
|
* Whether the user can create sections in the room list.
|
||||||
|
*/
|
||||||
|
canCreateSection: boolean;
|
||||||
|
/**
|
||||||
|
* Whether to use the compose icon instead of the create icon.
|
||||||
|
*/
|
||||||
|
useComposeIcon: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RoomListHeaderViewActions {
|
export interface RoomListHeaderViewActions {
|
||||||
@@ -98,6 +107,10 @@ export interface RoomListHeaderViewActions {
|
|||||||
* Toggle message preview display in the room list.
|
* Toggle message preview display in the room list.
|
||||||
*/
|
*/
|
||||||
toggleMessagePreview: () => void;
|
toggleMessagePreview: () => void;
|
||||||
|
/**
|
||||||
|
* Create a new section in the room list.
|
||||||
|
*/
|
||||||
|
createSection: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -123,7 +136,7 @@ interface RoomListHeaderViewProps {
|
|||||||
*/
|
*/
|
||||||
export function RoomListHeaderView({ vm }: Readonly<RoomListHeaderViewProps>): JSX.Element {
|
export function RoomListHeaderView({ vm }: Readonly<RoomListHeaderViewProps>): JSX.Element {
|
||||||
const { translate: _t } = useI18n();
|
const { translate: _t } = useI18n();
|
||||||
const { title, displaySpaceMenu, displayComposeMenu } = useViewModel(vm);
|
const { title, displaySpaceMenu, displayComposeMenu, useComposeIcon } = useViewModel(vm);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
@@ -153,7 +166,11 @@ export function RoomListHeaderView({ vm }: Readonly<RoomListHeaderViewProps>): J
|
|||||||
onClick={(e) => vm.createChatRoom(e.nativeEvent)}
|
onClick={(e) => vm.createChatRoom(e.nativeEvent)}
|
||||||
tooltip={_t("action|new_conversation")}
|
tooltip={_t("action|new_conversation")}
|
||||||
>
|
>
|
||||||
<ComposeIcon color="var(--cpd-color-icon-secondary)" aria-hidden />
|
{useComposeIcon ? (
|
||||||
|
<ComposeIcon color="var(--cpd-color-icon-secondary)" aria-hidden />
|
||||||
|
) : (
|
||||||
|
<PlusIcon color="var(--cpd-color-icon-secondary)" aria-hidden />
|
||||||
|
)}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
+2
@@ -110,6 +110,7 @@ exports[`RoomListHeaderView > renders the default state 1`] = `
|
|||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
|
color="var(--cpd-color-icon-secondary)"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
height="1em"
|
height="1em"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@@ -340,6 +341,7 @@ exports[`RoomListHeaderView > renders without space menu 1`] = `
|
|||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
|
color="var(--cpd-color-icon-secondary)"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
height="1em"
|
height="1em"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
|||||||
@@ -17,4 +17,6 @@ export const defaultSnapshot: RoomListHeaderViewSnapshot = {
|
|||||||
canAccessSpaceSettings: true,
|
canAccessSpaceSettings: true,
|
||||||
activeSortOption: "recent",
|
activeSortOption: "recent",
|
||||||
isMessagePreviewEnabled: true,
|
isMessagePreviewEnabled: true,
|
||||||
|
useComposeIcon: true,
|
||||||
|
canCreateSection: true,
|
||||||
};
|
};
|
||||||
|
|||||||
+11
@@ -101,4 +101,15 @@ describe("<ComposeMenuView />", () => {
|
|||||||
|
|
||||||
expect(vm.createVideoRoom).toHaveBeenCalledTimes(1);
|
expect(vm.createVideoRoom).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should create a new section", async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
|
||||||
|
const vm = new MockedViewModel({ ...defaultSnapshot, isMessagePreviewEnabled: true });
|
||||||
|
render(<ComposeMenuView vm={vm} />);
|
||||||
|
|
||||||
|
await user.click(screen.getByRole("button", { name: "New conversation" }));
|
||||||
|
await user.click(screen.getByRole("menuitem", { name: "New section" }));
|
||||||
|
expect(vm.createSection).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+11
-2
@@ -11,6 +11,8 @@ import ComposeIcon from "@vector-im/compound-design-tokens/assets/web/icons/comp
|
|||||||
import VideoCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/video-call";
|
import VideoCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/video-call";
|
||||||
import ChatIcon from "@vector-im/compound-design-tokens/assets/web/icons/chat";
|
import ChatIcon from "@vector-im/compound-design-tokens/assets/web/icons/chat";
|
||||||
import RoomIcon from "@vector-im/compound-design-tokens/assets/web/icons/room";
|
import RoomIcon from "@vector-im/compound-design-tokens/assets/web/icons/room";
|
||||||
|
import SectionIcon from "@vector-im/compound-design-tokens/assets/web/icons/section";
|
||||||
|
import PlusIcon from "@vector-im/compound-design-tokens/assets/web/icons/plus";
|
||||||
|
|
||||||
import { type RoomListHeaderViewModel } from "../RoomListHeaderView";
|
import { type RoomListHeaderViewModel } from "../RoomListHeaderView";
|
||||||
import { useI18n } from "../../../core/i18n/i18nContext";
|
import { useI18n } from "../../../core/i18n/i18nContext";
|
||||||
@@ -35,7 +37,7 @@ interface ComposeMenuViewProps {
|
|||||||
export function ComposeMenuView({ vm }: ComposeMenuViewProps): JSX.Element {
|
export function ComposeMenuView({ vm }: ComposeMenuViewProps): JSX.Element {
|
||||||
const { translate: _t } = useI18n();
|
const { translate: _t } = useI18n();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const { canCreateRoom, canCreateVideoRoom } = useViewModel(vm);
|
const { canCreateRoom, canCreateVideoRoom, canCreateSection, useComposeIcon } = useViewModel(vm);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu
|
<Menu
|
||||||
@@ -47,7 +49,11 @@ export function ComposeMenuView({ vm }: ComposeMenuViewProps): JSX.Element {
|
|||||||
trigger={
|
trigger={
|
||||||
// 28px button with a 20px icon
|
// 28px button with a 20px icon
|
||||||
<IconButton size="28px" style={{ padding: "4px" }} tooltip={_t("action|new_conversation")}>
|
<IconButton size="28px" style={{ padding: "4px" }} tooltip={_t("action|new_conversation")}>
|
||||||
<ComposeIcon aria-hidden />
|
{useComposeIcon ? (
|
||||||
|
<ComposeIcon color="var(--cpd-color-icon-secondary)" aria-hidden />
|
||||||
|
) : (
|
||||||
|
<PlusIcon color="var(--cpd-color-icon-secondary)" aria-hidden />
|
||||||
|
)}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -63,6 +69,9 @@ export function ComposeMenuView({ vm }: ComposeMenuViewProps): JSX.Element {
|
|||||||
hideChevron
|
hideChevron
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{canCreateSection && (
|
||||||
|
<MenuItem Icon={SectionIcon} label={_t("action|new_section")} onSelect={vm.createSection} hideChevron />
|
||||||
|
)}
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -22,6 +22,7 @@ exports[`<ComposeMenuView /> > should match snapshot 1`] = `
|
|||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
|
color="var(--cpd-color-icon-secondary)"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
height="1em"
|
height="1em"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export class MockedViewModel extends MockViewModel<RoomListHeaderViewSnapshot> i
|
|||||||
public sort = vi.fn<() => void>();
|
public sort = vi.fn<() => void>();
|
||||||
public openSpacePreferences = vi.fn<() => void>();
|
public openSpacePreferences = vi.fn<() => void>();
|
||||||
public toggleMessagePreview = vi.fn<() => void>();
|
public toggleMessagePreview = vi.fn<() => void>();
|
||||||
|
public createSection = vi.fn<() => void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
export { defaultSnapshot } from "./default-snapshot";
|
export { defaultSnapshot } from "./default-snapshot";
|
||||||
|
|||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
position: absolute;
|
||||||
|
bottom: var(--cpd-space-4x);
|
||||||
|
left: var(--cpd-space-3x);
|
||||||
|
right: var(--cpd-space-3x);
|
||||||
|
width: fit-content;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
+40
@@ -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 from "react";
|
||||||
|
import { fn } from "storybook/test";
|
||||||
|
|
||||||
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||||
|
import { RoomListToast } from "./RoomListToast";
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: "Room List/RoomListView/RoomListToast",
|
||||||
|
component: RoomListToast,
|
||||||
|
tags: ["autodocs"],
|
||||||
|
args: {
|
||||||
|
type: "section_created",
|
||||||
|
onClose: fn(),
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
type: {
|
||||||
|
control: "select",
|
||||||
|
options: ["section_created"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
decorators: [
|
||||||
|
(Story) => (
|
||||||
|
<div style={{ position: "relative", width: "320px", height: "100px", backgroundColor: "grey" }}>
|
||||||
|
<Story />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
],
|
||||||
|
} satisfies Meta<typeof RoomListToast>;
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof meta>;
|
||||||
|
|
||||||
|
export const SectionCreated: Story = {};
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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 from "react";
|
||||||
|
import { render, screen } from "@test-utils";
|
||||||
|
import { composeStories } from "@storybook/react-vite";
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
|
||||||
|
import * as stories from "./RoomListToast.stories";
|
||||||
|
|
||||||
|
const { SectionCreated } = composeStories(stories);
|
||||||
|
|
||||||
|
describe("<RoomListToast />", () => {
|
||||||
|
it("renders SectionCreated story", () => {
|
||||||
|
const { container } = render(<SectionCreated />);
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls onClose when the close button is clicked", async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
render(<SectionCreated />);
|
||||||
|
const closeButton = screen.getByRole("button", { name: "Close" });
|
||||||
|
await user.click(closeButton);
|
||||||
|
expect(SectionCreated.args.onClose).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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 ComponentType, type JSX, type MouseEventHandler } from "react";
|
||||||
|
import { Toast } from "@vector-im/compound-web";
|
||||||
|
import CheckIcon from "@vector-im/compound-design-tokens/assets/web/icons/check";
|
||||||
|
|
||||||
|
import styles from "./RoomListToast.module.css";
|
||||||
|
import { useI18n } from "../../../core/i18n/i18nContext";
|
||||||
|
|
||||||
|
export type ToastType = "section_created";
|
||||||
|
|
||||||
|
interface RoomListToastProps {
|
||||||
|
/** The type of toast to display */
|
||||||
|
type: ToastType;
|
||||||
|
/** Callback when the close button is clicked */
|
||||||
|
onClose: MouseEventHandler<HTMLButtonElement>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A toast component used for displaying temporary messages in the room list view.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```tsx
|
||||||
|
* <RoomListToast type="section_created" onClose={onCloseHandler} />
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export function RoomListToast({ type, onClose }: Readonly<RoomListToastProps>): JSX.Element {
|
||||||
|
const { translate: _t } = useI18n();
|
||||||
|
|
||||||
|
let content: { text: string; icon: ComponentType<React.SVGAttributes<SVGElement>> };
|
||||||
|
switch (type) {
|
||||||
|
case "section_created":
|
||||||
|
content = { text: _t("room_list|section_created"), icon: CheckIcon };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Toast className={styles.toast} Icon={content.icon} onClose={onClose} tooltip={_t("action|close")}>
|
||||||
|
{content.text}
|
||||||
|
</Toast>
|
||||||
|
);
|
||||||
|
}
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`<RoomListToast /> > renders SectionCreated story 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style="position: relative; width: 320px; height: 100px; background-color: grey;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_typography_6v6n8_153 _font-body-sm-medium_6v6n8_41 _toast-container_1ysb3_8 RoomListToast-module_toast _has-close_1ysb3_30"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="_content_1ysb3_34"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="_icon_1ysb3_26"
|
||||||
|
fill="currentColor"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M9.55 17.575q-.2 0-.375-.062a.9.9 0 0 1-.325-.213L4.55 13q-.274-.274-.262-.713.012-.437.287-.712a.95.95 0 0 1 .7-.275q.425 0 .7.275L9.55 15.15l8.475-8.475q.274-.275.713-.275.437 0 .712.275.275.274.275.713 0 .437-.275.712l-9.2 9.2q-.15.15-.325.212a1.1 1.1 0 0 1-.375.063"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Section created
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
aria-labelledby="_r_0_"
|
||||||
|
class="_icon-button_1215g_8 _close_1ysb3_41 _no-background_1215g_42"
|
||||||
|
data-kind="secondary"
|
||||||
|
role="button"
|
||||||
|
style="--cpd-icon-button-size: 24px;"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<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.293 6.293a1 1 0 0 1 1.414 0L12 10.586l4.293-4.293a1 1 0 1 1 1.414 1.414L13.414 12l4.293 4.293a1 1 0 0 1-1.414 1.414L12 13.414l-4.293 4.293a1 1 0 0 1-1.414-1.414L10.586 12 6.293 7.707a1 1 0 0 1 0-1.414"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type { ToastType } from "./RoomListToast";
|
||||||
|
export { RoomListToast } from "./RoomListToast";
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.list {
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ const RoomListViewWrapperImpl = ({
|
|||||||
getSectionHeaderViewModel,
|
getSectionHeaderViewModel,
|
||||||
updateVisibleRooms,
|
updateVisibleRooms,
|
||||||
renderAvatar: renderAvatarProp,
|
renderAvatar: renderAvatarProp,
|
||||||
|
closeToast,
|
||||||
...rest
|
...rest
|
||||||
}: RoomListViewProps): JSX.Element => {
|
}: RoomListViewProps): JSX.Element => {
|
||||||
const vm = useMockedViewModel(rest, {
|
const vm = useMockedViewModel(rest, {
|
||||||
@@ -48,6 +49,7 @@ const RoomListViewWrapperImpl = ({
|
|||||||
getRoomItemViewModel,
|
getRoomItemViewModel,
|
||||||
getSectionHeaderViewModel,
|
getSectionHeaderViewModel,
|
||||||
updateVisibleRooms,
|
updateVisibleRooms,
|
||||||
|
closeToast,
|
||||||
});
|
});
|
||||||
return <RoomListView vm={vm} renderAvatar={renderAvatarProp} />;
|
return <RoomListView vm={vm} renderAvatar={renderAvatarProp} />;
|
||||||
};
|
};
|
||||||
@@ -98,6 +100,8 @@ const meta = {
|
|||||||
updateVisibleRooms: fn(),
|
updateVisibleRooms: fn(),
|
||||||
renderAvatar,
|
renderAvatar,
|
||||||
isFlatList: true,
|
isFlatList: true,
|
||||||
|
toast: undefined,
|
||||||
|
closeToast: fn(),
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
design: {
|
design: {
|
||||||
@@ -245,3 +249,9 @@ export const LargeSectionList: Story = {
|
|||||||
getSectionHeaderViewModel: createGetSectionHeaderViewModel(mockLargeListSections.map((section) => section.id)),
|
getSectionHeaderViewModel: createGetSectionHeaderViewModel(mockLargeListSections.map((section) => section.id)),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const Toast: Story = {
|
||||||
|
args: {
|
||||||
|
toast: "section_created",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const {
|
|||||||
EmptyInvitesFilter,
|
EmptyInvitesFilter,
|
||||||
EmptyMentionsFilter,
|
EmptyMentionsFilter,
|
||||||
EmptyLowPriorityFilter,
|
EmptyLowPriorityFilter,
|
||||||
|
Toast,
|
||||||
} = composeStories(stories);
|
} = composeStories(stories);
|
||||||
|
|
||||||
const renderWithMockContext = (component: React.ReactElement): ReturnType<typeof render> => {
|
const renderWithMockContext = (component: React.ReactElement): ReturnType<typeof render> => {
|
||||||
@@ -124,6 +125,11 @@ describe("<RoomListView />", () => {
|
|||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders Toast story", () => {
|
||||||
|
const { container } = renderWithMockContext(<Toast />);
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
it("should call onToggleFilter when filter is clicked", async () => {
|
it("should call onToggleFilter when filter is clicked", async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
renderWithMockContext(<Default />);
|
renderWithMockContext(<Default />);
|
||||||
@@ -186,4 +192,13 @@ describe("<RoomListView />", () => {
|
|||||||
|
|
||||||
expect(EmptyLowPriorityFilter.args.onToggleFilter).toHaveBeenCalled();
|
expect(EmptyLowPriorityFilter.args.onToggleFilter).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should call closeToast when close button is clicked on toast", async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
renderWithMockContext(<Toast />);
|
||||||
|
|
||||||
|
await user.click(screen.getByRole("button", { name: "Close" }));
|
||||||
|
|
||||||
|
expect(EmptyLowPriorityFilter.args.closeToast).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ import {
|
|||||||
type RoomListItemViewModel,
|
type RoomListItemViewModel,
|
||||||
} from "../VirtualizedRoomListView/RoomListItemAccessibilityWrapper/RoomListItemView";
|
} from "../VirtualizedRoomListView/RoomListItemAccessibilityWrapper/RoomListItemView";
|
||||||
import { type RoomListSectionHeaderViewModel } from "../VirtualizedRoomListView/RoomListSectionHeaderView";
|
import { type RoomListSectionHeaderViewModel } from "../VirtualizedRoomListView/RoomListSectionHeaderView";
|
||||||
|
import { type ToastType, RoomListToast } from "./RoomListToast";
|
||||||
|
import styles from "./RoomListView.module.css";
|
||||||
|
import { Flex } from "../../core/utils/Flex";
|
||||||
|
|
||||||
export type RoomListSection = {
|
export type RoomListSection = {
|
||||||
/** Unique identifier for the section */
|
/** Unique identifier for the section */
|
||||||
@@ -49,6 +52,8 @@ export type RoomListViewSnapshot = {
|
|||||||
canCreateRoom?: boolean;
|
canCreateRoom?: boolean;
|
||||||
/** Whether the room list is displayed as a flat list */
|
/** Whether the room list is displayed as a flat list */
|
||||||
isFlatList: boolean;
|
isFlatList: boolean;
|
||||||
|
/** Optional toast to display */
|
||||||
|
toast?: ToastType;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,6 +75,8 @@ export interface RoomListViewActions {
|
|||||||
updateVisibleRooms: (startIndex: number, endIndex: number) => void;
|
updateVisibleRooms: (startIndex: number, endIndex: number) => void;
|
||||||
/** Get view model for a specific section header (virtualization API) */
|
/** Get view model for a specific section header (virtualization API) */
|
||||||
getSectionHeaderViewModel: (sectionId: string) => RoomListSectionHeaderViewModel;
|
getSectionHeaderViewModel: (sectionId: string) => RoomListSectionHeaderViewModel;
|
||||||
|
/** Called to close the toast message */
|
||||||
|
closeToast: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,7 +120,10 @@ export const RoomListView: React.FC<RoomListViewProps> = ({ vm, renderAvatar, on
|
|||||||
onToggleFilter={vm.onToggleFilter}
|
onToggleFilter={vm.onToggleFilter}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{listBody}
|
<Flex direction="column" className={styles.list}>
|
||||||
|
{listBody}
|
||||||
|
{snapshot.toast && <RoomListToast type={snapshot.toast} onClose={vm.closeToast} />}
|
||||||
|
</Flex>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+16005
-13196
File diff suppressed because it is too large
Load Diff
+17
-1
@@ -10,7 +10,7 @@ import { render, screen } from "@test-utils";
|
|||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { describe, it, expect, vi } from "vitest";
|
import { describe, it, expect, vi } from "vitest";
|
||||||
|
|
||||||
import { RoomListItemMoreOptionsMenu } from "./RoomListItemMoreOptionsMenu";
|
import { RoomListItemMoreOptionsMenu, MoreOptionContent } from "./RoomListItemMoreOptionsMenu";
|
||||||
import { useMockedViewModel } from "../../../../core/viewmodel";
|
import { useMockedViewModel } from "../../../../core/viewmodel";
|
||||||
import type { RoomListItemViewSnapshot } from "./RoomListItemView";
|
import type { RoomListItemViewSnapshot } from "./RoomListItemView";
|
||||||
import { defaultSnapshot } from "./default-snapshot";
|
import { defaultSnapshot } from "./default-snapshot";
|
||||||
@@ -26,6 +26,7 @@ describe("<RoomListItemMoreOptionsMenu />", () => {
|
|||||||
onCopyRoomLink: vi.fn(),
|
onCopyRoomLink: vi.fn(),
|
||||||
onLeaveRoom: vi.fn(),
|
onLeaveRoom: vi.fn(),
|
||||||
onSetRoomNotifState: vi.fn(),
|
onSetRoomNotifState: vi.fn(),
|
||||||
|
onCreateSection: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderMenu = (overrides: Partial<RoomListItemViewSnapshot> = {}): ReturnType<typeof render> => {
|
const renderMenu = (overrides: Partial<RoomListItemViewSnapshot> = {}): ReturnType<typeof render> => {
|
||||||
@@ -224,4 +225,19 @@ describe("<RoomListItemMoreOptionsMenu />", () => {
|
|||||||
|
|
||||||
expect(mockCallbacks.onLeaveRoom).toHaveBeenCalled();
|
expect(mockCallbacks.onLeaveRoom).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should call onCreateSection when new section is clicked", async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
// We need to render the MoreOptionContent directly here as radix is kind of messing in the test env
|
||||||
|
const TestComponent = (): JSX.Element => {
|
||||||
|
const vm = useMockedViewModel(defaultSnapshot, mockCallbacks);
|
||||||
|
return <MoreOptionContent vm={vm} />;
|
||||||
|
};
|
||||||
|
render(<TestComponent />);
|
||||||
|
|
||||||
|
const newSection = screen.getByRole("menuitem", { name: "New section" });
|
||||||
|
await user.click(newSection);
|
||||||
|
|
||||||
|
expect(mockCallbacks.onCreateSection).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+16
-1
@@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, type JSX } from "react";
|
import React, { useState, type JSX } from "react";
|
||||||
import { IconButton, Menu, MenuItem, Separator, ToggleMenuItem } from "@vector-im/compound-web";
|
import { IconButton, Menu, MenuItem, Separator, SubMenu, ToggleMenuItem } from "@vector-im/compound-web";
|
||||||
import {
|
import {
|
||||||
MarkAsReadIcon,
|
MarkAsReadIcon,
|
||||||
MarkAsUnreadIcon,
|
MarkAsUnreadIcon,
|
||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
LinkIcon,
|
LinkIcon,
|
||||||
LeaveIcon,
|
LeaveIcon,
|
||||||
OverflowHorizontalIcon,
|
OverflowHorizontalIcon,
|
||||||
|
ArrowRightIcon,
|
||||||
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
|
|
||||||
import { _t } from "../../../../core/i18n/i18n";
|
import { _t } from "../../../../core/i18n/i18n";
|
||||||
@@ -106,6 +107,7 @@ export function MoreOptionContent({ vm }: MoreOptionContentProps): JSX.Element {
|
|||||||
onSelect={vm.onToggleLowPriority}
|
onSelect={vm.onToggleLowPriority}
|
||||||
onClick={(evt) => evt.stopPropagation()}
|
onClick={(evt) => evt.stopPropagation()}
|
||||||
/>
|
/>
|
||||||
|
<Separator />
|
||||||
{snapshot.canInvite && (
|
{snapshot.canInvite && (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Icon={UserAddIcon}
|
Icon={UserAddIcon}
|
||||||
@@ -124,6 +126,19 @@ export function MoreOptionContent({ vm }: MoreOptionContentProps): JSX.Element {
|
|||||||
hideChevron={true}
|
hideChevron={true}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{snapshot.canMoveToSection && (
|
||||||
|
<SubMenu
|
||||||
|
trigger={
|
||||||
|
<MenuItem
|
||||||
|
Icon={ArrowRightIcon}
|
||||||
|
label={_t("room_list|more_options|move_to_section")}
|
||||||
|
onSelect={null}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<MenuItem label={_t("action|new_section")} onSelect={vm.onCreateSection} hideChevron={true} />
|
||||||
|
</SubMenu>
|
||||||
|
)}
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
kind="critical"
|
kind="critical"
|
||||||
|
|||||||
+1
@@ -27,6 +27,7 @@ describe("<RoomListItemNotificationMenu />", () => {
|
|||||||
onCopyRoomLink: vi.fn(),
|
onCopyRoomLink: vi.fn(),
|
||||||
onLeaveRoom: vi.fn(),
|
onLeaveRoom: vi.fn(),
|
||||||
onSetRoomNotifState: vi.fn(),
|
onSetRoomNotifState: vi.fn(),
|
||||||
|
onCreateSection: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderMenu = (roomNotifState: RoomNotifState = RoomNotifState.AllMessages): ReturnType<typeof render> => {
|
const renderMenu = (roomNotifState: RoomNotifState = RoomNotifState.AllMessages): ReturnType<typeof render> => {
|
||||||
|
|||||||
+2
@@ -38,6 +38,7 @@ const RoomListItemWrapperImpl = ({
|
|||||||
onCopyRoomLink,
|
onCopyRoomLink,
|
||||||
onLeaveRoom,
|
onLeaveRoom,
|
||||||
onSetRoomNotifState,
|
onSetRoomNotifState,
|
||||||
|
onCreateSection,
|
||||||
isSelected,
|
isSelected,
|
||||||
isFocused,
|
isFocused,
|
||||||
onFocus,
|
onFocus,
|
||||||
@@ -56,6 +57,7 @@ const RoomListItemWrapperImpl = ({
|
|||||||
onCopyRoomLink,
|
onCopyRoomLink,
|
||||||
onLeaveRoom,
|
onLeaveRoom,
|
||||||
onSetRoomNotifState,
|
onSetRoomNotifState,
|
||||||
|
onCreateSection,
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<RoomListItemView
|
<RoomListItemView
|
||||||
|
|||||||
+4
@@ -79,6 +79,8 @@ export interface RoomListItemViewSnapshot {
|
|||||||
canMarkAsUnread: boolean;
|
canMarkAsUnread: boolean;
|
||||||
/** The room's notification state */
|
/** The room's notification state */
|
||||||
roomNotifState: RoomNotifState;
|
roomNotifState: RoomNotifState;
|
||||||
|
/** Whether the room can be moved to a section */
|
||||||
|
canMoveToSection: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -104,6 +106,8 @@ export interface RoomListItemViewActions {
|
|||||||
onLeaveRoom: () => void;
|
onLeaveRoom: () => void;
|
||||||
/** Called when setting the room notification state */
|
/** Called when setting the room notification state */
|
||||||
onSetRoomNotifState: (state: RoomNotifState) => void;
|
onSetRoomNotifState: (state: RoomNotifState) => void;
|
||||||
|
/** Called when creating a new section */
|
||||||
|
onCreateSection: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+1
@@ -36,4 +36,5 @@ export const defaultSnapshot: RoomListItemViewSnapshot = {
|
|||||||
canMarkAsRead: false,
|
canMarkAsRead: false,
|
||||||
canMarkAsUnread: true,
|
canMarkAsUnread: true,
|
||||||
roomNotifState: RoomNotifState.AllMessages,
|
roomNotifState: RoomNotifState.AllMessages,
|
||||||
|
canMoveToSection: true,
|
||||||
};
|
};
|
||||||
|
|||||||
+1
@@ -19,4 +19,5 @@ export const mockedActions: RoomListItemViewActions = {
|
|||||||
onCopyRoomLink: fn(),
|
onCopyRoomLink: fn(),
|
||||||
onLeaveRoom: fn(),
|
onLeaveRoom: fn(),
|
||||||
onSetRoomNotifState: fn(),
|
onSetRoomNotifState: fn(),
|
||||||
|
onCreateSection: fn(),
|
||||||
};
|
};
|
||||||
|
|||||||
-1
@@ -9,6 +9,5 @@
|
|||||||
* Room list container styles
|
* Room list container styles
|
||||||
*/
|
*/
|
||||||
.roomList {
|
.roomList {
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -34,6 +34,7 @@ const RoomListWrapperImpl = ({
|
|||||||
getRoomItemViewModel,
|
getRoomItemViewModel,
|
||||||
getSectionHeaderViewModel,
|
getSectionHeaderViewModel,
|
||||||
updateVisibleRooms,
|
updateVisibleRooms,
|
||||||
|
closeToast,
|
||||||
renderAvatar: renderAvatarProp,
|
renderAvatar: renderAvatarProp,
|
||||||
...rest
|
...rest
|
||||||
}: RoomListStoryProps): JSX.Element => {
|
}: RoomListStoryProps): JSX.Element => {
|
||||||
@@ -44,6 +45,7 @@ const RoomListWrapperImpl = ({
|
|||||||
getRoomItemViewModel,
|
getRoomItemViewModel,
|
||||||
getSectionHeaderViewModel,
|
getSectionHeaderViewModel,
|
||||||
updateVisibleRooms,
|
updateVisibleRooms,
|
||||||
|
closeToast,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -82,6 +84,7 @@ const meta = {
|
|||||||
updateVisibleRooms: fn(),
|
updateVisibleRooms: fn(),
|
||||||
renderAvatar,
|
renderAvatar,
|
||||||
isFlatList: true,
|
isFlatList: true,
|
||||||
|
closeToast: fn(),
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
design: {
|
design: {
|
||||||
|
|||||||
+2
@@ -21,6 +21,7 @@ import type { RoomListViewSnapshot, RoomListViewModel } from "../RoomListView";
|
|||||||
import { GroupedVirtualizedList } from "../../core/VirtualizedList";
|
import { GroupedVirtualizedList } from "../../core/VirtualizedList";
|
||||||
import { RoomListSectionHeaderView } from "./RoomListSectionHeaderView";
|
import { RoomListSectionHeaderView } from "./RoomListSectionHeaderView";
|
||||||
import { RoomListItemAccessibilityWrapper } from "./RoomListItemAccessibilityWrapper";
|
import { RoomListItemAccessibilityWrapper } from "./RoomListItemAccessibilityWrapper";
|
||||||
|
import styles from "./VirtualizedRoomListView.module.css";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter key type - opaque string type for filter identifiers
|
* Filter key type - opaque string type for filter identifiers
|
||||||
@@ -350,6 +351,7 @@ export function VirtualizedRoomListView({ vm, renderAvatar, onKeyDown }: Virtual
|
|||||||
rangeChanged,
|
rangeChanged,
|
||||||
onKeyDown,
|
onKeyDown,
|
||||||
increaseViewportBy,
|
increaseViewportBy,
|
||||||
|
className: styles.roomList,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isFlatList) {
|
if (isFlatList) {
|
||||||
|
|||||||
+1
@@ -10,6 +10,7 @@ exports[`<VirtualizedRoomListView /> > renders Default story 1`] = `
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
aria-label="Room list"
|
aria-label="Room list"
|
||||||
|
class="VirtualizedRoomListView-module_roomList"
|
||||||
data-testid="room-list"
|
data-testid="room-list"
|
||||||
data-virtuoso-scroller="true"
|
data-virtuoso-scroller="true"
|
||||||
role="listbox"
|
role="listbox"
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ export const createMockRoomSnapshot = (id: string, name: string, index: number):
|
|||||||
canMarkAsRead: false,
|
canMarkAsRead: false,
|
||||||
canMarkAsUnread: true,
|
canMarkAsUnread: true,
|
||||||
roomNotifState: RoomNotifState.AllMessages,
|
roomNotifState: RoomNotifState.AllMessages,
|
||||||
|
canMoveToSection: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
export function createMockRoomItemViewModel(roomId: string, name: string, index: number): RoomListItemViewModel {
|
export function createMockRoomItemViewModel(roomId: string, name: string, index: number): RoomListItemViewModel {
|
||||||
@@ -121,6 +122,7 @@ export function createMockRoomItemViewModel(roomId: string, name: string, index:
|
|||||||
onCopyRoomLink: fn(),
|
onCopyRoomLink: fn(),
|
||||||
onLeaveRoom: fn(),
|
onLeaveRoom: fn(),
|
||||||
onSetRoomNotifState: fn(),
|
onSetRoomNotifState: fn(),
|
||||||
|
onCreateSection: fn(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -150,7 +150,11 @@ export default defineConfig({
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
include: ["vite-plugin-node-polyfills/shims/buffer", "vite-plugin-node-polyfills/shims/process"],
|
include: [
|
||||||
|
"vite-plugin-node-polyfills/shims/buffer",
|
||||||
|
"vite-plugin-node-polyfills/shims/process",
|
||||||
|
"@vector-im/compound-design-tokens/assets/web/icons",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
|||||||
Reference in New Issue
Block a user