@financial-times/x-teaser 8.1.0 → 8.2.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.
@@ -18,11 +18,9 @@ const rulesets = {
18
18
  if (props.showVideo && props.video && props.video.url) {
19
19
  return 'video';
20
20
  }
21
-
22
21
  if (props.showHeadshot && props.headshot && props.indicators.isColumn) {
23
22
  return 'headshot';
24
23
  }
25
-
26
24
  if (props.showImage && props.image && props.image.url) {
27
25
  return 'image';
28
26
  }
@@ -31,31 +29,27 @@ const rulesets = {
31
29
  if (props.theme) {
32
30
  return props.theme;
33
31
  }
34
-
35
32
  if (props.status === 'inprogress') {
36
33
  return 'live';
37
34
  }
38
-
39
35
  if (props.indicators && props.indicators.isOpinion) {
40
36
  return 'opinion';
41
37
  }
42
-
43
38
  if (props.indicators && props.indicators.isEditorsChoice) {
44
39
  return 'highlight';
45
40
  }
46
-
47
41
  if (props.parentTheme) {
48
42
  return props.parentTheme;
49
43
  }
50
44
  }
51
45
  };
46
+
52
47
  /**
53
48
  * Rules
54
49
  * @param {String} rule
55
50
  * @param {Props} props
56
51
  * @returns {String|null}
57
52
  */
58
-
59
53
  function rules(rule, props) {
60
54
  if (rulesets.hasOwnProperty(rule)) {
61
55
  return rulesets[rule](props);
@@ -69,23 +63,18 @@ const theme = props => rules('theme', props);
69
63
  const dynamicModifiers = props => {
70
64
  const modifiers = [];
71
65
  const mediaRule = media(props);
72
-
73
66
  if (mediaRule) {
74
67
  modifiers.push(`has-${mediaRule}`);
75
68
  }
76
-
77
69
  const themeRule = theme(props);
78
-
79
70
  if (themeRule) {
80
71
  modifiers.push(themeRule);
81
72
  }
82
-
83
73
  return modifiers;
84
74
  };
85
-
86
75
  var Container = (props => {
87
- const computed = dynamicModifiers(props); // Modifier props may be a string rather than a string[] so concat, don't spread.
88
-
76
+ const computed = dynamicModifiers(props);
77
+ // Modifier props may be a string rather than a string[] so concat, don't spread.
89
78
  const variants = [props.type, props.layout].concat(props.modifiers, computed);
90
79
  const classNames = variants.filter(Boolean).map(mod => `o-teaser--${mod}`).join(' ');
91
80
  return xEngine.h("div", {
@@ -106,7 +95,6 @@ var Content = (({
106
95
  * @param {String|ReactElement} action
107
96
  * @returns {ReactElement}
108
97
  */
109
-
110
98
  const render = action => {
111
99
  // Allow parent components to pass raw HTML strings
112
100
  if (typeof action === 'string') {
@@ -119,7 +107,6 @@ const render = action => {
119
107
  return action;
120
108
  }
121
109
  };
122
-
123
110
  var CustomSlot = (({
124
111
  customSlot
125
112
  }) => customSlot ? xEngine.h("div", {
@@ -134,8 +121,8 @@ const ImageSizes = {
134
121
  Large: 420,
135
122
  XL: 640,
136
123
  XXL: 1180 // max width of FT.com page
137
-
138
124
  };
125
+
139
126
  const Layouts = {
140
127
  Small: 'small',
141
128
  Large: 'large',
@@ -151,22 +138,24 @@ const OPTIONS = {
151
138
  fit: 'scale-down',
152
139
  dpr: 2
153
140
  };
141
+
154
142
  /**
155
143
  * Image Service
156
144
  * @param {String} url
157
145
  * @param {Number} width
158
146
  * @param {String} options
159
147
  */
160
-
161
148
  function imageService(url, width, options) {
162
149
  const imageSrc = new URL(`${BASE_URL}/${encodeURIComponent(url)}`);
163
- imageSrc.search = new URLSearchParams({ ...OPTIONS,
150
+ imageSrc.search = new URLSearchParams({
151
+ ...OPTIONS,
164
152
  ...options
165
153
  });
166
154
  imageSrc.searchParams.set('width', width);
167
155
  return imageSrc.href;
168
156
  }
169
157
 
158
+ // these colours are tweaked from o-colors palette colours to make headshots look less washed out
170
159
  const DEFAULT_TINT = '054593,d6d5d3';
171
160
  var Headshot = (({
172
161
  headshot,
@@ -198,7 +187,6 @@ const BaseLink = ({
198
187
  return xEngine.h("span", attrs, children);
199
188
  }
200
189
  };
201
-
202
190
  var Link = (({
203
191
  customElements = {},
204
192
  ...props
@@ -212,7 +200,6 @@ var Link = (({
212
200
  * @param {{ width: Number, height: Number }} image
213
201
  * @returns {String|null}
214
202
  */
215
-
216
203
  const aspectRatio = ({
217
204
  width,
218
205
  height
@@ -221,10 +208,8 @@ const aspectRatio = ({
221
208
  const ratio = 100 / width * height;
222
209
  return ratio.toFixed(4) + '%';
223
210
  }
224
-
225
211
  return null;
226
212
  };
227
-
228
213
  const NormalImage = ({
229
214
  src
230
215
  }) => xEngine.h("img", {
@@ -232,7 +217,6 @@ const NormalImage = ({
232
217
  src: src,
233
218
  alt: ""
234
219
  });
235
-
236
220
  const LazyImage = ({
237
221
  src,
238
222
  lazyLoad
@@ -244,7 +228,6 @@ const LazyImage = ({
244
228
  alt: ""
245
229
  });
246
230
  };
247
-
248
231
  var Image = (({
249
232
  relativeUrl,
250
233
  url,
@@ -257,7 +240,6 @@ var Image = (({
257
240
  if (!image || image && !image.url) {
258
241
  return null;
259
242
  }
260
-
261
243
  const displayUrl = relativeUrl || url;
262
244
  const useImageService = !(image.url.startsWith('data:') || image.url.startsWith('blob:'));
263
245
  const options = imageSize === 'XXL' && imageHighestQuality ? {
@@ -288,11 +270,9 @@ var Image = (({
288
270
  const sameId = (context = {}, id) => {
289
271
  return id && context && context.parentId && id === context.parentId;
290
272
  };
291
-
292
273
  const sameLabel = (context = {}, label) => {
293
274
  return label && context && context.parentLabel && label === context.parentLabel;
294
275
  };
295
-
296
276
  var MetaLink = (({
297
277
  metaPrefixText,
298
278
  metaLink,
@@ -353,7 +333,6 @@ const renderLink = ({
353
333
  href: displayUrl
354
334
  }, title));
355
335
  };
356
-
357
336
  var RelatedLinks = (({
358
337
  relatedLinks = []
359
338
  }) => relatedLinks && relatedLinks.length ? xEngine.h("ul", {
@@ -374,34 +353,31 @@ var TimeStamp = (({
374
353
  * @param {Date | String | Number} date
375
354
  * @returns {Date}
376
355
  */
377
-
378
356
  function toDate(date) {
379
357
  if (typeof date === 'string') {
380
358
  return new Date(date);
381
359
  }
382
-
383
360
  if (typeof date === 'number') {
384
361
  return new Date(date);
385
362
  }
386
-
387
363
  return date;
388
364
  }
365
+
389
366
  /**
390
367
  * Get Relative Date
391
368
  * @param {Date | String | Number} date
392
369
  * @returns {Number}
393
370
  */
394
-
395
371
  function getRelativeDate(date) {
396
372
  return Date.now() - toDate(date).getTime();
397
373
  }
374
+
398
375
  /**
399
376
  * Get Status
400
377
  * @param {Date | String | Number} publishedDate
401
378
  * @param {Date | String | Number} firstPublishedDate
402
379
  * @returns {String}
403
380
  */
404
-
405
381
  function getStatus(publishedDate, firstPublishedDate) {
406
382
  if (getRelativeDate(publishedDate) < Newish) {
407
383
  if (publishedDate === firstPublishedDate) {
@@ -410,15 +386,14 @@ function getStatus(publishedDate, firstPublishedDate) {
410
386
  return 'updated';
411
387
  }
412
388
  }
413
-
414
389
  return '';
415
390
  }
391
+
416
392
  /**
417
393
  * Is Recent
418
394
  * @param {Number} relativeDate
419
395
  * @returns {Boolean}
420
396
  */
421
-
422
397
  function isRecent(relativeDate) {
423
398
  return relativeDate < Recent;
424
399
  }
@@ -428,14 +403,12 @@ function isRecent(relativeDate) {
428
403
  * @param {Number} date
429
404
  * @returns {String}
430
405
  */
431
-
432
406
  const displayTime = date => {
433
407
  const hours = Math.floor(Math.abs(date / 3600000));
434
408
  const plural = hours === 1 ? 'hour' : 'hours';
435
409
  const suffix = hours === 0 ? '' : `${plural} ago`;
436
410
  return `${hours} ${suffix}`;
437
411
  };
438
-
439
412
  var RelativeTime = (({
440
413
  publishedDate,
441
414
  firstPublishedDate,
@@ -478,12 +451,9 @@ var LiveBlogStatus = (({
478
451
  * If same calendar day, we show relative time e.g. X hours ago or Updated X min ago
479
452
  * If different calendar day, we show full Date time e.g. June 9, 2021
480
453
  */
481
-
482
454
  var AlwaysShowTimestamp = (props => {
483
455
  const localTodayDate = new Date().toISOString().substr(0, 10); // keep only the date bit
484
-
485
456
  const dateToCompare = new Date(props.publishedDate).toISOString().substr(0, 10);
486
-
487
457
  if (dateFns.differenceInCalendarDays(localTodayDate, dateToCompare) >= 1) {
488
458
  return xEngine.h(TimeStamp, props);
489
459
  } else {
@@ -497,7 +467,6 @@ var Status = (props => {
497
467
  if (props.status) {
498
468
  return xEngine.h(LiveBlogStatus, props);
499
469
  }
500
-
501
470
  if (props.publishedDate) {
502
471
  if (props.useRelativeTimeIfToday) {
503
472
  return xEngine.h(AlwaysShowTimestamp, props);
@@ -507,7 +476,6 @@ var Status = (props => {
507
476
  return xEngine.h(TimeStamp, props);
508
477
  }
509
478
  }
510
-
511
479
  return null;
512
480
  });
513
481
 
@@ -543,17 +511,15 @@ var Title = (({
543
511
  ...props
544
512
  }) => {
545
513
  const displayTitle = headlineTesting && altTitle ? altTitle : title;
546
- const displayUrl = relativeUrl || url; // o-labels--premium left for backwards compatibility for o-labels v3
547
-
514
+ const displayUrl = relativeUrl || url;
515
+ // o-labels--premium left for backwards compatibility for o-labels v3
548
516
  const premiumClass = 'o-labels o-labels--premium o-labels--content-premium';
549
517
  let ariaLabel;
550
-
551
518
  if (props.type === 'video') {
552
519
  ariaLabel = `Watch video ${displayTitle}`;
553
520
  } else if (props.type === 'audio') {
554
521
  ariaLabel = `Listen to podcast ${displayTitle}`;
555
522
  }
556
-
557
523
  return xEngine.h("div", {
558
524
  className: "o-teaser__heading"
559
525
  }, xEngine.h(Link, Object.assign({}, props, {
@@ -570,13 +536,14 @@ var Title = (({
570
536
  }, "\xA0content")) : null);
571
537
  });
572
538
 
539
+ // Re-format the data for use with o-video
573
540
  const formatData = props => JSON.stringify({
574
541
  renditions: [props.video],
575
542
  mainImageUrl: props.image ? props.image.url : null
576
- }); // To prevent React from touching the DOM after mounting… return an empty <div />
577
- // <https://reactjs.org/docs/integrating-with-other-libraries.html>
578
-
543
+ });
579
544
 
545
+ // To prevent React from touching the DOM after mounting… return an empty <div />
546
+ // <https://reactjs.org/docs/integrating-with-other-libraries.html>
580
547
  const Embed = props => {
581
548
  const showGuidance = typeof props.showGuidance === 'boolean' ? props.showGuidance.toString() : 'true';
582
549
  return xEngine.h("div", {
@@ -597,7 +564,6 @@ const Embed = props => {
597
564
  "data-o-video-systemcode": props.systemCode
598
565
  }));
599
566
  };
600
-
601
567
  var Video = (props => xEngine.h("div", {
602
568
  className: "o-teaser__video"
603
569
  }, xEngine.h("div", {
@@ -18,11 +18,9 @@ var rulesets = {
18
18
  if (props.showVideo && props.video && props.video.url) {
19
19
  return 'video';
20
20
  }
21
-
22
21
  if (props.showHeadshot && props.headshot && props.indicators.isColumn) {
23
22
  return 'headshot';
24
23
  }
25
-
26
24
  if (props.showImage && props.image && props.image.url) {
27
25
  return 'image';
28
26
  }
@@ -31,31 +29,27 @@ var rulesets = {
31
29
  if (props.theme) {
32
30
  return props.theme;
33
31
  }
34
-
35
32
  if (props.status === 'inprogress') {
36
33
  return 'live';
37
34
  }
38
-
39
35
  if (props.indicators && props.indicators.isOpinion) {
40
36
  return 'opinion';
41
37
  }
42
-
43
38
  if (props.indicators && props.indicators.isEditorsChoice) {
44
39
  return 'highlight';
45
40
  }
46
-
47
41
  if (props.parentTheme) {
48
42
  return props.parentTheme;
49
43
  }
50
44
  }
51
45
  };
46
+
52
47
  /**
53
48
  * Rules
54
49
  * @param {String} rule
55
50
  * @param {Props} props
56
51
  * @returns {String|null}
57
52
  */
58
-
59
53
  function rules(rule, props) {
60
54
  if (rulesets.hasOwnProperty(rule)) {
61
55
  return rulesets[rule](props);
@@ -73,23 +67,18 @@ var theme = function theme(props) {
73
67
  var dynamicModifiers = function dynamicModifiers(props) {
74
68
  var modifiers = [];
75
69
  var mediaRule = media(props);
76
-
77
70
  if (mediaRule) {
78
71
  modifiers.push("has-".concat(mediaRule));
79
72
  }
80
-
81
73
  var themeRule = theme(props);
82
-
83
74
  if (themeRule) {
84
75
  modifiers.push(themeRule);
85
76
  }
86
-
87
77
  return modifiers;
88
78
  };
89
-
90
79
  var Container = (function (props) {
91
- var computed = dynamicModifiers(props); // Modifier props may be a string rather than a string[] so concat, don't spread.
92
-
80
+ var computed = dynamicModifiers(props);
81
+ // Modifier props may be a string rather than a string[] so concat, don't spread.
93
82
  var variants = [props.type, props.layout].concat(props.modifiers, computed);
94
83
  var classNames = variants.filter(Boolean).map(function (mod) {
95
84
  return "o-teaser--".concat(mod);
@@ -103,7 +92,7 @@ var Container = (function (props) {
103
92
 
104
93
  var Content = (function (_ref) {
105
94
  var _ref$children = _ref.children,
106
- children = _ref$children === void 0 ? [] : _ref$children;
95
+ children = _ref$children === void 0 ? [] : _ref$children;
107
96
  return xEngine.h("div", {
108
97
  className: "o-teaser__content"
109
98
  }, children);
@@ -114,7 +103,6 @@ var Content = (function (_ref) {
114
103
  * @param {String|ReactElement} action
115
104
  * @returns {ReactElement}
116
105
  */
117
-
118
106
  var render = function render(action) {
119
107
  // Allow parent components to pass raw HTML strings
120
108
  if (typeof action === 'string') {
@@ -127,7 +115,6 @@ var render = function render(action) {
127
115
  return action;
128
116
  }
129
117
  };
130
-
131
118
  var CustomSlot = (function (_ref) {
132
119
  var customSlot = _ref.customSlot;
133
120
  return customSlot ? xEngine.h("div", {
@@ -143,8 +130,8 @@ var ImageSizes = {
143
130
  Large: 420,
144
131
  XL: 640,
145
132
  XXL: 1180 // max width of FT.com page
146
-
147
133
  };
134
+
148
135
  var Layouts = {
149
136
  Small: 'small',
150
137
  Large: 'large',
@@ -156,17 +143,14 @@ var Recent = 1000 * 60 * 60 * 4;
156
143
 
157
144
  function ownKeys(object, enumerableOnly) {
158
145
  var keys = Object.keys(object);
159
-
160
146
  if (Object.getOwnPropertySymbols) {
161
147
  var symbols = Object.getOwnPropertySymbols(object);
162
148
  enumerableOnly && (symbols = symbols.filter(function (sym) {
163
149
  return Object.getOwnPropertyDescriptor(object, sym).enumerable;
164
150
  })), keys.push.apply(keys, symbols);
165
151
  }
166
-
167
152
  return keys;
168
153
  }
169
-
170
154
  function _objectSpread2(target) {
171
155
  for (var i = 1; i < arguments.length; i++) {
172
156
  var source = null != arguments[i] ? arguments[i] : {};
@@ -176,10 +160,8 @@ function _objectSpread2(target) {
176
160
  Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
177
161
  });
178
162
  }
179
-
180
163
  return target;
181
164
  }
182
-
183
165
  function _defineProperty(obj, key, value) {
184
166
  if (key in obj) {
185
167
  Object.defineProperty(obj, key, {
@@ -191,35 +173,26 @@ function _defineProperty(obj, key, value) {
191
173
  } else {
192
174
  obj[key] = value;
193
175
  }
194
-
195
176
  return obj;
196
177
  }
197
-
198
178
  function _objectWithoutPropertiesLoose(source, excluded) {
199
179
  if (source == null) return {};
200
180
  var target = {};
201
181
  var sourceKeys = Object.keys(source);
202
182
  var key, i;
203
-
204
183
  for (i = 0; i < sourceKeys.length; i++) {
205
184
  key = sourceKeys[i];
206
185
  if (excluded.indexOf(key) >= 0) continue;
207
186
  target[key] = source[key];
208
187
  }
209
-
210
188
  return target;
211
189
  }
212
-
213
190
  function _objectWithoutProperties(source, excluded) {
214
191
  if (source == null) return {};
215
-
216
192
  var target = _objectWithoutPropertiesLoose(source, excluded);
217
-
218
193
  var key, i;
219
-
220
194
  if (Object.getOwnPropertySymbols) {
221
195
  var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
222
-
223
196
  for (i = 0; i < sourceSymbolKeys.length; i++) {
224
197
  key = sourceSymbolKeys[i];
225
198
  if (excluded.indexOf(key) >= 0) continue;
@@ -227,7 +200,6 @@ function _objectWithoutProperties(source, excluded) {
227
200
  target[key] = source[key];
228
201
  }
229
202
  }
230
-
231
203
  return target;
232
204
  }
233
205
 
@@ -237,13 +209,13 @@ var OPTIONS = {
237
209
  fit: 'scale-down',
238
210
  dpr: 2
239
211
  };
212
+
240
213
  /**
241
214
  * Image Service
242
215
  * @param {String} url
243
216
  * @param {Number} width
244
217
  * @param {String} options
245
218
  */
246
-
247
219
  function imageService(url, width, options) {
248
220
  var imageSrc = new URL("".concat(BASE_URL, "/").concat(encodeURIComponent(url)));
249
221
  imageSrc.search = new URLSearchParams(_objectSpread2(_objectSpread2({}, OPTIONS), options));
@@ -251,10 +223,11 @@ function imageService(url, width, options) {
251
223
  return imageSrc.href;
252
224
  }
253
225
 
226
+ // these colours are tweaked from o-colors palette colours to make headshots look less washed out
254
227
  var DEFAULT_TINT = '054593,d6d5d3';
255
228
  var Headshot = (function (_ref) {
256
229
  var headshot = _ref.headshot,
257
- headshotTint = _ref.headshotTint;
230
+ headshotTint = _ref.headshotTint;
258
231
  var options = {
259
232
  tint: "".concat(headshotTint || DEFAULT_TINT)
260
233
  };
@@ -269,13 +242,11 @@ var Headshot = (function (_ref) {
269
242
  });
270
243
 
271
244
  var _excluded = ["customElements"];
272
-
273
245
  var BaseLink = function BaseLink(_ref) {
274
246
  var url = _ref.url,
275
- _ref$attrs = _ref.attrs,
276
- attrs = _ref$attrs === void 0 ? {} : _ref$attrs,
277
- children = _ref.children;
278
-
247
+ _ref$attrs = _ref.attrs,
248
+ attrs = _ref$attrs === void 0 ? {} : _ref$attrs,
249
+ children = _ref.children;
279
250
  if (url) {
280
251
  return xEngine.h("a", Object.assign({
281
252
  href: url
@@ -284,35 +255,30 @@ var BaseLink = function BaseLink(_ref) {
284
255
  return xEngine.h("span", attrs, children);
285
256
  }
286
257
  };
287
-
288
258
  var Link = (function (_ref2) {
289
259
  var _ref2$customElements = _ref2.customElements,
290
- customElements = _ref2$customElements === void 0 ? {} : _ref2$customElements,
291
- props = _objectWithoutProperties(_ref2, _excluded);
292
-
260
+ customElements = _ref2$customElements === void 0 ? {} : _ref2$customElements,
261
+ props = _objectWithoutProperties(_ref2, _excluded);
293
262
  var Link = customElements.Link || BaseLink;
294
263
  return xEngine.h(Link, props);
295
264
  });
296
265
 
297
266
  var _excluded$1 = ["relativeUrl", "url", "image", "imageSize", "imageLazyLoad", "imageHighestQuality"];
267
+
298
268
  /**
299
269
  * Aspect Ratio
300
270
  * @param {{ width: Number, height: Number }} image
301
271
  * @returns {String|null}
302
272
  */
303
-
304
273
  var aspectRatio = function aspectRatio(_ref) {
305
274
  var width = _ref.width,
306
- height = _ref.height;
307
-
275
+ height = _ref.height;
308
276
  if (typeof width === 'number' && typeof height === 'number') {
309
277
  var ratio = 100 / width * height;
310
278
  return ratio.toFixed(4) + '%';
311
279
  }
312
-
313
280
  return null;
314
281
  };
315
-
316
282
  var NormalImage = function NormalImage(_ref2) {
317
283
  var src = _ref2.src;
318
284
  return xEngine.h("img", {
@@ -321,10 +287,9 @@ var NormalImage = function NormalImage(_ref2) {
321
287
  alt: ""
322
288
  });
323
289
  };
324
-
325
290
  var LazyImage = function LazyImage(_ref3) {
326
291
  var src = _ref3.src,
327
- lazyLoad = _ref3.lazyLoad;
292
+ lazyLoad = _ref3.lazyLoad;
328
293
  var lazyClassName = typeof lazyLoad === 'string' ? lazyLoad : '';
329
294
  return xEngine.h("img", {
330
295
  className: "o-teaser__image ".concat(lazyClassName),
@@ -332,20 +297,17 @@ var LazyImage = function LazyImage(_ref3) {
332
297
  alt: ""
333
298
  });
334
299
  };
335
-
336
300
  var Image = (function (_ref4) {
337
301
  var relativeUrl = _ref4.relativeUrl,
338
- url = _ref4.url,
339
- image = _ref4.image,
340
- imageSize = _ref4.imageSize,
341
- imageLazyLoad = _ref4.imageLazyLoad,
342
- imageHighestQuality = _ref4.imageHighestQuality,
343
- props = _objectWithoutProperties(_ref4, _excluded$1);
344
-
302
+ url = _ref4.url,
303
+ image = _ref4.image,
304
+ imageSize = _ref4.imageSize,
305
+ imageLazyLoad = _ref4.imageLazyLoad,
306
+ imageHighestQuality = _ref4.imageHighestQuality,
307
+ props = _objectWithoutProperties(_ref4, _excluded$1);
345
308
  if (!image || image && !image.url) {
346
309
  return null;
347
310
  }
348
-
349
311
  var displayUrl = relativeUrl || url;
350
312
  var useImageService = !(image.url.startsWith('data:') || image.url.startsWith('blob:'));
351
313
  var options = imageSize === 'XXL' && imageHighestQuality ? {
@@ -378,19 +340,17 @@ var sameId = function sameId() {
378
340
  var id = arguments.length > 1 ? arguments[1] : undefined;
379
341
  return id && context && context.parentId && id === context.parentId;
380
342
  };
381
-
382
343
  var sameLabel = function sameLabel() {
383
344
  var context = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
384
345
  var label = arguments.length > 1 ? arguments[1] : undefined;
385
346
  return label && context && context.parentLabel && label === context.parentLabel;
386
347
  };
387
-
388
348
  var MetaLink = (function (_ref) {
389
349
  var metaPrefixText = _ref.metaPrefixText,
390
- metaLink = _ref.metaLink,
391
- metaAltLink = _ref.metaAltLink,
392
- metaSuffixText = _ref.metaSuffixText,
393
- context = _ref.context;
350
+ metaLink = _ref.metaLink,
351
+ metaAltLink = _ref.metaAltLink,
352
+ metaSuffixText = _ref.metaSuffixText,
353
+ context = _ref.context;
394
354
  var showPrefixText = metaPrefixText && !sameLabel(context, metaPrefixText);
395
355
  var showSuffixText = metaSuffixText && !sameLabel(context, metaSuffixText);
396
356
  var linkId = metaLink && metaLink.id;
@@ -413,7 +373,7 @@ var MetaLink = (function (_ref) {
413
373
 
414
374
  var Promoted = (function (_ref) {
415
375
  var promotedPrefixText = _ref.promotedPrefixText,
416
- promotedSuffixText = _ref.promotedSuffixText;
376
+ promotedSuffixText = _ref.promotedSuffixText;
417
377
  return xEngine.h("div", {
418
378
  className: "o-teaser__meta"
419
379
  }, xEngine.h("span", {
@@ -430,10 +390,10 @@ var Meta = (function (props) {
430
390
 
431
391
  var renderLink = function renderLink(_ref, i) {
432
392
  var id = _ref.id,
433
- type = _ref.type,
434
- title = _ref.title,
435
- url = _ref.url,
436
- relativeUrl = _ref.relativeUrl;
393
+ type = _ref.type,
394
+ title = _ref.title,
395
+ url = _ref.url,
396
+ relativeUrl = _ref.relativeUrl;
437
397
  var displayUrl = relativeUrl || url;
438
398
  return xEngine.h("li", {
439
399
  key: "related-".concat(i),
@@ -444,10 +404,9 @@ var renderLink = function renderLink(_ref, i) {
444
404
  href: displayUrl
445
405
  }, title));
446
406
  };
447
-
448
407
  var RelatedLinks = (function (_ref2) {
449
408
  var _ref2$relatedLinks = _ref2.relatedLinks,
450
- relatedLinks = _ref2$relatedLinks === void 0 ? [] : _ref2$relatedLinks;
409
+ relatedLinks = _ref2$relatedLinks === void 0 ? [] : _ref2$relatedLinks;
451
410
  return relatedLinks && relatedLinks.length ? xEngine.h("ul", {
452
411
  className: "o-teaser__related"
453
412
  }, relatedLinks.map(renderLink)) : null;
@@ -468,34 +427,31 @@ var TimeStamp = (function (_ref) {
468
427
  * @param {Date | String | Number} date
469
428
  * @returns {Date}
470
429
  */
471
-
472
430
  function toDate(date) {
473
431
  if (typeof date === 'string') {
474
432
  return new Date(date);
475
433
  }
476
-
477
434
  if (typeof date === 'number') {
478
435
  return new Date(date);
479
436
  }
480
-
481
437
  return date;
482
438
  }
439
+
483
440
  /**
484
441
  * Get Relative Date
485
442
  * @param {Date | String | Number} date
486
443
  * @returns {Number}
487
444
  */
488
-
489
445
  function getRelativeDate(date) {
490
446
  return Date.now() - toDate(date).getTime();
491
447
  }
448
+
492
449
  /**
493
450
  * Get Status
494
451
  * @param {Date | String | Number} publishedDate
495
452
  * @param {Date | String | Number} firstPublishedDate
496
453
  * @returns {String}
497
454
  */
498
-
499
455
  function getStatus(publishedDate, firstPublishedDate) {
500
456
  if (getRelativeDate(publishedDate) < Newish) {
501
457
  if (publishedDate === firstPublishedDate) {
@@ -504,15 +460,14 @@ function getStatus(publishedDate, firstPublishedDate) {
504
460
  return 'updated';
505
461
  }
506
462
  }
507
-
508
463
  return '';
509
464
  }
465
+
510
466
  /**
511
467
  * Is Recent
512
468
  * @param {Number} relativeDate
513
469
  * @returns {Boolean}
514
470
  */
515
-
516
471
  function isRecent(relativeDate) {
517
472
  return relativeDate < Recent;
518
473
  }
@@ -522,19 +477,17 @@ function isRecent(relativeDate) {
522
477
  * @param {Number} date
523
478
  * @returns {String}
524
479
  */
525
-
526
480
  var displayTime = function displayTime(date) {
527
481
  var hours = Math.floor(Math.abs(date / 3600000));
528
482
  var plural = hours === 1 ? 'hour' : 'hours';
529
483
  var suffix = hours === 0 ? '' : "".concat(plural, " ago");
530
484
  return "".concat(hours, " ").concat(suffix);
531
485
  };
532
-
533
486
  var RelativeTime = (function (_ref) {
534
487
  var publishedDate = _ref.publishedDate,
535
- firstPublishedDate = _ref.firstPublishedDate,
536
- _ref$showAlways = _ref.showAlways,
537
- showAlways = _ref$showAlways === void 0 ? false : _ref$showAlways;
488
+ firstPublishedDate = _ref.firstPublishedDate,
489
+ _ref$showAlways = _ref.showAlways,
490
+ showAlways = _ref$showAlways === void 0 ? false : _ref$showAlways;
538
491
  var relativeDate = getRelativeDate(publishedDate);
539
492
  var status = getStatus(publishedDate, firstPublishedDate);
540
493
  return showAlways === true || isRecent(relativeDate) ? xEngine.h("div", {
@@ -573,12 +526,9 @@ var LiveBlogStatus = (function (_ref) {
573
526
  * If same calendar day, we show relative time e.g. X hours ago or Updated X min ago
574
527
  * If different calendar day, we show full Date time e.g. June 9, 2021
575
528
  */
576
-
577
529
  var AlwaysShowTimestamp = (function (props) {
578
530
  var localTodayDate = new Date().toISOString().substr(0, 10); // keep only the date bit
579
-
580
531
  var dateToCompare = new Date(props.publishedDate).toISOString().substr(0, 10);
581
-
582
532
  if (dateFns.differenceInCalendarDays(localTodayDate, dateToCompare) >= 1) {
583
533
  return xEngine.h(TimeStamp, props);
584
534
  } else {
@@ -592,7 +542,6 @@ var Status = (function (props) {
592
542
  if (props.status) {
593
543
  return xEngine.h(LiveBlogStatus, props);
594
544
  }
595
-
596
545
  if (props.publishedDate) {
597
546
  if (props.useRelativeTimeIfToday) {
598
547
  return xEngine.h(AlwaysShowTimestamp, props);
@@ -602,19 +551,17 @@ var Status = (function (props) {
602
551
  return xEngine.h(TimeStamp, props);
603
552
  }
604
553
  }
605
-
606
554
  return null;
607
555
  });
608
556
 
609
557
  var _excluded$2 = ["standfirst", "altStandfirst", "headlineTesting", "relativeUrl", "url"];
610
558
  var Standfirst = (function (_ref) {
611
559
  var standfirst = _ref.standfirst,
612
- altStandfirst = _ref.altStandfirst,
613
- headlineTesting = _ref.headlineTesting,
614
- relativeUrl = _ref.relativeUrl,
615
- url = _ref.url,
616
- props = _objectWithoutProperties(_ref, _excluded$2);
617
-
560
+ altStandfirst = _ref.altStandfirst,
561
+ headlineTesting = _ref.headlineTesting,
562
+ relativeUrl = _ref.relativeUrl,
563
+ url = _ref.url,
564
+ props = _objectWithoutProperties(_ref, _excluded$2);
618
565
  var displayStandfirst = headlineTesting && altStandfirst ? altStandfirst : standfirst;
619
566
  var displayUrl = relativeUrl || url;
620
567
  return displayStandfirst ? xEngine.h("p", {
@@ -632,25 +579,22 @@ var Standfirst = (function (_ref) {
632
579
  var _excluded$3 = ["title", "altTitle", "headlineTesting", "relativeUrl", "url", "indicators"];
633
580
  var Title = (function (_ref) {
634
581
  var title = _ref.title,
635
- altTitle = _ref.altTitle,
636
- headlineTesting = _ref.headlineTesting,
637
- relativeUrl = _ref.relativeUrl,
638
- url = _ref.url,
639
- indicators = _ref.indicators,
640
- props = _objectWithoutProperties(_ref, _excluded$3);
641
-
582
+ altTitle = _ref.altTitle,
583
+ headlineTesting = _ref.headlineTesting,
584
+ relativeUrl = _ref.relativeUrl,
585
+ url = _ref.url,
586
+ indicators = _ref.indicators,
587
+ props = _objectWithoutProperties(_ref, _excluded$3);
642
588
  var displayTitle = headlineTesting && altTitle ? altTitle : title;
643
- var displayUrl = relativeUrl || url; // o-labels--premium left for backwards compatibility for o-labels v3
644
-
589
+ var displayUrl = relativeUrl || url;
590
+ // o-labels--premium left for backwards compatibility for o-labels v3
645
591
  var premiumClass = 'o-labels o-labels--premium o-labels--content-premium';
646
592
  var ariaLabel;
647
-
648
593
  if (props.type === 'video') {
649
594
  ariaLabel = "Watch video ".concat(displayTitle);
650
595
  } else if (props.type === 'audio') {
651
596
  ariaLabel = "Listen to podcast ".concat(displayTitle);
652
597
  }
653
-
654
598
  return xEngine.h("div", {
655
599
  className: "o-teaser__heading"
656
600
  }, xEngine.h(Link, Object.assign({}, props, {
@@ -667,15 +611,16 @@ var Title = (function (_ref) {
667
611
  }, "\xA0content")) : null);
668
612
  });
669
613
 
614
+ // Re-format the data for use with o-video
670
615
  var formatData = function formatData(props) {
671
616
  return JSON.stringify({
672
617
  renditions: [props.video],
673
618
  mainImageUrl: props.image ? props.image.url : null
674
619
  });
675
- }; // To prevent React from touching the DOM after mounting… return an empty <div />
676
- // <https://reactjs.org/docs/integrating-with-other-libraries.html>
677
-
620
+ };
678
621
 
622
+ // To prevent React from touching the DOM after mounting… return an empty <div />
623
+ // <https://reactjs.org/docs/integrating-with-other-libraries.html>
679
624
  var Embed = function Embed(props) {
680
625
  var showGuidance = typeof props.showGuidance === 'boolean' ? props.showGuidance.toString() : 'true';
681
626
  return xEngine.h("div", {
@@ -696,7 +641,6 @@ var Embed = function Embed(props) {
696
641
  "data-o-video-systemcode": props.systemCode
697
642
  }));
698
643
  };
699
-
700
644
  var Video = (function (props) {
701
645
  return xEngine.h("div", {
702
646
  className: "o-teaser__video"
@@ -12,11 +12,9 @@ const rulesets = {
12
12
  if (props.showVideo && props.video && props.video.url) {
13
13
  return 'video';
14
14
  }
15
-
16
15
  if (props.showHeadshot && props.headshot && props.indicators.isColumn) {
17
16
  return 'headshot';
18
17
  }
19
-
20
18
  if (props.showImage && props.image && props.image.url) {
21
19
  return 'image';
22
20
  }
@@ -25,31 +23,27 @@ const rulesets = {
25
23
  if (props.theme) {
26
24
  return props.theme;
27
25
  }
28
-
29
26
  if (props.status === 'inprogress') {
30
27
  return 'live';
31
28
  }
32
-
33
29
  if (props.indicators && props.indicators.isOpinion) {
34
30
  return 'opinion';
35
31
  }
36
-
37
32
  if (props.indicators && props.indicators.isEditorsChoice) {
38
33
  return 'highlight';
39
34
  }
40
-
41
35
  if (props.parentTheme) {
42
36
  return props.parentTheme;
43
37
  }
44
38
  }
45
39
  };
40
+
46
41
  /**
47
42
  * Rules
48
43
  * @param {String} rule
49
44
  * @param {Props} props
50
45
  * @returns {String|null}
51
46
  */
52
-
53
47
  function rules(rule, props) {
54
48
  if (rulesets.hasOwnProperty(rule)) {
55
49
  return rulesets[rule](props);
@@ -63,23 +57,18 @@ const theme = props => rules('theme', props);
63
57
  const dynamicModifiers = props => {
64
58
  const modifiers = [];
65
59
  const mediaRule = media(props);
66
-
67
60
  if (mediaRule) {
68
61
  modifiers.push(`has-${mediaRule}`);
69
62
  }
70
-
71
63
  const themeRule = theme(props);
72
-
73
64
  if (themeRule) {
74
65
  modifiers.push(themeRule);
75
66
  }
76
-
77
67
  return modifiers;
78
68
  };
79
-
80
69
  var Container = (props => {
81
- const computed = dynamicModifiers(props); // Modifier props may be a string rather than a string[] so concat, don't spread.
82
-
70
+ const computed = dynamicModifiers(props);
71
+ // Modifier props may be a string rather than a string[] so concat, don't spread.
83
72
  const variants = [props.type, props.layout].concat(props.modifiers, computed);
84
73
  const classNames = variants.filter(Boolean).map(mod => `o-teaser--${mod}`).join(' ');
85
74
  return h("div", {
@@ -100,7 +89,6 @@ var Content = (({
100
89
  * @param {String|ReactElement} action
101
90
  * @returns {ReactElement}
102
91
  */
103
-
104
92
  const render = action => {
105
93
  // Allow parent components to pass raw HTML strings
106
94
  if (typeof action === 'string') {
@@ -113,7 +101,6 @@ const render = action => {
113
101
  return action;
114
102
  }
115
103
  };
116
-
117
104
  var CustomSlot = (({
118
105
  customSlot
119
106
  }) => customSlot ? h("div", {
@@ -128,8 +115,8 @@ const ImageSizes = {
128
115
  Large: 420,
129
116
  XL: 640,
130
117
  XXL: 1180 // max width of FT.com page
131
-
132
118
  };
119
+
133
120
  const Layouts = {
134
121
  Small: 'small',
135
122
  Large: 'large',
@@ -145,22 +132,24 @@ const OPTIONS = {
145
132
  fit: 'scale-down',
146
133
  dpr: 2
147
134
  };
135
+
148
136
  /**
149
137
  * Image Service
150
138
  * @param {String} url
151
139
  * @param {Number} width
152
140
  * @param {String} options
153
141
  */
154
-
155
142
  function imageService(url, width, options) {
156
143
  const imageSrc = new URL(`${BASE_URL}/${encodeURIComponent(url)}`);
157
- imageSrc.search = new URLSearchParams({ ...OPTIONS,
144
+ imageSrc.search = new URLSearchParams({
145
+ ...OPTIONS,
158
146
  ...options
159
147
  });
160
148
  imageSrc.searchParams.set('width', width);
161
149
  return imageSrc.href;
162
150
  }
163
151
 
152
+ // these colours are tweaked from o-colors palette colours to make headshots look less washed out
164
153
  const DEFAULT_TINT = '054593,d6d5d3';
165
154
  var Headshot = (({
166
155
  headshot,
@@ -192,7 +181,6 @@ const BaseLink = ({
192
181
  return h("span", attrs, children);
193
182
  }
194
183
  };
195
-
196
184
  var Link = (({
197
185
  customElements = {},
198
186
  ...props
@@ -206,7 +194,6 @@ var Link = (({
206
194
  * @param {{ width: Number, height: Number }} image
207
195
  * @returns {String|null}
208
196
  */
209
-
210
197
  const aspectRatio = ({
211
198
  width,
212
199
  height
@@ -215,10 +202,8 @@ const aspectRatio = ({
215
202
  const ratio = 100 / width * height;
216
203
  return ratio.toFixed(4) + '%';
217
204
  }
218
-
219
205
  return null;
220
206
  };
221
-
222
207
  const NormalImage = ({
223
208
  src
224
209
  }) => h("img", {
@@ -226,7 +211,6 @@ const NormalImage = ({
226
211
  src: src,
227
212
  alt: ""
228
213
  });
229
-
230
214
  const LazyImage = ({
231
215
  src,
232
216
  lazyLoad
@@ -238,7 +222,6 @@ const LazyImage = ({
238
222
  alt: ""
239
223
  });
240
224
  };
241
-
242
225
  var Image = (({
243
226
  relativeUrl,
244
227
  url,
@@ -251,7 +234,6 @@ var Image = (({
251
234
  if (!image || image && !image.url) {
252
235
  return null;
253
236
  }
254
-
255
237
  const displayUrl = relativeUrl || url;
256
238
  const useImageService = !(image.url.startsWith('data:') || image.url.startsWith('blob:'));
257
239
  const options = imageSize === 'XXL' && imageHighestQuality ? {
@@ -282,11 +264,9 @@ var Image = (({
282
264
  const sameId = (context = {}, id) => {
283
265
  return id && context && context.parentId && id === context.parentId;
284
266
  };
285
-
286
267
  const sameLabel = (context = {}, label) => {
287
268
  return label && context && context.parentLabel && label === context.parentLabel;
288
269
  };
289
-
290
270
  var MetaLink = (({
291
271
  metaPrefixText,
292
272
  metaLink,
@@ -347,7 +327,6 @@ const renderLink = ({
347
327
  href: displayUrl
348
328
  }, title));
349
329
  };
350
-
351
330
  var RelatedLinks = (({
352
331
  relatedLinks = []
353
332
  }) => relatedLinks && relatedLinks.length ? h("ul", {
@@ -368,34 +347,31 @@ var TimeStamp = (({
368
347
  * @param {Date | String | Number} date
369
348
  * @returns {Date}
370
349
  */
371
-
372
350
  function toDate(date) {
373
351
  if (typeof date === 'string') {
374
352
  return new Date(date);
375
353
  }
376
-
377
354
  if (typeof date === 'number') {
378
355
  return new Date(date);
379
356
  }
380
-
381
357
  return date;
382
358
  }
359
+
383
360
  /**
384
361
  * Get Relative Date
385
362
  * @param {Date | String | Number} date
386
363
  * @returns {Number}
387
364
  */
388
-
389
365
  function getRelativeDate(date) {
390
366
  return Date.now() - toDate(date).getTime();
391
367
  }
368
+
392
369
  /**
393
370
  * Get Status
394
371
  * @param {Date | String | Number} publishedDate
395
372
  * @param {Date | String | Number} firstPublishedDate
396
373
  * @returns {String}
397
374
  */
398
-
399
375
  function getStatus(publishedDate, firstPublishedDate) {
400
376
  if (getRelativeDate(publishedDate) < Newish) {
401
377
  if (publishedDate === firstPublishedDate) {
@@ -404,15 +380,14 @@ function getStatus(publishedDate, firstPublishedDate) {
404
380
  return 'updated';
405
381
  }
406
382
  }
407
-
408
383
  return '';
409
384
  }
385
+
410
386
  /**
411
387
  * Is Recent
412
388
  * @param {Number} relativeDate
413
389
  * @returns {Boolean}
414
390
  */
415
-
416
391
  function isRecent(relativeDate) {
417
392
  return relativeDate < Recent;
418
393
  }
@@ -422,14 +397,12 @@ function isRecent(relativeDate) {
422
397
  * @param {Number} date
423
398
  * @returns {String}
424
399
  */
425
-
426
400
  const displayTime = date => {
427
401
  const hours = Math.floor(Math.abs(date / 3600000));
428
402
  const plural = hours === 1 ? 'hour' : 'hours';
429
403
  const suffix = hours === 0 ? '' : `${plural} ago`;
430
404
  return `${hours} ${suffix}`;
431
405
  };
432
-
433
406
  var RelativeTime = (({
434
407
  publishedDate,
435
408
  firstPublishedDate,
@@ -472,12 +445,9 @@ var LiveBlogStatus = (({
472
445
  * If same calendar day, we show relative time e.g. X hours ago or Updated X min ago
473
446
  * If different calendar day, we show full Date time e.g. June 9, 2021
474
447
  */
475
-
476
448
  var AlwaysShowTimestamp = (props => {
477
449
  const localTodayDate = new Date().toISOString().substr(0, 10); // keep only the date bit
478
-
479
450
  const dateToCompare = new Date(props.publishedDate).toISOString().substr(0, 10);
480
-
481
451
  if (differenceInCalendarDays(localTodayDate, dateToCompare) >= 1) {
482
452
  return h(TimeStamp, props);
483
453
  } else {
@@ -491,7 +461,6 @@ var Status = (props => {
491
461
  if (props.status) {
492
462
  return h(LiveBlogStatus, props);
493
463
  }
494
-
495
464
  if (props.publishedDate) {
496
465
  if (props.useRelativeTimeIfToday) {
497
466
  return h(AlwaysShowTimestamp, props);
@@ -501,7 +470,6 @@ var Status = (props => {
501
470
  return h(TimeStamp, props);
502
471
  }
503
472
  }
504
-
505
473
  return null;
506
474
  });
507
475
 
@@ -537,17 +505,15 @@ var Title = (({
537
505
  ...props
538
506
  }) => {
539
507
  const displayTitle = headlineTesting && altTitle ? altTitle : title;
540
- const displayUrl = relativeUrl || url; // o-labels--premium left for backwards compatibility for o-labels v3
541
-
508
+ const displayUrl = relativeUrl || url;
509
+ // o-labels--premium left for backwards compatibility for o-labels v3
542
510
  const premiumClass = 'o-labels o-labels--premium o-labels--content-premium';
543
511
  let ariaLabel;
544
-
545
512
  if (props.type === 'video') {
546
513
  ariaLabel = `Watch video ${displayTitle}`;
547
514
  } else if (props.type === 'audio') {
548
515
  ariaLabel = `Listen to podcast ${displayTitle}`;
549
516
  }
550
-
551
517
  return h("div", {
552
518
  className: "o-teaser__heading"
553
519
  }, h(Link, Object.assign({}, props, {
@@ -564,13 +530,14 @@ var Title = (({
564
530
  }, "\xA0content")) : null);
565
531
  });
566
532
 
533
+ // Re-format the data for use with o-video
567
534
  const formatData = props => JSON.stringify({
568
535
  renditions: [props.video],
569
536
  mainImageUrl: props.image ? props.image.url : null
570
- }); // To prevent React from touching the DOM after mounting… return an empty <div />
571
- // <https://reactjs.org/docs/integrating-with-other-libraries.html>
572
-
537
+ });
573
538
 
539
+ // To prevent React from touching the DOM after mounting… return an empty <div />
540
+ // <https://reactjs.org/docs/integrating-with-other-libraries.html>
574
541
  const Embed = props => {
575
542
  const showGuidance = typeof props.showGuidance === 'boolean' ? props.showGuidance.toString() : 'true';
576
543
  return h("div", {
@@ -591,7 +558,6 @@ const Embed = props => {
591
558
  "data-o-video-systemcode": props.systemCode
592
559
  }));
593
560
  };
594
-
595
561
  var Video = (props => h("div", {
596
562
  className: "o-teaser__video"
597
563
  }, h("div", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/x-teaser",
3
- "version": "8.1.0",
3
+ "version": "8.2.0",
4
4
  "description": "This module provides templates for use with o-teaser. Teasers are used to present content.",
5
5
  "source": "src/Teaser.jsx",
6
6
  "main": "dist/Teaser.cjs.js",
@@ -18,7 +18,7 @@
18
18
  "author": "",
19
19
  "license": "ISC",
20
20
  "dependencies": {
21
- "@financial-times/x-engine": "^8.1.0",
21
+ "@financial-times/x-engine": "^8.2.0",
22
22
  "date-fns": "^1.29.0",
23
23
  "dateformat": "^3.0.3"
24
24
  },