@jetshop/ui 7.0.0-canary-2 → 7.0.0-canary-4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CloudflareImage/BaseImage.d.ts +39 -0
  2. package/CloudflareImage/BaseImage.js +192 -0
  3. package/CloudflareImage/BaseImage.js.map +1 -0
  4. package/CloudflareImage/Fallback.d.ts +5 -0
  5. package/CloudflareImage/Fallback.js +4 -0
  6. package/CloudflareImage/Fallback.js.map +1 -0
  7. package/CloudflareImage/FlightImage.d.ts +51 -0
  8. package/CloudflareImage/FlightImage.js +61 -0
  9. package/CloudflareImage/FlightImage.js.map +1 -0
  10. package/CloudflareImage/FlightImage.test.js +277 -0
  11. package/CloudflareImage/Image.d.ts +2 -0
  12. package/CloudflareImage/Image.js +3 -0
  13. package/CloudflareImage/Image.js.map +1 -0
  14. package/CloudflareImage/index.d.ts +1 -0
  15. package/CloudflareImage/index.js +2 -0
  16. package/CloudflareImage/index.js.map +1 -0
  17. package/CloudflareImage/useConstructImage.d.ts +49 -0
  18. package/CloudflareImage/useConstructImage.js +123 -0
  19. package/CloudflareImage/useConstructImage.js.map +1 -0
  20. package/CloudflareImage/utils/aspectRatioToFraction.d.ts +2 -0
  21. package/CloudflareImage/utils/aspectRatioToFraction.js +11 -0
  22. package/CloudflareImage/utils/aspectRatioToFraction.js.map +1 -0
  23. package/CloudflareImage/utils/getFullUrl.d.ts +6 -0
  24. package/CloudflareImage/utils/getFullUrl.js +18 -0
  25. package/CloudflareImage/utils/getFullUrl.js.map +1 -0
  26. package/CloudflareImage/utils/getImageWidth.d.ts +1 -0
  27. package/CloudflareImage/utils/getImageWidth.js +12 -0
  28. package/CloudflareImage/utils/getImageWidth.js.map +1 -0
  29. package/CloudflareImage/utils/getImageWidths.d.ts +2 -0
  30. package/CloudflareImage/utils/getImageWidths.js +21 -0
  31. package/CloudflareImage/utils/getImageWidths.js.map +1 -0
  32. package/CloudflareImage/utils/getLQIP.d.ts +8 -0
  33. package/CloudflareImage/utils/getLQIP.js +21 -0
  34. package/CloudflareImage/utils/getLQIP.js.map +1 -0
  35. package/CloudflareImage/utils/getLargestSize.d.ts +7 -0
  36. package/CloudflareImage/utils/getLargestSize.js +17 -0
  37. package/CloudflareImage/utils/getLargestSize.js.map +1 -0
  38. package/CloudflareImage/utils/getSizeForBreakpoint.d.ts +2 -0
  39. package/CloudflareImage/utils/getSizeForBreakpoint.js +16 -0
  40. package/CloudflareImage/utils/getSizeForBreakpoint.js.map +1 -0
  41. package/CloudflareImage/utils/getSizesMap.d.ts +2 -0
  42. package/CloudflareImage/utils/getSizesMap.js +35 -0
  43. package/CloudflareImage/utils/getSizesMap.js.map +1 -0
  44. package/CloudflareImage/utils/getSmallestSize.d.ts +2 -0
  45. package/CloudflareImage/utils/getSmallestSize.js +12 -0
  46. package/CloudflareImage/utils/getSmallestSize.js.map +1 -0
  47. package/CloudflareImage/utils/getSrcSetFromWidths.d.ts +10 -0
  48. package/CloudflareImage/utils/getSrcSetFromWidths.js +10 -0
  49. package/CloudflareImage/utils/getSrcSetFromWidths.js.map +1 -0
  50. package/CloudflareImage/utils/getSrcWithParams.d.ts +3 -0
  51. package/CloudflareImage/utils/getSrcWithParams.js +30 -0
  52. package/CloudflareImage/utils/getSrcWithParams.js.map +1 -0
  53. package/CloudflareImage/utils/paddingForAspectRatio.d.ts +1 -0
  54. package/CloudflareImage/utils/paddingForAspectRatio.js +6 -0
  55. package/CloudflareImage/utils/paddingForAspectRatio.js.map +1 -0
  56. package/CloudflareImage/utils/remToPx.d.ts +1 -0
  57. package/CloudflareImage/utils/remToPx.js +2 -0
  58. package/CloudflareImage/utils/remToPx.js.map +1 -0
  59. package/CloudflareImage/utils/sizeToNumber.d.ts +2 -0
  60. package/CloudflareImage/utils/sizeToNumber.js +27 -0
  61. package/CloudflareImage/utils/sizeToNumber.js.map +1 -0
  62. package/Gallery/Gallery.js +1 -1
  63. package/Gallery/Gallery.js.map +1 -1
  64. package/ProductList/GridProduct.js +1 -1
  65. package/ProductList/GridProduct.js.map +1 -1
  66. package/package.json +1 -1
@@ -0,0 +1,39 @@
1
+ import * as React from 'react';
2
+ import { ProductImageSize } from '@jetshop/core/types';
3
+ export type ImageSrcType = string | ProductImageSize[];
4
+ export type BreakpointValues = string[];
5
+ export type ImageBreakpointSize = number | string;
6
+ export type ImageBreakpointSizes = ImageBreakpointSize | ImageBreakpointSize[];
7
+ export type AspectRatio = string | number;
8
+ export declare const transparentDataImg = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=";
9
+ export interface ImageParameters {
10
+ src: string | boolean;
11
+ sizes: string;
12
+ hasFalsySrc: boolean;
13
+ srcset: string;
14
+ webpSrcset: string;
15
+ lqip: string;
16
+ /**
17
+ * The aspect ratio of the image
18
+ * If unknown, use null to preserve original aspect ratio
19
+ */
20
+ aspect?: string;
21
+ critical?: boolean;
22
+ }
23
+ export interface ImageProps extends ImageParameters {
24
+ /** Whether or not to use `background-size: cover` */
25
+ cover?: boolean;
26
+ error?: (args: any) => React.ReactElement<any>;
27
+ alt?: string;
28
+ title?: string;
29
+ /**
30
+ * Useful for banner images. Sets the following CSS automatically:
31
+ * object-fit: cover
32
+ * padding-top: 0;
33
+ */
34
+ fillAvailableSpace?: boolean;
35
+ className?: string;
36
+ badges?: React.ReactElement<any>;
37
+ }
38
+ declare const Image: React.FC<ImageProps>;
39
+ export default Image;
@@ -0,0 +1,192 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import * as React from 'react';
13
+ import { useInView } from 'react-intersection-observer';
14
+ import { paddingForAspectRatio } from './utils/paddingForAspectRatio';
15
+ export const transparentDataImg = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
16
+ const defaultImageState = {
17
+ imgLoaded: false,
18
+ error: false
19
+ };
20
+ const loadedImageState = {
21
+ imgLoaded: true,
22
+ error: false
23
+ };
24
+ let hydrated = false;
25
+ const imageCache = Object.create({});
26
+ function isInCache(src) {
27
+ return src in imageCache;
28
+ }
29
+ function cacheImage(src) {
30
+ imageCache[src] = true;
31
+ }
32
+ function imageStateReducer(_, action) {
33
+ switch (action.type) {
34
+ case 'reset':
35
+ return defaultImageState;
36
+ case 'loaded':
37
+ cacheImage(action.cacheKey);
38
+ return loadedImageState;
39
+ case 'error':
40
+ return { imgLoaded: false, error: true };
41
+ default:
42
+ console.error(`Action ${action.type} is invalid for image reducer`);
43
+ return;
44
+ }
45
+ }
46
+ function useObjectFitPolyfill(imageRefs) {
47
+ const [polyfilled, setPolyfilled] = React.useState(false);
48
+ React.useEffect(() => {
49
+ const testImg = document.createElement(`img`);
50
+ if (typeof testImg.style.objectFit === `undefined` ||
51
+ typeof testImg.style.objectPosition === `undefined`) {
52
+ import(`object-fit-images`).then(({ default: ObjectFitImages }) => {
53
+ imageRefs.forEach((imageRef) => {
54
+ imageRef.current && ObjectFitImages(imageRef.current);
55
+ });
56
+ setPolyfilled(true);
57
+ });
58
+ }
59
+ }, [imageRefs]);
60
+ return polyfilled;
61
+ }
62
+ function getTypeByExtension(src) {
63
+ // Identifying type by extension, based on common image file types
64
+ // https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types
65
+ const extensionRegex = /\.(jpg|jpeg|jfif|pjpeg|pjp|png|gif|bmp|webp|svg|apng|ico|cur)(?:\?.*)?$/i;
66
+ const formatMatch = extensionRegex.exec(src);
67
+ if (!formatMatch)
68
+ return null;
69
+ let type = null;
70
+ switch (formatMatch[1]) {
71
+ case 'jpg':
72
+ case 'jpeg':
73
+ case 'jfif':
74
+ case 'pjpeg':
75
+ case 'pjp':
76
+ type = 'image/jpeg';
77
+ break;
78
+ case 'png':
79
+ type = 'image/png';
80
+ break;
81
+ case 'gif':
82
+ type = 'image/gif';
83
+ break;
84
+ case 'bmp':
85
+ type = 'image/bmp';
86
+ break;
87
+ case 'webp':
88
+ type = 'image/webp';
89
+ break;
90
+ case 'svg':
91
+ type = 'image/svg+xml';
92
+ break;
93
+ case 'apng':
94
+ type = 'image/apng';
95
+ break;
96
+ case 'ico':
97
+ case 'cur':
98
+ type = 'image/x-icon';
99
+ break;
100
+ default:
101
+ break;
102
+ }
103
+ return type;
104
+ }
105
+ const Img = React.forwardRef((props, ref) => {
106
+ const { sizes, srcSet, src, style, alt, onLoad, onError, objectFit, critical } = props, otherProps = __rest(props, ["sizes", "srcSet", "src", "style", "alt", "onLoad", "onError", "objectFit", "critical"]);
107
+ const imgStyle = Object.assign({ position: 'absolute', top: 0, left: 0, width: `100%`, height: `100%`, paddingTop: 0, objectPosition: `center`, objectFit }, style);
108
+ return (React.createElement("img", Object.assign({ alt: alt, sizes: sizes, srcSet: srcSet, src: src, onLoad: onLoad, onError: onError, loading: critical ? 'eager' : 'lazy' }, otherProps, { ref: ref, style: imgStyle })));
109
+ });
110
+ const Placeholder = React.forwardRef((props, ref) => {
111
+ return (React.createElement("picture", { "data-flight-image-placeholder": "" },
112
+ React.createElement(Img, Object.assign({ loading: "lazy" }, props, { ref: ref }))));
113
+ });
114
+ const Image = function Image(_a) {
115
+ var { src, hasFalsySrc, lqip, sizes, srcset, webpSrcset, critical, aspect, cover = false, alt = '', fillAvailableSpace = false, children, error: ErrorComp, className, badges } = _a, rest = __rest(_a, ["src", "hasFalsySrc", "lqip", "sizes", "srcset", "webpSrcset", "critical", "aspect", "cover", "alt", "fillAvailableSpace", "children", "error", "className", "badges"]);
116
+ // Set up the IntersectionObserver
117
+ const [ref, inView] = useInView({ triggerOnce: true });
118
+ // Check if the browser supports loading="lazy". In that case, we don't need
119
+ // the IntersectionObserver
120
+ const nativeLazyLoading = typeof HTMLImageElement !== 'undefined' &&
121
+ 'loading' in HTMLImageElement.prototype;
122
+ const imageRef = React.createRef();
123
+ const placeholderRef = React.createRef();
124
+ useObjectFitPolyfill([imageRef, placeholderRef]);
125
+ const [{ imgLoaded, error }, dispatch] = React.useReducer(imageStateReducer, critical ? loadedImageState : defaultImageState);
126
+ const cacheKey = srcset;
127
+ const handleImgLoad = React.useCallback(function handleImgLoad() {
128
+ dispatch({ type: 'loaded', cacheKey });
129
+ }, [cacheKey]);
130
+ function handleImgError() {
131
+ dispatch({ type: 'error' });
132
+ }
133
+ // Treat critical images as always cached
134
+ const isCached = critical || isInCache(cacheKey);
135
+ React.useEffect(() => {
136
+ hydrated = true;
137
+ }, []);
138
+ React.useEffect(() => {
139
+ dispatch({ type: 'reset' });
140
+ }, [src]);
141
+ React.useEffect(() => {
142
+ // Handle case where image onLoad fires during SSR
143
+ if (imageRef.current && imageRef.current.complete && !isCached) {
144
+ handleImgLoad();
145
+ }
146
+ }, [handleImgLoad, imageRef, isCached]);
147
+ // Render nothing if src is boolean
148
+ if (typeof src === 'boolean' && !src) {
149
+ return null;
150
+ }
151
+ const loaded = imgLoaded || hasFalsySrc || isCached;
152
+ const objectFit = cover || fillAvailableSpace ? 'cover' : 'contain';
153
+ const objectFitPolyfillStyles = { fontFamily: `"object-fit: ${objectFit}"` };
154
+ const type = getTypeByExtension(src);
155
+ return (React.createElement(React.Fragment, null,
156
+ React.createElement("div", Object.assign({ key: src, ref:
157
+ // If we have native lazyloading, by not applying the `ref` from
158
+ // useInView we don't use IntersectionObserver at all
159
+ nativeLazyLoading ? undefined : ref, "data-flight-image-container": "", "data-flight-image-loaded": loaded, className: className }, rest, { style: {
160
+ position: `relative`,
161
+ overflow: `hidden`,
162
+ width: `100%`,
163
+ height: fillAvailableSpace ? '100%' : 'auto',
164
+ paddingBottom: fillAvailableSpace
165
+ ? 0
166
+ : !aspect
167
+ ? '100%'
168
+ : paddingForAspectRatio(aspect)
169
+ } }),
170
+ error ? (React.createElement(ErrorComp, null)) : (React.createElement(React.Fragment, null,
171
+ React.createElement(Placeholder, { src: critical ? transparentDataImg : lqip || transparentDataImg, ref: placeholderRef, alt: "", "aria-hidden": "true", style: Object.assign({ objectFit, opacity: loaded ? 0 : 0.6, transitionDelay: `500ms` }, objectFitPolyfillStyles) }),
172
+ (inView || nativeLazyLoading || !hydrated || isCached) && (React.createElement("picture", { "data-flight-image": "" },
173
+ React.createElement("source", { type: "image/webp", srcSet: webpSrcset, sizes: sizes }),
174
+ React.createElement("source", { type: type, srcSet: srcset, sizes: sizes }),
175
+ React.createElement(Img, { srcSet: srcset, src: src || transparentDataImg, sizes: sizes, onLoad: handleImgLoad, onError: handleImgError, alt: alt, ref: imageRef, style: Object.assign({ objectFit, opacity: loaded ? 1 : 0, transition: `opacity 500ms` }, objectFitPolyfillStyles), critical: critical, fetchpriority: critical ? 'high' : 'auto' }))),
176
+ React.createElement("noscript", null,
177
+ React.createElement("picture", null,
178
+ React.createElement(Img, { srcSet: srcset, src: src, sizes: sizes, alt: alt, style: {
179
+ objectFit
180
+ }, critical: critical, fetchpriority: critical ? 'high' : 'auto' }))))),
181
+ children && (React.createElement("div", { "data-flight-image-children": "", style: {
182
+ position: 'relative',
183
+ zIndex: 2,
184
+ width: '100%',
185
+ height: '100%'
186
+ } }, children)),
187
+ badges && (React.createElement("div", { "data-flight-image-badges": "", style: {
188
+ zIndex: 2
189
+ } }, badges)))));
190
+ };
191
+ export default Image;
192
+ //# sourceMappingURL=BaseImage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseImage.js","sourceRoot":"","sources":["BaseImage.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ /** Display this if JS is disabled. */
3
+ export declare const Fallback: ({ children }: {
4
+ children: React.ReactElement<{}>;
5
+ }) => React.JSX.Element;
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ /** Display this if JS is disabled. */
3
+ export const Fallback = ({ children }) => React.createElement("noscript", null, children);
4
+ //# sourceMappingURL=Fallback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Fallback.js","sourceRoot":"","sources":["Fallback.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,sCAAsC;AAEtC,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,EACvB,QAAQ,EAGT,EAAE,EAAE,CAAC,sCAAW,QAAQ,CAAY,CAAC"}
@@ -0,0 +1,51 @@
1
+ import React from 'react';
2
+ import { ProductImageSize } from '@jetshop/core/types';
3
+ export type ImageSrcType = string | ProductImageSize[];
4
+ export type ImageBreakpointSize = number | string;
5
+ export type ImageBreakpointSizes = ImageBreakpointSize | ImageBreakpointSize[];
6
+ export type AspectRatio = string | number;
7
+ export type Gravity = 'north' | 'south' | 'west' | 'east' | 'centre' | 'smart';
8
+ export declare const transparentDataImg = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=";
9
+ interface ImageParameters {
10
+ /** The image src
11
+ * Also accepts an array of objects with `url` keys (deprecated image field)
12
+ */
13
+ src: ImageSrcType;
14
+ /**
15
+ * Whether to crop the image instead of resizing it when aspect ratio is provided
16
+ * @default false
17
+ */
18
+ crop?: boolean;
19
+ gravity?: Gravity;
20
+ quality?: number;
21
+ /** An array of image sizes, will be mapped against breakpoints.
22
+ * @see https://docs.dev.jetshop.se/ui/image#the-sizes-prop
23
+ */
24
+ sizes: ImageBreakpointSizes;
25
+ /**
26
+ * The aspect ratio of the image
27
+ * If unknown, use null to preserve original aspect ratio
28
+ */
29
+ aspect?: string;
30
+ critical?: boolean;
31
+ modifiedDate?: string;
32
+ focalPointX?: number;
33
+ focalPointY?: number;
34
+ }
35
+ interface ImageProps extends ImageParameters {
36
+ /** Whether or not to use `background-size: cover` */
37
+ cover?: boolean;
38
+ error?: (args: any) => React.ReactElement<any>;
39
+ alt?: string;
40
+ title?: string;
41
+ /**
42
+ * Useful for banner images. Sets the following CSS automatically:
43
+ * object-fit: cover
44
+ * padding-top: 0;
45
+ */
46
+ fillAvailableSpace?: boolean;
47
+ className?: string;
48
+ badges?: React.ReactElement<any>;
49
+ }
50
+ declare function FlightImage(props: ImageProps): React.JSX.Element;
51
+ export default FlightImage;
@@ -0,0 +1,61 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { useShopConfig } from '@jetshop/core/hooks/useShopConfig';
13
+ import Image from './BaseImage';
14
+ import React from 'react';
15
+ import t from '@jetshop/intl';
16
+ import { useConstructImage } from './useConstructImage';
17
+ import ChannelContext from '@jetshop/core/components/ChannelContext';
18
+ import { Below } from '../Breakpoints';
19
+ export const transparentDataImg = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
20
+ function Error() {
21
+ return (React.createElement("div", { style: {
22
+ background: '#e2e2e2',
23
+ display: 'flex',
24
+ justifyContent: 'center',
25
+ alignItems: 'center',
26
+ position: `absolute`,
27
+ top: 0,
28
+ left: 0,
29
+ width: `100%`,
30
+ height: `100%`
31
+ } }, t('Image Not Found')));
32
+ }
33
+ function FlightImage(props) {
34
+ const { gravity, crop, src: originalSrc, sizes: sizesArray = [1], aspect, quality, critical, modifiedDate, error: ErrorComp = Error, focalPointX, focalPointY } = props, rest = __rest(props, ["gravity", "crop", "src", "sizes", "aspect", "quality", "critical", "modifiedDate", "error", "focalPointX", "focalPointY"]);
35
+ const { theme: { breakpoints }, useMobileImageSizes = false } = useShopConfig();
36
+ // The baseUrl will change based on the currently-selected channel.
37
+ // We need it to correctly construct the image url
38
+ const { selectedChannel } = React.useContext(ChannelContext);
39
+ const baseUrl = (selectedChannel === null || selectedChannel === void 0 ? void 0 : selectedChannel.imageUrl)
40
+ ? String(selectedChannel.imageUrl)
41
+ : null;
42
+ const imageOptions = useConstructImage({
43
+ gravity,
44
+ crop,
45
+ src: originalSrc,
46
+ sizes: sizesArray,
47
+ aspect,
48
+ quality,
49
+ critical,
50
+ modifiedDate,
51
+ breakpoints,
52
+ baseUrl,
53
+ focalPointX,
54
+ focalPointY
55
+ });
56
+ const { hasFalsySrc, lqip, sizes, sizesMobile, srcset, srcsetMobile, webpSrcset, webpSrcsetMobile, src } = imageOptions;
57
+ // // https://<ZONE>/cdn-cgi/image/<OPTIONS>/<SOURCE-IMAGE>
58
+ return (React.createElement(Below, { breakpoint: 'sm' }, (matches) => (React.createElement(Image, Object.assign({}, rest, { critical: critical, aspect: aspect, src: hasFalsySrc && typeof originalSrc === 'boolean' ? false : src, hasFalsySrc: hasFalsySrc, lqip: lqip, sizes: matches && useMobileImageSizes ? sizesMobile : sizes, srcset: matches && useMobileImageSizes ? srcsetMobile : srcset, webpSrcset: matches && useMobileImageSizes ? webpSrcsetMobile : webpSrcset, error: ErrorComp })))));
59
+ }
60
+ export default FlightImage;
61
+ //# sourceMappingURL=FlightImage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FlightImage.js","sourceRoot":"","sources":["FlightImage.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,277 @@
1
+ import { ChannelProvider } from '@jetshop/core/components/ChannelContext';
2
+ import t from '@jetshop/intl';
3
+ import testImage from '@jetshop/ui/test-utils/fixtures/test-image.png';
4
+ import { fireEvent, queryByText, render } from '@testing-library/react';
5
+ import React from 'react';
6
+ import { useInView } from 'react-intersection-observer';
7
+ import Image from './FlightImage';
8
+ import { ChannelHandler } from '@jetshop/core/ChannelHandler/ChannelHandler';
9
+ import { selectedChannel, channels } from '@jetshop/core/test-utils/variables';
10
+ import { ConfigProvider } from '@jetshop/core/components/ConfigProvider';
11
+ import mockShopConfig from '@jetshop/core/test-utils/mockShopConfig';
12
+
13
+ // Mock intersection observer so we can always return true/false
14
+ jest.mock('react-intersection-observer');
15
+ // Suppress warnings from translations
16
+ jest.mock('@jetshop/intl');
17
+ t.mockImplementation((t) => t);
18
+
19
+ // browser mocks
20
+ global.IntersectionObserver = jest.fn(function () {
21
+ this.observe = jest.fn();
22
+ this.unobserve = jest.fn();
23
+ this.disconnect = jest.fn();
24
+ });
25
+
26
+ const transparent =
27
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
28
+
29
+ global.__IN_SERVER__ = false;
30
+
31
+ describe('Hooks image component', () => {
32
+ describe('on initial load', () => {
33
+ beforeAll(() => {
34
+ useInView.mockImplementation(() => [null, false]);
35
+ });
36
+ it('renders a lqip version of the image by default', () => {
37
+ const { placeholderImage } = setup();
38
+
39
+ const src = placeholderImage.getAttribute('src');
40
+
41
+ expect(src).toEqual(expect.stringContaining('sigma=2.5'));
42
+ expect(src).toEqual(expect.stringContaining('minampl=0.5'));
43
+ });
44
+
45
+ it('sets the lqip to the smallest of the calculated sizes', () => {
46
+ const { placeholderImage } = setup({ sizes: [0.5, 0.85, 1] });
47
+ // Should be 160
48
+ // The smallest breakpoint is 20rem, which is 360px.
49
+ // 0.5 x 360 = 160
50
+
51
+ const src = placeholderImage.getAttribute('src');
52
+
53
+ expect(src).toEqual(expect.stringContaining('width=160'));
54
+ });
55
+
56
+ it('does not render an lqip if critical is true', () => {
57
+ const { placeholderImage } = setup({ critical: true });
58
+
59
+ const src = placeholderImage.getAttribute('src');
60
+
61
+ // Sets the src to transaprent data image instead
62
+ expect(src).toEqual(expect.stringContaining('data:image'));
63
+ });
64
+
65
+ it('accepts sizes as pixel values', () => {
66
+ const { placeholderImage } = setup({ sizes: [123] });
67
+
68
+ const src = placeholderImage.getAttribute('src');
69
+
70
+ expect(src).toEqual(expect.stringContaining('width=123'));
71
+ });
72
+
73
+ it('sets opacity to 0.6 while image has not loaded', () => {
74
+ const { placeholderImage } = setup();
75
+
76
+ expect(placeholderImage).toHaveStyle('opacity: 0.6');
77
+ });
78
+ });
79
+
80
+ describe('sets the sizing method', () => {
81
+ it('to thumbnail if no aspect ratio is provided', () => {
82
+ const { placeholderImage } = setup();
83
+ });
84
+ it('to fit if an aspect ratio is provided', () => {
85
+ const { placeholderImage } = setup({ aspect: '1:1' });
86
+
87
+ expect(placeholderImage.getAttribute('src')).toEqual(
88
+ expect.stringContaining('&height=320')
89
+ );
90
+ expect(placeholderImage.getAttribute('src')).toEqual(
91
+ expect.stringContaining('&width=320')
92
+ );
93
+ });
94
+ it('to crop if crop prop is true', () => {
95
+ const { placeholderImage } = setup({ aspect: '1:1', crop: true });
96
+
97
+ expect(placeholderImage.getAttribute('src')).toEqual(
98
+ expect.stringContaining('&method=crop')
99
+ );
100
+ });
101
+ it('warns if gravity is provided without crop', () => {
102
+ console.warn = jest.fn();
103
+ setup({ aspect: '1:1', crop: false, gravity: 'north' });
104
+ expect(console.warn).toHaveBeenCalled();
105
+ });
106
+ });
107
+
108
+ describe('when image is in view', () => {
109
+ beforeAll(() => {
110
+ useInView.mockImplementation(() => [null, true]);
111
+ });
112
+
113
+ it('renders full image', () => {
114
+ const { image } = setup({
115
+ imageSrc: testImage
116
+ });
117
+
118
+ expect(image).toHaveAttribute('src', 'test-image.png');
119
+ });
120
+
121
+ it('applies the given alt as an aria-label', () => {
122
+ const { image } = setup({
123
+ alt: 'test alt'
124
+ });
125
+
126
+ expect(image).toHaveAttribute('alt', 'test alt');
127
+ });
128
+
129
+ it('defaults aspect to 1:1', () => {
130
+ const { imageContainer } = setup();
131
+
132
+ expect(imageContainer).toHaveStyle('padding-bottom : 100%');
133
+ });
134
+
135
+ it('sets aspect ratio using padding if aspect is passed', () => {
136
+ const { imageContainer } = setup({ aspect: '3:2' });
137
+
138
+ expect(imageContainer).toHaveStyle('padding-bottom : 66.6667%');
139
+ });
140
+
141
+ it('defaults to background-size contain', () => {
142
+ const { image } = setup();
143
+
144
+ expect(image).toHaveStyle('object-fit : contain');
145
+ });
146
+
147
+ it('sets background-size to cover if `cover` is passed', () => {
148
+ const { image } = setup({ cover: true });
149
+
150
+ expect(image).toHaveStyle('object-fit : cover');
151
+ });
152
+
153
+ it('sets up image to fill available space if `fillAvailableSpace` is passed', () => {
154
+ const { image } = setup({ fillAvailableSpace: true });
155
+
156
+ expect(image).toHaveStyle('object-fit : cover');
157
+ expect(image).toHaveStyle('padding-top : 0');
158
+ });
159
+
160
+ it('sets opacity to 1 when image loads', () => {
161
+ const { image } = setup();
162
+ fireEvent.load(image);
163
+
164
+ expect(image).toHaveStyle('opacity: 1');
165
+ });
166
+
167
+ it('fades lqip when image loads', () => {
168
+ const { image, placeholderImage } = setup();
169
+ fireEvent.load(image);
170
+
171
+ expect(placeholderImage).toHaveStyle('opacity: 0');
172
+ });
173
+
174
+ test('sets quality to quality prop', () => {
175
+ const { image } = setup({ quality: 1 });
176
+
177
+ expect(image.getAttribute('srcSet')).toEqual(
178
+ expect.stringContaining('&quality=1')
179
+ );
180
+ });
181
+ });
182
+
183
+ describe('when there is an image error', () => {
184
+ beforeAll(() => {
185
+ useInView.mockImplementation(() => [null, true]);
186
+ });
187
+
188
+ it('renders default error', () => {
189
+ const { image, getByText } = setup();
190
+ fireEvent.error(image);
191
+
192
+ expect(getByText('Image Not Found'));
193
+ });
194
+
195
+ it('renders error component', () => {
196
+ const { image, getByText, container } = setup({
197
+ error: () => <div>some error found</div>
198
+ });
199
+ fireEvent.error(image);
200
+
201
+ expect(getByText('some error found'));
202
+ expect(queryByText(container, 'Image Not Found')).not.toBeInTheDocument();
203
+ });
204
+ });
205
+
206
+ describe('when image url is falsy', () => {
207
+ it('renders a transparent background image when null', () => {
208
+ const { image } = setup({ falsySrc: true, imageSrc: null });
209
+
210
+ expect(image).toHaveAttribute(`src`, transparent);
211
+ expect(image).toHaveStyle('opacity : 1');
212
+ });
213
+ it('renders a transparent background image when undefined', () => {
214
+ const { image } = setup({ falsySrc: true, imageSrc: undefined });
215
+
216
+ expect(image).toHaveAttribute(`src`, transparent);
217
+ expect(image).toHaveStyle('opacity: 1');
218
+ });
219
+ it('renders nothing and warns if false', () => {
220
+ console.warn = jest.fn();
221
+ const { image } = setup({ falsySrc: true, imageSrc: false });
222
+
223
+ expect(image).not.toBeInTheDocument();
224
+ expect(console.warn).toHaveBeenCalled();
225
+ });
226
+ });
227
+
228
+ it('renders a webp and jpeg source', () => {
229
+ const { imageContainer } = setup();
230
+
231
+ const sources = imageContainer.querySelectorAll('source');
232
+ expect(sources.length).toBe(2);
233
+ expect(sources[0]).toHaveAttribute('type', 'image/webp');
234
+ });
235
+ });
236
+
237
+ function setup({
238
+ imageSrc,
239
+ falsySrc = false,
240
+ error,
241
+ alt,
242
+ sizes,
243
+ aspect,
244
+ cover,
245
+ ...rest
246
+ } = {}) {
247
+ const channelHandler = new ChannelHandler({
248
+ selectedChannel,
249
+ channels
250
+ });
251
+ const utils = render(
252
+ <ChannelProvider channelHandler={channelHandler}>
253
+ <ConfigProvider config={mockShopConfig}>
254
+ <Image
255
+ src={falsySrc ? imageSrc : imageSrc || 'default.png'}
256
+ error={error}
257
+ alt={alt}
258
+ sizes={sizes}
259
+ aspect={aspect}
260
+ cover={cover}
261
+ {...rest}
262
+ />
263
+ </ConfigProvider>
264
+ </ChannelProvider>
265
+ );
266
+
267
+ return {
268
+ ...utils,
269
+ image: utils.container.querySelector('[data-flight-image] img'),
270
+ placeholderImage: utils.container.querySelector(
271
+ '[data-flight-image-placeholder] img'
272
+ ),
273
+ imageContainer: utils.container.querySelector(
274
+ '[data-flight-image-container]'
275
+ )
276
+ };
277
+ }
@@ -0,0 +1,2 @@
1
+ export * from './FlightImage';
2
+ export { default } from './FlightImage';
@@ -0,0 +1,3 @@
1
+ export * from './FlightImage';
2
+ export { default } from './FlightImage';
3
+ //# sourceMappingURL=Image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Image.js","sourceRoot":"","sources":["Image.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC"}