Make shared-components tiles render identically outside Element Web - # 2 (#33485)
* Adding correct layout/density attributes to unit tests * Move app/web styling to FileBodyView * Unify styling for MediaBody wrapped components * Fix prettier issues * Update app/web snapshots * Wrap file download and audio loading states in MediaBody * Revert the MediaBody for download buttons. * Revert changes for MediaBody wrappers * Updated snaphots for MediaBody components
@@ -223,12 +223,10 @@
|
|||||||
@import "./views/messages/_CreateEvent.pcss";
|
@import "./views/messages/_CreateEvent.pcss";
|
||||||
@import "./views/messages/_DisambiguatedProfile.pcss";
|
@import "./views/messages/_DisambiguatedProfile.pcss";
|
||||||
@import "./views/messages/_LegacyCallEvent.pcss";
|
@import "./views/messages/_LegacyCallEvent.pcss";
|
||||||
@import "./views/messages/_MFileBody.pcss";
|
|
||||||
@import "./views/messages/_MImageReplyBody.pcss";
|
@import "./views/messages/_MImageReplyBody.pcss";
|
||||||
@import "./views/messages/_MLocationBody.pcss";
|
@import "./views/messages/_MLocationBody.pcss";
|
||||||
@import "./views/messages/_MPollBody.pcss";
|
@import "./views/messages/_MPollBody.pcss";
|
||||||
@import "./views/messages/_MStickerBody.pcss";
|
@import "./views/messages/_MStickerBody.pcss";
|
||||||
@import "./views/messages/_MediaBody.pcss";
|
|
||||||
@import "./views/messages/_MessageActionBar.pcss";
|
@import "./views/messages/_MessageActionBar.pcss";
|
||||||
@import "./views/messages/_ReactionsRow.pcss";
|
@import "./views/messages/_ReactionsRow.pcss";
|
||||||
@import "./views/messages/_TextualEvent.pcss";
|
@import "./views/messages/_TextualEvent.pcss";
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
/* are shared amongst multiple voice message components. */
|
/* are shared amongst multiple voice message components. */
|
||||||
|
|
||||||
/* Container for live recording and playback controls */
|
/* Container for live recording and playback controls */
|
||||||
.mx_MediaBody.mx_VoiceMessagePrimaryContainer {
|
.mx_VoiceMessagePrimaryContainer {
|
||||||
/* Match mx_MediaBody spacing, offsetting the waveform's 1px internal right padding. */
|
/* Match mx_MediaBody spacing, offsetting the waveform's 1px internal right padding. */
|
||||||
padding: 6px 11px 6px 12px;
|
padding: 6px 11px 6px 12px;
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2024 New Vector Ltd.
|
|
||||||
Copyright 2015-2021 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
.mx_MFileBody [data-type="download"] {
|
|
||||||
height: var(--cpd-space-9x);
|
|
||||||
|
|
||||||
& object {
|
|
||||||
margin-left: -16px;
|
|
||||||
padding-right: 4px;
|
|
||||||
margin-top: -4px;
|
|
||||||
vertical-align: middle;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MFileBody [data-type="info"] {
|
|
||||||
svg {
|
|
||||||
color: $secondary-content !important;
|
|
||||||
background-color: $system !important;
|
|
||||||
border-radius: 20px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2024 New Vector Ltd.
|
|
||||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* A "media body" is any file upload looking thing, apart from images and videos (they */
|
|
||||||
/* have unique styles). */
|
|
||||||
|
|
||||||
.mx_MediaBody {
|
|
||||||
background-color: $panels;
|
|
||||||
border-radius: 12px;
|
|
||||||
max-width: 243px; /* use max-width instead of width so it fits within right panels */
|
|
||||||
|
|
||||||
color: $secondary-content;
|
|
||||||
font: var(--cpd-font-body-md-regular);
|
|
||||||
line-height: $font-24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MAudioBody > .mx_MediaBody {
|
|
||||||
border-radius: var(--MBody-border-radius);
|
|
||||||
}
|
|
||||||
@@ -7,6 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { type ReactNode } from "react";
|
import React, { type ReactNode } from "react";
|
||||||
|
import { MediaBody } from "@element-hq/web-shared-components";
|
||||||
|
|
||||||
import PlayPauseButton from "./PlayPauseButton";
|
import PlayPauseButton from "./PlayPauseButton";
|
||||||
import PlaybackClock from "./PlaybackClock";
|
import PlaybackClock from "./PlaybackClock";
|
||||||
@@ -74,8 +75,8 @@ export default class RecordingPlayback extends AudioPlayerBase<IProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<MediaBody
|
||||||
className="mx_MediaBody mx_VoiceMessagePrimaryContainer"
|
className="mx_VoiceMessagePrimaryContainer"
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
data-testid="recording-playback"
|
data-testid="recording-playback"
|
||||||
>
|
>
|
||||||
@@ -85,7 +86,7 @@ export default class RecordingPlayback extends AudioPlayerBase<IProps> {
|
|||||||
ref={this.playPauseRef}
|
ref={this.playPauseRef}
|
||||||
/>
|
/>
|
||||||
{body}
|
{body}
|
||||||
</div>
|
</MediaBody>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { type ReactNode } from "react";
|
import React, { type ReactNode } from "react";
|
||||||
|
import { MediaBody } from "@element-hq/web-shared-components";
|
||||||
import { type Room, type IEventRelation, type MatrixEvent } from "matrix-js-sdk/src/matrix";
|
import { type Room, type IEventRelation, type MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { DeleteIcon, StopSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
import { DeleteIcon, StopSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
@@ -239,10 +240,10 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
|
|||||||
|
|
||||||
// only other UI is the recording-in-progress UI
|
// only other UI is the recording-in-progress UI
|
||||||
return (
|
return (
|
||||||
<div className="mx_MediaBody mx_VoiceMessagePrimaryContainer mx_VoiceRecordComposerTile_recording">
|
<MediaBody className="mx_VoiceMessagePrimaryContainer mx_VoiceRecordComposerTile_recording">
|
||||||
<LiveRecordingClock recorder={this.state.recorder} />
|
<LiveRecordingClock recorder={this.state.recorder} />
|
||||||
<LiveRecordingWaveform recorder={this.state.recorder} />
|
<LiveRecordingWaveform recorder={this.state.recorder} />
|
||||||
</div>
|
</MediaBody>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
exports[`MBodyFactory renderMBody fallback shows m.audio generic placeholder when showFileInfo is true 1`] = `
|
exports[`MBodyFactory renderMBody fallback shows m.audio generic placeholder when showFileInfo is true 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
class="_content_1t2mx_8 mx_MFileBody"
|
class="_content_bibip_8 mx_MFileBody"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_MediaBody _mediaBody_rgndh_8"
|
class="mx_MediaBody _mediaBody_14jys_8"
|
||||||
data-type="info"
|
data-type="info"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
@@ -44,10 +44,10 @@ exports[`MBodyFactory renderMBody fallback shows m.audio generic placeholder whe
|
|||||||
exports[`MBodyFactory renderMBody fallback shows m.file generic placeholder when showFileInfo is true 1`] = `
|
exports[`MBodyFactory renderMBody fallback shows m.file generic placeholder when showFileInfo is true 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
class="_content_1t2mx_8 mx_MFileBody"
|
class="_content_bibip_8 mx_MFileBody"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="mx_MediaBody _mediaBody_rgndh_8"
|
class="mx_MediaBody _mediaBody_14jys_8"
|
||||||
data-type="info"
|
data-type="info"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
@@ -82,7 +82,7 @@ exports[`MBodyFactory renderMBody fallback shows m.file generic placeholder when
|
|||||||
exports[`MBodyFactory renderMBody renders download button for m.file in file rendering type 1`] = `
|
exports[`MBodyFactory renderMBody renders download button for m.file in file rendering type 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<span
|
<span
|
||||||
class="_content_1t2mx_8 mx_MFileBody"
|
class="_content_bibip_8 mx_MFileBody"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
data-type="download"
|
data-type="download"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 30 KiB |
@@ -29,7 +29,9 @@ describe("TileErrorView", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("renders the bubble layout variant", () => {
|
it("renders the bubble layout variant", () => {
|
||||||
const { container } = render(<BubbleLayout />);
|
const { container } = render(<BubbleLayout />, {
|
||||||
|
presentation: { layout: "bubble" },
|
||||||
|
});
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ exports[`TileErrorView > renders the bubble layout variant 1`] = `
|
|||||||
<li
|
<li
|
||||||
class="TileErrorView-module_tileErrorView"
|
class="TileErrorView-module_tileErrorView"
|
||||||
data-event-density="default"
|
data-event-density="default"
|
||||||
data-event-layout="group"
|
data-event-layout="bubble"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="TileErrorView-module_line"
|
class="TileErrorView-module_line"
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ describe("UrlPreviewGroupView", () => {
|
|||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
it("renders with compact density", () => {
|
it("renders with compact density", () => {
|
||||||
const { container } = render(<WithCompactView />);
|
const { container } = render(<WithCompactView />, {
|
||||||
|
presentation: { density: "compact" },
|
||||||
|
});
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -342,7 +342,7 @@ exports[`UrlPreviewGroupView > renders with compact density 1`] = `
|
|||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="UrlPreviewGroupView-module_wrapper"
|
class="UrlPreviewGroupView-module_wrapper"
|
||||||
data-event-density="default"
|
data-event-density="compact"
|
||||||
data-event-layout="group"
|
data-event-layout="group"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
.audioPlayer {
|
.audioPlayer {
|
||||||
padding: var(--cpd-space-4x) var(--cpd-space-3x) var(--cpd-space-3x) var(--cpd-space-3x);
|
padding: var(--cpd-space-4x) var(--cpd-space-3x) var(--cpd-space-3x) var(--cpd-space-3x);
|
||||||
border-radius: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mediaInfo {
|
.mediaInfo {
|
||||||
|
|||||||
@@ -39,6 +39,9 @@
|
|||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
padding: var(--cpd-space-2x);
|
padding: var(--cpd-space-2x);
|
||||||
|
color: var(--cpd-color-text-secondary);
|
||||||
|
background-color: var(--cpd-color-bg-subtle-primary);
|
||||||
|
border-radius: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
& span {
|
& span {
|
||||||
@@ -57,6 +60,7 @@
|
|||||||
|
|
||||||
.content [data-type="download"] {
|
.content [data-type="download"] {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
height: var(--cpd-space-9x);
|
||||||
|
|
||||||
& iframe {
|
& iframe {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
contain: paint;
|
contain: paint;
|
||||||
border-radius: var(--MBody-border-radius);
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder {
|
.placeholder {
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
max-width: min(100%, 350px);
|
max-width: min(100%, 350px);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: var(--cpd-space-1x);
|
padding: var(--cpd-space-1x);
|
||||||
border-radius: var(--MBody-border-radius);
|
border-radius: 8px;
|
||||||
background-color: rgb(0 0 0 / 0.6);
|
background-color: rgb(0 0 0 / 0.6);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
.container {
|
.container {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: var(--MBody-border-radius);
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video {
|
.video {
|
||||||
|
|||||||
@@ -6,12 +6,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.mediaBody {
|
.mediaBody {
|
||||||
|
color: var(--cpd-color-text-secondary);
|
||||||
background-color: var(--cpd-color-bg-subtle-secondary);
|
background-color: var(--cpd-color-bg-subtle-secondary);
|
||||||
border-radius: 12px;
|
border-radius: 8px;
|
||||||
max-width: 243px; /* use max-width instead of width so it fits within right panels */
|
max-width: 243px; /* use max-width instead of width so it fits within right panels */
|
||||||
|
|
||||||
font: var(--cpd-font-body-md-regular);
|
|
||||||
color: var(--cpd-color-text-secondary);
|
|
||||||
|
|
||||||
padding: var(--cpd-space-1-5x) var(--cpd-space-3x);
|
padding: var(--cpd-space-1-5x) var(--cpd-space-3x);
|
||||||
|
|
||||||
|
font: var(--cpd-font-body-md-regular);
|
||||||
|
line-height: var(--cpd-font-size-heading-md);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,32 +16,49 @@ import React, { type ReactElement } from "react";
|
|||||||
import { render, type RenderOptions } from "@testing-library/react";
|
import { render, type RenderOptions } from "@testing-library/react";
|
||||||
import { TooltipProvider } from "@vector-im/compound-web";
|
import { TooltipProvider } from "@vector-im/compound-web";
|
||||||
|
|
||||||
import { I18nApi, I18nContext } from "../..";
|
import {
|
||||||
|
DEFAULT_EVENT_PRESENTATION,
|
||||||
|
EventPresentationProvider,
|
||||||
|
I18nApi,
|
||||||
|
I18nContext,
|
||||||
|
type EventPresentation,
|
||||||
|
} from "../..";
|
||||||
|
|
||||||
const wrapWithTooltipProvider = (Wrapper: RenderOptions["wrapper"]) => {
|
type SharedRenderOptions = RenderOptions & {
|
||||||
|
presentation?: Partial<EventPresentation>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapWithTooltipProvider = (Wrapper: RenderOptions["wrapper"], presentation?: Partial<EventPresentation>) => {
|
||||||
return ({ children }: { children: React.ReactNode }) => {
|
return ({ children }: { children: React.ReactNode }) => {
|
||||||
|
const resolvedPresentation: EventPresentation | undefined = presentation
|
||||||
|
? { ...DEFAULT_EVENT_PRESENTATION, ...presentation }
|
||||||
|
: undefined;
|
||||||
|
const content = resolvedPresentation ? (
|
||||||
|
<EventPresentationProvider value={resolvedPresentation}>
|
||||||
|
<TooltipProvider>{children}</TooltipProvider>
|
||||||
|
</EventPresentationProvider>
|
||||||
|
) : (
|
||||||
|
<TooltipProvider>{children}</TooltipProvider>
|
||||||
|
);
|
||||||
|
|
||||||
if (Wrapper) {
|
if (Wrapper) {
|
||||||
return (
|
return (
|
||||||
<I18nContext.Provider value={new I18nApi()}>
|
<I18nContext.Provider value={new I18nApi()}>
|
||||||
<Wrapper>
|
<Wrapper>{content}</Wrapper>
|
||||||
<TooltipProvider>{children}</TooltipProvider>
|
|
||||||
</Wrapper>
|
|
||||||
</I18nContext.Provider>
|
</I18nContext.Provider>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return <I18nContext.Provider value={new I18nApi()}>{content}</I18nContext.Provider>;
|
||||||
<I18nContext.Provider value={new I18nApi()}>
|
|
||||||
<TooltipProvider>{children}</TooltipProvider>
|
|
||||||
</I18nContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const customRender = (ui: ReactElement, options: RenderOptions = {}): ReturnType<typeof render> => {
|
const customRender = (ui: ReactElement, options: SharedRenderOptions = {}): ReturnType<typeof render> => {
|
||||||
|
const { presentation, wrapper, ...renderOptions } = options;
|
||||||
|
|
||||||
return render(ui, {
|
return render(ui, {
|
||||||
...options,
|
...renderOptions,
|
||||||
wrapper: wrapWithTooltipProvider(options?.wrapper) as RenderOptions["wrapper"],
|
wrapper: wrapWithTooltipProvider(wrapper, presentation) as RenderOptions["wrapper"],
|
||||||
}) as ReturnType<typeof render>;
|
}) as ReturnType<typeof render>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||