test: add std gfx test (#11442)
### Changed - Move some intergraion tests to std as they are more like basic tests - Add some basic gfx-related tests
@@ -11,6 +11,7 @@ import {
|
||||
HeadingBlockSchemaExtension,
|
||||
NoteBlockSchemaExtension,
|
||||
RootBlockSchemaExtension,
|
||||
SurfaceBlockSchemaExtension,
|
||||
} from './test-schema.js';
|
||||
import { testSpecs } from './test-spec.js';
|
||||
|
||||
@@ -20,6 +21,7 @@ const extensions = [
|
||||
RootBlockSchemaExtension,
|
||||
NoteBlockSchemaExtension,
|
||||
HeadingBlockSchemaExtension,
|
||||
SurfaceBlockSchemaExtension,
|
||||
];
|
||||
|
||||
function createTestOptions() {
|
||||
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
358
blocksuite/framework/std/src/__tests__/gfx/surface.unit.spec.ts
Normal file
@@ -0,0 +1,358 @@
|
||||
import {
|
||||
createAutoIncrementIdGenerator,
|
||||
TestWorkspace,
|
||||
} from '@blocksuite/store/test';
|
||||
import { describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import { effects } from '../../effects.js';
|
||||
import type { TestShapeElement } from '../test-gfx-element.js';
|
||||
import {
|
||||
RootBlockSchemaExtension,
|
||||
type SurfaceBlockModel,
|
||||
SurfaceBlockSchemaExtension,
|
||||
} from '../test-schema.js';
|
||||
|
||||
effects();
|
||||
|
||||
const extensions = [RootBlockSchemaExtension, SurfaceBlockSchemaExtension];
|
||||
|
||||
function createTestOptions() {
|
||||
const idGenerator = createAutoIncrementIdGenerator();
|
||||
return { id: 'test-collection', idGenerator };
|
||||
}
|
||||
|
||||
const commonSetup = () => {
|
||||
const collection = new TestWorkspace(createTestOptions());
|
||||
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc('home');
|
||||
const store = doc.getStore({ extensions });
|
||||
doc.load();
|
||||
|
||||
const rootId = store.addBlock('test:page');
|
||||
const surfaceId = store.addBlock('test:surface', {}, rootId);
|
||||
|
||||
const surfaceBlock = store.getBlock(surfaceId)!;
|
||||
|
||||
return {
|
||||
surfaceId,
|
||||
surfaceModel: surfaceBlock.model as SurfaceBlockModel,
|
||||
};
|
||||
};
|
||||
|
||||
describe('surface basic', () => {
|
||||
test('addElement should work correctly', () => {
|
||||
const { surfaceModel: model } = commonSetup();
|
||||
const id = model.addElement({
|
||||
type: 'testShape',
|
||||
});
|
||||
|
||||
expect(model.elementModels[0].id).toBe(id);
|
||||
});
|
||||
|
||||
test('removeElement should work correctly', () => {
|
||||
const { surfaceModel: model } = commonSetup();
|
||||
const id = model.addElement({
|
||||
type: 'testShape',
|
||||
});
|
||||
|
||||
model.deleteElement(id);
|
||||
|
||||
expect(model.elementModels.length).toBe(0);
|
||||
});
|
||||
|
||||
test('updateElement should work correctly', () => {
|
||||
const { surfaceModel: model } = commonSetup();
|
||||
const id = model.addElement({
|
||||
type: 'testShape',
|
||||
});
|
||||
|
||||
model.updateElement(id, { xywh: '[10,10,200,200]' });
|
||||
|
||||
expect(model.elementModels[0].xywh).toBe('[10,10,200,200]');
|
||||
});
|
||||
|
||||
test('getElementById should return element', () => {
|
||||
const { surfaceModel: model } = commonSetup();
|
||||
|
||||
const id = model.addElement({
|
||||
type: 'testShape',
|
||||
});
|
||||
|
||||
expect(model.getElementById(id)).not.toBeNull();
|
||||
});
|
||||
|
||||
test('getElementById should return null if not found', () => {
|
||||
const { surfaceModel: model } = commonSetup();
|
||||
|
||||
expect(model.getElementById('not-found')).toBeNull();
|
||||
});
|
||||
|
||||
test('created observer should be called', () => {
|
||||
const { surfaceModel } = commonSetup();
|
||||
|
||||
let expectPayload;
|
||||
const elementAddedCallback = vi.fn(payload => (expectPayload = payload));
|
||||
|
||||
surfaceModel.elementAdded.subscribe(elementAddedCallback);
|
||||
|
||||
const shapeId = surfaceModel.addElement({
|
||||
type: 'testShape',
|
||||
rotate: 0,
|
||||
xywh: '[0, 0, 10, 10]',
|
||||
});
|
||||
|
||||
expect(elementAddedCallback).toHaveBeenCalled();
|
||||
expect(expectPayload).toMatchObject({
|
||||
id: shapeId,
|
||||
});
|
||||
});
|
||||
|
||||
test('update and props observer should be called', () => {
|
||||
const { surfaceModel } = commonSetup();
|
||||
|
||||
const shapeId = surfaceModel.addElement({
|
||||
type: 'testShape',
|
||||
rotate: 0,
|
||||
xywh: '[0, 0, 10, 10]',
|
||||
});
|
||||
const shapeModel = surfaceModel.getElementById(shapeId)!;
|
||||
|
||||
let expectPayload;
|
||||
const elementUpdatedCallback = vi.fn(payload => (expectPayload = payload));
|
||||
let propsUpdatedPayload;
|
||||
const propsUpdatedCallback = vi.fn(payload => {
|
||||
propsUpdatedPayload = payload;
|
||||
});
|
||||
|
||||
surfaceModel.elementUpdated.subscribe(elementUpdatedCallback);
|
||||
shapeModel.propsUpdated.subscribe(propsUpdatedCallback);
|
||||
|
||||
surfaceModel.updateElement(shapeId, {
|
||||
rotate: 10,
|
||||
});
|
||||
|
||||
expect(elementUpdatedCallback).toHaveBeenCalled();
|
||||
expect(propsUpdatedCallback).toHaveBeenCalled();
|
||||
expect(expectPayload).toMatchObject({
|
||||
id: shapeId,
|
||||
props: {
|
||||
rotate: 10,
|
||||
},
|
||||
oldValues: {
|
||||
rotate: 0,
|
||||
},
|
||||
});
|
||||
expect(propsUpdatedPayload).toMatchObject({
|
||||
key: 'rotate',
|
||||
});
|
||||
});
|
||||
|
||||
test('delete observer should be called', () => {
|
||||
const { surfaceModel } = commonSetup();
|
||||
|
||||
const shapeId = surfaceModel.addElement({
|
||||
type: 'testShape',
|
||||
rotate: 0,
|
||||
xywh: '[0, 0, 10, 10]',
|
||||
});
|
||||
|
||||
let expectPayload;
|
||||
const deletedCallback = vi.fn(payload => (expectPayload = payload));
|
||||
|
||||
surfaceModel.elementRemoved.subscribe(deletedCallback);
|
||||
surfaceModel.deleteElement(shapeId);
|
||||
|
||||
expect(deletedCallback).toHaveBeenCalled();
|
||||
expect(expectPayload).toMatchObject({
|
||||
id: shapeId,
|
||||
type: 'testShape',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('element model', () => {
|
||||
test('default value should work correctly', () => {
|
||||
const { surfaceModel: model } = commonSetup();
|
||||
const id = model.addElement({
|
||||
type: 'testShape',
|
||||
});
|
||||
|
||||
const element = model.getElementById(id)! as TestShapeElement;
|
||||
|
||||
expect(element.rotate).toBe(0);
|
||||
expect(element.xywh).toBe('[0,0,10,10]');
|
||||
});
|
||||
|
||||
test('defined prop should not be overwritten by default value', () => {
|
||||
const { surfaceModel: model } = commonSetup();
|
||||
const id = model.addElement({
|
||||
type: 'testShape',
|
||||
rotate: 20,
|
||||
});
|
||||
|
||||
const element = model.getElementById(id)! as TestShapeElement;
|
||||
|
||||
expect(element.rotate).toBe(20);
|
||||
});
|
||||
|
||||
test('assign value to model property should update ymap directly', () => {
|
||||
const { surfaceModel: model } = commonSetup();
|
||||
const id = model.addElement({
|
||||
type: 'testShape',
|
||||
});
|
||||
|
||||
const element = model.getElementById(id)! as TestShapeElement;
|
||||
|
||||
expect(element.yMap.get('rotate')).toBe(0);
|
||||
element.rotate = 30;
|
||||
expect(element.yMap.get('rotate')).toBe(30);
|
||||
});
|
||||
});
|
||||
|
||||
describe('stash/pop', () => {
|
||||
const { surfaceModel: model } = commonSetup();
|
||||
test('stash and pop should work correctly', () => {
|
||||
const id = model.addElement({
|
||||
type: 'testShape',
|
||||
});
|
||||
const elementModel = model.getElementById(id)! as TestShapeElement;
|
||||
|
||||
expect(elementModel.rotate).toBe(0);
|
||||
|
||||
elementModel.stash('rotate');
|
||||
elementModel.rotate = 10;
|
||||
expect(elementModel.rotate).toBe(10);
|
||||
expect(elementModel.yMap.get('rotate')).toBe(0);
|
||||
|
||||
elementModel.pop('rotate');
|
||||
expect(elementModel.rotate).toBe(10);
|
||||
expect(elementModel.yMap.get('rotate')).toBe(10);
|
||||
|
||||
elementModel.rotate = 6;
|
||||
expect(elementModel.rotate).toBe(6);
|
||||
expect(elementModel.yMap.get('rotate')).toBe(6);
|
||||
});
|
||||
|
||||
test('assign stashed property should emit event', () => {
|
||||
const id = model.addElement({
|
||||
type: 'testShape',
|
||||
rotate: 4,
|
||||
});
|
||||
const elementModel = model.getElementById(id)! as TestShapeElement;
|
||||
|
||||
elementModel.stash('rotate');
|
||||
|
||||
const onchange = vi.fn();
|
||||
const subscription = model.elementUpdated.subscribe(({ id }) => {
|
||||
subscription.unsubscribe();
|
||||
onchange(id);
|
||||
});
|
||||
|
||||
elementModel.rotate = 10;
|
||||
expect(onchange).toHaveBeenCalledWith(id);
|
||||
});
|
||||
|
||||
test('stashed property should also trigger derive decorator', () => {
|
||||
const id = model.addElement({
|
||||
type: 'testShape',
|
||||
rotate: 20,
|
||||
});
|
||||
const elementModel = model.getElementById(id)! as TestShapeElement;
|
||||
|
||||
elementModel.stash('shapeType');
|
||||
elementModel.shapeType = 'triangle';
|
||||
|
||||
// rotation should be 0 'cause of derive decorator
|
||||
expect(elementModel.rotate).toBe(0);
|
||||
});
|
||||
|
||||
test('non-field property should not allow stash/pop, and should fail silently ', () => {
|
||||
const id = model.addElement({
|
||||
type: 'testShape',
|
||||
});
|
||||
const elementModel = model.getElementById(id)! as TestShapeElement;
|
||||
|
||||
// opacity is a local property, so it should not be stashed
|
||||
elementModel.stash('opacity');
|
||||
expect(elementModel['_stashed'].has('opacity')).toBe(false);
|
||||
|
||||
// pop the `opacity` should not affect yMap
|
||||
elementModel.opacity = 0.5;
|
||||
elementModel.pop('opacity');
|
||||
expect(elementModel.yMap.has('opacity')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('derive decorator', () => {
|
||||
test('derived decorator should work correctly', () => {
|
||||
const { surfaceModel: model } = commonSetup();
|
||||
const id = model.addElement({
|
||||
type: 'testShape',
|
||||
rotate: 20,
|
||||
});
|
||||
const elementModel = model.getElementById(id)! as TestShapeElement;
|
||||
|
||||
elementModel.shapeType = 'triangle';
|
||||
|
||||
expect(elementModel.rotate).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('local decorator', () => {
|
||||
test('local decorator should work correctly', () => {
|
||||
const { surfaceModel: model } = commonSetup();
|
||||
const id = model.addElement({
|
||||
type: 'testShape',
|
||||
});
|
||||
const elementModel = model.getElementById(id)! as TestShapeElement;
|
||||
|
||||
expect(elementModel.display).toBe(true);
|
||||
|
||||
elementModel.display = false;
|
||||
expect(elementModel.display).toBe(false);
|
||||
|
||||
elementModel.opacity = 0.5;
|
||||
expect(elementModel.opacity).toBe(0.5);
|
||||
});
|
||||
|
||||
test('assign local property should emit event', () => {
|
||||
const { surfaceModel: model } = commonSetup();
|
||||
const id = model.addElement({
|
||||
type: 'testShape',
|
||||
});
|
||||
const elementModel = model.getElementById(id)! as TestShapeElement;
|
||||
|
||||
const onchange = vi.fn();
|
||||
const subscription = model.elementUpdated.subscribe(({ id }) => {
|
||||
subscription.unsubscribe();
|
||||
onchange(id);
|
||||
});
|
||||
|
||||
const onPropChange = vi.fn();
|
||||
elementModel.propsUpdated.subscribe(({ key }) => {
|
||||
onPropChange(key);
|
||||
});
|
||||
|
||||
elementModel.display = false;
|
||||
|
||||
expect(elementModel.display).toBe(false);
|
||||
expect(onchange).toHaveBeenCalledWith(id);
|
||||
expect(onPropChange).toHaveBeenCalledWith('display');
|
||||
});
|
||||
});
|
||||
|
||||
describe('convert decorator', () => {
|
||||
test('convert decorator', () => {
|
||||
const { surfaceModel: model } = commonSetup();
|
||||
const id = model.addElement({
|
||||
type: 'testShape',
|
||||
});
|
||||
const elementModel = model.getElementById(id)! as TestShapeElement;
|
||||
|
||||
// @ts-expect-error test needed
|
||||
elementModel.shapeType = 'otherImpossibleType';
|
||||
|
||||
expect(elementModel.shapeType).toBe('rect');
|
||||
});
|
||||
});
|
||||
134
blocksuite/framework/std/src/__tests__/gfx/view.unit.spec.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import {
|
||||
createAutoIncrementIdGenerator,
|
||||
TestWorkspace,
|
||||
} from '@blocksuite/store/test';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import { effects } from '../../effects.js';
|
||||
import { GfxControllerIdentifier } from '../../gfx/identifiers.js';
|
||||
import { TestEditorContainer } from '../test-editor.js';
|
||||
import { TestLocalElement } from '../test-gfx-element.js';
|
||||
import {
|
||||
RootBlockSchemaExtension,
|
||||
type SurfaceBlockModel,
|
||||
SurfaceBlockSchemaExtension,
|
||||
TestGfxBlockSchemaExtension,
|
||||
} from '../test-schema.js';
|
||||
import { testSpecs } from '../test-spec.js';
|
||||
|
||||
effects();
|
||||
|
||||
const extensions = [
|
||||
RootBlockSchemaExtension,
|
||||
SurfaceBlockSchemaExtension,
|
||||
TestGfxBlockSchemaExtension,
|
||||
];
|
||||
|
||||
function createTestOptions() {
|
||||
const idGenerator = createAutoIncrementIdGenerator();
|
||||
return { id: 'test-collection', idGenerator };
|
||||
}
|
||||
|
||||
const commonSetup = async () => {
|
||||
const collection = new TestWorkspace(createTestOptions());
|
||||
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc('home');
|
||||
const store = doc.getStore({ extensions });
|
||||
doc.load();
|
||||
|
||||
const rootId = store.addBlock('test:page');
|
||||
const surfaceId = store.addBlock('test:surface', {}, rootId);
|
||||
|
||||
const surfaceBlock = store.getBlock(surfaceId)!;
|
||||
|
||||
const editorContainer = new TestEditorContainer();
|
||||
editorContainer.doc = store;
|
||||
editorContainer.specs = testSpecs;
|
||||
document.body.append(editorContainer);
|
||||
|
||||
await editorContainer.updateComplete;
|
||||
|
||||
const gfx = editorContainer.std.get(GfxControllerIdentifier);
|
||||
|
||||
return {
|
||||
gfx,
|
||||
surfaceId,
|
||||
rootId,
|
||||
surfaceModel: surfaceBlock.model as SurfaceBlockModel,
|
||||
};
|
||||
};
|
||||
|
||||
describe('gfx element view basic', () => {
|
||||
test('view should be created', async () => {
|
||||
const { gfx, surfaceModel } = await commonSetup();
|
||||
|
||||
const id = surfaceModel.addElement({
|
||||
type: 'testShape',
|
||||
});
|
||||
const shapeView = gfx.view.get(id);
|
||||
|
||||
expect(shapeView).not.toBeNull();
|
||||
expect(shapeView!.model.id).toBe(id);
|
||||
expect(shapeView!.isConnected).toBe(true);
|
||||
});
|
||||
|
||||
test('view should be removed', async () => {
|
||||
const { gfx, surfaceModel } = await commonSetup();
|
||||
|
||||
const id = surfaceModel.addElement({
|
||||
type: 'testShape',
|
||||
});
|
||||
const shapeView = gfx.view.get(id);
|
||||
|
||||
expect(shapeView).not.toBeNull();
|
||||
expect(shapeView!.model.id).toBe(id);
|
||||
|
||||
surfaceModel.deleteElement(id);
|
||||
expect(gfx.view.get(id)).toBeNull();
|
||||
expect(shapeView!.isConnected).toBe(false);
|
||||
});
|
||||
|
||||
test('query gfx block view should work', async () => {
|
||||
const { gfx, surfaceId, rootId } = await commonSetup();
|
||||
|
||||
const waitGfxViewConnected = (id: string) => {
|
||||
const { promise, resolve } = Promise.withResolvers<void>();
|
||||
const subscription = gfx.std.view.viewUpdated.subscribe(payload => {
|
||||
if (
|
||||
payload.id === id &&
|
||||
payload.type === 'block' &&
|
||||
payload.method === 'add'
|
||||
) {
|
||||
subscription.unsubscribe();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
const id = gfx.std.store.addBlock('test:gfx-block', undefined, surfaceId);
|
||||
await waitGfxViewConnected(id);
|
||||
const gfxBlockView = gfx.view.get(id);
|
||||
expect(gfxBlockView).not.toBeNull();
|
||||
|
||||
const rootView = gfx.view.get(rootId);
|
||||
// root is not a gfx block, so it should be null
|
||||
expect(rootView).toBeNull();
|
||||
});
|
||||
|
||||
test('local element view should be created', async () => {
|
||||
const { gfx, surfaceModel } = await commonSetup();
|
||||
const localElement = new TestLocalElement(surfaceModel);
|
||||
localElement.id = 'test-local-element';
|
||||
|
||||
surfaceModel.addLocalElement(localElement);
|
||||
|
||||
const localView = gfx.view.get(localElement);
|
||||
expect(localView).not.toBeNull();
|
||||
expect(localView!.isConnected).toBe(true);
|
||||
|
||||
surfaceModel.deleteLocalElement(localElement);
|
||||
expect(localView!.isConnected).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,11 +1,13 @@
|
||||
import { html } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
|
||||
import { BlockComponent } from '../view/index.js';
|
||||
import { BlockComponent, GfxBlockComponent } from '../view/index.js';
|
||||
import type {
|
||||
HeadingBlockModel,
|
||||
NoteBlockModel,
|
||||
RootBlockModel,
|
||||
SurfaceBlockModel,
|
||||
TestGfxBlockModel,
|
||||
} from './test-schema.js';
|
||||
|
||||
@customElement('test-root-block')
|
||||
@@ -39,3 +41,21 @@ export class HeadingH2BlockComponent extends BlockComponent<HeadingBlockModel> {
|
||||
return html` <div class="test-heading-block h2">${this.model.text}</div> `;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('test-surface-block')
|
||||
export class SurfaceBlockComponent extends BlockComponent<SurfaceBlockModel> {
|
||||
override renderBlock() {
|
||||
return html`
|
||||
<div class="test-surface-block">${this.renderChildren(this.model)}</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@customElement('test-gfx-block')
|
||||
export class TestGfxBlockComponent extends GfxBlockComponent<TestGfxBlockModel> {
|
||||
override renderGfxBlock() {
|
||||
return html`
|
||||
<div class="test-gfx-block">${this.renderChildren(this.model)}</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
44
blocksuite/framework/std/src/__tests__/test-gfx-element.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import type { SerializedXYWH } from '@blocksuite/global/gfx';
|
||||
|
||||
import {
|
||||
convert,
|
||||
derive,
|
||||
field,
|
||||
GfxLocalElementModel,
|
||||
GfxPrimitiveElementModel,
|
||||
} from '../gfx/index.js';
|
||||
|
||||
export class TestShapeElement extends GfxPrimitiveElementModel {
|
||||
get type() {
|
||||
return 'testShape';
|
||||
}
|
||||
|
||||
@field()
|
||||
accessor rotate: number = 0;
|
||||
|
||||
@field()
|
||||
accessor xywh: SerializedXYWH = '[0,0,10,10]';
|
||||
|
||||
@convert(val => {
|
||||
if (['rect', 'triangle'].includes(val)) {
|
||||
return val;
|
||||
}
|
||||
|
||||
return 'rect';
|
||||
})
|
||||
@derive(val => {
|
||||
if (val === 'triangle') {
|
||||
return {
|
||||
rotate: 0,
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
})
|
||||
@field()
|
||||
accessor shapeType: 'rect' | 'triangle' = 'rect';
|
||||
}
|
||||
|
||||
export class TestLocalElement extends GfxLocalElementModel {
|
||||
override type: string = 'testLocal';
|
||||
}
|
||||
@@ -1,8 +1,17 @@
|
||||
import type { SerializedXYWH } from '@blocksuite/global/gfx';
|
||||
import {
|
||||
BlockModel,
|
||||
BlockSchemaExtension,
|
||||
defineBlockSchema,
|
||||
} from '@blocksuite/store';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import { SurfaceBlockModel as BaseSurfaceModel } from '../gfx/index.js';
|
||||
import {
|
||||
GfxCompatibleBlockModel,
|
||||
type GfxCompatibleProps,
|
||||
} from '../gfx/model/gfx-block-model.js';
|
||||
import { TestShapeElement } from './test-gfx-element.js';
|
||||
|
||||
export const RootBlockSchema = defineBlockSchema({
|
||||
flavour: 'test:page',
|
||||
@@ -15,7 +24,7 @@ export const RootBlockSchema = defineBlockSchema({
|
||||
metadata: {
|
||||
version: 2,
|
||||
role: 'root',
|
||||
children: ['test:note'],
|
||||
children: ['test:note', 'test:surface'],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -61,3 +70,58 @@ export const HeadingBlockSchemaExtension =
|
||||
export class HeadingBlockModel extends BlockModel<
|
||||
ReturnType<(typeof HeadingBlockSchema)['model']['props']>
|
||||
> {}
|
||||
|
||||
export const SurfaceBlockSchema = defineBlockSchema({
|
||||
flavour: 'test:surface',
|
||||
props: internal => ({
|
||||
elements: internal.Boxed<Y.Map<Y.Map<unknown>>>(new Y.Map()),
|
||||
}),
|
||||
metadata: {
|
||||
version: 1,
|
||||
role: 'hub',
|
||||
parent: ['test:page'],
|
||||
},
|
||||
toModel: () => new SurfaceBlockModel(),
|
||||
});
|
||||
|
||||
export const SurfaceBlockSchemaExtension =
|
||||
BlockSchemaExtension(SurfaceBlockSchema);
|
||||
|
||||
export class SurfaceBlockModel extends BaseSurfaceModel {
|
||||
override _init() {
|
||||
this._extendElement({
|
||||
testShape: TestShapeElement,
|
||||
});
|
||||
super._init();
|
||||
}
|
||||
}
|
||||
|
||||
type GfxTestBlockProps = {
|
||||
xywh: SerializedXYWH;
|
||||
rotate: number;
|
||||
index: string;
|
||||
} & GfxCompatibleProps;
|
||||
|
||||
export const TestGfxBlockSchema = defineBlockSchema({
|
||||
flavour: 'test:gfx-block',
|
||||
props: () =>
|
||||
({
|
||||
xywh: '[0,0,10,10]' as SerializedXYWH,
|
||||
rotate: 0,
|
||||
index: 'a0',
|
||||
lockedBySelf: false,
|
||||
}) as GfxTestBlockProps,
|
||||
metadata: {
|
||||
version: 1,
|
||||
role: 'content',
|
||||
parent: ['test:surface'],
|
||||
},
|
||||
toModel: () => new TestGfxBlockModel(),
|
||||
});
|
||||
|
||||
export const TestGfxBlockSchemaExtension =
|
||||
BlockSchemaExtension(TestGfxBlockSchema);
|
||||
|
||||
export class TestGfxBlockModel extends GfxCompatibleBlockModel<GfxTestBlockProps>(
|
||||
BlockModel
|
||||
) {}
|
||||
|
||||
@@ -20,4 +20,8 @@ export const testSpecs: ExtensionType[] = [
|
||||
|
||||
return literal`test-h2-block`;
|
||||
}),
|
||||
|
||||
BlockViewExtension('test:surface', literal`test-surface-block`),
|
||||
|
||||
BlockViewExtension('test:gfx-block', literal`test-gfx-block`),
|
||||
];
|
||||
|
||||