Phase 2 Refactor MImageBody to MVVM and remove legacy component (#33212)
* MVVMing of MImageBody and removing legacy component + css * Fix Prettier * update small image to large image in test * Update test * Preserve MImageBody legacy class names * Click image in custom component download test * Update snapshots * Update MBodyFactory snapshots * Added new tests to pass coverage * Fix prettier * Remove legacy import that was removed * Add MImageReplayBody test for coverage * Remove legacy MImageBody selectors from image view * Update image body selectors in Playwright tests * Keep file panel image body spacing compact * Update apps/web/src/viewmodels/message-body/ImageBodyViewModel.ts Co-authored-by: Florian Duros <florian.duros@ormaz.fr> * added documentation to component * Fix hidden media placeholder import --------- Co-authored-by: Florian Duros <florian.duros@ormaz.fr>
This commit is contained in:
+32
@@ -112,6 +112,38 @@ describe("ImageBodyView", () => {
|
||||
expect(onLinkClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("merges supplied class names with module classes", () => {
|
||||
const vm = new TestImageBodyViewModel({
|
||||
state: ImageBodyViewState.READY,
|
||||
alt: "Custom class image",
|
||||
src: "https://example.org/full.png",
|
||||
thumbnailSrc: "https://example.org/thumb.png",
|
||||
maxWidth: 320,
|
||||
maxHeight: 240,
|
||||
aspectRatio: "4 / 3",
|
||||
});
|
||||
|
||||
const { container } = render(
|
||||
<ImageBodyView
|
||||
vm={vm}
|
||||
className="customRoot"
|
||||
containerClassName="customContainer"
|
||||
imageClassName="customImage"
|
||||
/>,
|
||||
);
|
||||
|
||||
const rootClassName = container.querySelector(".customRoot")?.className;
|
||||
const containerClassName = container.querySelector(".customContainer")?.className;
|
||||
const imageClassName = screen.getByRole("img", { name: "Custom class image" }).className;
|
||||
|
||||
expect(rootClassName).toContain("customRoot");
|
||||
expect(rootClassName).not.toBe("customRoot");
|
||||
expect(containerClassName).toContain("customContainer");
|
||||
expect(containerClassName).not.toBe("customContainer");
|
||||
expect(imageClassName).toContain("customImage");
|
||||
expect(imageClassName).not.toBe("customImage");
|
||||
});
|
||||
|
||||
it("swaps to the full source on hover for animated previews", async () => {
|
||||
const user = userEvent.setup();
|
||||
const vm = new TestImageBodyViewModel({
|
||||
|
||||
+26
-3
@@ -12,6 +12,7 @@ import React, {
|
||||
type MouseEventHandler,
|
||||
type PropsWithChildren,
|
||||
type ReactEventHandler,
|
||||
type Ref,
|
||||
useState,
|
||||
} from "react";
|
||||
import classNames from "classnames";
|
||||
@@ -148,6 +149,18 @@ interface ImageBodyViewProps {
|
||||
* Optional host CSS class.
|
||||
*/
|
||||
className?: string;
|
||||
/**
|
||||
* Optional CSS class applied to the media frame container.
|
||||
*/
|
||||
containerClassName?: string;
|
||||
/**
|
||||
* Optional CSS class applied to the rendered image element.
|
||||
*/
|
||||
imageClassName?: string;
|
||||
/**
|
||||
* Optional ref to the rendered image element.
|
||||
*/
|
||||
imageRef?: Ref<HTMLImageElement>;
|
||||
/**
|
||||
* Optional supplemental content rendered after the media frame.
|
||||
*/
|
||||
@@ -202,7 +215,14 @@ function renderPlaceholder({
|
||||
* </ImageBodyView>
|
||||
* ```
|
||||
*/
|
||||
export function ImageBodyView({ vm, className, children }: Readonly<ImageBodyViewProps>): JSX.Element {
|
||||
export function ImageBodyView({
|
||||
vm,
|
||||
className,
|
||||
containerClassName,
|
||||
imageClassName,
|
||||
imageRef,
|
||||
children,
|
||||
}: Readonly<ImageBodyViewProps>): JSX.Element {
|
||||
const { translate: _t } = useI18n();
|
||||
const {
|
||||
state,
|
||||
@@ -230,6 +250,8 @@ export function ImageBodyView({ vm, className, children }: Readonly<ImageBodyVie
|
||||
const hoverOrFocus = hover || focus;
|
||||
|
||||
const rootClassName = classNames(className, styles.root);
|
||||
const resolvedContainerClassName = classNames(containerClassName, styles.thumbnailContainer);
|
||||
const resolvedImageClassName = classNames(imageClassName, styles.image);
|
||||
|
||||
if (state === ImageBodyViewState.ERROR) {
|
||||
return (
|
||||
@@ -281,9 +303,10 @@ export function ImageBodyView({ vm, className, children }: Readonly<ImageBodyVie
|
||||
</div>
|
||||
) : resolvedImageSrc ? (
|
||||
<img
|
||||
className={styles.image}
|
||||
className={resolvedImageClassName}
|
||||
src={resolvedImageSrc}
|
||||
alt={alt}
|
||||
ref={imageRef}
|
||||
onError={vm.onImageError}
|
||||
onLoad={vm.onImageLoad}
|
||||
onMouseEnter={(): void => setHover(true)}
|
||||
@@ -302,7 +325,7 @@ export function ImageBodyView({ vm, className, children }: Readonly<ImageBodyVie
|
||||
) : null;
|
||||
|
||||
let frame = (
|
||||
<div className={styles.thumbnailContainer} style={containerStyle}>
|
||||
<div className={resolvedContainerClassName} style={containerStyle}>
|
||||
{showPlaceholder && (
|
||||
<div
|
||||
className={classNames(styles.placeholder, {
|
||||
|
||||
Reference in New Issue
Block a user