aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/atoms
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2023-10-03 19:36:03 +0200
committerArmand Philippot <git@armandphilippot.com>2023-11-11 18:14:41 +0100
commit0e60743d140aff66eca6df712f653ee20f5d4ef3 (patch)
treee72bc8bf2314a26ba3c8e27e571d72e203bbf0c8 /src/components/atoms
parenta3fb0aa94717aafae897ac293488c43a099c0b2b (diff)
refactor(components): rewrite SocialLink component
* replace default label with a label prop * rename name prop to icon prop
Diffstat (limited to 'src/components/atoms')
-rw-r--r--src/components/atoms/links/social-link.test.tsx19
-rw-r--r--src/components/atoms/links/social-link.tsx51
-rw-r--r--src/components/atoms/links/social-link/index.ts1
-rw-r--r--src/components/atoms/links/social-link/social-link.module.scss (renamed from src/components/atoms/links/social-link.module.scss)15
-rw-r--r--src/components/atoms/links/social-link/social-link.stories.tsx (renamed from src/components/atoms/links/social-link.stories.tsx)27
-rw-r--r--src/components/atoms/links/social-link/social-link.test.tsx62
-rw-r--r--src/components/atoms/links/social-link/social-link.tsx65
7 files changed, 143 insertions, 97 deletions
diff --git a/src/components/atoms/links/social-link.test.tsx b/src/components/atoms/links/social-link.test.tsx
deleted file mode 100644
index d2609ca..0000000
--- a/src/components/atoms/links/social-link.test.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { describe, expect, it } from '@jest/globals';
-import { render, screen } from '../../../../tests/utils';
-import { SocialLink } from './social-link';
-
-/**
- * Next.js mock images to use next/image component. So for now, I need to mock
- * the svg files manually.
- */
-jest.mock('@assets/images/social-media/github.svg', () => 'svg-file');
-jest.mock('@assets/images/social-media/gitlab.svg', () => 'svg-file');
-jest.mock('@assets/images/social-media/linkedin.svg', () => 'svg-file');
-jest.mock('@assets/images/social-media/twitter.svg', () => 'svg-file');
-
-describe('SocialLink', () => {
- it('render a social link', () => {
- render(<SocialLink name="Github" url="#" />);
- expect(screen.getByRole('link')).toHaveAccessibleName('Github');
- });
-});
diff --git a/src/components/atoms/links/social-link.tsx b/src/components/atoms/links/social-link.tsx
deleted file mode 100644
index 9f8feb6..0000000
--- a/src/components/atoms/links/social-link.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import { FC } from 'react';
-import GithubIcon from '../../../assets/images/social-media/github.svg';
-import GitlabIcon from '../../../assets/images/social-media/gitlab.svg';
-import LinkedInIcon from '../../../assets/images/social-media/linkedin.svg';
-import TwitterIcon from '../../../assets/images/social-media/twitter.svg';
-import styles from './social-link.module.scss';
-
-export type SocialWebsite = 'Github' | 'Gitlab' | 'LinkedIn' | 'Twitter';
-
-export type SocialLinkProps = {
- /**
- * The social website name.
- */
- name: SocialWebsite;
- /**
- * The social profile url.
- */
- url: string;
-};
-
-/**
- * SocialLink component
- *
- * Render a social icon link.
- */
-export const SocialLink: FC<SocialLinkProps> = ({ name, url }) => {
- /**
- * Retrieve a social link icon by id.
- * @param {string} id - The social website id.
- */
- const getIcon = (id: string) => {
- switch (id) {
- case 'Github':
- return <GithubIcon className={styles.icon} aria-hidden="true" />;
- case 'Gitlab':
- return <GitlabIcon className={styles.icon} aria-hidden="true" />;
- case 'LinkedIn':
- return <LinkedInIcon className={styles.icon} aria-hidden="true" />;
- case 'Twitter':
- return <TwitterIcon className={styles.icon} aria-hidden="true" />;
- default:
- break;
- }
- };
-
- return (
- <a aria-label={name} className={styles.link} href={url}>
- {getIcon(name)}
- </a>
- );
-};
diff --git a/src/components/atoms/links/social-link/index.ts b/src/components/atoms/links/social-link/index.ts
new file mode 100644
index 0000000..9ac1b3b
--- /dev/null
+++ b/src/components/atoms/links/social-link/index.ts
@@ -0,0 +1 @@
+export * from './social-link';
diff --git a/src/components/atoms/links/social-link.module.scss b/src/components/atoms/links/social-link/social-link.module.scss
index 57dcf42..1aeab91 100644
--- a/src/components/atoms/links/social-link.module.scss
+++ b/src/components/atoms/links/social-link/social-link.module.scss
@@ -1,11 +1,11 @@
-@use "../../../styles/abstracts/functions" as fun;
+@use "../../../../styles/abstracts/functions" as fun;
.link {
display: flex;
width: var(--link-size, #{fun.convert-px(60)});
height: var(--link-size, #{fun.convert-px(60)});
- box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow),
+ box-shadow:
+ fun.convert-px(1) fun.convert-px(1) fun.convert-px(1) var(--color-shadow),
fun.convert-px(1) fun.convert-px(2) fun.convert-px(2) fun.convert-px(-1)
var(--color-shadow),
fun.convert-px(3) fun.convert-px(4) fun.convert-px(4) fun.convert-px(-3)
@@ -15,8 +15,8 @@
&:hover,
&:focus {
- box-shadow: fun.convert-px(1) fun.convert-px(1) fun.convert-px(1)
- var(--color-shadow),
+ box-shadow:
+ fun.convert-px(1) fun.convert-px(1) fun.convert-px(1) var(--color-shadow),
fun.convert-px(1) fun.convert-px(1) fun.convert-px(2) fun.convert-px(-1)
var(--color-shadow-light),
fun.convert-px(3) fun.convert-px(3) fun.convert-px(4) fun.convert-px(-4)
@@ -36,8 +36,3 @@
transform: scale(0.9);
}
}
-
-.icon {
- max-width: 100%;
- max-height: 100%;
-}
diff --git a/src/components/atoms/links/social-link.stories.tsx b/src/components/atoms/links/social-link/social-link.stories.tsx
index b627e9f..cfb4ac7 100644
--- a/src/components/atoms/links/social-link.stories.tsx
+++ b/src/components/atoms/links/social-link/social-link.stories.tsx
@@ -1,24 +1,13 @@
-import { ComponentMeta, ComponentStory } from '@storybook/react';
+import type { ComponentMeta, ComponentStory } from '@storybook/react';
import { SocialLink } from './social-link';
/**
* SocialLink - Storybook Meta
*/
export default {
- title: 'Atoms/Buttons/Social',
+ title: 'Atoms/Links/Social',
component: SocialLink,
argTypes: {
- name: {
- control: {
- type: 'select',
- },
- description: 'Social website name.',
- options: ['Github', 'Gitlab', 'LinkedIn', 'Twitter'],
- type: {
- name: 'string',
- required: true,
- },
- },
url: {
control: {
type: null,
@@ -41,7 +30,8 @@ const Template: ComponentStory<typeof SocialLink> = (args) => (
*/
export const Github = Template.bind({});
Github.args = {
- name: 'Github',
+ icon: 'Github',
+ label: 'Github profile',
url: '#',
};
@@ -50,7 +40,8 @@ Github.args = {
*/
export const Gitlab = Template.bind({});
Gitlab.args = {
- name: 'Gitlab',
+ icon: 'Gitlab',
+ label: 'Gitlab profile',
url: '#',
};
@@ -59,7 +50,8 @@ Gitlab.args = {
*/
export const LinkedIn = Template.bind({});
LinkedIn.args = {
- name: 'LinkedIn',
+ icon: 'LinkedIn',
+ label: 'LinkedIn profile',
url: '#',
};
@@ -68,6 +60,7 @@ LinkedIn.args = {
*/
export const Twitter = Template.bind({});
Twitter.args = {
- name: 'Twitter',
+ icon: 'Twitter',
+ label: 'Twitter profile',
url: '#',
};
diff --git a/src/components/atoms/links/social-link/social-link.test.tsx b/src/components/atoms/links/social-link/social-link.test.tsx
new file mode 100644
index 0000000..9129c27
--- /dev/null
+++ b/src/components/atoms/links/social-link/social-link.test.tsx
@@ -0,0 +1,62 @@
+import { describe, expect, it } from '@jest/globals';
+import { render, screen as rtlScreen } from '@testing-library/react';
+import { SocialLink } from './social-link';
+
+/**
+ * Next.js mock images to use next/image component. So for now, I need to mock
+ * the svg files manually.
+ */
+jest.mock('@assets/images/social-media/github.svg', () => 'svg-file');
+jest.mock('@assets/images/social-media/gitlab.svg', () => 'svg-file');
+jest.mock('@assets/images/social-media/linkedin.svg', () => 'svg-file');
+jest.mock('@assets/images/social-media/twitter.svg', () => 'svg-file');
+
+describe('SocialLink', () => {
+ it('render a Github social link', () => {
+ const label = 'sed ea impedit';
+ const target = '/voluptas';
+
+ render(<SocialLink icon="Github" label={label} url={target} />);
+
+ expect(rtlScreen.getByRole('link', { name: label })).toHaveAttribute(
+ 'href',
+ target
+ );
+ });
+
+ it('render a Gitlab social link', () => {
+ const label = 'rerum velit asperiores';
+ const target = '/enim';
+
+ render(<SocialLink icon="Gitlab" label={label} url={target} />);
+
+ expect(rtlScreen.getByRole('link', { name: label })).toHaveAttribute(
+ 'href',
+ target
+ );
+ });
+
+ it('render a LinkedIn social link', () => {
+ const label = 'in dolores sed';
+ const target = '/ut';
+
+ render(<SocialLink icon="LinkedIn" label={label} url={target} />);
+
+ expect(rtlScreen.getByRole('link', { name: label })).toHaveAttribute(
+ 'href',
+ target
+ );
+ });
+
+ it('render a Twitter social link', () => {
+ const label = 'voluptatibus temporibus expedita';
+ const target = '/magni';
+
+ render(<SocialLink icon="Twitter" label={label} url={target} />);
+
+ expect(rtlScreen.getByRole('link', { name: label })).toHaveAttribute(
+ 'href',
+ target
+ );
+ });
+});
diff --git a/src/components/atoms/links/social-link/social-link.tsx b/src/components/atoms/links/social-link/social-link.tsx
new file mode 100644
index 0000000..1da1e7d
--- /dev/null
+++ b/src/components/atoms/links/social-link/social-link.tsx
@@ -0,0 +1,65 @@
+import type { AnchorHTMLAttributes, FC } from 'react';
+import GithubIcon from '../../../../assets/images/social-media/github.svg';
+import GitlabIcon from '../../../../assets/images/social-media/gitlab.svg';
+import LinkedInIcon from '../../../../assets/images/social-media/linkedin.svg';
+import TwitterIcon from '../../../../assets/images/social-media/twitter.svg';
+import styles from './social-link.module.scss';
+
+export type SocialWebsite = 'Github' | 'Gitlab' | 'LinkedIn' | 'Twitter';
+
+export type SocialLinkProps = Omit<
+ AnchorHTMLAttributes<HTMLAnchorElement>,
+ 'aria-label' | 'children' | 'href'
+> & {
+ /**
+ * The social link icon.
+ */
+ icon: SocialWebsite;
+ /**
+ * An accessible label for the link.
+ */
+ label: string;
+ /**
+ * The social profile url.
+ */
+ url: string;
+};
+
+/**
+ * SocialLink component
+ *
+ * Render a social icon link.
+ */
+export const SocialLink: FC<SocialLinkProps> = ({
+ className = '',
+ icon,
+ label,
+ url,
+ ...props
+}) => {
+ const linkClass = `${styles.link} ${className}`;
+
+ /**
+ * Retrieve a social link icon by id.
+ * @param {string} id - The social website id.
+ */
+ const getIcon = (id: string) => {
+ switch (id) {
+ case 'Github':
+ return <GithubIcon aria-hidden className={styles.icon} />;
+ case 'Gitlab':
+ return <GitlabIcon aria-hidden className={styles.icon} />;
+ case 'LinkedIn':
+ return <LinkedInIcon aria-hidden className={styles.icon} />;
+ case 'Twitter':
+ default:
+ return <TwitterIcon aria-hidden className={styles.icon} />;
+ }
+ };
+
+ return (
+ <a {...props} aria-label={label} className={linkClass} href={url}>
+ {getIcon(icon)}
+ </a>
+ );
+};