From a758254b60825cffce34c97f42e913ec511f9e5f Mon Sep 17 00:00:00 2001 From: Cyril Date: Thu, 30 Oct 2025 10:53:16 +0100 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=A8(frontend)=20improve=20mobile=20UX?= =?UTF-8?q?=20by=20showing=20subdocs=20count?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit helps users notice root documents have children in mobile view Signed-off-by: Cyril --- CHANGELOG.md | 1 + .../docs/doc-header/components/DocHeader.tsx | 50 +----------- .../doc-header/components/DocHeaderInfo.tsx | 80 +++++++++++++++++++ 3 files changed, 84 insertions(+), 47 deletions(-) create mode 100644 src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeaderInfo.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 15c9f786cb..ddb0c2152b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to - ✨(frontend) create skeleton component for DocEditor #1491 - ✨(frontend) add an EmojiPicker in the document tree and title #1381 - ✨(frontend) ajustable left panel #1456 +- ✨(frontend) improve mobile UX by showing subdocs count #1540 ### Changed diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx index 90a2a8034a..8e3b0cd334 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeader.tsx @@ -1,23 +1,20 @@ import { useTranslation } from 'react-i18next'; -import { Box, HorizontalSeparator, Text } from '@/components'; -import { useConfig } from '@/core'; +import { Box, HorizontalSeparator } from '@/components'; import { useCunninghamTheme } from '@/cunningham'; import { Doc, LinkReach, - Role, getDocLinkReach, useIsCollaborativeEditable, - useTrans, } from '@/docs/doc-management'; -import { useDate } from '@/hook'; import { useResponsiveStore } from '@/stores'; import { AlertNetwork } from './AlertNetwork'; import { AlertPublic } from './AlertPublic'; import { AlertRestore } from './AlertRestore'; import { BoutonShare } from './BoutonShare'; +import { DocHeaderInfo } from './DocHeaderInfo'; import { DocTitle } from './DocTitle'; import { DocToolBox } from './DocToolBox'; @@ -29,27 +26,11 @@ export const DocHeader = ({ doc }: DocHeaderProps) => { const { spacingsTokens } = useCunninghamTheme(); const { isDesktop } = useResponsiveStore(); const { t } = useTranslation(); - const { transRole } = useTrans(); const { isEditable } = useIsCollaborativeEditable(doc); const docIsPublic = getDocLinkReach(doc) === LinkReach.PUBLIC; const docIsAuth = getDocLinkReach(doc) === LinkReach.AUTHENTICATED; - const { relativeDate, calculateDaysLeft } = useDate(); - const { data: config } = useConfig(); const isDeletedDoc = !!doc.deleted_at; - let dateToDisplay = t('Last update: {{update}}', { - update: relativeDate(doc.updated_at), - }); - - if (config?.TRASHBIN_CUTOFF_DAYS && doc.deleted_at) { - const daysLeft = calculateDaysLeft( - doc.deleted_at, - config.TRASHBIN_CUTOFF_DAYS, - ); - - dateToDisplay = `${t('Days remaining:')} ${daysLeft} ${t('days', { count: daysLeft })}`; - } - return ( <> { > - - {isDesktop && ( - <> - - {transRole( - isEditable - ? doc.user_role || doc.link_role - : Role.READER, - )} -  ·  - - - {dateToDisplay} - - - )} - {!isDesktop && ( - - {dateToDisplay} - - )} + {!isDeletedDoc && } diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeaderInfo.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeaderInfo.tsx new file mode 100644 index 0000000000..998dce3faf --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeaderInfo.tsx @@ -0,0 +1,80 @@ +import { t } from 'i18next'; +import React from 'react'; + +import { Text } from '@/components'; +import { useConfig } from '@/core'; +import { useDate } from '@/hook'; +import { useResponsiveStore } from '@/stores'; + +import { + Doc, + Role, + useIsCollaborativeEditable, + useTrans, +} from '../../doc-management'; + +interface DocHeaderInfoProps { + doc: Doc; +} + +export const DocHeaderInfo = ({ doc }: DocHeaderInfoProps) => { + const { isDesktop } = useResponsiveStore(); + const { transRole } = useTrans(); + const { isEditable } = useIsCollaborativeEditable(doc); + const { relativeDate, calculateDaysLeft } = useDate(); + const { data: config } = useConfig(); + + const childrenCount = doc.numchild ?? 0; + + const relativeOnly = relativeDate(doc.updated_at); + + let dateToDisplay = t('Last update: {{update}}', { + update: relativeOnly, + }); + + if (config?.TRASHBIN_CUTOFF_DAYS && doc.deleted_at) { + const daysLeft = calculateDaysLeft( + doc.deleted_at, + config.TRASHBIN_CUTOFF_DAYS, + ); + + dateToDisplay = `${t('Days remaining:')} ${daysLeft} ${t('days', { count: daysLeft })}`; + } + + const hasChildren = childrenCount > 0; + + if (isDesktop) { + return ( + <> + + {transRole(isEditable ? doc.user_role || doc.link_role : Role.READER)} +  ·  + + + {dateToDisplay} + + + ); + } + + return ( + <> + + {hasChildren ? relativeOnly : dateToDisplay} + + {hasChildren && ( + +  •  + {t('Contains {{count}} sub-documents', { + count: childrenCount, + })} + + )} + + ); +}; From 82a0c1a7704e25283574b3e8854ec04a93f05180 Mon Sep 17 00:00:00 2001 From: Cyril Date: Wed, 12 Nov 2025 11:23:03 +0100 Subject: [PATCH 2/2] =?UTF-8?q?=E2=9C=85(frontend)=20add=20unit=20test=20f?= =?UTF-8?q?or=20mobile=20=20rendering=20in=20docheaderinfo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ensures numchild count is displayed correctly on mobile interface Signed-off-by: Cyril --- CHANGELOG.md | 2 +- .../__tests__/DocHeaderInfo.spec.tsx | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocHeaderInfo.spec.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index ddb0c2152b..a43001980c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to ### Added - ✨(frontend) enable ODT export for documents #1524 +- ✨(frontend) improve mobile UX by showing subdocs count #1540 ### Fixed @@ -27,7 +28,6 @@ and this project adheres to - ✨(frontend) create skeleton component for DocEditor #1491 - ✨(frontend) add an EmojiPicker in the document tree and title #1381 - ✨(frontend) ajustable left panel #1456 -- ✨(frontend) improve mobile UX by showing subdocs count #1540 ### Changed diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocHeaderInfo.spec.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocHeaderInfo.spec.tsx new file mode 100644 index 0000000000..93f1a49913 --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-header/__tests__/DocHeaderInfo.spec.tsx @@ -0,0 +1,46 @@ +import { render, screen } from '@testing-library/react'; +import React from 'react'; +import { describe, expect, test, vi } from 'vitest'; + +import { AppWrapper } from '@/tests/utils'; + +// Force mobile layout so the children count is rendered +vi.mock('@/stores', () => ({ + useResponsiveStore: () => ({ isDesktop: false }), +})); + +// Provide stable mocks for hooks used by the component +vi.mock('../../doc-management', async () => { + const actual = await vi.importActual('../../doc-management'); + return { + ...actual, + useTrans: () => ({ transRole: vi.fn((r) => String(r)) }), + useIsCollaborativeEditable: () => ({ isEditable: true }), + }; +}); + +vi.mock('@/core', () => ({ + useConfig: () => ({ data: {} }), +})); + +vi.mock('@/hook', () => ({ + useDate: () => ({ + relativeDate: () => 'yesterday', + calculateDaysLeft: () => 5, + }), +})); + +import { DocHeaderInfo } from '../components/DocHeaderInfo'; + +describe('DocHeaderInfo', () => { + test('renders the number of sub-documents when numchild is provided (mobile layout)', () => { + const doc = { + numchild: 3, + updated_at: new Date().toISOString(), + } as any; + + render(, { wrapper: AppWrapper }); + + expect(screen.getByText(/Contains 3 sub-documents/i)).toBeInTheDocument(); + }); +});