diff --git a/.changeset/slick-hats-arrive.md b/.changeset/slick-hats-arrive.md
new file mode 100644
index 0000000000000..4ce84a4a831ee
--- /dev/null
+++ b/.changeset/slick-hats-arrive.md
@@ -0,0 +1,7 @@
+---
+'@rocket.chat/federation-matrix': patch
+'@rocket.chat/core-typings': patch
+'@rocket.chat/meteor': patch
+---
+
+Fixes an issue where `description` was incorrectly being used as alternative text for image attachments
diff --git a/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts b/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts
index ec08a5d835a74..fe0fc015db704 100644
--- a/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts
+++ b/apps/meteor/app/file-upload/server/methods/sendFileMessage.ts
@@ -73,7 +73,7 @@ export const parseFileIntoMessageAttachments = async (
const attachment: FileAttachmentProps = {
title: file.name,
type: 'file',
- description: file?.description,
+ image_alt: file?.description,
title_link: fileUrl,
title_link_download: true,
image_url: fileUrl,
diff --git a/apps/meteor/client/components/message/content/attachments/DefaultAttachment.tsx b/apps/meteor/client/components/message/content/attachments/DefaultAttachment.tsx
index 2d6ae957f7209..d763362275959 100644
--- a/apps/meteor/client/components/message/content/attachments/DefaultAttachment.tsx
+++ b/apps/meteor/client/components/message/content/attachments/DefaultAttachment.tsx
@@ -98,7 +98,7 @@ const DefaultAttachment = (attachment: DefaultAttachmentProps) => {
/>
)}
{attachment.image_url && (
-
+
)}
{/* DEPRECATED */}
{isActionAttachment(attachment) && }
diff --git a/apps/meteor/client/components/message/content/attachments/file/ImageAttachment.tsx b/apps/meteor/client/components/message/content/attachments/file/ImageAttachment.tsx
index 29c1bf5b7c2f3..f653806208656 100644
--- a/apps/meteor/client/components/message/content/attachments/file/ImageAttachment.tsx
+++ b/apps/meteor/client/components/message/content/attachments/file/ImageAttachment.tsx
@@ -17,6 +17,7 @@ const ImageAttachment = ({
width: 368,
height: 368,
},
+ image_alt: altText,
description,
descriptionMd,
title_link: link,
@@ -38,7 +39,7 @@ const ImageAttachment = ({
src={getURL(url)}
previewUrl={`data:image/png;base64,${imagePreview}`}
id={id}
- alt={description}
+ alt={altText}
/>
>
diff --git a/apps/meteor/client/lib/chats/ChatAPI.ts b/apps/meteor/client/lib/chats/ChatAPI.ts
index 2826f3f56de58..e7cc2cf0fb6dd 100644
--- a/apps/meteor/client/lib/chats/ChatAPI.ts
+++ b/apps/meteor/client/lib/chats/ChatAPI.ts
@@ -122,7 +122,7 @@ export type UploadsAPI = {
cancel(id: Upload['id']): void;
removeUpload(id: Upload['id']): void;
editUploadFileName: (id: Upload['id'], fileName: string) => void;
- editUploadDescription: (id: Upload['id'], description: string) => void;
+ editUploadAltText: (id: Upload['id'], altText: string) => void;
send(file: File, encrypted?: never): Promise;
send(file: File, encrypted: EncryptedFileUploadContent): Promise;
};
diff --git a/apps/meteor/client/lib/chats/Upload.ts b/apps/meteor/client/lib/chats/Upload.ts
index 6916d90b9101c..7e48a9733dc12 100644
--- a/apps/meteor/client/lib/chats/Upload.ts
+++ b/apps/meteor/client/lib/chats/Upload.ts
@@ -6,7 +6,7 @@ export type NonEncryptedUpload = {
readonly url?: string;
readonly percentage: number;
readonly error?: Error;
- readonly description?: string;
+ readonly altText?: string;
};
export type EncryptedUpload = NonEncryptedUpload & {
diff --git a/apps/meteor/client/lib/chats/flows/processMessageUploads.ts b/apps/meteor/client/lib/chats/flows/processMessageUploads.ts
index 10ac33f930e52..2c3cb00ec41b6 100644
--- a/apps/meteor/client/lib/chats/flows/processMessageUploads.ts
+++ b/apps/meteor/client/lib/chats/flows/processMessageUploads.ts
@@ -59,7 +59,7 @@ const getAttachmentForFile = async (fileToUpload: EncryptedUpload): Promise {
+ editUploadAltText = (uploadId: Upload['id'], altText: string) => {
this.set(
this.uploads.map((upload) => {
if (upload.id !== uploadId) {
@@ -72,9 +72,9 @@ class UploadsStore extends Emitter<{ update: void; [x: `cancelling-${Upload['id'
return {
...upload,
- description,
+ altText,
...(isEncryptedUpload(upload) && {
- metadataForEncryption: { ...upload.metadataForEncryption, description },
+ metadataForEncryption: { ...upload.metadataForEncryption, altText },
}),
};
}),
diff --git a/apps/meteor/client/views/room/composer/messageBox/MessageComposerFileItem.tsx b/apps/meteor/client/views/room/composer/messageBox/MessageComposerFileItem.tsx
index 6ceece6a89f59..fcd87b9d3e37a 100644
--- a/apps/meteor/client/views/room/composer/messageBox/MessageComposerFileItem.tsx
+++ b/apps/meteor/client/views/room/composer/messageBox/MessageComposerFileItem.tsx
@@ -7,7 +7,7 @@ import { isPreviewableImage } from '../../../../lib/utils/isPreviewableImage';
export type MessageComposerFileItemProps = {
upload: Upload;
onRemove: (id: string) => void;
- onEdit: (id: Upload['id'], fileName: string, description?: string) => void;
+ onEdit: (id: Upload['id'], fileName: string, altText?: string) => void;
onCancel: (id: Upload['id']) => void;
disabled: boolean;
shouldPreview?: boolean;
diff --git a/apps/meteor/client/views/room/composer/messageBox/MessageComposerFiles.tsx b/apps/meteor/client/views/room/composer/messageBox/MessageComposerFiles.tsx
index 84f0056513d77..c8e6d60f30ed0 100644
--- a/apps/meteor/client/views/room/composer/messageBox/MessageComposerFiles.tsx
+++ b/apps/meteor/client/views/room/composer/messageBox/MessageComposerFiles.tsx
@@ -11,10 +11,10 @@ const MessageComposerFiles = () => {
const { uploads, uploadsStore, isProcessingUploads, hasUploads } = useFileUpload();
const handleEdit = useCallback(
- (id: Upload['id'], fileName: string, description?: string) => {
+ (id: Upload['id'], fileName: string, altText?: string) => {
uploadsStore?.editUploadFileName(id, fileName);
- if (description !== undefined) {
- uploadsStore?.editUploadDescription(id, description);
+ if (altText !== undefined) {
+ uploadsStore?.editUploadAltText(id, altText);
}
},
[uploadsStore],
diff --git a/apps/meteor/client/views/room/composer/messageBox/MessageComposerGenericFile.tsx b/apps/meteor/client/views/room/composer/messageBox/MessageComposerGenericFile.tsx
index d24e22b54c404..d68119b58dc6b 100644
--- a/apps/meteor/client/views/room/composer/messageBox/MessageComposerGenericFile.tsx
+++ b/apps/meteor/client/views/room/composer/messageBox/MessageComposerGenericFile.tsx
@@ -39,13 +39,13 @@ const MessageComposerGenericFile = ({
setModal(
{
- onEdit(upload.id, name, description);
+ onSubmit={(name, altText) => {
+ onEdit(upload.id, name, altText);
setModal(null);
chat?.composer?.focus();
}}
fileName={upload.file.name}
- fileDescription={upload.description}
+ fileAltText={upload.altText}
file={upload.file}
onClose={() => setModal(null)}
/>,
@@ -86,7 +86,7 @@ const MessageComposerGenericFile = ({
fileTitle={upload.file.name}
fileSubtitle={`${fileSize} - ${fileExtension}`}
previewUrl={shouldPreview ? previewUrl : undefined}
- alt={upload.description}
+ alt={upload.altText}
fileFormat={getFileExtension(upload.file.name)}
showPreview={shouldPreview}
actionIcon={actionIcon}
diff --git a/apps/meteor/client/views/room/modals/FileUploadModal/FilePreview.tsx b/apps/meteor/client/views/room/modals/FileUploadModal/FilePreview.tsx
index 6254c5740f329..35af01a288aa6 100644
--- a/apps/meteor/client/views/room/modals/FileUploadModal/FilePreview.tsx
+++ b/apps/meteor/client/views/room/modals/FileUploadModal/FilePreview.tsx
@@ -39,14 +39,14 @@ const shouldShowMediaPreview = (file: File, fileType: FilePreviewType | undefine
type FilePreviewProps = {
file: File;
- description?: string;
+ altText?: string;
};
-const FilePreview = ({ file, description }: FilePreviewProps) => {
+const FilePreview = ({ file, altText }: FilePreviewProps) => {
const fileType = getFileType(file.type);
if (shouldShowMediaPreview(file, fileType)) {
- return ;
+ return ;
}
return ;
diff --git a/apps/meteor/client/views/room/modals/FileUploadModal/FileUploadModal.tsx b/apps/meteor/client/views/room/modals/FileUploadModal/FileUploadModal.tsx
index 29e13e1cb3963..f970f92cbdcb9 100644
--- a/apps/meteor/client/views/room/modals/FileUploadModal/FileUploadModal.tsx
+++ b/apps/meteor/client/views/room/modals/FileUploadModal/FileUploadModal.tsx
@@ -30,13 +30,13 @@ import { getMimeTypeFromFileName } from '../../../../../app/utils/lib/mimeTypes'
type FileUploadModalProps = {
onClose: () => void;
- onSubmit: (name: string, description?: string) => void;
+ onSubmit: (name: string, altText?: string) => void;
file: File;
fileName: string;
- fileDescription?: string;
+ fileAltText?: string;
};
-const FileUploadModal = ({ onClose, file, fileName, fileDescription = '', onSubmit }: FileUploadModalProps) => {
+const FileUploadModal = ({ onClose, file, fileName, fileAltText = '', onSubmit }: FileUploadModalProps) => {
const { t } = useTranslation();
const fileUploadFormId = useId();
const isImage = file.type.startsWith('image/');
@@ -45,7 +45,7 @@ const FileUploadModal = ({ onClose, file, fileName, fileDescription = '', onSubm
control,
handleSubmit,
formState: { errors, isDirty, isSubmitting },
- } = useForm({ mode: 'onBlur', defaultValues: { name: fileName, description: fileDescription } });
+ } = useForm({ mode: 'onBlur', defaultValues: { name: fileName, altText: fileAltText } });
const validateFileName = useCallback(
(fieldValue: string) => {
@@ -63,12 +63,7 @@ const FileUploadModal = ({ onClose, file, fileName, fileDescription = '', onSubm
) => (
- onSubmit(name, description?.trim() || undefined))}
- {...props}
- />
+ onSubmit(name, altText?.trim()))} {...props} />
)}
>
@@ -78,7 +73,7 @@ const FileUploadModal = ({ onClose, file, fileName, fileDescription = '', onSubm
-
+
@@ -101,7 +96,7 @@ const FileUploadModal = ({ onClose, file, fileName, fileDescription = '', onSubm
{t('Alternative_text')}
{t('Alt_text_description')}
- } />
+ } />
)}
diff --git a/apps/meteor/client/views/room/modals/FileUploadModal/ImagePreview.tsx b/apps/meteor/client/views/room/modals/FileUploadModal/ImagePreview.tsx
index e00cd4cdfe6b1..635c7d4429e2b 100644
--- a/apps/meteor/client/views/room/modals/FileUploadModal/ImagePreview.tsx
+++ b/apps/meteor/client/views/room/modals/FileUploadModal/ImagePreview.tsx
@@ -7,10 +7,10 @@ import PreviewSkeleton from './PreviewSkeleton';
type ImagePreviewProps = {
url: string;
file: File;
- alt?: string;
+ altText?: string;
};
-const ImagePreview = ({ url, file, alt = '' }: ImagePreviewProps) => {
+const ImagePreview = ({ url, file, altText = '' }: ImagePreviewProps) => {
const [error, setError] = useState(false);
const [loading, setLoading] = useState(true);
@@ -30,7 +30,7 @@ const ImagePreview = ({ url, file, alt = '' }: ImagePreviewProps) => {
{
+const MediaPreview = ({ file, fileType, altText }: MediaPreviewProps) => {
const [loaded, url] = useFileAsDataURL(file);
const { t } = useTranslation();
@@ -32,7 +32,7 @@ const MediaPreview = ({ file, fileType, description }: MediaPreviewProps) => {
}
if (fileType === FilePreviewType.IMAGE) {
- return ;
+ return ;
}
if (fileType === FilePreviewType.VIDEO) {
diff --git a/apps/meteor/tests/end-to-end/api/rooms.ts b/apps/meteor/tests/end-to-end/api/rooms.ts
index f677aae918196..350c31c190454 100644
--- a/apps/meteor/tests/end-to-end/api/rooms.ts
+++ b/apps/meteor/tests/end-to-end/api/rooms.ts
@@ -641,7 +641,7 @@ describe('[Rooms]', () => {
expect(res.body.message.files).to.be.an('array').of.length(2);
expect(res.body.message.files[0]).to.have.property('type', 'image/png');
expect(res.body.message.files[0]).to.have.property('name', '1024x1024.png');
- expect(res.body.message.attachments[0]).to.have.property('description', 'some_file_description');
+ expect(res.body.message.attachments[0]).to.have.property('image_alt', 'some_file_description');
});
});
diff --git a/ee/packages/federation-matrix/tests/end-to-end/messaging.spec.ts b/ee/packages/federation-matrix/tests/end-to-end/messaging.spec.ts
index ffb0248a4a086..db88965674933 100644
--- a/ee/packages/federation-matrix/tests/end-to-end/messaging.spec.ts
+++ b/ee/packages/federation-matrix/tests/end-to-end/messaging.spec.ts
@@ -758,7 +758,7 @@ import { SynapseClient } from '../helper/synapse-client';
expect(rcMessage?.attachments?.[0]?.title_link).toMatch(/^\/file-upload\/[^/]+\/.+$/);
expect(rcMessage?.attachments?.[0]?.title_link_download).toBe(true);
expect((rcMessage?.attachments?.[0] as any)?.type).toBe('file');
- expect(rcMessage?.attachments?.[0]?.description).toBe(fileInfo.description);
+ expect((rcMessage?.attachments?.[0] as any)?.image_alt).toBe(fileInfo.description);
expect((rcMessage?.attachments?.[0] as any)?.image_url).toMatch(/^\/file-upload\/[^/]+\/.+$/);
expect((rcMessage?.attachments?.[0] as any)?.image_type).toBe('image/webp');
expect((rcMessage?.attachments?.[0] as any)?.image_size).toBe(uploadResponse.message.files?.[0]?.size);
diff --git a/packages/core-typings/src/IMessage/MessageAttachment/Files/ImageAttachmentProps.ts b/packages/core-typings/src/IMessage/MessageAttachment/Files/ImageAttachmentProps.ts
index 20d1d17390109..b94689a47dfee 100644
--- a/packages/core-typings/src/IMessage/MessageAttachment/Files/ImageAttachmentProps.ts
+++ b/packages/core-typings/src/IMessage/MessageAttachment/Files/ImageAttachmentProps.ts
@@ -11,6 +11,8 @@ export type ImageAttachmentProps = {
image_url: string;
image_type?: string;
image_size?: number;
+ /** Accessibility alternative text for the image. Kept separate from `description` so it is not rendered as a visible caption. */
+ image_alt?: string;
file?: FileProp;
} & MessageAttachmentBase;
diff --git a/packages/core-typings/src/IMessage/MessageAttachment/MessageAttachmentDefault.ts b/packages/core-typings/src/IMessage/MessageAttachment/MessageAttachmentDefault.ts
index 395c5f437962c..5c9f4cda338ee 100644
--- a/packages/core-typings/src/IMessage/MessageAttachment/MessageAttachmentDefault.ts
+++ b/packages/core-typings/src/IMessage/MessageAttachment/MessageAttachmentDefault.ts
@@ -20,6 +20,8 @@ export type MessageAttachmentDefault = {
// footer_icon
image_url?: string;
+ /** Accessibility alternative text for the image. Kept separate from `description` so it is not rendered as a visible caption. */
+ image_alt?: string;
image_dimensions?: {
width: number;
height: number;