Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change alt to required in next/future/image #40136

Merged
merged 7 commits into from Aug 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/api-reference/next/future/image.md
Expand Up @@ -9,6 +9,7 @@ description: Try the latest Image Optimization with the experimental `next/futur

| Version | Changes |
| --------- | -------------------------------------------- |
| `v12.3.0` | Changed `alt` property to required. |
| `v12.2.4` | Support for `fill` property added. |
| `v12.2.0` | Experimental `next/future/image` introduced. |

Expand Down Expand Up @@ -43,6 +44,7 @@ Compared to `next/image`, the new `next/future/image` component has the followin
- Removes `lazyBoundary` prop since there is no native equivalent
- Removes `lazyRoot` prop since there is no native equivalent
- Removes `loader` config in favor of [`loader`](#loader) prop
- Changed `alt` prop from optional to required

## Known Browser Bugs

Expand Down Expand Up @@ -175,6 +177,16 @@ The `height` property represents the _rendered_ height in pixels, so it will aff

Required, except for [statically imported images](/docs/basic-features/image-optimization.md#local-images) or images with the [`fill` property](#fill).

### alt

The `alt` property is used to describe the image for screen readers and search engines. It is also the fallback text if images have been disabled or an error occurs while loading the image.

It should contain text that could replace the image [without changing the meaning of the page](https://html.spec.whatwg.org/multipage/images.html#general-guidelines). It is not meant to supplement the image and should not repeat information that is already provided in the captions above or below the image.

If the image is [purely decorative](https://html.spec.whatwg.org/multipage/images.html#a-purely-decorative-image-that-doesn't-add-any-information) or [not intended for the user](https://html.spec.whatwg.org/multipage/images.html#an-image-not-intended-for-the-user), the `alt` property should be an empty string (`alt=""`).

[Learn more](https://html.spec.whatwg.org/multipage/images.html#alt)

## Optional Props

The `<Image />` component accepts a number of additional properties beyond those which are required. This section describes the most commonly-used properties of the Image component. Find details about more rarely-used properties in the [Advanced Props](#advanced-props) section.
Expand Down
10 changes: 8 additions & 2 deletions packages/next/client/future/image.tsx
Expand Up @@ -100,9 +100,10 @@ function isStaticImport(src: string | StaticImport): src is StaticImport {

export type ImageProps = Omit<
JSX.IntrinsicElements['img'],
'src' | 'srcSet' | 'ref' | 'width' | 'height' | 'loading'
'src' | 'srcSet' | 'ref' | 'alt' | 'width' | 'height' | 'loading'
> & {
src: string | StaticImport
alt: string
width?: number | string
height?: number | string
fill?: boolean
Expand All @@ -116,7 +117,7 @@ export type ImageProps = Omit<
onLoadingComplete?: OnLoadingComplete
}

type ImageElementProps = Omit<ImageProps, 'src' | 'loader'> & {
type ImageElementProps = Omit<ImageProps, 'src' | 'alt' | 'loader'> & {
srcString: string
imgAttributes: GenImgAttrsResult
heightInt: number | undefined
Expand Down Expand Up @@ -392,6 +393,11 @@ const ImageElement = ({
img
)
}
if (img.getAttribute('alt') === null) {
console.error(
`Image is missing required "alt" property. Please add Alternative Text to describe the image for screen readers and search engines.`
)
}
}
if (img.complete) {
handleLoading(
Expand Down
13 changes: 13 additions & 0 deletions test/integration/image-future/default/pages/missing-alt.js
@@ -0,0 +1,13 @@
import React from 'react'
import Image from 'next/future/image'
import testJPG from '../public/test.jpg'

export default function Page() {
return (
<div>
<p>Missing alt</p>

<Image src={testJPG} />
</div>
)
}
10 changes: 10 additions & 0 deletions test/integration/image-future/default/test/index.test.ts
Expand Up @@ -685,6 +685,16 @@ function runTests(mode) {
)
})

it('should show missing alt error', async () => {
const browser = await webdriver(appPort, '/missing-alt')

expect(await hasRedbox(browser)).toBe(false)

await check(async () => {
return (await browser.log()).map((log) => log.message).join('\n')
}, /Image is missing required "alt" property/gm)
})

it('should show error when missing width prop', async () => {
const browser = await webdriver(appPort, '/missing-width')

Expand Down
20 changes: 17 additions & 3 deletions test/integration/image-future/typescript/pages/invalid.tsx
Expand Up @@ -5,20 +5,34 @@ const Invalid = () => {
return (
<div>
<h1>Invalid TS</h1>
<Image id="invalid-src" src={new Date()} width={500} height={500}></Image>
<Image
id="invalid-src"
alt="invalid-src"
src={new Date()}
width={500}
height={500}
/>
<Image
id="invalid-width"
alt="invalid-width"
src="https://image-optimization-test.vercel.app/test.jpg"
width={new Date()}
height={500}
></Image>
/>
<Image
id="invalid-placeholder"
alt="invalid-placeholder"
src="https://image-optimization-test.vercel.app/test.jpg"
width="500"
height="500"
placeholder="invalid"
></Image>
/>
<Image
id="missing-alt"
src="https://image-optimization-test.vercel.app/test.jpg"
width={500}
height={500}
/>
<p id="stubtext">This is the invalid usage</p>
</div>
)
Expand Down
18 changes: 15 additions & 3 deletions test/integration/image-future/typescript/pages/valid.tsx
Expand Up @@ -12,12 +12,14 @@ const Page = () => {
<p>Valid TS</p>
<Image
id="width-and-height-num"
alt="width-and-height-num"
src="https://image-optimization-test.vercel.app/test.jpg"
width={500}
height={500}
/>
<Image
id="width-and-height-str"
alt="width-and-height-str"
src="https://image-optimization-test.vercel.app/test.jpg"
width="500"
height="500"
Expand All @@ -36,39 +38,49 @@ const Page = () => {
/>
<Image
id="quality-str"
alt="quality-str"
src="https://image-optimization-test.vercel.app/test.jpg"
quality="80"
width={500}
height={500}
/>
<Image
id="data-protocol"
alt="data-protocol"
src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="
width={100}
height={100}
/>
<Image
id="placeholder-and-blur-data-url"
alt="placeholder-and-blur-data-url"
src="https://image-optimization-test.vercel.app/test.jpg"
width={500}
height={500}
placeholder="blur"
blurDataURL="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="
/>
<Image id="no-width-and-height" src={testTall} />
<Image
id="no-width-and-height"
alt="no-width-and-height"
src={testTall}
/>
<Image
id="object-src-with-placeholder"
alt="object-src-with-placeholder"
src={testTall}
placeholder="blur"
/>
<Image id="object-src-with-svg" src={svg} />
<Image id="object-src-with-avif" src={avif} />
<Image id="object-src-with-svg" alt="object-src-with-svg" src={svg} />
<Image id="object-src-with-avif" alt="object-src-with-avif" src={avif} />
<ImageCard
id="image-card"
alt="image-card"
src="https://image-optimization-test.vercel.app/test.jpg"
/>
<DynamicSrcImage
id="dynamic-src"
alt="dynamic-src"
src="https://image-optimization-test.vercel.app/test.jpg"
width={400}
height={400}
Expand Down
4 changes: 2 additions & 2 deletions test/production/typescript-basic/app/pages/image-import.tsx
Expand Up @@ -6,9 +6,9 @@ export default function Page() {
return (
<>
<h1>Example Image Usage</h1>
<Image src="/test.jpg" width={200} height={200} />
<Image src="/test.jpg" width={200} height={200} alt="" />
<hr />
<FutureImage src={png} />
<FutureImage src={png} alt="" />
</>
)
}