From 66335a170466d21522f2ac508adfad471902aef3 Mon Sep 17 00:00:00 2001 From: Anton Arnautov Date: Fri, 20 Mar 2026 17:47:15 +0100 Subject: [PATCH 1/5] File changes --- src/components/ChannelList/ChannelList.tsx | 41 +++++------------ .../ChannelList/ChannelListMessenger.tsx | 45 +++++-------------- .../ChannelList/__tests__/ChannelList.test.js | 4 +- .../__tests__/ChannelListMessenger.test.js | 6 +-- .../ChannelList/styling/ChannelList.scss | 4 +- .../ChannelListItem/ChannelListItem.tsx | 3 -- .../ChannelListItem/ChannelListItemUI.tsx | 11 +++-- src/context/ComponentContext.tsx | 5 ++- 8 files changed, 38 insertions(+), 81 deletions(-) diff --git a/src/components/ChannelList/ChannelList.tsx b/src/components/ChannelList/ChannelList.tsx index f9e43757df..7d6e179e9c 100644 --- a/src/components/ChannelList/ChannelList.tsx +++ b/src/components/ChannelList/ChannelList.tsx @@ -19,16 +19,11 @@ import { usePrepareShapeHandlers, } from './hooks/useChannelListShape'; import { useStateStore } from '../../store'; -import type { ChannelListMessengerProps } from './ChannelListMessenger'; -import { ChannelListMessenger } from './ChannelListMessenger'; -import type { ChannelAvatarProps } from '../Avatar'; -import { Avatar as DefaultAvatar } from '../Avatar'; -import type { ChannelListItemUIProps } from '../ChannelListItem/ChannelListItem'; +import { ChannelListUI as DefaultChannelListUI } from './ChannelListMessenger'; import { ChannelListItem } from '../ChannelListItem/ChannelListItem'; import { Search as DefaultSearch } from '../Search'; import type { EmptyStateIndicatorProps } from '../EmptyStateIndicator'; import { EmptyStateIndicator as DefaultEmptyStateIndicator } from '../EmptyStateIndicator'; -import { LoadingChannels } from '../Loading/LoadingChannels'; import type { LoadMorePaginatorProps } from '../LoadMore/LoadMorePaginator'; import { LoadMorePaginator } from '../LoadMore/LoadMorePaginator'; import { NotificationList as DefaultNotificationList } from '../Notifications'; @@ -39,7 +34,6 @@ import { useChatContext, useComponentContext, } from '../../context'; -import { NullComponent } from '../UtilityComponents'; import { moveChannelUpwards } from './utils'; import type { TranslationContextValue } from '../../context/TranslationContext'; import type { PaginatorProps } from '../../types/types'; @@ -63,8 +57,6 @@ export type ChannelListProps = { * to false, which will prevent channels not in the list from incrementing the list. The default is true. */ allowNewMessagesFromUnfilteredChannels?: boolean; - /** UI component to display an avatar, defaults to [Avatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/Avatar.tsx) component and accepts the same props as: [ChannelAvatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/ChannelAvatar.tsx) */ - Avatar?: React.ComponentType; /** Optional function to filter channels prior to loading in the DOM. Do not use any complex or async logic that would delay the loading of the ChannelList. We recommend using a pure function with array methods like filter/sort/reduce. */ channelRenderFilterFn?: (channels: Array) => Array; // FIXME: how is this even legal (WHY IS IT STRING?!) @@ -83,12 +75,6 @@ export type ChannelListProps = { userLanguage: TranslationContextValue['userLanguage'], isMessageAIGenerated?: ChatContextValue['isMessageAIGenerated'], ) => ReactNode; - /** Custom UI component to display the container for the queried channels, defaults to and accepts same props as: [ChannelListMessenger](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelList/ChannelListMessenger.tsx) */ - List?: React.ComponentType; - /** Custom UI component to display the loading error indicator, defaults to component that renders null */ - LoadingErrorIndicator?: React.ComponentType; - /** Custom UI component to display the loading state, defaults to and accepts same props as: [LoadingChannels](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Loading/LoadingChannels.tsx) */ - LoadingIndicator?: React.ComponentType; /** When true, channels won't dynamically sort by most recent message */ lockChannelOrder?: boolean; /** Function to override the default behavior when a user is added to a channel, corresponds to [notification.added\_to\_channel](https://getstream.io/chat/docs/javascript/event_object/?language=javascript) event */ @@ -140,8 +126,6 @@ export type ChannelListProps = { options?: ChannelOptions; /** Custom UI component to handle channel pagination logic, defaults to and accepts same props as: [LoadMorePaginator](https://github.com/GetStream/stream-chat-react/blob/master/src/components/LoadMore/LoadMorePaginator.tsx) */ Paginator?: React.ComponentType; - /** Custom UI component to display the channel preview in the list, defaults to and accepts same props as: [ChannelPreviewMessenger](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelPreview/ChannelPreviewMessenger.tsx) */ - Preview?: React.ComponentType; /** * Custom interval during which the recovery channel list queries will be prevented. * This is to avoid firing unnecessary queries during internet connection fluctuation. @@ -169,16 +153,12 @@ export type ChannelListProps = { const UnMemoizedChannelList = (props: ChannelListProps) => { const { allowNewMessagesFromUnfilteredChannels = true, - Avatar = DefaultAvatar, channelRenderFilterFn, customActiveChannel, customQueryChannels, EmptyStateIndicator = DefaultEmptyStateIndicator, filters = {}, getLatestMessagePreview, - List = ChannelListMessenger, - LoadingErrorIndicator = NullComponent, - LoadingIndicator = LoadingChannels, lockChannelOrder = false, onAddedToChannel, onChannelDeleted, @@ -191,7 +171,6 @@ const UnMemoizedChannelList = (props: ChannelListProps) => { onRemovedFromChannel, options, Paginator = LoadMorePaginator, - Preview, recoveryThrottleIntervalMs, renderChannels, sendChannelsToList = false, @@ -215,8 +194,12 @@ const UnMemoizedChannelList = (props: ChannelListProps) => { theme, useImageFlagEmojisOnWindows, } = useChatContext('ChannelList'); - const { NotificationList = DefaultNotificationList, Search = DefaultSearch } = - useComponentContext(); // FIXME: use component context to retrieve ChannelListItemUI components too + const { + ChannelListUI = DefaultChannelListUI, + NotificationList = DefaultNotificationList, + Search = DefaultSearch, + } = useComponentContext(); + const channelListRef = useRef(null); const [channelUpdateCount, setChannelUpdateCount] = useState(0); @@ -334,12 +317,10 @@ const UnMemoizedChannelList = (props: ChannelListProps) => { const renderChannel = (item: Channel) => { const previewProps = { activeChannel: channel, - Avatar, channel: item, // forces the update of preview component on channel update channelUpdateCount, getLatestMessagePreview, - Preview, setActiveChannel, watchers, }; @@ -351,7 +332,7 @@ const UnMemoizedChannelList = (props: ChannelListProps) => { const className = clsx( customClasses?.chat ?? 'str-chat', theme, - customClasses?.channelList ?? `${baseClass} ${baseClass}-react`, + customClasses?.channelList ?? `${baseClass}`, { 'str-chat--windows-flags': useImageFlagEmojisOnWindows && navigator.userAgent.match(/Win/), @@ -369,15 +350,13 @@ const UnMemoizedChannelList = (props: ChannelListProps) => { {showChannelSearch && } {showChannelList && ( - {!loadedChannels?.length ? ( @@ -393,7 +372,7 @@ const UnMemoizedChannelList = (props: ChannelListProps) => { : loadedChannels.map((channel) => renderChannel(channel))} )} - + )} diff --git a/src/components/ChannelList/ChannelListMessenger.tsx b/src/components/ChannelList/ChannelListMessenger.tsx index 7b59daf874..cfc630da34 100644 --- a/src/components/ChannelList/ChannelListMessenger.tsx +++ b/src/components/ChannelList/ChannelListMessenger.tsx @@ -4,20 +4,15 @@ import type { APIErrorResponse, Channel, ErrorFromResponse } from 'stream-chat'; import { LoadingChannels } from '../Loading/LoadingChannels'; import { NullComponent } from '../UtilityComponents'; -import { useTranslationContext } from '../../context'; -import type { LoadingErrorIndicatorProps } from '../Loading'; +import { useComponentContext, useTranslationContext } from '../../context'; -export type ChannelListMessengerProps = { +export type ChannelListUIProps = { /** Whether the channel query request returned an errored response */ error: ErrorFromResponse | null; /** The channels currently loaded in the list, only defined if `sendChannelsToList` on `ChannelList` is true */ loadedChannels?: Channel[]; /** Whether the channels are currently loading */ loading?: boolean; - /** Custom UI component to display the loading error indicator, defaults to component that renders null */ - LoadingErrorIndicator?: React.ComponentType; - /** Custom UI component to display a loading indicator, defaults to and accepts same props as: [LoadingChannels](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Loading/LoadingChannels.tsx) */ - LoadingIndicator?: React.ComponentType; /** Local state hook that resets the currently loaded channels */ setChannels?: React.Dispatch>; }; @@ -25,44 +20,24 @@ export type ChannelListMessengerProps = { /** * A preview list of channels, allowing you to select the channel you want to open */ -export const ChannelListMessenger = ( - props: PropsWithChildren, -) => { - const { - children, - error = null, - loading, - LoadingErrorIndicator = NullComponent, - LoadingIndicator = LoadingChannels, - } = props; - const { t } = useTranslationContext('ChannelListMessenger'); +export const ChannelListUI = (props: PropsWithChildren) => { + const { children, error = null, loading = false } = props; + const { LoadingErrorIndicator = NullComponent, LoadingIndicator = LoadingChannels } = + useComponentContext('ChannelListUI'); + const { t } = useTranslationContext('ChannelListUI'); if (error) { return ; } - if (loading) { - return ( -
-
- -
-
- ); - } - return ( -
+
- {children} + {loading ? : children}
); diff --git a/src/components/ChannelList/__tests__/ChannelList.test.js b/src/components/ChannelList/__tests__/ChannelList.test.js index 84ddbaf9d9..d18d31f36c 100644 --- a/src/components/ChannelList/__tests__/ChannelList.test.js +++ b/src/components/ChannelList/__tests__/ChannelList.test.js @@ -46,7 +46,7 @@ import { useChatContext, WithComponents, } from '../../../context'; -import { ChannelListMessenger } from '../ChannelListMessenger'; +import { ChannelListUI } from '../ChannelListMessenger'; expect.extend(toHaveNoViolations); @@ -391,7 +391,7 @@ describe('ChannelList', () => { const ChannelListMessengerPropsInterceptor = (props) => { channelListMessengerLoadingHistory.push(props.loading); - return ; + return ; }; await render( diff --git a/src/components/ChannelList/__tests__/ChannelListMessenger.test.js b/src/components/ChannelList/__tests__/ChannelListMessenger.test.js index 7a947b3f02..ca84e2e6cd 100644 --- a/src/components/ChannelList/__tests__/ChannelListMessenger.test.js +++ b/src/components/ChannelList/__tests__/ChannelListMessenger.test.js @@ -2,7 +2,7 @@ import React from 'react'; import { cleanup, render } from '@testing-library/react'; import '@testing-library/jest-dom'; -import { ChannelListMessenger } from '../ChannelListMessenger'; +import { ChannelListUI } from '../ChannelListMessenger'; import { TranslationProvider } from '../../../context'; import { mockTranslationContext } from '../../../mock-builders'; @@ -12,7 +12,7 @@ console.warn = () => null; const Component = ({ error = false, loading = false }) => ( -
Loading Error Indicator
} @@ -20,7 +20,7 @@ const Component = ({ error = false, loading = false }) => ( >
children 1
children 2
-
+
); diff --git a/src/components/ChannelList/styling/ChannelList.scss b/src/components/ChannelList/styling/ChannelList.scss index 72751173cc..655e19c766 100644 --- a/src/components/ChannelList/styling/ChannelList.scss +++ b/src/components/ChannelList/styling/ChannelList.scss @@ -123,12 +123,12 @@ @include utils.scrollable-y; @include utils.component-layer-overrides('channel-list'); - .str-chat__channel-list-messenger { + .str-chat__channel-list-inner { height: 100%; overflow: hidden; padding-bottom: var(--str-chat__spacing-2_5); - .str-chat__channel-list-messenger__main { + .str-chat__channel-list-inner__main { height: 100%; overflow-y: auto; diff --git a/src/components/ChannelListItem/ChannelListItem.tsx b/src/components/ChannelListItem/ChannelListItem.tsx index 7df5158fca..9ebf33bd66 100644 --- a/src/components/ChannelListItem/ChannelListItem.tsx +++ b/src/components/ChannelListItem/ChannelListItem.tsx @@ -10,7 +10,6 @@ import { getLatestMessagePreview as defaultGetLatestMessagePreview } from './uti import { useTranslationContext } from '../../context/TranslationContext'; import { useMessageDeliveryStatus } from './hooks/useMessageDeliveryStatus'; import type { MessageDeliveryStatus } from './hooks/useMessageDeliveryStatus'; -import type { ChannelAvatarProps } from '../Avatar/ChannelAvatar'; import type { GroupChannelDisplayInfo } from './utils'; import { type ChatContextValue, @@ -47,8 +46,6 @@ export type ChannelListItemProps = { active?: boolean; /** Current selected channel object */ activeChannel?: Channel; - /** UI component to display an avatar, defaults to [Avatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/Avatar.tsx) component and accepts the same props as: [ChannelAvatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/ChannelAvatar.tsx) */ - Avatar?: React.ComponentType; /** Forces the update of preview component on channel update */ channelUpdateCount?: number; /** Custom class for the channel preview root */ diff --git a/src/components/ChannelListItem/ChannelListItemUI.tsx b/src/components/ChannelListItem/ChannelListItemUI.tsx index 746839c76a..98ca5b805b 100644 --- a/src/components/ChannelListItem/ChannelListItemUI.tsx +++ b/src/components/ChannelListItem/ChannelListItemUI.tsx @@ -14,7 +14,6 @@ import { SummarizedMessagePreview } from '../SummarizedMessagePreview'; const UnMemoizedChannelListItemUI = (props: ChannelListItemUIProps) => { const { active, - Avatar = DefaultChannelAvatar, channel, className: customClassName = '', displayImage, @@ -29,8 +28,10 @@ const UnMemoizedChannelListItemUI = (props: ChannelListItemUIProps) => { watchers, } = props; - const { ChannelListItemActionButtons = DefaultChannelListItemActionButtons } = - useComponentContext(); + const { + Avatar = DefaultChannelAvatar, + ChannelListItemActionButtons = DefaultChannelListItemActionButtons, + } = useComponentContext(); const channelPreviewButton = useRef(null); @@ -56,7 +57,9 @@ const UnMemoizedChannelListItemUI = (props: ChannelListItemUIProps) => { aria-pressed={active} className={clsx( 'str-chat__channel-list-item', - typeof unread === 'number' && unread > 0 && 'str-chat__channel-list-item--unread', + typeof unread === 'number' && + unread > 0 && + 'str-chat__channel-list-item--unread', muted && 'str-chat__channel-list-item--muted', customClassName, )} diff --git a/src/context/ComponentContext.tsx b/src/context/ComponentContext.tsx index 3d045c228b..da7328cf23 100644 --- a/src/context/ComponentContext.tsx +++ b/src/context/ComponentContext.tsx @@ -9,6 +9,7 @@ import { type BaseImageProps, type CalloutDialogProps, type ChannelListItemUIProps, + type ChannelListUIProps, type DateSeparatorProps, type EmojiSearchIndex, type EmptyStateIndicatorProps, @@ -102,7 +103,9 @@ export type ComponentContextValue = { CalloutDialog?: React.ComponentType; /** Custom UI component shown instead of the image when it fails to load, defaults to and accepts same props as: [ImagePlaceholder](https://github.com/GetStream/stream-chat-react/blob/master/src/components/BaseImage/ImagePlaceholder.tsx) */ ImagePlaceholder?: React.ComponentType; - /** Custom UI component to display set of action buttons within `ChannelPreviewMessenger` component, accepts same props as: [ChannelListItemActionButtons](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelList/ChannelListItemActionButtons.tsx) */ + /** Custom UI component to display the container for the queried channels, defaults to and accepts same props as: [ChannelListUI](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelList/ChannelListUI.tsx) */ + ChannelListUI?: React.ComponentType; + /** Custom UI component to display set of action buttons within `ChannelListItemUI` component, accepts same props as: [ChannelListItemActionButtons](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelList/ChannelListItemActionButtons.tsx) */ ChannelListItemActionButtons?: React.ComponentType; /** Custom UI component to display the channel preview in the list, defaults to and accepts same props as: [ChannelListItemUI](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelPreview/ChannelListItemUI.tsx) */ ChannelListItemUI?: React.ComponentType; From b17f9184328376a88b9801a46446129eb1ef1b34 Mon Sep 17 00:00:00 2001 From: Anton Arnautov Date: Fri, 20 Mar 2026 17:48:59 +0100 Subject: [PATCH 2/5] File renames --- src/components/ChannelList/ChannelList.tsx | 2 +- .../ChannelList/{ChannelListMessenger.tsx => ChannelListUI.tsx} | 0 src/components/ChannelList/__tests__/ChannelList.test.js | 2 +- .../{ChannelListMessenger.test.js => ChannelListUI.test.js} | 2 +- src/components/ChannelList/index.ts | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename src/components/ChannelList/{ChannelListMessenger.tsx => ChannelListUI.tsx} (100%) rename src/components/ChannelList/__tests__/{ChannelListMessenger.test.js => ChannelListUI.test.js} (96%) diff --git a/src/components/ChannelList/ChannelList.tsx b/src/components/ChannelList/ChannelList.tsx index 7d6e179e9c..7f7092431b 100644 --- a/src/components/ChannelList/ChannelList.tsx +++ b/src/components/ChannelList/ChannelList.tsx @@ -19,7 +19,7 @@ import { usePrepareShapeHandlers, } from './hooks/useChannelListShape'; import { useStateStore } from '../../store'; -import { ChannelListUI as DefaultChannelListUI } from './ChannelListMessenger'; +import { ChannelListUI as DefaultChannelListUI } from './ChannelListUI'; import { ChannelListItem } from '../ChannelListItem/ChannelListItem'; import { Search as DefaultSearch } from '../Search'; import type { EmptyStateIndicatorProps } from '../EmptyStateIndicator'; diff --git a/src/components/ChannelList/ChannelListMessenger.tsx b/src/components/ChannelList/ChannelListUI.tsx similarity index 100% rename from src/components/ChannelList/ChannelListMessenger.tsx rename to src/components/ChannelList/ChannelListUI.tsx diff --git a/src/components/ChannelList/__tests__/ChannelList.test.js b/src/components/ChannelList/__tests__/ChannelList.test.js index d18d31f36c..9c74a6cd65 100644 --- a/src/components/ChannelList/__tests__/ChannelList.test.js +++ b/src/components/ChannelList/__tests__/ChannelList.test.js @@ -46,7 +46,7 @@ import { useChatContext, WithComponents, } from '../../../context'; -import { ChannelListUI } from '../ChannelListMessenger'; +import { ChannelListUI } from '../ChannelListUI'; expect.extend(toHaveNoViolations); diff --git a/src/components/ChannelList/__tests__/ChannelListMessenger.test.js b/src/components/ChannelList/__tests__/ChannelListUI.test.js similarity index 96% rename from src/components/ChannelList/__tests__/ChannelListMessenger.test.js rename to src/components/ChannelList/__tests__/ChannelListUI.test.js index ca84e2e6cd..e8a5891fd8 100644 --- a/src/components/ChannelList/__tests__/ChannelListMessenger.test.js +++ b/src/components/ChannelList/__tests__/ChannelListUI.test.js @@ -2,7 +2,7 @@ import React from 'react'; import { cleanup, render } from '@testing-library/react'; import '@testing-library/jest-dom'; -import { ChannelListUI } from '../ChannelListMessenger'; +import { ChannelListUI } from '../ChannelListUI'; import { TranslationProvider } from '../../../context'; import { mockTranslationContext } from '../../../mock-builders'; diff --git a/src/components/ChannelList/index.ts b/src/components/ChannelList/index.ts index 6407096fa7..2f9989bb1e 100644 --- a/src/components/ChannelList/index.ts +++ b/src/components/ChannelList/index.ts @@ -1,4 +1,4 @@ export * from './ChannelList'; -export * from './ChannelListMessenger'; +export * from './ChannelListUI'; export * from './hooks'; export * from './utils'; From 8b32d6941fd592185574114f25837bc2a89a82ee Mon Sep 17 00:00:00 2001 From: Anton Arnautov Date: Fri, 20 Mar 2026 17:51:58 +0100 Subject: [PATCH 3/5] Adjust outdated Message.scss rule --- src/components/Message/styling/Message.scss | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/Message/styling/Message.scss b/src/components/Message/styling/Message.scss index 0db178ae91..6bc17cd447 100644 --- a/src/components/Message/styling/Message.scss +++ b/src/components/Message/styling/Message.scss @@ -625,10 +625,8 @@ .str-chat__message-with-touch-support ) { /* This rule won't be applied in browsers that don't support :has() */ - .str-chat__li:hover:not(:has(.str-chat__reaction-list:hover, .str-chat__modal--open)), - .str-chat__li:focus-within:not( - :has(.str-chat__reaction-list:focus-within, .str-chat__modal--open) - ), + .str-chat__li:hover:not(:has(.str-chat__modal--open)), + .str-chat__li:focus-within:not(:has(.str-chat__modal--open)), .str-chat__li:has(.str-chat__message-options--active) { .str-chat__message-options { display: flex; From e4be00709631259e613e123835bcea6a05abfd49 Mon Sep 17 00:00:00 2001 From: Anton Arnautov Date: Fri, 20 Mar 2026 18:01:53 +0100 Subject: [PATCH 4/5] Lint fixes --- src/components/ChannelList/ChannelList.tsx | 1 - .../ChannelListItem/ChannelListItemTimestamp.tsx | 5 ++++- .../MessageActions/MessageActions.defaults.tsx | 10 ++-------- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/components/ChannelList/ChannelList.tsx b/src/components/ChannelList/ChannelList.tsx index 7f7092431b..8f2ce3c7b3 100644 --- a/src/components/ChannelList/ChannelList.tsx +++ b/src/components/ChannelList/ChannelList.tsx @@ -37,7 +37,6 @@ import { import { moveChannelUpwards } from './utils'; import type { TranslationContextValue } from '../../context/TranslationContext'; import type { PaginatorProps } from '../../types/types'; -import type { LoadingErrorIndicatorProps } from '../Loading'; import { ChannelListHeader } from './ChannelListHeader'; import { useStableId } from '../UtilityComponents/useStableId'; diff --git a/src/components/ChannelListItem/ChannelListItemTimestamp.tsx b/src/components/ChannelListItem/ChannelListItemTimestamp.tsx index a0aba65f5b..1de5921482 100644 --- a/src/components/ChannelListItem/ChannelListItemTimestamp.tsx +++ b/src/components/ChannelListItem/ChannelListItemTimestamp.tsx @@ -30,7 +30,10 @@ export function ChannelListItemTimestamp({ lastMessage }: ChannelListItemTimesta if (!when) return null; return ( -