Move ToastContext and utilities to shared components (#33949)
* Move ToastContext and utilities to shared components * lint * fix type * cleanup * fix broken test * fix lint * Add more tests for ToastContext * Potential fix for pull request finding 'Unused variable, import, function or class' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
parent
6ce24e2668
commit
0ddc418cf9
@@ -23,6 +23,7 @@ import LockIcon from "@vector-im/compound-design-tokens/assets/web/icons/lock";
|
|||||||
import LabsIcon from "@vector-im/compound-design-tokens/assets/web/icons/labs";
|
import LabsIcon from "@vector-im/compound-design-tokens/assets/web/icons/labs";
|
||||||
import BlockIcon from "@vector-im/compound-design-tokens/assets/web/icons/block";
|
import BlockIcon from "@vector-im/compound-design-tokens/assets/web/icons/block";
|
||||||
import HelpIcon from "@vector-im/compound-design-tokens/assets/web/icons/help";
|
import HelpIcon from "@vector-im/compound-design-tokens/assets/web/icons/help";
|
||||||
|
import { ToastContext, useActiveToast } from "@element-hq/web-shared-components";
|
||||||
|
|
||||||
import TabbedView, { Tab, useActiveTabWithDefault } from "../../structures/TabbedView";
|
import TabbedView, { Tab, useActiveTabWithDefault } from "../../structures/TabbedView";
|
||||||
import { _t, _td } from "../../../languageHandler";
|
import { _t, _td } from "../../../languageHandler";
|
||||||
@@ -46,7 +47,6 @@ import { type NonEmptyArray } from "../../../@types/common";
|
|||||||
import { SDKContext, type SdkContextClass } from "../../../contexts/SDKContext";
|
import { SDKContext, type SdkContextClass } from "../../../contexts/SDKContext";
|
||||||
import { useSettingValue } from "../../../hooks/useSettings";
|
import { useSettingValue } from "../../../hooks/useSettings";
|
||||||
import { NoChange, useEventEmitterAsyncState, type AsyncStateCallbackResult } from "../../../hooks/useEventEmitter";
|
import { NoChange, useEventEmitterAsyncState, type AsyncStateCallbackResult } from "../../../hooks/useEventEmitter";
|
||||||
import { ToastContext, useActiveToast } from "../../../contexts/ToastContext";
|
|
||||||
import { EncryptionUserSettingsTab, type State } from "../settings/tabs/user/EncryptionUserSettingsTab";
|
import { EncryptionUserSettingsTab, type State } from "../settings/tabs/user/EncryptionUserSettingsTab";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
|
|||||||
@@ -11,14 +11,13 @@ import { logger } from "matrix-js-sdk/src/logger";
|
|||||||
import { EditInPlace, Alert, ErrorMessage } from "@vector-im/compound-web";
|
import { EditInPlace, Alert, ErrorMessage } from "@vector-im/compound-web";
|
||||||
import PopOutIcon from "@vector-im/compound-design-tokens/assets/web/icons/pop-out";
|
import PopOutIcon from "@vector-im/compound-design-tokens/assets/web/icons/pop-out";
|
||||||
import SignOutIcon from "@vector-im/compound-design-tokens/assets/web/icons/sign-out";
|
import SignOutIcon from "@vector-im/compound-design-tokens/assets/web/icons/sign-out";
|
||||||
import { Flex } from "@element-hq/web-shared-components";
|
import { Flex, useToastContext } from "@element-hq/web-shared-components";
|
||||||
|
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import { OwnProfileStore } from "../../../stores/OwnProfileStore";
|
import { OwnProfileStore } from "../../../stores/OwnProfileStore";
|
||||||
import AvatarSetting from "./AvatarSetting";
|
import AvatarSetting from "./AvatarSetting";
|
||||||
import PosthogTrackers from "../../../PosthogTrackers";
|
import PosthogTrackers from "../../../PosthogTrackers";
|
||||||
import { formatBytes } from "../../../utils/FormattingUtils";
|
import { formatBytes } from "../../../utils/FormattingUtils";
|
||||||
import { useToastContext } from "../../../contexts/ToastContext";
|
|
||||||
import InlineSpinner from "../elements/InlineSpinner";
|
import InlineSpinner from "../elements/InlineSpinner";
|
||||||
import UserIdentifierCustomisations from "../../../customisations/UserIdentifier";
|
import UserIdentifierCustomisations from "../../../customisations/UserIdentifier";
|
||||||
import CopyableText from "../elements/CopyableText";
|
import CopyableText from "../elements/CopyableText";
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ import { type MatrixClient, type UploadResponse } from "matrix-js-sdk/src/matrix
|
|||||||
import { mocked } from "jest-mock";
|
import { mocked } from "jest-mock";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { TooltipProvider } from "@vector-im/compound-web";
|
import { TooltipProvider } from "@vector-im/compound-web";
|
||||||
|
import { ToastContext, type ToastRack } from "@element-hq/web-shared-components";
|
||||||
|
|
||||||
import UserProfileSettings from "../../../../../src/components/views/settings/UserProfileSettings";
|
import UserProfileSettings from "../../../../../src/components/views/settings/UserProfileSettings";
|
||||||
import { mkStubRoom, stubClient } from "../../../../test-utils";
|
import { mkStubRoom, stubClient } from "../../../../test-utils";
|
||||||
import { ToastContext, type ToastRack } from "../../../../../src/contexts/ToastContext";
|
|
||||||
import { OwnProfileStore } from "../../../../../src/stores/OwnProfileStore";
|
import { OwnProfileStore } from "../../../../../src/stores/OwnProfileStore";
|
||||||
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext";
|
||||||
import Modal from "../../../../../src/Modal";
|
import Modal from "../../../../../src/Modal";
|
||||||
@@ -72,7 +72,7 @@ const renderProfileSettings = (toastRack: Partial<ToastRack>, client: MatrixClie
|
|||||||
return render(
|
return render(
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<MatrixClientContext.Provider value={client}>
|
<MatrixClientContext.Provider value={client}>
|
||||||
<ToastContext.Provider value={toastRack}>
|
<ToastContext.Provider value={toastRack as ToastRack}>
|
||||||
<UserProfileSettings canSetAvatar={true} canSetDisplayName={true} />
|
<UserProfileSettings canSetAvatar={true} canSetDisplayName={true} />
|
||||||
</ToastContext.Provider>
|
</ToastContext.Provider>
|
||||||
</MatrixClientContext.Provider>
|
</MatrixClientContext.Provider>
|
||||||
|
|||||||
+4
-1
@@ -12,6 +12,7 @@ import { type MatrixClient, ThreepidMedium } from "matrix-js-sdk/src/matrix";
|
|||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import userEvent from "@testing-library/user-event";
|
import userEvent from "@testing-library/user-event";
|
||||||
import { type MockedObject } from "jest-mock-vitest-adapter";
|
import { type MockedObject } from "jest-mock-vitest-adapter";
|
||||||
|
import { ToastContext, ToastRack } from "@element-hq/web-shared-components";
|
||||||
|
|
||||||
import AccountUserSettingsTab from "../../../../../../../src/components/views/settings/tabs/user/AccountUserSettingsTab";
|
import AccountUserSettingsTab from "../../../../../../../src/components/views/settings/tabs/user/AccountUserSettingsTab";
|
||||||
import { SdkContextClass, SDKContext } from "../../../../../../../src/contexts/SDKContext";
|
import { SdkContextClass, SDKContext } from "../../../../../../../src/contexts/SDKContext";
|
||||||
@@ -54,7 +55,9 @@ describe("<AccountUserSettingsTab />", () => {
|
|||||||
const getComponent = () => (
|
const getComponent = () => (
|
||||||
<MatrixClientContext.Provider value={mockClient}>
|
<MatrixClientContext.Provider value={mockClient}>
|
||||||
<SDKContext.Provider value={stores}>
|
<SDKContext.Provider value={stores}>
|
||||||
<AccountUserSettingsTab {...defaultProps} />
|
<ToastContext.Provider value={new ToastRack()}>
|
||||||
|
<AccountUserSettingsTab {...defaultProps} />
|
||||||
|
</ToastContext.Provider>
|
||||||
</SDKContext.Provider>
|
</SDKContext.Provider>
|
||||||
</MatrixClientContext.Provider>
|
</MatrixClientContext.Provider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2024 New Vector Ltd.
|
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
||||||
Please see LICENSE files in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { ToastRack } from "../../../src/contexts/ToastContext";
|
|
||||||
|
|
||||||
describe("ToastRack", () => {
|
|
||||||
it("should return a toast once one is displayed", () => {
|
|
||||||
const toastRack = new ToastRack();
|
|
||||||
toastRack.displayToast("Hello, world!");
|
|
||||||
|
|
||||||
expect(toastRack.getActiveToast()).toBe("Hello, world!");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("calls update callback when a toast is added", () => {
|
|
||||||
const toastRack = new ToastRack();
|
|
||||||
const updateCallbackFn = jest.fn();
|
|
||||||
toastRack.setCallback(updateCallbackFn);
|
|
||||||
toastRack.displayToast("Hello, world!");
|
|
||||||
|
|
||||||
expect(updateCallbackFn).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("removes toast when remove function is called", () => {
|
|
||||||
const toastRack = new ToastRack();
|
|
||||||
const removeFn = toastRack.displayToast("Hello, world!");
|
|
||||||
expect(toastRack.getActiveToast()).toBe("Hello, world!");
|
|
||||||
removeFn();
|
|
||||||
expect(toastRack.getActiveToast()).toBeUndefined();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 New Vector Ltd.
|
||||||
|
Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, it, expect, vitest } from "vitest";
|
||||||
|
import { render, waitFor } from "@testing-library/react";
|
||||||
|
import { Toast } from "@vector-im/compound-web";
|
||||||
|
import React, { type JSX } from "react";
|
||||||
|
|
||||||
|
import { ToastContext, ToastRack, useToastContext } from "./ToastContext";
|
||||||
|
|
||||||
|
describe("ToastRack", () => {
|
||||||
|
it("should return a toast once one is displayed", () => {
|
||||||
|
const toastRack = new ToastRack();
|
||||||
|
toastRack.displayToast("Hello, world!");
|
||||||
|
|
||||||
|
expect(toastRack.getActiveToast()).toBe("Hello, world!");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls update callback when a toast is added", () => {
|
||||||
|
const toastRack = new ToastRack();
|
||||||
|
const updateCallbackFn = vitest.fn();
|
||||||
|
toastRack.setCallback(updateCallbackFn);
|
||||||
|
toastRack.displayToast("Hello, world!");
|
||||||
|
|
||||||
|
expect(updateCallbackFn).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes toast when remove function is called", () => {
|
||||||
|
const toastRack = new ToastRack();
|
||||||
|
const removeFn = toastRack.displayToast("Hello, world!");
|
||||||
|
expect(toastRack.getActiveToast()).toBe("Hello, world!");
|
||||||
|
removeFn();
|
||||||
|
expect(toastRack.getActiveToast()).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("useToastContext", () => {
|
||||||
|
it("should fail if there is no context wrapper", () => {
|
||||||
|
function FailingComponent(): JSX.Element {
|
||||||
|
useToastContext();
|
||||||
|
return <p>Test</p>;
|
||||||
|
}
|
||||||
|
expect(() => render(<FailingComponent />)).toThrow(
|
||||||
|
"Component must be wrapped in <ToastContext.Provider /> to use useToastContext",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("should succeed if there is a context wrapper", async () => {
|
||||||
|
function HappyComponent(): JSX.Element {
|
||||||
|
const toaster = useToastContext();
|
||||||
|
return <>{toaster.getActiveToast()}</>;
|
||||||
|
}
|
||||||
|
const rack = new ToastRack();
|
||||||
|
rack.displayToast(<Toast>Toast!</Toast>);
|
||||||
|
const { getByText } = render(
|
||||||
|
<ToastContext value={rack}>
|
||||||
|
<HappyComponent />
|
||||||
|
</ToastContext>,
|
||||||
|
);
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(getByText("Toast!")).toBeInViewport();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
+12
-8
@@ -1,10 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2024 New Vector Ltd.
|
* Copyright 2024 New Vector Ltd.
|
||||||
Copyright 2024 The Matrix.org Foundation C.I.C.
|
* Copyright 2024 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
* 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.
|
* Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { type ReactNode, createContext, useCallback, useContext, useEffect, useState, useMemo } from "react";
|
import { type ReactNode, createContext, useCallback, useContext, useEffect, useState, useMemo } from "react";
|
||||||
|
|
||||||
@@ -18,14 +18,18 @@ import { type ReactNode, createContext, useCallback, useContext, useEffect, useS
|
|||||||
* corner of the app, however the name 'toast' as used in this class refers to the component
|
* corner of the app, however the name 'toast' as used in this class refers to the component
|
||||||
* of the same name in compound that it is written to manage.
|
* of the same name in compound that it is written to manage.
|
||||||
*/
|
*/
|
||||||
export const ToastContext = createContext(null as any);
|
export const ToastContext = createContext<ToastRack | null>(null);
|
||||||
ToastContext.displayName = "ToastContext";
|
ToastContext.displayName = "ToastContext";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the ToastRack in context in order to display toasts
|
* Returns the ToastRack in context in order to display toasts
|
||||||
*/
|
*/
|
||||||
export function useToastContext(): ToastRack {
|
export function useToastContext(): ToastRack {
|
||||||
return useContext(ToastContext);
|
const rack = useContext(ToastContext);
|
||||||
|
if (!rack) {
|
||||||
|
throw new Error("Component must be wrapped in <ToastContext.Provider /> to use useToastContext");
|
||||||
|
}
|
||||||
|
return rack;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,6 +86,7 @@ export * from "./core/utils/humanize";
|
|||||||
export * from "./core/utils/DateUtils";
|
export * from "./core/utils/DateUtils";
|
||||||
export * from "./core/utils/numbers";
|
export * from "./core/utils/numbers";
|
||||||
export * from "./core/utils/FormattingUtils";
|
export * from "./core/utils/FormattingUtils";
|
||||||
|
export * from "./core/utils/ToastContext.tsx";
|
||||||
export * from "./core/i18n/I18nApi";
|
export * from "./core/i18n/I18nApi";
|
||||||
export * from "./core/utils/linkify";
|
export * from "./core/utils/linkify";
|
||||||
export type * from "./core/userStatus.ts";
|
export type * from "./core/userStatus.ts";
|
||||||
|
|||||||
Reference in New Issue
Block a user