Implement customisations & login component Module API 1.11.0 (#32687)

* Update to Module API v1.11.0

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Remove stale state field to make CQL happy

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Bump npm dep

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Improve coverage

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Improve coverage

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Update comment

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski
2026-03-10 13:30:48 +00:00
committed by GitHub
parent 78b40a6fed
commit 35afc2fdf8
10 changed files with 159 additions and 63 deletions
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details. Please see LICENSE files in the repository root for full details.
*/ */
import React, { type JSX, type ReactNode } from "react"; import React, { type JSX, memo, type ReactNode } from "react";
import classNames from "classnames"; import classNames from "classnames";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { type SSOFlow, SSOAction } from "matrix-js-sdk/src/matrix"; import { type SSOFlow, SSOAction } from "matrix-js-sdk/src/matrix";
@@ -32,6 +32,7 @@ import AccessibleButton, { type ButtonEvent } from "../../views/elements/Accessi
import { type ValidatedServerConfig } from "../../../utils/ValidatedServerConfig"; import { type ValidatedServerConfig } from "../../../utils/ValidatedServerConfig";
import { filterBoolean } from "../../../utils/arrays"; import { filterBoolean } from "../../../utils/arrays";
import { startOidcLogin } from "../../../utils/oidc/authorize"; import { startOidcLogin } from "../../../utils/oidc/authorize";
import { ModuleApi } from "../../../modules/Api.ts";
interface IProps { interface IProps {
serverConfig: ValidatedServerConfig; serverConfig: ValidatedServerConfig;
@@ -45,13 +46,15 @@ interface IProps {
defaultDeviceDisplayName?: string; defaultDeviceDisplayName?: string;
fragmentAfterLogin?: string; fragmentAfterLogin?: string;
defaultUsername?: string; defaultUsername?: string;
// Any additional content to show, will be rendered between main actions & footer actions
children?: ReactNode;
// Called when the user has logged in. Params: // Called when the user has logged in. Params:
// - The object returned by the login API // - The object returned by the login API
onLoggedIn(data: IMatrixClientCreds): void; onLoggedIn(data: IMatrixClientCreds): void;
// login shouldn't know or care how registration, password recovery, etc is done. // login shouldn't know or care how registration, password recovery, etc is done.
onRegisterClick(): void; onRegisterClick?(): void;
onForgotPasswordClick?(): void; onForgotPasswordClick?(): void;
onServerConfigChange(config: ValidatedServerConfig): void; onServerConfigChange(config: ValidatedServerConfig): void;
} }
@@ -61,8 +64,6 @@ interface IState {
busyLoggingIn?: boolean; busyLoggingIn?: boolean;
errorText?: ReactNode; errorText?: ReactNode;
loginIncorrect: boolean; loginIncorrect: boolean;
// can we attempt to log in or are there validation errors?
canTryLogin: boolean;
flows?: ClientLoginFlow[]; flows?: ClientLoginFlow[];
@@ -88,7 +89,7 @@ type OnPasswordLogin = {
/* /*
* A wire component which glues together login UI components and Login logic * A wire component which glues together login UI components and Login logic
*/ */
export default class LoginComponent extends React.PureComponent<IProps, IState> { class LoginComponent extends React.PureComponent<IProps, IState> {
private unmounted = false; private unmounted = false;
private loginLogic!: Login; private loginLogic!: Login;
@@ -101,7 +102,6 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
busy: false, busy: false,
errorText: null, errorText: null,
loginIncorrect: false, loginIncorrect: false,
canTryLogin: true,
username: props.defaultUsername ? props.defaultUsername : "", username: props.defaultUsername ? props.defaultUsername : "",
phoneCountry: "", phoneCountry: "",
@@ -229,7 +229,6 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
username: username, username: username,
busy: doWellknownLookup, busy: doWellknownLookup,
errorText: null, errorText: null,
canTryLogin: true,
}); });
if (doWellknownLookup) { if (doWellknownLookup) {
const serverName = username.split(":").slice(1).join(":"); const serverName = username.split(":").slice(1).join(":");
@@ -281,7 +280,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
public onRegisterClick = (ev: ButtonEvent): void => { public onRegisterClick = (ev: ButtonEvent): void => {
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
this.props.onRegisterClick(); this.props.onRegisterClick?.();
}; };
public onTryRegisterClick = (ev: ButtonEvent): void => { public onTryRegisterClick = (ev: ButtonEvent): void => {
@@ -379,7 +378,6 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
this.setState({ this.setState({
errorText: messageForConnectionError(err, this.props.serverConfig), errorText: messageForConnectionError(err, this.props.serverConfig),
loginIncorrect: false, loginIncorrect: false,
canTryLogin: false,
}); });
}, },
) )
@@ -512,7 +510,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
)} )}
</div> </div>
); );
} else if (SettingsStore.getValue(UIFeature.Registration)) { } else if (this.props.onRegisterClick && SettingsStore.getValue(UIFeature.Registration)) {
footer = ( footer = (
<span className="mx_AuthBody_changeFlow"> <span className="mx_AuthBody_changeFlow">
{_t( {_t(
@@ -546,9 +544,21 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
disabled={this.isBusy()} disabled={this.isBusy()}
/> />
{this.renderLoginComponentForFlows()} {this.renderLoginComponentForFlows()}
{this.props.children}
{footer} {footer}
</AuthBody> </AuthBody>
</AuthPage> </AuthPage>
); );
} }
} }
const WrappedLoginComponent = memo((props: IProps): JSX.Element => {
const moduleRenderer = ModuleApi.instance.customComponents.loginComponentRenderer;
if (moduleRenderer) {
return moduleRenderer(props, (props) => <LoginComponent {...props} />);
}
return <LoginComponent {...props} />;
});
export default WrappedLoginComponent;
@@ -8,7 +8,12 @@ Please see LICENSE files in the repository root for full details.
import { type UIComponent } from "../../settings/UIFeature"; import { type UIComponent } from "../../settings/UIFeature";
import { ComponentVisibilityCustomisations } from "../ComponentVisibility"; import { ComponentVisibilityCustomisations } from "../ComponentVisibility";
import { ModuleApi } from "../../modules/Api.ts";
export function shouldShowComponent(component: UIComponent): boolean { export function shouldShowComponent(component: UIComponent): boolean {
return ComponentVisibilityCustomisations.shouldShowComponent?.(component) ?? true; return (
ModuleApi.instance.customisations.shouldShowComponent(component) ??
ComponentVisibilityCustomisations.shouldShowComponent?.(component) ??
true
);
} }
+2
View File
@@ -31,6 +31,7 @@ import { ElementWebBuiltinsApi } from "./BuiltinsApi.tsx";
import { ClientApi } from "./ClientApi.ts"; import { ClientApi } from "./ClientApi.ts";
import { StoresApi } from "./StoresApi.ts"; import { StoresApi } from "./StoresApi.ts";
import { WidgetLifecycleApi } from "./WidgetLifecycleApi.ts"; import { WidgetLifecycleApi } from "./WidgetLifecycleApi.ts";
import { CustomisationsApi } from "./customisationsApi.ts";
const legacyCustomisationsFactory = <T extends object>(baseCustomisations: T) => { const legacyCustomisationsFactory = <T extends object>(baseCustomisations: T) => {
let used = false; let used = false;
@@ -84,6 +85,7 @@ export class ModuleApi implements Api {
public readonly config = new ConfigApi(); public readonly config = new ConfigApi();
public readonly i18n = new I18nApi(); public readonly i18n = new I18nApi();
public readonly customComponents = new CustomComponentsApi(); public readonly customComponents = new CustomComponentsApi();
public readonly customisations = new CustomisationsApi();
public readonly extras = new ElementWebExtrasApi(); public readonly extras = new ElementWebExtrasApi();
public readonly builtins = new ElementWebBuiltinsApi(); public readonly builtins = new ElementWebBuiltinsApi();
public readonly widgetLifecycle = new WidgetLifecycleApi(); public readonly widgetLifecycle = new WidgetLifecycleApi();
@@ -16,6 +16,7 @@ import type {
CustomMessageRenderHints as ModuleCustomCustomMessageRenderHints, CustomMessageRenderHints as ModuleCustomCustomMessageRenderHints,
MatrixEvent as ModuleMatrixEvent, MatrixEvent as ModuleMatrixEvent,
CustomRoomPreviewBarRenderFunction, CustomRoomPreviewBarRenderFunction,
CustomLoginRenderFunction,
} from "@element-hq/element-web-module-api"; } from "@element-hq/element-web-module-api";
import type React from "react"; import type React from "react";
@@ -153,4 +154,21 @@ export class CustomComponentsApi implements ICustomComponentsApi {
public registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void { public registerRoomPreviewBar(renderer: CustomRoomPreviewBarRenderFunction): void {
this._roomPreviewBarRenderer = renderer; this._roomPreviewBarRenderer = renderer;
} }
private _loginRenderer?: CustomLoginRenderFunction;
/**
* Get the custom login component renderer, if any has been registered.
*/
public get loginComponentRenderer(): CustomLoginRenderFunction | undefined {
return this._loginRenderer;
}
/**
* Register a custom login component renderer.
* @param renderer - the function that will render the login component.
*/
public registerLoginComponent(renderer: CustomLoginRenderFunction): void {
this._loginRenderer = renderer;
}
} }
+35
View File
@@ -0,0 +1,35 @@
/*
Copyright 2026 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.
*/
import type { UIComponent, CustomisationsApi as ICustomisationsApi } from "@element-hq/element-web-module-api";
export class CustomisationsApi implements ICustomisationsApi {
private shouldShowComponentFunctions = new Set<(component: UIComponent) => boolean | void>();
/**
* Method to register a callback which can affect whether a given component is drawn or not.
* @param fn - the callback, if it returns true the component will be rendered, if false it will not be.
* If undefined will defer to the next callback, ultimately falling through to `true` if none return false.
* The next callback is decided in FIFO call order to this register function.
*/
public registerShouldShowComponent(fn: (this: void, component: UIComponent) => boolean | void): void {
this.shouldShowComponentFunctions.add(fn);
}
/**
* Method to check whether, according to any registered modules, a given component should be rendered.
* @param component - the component to check
*/
public shouldShowComponent(component: UIComponent): boolean | void {
for (const fn of this.shouldShowComponentFunctions) {
const v = fn(component);
if (typeof v === "boolean") {
return v;
}
}
}
}
+1 -41
View File
@@ -28,44 +28,4 @@ export const enum UIFeature {
AllowCreatingPublicSpaces = "UIFeature.allowCreatingPublicSpaces", AllowCreatingPublicSpaces = "UIFeature.allowCreatingPublicSpaces",
} }
export enum UIComponent { export { UIComponent } from "@element-hq/element-web-module-api";
/**
* Components that lead to a user being invited.
*/
InviteUsers = "UIComponent.sendInvites",
/**
* Components that lead to a room being created that aren't already
* guarded by some other condition (ie: "only if you can edit this
* space" is *not* guarded by this component, but "start DM" is).
*/
CreateRooms = "UIComponent.roomCreation",
/**
* Components that lead to a Space being created that aren't already
* guarded by some other condition (ie: "only if you can add subspaces"
* is *not* guarded by this component, but "create new space" is).
*/
CreateSpaces = "UIComponent.spaceCreation",
/**
* Components that lead to the public room directory.
*/
ExploreRooms = "UIComponent.exploreRooms",
/**
* Components that lead to the user being able to easily add widgets
* and integrations to the room, such as from the room information card.
*/
AddIntegrations = "UIComponent.addIntegrations",
/**
* Component that lead to the user being able to search, dial, explore rooms
*/
FilterContainer = "UIComponent.filterContainer",
/**
* Components that lead the user to room options menu.
*/
RoomOptionsMenu = "UIComponent.roomOptionsMenu",
}
@@ -20,6 +20,7 @@ import Login from "../../../../../src/components/structures/auth/Login";
import type BasePlatform from "../../../../../src/BasePlatform"; import type BasePlatform from "../../../../../src/BasePlatform";
import * as registerClientUtils from "../../../../../src/utils/oidc/registerClient"; import * as registerClientUtils from "../../../../../src/utils/oidc/registerClient";
import { makeDelegatedAuthConfig } from "../../../../test-utils/oidc"; import { makeDelegatedAuthConfig } from "../../../../test-utils/oidc";
import { ModuleApi } from "../../../../../src/modules/Api.ts";
jest.useRealTimers(); jest.useRealTimers();
@@ -100,6 +101,35 @@ describe("Login", function () {
expect(container.querySelector(".mx_ServerPicker_change")).toBeTruthy(); expect(container.querySelector(".mx_ServerPicker_change")).toBeTruthy();
}); });
it("should show register button", async () => {
const onRegisterClick = jest.fn();
const { getByText } = render(
<Login
serverConfig={mkServerConfig("https://matrix.org", "https://vector.im")}
onLoggedIn={() => {}}
onRegisterClick={onRegisterClick}
onServerConfigChange={() => {}}
/>,
);
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading…"));
fireEvent.click(getByText("Create an account"));
expect(onRegisterClick).toHaveBeenCalled();
});
it("should hide register button", async () => {
const { queryByText } = render(
<Login
serverConfig={mkServerConfig("https://matrix.org", "https://vector.im")}
onLoggedIn={() => {}}
onServerConfigChange={() => {}}
/>,
);
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading…"));
expect(queryByText("Create an account")).not.toBeInTheDocument();
});
it("should show form without change server link when custom URLs disabled", async () => { it("should show form without change server link when custom URLs disabled", async () => {
const { container } = getComponent(); const { container } = getComponent();
await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading…")); await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading…"));
@@ -417,4 +447,16 @@ describe("Login", function () {
expect(screen.getByText("Continue")).toBeInTheDocument(); expect(screen.getByText("Continue")).toBeInTheDocument();
}); });
}); });
describe("Module API", () => {
afterEach(() => {
ModuleApi.instance.customComponents.registerLoginComponent(undefined as any);
});
it("should use registered module renderer", async () => {
ModuleApi.instance.customComponents.registerLoginComponent(() => <>Test component</>);
const { getByText } = getComponent();
expect(getByText("Test component")).toBeTruthy();
});
});
}); });
@@ -0,0 +1,24 @@
/*
Copyright 2026 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.
*/
import { CustomisationsApi } from "../../../src/modules/customisationsApi";
import { UIComponent } from "../../../src/settings/UIFeature.ts";
describe("CustomisationsApi", () => {
let api: CustomisationsApi;
beforeEach(() => {
api = new CustomisationsApi();
});
it("should register a shouldShowComponent callback", () => {
const shouldShowComponent = jest.fn().mockReturnValue(true);
api.registerShouldShowComponent(shouldShowComponent);
expect(api.shouldShowComponent(UIComponent.CreateRooms)).toBe(true);
expect(shouldShowComponent).toHaveBeenCalledWith("UIComponent.roomCreation");
});
});
+10 -10
View File
@@ -7,8 +7,8 @@ settings:
catalogs: catalogs:
default: default:
'@element-hq/element-web-module-api': '@element-hq/element-web-module-api':
specifier: 1.10.0 specifier: 1.11.0
version: 1.10.0 version: 1.11.0
'@element-hq/element-web-playwright-common': '@element-hq/element-web-playwright-common':
specifier: 2.2.7 specifier: 2.2.7
version: 2.2.7 version: 2.2.7
@@ -149,7 +149,7 @@ importers:
version: 7.28.6 version: 7.28.6
'@element-hq/element-web-module-api': '@element-hq/element-web-module-api':
specifier: 'catalog:' specifier: 'catalog:'
version: 1.10.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4) version: 1.11.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4)
'@element-hq/web-shared-components': '@element-hq/web-shared-components':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/shared-components version: link:../../packages/shared-components
@@ -423,7 +423,7 @@ importers:
version: 0.16.3 version: 0.16.3
'@element-hq/element-web-playwright-common': '@element-hq/element-web-playwright-common':
specifier: 'catalog:' specifier: 'catalog:'
version: 2.2.7(@element-hq/element-web-module-api@1.10.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4))(@playwright/test@1.58.2)(playwright-core@1.58.2) version: 2.2.7(@element-hq/element-web-module-api@1.11.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4))(@playwright/test@1.58.2)(playwright-core@1.58.2)
'@element-hq/element-web-playwright-common-local': '@element-hq/element-web-playwright-common-local':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/playwright-common version: link:../../packages/playwright-common
@@ -762,7 +762,7 @@ importers:
dependencies: dependencies:
'@element-hq/element-web-module-api': '@element-hq/element-web-module-api':
specifier: 'catalog:' specifier: 'catalog:'
version: 1.10.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4) version: 1.11.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4)
'@matrix-org/spec': '@matrix-org/spec':
specifier: ^1.7.0 specifier: ^1.7.0
version: 1.16.0 version: 1.16.0
@@ -2016,8 +2016,8 @@ packages:
'@element-hq/element-call-embedded@0.16.3': '@element-hq/element-call-embedded@0.16.3':
resolution: {integrity: sha512-OViKJonDaDNVBUW9WdV9mk78/Ruh34C7XsEgt3O8D9z+64C39elbIgllHSoH5S12IRlv9RYrrV37FZLo6QWsDQ==} resolution: {integrity: sha512-OViKJonDaDNVBUW9WdV9mk78/Ruh34C7XsEgt3O8D9z+64C39elbIgllHSoH5S12IRlv9RYrrV37FZLo6QWsDQ==}
'@element-hq/element-web-module-api@1.10.0': '@element-hq/element-web-module-api@1.11.0':
resolution: {integrity: sha512-XIl6E73dn0cmR/03TRCpq7epyFQAa93GUz1j7EBP2pv5Erh59gq788ajFM2XYl7W2afRU0aasvJoI3iZIHbRig==} resolution: {integrity: sha512-RBkvt+Z32CGLkiPtYcQTryQBjG01ZrSVV98CS1cPz/kTeBtxEbVBpqDoOLdGvpmVe0dWo4DLaFcldw2iK39TPA==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
peerDependencies: peerDependencies:
'@matrix-org/react-sdk-module-api': '*' '@matrix-org/react-sdk-module-api': '*'
@@ -11980,7 +11980,7 @@ snapshots:
'@element-hq/element-call-embedded@0.16.3': {} '@element-hq/element-call-embedded@0.16.3': {}
'@element-hq/element-web-module-api@1.10.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4)': '@element-hq/element-web-module-api@1.11.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4)':
dependencies: dependencies:
'@types/react': 19.2.10 '@types/react': 19.2.10
'@types/react-dom': 19.2.3(@types/react@19.2.10) '@types/react-dom': 19.2.3(@types/react@19.2.10)
@@ -11989,10 +11989,10 @@ snapshots:
'@matrix-org/react-sdk-module-api': 2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4) '@matrix-org/react-sdk-module-api': 2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4)
matrix-web-i18n: 3.6.0 matrix-web-i18n: 3.6.0
'@element-hq/element-web-playwright-common@2.2.7(@element-hq/element-web-module-api@1.10.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4))(@playwright/test@1.58.2)(playwright-core@1.58.2)': '@element-hq/element-web-playwright-common@2.2.7(@element-hq/element-web-module-api@1.11.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4))(@playwright/test@1.58.2)(playwright-core@1.58.2)':
dependencies: dependencies:
'@axe-core/playwright': 4.11.1(playwright-core@1.58.2) '@axe-core/playwright': 4.11.1(playwright-core@1.58.2)
'@element-hq/element-web-module-api': 1.10.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4) '@element-hq/element-web-module-api': 1.11.0(@matrix-org/react-sdk-module-api@2.5.0(patch_hash=016146c9cc96e6363609d2b2ac0896ccef567882eb1d73b75a77b8a30929de96)(react@19.2.4))(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(matrix-web-i18n@3.6.0)(react@19.2.4)
'@playwright/test': 1.58.2 '@playwright/test': 1.58.2
'@testcontainers/postgresql': 11.11.0 '@testcontainers/postgresql': 11.11.0
glob: 13.0.6 glob: 13.0.6
+1 -1
View File
@@ -17,7 +17,7 @@ catalog:
"@playwright/test": 1.58.2 "@playwright/test": 1.58.2
"playwright-core": 1.58.2 "playwright-core": 1.58.2
# Module API # Module API
"@element-hq/element-web-module-api": 1.10.0 "@element-hq/element-web-module-api": 1.11.0
# Compound # Compound
"@vector-im/compound-design-tokens": 6.10.1 "@vector-im/compound-design-tokens": 6.10.1
"@vector-im/compound-web": 8.4.0 "@vector-im/compound-web": 8.4.0