You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2026-01-03 17:02:28 +03:00
move unverified email alert to header
This commit is contained in:
committed by
Quentin Gliech
parent
a3b7cc27bf
commit
7bc13ab536
@@ -0,0 +1,18 @@
|
|||||||
|
/* Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
margin-top: var(--cpd-space-4x);
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// @vitest-environment happy-dom
|
||||||
|
|
||||||
|
import { render, cleanup, fireEvent } from "@testing-library/react";
|
||||||
|
import { describe, it, expect, afterEach } from "vitest";
|
||||||
|
|
||||||
|
import { makeFragmentData } from "../../gql/fragment-masking";
|
||||||
|
import { WithLocation } from "../../test-utils/WithLocation";
|
||||||
|
|
||||||
|
import UnverifiedEmailAlert, {
|
||||||
|
UNVERIFIED_EMAILS_FRAGMENT,
|
||||||
|
} from "./UnverifiedEmailAlert";
|
||||||
|
|
||||||
|
describe("<UnverifiedEmailAlert />", () => {
|
||||||
|
afterEach(cleanup);
|
||||||
|
|
||||||
|
it("does not render a warning when there are no unverified emails", () => {
|
||||||
|
const data = makeFragmentData(
|
||||||
|
{
|
||||||
|
id: "abc123",
|
||||||
|
unverifiedEmails: {
|
||||||
|
totalCount: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UNVERIFIED_EMAILS_FRAGMENT,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { container } = render(
|
||||||
|
<WithLocation>
|
||||||
|
<UnverifiedEmailAlert unverifiedEmails={data} />
|
||||||
|
</WithLocation>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(container).toMatchInlineSnapshot("<div />");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders a warning when there are unverified emails", () => {
|
||||||
|
const data = makeFragmentData(
|
||||||
|
{
|
||||||
|
id: "abc123",
|
||||||
|
unverifiedEmails: {
|
||||||
|
totalCount: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UNVERIFIED_EMAILS_FRAGMENT,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { container } = render(
|
||||||
|
<WithLocation>
|
||||||
|
<UnverifiedEmailAlert unverifiedEmails={data} />
|
||||||
|
</WithLocation>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("hides warning after it has been dismissed", () => {
|
||||||
|
const data = makeFragmentData(
|
||||||
|
{
|
||||||
|
id: "abc123",
|
||||||
|
unverifiedEmails: {
|
||||||
|
totalCount: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UNVERIFIED_EMAILS_FRAGMENT,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { container, getByText, getByLabelText } = render(
|
||||||
|
<WithLocation>
|
||||||
|
<UnverifiedEmailAlert unverifiedEmails={data} />
|
||||||
|
</WithLocation>,
|
||||||
|
);
|
||||||
|
|
||||||
|
// warning is rendered
|
||||||
|
expect(getByText("Unverified email")).toBeTruthy();
|
||||||
|
|
||||||
|
fireEvent.click(getByLabelText("Close"));
|
||||||
|
|
||||||
|
// no more warning
|
||||||
|
expect(container).toMatchInlineSnapshot("<div />");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Alert } from "@vector-im/compound-web";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
import { Link } from "../../Router";
|
||||||
|
import { FragmentType, useFragment } from "../../gql/fragment-masking";
|
||||||
|
import { graphql } from "../../gql/gql";
|
||||||
|
|
||||||
|
import styles from "./UnverifiedEmailAlert.module.css";
|
||||||
|
|
||||||
|
export const UNVERIFIED_EMAILS_FRAGMENT = graphql(/* GraphQL */ `
|
||||||
|
fragment UnverifiedEmailAlert on User {
|
||||||
|
id
|
||||||
|
unverifiedEmails: emails(first: 0, state: PENDING) {
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const UnverifiedEmailAlert: React.FC<{
|
||||||
|
unverifiedEmails?: FragmentType<typeof UNVERIFIED_EMAILS_FRAGMENT>;
|
||||||
|
}> = ({ unverifiedEmails }) => {
|
||||||
|
const data = useFragment(UNVERIFIED_EMAILS_FRAGMENT, unverifiedEmails);
|
||||||
|
const [dismiss, setDismiss] = useState(false);
|
||||||
|
|
||||||
|
const doDismiss = (): void => setDismiss(true);
|
||||||
|
|
||||||
|
if (!data?.unverifiedEmails?.totalCount || dismiss) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Alert
|
||||||
|
type="critical"
|
||||||
|
title="Unverified email"
|
||||||
|
onClose={doDismiss}
|
||||||
|
className={styles.alert}
|
||||||
|
>
|
||||||
|
You have {data.unverifiedEmails.totalCount} unverified email address(es).{" "}
|
||||||
|
<Link kind="button" route={{ type: "profile" }}>
|
||||||
|
Review and verify
|
||||||
|
</Link>
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UnverifiedEmailAlert;
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`<UnverifiedEmailAlert /> > renders a warning when there are unverified emails 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="_alert_fxw8y_19 _alert_d86cd2"
|
||||||
|
data-type="critical"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="_icon_fxw8y_52"
|
||||||
|
fill="currentColor"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M12 17a.97.97 0 0 0 .713-.288A.968.968 0 0 0 13 16a.968.968 0 0 0-.287-.713A.968.968 0 0 0 12 15a.968.968 0 0 0-.713.287A.968.968 0 0 0 11 16c0 .283.096.52.287.712.192.192.43.288.713.288Zm0-4c.283 0 .52-.096.713-.287A.968.968 0 0 0 13 12V8a.967.967 0 0 0-.287-.713A.968.968 0 0 0 12 7a.968.968 0 0 0-.713.287A.967.967 0 0 0 11 8v4c0 .283.096.52.287.713.192.191.43.287.713.287Zm0 9a9.738 9.738 0 0 1-3.9-.788 10.099 10.099 0 0 1-3.175-2.137c-.9-.9-1.612-1.958-2.137-3.175A9.738 9.738 0 0 1 2 12a9.74 9.74 0 0 1 .788-3.9 10.099 10.099 0 0 1 2.137-3.175c.9-.9 1.958-1.612 3.175-2.137A9.738 9.738 0 0 1 12 2a9.74 9.74 0 0 1 3.9.788 10.098 10.098 0 0 1 3.175 2.137c.9.9 1.613 1.958 2.137 3.175A9.738 9.738 0 0 1 22 12a9.738 9.738 0 0 1-.788 3.9 10.098 10.098 0 0 1-2.137 3.175c-.9.9-1.958 1.613-3.175 2.137A9.738 9.738 0 0 1 12 22Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<div
|
||||||
|
class="_content_fxw8y_44"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="_title_fxw8y_48"
|
||||||
|
>
|
||||||
|
Unverified email
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
You have
|
||||||
|
2
|
||||||
|
unverified email address(es).
|
||||||
|
|
||||||
|
<a
|
||||||
|
class="_linkButton_4f14fc"
|
||||||
|
href="/profile"
|
||||||
|
>
|
||||||
|
Review and verify
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<svg
|
||||||
|
aria-label="Close"
|
||||||
|
class="_close_fxw8y_68"
|
||||||
|
fill="currentColor"
|
||||||
|
height="16"
|
||||||
|
role="button"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="16"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="m12 13.4-4.9 4.9a.948.948 0 0 1-.7.275.948.948 0 0 1-.7-.275.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7l4.9-4.9-4.9-4.9a.948.948 0 0 1-.275-.7.95.95 0 0 1 .275-.7.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275l4.9 4.9 4.9-4.9a.948.948 0 0 1 .7-.275.95.95 0 0 1 .7.275.948.948 0 0 1 .275.7.948.948 0 0 1-.275.7L13.4 12l4.9 4.9a.948.948 0 0 1 .275.7.948.948 0 0 1-.275.7.948.948 0 0 1-.7.275.948.948 0 0 1-.7-.275L12 13.4Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
15
frontend/src/components/UnverifiedEmailAlert/index.ts
Normal file
15
frontend/src/components/UnverifiedEmailAlert/index.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
export { default } from "./UnverifiedEmailAlert";
|
||||||
@@ -18,6 +18,11 @@ import { atomFamily } from "jotai/utils";
|
|||||||
import { atomWithQuery } from "jotai-urql";
|
import { atomWithQuery } from "jotai-urql";
|
||||||
|
|
||||||
import { graphql } from "../gql";
|
import { graphql } from "../gql";
|
||||||
|
import { FragmentType } from "../gql/fragment-masking";
|
||||||
|
|
||||||
|
import UnverifiedEmailAlert, {
|
||||||
|
UNVERIFIED_EMAILS_FRAGMENT,
|
||||||
|
} from "./UnverifiedEmailAlert/UnverifiedEmailAlert";
|
||||||
|
|
||||||
const QUERY = graphql(/* GraphQL */ `
|
const QUERY = graphql(/* GraphQL */ `
|
||||||
query UserGreeting($userId: ID!) {
|
query UserGreeting($userId: ID!) {
|
||||||
@@ -29,6 +34,14 @@ const QUERY = graphql(/* GraphQL */ `
|
|||||||
displayName
|
displayName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
viewer {
|
||||||
|
__typename
|
||||||
|
|
||||||
|
... on User {
|
||||||
|
id
|
||||||
|
...UnverifiedEmailAlert
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
@@ -47,17 +60,26 @@ const UserGreeting: React.FC<{ userId: string }> = ({ userId }) => {
|
|||||||
if (result.data?.user) {
|
if (result.data?.user) {
|
||||||
const user = result.data.user;
|
const user = result.data.user;
|
||||||
return (
|
return (
|
||||||
<header className="text-center">
|
<>
|
||||||
<Avatar
|
<header className="text-center">
|
||||||
size="var(--cpd-space-24x)"
|
<Avatar
|
||||||
id={user.matrix.mxid}
|
size="var(--cpd-space-24x)"
|
||||||
name={user.matrix.displayName || user.matrix.mxid}
|
id={user.matrix.mxid}
|
||||||
|
name={user.matrix.displayName || user.matrix.mxid}
|
||||||
|
/>
|
||||||
|
<Heading size="xl" weight="semibold">
|
||||||
|
{user.matrix.displayName || user.username}
|
||||||
|
</Heading>
|
||||||
|
<Body size="lg">{user.matrix.mxid}</Body>
|
||||||
|
</header>
|
||||||
|
<UnverifiedEmailAlert
|
||||||
|
unverifiedEmails={
|
||||||
|
result.data?.viewer as FragmentType<
|
||||||
|
typeof UNVERIFIED_EMAILS_FRAGMENT
|
||||||
|
>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<Heading size="xl" weight="semibold">
|
</>
|
||||||
{user.matrix.displayName || user.username}
|
|
||||||
</Heading>
|
|
||||||
<Body size="lg">{user.matrix.mxid}</Body>
|
|
||||||
</header>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Alert, Body, H3, H6 } from "@vector-im/compound-web";
|
import { Body, H3, H6 } from "@vector-im/compound-web";
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
import { Link } from "../../Router";
|
import { Link } from "../../Router";
|
||||||
import { FragmentType, graphql, useFragment } from "../../gql";
|
import { FragmentType, graphql, useFragment } from "../../gql";
|
||||||
@@ -35,10 +34,6 @@ export const FRAGMENT = graphql(/* GraphQL */ `
|
|||||||
totalCount
|
totalCount
|
||||||
}
|
}
|
||||||
|
|
||||||
unverifiedEmails: emails(first: 0, state: PENDING) {
|
|
||||||
totalCount
|
|
||||||
}
|
|
||||||
|
|
||||||
browserSessions(first: 0, state: ACTIVE) {
|
browserSessions(first: 0, state: ACTIVE) {
|
||||||
totalCount
|
totalCount
|
||||||
}
|
}
|
||||||
@@ -57,11 +52,6 @@ const UserHome: React.FC<{
|
|||||||
user: FragmentType<typeof FRAGMENT>;
|
user: FragmentType<typeof FRAGMENT>;
|
||||||
}> = ({ user }) => {
|
}> = ({ user }) => {
|
||||||
const data = useFragment(FRAGMENT, user);
|
const data = useFragment(FRAGMENT, user);
|
||||||
const [dismiss, setDismiss] = useState(false);
|
|
||||||
|
|
||||||
const doDismiss = (): void => {
|
|
||||||
setDismiss(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
// allow this until we get i18n
|
// allow this until we get i18n
|
||||||
const pluraliseSession = (count: number): string =>
|
const pluraliseSession = (count: number): string =>
|
||||||
@@ -74,14 +64,7 @@ const UserHome: React.FC<{
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<BlockList>
|
<BlockList>
|
||||||
{data.unverifiedEmails.totalCount > 0 && !dismiss && (
|
|
||||||
<Alert type="critical" title="Unverified email" onClose={doDismiss}>
|
|
||||||
You have {data.unverifiedEmails.totalCount} unverified email
|
|
||||||
address(es). <Link route={{ type: "profile" }}>Check</Link>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
{/* This is a short term solution, so I won't bother extracting these blocks into components */}
|
{/* This is a short term solution, so I won't bother extracting these blocks into components */}
|
||||||
|
|
||||||
<H3>Where you're signed in</H3>
|
<H3>Where you're signed in</H3>
|
||||||
<Block className={styles.sessionListBlock}>
|
<Block className={styles.sessionListBlock}>
|
||||||
<div className={styles.sessionListBlockInfo}>
|
<div className={styles.sessionListBlockInfo}>
|
||||||
@@ -91,7 +74,9 @@ const UserHome: React.FC<{
|
|||||||
{pluraliseSession(data.browserSessions.totalCount)}
|
{pluraliseSession(data.browserSessions.totalCount)}
|
||||||
</Body>
|
</Body>
|
||||||
</div>
|
</div>
|
||||||
<Link route={{ type: "browser-session-list" }}>View all</Link>
|
<Link kind="button" route={{ type: "browser-session-list" }}>
|
||||||
|
View all
|
||||||
|
</Link>
|
||||||
</Block>
|
</Block>
|
||||||
<Block className={styles.sessionListBlock}>
|
<Block className={styles.sessionListBlock}>
|
||||||
<div className={styles.sessionListBlockInfo}>
|
<div className={styles.sessionListBlockInfo}>
|
||||||
@@ -101,7 +86,9 @@ const UserHome: React.FC<{
|
|||||||
{pluraliseSession(data.oauth2Sessions.totalCount)}
|
{pluraliseSession(data.oauth2Sessions.totalCount)}
|
||||||
</Body>
|
</Body>
|
||||||
</div>
|
</div>
|
||||||
<Link route={{ type: "oauth2-session-list" }}>View all</Link>
|
<Link kind="button" route={{ type: "oauth2-session-list" }}>
|
||||||
|
View all
|
||||||
|
</Link>
|
||||||
</Block>
|
</Block>
|
||||||
<Block className={styles.sessionListBlock}>
|
<Block className={styles.sessionListBlock}>
|
||||||
<div className={styles.sessionListBlockInfo}>
|
<div className={styles.sessionListBlockInfo}>
|
||||||
@@ -111,7 +98,9 @@ const UserHome: React.FC<{
|
|||||||
{pluraliseSession(data.compatSessions.totalCount)}
|
{pluraliseSession(data.compatSessions.totalCount)}
|
||||||
</Body>
|
</Body>
|
||||||
</div>
|
</div>
|
||||||
<Link route={{ type: "compat-session-list" }}>View all</Link>
|
<Link kind="button" route={{ type: "compat-session-list" }}>
|
||||||
|
View all
|
||||||
|
</Link>
|
||||||
</Block>
|
</Block>
|
||||||
</BlockList>
|
</BlockList>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ exports[`UserHome > render a <UserHome /> with sessions 1`] = `
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
|
className="_linkButton_4f14fc"
|
||||||
href="/sessions"
|
href="/sessions"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
@@ -57,6 +58,7 @@ exports[`UserHome > render a <UserHome /> with sessions 1`] = `
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
|
className="_linkButton_4f14fc"
|
||||||
href="/oauth2-sessions"
|
href="/oauth2-sessions"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
@@ -84,6 +86,7 @@ exports[`UserHome > render a <UserHome /> with sessions 1`] = `
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
|
className="_linkButton_4f14fc"
|
||||||
href="/compat-sessions"
|
href="/compat-sessions"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
@@ -123,6 +126,7 @@ exports[`UserHome > render an simple <UserHome /> 1`] = `
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
|
className="_linkButton_4f14fc"
|
||||||
href="/sessions"
|
href="/sessions"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
@@ -150,6 +154,7 @@ exports[`UserHome > render an simple <UserHome /> 1`] = `
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
|
className="_linkButton_4f14fc"
|
||||||
href="/oauth2-sessions"
|
href="/oauth2-sessions"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
@@ -177,6 +182,7 @@ exports[`UserHome > render an simple <UserHome /> 1`] = `
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
|
className="_linkButton_4f14fc"
|
||||||
href="/compat-sessions"
|
href="/compat-sessions"
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -37,15 +37,17 @@ const documents = {
|
|||||||
types.EndOAuth2SessionDocument,
|
types.EndOAuth2SessionDocument,
|
||||||
"\n query OAuth2SessionListQuery(\n $userId: ID!\n $state: Oauth2SessionState\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n oauth2Sessions(\n state: $state\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n cursor\n node {\n id\n ...OAuth2Session_session\n }\n }\n\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n":
|
"\n query OAuth2SessionListQuery(\n $userId: ID!\n $state: Oauth2SessionState\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n oauth2Sessions(\n state: $state\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n cursor\n node {\n id\n ...OAuth2Session_session\n }\n }\n\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n":
|
||||||
types.OAuth2SessionListQueryDocument,
|
types.OAuth2SessionListQueryDocument,
|
||||||
|
"\n fragment UnverifiedEmailAlert on User {\n id\n unverifiedEmails: emails(first: 0, state: PENDING) {\n totalCount\n }\n }\n":
|
||||||
|
types.UnverifiedEmailAlertFragmentDoc,
|
||||||
"\n fragment UserEmail_email on UserEmail {\n id\n email\n confirmedAt\n }\n":
|
"\n fragment UserEmail_email on UserEmail {\n id\n email\n confirmedAt\n }\n":
|
||||||
types.UserEmail_EmailFragmentDoc,
|
types.UserEmail_EmailFragmentDoc,
|
||||||
"\n mutation RemoveEmail($id: ID!) {\n removeEmail(input: { userEmailId: $id }) {\n status\n\n user {\n id\n }\n }\n }\n":
|
"\n mutation RemoveEmail($id: ID!) {\n removeEmail(input: { userEmailId: $id }) {\n status\n\n user {\n id\n }\n }\n }\n":
|
||||||
types.RemoveEmailDocument,
|
types.RemoveEmailDocument,
|
||||||
"\n mutation SetPrimaryEmail($id: ID!) {\n setPrimaryEmail(input: { userEmailId: $id }) {\n status\n user {\n id\n primaryEmail {\n id\n }\n }\n }\n }\n":
|
"\n mutation SetPrimaryEmail($id: ID!) {\n setPrimaryEmail(input: { userEmailId: $id }) {\n status\n user {\n id\n primaryEmail {\n id\n }\n }\n }\n }\n":
|
||||||
types.SetPrimaryEmailDocument,
|
types.SetPrimaryEmailDocument,
|
||||||
"\n query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n matrix {\n mxid\n displayName\n }\n }\n }\n":
|
"\n query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n matrix {\n mxid\n displayName\n }\n }\n viewer {\n __typename\n\n ... on User {\n id\n ...UnverifiedEmailAlert\n }\n }\n }\n":
|
||||||
types.UserGreetingDocument,
|
types.UserGreetingDocument,
|
||||||
"\n fragment UserHome_user on User {\n id\n\n primaryEmail {\n id\n ...UserEmail_email\n }\n\n confirmedEmails: emails(first: 0, state: CONFIRMED) {\n totalCount\n }\n\n unverifiedEmails: emails(first: 0, state: PENDING) {\n totalCount\n }\n\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n oauth2Sessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n compatSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n":
|
"\n fragment UserHome_user on User {\n id\n\n primaryEmail {\n id\n ...UserEmail_email\n }\n\n confirmedEmails: emails(first: 0, state: CONFIRMED) {\n totalCount\n }\n\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n oauth2Sessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n compatSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n":
|
||||||
types.UserHome_UserFragmentDoc,
|
types.UserHome_UserFragmentDoc,
|
||||||
"\n mutation AddEmail($userId: ID!, $email: String!) {\n addEmail(input: { userId: $userId, email: $email }) {\n status\n violations\n email {\n id\n ...UserEmail_email\n }\n }\n }\n":
|
"\n mutation AddEmail($userId: ID!, $email: String!) {\n addEmail(input: { userId: $userId, email: $email }) {\n status\n violations\n email {\n id\n ...UserEmail_email\n }\n }\n }\n":
|
||||||
types.AddEmailDocument,
|
types.AddEmailDocument,
|
||||||
@@ -157,6 +159,12 @@ export function graphql(
|
|||||||
export function graphql(
|
export function graphql(
|
||||||
source: "\n query OAuth2SessionListQuery(\n $userId: ID!\n $state: Oauth2SessionState\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n oauth2Sessions(\n state: $state\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n cursor\n node {\n id\n ...OAuth2Session_session\n }\n }\n\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n",
|
source: "\n query OAuth2SessionListQuery(\n $userId: ID!\n $state: Oauth2SessionState\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n oauth2Sessions(\n state: $state\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n cursor\n node {\n id\n ...OAuth2Session_session\n }\n }\n\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n",
|
||||||
): (typeof documents)["\n query OAuth2SessionListQuery(\n $userId: ID!\n $state: Oauth2SessionState\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n oauth2Sessions(\n state: $state\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n cursor\n node {\n id\n ...OAuth2Session_session\n }\n }\n\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"];
|
): (typeof documents)["\n query OAuth2SessionListQuery(\n $userId: ID!\n $state: Oauth2SessionState\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n oauth2Sessions(\n state: $state\n first: $first\n after: $after\n last: $last\n before: $before\n ) {\n edges {\n cursor\n node {\n id\n ...OAuth2Session_session\n }\n }\n\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n"];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(
|
||||||
|
source: "\n fragment UnverifiedEmailAlert on User {\n id\n unverifiedEmails: emails(first: 0, state: PENDING) {\n totalCount\n }\n }\n",
|
||||||
|
): (typeof documents)["\n fragment UnverifiedEmailAlert on User {\n id\n unverifiedEmails: emails(first: 0, state: PENDING) {\n totalCount\n }\n }\n"];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
@@ -179,14 +187,14 @@ export function graphql(
|
|||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(
|
export function graphql(
|
||||||
source: "\n query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n matrix {\n mxid\n displayName\n }\n }\n }\n",
|
source: "\n query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n matrix {\n mxid\n displayName\n }\n }\n viewer {\n __typename\n\n ... on User {\n id\n ...UnverifiedEmailAlert\n }\n }\n }\n",
|
||||||
): (typeof documents)["\n query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n matrix {\n mxid\n displayName\n }\n }\n }\n"];
|
): (typeof documents)["\n query UserGreeting($userId: ID!) {\n user(id: $userId) {\n id\n username\n matrix {\n mxid\n displayName\n }\n }\n viewer {\n __typename\n\n ... on User {\n id\n ...UnverifiedEmailAlert\n }\n }\n }\n"];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(
|
export function graphql(
|
||||||
source: "\n fragment UserHome_user on User {\n id\n\n primaryEmail {\n id\n ...UserEmail_email\n }\n\n confirmedEmails: emails(first: 0, state: CONFIRMED) {\n totalCount\n }\n\n unverifiedEmails: emails(first: 0, state: PENDING) {\n totalCount\n }\n\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n oauth2Sessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n compatSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n",
|
source: "\n fragment UserHome_user on User {\n id\n\n primaryEmail {\n id\n ...UserEmail_email\n }\n\n confirmedEmails: emails(first: 0, state: CONFIRMED) {\n totalCount\n }\n\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n oauth2Sessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n compatSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n",
|
||||||
): (typeof documents)["\n fragment UserHome_user on User {\n id\n\n primaryEmail {\n id\n ...UserEmail_email\n }\n\n confirmedEmails: emails(first: 0, state: CONFIRMED) {\n totalCount\n }\n\n unverifiedEmails: emails(first: 0, state: PENDING) {\n totalCount\n }\n\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n oauth2Sessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n compatSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n"];
|
): (typeof documents)["\n fragment UserHome_user on User {\n id\n\n primaryEmail {\n id\n ...UserEmail_email\n }\n\n confirmedEmails: emails(first: 0, state: CONFIRMED) {\n totalCount\n }\n\n browserSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n oauth2Sessions(first: 0, state: ACTIVE) {\n totalCount\n }\n\n compatSessions(first: 0, state: ACTIVE) {\n totalCount\n }\n }\n"];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1146,6 +1146,12 @@ export type OAuth2SessionListQueryQuery = {
|
|||||||
} | null;
|
} | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UnverifiedEmailAlertFragment = {
|
||||||
|
__typename?: "User";
|
||||||
|
id: string;
|
||||||
|
unverifiedEmails: { __typename?: "UserEmailConnection"; totalCount: number };
|
||||||
|
} & { " $fragmentName"?: "UnverifiedEmailAlertFragment" };
|
||||||
|
|
||||||
export type UserEmail_EmailFragment = {
|
export type UserEmail_EmailFragment = {
|
||||||
__typename?: "UserEmail";
|
__typename?: "UserEmail";
|
||||||
id: string;
|
id: string;
|
||||||
@@ -1199,6 +1205,13 @@ export type UserGreetingQuery = {
|
|||||||
displayName?: string | null;
|
displayName?: string | null;
|
||||||
};
|
};
|
||||||
} | null;
|
} | null;
|
||||||
|
viewer:
|
||||||
|
| { __typename: "Anonymous" }
|
||||||
|
| ({ __typename: "User"; id: string } & {
|
||||||
|
" $fragmentRefs"?: {
|
||||||
|
UnverifiedEmailAlertFragment: UnverifiedEmailAlertFragment;
|
||||||
|
};
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UserHome_UserFragment = {
|
export type UserHome_UserFragment = {
|
||||||
@@ -1210,7 +1223,6 @@ export type UserHome_UserFragment = {
|
|||||||
})
|
})
|
||||||
| null;
|
| null;
|
||||||
confirmedEmails: { __typename?: "UserEmailConnection"; totalCount: number };
|
confirmedEmails: { __typename?: "UserEmailConnection"; totalCount: number };
|
||||||
unverifiedEmails: { __typename?: "UserEmailConnection"; totalCount: number };
|
|
||||||
browserSessions: {
|
browserSessions: {
|
||||||
__typename?: "BrowserSessionConnection";
|
__typename?: "BrowserSessionConnection";
|
||||||
totalCount: number;
|
totalCount: number;
|
||||||
@@ -1565,6 +1577,48 @@ export const OAuth2Session_SessionFragmentDoc = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
} as unknown as DocumentNode<OAuth2Session_SessionFragment, unknown>;
|
} as unknown as DocumentNode<OAuth2Session_SessionFragment, unknown>;
|
||||||
|
export const UnverifiedEmailAlertFragmentDoc = {
|
||||||
|
kind: "Document",
|
||||||
|
definitions: [
|
||||||
|
{
|
||||||
|
kind: "FragmentDefinition",
|
||||||
|
name: { kind: "Name", value: "UnverifiedEmailAlert" },
|
||||||
|
typeCondition: {
|
||||||
|
kind: "NamedType",
|
||||||
|
name: { kind: "Name", value: "User" },
|
||||||
|
},
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
alias: { kind: "Name", value: "unverifiedEmails" },
|
||||||
|
name: { kind: "Name", value: "emails" },
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
kind: "Argument",
|
||||||
|
name: { kind: "Name", value: "first" },
|
||||||
|
value: { kind: "IntValue", value: "0" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "Argument",
|
||||||
|
name: { kind: "Name", value: "state" },
|
||||||
|
value: { kind: "EnumValue", value: "PENDING" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "totalCount" } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as unknown as DocumentNode<UnverifiedEmailAlertFragment, unknown>;
|
||||||
export const UserEmail_EmailFragmentDoc = {
|
export const UserEmail_EmailFragmentDoc = {
|
||||||
kind: "Document",
|
kind: "Document",
|
||||||
definitions: [
|
definitions: [
|
||||||
@@ -1637,29 +1691,6 @@ export const UserHome_UserFragmentDoc = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
kind: "Field",
|
|
||||||
alias: { kind: "Name", value: "unverifiedEmails" },
|
|
||||||
name: { kind: "Name", value: "emails" },
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
kind: "Argument",
|
|
||||||
name: { kind: "Name", value: "first" },
|
|
||||||
value: { kind: "IntValue", value: "0" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "Argument",
|
|
||||||
name: { kind: "Name", value: "state" },
|
|
||||||
value: { kind: "EnumValue", value: "PENDING" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
selectionSet: {
|
|
||||||
kind: "SelectionSet",
|
|
||||||
selections: [
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "totalCount" } },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
kind: "Field",
|
kind: "Field",
|
||||||
name: { kind: "Name", value: "browserSessions" },
|
name: { kind: "Name", value: "browserSessions" },
|
||||||
@@ -3058,6 +3089,70 @@ export const UserGreetingDocument = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
name: { kind: "Name", value: "viewer" },
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "__typename" } },
|
||||||
|
{
|
||||||
|
kind: "InlineFragment",
|
||||||
|
typeCondition: {
|
||||||
|
kind: "NamedType",
|
||||||
|
name: { kind: "Name", value: "User" },
|
||||||
|
},
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
|
{
|
||||||
|
kind: "FragmentSpread",
|
||||||
|
name: { kind: "Name", value: "UnverifiedEmailAlert" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "FragmentDefinition",
|
||||||
|
name: { kind: "Name", value: "UnverifiedEmailAlert" },
|
||||||
|
typeCondition: {
|
||||||
|
kind: "NamedType",
|
||||||
|
name: { kind: "Name", value: "User" },
|
||||||
|
},
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "id" } },
|
||||||
|
{
|
||||||
|
kind: "Field",
|
||||||
|
alias: { kind: "Name", value: "unverifiedEmails" },
|
||||||
|
name: { kind: "Name", value: "emails" },
|
||||||
|
arguments: [
|
||||||
|
{
|
||||||
|
kind: "Argument",
|
||||||
|
name: { kind: "Name", value: "first" },
|
||||||
|
value: { kind: "IntValue", value: "0" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "Argument",
|
||||||
|
name: { kind: "Name", value: "state" },
|
||||||
|
value: { kind: "EnumValue", value: "PENDING" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selectionSet: {
|
||||||
|
kind: "SelectionSet",
|
||||||
|
selections: [
|
||||||
|
{ kind: "Field", name: { kind: "Name", value: "totalCount" } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -3951,29 +4046,6 @@ export const HomeQueryDocument = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
kind: "Field",
|
|
||||||
alias: { kind: "Name", value: "unverifiedEmails" },
|
|
||||||
name: { kind: "Name", value: "emails" },
|
|
||||||
arguments: [
|
|
||||||
{
|
|
||||||
kind: "Argument",
|
|
||||||
name: { kind: "Name", value: "first" },
|
|
||||||
value: { kind: "IntValue", value: "0" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "Argument",
|
|
||||||
name: { kind: "Name", value: "state" },
|
|
||||||
value: { kind: "EnumValue", value: "PENDING" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
selectionSet: {
|
|
||||||
kind: "SelectionSet",
|
|
||||||
selections: [
|
|
||||||
{ kind: "Field", name: { kind: "Name", value: "totalCount" } },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
kind: "Field",
|
kind: "Field",
|
||||||
name: { kind: "Name", value: "browserSessions" },
|
name: { kind: "Name", value: "browserSessions" },
|
||||||
|
|||||||
53
frontend/src/test-utils/WithLocation.tsx
Normal file
53
frontend/src/test-utils/WithLocation.tsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// @vitest-environment happy-dom
|
||||||
|
|
||||||
|
import { Provider } from "jotai";
|
||||||
|
import { useHydrateAtoms } from "jotai/utils";
|
||||||
|
|
||||||
|
import { appConfigAtom, locationAtom } from "../Router";
|
||||||
|
|
||||||
|
const HydrateLocation: React.FC<React.PropsWithChildren<{ path: string }>> = ({
|
||||||
|
children,
|
||||||
|
path,
|
||||||
|
}) => {
|
||||||
|
useHydrateAtoms([
|
||||||
|
[appConfigAtom, { root: "/" }],
|
||||||
|
[locationAtom, { pathname: path }],
|
||||||
|
]);
|
||||||
|
return <>{children}</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility for testing components that rely on routing or location
|
||||||
|
* For example any component that includes a <Link />
|
||||||
|
* Eg:
|
||||||
|
* ```
|
||||||
|
* const component = create(
|
||||||
|
<WithLocation path="/">
|
||||||
|
<NavItem route={{ type: "home" }}>Active</NavItem>
|
||||||
|
</WithLocation>,
|
||||||
|
);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export const WithLocation: React.FC<
|
||||||
|
React.PropsWithChildren<{ path?: string }>
|
||||||
|
> = ({ children, path }) => {
|
||||||
|
return (
|
||||||
|
<Provider>
|
||||||
|
<HydrateLocation path={path || "/"}>{children}</HydrateLocation>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user