New room list: avoid extra render for room list item (#29752)

* fix: avoid extra render in the new room list

* fix: listen to room name changes

* fix: trigger render when notification state change

* test: fix room list item tests

* chore: fix typo `RoomNotificationState.isUnsentMessage`

* refactor: move `isNotificationDecorationVisible` into `useRoomListItemViewModel`

* refactor: recalculate notification values on notification state changes

* refactor: rename `isNotificationDecorationVisible` to `showNotificationDecoration`

* test: add test for room list item view

* test: add notification tests in room list item vm

* fix: listen to notification updates in `NotificationDecoration`

* test: update notification decoration tests

* refactor: display notification decoration according to vm

* test: update room list item view tests

* fix: a11y label computation after room name change

* refactor: improve notification handling
This commit is contained in:
Florian Duros
2025-04-16 23:40:36 +02:00
committed by GitHub
parent 6767e4d6ad
commit fd455179f7
9 changed files with 290 additions and 55 deletions
@@ -8,60 +8,94 @@
import React from "react";
import { render, screen } from "jest-matrix-react";
import { type RoomNotificationState } from "../../../../../src/stores/notifications/RoomNotificationState";
import { RoomNotificationState } from "../../../../../src/stores/notifications/RoomNotificationState";
import { NotificationDecoration } from "../../../../../src/components/views/rooms/NotificationDecoration";
import { createTestClient, mkStubRoom } from "../../../../test-utils";
describe("<NotificationDecoration />", () => {
it("should not render if RoomNotificationState.isSilent=true", () => {
const state = { hasAnyNotificationOrActivity: false } as RoomNotificationState;
render(<NotificationDecoration notificationState={state} hasVideoCall={false} />);
let roomNotificationState: RoomNotificationState;
beforeEach(() => {
const matrixClient = createTestClient();
const room = mkStubRoom("roomId", "roomName", matrixClient);
roomNotificationState = new RoomNotificationState(room, false);
});
it("should not render if RoomNotificationState.hasAnyNotificationOrActivity=true", () => {
jest.spyOn(roomNotificationState, "hasAnyNotificationOrActivity", "get").mockReturnValue(false);
render(<NotificationDecoration notificationState={roomNotificationState} hasVideoCall={false} />);
expect(screen.queryByTestId("notification-decoration")).toBeNull();
});
it("should render the unset message decoration", () => {
const state = { hasAnyNotificationOrActivity: true, isUnsetMessage: true } as RoomNotificationState;
const { asFragment } = render(<NotificationDecoration notificationState={state} hasVideoCall={false} />);
jest.spyOn(roomNotificationState, "hasAnyNotificationOrActivity", "get").mockReturnValue(true);
jest.spyOn(roomNotificationState, "isUnsentMessage", "get").mockReturnValue(true);
const { asFragment } = render(
<NotificationDecoration notificationState={roomNotificationState} hasVideoCall={false} />,
);
expect(asFragment()).toMatchSnapshot();
});
it("should render the invitation decoration", () => {
const state = { hasAnyNotificationOrActivity: true, invited: true } as RoomNotificationState;
const { asFragment } = render(<NotificationDecoration notificationState={state} hasVideoCall={false} />);
jest.spyOn(roomNotificationState, "hasAnyNotificationOrActivity", "get").mockReturnValue(true);
jest.spyOn(roomNotificationState, "invited", "get").mockReturnValue(true);
const { asFragment } = render(
<NotificationDecoration notificationState={roomNotificationState} hasVideoCall={false} />,
);
expect(asFragment()).toMatchSnapshot();
});
it("should render the mention decoration", () => {
const state = { hasAnyNotificationOrActivity: true, isMention: true, count: 1 } as RoomNotificationState;
const { asFragment } = render(<NotificationDecoration notificationState={state} hasVideoCall={false} />);
jest.spyOn(roomNotificationState, "hasAnyNotificationOrActivity", "get").mockReturnValue(true);
jest.spyOn(roomNotificationState, "isMention", "get").mockReturnValue(true);
jest.spyOn(roomNotificationState, "count", "get").mockReturnValue(1);
const { asFragment } = render(
<NotificationDecoration notificationState={roomNotificationState} hasVideoCall={false} />,
);
expect(asFragment()).toMatchSnapshot();
});
it("should render the notification decoration", () => {
const state = { hasAnyNotificationOrActivity: true, isNotification: true, count: 1 } as RoomNotificationState;
const { asFragment } = render(<NotificationDecoration notificationState={state} hasVideoCall={false} />);
jest.spyOn(roomNotificationState, "hasAnyNotificationOrActivity", "get").mockReturnValue(true);
jest.spyOn(roomNotificationState, "isNotification", "get").mockReturnValue(true);
jest.spyOn(roomNotificationState, "count", "get").mockReturnValue(1);
const { asFragment } = render(
<NotificationDecoration notificationState={roomNotificationState} hasVideoCall={false} />,
);
expect(asFragment()).toMatchSnapshot();
});
it("should render the notification decoration without count", () => {
const state = { hasAnyNotificationOrActivity: true, isNotification: true, count: 0 } as RoomNotificationState;
const { asFragment } = render(<NotificationDecoration notificationState={state} hasVideoCall={false} />);
jest.spyOn(roomNotificationState, "hasAnyNotificationOrActivity", "get").mockReturnValue(true);
jest.spyOn(roomNotificationState, "isNotification", "get").mockReturnValue(true);
jest.spyOn(roomNotificationState, "count", "get").mockReturnValue(0);
const { asFragment } = render(
<NotificationDecoration notificationState={roomNotificationState} hasVideoCall={false} />,
);
expect(asFragment()).toMatchSnapshot();
});
it("should render the activity decoration", () => {
const state = { hasAnyNotificationOrActivity: true, isActivityNotification: true } as RoomNotificationState;
const { asFragment } = render(<NotificationDecoration notificationState={state} hasVideoCall={false} />);
jest.spyOn(roomNotificationState, "hasAnyNotificationOrActivity", "get").mockReturnValue(true);
jest.spyOn(roomNotificationState, "isActivityNotification", "get").mockReturnValue(true);
const { asFragment } = render(
<NotificationDecoration notificationState={roomNotificationState} hasVideoCall={false} />,
);
expect(asFragment()).toMatchSnapshot();
});
it("should render the muted decoration", () => {
const state = { hasAnyNotificationOrActivity: true, muted: true } as RoomNotificationState;
const { asFragment } = render(<NotificationDecoration notificationState={state} hasVideoCall={false} />);
jest.spyOn(roomNotificationState, "hasAnyNotificationOrActivity", "get").mockReturnValue(true);
jest.spyOn(roomNotificationState, "muted", "get").mockReturnValue(true);
const { asFragment } = render(
<NotificationDecoration notificationState={roomNotificationState} hasVideoCall={false} />,
);
expect(asFragment()).toMatchSnapshot();
});
it("should render the video decoration", () => {
const state = { hasAnyNotificationOrActivity: false } as RoomNotificationState;
const { asFragment } = render(<NotificationDecoration notificationState={state} hasVideoCall={true} />);
jest.spyOn(roomNotificationState, "hasAnyNotificationOrActivity", "get").mockReturnValue(false);
const { asFragment } = render(
<NotificationDecoration notificationState={roomNotificationState} hasVideoCall={true} />,
);
expect(asFragment()).toMatchSnapshot();
});
});