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
  • Loading branch information
edlerd committed Jan 26, 2024
1 parent 18e2ab8 commit a803eb2
Show file tree
Hide file tree
Showing 32 changed files with 2,353 additions and 1,169 deletions.
10 changes: 0 additions & 10 deletions ui/components/Logo.tsx

This file was deleted.

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;
92 changes: 92 additions & 0 deletions ui/components/Password.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
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;
Loading

0 comments on commit a803eb2

Please sign in to comment.