@financial-times/cp-content-pipeline-ui 6.15.6 → 7.0.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 +15 -0
- package/lib/components/BigNumber/index.d.ts +4 -1
- package/lib/components/BigNumber/index.js +1 -1
- package/lib/components/BigNumber/index.js.map +1 -1
- package/lib/components/Body/index.test.js +12 -1
- package/lib/components/Body/index.test.js.map +1 -1
- package/lib/components/Byline/index.js +2 -2
- package/lib/components/Byline/index.js.map +1 -1
- package/lib/components/Clip/components/ClipTag.d.ts +3 -3
- package/lib/components/Clip/components/ClipTag.js.map +1 -1
- package/lib/components/Clip/components/ClosedCaptions.d.ts +2 -2
- package/lib/components/Clip/components/VideoDescription.d.ts +2 -2
- package/lib/components/Clip/components/VideoDescription.js.map +1 -1
- package/lib/components/Clip/template/component.d.ts +9 -4
- package/lib/components/Clip/template/component.js +19 -14
- package/lib/components/Clip/template/component.js.map +1 -1
- package/lib/components/Clip/test/fixtures.js +114 -77
- package/lib/components/Clip/test/fixtures.js.map +1 -1
- package/lib/components/Clip/test/snapshot.spec.js +110 -61
- package/lib/components/Clip/test/snapshot.spec.js.map +1 -1
- package/lib/components/Flourish/index.d.ts +4 -12
- package/lib/components/Flourish/index.js +1 -1
- package/lib/components/Flourish/index.js.map +1 -1
- package/lib/components/Flourish/test/snapshot.spec.js +48 -6
- package/lib/components/Flourish/test/snapshot.spec.js.map +1 -1
- package/lib/components/Heading/index.d.ts +2 -1
- package/lib/components/Heading/index.js +2 -2
- package/lib/components/Heading/index.js.map +1 -1
- package/lib/components/ImageOverrideWrappers/index.d.ts +4 -0
- package/lib/components/ImageOverrideWrappers/index.js +44 -0
- package/lib/components/ImageOverrideWrappers/index.js.map +1 -0
- package/lib/components/ImageSet/index.d.ts +12 -8
- package/lib/components/ImageSet/index.js +13 -8
- package/lib/components/ImageSet/index.js.map +1 -1
- package/lib/components/Layout/index.d.ts +3 -13
- package/lib/components/Layout/index.js +1 -1
- package/lib/components/Layout/index.js.map +1 -1
- package/lib/components/MainImage/index.js +2 -2
- package/lib/components/MainImage/index.js.map +1 -1
- package/lib/components/Paragraph/index.d.ts +4 -1
- package/lib/components/Paragraph/index.js.map +1 -1
- package/lib/components/Pullquote/index.d.ts +4 -1
- package/lib/components/Pullquote/index.js +7 -6
- package/lib/components/Pullquote/index.js.map +1 -1
- package/lib/components/Recommended/index.d.ts +5 -8
- package/lib/components/Recommended/index.js +1 -6
- package/lib/components/Recommended/index.js.map +1 -1
- package/lib/components/RichText/BasicComponents.d.ts +12 -11
- package/lib/components/RichText/BasicComponents.js +2 -2
- package/lib/components/RichText/BasicComponents.js.map +1 -1
- package/lib/components/RichText/index.d.ts +10 -3
- package/lib/components/RichText/index.js +8 -2
- package/lib/components/RichText/index.js.map +1 -1
- package/lib/components/Scrollytelling/ScrollyImage.d.ts +6 -3
- package/lib/components/Scrollytelling/ScrollyImage.js +2 -2
- package/lib/components/Scrollytelling/ScrollyImage.js.map +1 -1
- package/lib/components/Scrollytelling/index.d.ts +14 -6
- package/lib/components/Scrollytelling/index.js +3 -3
- package/lib/components/Scrollytelling/index.js.map +1 -1
- package/lib/components/Table/TableBody.d.ts +4 -1
- package/lib/components/Table/TableBody.js +1 -1
- package/lib/components/Table/TableBody.js.map +1 -1
- package/lib/components/Table/TableCell.d.ts +4 -4
- package/lib/components/Table/TableCell.js +2 -2
- package/lib/components/Table/TableCell.js.map +1 -1
- package/lib/components/Table/index.d.ts +4 -1
- package/lib/components/Table/index.js +1 -1
- package/lib/components/Table/index.js.map +1 -1
- package/lib/components/Topper/Picture.d.ts +1 -1
- package/lib/components/Topper/Picture.js +6 -3
- package/lib/components/Topper/Picture.js.map +1 -1
- package/lib/components/Topper/index.js +7 -1
- package/lib/components/Topper/index.js.map +1 -1
- package/lib/components/Tweet/index.d.ts +4 -2
- package/lib/components/Tweet/index.js +1 -1
- package/lib/components/Tweet/index.js.map +1 -1
- package/lib/components/Video/index.d.ts +4 -2
- package/lib/components/Video/index.js +1 -1
- package/lib/components/Video/index.js.map +1 -1
- package/lib/components/Workarounds.d.ts +88 -0
- package/lib/components/Workarounds.js +3 -0
- package/lib/components/Workarounds.js.map +1 -0
- package/lib/components/YoutubeVideo/index.d.ts +5 -2
- package/lib/components/YoutubeVideo/index.js +1 -1
- package/lib/components/YoutubeVideo/index.js.map +1 -1
- package/lib/components/types.d.ts +5 -0
- package/lib/components/types.js +3 -0
- package/lib/components/types.js.map +1 -0
- package/lib/context.d.ts +14 -0
- package/lib/context.js +2 -1
- package/lib/context.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/stories/Clip.stories.d.ts +39 -243
- package/lib/stories/Clip.stories.js +320 -307
- package/lib/stories/Clip.stories.js.map +1 -1
- package/package.json +2 -1
- package/src/components/BigNumber/index.tsx +6 -3
- package/src/components/Body/__snapshots__/index.test.tsx.snap +2 -1
- package/src/components/Body/index.test.tsx +12 -1
- package/src/components/Byline/index.tsx +9 -4
- package/src/components/Clip/components/ClipTag.tsx +3 -6
- package/src/components/Clip/components/ClosedCaptions.tsx +2 -2
- package/src/components/Clip/components/VideoDescription.tsx +2 -2
- package/src/components/Clip/template/component.tsx +39 -41
- package/src/components/Clip/test/__snapshots__/snapshot.spec.tsx.snap +4 -8
- package/src/components/Clip/test/fixtures.ts +127 -90
- package/src/components/Clip/test/snapshot.spec.tsx +123 -102
- package/src/components/Flourish/index.tsx +10 -21
- package/src/components/Flourish/test/snapshot.spec.tsx +48 -36
- package/src/components/Heading/index.tsx +6 -5
- package/src/components/ImageOverrideWrappers/index.tsx +34 -0
- package/src/components/ImageSet/index.tsx +75 -55
- package/src/components/Layout/index.tsx +6 -18
- package/src/components/MainImage/index.tsx +10 -8
- package/src/components/Paragraph/index.tsx +4 -1
- package/src/components/Pullquote/index.tsx +15 -8
- package/src/components/Recommended/index.tsx +11 -11
- package/src/components/RichText/BasicComponents.tsx +22 -20
- package/src/components/RichText/index.tsx +35 -12
- package/src/components/Scrollytelling/ScrollyImage.tsx +10 -7
- package/src/components/Scrollytelling/index.tsx +19 -9
- package/src/components/Table/TableBody.tsx +7 -3
- package/src/components/Table/TableCell.tsx +9 -7
- package/src/components/Table/index.tsx +12 -9
- package/src/components/Topper/Picture.tsx +17 -12
- package/src/components/Topper/index.tsx +7 -4
- package/src/components/Tweet/index.tsx +4 -2
- package/src/components/Video/index.tsx +4 -6
- package/src/components/Workarounds.ts +188 -0
- package/src/components/YoutubeVideo/index.tsx +5 -4
- package/src/components/types.ts +6 -0
- package/src/context.ts +22 -1
- package/src/index.ts +4 -1
- package/src/stories/Clip.stories.tsx +357 -341
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -24,12 +24,14 @@ describe('Flourish component', () => {
|
|
|
24
24
|
it('renders with a full-grid layout and fallback image', async () => {
|
|
25
25
|
const { container } = render(
|
|
26
26
|
<Flourish
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
content={{
|
|
28
|
+
type: 'flourish',
|
|
29
|
+
id: '123',
|
|
30
|
+
flourishType: 'bar',
|
|
31
|
+
description: 'foo',
|
|
32
|
+
layoutWidth: 'full-grid',
|
|
33
|
+
fallbackImage: anImage,
|
|
34
|
+
}}
|
|
33
35
|
/>
|
|
34
36
|
)
|
|
35
37
|
expect(container).toMatchSnapshot()
|
|
@@ -38,12 +40,14 @@ describe('Flourish component', () => {
|
|
|
38
40
|
it('renders ignoring layout different from full-grid', async () => {
|
|
39
41
|
const { container } = render(
|
|
40
42
|
<Flourish
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
content={{
|
|
44
|
+
type: 'flourish',
|
|
45
|
+
id: '123',
|
|
46
|
+
flourishType: 'bar',
|
|
47
|
+
description: 'foo',
|
|
48
|
+
layoutWidth: 'whatever',
|
|
49
|
+
fallbackImage: anImage,
|
|
50
|
+
}}
|
|
47
51
|
/>
|
|
48
52
|
)
|
|
49
53
|
expect(container).toMatchSnapshot()
|
|
@@ -53,12 +57,14 @@ describe('Flourish component', () => {
|
|
|
53
57
|
const iFrame = true
|
|
54
58
|
const { container } = render(
|
|
55
59
|
<Flourish
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
content={{
|
|
61
|
+
type: 'flourish',
|
|
62
|
+
id: '123',
|
|
63
|
+
flourishType: 'bar',
|
|
64
|
+
description: 'foo',
|
|
65
|
+
layoutWidth: 'full-grid',
|
|
66
|
+
fallbackImage: anImage,
|
|
67
|
+
}}
|
|
62
68
|
iFrame={iFrame}
|
|
63
69
|
/>
|
|
64
70
|
)
|
|
@@ -69,12 +75,14 @@ describe('Flourish component', () => {
|
|
|
69
75
|
const inArticleBody = false
|
|
70
76
|
const { container } = render(
|
|
71
77
|
<Flourish
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
content={{
|
|
79
|
+
type: 'flourish',
|
|
80
|
+
id: '123',
|
|
81
|
+
flourishType: 'bar',
|
|
82
|
+
description: 'foo',
|
|
83
|
+
layoutWidth: 'full-grid',
|
|
84
|
+
fallbackImage: anImage,
|
|
85
|
+
}}
|
|
78
86
|
inArticleBody={inArticleBody}
|
|
79
87
|
/>
|
|
80
88
|
)
|
|
@@ -87,12 +95,14 @@ it('sets the hideTitle param in the iframe url to true when not in article body'
|
|
|
87
95
|
const iFrame = true
|
|
88
96
|
const { container } = render(
|
|
89
97
|
<Flourish
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
98
|
+
content={{
|
|
99
|
+
type: 'flourish',
|
|
100
|
+
id: '123',
|
|
101
|
+
flourishType: 'bar',
|
|
102
|
+
description: 'foo',
|
|
103
|
+
layoutWidth: 'full-grid',
|
|
104
|
+
fallbackImage: anImage,
|
|
105
|
+
}}
|
|
96
106
|
iFrame={iFrame}
|
|
97
107
|
inArticleBody={inArticleBody}
|
|
98
108
|
/>
|
|
@@ -103,12 +113,14 @@ it('sets the hideTitle param in the iframe url to true when not in article body'
|
|
|
103
113
|
it('does not set aspect ratios when in article body', async () => {
|
|
104
114
|
const { container } = render(
|
|
105
115
|
<Flourish
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
content={{
|
|
117
|
+
type: 'flourish',
|
|
118
|
+
id: '123',
|
|
119
|
+
flourishType: 'bar',
|
|
120
|
+
description: 'foo',
|
|
121
|
+
layoutWidth: 'full-grid',
|
|
122
|
+
fallbackImage: anImage,
|
|
123
|
+
}}
|
|
112
124
|
/>
|
|
113
125
|
)
|
|
114
126
|
expect(container).toMatchSnapshot()
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import type { ContentTree } from '@financial-times/content-tree'
|
|
3
|
+
import { ContentProps } from '../types'
|
|
3
4
|
|
|
4
|
-
const Heading: React.FC<
|
|
5
|
-
|
|
6
|
-
) => {
|
|
7
|
-
if (!props.children
|
|
5
|
+
const Heading: React.FC<
|
|
6
|
+
React.PropsWithChildren<ContentProps<ContentTree.Heading>>
|
|
7
|
+
> = (props) => {
|
|
8
|
+
if (!React.Children.count(props.children)) return null
|
|
8
9
|
|
|
9
|
-
switch (props.level) {
|
|
10
|
+
switch (props.content.level) {
|
|
10
11
|
case 'chapter':
|
|
11
12
|
return <h2 className="n-content-heading-2">{props.children}</h2>
|
|
12
13
|
case 'subheading':
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React, { useContext } from 'react'
|
|
2
|
+
|
|
3
|
+
import { PresentationOverridesContext } from '../../context'
|
|
4
|
+
|
|
5
|
+
import type { PresentationOverrideTypes } from '../../context'
|
|
6
|
+
|
|
7
|
+
export const FallbackImageOverrideWrapper: PresentationOverrideTypes['FallbackImage'] =
|
|
8
|
+
({ children, ...props }) => {
|
|
9
|
+
const { FallbackImage } = useContext(PresentationOverridesContext)
|
|
10
|
+
|
|
11
|
+
return FallbackImage ? (
|
|
12
|
+
<FallbackImage {...props}>{children}</FallbackImage>
|
|
13
|
+
) : (
|
|
14
|
+
children
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const ImageSetOverrideWrapper: PresentationOverrideTypes['ImageSet'] = ({
|
|
19
|
+
children,
|
|
20
|
+
...props
|
|
21
|
+
}) => {
|
|
22
|
+
const { ImageSet } = useContext(PresentationOverridesContext)
|
|
23
|
+
|
|
24
|
+
return ImageSet ? <ImageSet {...props}>{children}</ImageSet> : children
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const PictureOverrideWrapper: PresentationOverrideTypes['Picture'] = ({
|
|
28
|
+
children,
|
|
29
|
+
...props
|
|
30
|
+
}) => {
|
|
31
|
+
const { Picture } = useContext(PresentationOverridesContext)
|
|
32
|
+
|
|
33
|
+
return Picture ? <Picture {...props}>{children}</Picture> : children
|
|
34
|
+
}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import React, { PropsWithChildren, useContext } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
FallbackImageOverrideWrapper,
|
|
4
|
+
ImageSetOverrideWrapper,
|
|
5
|
+
} from '../ImageOverrideWrappers'
|
|
2
6
|
|
|
3
|
-
import type {
|
|
4
|
-
ImageFragment,
|
|
5
|
-
ImageSetFragment,
|
|
6
|
-
ImageSourceFragment,
|
|
7
|
-
PictureFragment,
|
|
8
|
-
} from '@financial-times/cp-content-pipeline-client'
|
|
9
7
|
import type { ContentTree } from '@financial-times/content-tree'
|
|
10
8
|
|
|
11
9
|
import { PresentationFlagsContext } from '../../context'
|
|
10
|
+
import { ContentProps } from '../types'
|
|
11
|
+
import { ContentTreeWorkarounds } from '@financial-times/cp-content-pipeline-schema'
|
|
12
|
+
import { SetRequired } from 'type-fest'
|
|
13
|
+
import type * as ComponentWorkarounds from '../Workarounds'
|
|
12
14
|
|
|
13
|
-
type ImageWithSourceSet =
|
|
14
|
-
sourceSet: ImageSourceFragment[]
|
|
15
|
-
}
|
|
15
|
+
type ImageWithSourceSet = SetRequired<ContentTree.Image, 'sourceSet'>
|
|
16
16
|
|
|
17
|
-
type ImageWithAliases =
|
|
17
|
+
type ImageWithAliases = ContentTree.Image & {
|
|
18
18
|
[alias: string]: ImageWithSourceSet['sourceSet']
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const formatSourceSet = (sourceSet:
|
|
21
|
+
const formatSourceSet = (sourceSet: ContentTree.ImageSource[]) =>
|
|
22
22
|
sourceSet.map((src) => `${src.url} ${src.dpr}x`).join(',')
|
|
23
23
|
|
|
24
24
|
const figureClassNameMap: Record<
|
|
@@ -42,7 +42,7 @@ export type AliasedBreakpoints = {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
const defaultGetBreakpoints = (
|
|
45
|
-
image:
|
|
45
|
+
image: ComponentWorkarounds.Image
|
|
46
46
|
): Breakpoint | Array<Breakpoint> | AliasedBreakpoints | undefined => {
|
|
47
47
|
switch (image.format) {
|
|
48
48
|
case 'mobile':
|
|
@@ -66,7 +66,9 @@ const breakpointToMedia = (breakpoint: Breakpoint): string =>
|
|
|
66
66
|
.filter(Boolean)
|
|
67
67
|
.join(' and ')
|
|
68
68
|
|
|
69
|
-
function hasSourceSet(
|
|
69
|
+
function hasSourceSet(
|
|
70
|
+
image: ComponentWorkarounds.Image | ContentTreeWorkarounds.RawImage
|
|
71
|
+
): image is ImageWithSourceSet {
|
|
70
72
|
return image && 'sourceSet' in image
|
|
71
73
|
}
|
|
72
74
|
|
|
@@ -80,7 +82,7 @@ function isAliasedBreakpoints(
|
|
|
80
82
|
)
|
|
81
83
|
}
|
|
82
84
|
|
|
83
|
-
function figureWidth(picture:
|
|
85
|
+
function figureWidth(picture: ComponentWorkarounds.ImageSetPicture) {
|
|
84
86
|
if (picture.layoutWidth === 'inset-left') {
|
|
85
87
|
const DEFAULT_WIDTH = 700
|
|
86
88
|
const width = picture.images[0]?.width ?? DEFAULT_WIDTH
|
|
@@ -97,7 +99,7 @@ function figureWidth(picture: PictureFragment) {
|
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
type SourceProps = {
|
|
100
|
-
image:
|
|
102
|
+
image: ComponentWorkarounds.Image
|
|
101
103
|
getBreakpoints?: BreakpointGetter
|
|
102
104
|
}
|
|
103
105
|
|
|
@@ -141,7 +143,7 @@ const Source: React.FC<SourceProps> = ({
|
|
|
141
143
|
}
|
|
142
144
|
|
|
143
145
|
type SourcesProps = {
|
|
144
|
-
images: readonly
|
|
146
|
+
images: readonly ComponentWorkarounds.Image[]
|
|
145
147
|
getBreakpoints?: BreakpointGetter
|
|
146
148
|
}
|
|
147
149
|
|
|
@@ -155,24 +157,34 @@ export const Sources: React.FC<SourcesProps> = ({ images, getBreakpoints }) => {
|
|
|
155
157
|
)
|
|
156
158
|
}
|
|
157
159
|
|
|
158
|
-
type
|
|
159
|
-
|
|
160
|
+
// HACK:20240809:IM This props type is quite a mess as the FallbackImage
|
|
161
|
+
// component can be used both directly in a RichTextChild with a Content Tree
|
|
162
|
+
// node, but also from the Image component above which the Image type. Image is
|
|
163
|
+
// defined in Content Tree but is _not_ a node so this is a bit icky :(
|
|
164
|
+
export type FallbackImageProps = (
|
|
165
|
+
| ContentProps<ComponentWorkarounds.RawImage>
|
|
166
|
+
| {
|
|
167
|
+
content: { image: ComponentWorkarounds.Image }
|
|
168
|
+
}
|
|
169
|
+
) & {
|
|
160
170
|
imageType?: string
|
|
161
|
-
picture?:
|
|
171
|
+
picture?: ComponentWorkarounds.ImageSetPicture
|
|
162
172
|
className?: string
|
|
163
173
|
inSourceSet?: boolean
|
|
164
174
|
isMainImage?: boolean
|
|
165
175
|
}
|
|
166
176
|
|
|
167
177
|
// TODO should `fallback` be added to content-tree imageset?
|
|
168
|
-
export const FallbackImage: React.FC<FallbackImageProps> = ({
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
178
|
+
export const FallbackImage: React.FC<FallbackImageProps> = (props) => {
|
|
179
|
+
const {
|
|
180
|
+
content: { image },
|
|
181
|
+
imageType = 'image',
|
|
182
|
+
picture,
|
|
183
|
+
className,
|
|
184
|
+
inSourceSet = false,
|
|
185
|
+
isMainImage = false,
|
|
186
|
+
} = props
|
|
187
|
+
|
|
176
188
|
const Wrapper = inSourceSet
|
|
177
189
|
? React.Fragment
|
|
178
190
|
: ({ children }: PropsWithChildren) => (
|
|
@@ -181,29 +193,35 @@ export const FallbackImage: React.FC<FallbackImageProps> = ({
|
|
|
181
193
|
|
|
182
194
|
return (
|
|
183
195
|
<Wrapper>
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
196
|
+
<FallbackImageOverrideWrapper {...props}>
|
|
197
|
+
<img
|
|
198
|
+
src={
|
|
199
|
+
hasSourceSet(image)
|
|
200
|
+
? image.sourceSet[0]?.url
|
|
201
|
+
: image?.url ?? undefined
|
|
202
|
+
}
|
|
203
|
+
srcSet={
|
|
204
|
+
hasSourceSet(image) ? formatSourceSet(image.sourceSet) : undefined
|
|
205
|
+
}
|
|
206
|
+
alt={picture?.alt ?? ''}
|
|
207
|
+
data-image-type={imageType}
|
|
208
|
+
width={image.width ?? undefined}
|
|
209
|
+
height={image.height ?? undefined}
|
|
210
|
+
className={className}
|
|
211
|
+
loading={isMainImage ? 'eager' : 'lazy'}
|
|
212
|
+
/>
|
|
213
|
+
</FallbackImageOverrideWrapper>
|
|
200
214
|
</Wrapper>
|
|
201
215
|
)
|
|
202
216
|
}
|
|
203
217
|
|
|
204
|
-
|
|
218
|
+
export interface ImageSetProps extends ContentProps<ComponentWorkarounds.ImageSet> {
|
|
219
|
+
isMainImage?: boolean
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const ImageSet: React.FC<ImageSetProps> = (props) => {
|
|
223
|
+
const { content: imageSet, isMainImage } = props
|
|
205
224
|
|
|
206
|
-
const ImageSet: React.FC<ImageSetProps> = (imageSet) => {
|
|
207
225
|
if (!imageSet.picture?.images) {
|
|
208
226
|
return null
|
|
209
227
|
}
|
|
@@ -232,16 +250,18 @@ const ImageSet: React.FC<ImageSetProps> = (imageSet) => {
|
|
|
232
250
|
style={figureWidth(imageSet.picture)}
|
|
233
251
|
data-component="image-set"
|
|
234
252
|
>
|
|
235
|
-
<
|
|
236
|
-
<
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
253
|
+
<ImageSetOverrideWrapper {...props}>
|
|
254
|
+
<picture>
|
|
255
|
+
<Sources images={imageSet.picture.images} />
|
|
256
|
+
<FallbackImage
|
|
257
|
+
picture={imageSet.picture}
|
|
258
|
+
content={{ image: imageSet.picture.fallbackImage }}
|
|
259
|
+
imageType={imageSet.picture.imageType || 'image'}
|
|
260
|
+
inSourceSet={true}
|
|
261
|
+
isMainImage={isMainImage}
|
|
262
|
+
/>
|
|
263
|
+
</picture>
|
|
264
|
+
</ImageSetOverrideWrapper>
|
|
245
265
|
{(imageSet.picture.caption || imageSet.picture.credit) && (
|
|
246
266
|
<figcaption className="n-content-picture__caption">
|
|
247
267
|
<span>{imageSet.picture.caption}</span>
|
|
@@ -1,21 +1,8 @@
|
|
|
1
1
|
import { ContentTree } from '@financial-times/content-tree'
|
|
2
2
|
import React from 'react'
|
|
3
|
+
import { ContentProps } from '../types'
|
|
3
4
|
|
|
4
|
-
interface
|
|
5
|
-
type: 'slot'
|
|
6
|
-
children: [ContentTree.ImageSet?, ...ContentTree.Paragraph[]]
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
interface BaseLayoutProps {
|
|
10
|
-
layoutWidth: 'inset-left' | 'full-width' | 'full-grid' | 'in-line'
|
|
11
|
-
layoutName?: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface ContentTreeCardLayout extends BaseLayoutProps, ContentTree.Node {
|
|
15
|
-
type: 'layout'
|
|
16
|
-
layoutName: 'auto' | 'card' | 'timeline'
|
|
17
|
-
children: ContentTreeLayoutSlot[]
|
|
18
|
-
}
|
|
5
|
+
interface LayoutProps extends ContentProps<ContentTree.transit.Layout> {}
|
|
19
6
|
|
|
20
7
|
/*
|
|
21
8
|
* @description Layout component is used to define the layout for content. It can be used with ContentTree Layout Nodes or as a static layout.
|
|
@@ -23,9 +10,10 @@ interface ContentTreeCardLayout extends BaseLayoutProps, ContentTree.Node {
|
|
|
23
10
|
* @param layoutName - The name of the layout.
|
|
24
11
|
* @param children - The content to be displayed in the layout.
|
|
25
12
|
*/
|
|
26
|
-
export const Layout: React.FC<
|
|
27
|
-
|
|
28
|
-
|
|
13
|
+
export const Layout: React.FC<React.PropsWithChildren<LayoutProps>> = ({
|
|
14
|
+
content: { layoutWidth, layoutName = 'auto' },
|
|
15
|
+
children,
|
|
16
|
+
}) => {
|
|
29
17
|
return (
|
|
30
18
|
<div
|
|
31
19
|
className="n-content-layout"
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import type {
|
|
3
3
|
ArticleQuery,
|
|
4
|
-
|
|
5
|
-
ImageSetFragment,
|
|
4
|
+
MainImageFragment,
|
|
6
5
|
} from '@financial-times/cp-content-pipeline-client'
|
|
7
6
|
import ImageSet, { FallbackImage } from '../ImageSet/index'
|
|
8
7
|
import { ContentTree } from '@financial-times/content-tree'
|
|
8
|
+
import type * as ComponentWorkarounds from '../Workarounds'
|
|
9
9
|
|
|
10
10
|
type MainImageProps = {
|
|
11
11
|
content: ArticleQuery['content']
|
|
@@ -20,19 +20,21 @@ const MainImage: React.FC<MainImageProps> = ({ content }) => {
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
const mainImage = references?.find(
|
|
23
|
-
(ref)
|
|
24
|
-
|
|
23
|
+
(ref): ref is ContentTree.ImageSet & MainImageFragment =>
|
|
24
|
+
ref.type === 'main-image'
|
|
25
|
+
)
|
|
25
26
|
|
|
26
27
|
if (mainImage) {
|
|
27
|
-
return <ImageSet isMainImage {
|
|
28
|
+
return <ImageSet isMainImage content={mainImage} />
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
const mainImageRaw = references?.find(
|
|
31
|
-
(ref)
|
|
32
|
-
|
|
32
|
+
(ref): ref is ComponentWorkarounds.MainImageRaw =>
|
|
33
|
+
ref.type === 'main-image-raw'
|
|
34
|
+
)
|
|
33
35
|
|
|
34
36
|
if (mainImageRaw) {
|
|
35
|
-
return <FallbackImage isMainImage {
|
|
37
|
+
return <FallbackImage isMainImage content={mainImageRaw} />
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
return null
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { ContentTree } from '@financial-times/content-tree'
|
|
2
2
|
import React, { createContext, useContext } from 'react'
|
|
3
|
+
import { ContentProps } from '../types'
|
|
3
4
|
|
|
4
5
|
export const ParagraphContext = createContext<{ className: string } | null>(
|
|
5
6
|
null
|
|
6
7
|
)
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
interface ParagraphProps extends ContentProps<ContentTree.Paragraph> {}
|
|
10
|
+
|
|
11
|
+
const Paragraph: React.FC<React.PropsWithChildren<ParagraphProps>> = (
|
|
9
12
|
props
|
|
10
13
|
) => {
|
|
11
14
|
const { className } = useContext(ParagraphContext) ?? { className: undefined }
|
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { ContentTreeWorkarounds } from '@financial-times/cp-content-pipeline-schema'
|
|
3
3
|
import classnames from 'classnames'
|
|
4
|
+
import { ContentProps } from '../types'
|
|
5
|
+
|
|
6
|
+
interface PullquoteProps
|
|
7
|
+
extends ContentProps<ContentTreeWorkarounds.Pullquote> {}
|
|
8
|
+
|
|
9
|
+
const Pullquote: React.FC<React.PropsWithChildren<PullquoteProps>> = ({
|
|
10
|
+
content: { text, source },
|
|
11
|
+
children,
|
|
12
|
+
}) => {
|
|
13
|
+
const hasChildren = React.Children.count(children) > 0
|
|
4
14
|
|
|
5
|
-
const Pullquote: React.FC<
|
|
6
|
-
React.PropsWithChildren<ContentTreeWorkarounds.ContentTreePullquote>
|
|
7
|
-
> = (props) => {
|
|
8
15
|
return (
|
|
9
16
|
<blockquote
|
|
10
17
|
className={classnames('n-content-pullquote', {
|
|
11
|
-
'n-content-pullquote--with-image':
|
|
12
|
-
'n-content-pullquote--no-image': !
|
|
18
|
+
'n-content-pullquote--with-image': hasChildren,
|
|
19
|
+
'n-content-pullquote--no-image': !hasChildren,
|
|
13
20
|
})}
|
|
14
21
|
aria-hidden="true"
|
|
15
22
|
>
|
|
16
23
|
<div className="n-content-pullquote__content">
|
|
17
|
-
<p>{
|
|
18
|
-
<footer className="n-content-pullquote__footer">{
|
|
24
|
+
<p>{text}</p>
|
|
25
|
+
<footer className="n-content-pullquote__footer">{source}</footer>
|
|
19
26
|
</div>
|
|
20
27
|
|
|
21
|
-
{
|
|
28
|
+
{children}
|
|
22
29
|
</blockquote>
|
|
23
30
|
)
|
|
24
31
|
}
|
|
@@ -2,19 +2,19 @@ import React from 'react'
|
|
|
2
2
|
|
|
3
3
|
//HACK: worked around missing Teaser type by declaring a module x-teaser.d.ts
|
|
4
4
|
import { presets, Teaser } from '@financial-times/x-teaser/dist/Teaser.cjs'
|
|
5
|
-
import { ContentTree } from '@financial-times/content-tree'
|
|
6
|
-
import type { RecommendedFragment } from '@financial-times/cp-content-pipeline-client'
|
|
7
5
|
import classnames from 'classnames'
|
|
8
|
-
|
|
9
|
-
*
|
|
10
|
-
* `<Teaser>` is imported from x-dash
|
|
11
|
-
* https://github.com/Financial-Times/x-dash/tree/main/components/x-teaser)
|
|
12
|
-
*/
|
|
6
|
+
import { ContentProps } from '../types'
|
|
7
|
+
import type * as ComponentWorkarounds from '../Workarounds'
|
|
13
8
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
// Renders a Recommended teaser component
|
|
10
|
+
// `<Teaser>` is imported from x-dash
|
|
11
|
+
// https://github.com/Financial-Times/x-dash/tree/main/components/x-teaser)
|
|
12
|
+
|
|
13
|
+
interface RecommendedProps
|
|
14
|
+
extends ContentProps<ComponentWorkarounds.Recommended> {}
|
|
15
|
+
|
|
16
|
+
const Recommended: React.FC<RecommendedProps> = ({
|
|
17
|
+
content: { heading, teaser, isInLiveBlog },
|
|
18
18
|
}) => {
|
|
19
19
|
if (!teaser) {
|
|
20
20
|
return null
|
|
@@ -1,34 +1,36 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import type { ContentTree } from '@financial-times/content-tree'
|
|
3
3
|
import type { ContentTreeWorkarounds } from '@financial-times/cp-content-pipeline-schema'
|
|
4
|
+
import { ContentProps } from '../types'
|
|
4
5
|
|
|
5
|
-
export const List: React.FC<
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
export const List: React.FC<
|
|
7
|
+
React.PropsWithChildren<ContentProps<ContentTree.List>>
|
|
8
|
+
> = (props) =>
|
|
9
|
+
props.content.ordered ? <ol>{props.children}</ol> : <ul>{props.children}</ul>
|
|
8
10
|
|
|
9
11
|
export const ListItem: React.FC<
|
|
10
|
-
React.PropsWithChildren<ContentTree.ListItem
|
|
12
|
+
React.PropsWithChildren<ContentProps<ContentTree.ListItem>>
|
|
11
13
|
> = (props) => <li>{props.children}</li>
|
|
12
14
|
|
|
13
|
-
export const Link: React.FC<
|
|
14
|
-
|
|
15
|
-
) => (
|
|
16
|
-
<a href={props.url} title={props.title}>
|
|
15
|
+
export const Link: React.FC<
|
|
16
|
+
React.PropsWithChildren<ContentProps<ContentTree.Link>>
|
|
17
|
+
> = (props) => (
|
|
18
|
+
<a href={props.content.url} title={props.content.title}>
|
|
17
19
|
{props.children}
|
|
18
20
|
</a>
|
|
19
21
|
)
|
|
20
22
|
|
|
21
23
|
export const Blockquote: React.FC<
|
|
22
|
-
React.PropsWithChildren<ContentTree.Blockquote
|
|
24
|
+
React.PropsWithChildren<ContentProps<ContentTree.Blockquote>>
|
|
23
25
|
> = (props) => {
|
|
24
26
|
return (
|
|
25
27
|
<blockquote className="n-content-blockquote">{props.children}</blockquote>
|
|
26
28
|
)
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
export const Cite: React.FC<
|
|
30
|
-
|
|
31
|
-
) => {
|
|
31
|
+
export const Cite: React.FC<
|
|
32
|
+
React.PropsWithChildren<ContentProps<ContentTreeWorkarounds.Cite>>
|
|
33
|
+
> = (props) => {
|
|
32
34
|
return <cite>{props.children}</cite>
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -37,23 +39,23 @@ export const LineBreak: React.FC = () => <br />
|
|
|
37
39
|
export const HorizontalRule: React.FC = () => <hr />
|
|
38
40
|
|
|
39
41
|
export const Emphasis: React.FC<
|
|
40
|
-
React.PropsWithChildren<ContentTree.Emphasis
|
|
42
|
+
React.PropsWithChildren<ContentProps<ContentTree.Emphasis>>
|
|
41
43
|
> = (props) => <em>{props.children}</em>
|
|
42
44
|
|
|
43
|
-
export const Strong: React.FC<
|
|
44
|
-
|
|
45
|
-
) => <strong>{props.children}</strong>
|
|
45
|
+
export const Strong: React.FC<
|
|
46
|
+
React.PropsWithChildren<ContentProps<ContentTree.Strong>>
|
|
47
|
+
> = (props) => <strong>{props.children}</strong>
|
|
46
48
|
|
|
47
49
|
export const Strikethrough: React.FC<
|
|
48
|
-
React.PropsWithChildren<ContentTree.Strikethrough
|
|
50
|
+
React.PropsWithChildren<ContentProps<ContentTree.Strikethrough>>
|
|
49
51
|
> = (props) => <s>{props.children}</s>
|
|
50
52
|
|
|
51
53
|
export const TableRow: React.FC<
|
|
52
|
-
React.PropsWithChildren<ContentTreeWorkarounds.TableRow
|
|
54
|
+
React.PropsWithChildren<ContentProps<ContentTreeWorkarounds.TableRow>>
|
|
53
55
|
> = (props) => <tr>{props.children}</tr>
|
|
54
56
|
|
|
55
57
|
export const TableFooter: React.FC<
|
|
56
|
-
React.PropsWithChildren<ContentTreeWorkarounds.TableFooter
|
|
58
|
+
React.PropsWithChildren<ContentProps<ContentTreeWorkarounds.TableFooter>>
|
|
57
59
|
> = (props) => (
|
|
58
60
|
<tfoot>
|
|
59
61
|
<tr>
|
|
@@ -65,7 +67,7 @@ export const TableFooter: React.FC<
|
|
|
65
67
|
)
|
|
66
68
|
|
|
67
69
|
export const TableCaption: React.FC<
|
|
68
|
-
React.PropsWithChildren<ContentTreeWorkarounds.TableCaption
|
|
70
|
+
React.PropsWithChildren<ContentProps<ContentTreeWorkarounds.TableCaption>>
|
|
69
71
|
> = (props) => (
|
|
70
72
|
<caption className="n-content-body__caption n-content-body__caption--auto">
|
|
71
73
|
{props.children}
|