mirror of
https://github.com/langgenius/dify.git
synced 2026-09-08 11:04:27 +08:00
fix(knowledge-fs): isolate document detail route state
This commit is contained in:
parent
a7a16c1335
commit
4dba1bc22e
@ -16,6 +16,7 @@ import { act, fireEvent, screen, waitFor, within } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import { createStore, Provider } from 'jotai'
|
||||
import { NuqsTestingAdapter } from 'nuqs/adapters/testing'
|
||||
import { renderWithNuqs } from '@/test/nuqs-testing'
|
||||
import { DocumentDetailPage } from '../page'
|
||||
|
||||
@ -2555,6 +2556,39 @@ describe('DocumentDetailPage', () => {
|
||||
getBoundingClientRect.mockRestore()
|
||||
})
|
||||
|
||||
it('isolates document route state while client navigation renders two route instances', () => {
|
||||
chunksQuery.data = {
|
||||
pages: [
|
||||
{
|
||||
items: [
|
||||
chunk({ id: 'first', text: 'First chunk' }),
|
||||
chunk({ id: 'target', ordinal: 2, text: 'Target chunk' }),
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
render(
|
||||
<>
|
||||
<NuqsTestingAdapter searchParams="?revision=3&chunk=first">
|
||||
<DocumentDetailPage documentId="document-1" knowledgeSpaceId="space-1" />
|
||||
</NuqsTestingAdapter>
|
||||
<NuqsTestingAdapter searchParams="?revision=3&chunk=target">
|
||||
<DocumentDetailPage documentId="document-1" knowledgeSpaceId="space-1" />
|
||||
</NuqsTestingAdapter>
|
||||
</>,
|
||||
)
|
||||
|
||||
const firstChunkRows = screen.getAllByRole('treeitem', { name: 'First chunk' })
|
||||
const targetChunkRows = screen.getAllByRole('treeitem', { name: 'Target chunk' })
|
||||
expect(firstChunkRows).toHaveLength(2)
|
||||
expect(targetChunkRows).toHaveLength(2)
|
||||
expect(firstChunkRows[0]).toHaveAttribute('aria-selected', 'true')
|
||||
expect(targetChunkRows[0]).toHaveAttribute('aria-selected', 'false')
|
||||
expect(firstChunkRows[1]).toHaveAttribute('aria-selected', 'false')
|
||||
expect(targetChunkRows[1]).toHaveAttribute('aria-selected', 'true')
|
||||
})
|
||||
|
||||
it('distinguishes missing, restricted, and retryable document failures', async () => {
|
||||
const user = userEvent.setup()
|
||||
documentQuery.data = undefined
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { ReactNode } from 'react'
|
||||
import { ScopeProvider } from 'jotai-scope'
|
||||
import { useHydrateAtoms } from 'jotai/utils'
|
||||
import { useQueryStates } from 'nuqs'
|
||||
import {
|
||||
@ -10,6 +11,27 @@ import {
|
||||
documentDetailRequestedRevisionAtom,
|
||||
} from './inputs'
|
||||
import { documentDetailChunkParser, documentDetailRevisionParser } from './location'
|
||||
import { documentWorkflowScopedAtoms } from './workflow'
|
||||
|
||||
function DocumentDetailLocationBridge({
|
||||
children,
|
||||
chunkId,
|
||||
revision,
|
||||
}: {
|
||||
children: ReactNode
|
||||
chunkId: string | null
|
||||
revision: number | null
|
||||
}) {
|
||||
useHydrateAtoms(
|
||||
[
|
||||
[documentDetailRequestedChunkIdAtom, chunkId],
|
||||
[documentDetailRequestedRevisionAtom, revision],
|
||||
],
|
||||
{ dangerouslyForceHydrate: true },
|
||||
)
|
||||
|
||||
return children
|
||||
}
|
||||
|
||||
export function DocumentDetailStateBoundary({
|
||||
children,
|
||||
@ -25,15 +47,24 @@ export function DocumentDetailStateBoundary({
|
||||
revision: documentDetailRevisionParser,
|
||||
})
|
||||
|
||||
useHydrateAtoms(
|
||||
[
|
||||
[documentDetailDocumentIdAtom, documentId],
|
||||
[documentDetailKnowledgeSpaceIdAtom, knowledgeSpaceId],
|
||||
[documentDetailRequestedChunkIdAtom, documentLocation.chunk],
|
||||
[documentDetailRequestedRevisionAtom, documentLocation.revision],
|
||||
],
|
||||
{ dangerouslyForceHydrate: true },
|
||||
return (
|
||||
<ScopeProvider
|
||||
key={`${knowledgeSpaceId}:${documentId}`}
|
||||
atoms={[
|
||||
[documentDetailDocumentIdAtom, documentId],
|
||||
[documentDetailKnowledgeSpaceIdAtom, knowledgeSpaceId],
|
||||
[documentDetailRequestedChunkIdAtom, documentLocation.chunk],
|
||||
[documentDetailRequestedRevisionAtom, documentLocation.revision],
|
||||
...documentWorkflowScopedAtoms,
|
||||
]}
|
||||
name="DocumentDetailPage"
|
||||
>
|
||||
<DocumentDetailLocationBridge
|
||||
chunkId={documentLocation.chunk}
|
||||
revision={documentLocation.revision}
|
||||
>
|
||||
{children}
|
||||
</DocumentDetailLocationBridge>
|
||||
</ScopeProvider>
|
||||
)
|
||||
|
||||
return children
|
||||
}
|
||||
|
||||
@ -67,6 +67,11 @@ function initialWorkflowState(identity: string): DocumentWorkflowState {
|
||||
|
||||
const documentWorkflowStateAtom = atom(initialWorkflowState(''))
|
||||
|
||||
export const documentWorkflowScopedAtoms = [
|
||||
documentHasEditPermissionAtom,
|
||||
documentWorkflowStateAtom,
|
||||
] as const
|
||||
|
||||
function workflowState(get: Getter) {
|
||||
const identity = workflowIdentity(get)
|
||||
const state = get(documentWorkflowStateAtom)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user