@financial-times/cp-content-pipeline-ui 9.10.0 → 9.11.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 CHANGED
@@ -574,6 +574,21 @@
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.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)
578
+
579
+
580
+ ### Features
581
+
582
+ * ci-3111 media queries on clips sources ([856bd45](https://github.com/Financial-Times/cp-content-pipeline/commit/856bd457ba6dc56f292c6a31e0edfe3f29b4a904))
583
+
584
+
585
+ ### Dependencies
586
+
587
+ * The following workspace dependencies were updated
588
+ * devDependencies
589
+ * @financial-times/cp-content-pipeline-client bumped from ^4.19.0 to ^4.20.0
590
+ * @financial-times/cp-content-pipeline-schema bumped from ^3.18.0 to ^3.19.0
591
+
577
592
  ## [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
593
 
579
594
 
@@ -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;
@@ -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, }) => {
11
11
  const { pixelWidth, pixelHeight } = clip?.dataSource?.[0]
12
12
  ? clip.dataSource[0]
13
13
  : { pixelWidth: 0, pixelHeight: 0 };
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();
14
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 },
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
- 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 }));
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;AAyBtE,MAAM,OAAO,GAA2B,CAAC,EAC9C,EAAE,EACF,UAAU,EACV,WAAW,EACX,MAAM,EACN,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,KAAK,EACL,IAAI,EACJ,OAAO,EACP,OAAO,EACP,aAAa,EACb,OAAO,EACP,aAAa,GAAG,KAAK,EACrB,SAAS,GAAG,KAAK,EACjB,SAAS,GAAG,KAAK,EACjB,aAAa,EACb,WAAW,GACZ,EAAE,EAAE;IACH,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;QACvD,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QACpB,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAA;IAErC,OAAO,CACL,8DACuB,UAAU,yBACV,MAAM,2BACJ,QAAQ,2BACR,OAAO,iCACD,aAAa,0BACpB,OAAO,8BACH,SAAS,6BACV,SAAS,iCACL,OAAO,CAAC,aAAa,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,uBACpD,IAAI,EACvB,SAAS,EAAC,SAAS,oBACH,aAAa,sBACZ,SAAS,qBACT,EAAE,+BACQ,WAAW;QAErC,CAAC,SAAS,IAAI,8BAAC,oBAAY,OAAG;QAC/B,uCAAK,SAAS,EAAC,0BAA0B;YAEvC,8BAAC,mCAAgB,IACf,KAAK,EAAE,UAAoB,EAC3B,MAAM,EAAE,WAAqB,EAC7B,OAAO,EAAC,OAAO,EACf,SAAS,EAAC,gBAAgB,EAC1B,QAAQ,QACR,YAAY,EAAC,4CAA4C,EACzD,uBAAuB,QACvB,qBAAqB,QACrB,WAAW,QACX,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,QAAQ,EAAE,EAAE,EAChB,WAAW,EAAC,WAAW;gBAEtB,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,EAAE;oBACzD,OAAO,CACL,0CACE,EAAE,EAAE,gBAAgB,KAAK,IAAI,EAAE,EAAE,uBACf,uBAAuB,EACzC,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,SAAS,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAC9C,GAAG,EAAE,SAAS,GACN,CACX,CAAA;gBACH,CAAC,CAAC;gBACF,8BAAC,sBAAc,IAAC,aAAa,EAAE,aAAa,GAAI,CAC/B,CACf;QACL,CAAC,CAAC,aAAa,IAAI,CAAC,SAAS,CAAC,IAAI,CACjC,uCAAK,SAAS,EAAC,0BAA0B;YACtC,CAAC,aAAa,IAAI,CACjB,8BAAC,wBAAgB,IACf,WAAW,EAAE,WAAW,EACxB,aAAa,EAAE,aAAa,GAC5B,CACH;YACA,CAAC,SAAS,IAAI,8BAAC,eAAO,IAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAI,CAC1D,CACP,CACG,CACP,CAAA;AACH,CAAC,CAAA;AAzFY,QAAA,OAAO,WAyFnB"}
1
+ {"version":3,"file":"ClipTag.js","sourceRoot":"","sources":["../../../../../src/components/content-tree/Clip/components/ClipTag.tsx"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ type Preset = 'full-player' | 'thumbnail';
5
5
  export interface ClipProps extends ContentProps<ComponentWorkarounds.ClipSet> {
6
6
  preload?: string;
7
7
  preset?: Preset;
8
+ maxClipWidth?: number;
8
9
  }
9
10
  export interface ClipPropsOld {
10
11
  url?: string;
@@ -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 }))));
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":"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"}
1
+ {"version":3,"file":"component.js","sourceRoot":"","sources":["../../../../../src/components/content-tree/Clip/template/component.tsx"],"names":[],"mappings":"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"}
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/cp-content-pipeline-ui",
3
- "version": "9.10.0",
3
+ "version": "9.11.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.19.0",
20
- "@financial-times/cp-content-pipeline-schema": "^3.18.0",
19
+ "@financial-times/cp-content-pipeline-client": "^4.20.0",
20
+ "@financial-times/cp-content-pipeline-schema": "^3.19.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
@@ -47,6 +48,7 @@ export const ClipTag: React.FC<ClipTagProps> = ({
47
48
  noDescription = false,
48
49
  noInfoBox = false,
49
50
  noCaption = false,
51
+ maxClipWidth = 0,
50
52
  dataTrackable,
51
53
  systemTitle,
52
54
  }) => {
@@ -54,6 +56,37 @@ export const ClipTag: React.FC<ClipTagProps> = ({
54
56
  ? clip.dataSource[0]
55
57
  : { pixelWidth: 0, pixelHeight: 0 }
56
58
 
59
+ const mediaQuery = ({
60
+ dppx,
61
+ previousSourceWidth,
62
+ }: {
63
+ dppx: number | null | undefined
64
+ previousSourceWidth: number | null | undefined
65
+ }) => {
66
+ const mqOptions = []
67
+ if (dppx && dppx > 1) {
68
+ mqOptions.push(`(min-resolution: ${dppx}dppx)`)
69
+ }
70
+ if (previousSourceWidth) {
71
+ mqOptions.push(`(min-width: ${previousSourceWidth}px)`)
72
+ }
73
+ if (mqOptions.length > 0) {
74
+ return mqOptions.join(' and ')
75
+ }
76
+ }
77
+
78
+ // get the index of the last source that is a video
79
+ const getLastVideoIndex = (): number => {
80
+ for (let i = clip?.dataSource.length - 1; i >= 0; i--) {
81
+ if (clip?.dataSource[i]?.mediaType.startsWith('video/')) {
82
+ return i
83
+ }
84
+ }
85
+ return 0
86
+ }
87
+
88
+ const lastVideoIndex: number = getLastVideoIndex()
89
+
57
90
  return (
58
91
  <div
59
92
  data-cp-clip-layout={dataLayout}
@@ -92,17 +125,31 @@ export const ClipTag: React.FC<ClipTagProps> = ({
92
125
  id={`clip-${id}`}
93
126
  crossOrigin="anonymous"
94
127
  >
95
- {clip?.dataSource?.map(({ binaryUrl, mediaType }, index) => {
96
- return (
97
- <source
98
- id={`video-source-${index}-${id}`}
99
- data-cp-component="cp-clip__video-source"
100
- src={binaryUrl}
101
- type={mediaType !== '' ? mediaType : undefined}
102
- key={binaryUrl}
103
- ></source>
104
- )
105
- })}
128
+ {clip?.dataSource?.map(
129
+ ({ binaryUrl, mediaType, dppx, previousSourceWidth }, index) => {
130
+ // allow user to set a max width for clip sources to avoid loading very large videos on places like the homepage
131
+ if (
132
+ maxClipWidth &&
133
+ previousSourceWidth &&
134
+ previousSourceWidth > maxClipWidth &&
135
+ index < lastVideoIndex
136
+ ) {
137
+ return null
138
+ }
139
+
140
+ return (
141
+ <source
142
+ id={`video-source-${index}-${id}`}
143
+ data-cp-component="cp-clip__video-source"
144
+ src={binaryUrl}
145
+ type={mediaType !== '' ? mediaType : undefined}
146
+ key={binaryUrl}
147
+ media={mediaQuery({ dppx, previousSourceWidth })}
148
+ ></source>
149
+ )
150
+ }
151
+ )}
152
+
106
153
  <ClosedCaptions accessibility={accessibility} />
107
154
  </FixedAspectRatio>
108
155
  </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,7 @@ const ClipComponent: React.FC<ClipProps> = ({
105
108
  accessibility={accessibility}
106
109
  preload={preload}
107
110
  dataTrackable="next-article-cp-clip"
111
+ maxClipWidth={maxClipWidth}
108
112
  />
109
113
  </Container>
110
114
  </ContentLayout>
@@ -144,6 +144,9 @@ interface ClipSource {
144
144
  pixelHeight?: number
145
145
  pixelWidth?: number
146
146
  videoCodec?: string
147
+ quality?: string
148
+ dppx?: number
149
+ previousSourceWidth?: number
147
150
  }
148
151
  export type Clip = PartialToMaybeDeep<
149
152
  ReadonlyArrays<{