Skip to content

Commit

Permalink
feat: use new design and add user flows as dummy pages
Browse files Browse the repository at this point in the history
Signed-off-by: David Edler <[email protected]>
  • Loading branch information
edlerd committed Jan 26, 2024
1 parent 18e2ab8 commit 95e5f77
Show file tree
Hide file tree
Showing 35 changed files with 2,356 additions and 1,174 deletions.
2 changes: 1 addition & 1 deletion ui/components/Flow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ export class Flow<T extends Values> extends Component<Props<T>, State<T>> {
[getNodeId(node)]: value,
},
}),
resolve
resolve,
);
})
}
Expand Down
10 changes: 0 additions & 10 deletions ui/components/Logo.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion ui/components/NodeText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const Content: FC<Props> = ({ attributes }) => {
{/* Used lookup_secret has ID 1050014 */}
<code>{text.id === 1050014 ? "Used" : text.text}</code>
</div>
)
),
);
return (
<div
Expand Down
39 changes: 39 additions & 0 deletions ui/components/PageLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Card, Col, Navigation, Row, Theme } from "@canonical/react-components";
import React, { FC, ReactNode } from "react";
import Head from "next/head";

interface Props {
children?: ReactNode;
title: string;
}

const PageLayout: FC<Props> = ({ children, title }) => {
return (
<>
<Head>
<title>{title}</title>
<body className="is-paper" />
</Head>
<Row className="p-strip page-row">
<Col emptyLarge={4} size={6}>
<Card className="u-no-padding page-card">
<Navigation
logo={{
src: "https://assets.ubuntu.com/v1/82818827-CoF_white.svg",
title: "Canonical",
url: `/`,
}}
theme={Theme.DARK}
/>
<div className="p-card__inner page-inner">
<h1 className="p-heading--4">{title}</h1>
<div>{children}</div>
</div>
</Card>
</Col>
</Row>
</>
);
};

export default PageLayout;
91 changes: 91 additions & 0 deletions ui/components/Password.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React, { FC } from "react";
import { Input } from "@canonical/react-components";
import PasswordCheck from "./PasswordCheck";

export type PasswordCheckType = "lowercase" | "uppercase" | "number" | "length";

type Props = {
checks: PasswordCheckType[];
password: string;
setPassword: (password: string) => void;
isValid: boolean;
setValid: (isValid: boolean) => void;
label?: string;
};

const Password: FC<Props> = ({
checks,
password,
setPassword,
isValid,
setValid,
label = "Password",
}) => {
const [confirmation, setConfirmation] = React.useState("");
const [hasPassBlur, setPasswordBlurred] = React.useState(false);
const [hasConfirmBlur, setConfirmationBlurred] = React.useState(false);

const getStatus = (check: PasswordCheckType) => {
if (!hasPassBlur) {
return "neutral";
}

switch (check) {
case "lowercase":
return /[a-z]/.test(password) ? "success" : "error";
case "uppercase":
return /[A-Z]/.test(password) ? "success" : "error";
case "number":
return /[0-9]/.test(password) ? "success" : "error";
case "length":
return password.length >= 8 ? "success" : "error";
}
};

const isCheckFailed = checks.some((check) => getStatus(check) === "error");
const isMismatch = hasConfirmBlur && password !== confirmation;

const localValid = hasPassBlur && !isCheckFailed && password === confirmation;
if (isValid !== localValid) {
setValid(localValid);
}

return (
<>
<Input
id="password"
name="password"
type="password"
label={label}
placeholder="Your password"
onBlur={() => setPasswordBlurred(true)}
onChange={(e) => setPassword(e.target.value)}
value={password}
help={checks.length > 0 && "Password must contain"}
/>
{checks.map((check) => {
return (
<PasswordCheck key={check} check={check} status={getStatus(check)} />
);
})}
<Input
id="passwordConfirm"
name="passwordConfirm"
type="password"
label={`Confirm ${label}`}
placeholder="Your password"
onBlur={() => setConfirmationBlurred(true)}
onChange={(e) => setConfirmation(e.target.value)}
error={
isCheckFailed
? "Password does not match requirements"
: isMismatch
? "Passwords do not match"
: undefined
}
/>
</>
);
};

export default Password;
47 changes: 47 additions & 0 deletions ui/components/PasswordCheck.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React, { FC } from "react";
import { Icon } from "@canonical/react-components";
import { PasswordCheckType } from "./Password";

type Props = {
check: PasswordCheckType;
status: "success" | "error" | "neutral";
};

const PasswordCheck: FC<Props> = ({ check, status }) => {
const getMessage = () => {
switch (check) {
case "lowercase":
return "Lowercase letters (a-z)";
case "uppercase":
return "Uppercase letters (A-Z)";
case "number":
return "Number (0-9)";
case "length":
return "Minimum 8 characters";
}
};

switch (status) {
case "success":
return (
<div className="p-form-validation is-success">
<p className="p-form-validation__message">{getMessage()}</p>
</div>
);
case "error":
return (
<div className="p-form-validation is-error">
<p className="p-form-validation__message">{getMessage()}</p>
</div>
);
case "neutral":
return (
<p className="p-text--small u-text--muted">
<Icon name="information" />
&nbsp;&nbsp;{getMessage()}
</p>
);
}
};

export default PasswordCheck;
4 changes: 2 additions & 2 deletions ui/components/helpers.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { UiNode, UiNodeInputAttributes } from "@ory/client";
import { FormEvent } from "react";

export type ValueSetter = (
value: string | number | boolean | undefined
value: string | number | boolean | undefined,
) => Promise<void>;

export type FormDispatcher = (e: MouseEvent | FormEvent) => Promise<void>;

export interface NodeInputProps {
node: UiNode;
attributes: UiNodeInputAttributes;
value: any;
value: unknown;
disabled: boolean;
dispatchSubmit: FormDispatcher;
setValue: ValueSetter;
Expand Down
Loading

0 comments on commit 95e5f77

Please sign in to comment.