Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
aaff823
collapsed media preview component
Siriusmart Jul 28, 2026
5186991
expanded preview tile
Siriusmart Jul 28, 2026
0866fd9
file body uses new previews
Siriusmart Jul 29, 2026
14dfc08
collapsed and uncollapsed image tile
Siriusmart Jul 29, 2026
01abf22
using the new preview tiles in upload confirm dialog
Siriusmart Jul 29, 2026
f06c3e4
audio and video tag
Siriusmart Jul 30, 2026
1b32f01
slapped on the styles for url previews as well
Siriusmart Jul 30, 2026
71e8259
uses css tokens
Siriusmart Aug 11, 2026
e1eb381
refactored so all media types use the same tile declaration
Siriusmart Aug 11, 2026
6f91f2d
remove old urlpreview code
Siriusmart Aug 11, 2026
a6cff06
refactored textpreview tile to be a normal media preview tile
Siriusmart Aug 11, 2026
1f44c23
force media width in confirm dialog to be max
Siriusmart Aug 11, 2026
56c9ba5
only display displayable images
Siriusmart Aug 11, 2026
7016cdb
fixed upload audio upload preview failing, and hide preview box when …
Siriusmart Aug 11, 2026
7f2a18e
undo the removal of the legacy mediabody
Siriusmart Aug 12, 2026
9c53021
button hover style
Siriusmart Aug 12, 2026
9218b6a
cleaning up PR for review
Siriusmart Aug 12, 2026
d3df1da
add the collapse previews button back
Siriusmart Aug 12, 2026
3a92527
cleaning up the PR more
Siriusmart Aug 12, 2026
0bf53ca
fixed oxlint issues
Siriusmart Aug 12, 2026
21e39f4
regenerate storybook screenshots
Siriusmart Aug 12, 2026
3e9dc1d
give old media previews the same gaps as the new previews
Siriusmart Aug 12, 2026
32ae917
updated jest test
Siriusmart Aug 12, 2026
1606996
claude fixed playwright tests
Siriusmart Aug 12, 2026
f3a9702
generated even more images
Siriusmart Aug 12, 2026
b065e55
coverage tests
Siriusmart Aug 12, 2026
6dd3e09
regenerate stuff for end to end tests
Siriusmart Aug 13, 2026
81946cc
jest and linter fix
Siriusmart Aug 13, 2026
92c465c
added gaps between buttons and text
Siriusmart Aug 13, 2026
0d3388b
pass lint
Siriusmart Aug 13, 2026
b57ff64
upload dialogue use tallbanner instead of full images
Siriusmart Aug 17, 2026
b886ffa
added comments
Siriusmart Aug 17, 2026
c50ffe2
added tallbanner variant to be used for upload confirm dialog
Siriusmart Aug 17, 2026
8221fd6
cleaned up pr for reviews
Siriusmart Aug 18, 2026
4a8d9c0
minwidth 0
Siriusmart Aug 21, 2026
aa16cb8
left group need some minwidth as well
Siriusmart Aug 21, 2026
6f2cf35
Merge branch 'develop' into unified-previews/1-mediapreview-components
Siriusmart Sep 1, 2026
36acc6f
Merge remote-tracking branch 'upstream/develop' into unified-previews…
Siriusmart Sep 1, 2026
3ae0756
Merge upstream copy of unified-previews/1-mediapreview-components (id…
Siriusmart Sep 1, 2026
4d1cf90
applied some of the reviews
Siriusmart Sep 2, 2026
af60b93
applied more reviews
Siriusmart Sep 2, 2026
66e8995
updated snapshots
Siriusmart Sep 2, 2026
db421bf
fixed vitest
Siriusmart Sep 2, 2026
ddc27c8
applied reviews
Siriusmart Sep 3, 2026
6369c88
image alt text is now required
Siriusmart Sep 3, 2026
e3e7f61
css tokens
Siriusmart Sep 3, 2026
06ab575
regenerated snapshot
Siriusmart Sep 3, 2026
dfc39a7
regen one more image
Siriusmart Sep 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/web/res/css/_common.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Please see LICENSE files in the repository root for full details.
--MBody-border-radius: 8px;
--EventTileBubble_margin-block: 10px;
--MessageTimestamp-width: 46px; /* 8 + 30 (avatar) + 8 */
--MediaPreview-width: 478px; /* keep in sync with PREVIEW_WIDTH_PX in src/utils/UrlPreviewFetcher.ts */

/* Expected z-indexes for dialogs:
4000 - Default wrapper index
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,11 @@ describe("WysiwygComposer", () => {

customRender(client, roomContext, editorState);
await waitFor(() => expect(screen.getByRole("textbox")).toHaveAttribute("contentEditable", "true"));
// Once ready, the composer puts its own caret at the end of the editor. That happens in
// an effect flushed after contentEditable lands in the DOM, so waiting for the attribute
// alone lets it run *after* a test has placed the caret, clobbering the selection the
// test set up. Wait for it here so the tests always get the last word on the caret.
await waitFor(() => expect(document.getSelection()?.anchorNode).toBe(screen.getByRole("textbox")));
return { textbox: screen.getByRole("textbox"), spyDispatcher };
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2026 Element Creations Ltd.
*
* 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.
*/

import { expect, waitFor } from "storybook/test";

/**
* A Storybook `play` helper that waits for every `<video>` in a story to be buffered end to end.
*
* The native video controls draw a buffered-progress bar, so a snapshot taken while the browser is
* still fetching the clip is not reproducible: the bar is a different width on every run. Stories that
* use this must point at a clip small enough for the browser to buffer in one go — see
* `videoPreviewDemo.webm` — otherwise the browser suspends the download part-way and this never
* settles.
*/
export async function prepareVideosForSnapshot(canvasElement: HTMLElement): Promise<void> {
const videos = Array.from(canvasElement.querySelectorAll("video"));

await Promise.all(
videos.map((video) =>
waitFor(() => {
expect(video.readyState).toBe(video.HAVE_ENOUGH_DATA);
expect(video.buffered.length).toBe(1);
expect(video.buffered.end(0)).toBeGreaterThanOrEqual(video.duration);
}),
),
);
}
5 changes: 5 additions & 0 deletions packages/shared-components/.storybook/preview.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/

/* Mirrors the token defined in app/web's _common.pcss, which stories don't load */
:root {
--MediaPreview-width: 478px;
}

.docs-story {
background: var(--cpd-color-bg-canvas-default);
}
Expand Down
18 changes: 17 additions & 1 deletion packages/shared-components/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@ import "./preview.css";
import React, { useLayoutEffect } from "react";
import { TooltipProvider } from "@vector-im/compound-web";

import { EventPresentationProvider, type EventDensity, type EventLayout, I18nApi, I18nContext } from "../src";
import {
EventPresentationProvider,
type EventDensity,
type EventLayout,
I18nApi,
I18nContext,
LinkedTextContext,
} from "../src";
import { setLanguage } from "../src/core/i18n/i18n";
import { type StoryContext } from "storybook/internal/csf";
import { DragDropProvider } from "@dnd-kit/react";
Expand Down Expand Up @@ -161,6 +168,14 @@ const withI18nProvider: Decorator = (Story) => {
);
};

const withLinkedTextProvider: Decorator = (Story) => {
return (
<LinkedTextContext.Provider value={{}}>
<Story />
</LinkedTextContext.Provider>
);
};

const withEventPresentationProvider: Decorator = (Story, context) => {
return (
<EventPresentationProvider
Expand Down Expand Up @@ -210,6 +225,7 @@ const preview: Preview = {
withEventPresentationProvider,
withTooltipProvider,
withI18nProvider,
withLinkedTextProvider,
withDragDropProvider,
],
parameters: {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion packages/shared-components/src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,10 @@
"one": "Show %(count)s other preview",
"other": "Show %(count)s other previews"
},
"view_image": "View image"
"view_audio": "View audio",
"view_file": "View file",
"view_image": "View image",
"view_video": "View video"
}
},
"user_menu": {
Expand Down
1 change: 1 addition & 0 deletions packages/shared-components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export * from "./room/timeline/event-tile/reactions/ReactionsRowButton";
export * from "./room/timeline/event-tile/reactions/ReactionsRowButtonTooltip";
export * from "./room/timeline/event-tile/timestamp/MessageTimestampView";
export * from "./room/timeline/event-tile/UrlPreviewGroupView";
export * from "./room/timeline/event-tile/MediaPreviewGroupView";
export * from "./core/rich-list/RichItem";
export * from "./core/rich-list/RichList";
export * from "./room-list/RoomListHeaderView";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
.header,
.body {
-webkit-line-clamp: 1;
line-clamp: 1;
-webkit-box-orient: vertical;
min-width: 0;
display: -webkit-box;
overflow: clip;
}

.leftGroup {
display: flex;
gap: var(--cpd-space-3x);
flex-grow: 1;
min-width: 0;
}

.header {
font-weight: 600;
}

.body {
font-size: var(--cpd-font-size-body-sm);
color: var(--cpd-color-text-secondary);
}

.textContent {
display: inline-block;
min-width: 0;
}

.icon {
button {
border: none;
background: none;
height: 100%;
aspect-ratio: 100%;
align-items: center;
justify-content: center;
display: flex;
border-radius: 6px;
cursor: pointer;
}

display: flex;
height: 100%;
aspect-ratio: 1;
align-items: center;
justify-content: center;
flex-shrink: 0;

border-radius: 6px;
border: 1px solid var(--cpd-color-icon-quaternary);
background-color: var(--cpd-color-bg-decorative-4);

svg {
display: block;
height: 57%;
width: 57%;
}
}

.buttonGroup {
display: flex;
gap: 12px;
}

.button {
background: none;
border: none;
cursor: pointer;

svg {
color: var(--cpd-color-icon-secondary);
display: block;
height: calc(var(--cpd-icon-button-size) * 0.625);
width: calc(var(--cpd-icon-button-size) * 0.625);
}
}

.button:hover svg {
color: var(--cpd-color-icon-primary);
}

.image button,
.video button,
.audio button {
cursor: pointer;
background: none;
border: none;
width: 100%;
padding: 0;
}

.image img,
.video video,
.audio audio {
width: 100%;
display: block;
}

.video.bannerVideo video,
.image.bannerImage img {
max-height: 100px;
object-fit: cover;
}

.video.tallBannerVideo video,
.image.tallBannerImage img {
max-height: 300px;
object-fit: cover;
}

.video.fullVideo video,
.image.fullImage img {
max-height: 300px;
object-fit: contain;
}
Loading
Loading