From 8e3f82922ec113f37a2ef5c23a1ed828d7b7730a Mon Sep 17 00:00:00 2001 From: Michael Weimann Date: Wed, 12 Oct 2022 10:41:01 +0200 Subject: [PATCH] Extract voice broadcast header --- res/css/_components.pcss | 1 + res/css/_spacing.pcss | 1 + res/css/components/atoms/_Icon.pcss | 4 + .../atoms/_VoiceBroadcastHeader.pcss | 44 ++++++++++ src/components/atoms/Icon.tsx | 1 + .../components/VoiceBroadcastBody.tsx | 7 +- .../components/atoms/VoiceBroadcastHeader.tsx | 55 ++++++++++++ .../molecules/VoiceBroadcastRecordingBody.tsx | 29 +++--- src/voice-broadcast/index.ts | 1 + .../components/VoiceBroadcastBody-test.tsx | 18 ++-- .../atoms/VoiceBroadcastHeader-test.tsx | 88 +++++++++++++++++++ .../VoiceBroadcastHeader-test.tsx.snap | 77 ++++++++++++++++ .../VoiceBroadcastRecordingBody-test.tsx | 40 ++++----- .../VoiceBroadcastRecordingBody-test.tsx.snap | 26 ++---- 14 files changed, 314 insertions(+), 78 deletions(-) create mode 100644 res/css/voice-broadcast/atoms/_VoiceBroadcastHeader.pcss create mode 100644 src/voice-broadcast/components/atoms/VoiceBroadcastHeader.tsx create mode 100644 test/voice-broadcast/components/atoms/VoiceBroadcastHeader-test.tsx create mode 100644 test/voice-broadcast/components/atoms/__snapshots__/VoiceBroadcastHeader-test.tsx.snap diff --git a/res/css/_components.pcss b/res/css/_components.pcss index efb3e9e5944b..faaf20894847 100644 --- a/res/css/_components.pcss +++ b/res/css/_components.pcss @@ -364,4 +364,5 @@ @import "./views/voip/_PiPContainer.pcss"; @import "./views/voip/_VideoFeed.pcss"; @import "./voice-broadcast/atoms/_LiveBadge.pcss"; +@import "./voice-broadcast/atoms/_VoiceBroadcastHeader.pcss"; @import "./voice-broadcast/molecules/_VoiceBroadcastRecordingBody.pcss"; diff --git a/res/css/_spacing.pcss b/res/css/_spacing.pcss index 40c470c26b68..cc2dff129333 100644 --- a/res/css/_spacing.pcss +++ b/res/css/_spacing.pcss @@ -17,6 +17,7 @@ limitations under the License. /* 1rem :: 10px */ $spacing-4: 4px; +$spacing-6: 6px; $spacing-8: 8px; $spacing-12: 12px; $spacing-16: 16px; diff --git a/res/css/components/atoms/_Icon.pcss b/res/css/components/atoms/_Icon.pcss index 08a72d3d5baa..1db3278fe934 100644 --- a/res/css/components/atoms/_Icon.pcss +++ b/res/css/components/atoms/_Icon.pcss @@ -32,3 +32,7 @@ limitations under the License. .mx_Icon_live-badge { background-color: #fff; } + +.mx_Icon_compound-secondary-content { + background-color: $secondary-content; +} diff --git a/res/css/voice-broadcast/atoms/_VoiceBroadcastHeader.pcss b/res/css/voice-broadcast/atoms/_VoiceBroadcastHeader.pcss new file mode 100644 index 000000000000..0856fb0430bb --- /dev/null +++ b/res/css/voice-broadcast/atoms/_VoiceBroadcastHeader.pcss @@ -0,0 +1,44 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_VoiceBroadcastHeader { + align-items: flex-start; + display: flex; + gap: $spacing-8; + line-height: 20px; + margin-bottom: $spacing-8; + width: 266px; +} + +.mx_VoiceBroadcastHeader_sender { + font-size: $font-12px; + font-weight: $font-semi-bold; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.mx_VoiceBroadcastHeader_room { + font-size: $font-12px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.mx_VoiceBroadcastHeader_line { + align-items: center; + color: $secondary-content; + font-size: $font-12px; + display: flex; + gap: $spacing-6; +} diff --git a/src/components/atoms/Icon.tsx b/src/components/atoms/Icon.tsx index bb6ea61524a1..6ec0194cc98f 100644 --- a/src/components/atoms/Icon.tsx +++ b/src/components/atoms/Icon.tsx @@ -29,6 +29,7 @@ const iconTypeMap = new Map([ export enum IconColour { Accent = "accent", LiveBadge = "live-badge", + CompoundSecondaryContent = "compound-secondary-content", } export enum IconSize { diff --git a/src/voice-broadcast/components/VoiceBroadcastBody.tsx b/src/voice-broadcast/components/VoiceBroadcastBody.tsx index 35913255851a..e36460b9f383 100644 --- a/src/voice-broadcast/components/VoiceBroadcastBody.tsx +++ b/src/voice-broadcast/components/VoiceBroadcastBody.tsx @@ -46,13 +46,10 @@ export const VoiceBroadcastBody: React.FC = ({ mxEvent }) => { recording.stop(); }; - const senderId = mxEvent.getSender(); - const sender = mxEvent.sender; return ; }; diff --git a/src/voice-broadcast/components/atoms/VoiceBroadcastHeader.tsx b/src/voice-broadcast/components/atoms/VoiceBroadcastHeader.tsx new file mode 100644 index 000000000000..d7175db30baa --- /dev/null +++ b/src/voice-broadcast/components/atoms/VoiceBroadcastHeader.tsx @@ -0,0 +1,55 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from "react"; +import { RoomMember } from "matrix-js-sdk/src/matrix"; + +import MemberAvatar from "../../../components/views/avatars/MemberAvatar"; +import { LiveBadge } from "../.."; +import { Icon, IconColour, IconType } from "../../../components/atoms/Icon"; +import { _t } from "../../../languageHandler"; + +interface VoiceBroadcastHeaderProps { + live: boolean; + sender: RoomMember; + roomName: string; + showBroadcast?: boolean; +} + +export const VoiceBroadcastHeader: React.FC = ({ + live, + sender, + roomName, + showBroadcast = false, +}) => { + const broadcast = showBroadcast + ?
+ + { _t("Voice broadcast") } +
+ : null; + const liveBadge = live ? : null; + return
+ +
+
+ { sender.name } +
+
+ { roomName } +
+ { broadcast } +
+ { liveBadge } +
; +}; diff --git a/src/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody.tsx b/src/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody.tsx index 13ea504ac431..0db9bb92e1b3 100644 --- a/src/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody.tsx +++ b/src/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody.tsx @@ -17,40 +17,31 @@ limitations under the License. import React, { MouseEventHandler } from "react"; import { RoomMember } from "matrix-js-sdk/src/matrix"; -import { LiveBadge } from "../.."; -import MemberAvatar from "../../../components/views/avatars/MemberAvatar"; +import { VoiceBroadcastHeader } from "../.."; interface VoiceBroadcastRecordingBodyProps { live: boolean; - member: RoomMember; onClick: MouseEventHandler; - title: string; - userId: string; + roomName: string; + sender: RoomMember; } export const VoiceBroadcastRecordingBody: React.FC = ({ live, - member, onClick, - title, - userId, + roomName, + sender, }) => { - const liveBadge = live - ? - : null; - return (
- -
-
- { title } -
-
- { liveBadge } +
); }; diff --git a/src/voice-broadcast/index.ts b/src/voice-broadcast/index.ts index 2f69b84918c2..c5379cced64a 100644 --- a/src/voice-broadcast/index.ts +++ b/src/voice-broadcast/index.ts @@ -24,6 +24,7 @@ import { RelationType } from "matrix-js-sdk/src/matrix"; export * from "./audio/VoiceBroadcastRecorder"; export * from "./components/VoiceBroadcastBody"; export * from "./components/atoms/LiveBadge"; +export * from "./components/atoms/VoiceBroadcastHeader"; export * from "./components/molecules/VoiceBroadcastRecordingBody"; export * from "./models/VoiceBroadcastRecording"; export * from "./stores/VoiceBroadcastRecordingsStore"; diff --git a/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx b/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx index 2c94ea6d0b81..d6f9da6a680d 100644 --- a/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx +++ b/test/voice-broadcast/components/VoiceBroadcastBody-test.tsx @@ -72,9 +72,8 @@ describe("VoiceBroadcastBody", () => { { onClick: expect.any(Function), live: true, - member: infoEvent.sender, - userId: client.getUserId(), - title: "@userId:matrix.org • test room", + sender: infoEvent.sender, + roomName: room.name, }, {}, ); @@ -89,9 +88,8 @@ describe("VoiceBroadcastBody", () => { { onClick: expect.any(Function), live: false, - member: infoEvent.sender, - userId: client.getUserId(), - title: "@userId:matrix.org • test room", + sender: infoEvent.sender, + roomName: room.name, }, {}, ); @@ -105,17 +103,17 @@ describe("VoiceBroadcastBody", () => { mocked(VoiceBroadcastRecordingBody).mockImplementation( ({ live, - member: _member, + sender, onClick, - title, - userId: _userId, + roomName, }) => { return (
-
{ title }
+
{ sender.name }
+
{ roomName }
{ live && "Live" }
); diff --git a/test/voice-broadcast/components/atoms/VoiceBroadcastHeader-test.tsx b/test/voice-broadcast/components/atoms/VoiceBroadcastHeader-test.tsx new file mode 100644 index 000000000000..36fc6d37e0e5 --- /dev/null +++ b/test/voice-broadcast/components/atoms/VoiceBroadcastHeader-test.tsx @@ -0,0 +1,88 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from "react"; +import { Container } from "react-dom"; +import { RoomMember } from "matrix-js-sdk/src/matrix"; +import { render, RenderResult } from "@testing-library/react"; + +import MemberAvatar from "../../../../src/components/views/avatars/MemberAvatar"; +import { VoiceBroadcastHeader } from "../../../../src/voice-broadcast"; +import { Icon } from "../../../../src/components/atoms/Icon"; + +jest.mock("../../../../src/components/views/avatars/MemberAvatar", () => ({ + __esModule: true, + default: ({ member, fallbackUserId }: React.ComponentProps) => { + return
+ { member.name }, + { fallbackUserId } +
; + }, +})); + +jest.mock("../../../../src/voice-broadcast/components/atoms/LiveBadge", () => ({ + LiveBadge: () => { + return
; + }, +})); + +jest.mock("../../../../src/components/atoms/Icon", () => ({ + ...jest.requireActual("../../../../src/components/atoms/Icon") as object, + Icon: ({ type, colour }: React.ComponentProps) => { + return
+ type: { type }, + colour: { colour } +
; + }, +})); + +describe("VoiceBroadcastHeader", () => { + const userId = "@user:example.com"; + const roomId = "!room:example.com"; + const roomName = "test room"; + const sender = new RoomMember(roomId, userId); + let container: Container; + + const renderHeader = (live: boolean, showBroadcast: boolean = undefined): RenderResult => { + return render(); + }; + + beforeAll(() => { + sender.name = "test user"; + }); + + describe("when rendering a live broadcast header with broadcast info", () => { + beforeEach(() => { + container = renderHeader(true, true).container; + }); + + it("should render the header with a live badge", () => { + expect(container).toMatchSnapshot(); + }); + }); + + describe("when rendering a non-live broadcast header", () => { + beforeEach(() => { + container = renderHeader(false).container; + }); + + it("should render the header without a live badge", () => { + expect(container).toMatchSnapshot(); + }); + }); +}); diff --git a/test/voice-broadcast/components/atoms/__snapshots__/VoiceBroadcastHeader-test.tsx.snap b/test/voice-broadcast/components/atoms/__snapshots__/VoiceBroadcastHeader-test.tsx.snap new file mode 100644 index 000000000000..0a0031b09c84 --- /dev/null +++ b/test/voice-broadcast/components/atoms/__snapshots__/VoiceBroadcastHeader-test.tsx.snap @@ -0,0 +1,77 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`VoiceBroadcastHeader when rendering a live broadcast header with broadcast info should render the header with a live badge 1`] = ` +
+
+
+ test user + , + @user:example.com +
+
+
+ test user +
+
+ test room +
+
+
+ type: + 0 + , colour: + compound-secondary-content +
+ Voice broadcast +
+
+
+
+
+`; + +exports[`VoiceBroadcastHeader when rendering a non-live broadcast header should render the header without a live badge 1`] = ` +
+
+
+ test user + , + @user:example.com +
+
+
+ test user +
+
+ test room +
+
+
+
+`; diff --git a/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody-test.tsx b/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody-test.tsx index 3f797025fb27..d9cb83642b26 100644 --- a/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody-test.tsx +++ b/test/voice-broadcast/components/molecules/VoiceBroadcastRecordingBody-test.tsx @@ -18,23 +18,27 @@ import React, { MouseEventHandler } from "react"; import { render, RenderResult } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { RoomMember } from "matrix-js-sdk/src/matrix"; -import { mocked } from "jest-mock"; -import { VoiceBroadcastRecordingBody } from "../../../../src/voice-broadcast"; -import MemberAvatar from "../../../../src/components/views/avatars/MemberAvatar"; +import { VoiceBroadcastHeader, VoiceBroadcastRecordingBody } from "../../../../src/voice-broadcast"; -jest.mock("../../../../src/components/views/avatars/MemberAvatar", () => jest.fn()); +jest.mock("../../../../src/voice-broadcast/components/atoms/VoiceBroadcastHeader", () => ({ + VoiceBroadcastHeader: ({ live, sender, roomName }: React.ComponentProps) => { + return
+ live: { live }, + sender: { sender.userId }, + room name: { roomName } +
; + }, +})); describe("VoiceBroadcastRecordingBody", () => { - const title = "Test Title"; + const testRoomName = "test room name"; const userId = "@user:example.com"; const roomMember = new RoomMember("!room:example.com", userId); let onClick: MouseEventHandler; beforeEach(() => { onClick = jest.fn(); - // @ts-ignore - mocked(MemberAvatar).mockReturnValue(
); }); describe("when rendered", () => { @@ -44,10 +48,9 @@ describe("VoiceBroadcastRecordingBody", () => { renderResult = render( , ); }); @@ -56,19 +59,9 @@ describe("VoiceBroadcastRecordingBody", () => { expect(renderResult.container).toMatchSnapshot(); }); - it("should pass the props to MemberAvatar", () => { - expect(mocked(MemberAvatar)).toHaveBeenCalledWith( - { - member: roomMember, - fallbackUserId: userId, - }, - {}, - ); - }); - describe("and clicked", () => { beforeEach(async () => { - await userEvent.click(renderResult.getByText(title)); + await userEvent.click(renderResult.getByTestId("voice-broadcast-header")); }); it("should call the onClick prop", () => { @@ -84,10 +77,9 @@ describe("VoiceBroadcastRecordingBody", () => { renderResult = render( , ); }); diff --git a/test/voice-broadcast/components/molecules/__snapshots__/VoiceBroadcastRecordingBody-test.tsx.snap b/test/voice-broadcast/components/molecules/__snapshots__/VoiceBroadcastRecordingBody-test.tsx.snap index 6ea866ec5669..a642e8dffcac 100644 --- a/test/voice-broadcast/components/molecules/__snapshots__/VoiceBroadcastRecordingBody-test.tsx.snap +++ b/test/voice-broadcast/components/molecules/__snapshots__/VoiceBroadcastRecordingBody-test.tsx.snap @@ -6,27 +6,13 @@ exports[`VoiceBroadcastRecordingBody when rendered should render the expected HT class="mx_VoiceBroadcastRecordingBody" >
-
-
- Test Title -
-
-
-