diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-10-25 18:33:22 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-11 18:15:27 +0100 |
| commit | 9aeb82269d7c74c4566b7ca254782a4dfbd69a6e (patch) | |
| tree | e2ab930204cbcf20cebaa6125ea022f050d973af /src | |
| parent | c21a137e1991af1331fe5768fc6bac15ea9230b1 (diff) | |
refactor(components): remove SiteHeader and SiteFooter components
They do not help to make the layout more readable (on the contrary I
think...) so the props drilling is useless.
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/organisms/layout/index.ts | 2 | ||||
| -rw-r--r-- | src/components/organisms/layout/site-footer.module.scss | 26 | ||||
| -rw-r--r-- | src/components/organisms/layout/site-footer.stories.tsx | 92 | ||||
| -rw-r--r-- | src/components/organisms/layout/site-footer.test.tsx | 38 | ||||
| -rw-r--r-- | src/components/organisms/layout/site-footer.tsx | 78 | ||||
| -rw-r--r-- | src/components/organisms/layout/site-header.module.scss | 50 | ||||
| -rw-r--r-- | src/components/organisms/layout/site-header.stories.tsx | 162 | ||||
| -rw-r--r-- | src/components/organisms/layout/site-header.test.tsx | 59 | ||||
| -rw-r--r-- | src/components/organisms/layout/site-header.tsx | 47 | ||||
| -rw-r--r-- | src/components/templates/layout/layout.module.scss | 65 | ||||
| -rw-r--r-- | src/components/templates/layout/layout.tsx | 151 | ||||
| -rw-r--r-- | src/i18n/en.json | 8 | ||||
| -rw-r--r-- | src/i18n/fr.json | 8 |
13 files changed, 159 insertions, 627 deletions
diff --git a/src/components/organisms/layout/index.ts b/src/components/organisms/layout/index.ts index 4593ccc..86670e3 100644 --- a/src/components/organisms/layout/index.ts +++ b/src/components/organisms/layout/index.ts @@ -3,6 +3,4 @@ export * from './comments-list'; export * from './no-results'; export * from './overview'; export * from './posts-list'; -export * from './site-footer'; -export * from './site-header'; export * from './summary'; diff --git a/src/components/organisms/layout/site-footer.module.scss b/src/components/organisms/layout/site-footer.module.scss deleted file mode 100644 index 158c419..0000000 --- a/src/components/organisms/layout/site-footer.module.scss +++ /dev/null @@ -1,26 +0,0 @@ -@use "../../../styles/abstracts/mixins" as mix; - -.wrapper { - display: flex; - flex-flow: column wrap; - gap: var(--spacing-xs); - place-items: center; - place-content: center; - padding: var(--spacing-md) 0 calc(var(--toolbar-size) + var(--spacing-md)); - - @include mix.media("screen") { - @include mix.dimensions("sm") { - --toolbar-size: 0px; - - flex-flow: row wrap; - font-size: var(--font-size-sm); - } - } -} - -.back-to-top { - position: fixed; - bottom: calc(var(--toolbar-size, 0px) + var(--spacing-md)); - right: var(--spacing-md); - transition: all 0.4s ease-in 0s; -} diff --git a/src/components/organisms/layout/site-footer.stories.tsx b/src/components/organisms/layout/site-footer.stories.tsx deleted file mode 100644 index 3f244b0..0000000 --- a/src/components/organisms/layout/site-footer.stories.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import type { ComponentMeta, ComponentStory } from '@storybook/react'; -import { Icon } from '../../atoms'; -import { SiteFooter as SiteFooterComponent } from './site-footer'; - -/** - * SiteFooter - Storybook Meta - */ -export default { - title: 'Organisms/Layout', - component: SiteFooterComponent, - argTypes: { - backToTopClassName: { - control: { - type: 'text', - }, - description: 'Set additional classnames to the back to top button.', - table: { - category: 'Styles', - }, - type: { - name: 'string', - required: false, - }, - }, - className: { - control: { - type: 'text', - }, - description: 'Set additional classnames to the footer element.', - table: { - category: 'Styles', - }, - type: { - name: 'string', - required: false, - }, - }, - copyright: { - description: 'The copyright information.', - type: { - name: 'object', - required: true, - value: {}, - }, - }, - navItems: { - description: 'The footer nav items.', - table: { - category: 'Options', - }, - type: { - name: 'object', - required: false, - value: {}, - }, - }, - topId: { - control: { - type: 'text', - }, - description: - 'An element id (without hashtag) used as target by back to top button.', - type: { - name: 'string', - required: true, - }, - }, - }, -} as ComponentMeta<typeof SiteFooterComponent>; - -const Template: ComponentStory<typeof SiteFooterComponent> = (args) => ( - <SiteFooterComponent {...args} /> -); - -const copyright = { - from: '2017', - owner: 'Lorem ipsum', - to: '2022', -}; - -const navItems = [{ id: 'legal-notice', href: '#', label: 'Legal notice' }]; - -/** - * Layout Stories - SiteFooter - */ -export const SiteFooter = Template.bind({}); -SiteFooter.args = { - copyright, - license: <Icon shape="cc-by-sa" />, - navItems, - topId: 'top', -}; diff --git a/src/components/organisms/layout/site-footer.test.tsx b/src/components/organisms/layout/site-footer.test.tsx deleted file mode 100644 index 3ad4022..0000000 --- a/src/components/organisms/layout/site-footer.test.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { describe, expect, it } from '@jest/globals'; -import { render, screen as rtlScreen } from '../../../../tests/utils'; -import { SiteFooter, type SiteFooterProps } from './site-footer'; - -const copyright: SiteFooterProps['copyright'] = { - from: '2017', - owner: 'Lorem ipsum', -}; - -const navItems = [ - { id: 'legal-notice', href: '#', label: 'Legal notice' }, -] satisfies SiteFooterProps['navItems']; - -describe('SiteFooter', () => { - it('renders the website copyright', () => { - render(<SiteFooter copyright={copyright} topId="top" />); - expect( - rtlScreen.getByText(new RegExp(copyright.owner)) - ).toBeInTheDocument(); - expect(rtlScreen.getByText(new RegExp(copyright.from))).toBeInTheDocument(); - }); - - it('renders a back to top link', () => { - render(<SiteFooter copyright={copyright} topId="top" />); - expect( - rtlScreen.getByRole('link', { name: 'Back to top' }) - ).toBeInTheDocument(); - }); - - it('renders some nav items', () => { - render( - <SiteFooter copyright={copyright} navItems={navItems} topId="top" /> - ); - expect( - rtlScreen.getByRole('link', { name: navItems[0].label }) - ).toBeInTheDocument(); - }); -}); diff --git a/src/components/organisms/layout/site-footer.tsx b/src/components/organisms/layout/site-footer.tsx deleted file mode 100644 index ccab051..0000000 --- a/src/components/organisms/layout/site-footer.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import type { FC, ReactNode } from 'react'; -import { useIntl } from 'react-intl'; -import { Footer } from '../../atoms'; -import { - BackToTop, - type BackToTopProps, - Colophon, - type ColophonLink, - type CopyrightProps, - Copyright, -} from '../../molecules'; -import styles from './site-footer.module.scss'; - -export type SiteFooterProps = { - /** - * Set additional classnames to the back to top button. - */ - backToTopClassName?: BackToTopProps['className']; - /** - * Set additional classnames to the footer element. - */ - className?: string; - /** - * Set the copyright information. - */ - copyright: CopyrightProps; - /** - * The website license. - */ - license?: ReactNode; - /** - * The footer nav items. - */ - navItems?: ColophonLink[]; - /** - * An element id (without hashtag) used as anchor for back to top button. - */ - topId: string; -}; - -/** - * SiteFooter component - * - * Renders a footer with copyright and nav; - */ -export const SiteFooter: FC<SiteFooterProps> = ({ - backToTopClassName, - className = '', - copyright, - license, - navItems, - topId, -}) => { - const intl = useIntl(); - const backToTop = intl.formatMessage({ - defaultMessage: 'Back to top', - description: 'SiteFooter: an accessible name for the back to top button', - id: 'OHvb01', - }); - const backToTopAnchor = `#${topId}`; - const footerClass = `${styles.wrapper} ${className}`; - const btnClass = `${styles['back-to-top']} ${backToTopClassName}`; - - return ( - <Footer className={footerClass}> - <Colophon - copyright={<Copyright {...copyright} />} - license={license} - links={navItems} - /> - <BackToTop - anchor={backToTopAnchor} - className={btnClass} - label={backToTop} - /> - </Footer> - ); -}; diff --git a/src/components/organisms/layout/site-header.module.scss b/src/components/organisms/layout/site-header.module.scss deleted file mode 100644 index 573d455..0000000 --- a/src/components/organisms/layout/site-header.module.scss +++ /dev/null @@ -1,50 +0,0 @@ -@use "../../../styles/abstracts/functions" as fun; -@use "../../../styles/abstracts/mixins" as mix; - -.wrapper { - display: grid; - grid-template-columns: - minmax(0, 1fr) min(calc(100vw - calc(var(--spacing-md) * 2)), 100ch) - minmax(0, 1fr); - align-items: center; - padding: var(--spacing-md) 0 var(--spacing-lg); - - .toolbar { - justify-content: space-around; - position: fixed; - bottom: 0; - left: 0; - z-index: 5; - background: var(--color-bg); - border-top: fun.convert-px(4) solid; - border-image: radial-gradient( - ellipse at top, - var(--color-primary-lighter) 20%, - var(--color-primary) 100% - ) - 1; - box-shadow: 0 fun.convert-px(-2) fun.convert-px(3) fun.convert-px(-1) - var(--color-shadow-dark); - - @include mix.media("screen") { - @include mix.dimensions("sm") { - justify-content: flex-end; - width: auto; - position: relative; - left: unset; - background: inherit; - border: none; - box-shadow: none; - } - } - } -} - -.body { - grid-column: 2; - display: flex; - flex-flow: row wrap; - align-items: center; - justify-content: space-between; - gap: var(--spacing-md); -} diff --git a/src/components/organisms/layout/site-header.stories.tsx b/src/components/organisms/layout/site-header.stories.tsx deleted file mode 100644 index e69ebfd..0000000 --- a/src/components/organisms/layout/site-header.stories.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import type { ComponentMeta, ComponentStory } from '@storybook/react'; -import NextImage from 'next/image'; -import { Heading } from '../../atoms'; -import { SiteHeader as SiteHeaderComponent } from './site-header'; - -/** - * SiteHeader - Storybook Meta - */ -export default { - title: 'Organisms/Layout', - component: SiteHeaderComponent, - args: { - ackeeStorageKey: 'ackee-tracking', - isHome: false, - motionStorageKey: 'reduced-motion', - searchPage: '#', - withLink: false, - }, - argTypes: { - ackeeStorageKey: { - control: { - type: 'text', - }, - description: 'Set Ackee settings local storage key.', - type: { - name: 'string', - required: true, - }, - }, - baseline: { - control: { - type: 'text', - }, - description: 'The branding baseline.', - table: { - category: 'Options', - }, - type: { - name: 'string', - required: false, - }, - }, - className: { - control: { - type: 'text', - }, - description: 'Set additional classnames to the header wrapper.', - table: { - category: 'Styles', - }, - type: { - name: 'string', - required: false, - }, - }, - isHome: { - control: { - type: 'boolean', - }, - description: 'Determine if the current page is homepage or not.', - table: { - category: 'Options', - }, - type: { - name: 'boolean', - required: false, - }, - }, - motionStorageKey: { - control: { - type: 'text', - }, - description: 'Set Reduced motion settings local storage key.', - type: { - name: 'string', - required: true, - }, - }, - nav: { - description: 'The main navigation items.', - type: { - name: 'object', - required: true, - value: {}, - }, - }, - photo: { - control: { - type: 'text', - }, - description: 'The branding photo.', - type: { - name: 'string', - required: true, - }, - }, - searchPage: { - control: { - type: 'text', - }, - description: 'The search results page url.', - type: { - name: 'string', - required: true, - }, - }, - title: { - control: { - type: 'text', - }, - description: 'The website title.', - type: { - name: 'string', - required: true, - }, - }, - withLink: { - control: { - type: 'boolean', - }, - description: 'Wrap the website title with a link to homepage.', - table: { - category: 'Options', - }, - type: { - name: 'boolean', - required: false, - }, - }, - }, - parameters: { - layout: 'fullscreen', - }, -} as ComponentMeta<typeof SiteHeaderComponent>; - -const Template: ComponentStory<typeof SiteHeaderComponent> = (args) => ( - <SiteHeaderComponent {...args} /> -); - -const nav = [ - { id: 'home-link', href: '#', label: 'Home' }, - { id: 'blog-link', href: '#', label: 'Blog' }, - { id: 'cv-link', href: '#', label: 'CV' }, - { id: 'contact-link', href: '#', label: 'Contact' }, -]; - -/** - * Layout Stories - SiteHeader - */ -export const SiteHeader = Template.bind({}); -SiteHeader.args = { - nav, - logo: ( - <NextImage - alt="A logo" - height={100} - src="https://picsum.photos/100" - width={100} - /> - ), - name: <Heading level={1}>Website title</Heading>, -}; diff --git a/src/components/organisms/layout/site-header.test.tsx b/src/components/organisms/layout/site-header.test.tsx deleted file mode 100644 index fa1b7be..0000000 --- a/src/components/organisms/layout/site-header.test.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { describe, expect, it } from '@jest/globals'; -import NextImage from 'next/image'; -import { render, screen as rtlScreen } from '../../../../tests/utils'; -import { SiteHeader } from './site-header'; - -const nav = [ - { id: 'home-link', href: '#', label: 'Home' }, - { id: 'blog-link', href: '#', label: 'Blog' }, - { id: 'cv-link', href: '#', label: 'CV' }, - { id: 'contact-link', href: '#', label: 'Contact' }, -]; - -const title = 'Assumenda quis quod'; - -describe('SiteHeader', () => { - it('renders the website title', () => { - render( - <SiteHeader - ackeeStorageKey="ackee-tracking" - logo={ - <NextImage - alt="A logo" - height={200} - src="https://picsum.photos/200" - width={200} - /> - } - motionStorageKey="reduced-motion" - name={<h1>{title}</h1>} - nav={nav} - searchPage="#" - /> - ); - expect( - rtlScreen.getByRole('heading', { level: 1, name: title }) - ).toBeInTheDocument(); - }); - - it('renders the main nav', () => { - render( - <SiteHeader - ackeeStorageKey="ackee-tracking" - logo={ - <NextImage - alt="A photo" - height={200} - src="https://picsum.photos/200" - width={200} - /> - } - motionStorageKey="reduced-motion" - name={<div>{title}</div>} - nav={nav} - searchPage="#" - /> - ); - expect(rtlScreen.getByRole('navigation')).toBeInTheDocument(); - }); -}); diff --git a/src/components/organisms/layout/site-header.tsx b/src/components/organisms/layout/site-header.tsx deleted file mode 100644 index e8953c0..0000000 --- a/src/components/organisms/layout/site-header.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import type { FC } from 'react'; -import { Header } from '../../atoms'; -import { Branding, type BrandingProps } from '../../molecules'; -import { Toolbar, type ToolbarProps } from '../toolbar'; -import styles from './site-header.module.scss'; - -export type SiteHeaderProps = BrandingProps & - Pick< - ToolbarProps, - 'ackeeStorageKey' | 'motionStorageKey' | 'nav' | 'searchPage' - > & { - /** - * Set additional classnames to the header element. - */ - className?: string; - }; - -/** - * SiteHeader component - * - * Render the website header. - */ -export const SiteHeader: FC<SiteHeaderProps> = ({ - ackeeStorageKey, - className, - motionStorageKey, - nav, - searchPage, - ...props -}) => { - const headerClass = `${styles.wrapper} ${className}`; - - return ( - <Header className={headerClass}> - <div className={styles.body}> - <Branding {...props} /> - <Toolbar - ackeeStorageKey={ackeeStorageKey} - className={styles.toolbar} - motionStorageKey={motionStorageKey} - nav={nav} - searchPage={searchPage} - /> - </div> - </Header> - ); -}; diff --git a/src/components/templates/layout/layout.module.scss b/src/components/templates/layout/layout.module.scss index 4f00742..4695948 100644 --- a/src/components/templates/layout/layout.module.scss +++ b/src/components/templates/layout/layout.module.scss @@ -33,7 +33,22 @@ } .header { + display: grid; + grid-template-columns: + minmax(0, 1fr) min(calc(100vw - calc(var(--spacing-md) * 2)), 100ch) + minmax(0, 1fr); + align-items: center; + padding: var(--spacing-md) 0 var(--spacing-lg); border-bottom: fun.convert-px(3) solid var(--color-border-light); + + &__body { + grid-column: 2; + display: flex; + flex-flow: row wrap; + align-items: center; + justify-content: space-between; + gap: var(--spacing-md); + } } .brand { @@ -62,6 +77,36 @@ } } +.toolbar { + justify-content: space-around; + position: fixed; + bottom: 0; + left: 0; + z-index: 5; + background: var(--color-bg); + border-top: fun.convert-px(4) solid; + border-image: radial-gradient( + ellipse at top, + var(--color-primary-lighter) 20%, + var(--color-primary) 100% + ) + 1; + box-shadow: 0 fun.convert-px(-2) fun.convert-px(3) fun.convert-px(-1) + var(--color-shadow-dark); + + @include mix.media("screen") { + @include mix.dimensions("sm") { + justify-content: flex-end; + width: auto; + position: relative; + left: unset; + background: inherit; + border: none; + box-shadow: none; + } + } +} + .main { flex: 1; } @@ -80,10 +125,30 @@ } .footer { + display: flex; + flex-flow: column wrap; + gap: var(--spacing-xs); + place-items: center; + place-content: center; + padding: var(--spacing-md) 0 calc(var(--toolbar-size) + var(--spacing-md)); border-top: fun.convert-px(3) solid var(--color-border-light); + + @include mix.media("screen") { + @include mix.dimensions("sm") { + --toolbar-size: 0px; + + flex-flow: row wrap; + font-size: var(--font-size-sm); + } + } } .back-to-top { + position: fixed; + bottom: calc(var(--toolbar-size, 0px) + var(--spacing-md)); + right: var(--spacing-md); + transition: all 0.4s ease-in 0s; + &--hidden { opacity: 0; transform: translateY(calc(var(--button-height) + var(--spacing-md))); diff --git a/src/components/templates/layout/layout.tsx b/src/components/templates/layout/layout.tsx index 2369669..bf9629c 100644 --- a/src/components/templates/layout/layout.tsx +++ b/src/components/templates/layout/layout.tsx @@ -18,15 +18,25 @@ import { useScrollPosition, useSettings, } from '../../../utils/hooks'; -import { ButtonLink, Heading, Icon, Logo, Main } from '../../atoms'; import { - SiteFooter, - type SiteFooterProps, - SiteHeader, - type SiteHeaderProps, -} from '../../organisms'; + ButtonLink, + Footer, + Header, + Heading, + Icon, + Logo, + Main, +} from '../../atoms'; +import { + BackToTop, + Branding, + Colophon, + type ColophonLink, + Copyright, + FlippingLogo, +} from '../../molecules'; +import { type MainNavItem, Toolbar } from '../../organisms'; import styles from './layout.module.scss'; -import { FlippingLogo } from 'src/components/molecules'; export type QueryAction = SearchAction & { 'query-input': string; @@ -87,12 +97,6 @@ export const Layout: FC<LayoutProps> = ({ id: 'yB1SPF', }); - const copyrightData = { - from: copyright.start, - owner: name, - to: copyright.end, - }; - const homeLabel = intl.formatMessage({ defaultMessage: 'Home', description: 'Layout: main nav - home link', @@ -134,8 +138,13 @@ export const Layout: FC<LayoutProps> = ({ }, { website: name } ); + const backToTop = intl.formatMessage({ + defaultMessage: 'Back to top', + description: 'Layout: an accessible name for the back to top button', + id: 'Kjj1Zk', + }); - const mainNav: SiteHeaderProps['nav'] = [ + const mainNav: MainNavItem[] = [ { id: 'home', label: homeLabel, @@ -174,7 +183,7 @@ export const Layout: FC<LayoutProps> = ({ id: 'nwbzKm', }); - const footerNav: SiteFooterProps['navItems'] = [ + const footerNav: ColophonLink[] = [ { id: 'legal-notice', label: legalNoticeLabel, href: ROUTES.LEGAL_NOTICE }, ]; @@ -215,14 +224,14 @@ export const Layout: FC<LayoutProps> = ({ }; const [backToTopClassName, setBackToTopClassName] = useState<string>( - styles['back-to-top--hidden'] + `${styles['back-to-top']} ${styles['back-to-top--hidden']}` ); const updateBackToTopClassName = () => { const visibleBreakpoint = 300; setBackToTopClassName( window.scrollY > visibleBreakpoint - ? styles['back-to-top--visible'] - : styles['back-to-top--hidden'] + ? `${styles['back-to-top']} ${styles['back-to-top--visible']}` + : `${styles['back-to-top']} ${styles['back-to-top--hidden']}` ); }; @@ -265,48 +274,54 @@ export const Layout: FC<LayoutProps> = ({ <ButtonLink className="screen-reader-text" to="#main"> {skipToContent} </ButtonLink> - <SiteHeader - // eslint-disable-next-line react/jsx-no-literals -- Storage key allowed - ackeeStorageKey="ackee-tracking" - baseline={ - <div - className={styles.brand__baseline} - style={brandingBaselineStyles} - > - {baseline} - </div> - } - className={styles.header} - logo={ - <FlippingLogo - back={<Logo heading={logoTitle} />} - className={styles.brand__logo} - front={ - <NextImage - alt={photoAltText} - height={120} - src="/armand-philippot.jpg" - width={120} + <Header className={styles.header}> + <div className={styles.header__body}> + <Branding + baseline={ + <div + className={styles.brand__baseline} + style={brandingBaselineStyles} + > + {baseline} + </div> + } + logo={ + <FlippingLogo + back={<Logo heading={logoTitle} />} + className={styles.brand__logo} + front={ + <NextImage + alt={photoAltText} + height={120} + src="/armand-philippot.jpg" + width={120} + /> + } /> } + name={ + <Heading + className={styles.brand__title} + isFake={!isHome} + level={1} + style={brandingTitleStyles} + > + {name} + </Heading> + } + url="/" /> - } - // eslint-disable-next-line react/jsx-no-literals -- Storage key allowed - motionStorageKey="reduced-motion" - name={ - <Heading - className={styles.brand__title} - isFake={!isHome} - level={1} - style={brandingTitleStyles} - > - {name} - </Heading> - } - nav={mainNav} - searchPage={ROUTES.SEARCH} - url="/" - /> + <Toolbar + // eslint-disable-next-line react/jsx-no-literals -- Storage key allowed + ackeeStorageKey="ackee-tracking" + className={styles.toolbar} + // eslint-disable-next-line react/jsx-no-literals -- Storage key allowed + motionStorageKey="reduced-motion" + nav={mainNav} + searchPage={ROUTES.SEARCH} + /> + </div> + </Header> <Main id="main" className={styles.main}> <article className={`${styles[articleGridClass]} ${styles[articleCommentsClass]}`} @@ -314,14 +329,20 @@ export const Layout: FC<LayoutProps> = ({ {children} </article> </Main> - <SiteFooter - backToTopClassName={backToTopClassName} - className={styles.footer} - copyright={copyrightData} - license={<Icon heading={copyrightTitle} shape="cc-by-sa" size="lg" />} - navItems={footerNav} - topId="top" - /> + <Footer className={styles.footer}> + <Colophon + copyright={ + <Copyright from={copyright.start} owner={name} to={copyright.end} /> + } + license={<Icon heading={copyrightTitle} shape="cc-by-sa" size="lg" />} + links={footerNav} + /> + <BackToTop + anchor="#top" + className={backToTopClassName} + label={backToTop} + /> + </Footer> <noscript> <div className={styles.noscript}>{noScript}</div> </noscript> diff --git a/src/i18n/en.json b/src/i18n/en.json index 2fd2edf..5984b2d 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -291,6 +291,10 @@ "defaultMessage": "Other thematics", "description": "ThematicPage: other thematics list widget title" }, + "Kjj1Zk": { + "defaultMessage": "Back to top", + "description": "Layout: an accessible name for the back to top button" + }, "KnWeKh": { "defaultMessage": "Page not found", "description": "Error404Page: page title" @@ -331,10 +335,6 @@ "defaultMessage": "Github profile", "description": "ProjectsPage: Github profile link" }, - "OHvb01": { - "defaultMessage": "Back to top", - "description": "SiteFooter: an accessible name for the back to top button" - }, "OL0Yzx": { "defaultMessage": "Publish", "description": "CommentForm: submit button" diff --git a/src/i18n/fr.json b/src/i18n/fr.json index bebfc47..2b20b14 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -291,6 +291,10 @@ "defaultMessage": "Autres thématiques", "description": "ThematicPage: other thematics list widget title" }, + "Kjj1Zk": { + "defaultMessage": "Retour en haut de page", + "description": "Layout: an accessible name for the back to top button" + }, "KnWeKh": { "defaultMessage": "Page non trouvée", "description": "Error404Page: page title" @@ -331,10 +335,6 @@ "defaultMessage": "Profil Github", "description": "ProjectsPage: Github profile link" }, - "OHvb01": { - "defaultMessage": "Retour en haut de page", - "description": "SiteFooter: an accessible name for the back to top button" - }, "OL0Yzx": { "defaultMessage": "Publier", "description": "CommentForm: submit button" |
