Adding working board
This commit is contained in:
+37
-32
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { CaseState, Evidence, WidgetRelation } from './types'
|
||||
import type { CaseState, ExhibitRelation, FolderExhibit } from './types'
|
||||
import {
|
||||
MAX_BOARD_ZOOM,
|
||||
MIN_BOARD_ZOOM,
|
||||
@@ -49,7 +49,7 @@ describe('red thread geometry', () => {
|
||||
})
|
||||
})
|
||||
|
||||
const folder: Evidence = {
|
||||
const folder: FolderExhibit = {
|
||||
id: 'folder-1',
|
||||
type: 'folder',
|
||||
title: 'Folder',
|
||||
@@ -57,7 +57,11 @@ const folder: Evidence = {
|
||||
x: 200,
|
||||
y: 300,
|
||||
width: 260,
|
||||
config: { open: false },
|
||||
height: 166,
|
||||
rotation: 0,
|
||||
zIndex: 1,
|
||||
hidden: false,
|
||||
isOpen: false,
|
||||
}
|
||||
|
||||
const state: CaseState = {
|
||||
@@ -65,11 +69,12 @@ const state: CaseState = {
|
||||
id: 'test-level',
|
||||
title: 'Test',
|
||||
subtitle: '',
|
||||
documents: [],
|
||||
evidence: [folder],
|
||||
exhibits: [folder],
|
||||
relations: [],
|
||||
connections: [],
|
||||
viewport: { x: 10, y: 20, zoom: 0.5 },
|
||||
views: [],
|
||||
revision: 0,
|
||||
}
|
||||
|
||||
describe('board coordinate math', () => {
|
||||
@@ -109,10 +114,10 @@ describe('board coordinate math', () => {
|
||||
it('places new exhibits in deterministic open slots', () => {
|
||||
const preferred = { x: 500, y: 400 }
|
||||
expect(nextOpenBoardPosition([], preferred, { width: 280 })).toEqual(preferred)
|
||||
expect(nextOpenBoardPosition([{ x: 500, y: 400, width: 280 }], preferred, { width: 280 })).toEqual({ x: 826, y: 400 })
|
||||
expect(nextOpenBoardPosition([{ x: 500, y: 400, width: 280, height: 160 }], preferred, { width: 280 })).toEqual({ x: 826, y: 400 })
|
||||
expect(nextOpenBoardPosition([
|
||||
{ x: 500, y: 400, width: 280 },
|
||||
{ x: 826, y: 400, width: 280 },
|
||||
{ x: 500, y: 400, width: 280, height: 160 },
|
||||
{ x: 826, y: 400, width: 280, height: 160 },
|
||||
], preferred, { width: 280 })).toEqual({ x: 174, y: 400 })
|
||||
})
|
||||
})
|
||||
@@ -147,25 +152,24 @@ describe('timeline projection', () => {
|
||||
})
|
||||
|
||||
describe('folder domain behavior', () => {
|
||||
const relations: WidgetRelation[] = [
|
||||
{ id: 'later', fromWidgetId: folder.id, toWidgetId: 'doc-2', type: 'contains', sortOrder: 2 },
|
||||
{ id: 'other', fromWidgetId: 'folder-2', toWidgetId: 'doc-x', type: 'contains', sortOrder: 0 },
|
||||
{ id: 'first', fromWidgetId: folder.id, toWidgetId: 'doc-1', type: 'contains', sortOrder: 0 },
|
||||
const relations: ExhibitRelation[] = [
|
||||
{ id: 'later', fromExhibitId: folder.id, toExhibitId: 'doc-2', type: 'contains', sortOrder: 2 },
|
||||
{ id: 'other', fromExhibitId: 'folder-2', toExhibitId: 'doc-x', type: 'contains', sortOrder: 0 },
|
||||
{ id: 'first', fromExhibitId: folder.id, toExhibitId: 'doc-1', type: 'contains', sortOrder: 0 },
|
||||
]
|
||||
|
||||
it('orders and scopes contained documents by their normalized relations', () => {
|
||||
expect(containedIds({ ...state, relations }, folder.id)).toEqual(['doc-1', 'doc-2'])
|
||||
})
|
||||
|
||||
it('only treats an explicit boolean true as open', () => {
|
||||
it('stores the open state explicitly on the folder exhibit', () => {
|
||||
expect(folderIsOpen(folder)).toBe(false)
|
||||
expect(folderIsOpen({ ...folder, config: { open: true } })).toBe(true)
|
||||
expect(folderIsOpen({ ...folder, config: { open: 'true' } })).toBe(false)
|
||||
expect(folderIsOpen({ ...folder, isOpen: true })).toBe(true)
|
||||
})
|
||||
|
||||
it('retains a configured expanded file position', () => {
|
||||
const relation = { ...relations[0], config: { x: 720, y: 415 } }
|
||||
expect(relationPosition({ ...state, relations }, relation)).toEqual({ x: 720, y: 415 })
|
||||
const document = { id: 'doc-2', type: 'document' as const, title: 'Source', x: 720, y: 415, width: 174, height: 145, rotation: 0, zIndex: 2, hidden: false, body: [], regions: [], fileType: 'text' as const, metadata: {} }
|
||||
expect(relationPosition({ ...state, exhibits: [...state.exhibits, document], relations }, relations[0])).toEqual({ x: 720, y: 415 })
|
||||
})
|
||||
|
||||
it('derives a deterministic position when a relation has not been moved', () => {
|
||||
@@ -175,38 +179,39 @@ describe('folder domain behavior', () => {
|
||||
|
||||
it('normalizes legacy containment without changing source ownership', () => {
|
||||
const legacy = {
|
||||
...state,
|
||||
id: state.id, title: state.title, subtitle: state.subtitle, viewport: state.viewport, brief: state.brief,
|
||||
documents: [{ id: 'doc-1', title: 'Image', kind: 'IMAGE', date: '', body: [], regions: [], mimeType: 'image/png' }],
|
||||
evidence: [{ ...folder, type: 'evidence', sourceDocumentId: 'doc-1', containedDocumentIds: ['doc-1'], config: undefined }],
|
||||
relations: undefined,
|
||||
} as unknown as CaseState
|
||||
}
|
||||
const normalized = normalizeCase(legacy)
|
||||
expect(normalized.evidence[0]).toMatchObject({ type: 'folder', config: {}, containedDocumentIds: ['doc-1'] })
|
||||
expect(normalized.documents[0]).toMatchObject({ fileType: 'image', metadata: {} })
|
||||
expect(normalized.exhibits.find(exhibit => exhibit.type === 'folder')).toMatchObject({ type: 'folder', isOpen: false })
|
||||
expect(normalized.exhibits.find(exhibit => exhibit.type === 'document')).toMatchObject({ fileType: 'image', metadata: {} })
|
||||
expect(normalized.relations).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('exhibit disposal', () => {
|
||||
it('removes an exhibit and every graph reference while retaining source documents', () => {
|
||||
it('removes an exhibit and every graph reference while retaining unrelated exhibits', () => {
|
||||
const note = { ...folder, id: 'note-1', type: 'note' as const, title: 'Working note' }
|
||||
const event = { ...folder, id: 'event-1', type: 'event' as const, supportingEvidenceIds: [note.id, 'doc-1'] }
|
||||
const party = { ...folder, id: 'party-1', type: 'party' as const, relatedEvidenceIds: [note.id] }
|
||||
const event = { ...folder, id: 'event-1', type: 'event' as const, eventDate: undefined }
|
||||
const party = { ...folder, id: 'party-1', type: 'party' as const, partyKind: 'person' as const, aliases: [] }
|
||||
const document = { id: 'doc-1', type: 'document' as const, title: 'Source', x: 20, y: 20, width: 174, height: 145, rotation: 0, zIndex: 2, hidden: false, body: [], regions: [], fileType: 'text' as const, metadata: {} }
|
||||
const discarded = discardExhibit({
|
||||
...state,
|
||||
documents: [{ id: 'doc-1', title: 'Source', kind: 'TEXT', date: '', body: [], regions: [], fileType: 'text', metadata: {} }],
|
||||
evidence: [folder, note, event, party],
|
||||
relations: [{ id: 'nested', fromWidgetId: folder.id, toWidgetId: note.id, type: 'contains' }],
|
||||
connections: [{ id: 'thread', fromEvidenceId: note.id, toEvidenceId: party.id }],
|
||||
exhibits: [folder, document, note, event, party],
|
||||
relations: [
|
||||
{ id: 'supports', fromExhibitId: event.id, toExhibitId: note.id, type: 'supports', sortOrder: 0 },
|
||||
{ id: 'concerns', fromExhibitId: party.id, toExhibitId: note.id, type: 'concerns', sortOrder: 0 },
|
||||
],
|
||||
connections: [{ id: 'thread', fromExhibitId: note.id, toExhibitId: party.id }],
|
||||
brief: { body: '', concepts: [{ id: 'concept-1', label: 'Unknown', context: '', resolvedPartyExhibitId: note.id }] },
|
||||
}, note.id)
|
||||
|
||||
expect(discarded.documents).toHaveLength(1)
|
||||
expect(discarded.evidence.map(exhibit => exhibit.id)).not.toContain(note.id)
|
||||
expect(discarded.exhibits).toContainEqual(document)
|
||||
expect(discarded.exhibits.map(exhibit => exhibit.id)).not.toContain(note.id)
|
||||
expect(discarded.relations).toEqual([])
|
||||
expect(discarded.connections).toEqual([])
|
||||
expect(discarded.evidence.find(exhibit => exhibit.id === event.id)?.supportingEvidenceIds).toEqual(['doc-1'])
|
||||
expect(discarded.evidence.find(exhibit => exhibit.id === party.id)?.relatedEvidenceIds).toEqual([])
|
||||
expect(discarded.brief.concepts[0].resolvedPartyExhibitId).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user