@tldraw/editor
Version:
tldraw infinite canvas SDK (editor).
245 lines (191 loc) • 6.39 kB
text/typescript
import { PageRecordType, TLStore, createShapeId } from '@tldraw/tlschema'
import { IndexKey } from '@tldraw/utils'
import { Editor } from '../editor/Editor'
import { Box } from '../primitives/Box'
import { TLEditorSnapshot, getSnapshot, loadSnapshot } from './TLEditorSnapshot'
import { createTLStore } from './createTLStore'
const createEditor = (store: TLStore) => {
return new Editor({
store,
bindingUtils: [],
shapeUtils: [],
getContainer: () => document.createElement('div'),
tools: [],
})
}
describe('getSnapshot', () => {
it('should return a TLEditorSnapshot', () => {
const store = createTLStore({})
store.ensureStoreIsUsable()
const snapshot = getSnapshot(store)
expect(snapshot).toMatchObject({
document: {
schema: {},
store: {
'document:document': {},
'page:page': {},
},
},
session: {
currentPageId: 'page:page',
exportBackground: true,
isDebugMode: false,
isFocusMode: false,
isGridMode: false,
isToolLocked: false,
pageStates: [
{
camera: { x: 0, y: 0, z: 1 },
focusedGroupId: null,
pageId: 'page:page',
selectedShapeIds: [],
},
],
version: 0,
},
})
const editor = createEditor(store)
editor.updateInstanceState({ isDebugMode: true })
editor.createPage({ id: PageRecordType.createId('page2') })
editor.setCurrentPage(PageRecordType.createId('page2'))
const snapshot2 = getSnapshot(store)
expect(snapshot2).toMatchObject({
document: {
schema: {},
store: {
'document:document': {},
'page:page': {},
'page:page2': {},
},
},
session: {
currentPageId: 'page:page2',
isDebugMode: true,
pageStates: [{}, {}],
version: 0,
},
})
})
})
const page2Id = PageRecordType.createId('page2')
function addPage2(snapshot: TLEditorSnapshot) {
// sneakily add a page
snapshot = structuredClone(snapshot)
snapshot.document.store[page2Id] = PageRecordType.create({
id: page2Id,
name: 'my lovely page',
index: 'a4' as IndexKey,
})
// and set the current page id
snapshot.session.currentPageId = page2Id
return snapshot
}
describe('loadSnapshot', () => {
it('loads a snapshot into the store', () => {
const store = createTLStore({})
const editor = createEditor(store)
const snapshot = addPage2(getSnapshot(store))
loadSnapshot(store, snapshot)
expect(
editor
.getPages()
.map((p) => p.id)
.sort()
).toEqual(['page:page', 'page:page2'])
expect(editor.getCurrentPageId()).toBe(page2Id)
})
it('does not overwrite changes to the viewport page bounds', () => {
const store = createTLStore({})
const editor = createEditor(store)
const snapshot = addPage2(getSnapshot(store))
expect(editor.getViewportScreenBounds()).not.toEqual(new Box(0, 0, 100, 100))
editor.updateViewportScreenBounds(new Box(0, 0, 100, 100))
expect(editor.getViewportScreenBounds()).toEqual(new Box(0, 0, 100, 100))
loadSnapshot(store, snapshot)
expect(editor.getViewportScreenBounds()).toEqual(new Box(0, 0, 100, 100))
})
it('works with just the document bits (1)', () => {
const store = createTLStore({})
const editor = createEditor(store)
const snapshot = addPage2(getSnapshot(store))
loadSnapshot(store, snapshot.document)
expect(
editor
.getPages()
.map((p) => p.id)
.sort()
).toEqual(['page:page', 'page:page2'])
expect(editor.getCurrentPageId()).toBe('page:page')
})
it('works with just the document bits (2)', () => {
const store = createTLStore({})
const editor = createEditor(store)
const snapshot = addPage2(getSnapshot(store))
loadSnapshot(store, { document: snapshot.document })
expect(
editor
.getPages()
.map((p) => p.id)
.sort()
).toEqual(['page:page', 'page:page2'])
expect(editor.getCurrentPageId()).toBe('page:page')
})
it('allows loading the session bits later', () => {
const store = createTLStore({})
const editor = createEditor(store)
const snapshot = addPage2(getSnapshot(store))
loadSnapshot(store, snapshot.document)
expect(
editor
.getPages()
.map((p) => p.id)
.sort()
).toEqual(['page:page', 'page:page2'])
expect(editor.getCurrentPageId()).toBe('page:page')
loadSnapshot(store, { session: snapshot.session })
expect(editor.getCurrentPageId()).toBe('page:page2')
})
it('preserves session stuff if no session snapshot is provided', () => {
const store = createTLStore({})
const editor = createEditor(store)
const snapshot = getSnapshot(store)
expect(editor.getCamera()).toMatchObject({ x: 0, y: 0, z: 1 })
editor.setCamera({ x: 1, y: 2, z: 3 })
loadSnapshot(store, snapshot.document)
expect(editor.getCamera()).toMatchObject({ x: 1, y: 2, z: 3 })
loadSnapshot(store, { document: snapshot.document })
expect(editor.getCamera()).toMatchObject({ x: 1, y: 2, z: 3 })
loadSnapshot(store, snapshot)
expect(editor.getCamera()).toMatchObject({ x: 0, y: 0, z: 1 })
})
it('preserves session stuff if only a partial session snapshot is provided', () => {
const store = createTLStore({})
const editor = createEditor(store)
const page1Id = editor.getCurrentPageId()
const page2Id = PageRecordType.createId('page2')
editor.createPage({ id: page2Id })
editor.setCurrentPage(PageRecordType.createId('page2'))
const snapshot = getSnapshot(store)
delete snapshot.session.pageStates
editor.setCurrentPage(page1Id)
editor.setCamera({ x: 1, y: 2, z: 3 })
loadSnapshot(store, snapshot)
// the page should have switched back to page2 with the default camera
expect(editor.getCurrentPageId()).toBe(page2Id)
expect(editor.getCamera()).toMatchObject({ x: 0, y: 0, z: 1 })
// and because we updated the page1 camera after the snapshot was taken, it should still be the same
editor.setCurrentPage(page1Id)
expect(editor.getCamera()).toMatchObject({ x: 1, y: 2, z: 3 })
})
it('cleans up references to missing shapes from page state', () => {
const store = createTLStore({})
const editor = createEditor(store)
const shapeA = createShapeId('a')
editor.createShape({ type: 'group', id: shapeA })
const snapshot = getSnapshot(store)
const shapeB = createShapeId('b')
editor.createShape({ type: 'group', id: shapeB }).select(shapeB, shapeA)
loadSnapshot(store, snapshot.document)
expect(editor.getCurrentPageState().selectedShapeIds).toEqual([shapeA])
})
})