mirror of
https://github.com/overleaf/overleaf.git
synced 2025-12-05 01:10:29 +00:00
[web] Create a first implementation of the CIAM version of the email confirmation page (#29432)
* Move `main#main-content.content.content-alt` into a React component (`ContentLayout`) * Create the CIAM variant of `RegistrationConfirmEmailForm` * `bin/run web npm run extract-translations` * Use `CiamLayout` in the Storybook demo * Add `SplitTestProvider` in tests * Fix Storybook: Wrap `RegistrationConfirmEmailForm` Story in `onboarding-confirm-email` * Refactor SCSS files: - only imports in all.scss - split .storybook-layout and .storybook-enabled - extract ciam-spacing.scss GitOrigin-RevId: f4a214a0978423a1621dd8f60bf459af7b8f877e
This commit is contained in:
@@ -12,6 +12,11 @@ export const defaultSplitTestsArgTypes = {
|
||||
},
|
||||
options: ['enabled'],
|
||||
},
|
||||
uniaccessphase1: {
|
||||
description: 'Enable CIAM designs',
|
||||
control: { type: 'select' as const },
|
||||
options: ['default', 'enabled'],
|
||||
},
|
||||
}
|
||||
|
||||
export const withSplitTests = <ArgTypes = typeof defaultSplitTestsArgTypes,>(
|
||||
|
||||
@@ -2086,6 +2086,7 @@
|
||||
"us_gov_banner_government_purchasing": "",
|
||||
"us_gov_banner_small_business_reseller": "",
|
||||
"usage_metrics": "",
|
||||
"use_a_different_email": "",
|
||||
"use_a_different_password": "",
|
||||
"use_saml_metadata_to_configure_sso_with_idp": "",
|
||||
"use_your_own_machine": "",
|
||||
@@ -2114,6 +2115,7 @@
|
||||
"vat": "",
|
||||
"vat_number": "",
|
||||
"verify_email_address_before_enabling_managed_users": "",
|
||||
"verify_your_email_address": "",
|
||||
"view": "",
|
||||
"view_all": "",
|
||||
"view_audit_logs_group_subtext": "",
|
||||
|
||||
@@ -2,7 +2,7 @@ import { postJSON } from '@/infrastructure/fetch-json'
|
||||
import useWaitForI18n from '@/shared/hooks/use-wait-for-i18n'
|
||||
import Notification from '@/shared/components/notification'
|
||||
import getMeta from '@/utils/meta'
|
||||
import { FormEvent, MouseEventHandler, ReactNode, useState } from 'react'
|
||||
import { FormEvent, MouseEventHandler, useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import LoadingSpinner from '@/shared/components/loading-spinner'
|
||||
import MaterialIcon from '@/shared/components/material-icon'
|
||||
@@ -27,8 +27,9 @@ type ConfirmEmailFormProps = {
|
||||
onSuccessfulConfirmation?: () => void
|
||||
interstitial: boolean
|
||||
isModal?: boolean
|
||||
onCancel?: () => void
|
||||
onCancel?: MouseEventHandler<HTMLButtonElement>
|
||||
outerError?: string
|
||||
isCiam?: boolean
|
||||
}
|
||||
|
||||
export function ConfirmEmailForm({
|
||||
@@ -43,6 +44,7 @@ export function ConfirmEmailForm({
|
||||
isModal,
|
||||
onCancel,
|
||||
outerError,
|
||||
isCiam,
|
||||
}: ConfirmEmailFormProps) {
|
||||
const { t } = useTranslation()
|
||||
const [confirmationCode, setConfirmationCode] = useState('')
|
||||
@@ -163,20 +165,6 @@ export function ConfirmEmailForm({
|
||||
)
|
||||
}
|
||||
|
||||
let intro: ReactNode | null = (
|
||||
<h5 className="h5">{t('confirm_your_email')}</h5>
|
||||
)
|
||||
if (isModal)
|
||||
intro = outerErrorDisplay ? (
|
||||
<div className="mt-4" />
|
||||
) : (
|
||||
<h3 className="h5">{outerErrorDisplay ? null : t('we_sent_code')}</h3>
|
||||
)
|
||||
if (interstitial)
|
||||
intro = (
|
||||
<h1 className="h3 interstitial-header">{t('confirm_your_email')}</h1>
|
||||
)
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={submitHandler}
|
||||
@@ -196,7 +184,12 @@ export function ConfirmEmailForm({
|
||||
/>
|
||||
)}
|
||||
|
||||
{intro}
|
||||
<Title
|
||||
isModal={isModal}
|
||||
interstitial={interstitial}
|
||||
isCiam={isCiam}
|
||||
outerErrorDisplay={outerErrorDisplay}
|
||||
/>
|
||||
|
||||
<OLFormLabel htmlFor="one-time-code">
|
||||
{isModal
|
||||
@@ -260,6 +253,30 @@ export function ConfirmEmailForm({
|
||||
)
|
||||
}
|
||||
|
||||
function Title({
|
||||
isModal,
|
||||
interstitial,
|
||||
outerErrorDisplay,
|
||||
isCiam,
|
||||
}: {
|
||||
isModal?: boolean
|
||||
interstitial: boolean
|
||||
isCiam?: boolean
|
||||
outerErrorDisplay: string | null
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
if (isCiam) return <h1>{t('verify_your_email_address')}</h1>
|
||||
if (isModal)
|
||||
return outerErrorDisplay ? (
|
||||
<div className="mt-4" />
|
||||
) : (
|
||||
<h3 className="h5">{outerErrorDisplay ? null : t('we_sent_code')}</h3>
|
||||
)
|
||||
if (interstitial)
|
||||
return <h1 className="h3 interstitial-header">{t('confirm_your_email')}</h1>
|
||||
return <h5 className="h5">{t('confirm_your_email')}</h5>
|
||||
}
|
||||
|
||||
function ConfirmEmailSuccessfullForm({
|
||||
successMessage,
|
||||
successButtonText,
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import React, { FC, ReactNode } from 'react'
|
||||
import overleafLogo from '@/shared/svgs/overleaf-a-ds-solution-mallard.svg'
|
||||
|
||||
type Props = { children: ReactNode }
|
||||
|
||||
const CiamLayout: FC<Props> = ({ children }: Props) => (
|
||||
<div className="ciam-layout ciam-enabled">
|
||||
<a
|
||||
href="/"
|
||||
aria-label="Overleaf"
|
||||
className="brand"
|
||||
style={{ backgroundImage: `url("${overleafLogo}")` }}
|
||||
/>
|
||||
<div className="ciam-container">
|
||||
<main className="ciam-card" id="main-content">
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<a href="https://www.overleaf.com/legal#Privacy">Privacy</a>
|
||||
<a href="https://www.overleaf.com/legal#Terms">Terms</a>
|
||||
</footer>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default CiamLayout
|
||||
@@ -0,0 +1,16 @@
|
||||
import React, { FC, ReactNode } from 'react'
|
||||
|
||||
type Props = { children: ReactNode; isMain?: boolean; alt?: boolean }
|
||||
|
||||
const ContentLayout: FC<Props> = ({ children, isMain, alt }: Props) => {
|
||||
const className = alt ? 'content content-alt' : 'content'
|
||||
return isMain ? (
|
||||
<main className={className} id="main-content">
|
||||
{children}
|
||||
</main>
|
||||
) : (
|
||||
<div className={className}>{children}</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ContentLayout
|
||||
@@ -5,7 +5,7 @@ import OLPageContentCard from '@/shared/components/ol/ol-page-content-card'
|
||||
import OLRow from '@/shared/components/ol/ol-row'
|
||||
import OLCol from '@/shared/components/ol/ol-col'
|
||||
import OLButton from '@/shared/components/ol/ol-button'
|
||||
import overleafLogo from '@/shared/svgs/overleaf-a-ds-solution-mallard.svg'
|
||||
import CiamLayout from '@/shared/components/layouts/ciam-layout'
|
||||
|
||||
const lorem = (n: number) => {
|
||||
const quacks = ['quack', 'quack', 'quack', 'quak']
|
||||
@@ -249,27 +249,13 @@ export const CompleteRegistration = () => (
|
||||
)
|
||||
|
||||
export const Ciam = () => (
|
||||
<div className="ciam-layout">
|
||||
<a
|
||||
href="/"
|
||||
aria-label="Overleaf"
|
||||
className="brand"
|
||||
style={{ backgroundImage: `url("${overleafLogo}")` }}
|
||||
/>
|
||||
<div className="ciam-container">
|
||||
<main className="ciam-card" id="main-content">
|
||||
<h1>Create your Overleaf account</h1>
|
||||
<p>{lorem(20)}</p>
|
||||
<hr />
|
||||
<p>{lorem(20)}</p>
|
||||
<OLButton>Button</OLButton>
|
||||
</main>
|
||||
</div>
|
||||
<footer>
|
||||
<a href="https://www.overleaf.com/legal#Privacy">Privacy</a>
|
||||
<a href="https://www.overleaf.com/legal#Terms">Terms</a>
|
||||
</footer>
|
||||
</div>
|
||||
<CiamLayout>
|
||||
<h1>Create your Overleaf account</h1>
|
||||
<p>{lorem(20)}</p>
|
||||
<hr />
|
||||
<p>{lorem(20)}</p>
|
||||
<OLButton>Button</OLButton>
|
||||
</CiamLayout>
|
||||
)
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,70 +1,5 @@
|
||||
@import 'ciam-variables';
|
||||
@import 'ciam-colors';
|
||||
@import 'ciam-layout';
|
||||
@import 'ciam-mixins';
|
||||
|
||||
.ciam-layout {
|
||||
padding: var(--ciam-spacing-350);
|
||||
display: flex;
|
||||
min-height: 100%;
|
||||
flex-direction: column;
|
||||
font-family: var(--ciam-font-family-sans), sans-serif;
|
||||
color: var(--ciam-color-text-primary);
|
||||
font-size: var(--ciam-font-size-400);
|
||||
line-height: 1.5;
|
||||
|
||||
@include ciam-body-md-regular;
|
||||
|
||||
.ciam-container {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.brand {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: contain;
|
||||
height: 64px;
|
||||
width: 130px;
|
||||
margin: var(--ciam-spacing-350) auto;
|
||||
display: block;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
margin: var(--ciam-spacing-350) var(--ciam-spacing-800);
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
@include ciam-heading-sm-semibold;
|
||||
}
|
||||
|
||||
.ciam-card {
|
||||
box-shadow:
|
||||
0 4px 6px -4px rgb(0 0 0 / 10%),
|
||||
0 1px 29px -3px rgb(0 0 0 / 16%);
|
||||
padding: var(--ciam-spacing-800) var(--ciam-spacing-400);
|
||||
border-radius: var(--ciam-border-radius-400);
|
||||
max-width: 460px;
|
||||
margin: var(--ciam-spacing-400) auto;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
padding: var(--ciam-spacing-1300);
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
gap: var(--ciam-spacing-600);
|
||||
text-transform: uppercase;
|
||||
justify-content: center;
|
||||
margin: var(--ciam-spacing-350) auto;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
margin: var(--ciam-spacing-350) var(--ciam-spacing-800);
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
|
||||
@include ciam-body-sm-regular;
|
||||
}
|
||||
}
|
||||
}
|
||||
@import 'ciam-spacing';
|
||||
@import 'ciam-variables';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.ciam-layout {
|
||||
.ciam-enabled {
|
||||
--ciam-color-neutral-50: #fafafa;
|
||||
--ciam-color-neutral-100: #f2f2f2;
|
||||
--ciam-color-neutral-200: #e6e6e6;
|
||||
|
||||
72
services/web/frontend/stylesheets/ciam/ciam-layout.scss
Normal file
72
services/web/frontend/stylesheets/ciam/ciam-layout.scss
Normal file
@@ -0,0 +1,72 @@
|
||||
@import 'ciam-mixins';
|
||||
|
||||
.ciam-layout {
|
||||
padding: var(--ciam-spacing-350);
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
flex-direction: column;
|
||||
|
||||
@include ciam-body-md-regular;
|
||||
}
|
||||
|
||||
.ciam-enabled {
|
||||
font-family: var(--ciam-font-family-sans), sans-serif;
|
||||
color: var(--ciam-color-text-primary);
|
||||
font-size: var(--ciam-font-size-400);
|
||||
line-height: 1.5;
|
||||
|
||||
.ciam-container {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.brand {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: contain;
|
||||
height: 64px;
|
||||
width: 130px;
|
||||
margin: var(--ciam-spacing-350) auto;
|
||||
display: block;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
margin: var(--ciam-spacing-350) var(--ciam-spacing-800);
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
@include ciam-heading-sm-semibold;
|
||||
}
|
||||
|
||||
.ciam-card {
|
||||
box-shadow:
|
||||
0 4px 6px -4px rgb(0 0 0 / 10%),
|
||||
0 1px 29px -3px rgb(0 0 0 / 16%);
|
||||
padding: var(--ciam-spacing-800) var(--ciam-spacing-400);
|
||||
border-radius: var(--ciam-border-radius-400);
|
||||
max-width: 460px;
|
||||
margin: var(--ciam-spacing-400) auto;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
padding: var(--ciam-spacing-1300);
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
gap: var(--ciam-spacing-600);
|
||||
text-transform: uppercase;
|
||||
justify-content: center;
|
||||
margin: var(--ciam-spacing-350) auto;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
margin: var(--ciam-spacing-350) var(--ciam-spacing-800);
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
|
||||
@include ciam-body-sm-regular;
|
||||
}
|
||||
}
|
||||
}
|
||||
66
services/web/frontend/stylesheets/ciam/ciam-spacing.scss
Normal file
66
services/web/frontend/stylesheets/ciam/ciam-spacing.scss
Normal file
@@ -0,0 +1,66 @@
|
||||
.ciam-enabled {
|
||||
--ciam-font-weight-regular: 400;
|
||||
--ciam-font-weight-medium: 500;
|
||||
--ciam-font-weight-semibold: 600;
|
||||
--ciam-font-weight-bold: 700;
|
||||
--ciam-font-family-sans: inter, sans-serif;
|
||||
--ciam-spacing-50: 2px;
|
||||
--ciam-spacing-100: 4px;
|
||||
--ciam-spacing-150: 6px;
|
||||
--ciam-spacing-200: 8px;
|
||||
--ciam-spacing-250: 10px;
|
||||
--ciam-spacing-300: 12px;
|
||||
--ciam-spacing-350: 14px;
|
||||
--ciam-spacing-400: 16px;
|
||||
--ciam-spacing-500: 20px;
|
||||
--ciam-spacing-600: 24px;
|
||||
--ciam-spacing-700: 28px;
|
||||
--ciam-spacing-800: 32px;
|
||||
--ciam-spacing-900: 36px;
|
||||
--ciam-spacing-1000: 40px;
|
||||
--ciam-spacing-1100: 44px;
|
||||
--ciam-spacing-1200: 48px;
|
||||
--ciam-spacing-1300: 52px;
|
||||
--ciam-spacing-1400: 56px;
|
||||
--ciam-spacing-1500: 60px;
|
||||
--ciam-spacing-1600: 64px;
|
||||
--ciam-spacing-1700: 68px;
|
||||
--ciam-spacing-1800: 72px;
|
||||
--ciam-spacing-1900: 76px;
|
||||
--ciam-spacing-2000: 80px;
|
||||
--ciam-spacing-2100: 84px;
|
||||
--ciam-spacing-2200: 88px;
|
||||
--ciam-spacing-2300: 92px;
|
||||
--ciam-spacing-2400: 96px;
|
||||
--ciam-base-unit: 4px;
|
||||
--ciam-font-size-300: 12px;
|
||||
--ciam-font-size-350: 14px;
|
||||
--ciam-font-size-400: 16px;
|
||||
--ciam-font-size-450: 18px;
|
||||
--ciam-font-size-500: 20px;
|
||||
--ciam-font-size-600: 24px;
|
||||
--ciam-font-size-700: 28px;
|
||||
--ciam-font-size-800: 32px;
|
||||
--ciam-font-size-1000: 40px;
|
||||
--ciam-font-size-1400: 56px;
|
||||
--ciam-font-size-1800: 72px;
|
||||
--ciam-font-line-height-400: 16px;
|
||||
--ciam-font-line-height-500: 20px;
|
||||
--ciam-font-line-height-600: 24px;
|
||||
--ciam-font-line-height-700: 28px;
|
||||
--ciam-font-line-height-800: 32px;
|
||||
--ciam-font-line-height-900: 36px;
|
||||
--ciam-font-line-height-1000: 40px;
|
||||
--ciam-font-line-height-1200: 48px;
|
||||
--ciam-font-line-height-1600: 64px;
|
||||
--ciam-font-line-height-1800: 72px;
|
||||
--ciam-border-width-25: 1px;
|
||||
--ciam-border-width-50: 2px;
|
||||
--ciam-border-radius-50: 2px;
|
||||
--ciam-border-radius-100: 4px;
|
||||
--ciam-border-radius-200: 8px;
|
||||
--ciam-border-radius-300: 12px;
|
||||
--ciam-border-radius-400: 16px;
|
||||
--ciam-border-radius-600: 24px;
|
||||
--ciam-border-radius-full: 9999px;
|
||||
}
|
||||
@@ -1,24 +1,9 @@
|
||||
@import 'ciam-mixins';
|
||||
@import 'ciam-colors';
|
||||
|
||||
// TODO: Replace `fuchsia` by the correct colors.
|
||||
|
||||
.ciam-layout {
|
||||
// Spacings
|
||||
--ciam-spacing-200: 8px;
|
||||
--ciam-spacing-250: 10px;
|
||||
--ciam-spacing-350: 12px;
|
||||
--ciam-spacing-400: 16px;
|
||||
--ciam-spacing-600: 24px; // TODO: confirm this variable name (couldn't find in design system)
|
||||
--ciam-spacing-800: 32px; // TODO: confirm this variable name (couldn't find in design system)
|
||||
--ciam-spacing-1300: 52px;
|
||||
|
||||
.ciam-enabled {
|
||||
// Base variables
|
||||
--ciam-color-text-secondary: var(--ciam-color-neutral-800);
|
||||
--ciam-color-text-primary: var(--ciam-color-neutral-900);
|
||||
--ciam-border-radius-200: var(--ciam-spacing-300);
|
||||
--ciam-border-radius-400: var(--ciam-spacing-400);
|
||||
--ciam-font-family-sans: 'Inter', sans-serif;
|
||||
|
||||
// Links
|
||||
// used in services/web/frontend/stylesheets/base/links.scss
|
||||
|
||||
@@ -2617,6 +2617,7 @@
|
||||
"us_gov_banner_government_purchasing": "<0>Get __appName__ for US federal government. </0>Move faster through procurement with our tailored purchasing options. Talk to our government team.",
|
||||
"us_gov_banner_small_business_reseller": "<0>Easy procurement for US federal government. </0>We partner with small business resellers to help you buy Overleaf organizational plans. Talk to our government team.",
|
||||
"usage_metrics": "Usage metrics",
|
||||
"use_a_different_email": "Use a <0>different email</0>.",
|
||||
"use_a_different_password": "Please use a different password",
|
||||
"use_saml_metadata_to_configure_sso_with_idp": "Use the Overleaf SAML metadata to configure SSO with your Identity Provider.",
|
||||
"use_your_own_machine": "Use your own machine, with your own setup",
|
||||
@@ -2652,6 +2653,7 @@
|
||||
"vat": "VAT",
|
||||
"vat_number": "VAT Number",
|
||||
"verify_email_address_before_enabling_managed_users": "You need to verify your email address before enabling managed users.",
|
||||
"verify_your_email_address": "Verify your email address",
|
||||
"view": "View",
|
||||
"view_all": "View all",
|
||||
"view_audit_logs_group_subtext": "View and download audit logs for your group subscription",
|
||||
|
||||
Reference in New Issue
Block a user