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 && (
)}
+ {favicon?.light.href && (
+
+
+
+ )}
+ {favicon?.dark.href && (
+
+
+
+ )}
+
{children}
>
);
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 00282fe8..d1aa1d9e 100644
--- a/src/frontend/apps/impress/src/core/config/api/useConfig.tsx
+++ b/src/frontend/apps/impress/src/core/config/api/useConfig.tsx
@@ -1,5 +1,7 @@
import { useQuery } from '@tanstack/react-query';
import { Resource } from 'i18next';
+import Image from 'next/image';
+import { LinkHTMLAttributes } from 'react';
import { APIError, errorCauses, fetchAPI } from '@/api';
import { Theme } from '@/cunningham/';
@@ -7,8 +9,18 @@ import { FooterType } from '@/features/footer';
import { HeaderType, WaffleType } from '@/features/header';
import { PostHogConf } from '@/services';
+type Imagetype = React.ComponentProps;
+
interface ThemeCustomization {
+ favicon?: {
+ light: LinkHTMLAttributes;
+ dark: LinkHTMLAttributes;
+ };
footer?: FooterType;
+ home: {
+ 'with-proconnect'?: boolean;
+ 'icon-banner'?: Imagetype;
+ };
translations?: Resource;
header?: HeaderType;
waffle?: WaffleType;
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 709f6d73..dea9c2b2 100644
--- a/src/frontend/apps/impress/src/cunningham/__tests__/useCunninghamTheme.spec.tsx
+++ b/src/frontend/apps/impress/src/cunningham/__tests__/useCunninghamTheme.spec.tsx
@@ -1,16 +1,16 @@
import { useCunninghamTheme } from '../useCunninghamTheme';
describe('', () => {
- it('has the logo correctly set', () => {
- expect(useCunninghamTheme.getState().componentTokens.logo?.src).toBe('');
+ it('changing theme update tokens', () => {
+ expect(
+ useCunninghamTheme.getState().currentTokens.globals?.font.families.base,
+ ).toBe('Hanken Grotesk, Inter, Roboto Flex Variable, sans-serif');
// Change theme
useCunninghamTheme.getState().setTheme('dsfr');
- const { componentTokens } = useCunninghamTheme.getState();
- const logo = componentTokens.logo;
- expect(logo?.src).toBe('/assets/logo-gouv.svg');
- expect(logo?.widthHeader).toBe('110px');
- expect(logo?.widthFooter).toBe('220px');
+ expect(
+ useCunninghamTheme.getState().currentTokens.globals?.font.families.base,
+ ).toBe('Marianne, Inter, Roboto Flex Variable, sans-serif');
});
});
diff --git a/src/frontend/apps/impress/src/cunningham/cunningham-tokens.css b/src/frontend/apps/impress/src/cunningham/cunningham-tokens.css
index c09bd662..a0756a08 100644
--- a/src/frontend/apps/impress/src/cunningham/cunningham-tokens.css
+++ b/src/frontend/apps/impress/src/cunningham/cunningham-tokens.css
@@ -889,16 +889,6 @@
--c--components--badge--info--color: var(
--c--contextuals--content--semantic--info--secondary
);
- --c--components--logo--src: ;
- --c--components--logo--alt: ;
- --c--components--logo--widthheader: ;
- --c--components--logo--widthfooter: ;
- --c--components--home-proconnect: false;
- --c--components--icon--src: /assets/icon-docs.svg;
- --c--components--icon--width: 32px;
- --c--components--icon--height: auto;
- --c--components--favicon--png-light: /assets/favicon-light.png;
- --c--components--favicon--png-dark: /assets/favicon-dark.png;
}
.cunningham-theme--dark {
@@ -2589,17 +2579,6 @@
--c--components--badge--info--color: var(
--c--contextuals--content--semantic--info--secondary
);
- --c--components--logo--src: /assets/logo-gouv.svg;
- --c--components--logo--alt: gouvernement logo;
- --c--components--logo--widthHeader: 110px;
- --c--components--logo--widthFooter: 220px;
- --c--components--home-proconnect: true;
- --c--components--icon--src: /assets/icon-docs-dsfr.svg;
- --c--components--icon--width: 32px;
- --c--components--icon--height: auto;
- --c--components--favicon--png-light: /assets/favicon-dsfr.png;
- --c--components--favicon--png-dark: /assets/favicon-dark-dsfr.png;
- --c--components--favicon--ico: /assets/favicon-dsfr.ico;
}
.clr-logo-1-light {
diff --git a/src/frontend/apps/impress/src/cunningham/cunningham-tokens.ts b/src/frontend/apps/impress/src/cunningham/cunningham-tokens.ts
index e3d77fe4..ce3e5d22 100644
--- a/src/frontend/apps/impress/src/cunningham/cunningham-tokens.ts
+++ b/src/frontend/apps/impress/src/cunningham/cunningham-tokens.ts
@@ -676,13 +676,6 @@ export const tokens = {
warning: { 'background-color': '#F1E0D3', color: '#AD3300' },
info: { 'background-color': '#D5E4F3', color: '#005BC0' },
},
- 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',
- },
},
},
dark: {
@@ -1966,23 +1959,6 @@ export const tokens = {
warning: { 'background-color': '#F1E0D3', color: '#AD3300' },
info: { 'background-color': '#D5E4F3', color: '#005BC0' },
},
- logo: {
- src: '/assets/logo-gouv.svg',
- alt: 'Gouvernement Logo',
- widthHeader: '110px',
- widthFooter: '220px',
- },
- 'home-proconnect': true,
- icon: {
- src: '/assets/icon-docs-dsfr.svg',
- width: '32px',
- height: 'auto',
- },
- favicon: {
- 'png-light': '/assets/favicon-dsfr.png',
- 'png-dark': '/assets/favicon-dark-dsfr.png',
- ico: '/assets/favicon-dsfr.ico',
- },
},
},
},
diff --git a/src/frontend/apps/impress/src/features/footer/Footer.tsx b/src/frontend/apps/impress/src/features/footer/Footer.tsx
index e2033d94..7c108021 100644
--- a/src/frontend/apps/impress/src/features/footer/Footer.tsx
+++ b/src/frontend/apps/impress/src/features/footer/Footer.tsx
@@ -22,7 +22,7 @@ const BlueStripe = styled.div`
export const Footer = () => {
const { data: config } = useConfig();
const footerJson = config?.theme_customization?.footer;
- const { i18n, t } = useTranslation();
+ const { i18n } = useTranslation();
const resolvedLanguage = i18n.resolvedLanguage;
const [content, setContent] = useState();
@@ -84,14 +84,12 @@ export const Footer = () => {
{logo?.src && (
rest)(logo)}
/>
)}
- {logo.withTitle && (
+ {logo?.withTitle && (
diff --git a/src/frontend/apps/impress/src/features/footer/types.ts b/src/frontend/apps/impress/src/features/footer/types.ts
index 10320f18..ef9b6865 100644
--- a/src/frontend/apps/impress/src/features/footer/types.ts
+++ b/src/frontend/apps/impress/src/features/footer/types.ts
@@ -1,28 +1,29 @@
+import Image from 'next/image';
+
+type Imagetype = React.ComponentProps;
+
export interface FooterType {
default: ContentType;
[key: string]: ContentType;
}
-export interface BottomInformation {
+export interface BottomInformationType {
label: string;
- link?: Link;
+ link?: LinkType;
}
-export interface Link {
+export interface LinkType {
label: string;
href: string;
}
-export interface Logo {
- src: string;
- width: string;
- alt: string;
+export type LogoType = Imagetype & {
withTitle: boolean;
-}
+};
export interface ContentType {
- logo?: Logo;
- externalLinks?: Link[];
- legalLinks?: Link[];
- bottomInformation?: BottomInformation;
+ logo?: LogoType;
+ externalLinks?: LinkType[];
+ legalLinks?: LinkType[];
+ bottomInformation?: BottomInformationType;
}
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 ffc8d5fc..d40651a6 100644
--- a/src/frontend/apps/impress/src/features/header/components/Header.tsx
+++ b/src/frontend/apps/impress/src/features/header/components/Header.tsx
@@ -18,11 +18,10 @@ import { Waffle } from './Waffle';
export const Header = () => {
const { t } = useTranslation();
const { data: config } = useConfig();
- const { spacingsTokens, componentTokens } = useCunninghamTheme();
+ const { spacingsTokens } = useCunninghamTheme();
const { isDesktop } = useResponsiveStore();
- const icon =
- config?.theme_customization?.header?.icon || componentTokens.icon;
+ const icon = config?.theme_customization?.header?.icon;
return (
<>
@@ -68,18 +67,15 @@ export const Header = () => {
$height="fit-content"
$margin={{ top: 'auto' }}
>
-
+ {icon && (
+
+ )}
diff --git a/src/frontend/apps/impress/src/features/header/types.ts b/src/frontend/apps/impress/src/features/header/types.ts
index 61184f6a..0a42f65b 100644
--- a/src/frontend/apps/impress/src/features/header/types.ts
+++ b/src/frontend/apps/impress/src/features/header/types.ts
@@ -1,7 +1,8 @@
+import Image from 'next/image';
+
+type Imagetype = React.ComponentProps;
+
export interface HeaderType {
- icon?: {
- src?: string;
- width?: string;
- height?: string;
- };
+ logo?: Imagetype;
+ icon?: Imagetype;
}
diff --git a/src/frontend/apps/impress/src/features/home/components/HomeBanner.tsx b/src/frontend/apps/impress/src/features/home/components/HomeBanner.tsx
index 6e2a92d1..e9ccf944 100644
--- a/src/frontend/apps/impress/src/features/home/components/HomeBanner.tsx
+++ b/src/frontend/apps/impress/src/features/home/components/HomeBanner.tsx
@@ -15,13 +15,11 @@ import { getHeaderHeight } from './HomeHeader';
export default function HomeBanner() {
const { t } = useTranslation();
- const { componentTokens, spacingsTokens } = useCunninghamTheme();
+ const { spacingsTokens } = useCunninghamTheme();
const { isMobile, isSmallMobile } = useResponsiveStore();
- const withProConnect = componentTokens['home-proconnect'];
const { data: config } = useConfig();
-
- const icon =
- config?.theme_customization?.header?.icon || componentTokens.icon;
+ const withProConnect = config?.theme_customization?.home?.['with-proconnect'];
+ const icon = config?.theme_customization?.home?.['icon-banner'];
return (
-
+ {icon?.src && (
+
+ )}
isSmallMobile ? HEADER_HEIGHT_MOBILE : HEADER_HEIGHT;
export const HomeHeader = () => {
- const { spacingsTokens, componentTokens } = useCunninghamTheme();
- const logo = componentTokens.logo;
+ const { spacingsTokens } = useCunninghamTheme();
const { isSmallMobile } = useResponsiveStore();
const { data: config } = useConfig();
- const icon =
- config?.theme_customization?.header?.icon || componentTokens.icon;
+ const icon = config?.theme_customization?.header?.icon;
+ const logo = config?.theme_customization?.header?.logo;
return (
{
{!isSmallMobile && logo?.src && (
)}
{
$position="relative"
$height="fit-content"
>
-
+ {icon && (
+
+ )}
diff --git a/src/frontend/apps/impress/src/pages/_app.tsx b/src/frontend/apps/impress/src/pages/_app.tsx
index dbe9bec4..c63e4482 100644
--- a/src/frontend/apps/impress/src/pages/_app.tsx
+++ b/src/frontend/apps/impress/src/pages/_app.tsx
@@ -3,7 +3,6 @@ import Head from 'next/head';
import { useTranslation } from 'react-i18next';
import { AppProvider } from '@/core/';
-import { useCunninghamTheme } from '@/cunningham';
import { useOffline, useSWRegister } from '@/features/service-worker/';
import '@/i18n/initI18n';
import { NextPageWithLayout } from '@/types/next';
@@ -19,8 +18,6 @@ export default function App({ Component, pageProps }: AppPropsWithLayout) {
useOffline();
const getLayout = Component.getLayout ?? ((page) => page);
const { t } = useTranslation();
- const { componentTokens } = useCunninghamTheme();
- const favicon = componentTokens['favicon'];
return (
<>
@@ -33,19 +30,6 @@ export default function App({ Component, pageProps }: AppPropsWithLayout) {
'Docs: Your new companion to collaborate on documents efficiently, intuitively, and securely.',
)}
/>
-
-
-
{getLayout()}