aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/molecules
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-10-04 18:17:35 +0200
committerArmand Philippot <git@armandphilippot.com>2023-11-11 18:14:41 +0100
commitbe61ffb6fe500cdbfa83b9cd131b8e72779f23c2 (patch)
tree9ac57f4fac173e13c0a461d75a206819be4f3c6f /src/components/molecules
parenta724b4b38bacc631410627395b0d1190a0e8de0d (diff)
refactor(components): rewrite BackToTop component
* replace `link` prop with `anchor` prop * add a `label` prop to let consumer handle the accessible name
Diffstat (limited to 'src/components/molecules')
-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
8 files changed, 131 insertions, 113 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';