diff --git a/web/app/components/datasets/documents/detail/__tests__/index.spec.tsx b/web/app/components/datasets/documents/detail/__tests__/index.spec.tsx index 7d90eecc8e..f01a64e34e 100644 --- a/web/app/components/datasets/documents/detail/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/detail/__tests__/index.spec.tsx @@ -296,6 +296,16 @@ describe('DocumentDetail', () => { expect(screen.queryByTestId('metadata')).not.toBeInTheDocument() }) + it('should expose aria semantics for metadata toggle button', () => { + render() + const toggle = screen.getByTestId('document-detail-metadata-toggle') + expect(toggle).toHaveAttribute('aria-label') + expect(toggle).toHaveAttribute('aria-pressed', 'true') + + fireEvent.click(toggle) + expect(toggle).toHaveAttribute('aria-pressed', 'false') + }) + it('should pass correct props to Metadata', () => { render() const metadata = screen.getByTestId('metadata') @@ -311,6 +321,11 @@ describe('DocumentDetail', () => { expect(mocks.push).toHaveBeenCalledWith('/datasets/ds-1/documents') }) + it('should expose aria label for back button', () => { + render() + expect(screen.getByTestId('document-detail-back-button')).toHaveAttribute('aria-label') + }) + it('should preserve query params when navigating back', () => { mocks.state.searchParams = 'page=2&status=active' render() diff --git a/web/app/components/datasets/documents/detail/index.tsx b/web/app/components/datasets/documents/detail/index.tsx index 64ebc83222..b6842605c6 100644 --- a/web/app/components/datasets/documents/detail/index.tsx +++ b/web/app/components/datasets/documents/detail/index.tsx @@ -149,6 +149,11 @@ const DocumentDetail: FC = ({ datasetId, documentId }) => { return chunkMode === ChunkingMode.parentChild && parentMode === 'full-doc' }, [documentDetail?.doc_form, parentMode]) + const backButtonLabel = t('operation.back', { ns: 'common' }) + const metadataToggleLabel = `${showMetadata + ? t('operation.close', { ns: 'common' }) + : t('operation.view', { ns: 'common' })} ${t('metadata.title', { ns: 'datasetDocuments' })}` + return ( = ({ datasetId, documentId }) => { = ({ datasetId, documentId }) => { diff --git a/web/app/components/datasets/documents/hooks/__tests__/use-document-list-query-state.spec.tsx b/web/app/components/datasets/documents/hooks/__tests__/use-document-list-query-state.spec.tsx index 207508173c..ed41b7c216 100644 --- a/web/app/components/datasets/documents/hooks/__tests__/use-document-list-query-state.spec.tsx +++ b/web/app/components/datasets/documents/hooks/__tests__/use-document-list-query-state.spec.tsx @@ -320,6 +320,32 @@ describe('useDocumentListQueryState', () => { const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] expect(update.searchParams.get('limit')).toBe('25') }) + + it('should sanitize invalid page to default and omit page from URL', async () => { + const { result, onUrlUpdate } = renderWithAdapter() + + act(() => { + result.current.updateQuery({ page: -1 }) + }) + + await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + expect(update.searchParams.has('page')).toBe(false) + expect(result.current.query.page).toBe(1) + }) + + it('should sanitize invalid limit to default and omit limit from URL', async () => { + const { result, onUrlUpdate } = renderWithAdapter() + + act(() => { + result.current.updateQuery({ limit: 999 }) + }) + + await waitFor(() => expect(onUrlUpdate).toHaveBeenCalled()) + const update = onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1][0] + expect(update.searchParams.has('limit')).toBe(false) + expect(result.current.query.limit).toBe(10) + }) }) describe('resetQuery', () => { diff --git a/web/app/components/datasets/documents/hooks/use-document-list-query-state.ts b/web/app/components/datasets/documents/hooks/use-document-list-query-state.ts index 035ed012e9..62b85c9911 100644 --- a/web/app/components/datasets/documents/hooks/use-document-list-query-state.ts +++ b/web/app/components/datasets/documents/hooks/use-document-list-query-state.ts @@ -12,6 +12,14 @@ const sanitizeSortValue = (value?: string | null): SortType => { return (ALLOWED_SORT_VALUES.includes(value as SortType) ? value : '-created_at') as SortType } +const sanitizePageValue = (value: number): number => { + return Number.isInteger(value) && value > 0 ? value : 1 +} + +const sanitizeLimitValue = (value: number): number => { + return Number.isInteger(value) && value > 0 && value <= 100 ? value : 10 +} + export type DocumentListQuery = { page: number limit: number @@ -76,6 +84,10 @@ function useDocumentListQueryState() { const updateQuery = useCallback((updates: Partial) => { const patch = { ...updates } + if ('page' in patch && patch.page !== undefined) + patch.page = sanitizePageValue(patch.page) + if ('limit' in patch && patch.limit !== undefined) + patch.limit = sanitizeLimitValue(patch.limit) if ('status' in patch) patch.status = sanitizeStatusValue(patch.status) if ('sort' in patch)