mirror of
https://github.com/suitenumerique/docs.git
synced 2026-05-06 15:12:27 +02:00
Compare commits
32 Commits
v4.8.0-psy
...
accessibil
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4d968eff3 | ||
|
|
12f7c8b371 | ||
|
|
70e3e9e7d4 | ||
|
|
e0426ca803 | ||
|
|
a01790dc0b | ||
|
|
e14622ff66 | ||
|
|
75fd994a5b | ||
|
|
239933aef3 | ||
|
|
95c06d68cb | ||
|
|
0d12278474 | ||
|
|
8ce13075c1 | ||
|
|
225f5a8abb | ||
|
|
71a8765770 | ||
|
|
9186a101ec | ||
|
|
74e816c479 | ||
|
|
5d9eb2d694 | ||
|
|
33e168ba17 | ||
|
|
f1c0f6bba0 | ||
|
|
8f4fd15495 | ||
|
|
cc6ce4a945 | ||
|
|
044c8f0bbd | ||
|
|
7e62dcf1fc | ||
|
|
e7742d914c | ||
|
|
b1a5c17d75 | ||
|
|
bd68396e52 | ||
|
|
9f1ae58ead | ||
|
|
f597549e96 | ||
|
|
847e120d67 | ||
|
|
6682ddafff | ||
|
|
2f23404003 | ||
|
|
6d4210d34b | ||
|
|
85f7598be8 |
3853
package-lock.json
generated
Normal file
3853
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
6
package.json
Normal file
6
package.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@blocknote/core": "^0.23.4",
|
||||
"next": "^15.1.7"
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { FocusScope } from '@react-aria/focus';
|
||||
import {
|
||||
PropsWithChildren,
|
||||
ReactNode,
|
||||
@@ -6,16 +7,16 @@ import {
|
||||
useState,
|
||||
} from 'react';
|
||||
import { Button, Popover } from 'react-aria-components';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledPopover = styled(Popover)`
|
||||
background-color: white;
|
||||
border-radius: 4px;
|
||||
box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.1);
|
||||
|
||||
border: 1px solid #dddddd;
|
||||
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
padding: 1rem;
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
@@ -29,6 +30,10 @@ const StyledButton = styled(Button)`
|
||||
font-size: 0.938rem;
|
||||
padding: 0;
|
||||
text-wrap: nowrap;
|
||||
|
||||
&:focus-within {
|
||||
outline: 2px solid #007bff;
|
||||
}
|
||||
`;
|
||||
|
||||
export interface DropButtonProps {
|
||||
@@ -43,15 +48,17 @@ export const DropButton = ({
|
||||
isOpen = false,
|
||||
onOpenChange,
|
||||
children,
|
||||
label,
|
||||
}: PropsWithChildren<DropButtonProps>) => {
|
||||
const { t } = useTranslation();
|
||||
const [isLocalOpen, setIsLocalOpen] = useState(isOpen);
|
||||
|
||||
const triggerRef = useRef(null);
|
||||
const triggerRef = useRef<HTMLButtonElement>(null);
|
||||
const firstFocusableRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setIsLocalOpen(isOpen);
|
||||
}, [isOpen]);
|
||||
if (isLocalOpen && firstFocusableRef.current) {
|
||||
firstFocusableRef.current.focus();
|
||||
}
|
||||
}, [isLocalOpen]);
|
||||
|
||||
const onOpenChangeHandler = (isOpen: boolean) => {
|
||||
setIsLocalOpen(isOpen);
|
||||
@@ -63,18 +70,30 @@ export const DropButton = ({
|
||||
<StyledButton
|
||||
ref={triggerRef}
|
||||
onPress={() => onOpenChangeHandler(true)}
|
||||
aria-label={label}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={isLocalOpen}
|
||||
aria-label={t('Open the document options')}
|
||||
>
|
||||
{button}
|
||||
<span aria-hidden="true">{button}</span>
|
||||
</StyledButton>
|
||||
|
||||
<StyledPopover
|
||||
triggerRef={triggerRef}
|
||||
isOpen={isLocalOpen}
|
||||
onOpenChange={onOpenChangeHandler}
|
||||
>
|
||||
{children}
|
||||
</StyledPopover>
|
||||
{isLocalOpen && (
|
||||
<StyledPopover
|
||||
triggerRef={triggerRef}
|
||||
isOpen={isLocalOpen}
|
||||
onOpenChange={onOpenChangeHandler}
|
||||
>
|
||||
<FocusScope contain restoreFocus>
|
||||
{children}
|
||||
<button
|
||||
ref={firstFocusableRef}
|
||||
onClick={() => setIsLocalOpen(false)}
|
||||
>
|
||||
{t('Close the modal')}
|
||||
</button>
|
||||
</FocusScope>
|
||||
</StyledPopover>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//import { t } from 'i18next';
|
||||
import { PropsWithChildren, useState } from 'react';
|
||||
import { css } from 'styled-components';
|
||||
|
||||
|
||||
@@ -47,7 +47,11 @@ export const QuickSearchInput = ({
|
||||
$gap={spacing['2xs']}
|
||||
$padding={{ all: 'base' }}
|
||||
>
|
||||
{!loading && <Icon iconName="search" $variation="600" />}
|
||||
{!loading && (
|
||||
<span aria-hidden="true">
|
||||
<Icon iconName="search" $variation="600" />
|
||||
</span>
|
||||
)}
|
||||
{loading && (
|
||||
<div>
|
||||
<Loader size="small" />
|
||||
|
||||
@@ -203,6 +203,7 @@ input:-webkit-autofill:focus {
|
||||
|
||||
.c__select__wrapper .c__select__inner__actions__open:focus {
|
||||
outline: none;
|
||||
|
||||
}
|
||||
|
||||
.c__select__wrapper .labelled-box__label.c__offscreen {
|
||||
@@ -605,3 +606,31 @@ input:-webkit-autofill:focus {
|
||||
.c__tooltip {
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
/**
|
||||
* lecture ou non des icons
|
||||
*/
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
[data-icon]:before {
|
||||
font-family: 'Material Icons';
|
||||
content: attr(data-icon);
|
||||
}
|
||||
|
||||
button:focus {
|
||||
background-color: var(
|
||||
--c--components--button--primary-text--background--color-hover
|
||||
);
|
||||
border-radius: var(--c--components--button--border-radius--focus);
|
||||
box-shadow: 0 0 0 2px var(--c--theme--colors--primary-400)
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
|
||||
import {
|
||||
Tooltip,
|
||||
VariantType,
|
||||
@@ -114,7 +112,6 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
|
||||
defaultValue={isUntitled ? undefined : titleDisplay}
|
||||
onKeyDownCapture={handleKeyDown}
|
||||
suppressContentEditableWarning={true}
|
||||
aria-label="doc title input"
|
||||
onBlurCapture={(event) =>
|
||||
handleTitleSubmit(event.target.textContent || '')
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ export const DocSearchModal = ({ ...modalProps }: DocSearchModalProps) => {
|
||||
return {
|
||||
groupName: docs.length > 0 ? t('Select a document') : '',
|
||||
elements: search ? docs : [],
|
||||
emptyString: t('No document found'),
|
||||
//emptyString: t('No document found'),
|
||||
endActions: hasNextPage
|
||||
? [{ content: <InView onChange={() => void fetchNextPage()} /> }]
|
||||
: [],
|
||||
@@ -96,6 +96,20 @@ export const DocSearchModal = ({ ...modalProps }: DocSearchModalProps) => {
|
||||
renderElement={(doc) => <DocSearchItem doc={doc} />}
|
||||
/>
|
||||
)}
|
||||
{/* Message accessible pour les résultats vides */}
|
||||
{search && docsData.elements.length === 0 && (
|
||||
<p
|
||||
role="alert"
|
||||
aria-live="polite"
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
marginTop: '1rem',
|
||||
color: '#666',
|
||||
}}
|
||||
>
|
||||
{t('No document found')}
|
||||
</p>
|
||||
)}
|
||||
</Box>
|
||||
</QuickSearch>
|
||||
</Box>
|
||||
|
||||
@@ -49,7 +49,6 @@ export const DocsGridActions = ({
|
||||
callback: () => {
|
||||
openShareModal?.();
|
||||
},
|
||||
|
||||
testId: `docs-grid-actions-share-${doc.id}`,
|
||||
},
|
||||
|
||||
@@ -70,6 +69,7 @@ export const DocsGridActions = ({
|
||||
iconName="more_horiz"
|
||||
$theme="primary"
|
||||
$variation="600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</DropdownMenu>
|
||||
|
||||
|
||||
@@ -54,6 +54,9 @@ export const DocsGridItem = ({ doc }: DocsGridItemProps) => {
|
||||
$css={css`
|
||||
flex: ${flexLeft};
|
||||
align-items: center;
|
||||
&:focus {
|
||||
outline: 2px solidrgb(33, 34, 82);
|
||||
}
|
||||
`}
|
||||
href={`/docs/${doc.id}`}
|
||||
>
|
||||
@@ -79,7 +82,11 @@ export const DocsGridItem = ({ doc }: DocsGridItemProps) => {
|
||||
>
|
||||
<Tooltip
|
||||
content={
|
||||
<Text $textAlign="center" $variation="000">
|
||||
<Text
|
||||
id={`tooltip-access-${doc.id}`}
|
||||
$textAlign="center"
|
||||
$variation="000"
|
||||
>
|
||||
{isPublic
|
||||
? t('Accessible to anyone')
|
||||
: t('Accessible to authenticated users')}
|
||||
@@ -87,12 +94,17 @@ export const DocsGridItem = ({ doc }: DocsGridItemProps) => {
|
||||
}
|
||||
placement="top"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-labelledby={`tooltip-access-${doc.id}`}
|
||||
>
|
||||
<Icon
|
||||
$theme="greyscale"
|
||||
$variation="600"
|
||||
$size="14px"
|
||||
iconName={isPublic ? 'public' : 'vpn_lock'}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
@@ -9,6 +9,7 @@ type Props = {
|
||||
doc: Doc;
|
||||
handleClick: () => void;
|
||||
};
|
||||
|
||||
export const DocsGridItemSharedButton = ({ doc, handleClick }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const sharedCount = doc.nb_accesses;
|
||||
@@ -18,11 +19,15 @@ export const DocsGridItemSharedButton = ({ doc, handleClick }: Props) => {
|
||||
return <Box $minWidth="50px"> </Box>;
|
||||
}
|
||||
|
||||
const tooltipContent = t('Shared with {{count}} users', {
|
||||
count: sharedCount,
|
||||
});
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
content={
|
||||
<Text $textAlign="center" $variation="000">
|
||||
{t('Shared with {{count}} users', { count: sharedCount })}
|
||||
<Text $variation="000" $textAlign="center">
|
||||
{tooltipContent}
|
||||
</Text>
|
||||
}
|
||||
placement="top"
|
||||
@@ -36,8 +41,14 @@ export const DocsGridItemSharedButton = ({ doc, handleClick }: Props) => {
|
||||
}}
|
||||
color="tertiary"
|
||||
size="nano"
|
||||
icon={<Icon $variation="800" $theme="primary" iconName="group" />}
|
||||
aria-label={tooltipContent} // Lecture directe pour les lecteurs d'écran
|
||||
>
|
||||
<Icon
|
||||
$variation="800"
|
||||
$theme="primary"
|
||||
iconName="group"
|
||||
aria-hidden="true" // Empêche la lecture de l'icône
|
||||
/>
|
||||
{sharedCount}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
@@ -47,9 +47,9 @@ export const SimpleDocItem = ({
|
||||
`}
|
||||
>
|
||||
{isPinned ? (
|
||||
<PinnedDocumentIcon aria-label={t('Pin document icon')} />
|
||||
<PinnedDocumentIcon aria-label={t('Pinned document.')} />
|
||||
) : (
|
||||
<SimpleFileIcon aria-label={t('Simple document icon')} />
|
||||
<SimpleFileIcon aria-label="" />
|
||||
)}
|
||||
</Box>
|
||||
<Box $justify="center">
|
||||
|
||||
@@ -54,6 +54,7 @@ export const LanguagePicker = () => {
|
||||
$theme="primary"
|
||||
$weight="bold"
|
||||
$variation="800"
|
||||
aria-hidden="true"
|
||||
>
|
||||
translate
|
||||
</Text>
|
||||
|
||||
@@ -52,6 +52,7 @@ export const LeftPanelTargetFilters = () => {
|
||||
|
||||
return (
|
||||
<Box
|
||||
role="tablist"
|
||||
$justify="center"
|
||||
$padding={{ horizontal: 'sm' }}
|
||||
$gap={spacing['2xs']}
|
||||
@@ -61,7 +62,7 @@ export const LeftPanelTargetFilters = () => {
|
||||
|
||||
return (
|
||||
<BoxButton
|
||||
aria-label={query.label}
|
||||
role="tab"
|
||||
key={query.label}
|
||||
onClick={() => onSelectQuery(query.targetQuery)}
|
||||
$direction="row"
|
||||
@@ -85,6 +86,7 @@ export const LeftPanelTargetFilters = () => {
|
||||
<Icon
|
||||
$variation={isActive ? '1000' : '700'}
|
||||
iconName={query.icon}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<Text $variation={isActive ? '1000' : '700'} $size="sm">
|
||||
{query.label}
|
||||
|
||||
@@ -50,8 +50,11 @@ export const LeftPanelHeader = ({ children }: PropsWithChildren) => {
|
||||
onClick={goToHome}
|
||||
size="medium"
|
||||
color="tertiary-text"
|
||||
aria-label={t('Back to home page')}
|
||||
icon={
|
||||
<Icon $variation="800" $theme="primary" iconName="house" />
|
||||
<span aria-hidden="true">
|
||||
<Icon $variation="800" $theme="primary" iconName="house" />
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
{authenticated && (
|
||||
@@ -59,8 +62,15 @@ export const LeftPanelHeader = ({ children }: PropsWithChildren) => {
|
||||
onClick={searchModal.open}
|
||||
size="medium"
|
||||
color="tertiary-text"
|
||||
aria-label={t('Search')}
|
||||
icon={
|
||||
<Icon $variation="800" $theme="primary" iconName="search" />
|
||||
<span aria-hidden="true">
|
||||
<Icon
|
||||
$variation="800"
|
||||
$theme="primary"
|
||||
iconName="search"
|
||||
/>
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
"AI seems busy! Please try again.": "KI scheint beschäftigt! Bitte versuchen Sie es erneut.",
|
||||
"Accessibility": "Barrierefreiheit",
|
||||
"Accessibility statement": "Erklärung zur Barrierefreiheit",
|
||||
"Accessible to anyone": "Für jeden zugänglich",
|
||||
"Accessible to authenticated users": "Für authentifizierte Benutzer zugänglich",
|
||||
"Add": "Hinzufügen",
|
||||
"Address:": "Anschrift:",
|
||||
"All docs": "Alle Dokumente",
|
||||
@@ -92,6 +94,7 @@
|
||||
"Pending invitations": "Ausstehende Einladungen",
|
||||
"Personal data and cookies": "Personenbezogene Daten und Cookies",
|
||||
"Pin": "Anheften",
|
||||
"Pinned document.": "Angeheftetes Dokument",
|
||||
"Pinned documents": "Angepinnte Dokumente",
|
||||
"Private": "Privat",
|
||||
"Public": "Öffentlich",
|
||||
@@ -118,6 +121,7 @@
|
||||
"Share with {{count}} users_many": "Teilen mit {{count}} Benutzern",
|
||||
"Share with {{count}} users_one": "Teilen mit {{count}} Benutzern",
|
||||
"Share with {{count}} users_other": "Teilen mit {{count}} Benutzern",
|
||||
"Shared with {{count}} users": "Mit {{count}} Benutzern geteilt",
|
||||
"Shared with me": "Mit mir geteilt",
|
||||
"Something bad happens, please retry.": "Etwas ist schiefgelaufen, bitte versuchen Sie es erneut.",
|
||||
"Stéphanie Schaer: Interministerial Digital Director (DINUM).": "Stéphanie Schaer: Interministerielle Digitaldirektorin (DINUM).",
|
||||
@@ -133,6 +137,7 @@
|
||||
"This site places a small text file (a \"cookie\") on your computer when you visit it.": "Diese Website platziert beim Besuch auf Ihrem Computer eine kleine Textdatei (ein \"Cookie\").",
|
||||
"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.": "Dies schützt Ihre Privatsphäre, verhindert jedoch auch, dass der Eigentümer aus Ihren Aktionen lernt und eine bessere Erfahrung für Sie und andere Benutzer schafft.",
|
||||
"Too many requests. Please wait 60 seconds.": "Zu viele Anfragen. Bitte warten Sie 60 Sekunden.",
|
||||
"Translate": "Übersetzen",
|
||||
"Type a name or email": "Geben Sie einen Namen oder eine E-Mail-Adresse ein",
|
||||
"Type the name of a document": "Geben Sie den Namen eines Dokuments ein",
|
||||
"Unless otherwise stated, all content on this site is under": "Sofern nicht anders angegeben, steht der gesamte Inhalt dieser Website unter",
|
||||
@@ -255,7 +260,7 @@
|
||||
"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!": "C'est vrai, vous n'avez pas à cliquer sur un bloc qui couvre la moitié de la page pour dire que vous acceptez le placement de cookies — même si vous ne savez pas ce que cela signifie !",
|
||||
"Language": "Langue",
|
||||
"Last update: {{update}}": "Dernière mise à jour : {{update}}",
|
||||
"Legal Notice": "Mentions Legales",
|
||||
"Legal Notice": "Mentions Légales",
|
||||
"Legal notice": "Mention légale",
|
||||
"Link Copied !": "Lien copié !",
|
||||
"Link parameters": "Paramètres du lien",
|
||||
@@ -289,6 +294,7 @@
|
||||
"Personal data and cookies": "Données personnelles et cookies",
|
||||
"Pin": "Épingler",
|
||||
"Pin document icon": "Icône épingler un document",
|
||||
"Pinned document.": "Document épinglé",
|
||||
"Pinned documents": "Documents épinglés",
|
||||
"Private": "Privé",
|
||||
"ProConnect Image": "Image ProConnect",
|
||||
@@ -317,6 +323,7 @@
|
||||
"Share with {{count}} users_many": "Partager avec {{count}} utilisateurs",
|
||||
"Share with {{count}} users_one": "Partager avec {{count}} utilisateur",
|
||||
"Share with {{count}} users_other": "Partager avec {{count}} utilisateurs",
|
||||
"Shared with {{count}} users": "Partagé avec {{count}} utilisateurs",
|
||||
"Shared with me": "Partagés avec moi",
|
||||
"Shared with {{count}} users_many": "Partager avec {{count}} utilisateurs",
|
||||
"Shared with {{count}} users_one": "Partager avec {{count}} utilisateur",
|
||||
@@ -342,6 +349,7 @@
|
||||
"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.": "Cela protégera votre vie privée, mais empêchera également le propriétaire d'apprendre de vos actions et de créer une meilleure expérience pour vous et les autres utilisateurs.",
|
||||
"To facilitate the circulation of documents, Docs allows you to export your content to the most common formats: PDF, Word or OpenDocument.": "Pour faciliter la circulation des documents, Docs permet d'exporter vos contenus vers les formats les plus courants : PDF, Word ou OpenDocument.",
|
||||
"Too many requests. Please wait 60 seconds.": "Trop de demandes. Veuillez patienter 60 secondes.",
|
||||
"Translate": "Traduire",
|
||||
"Type a name or email": "Tapez un nom ou un email",
|
||||
"Type the name of a document": "Tapez le nom d'un document",
|
||||
"Unless otherwise stated, all content on this site is under": "Sauf mention contraire, tout le contenu de ce site est sous",
|
||||
|
||||
Reference in New Issue
Block a user