1
0
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:
Quentin Gliech
2022-11-16 15:14:40 +01:00
parent 8f4873b535
commit 408a7ba363
11 changed files with 102 additions and 66 deletions

View File

@@ -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;

View File

@@ -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

View 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;

View File

@@ -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>
); );
}; };

View File

@@ -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>
); );
}; };

View File

@@ -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>
); );
}; };

View File

@@ -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>
); );
}; };

View File

@@ -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>
); );
}; };

View File

@@ -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>
); );
}; };

View File

@@ -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;

View File

@@ -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} />