Compare commits
9 Commits
midhun/rls
...
hs/fix-err
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9623639b08 | ||
|
|
976a8f44bc | ||
|
|
9cb55970d3 | ||
|
|
d82029d0c1 | ||
|
|
c1df3d1511 | ||
|
|
f9a85d37fa | ||
|
|
2abd5342c2 | ||
|
|
85f80b1d0a | ||
|
|
4b9382f888 |
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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 Page } from "@playwright/test";
|
||||
|
||||
import { test, expect } from "../../../element-web-test";
|
||||
|
||||
test.describe("Search section of the room list", () => {
|
||||
test.use({
|
||||
labsFlags: ["feature_new_room_list"],
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the search section of the room list
|
||||
* @param page
|
||||
*/
|
||||
function getSearchSection(page: Page) {
|
||||
return page.getByRole("search");
|
||||
}
|
||||
|
||||
test.beforeEach(async ({ page, app, user }) => {
|
||||
// The notification toast is displayed above the search section
|
||||
await app.closeNotificationToast();
|
||||
});
|
||||
|
||||
test("should render the search section", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
||||
const searchSection = getSearchSection(page);
|
||||
// exact=false to ignore the shortcut which is related to the OS
|
||||
await expect(searchSection.getByRole("button", { name: "Search", exact: false })).toBeVisible();
|
||||
await expect(searchSection).toMatchScreenshot("search-section.png");
|
||||
});
|
||||
|
||||
test("should open the spotlight when the search button is clicked", async ({ page, app, user }) => {
|
||||
const searchSection = getSearchSection(page);
|
||||
await searchSection.getByRole("button", { name: "Search", exact: false }).click();
|
||||
// The spotlight should be displayed
|
||||
await expect(page.getByRole("dialog", { name: "Search Dialog" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("should open the room directory when the search button is clicked", async ({ page, app, user }) => {
|
||||
const searchSection = getSearchSection(page);
|
||||
await searchSection.getByRole("button", { name: "Explore rooms" }).click();
|
||||
const dialog = page.getByRole("dialog", { name: "Search Dialog" });
|
||||
// The room directory should be displayed
|
||||
await expect(dialog).toBeVisible();
|
||||
// The public room filter should be displayed
|
||||
await expect(dialog.getByText("Public rooms")).toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 Page } from "@playwright/test";
|
||||
|
||||
import { test, expect } from "../../../element-web-test";
|
||||
|
||||
test.describe("Search section of the room list", () => {
|
||||
test.use({
|
||||
labsFlags: ["feature_new_room_list"],
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the room list view
|
||||
* @param page
|
||||
*/
|
||||
function getRoomListView(page: Page) {
|
||||
return page.getByTestId("room-list-view");
|
||||
}
|
||||
|
||||
test.beforeEach(async ({ page, app, user }) => {
|
||||
// The notification toast is displayed above the search section
|
||||
await app.closeNotificationToast();
|
||||
});
|
||||
|
||||
test("should render the room list view", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
||||
const roomListView = getRoomListView(page);
|
||||
await expect(roomListView).toMatchScreenshot("room-list-view.png");
|
||||
});
|
||||
});
|
||||
@@ -25,13 +25,9 @@ test.describe("Security user settings tab", () => {
|
||||
},
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ page, user }) => {
|
||||
test.beforeEach(async ({ page, app, user }) => {
|
||||
// Dismiss "Notification" toast
|
||||
await page
|
||||
.locator(".mx_Toast_toast", { hasText: "Notifications" })
|
||||
.getByRole("button", { name: "Dismiss" })
|
||||
.click();
|
||||
|
||||
await app.closeNotificationToast();
|
||||
await page.locator(".mx_Toast_buttons").getByRole("button", { name: "Yes" }).click(); // Allow analytics
|
||||
});
|
||||
|
||||
|
||||
@@ -202,4 +202,15 @@ export class ElementAppPage {
|
||||
}
|
||||
return this.page.locator(`id=${labelledById ?? describedById}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the notification toast
|
||||
*/
|
||||
public closeNotificationToast(): Promise<void> {
|
||||
// Dismiss "Notification" toast
|
||||
return this.page
|
||||
.locator(".mx_Toast_toast", { hasText: "Notifications" })
|
||||
.getByRole("button", { name: "Dismiss" })
|
||||
.click();
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
@@ -25,7 +25,7 @@ import { type HomeserverContainer, type StartedHomeserverContainer } from "./Hom
|
||||
import { type StartedMatrixAuthenticationServiceContainer } from "./mas.ts";
|
||||
import { Api, ClientServerApi, type Verb } from "../plugins/utils/api.ts";
|
||||
|
||||
const TAG = "develop@sha256:56456456f52cb3b9d23a3e5e889e5a2e908784f0459d4bf759835be87e7e5888";
|
||||
const TAG = "develop@sha256:dfacd4d40994c77eb478fc5773913a38fbf07d593421a5410c5dafb8330ddd13";
|
||||
|
||||
const DEFAULT_CONFIG = {
|
||||
server_name: "localhost",
|
||||
|
||||
@@ -269,6 +269,8 @@
|
||||
@import "./views/right_panel/_VerificationPanel.pcss";
|
||||
@import "./views/right_panel/_WidgetCard.pcss";
|
||||
@import "./views/room_settings/_AliasSettings.pcss";
|
||||
@import "./views/rooms/RoomListView/_RoomListSearch.pcss";
|
||||
@import "./views/rooms/RoomListView/_RoomListView.pcss";
|
||||
@import "./views/rooms/_AppsDrawer.pcss";
|
||||
@import "./views/rooms/_Autocomplete.pcss";
|
||||
@import "./views/rooms/_AuxPanel.pcss";
|
||||
|
||||
@@ -104,6 +104,12 @@ Please see LICENSE files in the repository root for full details.
|
||||
}
|
||||
}
|
||||
|
||||
.mx_QuickSettingsButton_ContextMenuWrapper_new_room_list {
|
||||
.mx_QuickThemeSwitcher {
|
||||
margin-top: var(--cpd-space-2x);
|
||||
}
|
||||
}
|
||||
|
||||
.mx_QuickSettingsButton_icon {
|
||||
// TODO remove when all icons have fill=currentColor
|
||||
* {
|
||||
|
||||
39
res/css/views/rooms/RoomListView/_RoomListSearch.pcss
Normal file
39
res/css/views/rooms/RoomListView/_RoomListSearch.pcss
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.mx_RoomListSearch {
|
||||
/* From figma, this should be aligned with the room header */
|
||||
height: 64px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid var(--cpd-color-bg-subtle-primary);
|
||||
padding: 0 var(--cpd-space-3x);
|
||||
|
||||
svg {
|
||||
fill: var(--cpd-color-icon-secondary);
|
||||
}
|
||||
|
||||
.mx_RoomListSearch_search {
|
||||
/* The search button should take all the remaining space */
|
||||
flex: 1;
|
||||
font: var(--cpd-font-body-md-regular);
|
||||
color: var(--cpd-color-text-secondary);
|
||||
|
||||
span {
|
||||
flex: 1;
|
||||
|
||||
kbd {
|
||||
font-family: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx_RoomListSearch_explore:hover {
|
||||
svg {
|
||||
fill: var(--cpd-color-icon-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
12
res/css/views/rooms/RoomListView/_RoomListView.pcss
Normal file
12
res/css/views/rooms/RoomListView/_RoomListView.pcss
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.mx_RoomListView {
|
||||
background-color: var(--cpd-color-bg-canvas-default);
|
||||
height: 100%;
|
||||
border-right: 1px solid var(--cpd-color-bg-subtle-primary);
|
||||
}
|
||||
@@ -8,6 +8,6 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
.mx_Field.mx_AppearanceUserSettingsTab_checkboxControlledField {
|
||||
width: 256px;
|
||||
/* matches checkbox box + padding to align with checkbox label */
|
||||
margin-inline-start: calc($font-16px + 10px);
|
||||
/* Line up with Settings field toggle button */
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||
return (
|
||||
<div className={containerClasses}>
|
||||
<div className="mx_LeftPanel_roomListContainer">
|
||||
<RoomListView />
|
||||
<RoomListView activeSpace={this.state.activeSpace} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -501,9 +501,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||
handled = true;
|
||||
break;
|
||||
case KeyBindingAction.FilterRooms:
|
||||
dis.dispatch({
|
||||
action: "focus_room_filter",
|
||||
});
|
||||
dis.fire(Action.OpenSpotlight);
|
||||
handled = true;
|
||||
break;
|
||||
case KeyBindingAction.ToggleUserMenu:
|
||||
|
||||
@@ -11,7 +11,6 @@ import * as React from "react";
|
||||
|
||||
import { ALTERNATE_KEY_NAME } from "../../accessibility/KeyboardShortcuts";
|
||||
import defaultDispatcher from "../../dispatcher/dispatcher";
|
||||
import { type ActionPayload } from "../../dispatcher/payloads";
|
||||
import { IS_MAC, Key } from "../../Keyboard";
|
||||
import { _t } from "../../languageHandler";
|
||||
import AccessibleButton from "../views/elements/AccessibleButton";
|
||||
@@ -22,26 +21,10 @@ interface IProps {
|
||||
}
|
||||
|
||||
export default class RoomSearch extends React.PureComponent<IProps> {
|
||||
private dispatcherRef?: string;
|
||||
|
||||
public componentDidMount(): void {
|
||||
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
defaultDispatcher.unregister(this.dispatcherRef);
|
||||
}
|
||||
|
||||
private openSpotlight(): void {
|
||||
defaultDispatcher.fire(Action.OpenSpotlight);
|
||||
}
|
||||
|
||||
private onAction = (payload: ActionPayload): void => {
|
||||
if (payload.action === "focus_room_filter") {
|
||||
this.openSpotlight();
|
||||
}
|
||||
};
|
||||
|
||||
public render(): React.ReactNode {
|
||||
const classes = classNames(
|
||||
{
|
||||
|
||||
@@ -60,8 +60,6 @@ interface IProps {
|
||||
// If specified, contents will appear as a tooltip on the element and
|
||||
// validation feedback tooltips will be suppressed.
|
||||
tooltipContent?: JSX.Element | string;
|
||||
// If specified the tooltip will be shown regardless of feedback
|
||||
forceTooltipVisible?: boolean;
|
||||
// If specified, the tooltip with be aligned accorindly with the field, defaults to Right.
|
||||
tooltipAlignment?: ComponentProps<typeof Tooltip>["placement"];
|
||||
// If specified alongside tooltipContent, the class name to apply to the
|
||||
@@ -274,7 +272,6 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
|
||||
validateOnChange,
|
||||
validateOnFocus,
|
||||
usePlaceholderAsHint,
|
||||
forceTooltipVisible,
|
||||
tooltipAlignment,
|
||||
...inputProps
|
||||
} = this.props;
|
||||
@@ -283,8 +280,7 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
|
||||
const tooltipProps: Pick<React.ComponentProps<typeof Tooltip>, "aria-live" | "aria-atomic"> = {};
|
||||
let tooltipOpen = false;
|
||||
if (tooltipContent || this.state.feedback) {
|
||||
tooltipOpen = (this.state.focused && forceTooltipVisible) || this.state.feedbackVisible;
|
||||
|
||||
tooltipOpen = this.state.feedbackVisible;
|
||||
if (!tooltipContent) {
|
||||
tooltipProps["aria-atomic"] = "true";
|
||||
tooltipProps["aria-live"] = this.state.valid ? "polite" : "assertive";
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
/*
|
||||
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 React from "react";
|
||||
|
||||
type IProps = unknown;
|
||||
|
||||
export const RoomListView: React.FC<IProps> = (props: IProps) => {
|
||||
return <div>New Room List</div>;
|
||||
};
|
||||
69
src/components/views/rooms/RoomListView/RoomListSearch.tsx
Normal file
69
src/components/views/rooms/RoomListView/RoomListSearch.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 React, { type JSX } from "react";
|
||||
import { Button } from "@vector-im/compound-web";
|
||||
import ExploreIcon from "@vector-im/compound-design-tokens/assets/web/icons/explore";
|
||||
import SearchIcon from "@vector-im/compound-design-tokens/assets/web/icons/search";
|
||||
|
||||
import { IS_MAC, Key } from "../../../../Keyboard";
|
||||
import { _t } from "../../../../languageHandler";
|
||||
import { ALTERNATE_KEY_NAME } from "../../../../accessibility/KeyboardShortcuts";
|
||||
import { shouldShowComponent } from "../../../../customisations/helpers/UIComponents";
|
||||
import { UIComponent } from "../../../../settings/UIFeature";
|
||||
import { MetaSpace } from "../../../../stores/spaces";
|
||||
import { Action } from "../../../../dispatcher/actions";
|
||||
import PosthogTrackers from "../../../../PosthogTrackers";
|
||||
import defaultDispatcher from "../../../../dispatcher/dispatcher";
|
||||
import { Flex } from "../../../utils/Flex";
|
||||
|
||||
type RoomListSearchProps = {
|
||||
/**
|
||||
* Current active space
|
||||
* The explore button is only displayed in the Home meta space
|
||||
*/
|
||||
activeSpace: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* A search component to be displayed at the top of the room list
|
||||
* The `Explore` button is displayed only in the Home meta space and when UIComponent.ExploreRooms is enabled.
|
||||
*/
|
||||
export function RoomListSearch({ activeSpace }: RoomListSearchProps): JSX.Element {
|
||||
const displayExploreButton = activeSpace === MetaSpace.Home && shouldShowComponent(UIComponent.ExploreRooms);
|
||||
|
||||
return (
|
||||
<Flex className="mx_RoomListSearch" role="search" gap="var(--cpd-space-2x)" align="center">
|
||||
<Button
|
||||
className="mx_RoomListSearch_search"
|
||||
kind="secondary"
|
||||
size="sm"
|
||||
Icon={SearchIcon}
|
||||
onClick={() => defaultDispatcher.fire(Action.OpenSpotlight)}
|
||||
>
|
||||
<Flex as="span" justify="space-between">
|
||||
{_t("action|search")}
|
||||
<kbd>{IS_MAC ? "⌘ K" : _t(ALTERNATE_KEY_NAME[Key.CONTROL]) + " K"}</kbd>
|
||||
</Flex>
|
||||
</Button>
|
||||
{displayExploreButton && (
|
||||
<Button
|
||||
className="mx_RoomListSearch_explore"
|
||||
kind="secondary"
|
||||
size="sm"
|
||||
Icon={ExploreIcon}
|
||||
iconOnly={true}
|
||||
aria-label={_t("action|explore_rooms")}
|
||||
onClick={(ev) => {
|
||||
defaultDispatcher.fire(Action.ViewRoomDirectory);
|
||||
PosthogTrackers.trackInteraction("WebLeftPanelExploreRoomsButton", ev);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
33
src/components/views/rooms/RoomListView/RoomListView.tsx
Normal file
33
src/components/views/rooms/RoomListView/RoomListView.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
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 React from "react";
|
||||
|
||||
import { shouldShowComponent } from "../../../../customisations/helpers/UIComponents";
|
||||
import { UIComponent } from "../../../../settings/UIFeature";
|
||||
import { RoomListSearch } from "./RoomListSearch";
|
||||
|
||||
type RoomListViewProps = {
|
||||
/**
|
||||
* Current active space
|
||||
* See {@link RoomListSearch}
|
||||
*/
|
||||
activeSpace: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* A view component for the room list.
|
||||
*/
|
||||
export const RoomListView: React.FC<RoomListViewProps> = ({ activeSpace }) => {
|
||||
const displayRoomSearch = shouldShowComponent(UIComponent.FilterContainer);
|
||||
|
||||
return (
|
||||
<div className="mx_RoomListView" data-testid="room-list-view">
|
||||
{displayRoomSearch && <RoomListSearch activeSpace={activeSpace} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
8
src/components/views/rooms/RoomListView/index.ts
Normal file
8
src/components/views/rooms/RoomListView/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { RoomListView } from "./RoomListView";
|
||||
@@ -9,6 +9,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
import React, { type ReactNode } from "react";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { type IThreepid } from "matrix-js-sdk/src/matrix";
|
||||
import { EditInPlace, ErrorMessage, TooltipProvider } from "@vector-im/compound-web";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
@@ -22,7 +23,6 @@ import { timeout } from "../../../utils/promise";
|
||||
import { type ActionPayload } from "../../../dispatcher/payloads";
|
||||
import InlineSpinner from "../elements/InlineSpinner";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import Field from "../elements/Field";
|
||||
import QuestionDialog from "../dialogs/QuestionDialog";
|
||||
import SettingsFieldset from "./SettingsFieldset";
|
||||
import { SettingsSubsectionText } from "./shared/SettingsSubsection";
|
||||
@@ -117,26 +117,7 @@ export default class SetIdServer extends React.Component<IProps, IState> {
|
||||
private onIdentityServerChanged = (ev: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const u = ev.target.value;
|
||||
|
||||
this.setState({ idServer: u });
|
||||
};
|
||||
|
||||
private getTooltip = (): JSX.Element | undefined => {
|
||||
if (this.state.checking) {
|
||||
return (
|
||||
<div>
|
||||
<InlineSpinner />
|
||||
{_t("identity_server|checking")}
|
||||
</div>
|
||||
);
|
||||
} else if (this.state.error) {
|
||||
return <strong className="warning">{this.state.error}</strong>;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
private idServerChangeEnabled = (): boolean => {
|
||||
return !!this.state.idServer && !this.state.busy;
|
||||
this.setState({ idServer: u, error: undefined });
|
||||
};
|
||||
|
||||
private saveIdServer = (fullUrl: string): void => {
|
||||
@@ -175,7 +156,7 @@ export default class SetIdServer extends React.Component<IProps, IState> {
|
||||
// Double check that the identity server even has terms of service.
|
||||
const hasTerms = await doesIdentityServerHaveTerms(MatrixClientPeg.safeGet(), fullUrl);
|
||||
if (!hasTerms) {
|
||||
const [confirmed] = await this.showNoTermsWarning(fullUrl);
|
||||
const [confirmed] = await this.showNoTermsWarning();
|
||||
save = !!confirmed;
|
||||
}
|
||||
|
||||
@@ -213,7 +194,7 @@ export default class SetIdServer extends React.Component<IProps, IState> {
|
||||
});
|
||||
};
|
||||
|
||||
private showNoTermsWarning(fullUrl: string): Promise<[ok?: boolean]> {
|
||||
private showNoTermsWarning(): Promise<[ok?: boolean]> {
|
||||
const { finished } = Modal.createDialog(QuestionDialog, {
|
||||
title: _t("terms|identity_server_no_terms_title"),
|
||||
description: (
|
||||
@@ -393,28 +374,27 @@ export default class SetIdServer extends React.Component<IProps, IState> {
|
||||
|
||||
return (
|
||||
<SettingsFieldset legend={sectionTitle} description={bodyText}>
|
||||
<form className="mx_SetIdServer" onSubmit={this.checkIdServer}>
|
||||
<Field
|
||||
<TooltipProvider>
|
||||
<EditInPlace
|
||||
cancelButtonLabel={_t("action|reset")}
|
||||
label={_t("identity_server|url_field_label")}
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
onChange={this.onIdentityServerChanged}
|
||||
onCancel={() => this.setState((s) => ({ idServer: s.currentClientIdServer ?? "" }))}
|
||||
onClearServerErrors={() => this.setState({ error: undefined })}
|
||||
onSave={this.checkIdServer}
|
||||
size={48}
|
||||
saveButtonLabel={_t("action|change")}
|
||||
savedLabel={this.state.error ? undefined : _t("identity_server|changed")}
|
||||
savingLabel={_t("identity_server|checking")}
|
||||
placeholder={this.state.defaultIdServer}
|
||||
value={this.state.idServer}
|
||||
onChange={this.onIdentityServerChanged}
|
||||
tooltipContent={this.getTooltip()}
|
||||
tooltipClassName="mx_SetIdServer_tooltip"
|
||||
disabled={this.state.busy}
|
||||
forceValidity={this.state.error ? false : undefined}
|
||||
/>
|
||||
<AccessibleButton
|
||||
kind="primary_sm"
|
||||
onClick={this.checkIdServer}
|
||||
disabled={!this.idServerChangeEnabled()}
|
||||
serverInvalid={!!this.state.error}
|
||||
>
|
||||
{_t("action|change")}
|
||||
</AccessibleButton>
|
||||
{this.state.error && <ErrorMessage>{this.state.error}</ErrorMessage>}
|
||||
</EditInPlace>
|
||||
{discoSection}
|
||||
</form>
|
||||
</TooltipProvider>
|
||||
</SettingsFieldset>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import React, { type ChangeEvent, type ReactNode } from "react";
|
||||
import { type EmptyObject } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { _t } from "../../../../../languageHandler";
|
||||
import SdkConfig from "../../../../../SdkConfig";
|
||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||
import SettingsFlag from "../../../elements/SettingsFlag";
|
||||
import Field from "../../../elements/Field";
|
||||
@@ -48,7 +47,6 @@ export default class AppearanceUserSettingsTab extends React.Component<EmptyObje
|
||||
private renderAdvancedSection(): ReactNode {
|
||||
if (!SettingsStore.getValue(UIFeature.AdvancedSettings)) return null;
|
||||
|
||||
const brand = SdkConfig.get().brand;
|
||||
const toggle = (
|
||||
<AccessibleButton
|
||||
kind="link"
|
||||
@@ -62,21 +60,18 @@ export default class AppearanceUserSettingsTab extends React.Component<EmptyObje
|
||||
let advanced: React.ReactNode;
|
||||
|
||||
if (this.state.showAdvanced) {
|
||||
const tooltipContent = _t("settings|appearance|custom_font_description", { brand });
|
||||
advanced = (
|
||||
<>
|
||||
<SettingsFlag name="useCompactLayout" level={SettingLevel.DEVICE} useCheckbox={true} />
|
||||
<SettingsFlag name="useCompactLayout" level={SettingLevel.DEVICE} />
|
||||
|
||||
<SettingsFlag
|
||||
name="useBundledEmojiFont"
|
||||
level={SettingLevel.DEVICE}
|
||||
useCheckbox={true}
|
||||
onChange={(checked) => this.setState({ useBundledEmojiFont: checked })}
|
||||
/>
|
||||
<SettingsFlag
|
||||
name="useSystemFont"
|
||||
level={SettingLevel.DEVICE}
|
||||
useCheckbox={true}
|
||||
onChange={(checked) => this.setState({ useSystemFont: checked })}
|
||||
/>
|
||||
<Field
|
||||
@@ -89,8 +84,6 @@ export default class AppearanceUserSettingsTab extends React.Component<EmptyObje
|
||||
|
||||
SettingsStore.setValue("systemFont", null, SettingLevel.DEVICE, value.target.value);
|
||||
}}
|
||||
tooltipContent={tooltipContent}
|
||||
forceTooltipVisible={true}
|
||||
disabled={!this.state.useSystemFont}
|
||||
value={this.state.systemFont}
|
||||
/>
|
||||
|
||||
@@ -72,6 +72,9 @@ const SidebarUserSettingsTab: React.FC = () => {
|
||||
PosthogTrackers.trackInteraction("WebSettingsSidebarTabSpacesCheckbox", event, 1);
|
||||
};
|
||||
|
||||
// "Favourites" and "People" meta spaces are not available in the new room list
|
||||
const newRoomListEnabled = useSettingValue("feature_new_room_list");
|
||||
|
||||
return (
|
||||
<SettingsTab>
|
||||
<SettingsSection>
|
||||
@@ -109,33 +112,43 @@ const SidebarUserSettingsTab: React.FC = () => {
|
||||
</SettingsSubsectionText>
|
||||
</StyledCheckbox>
|
||||
|
||||
<StyledCheckbox
|
||||
checked={!!favouritesEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(MetaSpace.Favourites, "WebSettingsSidebarTabSpacesCheckbox")}
|
||||
className="mx_SidebarUserSettingsTab_checkbox"
|
||||
>
|
||||
<SettingsSubsectionText>
|
||||
<FavouriteSolidIcon />
|
||||
{_t("common|favourites")}
|
||||
</SettingsSubsectionText>
|
||||
<SettingsSubsectionText>
|
||||
{_t("settings|sidebar|metaspaces_favourites_description")}
|
||||
</SettingsSubsectionText>
|
||||
</StyledCheckbox>
|
||||
{!newRoomListEnabled && (
|
||||
<>
|
||||
<StyledCheckbox
|
||||
checked={!!favouritesEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(
|
||||
MetaSpace.Favourites,
|
||||
"WebSettingsSidebarTabSpacesCheckbox",
|
||||
)}
|
||||
className="mx_SidebarUserSettingsTab_checkbox"
|
||||
>
|
||||
<SettingsSubsectionText>
|
||||
<FavouriteSolidIcon />
|
||||
{_t("common|favourites")}
|
||||
</SettingsSubsectionText>
|
||||
<SettingsSubsectionText>
|
||||
{_t("settings|sidebar|metaspaces_favourites_description")}
|
||||
</SettingsSubsectionText>
|
||||
</StyledCheckbox>
|
||||
|
||||
<StyledCheckbox
|
||||
checked={!!peopleEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(MetaSpace.People, "WebSettingsSidebarTabSpacesCheckbox")}
|
||||
className="mx_SidebarUserSettingsTab_checkbox"
|
||||
>
|
||||
<SettingsSubsectionText>
|
||||
<UserProfileSolidIcon />
|
||||
{_t("common|people")}
|
||||
</SettingsSubsectionText>
|
||||
<SettingsSubsectionText>
|
||||
{_t("settings|sidebar|metaspaces_people_description")}
|
||||
</SettingsSubsectionText>
|
||||
</StyledCheckbox>
|
||||
<StyledCheckbox
|
||||
checked={!!peopleEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(
|
||||
MetaSpace.People,
|
||||
"WebSettingsSidebarTabSpacesCheckbox",
|
||||
)}
|
||||
className="mx_SidebarUserSettingsTab_checkbox"
|
||||
>
|
||||
<SettingsSubsectionText>
|
||||
<UserProfileSolidIcon />
|
||||
{_t("common|people")}
|
||||
</SettingsSubsectionText>
|
||||
<SettingsSubsectionText>
|
||||
{_t("settings|sidebar|metaspaces_people_description")}
|
||||
</SettingsSubsectionText>
|
||||
</StyledCheckbox>
|
||||
</>
|
||||
)}
|
||||
|
||||
<StyledCheckbox
|
||||
checked={!!orphansEnabled}
|
||||
|
||||
@@ -40,13 +40,17 @@ const QuickSettingsButton: React.FC<{
|
||||
|
||||
const currentRoomId = SdkContextClass.instance.roomViewStore.getRoomId();
|
||||
const developerModeEnabled = useSettingValue("developerMode");
|
||||
// "Favourites" and "People" meta spaces are not available in the new room list
|
||||
const newRoomListEnabled = useSettingValue("feature_new_room_list");
|
||||
|
||||
let contextMenu: JSX.Element | undefined;
|
||||
if (menuDisplayed && handle.current) {
|
||||
contextMenu = (
|
||||
<ContextMenu
|
||||
{...alwaysAboveRightOf(handle.current.getBoundingClientRect(), ChevronFace.None, 16)}
|
||||
wrapperClassName="mx_QuickSettingsButton_ContextMenuWrapper"
|
||||
wrapperClassName={classNames("mx_QuickSettingsButton_ContextMenuWrapper", {
|
||||
mx_QuickSettingsButton_ContextMenuWrapper_new_room_list: newRoomListEnabled,
|
||||
})}
|
||||
onFinished={closeMenu}
|
||||
managed={false}
|
||||
focusLock={true}
|
||||
@@ -81,41 +85,50 @@ const QuickSettingsButton: React.FC<{
|
||||
</AccessibleButton>
|
||||
)}
|
||||
|
||||
<h4 className="mx_QuickSettingsButton_pinToSidebarHeading">
|
||||
<PinUprightIcon className="mx_QuickSettingsButton_icon" />
|
||||
{_t("quick_settings|metaspace_section")}
|
||||
</h4>
|
||||
|
||||
<StyledCheckbox
|
||||
className="mx_QuickSettingsButton_favouritesCheckbox"
|
||||
checked={!!favouritesEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(MetaSpace.Favourites, "WebQuickSettingsPinToSidebarCheckbox")}
|
||||
>
|
||||
<FavouriteSolidIcon className="mx_QuickSettingsButton_icon" />
|
||||
{_t("common|favourites")}
|
||||
</StyledCheckbox>
|
||||
<StyledCheckbox
|
||||
className="mx_QuickSettingsButton_peopleCheckbox"
|
||||
checked={!!peopleEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(MetaSpace.People, "WebQuickSettingsPinToSidebarCheckbox")}
|
||||
>
|
||||
<UserProfileSolidIcon className="mx_QuickSettingsButton_icon" />
|
||||
{_t("common|people")}
|
||||
</StyledCheckbox>
|
||||
<AccessibleButton
|
||||
className="mx_QuickSettingsButton_moreOptionsButton"
|
||||
onClick={() => {
|
||||
closeMenu();
|
||||
defaultDispatcher.dispatch({
|
||||
action: Action.ViewUserSettings,
|
||||
initialTabId: UserTab.Sidebar,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<OverflowHorizontalIcon className="mx_QuickSettingsButton_icon" />
|
||||
{_t("quick_settings|sidebar_settings")}
|
||||
</AccessibleButton>
|
||||
{!newRoomListEnabled && (
|
||||
<>
|
||||
<h4 className="mx_QuickSettingsButton_pinToSidebarHeading">
|
||||
<PinUprightIcon className="mx_QuickSettingsButton_icon" />
|
||||
{_t("quick_settings|metaspace_section")}
|
||||
</h4>
|
||||
|
||||
<StyledCheckbox
|
||||
className="mx_QuickSettingsButton_favouritesCheckbox"
|
||||
checked={!!favouritesEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(
|
||||
MetaSpace.Favourites,
|
||||
"WebQuickSettingsPinToSidebarCheckbox",
|
||||
)}
|
||||
>
|
||||
<FavouriteSolidIcon className="mx_QuickSettingsButton_icon" />
|
||||
{_t("common|favourites")}
|
||||
</StyledCheckbox>
|
||||
<StyledCheckbox
|
||||
className="mx_QuickSettingsButton_peopleCheckbox"
|
||||
checked={!!peopleEnabled}
|
||||
onChange={onMetaSpaceChangeFactory(
|
||||
MetaSpace.People,
|
||||
"WebQuickSettingsPinToSidebarCheckbox",
|
||||
)}
|
||||
>
|
||||
<UserProfileSolidIcon className="mx_QuickSettingsButton_icon" />
|
||||
{_t("common|people")}
|
||||
</StyledCheckbox>
|
||||
<AccessibleButton
|
||||
className="mx_QuickSettingsButton_moreOptionsButton"
|
||||
onClick={() => {
|
||||
closeMenu();
|
||||
defaultDispatcher.dispatch({
|
||||
action: Action.ViewUserSettings,
|
||||
initialTabId: UserTab.Sidebar,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<OverflowHorizontalIcon className="mx_QuickSettingsButton_icon" />
|
||||
{_t("quick_settings|sidebar_settings")}
|
||||
</AccessibleButton>
|
||||
</>
|
||||
)}
|
||||
<QuickThemeSwitcher requestClose={closeMenu} />
|
||||
</ContextMenu>
|
||||
);
|
||||
|
||||
@@ -1244,6 +1244,7 @@
|
||||
"change_prompt": "Disconnect from the identity server <current /> and connect to <new /> instead?",
|
||||
"change_server_prompt": "If you don't want to use <server /> to discover and be discoverable by existing contacts you know, enter another identity server below.",
|
||||
"checking": "Checking server",
|
||||
"changed": "Your identity server has been changed",
|
||||
"description_connected": "You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.",
|
||||
"description_disconnected": "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.",
|
||||
"description_optional": "Using an identity server is optional. If you choose not to use an identity server, you won't be discoverable by other users and you won't be able to invite others by email or phone.",
|
||||
|
||||
@@ -905,6 +905,10 @@ export const SETTINGS: Settings = {
|
||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
||||
default: false,
|
||||
displayName: _td("settings|appearance|custom_font"),
|
||||
description: () =>
|
||||
_t("settings|appearance|custom_font_description", {
|
||||
brand: SdkConfig.get().brand,
|
||||
}),
|
||||
controller: new SystemFontController(),
|
||||
},
|
||||
"systemFont": {
|
||||
|
||||
@@ -162,6 +162,20 @@ export class SpaceStoreClass extends AsyncStoreWithClient<EmptyObject> {
|
||||
SettingsStore.monitorSetting("feature_dynamic_room_predecessors", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the order of meta spaces to display in the space panel.
|
||||
*
|
||||
* This accessor should be removed when the "feature_new_room_list" labs flag is removed.
|
||||
* "People" and "Favourites" will be removed from the "metaSpaceOrder" array and this filter will no longer be needed.
|
||||
* @private
|
||||
*/
|
||||
private get metaSpaceOrder(): MetaSpace[] {
|
||||
if (!SettingsStore.getValue("feature_new_room_list")) return metaSpaceOrder;
|
||||
|
||||
// People and Favourites are not shown when the new room list is enabled
|
||||
return metaSpaceOrder.filter((space) => space !== MetaSpace.People && space !== MetaSpace.Favourites);
|
||||
}
|
||||
|
||||
public get invitedSpaces(): Room[] {
|
||||
return Array.from(this._invitedSpaces);
|
||||
}
|
||||
@@ -1164,7 +1178,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<EmptyObject> {
|
||||
|
||||
const oldMetaSpaces = this._enabledMetaSpaces;
|
||||
const enabledMetaSpaces = SettingsStore.getValue("Spaces.enabledMetaSpaces");
|
||||
this._enabledMetaSpaces = metaSpaceOrder.filter((k) => enabledMetaSpaces[k]);
|
||||
this._enabledMetaSpaces = this.metaSpaceOrder.filter((k) => enabledMetaSpaces[k]);
|
||||
|
||||
this._allRoomsInHome = SettingsStore.getValue("Spaces.allRoomsInHome");
|
||||
this.sendUserProperties();
|
||||
@@ -1278,7 +1292,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<EmptyObject> {
|
||||
|
||||
case "Spaces.enabledMetaSpaces": {
|
||||
const newValue = SettingsStore.getValue("Spaces.enabledMetaSpaces");
|
||||
const enabledMetaSpaces = metaSpaceOrder.filter((k) => newValue[k]);
|
||||
const enabledMetaSpaces = this.metaSpaceOrder.filter((k) => newValue[k]);
|
||||
if (arrayHasDiff(this._enabledMetaSpaces, enabledMetaSpaces)) {
|
||||
const hadPeopleOrHomeEnabled = this.enabledMetaSpaces.some((s) => {
|
||||
return s === MetaSpace.Home || s === MetaSpace.People;
|
||||
|
||||
@@ -421,6 +421,14 @@ describe("<LoggedInView />", () => {
|
||||
expect(defaultDispatcher.dispatch).not.toHaveBeenCalledWith({ action: Action.ViewHomePage });
|
||||
});
|
||||
|
||||
it("should open spotlight when Ctrl+k is fired", async () => {
|
||||
jest.spyOn(defaultDispatcher, "fire");
|
||||
|
||||
getComponent();
|
||||
await userEvent.keyboard("{Control>}k{/Control}");
|
||||
expect(defaultDispatcher.fire).toHaveBeenCalledWith(Action.OpenSpotlight);
|
||||
});
|
||||
|
||||
describe("timezone updates", () => {
|
||||
const userTimezone = "Europe/London";
|
||||
const originalController = SETTINGS["userTimezonePublish"].controller;
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 React from "react";
|
||||
import { render, screen } from "jest-matrix-react";
|
||||
import { mocked } from "jest-mock";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
||||
import { RoomListSearch } from "../../../../../../src/components/views/rooms/RoomListView/RoomListSearch";
|
||||
import { MetaSpace } from "../../../../../../src/stores/spaces";
|
||||
import { shouldShowComponent } from "../../../../../../src/customisations/helpers/UIComponents";
|
||||
import defaultDispatcher from "../../../../../../src/dispatcher/dispatcher";
|
||||
import { Action } from "../../../../../../src/dispatcher/actions";
|
||||
|
||||
jest.mock("../../../../../../src/customisations/helpers/UIComponents", () => ({
|
||||
shouldShowComponent: jest.fn(),
|
||||
}));
|
||||
|
||||
describe("<RoomListSearch />", () => {
|
||||
function renderComponent(activeSpace = MetaSpace.Home) {
|
||||
return render(<RoomListSearch activeSpace={activeSpace} />);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
// By default, we consider shouldShowComponent(UIComponent.ExploreRooms) should return true
|
||||
mocked(shouldShowComponent).mockReturnValue(true);
|
||||
});
|
||||
|
||||
it("should display all the buttons", () => {
|
||||
const { asFragment } = renderComponent();
|
||||
|
||||
// The search and explore buttons should be displayed
|
||||
expect(screen.getByRole("button", { name: "Search Ctrl K" })).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Explore rooms" })).toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should hide the explore button when the active space is not MetaSpace.Home", () => {
|
||||
const { asFragment } = renderComponent(MetaSpace.VideoRooms);
|
||||
|
||||
// The search button should be displayed but not the explore button
|
||||
expect(screen.getByRole("button", { name: "Search Ctrl K" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Explore rooms" })).not.toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should hide the explore button when UIComponent.ExploreRooms is disabled", () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(false);
|
||||
const { asFragment } = renderComponent();
|
||||
|
||||
// The search button should be displayed but not the explore button
|
||||
expect(screen.getByRole("button", { name: "Search Ctrl K" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Explore rooms" })).not.toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should open the spotlight when the search button is clicked", async () => {
|
||||
const fireSpy = jest.spyOn(defaultDispatcher, "fire");
|
||||
const user = userEvent.setup();
|
||||
renderComponent();
|
||||
|
||||
// Click on the search button
|
||||
await user.click(screen.getByRole("button", { name: "Search Ctrl K" }));
|
||||
|
||||
// The spotlight should be opened
|
||||
expect(fireSpy).toHaveBeenCalledWith(Action.OpenSpotlight);
|
||||
});
|
||||
|
||||
it("should open the room directory when the explore button is clicked", async () => {
|
||||
const fireSpy = jest.spyOn(defaultDispatcher, "fire");
|
||||
const user = userEvent.setup();
|
||||
renderComponent();
|
||||
|
||||
// Click on the search button
|
||||
await user.click(screen.getByRole("button", { name: "Explore rooms" }));
|
||||
|
||||
// The spotlight should be opened
|
||||
expect(fireSpy).toHaveBeenCalledWith(Action.ViewRoomDirectory);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 React from "react";
|
||||
import { render, screen } from "jest-matrix-react";
|
||||
import { mocked } from "jest-mock";
|
||||
|
||||
import { RoomListView } from "../../../../../../src/components/views/rooms/RoomListView";
|
||||
import { shouldShowComponent } from "../../../../../../src/customisations/helpers/UIComponents";
|
||||
import { MetaSpace } from "../../../../../../src/stores/spaces";
|
||||
|
||||
jest.mock("../../../../../../src/customisations/helpers/UIComponents", () => ({
|
||||
shouldShowComponent: jest.fn(),
|
||||
}));
|
||||
|
||||
describe("<RoomListView />", () => {
|
||||
function renderComponent() {
|
||||
return render(<RoomListView activeSpace={MetaSpace.Home} />);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
// By default, we consider shouldShowComponent(UIComponent.FilterContainer) should return true
|
||||
mocked(shouldShowComponent).mockReturnValue(true);
|
||||
});
|
||||
|
||||
it("should render the RoomListSearch component when UIComponent.FilterContainer is at true", () => {
|
||||
const { asFragment } = renderComponent();
|
||||
expect(screen.getByRole("button", { name: "Search Ctrl K" })).toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should not render the RoomListSearch component when UIComponent.FilterContainer is at false", () => {
|
||||
mocked(shouldShowComponent).mockReturnValue(false);
|
||||
const { asFragment } = renderComponent();
|
||||
|
||||
expect(screen.queryByRole("button", { name: "Search Ctrl K" })).toBeNull();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,142 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<RoomListSearch /> should display all the buttons 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_Flex mx_RoomListSearch"
|
||||
role="search"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
||||
>
|
||||
<button
|
||||
class="_button_i91xf_17 mx_RoomListSearch_search _has-icon_i91xf_66"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M15.05 16.463a7.5 7.5 0 1 1 1.414-1.414l3.243 3.244a1 1 0 0 1-1.414 1.414l-3.244-3.244ZM16 10.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0Z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="mx_Flex"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: space-between; --mx-flex-gap: 0;"
|
||||
>
|
||||
Search
|
||||
<kbd>
|
||||
Ctrl K
|
||||
</kbd>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Explore rooms"
|
||||
class="_button_i91xf_17 mx_RoomListSearch_explore _has-icon_i91xf_66 _icon-only_i91xf_59"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 13a.968.968 0 0 1-.713-.287A.968.968 0 0 1 11 12c0-.283.096-.52.287-.713A.968.968 0 0 1 12 11c.283 0 .52.096.713.287.191.192.287.43.287.713s-.096.52-.287.713A.968.968 0 0 1 12 13Zm0 9a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Zm0-2c2.233 0 4.125-.775 5.675-2.325C19.225 16.125 20 14.233 20 12c0-2.233-.775-4.125-2.325-5.675C16.125 4.775 14.233 4 12 4c-2.233 0-4.125.775-5.675 2.325C4.775 7.875 4 9.767 4 12c0 2.233.775 4.125 2.325 5.675C7.875 19.225 9.767 20 12 20Zm0 0c-2.233 0-4.125-.775-5.675-2.325C4.775 16.125 4 14.233 4 12c0-2.233.775-4.125 2.325-5.675C7.875 4.775 9.767 4 12 4c2.233 0 4.125.775 5.675 2.325C19.225 7.875 20 9.767 20 12c0 2.233-.775 4.125-2.325 5.675C16.125 19.225 14.233 20 12 20Zm1.675-5.85c.1-.05.192-.117.275-.2.083-.083.15-.175.2-.275l2.925-6.25c.083-.167.063-.313-.063-.438-.125-.125-.27-.145-.437-.062l-6.25 2.925c-.1.05-.192.117-.275.2-.083.083-.15.175-.2.275l-2.925 6.25c-.083.167-.063.313.063.438.124.124.27.145.437.062l6.25-2.925Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<RoomListSearch /> should hide the explore button when UIComponent.ExploreRooms is disabled 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_Flex mx_RoomListSearch"
|
||||
role="search"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
||||
>
|
||||
<button
|
||||
class="_button_i91xf_17 mx_RoomListSearch_search _has-icon_i91xf_66"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M15.05 16.463a7.5 7.5 0 1 1 1.414-1.414l3.243 3.244a1 1 0 0 1-1.414 1.414l-3.244-3.244ZM16 10.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0Z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="mx_Flex"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: space-between; --mx-flex-gap: 0;"
|
||||
>
|
||||
Search
|
||||
<kbd>
|
||||
Ctrl K
|
||||
</kbd>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<RoomListSearch /> should hide the explore button when the active space is not MetaSpace.Home 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_Flex mx_RoomListSearch"
|
||||
role="search"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
||||
>
|
||||
<button
|
||||
class="_button_i91xf_17 mx_RoomListSearch_search _has-icon_i91xf_66"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M15.05 16.463a7.5 7.5 0 1 1 1.414-1.414l3.243 3.244a1 1 0 0 1-1.414 1.414l-3.244-3.244ZM16 10.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0Z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="mx_Flex"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: space-between; --mx-flex-gap: 0;"
|
||||
>
|
||||
Search
|
||||
<kbd>
|
||||
Ctrl K
|
||||
</kbd>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
@@ -0,0 +1,76 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<RoomListView /> should not render the RoomListSearch component when UIComponent.FilterContainer is at false 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_RoomListView"
|
||||
data-testid="room-list-view"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<RoomListView /> should render the RoomListSearch component when UIComponent.FilterContainer is at true 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mx_RoomListView"
|
||||
data-testid="room-list-view"
|
||||
>
|
||||
<div
|
||||
class="mx_Flex mx_RoomListSearch"
|
||||
role="search"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-2x);"
|
||||
>
|
||||
<button
|
||||
class="_button_i91xf_17 mx_RoomListSearch_search _has-icon_i91xf_66"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M15.05 16.463a7.5 7.5 0 1 1 1.414-1.414l3.243 3.244a1 1 0 0 1-1.414 1.414l-3.244-3.244ZM16 10.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0Z"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="mx_Flex"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: space-between; --mx-flex-gap: 0;"
|
||||
>
|
||||
Search
|
||||
<kbd>
|
||||
Ctrl K
|
||||
</kbd>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Explore rooms"
|
||||
class="_button_i91xf_17 mx_RoomListSearch_explore _has-icon_i91xf_66 _icon-only_i91xf_59"
|
||||
data-kind="secondary"
|
||||
data-size="sm"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 13a.968.968 0 0 1-.713-.287A.968.968 0 0 1 11 12c0-.283.096-.52.287-.713A.968.968 0 0 1 12 11c.283 0 .52.096.713.287.191.192.287.43.287.713s-.096.52-.287.713A.968.968 0 0 1 12 13Zm0 9a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Zm0-2c2.233 0 4.125-.775 5.675-2.325C19.225 16.125 20 14.233 20 12c0-2.233-.775-4.125-2.325-5.675C16.125 4.775 14.233 4 12 4c-2.233 0-4.125.775-5.675 2.325C4.775 7.875 4 9.767 4 12c0 2.233.775 4.125 2.325 5.675C7.875 19.225 9.767 20 12 20Zm0 0c-2.233 0-4.125-.775-5.675-2.325C4.775 16.125 4 14.233 4 12c0-2.233.775-4.125 2.325-5.675C7.875 4.775 9.767 4 12 4c2.233 0 4.125.775 5.675 2.325C19.225 7.875 20 9.767 20 12c0 2.233-.775 4.125-2.325 5.675C16.125 19.225 14.233 20 12 20Zm1.675-5.85c.1-.05.192-.117.275-.2.083-.083.15-.175.2-.275l2.925-6.25c.083-.167.063-.313-.063-.438-.125-.125-.27-.145-.437-.062l-6.25 2.925c-.1.05-.192.117-.275.2-.083.083-.15.175-.2.275l-2.925 6.25c-.083.167-.063.313.063.438.124.124.27.145.437.062l6.25-2.925Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
@@ -141,6 +141,8 @@ describe("SpaceStore", () => {
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Disable the new room list feature flag
|
||||
await SettingsStore.setValue("feature_new_room_list", null, SettingLevel.DEVICE, false);
|
||||
await testUtils.resetAsyncStoreWithClient(store);
|
||||
});
|
||||
|
||||
@@ -1391,6 +1393,15 @@ describe("SpaceStore", () => {
|
||||
removeListener();
|
||||
});
|
||||
|
||||
it("Favourites and People meta spaces should not be returned when the feature_new_room_list labs flag is enabled", async () => {
|
||||
// Enable the new room list
|
||||
await SettingsStore.setValue("feature_new_room_list", null, SettingLevel.DEVICE, true);
|
||||
|
||||
await run();
|
||||
// Favourites and People meta spaces should not be returned
|
||||
expect(SpaceStore.instance.enabledMetaSpaces).toStrictEqual([MetaSpace.Home, MetaSpace.Orphans]);
|
||||
});
|
||||
|
||||
describe("when feature_dynamic_room_predecessors is not enabled", () => {
|
||||
beforeAll(() => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation(
|
||||
|
||||
Reference in New Issue
Block a user