Skip to content

Commit 4d66a85

Browse files
authored
Add options to hide header and composer of room view for the module api (#31095)
* feat: add props to hide header in `RoomView` * feat: add props to hide composer in `RoomView` * feat: pass `RoomViewProps` to room view in `renderRoomView` * refactor: add doc and use existing types * test: add tests for new room view props
1 parent 52eb8a9 commit 4d66a85

File tree

4 files changed

+491
-29
lines changed

4 files changed

+491
-29
lines changed

src/components/structures/RoomView.tsx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,16 @@ interface IRoomProps extends RoomViewProps {
165165
* Omitting this will mean that RoomView renders for the room held in SDKContext.RoomViewStore.
166166
*/
167167
roomId?: string;
168+
169+
/*
170+
* If true, hide the header
171+
*/
172+
hideHeader?: boolean;
173+
174+
/*
175+
* If true, hide the composer
176+
*/
177+
hideComposer?: boolean;
168178
}
169179

170180
export { MainSplitContentType };
@@ -2455,6 +2465,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
24552465

24562466
let messageComposer;
24572467
const showComposer =
2468+
!this.props.hideComposer &&
24582469
!isRoomEncryptionLoading &&
24592470
// joined and not showing search results
24602471
myMembership === KnownMembership.Join &&
@@ -2665,10 +2676,12 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
26652676
ref={this.roomViewBody}
26662677
data-layout={this.state.layout}
26672678
>
2668-
<RoomHeader
2669-
room={this.state.room}
2670-
additionalButtons={this.state.viewRoomOpts.buttons}
2671-
/>
2679+
{!this.props.hideHeader && (
2680+
<RoomHeader
2681+
room={this.state.room}
2682+
additionalButtons={this.state.viewRoomOpts.buttons}
2683+
/>
2684+
)}
26722685
{mainSplitBody}
26732686
</div>
26742687
</MainSplit>

src/modules/BuiltinsApi.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import { MatrixClientPeg } from "../MatrixClientPeg";
1212
import type { Room } from "matrix-js-sdk/src/matrix";
1313

1414
interface RoomViewPropsWithRoomId extends RoomViewProps {
15+
/**
16+
* The ID of the room to display
17+
*/
1518
roomId?: string;
1619
}
1720

@@ -26,9 +29,8 @@ interface Components {
2629
}
2730

2831
export class ElementWebBuiltinsApi implements BuiltinsApi {
29-
private _roomView?: React.ComponentType<RoomViewPropsWithRoomId>;
30-
private _roomAvatar?: React.ComponentType<RoomAvatarProps>;
31-
32+
private _roomView?: Components["roomView"];
33+
private _roomAvatar?: Components["roomAvatar"];
3234
/**
3335
* Sets the components used by the API.
3436
*
@@ -59,9 +61,9 @@ export class ElementWebBuiltinsApi implements BuiltinsApi {
5961
return this._roomAvatar;
6062
}
6163

62-
public renderRoomView(roomId: string): React.ReactNode {
64+
public renderRoomView(roomId: string, props?: RoomViewProps): React.ReactNode {
6365
const Component = this.getRoomViewComponent();
64-
return <Component roomId={roomId} />;
66+
return <Component roomId={roomId} {...props} />;
6567
}
6668

6769
public renderRoomAvatar(roomId: string, size?: string): React.ReactNode {

test/unit-tests/components/structures/RoomView-test.tsx

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ import MatrixClientContext from "../../../../src/contexts/MatrixClientContext";
7575
import { type ViewUserPayload } from "../../../../src/dispatcher/payloads/ViewUserPayload.ts";
7676
import { CallStore } from "../../../../src/stores/CallStore.ts";
7777
import MediaDeviceHandler, { MediaDeviceKindEnum } from "../../../../src/MediaDeviceHandler.ts";
78-
import Modal from "../../../../src/Modal.tsx";
78+
import Modal, { type ComponentProps } from "../../../../src/Modal.tsx";
7979
import ErrorDialog from "../../../../src/components/views/dialogs/ErrorDialog.tsx";
8080

8181
// Used by group calls
@@ -127,7 +127,10 @@ describe("RoomView", () => {
127127
cleanup();
128128
});
129129

130-
const mountRoomView = async (ref?: RefObject<RoomView | null>): Promise<RenderResult> => {
130+
const mountRoomView = async (
131+
ref?: RefObject<RoomView | null>,
132+
props?: Partial<ComponentProps<typeof RoomView>>,
133+
): Promise<RenderResult> => {
131134
if (stores.roomViewStore.getRoomId() !== room.roomId) {
132135
const switchedRoom = new Promise<void>((resolve) => {
133136
const subFn = () => {
@@ -159,6 +162,7 @@ describe("RoomView", () => {
159162
threepidInvite={undefined as any}
160163
forceTimeline={false}
161164
ref={ref}
165+
{...props}
162166
/>
163167
</SDKContext.Provider>
164168
</MatrixClientContext.Provider>,
@@ -250,6 +254,25 @@ describe("RoomView", () => {
250254
expect(instance.getHiddenHighlightCount()).toBe(0);
251255
});
252256

257+
it("should hide the composer when hideComposer=true", async () => {
258+
// Join the room
259+
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Join);
260+
const { asFragment } = await mountRoomView(undefined, { hideComposer: true });
261+
262+
expect(screen.queryByRole("textbox", { name: "Send an unencrypted message…" })).not.toBeInTheDocument();
263+
expect(asFragment()).toMatchSnapshot();
264+
});
265+
266+
it("should hide the header when hideHeader=true", async () => {
267+
// Join the room
268+
jest.spyOn(room, "getMyMembership").mockReturnValue(KnownMembership.Join);
269+
const { asFragment } = await mountRoomView(undefined, { hideHeader: true });
270+
271+
// Check that the room name button in the header is not rendered
272+
expect(screen.queryByRole("button", { name: room.name })).not.toBeInTheDocument();
273+
expect(asFragment()).toMatchSnapshot();
274+
});
275+
253276
describe("invites", () => {
254277
beforeEach(() => {
255278
const member = new RoomMember(room.roomId, cli.getSafeUserId());

0 commit comments

Comments
 (0)