💄(frontend) update ui to fit design mockups

Update lightly the ui to match the new design mockups,
to fit with what was proposed for the duplicate subdocs
feature.
It updates:
- modal closing position
- checkbox style
- toast style
- create a ButtonLink component, a wrapper above
the ui-kit component to fit the Next.js routing system.
This commit is contained in:
Anthony LC
2026-09-18 16:17:31 +02:00
parent f52224cac9
commit d471fd987e
9 changed files with 82 additions and 24 deletions
@@ -1,5 +1,8 @@
import { Button } from '@gouvfr-lasuite/ui-components';
import Link from 'next/link';
import styled, { RuleSet } from 'styled-components';
import { useRouter } from 'next/router';
import { type ComponentProps } from 'react';
import styled, { type RuleSet } from 'styled-components';
export interface LinkProps {
$css?: string | RuleSet<object>;
@@ -14,3 +17,35 @@ export const StyledLink = styled(Link)<LinkProps>`
display: flex;
${({ $css }) => $css && (typeof $css === 'string' ? `${$css};` : $css)}
`;
type ButtonLinkProps = ComponentProps<typeof Button> & {
href: string;
};
export const ButtonLink = ({
children,
onClick,
ref,
...props
}: ButtonLinkProps) => {
const router = useRouter();
return (
<Button
ref={ref}
onClick={(e) => {
if (!e.ctrlKey && !e.metaKey && !e.shiftKey) {
e.preventDefault();
if (props.href) {
void router.push(props.href);
}
}
onClick?.(e as React.MouseEvent<HTMLButtonElement, MouseEvent>);
}}
{...props}
>
{children}
</Button>
);
};
@@ -57,6 +57,28 @@
height: auto;
}
/**
* Checkbox
*/
.c__checkbox:focus-within {
border-color: transparent;
box-shadow: none;
}
.c__checkbox .c__checkbox__wrapper:focus-within {
outline: none;
}
.c__checkbox__wrapper input:focus,
.c__checkbox__wrapper input:checked:focus {
box-shadow: unset;
}
.c__checkbox input:focus-visible {
outline: 2px solid var(--c--globals--colors--brand-400);
outline-offset: var(--c--globals--spacings--4xs);
}
/**
* Modal
*/
@@ -104,6 +126,10 @@
/**
* Toast
*/
.c__toast {
max-width: min(100%, 100vw);
}
.c__toast__container {
z-index: -1;
}
@@ -111,3 +137,8 @@
.c__toast__container:has(.c__toast) {
z-index: 10000;
}
.c__toast__content__children {
flex-shrink: 0;
flex-grow: 0;
}
@@ -204,7 +204,7 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
>
{t('Export')}
</Text>
<Box $position="absolute" $css="top: 4px; right: 4px;">
<Box $position="absolute" $css="top: 8px; right: 8px;">
<ButtonCloseModal
aria-label={t('Close the download modal')}
onClick={() => onClose()}
@@ -202,6 +202,11 @@ const DocTitleInput = ({ doc, onTitleUpdate }: DocTitleProps) => {
$align="center"
$gap="4px"
$minHeight="40px"
$css={css`
&:focus-within {
outline: none;
}
`}
>
{!isTopRoot && <DocTitleEmojiPicker doc={doc} />}
{/**
@@ -187,7 +187,7 @@ export const DocMoveModal = ({
<Text as="h2" $margin="0" $size="s" $align="flex-start">
{t('Choose a new parent doc')}
</Text>
<Box $position="absolute" $css="top: 4px; right: 4px;">
<Box $position="absolute" $css="top: 8px; right: 8px;">
<ButtonCloseModal
aria-label={t('Close the move modal')}
onClick={onClose}
@@ -11,7 +11,7 @@ import { Trans, useTranslation } from 'react-i18next';
import { Box, ButtonCloseModal, Text, TextErrors } from '@/components';
import { useConfig } from '@/core';
import { KEY_LIST_DOC_TRASHBIN } from '@/docs/docs-grid';
import { useKeyboardAction, useToast } from '@/hooks';
import { useToast } from '@/hooks';
import { KEY_DOC, KEY_LIST_FAVORITE_DOC } from '../api';
import { KEY_LIST_DOC } from '../api/useDocs';
@@ -72,15 +72,10 @@ export const ModalRemoveDoc = ({
return () => cancelAnimationFrame(id);
}, []);
const keyboardAction = useKeyboardAction();
const handleDelete = () => {
removeDoc({ docId: doc.id });
};
const handleCloseKeyDown = keyboardAction(onClose);
const handleDeleteKeyDown = keyboardAction(handleDelete);
return (
<Modal
isOpen
@@ -97,7 +92,6 @@ export const ModalRemoveDoc = ({
fullWidth
autoFocus
onClick={onClose}
onKeyDown={handleCloseKeyDown}
>
{t('Cancel')}
</Button>
@@ -106,7 +100,6 @@ export const ModalRemoveDoc = ({
color="error"
fullWidth
onClick={handleDelete}
onKeyDown={handleDeleteKeyDown}
>
{t('Delete')}
</Button>
@@ -124,11 +117,10 @@ export const ModalRemoveDoc = ({
>
{t('Delete a doc')}
</Text>
<Box $position="absolute" $css="top: 4px; right: 4px;">
<Box $position="absolute" $css="top: 8px; right: 8px;">
<ButtonCloseModal
aria-label={t('Close the delete modal')}
onClick={onClose}
onKeyDown={handleCloseKeyDown}
/>
</Box>
</>
@@ -8,7 +8,7 @@ import { useRouter } from 'next/router';
import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Text } from '@/components';
import { ButtonLink, Text } from '@/components';
import { useConfig } from '@/core/config/api/useConfig';
import { useLeftPanelStore } from '@/features/left-panel/stores/useLeftPanelStore';
import ArrowDownIcon from '@/icons/arrow-drop-down.svg';
@@ -26,7 +26,6 @@ interface NewDocButtonProps {
}
export const NewDocButton = ({ onClose }: NewDocButtonProps) => {
const router = useRouter();
const { t } = useTranslation();
const { currentDoc } = useDocStore();
const { data: config } = useConfig();
@@ -34,15 +33,11 @@ export const NewDocButton = ({ onClose }: NewDocButtonProps) => {
return (
<>
<Button
<ButtonLink
href="/docs/new"
data-testid="new-doc-button"
color="brand"
onClick={(e) => {
if (!e.ctrlKey && !e.metaKey && !e.shiftKey) {
e.preventDefault();
void router.push('/docs/new');
}
onClick={() => {
onClose?.();
}}
icon={<PlusIcon aria-hidden="true" width={24} height={24} />}
@@ -55,7 +50,7 @@ export const NewDocButton = ({ onClose }: NewDocButtonProps) => {
<Text $withThemeInherited $size="md" $weight="500">
{t('New')}
</Text>
</Button>
</ButtonLink>
{isDropdownEnabled && <DropdownArrow />}
</>
);
@@ -112,7 +112,7 @@ const DocSearchModalGlobal = ({
'Search documents by name, navigate using arrows, and select a result with Enter.',
)}
</Text>
<Box $position="absolute" $css="top: 4px; right: 4px;">
<Box $position="absolute" $css="top: 8px; right: 8px;">
<ButtonCloseModal
aria-label={t('Close the search modal')}
onClick={modalProps.onClose}
@@ -80,7 +80,7 @@ export const ConfirmationLeaveModal = ({
>
{t('Leave a doc')}
</Text>
<Box $position="absolute" $css="top: 4px; right: 4px;">
<Box $position="absolute" $css="top: 8px; right: 8px;">
<ButtonCloseModal
aria-label={t('Close the leave modal')}
onClick={onClose}