From d20761586d502dc2a93acbaae2b174688add28a2 Mon Sep 17 00:00:00 2001
From: srijna
Date: Mon, 8 Jun 2026 15:46:28 +0530
Subject: [PATCH 1/9] feat: migrate MessageSearch to PaginatedVirtualList
---
.../MessageSearchTab/MessageSearchTab.tsx | 80 ++-----------
.../components/MessageSearch.spec.tsx | 110 ++++++++++++++++++
.../components/MessageSearch.tsx | 93 +++++++++++++++
.../hooks/useMessageSearchQuery.ts | 41 ++++---
4 files changed, 235 insertions(+), 89 deletions(-)
create mode 100644 apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx
create mode 100644 apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx
index c804515c61ac7..dabab41133660 100644
--- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx
@@ -1,5 +1,4 @@
-import { Callout, Box, MessageDivider, Throbber } from '@rocket.chat/fuselage';
-import { MessageTypes } from '@rocket.chat/message-types';
+import { Callout, Box, Throbber } from '@rocket.chat/fuselage';
import {
ContextualbarClose,
ContextualbarContent,
@@ -8,43 +7,28 @@ import {
ContextualbarIcon,
ContextualbarSection,
ContextualbarDialog,
- VirtualizedScrollbars,
- ContextualbarEmptyContent,
} from '@rocket.chat/ui-client';
-import { useRoomToolbox, useUserPreference, useSetting } from '@rocket.chat/ui-contexts';
-import { useState, memo, Fragment, useId } from 'react';
+import { useRoomToolbox } from '@rocket.chat/ui-contexts';
+import { memo, useId, useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { Virtuoso } from 'react-virtuoso';
+import MessageSearch from './components/MessageSearch';
import MessageSearchForm from './components/MessageSearchForm';
import { useMessageSearchProviderQuery } from './hooks/useMessageSearchProviderQuery';
import { useMessageSearchQuery } from './hooks/useMessageSearchQuery';
import ResultsLiveRegion from '../../../../components/ResultsLiveRegion';
-import RoomMessage from '../../../../components/message/variants/RoomMessage';
-import SystemMessage from '../../../../components/message/variants/SystemMessage';
-import { useFormatDate } from '../../../../hooks/useFormatDate';
-import MessageListErrorBoundary from '../../MessageList/MessageListErrorBoundary';
-import { isMessageNewDay } from '../../MessageList/lib/isMessageNewDay';
-import MessageListProvider from '../../MessageList/providers/MessageListProvider';
-import { useRoomSubscription } from '../../contexts/RoomContext';
// TODO: Refactor this component to isolate the data from the visual
const MessageSearchTab = () => {
const { t } = useTranslation();
const searchListId = useId();
- const formatDate = useFormatDate();
const { closeTab } = useRoomToolbox();
- const pageSize = useSetting('PageSize', 10);
-
- const [limit, setLimit] = useState(pageSize);
- const subscription = useRoomSubscription();
- const showUserAvatar = !!useUserPreference('displayAvatars');
const providerQuery = useMessageSearchProviderQuery();
const [{ searchText, globalSearch }, handleSearch] = useState({ searchText: '', globalSearch: false });
- const { isSuccess, data: messageSearchData, isPending } = useMessageSearchQuery({ searchText, limit, globalSearch });
- const itemCount = messageSearchData?.length ?? 0;
+ const { isSuccess, data, isPending } = useMessageSearchQuery({ searchText, globalSearch });
+ const itemCount = data?.itemCount ?? 0;
return (
@@ -65,57 +49,7 @@ const MessageSearchTab = () => {
{searchText && isPending && }
{isSuccess && (
- {messageSearchData.length === 0 && }
- {messageSearchData.length > 0 && (
-
-
-
-
- {
- const previous = messageSearchData[index - 1];
-
- const newDay = isMessageNewDay(message, previous);
-
- const system = MessageTypes.isSystemMessage(message);
-
- const unread = subscription?.tunread?.includes(message._id) ?? false;
- const mention = subscription?.tunreadUser?.includes(message._id) ?? false;
- const all = subscription?.tunreadGroup?.includes(message._id) ?? false;
-
- return (
-
- {newDay && {formatDate(message.ts)}}
-
- {system ? (
-
- ) : (
-
- )}
-
- );
- }}
- endReached={() => {
- setLimit((limit) => limit + pageSize);
- }}
- />
-
-
-
-
- )}
+
)}
>
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx
new file mode 100644
index 0000000000000..f56123f65543a
--- /dev/null
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx
@@ -0,0 +1,110 @@
+import type { IMessage } from '@rocket.chat/core-typings';
+import { render, screen } from '@testing-library/react';
+
+import MessageSearch from './MessageSearch';
+
+const useMessageSearchQueryMock = jest.fn();
+
+jest.mock('../hooks/useMessageSearchQuery', () => ({
+ useMessageSearchQuery: (...args: unknown[]) => useMessageSearchQueryMock(...args),
+}));
+
+jest.mock('../../../../../components/PaginatedVirtualList', () => ({
+ PaginatedVirtualList: ({ items, renderItem }: { items: IMessage[]; renderItem: (item: IMessage, index: number) => React.ReactNode }) => (
+
+ {items.map((item, index) => (
+
{renderItem(item, index)}
+ ))}
+
+ ),
+}));
+
+jest.mock('../../../../../components/message/variants/RoomMessage', () => ({ message }: { message: IMessage }) => (
+ {message.msg}
+));
+
+jest.mock('../../../../../components/message/variants/SystemMessage', () => ({ message }: { message: IMessage }) => (
+ {message.msg}
+));
+
+jest.mock('../../../../../hooks/useFormatDate', () => ({
+ useFormatDate: () => () => 'formatted-date',
+}));
+
+jest.mock('../../../MessageList/MessageListErrorBoundary', () => ({
+ __esModule: true,
+ default: ({ children }: { children: React.ReactNode }) => <>{children}>,
+}));
+
+jest.mock('../../../MessageList/providers/MessageListProvider', () => ({
+ __esModule: true,
+ default: ({ children }: { children: React.ReactNode }) => <>{children}>,
+}));
+
+jest.mock('../../../contexts/RoomContext', () => ({
+ useRoomSubscription: () => ({
+ tunread: ['message-1'],
+ tunreadUser: ['message-1'],
+ tunreadGroup: ['message-1'],
+ }),
+}));
+
+jest.mock('@rocket.chat/ui-contexts', () => ({
+ ...jest.requireActual('@rocket.chat/ui-contexts'),
+ useTranslation: () => (key: string) => key,
+ useUserPreference: () => true,
+}));
+
+const createMessage = (id: string, overrides: Partial = {}): IMessage =>
+ ({
+ _id: id,
+ rid: 'room-id',
+ msg: `Message ${id}`,
+ ts: new Date('2026-03-22T10:00:00.000Z'),
+ u: { _id: 'user-id', username: 'testuser', name: 'Test User' },
+ _updatedAt: new Date('2026-03-22T10:00:00.000Z'),
+ ...overrides,
+ }) as IMessage;
+
+describe('MessageSearch', () => {
+ beforeEach(() => {
+ useMessageSearchQueryMock.mockReturnValue({
+ isPending: false,
+ isSuccess: true,
+ data: { items: [], itemCount: 0 },
+ fetchNextPage: jest.fn(),
+ });
+ });
+
+ afterEach(() => {
+ useMessageSearchQueryMock.mockReset();
+ });
+
+ it('renders the empty state when no messages are returned', () => {
+ render();
+
+ expect(screen.getByText('No_results_found')).toBeInTheDocument();
+ });
+
+ it('renders room and system messages with date dividers', () => {
+ const roomMessage = createMessage('message-1');
+ const systemMessage = createMessage('message-2', {
+ ts: new Date('2026-03-23T10:00:00.000Z'),
+ t: 'au',
+ msg: 'System event',
+ });
+
+ useMessageSearchQueryMock.mockReturnValue({
+ isPending: false,
+ isSuccess: true,
+ data: { items: [roomMessage, systemMessage], itemCount: 2 },
+ fetchNextPage: jest.fn(),
+ });
+
+ render();
+
+ expect(screen.getByTestId('room-message')).toHaveTextContent('Message message-1');
+ expect(screen.getByTestId('system-message')).toHaveTextContent('System event');
+ expect(screen.getAllByText('formatted-date')).toHaveLength(2);
+ });
+});
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx
new file mode 100644
index 0000000000000..5fa0947e4ab81
--- /dev/null
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx
@@ -0,0 +1,93 @@
+import { Box, MessageDivider } from '@rocket.chat/fuselage';
+import { MessageTypes } from '@rocket.chat/message-types';
+import { ContextualbarEmptyContent } from '@rocket.chat/ui-client';
+import { useTranslation, useUserPreference } from '@rocket.chat/ui-contexts';
+import type { ReactElement } from 'react';
+import { Fragment, memo } from 'react';
+
+import { PaginatedVirtualList } from '../../../../../components/PaginatedVirtualList';
+import RoomMessage from '../../../../../components/message/variants/RoomMessage';
+import SystemMessage from '../../../../../components/message/variants/SystemMessage';
+import { useFormatDate } from '../../../../../hooks/useFormatDate';
+import MessageListErrorBoundary from '../../../MessageList/MessageListErrorBoundary';
+import { isMessageNewDay } from '../../../MessageList/lib/isMessageNewDay';
+import MessageListProvider from '../../../MessageList/providers/MessageListProvider';
+import { useRoomSubscription } from '../../../contexts/RoomContext';
+import type { MessageSearchItem } from '../hooks/useMessageSearchQuery';
+import { useMessageSearchQuery } from '../hooks/useMessageSearchQuery';
+
+type MessageSearchProps = {
+ searchText: string;
+ globalSearch: boolean;
+};
+
+const MessageSearch = ({ searchText, globalSearch }: MessageSearchProps): ReactElement => {
+ const t = useTranslation();
+ const formatDate = useFormatDate();
+ const showUserAvatar = !!useUserPreference('displayAvatars');
+
+ const subscription = useRoomSubscription();
+ const { isPending, isSuccess, data, fetchNextPage } = useMessageSearchQuery({ searchText, globalSearch });
+ const items = data?.items || [];
+ const itemCount = data?.itemCount ?? 0;
+
+ if (!isSuccess) {
+ return <>>;
+ }
+
+ return (
+ <>
+ {items.length === 0 && }
+ {items.length > 0 && (
+
+
+
+
+ {
+ const previous = items[index - 1];
+
+ const newDay = isMessageNewDay(message, previous);
+
+ const system = MessageTypes.isSystemMessage(message);
+
+ const unread = subscription?.tunread?.includes(message._id) ?? false;
+ const mention = subscription?.tunreadUser?.includes(message._id) ?? false;
+ const all = subscription?.tunreadGroup?.includes(message._id) ?? false;
+
+ return (
+
+ {newDay && {formatDate(message.ts)}}
+
+ {system ? (
+
+ ) : (
+
+ )}
+
+ );
+ }}
+ />
+
+
+
+
+ )}
+ >
+ );
+};
+
+export default memo(MessageSearch);
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/hooks/useMessageSearchQuery.ts b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/hooks/useMessageSearchQuery.ts
index ffa22b4ae69a6..3687ad8d724a7 100644
--- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/hooks/useMessageSearchQuery.ts
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/hooks/useMessageSearchQuery.ts
@@ -1,30 +1,39 @@
-import { useMethod, useTranslation, useUserId } from '@rocket.chat/ui-contexts';
-import { keepPreviousData, useQuery } from '@tanstack/react-query';
+import type { ServerMethods } from '@rocket.chat/ddp-client';
+import { useMethod, useSetting, useTranslation, useUserId } from '@rocket.chat/ui-contexts';
+import { keepPreviousData, useInfiniteQuery } from '@tanstack/react-query';
import { useRoom } from '../../../contexts/RoomContext';
-export const useMessageSearchQuery = ({
- searchText,
- limit,
- globalSearch,
-}: {
- searchText: string;
- limit: number;
- globalSearch: boolean;
-}) => {
+export type MessageSearchItem = NonNullable>['message']>['docs'][number];
+
+export const useMessageSearchQuery = ({ searchText, globalSearch }: { searchText: string; globalSearch: boolean }) => {
const uid = useUserId();
const room = useRoom();
+ const pageSize = useSetting('PageSize', 10);
const t = useTranslation();
const searchMessages = useMethod('rocketchatSearch.search');
- return useQuery({
- queryKey: ['rooms', room._id, 'message-search', { uid, rid: room._id, searchText, limit, globalSearch }] as const,
-
- queryFn: async () => {
+ return useInfiniteQuery({
+ queryKey: ['rooms', room._id, 'message-search', { uid, rid: room._id, searchText, globalSearch }] as const,
+ queryFn: async ({ pageParam: limit }) => {
const result = await searchMessages(searchText, { uid, rid: room._id }, { limit, searchAll: globalSearch });
- return result.message?.docs ?? [];
+ const items = result.message?.docs ?? [];
+
+ return {
+ items,
+ itemCount: items.length >= limit ? items.length + 1 : items.length,
+ };
+ },
+ initialPageParam: pageSize,
+ getNextPageParam: (lastPage, _allPages, lastPageParam) => {
+ if (lastPage.items.length < lastPageParam) {
+ return undefined;
+ }
+
+ return lastPageParam + pageSize;
},
+ select: ({ pages }) => pages.at(-1) ?? { items: [], itemCount: 0 },
placeholderData: keepPreviousData,
meta: {
errorToastMessage: t('Search_message_search_failed'),
From 2d5502ba9457c7ffa166a1a2c00a3da58d3f5f33 Mon Sep 17 00:00:00 2001
From: srijna
Date: Thu, 11 Jun 2026 18:19:27 +0530
Subject: [PATCH 2/9] fix: constrain message search list height
---
.../MessageSearchTab/MessageSearchTab.tsx | 2 +-
.../components/MessageSearch.spec.tsx | 13 +++++++++++++
.../MessageSearchTab/components/MessageSearch.tsx | 12 +++++++++++-
3 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx
index dabab41133660..8abf7d80899c2 100644
--- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx
@@ -48,7 +48,7 @@ const MessageSearchTab = () => {
<>
{searchText && isPending && }
{isSuccess && (
-
+
)}
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx
index f56123f65543a..066a5845fe0f8 100644
--- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx
@@ -86,6 +86,19 @@ describe('MessageSearch', () => {
expect(screen.getByText('No_results_found')).toBeInTheDocument();
});
+ it('renders nothing until the search query succeeds', () => {
+ useMessageSearchQueryMock.mockReturnValue({
+ isPending: true,
+ isSuccess: false,
+ data: undefined,
+ fetchNextPage: jest.fn(),
+ });
+
+ const { container } = render();
+
+ expect(container).toBeEmptyDOMElement();
+ });
+
it('renders room and system messages with date dividers', () => {
const roomMessage = createMessage('message-1');
const systemMessage = createMessage('message-2', {
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx
index 5fa0947e4ab81..0ceca44f54112 100644
--- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx
@@ -41,7 +41,17 @@ const MessageSearch = ({ searchText, globalSearch }: MessageSearchProps): ReactE
{items.length > 0 && (
-
+
Date: Thu, 11 Jun 2026 20:16:10 +0530
Subject: [PATCH 3/9] fix: address message search review feedback
---
.../MessageSearchTab/MessageSearchTab.tsx | 24 ++-
.../components/MessageSearch.spec.tsx | 91 +++++-----
.../components/MessageSearch.stories.tsx | 155 ++++++++++++++++++
.../components/MessageSearch.tsx | 40 +++--
4 files changed, 236 insertions(+), 74 deletions(-)
create mode 100644 apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx
index 8abf7d80899c2..a94eb0e29431f 100644
--- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/MessageSearchTab.tsx
@@ -8,7 +8,7 @@ import {
ContextualbarSection,
ContextualbarDialog,
} from '@rocket.chat/ui-client';
-import { useRoomToolbox } from '@rocket.chat/ui-contexts';
+import { useRoomToolbox, useUserPreference } from '@rocket.chat/ui-contexts';
import { memo, useId, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -17,8 +17,9 @@ import MessageSearchForm from './components/MessageSearchForm';
import { useMessageSearchProviderQuery } from './hooks/useMessageSearchProviderQuery';
import { useMessageSearchQuery } from './hooks/useMessageSearchQuery';
import ResultsLiveRegion from '../../../../components/ResultsLiveRegion';
+import { useFormatDate } from '../../../../hooks/useFormatDate';
+import { useRoomSubscription } from '../../contexts/RoomContext';
-// TODO: Refactor this component to isolate the data from the visual
const MessageSearchTab = () => {
const { t } = useTranslation();
const searchListId = useId();
@@ -27,8 +28,12 @@ const MessageSearchTab = () => {
const providerQuery = useMessageSearchProviderQuery();
const [{ searchText, globalSearch }, handleSearch] = useState({ searchText: '', globalSearch: false });
- const { isSuccess, data, isPending } = useMessageSearchQuery({ searchText, globalSearch });
+ const { isPending, isSuccess, data, fetchNextPage } = useMessageSearchQuery({ searchText, globalSearch });
+ const items = data?.items || [];
const itemCount = data?.itemCount ?? 0;
+ const subscription = useRoomSubscription();
+ const showUserAvatar = !!useUserPreference('displayAvatars');
+ const formatDate = useFormatDate();
return (
@@ -49,7 +54,18 @@ const MessageSearchTab = () => {
{searchText && isPending && }
{isSuccess && (
-
+
)}
>
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx
index 066a5845fe0f8..a071e3b34d6c9 100644
--- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx
@@ -1,17 +1,20 @@
-import type { IMessage } from '@rocket.chat/core-typings';
+import type { IMessage, ISubscription } from '@rocket.chat/core-typings';
import { render, screen } from '@testing-library/react';
import MessageSearch from './MessageSearch';
-
-const useMessageSearchQueryMock = jest.fn();
-
-jest.mock('../hooks/useMessageSearchQuery', () => ({
- useMessageSearchQuery: (...args: unknown[]) => useMessageSearchQueryMock(...args),
-}));
+import type { MessageSearchItem } from '../hooks/useMessageSearchQuery';
jest.mock('../../../../../components/PaginatedVirtualList', () => ({
- PaginatedVirtualList: ({ items, renderItem }: { items: IMessage[]; renderItem: (item: IMessage, index: number) => React.ReactNode }) => (
-
+ PaginatedVirtualList: ({
+ items,
+ totalCount,
+ renderItem,
+ }: {
+ items: MessageSearchItem[];
+ totalCount: number;
+ renderItem: (item: MessageSearchItem, index: number) => React.ReactNode;
+ }) => (
+
{items.map((item, index) => (
{renderItem(item, index)}
))}
@@ -27,10 +30,6 @@ jest.mock('../../../../../components/message/variants/SystemMessage', () => ({ m
{message.msg}
));
-jest.mock('../../../../../hooks/useFormatDate', () => ({
- useFormatDate: () => () => 'formatted-date',
-}));
-
jest.mock('../../../MessageList/MessageListErrorBoundary', () => ({
__esModule: true,
default: ({ children }: { children: React.ReactNode }) => <>{children}>,
@@ -41,60 +40,51 @@ jest.mock('../../../MessageList/providers/MessageListProvider', () => ({
default: ({ children }: { children: React.ReactNode }) => <>{children}>,
}));
-jest.mock('../../../contexts/RoomContext', () => ({
- useRoomSubscription: () => ({
- tunread: ['message-1'],
- tunreadUser: ['message-1'],
- tunreadGroup: ['message-1'],
- }),
-}));
-
jest.mock('@rocket.chat/ui-contexts', () => ({
...jest.requireActual('@rocket.chat/ui-contexts'),
useTranslation: () => (key: string) => key,
- useUserPreference: () => true,
}));
-const createMessage = (id: string, overrides: Partial
= {}): IMessage =>
+const createMessage = (id: string, overrides: Partial = {}): MessageSearchItem =>
({
_id: id,
rid: 'room-id',
+ user: 'testuser',
msg: `Message ${id}`,
ts: new Date('2026-03-22T10:00:00.000Z'),
u: { _id: 'user-id', username: 'testuser', name: 'Test User' },
_updatedAt: new Date('2026-03-22T10:00:00.000Z'),
...overrides,
- }) as IMessage;
+ }) as MessageSearchItem;
+
+const subscription = {
+ tunread: ['message-1'],
+ tunreadUser: ['message-1'],
+ tunreadGroup: ['message-1'],
+} as ISubscription;
+
+const defaultProps = {
+ items: [],
+ itemCount: 0,
+ isPending: false,
+ isSuccess: true,
+ fetchNextPage: jest.fn(),
+ subscription,
+ showUserAvatar: true,
+ formatDate: () => 'formatted-date',
+ searchText: 'hello',
+ noResultsTitle: 'No_results_found',
+};
describe('MessageSearch', () => {
- beforeEach(() => {
- useMessageSearchQueryMock.mockReturnValue({
- isPending: false,
- isSuccess: true,
- data: { items: [], itemCount: 0 },
- fetchNextPage: jest.fn(),
- });
- });
-
- afterEach(() => {
- useMessageSearchQueryMock.mockReset();
- });
-
it('renders the empty state when no messages are returned', () => {
- render();
+ render();
expect(screen.getByText('No_results_found')).toBeInTheDocument();
});
it('renders nothing until the search query succeeds', () => {
- useMessageSearchQueryMock.mockReturnValue({
- isPending: true,
- isSuccess: false,
- data: undefined,
- fetchNextPage: jest.fn(),
- });
-
- const { container } = render();
+ const { container } = render();
expect(container).toBeEmptyDOMElement();
});
@@ -107,14 +97,7 @@ describe('MessageSearch', () => {
msg: 'System event',
});
- useMessageSearchQueryMock.mockReturnValue({
- isPending: false,
- isSuccess: true,
- data: { items: [roomMessage, systemMessage], itemCount: 2 },
- fetchNextPage: jest.fn(),
- });
-
- render();
+ render();
expect(screen.getByTestId('room-message')).toHaveTextContent('Message message-1');
expect(screen.getByTestId('system-message')).toHaveTextContent('System event');
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx
new file mode 100644
index 0000000000000..c51494f9e5460
--- /dev/null
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx
@@ -0,0 +1,155 @@
+import type { IMessage } from '@rocket.chat/core-typings';
+import { Box } from '@rocket.chat/fuselage';
+import { mockAppRoot } from '@rocket.chat/mock-providers';
+import { Contextualbar } from '@rocket.chat/ui-client';
+import { action } from '@storybook/addon-actions';
+import type { Meta, StoryObj } from '@storybook/react';
+import type { UseInfiniteQueryResult } from '@tanstack/react-query';
+
+import MessageSearch from './MessageSearch';
+import { createFakeMessageWithMd, createFakeRoom, createFakeSubscription } from '../../../../../../tests/mocks/data';
+import type { MessageSearchItem } from '../hooks/useMessageSearchQuery';
+
+const room = createFakeRoom({ _id: 'room-id', t: 'c', name: 'general', fname: 'General' });
+const subscription = createFakeSubscription({
+ rid: room._id,
+ tunread: ['message-2'],
+ tunreadUser: ['message-2'],
+ tunreadGroup: [],
+});
+
+const fetchNextPageAction = action('fetchNextPage');
+const fetchNextPage = (async (options) => {
+ fetchNextPageAction(options);
+ return {} as Awaited>;
+}) satisfies UseInfiniteQueryResult['fetchNextPage'];
+
+const formatDate = (date: Date | string | number): string => new Intl.DateTimeFormat('en', { dateStyle: 'medium' }).format(new Date(date));
+
+const createMessage = (overrides: Partial): MessageSearchItem =>
+ createFakeMessageWithMd({
+ rid: room._id,
+ u: {
+ _id: 'user-id',
+ username: 'ana.silva',
+ name: 'Ana Silva',
+ },
+ ...overrides,
+ }) as MessageSearchItem;
+
+const messages = [
+ createMessage({
+ _id: 'message-1',
+ msg: 'Can you share the deployment checklist?',
+ ts: new Date('2026-06-09T14:15:00.000Z'),
+ }),
+ createMessage({
+ _id: 'message-2',
+ msg: 'The checklist is attached to the release room topic.',
+ ts: new Date('2026-06-09T14:18:00.000Z'),
+ u: {
+ _id: 'user-id-2',
+ username: 'sam.chen',
+ name: 'Sam Chen',
+ },
+ }),
+ createMessage({
+ _id: 'message-3',
+ msg: 'I found the rollback notes as well.',
+ ts: new Date('2026-06-09T14:22:00.000Z'),
+ }),
+];
+
+const systemMessages = [
+ createMessage({
+ _id: 'system-message-1',
+ msg: 'Sam Chen joined the room',
+ t: 'uj',
+ ts: new Date('2026-06-09T09:00:00.000Z'),
+ }),
+ createMessage({
+ _id: 'system-message-2',
+ msg: 'Room topic changed to Release coordination',
+ t: 'room_changed_topic',
+ ts: new Date('2026-06-09T09:05:00.000Z'),
+ }),
+];
+
+const multipleDateMessages = [
+ createMessage({
+ _id: 'date-message-1',
+ msg: 'Initial search result from Monday.',
+ ts: new Date('2026-06-08T10:00:00.000Z'),
+ }),
+ createMessage({
+ _id: 'date-message-2',
+ msg: 'Follow-up result from Tuesday.',
+ ts: new Date('2026-06-09T10:00:00.000Z'),
+ }),
+ createMessage({
+ _id: 'date-message-3',
+ msg: 'Final result from Wednesday.',
+ ts: new Date('2026-06-10T10:00:00.000Z'),
+ }),
+];
+
+const meta = {
+ component: MessageSearch,
+ parameters: {
+ layout: 'fullscreen',
+ actions: { argTypesRegex: '^on.*' },
+ },
+ decorators: [
+ mockAppRoot().withJohnDoe().withRoom(room).withSubscription(subscription).buildStoryDecorator(),
+ (fn) => (
+
+
+ {fn()}
+
+
+ ),
+ ],
+ args: {
+ itemCount: 0,
+ isPending: false,
+ isSuccess: true,
+ fetchNextPage,
+ subscription,
+ showUserAvatar: true,
+ formatDate,
+ searchText: 'release',
+ noResultsTitle: 'No results found',
+ },
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Empty: Story = {
+ args: {
+ items: [],
+ itemCount: 0,
+ },
+};
+
+export const Messages: Story = {
+ args: {
+ items: messages,
+ itemCount: messages.length,
+ },
+};
+
+export const SystemMessages: Story = {
+ args: {
+ items: systemMessages,
+ itemCount: systemMessages.length,
+ },
+};
+
+export const MultipleDateGroups: Story = {
+ args: {
+ items: multipleDateMessages,
+ itemCount: multipleDateMessages.length,
+ },
+};
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx
index 0ceca44f54112..645bb35e45e8c 100644
--- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.tsx
@@ -1,43 +1,51 @@
+import type { ISubscription } from '@rocket.chat/core-typings';
import { Box, MessageDivider } from '@rocket.chat/fuselage';
import { MessageTypes } from '@rocket.chat/message-types';
import { ContextualbarEmptyContent } from '@rocket.chat/ui-client';
-import { useTranslation, useUserPreference } from '@rocket.chat/ui-contexts';
+import type { UseInfiniteQueryResult } from '@tanstack/react-query';
import type { ReactElement } from 'react';
import { Fragment, memo } from 'react';
import { PaginatedVirtualList } from '../../../../../components/PaginatedVirtualList';
import RoomMessage from '../../../../../components/message/variants/RoomMessage';
import SystemMessage from '../../../../../components/message/variants/SystemMessage';
-import { useFormatDate } from '../../../../../hooks/useFormatDate';
import MessageListErrorBoundary from '../../../MessageList/MessageListErrorBoundary';
import { isMessageNewDay } from '../../../MessageList/lib/isMessageNewDay';
import MessageListProvider from '../../../MessageList/providers/MessageListProvider';
-import { useRoomSubscription } from '../../../contexts/RoomContext';
import type { MessageSearchItem } from '../hooks/useMessageSearchQuery';
-import { useMessageSearchQuery } from '../hooks/useMessageSearchQuery';
type MessageSearchProps = {
+ items: MessageSearchItem[];
+ itemCount: number;
+ isPending: boolean;
+ isSuccess: boolean;
+ fetchNextPage: UseInfiniteQueryResult['fetchNextPage'];
+ subscription: ISubscription | undefined;
+ showUserAvatar: boolean;
+ formatDate: (date: Date | string | number) => string;
searchText: string;
- globalSearch: boolean;
+ noResultsTitle: string;
};
-const MessageSearch = ({ searchText, globalSearch }: MessageSearchProps): ReactElement => {
- const t = useTranslation();
- const formatDate = useFormatDate();
- const showUserAvatar = !!useUserPreference('displayAvatars');
-
- const subscription = useRoomSubscription();
- const { isPending, isSuccess, data, fetchNextPage } = useMessageSearchQuery({ searchText, globalSearch });
- const items = data?.items || [];
- const itemCount = data?.itemCount ?? 0;
-
+const MessageSearch = ({
+ items,
+ itemCount,
+ isPending,
+ isSuccess,
+ fetchNextPage,
+ subscription,
+ showUserAvatar,
+ formatDate,
+ searchText,
+ noResultsTitle,
+}: MessageSearchProps): ReactElement => {
if (!isSuccess) {
return <>>;
}
return (
<>
- {items.length === 0 && }
+ {items.length === 0 && }
{items.length > 0 && (
From 8f9a77b9de548aff44bf536e345703633b972312 Mon Sep 17 00:00:00 2001
From: MartinSchoeler
Date: Mon, 15 Jun 2026 17:14:49 -0300
Subject: [PATCH 4/9] chore: update test
---
.../components/MessageSearch.spec.tsx | 72 ++++++++++++-------
1 file changed, 45 insertions(+), 27 deletions(-)
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx
index a071e3b34d6c9..125273efc72e0 100644
--- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx
@@ -1,7 +1,12 @@
-import type { IMessage, ISubscription } from '@rocket.chat/core-typings';
+import type { IMessage, IRoom, ISubscription, IUser } from '@rocket.chat/core-typings';
+import { mockAppRoot } from '@rocket.chat/mock-providers';
+import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts';
import { render, screen } from '@testing-library/react';
+import type { ReactNode } from 'react';
import MessageSearch from './MessageSearch';
+import FakeRoomProvider from '../../../../../../tests/mocks/client/FakeRoomProvider';
+import { createFakeRoom, createFakeSubscription, createFakeUser } from '../../../../../../tests/mocks/data';
import type { MessageSearchItem } from '../hooks/useMessageSearchQuery';
jest.mock('../../../../../components/PaginatedVirtualList', () => ({
@@ -12,7 +17,7 @@ jest.mock('../../../../../components/PaginatedVirtualList', () => ({
}: {
items: MessageSearchItem[];
totalCount: number;
- renderItem: (item: MessageSearchItem, index: number) => React.ReactNode;
+ renderItem: (item: MessageSearchItem, index: number) => ReactNode;
}) => (
{items.map((item, index) => (
@@ -22,27 +27,8 @@ jest.mock('../../../../../components/PaginatedVirtualList', () => ({
),
}));
-jest.mock('../../../../../components/message/variants/RoomMessage', () => ({ message }: { message: IMessage }) => (
-
{message.msg}
-));
-
-jest.mock('../../../../../components/message/variants/SystemMessage', () => ({ message }: { message: IMessage }) => (
-
{message.msg}
-));
-
-jest.mock('../../../MessageList/MessageListErrorBoundary', () => ({
- __esModule: true,
- default: ({ children }: { children: React.ReactNode }) => <>{children}>,
-}));
-
-jest.mock('../../../MessageList/providers/MessageListProvider', () => ({
- __esModule: true,
- default: ({ children }: { children: React.ReactNode }) => <>{children}>,
-}));
-
-jest.mock('@rocket.chat/ui-contexts', () => ({
- ...jest.requireActual('@rocket.chat/ui-contexts'),
- useTranslation: () => (key: string) => key,
+jest.mock('../../../../../../app/utils/client', () => ({
+ getURL: (url: string) => url,
}));
const createMessage = (id: string, overrides: Partial
= {}): MessageSearchItem =>
@@ -76,9 +62,39 @@ const defaultProps = {
noResultsTitle: 'No_results_found',
};
+// TODO: Create a to mock the MessageListProvider
+jest.mock('../../../MessageList/providers/MessageListProvider', () => ({ children }: { children: ReactNode }) => <>{children}>);
+
+const getUserInfoMocked = jest.fn().mockResolvedValue({ user: createFakeUser({ _id: 'peer-uid', username: 'peer-username' }) });
+
+const appRoot = (overrides: { user?: IUser | null; room?: IRoom; subscription?: SubscriptionWithRoom } = {}) => {
+ const {
+ user = createFakeUser({ _id: 'own-uid', username: 'own-username' }),
+ room = createFakeRoom({ uids: ['own-uid', 'peer-uid'] }),
+ subscription = createFakeSubscription(),
+ } = overrides;
+
+ const root = mockAppRoot()
+ .withRoom(room)
+ .withEndpoint('GET', '/v1/users.info', getUserInfoMocked)
+ .wrap((children) => (
+
+ {children}
+
+ ));
+
+ if (user !== null) {
+ root.withUser(user);
+ }
+
+ return root.build();
+};
+
describe('MessageSearch', () => {
it('renders the empty state when no messages are returned', () => {
- render();
+ render(, {
+ wrapper: appRoot(),
+ });
expect(screen.getByText('No_results_found')).toBeInTheDocument();
});
@@ -97,10 +113,12 @@ describe('MessageSearch', () => {
msg: 'System event',
});
- render();
+ render(, {
+ wrapper: appRoot(),
+ });
- expect(screen.getByTestId('room-message')).toHaveTextContent('Message message-1');
- expect(screen.getByTestId('system-message')).toHaveTextContent('System event');
+ expect(screen.getByText('Message message-1')).toBeInTheDocument();
+ expect(screen.getByText('User_added_to')).toBeInTheDocument();
expect(screen.getAllByText('formatted-date')).toHaveLength(2);
});
});
From 8848f240342407fd9e7e03e56fb2532de885f85e Mon Sep 17 00:00:00 2001
From: MartinSchoeler
Date: Mon, 15 Jun 2026 17:29:41 -0300
Subject: [PATCH 5/9] chore simplify tests even further
---
.../components/MessageSearch.spec.tsx | 108 +--
.../components/MessageSearch.stories.tsx | 24 +-
.../__snapshots__/MessageSearch.spec.tsx.snap | 832 ++++++++++++++++++
3 files changed, 862 insertions(+), 102 deletions(-)
create mode 100644 apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx
index 125273efc72e0..d4357aa47fe09 100644
--- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.spec.tsx
@@ -1,12 +1,9 @@
-import type { IMessage, IRoom, ISubscription, IUser } from '@rocket.chat/core-typings';
-import { mockAppRoot } from '@rocket.chat/mock-providers';
-import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts';
-import { render, screen } from '@testing-library/react';
+import { composeStories } from '@storybook/react';
+import { render } from '@testing-library/react';
+import { axe } from 'jest-axe';
import type { ReactNode } from 'react';
-import MessageSearch from './MessageSearch';
-import FakeRoomProvider from '../../../../../../tests/mocks/client/FakeRoomProvider';
-import { createFakeRoom, createFakeSubscription, createFakeUser } from '../../../../../../tests/mocks/data';
+import * as stories from './MessageSearch.stories';
import type { MessageSearchItem } from '../hooks/useMessageSearchQuery';
jest.mock('../../../../../components/PaginatedVirtualList', () => ({
@@ -31,94 +28,25 @@ jest.mock('../../../../../../app/utils/client', () => ({
getURL: (url: string) => url,
}));
-const createMessage = (id: string, overrides: Partial = {}): MessageSearchItem =>
- ({
- _id: id,
- rid: 'room-id',
- user: 'testuser',
- msg: `Message ${id}`,
- ts: new Date('2026-03-22T10:00:00.000Z'),
- u: { _id: 'user-id', username: 'testuser', name: 'Test User' },
- _updatedAt: new Date('2026-03-22T10:00:00.000Z'),
- ...overrides,
- }) as MessageSearchItem;
-
-const subscription = {
- tunread: ['message-1'],
- tunreadUser: ['message-1'],
- tunreadGroup: ['message-1'],
-} as ISubscription;
-
-const defaultProps = {
- items: [],
- itemCount: 0,
- isPending: false,
- isSuccess: true,
- fetchNextPage: jest.fn(),
- subscription,
- showUserAvatar: true,
- formatDate: () => 'formatted-date',
- searchText: 'hello',
- noResultsTitle: 'No_results_found',
-};
-
// TODO: Create a to mock the MessageListProvider
jest.mock('../../../MessageList/providers/MessageListProvider', () => ({ children }: { children: ReactNode }) => <>{children}>);
-const getUserInfoMocked = jest.fn().mockResolvedValue({ user: createFakeUser({ _id: 'peer-uid', username: 'peer-username' }) });
-
-const appRoot = (overrides: { user?: IUser | null; room?: IRoom; subscription?: SubscriptionWithRoom } = {}) => {
- const {
- user = createFakeUser({ _id: 'own-uid', username: 'own-username' }),
- room = createFakeRoom({ uids: ['own-uid', 'peer-uid'] }),
- subscription = createFakeSubscription(),
- } = overrides;
-
- const root = mockAppRoot()
- .withRoom(room)
- .withEndpoint('GET', '/v1/users.info', getUserInfoMocked)
- .wrap((children) => (
-
- {children}
-
- ));
-
- if (user !== null) {
- root.withUser(user);
- }
-
- return root.build();
-};
+const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]);
-describe('MessageSearch', () => {
- it('renders the empty state when no messages are returned', () => {
- render(, {
- wrapper: appRoot(),
- });
-
- expect(screen.getByText('No_results_found')).toBeInTheDocument();
- });
-
- it('renders nothing until the search query succeeds', () => {
- const { container } = render();
-
- expect(container).toBeEmptyDOMElement();
- });
-
- it('renders room and system messages with date dividers', () => {
- const roomMessage = createMessage('message-1');
- const systemMessage = createMessage('message-2', {
- ts: new Date('2026-03-23T10:00:00.000Z'),
- t: 'au',
- msg: 'System event',
- });
+test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => {
+ const { baseElement } = render();
+ expect(baseElement).toMatchSnapshot();
+});
- render(, {
- wrapper: appRoot(),
- });
+test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => {
+ const { container } = render();
- expect(screen.getByText('Message message-1')).toBeInTheDocument();
- expect(screen.getByText('User_added_to')).toBeInTheDocument();
- expect(screen.getAllByText('formatted-date')).toHaveLength(2);
+ const results = await axe(container, {
+ rules: {
+ 'nested-interactive': { enabled: false },
+ 'aria-required-parent': { enabled: false },
+ 'aria-required-children': { enabled: false },
+ },
});
+ expect(results).toHaveNoViolations();
});
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx
index c51494f9e5460..090b9bb00a642 100644
--- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx
@@ -2,11 +2,11 @@ import type { IMessage } from '@rocket.chat/core-typings';
import { Box } from '@rocket.chat/fuselage';
import { mockAppRoot } from '@rocket.chat/mock-providers';
import { Contextualbar } from '@rocket.chat/ui-client';
-import { action } from '@storybook/addon-actions';
import type { Meta, StoryObj } from '@storybook/react';
import type { UseInfiniteQueryResult } from '@tanstack/react-query';
import MessageSearch from './MessageSearch';
+import FakeRoomProvider from '../../../../../../tests/mocks/client/FakeRoomProvider';
import { createFakeMessageWithMd, createFakeRoom, createFakeSubscription } from '../../../../../../tests/mocks/data';
import type { MessageSearchItem } from '../hooks/useMessageSearchQuery';
@@ -18,13 +18,11 @@ const subscription = createFakeSubscription({
tunreadGroup: [],
});
-const fetchNextPageAction = action('fetchNextPage');
-const fetchNextPage = (async (options) => {
- fetchNextPageAction(options);
- return {} as Awaited>;
-}) satisfies UseInfiniteQueryResult['fetchNextPage'];
+const fetchNextPage = (async () =>
+ ({}) as Awaited>) satisfies UseInfiniteQueryResult['fetchNextPage'];
-const formatDate = (date: Date | string | number): string => new Intl.DateTimeFormat('en', { dateStyle: 'medium' }).format(new Date(date));
+const formatDate = (date: Date | string | number): string =>
+ new Intl.DateTimeFormat('en-US', { dateStyle: 'medium', timeZone: 'UTC' }).format(new Date(date));
const createMessage = (overrides: Partial): MessageSearchItem =>
createFakeMessageWithMd({
@@ -102,11 +100,13 @@ const meta = {
decorators: [
mockAppRoot().withJohnDoe().withRoom(room).withSubscription(subscription).buildStoryDecorator(),
(fn) => (
-
-
- {fn()}
-
-
+
+
+
+ {fn()}
+
+
+
),
],
args: {
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap
new file mode 100644
index 0000000000000..be92f861f5d64
--- /dev/null
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap
@@ -0,0 +1,832 @@
+// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
+
+exports[`renders Empty without crashing 1`] = `
+
+
+
+
+
+
+
+
+
+
+ No results found
+
+
+
+
+
+
+
+`;
+
+exports[`renders Messages without crashing 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+ Jun 9, 2026
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Can you share the deployment checklist?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The checklist is attached to the
+
+ release
+
+ room topic.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ I found the rollback notes as well.
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`renders MultipleDateGroups without crashing 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+ Jun 8, 2026
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Initial search result from Monday.
+
+
+
+
+
+
+
+
+
+
+ Jun 9, 2026
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Follow-up result from Tuesday.
+
+
+
+
+
+
+
+
+
+
+ Jun 10, 2026
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Final result from Wednesday.
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`renders SystemMessages without crashing 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+ Jun 9, 2026
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ User_joined_the_channel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ room_changed_topic_to
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
From c041d39f9385c12523ec91c518d4f961a17c1e84 Mon Sep 17 00:00:00 2001
From: MartinSchoeler
Date: Mon, 15 Jun 2026 17:32:39 -0300
Subject: [PATCH 6/9] chore: compound stories into one
---
.../components/MessageSearch.stories.tsx | 72 +--
.../__snapshots__/MessageSearch.spec.tsx.snap | 478 +++++++-----------
2 files changed, 218 insertions(+), 332 deletions(-)
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx
index 090b9bb00a642..93ea9b116ee0a 100644
--- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/MessageSearch.stories.tsx
@@ -35,7 +35,29 @@ const createMessage = (overrides: Partial): MessageSearchItem =>
...overrides,
}) as MessageSearchItem;
-const messages = [
+const searchResults = [
+ createMessage({
+ _id: 'date-message-1',
+ msg: 'Initial search result from Monday.',
+ ts: new Date('2026-06-08T10:00:00.000Z'),
+ }),
+ createMessage({
+ _id: 'system-message-1',
+ msg: 'Sam Chen joined the room',
+ t: 'uj',
+ ts: new Date('2026-06-09T09:00:00.000Z'),
+ }),
+ createMessage({
+ _id: 'system-message-2',
+ msg: 'Room topic changed to Release coordination',
+ t: 'room_changed_topic',
+ ts: new Date('2026-06-09T09:05:00.000Z'),
+ }),
+ createMessage({
+ _id: 'date-message-2',
+ msg: 'Follow-up result from Tuesday.',
+ ts: new Date('2026-06-09T10:00:00.000Z'),
+ }),
createMessage({
_id: 'message-1',
msg: 'Can you share the deployment checklist?',
@@ -56,34 +78,6 @@ const messages = [
msg: 'I found the rollback notes as well.',
ts: new Date('2026-06-09T14:22:00.000Z'),
}),
-];
-
-const systemMessages = [
- createMessage({
- _id: 'system-message-1',
- msg: 'Sam Chen joined the room',
- t: 'uj',
- ts: new Date('2026-06-09T09:00:00.000Z'),
- }),
- createMessage({
- _id: 'system-message-2',
- msg: 'Room topic changed to Release coordination',
- t: 'room_changed_topic',
- ts: new Date('2026-06-09T09:05:00.000Z'),
- }),
-];
-
-const multipleDateMessages = [
- createMessage({
- _id: 'date-message-1',
- msg: 'Initial search result from Monday.',
- ts: new Date('2026-06-08T10:00:00.000Z'),
- }),
- createMessage({
- _id: 'date-message-2',
- msg: 'Follow-up result from Tuesday.',
- ts: new Date('2026-06-09T10:00:00.000Z'),
- }),
createMessage({
_id: 'date-message-3',
msg: 'Final result from Wednesday.',
@@ -133,23 +127,9 @@ export const Empty: Story = {
},
};
-export const Messages: Story = {
- args: {
- items: messages,
- itemCount: messages.length,
- },
-};
-
-export const SystemMessages: Story = {
- args: {
- items: systemMessages,
- itemCount: systemMessages.length,
- },
-};
-
-export const MultipleDateGroups: Story = {
+export const WithResults: Story = {
args: {
- items: multipleDateMessages,
- itemCount: multipleDateMessages.length,
+ items: searchResults,
+ itemCount: searchResults.length,
},
};
diff --git a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap
index be92f861f5d64..cda1af676124a 100644
--- a/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap
+++ b/apps/meteor/client/views/room/contextualBar/MessageSearchTab/components/__snapshots__/MessageSearch.spec.tsx.snap
@@ -34,7 +34,7 @@ exports[`renders Empty without crashing 1`] = `
- Jun 9, 2026
+ Jun 8, 2026
@@ -116,7 +116,7 @@ exports[`renders Messages without crashing 1`] = `
@@ -158,15 +158,146 @@ exports[`renders Messages without crashing 1`] = `
+
+
+ Jun 9, 2026
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ User_joined_the_channel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ room_changed_topic_to
+
+
+
+
+
+
+
+
@@ -180,11 +311,11 @@ exports[`renders Messages without crashing 1`] = `
alt="Avatar"
aria-hidden="true"
class="rcx-avatar__element rcx-avatar__element--x36"
- data-username="sam.chen"
+ data-username="ana.silva"
role="button"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQYV2Oora39DwAFaQJ3y3rKeAAAAABJRU5ErkJggg=="
style="cursor: pointer;"
- title="sam.chen"
+ title="ana.silva"
/>
@@ -198,24 +329,24 @@ exports[`renders Messages without crashing 1`] = `
class="rcx-box rcx-box--full rcx-message-header__wrapper"
>
- The checklist is attached to the
-
- release
-
- room topic.
+ Follow-up result from Tuesday.
@@ -246,15 +373,15 @@ exports[`renders Messages without crashing 1`] = `
@@ -288,7 +415,7 @@ exports[`renders Messages without crashing 1`] = `
@@ -328,64 +455,17 @@ exports[`renders Messages without crashing 1`] = `
/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Jun 8, 2026
-
-
-
-
-
@@ -399,11 +479,11 @@ exports[`renders MultipleDateGroups without crashing 1`] = `
alt="Avatar"
aria-hidden="true"
class="rcx-avatar__element rcx-avatar__element--x36"
- data-username="ana.silva"
+ data-username="sam.chen"
role="button"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQYV2Oora39DwAFaQJ3y3rKeAAAAABJRU5ErkJggg=="
style="cursor: pointer;"
- title="ana.silva"
+ title="sam.chen"
/>
@@ -417,24 +497,24 @@ exports[`renders MultipleDateGroups without crashing 1`] = `
class="rcx-box rcx-box--full rcx-message-header__wrapper"
>
- Initial search result from Monday.
+ The checklist is attached to the
+
+ release
+
+ room topic.
@@ -461,32 +545,15 @@ exports[`renders MultipleDateGroups without crashing 1`] = `
-
-
- Jun 9, 2026
-
-
-
-
-
@@ -520,7 +587,7 @@ exports[`renders MultipleDateGroups without crashing 1`] = `
@@ -669,164 +736,3 @@ exports[`renders MultipleDateGroups without crashing 1`] = `
-
-
-
-
-
-
-
-
-
-
- Jun 9, 2026
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- User_joined_the_channel
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- room_changed_topic_to
-
-
-
-
-
-
-
-
-
-
-
-
-