From 61fb767b5ab0e716521782ee688ce4209dfa8323 Mon Sep 17 00:00:00 2001 From: Steve Ruiz Date: Mon, 6 Sep 2021 14:30:59 +0100 Subject: [PATCH] Add tests, fix bug --- .../translate/translate.session.spec.ts | 87 ++++++++++++++++++- .../sessions/translate/translate.session.ts | 56 +++++++++--- 2 files changed, 128 insertions(+), 15 deletions(-) diff --git a/packages/tldraw/src/state/session/sessions/translate/translate.session.spec.ts b/packages/tldraw/src/state/session/sessions/translate/translate.session.spec.ts index f3c4d614f..7fbaec96e 100644 --- a/packages/tldraw/src/state/session/sessions/translate/translate.session.spec.ts +++ b/packages/tldraw/src/state/session/sessions/translate/translate.session.spec.ts @@ -1,6 +1,6 @@ import { TLDrawState } from '~state' import { mockDocument } from '~test' -import { TLDrawShapeType, TLDrawStatus } from '~types' +import { GroupShape, TLDrawShapeType, TLDrawStatus } from '~types' describe('Translate session', () => { const tlstate = new TLDrawState() @@ -184,15 +184,98 @@ describe('Translate session', () => { // it.todo('clones a shape with a parent shape') + describe('when translating a child of a group', () => { + it('translates the shape and updates the groups size / point', () => { + tlstate + .loadDocument(mockDocument) + .select('rect1', 'rect2') + .group(['rect1', 'rect2'], 'groupA') + .select('rect1') + .startTranslateSession([10, 10]) + .updateTranslateSession([20, 20], false, false) + .completeSession() + + expect(tlstate.getShape('groupA').point).toStrictEqual([10, 10]) + expect(tlstate.getShape('rect1').point).toStrictEqual([10, 10]) + expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) + + tlstate.undo() + + expect(tlstate.getShape('groupA').point).toStrictEqual([0, 0]) + expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) + expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) + + tlstate.redo() + + expect(tlstate.getShape('groupA').point).toStrictEqual([10, 10]) + expect(tlstate.getShape('rect1').point).toStrictEqual([10, 10]) + expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) + }) + + it('clones the shape and updates the parent', () => { + tlstate + .loadDocument(mockDocument) + .select('rect1', 'rect2') + .group(['rect1', 'rect2'], 'groupA') + .select('rect1') + .startTranslateSession([10, 10]) + .updateTranslateSession([20, 20], false, true) + .completeSession() + + const children = tlstate.getShape('groupA').children + const newShapeId = children[children.length - 1] + + expect(tlstate.getShape('groupA').point).toStrictEqual([0, 0]) + expect(tlstate.getShape('groupA').children.length).toBe(3) + expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) + expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) + expect(tlstate.getShape(newShapeId).point).toStrictEqual([20, 20]) + expect(tlstate.getShape(newShapeId).parentId).toBe('groupA') + + tlstate.undo() + + expect(tlstate.getShape('groupA').point).toStrictEqual([0, 0]) + expect(tlstate.getShape('groupA').children.length).toBe(2) + expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) + expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) + expect(tlstate.getShape(newShapeId)).toBeUndefined() + + tlstate.redo() + + expect(tlstate.getShape('groupA').point).toStrictEqual([0, 0]) + expect(tlstate.getShape('groupA').children.length).toBe(3) + expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) + expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) + expect(tlstate.getShape(newShapeId).point).toStrictEqual([20, 20]) + expect(tlstate.getShape(newShapeId).parentId).toBe('groupA') + }) + }) + describe('when translating a shape with children', () => { it('translates the shapes children', () => { tlstate .loadDocument(mockDocument) .select('rect1', 'rect2') - .group() + .group(['rect1', 'rect2'], 'groupA') .startTranslateSession([10, 10]) .updateTranslateSession([20, 20], false, false) .completeSession() + + expect(tlstate.getShape('groupA').point).toStrictEqual([10, 10]) + expect(tlstate.getShape('rect1').point).toStrictEqual([10, 10]) + expect(tlstate.getShape('rect2').point).toStrictEqual([110, 110]) + + tlstate.undo() + + expect(tlstate.getShape('groupA').point).toStrictEqual([0, 0]) + expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) + expect(tlstate.getShape('rect2').point).toStrictEqual([100, 100]) + + tlstate.redo() + + expect(tlstate.getShape('groupA').point).toStrictEqual([10, 10]) + expect(tlstate.getShape('rect1').point).toStrictEqual([10, 10]) + expect(tlstate.getShape('rect2').point).toStrictEqual([110, 110]) }) it('clones the shapes and children', () => { diff --git a/packages/tldraw/src/state/session/sessions/translate/translate.session.ts b/packages/tldraw/src/state/session/sessions/translate/translate.session.ts index 33072125d..dbafaf07e 100644 --- a/packages/tldraw/src/state/session/sessions/translate/translate.session.ts +++ b/packages/tldraw/src/state/session/sessions/translate/translate.session.ts @@ -8,6 +8,7 @@ import { TLDrawCommand, TLDrawStatus, ArrowShape, + GroupShape, } from '~types' import { TLDR } from '~state/tldr' import type { Patch } from 'rko' @@ -230,7 +231,8 @@ export class TranslateSession implements Session { complete(data: Data): TLDrawCommand { const pageId = data.appState.currentPageId - const { initialShapes, bindingsToDelete, clones, clonedBindings } = this.snapshot + const { initialShapes, initialParentChildren, bindingsToDelete, clones, clonedBindings } = + this.snapshot const beforeBindings: Patch> = {} const beforeShapes: Patch> = {} @@ -238,21 +240,47 @@ export class TranslateSession implements Session { const afterBindings: Patch> = {} const afterShapes: Patch> = {} - clones.forEach((clone) => { - beforeShapes[clone.id] = undefined - afterShapes[clone.id] = this.isCloning ? TLDR.getShape(data, clone.id, pageId) : undefined - }) + if (this.isCloning) { + // Update the clones + clones.forEach((clone) => { + beforeShapes[clone.id] = undefined - initialShapes.forEach((shape) => { - beforeShapes[shape.id] = { point: shape.point } - afterShapes[shape.id] = { point: TLDR.getShape(data, shape.id, pageId).point } - }) + afterShapes[clone.id] = TLDR.getShape(data, clone.id, pageId) - clonedBindings.forEach((binding) => { - beforeBindings[binding.id] = undefined - afterBindings[binding.id] = TLDR.getBinding(data, binding.id, pageId) - }) + if (clone.parentId !== pageId) { + beforeShapes[clone.parentId] = { + ...beforeShapes[clone.parentId], + children: initialParentChildren[clone.parentId], + } + afterShapes[clone.parentId] = { + ...afterShapes[clone.parentId], + children: TLDR.getShape(data, clone.parentId, pageId).children, + } + } + }) + + // Update the cloned bindings + clonedBindings.forEach((binding) => { + beforeBindings[binding.id] = undefined + afterBindings[binding.id] = TLDR.getBinding(data, binding.id, pageId) + }) + } else { + // If we aren't cloning, then update the initial shapes + initialShapes.forEach((shape) => { + beforeShapes[shape.id] = { + ...beforeShapes[shape.id], + point: shape.point, + } + + afterShapes[shape.id] = { + ...afterShapes[shape.id], + point: TLDR.getShape(data, shape.id, pageId).point, + } + }) + } + + // Update the deleted bindings and any associated shapes bindingsToDelete.forEach((binding) => { beforeBindings[binding.id] = binding @@ -270,6 +298,8 @@ export class TranslateSession implements Session { afterShapes[id] = { ...afterShapes[id], handles: {} } + // There should be before and after shapes + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion beforeShapes[id]!.handles![handle.id as keyof ArrowShape['handles']] = { bindingId: binding.id,