@financial-times/cp-content-pipeline-ui 6.15.6-beta.0 → 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 +29 -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
|
@@ -34,8 +34,12 @@ import {
|
|
|
34
34
|
TableRow,
|
|
35
35
|
} from './BasicComponents'
|
|
36
36
|
|
|
37
|
-
import type {
|
|
37
|
+
import type {
|
|
38
|
+
StructuredContentFragment,
|
|
39
|
+
ReferenceMapping,
|
|
40
|
+
} from '@financial-times/cp-content-pipeline-client'
|
|
38
41
|
import { ContentTreeWorkarounds } from '@financial-times/cp-content-pipeline-schema'
|
|
42
|
+
import { mapNodeToReference } from '@financial-times/cp-content-pipeline-schema/lib/resolvers/content-tree/references'
|
|
39
43
|
import TableBody from '../Table/TableBody'
|
|
40
44
|
import { TableCell } from '../Table/TableCell'
|
|
41
45
|
import {
|
|
@@ -46,17 +50,33 @@ import {
|
|
|
46
50
|
} from '../Scrollytelling'
|
|
47
51
|
import { ScrollyImage } from '../Scrollytelling/ScrollyImage'
|
|
48
52
|
import YoutubeVideo from '../YoutubeVideo'
|
|
53
|
+
import { ContentProps } from '../types'
|
|
49
54
|
|
|
50
55
|
import RichTextContext from './context'
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
// A helper type that takes a content tree type identifier and maps it to the
|
|
58
|
+
// object that is returned in an accompanying reference
|
|
59
|
+
type mapComponentToReference<T extends ContentTreeWorkarounds.AnyNode['type']> =
|
|
60
|
+
T extends keyof typeof mapNodeToReference
|
|
61
|
+
? (typeof mapNodeToReference)[T] extends keyof ReferenceMapping
|
|
62
|
+
? ReferenceMapping[(typeof mapNodeToReference)[T]]
|
|
63
|
+
: never
|
|
64
|
+
: Record<string, never>
|
|
65
|
+
|
|
66
|
+
// Partial record of content tree nodes and their associated reference types
|
|
67
|
+
export type RichTextComponentMapRecord = {
|
|
68
|
+
[T in
|
|
69
|
+
| ContentTreeWorkarounds.AnyNode
|
|
70
|
+
| { type: 'fallback' } as T['type']]?: JSXElementConstructor<
|
|
71
|
+
React.PropsWithChildren<
|
|
72
|
+
T['type'] extends 'fallback'
|
|
73
|
+
? unknown
|
|
74
|
+
: ContentProps<
|
|
75
|
+
T & mapComponentToReference<Exclude<T['type'], 'fallback'>>
|
|
76
|
+
>
|
|
77
|
+
>
|
|
58
78
|
>
|
|
59
|
-
|
|
79
|
+
}
|
|
60
80
|
|
|
61
81
|
export const ComponentsContext = createContext<
|
|
62
82
|
RichTextComponentMapRecord | undefined
|
|
@@ -165,16 +185,19 @@ const RichTextChild: React.FC<RichTextChildProps> = ({
|
|
|
165
185
|
.filter(Boolean)
|
|
166
186
|
: []
|
|
167
187
|
|
|
168
|
-
const reference
|
|
188
|
+
const reference:
|
|
189
|
+
| StructuredContentFragment['references'][number]
|
|
190
|
+
| Record<string, never> =
|
|
169
191
|
typeof node.data?.referenceIndex === 'number'
|
|
170
|
-
? references[node.data?.referenceIndex]
|
|
192
|
+
? references[node.data?.referenceIndex] ?? {}
|
|
171
193
|
: {}
|
|
172
194
|
|
|
173
195
|
return (
|
|
174
196
|
<RichTextContext.Provider value={RichText}>
|
|
175
197
|
<Component
|
|
176
|
-
|
|
177
|
-
|
|
198
|
+
// HACK:20240808:IM TypeScript fails to unify the content types here
|
|
199
|
+
// and I'm not sure why :(
|
|
200
|
+
content={{ ...node, ...reference } as never}
|
|
178
201
|
key={parentIndex}
|
|
179
202
|
parentIndex={parentIndex}
|
|
180
203
|
>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ContentTree } from '@financial-times/content-tree'
|
|
2
|
-
import { ScrollyImageFragment } from '@financial-times/cp-content-pipeline-client'
|
|
3
1
|
import React, { useContext } from 'react'
|
|
4
2
|
import { BreakpointGetter, FallbackImage, Sources } from '../ImageSet'
|
|
5
3
|
import { ScrollyIDContext } from '.'
|
|
4
|
+
import { ContentProps } from '../types'
|
|
5
|
+
import type * as ComponentWorkarounds from '../Workarounds'
|
|
6
6
|
|
|
7
7
|
const scrollyGetBreakpoints: BreakpointGetter = (image) => {
|
|
8
8
|
switch (image.format) {
|
|
@@ -25,9 +25,12 @@ const scrollyGetBreakpoints: BreakpointGetter = (image) => {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
interface ScrollyImageProps
|
|
29
|
+
extends ContentProps<ComponentWorkarounds.ScrollyImage> {}
|
|
30
|
+
|
|
31
|
+
export const ScrollyImage: React.FC<ScrollyImageProps> = ({
|
|
32
|
+
content: { picture },
|
|
33
|
+
}) => {
|
|
31
34
|
const scrollyId = useContext(ScrollyIDContext)
|
|
32
35
|
|
|
33
36
|
if (!picture?.images) {
|
|
@@ -47,7 +50,7 @@ export const ScrollyImage: React.FC<
|
|
|
47
50
|
getBreakpoints={scrollyGetBreakpoints}
|
|
48
51
|
/>
|
|
49
52
|
<FallbackImage
|
|
50
|
-
|
|
53
|
+
content={{ image: picture.fallbackImage }}
|
|
51
54
|
imageType={picture.imageType}
|
|
52
55
|
className="n-scrollytelling__image"
|
|
53
56
|
inSourceSet={true}
|
|
@@ -61,4 +64,4 @@ export const ScrollyImage: React.FC<
|
|
|
61
64
|
)}
|
|
62
65
|
</figure>
|
|
63
66
|
)
|
|
64
|
-
}
|
|
67
|
+
}
|
|
@@ -3,14 +3,16 @@ import type { ContentTree } from '@financial-times/content-tree'
|
|
|
3
3
|
import classnames from 'classnames'
|
|
4
4
|
import type scrollytelling from '@financial-times/n-scrollytelling-image/server'
|
|
5
5
|
import { ParagraphContext } from '../Paragraph'
|
|
6
|
+
import { ContentProps } from '../types'
|
|
6
7
|
|
|
7
8
|
export const ScrollyIDContext = createContext<string | null>(null)
|
|
8
9
|
|
|
10
|
+
interface ScrollyBlockProps
|
|
11
|
+
extends ContentProps<ContentTree.transit.ScrollyBlock> {}
|
|
12
|
+
|
|
9
13
|
export const ScrollyBlock: React.FC<
|
|
10
|
-
React.PropsWithChildren<
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
> = ({ children, theme, parentIndex }) => (
|
|
14
|
+
React.PropsWithChildren<ScrollyBlockProps>
|
|
15
|
+
> = ({ content: { theme }, children, parentIndex }) => (
|
|
14
16
|
<div
|
|
15
17
|
className="n-content-layout n-content-layout--scrollytelling"
|
|
16
18
|
data-layout-name="auto"
|
|
@@ -42,9 +44,12 @@ type ScrollySectionOptions = Pick<
|
|
|
42
44
|
const ScrollySectionOptionsContext =
|
|
43
45
|
createContext<ScrollySectionOptions | null>(null)
|
|
44
46
|
|
|
47
|
+
interface ScrollySectionProps
|
|
48
|
+
extends ContentProps<ContentTree.transit.ScrollySection> {}
|
|
49
|
+
|
|
45
50
|
export const ScrollySection: React.FC<
|
|
46
|
-
React.PropsWithChildren<
|
|
47
|
-
> = ({
|
|
51
|
+
React.PropsWithChildren<ScrollySectionProps>
|
|
52
|
+
> = ({ content: { display, noBox, position, transition }, children }) => (
|
|
48
53
|
<ScrollySectionOptionsContext.Provider
|
|
49
54
|
value={{ display, noBox, position, transition }}
|
|
50
55
|
>
|
|
@@ -55,8 +60,10 @@ export const ScrollySection: React.FC<
|
|
|
55
60
|
type ScrollytellingDisplaySuffix =
|
|
56
61
|
scrollytelling.ScrollytellingOptionsMap['display'][keyof scrollytelling.ScrollytellingOptionsMap['display']]
|
|
57
62
|
|
|
63
|
+
interface ScrollyCopyProps extends ContentProps<ContentTree.ScrollyCopy> {}
|
|
64
|
+
|
|
58
65
|
export const ScrollyCopy: React.FC<
|
|
59
|
-
React.PropsWithChildren<
|
|
66
|
+
React.PropsWithChildren<ScrollyCopyProps>
|
|
60
67
|
> = ({ children }) => {
|
|
61
68
|
const { display, noBox, position, transition } = useContext(
|
|
62
69
|
ScrollySectionOptionsContext
|
|
@@ -100,9 +107,12 @@ export const ScrollyCopy: React.FC<
|
|
|
100
107
|
)
|
|
101
108
|
}
|
|
102
109
|
|
|
110
|
+
interface ScrollyHeadingProps
|
|
111
|
+
extends ContentProps<ContentTree.ScrollyHeading> {}
|
|
112
|
+
|
|
103
113
|
export const ScrollyHeading: React.FC<
|
|
104
|
-
React.PropsWithChildren<
|
|
105
|
-
> = ({
|
|
114
|
+
React.PropsWithChildren<ScrollyHeadingProps>
|
|
115
|
+
> = ({ content: { level }, children }) => {
|
|
106
116
|
const classNames = classnames(
|
|
107
117
|
'n-scrollytelling__overlay-text',
|
|
108
118
|
`n-scrollytelling__overlay-text--text-style-${level}`
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { ContentTreeWorkarounds } from '@financial-times/cp-content-pipeline-schema'
|
|
2
2
|
import React from 'react'
|
|
3
|
+
import { ContentProps } from '../types'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
interface TableBodyProps
|
|
6
|
+
extends ContentProps<ContentTreeWorkarounds.TableBody> {}
|
|
7
|
+
|
|
8
|
+
const TableBody: React.FC<React.PropsWithChildren<TableBodyProps>> = ({
|
|
9
|
+
children,
|
|
10
|
+
}) => {
|
|
7
11
|
const [headerRow, ...bodyRows] = React.Children.toArray(children)
|
|
8
12
|
|
|
9
13
|
return (
|
|
@@ -2,20 +2,22 @@ import React, { useContext } from 'react'
|
|
|
2
2
|
import type { ContentTreeWorkarounds } from '@financial-times/cp-content-pipeline-schema'
|
|
3
3
|
import { ColumnSettingsContext } from '.'
|
|
4
4
|
import classNames from 'classnames'
|
|
5
|
+
import { ContentProps } from '../types'
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
interface TableCellProps
|
|
8
|
+
extends ContentProps<ContentTreeWorkarounds.TableCell> {}
|
|
9
|
+
|
|
10
|
+
export const TableCell: React.FC<React.PropsWithChildren<TableCellProps>> = (
|
|
11
|
+
props
|
|
12
|
+
) => {
|
|
11
13
|
const allColumnSettings = useContext(ColumnSettingsContext)
|
|
12
14
|
const columnSettings: ContentTreeWorkarounds.TableColumnSettings =
|
|
13
|
-
allColumnSettings[props.parentIndex] ?? {
|
|
15
|
+
(props.parentIndex ? allColumnSettings[props.parentIndex] : undefined) ?? {
|
|
14
16
|
hideOnMobile: false,
|
|
15
17
|
sortable: false,
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
return props.heading ? (
|
|
20
|
+
return props.content.heading ? (
|
|
19
21
|
<th
|
|
20
22
|
className={classNames({
|
|
21
23
|
'n-content-body__table-cell--hide-mobile': columnSettings.hideOnMobile,
|
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import type { ContentTreeWorkarounds } from '@financial-times/cp-content-pipeline-schema'
|
|
2
2
|
import React, { createContext } from 'react'
|
|
3
3
|
import classNames from 'classnames'
|
|
4
|
+
import { ContentProps } from '../types'
|
|
4
5
|
|
|
5
6
|
export const ColumnSettingsContext = createContext<
|
|
6
7
|
ContentTreeWorkarounds.TableColumnSettings[]
|
|
7
8
|
>([])
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
interface TableProps extends ContentProps<ContentTreeWorkarounds.Table> {}
|
|
11
|
+
|
|
12
|
+
const Table: React.FC<React.PropsWithChildren<TableProps>> = ({
|
|
12
13
|
children,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
content: {
|
|
15
|
+
compact,
|
|
16
|
+
layoutWidth,
|
|
17
|
+
stripes,
|
|
18
|
+
responsiveStyle,
|
|
19
|
+
columnSettings,
|
|
20
|
+
collapseAfterHowManyRows,
|
|
21
|
+
},
|
|
19
22
|
}) => {
|
|
20
23
|
return (
|
|
21
24
|
<ColumnSettingsContext.Provider value={columnSettings}>
|
|
@@ -3,6 +3,8 @@ import React from 'react'
|
|
|
3
3
|
import type { TopperFragment } from '@financial-times/cp-content-pipeline-client'
|
|
4
4
|
|
|
5
5
|
import { AliasedBreakpoints, BreakpointGetter, Sources } from '../ImageSet'
|
|
6
|
+
import { PictureOverrideWrapper } from '../ImageOverrideWrappers'
|
|
7
|
+
import { ContentTree } from '@financial-times/content-tree'
|
|
6
8
|
|
|
7
9
|
const topperGetBreakpointsMap: Partial<
|
|
8
10
|
Record<TopperFragment['__typename'], BreakpointGetter>
|
|
@@ -64,12 +66,13 @@ const topperGetBreakpointsMap: Partial<
|
|
|
64
66
|
},
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
type PictureProps = {
|
|
69
|
+
export type PictureProps = {
|
|
68
70
|
topper: TopperFragment
|
|
69
71
|
alt?: string
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
const Picture: React.FC<PictureProps> = (
|
|
74
|
+
const Picture: React.FC<PictureProps> = (props) => {
|
|
75
|
+
const { topper, alt = '' } = props
|
|
73
76
|
if (!('images' in topper) || !topper.fallbackImage) {
|
|
74
77
|
return null
|
|
75
78
|
}
|
|
@@ -77,17 +80,19 @@ const Picture: React.FC<PictureProps> = ({ topper, alt = '' }) => {
|
|
|
77
80
|
return (
|
|
78
81
|
<figure className="o-topper__visual">
|
|
79
82
|
<picture className="o-topper__picture" key="responsive-images">
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
<PictureOverrideWrapper {...props}>
|
|
84
|
+
<Sources
|
|
85
|
+
images={topper.images as readonly ContentTree.Image[]}
|
|
86
|
+
getBreakpoints={topperGetBreakpointsMap[topper.__typename]}
|
|
87
|
+
/>
|
|
84
88
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
<img
|
|
90
|
+
alt={alt}
|
|
91
|
+
className="o-topper__image"
|
|
92
|
+
src={topper.fallbackImage.sourceSet[0]?.url}
|
|
93
|
+
key="fallback-image"
|
|
94
|
+
/>
|
|
95
|
+
</PictureOverrideWrapper>
|
|
91
96
|
</picture>
|
|
92
97
|
{topper.fallbackImage.caption ||
|
|
93
98
|
topper.__typename === 'DeepPortraitTopper' ||
|
|
@@ -147,10 +147,13 @@ const Topper: React.FC<TopperProps> = ({
|
|
|
147
147
|
/>
|
|
148
148
|
{isLiveBlog && isFlourish ? (
|
|
149
149
|
<Flourish
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
150
|
+
content={{
|
|
151
|
+
type: 'flourish',
|
|
152
|
+
id: topper.leadFlourish.id ?? '',
|
|
153
|
+
flourishType: topper.leadFlourish.type ?? '',
|
|
154
|
+
description: topper.leadFlourish.description ?? '',
|
|
155
|
+
fallbackImage: topper.leadFlourish.fallbackImage,
|
|
156
|
+
}}
|
|
154
157
|
iFrame={true}
|
|
155
158
|
inArticleBody={false}
|
|
156
159
|
/>
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import type { ContentTree } from '@financial-times/content-tree'
|
|
3
|
-
import {
|
|
3
|
+
import { ContentProps } from '../types'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
interface TweetProps extends ContentProps<ContentTree.Tweet> {}
|
|
6
|
+
|
|
7
|
+
const Tweet: React.FC<TweetProps> = ({ content: tweet }) => {
|
|
6
8
|
if (tweet.html) {
|
|
7
9
|
return (
|
|
8
10
|
<div
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import { ContentProps } from '../types'
|
|
3
3
|
import { ContentTreeWorkarounds } from '@financial-times/cp-content-pipeline-schema'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
title,
|
|
9
|
-
}) => {
|
|
5
|
+
interface VideoProps extends ContentProps<ContentTreeWorkarounds.Video> {}
|
|
6
|
+
|
|
7
|
+
const Video: React.FC<VideoProps> = ({ content: { id, embedded, title } }) => {
|
|
10
8
|
return (
|
|
11
9
|
<div
|
|
12
10
|
className="n-content-video n-content-video--internal"
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import type { ContentTree } from '@financial-times/content-tree'
|
|
2
|
+
import {
|
|
3
|
+
ConceptFragment,
|
|
4
|
+
TranscriptFragment,
|
|
5
|
+
} from '@financial-times/cp-content-pipeline-client'
|
|
6
|
+
import type { ContentTreeWorkarounds } from '@financial-times/cp-content-pipeline-schema'
|
|
7
|
+
import type {
|
|
8
|
+
LiteralToPrimitiveDeep,
|
|
9
|
+
SetFieldType,
|
|
10
|
+
SetOptional,
|
|
11
|
+
Simplify,
|
|
12
|
+
} from 'type-fest'
|
|
13
|
+
|
|
14
|
+
// Analogous to the Content Tree workarounds we use in the schema package,
|
|
15
|
+
// these are workarounds for Content Tree types passed to components. These
|
|
16
|
+
// differ insofar as they also include the reference type for nodes, i.e., they
|
|
17
|
+
// are the ContentTree.full types to schema's ContentTree.transit types.
|
|
18
|
+
// Similarly, we should aim to reduce these divergences by either adding them
|
|
19
|
+
// to Content Tree or finding alternative ways of resolving them in our API.
|
|
20
|
+
// Unlike the schema's workarounds, I've opted for a more declarative, verbose
|
|
21
|
+
// approach to listing the differences between the workarounds and the original
|
|
22
|
+
// types, with the hope that the differences can be fixed more incrementally.
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Helper type to extend partial/undefined fields to also be null. Optional
|
|
26
|
+
* fields in the Content Tree are represented by a partial field, but are
|
|
27
|
+
* represented by null (abstracted by the Maybe type) in the content pipeline.
|
|
28
|
+
*/
|
|
29
|
+
type PartialToMaybe<T> = Simplify<{
|
|
30
|
+
[K in keyof T]: Extract<T[K], undefined> extends never ? T[K] : T[K] | null
|
|
31
|
+
}>
|
|
32
|
+
/**
|
|
33
|
+
* Helper type to apply PartialToMaybe mapper type to each object field
|
|
34
|
+
* recursively.
|
|
35
|
+
*/
|
|
36
|
+
type PartialToMaybeDeep<T> = T extends object
|
|
37
|
+
? PartialToMaybe<{
|
|
38
|
+
[K in keyof T]: PartialToMaybeDeep<T[K]>
|
|
39
|
+
}>
|
|
40
|
+
: T
|
|
41
|
+
/**
|
|
42
|
+
* Helper type to convert arrays to readonly arrays. Content Tree arrays are
|
|
43
|
+
* mutable but the arrays we return from our API are expected to be readonly. We
|
|
44
|
+
* should explore which assumption is correct and match that in the other
|
|
45
|
+
* declaration file.
|
|
46
|
+
*/
|
|
47
|
+
type ReadonlyArrays<T> = {
|
|
48
|
+
[K in keyof T]: ReadonlyArray<T[K]>
|
|
49
|
+
}
|
|
50
|
+
// define conditional type separately so that it distributes the mapping over
|
|
51
|
+
// union types (e.g., to avoid removing the undefined from a union)
|
|
52
|
+
type ReadonlyArray<T> = T extends Array<infer Item> ? readonly Item[] : T
|
|
53
|
+
|
|
54
|
+
export type TeaserConcept = PartialToMaybe<
|
|
55
|
+
ReadonlyArrays<
|
|
56
|
+
SetOptional<
|
|
57
|
+
ContentTree.TeaserConcept,
|
|
58
|
+
'apiUrl' | 'directType' | 'predicate' | 'prefLabel' | 'type' | 'types'
|
|
59
|
+
>
|
|
60
|
+
>
|
|
61
|
+
>
|
|
62
|
+
|
|
63
|
+
export type Image = PartialToMaybe<
|
|
64
|
+
ReadonlyArrays<
|
|
65
|
+
LiteralToPrimitiveDeep<
|
|
66
|
+
SetOptional<ContentTree.Image, 'width' | 'height' | 'sourceSet'>
|
|
67
|
+
>
|
|
68
|
+
>
|
|
69
|
+
>
|
|
70
|
+
|
|
71
|
+
export type Teaser = PartialToMaybeDeep<
|
|
72
|
+
LiteralToPrimitiveDeep<
|
|
73
|
+
SetOptional<
|
|
74
|
+
SetFieldType<
|
|
75
|
+
SetFieldType<
|
|
76
|
+
ContentTree.Teaser,
|
|
77
|
+
'metaLink' | 'metaAltLink',
|
|
78
|
+
TeaserConcept
|
|
79
|
+
>,
|
|
80
|
+
'image',
|
|
81
|
+
Omit<Image, 'id' | 'format'>
|
|
82
|
+
>,
|
|
83
|
+
'type' | 'metaLink' | 'metaAltLink' | 'indicators' | 'image'
|
|
84
|
+
>
|
|
85
|
+
>
|
|
86
|
+
>
|
|
87
|
+
|
|
88
|
+
export type Recommended = PartialToMaybe<
|
|
89
|
+
ContentTreeWorkarounds.Recommended & {
|
|
90
|
+
teaser?: Teaser
|
|
91
|
+
}
|
|
92
|
+
>
|
|
93
|
+
|
|
94
|
+
export type ImageSetPicture = PartialToMaybe<
|
|
95
|
+
ReadonlyArrays<
|
|
96
|
+
SetOptional<
|
|
97
|
+
SetFieldType<
|
|
98
|
+
SetFieldType<ContentTree.ImageSetPicture, 'fallbackImage', Image>,
|
|
99
|
+
'images',
|
|
100
|
+
Image[]
|
|
101
|
+
>,
|
|
102
|
+
'caption' | 'credit'
|
|
103
|
+
>
|
|
104
|
+
>
|
|
105
|
+
>
|
|
106
|
+
|
|
107
|
+
export type ImageSet = PartialToMaybe<
|
|
108
|
+
SetOptional<
|
|
109
|
+
SetFieldType<
|
|
110
|
+
ContentTree.ImageSet | ContentTree.LayoutImage,
|
|
111
|
+
'picture',
|
|
112
|
+
ImageSetPicture
|
|
113
|
+
>,
|
|
114
|
+
'picture'
|
|
115
|
+
>
|
|
116
|
+
>
|
|
117
|
+
|
|
118
|
+
export type ScrollyImage = PartialToMaybe<
|
|
119
|
+
SetOptional<
|
|
120
|
+
SetFieldType<ContentTree.ScrollyImage, 'picture', ImageSetPicture>,
|
|
121
|
+
'picture'
|
|
122
|
+
>
|
|
123
|
+
>
|
|
124
|
+
|
|
125
|
+
type ClipFormat = 'standard-inline' | 'mobile'
|
|
126
|
+
interface ClipSource {
|
|
127
|
+
audioCodec?: string
|
|
128
|
+
binaryUrl: string
|
|
129
|
+
duration?: number
|
|
130
|
+
mediaType: string
|
|
131
|
+
pixelHeight?: number
|
|
132
|
+
pixelWidth?: number
|
|
133
|
+
videoCodec?: string
|
|
134
|
+
}
|
|
135
|
+
export type Clip = PartialToMaybeDeep<
|
|
136
|
+
ReadonlyArrays<{
|
|
137
|
+
format?: ClipFormat
|
|
138
|
+
dataSource: ClipSource[]
|
|
139
|
+
poster?: string
|
|
140
|
+
}>
|
|
141
|
+
>
|
|
142
|
+
interface ClipCaption {
|
|
143
|
+
mediaType?: string
|
|
144
|
+
url?: string
|
|
145
|
+
}
|
|
146
|
+
type ClipTranscript = TranscriptFragment
|
|
147
|
+
export type ClipAccessibility = PartialToMaybeDeep<
|
|
148
|
+
ReadonlyArrays<{
|
|
149
|
+
captions?: ClipCaption[]
|
|
150
|
+
transcript?: ClipTranscript
|
|
151
|
+
}>
|
|
152
|
+
>
|
|
153
|
+
interface ClipSetReferences {
|
|
154
|
+
id: string
|
|
155
|
+
type: string
|
|
156
|
+
accessibility?: ClipAccessibility
|
|
157
|
+
noAudio?: boolean
|
|
158
|
+
caption?: string
|
|
159
|
+
credits?: string
|
|
160
|
+
description?: string
|
|
161
|
+
displayTitle?: string
|
|
162
|
+
contentWarning?: string[]
|
|
163
|
+
source?: string
|
|
164
|
+
subtitle?: string
|
|
165
|
+
publishedDate?: string
|
|
166
|
+
clips?: Clip[]
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export type ClipSet = PartialToMaybeDeep<
|
|
170
|
+
ReadonlyArrays<
|
|
171
|
+
(ContentTreeWorkarounds.OldClip | ContentTreeWorkarounds.ClipSet) &
|
|
172
|
+
ClipSetReferences
|
|
173
|
+
>
|
|
174
|
+
>
|
|
175
|
+
|
|
176
|
+
export type RawImage = ContentTreeWorkarounds.RawImage & {
|
|
177
|
+
image: Image
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export type MainImageRaw = ContentTreeWorkarounds.MainImageRaw & {
|
|
181
|
+
image: Image
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export type Author = PartialToMaybe<
|
|
185
|
+
ContentTreeWorkarounds.Author & {
|
|
186
|
+
concept?: ConceptFragment
|
|
187
|
+
}
|
|
188
|
+
>
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import { ContentTree } from '@financial-times/content-tree'
|
|
3
|
+
import { ContentProps } from '../types'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}) => {
|
|
5
|
+
interface YoutubeVideoProps extends ContentProps<ContentTree.YoutubeVideo> {}
|
|
6
|
+
|
|
7
|
+
const YoutubeVideo: React.FC<YoutubeVideoProps> = ({ content: { url } }) => {
|
|
7
8
|
try {
|
|
8
9
|
const parsed = new URL(url)
|
|
9
10
|
const id = parsed.searchParams.get('v')
|
package/src/context.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import { createContext } from 'react'
|
|
1
|
+
import { createContext, PropsWithChildren } from 'react'
|
|
2
|
+
|
|
3
|
+
import type { FallbackImageProps, ImageSetProps } from './components/ImageSet'
|
|
4
|
+
import type { PictureProps } from './components/Topper/Picture'
|
|
5
|
+
|
|
6
|
+
export type PresentationOverrideTypes = {
|
|
7
|
+
FallbackImage: React.FC<PropsWithChildren<FallbackImageProps>>
|
|
8
|
+
ImageSet: React.FC<PropsWithChildren<ImageSetProps>>
|
|
9
|
+
Picture: React.FC<PropsWithChildren<PictureProps>>
|
|
10
|
+
}
|
|
2
11
|
|
|
3
12
|
// HACK:20240618:IM we're still not at the point where all components behave
|
|
4
13
|
// correctly across all consumers. this context allows consumers to
|
|
@@ -9,3 +18,15 @@ export interface PresentationFlags {
|
|
|
9
18
|
reduceFullBleedImages?: boolean
|
|
10
19
|
}
|
|
11
20
|
export const PresentationFlagsContext = createContext<PresentationFlags>({})
|
|
21
|
+
|
|
22
|
+
// COMPLEX:20240815:RB similarly to PresentationFlags, some consumers need
|
|
23
|
+
// to customise the presentation and behaviour of some components. This should be
|
|
24
|
+
// replaced by more transparent support in the consumer with the aim of removing
|
|
25
|
+
// this context.
|
|
26
|
+
export interface PresentationOverrides {
|
|
27
|
+
FallbackImage?: PresentationOverrideTypes['FallbackImage']
|
|
28
|
+
ImageSet?: PresentationOverrideTypes['ImageSet']
|
|
29
|
+
Picture?: PresentationOverrideTypes['Picture']
|
|
30
|
+
}
|
|
31
|
+
export const PresentationOverridesContext =
|
|
32
|
+
createContext<PresentationOverrides>({})
|
package/src/index.ts
CHANGED