diff --git a/packages/components/src/CommentItem/CommentItem.test.tsx b/packages/components/src/CommentItem/CommentItem.test.tsx
index 53def4fb0d..b17dc2b3b7 100644
--- a/packages/components/src/CommentItem/CommentItem.test.tsx
+++ b/packages/components/src/CommentItem/CommentItem.test.tsx
@@ -7,10 +7,20 @@ import { Default } from './CommentItem.stories'
import type { IProps } from './CommentItem'
-describe('CommentList', () => {
+describe('CommentItem', () => {
it('shows the avatar', () => {
const { getByTestId } = render()
expect(getByTestId('commentAvatar')).toBeInTheDocument()
+ expect(
+ (
+ getByTestId('commentAvatar').firstChild as HTMLImageElement
+ ).getAttribute('alt'),
+ ).not.empty
+ })
+ it('shows the comment text', () => {
+ const { getByTestId } = render()
+
+ expect(getByTestId('commentText')).toBeInTheDocument()
})
})
diff --git a/packages/components/src/CommentItem/CommentItem.tsx b/packages/components/src/CommentItem/CommentItem.tsx
index 5beb7cc60c..e96cbb1d42 100644
--- a/packages/components/src/CommentItem/CommentItem.tsx
+++ b/packages/components/src/CommentItem/CommentItem.tsx
@@ -91,6 +91,11 @@ export const CommentItem = (props: IProps) => {
width: ['30px', '50px'],
height: ['30px', '50px'],
}}
+ alt={
+ creatorName
+ ? `Avatar of ${creatorName}`
+ : 'Avatar of comment author'
+ }
/>
@@ -155,6 +160,7 @@ export const CommentItem = (props: IProps) => {
{
onClick={() => {
triggerLightbox()
}}
- alt={activeImage.name}
+ alt={activeImage.alt ?? activeImage.name}
crossOrigin=""
/>
{showNextPrevButton ? (
@@ -208,7 +209,7 @@ export const ImageGallery = (props: ImageGalleryProps) => {
loading="lazy"
src={image.thumbnailUrl}
key={index}
- alt={image.name}
+ alt={image.alt ?? image.name}
sx={{
width: 100,
height: 67,
diff --git a/src/models/howto.models.tsx b/src/models/howto.models.tsx
index afadc8d3a6..ea217e7faf 100644
--- a/src/models/howto.models.tsx
+++ b/src/models/howto.models.tsx
@@ -15,6 +15,7 @@ export interface IHowto extends IHowtoFormInput {
_createdBy: string
_deleted: boolean
cover_image?: IUploadedFileMeta
+ cover_image_alt?: string
fileLink?: string
total_downloads?: number
latestCommentDate?: string | undefined
@@ -54,6 +55,7 @@ export interface IHowtoFormInput extends IModerable, ISharedFeatures {
category?: ICategory
// NOTE cover image input starts as convertedFileMeta but is transformed on upload
cover_image?: IUploadedFileMeta | IConvertedFileMeta
+ cover_image_alt?: string
// Added to be able to recover on edit by admin
creatorCountry?: string
totalComments?: number
diff --git a/src/pages/Howto/Content/Common/Howto.form.tsx b/src/pages/Howto/Content/Common/Howto.form.tsx
index eb450cd627..a70bed3286 100644
--- a/src/pages/Howto/Content/Common/Howto.form.tsx
+++ b/src/pages/Howto/Content/Common/Howto.form.tsx
@@ -23,6 +23,7 @@ import { HowtoButtonPublish } from './HowtoButtonPublish'
import { HowtoErrors } from './HowtoErrors'
import { HowtoFieldCategory } from './HowtoFieldCategory'
import { HowtoFieldCoverImage } from './HowtoFieldCoverImage'
+import { HowtoFieldCoverImageAlt } from './HowtoFieldCoverImageAlt'
import { HowtoFieldDescription } from './HowtoFieldDescription'
import { HowtoFieldDifficulty } from './HowtoFieldDifficulty'
import { HowtoFieldFiles } from './HowtoFieldFiles'
@@ -218,6 +219,7 @@ export const HowtoForm = observer((props: IProps) => {
data-cy={'intro-cover'}
>
+
diff --git a/src/pages/Howto/Content/Common/HowtoFieldCoverImageAlt.tsx b/src/pages/Howto/Content/Common/HowtoFieldCoverImageAlt.tsx
new file mode 100644
index 0000000000..3a12f8b43a
--- /dev/null
+++ b/src/pages/Howto/Content/Common/HowtoFieldCoverImageAlt.tsx
@@ -0,0 +1,27 @@
+import { Field } from 'react-final-form'
+import { FieldInput } from 'oa-components'
+import { FormFieldWrapper } from 'src/pages/Howto/Content/Common/FormFieldWrapper'
+import { Box, Text } from 'theme-ui'
+
+import { intro } from '../../labels'
+
+export const HowtoFieldCoverImageAlt = () => {
+ const { description, placeholder, title } = intro.cover_image_alt
+ const name = 'cover_image_alt'
+
+ return (
+
+
+
+
+
+ {description}
+
+
+ )
+}
diff --git a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx
index 6f860e49b8..4c28c4de72 100644
--- a/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx
+++ b/src/pages/Howto/Content/Howto/HowtoDescription/HowtoDescription.tsx
@@ -269,7 +269,7 @@ const HowtoDescription = ({ howto, loggedInUser, ...props }: IProps) => {
width: '100%',
}}
crossOrigin=""
- alt="how-to cover"
+ alt={howto.cover_image_alt ?? 'how-to cover'}
/>
)}
diff --git a/src/pages/Howto/Content/Howto/Step/Step.tsx b/src/pages/Howto/Content/Howto/Step/Step.tsx
index e0a9e0e5c7..8dc2f37b3b 100644
--- a/src/pages/Howto/Content/Howto/Step/Step.tsx
+++ b/src/pages/Howto/Content/Howto/Step/Step.tsx
@@ -82,7 +82,12 @@ const Step = (props: IProps) => {
) : step.images ? (
) : null}
diff --git a/src/pages/Howto/Content/HowtoList/HowToCard.tsx b/src/pages/Howto/Content/HowtoList/HowToCard.tsx
index ff7f6bfcb6..ed3e138e40 100644
--- a/src/pages/Howto/Content/HowtoList/HowToCard.tsx
+++ b/src/pages/Howto/Content/HowtoList/HowToCard.tsx
@@ -43,6 +43,7 @@ export const HowToCard = ({ howto }: IProps) => {
width: 500,
})}
crossOrigin=""
+ alt={howto.cover_image_alt ?? `Cover image of ${howto.title}`}
/>
diff --git a/src/pages/Howto/labels.ts b/src/pages/Howto/labels.ts
index 2bcf8b91e0..b798fca587 100644
--- a/src/pages/Howto/labels.ts
+++ b/src/pages/Howto/labels.ts
@@ -52,6 +52,12 @@ export const intro: ILabels = {
description: 'This image should be landscape. We advise 1280x960px',
title: 'Cover image',
},
+ cover_image_alt: {
+ description:
+ 'This is the alternative text that is read out if the cover image is not available',
+ placeholder: 'People working on a house',
+ title: 'Cover image alt text',
+ },
description: {
description: `Provide a short introduction (max ${HOWTO_MAX_LENGTH} characters)`,
title: 'Short description',
diff --git a/src/pages/Research/Content/ResearchListItem.tsx b/src/pages/Research/Content/ResearchListItem.tsx
index 06ac728600..99c472e0a1 100644
--- a/src/pages/Research/Content/ResearchListItem.tsx
+++ b/src/pages/Research/Content/ResearchListItem.tsx
@@ -76,6 +76,7 @@ const ResearchListItem = ({ item }: IProps) => {
src={cdnImageUrl(getItemThumbnail(item), {
width: 125,
})}
+ alt={`Thumbnail of ${item.title}`}
crossOrigin=""
/>
diff --git a/src/stores/Howto/howto.store.tsx b/src/stores/Howto/howto.store.tsx
index 147023bfa7..9f7a2d4108 100644
--- a/src/stores/Howto/howto.store.tsx
+++ b/src/stores/Howto/howto.store.tsx
@@ -325,6 +325,7 @@ export class HowtoStore extends ModuleStore {
const slug = await this.setSlug(values)
const previousSlugs = this.setPreviousSlugs(values, slug)
const total_downloads = values['total_downloads'] ?? 0
+ const cover_image_alt = values.cover_image_alt ?? ''
const keywords = getKeywords(values.title + ' ' + values.description)
keywords.push(_createdBy)
@@ -346,6 +347,7 @@ export class HowtoStore extends ModuleStore {
steps,
title,
keywords,
+ cover_image_alt,
...(latestCommentDate ? { latestCommentDate } : {}),
...(files ? { total_downloads } : {}),
...(category ? { category } : {}),
diff --git a/src/utils/formatImageListForGallery.ts b/src/utils/formatImageListForGallery.ts
index ba2ceeca22..296e5dab9c 100644
--- a/src/utils/formatImageListForGallery.ts
+++ b/src/utils/formatImageListForGallery.ts
@@ -5,6 +5,7 @@ import type { IConvertedFileMeta } from 'src/types'
export const formatImagesForGallery = (
imageList: (IUploadedFileMeta | File | IConvertedFileMeta | null)[],
+ altPrefix?: string,
) => {
if (!imageList) {
return []
@@ -13,10 +14,11 @@ export const formatImagesForGallery = (
return imageList
.filter(Boolean)
.filter((i: any) => !!i?.downloadUrl)
- .map((i: any) => ({
- downloadUrl: i.downloadUrl,
- thumbnailUrl: cdnImageUrl(i.downloadUrl, {
+ .map((image: any, index: number) => ({
+ downloadUrl: image.downloadUrl,
+ thumbnailUrl: cdnImageUrl(image.downloadUrl, {
width: 150,
}),
+ alt: `${altPrefix ? altPrefix + ' ' : ''}Gallery image ${index + 1}`,
}))
}