aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/molecules/buttons/back-to-top.module.scss50
-rw-r--r--src/components/molecules/buttons/back-to-top.test.tsx14
-rw-r--r--src/components/molecules/buttons/back-to-top.tsx46
-rw-r--r--src/components/molecules/buttons/back-to-top/back-to-top.module.scss54
-rw-r--r--src/components/molecules/buttons/back-to-top/back-to-top.stories.tsx (renamed from src/components/molecules/buttons/back-to-top.stories.tsx)17
-rw-r--r--src/components/molecules/buttons/back-to-top/back-to-top.test.tsx17
-rw-r--r--src/components/molecules/buttons/back-to-top/back-to-top.tsx45
-rw-r--r--src/components/molecules/buttons/back-to-top/index.ts1
-rw-r--r--src/components/organisms/layout/site-footer.tsx12
-rw-r--r--src/components/templates/layout/layout.module.scss2
-rw-r--r--src/i18n/en.json8
-rw-r--r--src/i18n/fr.json8
12 files changed, 151 insertions, 123 deletions
diff --git a/src/components/molecules/buttons/back-to-top.module.scss b/src/components/molecules/buttons/back-to-top.module.scss
deleted file mode 100644
index ba746df..0000000
--- a/src/components/molecules/buttons/back-to-top.module.scss
+++ /dev/null
@@ -1,50 +0,0 @@
-@use "../../../styles/abstracts/functions" as fun;
-
-.wrapper {
- .link {
- width: clamp(#{fun.convert-px(48)}, 8vw, #{fun.convert-px(55)});
- height: clamp(#{fun.convert-px(48)}, 8vw, #{fun.convert-px(55)});
- padding: 0;
-
- :global {
- .arrow-head {
- transform: translateX(-9%) translateY(30%) scale(1.2);
- transition: all 0.45s ease-in-out 0s;
- }
-
- .arrow-bar {
- opacity: 0;
- transform: translateY(30%) scaleY(0);
- transition:
- transform 0.45s ease-in-out 0s,
- opacity 0.1s linear 0.2s;
- }
- }
-
- &:hover,
- &:focus {
- :global {
- .arrow-head {
- transform: translateY(0) scale(1);
- }
-
- .arrow-bar {
- opacity: 1;
- transform: translateY(0) scaleY(1);
- }
- }
-
- svg {
- :global {
- animation: pulse 1.2s ease-in-out 0.6s infinite;
- }
- }
- }
-
- &:active {
- svg {
- animation-play-state: paused;
- }
- }
- }
-}
diff --git a/src/components/molecules/buttons/back-to-top.test.tsx b/src/components/molecules/buttons/back-to-top.test.tsx
deleted file mode 100644
index a775841..0000000
--- a/src/components/molecules/buttons/back-to-top.test.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { describe, expect, it } from '@jest/globals';
-import { render, screen as rtlScreen } from '../../../../tests/utils';
-import { BackToTop } from './back-to-top';
-
-describe('BackToTop', () => {
- it('renders a BackToTop link', () => {
- const id = 'top';
-
- render(<BackToTop to={id} />);
-
- expect(rtlScreen.getByRole('link')).toHaveAccessibleName('Back to top');
- expect(rtlScreen.getByRole('link')).toHaveAttribute('href', `#${id}`);
- });
-});
diff --git a/src/components/molecules/buttons/back-to-top.tsx b/src/components/molecules/buttons/back-to-top.tsx
deleted file mode 100644
index f2e2073..0000000
--- a/src/components/molecules/buttons/back-to-top.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import type { FC, HTMLAttributes } from 'react';
-import { useIntl } from 'react-intl';
-import { ButtonLink, Icon } from '../../atoms';
-import styles from './back-to-top.module.scss';
-
-export type BackToTopProps = HTMLAttributes<HTMLDivElement> & {
- /**
- * Define the element id to us as anchor.
- */
- to: string;
-};
-
-/**
- * BackToTop component
- *
- * Render a back to top link.
- */
-export const BackToTop: FC<BackToTopProps> = ({
- className = '',
- to,
- ...props
-}) => {
- const intl = useIntl();
- const linkName = intl.formatMessage({
- defaultMessage: 'Back to top',
- description: 'BackToTop: link text',
- id: 'm+SUSR',
- });
- const btnClass = `${styles.wrapper} ${className}`;
- const anchor = `#${to}`;
-
- return (
- <div {...props} className={btnClass}>
- <ButtonLink
- aria-label={linkName}
- className={styles.link}
- // eslint-disable-next-line react/jsx-no-literals -- Shape allowed
- shape="square"
- to={anchor}
- >
- {/* eslint-disable-next-line react/jsx-no-literals -- Config allowed */}
- <Icon aria-hidden={true} orientation="top" shape="arrow" />
- </ButtonLink>
- </div>
- );
-};
diff --git a/src/components/molecules/buttons/back-to-top/back-to-top.module.scss b/src/components/molecules/buttons/back-to-top/back-to-top.module.scss
new file mode 100644
index 0000000..10171d2
--- /dev/null
+++ b/src/components/molecules/buttons/back-to-top/back-to-top.module.scss
@@ -0,0 +1,54 @@
+@use "../../../../styles/abstracts/functions" as fun;
+
+.link {
+ --button-height: clamp(#{fun.convert-px(48)}, 8vw, #{fun.convert-px(55)});
+
+ height: var(--button-height);
+ padding: 0;
+
+ svg {
+ max-width: 100%;
+ max-height: 100%;
+ }
+
+ :global {
+ .arrow-head {
+ transform: translateX(-10%) translateY(30%) scale(1.2);
+ transition: all 0.45s ease-in-out 0s;
+ }
+
+ .arrow-bar {
+ opacity: 0;
+ transform: translateY(30%) scaleY(0);
+ transition:
+ transform 0.45s ease-in-out 0s,
+ opacity 0.1s linear 0.2s;
+ }
+ }
+
+ &:hover,
+ &:focus {
+ :global {
+ .arrow-head {
+ transform: translateY(0) scale(1);
+ }
+
+ .arrow-bar {
+ opacity: 1;
+ transform: translateY(0) scaleY(1);
+ }
+ }
+
+ svg {
+ :global {
+ animation: pulse 1.2s ease-in-out 0.6s infinite;
+ }
+ }
+ }
+
+ &:active {
+ svg {
+ animation-play-state: paused;
+ }
+ }
+}
diff --git a/src/components/molecules/buttons/back-to-top.stories.tsx b/src/components/molecules/buttons/back-to-top/back-to-top.stories.tsx
index 40acd33..3abb59d 100644
--- a/src/components/molecules/buttons/back-to-top.stories.tsx
+++ b/src/components/molecules/buttons/back-to-top/back-to-top.stories.tsx
@@ -8,6 +8,16 @@ export default {
title: 'Molecules/Buttons',
component: BackToTopComponent,
argTypes: {
+ anchor: {
+ control: {
+ type: 'text',
+ },
+ description: 'An element id with leading hashtag to use as anchor.',
+ type: {
+ name: 'string',
+ required: true,
+ },
+ },
className: {
control: {
type: 'text',
@@ -21,11 +31,11 @@ export default {
required: false,
},
},
- to: {
+ label: {
control: {
type: 'text',
},
- description: 'An element id (without hashtag) to use as anchor.',
+ description: 'An accessible name for the button.',
type: {
name: 'string',
required: true,
@@ -43,5 +53,6 @@ const Template: ComponentStory<typeof BackToTopComponent> = (args) => (
*/
export const BackToTop = Template.bind({});
BackToTop.args = {
- to: 'top',
+ anchor: '#top',
+ label: 'Back to top',
};
diff --git a/src/components/molecules/buttons/back-to-top/back-to-top.test.tsx b/src/components/molecules/buttons/back-to-top/back-to-top.test.tsx
new file mode 100644
index 0000000..00eb020
--- /dev/null
+++ b/src/components/molecules/buttons/back-to-top/back-to-top.test.tsx
@@ -0,0 +1,17 @@
+import { describe, expect, it } from '@jest/globals';
+import { render, screen as rtlScreen } from '@testing-library/react';
+import { BackToTop } from './back-to-top';
+
+describe('BackToTop', () => {
+ it('renders a BackToTop link', () => {
+ const anchor = '#top';
+ const label = 'eveniet';
+
+ render(<BackToTop anchor={anchor} label={label} />);
+
+ const link = rtlScreen.getByRole('link');
+
+ expect(link).toHaveAccessibleName(label);
+ expect(link).toHaveAttribute('href', anchor);
+ });
+});
diff --git a/src/components/molecules/buttons/back-to-top/back-to-top.tsx b/src/components/molecules/buttons/back-to-top/back-to-top.tsx
new file mode 100644
index 0000000..83e2161
--- /dev/null
+++ b/src/components/molecules/buttons/back-to-top/back-to-top.tsx
@@ -0,0 +1,45 @@
+import type { FC } from 'react';
+import {
+ ButtonLink,
+ type ButtonLinkProps,
+ Icon,
+ VisuallyHidden,
+} from '../../../atoms';
+import styles from './back-to-top.module.scss';
+
+export type BackToTopProps = Omit<
+ ButtonLinkProps,
+ 'children' | 'isExternal' | 'kind' | 'to'
+> & {
+ /**
+ * Define the anchor.
+ */
+ anchor: string;
+ /**
+ * Define an accessible label for the button.
+ */
+ label: string;
+};
+
+/**
+ * BackToTop component
+ *
+ * Render a back to top link.
+ */
+export const BackToTop: FC<BackToTopProps> = ({
+ anchor,
+ className = '',
+ label,
+ shape = 'square',
+ ...props
+}) => {
+ const btnClass = `${styles.link} ${className}`;
+
+ return (
+ <ButtonLink {...props} className={btnClass} shape={shape} to={anchor}>
+ {/* eslint-disable-next-line react/jsx-no-literals -- Config allowed */}
+ <Icon aria-hidden orientation="top" shape="arrow" />
+ <VisuallyHidden>{label}</VisuallyHidden>
+ </ButtonLink>
+ );
+};
diff --git a/src/components/molecules/buttons/back-to-top/index.ts b/src/components/molecules/buttons/back-to-top/index.ts
new file mode 100644
index 0000000..fa6b1c0
--- /dev/null
+++ b/src/components/molecules/buttons/back-to-top/index.ts
@@ -0,0 +1 @@
+export * from './back-to-top';
diff --git a/src/components/organisms/layout/site-footer.tsx b/src/components/organisms/layout/site-footer.tsx
index d767a4d..0866924 100644
--- a/src/components/organisms/layout/site-footer.tsx
+++ b/src/components/organisms/layout/site-footer.tsx
@@ -50,6 +50,12 @@ export const SiteFooter: FC<SiteFooterProps> = ({
description: 'SiteFooter: an accessible name for the footer nav',
id: 'pRzkFR',
});
+ 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}`;
@@ -69,7 +75,11 @@ export const SiteFooter: FC<SiteFooterProps> = ({
kind="footer"
/>
) : null}
- <BackToTop className={btnClass} to={topId} />
+ <BackToTop
+ anchor={backToTopAnchor}
+ className={btnClass}
+ label={backToTop}
+ />
</Footer>
);
};
diff --git a/src/components/templates/layout/layout.module.scss b/src/components/templates/layout/layout.module.scss
index d86340f..223fb93 100644
--- a/src/components/templates/layout/layout.module.scss
+++ b/src/components/templates/layout/layout.module.scss
@@ -30,7 +30,7 @@
.back-to-top {
&--hidden {
opacity: 0;
- transform: translateY(calc(var(--button-size) + var(--spacing-md)));
+ transform: translateY(calc(var(--button-height) + var(--spacing-md)));
visibility: hidden;
}
diff --git a/src/i18n/en.json b/src/i18n/en.json
index e50be99..cf1526b 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -299,6 +299,10 @@
"defaultMessage": "{postsCount, plural, =0 {No articles} one {# article} other {# articles}}",
"description": "BlogPage: posts count meta"
},
+ "OHvb01": {
+ "defaultMessage": "Back to top",
+ "description": "SiteFooter: an accessible name for the back to top button"
+ },
"OI0N37": {
"defaultMessage": "Written by:",
"description": "Meta: author label"
@@ -543,10 +547,6 @@
"defaultMessage": "Use Ctrl+c to copy",
"description": "usePrism: copy button error text"
},
- "m+SUSR": {
- "defaultMessage": "Back to top",
- "description": "BackToTop: link text"
- },
"nGss/j": {
"defaultMessage": "Ackee tracking (analytics)",
"description": "AckeeToggle: tooltip title"
diff --git a/src/i18n/fr.json b/src/i18n/fr.json
index 4e10be0..a3b21b8 100644
--- a/src/i18n/fr.json
+++ b/src/i18n/fr.json
@@ -299,6 +299,10 @@
"defaultMessage": "{postsCount, plural, =0 {0 article} one {# article} other {# articles}}",
"description": "BlogPage: posts count meta"
},
+ "OHvb01": {
+ "defaultMessage": "Retour en haut de page",
+ "description": "SiteFooter: an accessible name for the back to top button"
+ },
"OI0N37": {
"defaultMessage": "Écrit par :",
"description": "Meta: author label"
@@ -543,10 +547,6 @@
"defaultMessage": "Utilisez Ctrl+c pour copier",
"description": "usePrism: copy button error text"
},
- "m+SUSR": {
- "defaultMessage": "Retour en haut de page",
- "description": "BackToTop: link text"
- },
"nGss/j": {
"defaultMessage": "Suivi Ackee (analytique)",
"description": "AckeeToggle: tooltip title"