diff --git a/CHANGELOG.md b/CHANGELOG.md index 68305f62..89f1caf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to ## [Unreleased] +### Added + +- ✨(frontend) integrate configurable Waffle #1795 + ### Fixed - ✅(e2e) fix e2e test for other browsers #1799 diff --git a/docs/assets/waffle.png b/docs/assets/waffle.png new file mode 100644 index 00000000..8597eb0a Binary files /dev/null and b/docs/assets/waffle.png differ diff --git a/docs/theming.md b/docs/customization.md similarity index 80% rename from docs/theming.md rename to docs/customization.md index f67024df..ee192003 100644 --- a/docs/theming.md +++ b/docs/customization.md @@ -1,4 +1,6 @@ -# Runtime Theming 🎨 +# Customization Guide 🛠 ️ + +## Runtime Theming 🎨 ### How to Use @@ -32,7 +34,7 @@ Then, set the `FRONTEND_CSS_URL` environment variable to the URL of your custom ---- -# Runtime JavaScript Injection 🚀 +## Runtime JavaScript Injection 🚀 ### How to Use @@ -87,7 +89,7 @@ Then, set the `FRONTEND_JS_URL` environment variable to the URL of your custom J ---- -# **Your Docs icon** 📝 +## **Your Docs icon** 📝 You can add your own Docs icon in the header from the theme customization file. @@ -105,7 +107,7 @@ This configuration is optional. If not set, the default icon will be used. ---- -# **Footer Configuration** 📝 +## **Footer Configuration** 📝 The footer is configurable from the theme customization file. @@ -128,7 +130,7 @@ Below is a visual example of a configured footer ⬇️: ---- -# **Custom Translations** 📝 +## **Custom Translations** 📝 The translations can be partially overridden from the theme customization file. @@ -140,4 +142,36 @@ THEME_CUSTOMIZATION_FILE_PATH= ### Example of JSON -The json must follow some rules: https://github.com/suitenumerique/docs/blob/main/src/helm/env.d/dev/configuration/theme/demo.json \ No newline at end of file +The json must follow some rules: https://github.com/suitenumerique/docs/blob/main/src/helm/env.d/dev/configuration/theme/demo.json + +---- + +## **Waffle Configuration** 🧇 + +The Waffle (La Gaufre) is a widget that displays a grid of services. + +![Waffle Configuration Example](./assets/waffle.png) + +### Settings 🔧 + +```shellscript +THEME_CUSTOMIZATION_FILE_PATH= +``` + +### Configuration + +The Waffle can be configured in the theme customization file with the `waffle` key. + +### Available Properties + +See: [LaGaufreV2Props](https://github.com/suitenumerique/ui-kit/blob/main/src/components/la-gaufre/LaGaufreV2.tsx#L49) + +### Complete Example + +From the theme customization file: https://github.com/suitenumerique/docs/blob/main/src/helm/env.d/dev/configuration/theme/demo.json + +### Behavior + +- If `data.services` is provided, the Waffle will display those services statically +- If no data is provided, services can be fetched dynamically from an API endpoint thanks to the `apiUrl` property + 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 a458dac8..6d31d28e 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/header.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/header.spec.ts @@ -59,45 +59,90 @@ test.describe('Header', () => { ).toBeVisible(); await expect(header.getByText('English')).toBeVisible(); - - await expect( - header.getByRole('button', { - name: 'Les services de La Suite numérique', - }), - ).toBeVisible(); }); - test('checks La Gauffre interaction', async ({ page }) => { + test('checks a custom waffle', async ({ page }) => { await overrideConfig(page, { - FRONTEND_THEME: 'dsfr', + theme_customization: { + waffle: { + data: { + services: [ + { + name: 'Docs E2E Custom 1', + url: 'https://docs.numerique.gouv.fr/', + maturity: 'stable', + logo: 'https://lasuite.numerique.gouv.fr/assets/products/docs.svg', + }, + { + name: 'Docs E2E Custom 2', + url: 'https://docs.numerique.gouv.fr/', + maturity: 'stable', + logo: 'https://lasuite.numerique.gouv.fr/assets/products/docs.svg', + }, + ], + }, + showMoreLimit: 9, + }, + }, }); await page.goto('/'); const header = page.locator('header').first(); await expect( - header.getByRole('button', { - name: 'Les services de La Suite numérique', - }), + header.getByRole('button', { name: 'Digital LaSuite services' }), ).toBeVisible(); /** - * La gaufre load a js file from a remote server, + * The Waffle loads a js file from a remote server, + * it takes some time to load the file and have the interaction available + */ + await page.waitForTimeout(1500); + + await header + .getByRole('button', { name: 'Digital LaSuite services' }) + .click(); + + await expect( + page.getByRole('link', { name: 'Docs E2E Custom 1' }), + ).toBeVisible(); + await expect( + page.getByRole('link', { name: 'Docs E2E Custom 2' }), + ).toBeVisible(); + }); + + test('checks the waffle dsfr', async ({ page }) => { + await overrideConfig(page, { + theme_customization: { + waffle: { + apiUrl: 'https://lasuite.numerique.gouv.fr/api/services', + showMoreLimit: 9, + }, + }, + }); + await page.goto('/'); + + const header = page.locator('header').first(); + + await expect( + header.getByRole('button', { name: 'Digital LaSuite services' }), + ).toBeVisible(); + + /** + * The Waffle loads a js file from a remote server, * it takes some time to load the file and have the interaction available */ await page.waitForTimeout(1500); await header .getByRole('button', { - name: 'Les services de La Suite numérique', + name: 'Digital LaSuite services', }) .click(); - await expect( - page.getByRole('link', { name: 'France Transfert' }), - ).toBeVisible(); - + await expect(page.getByRole('link', { name: 'Tchap' })).toBeVisible(); await expect(page.getByRole('link', { name: 'Grist' })).toBeVisible(); + await expect(page.getByRole('link', { name: 'Visio' })).toBeVisible(); }); }); @@ -124,11 +169,6 @@ test.describe('Header mobile', () => { await expect(header.getByLabel('Open the header menu')).toBeVisible(); await expect(header.getByTestId('header-icon-docs')).toBeVisible(); - await expect( - header.getByRole('button', { - name: 'Les services de La Suite numérique', - }), - ).toBeVisible(); }); }); diff --git a/src/frontend/apps/e2e/__tests__/app-impress/home.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/home.spec.ts index b1f7484b..e5d72bd8 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/home.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/home.spec.ts @@ -113,9 +113,6 @@ test.describe('Home page', () => { }); await expect(languageButton).toBeVisible(); - await expect( - header.getByRole('button', { name: 'Les services de La Suite numé' }), - ).toBeVisible(); await expect( header.getByRole('img', { name: 'Gouvernement Logo' }), ).toBeVisible(); diff --git a/src/frontend/apps/impress/cunningham.ts b/src/frontend/apps/impress/cunningham.ts index f9b26046..d59a0b28 100644 --- a/src/frontend/apps/impress/cunningham.ts +++ b/src/frontend/apps/impress/cunningham.ts @@ -26,7 +26,6 @@ const themeWhiteLabelLight = getUIKitThemesFromGlobals(whiteLabelGlobals, { widthHeader: '', widthFooter: '', }, - 'la-gaufre': false, 'home-proconnect': false, icon: { src: '/assets/icon-docs.svg', @@ -64,7 +63,6 @@ const themesDSFRLight = getUIKitThemesFromGlobals(dsfrGlobals, { widthFooter: '220px', alt: 'Gouvernement Logo', }, - 'la-gaufre': true, 'home-proconnect': true, icon: { src: '/assets/icon-docs-dsfr.svg', diff --git a/src/frontend/apps/impress/src/core/config/api/useConfig.tsx b/src/frontend/apps/impress/src/core/config/api/useConfig.tsx index c1e9c26d..df846e67 100644 --- a/src/frontend/apps/impress/src/core/config/api/useConfig.tsx +++ b/src/frontend/apps/impress/src/core/config/api/useConfig.tsx @@ -4,13 +4,14 @@ import { Resource } from 'i18next'; import { APIError, errorCauses, fetchAPI } from '@/api'; import { Theme } from '@/cunningham/'; import { FooterType } from '@/features/footer'; -import { HeaderType } from '@/features/header'; +import { HeaderType, WaffleType } from '@/features/header'; import { PostHogConf } from '@/services'; interface ThemeCustomization { footer?: FooterType; translations?: Resource; header?: HeaderType; + waffle?: WaffleType; } export interface ConfigResponse { diff --git a/src/frontend/apps/impress/src/cunningham/__tests__/useCunninghamTheme.spec.tsx b/src/frontend/apps/impress/src/cunningham/__tests__/useCunninghamTheme.spec.tsx index 20c99e23..709f6d73 100644 --- a/src/frontend/apps/impress/src/cunningham/__tests__/useCunninghamTheme.spec.tsx +++ b/src/frontend/apps/impress/src/cunningham/__tests__/useCunninghamTheme.spec.tsx @@ -5,7 +5,7 @@ describe('', () => { expect(useCunninghamTheme.getState().componentTokens.logo?.src).toBe(''); // Change theme - useCunninghamTheme.getState().setTheme('dsfr-light'); + useCunninghamTheme.getState().setTheme('dsfr'); const { componentTokens } = useCunninghamTheme.getState(); const logo = componentTokens.logo; diff --git a/src/frontend/apps/impress/src/cunningham/cunningham-tokens.css b/src/frontend/apps/impress/src/cunningham/cunningham-tokens.css index 77241e67..c09bd662 100644 --- a/src/frontend/apps/impress/src/cunningham/cunningham-tokens.css +++ b/src/frontend/apps/impress/src/cunningham/cunningham-tokens.css @@ -893,7 +893,6 @@ --c--components--logo--alt: ; --c--components--logo--widthheader: ; --c--components--logo--widthfooter: ; - --c--components--la-gaufre: false; --c--components--home-proconnect: false; --c--components--icon--src: /assets/icon-docs.svg; --c--components--icon--width: 32px; @@ -2594,7 +2593,6 @@ --c--components--logo--alt: gouvernement logo; --c--components--logo--widthHeader: 110px; --c--components--logo--widthFooter: 220px; - --c--components--la-gaufre: true; --c--components--home-proconnect: true; --c--components--icon--src: /assets/icon-docs-dsfr.svg; --c--components--icon--width: 32px; diff --git a/src/frontend/apps/impress/src/cunningham/cunningham-tokens.ts b/src/frontend/apps/impress/src/cunningham/cunningham-tokens.ts index 9b543da7..e3d77fe4 100644 --- a/src/frontend/apps/impress/src/cunningham/cunningham-tokens.ts +++ b/src/frontend/apps/impress/src/cunningham/cunningham-tokens.ts @@ -677,7 +677,6 @@ export const tokens = { info: { 'background-color': '#D5E4F3', color: '#005BC0' }, }, logo: { src: '', alt: '', widthHeader: '', widthFooter: '' }, - 'la-gaufre': false, 'home-proconnect': false, icon: { src: '/assets/icon-docs.svg', width: '32px', height: 'auto' }, favicon: { @@ -1973,7 +1972,6 @@ export const tokens = { widthHeader: '110px', widthFooter: '220px', }, - 'la-gaufre': true, 'home-proconnect': true, icon: { src: '/assets/icon-docs-dsfr.svg', diff --git a/src/frontend/apps/impress/src/features/header/components/Header.tsx b/src/frontend/apps/impress/src/features/header/components/Header.tsx index a792cc42..ffc8d5fc 100644 --- a/src/frontend/apps/impress/src/features/header/components/Header.tsx +++ b/src/frontend/apps/impress/src/features/header/components/Header.tsx @@ -12,8 +12,8 @@ import { useResponsiveStore } from '@/stores'; import { HEADER_HEIGHT } from '../conf'; import { ButtonTogglePanel } from './ButtonTogglePanel'; -import { LaGaufre } from './LaGaufre'; import { Title } from './Title'; +import { Waffle } from './Waffle'; export const Header = () => { const { t } = useTranslation(); @@ -85,7 +85,7 @@ export const Header = () => { {!isDesktop ? ( - + ) : ( { > - + )} diff --git a/src/frontend/apps/impress/src/features/header/components/LaGaufre.tsx b/src/frontend/apps/impress/src/features/header/components/LaGaufre.tsx deleted file mode 100644 index 1910124a..00000000 --- a/src/frontend/apps/impress/src/features/header/components/LaGaufre.tsx +++ /dev/null @@ -1,39 +0,0 @@ -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'; - -import { useCunninghamTheme } from '@/cunningham'; - -const GaufreStyle = createGlobalStyle` - .lasuite-gaufre-btn{ - box-shadow: inset 0 0 0 0 !important; - border-radius: var(--c--components--button--border-radius) !important; - transition: all var(--c--globals--transitions--duration) var(--c--globals--transitions--ease-out) !important; - &:hover, &:focus-visible { - background: var(--c--contextuals--background--semantic--contextual--primary) !important; - } - color: var(--c--contextuals--content--semantic--brand--tertiary) !important; - } -`; - -export const LaGaufre = () => { - const { componentTokens } = useCunninghamTheme(); - - if (!componentTokens['la-gaufre']) { - return null; - } - - return ( - <> -