@linktr.ee/messaging-react 3.10.0-rc-1783660962 → 3.10.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.
Files changed (86) hide show
  1. package/dist/Card-B-L2MST4.js +107 -0
  2. package/dist/Card-B-L2MST4.js.map +1 -0
  3. package/dist/Card-B56QxJW3.cjs +2 -0
  4. package/dist/Card-B56QxJW3.cjs.map +1 -0
  5. package/dist/Card-Bqh-BQaX.js +132 -0
  6. package/dist/Card-Bqh-BQaX.js.map +1 -0
  7. package/dist/Card-ZCE6Lhfc.cjs +2 -0
  8. package/dist/Card-ZCE6Lhfc.cjs.map +1 -0
  9. package/dist/Card-g4JD-2ES.js +163 -0
  10. package/dist/Card-g4JD-2ES.js.map +1 -0
  11. package/dist/Card-wac6AMNc.cjs +2 -0
  12. package/dist/Card-wac6AMNc.cjs.map +1 -0
  13. package/dist/LockedThumbnail-BAQmoIff.js +220 -0
  14. package/dist/LockedThumbnail-BAQmoIff.js.map +1 -0
  15. package/dist/LockedThumbnail-Cr0Gqs3Y.cjs +2 -0
  16. package/dist/LockedThumbnail-Cr0Gqs3Y.cjs.map +1 -0
  17. package/dist/assets/index.css +1 -1
  18. package/dist/index-BPhZyaxT.cjs +2 -0
  19. package/dist/index-BPhZyaxT.cjs.map +1 -0
  20. package/dist/{index-DFZrAiLB.js → index-BxzzWK4K.js} +1249 -1276
  21. package/dist/index-BxzzWK4K.js.map +1 -0
  22. package/dist/index.cjs +1 -1
  23. package/dist/index.d.ts +101 -164
  24. package/dist/index.js +1 -1
  25. package/dist/testing.d.ts +0 -1
  26. package/package.json +2 -2
  27. package/src/components/ChannelList/CustomChannelPreview.test.tsx +0 -95
  28. package/src/components/ChannelList/CustomChannelPreview.tsx +9 -21
  29. package/src/components/CustomMessage/CustomMessage.stories.tsx +1 -54
  30. package/src/components/CustomMessage/MessageTag.tsx +0 -8
  31. package/src/components/CustomMessage/context.tsx +1 -1
  32. package/src/components/CustomMessage/index.tsx +42 -76
  33. package/src/components/CustomMessageInput/CustomMessageInput.stories.tsx +3 -5
  34. package/src/components/LockedAttachment/LockedAttachment.stories.tsx +450 -0
  35. package/src/components/LockedAttachment/components/Composer/Card.tsx +221 -0
  36. package/src/components/LockedAttachment/components/Composer/index.ts +2 -0
  37. package/src/components/LockedAttachment/components/Received/Card.tsx +191 -0
  38. package/src/components/{CustomMessage/LockedAttachment/components/Media → LockedAttachment/components/Received}/CardActions.tsx +2 -9
  39. package/src/components/LockedAttachment/components/Received/index.ts +2 -0
  40. package/src/components/LockedAttachment/components/Sent/Card.tsx +177 -0
  41. package/src/components/LockedAttachment/components/Sent/index.ts +2 -0
  42. package/src/components/{CustomMessage/LockedAttachment → LockedAttachment}/components/_shared/CardBody.tsx +7 -8
  43. package/src/components/{CustomMessage/LockedAttachment → LockedAttachment}/components/_shared/GalleryThumbnail.tsx +39 -31
  44. package/src/components/LockedAttachment/components/_shared/LockBadge.tsx +39 -0
  45. package/src/components/{CustomMessage/LockedAttachment/components/_shared/MediaShell.tsx → LockedAttachment/components/_shared/LockedCardShell.tsx} +8 -8
  46. package/src/components/{CustomMessage/LockedAttachment/components/_shared/SingleThumbnail.tsx → LockedAttachment/components/_shared/LockedThumbnail.tsx} +7 -11
  47. package/src/components/LockedAttachment/index.tsx +63 -0
  48. package/src/components/LockedAttachment/types.ts +51 -0
  49. package/src/index.ts +8 -3
  50. package/src/stream-custom-data.ts +0 -1
  51. package/src/styles.css +0 -12
  52. package/src/types.ts +1 -1
  53. package/dist/Card-415OfRhw.cjs +0 -2
  54. package/dist/Card-415OfRhw.cjs.map +0 -1
  55. package/dist/Card-CyWdUMCB.cjs +0 -2
  56. package/dist/Card-CyWdUMCB.cjs.map +0 -1
  57. package/dist/Card-V5d6qhDT.js +0 -694
  58. package/dist/Card-V5d6qhDT.js.map +0 -1
  59. package/dist/Card-qS17qnN0.js +0 -103
  60. package/dist/Card-qS17qnN0.js.map +0 -1
  61. package/dist/LoadingDots-Ct3WmVlT.js +0 -59
  62. package/dist/LoadingDots-Ct3WmVlT.js.map +0 -1
  63. package/dist/LoadingDots-D9GGphgM.cjs +0 -2
  64. package/dist/LoadingDots-D9GGphgM.cjs.map +0 -1
  65. package/dist/index-Bx_zjNMR.cjs +0 -2
  66. package/dist/index-Bx_zjNMR.cjs.map +0 -1
  67. package/dist/index-DFZrAiLB.js.map +0 -1
  68. package/src/components/CustomMessage/LockedAttachment/LockedAttachment.stories.tsx +0 -541
  69. package/src/components/CustomMessage/LockedAttachment/LockedAttachment.test.tsx +0 -117
  70. package/src/components/CustomMessage/LockedAttachment/components/Media/Card.tsx +0 -358
  71. package/src/components/CustomMessage/LockedAttachment/components/Media/types.ts +0 -79
  72. package/src/components/CustomMessage/LockedAttachment/components/Text/Card.tsx +0 -153
  73. package/src/components/CustomMessage/LockedAttachment/components/Text/types.ts +0 -58
  74. package/src/components/CustomMessage/LockedAttachment/components/_shared/CardThumbnail.tsx +0 -92
  75. package/src/components/CustomMessage/LockedAttachment/components/_shared/LoadingDots.tsx +0 -36
  76. package/src/components/CustomMessage/LockedAttachment/components/_shared/LockBadge.tsx +0 -63
  77. package/src/components/CustomMessage/LockedAttachment/components/_shared/PreviewToggle.tsx +0 -58
  78. package/src/components/CustomMessage/LockedAttachment/components/_shared/PriceLockRow.tsx +0 -21
  79. package/src/components/CustomMessage/LockedAttachment/components/_shared/PurchaseStatusBadge.tsx +0 -74
  80. package/src/components/CustomMessage/LockedAttachment/components/_shared/SkeletonTextLines.tsx +0 -29
  81. package/src/components/CustomMessage/LockedAttachment/components/_shared/TextShell.tsx +0 -28
  82. package/src/components/CustomMessage/LockedAttachment/components/_shared/usePreviewToggle.test.ts +0 -67
  83. package/src/components/CustomMessage/LockedAttachment/components/_shared/usePreviewToggle.ts +0 -89
  84. package/src/components/CustomMessage/LockedAttachment/components/_shared/useReceivedSource.ts +0 -83
  85. package/src/components/CustomMessage/LockedAttachment/index.tsx +0 -105
  86. package/src/components/CustomMessage/LockedAttachment/types.ts +0 -44
@@ -1,541 +0,0 @@
1
- import type { Meta, StoryFn } from '@storybook/react'
2
- import React, { useState } from 'react'
3
- import { MessageText } from 'stream-chat-react'
4
-
5
- import type { LockedAttachmentGalleryItem } from './types'
6
-
7
- import LockedAttachment from '.'
8
-
9
- const meta: Meta = {
10
- title: 'LockedAttachment',
11
- parameters: { layout: 'fullscreen' },
12
- }
13
- export default meta
14
-
15
- const MatrixTable: React.FC<{ children: React.ReactNode }> = ({ children }) => (
16
- <div className="min-h-screen w-full bg-[#F9F7F4] p-12">
17
- <table className="border-separate border-spacing-4">
18
- <thead>
19
- <tr>
20
- <th className="w-8 pb-2" />
21
- <th className="pb-2 text-left text-xs font-medium text-black/40">
22
- Visitor
23
- </th>
24
- <th className="pb-2 text-left text-xs font-medium text-black/40">
25
- Creator
26
- </th>
27
- </tr>
28
- </thead>
29
- <tbody>{children}</tbody>
30
- </table>
31
- </div>
32
- )
33
-
34
- const MatrixTableRow: React.FC<{
35
- label: React.ReactNode
36
- received: React.ReactNode
37
- sent: React.ReactNode
38
- }> = ({ label, received, sent }) => (
39
- <tr>
40
- <td className="whitespace-nowrap pr-4 align-top text-xs font-medium text-black/40">
41
- {label}
42
- </td>
43
- <td className="align-top">
44
- <MatrixTableCell isMine={false}>{received}</MatrixTableCell>
45
- </td>
46
- <td className="align-top">
47
- <MatrixTableCell isMine>{sent}</MatrixTableCell>
48
- </td>
49
- </tr>
50
- )
51
-
52
- /**
53
- * Deliberately renders WITHOUT any `.str-chat` ancestor or
54
- * `str-chat__message*` structure — the real Composer/preview surfaces this
55
- * card renders in outside a message thread. If a card looks wrong here, it's
56
- * leaning on ambient stream-chat-react CSS instead of its own explicit
57
- * styling.
58
- */
59
- const MatrixTableCell: React.FC<{
60
- isMine: boolean
61
- children: React.ReactNode
62
- }> = ({ children }) => <div>{children}</div>
63
-
64
- export const Text: StoryFn = () => {
65
- const [isUnlocking, setUnlocking] = useState(false)
66
-
67
- // Matches what `CustomMessage` passes `MessageText` for a revealed paid
68
- // text attachment (`displayMessage`) — just enough of a `LocalMessage`
69
- // shape to render the real `str-chat__message-text` markup/CSS.
70
- const purchasedMessage = {
71
- text: "Here's the address, see you Thursday at 7!",
72
- type: 'regular',
73
- } as Parameters<typeof MessageText>[0]['message']
74
-
75
- return (
76
- <MatrixTable>
77
- <MatrixTableRow
78
- label="Draft"
79
- received={null}
80
- sent={
81
- <LockedAttachment
82
- isDraft
83
- contentType="text"
84
- renderedText={purchasedMessage?.text}
85
- amountText="AU$4.99"
86
- onDismiss={() => alert('Removed')}
87
- />
88
- }
89
- />
90
- <MatrixTableRow
91
- label="Preview"
92
- received={null}
93
- sent={
94
- <LockedAttachment
95
- isPreview
96
- contentType="text"
97
- placeholderAmountText="AU$0.00"
98
- />
99
- }
100
- />
101
- <MatrixTableRow
102
- label="Idle"
103
- received={
104
- <LockedAttachment
105
- isMine={false}
106
- contentType="text"
107
- amountText="AU$4.99"
108
- isUnlocking={isUnlocking}
109
- onUnlockClick={() => {
110
- setUnlocking(true)
111
- setTimeout(() => setUnlocking(false), 1500)
112
- }}
113
- />
114
- }
115
- sent={
116
- <LockedAttachment
117
- isMine
118
- contentType="text"
119
- amountText="AU$4.99"
120
- paymentStatus="pending"
121
- />
122
- }
123
- />
124
- <MatrixTableRow
125
- label="Unlocking"
126
- received={
127
- <LockedAttachment
128
- isMine={false}
129
- contentType="text"
130
- amountText="AU$4.99"
131
- isUnlocking
132
- />
133
- }
134
- sent={null}
135
- />
136
- <MatrixTableRow
137
- label="Purchased"
138
- received={
139
- <LockedAttachment
140
- isMine={false}
141
- contentType="text"
142
- amountText="AU$4.99"
143
- paymentStatus="paid"
144
- renderedText={<MessageText message={purchasedMessage} />}
145
- />
146
- }
147
- sent={
148
- <LockedAttachment
149
- isMine
150
- contentType="text"
151
- amountText="AU$4.99"
152
- paymentStatus="paid"
153
- renderedText={<MessageText message={purchasedMessage} />}
154
- />
155
- }
156
- />
157
- </MatrixTable>
158
- )
159
- }
160
-
161
- const VIDEO_THUMBNAIL_BLURRED = '/video-thumbnail-blurred.jpg'
162
- const VIDEO_THUMBNAIL = '/video-thumbnail.jpg'
163
- const VIDEO_SOURCE = '/video-source.mp4'
164
-
165
- const AUDIO_THUMBNAIL_BLURRED = '/audio-thumbnail-blurred.jpg'
166
- const AUDIO_THUMBNAIL = '/audio-thumbnail.jpg'
167
- const AUDIO_SOURCE = '/audio-source.mp3'
168
-
169
- const IMAGE_THUMBNAIL_BLURRED = '/image-thumbnail-blurred.jpg'
170
- const IMAGE_THUMBNAIL = '/image-thumbnail.jpg'
171
- const IMAGE_SOURCE = '/image-source.jpg'
172
-
173
- const DOCUMENT_THUMBNAIL_BLURRED = '/document-thumbnail-blurred.jpg'
174
- const DOCUMENT_THUMBNAIL = '/document-thumbnail.jpg'
175
- const DOCUMENT_SOURCE = '/document-source.pdf'
176
-
177
- const VARIANTS = [
178
- {
179
- label: 'Video',
180
- title: "Alicia's Workout Plan",
181
- mimeType: 'video/mp4',
182
- detail: '1:20',
183
- thumbnailUrl: VIDEO_THUMBNAIL_BLURRED,
184
- thumbnailUnlockedUrl: VIDEO_THUMBNAIL,
185
- sourceUrl: VIDEO_SOURCE,
186
- },
187
- {
188
- label: 'Audio',
189
- title: 'Morning Meditation',
190
- mimeType: 'audio/mpeg',
191
- detail: '4:35',
192
- thumbnailUrl: AUDIO_THUMBNAIL_BLURRED,
193
- thumbnailUnlockedUrl: AUDIO_THUMBNAIL,
194
- sourceUrl: AUDIO_SOURCE,
195
- },
196
- {
197
- label: 'Image',
198
- title: 'Picture of my cat',
199
- mimeType: 'image/jpeg',
200
- detail: '3.2 MB',
201
- thumbnailUrl: IMAGE_THUMBNAIL_BLURRED,
202
- thumbnailUnlockedUrl: IMAGE_THUMBNAIL,
203
- sourceUrl: IMAGE_SOURCE,
204
- },
205
- {
206
- label: 'Document',
207
- title: 'Strength Training Guide',
208
- mimeType: 'application/zip',
209
- detail: '14 files',
210
- thumbnailUrl: DOCUMENT_THUMBNAIL_BLURRED,
211
- thumbnailUnlockedUrl: DOCUMENT_THUMBNAIL,
212
- sourceUrl: DOCUMENT_SOURCE,
213
- },
214
- {
215
- label: 'Unknown',
216
- title: 'Unknown Attachment',
217
- mimeType: 'application/octet-stream',
218
- detail: '2.4 MB',
219
- thumbnailUrl: undefined,
220
- thumbnailUnlockedUrl: undefined,
221
- sourceUrl: DOCUMENT_SOURCE,
222
- },
223
- ]
224
-
225
- /**
226
- * A mixed-media collection (a video + 3 photos) used by the Gallery stories
227
- * below. `LockedAttachment` accepts a `gallery` prop and renders a carousel
228
- * of these items when 2+ items are provided.
229
- */
230
- const GALLERY_ITEMS: LockedAttachmentGalleryItem[] = [
231
- {
232
- mimeType: 'video/mp4',
233
- thumbnailUrl: VIDEO_THUMBNAIL,
234
- sourceUrl: VIDEO_SOURCE,
235
- },
236
- {
237
- mimeType: 'image/jpeg',
238
- thumbnailUrl: IMAGE_THUMBNAIL,
239
- sourceUrl: IMAGE_SOURCE,
240
- },
241
- {
242
- mimeType: 'application/pdf',
243
- thumbnailUrl: DOCUMENT_THUMBNAIL,
244
- sourceUrl: DOCUMENT_SOURCE,
245
- },
246
- {
247
- mimeType: 'audio/mpeg',
248
- thumbnailUrl: AUDIO_THUMBNAIL,
249
- sourceUrl: AUDIO_SOURCE,
250
- },
251
- ]
252
-
253
- const GALLERY_DETAIL = '4 items'
254
- const GALLERY_TITLE = 'Workout Guide'
255
-
256
- const MediaMatrix: React.FC<{ variant: (typeof VARIANTS)[number] }> = ({
257
- variant,
258
- }) => {
259
- const [isPaid, setPaid] = useState(false)
260
- const [isUnlocking, setUnlocking] = useState(false)
261
- const {
262
- title,
263
- mimeType,
264
- detail,
265
- thumbnailUrl,
266
- thumbnailUnlockedUrl,
267
- sourceUrl,
268
- } = variant
269
-
270
- const fetchSource = async () => ({
271
- sourceUrl,
272
- thumbnailUrl: thumbnailUnlockedUrl,
273
- })
274
-
275
- return (
276
- <MatrixTable>
277
- <MatrixTableRow
278
- label="Draft"
279
- received={null}
280
- sent={
281
- <LockedAttachment
282
- isDraft
283
- title={title}
284
- thumbnailUrl={thumbnailUrl}
285
- contentType="media"
286
- mimeType={mimeType}
287
- detail={detail}
288
- amountText="AU$9.99"
289
- onDismiss={() => alert('Dismissed')}
290
- />
291
- }
292
- />
293
- <MatrixTableRow
294
- label="Preview"
295
- received={null}
296
- sent={
297
- <LockedAttachment
298
- isPreview
299
- placeholderTitle="Attachment title"
300
- placeholderAmountText="AU$0.00"
301
- thumbnailUrl={thumbnailUrl}
302
- mimeType={mimeType}
303
- contentType="media"
304
- detail={detail}
305
- />
306
- }
307
- />
308
- <MatrixTableRow
309
- label="Idle"
310
- received={
311
- <LockedAttachment
312
- isMine={false}
313
- title={title}
314
- thumbnailUrl={thumbnailUrl}
315
- mimeType={mimeType}
316
- contentType="media"
317
- detail={detail}
318
- amountText="AU$9.99"
319
- paymentStatus={isPaid ? 'paid' : undefined}
320
- isUnlocking={isUnlocking}
321
- onUnlockClick={() => {
322
- setUnlocking(true)
323
- setTimeout(() => {
324
- setUnlocking(false)
325
- setPaid(true)
326
- }, 1500)
327
- }}
328
- onDownloadClick={() => alert('Download clicked')}
329
- onFetchSource={fetchSource}
330
- />
331
- }
332
- sent={
333
- <LockedAttachment
334
- isMine
335
- title={title}
336
- thumbnailUrl={thumbnailUrl}
337
- mimeType={mimeType}
338
- contentType="media"
339
- detail={detail}
340
- amountText="AU$9.99"
341
- onFetchSource={fetchSource}
342
- />
343
- }
344
- />
345
- <MatrixTableRow
346
- label="Unlocking"
347
- received={
348
- <LockedAttachment
349
- isMine={false}
350
- title={title}
351
- thumbnailUrl={thumbnailUrl}
352
- mimeType={mimeType}
353
- contentType="media"
354
- detail={detail}
355
- amountText="AU$9.99"
356
- isUnlocking
357
- onDownloadClick={() => alert('Download clicked')}
358
- onFetchSource={fetchSource}
359
- />
360
- }
361
- // Sender never unlocks their own message — no distinct state to show.
362
- sent={null}
363
- />
364
- <MatrixTableRow
365
- label="Purchased"
366
- received={
367
- <LockedAttachment
368
- isMine={false}
369
- title={title}
370
- thumbnailUrl={thumbnailUrl}
371
- mimeType={mimeType}
372
- contentType="media"
373
- detail={detail}
374
- amountText="AU$9.99"
375
- paymentStatus="paid"
376
- onUnlockClick={() => alert('Unlock clicked')}
377
- onDownloadClick={() => alert('Download clicked')}
378
- onFetchSource={fetchSource}
379
- />
380
- }
381
- sent={
382
- <LockedAttachment
383
- isMine
384
- title={title}
385
- thumbnailUrl={thumbnailUrl}
386
- mimeType={mimeType}
387
- contentType="media"
388
- detail={detail}
389
- amountText="AU$9.99"
390
- paymentStatus="paid"
391
- onFetchSource={fetchSource}
392
- />
393
- }
394
- />
395
- </MatrixTable>
396
- )
397
- }
398
-
399
- const variantByLabel = (label: string) =>
400
- VARIANTS.find((v) => v.label === label)!
401
-
402
- export const Video: StoryFn = () => (
403
- <MediaMatrix variant={variantByLabel('Video')} />
404
- )
405
- // LockedAttachment (contentType="media", !isMine) autoplays the video preview once paid; even with
406
- // our SMIL/CSS animation freeze the <video> element advances frames
407
- // independently and produces a non-deterministic snapshot. Skip in Chromatic.
408
- Video.parameters = { chromatic: { disableSnapshot: true } }
409
-
410
- export const Audio: StoryFn = () => (
411
- <MediaMatrix variant={variantByLabel('Audio')} />
412
- )
413
-
414
- export const Image: StoryFn = () => (
415
- <MediaMatrix variant={variantByLabel('Image')} />
416
- )
417
-
418
- export const Document: StoryFn = () => (
419
- <MediaMatrix variant={variantByLabel('Document')} />
420
- )
421
-
422
- export const Unknown: StoryFn = () => (
423
- <MediaMatrix variant={variantByLabel('Unknown')} />
424
- )
425
-
426
- export const Gallery: StoryFn = () => {
427
- const [isPaid, setPaid] = useState(false)
428
- const [isUnlocking, setUnlocking] = useState(false)
429
-
430
- return (
431
- <MatrixTable>
432
- <MatrixTableRow
433
- label="Draft"
434
- received={null}
435
- sent={
436
- <LockedAttachment
437
- isDraft
438
- contentType="media"
439
- title={GALLERY_TITLE}
440
- detail={GALLERY_DETAIL}
441
- amountText="AU$50.00"
442
- gallery={GALLERY_ITEMS}
443
- onDismiss={() => alert('Dismissed gallery')}
444
- onEditClick={() => alert('Edit gallery')}
445
- />
446
- }
447
- />
448
- <MatrixTableRow
449
- label="Preview"
450
- received={null}
451
- sent={
452
- <LockedAttachment
453
- isPreview
454
- contentType="media"
455
- placeholderTitle="Attachment title"
456
- detail={GALLERY_DETAIL}
457
- placeholderAmountText="AU$0.00"
458
- gallery={GALLERY_ITEMS}
459
- />
460
- }
461
- />
462
- <MatrixTableRow
463
- label="Idle"
464
- received={
465
- <LockedAttachment
466
- isMine={false}
467
- contentType="media"
468
- title={GALLERY_TITLE}
469
- detail={GALLERY_DETAIL}
470
- amountText="AU$50.00"
471
- gallery={GALLERY_ITEMS}
472
- paymentStatus={isPaid ? 'paid' : undefined}
473
- isUnlocking={isUnlocking}
474
- onUnlockClick={() => {
475
- setUnlocking(true)
476
- setTimeout(() => {
477
- setUnlocking(false)
478
- setPaid(true)
479
- }, 1500)
480
- }}
481
- onDownloadClick={() => alert('Download gallery')}
482
- />
483
- }
484
- sent={
485
- <LockedAttachment
486
- isMine
487
- contentType="media"
488
- title={GALLERY_TITLE}
489
- detail={GALLERY_DETAIL}
490
- amountText="AU$50.00"
491
- gallery={GALLERY_ITEMS}
492
- />
493
- }
494
- />
495
- <MatrixTableRow
496
- label="Unlocking"
497
- received={
498
- <LockedAttachment
499
- isMine={false}
500
- contentType="media"
501
- title={GALLERY_TITLE}
502
- detail={GALLERY_DETAIL}
503
- amountText="AU$50.00"
504
- gallery={GALLERY_ITEMS}
505
- isUnlocking
506
- onDownloadClick={() => alert('Download gallery')}
507
- />
508
- }
509
- // Sender never unlocks their own message — no distinct state to show.
510
- sent={null}
511
- />
512
- <MatrixTableRow
513
- label="Purchased"
514
- received={
515
- <LockedAttachment
516
- isMine={false}
517
- contentType="media"
518
- title={GALLERY_TITLE}
519
- detail={GALLERY_DETAIL}
520
- amountText="AU$50.00"
521
- paymentStatus="paid"
522
- gallery={GALLERY_ITEMS}
523
- onUnlockClick={() => alert('Unlock clicked')}
524
- onDownloadClick={() => alert('Download gallery')}
525
- />
526
- }
527
- sent={
528
- <LockedAttachment
529
- isMine
530
- contentType="media"
531
- title={GALLERY_TITLE}
532
- detail={GALLERY_DETAIL}
533
- amountText="AU$50.00"
534
- paymentStatus="paid"
535
- gallery={GALLERY_ITEMS}
536
- />
537
- }
538
- />
539
- </MatrixTable>
540
- )
541
- }
@@ -1,117 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest'
2
-
3
- import { renderWithProviders, screen } from '../../../test/utils'
4
-
5
- import MediaCard from './components/Media/Card'
6
- import TextCard from './components/Text/Card'
7
-
8
- /**
9
- * Characterization tests — the safety net for the LockedAttachment
10
- * separate-concerns refactor. They assert stable, user-visible facts only
11
- * (lock affordances, CTAs, amount/status text, the preview toggle), never
12
- * DOM structure or class names — exactly the things the refactor moves
13
- * around. Storybook remains the pixel-level visual oracle.
14
- */
15
- describe('LockedAttachment (characterization)', () => {
16
- describe('Draft', () => {
17
- it('media mode: shows the placeholder title when locked with no title', () => {
18
- renderWithProviders(<MediaCard isDraft />)
19
- expect(screen.getByText('Attachment title')).toBeInTheDocument()
20
- })
21
-
22
- it('media mode: renders a preview toggle when a source fetcher is wired', () => {
23
- renderWithProviders(
24
- <MediaCard isDraft onFetchSource={vi.fn().mockResolvedValue(undefined)} />
25
- )
26
- expect(
27
- screen.getByRole('button', { name: 'Toggle preview' })
28
- ).toBeInTheDocument()
29
- })
30
-
31
- it('media mode: renders a dismiss control when onDismiss is provided', () => {
32
- renderWithProviders(<MediaCard isDraft onDismiss={vi.fn()} />)
33
- expect(
34
- screen.getByRole('button', { name: 'Dismiss attachment' })
35
- ).toBeInTheDocument()
36
- })
37
-
38
- it('text mode: shows the draft text and amount', () => {
39
- renderWithProviders(
40
- <TextCard isDraft amountText="$5" renderedText="Secret draft" />
41
- )
42
- expect(screen.getByText('Secret draft')).toBeInTheDocument()
43
- expect(screen.getByText('$5')).toBeInTheDocument()
44
- })
45
- })
46
-
47
- describe('Preview', () => {
48
- it('media mode: shows the placeholder title when locked with no title', () => {
49
- renderWithProviders(<MediaCard isPreview amountText="$5" />)
50
- expect(screen.getByText('Attachment title')).toBeInTheDocument()
51
- expect(screen.getByText('$5')).toBeInTheDocument()
52
- })
53
-
54
- it('text mode: never shows real text, only the amount', () => {
55
- renderWithProviders(<TextCard isPreview amountText="$5" />)
56
- expect(screen.queryByText('Secret draft')).not.toBeInTheDocument()
57
- expect(screen.getByText('$5')).toBeInTheDocument()
58
- })
59
- })
60
-
61
- describe('Media (isMine)', () => {
62
- it('shows the amount in the status badge', () => {
63
- renderWithProviders(<MediaCard isMine amountText="$5" />)
64
- expect(screen.getByText('$5')).toBeInTheDocument()
65
- })
66
- })
67
-
68
- describe('Text (isMine)', () => {
69
- it('pending: shows the amount with no status label', () => {
70
- renderWithProviders(
71
- <TextCard isMine amountText="$5" paymentStatus="pending" />
72
- )
73
- expect(screen.queryByText('Sent')).not.toBeInTheDocument()
74
- expect(screen.getByText('$5')).toBeInTheDocument()
75
- })
76
-
77
- it('paid: shows the Sold label', () => {
78
- renderWithProviders(
79
- <TextCard isMine amountText="$5" paymentStatus="paid" />
80
- )
81
- expect(screen.getByText('Sold')).toBeInTheDocument()
82
- })
83
- })
84
-
85
- describe('Media (!isMine)', () => {
86
- it('renders the locked-attachment root and an Unlock CTA when locked', () => {
87
- renderWithProviders(
88
- <MediaCard isMine={false} amountText="$5" onUnlockClick={vi.fn()} />
89
- )
90
- expect(screen.getByTestId('locked-attachment')).toBeInTheDocument()
91
- expect(
92
- screen.getByRole('button', { name: 'Unlock' })
93
- ).toBeInTheDocument()
94
- })
95
- })
96
-
97
- describe('Text (!isMine)', () => {
98
- it('shows an Unlock control and the amount', () => {
99
- renderWithProviders(
100
- <TextCard isMine={false} amountText="$5" onUnlockClick={vi.fn()} />
101
- )
102
- expect(
103
- screen.getByRole('button', { name: 'Unlock message' })
104
- ).toBeInTheDocument()
105
- expect(screen.getByText('$5')).toBeInTheDocument()
106
- })
107
-
108
- it('unlocking: marks the Unlock control busy', () => {
109
- renderWithProviders(
110
- <TextCard isMine={false} isUnlocking onUnlockClick={vi.fn()} />
111
- )
112
- const button = screen.getByRole('button', { name: 'Unlocking message' })
113
- expect(button).toBeDisabled()
114
- expect(button).toHaveAttribute('aria-busy', 'true')
115
- })
116
- })
117
- })