@linktr.ee/messaging-react 1.24.2 → 1.24.3-rc-1773288966
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/index.d.ts +2 -1
- package/dist/index.js +906 -900
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/ChannelView.test.tsx +147 -0
- package/src/components/ChannelView.tsx +6 -0
- package/src/components/MessagingShell/index.tsx +2 -0
- package/src/types.ts +2 -0
package/package.json
CHANGED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import type { Channel } from 'stream-chat'
|
|
3
|
+
import { describe, expect, it, vi, beforeEach } from 'vitest'
|
|
4
|
+
|
|
5
|
+
import { renderWithProviders, screen } from '../test/utils'
|
|
6
|
+
|
|
7
|
+
import { ChannelView } from './ChannelView'
|
|
8
|
+
|
|
9
|
+
let activeChannel: unknown
|
|
10
|
+
|
|
11
|
+
vi.mock('stream-chat-react', () => ({
|
|
12
|
+
Channel: ({
|
|
13
|
+
channel,
|
|
14
|
+
children,
|
|
15
|
+
}: {
|
|
16
|
+
channel: unknown
|
|
17
|
+
children: React.ReactNode
|
|
18
|
+
}) => {
|
|
19
|
+
activeChannel = channel
|
|
20
|
+
return <div data-testid="channel">{children}</div>
|
|
21
|
+
},
|
|
22
|
+
Window: ({ children }: { children: React.ReactNode }) => (
|
|
23
|
+
<div data-testid="window">{children}</div>
|
|
24
|
+
),
|
|
25
|
+
MessageList: () => <div data-testid="message-list" />,
|
|
26
|
+
WithComponents: ({ children }: { children: React.ReactNode }) => (
|
|
27
|
+
<>{children}</>
|
|
28
|
+
),
|
|
29
|
+
useMessageContext: () => ({ message: { id: 'message-1', text: 'hello' } }),
|
|
30
|
+
useChannelStateContext: () => ({ channel: activeChannel }),
|
|
31
|
+
}))
|
|
32
|
+
|
|
33
|
+
vi.mock('../providers/MessagingProvider', () => ({
|
|
34
|
+
useMessagingContext: () => ({ service: null, debug: false }),
|
|
35
|
+
}))
|
|
36
|
+
|
|
37
|
+
vi.mock('./CustomMessageInput', () => ({
|
|
38
|
+
CustomMessageInput: ({
|
|
39
|
+
renderActions,
|
|
40
|
+
}: {
|
|
41
|
+
renderActions?: () => React.ReactNode
|
|
42
|
+
}) => <div data-testid="message-input">{renderActions?.()}</div>,
|
|
43
|
+
}))
|
|
44
|
+
|
|
45
|
+
vi.mock('./CustomMessage', () => ({
|
|
46
|
+
CustomMessage: () => <div data-testid="custom-message" />,
|
|
47
|
+
}))
|
|
48
|
+
|
|
49
|
+
vi.mock('./CustomSystemMessage', () => ({
|
|
50
|
+
CustomSystemMessage: () => <div data-testid="custom-system-message" />,
|
|
51
|
+
}))
|
|
52
|
+
|
|
53
|
+
vi.mock('./CustomDateSeparator', () => ({
|
|
54
|
+
CustomDateSeparator: () => <div data-testid="custom-date-separator" />,
|
|
55
|
+
}))
|
|
56
|
+
|
|
57
|
+
vi.mock('./Avatar', () => ({
|
|
58
|
+
Avatar: () => <div data-testid="avatar" />,
|
|
59
|
+
}))
|
|
60
|
+
|
|
61
|
+
const createChannel = () =>
|
|
62
|
+
({
|
|
63
|
+
id: 'channel-1',
|
|
64
|
+
cid: 'messaging:channel-1',
|
|
65
|
+
data: {},
|
|
66
|
+
_client: { userID: 'visitor-1' },
|
|
67
|
+
state: {
|
|
68
|
+
members: {
|
|
69
|
+
visitor: { user: { id: 'visitor-1', name: 'Visitor' }, role: 'owner' },
|
|
70
|
+
linker: { user: { id: 'linker-1', name: 'Linker' }, role: 'member' },
|
|
71
|
+
},
|
|
72
|
+
membership: {},
|
|
73
|
+
messages: [],
|
|
74
|
+
},
|
|
75
|
+
on: vi.fn(),
|
|
76
|
+
off: vi.fn(),
|
|
77
|
+
sendMessage: vi.fn(),
|
|
78
|
+
hide: vi.fn(),
|
|
79
|
+
pin: vi.fn(),
|
|
80
|
+
unpin: vi.fn(),
|
|
81
|
+
}) as unknown as Channel
|
|
82
|
+
|
|
83
|
+
describe('ChannelView', () => {
|
|
84
|
+
beforeEach(() => {
|
|
85
|
+
activeChannel = undefined
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('renders conversation footer between message list and message input', () => {
|
|
89
|
+
const channel = createChannel()
|
|
90
|
+
const renderConversationFooter = vi.fn((currentChannel: Channel) => (
|
|
91
|
+
<div data-testid="conversation-footer">
|
|
92
|
+
footer-{currentChannel.id}
|
|
93
|
+
</div>
|
|
94
|
+
))
|
|
95
|
+
|
|
96
|
+
renderWithProviders(
|
|
97
|
+
<ChannelView
|
|
98
|
+
channel={channel}
|
|
99
|
+
renderConversationFooter={renderConversationFooter}
|
|
100
|
+
/>
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
const messageList = screen.getByTestId('message-list')
|
|
104
|
+
const conversationFooter = screen.getByTestId('conversation-footer')
|
|
105
|
+
const messageInput = screen.getByTestId('message-input')
|
|
106
|
+
|
|
107
|
+
expect(renderConversationFooter).toHaveBeenCalledWith(channel)
|
|
108
|
+
expect(conversationFooter).toHaveTextContent('footer-channel-1')
|
|
109
|
+
expect(
|
|
110
|
+
messageList.compareDocumentPosition(conversationFooter) &
|
|
111
|
+
Node.DOCUMENT_POSITION_FOLLOWING
|
|
112
|
+
).toBeTruthy()
|
|
113
|
+
expect(
|
|
114
|
+
conversationFooter.compareDocumentPosition(messageInput) &
|
|
115
|
+
Node.DOCUMENT_POSITION_FOLLOWING
|
|
116
|
+
).toBeTruthy()
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('does not render conversation footer when no renderer is provided', () => {
|
|
120
|
+
renderWithProviders(<ChannelView channel={createChannel()} />)
|
|
121
|
+
|
|
122
|
+
expect(screen.queryByTestId('conversation-footer')).not.toBeInTheDocument()
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('keeps channel banner and input action renderers working', () => {
|
|
126
|
+
const channel = createChannel()
|
|
127
|
+
const renderMessageInputActions = vi.fn((currentChannel: Channel) => (
|
|
128
|
+
<button data-testid="message-input-action">{currentChannel.id}</button>
|
|
129
|
+
))
|
|
130
|
+
|
|
131
|
+
renderWithProviders(
|
|
132
|
+
<ChannelView
|
|
133
|
+
channel={channel}
|
|
134
|
+
renderChannelBanner={() => (
|
|
135
|
+
<div data-testid="channel-banner">channel-banner</div>
|
|
136
|
+
)}
|
|
137
|
+
renderMessageInputActions={renderMessageInputActions}
|
|
138
|
+
/>
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
expect(screen.getByTestId('channel-banner')).toBeInTheDocument()
|
|
142
|
+
expect(screen.getByTestId('message-input-action')).toHaveTextContent(
|
|
143
|
+
'channel-1'
|
|
144
|
+
)
|
|
145
|
+
expect(renderMessageInputActions).toHaveBeenCalledWith(channel)
|
|
146
|
+
})
|
|
147
|
+
})
|
|
@@ -524,6 +524,7 @@ const ChannelViewInner: React.FC<{
|
|
|
524
524
|
onBack?: () => void
|
|
525
525
|
showBackButton: boolean
|
|
526
526
|
renderMessageInputActions?: (channel: ChannelType) => React.ReactNode
|
|
527
|
+
renderConversationFooter?: (channel: ChannelType) => React.ReactNode
|
|
527
528
|
onLeaveConversation?: (channel: ChannelType) => void
|
|
528
529
|
onBlockParticipant?: (participantId?: string) => void
|
|
529
530
|
CustomChannelEmptyState?: React.ComponentType
|
|
@@ -543,6 +544,7 @@ const ChannelViewInner: React.FC<{
|
|
|
543
544
|
onBack,
|
|
544
545
|
showBackButton,
|
|
545
546
|
renderMessageInputActions,
|
|
547
|
+
renderConversationFooter,
|
|
546
548
|
onLeaveConversation,
|
|
547
549
|
onBlockParticipant,
|
|
548
550
|
showDeleteConversation = true,
|
|
@@ -640,6 +642,8 @@ const ChannelViewInner: React.FC<{
|
|
|
640
642
|
/>
|
|
641
643
|
</div>
|
|
642
644
|
|
|
645
|
+
{renderConversationFooter?.(channel)}
|
|
646
|
+
|
|
643
647
|
{/* Message Input */}
|
|
644
648
|
<CustomMessageInput
|
|
645
649
|
renderActions={() => renderMessageInputActions?.(channel)}
|
|
@@ -675,6 +679,7 @@ export const ChannelView = React.memo<ChannelViewProps>(
|
|
|
675
679
|
onBack,
|
|
676
680
|
showBackButton = false,
|
|
677
681
|
renderMessageInputActions,
|
|
682
|
+
renderConversationFooter,
|
|
678
683
|
onLeaveConversation,
|
|
679
684
|
onBlockParticipant,
|
|
680
685
|
className,
|
|
@@ -755,6 +760,7 @@ export const ChannelView = React.memo<ChannelViewProps>(
|
|
|
755
760
|
onBack={onBack}
|
|
756
761
|
showBackButton={showBackButton}
|
|
757
762
|
renderMessageInputActions={renderMessageInputActions}
|
|
763
|
+
renderConversationFooter={renderConversationFooter}
|
|
758
764
|
onLeaveConversation={onLeaveConversation}
|
|
759
765
|
onBlockParticipant={onBlockParticipant}
|
|
760
766
|
CustomChannelEmptyState={CustomChannelEmptyState}
|
|
@@ -19,6 +19,7 @@ export const MessagingShell: React.FC<MessagingShellProps> = ({
|
|
|
19
19
|
capabilities = {},
|
|
20
20
|
className,
|
|
21
21
|
renderMessageInputActions,
|
|
22
|
+
renderConversationFooter,
|
|
22
23
|
onChannelSelect,
|
|
23
24
|
onParticipantSelect,
|
|
24
25
|
initialParticipantFilter,
|
|
@@ -484,6 +485,7 @@ export const MessagingShell: React.FC<MessagingShellProps> = ({
|
|
|
484
485
|
onBack={handleBackToChannelList}
|
|
485
486
|
showBackButton={!directConversationMode}
|
|
486
487
|
renderMessageInputActions={renderMessageInputActions}
|
|
488
|
+
renderConversationFooter={renderConversationFooter}
|
|
487
489
|
renderChannelBanner={renderChannelBanner}
|
|
488
490
|
onLeaveConversation={handleLeaveConversation}
|
|
489
491
|
onBlockParticipant={handleBlockParticipant}
|
package/src/types.ts
CHANGED
|
@@ -88,6 +88,7 @@ export interface ChannelViewProps {
|
|
|
88
88
|
onBack?: () => void
|
|
89
89
|
showBackButton?: boolean
|
|
90
90
|
renderMessageInputActions?: (channel: Channel) => React.ReactNode
|
|
91
|
+
renderConversationFooter?: (channel: Channel) => React.ReactNode
|
|
91
92
|
onLeaveConversation?: (channel: Channel) => void
|
|
92
93
|
onBlockParticipant?: (participantId?: string) => void
|
|
93
94
|
className?: string
|
|
@@ -192,6 +193,7 @@ export interface ChannelViewProps {
|
|
|
192
193
|
export type ChannelViewPassthroughProps = Pick<
|
|
193
194
|
ChannelViewProps,
|
|
194
195
|
| 'renderMessageInputActions'
|
|
196
|
+
| 'renderConversationFooter'
|
|
195
197
|
| 'CustomChannelEmptyState'
|
|
196
198
|
| 'onDeleteConversationClick'
|
|
197
199
|
| 'onBlockParticipantClick'
|