@financial-times/cp-content-pipeline-ui 9.10.0 → 9.12.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 +35 -0
- package/lib/components/content-tree/Clip/components/ClipTag.d.ts +2 -0
- package/lib/components/content-tree/Clip/components/ClipTag.js +33 -4
- package/lib/components/content-tree/Clip/components/ClipTag.js.map +1 -1
- package/lib/components/content-tree/Clip/template/component.d.ts +1 -0
- package/lib/components/content-tree/Clip/template/component.js +3 -3
- package/lib/components/content-tree/Clip/template/component.js.map +1 -1
- package/lib/components/content-tree/RecommendedList/index.js +6 -5
- package/lib/components/content-tree/RecommendedList/index.js.map +1 -1
- package/lib/components/content-tree/Workarounds.d.ts +4 -0
- package/package.json +3 -3
- package/src/components/content-tree/Clip/components/ClipTag.tsx +61 -11
- package/src/components/content-tree/Clip/template/component.tsx +5 -0
- package/src/components/content-tree/Clip/test/__snapshots__/snapshot.spec.tsx.snap +8 -0
- package/src/components/content-tree/RecommendedList/index.tsx +9 -10
- package/src/components/content-tree/Workarounds.ts +8 -5
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -574,6 +574,41 @@
|
|
|
574
574
|
* @financial-times/cp-content-pipeline-client bumped from ^3.7.2 to ^3.7.3
|
|
575
575
|
* @financial-times/cp-content-pipeline-schema bumped from ^2.10.1 to ^2.10.2
|
|
576
576
|
|
|
577
|
+
## [9.12.0](https://github.com/Financial-Times/cp-content-pipeline/compare/cp-content-pipeline-ui-v9.11.0...cp-content-pipeline-ui-v9.12.0) (2025-11-21)
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
### Features
|
|
581
|
+
|
|
582
|
+
* anchor links in clips ([f753082](https://github.com/Financial-Times/cp-content-pipeline/commit/f753082b61b1e5aa989cd1db901546eeee71fe83))
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
### Bug Fixes
|
|
586
|
+
|
|
587
|
+
* avoid fetching teaser data from RecommendedList resolver ([54bc53f](https://github.com/Financial-Times/cp-content-pipeline/commit/54bc53f9a40446667a9f93b85d710cd6a943157d))
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
### Dependencies
|
|
591
|
+
|
|
592
|
+
* The following workspace dependencies were updated
|
|
593
|
+
* devDependencies
|
|
594
|
+
* @financial-times/cp-content-pipeline-client bumped from ^4.20.0 to ^4.20.1
|
|
595
|
+
* @financial-times/cp-content-pipeline-schema bumped from ^3.19.0 to ^3.20.0
|
|
596
|
+
|
|
597
|
+
## [9.11.0](https://github.com/Financial-Times/cp-content-pipeline/compare/cp-content-pipeline-ui-v9.10.0...cp-content-pipeline-ui-v9.11.0) (2025-11-11)
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
### Features
|
|
601
|
+
|
|
602
|
+
* ci-3111 media queries on clips sources ([856bd45](https://github.com/Financial-Times/cp-content-pipeline/commit/856bd457ba6dc56f292c6a31e0edfe3f29b4a904))
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
### Dependencies
|
|
606
|
+
|
|
607
|
+
* The following workspace dependencies were updated
|
|
608
|
+
* devDependencies
|
|
609
|
+
* @financial-times/cp-content-pipeline-client bumped from ^4.19.0 to ^4.20.0
|
|
610
|
+
* @financial-times/cp-content-pipeline-schema bumped from ^3.18.0 to ^3.19.0
|
|
611
|
+
|
|
577
612
|
## [9.10.0](https://github.com/Financial-Times/cp-content-pipeline/compare/cp-content-pipeline-ui-v9.9.0...cp-content-pipeline-ui-v9.10.0) (2025-11-06)
|
|
578
613
|
|
|
579
614
|
|
|
@@ -13,6 +13,7 @@ interface ClipTagProps {
|
|
|
13
13
|
noDescription?: boolean;
|
|
14
14
|
noInfoBox?: boolean;
|
|
15
15
|
noCaption?: boolean;
|
|
16
|
+
maxClipWidth?: number;
|
|
16
17
|
noClosedCaption?: boolean;
|
|
17
18
|
accessibility?: ComponentWorkarounds.ClipAccessibility;
|
|
18
19
|
muted?: boolean;
|
|
@@ -20,6 +21,7 @@ interface ClipTagProps {
|
|
|
20
21
|
caption?: string;
|
|
21
22
|
preload?: string;
|
|
22
23
|
systemTitle?: string;
|
|
24
|
+
fragmentIdentifier?: string;
|
|
23
25
|
}
|
|
24
26
|
export declare const ClipTag: React.FC<ClipTagProps>;
|
|
25
27
|
export {};
|
|
@@ -7,16 +7,45 @@ exports.ClipTag = void 0;
|
|
|
7
7
|
const react_1 = __importDefault(require("react"));
|
|
8
8
|
const index_1 = require("./index");
|
|
9
9
|
const FixedAspectRatio_1 = require("../../../FixedAspectRatio/FixedAspectRatio");
|
|
10
|
-
const ClipTag = ({ id, dataLayout, description, poster, autoplay, noAudio, loop, muted, clip, credits, caption, accessibility, preload, noDescription = false, noInfoBox = false, noCaption = false, dataTrackable, systemTitle, }) => {
|
|
10
|
+
const ClipTag = ({ id, dataLayout, description, poster, autoplay, noAudio, loop, muted, clip, credits, caption, accessibility, preload, noDescription = false, noInfoBox = false, noCaption = false, maxClipWidth = 0, dataTrackable, systemTitle, fragmentIdentifier, }) => {
|
|
11
11
|
const { pixelWidth, pixelHeight } = clip?.dataSource?.[0]
|
|
12
12
|
? clip.dataSource[0]
|
|
13
13
|
: { pixelWidth: 0, pixelHeight: 0 };
|
|
14
|
-
|
|
14
|
+
const mediaQuery = ({ dppx, previousSourceWidth, }) => {
|
|
15
|
+
const mqOptions = [];
|
|
16
|
+
if (dppx && dppx > 1) {
|
|
17
|
+
mqOptions.push(`(min-resolution: ${dppx}dppx)`);
|
|
18
|
+
}
|
|
19
|
+
if (previousSourceWidth) {
|
|
20
|
+
mqOptions.push(`(min-width: ${previousSourceWidth}px)`);
|
|
21
|
+
}
|
|
22
|
+
if (mqOptions.length > 0) {
|
|
23
|
+
return mqOptions.join(' and ');
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
// get the index of the last source that is a video
|
|
27
|
+
const getLastVideoIndex = () => {
|
|
28
|
+
for (let i = clip?.dataSource.length - 1; i >= 0; i--) {
|
|
29
|
+
if (clip?.dataSource[i]?.mediaType.startsWith('video/')) {
|
|
30
|
+
return i;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return 0;
|
|
34
|
+
};
|
|
35
|
+
const lastVideoIndex = getLastVideoIndex();
|
|
36
|
+
return (react_1.default.createElement("div", { "data-cp-clip-layout": dataLayout, "data-cp-clip-poster": poster, "data-cp-clip-autoplay": autoplay, "data-cp-clip-no-audio": noAudio, "data-cp-clip-no-description": noDescription, "data-cp-clip-caption": caption, "data-cp-clip-no-info-box": noInfoBox, "data-cp-clip-no-caption": noCaption, "data-cp-clip-closed-caption": Boolean(accessibility?.captions?.[0]?.url), "data-cp-clip-loop": loop, className: "cp-clip", "data-trackable": dataTrackable, "data-o-component": "cp-clip", "data-cp-clip-id": id, "data-cp-clip-system-title": systemTitle, id: fragmentIdentifier },
|
|
15
37
|
!noInfoBox && react_1.default.createElement(index_1.VideoInfoBox, null),
|
|
16
38
|
react_1.default.createElement("div", { className: "cp-clip__video-container" },
|
|
17
39
|
react_1.default.createElement(FixedAspectRatio_1.FixedAspectRatio, { width: pixelWidth, height: pixelHeight, element: "video", className: "cp-clip__video", controls: true, controlsList: "nodownload noremoteplayback noplaybackrate", disablePictureInPicture: true, disableRemotePlayback: true, playsInline: true, muted: muted, loop: loop, poster: poster, preload: preload, id: `clip-${id}`, crossOrigin: "anonymous" },
|
|
18
|
-
clip?.dataSource?.map(({ binaryUrl, mediaType }, index) => {
|
|
19
|
-
|
|
40
|
+
clip?.dataSource?.map(({ binaryUrl, mediaType, dppx, previousSourceWidth }, index) => {
|
|
41
|
+
// allow user to set a max width for clip sources to avoid loading very large videos on places like the homepage
|
|
42
|
+
if (maxClipWidth &&
|
|
43
|
+
previousSourceWidth &&
|
|
44
|
+
previousSourceWidth > maxClipWidth &&
|
|
45
|
+
index < lastVideoIndex) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return (react_1.default.createElement("source", { id: `video-source-${index}-${id}`, "data-cp-component": "cp-clip__video-source", src: binaryUrl, type: mediaType !== '' ? mediaType : undefined, key: binaryUrl, media: mediaQuery({ dppx, previousSourceWidth }) }));
|
|
20
49
|
}),
|
|
21
50
|
react_1.default.createElement(index_1.ClosedCaptions, { accessibility: accessibility }))),
|
|
22
51
|
(!noDescription || !noCaption) && (react_1.default.createElement("div", { className: "cp-clip__video-meta-info", "data-cp-clip-video-meta-info": true },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClipTag.js","sourceRoot":"","sources":["../../../../../src/components/content-tree/Clip/components/ClipTag.tsx"],"names":[],"mappings":";;;;;;AAAA,kDAAyB;AACzB,mCAKgB;AAChB,iFAA6E;
|
|
1
|
+
{"version":3,"file":"ClipTag.js","sourceRoot":"","sources":["../../../../../src/components/content-tree/Clip/components/ClipTag.tsx"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const react_1 = __importDefault(require("react"));
|
|
7
7
|
const components_1 = require("../components");
|
|
8
|
-
const ClipComponent = ({ content, preset = 'full-player', preload = 'auto', }) => {
|
|
8
|
+
const ClipComponent = ({ content, preset = 'full-player', preload = 'auto', maxClipWidth = 0, }) => {
|
|
9
9
|
let poster = 'poster' in content ? content.poster : '';
|
|
10
10
|
let id;
|
|
11
11
|
// We support currently a single clip, with multiple sources.
|
|
@@ -36,11 +36,11 @@ const ClipComponent = ({ content, preset = 'full-player', preload = 'auto', }) =
|
|
|
36
36
|
const accessibility = content.accessibility ?? {};
|
|
37
37
|
const systemTitle = content.systemTitle ?? '';
|
|
38
38
|
if (preset === 'thumbnail') {
|
|
39
|
-
return (react_1.default.createElement(components_1.ClipTag, { id: id, clip: clip, poster: poster, accessibility: accessibility, autoplay: content.autoplay, loop: content.loop, noAudio: noAudio, muted: content.muted, preload: preload, noCaption: true, noDescription: true, noInfoBox: true, systemTitle: systemTitle }));
|
|
39
|
+
return (react_1.default.createElement(components_1.ClipTag, { id: id, clip: clip, poster: poster, accessibility: accessibility, autoplay: content.autoplay, loop: content.loop, noAudio: noAudio, muted: content.muted, preload: preload, noCaption: true, noDescription: true, noInfoBox: true, systemTitle: systemTitle, maxClipWidth: maxClipWidth }));
|
|
40
40
|
}
|
|
41
41
|
return (react_1.default.createElement(components_1.ContentLayout, { dataLayout: content.dataLayout },
|
|
42
42
|
react_1.default.createElement(components_1.Container, { dataLayout: content.dataLayout },
|
|
43
|
-
react_1.default.createElement(components_1.ClipTag, { id: id, dataLayout: content.dataLayout, description: content.description ?? '', poster: posterAttribute, autoplay: content.autoplay, noAudio: noAudio, loop: content.loop, muted: content.muted, clip: clip, credits: content.credits ?? '', caption: content.caption ?? '', systemTitle: systemTitle, accessibility: accessibility, preload: preload, dataTrackable: "next-article-cp-clip" }))));
|
|
43
|
+
react_1.default.createElement(components_1.ClipTag, { id: id, dataLayout: content.dataLayout, description: content.description ?? '', poster: posterAttribute, autoplay: content.autoplay, noAudio: noAudio, loop: content.loop, muted: content.muted, clip: clip, credits: content.credits ?? '', caption: content.caption ?? '', systemTitle: systemTitle, accessibility: accessibility, preload: preload, dataTrackable: "next-article-cp-clip", maxClipWidth: maxClipWidth, fragmentIdentifier: content.fragmentIdentifier ?? '' }))));
|
|
44
44
|
};
|
|
45
45
|
exports.default = ClipComponent;
|
|
46
46
|
//# sourceMappingURL=component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.js","sourceRoot":"","sources":["../../../../../src/components/content-tree/Clip/template/component.tsx"],"names":[],"mappings":";;;;;AAAA,kDAAyB;AACzB,8CAAiE;
|
|
1
|
+
{"version":3,"file":"component.js","sourceRoot":"","sources":["../../../../../src/components/content-tree/Clip/template/component.tsx"],"names":[],"mappings":"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"}
|
|
@@ -6,18 +6,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const react_1 = __importDefault(require("react"));
|
|
7
7
|
//HACK: worked around missing Teaser type by declaring a module x-teaser.d.ts
|
|
8
8
|
const x_teaser_1 = require("@financial-times/x-teaser");
|
|
9
|
-
const RecommendedList = ({ content
|
|
10
|
-
|
|
9
|
+
const RecommendedList = ({ content }) => {
|
|
10
|
+
const { heading, children } = content;
|
|
11
|
+
if (!children || children.length === 0) {
|
|
11
12
|
return null;
|
|
12
13
|
}
|
|
13
14
|
return (react_1.default.createElement("aside", { "aria-labelledby": "aside-label", className: "n-content-recommended-list", "data-component": "recommended-list" },
|
|
14
15
|
react_1.default.createElement("p", { className: "n-content-recommended-list__title o3-type-title-lg" }, heading),
|
|
15
|
-
react_1.default.createElement("div", { className: "n-content-recommended-list__teasers" },
|
|
16
|
-
prefLabel: teaser?.clientName,
|
|
16
|
+
react_1.default.createElement("div", { className: "n-content-recommended-list__teasers" }, children.map((child, index) => (react_1.default.createElement(x_teaser_1.Teaser, { key: child?.id || index, modifiers: ['stacked'], ...x_teaser_1.presets.SmallHeavy, ...child.teaser, metaPrefixText: 'By', metaLink: {
|
|
17
|
+
prefLabel: child.teaser?.clientName,
|
|
17
18
|
// clientName is a free form text field. Currently, there is no way to map it to an organisation
|
|
18
19
|
// from the annotations. For now, we're linking it to the article url.
|
|
19
20
|
// A property like isSponsoredBy could be added in future to support this properly.
|
|
20
|
-
url: teaser?.url,
|
|
21
|
+
url: child.teaser?.url,
|
|
21
22
|
} }))))));
|
|
22
23
|
};
|
|
23
24
|
exports.default = RecommendedList;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/content-tree/RecommendedList/index.tsx"],"names":[],"mappings":";;;;;AAAA,kDAAyB;AAEzB,6EAA6E;AAC7E,wDAA2D;AAW3D,MAAM,eAAe,GAAmC,CAAC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/components/content-tree/RecommendedList/index.tsx"],"names":[],"mappings":";;;;;AAAA,kDAAyB;AAEzB,6EAA6E;AAC7E,wDAA2D;AAW3D,MAAM,eAAe,GAAmC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;IACtE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAA;IACrC,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,CACL,4DACkB,aAAa,EAC7B,SAAS,EAAC,4BAA4B,oBACvB,kBAAkB;QAEjC,qCAAG,SAAS,EAAC,oDAAoD,IAC9D,OAAO,CACN;QACJ,uCAAK,SAAS,EAAC,qCAAqC,IACjD,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAC9B,8BAAC,iBAAM,IACL,GAAG,EAAE,KAAK,EAAE,EAAE,IAAI,KAAK,EACvB,SAAS,EAAE,CAAC,SAAS,CAAC,KAClB,kBAAO,CAAC,UAAU,KAClB,KAAK,CAAC,MAAM,EAChB,cAAc,EAAE,IAAI,EACpB,QAAQ,EAAE;gBACR,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,UAAU;gBACnC,gGAAgG;gBAChG,sEAAsE;gBACtE,mFAAmF;gBACnF,GAAG,EAAE,KAAK,CAAC,MAAM,EAAE,GAAG;aACvB,GACD,CACH,CAAC,CACE,CACA,CACT,CAAA;AACH,CAAC,CAAA;AAED,kBAAe,eAAe,CAAA"}
|
|
@@ -50,6 +50,9 @@ interface ClipSource {
|
|
|
50
50
|
pixelHeight?: number;
|
|
51
51
|
pixelWidth?: number;
|
|
52
52
|
videoCodec?: string;
|
|
53
|
+
quality?: string;
|
|
54
|
+
dppx?: number;
|
|
55
|
+
previousSourceWidth?: number;
|
|
53
56
|
}
|
|
54
57
|
export type Clip = PartialToMaybeDeep<ReadonlyArrays<{
|
|
55
58
|
format?: ClipFormat;
|
|
@@ -80,6 +83,7 @@ interface ClipSetReferences {
|
|
|
80
83
|
subtitle?: string;
|
|
81
84
|
publishedDate?: string;
|
|
82
85
|
clips?: Clip[];
|
|
86
|
+
fragmentIdentifier?: string | undefined;
|
|
83
87
|
}
|
|
84
88
|
export type ClipSet = PartialToMaybeDeep<ReadonlyArrays<(ContentTreeWorkarounds.OldClip | ContentTreeWorkarounds.ClipSet) & ClipSetReferences>>;
|
|
85
89
|
export type RawImage = ContentTreeWorkarounds.RawImage & {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@financial-times/cp-content-pipeline-ui",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.12.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"@babel/preset-env": "^7.22.5",
|
|
17
17
|
"@babel/preset-react": "^7.22.5",
|
|
18
18
|
"@financial-times/content-tree": "github:financial-times/content-tree#3f77ec4",
|
|
19
|
-
"@financial-times/cp-content-pipeline-client": "^4.
|
|
20
|
-
"@financial-times/cp-content-pipeline-schema": "^3.
|
|
19
|
+
"@financial-times/cp-content-pipeline-client": "^4.20.1",
|
|
20
|
+
"@financial-times/cp-content-pipeline-schema": "^3.20.0",
|
|
21
21
|
"@financial-times/cp-content-pipeline-styles": "^4.5.0",
|
|
22
22
|
"@financial-times/n-scrollytelling-image": "^1.1.0",
|
|
23
23
|
"@financial-times/o-grid": "^6.1.8",
|
|
@@ -21,6 +21,7 @@ interface ClipTagProps {
|
|
|
21
21
|
noDescription?: boolean
|
|
22
22
|
noInfoBox?: boolean
|
|
23
23
|
noCaption?: boolean
|
|
24
|
+
maxClipWidth?: number
|
|
24
25
|
noClosedCaption?: boolean
|
|
25
26
|
accessibility?: ComponentWorkarounds.ClipAccessibility
|
|
26
27
|
muted?: boolean
|
|
@@ -28,6 +29,7 @@ interface ClipTagProps {
|
|
|
28
29
|
caption?: string
|
|
29
30
|
preload?: string
|
|
30
31
|
systemTitle?: string
|
|
32
|
+
fragmentIdentifier?: string //a unique identifier used to locate the component inside a page for features like scrolling to it
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export const ClipTag: React.FC<ClipTagProps> = ({
|
|
@@ -47,13 +49,46 @@ export const ClipTag: React.FC<ClipTagProps> = ({
|
|
|
47
49
|
noDescription = false,
|
|
48
50
|
noInfoBox = false,
|
|
49
51
|
noCaption = false,
|
|
52
|
+
maxClipWidth = 0,
|
|
50
53
|
dataTrackable,
|
|
51
54
|
systemTitle,
|
|
55
|
+
fragmentIdentifier,
|
|
52
56
|
}) => {
|
|
53
57
|
const { pixelWidth, pixelHeight } = clip?.dataSource?.[0]
|
|
54
58
|
? clip.dataSource[0]
|
|
55
59
|
: { pixelWidth: 0, pixelHeight: 0 }
|
|
56
60
|
|
|
61
|
+
const mediaQuery = ({
|
|
62
|
+
dppx,
|
|
63
|
+
previousSourceWidth,
|
|
64
|
+
}: {
|
|
65
|
+
dppx: number | null | undefined
|
|
66
|
+
previousSourceWidth: number | null | undefined
|
|
67
|
+
}) => {
|
|
68
|
+
const mqOptions = []
|
|
69
|
+
if (dppx && dppx > 1) {
|
|
70
|
+
mqOptions.push(`(min-resolution: ${dppx}dppx)`)
|
|
71
|
+
}
|
|
72
|
+
if (previousSourceWidth) {
|
|
73
|
+
mqOptions.push(`(min-width: ${previousSourceWidth}px)`)
|
|
74
|
+
}
|
|
75
|
+
if (mqOptions.length > 0) {
|
|
76
|
+
return mqOptions.join(' and ')
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// get the index of the last source that is a video
|
|
81
|
+
const getLastVideoIndex = (): number => {
|
|
82
|
+
for (let i = clip?.dataSource.length - 1; i >= 0; i--) {
|
|
83
|
+
if (clip?.dataSource[i]?.mediaType.startsWith('video/')) {
|
|
84
|
+
return i
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return 0
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const lastVideoIndex: number = getLastVideoIndex()
|
|
91
|
+
|
|
57
92
|
return (
|
|
58
93
|
<div
|
|
59
94
|
data-cp-clip-layout={dataLayout}
|
|
@@ -71,6 +106,7 @@ export const ClipTag: React.FC<ClipTagProps> = ({
|
|
|
71
106
|
data-o-component="cp-clip"
|
|
72
107
|
data-cp-clip-id={id}
|
|
73
108
|
data-cp-clip-system-title={systemTitle}
|
|
109
|
+
id={fragmentIdentifier}
|
|
74
110
|
>
|
|
75
111
|
{!noInfoBox && <VideoInfoBox />}
|
|
76
112
|
<div className="cp-clip__video-container">
|
|
@@ -92,17 +128,31 @@ export const ClipTag: React.FC<ClipTagProps> = ({
|
|
|
92
128
|
id={`clip-${id}`}
|
|
93
129
|
crossOrigin="anonymous"
|
|
94
130
|
>
|
|
95
|
-
{clip?.dataSource?.map(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
131
|
+
{clip?.dataSource?.map(
|
|
132
|
+
({ binaryUrl, mediaType, dppx, previousSourceWidth }, index) => {
|
|
133
|
+
// allow user to set a max width for clip sources to avoid loading very large videos on places like the homepage
|
|
134
|
+
if (
|
|
135
|
+
maxClipWidth &&
|
|
136
|
+
previousSourceWidth &&
|
|
137
|
+
previousSourceWidth > maxClipWidth &&
|
|
138
|
+
index < lastVideoIndex
|
|
139
|
+
) {
|
|
140
|
+
return null
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<source
|
|
145
|
+
id={`video-source-${index}-${id}`}
|
|
146
|
+
data-cp-component="cp-clip__video-source"
|
|
147
|
+
src={binaryUrl}
|
|
148
|
+
type={mediaType !== '' ? mediaType : undefined}
|
|
149
|
+
key={binaryUrl}
|
|
150
|
+
media={mediaQuery({ dppx, previousSourceWidth })}
|
|
151
|
+
></source>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
)}
|
|
155
|
+
|
|
106
156
|
<ClosedCaptions accessibility={accessibility} />
|
|
107
157
|
</FixedAspectRatio>
|
|
108
158
|
</div>
|
|
@@ -8,6 +8,7 @@ type Preset = 'full-player' | 'thumbnail'
|
|
|
8
8
|
export interface ClipProps extends ContentProps<ComponentWorkarounds.ClipSet> {
|
|
9
9
|
preload?: string
|
|
10
10
|
preset?: Preset
|
|
11
|
+
maxClipWidth?: number
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export interface ClipPropsOld {
|
|
@@ -34,6 +35,7 @@ const ClipComponent: React.FC<ClipProps> = ({
|
|
|
34
35
|
content,
|
|
35
36
|
preset = 'full-player',
|
|
36
37
|
preload = 'auto',
|
|
38
|
+
maxClipWidth = 0,
|
|
37
39
|
}) => {
|
|
38
40
|
let poster = 'poster' in content ? content.poster : ''
|
|
39
41
|
let id: string
|
|
@@ -82,6 +84,7 @@ const ClipComponent: React.FC<ClipProps> = ({
|
|
|
82
84
|
noDescription
|
|
83
85
|
noInfoBox
|
|
84
86
|
systemTitle={systemTitle}
|
|
87
|
+
maxClipWidth={maxClipWidth}
|
|
85
88
|
/>
|
|
86
89
|
)
|
|
87
90
|
}
|
|
@@ -105,6 +108,8 @@ const ClipComponent: React.FC<ClipProps> = ({
|
|
|
105
108
|
accessibility={accessibility}
|
|
106
109
|
preload={preload}
|
|
107
110
|
dataTrackable="next-article-cp-clip"
|
|
111
|
+
maxClipWidth={maxClipWidth}
|
|
112
|
+
fragmentIdentifier={content.fragmentIdentifier ?? ''}
|
|
108
113
|
/>
|
|
109
114
|
</Container>
|
|
110
115
|
</ContentLayout>
|
|
@@ -17,6 +17,7 @@ exports[`Clip Snapshot component rendered on server full-grid default render 1`]
|
|
|
17
17
|
data-o-component="cp-clip"
|
|
18
18
|
data-cp-clip-id="localhost:8080/fakevideo.mpg"
|
|
19
19
|
data-cp-clip-system-title=""
|
|
20
|
+
id=""
|
|
20
21
|
>
|
|
21
22
|
<div
|
|
22
23
|
data-o-component="o-expander"
|
|
@@ -87,6 +88,7 @@ exports[`Clip Snapshot component rendered on server full-grid render with attrib
|
|
|
87
88
|
data-o-component="cp-clip"
|
|
88
89
|
data-cp-clip-id="localhost:8080/fakevideo.mpg"
|
|
89
90
|
data-cp-clip-system-title=""
|
|
91
|
+
id=""
|
|
90
92
|
>
|
|
91
93
|
<div
|
|
92
94
|
data-o-component="o-expander"
|
|
@@ -188,6 +190,7 @@ exports[`Clip Snapshot component rendered on server in-line render 1`] = `
|
|
|
188
190
|
data-o-component="cp-clip"
|
|
189
191
|
data-cp-clip-id="localhost:8080/fakevideo.mpg"
|
|
190
192
|
data-cp-clip-system-title=""
|
|
193
|
+
id=""
|
|
191
194
|
>
|
|
192
195
|
<div
|
|
193
196
|
data-o-component="o-expander"
|
|
@@ -256,6 +259,7 @@ exports[`Clip Snapshot component rendered on server in-line render with attribut
|
|
|
256
259
|
data-o-component="cp-clip"
|
|
257
260
|
data-cp-clip-id="localhost:8080/fakevideo.mpg"
|
|
258
261
|
data-cp-clip-system-title=""
|
|
262
|
+
id=""
|
|
259
263
|
>
|
|
260
264
|
<div
|
|
261
265
|
data-o-component="o-expander"
|
|
@@ -361,6 +365,7 @@ exports[`Clip Snapshot component rendered on server mid-grid default render 1`]
|
|
|
361
365
|
data-o-component="cp-clip"
|
|
362
366
|
data-cp-clip-id="localhost:8080/fakevideo.mpg"
|
|
363
367
|
data-cp-clip-system-title=""
|
|
368
|
+
id=""
|
|
364
369
|
>
|
|
365
370
|
<div
|
|
366
371
|
data-o-component="o-expander"
|
|
@@ -436,6 +441,7 @@ exports[`Clip Snapshot component rendered on server mid-grid render with attribu
|
|
|
436
441
|
data-o-component="cp-clip"
|
|
437
442
|
data-cp-clip-id="localhost:8080/fakevideo.mpg"
|
|
438
443
|
data-cp-clip-system-title=""
|
|
444
|
+
id=""
|
|
439
445
|
>
|
|
440
446
|
<div
|
|
441
447
|
data-o-component="o-expander"
|
|
@@ -544,6 +550,7 @@ exports[`Clip Snapshot component rendered on server renders multiple video sourc
|
|
|
544
550
|
data-o-component="cp-clip"
|
|
545
551
|
data-cp-clip-id="84d7e1b0-e8b2-4ffc-a798-306f29dc9d52"
|
|
546
552
|
data-cp-clip-system-title=""
|
|
553
|
+
id=""
|
|
547
554
|
>
|
|
548
555
|
<div
|
|
549
556
|
data-o-component="o-expander"
|
|
@@ -655,6 +662,7 @@ exports[`Clip Snapshot component rendered on server supports new Origami images,
|
|
|
655
662
|
data-o-component="cp-clip"
|
|
656
663
|
data-cp-clip-id="localhost:8080/fakevideo.mpg"
|
|
657
664
|
data-cp-clip-system-title=""
|
|
665
|
+
id=""
|
|
658
666
|
>
|
|
659
667
|
<div
|
|
660
668
|
data-o-component="o-expander"
|
|
@@ -5,17 +5,16 @@ import { presets, Teaser } from '@financial-times/x-teaser'
|
|
|
5
5
|
import { ContentProps } from '../../types'
|
|
6
6
|
import type * as ComponentWorkarounds from '../Workarounds'
|
|
7
7
|
|
|
8
|
-
// Renders a Recommended teaser
|
|
8
|
+
// Renders a Recommended teaser componentx
|
|
9
9
|
// `<Teaser>` is imported from x-dash
|
|
10
10
|
// https://github.com/Financial-Times/x-dash/tree/main/components/x-teaser)
|
|
11
11
|
|
|
12
12
|
interface RecommendedListProps
|
|
13
13
|
extends ContentProps<ComponentWorkarounds.RecommendedList> {}
|
|
14
14
|
|
|
15
|
-
const RecommendedList: React.FC<RecommendedListProps> = ({
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (!teasers || teasers.length === 0) {
|
|
15
|
+
const RecommendedList: React.FC<RecommendedListProps> = ({ content }) => {
|
|
16
|
+
const { heading, children } = content
|
|
17
|
+
if (!children || children.length === 0) {
|
|
19
18
|
return null
|
|
20
19
|
}
|
|
21
20
|
|
|
@@ -29,19 +28,19 @@ const RecommendedList: React.FC<RecommendedListProps> = ({
|
|
|
29
28
|
{heading}
|
|
30
29
|
</p>
|
|
31
30
|
<div className="n-content-recommended-list__teasers">
|
|
32
|
-
{
|
|
31
|
+
{children.map((child, index) => (
|
|
33
32
|
<Teaser
|
|
34
|
-
key={
|
|
33
|
+
key={child?.id || index}
|
|
35
34
|
modifiers={['stacked']}
|
|
36
35
|
{...presets.SmallHeavy}
|
|
37
|
-
{...teaser}
|
|
36
|
+
{...child.teaser}
|
|
38
37
|
metaPrefixText={'By'}
|
|
39
38
|
metaLink={{
|
|
40
|
-
prefLabel: teaser?.clientName,
|
|
39
|
+
prefLabel: child.teaser?.clientName,
|
|
41
40
|
// clientName is a free form text field. Currently, there is no way to map it to an organisation
|
|
42
41
|
// from the annotations. For now, we're linking it to the article url.
|
|
43
42
|
// A property like isSponsoredBy could be added in future to support this properly.
|
|
44
|
-
url: teaser?.url,
|
|
43
|
+
url: child.teaser?.url,
|
|
45
44
|
}}
|
|
46
45
|
/>
|
|
47
46
|
))}
|
|
@@ -87,7 +87,7 @@ 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
|
>
|
|
@@ -95,11 +95,10 @@ ContentTreeWorkarounds.Recommended & {
|
|
|
95
95
|
export type RecommendedList = PartialToMaybe<
|
|
96
96
|
ReadonlyArrays<
|
|
97
97
|
Omit<ContentTree.RecommendedList, 'children'> & {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
teasers?: (Teaser | null)[]
|
|
99
|
+
children: Recommended[]
|
|
100
|
+
}
|
|
101
101
|
>
|
|
102
|
-
|
|
103
102
|
>
|
|
104
103
|
|
|
105
104
|
export type ImageSetPicture = PartialToMaybe<
|
|
@@ -144,6 +143,9 @@ interface ClipSource {
|
|
|
144
143
|
pixelHeight?: number
|
|
145
144
|
pixelWidth?: number
|
|
146
145
|
videoCodec?: string
|
|
146
|
+
quality?: string
|
|
147
|
+
dppx?: number
|
|
148
|
+
previousSourceWidth?: number
|
|
147
149
|
}
|
|
148
150
|
export type Clip = PartialToMaybeDeep<
|
|
149
151
|
ReadonlyArrays<{
|
|
@@ -178,6 +180,7 @@ interface ClipSetReferences {
|
|
|
178
180
|
subtitle?: string
|
|
179
181
|
publishedDate?: string
|
|
180
182
|
clips?: Clip[]
|
|
183
|
+
fragmentIdentifier?: string | undefined
|
|
181
184
|
}
|
|
182
185
|
|
|
183
186
|
export type ClipSet = PartialToMaybeDeep<
|