Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ - feat: login page #118

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion src/components/card/card.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
box-sizing: border-box;
display: flex;
flex-direction: column;
//overflow: clip; // Turned off for now to allow DatePicker to overflow Card and Modal.
width: 100%;

&__header {
Expand All @@ -16,4 +15,35 @@
& + & {
margin-block-start: var(--spacing-v);
}

&--halign-start {
align-items: flex-start;
text-align: left;
}

&--halign-center {
align-items: center;
text-align: center;
}

&--halign-end {
align-items: flex-end;
text-align: right;
}

&--valign-start {
justify-content: flex-start;
}

&--valign-center {
justify-content: center;
}

&--valign-end {
justify-content: flex-end;
}

&--shadow {
box-shadow: 0px 0px 5px 0.5px var(--page-color-shadow);
}
}
13 changes: 12 additions & 1 deletion src/components/card/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import "./card.scss";
export type CardProps = React.PropsWithChildren<{
controls?: ButtonProps[];
title?: string | React.ReactNode;
halign?: "start" | "center" | "end";
valign?: "start" | "center" | "end";
shadow?: boolean;
}>;

/**
Expand All @@ -17,9 +20,17 @@ export const Card: React.FC<CardProps> = ({
controls = [],
children,
title,
halign = "start",
valign = "start",
shadow = false,
...props
}) => (
<div className="mykn-card" {...props}>
<div
className={`mykn-card mykn-card--halign-${halign} mykn-card--valign-${valign} ${
shadow ? "mykn-card--shadow" : ""
}`}
{...props}
>
{(controls.length || title) && (
<div className="mykn-card__header">
<Body stretch>
Expand Down
29 changes: 29 additions & 0 deletions src/components/layout/page/page.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,32 @@
justify-content: center;
}
}

.mykn-background-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1; /* Ensure it is behind the content */

&__background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}

&__overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: black; /* Black overlay */
}
}
61 changes: 52 additions & 9 deletions src/components/layout/page/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ export type PageProps = React.PropsWithChildren<{

/** Whether to apply padding to the page. */
pad?: boolean | "h" | "v";

/** A background image */
backgroundImageUrl?: string;

/** The background image opacity */
backgroundOverlayOpacity?: number;
}>;

/**
Expand All @@ -18,16 +24,53 @@ export const Page: React.FC<PageProps> = ({
children,
pad = false,
valign,
backgroundImageUrl,
backgroundOverlayOpacity = 0.5,
...props
}) => {
return (
<div
className={clsx("mykn-page", {
[`mykn-page--valign-${valign}`]: valign,
"mykn-page--pad-h": pad === true || pad === "h",
"mykn-page--pad-v": pad === true || pad === "v",
})}
{...props}
>
{backgroundImageUrl && (
<BackgroundImage
Xaohs marked this conversation as resolved.
Show resolved Hide resolved
backgroundImageUrl={backgroundImageUrl}
backgroundOverlayOpacity={backgroundOverlayOpacity}
/>
)}
{children}
</div>
);
};

export type BackgroundImageProps = React.PropsWithChildren<{
backgroundImageUrl: string;
backgroundOverlayOpacity: number;
}>;

/**
* BackgroundImage component
* @param children
* @param props
* @constructor
*/
const BackgroundImage: React.FC<BackgroundImageProps> = ({
backgroundImageUrl,
backgroundOverlayOpacity,
}) => (
<div
className={clsx("mykn-page", {
[`mykn-page--valign-${valign}`]: valign,
"mykn-page--pad-h": pad === true || pad === "h",
"mykn-page--pad-v": pad === true || pad === "v",
})}
{...props}
>
{children}
<div className="mykn-background-image">
<div
className="mykn-background-image__background"
style={{ backgroundImage: `url(${backgroundImageUrl})` }}
/>
<div
className="mykn-background-image__overlay"
style={{ opacity: backgroundOverlayOpacity }}
/>
</div>
);
7 changes: 5 additions & 2 deletions src/lib/i18n/compiled/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,8 @@
"mykn.components.Select.labelClear": "clear value",
"mykn.components.Sidebar.collapse": "Collapse sidebar",
"mykn.components.Sidebar.expand": "Expand sidebar",
"mykn.templates.Login.labelLogin": "inloggen"
}
"mykn.templates.Login.labelLogin": "inloggen",
"mykn.templates.Login.titleLogin": "Welcome back",
"mykn.templates.Login.titlePasswordForgotten": "Password forgotten?",
"mykn.templates.Login.linkPasswordForgotten": "Click here"
}
7 changes: 5 additions & 2 deletions src/lib/i18n/compiled/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,8 @@
"mykn.components.Select.labelClear": "waarde wissen",
"mykn.components.Sidebar.collapse": "Zijbalk inklappen",
"mykn.components.Sidebar.expand": "Zijbalk uitklappen",
"mykn.templates.Login.labelLogin": "inloggen"
}
"mykn.templates.Login.labelLogin": "inloggen",
"mykn.templates.Login.titleLogin": "Welkom terug",
"mykn.templates.Login.titlePasswordForgotten": "Wachtwoord vergeten?",
"mykn.templates.Login.linkPasswordForgotten": "Klik hier"
}
15 changes: 15 additions & 0 deletions src/lib/i18n/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,5 +193,20 @@
"defaultMessage": "inloggen",
"description": "templates.Login: The login button label",
"originalDefault": "inloggen"
},
"mykn.templates.Login.titleLogin": {
"defaultMessage": "Welcome back",
"description": "templates.Login: The login title",
"originalDefault": "Welkom terug"
},
"mykn.templates.Login.titlePasswordForgotten": {
"defaultMessage": "Password forgotten?",
"description": "templates.Login: The password forgotten title",
"originalDefault": "Wachtwoord vergeten?"
},
"mykn.templates.Login.linkPasswordForgotten": {
"defaultMessage": "Click here",
"description": "templates.Login: The link to the password forgotten page",
"originalDefault": "Klik hier"
}
}
15 changes: 15 additions & 0 deletions src/lib/i18n/messages/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,5 +193,20 @@
"defaultMessage": "inloggen",
"description": "templates.Login: The login button label",
"originalDefault": "inloggen"
},
"mykn.templates.Login.titleLogin": {
"defaultMessage": "Welkom terug",
"description": "templates.Login: The login title",
"originalDefault": "Welkom terug"
},
"mykn.templates.Login.titlePasswordForgotten": {
"defaultMessage": "Wachtwoord vergeten?",
"description": "templates.Login: The password forgotten title",
"originalDefault": "Wachtwoord vergeten?"
},
"mykn.templates.Login.linkPasswordForgotten": {
"defaultMessage": "Klik hier",
"description": "templates.Login: The link to the password forgotten page",
"originalDefault": "Klik hier"
}
}
3 changes: 3 additions & 0 deletions src/templates/base/base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export type BaseTemplateProps = React.PropsWithChildren & {
/** Primary navigation items. */
primaryNavigationItems?: ToolbarItem[];

/** Background image */
backgroundImageUrl?: string;

/** Sidebar items. */
sidebarItems?: ToolbarItem[];

Expand Down
21 changes: 21 additions & 0 deletions src/templates/login/login.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,24 @@ export const loginTemplate: Story = {
labelLogin: "Log in",
},
};

export const loginTemplateWithBackground: Story = {
args: {
formProps: {
autoComplete: "off",
fields: [
{
label: "Gebruikersnaam",
name: "username",
},
{
label: "Wachtwoord",
name: "Password",
},
],
},
labelLogin: "Log in",
backgroundImageUrl:
"https://s3-alpha-sig.figma.com/img/18d5/48c4/f78435ff97a760d6c9433645c785f392?Expires=1724025600&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=MFKwLX5b6hitZ-uchYo3jmmQD4cpkCdj3czvBiQ93Fxw6BNpTpm4mrPkt6RqEfr6eE2gUQJ~qZjOOdrm2sRmwQmz1XXaLC41eDGcXrvr478IL39PjsrpRX4lsetP2lBGO0wtWu3nVl-9-ubqGqA1qY~2PmnOhAjZ7BiQ4IQRTfe6IT3BN3Ty7wfeIFP8tvyMrsTPE1vNgwBgfC8G~N0cCJ7uuxwv9T-54FFpDvDKQb~Dd2svIGIoUFfl8~E~AMUtPyVLFmUdeDXuK4yUyiwTWtncIPnA~GgeI~em0L~OYmqbh-otzZgWBTLngDZxzUhjyYIKkYGI3sNGYeW8oyGygQ__",
},
};
74 changes: 68 additions & 6 deletions src/templates/login/login.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { useContext } from "react";

import { Body, Card, Form, FormProps, Hr, Logo } from "../../components";
import { A, Body, Card, Form, FormProps, H1, Logo, P } from "../../components";
import { ConfigContext } from "../../contexts";
import { ucFirst } from "../../lib/format/string";
import { ucFirst } from "../../lib";
import { useIntl } from "../../lib/i18n/useIntl";
import { BaseTemplate, BaseTemplateProps } from "../base";

Expand All @@ -12,9 +12,24 @@ export type LoginTemplateProps = BaseTemplateProps & {
/** Form props. */
formProps: FormProps;

/** Password Forgotten Href */
passwordForgottenHref?: string;

/** The login form label. */
labelLogin?: FormProps["labelSubmit"];

/** The login form title. */
titleLogin?: string;

/** The password forgotten title. */
titlePasswordForgotten?: string;

/** The password forgotten "click here" link. */
linkPasswordForgotten?: string;

/** Background image */
backgroundImageUrl?: string;

/** Logo (JSX) slot. */
slotLogo?: React.ReactNode;
};
Expand All @@ -25,7 +40,12 @@ export type LoginTemplateProps = BaseTemplateProps & {
*/
export const LoginTemplate: React.FC<LoginTemplateProps> = ({
formProps,
passwordForgottenHref,
labelLogin,
titleLogin,
titlePasswordForgotten,
linkPasswordForgotten,
backgroundImageUrl,
slotLogo,
...props
}) => {
Expand All @@ -40,18 +60,60 @@ export const LoginTemplate: React.FC<LoginTemplateProps> = ({
defaultMessage: "inloggen",
});

const _titleLogin = titleLogin
? titleLogin
: intl.formatMessage({
id: "mykn.templates.Login.titleLogin",
description: "templates.Login: The login title",
defaultMessage: "Welkom Terug",
});

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

labelForgotPassword and I think one label (for both the title and link) should be sufficient, given that both the fallback and React Intl implementation support placeholders for values.

const _titlePasswordForgotten = titlePasswordForgotten
? titlePasswordForgotten
: intl.formatMessage({
id: "mykn.templates.Login.titlePasswordForgotten",
description: "templates.Login: The password forgotten title",
defaultMessage: "Wachtwoord vergeten?",
});

const _linkPasswordForgotten = linkPasswordForgotten
? linkPasswordForgotten
: intl.formatMessage({
id: "mykn.templates.Login.linkPasswordForgotten",
description: "templates.Login: The password forgotten link",
defaultMessage: "Klik hier",
});

return (
<BaseTemplate
columnProps={{ start: 5, span: 4 }}
pageProps={{ valign: "middle" }}
pageProps={{
valign: "middle",
backgroundImageUrl,
}}
container={true}
{...props}
>
<Card>
<Card halign="center" shadow>
<Body>
{slotLogo || CustomLogo || <Logo />}
<Hr />
<Form labelSubmit={ucFirst(_labelLogin)} {...formProps} />
<br />
<br />
<H1>{_titleLogin}</H1>
<br />
<Form
labelSubmit={ucFirst(_labelLogin)}
{...formProps}
buttonProps={{
justify: true,
}}
/>
{passwordForgottenHref && (
<P muted>
{_titlePasswordForgotten}{" "}
<A href={passwordForgottenHref}>{_linkPasswordForgotten}</A>
</P>
)}
</Body>
</Card>
</BaseTemplate>
Expand Down
Loading