Skip to content

Commit a428dc9

Browse files
authored
feat: allow custom ReactionsListModal (#2632)
1 parent 6f2de4e commit a428dc9

File tree

6 files changed

+64
-13
lines changed

6 files changed

+64
-13
lines changed

src/components/Channel/Channel.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ type ChannelPropsForwardedToComponentContext<
150150
| 'reactionOptions'
151151
| 'ReactionSelector'
152152
| 'ReactionsList'
153+
| 'ReactionsListModal'
153154
| 'SendButton'
154155
| 'StartRecordingAudioButton'
155156
| 'ThreadHead'
@@ -1340,6 +1341,7 @@ const ChannelInner = <
13401341
reactionOptions: props.reactionOptions,
13411342
ReactionSelector: props.ReactionSelector,
13421343
ReactionsList: props.ReactionsList,
1344+
ReactionsListModal: props.ReactionsListModal,
13431345
SendButton: props.SendButton,
13441346
StartRecordingAudioButton: props.StartRecordingAudioButton,
13451347
StopAIGenerationButton: props.StopAIGenerationButton,
@@ -1399,6 +1401,7 @@ const ChannelInner = <
13991401
props.QuotedPoll,
14001402
props.ReactionSelector,
14011403
props.ReactionsList,
1404+
props.ReactionsListModal,
14021405
props.SendButton,
14031406
props.StartRecordingAudioButton,
14041407
props.ThreadHead,

src/components/Reactions/ReactionsList.tsx

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
import React, { useState } from 'react';
22
import clsx from 'clsx';
33

4-
import type { ReactionGroupResponse, ReactionResponse } from 'stream-chat';
5-
4+
import {
5+
ReactionsListModal as DefaultReactionsListModal,
6+
ReactionsListModalProps,
7+
} from './ReactionsListModal';
68
import { useProcessReactions } from './hooks/useProcessReactions';
9+
import {
10+
MessageContextValue,
11+
useComponentContext,
12+
useTranslationContext,
13+
} from '../../context';
14+
15+
import { MAX_MESSAGE_REACTIONS_TO_FETCH } from '../Message/hooks';
16+
17+
import type { ReactionGroupResponse, ReactionResponse, ReactionSort } from 'stream-chat';
718
import type { DefaultStreamChatGenerics } from '../../types/types';
819
import type { ReactionOptions } from './reactionOptions';
920
import type {
1021
ReactionDetailsComparator,
1122
ReactionsComparator,
1223
ReactionType,
1324
} from './types';
14-
import { ReactionsListModal } from './ReactionsListModal';
15-
import { MessageContextValue, useTranslationContext } from '../../context';
16-
import { MAX_MESSAGE_REACTIONS_TO_FETCH } from '../Message/hooks';
1725

1826
export type ReactionsListProps<
1927
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
@@ -44,7 +52,7 @@ export type ReactionsListProps<
4452
/** Comparator function to sort the list of reacted users
4553
* @deprecated use `reactionDetailsSort` instead
4654
*/
47-
sortReactionDetails?: ReactionDetailsComparator;
55+
sortReactionDetails?: ReactionDetailsComparator<StreamChatGenerics>;
4856
/** Comparator function to sort reactions, defaults to chronological order */
4957
sortReactions?: ReactionsComparator;
5058
};
@@ -67,6 +75,7 @@ const UnMemoizedReactionsList = <
6775
const [selectedReactionType, setSelectedReactionType] =
6876
useState<ReactionType<StreamChatGenerics> | null>(null);
6977
const { t } = useTranslationContext('ReactionsList');
78+
const { ReactionsListModal = DefaultReactionsListModal } = useComponentContext();
7079

7180
const handleReactionButtonClick = (reactionType: string) => {
7281
if (totalReactionCount > MAX_MESSAGE_REACTIONS_TO_FETCH) {
@@ -126,13 +135,25 @@ const UnMemoizedReactionsList = <
126135
</div>
127136
{selectedReactionType !== null && (
128137
<ReactionsListModal
129-
handleFetchReactions={handleFetchReactions}
138+
handleFetchReactions={
139+
handleFetchReactions as (
140+
reactionType?: string,
141+
sort?: ReactionSort<StreamChatGenerics>,
142+
) => Promise<Array<ReactionResponse<StreamChatGenerics>>>
143+
}
130144
onClose={() => setSelectedReactionType(null)}
131-
onSelectedReactionTypeChange={setSelectedReactionType}
145+
onSelectedReactionTypeChange={
146+
setSelectedReactionType as ReactionsListModalProps['onSelectedReactionTypeChange']
147+
}
132148
open={selectedReactionType !== null}
133149
reactions={existingReactions}
134150
selectedReactionType={selectedReactionType}
135-
sortReactionDetails={sortReactionDetails}
151+
sortReactionDetails={
152+
sortReactionDetails as (
153+
a: ReactionResponse<StreamChatGenerics>,
154+
b: ReactionResponse<StreamChatGenerics>,
155+
) => number
156+
}
136157
/>
137158
)}
138159
</>

src/components/Reactions/ReactionsListModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { MessageContextValue, useMessageContext } from '../../context';
1111
import { DefaultStreamChatGenerics } from '../../types/types';
1212
import { ReactionSort } from 'stream-chat';
1313

14-
type ReactionsListModalProps<
14+
export type ReactionsListModalProps<
1515
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
1616
> = ModalProps &
1717
Partial<

src/components/Reactions/__tests__/ReactionsList.test.js

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import { render } from '@testing-library/react';
2+
import { act, fireEvent, render, screen } from '@testing-library/react';
33
import '@testing-library/jest-dom';
44
import { toHaveNoViolations } from 'jest-axe';
55

@@ -16,15 +16,21 @@ import { defaultReactionOptions } from '../reactionOptions';
1616

1717
const USER_ID = 'mark';
1818

19-
const renderComponent = ({ reaction_groups = {}, ...props }) => {
19+
const renderComponent = ({
20+
reaction_groups = {},
21+
ReactionsListModal = undefined,
22+
...props
23+
}) => {
2024
const reactions = Object.entries(reaction_groups).flatMap(([type, { count }]) =>
2125
Array.from({ length: count }, (_, i) =>
2226
generateReaction({ type, user: { id: `${USER_ID}-${i}` } }),
2327
),
2428
);
2529

2630
return render(
27-
<ComponentProvider value={{ reactionOptions: defaultReactionOptions }}>
31+
<ComponentProvider
32+
value={{ reactionOptions: defaultReactionOptions, ReactionsListModal }}
33+
>
2834
<MessageProvider value={{}}>
2935
<ReactionsList
3036
reaction_groups={reaction_groups}
@@ -43,6 +49,23 @@ describe('ReactionsList', () => {
4349
// disable warnings (unreachable context)
4450
jest.spyOn(console, 'warn').mockImplementation(null);
4551

52+
it('renders custom ReactionsListModal', async () => {
53+
const CUSTOM_MODAL_TEST_ID = 'custom-reaction-list-modal';
54+
const ReactionsListModal = () => <div data-testid={CUSTOM_MODAL_TEST_ID} />;
55+
renderComponent({
56+
reaction_groups: {
57+
haha: { count: 2 },
58+
love: { count: 5 },
59+
},
60+
ReactionsListModal,
61+
});
62+
63+
await act(() => {
64+
fireEvent.click(screen.getByTestId('reactions-list-button-haha'));
65+
});
66+
expect(screen.getByTestId(CUSTOM_MODAL_TEST_ID)).toBeInTheDocument();
67+
});
68+
4669
it('should render the total reaction count', async () => {
4770
const { container, getByText } = renderComponent({
4871
reaction_groups: {

src/components/Reactions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export * from './ReactionSelector';
22
export * from './ReactionsList';
3+
export * from './ReactionsListModal';
34
export * from './SimpleReactionsList';
45
export * from './SpriteImage';
56
export * from './StreamEmoji';

src/context/ComponentContext.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
QuotedMessagePreviewProps,
3535
ReactionOptions,
3636
ReactionSelectorProps,
37+
ReactionsListModalProps,
3738
ReactionsListProps,
3839
RecordingPermissionDeniedNotificationProps,
3940
SendButtonProps,
@@ -176,6 +177,8 @@ export type ComponentContextValue<
176177
>;
177178
/** Custom UI component to display the list of reactions on a message, defaults to and accepts same props as: [ReactionsList](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Reactions/ReactionsList.tsx) */
178179
ReactionsList?: React.ComponentType<ReactionsListProps<StreamChatGenerics>>;
180+
/** Custom UI component to display the reactions modal, defaults to and accepts same props as: [ReactionsListModal](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Reactions/ReactionsListModal.tsx) */
181+
ReactionsListModal?: React.ComponentType<ReactionsListModalProps<StreamChatGenerics>>;
179182
RecordingPermissionDeniedNotification?: React.ComponentType<RecordingPermissionDeniedNotificationProps>;
180183
/** Custom UI component for send button, defaults to and accepts same props as: [SendButton](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageInput/icons.tsx) */
181184
SendButton?: React.ComponentType<SendButtonProps<StreamChatGenerics>>;

0 commit comments

Comments
 (0)