From f914ff8376dd91c4f6f8ca149e1cb6becb622d88 Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Mon, 2 Oct 2023 18:45:30 +0200 Subject: refactor(components): rewrite Link component * rename `external` prop to `isExternal` * rename `download` prop to `isDownload` * rewrite CSS to reduce code length and complexity * move link styles in Sass placeholders to avoid repeats because of WordPress articles * move NavLink component to molecules --- src/components/molecules/nav/index.ts | 1 + src/components/molecules/nav/nav-link/index.ts | 1 + .../molecules/nav/nav-link/nav-link.module.scss | 61 +++++++++++++++++ .../molecules/nav/nav-link/nav-link.stories.tsx | 79 ++++++++++++++++++++++ .../molecules/nav/nav-link/nav-link.test.tsx | 28 ++++++++ src/components/molecules/nav/nav-link/nav-link.tsx | 44 ++++++++++++ src/components/molecules/nav/nav-list.module.scss | 10 +++ src/components/molecules/nav/nav-list.tsx | 5 +- 8 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 src/components/molecules/nav/nav-link/index.ts create mode 100644 src/components/molecules/nav/nav-link/nav-link.module.scss create mode 100644 src/components/molecules/nav/nav-link/nav-link.stories.tsx create mode 100644 src/components/molecules/nav/nav-link/nav-link.test.tsx create mode 100644 src/components/molecules/nav/nav-link/nav-link.tsx (limited to 'src/components/molecules/nav') diff --git a/src/components/molecules/nav/index.ts b/src/components/molecules/nav/index.ts index 9c46050..fe7cd0b 100644 --- a/src/components/molecules/nav/index.ts +++ b/src/components/molecules/nav/index.ts @@ -1,3 +1,4 @@ export * from './breadcrumb'; +export * from './nav-link'; export * from './nav-list'; export * from './pagination'; diff --git a/src/components/molecules/nav/nav-link/index.ts b/src/components/molecules/nav/nav-link/index.ts new file mode 100644 index 0000000..f1b68c2 --- /dev/null +++ b/src/components/molecules/nav/nav-link/index.ts @@ -0,0 +1 @@ +export * from './nav-link'; diff --git a/src/components/molecules/nav/nav-link/nav-link.module.scss b/src/components/molecules/nav/nav-link/nav-link.module.scss new file mode 100644 index 0000000..8a7d371 --- /dev/null +++ b/src/components/molecules/nav/nav-link/nav-link.module.scss @@ -0,0 +1,61 @@ +@use "../../../../styles/abstracts/functions" as fun; +@use "../../../../styles/abstracts/placeholders"; + +.link { + --draw-border-thickness: #{fun.convert-px(4)}; + --draw-border-color1: var(--color-primary-light); + --draw-border-color2: var(--color-primary-lighter); + + display: flex; + flex-flow: row wrap; + place-items: center; + place-content: center; + row-gap: var(--spacing-2xs); + min-width: var(--link-min-width, fun.convert-px(80)); + padding: var(--spacing-xs) var(--spacing-xs) var(--spacing-2xs); + background: none; + border-radius: 8%; + font-size: var(--font-size-sm); + font-variant: small-caps; + font-weight: 600; + text-align: center; + text-decoration: none; + + &--inline { + width: fit-content; + + .logo { + margin-right: var(--spacing-xs); + } + } + + &--stack { + .logo { + flex: 0 0 100%; + display: flex; + place-content: center; + } + } + + &:hover, + &:focus { + @extend %draw-borders; + } + + &:hover { + color: var(--color-primary-light); + } + + &:focus { + color: var(--color-primary-light); + } + + &:active { + --draw-border-color1: var(--color-primary-dark); + --draw-border-color2: var(--color-primary-light); + + color: var(--color-primary-dark); + + @extend %draw-borders; + } +} diff --git a/src/components/molecules/nav/nav-link/nav-link.stories.tsx b/src/components/molecules/nav/nav-link/nav-link.stories.tsx new file mode 100644 index 0000000..4e8400f --- /dev/null +++ b/src/components/molecules/nav/nav-link/nav-link.stories.tsx @@ -0,0 +1,79 @@ +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import { Icon } from '../../../atoms'; +import { NavLink as NavLinkComponent } from './nav-link'; + +/** + * NavLink - Storybook Meta + */ +export default { + title: 'Molecules/Nav/NavLink', + component: NavLinkComponent, + argTypes: { + href: { + control: { + type: 'text', + }, + description: 'The link target.', + type: { + name: 'string', + required: true, + }, + }, + label: { + control: { + type: 'text', + }, + description: 'The link label.', + type: { + name: 'string', + required: true, + }, + }, + logo: { + control: { + type: null, + }, + description: 'The link logo.', + type: { + name: 'string', + required: true, + }, + }, + }, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ( +
+ +
+); + +/** + * NavLink Stories - Default + */ +export const Default = Template.bind({}); +Default.args = { + href: '#', + label: 'A nav link', +}; + +/** + * NavLink Stories - StackWithLogo + */ +export const StackWithLogo = Template.bind({}); +StackWithLogo.args = { + href: '#example', + label: 'A nav link', + logo: , +}; + +/** + * NavLink Stories - InlineWithLogo + */ +export const InlineWithLogo = Template.bind({}); +InlineWithLogo.args = { + href: '#example', + isInline: true, + label: 'A nav link', + logo: , +}; diff --git a/src/components/molecules/nav/nav-link/nav-link.test.tsx b/src/components/molecules/nav/nav-link/nav-link.test.tsx new file mode 100644 index 0000000..aa9b557 --- /dev/null +++ b/src/components/molecules/nav/nav-link/nav-link.test.tsx @@ -0,0 +1,28 @@ +import { describe, expect, it } from '@jest/globals'; +import { render, screen as rtlScreen } from '@testing-library/react'; +import { NavLink } from './nav-link'; + +describe('NavLink', () => { + it('renders a link', () => { + const label = 'eius'; + const target = '#harum'; + + render(); + + expect(rtlScreen.getByRole('link', { name: label })).toHaveAttribute( + 'href', + target + ); + }); + + it('can render a nav link with inlined contents', () => { + const label = 'eius'; + const target = '#harum'; + + render(); + + expect(rtlScreen.getByRole('link', { name: label })).toHaveClass( + 'link--inline' + ); + }); +}); diff --git a/src/components/molecules/nav/nav-link/nav-link.tsx b/src/components/molecules/nav/nav-link/nav-link.tsx new file mode 100644 index 0000000..f9fc529 --- /dev/null +++ b/src/components/molecules/nav/nav-link/nav-link.tsx @@ -0,0 +1,44 @@ +import { + type ForwardRefRenderFunction, + forwardRef, + type ReactNode, +} from 'react'; +import { Link, type LinkProps } from '../../../atoms'; +import styles from './nav-link.module.scss'; + +export type NavLinkProps = Omit & { + /** + * Should the logo and label be inlined? + * + * @default false + */ + isInline?: boolean; + /** + * The link label. + */ + label: string; + /** + * The link logo. + */ + logo?: ReactNode; +}; + +const NavLinkWithRef: ForwardRefRenderFunction< + HTMLAnchorElement, + NavLinkProps +> = ({ className = '', isInline = false, label, logo, ...props }, ref) => { + const linkClass = [ + styles.link, + styles[isInline ? 'link--inline' : 'link--stack'], + className, + ].join(' '); + + return ( + + {logo ? {logo} : null} + {label} + + ); +}; + +export const NavLink = forwardRef(NavLinkWithRef); diff --git a/src/components/molecules/nav/nav-list.module.scss b/src/components/molecules/nav/nav-list.module.scss index ff99581..316638e 100644 --- a/src/components/molecules/nav/nav-list.module.scss +++ b/src/components/molecules/nav/nav-list.module.scss @@ -1,4 +1,14 @@ .nav { + &--main { + width: fit-content; + } + + &--main & { + &__item { + flex: 1; + } + } + &--footer & { &__item:not(:first-child) { &::before { diff --git a/src/components/molecules/nav/nav-list.tsx b/src/components/molecules/nav/nav-list.tsx index 55c2aa9..b3c7138 100644 --- a/src/components/molecules/nav/nav-list.tsx +++ b/src/components/molecules/nav/nav-list.tsx @@ -1,5 +1,6 @@ import type { FC, ReactNode } from 'react'; -import { Link, List, ListItem, Nav, NavLink, type NavProps } from '../../atoms'; +import { Link, List, ListItem, Nav, type NavProps } from '../../atoms'; +import { NavLink } from './nav-link'; import styles from './nav-list.module.scss'; export type NavItem = { @@ -49,7 +50,7 @@ export const NavList: FC = ({ ...props }) => { const kindClass = `nav--${kind}`; - const navClass = `${styles[kindClass]} ${className}`; + const navClass = `${styles.nav} ${styles[kindClass]} ${className}`; /** * Get the nav items. -- cgit v1.2.3