You've already forked element-web
mirror of
https://github.com/element-hq/element-web.git
synced 2025-08-08 03:42:14 +03:00
Allow jumping to message search from spotlight (#29850)
* Allow jumping to message search from spotlight replaces the message search hint which referenced the old UX Fixes #29831 Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update RoomSummaryCard.tsx * Update actions.ts * Delete src/hooks/useTransition.ts * Update RoomSummaryCard.tsx * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Add test Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
committed by
GitHub
parent
23597e959b
commit
4bf28f8159
@@ -412,7 +412,8 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
.mx_SpotlightDialog_joinRoomAlias,
|
.mx_SpotlightDialog_joinRoomAlias,
|
||||||
.mx_SpotlightDialog_explorePublicRooms,
|
.mx_SpotlightDialog_explorePublicRooms,
|
||||||
.mx_SpotlightDialog_explorePublicSpaces,
|
.mx_SpotlightDialog_explorePublicSpaces,
|
||||||
.mx_SpotlightDialog_startGroupChat {
|
.mx_SpotlightDialog_startGroupChat,
|
||||||
|
.mx_SpotlightDialog_searchMessages {
|
||||||
padding-left: $spacing-32;
|
padding-left: $spacing-32;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
@@ -451,22 +452,14 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
mask-image: url("$(res)/img/element-icons/group-members.svg");
|
mask-image: url("$(res)/img/element-icons/group-members.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_SpotlightDialog_searchMessages::before {
|
||||||
|
mask-image: url("$(res)/img/element-icons/room/search-inset.svg");
|
||||||
|
}
|
||||||
|
|
||||||
.mx_SpotlightDialog_otherSearches_messageSearchText {
|
.mx_SpotlightDialog_otherSearches_messageSearchText {
|
||||||
font-size: $font-15px;
|
font-size: $font-15px;
|
||||||
line-height: $font-24px;
|
line-height: $font-24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SpotlightDialog_otherSearches_messageSearchIcon {
|
|
||||||
display: inline-block;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
background-color: $secondary-content;
|
|
||||||
vertical-align: text-bottom;
|
|
||||||
mask-repeat: no-repeat;
|
|
||||||
mask-position: center;
|
|
||||||
mask-size: contain;
|
|
||||||
mask-image: url("$(res)/img/element-icons/room/search-inset.svg");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SpotlightDialog_result_details {
|
.mx_SpotlightDialog_result_details {
|
||||||
|
@@ -163,7 +163,8 @@ $accent-1400: var(--cpd-color-green-1400);
|
|||||||
&.mx_SpotlightDialog_startChat::before,
|
&.mx_SpotlightDialog_startChat::before,
|
||||||
&.mx_SpotlightDialog_joinRoomAlias::before,
|
&.mx_SpotlightDialog_joinRoomAlias::before,
|
||||||
&.mx_SpotlightDialog_explorePublicRooms::before,
|
&.mx_SpotlightDialog_explorePublicRooms::before,
|
||||||
&.mx_SpotlightDialog_startGroupChat::before {
|
&.mx_SpotlightDialog_startGroupChat::before,
|
||||||
|
&.mx_SpotlightDialog_searchMessages::before {
|
||||||
background-color: $background !important;
|
background-color: $background !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
|||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { type ChangeEvent } from "react";
|
import React from "react";
|
||||||
import { type Room, type RoomState, RoomStateEvent, RoomMember, type MatrixEvent } from "matrix-js-sdk/src/matrix";
|
import { type Room, type RoomState, RoomStateEvent, RoomMember, type MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||||
import { throttle } from "lodash";
|
import { throttle } from "lodash";
|
||||||
|
|
||||||
@@ -49,8 +49,9 @@ interface RoomlessProps extends BaseProps {
|
|||||||
interface RoomProps extends BaseProps {
|
interface RoomProps extends BaseProps {
|
||||||
room: Room;
|
room: Room;
|
||||||
permalinkCreator: RoomPermalinkCreator;
|
permalinkCreator: RoomPermalinkCreator;
|
||||||
onSearchChange?: (e: ChangeEvent) => void;
|
onSearchChange?: (term: string) => void;
|
||||||
onSearchCancel?: () => void;
|
onSearchCancel?: () => void;
|
||||||
|
searchTerm?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = XOR<RoomlessProps, RoomProps>;
|
type Props = XOR<RoomlessProps, RoomProps>;
|
||||||
@@ -260,6 +261,7 @@ export default class RightPanel extends React.Component<Props, IState> {
|
|||||||
permalinkCreator={this.props.permalinkCreator!}
|
permalinkCreator={this.props.permalinkCreator!}
|
||||||
onSearchChange={this.props.onSearchChange}
|
onSearchChange={this.props.onSearchChange}
|
||||||
onSearchCancel={this.props.onSearchCancel}
|
onSearchCancel={this.props.onSearchCancel}
|
||||||
|
searchTerm={this.props.searchTerm}
|
||||||
focusRoomSearch={cardState?.focusRoomSearch}
|
focusRoomSearch={cardState?.focusRoomSearch}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@@ -10,7 +10,6 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {
|
import React, {
|
||||||
type ChangeEvent,
|
|
||||||
type ComponentProps,
|
type ComponentProps,
|
||||||
createRef,
|
createRef,
|
||||||
type ReactElement,
|
type ReactElement,
|
||||||
@@ -133,6 +132,7 @@ import RoomSearchAuxPanel from "../views/rooms/RoomSearchAuxPanel";
|
|||||||
import { PinnedMessageBanner } from "../views/rooms/PinnedMessageBanner";
|
import { PinnedMessageBanner } from "../views/rooms/PinnedMessageBanner";
|
||||||
import { ScopedRoomContextProvider, useScopedRoomContext } from "../../contexts/ScopedRoomContext";
|
import { ScopedRoomContextProvider, useScopedRoomContext } from "../../contexts/ScopedRoomContext";
|
||||||
import { DeclineAndBlockInviteDialog } from "../views/dialogs/DeclineAndBlockInviteDialog";
|
import { DeclineAndBlockInviteDialog } from "../views/dialogs/DeclineAndBlockInviteDialog";
|
||||||
|
import { type FocusMessageSearchPayload } from "../../dispatcher/payloads/FocusMessageSearchPayload.ts";
|
||||||
|
|
||||||
const DEBUG = false;
|
const DEBUG = false;
|
||||||
const PREVENT_MULTIPLE_JITSI_WITHIN = 30_000;
|
const PREVENT_MULTIPLE_JITSI_WITHIN = 30_000;
|
||||||
@@ -1244,6 +1244,11 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
case Action.View3pidInvite:
|
case Action.View3pidInvite:
|
||||||
onView3pidInvite(payload, RightPanelStore.instance);
|
onView3pidInvite(payload, RightPanelStore.instance);
|
||||||
break;
|
break;
|
||||||
|
case Action.FocusMessageSearch:
|
||||||
|
if ((payload as FocusMessageSearchPayload).initialText) {
|
||||||
|
this.onSearch(payload.initialText);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1806,8 +1811,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
defaultDispatcher.fire(Action.ViewRoomDirectory);
|
defaultDispatcher.fire(Action.ViewRoomDirectory);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSearchChange = debounce((e: ChangeEvent): void => {
|
private onSearchChange = debounce((term: string): void => {
|
||||||
const term = (e.target as HTMLInputElement).value;
|
|
||||||
this.onSearch(term);
|
this.onSearch(term);
|
||||||
}, 300);
|
}, 300);
|
||||||
|
|
||||||
@@ -2487,6 +2491,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
e2eStatus={this.state.e2eStatus}
|
e2eStatus={this.state.e2eStatus}
|
||||||
onSearchChange={this.onSearchChange}
|
onSearchChange={this.onSearchChange}
|
||||||
onSearchCancel={this.onCancelSearchClick}
|
onSearchCancel={this.onCancelSearchClick}
|
||||||
|
searchTerm={this.state.search?.term ?? ""}
|
||||||
/>
|
/>
|
||||||
) : undefined;
|
) : undefined;
|
||||||
|
|
||||||
|
@@ -608,6 +608,21 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||||||
{filterToLabel(Filter.People)}
|
{filterToLabel(Filter.People)}
|
||||||
</Option>
|
</Option>
|
||||||
)}
|
)}
|
||||||
|
{filter === null && (
|
||||||
|
<Option
|
||||||
|
id="mx_SpotlightDialog_button_searchMessages"
|
||||||
|
className="mx_SpotlightDialog_searchMessages"
|
||||||
|
onClick={() => {
|
||||||
|
defaultDispatcher.dispatch({
|
||||||
|
action: Action.FocusMessageSearch,
|
||||||
|
initialText: trimmedQuery,
|
||||||
|
});
|
||||||
|
onFinished();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{_t("spotlight_dialog|messages_label")}
|
||||||
|
</Option>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -997,28 +1012,6 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let messageSearchSection: JSX.Element | undefined;
|
|
||||||
if (filter === null) {
|
|
||||||
messageSearchSection = (
|
|
||||||
<div
|
|
||||||
className="mx_SpotlightDialog_section mx_SpotlightDialog_otherSearches"
|
|
||||||
role="group"
|
|
||||||
aria-labelledby="mx_SpotlightDialog_section_messageSearch"
|
|
||||||
>
|
|
||||||
<h4 id="mx_SpotlightDialog_section_messageSearch">
|
|
||||||
{_t("spotlight_dialog|message_search_section_title")}
|
|
||||||
</h4>
|
|
||||||
<div className="mx_SpotlightDialog_otherSearches_messageSearchText">
|
|
||||||
{_t(
|
|
||||||
"spotlight_dialog|search_messages_hint",
|
|
||||||
{},
|
|
||||||
{ icon: () => <div className="mx_SpotlightDialog_otherSearches_messageSearchIcon" /> },
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
content = (
|
content = (
|
||||||
<>
|
<>
|
||||||
{peopleSection}
|
{peopleSection}
|
||||||
@@ -1031,7 +1024,6 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||||||
{hiddenResultsSection}
|
{hiddenResultsSection}
|
||||||
{otherSearchesSection}
|
{otherSearchesSection}
|
||||||
{groupChatSection}
|
{groupChatSection}
|
||||||
{messageSearchSection}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
|||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { type JSX, type ChangeEvent, useContext, useEffect, useRef, useState } from "react";
|
import React, { type JSX, useContext, useEffect, useRef, useState } from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import {
|
import {
|
||||||
MenuItem,
|
MenuItem,
|
||||||
@@ -52,7 +52,6 @@ import { ShareDialog } from "../dialogs/ShareDialog";
|
|||||||
import { useEventEmitterState } from "../../../hooks/useEventEmitter";
|
import { useEventEmitterState } from "../../../hooks/useEventEmitter";
|
||||||
import { E2EStatus } from "../../../utils/ShieldUtils";
|
import { E2EStatus } from "../../../utils/ShieldUtils";
|
||||||
import { type RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
import { type RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
||||||
import { TimelineRenderingType } from "../../../contexts/RoomContext";
|
|
||||||
import RoomName from "../elements/RoomName";
|
import RoomName from "../elements/RoomName";
|
||||||
import ExportDialog from "../dialogs/ExportDialog";
|
import ExportDialog from "../dialogs/ExportDialog";
|
||||||
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
||||||
@@ -71,7 +70,6 @@ import { Box } from "../../utils/Box";
|
|||||||
import { useDispatcher } from "../../../hooks/useDispatcher";
|
import { useDispatcher } from "../../../hooks/useDispatcher";
|
||||||
import { Action } from "../../../dispatcher/actions";
|
import { Action } from "../../../dispatcher/actions";
|
||||||
import { Key } from "../../../Keyboard";
|
import { Key } from "../../../Keyboard";
|
||||||
import { useTransition } from "../../../hooks/useTransition";
|
|
||||||
import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms";
|
import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms";
|
||||||
import { usePinnedEvents } from "../../../hooks/usePinnedEvents";
|
import { usePinnedEvents } from "../../../hooks/usePinnedEvents";
|
||||||
import { ReleaseAnnouncement } from "../../structures/ReleaseAnnouncement.tsx";
|
import { ReleaseAnnouncement } from "../../structures/ReleaseAnnouncement.tsx";
|
||||||
@@ -82,9 +80,10 @@ import { useRoomTopicViewModel } from "../../viewmodels/right_panel/RoomSummaryC
|
|||||||
interface IProps {
|
interface IProps {
|
||||||
room: Room;
|
room: Room;
|
||||||
permalinkCreator: RoomPermalinkCreator;
|
permalinkCreator: RoomPermalinkCreator;
|
||||||
onSearchChange?: (e: ChangeEvent) => void;
|
onSearchChange?: (term: string) => void;
|
||||||
onSearchCancel?: () => void;
|
onSearchCancel?: () => void;
|
||||||
focusRoomSearch?: boolean;
|
focusRoomSearch?: boolean;
|
||||||
|
searchTerm?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const onRoomMembersClick = (): void => {
|
const onRoomMembersClick = (): void => {
|
||||||
@@ -180,6 +179,7 @@ const RoomSummaryCard: React.FC<IProps> = ({
|
|||||||
onSearchChange,
|
onSearchChange,
|
||||||
onSearchCancel,
|
onSearchCancel,
|
||||||
focusRoomSearch,
|
focusRoomSearch,
|
||||||
|
searchTerm,
|
||||||
}) => {
|
}) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
|
|
||||||
@@ -244,19 +244,6 @@ const RoomSummaryCard: React.FC<IProps> = ({
|
|||||||
searchInputRef.current?.focus();
|
searchInputRef.current?.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Clear the search field when the user leaves the search view
|
|
||||||
useTransition(
|
|
||||||
(prevTimelineRenderingType) => {
|
|
||||||
if (
|
|
||||||
prevTimelineRenderingType === TimelineRenderingType.Search &&
|
|
||||||
roomContext.timelineRenderingType !== TimelineRenderingType.Search &&
|
|
||||||
searchInputRef.current
|
|
||||||
) {
|
|
||||||
searchInputRef.current.value = "";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[roomContext.timelineRenderingType],
|
|
||||||
);
|
|
||||||
|
|
||||||
const alias = room.getCanonicalAlias() || room.getAltAliases()[0] || "";
|
const alias = room.getCanonicalAlias() || room.getAltAliases()[0] || "";
|
||||||
const roomInfo = (
|
const roomInfo = (
|
||||||
@@ -332,7 +319,10 @@ const RoomSummaryCard: React.FC<IProps> = ({
|
|||||||
<Search
|
<Search
|
||||||
placeholder={_t("room|search|placeholder")}
|
placeholder={_t("room|search|placeholder")}
|
||||||
name="room_message_search"
|
name="room_message_search"
|
||||||
onChange={onSearchChange}
|
onChange={(e) => {
|
||||||
|
onSearchChange(e.currentTarget.value);
|
||||||
|
}}
|
||||||
|
value={searchTerm}
|
||||||
className="mx_no_textinput"
|
className="mx_no_textinput"
|
||||||
ref={searchInputRef}
|
ref={searchInputRef}
|
||||||
autoFocus={focusRoomSearch}
|
autoFocus={focusRoomSearch}
|
||||||
|
@@ -362,7 +362,7 @@ export enum Action {
|
|||||||
View3pidInvite = "view_3pid_invite",
|
View3pidInvite = "view_3pid_invite",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens right panel room summary and focuses the search input
|
* Opens right panel room summary and focuses the search input. Use with a FocusMessageSearchPayload.
|
||||||
*/
|
*/
|
||||||
FocusMessageSearch = "focus_search",
|
FocusMessageSearch = "focus_search",
|
||||||
|
|
||||||
|
15
src/dispatcher/payloads/FocusMessageSearchPayload.ts
Normal file
15
src/dispatcher/payloads/FocusMessageSearchPayload.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2025 New Vector Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { type Action } from "../actions";
|
||||||
|
import { type ActionPayload } from "../payloads";
|
||||||
|
|
||||||
|
export interface FocusMessageSearchPayload extends ActionPayload {
|
||||||
|
action: Action.FocusMessageSearch;
|
||||||
|
|
||||||
|
initialText?: string;
|
||||||
|
}
|
@@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2024 New Vector Ltd.
|
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Based on https://stackoverflow.com/a/61680184
|
|
||||||
|
|
||||||
import { type DependencyList, useEffect, useRef } from "react";
|
|
||||||
|
|
||||||
export const useTransition = <D extends DependencyList>(callback: (...params: D) => void, deps: D): void => {
|
|
||||||
const func = useRef<(...params: D) => void>(callback);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
func.current = callback;
|
|
||||||
}, [callback]);
|
|
||||||
|
|
||||||
const args = useRef<D | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (args.current !== null) func.current(...args.current);
|
|
||||||
args.current = deps;
|
|
||||||
// eslint-disable-next-line react-compiler/react-compiler,react-hooks/exhaustive-deps
|
|
||||||
}, deps);
|
|
||||||
};
|
|
@@ -3230,7 +3230,7 @@
|
|||||||
"heading_without_query": "Search for",
|
"heading_without_query": "Search for",
|
||||||
"join_button_text": "Join %(roomAddress)s",
|
"join_button_text": "Join %(roomAddress)s",
|
||||||
"keyboard_scroll_hint": "Use <arrows/> to scroll",
|
"keyboard_scroll_hint": "Use <arrows/> to scroll",
|
||||||
"message_search_section_title": "Other searches",
|
"messages_label": "Messages",
|
||||||
"other_rooms_in_space": "Other rooms in %(spaceName)s",
|
"other_rooms_in_space": "Other rooms in %(spaceName)s",
|
||||||
"public_rooms_label": "Public rooms",
|
"public_rooms_label": "Public rooms",
|
||||||
"public_spaces_label": "Public spaces",
|
"public_spaces_label": "Public spaces",
|
||||||
@@ -3240,7 +3240,6 @@
|
|||||||
"result_may_be_hidden_privacy_warning": "Some results may be hidden for privacy",
|
"result_may_be_hidden_privacy_warning": "Some results may be hidden for privacy",
|
||||||
"result_may_be_hidden_warning": "Some results may be hidden",
|
"result_may_be_hidden_warning": "Some results may be hidden",
|
||||||
"search_dialog": "Search Dialog",
|
"search_dialog": "Search Dialog",
|
||||||
"search_messages_hint": "To search messages, look for this icon at the top of a room <icon/>",
|
|
||||||
"spaces_title": "Spaces you're in",
|
"spaces_title": "Spaces you're in",
|
||||||
"start_group_chat_button": "Start a group chat"
|
"start_group_chat_button": "Start a group chat"
|
||||||
},
|
},
|
||||||
|
@@ -309,6 +309,8 @@ export function createTestClient(): MatrixClient {
|
|||||||
pushProcessor: {
|
pushProcessor: {
|
||||||
getPushRuleById: jest.fn(),
|
getPushRuleById: jest.fn(),
|
||||||
},
|
},
|
||||||
|
search: jest.fn().mockResolvedValue({}),
|
||||||
|
processRoomEventsSearch: jest.fn().mockResolvedValue({ highlights: [], results: [] }),
|
||||||
} as unknown as MatrixClient;
|
} as unknown as MatrixClient;
|
||||||
|
|
||||||
client.reEmitter = new ReEmitter(client);
|
client.reEmitter = new ReEmitter(client);
|
||||||
|
@@ -608,6 +608,7 @@ describe("RoomView", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("message search", () => {
|
||||||
it("should close search results when edit is clicked", async () => {
|
it("should close search results when edit is clicked", async () => {
|
||||||
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Join);
|
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Join);
|
||||||
|
|
||||||
@@ -733,6 +734,24 @@ describe("RoomView", () => {
|
|||||||
await expect(prom).resolves.toEqual(expect.objectContaining({ room_id: room2.roomId }));
|
await expect(prom).resolves.toEqual(expect.objectContaining({ room_id: room2.roomId }));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should pre-fill search field on FocusMessageSearch dispatch", async () => {
|
||||||
|
room.getMyMembership = jest.fn().mockReturnValue(KnownMembership.Join);
|
||||||
|
|
||||||
|
const roomViewRef = createRef<RoomView>();
|
||||||
|
const { findByPlaceholderText } = await mountRoomView(roomViewRef);
|
||||||
|
await waitFor(() => expect(roomViewRef.current).toBeTruthy());
|
||||||
|
|
||||||
|
act(() =>
|
||||||
|
defaultDispatcher.dispatch({
|
||||||
|
action: Action.FocusMessageSearch,
|
||||||
|
initialText: "search term",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(findByPlaceholderText("Search messages…")).resolves.toHaveValue("search term");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("fires Action.RoomLoaded", async () => {
|
it("fires Action.RoomLoaded", async () => {
|
||||||
jest.spyOn(defaultDispatcher, "dispatch");
|
jest.spyOn(defaultDispatcher, "dispatch");
|
||||||
await mountRoomView();
|
await mountRoomView();
|
||||||
|
@@ -353,12 +353,12 @@ describe("Spotlight Dialog", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should find Rooms", () => {
|
it("should find Rooms", () => {
|
||||||
expect(options).toHaveLength(4);
|
expect(options).toHaveLength(5);
|
||||||
expect(options[0]!.innerHTML).toContain(testRoom.name);
|
expect(options[0]!.innerHTML).toContain(testRoom.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not find LocalRooms", () => {
|
it("should not find LocalRooms", () => {
|
||||||
expect(options).toHaveLength(4);
|
expect(options).toHaveLength(5);
|
||||||
expect(options[0]!.innerHTML).not.toContain(testLocalRoom.name);
|
expect(options[0]!.innerHTML).not.toContain(testLocalRoom.name);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -648,4 +648,20 @@ describe("Spotlight Dialog", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should allow jumping into message search", async () => {
|
||||||
|
const onFinished = jest.fn();
|
||||||
|
render(<SpotlightDialog initialText="search term" onFinished={onFinished} />);
|
||||||
|
jest.advanceTimersByTime(200);
|
||||||
|
await flushPromisesWithFakeTimers();
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByText("Messages"));
|
||||||
|
|
||||||
|
expect(defaultDispatcher.dispatch).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
action: Action.FocusMessageSearch,
|
||||||
|
initialText: "search term",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -11,7 +11,6 @@ import { render, fireEvent, screen, waitFor } from "jest-matrix-react";
|
|||||||
import { EventType, MatrixEvent, Room, type MatrixClient, JoinRule } from "matrix-js-sdk/src/matrix";
|
import { EventType, MatrixEvent, Room, type MatrixClient, JoinRule } from "matrix-js-sdk/src/matrix";
|
||||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||||
import { mocked, type MockedObject } from "jest-mock";
|
import { mocked, type MockedObject } from "jest-mock";
|
||||||
import userEvent from "@testing-library/user-event";
|
|
||||||
|
|
||||||
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
import DMRoomMap from "../../../../../src/utils/DMRoomMap";
|
||||||
import RoomSummaryCard from "../../../../../src/components/views/right_panel/RoomSummaryCard";
|
import RoomSummaryCard from "../../../../../src/components/views/right_panel/RoomSummaryCard";
|
||||||
@@ -30,8 +29,6 @@ import { _t } from "../../../../../src/languageHandler";
|
|||||||
import { tagRoom } from "../../../../../src/utils/room/tagRoom";
|
import { tagRoom } from "../../../../../src/utils/room/tagRoom";
|
||||||
import { DefaultTagID } from "../../../../../src/stores/room-list/models";
|
import { DefaultTagID } from "../../../../../src/stores/room-list/models";
|
||||||
import { Action } from "../../../../../src/dispatcher/actions";
|
import { Action } from "../../../../../src/dispatcher/actions";
|
||||||
import { TimelineRenderingType } from "../../../../../src/contexts/RoomContext";
|
|
||||||
import { ScopedRoomContextProvider } from "../../../../../src/contexts/ScopedRoomContext.tsx";
|
|
||||||
import { ReportRoomDialog } from "../../../../../src/components/views/dialogs/ReportRoomDialog.tsx";
|
import { ReportRoomDialog } from "../../../../../src/components/views/dialogs/ReportRoomDialog.tsx";
|
||||||
|
|
||||||
jest.mock("../../../../../src/utils/room/tagRoom");
|
jest.mock("../../../../../src/utils/room/tagRoom");
|
||||||
@@ -169,38 +166,6 @@ describe("<RoomSummaryCard />", () => {
|
|||||||
fireEvent.keyDown(getByPlaceholderText("Search messages…"), { key: "Escape" });
|
fireEvent.keyDown(getByPlaceholderText("Search messages…"), { key: "Escape" });
|
||||||
expect(onSearchCancel).toHaveBeenCalled();
|
expect(onSearchCancel).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should empty search field when the timeline rendering type changes away", async () => {
|
|
||||||
const onSearchChange = jest.fn();
|
|
||||||
const { rerender } = render(
|
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
|
||||||
<ScopedRoomContextProvider {...({ timelineRenderingType: TimelineRenderingType.Search } as any)}>
|
|
||||||
<RoomSummaryCard
|
|
||||||
room={room}
|
|
||||||
permalinkCreator={new RoomPermalinkCreator(room)}
|
|
||||||
onSearchChange={onSearchChange}
|
|
||||||
focusRoomSearch={true}
|
|
||||||
/>
|
|
||||||
</ScopedRoomContextProvider>
|
|
||||||
</MatrixClientContext.Provider>,
|
|
||||||
);
|
|
||||||
|
|
||||||
await userEvent.type(screen.getByPlaceholderText("Search messages…"), "test");
|
|
||||||
expect(screen.getByPlaceholderText("Search messages…")).toHaveValue("test");
|
|
||||||
|
|
||||||
rerender(
|
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
|
||||||
<ScopedRoomContextProvider {...({ timelineRenderingType: TimelineRenderingType.Room } as any)}>
|
|
||||||
<RoomSummaryCard
|
|
||||||
room={room}
|
|
||||||
permalinkCreator={new RoomPermalinkCreator(room)}
|
|
||||||
onSearchChange={onSearchChange}
|
|
||||||
/>
|
|
||||||
</ScopedRoomContextProvider>
|
|
||||||
</MatrixClientContext.Provider>,
|
|
||||||
);
|
|
||||||
expect(screen.getByPlaceholderText("Search messages…")).toHaveValue("");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("opens room file panel on button click", () => {
|
it("opens room file panel on button click", () => {
|
||||||
|
Reference in New Issue
Block a user