Skip to content

Commit 985f5e3

Browse files
authored
feat: replace SuggestionItem prop with suggestionItemComponents prop for SuggestionList (#2693)
1 parent 7e5f19b commit 985f5e3

File tree

7 files changed

+55
-39
lines changed

7 files changed

+55
-39
lines changed

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@
145145
"emoji-mart": "^5.4.0",
146146
"react": "^19.0.0 || ^18.0.0 || ^17.0.0 || ^16.14.0",
147147
"react-dom": "^19.0.0 || ^18.0.0 || ^17.0.0 || ^16.14.0",
148-
"stream-chat": "^9.0.0-rc.13"
148+
"stream-chat": "^9.0.0-rc.15"
149149
},
150150
"peerDependenciesMeta": {
151151
"@breezystack/lamejs": {
@@ -239,7 +239,7 @@
239239
"react": "^19.0.0",
240240
"react-dom": "^19.0.0",
241241
"semantic-release": "^24.2.3",
242-
"stream-chat": "^9.0.0-rc.13",
242+
"stream-chat": "^9.0.0-rc.15",
243243
"ts-jest": "^29.2.5",
244244
"typescript": "^5.4.5",
245245
"typescript-eslint": "^8.17.0"

src/components/MessageInput/__tests__/EditMessageForm.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -926,7 +926,7 @@ describe(`EditMessageForm`, () => {
926926
const CustomStateSetter = () => {
927927
const composer = useMessageComposerMock();
928928
useEffect(() => {
929-
composer.customDataManager.setData(customMessageData);
929+
composer.customDataManager.setMessageData(customMessageData);
930930
}, [composer]);
931931
};
932932
const { container, submit } = await renderComponent({
@@ -954,7 +954,7 @@ describe(`EditMessageForm`, () => {
954954
const CustomStateSetter = () => {
955955
const composer = useMessageComposerMock();
956956
useEffect(() => {
957-
composer.customDataManager.setData(customMessageData);
957+
composer.customDataManager.setMessageData(customMessageData);
958958
}, [composer]);
959959
};
960960
const { container, submit } = await renderComponent({

src/components/MessageInput/__tests__/MessageInput.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -794,7 +794,7 @@ describe(`MessageInputFlat`, () => {
794794
const customMessageData = { customX: 'customX' };
795795
const CustomStateSetter = null;
796796

797-
customChannel.messageComposer.customDataManager.setData(customMessageData);
797+
customChannel.messageComposer.customDataManager.setMessageData(customMessageData);
798798
const { container, submit } = await renderComponent({
799799
customChannel,
800800
customClient,
@@ -823,7 +823,7 @@ describe(`MessageInputFlat`, () => {
823823
const overrideMock = jest.fn().mockImplementation(() => Promise.resolve());
824824
const { customChannel, customClient } = await setup();
825825
const customMessageData = { customX: 'customX' };
826-
customChannel.messageComposer.customDataManager.setData(customMessageData);
826+
customChannel.messageComposer.customDataManager.setMessageData(customMessageData);
827827
const { container, submit } = await renderComponent({
828828
customChannel,
829829
customClient,

src/components/TextareaComposer/SuggestionList/CommandItem.tsx

+2-8
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
11
import type { PropsWithChildren } from 'react';
22
import React from 'react';
3+
import type { CommandResponse } from 'stream-chat';
34

45
export type CommandItemProps = {
5-
entity: {
6-
/** Arguments of command */
7-
args?: string;
8-
/** Description of command */
9-
description?: string;
10-
/** Name of the command */
11-
name?: string;
12-
};
6+
entity: CommandResponse;
137
};
148

159
export const CommandItem = (props: PropsWithChildren<CommandItemProps>) => {

src/components/TextareaComposer/SuggestionList/SuggestionList.tsx

+28-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import clsx from 'clsx';
22
import React, { useEffect, useState } from 'react';
3+
import type { CommandItemProps } from './CommandItem';
34
import { CommandItem } from './CommandItem';
5+
import type { EmoticonItemProps } from './EmoticonItem';
46
import { EmoticonItem } from './EmoticonItem';
7+
import type { SuggestionListItemComponentProps } from './SuggestionListItem';
58
import { SuggestionListItem as DefaultSuggestionListItem } from './SuggestionListItem';
69
import { UserItem } from './UserItem';
710
import { useComponentContext } from '../../../context/ComponentContext';
@@ -13,10 +16,15 @@ import type {
1316
TextComposerState,
1417
TextComposerSuggestion,
1518
} from 'stream-chat';
16-
import type { SuggestionItemProps } from './SuggestionListItem';
19+
import type { UserItemProps } from './UserItem';
20+
21+
type SuggestionTrigger = '/' | ':' | '@' | string;
1722

1823
export type SuggestionListProps = Partial<{
19-
SuggestionItem: React.ComponentType<SuggestionItemProps>;
24+
suggestionItemComponents: Record<
25+
SuggestionTrigger,
26+
React.ComponentType<SuggestionListItemComponentProps>
27+
>;
2028
className?: string;
2129
closeOnClickOutside?: boolean;
2230
containerClassName?: string;
@@ -34,18 +42,28 @@ const searchSourceStateSelector = (
3442
items: nextValue.items ?? [],
3543
});
3644

37-
export const defaultComponents = {
38-
'/': CommandItem,
39-
':': EmoticonItem,
40-
'@': UserItem,
41-
};
45+
export const defaultComponents: Record<
46+
SuggestionTrigger,
47+
React.ComponentType<SuggestionListItemComponentProps>
48+
> = {
49+
'/': (props: SuggestionListItemComponentProps) => (
50+
<CommandItem entity={props.entity as CommandItemProps['entity']} />
51+
),
52+
':': (props: SuggestionListItemComponentProps) => (
53+
<EmoticonItem entity={props.entity as EmoticonItemProps['entity']} />
54+
),
55+
'@': (props: SuggestionListItemComponentProps) => (
56+
<UserItem entity={props.entity as UserItemProps['entity']} />
57+
),
58+
} as const;
4259

4360
export const SuggestionList = ({
4461
className,
4562
closeOnClickOutside = true,
4663
containerClassName,
4764
focusedItemIndex,
4865
setFocusedItemIndex,
66+
suggestionItemComponents = defaultComponents,
4967
}: SuggestionListProps) => {
5068
const { AutocompleteSuggestionItem = DefaultSuggestionListItem } =
5169
useComponentContext();
@@ -56,8 +74,9 @@ export const SuggestionList = ({
5674
useStateStore(suggestions?.searchSource.state, searchSourceStateSelector) ?? {};
5775
const [container, setContainer] = useState<HTMLDivElement | null>(null);
5876

59-
// @ts-expect-error component type mismatch
60-
const component = suggestions?.trigger && defaultComponents[suggestions?.trigger];
77+
const component = suggestions?.trigger
78+
? suggestionItemComponents[suggestions?.trigger]
79+
: undefined;
6180

6281
useEffect(() => {
6382
if (!closeOnClickOutside || !suggestions || !container) return;

src/components/TextareaComposer/SuggestionList/SuggestionListItem.tsx

+15-12
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
import clsx from 'clsx';
22
import type { Ref } from 'react';
3-
import { useLayoutEffect } from 'react';
4-
import React, { useCallback, useRef } from 'react';
3+
import React, { useCallback, useLayoutEffect, useRef } from 'react';
54
import { useMessageComposer } from '../../MessageInput';
6-
import type { CommandResponse, TextComposerSuggestion, UserResponse } from 'stream-chat';
7-
import type { EmojiSearchIndexResult } from '../../MessageInput';
5+
import type { TextComposerSuggestion } from 'stream-chat';
6+
import type { UserItemProps } from './UserItem';
7+
import type { CommandItemProps } from './CommandItem';
8+
import type { EmoticonItemProps } from './EmoticonItem';
89

9-
export type SuggestionCommand = CommandResponse;
10-
export type SuggestionUser = UserResponse;
11-
export type SuggestionEmoji = EmojiSearchIndexResult;
12-
export type SuggestionItem = SuggestionUser | SuggestionCommand | SuggestionEmoji;
10+
export type DefaultSuggestionListItemEntity =
11+
| UserItemProps['entity']
12+
| CommandItemProps['entity']
13+
| EmoticonItemProps['entity'];
14+
15+
export type SuggestionListItemComponentProps = {
16+
entity: DefaultSuggestionListItemEntity | unknown;
17+
focused: boolean;
18+
};
1319

1420
export type SuggestionItemProps = {
15-
component: React.ComponentType<{
16-
entity: SuggestionItem;
17-
focused: boolean;
18-
}>;
21+
component: React.ComponentType<SuggestionListItemComponentProps>;
1922
item: TextComposerSuggestion;
2023
focused: boolean;
2124
className?: string;

yarn.lock

+4-4
Original file line numberDiff line numberDiff line change
@@ -12155,10 +12155,10 @@ statuses@2.0.1:
1215512155
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
1215612156
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
1215712157

12158-
stream-chat@^9.0.0-rc.13:
12159-
version "9.0.0-rc.13"
12160-
resolved "https://registry.yarnpkg.com/stream-chat/-/stream-chat-9.0.0-rc.13.tgz#90396d724bd5f81e2d8eb051c9c56b075892305f"
12161-
integrity sha512-qJPcHsQUfu0qMb7p7Bv9OnL6qID/qxYLKOIsG6KbUNEQHwkKSw/aDwExpvgJR7dXsUOLyLZFhVtYnFkAPDdJ3A==
12158+
stream-chat@^9.0.0-rc.15:
12159+
version "9.0.0-rc.15"
12160+
resolved "https://registry.yarnpkg.com/stream-chat/-/stream-chat-9.0.0-rc.15.tgz#6a35f27c38be2e021a3e62abcffa40aaef84fc0a"
12161+
integrity sha512-n8IftP103jp8IVOUZA4mnOvEIbyzGTVjoL3rDm42apk8DRHz2/vdvgyTYUJysDNYh+Oxu5HwWObToKv1c/Bxdw==
1216212162
dependencies:
1216312163
"@types/jsonwebtoken" "^9.0.8"
1216412164
"@types/ws" "^8.5.14"

0 commit comments

Comments
 (0)