diff options
| author | Armand Philippot <git@armandphilippot.com> | 2022-05-02 18:57:29 +0200 | 
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2022-05-03 15:22:24 +0200 | 
| commit | 732d0943f8041d76262222a092b014f2557085ef (patch) | |
| tree | 16f6f76648b479a9591400ab15bb3e9c914f2226 /src/components | |
| parent | ca921d7536cfe950b5a7d442977bbf900b48faf4 (diff) | |
chore: add homepage
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/atoms/lists/list.module.scss | 16 | ||||
| -rw-r--r-- | src/components/atoms/lists/list.stories.tsx | 13 | ||||
| -rw-r--r-- | src/components/atoms/lists/list.tsx | 4 | ||||
| -rw-r--r-- | src/components/molecules/layout/card.module.scss | 3 | ||||
| -rw-r--r-- | src/components/molecules/layout/card.tsx | 2 | ||||
| -rw-r--r-- | src/components/molecules/layout/page-header.tsx | 2 | ||||
| -rw-r--r-- | src/components/molecules/nav/nav.stories.tsx | 26 | ||||
| -rw-r--r-- | src/components/molecules/nav/nav.tsx | 7 | ||||
| -rw-r--r-- | src/components/organisms/layout/cards-list.module.scss | 4 | ||||
| -rw-r--r-- | src/components/organisms/layout/cards-list.stories.tsx | 13 | ||||
| -rw-r--r-- | src/components/organisms/layout/cards-list.tsx | 8 | ||||
| -rw-r--r-- | src/components/templates/layout/layout.module.scss | 9 | ||||
| -rw-r--r-- | src/components/templates/layout/layout.stories.tsx | 10 | ||||
| -rw-r--r-- | src/components/templates/layout/layout.tsx | 107 | 
14 files changed, 183 insertions, 41 deletions
| diff --git a/src/components/atoms/lists/list.module.scss b/src/components/atoms/lists/list.module.scss index df3b49c..f647072 100644 --- a/src/components/atoms/lists/list.module.scss +++ b/src/components/atoms/lists/list.module.scss @@ -1,3 +1,5 @@ +@use "@styles/abstracts/placeholders"; +  .list {    margin: 0; @@ -36,4 +38,18 @@        margin-bottom: var(--spacing-2xs);      }    } + +  &--flex { +    @extend %reset-list; + +    display: flex; +    flex-flow: row wrap; +    gap: var(--spacing-sm); +  } + +  &--flex &--flex { +    display: initial; +    position: relative; +    top: var(--spacing-2xs); +  }  } diff --git a/src/components/atoms/lists/list.stories.tsx b/src/components/atoms/lists/list.stories.tsx index 3a80962..54fdd3a 100644 --- a/src/components/atoms/lists/list.stories.tsx +++ b/src/components/atoms/lists/list.stories.tsx @@ -39,8 +39,8 @@ export default {        control: {          type: 'select',        }, -      description: 'The list kind: ordered or unordered.', -      options: ['ordered', 'unordered'], +      description: 'The list kind: flex, ordered or unordered.', +      options: ['flex', 'ordered', 'unordered'],        table: {          category: 'Options',          defaultValue: { summary: 'unordered' }, @@ -72,6 +72,15 @@ const items: ListItem[] = [  ];  /** + * List Stories - Flex list + */ +export const Flex = Template.bind({}); +Flex.args = { +  items, +  kind: 'flex', +}; + +/**   * List Stories - Ordered list   */  export const Ordered = Template.bind({}); diff --git a/src/components/atoms/lists/list.tsx b/src/components/atoms/lists/list.tsx index 6726802..711ade1 100644 --- a/src/components/atoms/lists/list.tsx +++ b/src/components/atoms/lists/list.tsx @@ -30,9 +30,9 @@ export type ListProps = {     */    itemsClassName?: string;    /** -   * The list kind (ordered or unordered). +   * The list kind.     */ -  kind?: 'ordered' | 'unordered'; +  kind?: 'ordered' | 'unordered' | 'flex';    /**     * Set margin between list items. Default: true.     */ diff --git a/src/components/molecules/layout/card.module.scss b/src/components/molecules/layout/card.module.scss index 85c319a..d5b9836 100644 --- a/src/components/molecules/layout/card.module.scss +++ b/src/components/molecules/layout/card.module.scss @@ -19,7 +19,8 @@    .cover {      align-self: flex-start; -    max-height: fun.convert-px(150); +    place-content: center; +    height: fun.convert-px(150);      margin: auto;      border-bottom: fun.convert-px(1) solid var(--color-border);    } diff --git a/src/components/molecules/layout/card.tsx b/src/components/molecules/layout/card.tsx index 15927e9..89f100e 100644 --- a/src/components/molecules/layout/card.tsx +++ b/src/components/molecules/layout/card.tsx @@ -93,7 +93,7 @@ const Card: FC<CardProps> = ({              {title}            </Heading>          </header> -        {tagline && <div className={styles.tagline}>{tagline}</div>} +        <div className={styles.tagline}>{tagline}</div>          {meta && (            <footer className={styles.footer}>              <DescriptionList diff --git a/src/components/molecules/layout/page-header.tsx b/src/components/molecules/layout/page-header.tsx index 174e246..1663085 100644 --- a/src/components/molecules/layout/page-header.tsx +++ b/src/components/molecules/layout/page-header.tsx @@ -11,7 +11,7 @@ export type PageHeaderProps = {    /**     * The page introduction.     */ -  intro?: string; +  intro?: string | JSX.Element;    /**     * The page metadata.     */ diff --git a/src/components/molecules/nav/nav.stories.tsx b/src/components/molecules/nav/nav.stories.tsx index 25455fd..5cef5f0 100644 --- a/src/components/molecules/nav/nav.stories.tsx +++ b/src/components/molecules/nav/nav.stories.tsx @@ -11,6 +11,19 @@ export default {    title: 'Molecules/Navigation/Nav',    component: NavComponent,    argTypes: { +    'aria-label': { +      control: { +        type: 'text', +      }, +      description: 'An accessible name for the navigation.', +      table: { +        category: 'Accessibility', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    },      className: {        control: {          type: 'text', @@ -46,6 +59,19 @@ export default {          required: true,        },      }, +    listClassName: { +      control: { +        type: 'text', +      }, +      description: 'Set additional classnames to the navigation list.', +      table: { +        category: 'Styles', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    },    },    decorators: [      (Story) => ( diff --git a/src/components/molecules/nav/nav.tsx b/src/components/molecules/nav/nav.tsx index 2666ea2..581f813 100644 --- a/src/components/molecules/nav/nav.tsx +++ b/src/components/molecules/nav/nav.tsx @@ -24,6 +24,10 @@ export type NavItem = {  export type NavProps = {    /** +   * An accessible name. +   */ +  'aria-label'?: string; +  /**     * Set additional classnames to the navigation wrapper.     */    className?: string; @@ -51,6 +55,7 @@ const Nav: FC<NavProps> = ({    items,    kind,    listClassName = '', +  ...props  }) => {    const kindClass = `nav--${kind}`; @@ -71,7 +76,7 @@ const Nav: FC<NavProps> = ({    };    return ( -    <nav className={`${styles[kindClass]} ${className}`}> +    <nav className={`${styles[kindClass]} ${className}`} {...props}>        <ul className={`${styles.nav__list} ${listClassName}`}>{getItems()}</ul>      </nav>    ); diff --git a/src/components/organisms/layout/cards-list.module.scss b/src/components/organisms/layout/cards-list.module.scss index 9fe428c..2763585 100644 --- a/src/components/organisms/layout/cards-list.module.scss +++ b/src/components/organisms/layout/cards-list.module.scss @@ -1,12 +1,10 @@  @use "@styles/abstracts/placeholders";  .wrapper { -  --card-width: 30ch; -    display: grid;    grid-template-columns: repeat(      auto-fit, -    min(calc(100vw - (var(--spacing-md) * 2)), var(--card-width)) +    min(calc(100vw - (var(--spacing-md) * 2)), var(--card-width, 30ch))    );    gap: var(--spacing-sm);    place-content: center; diff --git a/src/components/organisms/layout/cards-list.stories.tsx b/src/components/organisms/layout/cards-list.stories.tsx index 7ff4365..fe0ebfd 100644 --- a/src/components/organisms/layout/cards-list.stories.tsx +++ b/src/components/organisms/layout/cards-list.stories.tsx @@ -12,6 +12,19 @@ export default {      kind: 'unordered',    },    argTypes: { +    className: { +      control: { +        type: 'text', +      }, +      description: 'Set additional classnames to the list wrapper.', +      table: { +        category: 'Styles', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    },      coverFit: {        control: {          type: 'select', diff --git a/src/components/organisms/layout/cards-list.tsx b/src/components/organisms/layout/cards-list.tsx index 33ffe23..1558d7c 100644 --- a/src/components/organisms/layout/cards-list.tsx +++ b/src/components/organisms/layout/cards-list.tsx @@ -15,6 +15,10 @@ export type CardsListItem = Omit<  export type CardsListProps = {    /** +   * Set additional classnames to the list wrapper. +   */ +  className?: string; +  /**     * The cover fit.     */    coverFit?: CardProps['coverFit']; @@ -38,6 +42,7 @@ export type CardsListProps = {   * Return a list of Card components.   */  const CardsList: FC<CardsListProps> = ({ +  className = '',    coverFit,    items,    kind = 'unordered', @@ -70,9 +75,10 @@ const CardsList: FC<CardsListProps> = ({    return (      <List +      kind="flex"        items={getCards(items)}        withMargin={false} -      className={`${styles.wrapper} ${styles[kindModifier]}`} +      className={`${styles.wrapper} ${styles[kindModifier]} ${className}`}      />    );  }; diff --git a/src/components/templates/layout/layout.module.scss b/src/components/templates/layout/layout.module.scss index 3533257..806d2d7 100644 --- a/src/components/templates/layout/layout.module.scss +++ b/src/components/templates/layout/layout.module.scss @@ -1,15 +1,6 @@  @use "@styles/abstracts/functions" as fun;  @use "@styles/abstracts/mixins" as mix; -:global { -  #__next { -    flex: 1; -    display: flex; -    flex-flow: column nowrap; -    min-height: 100vh; -  } -} -  .header {    border-bottom: fun.convert-px(3) solid var(--color-border-light);  } diff --git a/src/components/templates/layout/layout.stories.tsx b/src/components/templates/layout/layout.stories.tsx index f3579e3..2415412 100644 --- a/src/components/templates/layout/layout.stories.tsx +++ b/src/components/templates/layout/layout.stories.tsx @@ -36,7 +36,15 @@ export default {    decorators: [      (Story) => (        <IntlProvider locale="en"> -        <div id="__next"> +        <div +          id="__next" +          style={{ +            flex: 1, +            display: 'flex', +            flexFlow: 'column nowrap', +            minHeight: '100vh', +          }} +        >            <Story />          </div>        </IntlProvider> diff --git a/src/components/templates/layout/layout.tsx b/src/components/templates/layout/layout.tsx index 601ced4..e1be1af 100644 --- a/src/components/templates/layout/layout.tsx +++ b/src/components/templates/layout/layout.tsx @@ -1,4 +1,3 @@ -import photo from '@assets/images/armand-philippot.jpg';  import ButtonLink from '@components/atoms/buttons/button-link';  import Career from '@components/atoms/icons/career';  import CCBySA from '@components/atoms/icons/cc-by-sa'; @@ -8,13 +7,19 @@ import Home from '@components/atoms/icons/home';  import PostsStack from '@components/atoms/icons/posts-stack';  import Main from '@components/atoms/layout/main';  import NoScript from '@components/atoms/layout/no-script'; -import Footer from '@components/organisms/layout/footer'; -import Header, { HeaderProps } from '@components/organisms/layout/header'; -import { settings } from '@utils/config'; +import Footer, { FooterProps } from '@components/organisms/layout/footer'; +import Header, { type HeaderProps } from '@components/organisms/layout/header'; +import useSettings from '@utils/hooks/use-settings'; +import Script from 'next/script';  import { FC, ReactNode } from 'react';  import { useIntl } from 'react-intl'; +import { Person, SearchAction, WebSite, WithContext } from 'schema-dts';  import styles from './layout.module.scss'; +export type QueryAction = SearchAction & { +  'query-input': string; +}; +  export type LayoutProps = Pick<HeaderProps, 'isHome'> & {    /**     * The layout main content. @@ -33,6 +38,9 @@ export type LayoutProps = Pick<HeaderProps, 'isHome'> & {   */  const Layout: FC<LayoutProps> = ({ children, isHome, ...props }) => {    const intl = useIntl(); +  const { website } = useSettings(); +  const { baseline, copyright, locales, name, picture, url } = website; +    const skipToContent = intl.formatMessage({      defaultMessage: 'Skip to content',      description: 'Layout: Skip to content link', @@ -45,12 +53,12 @@ const Layout: FC<LayoutProps> = ({ children, isHome, ...props }) => {      id: '7jVUT6',    }); -  const copyright = { +  const copyrightData = {      dates: { -      start: settings.copyright.startYear, -      end: settings.copyright.endYear, +      start: copyright.start, +      end: copyright.end,      }, -    owner: settings.name, +    owner: name,      icon: <CCBySA />,    }; @@ -80,21 +88,77 @@ const Layout: FC<LayoutProps> = ({ children, isHome, ...props }) => {      id: 'AE4kCD',    }); -  const nav: HeaderProps['nav'] = [ -    { id: 'home', label: homeLabel, href: '#', logo: <Home /> }, -    { id: 'blog', label: blogLabel, href: '#', logo: <PostsStack /> }, +  const mainNav: HeaderProps['nav'] = [ +    { id: 'home', label: homeLabel, href: '/', logo: <Home /> }, +    { id: 'blog', label: blogLabel, href: '/blog', logo: <PostsStack /> },      {        id: 'projects',        label: projectsLabel, -      href: '#', +      href: '/projets',        logo: <ComputerScreen />,      }, -    { id: 'cv', label: cvLabel, href: '#', logo: <Career /> }, -    { id: 'contact', label: contactLabel, href: '#', logo: <Envelop /> }, +    { id: 'cv', label: cvLabel, href: '/cv', logo: <Career /> }, +    { id: 'contact', label: contactLabel, href: '/contact', logo: <Envelop /> }, +  ]; + +  const legalNoticeLabel = intl.formatMessage({ +    defaultMessage: 'Legal notice', +    description: 'Layout: Legal notice label', +    id: 'nwbzKm', +  }); + +  const footerNav: FooterProps['navItems'] = [ +    { id: 'legal-notice', label: legalNoticeLabel, href: '/mentions-legales' },    ]; +  const searchActionSchema: QueryAction = { +    '@type': 'SearchAction', +    target: { +      '@type': 'EntryPoint', +      urlTemplate: `${url}/recherche?s={search_term_string}`, +    }, +    query: 'required', +    'query-input': 'required name=search_term_string', +  }; + +  const schemaJsonLd: WithContext<WebSite> = { +    '@context': 'https://schema.org', +    '@id': `${url}`, +    '@type': 'WebSite', +    name: name, +    description: baseline, +    url: url, +    author: { '@id': `${url}/#branding` }, +    copyrightYear: Number(copyright.start), +    creator: { '@id': `${url}/#branding` }, +    editor: { '@id': `${url}/#branding` }, +    inLanguage: locales.default, +    potentialAction: searchActionSchema, +  }; + +  const brandingSchema: WithContext<Person> = { +    '@context': 'https://schema.org', +    '@type': 'Person', +    '@id': `${url}/#branding`, +    name: name, +    url: url, +    jobTitle: baseline, +    image: picture.src, +    subjectOf: { '@id': `${url}` }, +  }; +    return (      <> +      <Script +        id="schema-layout" +        type="application/ld+json" +        dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }} +      ></Script> +      <Script +        id="schema-branding" +        type="application/ld+json" +        dangerouslySetInnerHTML={{ __html: JSON.stringify(brandingSchema) }} +      />        <noscript>          <div className={styles['noscript-spacing']}></div>        </noscript> @@ -103,10 +167,10 @@ const Layout: FC<LayoutProps> = ({ children, isHome, ...props }) => {          {skipToContent}        </ButtonLink>        <Header -        title={settings.name} -        baseline={settings.baseline.fr} -        photo={photo.src} -        nav={nav} +        title={name} +        baseline={baseline} +        photo={picture} +        nav={mainNav}          isHome={isHome}          className={styles.header}          withLink={true} @@ -114,7 +178,12 @@ const Layout: FC<LayoutProps> = ({ children, isHome, ...props }) => {        <Main id="main" className={styles.main}>          <article {...props}>{children}</article>        </Main> -      <Footer copyright={copyright} topId="top" className={styles.footer} /> +      <Footer +        copyright={copyrightData} +        navItems={footerNav} +        topId="top" +        className={styles.footer} +      />        <noscript>          <NoScript message={noScript} position="top" />        </noscript> | 
