From fe391523c8bd1b87093bd2e35bf14f59b39a6298 Mon Sep 17 00:00:00 2001 From: Anthony LC Date: Fri, 4 Oct 2024 13:24:57 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=B1(frontend)=20header=20small=20mobil?= =?UTF-8?q?e=20friendly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We adapt the header to be small mobile friendly. We added a burger menu to display the dropdown menu on small mobile. --- .../e2e/__tests__/app-impress/header.spec.ts | 38 +++++++++- .../{AccountDropdown.tsx => ButtonLogin.tsx} | 2 +- .../apps/impress/src/core/auth/index.ts | 2 +- .../components/Burger/Burger.module.css | 32 ++++++++ .../header/components/Burger/Burger.spec.tsx | 18 +++++ .../header/components/Burger/Burger.tsx | 31 ++++++++ .../header/components/Burger/burger.svg | 10 +++ .../header/components/DropdownMenu.tsx | 46 +++++++++++ .../header/{ => components}/Header.tsx | 76 +++++++++---------- .../header/{ => components}/LaGaufre.tsx | 10 ++- .../apps/impress/src/features/header/index.ts | 2 +- .../src/features/language/LanguagePicker.tsx | 6 +- .../apps/impress/src/layouts/MainLayout.tsx | 4 +- src/frontend/apps/impress/src/pages/_app.tsx | 1 + 14 files changed, 226 insertions(+), 52 deletions(-) rename src/frontend/apps/impress/src/core/auth/{AccountDropdown.tsx => ButtonLogin.tsx} (94%) create mode 100644 src/frontend/apps/impress/src/features/header/components/Burger/Burger.module.css create mode 100644 src/frontend/apps/impress/src/features/header/components/Burger/Burger.spec.tsx create mode 100644 src/frontend/apps/impress/src/features/header/components/Burger/Burger.tsx create mode 100644 src/frontend/apps/impress/src/features/header/components/Burger/burger.svg create mode 100644 src/frontend/apps/impress/src/features/header/components/DropdownMenu.tsx rename src/frontend/apps/impress/src/features/header/{ => components}/Header.tsx (50%) rename src/frontend/apps/impress/src/features/header/{ => components}/LaGaufre.tsx (63%) diff --git a/src/frontend/apps/e2e/__tests__/app-impress/header.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/header.spec.ts index e2508a69..b29f610b 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/header.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/header.spec.ts @@ -10,8 +10,6 @@ test.describe('Header', () => { test('checks all the elements are visible', async ({ page }) => { const header = page.locator('header').first(); - await expect(header.getByAltText('Gouvernement Logo')).toBeVisible(); - await expect(header.getByAltText('Docs Logo')).toBeVisible(); await expect(header.locator('h2').getByText('Docs')).toHaveCSS( 'color', @@ -67,6 +65,42 @@ test.describe('Header', () => { }); }); +test.describe('Header mobile', () => { + test.use({ viewport: { width: 500, height: 1200 } }); + + test.beforeEach(async ({ page }) => { + await page.goto('/'); + }); + + test('it checks the header when mobile', async ({ page }) => { + const header = page.locator('header').first(); + + await expect( + header.getByRole('button', { + name: 'Les services de La Suite numérique', + }), + ).toBeVisible(); + + await expect( + page.getByRole('button', { + name: 'Logout', + }), + ).toBeHidden(); + + await expect(page.getByAltText('Language Icon')).toBeHidden(); + + await header.getByLabel('Open the header menu').click(); + + await expect( + page.getByRole('button', { + name: 'Logout', + }), + ).toBeVisible(); + + await expect(page.getByAltText('Language Icon')).toBeVisible(); + }); +}); + test.describe('Header: Log out', () => { test.use({ storageState: { cookies: [], origins: [] } }); diff --git a/src/frontend/apps/impress/src/core/auth/AccountDropdown.tsx b/src/frontend/apps/impress/src/core/auth/ButtonLogin.tsx similarity index 94% rename from src/frontend/apps/impress/src/core/auth/AccountDropdown.tsx rename to src/frontend/apps/impress/src/core/auth/ButtonLogin.tsx index 43344d4b..f54fab32 100644 --- a/src/frontend/apps/impress/src/core/auth/AccountDropdown.tsx +++ b/src/frontend/apps/impress/src/core/auth/ButtonLogin.tsx @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'; import { useAuthStore } from '@/core/auth'; -export const AccountDropdown = () => { +export const ButtonLogin = () => { const { t } = useTranslation(); const { logout, authenticated, login } = useAuthStore(); diff --git a/src/frontend/apps/impress/src/core/auth/index.ts b/src/frontend/apps/impress/src/core/auth/index.ts index abae4bc5..e45c9a64 100644 --- a/src/frontend/apps/impress/src/core/auth/index.ts +++ b/src/frontend/apps/impress/src/core/auth/index.ts @@ -1,4 +1,4 @@ -export * from './AccountDropdown'; export * from './api/types'; export * from './Auth'; +export * from './ButtonLogin'; export * from './useAuthStore'; diff --git a/src/frontend/apps/impress/src/features/header/components/Burger/Burger.module.css b/src/frontend/apps/impress/src/features/header/components/Burger/Burger.module.css new file mode 100644 index 00000000..506ecfbe --- /dev/null +++ b/src/frontend/apps/impress/src/features/header/components/Burger/Burger.module.css @@ -0,0 +1,32 @@ +.burgerIcon { + cursor: pointer; + transform: translate(-20%, 0%); +} + +.burgerIcon path { + stroke-width: 40; + stroke-linecap: round; + fill: none; + transition: all 0.5s ease-in-out; +} + +/* In menu form */ +.burgerIcon path:first-child, +.burgerIcon path:last-child { + stroke-dasharray: 240px 910px; +} + +.burgerIcon .middle_bar { + stroke-dasharray: 240px 240px; +} + +/* In cross form */ +.open path:first-child, +.open path:last-child { + stroke-dashoffset: -650px; +} + +.open :nth-child(2) { + stroke-dasharray: 0 220px; + stroke-dashoffset: -120px; +} diff --git a/src/frontend/apps/impress/src/features/header/components/Burger/Burger.spec.tsx b/src/frontend/apps/impress/src/features/header/components/Burger/Burger.spec.tsx new file mode 100644 index 00000000..dcf7c10f --- /dev/null +++ b/src/frontend/apps/impress/src/features/header/components/Burger/Burger.spec.tsx @@ -0,0 +1,18 @@ +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { Burger } from './Burger'; + +describe('', () => { + test('Burger interactions', () => { + const { rerender } = render(); + + const burger = screen.getByRole('img'); + expect(burger).toBeInTheDocument(); + expect(burger.classList.contains('open')).toBeTruthy(); + + rerender(); + + expect(burger.classList.contains('open')).not.toBeTruthy(); + }); +}); diff --git a/src/frontend/apps/impress/src/features/header/components/Burger/Burger.tsx b/src/frontend/apps/impress/src/features/header/components/Burger/Burger.tsx new file mode 100644 index 00000000..c5941f81 --- /dev/null +++ b/src/frontend/apps/impress/src/features/header/components/Burger/Burger.tsx @@ -0,0 +1,31 @@ +import { SVGProps } from 'react'; + +import { Box } from '@/components'; +import { useCunninghamTheme } from '@/cunningham'; + +import styles from './Burger.module.css'; +import BurgerIcon from './burger.svg'; + +type BurgerProps = SVGProps & { + isOpen: boolean; +}; + +export const Burger = ({ isOpen, ...props }: BurgerProps) => { + const { colorsTokens } = useCunninghamTheme(); + + return ( + + + + ); +}; + +export default Burger; diff --git a/src/frontend/apps/impress/src/features/header/components/Burger/burger.svg b/src/frontend/apps/impress/src/features/header/components/Burger/burger.svg new file mode 100644 index 00000000..ac1d6e3a --- /dev/null +++ b/src/frontend/apps/impress/src/features/header/components/Burger/burger.svg @@ -0,0 +1,10 @@ + + + + + diff --git a/src/frontend/apps/impress/src/features/header/components/DropdownMenu.tsx b/src/frontend/apps/impress/src/features/header/components/DropdownMenu.tsx new file mode 100644 index 00000000..1b17fc9e --- /dev/null +++ b/src/frontend/apps/impress/src/features/header/components/DropdownMenu.tsx @@ -0,0 +1,46 @@ +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { Box, DropButton } from '@/components'; +import { ButtonLogin } from '@/core'; +import { useCunninghamTheme } from '@/cunningham'; +import { LanguagePicker } from '@/features/language'; + +import { Burger } from './Burger/Burger'; + +export const DropdownMenu = () => { + const { colorsTokens } = useCunninghamTheme(); + const [isDropOpen, setIsDropOpen] = useState(false); + const { t } = useTranslation(); + + return ( + + } + onOpenChange={(isOpen) => setIsDropOpen(isOpen)} + isOpen={isDropOpen} + > + + + + + + + + ); +}; diff --git a/src/frontend/apps/impress/src/features/header/Header.tsx b/src/frontend/apps/impress/src/features/header/components/Header.tsx similarity index 50% rename from src/frontend/apps/impress/src/features/header/Header.tsx rename to src/frontend/apps/impress/src/features/header/components/Header.tsx index f5802b3d..7e022e5d 100644 --- a/src/frontend/apps/impress/src/features/header/Header.tsx +++ b/src/frontend/apps/impress/src/features/header/components/Header.tsx @@ -1,59 +1,40 @@ import Image from 'next/image'; import React from 'react'; import { useTranslation } from 'react-i18next'; -import styled from 'styled-components'; import { Box, StyledLink, Text } from '@/components/'; -import { AccountDropdown } from '@/core/auth'; -import { useCunninghamTheme } from '@/cunningham'; +import { ButtonLogin } from '@/core/auth'; +import { LanguagePicker } from '@/features/language'; +import { useResponsiveStore } from '@/stores'; -import { LanguagePicker } from '../language/'; +import { default as IconDocs } from '../assets/icon-docs.svg?url'; +import { DropdownMenu } from './DropdownMenu'; import { LaGaufre } from './LaGaufre'; -import { default as IconDocs } from './assets/icon-docs.svg?url'; - -export const HEADER_HEIGHT = '100px'; - -const RedStripe = styled.div` - position: absolute; - height: 5px; - width: 100%; - background: var(--c--theme--colors--danger-500); - top: 0; -`; export const Header = () => { const { t } = useTranslation(); - const { themeTokens } = useCunninghamTheme(); - const logo = themeTokens().logo; + const { isSmallMobile } = useResponsiveStore(); return ( - - - {logo && ( - {logo.alt} - )} + { $height="fit-content" $margin={{ top: 'auto' }} > - {t('Docs + {t('Docs BETA - + {t('Docs')} - - - - - + {isSmallMobile ? ( + + + + + ) : ( + + + + + + )} ); diff --git a/src/frontend/apps/impress/src/features/header/LaGaufre.tsx b/src/frontend/apps/impress/src/features/header/components/LaGaufre.tsx similarity index 63% rename from src/frontend/apps/impress/src/features/header/LaGaufre.tsx rename to src/frontend/apps/impress/src/features/header/components/LaGaufre.tsx index 58dbdefa..627ac8ee 100644 --- a/src/frontend/apps/impress/src/features/header/LaGaufre.tsx +++ b/src/frontend/apps/impress/src/features/header/components/LaGaufre.tsx @@ -2,6 +2,13 @@ import { Gaufre } from '@gouvfr-lasuite/integration'; import '@gouvfr-lasuite/integration/dist/css/gaufre.css'; import Script from 'next/script'; import React from 'react'; +import { createGlobalStyle } from 'styled-components'; + +const GaufreStyle = createGlobalStyle` + .lasuite-gaufre-btn{ + box-shadow: inset 0 0 0 0 !important; + } +`; export const LaGaufre = () => ( <> @@ -10,6 +17,7 @@ export const LaGaufre = () => ( strategy="lazyOnload" id="lasuite-gaufre-script" /> - + + ); diff --git a/src/frontend/apps/impress/src/features/header/index.ts b/src/frontend/apps/impress/src/features/header/index.ts index 266dec8a..428157f9 100644 --- a/src/frontend/apps/impress/src/features/header/index.ts +++ b/src/frontend/apps/impress/src/features/header/index.ts @@ -1 +1 @@ -export * from './Header'; +export * from './components/Header'; diff --git a/src/frontend/apps/impress/src/features/language/LanguagePicker.tsx b/src/frontend/apps/impress/src/features/language/LanguagePicker.tsx index 990cd772..e40a8475 100644 --- a/src/frontend/apps/impress/src/features/language/LanguagePicker.tsx +++ b/src/frontend/apps/impress/src/features/language/LanguagePicker.tsx @@ -10,12 +10,12 @@ import IconLanguage from './assets/icon-language.svg?url'; const SelectStyled = styled(Select)<{ $isSmall?: boolean }>` flex-shrink: 0; - width: 5.5rem; + width: auto; .c__select__wrapper { min-height: 2rem; height: auto; - border-color: #ddd; + border-color: transparent; padding: 0 0.15rem 0 0.45rem; border-radius: 1px; @@ -28,7 +28,7 @@ const SelectStyled = styled(Select)<{ $isSmall?: boolean }>` } &:hover { - border-color: var(--c--theme--colors--primary-500); + box-shadow: var(--c--theme--colors--primary-100) 0 0 0 2px !important; } } `; diff --git a/src/frontend/apps/impress/src/layouts/MainLayout.tsx b/src/frontend/apps/impress/src/layouts/MainLayout.tsx index db050df1..5176557e 100644 --- a/src/frontend/apps/impress/src/layouts/MainLayout.tsx +++ b/src/frontend/apps/impress/src/layouts/MainLayout.tsx @@ -1,6 +1,6 @@ import { Box } from '@/components'; import { useCunninghamTheme } from '@/cunningham'; -import { HEADER_HEIGHT, Header } from '@/features/header'; +import { Header } from '@/features/header'; export function MainLayout({ children }: { children: React.ReactNode }) { const { colorsTokens } = useCunninghamTheme(); @@ -12,7 +12,7 @@ export function MainLayout({ children }: { children: React.ReactNode }) { diff --git a/src/frontend/apps/impress/src/pages/_app.tsx b/src/frontend/apps/impress/src/pages/_app.tsx index 48b8a2d0..6af42f45 100644 --- a/src/frontend/apps/impress/src/pages/_app.tsx +++ b/src/frontend/apps/impress/src/pages/_app.tsx @@ -28,6 +28,7 @@ export default function App({ Component, pageProps }: AppPropsWithLayout) { )} /> + {getLayout()}