@financial-times/cp-content-pipeline-ui 9.8.0 → 9.10.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/lib/components/RichText/BasicComponents.js +1 -1
  3. package/lib/components/RichText/BasicComponents.js.map +1 -1
  4. package/lib/components/RichText/index.js +2 -0
  5. package/lib/components/RichText/index.js.map +1 -1
  6. package/lib/components/Topper/{Columnist.d.ts → Columnists.d.ts} +3 -3
  7. package/lib/components/Topper/Columnists.js +22 -0
  8. package/lib/components/Topper/Columnists.js.map +1 -0
  9. package/lib/components/Topper/Headline.js +1 -1
  10. package/lib/components/Topper/Headline.js.map +1 -1
  11. package/lib/components/Topper/Tags.d.ts +1 -1
  12. package/lib/components/Topper/Tags.js +2 -2
  13. package/lib/components/Topper/Tags.js.map +1 -1
  14. package/lib/components/Topper/index.js +17 -8
  15. package/lib/components/Topper/index.js.map +1 -1
  16. package/lib/components/content-tree/RecommendedList/index.d.ts +7 -0
  17. package/lib/components/content-tree/RecommendedList/index.js +24 -0
  18. package/lib/components/content-tree/RecommendedList/index.js.map +1 -0
  19. package/lib/components/content-tree/Workarounds.d.ts +4 -0
  20. package/lib/index.d.ts +1 -0
  21. package/lib/index.js +3 -1
  22. package/lib/index.js.map +1 -1
  23. package/package.json +6 -6
  24. package/src/components/RichText/BasicComponents.tsx +1 -0
  25. package/src/components/RichText/index.tsx +2 -0
  26. package/src/components/Topper/Columnists.tsx +50 -0
  27. package/src/components/Topper/Headline.tsx +1 -1
  28. package/src/components/Topper/Tags.tsx +3 -3
  29. package/src/components/Topper/client/main.scss +5 -1
  30. package/src/components/Topper/index.tsx +21 -10
  31. package/src/components/content-tree/RecommendedList/index.tsx +53 -0
  32. package/src/components/content-tree/Workarounds.ts +11 -1
  33. package/src/index.ts +1 -0
  34. package/tsconfig.tsbuildinfo +1 -1
  35. package/lib/components/Topper/Columnist.js +0 -18
  36. package/lib/components/Topper/Columnist.js.map +0 -1
  37. package/src/components/Topper/Columnist.tsx +0 -40
@@ -5,7 +5,7 @@ import Headline from './Headline'
5
5
  import Intro from './Intro'
6
6
  import Picture from './Picture'
7
7
  import Headshot from '../Headshot'
8
- import Columnist from './Columnist'
8
+ import Columnists from './Columnists'
9
9
  import LiveBlogIndicator from './LiveBlogIndicator'
10
10
  import { FollowButtonSlot } from './FollowButtonSlot'
11
11
  import Flourish from '../content-tree/Flourish'
@@ -65,7 +65,18 @@ const Topper: React.FC<TopperProps> = ({
65
65
  const hasLayoutWidth = 'layoutWidth' in topper && topper.layoutWidth
66
66
  const hasDesign = 'design' in topper && topper.design
67
67
  const hasHeadshot = 'headshot' in topper && topper.headshot
68
- const hasColumnist = 'columnist' in topper && !!topper.columnist
68
+ const hasColumnists =
69
+ 'columnists' in topper &&
70
+ !!topper.columnists &&
71
+ topper.columnists.length > 0
72
+ const legacyHasColumnist = 'columnist' in topper && !!topper.columnist // @deprecated Replaced with usage of `columinists`
73
+ const hasOneColumnist =
74
+ (hasColumnists && topper.columnists.length === 1) || legacyHasColumnist
75
+ const oneColumnist = hasOneColumnist
76
+ ? hasColumnists
77
+ ? topper.columnists[0]
78
+ : topper.columnist
79
+ : null
69
80
  const sponsorName = 'clientName' in content && content.clientName
70
81
  const disclaimerType = isArticle ? content.disclaimerType : undefined
71
82
 
@@ -81,8 +92,8 @@ const Topper: React.FC<TopperProps> = ({
81
92
  modifiers.push(topper.layoutWidth === 'full-grid' ? 'full-grid' : 'in-line')
82
93
 
83
94
  const headshotAltText =
84
- isOpinion && topper.columnist
85
- ? `Headshot for ${topper.columnist.prefLabel}`
95
+ isOpinion && oneColumnist
96
+ ? `Headshot for ${oneColumnist.prefLabel}`
86
97
  : isPodcast
87
98
  ? 'Cover photo for podcast'
88
99
  : ''
@@ -161,7 +172,7 @@ const Topper: React.FC<TopperProps> = ({
161
172
  ('genreConcept' in topper && topper.genreConcept) || undefined
162
173
  }
163
174
  headshot={hasHeadshot || undefined}
164
- hasColumnist={hasColumnist}
175
+ hasColumnists={hasColumnists}
165
176
  followButtonVariant={topper.followButtonVariant}
166
177
  FollowButtonSlot={followButtonSlot}
167
178
  />
@@ -197,19 +208,19 @@ const Topper: React.FC<TopperProps> = ({
197
208
  source={topper.intro.source}
198
209
  />
199
210
  )}
200
- {hasHeadshot && !isAudio && isOpinion && topper.columnist && (
211
+ {hasHeadshot && !isAudio && isOpinion && oneColumnist && (
201
212
  <div className="o-topper__headshot">
202
213
  <Headshot
203
214
  headshot={topper.headshot}
204
- prefLabel={topper.columnist.prefLabel}
215
+ prefLabel={oneColumnist.prefLabel}
205
216
  className={'o-topper__headshot-image'}
206
217
  altText={headshotAltText}
207
218
  />
208
219
  </div>
209
220
  )}
210
- {isOpinion && topper.columnist && (
211
- <Columnist
212
- authorConcept={topper.columnist}
221
+ {isOpinion && hasColumnists && (
222
+ <Columnists
223
+ authorConcepts={[...topper.columnists]}
213
224
  followButtonVariant={topper.followButtonVariant}
214
225
  followButtonSlot={followButtonSlot}
215
226
  />
@@ -0,0 +1,53 @@
1
+ import React from 'react'
2
+
3
+ //HACK: worked around missing Teaser type by declaring a module x-teaser.d.ts
4
+ import { presets, Teaser } from '@financial-times/x-teaser'
5
+ import { ContentProps } from '../../types'
6
+ import type * as ComponentWorkarounds from '../Workarounds'
7
+
8
+ // Renders a Recommended teaser component
9
+ // `<Teaser>` is imported from x-dash
10
+ // https://github.com/Financial-Times/x-dash/tree/main/components/x-teaser)
11
+
12
+ interface RecommendedListProps
13
+ extends ContentProps<ComponentWorkarounds.RecommendedList> {}
14
+
15
+ const RecommendedList: React.FC<RecommendedListProps> = ({
16
+ content: { heading, teasers },
17
+ }) => {
18
+ if (!teasers || teasers.length === 0) {
19
+ return null
20
+ }
21
+
22
+ return (
23
+ <aside
24
+ aria-labelledby="aside-label"
25
+ className="n-content-recommended-list"
26
+ data-component="recommended-list"
27
+ >
28
+ <p className="n-content-recommended-list__title o3-type-title-lg">
29
+ {heading}
30
+ </p>
31
+ <div className="n-content-recommended-list__teasers">
32
+ {teasers.map((teaser, index) => (
33
+ <Teaser
34
+ key={teaser?.id || index}
35
+ modifiers={['stacked']}
36
+ {...presets.SmallHeavy}
37
+ {...teaser}
38
+ metaPrefixText={'By'}
39
+ metaLink={{
40
+ prefLabel: teaser?.clientName,
41
+ // clientName is a free form text field. Currently, there is no way to map it to an organisation
42
+ // from the annotations. For now, we're linking it to the article url.
43
+ // A property like isSponsoredBy could be added in future to support this properly.
44
+ url: teaser?.url,
45
+ }}
46
+ />
47
+ ))}
48
+ </div>
49
+ </aside>
50
+ )
51
+ }
52
+
53
+ export default RecommendedList
@@ -87,11 +87,21 @@ export type Teaser = PartialToMaybeDeep<
87
87
  >
88
88
 
89
89
  export type Recommended = PartialToMaybe<
90
- ContentTreeWorkarounds.Recommended & {
90
+ ContentTreeWorkarounds.Recommended & {
91
91
  teaser?: Teaser
92
92
  }
93
93
  >
94
94
 
95
+ export type RecommendedList = PartialToMaybe<
96
+ ReadonlyArrays<
97
+ Omit<ContentTree.RecommendedList, 'children'> & {
98
+ teasers?: (Teaser | null)[]
99
+ children: Recommended[]
100
+ }
101
+ >
102
+
103
+ >
104
+
95
105
  export type ImageSetPicture = PartialToMaybe<
96
106
  ReadonlyArrays<
97
107
  SetOptional<
package/src/index.ts CHANGED
@@ -24,6 +24,7 @@ export {
24
24
  export { default as Paragraph } from './components/content-tree/Paragraph'
25
25
  export { default as Pullquote } from './components/content-tree/Pullquote/index'
26
26
  export { default as Recommended } from './components/content-tree/Recommended'
27
+ export { default as RecommendedList } from './components/content-tree/RecommendedList'
27
28
  export { ScrollyBlock } from './components/content-tree/Scrollytelling'
28
29
  export { default as Table } from './components/content-tree/Table'
29
30
  export { default as Tweet } from './components/content-tree/Tweet'