diff options
| author | Armand Philippot <git@armandphilippot.com> | 2022-04-12 16:55:59 +0200 | 
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2022-04-12 17:07:12 +0200 | 
| commit | 017d01680a933897df6ddd11d2e081730756250b (patch) | |
| tree | 487170c0a92efa61f82c2d45c0da2a6dfea0ef5d /src/components/organisms | |
| parent | 4a6f3dbde9adaa671e622215654a1dd9b329610d (diff) | |
chore: add a Footer component
Diffstat (limited to 'src/components/organisms')
| -rw-r--r-- | src/components/organisms/layout/footer.module.scss | 39 | ||||
| -rw-r--r-- | src/components/organisms/layout/footer.stories.tsx | 74 | ||||
| -rw-r--r-- | src/components/organisms/layout/footer.test.tsx | 33 | ||||
| -rw-r--r-- | src/components/organisms/layout/footer.tsx | 52 | 
4 files changed, 198 insertions, 0 deletions
| diff --git a/src/components/organisms/layout/footer.module.scss b/src/components/organisms/layout/footer.module.scss new file mode 100644 index 0000000..a809d3c --- /dev/null +++ b/src/components/organisms/layout/footer.module.scss @@ -0,0 +1,39 @@ +@use "@styles/abstracts/functions" as fun; +@use "@styles/abstracts/mixins" as mix; + +.wrapper { +  display: flex; +  flex-flow: column wrap; +  gap: var(--spacing-xs); +  place-items: center; +  place-content: center; +  padding: var(--spacing-md) 0 calc(var(--toolbar-size) + var(--spacing-md)); + +  @include mix.media("screen") { +    @include mix.dimensions("sm") { +      flex-flow: row wrap; +      font-size: var(--font-size-sm); +    } +  } +} + +.nav { +  display: flex; +  flex-flow: row wrap; + +  @include mix.media("screen") { +    @include mix.dimensions("sm") { +      &::before { +        content: "\2022"; +        margin-right: var(--spacing-2xs); +      } +    } +  } +} + +.back-to-top { +  position: fixed; +  bottom: calc(var(--toolbar-size, 0px) + var(--spacing-md)); +  right: var(--spacing-md); +  transition: all 0.4s ease-in 0s; +} diff --git a/src/components/organisms/layout/footer.stories.tsx b/src/components/organisms/layout/footer.stories.tsx new file mode 100644 index 0000000..2ce7ee1 --- /dev/null +++ b/src/components/organisms/layout/footer.stories.tsx @@ -0,0 +1,74 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import { IntlProvider } from 'react-intl'; +import FooterComponent from './footer'; + +export default { +  title: 'Organisms/Layout', +  component: FooterComponent, +  argTypes: { +    className: { +      control: { +        type: 'text', +      }, +      description: 'Set additional classnames to the footer element.', +      table: { +        category: 'Styles', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    }, +    copyright: { +      description: 'The copyright information.', +      type: { +        name: 'object', +        required: true, +        value: {}, +      }, +    }, +    navItems: { +      description: 'The footer nav items.', +      table: { +        category: 'Options', +      }, +      type: { +        name: 'object', +        required: false, +        value: {}, +      }, +    }, +    topId: { +      control: { +        type: 'text', +      }, +      description: +        'An element id (without hashtag) used as target by back to top button.', +      type: { +        name: 'string', +        required: true, +      }, +    }, +  }, +} as ComponentMeta<typeof FooterComponent>; + +const Template: ComponentStory<typeof FooterComponent> = (args) => ( +  <IntlProvider locale="en"> +    <FooterComponent {...args} /> +  </IntlProvider> +); + +const copyright = { +  dates: { start: '2017', end: '2022' }, +  owner: 'Lorem ipsum', +  icon: 'CC', +}; + +const navItems = [{ id: 'legal-notice', href: '#', label: 'Legal notice' }]; + +export const Footer = Template.bind({}); +Footer.args = { +  copyright, +  navItems, +  topId: 'top', +}; diff --git a/src/components/organisms/layout/footer.test.tsx b/src/components/organisms/layout/footer.test.tsx new file mode 100644 index 0000000..bc23732 --- /dev/null +++ b/src/components/organisms/layout/footer.test.tsx @@ -0,0 +1,33 @@ +import { render, screen } from '@test-utils'; +import Footer, { type FooterProps } from './footer'; + +const copyright: FooterProps['copyright'] = { +  dates: { start: '2017', end: '2022' }, +  owner: 'Lorem ipsum', +  icon: 'CC', +}; + +const navItems: FooterProps['navItems'] = [ +  { id: 'legal-notice', href: '#', label: 'Legal notice' }, +]; + +describe('Footer', () => { +  it('renders the website copyright', () => { +    render(<Footer copyright={copyright} topId="top" />); +    expect(screen.getByText(copyright.owner)).toBeInTheDocument(); +  }); + +  it('renders a back to top link', () => { +    render(<Footer copyright={copyright} topId="top" />); +    expect( +      screen.getByRole('link', { name: 'Back to top' }) +    ).toBeInTheDocument(); +  }); + +  it('renders some nav items', () => { +    render(<Footer copyright={copyright} navItems={navItems} topId="top" />); +    expect( +      screen.getByRole('link', { name: navItems[0].label }) +    ).toBeInTheDocument(); +  }); +}); diff --git a/src/components/organisms/layout/footer.tsx b/src/components/organisms/layout/footer.tsx new file mode 100644 index 0000000..c9cb067 --- /dev/null +++ b/src/components/organisms/layout/footer.tsx @@ -0,0 +1,52 @@ +import Copyright, { CopyrightProps } from '@components/atoms/layout/copyright'; +import BackToTop from '@components/molecules/buttons/back-to-top'; +import Nav, { type NavItem } from '@components/molecules/nav/nav'; +import { VFC } from 'react'; +import styles from './footer.module.scss'; + +export type FooterProps = { +  /** +   * Set additional classnames to the footer element. +   */ +  className?: string; +  /** +   * Set the copyright information. +   */ +  copyright: CopyrightProps; +  /** +   * The footer nav items. +   */ +  navItems?: NavItem[]; +  /** +   * An element id (without hashtag) used as anchor for back to top button. +   */ +  topId: string; +}; + +/** + * Footer component + * + * Renders a footer with copyright and nav; + */ +const Footer: VFC<FooterProps> = ({ +  className, +  copyright, +  navItems, +  topId, +}) => { +  return ( +    <footer className={`${styles.wrapper} ${className}`}> +      <Copyright +        dates={copyright.dates} +        owner={copyright.owner} +        icon={copyright.icon} +      /> +      {navItems && ( +        <Nav kind="footer" items={navItems} className={styles.nav} /> +      )} +      <BackToTop target={topId} className={styles['back-to-top']} /> +    </footer> +  ); +}; + +export default Footer; | 
