✨(app-impress) add footer
Add footer to the impress app.
This commit is contained in:
42
src/frontend/apps/e2e/__tests__/app-impress/footer.spec.ts
Normal file
42
src/frontend/apps/e2e/__tests__/app-impress/footer.spec.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { expect, test } from '@playwright/test';
|
||||||
|
|
||||||
|
import { keyCloakSignIn } from './common';
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page, browserName }) => {
|
||||||
|
await page.goto('/');
|
||||||
|
await keyCloakSignIn(page, browserName);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.describe('Footer', () => {
|
||||||
|
test('checks all the elements are visible', async ({ page }) => {
|
||||||
|
const footer = page.locator('footer').first();
|
||||||
|
|
||||||
|
await expect(footer.getByAltText('Marianne Logo')).toBeVisible();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
footer.getByAltText('Freedom Equality Fraternity 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.getByText(
|
||||||
|
'Unless otherwise stated, all content on this site is under licence',
|
||||||
|
),
|
||||||
|
).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
@@ -33,6 +33,7 @@ export interface BoxProps {
|
|||||||
$radius?: CSSProperties['borderRadius'];
|
$radius?: CSSProperties['borderRadius'];
|
||||||
$transition?: CSSProperties['transition'];
|
$transition?: CSSProperties['transition'];
|
||||||
$width?: CSSProperties['width'];
|
$width?: CSSProperties['width'];
|
||||||
|
$zIndex?: CSSProperties['zIndex'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BoxType = ComponentPropsWithRef<typeof Box>;
|
export type BoxType = ComponentPropsWithRef<typeof Box>;
|
||||||
@@ -61,6 +62,7 @@ export const Box = styled('div')<BoxProps>`
|
|||||||
${({ $transition }) => $transition && `transition: ${$transition};`}
|
${({ $transition }) => $transition && `transition: ${$transition};`}
|
||||||
${({ $width }) => $width && `width: ${$width};`}
|
${({ $width }) => $width && `width: ${$width};`}
|
||||||
${({ $css }) => $css && `${$css};`}
|
${({ $css }) => $css && `${$css};`}
|
||||||
|
${({ $zIndex }) => $zIndex && `z-index: ${$zIndex};`}
|
||||||
${({ $effect }) => {
|
${({ $effect }) => {
|
||||||
let effect;
|
let effect;
|
||||||
switch ($effect) {
|
switch ($effect) {
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const StyledLink = styled(Link)`
|
export interface LinkProps {
|
||||||
|
$css?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StyledLink = styled(Link)<LinkProps>`
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #ffffff33;
|
color: #ffffff33;
|
||||||
&[aria-current='page'] {
|
&[aria-current='page'] {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
display: flex;
|
display: flex;
|
||||||
|
${({ $css }) => $css && `${$css};`}
|
||||||
`;
|
`;
|
||||||
|
|||||||
119
src/frontend/apps/impress/src/features/footer/Footer.tsx
Normal file
119
src/frontend/apps/impress/src/features/footer/Footer.tsx
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import Image from 'next/image';
|
||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { default as IconGouv } from '@/assets/icons/icon-gouv.svg?url';
|
||||||
|
import { default as IconMarianne } from '@/assets/icons/icon-marianne.svg?url';
|
||||||
|
import { Box, StyledLink, Text } from '@/components/';
|
||||||
|
|
||||||
|
import IconLink from './assets/external-link.svg';
|
||||||
|
|
||||||
|
const BlueStripe = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
height: 2px;
|
||||||
|
width: 100%;
|
||||||
|
background: var(--c--theme--colors--primary-600);
|
||||||
|
top: 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Footer = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box $position="relative" as="footer">
|
||||||
|
<BlueStripe />
|
||||||
|
<Box $padding={{ top: 'large', horizontal: 'big', bottom: 'small' }}>
|
||||||
|
<Box>
|
||||||
|
<Image
|
||||||
|
priority
|
||||||
|
src={IconMarianne}
|
||||||
|
alt={t('Marianne Logo')}
|
||||||
|
width={70}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
$direction="row"
|
||||||
|
$gap="1.5rem"
|
||||||
|
$align="center"
|
||||||
|
$justify="space-between"
|
||||||
|
>
|
||||||
|
<Box>
|
||||||
|
<Box $align="center" $gap="6rem" $direction="row">
|
||||||
|
<Image
|
||||||
|
width={100}
|
||||||
|
priority
|
||||||
|
src={IconGouv}
|
||||||
|
alt={t('Freedom Equality Fraternity Logo')}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
$direction="row"
|
||||||
|
$justify="flex-end"
|
||||||
|
$css={`
|
||||||
|
column-gap: 1.5rem;
|
||||||
|
row-gap: .5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
{[
|
||||||
|
{
|
||||||
|
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 }) => (
|
||||||
|
<StyledLink
|
||||||
|
key={label}
|
||||||
|
href={href}
|
||||||
|
target="__blank"
|
||||||
|
$css={`
|
||||||
|
gap:0.2rem;
|
||||||
|
transition: box-shadow 0.3s;
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0px 2px 0 0 var(--c--theme--colors--greyscale-text);
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<Text $weight="bold">{label}</Text>
|
||||||
|
<IconLink width={18} />
|
||||||
|
</StyledLink>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Text
|
||||||
|
as="p"
|
||||||
|
$size="m"
|
||||||
|
$margin={{ top: 'big' }}
|
||||||
|
$variation="600"
|
||||||
|
$display="inline"
|
||||||
|
>
|
||||||
|
{t('Unless otherwise stated, all content on this site is under')}{' '}
|
||||||
|
<StyledLink
|
||||||
|
href="https://github.com/etalab/licence-ouverte/blob/master/LO.md"
|
||||||
|
target="__blank"
|
||||||
|
$css={`
|
||||||
|
display:inline-flex;
|
||||||
|
box-shadow: 0px 1px 0 0 var(--c--theme--colors--greyscale-text);
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<Text $variation="600">licence etalab-2.0</Text>
|
||||||
|
<IconLink width={18} />
|
||||||
|
</StyledLink>
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
d="M10 6v2H5v11h11v-5h2v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h6Zm11-3v8h-2V6.413l-7.793 7.794-1.414-1.414L17.585 5H13V3h8Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 219 B |
@@ -3,13 +3,13 @@ import React from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import { default as IconGouv } from '@/assets/icons/icon-gouv.svg?url';
|
||||||
|
import { default as IconMarianne } from '@/assets/icons/icon-marianne.svg?url';
|
||||||
import { Box, Text } from '@/components/';
|
import { Box, Text } from '@/components/';
|
||||||
|
|
||||||
import { LanguagePicker } from '../language/';
|
import { LanguagePicker } from '../language/';
|
||||||
|
|
||||||
import { default as IconGouv } from './assets/icon-gouv.svg?url';
|
|
||||||
import { default as IconImpress } from './assets/icon-impress.svg?url';
|
import { default as IconImpress } from './assets/icon-impress.svg?url';
|
||||||
import { default as IconMarianne } from './assets/icon-marianne.svg?url';
|
|
||||||
import IconMyAccount from './assets/icon-my-account.png';
|
import IconMyAccount from './assets/icon-my-account.png';
|
||||||
|
|
||||||
export const HEADER_HEIGHT = '100px';
|
export const HEADER_HEIGHT = '100px';
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
import { Box } from '@/components';
|
import { Box } from '@/components';
|
||||||
|
import { Footer } from '@/features/footer/Footer';
|
||||||
import { HEADER_HEIGHT, Header } from '@/features/header';
|
import { HEADER_HEIGHT, Header } from '@/features/header';
|
||||||
|
|
||||||
export function MainLayout({ children }: { children: React.ReactNode }) {
|
export function MainLayout({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<Box $height="100vh" $css="overflow:hidden;">
|
<Box>
|
||||||
<Header />
|
<Box $height="100vh">
|
||||||
<Box $css="flex: 1;" $direction="row">
|
<Header />
|
||||||
<Box as="main" $height={`calc(100vh - ${HEADER_HEIGHT})`} $width="100%">
|
<Box $css="flex: 1;" $direction="row">
|
||||||
{children}
|
<Box
|
||||||
|
as="main"
|
||||||
|
$height={`calc(100vh - ${HEADER_HEIGHT})`}
|
||||||
|
$width="100%"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
<Footer />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,21 +9,21 @@ body {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
main ::-webkit-scrollbar {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
main ::-webkit-scrollbar-track {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
main ::-webkit-scrollbar-thumb {
|
||||||
background-color: #d6dee1;
|
background-color: #d6dee1;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
border: 6px solid transparent;
|
border: 6px solid transparent;
|
||||||
background-clip: content-box;
|
background-clip: content-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
main ::-webkit-scrollbar-thumb:hover {
|
||||||
background-color: #a8bbbf;
|
background-color: #a8bbbf;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user