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)