-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: add `<PrismicImage>` * test: check that Imgix parameters are supported in `<PrismicImage>` * fix: only allow alt and fallbackAlt to declare an image as decorative * fix: incorrect import * docs: add TSDoc * docs: document default alt behavior * docs: add note about empty image field alt behavior
- Loading branch information
1 parent
bc7ad19
commit 7a93f69
Showing
8 changed files
with
596 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# `alt` must be an empty string | ||
|
||
`<PrismicImage>` allows the the `alt` HTML attribute to be configured using two props: `alt` and `fallbackAlt`. | ||
|
||
Both `alt` and `fallbackAlt` can only be used to [declare an image as decorative][mdn-alt-decorative-image] by pasing an empty string. You may not pass arbitrary alternative text to the `alt` prop. | ||
|
||
```tsx | ||
// Will render `alt` using the Image field's `alt` property. | ||
<PrismicImage field={doc.data.imageField} /> | ||
|
||
// Will always render `alt=""`. | ||
<PrismicImage field={doc.data.imageField} alt="" /> | ||
|
||
// Will render `alt=""` only if the Image field's alt property is empty. | ||
<PrismicImage field={doc.data.imageField} fallbackAlt="" /> | ||
``` | ||
|
||
All images should have an alt value. `<PrismicImage>` will automatically use the Image field's `alt` property written in the Prismic Editor. If the Image field's `alt` property is empty, the `alt` HTML attribute will not be included _unless_ one of `alt` or `fallbackAlt` is used. | ||
|
||
For more details on decorative images, [see the MDN article on the `<img>` HTMl element's `alt` attribute][mdn-alt-decorative-image]. | ||
|
||
## Deciding between `alt=""` and `fallbackAlt=""` | ||
|
||
`alt=""` will always mark the image as decorative, ignoring the provied Image field's `alt` property. Use this when the image is always used for decorative or presentational purposes. | ||
|
||
`fallbackAlt=""` will only mark the image as decorative if the provided Image field's `alt` property is empty. Use this when you want to mark the image as decorative _unless_ alternative text is provided in the Prismic Editor. **Generally speaking, this is discouraged**; prefer marking the image as decorative intentionally. | ||
|
||
[mdn-alt-decorative-image]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/alt#decorative_images |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
import * as React from "react"; | ||
import * as prismicT from "@prismicio/types"; | ||
import * as prismicH from "@prismicio/helpers"; | ||
|
||
import { __PRODUCTION__ } from "./lib/__PRODUCTION__"; | ||
import { devMsg } from "./lib/devMsg"; | ||
|
||
/** | ||
* Props for `<PrismicImage>`. | ||
*/ | ||
export type PrismicImageProps = Omit< | ||
React.DetailedHTMLProps< | ||
React.ImgHTMLAttributes<HTMLImageElement>, | ||
HTMLImageElement | ||
>, | ||
"src" | "srcset" | "alt" | ||
> & { | ||
/** | ||
* The Prismic Image field or thumbnail to render. | ||
*/ | ||
field: prismicT.ImageFieldImage | null | undefined; | ||
|
||
/** | ||
* An object of Imgix URL API parameters to transform the image. | ||
* | ||
* See: https://docs.imgix.com/apis/rendering | ||
*/ | ||
imgixParams?: Parameters<typeof prismicH.asImageSrc>[1]; | ||
|
||
/** | ||
* Declare an image as decorative by providing `alt=""`. | ||
* | ||
* See: | ||
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/alt#decorative_images | ||
*/ | ||
alt?: ""; | ||
|
||
/** | ||
* Declare an image as decorative only if the Image field does not have | ||
* alternative text by providing `fallbackAlt=""`. | ||
* | ||
* See: | ||
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/alt#decorative_images | ||
*/ | ||
fallbackAlt?: ""; | ||
} & ( | ||
| { | ||
/** | ||
* Widths used to build a `srcset` value for the Image field. | ||
* | ||
* If a `widths` prop is not given or `"defaults"` is passed, the | ||
* following widths will be used: 640, 750, 828, 1080, 1200, 1920, 2048, 3840. | ||
* | ||
* If the Image field contains responsive views, each responsive view | ||
* can be used as a width in the resulting `srcset` by passing | ||
* `"thumbnails"` as the `widths` prop. | ||
*/ | ||
widths?: | ||
| NonNullable< | ||
Parameters<typeof prismicH.asImageWidthSrcSet>[1] | ||
>["widths"] | ||
| "defaults"; | ||
/** | ||
* Not used when the `widths` prop is used. | ||
*/ | ||
pixelDensities?: never; | ||
} | ||
| { | ||
/** | ||
* Not used when the `widths` prop is used. | ||
*/ | ||
widths?: never; | ||
/** | ||
* Pixel densities used to build a `srcset` value for the Image field. | ||
* | ||
* If a `pixelDensities` prop is passed `"defaults"`, the following | ||
* pixel densities will be used: 1, 2, 3. | ||
*/ | ||
pixelDensities: | ||
| NonNullable< | ||
Parameters<typeof prismicH.asImagePixelDensitySrcSet>[1] | ||
>["pixelDensities"] | ||
| "defaults"; | ||
} | ||
); | ||
|
||
const _PrismicImage = ( | ||
props: PrismicImageProps, | ||
ref: React.ForwardedRef<HTMLImageElement>, | ||
): JSX.Element | null => { | ||
const { | ||
field, | ||
alt, | ||
fallbackAlt, | ||
imgixParams, | ||
widths, | ||
pixelDensities, | ||
...restProps | ||
} = props; | ||
|
||
if (!__PRODUCTION__) { | ||
if (typeof alt === "string" && props.alt !== "") { | ||
console.warn( | ||
`[PrismicImage] The alt prop can only be used to declare an image as decorative by passing an empty string (alt=""). For more details, see ${devMsg( | ||
"alt-must-be-an-empty-string", | ||
)}`, | ||
); | ||
} | ||
|
||
if (typeof fallbackAlt === "string" && fallbackAlt !== "") { | ||
console.warn( | ||
`[PrismicImage] The fallbackAlt prop can only be used to declare an image as decorative by passing an empty string (fallbackAlt=""). For more details, see ${devMsg( | ||
"alt-must-be-an-empty-string", | ||
)}`, | ||
); | ||
} | ||
|
||
if (widths && pixelDensities) { | ||
console.warn( | ||
`[PrismicImage] Only one of "widths" or "pixelDensities" props can be provided. "widths" will be used in this case.`, | ||
); | ||
} | ||
} | ||
|
||
if (prismicH.isFilled.imageThumbnail(field)) { | ||
let src: string | undefined; | ||
let srcSet: string | undefined; | ||
|
||
if (widths || !pixelDensities) { | ||
const res = prismicH.asImageWidthSrcSet(field, { | ||
...imgixParams, | ||
widths: widths === "defaults" ? undefined : widths, | ||
}); | ||
|
||
src = res.src; | ||
srcSet = res.srcset; | ||
} else if (pixelDensities) { | ||
const res = prismicH.asImagePixelDensitySrcSet(field, { | ||
...imgixParams, | ||
pixelDensities: | ||
pixelDensities === "defaults" ? undefined : pixelDensities, | ||
}); | ||
|
||
src = res.src; | ||
srcSet = res.srcset; | ||
} | ||
|
||
return ( | ||
<img | ||
ref={ref} | ||
src={src} | ||
srcSet={srcSet} | ||
alt={alt ?? (field.alt || fallbackAlt)} | ||
{...restProps} | ||
/> | ||
); | ||
} else { | ||
return null; | ||
} | ||
}; | ||
|
||
if (!__PRODUCTION__) { | ||
_PrismicImage.displayName = "PrismicImage"; | ||
} | ||
|
||
/** | ||
* React component that renders an image from a Prismic Image field or one of | ||
* its thumbnails. It will automatically set the `alt` attribute using the Image | ||
* field's `alt` property. | ||
* | ||
* By default, a widths-based srcset will be used to support responsive images. | ||
* This ensures only the smallest image needed for a browser is downloaded. | ||
* | ||
* To use a pixel-density-based srcset, use the `pixelDensities` prop. Default | ||
* pixel densities can be used by using `pixelDensities="defaults"`. | ||
* | ||
* **Note**: If you are using a framework that has a native image component, | ||
* such as Next.js and Gatsby, prefer using those image components instead. They | ||
* can provide deeper framework integration than `<PrismicImage>`. | ||
* | ||
* @param props - Props for the component. | ||
* | ||
* @returns A responsive image component for the given Image field. | ||
*/ | ||
export const PrismicImage = React.forwardRef(_PrismicImage); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { version } from "../../package.json"; | ||
|
||
/** | ||
* Returns a `prismic.dev/msg` URL for a given message slug. | ||
* | ||
* @example | ||
* | ||
* ```ts | ||
* devMsg("missing-param"); | ||
* // => "https://prismic.dev/msg/react/v1.2.3/missing-param.md" | ||
* ``` | ||
* | ||
* @param slug - Slug for the message. This corresponds to a Markdown file in | ||
* the Git repository's `/messages` directory. | ||
* | ||
* @returns The `prismic.dev/msg` URL for the given slug. | ||
*/ | ||
export const devMsg = (slug: string) => { | ||
return `https://prismic.dev/msg/react/v${version}/${slug}`; | ||
}; |
Oops, something went wrong.