From d75b9a1e150ab211c1052fb49bede9bd16320aca Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Sat, 7 Oct 2023 18:44:14 +0200 Subject: feat(components): add a generic Flip component The flipping animation is used at several places so it makes sense to use a single component to handle the animation. It will avoid styles duplication. --- .../molecules/layout/branding.module.scss | 32 ++++++++- .../molecules/layout/branding.stories.tsx | 26 ++++++-- src/components/molecules/layout/branding.test.tsx | 75 ++++++++++++++++++---- src/components/molecules/layout/branding.tsx | 54 ++++++---------- 4 files changed, 132 insertions(+), 55 deletions(-) (limited to 'src/components/molecules/layout') diff --git a/src/components/molecules/layout/branding.module.scss b/src/components/molecules/layout/branding.module.scss index 4d9e32c..bacf381 100644 --- a/src/components/molecules/layout/branding.module.scss +++ b/src/components/molecules/layout/branding.module.scss @@ -42,7 +42,7 @@ @include mix.media("screen") { @include mix.dimensions("2xs") { grid-template-columns: - var(--logo-size, fun.convert-px(100)) + var(--logo-size) minmax(0, 1fr); grid-template-rows: 1fr min-content; align-items: center; @@ -55,6 +55,8 @@ .logo { grid-row: span 2; margin-bottom: var(--spacing-sm); + border-radius: 50%; + animation: flip-logo 9s ease-in 0s 1; @include mix.media("screen") { @include mix.dimensions("2xs") { @@ -103,3 +105,31 @@ } } } + +.flip { + width: var(--logo-size); + height: var(--logo-size); + border: fun.convert-px(2) solid var(--color-primary-dark); + border-radius: 50%; + box-shadow: + fun.convert-px(1) fun.convert-px(2) fun.convert-px(1) 0 + var(--color-shadow-light), + fun.convert-px(2) fun.convert-px(3) fun.convert-px(3) 0 + var(--color-shadow-light); + + > * { + padding: fun.convert-px(2); + border-radius: 50%; + } +} + +@keyframes flip-logo { + 0%, + 90% { + transform: rotateY(180deg); + } + + 100% { + transform: rotateY(0deg); + } +} diff --git a/src/components/molecules/layout/branding.stories.tsx b/src/components/molecules/layout/branding.stories.tsx index 04844e2..7ff88c9 100644 --- a/src/components/molecules/layout/branding.stories.tsx +++ b/src/components/molecules/layout/branding.stories.tsx @@ -1,4 +1,6 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import NextImage from 'next/image'; +import { Logo } from '../../atoms'; import { Branding } from './branding'; /** @@ -82,8 +84,16 @@ const Template: ComponentStory = (args) => ( */ export const Default = Template.bind({}); Default.args = { + logo: , + photo: ( + + ), title: 'Website title', - photo: 'http://placeimg.com/640/480', }; /** @@ -91,7 +101,15 @@ Default.args = { */ export const WithBaseline = Template.bind({}); WithBaseline.args = { - title: 'Website title', baseline: 'Maiores corporis qui', - photo: 'http://placeimg.com/640/480', + logo: , + photo: ( + + ), + title: 'Website title', }; diff --git a/src/components/molecules/layout/branding.test.tsx b/src/components/molecules/layout/branding.test.tsx index 4b76446..cfb55c5 100644 --- a/src/components/molecules/layout/branding.test.tsx +++ b/src/components/molecules/layout/branding.test.tsx @@ -1,62 +1,109 @@ import { describe, expect, it } from '@jest/globals'; -import { render, screen } from '../../../../tests/utils'; +import NextImage from 'next/image'; +import { render, screen as rtlScreen } from '../../../../tests/utils'; +import { Logo } from '../../atoms'; import { Branding } from './branding'; describe('Branding', () => { it('renders a photo', () => { + const altText = 'A photo example'; + render( } + photo={ + + } title="Website title" /> ); - expect( - screen.getByRole('img', { name: 'Website title picture' }) - ).toBeInTheDocument(); + expect(rtlScreen.getByRole('img', { name: altText })).toBeInTheDocument(); }); it('renders a logo', () => { + const logoHeading = 'sed enim voluptatem'; + render( - + } + photo={ + + } + title="Website name" + /> ); - expect(screen.getByTitle('Website name logo')).toBeInTheDocument(); + expect(rtlScreen.getByTitle(logoHeading)).toBeInTheDocument(); }); it('renders a baseline', () => { render( } + photo={ + + } title="Website title" baseline="Website baseline" /> ); - expect(screen.getByText('Website baseline')).toBeInTheDocument(); + expect(rtlScreen.getByText('Website baseline')).toBeInTheDocument(); }); it('renders a title wrapped with h1 element', () => { render( } + photo={ + + } title="Website title" isHome={true} /> ); expect( - screen.getByRole('heading', { level: 1, name: 'Website title' }) + rtlScreen.getByRole('heading', { level: 1, name: 'Website title' }) ).toBeInTheDocument(); }); it('renders a title with h1 styles', () => { render( } + photo={ + + } title="Website title" isHome={false} /> ); expect( - screen.queryByRole('heading', { level: 1, name: 'Website title' }) + rtlScreen.queryByRole('heading', { level: 1, name: 'Website title' }) ).not.toBeInTheDocument(); - expect(screen.getByText('Website title')).toHaveClass('heading--1'); + expect(rtlScreen.getByText('Website title')).toHaveClass('heading--1'); }); }); diff --git a/src/components/molecules/layout/branding.tsx b/src/components/molecules/layout/branding.tsx index dceee5e..c3d3b7c 100644 --- a/src/components/molecules/layout/branding.tsx +++ b/src/components/molecules/layout/branding.tsx @@ -1,11 +1,9 @@ -import { type FC, useRef } from 'react'; -import { useIntl } from 'react-intl'; +import { type FC, useRef, type ReactNode } from 'react'; import { useStyles } from '../../../utils/hooks'; -import { Heading, Link } from '../../atoms'; -import { FlippingLogo, type FlippingLogoProps } from '../images'; +import { Flip, FlipSide, Heading, Link } from '../../atoms'; import styles from './branding.module.scss'; -export type BrandingProps = Pick & { +export type BrandingProps = { /** * The Branding baseline. */ @@ -14,6 +12,14 @@ export type BrandingProps = Pick & { * Use H1 if the current page is homepage. Default: false. */ isHome?: boolean; + /** + * The website logo. + */ + logo: ReactNode; + /** + * Your photo. + */ + photo: ReactNode; /** * The Branding title; */ @@ -32,31 +38,14 @@ export type BrandingProps = Pick & { export const Branding: FC = ({ baseline, isHome = false, + logo, photo, title, withLink = false, ...props }) => { const baselineRef = useRef(null); - const logoRef = useRef(null); const titleRef = useRef(null); - const intl = useIntl(); - const altText = intl.formatMessage( - { - defaultMessage: '{website} picture', - description: 'Branding: photo alternative text', - id: 'dDK5oc', - }, - { website: title } - ); - const logoTitle = intl.formatMessage( - { - defaultMessage: '{website} logo', - description: 'Branding: logo title', - id: 'x55qsD', - }, - { website: title } - ); useStyles({ property: '--typing-animation', @@ -69,22 +58,15 @@ export const Branding: FC = ({ 'hide-text 4.25s linear 0s 1, blink 0.8s ease-in-out 4.25s 2, typing 3.8s linear 4.25s 1', target: baselineRef, }); - useStyles({ - property: 'animation', - styles: 'flip-logo 9s ease-in 0s 1', - target: logoRef, - }); return (
- + + {photo} + + {logo} + +