Replace Icon with webpack loaded SVG (#9464)

This commit is contained in:
Michael Weimann 2022-10-20 10:04:14 +02:00 committed by GitHub
parent 6fe8744e4d
commit 3c9ba3e69f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 298 additions and 564 deletions

View file

@ -4,7 +4,7 @@
@import "./_font-sizes.pcss";
@import "./_font-weights.pcss";
@import "./_spacing.pcss";
@import "./components/atoms/_Icon.pcss";
@import "./compound/_Icon.pcss";
@import "./components/views/beacon/_BeaconListItem.pcss";
@import "./components/views/beacon/_BeaconStatus.pcss";
@import "./components/views/beacon/_BeaconStatusTooltip.pcss";
@ -369,6 +369,7 @@
@import "./views/voip/_VideoFeed.pcss";
@import "./voice-broadcast/atoms/_LiveBadge.pcss";
@import "./voice-broadcast/atoms/_PlaybackControlButton.pcss";
@import "./voice-broadcast/atoms/_VoiceBroadcastControl.pcss";
@import "./voice-broadcast/atoms/_VoiceBroadcastHeader.pcss";
@import "./voice-broadcast/molecules/_VoiceBroadcastPlaybackBody.pcss";
@import "./voice-broadcast/molecules/_VoiceBroadcastRecordingBody.pcss";

View file

@ -14,13 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
/*
* Compound icon
* {@link https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed}
*/
.mx_Icon {
box-sizing: border-box;
display: inline-block;
mask-origin: content-box;
mask-position: center;
mask-repeat: no-repeat;
mask-size: contain;
padding: 1px;
}
@ -28,15 +29,3 @@ limitations under the License.
height: 16px;
width: 16px;
}
.mx_Icon_accent {
background-color: $accent;
}
.mx_Icon_live-badge {
background-color: #fff;
}
.mx_Icon_compound-secondary-content {
background-color: $secondary-content;
}

View file

@ -0,0 +1,27 @@
/*
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_VoiceBroadcastControl {
align-items: center;
background-color: $background;
border-radius: 50%;
color: $secondary-content;
display: flex;
height: 32px;
justify-content: center;
margin-bottom: $spacing-8;
width: 32px;
}

View file

@ -1,3 +1,3 @@
<svg width="13" height="16" viewBox="0 0 13 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.973633" y="2" width="12" height="12" rx="1" fill="#737D8C"/>
<rect x="0.973633" y="2" width="12" height="12" rx="1" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 175 B

After

Width:  |  Height:  |  Size: 180 B

View file

@ -5,54 +5,23 @@
viewBox="0 0 21.799 21.799"
fill="none"
version="1.1"
id="svg12"
sodipodi:docname="live.svg"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs16" />
<sodipodi:namedview
id="namedview14"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="35.416667"
inkscape:cx="8.7670588"
inkscape:cy="8.1882353"
inkscape:window-width="1920"
inkscape:window-height="1053"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg12" />
<path
d="m 19.188575,3.0855891 c -0.3391,-0.43594 -0.9674,-0.51448 -1.4033,-0.17541 -0.4355,0.3387 -0.5143,0.96596 -0.1766,1.40185 l 8e-4,9.8e-4 0.001,0.00129 0.0136,0.01816 c 0.0133,0.0178 0.0346,0.04686 0.0629,0.0867 0.0566,0.07972 0.1408,0.20227 0.2435,0.36368 0.2058,0.32333 0.4838,0.79947 0.7625,1.39672 0.5603,1.20054 1.1062,2.85339 1.1062,4.7199899 0,1.8666 -0.5459,3.5194 -1.1062,4.72 -0.2787,0.5972 -0.5567,1.0733 -0.7625,1.3967 -0.1027,0.1614 -0.1869,0.2839 -0.2435,0.3637 -0.0283,0.0398 -0.0496,0.0689 -0.0629,0.0867 l -0.0136,0.0181 -0.001,0.0013 -9e-4,0.0012 c -0.3376,0.4358 -0.2587,1.063 0.1767,1.4016 0.4359,0.3391 1.0642,0.2606 1.4033,-0.1754 l -0.7453,-0.5796 c 0.7453,0.5796 0.7453,0.5796 0.7453,0.5796 l 0.002,-0.0025 0.0028,-0.0038 0.0083,-0.0108 0.0265,-0.0352 c 0.0219,-0.0294 0.0521,-0.0707 0.0895,-0.1232 0.0746,-0.1051 0.1779,-0.2558 0.3002,-0.448 0.2442,-0.3838 0.5662,-0.9362 0.8875,-1.6247 0.6397,-1.3709 1.2938,-3.318 1.2938,-5.5657 0,-2.2477199 -0.6541,-4.1948699 -1.2938,-5.5657599 -0.3213,-0.68846 -0.6433,-1.2409 -0.8875,-1.6247 -0.1223,-0.19217 -0.2256,-0.34283 -0.3002,-0.44793 -0.0374,-0.05258 -0.0676,-0.09382 -0.0895,-0.12323 l -0.0265,-0.03521 -0.0083,-0.01085 -0.0028,-0.00371 -0.0012,-0.00143 c 0,0 -8e-4,-0.00114 -0.7902,0.6128 z"
fill="#c1c6cd"
id="path2" />
fill="currentColor" />
<path
d="m 16.589375,6.6858091 c -0.339,-0.43595 -0.9673,-0.51448 -1.4033,-0.17541 -0.4348,0.33819 -0.514,0.96407 -0.178,1.39987 l 0.0034,0.00458 c 0.0045,0.00599 0.0129,0.01748 0.0248,0.03422 0.0238,0.03351 0.0612,0.08776 0.1076,0.16077 0.0933,0.14655 0.2213,0.36554 0.35,0.64137 0.2603,0.55764 0.5062,1.3105399 0.5062,2.1485399 0,0.838 -0.2459,1.5909 -0.5062,2.1485 -0.1287,0.2759 -0.2567,0.4949 -0.35,0.6414 -0.0464,0.073 -0.0838,0.1273 -0.1076,0.1608 -0.0119,0.0167 -0.0203,0.0282 -0.0248,0.0342 l -0.0034,0.0046 c -0.336,0.4358 -0.2568,1.0617 0.178,1.3999 0.436,0.339 1.0643,0.2605 1.4033,-0.1755 l -0.7893,-0.6139 c 0.7893,0.6139 0.7893,0.6139 0.7893,0.6139 l 0.0018,-0.0022 0.0022,-0.0029 0.0058,-0.0075 0.0164,-0.0219 c 0.0131,-0.0176 0.0305,-0.0412 0.0514,-0.0707 0.0418,-0.0589 0.0982,-0.1413 0.1643,-0.245 0.1317,-0.2071 0.3037,-0.5024 0.475,-0.8694 0.3397,-0.728 0.6938,-1.7752 0.6938,-2.9943 0,-1.2190999 -0.3541,-2.2662799 -0.6938,-2.9943099 -0.1713,-0.36703 -0.3433,-0.66233 -0.475,-0.86935 -0.0661,-0.10377 -0.1225,-0.18613 -0.1643,-0.24503 -0.0209,-0.02947 -0.0383,-0.05314 -0.0514,-0.07074 l -0.0164,-0.02187 -0.0058,-0.00748 -0.0022,-0.00287 -9e-4,-0.00122 c 0,0 -9e-4,-0.00107 -0.7902,0.61287 z"
fill="#c1c6cd"
id="path4" />
fill="currentColor" />
<path
d="m 2.6104749,18.713449 c 0.33907,0.4359 0.96734,0.5144 1.40329,0.1754 0.43547,-0.3387 0.5143,-0.966 0.17653,-1.4019 l -7.6e-4,-10e-4 -9.7e-4,-0.0013 -0.01365,-0.0181 c -0.01325,-0.0178 -0.0346,-0.0469 -0.06289,-0.0867 -0.05661,-0.0797 -0.14082,-0.2023 -0.24354,-0.3637 -0.20576,-0.3233 -0.48376,-0.7995 -0.76248,-1.3967 -0.56025,-1.2006 -1.10618,-2.8534 -1.10618,-4.72 0,-1.8665999 0.54593,-3.5194099 1.10618,-4.7199499 0.27872,-0.59726 0.55672,-1.07339 0.76248,-1.39673 0.10272,-0.1614 0.18693,-0.28396 0.24354,-0.36368 0.02829,-0.03983 0.04964,-0.0689 0.06289,-0.0867 l 0.01365,-0.01815 9.7e-4,-0.00129 9.1e-4,-0.00117 c 0.33761,-0.43588 0.25873,-1.06302 -0.17668,-1.40166 -0.43595,-0.33907 -1.06422,-0.26054 -1.40329,0.17541 l 0.74526,0.57964 c -0.74527,-0.57963 -0.74526,-0.57964 -0.74526,-0.57964 l -0.00199,0.00256 -0.00287,0.00372 -0.0083,0.01085 -0.02649,0.0352 c -0.0219,0.02941 -0.05212,0.07066 -0.08945,0.12323 -0.07464,0.10511 -0.17792,0.25577 -0.30021,0.44793 -0.24424,0.38381 -0.56624,0.93625 -0.88752,1.62471 -0.63975001,1.37088 -1.29382000681,3.31804 -1.29382000681,5.5657199 0,2.2477 0.65406999681,4.1949 1.29382000681,5.5658 0.32128,0.6884 0.64328,1.2409 0.88752,1.6247 0.12229,0.1921 0.22557,0.3428 0.30021,0.4479 0.03733,0.0526 0.06755,0.0938 0.08945,0.1232 l 0.02649,0.0352 0.0083,0.0109 0.00287,0.0037 0.0011,0.0014 c 0,0 8.9e-4,0.0012 0.79024,-0.6128 z"
fill="#c1c6cd"
id="path6" />
fill="currentColor" />
<path
d="m 5.2095949,15.113149 c 0.33907,0.436 0.96735,0.5145 1.40329,0.1754 0.43481,-0.3381 0.51407,-0.964 0.17806,-1.3998 l -0.00343,-0.0046 c -0.00447,-0.006 -0.01292,-0.0175 -0.0248,-0.0342 -0.0238,-0.0335 -0.06114,-0.0878 -0.10761,-0.1608 -0.09326,-0.1465 -0.22126,-0.3655 -0.34997,-0.6414 -0.26026,-0.5576 -0.50619,-1.3105 -0.50619,-2.1485 0,-0.838 0.24593,-1.5908999 0.50619,-2.1485499 0.12871,-0.27582 0.25671,-0.49481 0.34997,-0.64136 0.04647,-0.07302 0.08381,-0.12727 0.10761,-0.16078 0.01188,-0.01673 0.02033,-0.02822 0.0248,-0.03422 l 0.00344,-0.00458 c 0.336,-0.43581 0.25674,-1.06168 -0.17807,-1.39986 -0.43594,-0.33907 -1.06422,-0.26054 -1.40329,0.17541 l 0.78935,0.61394 c -0.78935,-0.61394 -0.78935,-0.61394 -0.78935,-0.61394 l -0.00177,0.00228 -0.00222,0.00287 -0.00572,0.00749 -0.01646,0.02186 c -0.01311,0.01761 -0.03044,0.04128 -0.05137,0.07075 -0.04182,0.0589 -0.09823,0.14125 -0.16427,0.24503 -0.13174,0.20702 -0.30374,0.50231 -0.47502,0.86934 -0.33975,0.72803 -0.69382,1.77522 -0.69382,2.9943199 0,1.2191 0.35407,2.2663 0.69382,2.9943 0.17128,0.367 0.34328,0.6623 0.47502,0.8694 0.06604,0.1037 0.12245,0.1861 0.16427,0.245 0.02093,0.0295 0.03826,0.0531 0.05137,0.0707 l 0.01646,0.0219 0.00572,0.0075 0.00222,0.0029 9.4e-4,0.0012 c 0,0 8.3e-4,10e-4 0.79018,-0.6129 z"
fill="#c1c6cd"
id="path8" />
fill="currentColor" />
<circle
cx="10.999774"
cy="10.898949"
r="2"
fill="#c1c6cd"
id="circle10" />
fill="currentColor" />
</svg>

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View file

@ -1,4 +1,4 @@
<svg width="10" height="12" viewBox="0 0 10 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 1C0 0.447715 0.447715 0 1 0H2C2.55228 0 3 0.447715 3 1V11C3 11.5523 2.55228 12 2 12H1C0.447715 12 0 11.5523 0 11V1Z" fill="#737D8C"/>
<path d="M7 1C7 0.447715 7.44772 0 8 0H9C9.55228 0 10 0.447715 10 1V11C10 11.5523 9.55228 12 9 12H8C7.44772 12 7 11.5523 7 11V1Z" fill="#737D8C"/>
<path d="M0 1C0 0.447715 0.447715 0 1 0H2C2.55228 0 3 0.447715 3 1V11C3 11.5523 2.55228 12 2 12H1C0.447715 12 0 11.5523 0 11V1Z" fill="currentColor"/>
<path d="M7 1C7 0.447715 7.44772 0 8 0H9C9.55228 0 10 0.447715 10 1V11C10 11.5523 9.55228 12 9 12H8C7.44772 12 7 11.5523 7 11V1Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 396 B

After

Width:  |  Height:  |  Size: 406 B

View file

@ -1,3 +1,3 @@
<svg width="13" height="16" viewBox="0 0 13 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 14.2104V1.78956C0 1.00724 0.857827 0.527894 1.5241 0.937906L11.6161 7.14834C12.2506 7.53883 12.2506 8.46117 11.6161 8.85166L1.5241 15.0621C0.857828 15.4721 0 14.9928 0 14.2104Z" fill="#737D8C"/>
<path d="M0 14.2104V1.78956C0 1.00724 0.857827 0.527894 1.5241 0.937906L11.6161 7.14834C12.2506 7.53883 12.2506 8.46117 11.6161 8.85166L1.5241 15.0621C0.857828 15.4721 0 14.9928 0 14.2104Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 310 B

After

Width:  |  Height:  |  Size: 315 B

View file

@ -1,3 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.09999 6.15C8.09999 3.99609 9.84608 2.25 12 2.25C14.1539 2.25 15.9 3.99609 15.9 6.15V11.9825C15.9 14.1365 14.1539 15.8825 12 15.8825C9.84608 15.8825 8.09999 14.1365 8.09999 11.9825V6.15ZM5.1748 10.7375C5.86516 10.7375 6.4248 11.2972 6.4248 11.9875C6.4248 15.0574 8.91483 17.5493 11.9915 17.5538C11.9943 17.5538 11.9972 17.5538 12 17.5538C12.0028 17.5538 12.0056 17.5538 12.0084 17.5538C15.085 17.5492 17.5748 15.0573 17.5748 11.9875C17.5748 11.2972 18.1344 10.7375 18.8248 10.7375C19.5152 10.7375 20.0748 11.2972 20.0748 11.9875C20.0748 16.0189 17.115 19.3576 13.25 19.9577V20.7513C13.25 21.4416 12.6904 22.0013 12 22.0013C11.3096 22.0013 10.75 21.4416 10.75 20.7513V19.9578C6.88482 19.3578 3.9248 16.0191 3.9248 11.9875C3.9248 11.2972 4.48445 10.7375 5.1748 10.7375Z" fill="#737D8C"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.09999 6.15C8.09999 3.99609 9.84608 2.25 12 2.25C14.1539 2.25 15.9 3.99609 15.9 6.15V11.9825C15.9 14.1365 14.1539 15.8825 12 15.8825C9.84608 15.8825 8.09999 14.1365 8.09999 11.9825V6.15ZM5.1748 10.7375C5.86516 10.7375 6.4248 11.2972 6.4248 11.9875C6.4248 15.0574 8.91483 17.5493 11.9915 17.5538C11.9943 17.5538 11.9972 17.5538 12 17.5538C12.0028 17.5538 12.0056 17.5538 12.0084 17.5538C15.085 17.5492 17.5748 15.0573 17.5748 11.9875C17.5748 11.2972 18.1344 10.7375 18.8248 10.7375C19.5152 10.7375 20.0748 11.2972 20.0748 11.9875C20.0748 16.0189 17.115 19.3576 13.25 19.9577V20.7513C13.25 21.4416 12.6904 22.0013 12 22.0013C11.3096 22.0013 10.75 21.4416 10.75 20.7513V19.9578C6.88482 19.3578 3.9248 16.0191 3.9248 11.9875C3.9248 11.2972 4.48445 10.7375 5.1748 10.7375Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 940 B

After

Width:  |  Height:  |  Size: 945 B

View file

@ -1,83 +0,0 @@
/*
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 liveIcon from "../../../res/img/element-icons/live.svg";
import microphoneIcon from "../../../res/img/voip/call-view/mic-on.svg";
import pauseIcon from "../../../res/img/element-icons/pause.svg";
import playIcon from "../../../res/img/element-icons/play.svg";
import stopIcon from "../../../res/img/element-icons/Stop.svg";
export enum IconType {
Live,
Microphone,
Pause,
Play,
Stop,
}
const iconTypeMap = new Map([
[IconType.Live, liveIcon],
[IconType.Microphone, microphoneIcon],
[IconType.Pause, pauseIcon],
[IconType.Play, playIcon],
[IconType.Stop, stopIcon],
]);
export enum IconColour {
Accent = "accent",
LiveBadge = "live-badge",
CompoundSecondaryContent = "compound-secondary-content",
}
export enum IconSize {
S16 = "16",
}
interface IconProps {
colour?: IconColour;
size?: IconSize;
type: IconType;
}
export const Icon: React.FC<IconProps> = ({
size = IconSize.S16,
colour = IconColour.Accent,
type,
...rest
}) => {
const classes = [
"mx_Icon",
`mx_Icon_${size}`,
`mx_Icon_${colour}`,
];
const styles: React.CSSProperties = {
maskImage: `url("${iconTypeMap.get(type)}")`,
WebkitMaskImage: `url("${iconTypeMap.get(type)}")`,
};
return (
<i
aria-hidden
className={classes.join(" ")}
role="presentation"
style={styles}
{...rest}
/>
);
};

View file

@ -644,10 +644,10 @@
"Stop live broadcasting?": "Stop live broadcasting?",
"Are you sure you want to stop your live broadcast?This will end the broadcast and the full recording will be available in the room.": "Are you sure you want to stop your live broadcast?This will end the broadcast and the full recording will be available in the room.",
"Yes, stop broadcast": "Yes, stop broadcast",
"Live": "Live",
"pause voice broadcast": "pause voice broadcast",
"play voice broadcast": "play voice broadcast",
"resume voice broadcast": "resume voice broadcast",
"stop voice broadcast": "stop voice broadcast",
"pause voice broadcast": "pause voice broadcast",
"Live": "Live",
"Voice broadcast": "Voice broadcast",
"Cannot reach homeserver": "Cannot reach homeserver",
"Ensure you have a stable internet connection, or get in touch with the server admin": "Ensure you have a stable internet connection, or get in touch with the server admin",

View file

@ -16,12 +16,12 @@ limitations under the License.
import React from "react";
import { Icon, IconColour, IconType } from "../../../components/atoms/Icon";
import { Icon as LiveIcon } from "../../../../res/img/element-icons/live.svg";
import { _t } from "../../../languageHandler";
export const LiveBadge: React.FC = () => {
return <div className="mx_LiveBadge">
<Icon type={IconType.Live} colour={IconColour.LiveBadge} />
<LiveIcon className="mx_Icon mx_Icon_16" />
{ _t("Live") }
</div>;
};

View file

@ -1,53 +0,0 @@
/*
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 { VoiceBroadcastPlaybackState } from "../..";
import { Icon, IconColour, IconType } from "../../../components/atoms/Icon";
import AccessibleButton from "../../../components/views/elements/AccessibleButton";
import { _t } from "../../../languageHandler";
const stateIconMap = new Map([
[VoiceBroadcastPlaybackState.Playing, IconType.Pause],
[VoiceBroadcastPlaybackState.Paused, IconType.Play],
[VoiceBroadcastPlaybackState.Stopped, IconType.Play],
]);
interface Props {
onClick: () => void;
state: VoiceBroadcastPlaybackState;
}
export const PlaybackControlButton: React.FC<Props> = ({
onClick,
state,
}) => {
const ariaLabel = state === VoiceBroadcastPlaybackState.Playing
? _t("pause voice broadcast")
: _t("resume voice broadcast");
return <AccessibleButton
className="mx_BroadcastPlaybackControlButton"
onClick={onClick}
aria-label={ariaLabel}
>
<Icon
colour={IconColour.CompoundSecondaryContent}
type={stateIconMap.get(state)}
/>
</AccessibleButton>;
};

View file

@ -16,25 +16,24 @@ limitations under the License.
import React from "react";
import { Icon, IconColour, IconType } from "../../../components/atoms/Icon";
import AccessibleButton from "../../../components/views/elements/AccessibleButton";
import { _t } from "../../../languageHandler";
interface Props {
icon: React.FC<React.SVGProps<SVGSVGElement>>;
label: string;
onClick: () => void;
}
export const StopButton: React.FC<Props> = ({
export const VoiceBroadcastControl: React.FC<Props> = ({
icon: Icon,
label,
onClick,
}) => {
return <AccessibleButton
className="mx_BroadcastPlaybackControlButton"
className="mx_VoiceBroadcastControl"
onClick={onClick}
aria-label={_t("stop voice broadcast")}
aria-label={label}
>
<Icon
colour={IconColour.CompoundSecondaryContent}
type={IconType.Stop}
/>
<Icon className="mx_Icon mx_Icon_16" />
</AccessibleButton>;
};

View file

@ -15,7 +15,8 @@ import React from "react";
import { Room, RoomMember } from "matrix-js-sdk/src/matrix";
import { LiveBadge } from "../..";
import { Icon, IconColour, IconType } from "../../../components/atoms/Icon";
import { Icon as LiveIcon } from "../../../../res/img/element-icons/live.svg";
import { Icon as MicrophoneIcon } from "../../../../res/img/voip/call-view/mic-on.svg";
import { _t } from "../../../languageHandler";
import RoomAvatar from "../../../components/views/avatars/RoomAvatar";
@ -34,7 +35,7 @@ export const VoiceBroadcastHeader: React.FC<VoiceBroadcastHeaderProps> = ({
}) => {
const broadcast = showBroadcast
? <div className="mx_VoiceBroadcastHeader_line">
<Icon type={IconType.Live} colour={IconColour.CompoundSecondaryContent} />
<LiveIcon className="mx_Icon mx_Icon_16" />
{ _t("Voice broadcast") }
</div>
: null;
@ -46,7 +47,7 @@ export const VoiceBroadcastHeader: React.FC<VoiceBroadcastHeaderProps> = ({
{ room.name }
</div>
<div className="mx_VoiceBroadcastHeader_line">
<Icon type={IconType.Microphone} colour={IconColour.CompoundSecondaryContent} />
<MicrophoneIcon className="mx_Icon mx_Icon_16" />
<span>{ sender.name }</span>
</div>
{ broadcast }

View file

@ -17,13 +17,16 @@ limitations under the License.
import React from "react";
import {
PlaybackControlButton,
VoiceBroadcastControl,
VoiceBroadcastHeader,
VoiceBroadcastPlayback,
VoiceBroadcastPlaybackState,
} from "../..";
import Spinner from "../../../components/views/elements/Spinner";
import { useVoiceBroadcastPlayback } from "../../hooks/useVoiceBroadcastPlayback";
import { Icon as PlayIcon } from "../../../../res/img/element-icons/play.svg";
import { Icon as PauseIcon } from "../../../../res/img/element-icons/pause.svg";
import { _t } from "../../../languageHandler";
interface VoiceBroadcastPlaybackBodyProps {
playback: VoiceBroadcastPlayback;
@ -40,9 +43,35 @@ export const VoiceBroadcastPlaybackBody: React.FC<VoiceBroadcastPlaybackBodyProp
playbackState,
} = useVoiceBroadcastPlayback(playback);
const control = playbackState === VoiceBroadcastPlaybackState.Buffering
? <Spinner />
: <PlaybackControlButton onClick={toggle} state={playbackState} />;
let control: React.ReactNode;
if (playbackState === VoiceBroadcastPlaybackState.Buffering) {
control = <Spinner />;
} else {
let controlIcon: React.FC<React.SVGProps<SVGSVGElement>>;
let controlLabel: string;
switch (playbackState) {
case VoiceBroadcastPlaybackState.Stopped:
controlIcon = PlayIcon;
controlLabel = _t("play voice broadcast");
break;
case VoiceBroadcastPlaybackState.Paused:
controlIcon = PlayIcon;
controlLabel = _t("resume voice broadcast");
break;
case VoiceBroadcastPlaybackState.Playing:
controlIcon = PauseIcon;
controlLabel = _t("pause voice broadcast");
break;
}
control = <VoiceBroadcastControl
label={controlLabel}
icon={controlIcon}
onClick={toggle}
/>;
}
return (
<div className="mx_VoiceBroadcastPlaybackBody">

View file

@ -17,11 +17,12 @@ limitations under the License.
import React from "react";
import {
StopButton,
VoiceBroadcastControl,
VoiceBroadcastRecording,
} from "../..";
import { useVoiceBroadcastRecording } from "../../hooks/useVoiceBroadcastRecording";
import { VoiceBroadcastHeader } from "../atoms/VoiceBroadcastHeader";
import { Icon as StopIcon } from "../../../../res/img/element-icons/Stop.svg";
interface VoiceBroadcastRecordingPipProps {
recording: VoiceBroadcastRecording;
@ -45,7 +46,11 @@ export const VoiceBroadcastRecordingPip: React.FC<VoiceBroadcastRecordingPipProp
/>
<hr className="mx_VoiceBroadcastRecordingPip_divider" />
<div className="mx_VoiceBroadcastRecordingPip_controls">
<StopButton onClick={stopRecording} />
<VoiceBroadcastControl
icon={StopIcon}
label="Stop Recording"
onClick={stopRecording}
/>
</div>
</div>;
};

View file

@ -26,8 +26,7 @@ export * from "./models/VoiceBroadcastRecording";
export * from "./audio/VoiceBroadcastRecorder";
export * from "./components/VoiceBroadcastBody";
export * from "./components/atoms/LiveBadge";
export * from "./components/atoms/PlaybackControlButton";
export * from "./components/atoms/StopButton";
export * from "./components/atoms/VoiceBroadcastControl";
export * from "./components/atoms/VoiceBroadcastHeader";
export * from "./components/molecules/VoiceBroadcastPlaybackBody";
export * from "./components/molecules/VoiceBroadcastRecordingBody";

View file

@ -1,47 +0,0 @@
/*
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 { render } from "@testing-library/react";
import { Icon, IconColour, IconSize, IconType } from "../../../src/components/atoms/Icon";
describe("Icon", () => {
it.each([
IconColour.Accent,
IconColour.LiveBadge,
])("should render the colour %s", (colour: IconColour) => {
const { container } = render(
<Icon
colour={colour}
type={IconType.Live}
/>,
);
expect(container).toMatchSnapshot();
});
it.each([
IconSize.S16,
])("should render the size %s", (size: IconSize) => {
const { container } = render(
<Icon
size={size}
type={IconType.Live}
/>,
);
expect(container).toMatchSnapshot();
});
});

View file

@ -1,34 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Icon should render the colour accent 1`] = `
<div>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_accent"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
/>
</div>
`;
exports[`Icon should render the colour live-badge 1`] = `
<div>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_live-badge"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
/>
</div>
`;
exports[`Icon should render the size 16 1`] = `
<div>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_accent"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
/>
</div>
`;

View file

@ -1,45 +0,0 @@
/*
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 { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { PlaybackControlButton, VoiceBroadcastPlaybackState } from "../../../../src/voice-broadcast";
describe("PlaybackControlButton", () => {
let onClick: () => void;
beforeEach(() => {
onClick = jest.fn();
});
it.each([
[VoiceBroadcastPlaybackState.Playing],
[VoiceBroadcastPlaybackState.Paused],
[VoiceBroadcastPlaybackState.Stopped],
])("should render state »%s« as expected", (state: VoiceBroadcastPlaybackState) => {
const result = render(<PlaybackControlButton state={state} onClick={onClick} />);
expect(result.container).toMatchSnapshot();
});
it("should call onClick on click", async () => {
render(<PlaybackControlButton state={VoiceBroadcastPlaybackState.Playing} onClick={onClick} />);
const button = screen.getByLabelText("pause voice broadcast");
await userEvent.click(button);
expect(onClick).toHaveBeenCalled();
});
});

View file

@ -1,45 +0,0 @@
/*
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 { render, RenderResult } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { StopButton } from "../../../../src/voice-broadcast";
describe("StopButton", () => {
let result: RenderResult;
let onClick: () => {};
beforeEach(() => {
onClick = jest.fn();
result = render(<StopButton onClick={onClick} />);
});
it("should render as expected", () => {
expect(result.container).toMatchSnapshot();
});
describe("when clicking it", () => {
beforeEach(async () => {
await userEvent.click(result.getByLabelText("stop voice broadcast"));
});
it("should invoke the callback", () => {
expect(onClick).toHaveBeenCalled();
});
});
});

View file

@ -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 { render, RenderResult, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { VoiceBroadcastControl } from "../../../../src/voice-broadcast";
import { Icon as StopIcon } from "../../../../res/img/element-icons/Stop.svg";
describe("VoiceBroadcastControl", () => {
let result: RenderResult;
let onClick: () => void;
beforeEach(() => {
onClick = jest.fn();
});
describe("when rendering it", () => {
beforeEach(() => {
result = render(<VoiceBroadcastControl
onClick={onClick}
label="test label"
icon={StopIcon}
/>);
});
it("should render as expected", () => {
expect(result.container).toMatchSnapshot();
});
describe("when clicking it", () => {
beforeEach(async () => {
await userEvent.click(screen.getByLabelText("test label"));
});
it("should call onClick", () => {
expect(onClick).toHaveBeenCalled();
});
});
});
});

View file

@ -5,11 +5,8 @@ exports[`LiveBadge should render the expected HTML 1`] = `
<div
class="mx_LiveBadge"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_live-badge"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>

View file

@ -1,55 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PlaybackControlButton should render state »0« as expected 1`] = `
<div>
<div
aria-label="resume voice broadcast"
class="mx_AccessibleButton mx_BroadcastPlaybackControlButton"
role="button"
tabindex="0"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_compound-secondary-content"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
/>
</div>
</div>
`;
exports[`PlaybackControlButton should render state »1« as expected 1`] = `
<div>
<div
aria-label="pause voice broadcast"
class="mx_AccessibleButton mx_BroadcastPlaybackControlButton"
role="button"
tabindex="0"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_compound-secondary-content"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
/>
</div>
</div>
`;
exports[`PlaybackControlButton should render state »2« as expected 1`] = `
<div>
<div
aria-label="resume voice broadcast"
class="mx_AccessibleButton mx_BroadcastPlaybackControlButton"
role="button"
tabindex="0"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_compound-secondary-content"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
/>
</div>
</div>
`;

View file

@ -1,19 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`StopButton should render as expected 1`] = `
<div>
<div
aria-label="stop voice broadcast"
class="mx_AccessibleButton mx_BroadcastPlaybackControlButton"
role="button"
tabindex="0"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_compound-secondary-content"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
/>
</div>
</div>
`;

View file

@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`VoiceBroadcastControl when rendering it should render as expected 1`] = `
<div>
<div
aria-label="test label"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_16"
/>
</div>
</div>
`;

View file

@ -22,11 +22,8 @@ exports[`VoiceBroadcastHeader when rendering a live broadcast header with broadc
<div
class="mx_VoiceBroadcastHeader_line"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_compound-secondary-content"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
<span>
test user
@ -35,11 +32,8 @@ exports[`VoiceBroadcastHeader when rendering a live broadcast header with broadc
<div
class="mx_VoiceBroadcastHeader_line"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_compound-secondary-content"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
Voice broadcast
</div>
@ -47,11 +41,8 @@ exports[`VoiceBroadcastHeader when rendering a live broadcast header with broadc
<div
class="mx_LiveBadge"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_live-badge"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
@ -81,11 +72,8 @@ exports[`VoiceBroadcastHeader when rendering a non-live broadcast header should
<div
class="mx_VoiceBroadcastHeader_line"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_compound-secondary-content"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
<span>
test user

View file

@ -64,9 +64,6 @@ describe("VoiceBroadcastPlaybackBody", () => {
describe("when rendering a buffering voice broadcast", () => {
beforeEach(() => {
mocked(playback.getState).mockReturnValue(VoiceBroadcastPlaybackState.Buffering);
});
beforeEach(() => {
renderResult = render(<VoiceBroadcastPlaybackBody playback={playback} />);
});
@ -75,18 +72,15 @@ describe("VoiceBroadcastPlaybackBody", () => {
});
});
describe("when rendering a broadcast", () => {
describe(`when rendering a ${VoiceBroadcastPlaybackState.Stopped} broadcast`, () => {
beforeEach(() => {
mocked(playback.getState).mockReturnValue(VoiceBroadcastPlaybackState.Stopped);
renderResult = render(<VoiceBroadcastPlaybackBody playback={playback} />);
});
it("should render as expected", () => {
expect(renderResult.container).toMatchSnapshot();
});
describe("and clicking the play button", () => {
beforeEach(async () => {
await userEvent.click(renderResult.getByLabelText("resume voice broadcast"));
await userEvent.click(renderResult.getByLabelText("play voice broadcast"));
});
it("should toggle the recording", () => {
@ -94,4 +88,18 @@ describe("VoiceBroadcastPlaybackBody", () => {
});
});
});
describe.each([
VoiceBroadcastPlaybackState.Paused,
VoiceBroadcastPlaybackState.Playing,
])("when rendering a %s broadcast", (playbackState: VoiceBroadcastPlaybackState) => {
beforeEach(() => {
mocked(playback.getState).mockReturnValue(playbackState);
renderResult = render(<VoiceBroadcastPlaybackBody playback={playback} />);
});
it("should render as expected", () => {
expect(renderResult.container).toMatchSnapshot();
});
});
});

View file

@ -69,7 +69,7 @@ describe("VoiceBroadcastRecordingPip", () => {
describe("and clicking the stop button", () => {
beforeEach(async () => {
await userEvent.click(screen.getByLabelText("stop voice broadcast"));
await userEvent.click(screen.getByLabelText("Stop Recording"));
// modal rendering has some weird sleeps
await sleep(100);
});

View file

@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`VoiceBroadcastPlaybackBody when rendering a broadcast should render as expected 1`] = `
exports[`VoiceBroadcastPlaybackBody when rendering a 0 broadcast should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastPlaybackBody"
@ -25,11 +25,8 @@ exports[`VoiceBroadcastPlaybackBody when rendering a broadcast should render as
<div
class="mx_VoiceBroadcastHeader_line"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_compound-secondary-content"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
<span>
@user:example.com
@ -38,11 +35,8 @@ exports[`VoiceBroadcastPlaybackBody when rendering a broadcast should render as
<div
class="mx_VoiceBroadcastHeader_line"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_compound-secondary-content"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
Voice broadcast
</div>
@ -50,11 +44,8 @@ exports[`VoiceBroadcastPlaybackBody when rendering a broadcast should render as
<div
class="mx_LiveBadge"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_live-badge"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
@ -64,15 +55,80 @@ exports[`VoiceBroadcastPlaybackBody when rendering a broadcast should render as
>
<div
aria-label="resume voice broadcast"
class="mx_AccessibleButton mx_BroadcastPlaybackControlButton"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_compound-secondary-content"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
</div>
</div>
</div>
</div>
`;
exports[`VoiceBroadcastPlaybackBody when rendering a 1 broadcast should render as expected 1`] = `
<div>
<div
class="mx_VoiceBroadcastPlaybackBody"
>
<div
class="mx_VoiceBroadcastHeader"
>
<div
data-testid="room-avatar"
>
room avatar:
My room
</div>
<div
class="mx_VoiceBroadcastHeader_content"
>
<div
class="mx_VoiceBroadcastHeader_room"
>
My room
</div>
<div
class="mx_VoiceBroadcastHeader_line"
>
<div
class="mx_Icon mx_Icon_16"
/>
<span>
@user:example.com
</span>
</div>
<div
class="mx_VoiceBroadcastHeader_line"
>
<div
class="mx_Icon mx_Icon_16"
/>
Voice broadcast
</div>
</div>
<div
class="mx_LiveBadge"
>
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
</div>
<div
class="mx_VoiceBroadcastPlaybackBody_controls"
>
<div
aria-label="pause voice broadcast"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<div
class="mx_Icon mx_Icon_16"
/>
</div>
</div>
@ -105,11 +161,8 @@ exports[`VoiceBroadcastPlaybackBody when rendering a buffering voice broadcast s
<div
class="mx_VoiceBroadcastHeader_line"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_compound-secondary-content"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
<span>
@user:example.com
@ -118,11 +171,8 @@ exports[`VoiceBroadcastPlaybackBody when rendering a buffering voice broadcast s
<div
class="mx_VoiceBroadcastHeader_line"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_compound-secondary-content"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
Voice broadcast
</div>
@ -130,11 +180,8 @@ exports[`VoiceBroadcastPlaybackBody when rendering a buffering voice broadcast s
<div
class="mx_LiveBadge"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_live-badge"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>

View file

@ -25,11 +25,8 @@ exports[`VoiceBroadcastRecordingBody when rendering a live broadcast should rend
<div
class="mx_VoiceBroadcastHeader_line"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_compound-secondary-content"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
<span>
@user:example.com
@ -39,11 +36,8 @@ exports[`VoiceBroadcastRecordingBody when rendering a live broadcast should rend
<div
class="mx_LiveBadge"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_live-badge"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>

View file

@ -25,11 +25,8 @@ exports[`VoiceBroadcastRecordingPip when rendering should create the expected re
<div
class="mx_VoiceBroadcastHeader_line"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_compound-secondary-content"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
<span>
@user:example.com
@ -39,11 +36,8 @@ exports[`VoiceBroadcastRecordingPip when rendering should create the expected re
<div
class="mx_LiveBadge"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_live-badge"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
Live
</div>
@ -55,16 +49,13 @@ exports[`VoiceBroadcastRecordingPip when rendering should create the expected re
class="mx_VoiceBroadcastRecordingPip_controls"
>
<div
aria-label="stop voice broadcast"
class="mx_AccessibleButton mx_BroadcastPlaybackControlButton"
aria-label="Stop Recording"
class="mx_AccessibleButton mx_VoiceBroadcastControl"
role="button"
tabindex="0"
>
<i
aria-hidden="true"
class="mx_Icon mx_Icon_16 mx_Icon_compound-secondary-content"
role="presentation"
style="mask-image: url(\\"image-file-stub\\");"
<div
class="mx_Icon mx_Icon_16"
/>
</div>
</div>