From a3fb0aa94717aafae897ac293488c43a099c0b2b Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Tue, 3 Oct 2023 18:52:57 +0200 Subject: refactor(components): rewrite SharingLink component * replace default label with label prop * simplify CSS rules --- src/components/atoms/links/sharing-link/index.ts | 1 + .../links/sharing-link/sharing-link.module.scss | 88 ++++++++++++++++ .../links/sharing-link/sharing-link.stories.tsx | 114 +++++++++++++++++++++ .../atoms/links/sharing-link/sharing-link.test.tsx | 83 +++++++++++++++ .../atoms/links/sharing-link/sharing-link.tsx | 51 +++++++++ 5 files changed, 337 insertions(+) create mode 100644 src/components/atoms/links/sharing-link/index.ts create mode 100644 src/components/atoms/links/sharing-link/sharing-link.module.scss create mode 100644 src/components/atoms/links/sharing-link/sharing-link.stories.tsx create mode 100644 src/components/atoms/links/sharing-link/sharing-link.test.tsx create mode 100644 src/components/atoms/links/sharing-link/sharing-link.tsx (limited to 'src/components/atoms/links/sharing-link') diff --git a/src/components/atoms/links/sharing-link/index.ts b/src/components/atoms/links/sharing-link/index.ts new file mode 100644 index 0000000..20201eb --- /dev/null +++ b/src/components/atoms/links/sharing-link/index.ts @@ -0,0 +1 @@ +export * from './sharing-link'; diff --git a/src/components/atoms/links/sharing-link/sharing-link.module.scss b/src/components/atoms/links/sharing-link/sharing-link.module.scss new file mode 100644 index 0000000..e1c9c3c --- /dev/null +++ b/src/components/atoms/links/sharing-link/sharing-link.module.scss @@ -0,0 +1,88 @@ +@use "../../../../styles/abstracts/functions" as fun; + +.link { + display: inline-flex; + align-items: center; + padding: var(--spacing-2xs) var(--spacing-xs); + border-radius: fun.convert-px(3); + box-shadow: #{fun.convert-px(3)} #{fun.convert-px(3)} 0 0 var(--shadowColor); + transition: all 0.3s linear 0s; + + &:hover, + &:focus { + box-shadow: #{fun.convert-px(6)} #{fun.convert-px(6)} 0 0 var(--shadowColor); + transform: translateX(#{fun.convert-px(-3)}) + translateY(#{fun.convert-px(-3)}); + } + + &:active { + box-shadow: #{fun.convert-px(1)} #{fun.convert-px(1)} 0 0 var(--shadowColor); + transform: translateX(#{fun.convert-px(2)}) translateY(#{fun.convert-px(2)}); + } + + &::before { + content: ""; + display: block; + width: fun.convert-px(30); + height: fun.convert-px(30); + background-image: var(--logo); + background-repeat: no-repeat; + filter: drop-shadow( + #{fun.convert-px(1)} #{fun.convert-px(1)} #{fun.convert-px(1)} hsl(0, 0%, 0%) + ); + } + + &--diaspora { + // Prettier is removing spacing between attributes. + // prettier-ignore + --logo: url('#{fun.encode-svg('')}'); + --shadowColor: hsl(0, 0%, 3%); + + background: hsl(0, 0%, 13%); + } + + &--email { + // Prettier is removing spacing between attributes. + // prettier-ignore + --logo: url('#{fun.encode-svg('')}'); + --shadowColor: hsl(0, 0%, 34%); + + background: hsl(0, 0%, 44%); + } + + &--facebook { + // Prettier is removing spacing between attributes. + // prettier-ignore + --logo: url('#{fun.encode-svg('')}'); + --shadowColor: hsl(214, 89%, 42%); + + background: hsl(214, 89%, 52%); + } + + &--journal-du-hacker { + // Prettier is removing spacing between attributes. + // prettier-ignore + --logo: url('#{fun.encode-svg('')}'); + --shadowColor: hsl(210, 24%, 41%); + + background: hsl(210, 24%, 51%); + } + + &--linkedin { + // Prettier is removing spacing between attributes. + // prettier-ignore + --logo: url('#{fun.encode-svg('')}'); + --shadowColor: hsl(210, 90%, 30%); + + background: hsl(210, 90%, 40%); + } + + &--twitter { + // Prettier is removing spacing between attributes. + // prettier-ignore + --logo: url('#{fun.encode-svg('')}'); + --shadowColor: hsl(203, 89%, 43%); + + background: hsl(203, 89%, 53%); + } +} diff --git a/src/components/atoms/links/sharing-link/sharing-link.stories.tsx b/src/components/atoms/links/sharing-link/sharing-link.stories.tsx new file mode 100644 index 0000000..932d468 --- /dev/null +++ b/src/components/atoms/links/sharing-link/sharing-link.stories.tsx @@ -0,0 +1,114 @@ +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import { SharingLink } from './sharing-link'; + +/** + * SharingLink - Storybook Meta + */ +export default { + title: 'Atoms/Links/Sharing', + component: SharingLink, + argTypes: { + medium: { + control: { + type: 'select', + }, + description: 'The sharing medium.', + options: [ + 'diaspora', + 'email', + 'facebook', + 'journal-du-hacker', + 'linkedin', + 'twitter', + ], + type: { + name: 'string', + required: true, + }, + }, + label: { + control: { + type: 'text', + }, + description: 'An accessible label that describe the link..', + type: { + name: 'string', + required: true, + }, + }, + url: { + control: { + type: 'text', + }, + description: 'The sharing url.', + type: { + name: 'string', + required: true, + }, + }, + }, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ( + +); + +/** + * Sharing Link Stories - Diaspora + */ +export const Diaspora = Template.bind({}); +Diaspora.args = { + label: 'Share on Diaspora', + medium: 'diaspora', + url: '#', +}; + +/** + * Sharing Link Stories - Email + */ +export const Email = Template.bind({}); +Email.args = { + label: 'Share by Email', + medium: 'email', + url: '#', +}; + +/** + * Sharing Link Stories - Facebook + */ +export const Facebook = Template.bind({}); +Facebook.args = { + label: 'Share on Facebook', + medium: 'facebook', + url: '#', +}; + +/** + * Sharing Link Stories - Journal du Hacker + */ +export const JournalDuHacker = Template.bind({}); +JournalDuHacker.args = { + label: 'Share on Journal du Hacker', + medium: 'journal-du-hacker', + url: '#', +}; + +/** + * Sharing Link Stories - LinkedIn + */ +export const LinkedIn = Template.bind({}); +LinkedIn.args = { + label: 'Share on LinkedIn', + medium: 'linkedin', + url: '#', +}; + +/** + * Sharing Link Stories - Twitter + */ +export const Twitter = Template.bind({}); +Twitter.args = { + label: 'Share on Twitter', + medium: 'twitter', + url: '#', +}; diff --git a/src/components/atoms/links/sharing-link/sharing-link.test.tsx b/src/components/atoms/links/sharing-link/sharing-link.test.tsx new file mode 100644 index 0000000..06cfb22 --- /dev/null +++ b/src/components/atoms/links/sharing-link/sharing-link.test.tsx @@ -0,0 +1,83 @@ +import { describe, expect, it } from '@jest/globals'; +import { render, screen as rtlScreen } from '@testing-library/react'; +import { SharingLink, type SharingMedium } from './sharing-link'; + +describe('SharingLink', () => { + it('render a Diaspora sharing link', () => { + const label = 'ab'; + const medium: SharingMedium = 'diaspora'; + const target = '/totam'; + + render(); + + const link = rtlScreen.getByRole('link', { name: label }); + + expect(link).toHaveAttribute('href', target); + expect(link).toHaveClass('link--diaspora'); + }); + + it('render an Email sharing link', () => { + const label = 'ut'; + const medium: SharingMedium = 'email'; + const target = '/nostrum'; + + render(); + + const link = rtlScreen.getByRole('link', { name: label }); + + expect(link).toHaveAttribute('href', target); + expect(link).toHaveClass('link--email'); + }); + + it('render a Facebook sharing link', () => { + const label = 'autem'; + const medium: SharingMedium = 'facebook'; + const target = '/perspiciatis'; + + render(); + + const link = rtlScreen.getByRole('link', { name: label }); + + expect(link).toHaveAttribute('href', target); + expect(link).toHaveClass('link--facebook'); + }); + + it('render a Journal du Hacker sharing link', () => { + const label = 'in'; + const medium: SharingMedium = 'journal-du-hacker'; + const target = '/labore'; + + render(); + + const link = rtlScreen.getByRole('link', { name: label }); + + expect(link).toHaveAttribute('href', target); + expect(link).toHaveClass('link--journal-du-hacker'); + }); + + it('render a LinkedIn sharing link', () => { + const label = 'id'; + const medium: SharingMedium = 'linkedin'; + const target = '/nesciunt'; + + render(); + + const link = rtlScreen.getByRole('link', { name: label }); + + expect(link).toHaveAttribute('href', target); + expect(link).toHaveClass('link--linkedin'); + }); + + it('render a Twitter sharing link', () => { + const label = 'illum'; + const medium: SharingMedium = 'twitter'; + const target = '/consectetur'; + + render(); + + const link = rtlScreen.getByRole('link', { name: label }); + + expect(link).toHaveAttribute('href', target); + expect(link).toHaveClass('link--twitter'); + }); +}); diff --git a/src/components/atoms/links/sharing-link/sharing-link.tsx b/src/components/atoms/links/sharing-link/sharing-link.tsx new file mode 100644 index 0000000..186000e --- /dev/null +++ b/src/components/atoms/links/sharing-link/sharing-link.tsx @@ -0,0 +1,51 @@ +import type { AnchorHTMLAttributes, FC } from 'react'; +import styles from './sharing-link.module.scss'; + +export type SharingMedium = + | 'diaspora' + | 'email' + | 'facebook' + | 'journal-du-hacker' + | 'linkedin' + | 'twitter'; + +export type SharingLinkProps = Omit< + AnchorHTMLAttributes, + 'children' | 'href' +> & { + /** + * An accessible label (visually hidden). + */ + label: string; + /** + * The sharing medium id. + */ + medium: SharingMedium; + /** + * The sharing url. + */ + url: string; +}; + +/** + * SharingLink component + * + * Render a sharing link. + */ +export const SharingLink: FC = ({ + className = '', + label, + medium, + url, + ...props +}) => { + const mediumClass = `link--${medium}`; + const linkClass = `${styles.link} ${styles[mediumClass]} ${className}`; + + return ( + + {/* eslint-disable-next-line -- SR class allowed */} + {label} + + ); +}; -- cgit v1.2.3