@financial-times/cp-content-pipeline-ui 9.12.1 → 9.14.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 (64) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/lib/components/ContentLayout/index.d.ts +25 -0
  3. package/lib/components/ContentLayout/index.js +34 -0
  4. package/lib/components/ContentLayout/index.js.map +1 -0
  5. package/lib/components/ContentLayout/test/index.spec.d.ts +1 -0
  6. package/lib/components/ContentLayout/test/index.spec.js +57 -0
  7. package/lib/components/ContentLayout/test/index.spec.js.map +1 -0
  8. package/lib/components/ContentLayout/test/snapshot.spec.d.ts +1 -0
  9. package/lib/components/ContentLayout/test/snapshot.spec.js +33 -0
  10. package/lib/components/ContentLayout/test/snapshot.spec.js.map +1 -0
  11. package/lib/components/RichText/index.js +4 -0
  12. package/lib/components/RichText/index.js.map +1 -1
  13. package/lib/components/content-tree/Clip/components/index.d.ts +0 -2
  14. package/lib/components/content-tree/Clip/components/index.js +1 -5
  15. package/lib/components/content-tree/Clip/components/index.js.map +1 -1
  16. package/lib/components/content-tree/Clip/template/component.d.ts +1 -1
  17. package/lib/components/content-tree/Clip/template/component.js +3 -3
  18. package/lib/components/content-tree/Clip/template/component.js.map +1 -1
  19. package/lib/components/content-tree/Clip/test/snapshot.spec.js +26 -0
  20. package/lib/components/content-tree/Clip/test/snapshot.spec.js.map +1 -1
  21. package/lib/components/content-tree/Timeline/TimelineEvent.d.ts +7 -0
  22. package/lib/components/content-tree/Timeline/TimelineEvent.js +17 -0
  23. package/lib/components/content-tree/Timeline/TimelineEvent.js.map +1 -0
  24. package/lib/components/content-tree/Timeline/index.d.ts +6 -0
  25. package/lib/components/content-tree/Timeline/index.js +17 -0
  26. package/lib/components/content-tree/Timeline/index.js.map +1 -0
  27. package/lib/components/content-tree/Timeline/test/TimelineEvent.spec.d.ts +1 -0
  28. package/lib/components/content-tree/Timeline/test/TimelineEvent.spec.js +48 -0
  29. package/lib/components/content-tree/Timeline/test/TimelineEvent.spec.js.map +1 -0
  30. package/lib/components/content-tree/Timeline/test/snapshot.spec.d.ts +1 -0
  31. package/lib/components/content-tree/Timeline/test/snapshot.spec.js +17 -0
  32. package/lib/components/content-tree/Timeline/test/snapshot.spec.js.map +1 -0
  33. package/lib/index.d.ts +2 -0
  34. package/lib/index.js +5 -1
  35. package/lib/index.js.map +1 -1
  36. package/lib/main.scss +1 -0
  37. package/package.json +6 -6
  38. package/src/components/ContentLayout/index.tsx +87 -0
  39. package/src/components/ContentLayout/test/__snapshots__/index.spec.tsx.snap +96 -0
  40. package/src/components/ContentLayout/test/__snapshots__/snapshot.spec.tsx.snap +80 -0
  41. package/src/components/ContentLayout/test/index.spec.tsx +81 -0
  42. package/src/components/ContentLayout/test/snapshot.spec.tsx +47 -0
  43. package/src/components/RichText/index.tsx +4 -0
  44. package/src/components/content-tree/Clip/components/index.tsx +0 -2
  45. package/src/components/content-tree/Clip/template/component.tsx +23 -24
  46. package/src/components/content-tree/Clip/test/__snapshots__/snapshot.spec.tsx.snap +277 -0
  47. package/src/components/content-tree/Clip/test/snapshot.spec.tsx +40 -0
  48. package/src/components/content-tree/Timeline/TimelineEvent.tsx +25 -0
  49. package/src/components/content-tree/Timeline/client/main.scss +55 -0
  50. package/src/components/content-tree/Timeline/index.tsx +19 -0
  51. package/src/components/content-tree/Timeline/test/TimelineEvent.spec.tsx +66 -0
  52. package/src/components/content-tree/Timeline/test/__snapshots__/TimelineEvent.spec.tsx.snap +94 -0
  53. package/src/components/content-tree/Timeline/test/__snapshots__/snapshot.spec.tsx.snap +20 -0
  54. package/src/components/content-tree/Timeline/test/snapshot.spec.tsx +15 -0
  55. package/src/index.ts +2 -0
  56. package/tsconfig.tsbuildinfo +1 -1
  57. package/lib/components/content-tree/Clip/components/Container.d.ts +0 -6
  58. package/lib/components/content-tree/Clip/components/Container.js +0 -13
  59. package/lib/components/content-tree/Clip/components/Container.js.map +0 -1
  60. package/lib/components/content-tree/Clip/components/ContentLayout.d.ts +0 -6
  61. package/lib/components/content-tree/Clip/components/ContentLayout.js +0 -13
  62. package/lib/components/content-tree/Clip/components/ContentLayout.js.map +0 -1
  63. package/src/components/content-tree/Clip/components/Container.tsx +0 -32
  64. package/src/components/content-tree/Clip/components/ContentLayout.tsx +0 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/cp-content-pipeline-ui",
3
- "version": "9.12.1",
3
+ "version": "9.14.0",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -15,10 +15,11 @@
15
15
  "devDependencies": {
16
16
  "@babel/preset-env": "^7.22.5",
17
17
  "@babel/preset-react": "^7.22.5",
18
- "@financial-times/content-tree": "github:financial-times/content-tree#3f77ec4",
19
- "@financial-times/cp-content-pipeline-client": "^4.20.1",
20
- "@financial-times/cp-content-pipeline-schema": "^3.20.0",
21
- "@financial-times/cp-content-pipeline-styles": "^4.5.0",
18
+ "@dotcom-tool-kit/npm": "^5.0.0",
19
+ "@financial-times/content-tree": "github:financial-times/content-tree#aa5cf1",
20
+ "@financial-times/cp-content-pipeline-client": "^4.21.0",
21
+ "@financial-times/cp-content-pipeline-schema": "^3.22.0",
22
+ "@financial-times/cp-content-pipeline-styles": "^4.6.0",
22
23
  "@financial-times/n-scrollytelling-image": "^1.1.0",
23
24
  "@financial-times/o-grid": "^6.1.8",
24
25
  "@financial-times/o-share": "^11.0.0",
@@ -50,7 +51,6 @@
50
51
  "webpack": "^5.88.1"
51
52
  },
52
53
  "dependencies": {
53
- "@dotcom-tool-kit/npm": "^5.0.0",
54
54
  "@financial-times/ft-date-format": "^2.1.0",
55
55
  "@financial-times/o-expander": "^7.0.1",
56
56
  "@financial-times/o-labels": "^7.1.2",
@@ -0,0 +1,87 @@
1
+ import React from 'react'
2
+ import classnames from 'classnames'
3
+
4
+ /**
5
+ * The available layout widths for the Layout component.
6
+ *
7
+ * Available options:
8
+ * - `in-line` - [Default] 7 columns (700 pixels)
9
+ * - `full-width` - Legacy alias for `in-line`
10
+ * - `mid-grid` - 9 columns (900 pixels)
11
+ * - `full-grid` - 12 columns of the grid (1200 pixels)
12
+ *
13
+ * @todo Existing layouts not currently supported by this component. To be implemented in the future:
14
+ * @todo `inset-left` - 5 columns (up to 500 pixels), medium screens and above: 3 columns (300 pixels), float left
15
+ * @todo `inset-right` - 5 columns (up to 500 pixels), medium screens and above: 3 columns (300 pixels), float right
16
+ * @todo `full-bleed` - Edge to edge of the viewport, no padding
17
+ * @todo The terminology in Spark doesn't align with our terminology. For instance, Editorial uses `full-width` that we map to `full-grid`. We should likely move towards an ubiquitous language prioritising the Editorial one.
18
+ */
19
+ type LayoutWidth = 'in-line' | 'mid-grid' | 'full-grid'
20
+
21
+ interface ContentLayoutProps {
22
+ dataLayout?: LayoutWidth
23
+ dataComponent?: string
24
+ className?: string
25
+ children: React.ReactNode
26
+ }
27
+
28
+ /*
29
+ * @description OverflowingLayout component prevent clashes with ads on the RHR (Righthand-rail) and share bar
30
+ * via the usage of data-layout-width='full-grid'
31
+ * See related code here: https://github.com/Financial-Times/next-article/blob/5766d82de6125ffc99fa6e3bc132311b3434f8f0/client/components/share/main.js#L8
32
+ */
33
+ const OverflowingLayout: React.FC<{ children: React.ReactNode }> = ({
34
+ children,
35
+ }) => (
36
+ <div className="n-content-layout" data-layout-width="full-grid">
37
+ {children}
38
+ </div>
39
+ )
40
+
41
+ /*
42
+ * @description ContentLayout component is used to fulfill the legacy usage of n-layout.
43
+ * @param dataLayout - The layout type of the content (e.g., 'full-grid', 'mid-grid', etc.).
44
+ * @param dataComponent - The component type used as data-component attribute for client-side targeting.
45
+ */
46
+ export const ContentLayout: React.FC<
47
+ React.PropsWithChildren<ContentLayoutProps>
48
+ > = ({ dataLayout = 'in-line', children, dataComponent, className = '' }) => {
49
+ const ContainerClassNames = classnames(
50
+ 'n-content-layout__container',
51
+ className
52
+ )
53
+
54
+ if (dataLayout === 'full-grid') {
55
+ return (
56
+ <OverflowingLayout>
57
+ <div className={ContainerClassNames} data-component={dataComponent}>
58
+ {children}
59
+ </div>
60
+ </OverflowingLayout>
61
+ )
62
+ }
63
+
64
+ if (dataLayout === 'mid-grid') {
65
+ return (
66
+ <OverflowingLayout>
67
+ <div className={ContainerClassNames} data-component={dataComponent}>
68
+ <div
69
+ data-o-grid-colspan="12 S12 M12 L10 XL10"
70
+ className="n-content-layout__container--mid-grid"
71
+ >
72
+ {children}
73
+ </div>
74
+ </div>
75
+ </OverflowingLayout>
76
+ )
77
+ }
78
+
79
+ return (
80
+ <div
81
+ className="n-content-layout__container--in-line"
82
+ data-component={dataComponent}
83
+ >
84
+ {children}
85
+ </div>
86
+ )
87
+ }
@@ -0,0 +1,96 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`ContentLayout Edge cases handles conditional children 1`] = `
4
+ <div
5
+ className="n-content-layout"
6
+ data-layout-width="full-grid"
7
+ >
8
+ <div
9
+ className="n-content-layout__container"
10
+ data-component="clip-set"
11
+ >
12
+ <div>
13
+ <div>
14
+ Conditional content
15
+ </div>
16
+ </div>
17
+ <div />
18
+ </div>
19
+ </div>
20
+ `;
21
+
22
+ exports[`ContentLayout Edge cases handles empty children 1`] = `
23
+ <div
24
+ className="n-content-layout"
25
+ data-layout-width="full-grid"
26
+ >
27
+ <div
28
+ className="n-content-layout__container"
29
+ data-component="clip-set"
30
+ >
31
+ <div />
32
+ </div>
33
+ </div>
34
+ `;
35
+
36
+ exports[`ContentLayout Edge cases handles null children 1`] = `
37
+ <div
38
+ className="n-content-layout"
39
+ data-layout-width="full-grid"
40
+ >
41
+ <div
42
+ className="n-content-layout__container"
43
+ data-component="clip-set"
44
+ >
45
+ <div />
46
+ </div>
47
+ </div>
48
+ `;
49
+
50
+ exports[`ContentLayout renders full-grid layout with container correctly 1`] = `
51
+ <div
52
+ className="n-content-layout"
53
+ data-layout-width="full-grid"
54
+ >
55
+ <div
56
+ className="n-content-layout__container"
57
+ data-component="clip-set"
58
+ >
59
+ <div>
60
+ Test content
61
+ </div>
62
+ </div>
63
+ </div>
64
+ `;
65
+
66
+ exports[`ContentLayout renders in-line layout with container correctly 1`] = `
67
+ <div
68
+ className="n-content-layout__container--in-line"
69
+ data-component="clip-set"
70
+ >
71
+ <div>
72
+ Test content
73
+ </div>
74
+ </div>
75
+ `;
76
+
77
+ exports[`ContentLayout renders mid-grid layout with container correctly 1`] = `
78
+ <div
79
+ className="n-content-layout"
80
+ data-layout-width="full-grid"
81
+ >
82
+ <div
83
+ className="n-content-layout__container"
84
+ data-component="clip-set"
85
+ >
86
+ <div
87
+ className="n-content-layout__container--mid-grid"
88
+ data-o-grid-colspan="12 S12 M12 L10 XL10"
89
+ >
90
+ <div>
91
+ Test content
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ `;
@@ -0,0 +1,80 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`ContentLayout renders treating no dataLayout as default inline) 1`] = `
4
+ <div>
5
+ <div
6
+ class="n-content-layout__container--in-line"
7
+ >
8
+ <div>
9
+ Empty layout content
10
+ </div>
11
+ </div>
12
+ </div>
13
+ `;
14
+
15
+ exports[`ContentLayout renders with any accepted layout 1`] = `
16
+ <div>
17
+ <div
18
+ class="n-content-layout"
19
+ data-layout-width="full-grid"
20
+ >
21
+ <div
22
+ class="n-content-layout__container"
23
+ >
24
+ <div
25
+ class="n-content-layout__container--mid-grid"
26
+ data-o-grid-colspan="12 S12 M12 L10 XL10"
27
+ >
28
+ <span>
29
+ Inline content
30
+ </span>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ </div>
35
+ `;
36
+
37
+ exports[`ContentLayout renders with full-grid layout 1`] = `
38
+ <div>
39
+ <div
40
+ class="n-content-layout"
41
+ data-layout-width="full-grid"
42
+ >
43
+ <div
44
+ class="n-content-layout__container"
45
+ >
46
+ <div>
47
+ Test content
48
+ </div>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ `;
53
+
54
+ exports[`ContentLayout renders with multiple children 1`] = `
55
+ <div>
56
+ <div
57
+ class="n-content-layout"
58
+ data-layout-width="full-grid"
59
+ >
60
+ <div
61
+ class="n-content-layout__container"
62
+ >
63
+ <div
64
+ class="n-content-layout__container--mid-grid"
65
+ data-o-grid-colspan="12 S12 M12 L10 XL10"
66
+ >
67
+ <h1>
68
+ Title
69
+ </h1>
70
+ <p>
71
+ Paragraph content
72
+ </p>
73
+ <div>
74
+ Additional content
75
+ </div>
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ `;
@@ -0,0 +1,81 @@
1
+ import React from 'react'
2
+ import renderer from 'react-test-renderer'
3
+ import { ContentLayout } from '../index'
4
+
5
+ describe('ContentLayout', () => {
6
+ it('renders full-grid layout with container correctly', () => {
7
+ const tree = renderer
8
+ .create(
9
+ <ContentLayout dataLayout="full-grid" dataComponent="clip-set">
10
+ <div>Test content</div>
11
+ </ContentLayout>
12
+ )
13
+ .toJSON()
14
+
15
+ expect(tree).toMatchSnapshot()
16
+ })
17
+
18
+ it('renders mid-grid layout with container correctly', () => {
19
+ const tree = renderer
20
+ .create(
21
+ <ContentLayout dataLayout="mid-grid" dataComponent="clip-set">
22
+ <div>Test content</div>
23
+ </ContentLayout>
24
+ )
25
+ .toJSON()
26
+
27
+ expect(tree).toMatchSnapshot()
28
+ })
29
+
30
+ it('renders in-line layout with container correctly', () => {
31
+ const tree = renderer
32
+ .create(
33
+ <ContentLayout dataLayout="in-line" dataComponent="clip-set">
34
+ <div>Test content</div>
35
+ </ContentLayout>
36
+ )
37
+ .toJSON()
38
+
39
+ expect(tree).toMatchSnapshot()
40
+ })
41
+
42
+ describe('Edge cases', () => {
43
+ it('handles empty children', () => {
44
+ const tree = renderer
45
+ .create(
46
+ <ContentLayout dataLayout="full-grid" dataComponent="clip-set">
47
+ <div></div>
48
+ </ContentLayout>
49
+ )
50
+ .toJSON()
51
+
52
+ expect(tree).toMatchSnapshot()
53
+ })
54
+
55
+ it('handles null children', () => {
56
+ const tree = renderer
57
+ .create(
58
+ <ContentLayout dataLayout="full-grid" dataComponent="clip-set">
59
+ <div>{null}</div>
60
+ </ContentLayout>
61
+ )
62
+ .toJSON()
63
+
64
+ expect(tree).toMatchSnapshot()
65
+ })
66
+
67
+ it('handles conditional children', () => {
68
+ const showContent = true
69
+ const tree = renderer
70
+ .create(
71
+ <ContentLayout dataLayout="full-grid" dataComponent="clip-set">
72
+ <div>{showContent && <div>Conditional content</div>}</div>
73
+ <div>{!showContent && <div>Alternative content</div>}</div>
74
+ </ContentLayout>
75
+ )
76
+ .toJSON()
77
+
78
+ expect(tree).toMatchSnapshot()
79
+ })
80
+ })
81
+ })
@@ -0,0 +1,47 @@
1
+ import React from 'react'
2
+ import { render } from '@testing-library/react'
3
+ import { ContentLayout } from '../index'
4
+
5
+ describe('ContentLayout', () => {
6
+ it('renders with full-grid layout', () => {
7
+ const { container } = render(
8
+ <ContentLayout dataLayout="full-grid">
9
+ <div>Test content</div>
10
+ </ContentLayout>
11
+ )
12
+
13
+ expect(container).toMatchSnapshot()
14
+ })
15
+
16
+ it('renders with any accepted layout', () => {
17
+ const { container } = render(
18
+ <ContentLayout dataLayout="mid-grid">
19
+ <span>Inline content</span>
20
+ </ContentLayout>
21
+ )
22
+
23
+ expect(container).toMatchSnapshot()
24
+ })
25
+
26
+ it('renders with multiple children', () => {
27
+ const { container } = render(
28
+ <ContentLayout dataLayout="mid-grid">
29
+ <h1>Title</h1>
30
+ <p>Paragraph content</p>
31
+ <div>Additional content</div>
32
+ </ContentLayout>
33
+ )
34
+
35
+ expect(container).toMatchSnapshot()
36
+ })
37
+
38
+ it('renders treating no dataLayout as default inline)', () => {
39
+ const { container } = render(
40
+ <ContentLayout>
41
+ <div>Empty layout content</div>
42
+ </ContentLayout>
43
+ )
44
+
45
+ expect(container).toMatchSnapshot()
46
+ })
47
+ })
@@ -30,6 +30,8 @@ import {
30
30
  import { ScrollyImage } from '../content-tree/Scrollytelling/ScrollyImage'
31
31
  import YoutubeVideo from '../content-tree/YoutubeVideo'
32
32
  import CustomCodeComponent from '../content-tree/CustomCodeComponent'
33
+ import Timeline from '../content-tree/Timeline'
34
+ import TimelineEvent from '../content-tree/Timeline/TimelineEvent'
33
35
 
34
36
  import {
35
37
  List,
@@ -144,6 +146,8 @@ const componentMap: RichTextComponentMapRecord = {
144
146
  tweet: Tweet,
145
147
  video: Video,
146
148
  'youtube-video': YoutubeVideo,
149
+ timeline: Timeline,
150
+ 'timeline-event': TimelineEvent,
147
151
  }
148
152
 
149
153
  function isParentNode(
@@ -3,6 +3,4 @@ export { Caption } from './Caption'
3
3
  export { VideoInfoBox } from './VideoInfoBox'
4
4
  export { ClosedCaptions } from './ClosedCaptions'
5
5
  export { VideoDescription } from './VideoDescription'
6
- export { Container } from './Container'
7
- export { ContentLayout } from './ContentLayout'
8
6
  export { ClipTag } from './ClipTag'
@@ -1,6 +1,7 @@
1
1
  import React from 'react'
2
- import { ClipTag, ContentLayout, Container } from '../components'
2
+ import { ClipTag } from '../components'
3
3
  import { ContentProps } from '../../../types'
4
+ import { ContentLayout } from '../../../ContentLayout'
4
5
  import type * as ComponentWorkarounds from '../../Workarounds'
5
6
 
6
7
  type Preset = 'full-player' | 'thumbnail'
@@ -21,7 +22,7 @@ export interface ClipPropsOld {
21
22
  clips?: ComponentWorkarounds.Clip[]
22
23
  poster?: string
23
24
  posterAlt?: string
24
- dataLayout: string
25
+ dataLayout: 'in-line' | 'mid-grid' | 'full-grid'
25
26
  description?: string
26
27
  muted?: boolean
27
28
  caption?: string
@@ -90,28 +91,26 @@ const ClipComponent: React.FC<ClipProps> = ({
90
91
  }
91
92
 
92
93
  return (
93
- <ContentLayout dataLayout={content.dataLayout}>
94
- <Container dataLayout={content.dataLayout}>
95
- <ClipTag
96
- id={id}
97
- dataLayout={content.dataLayout}
98
- description={content.description ?? ''}
99
- poster={posterAttribute}
100
- autoplay={content.autoplay}
101
- noAudio={noAudio}
102
- loop={content.loop}
103
- muted={content.muted}
104
- clip={clip}
105
- credits={content.credits ?? ''}
106
- caption={content.caption ?? ''}
107
- systemTitle={systemTitle}
108
- accessibility={accessibility}
109
- preload={preload}
110
- dataTrackable="next-article-cp-clip"
111
- maxClipWidth={maxClipWidth}
112
- fragmentIdentifier={content.fragmentIdentifier ?? ''}
113
- />
114
- </Container>
94
+ <ContentLayout dataLayout={content.dataLayout} dataComponent="clip-set">
95
+ <ClipTag
96
+ id={id}
97
+ dataLayout={content.dataLayout}
98
+ description={content.description ?? ''}
99
+ poster={posterAttribute}
100
+ autoplay={content.autoplay}
101
+ noAudio={noAudio}
102
+ loop={content.loop}
103
+ muted={content.muted}
104
+ clip={clip}
105
+ credits={content.credits ?? ''}
106
+ caption={content.caption ?? ''}
107
+ systemTitle={systemTitle}
108
+ accessibility={accessibility}
109
+ preload={preload}
110
+ dataTrackable="next-article-cp-clip"
111
+ maxClipWidth={maxClipWidth}
112
+ fragmentIdentifier={content.fragmentIdentifier ?? ''}
113
+ />
115
114
  </ContentLayout>
116
115
  )
117
116
  }