From 57dc56f83eb7ec307863de782ccb4d7591ecb633 Mon Sep 17 00:00:00 2001 From: Anthony LC Date: Fri, 6 Feb 2026 16:01:30 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8(frontend)=20improve=20overriding?= =?UTF-8?q?=20from=20configuration=20theme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We were partially overriding the frontend with the cunningham theme meaning at build time. We stop to do this way to do it only from the configuration theme. This way it will be easier to maintain and to update. We improve as well the typing with more global types like Image type from logo and icons, and HTMLLinkElement type for the favicon, meaning you can really override compoments from the configuration theme. --- .../impress/configuration/theme/default.json | 34 ++- .../e2e/__tests__/app-impress/config.spec.ts | 42 +-- .../e2e/__tests__/app-impress/footer.spec.ts | 3 +- .../e2e/__tests__/app-impress/header.spec.ts | 13 +- .../__tests__/app-impress/language.spec.ts | 26 ++ .../e2e/__tests__/app-impress/utils-common.ts | 4 +- src/frontend/apps/impress/cunningham.ts | 37 --- .../apps/impress/public/assets/logo-pdf.svg | 248 ------------------ .../src/core/config/ConfigProvider.tsx | 20 ++ .../impress/src/core/config/api/useConfig.tsx | 12 + .../__tests__/useCunninghamTheme.spec.tsx | 14 +- .../src/cunningham/cunningham-tokens.css | 21 -- .../src/cunningham/cunningham-tokens.ts | 24 -- .../impress/src/features/footer/Footer.tsx | 8 +- .../apps/impress/src/features/footer/types.ts | 25 +- .../src/features/header/components/Header.tsx | 26 +- .../apps/impress/src/features/header/types.ts | 11 +- .../features/home/components/HomeBanner.tsx | 33 ++- .../features/home/components/HomeBottom.tsx | 5 +- .../features/home/components/HomeHeader.tsx | 37 ++- src/frontend/apps/impress/src/pages/_app.tsx | 16 -- 21 files changed, 197 insertions(+), 462 deletions(-) delete mode 100644 src/frontend/apps/impress/public/assets/logo-pdf.svg diff --git a/src/backend/impress/configuration/theme/default.json b/src/backend/impress/configuration/theme/default.json index 9f1b718c..376be0d8 100644 --- a/src/backend/impress/configuration/theme/default.json +++ b/src/backend/impress/configuration/theme/default.json @@ -3,8 +3,8 @@ "default": { "logo": { "src": "/assets/icon-docs.svg", - "width": "54px", "alt": "Docs Logo", + "style": { "width": "54px", "height": "auto" }, "withTitle": true }, "externalLinks": [ @@ -125,5 +125,37 @@ } } } + }, + "home": { + "with-proconnect": false, + "icon-banner": { + "src": "/assets/icon-docs.svg", + "style": { + "width": "64px", + "height": "auto" + }, + "alt": "" + } + }, + "header": { + "logo": {}, + "icon": { + "src": "/assets/icon-docs.svg", + "style": { + "width": "32px", + "height": "auto" + }, + "alt": "" + } + }, + "favicon": { + "light": { + "href": "/assets/favicon-light.png", + "type": "image/png" + }, + "dark": { + "href": "/assets/favicon-dark.png", + "type": "image/png" + } } } diff --git a/src/frontend/apps/e2e/__tests__/app-impress/config.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/config.spec.ts index 44470fbb..87112e5e 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/config.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/config.spec.ts @@ -140,26 +140,6 @@ test.describe('Config', () => { ).toBeAttached(); }); - test('it checks theme_customization.translations config', async ({ - page, - }) => { - await overrideConfig(page, { - theme_customization: { - translations: { - en: { - translation: { - Docs: 'MyCustomDocs', - }, - }, - }, - }, - }); - - await page.goto('/'); - - await expect(page.getByText('MyCustomDocs')).toBeAttached(); - }); - test('it checks the config api is called', async ({ page }) => { const responsePromise = page.waitForResponse( (response) => @@ -172,11 +152,7 @@ test.describe('Config', () => { expect(response.ok()).toBeTruthy(); const json = (await response.json()) as typeof CONFIG; - const { theme_customization, ...configApi } = json; - expect(theme_customization).toBeDefined(); - const { theme_customization: _, ...CONFIG_LEFT } = CONFIG; - - expect(configApi).toStrictEqual(CONFIG_LEFT); + expect(json).toStrictEqual(CONFIG); }); }); @@ -186,14 +162,24 @@ test.describe('Config: Not logged', () => { test('it checks that theme is configured from config endpoint', async ({ page, }) => { + await page.goto('/'); + + await expect( + page.getByText('Collaborative writing, Simplified.'), + ).toHaveCSS('font-family', /Roboto/i, { + timeout: 10000, + }); + await overrideConfig(page, { FRONTEND_THEME: 'dsfr', }); await page.goto('/'); - const header = page.locator('header').first(); - // alt 'Gouvernement Logo' comes from the theme - await expect(header.getByAltText('Gouvernement Logo')).toBeVisible(); + await expect( + page.getByText('Collaborative writing, Simplified.'), + ).toHaveCSS('font-family', /Marianne/i, { + timeout: 10000, + }); }); }); diff --git a/src/frontend/apps/e2e/__tests__/app-impress/footer.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/footer.spec.ts index c46da0ba..c7e03038 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/footer.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/footer.spec.ts @@ -56,14 +56,13 @@ test.describe('Footer', () => { test('checks the footer is correctly overrided', async ({ page }) => { await overrideConfig(page, { - FRONTEND_THEME: 'dsfr', theme_customization: { footer: { default: { logo: { src: '/assets/logo-gouv.svg', - width: '220px', alt: 'Gouvernement Logo', + style: { width: '220px', height: 'auto' }, }, externalLinks: [ { 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 6d31d28e..6d1ad49a 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/header.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/header.spec.ts @@ -36,9 +36,18 @@ test.describe('Header', () => { header: { logo: { src: '/assets/logo-gouv.svg', - width: '220px', + style: { width: '220px', height: 'auto' }, alt: 'Gouvernement Logo', }, + icon: { + src: '/assets/icon-docs.svg', + style: { + width: '32px', + height: 'auto', + }, + alt: '', + 'data-testid': 'custom-testid-docs', + }, }, }, }); @@ -46,7 +55,7 @@ test.describe('Header', () => { const header = page.locator('header').first(); - await expect(header.getByTestId('header-icon-docs')).toBeVisible(); + await expect(header.getByTestId('custom-testid-docs')).toBeVisible(); await expect(header.locator('h1').getByText('Docs')).toHaveCSS( 'font-family', /Marianne/i, diff --git a/src/frontend/apps/e2e/__tests__/app-impress/language.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/language.spec.ts index 27250623..8538239e 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/language.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/language.spec.ts @@ -13,6 +13,32 @@ test.describe('Language', () => { await page.goto('/'); }); + test('it checks theme_customization.translations config', async ({ + page, + }) => { + await overrideConfig(page, { + theme_customization: { + translations: { + en: { + translation: { + Docs: 'MyCustomDocs', + }, + }, + }, + header: { + logo: {}, + icon: { + withTitle: true, + }, + }, + }, + }); + + await page.goto('/'); + + await expect(page.getByText('MyCustomDocs')).toBeAttached(); + }); + test('checks language switching', async ({ page }) => { const header = page.locator('header').first(); const languagePicker = header.locator('.--docs--language-picker-text'); diff --git a/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts b/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts index b7a0672c..ed28a911 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/utils-common.ts @@ -3,6 +3,8 @@ import path from 'path'; import { Locator, Page, TestInfo, expect } from '@playwright/test'; +import theme_customization from '../../../../../backend/impress/configuration/theme/default.json'; + export type BrowserName = 'chromium' | 'firefox' | 'webkit'; export const BROWSERS: BrowserName[] = ['chromium', 'webkit', 'firefox']; @@ -32,7 +34,7 @@ export const CONFIG = { POSTHOG_KEY: {}, SENTRY_DSN: null, TRASHBIN_CUTOFF_DAYS: 30, - theme_customization: {}, + theme_customization, } as const; export const overrideConfig = async ( diff --git a/src/frontend/apps/impress/cunningham.ts b/src/frontend/apps/impress/cunningham.ts index d59a0b28..b548f250 100644 --- a/src/frontend/apps/impress/cunningham.ts +++ b/src/frontend/apps/impress/cunningham.ts @@ -19,24 +19,6 @@ const themeWhiteLabelLight = getUIKitThemesFromGlobals(whiteLabelGlobals, { '2xs': '0.375rem', }, }, - components: { - logo: { - src: '', - alt: '', - widthHeader: '', - widthFooter: '', - }, - 'home-proconnect': false, - icon: { - src: '/assets/icon-docs.svg', - width: '32px', - height: 'auto', - }, - favicon: { - 'png-light': '/assets/favicon-light.png', - 'png-dark': '/assets/favicon-dark.png', - }, - }, }, }); @@ -56,25 +38,6 @@ const themesDSFRLight = getUIKitThemesFromGlobals(dsfrGlobals, { }, }, }, - components: { - logo: { - src: '/assets/logo-gouv.svg', - widthHeader: '110px', - widthFooter: '220px', - alt: 'Gouvernement Logo', - }, - 'home-proconnect': true, - icon: { - src: '/assets/icon-docs-dsfr.svg', - width: '32px', - height: 'auto', - }, - favicon: { - ico: '/assets/favicon-dsfr.ico', - 'png-light': '/assets/favicon-dsfr.png', - 'png-dark': '/assets/favicon-dark-dsfr.png', - }, - }, }, }); diff --git a/src/frontend/apps/impress/public/assets/logo-pdf.svg b/src/frontend/apps/impress/public/assets/logo-pdf.svg deleted file mode 100644 index 19cf5153..00000000 --- a/src/frontend/apps/impress/public/assets/logo-pdf.svg +++ /dev/null @@ -1,248 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/src/frontend/apps/impress/src/core/config/ConfigProvider.tsx b/src/frontend/apps/impress/src/core/config/ConfigProvider.tsx index a318c66b..b2b5e184 100644 --- a/src/frontend/apps/impress/src/core/config/ConfigProvider.tsx +++ b/src/frontend/apps/impress/src/core/config/ConfigProvider.tsx @@ -27,6 +27,7 @@ export const ConfigProvider = ({ children }: PropsWithChildren) => { const { AnalyticsProvider } = useAnalytics(); const { i18n } = useTranslation(); const languageSynchronized = useRef(false); + const favicon = conf?.theme_customization?.favicon; useEffect(() => { if (!user || languageSynchronized.current) { @@ -99,6 +100,25 @@ export const ConfigProvider = ({ children }: PropsWithChildren) => { {conf?.FRONTEND_JS_URL && (