Sfoglia il codice sorgente

[web] Create an initial implementation for the CIAM page layout (#29373)

* Add a Storybook Layout page compiling all the "small pages" layouts

* Add a CIAM page layout to Storybook and create an initial Layout

* Use rem in font mixins

* Add a `--ciam-` prefix to the new CSS variables

* Fix linting

GitOrigin-RevId: 7a89fd1531c87597a918a9170d174cce556d77c4
Antoine Clausse 9 mesi fa
parent
commit
698a6013de

+ 297 - 0
services/web/frontend/stories/page-layouts.stories.tsx

@@ -0,0 +1,297 @@
+import _ from 'lodash'
+import { ComponentType } from 'react'
+import { Navbar } from 'react-bootstrap'
+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'
+
+const lorem = (n: number) => {
+  const quacks = ['quack', 'quack', 'quack', 'quak']
+  let result = ''
+  if (n >= 1) result += 'Lorem'
+  if (n >= 2) result += ' epsom'
+  for (let i = 2; i < n; i++) {
+    const next =
+      result.at(-1) === '.'
+        ? ' ' + _.capitalize(quacks[Math.floor(Math.random() * quacks.length)])
+        : quacks[Math.floor(Math.random() * (quacks.length + 1))]
+    result += next ? ' ' + next : '.'
+  }
+  if (result.at(-1) !== '.') result += '.'
+  return result
+}
+
+const Nav = () => <Navbar className="navbar-default navbar-main" />
+
+export const UnsuportedBrowser = () => (
+  <main className="content content-alt full-height" id="main-content">
+    <div className="container full-height">
+      <div className="error-container full-height">
+        <div className="error-details">
+          <h1 className="error-status">Unsupported Browser</h1>
+          <p className="error-description">{lorem(60)}</p>
+          <hr />
+          <p>{lorem(40)}</p>
+        </div>
+      </div>
+    </div>
+  </main>
+)
+
+export const Error400 = () => (
+  <main className="content content-alt full-height" id="main-content">
+    <div className="container full-height">
+      <div className="error-container full-height">
+        <div className="error-details">
+          <p className="error-status">Something went wrong, sorry.</p>
+          <p className="error-description">{lorem(15)}</p>
+        </div>
+      </div>
+    </div>
+  </main>
+)
+
+export const Error404 = () => (
+  <>
+    <Nav />
+    <main className="content content-alt" id="main-content">
+      <div className="container">
+        <div className="error-container">
+          <div className="error-details">
+            <p className="error-status">Not found</p>
+            <p className="error-description">{lorem(20)}</p>
+          </div>
+        </div>
+      </div>
+    </main>
+  </>
+)
+
+export const Closed = () => (
+  <>
+    <Nav />
+    <main className="content" id="main-content">
+      <div className="container">
+        <div className="row">
+          <div className="col-lg-8 col-lg-offset-2 text-center">
+            <div className="page-header">
+              <h1>Maintenance</h1>
+            </div>
+            <p>{lorem(6)}</p>
+          </div>
+        </div>
+      </div>
+    </main>
+  </>
+)
+
+export const PlannedMaintenance = () => (
+  <>
+    <Nav />
+    <main className="content" id="main-content">
+      <div className="container">
+        <div className="row">
+          <div className="col-lg-8 col-lg-offset-2">
+            <div className="page-header">
+              <h1>Planned Maintenance</h1>
+            </div>
+            <p>{lorem(6)}</p>
+          </div>
+        </div>
+      </div>
+    </main>
+  </>
+)
+
+export const PostGateway = () => (
+  <>
+    <div className="content content-alt">
+      <div className="container">
+        <div className="row">
+          <div className="col-lg-6 offset-lg-3">
+            <div className="card">
+              <div className="card-body">
+                <p className="text-center">
+                  Please wait while we process your request.
+                </p>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </>
+)
+
+export const AccountSuspended = () => (
+  <main className="content content-alt" id="main-content">
+    <div className="container-custom-sm mx-auto">
+      <div className="card">
+        <div className="card-body">
+          <h3>Your account is suspended</h3>
+          <p>{lorem(6)}</p>
+        </div>
+      </div>
+    </div>
+  </main>
+)
+
+export const Restricted = () => (
+  <>
+    <Nav />
+    <main className="content" id="main-content">
+      <div className="container">
+        <div className="row">
+          <div className="col-md-8 offset-md-2 text-center">
+            <div className="page-header">
+              <h2>
+                Restricted, sorry you don’t have permission to load this page.
+              </h2>
+            </div>
+            <p>{lorem(23)}</p>
+          </div>
+        </div>
+      </div>
+    </main>
+  </>
+)
+
+export const OneTimeLogin = () => (
+  <>
+    <Nav />
+    <main className="content content-alt" id="main-content">
+      <div className="container">
+        <div className="row">
+          <div className="col-lg-6 offset-lg-3 col-xl-4 offset-xl-4">
+            <div className="card">
+              <div className="card-body">
+                <div className="page-header">
+                  <h1>We're back!</h1>
+                </div>
+                <p>Overleaf is now running normally.</p>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </main>
+  </>
+)
+
+export const Invite = () => (
+  <main className="content content-alt" id="invite-root">
+    <OLRow className="row row-spaced">
+      <OLCol lg={{ span: 8, offset: 2 }}>
+        <OLPageContentCard>
+          <div className="page-header">
+            <h1 className="text-center">
+              <span className="team-invite-name">
+                max.mustermann@example.com
+              </span>{' '}
+              has invited you to join a group subscription on Overleaf
+            </h1>
+          </div>
+          <p className="text-center">{lorem(20)}</p>
+        </OLPageContentCard>
+      </OLCol>
+    </OLRow>
+  </main>
+)
+
+export const NotValid = () => (
+  <>
+    <Nav />
+    <main className="content content-alt" id="main-content">
+      <div className="container">
+        <div className="row">
+          <div className="col-md-8 col-md-offset-2 offset-md-2">
+            <div className="card project-invite-invalid">
+              <div className="card-body">
+                <div className="page-header text-center">
+                  <h1>Invite not valid</h1>
+                </div>
+                <div className="row text-center">
+                  <div className="col-12 col-md-12">
+                    <p>{lorem(20)}</p>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </main>
+  </>
+)
+
+export const CompleteRegistration = () => (
+  <>
+    <Nav />
+    <main className="content content-alt" id="main-content">
+      <div className="container">
+        <div className="row">
+          <div className="col-12 col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2 offset-md-1 offset-lg-2">
+            <div className="card">
+              <div className="card-body">
+                <div className="page-header">
+                  <h1 className="text-center">Dropbox Sync</h1>
+                </div>
+                <p>{lorem(20)}</p>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </main>
+  </>
+)
+
+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>
+)
+
+export default {
+  title: 'Shared / Layouts',
+  args: {
+    label: 'Option',
+  },
+
+  parameters: {
+    layout: 'fullscreen', // This is crucial for vh/vw layouts
+  },
+  decorators: [
+    (Story: ComponentType) => (
+      <div style={{ height: '100vh', width: '100vw' }}>
+        <style>
+          {`.content {
+                min-height: 100vh;
+                padding-top: 93px;
+           }`}
+        </style>
+        <Story />
+      </div>
+    ),
+  ],
+}

+ 1 - 0
services/web/frontend/stylesheets/pages/all.scss

@@ -1,4 +1,5 @@
 @import 'account-settings';
+@import 'ciam';
 @import 'cms';
 @import 'content';
 @import 'project-list';

+ 68 - 0
services/web/frontend/stylesheets/pages/ciam-colors.scss

@@ -0,0 +1,68 @@
+.ciam-layout {
+  --ciam-color-neutral-50: #fafafa;
+  --ciam-color-neutral-100: #f2f2f2;
+  --ciam-color-neutral-200: #e6e6e6;
+  --ciam-color-neutral-300: #d6d6d6;
+  --ciam-color-neutral-400: #c7c7c7;
+  --ciam-color-neutral-500: #b5b5b5;
+  --ciam-color-neutral-600: #a1a1a1;
+  --ciam-color-neutral-700: #8a8a8a;
+  --ciam-color-neutral-800: #6b6b6b;
+  --ciam-color-neutral-900: #383838;
+  --ciam-color-neutral-950: #262626;
+  --ciam-color-green-50: #f2f8f5;
+  --ciam-color-green-100: #e3f2eb;
+  --ciam-color-green-200: #c0e7d6;
+  --ciam-color-green-300: #8adbb7;
+  --ciam-color-green-400: #38cc89;
+  --ciam-color-green-500: #26b072;
+  --ciam-color-green-600: #158954;
+  --ciam-color-green-700: #19754c;
+  --ciam-color-green-800: #196241;
+  --ciam-color-green-900: #164630;
+  --ciam-color-green-950: #112c20;
+  --ciam-color-yellow-50: #fffaeb;
+  --ciam-color-yellow-100: #fff7db;
+  --ciam-color-yellow-200: #ffeeb8;
+  --ciam-color-yellow-300: #ffe58f;
+  --ciam-color-yellow-400: #ffda61;
+  --ciam-color-yellow-500: #ffcc20;
+  --ciam-color-yellow-600: #f0b800;
+  --ciam-color-yellow-700: #d1a000;
+  --ciam-color-yellow-800: #ad8500;
+  --ciam-color-yellow-900: #806200;
+  --ciam-color-yellow-950: #574200;
+  --ciam-color-red-50: #fff5f7;
+  --ciam-color-red-100: #fee7eb;
+  --ciam-color-red-200: #fdc9d3;
+  --ciam-color-red-300: #fba7b7;
+  --ciam-color-red-400: #f97b92;
+  --ciam-color-red-500: #f51d43;
+  --ciam-color-red-600: #e60a32;
+  --ciam-color-red-700: #c3092b;
+  --ciam-color-red-800: #a10723;
+  --ciam-color-red-900: #75051a;
+  --ciam-color-red-950: #530412;
+  --ciam-color-blue-50: #f7fafd;
+  --ciam-color-blue-100: #ecf2f9;
+  --ciam-color-blue-200: #d4e3f2;
+  --ciam-color-blue-300: #bdd4ea;
+  --ciam-color-blue-400: #a2c3e2;
+  --ciam-color-blue-500: #7facd7;
+  --ciam-color-blue-600: #5893cb;
+  --ciam-color-blue-700: #3470a8;
+  --ciam-color-blue-800: #2b5d8c;
+  --ciam-color-blue-900: #1e4161;
+  --ciam-color-blue-950: #17314a;
+  --ciam-color-teal-50: #f1f8f8;
+  --ciam-color-teal-100: #e3f2f1;
+  --ciam-color-teal-200: #c1e2df;
+  --ciam-color-teal-300: #9ed1cd;
+  --ciam-color-teal-400: #6dbab4;
+  --ciam-color-teal-500: #4a9d96;
+  --ciam-color-teal-600: #438e88;
+  --ciam-color-teal-700: #3b7d77;
+  --ciam-color-teal-800: #2f6460;
+  --ciam-color-teal-900: #244c49;
+  --ciam-color-teal-950: #193432;
+}

+ 107 - 0
services/web/frontend/stylesheets/pages/ciam-mixins.scss

@@ -0,0 +1,107 @@
+@mixin ciam-body-xs-regular() {
+  font-size: 0.75rem;
+  font-weight: 400;
+  line-height: 1.25rem;
+}
+
+@mixin ciam-body-xs-semibold() {
+  font-size: 0.75rem;
+  font-weight: 600;
+  line-height: 1.25rem;
+}
+
+@mixin ciam-body-sm-regular() {
+  font-size: 0.875rem;
+  font-weight: 400;
+  line-height: 1.25rem;
+}
+
+@mixin ciam-body-sm-semibold() {
+  font-size: 0.875rem;
+  font-weight: 600;
+  line-height: 1.25rem;
+}
+
+@mixin ciam-body-md-regular() {
+  font-size: 1rem;
+  font-weight: 400;
+  line-height: 1.5rem;
+}
+
+@mixin ciam-body-md-semibold() {
+  font-size: 1rem;
+  font-weight: 600;
+  line-height: 1.5rem;
+}
+
+@mixin ciam-body-lg-regular() {
+  font-size: 1.125rem;
+  font-weight: 400;
+  line-height: 1.75rem;
+}
+
+@mixin ciam-body-lg-semibold() {
+  font-size: 1.125rem;
+  font-weight: 600;
+  line-height: 1.75rem;
+}
+
+@mixin ciam-heading-xs-regular() {
+  font-size: 1.125rem;
+  font-weight: 400;
+  line-height: 1.5rem;
+}
+
+@mixin ciam-heading-xs-semibold() {
+  font-size: 1.125rem;
+  font-weight: 600;
+  line-height: 1.5rem;
+}
+
+@mixin ciam-heading-sm-regular() {
+  font-size: 1.25rem;
+  font-weight: 400;
+  line-height: 1.75rem;
+}
+
+@mixin ciam-heading-sm-semibold() {
+  font-size: 1.25rem;
+  font-weight: 600;
+  line-height: 1.75rem;
+}
+
+@mixin ciam-heading-md-regular() {
+  font-size: 1.5rem;
+  font-weight: 400;
+  line-height: 2rem;
+}
+
+@mixin ciam-heading-md-semibold() {
+  font-size: 1.5rem;
+  font-weight: 600;
+  line-height: 2rem;
+}
+
+@mixin ciam-heading-lg-regular() {
+  font-size: 2rem;
+  font-weight: 400;
+  line-height: 2.5rem;
+}
+
+@mixin ciam-heading-lg-semibold() {
+  font-size: 2rem;
+  font-weight: 600;
+  line-height: 2.5rem;
+}
+
+@mixin ciam-heading-xl-regular() {
+  font-size: 2.5rem;
+  font-weight: 400;
+  line-height: 3rem;
+}
+
+@mixin ciam-heading-xl-semibold() {
+  font-size: 2.5rem;
+  font-weight: 600;
+  line-height: 3rem;
+}

+ 35 - 0
services/web/frontend/stylesheets/pages/ciam-variables.scss

@@ -0,0 +1,35 @@
+@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;
+
+  // 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
+  --link-color: var(--ciam-color-text-secondary);
+  --link-hover-color: fuchsia;
+
+  // TODO: validate that this is correct
+  --link-visited-color: var(--ciam-color-text-secondary);
+  --link-color-dark: fuchsia;
+  --link-hover-color-dark: fuchsia;
+  --link-visited-color-dark: fuchsia;
+  --link-text-decoration: underline;
+  --link-hover-text-decoration: none;
+}

+ 70 - 0
services/web/frontend/stylesheets/pages/ciam.scss

@@ -0,0 +1,70 @@
+@import 'ciam-variables';
+@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;
+    }
+  }
+}