Merge pull request #35 from tldraw/21-toggle-command-tests
Adds test for the toggle command, renames files to include test
This commit is contained in:
commit
8b1072c3c3
12 changed files with 279 additions and 20 deletions
237
__tests__/commands/toggle.test.ts
Normal file
237
__tests__/commands/toggle.test.ts
Normal file
|
@ -0,0 +1,237 @@
|
|||
import { ShapeType } from 'types'
|
||||
import TestState from '../test-utils'
|
||||
|
||||
describe('toggle command', () => {
|
||||
const tt = new TestState()
|
||||
tt.resetDocumentState()
|
||||
.createShape(
|
||||
{
|
||||
type: ShapeType.Rectangle,
|
||||
point: [0, 0],
|
||||
size: [100, 100],
|
||||
childIndex: 1,
|
||||
isLocked: false,
|
||||
isHidden: false,
|
||||
isAspectRatioLocked: false,
|
||||
},
|
||||
'rect1'
|
||||
)
|
||||
.createShape(
|
||||
{
|
||||
type: ShapeType.Rectangle,
|
||||
point: [400, 0],
|
||||
size: [100, 100],
|
||||
childIndex: 2,
|
||||
isHidden: false,
|
||||
isLocked: false,
|
||||
isAspectRatioLocked: false,
|
||||
},
|
||||
'rect2'
|
||||
)
|
||||
.createShape(
|
||||
{
|
||||
type: ShapeType.Rectangle,
|
||||
point: [800, 0],
|
||||
size: [100, 100],
|
||||
childIndex: 3,
|
||||
isHidden: true,
|
||||
isLocked: true,
|
||||
isAspectRatioLocked: true,
|
||||
},
|
||||
'rect3'
|
||||
)
|
||||
.createShape(
|
||||
{
|
||||
type: ShapeType.Rectangle,
|
||||
point: [1000, 0],
|
||||
size: [100, 100],
|
||||
childIndex: 4,
|
||||
isHidden: true,
|
||||
isLocked: true,
|
||||
isAspectRatioLocked: true,
|
||||
},
|
||||
'rect4'
|
||||
)
|
||||
.save()
|
||||
|
||||
describe('toggles properties on single shape', () => {
|
||||
it('does command', () => {
|
||||
tt.restore().clickShape('rect1')
|
||||
|
||||
tt.send('TOGGLED_SHAPE_LOCK')
|
||||
|
||||
expect(tt.getShape('rect1').isLocked).toBe(true)
|
||||
|
||||
tt.send('TOGGLED_SHAPE_LOCK')
|
||||
|
||||
expect(tt.getShape('rect1').isLocked).toBe(false)
|
||||
})
|
||||
|
||||
it('undoes and redoes command', () => {
|
||||
// Restore the saved data state, with the initial selection
|
||||
tt.restore().clickShape('rect1')
|
||||
|
||||
tt.send('TOGGLED_SHAPE_LOCK')
|
||||
|
||||
tt.send('UNDO')
|
||||
|
||||
expect(tt.getShape('rect1').isLocked).toBe(false)
|
||||
|
||||
tt.send('REDO')
|
||||
|
||||
expect(tt.getShape('rect1').isLocked).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('toggles properties on shapes with matching properties, starting from false', () => {
|
||||
it('does command', () => {
|
||||
// Restore the saved data state, with the initial selection
|
||||
tt.restore().clickShape('rect1').clickShape('rect2', { shiftKey: true })
|
||||
|
||||
tt.send('TOGGLED_SHAPE_LOCK')
|
||||
|
||||
expect(tt.getShape('rect1').isLocked).toBe(true)
|
||||
expect(tt.getShape('rect2').isLocked).toBe(true)
|
||||
|
||||
tt.send('TOGGLED_SHAPE_LOCK')
|
||||
|
||||
expect(tt.getShape('rect1').isLocked).toBe(false)
|
||||
expect(tt.getShape('rect2').isLocked).toBe(false)
|
||||
|
||||
tt.send('TOGGLED_SHAPE_LOCK')
|
||||
|
||||
expect(tt.getShape('rect1').isLocked).toBe(true)
|
||||
expect(tt.getShape('rect2').isLocked).toBe(true)
|
||||
})
|
||||
|
||||
it('undoes and redoes command', () => {
|
||||
// Restore the saved data state, with the initial selection
|
||||
tt.restore().clickShape('rect1').clickShape('rect2', { shiftKey: true })
|
||||
|
||||
tt.send('TOGGLED_SHAPE_LOCK').undo()
|
||||
|
||||
expect(tt.getShape('rect1').isLocked).toBe(false)
|
||||
expect(tt.getShape('rect2').isLocked).toBe(false)
|
||||
|
||||
tt.redo()
|
||||
|
||||
expect(tt.getShape('rect1').isLocked).toBe(true)
|
||||
expect(tt.getShape('rect2').isLocked).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('toggles properties on shapes with matching properties, starting from true', () => {
|
||||
it('does command', () => {
|
||||
// Restore the saved data state, with the initial selection
|
||||
tt.restore().clickShape('rect3').clickShape('rect4', { shiftKey: true })
|
||||
|
||||
tt.send('TOGGLED_SHAPE_LOCK')
|
||||
|
||||
expect(tt.getShape('rect3').isLocked).toBe(false)
|
||||
expect(tt.getShape('rect4').isLocked).toBe(false)
|
||||
|
||||
tt.send('TOGGLED_SHAPE_LOCK')
|
||||
|
||||
expect(tt.getShape('rect3').isLocked).toBe(true)
|
||||
expect(tt.getShape('rect4').isLocked).toBe(true)
|
||||
})
|
||||
|
||||
it('undoes and redoes command', () => {
|
||||
// Restore the saved data state, with the initial selection
|
||||
tt.restore().clickShape('rect3').clickShape('rect4', { shiftKey: true })
|
||||
|
||||
tt.send('TOGGLED_SHAPE_LOCK').undo()
|
||||
|
||||
expect(tt.getShape('rect3').isLocked).toBe(true)
|
||||
expect(tt.getShape('rect4').isLocked).toBe(true)
|
||||
|
||||
tt.redo()
|
||||
|
||||
expect(tt.getShape('rect3').isLocked).toBe(false)
|
||||
expect(tt.getShape('rect4').isLocked).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('toggles properties on shapes with different properties', () => {
|
||||
it('does command', () => {
|
||||
// Restore the saved data state, with the initial selection
|
||||
tt.restore()
|
||||
.clickShape('rect1')
|
||||
.clickShape('rect2', { shiftKey: true })
|
||||
.clickShape('rect3', { shiftKey: true })
|
||||
|
||||
tt.send('TOGGLED_SHAPE_LOCK')
|
||||
|
||||
expect(tt.getShape('rect1').isLocked).toBe(true)
|
||||
expect(tt.getShape('rect2').isLocked).toBe(true)
|
||||
expect(tt.getShape('rect3').isLocked).toBe(true)
|
||||
|
||||
tt.send('TOGGLED_SHAPE_LOCK')
|
||||
|
||||
expect(tt.getShape('rect1').isLocked).toBe(false)
|
||||
expect(tt.getShape('rect2').isLocked).toBe(false)
|
||||
expect(tt.getShape('rect3').isLocked).toBe(false)
|
||||
})
|
||||
|
||||
it('undoes and redoes command', () => {
|
||||
tt.restore()
|
||||
.clickShape('rect1')
|
||||
.clickShape('rect2', { shiftKey: true })
|
||||
.clickShape('rect3', { shiftKey: true })
|
||||
|
||||
tt.send('TOGGLED_SHAPE_LOCK').undo()
|
||||
|
||||
expect(tt.getShape('rect1').isLocked).toBe(false)
|
||||
expect(tt.getShape('rect2').isLocked).toBe(false)
|
||||
expect(tt.getShape('rect3').isLocked).toBe(true)
|
||||
|
||||
tt.redo()
|
||||
|
||||
expect(tt.getShape('rect1').isLocked).toBe(true)
|
||||
expect(tt.getShape('rect2').isLocked).toBe(true)
|
||||
expect(tt.getShape('rect3').isLocked).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('catch all for other toggle props', () => {
|
||||
const eventPropertyPairs = {
|
||||
TOGGLED_SHAPE_LOCK: 'isLocked',
|
||||
TOGGLED_SHAPE_HIDE: 'isHidden',
|
||||
TOGGLED_SHAPE_ASPECT_LOCK: 'isAspectRatioLocked',
|
||||
}
|
||||
|
||||
it('toggles all event property pairs', () => {
|
||||
Object.entries(eventPropertyPairs).forEach(([eventName, propName]) => {
|
||||
// Restore the saved data state, with the initial selection
|
||||
tt.restore()
|
||||
.clickShape('rect1')
|
||||
.clickShape('rect2', { shiftKey: true })
|
||||
.clickShape('rect3', { shiftKey: true })
|
||||
|
||||
tt.send(eventName)
|
||||
|
||||
expect(tt.getShape('rect1')[propName]).toBe(true)
|
||||
expect(tt.getShape('rect2')[propName]).toBe(true)
|
||||
expect(tt.getShape('rect3')[propName]).toBe(true)
|
||||
|
||||
tt.undo()
|
||||
|
||||
expect(tt.getShape('rect1')[propName]).toBe(false)
|
||||
expect(tt.getShape('rect2')[propName]).toBe(false)
|
||||
expect(tt.getShape('rect3')[propName]).toBe(true)
|
||||
|
||||
tt.redo()
|
||||
|
||||
expect(tt.getShape('rect1')[propName]).toBe(true)
|
||||
expect(tt.getShape('rect2')[propName]).toBe(true)
|
||||
expect(tt.getShape('rect3')[propName]).toBe(true)
|
||||
|
||||
tt.send(eventName)
|
||||
|
||||
expect(tt.getShape('rect1')[propName]).toBe(false)
|
||||
expect(tt.getShape('rect2')[propName]).toBe(false)
|
||||
expect(tt.getShape('rect3')[propName]).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -228,15 +228,18 @@ class TestState {
|
|||
*```
|
||||
*/
|
||||
clickShape(id: string, options: PointerOptions = {}): TestState {
|
||||
this.state.send(
|
||||
'POINTED_SHAPE',
|
||||
inputs.pointerDown(TestState.point(options), id)
|
||||
)
|
||||
const shape = tld.getShape(this.data, id)
|
||||
const [x, y] = shape ? vec.add(shape.point, [1, 1]) : [0, 0]
|
||||
|
||||
this.state.send(
|
||||
'STOPPED_POINTING',
|
||||
inputs.pointerUp(TestState.point(options), id)
|
||||
)
|
||||
this.state
|
||||
.send(
|
||||
'POINTED_SHAPE',
|
||||
inputs.pointerDown(TestState.point({ x, y, ...options }), id)
|
||||
)
|
||||
.send(
|
||||
'STOPPED_POINTING',
|
||||
inputs.pointerUp(TestState.point({ x, y, ...options }), id)
|
||||
)
|
||||
|
||||
return this
|
||||
}
|
||||
|
@ -251,9 +254,12 @@ class TestState {
|
|||
*```
|
||||
*/
|
||||
startClick(id: string, options: PointerOptions = {}): TestState {
|
||||
const shape = tld.getShape(this.data, id)
|
||||
const [x, y] = shape ? vec.add(shape.point, [1, 1]) : [0, 0]
|
||||
|
||||
this.state.send(
|
||||
'POINTED_SHAPE',
|
||||
inputs.pointerDown(TestState.point(options), id)
|
||||
inputs.pointerDown(TestState.point({ x, y, ...options }), id)
|
||||
)
|
||||
|
||||
return this
|
||||
|
@ -269,9 +275,12 @@ class TestState {
|
|||
*```
|
||||
*/
|
||||
stopClick(id: string, options: PointerOptions = {}): TestState {
|
||||
const shape = tld.getShape(this.data, id)
|
||||
const [x, y] = shape ? vec.add(shape.point, [1, 1]) : [0, 0]
|
||||
|
||||
this.state.send(
|
||||
'STOPPED_POINTING',
|
||||
inputs.pointerUp(TestState.point(options), id)
|
||||
inputs.pointerUp(TestState.point({ x, y, ...options }), id)
|
||||
)
|
||||
|
||||
return this
|
||||
|
@ -287,12 +296,18 @@ class TestState {
|
|||
*```
|
||||
*/
|
||||
doubleClickShape(id: string, options: PointerOptions = {}): TestState {
|
||||
const shape = tld.getShape(this.data, id)
|
||||
const [x, y] = shape ? vec.add(shape.point, [1, 1]) : [0, 0]
|
||||
|
||||
this.state
|
||||
.send(
|
||||
'DOUBLE_POINTED_SHAPE',
|
||||
inputs.pointerDown(TestState.point(options), id)
|
||||
inputs.pointerDown(TestState.point({ x, y, ...options }), id)
|
||||
)
|
||||
.send(
|
||||
'STOPPED_POINTING',
|
||||
inputs.pointerUp(TestState.point({ x, y, ...options }), id)
|
||||
)
|
||||
.send('STOPPED_POINTING', inputs.pointerUp(TestState.point(options), id))
|
||||
|
||||
return this
|
||||
}
|
||||
|
|
|
@ -5,15 +5,22 @@ import tld from 'utils/tld'
|
|||
import { getShapeUtils } from 'state/shape-utils'
|
||||
import { PropsOfType } from 'types'
|
||||
|
||||
/**
|
||||
* The toggle command toggles a boolean property on all selected shapes.
|
||||
* If the value is true for all selected shapes, then the property is
|
||||
* set to false; the value is false for one or more of the selected
|
||||
* shapes, then the value for all shapes is set to true.
|
||||
*/
|
||||
export default function toggleCommand(
|
||||
data: Data,
|
||||
prop: PropsOfType<Shape>
|
||||
): void {
|
||||
const selectedShapes = tld.getSelectedShapes(data)
|
||||
const isAllToggled = selectedShapes.every((shape) => shape[prop])
|
||||
const initialShapes = Object.fromEntries(
|
||||
selectedShapes.map((shape) => [shape.id, shape[prop]])
|
||||
)
|
||||
const initialShapes = selectedShapes.map((shape) => ({
|
||||
id: shape.id,
|
||||
value: shape[prop],
|
||||
}))
|
||||
|
||||
history.execute(
|
||||
data,
|
||||
|
@ -23,22 +30,22 @@ export default function toggleCommand(
|
|||
do(data) {
|
||||
const { shapes } = tld.getPage(data)
|
||||
|
||||
for (const id in initialShapes) {
|
||||
initialShapes.forEach(({ id }) => {
|
||||
const shape = shapes[id]
|
||||
getShapeUtils(shape).setProperty(
|
||||
shape,
|
||||
prop,
|
||||
isAllToggled ? false : true
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
undo(data) {
|
||||
const { shapes } = tld.getPage(data)
|
||||
|
||||
for (const id in initialShapes) {
|
||||
initialShapes.forEach(({ id, value }) => {
|
||||
const shape = shapes[id]
|
||||
getShapeUtils(shape).setProperty(shape, prop, initialShapes[id])
|
||||
}
|
||||
getShapeUtils(shape).setProperty(shape, prop, value)
|
||||
})
|
||||
},
|
||||
})
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue