diff options
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> |
