diff options
| author | Armand Philippot <git@armandphilippot.com> | 2023-10-03 18:52:57 +0200 |
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2023-11-11 18:14:41 +0100 |
| commit | a3fb0aa94717aafae897ac293488c43a099c0b2b (patch) | |
| tree | bb5c9fcb093779061cd11e08d94f30bbb3a9b854 | |
| parent | f914ff8376dd91c4f6f8ca149e1cb6becb622d88 (diff) | |
refactor(components): rewrite SharingLink component
* replace default label with label prop
* simplify CSS rules
| -rw-r--r-- | src/components/atoms/links/sharing-link.module.scss | 157 | ||||
| -rw-r--r-- | src/components/atoms/links/sharing-link.test.tsx | 47 | ||||
| -rw-r--r-- | src/components/atoms/links/sharing-link.tsx | 47 | ||||
| -rw-r--r-- | src/components/atoms/links/sharing-link/index.ts | 1 | ||||
| -rw-r--r-- | src/components/atoms/links/sharing-link/sharing-link.module.scss | 88 | ||||
| -rw-r--r-- | src/components/atoms/links/sharing-link/sharing-link.stories.tsx (renamed from src/components/atoms/links/sharing-link.stories.tsx) | 20 | ||||
| -rw-r--r-- | src/components/atoms/links/sharing-link/sharing-link.test.tsx | 83 | ||||
| -rw-r--r-- | src/components/atoms/links/sharing-link/sharing-link.tsx | 51 | ||||
| -rw-r--r-- | src/components/organisms/widgets/sharing.test.tsx | 8 | ||||
| -rw-r--r-- | src/components/organisms/widgets/sharing.tsx | 176 | ||||
| -rw-r--r-- | src/i18n/en.json | 28 | ||||
| -rw-r--r-- | src/i18n/fr.json | 28 |
12 files changed, 407 insertions, 327 deletions
diff --git a/src/components/atoms/links/sharing-link.module.scss b/src/components/atoms/links/sharing-link.module.scss deleted file mode 100644 index 089fd13..0000000 --- a/src/components/atoms/links/sharing-link.module.scss +++ /dev/null @@ -1,157 +0,0 @@ -@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); - - &:hover, - &:focus { - transform: translateX(#{fun.convert-px(-3)}) - translateY(#{fun.convert-px(-3)}); - } - - &:active { - 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-repeat: no-repeat; - filter: drop-shadow( - #{fun.convert-px(1)} #{fun.convert-px(1)} #{fun.convert-px(1)} hsl(0, 0%, 0%) - ); - } - - &--diaspora { - background: hsl(0, 0%, 13%); - box-shadow: #{fun.convert-px(3)} #{fun.convert-px(3)} 0 0 hsl(0, 0%, 3%); - - &:hover, - &:focus { - box-shadow: #{fun.convert-px(6)} #{fun.convert-px(6)} 0 0 hsl(0, 0%, 3%); - } - - &:active { - box-shadow: #{fun.convert-px(1)} #{fun.convert-px(1)} 0 0 hsl(0, 0%, 3%); - } - - &::before { - background-image: url(fun.encode-svg( - '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path style="fill:#ffffff;" d="M15.257 21.928l-2.33-3.255c-.622-.87-1.128-1.549-1.155-1.55-.027 0-1.007 1.317-2.317 3.115-1.248 1.713-2.28 3.115-2.292 3.115-.035 0-4.5-3.145-4.51-3.178-.006-.016 1.003-1.497 2.242-3.292 1.239-1.794 2.252-3.29 2.252-3.325 0-.056-.401-.197-3.55-1.247a1604.93 1604.93 0 01-3.593-1.2c-.033-.013.153-.635.79-2.648.46-1.446.845-2.642.857-2.656.013-.015 1.71.528 3.772 1.207 2.062.678 3.766 1.233 3.787 1.233.021 0 .045-.032.053-.07.008-.039.026-1.794.04-3.902.013-2.107.036-3.848.05-3.87.02-.03.599-.038 2.725-.038 1.485 0 2.716.01 2.735.023.023.016.064 1.175.132 3.776.112 4.273.115 4.33.183 4.33.026 0 1.66-.547 3.631-1.216 1.97-.668 3.593-1.204 3.605-1.191.04.045 1.656 5.307 1.636 5.327-.011.01-1.656.574-3.655 1.252-2.75.932-3.638 1.244-3.645 1.284-.006.029.94 1.442 2.143 3.202 1.184 1.733 2.148 3.164 2.143 3.18-.012.036-4.442 3.299-4.48 3.299-.015 0-.577-.767-1.249-1.705z"/></svg>' - )); - } - } - - &--email { - background: hsl(0, 0%, 44%); - box-shadow: #{fun.convert-px(3)} #{fun.convert-px(3)} 0 0 hsl(0, 0%, 34%); - - &:hover, - &:focus { - box-shadow: #{fun.convert-px(6)} #{fun.convert-px(6)} 0 0 hsl(0, 0%, 34%); - } - - &:active { - box-shadow: #{fun.convert-px(1)} #{fun.convert-px(1)} 0 0 hsl(0, 0%, 34%); - } - - &::before { - background-image: url(fun.encode-svg( - '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill:#ffffff;" d="M15.909 12.123L24 17.238V6.792zM0 6.792v10.446l8.091-5.115zM22.5 3.75h-21c-.748 0-1.343.558-1.455 1.276L12 12.904l11.955-7.877c-.112-.718-.706-1.276-1.455-1.276zm-7.965 9.279l-2.123 1.398a.75.75 0 01-.825 0l-2.122-1.4-9.417 5.957c.116.712.707 1.266 1.452 1.266h21c.746 0 1.337-.553 1.452-1.266z"/></svg>' - )); - } - } - - &--facebook { - background: hsl(214, 89%, 52%); - box-shadow: #{fun.convert-px(3)} #{fun.convert-px(3)} 0 0 hsl(214, 89%, 42%); - - &:hover, - &:focus { - box-shadow: #{fun.convert-px(6)} #{fun.convert-px(6)} 0 0 - hsl(214, 89%, 42%); - } - - &:active { - box-shadow: #{fun.convert-px(1)} #{fun.convert-px(1)} 0 0 - hsl(214, 89%, 42%); - } - - &::before { - background-image: url(fun.encode-svg( - '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill:#ffffff;" d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>' - )); - } - } - - &--journal-du-hacker { - background: hsl(210, 24%, 51%); - box-shadow: #{fun.convert-px(3)} #{fun.convert-px(3)} 0 0 hsl(210, 24%, 41%); - - &:hover, - &:focus { - box-shadow: #{fun.convert-px(6)} #{fun.convert-px(6)} 0 0 - hsl(210, 24%, 41%); - } - - &:active { - box-shadow: #{fun.convert-px(1)} #{fun.convert-px(1)} 0 0 - hsl(210, 24%, 41%); - } - - &::before { - background-image: url(fun.encode-svg( - '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill:#ffffff;" d="M17.822 23.297a6.644 6.644 0 00-.654.032c-1.104.1-2.451-.378-3.244-1.15a3.223 3.223 0 01-.52-.739c-.209-.425-.22-.489-.211-1.178a8.174 8.174 0 01.19-1.585c.243-1.151.155-1.449-.514-1.737-.4-.172-.632-.135-1 .16-.268.215-.28.463-.07 1.532.298 1.526.286 2.238-.05 2.907-.28.56-.443.703-1.287 1.133-1.005.513-1.461.638-2.332.638-.73 0-1.014-.082-1.276-.366-.134-.145-.148-.2-.085-.32.099-.184.329-.3.959-.488.277-.082.604-.236.727-.341.123-.105.329-.265.457-.354.32-.222.562-.761.563-1.254 0-.331-.188-1.034-.45-1.676-.138-.338-.38.085-.38.666 0 .434-.673 1.569-.93 1.569-.048 0-.288.101-.532.225-.43.219-.47.225-1.31.225-.815 0-.889-.011-1.235-.194-.42-.22-.902-.694-1.094-1.073a2.752 2.752 0 00-.227-.377c-.083-.102-.08-.143.018-.293.206-.314.473-.317 1.186-.011.583.25 1.22.215 1.582-.086.168-.139.325-.697.342-1.217.02-.598-.049-.66-.596-.528-.86.206-1.762-.084-2.76-.887-.916-.739-1.362-.845-2.241-.538-.262.092-.51.153-.552.137-.042-.016-.134-.136-.204-.268-.118-.218-.12-.252-.02-.403.156-.24.714-.573 1.185-.708.297-.086.588-.11 1.076-.09.655.026.687.035 1.567.458.54.259.99.43 1.127.43.27 0 1.014-.37 1.159-.577.167-.238.124-.34-.322-.776-1.19-1.16-1.943-2.608-2.24-4.31-.124-.702-.14-1.888-.035-2.483.116-.656.677-2.273.915-2.64.385-.59 1.823-1.965 2.585-2.469C9.187.905 11.43.395 13.715.785c2.457.42 4.507 1.61 5.849 3.394 1.062 1.414 1.554 2.859 1.553 4.57 0 1.778-.497 3.238-1.599 4.693a6.207 6.207 0 00-.34.476c0 .013.205.12.456.238.737.345 1.169.844 1.726 1.994.256.527.531 1.031.613 1.12.225.247.614.42 1.099.49.588.085.804.178.9.388.109.24-.111.55-.402.563-.11.005-.394.033-.63.062-.887.107-1.851-.251-2.416-.898-.17-.193-.503-.616-.74-.939-.455-.616-.818-.922-1.054-.888-.117.017-.14.066-.127.28.008.142.068.34.133.438.09.137.127.412.161 1.196.05 1.153.147 1.458.55 1.726.306.204.552.198 1.11-.025.581-.233.923-.238 1.159-.018.243.227.2.637-.11 1.026-.33.419-1.338.899-2.001.954-1.194.1-2.371-.602-2.828-1.686-.062-.147-.197-.61-.301-1.03-.12-.486-.221-.762-.28-.762-.109 0-.263.401-.27.705-.003.12-.056.417-.118.657-.328 1.282.307 2.309 1.66 2.684.657.182.808.299.808.623 0 .319-.165.494-.454.481z"/></svg>' - )); - } - } - - &--linkedin { - background: hsl(210, 90%, 40%); - box-shadow: #{fun.convert-px(3)} #{fun.convert-px(3)} 0 0 hsl(210, 90%, 30%); - - &:hover, - &:focus { - box-shadow: #{fun.convert-px(6)} #{fun.convert-px(6)} 0 0 - hsl(210, 90%, 30%); - } - - &:active { - box-shadow: #{fun.convert-px(1)} #{fun.convert-px(1)} 0 0 - hsl(210, 90%, 30%); - } - - &::before { - background-image: url(fun.encode-svg( - '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path style="fill:#ffffff;" d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>' - )); - } - } - - &--twitter { - background: hsl(203, 89%, 53%); - box-shadow: #{fun.convert-px(3)} #{fun.convert-px(3)} 0 0 hsl(203, 89%, 43%); - - &:hover, - &:focus { - box-shadow: #{fun.convert-px(6)} #{fun.convert-px(6)} 0 0 - hsl(203, 89%, 43%); - } - - &:active { - box-shadow: #{fun.convert-px(1)} #{fun.convert-px(1)} 0 0 - hsl(203, 89%, 43%); - } - - &::before { - background-image: url(fun.encode-svg( - '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill:#ffffff;" d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg>' - )); - } - } -} diff --git a/src/components/atoms/links/sharing-link.test.tsx b/src/components/atoms/links/sharing-link.test.tsx deleted file mode 100644 index 87375a9..0000000 --- a/src/components/atoms/links/sharing-link.test.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { describe, expect, it } from '@jest/globals'; -import { render, screen } from '../../../../tests/utils'; -import { SharingLink } from './sharing-link'; - -describe('SharingLink', () => { - it('render a Diaspora sharing link', () => { - render(<SharingLink medium="diaspora" url="#" />); - expect(screen.getByRole('link', { name: 'Share on diaspora' })).toHaveClass( - 'link--diaspora' - ); - }); - - it('render an Email sharing link', () => { - render(<SharingLink medium="email" url="#" />); - expect(screen.getByRole('link', { name: 'Share on email' })).toHaveClass( - 'link--email' - ); - }); - - it('render a Facebook sharing link', () => { - render(<SharingLink medium="facebook" url="#" />); - expect(screen.getByRole('link', { name: 'Share on facebook' })).toHaveClass( - 'link--facebook' - ); - }); - - it('render a Journal du Hacker sharing link', () => { - render(<SharingLink medium="journal-du-hacker" url="#" />); - expect( - screen.getByRole('link', { name: 'Share on journal-du-hacker' }) - ).toHaveClass('link--journal-du-hacker'); - }); - - it('render a LinkedIn sharing link', () => { - render(<SharingLink medium="linkedin" url="#" />); - expect(screen.getByRole('link', { name: 'Share on linkedin' })).toHaveClass( - 'link--linkedin' - ); - }); - - it('render a Twitter sharing link', () => { - render(<SharingLink medium="twitter" url="#" />); - expect(screen.getByRole('link', { name: 'Share on twitter' })).toHaveClass( - 'link--twitter' - ); - }); -}); diff --git a/src/components/atoms/links/sharing-link.tsx b/src/components/atoms/links/sharing-link.tsx deleted file mode 100644 index 0b9d5fb..0000000 --- a/src/components/atoms/links/sharing-link.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { FC } from 'react'; -import { useIntl } from 'react-intl'; -import styles from './sharing-link.module.scss'; - -export type SharingMedium = - | 'diaspora' - | 'email' - | 'facebook' - | 'journal-du-hacker' - | 'linkedin' - | 'twitter'; - -export type SharingLinkProps = { - /** - * The sharing medium id. - */ - medium: SharingMedium; - /** - * The sharing url. - */ - url: string; -}; - -/** - * SharingLink component - * - * Render a sharing link. - */ -export const SharingLink: FC<SharingLinkProps> = ({ medium, url }) => { - const intl = useIntl(); - const text = intl.formatMessage( - { - defaultMessage: 'Share on {name}', - description: 'Sharing: share on social network text', - id: 'ureXFw', - }, - { name: medium } - ); - const mediumClass = `link--${medium}`; - const linkClass = `${styles.link} ${styles[mediumClass]}`; - - return ( - <a className={linkClass} href={url}> - <span className="screen-reader-text">{text}</span> - </a> - ); -}; 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('<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path style="fill:#ffffff;" d="M15.257 21.928l-2.33-3.255c-.622-.87-1.128-1.549-1.155-1.55-.027 0-1.007 1.317-2.317 3.115-1.248 1.713-2.28 3.115-2.292 3.115-.035 0-4.5-3.145-4.51-3.178-.006-.016 1.003-1.497 2.242-3.292 1.239-1.794 2.252-3.29 2.252-3.325 0-.056-.401-.197-3.55-1.247a1604.93 1604.93 0 01-3.593-1.2c-.033-.013.153-.635.79-2.648.46-1.446.845-2.642.857-2.656.013-.015 1.71.528 3.772 1.207 2.062.678 3.766 1.233 3.787 1.233.021 0 .045-.032.053-.07.008-.039.026-1.794.04-3.902.013-2.107.036-3.848.05-3.87.02-.03.599-.038 2.725-.038 1.485 0 2.716.01 2.735.023.023.016.064 1.175.132 3.776.112 4.273.115 4.33.183 4.33.026 0 1.66-.547 3.631-1.216 1.97-.668 3.593-1.204 3.605-1.191.04.045 1.656 5.307 1.636 5.327-.011.01-1.656.574-3.655 1.252-2.75.932-3.638 1.244-3.645 1.284-.006.029.94 1.442 2.143 3.202 1.184 1.733 2.148 3.164 2.143 3.18-.012.036-4.442 3.299-4.48 3.299-.015 0-.577-.767-1.249-1.705z"/></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('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill:#ffffff;" d="M15.909 12.123L24 17.238V6.792zM0 6.792v10.446l8.091-5.115zM22.5 3.75h-21c-.748 0-1.343.558-1.455 1.276L12 12.904l11.955-7.877c-.112-.718-.706-1.276-1.455-1.276zm-7.965 9.279l-2.123 1.398a.75.75 0 01-.825 0l-2.122-1.4-9.417 5.957c.116.712.707 1.266 1.452 1.266h21c.746 0 1.337-.553 1.452-1.266z"/></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('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill:#ffffff;" d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></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('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill:#ffffff;" d="M17.822 23.297a6.644 6.644 0 00-.654.032c-1.104.1-2.451-.378-3.244-1.15a3.223 3.223 0 01-.52-.739c-.209-.425-.22-.489-.211-1.178a8.174 8.174 0 01.19-1.585c.243-1.151.155-1.449-.514-1.737-.4-.172-.632-.135-1 .16-.268.215-.28.463-.07 1.532.298 1.526.286 2.238-.05 2.907-.28.56-.443.703-1.287 1.133-1.005.513-1.461.638-2.332.638-.73 0-1.014-.082-1.276-.366-.134-.145-.148-.2-.085-.32.099-.184.329-.3.959-.488.277-.082.604-.236.727-.341.123-.105.329-.265.457-.354.32-.222.562-.761.563-1.254 0-.331-.188-1.034-.45-1.676-.138-.338-.38.085-.38.666 0 .434-.673 1.569-.93 1.569-.048 0-.288.101-.532.225-.43.219-.47.225-1.31.225-.815 0-.889-.011-1.235-.194-.42-.22-.902-.694-1.094-1.073a2.752 2.752 0 00-.227-.377c-.083-.102-.08-.143.018-.293.206-.314.473-.317 1.186-.011.583.25 1.22.215 1.582-.086.168-.139.325-.697.342-1.217.02-.598-.049-.66-.596-.528-.86.206-1.762-.084-2.76-.887-.916-.739-1.362-.845-2.241-.538-.262.092-.51.153-.552.137-.042-.016-.134-.136-.204-.268-.118-.218-.12-.252-.02-.403.156-.24.714-.573 1.185-.708.297-.086.588-.11 1.076-.09.655.026.687.035 1.567.458.54.259.99.43 1.127.43.27 0 1.014-.37 1.159-.577.167-.238.124-.34-.322-.776-1.19-1.16-1.943-2.608-2.24-4.31-.124-.702-.14-1.888-.035-2.483.116-.656.677-2.273.915-2.64.385-.59 1.823-1.965 2.585-2.469C9.187.905 11.43.395 13.715.785c2.457.42 4.507 1.61 5.849 3.394 1.062 1.414 1.554 2.859 1.553 4.57 0 1.778-.497 3.238-1.599 4.693a6.207 6.207 0 00-.34.476c0 .013.205.12.456.238.737.345 1.169.844 1.726 1.994.256.527.531 1.031.613 1.12.225.247.614.42 1.099.49.588.085.804.178.9.388.109.24-.111.55-.402.563-.11.005-.394.033-.63.062-.887.107-1.851-.251-2.416-.898-.17-.193-.503-.616-.74-.939-.455-.616-.818-.922-1.054-.888-.117.017-.14.066-.127.28.008.142.068.34.133.438.09.137.127.412.161 1.196.05 1.153.147 1.458.55 1.726.306.204.552.198 1.11-.025.581-.233.923-.238 1.159-.018.243.227.2.637-.11 1.026-.33.419-1.338.899-2.001.954-1.194.1-2.371-.602-2.828-1.686-.062-.147-.197-.61-.301-1.03-.12-.486-.221-.762-.28-.762-.109 0-.263.401-.27.705-.003.12-.056.417-.118.657-.328 1.282.307 2.309 1.66 2.684.657.182.808.299.808.623 0 .319-.165.494-.454.481z"/></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('<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path style="fill:#ffffff;" d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></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('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path style="fill:#ffffff;" d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg>')}'); + --shadowColor: hsl(203, 89%, 43%); + + background: hsl(203, 89%, 53%); + } +} diff --git a/src/components/atoms/links/sharing-link.stories.tsx b/src/components/atoms/links/sharing-link/sharing-link.stories.tsx index 7ab5caf..932d468 100644 --- a/src/components/atoms/links/sharing-link.stories.tsx +++ b/src/components/atoms/links/sharing-link/sharing-link.stories.tsx @@ -1,11 +1,11 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; import { SharingLink } from './sharing-link'; /** * SharingLink - Storybook Meta */ export default { - title: 'Atoms/Buttons/Sharing', + title: 'Atoms/Links/Sharing', component: SharingLink, argTypes: { medium: { @@ -26,6 +26,16 @@ export default { required: true, }, }, + label: { + control: { + type: 'text', + }, + description: 'An accessible label that describe the link..', + type: { + name: 'string', + required: true, + }, + }, url: { control: { type: 'text', @@ -48,6 +58,7 @@ const Template: ComponentStory<typeof SharingLink> = (args) => ( */ export const Diaspora = Template.bind({}); Diaspora.args = { + label: 'Share on Diaspora', medium: 'diaspora', url: '#', }; @@ -57,6 +68,7 @@ Diaspora.args = { */ export const Email = Template.bind({}); Email.args = { + label: 'Share by Email', medium: 'email', url: '#', }; @@ -66,6 +78,7 @@ Email.args = { */ export const Facebook = Template.bind({}); Facebook.args = { + label: 'Share on Facebook', medium: 'facebook', url: '#', }; @@ -75,6 +88,7 @@ Facebook.args = { */ export const JournalDuHacker = Template.bind({}); JournalDuHacker.args = { + label: 'Share on Journal du Hacker', medium: 'journal-du-hacker', url: '#', }; @@ -84,6 +98,7 @@ JournalDuHacker.args = { */ export const LinkedIn = Template.bind({}); LinkedIn.args = { + label: 'Share on LinkedIn', medium: 'linkedin', url: '#', }; @@ -93,6 +108,7 @@ LinkedIn.args = { */ 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(<SharingLink label={label} medium={medium} url={target} />); + + 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(<SharingLink label={label} medium={medium} url={target} />); + + 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(<SharingLink label={label} medium={medium} url={target} />); + + 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(<SharingLink label={label} medium={medium} url={target} />); + + 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(<SharingLink label={label} medium={medium} url={target} />); + + 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(<SharingLink label={label} medium={medium} url={target} />); + + 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<HTMLElement>, + '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<SharingLinkProps> = ({ + className = '', + label, + medium, + url, + ...props +}) => { + const mediumClass = `link--${medium}`; + const linkClass = `${styles.link} ${styles[mediumClass]} ${className}`; + + return ( + <a {...props} className={linkClass} href={url}> + {/* eslint-disable-next-line -- SR class allowed */} + <span className="screen-reader-text">{label}</span> + </a> + ); +}; diff --git a/src/components/organisms/widgets/sharing.test.tsx b/src/components/organisms/widgets/sharing.test.tsx index 40d2a2c..c7211f0 100644 --- a/src/components/organisms/widgets/sharing.test.tsx +++ b/src/components/organisms/widgets/sharing.test.tsx @@ -1,5 +1,5 @@ import { describe, expect, it } from '@jest/globals'; -import { render, screen } from '../../../../tests/utils'; +import { render, screen as rtlScreen } from '../../../../tests/utils'; import { Sharing, type SharingData } from './sharing'; const postData: SharingData = { @@ -12,13 +12,13 @@ describe('Sharing', () => { it('renders a sharing widget', () => { render(<Sharing data={postData} media={['facebook', 'twitter']} />); expect( - screen.getByRole('link', { name: 'Share on facebook' }) + rtlScreen.getByRole('link', { name: 'Share on Facebook' }) ).toBeInTheDocument(); expect( - screen.getByRole('link', { name: 'Share on twitter' }) + rtlScreen.getByRole('link', { name: 'Share on Twitter' }) ).toBeInTheDocument(); expect( - screen.queryByRole('link', { name: 'Share on linkedin' }) + rtlScreen.queryByRole('link', { name: 'Share on LinkedIn' }) ).not.toBeInTheDocument(); }); }); diff --git a/src/components/organisms/widgets/sharing.tsx b/src/components/organisms/widgets/sharing.tsx index 61d54d8..eeffb71 100644 --- a/src/components/organisms/widgets/sharing.tsx +++ b/src/components/organisms/widgets/sharing.tsx @@ -1,9 +1,70 @@ -import { FC } from 'react'; +import type { FC } from 'react'; import { useIntl } from 'react-intl'; import { SharingLink, type SharingMedium } from '../../atoms'; import { Widget, type WidgetProps } from '../../molecules'; import styles from './sharing.module.scss'; +/** + * Build the Diaspora sharing url with provided data. + * + * @param {string} title - The content title. + * @param {string} url - The content url. + * @returns {string} The Diaspora url. + */ +const buildDiasporaUrl = (title: string, url: string): string => { + const titleParam = `title=${encodeURI(title)}`; + const urlParam = `url=${encodeURI(url)}`; + return `https://share.diasporafoundation.org/?${titleParam}&${urlParam}`; +}; + +/** + * Build the Facebook sharing url with provided data. + * + * @param {string} url - The content url. + * @returns {string} The Facebook url. + */ +const buildFacebookUrl = (url: string): string => { + const urlParam = `u=${encodeURI(url)}`; + return `https://www.facebook.com/sharer/sharer.php?${urlParam}`; +}; + +/** + * Build the Journal du Hacker sharing url with provided data. + * + * @param {string} title - The content title. + * @param {string} url - The content url. + * @returns {string} The Journal du Hacker url. + */ +const buildJdHUrl = (title: string, url: string): string => { + const titleParam = `title=${encodeURI(title)}`; + const urlParam = `url=${encodeURI(url)}`; + return `https://www.journalduhacker.net/stories/new?${titleParam}&${urlParam}`; +}; + +/** + * Build the LinkedIn sharing url with provided data. + * + * @param {string} url - The content url. + * @returns {string} The LinkedIn url. + */ +const buildLinkedInUrl = (url: string): string => { + const urlParam = `url=${encodeURI(url)}`; + return `https://www.linkedin.com/sharing/share-offsite/?${urlParam}`; +}; + +/** + * Build the Twitter sharing url with provided data. + * + * @param {string} title - The content title. + * @param {string} url - The content url. + * @returns {string} The Twitter url. + */ +const buildTwitterUrl = (title: string, url: string): string => { + const titleParam = `text=${encodeURI(title)}`; + const urlParam = `url=${encodeURI(url)}`; + return `https://twitter.com/intent/tweet?${titleParam}&${urlParam}`; +}; + export type SharingData = { /** * The content excerpt. @@ -55,6 +116,7 @@ export const Sharing: FC<SharingProps> = ({ level = 2, ...props }) => { + const listClass = `${styles.list} ${className}`; const intl = useIntl(); const widgetTitle = intl.formatMessage({ defaultMessage: 'Share', @@ -63,19 +125,6 @@ export const Sharing: FC<SharingProps> = ({ }); /** - * Build the Diaspora sharing url with provided data. - * - * @param {string} title - The content title. - * @param {string} url - The content url. - * @returns {string} The Diaspora url. - */ - const buildDiasporaUrl = (title: string, url: string): string => { - const titleParam = `title=${encodeURI(title)}`; - const urlParam = `url=${encodeURI(url)}`; - return `https://share.diasporafoundation.org/?${titleParam}&${urlParam}`; - }; - - /** * Build the mailto url from provided data. * * @param {string} excerpt - The content excerpt. @@ -115,51 +164,51 @@ export const Sharing: FC<SharingProps> = ({ }; /** - * Build the Facebook sharing url with provided data. + * Retrieve the sharing label by medium id. * - * @param {string} url - The content url. - * @returns {string} The Facebook url. - */ - const buildFacebookUrl = (url: string): string => { - const urlParam = `u=${encodeURI(url)}`; - return `https://www.facebook.com/sharer/sharer.php?${urlParam}`; - }; - - /** - * Build the Journal du Hacker sharing url with provided data. - * - * @param {string} title - The content title. - * @param {string} url - The content url. - * @returns {string} The Journal du Hacker url. - */ - const buildJdHUrl = (title: string, url: string): string => { - const titleParam = `title=${encodeURI(title)}`; - const urlParam = `url=${encodeURI(url)}`; - return `https://www.journalduhacker.net/stories/new?${titleParam}&${urlParam}`; - }; - - /** - * Build the LinkedIn sharing url with provided data. - * - * @param {string} url - The content url. - * @returns {string} The LinkedIn url. - */ - const buildLinkedInUrl = (url: string): string => { - const urlParam = `url=${encodeURI(url)}`; - return `https://www.linkedin.com/sharing/share-offsite/?${urlParam}`; - }; - - /** - * Build the Twitter sharing url with provided data. - * - * @param {string} title - The content title. - * @param {string} url - The content url. - * @returns {string} The Twitter url. + * @param {SharingMedium} medium - A sharing medium id. + * @returns {string} The sharing label. */ - const buildTwitterUrl = (title: string, url: string): string => { - const titleParam = `text=${encodeURI(title)}`; - const urlParam = `url=${encodeURI(url)}`; - return `https://twitter.com/intent/tweet?${titleParam}&${urlParam}`; + const getLabel = (medium: SharingMedium): string => { + switch (medium) { + case 'diaspora': + return intl.formatMessage({ + defaultMessage: 'Share on Diaspora', + description: 'Sharing: Diaspora sharing link', + id: 'oVLRW8', + }); + case 'email': + return intl.formatMessage({ + defaultMessage: 'Share by Email', + description: 'Sharing: Email sharing link', + id: '2ukj9H', + }); + case 'facebook': + return intl.formatMessage({ + defaultMessage: 'Share on Facebook', + description: 'Sharing: Facebook sharing link', + id: 'o0DAK4', + }); + case 'journal-du-hacker': + return intl.formatMessage({ + defaultMessage: 'Share on Journal du Hacker', + description: 'Sharing: Journal du Hacker sharing link', + id: 'vnbryZ', + }); + case 'linkedin': + return intl.formatMessage({ + defaultMessage: 'Share on LinkedIn', + description: 'Sharing: LinkedIn sharing link', + id: 'Y+DYja', + }); + case 'twitter': + default: + return intl.formatMessage({ + defaultMessage: 'Share on Twitter', + description: 'Sharing: Twitter sharing link', + id: 'NI5gXc', + }); + } }; /** @@ -194,17 +243,20 @@ export const Sharing: FC<SharingProps> = ({ * * @returns {JSX.Element[]} The sharing links wrapped with li element. */ - const getItems = (): JSX.Element[] => { - return media.map((medium) => ( + const getItems = (): JSX.Element[] => + media.map((medium) => ( <li key={medium}> - <SharingLink medium={medium} url={getUrl(medium)} /> + <SharingLink + label={getLabel(medium)} + medium={medium} + url={getUrl(medium)} + /> </li> )); - }; return ( <Widget {...props} expanded={expanded} level={level} title={widgetTitle}> - <ul className={`${styles.list} ${className}`}>{getItems()}</ul> + <ul className={listClass}>{getItems()}</ul> </Widget> ); }; diff --git a/src/i18n/en.json b/src/i18n/en.json index 2faedfa..640fbdc 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -67,6 +67,10 @@ "defaultMessage": "Leave a reply", "description": "Comment: comment form title" }, + "2ukj9H": { + "defaultMessage": "Share by Email", + "description": "Sharing: Email sharing link" + }, "310o3F": { "defaultMessage": "Error 404: Page not found - {websiteName}", "description": "404Page: SEO - Page title" @@ -267,6 +271,10 @@ "defaultMessage": "Topics", "description": "SearchPage: topics list widget title" }, + "NI5gXc": { + "defaultMessage": "Share on Twitter", + "description": "Sharing: Twitter sharing link" + }, "OF5cPz": { "defaultMessage": "{postsCount, plural, =0 {No articles} one {# article} other {# articles}}", "description": "BlogPage: posts count meta" @@ -371,6 +379,10 @@ "defaultMessage": "Open search", "description": "Search: Open label" }, + "Y+DYja": { + "defaultMessage": "Share on LinkedIn", + "description": "Sharing: LinkedIn sharing link" + }, "Ygea7s": { "defaultMessage": "Light theme", "description": "ThemeToggle: light theme label" @@ -523,6 +535,14 @@ "defaultMessage": "Legal notice", "description": "Layout: Legal notice label" }, + "o0DAK4": { + "defaultMessage": "Share on Facebook", + "description": "Sharing: Facebook sharing link" + }, + "oVLRW8": { + "defaultMessage": "Share on Diaspora", + "description": "Sharing: Diaspora sharing link" + }, "og/zWL": { "defaultMessage": "Dark theme", "description": "PrismThemeToggle: dark theme label" @@ -599,10 +619,6 @@ "defaultMessage": "Load more articles?", "description": "PostsList: load more button" }, - "ureXFw": { - "defaultMessage": "Share on {name}", - "description": "Sharing: share on social network text" - }, "va65iw": { "defaultMessage": "On", "description": "MotionToggle: activate reduce motion label" @@ -611,6 +627,10 @@ "defaultMessage": "Web development", "description": "HomePage: link to web development thematic" }, + "vnbryZ": { + "defaultMessage": "Share on Journal du Hacker", + "description": "Sharing: Journal du Hacker sharing link" + }, "w4B5PA": { "defaultMessage": "Email:", "description": "ContactForm: email label" diff --git a/src/i18n/fr.json b/src/i18n/fr.json index 0f79416..ebdc3ad 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -67,6 +67,10 @@ "defaultMessage": "Laisser une réponse", "description": "Comment: comment form title" }, + "2ukj9H": { + "defaultMessage": "Partager par email", + "description": "Sharing: Email sharing link" + }, "310o3F": { "defaultMessage": "Erreur 404 : Page non trouvée - {websiteName}", "description": "404Page: SEO - Page title" @@ -267,6 +271,10 @@ "defaultMessage": "Sujets", "description": "SearchPage: topics list widget title" }, + "NI5gXc": { + "defaultMessage": "Partager sur Twitter", + "description": "Sharing: Twitter sharing link" + }, "OF5cPz": { "defaultMessage": "{postsCount, plural, =0 {0 article} one {# article} other {# articles}}", "description": "BlogPage: posts count meta" @@ -371,6 +379,10 @@ "defaultMessage": "Ouvrir la recherche", "description": "Search: Open label" }, + "Y+DYja": { + "defaultMessage": "Partager sur LinkedIn", + "description": "Sharing: LinkedIn sharing link" + }, "Ygea7s": { "defaultMessage": "Thème clair", "description": "ThemeToggle: light theme label" @@ -523,6 +535,14 @@ "defaultMessage": "Mentions légales", "description": "Layout: Legal notice label" }, + "o0DAK4": { + "defaultMessage": "Partager sur Facebook", + "description": "Sharing: Facebook sharing link" + }, + "oVLRW8": { + "defaultMessage": "Partager sur Diaspora", + "description": "Sharing: Diaspora sharing link" + }, "og/zWL": { "defaultMessage": "Thème sombre", "description": "PrismThemeToggle: dark theme label" @@ -599,10 +619,6 @@ "defaultMessage": "Charger plus d’articles ?", "description": "PostsList: load more button" }, - "ureXFw": { - "defaultMessage": "Partager via {name}", - "description": "Sharing: share on social network text" - }, "va65iw": { "defaultMessage": "Marche", "description": "MotionToggle: activate reduce motion label" @@ -611,6 +627,10 @@ "defaultMessage": "Développement web", "description": "HomePage: link to web development thematic" }, + "vnbryZ": { + "defaultMessage": "Partager sur le Journal du Hacker", + "description": "Sharing: Journal du Hacker sharing link" + }, "w4B5PA": { "defaultMessage": "E-mail :", "description": "ContactForm: email label" |
