You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-11-21 23:00:50 +03:00
More components reuse
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect } from "react";
|
import { useLayoutEffect } from "react";
|
||||||
import "../src/index.css";
|
import "../src/index.css";
|
||||||
|
|
||||||
export const parameters = {
|
export const parameters = {
|
||||||
@@ -35,12 +35,14 @@ export const globalTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ThemeSwitcher = ({ theme }) => {
|
const ThemeSwitcher = ({ theme }) => {
|
||||||
useEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (theme === "dark") {
|
if (theme === "dark") {
|
||||||
document.documentElement.classList.add("dark");
|
document.documentElement.classList.add("dark");
|
||||||
} else {
|
} else {
|
||||||
document.documentElement.classList.remove("dark");
|
document.documentElement.classList.remove("dark");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return () => document.documentElement.classList.remove("dark");
|
||||||
}, [theme]);
|
}, [theme]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export const Basic: Story = {
|
|||||||
<Block {...args}>
|
<Block {...args}>
|
||||||
<Title>Title</Title>
|
<Title>Title</Title>
|
||||||
<Subtitle>Subtitle</Subtitle>
|
<Subtitle>Subtitle</Subtitle>
|
||||||
<Body>
|
<Body justified>
|
||||||
Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit
|
Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit
|
||||||
enim labore culpa sint ad nisi Lorem pariatur mollit ex esse
|
enim labore culpa sint ad nisi Lorem pariatur mollit ex esse
|
||||||
exercitation amet. Nisi anim cupidatat excepteur officia. Reprehenderit
|
exercitation amet. Nisi anim cupidatat excepteur officia. Reprehenderit
|
||||||
|
|||||||
23
frontend/src/components/BlockList.tsx
Normal file
23
frontend/src/components/BlockList.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2022 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.
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const BlockList: React.FC<Props> = ({ children }) => {
|
||||||
|
return <div className="grid grid-cols-1 gap-1 content-start">{children}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BlockList;
|
||||||
@@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
import type { BrowserSession_session$key } from "./__generated__/BrowserSession_session.graphql";
|
import type { BrowserSession_session$key } from "./__generated__/BrowserSession_session.graphql";
|
||||||
import { graphql, useFragment } from "react-relay";
|
import { graphql, useFragment } from "react-relay";
|
||||||
|
import Block from "./Block";
|
||||||
|
import { Body, Code, Subtitle } from "./Typography";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
session: BrowserSession_session$key;
|
session: BrowserSession_session$key;
|
||||||
@@ -35,19 +37,19 @@ const BrowserSession: React.FC<Props> = ({ session, isCurrent }) => {
|
|||||||
session
|
session
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const lastAuthentication = data.lastAuthentication?.createdAt || "never";
|
||||||
|
const createdAt = data.createdAt;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-2 my-1 bg-grey-50 dark:bg-grey-450 dark:text-white rounded">
|
<Block>
|
||||||
{isCurrent && <div className="font-bold">Current session</div>}
|
{isCurrent && <Subtitle>Current session</Subtitle>}
|
||||||
<div>
|
<Body>
|
||||||
Started: <span className="font-mono text-sm">{data.createdAt}</span>
|
Started: <Code>{createdAt}</Code>
|
||||||
</div>
|
</Body>
|
||||||
<div>
|
<Body>
|
||||||
Last authentication:{" "}
|
Last authentication: <Code>{lastAuthentication}</Code>
|
||||||
<span className="font-semibold">
|
</Body>
|
||||||
{data.lastAuthentication?.createdAt || "never"}
|
</Block>
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,10 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { graphql, usePaginationFragment } from "react-relay";
|
import { graphql, usePaginationFragment } from "react-relay";
|
||||||
|
import BlockList from "./BlockList";
|
||||||
import BrowserSession from "./BrowserSession";
|
import BrowserSession from "./BrowserSession";
|
||||||
import Button from "./Button";
|
import Button from "./Button";
|
||||||
|
import { Title } from "./Typography";
|
||||||
|
|
||||||
import { BrowserSessionList_user$key } from "./__generated__/BrowserSessionList_user.graphql";
|
import { BrowserSessionList_user$key } from "./__generated__/BrowserSessionList_user.graphql";
|
||||||
|
|
||||||
@@ -44,8 +46,8 @@ const BrowserSessionList: React.FC<Props> = ({ user, currentSessionId }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<BlockList>
|
||||||
<h2 className="text-lg">List of browser sessions:</h2>
|
<Title>List of browser sessions:</Title>
|
||||||
{data.browserSessions.edges.map((n) => (
|
{data.browserSessions.edges.map((n) => (
|
||||||
<BrowserSession
|
<BrowserSession
|
||||||
key={n.cursor}
|
key={n.cursor}
|
||||||
@@ -54,7 +56,7 @@ const BrowserSessionList: React.FC<Props> = ({ user, currentSessionId }) => {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{hasNext && <Button onClick={() => loadNext(2)}>Load more</Button>}
|
{hasNext && <Button onClick={() => loadNext(2)}>Load more</Button>}
|
||||||
</div>
|
</BlockList>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
import type { CompatSsoLogin_login$key } from "./__generated__/CompatSsoLogin_login.graphql";
|
import type { CompatSsoLogin_login$key } from "./__generated__/CompatSsoLogin_login.graphql";
|
||||||
import { graphql, useFragment } from "react-relay";
|
import { graphql, useFragment } from "react-relay";
|
||||||
|
import Block from "./Block";
|
||||||
|
import { Body, Bold, Code } from "./Typography";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
login: CompatSsoLogin_login$key;
|
login: CompatSsoLogin_login$key;
|
||||||
@@ -41,36 +43,31 @@ const CompatSsoLogin: React.FC<Props> = ({ login }) => {
|
|||||||
if (data.session) {
|
if (data.session) {
|
||||||
info = (
|
info = (
|
||||||
<>
|
<>
|
||||||
<div>
|
<Body>
|
||||||
Started:{" "}
|
Started: <Code>{data.session.createdAt}</Code>
|
||||||
<span className="font-mono text-sm">{data.session.createdAt}</span>
|
</Body>
|
||||||
</div>
|
|
||||||
{data.session.finishedAt ? (
|
{data.session.finishedAt ? (
|
||||||
<div className="text-alert">
|
<Body>
|
||||||
Finished:{" "}
|
Finished: <Code>{data.session.createdAt}</Code>
|
||||||
<span className="font-mono text-sm">{data.session.createdAt}</span>
|
</Body>
|
||||||
</div>
|
|
||||||
) : null}
|
) : null}
|
||||||
<div>
|
<Body>
|
||||||
Device ID:{" "}
|
Device ID: <Code>{data.session.deviceId}</Code>
|
||||||
<span className="font-mono text-sm font-semibold">
|
</Body>
|
||||||
{data.session.deviceId}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-2 my-1 bg-grey-50 dark:bg-grey-450 dark:text-white rounded">
|
<Block>
|
||||||
<div>
|
<Body>
|
||||||
Requested: <span className="font-mono text-sm">{data.createdAt}</span>
|
Requested: <Code>{data.createdAt}</Code>
|
||||||
</div>
|
</Body>
|
||||||
{info}
|
{info}
|
||||||
<div>
|
<Body>
|
||||||
Redirect URI: <span className="font-semibold">{data.redirectUri}</span>
|
Redirect URI: <Bold>{data.redirectUri}</Bold>
|
||||||
</div>
|
</Body>
|
||||||
</div>
|
</Block>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,10 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { graphql, usePaginationFragment } from "react-relay";
|
import { graphql, usePaginationFragment } from "react-relay";
|
||||||
|
import BlockList from "./BlockList";
|
||||||
import Button from "./Button";
|
import Button from "./Button";
|
||||||
import CompatSsoLogin from "./CompatSsoLogin";
|
import CompatSsoLogin from "./CompatSsoLogin";
|
||||||
|
import { Title } from "./Typography";
|
||||||
import { CompatSsoLoginList_user$key } from "./__generated__/CompatSsoLoginList_user.graphql";
|
import { CompatSsoLoginList_user$key } from "./__generated__/CompatSsoLoginList_user.graphql";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -41,13 +43,13 @@ const CompatSsoLoginList: React.FC<Props> = ({ user }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<BlockList>
|
||||||
<h2 className="text-lg">List of compatibility sessions:</h2>
|
<Title>List of compatibility sessions:</Title>
|
||||||
{data.compatSsoLogins.edges.map((n) => (
|
{data.compatSsoLogins.edges.map((n) => (
|
||||||
<CompatSsoLogin login={n.node} key={n.node.id} />
|
<CompatSsoLogin login={n.node} key={n.node.id} />
|
||||||
))}
|
))}
|
||||||
{hasNext && <Button onClick={() => loadNext(2)}>Load more</Button>}
|
{hasNext && <Button onClick={() => loadNext(2)}>Load more</Button>}
|
||||||
</div>
|
</BlockList>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
|
|
||||||
import type { OAuth2Session_session$key } from "./__generated__/OAuth2Session_session.graphql";
|
import type { OAuth2Session_session$key } from "./__generated__/OAuth2Session_session.graphql";
|
||||||
import { graphql, useFragment } from "react-relay";
|
import { graphql, useFragment } from "react-relay";
|
||||||
import Typography, { Bold, Code } from "./Typography";
|
import { Body, Bold, Code } from "./Typography";
|
||||||
|
import Block from "./Block";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
session: OAuth2Session_session$key;
|
session: OAuth2Session_session$key;
|
||||||
@@ -38,21 +39,19 @@ const OAuth2Session: React.FC<Props> = ({ session }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-2 my-1 bg-grey-50 dark:bg-grey-450 dark:text-white rounded">
|
<Block>
|
||||||
<div>
|
<Body>
|
||||||
<Typography variant="body">
|
Client ID: <Code>{data.client.clientId}</Code>
|
||||||
Client ID: <Code>{data.client.clientId}</Code>
|
</Body>
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
{data.client.clientName && (
|
{data.client.clientName && (
|
||||||
<Typography variant="body">
|
<Body>
|
||||||
Client name: <Bold>{data.client.clientName}</Bold>
|
Client name: <Bold>{data.client.clientName}</Bold>
|
||||||
</Typography>
|
</Body>
|
||||||
)}
|
)}
|
||||||
<Typography variant="body">
|
<Body>
|
||||||
Scope: <Code>{data.scope}</Code>
|
Scope: <Code>{data.scope}</Code>
|
||||||
</Typography>
|
</Body>
|
||||||
</div>
|
</Block>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,11 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { graphql, usePaginationFragment } from "react-relay";
|
import { graphql, usePaginationFragment } from "react-relay";
|
||||||
|
import Block from "./Block";
|
||||||
|
import BlockList from "./BlockList";
|
||||||
import Button from "./Button";
|
import Button from "./Button";
|
||||||
import OAuth2Session from "./OAuth2Session";
|
import OAuth2Session from "./OAuth2Session";
|
||||||
|
import { Title } from "./Typography";
|
||||||
|
|
||||||
import { OAuth2SessionList_user$key } from "./__generated__/OAuth2SessionList_user.graphql";
|
import { OAuth2SessionList_user$key } from "./__generated__/OAuth2SessionList_user.graphql";
|
||||||
|
|
||||||
@@ -43,13 +46,13 @@ const OAuth2SessionList: React.FC<Props> = ({ user }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<BlockList>
|
||||||
<h2 className="text-lg">List of OAuth 2.0 sessions:</h2>
|
<Title>List of OAuth 2.0 sessions:</Title>
|
||||||
{data.oauth2Sessions.edges.map((n) => (
|
{data.oauth2Sessions.edges.map((n) => (
|
||||||
<OAuth2Session key={n.cursor} session={n.node} />
|
<OAuth2Session key={n.cursor} session={n.node} />
|
||||||
))}
|
))}
|
||||||
{hasNext && <Button onClick={() => loadNext(2)}>Load more</Button>}
|
{hasNext && <Button onClick={() => loadNext(2)}>Load more</Button>}
|
||||||
</div>
|
</BlockList>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ type Props = {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
variant: Variant;
|
variant: Variant;
|
||||||
bold?: boolean;
|
bold?: boolean;
|
||||||
|
justified?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const elementMap: Record<Variant, "h1" | "h2" | "h3" | "p" | "small"> = {
|
const elementMap: Record<Variant, "h1" | "h2" | "h3" | "p" | "small"> = {
|
||||||
@@ -35,22 +36,23 @@ const classMap: Record<Variant, string> = {
|
|||||||
headline: "text-3xl font-semibold",
|
headline: "text-3xl font-semibold",
|
||||||
title: "text-2xl font-semibold",
|
title: "text-2xl font-semibold",
|
||||||
subtitle: "text-lg",
|
subtitle: "text-lg",
|
||||||
body: "text-base text-justify",
|
body: "text-base",
|
||||||
caption: "text-sm",
|
caption: "text-sm",
|
||||||
micro: "text-xs",
|
micro: "text-xs",
|
||||||
};
|
};
|
||||||
|
|
||||||
const Typography = ({ variant, children, bold }: Props) => {
|
const Typography = ({ variant, children, bold, justified }: Props) => {
|
||||||
const element = elementMap[variant];
|
const element = elementMap[variant];
|
||||||
const boldClass = bold ? "font-semibold" : "";
|
const boldClass = bold ? "font-semibold" : "";
|
||||||
const className = `text-black dark:text-white ${boldClass} ${classMap[variant]}`;
|
const justifiedClass = justified ? "text-justify" : "";
|
||||||
|
const className = `text-black dark:text-white ${boldClass} ${justifiedClass} ${classMap[variant]}`;
|
||||||
return createElement(element, { className }, ...Children.toArray(children));
|
return createElement(element, { className }, ...Children.toArray(children));
|
||||||
};
|
};
|
||||||
|
|
||||||
type SimpleProps = { children: React.ReactNode };
|
type SimpleProps = { children: React.ReactNode };
|
||||||
|
|
||||||
export const Bold = ({ children }: SimpleProps) => (
|
export const Bold = ({ children }: SimpleProps) => (
|
||||||
<em className="font-semibold">{children}</em>
|
<strong className="font-semibold">{children}</strong>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const Code = ({ children }: SimpleProps) => (
|
export const Code = ({ children }: SimpleProps) => (
|
||||||
@@ -65,8 +67,12 @@ export const Subtitle = ({ children }: SimpleProps) => (
|
|||||||
<Typography variant="subtitle" children={children} />
|
<Typography variant="subtitle" children={children} />
|
||||||
);
|
);
|
||||||
|
|
||||||
export const Body = ({ children }: SimpleProps) => (
|
export const Body = ({
|
||||||
<Typography variant="body" children={children} />
|
children,
|
||||||
);
|
justified,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
justified?: boolean;
|
||||||
|
}) => <Typography variant="body" children={children} justified={justified} />;
|
||||||
|
|
||||||
export default Typography;
|
export default Typography;
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const Home: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Typography variant="headline">Hello {user.username}!</Typography>
|
<Typography variant="headline">Hello {user.username}!</Typography>
|
||||||
<div className="grid lg:grid-cols-3 gap-1">
|
<div className="mt-4 grid lg:grid-cols-3 gap-1">
|
||||||
<OAuth2SessionList user={user} />
|
<OAuth2SessionList user={user} />
|
||||||
<CompatSsoLoginList user={user} />
|
<CompatSsoLoginList user={user} />
|
||||||
<BrowserSessionList user={user} currentSessionId={session.id} />
|
<BrowserSessionList user={user} currentSessionId={session.id} />
|
||||||
|
|||||||
Reference in New Issue
Block a user