@linktr.ee/messaging-react 4.4.7 → 4.5.0-rc-1788297811
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/dist/assets/index.css +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +58 -43
- package/dist/index.js +1167 -1033
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/AttachmentCard/MediaPlayer.test.tsx +9 -5
- package/src/components/AttachmentCard/Thumbnail.test.tsx +4 -0
- package/src/components/ChannelActionsMenu/ChannelActionsMenu.test.tsx +18 -10
- package/src/components/ChannelList/ChannelListContext.test.tsx +7 -5
- package/src/components/ChannelList/CustomChannelPreview.test.tsx +7 -5
- package/src/components/ChannelList/index.test.tsx +1 -0
- package/src/components/ChannelView.test.tsx +20 -13
- package/src/components/CustomLinkPreviewList/CustomLinkPreviewList.test.tsx +4 -3
- package/src/components/CustomMessage/CustomMessage.test.tsx +6 -5
- package/src/components/CustomMessage/MessageTag.test.tsx +6 -0
- package/src/components/CustomMessage/SentMessageDeliveryStatus.test.tsx +19 -9
- package/src/components/CustomMessage/StreamAttachmentMessage.test.tsx +45 -5
- package/src/components/CustomMessage/StreamAttachmentMessage.tsx +6 -0
- package/src/components/CustomMessage/TipMessage/TipMessage.test.tsx +3 -0
- package/src/components/CustomMessageInput/CustomMessageInput.test.tsx +16 -7
- package/src/components/FaqList/FaqList.test.tsx +20 -12
- package/src/components/LinkAttachment/LinkAttachment.test.tsx +27 -14
- package/src/components/LinkAttachment/components/_shared/BubbleTail.test.tsx +1 -0
- package/src/components/LinkAttachment/components/_shared/CardBody.test.tsx +6 -6
- package/src/components/LinkAttachment/components/_shared/CardCta.test.tsx +8 -5
- package/src/components/LinkAttachment/components/_shared/CardShell.test.tsx +7 -5
- package/src/components/LinkAttachment/components/_shared/CardThumbnail.test.tsx +3 -8
- package/src/components/MessageAttachment/Audio/AudioAttachment.stories.tsx +165 -58
- package/src/components/MessageAttachment/Audio/WaveformAudioRow.test.tsx +155 -0
- package/src/components/MessageAttachment/Audio/WaveformAudioRow.tsx +455 -0
- package/src/components/MessageAttachment/Audio/audioRowRequester.test.ts +24 -0
- package/src/components/MessageAttachment/Audio/audioRowRequester.ts +16 -0
- package/src/components/MessageAttachment/Audio/index.tsx +83 -79
- package/src/components/MessageAttachment/MessageAttachment.behavior.test.tsx +22 -15
- package/src/components/MessageAttachment/MessageAttachment.test.tsx +279 -134
- package/src/components/MessageAttachment/_shared/Bubble.test.tsx +2 -0
- package/src/components/MessageAttachment/_shared/CarouselNav.test.tsx +5 -4
- package/src/components/MessageAttachment/_shared/CompactDocumentRow.test.tsx +4 -3
- package/src/components/MessageAttachment/_shared/DismissButton.test.tsx +4 -3
- package/src/components/MessageAttachment/_shared/DownloadAction.test.tsx +8 -5
- package/src/components/MessageAttachment/_shared/ImageViewer.test.tsx +6 -3
- package/src/components/MessageAttachment/_shared/MediaStackGrid.test.tsx +11 -4
- package/src/components/MessageAttachment/_shared/PdfViewer.test.tsx +5 -3
- package/src/components/MessageAttachment/_shared/VideoViewer.test.tsx +12 -3
- package/src/components/MessageAttachment/_shared/ViewerShell.test.tsx +13 -6
- package/src/components/MessageAttachment/_shared/useCarousel.test.tsx +10 -4
- package/src/components/MessageAttachment/_shared/useIsClient.ts +23 -0
- package/src/components/MessageAttachment/index.tsx +25 -9
- package/src/components/MessageAttachment/types.ts +19 -9
- package/src/components/MessageBubble/MessageBubble.test.tsx +8 -0
- package/src/components/RichCard/RichCard.test.tsx +10 -2
- package/src/styles.css +120 -0
|
@@ -1,95 +1,75 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
+
import { useMessageContext } from 'stream-chat-react'
|
|
2
3
|
|
|
3
|
-
import { nativePlayerProps } from '../../../utils/nativeMediaPlayers'
|
|
4
4
|
import Bubble from '../_shared/Bubble'
|
|
5
|
-
import DismissButton from '../_shared/DismissButton'
|
|
6
5
|
import {
|
|
7
6
|
bubbleVariantForState,
|
|
8
7
|
type AudioItem,
|
|
9
8
|
type ComposerExtras,
|
|
10
|
-
type MediaPreloadMode,
|
|
11
9
|
type MessageAttachmentBaseProps,
|
|
12
10
|
type MessageAttachmentState,
|
|
13
11
|
} from '../types'
|
|
14
12
|
|
|
13
|
+
import { audioRowRequester } from './audioRowRequester'
|
|
14
|
+
import WaveformAudioRow, {
|
|
15
|
+
type AudioRowTrailingAction,
|
|
16
|
+
} from './WaveformAudioRow'
|
|
17
|
+
|
|
18
|
+
// Figma `2754:17710` → `attachment-audio` (`…;2754:8062`): a 299×72
|
|
19
|
+
// card on `#1E2330` with a 20/12/20/20 inset, a uniform 20px radius and
|
|
20
|
+
// no stroke. `@linktr.ee/component-library@11.8.6`'s radius scale is
|
|
21
|
+
// 8 / 16 / 24 / 64 / full, so there is no 20px token to reach for.
|
|
22
|
+
//
|
|
23
|
+
// The radius is uniform in every state, so the card opts out of the
|
|
24
|
+
// same-author corner flattening the text bubbles apply — a 32px control
|
|
25
|
+
// tucked into a 4px corner would collide with it.
|
|
26
|
+
const CARD_WIDTH_CLASS = 'w-[299px]'
|
|
27
|
+
const CARD_PADDING_CLASS = 'py-5 pl-5 pr-3'
|
|
28
|
+
const CARD_RADIUS_CLASS = 'rounded-[20px]'
|
|
29
|
+
|
|
15
30
|
export interface AudioAttachmentSharedProps extends MessageAttachmentBaseProps {
|
|
16
31
|
/** Audio source URL (`mp3`, `aac`, `wav`, …). */
|
|
17
32
|
src?: string
|
|
18
|
-
/** MIME type hint —
|
|
33
|
+
/** MIME type hint — gates playability via the player's `canPlayType` probe. */
|
|
19
34
|
mimeType?: string
|
|
20
35
|
/**
|
|
21
|
-
* Filename —
|
|
22
|
-
*
|
|
23
|
-
* expose a built-in title slot, so we don't render the filename
|
|
24
|
-
* inside the bubble itself.
|
|
36
|
+
* Filename — the download default name for the trailing action, and
|
|
37
|
+
* part of each control's accessible name.
|
|
25
38
|
*/
|
|
26
39
|
filename?: string
|
|
27
40
|
/**
|
|
28
41
|
* Stacked audio. Takes precedence over `src` when set. Each item
|
|
29
|
-
* renders its own
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* attachment at a time.
|
|
42
|
+
* renders its own waveform row, vertically stacked inside the same
|
|
43
|
+
* bubble with an 8px gap. Sent + Received only — the composer surface
|
|
44
|
+
* accepts a single attachment at a time.
|
|
33
45
|
*/
|
|
34
46
|
items?: AudioItem[]
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
* - Single audio (no `items`, or `items.length === 1`) →
|
|
40
|
-
* `'metadata'` so the native player surfaces duration
|
|
41
|
-
* immediately.
|
|
42
|
-
* - Stacked audio (`items.length > 1`) → `'none'` so a thread
|
|
43
|
-
* of voice memos doesn't fan out N parallel metadata requests
|
|
44
|
-
* on first paint.
|
|
45
|
-
*
|
|
46
|
-
* Per-track overrides live on `AudioItem.preload`.
|
|
47
|
-
*/
|
|
48
|
-
preload?: MediaPreloadMode
|
|
47
|
+
/** See `AudioItem.durationSeconds`. Applies to the single-`src` shape. */
|
|
48
|
+
durationSeconds?: number
|
|
49
|
+
/** See `AudioItem.waveformData`. Applies to the single-`src` shape. */
|
|
50
|
+
waveformData?: number[]
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
const resolveItems = ({
|
|
52
54
|
src,
|
|
53
55
|
mimeType,
|
|
54
56
|
filename,
|
|
57
|
+
durationSeconds,
|
|
58
|
+
waveformData,
|
|
55
59
|
items,
|
|
56
60
|
}: {
|
|
57
61
|
src?: string
|
|
58
62
|
mimeType?: string
|
|
59
63
|
filename?: string
|
|
64
|
+
durationSeconds?: number
|
|
65
|
+
waveformData?: number[]
|
|
60
66
|
items?: AudioItem[]
|
|
61
67
|
}): AudioItem[] => {
|
|
62
68
|
if (items && items.length > 0) return items
|
|
63
|
-
if (src) return [{ src, mimeType, filename }]
|
|
69
|
+
if (src) return [{ src, mimeType, filename, durationSeconds, waveformData }]
|
|
64
70
|
return []
|
|
65
71
|
}
|
|
66
72
|
|
|
67
|
-
const NativeAudioPlayer: React.FC<{
|
|
68
|
-
item: AudioItem
|
|
69
|
-
preload: MediaPreloadMode
|
|
70
|
-
trailingAction?: React.ReactNode
|
|
71
|
-
}> = ({ item, preload, trailingAction }) => (
|
|
72
|
-
<div className="flex items-center gap-2">
|
|
73
|
-
{/* No `<track>` is rendered — we don't author caption sidecars
|
|
74
|
-
for chat attachments, and an empty `<track kind="captions" />`
|
|
75
|
-
emits a runtime warning. Re-add a real track (with `src`,
|
|
76
|
-
`srcLang`, and `default`) once caption support actually ships.
|
|
77
|
-
The `jsx-a11y/media-has-caption` rule is suppressed for the
|
|
78
|
-
same reason. */}
|
|
79
|
-
{/* eslint-disable-next-line jsx-a11y/media-has-caption */}
|
|
80
|
-
<audio
|
|
81
|
-
{...nativePlayerProps}
|
|
82
|
-
src={item.src}
|
|
83
|
-
controls
|
|
84
|
-
preload={item.preload ?? preload}
|
|
85
|
-
className="block min-w-0 flex-1"
|
|
86
|
-
>
|
|
87
|
-
{item.mimeType ? <source src={item.src} type={item.mimeType} /> : null}
|
|
88
|
-
</audio>
|
|
89
|
-
{trailingAction ?? null}
|
|
90
|
-
</div>
|
|
91
|
-
)
|
|
92
|
-
|
|
93
73
|
interface InternalAudioRowProps extends AudioAttachmentSharedProps {
|
|
94
74
|
state: MessageAttachmentState
|
|
95
75
|
onDismiss?: () => void
|
|
@@ -101,52 +81,74 @@ const AudioAttachmentRow: React.FC<InternalAudioRowProps> = ({
|
|
|
101
81
|
mimeType,
|
|
102
82
|
filename,
|
|
103
83
|
items,
|
|
84
|
+
durationSeconds,
|
|
85
|
+
waveformData,
|
|
104
86
|
text,
|
|
105
87
|
groupPosition,
|
|
106
|
-
preload,
|
|
107
88
|
onDismiss,
|
|
108
89
|
}) => {
|
|
90
|
+
// Namespaces the pooled players per message, so scrolling a
|
|
91
|
+
// virtualized thread away and back restores the playback position
|
|
92
|
+
// instead of starting a fresh player. Returns `{}` (silently) outside
|
|
93
|
+
// a message, which is the composer case.
|
|
94
|
+
const { message, threadList } = useMessageContext()
|
|
95
|
+
// Fallback namespace for a card rendered outside a message — the
|
|
96
|
+
// composer, a story, a consumer mounting one directly. Stable for the
|
|
97
|
+
// life of the component, which is all those surfaces need; a message
|
|
98
|
+
// id is preferred because it also survives a virtualized thread
|
|
99
|
+
// scrolling the row away and back.
|
|
100
|
+
const instanceId = React.useId()
|
|
101
|
+
|
|
109
102
|
const variant = bubbleVariantForState(state)
|
|
110
|
-
const
|
|
111
|
-
|
|
103
|
+
const resolvedItems = resolveItems({
|
|
104
|
+
src,
|
|
105
|
+
mimeType,
|
|
106
|
+
filename,
|
|
107
|
+
durationSeconds,
|
|
108
|
+
waveformData,
|
|
109
|
+
items,
|
|
110
|
+
})
|
|
112
111
|
|
|
113
112
|
if (resolvedItems.length === 0) {
|
|
114
113
|
return null
|
|
115
114
|
}
|
|
116
115
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
116
|
+
const requesterBase = message?.id
|
|
117
|
+
? `${threadList ? (message.parent_id ?? message.id) : ''}${message.id}`
|
|
118
|
+
: instanceId
|
|
119
|
+
|
|
120
|
+
// The composer's single action is removing the draft; every other
|
|
121
|
+
// surface carries forward the download the native player's own kebab
|
|
122
|
+
// used to provide. See `AudioRowTrailingAction`.
|
|
123
|
+
// A composer draft mounted without `onDismiss` gets no trailing
|
|
124
|
+
// control: falling through to `download` would hand a draft the
|
|
125
|
+
// sent-only action, which the native player it replaces never did.
|
|
126
|
+
const trailingAction: AudioRowTrailingAction | undefined =
|
|
127
|
+
state === 'composer'
|
|
128
|
+
? onDismiss
|
|
129
|
+
? { kind: 'dismiss', onDismiss }
|
|
130
|
+
: undefined
|
|
131
|
+
: { kind: 'download' }
|
|
123
132
|
|
|
124
133
|
return (
|
|
125
134
|
<Bubble
|
|
126
135
|
variant={variant}
|
|
127
136
|
text={text}
|
|
128
137
|
groupPosition={groupPosition}
|
|
138
|
+
bordered={false}
|
|
139
|
+
widthClassName={CARD_WIDTH_CLASS}
|
|
140
|
+
paddingClassName={CARD_PADDING_CLASS}
|
|
141
|
+
radiusClassName={CARD_RADIUS_CLASS}
|
|
129
142
|
data-testid="audio-attachment"
|
|
130
143
|
>
|
|
131
|
-
{/* Native `<audio controls>` already exposes a download in its
|
|
132
|
-
kebab menu, so we don't render a separate Download button.
|
|
133
|
-
The element also has no built-in title slot — the dismiss
|
|
134
|
-
button (Composer only) sits inline next to the player.
|
|
135
|
-
Stacked players get an 8px vertical gap so each track reads
|
|
136
|
-
as a discrete attachment. */}
|
|
137
144
|
<div className="flex flex-col gap-2">
|
|
138
145
|
{resolvedItems.map((item, index) => (
|
|
139
|
-
<
|
|
146
|
+
<WaveformAudioRow
|
|
140
147
|
key={`${item.src}-${index}`}
|
|
141
148
|
item={item}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
// dismiss control sits on the only player.
|
|
146
|
-
showDismiss && index === 0 ? (
|
|
147
|
-
<DismissButton onClick={onDismiss!} variant="inline" />
|
|
148
|
-
) : undefined
|
|
149
|
-
}
|
|
149
|
+
variant={variant}
|
|
150
|
+
requester={audioRowRequester(requesterBase, index)}
|
|
151
|
+
trailingAction={trailingAction}
|
|
150
152
|
/>
|
|
151
153
|
))}
|
|
152
154
|
</div>
|
|
@@ -166,8 +168,10 @@ export interface AudioComposerProps extends ComposerExtras {
|
|
|
166
168
|
src?: string
|
|
167
169
|
mimeType?: string
|
|
168
170
|
filename?: string
|
|
169
|
-
/** See `
|
|
170
|
-
|
|
171
|
+
/** See `AudioItem.durationSeconds`. */
|
|
172
|
+
durationSeconds?: number
|
|
173
|
+
/** See `AudioItem.waveformData`. */
|
|
174
|
+
waveformData?: number[]
|
|
171
175
|
}
|
|
172
176
|
export type AudioSentProps = AudioAttachmentSharedProps
|
|
173
177
|
export type AudioReceivedProps = AudioAttachmentSharedProps
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { userEvent, renderWithProviders, screen } from '../../test/utils'
|
|
4
4
|
|
|
5
5
|
import { triggerDownload } from './_shared/triggerDownload'
|
|
6
6
|
import { bubbleVariantForState, bubbleGroupPositionFromStream } from './types'
|
|
@@ -14,7 +14,8 @@ vi.mock('./_shared/triggerDownload', () => ({
|
|
|
14
14
|
const mockedDownload = vi.mocked(triggerDownload)
|
|
15
15
|
|
|
16
16
|
describe('MessageAttachment behavior', () => {
|
|
17
|
-
it('renders file rows with precedence, fallback names, and cancellation', () => {
|
|
17
|
+
it('renders file rows with precedence, fallback names, and cancellation', async () => {
|
|
18
|
+
const user = userEvent.setup()
|
|
18
19
|
const onClick = vi.fn(() => false)
|
|
19
20
|
const { rerender } = renderWithProviders(
|
|
20
21
|
<MessageAttachment.File.Sent
|
|
@@ -32,14 +33,14 @@ describe('MessageAttachment behavior', () => {
|
|
|
32
33
|
)
|
|
33
34
|
expect(screen.getByText('first.pdf')).toBeInTheDocument()
|
|
34
35
|
expect(screen.getByText('second.pdf')).toBeInTheDocument()
|
|
35
|
-
|
|
36
|
+
await user.click(screen.getByRole('button', { name: 'Download first.pdf' }))
|
|
36
37
|
expect(onClick).toHaveBeenCalledWith(0)
|
|
37
38
|
expect(mockedDownload).not.toHaveBeenCalled()
|
|
38
39
|
|
|
39
40
|
rerender(
|
|
40
41
|
<MessageAttachment.File.Sent src="https://cdn.example.com/fallback.txt" />
|
|
41
42
|
)
|
|
42
|
-
|
|
43
|
+
await user.click(
|
|
43
44
|
screen.getByRole('button', { name: 'Download fallback.txt' })
|
|
44
45
|
)
|
|
45
46
|
expect(mockedDownload).toHaveBeenCalledWith(
|
|
@@ -48,10 +49,12 @@ describe('MessageAttachment behavior', () => {
|
|
|
48
49
|
)
|
|
49
50
|
})
|
|
50
51
|
|
|
51
|
-
it('renders no file when inputs are absent and supports composer dismissal', () => {
|
|
52
|
+
it('renders no file when inputs are absent and supports composer dismissal', async () => {
|
|
53
|
+
const user = userEvent.setup()
|
|
52
54
|
const { container } = renderWithProviders(
|
|
53
55
|
<MessageAttachment.File.Received />
|
|
54
56
|
)
|
|
57
|
+
// eslint-disable-next-line testing-library/no-node-access -- the null-rendering DOM contract has no semantic query.
|
|
55
58
|
expect(container.firstChild).toBeNull()
|
|
56
59
|
const onDismiss = vi.fn()
|
|
57
60
|
renderWithProviders(
|
|
@@ -60,11 +63,12 @@ describe('MessageAttachment behavior', () => {
|
|
|
60
63
|
onDismiss={onDismiss}
|
|
61
64
|
/>
|
|
62
65
|
)
|
|
63
|
-
|
|
66
|
+
await user.click(screen.getByRole('button', { name: 'Dismiss attachment' }))
|
|
64
67
|
expect(onDismiss).toHaveBeenCalledOnce()
|
|
65
68
|
})
|
|
66
69
|
|
|
67
|
-
it('renders image stacks, forwards loading, and supports click cancellation', () => {
|
|
70
|
+
it('renders image stacks, forwards loading, and supports click cancellation', async () => {
|
|
71
|
+
const user = userEvent.setup()
|
|
68
72
|
const onClick = vi.fn(() => false)
|
|
69
73
|
renderWithProviders(
|
|
70
74
|
<MessageAttachment.Image.Sent
|
|
@@ -79,12 +83,13 @@ describe('MessageAttachment behavior', () => {
|
|
|
79
83
|
)
|
|
80
84
|
expect(screen.getAllByRole('img')[0]).toHaveAttribute('loading', 'lazy')
|
|
81
85
|
expect(screen.getAllByRole('img')).toHaveLength(2)
|
|
82
|
-
|
|
86
|
+
await user.click(screen.getByRole('button', { name: 'Open image 2 of 2' }))
|
|
83
87
|
expect(onClick).toHaveBeenCalledWith(1)
|
|
84
88
|
expect(screen.queryByTestId('image-viewer')).not.toHaveAttribute('open')
|
|
85
89
|
})
|
|
86
90
|
|
|
87
|
-
it('opens images at the clicked index and suffixes a shared filename', () => {
|
|
91
|
+
it('opens images at the clicked index and suffixes a shared filename', async () => {
|
|
92
|
+
const user = userEvent.setup()
|
|
88
93
|
renderWithProviders(
|
|
89
94
|
<MessageAttachment.Image.Sent
|
|
90
95
|
filename="photos.jpg"
|
|
@@ -94,7 +99,7 @@ describe('MessageAttachment behavior', () => {
|
|
|
94
99
|
]}
|
|
95
100
|
/>
|
|
96
101
|
)
|
|
97
|
-
|
|
102
|
+
await user.click(screen.getByRole('button', { name: 'Open image 2 of 2' }))
|
|
98
103
|
expect(screen.getByTestId('image-viewer')).toHaveAttribute(
|
|
99
104
|
'aria-label',
|
|
100
105
|
'photos.jpg (2)'
|
|
@@ -105,7 +110,8 @@ describe('MessageAttachment behavior', () => {
|
|
|
105
110
|
)
|
|
106
111
|
})
|
|
107
112
|
|
|
108
|
-
it('renders video posters and posterless first-frame previews', () => {
|
|
113
|
+
it('renders video posters and posterless first-frame previews', async () => {
|
|
114
|
+
const user = userEvent.setup()
|
|
109
115
|
const onDismiss = vi.fn()
|
|
110
116
|
const { rerender } = renderWithProviders(
|
|
111
117
|
<MessageAttachment.Video.Composer
|
|
@@ -119,7 +125,7 @@ describe('MessageAttachment behavior', () => {
|
|
|
119
125
|
expect(
|
|
120
126
|
screen.getByRole('img', { name: 'Video 1 thumbnail' })
|
|
121
127
|
).toHaveAttribute('src', '/poster.jpg')
|
|
122
|
-
|
|
128
|
+
await user.click(screen.getByRole('button', { name: 'Dismiss attachment' }))
|
|
123
129
|
expect(onDismiss).toHaveBeenCalledOnce()
|
|
124
130
|
rerender(
|
|
125
131
|
<MessageAttachment.Video.Sent src="https://cdn.example.com/video.mp4" />
|
|
@@ -172,7 +178,8 @@ describe('MessageAttachment behavior', () => {
|
|
|
172
178
|
expect(screen.queryByLabelText('Video 1 thumbnail')).not.toBeInTheDocument()
|
|
173
179
|
})
|
|
174
180
|
|
|
175
|
-
it('supports PDF titles, download actions, dismiss, and viewer index', () => {
|
|
181
|
+
it('supports PDF titles, download actions, dismiss, and viewer index', async () => {
|
|
182
|
+
const user = userEvent.setup()
|
|
176
183
|
const onDismiss = vi.fn()
|
|
177
184
|
renderWithProviders(
|
|
178
185
|
<MessageAttachment.Pdf.Composer
|
|
@@ -182,7 +189,7 @@ describe('MessageAttachment behavior', () => {
|
|
|
182
189
|
/>
|
|
183
190
|
)
|
|
184
191
|
expect(screen.getByText('Quarterly report')).toBeInTheDocument()
|
|
185
|
-
|
|
192
|
+
await user.click(screen.getByRole('button', { name: 'Dismiss attachment' }))
|
|
186
193
|
expect(onDismiss).toHaveBeenCalledOnce()
|
|
187
194
|
|
|
188
195
|
renderWithProviders(
|
|
@@ -193,7 +200,7 @@ describe('MessageAttachment behavior', () => {
|
|
|
193
200
|
]}
|
|
194
201
|
/>
|
|
195
202
|
)
|
|
196
|
-
|
|
203
|
+
await user.click(screen.getByRole('button', { name: 'Open two.pdf' }))
|
|
197
204
|
expect(screen.getAllByTestId('pdf-viewer').at(-1)).toHaveAttribute(
|
|
198
205
|
'aria-label',
|
|
199
206
|
'two.pdf'
|