@financial-times/x-teaser 18.0.0 → 18.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.
- package/Props.d.ts +1 -0
- package/__tests__/Status.test.jsx +312 -0
- package/__tests__/__snapshots__/snapshots.test.js.snap +45 -45
- package/__tests__/image-service.test.js +65 -0
- package/dist/Teaser.cjs.js +60 -19
- package/dist/Teaser.es5.js +56 -19
- package/dist/Teaser.esm.js +60 -19
- package/package.json +2 -2
- package/readme.md +1 -0
- package/src/PremiumLabel.jsx +4 -1
- package/src/ScoopLabel.jsx +10 -0
- package/src/Status.jsx +23 -13
- package/src/Teaser.scss +4 -0
- package/src/concerns/image-service.js +19 -2
- package/src/concerns/presets.js +8 -0
- package/storybook/index.jsx +1 -1
package/Props.d.ts
CHANGED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
const { h } = require('@financial-times/x-engine')
|
|
2
|
+
const { mount } = require('@financial-times/x-test-utils/enzyme')
|
|
3
|
+
|
|
4
|
+
import * as LiveBlogStatus from '../src/LiveBlogStatus'
|
|
5
|
+
import * as ScoopLabel from '../src/ScoopLabel'
|
|
6
|
+
import * as PremiumLabel from '../src/PremiumLabel'
|
|
7
|
+
import * as AlwaysShowTimestamp from '../src/AlwaysShowTimestamp'
|
|
8
|
+
import * as RelativeTime from '../src/RelativeTime'
|
|
9
|
+
import * as TimeStamp from '../src/TimeStamp'
|
|
10
|
+
|
|
11
|
+
const LiveBlogStatusSpy = jest
|
|
12
|
+
.spyOn(LiveBlogStatus, 'default')
|
|
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>)
|
|
17
|
+
const PremiumLabelSpy = jest
|
|
18
|
+
.spyOn(PremiumLabel, 'default')
|
|
19
|
+
.mockReturnValue(<div className="premium-label">PremiumLabel</div>)
|
|
20
|
+
const AlwaysShowTimestampSpy = jest
|
|
21
|
+
.spyOn(AlwaysShowTimestamp, 'default')
|
|
22
|
+
.mockReturnValue(<div className="always-show-timestamp">AlwaysShowTimestamp</div>)
|
|
23
|
+
const RelativeTimeSpy = jest
|
|
24
|
+
.spyOn(RelativeTime, 'default')
|
|
25
|
+
.mockReturnValue(<div className="relative-time">RelativeTimeSpy</div>)
|
|
26
|
+
const TimeStampSpy = jest
|
|
27
|
+
.spyOn(TimeStamp, 'default')
|
|
28
|
+
.mockReturnValue(<div className="timestamp">TimeStamp</div>)
|
|
29
|
+
|
|
30
|
+
import Status from '../src/Status'
|
|
31
|
+
|
|
32
|
+
describe('Status - Display Logic', () => {
|
|
33
|
+
let props
|
|
34
|
+
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
props = {
|
|
37
|
+
showStatus: true,
|
|
38
|
+
showPremiumLabel: true,
|
|
39
|
+
showScoopLabel: true,
|
|
40
|
+
status: 'inprogress',
|
|
41
|
+
indicators: { isScoop: true, accessLevel: 'premium' },
|
|
42
|
+
firstPublishedDate: '2025-10-01T00:00:00.000Z',
|
|
43
|
+
publishedDate: '2025-10-01T00:00:00.000Z',
|
|
44
|
+
useRelativeTimeIfToday: true,
|
|
45
|
+
useRelativeTime: true
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
jest.clearAllMocks()
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
describe('LiveBlogStatus', () => {
|
|
54
|
+
it('renders only LiveBlogStatus when LiveBlogStatus props are present', () => {
|
|
55
|
+
mount(<Status {...props} />)
|
|
56
|
+
|
|
57
|
+
expect(LiveBlogStatusSpy).toHaveBeenCalledTimes(1)
|
|
58
|
+
expect(ScoopLabelSpy).not.toHaveBeenCalled()
|
|
59
|
+
expect(PremiumLabelSpy).not.toHaveBeenCalled()
|
|
60
|
+
expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
|
|
61
|
+
expect(RelativeTimeSpy).not.toHaveBeenCalled()
|
|
62
|
+
expect(TimeStampSpy).not.toHaveBeenCalled()
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('should not render LiveBlogStatus when status is not present', () => {
|
|
66
|
+
delete props.status
|
|
67
|
+
mount(<Status {...props} />)
|
|
68
|
+
|
|
69
|
+
expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('should not render LiveBlogStatus when showStatus is false', () => {
|
|
73
|
+
props.showStatus = false
|
|
74
|
+
mount(<Status {...props} />)
|
|
75
|
+
|
|
76
|
+
expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
describe('ScoopLabel', () => {
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
delete props.status
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('renders only ScoopLabel when higher-level render props are absent and ScoopLabel props are present', () => {
|
|
86
|
+
mount(<Status {...props} />)
|
|
87
|
+
|
|
88
|
+
expect(ScoopLabelSpy).toHaveBeenCalledTimes(1)
|
|
89
|
+
expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
|
|
90
|
+
expect(PremiumLabelSpy).not.toHaveBeenCalled()
|
|
91
|
+
expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
|
|
92
|
+
expect(RelativeTimeSpy).not.toHaveBeenCalled()
|
|
93
|
+
expect(TimeStampSpy).not.toHaveBeenCalled()
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('should not render ScoopLabel when showScoopLabel = false', () => {
|
|
97
|
+
props.showScoopLabel = false
|
|
98
|
+
mount(<Status {...props} />)
|
|
99
|
+
|
|
100
|
+
expect(ScoopLabelSpy).not.toHaveBeenCalled()
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('should not render ScoopLabel when props.indicators.isScoop = false', () => {
|
|
104
|
+
props.indicators.isScoop = false
|
|
105
|
+
mount(<Status {...props} />)
|
|
106
|
+
|
|
107
|
+
expect(ScoopLabelSpy).not.toHaveBeenCalled()
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('should not render ScoopLabel when firstPublishedDate is older than the cutoff date', () => {
|
|
111
|
+
props.firstPublishedDate = '2025-09-01T00:00:00.000Z'
|
|
112
|
+
mount(<Status {...props} />)
|
|
113
|
+
|
|
114
|
+
expect(ScoopLabelSpy).not.toHaveBeenCalled()
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
describe('PremiumLabel', () => {
|
|
119
|
+
beforeEach(() => {
|
|
120
|
+
delete props.status
|
|
121
|
+
props.showScoopLabel = false
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('renders only PremiumLabel when higher-level render props are absent and PremiumLabel props are present', () => {
|
|
125
|
+
mount(<Status {...props} />)
|
|
126
|
+
|
|
127
|
+
expect(PremiumLabelSpy).toHaveBeenCalledTimes(1)
|
|
128
|
+
expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
|
|
129
|
+
expect(ScoopLabelSpy).not.toHaveBeenCalled()
|
|
130
|
+
expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
|
|
131
|
+
expect(RelativeTimeSpy).not.toHaveBeenCalled()
|
|
132
|
+
expect(TimeStampSpy).not.toHaveBeenCalled()
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('should not render PremiumLabel when showPremiumLabel = false', () => {
|
|
136
|
+
props.showPremiumLabel = false
|
|
137
|
+
mount(<Status {...props} />)
|
|
138
|
+
|
|
139
|
+
expect(PremiumLabelSpy).not.toHaveBeenCalled()
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('should not render PremiumLabel when accessLevel is not premium', () => {
|
|
143
|
+
props.indicators.accessLevel = 'subscribed'
|
|
144
|
+
mount(<Status {...props} />)
|
|
145
|
+
|
|
146
|
+
expect(PremiumLabelSpy).not.toHaveBeenCalled()
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
describe('AlwaysShowTimestamp', () => {
|
|
151
|
+
beforeEach(() => {
|
|
152
|
+
delete props.status
|
|
153
|
+
props.showScoopLabel = false
|
|
154
|
+
props.showPremiumLabel = false
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('renders only AlwaysShowTimestamp when higher-level render props are absent and AlwaysShowTimestamp props are present', () => {
|
|
158
|
+
mount(<Status {...props} />)
|
|
159
|
+
|
|
160
|
+
expect(AlwaysShowTimestampSpy).toHaveBeenCalledTimes(1)
|
|
161
|
+
expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
|
|
162
|
+
expect(ScoopLabelSpy).not.toHaveBeenCalled()
|
|
163
|
+
expect(PremiumLabelSpy).not.toHaveBeenCalled()
|
|
164
|
+
expect(RelativeTimeSpy).not.toHaveBeenCalled()
|
|
165
|
+
expect(TimeStampSpy).not.toHaveBeenCalled()
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('should not render AlwaysShowTimestamp when showStatus = false', () => {
|
|
169
|
+
props.showStatus = false
|
|
170
|
+
mount(<Status {...props} />)
|
|
171
|
+
|
|
172
|
+
expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('should not render AlwaysShowTimestamp when publishedDate is not present', () => {
|
|
176
|
+
delete props.publishedDate
|
|
177
|
+
mount(<Status {...props} />)
|
|
178
|
+
|
|
179
|
+
expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('should not render AlwaysShowTimestamp when useRelativeTimeIfToday = false', () => {
|
|
183
|
+
props.useRelativeTimeIfToday = false
|
|
184
|
+
mount(<Status {...props} />)
|
|
185
|
+
|
|
186
|
+
expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
describe('RelativeTime', () => {
|
|
191
|
+
beforeEach(() => {
|
|
192
|
+
delete props.status
|
|
193
|
+
props.showScoopLabel = false
|
|
194
|
+
props.showPremiumLabel = false
|
|
195
|
+
props.useRelativeTimeIfToday = false
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('renders only RelativeTime when higher-level render props are absent and RelativeTime props are present', () => {
|
|
199
|
+
mount(<Status {...props} />)
|
|
200
|
+
|
|
201
|
+
expect(RelativeTimeSpy).toHaveBeenCalledTimes(1)
|
|
202
|
+
expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
|
|
203
|
+
expect(ScoopLabelSpy).not.toHaveBeenCalled()
|
|
204
|
+
expect(PremiumLabelSpy).not.toHaveBeenCalled()
|
|
205
|
+
expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
|
|
206
|
+
expect(TimeStampSpy).not.toHaveBeenCalled()
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
it('should not render RelativeTime when showStatus = false', () => {
|
|
210
|
+
props.showStatus = false
|
|
211
|
+
mount(<Status {...props} />)
|
|
212
|
+
|
|
213
|
+
expect(RelativeTimeSpy).not.toHaveBeenCalled()
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
it('should not render RelativeTime when publishedDate is not present', () => {
|
|
217
|
+
delete props.publishedDate
|
|
218
|
+
mount(<Status {...props} />)
|
|
219
|
+
|
|
220
|
+
expect(RelativeTimeSpy).not.toHaveBeenCalled()
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('should not render RelativeTime when useRelativeTime = false', () => {
|
|
224
|
+
props.useRelativeTime = false
|
|
225
|
+
mount(<Status {...props} />)
|
|
226
|
+
|
|
227
|
+
expect(RelativeTimeSpy).not.toHaveBeenCalled()
|
|
228
|
+
})
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
describe('TimeStamp', () => {
|
|
232
|
+
beforeEach(() => {
|
|
233
|
+
delete props.status
|
|
234
|
+
props.showScoopLabel = false
|
|
235
|
+
props.showPremiumLabel = false
|
|
236
|
+
props.useRelativeTimeIfToday = false
|
|
237
|
+
props.useRelativeTime = false
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('renders only TimeStamp when higher-level render props are absent and TimeStamp props are present', () => {
|
|
241
|
+
mount(<Status {...props} />)
|
|
242
|
+
|
|
243
|
+
expect(TimeStampSpy).toHaveBeenCalledTimes(1)
|
|
244
|
+
expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
|
|
245
|
+
expect(ScoopLabelSpy).not.toHaveBeenCalled()
|
|
246
|
+
expect(PremiumLabelSpy).not.toHaveBeenCalled()
|
|
247
|
+
expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
|
|
248
|
+
expect(RelativeTimeSpy).not.toHaveBeenCalled()
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
it('should not render TimeStamp when showStatus = false', () => {
|
|
252
|
+
props.showStatus = false
|
|
253
|
+
mount(<Status {...props} />)
|
|
254
|
+
|
|
255
|
+
expect(TimeStampSpy).not.toHaveBeenCalled()
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
it('should not render TimeStamp when publishedDate is not present', () => {
|
|
259
|
+
delete props.publishedDate
|
|
260
|
+
mount(<Status {...props} />)
|
|
261
|
+
|
|
262
|
+
expect(TimeStampSpy).not.toHaveBeenCalled()
|
|
263
|
+
})
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
describe('No Status rendered', () => {
|
|
267
|
+
it('should not render any Status - Case 1: all showXXX properties are set to false', () => {
|
|
268
|
+
props = {
|
|
269
|
+
showStatus: false,
|
|
270
|
+
showPremiumLabel: false,
|
|
271
|
+
showScoopLabel: false,
|
|
272
|
+
status: 'inprogress',
|
|
273
|
+
indicators: { isScoop: true, accessLevel: 'premium' },
|
|
274
|
+
firstPublishedDate: '2025-10-01T00:00:00.000Z',
|
|
275
|
+
publishedDate: '2025-10-01T00:00:00.000Z',
|
|
276
|
+
useRelativeTimeIfToday: true,
|
|
277
|
+
useRelativeTime: true
|
|
278
|
+
}
|
|
279
|
+
const subject = mount(<Status {...props} />)
|
|
280
|
+
|
|
281
|
+
expect(subject.isEmptyRender()).toBeTruthy()
|
|
282
|
+
expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
|
|
283
|
+
expect(ScoopLabelSpy).not.toHaveBeenCalled()
|
|
284
|
+
expect(PremiumLabelSpy).not.toHaveBeenCalled()
|
|
285
|
+
expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
|
|
286
|
+
expect(RelativeTimeSpy).not.toHaveBeenCalled()
|
|
287
|
+
expect(TimeStampSpy).not.toHaveBeenCalled()
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
it('should not render any Status - Case 2: showStatus = true but other properties are not present', () => {
|
|
291
|
+
// status & publishedDate are missing
|
|
292
|
+
props = {
|
|
293
|
+
showStatus: true,
|
|
294
|
+
showPremiumLabel: false,
|
|
295
|
+
showScoopLabel: false,
|
|
296
|
+
indicators: { isScoop: true, accessLevel: 'premium' },
|
|
297
|
+
firstPublishedDate: '2025-10-01T00:00:00.000Z',
|
|
298
|
+
useRelativeTimeIfToday: true,
|
|
299
|
+
useRelativeTime: true
|
|
300
|
+
}
|
|
301
|
+
const subject = mount(<Status {...props} />)
|
|
302
|
+
|
|
303
|
+
expect(subject.isEmptyRender()).toBeTruthy()
|
|
304
|
+
expect(LiveBlogStatusSpy).not.toHaveBeenCalled()
|
|
305
|
+
expect(ScoopLabelSpy).not.toHaveBeenCalled()
|
|
306
|
+
expect(PremiumLabelSpy).not.toHaveBeenCalled()
|
|
307
|
+
expect(AlwaysShowTimestampSpy).not.toHaveBeenCalled()
|
|
308
|
+
expect(RelativeTimeSpy).not.toHaveBeenCalled()
|
|
309
|
+
expect(TimeStampSpy).not.toHaveBeenCalled()
|
|
310
|
+
})
|
|
311
|
+
})
|
|
312
|
+
})
|