Compare commits

..

2 Commits

Author SHA1 Message Date
Will Hunt
be3092f484 Initial work 2025-12-16 12:36:00 +00:00
Will Hunt
c3e20b3385 Refactor RoomStatusBar into MVVM 2025-12-11 19:37:44 +00:00
300 changed files with 3070 additions and 13867 deletions

View File

@@ -1,52 +1,3 @@
Changes in [1.12.7](https://github.com/element-hq/element-web/releases/tag/v1.12.7) (2025-12-16)
================================================================================================
## ✨ Features
* Replace legacy icons with compound ([#31424](https://github.com/element-hq/element-web/pull/31424)). Contributed by @t3chguy.
* Update polls UX to match EX Mobile and improve accessibility ([#31245](https://github.com/element-hq/element-web/pull/31245)). Contributed by @langleyd.
* Add option to enable read receipt and marker when user interact with UI ([#31353](https://github.com/element-hq/element-web/pull/31353)). Contributed by @florianduros.
* Introduce a hook to auto dispose view models ([#31178](https://github.com/element-hq/element-web/pull/31178)). Contributed by @MidhunSureshR.
* Update settings toggles to use consistent design across app. ([#30169](https://github.com/element-hq/element-web/pull/30169)). Contributed by @Half-Shot.
* Add ability to the room view to hide widgets ([#31400](https://github.com/element-hq/element-web/pull/31400)). Contributed by @langleyd.
* call: Pass the echo cancellation and noise suppression settings to EC ([#31317](https://github.com/element-hq/element-web/pull/31317)). Contributed by @BillCarsonFr.
* Tweak rendering of icons for a11y ([#31358](https://github.com/element-hq/element-web/pull/31358)). Contributed by @t3chguy.
* Implement new `renderNotificationDecoration` from module API ([#31389](https://github.com/element-hq/element-web/pull/31389)). Contributed by @MidhunSureshR.
* Replace more icons with compound ([#31381](https://github.com/element-hq/element-web/pull/31381)). Contributed by @t3chguy.
* Replace more icons with compound ([#31378](https://github.com/element-hq/element-web/pull/31378)). Contributed by @t3chguy.
* `<Banner/>`: Hide `Dismiss` button if `onClose` handler is not provided. ([#31362](https://github.com/element-hq/element-web/pull/31362)). Contributed by @kaylendog.
* Replace batch of legacy icons with compound design tokens ([#31360](https://github.com/element-hq/element-web/pull/31360)). Contributed by @t3chguy.
* MSC4380: Invite blocking ([#31268](https://github.com/element-hq/element-web/pull/31268)). Contributed by @richvdh.
* Tweak rendering of icons for accessibility ([#31346](https://github.com/element-hq/element-web/pull/31346)). Contributed by @t3chguy.
* Implement a shared `Banner` component. ([#31266](https://github.com/element-hq/element-web/pull/31266)). Contributed by @kaylendog.
* Allow the Login screen to use the dark theme ([#31293](https://github.com/element-hq/element-web/pull/31293)). Contributed by @richvdh.
## 🐛 Bug Fixes
* [Backport staging] Amend e2e normal icon from lock-solid to info ([#31559](https://github.com/element-hq/element-web/pull/31559)). Contributed by @t3chguy.
* [Backport staging] Fix CSS specificity causing icon issues in e2e verification ([#31548](https://github.com/element-hq/element-web/pull/31548)). Contributed by @RiotRobot.
* [Backport staging] Fix e2e icons in CompleteSecurity \& SetupEncryptionBody ([#31522](https://github.com/element-hq/element-web/pull/31522)). Contributed by @RiotRobot.
* [Backport staging] Remove an extra paragraph in advanced room settings ([#31511](https://github.com/element-hq/element-web/pull/31511)). Contributed by @RiotRobot.
* [Backport staging] Don't show the key storage out of sync toast when backup disabled ([#31507](https://github.com/element-hq/element-web/pull/31507)). Contributed by @RiotRobot.
* Fix composer button visibility in contrast colour mode ([#31255](https://github.com/element-hq/element-web/pull/31255)). Contributed by @t3chguy.
* Ensure correct room version is used and permissions are appropriately sert when creating rooms ([#31464](https://github.com/element-hq/element-web/pull/31464)). Contributed by @Half-Shot.
* Fix e2e icon rendering ([#31454](https://github.com/element-hq/element-web/pull/31454)). Contributed by @t3chguy.
* EventIndexer: ensure we add initial checkpoints when the db is first opened ([#31448](https://github.com/element-hq/element-web/pull/31448)). Contributed by @richvdh.
* Fix `/join <alias>` command failing due to race condition ([#31433](https://github.com/element-hq/element-web/pull/31433)). Contributed by @MidhunSureshR.
* MessageEventIndexDialog: distinguish indexed rooms ([#31436](https://github.com/element-hq/element-web/pull/31436)). Contributed by @richvdh.
* Move `EditInPlace` out of `Form` (Fixes: reloading EW on EC url update) ([#31434](https://github.com/element-hq/element-web/pull/31434)). Contributed by @toger5.
* Fixes issue where cursor would jump to the beginning of the input field after converting Japanese text and pressing Tab ([#31432](https://github.com/element-hq/element-web/pull/31432)). Contributed by @shinaoka.
* Fix widgets getting stuck in loading states ([#31314](https://github.com/element-hq/element-web/pull/31314)). Contributed by @robintown.
* Room list: fix room options remaining on room item after mouse leaving ([#31414](https://github.com/element-hq/element-web/pull/31414)). Contributed by @florianduros.
* Make `RoomList.showMessagePreview` configurable by `config.json` ([#31419](https://github.com/element-hq/element-web/pull/31419)). Contributed by @florianduros.
* Fix bug which caused app not to load correctly when `force_verification` is enabled ([#31265](https://github.com/element-hq/element-web/pull/31265)). Contributed by @richvdh.
* Room list: display the menu option on the room list item when clicked/opened ([#31380](https://github.com/element-hq/element-web/pull/31380)). Contributed by @florianduros.
* Fix handling of SVGs ([#31359](https://github.com/element-hq/element-web/pull/31359)). Contributed by @t3chguy.
* Fix word wrapping in expanded left panel buttons ([#31377](https://github.com/element-hq/element-web/pull/31377)). Contributed by @t3chguy.
* Fix aspect ratio on error view background ([#31361](https://github.com/element-hq/element-web/pull/31361)). Contributed by @t3chguy.
* Fix failure to request persistent storage perms ([#31299](https://github.com/element-hq/element-web/pull/31299)). Contributed by @richvdh.
* Fix calls sometimes not knowing that they're presented ([#31313](https://github.com/element-hq/element-web/pull/31313)). Contributed by @robintown.
Changes in [1.12.6](https://github.com/element-hq/element-web/releases/tag/v1.12.6) (2025-12-03) Changes in [1.12.6](https://github.com/element-hq/element-web/releases/tag/v1.12.6) (2025-12-03)
================================================================================================ ================================================================================================
This release fixes a bug where 1:1 calling was incorrectly not available if no Element Call focus was set. This release fixes a bug where 1:1 calling was incorrectly not available if no Element Call focus was set.

View File

@@ -112,25 +112,3 @@ Enables knock feature for rooms. This allows users to ask to join a room.
## New room list (`feature_new_room_list`) [In Development] ## New room list (`feature_new_room_list`) [In Development]
Enable the new room list that is currently in development. Enable the new room list that is currently in development.
## Exclude insecure devices when sending/receiving messages (`feature_exclude_insecure_devices`)
Do not send or receive messages to/from devices that are not properly verified. Users with unverified devices will not
receive your messages at all on those devices, and if they send messages, you will not be able to read them, but you
will be aware that a message exists.
## Share encrypted history with new members (`feature_share_history_on_invite`) [In Development]
When inviting users to an encrypted room with shared history (i.e. a room with the "Who can read history?" setting set
to "Members only (since the point in time of selecting this option)"), send the keys for previous messages to the
invitee so they can read them.
Both the inviter and the invitee must set this labs flag, before the invitation is sent.
## Encrypted state events (MSC4362) (`feature_msc4362_encrypted_state_events`)
Encrypt most of the state events in the room, including the room name and topic.
WARNING: this means that users joining a room who do not have access to its history will not be able to see the name or
topic of the room, or any other room state information. It also means the room name and topic are not available before
joining a room.

View File

@@ -1,6 +1,6 @@
{ {
"name": "element-web", "name": "element-web",
"version": "1.12.7", "version": "1.12.6",
"description": "Element: the future of secure communication", "description": "Element: the future of secure communication",
"author": "New Vector Ltd.", "author": "New Vector Ltd.",
"repository": { "repository": {
@@ -92,7 +92,7 @@
"@matrix-org/spec": "^1.7.0", "@matrix-org/spec": "^1.7.0",
"@sentry/browser": "^10.0.0", "@sentry/browser": "^10.0.0",
"@types/png-chunks-extract": "^1.0.2", "@types/png-chunks-extract": "^1.0.2",
"@vector-im/compound-design-tokens": "6.4.2", "@vector-im/compound-design-tokens": "6.4.1",
"@vector-im/compound-web": "^8.3.1", "@vector-im/compound-web": "^8.3.1",
"@vector-im/matrix-wysiwyg": "2.40.0", "@vector-im/matrix-wysiwyg": "2.40.0",
"@zxcvbn-ts/core": "^3.0.4", "@zxcvbn-ts/core": "^3.0.4",
@@ -129,7 +129,7 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"maplibre-gl": "^5.0.0", "maplibre-gl": "^5.0.0",
"matrix-encrypt-attachment": "^1.0.3", "matrix-encrypt-attachment": "^1.0.3",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#hs/safety-error-code",
"matrix-widget-api": "^1.14.0", "matrix-widget-api": "^1.14.0",
"memoize-one": "^6.0.0", "memoize-one": "^6.0.0",
"mime": "^4.0.4", "mime": "^4.0.4",
@@ -137,7 +137,7 @@
"opus-recorder": "^8.0.3", "opus-recorder": "^8.0.3",
"pako": "^2.0.3", "pako": "^2.0.3",
"png-chunks-extract": "^1.0.0", "png-chunks-extract": "^1.0.0",
"posthog-js": "1.302.2", "posthog-js": "1.297.2",
"qrcode": "1.5.4", "qrcode": "1.5.4",
"re-resizable": "6.11.2", "re-resizable": "6.11.2",
"react": "^19.0.0", "react": "^19.0.0",
@@ -281,7 +281,7 @@
"postcss-preset-env": "^10.0.0", "postcss-preset-env": "^10.0.0",
"postcss-scss": "^4.0.4", "postcss-scss": "^4.0.4",
"postcss-simple-vars": "^7.0.1", "postcss-simple-vars": "^7.0.1",
"prettier": "3.7.4", "prettier": "3.6.2",
"process": "^0.11.10", "process": "^0.11.10",
"raw-loader": "^4.0.2", "raw-loader": "^4.0.2",
"rimraf": "^6.0.0", "rimraf": "^6.0.0",

View File

@@ -43,7 +43,7 @@
"lint:types": "tsc --noEmit --jsx react", "lint:types": "tsc --noEmit --jsx react",
"test:storybook": "test-storybook --url http://localhost:6007/", "test:storybook": "test-storybook --url http://localhost:6007/",
"test:storybook:ci": "concurrently -k -s first -n \"SB,TEST\" \"yarn storybook --no-open\" \"wait-on tcp:6007 && yarn test-storybook --url http://localhost:6007/ --ci --maxWorkers=2\"", "test:storybook:ci": "concurrently -k -s first -n \"SB,TEST\" \"yarn storybook --no-open\" \"wait-on tcp:6007 && yarn test-storybook --url http://localhost:6007/ --ci --maxWorkers=2\"",
"test:storybook:update": "playwright-screenshots --entrypoint /work/scripts/storybook-screenshot-update.sh --with-node-modules" "test:storybook:update": "playwright-screenshots --entrypoint yarn --with-node-modules && playwright-screenshots --entrypoint /work/node_modules/.bin/test-storybook --with-node-modules --url http://host.docker.internal:6007/ --updateSnapshot"
}, },
"resolutions": { "resolutions": {
"playwright": "1.57.0" "playwright": "1.57.0"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

View File

@@ -1,32 +0,0 @@
#!/bin/bash
#
# Update storybook screenshots
#
# This script should be used as the entrypoint parameter of the `playwright-screenshots` script. It
# installs the yarn dependencies, and then runs `test-storybook` to update the storybook screenshots.
#
# It expects to find a storybook instance running at :6007 on the host machine. It also requires that
# `playwright-screenshots` is given the `--with-node-modules` parameter.
#
# Example:
#
# test-storybook --url http://localhost:6007/
# playwright-screenshots --entrypoint /work/scripts/storybook-screenshot-update.sh --with-node-modules
#
#
# Note: even though this script is small, it is important because the alternative is running
# `playwright-screenshots` twice in quick succession (once to do `yarn install`, a second to do the
# actual updates): and that fails, because running `playwright-screenshots` without actually starting
# Testcontainers leaves a ryuk container hanging around for up to 60s, which will block the second
# invocation.
set -e
# First install dependencies. We have to do this within the playwright container rather than the host,
# because we have which must be built for the right architecture (and some environments use a VM
# to run docker containers, meaning that things inside a container use a different architecture than
# those on the host).
yarn
# Now run the screenshot update
/work/node_modules/.bin/test-storybook --url http://host.docker.internal:6007/ --updateSnapshot

View File

@@ -19,7 +19,6 @@ export * from "./pill-input/Pill";
export * from "./pill-input/PillInput"; export * from "./pill-input/PillInput";
export * from "./rich-list/RichItem"; export * from "./rich-list/RichItem";
export * from "./rich-list/RichList"; export * from "./rich-list/RichList";
export * from "./room-list/RoomListSearchView";
export * from "./utils/Box"; export * from "./utils/Box";
export * from "./utils/Flex"; export * from "./utils/Flex";

View File

@@ -1,47 +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.
*/
.view {
/* From figma, this should be aligned with the room header */
min-height: 64px;
box-sizing: border-box;
border-bottom: var(--cpd-border-width-1) solid var(--cpd-color-bg-subtle-primary);
padding: 0 var(--cpd-space-3x);
}
.search {
/* The search button should take all the remaining space */
flex: 1;
/* !important is needed to override compound button in EW */
font: var(--cpd-font-body-md-regular) !important;
color: var(--cpd-color-text-secondary) !important;
min-width: 0;
svg {
fill: var(--cpd-color-icon-secondary);
}
}
.search_container {
flex: 1;
/* Shrink and truncate the search text */
white-space: nowrap;
overflow: hidden;
kbd {
font-family: inherit;
}
}
.search_text {
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: start;
}

View File

@@ -1,74 +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, { type JSX } from "react";
import { fn } from "storybook/test";
import type { Meta, StoryFn } from "@storybook/react-vite";
import {
RoomListSearchView,
type RoomListSearchViewActions,
type RoomListSearchViewSnapshot,
} from "./RoomListSearchView";
import { useMockedViewModel } from "../../useMockedViewModel";
type RoomListSearchProps = RoomListSearchViewSnapshot & RoomListSearchViewActions;
const RoomListSearchViewWrapper = ({
onSearchClick,
onDialPadClick,
onExploreClick,
...rest
}: RoomListSearchProps): JSX.Element => {
const vm = useMockedViewModel(rest, {
onSearchClick,
onDialPadClick,
onExploreClick,
});
return <RoomListSearchView vm={vm} />;
};
export default {
title: "Room List/RoomListSearchView",
component: RoomListSearchViewWrapper,
tags: ["autodocs"],
args: {
displayExploreButton: true,
displayDialButton: false,
searchShortcut: "⌘ K",
onSearchClick: fn(),
onDialPadClick: fn(),
onExploreClick: fn(),
},
parameters: {
design: {
type: "figma",
url: "https://www.figma.com/design/vlmt46QDdE4dgXDiyBJXqp/ER-33-Left-Panel-2025?node-id=98-1979&t=vafb4zoYMNLRuAbh-4",
},
},
} as Meta<typeof RoomListSearchViewWrapper>;
const Template: StoryFn<typeof RoomListSearchViewWrapper> = (args) => <RoomListSearchViewWrapper {...args} />;
export const Default = Template.bind({});
export const WithDialPad = Template.bind({});
WithDialPad.args = {
displayDialButton: true,
};
export const WithoutExplore = Template.bind({});
WithoutExplore.args = {
displayExploreButton: false,
};
export const AllButtons = Template.bind({});
AllButtons.args = {
displayExploreButton: true,
displayDialButton: true,
searchShortcut: "⌘ K",
};

View File

@@ -1,103 +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 { render, screen } from "jest-matrix-react";
import { composeStories } from "@storybook/react-vite";
import React from "react";
import userEvent from "@testing-library/user-event";
import * as stories from "./RoomListSearchView.stories";
import {
RoomListSearchView,
type RoomListSearchViewActions,
type RoomListSearchViewSnapshot,
} from "./RoomListSearchView";
import { MockViewModel } from "../../viewmodel/MockViewModel";
const { Default, WithDialPad, WithoutExplore, AllButtons } = composeStories(stories);
describe("RoomListSearchView", () => {
afterEach(() => {
jest.clearAllMocks();
});
describe("Storybook snapshots", () => {
it("renders the default state", () => {
const { container } = render(<Default />);
expect(container).toMatchSnapshot();
});
it("renders with dial pad button", () => {
const { container } = render(<WithDialPad />);
expect(container).toMatchSnapshot();
});
it("renders without explore button", () => {
const { container } = render(<WithoutExplore />);
expect(container).toMatchSnapshot();
});
it("renders with all buttons visible", () => {
const { container } = render(<AllButtons />);
expect(container).toMatchSnapshot();
});
});
describe("User interactions", () => {
const onSearchClick = jest.fn();
const onDialPadClick = jest.fn();
const onExploreClick = jest.fn();
class TestViewModel extends MockViewModel<RoomListSearchViewSnapshot> implements RoomListSearchViewActions {
public onSearchClick = onSearchClick;
public onDialPadClick = onDialPadClick;
public onExploreClick = onExploreClick;
}
it("should call onSearchClick when search button is clicked", async () => {
const user = userEvent.setup();
const vm = new TestViewModel({
displayExploreButton: false,
displayDialButton: false,
searchShortcut: "⌘ K",
});
render(<RoomListSearchView vm={vm} />);
await user.click(screen.getByRole("button", { name: "Search ⌘ K" }));
expect(onSearchClick).toHaveBeenCalledTimes(1);
});
it("should call onDialPadClick when dial pad button is clicked", async () => {
const user = userEvent.setup();
const vm = new TestViewModel({
displayExploreButton: false,
displayDialButton: true,
searchShortcut: "⌘ K",
});
render(<RoomListSearchView vm={vm} />);
await user.click(screen.getByRole("button", { name: "Open dial pad" }));
expect(onDialPadClick).toHaveBeenCalledTimes(1);
});
it("should call onExploreClick when explore button is clicked", async () => {
const user = userEvent.setup();
const vm = new TestViewModel({
displayExploreButton: true,
displayDialButton: false,
searchShortcut: "⌘ K",
});
render(<RoomListSearchView vm={vm} />);
await user.click(screen.getByRole("button", { name: "Explore rooms" }));
expect(onExploreClick).toHaveBeenCalledTimes(1);
});
});
});

View File

@@ -1,119 +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, { type JSX, type MouseEventHandler } 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 DialPadIcon from "@vector-im/compound-design-tokens/assets/web/icons/dial-pad";
import styles from "./RoomListSearchView.module.css";
import { type ViewModel } from "../../viewmodel/ViewModel";
import { useViewModel } from "../../useViewModel";
import { Flex } from "../../utils/Flex";
import { useI18n } from "../../utils/i18nContext";
export interface RoomListSearchViewSnapshot {
/**
* Whether to display the explore button.
*/
displayExploreButton: boolean;
/**
* Whether to display the dial pad button.
*/
displayDialButton: boolean;
/**
* The keyboard shortcut text to display for the search action.
* For example: "⌘ K" on macOS or "Ctrl K" on other platforms.
*/
searchShortcut: string;
}
export interface RoomListSearchViewActions {
/**
* Handles the click event on the search button.
*/
onSearchClick: MouseEventHandler<HTMLButtonElement>;
/**
* Handles the click event on the dial pad button.
*/
onDialPadClick: MouseEventHandler<HTMLButtonElement>;
/**
* Handles the click event on the explore button.
*/
onExploreClick: MouseEventHandler<HTMLButtonElement>;
}
/**
* The view model for the room list search component.
*/
export type RoomListSearchViewModel = ViewModel<RoomListSearchViewSnapshot> & RoomListSearchViewActions;
interface RoomListSearchViewProps {
/**
* The view model for the room list search component.
*/
vm: RoomListSearchViewModel;
}
/**
* A search component to be displayed at the top of the room list.
* The component provides search functionality, optional dial pad access, and optional room exploration.
*
* @example
* ```tsx
* <RoomListSearchView vm={roomListSearchViewModel} />
* ```
*/
export function RoomListSearchView({ vm }: Readonly<RoomListSearchViewProps>): JSX.Element {
const { translate: _t } = useI18n();
const { displayExploreButton, displayDialButton, searchShortcut } = useViewModel(vm);
return (
<Flex
data-testid="room-list-search"
className={styles.view}
role="search"
gap="var(--cpd-space-2x)"
align="center"
>
<Button
id="room-list-search-button"
className={styles.search}
kind="secondary"
size="sm"
Icon={SearchIcon}
onClick={vm.onSearchClick}
>
<Flex className={styles["search_container"]} as="span" justify="space-between">
<span className={styles["search_text"]}>{_t("action|search")}</span>
<kbd>{searchShortcut}</kbd>
</Flex>
</Button>
{displayDialButton && (
<Button
kind="secondary"
size="sm"
Icon={DialPadIcon}
iconOnly={true}
aria-label={_t("left_panel|open_dial_pad")}
onClick={vm.onDialPadClick}
/>
)}
{displayExploreButton && (
<Button
kind="secondary"
size="sm"
Icon={ExploreIcon}
iconOnly={true}
aria-label={_t("action|explore_rooms")}
onClick={vm.onExploreClick}
/>
)}
</Flex>
);
}

View File

@@ -1,290 +0,0 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`RoomListSearchView Storybook snapshots renders the default state 1`] = `
<div>
<div
class="flex view"
data-testid="room-list-search"
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); --mx-flex-wrap: nowrap;"
>
<button
class="_button_187yx_8 search _has-icon_187yx_57"
data-kind="secondary"
data-size="sm"
id="room-list-search-button"
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.414zM16 10.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0"
/>
</svg>
<span
class="flex search_container"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: space-between; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
>
<span
class="search_text"
>
Search
</span>
<kbd>
⌘ K
</kbd>
</span>
</button>
<button
aria-label="Explore rooms"
class="_button_187yx_8 _has-icon_187yx_57 _icon-only_187yx_50"
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.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 12q0-.424.287-.713A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713 0 .424-.287.713A.97.97 0 0 1 12 13m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4 6.325 6.325 4 12t2.325 5.675T12 20m0 0q-3.35 0-5.675-2.325T4 12t2.325-5.675T12 4t5.675 2.325T20 12t-2.325 5.675T12 20m1.675-5.85q.15-.075.275-.2t.2-.275l2.925-6.25q.125-.25-.062-.437-.188-.188-.438-.063l-6.25 2.925q-.15.075-.275.2t-.2.275l-2.925 6.25q-.125.25.063.438.186.186.437.062z"
/>
</svg>
</button>
</div>
</div>
`;
exports[`RoomListSearchView Storybook snapshots renders with all buttons visible 1`] = `
<div>
<div
class="flex view"
data-testid="room-list-search"
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); --mx-flex-wrap: nowrap;"
>
<button
class="_button_187yx_8 search _has-icon_187yx_57"
data-kind="secondary"
data-size="sm"
id="room-list-search-button"
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.414zM16 10.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0"
/>
</svg>
<span
class="flex search_container"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: space-between; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
>
<span
class="search_text"
>
Search
</span>
<kbd>
⌘ K
</kbd>
</span>
</button>
<button
aria-label="Open dial pad"
class="_button_187yx_8 _has-icon_187yx_57 _icon-only_187yx_50"
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 18.6c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8M6.6 2.4c-.99 0-1.8.81-1.8 1.8S5.61 6 6.6 6s1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m0 5.4c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m0 5.4c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8M17.4 6c.99 0 1.8-.81 1.8-1.8s-.81-1.8-1.8-1.8-1.8.81-1.8 1.8.81 1.8 1.8 1.8M12 13.2c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m5.4 0c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m0-5.4c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m-5.4 0c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m0-5.4c-.99 0-1.8.81-1.8 1.8S11.01 6 12 6s1.8-.81 1.8-1.8-.81-1.8-1.8-1.8"
/>
</svg>
</button>
<button
aria-label="Explore rooms"
class="_button_187yx_8 _has-icon_187yx_57 _icon-only_187yx_50"
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.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 12q0-.424.287-.713A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713 0 .424-.287.713A.97.97 0 0 1 12 13m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4 6.325 6.325 4 12t2.325 5.675T12 20m0 0q-3.35 0-5.675-2.325T4 12t2.325-5.675T12 4t5.675 2.325T20 12t-2.325 5.675T12 20m1.675-5.85q.15-.075.275-.2t.2-.275l2.925-6.25q.125-.25-.062-.437-.188-.188-.438-.063l-6.25 2.925q-.15.075-.275.2t-.2.275l-2.925 6.25q-.125.25.063.438.186.186.437.062z"
/>
</svg>
</button>
</div>
</div>
`;
exports[`RoomListSearchView Storybook snapshots renders with dial pad button 1`] = `
<div>
<div
class="flex view"
data-testid="room-list-search"
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); --mx-flex-wrap: nowrap;"
>
<button
class="_button_187yx_8 search _has-icon_187yx_57"
data-kind="secondary"
data-size="sm"
id="room-list-search-button"
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.414zM16 10.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0"
/>
</svg>
<span
class="flex search_container"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: space-between; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
>
<span
class="search_text"
>
Search
</span>
<kbd>
⌘ K
</kbd>
</span>
</button>
<button
aria-label="Open dial pad"
class="_button_187yx_8 _has-icon_187yx_57 _icon-only_187yx_50"
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 18.6c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8M6.6 2.4c-.99 0-1.8.81-1.8 1.8S5.61 6 6.6 6s1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m0 5.4c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m0 5.4c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8M17.4 6c.99 0 1.8-.81 1.8-1.8s-.81-1.8-1.8-1.8-1.8.81-1.8 1.8.81 1.8 1.8 1.8M12 13.2c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m5.4 0c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m0-5.4c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m-5.4 0c-.99 0-1.8.81-1.8 1.8s.81 1.8 1.8 1.8 1.8-.81 1.8-1.8-.81-1.8-1.8-1.8m0-5.4c-.99 0-1.8.81-1.8 1.8S11.01 6 12 6s1.8-.81 1.8-1.8-.81-1.8-1.8-1.8"
/>
</svg>
</button>
<button
aria-label="Explore rooms"
class="_button_187yx_8 _has-icon_187yx_57 _icon-only_187yx_50"
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.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 12q0-.424.287-.713A.97.97 0 0 1 12 11q.424 0 .713.287.287.288.287.713 0 .424-.287.713A.97.97 0 0 1 12 13m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22m0-2q3.35 0 5.675-2.325T20 12t-2.325-5.675T12 4 6.325 6.325 4 12t2.325 5.675T12 20m0 0q-3.35 0-5.675-2.325T4 12t2.325-5.675T12 4t5.675 2.325T20 12t-2.325 5.675T12 20m1.675-5.85q.15-.075.275-.2t.2-.275l2.925-6.25q.125-.25-.062-.437-.188-.188-.438-.063l-6.25 2.925q-.15.075-.275.2t-.2.275l-2.925 6.25q-.125.25.063.438.186.186.437.062z"
/>
</svg>
</button>
</div>
</div>
`;
exports[`RoomListSearchView Storybook snapshots renders without explore button 1`] = `
<div>
<div
class="flex view"
data-testid="room-list-search"
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); --mx-flex-wrap: nowrap;"
>
<button
class="_button_187yx_8 search _has-icon_187yx_57"
data-kind="secondary"
data-size="sm"
id="room-list-search-button"
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.414zM16 10.5a5.5 5.5 0 1 0-11 0 5.5 5.5 0 0 0 11 0"
/>
</svg>
<span
class="flex search_container"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: start; --mx-flex-justify: space-between; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
>
<span
class="search_text"
>
Search
</span>
<kbd>
⌘ K
</kbd>
</span>
</button>
</div>
</div>
`;

View File

@@ -1,9 +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.
*/
export type { RoomListSearchViewModel, RoomListSearchViewSnapshot } from "./RoomListSearchView";
export { RoomListSearchView } from "./RoomListSearchView";

View File

@@ -313,9 +313,9 @@
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@element-hq/element-web-module-api@^1.8.0": "@element-hq/element-web-module-api@^1.8.0":
version "1.9.0" version "1.8.0"
resolved "https://registry.yarnpkg.com/@element-hq/element-web-module-api/-/element-web-module-api-1.9.0.tgz#2e4fcc8809418c8670d4f0576bc4a9a235bc6c50" resolved "https://registry.yarnpkg.com/@element-hq/element-web-module-api/-/element-web-module-api-1.8.0.tgz#95aa4ec22609cf0f4a7f24274473af0645a16f2a"
integrity sha512-Ao/V9w+wysZK4bh61LlKlznF10n2ZbD6KcUI46/zUMttXbmJn3ahvbzhEpwYcD+Cjy3ag5ycxLIIGkKV/fncXg== integrity sha512-lMiDA9ubP3mZZupIMT8T3wS0riX30rYZj3pFpdP4cfZhkWZa3FJFStokAy5OnaHyENC7Px1cqkBGqilOWewY/A==
"@element-hq/element-web-playwright-common@^2.0.0": "@element-hq/element-web-playwright-common@^2.0.0":
version "2.1.0" version "2.1.0"
@@ -2032,9 +2032,9 @@
integrity sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g== integrity sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==
"@vector-im/compound-design-tokens@^6.3.0": "@vector-im/compound-design-tokens@^6.3.0":
version "6.4.2" version "6.4.0"
resolved "https://registry.yarnpkg.com/@vector-im/compound-design-tokens/-/compound-design-tokens-6.4.2.tgz#29189d6480c8ccf09ce143cb4618fb13a56a7583" resolved "https://registry.yarnpkg.com/@vector-im/compound-design-tokens/-/compound-design-tokens-6.4.0.tgz#2e51f39f79ebda985a2f6cf80d567b9307aff03a"
integrity sha512-LHLGZgnatH3mQXn9TF+m/SUinPS2nKvuCT/r2AQ7HAgEIG/S/Ck6e/iV4IFQLSZnd9gU0RlMsLkP2UQ/AKUEBA== integrity sha512-93nYQZMgUt6apjCwwnMhMxN8VYQXN3GYOnwovwJjavImwsCGwI/e853BV/DstrWumYh6k5pZsP9e6AF+nz3SIQ==
"@vitest/expect@3.2.4": "@vitest/expect@3.2.4":
version "3.2.4" version "3.2.4"
@@ -5855,9 +5855,9 @@ prelude-ls@^1.2.1:
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
prettier@^3.6.2: prettier@^3.6.2:
version "3.7.4" version "3.6.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.7.4.tgz#d2f8335d4b1cec47e1c8098645411b0c9dff9c0f" resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393"
integrity sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA== integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==
pretty-format@30.2.0, pretty-format@^30.0.0: pretty-format@30.2.0, pretty-format@^30.0.0:
version "30.2.0" version "30.2.0"
@@ -7088,9 +7088,9 @@ vite-plugin-node-polyfills@^0.24.0:
node-stdlib-browser "^1.2.0" node-stdlib-browser "^1.2.0"
vite@^7.1.9: vite@^7.1.9:
version "7.2.7" version "7.2.4"
resolved "https://registry.yarnpkg.com/vite/-/vite-7.2.7.tgz#0789a4c3206081699f34a9ecca2dda594a07478e" resolved "https://registry.yarnpkg.com/vite/-/vite-7.2.4.tgz#a3a09c7e25487612ecc1119c7d412c73da35bd4e"
integrity sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ== integrity sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==
dependencies: dependencies:
esbuild "^0.25.0" esbuild "^0.25.0"
fdir "^6.5.0" fdir "^6.5.0"

View File

@@ -29,7 +29,7 @@ test.describe("Landmark navigation tests", () => {
// Pressing Control+F6 again will focus room search // Pressing Control+F6 again will focus room search
await page.keyboard.press("ControlOrMeta+F6"); await page.keyboard.press("ControlOrMeta+F6");
await expect(page.locator("#room-list-search-button")).toBeFocused(); await expect(page.locator(".mx_RoomListSearch_search")).toBeFocused();
// Pressing Control+F6 again will focus the message composer // Pressing Control+F6 again will focus the message composer
await page.keyboard.press("ControlOrMeta+F6"); await page.keyboard.press("ControlOrMeta+F6");
@@ -44,7 +44,7 @@ test.describe("Landmark navigation tests", () => {
await expect(page.locator(".mx_HomePage")).toBeFocused(); await expect(page.locator(".mx_HomePage")).toBeFocused();
await page.keyboard.press("ControlOrMeta+Shift+F6"); await page.keyboard.press("ControlOrMeta+Shift+F6");
await expect(page.locator("#room-list-search-button")).toBeFocused(); await expect(page.locator(".mx_RoomListSearch_search")).toBeFocused();
await page.keyboard.press("ControlOrMeta+Shift+F6"); await page.keyboard.press("ControlOrMeta+Shift+F6");
await expect(page.locator(".mx_SpaceButton_active")).toBeFocused(); await expect(page.locator(".mx_SpaceButton_active")).toBeFocused();
@@ -75,7 +75,7 @@ test.describe("Landmark navigation tests", () => {
// Pressing Control+F6 again will focus room search // Pressing Control+F6 again will focus room search
await page.keyboard.press("ControlOrMeta+F6"); await page.keyboard.press("ControlOrMeta+F6");
await expect(page.locator("#room-list-search-button")).toBeFocused(); await expect(page.locator(".mx_RoomListSearch_search")).toBeFocused();
// Pressing Control+F6 again will focus the room tile in the room list // Pressing Control+F6 again will focus the room tile in the room list
await page.keyboard.press("ControlOrMeta+F6"); await page.keyboard.press("ControlOrMeta+F6");
@@ -97,7 +97,7 @@ test.describe("Landmark navigation tests", () => {
await expect(page.locator(".mx_RoomListItemView_selected")).toBeFocused(); await expect(page.locator(".mx_RoomListItemView_selected")).toBeFocused();
await page.keyboard.press("ControlOrMeta+Shift+F6"); await page.keyboard.press("ControlOrMeta+Shift+F6");
await expect(page.locator("#room-list-search-button")).toBeFocused(); await expect(page.locator(".mx_RoomListSearch_search")).toBeFocused();
await page.keyboard.press("ControlOrMeta+Shift+F6"); await page.keyboard.press("ControlOrMeta+Shift+F6");
await expect(page.locator(".mx_SpaceButton_active")).toBeFocused(); await expect(page.locator(".mx_SpaceButton_active")).toBeFocused();
@@ -131,7 +131,7 @@ test.describe("Landmark navigation tests", () => {
// Pressing Control+F6 again will focus room search // Pressing Control+F6 again will focus room search
await page.keyboard.press("ControlOrMeta+F6"); await page.keyboard.press("ControlOrMeta+F6");
await expect(page.locator("#room-list-search-button")).toBeFocused(); await expect(page.locator(".mx_RoomListSearch_search")).toBeFocused();
// Pressing Control+F6 again will focus the room tile in the room list // Pressing Control+F6 again will focus the room tile in the room list
await page.keyboard.press("ControlOrMeta+F6"); await page.keyboard.press("ControlOrMeta+F6");
@@ -153,7 +153,7 @@ test.describe("Landmark navigation tests", () => {
await expect(page.locator(".mx_RoomListItemView")).toBeFocused(); await expect(page.locator(".mx_RoomListItemView")).toBeFocused();
await page.keyboard.press("ControlOrMeta+Shift+F6"); await page.keyboard.press("ControlOrMeta+Shift+F6");
await expect(page.locator("#room-list-search-button")).toBeFocused(); await expect(page.locator(".mx_RoomListSearch_search")).toBeFocused();
await page.keyboard.press("ControlOrMeta+Shift+F6"); await page.keyboard.press("ControlOrMeta+Shift+F6");
await expect(page.locator(".mx_SpaceButton_active")).toBeFocused(); await expect(page.locator(".mx_SpaceButton_active")).toBeFocused();

View File

@@ -168,19 +168,5 @@ test.describe("Composer", () => {
await composer.press("Enter"); await composer.press("Enter");
await expect(page.locator(".mx_EventTile_body", { hasText: "Bob" })).toBeVisible(); await expect(page.locator(".mx_EventTile_body", { hasText: "Bob" })).toBeVisible();
}); });
test("renders emoji autocomplete", { tag: "@screenshot" }, async ({ page }) => {
const composer = page.getByRole("textbox", { name: "Send an unencrypted message…" });
// Type ":+1" to trigger emoji autocomplete
await composer.pressSequentially(":+1");
// Wait for autocomplete to appear
const autocomplete = page.locator("#mx_Autocomplete");
await expect(autocomplete).toBeVisible();
// Take a screenshot of the autocomplete
await expect(autocomplete).toMatchScreenshot("emoji-autocomplete.png");
});
}); });
}); });

View File

@@ -130,68 +130,53 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => {
await page.unrouteAll({ behavior: "ignoreErrors" }); await page.unrouteAll({ behavior: "ignoreErrors" });
}); });
test( test("Verify device with QR code during login", async ({ page, app, credentials, homeserver }) => {
"Verify device with QR code during login", // A mode 0x02 verification: "self-verifying in which the current device does not yet trust the master key"
{ tag: "@screenshot" }, await logIntoElement(page, credentials);
async ({ page, app, credentials, homeserver }) => {
// A mode 0x02 verification: "self-verifying in which the current device does not yet trust the master key"
await logIntoElement(page, credentials);
// Launch the verification request between alice and the bot // Launch the verification request between alice and the bot
const verificationRequest = await initiateAliceVerificationRequest(page); const verificationRequest = await initiateAliceVerificationRequest(page);
const infoDialog = page.locator(".mx_InfoDialog"); const infoDialog = page.locator(".mx_InfoDialog");
// feed the QR code into the verification request. // feed the QR code into the verification request.
const qrData = await readQrCode(infoDialog); const qrData = await readQrCode(infoDialog);
await expect(page.locator(".mx_Dialog")).toMatchScreenshot("qr-code.png", { const verifier = await verificationRequest.evaluateHandle(
mask: [infoDialog.locator("img")], (request, qrData) => request.scanQRCode(new Uint8ClampedArray(qrData)),
}); [...qrData],
const verifier = await verificationRequest.evaluateHandle( );
(request, qrData) => request.scanQRCode(new Uint8ClampedArray(qrData)),
[...qrData],
);
// Confirm that the bot user scanned successfully // Confirm that the bot user scanned successfully
await expect( await expect(infoDialog.getByText("Confirm that you see a green shield on your other device")).toBeVisible();
infoDialog.getByText("Confirm that you see a green shield on your other device"), await infoDialog.getByRole("button", { name: "Yes, I see a green shield" }).click();
).toBeVisible(); await infoDialog.getByRole("button", { name: "Got it" }).click();
await expect(page.locator(".mx_Dialog")).toMatchScreenshot("confirm-green-shield.png");
await infoDialog.getByRole("button", { name: "Yes, I see a green shield" }).click();
await expect(page.locator(".mx_Dialog")).toMatchScreenshot("got-it.png");
await infoDialog.getByRole("button", { name: "Got it" }).click();
// wait for the bot to see we have finished // wait for the bot to see we have finished
await verifier.evaluate((verifier) => verifier.verify()); await verifier.evaluate((verifier) => verifier.verify());
// the bot uploads the signatures asynchronously, so wait for that to happen // the bot uploads the signatures asynchronously, so wait for that to happen
await page.waitForTimeout(1000); await page.waitForTimeout(1000);
// our device should trust the bot device // our device should trust the bot device
await app.client.evaluate(async (cli, aliceBotCredentials) => { await app.client.evaluate(async (cli, aliceBotCredentials) => {
const deviceStatus = await cli const deviceStatus = await cli
.getCrypto()! .getCrypto()!
.getDeviceVerificationStatus(aliceBotCredentials.userId, aliceBotCredentials.deviceId); .getDeviceVerificationStatus(aliceBotCredentials.userId, aliceBotCredentials.deviceId);
if (!deviceStatus.isVerified()) { if (!deviceStatus.isVerified()) {
throw new Error("Bot device was not verified after QR code verification"); throw new Error("Bot device was not verified after QR code verification");
} }
}, aliceBotClient.credentials); }, aliceBotClient.credentials);
// Check that our device is now cross-signed // Check that our device is now cross-signed
await checkDeviceIsCrossSigned(app); await checkDeviceIsCrossSigned(app);
// Check that the current device is connected to key backup // Check that the current device is connected to key backup
await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, true); await checkDeviceIsConnectedKeyBackup(app, expectedBackupVersion, true);
}, });
);
test( test("Verify device with Security Phrase during login", async ({ page, app, credentials, homeserver }) => {
"Verify device with Security Phrase during login", await logIntoElement(page, credentials);
{ tag: "@screenshot" }, await enterRecoveryKeyAndCheckVerified(page, app, "new passphrase");
async ({ page, app, credentials, homeserver }) => { });
await logIntoElement(page, credentials);
await enterRecoveryKeyAndCheckVerified(page, app, "new passphrase", true);
},
);
test("Verify device with Recovery Key during login", async ({ page, app, credentials, homeserver }) => { test("Verify device with Recovery Key during login", async ({ page, app, credentials, homeserver }) => {
const recoveryKey = (await aliceBotClient.getRecoveryKey()).encodedPrivateKey; const recoveryKey = (await aliceBotClient.getRecoveryKey()).encodedPrivateKey;
@@ -241,12 +226,7 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => {
}); });
/** Helper for the three tests above which verify by recovery key */ /** Helper for the three tests above which verify by recovery key */
async function enterRecoveryKeyAndCheckVerified( async function enterRecoveryKeyAndCheckVerified(page: Page, app: ElementAppPage, recoveryKey: string) {
page: Page,
app: ElementAppPage,
recoveryKey: string,
screenshot = false,
) {
await page.getByRole("button", { name: "Use recovery key" }).click(); await page.getByRole("button", { name: "Use recovery key" }).click();
// Enter the recovery key // Enter the recovery key
@@ -254,12 +234,8 @@ test.describe("Device verification", { tag: "@no-webkit" }, () => {
// We use `pressSequentially` here to make sure that the FocusLock isn't causing us any problems // We use `pressSequentially` here to make sure that the FocusLock isn't causing us any problems
// (cf https://github.com/element-hq/element-web/issues/30089) // (cf https://github.com/element-hq/element-web/issues/30089)
await dialog.getByTitle("Recovery key").pressSequentially(recoveryKey); await dialog.getByTitle("Recovery key").pressSequentially(recoveryKey);
if (screenshot) {
await expect(page.locator(".mx_Dialog").filter({ hasText: "Enter your recovery key" })).toMatchScreenshot(
"recovery-key.png",
);
}
await dialog.getByRole("button", { name: "Continue", disabled: false }).click(); await dialog.getByRole("button", { name: "Continue", disabled: false }).click();
await page.getByRole("button", { name: "Done" }).click(); await page.getByRole("button", { name: "Done" }).click();
// Check that our device is now cross-signed // Check that our device is now cross-signed

View File

@@ -29,7 +29,6 @@ export const test = base.extend<{
room1Name: "Room 1", room1Name: "Room 1",
room1: async ({ room1Name: name, app, user, bot }, use) => { room1: async ({ room1Name: name, app, user, bot }, use) => {
const roomId = await app.client.createRoom({ name, invite: [bot.credentials.userId] }); const roomId = await app.client.createRoom({ name, invite: [bot.credentials.userId] });
await bot.awaitRoomMembership(roomId);
await use({ name, roomId }); await use({ name, roomId });
}, },

View File

@@ -36,13 +36,11 @@ export const test = base.extend<{
roomAlphaName: "Room Alpha", roomAlphaName: "Room Alpha",
roomAlpha: async ({ roomAlphaName: name, app, user, bot }, use) => { roomAlpha: async ({ roomAlphaName: name, app, user, bot }, use) => {
const roomId = await app.client.createRoom({ name, invite: [bot.credentials.userId] }); const roomId = await app.client.createRoom({ name, invite: [bot.credentials.userId] });
await bot.awaitRoomMembership(roomId);
await use({ name, roomId }); await use({ name, roomId });
}, },
roomBetaName: "Room Beta", roomBetaName: "Room Beta",
roomBeta: async ({ roomBetaName: name, app, user, bot }, use) => { roomBeta: async ({ roomBetaName: name, app, user, bot }, use) => {
const roomId = await app.client.createRoom({ name, invite: [bot.credentials.userId] }); const roomId = await app.client.createRoom({ name, invite: [bot.credentials.userId] });
await bot.awaitRoomMembership(roomId);
await use({ name, roomId }); await use({ name, roomId });
}, },
msg: async ({ page, app, util }, use) => { msg: async ({ page, app, util }, use) => {

View File

@@ -13,30 +13,72 @@ import { test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.describe("Message ordering", () => { test.describe("Message ordering", () => {
test.describe("in the main timeline", () => { test.describe("in the main timeline", () => {
test.fixme("A receipt for the last event in sync order (even with wrong ts) marks a room as read", () => {}); test.fixme(
test.fixme("A receipt for a non-last event in sync order (even when ts makes it last) leaves room unread", () => {}); "A receipt for the last event in sync order (even with wrong ts) marks a room as read",
() => {},
);
test.fixme(
"A receipt for a non-last event in sync order (even when ts makes it last) leaves room unread",
() => {},
);
}); });
test.describe("in threads", () => { test.describe("in threads", () => {
// These don't pass yet - we need MSC4033 - we don't even know the Sync order yet // These don't pass yet - we need MSC4033 - we don't even know the Sync order yet
test.fixme("A receipt for the last event in sync order (even with wrong ts) marks a thread as read", () => {}); test.fixme(
test.fixme("A receipt for a non-last event in sync order (even when ts makes it last) leaves thread unread", () => {}); "A receipt for the last event in sync order (even with wrong ts) marks a thread as read",
() => {},
);
test.fixme(
"A receipt for a non-last event in sync order (even when ts makes it last) leaves thread unread",
() => {},
);
// These pass now and should not later - we should use order from MSC4033 instead of ts // These pass now and should not later - we should use order from MSC4033 instead of ts
// These are broken out // These are broken out
test.fixme("A receipt for last threaded event in ts order (even when it was received non-last) marks a thread as read", () => {}); test.fixme(
test.fixme("A receipt for non-last threaded event in ts order (even when it was received last) leaves thread unread", () => {}); "A receipt for last threaded event in ts order (even when it was received non-last) marks a thread as read",
test.fixme("A receipt for last threaded edit in ts order (even when it was received non-last) marks a thread as read", () => {}); () => {},
test.fixme("A receipt for non-last threaded edit in ts order (even when it was received last) leaves thread unread", () => {}); );
test.fixme("A receipt for last threaded reaction in ts order (even when it was received non-last) marks a thread as read", () => {}); test.fixme(
test.fixme("A receipt for non-last threaded reaction in ts order (even when it was received last) leaves thread unread", () => {}); "A receipt for non-last threaded event in ts order (even when it was received last) leaves thread unread",
() => {},
);
test.fixme(
"A receipt for last threaded edit in ts order (even when it was received non-last) marks a thread as read",
() => {},
);
test.fixme(
"A receipt for non-last threaded edit in ts order (even when it was received last) leaves thread unread",
() => {},
);
test.fixme(
"A receipt for last threaded reaction in ts order (even when it was received non-last) marks a thread as read",
() => {},
);
test.fixme(
"A receipt for non-last threaded reaction in ts order (even when it was received last) leaves thread unread",
() => {},
);
}); });
test.describe("thread roots", () => { test.describe("thread roots", () => {
test.fixme("A receipt for last reaction to thread root in sync order (even when ts makes it last) marks room as read", () => {}); test.fixme(
test.fixme("A receipt for non-last reaction to thread root in sync order (even when ts makes it last) leaves room unread", () => {}); "A receipt for last reaction to thread root in sync order (even when ts makes it last) marks room as read",
test.fixme("A receipt for last edit to thread root in sync order (even when ts makes it last) marks room as read", () => {}); () => {},
test.fixme("A receipt for non-last edit to thread root in sync order (even when ts makes it last) leaves room unread", () => {}); );
test.fixme(
"A receipt for non-last reaction to thread root in sync order (even when ts makes it last) leaves room unread",
() => {},
);
test.fixme(
"A receipt for last edit to thread root in sync order (even when ts makes it last) marks room as read",
() => {},
);
test.fixme(
"A receipt for non-last edit to thread root in sync order (even when ts makes it last) leaves room unread",
() => {},
);
}); });
}); });
}); });

View File

@@ -12,20 +12,18 @@ import { test } from ".";
test.describe("Read receipts", { tag: "@mergequeue" }, () => { test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.describe("messages with missing referents", () => { test.describe("messages with missing referents", () => {
test.fixme("A message in an unknown thread is not visible and the room is read", async ({ test.fixme(
roomAlpha: room1, "A message in an unknown thread is not visible and the room is read",
roomBeta: room2, async ({ roomAlpha: room1, roomBeta: room2, util, msg }) => {
util, // Given a thread existed and the room is read
msg, await util.goTo(room1);
}) => { await util.receiveMessages(room2, ["Root1", msg.threadedOff("Root1", "T1a")]);
// Given a thread existed and the room is read
await util.goTo(room1);
await util.receiveMessages(room2, ["Root1", msg.threadedOff("Root1", "T1a")]);
// When I restart, forgetting the thread root // When I restart, forgetting the thread root
// And I receive a message on that thread // And I receive a message on that thread
// Then the message is invisible and the room remains read // Then the message is invisible and the room remains read
}); },
);
test.fixme("When a message's thread root appears later the thread appears and the room is unread", () => {}); test.fixme("When a message's thread root appears later the thread appears and the room is unread", () => {});
test.fixme("An edit of an unknown message is not visible and the room is read", () => {}); test.fixme("An edit of an unknown message is not visible and the room is read", () => {});
test.fixme("When an edit's message appears later the edited version appears and the room is unread", () => {}); test.fixme("When an edit's message appears later the edited version appears and the room is unread", () => {});

View File

@@ -14,8 +14,14 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.describe("Notifications", () => { test.describe("Notifications", () => {
test.describe("in the main timeline", () => { test.describe("in the main timeline", () => {
test.fixme("A new message that mentions me shows a notification", () => {}); test.fixme("A new message that mentions me shows a notification", () => {});
test.fixme("Reading a notifying message reduces the notification count in the room list, space and tab", () => {}); test.fixme(
test.fixme("Reading the last notifying message removes the notification marker from room list, space and tab", () => {}); "Reading a notifying message reduces the notification count in the room list, space and tab",
() => {},
);
test.fixme(
"Reading the last notifying message removes the notification marker from room list, space and tab",
() => {},
);
test.fixme("Editing a message to mentions me shows a notification", () => {}); test.fixme("Editing a message to mentions me shows a notification", () => {});
test.fixme("Reading the last notifying edited message removes the notification marker", () => {}); test.fixme("Reading the last notifying edited message removes the notification marker", () => {});
test.fixme("Redacting a notifying message removes the notification marker", () => {}); test.fixme("Redacting a notifying message removes the notification marker", () => {});
@@ -24,9 +30,18 @@ test.describe("Read receipts", { tag: "@mergequeue" }, () => {
test.describe("in threads", () => { test.describe("in threads", () => {
test.fixme("A new threaded message that mentions me shows a notification", () => {}); test.fixme("A new threaded message that mentions me shows a notification", () => {});
test.fixme("Reading a notifying threaded message removes the notification count", () => {}); test.fixme("Reading a notifying threaded message removes the notification count", () => {});
test.fixme("Notification count remains steady when reading threads that contain seen notifications", () => {}); test.fixme(
test.fixme("Notification count remains steady when paging up thread view even when threads contain seen notifications", () => {}); "Notification count remains steady when reading threads that contain seen notifications",
test.fixme("Notification count remains steady when paging up thread view after mark as unread even if older threads contain notifications", () => {}); () => {},
);
test.fixme(
"Notification count remains steady when paging up thread view even when threads contain seen notifications",
() => {},
);
test.fixme(
"Notification count remains steady when paging up thread view after mark as unread even if older threads contain notifications",
() => {},
);
test.fixme("Redacting a notifying threaded message removes the notification marker", () => {}); test.fixme("Redacting a notifying threaded message removes the notification marker", () => {});
}); });
}); });

View File

@@ -207,7 +207,7 @@ test.describe("RightPanel", () => {
// \d represents the number of the space members // \d represents the number of the space members
await page await page
.locator(".mx_RoomInfoLine") .locator(".mx_RoomInfoLine_private")
.getByRole("button", { name: /\d member/ }) .getByRole("button", { name: /\d member/ })
.click(); .click();
await expect(page.locator(".mx_MemberListView")).toBeVisible(); await expect(page.locator(".mx_MemberListView")).toBeVisible();

View File

@@ -6,8 +6,6 @@ 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 { type Page } from "playwright-core";
import { SettingLevel } from "../../../src/settings/SettingLevel"; import { SettingLevel } from "../../../src/settings/SettingLevel";
import { UIFeature } from "../../../src/settings/UIFeature"; import { UIFeature } from "../../../src/settings/UIFeature";
import { test, expect } from "../../element-web-test"; import { test, expect } from "../../element-web-test";
@@ -112,107 +110,4 @@ test.describe("Create Room", () => {
await expect(header).toContainText(name); await expect(header).toContainText(name);
}); });
}); });
test.describe("when the encrypted state labs flag is turned off", () => {
test.use({ labsFlags: [] });
test("creates a room without encrypted state", { tag: "@screenshot" }, async ({ page, user: _user }) => {
// When we start to create a room
await page.getByRole("button", { name: "New conversation", exact: true }).click();
await page.getByRole("menuitem", { name: "New room" }).click();
await page.getByRole("textbox", { name: "Name" }).fill(name);
// Then there is no Encrypt state events button
await expect(page.getByRole("checkbox", { name: "Encrypt state events" })).not.toBeVisible();
// And when we create the room
await page.getByRole("button", { name: "Create room" }).click();
// Then we created a normal encrypted room, without encrypted state
await expect(page.getByText("Encryption enabled")).toBeVisible();
await expect(page.getByText("State encryption enabled")).not.toBeVisible();
// And the room name state event is not encrypted
await viewSourceOnRoomNameEvent(page);
await expect(page.getByText("Original event source")).toBeVisible();
await expect(page.getByText("Decrypted event source")).not.toBeVisible();
});
});
test.describe("when the encrypted state labs flag is turned on", () => {
test.use({ labsFlags: ["feature_msc4362_encrypted_state_events"] });
test(
"creates a room with encrypted state if we check the box",
{ tag: "@screenshot" },
async ({ page, user: _user }) => {
// Given we check the Encrypted State checkbox
await page.getByRole("button", { name: "New conversation", exact: true }).click();
await page.getByRole("menuitem", { name: "New room" }).click();
await expect(page.getByRole("switch", { name: "Enable end-to-end encryption" })).toBeChecked();
await page.getByRole("switch", { name: "Encrypt state events" }).click();
await expect(page.getByRole("switch", { name: "Encrypt state events" })).toBeChecked();
// When we create a room
await page.getByRole("textbox", { name: "Name" }).fill(name);
await page.getByRole("button", { name: "Create room" }).click();
// Then we created an encrypted state room
await expect(page.getByText("State encryption enabled")).toBeVisible();
// And it has the correct name
await expect(page.getByTestId("timeline").getByRole("heading", { name })).toBeVisible();
// And the room name state event is encrypted
await viewSourceOnRoomNameEvent(page);
await expect(page.getByText("Decrypted event source")).toBeVisible();
},
);
test(
"creates a room without encrypted state if we don't check the box",
{ tag: "@screenshot" },
async ({ page, user: _user }) => {
// Given we did not check the Encrypted State checkbox
await page.getByRole("button", { name: "New conversation", exact: true }).click();
await page.getByRole("menuitem", { name: "New room" }).click();
await expect(page.getByRole("switch", { name: "Enable end-to-end encryption" })).toBeChecked();
// And it is off by default
await expect(page.getByRole("switch", { name: "Encrypt state events" })).not.toBeChecked();
// When we create a room
await page.getByRole("textbox", { name: "Name" }).fill(name);
await page.getByRole("button", { name: "Create room" }).click();
// Then we created a normal encrypted room, without encrypted state
await expect(page.getByText("Encryption enabled")).toBeVisible();
await expect(page.getByText("State encryption enabled")).not.toBeVisible();
// And it has the correct name
await expect(page.getByTestId("timeline").getByRole("heading", { name })).toBeVisible();
// And the room name state event is not encrypted
await viewSourceOnRoomNameEvent(page);
await expect(page.getByText("Original event source")).toBeVisible();
await expect(page.getByText("Decrypted event source")).not.toBeVisible();
},
);
});
}); });
async function viewSourceOnRoomNameEvent(page: Page) {
await page
.getByRole("listitem")
.filter({ hasText: "created and configured the room" })
.getByRole("button", { name: "expand" })
.click();
await page
.getByRole("listitem")
.filter({ hasText: "changed the room name to" })
.getByRole("button", { name: "Options" })
.click();
await page.getByRole("menuitem", { name: "View source" }).click();
}

View File

@@ -264,7 +264,6 @@ test.describe("Element Call", () => {
preset: "trusted_private_chat" as Preset.TrustedPrivateChat, preset: "trusted_private_chat" as Preset.TrustedPrivateChat,
invite: [bot.credentials.userId], invite: [bot.credentials.userId],
}); });
await bot.awaitRoomMembership(roomId);
await app.client.setAccountData("m.direct" as EventType.Direct, { await app.client.setAccountData("m.direct" as EventType.Direct, {
[bot.credentials.userId]: [roomId], [bot.credentials.userId]: [roomId],
}); });

View File

@@ -24,7 +24,7 @@ test.describe("PSTN", () => {
await toasts.rejectToast("Notifications"); await toasts.rejectToast("Notifications");
await toasts.assertNoToasts(); await toasts.assertNoToasts();
await expect(page.getByTestId("room-list-search")).toMatchScreenshot("dialpad-trigger.png"); await expect(page.locator(".mx_RoomListSearch")).toMatchScreenshot("dialpad-trigger.png");
await page.getByLabel("Open dial pad").click(); await page.getByLabel("Open dial pad").click();
await expect(page.locator(".mx_Dialog")).toMatchScreenshot("dialpad.png"); await expect(page.locator(".mx_Dialog")).toMatchScreenshot("dialpad.png");
}); });

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 984 KiB

After

Width:  |  Height:  |  Size: 984 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 166 KiB

View File

@@ -10,7 +10,7 @@ import {
type StartedPostgreSqlContainer, type StartedPostgreSqlContainer,
} from "@element-hq/element-web-playwright-common/lib/testcontainers"; } from "@element-hq/element-web-playwright-common/lib/testcontainers";
const TAG = "main@sha256:2c5966c2ff06458ac5cbae959f12e19d30e3ebb63c641d31ec1ae08abccb9c6d"; const TAG = "main@sha256:70ca0df3b7a8a92ebb6a679286c626084107b41f0fcceeb3f8ae43983d874474";
/** /**
* MatrixAuthenticationServiceContainer which freezes the docker digest to * MatrixAuthenticationServiceContainer which freezes the docker digest to

View File

@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
import { SynapseContainer as BaseSynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers"; import { SynapseContainer as BaseSynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers";
const TAG = "develop@sha256:7c3dce1d2b44fdc4b1494c5b8f4792018733ad323f823b88aac30c883d09fb35"; const TAG = "develop@sha256:ac511632cf3b91b27a2c9f2274edd9eb8777fb0a521982db7ee4e4e386dde62f";
/** /**
* SynapseContainer which freezes the docker digest to stabilise tests, * SynapseContainer which freezes the docker digest to stabilise tests,

View File

@@ -79,7 +79,6 @@
@import "./structures/_SearchBox.pcss"; @import "./structures/_SearchBox.pcss";
@import "./structures/_SpaceHierarchy.pcss"; @import "./structures/_SpaceHierarchy.pcss";
@import "./structures/_SpacePanel.pcss"; @import "./structures/_SpacePanel.pcss";
@import "./structures/_SpacePillButton.pcss";
@import "./structures/_SpaceRoomView.pcss"; @import "./structures/_SpaceRoomView.pcss";
@import "./structures/_SplashPage.pcss"; @import "./structures/_SplashPage.pcss";
@import "./structures/_TabbedView.pcss"; @import "./structures/_TabbedView.pcss";
@@ -274,6 +273,7 @@
@import "./views/rooms/RoomListPanel/_RoomListItemView.pcss"; @import "./views/rooms/RoomListPanel/_RoomListItemView.pcss";
@import "./views/rooms/RoomListPanel/_RoomListPanel.pcss"; @import "./views/rooms/RoomListPanel/_RoomListPanel.pcss";
@import "./views/rooms/RoomListPanel/_RoomListPrimaryFilters.pcss"; @import "./views/rooms/RoomListPanel/_RoomListPrimaryFilters.pcss";
@import "./views/rooms/RoomListPanel/_RoomListSearch.pcss";
@import "./views/rooms/RoomListPanel/_RoomListSecondaryFilters.pcss"; @import "./views/rooms/RoomListPanel/_RoomListSecondaryFilters.pcss";
@import "./views/rooms/RoomListPanel/_RoomListSkeleton.pcss"; @import "./views/rooms/RoomListPanel/_RoomListSkeleton.pcss";
@import "./views/rooms/_AppsDrawer.pcss"; @import "./views/rooms/_AppsDrawer.pcss";
@@ -281,6 +281,7 @@
@import "./views/rooms/_AuxPanel.pcss"; @import "./views/rooms/_AuxPanel.pcss";
@import "./views/rooms/_BasicMessageComposer.pcss"; @import "./views/rooms/_BasicMessageComposer.pcss";
@import "./views/rooms/_CallGuestLinkButton.pcss"; @import "./views/rooms/_CallGuestLinkButton.pcss";
@import "./views/rooms/_DecryptionFailureBar.pcss";
@import "./views/rooms/_E2EIcon.pcss"; @import "./views/rooms/_E2EIcon.pcss";
@import "./views/rooms/_E2EIconView.pcss"; @import "./views/rooms/_E2EIconView.pcss";
@import "./views/rooms/_EditMessageComposer.pcss"; @import "./views/rooms/_EditMessageComposer.pcss";
@@ -382,6 +383,7 @@
@import "./views/spaces/_SpaceBasicSettings.pcss"; @import "./views/spaces/_SpaceBasicSettings.pcss";
@import "./views/spaces/_SpaceChildrenPicker.pcss"; @import "./views/spaces/_SpaceChildrenPicker.pcss";
@import "./views/spaces/_SpaceCreateMenu.pcss"; @import "./views/spaces/_SpaceCreateMenu.pcss";
@import "./views/spaces/_SpacePublicShare.pcss";
@import "./views/terms/_InlineTermsAgreement.pcss"; @import "./views/terms/_InlineTermsAgreement.pcss";
@import "./views/toasts/_AnalyticsToast.pcss"; @import "./views/toasts/_AnalyticsToast.pcss";
@import "./views/toasts/_IncomingCallToast.pcss"; @import "./views/toasts/_IncomingCallToast.pcss";

View File

@@ -114,30 +114,67 @@ Please see LICENSE files in the repository root for full details.
margin-top: 12px; margin-top: 12px;
} }
.mx_LeftPanel_dialPadButton, .mx_LeftPanel_dialPadButton {
.mx_LeftPanel_exploreButton { width: 32px;
width: 20px; height: 32px;
height: 20px;
padding: var(--cpd-space-1-5x);
border-radius: 8px; border-radius: 8px;
background-color: $panel-actions; background-color: $panel-actions;
position: relative;
margin-left: 8px; margin-left: 8px;
svg { &::before {
width: inherit; content: "";
height: inherit; position: absolute;
display: block; top: 6px;
color: $secondary-content; left: 6px;
width: 20px;
height: 20px;
mask-image: url("@vector-im/compound-design-tokens/icons/dial-pad.svg");
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
background-color: $secondary-content;
}
}
.mx_LeftPanel_exploreButton,
.mx_LeftPanel_recentsButton {
width: 32px;
height: 32px;
border-radius: 8px;
background-color: $panel-actions;
position: relative;
margin-left: 8px;
&::before {
content: "";
position: absolute;
top: 8px;
left: 8px;
width: 16px;
height: 16px;
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
background-color: $secondary-content;
} }
&:hover { &:hover {
background-color: $tertiary-content; background-color: $tertiary-content;
svg { &::before {
color: $background; background-color: $background;
} }
} }
} }
.mx_LeftPanel_exploreButton::before {
mask-image: url("@vector-im/compound-design-tokens/icons/explore.svg");
}
.mx_LeftPanel_recentsButton::before {
mask-image: url("@vector-im/compound-design-tokens/icons/time.svg");
}
} }
.mx_LegacyRoomListHeader:first-child { .mx_LegacyRoomListHeader:first-child {
@@ -191,7 +228,8 @@ Please see LICENSE files in the repository root for full details.
background-color: transparent; background-color: transparent;
} }
.mx_LeftPanel_exploreButton { .mx_LeftPanel_exploreButton,
.mx_LeftPanel_recentsButton {
margin-left: 0; margin-left: 0;
margin-top: 8px; margin-top: 8px;
} }

View File

@@ -99,17 +99,34 @@ Please see LICENSE files in the repository root for full details.
position: relative; position: relative;
user-select: none; user-select: none;
& + .mx_AccessibleButton { &:nth-child(2) {
border-left: 1px solid $resend-button-divider-color; border-left: 1px solid $resend-button-divider-color;
} }
svg { &::before {
content: "";
position: absolute;
left: 10px; /* inset for regular button padding */ left: 10px; /* inset for regular button padding */
background-color: $muted-fg-color;
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
width: 18px; width: 18px;
height: 18px; height: 18px;
vertical-align: middle; top: 50%; /* text sizes are dynamic */
color: $muted-fg-color; transform: translateY(-50%);
}
&.mx_RoomStatusBar_unsentCancelAllBtn::before {
mask-image: url("@vector-im/compound-design-tokens/icons/delete.svg");
}
&.mx_RoomStatusBar_unsentRetry {
padding-left: 34px; /* 28px from above, but +6px to account for the wider icon */
&::before {
mask-image: url("@vector-im/compound-design-tokens/icons/restart.svg");
}
} }
} }

View File

@@ -76,8 +76,6 @@ Please see LICENSE files in the repository root for full details.
flex-direction: column; flex-direction: column;
flex: 1; flex: 1;
min-width: 0; min-width: 0;
container-type: size;
container-name: roomview;
.mx_RoomView_messagePanel, .mx_RoomView_messagePanel,
.mx_RoomView_messagePanelSpinner, .mx_RoomView_messagePanelSpinner,

View File

@@ -17,14 +17,13 @@ Please see LICENSE files in the repository root for full details.
.mx_SearchBox_closeButton { .mx_SearchBox_closeButton {
cursor: pointer; cursor: pointer;
height: 16px; mask-image: url("@vector-im/compound-design-tokens/icons/close.svg");
mask-repeat: no-repeat;
mask-position: center;
mask-size: 16px;
width: 16px; width: 16px;
height: 16px;
padding: 9px; padding: 9px;
background-color: var(--cpd-color-icon-secondary);
svg {
height: inherit;
width: inherit;
color: var(--cpd-color-icon-secondary);
}
} }
} }

View File

@@ -44,23 +44,29 @@ Please see LICENSE files in the repository root for full details.
top: 19px; /* v-align with avatar */ top: 19px; /* v-align with avatar */
right: -8px; right: -8px;
svg { &::before {
height: inherit; content: "";
position: absolute;
width: inherit; width: inherit;
display: inline-block; height: inherit;
color: $background; mask-position: center;
/* Slight alignment tweak to center the asset */ mask-size: contain;
margin-left: 1px; mask-repeat: no-repeat;
background-color: $background;
mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
transform: rotate(270deg);
} }
&:not(.expanded) { &:not(.expanded) {
opacity: 0; opacity: 0;
&::before {
mask-position: center 1px;
}
} }
&.expanded svg { &.expanded::before {
transform: rotate(180deg); transform: rotate(90deg);
/* Slight alignment tweak to center the asset */
margin-left: -1px;
} }
} }
@@ -97,6 +103,7 @@ Please see LICENSE files in the repository root for full details.
& > .mx_SpaceButton > .mx_SpaceButton_toggleCollapse { & > .mx_SpaceButton > .mx_SpaceButton_toggleCollapse {
padding: 0 10px; padding: 0 10px;
margin: 0 -10px; margin: 0 -10px;
transform: rotate(-90deg);
} }
& > .mx_SpaceTreeLevel { & > .mx_SpaceTreeLevel {
@@ -159,67 +166,109 @@ Please see LICENSE files in the repository root for full details.
} }
.mx_SpaceButton_toggleCollapse { .mx_SpaceButton_toggleCollapse {
height: 20px;
width: var(--gutterSize); width: var(--gutterSize);
flex-shrink: 0;
padding: 10px 0; padding: 10px 0;
min-width: var(--gutterSize);
svg { height: 20px;
width: 20px; mask-position: center;
height: inherit; mask-size: 20px;
display: inline-block; mask-repeat: no-repeat;
color: $tertiary-content; background-color: $tertiary-content;
/* Re-align with parent */ mask-image: url("@vector-im/compound-design-tokens/icons/chevron-down.svg");
margin-left: -3px;
}
} }
.mx_SpaceButton_icon { .mx_SpaceButton_icon {
/* Calculate height excluding padding to allow svg to inherit */ width: var(--height-topLevel);
width: calc(var(--height-topLevel) - 14px); min-width: var(--height-topLevel);
height: calc(var(--height-topLevel) - 14px); height: var(--height-topLevel);
flex-shrink: 0;
border-radius: 8px; border-radius: 8px;
padding: 7px; position: relative;
background-color: $panel-actions;
svg { &::before {
width: inherit; position: absolute;
height: inherit; content: "";
display: block; width: var(--height-topLevel);
color: $secondary-content; height: var(--height-topLevel);
top: 0;
left: 0;
mask-position: center;
mask-repeat: no-repeat;
mask-size: 18px;
} }
} }
&.mx_SpaceButton_new .mx_SpaceButton_icon { &.mx_SpaceButton_home,
background-color: unset; &.mx_SpaceButton_favourites,
&.mx_SpaceButton_people,
&.mx_SpaceButton_orphans,
&.mx_SpaceButton_videoRooms {
.mx_SpaceButton_icon {
background-color: $panel-actions;
svg { &::before {
color: $primary-content; background-color: $secondary-content;
}
}
}
&.mx_SpaceButton_withIcon .mx_SpaceButton_icon {
background-color: $panel-actions;
}
&.mx_SpaceButton_home .mx_SpaceButton_icon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/home-solid.svg");
}
&.mx_SpaceButton_favourites .mx_SpaceButton_icon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/favourite-solid.svg");
}
&.mx_SpaceButton_people .mx_SpaceButton_icon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/user-profile-solid.svg");
}
&.mx_SpaceButton_orphans .mx_SpaceButton_icon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/room.svg");
}
&.mx_SpaceButton_videoRooms .mx_SpaceButton_icon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/video-call-solid.svg");
}
&.mx_SpaceButton_new .mx_SpaceButton_icon {
&::before {
background-color: $primary-content;
mask-image: url("@vector-im/compound-design-tokens/icons/plus.svg");
transition: all 0.2s ease-in-out; /* TODO transition */ transition: all 0.2s ease-in-out; /* TODO transition */
} }
} }
&.mx_SpaceButton_newCancel .mx_SpaceButton_icon svg { &.mx_SpaceButton_newCancel .mx_SpaceButton_icon::before {
transform: rotate(45deg); transform: rotate(45deg);
} }
.mx_SpaceButton_menuButton { .mx_SpaceButton_menuButton {
width: 16px; width: 20px;
height: 16px; min-width: 20px; /* yay flex */
padding: var(--cpd-space-0-5x); height: 20px;
flex-shrink: 0;
margin-top: auto; margin-top: auto;
margin-bottom: auto; margin-bottom: auto;
display: none; display: none;
position: absolute; position: absolute;
right: 4px; right: 4px;
svg { &::before {
width: inherit; top: 3px;
height: inherit; left: 2px;
display: block; content: "";
color: $primary-content; width: 16px;
height: 16px;
position: absolute;
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
mask-image: url("@vector-im/compound-design-tokens/icons/overflow-horizontal.svg");
background: $primary-content;
} }
} }
} }
@@ -292,6 +341,18 @@ Please see LICENSE files in the repository root for full details.
padding: 0 0 16px 0; padding: 0 0 16px 0;
scrollbar-gutter: stable; scrollbar-gutter: stable;
& > .mx_SpaceButton {
height: var(--height-topLevel);
&.mx_SpaceButton_active::before {
height: var(--height-topLevel);
}
}
& > ul {
padding-left: 0;
}
&.mx_IndicatorScrollbar_topOverflow { &.mx_IndicatorScrollbar_topOverflow {
mask-image: linear-gradient(to bottom, transparent, black 16px); mask-image: linear-gradient(to bottom, transparent, black 16px);
} }

View File

@@ -1,48 +0,0 @@
/*
Copyright 2025 Element Creations 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_SpacePillButton {
position: relative;
padding: 16px 32px 16px 72px;
width: 432px;
box-sizing: border-box;
border-radius: 8px;
border: 1px solid $input-border-color;
font-size: $font-17px;
font-weight: var(--cpd-font-weight-semibold);
margin: 20px 0;
> div {
margin-top: 4px;
font-weight: normal;
font-size: $font-15px;
color: $secondary-content;
}
svg {
position: absolute;
content: "";
width: 28px;
height: 28px;
top: 50%;
transform: translateY(-50%);
left: 22px;
color: $tertiary-content;
}
&:hover {
border-color: var(--cpd-color-bg-action-primary-rest);
svg {
color: var(--cpd-color-icon-primary);
}
> span {
color: $primary-content;
}
}
}

View File

@@ -6,6 +6,51 @@ 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.
*/ */
@define-mixin SpacePillButton {
position: relative;
padding: 16px 32px 16px 72px;
width: 432px;
box-sizing: border-box;
border-radius: 8px;
border: 1px solid $input-border-color;
font-size: $font-17px;
font-weight: var(--cpd-font-weight-semibold);
margin: 20px 0;
> div {
margin-top: 4px;
font-weight: normal;
font-size: $font-15px;
color: $secondary-content;
}
&::before {
position: absolute;
content: "";
width: 28px;
height: 28px;
top: 50%;
transform: translateY(-50%);
left: 22px;
mask-position: center;
mask-repeat: no-repeat;
mask-size: 28px;
background-color: $tertiary-content;
}
&:hover {
border-color: var(--cpd-color-bg-action-primary-rest);
&::before {
background-color: var(--cpd-color-icon-primary);
}
> span {
color: $primary-content;
}
}
}
.mx_SpaceRoomView { .mx_SpaceRoomView {
--innerWidth: 428px; --innerWidth: 428px;
@@ -162,7 +207,7 @@ Please see LICENSE files in the repository root for full details.
width: 16px; width: 16px;
background: var(--cpd-color-icon-on-solid-primary); background: var(--cpd-color-icon-on-solid-primary);
mask-size: 16px; mask-size: 16px;
mask-image: url("@vector-im/compound-design-tokens/icons/user-add.svg"); mask-image: url("$(res)/img/element-icons/room/invite.svg");
} }
} }
@@ -197,6 +242,20 @@ Please see LICENSE files in the repository root for full details.
} }
} }
.mx_SpaceRoomView_privateScope {
> .mx_AccessibleButton {
@mixin SpacePillButton;
}
.mx_SpaceRoomView_privateScope_justMeButton::before {
mask-image: url("@vector-im/compound-design-tokens/icons/user-profile-solid.svg");
}
.mx_SpaceRoomView_privateScope_meAndMyTeammatesButton::before {
mask-image: url("@vector-im/compound-design-tokens/icons/group.svg");
}
}
.mx_SpaceRoomView_inviteTeammates { .mx_SpaceRoomView_inviteTeammates {
.mx_SpaceRoomView_inviteTeammates_buttons { .mx_SpaceRoomView_inviteTeammates_buttons {
color: $secondary-content; color: $secondary-content;
@@ -232,7 +291,7 @@ Please see LICENSE files in the repository root for full details.
text-decoration: underline; text-decoration: underline;
&::before { &::before {
mask-image: url("@vector-im/compound-design-tokens/icons/user-add.svg"); mask-image: url("$(res)/img/element-icons/room/invite.svg");
background-color: var(--cpd-color-icon-primary); background-color: var(--cpd-color-icon-primary);
} }
} }

View File

@@ -32,7 +32,7 @@ Please see LICENSE files in the repository root for full details.
/* mask to dither resulting combined gradient */ /* mask to dither resulting combined gradient */
url("$(res)/img/noise.png"), url("$(res)/img/noise.png"),
/* gradient to apply different amounts of dithering to different parts of the gradient */ /* gradient to apply different amounts of dithering to different parts of the gradient */
linear-gradient( linear-gradient(
to bottom, to bottom,
/* 10% dithering at the top */ rgb(0, 0, 0, 0.9) 20%, /* 10% dithering at the top */ rgb(0, 0, 0, 0.9) 20%,
/* 80% dithering at the bottom */ rgb(0, 0, 0, 0.2) 100% /* 80% dithering at the bottom */ rgb(0, 0, 0, 0.2) 100%

View File

@@ -41,10 +41,42 @@ Please see LICENSE files in the repository root for full details.
padding: var(--cpd-space-3x); padding: var(--cpd-space-3x);
&.mx_Toast_hasIcon { &.mx_Toast_hasIcon {
svg { &::before,
&::after {
content: "";
width: 22px; width: 22px;
height: 22px; height: 22px;
grid-column: 1; grid-column: 1;
grid-row: 1;
mask-size: 100%;
mask-position: center;
mask-repeat: no-repeat;
background-size: 100%;
background-repeat: no-repeat;
}
&.mx_Toast_icon_verification::after {
mask-image: url("@vector-im/compound-design-tokens/icons/lock-solid.svg");
background-color: $primary-content;
}
&.mx_Toast_icon_verification_warning {
/* white infill for the hollow svg mask */
&::before {
background-color: #ffffff;
mask-image: url("@vector-im/compound-design-tokens/icons/lock-solid.svg");
mask-size: 80%;
}
&::after {
mask-image: url("@vector-im/compound-design-tokens/icons/error-solid.svg");
background-color: $e2e-warning-color;
}
}
&.mx_Toast_icon_key_storage::after {
mask-image: url("@vector-im/compound-design-tokens/icons/settings-solid.svg");
background-color: $primary-content;
} }
.mx_Toast_title, .mx_Toast_title,

View File

@@ -7,44 +7,12 @@ Please see LICENSE files in the repository root for full details.
*/ */
.mx_UploadBar { .mx_UploadBar {
/* line up with the shield area in the composer */ padding-left: 65px; /* line up with the shield area in the composer */
padding: 5px 21px 0; padding-top: 5px;
position: relative; position: relative;
display: grid;
grid-template:
"icon filename cancel" auto
"progress progress progress" auto
/ min-content auto min-content;
gap: var(--cpd-space-1-5x);
& > svg {
grid-area: icon;
height: 18px;
width: 18px;
color: $muted-fg-color;
align-self: center;
}
.mx_UploadBar_filename {
grid-area: filename;
color: $muted-fg-color;
position: relative;
font-size: $font-15px;
vertical-align: middle;
}
.mx_UploadBar_cancel {
grid-area: cancel;
height: 16px;
width: 16px;
color: $muted-fg-color;
align-self: center;
}
.mx_ProgressBar { .mx_ProgressBar {
grid-area: progress; width: calc(100% - 40px); /* cheating at a right margin */
width: 100%;
} }
} }
@@ -53,3 +21,39 @@ Please see LICENSE files in the repository root for full details.
padding-left: 0; padding-left: 0;
} }
} }
.mx_UploadBar_filename {
color: $muted-fg-color;
position: relative;
padding-right: 38px; /* 32px for cancel icon, 6px for padding */
padding-left: 22px; /* 18px for icon, 4px for padding */
font-size: $font-15px;
vertical-align: middle;
&::before {
content: "";
height: 18px;
width: 18px;
position: absolute;
top: 0;
left: 0;
mask-repeat: no-repeat;
mask-position: center;
background-color: $muted-fg-color;
mask-image: url("@vector-im/compound-design-tokens/icons/share.svg");
}
}
.mx_UploadBar_cancel {
position: absolute;
top: 0;
right: 0;
height: 16px;
width: 16px;
margin-right: 16px; /* align over rightmost button in composer */
margin-top: 5px;
mask-repeat: no-repeat;
mask-position: center;
background-color: $muted-fg-color;
mask-image: url("@vector-im/compound-design-tokens/icons/close.svg");
}

View File

@@ -119,4 +119,8 @@ Please see LICENSE files in the repository root for full details.
.mx_IconizedContextMenu_icon svg { .mx_IconizedContextMenu_icon svg {
color: $icon-button-color; color: $icon-button-color;
} }
.mx_UserMenu_iconMessage::before {
mask-image: url("$(res)/img/element-icons/feedback.svg");
}
} }

View File

@@ -11,16 +11,17 @@ Please see LICENSE files in the repository root for full details.
align-items: center; align-items: center;
} }
.mx_E2EIcon.mx_CompleteSecurity_headerIcon { .mx_CompleteSecurity_headerIcon {
width: 24px; width: 24px;
height: 24px; height: 24px;
margin-right: 4px; margin-right: 4px;
display: inline-block; position: relative;
} }
.mx_E2EIcon.mx_CompleteSecurity_heroIcon { .mx_CompleteSecurity_heroIcon {
width: 128px; width: 128px;
height: 128px; height: 128px;
position: relative;
margin: 0 auto; margin: 0 auto;
} }

View File

@@ -237,7 +237,7 @@ Please see LICENSE files in the repository root for full details.
font-size: $font-15px; font-size: $font-15px;
line-height: $font-24px; line-height: $font-24px;
.mx_InlineSpinner svg { .mx_InlineSpinner img {
vertical-align: sub; vertical-align: sub;
margin-right: 5px; margin-right: 5px;
} }

View File

@@ -32,20 +32,27 @@ Please see LICENSE files in the repository root for full details.
} }
.mx_ConfirmSpaceUserActionDialog_warning { .mx_ConfirmSpaceUserActionDialog_warning {
position: relative;
border-radius: 8px; border-radius: 8px;
padding: 12px 8px; padding: 12px 8px 12px 42px;
background-color: $header-panel-bg-color; background-color: $header-panel-bg-color;
font-size: $font-12px; font-size: $font-12px;
line-height: $font-15px; line-height: $font-15px;
color: $secondary-content; color: $secondary-content;
svg { &::before {
content: "";
position: absolute;
left: 10px;
top: calc(50% - 8px); /* vertical centering */
height: 16px; height: 16px;
width: 16px; width: 16px;
vertical-align: -4px; background-color: $secondary-content;
margin-right: var(--cpd-space-1-5x); mask-repeat: no-repeat;
color: $secondary-content; mask-size: contain;
mask-image: url("@vector-im/compound-design-tokens/icons/info-solid.svg");
mask-position: center;
} }
} }
} }

View File

@@ -55,17 +55,33 @@ Please see LICENSE files in the repository root for full details.
text-decoration: underline; text-decoration: underline;
} }
& > svg { &::before,
&::after {
content: "";
position: absolute; position: absolute;
width: 40px;
height: 40px;
left: 16px; left: 16px;
top: 12px; top: 12px;
padding: var(--cpd-space-2x); }
width: 24px;
height: 24px; &::before {
background-color: $icon-button-color; background-color: $icon-button-color;
color: $avatar-initial-color;
border-radius: 8px; border-radius: 8px;
} }
&::after {
background: $avatar-initial-color; /* TODO */
mask-position: center;
mask-size: 24px;
mask-repeat: no-repeat;
}
}
.mx_FeedbackDialog_reportBug {
&::after {
mask-image: url("$(res)/img/feather-customised/bug.svg");
}
} }
.mx_FeedbackDialog_rateApp { .mx_FeedbackDialog_rateApp {
@@ -109,5 +125,9 @@ Please see LICENSE files in the repository root for full details.
font-size: 24px; font-size: 24px;
border-color: var(--cpd-color-bg-action-primary-rest); border-color: var(--cpd-color-bg-action-primary-rest);
} }
&::after {
mask-image: url("$(res)/img/element-icons/feedback.svg");
}
} }
} }

View File

@@ -28,9 +28,9 @@ Please see LICENSE files in the repository root for full details.
} }
.mx_InviteDialog_goButton { .mx_InviteDialog_goButton {
min-width: 86px; min-width: 48px;
margin-inline-start: 10px; margin-inline-start: 10px;
height: 41px; height: 25px;
line-height: $font-25px; line-height: $font-25px;
} }
} }
@@ -223,6 +223,14 @@ Please see LICENSE files in the repository root for full details.
margin-inline-start: auto; margin-inline-start: auto;
} }
.mx_InviteDialog_userDirectoryIcon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/user-profile-solid.svg");
}
.mx_InviteDialog_dialPadIcon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/dial-pad.svg");
}
.mx_InviteDialog_tile { .mx_InviteDialog_tile {
cursor: pointer; cursor: pointer;
display: grid; display: grid;

View File

@@ -18,14 +18,19 @@ Please see LICENSE files in the repository root for full details.
min-height: 32px; min-height: 32px;
> div { > div {
padding-left: var(--cpd-space-1-5x); padding-left: 30px;
position: relative;
svg { &::before {
width: 16px; content: "";
position: absolute;
height: 16px; height: 16px;
color: $secondary-content; width: 16px;
vertical-align: -2px; left: 6px;
margin-right: var(--cpd-space-1-5x); top: 8px;
mask-repeat: no-repeat;
mask-position: center;
background-color: $secondary-content;
} }
&.mx_JoinRuleDropdown_knock::before { &.mx_JoinRuleDropdown_knock::before {
@@ -34,6 +39,22 @@ Please see LICENSE files in the repository root for full details.
} }
} }
.mx_JoinRuleDropdown_invite::before {
box-sizing: border-box;
mask-image: url("@vector-im/compound-design-tokens/icons/lock-solid.svg");
mask-size: contain;
padding: 1px;
}
.mx_JoinRuleDropdown_public::before {
mask-image: url("@vector-im/compound-design-tokens/icons/public.svg");
}
.mx_JoinRuleDropdown_restricted::before {
mask-image: url("@vector-im/compound-design-tokens/icons/group.svg");
mask-size: contain;
}
.mx_JoinRuleDropdown_icon { .mx_JoinRuleDropdown_icon {
color: $secondary-content; color: $secondary-content;
position: absolute; position: absolute;

View File

@@ -25,21 +25,28 @@ Please see LICENSE files in the repository root for full details.
overflow-y: auto; overflow-y: auto;
.mx_LeaveSpaceDialog_section_warning { .mx_LeaveSpaceDialog_section_warning {
position: relative;
border-radius: 8px; border-radius: 8px;
margin: 12px 0 0; margin: 12px 0 0;
padding: 12px 8px; padding: 12px 8px 12px 42px;
background-color: $header-panel-bg-color; background-color: $header-panel-bg-color;
font-size: $font-12px; font-size: $font-12px;
line-height: $font-15px; line-height: $font-15px;
color: $secondary-content; color: $secondary-content;
svg { &::before {
content: "";
position: absolute;
left: 10px;
top: calc(50% - 8px); /* vertical centering */
height: 16px; height: 16px;
width: 16px; width: 16px;
color: $secondary-content; background-color: $secondary-content;
vertical-align: middle; mask-repeat: no-repeat;
margin: 0 var(--cpd-space-1x); mask-size: contain;
mask-image: url("@vector-im/compound-design-tokens/icons/info-solid.svg");
mask-position: center;
} }
} }

View File

@@ -87,19 +87,25 @@ Please see LICENSE files in the repository root for full details.
position: relative; position: relative;
border-radius: 8px; border-radius: 8px;
margin: 12px 0; margin: 12px 0;
padding: 8px; padding: 8px 8px 8px 42px;
background-color: $header-panel-bg-color; background-color: $header-panel-bg-color;
font-size: $font-12px; font-size: $font-12px;
line-height: $font-15px; line-height: $font-15px;
color: $secondary-content; color: $secondary-content;
svg { &::before {
content: "";
position: absolute;
left: 10px;
top: calc(50% - 8px); /* vertical centering */
height: 16px; height: 16px;
width: 16px; width: 16px;
color: $secondary-content; background-color: $secondary-content;
vertical-align: middle; mask-repeat: no-repeat;
margin: 0 var(--cpd-space-1x); mask-size: contain;
mask-image: url("@vector-im/compound-design-tokens/icons/info-solid.svg");
mask-position: center;
} }
} }

View File

@@ -5,6 +5,46 @@ 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.
*/ */
/* ICONS */
/* ========================================================== */
.mx_RoomSettingsDialog_settingsIcon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/settings-solid.svg");
}
.mx_RoomSettingsDialog_voiceIcon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/voice-call-solid.svg");
}
.mx_RoomSettingsDialog_securityIcon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/lock-solid.svg");
}
.mx_RoomSettingsDialog_rolesIcon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/admin.svg");
}
.mx_RoomSettingsDialog_notificationsIcon::before {
mask-image: url("$(res)/img/element-icons/notifications.svg");
}
.mx_RoomSettingsDialog_bridgesIcon::before {
/* This icon is pants, please improve :) */
mask-image: url("$(res)/img/feather-customised/bridge.svg");
}
.mx_RoomSettingsDialog_pollsIcon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/polls.svg");
}
.mx_RoomSettingsDialog_warningIcon::before {
mask-image: url("$(res)/img/element-icons/room/settings/advanced.svg");
}
.mx_RoomSettingsDialog_peopleIcon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/group.svg");
}
.mx_RoomSettingsDialog .mx_Dialog_title { .mx_RoomSettingsDialog .mx_Dialog_title {
-ms-text-overflow: ellipsis; -ms-text-overflow: ellipsis;
text-overflow: ellipsis; text-overflow: ellipsis;

View File

@@ -64,7 +64,7 @@ Please see LICENSE files in the repository root for full details.
} }
.mx_AccessibleButton_kind_primary { .mx_AccessibleButton_kind_primary {
width: calc(100% - 26px); width: calc(100% - 64px);
margin: 0 8px; margin: 0 8px;
padding: 15px 18px; padding: 15px 18px;
} }

View File

@@ -26,3 +26,7 @@ Please see LICENSE files in the repository root for full details.
} }
} }
} }
.mx_SpacePreferencesDialog_appearanceIcon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/visibility-on.svg");
}

View File

@@ -71,4 +71,14 @@ Please see LICENSE files in the repository root for full details.
} }
} }
} }
.mx_TabbedView_tabLabel {
.mx_SpaceSettingsDialog_generalIcon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/settings-solid.svg");
}
.mx_SpaceSettingsDialog_visibilityIcon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/visibility-on.svg");
}
}
} }

View File

@@ -454,7 +454,7 @@ Please see LICENSE files in the repository root for full details.
} }
.mx_SpotlightDialog_searchMessages::before { .mx_SpotlightDialog_searchMessages::before {
mask-image: url("@vector-im/compound-design-tokens/icons/chat.svg"); mask-image: url("$(res)/img/element-icons/room/search-inset.svg");
} }
.mx_SpotlightDialog_otherSearches_messageSearchText { .mx_SpotlightDialog_otherSearches_messageSearchText {

View File

@@ -10,12 +10,20 @@ Please see LICENSE files in the repository root for full details.
} }
.mx_KeyBackupFailedDialog_title { .mx_KeyBackupFailedDialog_title {
position: relative;
padding-left: 45px;
padding-bottom: 10px; padding-bottom: 10px;
svg { &::before {
margin-right: var(--cpd-space-2x); mask-image: url("@vector-im/compound-design-tokens/icons/error.svg");
vertical-align: -2px; mask-repeat: no-repeat;
color: $primary-content; background-color: $primary-content;
content: "";
position: absolute;
top: -6px;
right: 0;
bottom: 0;
left: 0;
} }
} }

Some files were not shown because too many files have changed in this diff Show More