Compare commits

..

4 Commits

Author SHA1 Message Date
Anthony LC c89909bd1a save 2026-03-19 14:18:01 +01:00
Anthony LC 9991820cb1 🔊(CHANGELOG) fix entries changelog
The changelog was not updated correctly.
By not updating correctly, the changelog was not
showing the correct entries for the release,
leading to a patch release instead of a minor
release.
2026-03-19 13:36:48 +01:00
Anthony LC 2801ece358 ️(frontend) change aria-label for help menu button
The help menu button's aria-label was
previously "Open onboarding menu", which was not
accurate and could be confusing for screen reader
users. This commit updates the aria-label to
"Open help menu" to better reflect the button's
purpose and improve accessibility.
2026-03-19 13:31:03 +01:00
Anthony LC 0b37996899 💫(frontend) fix the help button to the bottom in tree
The tree take a bit of time to load, during this
time the help button was not at the bottom of
the left panel. To fix this issue, we addded a
skeleton for the tree in wait for the tree to
load, by doing this, the help button
is always at the bottom.
2026-03-19 13:28:22 +01:00
16 changed files with 190 additions and 138 deletions
+17 -5
View File
@@ -6,12 +6,24 @@ and this project adheres to
## [Unreleased]
### Changed
- 💫(frontend) fix the help button to the bottom in tree #2073
## [v4.8.2] - 2026-03-19
### Added
- ✨(backend) add resource server api #1923
- ✨(frontend) activate Find search #1834
- ✨ handle searching on subdocuments #1834
- ✨(backend) add search feature flags #1897
### Changed
- ♿️(frontend) ensure doc title is h1 for accessibility #2006
- ♿️(frontend) add nb accesses in share button aria-label #2017
- ✨(backend) improve fallback logic on search endpoint #1834
### Fixed
@@ -25,6 +37,11 @@ and this project adheres to
- 🐛(backend) stop using add_sibling method to create sandbox document #2084
- 🐛(backend) duplicate a document as last-sibling #2084
### Removed
- 🔥(api) remove `documents/<document_id>/descendants/` endpoint #1834
- 🔥(api) remove pagination on `documents/search/` endpoint #1834
## [v4.8.1] - 2026-03-17
### Added
@@ -41,7 +58,6 @@ and this project adheres to
- ✨(backend) add a is_first_connection flag to the User model #1938
- ✨(frontend) add onboarding modal with help menu button #1868
- ✨(backend) add resource server api #1923
### Changed
@@ -127,16 +143,12 @@ and this project adheres to
- ✨(frontend) Add stat for Crisp #1824
- ✨(auth) add silent login #1690
- 🔧(project) add DJANGO_EMAIL_URL_APP environment variable #1825
- ✨(frontend) activate Find search #1834
- ✨ handle searching on subdocuments #1834
- ✨(backend) add search feature flags #1897
### Changed
- ♿(frontend) improve accessibility:
- ♿️(frontend) fix subdoc opening and emoji pick focus #1745
- ✨(backend) add field for button label in email template #1817
- ✨(backend) improve fallback logic on search endpoint #1834
### Fixed
@@ -299,7 +299,7 @@ test.describe('Doc Tree', () => {
await page.keyboard.press('Tab');
await expect(page.getByLabel('Open onboarding menu')).toBeFocused();
await expect(page.getByLabel('Open help menu')).toBeFocused();
await page.keyboard.press('Tab');
@@ -309,7 +309,7 @@ test.describe('Doc Tree', () => {
await page.keyboard.press('Shift+Tab');
await expect(page.getByLabel('Open onboarding menu')).toBeFocused();
await expect(page.getByLabel('Open help menu')).toBeFocused();
await page.keyboard.press('Shift+Tab');
@@ -27,7 +27,7 @@ test.describe('Help feature', () => {
await expect(page.getByRole('button', { name: 'New doc' })).toBeVisible();
await expect(
page.getByRole('button', { name: 'Open onboarding menu' }),
page.getByRole('button', { name: 'Open help menu' }),
).toBeHidden();
});
@@ -43,7 +43,7 @@ test.describe('Help feature', () => {
},
});
await page.getByRole('button', { name: 'Open onboarding menu' }).click();
await page.getByRole('button', { name: 'Open help menu' }).click();
await getMenuItem(page, 'Onboarding').click();
@@ -87,7 +87,7 @@ test.describe('Help feature', () => {
});
test('closes modal with Skip button', async ({ page }) => {
await page.getByRole('button', { name: 'Open onboarding menu' }).click();
await page.getByRole('button', { name: 'Open help menu' }).click();
await getMenuItem(page, 'Onboarding').click();
const modal = page.getByTestId('onboarding-modal');
@@ -107,9 +107,7 @@ test.describe('Help feature', () => {
// switch to french
await waitForLanguageSwitch(page, TestLanguage.French);
await page
.getByRole('button', { name: "Ouvrir le menu d'embarquement" })
.click();
await page.getByRole('button', { name: "Ouvrir le menu d'aide" }).click();
await getMenuItem(page, 'Premiers pas').click();
@@ -46,7 +46,7 @@ export const FilterDropdown = ({
$direction="row"
$align="center"
>
<Text $weight={400} $variation="tertiary" $theme="neutral">
<Text $weight={400} $variation="tertiary" $theme="neutral" $size="sm">
{selectedOption?.label ?? options[0].label}
</Text>
<Icon
@@ -43,7 +43,7 @@ export const QuickSearchInput = ({
$align="center"
className="quick-search-input"
$gap={spacingsTokens['2xs']}
$padding={{ horizontal: 'base', vertical: 'xxs' }}
$padding={{ horizontal: 'base', vertical: 'xs' }}
>
<Icon iconName="search" $variation="secondary" aria-hidden="true" />
<Command.Input
@@ -58,7 +58,7 @@ export const QuickSearchInput = ({
data-testid="quick-search-input"
/>
</Box>
{separator && <HorizontalSeparator $margin={{ top: 'base' }} />}
{separator && <HorizontalSeparator $margin={{ top: '2xs' }} />}
</>
);
};
@@ -16,9 +16,10 @@ export const QuickSearchStyle = createGlobalStyle`
}
[cmdk-input] {
font-family: var(--c--globals--font--families--base);
border: none;
width: 100%;
font-size: 17px;
font-size: 16px;
background: white;
outline: none;
color: var(--c--contextuals--content--semantic--neutral--primary);
@@ -38,19 +38,19 @@ export const DocSearchFilters = ({
$justify="space-between"
$gap="10px"
data-testid="doc-search-filters"
$margin={{ vertical: 'base' }}
$margin={{ vertical: 'sm' }}
>
<Box $direction="row" $align="center" $gap="10px">
<FilterDropdown
selectedValue={values?.target}
options={[
{
label: t('All docs'),
label: t('All documents'),
value: DocSearchTarget.ALL,
callback: () => handleTargetChange(DocSearchTarget.ALL),
},
{
label: t('Current doc'),
label: t('Current document only'),
value: DocSearchTarget.CURRENT,
callback: () => handleTargetChange(DocSearchTarget.CURRENT),
},
@@ -60,37 +60,36 @@ const DocSearchModalGlobal = ({
size={isDesktop ? ModalSize.LARGE : ModalSize.FULL}
hideCloseButton
aria-describedby="doc-search-modal-title"
title={
<>
<Text as="h2" $margin="0" $size="s" $align="flex-start">
{t('Search for a document')}
</Text>
<Box $position="absolute" $css="top: 4px; right: 4px;">
<ButtonCloseModal
aria-label={t('Close the search modal')}
onClick={modalProps.onClose}
size="small"
color="brand"
variant="tertiary"
/>
</Box>
</>
}
>
<Box
$direction="column"
$justify="space-between"
className="--docs--doc-search-modal"
$padding={{ vertical: 'base' }}
$padding={{ bottom: 'base' }}
>
<Text
as="h1"
$margin="0"
id="doc-search-modal-title"
className="sr-only"
>
{t('Search docs')}
</Text>
<Box $position="absolute" $css="top: 4px; right: 4px;">
<ButtonCloseModal
aria-label={t('Close the search modal')}
onClick={modalProps.onClose}
size="small"
color="brand"
variant="tertiary"
/>
</Box>
<QuickSearch
placeholder={t('Type the name of a document')}
loading={loading}
onFilter={handleInputSearch}
>
<Box
$padding={{ horizontal: '10px', vertical: 'base' }}
$padding={{ horizontal: 'sm', vertical: 'base' }}
$height={isDesktop ? '500px' : 'calc(100vh - 68px - 1rem)'}
>
{showFilters && (
@@ -18,6 +18,7 @@ import {
useMoveDoc,
useTrans,
} from '@/docs/doc-management';
import { TreeSkeleton } from '@/features/skeletons/components/TreeSkeleton';
import { CLASS_DOC_TITLE } from '../../doc-header';
import { KEY_DOC_TREE, useDocTree } from '../api/useDocTree';
@@ -257,7 +258,7 @@ export const DocTree = ({ currentDoc }: DocTreeProps) => {
}, [currentDoc, treeContext]);
if (!treeContext || !treeContext.root) {
return null;
return <TreeSkeleton />;
}
return (
@@ -60,7 +60,7 @@ export const HelpMenu = ({
>
<Box $direction="row" $align="center">
<Button
aria-label={t('Open onboarding menu')}
aria-label={t('Open help menu')}
color={colorButton || 'neutral'}
variant="tertiary"
iconPosition="left"
@@ -2,7 +2,6 @@ import { useRouter } from 'next/router';
import { css } from 'styled-components';
import { Box, SeparatedSection } from '@/components';
import { useDocStore } from '@/docs/doc-management';
import { LeftPanelTargetFilters } from './LefPanelTargetFilters';
import { LeftPanelDocContent } from './LeftPanelDocContent';
@@ -12,33 +11,35 @@ export const LeftPanelContent = () => {
const router = useRouter();
const isHome = router.pathname === '/';
const isDoc = router.pathname === '/docs/[id]';
const { currentDoc } = useDocStore();
return (
<>
{isHome && (
<>
<Box
$width="100%"
$css={css`
flex: 0 0 auto;
`}
className="--docs--home-left-panel-content"
>
<SeparatedSection showSeparator={false}>
<LeftPanelTargetFilters />
</SeparatedSection>
</Box>
<Box
$flex={1}
$width="100%"
$css="overflow-y: auto; overflow-x: hidden;"
>
<LeftPanelFavorites />
</Box>
</>
)}
{isDoc && currentDoc && <LeftPanelDocContent doc={currentDoc} />}
</>
);
if (isHome) {
return (
<>
<Box
$width="100%"
$css={css`
flex: 0 0 auto;
`}
className="--docs--home-left-panel-content"
>
<SeparatedSection showSeparator={false}>
<LeftPanelTargetFilters />
</SeparatedSection>
</Box>
<Box
$flex={1}
$width="100%"
$css="overflow-y: auto; overflow-x: hidden;"
>
<LeftPanelFavorites />
</Box>
</>
);
}
if (isDoc) {
return <LeftPanelDocContent />;
}
return null;
};
@@ -1,15 +1,13 @@
import { useTreeContext } from '@gouvfr-lasuite/ui-kit';
import { Box } from '@/components';
import { Doc } from '@/docs/doc-management';
import { Doc, useDocStore } from '@/docs/doc-management';
import { DocTree } from '@/docs/doc-tree/';
import { TreeSkeleton } from '@/features/skeletons/components/TreeSkeleton';
export const LeftPanelDocContent = ({ doc }: { doc: Doc }) => {
export const LeftPanelDocContent = () => {
const tree = useTreeContext<Doc>();
if (!tree) {
return null;
}
const { currentDoc } = useDocStore();
return (
<Box
@@ -18,7 +16,11 @@ export const LeftPanelDocContent = ({ doc }: { doc: Doc }) => {
$css="width: 100%; overflow-y: auto; overflow-x: hidden;"
className="--docs--left-panel-doc-content"
>
<DocTree currentDoc={doc} />
{tree && currentDoc ? (
<DocTree currentDoc={currentDoc} />
) : (
<TreeSkeleton />
)}
</Box>
);
};
@@ -1,69 +1,12 @@
import { css, keyframes } from 'styled-components';
import { Box, BoxType } from '@/components';
import { Box } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { useResponsiveStore } from '@/stores';
const shimmer = keyframes`
0% {
background-position: -1000px 0;
}
100% {
background-position: 1000px 0;
}
`;
type SkeletonLineProps = Partial<BoxType>;
type SkeletonCircleProps = Partial<BoxType>;
import { SkeletonCircle, SkeletonLine } from './SkeletionUI';
export const DocEditorSkeleton = () => {
const { isDesktop } = useResponsiveStore();
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
const SkeletonLine = ({ $css, ...props }: SkeletonLineProps) => {
return (
<Box
$width="100%"
$height="16px"
$css={css`
background: linear-gradient(
90deg,
${colorsTokens['black-050']} 0%,
${colorsTokens['black-100']} 50%,
${colorsTokens['black-050']} 100%
);
background-size: 1000px 100%;
animation: ${shimmer} 2s infinite linear;
border-radius: 4px;
${$css}
`}
{...props}
/>
);
};
const SkeletonCircle = ({ $css, ...props }: SkeletonCircleProps) => {
return (
<Box
$width="32px"
$height="32px"
$css={css`
background: linear-gradient(
90deg,
${colorsTokens['black-050']} 0%,
${colorsTokens['black-100']} 50%,
${colorsTokens['black-050']} 100%
);
background-size: 1000px 100%;
animation: ${shimmer} 2s infinite linear;
border-radius: 50%;
${$css}
`}
{...props}
/>
);
};
const { spacingsTokens } = useCunninghamTheme();
return (
<>
@@ -0,0 +1,65 @@
import { css, keyframes } from 'styled-components';
import { Box, BoxType } from '@/components/Box';
import { useCunninghamTheme } from '@/cunningham/useCunninghamTheme';
const shimmer = keyframes`
0% {
background-position: -1000px 0;
}
100% {
background-position: 1000px 0;
}
`;
type SkeletonLineProps = Partial<BoxType>;
type SkeletonCircleProps = Partial<BoxType>;
export const SkeletonLine = ({ $css, ...props }: SkeletonLineProps) => {
const { colorsTokens } = useCunninghamTheme();
return (
<Box
$width="100%"
$height="16px"
$css={css`
background: linear-gradient(
90deg,
${colorsTokens['black-050']} 0%,
${colorsTokens['black-100']} 50%,
${colorsTokens['black-050']} 100%
);
background-size: 1000px 100%;
animation: ${shimmer} 2s infinite linear;
border-radius: 4px;
${$css}
`}
{...props}
/>
);
};
export const SkeletonCircle = ({ $css, ...props }: SkeletonCircleProps) => {
const { colorsTokens } = useCunninghamTheme();
return (
<Box
$width="32px"
$height="32px"
$css={css`
background: linear-gradient(
90deg,
${colorsTokens['black-050']} 0%,
${colorsTokens['black-100']} 50%,
${colorsTokens['black-050']} 100%
);
background-size: 1000px 100%;
animation: ${shimmer} 2s infinite linear;
border-radius: 50%;
${$css}
`}
{...props}
/>
);
};
@@ -0,0 +1,30 @@
import { Box } from '@/components';
import { SkeletonLine } from './SkeletionUI';
export const TreeSkeleton = () => {
return (
<Box className="--docs--tree-skeleton">
<SkeletonLine
$width="92%"
$height="40px"
$margin={{ left: 'sm', top: 'sm' }}
/>
<SkeletonLine
$width="92%"
$height="30px"
$margin={{ left: 'sm', top: 'sm' }}
/>
<SkeletonLine
$width="92%"
$height="30px"
$margin={{ left: 'sm', top: 'sm' }}
/>
<SkeletonLine
$width="92%"
$height="30px"
$margin={{ left: 'sm', top: 'sm' }}
/>
</Box>
);
};
@@ -905,7 +905,7 @@
"Open Source": "Open Source",
"Open document {{title}}": "Ouvrir le document {{title}}",
"Open document: {{title}}": "Ouvrir le document : {{title}}",
"Open onboarding menu": "Ouvrir le menu d'embarquement",
"Open help menu": "Ouvrir le menu d'aide",
"Open root document": "Ouvrir le document racine",
"Open the document options": "Ouvrir les options du document",
"Open the menu of actions for the document: {{title}}": "Ouvrir le menu des actions du document : {{title}}",