diff options
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/atoms/links/social-link.test.tsx | 19 | ||||
| -rw-r--r-- | src/components/atoms/links/social-link.tsx | 51 | ||||
| -rw-r--r-- | src/components/atoms/links/social-link/index.ts | 1 | ||||
| -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.tsx | 62 | ||||
| -rw-r--r-- | src/components/atoms/links/social-link/social-link.tsx | 65 | ||||
| -rw-r--r-- | src/components/organisms/widgets/social-media.stories.tsx | 8 | ||||
| -rw-r--r-- | src/components/organisms/widgets/social-media.test.tsx | 12 | ||||
| -rw-r--r-- | src/components/organisms/widgets/social-media.tsx | 10 |
10 files changed, 159 insertions, 111 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> + ); +}; diff --git a/src/components/organisms/widgets/social-media.stories.tsx b/src/components/organisms/widgets/social-media.stories.tsx index f012554..6da3f3a 100644 --- a/src/components/organisms/widgets/social-media.stories.tsx +++ b/src/components/organisms/widgets/social-media.stories.tsx @@ -1,5 +1,5 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { SocialMedia as SocialMediaWidget, Media } from './social-media'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import { SocialMedia as SocialMediaWidget, type Media } from './social-media'; /** * SocialMedia - Storybook Meta @@ -46,8 +46,8 @@ const Template: ComponentStory<typeof SocialMediaWidget> = (args) => ( ); const media: Media[] = [ - { name: 'Github', url: '#' }, - { name: 'LinkedIn', url: '#' }, + { icon: 'Github', id: 'github', label: 'Github', url: '#' }, + { icon: 'LinkedIn', id: 'gitlab', label: 'Gitlab', url: '#' }, ]; /** diff --git a/src/components/organisms/widgets/social-media.test.tsx b/src/components/organisms/widgets/social-media.test.tsx index c0786ad..2cd3afb 100644 --- a/src/components/organisms/widgets/social-media.test.tsx +++ b/src/components/organisms/widgets/social-media.test.tsx @@ -1,10 +1,10 @@ import { describe, expect, it } from '@jest/globals'; -import { render, screen } from '../../../../tests/utils'; -import { SocialMedia, Media } from './social-media'; +import { render, screen as rtlScreen } from '../../../../tests/utils'; +import { SocialMedia, type Media } from './social-media'; const media: Media[] = [ - { name: 'Github', url: '#' }, - { name: 'LinkedIn', url: '#' }, + { icon: 'Github', id: 'github', label: 'Github', url: '#' }, + { icon: 'LinkedIn', id: 'gitlab', label: 'Gitlab', url: '#' }, ]; const title = 'Dolores ut ut'; const titleLevel = 2; @@ -22,7 +22,7 @@ describe('SocialMedia', () => { it('renders the widget title', () => { render(<SocialMedia media={media} title={title} level={titleLevel} />); expect( - screen.getByRole('heading', { + rtlScreen.getByRole('heading', { level: titleLevel, name: new RegExp(title, 'i'), }) @@ -31,6 +31,6 @@ describe('SocialMedia', () => { it('renders the correct number of items', () => { render(<SocialMedia media={media} title={title} level={titleLevel} />); - expect(screen.getAllByRole('listitem')).toHaveLength(media.length); + expect(rtlScreen.getAllByRole('listitem')).toHaveLength(media.length); }); }); diff --git a/src/components/organisms/widgets/social-media.tsx b/src/components/organisms/widgets/social-media.tsx index f9dea58..ddeb09c 100644 --- a/src/components/organisms/widgets/social-media.tsx +++ b/src/components/organisms/widgets/social-media.tsx @@ -3,7 +3,9 @@ import { List, ListItem, SocialLink, type SocialLinkProps } from '../../atoms'; import { Widget, type WidgetProps } from '../../molecules'; import styles from './social-media.module.scss'; -export type Media = SocialLinkProps; +export type Media = Required< + Pick<SocialLinkProps, 'icon' | 'id' | 'label' | 'url'> +>; export type SocialMediaProps = Pick<WidgetProps, 'level' | 'title'> & { media: Media[]; @@ -22,9 +24,9 @@ export const SocialMedia: FC<SocialMediaProps> = ({ media, ...props }) => { * @returns {JSX.Element[]} The social links. */ const getItems = (links: Media[]): JSX.Element[] => - links.map((link, index) => ( - <ListItem key={`media-${index}`}> - <SocialLink name={link.name} url={link.url} /> + links.map(({ id, ...link }) => ( + <ListItem key={id}> + <SocialLink {...link} /> </ListItem> )); |
