diff --git a/apps/web/src/components/views/elements/RoomTopic.tsx b/apps/web/src/components/views/elements/RoomTopic.tsx
index f7b7751bb3..543037dad5 100644
--- a/apps/web/src/components/views/elements/RoomTopic.tsx
+++ b/apps/web/src/components/views/elements/RoomTopic.tsx
@@ -22,6 +22,7 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
import AccessibleButton, { type ButtonEvent } from "./AccessibleButton";
import { topicToHtml } from "../../../HtmlUtils";
import { tryTransformPermalinkToLocalHref } from "../../../utils/permalinks/Permalinks";
+import { showSpaceSettings } from "../../../utils/space";
interface IProps {
room: Room;
@@ -76,7 +77,13 @@ export default function RoomTopic({ room, className }: IProps): JSX.Element {
kind="primary_outline"
onClick={() => {
modal.close();
- dis.dispatch({ action: "open_room_settings" });
+ // SpaceRoomView renders this component for spaces too, and a space
+ // has its own settings dialog.
+ if (room.isSpaceRoom()) {
+ showSpaceSettings(room);
+ } else {
+ dis.dispatch({ action: "open_room_settings" });
+ }
}}
>
{_t("room|edit_topic")}
diff --git a/apps/web/test/unit-tests/components/views/elements/RoomTopic-test.tsx b/apps/web/test/unit-tests/components/views/elements/RoomTopic-test.tsx
index e53af36b71..56cd3b83eb 100644
--- a/apps/web/test/unit-tests/components/views/elements/RoomTopic-test.tsx
+++ b/apps/web/test/unit-tests/components/views/elements/RoomTopic-test.tsx
@@ -17,6 +17,9 @@ import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
import RoomTopic from "../../../../../src/components/views/elements/RoomTopic";
import dis from "../../../../../src/dispatcher/dispatcher";
import { Action } from "../../../../../src/dispatcher/actions";
+import Modal from "../../../../../src/Modal";
+import { type ActionPayload } from "../../../../../src/dispatcher/payloads";
+import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
jest.mock("../../../../../src/dispatcher/dispatcher");
@@ -94,6 +97,62 @@ describe("", () => {
expect(dis.fire).toHaveBeenCalledWith(Action.ShowRoomTopic);
});
+ describe("the edit topic button in the topic dialog", () => {
+ /**
+ * Render the topic, open the topic dialog and render its contents, so the
+ * "Edit topic" button can be clicked.
+ * @param isSpaceRoom whether the room should report itself as a space
+ */
+ function renderTopicDialog(isSpaceRoom: boolean) {
+ const room = createRoom("a topic");
+ jest.spyOn(room, "isSpaceRoom").mockReturnValue(isSpaceRoom);
+ jest.spyOn(room.currentState, "maySendStateEvent").mockReturnValue(true);
+
+ const createDialog = jest
+ .spyOn(Modal, "createDialog")
+ .mockReturnValue({ close: jest.fn() } as unknown as ReturnType);
+
+ render(, {
+ wrapper: ({ children }) => (
+
+ {children}
+
+ ),
+ });
+
+ // The dispatcher is mocked for this suite, so drive the registered handler directly
+ // instead of firing Action.ShowRoomTopic through it.
+ const handler = jest.mocked(dis.register).mock.calls.at(-1)![0] as (payload: ActionPayload) => void;
+ handler({ action: Action.ShowRoomTopic });
+
+ const { description } = createDialog.mock.calls.at(-1)![1] as { description: React.ReactNode };
+ render(
+
+ <>{description}>
+ ,
+ );
+
+ return room;
+ }
+
+ it("opens space settings for a space", () => {
+ const room = renderTopicDialog(true);
+
+ fireEvent.click(screen.getByRole("button", { name: "Edit topic" }));
+
+ expect(dis.dispatch).toHaveBeenCalledWith({ action: Action.OpenSpaceSettings, space: room });
+ expect(dis.dispatch).not.toHaveBeenCalledWith({ action: "open_room_settings" });
+ });
+
+ it("opens room settings for a room", () => {
+ renderTopicDialog(false);
+
+ fireEvent.click(screen.getByRole("button", { name: "Edit topic" }));
+
+ expect(dis.dispatch).toHaveBeenCalledWith({ action: "open_room_settings" });
+ });
+ });
+
it("should open the tooltip when hovering a text", async () => {
const topic = "room topic";
renderRoom(topic);