react-datocms 5.0.3 → 6.0.1

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 (72) hide show
  1. package/README.md +4 -13
  2. package/dist/cjs/Image/index.js +39 -138
  3. package/dist/cjs/Image/index.js.map +1 -1
  4. package/dist/cjs/Image/utils.js +52 -0
  5. package/dist/cjs/Image/utils.js.map +1 -0
  6. package/dist/cjs/SRCImage/index.js +44 -0
  7. package/dist/cjs/SRCImage/index.js.map +1 -0
  8. package/dist/cjs/SRCImage/utils.js +82 -0
  9. package/dist/cjs/SRCImage/utils.js.map +1 -0
  10. package/dist/cjs/Seo/nextUtils.js +15 -15
  11. package/dist/cjs/Seo/nextUtils.js.map +1 -1
  12. package/dist/cjs/Seo/renderMetaTags.js +1 -1
  13. package/dist/cjs/Seo/renderMetaTags.js.map +1 -1
  14. package/dist/cjs/StructuredText/index.js.map +1 -1
  15. package/dist/cjs/VideoPlayer/index.js +1 -1
  16. package/dist/cjs/index.js +2 -1
  17. package/dist/cjs/index.js.map +1 -1
  18. package/dist/cjs/useSiteSearch/index.js +9 -31
  19. package/dist/cjs/useSiteSearch/index.js.map +1 -1
  20. package/dist/cjs/useVideoPlayer/index.js.map +1 -1
  21. package/dist/esm/Image/index.js +31 -110
  22. package/dist/esm/Image/index.js.map +1 -1
  23. package/dist/esm/Image/utils.js +46 -0
  24. package/dist/esm/Image/utils.js.map +1 -0
  25. package/dist/esm/SRCImage/index.js +37 -0
  26. package/dist/esm/SRCImage/index.js.map +1 -0
  27. package/dist/esm/SRCImage/utils.js +52 -0
  28. package/dist/esm/SRCImage/utils.js.map +1 -0
  29. package/dist/esm/Seo/nextUtils.js +15 -15
  30. package/dist/esm/Seo/nextUtils.js.map +1 -1
  31. package/dist/esm/Seo/renderMetaTags.js +1 -1
  32. package/dist/esm/Seo/renderMetaTags.js.map +1 -1
  33. package/dist/esm/StructuredText/index.js.map +1 -1
  34. package/dist/esm/VideoPlayer/index.js +1 -1
  35. package/dist/esm/index.js +2 -1
  36. package/dist/esm/index.js.map +1 -1
  37. package/dist/esm/useSiteSearch/index.js +2 -1
  38. package/dist/esm/useSiteSearch/index.js.map +1 -1
  39. package/dist/esm/useVideoPlayer/index.js.map +1 -1
  40. package/dist/types/Image/index.d.ts +3 -4
  41. package/dist/types/Image/utils.d.ts +7 -0
  42. package/dist/types/SRCImage/index.d.ts +33 -0
  43. package/dist/types/SRCImage/utils.d.ts +6 -0
  44. package/dist/types/Seo/remixUtils.d.ts +1 -1
  45. package/dist/types/Seo/renderMetaTags.d.ts +1 -1
  46. package/dist/types/Seo/renderMetaTagsToString.d.ts +1 -1
  47. package/dist/types/StructuredText/index.d.ts +3 -3
  48. package/dist/types/VideoPlayer/index.d.ts +1 -1
  49. package/dist/types/index.d.ts +2 -1
  50. package/dist/types/useQuerySubscription/index.d.ts +1 -1
  51. package/dist/types/useVideoPlayer/index.d.ts +2 -2
  52. package/package.json +3 -4
  53. package/src/Image/__tests__/__snapshots__/index.test.tsx.snap +387 -60
  54. package/src/Image/__tests__/index.test.tsx +55 -8
  55. package/src/Image/index.tsx +65 -177
  56. package/src/Image/utils.ts +58 -0
  57. package/src/SRCImage/__tests__/__snapshots__/index.test.tsx.snap +274 -0
  58. package/src/SRCImage/__tests__/index.test.tsx +91 -0
  59. package/src/SRCImage/index.tsx +99 -0
  60. package/src/SRCImage/utils.tsx +95 -0
  61. package/src/Seo/__tests__/index.test.tsx +1 -1
  62. package/src/Seo/nextUtils.ts +20 -20
  63. package/src/Seo/remixUtils.ts +1 -1
  64. package/src/Seo/renderMetaTags.tsx +2 -2
  65. package/src/Seo/renderMetaTagsToString.tsx +1 -1
  66. package/src/StructuredText/__tests__/index.test.tsx +2 -2
  67. package/src/StructuredText/index.tsx +10 -10
  68. package/src/VideoPlayer/index.tsx +2 -2
  69. package/src/index.ts +2 -1
  70. package/src/useQuerySubscription/index.ts +4 -4
  71. package/src/useSiteSearch/index.tsx +29 -28
  72. package/src/useVideoPlayer/index.ts +3 -6
@@ -0,0 +1,274 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Image explicit sizes renders correctly 1`] = `
4
+ <SRCImage
5
+ data={
6
+ {
7
+ "base64": "data:image/jpeg;base64,<IMAGE-DATA>",
8
+ "height": 421,
9
+ "src": "https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750",
10
+ "width": 750,
11
+ }
12
+ }
13
+ sizes="(max-width: 600px) 200px, 50vw"
14
+ >
15
+ <picture>
16
+ <source
17
+ sizes="(max-width: 600px) 200px, 50vw"
18
+ srcSet="https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.25 187w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.5 375w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.75 562w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750 750w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=1.5 1125w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=2 1500w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=3 2250w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=4 3000w"
19
+ />
20
+ <img
21
+ alt=""
22
+ loading="lazy"
23
+ src="https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750"
24
+ style={
25
+ {
26
+ "aspectRatio": "750 / 421",
27
+ "backgroundImage": "url("data:image/jpeg;base64,<IMAGE-DATA>")",
28
+ "backgroundPosition": "50% 50%",
29
+ "backgroundRepeat": "no-repeat",
30
+ "backgroundSize": "cover",
31
+ "color": "transparent",
32
+ "height": "auto",
33
+ "maxWidth": "750px",
34
+ "width": "100%",
35
+ }
36
+ }
37
+ />
38
+ </picture>
39
+ </SRCImage>
40
+ `;
41
+
42
+ exports[`Image full data renders correctly 1`] = `
43
+ <SRCImage
44
+ data={
45
+ {
46
+ "alt": "DatoCMS swag",
47
+ "aspectRatio": 1.7777777777777777,
48
+ "base64": "data:image/jpeg;base64,<IMAGE-DATA>",
49
+ "height": 421,
50
+ "sizes": "(max-width: 750px) 100vw, 750px",
51
+ "src": "https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750",
52
+ "srcSet": "https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=0.25&fit=crop&w=750 187w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=0.5&fit=crop&w=750 375w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=0.75&fit=crop&w=750 562w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=1&fit=crop&w=750 750w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=1.5&fit=crop&w=750 1125w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=2&fit=crop&w=750 1500w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=3&fit=crop&w=750 2250w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=4&fit=crop&w=750 3000w",
53
+ "title": "These are awesome, we know that.",
54
+ "width": 750,
55
+ }
56
+ }
57
+ >
58
+ <picture>
59
+ <source
60
+ sizes="(max-width: 750px) 100vw, 750px"
61
+ srcSet="https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=0.25&fit=crop&w=750 187w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=0.5&fit=crop&w=750 375w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=0.75&fit=crop&w=750 562w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=1&fit=crop&w=750 750w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=1.5&fit=crop&w=750 1125w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=2&fit=crop&w=750 1500w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=3&fit=crop&w=750 2250w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=4&fit=crop&w=750 3000w"
62
+ />
63
+ <img
64
+ alt="DatoCMS swag"
65
+ loading="lazy"
66
+ src="https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750"
67
+ style={
68
+ {
69
+ "aspectRatio": "750 / 421",
70
+ "backgroundImage": "url("data:image/jpeg;base64,<IMAGE-DATA>")",
71
+ "backgroundPosition": "50% 50%",
72
+ "backgroundRepeat": "no-repeat",
73
+ "backgroundSize": "cover",
74
+ "color": "transparent",
75
+ "height": "auto",
76
+ "maxWidth": "750px",
77
+ "width": "100%",
78
+ }
79
+ }
80
+ title="These are awesome, we know that."
81
+ />
82
+ </picture>
83
+ </SRCImage>
84
+ `;
85
+
86
+ exports[`Image minimal data renders correctly 1`] = `
87
+ <SRCImage
88
+ data={
89
+ {
90
+ "base64": "data:image/jpeg;base64,<IMAGE-DATA>",
91
+ "height": 421,
92
+ "src": "https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750",
93
+ "width": 750,
94
+ }
95
+ }
96
+ >
97
+ <picture>
98
+ <source
99
+ srcSet="https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.25 187w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.5 375w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.75 562w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750 750w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=1.5 1125w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=2 1500w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=3 2250w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=4 3000w"
100
+ />
101
+ <img
102
+ alt=""
103
+ loading="lazy"
104
+ src="https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750"
105
+ style={
106
+ {
107
+ "aspectRatio": "750 / 421",
108
+ "backgroundImage": "url("data:image/jpeg;base64,<IMAGE-DATA>")",
109
+ "backgroundPosition": "50% 50%",
110
+ "backgroundRepeat": "no-repeat",
111
+ "backgroundSize": "cover",
112
+ "color": "transparent",
113
+ "height": "auto",
114
+ "maxWidth": "750px",
115
+ "width": "100%",
116
+ }
117
+ }
118
+ />
119
+ </picture>
120
+ </SRCImage>
121
+ `;
122
+
123
+ exports[`Image minimalDataWithRelativeUrl renders correctly 1`] = `
124
+ <SRCImage
125
+ data={
126
+ {
127
+ "base64": "data:image/jpeg;base64,<IMAGE-DATA>",
128
+ "height": 421,
129
+ "src": "/205/image.png?ar=16%3A9&fit=crop&w=750",
130
+ "width": 750,
131
+ }
132
+ }
133
+ >
134
+ <picture>
135
+ <source
136
+ srcSet="/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.25 187w,/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.5 375w,/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.75 562w,/205/image.png?ar=16%3A9&fit=crop&w=750 750w,/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=1.5 1125w,/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=2 1500w,/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=3 2250w,/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=4 3000w"
137
+ />
138
+ <img
139
+ alt=""
140
+ loading="lazy"
141
+ src="/205/image.png?ar=16%3A9&fit=crop&w=750"
142
+ style={
143
+ {
144
+ "aspectRatio": "750 / 421",
145
+ "backgroundImage": "url("data:image/jpeg;base64,<IMAGE-DATA>")",
146
+ "backgroundPosition": "50% 50%",
147
+ "backgroundRepeat": "no-repeat",
148
+ "backgroundSize": "cover",
149
+ "color": "transparent",
150
+ "height": "auto",
151
+ "maxWidth": "750px",
152
+ "width": "100%",
153
+ }
154
+ }
155
+ />
156
+ </picture>
157
+ </SRCImage>
158
+ `;
159
+
160
+ exports[`Image passing className and/or style renders correctly 1`] = `
161
+ <SRCImage
162
+ className="class-name"
163
+ data={
164
+ {
165
+ "base64": "data:image/jpeg;base64,<IMAGE-DATA>",
166
+ "height": 421,
167
+ "src": "https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750",
168
+ "width": 750,
169
+ }
170
+ }
171
+ style={
172
+ {
173
+ "border": "1px solid red",
174
+ }
175
+ }
176
+ >
177
+ <picture>
178
+ <source
179
+ srcSet="https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.25 187w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.5 375w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.75 562w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750 750w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=1.5 1125w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=2 1500w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=3 2250w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=4 3000w"
180
+ />
181
+ <img
182
+ alt=""
183
+ className="class-name"
184
+ loading="lazy"
185
+ src="https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750"
186
+ style={
187
+ {
188
+ "aspectRatio": "750 / 421",
189
+ "backgroundImage": "url("data:image/jpeg;base64,<IMAGE-DATA>")",
190
+ "backgroundPosition": "50% 50%",
191
+ "backgroundRepeat": "no-repeat",
192
+ "backgroundSize": "cover",
193
+ "border": "1px solid red",
194
+ "color": "transparent",
195
+ "height": "auto",
196
+ "maxWidth": "750px",
197
+ "width": "100%",
198
+ }
199
+ }
200
+ />
201
+ </picture>
202
+ </SRCImage>
203
+ `;
204
+
205
+ exports[`Image priority=true renders correctly 1`] = `
206
+ <SRCImage
207
+ data={
208
+ {
209
+ "base64": "data:image/jpeg;base64,<IMAGE-DATA>",
210
+ "height": 421,
211
+ "src": "https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750",
212
+ "width": 750,
213
+ }
214
+ }
215
+ priority={true}
216
+ >
217
+ <picture>
218
+ <source
219
+ srcSet="https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.25 187w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.5 375w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.75 562w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750 750w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=1.5 1125w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=2 1500w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=3 2250w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=4 3000w"
220
+ />
221
+ <img
222
+ alt=""
223
+ fetchpriority="high"
224
+ src="https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750"
225
+ style={
226
+ {
227
+ "aspectRatio": "750 / 421",
228
+ "backgroundImage": "url("data:image/jpeg;base64,<IMAGE-DATA>")",
229
+ "backgroundPosition": "50% 50%",
230
+ "backgroundRepeat": "no-repeat",
231
+ "backgroundSize": "cover",
232
+ "color": "transparent",
233
+ "height": "auto",
234
+ "maxWidth": "750px",
235
+ "width": "100%",
236
+ }
237
+ }
238
+ />
239
+ </picture>
240
+ </SRCImage>
241
+ `;
242
+
243
+ exports[`Image usePlaceholder=false renders correctly 1`] = `
244
+ <SRCImage
245
+ data={
246
+ {
247
+ "base64": "data:image/jpeg;base64,<IMAGE-DATA>",
248
+ "height": 421,
249
+ "src": "https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750",
250
+ "width": 750,
251
+ }
252
+ }
253
+ usePlaceholder={false}
254
+ >
255
+ <picture>
256
+ <source
257
+ srcSet="https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.25 187w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.5 375w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=0.75 562w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750 750w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=1.5 1125w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=2 1500w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=3 2250w,https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750&dpr=4 3000w"
258
+ />
259
+ <img
260
+ alt=""
261
+ loading="lazy"
262
+ src="https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750"
263
+ style={
264
+ {
265
+ "aspectRatio": "750 / 421",
266
+ "height": "auto",
267
+ "maxWidth": "750px",
268
+ "width": "100%",
269
+ }
270
+ }
271
+ />
272
+ </picture>
273
+ </SRCImage>
274
+ `;
@@ -0,0 +1,91 @@
1
+ import { mount } from 'enzyme';
2
+ import * as React from 'react';
3
+ import { SRCImage } from '../index.js';
4
+
5
+ const data = {
6
+ alt: 'DatoCMS swag',
7
+ aspectRatio: 1.7777777777777777,
8
+ base64: 'data:image/jpeg;base64,<IMAGE-DATA>',
9
+ height: 421,
10
+ sizes: '(max-width: 750px) 100vw, 750px',
11
+ src: 'https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750',
12
+ srcSet:
13
+ 'https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=0.25&fit=crop&w=750 187w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=0.5&fit=crop&w=750 375w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=0.75&fit=crop&w=750 562w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=1&fit=crop&w=750 750w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=1.5&fit=crop&w=750 1125w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=2&fit=crop&w=750 1500w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=3&fit=crop&w=750 2250w,↵https://www.datocms-assets.com/205/image.png?ar=16%3A9&dpr=4&fit=crop&w=750 3000w',
14
+ title: 'These are awesome, we know that.',
15
+ width: 750,
16
+ };
17
+
18
+ const minimalData = {
19
+ base64: 'data:image/jpeg;base64,<IMAGE-DATA>',
20
+ height: 421,
21
+ src: 'https://www.datocms-assets.com/205/image.png?ar=16%3A9&fit=crop&w=750',
22
+ width: 750,
23
+ };
24
+
25
+ const minimalDataWithRelativeUrl = {
26
+ base64: 'data:image/jpeg;base64,<IMAGE-DATA>',
27
+ height: 421,
28
+ src: '/205/image.png?ar=16%3A9&fit=crop&w=750',
29
+ width: 750,
30
+ };
31
+
32
+ describe('Image', () => {
33
+ describe('full data', () => {
34
+ it('renders correctly', () => {
35
+ const wrapper = mount(<SRCImage data={data} />);
36
+ expect(wrapper).toMatchSnapshot();
37
+ });
38
+ });
39
+
40
+ describe('minimal data', () => {
41
+ it('renders correctly', () => {
42
+ const wrapper = mount(<SRCImage data={minimalData} />);
43
+ expect(wrapper).toMatchSnapshot();
44
+ });
45
+ });
46
+
47
+ describe('minimalDataWithRelativeUrl', () => {
48
+ it('renders correctly', () => {
49
+ const wrapper = mount(<SRCImage data={minimalDataWithRelativeUrl} />);
50
+ expect(wrapper).toMatchSnapshot();
51
+ });
52
+ });
53
+
54
+ describe('passing className and/or style', () => {
55
+ it('renders correctly', () => {
56
+ const wrapper = mount(
57
+ <SRCImage
58
+ data={minimalData}
59
+ className="class-name"
60
+ style={{ border: '1px solid red' }}
61
+ />,
62
+ );
63
+ expect(wrapper).toMatchSnapshot();
64
+ });
65
+ });
66
+
67
+ describe('priority=true', () => {
68
+ it('renders correctly', () => {
69
+ const wrapper = mount(<SRCImage data={minimalData} priority={true} />);
70
+ expect(wrapper).toMatchSnapshot();
71
+ });
72
+ });
73
+
74
+ describe('usePlaceholder=false', () => {
75
+ it('renders correctly', () => {
76
+ const wrapper = mount(
77
+ <SRCImage data={minimalData} usePlaceholder={false} />,
78
+ );
79
+ expect(wrapper).toMatchSnapshot();
80
+ });
81
+ });
82
+
83
+ describe('explicit sizes', () => {
84
+ it('renders correctly', () => {
85
+ const wrapper = mount(
86
+ <SRCImage data={minimalData} sizes="(max-width: 600px) 200px, 50vw" />,
87
+ );
88
+ expect(wrapper).toMatchSnapshot();
89
+ });
90
+ });
91
+ });
@@ -0,0 +1,99 @@
1
+ // biome-ignore lint/style/useImportType: wrong warning
2
+ import React from 'react';
3
+ import type { ResponsiveImageType } from '../Image';
4
+ import { buildRegularSource, buildWebpSource, priorityProp } from './utils.js';
5
+
6
+ export type SRCImagePropTypes = {
7
+ /** The actual response you get from a DatoCMS `responsiveImage` GraphQL query */
8
+ data: ResponsiveImageType;
9
+ /** Additional CSS className for root node */
10
+ className?: string;
11
+ /** Additional CSS rules to add to the root node */
12
+ style?: React.CSSProperties;
13
+ /**
14
+ * When true, the image will be considered high priority. Lazy loading is automatically disabled, and fetchpriority="high" is added to the image.
15
+ * You should use the priority property on any image detected as the Largest Contentful Paint (LCP) element. It may be appropriate to have multiple priority images, as different images may be the LCP element for different viewport sizes.
16
+ * Should only be used when the image is visible above the fold.
17
+ **/
18
+ priority?: boolean;
19
+ /** Whether the component should use a blurred image placeholder */
20
+ usePlaceholder?: boolean;
21
+ /**
22
+ * The HTML5 `sizes` attribute for the image
23
+ *
24
+ * Learn more about srcset and sizes:
25
+ * -> https://web.dev/learn/design/responsive-images/#sizes
26
+ * -> https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-sizes
27
+ **/
28
+ sizes?: HTMLImageElement['sizes'];
29
+ /**
30
+ * If `data` does not contain `srcSet`, the candidates for the `srcset` of the image will be auto-generated based on these width multipliers
31
+ *
32
+ * Default candidate multipliers are [0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4]
33
+ **/
34
+ srcSetCandidates?: number[];
35
+ };
36
+
37
+ export function SRCImage({
38
+ className,
39
+ style,
40
+ data,
41
+ usePlaceholder = true,
42
+ priority = false,
43
+ sizes,
44
+ srcSetCandidates = [0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4],
45
+ }: SRCImagePropTypes) {
46
+ const webpSource = buildWebpSource(data, sizes);
47
+ const regularSource = buildRegularSource(data, sizes, srcSetCandidates);
48
+
49
+ const placeholderStyle: React.CSSProperties | undefined =
50
+ usePlaceholder && data.base64
51
+ ? {
52
+ backgroundImage: `url("${data.base64}")`,
53
+ backgroundSize: 'cover',
54
+ backgroundRepeat: 'no-repeat',
55
+ backgroundPosition: '50% 50%',
56
+ color: 'transparent',
57
+ }
58
+ : usePlaceholder && data.bgColor
59
+ ? { backgroundColor: data.bgColor ?? undefined, color: 'transparent' }
60
+ : undefined;
61
+
62
+ const { width } = data;
63
+
64
+ const height =
65
+ data.height ?? Math.round(data.aspectRatio ? width / data.aspectRatio : 0);
66
+
67
+ const sizingStyle = {
68
+ aspectRatio: `${width} / ${height}`,
69
+ width: '100%',
70
+ maxWidth: `${width}px`,
71
+ height: 'auto',
72
+ };
73
+
74
+ const loadingBehaviourProps = priority
75
+ ? priorityProp(priority ? 'high' : undefined)
76
+ : { loading: 'lazy' };
77
+
78
+ return (
79
+ <picture>
80
+ {webpSource}
81
+ {regularSource}
82
+ {data.src && (
83
+ // biome-ignore lint/a11y/useAltText: We do with alt the best we can
84
+ <img
85
+ src={data.src}
86
+ alt={data.alt ?? ''}
87
+ title={data.title ?? undefined}
88
+ {...loadingBehaviourProps}
89
+ className={className}
90
+ style={{
91
+ ...placeholderStyle,
92
+ ...sizingStyle,
93
+ ...style,
94
+ }}
95
+ />
96
+ )}
97
+ </picture>
98
+ );
99
+ }
@@ -0,0 +1,95 @@
1
+ import React, { version } from 'react';
2
+ import type { ResponsiveImageType } from '../Image';
3
+
4
+ export function priorityProp(
5
+ fetchPriority?: string,
6
+ ): Record<string, string | undefined> {
7
+ const [majorStr, minorStr] = version.split('.');
8
+ const major = Number.parseInt(majorStr, 10);
9
+ const minor = Number.parseInt(minorStr, 10);
10
+ if (major > 18 || (major === 18 && minor >= 3)) {
11
+ // In React 18.3.0 or newer, we must use camelCase
12
+ // prop to avoid "Warning: Invalid DOM property".
13
+ // See https://github.com/facebook/react/pull/25927
14
+ return { fetchPriority };
15
+ }
16
+ // In React 18.2.0 or older, we must use lowercase prop
17
+ // to avoid "Warning: Invalid DOM property".
18
+ return { fetchpriority: fetchPriority };
19
+ }
20
+
21
+ const bogusBaseUrl = 'https://example.com/';
22
+
23
+ export const buildSrcSetFromSrc = (
24
+ src: string | null | undefined,
25
+ width: number | undefined,
26
+ candidateMultipliers: number[],
27
+ ) => {
28
+ if (!(src && width)) {
29
+ return undefined;
30
+ }
31
+
32
+ return candidateMultipliers
33
+ .map((multiplier) => {
34
+ const url = new URL(src, bogusBaseUrl);
35
+
36
+ if (multiplier !== 1) {
37
+ url.searchParams.set('dpr', `${multiplier}`);
38
+ const maxH = url.searchParams.get('max-h');
39
+ const maxW = url.searchParams.get('max-w');
40
+ if (maxH) {
41
+ url.searchParams.set(
42
+ 'max-h',
43
+ `${Math.floor(Number.parseInt(maxH) * multiplier)}`,
44
+ );
45
+ }
46
+ if (maxW) {
47
+ url.searchParams.set(
48
+ 'max-w',
49
+ `${Math.floor(Number.parseInt(maxW) * multiplier)}`,
50
+ );
51
+ }
52
+ }
53
+
54
+ const finalWidth = Math.floor(width * multiplier);
55
+
56
+ if (finalWidth < 50) {
57
+ return null;
58
+ }
59
+
60
+ return `${url.toString().replace(bogusBaseUrl, '/')} ${finalWidth}w`;
61
+ })
62
+ .filter(Boolean)
63
+ .join(',');
64
+ };
65
+
66
+ export function buildWebpSource(
67
+ data: ResponsiveImageType,
68
+ sizes: HTMLImageElement['sizes'] | undefined,
69
+ ) {
70
+ return (
71
+ data.webpSrcSet && (
72
+ <source
73
+ srcSet={data.webpSrcSet}
74
+ sizes={sizes ?? data.sizes ?? undefined}
75
+ type="image/webp"
76
+ />
77
+ )
78
+ );
79
+ }
80
+
81
+ export function buildRegularSource(
82
+ data: ResponsiveImageType,
83
+ sizes: HTMLImageElement['sizes'] | undefined,
84
+ srcSetCandidates: number[],
85
+ ) {
86
+ return (
87
+ <source
88
+ srcSet={
89
+ data.srcSet ??
90
+ buildSrcSetFromSrc(data.src, data.width, srcSetCandidates)
91
+ }
92
+ sizes={sizes ?? data.sizes ?? undefined}
93
+ />
94
+ );
95
+ }
@@ -7,7 +7,7 @@ import {
7
7
  toRemixMeta,
8
8
  toRemixV1Meta,
9
9
  } from '../index.js';
10
- import { TitleMetaLinkTag } from '../types.js';
10
+ import type { TitleMetaLinkTag } from '../types.js';
11
11
 
12
12
  const metaTags: TitleMetaLinkTag[] = [
13
13
  {
@@ -76,7 +76,7 @@ export function toNextMetadata(
76
76
  const { tag, attributes, content } = datum;
77
77
 
78
78
  if (tag === 'title') {
79
- metadata['title'] = content;
79
+ metadata.title = content;
80
80
  }
81
81
 
82
82
  if (isSeoOrFaviconTag(datum) && isSeoMetaTag(datum)) {
@@ -88,15 +88,15 @@ export function toNextMetadata(
88
88
 
89
89
  if (parts?.length === 1) {
90
90
  if (parts[0] === 'image') {
91
- metadata['openGraph'] ||= {};
91
+ metadata.openGraph ||= {};
92
92
 
93
- metadata['openGraph']['images'] = {
94
- ...metadata['openGraph']['images'],
93
+ metadata.openGraph.images = {
94
+ ...metadata.openGraph.images,
95
95
  url: content,
96
96
  };
97
97
  } else {
98
- metadata['openGraph'] = {
99
- ...metadata['openGraph'],
98
+ metadata.openGraph = {
99
+ ...metadata.openGraph,
100
100
  [camelize(parts[0])]: content,
101
101
  };
102
102
  }
@@ -104,17 +104,17 @@ export function toNextMetadata(
104
104
 
105
105
  if (parts?.length === 2) {
106
106
  if (parts[0] === 'image' && parts[1] === 'width') {
107
- metadata['openGraph'] ||= {};
107
+ metadata.openGraph ||= {};
108
108
 
109
- metadata['openGraph']['images'] = {
110
- ...metadata['openGraph']['images'],
109
+ metadata.openGraph.images = {
110
+ ...metadata.openGraph.images,
111
111
  width: content,
112
112
  };
113
113
  } else if (parts[0] === 'image' && parts[1] === 'height') {
114
- metadata['openGraph'] ||= {};
114
+ metadata.openGraph ||= {};
115
115
 
116
- metadata['openGraph']['images'] = {
117
- ...metadata['openGraph']['images'],
116
+ metadata.openGraph.images = {
117
+ ...metadata.openGraph.images,
118
118
  height: content,
119
119
  };
120
120
  }
@@ -131,8 +131,8 @@ export function toNextMetadata(
131
131
  const [_, ...parts] = name.split(':');
132
132
 
133
133
  if (parts?.length === 1) {
134
- metadata['twitter'] = {
135
- ...metadata['twitter'],
134
+ metadata.twitter = {
135
+ ...metadata.twitter,
136
136
  [camelize(parts[0])]: content,
137
137
  };
138
138
  }
@@ -146,10 +146,10 @@ export function toNextMetadata(
146
146
  if (isAppleTouchIconAttributes(datum.attributes)) {
147
147
  const { sizes, href } = datum.attributes;
148
148
 
149
- metadata['icons'] ||= {};
149
+ metadata.icons ||= {};
150
150
 
151
- metadata['icons']['apple'] = [
152
- ...(metadata['icons']['apple'] || []),
151
+ metadata.icons.apple = [
152
+ ...(metadata.icons.apple || []),
153
153
  { url: href, sizes },
154
154
  ];
155
155
  }
@@ -157,10 +157,10 @@ export function toNextMetadata(
157
157
  if (isFaviconAttributes(datum.attributes)) {
158
158
  const { sizes, type, rel, href } = datum.attributes;
159
159
 
160
- metadata['icons'] ||= {};
160
+ metadata.icons ||= {};
161
161
 
162
- metadata['icons']['icon'] = [
163
- ...(metadata['icons']['icon'] || []),
162
+ metadata.icons.icon = [
163
+ ...(metadata.icons.icon || []),
164
164
  { url: href, sizes, type, rel },
165
165
  ];
166
166
  }
@@ -1,4 +1,4 @@
1
- import { SeoOrFaviconTag, TitleMetaLinkTag } from './types.js';
1
+ import type { SeoOrFaviconTag, TitleMetaLinkTag } from './types.js';
2
2
 
3
3
  interface RemixV1HtmlMetaDescriptor {
4
4
  [name: string]: string | string[];