diff --git a/CHANGELOG.md b/CHANGELOG.md
index b510b51ae..3fc8dc400 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@ and this project adheres to
- đź’„(frontend) error alert closeable on editor #284
- ♻️(backend) Change email content #283
- 🛂(frontend) viewers and editors can access share modal #302
+- ♻️(frontend) remove footer on doc editor #313
## Fixed
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-visibility.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-visibility.spec.ts
index 5f7a846c6..ba2b7ea6c 100644
--- a/src/frontend/apps/e2e/__tests__/app-impress/doc-visibility.spec.ts
+++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-visibility.spec.ts
@@ -94,6 +94,7 @@ test.describe('Doc Visibility: Not loggued', () => {
await page.goto(urlDoc);
await expect(page.locator('h2').getByText(docTitle)).toBeVisible();
+ await expect(page.getByRole('button', { name: 'Share' })).toBeHidden();
});
test('A private doc redirect to the OIDC when not authentified.', async ({
diff --git a/src/frontend/apps/e2e/__tests__/app-impress/footer.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/footer.spec.ts
new file mode 100644
index 000000000..0862051f4
--- /dev/null
+++ b/src/frontend/apps/e2e/__tests__/app-impress/footer.spec.ts
@@ -0,0 +1,77 @@
+import { expect, test } from '@playwright/test';
+
+import { goToGridDoc } from './common';
+
+test.beforeEach(async ({ page }) => {
+ await page.goto('/');
+});
+
+test.describe('Footer', () => {
+ test('checks all the elements are visible', async ({ page }) => {
+ const footer = page.locator('footer').first();
+
+ await expect(footer.getByAltText('Gouvernement Logo')).toBeVisible();
+
+ await expect(
+ footer.getByRole('link', { name: 'legifrance.gouv.fr' }),
+ ).toBeVisible();
+
+ await expect(
+ footer.getByRole('link', { name: 'info.gouv.fr' }),
+ ).toBeVisible();
+
+ await expect(
+ footer.getByRole('link', { name: 'service-public.fr' }),
+ ).toBeVisible();
+
+ await expect(
+ footer.getByRole('link', { name: 'data.gouv.fr' }),
+ ).toBeVisible();
+
+ await expect(
+ footer.getByRole('link', { name: 'Legal Notice' }),
+ ).toBeVisible();
+
+ await expect(
+ footer.getByRole('link', { name: 'Personal data and cookies' }),
+ ).toBeVisible();
+
+ await expect(
+ footer.getByRole('link', { name: 'Accessibility' }),
+ ).toBeVisible();
+
+ await expect(
+ footer.getByText(
+ 'Unless otherwise stated, all content on this site is under licence',
+ ),
+ ).toBeVisible();
+ });
+
+ test('checks footer is not visible on doc editor', async ({ page }) => {
+ await expect(page.locator('footer')).toBeVisible();
+ await goToGridDoc(page);
+ await expect(page.locator('footer')).toBeHidden();
+ });
+
+ const legalPages = [
+ { name: 'Legal Notice', url: '/legal-notice/' },
+ { name: 'Personal data and cookies', url: '/personal-data-cookies/' },
+ { name: 'Accessibility', url: '/accessibility/' },
+ ];
+ for (const { name, url } of legalPages) {
+ test(`checks ${name} page`, async ({ page }) => {
+ const footer = page.locator('footer').first();
+ await footer.getByRole('link', { name }).click();
+
+ await expect(
+ page
+ .getByRole('heading', {
+ name,
+ })
+ .first(),
+ ).toBeVisible();
+
+ await expect(page).toHaveURL(url);
+ });
+ }
+});
diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx
index 98dfd6386..c74fc3d88 100644
--- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx
+++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocToolBox.tsx
@@ -3,6 +3,7 @@ import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Box, DropButton, IconOptions, Text } from '@/components';
+import { useAuthStore } from '@/core';
import { usePanelEditorStore } from '@/features/docs/doc-editor/';
import {
Doc,
@@ -29,6 +30,7 @@ export const DocToolBox = ({ doc, versionId }: DocToolBoxProps) => {
const { setIsPanelOpen, setIsPanelTableContentOpen } = usePanelEditorStore();
const [isModalVersionOpen, setIsModalVersionOpen] = useState(false);
const { isSmallMobile } = useResponsiveStore();
+ const { authenticated } = useAuthStore();
return (
{
)}
- {
- setIsModalShareOpen(true);
- }}
- size={isSmallMobile ? 'small' : 'medium'}
- >
- {t('Share')}
-
+ {authenticated && (
+ {
+ setIsModalShareOpen(true);
+ }}
+ size={isSmallMobile ? 'small' : 'medium'}
+ >
+ {t('Share')}
+
+ )}
{
+ const { t } = useTranslation();
+ const { themeTokens } = useCunninghamTheme();
+ const logo = themeTokens().logo;
+
+ return (
+
+
+
+
+
+
+ {logo && (
+
+ )}
+
+
+
+ {[
+ {
+ label: 'legifrance.gouv.fr',
+ href: 'https://legifrance.gouv.fr/',
+ },
+ {
+ label: 'info.gouv.fr',
+ href: 'https://info.gouv.fr/',
+ },
+ {
+ label: 'service-public.fr',
+ href: 'https://service-public.fr/',
+ },
+ {
+ label: 'data.gouv.fr',
+ href: 'https://data.gouv.fr/',
+ },
+ ].map(({ label, href }) => (
+
+ {label}
+
+
+ ))}
+
+
+
+ {[
+ {
+ label: t('Legal Notice'),
+ href: '/legal-notice',
+ },
+ {
+ label: t('Personal data and cookies'),
+ href: '/personal-data-cookies',
+ },
+ {
+ label: t('Accessibility'),
+ href: '/accessibility',
+ },
+ ].map(({ label, href }) => (
+
+
+ {label}
+
+
+ ))}
+
+
+ {t('Unless otherwise stated, all content on this site is under')}{' '}
+
+ licence etalab-2.0
+
+
+
+
+
+ );
+};
diff --git a/src/frontend/apps/impress/src/features/footer/assets/external-link.svg b/src/frontend/apps/impress/src/features/footer/assets/external-link.svg
new file mode 100644
index 000000000..25093d139
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/footer/assets/external-link.svg
@@ -0,0 +1,5 @@
+
+
+
diff --git a/src/frontend/apps/impress/src/features/footer/index.tsx b/src/frontend/apps/impress/src/features/footer/index.tsx
new file mode 100644
index 000000000..ddcc5a9cd
--- /dev/null
+++ b/src/frontend/apps/impress/src/features/footer/index.tsx
@@ -0,0 +1 @@
+export * from './Footer';
diff --git a/src/frontend/apps/impress/src/features/service-worker/service-worker.ts b/src/frontend/apps/impress/src/features/service-worker/service-worker.ts
index 7d42d62cf..94b9c37c7 100644
--- a/src/frontend/apps/impress/src/features/service-worker/service-worker.ts
+++ b/src/frontend/apps/impress/src/features/service-worker/service-worker.ts
@@ -94,6 +94,9 @@ const precacheResources = [
'/',
'/index.html',
'/404/',
+ '/accessibility/',
+ '/legal-notice/',
+ '/personal-data-cookies/',
FALLBACK.offline,
FALLBACK.images,
FALLBACK.docs,
diff --git a/src/frontend/apps/impress/src/layouts/MainLayout.tsx b/src/frontend/apps/impress/src/layouts/MainLayout.tsx
index 5176557e6..bdfe587cc 100644
--- a/src/frontend/apps/impress/src/layouts/MainLayout.tsx
+++ b/src/frontend/apps/impress/src/layouts/MainLayout.tsx
@@ -1,8 +1,18 @@
+import { PropsWithChildren } from 'react';
+
import { Box } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
+import { Footer } from '@/features/footer';
import { Header } from '@/features/header';
-export function MainLayout({ children }: { children: React.ReactNode }) {
+interface MainLayoutProps {
+ withoutFooter?: boolean;
+}
+
+export function MainLayout({
+ children,
+ withoutFooter,
+}: PropsWithChildren) {
const { colorsTokens } = useCunninghamTheme();
return (
@@ -20,6 +30,7 @@ export function MainLayout({ children }: { children: React.ReactNode }) {
+ {!withoutFooter && }
);
}
diff --git a/src/frontend/apps/impress/src/layouts/PageLayout.tsx b/src/frontend/apps/impress/src/layouts/PageLayout.tsx
new file mode 100644
index 000000000..8efaeccc3
--- /dev/null
+++ b/src/frontend/apps/impress/src/layouts/PageLayout.tsx
@@ -0,0 +1,17 @@
+import { PropsWithChildren } from 'react';
+
+import { Box } from '@/components';
+import { Footer } from '@/features/footer';
+import { Header } from '@/features/header';
+
+export function PageLayout({ children }: PropsWithChildren) {
+ return (
+
+
+
+ {children}
+
+
+
+ );
+}
diff --git a/src/frontend/apps/impress/src/layouts/index.ts b/src/frontend/apps/impress/src/layouts/index.ts
index 1abe55193..eb143cc43 100644
--- a/src/frontend/apps/impress/src/layouts/index.ts
+++ b/src/frontend/apps/impress/src/layouts/index.ts
@@ -1 +1,2 @@
export * from './MainLayout';
+export * from './PageLayout';
diff --git a/src/frontend/apps/impress/src/pages/accessibility/index.tsx b/src/frontend/apps/impress/src/pages/accessibility/index.tsx
new file mode 100644
index 000000000..d89f2ee18
--- /dev/null
+++ b/src/frontend/apps/impress/src/pages/accessibility/index.tsx
@@ -0,0 +1,133 @@
+import { ReactElement } from 'react';
+import { Trans, useTranslation } from 'react-i18next';
+
+import { Box, Text, TextStyled } from '@/components';
+import { useCunninghamTheme } from '@/cunningham';
+import { PageLayout } from '@/layouts';
+import { NextPageWithLayout } from '@/types/next';
+
+const Page: NextPageWithLayout = () => {
+ const { t } = useTranslation();
+ const { colorsTokens } = useCunninghamTheme();
+
+ return (
+
+
+ {t('Accessibility')}
+
+
+
+
+ DINUM is committed to making its digital services
+ accessible, in accordance with article 47 of French law n° 2005-102
+ dated February 11, 2005.
+
+
+
+ {t('Accessibility statement')}
+
+ {t('Established on December 20, 2023.')}
+
+ {t('This accessibility statement applies to the site hosted on')}{' '}
+ https://docs.numerique.gouv.fr .
+
+
+ {t('Compliance status')}
+
+
+
+ https://docs.numerique.gouv.fr is not compliant
+ with RGAA 4.1. The site has not yet been audited.
+
+
+
+ {t('Improvement and contact')}
+
+
+ {t(
+ 'If you are unable to access a content or a service, you can contact the person responsible for https://lasuite.numerique.gouv.fr to be directed to an accessible alternative or to obtain the content in another form.',
+ )}
+
+
+
+ {t('E-mail:')}{' '}
+
+ lasuite@modernisation.gouv.fr
+
+
+
+ {t('Address:')} DINUM , 20 avenue de SĂ©gur 75007
+ Paris
+
+
+
+ {t('We try to respond within 2 working days.')}
+
+
+
+ {t('Remedies')}
+
+
+ {t('This procedure should be used in the following case:')}{' '}
+ {t(
+ 'you have reported to the website manager a lack of accessibility that prevents you from accessing content or one of the services of the portal and you have not received a satisfactory response.',
+ )}
+
+
+ {t('You can:')}
+
+
+
+
+ Write a message to the
+
+ Defender of Rights
+
+
+
+
+
+ Contact the delegate of the
+
+ Defender of Rights in your region
+
+
+
+
+ {t('Send a letter by post (free of charge, no stamp needed):')}{' '}
+
+ {t(
+ 'Defender of Rights - Free response - 71120 75342 Paris CEDEX 07',
+ )}
+
+
+
+
+
+ );
+};
+
+Page.getLayout = function getLayout(page: ReactElement) {
+ return {page} ;
+};
+
+export default Page;
diff --git a/src/frontend/apps/impress/src/pages/docs/[id]/index.tsx b/src/frontend/apps/impress/src/pages/docs/[id]/index.tsx
index 378713c92..f0fdf678f 100644
--- a/src/frontend/apps/impress/src/pages/docs/[id]/index.tsx
+++ b/src/frontend/apps/impress/src/pages/docs/[id]/index.tsx
@@ -21,7 +21,7 @@ export function DocLayout() {
}
return (
-
+
);
diff --git a/src/frontend/apps/impress/src/pages/legal-notice/index.tsx b/src/frontend/apps/impress/src/pages/legal-notice/index.tsx
new file mode 100644
index 000000000..0abbf9394
--- /dev/null
+++ b/src/frontend/apps/impress/src/pages/legal-notice/index.tsx
@@ -0,0 +1,72 @@
+import { ReactElement } from 'react';
+import { useTranslation } from 'react-i18next';
+
+import { Box, Text, TextStyled } from '@/components';
+import { useCunninghamTheme } from '@/cunningham';
+import { PageLayout } from '@/layouts';
+import { NextPageWithLayout } from '@/types/next';
+
+const Page: NextPageWithLayout = () => {
+ const { t } = useTranslation();
+ const { colorsTokens } = useCunninghamTheme();
+
+ return (
+
+
+ {t('Legal notice')}
+
+
+
+ {t('Publisher')}
+
+
+ {t(
+ 'French Interministerial Directorate for Digital Affairs (DINUM), 20 avenue de SĂ©gur 75007 Paris.',
+ )}
+
+
+ {t('Publication Director')}
+
+
+ {t('Stéphanie Schaer: Interministerial Digital Director (DINUM).')}
+
+
+ {t('Copyright')}
+
+
+ {t('Illustration:')}{' '}
+
+ DINUM
+
+
+
+ {t('More info?')}
+
+
+ {t(
+ 'The team in charge of the digital workspace "La Suite numérique" can be contacted directly at',
+ )}{' '}
+
+ lasuite@modernisation.gouv.fr
+
+ .
+
+
+
+ );
+};
+
+Page.getLayout = function getLayout(page: ReactElement) {
+ return {page} ;
+};
+
+export default Page;
diff --git a/src/frontend/apps/impress/src/pages/personal-data-cookies/index.tsx b/src/frontend/apps/impress/src/pages/personal-data-cookies/index.tsx
new file mode 100644
index 000000000..1a064006b
--- /dev/null
+++ b/src/frontend/apps/impress/src/pages/personal-data-cookies/index.tsx
@@ -0,0 +1,66 @@
+import { ReactElement } from 'react';
+import { useTranslation } from 'react-i18next';
+
+import { Box, Text } from '@/components';
+import { useCunninghamTheme } from '@/cunningham';
+import { PageLayout } from '@/layouts';
+import { NextPageWithLayout } from '@/types/next';
+
+const Page: NextPageWithLayout = () => {
+ const { t } = useTranslation();
+ const { colorsTokens } = useCunninghamTheme();
+
+ return (
+
+
+ {t('Personal data and cookies')}
+
+
+
+ {t('Cookies placed')}
+
+
+ {t(
+ 'This site places a small text file (a "cookie") on your computer when you visit it.',
+ )}
+ {t(
+ 'This allows us to measure the number of visits and understand which pages are the most viewed.',
+ )}
+
+
+ {t('You can oppose the tracking of your browsing on this website.')}
+ {t(
+ 'This will protect your privacy, but will also prevent the owner from learning from your actions and creating a better experience for you and other users.',
+ )}
+
+
+ {t('This site does not display a cookie consent banner, why?')}
+
+
+ {t(
+ "It's true, you didn't have to click on a block that covers half the page to say you agree to the placement of cookies — even if you don't know what it means!",
+ )}
+
+
+ {t(
+ 'Nothing exceptional, no special privileges related to a .gouv.fr.',
+ )}
+ {t(
+ 'We simply comply with the law, which states that certain audience measurement tools, properly configured to respect privacy, are exempt from prior authorization.',
+ )}
+
+
+
+ );
+};
+
+Page.getLayout = function getLayout(page: ReactElement) {
+ return {page} ;
+};
+
+export default Page;