@financial-times/cp-content-pipeline-ui 6.8.0 → 6.9.0
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.
- package/CHANGELOG.md +27 -0
- package/README.md +30 -8
- package/jest-global-setup.js +1 -0
- package/jest.config.js +17 -16
- package/lib/components/Body/index.d.ts +3 -14
- package/lib/components/Body/index.js +22 -15
- package/lib/components/Body/index.js.map +1 -1
- package/lib/components/Body/index.test.d.ts +1 -0
- package/lib/components/Body/index.test.js +121 -0
- package/lib/components/Body/index.test.js.map +1 -0
- package/lib/components/Byline/index.js +1 -1
- package/lib/components/Byline/index.js.map +1 -1
- package/lib/components/ContentPackageBody/index.d.ts +5 -1
- package/lib/components/ContentPackageBody/index.js.map +1 -1
- package/lib/components/LiveBlogWrapper/index.js +3 -2
- package/lib/components/LiveBlogWrapper/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/Body/__snapshots__/index.test.tsx.snap +352 -0
- package/src/components/Body/index.test.tsx +159 -0
- package/src/components/Body/index.tsx +46 -46
- package/src/components/Byline/index.tsx +8 -4
- package/src/components/ContentPackageBody/index.tsx +5 -1
- package/src/components/LiveBlogWrapper/index.tsx +6 -5
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`Body content type override renderers renders override renderer for Article 1`] = `
|
|
4
|
+
<DocumentFragment>
|
|
5
|
+
body override Article
|
|
6
|
+
</DocumentFragment>
|
|
7
|
+
`;
|
|
8
|
+
|
|
9
|
+
exports[`Body content type override renderers renders override renderer for Audio 1`] = `
|
|
10
|
+
<DocumentFragment>
|
|
11
|
+
body override Audio
|
|
12
|
+
</DocumentFragment>
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
exports[`Body content type override renderers renders override renderer for ContentPackage 1`] = `
|
|
16
|
+
<DocumentFragment>
|
|
17
|
+
body override ContentPackage
|
|
18
|
+
</DocumentFragment>
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
exports[`Body content type override renderers renders override renderer for LiveBlogPackage 1`] = `
|
|
22
|
+
<DocumentFragment>
|
|
23
|
+
body override LiveBlogPackage
|
|
24
|
+
</DocumentFragment>
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
exports[`Body content type override renderers renders override renderer for Video 1`] = `
|
|
28
|
+
<DocumentFragment>
|
|
29
|
+
body override Video
|
|
30
|
+
</DocumentFragment>
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
exports[`Body default renderers renders default renderer for Article 1`] = `
|
|
34
|
+
<DocumentFragment>
|
|
35
|
+
<article
|
|
36
|
+
class="n-content-body"
|
|
37
|
+
>
|
|
38
|
+
<p>
|
|
39
|
+
Lorem ipsum dolor sit amet
|
|
40
|
+
</p>
|
|
41
|
+
</article>
|
|
42
|
+
</DocumentFragment>
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
exports[`Body default renderers renders default renderer for Audio 1`] = `
|
|
46
|
+
<DocumentFragment>
|
|
47
|
+
<div
|
|
48
|
+
class="article__content-body n-content-body js-article__content-body"
|
|
49
|
+
data-attribute="article-content-body"
|
|
50
|
+
data-trackable="article-body"
|
|
51
|
+
>
|
|
52
|
+
<div
|
|
53
|
+
class="article__media"
|
|
54
|
+
>
|
|
55
|
+
<audio
|
|
56
|
+
controls=""
|
|
57
|
+
data-audio-subtype="podcast"
|
|
58
|
+
data-content-id="00000000-0000-0000-0000-000000000000"
|
|
59
|
+
data-dispatch-listened-event-on-unload="true"
|
|
60
|
+
data-o-component="o-audio"
|
|
61
|
+
>
|
|
62
|
+
<source
|
|
63
|
+
src="https://open.spotify.com/album/34a7B1mPM7RqlwsjPPmzEg?si=aHxMBJ1zRES6Z0iIrTrwBQ"
|
|
64
|
+
type="audio/mpeg"
|
|
65
|
+
/>
|
|
66
|
+
<p>
|
|
67
|
+
Your browser does not support playing this file but you can still
|
|
68
|
+
<a
|
|
69
|
+
href="https://open.spotify.com/album/34a7B1mPM7RqlwsjPPmzEg?si=aHxMBJ1zRES6Z0iIrTrwBQ"
|
|
70
|
+
>
|
|
71
|
+
download the MP3 file
|
|
72
|
+
</a>
|
|
73
|
+
to play locally.
|
|
74
|
+
</p>
|
|
75
|
+
</audio>
|
|
76
|
+
</div>
|
|
77
|
+
<article
|
|
78
|
+
class="n-content-body"
|
|
79
|
+
>
|
|
80
|
+
<p>
|
|
81
|
+
Lorem ipsum dolor sit amet
|
|
82
|
+
</p>
|
|
83
|
+
<p>
|
|
84
|
+
<a
|
|
85
|
+
href="/accessibility#podcast-transcriptions"
|
|
86
|
+
>
|
|
87
|
+
View our accessibility guide
|
|
88
|
+
</a>
|
|
89
|
+
.
|
|
90
|
+
</p>
|
|
91
|
+
</article>
|
|
92
|
+
</div>
|
|
93
|
+
</DocumentFragment>
|
|
94
|
+
`;
|
|
95
|
+
|
|
96
|
+
exports[`Body default renderers renders default renderer for ContentPackage 1`] = `
|
|
97
|
+
<DocumentFragment>
|
|
98
|
+
<article
|
|
99
|
+
class="article n-content-body js-article__content-body n-content-body"
|
|
100
|
+
data-attribute="article-content-body"
|
|
101
|
+
data-content-id="00000000-0000-0000-0000-000000000000"
|
|
102
|
+
id="site-content"
|
|
103
|
+
role="main"
|
|
104
|
+
>
|
|
105
|
+
<div
|
|
106
|
+
class="package-container package-grid-1-column"
|
|
107
|
+
>
|
|
108
|
+
<div
|
|
109
|
+
class="package-contents package-teasers--large"
|
|
110
|
+
>
|
|
111
|
+
<ul
|
|
112
|
+
class="package-contents package-teasers__list"
|
|
113
|
+
>
|
|
114
|
+
<li
|
|
115
|
+
class="package-contents__item o-teaser-collection__item o-teaser-collection__item--stretched"
|
|
116
|
+
>
|
|
117
|
+
<div
|
|
118
|
+
class="o-teaser o-teaser--hero o-teaser--centre o-teaser--stretched js-teaser"
|
|
119
|
+
>
|
|
120
|
+
<div
|
|
121
|
+
class="o-teaser__content"
|
|
122
|
+
>
|
|
123
|
+
<div
|
|
124
|
+
class="o-teaser__heading"
|
|
125
|
+
>
|
|
126
|
+
<a
|
|
127
|
+
class="js-teaser-heading-link"
|
|
128
|
+
data-trackable="heading-link"
|
|
129
|
+
href="/content/00000000-0000-0000-0000-000000000000"
|
|
130
|
+
>
|
|
131
|
+
teaser
|
|
132
|
+
</a>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</li>
|
|
137
|
+
</ul>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</article>
|
|
141
|
+
</DocumentFragment>
|
|
142
|
+
`;
|
|
143
|
+
|
|
144
|
+
exports[`Body default renderers renders default renderer for LiveBlogPackage 1`] = `
|
|
145
|
+
<DocumentFragment>
|
|
146
|
+
<div
|
|
147
|
+
class="x-live-blog-wrapper"
|
|
148
|
+
data-live-blog-wrapper-id="live-blog-wrapper"
|
|
149
|
+
>
|
|
150
|
+
<article
|
|
151
|
+
class="x-live-blog-post"
|
|
152
|
+
data-trackable="live-post"
|
|
153
|
+
data-x-component="live-blog-post"
|
|
154
|
+
id="post-00000000-0000-0000-0000-000000000000"
|
|
155
|
+
>
|
|
156
|
+
<div
|
|
157
|
+
class="x-live-blog-post__standout"
|
|
158
|
+
>
|
|
159
|
+
<span
|
|
160
|
+
class=""
|
|
161
|
+
/>
|
|
162
|
+
<div
|
|
163
|
+
class="x-live-blog-post__meta-container"
|
|
164
|
+
>
|
|
165
|
+
<div
|
|
166
|
+
class="x-live-blog-post__meta"
|
|
167
|
+
>
|
|
168
|
+
<span>
|
|
169
|
+
<span
|
|
170
|
+
class="x-live-blog-post__timestamp-container"
|
|
171
|
+
>
|
|
172
|
+
<time
|
|
173
|
+
class="o-date x-live-blog-post__timestamp"
|
|
174
|
+
data-o-component="o-date"
|
|
175
|
+
data-o-date-format="time-ago-no-seconds"
|
|
176
|
+
data-o-date-text-case="sentence"
|
|
177
|
+
datetime="2024-06-11T14:13:25.527Z"
|
|
178
|
+
itemprop="datePublished"
|
|
179
|
+
>
|
|
180
|
+
<span
|
|
181
|
+
data-o-date-printer="true"
|
|
182
|
+
>
|
|
183
|
+
6/11/2024, 2:13:25 PM
|
|
184
|
+
</span>
|
|
185
|
+
</time>
|
|
186
|
+
</span>
|
|
187
|
+
</span>
|
|
188
|
+
</div>
|
|
189
|
+
<div
|
|
190
|
+
class="x-live-blog-post__byline"
|
|
191
|
+
>
|
|
192
|
+
<p
|
|
193
|
+
class="article-info__byline"
|
|
194
|
+
/>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
<h2
|
|
199
|
+
class="x-live-blog-post__title"
|
|
200
|
+
>
|
|
201
|
+
live post 1
|
|
202
|
+
</h2>
|
|
203
|
+
<div
|
|
204
|
+
class="x-live-blog-post__body n-content-body article--body"
|
|
205
|
+
>
|
|
206
|
+
<p>
|
|
207
|
+
Lorem ipsum dolor sit amet
|
|
208
|
+
</p>
|
|
209
|
+
</div>
|
|
210
|
+
<div
|
|
211
|
+
class="x-live-blog-post__controls"
|
|
212
|
+
>
|
|
213
|
+
<div>
|
|
214
|
+
<div
|
|
215
|
+
class="o-share o-share--small"
|
|
216
|
+
data-o-component="o-share"
|
|
217
|
+
data-o-share-location="live-blog-post-00000000-0000-0000-0000-000000000000"
|
|
218
|
+
>
|
|
219
|
+
<ul
|
|
220
|
+
data-toolbar="share"
|
|
221
|
+
>
|
|
222
|
+
<li
|
|
223
|
+
class="o-share__action"
|
|
224
|
+
data-share="twitter"
|
|
225
|
+
>
|
|
226
|
+
<a
|
|
227
|
+
class="o-share__icon o-share__icon--x"
|
|
228
|
+
data-trackable="twitter"
|
|
229
|
+
href="https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.ft.com%2Fcontent%2F00000000-0000-0000-0000-000000000000%23post-00000000-0000-0000-0000-000000000000&text=live%20post%201&via=ft"
|
|
230
|
+
rel="noopener"
|
|
231
|
+
>
|
|
232
|
+
<div
|
|
233
|
+
class="o-share__icon__image"
|
|
234
|
+
>
|
|
235
|
+
<svg
|
|
236
|
+
viewBox="0 0 40 40"
|
|
237
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
238
|
+
>
|
|
239
|
+
<path
|
|
240
|
+
d="M21.647 18.469 28.932 10h-1.726l-6.326 7.353L15.827 10H10l7.64 11.12L10 30h1.726l6.68-7.765L23.744 30h5.827l-7.924-11.531Zm-2.365 2.748-.774-1.107-6.16-8.81H15l4.971 7.11.774 1.107 6.462 9.242h-2.652l-5.273-7.541Z"
|
|
241
|
+
/>
|
|
242
|
+
</svg>
|
|
243
|
+
</div>
|
|
244
|
+
<span
|
|
245
|
+
class="o-share__text"
|
|
246
|
+
>
|
|
247
|
+
Share $live post 1 on X (opens in a new window)
|
|
248
|
+
</span>
|
|
249
|
+
</a>
|
|
250
|
+
</li>
|
|
251
|
+
<li
|
|
252
|
+
class="o-share__action"
|
|
253
|
+
data-share="facebook"
|
|
254
|
+
>
|
|
255
|
+
<a
|
|
256
|
+
class="o-share__icon o-share__icon--facebook"
|
|
257
|
+
data-trackable="facebook"
|
|
258
|
+
href="http://www.facebook.com/sharer.php?u=https%3A%2F%2Fwww.ft.com%2Fcontent%2F00000000-0000-0000-0000-000000000000%23post-00000000-0000-0000-0000-000000000000&t=live%20post%201"
|
|
259
|
+
rel="noopener"
|
|
260
|
+
>
|
|
261
|
+
<div
|
|
262
|
+
class="o-share__icon__image"
|
|
263
|
+
>
|
|
264
|
+
<svg
|
|
265
|
+
viewBox="0 0 1024 1024"
|
|
266
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
267
|
+
>
|
|
268
|
+
<path
|
|
269
|
+
d="M643.9 342h-48.2c-37.8 0-45.1 18-45.1 44.3v58.1h90.1l-11.7 91h-78.4V769h-94V535.5H378v-91h78.6v-67.1c0-77.9 47.6-120.3 117.1-120.3 33.3 0 61.9 2.5 70.2 3.6V342z"
|
|
270
|
+
/>
|
|
271
|
+
</svg>
|
|
272
|
+
</div>
|
|
273
|
+
<span
|
|
274
|
+
class="o-share__text"
|
|
275
|
+
>
|
|
276
|
+
Share $live post 1 on Facebook (opens in a new window)
|
|
277
|
+
</span>
|
|
278
|
+
</a>
|
|
279
|
+
</li>
|
|
280
|
+
<li
|
|
281
|
+
class="o-share__action"
|
|
282
|
+
data-share="linkedin"
|
|
283
|
+
>
|
|
284
|
+
<a
|
|
285
|
+
class="o-share__icon o-share__icon--linkedin"
|
|
286
|
+
data-trackable="linkedin"
|
|
287
|
+
href="http://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fwww.ft.com%2Fcontent%2F00000000-0000-0000-0000-000000000000%23post-00000000-0000-0000-0000-000000000000&title=live%20post%201&source=Financial+Times"
|
|
288
|
+
rel="noopener"
|
|
289
|
+
>
|
|
290
|
+
<div
|
|
291
|
+
class="o-share__icon__image"
|
|
292
|
+
>
|
|
293
|
+
<svg
|
|
294
|
+
viewBox="0 0 1024 1024"
|
|
295
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
296
|
+
>
|
|
297
|
+
<path
|
|
298
|
+
d="M264.4 426.2h106.2v341.4H264.4V426.2zm53.2-169.7c-34.1 0-61.6 27.6-61.6 61.5 0 34 27.5 61.5 61.6 61.5 33.9 0 61.5-27.6 61.5-61.5-.1-34-27.6-61.5-61.5-61.5zm323.1 161.2c-51.6 0-86.2 28.3-100.4 55.1h-1.5v-46.7H437.2v341.4h106V598.7c0-44.5 8.4-87.7 63.6-87.7 54.5 0 55.1 50.9 55.1 90.5v166H768V580.3c0-91.9-19.9-162.6-127.3-162.6z"
|
|
299
|
+
/>
|
|
300
|
+
</svg>
|
|
301
|
+
</div>
|
|
302
|
+
<span
|
|
303
|
+
class="o-share__text"
|
|
304
|
+
>
|
|
305
|
+
Share $live post 1 on LinkedIn (opens in a new window)
|
|
306
|
+
</span>
|
|
307
|
+
</a>
|
|
308
|
+
</li>
|
|
309
|
+
</ul>
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
</article>
|
|
314
|
+
</div>
|
|
315
|
+
</DocumentFragment>
|
|
316
|
+
`;
|
|
317
|
+
|
|
318
|
+
exports[`Body default renderers renders default renderer for Video 1`] = `
|
|
319
|
+
<DocumentFragment>
|
|
320
|
+
<article
|
|
321
|
+
class="n-content-body"
|
|
322
|
+
>
|
|
323
|
+
<p>
|
|
324
|
+
Lorem ipsum dolor sit amet
|
|
325
|
+
</p>
|
|
326
|
+
</article>
|
|
327
|
+
</DocumentFragment>
|
|
328
|
+
`;
|
|
329
|
+
|
|
330
|
+
exports[`Body legacy override renderers renders override renderer for Article 1`] = `
|
|
331
|
+
<DocumentFragment>
|
|
332
|
+
body override Article
|
|
333
|
+
</DocumentFragment>
|
|
334
|
+
`;
|
|
335
|
+
|
|
336
|
+
exports[`Body legacy override renderers renders override renderer for Audio 1`] = `
|
|
337
|
+
<DocumentFragment>
|
|
338
|
+
body override Audio
|
|
339
|
+
</DocumentFragment>
|
|
340
|
+
`;
|
|
341
|
+
|
|
342
|
+
exports[`Body legacy override renderers renders override renderer for ContentPackage 1`] = `
|
|
343
|
+
<DocumentFragment>
|
|
344
|
+
body override ContentPackage
|
|
345
|
+
</DocumentFragment>
|
|
346
|
+
`;
|
|
347
|
+
|
|
348
|
+
exports[`Body legacy override renderers renders override renderer for LiveBlogPackage 1`] = `
|
|
349
|
+
<DocumentFragment>
|
|
350
|
+
body override LiveBlogPackage
|
|
351
|
+
</DocumentFragment>
|
|
352
|
+
`;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { render } from '@testing-library/react'
|
|
3
|
+
import Body, { BodyComponentMapRecord } from '.'
|
|
4
|
+
import { ArticleQuery } from '@financial-times/cp-content-pipeline-client'
|
|
5
|
+
|
|
6
|
+
const baseContent: ArticleQuery['content'] = {
|
|
7
|
+
__typename: 'Article',
|
|
8
|
+
title: 'title',
|
|
9
|
+
id: '00000000-0000-0000-0000-000000000000',
|
|
10
|
+
url: 'https://www.ft.com/content/00000000-0000-0000-0000-000000000000',
|
|
11
|
+
publishedDate: '2024-06-11T14:13:25.527Z',
|
|
12
|
+
publishedTimestamp: 1718115205527,
|
|
13
|
+
firstPublishedDate: '2024-06-11T14:13:25.527Z',
|
|
14
|
+
body: {
|
|
15
|
+
structured: {
|
|
16
|
+
tree: {
|
|
17
|
+
type: 'body',
|
|
18
|
+
version: 1,
|
|
19
|
+
children: [
|
|
20
|
+
{
|
|
21
|
+
type: 'paragraph',
|
|
22
|
+
children: [
|
|
23
|
+
{
|
|
24
|
+
type: 'text',
|
|
25
|
+
value: 'Lorem ipsum dolor sit amet',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
references: [],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const extraContentForType: Partial<
|
|
37
|
+
Record<
|
|
38
|
+
ArticleQuery['content']['__typename'],
|
|
39
|
+
Partial<ArticleQuery['content']>
|
|
40
|
+
>
|
|
41
|
+
> = {
|
|
42
|
+
Audio: {
|
|
43
|
+
__typename: 'Audio',
|
|
44
|
+
media: [
|
|
45
|
+
{
|
|
46
|
+
mediaType: 'audio/mpeg',
|
|
47
|
+
url: 'https://open.spotify.com/album/34a7B1mPM7RqlwsjPPmzEg?si=aHxMBJ1zRES6Z0iIrTrwBQ',
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
ContentPackage: {
|
|
52
|
+
__typename: 'ContentPackage',
|
|
53
|
+
contains: [
|
|
54
|
+
{
|
|
55
|
+
title: 'teaser',
|
|
56
|
+
url: 'https://www.ft.com/content/00000000-0000-0000-0000-000000000000',
|
|
57
|
+
relativeUrl: '/content/00000000-0000-0000-0000-000000000000',
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
LiveBlogPackage: {
|
|
62
|
+
__typename: 'LiveBlogPackage',
|
|
63
|
+
liveBlogPosts: [
|
|
64
|
+
{
|
|
65
|
+
...baseContent,
|
|
66
|
+
__typename: 'LiveBlogPost',
|
|
67
|
+
title: 'live post 1',
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
Video: {
|
|
72
|
+
__typename: 'Video',
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const extraPropsForType: Partial<
|
|
77
|
+
Record<ArticleQuery['content']['__typename'], Record<string, unknown>>
|
|
78
|
+
> = {
|
|
79
|
+
LiveBlogPackage: {
|
|
80
|
+
postTrackerConfig: {
|
|
81
|
+
usePostTracker: false,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// these are the only content types we currently care about rendering bodies for
|
|
87
|
+
const typesToTest: ArticleQuery['content']['__typename'][] = [
|
|
88
|
+
'Article',
|
|
89
|
+
'Video',
|
|
90
|
+
'Audio',
|
|
91
|
+
'LiveBlogPackage',
|
|
92
|
+
'ContentPackage',
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
const legacyOverridesToTest: [
|
|
96
|
+
ArticleQuery['content']['__typename'],
|
|
97
|
+
keyof BodyComponentMapRecord
|
|
98
|
+
][] = [
|
|
99
|
+
['Article', 'article-body'],
|
|
100
|
+
['LiveBlogPackage', 'live-blog-body'],
|
|
101
|
+
['ContentPackage', 'content-package-body'],
|
|
102
|
+
['Audio', 'podcast-body'],
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
describe('Body', () => {
|
|
106
|
+
describe('default renderers', () => {
|
|
107
|
+
test.each(typesToTest)('renders default renderer for %s', (type) => {
|
|
108
|
+
const { asFragment } = render(
|
|
109
|
+
<Body
|
|
110
|
+
content={{ ...baseContent, ...(extraContentForType[type] ?? {}) }}
|
|
111
|
+
{...(extraPropsForType[type] ?? {})}
|
|
112
|
+
/>
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
expect(asFragment()).toMatchSnapshot()
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
describe('legacy override renderers', () => {
|
|
120
|
+
test.each(legacyOverridesToTest)(
|
|
121
|
+
'renders override renderer for %s',
|
|
122
|
+
(type, overrideName) => {
|
|
123
|
+
const { asFragment } = render(
|
|
124
|
+
<Body
|
|
125
|
+
content={{ ...baseContent, ...(extraContentForType[type] ?? {}) }}
|
|
126
|
+
{...(extraPropsForType[type] ?? {})}
|
|
127
|
+
bodyComponents={{
|
|
128
|
+
[overrideName]: ({
|
|
129
|
+
content,
|
|
130
|
+
}: {
|
|
131
|
+
content: ArticleQuery['content']
|
|
132
|
+
}) => <>body override {content.__typename}</>,
|
|
133
|
+
}}
|
|
134
|
+
/>
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
expect(asFragment()).toMatchSnapshot()
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
describe('content type override renderers', () => {
|
|
143
|
+
test.each(typesToTest)('renders override renderer for %s', (type) => {
|
|
144
|
+
const { asFragment } = render(
|
|
145
|
+
<Body
|
|
146
|
+
content={{ ...baseContent, ...(extraContentForType[type] ?? {}) }}
|
|
147
|
+
{...(extraPropsForType[type] ?? {})}
|
|
148
|
+
bodyComponents={{
|
|
149
|
+
[type]: ({ content }: { content: ArticleQuery['content'] }) => (
|
|
150
|
+
<>body override {content.__typename}</>
|
|
151
|
+
),
|
|
152
|
+
}}
|
|
153
|
+
/>
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
expect(asFragment()).toMatchSnapshot()
|
|
157
|
+
})
|
|
158
|
+
})
|
|
159
|
+
})
|
|
@@ -5,74 +5,74 @@ import PodcastBody from '../PodcastBody'
|
|
|
5
5
|
import ContentPackageBody from '../ContentPackageBody'
|
|
6
6
|
import type { ArticleQuery } from '@financial-times/cp-content-pipeline-client'
|
|
7
7
|
import type { RichTextComponentMapRecord } from '../RichText'
|
|
8
|
-
import type { Serialiser } from '@financial-times/x-interaction'
|
|
9
8
|
|
|
10
|
-
type
|
|
11
|
-
usePostTracker: boolean
|
|
12
|
-
onEntersViewport: () => void
|
|
13
|
-
onRead: () => void
|
|
14
|
-
onError: () => void
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
type BodyComponentMapRecord = Partial<
|
|
9
|
+
export type BodyComponentMapRecord = Partial<
|
|
18
10
|
Record<
|
|
19
|
-
|
|
11
|
+
| ArticleQuery['content']['__typename']
|
|
12
|
+
// these are here for backwards compatibility:
|
|
13
|
+
| 'live-blog-body'
|
|
14
|
+
| 'article-body'
|
|
15
|
+
| 'podcast-body'
|
|
16
|
+
| 'content-package-body'
|
|
17
|
+
// this will be used for any content type without a default or override body
|
|
18
|
+
| 'fallback',
|
|
20
19
|
// using any here, no easy way to make a type that works for all the potential renderers
|
|
21
20
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
21
|
JSXElementConstructor<any>
|
|
23
22
|
>
|
|
24
23
|
>
|
|
25
24
|
|
|
25
|
+
const contentTypeLegacyOverrideMap: Partial<
|
|
26
|
+
Record<
|
|
27
|
+
ArticleQuery['content']['__typename'],
|
|
28
|
+
Exclude<
|
|
29
|
+
keyof BodyComponentMapRecord,
|
|
30
|
+
ArticleQuery['content']['__typename'] | 'fallback'
|
|
31
|
+
>
|
|
32
|
+
>
|
|
33
|
+
> = {
|
|
34
|
+
Audio: 'podcast-body',
|
|
35
|
+
LiveBlogPackage: 'live-blog-body',
|
|
36
|
+
ContentPackage: 'content-package-body',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const defaultBodyComponents = {
|
|
40
|
+
Audio: PodcastBody,
|
|
41
|
+
LiveBlogPackage: LiveBlogBody,
|
|
42
|
+
ContentPackage: ContentPackageBody,
|
|
43
|
+
fallback: ArticleBody,
|
|
44
|
+
} satisfies BodyComponentMapRecord
|
|
45
|
+
|
|
26
46
|
export type BodyProps = {
|
|
27
47
|
content: ArticleQuery['content']
|
|
28
48
|
richTextComponents?: RichTextComponentMapRecord
|
|
29
49
|
bodyComponents?: BodyComponentMapRecord
|
|
30
|
-
|
|
31
|
-
xInteractionSerialiser?: Serialiser
|
|
32
|
-
postTrackerConfig?: PostTrackerConfig
|
|
33
|
-
AdSlot?: React.FC
|
|
50
|
+
[key: string]: unknown
|
|
34
51
|
}
|
|
35
52
|
|
|
36
53
|
export default function Body({
|
|
37
54
|
content,
|
|
38
|
-
richTextComponents,
|
|
39
55
|
bodyComponents,
|
|
40
|
-
|
|
41
|
-
xInteractionSerialiser,
|
|
42
|
-
postTrackerConfig,
|
|
43
|
-
AdSlot,
|
|
56
|
+
...extraProps
|
|
44
57
|
}: BodyProps) {
|
|
45
58
|
if (!content?.body?.structured) {
|
|
46
59
|
return null
|
|
47
60
|
}
|
|
48
61
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const ContentPackage =
|
|
53
|
-
bodyComponents?.['content-package-body'] ?? ContentPackageBody
|
|
54
|
-
|
|
55
|
-
if (content.__typename === 'LiveBlogPackage') {
|
|
56
|
-
return (
|
|
57
|
-
<LiveBlog
|
|
58
|
-
content={content}
|
|
59
|
-
richTextComponents={richTextComponents}
|
|
60
|
-
showShareButtons={showShareButtons}
|
|
61
|
-
xInteractionSerialiser={xInteractionSerialiser}
|
|
62
|
-
postTrackerConfig={postTrackerConfig}
|
|
63
|
-
/>
|
|
64
|
-
)
|
|
65
|
-
} else if (content.__typename === 'Audio') {
|
|
66
|
-
return <Podcast content={content} richTextComponents={richTextComponents} />
|
|
67
|
-
} else if (content.__typename === 'ContentPackage') {
|
|
68
|
-
return (
|
|
69
|
-
<ContentPackage
|
|
70
|
-
content={content}
|
|
71
|
-
richTextComponents={richTextComponents}
|
|
72
|
-
AdSlot={AdSlot}
|
|
73
|
-
/>
|
|
74
|
-
)
|
|
62
|
+
const bodyComponentsWithOverrides = {
|
|
63
|
+
...defaultBodyComponents,
|
|
64
|
+
...bodyComponents,
|
|
75
65
|
}
|
|
76
66
|
|
|
77
|
-
|
|
67
|
+
const legacyOverrideKey =
|
|
68
|
+
contentTypeLegacyOverrideMap[content.__typename] ?? 'article-body'
|
|
69
|
+
const overrideComponent =
|
|
70
|
+
legacyOverrideKey in bodyComponentsWithOverrides
|
|
71
|
+
? bodyComponentsWithOverrides[legacyOverrideKey]
|
|
72
|
+
: bodyComponentsWithOverrides[content.__typename]
|
|
73
|
+
|
|
74
|
+
const BodyComponent =
|
|
75
|
+
overrideComponent ?? bodyComponentsWithOverrides.fallback ?? ArticleBody
|
|
76
|
+
|
|
77
|
+
return <BodyComponent content={content} {...extraProps} />
|
|
78
78
|
}
|
|
@@ -35,10 +35,14 @@ export default function Byline({
|
|
|
35
35
|
Edited by <br />
|
|
36
36
|
</span>
|
|
37
37
|
)}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
{typeof structuredContent === 'string' ? (
|
|
39
|
+
<>{structuredContent}</>
|
|
40
|
+
) : (
|
|
41
|
+
<RichText
|
|
42
|
+
structuredContent={structuredContent}
|
|
43
|
+
components={bylineComponents}
|
|
44
|
+
/>
|
|
45
|
+
)}
|
|
42
46
|
</p>
|
|
43
47
|
)
|
|
44
48
|
}
|
|
@@ -33,11 +33,15 @@ type SecondaryTeasersProps = {
|
|
|
33
33
|
AdSlot?: React.FC
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
type ContentPackageBodyProps = BodyProps & {
|
|
37
|
+
AdSlot?: React.FC
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
export default function ContentPackageBody({
|
|
37
41
|
content,
|
|
38
42
|
richTextComponents,
|
|
39
43
|
AdSlot,
|
|
40
|
-
}:
|
|
44
|
+
}: ContentPackageBodyProps) {
|
|
41
45
|
if (content.__typename !== 'ContentPackage') {
|
|
42
46
|
return null
|
|
43
47
|
}
|
|
@@ -40,12 +40,13 @@ type LiveBlogWrapperTypes = {
|
|
|
40
40
|
serialiser: any //TODO: keeps TS happy, but maybe not required?
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
type
|
|
43
|
+
type LiveBlogWrapperState = { postCount?: number; tracker?: any }
|
|
44
44
|
|
|
45
|
-
class BaseLiveBlogWrapper extends Component<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
class BaseLiveBlogWrapper extends Component<
|
|
46
|
+
LiveBlogWrapperTypes,
|
|
47
|
+
LiveBlogWrapperState
|
|
48
|
+
> {
|
|
49
|
+
state: LiveBlogWrapperState = {}
|
|
49
50
|
|
|
50
51
|
componentDidUpdate() {
|
|
51
52
|
if (this.props.posts && this.props.posts.length && !this.state.tracker) {
|