@financial-times/x-teaser 18.2.1 → 19.0.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/Props.d.ts CHANGED
@@ -31,7 +31,7 @@ export interface Features {
31
31
  showTitle?: boolean
32
32
  showStandfirst?: boolean
33
33
  showPremiumLabel?: boolean
34
- showScoopLabel?: boolean
34
+ showExclusiveLabel?: boolean
35
35
  showStatus?: boolean
36
36
  showImage?: boolean
37
37
  showHeadshot?: boolean
@@ -159,6 +159,7 @@ export interface Indicators {
159
159
  isPodcast?: boolean
160
160
  /** Methode packaging options */
161
161
  isEditorsChoice?: boolean
162
+ /** Controls whether the Exclusive badge is displayed */
162
163
  isExclusive?: boolean
163
164
  isScoop?: boolean
164
165
  }
@@ -2,7 +2,7 @@ const { h } = require('@financial-times/x-engine')
2
2
  const { mount } = require('@financial-times/x-test-utils/enzyme')
3
3
 
4
4
  import * as LiveBlogStatus from '../src/LiveBlogStatus'
5
- import * as ScoopLabel from '../src/ScoopLabel'
5
+ import * as ExclusiveLabel from '../src/ExclusiveLabel'
6
6
  import * as PremiumLabel from '../src/PremiumLabel'
7
7
  import * as AlwaysShowTimestamp from '../src/AlwaysShowTimestamp'
8
8
  import * as RelativeTime from '../src/RelativeTime'
@@ -11,9 +11,9 @@ import * as TimeStamp from '../src/TimeStamp'
11
11
  const LiveBlogStatusSpy = jest
12
12
  .spyOn(LiveBlogStatus, 'default')
13
13
  .mockReturnValue(<div className="live-blog-status">LiveBlogStatus</div>)
14
- const ScoopLabelSpy = jest
15
- .spyOn(ScoopLabel, 'default')
16
- .mockReturnValue(<div className="scoop-label">ScoopLabel</div>)
14
+ const ExclusiveLabelSpy = jest
15
+ .spyOn(ExclusiveLabel, 'default')
16
+ .mockReturnValue(<div className="exclusive-label">ExclusiveLabel</div>)
17
17
  const PremiumLabelSpy = jest
18
18
  .spyOn(PremiumLabel, 'default')
19
19
  .mockReturnValue(<div className="premium-label">PremiumLabel</div>)
@@ -35,9 +35,9 @@ describe('Status - Display Logic', () => {
35
35
  props = {
36
36
  showStatus: true,
37
37
  showPremiumLabel: true,
38
- showScoopLabel: true,
38
+ showExclusiveLabel: true,
39
39
  status: 'inprogress',
40
- indicators: { isScoop: true, accessLevel: 'premium' },
40
+ indicators: { isExclusive: true, accessLevel: 'premium' },
41
41
  firstPublishedDate: '2025-10-01T00:00:00.000Z',
42
42
  publishedDate: '2025-10-01T00:00:00.000Z',
43
43
  useRelativeTimeIfToday: true,
@@ -54,7 +54,7 @@ describe('Status - Display Logic', () => {
54
54
  mount(<Status {...props} />)
55
55
 
56
56
  expect(LiveBlogStatusSpy).toHaveBeenCalledTimes(1)
57
- expect(ScoopLabelSpy).not.toHaveBeenCalled()
57
+ expect(ExclusiveLabelSpy).not.toHaveBeenCalled()
58
58
  expect(PremiumLabelSpy).not.toHaveBeenCalled()
59
59
  expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
60
60
  expect(RelativeTimeSpy).not.toHaveBeenCalled()
@@ -76,15 +76,15 @@ describe('Status - Display Logic', () => {
76
76
  })
77
77
  })
78
78
 
79
- describe('ScoopLabel', () => {
79
+ describe('ExclusiveLabel', () => {
80
80
  beforeEach(() => {
81
81
  delete props.status
82
82
  })
83
83
 
84
- it('renders only ScoopLabel when higher-level render props are absent and ScoopLabel props are present', () => {
84
+ it('renders only ExclusiveLabel when higher-level render props are absent and ExclusiveLabel props are present', () => {
85
85
  mount(<Status {...props} />)
86
86
 
87
- expect(ScoopLabelSpy).toHaveBeenCalledTimes(1)
87
+ expect(ExclusiveLabelSpy).toHaveBeenCalledTimes(1)
88
88
  expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
89
89
  expect(PremiumLabelSpy).not.toHaveBeenCalled()
90
90
  expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
@@ -92,32 +92,25 @@ describe('Status - Display Logic', () => {
92
92
  expect(TimeStampSpy).not.toHaveBeenCalled()
93
93
  })
94
94
 
95
- it('should not render ScoopLabel when showScoopLabel = false', () => {
96
- props.showScoopLabel = false
95
+ it('should not render ExclusiveLabel when showExclusiveLabel = false', () => {
96
+ props.showExclusiveLabel = false
97
97
  mount(<Status {...props} />)
98
98
 
99
- expect(ScoopLabelSpy).not.toHaveBeenCalled()
99
+ expect(ExclusiveLabelSpy).not.toHaveBeenCalled()
100
100
  })
101
101
 
102
- it('should not render ScoopLabel when props.indicators.isScoop = false', () => {
103
- props.indicators.isScoop = false
102
+ it('should not render ExclusiveLabel when props.indicators.isExclusive = false', () => {
103
+ props.indicators.isExclusive = false
104
104
  mount(<Status {...props} />)
105
105
 
106
- expect(ScoopLabelSpy).not.toHaveBeenCalled()
107
- })
108
-
109
- it('should not render ScoopLabel when firstPublishedDate is older than the cutoff date', () => {
110
- props.firstPublishedDate = '2025-09-01T00:00:00.000Z'
111
- mount(<Status {...props} />)
112
-
113
- expect(ScoopLabelSpy).not.toHaveBeenCalled()
106
+ expect(ExclusiveLabelSpy).not.toHaveBeenCalled()
114
107
  })
115
108
  })
116
109
 
117
110
  describe('PremiumLabel', () => {
118
111
  beforeEach(() => {
119
112
  delete props.status
120
- props.showScoopLabel = false
113
+ props.showExclusiveLabel = false
121
114
  })
122
115
 
123
116
  it('renders only PremiumLabel when higher-level render props are absent and PremiumLabel props are present', () => {
@@ -125,7 +118,7 @@ describe('Status - Display Logic', () => {
125
118
 
126
119
  expect(PremiumLabelSpy).toHaveBeenCalledTimes(1)
127
120
  expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
128
- expect(ScoopLabelSpy).not.toHaveBeenCalled()
121
+ expect(ExclusiveLabelSpy).not.toHaveBeenCalled()
129
122
  expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
130
123
  })
131
124
 
@@ -147,7 +140,7 @@ describe('Status - Display Logic', () => {
147
140
  describe('AlwaysShowTimestamp', () => {
148
141
  beforeEach(() => {
149
142
  delete props.status
150
- props.showScoopLabel = false
143
+ props.showExclusiveLabel = false
151
144
  props.showPremiumLabel = false
152
145
  })
153
146
 
@@ -159,7 +152,7 @@ describe('Status - Display Logic', () => {
159
152
 
160
153
  expect(AlwaysShowTimestampSpy).toHaveBeenCalledTimes(1)
161
154
  expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
162
- expect(ScoopLabelSpy).not.toHaveBeenCalled()
155
+ expect(ExclusiveLabelSpy).not.toHaveBeenCalled()
163
156
  expect(PremiumLabelSpy).not.toHaveBeenCalled()
164
157
  expect(RelativeTimeSpy).not.toHaveBeenCalled()
165
158
  expect(TimeStampSpy).not.toHaveBeenCalled()
@@ -211,7 +204,7 @@ describe('Status - Display Logic', () => {
211
204
  describe('RelativeTime', () => {
212
205
  beforeEach(() => {
213
206
  delete props.status
214
- props.showScoopLabel = false
207
+ props.showExclusiveLabel = false
215
208
  props.showPremiumLabel = false
216
209
  props.useRelativeTimeIfToday = false
217
210
  })
@@ -221,7 +214,7 @@ describe('Status - Display Logic', () => {
221
214
 
222
215
  expect(RelativeTimeSpy).toHaveBeenCalledTimes(1)
223
216
  expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
224
- expect(ScoopLabelSpy).not.toHaveBeenCalled()
217
+ expect(ExclusiveLabelSpy).not.toHaveBeenCalled()
225
218
  expect(PremiumLabelSpy).not.toHaveBeenCalled()
226
219
  expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
227
220
  expect(TimeStampSpy).not.toHaveBeenCalled()
@@ -252,7 +245,7 @@ describe('Status - Display Logic', () => {
252
245
  describe('TimeStamp', () => {
253
246
  beforeEach(() => {
254
247
  delete props.status
255
- props.showScoopLabel = false
248
+ props.showExclusiveLabel = false
256
249
  props.showPremiumLabel = false
257
250
  props.useRelativeTimeIfToday = false
258
251
  props.useRelativeTime = false
@@ -263,7 +256,7 @@ describe('Status - Display Logic', () => {
263
256
 
264
257
  expect(TimeStampSpy).toHaveBeenCalledTimes(1)
265
258
  expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
266
- expect(ScoopLabelSpy).not.toHaveBeenCalled()
259
+ expect(ExclusiveLabelSpy).not.toHaveBeenCalled()
267
260
  expect(PremiumLabelSpy).not.toHaveBeenCalled()
268
261
  expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
269
262
  expect(RelativeTimeSpy).not.toHaveBeenCalled()
@@ -289,9 +282,9 @@ describe('Status - Display Logic', () => {
289
282
  props = {
290
283
  showStatus: false,
291
284
  showPremiumLabel: false,
292
- showScoopLabel: false,
285
+ showExclusiveLabel: false,
293
286
  status: 'inprogress',
294
- indicators: { isScoop: true, accessLevel: 'premium' },
287
+ indicators: { isExclusive: true, accessLevel: 'premium' },
295
288
  firstPublishedDate: '2025-10-01T00:00:00.000Z',
296
289
  publishedDate: '2025-10-01T00:00:00.000Z',
297
290
  useRelativeTimeIfToday: true,
@@ -301,7 +294,7 @@ describe('Status - Display Logic', () => {
301
294
 
302
295
  expect(subject.isEmptyRender()).toBeTruthy()
303
296
  expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
304
- expect(ScoopLabelSpy).not.toHaveBeenCalled()
297
+ expect(ExclusiveLabelSpy).not.toHaveBeenCalled()
305
298
  expect(PremiumLabelSpy).not.toHaveBeenCalled()
306
299
  expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
307
300
  expect(RelativeTimeSpy).not.toHaveBeenCalled()
@@ -313,8 +306,8 @@ describe('Status - Display Logic', () => {
313
306
  props = {
314
307
  showStatus: true,
315
308
  showPremiumLabel: false,
316
- showScoopLabel: false,
317
- indicators: { isScoop: true, accessLevel: 'premium' },
309
+ showExclusiveLabel: false,
310
+ indicators: { isExclusive: true, accessLevel: 'premium' },
318
311
  firstPublishedDate: '2025-10-01T00:00:00.000Z',
319
312
  useRelativeTimeIfToday: true,
320
313
  useRelativeTime: true
@@ -323,7 +316,7 @@ describe('Status - Display Logic', () => {
323
316
 
324
317
  expect(subject.isEmptyRender()).toBeTruthy()
325
318
  expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
326
- expect(ScoopLabelSpy).not.toHaveBeenCalled()
319
+ expect(ExclusiveLabelSpy).not.toHaveBeenCalled()
327
320
  expect(PremiumLabelSpy).not.toHaveBeenCalled()
328
321
  expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
329
322
  expect(RelativeTimeSpy).not.toHaveBeenCalled()
@@ -499,11 +499,11 @@ function PremiumLabel() {
499
499
  );
500
500
  }
501
501
 
502
- function ScoopLabel() {
502
+ function ExclusiveLabel() {
503
503
  return xEngine.h("div", {
504
- className: "o-teaser__labels o-teaser__labels--scoop"
504
+ className: "o-teaser__labels o-teaser__labels--exclusive"
505
505
  }, xEngine.h("span", {
506
- className: "o-labels o-labels--content-scoop"
506
+ className: "o-labels o-labels--content-exclusive"
507
507
  }, "Exclusive"), xEngine.h("span", {
508
508
  className: "o3-visually-hidden"
509
509
  }, "\xA0content"));
@@ -514,12 +514,8 @@ var Status = props => {
514
514
  if (props.showStatus && props.status) {
515
515
  return xEngine.h(LiveBlogStatus, props);
516
516
  }
517
- if (props.showScoopLabel && props !== null && props !== void 0 && (_props$indicators = props.indicators) !== null && _props$indicators !== void 0 && _props$indicators.isScoop &&
518
- // We plan to show the Scoop label only on homepages.
519
- // If we later show it on other pages, this cutoff date will need review.
520
- // The `isScoop` property already exists, but Editorial will use it differently after 2025-10-01.
521
- new Date(props.firstPublishedDate) >= new Date('2025-10-01T00:00:00.000Z')) {
522
- return xEngine.h(ScoopLabel, props);
517
+ if (props.showExclusiveLabel && props !== null && props !== void 0 && (_props$indicators = props.indicators) !== null && _props$indicators !== void 0 && _props$indicators.isExclusive) {
518
+ return xEngine.h(ExclusiveLabel, props);
523
519
  }
524
520
  if (props.showPremiumLabel && (props === null || props === void 0 || (_props$indicators2 = props.indicators) === null || _props$indicators2 === void 0 ? void 0 : _props$indicators2.accessLevel) === 'premium') {
525
521
  return xEngine.h(PremiumLabel, props);
@@ -636,7 +632,7 @@ const Small = {
636
632
  showMeta: true,
637
633
  showTitle: true,
638
634
  showPremiumLabel: true,
639
- showScoopLabel: false,
635
+ showExclusiveLabel: false,
640
636
  showStatus: true
641
637
  };
642
638
  const SmallHeavy = {
@@ -646,7 +642,7 @@ const SmallHeavy = {
646
642
  showTitle: true,
647
643
  showStandfirst: true,
648
644
  showPremiumLabel: true,
649
- showScoopLabel: false,
645
+ showExclusiveLabel: false,
650
646
  showStatus: true,
651
647
  showImage: true,
652
648
  imageSize: 'Small'
@@ -658,7 +654,7 @@ const Large = {
658
654
  showTitle: true,
659
655
  showStandfirst: true,
660
656
  showPremiumLabel: true,
661
- showScoopLabel: false,
657
+ showExclusiveLabel: false,
662
658
  showStatus: true,
663
659
  showImage: true,
664
660
  imageSize: 'Medium'
@@ -669,7 +665,7 @@ const Hero = {
669
665
  showMeta: true,
670
666
  showTitle: true,
671
667
  showPremiumLabel: true,
672
- showScoopLabel: false,
668
+ showExclusiveLabel: false,
673
669
  showStatus: true,
674
670
  showImage: true,
675
671
  imageSize: 'Medium'
@@ -681,7 +677,7 @@ const HeroNarrow = {
681
677
  showTitle: true,
682
678
  showStandfirst: true,
683
679
  showPremiumLabel: true,
684
- showScoopLabel: false,
680
+ showExclusiveLabel: false,
685
681
  showStatus: true
686
682
  };
687
683
  const HeroVideo = {
@@ -698,7 +694,7 @@ const HeroOverlay = {
698
694
  showMeta: true,
699
695
  showTitle: true,
700
696
  showPremiumLabel: true,
701
- showScoopLabel: false,
697
+ showExclusiveLabel: false,
702
698
  showStatus: true,
703
699
  showImage: true,
704
700
  imageSize: 'XL',
@@ -711,7 +707,7 @@ const TopStory = {
711
707
  showTitle: true,
712
708
  showStandfirst: true,
713
709
  showPremiumLabel: true,
714
- showScoopLabel: false,
710
+ showExclusiveLabel: false,
715
711
  showStatus: true,
716
712
  showRelatedLinks: true
717
713
  };
@@ -722,7 +718,7 @@ const TopStoryLandscape = {
722
718
  showTitle: true,
723
719
  showStandfirst: true,
724
720
  showPremiumLabel: true,
725
- showScoopLabel: false,
721
+ showExclusiveLabel: false,
726
722
  showStatus: true,
727
723
  showImage: true,
728
724
  imageSize: 'XL',
@@ -584,11 +584,11 @@ function PremiumLabel() {
584
584
  );
585
585
  }
586
586
 
587
- function ScoopLabel() {
587
+ function ExclusiveLabel() {
588
588
  return xEngine.h("div", {
589
- className: "o-teaser__labels o-teaser__labels--scoop"
589
+ className: "o-teaser__labels o-teaser__labels--exclusive"
590
590
  }, xEngine.h("span", {
591
- className: "o-labels o-labels--content-scoop"
591
+ className: "o-labels o-labels--content-exclusive"
592
592
  }, "Exclusive"), xEngine.h("span", {
593
593
  className: "o3-visually-hidden"
594
594
  }, "\xA0content"));
@@ -599,12 +599,8 @@ var Status = (function (props) {
599
599
  if (props.showStatus && props.status) {
600
600
  return xEngine.h(LiveBlogStatus, props);
601
601
  }
602
- if (props.showScoopLabel && props !== null && props !== void 0 && (_props$indicators = props.indicators) !== null && _props$indicators !== void 0 && _props$indicators.isScoop &&
603
- // We plan to show the Scoop label only on homepages.
604
- // If we later show it on other pages, this cutoff date will need review.
605
- // The `isScoop` property already exists, but Editorial will use it differently after 2025-10-01.
606
- new Date(props.firstPublishedDate) >= new Date('2025-10-01T00:00:00.000Z')) {
607
- return xEngine.h(ScoopLabel, props);
602
+ if (props.showExclusiveLabel && props !== null && props !== void 0 && (_props$indicators = props.indicators) !== null && _props$indicators !== void 0 && _props$indicators.isExclusive) {
603
+ return xEngine.h(ExclusiveLabel, props);
608
604
  }
609
605
  if (props.showPremiumLabel && (props === null || props === void 0 || (_props$indicators2 = props.indicators) === null || _props$indicators2 === void 0 ? void 0 : _props$indicators2.accessLevel) === 'premium') {
610
606
  return xEngine.h(PremiumLabel, props);
@@ -726,7 +722,7 @@ var Small = {
726
722
  showMeta: true,
727
723
  showTitle: true,
728
724
  showPremiumLabel: true,
729
- showScoopLabel: false,
725
+ showExclusiveLabel: false,
730
726
  showStatus: true
731
727
  };
732
728
  var SmallHeavy = {
@@ -736,7 +732,7 @@ var SmallHeavy = {
736
732
  showTitle: true,
737
733
  showStandfirst: true,
738
734
  showPremiumLabel: true,
739
- showScoopLabel: false,
735
+ showExclusiveLabel: false,
740
736
  showStatus: true,
741
737
  showImage: true,
742
738
  imageSize: 'Small'
@@ -748,7 +744,7 @@ var Large = {
748
744
  showTitle: true,
749
745
  showStandfirst: true,
750
746
  showPremiumLabel: true,
751
- showScoopLabel: false,
747
+ showExclusiveLabel: false,
752
748
  showStatus: true,
753
749
  showImage: true,
754
750
  imageSize: 'Medium'
@@ -759,7 +755,7 @@ var Hero = {
759
755
  showMeta: true,
760
756
  showTitle: true,
761
757
  showPremiumLabel: true,
762
- showScoopLabel: false,
758
+ showExclusiveLabel: false,
763
759
  showStatus: true,
764
760
  showImage: true,
765
761
  imageSize: 'Medium'
@@ -771,7 +767,7 @@ var HeroNarrow = {
771
767
  showTitle: true,
772
768
  showStandfirst: true,
773
769
  showPremiumLabel: true,
774
- showScoopLabel: false,
770
+ showExclusiveLabel: false,
775
771
  showStatus: true
776
772
  };
777
773
  var HeroVideo = {
@@ -788,7 +784,7 @@ var HeroOverlay = {
788
784
  showMeta: true,
789
785
  showTitle: true,
790
786
  showPremiumLabel: true,
791
- showScoopLabel: false,
787
+ showExclusiveLabel: false,
792
788
  showStatus: true,
793
789
  showImage: true,
794
790
  imageSize: 'XL',
@@ -801,7 +797,7 @@ var TopStory = {
801
797
  showTitle: true,
802
798
  showStandfirst: true,
803
799
  showPremiumLabel: true,
804
- showScoopLabel: false,
800
+ showExclusiveLabel: false,
805
801
  showStatus: true,
806
802
  showRelatedLinks: true
807
803
  };
@@ -812,7 +808,7 @@ var TopStoryLandscape = {
812
808
  showTitle: true,
813
809
  showStandfirst: true,
814
810
  showPremiumLabel: true,
815
- showScoopLabel: false,
811
+ showExclusiveLabel: false,
816
812
  showStatus: true,
817
813
  showImage: true,
818
814
  imageSize: 'XL',
@@ -493,11 +493,11 @@ function PremiumLabel() {
493
493
  );
494
494
  }
495
495
 
496
- function ScoopLabel() {
496
+ function ExclusiveLabel() {
497
497
  return h("div", {
498
- className: "o-teaser__labels o-teaser__labels--scoop"
498
+ className: "o-teaser__labels o-teaser__labels--exclusive"
499
499
  }, h("span", {
500
- className: "o-labels o-labels--content-scoop"
500
+ className: "o-labels o-labels--content-exclusive"
501
501
  }, "Exclusive"), h("span", {
502
502
  className: "o3-visually-hidden"
503
503
  }, "\xA0content"));
@@ -508,12 +508,8 @@ var Status = props => {
508
508
  if (props.showStatus && props.status) {
509
509
  return h(LiveBlogStatus, props);
510
510
  }
511
- if (props.showScoopLabel && props !== null && props !== void 0 && (_props$indicators = props.indicators) !== null && _props$indicators !== void 0 && _props$indicators.isScoop &&
512
- // We plan to show the Scoop label only on homepages.
513
- // If we later show it on other pages, this cutoff date will need review.
514
- // The `isScoop` property already exists, but Editorial will use it differently after 2025-10-01.
515
- new Date(props.firstPublishedDate) >= new Date('2025-10-01T00:00:00.000Z')) {
516
- return h(ScoopLabel, props);
511
+ if (props.showExclusiveLabel && props !== null && props !== void 0 && (_props$indicators = props.indicators) !== null && _props$indicators !== void 0 && _props$indicators.isExclusive) {
512
+ return h(ExclusiveLabel, props);
517
513
  }
518
514
  if (props.showPremiumLabel && (props === null || props === void 0 || (_props$indicators2 = props.indicators) === null || _props$indicators2 === void 0 ? void 0 : _props$indicators2.accessLevel) === 'premium') {
519
515
  return h(PremiumLabel, props);
@@ -630,7 +626,7 @@ const Small = {
630
626
  showMeta: true,
631
627
  showTitle: true,
632
628
  showPremiumLabel: true,
633
- showScoopLabel: false,
629
+ showExclusiveLabel: false,
634
630
  showStatus: true
635
631
  };
636
632
  const SmallHeavy = {
@@ -640,7 +636,7 @@ const SmallHeavy = {
640
636
  showTitle: true,
641
637
  showStandfirst: true,
642
638
  showPremiumLabel: true,
643
- showScoopLabel: false,
639
+ showExclusiveLabel: false,
644
640
  showStatus: true,
645
641
  showImage: true,
646
642
  imageSize: 'Small'
@@ -652,7 +648,7 @@ const Large = {
652
648
  showTitle: true,
653
649
  showStandfirst: true,
654
650
  showPremiumLabel: true,
655
- showScoopLabel: false,
651
+ showExclusiveLabel: false,
656
652
  showStatus: true,
657
653
  showImage: true,
658
654
  imageSize: 'Medium'
@@ -663,7 +659,7 @@ const Hero = {
663
659
  showMeta: true,
664
660
  showTitle: true,
665
661
  showPremiumLabel: true,
666
- showScoopLabel: false,
662
+ showExclusiveLabel: false,
667
663
  showStatus: true,
668
664
  showImage: true,
669
665
  imageSize: 'Medium'
@@ -675,7 +671,7 @@ const HeroNarrow = {
675
671
  showTitle: true,
676
672
  showStandfirst: true,
677
673
  showPremiumLabel: true,
678
- showScoopLabel: false,
674
+ showExclusiveLabel: false,
679
675
  showStatus: true
680
676
  };
681
677
  const HeroVideo = {
@@ -692,7 +688,7 @@ const HeroOverlay = {
692
688
  showMeta: true,
693
689
  showTitle: true,
694
690
  showPremiumLabel: true,
695
- showScoopLabel: false,
691
+ showExclusiveLabel: false,
696
692
  showStatus: true,
697
693
  showImage: true,
698
694
  imageSize: 'XL',
@@ -705,7 +701,7 @@ const TopStory = {
705
701
  showTitle: true,
706
702
  showStandfirst: true,
707
703
  showPremiumLabel: true,
708
- showScoopLabel: false,
704
+ showExclusiveLabel: false,
709
705
  showStatus: true,
710
706
  showRelatedLinks: true
711
707
  };
@@ -716,7 +712,7 @@ const TopStoryLandscape = {
716
712
  showTitle: true,
717
713
  showStandfirst: true,
718
714
  showPremiumLabel: true,
719
- showScoopLabel: false,
715
+ showExclusiveLabel: false,
720
716
  showStatus: true,
721
717
  showImage: true,
722
718
  imageSize: 'XL',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/x-teaser",
3
- "version": "18.2.1",
3
+ "version": "19.0.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": "^18.2.1",
21
+ "@financial-times/x-engine": "^19.0.0",
22
22
  "dateformat": "^3.0.3"
23
23
  },
24
24
  "devDependencies": {
package/readme.md CHANGED
@@ -114,7 +114,7 @@ As covered in the [features](#features) documentation the teaser properties, or
114
114
  | `showTitle` | Boolean |
115
115
  | `showStandfirst` | Boolean |
116
116
  | `showPremiumLabel` | Boolean |
117
- | `showScoopLabel` | Boolean |
117
+ | `showExclusiveLabel` | Boolean |
118
118
  | `showStatus` | Boolean |
119
119
  | `showImage` | Boolean |
120
120
  | `showHeadshot` | Boolean | Takes precedence over image |
@@ -255,7 +255,7 @@ As covered in the [features](#features) documentation the teaser properties, or
255
255
  | `isColumn` | Boolean |
256
256
  | `isPodcast` | Boolean |
257
257
  | `isEditorsChoice` | Boolean |
258
- | `isExclusive` | Boolean |
258
+ | `isExclusive` | Boolean | Controls whether the Exclusive badge is displayed |
259
259
  | `isScoop` | Boolean |
260
260
 
261
261
  #### Special Styling Props
@@ -0,0 +1,10 @@
1
+ import { h } from '@financial-times/x-engine'
2
+
3
+ export default function ExclusiveLabel() {
4
+ return (
5
+ <div className="o-teaser__labels o-teaser__labels--exclusive">
6
+ <span className="o-labels o-labels--content-exclusive">Exclusive</span>
7
+ <span className="o3-visually-hidden">&nbsp;content</span>
8
+ </div>
9
+ )
10
+ }
package/src/Status.jsx CHANGED
@@ -4,22 +4,15 @@ import RelativeTime from './RelativeTime'
4
4
  import LiveBlogStatus from './LiveBlogStatus'
5
5
  import AlwaysShowTimestamp from './AlwaysShowTimestamp'
6
6
  import PremiumLabel from './PremiumLabel'
7
- import ScoopLabel from './ScoopLabel'
7
+ import ExclusiveLabel from './ExclusiveLabel'
8
8
 
9
9
  export default (props) => {
10
10
  if (props.showStatus && props.status) {
11
11
  return <LiveBlogStatus {...props} />
12
12
  }
13
13
 
14
- if (
15
- props.showScoopLabel &&
16
- props?.indicators?.isScoop &&
17
- // We plan to show the Scoop label only on homepages.
18
- // If we later show it on other pages, this cutoff date will need review.
19
- // The `isScoop` property already exists, but Editorial will use it differently after 2025-10-01.
20
- new Date(props.firstPublishedDate) >= new Date('2025-10-01T00:00:00.000Z')
21
- ) {
22
- return <ScoopLabel {...props} />
14
+ if (props.showExclusiveLabel && props?.indicators?.isExclusive) {
15
+ return <ExclusiveLabel {...props} />
23
16
  }
24
17
 
25
18
  if (props.showPremiumLabel && props?.indicators?.accessLevel === 'premium') {
@@ -6,7 +6,7 @@ const Small = {
6
6
  showMeta: true,
7
7
  showTitle: true,
8
8
  showPremiumLabel: true,
9
- showScoopLabel: false,
9
+ showExclusiveLabel: false,
10
10
  showStatus: true
11
11
  }
12
12
 
@@ -17,7 +17,7 @@ const SmallHeavy = {
17
17
  showTitle: true,
18
18
  showStandfirst: true,
19
19
  showPremiumLabel: true,
20
- showScoopLabel: false,
20
+ showExclusiveLabel: false,
21
21
  showStatus: true,
22
22
  showImage: true,
23
23
  imageSize: 'Small'
@@ -30,7 +30,7 @@ const Large = {
30
30
  showTitle: true,
31
31
  showStandfirst: true,
32
32
  showPremiumLabel: true,
33
- showScoopLabel: false,
33
+ showExclusiveLabel: false,
34
34
  showStatus: true,
35
35
  showImage: true,
36
36
  imageSize: 'Medium'
@@ -42,7 +42,7 @@ const Hero = {
42
42
  showMeta: true,
43
43
  showTitle: true,
44
44
  showPremiumLabel: true,
45
- showScoopLabel: false,
45
+ showExclusiveLabel: false,
46
46
  showStatus: true,
47
47
  showImage: true,
48
48
  imageSize: 'Medium'
@@ -55,7 +55,7 @@ const HeroNarrow = {
55
55
  showTitle: true,
56
56
  showStandfirst: true,
57
57
  showPremiumLabel: true,
58
- showScoopLabel: false,
58
+ showExclusiveLabel: false,
59
59
  showStatus: true
60
60
  }
61
61
 
@@ -74,7 +74,7 @@ const HeroOverlay = {
74
74
  showMeta: true,
75
75
  showTitle: true,
76
76
  showPremiumLabel: true,
77
- showScoopLabel: false,
77
+ showExclusiveLabel: false,
78
78
  showStatus: true,
79
79
  showImage: true,
80
80
  imageSize: 'XL',
@@ -88,7 +88,7 @@ const TopStory = {
88
88
  showTitle: true,
89
89
  showStandfirst: true,
90
90
  showPremiumLabel: true,
91
- showScoopLabel: false,
91
+ showExclusiveLabel: false,
92
92
  showStatus: true,
93
93
  showRelatedLinks: true
94
94
  }
@@ -100,7 +100,7 @@ const TopStoryLandscape = {
100
100
  showTitle: true,
101
101
  showStandfirst: true,
102
102
  showPremiumLabel: true,
103
- showScoopLabel: false,
103
+ showExclusiveLabel: false,
104
104
  showStatus: true,
105
105
  showImage: true,
106
106
  imageSize: 'XL',
@@ -6,7 +6,7 @@ import '../src/Teaser.scss'
6
6
 
7
7
  const dependencies = {
8
8
  'o-date': '^7.0.1',
9
- 'o-labels': '^7.1.0',
9
+ 'o-labels': '^8.0.0',
10
10
  'o-teaser': '^9.1.0',
11
11
  'o-video': '^8.0.0'
12
12
  }
@@ -1,10 +0,0 @@
1
- import { h } from '@financial-times/x-engine'
2
-
3
- export default function ScoopLabel() {
4
- return (
5
- <div className="o-teaser__labels o-teaser__labels--scoop">
6
- <span className="o-labels o-labels--content-scoop">Exclusive</span>
7
- <span className="o3-visually-hidden">&nbsp;content</span>
8
- </div>
9
- )
10
- }