mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-04-17 19:08:06 +00:00
feat: add some init codes
This commit is contained in:
parent
738f7af6de
commit
d1702161c1
@ -30,10 +30,8 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@alilc/lowcode-core": "workspace:*",
|
||||
"@alilc/lowcode-engine-core": "workspace:*",
|
||||
"@alilc/lowcode-shared": "workspace:*",
|
||||
"@alilc/lowcode-types": "workspace:*",
|
||||
"@alilc/lowcode-utils": "workspace:*",
|
||||
"@alifd/next": "^1.27.8",
|
||||
"classnames": "^2.5.1",
|
||||
"lodash-es": "^4.17.20",
|
||||
@ -55,10 +53,8 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@alifd/next": "^1.27.8",
|
||||
"@alilc/lowcode-core": "workspace:*",
|
||||
"@alilc/lowcode-engine-core": "workspace:*",
|
||||
"@alilc/lowcode-shared": "workspace:*",
|
||||
"@alilc/lowcode-types": "workspace:*",
|
||||
"@alilc/lowcode-utils": "workspace:*",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { signal, uniqueId, type Spec } from '@alilc/lowcode-shared';
|
||||
import { signal, uniqueId, ComponentTreeRoot } from '@alilc/lowcode-shared';
|
||||
import { type Project } from '../project';
|
||||
import { History } from './history';
|
||||
|
||||
export interface DocumentSchema extends Spec.ComponentTreeRoot {
|
||||
export interface DocumentSchema extends ComponentTreeRoot {
|
||||
id: string;
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Spec } from '@alilc/lowcode-shared';
|
||||
import { ComponentNode } from '@alilc/lowcode-shared';
|
||||
import { type ComponentMeta } from '../component-meta';
|
||||
import { type Prop } from './prop';
|
||||
|
||||
export interface Node<Schema extends Spec.ComponentNode = Spec.ComponentNode> {
|
||||
export interface Node<Schema extends ComponentNode = ComponentNode> {
|
||||
/**
|
||||
* 节点 id
|
||||
* node id
|
||||
@ -353,6 +353,6 @@ export interface Node<Schema extends Spec.ComponentNode = Spec.ComponentNode> {
|
||||
};
|
||||
}
|
||||
|
||||
export function createNode<Schema extends Spec.ComponentNode>(nodeSchema: Schema): Node<Schema> {
|
||||
export function createNode<Schema extends ComponentNode>(nodeSchema: Schema): Node<Schema> {
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
export class DocumentModel {
|
||||
a = 1;
|
||||
c = {};
|
||||
constructor() {
|
||||
const b = { x: { y: 2 } };
|
||||
const c: number = 2;
|
||||
this.a = b?.x?.y;
|
||||
}
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
export class Node2 {
|
||||
a = 1;
|
||||
c = {};
|
||||
constructor() {
|
||||
const b = { x: { y: 2 } };
|
||||
const c: number = 2;
|
||||
this.a = b?.x?.y;
|
||||
}
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
import set from 'lodash/set';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import '../fixtures/window';
|
||||
import { Project } from '../../src/project/project';
|
||||
// import { Node } from '../../../src/document/node/node';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import { getIdsFromSchema, getNodeFromSchemaById } from '../utils';
|
||||
|
||||
const mockCreateSettingEntry = jest.fn();
|
||||
jest.mock('../../src/designer/designer', () => {
|
||||
return {
|
||||
Designer: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
getComponentMeta() {
|
||||
return {
|
||||
getMetadata() {
|
||||
return { configure: { advanced: null } };
|
||||
},
|
||||
};
|
||||
},
|
||||
transformProps(props) { return props; },
|
||||
createSettingEntry: mockCreateSettingEntry,
|
||||
postEvent() {},
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let designer = null;
|
||||
beforeAll(() => {
|
||||
designer = new Designer({});
|
||||
});
|
||||
|
||||
it.todo('在同一个节点下,相同名称的 slot 只能有一个', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
expect(nodesMap.size).toBe(expectedNodeCnt);
|
||||
ids.forEach(id => {
|
||||
expect(nodesMap.get(id).componentName).toBe(getNodeFromSchemaById(formSchema, id).componentName);
|
||||
});
|
||||
|
||||
const exportSchema = currentDocument?.export(1);
|
||||
expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt);
|
||||
expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt);
|
||||
});
|
||||
@ -1,72 +0,0 @@
|
||||
import { Editor } from '@alilc/lowcode-editor-core';
|
||||
import { IPublicEnumTransformStage } from '@alilc/lowcode-types';
|
||||
import { isPlainObject, isVariable, isJSBlock } from '@alilc/lowcode-utils';
|
||||
import '../fixtures/window';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import { DocumentModel } from '../../src/document/document-model';
|
||||
import { Project } from '../../src/project/project';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
/**
|
||||
* bug 背景:
|
||||
* Prop 在每次 setValue 时都会调用 dispose 方法用于重新计算子 Prop,我认为在 Node 未完成初始化之前的 dispose 都是
|
||||
* 无意义的,所以增加了判断条件来调用 dispose,结果导致了 variable 结果没有正确转成 JSExpression 结构。
|
||||
*
|
||||
* 因为 propsReducer 的 Init / Upgrade 阶段依然可以更改 props,且此时的 Node 也未完成初始化,不调用 dispose 则导致新的 Prop 结构无法生效
|
||||
*/
|
||||
|
||||
function upgradePropsReducer(props: any): any {
|
||||
if (!props || !isPlainObject(props)) {
|
||||
return props;
|
||||
}
|
||||
|
||||
if (isJSBlock(props)) {
|
||||
if (props.value.componentName === 'Slot') {
|
||||
return {
|
||||
type: 'JSSlot',
|
||||
title: (props.value.props as any)?.slotTitle,
|
||||
name: (props.value.props as any)?.slotName,
|
||||
value: props.value.children,
|
||||
};
|
||||
} else {
|
||||
return props.value;
|
||||
}
|
||||
}
|
||||
if (isVariable(props)) {
|
||||
return {
|
||||
type: 'JSExpression',
|
||||
value: props.variable,
|
||||
mock: props.value,
|
||||
};
|
||||
}
|
||||
const newProps: any = {};
|
||||
Object.keys(props).forEach((key) => {
|
||||
if (/^__slot__/.test(key) && props[key] === true) {
|
||||
return;
|
||||
}
|
||||
newProps[key] = upgradePropsReducer(props[key]);
|
||||
});
|
||||
return newProps;
|
||||
}
|
||||
|
||||
describe('Node 方法测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
|
||||
it('原始 prop 值是 variable 结构,通过一个 propsReducer 转成了 JSExpression 结构', () => {
|
||||
editor = new Editor();
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
designer.addPropsReducer(upgradePropsReducer, IPublicEnumTransformStage.Upgrade);
|
||||
project = designer.project;
|
||||
doc = new DocumentModel(project, formSchema);
|
||||
|
||||
const form = doc.getNode('form');
|
||||
expect(form.getPropValue('dataSource')).toEqual({
|
||||
type: 'JSExpression',
|
||||
value: 'state.formData',
|
||||
})
|
||||
});
|
||||
});
|
||||
@ -1,6 +0,0 @@
|
||||
背景:
|
||||
在 UT 的基础上,希望借助一些 Bug 修复来完成场景测试,从而进一步增强稳定性。
|
||||
至少在真正的 E2E 测试来临之前,我们保证不会重复犯两次相同的错误。
|
||||
|
||||
做法:
|
||||
Bugs 文件夹每个文件记录一个 bug 修复的场景测试~
|
||||
@ -1,127 +0,0 @@
|
||||
import '../../fixtures/window';
|
||||
import { Editor, globalContext } from '@alilc/lowcode-editor-core';
|
||||
import { Project } from '../../../src/project/project';
|
||||
import { DocumentModel } from '../../../src/document/document-model';
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import DragResizeEngine from '../../../src/builtin-simulator/bem-tools/drag-resize-engine';
|
||||
import formSchema from '../../fixtures/schema/form';
|
||||
import { fireEvent, createEvent } from '@testing-library/react';
|
||||
import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
describe('DragResizeEngine 测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
let resizeEngine: DragResizeEngine;
|
||||
|
||||
beforeAll(() => {
|
||||
editor = new Editor();
|
||||
!globalContext.has(Editor) && globalContext.register(editor, Editor);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
project = designer.project;
|
||||
doc = project.createDocument(formSchema);
|
||||
doc.open();
|
||||
resizeEngine = new DragResizeEngine(designer);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
project.mountSimulator(undefined);
|
||||
designer.purge();
|
||||
resizeEngine = null;
|
||||
designer = null;
|
||||
project = null;
|
||||
});
|
||||
|
||||
it('from', () => {
|
||||
const resizeStartMockFn = jest.fn();
|
||||
const resizeMockFn = jest.fn();
|
||||
const resizeEndMockFn = jest.fn();
|
||||
|
||||
const offResizeStart = resizeEngine.onResizeStart(resizeStartMockFn);
|
||||
const offResize = resizeEngine.onResize(resizeMockFn);
|
||||
const offResizeEnd = resizeEngine.onResizeEnd(resizeEndMockFn);
|
||||
const boostedNode = doc.getNode('node_k1ow3cbn');
|
||||
const mockBoostFn = jest
|
||||
.fn((e) => {
|
||||
return boostedNode;
|
||||
});
|
||||
|
||||
// do nothing
|
||||
const noop = resizeEngine.from();
|
||||
noop();
|
||||
|
||||
const offFrom = resizeEngine.from(document, 'e', mockBoostFn);
|
||||
|
||||
const mouseDownEvt = createEvent.mouseDown(document, { clientX: 100, clientY: 100 });
|
||||
fireEvent(document, mouseDownEvt);
|
||||
|
||||
expect(resizeStartMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(resizeStartMockFn.mock.calls[0][0]).toBe(mouseDownEvt);
|
||||
expect(resizeStartMockFn.mock.calls[0][1]).toBe('e');
|
||||
expect(resizeStartMockFn.mock.calls[0][2]).toBe(boostedNode);
|
||||
expect(resizeEngine.isDragResizing()).toBeTruthy();
|
||||
|
||||
const mouseMoveEvt1 = createEvent.mouseMove(document, { clientX: 108, clientY: 108 });
|
||||
fireEvent(document, mouseMoveEvt1);
|
||||
expect(resizeMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(resizeMockFn.mock.calls[0][0]).toBe(mouseMoveEvt1);
|
||||
expect(resizeMockFn.mock.calls[0][1]).toBe('e');
|
||||
expect(resizeMockFn.mock.calls[0][2]).toBe(boostedNode);
|
||||
expect(resizeMockFn.mock.calls[0][3]).toBe(8);
|
||||
expect(resizeMockFn.mock.calls[0][4]).toBe(8);
|
||||
|
||||
const mouseMoveEvt2 = createEvent.mouseMove(document, { clientX: 110, clientY: 110 }, 10, 10);
|
||||
fireEvent(document, mouseMoveEvt2);
|
||||
expect(resizeMockFn).toHaveBeenCalledTimes(2);
|
||||
expect(resizeMockFn.mock.calls[1][0]).toBe(mouseMoveEvt2);
|
||||
expect(resizeMockFn.mock.calls[1][1]).toBe('e');
|
||||
expect(resizeMockFn.mock.calls[1][2]).toBe(boostedNode);
|
||||
expect(resizeMockFn.mock.calls[1][3]).toBe(10);
|
||||
expect(resizeMockFn.mock.calls[1][4]).toBe(10);
|
||||
|
||||
const mouseUpEvt = createEvent.mouseUp(document, { clientX: 118, clientY: 118 });
|
||||
fireEvent(document, mouseUpEvt);
|
||||
|
||||
expect(resizeEndMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(resizeEndMockFn.mock.calls[0][0]).toBe(mouseUpEvt);
|
||||
expect(resizeEndMockFn.mock.calls[0][1]).toBe('e');
|
||||
expect(resizeEndMockFn.mock.calls[0][2]).toBe(boostedNode);
|
||||
expect(resizeEngine.isDragResizing()).toBeFalsy();
|
||||
|
||||
offResizeStart();
|
||||
offResize();
|
||||
offResizeEnd();
|
||||
resizeStartMockFn.mockClear();
|
||||
resizeMockFn.mockClear();
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 100, clientY: 100 });
|
||||
expect(resizeMockFn).not.toHaveBeenCalled();
|
||||
|
||||
offFrom();
|
||||
fireEvent.mouseDown(document, { clientX: 100, clientY: 100 });
|
||||
expect(resizeStartMockFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('has sensor', () => {
|
||||
const mockDoc = document.createElement('iframe').contentWindow?.document;
|
||||
project.mountSimulator({
|
||||
sensorAvailable: true,
|
||||
contentDocument: document,
|
||||
});
|
||||
|
||||
const mockBoostFn = jest
|
||||
.fn((e) => {
|
||||
return doc.getNode('node_k1ow3cbn');
|
||||
});
|
||||
|
||||
const offFrom = resizeEngine.from(document, 'e', mockBoostFn);
|
||||
|
||||
// TODO: 想办法 mock 一个 iframe.currentDocument
|
||||
fireEvent.mouseDown(document, { clientX: 100, clientY: 100 });
|
||||
});
|
||||
});
|
||||
@ -1,50 +0,0 @@
|
||||
import '../../fixtures/window';
|
||||
import { Editor } from '@alilc/lowcode-editor-core';
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import { BemToolsManager } from '../../../src/builtin-simulator/bem-tools/manager';
|
||||
import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
describe('Node 方法测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
// let project: Project;
|
||||
// let doc: DocumentModel;
|
||||
let manager: BemToolsManager;
|
||||
|
||||
beforeEach(() => {
|
||||
editor = new Editor();
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
// project = designer.project;
|
||||
// doc = new DocumentModel(project, formSchema);
|
||||
manager = new BemToolsManager(designer);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// project.unload();
|
||||
designer.purge();
|
||||
editor = null;
|
||||
designer = null;
|
||||
// project = null;
|
||||
});
|
||||
|
||||
it('addBemTools / removeBemTools / getAllBemTools', () => {
|
||||
manager.addBemTools({
|
||||
name: 't1',
|
||||
item: (props: any) => { return <div />; },
|
||||
});
|
||||
expect(manager.getAllBemTools().length).toBe(1);
|
||||
|
||||
expect(() => {
|
||||
manager.addBemTools({
|
||||
name: 't1',
|
||||
item: (props: any) => { return <div />; },
|
||||
});
|
||||
}).toThrow(/already exists/);
|
||||
|
||||
manager.removeBemTools('t2');
|
||||
expect(manager.getAllBemTools().length).toBe(1);
|
||||
|
||||
manager.removeBemTools('t1');
|
||||
expect(manager.getAllBemTools().length).toBe(0);
|
||||
});
|
||||
});
|
||||
@ -1,510 +0,0 @@
|
||||
import { IPublicTypePluginMeta } from './../../../../lib/packages/types/src/shell/type/plugin-meta.d';
|
||||
import '../fixtures/window';
|
||||
import {
|
||||
Editor,
|
||||
globalContext,
|
||||
Hotkey as InnerHotkey,
|
||||
Setters as InnerSetters,
|
||||
} from '@alilc/lowcode-editor-core';
|
||||
import { Workspace as InnerWorkspace } from '@alilc/lowcode-workspace';
|
||||
import {
|
||||
AssetType,
|
||||
} from '@alilc/lowcode-utils';
|
||||
import {
|
||||
IPublicEnumDragObjectType,
|
||||
} from '@alilc/lowcode-types';
|
||||
import { Project } from '../../src/project/project';
|
||||
import pageMetadata from '../fixtures/component-metadata/page';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import { DocumentModel } from '../../src/document/document-model';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import { getMockDocument, getMockWindow, getMockEvent, delayObxTick } from '../utils';
|
||||
import { BuiltinSimulatorHost } from '../../src/builtin-simulator/host';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory';
|
||||
import { Setters, Workspace } from '@alilc/lowcode-shell';
|
||||
import { ILowCodePluginContextApiAssembler, ILowCodePluginContextPrivate, LowCodePluginManager } from '@alilc/lowcode-designer';
|
||||
import {
|
||||
Skeleton as InnerSkeleton,
|
||||
} from '@alilc/lowcode-editor-skeleton';
|
||||
|
||||
describe('Host 测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
let host: BuiltinSimulatorHost;
|
||||
|
||||
beforeAll(() => {
|
||||
editor = new Editor();
|
||||
const pluginContextApiAssembler: ILowCodePluginContextApiAssembler = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
assembleApis: (context: ILowCodePluginContextPrivate, pluginName: string, meta: IPublicTypePluginMeta) => {
|
||||
context.project = project;
|
||||
const eventPrefix = meta?.eventPrefix || 'common';
|
||||
context.workspace = workspace;
|
||||
},
|
||||
};
|
||||
const innerPlugins = new LowCodePluginManager(pluginContextApiAssembler);
|
||||
const innerWorkspace = new InnerWorkspace(() => {}, {});
|
||||
const workspace = new Workspace(innerWorkspace);
|
||||
const innerSkeleton = new InnerSkeleton(editor);
|
||||
editor.set('skeleton' as any, innerSkeleton);
|
||||
editor.set('innerHotkey', new InnerHotkey())
|
||||
editor.set('setters', new Setters(new InnerSetters()));
|
||||
editor.set('innerPlugins' as any, innerPlugins);
|
||||
!globalContext.has(Editor) && globalContext.register(editor, Editor);
|
||||
!globalContext.has('workspace') && globalContext.register(innerWorkspace, 'workspace');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
project = designer.project;
|
||||
designer.createComponentMeta(pageMetadata);
|
||||
doc = project.createDocument(formSchema);
|
||||
host = new BuiltinSimulatorHost(designer.project, designer);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
project.mountSimulator(undefined);
|
||||
designer._componentMetasMap.clear();
|
||||
designer.purge();
|
||||
host.purge();
|
||||
designer = null;
|
||||
project = null;
|
||||
host = null;
|
||||
});
|
||||
|
||||
describe('基础方法测试', () => {
|
||||
it('setProps / get / set', async () => {
|
||||
expect(host.currentDocument).toBe(designer.project.currentDocument);
|
||||
expect(host.renderEnv).toBe('default');
|
||||
expect(host.device).toBe('default');
|
||||
expect(host.deviceClassName).toBeUndefined();
|
||||
expect(host.requestHandlersMap).toBeNull();
|
||||
host.setProps({
|
||||
renderEnv: 'rax',
|
||||
device: 'mobile',
|
||||
deviceClassName: 'mobile-rocks',
|
||||
componentsAsset: [
|
||||
{
|
||||
type: AssetType.JSText,
|
||||
content: 'console.log(1)',
|
||||
},
|
||||
{
|
||||
type: AssetType.JSUrl,
|
||||
content: '//path/to/js',
|
||||
},
|
||||
],
|
||||
theme: {
|
||||
type: AssetType.CSSText,
|
||||
content: '.theme {font-size: 50px;}',
|
||||
},
|
||||
requestHandlersMap: {},
|
||||
});
|
||||
expect(host.renderEnv).toBe('rax');
|
||||
expect(host.device).toBe('mobile');
|
||||
expect(host.deviceClassName).toBe('mobile-rocks');
|
||||
expect(host.componentsAsset).toEqual([
|
||||
{
|
||||
type: AssetType.JSText,
|
||||
content: 'console.log(1)',
|
||||
},
|
||||
{
|
||||
type: AssetType.JSUrl,
|
||||
content: '//path/to/js',
|
||||
},
|
||||
]);
|
||||
expect(host.theme).toEqual({
|
||||
type: AssetType.CSSText,
|
||||
content: '.theme {font-size: 50px;}',
|
||||
});
|
||||
expect(host.componentsMap).toEqual(designer.componentsMap);
|
||||
expect(host.requestHandlersMap).toEqual({});
|
||||
|
||||
host.set('renderEnv', 'vue');
|
||||
expect(host.renderEnv).toBe('vue');
|
||||
|
||||
expect(host.getComponentContext).toThrow('Method not implemented.');
|
||||
});
|
||||
|
||||
it('connect', () => {
|
||||
const mockFn = jest.fn();
|
||||
const mockRenderer = { isSimulatorRenderer: true };
|
||||
host.connect(mockRenderer, mockFn);
|
||||
expect(host.renderer).toEqual(mockRenderer);
|
||||
|
||||
// await delayObxTick();
|
||||
expect(mockFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('mountViewport', () => {
|
||||
const mockBounds = {
|
||||
top: 10,
|
||||
bottom: 100,
|
||||
left: 10,
|
||||
right: 100,
|
||||
};
|
||||
host.mountViewport({
|
||||
getBoundingClientRect() {
|
||||
return mockBounds;
|
||||
},
|
||||
});
|
||||
expect(host.viewport.bounds).toEqual(mockBounds);
|
||||
});
|
||||
|
||||
it('autorun', () => {
|
||||
const mockFn = jest.fn();
|
||||
host.autorun(mockFn);
|
||||
expect(mockFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('purge', () => {
|
||||
host.purge();
|
||||
});
|
||||
|
||||
it('isEnter', () => {
|
||||
const mockBounds = {
|
||||
top: 10,
|
||||
bottom: 100,
|
||||
left: 10,
|
||||
right: 100,
|
||||
};
|
||||
host.mountViewport({
|
||||
getBoundingClientRect() {
|
||||
return mockBounds;
|
||||
},
|
||||
});
|
||||
expect(
|
||||
host.isEnter({
|
||||
globalX: 5,
|
||||
globalY: 50,
|
||||
}),
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
host.isEnter({
|
||||
globalX: 115,
|
||||
globalY: 50,
|
||||
}),
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
host.isEnter({
|
||||
globalX: 50,
|
||||
globalY: 50,
|
||||
}),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
host.isEnter({
|
||||
globalX: 50,
|
||||
globalY: 5,
|
||||
}),
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
host.isEnter({
|
||||
globalX: 50,
|
||||
globalY: 150,
|
||||
}),
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
host.isEnter({
|
||||
globalX: 150,
|
||||
globalY: 150,
|
||||
}),
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('fixEvent', () => {
|
||||
expect(host.fixEvent({ fixed: true, clientX: 1 })).toEqual({ fixed: true, clientX: 1 });
|
||||
});
|
||||
|
||||
it('findDOMNodes', () => {
|
||||
host.connect({
|
||||
findDOMNodes: () => {
|
||||
return null;
|
||||
},
|
||||
}, () => {});
|
||||
expect(host.findDOMNodes()).toBeNull();
|
||||
|
||||
const mockElems = [document.createElement('div')];
|
||||
host.connect({
|
||||
findDOMNodes: () => {
|
||||
return mockElems;
|
||||
},
|
||||
}, () => {});
|
||||
expect(host.findDOMNodes({})).toBe(mockElems);
|
||||
expect(host.findDOMNodes({}, 'xxx')).toBeNull();
|
||||
expect(host.findDOMNodes({}, 'div')).toEqual(mockElems);
|
||||
});
|
||||
|
||||
it('getClosestNodeInstance', () => {
|
||||
const mockFn = jest.fn(() => {
|
||||
return {
|
||||
node: {},
|
||||
nodeId: 'id',
|
||||
docId: 'docId',
|
||||
};
|
||||
});
|
||||
host.connect({
|
||||
getClosestNodeInstance: mockFn,
|
||||
}, () => {});
|
||||
expect(host.getClosestNodeInstance()).toEqual({
|
||||
node: {},
|
||||
nodeId: 'id',
|
||||
docId: 'docId',
|
||||
});
|
||||
});
|
||||
|
||||
it('getNodeInstanceFromElement', () => {
|
||||
expect(host.getNodeInstanceFromElement()).toBeNull();
|
||||
host.getClosestNodeInstance = () => {
|
||||
return null;
|
||||
};
|
||||
expect(host.getNodeInstanceFromElement({})).toBeNull();
|
||||
host.getClosestNodeInstance = () => {
|
||||
return {
|
||||
docId: project.currentDocument.id,
|
||||
nodeId: 'xxx',
|
||||
};
|
||||
};
|
||||
expect(host.getNodeInstanceFromElement({})).toBeTruthy();
|
||||
});
|
||||
|
||||
it('getDropContainer', () => {
|
||||
host.getNodeInstanceFromElement = () => {
|
||||
return {
|
||||
node: doc.rootNode,
|
||||
};
|
||||
};
|
||||
host.getDropContainer({
|
||||
target: {},
|
||||
dragObject: {
|
||||
type: IPublicEnumDragObjectType.Node,
|
||||
nodes: [doc.getNode('page')],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('getComponentInstances', () => {
|
||||
const mockNode = {
|
||||
document: { id: 'docId' },
|
||||
};
|
||||
host.instancesMap = {
|
||||
docId: {
|
||||
get() {
|
||||
return [{ comp: true }, { comp2: true }];
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(host.getComponentInstances(mockNode))
|
||||
.toEqual([{ comp: true }, { comp2: true }]);
|
||||
|
||||
const mockInst = { inst: true };
|
||||
host.getClosestNodeInstance = () => {
|
||||
return {
|
||||
instance: mockInst,
|
||||
};
|
||||
};
|
||||
expect(host.getComponentInstances(mockNode, { instance: mockInst }))
|
||||
.toEqual([{ comp: true }, { comp2: true }]);
|
||||
});
|
||||
|
||||
it('setNativeSelection / setDraggingState / setCopyState / clearState', () => {
|
||||
const mockFn1 = jest.fn();
|
||||
const mockFn2 = jest.fn();
|
||||
const mockFn3 = jest.fn();
|
||||
const mockFn4 = jest.fn();
|
||||
host.connect({
|
||||
setNativeSelection: mockFn1,
|
||||
setDraggingState: mockFn2,
|
||||
setCopyState: mockFn3,
|
||||
clearState: mockFn4,
|
||||
}, () => {});
|
||||
host.setNativeSelection(true);
|
||||
expect(mockFn1).toHaveBeenCalledWith(true);
|
||||
host.setDraggingState(false);
|
||||
expect(mockFn2).toHaveBeenCalledWith(false);
|
||||
host.setCopyState(true);
|
||||
expect(mockFn3).toHaveBeenCalledWith(true);
|
||||
host.clearState();
|
||||
expect(mockFn4).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('sensorAvailable / deactiveSensor', () => {
|
||||
expect(host.sensorAvailable).toBeTruthy();
|
||||
host.deactiveSensor();
|
||||
expect(host.sensing).toBeFalsy();
|
||||
});
|
||||
|
||||
it('getComponent', () => {
|
||||
host.connect({
|
||||
getComponent: () => {
|
||||
return {};
|
||||
},
|
||||
}, () => {});
|
||||
expect(host.getComponent()).toEqual({});
|
||||
expect(host.createComponent()).toBeNull();
|
||||
expect(host.setSuspense()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('setInstance', () => {
|
||||
host.instancesMap = {};
|
||||
host.setInstance('docId1', 'id1', [{}]);
|
||||
expect(host.instancesMap.docId1.get('id1')).toEqual([{}]);
|
||||
|
||||
host.setInstance('docId1', 'id1', null);
|
||||
expect(host.instancesMap.docId1.get('id1')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('locate 方法', () => {
|
||||
beforeEach(() => {
|
||||
const mockBounds = {
|
||||
top: 10,
|
||||
bottom: 100,
|
||||
left: 10,
|
||||
right: 100,
|
||||
};
|
||||
host.mountViewport({
|
||||
getBoundingClientRect() {
|
||||
return mockBounds;
|
||||
},
|
||||
});
|
||||
});
|
||||
it('locate,没有 nodes', () => {
|
||||
expect(host.locate({
|
||||
dragObject: {
|
||||
type: IPublicEnumDragObjectType.Node,
|
||||
nodes: [],
|
||||
},
|
||||
})).toBeUndefined();
|
||||
});
|
||||
it('locate,没有 document', () => {
|
||||
project.removeDocument(doc);
|
||||
expect(host.locate({
|
||||
dragObject: {
|
||||
type: IPublicEnumDragObjectType.Node,
|
||||
nodes: [doc.getNode('page')],
|
||||
},
|
||||
})).toBeNull();
|
||||
});
|
||||
it('notFoundComponent', () => {
|
||||
expect(host.locate({
|
||||
dragObject: {
|
||||
type: IPublicEnumDragObjectType.Node,
|
||||
nodes: [doc.getNode('form')],
|
||||
},
|
||||
})).toBeUndefined();
|
||||
})
|
||||
it('locate', () => {
|
||||
host.locate({
|
||||
dragObject: {
|
||||
type: IPublicEnumDragObjectType.Node,
|
||||
nodes: [doc.getNode('page')],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('事件测试', () => {
|
||||
it('setupDragAndClick', () => {});
|
||||
it('setupContextMenu', async () => {
|
||||
const mockDocument = getMockDocument();
|
||||
const mockWindow = getMockWindow(mockDocument);
|
||||
const mockIframe = {
|
||||
contentWindow: mockWindow,
|
||||
contentDocument: mockDocument,
|
||||
dispatchEvent() {},
|
||||
};
|
||||
|
||||
host.set('library', [
|
||||
{
|
||||
package: '@ali/vc-deep',
|
||||
library: 'lib',
|
||||
urls: ['a.js', 'b.js'],
|
||||
},
|
||||
]);
|
||||
|
||||
host.componentsConsumer.consume(() => {});
|
||||
host.injectionConsumer.consume(() => {});
|
||||
await host.mountContentFrame(mockIframe);
|
||||
|
||||
host.setupContextMenu();
|
||||
host.getNodeInstanceFromElement = () => {
|
||||
return {
|
||||
node: { componentMeta: { componentName: 'Button', getMetadata() { return {} } }, contains() {} },
|
||||
};
|
||||
};
|
||||
const mockFn = jest.fn();
|
||||
host.designer.editor.on('designer.builtinSimulator.contextmenu', mockFn);
|
||||
fireEvent.contextMenu(document, {});
|
||||
// TODO:
|
||||
// expect(mockFn).toHaveBeenCalledWith({ selected: 'Button' });
|
||||
});
|
||||
});
|
||||
|
||||
it('事件测试', async () => {
|
||||
const mockDocument = getMockDocument();
|
||||
const mockWindow = getMockWindow(mockDocument);
|
||||
const mockIframe = {
|
||||
contentWindow: mockWindow,
|
||||
contentDocument: mockDocument,
|
||||
dispatchEvent() {},
|
||||
};
|
||||
|
||||
// 非法分支测试
|
||||
host.mountContentFrame();
|
||||
expect(host._iframe).toBeUndefined();
|
||||
|
||||
host.set('library', [
|
||||
{
|
||||
package: '@ali/vc-deep',
|
||||
library: 'lib',
|
||||
urls: ['a.js', 'b.js'],
|
||||
},
|
||||
]);
|
||||
|
||||
host.componentsConsumer.consume(() => {});
|
||||
host.injectionConsumer.consume(() => {});
|
||||
await host.mountContentFrame(mockIframe);
|
||||
|
||||
expect(host.contentWindow).toBe(mockWindow);
|
||||
|
||||
mockDocument.triggerEventListener(
|
||||
'mouseover',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'mouseleave',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'mousedown',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'mouseup',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'mousemove',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener('click', getMockEvent(document.createElement('input')), host);
|
||||
mockDocument.triggerEventListener(
|
||||
'dblclick',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'contextmenu',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
import '../fixtures/window';
|
||||
import { getMockRenderer } from '../utils';
|
||||
import { isSimulatorRenderer } from '../../src/builtin-simulator/renderer';
|
||||
|
||||
describe('renderer 测试', () => {
|
||||
it('renderer', () => {
|
||||
expect(isSimulatorRenderer(getMockRenderer())).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -1,60 +0,0 @@
|
||||
import ResourceConsumer from '../../src/builtin-simulator/resource-consumer';
|
||||
import { delayObxTick, delay } from '../utils';
|
||||
|
||||
it('ResourceConsumer 测试,先消费再监听', async () => {
|
||||
const con = new ResourceConsumer(() => ({ a: 1, b: 2 }));
|
||||
|
||||
const mockFn = jest.fn();
|
||||
con.consume((data) => {
|
||||
mockFn(data);
|
||||
});
|
||||
|
||||
await delay(1000);
|
||||
|
||||
expect(mockFn).toHaveBeenCalledWith({ a: 1, b: 2 });
|
||||
con.consume(() => {});
|
||||
|
||||
await con.waitFirstConsume();
|
||||
|
||||
con.dispose();
|
||||
});
|
||||
|
||||
it('ResourceConsumer 测试,先消费再监听,isSimulatorRenderer', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const con = new ResourceConsumer(() => ({ a: 1, b: 2 }), () => {
|
||||
const o = { a: 3, b: 4 };
|
||||
mockFn(o);
|
||||
return o;
|
||||
});
|
||||
|
||||
con.consume({ isSimulatorRenderer: true });
|
||||
|
||||
await delay(1000);
|
||||
|
||||
expect(mockFn).toHaveBeenCalledWith({ a: 3, b: 4 });
|
||||
con.consume(() => {});
|
||||
|
||||
await con.waitFirstConsume();
|
||||
});
|
||||
|
||||
it('ResourceConsumer 测试,先消费再监听,isSimulatorRenderer,没有 consume', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const con = new ResourceConsumer(() => ({ a: 1, b: 2 }));
|
||||
|
||||
con.consume({ isSimulatorRenderer: true });
|
||||
});
|
||||
|
||||
it('ResourceConsumer 测试,先监听再消费', async () => {
|
||||
const con = new ResourceConsumer(() => ({ a: 1, b: 2 }));
|
||||
|
||||
con.waitFirstConsume();
|
||||
|
||||
const mockFn = jest.fn();
|
||||
con.consume((data) => {
|
||||
mockFn(data);
|
||||
});
|
||||
|
||||
await delay(1000);
|
||||
|
||||
expect(mockFn).toHaveBeenCalledWith({ a: 1, b: 2 });
|
||||
});
|
||||
@ -1,177 +0,0 @@
|
||||
import '../../fixtures/window';
|
||||
import PropTypes from 'prop-types';
|
||||
import { LowcodeTypes, parseMetadata, parseProps } from '../../../src/builtin-simulator/utils/parse-metadata';
|
||||
import { default as ReactPropTypesSecret } from 'prop-types/lib/ReactPropTypesSecret';
|
||||
|
||||
describe('parseMetadata', () => {
|
||||
it('parseMetadata', async () => {
|
||||
const md1 = parseMetadata('Div');
|
||||
const md2 = parseMetadata({ componentName: 'Div' });
|
||||
});
|
||||
it('LowcodeTypes.shape', async () => {
|
||||
const result = (window as any).PropTypes.shape()
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('LowcodeTypes basic type validators', () => {
|
||||
it('should validate string types', () => {
|
||||
const stringValidator = LowcodeTypes.string;
|
||||
// 对 stringValidator 进行测试
|
||||
const props = { testProp: 'This is a string' };
|
||||
const propName = 'testProp';
|
||||
const componentName = 'TestComponent';
|
||||
|
||||
const result = stringValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(result).toBeNull(); // No error for valid string
|
||||
});
|
||||
|
||||
it('should fail with a non-string type', () => {
|
||||
const stringValidator = LowcodeTypes.string;
|
||||
const props = { testProp: 42 };
|
||||
const propName = 'testProp';
|
||||
const componentName = 'TestComponent';
|
||||
|
||||
const result = stringValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(result).toBeInstanceOf(Error); // Error for non-string type
|
||||
expect(result.message).toContain('Invalid prop `testProp` of type `number` supplied to `TestComponent`, expected `string`.');
|
||||
});
|
||||
|
||||
it('should pass with a valid number', () => {
|
||||
const numberValidator = LowcodeTypes.number;
|
||||
const props = { testProp: 42 };
|
||||
const propName = 'testProp';
|
||||
const componentName = 'TestComponent';
|
||||
|
||||
const result = numberValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(result).toBeNull(); // No error for valid number
|
||||
});
|
||||
|
||||
it('should fail with a non-number type', () => {
|
||||
const numberValidator = LowcodeTypes.number;
|
||||
const props = { testProp: 'Not a number' };
|
||||
const propName = 'testProp';
|
||||
const componentName = 'TestComponent';
|
||||
|
||||
const result = numberValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(result).toBeInstanceOf(Error); // Error for non-number type
|
||||
expect(result.message).toContain('Invalid prop `testProp` of type `string` supplied to `TestComponent`, expected `number`.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Custom type constructors', () => {
|
||||
it('should create a custom type validator using define', () => {
|
||||
const customType = LowcodeTypes.define(PropTypes.string, 'customType');
|
||||
const props = { testProp: 'This is a string' };
|
||||
const propName = 'testProp';
|
||||
const componentName = 'TestComponent';
|
||||
|
||||
// 测试有效值
|
||||
const validResult = customType(props, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(validResult).toBeNull(); // No error for valid string
|
||||
|
||||
// 测试无效值
|
||||
const invalidProps = { testProp: 42 };
|
||||
const invalidResult = customType(invalidProps, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(invalidResult).toBeInstanceOf(Error); // Error for non-string type
|
||||
|
||||
// 验证 lowcodeType 属性
|
||||
expect(customType.lowcodeType).toEqual('customType');
|
||||
|
||||
// 验证 isRequired 属性
|
||||
const requiredResult = customType.isRequired(invalidProps, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(requiredResult).toBeInstanceOf(Error); // Error for non-string type
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('Advanced type constructors', () => {
|
||||
describe('oneOf Type Validator', () => {
|
||||
const oneOfValidator = LowcodeTypes.oneOf(['red', 'green', 'blue']);
|
||||
const propName = 'color';
|
||||
const componentName = 'ColorPicker';
|
||||
|
||||
it('should pass with a valid value', () => {
|
||||
const props = { color: 'red' };
|
||||
const result = oneOfValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(result).toBeNull(); // No error for valid value
|
||||
});
|
||||
|
||||
it('should fail with an invalid value', () => {
|
||||
const props = { color: 'yellow' };
|
||||
const result = oneOfValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(result).toBeInstanceOf(Error); // Error for invalid value
|
||||
expect(result.message).toContain(`Invalid prop \`${propName}\` of value \`yellow\` supplied to \`${componentName}\`, expected one of ["red","green","blue"].`);
|
||||
});
|
||||
|
||||
it('should fail with a non-existing value', () => {
|
||||
const props = { color: 'others' };
|
||||
const result = oneOfValidator(props, propName, componentName, 'prop', null, ReactPropTypesSecret);
|
||||
expect(result).toBeInstanceOf(Error); // Error for non-existing value
|
||||
expect(result.message).toContain(`Invalid prop \`${propName}\` of value \`others\` supplied to \`${componentName}\`, expected one of ["red","green","blue"].`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('parseProps function', () => {
|
||||
it('should correctly parse propTypes and defaultProps', () => {
|
||||
const component = {
|
||||
propTypes: {
|
||||
name: LowcodeTypes.string,
|
||||
age: LowcodeTypes.number,
|
||||
},
|
||||
defaultProps: {
|
||||
name: 'John Doe',
|
||||
age: 30,
|
||||
},
|
||||
};
|
||||
const parsedProps = parseProps(component);
|
||||
|
||||
// 测试结果长度
|
||||
expect(parsedProps.length).toBe(2);
|
||||
|
||||
// 测试 name 属性
|
||||
const nameProp: any = parsedProps.find(prop => prop.name === 'name');
|
||||
expect(nameProp).toBeDefined();
|
||||
expect(nameProp.propType).toEqual('string');
|
||||
expect(nameProp.defaultValue).toEqual('John Doe');
|
||||
|
||||
// 测试 age 属性
|
||||
const ageProp: any = parsedProps.find(prop => prop.name === 'age');
|
||||
expect(ageProp).toBeDefined();
|
||||
expect(ageProp.propType).toEqual('number');
|
||||
expect(ageProp.defaultValue).toEqual(30);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseProps function', () => {
|
||||
it('should correctly parse propTypes and defaultProps', () => {
|
||||
const component = {
|
||||
propTypes: {
|
||||
name: LowcodeTypes.string,
|
||||
age: LowcodeTypes.number,
|
||||
},
|
||||
defaultProps: {
|
||||
name: 'John Doe',
|
||||
age: 30,
|
||||
},
|
||||
};
|
||||
const parsedProps = parseProps(component);
|
||||
|
||||
// 测试结果长度
|
||||
expect(parsedProps.length).toBe(2);
|
||||
|
||||
// 测试 name 属性
|
||||
const nameProp: any = parsedProps.find(prop => prop.name === 'name');
|
||||
expect(nameProp).toBeDefined();
|
||||
expect(nameProp.propType).toEqual('string');
|
||||
expect(nameProp.defaultValue).toEqual('John Doe');
|
||||
|
||||
// 测试 age 属性
|
||||
const ageProp: any = parsedProps.find(prop => prop.name === 'age');
|
||||
expect(ageProp).toBeDefined();
|
||||
expect(ageProp.propType).toEqual('number');
|
||||
expect(ageProp.defaultValue).toEqual(30);
|
||||
});
|
||||
});
|
||||
@ -1,78 +0,0 @@
|
||||
import {
|
||||
generateComponentName,
|
||||
getNormalizedImportPath,
|
||||
isPackagePath,
|
||||
toTitleCase,
|
||||
makeRelativePath,
|
||||
removeVersion,
|
||||
resolveAbsoluatePath,
|
||||
joinPath,
|
||||
} from '../../../src/builtin-simulator/utils/path';
|
||||
|
||||
describe('builtin-simulator/utils/path 测试', () => {
|
||||
it('isPackagePath', () => {
|
||||
expect(isPackagePath('a')).toBeTruthy();
|
||||
expect(isPackagePath('@ali/a')).toBeTruthy();
|
||||
expect(isPackagePath('@alife/a')).toBeTruthy();
|
||||
expect(isPackagePath('a.b')).toBeTruthy();
|
||||
expect(isPackagePath('./a')).toBeFalsy();
|
||||
expect(isPackagePath('../a')).toBeFalsy();
|
||||
expect(isPackagePath('/a')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('toTitleCase', () => {
|
||||
expect(toTitleCase('a')).toBe('A');
|
||||
expect(toTitleCase('a_b')).toBe('AB');
|
||||
expect(toTitleCase('a b')).toBe('AB');
|
||||
expect(toTitleCase('a-b')).toBe('AB');
|
||||
expect(toTitleCase('a.b')).toBe('AB');
|
||||
expect(toTitleCase('a.b.cx')).toBe('ABCx');
|
||||
});
|
||||
|
||||
it('generateComponentName', () => {
|
||||
expect(generateComponentName('a/index.js')).toBe('A');
|
||||
expect(generateComponentName('a_b/index.js')).toBe('AB');
|
||||
expect(generateComponentName('a_b/index.web.js')).toBe('AB');
|
||||
expect(generateComponentName('a_b/index.xxx.js')).toBe('AB');
|
||||
expect(generateComponentName('a_b')).toBe('AB');
|
||||
expect(generateComponentName('')).toBe('Component');
|
||||
});
|
||||
|
||||
it('getNormalizedImportPath', () => {
|
||||
expect(getNormalizedImportPath('/a')).toBe('/a');
|
||||
expect(getNormalizedImportPath('/a/')).toBe('/a/');
|
||||
expect(getNormalizedImportPath('/a/index.js')).toBe('/a');
|
||||
expect(getNormalizedImportPath('/a/index.ts')).toBe('/a');
|
||||
expect(getNormalizedImportPath('/a/index.jsx')).toBe('/a');
|
||||
expect(getNormalizedImportPath('/a/index.tsx')).toBe('/a');
|
||||
expect(getNormalizedImportPath('/a/index.x')).toBe('/a/index.x');
|
||||
});
|
||||
|
||||
it('makeRelativePath', () => {
|
||||
expect(makeRelativePath('/a/b/c', '/a/b')).toBe('c');
|
||||
expect(makeRelativePath('a/b/c', '/a/c')).toBe('a/b/c');
|
||||
expect(makeRelativePath('/a/b/c', '/a/c')).toBe('./b/c');
|
||||
expect(makeRelativePath('/a/b/c', '/a/c/d')).toBe('../b/c');
|
||||
});
|
||||
|
||||
it('resolveAbsoluatePath', () => {
|
||||
expect(resolveAbsoluatePath('/a/b/c', '/a')).toBe('/a/b/c');
|
||||
expect(resolveAbsoluatePath('@ali/fe', '/a')).toBe('@ali/fe');
|
||||
expect(resolveAbsoluatePath('./a/b', '/c')).toBe('/c/a/b');
|
||||
expect(resolveAbsoluatePath('./a/b/d', '/c')).toBe('/c/a/b/d');
|
||||
expect(resolveAbsoluatePath('../a/b', '/c')).toBe('/a/b');
|
||||
expect(resolveAbsoluatePath('../a/b/d', '/c')).toBe('/a/b/d');
|
||||
expect(resolveAbsoluatePath('../../a', 'c')).toBe('../a');
|
||||
});
|
||||
|
||||
it('joinPath', () => {
|
||||
expect(joinPath('/a', 'b', 'c')).toBe('/a/b/c');
|
||||
expect(joinPath('a', 'b', 'c')).toBe('./a/b/c');
|
||||
});
|
||||
|
||||
it('removeVersion', () => {
|
||||
expect(removeVersion('@ali/fe')).toBe('@ali/fe');
|
||||
expect(removeVersion('@ali/fe@1.0.0/index')).toBe('@ali/fe/index');
|
||||
expect(removeVersion('haha')).toBe('haha');
|
||||
});
|
||||
});
|
||||
@ -1,22 +0,0 @@
|
||||
import '../../fixtures/disable-raf';
|
||||
import { throttle } from '../../../src/builtin-simulator/utils/throttle';
|
||||
|
||||
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
const cb = jest.fn();
|
||||
|
||||
describe('throttle', () => {
|
||||
it('simple', async () => {
|
||||
const fn = throttle(cb, 1000);
|
||||
fn();
|
||||
|
||||
expect(cb).toBeCalledTimes(1);
|
||||
|
||||
await delay(200);
|
||||
fn();
|
||||
|
||||
await delay(400);
|
||||
fn();
|
||||
expect(cb).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@ -1,180 +0,0 @@
|
||||
import '../fixtures/window';
|
||||
import { getMockWindow, getMockElement, delay } from '../utils';
|
||||
import { Editor, globalContext } from '@alilc/lowcode-editor-core';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { DocumentModel } from '../../src/document/document-model';
|
||||
import Viewport from '../../src/builtin-simulator/viewport';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
|
||||
describe('Viewport 测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
let viewport: Viewport;
|
||||
let viewportElem;
|
||||
|
||||
beforeAll(() => {
|
||||
editor = new Editor();
|
||||
!globalContext.has(Editor) && globalContext.register(editor, Editor);
|
||||
|
||||
window.DOMRect = class {
|
||||
constructor(top, left, width, height) {
|
||||
return { top, left, width, height };
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
project = designer.project;
|
||||
// doc = project.createDocument(formSchema);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
// project.mountSimulator(undefined);
|
||||
designer.purge();
|
||||
designer = null;
|
||||
project = null;
|
||||
viewport = null;
|
||||
});
|
||||
|
||||
it('基本函数测试', async () => {
|
||||
const rect = {
|
||||
width: 500,
|
||||
height: 500,
|
||||
top: 100,
|
||||
bottom: 500,
|
||||
left: 100,
|
||||
right: 500,
|
||||
};
|
||||
viewportElem = getMockElement('div', rect);
|
||||
viewport = new Viewport();
|
||||
viewport.mount();
|
||||
expect(viewport.viewportElement).toBeUndefined();
|
||||
expect(viewport.width).toBe(1000);
|
||||
expect(viewport.height).toBe(600);
|
||||
expect(viewport.toGlobalPoint({ left: 0, top: 0 })).toEqual({ left: 0, top: 0 });
|
||||
expect(viewport.toLocalPoint({ left: 0, top: 0 })).toEqual({ left: 0, top: 0 });
|
||||
|
||||
viewport.mount(viewportElem);
|
||||
expect(viewport.viewportElement).toBe(viewportElem);
|
||||
|
||||
expect(viewport.bounds).toEqual(rect);
|
||||
expect(viewport.contentBounds).toEqual({ top: 0, left: 0, width: 500, height: 500 });
|
||||
expect(viewport.rect).toEqual(rect);
|
||||
|
||||
expect(viewport.width).toBe(500);
|
||||
expect(viewport.contentWidth).toBe('100%');
|
||||
expect(viewport.height).toBe(500);
|
||||
expect(viewport.contentHeight).toBe('100%');
|
||||
|
||||
await delay(100);
|
||||
viewportElem.setWidth(300);
|
||||
viewport.width = 300;
|
||||
expect(viewport.width).toBe(300);
|
||||
|
||||
await delay(100);
|
||||
viewportElem.setHeight(300);
|
||||
viewport.height = 300;
|
||||
expect(viewport.height).toBe(300);
|
||||
|
||||
viewport.contentWidth = 200;
|
||||
expect(viewport.contentWidth).toBe(200);
|
||||
|
||||
viewport.contentHeight = 200;
|
||||
expect(viewport.contentHeight).toBe(200);
|
||||
});
|
||||
|
||||
it('scale', () => {
|
||||
const rect = {
|
||||
width: 500,
|
||||
height: 500,
|
||||
top: 100,
|
||||
bottom: 500,
|
||||
left: 100,
|
||||
right: 500,
|
||||
};
|
||||
viewportElem = getMockElement('div', rect);
|
||||
viewport = new Viewport();
|
||||
viewport.mount(viewportElem);
|
||||
|
||||
expect(viewport.scale).toBe(1);
|
||||
viewport.scale = 2;
|
||||
expect(viewport.scale).toBe(2);
|
||||
|
||||
expect(viewport.contentWidth).toBe(500 / 2);
|
||||
expect(viewport.contentHeight).toBe(500 / 2);
|
||||
|
||||
viewport.width = 300;
|
||||
viewportElem.setWidth(300);
|
||||
expect(viewport.contentWidth).toBe(300 / 2);
|
||||
|
||||
viewport.height = 300;
|
||||
viewportElem.setHeight(300);
|
||||
expect(viewport.contentHeight).toBe(300 / 2);
|
||||
|
||||
expect(() => { viewport.scale = NaN; }).toThrow();
|
||||
expect(() => { viewport.scale = -1; }).toThrow();
|
||||
});
|
||||
|
||||
it('setScrollTarget / scrollTarget / scrolling', async () => {
|
||||
const rect = {
|
||||
width: 500,
|
||||
height: 500,
|
||||
top: 100,
|
||||
bottom: 500,
|
||||
left: 100,
|
||||
right: 500,
|
||||
};
|
||||
viewportElem = getMockElement('div', rect);
|
||||
viewport = new Viewport();
|
||||
viewport.mount(viewportElem);
|
||||
|
||||
const mockWindow = getMockWindow();
|
||||
viewport.setScrollTarget(mockWindow);
|
||||
// TODO: 待 mock
|
||||
viewport.scrollTarget;
|
||||
// expect(viewport.scrollTarget).toBe(mockWindow);
|
||||
|
||||
// mock scrollTarget
|
||||
// viewport._scrollTarget = { left: 0, top: 0 };
|
||||
// viewport._scrollTarget.left = 123;
|
||||
// viewport._scrollTarget.top = 1234;
|
||||
mockWindow.triggerEventListener('scroll');
|
||||
expect(viewport.scrolling).toBeTruthy();
|
||||
// TODO: 待 mock
|
||||
viewport.scrollX;
|
||||
viewport.scrollY;
|
||||
// expect(viewport.scrollX).toBe(123);
|
||||
// expect(viewport.scrollY).toBe(1234);
|
||||
await delay(100);
|
||||
expect(viewport.scrolling).toBeFalsy();
|
||||
|
||||
mockWindow.triggerEventListener('resize');
|
||||
});
|
||||
|
||||
it('toGlobalPoint / toLocalPoint', () => {
|
||||
const rect = {
|
||||
width: 500,
|
||||
height: 500,
|
||||
top: 100,
|
||||
bottom: 500,
|
||||
left: 100,
|
||||
right: 500,
|
||||
};
|
||||
viewportElem = getMockElement('div', rect);
|
||||
viewport = new Viewport();
|
||||
viewport.mount(viewportElem);
|
||||
|
||||
expect(viewport.toGlobalPoint({ clientX: 100, clientY: 100 })).toEqual({ clientX: 200, clientY: 200 });
|
||||
expect(viewport.toLocalPoint({ clientX: 200, clientY: 200 })).toEqual({ clientX: 100, clientY: 100 });
|
||||
|
||||
viewport.scale = 2;
|
||||
expect(viewport.toGlobalPoint({ clientX: 100, clientY: 100 })).toEqual({ clientX: 300, clientY: 300 });
|
||||
expect(viewport.toLocalPoint({ clientX: 300, clientY: 300 })).toEqual({ clientX: 100, clientY: 100 });
|
||||
});
|
||||
});
|
||||
@ -1,41 +0,0 @@
|
||||
import '../fixtures/window';
|
||||
import { ActiveTracker } from '../../src/designer/active-tracker';
|
||||
|
||||
it('ActiveTracker 测试,Node', () => {
|
||||
const tracker = new ActiveTracker();
|
||||
|
||||
const mockFn = jest.fn();
|
||||
const mockNode = { isNode: true };
|
||||
const off = tracker.onChange(mockFn);
|
||||
|
||||
tracker.track(mockNode);
|
||||
expect(mockFn).toHaveBeenCalledWith({ node: mockNode });
|
||||
|
||||
expect(tracker.currentNode).toBe(mockNode);
|
||||
|
||||
off();
|
||||
mockFn.mockClear();
|
||||
tracker.track(mockNode);
|
||||
expect(mockFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ActiveTracker 测试,ActiveTarget', () => {
|
||||
const tracker = new ActiveTracker();
|
||||
|
||||
const mockFn = jest.fn();
|
||||
const mockNode = { isNode: true };
|
||||
const off = tracker.onChange(mockFn);
|
||||
const mockTarget = { node: mockNode, detail: { isDetail: true }, instance: { isInstance: true } };
|
||||
|
||||
tracker.track(mockTarget);
|
||||
expect(mockFn).toHaveBeenCalledWith(mockTarget);
|
||||
|
||||
expect(tracker.currentNode).toBe(mockNode);
|
||||
expect(tracker.detail).toEqual({ isDetail: true });
|
||||
expect(tracker.instance).toEqual({ isInstance: true });
|
||||
|
||||
off();
|
||||
mockFn.mockClear();
|
||||
tracker.track(mockNode);
|
||||
expect(mockFn).not.toHaveBeenCalled();
|
||||
});
|
||||
@ -1,395 +0,0 @@
|
||||
import '../fixtures/window';
|
||||
import {
|
||||
Editor,
|
||||
globalContext,
|
||||
Hotkey as InnerHotkey,
|
||||
} from '@alilc/lowcode-editor-core';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { builtinHotkey } from '../../../engine/src/inner-plugins/builtin-hotkey';
|
||||
import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory';
|
||||
import { ILowCodePluginContextPrivate, LowCodePluginManager } from '@alilc/lowcode-designer';
|
||||
import { IPublicApiPlugins } from '@alilc/lowcode-types';
|
||||
import { Logger, Project, Canvas } from '@alilc/lowcode-shell';
|
||||
import { Workspace } from '@alilc/lowcode-workspace';
|
||||
|
||||
const editor = new Editor();
|
||||
const workspace = new Workspace();
|
||||
|
||||
let designer: Designer;
|
||||
|
||||
// keyCode 对应表:https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode
|
||||
// hotkey 模块底层用的 keyCode,所以还不能用 key / code 测试
|
||||
describe('快捷键测试', () => {
|
||||
let pluginManager: LowCodePluginManager;
|
||||
let project: any = {};
|
||||
beforeAll(() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const hotkey: any = new InnerHotkey();
|
||||
const logger = new Logger({ level: 'warn', bizName: 'common' });
|
||||
const contextApiAssembler = {
|
||||
assembleApis(context: ILowCodePluginContextPrivate){
|
||||
context.plugins = pluginManager as IPublicApiPlugins;
|
||||
context.hotkey = hotkey;
|
||||
context.logger = logger;
|
||||
context.project = project;
|
||||
context.canvas = new Canvas(editor);
|
||||
}
|
||||
};
|
||||
pluginManager = new LowCodePluginManager(contextApiAssembler).toProxy();
|
||||
pluginManager.register(builtinHotkey);
|
||||
globalContext.register(editor, Editor);
|
||||
globalContext.register(editor, 'editor');
|
||||
globalContext.register(workspace, 'workspace');
|
||||
pluginManager.init().then(() => {
|
||||
resolve({});
|
||||
});
|
||||
})
|
||||
});
|
||||
afterAll(() => {
|
||||
pluginManager.dispose();
|
||||
});
|
||||
beforeEach(() => {
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
editor.set('designer', designer);
|
||||
designer.project.open(formSchema);
|
||||
project.__proto__ = new Project(designer.project);
|
||||
});
|
||||
afterEach(() => {
|
||||
designer = null;
|
||||
});
|
||||
|
||||
it('right', () => {
|
||||
const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbj')!;
|
||||
firstCardNode.select();
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 39 });
|
||||
|
||||
expect(designer.currentSelection?.selected.includes('node_k1ow3cbl')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('left', () => {
|
||||
const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbl')!;
|
||||
firstCardNode.select();
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 37 });
|
||||
|
||||
expect(designer.currentSelection?.selected.includes('node_k1ow3cbj')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('down', () => {
|
||||
const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbl')!;
|
||||
firstCardNode.select();
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 40 });
|
||||
|
||||
expect(designer.currentSelection?.selected.includes('node_k1ow3cbo')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('up', () => {
|
||||
const secondCardNode = designer.currentDocument?.getNode('node_k1ow3cbm')!;
|
||||
secondCardNode.select();
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 38 });
|
||||
|
||||
expect(designer.currentSelection?.selected.includes('node_k1ow3cbl')).toBeTruthy();
|
||||
});
|
||||
|
||||
// 跟右侧节点调换位置
|
||||
it('option + right', () => {
|
||||
const firstButtonNode = designer.currentDocument?.getNode('node_k1ow3cbn')!;
|
||||
firstButtonNode.select();
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 39, altKey: true });
|
||||
|
||||
expect(firstButtonNode.prevSibling?.getId()).toBe('node_k1ow3cbp');
|
||||
});
|
||||
|
||||
// 跟左侧节点调换位置
|
||||
it('option + left', () => {
|
||||
const secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
secondButtonNode.select();
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 37, altKey: true });
|
||||
|
||||
expect(secondButtonNode.nextSibling?.getId()).toBe('node_k1ow3cbn');
|
||||
});
|
||||
|
||||
// 向父级移动该节点
|
||||
it('option + up', () => {
|
||||
const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
firstCardNode.select();
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 38, altKey: true });
|
||||
});
|
||||
|
||||
// 将节点移入到兄弟节点中
|
||||
it('option + up', () => {
|
||||
const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
firstCardNode.select();
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 40, altKey: true });
|
||||
});
|
||||
|
||||
// 撤销
|
||||
it('command + z', async () => {
|
||||
const firstButtonNode = designer.currentDocument?.getNode('node_k1ow3cbn')!;
|
||||
let secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
|
||||
// 等待第一个 session 结束
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
firstButtonNode.remove();
|
||||
expect(secondButtonNode.getParent()?.children.size).toBe(1);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 90, metaKey: true });
|
||||
|
||||
// 重新获取一次节点,因为 documentModel.import 是全画布刷新
|
||||
secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
expect(secondButtonNode.getParent()?.children.size).toBe(2);
|
||||
});
|
||||
|
||||
// 重做
|
||||
it('command + y', async () => {
|
||||
const firstButtonNode = designer.currentDocument?.getNode('node_k1ow3cbn')!;
|
||||
let secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
|
||||
// 等待第一个 session 结束
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
firstButtonNode.remove();
|
||||
expect(secondButtonNode.getParent()?.children.size).toBe(1);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 90, metaKey: true });
|
||||
|
||||
// 重新获取一次节点,因为 documentModel.import 是全画布刷新
|
||||
secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
expect(secondButtonNode.getParent()?.children.size).toBe(2);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 89, metaKey: true });
|
||||
|
||||
// 重新获取一次节点,因为 documentModel.import 是全画布刷新
|
||||
secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
expect(secondButtonNode.getParent()?.children.size).toBe(1);
|
||||
});
|
||||
|
||||
it('command + c', () => {
|
||||
const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
firstCardNode.select();
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 67, metaKey: true });
|
||||
});
|
||||
|
||||
it('command + v', async () => {
|
||||
const secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
secondButtonNode.select();
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 67, metaKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 86, metaKey: true });
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// clipboard 异步,先注释
|
||||
// expect(secondButtonNode.getParent()?.children.size).toBe(3);
|
||||
});
|
||||
|
||||
// 撤销所有选中
|
||||
it('escape', () => {
|
||||
const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
firstCardNode.select();
|
||||
|
||||
expect(designer.currentSelection!.selected.includes('node_k1ow3cbp')).toBeTruthy();
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 27 });
|
||||
|
||||
expect(designer.currentSelection!.selected.length).toBe(0);
|
||||
});
|
||||
|
||||
// 删除节点
|
||||
it('delete', () => {
|
||||
const firstButtonNode = designer.currentDocument?.getNode('node_k1ow3cbn')!;
|
||||
const secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
firstButtonNode.select();
|
||||
|
||||
expect(secondButtonNode.prevSibling.id).toBe('node_k1ow3cbn');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 46 });
|
||||
|
||||
expect(secondButtonNode.prevSibling).toBeNull();
|
||||
});
|
||||
|
||||
|
||||
describe('非正常分支', () => {
|
||||
it('liveEditing mode', () => {
|
||||
designer.project.mountSimulator({
|
||||
liveEditing: {
|
||||
editing: {},
|
||||
},
|
||||
});
|
||||
editor.set('designer', designer);
|
||||
designer.currentDocument?.selection.select('page');
|
||||
// nothing happened
|
||||
fireEvent.keyDown(document, { keyCode: 39 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 37 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 40 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 38 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 39, altKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 37, altKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 40, altKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 38, altKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 90, metaKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 89, metaKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 67, metaKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 86, metaKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 27 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 46 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
});
|
||||
|
||||
it('isFormEvent: true', () => {
|
||||
const inputDOMNode = document.createElement('INPUT');
|
||||
document.body.appendChild(inputDOMNode);
|
||||
designer.currentDocument?.selection.select('page');
|
||||
// nothing happened
|
||||
|
||||
fireEvent.keyDown(inputDOMNode, { keyCode: 39 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(inputDOMNode, { keyCode: 37 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(inputDOMNode, { keyCode: 40 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(inputDOMNode, { keyCode: 38 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(inputDOMNode, { keyCode: 39, altKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(inputDOMNode, { keyCode: 37, altKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(inputDOMNode, { keyCode: 40, altKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(inputDOMNode, { keyCode: 38, altKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(inputDOMNode, { keyCode: 90, metaKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(inputDOMNode, { keyCode: 89, metaKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(inputDOMNode, { keyCode: 67, metaKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(inputDOMNode, { keyCode: 86, metaKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(inputDOMNode, { keyCode: 27 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(inputDOMNode, { keyCode: 46 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
});
|
||||
|
||||
it('doc is null', () => {
|
||||
designer.currentDocument?.selection.select('page');
|
||||
designer.project.documents = [];
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 39 });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 37 });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 40 });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 38 });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 39, altKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 37, altKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 40, altKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 38, altKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 90, metaKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 89, metaKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 67, metaKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 86, metaKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 27 });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 46 });
|
||||
});
|
||||
|
||||
it('selected is []', () => {
|
||||
fireEvent.keyDown(document, { keyCode: 39 });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 37 });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 40 });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 38 });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 39, altKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 37, altKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 40, altKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 38, altKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 90, metaKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 89, metaKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 67, metaKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 86, metaKey: true });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 27 });
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 46 });
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,500 +0,0 @@
|
||||
import '../fixtures/window';
|
||||
import { Editor, globalContext, Setters } from '@alilc/lowcode-editor-core';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { DocumentModel } from '../../src/document/document-model';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import { Dragon } from '../../src/designer/dragon';
|
||||
// import { TransformStage } from '../../src/document/node/transform-stage';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import buttonMetadata from '../fixtures/component-metadata/button';
|
||||
import pageMetadata from '../fixtures/component-metadata/page';
|
||||
import divMetadata from '../fixtures/component-metadata/div';
|
||||
import { delayObxTick } from '../utils';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { IPublicEnumDragObjectType, IPublicEnumTransformStage } from '@alilc/lowcode-types';
|
||||
import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
const mockNode = {
|
||||
internalToShellNode() {
|
||||
return 'mockNode';
|
||||
},
|
||||
};
|
||||
|
||||
describe('Designer 测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
let dragon: Dragon;
|
||||
|
||||
beforeAll(() => {
|
||||
editor = new Editor();
|
||||
const setters = new Setters();
|
||||
editor.set('setters', setters);
|
||||
!globalContext.has(Editor) && globalContext.register(editor, Editor);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
project = designer.project;
|
||||
doc = project.createDocument(formSchema);
|
||||
dragon = new Dragon(designer);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
project.mountSimulator(undefined);
|
||||
designer.purge();
|
||||
designer = null;
|
||||
project = null;
|
||||
dragon = null;
|
||||
});
|
||||
|
||||
describe('onDragstart / onDrag / onDragend', () => {
|
||||
it('IPublicEnumDragObjectType.Node', () => {
|
||||
const dragStartMockFn = jest.fn();
|
||||
const dragMockFn = jest.fn();
|
||||
const dragEndMockFn = jest.fn();
|
||||
const dragStartMockFn2 = jest.fn();
|
||||
const dragMockFn2 = jest.fn();
|
||||
const dragEndMockFn2 = jest.fn();
|
||||
|
||||
const designer = new Designer({
|
||||
editor,
|
||||
shellModelFactory,
|
||||
onDragstart: dragStartMockFn,
|
||||
onDrag: dragMockFn,
|
||||
onDragend: dragEndMockFn,
|
||||
});
|
||||
editor.on('designer.dragstart', dragStartMockFn2);
|
||||
editor.on('designer.drag', dragMockFn2);
|
||||
editor.on('designer.dragend', dragEndMockFn2);
|
||||
const { dragon } = designer;
|
||||
|
||||
dragon.boost(
|
||||
{
|
||||
type: IPublicEnumDragObjectType.Node,
|
||||
nodes: [doc.getNode('node_k1ow3cbn')],
|
||||
},
|
||||
new MouseEvent('mousedown', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 108, clientY: 108 });
|
||||
expect(dragStartMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragStartMockFn2).toHaveBeenCalledTimes(1);
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragMockFn2).toHaveBeenCalledTimes(1);
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 110, clientY: 110 });
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(2);
|
||||
expect(dragMockFn2).toHaveBeenCalledTimes(2);
|
||||
|
||||
setMockDropLocation();
|
||||
fireEvent.mouseUp(document, { clientX: 118, clientY: 118 });
|
||||
|
||||
expect(dragEndMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragEndMockFn2).toHaveBeenCalledTimes(1);
|
||||
|
||||
function setMockDropLocation() {
|
||||
const mockTarget = {
|
||||
document: doc,
|
||||
children: {
|
||||
get(x) {
|
||||
return x;
|
||||
},
|
||||
insert() {},
|
||||
internalInsert() {},
|
||||
},
|
||||
};
|
||||
const mockDetail = { type: 'Children', index: 1, near: { node: { x: 1 } } };
|
||||
const mockSource = {};
|
||||
const mockEvent = { type: 'LocateEvent', nodes: [] };
|
||||
|
||||
return designer.createLocation({
|
||||
target: mockTarget,
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('IPublicEnumDragObjectType.NodeData', () => {
|
||||
const dragStartMockFn = jest.fn();
|
||||
const dragMockFn = jest.fn();
|
||||
const dragEndMockFn = jest.fn();
|
||||
const dragStartMockFn2 = jest.fn();
|
||||
const dragMockFn2 = jest.fn();
|
||||
const dragEndMockFn2 = jest.fn();
|
||||
|
||||
const designer = new Designer({
|
||||
editor,
|
||||
shellModelFactory,
|
||||
onDragstart: dragStartMockFn,
|
||||
onDrag: dragMockFn,
|
||||
onDragend: dragEndMockFn,
|
||||
});
|
||||
editor.on('designer.dragstart', dragStartMockFn2);
|
||||
editor.on('designer.drag', dragMockFn2);
|
||||
editor.on('designer.dragend', dragEndMockFn2);
|
||||
const { dragon } = designer;
|
||||
|
||||
dragon.boost(
|
||||
{
|
||||
type: IPublicEnumDragObjectType.NodeData,
|
||||
data: [{
|
||||
componentName: 'Button',
|
||||
}],
|
||||
},
|
||||
new MouseEvent('mousedown', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 108, clientY: 108 });
|
||||
expect(dragStartMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragStartMockFn2).toHaveBeenCalledTimes(1);
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragMockFn2).toHaveBeenCalledTimes(1);
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 110, clientY: 110 });
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(2);
|
||||
expect(dragMockFn2).toHaveBeenCalledTimes(2);
|
||||
|
||||
setMockDropLocation();
|
||||
fireEvent.mouseUp(document, { clientX: 118, clientY: 118 });
|
||||
|
||||
expect(dragEndMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragEndMockFn2).toHaveBeenCalledTimes(1);
|
||||
|
||||
function setMockDropLocation() {
|
||||
const mockTarget = {
|
||||
document: doc,
|
||||
children: {
|
||||
get(x) {
|
||||
return x;
|
||||
},
|
||||
insert() {},
|
||||
internalInsert() {},
|
||||
},
|
||||
};
|
||||
const mockDetail = { type: 'Children', index: 1, near: { node: { x: 1 } } };
|
||||
const mockSource = {};
|
||||
const mockEvent = { type: 'LocateEvent', nodes: [] };
|
||||
|
||||
return designer.createLocation({
|
||||
target: mockTarget,
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('addPropsReducer / transformProps', () => {
|
||||
// 没有相应的 reducer
|
||||
expect(designer.transformProps({ num: 1 }, mockNode, IPublicEnumTransformStage.Init)).toEqual({ num: 1 });
|
||||
// props 是数组
|
||||
expect(designer.transformProps([{ num: 1 }], mockNode, IPublicEnumTransformStage.Init)).toEqual([{ num: 1 }]);
|
||||
|
||||
designer.addPropsReducer((props, node) => {
|
||||
props.num += 1;
|
||||
return props;
|
||||
}, IPublicEnumTransformStage.Init);
|
||||
|
||||
designer.addPropsReducer((props, node) => {
|
||||
props.num += 1;
|
||||
return props;
|
||||
}, IPublicEnumTransformStage.Init);
|
||||
|
||||
designer.addPropsReducer((props, node) => {
|
||||
props.num += 1;
|
||||
return props;
|
||||
}, IPublicEnumTransformStage.Clone);
|
||||
|
||||
designer.addPropsReducer((props, node) => {
|
||||
props.num += 1;
|
||||
return props;
|
||||
}, IPublicEnumTransformStage.Serilize);
|
||||
|
||||
designer.addPropsReducer((props, node) => {
|
||||
props.num += 1;
|
||||
return props;
|
||||
}, IPublicEnumTransformStage.Render);
|
||||
|
||||
designer.addPropsReducer((props, node) => {
|
||||
props.num += 1;
|
||||
return props;
|
||||
}, IPublicEnumTransformStage.Save);
|
||||
|
||||
designer.addPropsReducer((props, node) => {
|
||||
props.num += 1;
|
||||
return props;
|
||||
}, IPublicEnumTransformStage.Upgrade);
|
||||
|
||||
expect(designer.transformProps({ num: 1 }, mockNode, IPublicEnumTransformStage.Init)).toEqual({ num: 3 });
|
||||
expect(designer.transformProps({ num: 1 }, mockNode, IPublicEnumTransformStage.Clone)).toEqual({ num: 2 });
|
||||
expect(designer.transformProps({ num: 1 }, mockNode, IPublicEnumTransformStage.Serilize)).toEqual({ num: 2 });
|
||||
expect(designer.transformProps({ num: 1 }, mockNode, IPublicEnumTransformStage.Render)).toEqual({ num: 2 });
|
||||
expect(designer.transformProps({ num: 1 }, mockNode, IPublicEnumTransformStage.Save)).toEqual({ num: 2 });
|
||||
expect(designer.transformProps({ num: 1 }, mockNode, IPublicEnumTransformStage.Upgrade)).toEqual({ num: 2 });
|
||||
|
||||
designer.addPropsReducer((props, node) => {
|
||||
throw new Error('calculate error');
|
||||
}, IPublicEnumTransformStage.Upgrade);
|
||||
expect(designer.transformProps({ num: 1 }, mockNode, IPublicEnumTransformStage.Upgrade)).toEqual({ num: 2 });
|
||||
});
|
||||
|
||||
it('setProps', () => {
|
||||
// 第一次设置 props
|
||||
const initialProps = {
|
||||
simulatorComponent: { isSimulatorComp: true },
|
||||
simulatorProps: { designMode: 'design' },
|
||||
suspensed: true,
|
||||
componentMetadatas: [buttonMetadata, divMetadata],
|
||||
};
|
||||
designer = new Designer({
|
||||
editor,
|
||||
shellModelFactory,
|
||||
...initialProps,
|
||||
});
|
||||
|
||||
expect(designer.simulatorComponent).toEqual({ isSimulatorComp: true });
|
||||
expect(designer.simulatorProps).toEqual({ designMode: 'design' });
|
||||
expect(designer.suspensed).toBeTruthy();
|
||||
expect((designer as any)._componentMetasMap.has('Div')).toBeTruthy();
|
||||
expect((designer as any)._componentMetasMap.has('Button')).toBeTruthy();
|
||||
const { editor: editorFromDesigner, shellModelFactory: shellModelFactoryFromDesigner, ...others } = (designer as any).props;
|
||||
expect(others).toEqual(initialProps);
|
||||
expect(designer.get('simulatorProps')).toEqual({ designMode: 'design' });
|
||||
expect(designer.get('suspensed')).toBeTruthy();
|
||||
expect(designer.get('xxx')).toBeUndefined();
|
||||
|
||||
// 第二次设置 props
|
||||
const updatedProps = {
|
||||
simulatorComponent: { isSimulatorComp2: true },
|
||||
simulatorProps: { designMode: 'live' },
|
||||
suspensed: false,
|
||||
componentMetadatas: [buttonMetadata],
|
||||
};
|
||||
designer.setProps(updatedProps);
|
||||
|
||||
expect(designer.simulatorComponent).toEqual({ isSimulatorComp2: true });
|
||||
expect(designer.simulatorProps).toEqual({ designMode: 'live' });
|
||||
expect(designer.suspensed).toBeFalsy();
|
||||
expect((designer as any)._componentMetasMap.has('Button')).toBeTruthy();
|
||||
expect((designer as any)._componentMetasMap.has('Div')).toBeTruthy();
|
||||
const { editor: editorFromDesigner2, shellModelFactory: shellModelFactoryFromDesigner2, ...others2 } = (designer as any).props;
|
||||
expect(others2).toEqual(updatedProps);
|
||||
|
||||
// 第三次设置 props,跟第二次值一样,for 覆盖率测试
|
||||
const updatedProps2 = updatedProps;
|
||||
designer.setProps(updatedProps2);
|
||||
|
||||
expect(designer.simulatorComponent).toEqual({ isSimulatorComp2: true });
|
||||
expect(designer.simulatorProps).toEqual({ designMode: 'live' });
|
||||
expect(designer.suspensed).toBeFalsy();
|
||||
expect((designer as any)._componentMetasMap.has('Button')).toBeTruthy();
|
||||
expect((designer as any)._componentMetasMap.has('Div')).toBeTruthy();
|
||||
const { editor: editorFromDesigner3, shellModelFactory: shellModelFactoryFromDesigner3, ...others3 } = (designer as any).props;
|
||||
expect(others3).toEqual(updatedProps);
|
||||
});
|
||||
|
||||
describe('getSuitableInsertion', () => {
|
||||
it('没有 currentDocument', () => {
|
||||
project.unload();
|
||||
expect(designer.getSuitableInsertion({})).toBeNull();
|
||||
});
|
||||
|
||||
it('有选中节点,isContainer && 允许放子节点', () => {
|
||||
designer.createComponentMeta(divMetadata);
|
||||
designer.createComponentMeta(buttonMetadata);
|
||||
designer.currentSelection?.select('node_k1ow3cbo');
|
||||
const { target, index } = designer.getSuitableInsertion(
|
||||
doc.createNode({ componentName: 'Button' }),
|
||||
);
|
||||
expect(target).toBe(doc.getNode('node_k1ow3cbo'));
|
||||
expect(index).toBeUndefined();
|
||||
});
|
||||
|
||||
it('有选中节点,不是 isContainer', () => {
|
||||
designer.createComponentMeta(divMetadata);
|
||||
designer.createComponentMeta(buttonMetadata);
|
||||
designer.currentSelection?.select('node_k1ow3cbn');
|
||||
const { target, index } = designer.getSuitableInsertion(
|
||||
doc.createNode({ componentName: 'Button' }),
|
||||
);
|
||||
expect(target).toBe(doc.getNode('node_k1ow3cbo'));
|
||||
expect(index).toBe(1);
|
||||
});
|
||||
|
||||
it('无选中节点', () => {
|
||||
designer.createComponentMeta(pageMetadata);
|
||||
const { target, index } = designer.getSuitableInsertion(
|
||||
doc.createNode({ componentName: 'Button' }),
|
||||
);
|
||||
expect(target).toBe(doc.getNode('page'));
|
||||
expect(index).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('getComponentMetasMap', () => {
|
||||
designer.createComponentMeta({
|
||||
componentName: 'Div',
|
||||
title: '容器',
|
||||
docUrl: 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md',
|
||||
devMode: 'procode',
|
||||
tags: ['布局'],
|
||||
});
|
||||
|
||||
expect(designer.getComponentMetasMap().get('Div')).not.toBeUndefined();
|
||||
});
|
||||
|
||||
it('refreshComponentMetasMap', () => {
|
||||
designer.createComponentMeta({
|
||||
componentName: 'Div',
|
||||
title: '容器',
|
||||
docUrl: 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md',
|
||||
devMode: 'procode',
|
||||
tags: ['布局'],
|
||||
});
|
||||
|
||||
const originalMetasMap = designer.getComponentMetasMap();
|
||||
designer.refreshComponentMetasMap();
|
||||
|
||||
expect(originalMetasMap).not.toBe(designer.getComponentMetasMap());
|
||||
});
|
||||
|
||||
describe('loadIncrementalAssets', () => {
|
||||
it('components && packages', async () => {
|
||||
editor.set('assets', { components: [], packages: [] });
|
||||
const fn = jest.fn();
|
||||
|
||||
project.mountSimulator({
|
||||
setupComponents: fn,
|
||||
});
|
||||
await designer.loadIncrementalAssets({
|
||||
components: [{
|
||||
componentName: 'Div2',
|
||||
title: '容器',
|
||||
docUrl: 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
}],
|
||||
packages: [],
|
||||
});
|
||||
|
||||
const comps = editor.get('assets').components;
|
||||
expect(comps).toHaveLength(1);
|
||||
expect(fn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('no components && packages', async () => {
|
||||
editor.set('assets', { components: [], packages: [] });
|
||||
const fn = jest.fn();
|
||||
|
||||
project.mountSimulator({
|
||||
setupComponents: fn,
|
||||
});
|
||||
await designer.loadIncrementalAssets({});
|
||||
|
||||
expect(fn).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('createLocation / clearLocation', () => {
|
||||
const mockTarget = {
|
||||
document: doc,
|
||||
children: {
|
||||
get(x) {
|
||||
return x;
|
||||
},
|
||||
insert() {},
|
||||
internalInsert() {},
|
||||
},
|
||||
};
|
||||
const mockDetail = { type: 'Children', index: 1, near: { node: { x: 1 } } };
|
||||
const mockSource = {};
|
||||
const mockEvent = { type: 'LocateEvent', nodes: [] };
|
||||
|
||||
const loc = designer.createLocation({
|
||||
target: mockTarget,
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
|
||||
expect(designer.dropLocation).toBe(loc);
|
||||
|
||||
const doc2 = project.createDocument({ componentName: 'Page' });
|
||||
designer.createLocation({
|
||||
target: {
|
||||
document: doc2,
|
||||
children: {
|
||||
get(x) {
|
||||
return x;
|
||||
},
|
||||
insert() {},
|
||||
internalInsert() {},
|
||||
},
|
||||
},
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
|
||||
designer.clearLocation();
|
||||
expect(designer.dropLocation).toBeUndefined();
|
||||
});
|
||||
|
||||
it('autorun', async () => {
|
||||
const mockFn = jest.fn();
|
||||
designer.autorun(() => {
|
||||
mockFn();
|
||||
}, true);
|
||||
|
||||
await delayObxTick();
|
||||
|
||||
expect(mockFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('suspensed', () => {
|
||||
designer.suspensed = true;
|
||||
expect(designer.suspensed).toBeTruthy();
|
||||
designer.suspensed = false;
|
||||
expect(designer.suspensed).toBeFalsy();
|
||||
});
|
||||
|
||||
it('schema', () => {
|
||||
// TODO: matchSnapshot
|
||||
designer.schema;
|
||||
designer.setSchema({
|
||||
componentsTree: [
|
||||
{
|
||||
componentName: 'Page',
|
||||
props: {},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('createOffsetObserver / clearOobxList / touchOffsetObserver', () => {
|
||||
project.mountSimulator({
|
||||
computeComponentInstanceRect() {},
|
||||
});
|
||||
designer.createOffsetObserver({ node: doc.getNode('page'), instance: {} });
|
||||
expect(designer.oobxList).toHaveLength(1);
|
||||
designer.createOffsetObserver({ node: doc.getNode('page'), instance: {} });
|
||||
expect(designer.oobxList).toHaveLength(2);
|
||||
|
||||
designer.clearOobxList(true);
|
||||
expect(designer.oobxList).toHaveLength(0);
|
||||
|
||||
const obx = designer.createOffsetObserver({ node: doc.getNode('page'), instance: {} });
|
||||
obx.pid = 'xxx';
|
||||
obx.compute = () => {};
|
||||
expect(designer.oobxList).toHaveLength(1);
|
||||
|
||||
designer.touchOffsetObserver();
|
||||
expect(designer.oobxList).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
@ -1,26 +0,0 @@
|
||||
import { Detecting } from '../../src/designer/detecting';
|
||||
|
||||
it('Detecting 测试', () => {
|
||||
const fn = jest.fn();
|
||||
const detecting = new Detecting();
|
||||
detecting.onDetectingChange(fn);
|
||||
|
||||
expect(detecting.enable).toBeTruthy();
|
||||
|
||||
const mockNode = { document };
|
||||
detecting.capture(mockNode);
|
||||
expect(fn).toHaveBeenCalledWith(detecting.current);
|
||||
expect(detecting.current).toBe(mockNode);
|
||||
|
||||
detecting.release({});
|
||||
detecting.release(mockNode);
|
||||
expect(detecting.current).toBeNull();
|
||||
|
||||
detecting.capture(mockNode);
|
||||
detecting.leave(document);
|
||||
expect(detecting.current).toBeNull();
|
||||
|
||||
detecting.capture(mockNode);
|
||||
detecting.enable = false;
|
||||
expect(detecting.current).toBeNull();
|
||||
});
|
||||
@ -1,369 +0,0 @@
|
||||
import '../fixtures/window';
|
||||
import { Editor, globalContext } from '@alilc/lowcode-editor-core';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { DocumentModel } from '../../src/document/document-model';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import {
|
||||
Dragon,
|
||||
isDragNodeObject,
|
||||
isDragNodeDataObject,
|
||||
isDragAnyObject,
|
||||
isLocateEvent,
|
||||
isShaken,
|
||||
setShaken,
|
||||
isInvalidPoint,
|
||||
isSameAs,
|
||||
} from '../../src/designer/dragon';
|
||||
import { IPublicEnumDragObjectType } from '@alilc/lowcode-types';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
describe('Dragon 测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
let dragon: Dragon;
|
||||
|
||||
beforeAll(() => {
|
||||
editor = new Editor();
|
||||
!globalContext.has(Editor) && globalContext.register(editor, Editor);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
project = designer.project;
|
||||
doc = project.createDocument(formSchema);
|
||||
dragon = new Dragon(designer);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
project.mountSimulator(undefined);
|
||||
designer.purge();
|
||||
designer = null;
|
||||
project = null;
|
||||
dragon = null;
|
||||
});
|
||||
|
||||
it.skip('drag NodeData', () => {
|
||||
const dragStartMockFn = jest.fn();
|
||||
const dragMockFn = jest.fn();
|
||||
const dragEndMockFn = jest.fn();
|
||||
|
||||
dragon.onDragstart((e) => {
|
||||
console.log('start', e, e.originalEvent, e.originalEvent.clientX);
|
||||
});
|
||||
|
||||
dragon.onDrag((e) => {
|
||||
console.log('drag', e, e.originalEvent, e.originalEvent.clientX);
|
||||
});
|
||||
|
||||
dragon.onDragend((e) => {
|
||||
console.log('end', e, e.originalEvent);
|
||||
});
|
||||
|
||||
dragon.boost(
|
||||
{
|
||||
type: IPublicEnumDragObjectType.NodeData,
|
||||
data: [{ componentName: 'Button' }],
|
||||
},
|
||||
new Event('dragstart', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
fireEvent.dragOver(document, { clientX: 108, clientY: 108 });
|
||||
fireEvent.dragEnd(document, { clientX: 118, clientY: 118 });
|
||||
});
|
||||
|
||||
it.skip('drag Node', () => {
|
||||
console.log(new MouseEvent('mousedown', { clientX: 1 }).clientX);
|
||||
// console.log(new Event('mousedown', { clientX: 1 }).clientX);
|
||||
// console.log(new Event('drag', { clientX: 1 }).clientX);
|
||||
// console.log(new CustomEvent('drag', { clientX: 1 }).clientX);
|
||||
console.log(document.createEvent('dragstart', { clientX: 1 }).clientX);
|
||||
});
|
||||
|
||||
it('mouse NodeData', () => {
|
||||
const dragStartMockFn = jest.fn();
|
||||
const dragMockFn = jest.fn();
|
||||
const dragEndMockFn = jest.fn();
|
||||
|
||||
const offDragStart = dragon.onDragstart(dragStartMockFn);
|
||||
|
||||
const offDrag = dragon.onDrag(dragMockFn);
|
||||
|
||||
const offDragEnd = dragon.onDragend(dragEndMockFn);
|
||||
|
||||
dragon.boost(
|
||||
{
|
||||
type: IPublicEnumDragObjectType.NodeData,
|
||||
data: [{ componentName: 'Button' }],
|
||||
},
|
||||
new MouseEvent('mousedown', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 108, clientY: 108 });
|
||||
fireEvent.mouseMove(document, { clientX: 110, clientY: 110 });
|
||||
fireEvent.mouseUp(document, { clientX: 118, clientY: 118 });
|
||||
|
||||
expect(dragStartMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(2);
|
||||
expect(dragEndMockFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('mouse Node', () => {
|
||||
const dragStartMockFn = jest.fn();
|
||||
const dragMockFn = jest.fn();
|
||||
const dragEndMockFn = jest.fn();
|
||||
|
||||
const offDragStart = dragon.onDragstart(dragStartMockFn);
|
||||
const offDrag = dragon.onDrag(dragMockFn);
|
||||
const offDragEnd = dragon.onDragend(dragEndMockFn);
|
||||
|
||||
dragon.boost(
|
||||
{
|
||||
type: IPublicEnumDragObjectType.Node,
|
||||
nodes: [doc.getNode('node_k1ow3cbn')],
|
||||
},
|
||||
new MouseEvent('mousedown', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
// mouseDown 模式正常不会触发 dragStart 事件,除非 shaken 型
|
||||
expect(dragStartMockFn).not.toHaveBeenCalled();
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 108, clientY: 108 });
|
||||
expect(dragStartMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(1);
|
||||
fireEvent.mouseMove(document, { clientX: 110, clientY: 110 });
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(2);
|
||||
expect(dragon.dragging).toBeTruthy();
|
||||
|
||||
fireEvent.mouseUp(document, { clientX: 118, clientY: 118 });
|
||||
|
||||
expect(dragEndMockFn).toHaveBeenCalledTimes(1);
|
||||
|
||||
offDragStart();
|
||||
offDrag();
|
||||
offDragEnd();
|
||||
dragMockFn.mockClear();
|
||||
|
||||
dragon.boost(
|
||||
{
|
||||
type: IPublicEnumDragObjectType.Node,
|
||||
nodes: [doc.getNode('node_k1ow3cbn')],
|
||||
},
|
||||
new MouseEvent('mousedown', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 108, clientY: 108 });
|
||||
|
||||
expect(dragMockFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('mouse Node & esc', () => {
|
||||
const dragStartMockFn = jest.fn();
|
||||
const dragMockFn = jest.fn();
|
||||
const dragEndMockFn = jest.fn();
|
||||
|
||||
const offDragStart = dragon.onDragstart(dragStartMockFn);
|
||||
const offDrag = dragon.onDrag(dragMockFn);
|
||||
const offDragEnd = dragon.onDragend(dragEndMockFn);
|
||||
|
||||
dragon.boost(
|
||||
{
|
||||
type: IPublicEnumDragObjectType.Node,
|
||||
nodes: [doc.getNode('node_k1ow3cbn')],
|
||||
},
|
||||
new MouseEvent('mousedown', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 27 });
|
||||
expect(dragon.designer.dropLocation).toBeUndefined();
|
||||
});
|
||||
|
||||
it('mouse Node & copy', () => {
|
||||
const dragStartMockFn = jest.fn();
|
||||
const dragMockFn = jest.fn();
|
||||
const dragEndMockFn = jest.fn();
|
||||
|
||||
const offDragStart = dragon.onDragstart(dragStartMockFn);
|
||||
const offDrag = dragon.onDrag(dragMockFn);
|
||||
const offDragEnd = dragon.onDragend(dragEndMockFn);
|
||||
|
||||
dragon.boost(
|
||||
{
|
||||
type: IPublicEnumDragObjectType.Node,
|
||||
nodes: [doc.getNode('node_k1ow3cbn')],
|
||||
},
|
||||
new MouseEvent('mousedown', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
const mockFn1 = jest.fn();
|
||||
project.mountSimulator({ setCopyState: mockFn1 });
|
||||
expect(dragon.getSimulators().size).toBe(1);
|
||||
fireEvent.keyDown(document, { ctrlKey: true });
|
||||
expect(mockFn1).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('from', () => {
|
||||
const dragStartMockFn = jest.fn();
|
||||
const dragMockFn = jest.fn();
|
||||
const dragEndMockFn = jest.fn();
|
||||
|
||||
const offDragStart = dragon.onDragstart(dragStartMockFn);
|
||||
const offDrag = dragon.onDrag(dragMockFn);
|
||||
const offDragEnd = dragon.onDragend(dragEndMockFn);
|
||||
const mockBoostFn = jest
|
||||
.fn((e) => {
|
||||
return {
|
||||
type: IPublicEnumDragObjectType.Node,
|
||||
nodes: [doc.getNode('node_k1ow3cbn')],
|
||||
};
|
||||
})
|
||||
.mockImplementationOnce(() => null);
|
||||
|
||||
const offFrom = dragon.from(document, mockBoostFn);
|
||||
|
||||
// 无用 mouseDown,无效的按钮
|
||||
fireEvent.mouseDown(document, { button: 2 });
|
||||
expect(dragStartMockFn).not.toHaveBeenCalled();
|
||||
|
||||
// 无用 mouseDown,无效的 dragObject
|
||||
fireEvent.mouseDown(document, { clientX: 100, clientY: 100 });
|
||||
expect(dragStartMockFn).not.toHaveBeenCalled();
|
||||
|
||||
fireEvent.mouseDown(document, { clientX: 100, clientY: 100 });
|
||||
expect(dragStartMockFn).not.toHaveBeenCalled();
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 108, clientY: 108 });
|
||||
expect(dragStartMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(1);
|
||||
fireEvent.mouseMove(document, { clientX: 110, clientY: 110 });
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(2);
|
||||
expect(dragon.dragging).toBeTruthy();
|
||||
|
||||
fireEvent.mouseUp(document, { clientX: 118, clientY: 118 });
|
||||
|
||||
expect(dragEndMockFn).toHaveBeenCalledTimes(1);
|
||||
|
||||
offDragStart();
|
||||
offDrag();
|
||||
offDragEnd();
|
||||
dragMockFn.mockClear();
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 100, clientY: 100 });
|
||||
expect(dragMockFn).not.toHaveBeenCalled();
|
||||
|
||||
offFrom();
|
||||
fireEvent.mouseMove(document, { clientX: 100, clientY: 100 });
|
||||
expect(dragMockFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('addSensor / removeSensor', () => {
|
||||
const sensor = {
|
||||
locate: () => {},
|
||||
sensorAvailable: true,
|
||||
isEnter: () => true,
|
||||
fixEvent: () => {},
|
||||
deactiveSensor: () => {},
|
||||
};
|
||||
const sensor2 = {};
|
||||
dragon.addSensor(sensor);
|
||||
expect(dragon.sensors.length).toBe(1);
|
||||
expect(dragon.activeSensor).toBeUndefined();
|
||||
dragon.boost(
|
||||
{
|
||||
type: IPublicEnumDragObjectType.NodeData,
|
||||
data: [{ componentName: 'Button' }],
|
||||
},
|
||||
new MouseEvent('mousedown', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 108, clientY: 108 });
|
||||
fireEvent.mouseMove(document, { clientX: 110, clientY: 110 });
|
||||
fireEvent.mouseUp(document, { clientX: 118, clientY: 118 });
|
||||
expect(dragon.activeSensor).toBe(sensor);
|
||||
// remove a non-existing sensor
|
||||
dragon.removeSensor(sensor2);
|
||||
expect(dragon.sensors.length).toBe(1);
|
||||
dragon.removeSensor(sensor);
|
||||
expect(dragon.sensors.length).toBe(0);
|
||||
});
|
||||
|
||||
it('has sensor', () => {
|
||||
const mockFn1 = jest.fn();
|
||||
const mockDoc = document.createElement('iframe').contentWindow?.document;
|
||||
dragon.addSensor({
|
||||
fixEvent: () => {},
|
||||
locate: () => {},
|
||||
contentDocument: mockDoc,
|
||||
});
|
||||
project.mountSimulator({
|
||||
setCopyState: mockFn1,
|
||||
setNativeSelection: () => {},
|
||||
clearState: () => {},
|
||||
setDraggingState: () => {},
|
||||
});
|
||||
|
||||
const mockBoostFn = jest
|
||||
.fn((e) => {
|
||||
return {
|
||||
type: IPublicEnumDragObjectType.Node,
|
||||
nodes: [doc.getNode('node_k1ow3cbn')],
|
||||
};
|
||||
})
|
||||
.mockImplementationOnce(() => null);
|
||||
|
||||
const offFrom = dragon.from(document, mockBoostFn);
|
||||
|
||||
// TODO: 想办法 mock 一个 iframe.currentDocument
|
||||
fireEvent.mouseDown(document, { clientX: 100, clientY: 100 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('导出的其他函数', () => {
|
||||
it('isDragNodeObject', () => {
|
||||
expect(isDragNodeObject({ type: IPublicEnumDragObjectType.Node, nodes: [] })).toBeTruthy();
|
||||
});
|
||||
it('isDragNodeDataObject', () => {
|
||||
expect(isDragNodeDataObject({ type: IPublicEnumDragObjectType.NodeData, data: [] })).toBeTruthy();
|
||||
});
|
||||
it('isDragAnyObject', () => {
|
||||
expect(isDragAnyObject()).toBeFalsy();
|
||||
expect(isDragAnyObject({ type: IPublicEnumDragObjectType.Node, nodes: [] })).toBeFalsy();
|
||||
expect(isDragAnyObject({ type: IPublicEnumDragObjectType.NodeData, data: [] })).toBeFalsy();
|
||||
expect(isDragAnyObject({ type: 'others', data: [] })).toBeTruthy();
|
||||
});
|
||||
it('isLocateEvent', () => {
|
||||
expect(isLocateEvent({ type: 'LocateEvent' })).toBeTruthy();
|
||||
});
|
||||
it('isShaken', () => {
|
||||
expect(
|
||||
isShaken(
|
||||
{ clientX: 1, clientY: 1, target: {} },
|
||||
{ clientX: 1, clientY: 1, target: { other: 1 } },
|
||||
),
|
||||
).toBeTruthy();
|
||||
expect(isShaken({ shaken: true })).toBeTruthy();
|
||||
expect(isShaken({ clientX: 1, clientY: 1 }, { clientX: 2, clientY: 2 })).toBeFalsy();
|
||||
expect(isShaken({ clientX: 1, clientY: 1 }, { clientX: 3, clientY: 5 })).toBeTruthy();
|
||||
});
|
||||
it('setShaken', () => {
|
||||
const e = {};
|
||||
setShaken(e);
|
||||
expect(isShaken(e)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('isInvalidPoint', () => {
|
||||
expect(isInvalidPoint({ clientX: 0, clientY: 0 }, { clientX: 6, clientY: 1 })).toBeTruthy();
|
||||
expect(isInvalidPoint({ clientX: 0, clientY: 0 }, { clientX: 1, clientY: 6 })).toBeTruthy();
|
||||
expect(isInvalidPoint({ clientX: 0, clientY: 0 }, { clientX: 6, clientY: 6 })).toBeTruthy();
|
||||
expect(isInvalidPoint({ clientX: 1, clientY: 1 }, { clientX: 2, clientY: 1 })).toBeFalsy();
|
||||
});
|
||||
|
||||
it('isSameAs', () => {
|
||||
expect(isSameAs({ clientX: 1, clientY: 1 }, { clientX: 1, clientY: 1 })).toBeTruthy();
|
||||
expect(isSameAs({ clientX: 1, clientY: 1 }, { clientX: 2, clientY: 1 })).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@ -1,209 +0,0 @@
|
||||
import {
|
||||
DropLocation,
|
||||
isLocationData,
|
||||
isLocationChildrenDetail,
|
||||
isRowContainer,
|
||||
isChildInline,
|
||||
getRectTarget,
|
||||
isVerticalContainer,
|
||||
isVertical,
|
||||
getWindow,
|
||||
} from '../../src/designer/location';
|
||||
import { getMockElement } from '../utils';
|
||||
|
||||
describe('DropLocation 测试', () => {
|
||||
it('constructor', () => {
|
||||
const mockTarget = { document };
|
||||
const mockDetail = {};
|
||||
const mockSource = {};
|
||||
const mockEvent = { type: 'LocateEvent', nodes: [] };
|
||||
const loc = new DropLocation({
|
||||
target: mockTarget,
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
|
||||
expect(loc.getContainer()).toBe(mockTarget);
|
||||
expect(loc.document).toBe(document);
|
||||
expect(loc.target).toBe(mockTarget);
|
||||
expect(loc.detail).toBe(mockDetail);
|
||||
expect(loc.source).toBe(mockSource);
|
||||
expect(loc.event).toBe(mockEvent);
|
||||
|
||||
const mockEvent2 = { type: 'LocateEvent', data: [] };
|
||||
const loc2 = loc.clone(mockEvent2);
|
||||
expect(loc2.target).toBe(mockTarget);
|
||||
expect(loc2.detail).toBe(mockDetail);
|
||||
expect(loc2.source).toBe(mockSource);
|
||||
expect(loc2.event).toBe(mockEvent2);
|
||||
});
|
||||
|
||||
it('constructor, detail: undefined', () => {
|
||||
const mockTarget = { document };
|
||||
const mockDetail = undefined;
|
||||
const mockSource = {};
|
||||
const mockEvent = { type: 'LocateEvent', nodes: [] };
|
||||
const loc = new DropLocation({
|
||||
target: mockTarget,
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
|
||||
expect(loc.getInsertion()).toBeNull();
|
||||
});
|
||||
|
||||
it('constructor, detail.type: Children, detail.index <= 0', () => {
|
||||
const mockTarget = { document };
|
||||
const mockDetail = { type: 'Children', index: -1 };
|
||||
const mockSource = {};
|
||||
const mockEvent = { type: 'LocateEvent', nodes: [] };
|
||||
const loc = new DropLocation({
|
||||
target: mockTarget,
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
|
||||
expect(loc.getInsertion()).toBeNull();
|
||||
});
|
||||
|
||||
it('constructor, detail.type: Children, detail.index > 0', () => {
|
||||
const mockTarget = {
|
||||
document,
|
||||
children: {
|
||||
get(x) {
|
||||
return x;
|
||||
},
|
||||
},
|
||||
};
|
||||
const mockDetail = { type: 'Children', index: 1 };
|
||||
const mockSource = {};
|
||||
const mockEvent = { type: 'LocateEvent', nodes: [] };
|
||||
const loc = new DropLocation({
|
||||
target: mockTarget,
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
|
||||
expect(loc.getInsertion()).toBe(0);
|
||||
});
|
||||
|
||||
it('constructor, detail.type: Prop', () => {
|
||||
const mockTarget = {
|
||||
document,
|
||||
children: {
|
||||
get(x) {
|
||||
return x;
|
||||
},
|
||||
},
|
||||
};
|
||||
const mockDetail = { type: 'Prop', index: 1, near: { node: { x: 1 } } };
|
||||
const mockSource = {};
|
||||
const mockEvent = { type: 'LocateEvent', nodes: [] };
|
||||
const loc = new DropLocation({
|
||||
target: mockTarget,
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
|
||||
expect(loc.getInsertion()).toEqual({ x: 1 });
|
||||
});
|
||||
});
|
||||
|
||||
it('isLocationData', () => {
|
||||
expect(isLocationData({ target: {}, detail: {} })).toBeTruthy();
|
||||
});
|
||||
|
||||
it('isLocationChildrenDetail', () => {
|
||||
expect(isLocationChildrenDetail({ type: 'Children' })).toBeTruthy();
|
||||
});
|
||||
|
||||
it('isRowContainer', () => {
|
||||
expect(isRowContainer({ nodeType: Node.TEXT_NODE })).toBeTruthy();
|
||||
window.getComputedStyle = jest
|
||||
.fn(() => {
|
||||
return {
|
||||
getPropertyValue: (pName) => {
|
||||
return pName === 'display' ? 'flex' : '';
|
||||
},
|
||||
};
|
||||
})
|
||||
.mockImplementationOnce(() => {
|
||||
return {
|
||||
getPropertyValue: (pName) => {
|
||||
return pName === 'display' ? 'flex' : 'column';
|
||||
},
|
||||
};
|
||||
})
|
||||
.mockImplementationOnce(() => {
|
||||
return {
|
||||
getPropertyValue: (pName) => {
|
||||
return pName === 'display' ? 'grid' : 'column';
|
||||
},
|
||||
};
|
||||
});
|
||||
expect(isRowContainer(getMockElement('div'))).toBeFalsy();
|
||||
expect(isRowContainer(getMockElement('div'))).toBeTruthy();
|
||||
expect(isRowContainer(getMockElement('div'))).toBeTruthy();
|
||||
});
|
||||
|
||||
it('isChildInline', () => {
|
||||
window.getComputedStyle = jest
|
||||
.fn(() => {
|
||||
return {
|
||||
getPropertyValue: (pName) => {
|
||||
return pName === 'display' ? 'inline' : 'float';
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
expect(isChildInline({ nodeType: Node.TEXT_NODE })).toBeTruthy();
|
||||
expect(isChildInline(getMockElement('div'))).toBeTruthy();
|
||||
});
|
||||
|
||||
it('getRectTarget', () => {
|
||||
expect(getRectTarget()).toBeNull();
|
||||
expect(getRectTarget({ computed: false })).toBeNull();
|
||||
expect(getRectTarget({ elements: [{}] })).toEqual({});
|
||||
});
|
||||
|
||||
it('isVerticalContainer', () => {
|
||||
window.getComputedStyle = jest
|
||||
.fn(() => {
|
||||
return {
|
||||
getPropertyValue: (pName) => {
|
||||
return pName === 'display' ? 'flex' : 'row';
|
||||
},
|
||||
};
|
||||
});
|
||||
expect(isVerticalContainer()).toBeFalsy();
|
||||
expect(isVerticalContainer({ elements: [getMockElement('div')] })).toBeTruthy();
|
||||
});
|
||||
|
||||
it('isVertical', () => {
|
||||
expect(isVertical({ elements: [] })).toBeFalsy();
|
||||
expect(isVertical({ elements: [getMockElement('div')] })).toBeFalsy();
|
||||
const e1 = getMockElement('div');
|
||||
const e2 = getMockElement('div');
|
||||
e2.appendChild(e1);
|
||||
expect(isVertical({ elements: [e1] })).toBeTruthy();
|
||||
window.getComputedStyle = jest
|
||||
.fn(() => {
|
||||
return {
|
||||
getPropertyValue: (pName) => {
|
||||
return pName === 'display' ? 'inline' : 'float';
|
||||
},
|
||||
};
|
||||
});
|
||||
expect(isVertical({ elements: [getMockElement('div')] })).toBeTruthy();
|
||||
});
|
||||
|
||||
it('getWindow', () => {
|
||||
const mockElem = getMockElement('div');
|
||||
expect(getWindow(mockElem)).toBe(window);
|
||||
expect(getWindow(document)).toBe(window);
|
||||
});
|
||||
@ -1,136 +0,0 @@
|
||||
import '../fixtures/window';
|
||||
import { Editor, globalContext } from '@alilc/lowcode-editor-core';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { DocumentModel } from '../../src/document/document-model';
|
||||
import { ScrollTarget, Scroller } from '../../src/designer/scroller';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import {
|
||||
Dragon,
|
||||
} from '../../src/designer/dragon';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
describe('Scroller 测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
let dragon: Dragon;
|
||||
|
||||
beforeAll(() => {
|
||||
editor = new Editor();
|
||||
!globalContext.has(Editor) && globalContext.register(editor, Editor);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
project = designer.project;
|
||||
doc = project.createDocument(formSchema);
|
||||
dragon = new Dragon(designer);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
project.mountSimulator(undefined);
|
||||
designer.purge();
|
||||
designer = null;
|
||||
project = null;
|
||||
dragon = null;
|
||||
});
|
||||
|
||||
function getMockWindow() {
|
||||
let scrollX = 0;
|
||||
let scrollY = 0;
|
||||
const mockWindow = {
|
||||
scrollTo(x, y) {
|
||||
if (typeof x === 'number') {
|
||||
scrollX = x;
|
||||
scrollY = y;
|
||||
} else {
|
||||
scrollX = x.left;
|
||||
scrollY = x.top;
|
||||
}
|
||||
},
|
||||
get scrollX() { return scrollX; },
|
||||
get scrollY() { return scrollY; },
|
||||
scrollHeight: 1000,
|
||||
scrollWidth: 500,
|
||||
document: {},
|
||||
nodeType: Node.ELEMENT_NODE,
|
||||
};
|
||||
return mockWindow;
|
||||
}
|
||||
|
||||
describe('ScrollTarget 测试', () => {
|
||||
it('constructor', () => {
|
||||
const win = getMockWindow();
|
||||
const target = new ScrollTarget(win);
|
||||
expect(target.scrollWidth).toBe(500);
|
||||
expect(target.scrollHeight).toBe(1000);
|
||||
target.scrollToXY(50, 50);
|
||||
expect(target.left).toBe(50);
|
||||
expect(target.top).toBe(50);
|
||||
|
||||
target.scrollTo({ left: 100, top: 100 });
|
||||
expect(target.left).toBe(100);
|
||||
expect(target.top).toBe(100);
|
||||
});
|
||||
});
|
||||
|
||||
function mockRAF() {
|
||||
let rafCount = 0;
|
||||
window.requestAnimationFrame = (fn) => {
|
||||
if (rafCount++ < 2) {
|
||||
fn();
|
||||
} else {
|
||||
window.requestAnimationFrame = () => {};
|
||||
}
|
||||
};
|
||||
}
|
||||
describe('Scroller 测试', () => {
|
||||
it('scrollTarget: ScrollTarget', () => {
|
||||
const win = getMockWindow();
|
||||
const scrollTarget = new ScrollTarget(win);
|
||||
const scroller = new Scroller({ scrollTarget, bounds: { width: 50, height: 50, top: 50, bottom: 50, left: 50, right: 50 } });
|
||||
mockRAF();
|
||||
scroller.scrollTo({ left: 50, top: 50 });
|
||||
|
||||
mockRAF();
|
||||
scroller.scrolling({ globalX: 100, globalY: 100 });
|
||||
});
|
||||
|
||||
it('scrollTarget: ScrollTarget, same left / top', () => {
|
||||
const win = getMockWindow();
|
||||
const scrollTarget = new ScrollTarget(win);
|
||||
const scroller = new Scroller({ scrollTarget, bounds: { width: 50, height: 50, top: 50, bottom: 50, left: 50, right: 50 } });
|
||||
mockRAF();
|
||||
scrollTarget.scrollTo({ left: 50, top: 50 });
|
||||
scroller.scrollTo({ left: 50, top: 50 });
|
||||
|
||||
mockRAF();
|
||||
scroller.scrolling({ globalX: 100, globalY: 100 });
|
||||
});
|
||||
|
||||
it('scrollTarget: Element', () => {
|
||||
const win = getMockWindow();
|
||||
// const scrollTarget = new ScrollTarget(win);
|
||||
const scroller = new Scroller({ scrollTarget: win, bounds: { width: 50, height: 50, top: 50, bottom: 50, left: 50, right: 50 } });
|
||||
mockRAF();
|
||||
scroller.scrollTo({ left: 50, top: 50 });
|
||||
|
||||
mockRAF();
|
||||
scroller.scrolling({ globalX: 100, globalY: 100 });
|
||||
});
|
||||
|
||||
it('scrollTarget: null', () => {
|
||||
const win = getMockWindow();
|
||||
// const scrollTarget = new ScrollTarget(win);
|
||||
const scroller = new Scroller({ scrollTarget: null, bounds: { width: 50, height: 50, top: 50, bottom: 50, left: 50, right: 50 } });
|
||||
mockRAF();
|
||||
scroller.scrollTo({ left: 50, top: 50 });
|
||||
|
||||
mockRAF();
|
||||
scroller.scrolling({ globalX: 100, globalY: 100 });
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,79 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`setting-field 测试 纯粹的 UnitTest 常规方法 1`] = `
|
||||
Object {
|
||||
"extraProps": Object {
|
||||
"defaultValue": "NORMAL",
|
||||
"display": "inline",
|
||||
},
|
||||
"name": "behavior",
|
||||
"setter": Object {
|
||||
"componentName": "MixedSetter",
|
||||
"props": Object {
|
||||
"setters": Array [
|
||||
Object {
|
||||
"_owner": null,
|
||||
"key": null,
|
||||
"props": Object {
|
||||
"cancelable": false,
|
||||
"loose": false,
|
||||
"options": Array [
|
||||
Object {
|
||||
"title": "普通",
|
||||
"value": "NORMAL",
|
||||
},
|
||||
Object {
|
||||
"title": "隐藏",
|
||||
"value": "HIDDEN",
|
||||
},
|
||||
],
|
||||
},
|
||||
"ref": null,
|
||||
},
|
||||
"VariableSetter",
|
||||
],
|
||||
},
|
||||
},
|
||||
"title": "默认状态",
|
||||
"type": "field",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`setting-field 测试 纯粹的 UnitTest 常规方法 2`] = `
|
||||
Object {
|
||||
"extraProps": Object {
|
||||
"defaultValue": "NORMAL",
|
||||
"display": "inline",
|
||||
},
|
||||
"name": "behavior",
|
||||
"setter": Object {
|
||||
"componentName": "MixedSetter",
|
||||
"props": Object {
|
||||
"setters": Array [
|
||||
Object {
|
||||
"_owner": null,
|
||||
"key": null,
|
||||
"props": Object {
|
||||
"cancelable": false,
|
||||
"loose": false,
|
||||
"options": Array [
|
||||
Object {
|
||||
"title": "普通",
|
||||
"value": "NORMAL",
|
||||
},
|
||||
Object {
|
||||
"title": "隐藏",
|
||||
"value": "HIDDEN",
|
||||
},
|
||||
],
|
||||
},
|
||||
"ref": null,
|
||||
},
|
||||
"VariableSetter",
|
||||
],
|
||||
},
|
||||
},
|
||||
"title": "默认状态",
|
||||
"type": "field",
|
||||
}
|
||||
`;
|
||||
@ -1,281 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import '../../fixtures/window';
|
||||
import {
|
||||
Editor,
|
||||
Setters as InnerSetters,
|
||||
} from '@alilc/lowcode-editor-core';
|
||||
import {
|
||||
Setters,
|
||||
} from '@alilc/lowcode-shell';
|
||||
import { SettingTopEntry } from '../../../src/designer/setting/setting-top-entry';
|
||||
import { SettingField } from '../../../src/designer/setting/setting-field';
|
||||
import { Node } from '../../../src/document/node/node';
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import settingSchema from '../../fixtures/schema/setting';
|
||||
import buttonMeta from '../../fixtures/component-metadata/button';
|
||||
import { DocumentModel } from 'designer/src/document';
|
||||
import { delayObxTick } from '../../utils';
|
||||
import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
const editor = new Editor();
|
||||
|
||||
describe('setting-field 测试', () => {
|
||||
let designer: Designer;
|
||||
let doc: DocumentModel;
|
||||
let setters: Setters;
|
||||
beforeEach(() => {
|
||||
setters = new InnerSetters();
|
||||
editor.set('setters', setters);
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
designer.createComponentMeta(buttonMeta);
|
||||
doc = designer.project.open(settingSchema);
|
||||
});
|
||||
afterEach(() => {
|
||||
designer._componentMetasMap.clear();
|
||||
designer = null;
|
||||
doc.purge();
|
||||
doc = null;
|
||||
});
|
||||
|
||||
describe('纯粹的 UnitTest', () => {
|
||||
let mockNode: Node;
|
||||
let mockTopEntry: SettingTopEntry;
|
||||
beforeEach(() => {
|
||||
mockNode = new Node(designer.currentDocument, {
|
||||
componentName: 'Button',
|
||||
props: {
|
||||
// a: 'str',
|
||||
// b: 222,
|
||||
// obj: {
|
||||
// x: 1,
|
||||
// },
|
||||
// jse: {
|
||||
// type: 'JSExpression',
|
||||
// value: 'state.a',
|
||||
// mock: 111,
|
||||
// }
|
||||
},
|
||||
});
|
||||
// mockTopEntry = new SettingTopEntry(editor, [mockNode]);
|
||||
});
|
||||
afterEach(() => {
|
||||
mockNode = null;
|
||||
mockTopEntry = null;
|
||||
});
|
||||
|
||||
it('常规方法', () => {
|
||||
// 普通 field
|
||||
const settingEntry = mockNode.settingEntry;
|
||||
const field = settingEntry.get('behavior');
|
||||
expect(field.title).toBe('默认状态');
|
||||
expect(field.expanded).toBeTruthy();
|
||||
field.setExpanded(false);
|
||||
expect(field.expanded).toBeFalsy();
|
||||
expect(field.config).toMatchSnapshot();
|
||||
expect(field.getConfig()).toMatchSnapshot();
|
||||
expect(field.getConfig('extraProps')).toEqual({
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
});
|
||||
expect(field.items).toHaveLength(0);
|
||||
expect(field.getItems()).toHaveLength(0);
|
||||
expect(field.getItems(x => x)).toHaveLength(0);
|
||||
|
||||
expect(field.setter.componentName).toBe('MixedSetter');
|
||||
field.purge();
|
||||
expect(field.items).toHaveLength(0);
|
||||
|
||||
const subField = field.createField({
|
||||
name: 'sub',
|
||||
title: 'sub',
|
||||
});
|
||||
subField.setValue({
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
mock: 'haha',
|
||||
});
|
||||
subField.setHotValue('heihei');
|
||||
expect(subField.getHotValue('heihei'));
|
||||
expect(subField.getValue().mock).toBe('heihei');
|
||||
|
||||
// 不存在的 field
|
||||
const nonExistingField = mockNode.settingEntry.get('non-exsiting');
|
||||
expect(nonExistingField.setter).toBeNull();
|
||||
|
||||
// group 类型的 field
|
||||
const groupField = settingEntry.get('groupkgzzeo41');
|
||||
expect(groupField.items).toEqual([]);
|
||||
|
||||
// 有子节点的 field
|
||||
const objField = settingEntry.get('obj');
|
||||
expect(objField.items).toHaveLength(3);
|
||||
expect(objField.getItems()).toHaveLength(3);
|
||||
expect(objField.getItems(x => x.name === 'a')).toHaveLength(1);
|
||||
objField.purge();
|
||||
expect(objField.items).toHaveLength(0);
|
||||
const objAField = settingEntry.get('obj.a');
|
||||
expect(objAField.setter).toBe('StringSetter');
|
||||
});
|
||||
|
||||
it('setValue / getValue / setHotValue / getHotValue', () => {
|
||||
// 获取已有的 prop
|
||||
const settingEntry = mockNode.settingEntry as SettingTopEntry;
|
||||
const field = settingEntry.get('behavior');
|
||||
|
||||
// 会读取 extraProps.defaultValue
|
||||
expect(field.getHotValue()).toBe('NORMAL');
|
||||
|
||||
field.setValue('HIDDEN');
|
||||
expect(field.getValue()).toBe('HIDDEN');
|
||||
expect(field.getHotValue()).toBe('HIDDEN');
|
||||
|
||||
field.setHotValue('DISABLED');
|
||||
expect(field.getHotValue()).toBe('DISABLED');
|
||||
|
||||
field.setHotValue('NORMAL', { fromSetHotValue: true });
|
||||
expect(field.getHotValue()).toBe('NORMAL');
|
||||
|
||||
field.setValue('HIDDEN', true);
|
||||
expect(field.getHotValue()).toBe('HIDDEN');
|
||||
|
||||
// dirty fix list setter
|
||||
field.setHotValue([{ __sid__: 1 }]);
|
||||
|
||||
// 数组的 field
|
||||
const arrField = settingEntry.get('arr');
|
||||
const subArrField = arrField.createField({
|
||||
name: 0,
|
||||
title: 'sub',
|
||||
});
|
||||
const subArrField02 = arrField.createField({
|
||||
name: 1,
|
||||
title: 'sub',
|
||||
});
|
||||
const subArrField03 = arrField.createField({
|
||||
name: '2',
|
||||
title: 'sub',
|
||||
});
|
||||
subArrField.setValue({name: '1'});
|
||||
expect(subArrField.path).toEqual(['arr', 0]);
|
||||
expect(subArrField02.path).toEqual(['arr', 1]);
|
||||
subArrField02.setValue({name: '2'});
|
||||
expect(subArrField.getValue()).toEqual({name: '1'});
|
||||
expect(arrField.getHotValue()).toEqual([{name: '1'}, {name: '2'}]);
|
||||
subArrField.clearValue();
|
||||
expect(subArrField.getValue()).toBeUndefined();
|
||||
expect(arrField.getHotValue()).toEqual([undefined, {name: '2'}]);
|
||||
subArrField03.setValue({name: '3'});
|
||||
expect(arrField.getHotValue()).toEqual([undefined, {name: '2'}, {name: '3'}]);
|
||||
});
|
||||
|
||||
it('js expression setValue / setHotValue', () => {
|
||||
const settingEntry = mockNode.settingEntry;
|
||||
const field = settingEntry.get('behavior');
|
||||
|
||||
const subField = field.createField({
|
||||
name: 'sub',
|
||||
title: 'sub',
|
||||
});
|
||||
subField.setValue({
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
mock: 'haha',
|
||||
});
|
||||
|
||||
subField.setHotValue({
|
||||
type: 'JSExpression',
|
||||
value: 'state.b',
|
||||
});
|
||||
|
||||
expect(subField.getValue()).toEqual({
|
||||
type: 'JSExpression',
|
||||
value: 'state.b',
|
||||
mock: 'haha',
|
||||
});
|
||||
|
||||
subField.setHotValue('mock02');
|
||||
|
||||
expect(subField.getValue()).toEqual({
|
||||
type: 'JSExpression',
|
||||
value: 'state.b',
|
||||
mock: 'mock02',
|
||||
});
|
||||
});
|
||||
|
||||
it('onEffect', async () => {
|
||||
const settingEntry = mockNode.settingEntry as SettingTopEntry;
|
||||
const field = settingEntry.get('behavior');
|
||||
|
||||
const mockFn = jest.fn();
|
||||
|
||||
field.onEffect(mockFn);
|
||||
|
||||
field.setValue('DISABLED');
|
||||
|
||||
await delayObxTick();
|
||||
|
||||
expect(mockFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('autorun', async () => {
|
||||
const settingEntry = mockNode.settingEntry as SettingTopEntry;
|
||||
const arrField = settingEntry.get('columns');
|
||||
const subArrField = arrField.createField({
|
||||
name: 0,
|
||||
title: 'sub',
|
||||
});
|
||||
const objSubField = subArrField.createField({
|
||||
name: 'objSub',
|
||||
title: 'objSub',
|
||||
});
|
||||
const mockFnArrField = jest.fn();
|
||||
const mockFnSubArrField = jest.fn();
|
||||
const mockFnObjSubField = jest.fn();
|
||||
|
||||
arrField.setValue([{ objSub: "subMock0.Index.0" }]);
|
||||
// 这里需要 setValue 两遍,触发 prop 的 purge 方法,使 purged 为 true,之后的 purge 方法不会正常执行,prop 才能正常缓存,autorun 才能正常执行
|
||||
// TODO: 该机制后续得研究一下,再确定是否要修改
|
||||
arrField.setValue([{ objSub: "subMock0.Index.0" }]);
|
||||
|
||||
arrField.onEffect(() => {
|
||||
mockFnArrField(arrField.getValue());
|
||||
});
|
||||
arrField.onEffect(() => {
|
||||
mockFnSubArrField(subArrField.getValue());
|
||||
});
|
||||
arrField.onEffect(() => {
|
||||
mockFnObjSubField(objSubField.getValue());
|
||||
});
|
||||
|
||||
await delayObxTick();
|
||||
|
||||
expect(mockFnObjSubField).toHaveBeenCalledWith('subMock0.Index.0');
|
||||
expect(mockFnSubArrField).toHaveBeenCalledWith({ objSub: "subMock0.Index.0" });
|
||||
expect(mockFnArrField).toHaveBeenCalledWith([{ objSub: "subMock0.Index.0" }]);
|
||||
|
||||
arrField.setValue([{ objSub: "subMock0.Index.1" }]);
|
||||
|
||||
await delayObxTick();
|
||||
|
||||
expect(mockFnObjSubField).toHaveBeenCalledWith('subMock0.Index.1');
|
||||
expect(mockFnSubArrField).toHaveBeenCalledWith({ objSub: "subMock0.Index.1" });
|
||||
expect(mockFnArrField).toHaveBeenCalledWith([{ objSub: "subMock0.Index.1" }]);
|
||||
|
||||
subArrField.setValue({ objSub: "subMock0.Index.2" });
|
||||
|
||||
await delayObxTick();
|
||||
|
||||
expect(mockFnObjSubField).toHaveBeenCalledWith('subMock0.Index.2');
|
||||
expect(mockFnSubArrField).toHaveBeenCalledWith({ objSub: "subMock0.Index.2" });
|
||||
expect(mockFnArrField).toHaveBeenCalledWith([{ objSub: "subMock0.Index.2" }]);
|
||||
|
||||
objSubField.setValue('subMock0.Index.3');
|
||||
|
||||
await delayObxTick();
|
||||
|
||||
expect(mockFnObjSubField).toHaveBeenCalledWith('subMock0.Index.3');
|
||||
expect(mockFnSubArrField).toHaveBeenCalledWith({ objSub: "subMock0.Index.3" });
|
||||
expect(mockFnArrField).toHaveBeenCalledWith([{ objSub: "subMock0.Index.3" }]);
|
||||
})
|
||||
});
|
||||
});
|
||||
@ -1,230 +0,0 @@
|
||||
import '../../fixtures/window';
|
||||
import {
|
||||
Editor,
|
||||
Setters as InnerSetters,
|
||||
} from '@alilc/lowcode-editor-core';
|
||||
import { SettingTopEntry } from '../../../src/designer/setting/setting-top-entry';
|
||||
import { SettingPropEntry } from '../../../src/designer/setting/setting-prop-entry';
|
||||
import { Node } from '../../../src/document/node/node';
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import settingSchema from '../../fixtures/schema/setting';
|
||||
import divMeta from '../../fixtures/component-metadata/div';
|
||||
import { DocumentModel } from 'designer/src/document';
|
||||
import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
const editor = new Editor();
|
||||
|
||||
describe('setting-prop-entry 测试', () => {
|
||||
let designer: Designer;
|
||||
let doc: DocumentModel;
|
||||
let setters: any;
|
||||
beforeEach(() => {
|
||||
setters = new InnerSetters();
|
||||
editor.set('setters', setters);
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
designer.createComponentMeta(divMeta);
|
||||
doc = designer.project.open(settingSchema);
|
||||
});
|
||||
afterEach(() => {
|
||||
designer._componentMetasMap.clear();
|
||||
designer = null;
|
||||
doc.purge();
|
||||
doc = null;
|
||||
});
|
||||
|
||||
describe('纯粹的 UnitTest', () => {
|
||||
let mockNode: Node;
|
||||
let mockTopEntry: SettingTopEntry;
|
||||
beforeEach(() => {
|
||||
mockNode = new Node(designer.currentDocument, {
|
||||
componentName: 'Button',
|
||||
props: {
|
||||
a: 'str',
|
||||
b: 222,
|
||||
obj: {
|
||||
x: 1,
|
||||
},
|
||||
jse: {
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
mock: 111,
|
||||
}
|
||||
},
|
||||
});
|
||||
mockTopEntry = new SettingTopEntry(editor, [mockNode]);
|
||||
});
|
||||
afterEach(() => {
|
||||
mockNode = null;
|
||||
mockTopEntry = null;
|
||||
});
|
||||
|
||||
it('常规方法', () => {
|
||||
// type: group 类型
|
||||
const prop = new SettingPropEntry(mockTopEntry, 'xGroup', 'group');
|
||||
expect(prop.setKey('xxx')).toBeUndefined();
|
||||
expect(prop.remove()).toBeUndefined();
|
||||
|
||||
const prop2 = new SettingPropEntry(mockTopEntry, '#xGroup');
|
||||
expect(prop2.setKey('xxx')).toBeUndefined();
|
||||
expect(prop2.remove()).toBeUndefined();
|
||||
|
||||
expect(prop.getVariableValue()).toBe('');
|
||||
});
|
||||
|
||||
it('setValue / getValue / onValueChange', () => {
|
||||
// 获取已有的 prop
|
||||
const prop1 = mockTopEntry.getProp('a');
|
||||
prop1.extraProps = {
|
||||
getValue: (prop, val) => `prefix ${val}`,
|
||||
// prop 是 shell prop entry
|
||||
setValue: (prop, val) => { prop.setValue(`modified ${val}`, { disableMutator: true }) },
|
||||
defaultValue: 'default',
|
||||
};
|
||||
|
||||
expect(prop1.getDefaultValue()).toBe('default');
|
||||
expect(prop1.getValue()).toBe('prefix str');
|
||||
|
||||
// disableMutator: true
|
||||
prop1.setValue('bbb', false, false, { disableMutator: true });
|
||||
expect(prop1.getValue()).toBe('prefix bbb');
|
||||
|
||||
// disableMutator: false
|
||||
prop1.setValue('bbb');
|
||||
expect(prop1.getValue()).toBe('prefix modified bbb');
|
||||
|
||||
const mockFn3 = jest.fn();
|
||||
const prop2 = mockTopEntry.getProp('obj');
|
||||
const prop3 = prop2.get('x');
|
||||
const offFn = prop3.onValueChange(mockFn3);
|
||||
expect(prop3.getValue()).toBe(1);
|
||||
prop3.setValue(2);
|
||||
expect(mockFn3).toHaveBeenCalled();
|
||||
|
||||
offFn();
|
||||
prop3.setValue(3);
|
||||
mockFn3.mockClear();
|
||||
expect(mockFn3).toHaveBeenCalledTimes(0);
|
||||
|
||||
const prop4 = mockTopEntry.getProp('b');
|
||||
prop4.extraProps = {
|
||||
getValue: () => { throw 'error'; },
|
||||
};
|
||||
expect(prop4.getValue()).toBe(222);
|
||||
});
|
||||
|
||||
it('clearValue', () => {
|
||||
const prop1 = mockTopEntry.getProp('a');
|
||||
prop1.clearValue();
|
||||
expect(prop1.getValue()).toBeUndefined();
|
||||
|
||||
const mockFn = jest.fn();
|
||||
prop1.extraProps = {
|
||||
setValue: mockFn,
|
||||
};
|
||||
prop1.clearValue();
|
||||
expect(mockFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('getVariableValue/ setUseVariable / isUseVariable / getMockOrValue', () => {
|
||||
const prop1 = mockTopEntry.getProp('jse');
|
||||
|
||||
expect(prop1.isUseVariable()).toBeTruthy();
|
||||
expect(prop1.useVariable).toBeTruthy();
|
||||
|
||||
expect(prop1.getMockOrValue()).toEqual(111);
|
||||
expect(prop1.getVariableValue()).toEqual('state.a');
|
||||
|
||||
prop1.setUseVariable(false);
|
||||
expect(prop1.getValue()).toEqual(111);
|
||||
prop1.setUseVariable(true);
|
||||
expect(prop1.getValue()).toEqual({
|
||||
type: 'JSExpression',
|
||||
value: '',
|
||||
mock: 111,
|
||||
});
|
||||
prop1.setUseVariable(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('node 构造函数生成 settingEntry', () => {
|
||||
it('常规方法测试', () => {
|
||||
const divNode = doc?.getNode('div');
|
||||
|
||||
const { settingEntry } = divNode!;
|
||||
const behaviorProp = settingEntry.getProp('behavior');
|
||||
expect(behaviorProp.getProps()).toBe(settingEntry);
|
||||
expect(behaviorProp.props).toBe(settingEntry);
|
||||
expect(behaviorProp.getName()).toBe('behavior');
|
||||
expect(behaviorProp.getKey()).toBe('behavior');
|
||||
expect(behaviorProp.isIgnore()).toBeFalsy();
|
||||
behaviorProp.setKey('behavior2');
|
||||
expect(behaviorProp.getKey()).toBe('behavior2');
|
||||
behaviorProp.setKey('behavior');
|
||||
|
||||
expect(behaviorProp.getNode()).toBe(divNode);
|
||||
expect(behaviorProp.getId().startsWith('entry')).toBeTruthy();
|
||||
expect(behaviorProp.designer).toBe(designer);
|
||||
expect(behaviorProp.isSingle).toBeTruthy();
|
||||
expect(behaviorProp.isMultiple).toBeFalsy();
|
||||
expect(behaviorProp.isGroup).toBeFalsy();
|
||||
expect(behaviorProp.isSameComponent).toBeTruthy();
|
||||
expect(typeof settingEntry.getValue).toBe('function');
|
||||
settingEntry.getValue();
|
||||
|
||||
behaviorProp.setExtraPropValue('extraPropA', 'heihei');
|
||||
expect(behaviorProp.getExtraPropValue('extraPropA', 'heihei'));
|
||||
});
|
||||
|
||||
it('setValue / getValue', () => {
|
||||
const divNode = doc?.getNode('div');
|
||||
|
||||
const { settingEntry } = divNode!;
|
||||
const behaviorProp = settingEntry.getProp('behavior');
|
||||
expect(behaviorProp.getValue()).toBe('NORMAL');
|
||||
expect(behaviorProp.getMockOrValue()).toBe('NORMAL');
|
||||
|
||||
behaviorProp.setValue('LARGE');
|
||||
expect(behaviorProp.getValue()).toBe('LARGE');
|
||||
// behaviorProp.setPropValue('behavior', 'SMALL');
|
||||
// expect(behaviorProp.getValue()).toBe('SMALL');
|
||||
behaviorProp.setValue('NORMAL');
|
||||
expect(behaviorProp.getValue()).toBe('NORMAL');
|
||||
behaviorProp.clearValue();
|
||||
behaviorProp.clearPropValue();
|
||||
expect(behaviorProp.getValue()).toBeUndefined();
|
||||
|
||||
behaviorProp.setValue('LARGE');
|
||||
expect(behaviorProp.getValue()).toBe('LARGE');
|
||||
behaviorProp.remove();
|
||||
expect(divNode?.getProp('behavior').getValue()).toBeUndefined();
|
||||
});
|
||||
|
||||
it.skip('type: group 场景测试', () => {});
|
||||
|
||||
it('JSExpression 类型的 prop', () => {
|
||||
designer.createComponentMeta(divMeta);
|
||||
designer.project.open(settingSchema);
|
||||
const { currentDocument } = designer.project;
|
||||
const divNode = currentDocument?.getNode('div');
|
||||
|
||||
const { settingEntry } = divNode!;
|
||||
const customClassNameProp = settingEntry.getProp('customClassName');
|
||||
expect(customClassNameProp.isUseVariable()).toBeTruthy();
|
||||
expect(customClassNameProp.useVariable).toBeTruthy();
|
||||
|
||||
expect(customClassNameProp.getValue()).toEqual({
|
||||
type: 'JSExpression',
|
||||
value: 'getFromSomewhere()',
|
||||
});
|
||||
expect(customClassNameProp.getMockOrValue()).toBeUndefined();
|
||||
expect(customClassNameProp.getVariableValue()).toBe('getFromSomewhere()');
|
||||
customClassNameProp.setVariableValue('xxx');
|
||||
expect(customClassNameProp.getVariableValue()).toBe('xxx');
|
||||
|
||||
const customClassName2Prop = settingEntry.getProp('customClassName2');
|
||||
expect(customClassName2Prop.getMockOrValue()).toEqual({
|
||||
hi: 'mock',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,196 +0,0 @@
|
||||
import '../../fixtures/window';
|
||||
import { Editor, Setters } from '@alilc/lowcode-editor-core';
|
||||
import { Node } from '../../../src/document/node/node';
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import settingSchema from '../../fixtures/schema/setting';
|
||||
import divMeta from '../../fixtures/component-metadata/div';
|
||||
import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
const editor = new Editor();
|
||||
|
||||
describe('setting-top-entry 测试', () => {
|
||||
let designer: Designer;
|
||||
beforeEach(() => {
|
||||
editor.set('setters', new Setters())
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
});
|
||||
afterEach(() => {
|
||||
designer._componentMetasMap.clear();
|
||||
designer = null;
|
||||
});
|
||||
|
||||
describe('node 构造函数生成 settingEntry', () => {
|
||||
it('常规方法测试', () => {
|
||||
designer.createComponentMeta(divMeta);
|
||||
designer.project.open(settingSchema);
|
||||
const { currentDocument } = designer.project;
|
||||
const divNode = currentDocument?.getNode('div');
|
||||
|
||||
const { settingEntry } = divNode!;
|
||||
expect(settingEntry.getPropValue('behavior')).toBe('NORMAL');
|
||||
expect(settingEntry.getProp('behavior').getValue()).toBe('NORMAL');
|
||||
settingEntry.setPropValue('behavior', 'LARGE');
|
||||
expect(settingEntry.getPropValue('behavior')).toBe('LARGE');
|
||||
expect(settingEntry.get('behavior').getValue()).toBe('LARGE');
|
||||
settingEntry.getProp('behavior').setValue('SMALL');
|
||||
expect(settingEntry.getPropValue('behavior')).toBe('SMALL');
|
||||
settingEntry.clearPropValue('behavior');
|
||||
expect(settingEntry.getPropValue('behavior')).toBeUndefined();
|
||||
|
||||
expect(settingEntry.getPropValue('fieldId')).toBe('div_k1ow3h1o');
|
||||
settingEntry.setPropValue('fieldId', 'div_k1ow3h1o_new');
|
||||
expect(settingEntry.getPropValue('fieldId')).toBe('div_k1ow3h1o_new');
|
||||
|
||||
expect(settingEntry.getExtraPropValue('extraPropA')).toBe('haha');
|
||||
settingEntry.setExtraPropValue('extraPropA', 'haha2');
|
||||
expect(settingEntry.getExtraPropValue('extraPropA')).toBe('haha2');
|
||||
|
||||
settingEntry.mergeProps({
|
||||
newPropA: 'haha',
|
||||
});
|
||||
expect(settingEntry.getPropValue('newPropA')).toBe('haha');
|
||||
settingEntry.setProps({
|
||||
newPropB: 'haha',
|
||||
});
|
||||
expect(settingEntry.getPropValue('newPropB')).toBe('haha');
|
||||
settingEntry.setValue({
|
||||
newPropC: 'haha',
|
||||
});
|
||||
expect(settingEntry.getPropValue('newPropC')).toBe('haha');
|
||||
|
||||
expect(settingEntry.getPage()).toBe(currentDocument);
|
||||
expect(settingEntry.getNode()).toBe(divNode);
|
||||
expect(settingEntry.node).toBe(divNode);
|
||||
expect(settingEntry.getId()).toBe('div');
|
||||
expect(settingEntry.first).toBe(divNode);
|
||||
expect(settingEntry.designer).toBe(designer);
|
||||
expect(settingEntry.isSingle).toBeTruthy();
|
||||
expect(settingEntry.isMultiple).toBeFalsy();
|
||||
expect(settingEntry.isSameComponent).toBeTruthy();
|
||||
|
||||
expect(typeof settingEntry.getValue).toBe('function');
|
||||
settingEntry.getValue();
|
||||
});
|
||||
|
||||
it('onMetadataChange', () => {
|
||||
designer.createComponentMeta(divMeta);
|
||||
designer.project.open(settingSchema);
|
||||
const { currentDocument } = designer.project;
|
||||
const divNode = currentDocument?.getNode('div') as Node;
|
||||
|
||||
const { settingEntry } = divNode!;
|
||||
const mockFn = jest.fn();
|
||||
settingEntry.componentMeta.onMetadataChange(mockFn);
|
||||
settingEntry.componentMeta.refreshMetadata();
|
||||
expect(mockFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.skip('setupItems - customView', () => {
|
||||
designer.createComponentMeta(divMeta);
|
||||
designer.project.open(settingSchema);
|
||||
const { currentDocument } = designer.project;
|
||||
const divNode = currentDocument?.getNode('div') as Node;
|
||||
|
||||
const { settingEntry } = divNode;
|
||||
// 模拟将第一个配置变成 react funcional component
|
||||
settingEntry.componentMeta.getMetadata().combined[0].items[0] = props => props.xx;
|
||||
settingEntry.setupItems();
|
||||
});
|
||||
|
||||
it('清理方法测试', () => {
|
||||
designer.createComponentMeta(divMeta);
|
||||
designer.project.open(settingSchema);
|
||||
const { currentDocument } = designer.project;
|
||||
const divNode = currentDocument?.getNode('div');
|
||||
|
||||
const { settingEntry } = divNode!;
|
||||
expect(settingEntry.items).toHaveLength(3);
|
||||
settingEntry.purge();
|
||||
expect(settingEntry.items).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('vision 兼容测试', () => {
|
||||
designer.createComponentMeta(divMeta);
|
||||
designer.project.open(settingSchema);
|
||||
const { currentDocument } = designer.project;
|
||||
const divNode = currentDocument?.getNode('div');
|
||||
|
||||
// console.log(divNode?.getPropValue('behavior'));
|
||||
const { settingEntry } = divNode!;
|
||||
|
||||
expect(typeof settingEntry.getChildren).toBe('function');
|
||||
expect(typeof settingEntry.getDOMNode).toBe('function');
|
||||
expect(typeof settingEntry.getStatus).toBe('function');
|
||||
expect(typeof settingEntry.setStatus).toBe('function');
|
||||
settingEntry.getStatus();
|
||||
settingEntry.setStatus();
|
||||
settingEntry.getChildren();
|
||||
settingEntry.getDOMNode();
|
||||
});
|
||||
|
||||
it('没有 node', () => {
|
||||
const create1 = designer.createSettingEntry.bind(designer);
|
||||
const create2 = designer.createSettingEntry.bind(designer, []);
|
||||
expect(create1).toThrowError('nodes should not be empty');
|
||||
expect(create2).toThrowError('nodes should not be empty');
|
||||
});
|
||||
});
|
||||
|
||||
describe('designer.createSettingEntry 生成 settingEntry(多 node 场景)', () => {
|
||||
it('相同类型的 node', () => {
|
||||
designer.project.open(settingSchema);
|
||||
const { currentDocument } = designer.project;
|
||||
const divNode = currentDocument?.getNode('div');
|
||||
const divNode2 = currentDocument?.getNode('div2');
|
||||
const settingEntry = designer.createSettingEntry([divNode, divNode2]);
|
||||
|
||||
expect(settingEntry.isMultiple).toBeTruthy();
|
||||
expect(settingEntry.isSameComponent).toBeTruthy();
|
||||
expect(settingEntry.isSingle).toBeFalsy();
|
||||
|
||||
expect(settingEntry.getPropValue('behavior')).toBe('NORMAL');
|
||||
expect(settingEntry.getProp('behavior').getValue()).toBe('NORMAL');
|
||||
settingEntry.setPropValue('behavior', 'LARGE');
|
||||
expect(settingEntry.getPropValue('behavior')).toBe('LARGE');
|
||||
expect(settingEntry.get('behavior').getValue()).toBe('LARGE');
|
||||
// 多个 node 都被成功设值
|
||||
expect(divNode?.getPropValue('behavior')).toBe('LARGE');
|
||||
expect(divNode2?.getPropValue('behavior')).toBe('LARGE');
|
||||
|
||||
settingEntry.getProp('behavior').setValue('SMALL');
|
||||
expect(settingEntry.getPropValue('behavior')).toBe('SMALL');
|
||||
// 多个 node 都被成功设值
|
||||
expect(divNode?.getPropValue('behavior')).toBe('SMALL');
|
||||
expect(divNode2?.getPropValue('behavior')).toBe('SMALL');
|
||||
|
||||
settingEntry.clearPropValue('behavior');
|
||||
expect(settingEntry.getPropValue('behavior')).toBeUndefined();
|
||||
// 多个 node 都被成功设值
|
||||
expect(divNode?.getPropValue('behavior')).toBeUndefined();
|
||||
expect(divNode2?.getPropValue('behavior')).toBeUndefined();
|
||||
|
||||
expect(settingEntry.getPropValue('fieldId')).toBe('div_k1ow3h1o');
|
||||
settingEntry.setPropValue('fieldId', 'div_k1ow3h1o_new');
|
||||
expect(settingEntry.getPropValue('fieldId')).toBe('div_k1ow3h1o_new');
|
||||
|
||||
expect(settingEntry.getExtraPropValue('extraPropA')).toBe('haha');
|
||||
settingEntry.setExtraPropValue('extraPropA', 'haha2');
|
||||
expect(settingEntry.getExtraPropValue('extraPropA')).toBe('haha2');
|
||||
});
|
||||
|
||||
it('不同类型的 node', () => {
|
||||
designer.project.open(settingSchema);
|
||||
const { currentDocument } = designer.project;
|
||||
const divNode = currentDocument?.getNode('div');
|
||||
const testNode = currentDocument?.getNode('test');
|
||||
const settingEntry = designer.createSettingEntry([divNode, testNode]);
|
||||
|
||||
expect(settingEntry.isMultiple).toBeTruthy();
|
||||
expect(settingEntry.isSameComponent).toBeFalsy();
|
||||
expect(settingEntry.isSingle).toBeFalsy();
|
||||
|
||||
// 不同类型的 node 场景下,理论上从页面上已没有修改属性的方法调用,所以此处不再断言各设值方法
|
||||
// 思考:假如以后面向其他场景,比如用户用 API 强行调用,是否需要做健壮性保护?
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,313 +0,0 @@
|
||||
import '../../fixtures/window';
|
||||
import { DocumentModel, isDocumentModel, isPageSchema } from '../../../src/document/document-model';
|
||||
import { Editor } from '@alilc/lowcode-editor-core';
|
||||
import { Project } from '../../../src/project/project';
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import formSchema from '../../fixtures/schema/form';
|
||||
import divMeta from '../../fixtures/component-metadata/div';
|
||||
import formMeta from '../../fixtures/component-metadata/form';
|
||||
import otherMeta from '../../fixtures/component-metadata/other';
|
||||
import pageMeta from '../../fixtures/component-metadata/page';
|
||||
import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
describe('document-model 测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
|
||||
beforeEach(() => {
|
||||
editor = new Editor();
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
project = designer.project;
|
||||
});
|
||||
|
||||
it('empty schema', () => {
|
||||
const doc = new DocumentModel(project);
|
||||
expect(doc.rootNode?.id).toBe('root');
|
||||
expect(doc.currentRoot).toBe(doc.rootNode);
|
||||
expect(doc.root).toBe(doc.rootNode);
|
||||
expect(doc.modalNode).toBeUndefined();
|
||||
expect(doc.isBlank()).toBeTruthy();
|
||||
expect(doc.schema).toEqual({
|
||||
componentName: 'Page',
|
||||
condition: true,
|
||||
conditionGroup: '',
|
||||
hidden: false,
|
||||
isLocked: false,
|
||||
loop: undefined,
|
||||
title: '',
|
||||
id: 'root',
|
||||
fileName: '',
|
||||
props: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('各种方法测试', () => {
|
||||
const doc = new DocumentModel(project, formSchema);
|
||||
const mockNode = { id: 1 };
|
||||
doc.addWillPurge(mockNode);
|
||||
expect(doc.willPurgeSpace).toHaveLength(1);
|
||||
doc.removeWillPurge(mockNode);
|
||||
expect(doc.willPurgeSpace).toHaveLength(0);
|
||||
|
||||
expect(doc.toData()).toMatchSnapshot();
|
||||
|
||||
// 测试插入已存在的 id,id 将会被重置
|
||||
const formParentNode = doc.getNode('form').parent;
|
||||
doc.insertNode(formParentNode, { id: 'form', componentName: 'Form' });
|
||||
expect(formParentNode.children.get(formParentNode.children.size - 1).id).not.toBe('form');
|
||||
|
||||
doc.internalRemoveAndPurgeNode({ id: 'mockId' });
|
||||
|
||||
// internalSetDropLocation
|
||||
doc.dropLocation = { a: 1 };
|
||||
expect(doc.dropLocation).toEqual({ a: 1 });
|
||||
|
||||
// wrapWith
|
||||
// none-selected
|
||||
doc.wrapWith({ componentName: 'Wrap' });
|
||||
doc.selection.select('form');
|
||||
doc.wrapWith({ componentName: 'Wrap' });
|
||||
expect(doc.getNode('form').parent.componentName).toBe('Wrap');
|
||||
expect(doc.wrapWith({ componentName: 'Leaf' })).toBeNull();
|
||||
|
||||
// fileName
|
||||
expect(doc.fileName).toBeTruthy();
|
||||
doc.fileName = 'fileName';
|
||||
expect(doc.fileName).toBe('fileName');
|
||||
|
||||
expect(doc.getNodeSchema(doc.getNode('form'))).toMatchSnapshot();
|
||||
|
||||
// TODO:
|
||||
// expect(doc.simulatorProps).toMatchSnapshot();
|
||||
|
||||
const mockSimulator = {
|
||||
isSimulator: true,
|
||||
getComponent() {
|
||||
return 'haha';
|
||||
},
|
||||
setSuspense() {},
|
||||
};
|
||||
doc.project.mountSimulator(mockSimulator);
|
||||
expect(doc.simulator).toEqual(mockSimulator);
|
||||
expect(doc.getComponent('Div')).toBe('haha');
|
||||
|
||||
expect(doc.opened).toBeFalsy();
|
||||
expect(doc.isModified).toBeTruthy();
|
||||
expect(doc.suspensed).toBeTruthy();
|
||||
|
||||
doc.open();
|
||||
expect(doc.opened).toBeTruthy();
|
||||
expect(doc.actived).toBeTruthy();
|
||||
expect(doc.isModified).toBeTruthy();
|
||||
expect(doc.suspensed).toBeFalsy();
|
||||
|
||||
doc.suspense();
|
||||
doc.activate();
|
||||
doc.close();
|
||||
doc.remove();
|
||||
|
||||
const offReady = doc.onReady(() => {});
|
||||
offReady();
|
||||
|
||||
expect(doc.history).toBe(doc.getHistory());
|
||||
});
|
||||
|
||||
it('focusNode - using drillDown', () => {
|
||||
const doc = new DocumentModel(project, formSchema);
|
||||
expect(doc.focusNode.id).toBe('page');
|
||||
|
||||
doc.drillDown(doc.getNode('node_k1ow3cbb'));
|
||||
expect(doc.focusNode.id).toBe('node_k1ow3cbb');
|
||||
});
|
||||
|
||||
it('focusNode - using drillDown & import', () => {
|
||||
const doc = new DocumentModel(project, formSchema);
|
||||
expect(doc.focusNode.id).toBe('page');
|
||||
|
||||
doc.drillDown(doc.getNode('node_k1ow3cbb'));
|
||||
doc.import(formSchema);
|
||||
expect(doc.focusNode.id).toBe('node_k1ow3cbb');
|
||||
});
|
||||
|
||||
it('focusNode - using focusNodeSelector', () => {
|
||||
const doc = new DocumentModel(project, formSchema);
|
||||
editor.set('focusNodeSelector', (rootNode) => {
|
||||
return rootNode.children.get(1);
|
||||
});
|
||||
expect(doc.focusNode.id).toBe('node_k1ow3cbb');
|
||||
});
|
||||
|
||||
it('getNodeCount', () => {
|
||||
const doc = new DocumentModel(project);
|
||||
// using default schema, only one node
|
||||
expect(doc.getNodeCount()).toBe(1);
|
||||
});
|
||||
|
||||
it('getNodeSchema', () => {
|
||||
const doc = new DocumentModel(project, formSchema);
|
||||
expect(doc.getNodeSchema('page').id).toBe('page');
|
||||
});
|
||||
|
||||
it('export - with __isTopFixed__', () => {
|
||||
formSchema.children[1].props.__isTopFixed__ = true;
|
||||
const doc = new DocumentModel(project, formSchema);
|
||||
|
||||
const schema = doc.export();
|
||||
expect(schema.children).toHaveLength(3);
|
||||
expect(schema.children[0].componentName).toBe('RootContent');
|
||||
expect(schema.children[1].componentName).toBe('RootHeader');
|
||||
expect(schema.children[2].componentName).toBe('RootFooter');
|
||||
});
|
||||
|
||||
describe('createNode', () => {
|
||||
it('same id && componentName', () => {
|
||||
const doc = new DocumentModel(project, formSchema);
|
||||
const node = doc.createNode({
|
||||
componentName: 'RootFooter',
|
||||
id: 'node_k1ow3cbc',
|
||||
props: {},
|
||||
condition: true,
|
||||
});
|
||||
expect(node.parent).toBeNull();
|
||||
});
|
||||
|
||||
it('same id && different componentName', () => {
|
||||
const doc = new DocumentModel(project, formSchema);
|
||||
const originalNode = doc.getNode('node_k1ow3cbc');
|
||||
const node = doc.createNode({
|
||||
componentName: 'RootFooter2',
|
||||
id: 'node_k1ow3cbc',
|
||||
props: {},
|
||||
condition: true,
|
||||
});
|
||||
// expect(originalNode.parent).toBeNull();
|
||||
expect(node.id).not.toBe('node_k1ow3cbc');
|
||||
});
|
||||
});
|
||||
|
||||
it('setSuspense', () => {
|
||||
const doc = new DocumentModel(project, formSchema);
|
||||
expect(doc.opened).toBeFalsy();
|
||||
doc.setSuspense(false);
|
||||
});
|
||||
|
||||
it('registerAddon / getAddonData / exportAddonData', () => {
|
||||
const doc = new DocumentModel(project);
|
||||
expect(doc.getAddonData('a')).toBeUndefined();
|
||||
|
||||
doc.registerAddon('a', () => 'addon a');
|
||||
doc.registerAddon('a', () => 'modified addon a');
|
||||
doc.registerAddon('b', () => 'addon b');
|
||||
doc.registerAddon('c', () => null);
|
||||
|
||||
['id', 'layout', 'params'].forEach((name) => {
|
||||
expect(() => doc.registerAddon(name, () => {})).toThrow();
|
||||
});
|
||||
|
||||
expect(doc.getAddonData('a')).toBe('modified addon a');
|
||||
expect(doc.getAddonData('b')).toBe('addon b');
|
||||
|
||||
expect(doc.exportAddonData()).toEqual({
|
||||
a: 'modified addon a',
|
||||
b: 'addon b',
|
||||
});
|
||||
});
|
||||
|
||||
it('checkNesting / checkDropTarget / checkNestingUp / checkNestingDown', () => {
|
||||
designer.createComponentMeta(pageMeta);
|
||||
designer.createComponentMeta(formMeta);
|
||||
designer.createComponentMeta(otherMeta);
|
||||
const doc = new DocumentModel(project, formSchema);
|
||||
|
||||
expect(
|
||||
doc.checkDropTarget(doc.getNode('page'), { type: 'node', nodes: [doc.getNode('form')] }),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
doc.checkDropTarget(doc.getNode('page'), {
|
||||
type: 'nodedata',
|
||||
data: { componentName: 'Form' },
|
||||
}),
|
||||
).toBeTruthy();
|
||||
|
||||
expect(
|
||||
doc.checkNesting(doc.getNode('page'), { type: 'node', nodes: [doc.getNode('form')] }),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
doc.checkNesting(doc.getNode('page'), {
|
||||
type: 'nodedata',
|
||||
data: { componentName: 'Form' },
|
||||
}),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
doc.checkNesting(doc.getNode('page'), doc.getNode('form'))
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
doc.checkNesting(doc.getNode('page'), null)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
doc.checkNesting(doc.getNode('page'), {
|
||||
type: 'nodedata',
|
||||
data: { componentName: 'Other' },
|
||||
})
|
||||
).toBeFalsy();
|
||||
|
||||
expect(
|
||||
doc.checkNestingUp(doc.getNode('page'), { componentName: 'Other' })
|
||||
).toBeFalsy();
|
||||
|
||||
expect(
|
||||
doc.checkNestingDown(doc.getNode('page'), { componentName: 'Other' })
|
||||
).toBeTruthy();
|
||||
|
||||
expect(doc.checkNestingUp(doc.getNode('page'), null)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('getComponentsMap', () => {
|
||||
designer.createComponentMeta(divMeta);
|
||||
designer.createComponentMeta(otherMeta);
|
||||
const doc = new DocumentModel(project, formSchema);
|
||||
const comps = doc.getComponentsMap(['Other']);
|
||||
expect(comps.find(comp => comp.componentName === 'Div')).toEqual(
|
||||
{ componentName: 'Div', package: '@ali/vc-div' }
|
||||
);
|
||||
expect(comps.find(comp => comp.componentName === 'Other')).toEqual(
|
||||
{ componentName: 'Other', package: '@ali/vc-other' }
|
||||
);
|
||||
expect(comps.find(comp => comp.componentName === 'Page')).toEqual(
|
||||
{ componentName: 'Page', devMode: 'lowCode' }
|
||||
);
|
||||
|
||||
const comps2 = doc.getComponentsMap(['Div']);
|
||||
});
|
||||
|
||||
it('acceptRootNodeVisitor / getRootNodeVisitor', () => {
|
||||
designer.createComponentMeta(divMeta);
|
||||
designer.createComponentMeta(otherMeta);
|
||||
const doc = new DocumentModel(project, formSchema);
|
||||
const ret = doc.acceptRootNodeVisitor('getPageId', (root) => {
|
||||
return 'page';
|
||||
});
|
||||
expect(ret).toBe('page');
|
||||
expect(doc.getRootNodeVisitor('getPageId')).toBe('page');
|
||||
|
||||
// expect(doc.getComponentsMap(['Other'])).toEqual([
|
||||
// { componentName: 'Div', package: '@ali/vc-div' },
|
||||
// { componentName: 'Other', package: '@ali/vc-other' },
|
||||
// ]);
|
||||
});
|
||||
|
||||
it('deprecated methods', () => {
|
||||
const doc = new DocumentModel(project, formSchema);
|
||||
doc.refresh();
|
||||
doc.onRefresh();
|
||||
});
|
||||
});
|
||||
|
||||
it('isDocumentModel', () => {
|
||||
expect(isDocumentModel({ rootNode: {} })).toBeTruthy();
|
||||
});
|
||||
|
||||
it('isPageSchema', () => {
|
||||
expect(isPageSchema({ componentName: 'Page' })).toBeTruthy();
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`History data function & records 1`] = `"{\\"data\\":1,\\"children\\":[{\\"data\\":2,\\"children\\":[]}]}"`;
|
||||
|
||||
exports[`History data function & records 2`] = `"{\\"data\\":3,\\"children\\":[{\\"data\\":2,\\"children\\":[]}]}"`;
|
||||
|
||||
exports[`History data function & records 3`] = `"{\\"data\\":5,\\"children\\":[{\\"data\\":2,\\"children\\":[]}]}"`;
|
||||
|
||||
exports[`History data function & records 4`] = `"{\\"data\\":7,\\"children\\":[{\\"data\\":2,\\"children\\":[]}]}"`;
|
||||
@ -1,343 +0,0 @@
|
||||
import '../../fixtures/window';
|
||||
import { mobx, makeAutoObservable, globalContext, Editor } from '@alilc/lowcode-editor-core';
|
||||
import { History } from '../../../src/document/history';
|
||||
import { delay } from '../../utils/misc';
|
||||
import { Workspace } from '@alilc/lowcode-workspace';
|
||||
|
||||
class Node {
|
||||
data: number;
|
||||
children: Node[] = [];
|
||||
|
||||
constructor(data: number) {
|
||||
makeAutoObservable(this);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
addNode(node: Node) {
|
||||
this.children.push(node);
|
||||
}
|
||||
|
||||
toObject() {
|
||||
return {
|
||||
data: this.data,
|
||||
children: this.children.map((c) => c.toObject()),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let tree: Node = null;
|
||||
beforeEach(() => {
|
||||
tree = new Node(1);
|
||||
tree.addNode(new Node(2));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
tree = null;
|
||||
});
|
||||
|
||||
describe('History', () => {
|
||||
beforeAll(() => {
|
||||
const editor = new Editor();
|
||||
globalContext.register(editor, Editor);
|
||||
globalContext.register(editor, 'editor');
|
||||
globalContext.register(new Workspace(), 'workspace');
|
||||
});
|
||||
|
||||
it('data function & records', async () => {
|
||||
const mockRedoFn = jest.fn();
|
||||
const mockDataFn = jest.fn();
|
||||
const history = new History<Node>(() => {
|
||||
const data = tree.toObject();
|
||||
mockDataFn(data);
|
||||
return data;
|
||||
}, mockRedoFn);
|
||||
|
||||
expect(mockDataFn).toHaveBeenCalledTimes(1);
|
||||
expect(mockDataFn).toHaveBeenCalledWith({ data: 1, children: [{ data: 2, children: [] }] });
|
||||
expect(history.hotData).toMatchSnapshot();
|
||||
// @ts-ignore
|
||||
expect(history.session.cursor).toBe(0);
|
||||
// @ts-ignore
|
||||
expect(history.records).toHaveLength(1);
|
||||
|
||||
tree.data = 3;
|
||||
expect(mockDataFn).toHaveBeenCalledTimes(2);
|
||||
expect(mockDataFn).toHaveBeenCalledWith({ data: 3, children: [{ data: 2, children: [] }] });
|
||||
expect(history.hotData).toMatchSnapshot();
|
||||
// @ts-ignore
|
||||
expect(history.session.cursor).toBe(0);
|
||||
// @ts-ignore
|
||||
expect(history.records).toHaveLength(1);
|
||||
|
||||
// modify data after timeGap
|
||||
await delay(1200);
|
||||
tree.data = 5;
|
||||
expect(mockDataFn).toHaveBeenCalledTimes(3);
|
||||
expect(mockDataFn).toHaveBeenCalledWith({ data: 5, children: [{ data: 2, children: [] }] });
|
||||
expect(history.hotData).toMatchSnapshot();
|
||||
// @ts-ignore
|
||||
expect(history.session.cursor).toBe(1);
|
||||
// @ts-ignore
|
||||
expect(history.records).toHaveLength(2);
|
||||
|
||||
history.setSerialization({
|
||||
serialize(data: Node): string {
|
||||
return JSON.stringify(data);
|
||||
},
|
||||
unserialize(data: string) {
|
||||
return JSON.parse(data);
|
||||
},
|
||||
});
|
||||
|
||||
// modify data after timeGap
|
||||
await delay(1200);
|
||||
tree.data = 7;
|
||||
expect(mockDataFn).toHaveBeenCalledTimes(4);
|
||||
expect(mockDataFn).toHaveBeenCalledWith({ data: 7, children: [{ data: 2, children: [] }] });
|
||||
expect(history.hotData).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('isSavePoint & savePoint', async () => {
|
||||
const history = new History<Node>(
|
||||
() => {
|
||||
const data = tree.toObject();
|
||||
return data;
|
||||
},
|
||||
() => {},
|
||||
);
|
||||
|
||||
expect(history.isSavePoint()).toBeFalsy();
|
||||
expect(history.isModified()).toBeFalsy();
|
||||
|
||||
await delay(1200);
|
||||
tree.data = 3;
|
||||
expect(history.isSavePoint()).toBeTruthy();
|
||||
|
||||
history.savePoint();
|
||||
expect(history.isSavePoint()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('go & forward & back & onCursor', async () => {
|
||||
const mockRedoFn = jest.fn();
|
||||
const mockCursorFn = jest.fn();
|
||||
const mockStateFn = jest.fn();
|
||||
const history = new History<Node>(
|
||||
() => {
|
||||
const data = tree.toObject();
|
||||
return data;
|
||||
},
|
||||
(data) => {
|
||||
mockRedoFn(data);
|
||||
},
|
||||
);
|
||||
|
||||
// undoable ❌ & redoable ❌ & modified ❌
|
||||
expect(history.getState()).toBe(0);
|
||||
|
||||
await delay(1200);
|
||||
tree.data = 3;
|
||||
|
||||
await delay(1200);
|
||||
tree.data = 5;
|
||||
|
||||
await delay(1200);
|
||||
tree.data = 7;
|
||||
|
||||
const dataCursor0 = { data: 1, children: [{ data: 2, children: [] }] };
|
||||
const dataCursor1 = { data: 3, children: [{ data: 2, children: [] }] };
|
||||
const dataCursor2 = { data: 5, children: [{ data: 2, children: [] }] };
|
||||
const dataCursor3 = { data: 7, children: [{ data: 2, children: [] }] };
|
||||
|
||||
// redoable ❌
|
||||
expect(history.getState()).toBe(7 - 2);
|
||||
|
||||
const off1 = history.onCursor(mockCursorFn);
|
||||
const off2 = history.onStateChange(mockStateFn);
|
||||
|
||||
// @ts-ignore
|
||||
expect(history.records).toHaveLength(4);
|
||||
// @ts-ignore
|
||||
expect(history.session.cursor).toBe(3);
|
||||
|
||||
// step 1
|
||||
history.back();
|
||||
expect(mockCursorFn).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
JSON.stringify(dataCursor2),
|
||||
);
|
||||
expect(mockStateFn).toHaveBeenNthCalledWith(1, 7);
|
||||
expect(mockRedoFn).toHaveBeenNthCalledWith(1, dataCursor2);
|
||||
|
||||
// step 2
|
||||
history.back();
|
||||
expect(mockCursorFn).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
JSON.stringify(dataCursor1),
|
||||
);
|
||||
expect(mockStateFn).toHaveBeenNthCalledWith(2, 7);
|
||||
expect(mockRedoFn).toHaveBeenNthCalledWith(2, dataCursor1);
|
||||
|
||||
// step 3
|
||||
history.back();
|
||||
expect(mockCursorFn).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
JSON.stringify(dataCursor0),
|
||||
);
|
||||
expect(mockStateFn).toHaveBeenNthCalledWith(3, 7 - 4 - 1);
|
||||
expect(mockRedoFn).toHaveBeenNthCalledWith(3, dataCursor0);
|
||||
|
||||
// step 4
|
||||
history.forward();
|
||||
expect(mockCursorFn).toHaveBeenNthCalledWith(
|
||||
4,
|
||||
JSON.stringify(dataCursor1),
|
||||
);
|
||||
expect(mockStateFn).toHaveBeenNthCalledWith(4, 7);
|
||||
expect(mockRedoFn).toHaveBeenNthCalledWith(4, dataCursor1);
|
||||
|
||||
// step 5
|
||||
history.forward();
|
||||
expect(mockCursorFn).toHaveBeenNthCalledWith(
|
||||
5,
|
||||
JSON.stringify(dataCursor2),
|
||||
);
|
||||
expect(mockStateFn).toHaveBeenNthCalledWith(5, 7);
|
||||
expect(mockRedoFn).toHaveBeenNthCalledWith(5, dataCursor2);
|
||||
|
||||
// step 6
|
||||
history.go(3);
|
||||
expect(mockCursorFn).toHaveBeenNthCalledWith(
|
||||
6,
|
||||
JSON.stringify(dataCursor3),
|
||||
);
|
||||
expect(mockStateFn).toHaveBeenNthCalledWith(6, 7 - 2);
|
||||
expect(mockRedoFn).toHaveBeenNthCalledWith(6, dataCursor3);
|
||||
|
||||
// step 7
|
||||
history.go(0);
|
||||
expect(mockCursorFn).toHaveBeenNthCalledWith(
|
||||
7,
|
||||
JSON.stringify(dataCursor0),
|
||||
);
|
||||
expect(mockStateFn).toHaveBeenNthCalledWith(7, 7 - 4 - 1);
|
||||
expect(mockRedoFn).toHaveBeenNthCalledWith(7, dataCursor0);
|
||||
|
||||
off1();
|
||||
off2();
|
||||
mockStateFn.mockClear();
|
||||
mockCursorFn.mockClear();
|
||||
history.go(1);
|
||||
expect(mockStateFn).not.toHaveBeenCalled();
|
||||
expect(mockCursorFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('go() - edge case of cursor', async () => {
|
||||
const mockRedoFn = jest.fn();
|
||||
const mockCursorFn = jest.fn();
|
||||
const mockStateFn = jest.fn();
|
||||
const history = new History<Node>(
|
||||
() => {
|
||||
const data = tree.toObject();
|
||||
return data;
|
||||
},
|
||||
(data) => {
|
||||
mockRedoFn(data);
|
||||
},
|
||||
);
|
||||
|
||||
await delay(1200);
|
||||
tree.data = 3;
|
||||
|
||||
await delay(1200);
|
||||
tree.data = 5;
|
||||
|
||||
history.go(-1);
|
||||
// @ts-ignore
|
||||
expect(history.session.cursor).toBe(0);
|
||||
|
||||
history.go(3);
|
||||
// @ts-ignore
|
||||
expect(history.session.cursor).toBe(2);
|
||||
});
|
||||
|
||||
it('destroy()', async () => {
|
||||
const history = new History<Node>(
|
||||
() => {
|
||||
const data = tree.toObject();
|
||||
return data;
|
||||
},
|
||||
(data) => {
|
||||
mockRedoFn(data);
|
||||
},
|
||||
);
|
||||
|
||||
history.destroy();
|
||||
// @ts-ignore
|
||||
expect(history.records).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('sleep & wakeup', async () => {
|
||||
const mockRedoFn = jest.fn();
|
||||
const history = new History<Node>(
|
||||
() => {
|
||||
const data = tree.toObject();
|
||||
return data;
|
||||
},
|
||||
(data) => {
|
||||
mockRedoFn(data);
|
||||
},
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
history.sleep();
|
||||
|
||||
await delay(1200);
|
||||
tree.data = 3;
|
||||
// no record has been pushed into records because of history is asleep.
|
||||
// @ts-ignore
|
||||
expect(history.records).toHaveLength(1);
|
||||
|
||||
// @ts-ignore
|
||||
history.wakeup();
|
||||
tree.data = 4;
|
||||
// @ts-ignore
|
||||
expect(history.records).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('History - errors', () => {
|
||||
beforeAll(() => {
|
||||
globalContext.replace(Editor, null);
|
||||
});
|
||||
|
||||
it('no editor', () => {
|
||||
const history = new History<Node>(
|
||||
() => {
|
||||
const data = tree.toObject();
|
||||
return data;
|
||||
},
|
||||
(data) => {
|
||||
},
|
||||
);
|
||||
|
||||
history.back();
|
||||
history.forward();
|
||||
});
|
||||
|
||||
it('no session', () => {
|
||||
const history = new History<Node>(
|
||||
() => {
|
||||
const data = tree.toObject();
|
||||
return data;
|
||||
},
|
||||
(data) => {
|
||||
},
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
history.session = undefined;
|
||||
history.back();
|
||||
history.forward();
|
||||
history.savePoint();
|
||||
});
|
||||
});
|
||||
@ -1,57 +0,0 @@
|
||||
import '../../fixtures/window';
|
||||
import { Session } from '../../../src/document/history';
|
||||
import { delay } from '../../utils/misc';
|
||||
|
||||
describe('Session', () => {
|
||||
it('constructor', () => {
|
||||
const session = new Session(1, { a: 1 });
|
||||
expect(session.cursor).toBe(1);
|
||||
expect(session.data).toEqual({ a: 1 });
|
||||
// @ts-ignore
|
||||
expect(session.timeGap).toBe(1000);
|
||||
expect(session.isActive()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('log()', () => {
|
||||
const session = new Session(1, { a: 1 });
|
||||
|
||||
session.log({ a: 2 });
|
||||
session.log({ a: 3 });
|
||||
expect(session.data).toEqual({ a: 3 });
|
||||
});
|
||||
|
||||
it('end()', () => {
|
||||
const session = new Session(1, { a: 1 });
|
||||
|
||||
session.end();
|
||||
expect(session.isActive()).toBeFalsy();
|
||||
session.log({ a: 2 });
|
||||
// log is not possible if current session is inactive
|
||||
expect(session.data).toEqual({ a: 1 });
|
||||
});
|
||||
|
||||
|
||||
it('timeGap', async () => {
|
||||
const session = new Session(1, { a: 1 });
|
||||
|
||||
expect(session.isActive()).toBeTruthy();
|
||||
await delay(1200);
|
||||
expect(session.isActive()).toBeFalsy();
|
||||
session.log({ a: 2 });
|
||||
// log is not possible if current session is inactive
|
||||
expect(session.data).toEqual({ a: 1 });
|
||||
});
|
||||
|
||||
it('custom timeGap', async () => {
|
||||
const session = new Session(1, { a: 1 }, 2000);
|
||||
|
||||
expect(session.isActive()).toBeTruthy();
|
||||
await delay(1200);
|
||||
expect(session.isActive()).toBeTruthy();
|
||||
await delay(1000);
|
||||
expect(session.isActive()).toBeFalsy();
|
||||
session.log({ a: 2 });
|
||||
// log is not possible if current session is inactive
|
||||
expect(session.data).toEqual({ a: 1 });
|
||||
});
|
||||
});
|
||||
@ -1,112 +0,0 @@
|
||||
import '../../fixtures/window';
|
||||
import { Editor } from '@alilc/lowcode-editor-core';
|
||||
import { Project } from '../../../src/project/project';
|
||||
import { DocumentModel } from '../../../src/document/document-model';
|
||||
import { Node } from '../../../src/document/node/node';
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import formSchema from '../../fixtures/schema/form-with-modal';
|
||||
import dlgMetadata from '../../fixtures/component-metadata/dialog';
|
||||
import { getModalNodes } from '../../../src/document/node/modal-nodes-manager';
|
||||
import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
|
||||
beforeEach(() => {
|
||||
editor = new Editor();
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
designer.createComponentMeta(dlgMetadata);
|
||||
project = designer.project;
|
||||
doc = new DocumentModel(project, formSchema);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
designer.purge();
|
||||
editor = null;
|
||||
designer = null;
|
||||
project = null;
|
||||
});
|
||||
|
||||
describe('ModalNodesManager 方法测试', () => {
|
||||
it('getModalNodes / getVisibleModalNode', () => {
|
||||
const mgr = doc.modalNodesManager;
|
||||
const nodes = mgr.getModalNodes();
|
||||
expect(nodes).toHaveLength(1);
|
||||
expect(nodes[0].componentName).toBe('Dialog');
|
||||
expect(mgr.getVisibleModalNode()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('setVisible / setInvisible / onVisibleChange', () => {
|
||||
const mgr = doc.modalNodesManager;
|
||||
const nodes = mgr.getModalNodes();
|
||||
|
||||
const mockFn = jest.fn();
|
||||
const off = mgr.onVisibleChange(mockFn);
|
||||
|
||||
mgr.setVisible(nodes[0]);
|
||||
expect(mockFn).toHaveBeenCalledTimes(2);
|
||||
|
||||
mgr.setInvisible(nodes[0]);
|
||||
expect(mockFn).toHaveBeenCalledTimes(3);
|
||||
|
||||
off();
|
||||
});
|
||||
|
||||
it('addNode / removeNode', () => {
|
||||
const mgr = doc.modalNodesManager!;
|
||||
const nodes = mgr.getModalNodes();
|
||||
|
||||
const nodesMockFn = jest.fn();
|
||||
const visibleMockFn = jest.fn();
|
||||
const off = mgr.onModalNodesChange(nodesMockFn);
|
||||
const offVisible = mgr.onVisibleChange(visibleMockFn);
|
||||
|
||||
const newNode = new Node(doc, { componentName: 'Dialog' });
|
||||
mgr.addNode(newNode);
|
||||
expect(visibleMockFn).toHaveBeenCalledTimes(2);
|
||||
expect(nodesMockFn).toHaveBeenCalledTimes(1);
|
||||
|
||||
mgr.setVisible(newNode);
|
||||
mgr.removeNode(newNode);
|
||||
|
||||
expect(visibleMockFn).toHaveBeenCalledTimes(6);
|
||||
expect(nodesMockFn).toHaveBeenCalledTimes(2);
|
||||
|
||||
off();
|
||||
offVisible();
|
||||
visibleMockFn.mockClear();
|
||||
nodesMockFn.mockClear();
|
||||
|
||||
mgr.addNode(newNode);
|
||||
expect(visibleMockFn).not.toHaveBeenCalled();
|
||||
expect(nodesMockFn).not.toHaveBeenCalled();
|
||||
|
||||
const newNode2 = new Node(doc, { componentName: 'Dialog' });
|
||||
mgr.addNode(newNode2);
|
||||
mgr.setInvisible(newNode2);
|
||||
mgr.removeNode(newNode2);
|
||||
|
||||
const newNode3 = new Node(doc, { componentName: 'Dialog' });
|
||||
mgr.removeNode(newNode3);
|
||||
|
||||
const newNode4 = new Node(doc, { componentName: 'Non-Modal' });
|
||||
mgr.removeNode(newNode4);
|
||||
|
||||
const newNode5 = doc.createNode({ componentName: 'Non-Modal' });
|
||||
newNode5.remove(); // trigger node destroy
|
||||
});
|
||||
});
|
||||
|
||||
describe('其他方法', () => {
|
||||
it('getModalNodes - null', () => {
|
||||
expect(getModalNodes()).toEqual([]);
|
||||
});
|
||||
|
||||
it('getModalNodes - no children', () => {
|
||||
const node = doc.createNode({ componentName: 'Leaf', children: 'haha' });
|
||||
expect(getModalNodes(node)).toEqual([]);
|
||||
});
|
||||
});
|
||||
@ -1,250 +0,0 @@
|
||||
import '../../fixtures/window';
|
||||
import { Editor } from '@alilc/lowcode-editor-core';
|
||||
import { Project } from '../../../src/project/project';
|
||||
import { DocumentModel } from '../../../src/document/document-model';
|
||||
import {
|
||||
Node,
|
||||
} from '../../../src/document/node/node';
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import formSchema from '../../fixtures/schema/form';
|
||||
import divMetadata from '../../fixtures/component-metadata/div';
|
||||
import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
describe('NodeChildren 方法测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
|
||||
beforeEach(() => {
|
||||
editor = new Editor();
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
project = designer.project;
|
||||
doc = new DocumentModel(project, formSchema);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
designer.purge();
|
||||
editor = null;
|
||||
designer = null;
|
||||
project = null;
|
||||
});
|
||||
|
||||
it('isEmpty / notEmpty', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const secondBtn = doc.getNode('node_k1ow3cbp')!;
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
expect(children.isEmpty()).toBeFalsy();
|
||||
expect(children.notEmpty()).toBeTruthy();
|
||||
expect(firstBtn.children.notEmpty()).toBeFalsy();
|
||||
expect(firstBtn.children.isEmpty()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('export', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
expect(children.export().length).toBe(2);
|
||||
});
|
||||
|
||||
it('export - Leaf', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
firstBtn.parent!.insertAfter({ componentName: 'Leaf', children: 'haha' });
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
expect(children.export().length).toBe(3);
|
||||
expect(children.export()[2]).toBe('haha');
|
||||
});
|
||||
|
||||
it('import', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
children.import(children.export());
|
||||
|
||||
expect(children.export().length).toBe(2);
|
||||
});
|
||||
|
||||
it('delete', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const leafNode = doc.createNode({ componentName: 'Leaf', children: 'haha' });
|
||||
firstBtn.parent!.insertAfter(leafNode);
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
children.delete(leafNode);
|
||||
expect(children.export().length).toBe(2);
|
||||
});
|
||||
|
||||
it('delete - 插入已有的节点', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
firstBtn.parent!.insertBefore(firstBtn, firstBtn);
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
expect(children.export().length).toBe(2);
|
||||
});
|
||||
|
||||
it('purge / for of', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const { children } = firstBtn.parent!;
|
||||
children.purge();
|
||||
|
||||
for (const child of children) {
|
||||
expect(child.isPurged).toBeTruthy();
|
||||
}
|
||||
|
||||
// purge when children is purged
|
||||
children.purge();
|
||||
});
|
||||
|
||||
it('splice', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const { children } = firstBtn.parent!;
|
||||
children.splice(0, 1);
|
||||
|
||||
expect(children.size).toBe(1);
|
||||
expect(children.length).toBe(1);
|
||||
|
||||
children.splice(0, 0, doc.createNode({ componentName: 'Button' }));
|
||||
expect(children.size).toBe(2);
|
||||
expect(children.length).toBe(2);
|
||||
});
|
||||
|
||||
it('map', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
const newMap = children.map((item) => item);
|
||||
|
||||
newMap?.forEach((item) => {
|
||||
expect(item.componentName).toBe('Button');
|
||||
});
|
||||
});
|
||||
|
||||
it('forEach', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
children.forEach((item) => {
|
||||
expect(item.componentName).toBe('Button');
|
||||
});
|
||||
});
|
||||
|
||||
it('some', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
children.some((item) => {
|
||||
return expect(item.componentName).toBe('Button');
|
||||
});
|
||||
});
|
||||
|
||||
it('every', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
children.every((item) => {
|
||||
return expect(item.componentName).toBe('Button');
|
||||
});
|
||||
});
|
||||
|
||||
it('filter', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
children
|
||||
.filter((item) => item.componentName === 'Button')
|
||||
.forEach((item) => {
|
||||
expect(item.componentName).toBe('Button');
|
||||
});
|
||||
});
|
||||
|
||||
it('find', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
const found = children.find((item) => item.componentName === 'Button');
|
||||
|
||||
expect(found?.componentName).toBe('Button');
|
||||
});
|
||||
|
||||
it('concat', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
const ret = children.concat([doc.createNode({ componentName: 'Button' })]);
|
||||
|
||||
expect(ret.length).toBe(3);
|
||||
});
|
||||
|
||||
it('reduce', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
let ret = 0;
|
||||
ret = children.reduce((count, node) => {
|
||||
count = count + 1;
|
||||
return count;
|
||||
}, 0);
|
||||
|
||||
expect(ret).toBe(2);
|
||||
});
|
||||
|
||||
it('mergeChildren', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
const changeMockFn = jest.fn();
|
||||
const offChange = children.onChange(changeMockFn);
|
||||
const rmMockFn = jest.fn((item) => {
|
||||
if (item.index === 1) return true;
|
||||
return false;
|
||||
});
|
||||
const addMockFn = jest.fn((children) => {
|
||||
return [{ componentName: 'Button' }, { componentName: 'Button' }];
|
||||
});
|
||||
const sortMockFn = jest.fn((a, b) => {
|
||||
return a > b ? 1 : -1;
|
||||
});
|
||||
children.mergeChildren(rmMockFn, addMockFn, sortMockFn);
|
||||
|
||||
expect(children.size).toBe(3);
|
||||
expect(changeMockFn).toHaveBeenCalled();
|
||||
offChange();
|
||||
|
||||
// no remover && adder && sorter
|
||||
children.mergeChildren();
|
||||
});
|
||||
|
||||
it('insert / onInsert', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
const mockFn = jest.fn();
|
||||
const off = children.onInsert(mockFn);
|
||||
|
||||
children.insert(new Node(doc, { componentName: 'Button' }));
|
||||
expect(mockFn).toHaveBeenCalledTimes(1);
|
||||
off();
|
||||
children.insert(new Node(doc, { componentName: 'Button' }));
|
||||
expect(mockFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('reportModified', () => {
|
||||
const divMeta = designer.createComponentMeta(divMetadata);
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const { children } = firstBtn.parent!;
|
||||
|
||||
const modifiedMockFn = jest.fn();
|
||||
divMeta.getMetadata = () => {
|
||||
return { configure: { advanced: { callbacks: { onSubtreeModified: modifiedMockFn } } } };
|
||||
};
|
||||
|
||||
children.reportModified(null);
|
||||
children.reportModified(doc.rootNode);
|
||||
|
||||
children.reportModified(firstBtn, firstBtn.parent);
|
||||
expect(modifiedMockFn).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@ -1,588 +0,0 @@
|
||||
import { set, cloneDeep } from 'lodash-es';
|
||||
import '../../fixtures/window';
|
||||
import { Project, IProject } from '../../../src/project/project';
|
||||
import { Node, INode } from '../../../src/document/node/node';
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import formSchema from '../../fixtures/schema/form';
|
||||
import { getIdsFromSchema, getNodeFromSchemaById } from '../../utils';
|
||||
|
||||
const mockCreateSettingEntry = jest.fn();
|
||||
jest.mock('../../../src/designer/designer', () => {
|
||||
return {
|
||||
Designer: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
getComponentMeta() {
|
||||
return {
|
||||
getMetadata() {
|
||||
return { configure: { advanced: null } };
|
||||
},
|
||||
get advanced() {
|
||||
return {};
|
||||
},
|
||||
};
|
||||
},
|
||||
transformProps(props) {
|
||||
return props;
|
||||
},
|
||||
createSettingEntry: mockCreateSettingEntry,
|
||||
postEvent() {},
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let designer = null;
|
||||
beforeAll(() => {
|
||||
designer = new Designer({});
|
||||
});
|
||||
|
||||
describe('schema 生成节点模型测试', () => {
|
||||
describe('block ❌ | component ❌ | slot ❌', () => {
|
||||
let project: IProject;
|
||||
beforeEach(() => {
|
||||
project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
});
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
});
|
||||
it('基本的节点模型初始化,模型导出', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
expect(nodesMap?.size).toBe(expectedNodeCnt);
|
||||
ids.forEach((id) => {
|
||||
expect(nodesMap?.get(id)?.componentName).toBe(
|
||||
getNodeFromSchemaById(formSchema, id).componentName,
|
||||
);
|
||||
});
|
||||
|
||||
const pageNode = currentDocument?.getNode('page');
|
||||
expect(pageNode?.getComponentName()).toBe('Page');
|
||||
expect(pageNode?.getIcon()).toBeUndefined();
|
||||
|
||||
const exportSchema = currentDocument?.export(1);
|
||||
expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt);
|
||||
nodesMap.forEach((node) => {
|
||||
// 触发 getter
|
||||
node.settingEntry;
|
||||
});
|
||||
expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt);
|
||||
});
|
||||
|
||||
it('基本的节点模型初始化,节点深度', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const getNode = currentDocument?.getNode.bind(currentDocument);
|
||||
|
||||
const pageNode = getNode?.('page');
|
||||
const rootHeaderNode = getNode?.('node_k1ow3cba');
|
||||
const rootContentNode = getNode?.('node_k1ow3cbb');
|
||||
const rootFooterNode = getNode?.('node_k1ow3cbc');
|
||||
const formNode = getNode?.('form');
|
||||
const cardNode = getNode?.('node_k1ow3cbj');
|
||||
const cardContentNode = getNode?.('node_k1ow3cbk');
|
||||
const columnsLayoutNode = getNode?.('node_k1ow3cbw');
|
||||
const columnNode = getNode?.('node_k1ow3cbx');
|
||||
const textFieldNode = getNode?.('node_k1ow3cbz');
|
||||
|
||||
expect(pageNode?.zLevel).toBe(0);
|
||||
expect(rootHeaderNode?.zLevel).toBe(1);
|
||||
expect(rootContentNode?.zLevel).toBe(1);
|
||||
expect(rootFooterNode?.zLevel).toBe(1);
|
||||
expect(formNode?.zLevel).toBe(2);
|
||||
expect(cardNode?.zLevel).toBe(3);
|
||||
expect(cardContentNode?.zLevel).toBe(4);
|
||||
expect(columnsLayoutNode?.zLevel).toBe(5);
|
||||
expect(columnNode?.zLevel).toBe(6);
|
||||
expect(textFieldNode?.zLevel).toBe(7);
|
||||
|
||||
expect(textFieldNode?.getZLevelTop(7)).toEqual(textFieldNode);
|
||||
expect(textFieldNode?.getZLevelTop(6)).toEqual(columnNode);
|
||||
expect(textFieldNode?.getZLevelTop(5)).toEqual(columnsLayoutNode);
|
||||
expect(textFieldNode?.getZLevelTop(4)).toEqual(cardContentNode);
|
||||
expect(textFieldNode?.getZLevelTop(3)).toEqual(cardNode);
|
||||
expect(textFieldNode?.getZLevelTop(2)).toEqual(formNode);
|
||||
expect(textFieldNode?.getZLevelTop(1)).toEqual(rootContentNode);
|
||||
expect(textFieldNode?.getZLevelTop(0)).toEqual(pageNode);
|
||||
|
||||
// 异常情况
|
||||
expect(textFieldNode?.getZLevelTop(8)).toBeNull();
|
||||
expect(textFieldNode?.getZLevelTop(-1)).toBeNull();
|
||||
});
|
||||
|
||||
it('基本的节点模型初始化,节点父子、兄弟相关方法', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const getNode = currentDocument.getNode.bind(currentDocument);
|
||||
|
||||
const pageNode = getNode('page');
|
||||
const rootHeaderNode = getNode('node_k1ow3cba');
|
||||
const rootContentNode = getNode('node_k1ow3cbb');
|
||||
const rootFooterNode = getNode('node_k1ow3cbc');
|
||||
const formNode = getNode('form');
|
||||
const cardNode = getNode('node_k1ow3cbj');
|
||||
const cardContentNode = getNode('node_k1ow3cbk');
|
||||
const columnsLayoutNode = getNode('node_k1ow3cbw');
|
||||
const columnNode = getNode('node_k1ow3cbx');
|
||||
const textFieldNode = getNode('node_k1ow3cbz');
|
||||
|
||||
expect(pageNode?.index).toBe(-1);
|
||||
expect(pageNode?.children?.toString()).toBe('[object Array]');
|
||||
expect(pageNode?.children?.get(1)).toBe(rootContentNode);
|
||||
expect(pageNode?.getChildren()?.get(1)).toBe(rootContentNode);
|
||||
expect(pageNode?.getNode()).toBe(pageNode);
|
||||
|
||||
expect(rootFooterNode?.index).toBe(2);
|
||||
|
||||
expect(textFieldNode?.getParent()).toBe(columnNode);
|
||||
expect(columnNode?.getParent()).toBe(columnsLayoutNode);
|
||||
expect(columnsLayoutNode?.getParent()).toBe(cardContentNode);
|
||||
expect(cardContentNode?.getParent()).toBe(cardNode);
|
||||
expect(cardNode?.getParent()).toBe(formNode);
|
||||
expect(formNode?.getParent()).toBe(rootContentNode);
|
||||
expect(rootContentNode?.getParent()).toBe(pageNode);
|
||||
expect(rootContentNode?.prevSibling).toBe(rootHeaderNode);
|
||||
expect(rootContentNode?.nextSibling).toBe(rootFooterNode);
|
||||
|
||||
expect(pageNode?.isRoot()).toBe(true);
|
||||
expect(pageNode?.contains(textFieldNode)).toBe(true);
|
||||
expect(textFieldNode?.getRoot()).toBe(pageNode);
|
||||
expect(columnNode?.getRoot()).toBe(pageNode);
|
||||
expect(columnsLayoutNode?.getRoot()).toBe(pageNode);
|
||||
expect(cardContentNode?.getRoot()).toBe(pageNode);
|
||||
expect(cardNode?.getRoot()).toBe(pageNode);
|
||||
expect(formNode?.getRoot()).toBe(pageNode);
|
||||
expect(rootContentNode?.getRoot()).toBe(pageNode);
|
||||
});
|
||||
|
||||
it('基本的节点模型初始化,节点新建、删除等事件', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const getNode = currentDocument?.getNode.bind(currentDocument);
|
||||
const createNode = currentDocument?.createNode.bind(currentDocument);
|
||||
|
||||
const pageNode = getNode?.('page');
|
||||
const nodeCreateHandler = jest.fn();
|
||||
const offCreate = currentDocument?.onNodeCreate(nodeCreateHandler);
|
||||
|
||||
const node = createNode?.({
|
||||
componentName: 'TextInput',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
},
|
||||
});
|
||||
pageNode && node && currentDocument?.insertNode(pageNode, node);
|
||||
|
||||
expect(nodeCreateHandler).toHaveBeenCalledTimes(1);
|
||||
expect(nodeCreateHandler.mock.calls[0][0]).toBe(node);
|
||||
expect(nodeCreateHandler.mock.calls[0][0].componentName).toBe('TextInput');
|
||||
expect(nodeCreateHandler.mock.calls[0][0].getPropValue('propA')).toBe('haha');
|
||||
|
||||
const nodeDestroyHandler = jest.fn();
|
||||
const offDestroy = currentDocument?.onNodeDestroy(nodeDestroyHandler);
|
||||
node?.remove();
|
||||
expect(nodeDestroyHandler).toHaveBeenCalledTimes(1);
|
||||
expect(nodeDestroyHandler.mock.calls[0][0]).toBe(node);
|
||||
expect(nodeDestroyHandler.mock.calls[0][0].componentName).toBe('TextInput');
|
||||
expect(nodeDestroyHandler.mock.calls[0][0].getPropValue('propA')).toBe('haha');
|
||||
|
||||
offCreate();
|
||||
offDestroy();
|
||||
});
|
||||
|
||||
it.skip('基本的节点模型初始化,节点插入等方法', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const getNode = currentDocument.getNode.bind(currentDocument);
|
||||
|
||||
const formNode = getNode('form');
|
||||
const node1 = currentDocument.createNode({
|
||||
componentName: 'TextInput',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
},
|
||||
});
|
||||
const node2 = currentDocument.createNode({
|
||||
componentName: 'TextInput',
|
||||
props: {
|
||||
propA: 'heihei',
|
||||
},
|
||||
});
|
||||
const node3 = currentDocument.createNode({
|
||||
componentName: 'TextInput',
|
||||
props: {
|
||||
propA: 'heihei2',
|
||||
},
|
||||
});
|
||||
const node4 = currentDocument.createNode({
|
||||
componentName: 'TextInput',
|
||||
props: {
|
||||
propA: 'heihei3',
|
||||
},
|
||||
});
|
||||
|
||||
formNode?.insertBefore(node2);
|
||||
// formNode?.insertBefore(node1, node2);
|
||||
// formNode?.insertAfter(node3);
|
||||
// formNode?.insertAfter(node4, node3);
|
||||
|
||||
expect(formNode?.children?.get(0)).toBe(node1);
|
||||
expect(formNode?.children?.get(1)).toBe(node2);
|
||||
// expect(formNode?.children?.get(5)).toBe(node3);
|
||||
// expect(formNode?.children?.get(6)).toBe(node4);
|
||||
});
|
||||
|
||||
it('基本的节点模型初始化,节点其他方法', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const getNode = currentDocument.getNode.bind(currentDocument);
|
||||
|
||||
const pageNode = getNode('page');
|
||||
expect(pageNode?.isPage()).toBe(true);
|
||||
expect(pageNode?.isComponent()).toBe(false);
|
||||
expect(pageNode?.isSlot()).toBe(false);
|
||||
expect(pageNode?.title).toBe("hey, i' a page!");
|
||||
});
|
||||
|
||||
describe('节点新增(insertNode)', () => {
|
||||
let project: Project;
|
||||
beforeEach(() => {
|
||||
project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
});
|
||||
it('场景一:插入 NodeSchema,不指定 index', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form') as Node;
|
||||
const formNode2 = currentDocument?.getNode('form');
|
||||
expect(formNode).toEqual(formNode2);
|
||||
currentDocument?.insertNode(formNode, {
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
},
|
||||
});
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
expect(formNode.children?.length).toBe(4);
|
||||
const insertedNode = formNode.children.get(formNode.children.length - 1);
|
||||
expect(insertedNode.componentName).toBe('TextInput');
|
||||
expect(insertedNode.propsData).toEqual({
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
});
|
||||
// TODO: 把 checkId 的 commit pick 过来
|
||||
// expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput');
|
||||
});
|
||||
|
||||
it('场景一:插入 NodeSchema,指定 index: 0', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form');
|
||||
formNode &&
|
||||
currentDocument?.insertNode(
|
||||
formNode,
|
||||
{
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
},
|
||||
},
|
||||
0,
|
||||
);
|
||||
expect(nodesMap?.size).toBe(ids.length + 1);
|
||||
expect(formNode?.children?.length).toBe(4);
|
||||
const insertedNode = formNode?.children?.get(0);
|
||||
expect(insertedNode?.componentName).toBe('TextInput');
|
||||
expect(insertedNode?.propsData).toEqual({
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
});
|
||||
// TODO: 把 checkId 的 commit pick 过来
|
||||
// expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput');
|
||||
});
|
||||
|
||||
it('场景一:插入 NodeSchema,指定 index: 1', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form');
|
||||
formNode &&
|
||||
currentDocument?.insertNode(
|
||||
formNode,
|
||||
{
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
},
|
||||
},
|
||||
1,
|
||||
);
|
||||
expect(nodesMap?.size).toBe(ids.length + 1);
|
||||
expect(formNode?.children?.length).toBe(4);
|
||||
const insertedNode = formNode?.children?.get(1);
|
||||
expect(insertedNode?.componentName).toBe('TextInput');
|
||||
expect(insertedNode?.propsData).toEqual({
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
});
|
||||
// TODO: 把 checkId 的 commit pick 过来
|
||||
// expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput');
|
||||
});
|
||||
|
||||
it('场景一:插入 NodeSchema,有 children', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form') as INode;
|
||||
currentDocument?.insertNode(formNode, {
|
||||
componentName: 'ParentNode',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'SubNode',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'SubNode2',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(nodesMap?.size).toBe(ids.length + 3);
|
||||
expect(formNode.children?.length).toBe(4);
|
||||
expect(formNode.children?.get(3)?.componentName).toBe('ParentNode');
|
||||
expect(formNode.children?.get(3)?.children?.get(0)?.componentName).toBe('SubNode');
|
||||
expect(formNode.children?.get(3)?.children?.get(1)?.componentName).toBe('SubNode2');
|
||||
});
|
||||
|
||||
it.skip('场景一:插入 NodeSchema,id 与现有 schema 里的 id 重复', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form');
|
||||
formNode &&
|
||||
currentDocument?.insertNode(formNode, {
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
},
|
||||
});
|
||||
expect(nodesMap?.get('nodeschema-id1')?.componentName).toBe('TextInput');
|
||||
expect(nodesMap?.size).toBe(ids.length + 1);
|
||||
});
|
||||
|
||||
it.skip('场景一:插入 NodeSchema,id 与现有 schema 里的 id 重复,但关闭了 id 检测器', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form');
|
||||
formNode &&
|
||||
currentDocument?.insertNode(formNode, {
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
},
|
||||
});
|
||||
expect(nodesMap?.get('nodeschema-id1')?.componentName).toBe('TextInput');
|
||||
expect(nodesMap?.size).toBe(ids.length + 1);
|
||||
});
|
||||
|
||||
it('场景二:插入 Node 实例', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form');
|
||||
const inputNode = currentDocument?.createNode({
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id2',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
},
|
||||
});
|
||||
formNode && currentDocument?.insertNode(formNode, inputNode);
|
||||
expect(formNode?.children?.get(3)?.componentName).toBe('TextInput');
|
||||
expect(nodesMap?.size).toBe(ids.length + 1);
|
||||
});
|
||||
|
||||
it('场景三:插入 JSExpression', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form') as Node;
|
||||
currentDocument?.insertNode(formNode, {
|
||||
type: 'JSExpression',
|
||||
value: 'just a expression',
|
||||
});
|
||||
expect(nodesMap?.size).toBe(ids.length + 1);
|
||||
expect(formNode.children?.get(3)?.componentName).toBe('Leaf');
|
||||
// expect(formNode.children?.get(3)?.children).toEqual({
|
||||
// type: 'JSExpression',
|
||||
// value: 'just a expression'
|
||||
// });
|
||||
});
|
||||
it('场景四:插入 string', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form') as Node;
|
||||
currentDocument?.insertNode(formNode, 'just a string');
|
||||
expect(nodesMap?.size).toBe(ids.length + 1);
|
||||
expect(formNode.children?.get(3)?.componentName).toBe('Leaf');
|
||||
// expect(formNode.children?.get(3)?.children).toBe('just a string');
|
||||
});
|
||||
});
|
||||
|
||||
describe('节点新增(insertNodes)', () => {
|
||||
let project: Project;
|
||||
beforeEach(() => {
|
||||
project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
});
|
||||
it('场景一:插入 NodeSchema,指定 index', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form') as Node;
|
||||
const formNode2 = currentDocument?.getNode('form');
|
||||
expect(formNode).toEqual(formNode2);
|
||||
currentDocument?.insertNodes(
|
||||
formNode,
|
||||
[
|
||||
{
|
||||
componentName: 'TextInput',
|
||||
props: {
|
||||
propA: 'haha2',
|
||||
propB: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'TextInput2',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
},
|
||||
},
|
||||
],
|
||||
1,
|
||||
);
|
||||
expect(nodesMap?.size).toBe(ids.length + 2);
|
||||
expect(formNode.children?.length).toBe(5);
|
||||
const insertedNode1 = formNode.children?.get(1);
|
||||
const insertedNode2 = formNode.children?.get(2);
|
||||
expect(insertedNode1?.componentName).toBe('TextInput');
|
||||
expect(insertedNode1?.propsData).toEqual({
|
||||
propA: 'haha2',
|
||||
propB: 3,
|
||||
});
|
||||
expect(insertedNode2?.componentName).toBe('TextInput2');
|
||||
expect(insertedNode2?.propsData).toEqual({
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
});
|
||||
});
|
||||
|
||||
it.only('场景二:插入 Node 实例,指定 index', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form') as INode;
|
||||
const formNode2 = currentDocument?.getNode('form');
|
||||
expect(formNode).toEqual(formNode2);
|
||||
const createdNode1 = currentDocument?.createNode({
|
||||
componentName: 'TextInput',
|
||||
props: {
|
||||
propA: 'haha2',
|
||||
propB: 3,
|
||||
},
|
||||
});
|
||||
const createdNode2 = currentDocument?.createNode({
|
||||
componentName: 'TextInput2',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
},
|
||||
});
|
||||
currentDocument?.insertNodes(formNode, [createdNode1, createdNode2], 1);
|
||||
expect(nodesMap?.size).toBe(ids.length + 2);
|
||||
expect(formNode.children?.length).toBe(5);
|
||||
const insertedNode1 = formNode.children?.get(1);
|
||||
const insertedNode2 = formNode.children?.get(2);
|
||||
expect(insertedNode1?.componentName).toBe('TextInput');
|
||||
expect(insertedNode1?.propsData).toEqual({
|
||||
propA: 'haha2',
|
||||
propB: 3,
|
||||
});
|
||||
expect(insertedNode2?.componentName).toBe('TextInput2');
|
||||
expect(insertedNode2?.propsData).toEqual({
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('block ❌ | component ❌ | slot ✅', () => {
|
||||
it('基本的 slot 创建', () => {
|
||||
const formSchemaWithSlot = set(
|
||||
cloneDeep(formSchema),
|
||||
'children[0].children[0].props.title.type',
|
||||
'JSSlot',
|
||||
);
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchemaWithSlot],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
// 目前每个 slot 会新增(1 + children.length)个节点
|
||||
const expectedNodeCnt = ids.length + 2;
|
||||
expect(nodesMap?.size).toBe(expectedNodeCnt);
|
||||
// PageHeader
|
||||
expect(nodesMap?.get('node_k1ow3cbd')?.slots).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,56 +0,0 @@
|
||||
import '../../fixtures/window';
|
||||
import { Project } from '../../../src/project/project';
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import formSchema from '../../fixtures/schema/form';
|
||||
import { getIdsFromSchema, getNodeFromSchemaById } from '../../utils';
|
||||
|
||||
const mockCreateSettingEntry = jest.fn();
|
||||
jest.mock('../../../src/designer/designer', () => {
|
||||
return {
|
||||
Designer: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
getComponentMeta() {
|
||||
return {
|
||||
getMetadata() {
|
||||
return { configure: { advanced: null } };
|
||||
},
|
||||
};
|
||||
},
|
||||
transformProps(props) { return props; },
|
||||
createSettingEntry: mockCreateSettingEntry,
|
||||
postEvent() {},
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let designer = null;
|
||||
beforeAll(() => {
|
||||
designer = new Designer({});
|
||||
});
|
||||
|
||||
describe.skip('节点拖拽测试', () => {
|
||||
describe('block ❌ | component ❌ | slot ❌', () => {
|
||||
it('修改普通属性,string | number', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
expect(nodesMap.size).toBe(expectedNodeCnt);
|
||||
ids.forEach(id => {
|
||||
expect(nodesMap.get(id).componentName).toBe(getNodeFromSchemaById(formSchema, id).componentName);
|
||||
});
|
||||
|
||||
const exportSchema = currentDocument?.export(1);
|
||||
expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt);
|
||||
expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,456 +0,0 @@
|
||||
import '../../fixtures/window';
|
||||
import { Project } from '../../../src/project/project';
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import formSchema from '../../fixtures/schema/form';
|
||||
import { getIdsFromSchema, getNodeFromSchemaById } from '../../utils';
|
||||
|
||||
const mockCreateSettingEntry = jest.fn();
|
||||
jest.mock('../../../src/designer/designer', () => {
|
||||
return {
|
||||
Designer: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
getComponentMeta() {
|
||||
return {
|
||||
getMetadata() {
|
||||
return { configure: { advanced: null } };
|
||||
},
|
||||
get advanced() {
|
||||
return {};
|
||||
},
|
||||
};
|
||||
},
|
||||
transformProps(props) { return props; },
|
||||
createSettingEntry: mockCreateSettingEntry,
|
||||
postEvent() {},
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let designer = null;
|
||||
beforeAll(() => {
|
||||
designer = new Designer({});
|
||||
});
|
||||
|
||||
describe('schema 生成节点模型测试', () => {
|
||||
describe('block ❌ | component ❌ | slot ❌', () => {
|
||||
let project: Project;
|
||||
beforeEach(() => {
|
||||
project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
});
|
||||
it('读取普通属性,string | number | object', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
const formNode = currentDocument?.getNode('form');
|
||||
/*
|
||||
props: {
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
autoValidate: true,
|
||||
scrollToFirstError: true,
|
||||
autoUnmount: true,
|
||||
behavior: 'NORMAL',
|
||||
dataSource: {
|
||||
type: 'variable',
|
||||
variable: 'state.formData',
|
||||
},
|
||||
obj: {
|
||||
a: 1,
|
||||
b: false,
|
||||
c: 'string',
|
||||
},
|
||||
__style__: {},
|
||||
fieldId: 'form',
|
||||
fieldOptions: {},
|
||||
},
|
||||
id: 'form',
|
||||
condition: true,
|
||||
*/
|
||||
const sizeProp = formNode?.getProp('size');
|
||||
const sizeProp2 = formNode?.getProps().getProp('size');
|
||||
expect(sizeProp).toBe(sizeProp2);
|
||||
expect(sizeProp?.getAsString()).toBe('medium');
|
||||
expect(sizeProp?.getValue()).toBe('medium');
|
||||
|
||||
const autoValidateProp = formNode?.getProp('autoValidate');
|
||||
expect(autoValidateProp?.getValue()).toBe(true);
|
||||
|
||||
const objProp = formNode?.getProp('obj');
|
||||
expect(objProp?.getValue()).toEqual({
|
||||
a: 1,
|
||||
b: false,
|
||||
c: 'string',
|
||||
});
|
||||
const objAProp = formNode?.getProp('obj.a');
|
||||
const objBProp = formNode?.getProp('obj.b');
|
||||
const objCProp = formNode?.getProp('obj.c');
|
||||
expect(objAProp?.getValue()).toBe(1);
|
||||
expect(objBProp?.getValue()).toBe(false);
|
||||
expect(objCProp?.getValue()).toBe('string');
|
||||
|
||||
const idProp = formNode?.getExtraProp('extraPropA');
|
||||
expect(idProp?.getValue()).toBe('extraPropA');
|
||||
});
|
||||
|
||||
it('修改普通属性,string | number | object,使用 Node 实例接口', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
const formNode = currentDocument?.getNode('form');
|
||||
/*
|
||||
props: {
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
autoValidate: true,
|
||||
scrollToFirstError: true,
|
||||
autoUnmount: true,
|
||||
behavior: 'NORMAL',
|
||||
dataSource: {
|
||||
type: 'variable',
|
||||
variable: 'state.formData',
|
||||
},
|
||||
obj: {
|
||||
a: 1,
|
||||
b: false,
|
||||
c: 'string',
|
||||
},
|
||||
__style__: {},
|
||||
fieldId: 'form',
|
||||
fieldOptions: {},
|
||||
},
|
||||
id: 'form',
|
||||
condition: true,
|
||||
*/
|
||||
formNode?.setPropValue('size', 'large');
|
||||
const sizeProp = formNode?.getProp('size');
|
||||
expect(sizeProp?.getAsString()).toBe('large');
|
||||
expect(sizeProp?.getValue()).toBe('large');
|
||||
|
||||
formNode?.setPropValue('autoValidate', false);
|
||||
const autoValidateProp = formNode?.getProp('autoValidate');
|
||||
expect(autoValidateProp?.getValue()).toBe(false);
|
||||
|
||||
formNode?.setPropValue('obj', {
|
||||
a: 2,
|
||||
b: true,
|
||||
c: 'another string',
|
||||
});
|
||||
const objProp = formNode?.getProp('obj');
|
||||
expect(objProp?.getValue()).toEqual({
|
||||
a: 2,
|
||||
b: true,
|
||||
c: 'another string',
|
||||
});
|
||||
formNode?.setPropValue('obj.a', 3);
|
||||
formNode?.setPropValue('obj.b', false);
|
||||
formNode?.setPropValue('obj.c', 'string');
|
||||
const objAProp = formNode?.getProp('obj.a');
|
||||
const objBProp = formNode?.getProp('obj.b');
|
||||
const objCProp = formNode?.getProp('obj.c');
|
||||
expect(objAProp?.getValue()).toBe(3);
|
||||
expect(objBProp?.getValue()).toBe(false);
|
||||
expect(objCProp?.getValue()).toBe('string');
|
||||
expect(objProp?.getValue()).toEqual({
|
||||
a: 3,
|
||||
b: false,
|
||||
c: 'string',
|
||||
});
|
||||
});
|
||||
|
||||
it('修改普通属性,string | number | object,使用 Props 实例接口', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
const formNode = currentDocument?.getNode('form');
|
||||
/*
|
||||
props: {
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
autoValidate: true,
|
||||
scrollToFirstError: true,
|
||||
autoUnmount: true,
|
||||
behavior: 'NORMAL',
|
||||
dataSource: {
|
||||
type: 'variable',
|
||||
variable: 'state.formData',
|
||||
},
|
||||
obj: {
|
||||
a: 1,
|
||||
b: false,
|
||||
c: 'string',
|
||||
},
|
||||
__style__: {},
|
||||
fieldId: 'form',
|
||||
fieldOptions: {},
|
||||
},
|
||||
id: 'form',
|
||||
condition: true,
|
||||
*/
|
||||
const props = formNode?.getProps();
|
||||
props?.setPropValue('size', 'large');
|
||||
const sizeProp = formNode?.getProp('size');
|
||||
expect(sizeProp?.getAsString()).toBe('large');
|
||||
expect(sizeProp?.getValue()).toBe('large');
|
||||
|
||||
props?.setPropValue('autoValidate', false);
|
||||
const autoValidateProp = formNode?.getProp('autoValidate');
|
||||
expect(autoValidateProp?.getValue()).toBe(false);
|
||||
|
||||
props?.setPropValue('obj', {
|
||||
a: 2,
|
||||
b: true,
|
||||
c: 'another string',
|
||||
});
|
||||
const objProp = formNode?.getProp('obj');
|
||||
expect(objProp?.getValue()).toEqual({
|
||||
a: 2,
|
||||
b: true,
|
||||
c: 'another string',
|
||||
});
|
||||
props?.setPropValue('obj.a', 3);
|
||||
props?.setPropValue('obj.b', false);
|
||||
props?.setPropValue('obj.c', 'string');
|
||||
const objAProp = formNode?.getProp('obj.a');
|
||||
const objBProp = formNode?.getProp('obj.b');
|
||||
const objCProp = formNode?.getProp('obj.c');
|
||||
expect(objAProp?.getValue()).toBe(3);
|
||||
expect(objBProp?.getValue()).toBe(false);
|
||||
expect(objCProp?.getValue()).toBe('string');
|
||||
expect(objProp?.getValue()).toEqual({
|
||||
a: 3,
|
||||
b: false,
|
||||
c: 'string',
|
||||
});
|
||||
});
|
||||
|
||||
it('修改普通属性,string | number | object,使用 Prop 实例接口', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
const formNode = currentDocument?.getNode('form');
|
||||
/*
|
||||
props: {
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
autoValidate: true,
|
||||
scrollToFirstError: true,
|
||||
autoUnmount: true,
|
||||
behavior: 'NORMAL',
|
||||
dataSource: {
|
||||
type: 'variable',
|
||||
variable: 'state.formData',
|
||||
},
|
||||
obj: {
|
||||
a: 1,
|
||||
b: false,
|
||||
c: 'string',
|
||||
},
|
||||
__style__: {},
|
||||
fieldId: 'form',
|
||||
fieldOptions: {},
|
||||
},
|
||||
id: 'form',
|
||||
condition: true,
|
||||
*/
|
||||
const sizeProp = formNode?.getProp('size');
|
||||
sizeProp?.setValue('large');
|
||||
expect(sizeProp?.getAsString()).toBe('large');
|
||||
expect(sizeProp?.getValue()).toBe('large');
|
||||
|
||||
const autoValidateProp = formNode?.getProp('autoValidate');
|
||||
autoValidateProp?.setValue(false);
|
||||
expect(autoValidateProp?.getValue()).toBe(false);
|
||||
|
||||
|
||||
const objProp = formNode?.getProp('obj');
|
||||
objProp?.setValue({
|
||||
a: 2,
|
||||
b: true,
|
||||
c: 'another string',
|
||||
});
|
||||
expect(objProp?.getValue()).toEqual({
|
||||
a: 2,
|
||||
b: true,
|
||||
c: 'another string',
|
||||
});
|
||||
const objAProp = formNode?.getProp('obj.a');
|
||||
const objBProp = formNode?.getProp('obj.b');
|
||||
const objCProp = formNode?.getProp('obj.c');
|
||||
objAProp?.setValue(3);
|
||||
objBProp?.setValue(false);
|
||||
objCProp?.setValue('string');
|
||||
expect(objAProp?.getValue()).toBe(3);
|
||||
expect(objBProp?.getValue()).toBe(false);
|
||||
expect(objCProp?.getValue()).toBe('string');
|
||||
expect(objProp?.getValue()).toEqual({
|
||||
a: 3,
|
||||
b: false,
|
||||
c: 'string',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('block ❌ | component ❌ | slot ✅', () => {
|
||||
let project: Project;
|
||||
beforeEach(() => {
|
||||
project = new Project(designer, {
|
||||
componentsTree: [
|
||||
formSchema,
|
||||
],
|
||||
});
|
||||
project.open();
|
||||
});
|
||||
it('修改 slot 属性,初始存在 slot 属性名,正常生成节点模型', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
const formNode = currentDocument?.getNode('form');
|
||||
|
||||
formNode?.setPropValue('slotA', {
|
||||
type: 'JSSlot',
|
||||
value: [{
|
||||
componentName: 'TextInput1',
|
||||
props: {
|
||||
txt: 'haha',
|
||||
num: 1,
|
||||
bool: true,
|
||||
},
|
||||
}, {
|
||||
componentName: 'TextInput2',
|
||||
props: {
|
||||
txt: 'heihei',
|
||||
num: 2,
|
||||
bool: false,
|
||||
},
|
||||
}],
|
||||
});
|
||||
|
||||
expect(nodesMap.size).toBe(ids.length + 3);
|
||||
expect(formNode?.slots).toHaveLength(1);
|
||||
expect(formNode?.slots[0].children).toHaveLength(2);
|
||||
const firstChildNode = formNode?.slots[0].children?.get(0);
|
||||
const secondChildNode = formNode?.slots[0].children?.get(1);
|
||||
expect(firstChildNode?.componentName).toBe('TextInput1');
|
||||
expect(firstChildNode?.getPropValue('txt')).toBe('haha');
|
||||
expect(firstChildNode?.getPropValue('num')).toBe(1);
|
||||
expect(firstChildNode?.getPropValue('bool')).toBe(true);
|
||||
expect(secondChildNode?.componentName).toBe('TextInput2');
|
||||
expect(secondChildNode?.getPropValue('txt')).toBe('heihei');
|
||||
expect(secondChildNode?.getPropValue('num')).toBe(2);
|
||||
expect(secondChildNode?.getPropValue('bool')).toBe(false);
|
||||
});
|
||||
|
||||
it('修改 slot 属性,初始存在 slot 属性名,关闭 slot', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
const formNode = currentDocument?.getNode('form');
|
||||
|
||||
formNode?.setPropValue('slotA', {
|
||||
type: 'JSSlot',
|
||||
value: [{
|
||||
componentName: 'TextInput1',
|
||||
props: {
|
||||
txt: 'haha',
|
||||
num: 1,
|
||||
bool: true,
|
||||
},
|
||||
}, {
|
||||
componentName: 'TextInput2',
|
||||
props: {
|
||||
txt: 'heihei',
|
||||
num: 2,
|
||||
bool: false,
|
||||
},
|
||||
}],
|
||||
});
|
||||
|
||||
expect(nodesMap.size).toBe(ids.length + 3);
|
||||
expect(formNode?.slots).toHaveLength(1);
|
||||
|
||||
formNode?.setPropValue('slotA', '');
|
||||
|
||||
expect(nodesMap.size).toBe(ids.length);
|
||||
expect(formNode?.slots).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('修改 slot 属性,初始存在 slot 属性名,同名覆盖 slot', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
const formNode = currentDocument?.getNode('form');
|
||||
|
||||
formNode?.setPropValue('slotA', {
|
||||
type: 'JSSlot',
|
||||
name: 'slotA',
|
||||
value: [{
|
||||
componentName: 'TextInput1',
|
||||
props: {
|
||||
txt: 'haha',
|
||||
num: 1,
|
||||
bool: true,
|
||||
},
|
||||
}, {
|
||||
componentName: 'TextInput2',
|
||||
props: {
|
||||
txt: 'heihei',
|
||||
num: 2,
|
||||
bool: false,
|
||||
},
|
||||
}],
|
||||
});
|
||||
|
||||
expect(nodesMap.size).toBe(ids.length + 3);
|
||||
expect(formNode?.slots).toHaveLength(1);
|
||||
expect(formNode?.slots[0].children).toHaveLength(2);
|
||||
|
||||
let firstChildNode = formNode?.slots[0].children?.get(0);
|
||||
expect(firstChildNode?.componentName).toBe('TextInput1');
|
||||
expect(firstChildNode?.getPropValue('txt')).toBe('haha');
|
||||
expect(firstChildNode?.getPropValue('num')).toBe(1);
|
||||
expect(firstChildNode?.getPropValue('bool')).toBe(true);
|
||||
|
||||
formNode?.setPropValue('slotA', {
|
||||
type: 'JSSlot',
|
||||
name: 'slotA',
|
||||
value: [{
|
||||
componentName: 'TextInput3',
|
||||
props: {
|
||||
txt: 'xixi',
|
||||
num: 3,
|
||||
bool: false,
|
||||
},
|
||||
}],
|
||||
});
|
||||
|
||||
expect(nodesMap.size).toBe(ids.length + 2);
|
||||
expect(formNode?.slots).toHaveLength(1);
|
||||
expect(formNode?.slots[0].children).toHaveLength(1);
|
||||
firstChildNode = formNode?.slots[0].children?.get(0);
|
||||
expect(firstChildNode?.componentName).toBe('TextInput3');
|
||||
expect(firstChildNode?.getPropValue('txt')).toBe('xixi');
|
||||
expect(firstChildNode?.getPropValue('num')).toBe(3);
|
||||
expect(firstChildNode?.getPropValue('bool')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,123 +0,0 @@
|
||||
import { set, cloneDeep } from 'lodash-es';
|
||||
import '../../fixtures/window';
|
||||
import { Project } from '../../../src/project/project';
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import formSchema from '../../fixtures/schema/form';
|
||||
import { getIdsFromSchema } from '../../utils';
|
||||
|
||||
const mockCreateSettingEntry = jest.fn();
|
||||
jest.mock('../../../src/designer/designer', () => {
|
||||
return {
|
||||
Designer: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
getComponentMeta() {
|
||||
return {
|
||||
getMetadata() {
|
||||
return { configure: { advanced: null } };
|
||||
},
|
||||
get advanced() {
|
||||
return {};
|
||||
},
|
||||
};
|
||||
},
|
||||
transformProps(props) {
|
||||
return props;
|
||||
},
|
||||
createSettingEntry: mockCreateSettingEntry,
|
||||
postEvent() {},
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let designer = null;
|
||||
beforeAll(() => {
|
||||
designer = new Designer({});
|
||||
});
|
||||
|
||||
describe('节点模型删除测试', () => {
|
||||
it('删除叶子节点', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const originalNodeCnt = ids.length;
|
||||
expect(nodesMap.size).toBe(originalNodeCnt);
|
||||
|
||||
currentDocument?.removeNode('node_k1ow3cbn');
|
||||
// Button#1
|
||||
expect(nodesMap.size).toBe(originalNodeCnt - 1);
|
||||
|
||||
currentDocument?.removeNode(nodesMap.get('node_k1ow3cbp'));
|
||||
// Button#2
|
||||
expect(nodesMap.size).toBe(originalNodeCnt - 2);
|
||||
|
||||
currentDocument?.removeNode('unexisting_node');
|
||||
expect(nodesMap.size).toBe(originalNodeCnt - 2);
|
||||
});
|
||||
|
||||
it('删除叶子节点,带有 slot', () => {
|
||||
const formSchemaWithSlot = set(
|
||||
cloneDeep(formSchema),
|
||||
'children[1].children[0].children[2].children[1].props.greeting.type',
|
||||
'JSSlot',
|
||||
);
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchemaWithSlot],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const originalNodeCnt = ids.length + 2;
|
||||
expect(nodesMap.size).toBe(originalNodeCnt);
|
||||
|
||||
currentDocument?.removeNode('node_k1ow3cbp');
|
||||
// Button + Slot + Text
|
||||
expect(nodesMap.size).toBe(originalNodeCnt - 3);
|
||||
});
|
||||
|
||||
it('删除分支节点', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const originalNodeCnt = ids.length;
|
||||
expect(nodesMap.size).toBe(originalNodeCnt);
|
||||
|
||||
currentDocument?.removeNode('node_k1ow3cbo');
|
||||
// Div + 2 * Button
|
||||
expect(nodesMap.size).toBe(originalNodeCnt - 3);
|
||||
});
|
||||
|
||||
it('删除分支节点,带有 slot', () => {
|
||||
const formSchemaWithSlot = set(
|
||||
cloneDeep(formSchema),
|
||||
'children[1].children[0].children[2].children[1].props.greeting.type',
|
||||
'JSSlot',
|
||||
);
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchemaWithSlot],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const originalNodeCnt = ids.length + 2;
|
||||
expect(nodesMap.size).toBe(originalNodeCnt);
|
||||
|
||||
currentDocument?.removeNode('node_k1ow3cbo');
|
||||
// Div + 2 * Button + Slot + Text
|
||||
expect(nodesMap.size).toBe(originalNodeCnt - 5);
|
||||
});
|
||||
});
|
||||
@ -1,664 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import '../../fixtures/window';
|
||||
import { set } from '../../utils';
|
||||
import {
|
||||
Editor,
|
||||
globalContext,
|
||||
Setters as InnerSetters,
|
||||
} from '@alilc/lowcode-editor-core';
|
||||
import { Project } from '../../../src/project/project';
|
||||
import { Workspace as InnerWorkspace } from '@alilc/lowcode-workspace';
|
||||
import { DocumentModel } from '../../../src/document/document-model';
|
||||
import {
|
||||
isRootNode,
|
||||
Node,
|
||||
comparePosition,
|
||||
contains,
|
||||
PositionNO,
|
||||
} from '../../../src/document/node/node';
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import formSchema from '../../fixtures/schema/form';
|
||||
import divMetadata from '../../fixtures/component-metadata/div';
|
||||
import dialogMetadata from '../../fixtures/component-metadata/dialog';
|
||||
import btnMetadata from '../../fixtures/component-metadata/button';
|
||||
import formMetadata from '../../fixtures/component-metadata/form';
|
||||
import pageMetadata from '../../fixtures/component-metadata/page';
|
||||
import rootHeaderMetadata from '../../fixtures/component-metadata/root-header';
|
||||
import rootContentMetadata from '../../fixtures/component-metadata/root-content';
|
||||
import rootFooterMetadata from '../../fixtures/component-metadata/root-footer';
|
||||
import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory';
|
||||
import { isNode } from '@alilc/lowcode-utils';
|
||||
import { Setters } from '@alilc/lowcode-shell';
|
||||
|
||||
describe('Node 方法测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
|
||||
beforeEach(() => {
|
||||
editor = new Editor();
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
project = designer.project;
|
||||
doc = new DocumentModel(project, formSchema);
|
||||
editor.set('setters', new Setters(new InnerSetters()));
|
||||
!globalContext.has(Editor) && globalContext.register(editor, Editor);
|
||||
!globalContext.has('workspace') && globalContext.register(new InnerWorkspace(), 'workspace');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
designer.purge();
|
||||
editor = null;
|
||||
designer = null;
|
||||
project = null;
|
||||
});
|
||||
|
||||
// Case 1: When children is null
|
||||
test('initialChildren returns result of initialChildren function when children is null ', () => {
|
||||
const node = new Node(doc, { componentName: 'Button', props: { a: 1 } });
|
||||
const result = node.initialChildren(null);
|
||||
// 预期结果是一个空数组
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
// Case 2: When children is undefined
|
||||
test('initialChildren returns result of initialChildren function when children is null ', () => {
|
||||
const node = new Node(doc, { componentName: 'Button', props: { a: 1 } });
|
||||
const result = node.initialChildren(undefined);
|
||||
// 预期结果是一个空数组
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
// Case 3: When children is array
|
||||
test('initialChildren returns result of initialChildren function when children is null ', () => {
|
||||
const node = new Node(doc, { componentName: 'Button', props: { a: 1 } });
|
||||
const childrenArray = [{ id: 1, name: 'Child 1' }, { id: 2, name: 'Child 2' }];
|
||||
const result = node.initialChildren(childrenArray);
|
||||
// 预期结果是一个数组
|
||||
expect(result).toEqual(childrenArray);
|
||||
});
|
||||
|
||||
// Case 4: When children is not null and not an array
|
||||
test('initialChildren returns result of initialChildren function when children is null ', () => {
|
||||
const node = new Node(doc, { componentName: 'Button', props: { a: 1 } });
|
||||
const childObject = { id: 1, name: 'Child 1' };
|
||||
const result = node.initialChildren(childObject);
|
||||
// 预期结果是一个数组
|
||||
expect(result).toEqual([childObject]);
|
||||
});
|
||||
|
||||
// Case 5: When children 0
|
||||
test('initialChildren returns result of initialChildren function when children is null ', () => {
|
||||
const node = new Node(doc, { componentName: 'Button', props: { a: 1 } });
|
||||
const childObject = 0;
|
||||
const result = node.initialChildren(childObject);
|
||||
// 预期结果是一个数组
|
||||
expect(result).toEqual([0]);
|
||||
});
|
||||
|
||||
// Case 6: When children false
|
||||
test('initialChildren returns result of initialChildren function when children is null ', () => {
|
||||
const node = new Node(doc, { componentName: 'Button', props: { a: 1 } });
|
||||
const childObject = false;
|
||||
const result = node.initialChildren(childObject);
|
||||
// 预期结果是一个数组
|
||||
expect(result).toEqual([false]);
|
||||
});
|
||||
|
||||
|
||||
it('condition group', () => { });
|
||||
|
||||
it('getExtraProp / setExtraProp', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
expect(firstBtn.getExtraProp('non-existing', false)).toBeNull();
|
||||
|
||||
firstBtn.setExtraProp('xxx', '1111');
|
||||
expect(firstBtn.getExtraProp('xxx', false).getValue()).toBe('1111');
|
||||
});
|
||||
|
||||
it('import(leaf)', () => {
|
||||
const form = doc.getNode('node_k1ow3cbo');
|
||||
form.insert({ componentName: 'Leaf', children: '111' });
|
||||
|
||||
const leaf = form.getChildren().get(2);
|
||||
expect(leaf.getPropValue('children')).toBe('111');
|
||||
|
||||
leaf.import({ componentName: 'Leaf', children: '222' });
|
||||
expect(leaf.getPropValue('children')).toBe('222');
|
||||
|
||||
leaf.import({ componentName: 'Leaf', children: { type: 'JSExpression', value: 'state.x' } });
|
||||
expect(leaf.getPropValue('children')).toEqual({ type: 'JSExpression', value: 'state.x' });
|
||||
});
|
||||
|
||||
it('hasCondition', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
firstBtn.getExtraProp('condition')?.setValue(undefined);
|
||||
expect(firstBtn.hasCondition()).toBeFalsy();
|
||||
|
||||
firstBtn.getExtraProp('condition')?.setValue(null);
|
||||
expect(firstBtn.hasCondition()).toBeFalsy();
|
||||
|
||||
firstBtn.getExtraProp('condition')?.setValue(true);
|
||||
expect(firstBtn.hasCondition()).toBeFalsy();
|
||||
|
||||
firstBtn.getExtraProp('condition')?.setValue('');
|
||||
expect(firstBtn.hasCondition()).toBeFalsy();
|
||||
|
||||
firstBtn.getExtraProp('condition')?.setValue(1);
|
||||
expect(firstBtn.hasCondition()).toBeTruthy();
|
||||
|
||||
firstBtn.getExtraProp('condition')?.setValue(false);
|
||||
expect(firstBtn.hasCondition()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('hasLoop', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
expect(firstBtn.hasLoop()).toBeFalsy();
|
||||
|
||||
// 这里必须用 add,因为 hasLoop 实现的跳过了 stash
|
||||
firstBtn.props.add([1, 2], '___loop___');
|
||||
expect(firstBtn.hasLoop()).toBeTruthy();
|
||||
|
||||
firstBtn.getExtraProp('loop')?.setValue({ type: 'JSExpression', value: 'state.a' });
|
||||
expect(firstBtn.hasLoop()).toBeTruthy();
|
||||
|
||||
firstBtn.getExtraProp('loop')?.setValue(1);
|
||||
expect(firstBtn.hasLoop()).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('getSuitablePlace', () => {
|
||||
it('root,子节点中有容器节点', () => {
|
||||
designer.createComponentMeta(pageMetadata);
|
||||
designer.createComponentMeta(rootHeaderMetadata);
|
||||
designer.createComponentMeta(rootContentMetadata);
|
||||
designer.createComponentMeta(rootFooterMetadata);
|
||||
|
||||
const rootHeaderMeta = designer.getComponentMeta('RootHeader');
|
||||
set(rootHeaderMeta, 'prototype.options.canDropIn', true);
|
||||
|
||||
let o = doc.rootNode?.getSuitablePlace(doc.getNode('form'), 1);
|
||||
expect(o).toEqual({
|
||||
container: doc.getNode('node_k1ow3cba'),
|
||||
ref: 1,
|
||||
});
|
||||
|
||||
set(rootHeaderMeta, 'prototype.options.canDropIn', () => true);
|
||||
o = doc.rootNode?.getSuitablePlace(doc.getNode('form'), 1);
|
||||
expect(o).toEqual({
|
||||
container: doc.getNode('node_k1ow3cba'),
|
||||
ref: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it('root,直接子节点中无容器节点,自身支持放入子节点', () => {
|
||||
designer.createComponentMeta(pageMetadata);
|
||||
|
||||
let o = doc.rootNode?.getSuitablePlace(doc.getNode('form'), 1);
|
||||
|
||||
const pageMeta = designer.getComponentMeta('Page');
|
||||
set(pageMeta, 'prototype.options.canDropIn', () => true);
|
||||
|
||||
expect(o).toEqual({
|
||||
container: doc.rootNode,
|
||||
ref: 1,
|
||||
});
|
||||
|
||||
set(pageMeta, 'prototype.options.canDropIn', undefined);
|
||||
o = doc.rootNode?.getSuitablePlace(doc.getNode('form'), 1);
|
||||
expect(o).toEqual({
|
||||
container: doc.rootNode,
|
||||
ref: 1,
|
||||
});
|
||||
|
||||
set(pageMeta, 'prototype.options.canDropIn', true);
|
||||
o = doc.rootNode?.getSuitablePlace(doc.getNode('form'), 1);
|
||||
expect(o).toEqual({
|
||||
container: doc.rootNode,
|
||||
ref: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it('root,子节点中无容器节点,自己也不支持放入子节点', () => {
|
||||
designer.createComponentMeta(pageMetadata);
|
||||
|
||||
let pageMeta = designer.getComponentMeta('Page');
|
||||
|
||||
pageMeta = set(pageMeta, 'prototype.options.canDropIn', () => false);
|
||||
let o = doc.rootNode?.getSuitablePlace(doc.getNode('form'), 1);
|
||||
expect(o).toBeNull();
|
||||
|
||||
set(pageMeta, 'prototype.options.canDropIn', false);
|
||||
o = doc.rootNode?.getSuitablePlace(doc.getNode('form'), 1);
|
||||
expect(o).toBeNull();
|
||||
});
|
||||
|
||||
it('放入模态节点', () => {
|
||||
designer.createComponentMeta(pageMetadata);
|
||||
designer.createComponentMeta(dialogMetadata);
|
||||
|
||||
const dialog = doc.createNode({ componentName: 'Dialog' });
|
||||
|
||||
const o = doc.rootNode?.getSuitablePlace(dialog, 1);
|
||||
expect(o.container).toBe(doc.rootNode);
|
||||
expect(o.ref).toBe(1);
|
||||
});
|
||||
|
||||
it('包含 focusNode', () => {
|
||||
const o = doc.rootNode?.getSuitablePlace(doc.rootNode);
|
||||
expect(o.container).toBe(doc.rootNode);
|
||||
});
|
||||
|
||||
it.skip('非 root 节点,不能放入子节点', () => {
|
||||
designer.createComponentMeta(formMetadata);
|
||||
designer.createComponentMeta(pageMetadata);
|
||||
|
||||
// form 子节点以及自身都不能放入子节点
|
||||
const formMeta = designer.getComponentMeta('Form');
|
||||
set(formMeta, 'prototype.options.canDropIn', false);
|
||||
|
||||
const pageMeta = designer.getComponentMeta('Page');
|
||||
set(pageMeta, 'prototype.options.canDropIn', () => true);
|
||||
|
||||
const o = doc.getNode('form')!.getSuitablePlace(doc.getNode('node_k1ow3cbj'), { index: 1 });
|
||||
expect(o).toEqual({
|
||||
container: doc.rootNode,
|
||||
ref: { index: 1 },
|
||||
});
|
||||
});
|
||||
|
||||
it('非 root 节点,能放入子节点', () => {
|
||||
designer.createComponentMeta(formMetadata);
|
||||
designer.createComponentMeta(pageMetadata);
|
||||
|
||||
// form 子节点以及自身都不能放入子节点
|
||||
const formMeta = designer.getComponentMeta('Form');
|
||||
set(formMeta, 'prototype.options.canDropIn', true);
|
||||
|
||||
const o = doc.getNode('form')!.getSuitablePlace(doc.getNode('node_k1ow3cbj'), 1);
|
||||
expect(o).toEqual({
|
||||
container: doc.getNode('form'),
|
||||
ref: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it('null', () => {
|
||||
expect(
|
||||
doc.rootNode?.getSuitablePlace.call({
|
||||
contains: () => false,
|
||||
isContainer: () => false,
|
||||
isRoot: () => false,
|
||||
}),
|
||||
).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('removeChild / replaceWith / replaceChild', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const form = doc.getNode('node_k1ow3cbo');
|
||||
|
||||
// 不符合条件的节点直接返回
|
||||
expect(firstBtn.replaceChild(form, { componentName: 'Button', props: { x: 1 } })).toBe(form);
|
||||
|
||||
firstBtn.select();
|
||||
firstBtn.parent?.replaceChild(firstBtn, { componentName: 'Button', props: { x: 1 } });
|
||||
|
||||
expect(firstBtn.parent?.getChildren()?.size).toBe(2);
|
||||
expect(firstBtn.parent?.getChildren()?.get(0)?.getPropValue('x')).toBe(1);
|
||||
|
||||
const secondBtn = doc.getNode('node_k1ow3cbp')!;
|
||||
secondBtn.replaceWith({ componentName: 'Button', props: { y: 1 } });
|
||||
expect(firstBtn.parent?.getChildren()?.size).toBe(2);
|
||||
expect(firstBtn.parent?.getChildren()?.get(1)?.getPropValue('y')).toBe(1);
|
||||
});
|
||||
|
||||
it('schema', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const schema = firstBtn.schema;
|
||||
schema.props.size = 'large';
|
||||
firstBtn.schema = schema;
|
||||
|
||||
expect(firstBtn.getPropValue('size')).toBe('large');
|
||||
});
|
||||
|
||||
describe('插入相关方法', () => {
|
||||
it('insertBefore / onChildrenChange', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const secondBtn = doc.getNode('node_k1ow3cbp')!;
|
||||
const btnParent = firstBtn.parent!;
|
||||
const mockFn = jest.fn();
|
||||
const off = btnParent.onChildrenChange(mockFn);
|
||||
|
||||
// Node 实例
|
||||
btnParent.insertBefore(new Node(doc, { componentName: 'Button', props: { a: 1 } }), firstBtn);
|
||||
expect(btnParent.children.get(0)?.getProps().export().props).toEqual({ a: 1 });
|
||||
expect(mockFn).toHaveBeenCalledTimes(1);
|
||||
|
||||
// TODO: 暂时不支持,后面补上
|
||||
// // NodeSchema
|
||||
// btnParent.insertBefore({ componentName: 'Button', props: { b: 1 } }, firstBtn);
|
||||
// expect(btnParent.children.get(0)?.getProps().export().props).toEqual({ b: 1 });
|
||||
// expect(mockFn).toHaveBeenCalledTimes(2);
|
||||
|
||||
// // getComponentName
|
||||
// btnParent.insertBefore({ getComponentName: () => 'Button', props: { c: 1 } }, firstBtn);
|
||||
// expect(btnParent.children.get(0)?.getProps().export().props).toEqual({ c: 1 });
|
||||
// expect(mockFn).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('insertAfter / onChildrenChange', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const secondBtn = doc.getNode('node_k1ow3cbp')!;
|
||||
const btnParent = firstBtn.parent!;
|
||||
const mockFn = jest.fn();
|
||||
const off = btnParent.onChildrenChange(mockFn);
|
||||
|
||||
// Node 实例
|
||||
btnParent.insertAfter(new Node(doc, { componentName: 'Button', props: { a: 1 } }), firstBtn);
|
||||
expect(btnParent.children.get(1)?.getProps().export().props).toEqual({ a: 1 });
|
||||
expect(mockFn).toHaveBeenCalledTimes(1);
|
||||
|
||||
// NodeSchema
|
||||
btnParent.insertAfter({ componentName: 'Button', props: { b: 1 } }, firstBtn);
|
||||
expect(btnParent.children.get(1)?.getProps().export().props).toEqual({ b: 1 });
|
||||
expect(mockFn).toHaveBeenCalledTimes(2);
|
||||
|
||||
// getComponentName
|
||||
btnParent.insertAfter({ getComponentName: () => 'Button' }, firstBtn);
|
||||
expect(btnParent.children.get(1)?.getProps().export().props).toEqual({});
|
||||
expect(mockFn).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
});
|
||||
|
||||
it('setVisible / getVisible / onVisibleChange', () => {
|
||||
const mockFn = jest.fn();
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const off = firstBtn.onVisibleChange(mockFn);
|
||||
firstBtn.setVisible(true);
|
||||
expect(firstBtn.getVisible()).toBeTruthy();
|
||||
expect(mockFn).toHaveBeenCalledTimes(1);
|
||||
expect(mockFn).toHaveBeenCalledWith(true);
|
||||
|
||||
firstBtn.setVisible(false);
|
||||
|
||||
expect(firstBtn.getVisible()).toBeFalsy();
|
||||
expect(mockFn).toHaveBeenCalledTimes(2);
|
||||
expect(mockFn).toHaveBeenCalledWith(false);
|
||||
|
||||
off();
|
||||
mockFn.mockClear();
|
||||
firstBtn.setVisible(true);
|
||||
expect(mockFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('RGL / getRGL', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
firstBtn.isRGLContainer = true;
|
||||
expect(firstBtn.isRGLContainer).toBeTruthy();
|
||||
|
||||
const rgl = firstBtn.getRGL();
|
||||
expect(rgl.isContainerNode).toBeFalsy();
|
||||
expect(rgl.isEmptyNode).toBeTruthy();
|
||||
expect(rgl.isRGLContainerNode).toBeTruthy();
|
||||
expect(rgl.isRGLNode).toBeFalsy();
|
||||
expect(rgl.isRGL).toBeTruthy();
|
||||
});
|
||||
|
||||
it('onPropChange', () => {
|
||||
const mockFn = jest.fn();
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const off = firstBtn.onPropChange(mockFn);
|
||||
|
||||
firstBtn.setPropValue('x', 1);
|
||||
expect(mockFn).toHaveBeenCalledTimes(1);
|
||||
firstBtn.setPropValue('x', 2);
|
||||
expect(mockFn).toHaveBeenCalledTimes(2);
|
||||
|
||||
off();
|
||||
mockFn.mockClear();
|
||||
firstBtn.setPropValue('x', 3);
|
||||
expect(mockFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('addSlot / unlinkSlot / removeSlot', () => { });
|
||||
|
||||
it('setProps', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const secondBtn = doc.getNode('node_k1ow3cbp')!;
|
||||
|
||||
firstBtn.setProps(secondBtn.getProps());
|
||||
expect(firstBtn.getProps()).toBe(secondBtn.getProps());
|
||||
});
|
||||
|
||||
it('advanced initials / autoruns', async () => {
|
||||
designer.createComponentMeta(pageMetadata);
|
||||
|
||||
const pageMeta = designer.getComponentMeta('Page');
|
||||
const autorunMockFn = jest.fn();
|
||||
set(pageMeta, '_transformedMetadata.configure.advanced.autoruns', [
|
||||
{ name: 'a', autorun: autorunMockFn },
|
||||
]);
|
||||
const initialChildrenMockFn = jest.fn();
|
||||
set(pageMeta, '_transformedMetadata.configure.advanced.initialChildren', initialChildrenMockFn);
|
||||
doc.createNode({ componentName: 'Page', props: { a: 1 } });
|
||||
|
||||
expect(autorunMockFn).toHaveBeenCalled();
|
||||
expect(initialChildrenMockFn).toHaveBeenCalled();
|
||||
|
||||
set(pageMeta, '_transformedMetadata.configure.advanced.initialChildren', {});
|
||||
doc.createNode({ componentName: 'Page', props: { a: 1 } });
|
||||
expect(autorunMockFn).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('isValidComponent', () => {
|
||||
designer.createComponentMeta(divMetadata);
|
||||
expect(doc.getNode('node_k1ow3cbo')?.isValidComponent()).toBeTruthy();
|
||||
expect(doc.getNode('form')?.isValidComponent()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('title', () => {
|
||||
designer.createComponentMeta(btnMetadata);
|
||||
const btn = doc.getNode('node_k1ow3cbn');
|
||||
// 从 componentMeta 中获取到 title 值
|
||||
expect(btn.title).toEqual({ type: 'i18n', 'zh-CN': '按钮', 'en-US': 'Button' });
|
||||
// 从 extraProp 中获取值
|
||||
btn.setExtraProp('title', 'hello button');
|
||||
expect(btn.title).toBe('hello button');
|
||||
|
||||
// btn.props.deleteKey('___title___');
|
||||
// 从 componentMeta descriptor 指向的 key 获取 title
|
||||
// btn.setPropValue('xTitle', 'title from descriptor')
|
||||
// expect(btn.title).toBe('title from descriptor');
|
||||
});
|
||||
|
||||
it('isEmpty / getIndex / getIcon', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
// expect(firstBtn.children).toBeNull();
|
||||
expect(firstBtn.isEmpty()).toBeTruthy();
|
||||
expect(firstBtn.index).toBe(0);
|
||||
expect(firstBtn.getIndex()).toBe(0);
|
||||
expect(typeof firstBtn.getIcon()).toBe('function');
|
||||
expect(doc.getNode('page')!.index).toBe(-1);
|
||||
});
|
||||
|
||||
it('schema / toData / export', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
expect(firstBtn.toData().componentName).toBe('Button');
|
||||
});
|
||||
|
||||
it('internalSetParent / internalSetWillPurge', () => {
|
||||
const firstChild = doc.rootNode?.getChildren()?.get(0);
|
||||
firstChild?.internalSetParent(doc.rootNode);
|
||||
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
firstBtn.internalSetWillPurge();
|
||||
// expect(firstBtn.parent).();
|
||||
|
||||
expect(firstBtn.hasSlots()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('prevSibling / nextSibling', () => {
|
||||
// no parent
|
||||
const page = doc.getNode('page');
|
||||
expect(page?.nextSibling).toBeNull();
|
||||
expect(page?.prevSibling).toBeNull();
|
||||
|
||||
// normal
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn');
|
||||
const secondBtn = doc.getNode('node_k1ow3cbp');
|
||||
expect(firstBtn?.nextSibling).toBe(secondBtn);
|
||||
expect(secondBtn?.prevSibling).toBe(firstBtn);
|
||||
expect(secondBtn?.nextSibling).toBeNull();
|
||||
|
||||
// index < 0
|
||||
firstBtn?.parent?.removeChild(firstBtn);
|
||||
expect(firstBtn?.nextSibling).toBeNull();
|
||||
expect(firstBtn?.prevSibling).toBeNull();
|
||||
});
|
||||
|
||||
it('toString', () => {
|
||||
expect(doc.rootNode.toString()).toBe('page');
|
||||
});
|
||||
|
||||
it('lock', () => {
|
||||
const form = doc.getNode('node_k1ow3cbo');
|
||||
expect(form.isLocked).toBeFalsy();
|
||||
form.lock(true);
|
||||
expect(form.isLocked).toBeTruthy();
|
||||
form.lock(false);
|
||||
expect(form.isLocked).toBeFalsy();
|
||||
form.lock();
|
||||
expect(form.isLocked).toBeTruthy();
|
||||
});
|
||||
|
||||
it('didDropIn / didDropOut', () => {
|
||||
const form = doc.getNode('node_k1ow3cbo');
|
||||
designer.createComponentMeta(divMetadata);
|
||||
designer.createComponentMeta(formMetadata);
|
||||
const callbacks = form.componentMeta.advanced.callbacks;
|
||||
const fn1 = callbacks.onNodeAdd = jest.fn();
|
||||
const fn2 = callbacks.onNodeRemove = jest.fn();
|
||||
const textField = doc.getNode('node_k1ow3cc9');
|
||||
form.didDropIn(textField);
|
||||
expect(fn1).toHaveBeenCalledWith(textField.internalToShellNode(), form.internalToShellNode());
|
||||
|
||||
form.didDropOut(textField);
|
||||
expect(fn2).toHaveBeenCalledWith(textField.internalToShellNode(), form.internalToShellNode());
|
||||
});
|
||||
|
||||
it('hover', () => {
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
firstBtn.hover(true);
|
||||
expect(doc.designer.detecting.current).toBe(firstBtn);
|
||||
firstBtn.hover(false);
|
||||
expect(doc.designer.detecting.current).toBeNull();
|
||||
firstBtn.hover();
|
||||
expect(doc.designer.detecting.current).toBe(firstBtn);
|
||||
});
|
||||
|
||||
it('getRect', () => {
|
||||
const root = doc.rootNode!;
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
expect(root.getRect()).toBeNull();
|
||||
expect(firstBtn.getRect()).toBeNull();
|
||||
|
||||
doc.project.mountSimulator({
|
||||
computeRect: () => ({ x: 2, y: 2 }),
|
||||
viewport: {
|
||||
contentBounds: { x: 1, y: 1 },
|
||||
},
|
||||
});
|
||||
|
||||
expect(root.getRect()).toEqual({ x: 1, y: 1 });
|
||||
expect(firstBtn.getRect()).toEqual({ x: 2, y: 2 });
|
||||
});
|
||||
|
||||
it('isRootNode / isRoot / isNode', () => {
|
||||
expect(isRootNode(doc.rootNode)).toBeTruthy();
|
||||
expect(isNode(doc.rootNode)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('contains / comparePosition', () => {
|
||||
const page = doc.getNode('page')!;
|
||||
const content = doc.getNode('node_k1ow3cbb')!;
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
const secondBtn = doc.getNode('node_k1ow3cbp')!;
|
||||
const firstCard = doc.getNode('node_k1ow3cbj')!;
|
||||
expect(contains(firstBtn, firstBtn)).toBeTruthy();
|
||||
expect(contains(firstBtn, secondBtn)).toBeFalsy();
|
||||
expect(contains(firstBtn, page)).toBeFalsy();
|
||||
expect(contains(firstBtn, content)).toBeFalsy();
|
||||
expect(contains(firstCard, firstBtn)).toBeFalsy();
|
||||
|
||||
expect(comparePosition(firstBtn, secondBtn)).toBe(PositionNO.BeforeOrAfter);
|
||||
expect(firstBtn.comparePosition(firstBtn)).toBe(PositionNO.TheSame);
|
||||
expect(comparePosition(firstBtn, firstBtn)).toBe(PositionNO.TheSame);
|
||||
expect(comparePosition(firstBtn, firstBtn.parent)).toBe(PositionNO.ContainedBy);
|
||||
expect(comparePosition(firstBtn.parent, firstBtn)).toBe(PositionNO.Contains);
|
||||
expect(comparePosition(firstCard, firstBtn)).toBe(PositionNO.BeforeOrAfter);
|
||||
expect(comparePosition(firstBtn, firstCard)).toBe(PositionNO.BeforeOrAfter);
|
||||
});
|
||||
|
||||
it('getZLevelTop', () => { });
|
||||
it('propsData', () => {
|
||||
expect(new Node(doc, { componentName: 'Leaf' }).propsData).toBeNull();
|
||||
expect(new Node(doc, { componentName: 'Fragment' }).propsData).toBeNull();
|
||||
});
|
||||
|
||||
describe('deprecated methods', () => {
|
||||
it('setStatus / getStatus', () => {
|
||||
const root = doc.rootNode!;
|
||||
root.setStatus('xxx', true);
|
||||
|
||||
root.setStatus('locking', true);
|
||||
root.setStatus('pseudo', true);
|
||||
root.setStatus('inPlaceEditing', true);
|
||||
|
||||
expect(root.getStatus('locking')).toBeTruthy();
|
||||
expect(root.getStatus('pseudo')).toBeTruthy();
|
||||
expect(root.getStatus('inPlaceEditing')).toBeTruthy();
|
||||
expect(root.getStatus()).toEqual({
|
||||
locking: true,
|
||||
pseudo: true,
|
||||
inPlaceEditing: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('getPage', () => {
|
||||
expect(doc.rootNode?.getPage()).toBe(doc);
|
||||
});
|
||||
|
||||
it('getDOMNode', () => {
|
||||
const root = doc.rootNode!;
|
||||
const firstBtn = doc.getNode('node_k1ow3cbn')!;
|
||||
|
||||
doc.project.mountSimulator({
|
||||
findDOMNodes: () => [{ x: 1, y: 1 }],
|
||||
getComponentInstances: (node) => {
|
||||
if (node.componentName === 'Page') {
|
||||
return [];
|
||||
}
|
||||
return [{}];
|
||||
},
|
||||
});
|
||||
|
||||
expect(root.getDOMNode()).toBeUndefined();
|
||||
expect(firstBtn.getDOMNode()).toEqual({ x: 1, y: 1 });
|
||||
});
|
||||
|
||||
it('registerAddon / getAddonData', () => {
|
||||
const page = doc.getNode('page')!;
|
||||
page.registerAddon('a', () => 'prop a');
|
||||
expect(page.getAddonData('a')).toBe('prop a');
|
||||
expect(page.getAddonData('b')).toBeUndefined();
|
||||
|
||||
expect(page.export().a).toBe('prop a');
|
||||
});
|
||||
|
||||
it('getPrototype / setPrototype', () => {
|
||||
const page = doc.getNode('page')!;
|
||||
page.setPrototype({ a: 1 });
|
||||
expect(page.getPrototype()).toEqual({ a: 1 });
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,37 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`valueToSource 1`] = `"1"`;
|
||||
|
||||
exports[`valueToSource 2`] = `"true"`;
|
||||
|
||||
exports[`valueToSource 3`] = `"[]"`;
|
||||
|
||||
exports[`valueToSource 4`] = `
|
||||
"[{
|
||||
\\"a\\": 1
|
||||
}]"
|
||||
`;
|
||||
|
||||
exports[`valueToSource 5`] = `
|
||||
"{
|
||||
\\"a\\": 1
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`valueToSource 6`] = `"null"`;
|
||||
|
||||
exports[`valueToSource 7`] = `"() => {}"`;
|
||||
|
||||
exports[`valueToSource 8`] = `"new Map()"`;
|
||||
|
||||
exports[`valueToSource 9`] = `"new Set()"`;
|
||||
|
||||
exports[`valueToSource 10`] = `"/haha/"`;
|
||||
|
||||
exports[`valueToSource 11`] = `"\\"hahah\\""`;
|
||||
|
||||
exports[`valueToSource 12`] = `"Symbol(\\"haha\\")"`;
|
||||
|
||||
exports[`valueToSource 13`] = `"undefined"`;
|
||||
|
||||
exports[`valueToSource 14`] = `"new Date(\\"2020-12-11T10:03:18.520Z\\")"`;
|
||||
@ -1,697 +0,0 @@
|
||||
import '../../../fixtures/window';
|
||||
import { Editor, engineConfig } from '@alilc/lowcode-editor-core';
|
||||
import { Designer } from '../../../../src/designer/designer';
|
||||
import { DocumentModel } from '../../../../src/document/document-model';
|
||||
import { Prop, isProp, isValidArrayIndex } from '../../../../src/document/node/props/prop';
|
||||
import { GlobalEvent, IPublicEnumTransformStage } from '@alilc/lowcode-types';
|
||||
import { shellModelFactory } from '../../../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
const slotNodeImportMockFn = jest.fn();
|
||||
const slotNodeRemoveMockFn = jest.fn();
|
||||
const mockOwner = {
|
||||
componentName: 'Div',
|
||||
addSlot() {},
|
||||
document: {
|
||||
createNode(schema) {
|
||||
return {
|
||||
...schema,
|
||||
addSlot() {},
|
||||
internalSetSlotFor() {},
|
||||
import: slotNodeImportMockFn,
|
||||
export() {
|
||||
return schema;
|
||||
},
|
||||
remove: slotNodeRemoveMockFn,
|
||||
};
|
||||
},
|
||||
designer: {
|
||||
editor: {
|
||||
eventBus: {
|
||||
emit: jest.fn(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
isInited: true,
|
||||
emitPropChange: jest.fn(),
|
||||
delete() {},
|
||||
};
|
||||
|
||||
const mockPropsInst = {
|
||||
owner: mockOwner,
|
||||
delete() {},
|
||||
};
|
||||
|
||||
mockPropsInst.props = mockPropsInst;
|
||||
|
||||
describe('Prop 类测试', () => {
|
||||
describe('基础类型', () => {
|
||||
let boolProp: Prop;
|
||||
let strProp: Prop;
|
||||
let numProp: Prop;
|
||||
let nullProp: Prop;
|
||||
let expProp: Prop;
|
||||
let slotProp: Prop;
|
||||
beforeEach(() => {
|
||||
boolProp = new Prop(mockPropsInst, true, 'boolProp');
|
||||
strProp = new Prop(mockPropsInst, 'haha', 'strProp');
|
||||
numProp = new Prop(mockPropsInst, 1, 'numProp');
|
||||
nullProp = new Prop(mockPropsInst, null, 'nullProp');
|
||||
expProp = new Prop(mockPropsInst, { type: 'JSExpression', value: 'state.haha' }, 'expProp');
|
||||
slotProp = new Prop(
|
||||
mockPropsInst,
|
||||
{
|
||||
type: 'JSSlot',
|
||||
title: '测试 slot',
|
||||
name: 'testSlot',
|
||||
params: { a: 1 },
|
||||
value: [{ componentName: 'Button' }],
|
||||
},
|
||||
'slotProp',
|
||||
);
|
||||
slotNodeImportMockFn.mockClear();
|
||||
slotNodeRemoveMockFn.mockClear();
|
||||
});
|
||||
afterEach(() => {
|
||||
boolProp.purge();
|
||||
strProp.purge();
|
||||
numProp.purge();
|
||||
nullProp.purge();
|
||||
expProp.purge();
|
||||
slotProp.purge();
|
||||
});
|
||||
|
||||
it('consturctor / getProps / getNode', () => {
|
||||
expect(boolProp.parent).toBe(mockPropsInst);
|
||||
expect(boolProp.getProps()).toBe(mockPropsInst);
|
||||
expect(boolProp.getNode()).toBe(mockOwner);
|
||||
});
|
||||
|
||||
it('misc', () => {
|
||||
expect(boolProp.get('x', false)).toBeNull();
|
||||
expect(boolProp.maps).toBeNull();
|
||||
expect(boolProp.add()).toBeNull();
|
||||
|
||||
strProp.unset();
|
||||
strProp.add(2, true);
|
||||
strProp.set(0);
|
||||
|
||||
expect(numProp.set()).toBeNull();
|
||||
expect(numProp.has()).toBeFalsy();
|
||||
expect(numProp.path).toEqual(['numProp']);
|
||||
});
|
||||
|
||||
it('getValue / getAsString / setValue', () => {
|
||||
expect(strProp.getValue()).toBe('haha');
|
||||
strProp.setValue('heihei');
|
||||
strProp.setValue('heihei');
|
||||
expect(strProp.getValue()).toBe('heihei');
|
||||
expect(strProp.getAsString()).toBe('heihei');
|
||||
|
||||
strProp.unset();
|
||||
expect(strProp.getValue()).toBeUndefined();
|
||||
});
|
||||
|
||||
it('code', () => {
|
||||
expect(expProp.code).toBe('state.haha');
|
||||
expect(boolProp.code).toBe('true');
|
||||
expect(strProp.code).toBe('"haha"');
|
||||
|
||||
expProp.code = 'state.heihei';
|
||||
expect(expProp.code).toBe('state.heihei');
|
||||
expect(expProp.getValue()).toEqual({
|
||||
type: 'JSExpression',
|
||||
value: 'state.heihei',
|
||||
});
|
||||
|
||||
boolProp.code = 'false';
|
||||
expect(boolProp.code).toBe('false');
|
||||
expect(boolProp.getValue()).toBe(false);
|
||||
|
||||
strProp.code = '"heihei"';
|
||||
expect(strProp.code).toBe('"heihei"');
|
||||
expect(strProp.getValue()).toBe('heihei');
|
||||
|
||||
// TODO: 不确定为什么会有这个分支
|
||||
strProp.code = 'state.a';
|
||||
expect(strProp.code).toBe('state.a');
|
||||
expect(strProp.getValue()).toEqual({
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
mock: 'heihei',
|
||||
});
|
||||
});
|
||||
|
||||
it('export', () => {
|
||||
expect(boolProp.export(IPublicEnumTransformStage.Save)).toBe(true);
|
||||
expect(strProp.export(IPublicEnumTransformStage.Save)).toBe('haha');
|
||||
expect(numProp.export(IPublicEnumTransformStage.Save)).toBe(1);
|
||||
expect(nullProp.export(IPublicEnumTransformStage.Save)).toBe(null);
|
||||
expect(nullProp.export(IPublicEnumTransformStage.Serilize)).toBe(null);
|
||||
expect(expProp.export(IPublicEnumTransformStage.Save)).toEqual({
|
||||
type: 'JSExpression',
|
||||
value: 'state.haha',
|
||||
});
|
||||
|
||||
strProp.unset();
|
||||
expect(strProp.getValue()).toBeUndefined();
|
||||
expect(strProp.isUnset()).toBeTruthy();
|
||||
expect(strProp.export(IPublicEnumTransformStage.Save)).toBeUndefined();
|
||||
|
||||
expect(
|
||||
new Prop(mockPropsInst, false, '___condition___').export(IPublicEnumTransformStage.Render),
|
||||
).toBeTruthy();
|
||||
engineConfig.set('enableCondition', true);
|
||||
expect(
|
||||
new Prop(mockPropsInst, false, '___condition___').export(IPublicEnumTransformStage.Render),
|
||||
).toBeFalsy();
|
||||
expect(slotProp.export(IPublicEnumTransformStage.Render)).toEqual({
|
||||
type: 'JSSlot',
|
||||
params: { a: 1 },
|
||||
value: {
|
||||
componentName: 'Slot',
|
||||
title: '测试 slot',
|
||||
name: 'testSlot',
|
||||
params: { a: 1 },
|
||||
children: [{ componentName: 'Button' }],
|
||||
},
|
||||
});
|
||||
expect(slotProp.export(IPublicEnumTransformStage.Save)).toEqual({
|
||||
type: 'JSSlot',
|
||||
params: { a: 1 },
|
||||
value: [{ componentName: 'Button' }],
|
||||
title: '测试 slot',
|
||||
name: 'testSlot',
|
||||
});
|
||||
});
|
||||
|
||||
it('compare', () => {
|
||||
const newProp = new Prop(mockPropsInst, 'haha');
|
||||
const newProp2 = new Prop(mockPropsInst, { a: 1 });
|
||||
expect(strProp.compare(newProp)).toBe(0);
|
||||
expect(strProp.compare(expProp)).toBe(2);
|
||||
|
||||
newProp.unset();
|
||||
expect(strProp.compare(newProp)).toBe(2);
|
||||
strProp.unset();
|
||||
expect(strProp.compare(newProp)).toBe(0);
|
||||
expect(strProp.compare(newProp2)).toBe(2);
|
||||
});
|
||||
|
||||
it('isVirtual', () => {
|
||||
expect(new Prop(mockPropsInst, 111, '!virtualProp')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('purge', () => {
|
||||
boolProp.purge();
|
||||
expect(boolProp.purged).toBeTruthy();
|
||||
boolProp.purge();
|
||||
});
|
||||
|
||||
it('slot', () => {
|
||||
// 更新 slot
|
||||
slotProp.setValue({
|
||||
type: 'JSSlot',
|
||||
value: [
|
||||
{
|
||||
componentName: 'Form',
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(slotNodeImportMockFn).toBeCalled();
|
||||
|
||||
// 节点类型转换
|
||||
slotProp.setValue(true);
|
||||
expect(slotNodeRemoveMockFn).toBeCalled();
|
||||
});
|
||||
|
||||
it('迭代器 / map / forEach', () => {
|
||||
const mockFn = jest.fn();
|
||||
for (const item of strProp) {
|
||||
mockFn();
|
||||
}
|
||||
expect(mockFn).not.toHaveBeenCalled();
|
||||
mockFn.mockClear();
|
||||
|
||||
strProp.forEach((item) => {
|
||||
mockFn();
|
||||
});
|
||||
expect(mockFn).not.toHaveBeenCalled();
|
||||
mockFn.mockClear();
|
||||
|
||||
strProp.map((item) => {
|
||||
return mockFn();
|
||||
});
|
||||
expect(mockFn).not.toHaveBeenCalled();
|
||||
mockFn.mockClear();
|
||||
});
|
||||
});
|
||||
|
||||
describe('复杂类型', () => {
|
||||
describe('items(map 类型)', () => {
|
||||
let prop: Prop;
|
||||
beforeEach(() => {
|
||||
prop = new Prop(mockPropsInst, {
|
||||
a: 1,
|
||||
b: 'str',
|
||||
c: true,
|
||||
d: {
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
},
|
||||
emptyArr: [],
|
||||
emptyObj: {},
|
||||
z: {
|
||||
z1: 1,
|
||||
z2: 'str',
|
||||
},
|
||||
});
|
||||
});
|
||||
afterEach(() => {
|
||||
prop.purge();
|
||||
});
|
||||
|
||||
it('items / get', async () => {
|
||||
expect(prop.size).toBe(7);
|
||||
|
||||
expect(prop.get('a').getValue()).toBe(1);
|
||||
expect(prop.get('b').getValue()).toBe('str');
|
||||
expect(prop.get('c').getValue()).toBe(true);
|
||||
expect(prop.get('d').getValue()).toEqual({ type: 'JSExpression', value: 'state.a' });
|
||||
expect(prop.get('z').getValue()).toEqual({
|
||||
z1: 1,
|
||||
z2: 'str',
|
||||
});
|
||||
|
||||
expect(prop.getPropValue('a')).toBe(1);
|
||||
prop.setPropValue('a', 2);
|
||||
expect(prop.getPropValue('a')).toBe(2);
|
||||
prop.clearPropValue('a');
|
||||
expect(prop.get('a')?.isUnset()).toBeTruthy();
|
||||
|
||||
expect(prop.get('z.z1')?.getValue()).toBe(1);
|
||||
expect(prop.get('z.z2')?.getValue()).toBe('str');
|
||||
|
||||
const newlyCreatedProp = prop.get('l', true);
|
||||
const newlyCreatedNestedProp = prop.get('m.m1', true);
|
||||
newlyCreatedProp.setValue('newlyCreatedProp');
|
||||
newlyCreatedNestedProp?.setValue('newlyCreatedNestedProp');
|
||||
|
||||
expect(prop.get('l').getValue()).toBe('newlyCreatedProp');
|
||||
expect(prop.get('m.m1').getValue()).toBe('newlyCreatedNestedProp');
|
||||
|
||||
const newlyCreatedNestedProp2 = prop.get('m.m2', true);
|
||||
// .m2 的值为 undefined,导出时将会被移除
|
||||
expect(prop.get('m').getValue()).toEqual({ m1: 'newlyCreatedNestedProp' });
|
||||
|
||||
// 对于空值的 list / map 类型,_items 应该为 null
|
||||
expect(prop.get('emptyArr')._items).toBeNull();
|
||||
expect(prop.get('emptyObj')._items).toBeNull();
|
||||
});
|
||||
|
||||
it('export', () => {
|
||||
expect(prop.export()).toEqual({
|
||||
a: 1,
|
||||
b: 'str',
|
||||
c: true,
|
||||
d: {
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
},
|
||||
emptyArr: [],
|
||||
emptyObj: {},
|
||||
z: {
|
||||
z1: 1,
|
||||
z2: 'str',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('compare', () => {
|
||||
const prop1 = new Prop(mockPropsInst, { a: 1 });
|
||||
const prop2 = new Prop(mockPropsInst, { b: 1 });
|
||||
expect(prop1.compare(prop2)).toBe(1);
|
||||
});
|
||||
|
||||
it('has / add / delete / deleteKey / remove', () => {
|
||||
expect(prop.has('a')).toBeTruthy();
|
||||
expect(prop.has('b')).toBeTruthy();
|
||||
expect(prop.has('c')).toBeTruthy();
|
||||
expect(prop.has('d')).toBeTruthy();
|
||||
expect(prop.has('z')).toBeTruthy();
|
||||
expect(prop.has('y')).toBeFalsy();
|
||||
|
||||
// 触发一下内部 maps 构造
|
||||
prop.items;
|
||||
expect(prop.has('z')).toBeTruthy();
|
||||
|
||||
expect(prop.add(1)).toBeNull();
|
||||
|
||||
prop.deleteKey('c');
|
||||
expect(prop.get('c', false)).toBeNull();
|
||||
prop.delete(prop.get('b'));
|
||||
expect(prop.get('b', false)).toBeNull();
|
||||
|
||||
prop.get('d')?.remove();
|
||||
expect(prop.get('d', false)).toBeNull();
|
||||
});
|
||||
|
||||
it('set', () => {
|
||||
prop.set('e', 1);
|
||||
expect(prop.get('e', false)?.getValue()).toBe(1);
|
||||
prop.set('a', 5);
|
||||
expect(prop.get('a', false)?.getValue()).toBe(5);
|
||||
});
|
||||
|
||||
it('迭代器 / map / forEach', () => {
|
||||
const mockFn = jest.fn();
|
||||
for (const item of prop) {
|
||||
mockFn();
|
||||
}
|
||||
expect(mockFn).toHaveBeenCalledTimes(7);
|
||||
mockFn.mockClear();
|
||||
|
||||
prop.forEach((item) => {
|
||||
mockFn();
|
||||
});
|
||||
expect(mockFn).toHaveBeenCalledTimes(7);
|
||||
mockFn.mockClear();
|
||||
|
||||
prop.map((item) => {
|
||||
return mockFn();
|
||||
});
|
||||
expect(mockFn).toHaveBeenCalledTimes(7);
|
||||
mockFn.mockClear();
|
||||
});
|
||||
|
||||
it('dispose', () => {
|
||||
prop.items;
|
||||
prop.dispose();
|
||||
|
||||
expect(prop._items).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('items(list 类型)', () => {
|
||||
let prop: Prop;
|
||||
beforeEach(() => {
|
||||
prop = new Prop(mockPropsInst, [1, true, 'haha']);
|
||||
});
|
||||
afterEach(() => {
|
||||
prop.purge();
|
||||
});
|
||||
|
||||
it('items / get', () => {
|
||||
expect(prop.size).toBe(3);
|
||||
|
||||
expect(prop.get(0).getValue()).toBe(1);
|
||||
expect(prop.get(1).getValue()).toBe(true);
|
||||
expect(prop.get(2).getValue()).toBe('haha');
|
||||
|
||||
expect(prop.getAsString()).toBe('');
|
||||
|
||||
prop.unset();
|
||||
prop.set(0, true);
|
||||
expect(prop.set('x', 'invalid')).toBeNull();
|
||||
expect(prop.get(0).getValue()).toBeTruthy();
|
||||
|
||||
// map / list 级联测试
|
||||
prop.get('loopArgs.0', true).setValue('newItem');;
|
||||
expect(prop.get('loopArgs.0').getValue()).toBe('newItem');
|
||||
});
|
||||
|
||||
it('export', () => {
|
||||
expect(prop.export()).toEqual([1, true, 'haha']);
|
||||
// 触发构造
|
||||
prop.items;
|
||||
expect(prop.export()).toEqual([1, true, 'haha']);
|
||||
});
|
||||
|
||||
it('compare', () => {
|
||||
const prop1 = new Prop(mockPropsInst, [1]);
|
||||
const prop2 = new Prop(mockPropsInst, [2]);
|
||||
const prop3 = new Prop(mockPropsInst, [1, 2]);
|
||||
expect(prop1.compare(prop2)).toBe(1);
|
||||
expect(prop1.compare(prop3)).toBe(2);
|
||||
});
|
||||
|
||||
it('set', () => {
|
||||
prop.set(0, 1);
|
||||
expect(prop.get(0, false)?.getValue()).toBe(1);
|
||||
// illegal
|
||||
// expect(prop.set(5, 1)).toBeNull();
|
||||
});
|
||||
|
||||
it('should return undefined when all items are undefined', () => {
|
||||
prop = new Prop(mockPropsInst, [undefined, undefined], '___loopArgs___');
|
||||
expect(prop.getValue()).toEqual([undefined, undefined]);
|
||||
});
|
||||
|
||||
it('迭代器 / map / forEach', () => {
|
||||
const listProp = new Prop(mockPropsInst, [1, 2]);
|
||||
const mockFn = jest.fn();
|
||||
for (const item of listProp) {
|
||||
mockFn();
|
||||
}
|
||||
expect(mockFn).toHaveBeenCalledTimes(2);
|
||||
mockFn.mockClear();
|
||||
|
||||
listProp.forEach((item) => {
|
||||
mockFn();
|
||||
});
|
||||
expect(mockFn).toHaveBeenCalledTimes(2);
|
||||
mockFn.mockClear();
|
||||
|
||||
listProp.map((item) => {
|
||||
return mockFn();
|
||||
});
|
||||
expect(mockFn).toHaveBeenCalledTimes(2);
|
||||
mockFn.mockClear();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('slotNode / setAsSlot', () => {
|
||||
const editor = new Editor();
|
||||
const designer = new Designer({ editor, shellModelFactory });
|
||||
const doc = new DocumentModel(designer.project, {
|
||||
componentName: 'Page',
|
||||
children: [
|
||||
{
|
||||
id: 'div',
|
||||
componentName: 'Div',
|
||||
},
|
||||
],
|
||||
});
|
||||
const div = doc.getNode('div');
|
||||
|
||||
const slotProp = new Prop(div?.getProps(), {
|
||||
type: 'JSSlot',
|
||||
value: [
|
||||
{
|
||||
componentName: 'Button',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(slotProp.slotNode?.componentName).toBe('Slot');
|
||||
|
||||
// TODO: id 总是变,不好断言
|
||||
expect(slotProp.code.includes('Button')).toBeTruthy();
|
||||
|
||||
slotProp.export();
|
||||
|
||||
expect(slotProp.export().value[0].componentName).toBe('Button');
|
||||
expect(slotProp.export(IPublicEnumTransformStage.Serilize).value[0].componentName).toBe('Button');
|
||||
|
||||
slotProp.purge();
|
||||
expect(slotProp.purged).toBeTruthy();
|
||||
slotProp.dispose();
|
||||
});
|
||||
|
||||
describe('slotNode-value / setAsSlot', () => {
|
||||
const editor = new Editor();
|
||||
const designer = new Designer({ editor, shellModelFactory });
|
||||
const doc = new DocumentModel(designer.project, {
|
||||
componentName: 'Page',
|
||||
children: [
|
||||
{
|
||||
id: 'div',
|
||||
componentName: 'Div',
|
||||
},
|
||||
],
|
||||
});
|
||||
const div = doc.getNode('div');
|
||||
|
||||
const slotProp = new Prop(div?.getProps(), {
|
||||
type: 'JSSlot',
|
||||
value: {
|
||||
componentName: 'Slot',
|
||||
id: 'node_oclei5rv2e2',
|
||||
props: {
|
||||
slotName: "content",
|
||||
slotTitle: "主内容"
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Button',
|
||||
}
|
||||
]
|
||||
},
|
||||
});
|
||||
|
||||
expect(slotProp.slotNode?.componentName).toBe('Slot');
|
||||
|
||||
expect(slotProp.slotNode?.title).toBe('主内容');
|
||||
expect(slotProp.slotNode?.getExtraProp('name')?.getValue()).toBe('content');
|
||||
expect(slotProp.slotNode?.export()?.id).toBe('node_oclei5rv2e2');
|
||||
|
||||
slotProp.export();
|
||||
|
||||
// Save
|
||||
expect(slotProp.export()?.value[0].componentName).toBe('Button');
|
||||
expect(slotProp.export()?.title).toBe('主内容');
|
||||
expect(slotProp.export()?.name).toBe('content');
|
||||
|
||||
// Render
|
||||
expect(slotProp.export(IPublicEnumTransformStage.Render)?.value.children[0].componentName).toBe('Button');
|
||||
expect(slotProp.export(IPublicEnumTransformStage.Render)?.value.componentName).toBe('Slot');
|
||||
|
||||
slotProp.purge();
|
||||
expect(slotProp.purged).toBeTruthy();
|
||||
slotProp.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
describe('其他导出函数', () => {
|
||||
it('isProp', () => {
|
||||
expect(isProp({ isProp: true })).toBeTruthy();
|
||||
});
|
||||
|
||||
it('isValidArrayIndex', () => {
|
||||
expect(isValidArrayIndex('1')).toBeTruthy();
|
||||
expect(isValidArrayIndex('1', 2)).toBeTruthy();
|
||||
expect(isValidArrayIndex('2', 1)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('setValue with event', () => {
|
||||
let propInstance;
|
||||
let mockEmitChange;
|
||||
let mockEventBusEmit;
|
||||
let mockEmitPropChange;
|
||||
|
||||
beforeEach(() => {
|
||||
// Initialize the instance of your class
|
||||
propInstance = new Prop(mockPropsInst, true, 'stringProp');;
|
||||
|
||||
// Mock necessary methods and properties
|
||||
mockEmitChange = jest.spyOn(propInstance, 'emitChange');
|
||||
propInstance.owner = {
|
||||
document: {
|
||||
designer: {
|
||||
editor: {
|
||||
eventBus: {
|
||||
emit: jest.fn(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
emitPropChange: jest.fn(),
|
||||
delete() {},
|
||||
};
|
||||
mockEventBusEmit = jest.spyOn(propInstance.owner.document.designer.editor.eventBus, 'emit');
|
||||
mockEmitPropChange = jest.spyOn(propInstance.owner, 'emitPropChange');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should correctly handle string values and emit changes', () => {
|
||||
const oldValue = propInstance._value;
|
||||
const newValue = 'new string value';
|
||||
|
||||
propInstance.setValue(newValue);
|
||||
|
||||
const expectedPartialPropsInfo = expect.objectContaining({
|
||||
key: propInstance.key,
|
||||
newValue, // You can specifically test only certain keys
|
||||
oldValue,
|
||||
});
|
||||
|
||||
expect(propInstance.getValue()).toBe(newValue);
|
||||
expect(propInstance.type).toBe('literal');
|
||||
expect(mockEmitChange).toHaveBeenCalledWith({ oldValue });
|
||||
expect(mockEventBusEmit).toHaveBeenCalledWith(GlobalEvent.Node.Prop.InnerChange, expectedPartialPropsInfo);
|
||||
expect(mockEmitPropChange).toHaveBeenCalledWith(expectedPartialPropsInfo);
|
||||
});
|
||||
|
||||
it('should handle object values and set type to map', () => {
|
||||
const oldValue = propInstance._value;
|
||||
const newValue = 234;
|
||||
|
||||
const expectedPartialPropsInfo = expect.objectContaining({
|
||||
key: propInstance.key,
|
||||
newValue, // You can specifically test only certain keys
|
||||
oldValue,
|
||||
});
|
||||
|
||||
propInstance.setValue(newValue);
|
||||
|
||||
expect(propInstance.getValue()).toEqual(newValue);
|
||||
expect(propInstance.type).toBe('literal');
|
||||
expect(mockEmitChange).toHaveBeenCalledWith({ oldValue });
|
||||
expect(mockEventBusEmit).toHaveBeenCalledWith(GlobalEvent.Node.Prop.InnerChange, expectedPartialPropsInfo);
|
||||
expect(mockEmitPropChange).toHaveBeenCalledWith(expectedPartialPropsInfo);
|
||||
});
|
||||
|
||||
it('should has event when unset call', () => {
|
||||
const oldValue = propInstance._value;
|
||||
|
||||
propInstance.unset();
|
||||
|
||||
const expectedPartialPropsInfo = expect.objectContaining({
|
||||
key: propInstance.key,
|
||||
newValue: undefined, // You can specifically test only certain keys
|
||||
oldValue,
|
||||
});
|
||||
|
||||
expect(propInstance.getValue()).toEqual(undefined);
|
||||
expect(propInstance.type).toBe('unset');
|
||||
expect(mockEmitChange).toHaveBeenCalledWith({
|
||||
oldValue,
|
||||
newValue: undefined,
|
||||
});
|
||||
expect(mockEventBusEmit).toHaveBeenCalledWith(GlobalEvent.Node.Prop.InnerChange, expectedPartialPropsInfo);
|
||||
expect(mockEmitPropChange).toHaveBeenCalledWith(expectedPartialPropsInfo);
|
||||
|
||||
propInstance.unset();
|
||||
expect(mockEmitChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
// remove
|
||||
it('should has event when remove call', () => {
|
||||
const oldValue = propInstance._value;
|
||||
|
||||
propInstance.remove();
|
||||
|
||||
const expectedPartialPropsInfo = expect.objectContaining({
|
||||
key: propInstance.key,
|
||||
newValue: undefined, // You can specifically test only certain keys
|
||||
oldValue,
|
||||
});
|
||||
|
||||
expect(propInstance.getValue()).toEqual(undefined);
|
||||
// expect(propInstance.type).toBe('unset');
|
||||
expect(mockEmitChange).toHaveBeenCalledWith({
|
||||
oldValue,
|
||||
newValue: undefined,
|
||||
});
|
||||
expect(mockEventBusEmit).toHaveBeenCalledWith(GlobalEvent.Node.Prop.InnerChange, expectedPartialPropsInfo);
|
||||
expect(mockEmitPropChange).toHaveBeenCalledWith(expectedPartialPropsInfo);
|
||||
|
||||
propInstance.remove();
|
||||
expect(mockEmitChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@ -1,313 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import '../../../fixtures/window';
|
||||
import { set, delayObxTick } from '../../../utils';
|
||||
import { Editor } from '@alilc/lowcode-editor-core';
|
||||
import {
|
||||
Props,
|
||||
getConvertedExtraKey,
|
||||
getOriginalExtraKey,
|
||||
Prop,
|
||||
isProp,
|
||||
isValidArrayIndex,
|
||||
} from '../../../../src/document/node/props/props';
|
||||
import { Designer } from '../../../../src/designer/designer';
|
||||
import { Project } from '../../../../src/project/project';
|
||||
import { DocumentModel } from '../../../../src/document/document-model';
|
||||
|
||||
import { TransformStage } from '@alilc/lowcode-types';
|
||||
|
||||
const mockOwner = { componentName: 'Page' };
|
||||
|
||||
describe('Props 类测试', () => {
|
||||
let props: Props;
|
||||
beforeEach(() => {
|
||||
props = new Props(
|
||||
mockOwner,
|
||||
{
|
||||
a: 1,
|
||||
b: 'str',
|
||||
c: true,
|
||||
d: {
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
},
|
||||
z: {
|
||||
z1: 1,
|
||||
z2: 'str',
|
||||
},
|
||||
},
|
||||
{ condition: true },
|
||||
);
|
||||
});
|
||||
afterEach(() => {
|
||||
props.purge();
|
||||
});
|
||||
|
||||
it('getNode', () => {
|
||||
expect(props.getNode()).toBe(mockOwner);
|
||||
});
|
||||
|
||||
it('items / get', async () => {
|
||||
expect(props.size).toBe(6);
|
||||
|
||||
expect(props.get('a').getValue()).toBe(1);
|
||||
expect(props.get('b').getValue()).toBe('str');
|
||||
expect(props.get('c').getValue()).toBe(true);
|
||||
expect(props.get('d').getValue()).toEqual({ type: 'JSExpression', value: 'state.a' });
|
||||
expect(props.get('z').getValue()).toEqual({
|
||||
z1: 1,
|
||||
z2: 'str',
|
||||
});
|
||||
|
||||
expect(props.getPropValue('a')).toBe(1);
|
||||
props.setPropValue('a', 2);
|
||||
expect(props.getPropValue('a')).toBe(2);
|
||||
// props.clearPropValue('a');
|
||||
// expect(props.get('a')?.isUnset()).toBeTruthy();
|
||||
|
||||
expect(props.get('z.z1')?.getValue()).toBe(1);
|
||||
expect(props.get('z.z2')?.getValue()).toBe('str');
|
||||
|
||||
const notCreatedProp = props.get('i');
|
||||
expect(notCreatedProp).toBeNull();
|
||||
const newlyCreatedProp = props.get('l', true);
|
||||
const newlyCreatedNestedProp = props.get('m.m1', true);
|
||||
newlyCreatedProp.setValue('newlyCreatedProp');
|
||||
newlyCreatedNestedProp?.setValue('newlyCreatedNestedProp');
|
||||
|
||||
expect(props.get('l').getValue()).toBe('newlyCreatedProp');
|
||||
expect(props.get('m.m1').getValue()).toBe('newlyCreatedNestedProp');
|
||||
|
||||
// map / list 级联测试
|
||||
props.get('loopArgs.0', true).setValue('newItem');
|
||||
expect(props.get('loopArgs.0').getValue()).toBe('newItem');
|
||||
});
|
||||
|
||||
it('export', () => {
|
||||
expect(props.export()).toEqual({
|
||||
props: {
|
||||
a: 1,
|
||||
b: 'str',
|
||||
c: true,
|
||||
d: {
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
},
|
||||
z: {
|
||||
z1: 1,
|
||||
z2: 'str',
|
||||
},
|
||||
},
|
||||
extras: {
|
||||
condition: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(props.toData()).toEqual({
|
||||
a: 1,
|
||||
b: 'str',
|
||||
c: true,
|
||||
d: {
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
},
|
||||
z: {
|
||||
z1: 1,
|
||||
z2: 'str',
|
||||
},
|
||||
});
|
||||
|
||||
props.get('a')?.unset();
|
||||
expect(props.toData()).toEqual({
|
||||
a: undefined,
|
||||
b: 'str',
|
||||
c: true,
|
||||
d: {
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
},
|
||||
z: {
|
||||
z1: 1,
|
||||
z2: 'str',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('export - remove undefined items', () => {
|
||||
props.import(
|
||||
{
|
||||
a: 1,
|
||||
},
|
||||
{ loop: false },
|
||||
);
|
||||
props.setPropValue('x', undefined);
|
||||
expect(props.export()).toEqual({
|
||||
props: {
|
||||
a: 1,
|
||||
},
|
||||
extras: {
|
||||
loop: false,
|
||||
},
|
||||
});
|
||||
|
||||
props.setPropValue('x', 2);
|
||||
expect(props.export()).toEqual({
|
||||
props: {
|
||||
a: 1,
|
||||
x: 2,
|
||||
},
|
||||
extras: {
|
||||
loop: false,
|
||||
},
|
||||
});
|
||||
|
||||
props.setPropValue('y.z', undefined);
|
||||
expect(props.export()).toEqual({
|
||||
props: {
|
||||
a: 1,
|
||||
x: 2,
|
||||
},
|
||||
extras: {
|
||||
loop: false,
|
||||
},
|
||||
});
|
||||
|
||||
props.setPropValue('y.z', 2);
|
||||
expect(props.export()).toEqual({
|
||||
props: {
|
||||
a: 1,
|
||||
x: 2,
|
||||
y: { z: 2 },
|
||||
},
|
||||
extras: {
|
||||
loop: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('import', () => {
|
||||
props.import(
|
||||
{
|
||||
x: 1,
|
||||
y: true,
|
||||
},
|
||||
{ loop: false },
|
||||
);
|
||||
expect(props.export()).toEqual({
|
||||
props: {
|
||||
x: 1,
|
||||
y: true,
|
||||
},
|
||||
extras: {
|
||||
loop: false,
|
||||
},
|
||||
});
|
||||
|
||||
props.import();
|
||||
});
|
||||
|
||||
it('merge', async () => {
|
||||
props.merge({ x: 1 });
|
||||
|
||||
await delayObxTick();
|
||||
|
||||
expect(props.get('x')?.getValue()).toBe(1);
|
||||
});
|
||||
|
||||
it('has / add / delete / deleteKey / remove', () => {
|
||||
expect(props.has('a')).toBeTruthy();
|
||||
expect(props.has('b')).toBeTruthy();
|
||||
expect(props.has('c')).toBeTruthy();
|
||||
expect(props.has('d')).toBeTruthy();
|
||||
expect(props.has('z')).toBeTruthy();
|
||||
expect(props.has('y')).toBeFalsy();
|
||||
|
||||
props.add(1, 'newAdded');
|
||||
expect(props.has('newAdded')).toBeTruthy();
|
||||
|
||||
props.deleteKey('c');
|
||||
expect(props.get('c', false)).toBeNull();
|
||||
props.delete(props.get('b'));
|
||||
expect(props.get('b', false)).toBeNull();
|
||||
|
||||
props.get('d')?.remove();
|
||||
expect(props.get('d', false)).toBeNull();
|
||||
});
|
||||
|
||||
it('迭代器 / map / forEach', () => {
|
||||
const mockFn = jest.fn();
|
||||
for (const item of props) {
|
||||
mockFn();
|
||||
}
|
||||
expect(mockFn).toHaveBeenCalledTimes(6);
|
||||
mockFn.mockClear();
|
||||
|
||||
props.forEach((item) => {
|
||||
mockFn();
|
||||
});
|
||||
expect(mockFn).toHaveBeenCalledTimes(6);
|
||||
mockFn.mockClear();
|
||||
|
||||
props.map((item) => {
|
||||
return mockFn();
|
||||
});
|
||||
expect(mockFn).toHaveBeenCalledTimes(6);
|
||||
mockFn.mockClear();
|
||||
|
||||
props.filter((item) => {
|
||||
return mockFn();
|
||||
});
|
||||
expect(mockFn).toHaveBeenCalledTimes(6);
|
||||
mockFn.mockClear();
|
||||
});
|
||||
|
||||
it('purge', () => {
|
||||
props.purge();
|
||||
|
||||
expect(props.purged).toBeTruthy();
|
||||
});
|
||||
|
||||
it('empty items', () => {
|
||||
expect(new Props(mockOwner).export()).toEqual({});
|
||||
});
|
||||
|
||||
describe('list 类型', () => {
|
||||
let props: Props;
|
||||
beforeEach(() => {
|
||||
props = new Props(mockOwner, [1, true, 'haha'], { condition: true });
|
||||
});
|
||||
it('constructor', () => {
|
||||
props.purge();
|
||||
});
|
||||
|
||||
it('export', () => {
|
||||
expect(props.export().extras).toEqual({
|
||||
condition: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('import', () => {
|
||||
props.import([1], { loop: true });
|
||||
expect(props.export().extras).toEqual({
|
||||
loop: true,
|
||||
});
|
||||
|
||||
props.items[0]?.unset();
|
||||
props.export();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('其他函数', () => {
|
||||
it('getConvertedExtraKey', () => {
|
||||
expect(getConvertedExtraKey()).toBe('');
|
||||
expect(getConvertedExtraKey('a')).toBe('___a___');
|
||||
expect(getConvertedExtraKey('a.b')).toBe('___a___.b');
|
||||
expect(getConvertedExtraKey('a.0')).toBe('___a___.0');
|
||||
});
|
||||
|
||||
it('getOriginalExtraKey', () => {
|
||||
expect(getOriginalExtraKey('___a___')).toBe('a');
|
||||
expect(getOriginalExtraKey('___a___.b')).toBe('a.b');
|
||||
});
|
||||
});
|
||||
@ -1,29 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import '../../../fixtures/silent-console';
|
||||
import { getSource, valueToSource } from '../../../../src/document/node/props/value-to-source';
|
||||
|
||||
it('valueToSource', () => {
|
||||
expect(valueToSource(1)).toMatchSnapshot();
|
||||
expect(valueToSource(true)).toMatchSnapshot();
|
||||
expect(valueToSource([])).toMatchSnapshot();
|
||||
expect(valueToSource([{ a: 1 }])).toMatchSnapshot();
|
||||
expect(valueToSource({ a: 1 })).toMatchSnapshot();
|
||||
expect(valueToSource(null)).toMatchSnapshot();
|
||||
expect(valueToSource(() => {})).toMatchSnapshot();
|
||||
expect(valueToSource(new Map())).toMatchSnapshot();
|
||||
expect(valueToSource(new Set())).toMatchSnapshot();
|
||||
expect(valueToSource(/haha/)).toMatchSnapshot();
|
||||
expect(valueToSource('hahah')).toMatchSnapshot();
|
||||
expect(valueToSource(Symbol('haha'))).toMatchSnapshot();
|
||||
expect(valueToSource()).toMatchSnapshot();
|
||||
expect(valueToSource(new Date(1607680998520))).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('getSource', () => {
|
||||
expect(getSource({ __source: { a: 1 } })).toEqual({ a: 1 });
|
||||
expect(getSource()).toBe('');
|
||||
const value = { abc: 1 };
|
||||
getSource(value);
|
||||
expect(value).toHaveProperty('__source');
|
||||
expect(getSource(1)).toBe('1');
|
||||
});
|
||||
@ -1,263 +0,0 @@
|
||||
import '../fixtures/window';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
|
||||
const mockCreateSettingEntry = jest.fn();
|
||||
jest.mock('../../src/designer/designer', () => {
|
||||
return {
|
||||
Designer: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
getComponentMeta() {
|
||||
return {
|
||||
getMetadata() {
|
||||
return { configure: { advanced: null } };
|
||||
},
|
||||
get advanced() {
|
||||
return {};
|
||||
},
|
||||
};
|
||||
},
|
||||
transformProps(props) {
|
||||
return props;
|
||||
},
|
||||
createSettingEntry: mockCreateSettingEntry,
|
||||
postEvent() {},
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let designer = null;
|
||||
beforeAll(() => {
|
||||
designer = new Designer({});
|
||||
});
|
||||
|
||||
describe('选择区测试', () => {
|
||||
it('常规方法', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap, selection } = currentDocument!;
|
||||
const selectionChangeHandler = jest.fn();
|
||||
selection.onSelectionChange(selectionChangeHandler);
|
||||
|
||||
selection.select('form');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selection.selected).toEqual(['form']);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
selection.select('form');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(0);
|
||||
expect(selection.selected).toEqual(['form']);
|
||||
|
||||
selection.select('node_k1ow3cbj');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['node_k1ow3cbj']);
|
||||
expect(selection.selected).toEqual(['node_k1ow3cbj']);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
selection.selectAll(['node_k1ow3cbj', 'form']);
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['node_k1ow3cbj', 'form']);
|
||||
expect(selection.selected).toEqual(['node_k1ow3cbj', 'form']);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
selection.remove('node_k1ow3cbj_fake');
|
||||
selection.remove('node_k1ow3cbj');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form']);
|
||||
expect(selection.selected).toEqual(['form']);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
selection.clear();
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual([]);
|
||||
expect(selection.selected).toEqual([]);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
// 无选中时调用 clear,不再触发事件
|
||||
selection.clear();
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(0);
|
||||
expect(selection.selected).toEqual([]);
|
||||
selectionChangeHandler.mockClear();
|
||||
});
|
||||
|
||||
it('add 方法', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap, selection } = currentDocument!;
|
||||
const selectionChangeHandler = jest.fn();
|
||||
selection.onSelectionChange(selectionChangeHandler);
|
||||
|
||||
selection.add('form');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form']);
|
||||
expect(selection.selected).toEqual(['form']);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
// 再加一次相同的节点,不触发事件
|
||||
selection.add('form');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(0);
|
||||
expect(selection.selected).toEqual(['form']);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
selection.add('form2');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form', 'form2']);
|
||||
expect(selection.selected).toEqual(['form', 'form2']);
|
||||
selectionChangeHandler.mockClear();
|
||||
});
|
||||
|
||||
it('selectAll 包含不存在的 id', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap, selection } = currentDocument!;
|
||||
|
||||
selection.selectAll(['form', 'node_k1ow3cbj', 'form2']);
|
||||
|
||||
expect(selection.selected).toEqual(['form', 'node_k1ow3cbj']);
|
||||
});
|
||||
|
||||
it('dispose 方法 - 选中的节点没有被删除的', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap, selection } = currentDocument!;
|
||||
|
||||
selection.selectAll(['form', 'node_k1ow3cbj']);
|
||||
|
||||
const selectionChangeHandler = jest.fn();
|
||||
selection.onSelectionChange(selectionChangeHandler);
|
||||
selection.dispose();
|
||||
|
||||
expect(selectionChangeHandler).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('containsNode 方法', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap, selection } = currentDocument!;
|
||||
const selectionChangeHandler = jest.fn();
|
||||
selection.onSelectionChange(selectionChangeHandler);
|
||||
|
||||
selection.select('form');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form']);
|
||||
expect(selection.selected).toEqual(['form']);
|
||||
expect(selection.has('form')).toBe(true);
|
||||
expect(selection.containsNode(currentDocument?.getNode('form'))).toBe(true);
|
||||
expect(selection.containsNode(currentDocument?.getNode('node_k1ow3cbj'))).toBe(true);
|
||||
expect(selection.containsNode(currentDocument?.getNode('page'))).toBe(false);
|
||||
expect(selection.getNodes()).toEqual([currentDocument?.getNode('form')]);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
selection.add('node_k1ow3cbj');
|
||||
expect(selection.selected).toEqual(['form', 'node_k1ow3cbj']);
|
||||
expect(selection.getTopNodes()).toEqual([currentDocument?.getNode('form')]);
|
||||
expect(selection.getTopNodes(true)).toEqual([currentDocument?.getNode('form')]);
|
||||
});
|
||||
|
||||
it('containsNode 方法 - excludeRoot: true', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap, selection } = currentDocument!;
|
||||
const selectionChangeHandler = jest.fn();
|
||||
selection.onSelectionChange(selectionChangeHandler);
|
||||
|
||||
selection.select('page');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['page']);
|
||||
expect(selection.selected).toEqual(['page']);
|
||||
expect(selection.has('page')).toBe(true);
|
||||
expect(selection.containsNode(currentDocument?.getNode('form'))).toBe(true);
|
||||
expect(selection.containsNode(currentDocument?.getNode('form'), true)).toBe(false);
|
||||
selectionChangeHandler.mockClear();
|
||||
});
|
||||
|
||||
it('containsNode 方法 - excludeRoot: true', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap, selection } = currentDocument!;
|
||||
const selectionChangeHandler = jest.fn();
|
||||
const dispose = selection.onSelectionChange(selectionChangeHandler);
|
||||
|
||||
selection.select('form');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(1);
|
||||
expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form']);
|
||||
selectionChangeHandler.mockClear();
|
||||
|
||||
// dispose 后,selected 会被赋值,但是变更事件不会被触发
|
||||
dispose();
|
||||
selection.select('page');
|
||||
expect(selectionChangeHandler).toHaveBeenCalledTimes(0);
|
||||
expect(selection.selected).toEqual(['page']);
|
||||
selectionChangeHandler.mockClear();
|
||||
});
|
||||
|
||||
it('getNodes', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
const { currentDocument } = project;
|
||||
const { selection } = currentDocument!;
|
||||
|
||||
selection.selectAll(['form', 'node_k1ow3cbj', 'form2']);
|
||||
|
||||
// form2 is not a valid node
|
||||
expect(selection.getNodes()).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('getTopNodes - BeforeOrAfter', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
const { currentDocument } = project;
|
||||
const { selection } = currentDocument!;
|
||||
|
||||
selection.selectAll(['node_k1ow3cbj', 'node_k1ow3cbo']);
|
||||
|
||||
expect(selection.getTopNodes()).toHaveLength(2);
|
||||
});
|
||||
it('getTopNodes', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
const { currentDocument } = project;
|
||||
const { selection } = currentDocument!;
|
||||
|
||||
selection.selectAll(['node_k1ow3cbj', 'node_k1ow3cbo', 'form', 'node_k1ow3cbl', 'form2']);
|
||||
|
||||
// form2 is not a valid node, and node_k1ow3cbj is a child node of form
|
||||
expect(selection.getTopNodes()).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
@ -1,280 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Abc.Group',
|
||||
npm: {
|
||||
package: '@ali/vc-div',
|
||||
componentName: 'Div',
|
||||
},
|
||||
title: { label: '容器' },
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
},
|
||||
supports: {},
|
||||
advanced: {
|
||||
isTopFixed: true,
|
||||
callbacks: {
|
||||
onNodeAdd: (dragment, self) => { console.log(dragment); },
|
||||
onNodeRemove: (dragment, self) => { console.log(dragment); }
|
||||
},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,280 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Abc.Item',
|
||||
npm: {
|
||||
package: '@ali/vc-div',
|
||||
componentName: 'Div',
|
||||
},
|
||||
title: { label: '容器' },
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
},
|
||||
supports: {},
|
||||
advanced: {
|
||||
isTopFixed: true,
|
||||
callbacks: {
|
||||
onNodeAdd: (dragment, self) => { console.log(dragment); },
|
||||
onNodeRemove: (dragment, self) => { console.log(dragment); }
|
||||
},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,280 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Abc.Node',
|
||||
npm: {
|
||||
package: '@ali/vc-div',
|
||||
componentName: 'Div',
|
||||
},
|
||||
title: { label: '容器' },
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
},
|
||||
supports: {},
|
||||
advanced: {
|
||||
isTopFixed: true,
|
||||
callbacks: {
|
||||
onNodeAdd: (dragment, self) => { console.log(dragment); },
|
||||
onNodeRemove: (dragment, self) => { console.log(dragment); }
|
||||
},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,280 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Abc.Option',
|
||||
npm: {
|
||||
package: '@ali/vc-div',
|
||||
componentName: 'Div',
|
||||
},
|
||||
title: { label: '容器' },
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
},
|
||||
supports: {},
|
||||
advanced: {
|
||||
isTopFixed: true,
|
||||
callbacks: {
|
||||
onNodeAdd: (dragment, self) => { console.log(dragment); },
|
||||
onNodeRemove: (dragment, self) => { console.log(dragment); }
|
||||
},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,308 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Button',
|
||||
npm: {
|
||||
package: '@ali/vc-button',
|
||||
componentName: 'Button',
|
||||
},
|
||||
title: '按钮',
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'non-exsiting',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'obj',
|
||||
items: [
|
||||
{
|
||||
name: 'a',
|
||||
title: 'a',
|
||||
setter: () => 'StringSetter',
|
||||
},
|
||||
{
|
||||
name: 'b',
|
||||
title: 'b',
|
||||
setter: 'NumberSetter',
|
||||
},
|
||||
{
|
||||
name: 'c',
|
||||
title: 'c',
|
||||
setter: {
|
||||
componentName: 'ColorSetter'
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
() => 'haha', // IPublicTypeCustomView
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
nestingRule: {
|
||||
// parentWhitelist: 'Div',
|
||||
// childWhitelist: 'Div',
|
||||
},
|
||||
descriptor: 'xTitle'
|
||||
},
|
||||
supports: {},
|
||||
},
|
||||
experimental: {
|
||||
callbacks: {},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,277 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Dialog',
|
||||
npm: {
|
||||
package: '@ali/vc-dialog',
|
||||
componentName: 'Dialog',
|
||||
},
|
||||
title: '容器',
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
isModal: true,
|
||||
},
|
||||
supports: {},
|
||||
},
|
||||
experimental: {
|
||||
callbacks: {},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,283 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Div',
|
||||
npm: {
|
||||
package: '@ali/vc-div',
|
||||
componentName: 'Div',
|
||||
},
|
||||
title: '容器',
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
nestingRule: {
|
||||
// parentWhitelist: 'Div',
|
||||
// childWhitelist: 'Div',
|
||||
},
|
||||
},
|
||||
supports: {},
|
||||
advanced: {
|
||||
callbacks: {
|
||||
onNodeAdd: (dragment, self) => { console.log(dragment); },
|
||||
onNodeRemove: (dragment, self) => { console.log(dragment); }
|
||||
},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,22 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Div',
|
||||
title: '容器',
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
component: {
|
||||
nestingRule: {
|
||||
parentWhitelist: (parent, my) => {
|
||||
if (parent.componentName === 'Form' && my.componentName === 'Div') return true;
|
||||
return false;
|
||||
},
|
||||
childWhitelist: (child, my) => {
|
||||
if (child.componentName === 'Image' && my.componentName === 'Div') return true;
|
||||
return false;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,280 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Div',
|
||||
npm: {
|
||||
package: '@ali/vc-div',
|
||||
componentName: 'Div',
|
||||
},
|
||||
title: { label: '容器' },
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
},
|
||||
supports: {},
|
||||
advanced: {
|
||||
isTopFixed: true,
|
||||
callbacks: {
|
||||
onNodeAdd: (dragment, self) => { console.log(dragment); },
|
||||
onNodeRemove: (dragment, self) => { console.log(dragment); }
|
||||
},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,282 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Div',
|
||||
npm: {
|
||||
package: '@ali/vc-div',
|
||||
componentName: 'Div',
|
||||
},
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
nestingRule: {
|
||||
// parentWhitelist: 'Div',
|
||||
// childWhitelist: 'Div',
|
||||
}
|
||||
},
|
||||
supports: {},
|
||||
advanced: {
|
||||
callbacks: {
|
||||
onNodeAdd: (dragment, self) => { console.log(dragment); },
|
||||
onNodeRemove: (dragment, self) => { console.log(dragment); }
|
||||
},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,272 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Div',
|
||||
npm: {
|
||||
package: '@ali/vc-div',
|
||||
componentName: 'Div',
|
||||
},
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
experimental: {
|
||||
callbacks: {
|
||||
onNodeAdd: (dragment, self) => { console.log(dragment); },
|
||||
onNodeRemove: (dragment, self) => { console.log(dragment); }
|
||||
},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,283 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Div',
|
||||
npm: {
|
||||
package: '@ali/vc-div',
|
||||
componentName: 'Div',
|
||||
},
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
nestingRule: {
|
||||
// parentWhitelist: 'Div',
|
||||
// childWhitelist: 'Div',
|
||||
},
|
||||
disableBehaviors: '*',
|
||||
},
|
||||
supports: {},
|
||||
advanced: {
|
||||
callbacks: {
|
||||
onNodeAdd: (dragment, self) => { console.log(dragment); },
|
||||
onNodeRemove: (dragment, self) => { console.log(dragment); }
|
||||
},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,283 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Div',
|
||||
npm: {
|
||||
package: '@ali/vc-div',
|
||||
componentName: 'Div',
|
||||
},
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
nestingRule: {
|
||||
// parentWhitelist: 'Div',
|
||||
// childWhitelist: 'Div',
|
||||
},
|
||||
disableBehaviors: '*',
|
||||
},
|
||||
supports: {},
|
||||
},
|
||||
experimental: {
|
||||
callbacks: {
|
||||
onNodeAdd: (dragment, self) => { console.log(dragment); },
|
||||
onNodeRemove: (dragment, self) => { console.log(dragment); }
|
||||
},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,276 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Div',
|
||||
npm: {
|
||||
package: '@ali/vc-div',
|
||||
componentName: 'Div',
|
||||
},
|
||||
title: '容器',
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
supports: {},
|
||||
advanced: {
|
||||
callbacks: {
|
||||
onNodeAdd: (dragment, self) => { console.log(dragment); },
|
||||
onNodeRemove: (dragment, self) => { console.log(dragment); }
|
||||
},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,12 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Div',
|
||||
npm: {
|
||||
package: '@ali/vc-div',
|
||||
componentName: 'Div',
|
||||
},
|
||||
title: '容器',
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,8 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Div',
|
||||
title: '容器',
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,279 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Form',
|
||||
npm: {
|
||||
package: '@ali/vc-form',
|
||||
},
|
||||
title: '表单',
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
nestingRule: {
|
||||
parentWhitelist: 'Div,Page',
|
||||
// childWhitelist: 'Div',
|
||||
},
|
||||
},
|
||||
supports: {},
|
||||
},
|
||||
experimental: {
|
||||
callbacks: {},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,279 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Other',
|
||||
npm: {
|
||||
package: '@ali/vc-other',
|
||||
},
|
||||
title: '容器',
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
nestingRule: {
|
||||
parentWhitelist: 'Div',
|
||||
childWhitelist: 'Div',
|
||||
},
|
||||
},
|
||||
supports: {},
|
||||
},
|
||||
experimental: {
|
||||
callbacks: {},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,279 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Page',
|
||||
npm: {
|
||||
package: '@ali/vc-page',
|
||||
},
|
||||
title: '容器',
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
nestingRule: {
|
||||
// parentWhitelist: 'Div',
|
||||
// childWhitelist: 'Div',
|
||||
},
|
||||
},
|
||||
supports: {},
|
||||
},
|
||||
experimental: {
|
||||
callbacks: {},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,279 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'Page',
|
||||
npm: {
|
||||
package: '@ali/vc-page',
|
||||
},
|
||||
title: '容器',
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
nestingRule: {
|
||||
// parentWhitelist: 'Div',
|
||||
// childWhitelist: 'Div',
|
||||
},
|
||||
},
|
||||
supports: {},
|
||||
},
|
||||
experimental: {
|
||||
callbacks: {},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,279 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'RootContent',
|
||||
npm: {
|
||||
package: '@ali/vc-page',
|
||||
},
|
||||
title: '容器',
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
nestingRule: {
|
||||
// parentWhitelist: 'Div',
|
||||
// childWhitelist: 'Div',
|
||||
},
|
||||
},
|
||||
supports: {},
|
||||
},
|
||||
experimental: {
|
||||
callbacks: {},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,279 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'RootFooter',
|
||||
npm: {
|
||||
package: '@ali/vc-page',
|
||||
},
|
||||
title: '容器',
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
nestingRule: {
|
||||
// parentWhitelist: 'Div',
|
||||
// childWhitelist: 'Div',
|
||||
},
|
||||
},
|
||||
supports: {},
|
||||
},
|
||||
experimental: {
|
||||
callbacks: {},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,279 +0,0 @@
|
||||
import { IPublicTypeComponentMetadata } from "@alilc/lowcode-types";
|
||||
export default {
|
||||
componentName: 'RootHeader',
|
||||
npm: {
|
||||
package: '@ali/vc-page',
|
||||
},
|
||||
title: '容器',
|
||||
docUrl: 'https://github.com/alibaba/lowcode-materials/tree/main/docs',
|
||||
devMode: 'proCode',
|
||||
tags: ['布局'],
|
||||
configure: {
|
||||
props: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'behavior',
|
||||
title: '默认状态',
|
||||
extraProps: {
|
||||
display: 'inline',
|
||||
defaultValue: 'NORMAL',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
title: '普通',
|
||||
value: 'NORMAL',
|
||||
},
|
||||
{
|
||||
title: '隐藏',
|
||||
value: 'HIDDEN',
|
||||
},
|
||||
],
|
||||
loose: false,
|
||||
cancelable: false,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: '__style__',
|
||||
title: {
|
||||
label: '样式设置',
|
||||
tip: '点击 ? 查看样式设置器用法指南',
|
||||
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
advanced: true,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'groupkgzzeo41',
|
||||
title: '高级',
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'field',
|
||||
name: 'fieldId',
|
||||
title: {
|
||||
label: '唯一标识',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: '请输入唯一标识',
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'useFieldIdAsDomId',
|
||||
title: {
|
||||
label: '将唯一标识用作 DOM ID',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: false,
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'customClassName',
|
||||
title: '自定义样式类',
|
||||
extraProps: {
|
||||
display: 'block',
|
||||
defaultValue: '',
|
||||
},
|
||||
setter: {
|
||||
componentName: 'MixedSetter',
|
||||
props: {
|
||||
setters: [
|
||||
{
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
placeholder: null,
|
||||
multiline: false,
|
||||
rows: 10,
|
||||
required: false,
|
||||
pattern: null,
|
||||
maxLength: null,
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
'VariableSetter',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'events',
|
||||
title: {
|
||||
label: '动作设置',
|
||||
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||
},
|
||||
extraProps: {
|
||||
display: 'accordion',
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: {
|
||||
key: null,
|
||||
ref: null,
|
||||
props: {
|
||||
events: [
|
||||
{
|
||||
name: 'onClick',
|
||||
title: '当点击时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
title: '当鼠标进入时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
title: '当鼠标离开时',
|
||||
initialValue:
|
||||
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||
},
|
||||
],
|
||||
},
|
||||
_owner: null,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onClick',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseEnter',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
{
|
||||
type: 'field',
|
||||
name: 'onMouseLeave',
|
||||
extraProps: {
|
||||
defaultValue: {
|
||||
ignored: true,
|
||||
},
|
||||
},
|
||||
setter: 'I18nSetter',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
nestingRule: {
|
||||
// parentWhitelist: 'Div',
|
||||
// childWhitelist: 'Div',
|
||||
},
|
||||
},
|
||||
supports: {},
|
||||
},
|
||||
experimental: {
|
||||
callbacks: {},
|
||||
initials: [
|
||||
{
|
||||
name: 'behavior',
|
||||
},
|
||||
{
|
||||
name: '__style__',
|
||||
},
|
||||
{
|
||||
name: 'fieldId',
|
||||
},
|
||||
{
|
||||
name: 'useFieldIdAsDomId',
|
||||
},
|
||||
{
|
||||
name: 'customClassName',
|
||||
},
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
filters: [
|
||||
{
|
||||
name: 'events',
|
||||
},
|
||||
{
|
||||
name: 'onClick',
|
||||
},
|
||||
{
|
||||
name: 'onMouseEnter',
|
||||
},
|
||||
{
|
||||
name: 'onMouseLeave',
|
||||
},
|
||||
],
|
||||
autoruns: [],
|
||||
},
|
||||
} as IPublicTypeComponentMetadata;
|
||||
@ -1,3 +0,0 @@
|
||||
Object.defineProperty(window, 'requestAnimationFrame', {
|
||||
value: null,
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
993
packages/designer/tests/fixtures/schema/form.ts
vendored
993
packages/designer/tests/fixtures/schema/form.ts
vendored
@ -1,993 +0,0 @@
|
||||
export default {
|
||||
componentName: 'Page',
|
||||
id: 'page',
|
||||
title: 'hey, i\' a page!',
|
||||
props: {
|
||||
extensions: {
|
||||
启用页头: true,
|
||||
},
|
||||
pageStyle: {
|
||||
backgroundColor: '#f2f3f5',
|
||||
},
|
||||
containerStyle: {},
|
||||
className: 'page_kgaqfbm4',
|
||||
templateVersion: '1.0.0',
|
||||
},
|
||||
lifeCycles: {
|
||||
constructor: {
|
||||
type: 'js',
|
||||
compiled:
|
||||
"function constructor() {\nvar module = { exports: {} };\nvar _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === 'function'){\n _this[item] = module.exports[item];\n }\n});\n\n}",
|
||||
source:
|
||||
"function constructor() {\nvar module = { exports: {} };\nvar _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === 'function'){\n _this[item] = module.exports[item];\n }\n});\n\n}",
|
||||
},
|
||||
},
|
||||
condition: true,
|
||||
css:
|
||||
'body{background-color:#f2f3f5}.card_kgaqfbm5 {\n margin-bottom: 12px;\n}.card_kgaqfbm6 {\n margin-bottom: 12px;\n}.button_kgaqfbm7 {\n margin-right: 16px;\n width: 80px\n}.button_kgaqfbm8 {\n width: 80px;\n}.div_kgaqfbm9 {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}',
|
||||
methods: {
|
||||
__initMethods__: {
|
||||
type: 'js',
|
||||
source: 'function (exports, module) { /*set actions code here*/ }',
|
||||
compiled: 'function (exports, module) { /*set actions code here*/ }',
|
||||
},
|
||||
},
|
||||
dataSource: {
|
||||
offline: [],
|
||||
globalConfig: {
|
||||
fit: {
|
||||
compiled: '',
|
||||
source: '',
|
||||
type: 'js',
|
||||
error: {},
|
||||
},
|
||||
},
|
||||
online: [],
|
||||
sync: true,
|
||||
list: [],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'RootHeader',
|
||||
id: 'node_k1ow3cba',
|
||||
props: {},
|
||||
condition: true,
|
||||
children: [
|
||||
{
|
||||
componentName: 'PageHeader',
|
||||
id: 'node_k1ow3cbd',
|
||||
props: {
|
||||
extraContent: '',
|
||||
__slot__extraContent: false,
|
||||
__slot__action: false,
|
||||
title: {
|
||||
// type: 'JSSlot',
|
||||
value: [
|
||||
{
|
||||
componentName: 'Text',
|
||||
id: 'node_k1ow3cbf',
|
||||
props: {
|
||||
showTitle: false,
|
||||
behavior: 'NORMAL',
|
||||
content: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'Title',
|
||||
'zh-CN': '个人信息',
|
||||
type: 'i18n',
|
||||
},
|
||||
__style__: {},
|
||||
fieldId: 'text_k1ow3h1j',
|
||||
maxLine: 0,
|
||||
},
|
||||
condition: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
content: '',
|
||||
__slot__logo: false,
|
||||
__slot__crumb: false,
|
||||
crumb: '',
|
||||
tab: '',
|
||||
logo: '',
|
||||
action: '',
|
||||
__slot__tab: false,
|
||||
__style__: {},
|
||||
__slot__content: false,
|
||||
fieldId: 'pageHeader_k1ow3h1i',
|
||||
subTitle: false,
|
||||
},
|
||||
condition: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'RootContent',
|
||||
id: 'node_k1ow3cbb',
|
||||
props: {
|
||||
contentBgColor: 'transparent',
|
||||
contentPadding: '0',
|
||||
contentMargin: '20',
|
||||
},
|
||||
condition: true,
|
||||
children: [
|
||||
{
|
||||
componentName: 'Form',
|
||||
id: 'form',
|
||||
extraPropA: 'extraPropA',
|
||||
props: {
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
autoValidate: true,
|
||||
scrollToFirstError: true,
|
||||
autoUnmount: true,
|
||||
behavior: 'NORMAL',
|
||||
dataSource: {
|
||||
type: 'variable',
|
||||
variable: 'state.formData',
|
||||
},
|
||||
obj: {
|
||||
a: 1,
|
||||
b: false,
|
||||
c: 'string',
|
||||
},
|
||||
__style__: {},
|
||||
fieldId: 'form',
|
||||
fieldOptions: {},
|
||||
slotA: '',
|
||||
},
|
||||
condition: true,
|
||||
children: [
|
||||
{
|
||||
componentName: 'Card',
|
||||
id: 'node_k1ow3cbj',
|
||||
props: {
|
||||
__slot__title: false,
|
||||
subTitle: {
|
||||
use: 'zh-CN',
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
__slot__subTitle: false,
|
||||
extra: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
className: 'card_kgaqfbm5',
|
||||
title: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'Title',
|
||||
'zh-CN': '基本信息',
|
||||
type: 'i18n',
|
||||
},
|
||||
__slot__extra: false,
|
||||
showHeadDivider: true,
|
||||
__style__: ':root {\n margin-bottom: 12px;\n}',
|
||||
showTitleBullet: true,
|
||||
contentHeight: '',
|
||||
fieldId: 'card_k1ow3h1l',
|
||||
dividerNoInset: false,
|
||||
},
|
||||
condition: true,
|
||||
children: [
|
||||
{
|
||||
componentName: 'CardContent',
|
||||
id: 'node_k1ow3cbk',
|
||||
props: {},
|
||||
condition: true,
|
||||
children: [
|
||||
{
|
||||
componentName: 'ColumnsLayout',
|
||||
id: 'node_k1ow3cbw',
|
||||
props: {
|
||||
layout: '6:6',
|
||||
columnGap: '20',
|
||||
rowGap: 0,
|
||||
__style__: {},
|
||||
fieldId: 'columns_k1ow3h1v',
|
||||
},
|
||||
condition: true,
|
||||
children: [
|
||||
{
|
||||
componentName: 'Column',
|
||||
id: 'node_k1ow3cbx',
|
||||
props: {
|
||||
colSpan: '',
|
||||
__style__: {},
|
||||
fieldId: 'column_k1p1bnjm',
|
||||
},
|
||||
condition: true,
|
||||
children: [
|
||||
{
|
||||
componentName: 'TextField',
|
||||
id: 'node_k1ow3cbz',
|
||||
props: {
|
||||
fieldName: 'name',
|
||||
hasClear: false,
|
||||
autoFocus: false,
|
||||
tips: {
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
trim: false,
|
||||
labelTextAlign: 'right',
|
||||
placeholder: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'please input',
|
||||
'zh-CN': '请输入',
|
||||
type: 'i18n',
|
||||
},
|
||||
state: '',
|
||||
behavior: 'NORMAL',
|
||||
value: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
addonBefore: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
validation: [
|
||||
{
|
||||
type: 'required',
|
||||
},
|
||||
],
|
||||
hasLimitHint: false,
|
||||
cutString: false,
|
||||
__style__: {},
|
||||
fieldId: 'textField_k1ow3h1w',
|
||||
htmlType: 'input',
|
||||
autoHeight: false,
|
||||
labelColOffset: 0,
|
||||
label: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'TextField',
|
||||
'zh-CN': '姓名',
|
||||
type: 'i18n',
|
||||
},
|
||||
__category__: 'form',
|
||||
labelColSpan: 4,
|
||||
wrapperColSpan: 0,
|
||||
rows: 4,
|
||||
addonAfter: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
wrapperColOffset: 0,
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
__useMediator: 'value',
|
||||
labelTipsTypes: 'none',
|
||||
labelTipsIcon: '',
|
||||
labelTipsText: {
|
||||
type: 'i18n',
|
||||
use: 'zh-CN',
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
},
|
||||
},
|
||||
condition: true,
|
||||
},
|
||||
{
|
||||
componentName: 'TextField',
|
||||
id: 'node_k1ow3cc1',
|
||||
props: {
|
||||
fieldName: 'englishName',
|
||||
hasClear: false,
|
||||
autoFocus: false,
|
||||
tips: {
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
trim: false,
|
||||
labelTextAlign: 'right',
|
||||
placeholder: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'please input',
|
||||
'zh-CN': '请输入',
|
||||
type: 'i18n',
|
||||
},
|
||||
state: '',
|
||||
behavior: 'NORMAL',
|
||||
value: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
addonBefore: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
validation: [],
|
||||
hasLimitHint: false,
|
||||
cutString: false,
|
||||
__style__: {},
|
||||
fieldId: 'textField_k1ow3h1y',
|
||||
htmlType: 'input',
|
||||
autoHeight: false,
|
||||
labelColOffset: 0,
|
||||
label: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'TextField',
|
||||
'zh-CN': '英文名',
|
||||
type: 'i18n',
|
||||
},
|
||||
__category__: 'form',
|
||||
labelColSpan: 4,
|
||||
wrapperColSpan: 0,
|
||||
rows: 4,
|
||||
addonAfter: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
wrapperColOffset: 0,
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
__useMediator: 'value',
|
||||
labelTipsTypes: 'none',
|
||||
labelTipsIcon: '',
|
||||
labelTipsText: {
|
||||
type: 'i18n',
|
||||
use: 'zh-CN',
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
},
|
||||
},
|
||||
condition: true,
|
||||
},
|
||||
{
|
||||
componentName: 'TextField',
|
||||
id: 'node_k1ow3cc3',
|
||||
props: {
|
||||
fieldName: 'jobTitle',
|
||||
hasClear: false,
|
||||
autoFocus: false,
|
||||
tips: {
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
trim: false,
|
||||
labelTextAlign: 'right',
|
||||
placeholder: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'please input',
|
||||
'zh-CN': '请输入',
|
||||
type: 'i18n',
|
||||
},
|
||||
state: '',
|
||||
behavior: 'NORMAL',
|
||||
value: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
addonBefore: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
validation: [],
|
||||
hasLimitHint: false,
|
||||
cutString: false,
|
||||
__style__: {},
|
||||
fieldId: 'textField_k1ow3h20',
|
||||
htmlType: 'input',
|
||||
autoHeight: false,
|
||||
labelColOffset: 0,
|
||||
label: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'TextField',
|
||||
'zh-CN': '职位',
|
||||
type: 'i18n',
|
||||
},
|
||||
__category__: 'form',
|
||||
labelColSpan: 4,
|
||||
wrapperColSpan: 0,
|
||||
rows: 4,
|
||||
addonAfter: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
wrapperColOffset: 0,
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
__useMediator: 'value',
|
||||
labelTipsTypes: 'none',
|
||||
labelTipsIcon: '',
|
||||
labelTipsText: {
|
||||
type: 'i18n',
|
||||
use: 'zh-CN',
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
},
|
||||
},
|
||||
condition: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Column',
|
||||
id: 'node_k1ow3cby',
|
||||
props: {
|
||||
colSpan: '',
|
||||
__style__: {},
|
||||
fieldId: 'column_k1p1bnjn',
|
||||
},
|
||||
condition: true,
|
||||
children: [
|
||||
{
|
||||
componentName: 'TextField',
|
||||
id: 'node_k1ow3cc2',
|
||||
props: {
|
||||
fieldName: 'nickName',
|
||||
hasClear: false,
|
||||
autoFocus: false,
|
||||
tips: {
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
trim: false,
|
||||
labelTextAlign: 'right',
|
||||
placeholder: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'please input',
|
||||
'zh-CN': '请输入',
|
||||
type: 'i18n',
|
||||
},
|
||||
state: '',
|
||||
behavior: 'NORMAL',
|
||||
value: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
addonBefore: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
validation: [],
|
||||
hasLimitHint: false,
|
||||
cutString: false,
|
||||
__style__: {},
|
||||
fieldId: 'textField_k1ow3h1z',
|
||||
htmlType: 'input',
|
||||
autoHeight: false,
|
||||
labelColOffset: 0,
|
||||
label: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'TextField',
|
||||
'zh-CN': '花名',
|
||||
type: 'i18n',
|
||||
},
|
||||
__category__: 'form',
|
||||
labelColSpan: 4,
|
||||
wrapperColSpan: 0,
|
||||
rows: 4,
|
||||
addonAfter: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
wrapperColOffset: 0,
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
__useMediator: 'value',
|
||||
labelTipsTypes: 'none',
|
||||
labelTipsIcon: '',
|
||||
labelTipsText: {
|
||||
type: 'i18n',
|
||||
use: 'zh-CN',
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
},
|
||||
},
|
||||
condition: true,
|
||||
},
|
||||
{
|
||||
componentName: 'SelectField',
|
||||
id: 'node_k1ow3cc0',
|
||||
props: {
|
||||
fieldName: 'gender',
|
||||
hasClear: false,
|
||||
tips: {
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
mode: 'single',
|
||||
showSearch: false,
|
||||
autoWidth: true,
|
||||
labelTextAlign: 'right',
|
||||
placeholder: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'please select',
|
||||
'zh-CN': '请选择',
|
||||
type: 'i18n',
|
||||
},
|
||||
hasBorder: true,
|
||||
behavior: 'NORMAL',
|
||||
value: '',
|
||||
validation: [
|
||||
{
|
||||
type: 'required',
|
||||
},
|
||||
],
|
||||
__style__: {},
|
||||
fieldId: 'select_k1ow3h1x',
|
||||
notFoundContent: {
|
||||
use: 'zh-CN',
|
||||
type: 'i18n',
|
||||
},
|
||||
labelColOffset: 0,
|
||||
label: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'SelectField',
|
||||
'zh-CN': '性别',
|
||||
type: 'i18n',
|
||||
},
|
||||
__category__: 'form',
|
||||
labelColSpan: 4,
|
||||
wrapperColSpan: 0,
|
||||
wrapperColOffset: 0,
|
||||
hasSelectAll: false,
|
||||
hasArrow: true,
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
filterLocal: true,
|
||||
dataSource: [
|
||||
{
|
||||
defaultChecked: false,
|
||||
text: {
|
||||
'en-US': 'Option 1',
|
||||
'zh-CN': '男',
|
||||
type: 'i18n',
|
||||
__sid__: 'param_k1owc4tb',
|
||||
},
|
||||
__sid__: 'serial_k1owc4t1',
|
||||
value: 'M',
|
||||
sid: 'opt_k1owc4t2',
|
||||
},
|
||||
{
|
||||
defaultChecked: false,
|
||||
text: {
|
||||
'en-US': 'Option 2',
|
||||
'zh-CN': '女',
|
||||
type: 'i18n',
|
||||
__sid__: 'param_k1owc4tf',
|
||||
},
|
||||
__sid__: 'serial_k1owc4t2',
|
||||
value: 'F',
|
||||
sid: 'opt_k1owc4t3',
|
||||
},
|
||||
],
|
||||
__useMediator: 'value',
|
||||
labelTipsTypes: 'none',
|
||||
labelTipsIcon: '',
|
||||
labelTipsText: {
|
||||
type: 'i18n',
|
||||
use: 'zh-CN',
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
},
|
||||
searchDelay: 300,
|
||||
},
|
||||
condition: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Card',
|
||||
id: 'node_k1ow3cbl',
|
||||
props: {
|
||||
__slot__title: false,
|
||||
subTitle: {
|
||||
use: 'zh-CN',
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
__slot__subTitle: false,
|
||||
extra: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
className: 'card_kgaqfbm6',
|
||||
title: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'Title',
|
||||
'zh-CN': '部门信息',
|
||||
type: 'i18n',
|
||||
},
|
||||
__slot__extra: false,
|
||||
showHeadDivider: true,
|
||||
__style__: ':root {\n margin-bottom: 12px;\n}',
|
||||
showTitleBullet: true,
|
||||
contentHeight: '',
|
||||
fieldId: 'card_k1ow3h1m',
|
||||
dividerNoInset: false,
|
||||
},
|
||||
condition: true,
|
||||
children: [
|
||||
{
|
||||
componentName: 'CardContent',
|
||||
id: 'node_k1ow3cbm',
|
||||
props: {},
|
||||
condition: true,
|
||||
children: [
|
||||
{
|
||||
componentName: 'TextField',
|
||||
id: 'node_k1ow3cc4',
|
||||
props: {
|
||||
fieldName: 'department',
|
||||
hasClear: false,
|
||||
autoFocus: false,
|
||||
tips: {
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
trim: false,
|
||||
labelTextAlign: 'right',
|
||||
placeholder: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'please input',
|
||||
'zh-CN': '请输入',
|
||||
type: 'i18n',
|
||||
},
|
||||
state: '',
|
||||
behavior: 'NORMAL',
|
||||
value: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
addonBefore: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
validation: [],
|
||||
hasLimitHint: false,
|
||||
cutString: false,
|
||||
__style__: {},
|
||||
fieldId: 'textField_k1ow3h21',
|
||||
htmlType: 'input',
|
||||
autoHeight: false,
|
||||
labelColOffset: 0,
|
||||
label: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'TextField',
|
||||
'zh-CN': '所属部门',
|
||||
type: 'i18n',
|
||||
},
|
||||
__category__: 'form',
|
||||
labelColSpan: 4,
|
||||
wrapperColSpan: 0,
|
||||
rows: 4,
|
||||
addonAfter: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
wrapperColOffset: 0,
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
__useMediator: 'value',
|
||||
labelTipsTypes: 'none',
|
||||
labelTipsIcon: '',
|
||||
labelTipsText: {
|
||||
type: 'i18n',
|
||||
use: 'zh-CN',
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
},
|
||||
},
|
||||
condition: true,
|
||||
},
|
||||
{
|
||||
componentName: 'ColumnsLayout',
|
||||
id: 'node_k1ow3cc5',
|
||||
props: {
|
||||
layout: '6:6',
|
||||
columnGap: '20',
|
||||
rowGap: 0,
|
||||
__style__: {},
|
||||
fieldId: 'columns_k1ow3h22',
|
||||
},
|
||||
condition: true,
|
||||
children: [
|
||||
{
|
||||
componentName: 'Column',
|
||||
id: 'node_k1ow3cc6',
|
||||
props: {
|
||||
colSpan: '',
|
||||
__style__: {},
|
||||
fieldId: 'column_k1p1bnjo',
|
||||
},
|
||||
condition: true,
|
||||
children: [
|
||||
{
|
||||
componentName: 'TextField',
|
||||
id: 'node_k1ow3cc8',
|
||||
props: {
|
||||
fieldName: 'leader',
|
||||
hasClear: false,
|
||||
autoFocus: false,
|
||||
tips: {
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
trim: false,
|
||||
labelTextAlign: 'right',
|
||||
placeholder: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'please input',
|
||||
'zh-CN': '请输入',
|
||||
type: 'i18n',
|
||||
},
|
||||
state: '',
|
||||
behavior: 'NORMAL',
|
||||
value: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
addonBefore: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
validation: [],
|
||||
hasLimitHint: false,
|
||||
cutString: false,
|
||||
__style__: {},
|
||||
fieldId: 'textField_k1ow3h23',
|
||||
htmlType: 'input',
|
||||
autoHeight: false,
|
||||
labelColOffset: 0,
|
||||
label: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'TextField',
|
||||
'zh-CN': '主管',
|
||||
type: 'i18n',
|
||||
},
|
||||
__category__: 'form',
|
||||
labelColSpan: 4,
|
||||
wrapperColSpan: 0,
|
||||
rows: 4,
|
||||
addonAfter: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
wrapperColOffset: 0,
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
__useMediator: 'value',
|
||||
labelTipsTypes: 'none',
|
||||
labelTipsIcon: '',
|
||||
labelTipsText: {
|
||||
type: 'i18n',
|
||||
use: 'zh-CN',
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
},
|
||||
},
|
||||
condition: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Column',
|
||||
id: 'node_k1ow3cc7',
|
||||
props: {
|
||||
colSpan: '',
|
||||
__style__: {},
|
||||
fieldId: 'column_k1p1bnjp',
|
||||
},
|
||||
condition: true,
|
||||
children: [
|
||||
{
|
||||
componentName: 'TextField',
|
||||
id: 'node_k1ow3cc9',
|
||||
props: {
|
||||
fieldName: 'hrg',
|
||||
hasClear: false,
|
||||
autoFocus: false,
|
||||
tips: {
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
trim: false,
|
||||
labelTextAlign: 'right',
|
||||
placeholder: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'please input',
|
||||
'zh-CN': '请输入',
|
||||
type: 'i18n',
|
||||
},
|
||||
state: '',
|
||||
behavior: 'NORMAL',
|
||||
value: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
addonBefore: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
validation: [],
|
||||
hasLimitHint: false,
|
||||
cutString: false,
|
||||
__style__: {},
|
||||
fieldId: 'textField_k1ow3h24',
|
||||
htmlType: 'input',
|
||||
autoHeight: false,
|
||||
labelColOffset: 0,
|
||||
label: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'TextField',
|
||||
'zh-CN': 'HRG',
|
||||
type: 'i18n',
|
||||
},
|
||||
__category__: 'form',
|
||||
labelColSpan: 4,
|
||||
wrapperColSpan: 0,
|
||||
rows: 4,
|
||||
addonAfter: {
|
||||
use: 'zh-CN',
|
||||
'zh-CN': '',
|
||||
type: 'i18n',
|
||||
},
|
||||
wrapperColOffset: 0,
|
||||
size: 'medium',
|
||||
labelAlign: 'top',
|
||||
__useMediator: 'value',
|
||||
labelTipsTypes: 'none',
|
||||
labelTipsIcon: '',
|
||||
labelTipsText: {
|
||||
type: 'i18n',
|
||||
use: 'zh-CN',
|
||||
'en-US': '',
|
||||
'zh-CN': '',
|
||||
},
|
||||
},
|
||||
condition: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Div',
|
||||
id: 'node_k1ow3cbo',
|
||||
props: {
|
||||
className: 'div_kgaqfbm9',
|
||||
behavior: 'NORMAL',
|
||||
__style__:
|
||||
':root {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}',
|
||||
events: {},
|
||||
fieldId: 'div_k1ow3h1o',
|
||||
useFieldIdAsDomId: false,
|
||||
customClassName: '',
|
||||
},
|
||||
condition: true,
|
||||
children: [
|
||||
{
|
||||
componentName: 'Button',
|
||||
id: 'node_k1ow3cbn',
|
||||
props: {
|
||||
triggerEventsWhenLoading: false,
|
||||
onClick: {
|
||||
rawType: 'events',
|
||||
type: 'JSExpression',
|
||||
value: 'this.utils.legaoBuiltin.execEventFlow.bind(this, [this.submit])',
|
||||
events: [
|
||||
{
|
||||
name: 'submit',
|
||||
id: 'submit',
|
||||
params: {},
|
||||
type: 'actionRef',
|
||||
uuid: '1570966253282_0',
|
||||
},
|
||||
],
|
||||
},
|
||||
size: 'medium',
|
||||
baseIcon: '',
|
||||
otherIcon: '',
|
||||
className: 'button_kgaqfbm7',
|
||||
type: 'primary',
|
||||
behavior: 'NORMAL',
|
||||
loading: false,
|
||||
content: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'Button',
|
||||
'zh-CN': '提交',
|
||||
type: 'i18n',
|
||||
},
|
||||
__style__: ':root {\n margin-right: 16px;\n width: 80px\n}',
|
||||
fieldId: 'button_k1ow3h1n',
|
||||
},
|
||||
condition: true,
|
||||
},
|
||||
{
|
||||
componentName: 'Button',
|
||||
id: 'node_k1ow3cbp',
|
||||
props: {
|
||||
triggerEventsWhenLoading: false,
|
||||
size: 'medium',
|
||||
baseIcon: '',
|
||||
otherIcon: '',
|
||||
className: 'button_kgaqfbm8',
|
||||
type: 'normal',
|
||||
behavior: 'NORMAL',
|
||||
loading: false,
|
||||
content: {
|
||||
use: 'zh-CN',
|
||||
'en-US': 'Button',
|
||||
'zh-CN': '取消',
|
||||
type: 'i18n',
|
||||
},
|
||||
__style__: ':root {\n width: 80px;\n}',
|
||||
fieldId: 'button_k1ow3h1p',
|
||||
greeting: {
|
||||
// type: 'JSSlot',
|
||||
value: [{
|
||||
componentName: 'Text',
|
||||
props: {},
|
||||
}],
|
||||
},
|
||||
},
|
||||
condition: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'RootFooter',
|
||||
id: 'node_k1ow3cbc',
|
||||
props: {},
|
||||
condition: true,
|
||||
},
|
||||
],
|
||||
i18n: {
|
||||
'zh-CN': {
|
||||
'i18n-jwg27yo4': '你好',
|
||||
'i18n-jwg27yo3': '中国',
|
||||
},
|
||||
'en-US': {
|
||||
'i18n-jwg27yo4': 'Hello',
|
||||
'i18n-jwg27yo3': 'China',
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -1,90 +0,0 @@
|
||||
export default {
|
||||
componentName: 'Page',
|
||||
id: 'page',
|
||||
title: 'hey, i\' a page!',
|
||||
props: {
|
||||
extensions: {
|
||||
启用页头: true,
|
||||
},
|
||||
pageStyle: {
|
||||
backgroundColor: '#f2f3f5',
|
||||
},
|
||||
containerStyle: {},
|
||||
className: 'page_kgaqfbm4',
|
||||
templateVersion: '1.0.0',
|
||||
},
|
||||
lifeCycles: {
|
||||
constructor: {
|
||||
type: 'js',
|
||||
compiled:
|
||||
"function constructor() {\nvar module = { exports: {} };\nvar _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === 'function'){\n _this[item] = module.exports[item];\n }\n});\n\n}",
|
||||
source:
|
||||
"function constructor() {\nvar module = { exports: {} };\nvar _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === 'function'){\n _this[item] = module.exports[item];\n }\n});\n\n}",
|
||||
},
|
||||
},
|
||||
condition: true,
|
||||
css:
|
||||
'body{background-color:#f2f3f5}.card_kgaqfbm5 {\n margin-bottom: 12px;\n}.card_kgaqfbm6 {\n margin-bottom: 12px;\n}.button_kgaqfbm7 {\n margin-right: 16px;\n width: 80px\n}.button_kgaqfbm8 {\n width: 80px;\n}.div_kgaqfbm9 {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}',
|
||||
methods: {
|
||||
__initMethods__: {
|
||||
type: 'js',
|
||||
source: 'function (exports, module) { /*set actions code here*/ }',
|
||||
compiled: 'function (exports, module) { /*set actions code here*/ }',
|
||||
},
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Div',
|
||||
id: 'div',
|
||||
props: {
|
||||
className: 'div_kgaqfbm9',
|
||||
behavior: 'NORMAL',
|
||||
__style__:
|
||||
':root {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}',
|
||||
events: {},
|
||||
fieldId: 'div_k1ow3h1o',
|
||||
useFieldIdAsDomId: false,
|
||||
customClassName: {
|
||||
type: 'JSExpression',
|
||||
value: 'getFromSomewhere()',
|
||||
},
|
||||
customClassName2: {
|
||||
type: 'JSExpression',
|
||||
mock: { hi: 'mock' },
|
||||
value: 'getFromSomewhere()',
|
||||
},
|
||||
},
|
||||
extraPropA: 'haha',
|
||||
},
|
||||
{
|
||||
componentName: 'Div',
|
||||
id: 'div2',
|
||||
props: {
|
||||
className: 'div_kgaqfbm9',
|
||||
behavior: 'NORMAL',
|
||||
__style__:
|
||||
':root {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}',
|
||||
events: {},
|
||||
fieldId: 'div_k1ow3h1o',
|
||||
useFieldIdAsDomId: false,
|
||||
customClassName: '',
|
||||
},
|
||||
extraPropA: 'haha',
|
||||
},
|
||||
{
|
||||
componentName: 'Test',
|
||||
id: 'test',
|
||||
props: {
|
||||
className: 'div_kgaqfbm9',
|
||||
behavior: 'NORMAL',
|
||||
__style__:
|
||||
':root {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}',
|
||||
events: {},
|
||||
fieldId: 'div_k1ow3h1o',
|
||||
useFieldIdAsDomId: false,
|
||||
customClassName: '',
|
||||
},
|
||||
extraPropA: 'haha',
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -1,6 +0,0 @@
|
||||
export const mockConsoleError = jest.fn();
|
||||
export const mockConsoleWarn = jest.fn();
|
||||
// const mockConsoleInfo = jest.fn();
|
||||
console.error = mockConsoleError;
|
||||
console.warn = mockConsoleWarn;
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
if (!process.env.LISTENING_TO_UNHANDLED_REJECTION) {
|
||||
process.on('unhandledRejection', reason => {
|
||||
throw reason;
|
||||
});
|
||||
// Avoid memory leak by adding too many listeners
|
||||
process.env.LISTENING_TO_UNHANDLED_REJECTION = true;
|
||||
}
|
||||
28
packages/designer/tests/fixtures/window.ts
vendored
28
packages/designer/tests/fixtures/window.ts
vendored
@ -1,28 +0,0 @@
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
value: jest.fn().mockImplementation(query => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: jest.fn(), // deprecated
|
||||
removeListener: jest.fn(), // deprecated
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
})),
|
||||
});
|
||||
|
||||
Object.defineProperty(window, 'React', {
|
||||
writable: true,
|
||||
value: {},
|
||||
});
|
||||
|
||||
window.scrollTo = () => {};
|
||||
window.console.warn = () => {};
|
||||
const originalLog = window.console.log;
|
||||
window.console.log = (...args) => {
|
||||
// suppress boring warnings
|
||||
if (args[0]?.includes && args[0].includes('@babel/plugin-proposal-private-property-in-object')) return;
|
||||
originalLog.apply(window.console, args);
|
||||
};
|
||||
window.React = window.React || {};
|
||||
@ -1,252 +0,0 @@
|
||||
import '../../fixtures/window';
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import divMeta from '../../fixtures/component-metadata/div';
|
||||
import div2Meta from '../../fixtures/component-metadata/div2';
|
||||
import div3Meta from '../../fixtures/component-metadata/div3';
|
||||
import div4Meta from '../../fixtures/component-metadata/div4';
|
||||
import div5Meta from '../../fixtures/component-metadata/div5';
|
||||
import div6Meta from '../../fixtures/component-metadata/div6';
|
||||
import div7Meta from '../../fixtures/component-metadata/div7';
|
||||
import div8Meta from '../../fixtures/component-metadata/div8';
|
||||
import div9Meta from '../../fixtures/component-metadata/div9';
|
||||
import div10Meta from '../../fixtures/component-metadata/div10';
|
||||
import abcgroup from '../../fixtures/component-metadata/abcgroup';
|
||||
import abcitem from '../../fixtures/component-metadata/abcitem';
|
||||
import abcnode from '../../fixtures/component-metadata/abcnode';
|
||||
import abcoption from '../../fixtures/component-metadata/abcoption';
|
||||
import page2Meta from '../../fixtures/component-metadata/page2';
|
||||
import {
|
||||
ComponentMeta,
|
||||
isComponentMeta,
|
||||
ensureAList,
|
||||
buildFilter,
|
||||
} from '../../../src/component-meta';
|
||||
|
||||
|
||||
jest.mock('../../../src/designer/designer', () => {
|
||||
return {
|
||||
Designer: jest.fn().mockImplementation(() => {
|
||||
const { ComponentActions } = require('../../../src/component-actions');
|
||||
return {
|
||||
getGlobalComponentActions: () => [],
|
||||
componentActions: new ComponentActions(),
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let designer = null;
|
||||
beforeAll(() => {
|
||||
designer = new Designer({} as any);
|
||||
});
|
||||
|
||||
describe('组件元数据处理', () => {
|
||||
it('构造函数', () => {
|
||||
const meta = new ComponentMeta(designer, divMeta);
|
||||
expect(meta.isContainer).toBeTruthy();
|
||||
expect(isComponentMeta(meta)).toBeTruthy();
|
||||
expect(meta.acceptable).toBeFalsy();
|
||||
expect(meta.isRootComponent()).toBeFalsy();
|
||||
expect(meta.isModal).toBeFalsy();
|
||||
expect(meta.rootSelector).toBeUndefined();
|
||||
expect(meta.liveTextEditing).toBeUndefined();
|
||||
expect(meta.descriptor).toBeUndefined();
|
||||
expect(typeof meta.icon).toBe('function');
|
||||
expect(meta.getMetadata().title).toBe('容器');
|
||||
expect(meta.title).toEqual({ type: 'i18n', 'en-US': 'Div', 'zh-CN': '容器' });
|
||||
expect(meta.isMinimalRenderUnit).toBeFalsy();
|
||||
expect(meta.isTopFixed).toBeFalsy();
|
||||
|
||||
meta.setNpm({ package: '@ali/vc-div', componentName: 'Div' });
|
||||
expect(meta.npm).toEqual({ package: '@ali/vc-div', componentName: 'Div' });
|
||||
meta.npm = { package: '@ali/vc-div', componentName: 'Div' };
|
||||
expect(meta.npm).toEqual({ package: '@ali/vc-div', componentName: 'Div' });
|
||||
|
||||
|
||||
const mockFn = jest.fn();
|
||||
const offFn = meta.onMetadataChange(mockFn);
|
||||
meta.setMetadata(divMeta);
|
||||
expect(mockFn).toHaveBeenCalledTimes(1);
|
||||
offFn();
|
||||
meta.setMetadata(divMeta);
|
||||
// 不会再触发函数
|
||||
expect(mockFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('构造函数 - 兼容场景(title 是个普通对象)', () => {
|
||||
const meta = new ComponentMeta(designer, div2Meta);
|
||||
expect(meta.title).toEqual('容器');
|
||||
|
||||
expect(meta.isTopFixed).toBeTruthy();
|
||||
});
|
||||
|
||||
it('构造函数 - 兼容场景(title fallback 到 componentName)', () => {
|
||||
const meta = new ComponentMeta(designer, div3Meta);
|
||||
expect(meta.title).toEqual('Div');
|
||||
});
|
||||
|
||||
it('构造函数 - 兼容场景(configure 是个数组)', () => {
|
||||
const meta = new ComponentMeta(designer, div4Meta);
|
||||
expect(meta.configure).toEqual(div4Meta.configure);
|
||||
});
|
||||
|
||||
it('构造函数 - 兼容场景(使用 experimental)', () => {
|
||||
const meta = new ComponentMeta(designer, div6Meta);
|
||||
expect(meta.getMetadata().configure.advanced.initials).toHaveLength(9);
|
||||
});
|
||||
|
||||
it('构造函数 - 兼容场景(没有 configure.component)', () => {
|
||||
const meta = new ComponentMeta(designer, div7Meta);
|
||||
expect(meta.isContainer).toBeFalsy();
|
||||
expect(meta.isModal).toBeFalsy();
|
||||
});
|
||||
|
||||
it('构造函数 - 兼容场景(没有 configure)', () => {
|
||||
const meta = new ComponentMeta(designer, div8Meta);
|
||||
expect(meta.configure).toEqual([]);
|
||||
});
|
||||
|
||||
it('构造函数 - 兼容场景(没有 npm)', () => {
|
||||
const meta = new ComponentMeta(designer, div9Meta);
|
||||
expect(meta.npm).toBeUndefined();
|
||||
|
||||
meta.setNpm({ package: '@ali/vc-div', componentName: 'Div' });
|
||||
expect(meta.npm).toEqual({ package: '@ali/vc-div', componentName: 'Div' });
|
||||
});
|
||||
|
||||
it('availableActions', () => {
|
||||
const meta = new ComponentMeta(designer, divMeta);
|
||||
expect(meta.availableActions).toHaveLength(5);
|
||||
expect(meta.availableActions[0].name).toBe('remove');
|
||||
expect(meta.availableActions[1].name).toBe('hide');
|
||||
expect(meta.availableActions[2].name).toBe('copy');
|
||||
|
||||
designer.componentActions.removeBuiltinComponentAction('remove');
|
||||
expect(meta.availableActions).toHaveLength(4);
|
||||
expect(meta.availableActions[0].name).toBe('hide');
|
||||
expect(meta.availableActions[1].name).toBe('copy');
|
||||
|
||||
designer.componentActions.addBuiltinComponentAction({
|
||||
name: 'new',
|
||||
content: {
|
||||
action() {},
|
||||
},
|
||||
});
|
||||
expect(meta.availableActions).toHaveLength(5);
|
||||
expect(meta.availableActions[0].name).toBe('hide');
|
||||
expect(meta.availableActions[1].name).toBe('copy');
|
||||
expect(meta.availableActions[4].name).toBe('new');
|
||||
});
|
||||
|
||||
it('availableActions - disableBehaviors: *', () => {
|
||||
const meta = new ComponentMeta(designer, div5Meta);
|
||||
expect(meta.availableActions).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('availableActions - rootCompoment', () => {
|
||||
const meta = new ComponentMeta(designer, page2Meta);
|
||||
// (hide + new) left
|
||||
expect(meta.availableActions).toHaveLength(2);
|
||||
});
|
||||
|
||||
describe('checkNesting', () => {
|
||||
const mockNode = (componentName) => {
|
||||
return {
|
||||
internalToShellNode() {
|
||||
return {
|
||||
componentName,
|
||||
};
|
||||
},
|
||||
isNode: true,
|
||||
};
|
||||
};
|
||||
const mockNodeForm = mockNode('Form');
|
||||
const mockNodeImage = mockNode('Image');
|
||||
const mockNodeDiv = mockNode('Div');
|
||||
it('checkNestingUp', () => {
|
||||
const meta1 = new ComponentMeta(designer, divMeta);
|
||||
// 没有配置 parentWhitelist,判断默认为 true
|
||||
expect(meta1.checkNestingUp(mockNodeDiv, mockNodeDiv)).toBeTruthy();
|
||||
|
||||
const meta2 = new ComponentMeta(designer, div10Meta);
|
||||
expect(meta2.checkNestingUp(mockNodeDiv, mockNodeForm)).toBeTruthy();
|
||||
expect(meta2.checkNestingUp(mockNodeDiv, mockNodeDiv)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('checkNestingDown', () => {
|
||||
const meta1 = new ComponentMeta(designer, divMeta);
|
||||
// 没有配置 childWhitelist,判断默认为 true
|
||||
expect(meta1.checkNestingDown(mockNodeDiv, mockNodeDiv)).toBeTruthy();
|
||||
|
||||
const meta2 = new ComponentMeta(designer, div10Meta);
|
||||
expect(meta2.checkNestingDown(mockNodeDiv, mockNodeForm)).toBeFalsy();
|
||||
expect(meta2.checkNestingDown(mockNodeDiv, mockNodeImage)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('组件元数据 transducers', () => {
|
||||
it('legacyIssues', () => {
|
||||
const legacyMeta: any = {
|
||||
...divMeta,
|
||||
devMode: 'procode',
|
||||
};
|
||||
const meta = new ComponentMeta(designer, legacyMeta);
|
||||
const metadata = meta.getMetadata();
|
||||
expect(metadata.devMode).toBe('proCode');
|
||||
});
|
||||
});
|
||||
|
||||
describe('帮助函数', () => {
|
||||
it('ensureAList', () => {
|
||||
expect(ensureAList()).toBeNull();
|
||||
expect(ensureAList(1)).toBeNull();
|
||||
expect(ensureAList([])).toBeNull();
|
||||
expect(ensureAList('copy lock')).toEqual(['copy', 'lock']);
|
||||
expect(ensureAList(['copy', 'lock'])).toEqual(['copy', 'lock']);
|
||||
});
|
||||
|
||||
it('buildFilter', () => {
|
||||
const mockFn = () => {};
|
||||
expect(buildFilter()).toBeNull();
|
||||
expect(buildFilter([])).toBeNull();
|
||||
expect(buildFilter(mockFn)).toBe(mockFn);
|
||||
|
||||
const mockRE = /xxx/;
|
||||
const filter = buildFilter(mockRE);
|
||||
expect(filter({ componentName: 'xxx' })).toBeTruthy();
|
||||
expect(filter({ componentName: 'yyy' })).toBeFalsy();
|
||||
|
||||
expect(buildFilter('xxx yyy')({ componentName: 'xxx' })).toBeTruthy();
|
||||
expect(buildFilter('xxx yyy')({ componentName: 'zzz' })).toBeFalsy();
|
||||
});
|
||||
|
||||
it('registerMetadataTransducer', () => {
|
||||
expect(designer.componentActions.getRegisteredMetadataTransducers()).toHaveLength(2);
|
||||
// 插入到 legacy-issues 和 component-defaults 的中间
|
||||
designer.componentActions.registerMetadataTransducer((metadata) => metadata, 3, 'noop');
|
||||
expect(designer.componentActions.getRegisteredMetadataTransducers()).toHaveLength(3);
|
||||
|
||||
designer.componentActions.registerMetadataTransducer((metadata) => metadata);
|
||||
expect(designer.componentActions.getRegisteredMetadataTransducers()).toHaveLength(4);
|
||||
});
|
||||
|
||||
it('modifyBuiltinComponentAction', () => {
|
||||
designer.componentActions.modifyBuiltinComponentAction('copy', (action) => {
|
||||
expect(action.name).toBe('copy');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('transducers', () => {
|
||||
it('componentDefaults', () => {
|
||||
const meta1 = new ComponentMeta(designer, abcgroup);
|
||||
const meta2 = new ComponentMeta(designer, abcitem);
|
||||
const meta3 = new ComponentMeta(designer, abcnode);
|
||||
const meta4 = new ComponentMeta(designer, abcoption);
|
||||
expect(meta1.getMetadata().configure.component.nestingRule.childWhitelist).toEqual(['Abc']);
|
||||
expect(meta2.getMetadata().configure.component.nestingRule.parentWhitelist).toEqual(['Abc']);
|
||||
expect(meta3.getMetadata().configure.component.nestingRule.parentWhitelist).toEqual(['Abc', 'Abc.Node']);
|
||||
expect(meta4.getMetadata().configure.component.nestingRule.parentWhitelist).toEqual(['Abc']);
|
||||
});
|
||||
});
|
||||
@ -1,7 +0,0 @@
|
||||
import '../fixtures/window';
|
||||
import { isSimulatorHost } from '../../src/simulator';
|
||||
|
||||
it('isSimulatorHost', () => {
|
||||
expect(isSimulatorHost({ isSimulator: true })).toBeTruthy();
|
||||
expect(isSimulatorHost({ a: 1 })).toBeFalsy();
|
||||
});
|
||||
@ -1,529 +0,0 @@
|
||||
import '../fixtures/window';
|
||||
import { Editor, engineConfig } from '@alilc/lowcode-editor-core';
|
||||
import { LowCodePluginManager } from '../../src/plugin/plugin-manager';
|
||||
import { IPublicModelPluginContext, IPublicApiPlugins } from '@alilc/lowcode-types';
|
||||
import { ILowCodePluginContextPrivate } from '../../src/plugin/plugin-types';
|
||||
|
||||
const editor = new Editor();
|
||||
let contextApiAssembler;
|
||||
|
||||
describe('plugin 测试', () => {
|
||||
let pluginManager: IPublicApiPlugins;
|
||||
beforeEach(() => {
|
||||
contextApiAssembler = {
|
||||
assembleApis(context: ILowCodePluginContextPrivate){
|
||||
context.plugins = pluginManager as IPublicApiPlugins;
|
||||
// mock set apis
|
||||
}
|
||||
};
|
||||
pluginManager = new LowCodePluginManager(contextApiAssembler).toProxy();
|
||||
});
|
||||
afterEach(() => {
|
||||
pluginManager.dispose();
|
||||
});
|
||||
|
||||
it('注册插件,插件参数生成函数能被调用,且能拿到正确的 ctx ', () => {
|
||||
const mockFn = jest.fn();
|
||||
const creator2 = (ctx: IPublicModelPluginContext) => {
|
||||
mockFn(ctx);
|
||||
return {
|
||||
init: jest.fn(),
|
||||
};
|
||||
};
|
||||
creator2.pluginName = 'demo1';
|
||||
pluginManager.register(creator2);
|
||||
|
||||
const [expectedCtx] = mockFn.mock.calls[0];
|
||||
expect(expectedCtx).toHaveProperty('project');
|
||||
expect(expectedCtx).toHaveProperty('setters');
|
||||
expect(expectedCtx).toHaveProperty('material');
|
||||
expect(expectedCtx).toHaveProperty('hotkey');
|
||||
expect(expectedCtx).toHaveProperty('plugins');
|
||||
expect(expectedCtx).toHaveProperty('skeleton');
|
||||
expect(expectedCtx).toHaveProperty('logger');
|
||||
expect(expectedCtx).toHaveProperty('config');
|
||||
expect(expectedCtx).toHaveProperty('event');
|
||||
expect(expectedCtx).toHaveProperty('preference');
|
||||
});
|
||||
|
||||
it('注册插件,调用插件 init 方法', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const creator2 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: mockFn,
|
||||
exports() {
|
||||
return {
|
||||
x: 1,
|
||||
y: 2,
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
creator2.pluginName = 'demo1';
|
||||
pluginManager.register(creator2);
|
||||
await pluginManager.init();
|
||||
expect(pluginManager.size).toBe(1);
|
||||
expect(pluginManager.has('demo1')).toBeTruthy();
|
||||
expect(pluginManager.get('demo1')!.isInited()).toBeTruthy();
|
||||
expect(pluginManager.demo1).toBeTruthy();
|
||||
expect(pluginManager.demo1.x).toBe(1);
|
||||
expect(pluginManager.demo1.y).toBe(2);
|
||||
expect(pluginManager.demo1.z).toBeUndefined();
|
||||
expect(mockFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('注册插件,调用 setDisabled 方法', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const creator2 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: mockFn,
|
||||
};
|
||||
};
|
||||
creator2.pluginName = 'demo1';
|
||||
|
||||
pluginManager.register(creator2);
|
||||
await pluginManager.init();
|
||||
expect(pluginManager.demo1).toBeTruthy();
|
||||
pluginManager.setDisabled('demo1', true);
|
||||
expect(pluginManager.demo1).toBeUndefined();
|
||||
});
|
||||
|
||||
it('注册插件,调用 plugin.setDisabled 方法', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const creator2 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: mockFn,
|
||||
};
|
||||
};
|
||||
creator2.pluginName = 'demo1';
|
||||
|
||||
pluginManager.register(creator2);
|
||||
await pluginManager.init();
|
||||
expect(pluginManager.demo1).toBeTruthy();
|
||||
pluginManager.get('demo1').setDisabled();
|
||||
expect(pluginManager.demo1).toBeUndefined();
|
||||
});
|
||||
|
||||
it('删除插件,调用插件 destroy 方法', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const creator2 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: jest.fn(),
|
||||
destroy: mockFn,
|
||||
};
|
||||
};
|
||||
creator2.pluginName = 'demo1';
|
||||
pluginManager.register(creator2);
|
||||
|
||||
await pluginManager.init();
|
||||
await pluginManager.delete('demo1');
|
||||
expect(mockFn).toHaveBeenCalled();
|
||||
await pluginManager.delete('non-existing');
|
||||
});
|
||||
|
||||
describe('dependencies 依赖', () => {
|
||||
it('dependencies 依赖', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const creator21 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: () => mockFn('demo1'),
|
||||
};
|
||||
};
|
||||
creator21.pluginName = 'demo1';
|
||||
creator21.meta = {
|
||||
dependencies: ['demo2'],
|
||||
};
|
||||
pluginManager.register(creator21);
|
||||
const creator22 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: () => mockFn('demo2'),
|
||||
};
|
||||
};
|
||||
creator22.pluginName = 'demo2';
|
||||
pluginManager.register(creator22);
|
||||
|
||||
await pluginManager.init();
|
||||
expect(mockFn).toHaveBeenNthCalledWith(1, 'demo2');
|
||||
expect(mockFn).toHaveBeenNthCalledWith(2, 'demo1');
|
||||
});
|
||||
|
||||
it('dependencies 依赖 - string', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const creator21 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: () => mockFn('demo1'),
|
||||
};
|
||||
};
|
||||
creator21.pluginName = 'demo1';
|
||||
creator21.meta = {
|
||||
dependencies: 'demo2',
|
||||
};
|
||||
pluginManager.register(creator21);
|
||||
const creator22 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: () => mockFn('demo2'),
|
||||
};
|
||||
};
|
||||
creator22.pluginName = 'demo2';
|
||||
pluginManager.register(creator22);
|
||||
|
||||
await pluginManager.init();
|
||||
expect(mockFn).toHaveBeenNthCalledWith(1, 'demo2');
|
||||
expect(mockFn).toHaveBeenNthCalledWith(2, 'demo1');
|
||||
});
|
||||
|
||||
it('dependencies 依赖 - 兼容 dep', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const creator21 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
dep: ['demo4'],
|
||||
init: () => mockFn('demo3'),
|
||||
};
|
||||
};
|
||||
creator21.pluginName = 'demo3';
|
||||
pluginManager.register(creator21);
|
||||
const creator22 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: () => mockFn('demo4'),
|
||||
};
|
||||
};
|
||||
creator22.pluginName = 'demo4';
|
||||
pluginManager.register(creator22);
|
||||
|
||||
await pluginManager.init();
|
||||
expect(mockFn).toHaveBeenNthCalledWith(1, 'demo4');
|
||||
expect(mockFn).toHaveBeenNthCalledWith(2, 'demo3');
|
||||
});
|
||||
|
||||
it('dependencies 依赖 - 兼容 dep & string', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const creator21 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
dep: 'demo4',
|
||||
init: () => mockFn('demo3'),
|
||||
};
|
||||
};
|
||||
creator21.pluginName = 'demo3';
|
||||
pluginManager.register(creator21);
|
||||
const creator22 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: () => mockFn('demo4'),
|
||||
};
|
||||
};
|
||||
creator22.pluginName = 'demo4';
|
||||
pluginManager.register(creator22);
|
||||
|
||||
await pluginManager.init();
|
||||
expect(mockFn).toHaveBeenNthCalledWith(1, 'demo4');
|
||||
expect(mockFn).toHaveBeenNthCalledWith(2, 'demo3');
|
||||
});
|
||||
});
|
||||
|
||||
it('version 依赖', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const creator21 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: () => mockFn('demo1'),
|
||||
};
|
||||
};
|
||||
creator21.pluginName = 'demo1';
|
||||
creator21.meta = {
|
||||
engines: {
|
||||
lowcodeEngine: '^1.1.0',
|
||||
},
|
||||
};
|
||||
engineConfig.set('ENGINE_VERSION', '1.0.1');
|
||||
|
||||
console.log('version: ', engineConfig.get('ENGINE_VERSION'));
|
||||
// not match should skip
|
||||
pluginManager.register(creator21).catch((e) => {
|
||||
expect(e).toEqual(
|
||||
new Error(
|
||||
'plugin demo1 skipped, engine check failed, current engine version is 1.0.1, meta.engines.lowcodeEngine is ^1.1.0',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
expect(pluginManager.plugins.length).toBe(0);
|
||||
|
||||
const creator22 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: () => mockFn('demo2'),
|
||||
};
|
||||
};
|
||||
creator22.pluginName = 'demo2';
|
||||
creator22.meta = {
|
||||
engines: {
|
||||
lowcodeEngine: '^1.0.1',
|
||||
},
|
||||
};
|
||||
|
||||
engineConfig.set('ENGINE_VERSION', '1.0.3');
|
||||
pluginManager.register(creator22);
|
||||
expect(pluginManager.plugins.length).toBe(1);
|
||||
|
||||
const creator23 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: () => mockFn('demo3'),
|
||||
};
|
||||
};
|
||||
creator23.pluginName = 'demo3';
|
||||
creator23.meta = {
|
||||
engines: {
|
||||
lowcodeEngine: '1.x',
|
||||
},
|
||||
};
|
||||
engineConfig.set('ENGINE_VERSION', '1.1.1');
|
||||
pluginManager.register(creator23);
|
||||
expect(pluginManager.plugins.length).toBe(2);
|
||||
});
|
||||
|
||||
it('autoInit 功能', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const creator2 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: mockFn,
|
||||
};
|
||||
};
|
||||
creator2.pluginName = 'demo1';
|
||||
await pluginManager.register(creator2, { autoInit: true });
|
||||
expect(mockFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('插件不会重复 init,除非强制重新 init', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const creator2 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
name: 'demo1',
|
||||
init: mockFn,
|
||||
};
|
||||
};
|
||||
creator2.pluginName = 'demo1';
|
||||
pluginManager.register(creator2);
|
||||
await pluginManager.init();
|
||||
expect(mockFn).toHaveBeenCalledTimes(1);
|
||||
|
||||
pluginManager.get('demo1')!.init();
|
||||
expect(mockFn).toHaveBeenCalledTimes(1);
|
||||
|
||||
pluginManager.get('demo1')!.init(true);
|
||||
expect(mockFn).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('默认情况不允许重复注册', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const mockPlugin = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: mockFn,
|
||||
};
|
||||
};
|
||||
mockPlugin.pluginName = 'demoDuplicated';
|
||||
pluginManager.register(mockPlugin);
|
||||
pluginManager.register(mockPlugin).catch((e) => {
|
||||
expect(e).toEqual(new Error('Plugin with name demoDuplicated exists'));
|
||||
});
|
||||
await pluginManager.init();
|
||||
});
|
||||
|
||||
it('插件增加 override 参数时可以重复注册', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const mockPlugin = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: mockFn,
|
||||
};
|
||||
};
|
||||
mockPlugin.pluginName = 'demoOverride';
|
||||
pluginManager.register(mockPlugin);
|
||||
pluginManager.register(mockPlugin, { override: true });
|
||||
await pluginManager.init();
|
||||
});
|
||||
|
||||
it('插件增加 override 参数时可以重复注册, 被覆盖的如果已初始化,会被销毁', async () => {
|
||||
const mockInitFn = jest.fn();
|
||||
const mockDestroyFn = jest.fn();
|
||||
const mockPlugin = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
init: mockInitFn,
|
||||
destroy: mockDestroyFn,
|
||||
};
|
||||
};
|
||||
mockPlugin.pluginName = 'demoOverride';
|
||||
await pluginManager.register(mockPlugin, { autoInit: true });
|
||||
expect(mockInitFn).toHaveBeenCalledTimes(1);
|
||||
await pluginManager.register(mockPlugin, { override: true });
|
||||
expect(mockDestroyFn).toHaveBeenCalledTimes(1);
|
||||
await pluginManager.init();
|
||||
});
|
||||
|
||||
it('dispose 方法', async () => {
|
||||
const creator2 = (ctx: IPublicModelPluginContext) => {
|
||||
return {};
|
||||
};
|
||||
creator2.pluginName = 'demo1';
|
||||
pluginManager.register(creator2);
|
||||
await pluginManager.init();
|
||||
const plugin = pluginManager.get('demo1')!;
|
||||
await plugin.dispose();
|
||||
|
||||
expect(pluginManager.has('demo1')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('getAll 方法', async () => {
|
||||
const creator2 = (ctx: IPublicModelPluginContext) => {
|
||||
return {};
|
||||
};
|
||||
creator2.pluginName = 'demo1';
|
||||
pluginManager.register(creator2);
|
||||
await pluginManager.init();
|
||||
|
||||
expect(pluginManager.getAll()).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('getPluginPreference 方法 - null', async () => {
|
||||
const creator2 = (ctx: IPublicModelPluginContext) => {
|
||||
return {};
|
||||
};
|
||||
creator2.pluginName = 'demo1';
|
||||
pluginManager.register(creator2);
|
||||
await pluginManager.init();
|
||||
|
||||
expect(pluginManager.getPluginPreference()).toBeNull();
|
||||
});
|
||||
|
||||
it('getPluginPreference 方法', async () => {
|
||||
const creator2 = (ctx: IPublicModelPluginContext) => {
|
||||
return {};
|
||||
};
|
||||
const preference = new Map();
|
||||
preference.set('demo1', { a: 1, b: 2 });
|
||||
creator2.pluginName = 'demo1';
|
||||
pluginManager.register(creator2);
|
||||
await pluginManager.init(preference);
|
||||
|
||||
expect(pluginManager.getPluginPreference('demo1')).toEqual({ a: 1, b: 2 });
|
||||
});
|
||||
|
||||
it('注册插件,调用插件 init 方法并传入 preference,可以成功获取', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const mockFnForCtx = jest.fn();
|
||||
const mockFnForCtx2 = jest.fn();
|
||||
const mockPreference = new Map();
|
||||
mockPreference.set('demo1', {
|
||||
key1: 'value for key1',
|
||||
key2: false,
|
||||
key3: 123,
|
||||
key5: 'value for key5, but declared, should not work',
|
||||
});
|
||||
|
||||
const creator2 = (ctx: IPublicModelPluginContext) => {
|
||||
mockFnForCtx(ctx);
|
||||
return {
|
||||
init: jest.fn(),
|
||||
};
|
||||
};
|
||||
creator2.pluginName = 'demo1';
|
||||
creator2.meta = {
|
||||
preferenceDeclaration: {
|
||||
title: 'demo1的的参数定义',
|
||||
properties: [
|
||||
{
|
||||
key: 'key1',
|
||||
type: 'string',
|
||||
description: 'this is description for key1',
|
||||
},
|
||||
{
|
||||
key: 'key2',
|
||||
type: 'boolean',
|
||||
description: 'this is description for key2',
|
||||
},
|
||||
{
|
||||
key: 'key3',
|
||||
type: 'number',
|
||||
description: 'this is description for key3',
|
||||
},
|
||||
{
|
||||
key: 'key4',
|
||||
type: 'string',
|
||||
description: 'this is description for key4',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const creator22 = (ctx: IPublicModelPluginContext) => {
|
||||
mockFnForCtx2(ctx);
|
||||
return {
|
||||
init: jest.fn(),
|
||||
};
|
||||
};
|
||||
creator22.pluginName = 'demo2';
|
||||
creator22.meta = {
|
||||
preferenceDeclaration: {
|
||||
title: 'demo1的的参数定义',
|
||||
properties: [
|
||||
{
|
||||
key: 'key1',
|
||||
type: 'string',
|
||||
description: 'this is description for key1',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
pluginManager.register(creator2);
|
||||
pluginManager.register(creator22);
|
||||
expect(mockFnForCtx).toHaveBeenCalledTimes(1);
|
||||
|
||||
await pluginManager.init(mockPreference);
|
||||
// creator2 only get excuted once
|
||||
expect(mockFnForCtx).toHaveBeenCalledTimes(1);
|
||||
|
||||
const [expectedCtx, expectedOptions] = mockFnForCtx.mock.calls[0];
|
||||
expect(expectedCtx).toHaveProperty('preference');
|
||||
|
||||
// test normal case
|
||||
expect(expectedCtx.preference.getPreferenceValue('key1', 'default')).toBe('value for key1');
|
||||
|
||||
// test default value logic
|
||||
expect(expectedCtx.preference.getPreferenceValue('key4', 'default for key4')).toBe(
|
||||
'default for key4',
|
||||
);
|
||||
|
||||
// test undeclared key
|
||||
expect(expectedCtx.preference.getPreferenceValue('key5', 'default for key5')).toBeUndefined();
|
||||
|
||||
// no preference defined
|
||||
const [expectedCtx2] = mockFnForCtx2.mock.calls[0];
|
||||
expect(expectedCtx2.preference.getPreferenceValue('key1')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('注册插件,没有填写 pluginName,默认值为 anonymous', async () => {
|
||||
const mockFn = jest.fn();
|
||||
|
||||
const creator2 = (ctx: IPublicModelPluginContext) => {
|
||||
return {
|
||||
name: 'xxx',
|
||||
init: () => mockFn('anonymous'),
|
||||
};
|
||||
};
|
||||
await pluginManager.register(creator2);
|
||||
expect(pluginManager.get('anonymous')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('自定义/扩展 plugin context', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const mockFn2 = jest.fn();
|
||||
|
||||
const creator2 = (ctx: IPublicModelPluginContext) => {
|
||||
mockFn2(ctx);
|
||||
return {
|
||||
init: () => mockFn('anonymous'),
|
||||
};
|
||||
};
|
||||
creator2.pluginName = 'yyy';
|
||||
editor.set('enhancePluginContextHook', (originalContext) => {
|
||||
originalContext.newProp = 1;
|
||||
});
|
||||
await pluginManager.register(creator2);
|
||||
const [expectedCtx] = mockFn2.mock.calls[0];
|
||||
expect(expectedCtx).toHaveProperty('newProp');
|
||||
});
|
||||
});
|
||||
@ -1,85 +0,0 @@
|
||||
import '../fixtures/window';
|
||||
import { isValidPreferenceKey, filterValidOptions } from '../../src/plugin/plugin-utils';
|
||||
|
||||
describe('plugin utils 测试', () => {
|
||||
it('isValidPreferenceKey', () => {
|
||||
expect(isValidPreferenceKey('x')).toBeFalsy();
|
||||
expect(isValidPreferenceKey('x', { properties: {} })).toBeFalsy();
|
||||
expect(isValidPreferenceKey('x', { properties: 1 })).toBeFalsy();
|
||||
expect(isValidPreferenceKey('x', { properties: 'str' })).toBeFalsy();
|
||||
expect(isValidPreferenceKey('x', { properties: [] })).toBeFalsy();
|
||||
expect(
|
||||
isValidPreferenceKey('x', {
|
||||
title: 'title',
|
||||
properties: [
|
||||
{
|
||||
key: 'y',
|
||||
type: 'string',
|
||||
description: 'x desc',
|
||||
},
|
||||
],
|
||||
}),
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
isValidPreferenceKey('x', {
|
||||
title: 'title',
|
||||
properties: [
|
||||
{
|
||||
key: 'x',
|
||||
type: 'string',
|
||||
description: 'x desc',
|
||||
},
|
||||
],
|
||||
}),
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('filterValidOptions', () => {
|
||||
const mockDeclaration = {
|
||||
title: 'title',
|
||||
properties: [
|
||||
{
|
||||
key: 'x',
|
||||
type: 'string',
|
||||
description: 'x desc',
|
||||
},
|
||||
{
|
||||
key: 'y',
|
||||
type: 'string',
|
||||
description: 'y desc',
|
||||
},
|
||||
{
|
||||
key: 'z',
|
||||
type: 'string',
|
||||
description: 'z desc',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(filterValidOptions()).toBeUndefined();
|
||||
expect(filterValidOptions(1)).toBe(1);
|
||||
expect(filterValidOptions({
|
||||
x: 1,
|
||||
y: 2,
|
||||
}, mockDeclaration)).toEqual({
|
||||
x: 1,
|
||||
y: 2,
|
||||
});
|
||||
expect(filterValidOptions({
|
||||
x: 1,
|
||||
y: undefined,
|
||||
}, mockDeclaration)).toEqual({
|
||||
x: 1,
|
||||
});
|
||||
expect(filterValidOptions({
|
||||
x: 1,
|
||||
z: null,
|
||||
}, mockDeclaration)).toEqual({
|
||||
x: 1,
|
||||
});
|
||||
expect(filterValidOptions({
|
||||
a: 1,
|
||||
}, mockDeclaration)).toEqual({
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,128 +0,0 @@
|
||||
import sequencify, { sequence } from '../../src/plugin/sequencify';
|
||||
|
||||
describe('sequence', () => {
|
||||
it('handles tasks with no dependencies', () => {
|
||||
const tasks = {
|
||||
task1: { name: 'Task 1', dep: [] },
|
||||
task2: { name: 'Task 2', dep: [] }
|
||||
};
|
||||
const results = [];
|
||||
const missing = [];
|
||||
const recursive = [];
|
||||
sequence({ tasks, names: ['task1', 'task2'], results, missing, recursive, nest: [] });
|
||||
|
||||
expect(results).toEqual(['task1', 'task2']);
|
||||
expect(missing).toEqual([]);
|
||||
expect(recursive).toEqual([]);
|
||||
});
|
||||
|
||||
it('correctly orders tasks based on dependencies', () => {
|
||||
const tasks = {
|
||||
task1: { name: 'Task 1', dep: [] },
|
||||
task2: { name: 'Task 2', dep: ['task1'] }
|
||||
};
|
||||
const results = [];
|
||||
const missing = [];
|
||||
const recursive = [];
|
||||
sequence({ tasks, names: ['task2', 'task1'], results, missing, recursive, nest: [] });
|
||||
|
||||
expect(results).toEqual(['task1', 'task2']);
|
||||
expect(missing).toEqual([]);
|
||||
expect(recursive).toEqual([]);
|
||||
});
|
||||
|
||||
it('identifies missing tasks', () => {
|
||||
const tasks = {
|
||||
task1: { name: 'Task 1', dep: [] }
|
||||
};
|
||||
const results = [];
|
||||
const missing = [];
|
||||
const recursive = [];
|
||||
const nest = []
|
||||
sequence({ tasks, names: ['task2'], results, missing, recursive, nest });
|
||||
|
||||
expect(results).toEqual(['task2']);
|
||||
expect(missing).toEqual(['task2']);
|
||||
expect(recursive).toEqual([]);
|
||||
expect(nest).toEqual([]);
|
||||
});
|
||||
|
||||
it('detects recursive dependencies', () => {
|
||||
const tasks = {
|
||||
task1: { name: 'Task 1', dep: ['task2'] },
|
||||
task2: { name: 'Task 2', dep: ['task1'] }
|
||||
};
|
||||
const results = [];
|
||||
const missing = [];
|
||||
const recursive = [];
|
||||
const nest = []
|
||||
sequence({ tasks, names: ['task1', 'task2'], results, missing, recursive, nest });
|
||||
|
||||
expect(results).toEqual(['task1', 'task2', 'task1']);
|
||||
expect(missing).toEqual([]);
|
||||
expect(recursive).toEqual([['task1', 'task2', 'task1']]);
|
||||
expect(nest).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sequence', () => {
|
||||
|
||||
it('should return tasks in sequence without dependencies', () => {
|
||||
const tasks = {
|
||||
task1: { name: 'Task 1', dep: [] },
|
||||
task2: { name: 'Task 2', dep: [] },
|
||||
task3: { name: 'Task 3', dep: [] }
|
||||
};
|
||||
const names = ['task1', 'task2', 'task3'];
|
||||
const expected = {
|
||||
sequence: ['task1', 'task2', 'task3'],
|
||||
missingTasks: [],
|
||||
recursiveDependencies: []
|
||||
};
|
||||
expect(sequencify(tasks, names)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should handle tasks with dependencies', () => {
|
||||
const tasks = {
|
||||
task1: { name: 'Task 1', dep: [] },
|
||||
task2: { name: 'Task 2', dep: ['task1'] },
|
||||
task3: { name: 'Task 3', dep: ['task2'] }
|
||||
};
|
||||
const names = ['task3', 'task2', 'task1'];
|
||||
const expected = {
|
||||
sequence: ['task1', 'task2', 'task3'],
|
||||
missingTasks: [],
|
||||
recursiveDependencies: []
|
||||
};
|
||||
expect(sequencify(tasks, names)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should identify missing tasks', () => {
|
||||
const tasks = {
|
||||
task1: { name: 'Task 1', dep: [] },
|
||||
task2: { name: 'Task 2', dep: ['task3'] } // task3 is missing
|
||||
};
|
||||
const names = ['task1', 'task2'];
|
||||
const expected = {
|
||||
sequence: [],
|
||||
missingTasks: ['task2.task3'],
|
||||
recursiveDependencies: []
|
||||
};
|
||||
expect(sequencify(tasks, names)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should detect recursive dependencies', () => {
|
||||
const tasks = {
|
||||
task1: { name: 'Task 1', dep: ['task2'] },
|
||||
task2: { name: 'Task 2', dep: ['task1'] } // Recursive dependency
|
||||
};
|
||||
const names = ['task1', 'task2'];
|
||||
const expected = {
|
||||
sequence: [],
|
||||
missingTasks: [],
|
||||
recursiveDependencies: [['task1', 'task2', 'task1']]
|
||||
};
|
||||
expect(sequencify(tasks, names)).toEqual(expected);
|
||||
});
|
||||
|
||||
});
|
||||
@ -1,181 +0,0 @@
|
||||
import '../fixtures/window';
|
||||
import { Editor } from '@alilc/lowcode-editor-core';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { DocumentModel } from '../../src/document/document-model';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import { shellModelFactory } from '../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
describe.only('Project 方法测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
|
||||
beforeEach(() => {
|
||||
editor = new Editor();
|
||||
designer = new Designer({ editor, shellModelFactory });
|
||||
project = designer.project;
|
||||
doc = new DocumentModel(project, formSchema);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
designer.purge();
|
||||
editor = null;
|
||||
designer = null;
|
||||
project = null;
|
||||
});
|
||||
|
||||
it('simulator', () => {
|
||||
const mockSimulator = { isSimulator: true, a: 1 };
|
||||
project.mountSimulator(mockSimulator);
|
||||
expect(project.simulator).toEqual(mockSimulator);
|
||||
});
|
||||
|
||||
it('config / get / set', () => {
|
||||
const mockConfig = { version: '1.0.0', componentsTree: [] };
|
||||
project.config = mockConfig;
|
||||
expect(project.config).toEqual(mockConfig);
|
||||
const mockConfig2 = { version: '2.0.0', componentsTree: [] };
|
||||
project.set('config', mockConfig2);
|
||||
expect(project.get('config')).toEqual(mockConfig2);
|
||||
|
||||
project.set('version', '2.0.0');
|
||||
expect(project.get('version')).toBe('2.0.0');
|
||||
});
|
||||
|
||||
it('load', () => {
|
||||
project.load({
|
||||
componentsTree: [{
|
||||
componentName: 'Page',
|
||||
fileName: 'f1',
|
||||
}],
|
||||
}, 'f1');
|
||||
expect(project.currentDocument?.fileName).toBe('f1');
|
||||
});
|
||||
|
||||
it.skip('setSchema', () => {
|
||||
project.load({
|
||||
componentsTree: [{
|
||||
componentName: 'Page',
|
||||
fileName: 'f1',
|
||||
}],
|
||||
}, true);
|
||||
project.setSchema({
|
||||
componentsTree: [{
|
||||
componentName: 'Page',
|
||||
props: { a: 1 },
|
||||
}],
|
||||
});
|
||||
expect(project.currentDocument?.rootNode?.propsData).toEqual({ a: 1 });
|
||||
});
|
||||
|
||||
it('open / getDocument / checkExclusive', () => {
|
||||
project.load({
|
||||
componentsTree: [{
|
||||
componentName: 'Page',
|
||||
fileName: 'f1',
|
||||
}],
|
||||
});
|
||||
const doc1 = project.createDocument({
|
||||
componentName: 'Page',
|
||||
fileName: 'f2',
|
||||
});
|
||||
const doc2 = project.createDocument({
|
||||
componentName: 'Page',
|
||||
fileName: 'f3',
|
||||
});
|
||||
|
||||
project.open();
|
||||
|
||||
project.open('f2');
|
||||
expect(project.currentDocument).toBe(doc1);
|
||||
project.open('f3');
|
||||
expect(project.currentDocument).toBe(doc2);
|
||||
|
||||
project.open('f1');
|
||||
expect(project.currentDocument?.fileName).toBe('f1');
|
||||
|
||||
expect(project.open('not-existing')).toBeNull();
|
||||
|
||||
project.open(doc2);
|
||||
expect(project.currentDocument).toBe(doc2);
|
||||
|
||||
const doc3 = project.open({
|
||||
componentName: 'Page',
|
||||
fileName: 'f4',
|
||||
});
|
||||
expect(project.currentDocument).toBe(doc3);
|
||||
expect(project.documents.length).toBe(4);
|
||||
|
||||
expect(project.getDocument(project.currentDocument?.id)).toBe(doc3);
|
||||
expect(project.getDocumentByFileName(project.currentDocument?.fileName)).toBe(doc3);
|
||||
expect(project.getDocumentByFileName('unknown')).toBeNull();
|
||||
expect(project.checkExclusive(project.currentDocument));
|
||||
|
||||
expect(project.documents[0].opened).toBeTruthy();
|
||||
expect(project.documents[1].opened).toBeTruthy();
|
||||
expect(project.documents[2].opened).toBeTruthy();
|
||||
expect(project.documents[3].opened).toBeTruthy();
|
||||
expect(project.documents[0].suspensed).toBeTruthy();
|
||||
expect(project.documents[1].suspensed).toBeTruthy();
|
||||
expect(project.documents[2].suspensed).toBeTruthy();
|
||||
expect(project.documents[3].suspensed).toBeFalsy();
|
||||
|
||||
project.closeOthers(project.currentDocument);
|
||||
expect(project.documents[0].opened).toBeFalsy();
|
||||
expect(project.documents[1].opened).toBeFalsy();
|
||||
expect(project.documents[2].opened).toBeFalsy();
|
||||
expect(project.documents[3].opened).toBeTruthy();
|
||||
expect(project.documents[0].suspensed).toBeTruthy();
|
||||
expect(project.documents[1].suspensed).toBeTruthy();
|
||||
expect(project.documents[2].suspensed).toBeTruthy();
|
||||
expect(project.documents[3].suspensed).toBeFalsy();
|
||||
});
|
||||
|
||||
it('removeDocument', () => {
|
||||
const doc1 = project.createDocument({
|
||||
componentName: 'Page',
|
||||
fileName: 'f1',
|
||||
});
|
||||
project.removeDocument({});
|
||||
expect(project.documents.length).toBe(1);
|
||||
});
|
||||
|
||||
it('simulatorProps', () => {
|
||||
designer._simulatorProps = { a: 1 };
|
||||
expect(designer.simulatorProps.a).toBe(1);
|
||||
designer._simulatorProps = () => ({ a: 1 });
|
||||
expect(designer.simulatorProps.a).toBe(1);
|
||||
});
|
||||
|
||||
it('onCurrentDocumentChange', () => {
|
||||
const mockFn = jest.fn();
|
||||
const off = project.onCurrentDocumentChange(mockFn);
|
||||
|
||||
project.open({
|
||||
componentName: 'Page',
|
||||
});
|
||||
|
||||
expect(mockFn).toHaveBeenCalled();
|
||||
|
||||
off();
|
||||
mockFn.mockClear();
|
||||
project.open({
|
||||
componentName: 'Page',
|
||||
});
|
||||
expect(mockFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('setRendererReady / onRendererReady', () => {
|
||||
const mockFn = jest.fn();
|
||||
const off = project.onRendererReady(mockFn);
|
||||
project.setRendererReady({ a: 1 });
|
||||
expect(mockFn).toHaveBeenCalledWith({ a: 1 });
|
||||
off();
|
||||
mockFn.mockClear();
|
||||
project.setRendererReady({ a: 1 });
|
||||
expect(mockFn).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@ -1,305 +0,0 @@
|
||||
import { set, cloneDeep } from 'lodash-es';
|
||||
import '../fixtures/window';
|
||||
import { Editor } from '@alilc/lowcode-editor-core';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import { getIdsFromSchema, getNodeFromSchemaById } from '../utils';
|
||||
|
||||
const mockCreateSettingEntry = jest.fn();
|
||||
jest.mock('../../src/designer/designer', () => {
|
||||
return {
|
||||
Designer: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
getComponentMeta() {
|
||||
return {
|
||||
getMetadata() {
|
||||
return { configure: { advanced: null } };
|
||||
},
|
||||
get advanced() {
|
||||
return {};
|
||||
},
|
||||
};
|
||||
},
|
||||
transformProps(props) {
|
||||
return props;
|
||||
},
|
||||
createSettingEntry: mockCreateSettingEntry,
|
||||
postEvent() {},
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let designer = null;
|
||||
beforeAll(() => {
|
||||
designer = new Designer({});
|
||||
designer.editor = new Editor();
|
||||
});
|
||||
|
||||
describe('schema 生成节点模型测试', () => {
|
||||
describe('block ❌ | component ❌ | slot ❌', () => {
|
||||
beforeEach(() => {
|
||||
mockCreateSettingEntry.mockClear();
|
||||
});
|
||||
|
||||
it('基本的节点模型初始化,模型导出,初始化传入 schema', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
expect(nodesMap.size).toBe(expectedNodeCnt);
|
||||
ids.forEach((id) => {
|
||||
expect(nodesMap.get(id).componentName).toBe(
|
||||
getNodeFromSchemaById(formSchema, id).componentName,
|
||||
);
|
||||
});
|
||||
|
||||
const exportSchema = currentDocument?.export(1);
|
||||
expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt);
|
||||
nodesMap.forEach((node) => {
|
||||
// 触发 getter
|
||||
node.settingEntry;
|
||||
});
|
||||
expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt);
|
||||
});
|
||||
|
||||
it('onSimulatorReady works', () => {
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchema],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const mockCallback = jest.fn();
|
||||
const removeListener = project.onSimulatorReady(mockCallback);
|
||||
project.mountSimulator(undefined);
|
||||
expect(mockCallback).toBeCalled();
|
||||
removeListener();
|
||||
});
|
||||
|
||||
it('open doc when doc is blank', () => {
|
||||
const project = new Project(designer);
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const blankDoc = project.documents[0];
|
||||
expect(blankDoc).toBeTruthy();
|
||||
// 触发保存
|
||||
blankDoc.history.savePoint();
|
||||
expect(blankDoc.isModified()).toBeFalsy();
|
||||
expect(blankDoc.isBlank()).toBeTruthy();
|
||||
|
||||
//二次打开doc,会使用前面那个
|
||||
const openedDoc = project.open();
|
||||
expect(openedDoc).toBe(blankDoc);
|
||||
});
|
||||
|
||||
it('load schema with autoOpen === true', () => {
|
||||
const project = new Project(designer);
|
||||
expect(project).toBeTruthy();
|
||||
// trigger autoOpen case
|
||||
project.load(
|
||||
{
|
||||
componentsTree: [formSchema],
|
||||
},
|
||||
true,
|
||||
);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
expect(nodesMap.size).toBe(expectedNodeCnt);
|
||||
ids.forEach((id) => {
|
||||
expect(nodesMap.get(id).componentName).toBe(
|
||||
getNodeFromSchemaById(formSchema, id).componentName,
|
||||
);
|
||||
});
|
||||
|
||||
const exportSchema = currentDocument?.export(1);
|
||||
expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt);
|
||||
nodesMap.forEach((node) => {
|
||||
// 触发 getter
|
||||
node.settingEntry;
|
||||
});
|
||||
expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt);
|
||||
});
|
||||
it('load schema with autoOpen === true, and config contains layout.props.tabBar.item', () => {
|
||||
const project = new Project(designer);
|
||||
expect(project).toBeTruthy();
|
||||
// trigger autoOpen case
|
||||
project.load(
|
||||
{
|
||||
componentsTree: [
|
||||
{
|
||||
...formSchema,
|
||||
fileName: 'demoFile1',
|
||||
},
|
||||
{
|
||||
...formSchema,
|
||||
fileName: 'demoFile2',
|
||||
},
|
||||
],
|
||||
config: {
|
||||
layout: {
|
||||
props: {
|
||||
tabBar: {
|
||||
items: [
|
||||
{
|
||||
path: '/demoFile2',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
true,
|
||||
);
|
||||
const { currentDocument } = project;
|
||||
expect(currentDocument.fileName).toBe('demoFile2');
|
||||
});
|
||||
|
||||
it('load schema with autoOpen === true', () => {
|
||||
const project = new Project(designer);
|
||||
expect(project).toBeTruthy();
|
||||
// trigger autoOpen case
|
||||
project.load(
|
||||
{
|
||||
componentsTree: [
|
||||
{
|
||||
...formSchema,
|
||||
fileName: 'demoFile1',
|
||||
},
|
||||
{
|
||||
...formSchema,
|
||||
fileName: 'demoFile2',
|
||||
},
|
||||
],
|
||||
},
|
||||
'demoFile2',
|
||||
);
|
||||
const { currentDocument } = project;
|
||||
expect(currentDocument.fileName).toBe('demoFile2');
|
||||
});
|
||||
|
||||
it('setSchema works', () => {
|
||||
const project = new Project(designer);
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
project.setSchema({
|
||||
componentsTree: [
|
||||
{
|
||||
...formSchema,
|
||||
fileName: 'demoFile1',
|
||||
},
|
||||
],
|
||||
});
|
||||
const { currentDocument } = project;
|
||||
expect(currentDocument.fileName).toBe('demoFile1');
|
||||
});
|
||||
|
||||
it('基本的节点模型初始化,模型导出,project.open 传入 schema', () => {
|
||||
const project = new Project(designer);
|
||||
project.open(formSchema);
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
expect(nodesMap.size).toBe(expectedNodeCnt);
|
||||
ids.forEach((id) => {
|
||||
expect(nodesMap.get(id).componentName).toBe(
|
||||
getNodeFromSchemaById(formSchema, id).componentName,
|
||||
);
|
||||
});
|
||||
|
||||
const exportSchema = currentDocument?.export(1);
|
||||
expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt);
|
||||
nodesMap.forEach((node) => {
|
||||
// 触发 getter
|
||||
node.settingEntry;
|
||||
});
|
||||
expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt);
|
||||
});
|
||||
|
||||
it('project 卸载所有 document - unload()', () => {
|
||||
const project = new Project(designer);
|
||||
project.open(formSchema);
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument, documents } = project;
|
||||
|
||||
expect(documents).toHaveLength(1);
|
||||
expect(currentDocument).toBe(documents[0]);
|
||||
|
||||
project.unload();
|
||||
|
||||
expect(documents).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('project 卸载指定 document - removeDocument()', () => {
|
||||
const project = new Project(designer);
|
||||
project.open(formSchema);
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument, documents } = project;
|
||||
|
||||
expect(documents).toHaveLength(1);
|
||||
expect(currentDocument).toBe(documents[0]);
|
||||
|
||||
project.removeDocument(currentDocument);
|
||||
|
||||
expect(documents).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('get unknown document', () => {
|
||||
const project = new Project(designer);
|
||||
project.open(formSchema);
|
||||
expect(project).toBeTruthy();
|
||||
expect(project.getDocument('unknownId')).toBeNull();
|
||||
});
|
||||
|
||||
it('get set i18n works', () => {
|
||||
const project = new Project(designer);
|
||||
project.open(formSchema);
|
||||
expect(project).toBeTruthy();
|
||||
|
||||
project.i18n = formSchema.i18n;
|
||||
expect(project.i18n).toStrictEqual(formSchema.i18n);
|
||||
project.i18n = null;
|
||||
expect(project.i18n).toStrictEqual({});
|
||||
|
||||
project.set('i18n', formSchema.i18n);
|
||||
expect(project.get('i18n')).toStrictEqual(formSchema.i18n);
|
||||
project.set('i18n', null);
|
||||
expect(project.get('i18n')).toStrictEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('block ❌ | component ❌ | slot ✅', () => {
|
||||
it('基本的节点模型初始化,模型导出,初始化传入 schema', () => {
|
||||
const formSchemaWithSlot = set(
|
||||
cloneDeep(formSchema),
|
||||
'children[0].children[0].props.title.type',
|
||||
'JSSlot',
|
||||
);
|
||||
const project = new Project(designer, {
|
||||
componentsTree: [formSchemaWithSlot],
|
||||
});
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument!;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
// 目前每个 slot 会新增(1 + children.length)个节点
|
||||
const expectedNodeCnt = ids.length + 2;
|
||||
expect(nodesMap.size).toBe(expectedNodeCnt);
|
||||
// PageHeader
|
||||
expect(nodesMap.get('node_k1ow3cbd').slots).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('多 document 测试', () => {});
|
||||
});
|
||||
@ -1,8 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import { invariant } from '../../src/utils/invariant';
|
||||
|
||||
it('invariant', () => {
|
||||
expect(() => invariant(true)).not.toThrow();
|
||||
expect(() => invariant(false, 'abc', 'xxx')).toThrow(/Invariant failed:/);
|
||||
expect(() => invariant(false, 'abc')).toThrow(/Invariant failed:/);
|
||||
});
|
||||
@ -1,164 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import { isElementNode, isDOMNodeVisible, normalizeTriggers, makeEventsHandler } from '../../src/utils/misc';
|
||||
|
||||
it('isElementNode', () => {
|
||||
expect(isElementNode(document.createElement('div'))).toBeTruthy();
|
||||
expect(isElementNode(1)).toBeFalsy();
|
||||
});
|
||||
|
||||
/**
|
||||
* const domNodeRect = domNode.getBoundingClientRect();
|
||||
const { width, height } = viewport.contentBounds;
|
||||
const { left, right, top, bottom, width: nodeWidth, height: nodeHeight } = domNodeRect;
|
||||
return (
|
||||
left >= -nodeWidth &&
|
||||
top >= -nodeHeight &&
|
||||
bottom <= height + nodeHeight &&
|
||||
right <= width + nodeWidth
|
||||
);
|
||||
*/
|
||||
|
||||
const genMockNode = ({ left, right, top, bottom, width, height }) => {
|
||||
return { getBoundingClientRect: () => {
|
||||
if (width === undefined || height === undefined) throw new Error('width and height is required.');
|
||||
const base = { width, height };
|
||||
let coordinate = {};
|
||||
if (left !== undefined) {
|
||||
coordinate = top !== undefined ? {
|
||||
left,
|
||||
right: left + width,
|
||||
top,
|
||||
bottom: top + height,
|
||||
} : {
|
||||
left,
|
||||
right: left + width,
|
||||
bottom,
|
||||
top: bottom - height,
|
||||
}
|
||||
} else if (right !== undefined) {
|
||||
coordinate = top !== undefined ? {
|
||||
left: right - width,
|
||||
right,
|
||||
top,
|
||||
bottom: top + height,
|
||||
} : {
|
||||
left: right - width,
|
||||
right,
|
||||
bottom,
|
||||
top: bottom - height,
|
||||
}
|
||||
}
|
||||
return { ...base, ...coordinate };
|
||||
} };
|
||||
};
|
||||
const mockViewport = {
|
||||
contentBounds: {
|
||||
width: 300,
|
||||
height: 300,
|
||||
},
|
||||
};
|
||||
describe('isDOMNodeVisible', () => {
|
||||
it('isDOMNodeVisible', () => {
|
||||
expect(
|
||||
isDOMNodeVisible(
|
||||
genMockNode({
|
||||
width: 100,
|
||||
height: 100,
|
||||
left: 0,
|
||||
top: 0,
|
||||
}),
|
||||
mockViewport,
|
||||
),
|
||||
).toBeTruthy();
|
||||
|
||||
expect(
|
||||
isDOMNodeVisible(
|
||||
genMockNode({
|
||||
width: 100,
|
||||
height: 100,
|
||||
left: -100,
|
||||
top: 0,
|
||||
}),
|
||||
mockViewport,
|
||||
),
|
||||
).toBeTruthy();
|
||||
|
||||
expect(
|
||||
isDOMNodeVisible(
|
||||
genMockNode({
|
||||
width: 100,
|
||||
height: 100,
|
||||
left: 50,
|
||||
top: 50,
|
||||
}),
|
||||
mockViewport,
|
||||
),
|
||||
).toBeTruthy();
|
||||
|
||||
// 左侧出界了
|
||||
expect(
|
||||
isDOMNodeVisible(
|
||||
genMockNode({
|
||||
width: 100,
|
||||
height: 100,
|
||||
left: -101,
|
||||
top: 0,
|
||||
}),
|
||||
mockViewport,
|
||||
),
|
||||
).toBeFalsy();
|
||||
|
||||
// 右侧出界了
|
||||
expect(
|
||||
isDOMNodeVisible(
|
||||
genMockNode({
|
||||
width: 100,
|
||||
height: 100,
|
||||
right: 401,
|
||||
top: 0,
|
||||
}),
|
||||
mockViewport,
|
||||
),
|
||||
).toBeFalsy();
|
||||
|
||||
// 上侧出界了
|
||||
expect(
|
||||
isDOMNodeVisible(
|
||||
genMockNode({
|
||||
width: 100,
|
||||
height: 100,
|
||||
left: 50,
|
||||
top: -101,
|
||||
}),
|
||||
mockViewport,
|
||||
),
|
||||
).toBeFalsy();
|
||||
|
||||
// 下侧出界了
|
||||
expect(
|
||||
isDOMNodeVisible(
|
||||
genMockNode({
|
||||
width: 100,
|
||||
height: 100,
|
||||
left: 50,
|
||||
bottom: 401,
|
||||
}),
|
||||
mockViewport,
|
||||
),
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('normalizeTriggers', () => {
|
||||
expect(normalizeTriggers(['n', 'w'])).toEqual(['N', 'W']);
|
||||
});
|
||||
|
||||
it('makeEventsHandler', () => {
|
||||
const sensor = { contentDocument: document };
|
||||
// no contentDocument
|
||||
const sensor2 = {};
|
||||
const bind = makeEventsHandler({ view: { document } } as any, [sensor, sensor2]);
|
||||
const fn = jest.fn();
|
||||
bind((doc) => fn(doc));
|
||||
expect(fn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
@ -1,43 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import { includeSlot, removeSlot } from '../../src/utils/slot';
|
||||
|
||||
const genGetExtraProp = (val: string) => () => {
|
||||
return {
|
||||
getAsString() {
|
||||
return val;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const remove = () => {};
|
||||
|
||||
const mockNode = {
|
||||
slots: [{
|
||||
getExtraProp: genGetExtraProp('haha'),
|
||||
remove,
|
||||
}, {
|
||||
getExtraProp: genGetExtraProp('heihei'),
|
||||
remove,
|
||||
}]
|
||||
};
|
||||
|
||||
// 没有 slots
|
||||
const mockNode2 = {};
|
||||
|
||||
it('includeSlot', () => {
|
||||
expect(includeSlot(mockNode, 'haha')).toBeTruthy();
|
||||
expect(includeSlot(mockNode, 'heihei')).toBeTruthy();
|
||||
expect(includeSlot(mockNode, 'xixi')).toBeFalsy();
|
||||
expect(includeSlot(mockNode2, 'xixi')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('removeSlot', () => {
|
||||
expect(removeSlot(mockNode, 'xixi')).toBeFalsy();
|
||||
expect(mockNode.slots).toHaveLength(2);
|
||||
expect(removeSlot(mockNode, 'haha')).toBeTruthy();
|
||||
expect(mockNode.slots).toHaveLength(1);
|
||||
expect(removeSlot(mockNode, 'heihei')).toBeTruthy();
|
||||
expect(mockNode.slots).toHaveLength(0);
|
||||
|
||||
expect(removeSlot(mockNode2, 'xixi')).toBeFalsy();
|
||||
});
|
||||
@ -1,111 +0,0 @@
|
||||
import { getMockRenderer } from './renderer';
|
||||
|
||||
interface MockDocument extends Document {
|
||||
// open(): any;
|
||||
// write(): any;
|
||||
// close(): any;
|
||||
// addEventListener(): any;
|
||||
// removeEventListener(): any;
|
||||
triggerEventListener(): any;
|
||||
// createElement(): any;
|
||||
// appendChild(): any;
|
||||
// removeChild(): any;
|
||||
}
|
||||
|
||||
|
||||
const eventsMap : Map<string, Set<Function>> = new Map<string, Set<Function>>();
|
||||
const mockRemoveAttribute = jest.fn();
|
||||
const mockAddEventListener = jest.fn((eventName: string, cb) => {
|
||||
if (!eventsMap.has(eventName)) {
|
||||
eventsMap.set(eventName, new Set([cb]));
|
||||
return;
|
||||
}
|
||||
eventsMap.get(eventName)!.add(cb);
|
||||
});
|
||||
|
||||
const mockRemoveEventListener = jest.fn((eventName: string, cb) => {
|
||||
if (!eventsMap.has(eventName)) return;
|
||||
if (!cb) {
|
||||
eventsMap.delete(eventName);
|
||||
return;
|
||||
}
|
||||
eventsMap.get(eventName)?.delete(cb);
|
||||
});
|
||||
|
||||
const mockTriggerEventListener = jest.fn((eventName: string, data: any, context: object = {}) => {
|
||||
if (!eventsMap.has(eventName)) return;
|
||||
for (const cb of eventsMap.get(eventName)) {
|
||||
cb.call(context, data);
|
||||
}
|
||||
});
|
||||
|
||||
const mockCreateElement = jest.fn((tagName) => {
|
||||
return {
|
||||
style: {},
|
||||
appendChild() {},
|
||||
addEventListener: mockAddEventListener,
|
||||
removeEventListener: mockRemoveEventListener,
|
||||
triggerEventListener: mockTriggerEventListener,
|
||||
removeAttribute: mockRemoveAttribute,
|
||||
};
|
||||
});
|
||||
|
||||
export function getMockDocument(): MockDocument {
|
||||
return {
|
||||
open() {},
|
||||
write() {},
|
||||
close() {},
|
||||
addEventListener: mockAddEventListener,
|
||||
removeEventListener: mockRemoveEventListener,
|
||||
triggerEventListener: mockTriggerEventListener,
|
||||
createElement: mockCreateElement,
|
||||
removeChild() {},
|
||||
body: { appendChild() {}, removeChild() {} },
|
||||
};
|
||||
}
|
||||
|
||||
export function getMockWindow(doc?: MockDocument) {
|
||||
return {
|
||||
SimulatorRenderer: getMockRenderer(),
|
||||
addEventListener: mockAddEventListener,
|
||||
removeEventListener: mockRemoveEventListener,
|
||||
triggerEventListener: mockTriggerEventListener,
|
||||
document: doc || getMockDocument(),
|
||||
};
|
||||
}
|
||||
|
||||
export function clearEventsMap() {
|
||||
eventsMap.clear();
|
||||
}
|
||||
|
||||
export function getMockElement(tagName, options = {}) {
|
||||
const elem = document.createElement(tagName);
|
||||
let {
|
||||
width = 0,
|
||||
height = 0,
|
||||
top = 0,
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
} = options;
|
||||
elem.getBoundingClientRect = () => {
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
top,
|
||||
bottom,
|
||||
left,
|
||||
right,
|
||||
};
|
||||
};
|
||||
elem.setWidth = (newWidth) => {
|
||||
width = newWidth;
|
||||
};
|
||||
elem.setHeight = (newHeight) => {
|
||||
height = newHeight;
|
||||
};
|
||||
// console.log(elem.ownerDocument);
|
||||
// elem.ownerDocument = document;
|
||||
// elem.ownerDocument.defaultView = window;
|
||||
return elem;
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
export function getMockEvent(target, options) {
|
||||
return {
|
||||
target,
|
||||
preventDefault() {},
|
||||
stopPropagation() {},
|
||||
...options,
|
||||
};
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
export { getIdsFromSchema, getNodeFromSchemaById } from '@alilc/lowcode-test-mate/es/utils';
|
||||
export * from './bom';
|
||||
export * from './event';
|
||||
export * from './renderer';
|
||||
export * from './misc';
|
||||
@ -1,25 +0,0 @@
|
||||
import { set as lodashSet } from 'lodash-es';
|
||||
|
||||
export function set(obj: any, path: any, val: any) {
|
||||
if (typeof path === 'string' && path.startsWith('prototype')) {
|
||||
const segs = path.split('.');
|
||||
let acc = obj;
|
||||
segs.forEach((seg, idx) => {
|
||||
if (idx !== segs.length - 1) {
|
||||
acc[seg] = acc[seg] || {};
|
||||
acc = acc[seg];
|
||||
} else {
|
||||
acc[seg] = val;
|
||||
}
|
||||
});
|
||||
}
|
||||
return lodashSet(obj, path, val);
|
||||
}
|
||||
|
||||
export function delay(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export function delayObxTick() {
|
||||
return delay(100);
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
export function getMockRenderer() {
|
||||
return {
|
||||
isSimulatorRenderer: true,
|
||||
run() {
|
||||
// console.log('renderer run');
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
import { ConfigurationModel } from '../../src';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
describe('ConfigurationModel', () => {
|
||||
it('should create an empty model', () => {
|
||||
const model = ConfigurationModel.createEmptyModel();
|
||||
expect(model.isEmpty()).toBe(true);
|
||||
});
|
||||
|
||||
it('should add, set, and get values', () => {
|
||||
const model = ConfigurationModel.createEmptyModel();
|
||||
const key = 'testKey';
|
||||
const value = 'testValue';
|
||||
|
||||
model.setValue(key, value);
|
||||
expect(model.getValue(key)).toBe(value);
|
||||
|
||||
const newValue = 'newValue';
|
||||
model.addValue(key, newValue);
|
||||
expect(model.getValue(key)).toBe(newValue);
|
||||
|
||||
model.removeValue(key);
|
||||
expect(model.getValue(key)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle overrides', () => {
|
||||
const model = ConfigurationModel.createEmptyModel();
|
||||
const key = '[env].testKey';
|
||||
const value = 'testValue';
|
||||
|
||||
model.setValue(key, value);
|
||||
const overrides = model.overrides[0];
|
||||
expect(overrides.keys).toContain('testKey');
|
||||
expect(overrides.contents).toHaveProperty('testKey', value);
|
||||
expect(overrides.identifiers).toContain('env');
|
||||
|
||||
model.removeValue(key);
|
||||
expect(model.getValue(key)).toBeUndefined();
|
||||
expect(model.overrides.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should create overrides correctly', () => {
|
||||
const model = ConfigurationModel.createEmptyModel();
|
||||
const baseKey = 'baseKey';
|
||||
const baseValue = 'baseValue';
|
||||
const overrideKey = '[environment].overrideKey';
|
||||
const overrideValue = 'overrideValue';
|
||||
|
||||
// Set base and override values
|
||||
model.setValue(baseKey, baseValue);
|
||||
model.setValue(overrideKey, overrideValue);
|
||||
|
||||
// Override configuration model for a specific environment
|
||||
const envModel = model.override('environment');
|
||||
expect(envModel.getValue('overrideKey')).toBe(overrideValue);
|
||||
expect(envModel.getValue(baseKey)).toBeUndefined();
|
||||
expect(envModel.overrides).toEqual(model.overrides);
|
||||
});
|
||||
});
|
||||
@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-engine-core",
|
||||
"name": "@alilc/lowcode-engine-core",
|
||||
"version": "2.0.0-beta.0",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"main": "dist/engine-core.js",
|
||||
|
||||
34
packages/engine-core/src/command/command.ts
Normal file
34
packages/engine-core/src/command/command.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { type InstanceAccessor } from '@alilc/lowcode-shared';
|
||||
|
||||
export interface ICommandEvent {
|
||||
commandId: string;
|
||||
args: any[];
|
||||
}
|
||||
|
||||
export interface ICommandHandler {
|
||||
(accessor: InstanceAccessor, ...args: any[]): void;
|
||||
}
|
||||
|
||||
export interface ICommand {
|
||||
id: string;
|
||||
handler: ICommandHandler;
|
||||
metadata?: ICommandMetadata | null;
|
||||
}
|
||||
|
||||
export interface ICommandMetadata {
|
||||
/**
|
||||
* A short summary of what the command does. This will be used in:
|
||||
* - API commands
|
||||
* - when showing keybindings that have no other UX
|
||||
* - when searching for commands in the Command Palette
|
||||
*/
|
||||
readonly description: string;
|
||||
readonly args?: ReadonlyArray<{
|
||||
readonly name: string;
|
||||
readonly isOptional?: boolean;
|
||||
readonly description?: string;
|
||||
// readonly constraint?: TypeConstraint;
|
||||
// readonly schema?: IJSONSchema;
|
||||
}>;
|
||||
readonly returns?: string;
|
||||
}
|
||||
115
packages/engine-core/src/command/commandRegistry.ts
Normal file
115
packages/engine-core/src/command/commandRegistry.ts
Normal file
@ -0,0 +1,115 @@
|
||||
import {
|
||||
type Event,
|
||||
type EventDisposable,
|
||||
type EventListener,
|
||||
Emitter,
|
||||
} from '@alilc/lowcode-shared';
|
||||
import { ICommand, ICommandHandler } from './command';
|
||||
import { Registry } from '../extension';
|
||||
|
||||
export type ICommandsMap = Map<string, ICommand>;
|
||||
|
||||
export interface ICommandRegistry {
|
||||
onDidRegisterCommand: Event<string>;
|
||||
|
||||
registerCommand(id: string, command: ICommandHandler): EventDisposable;
|
||||
registerCommand(command: ICommand): EventDisposable;
|
||||
|
||||
registerCommandAlias(oldId: string, newId: string): EventDisposable;
|
||||
|
||||
getCommand(id: string): ICommand | undefined;
|
||||
getCommands(): ICommandsMap;
|
||||
}
|
||||
|
||||
class CommandsRegistry implements ICommandRegistry {
|
||||
private readonly _commands = new Map<string, LinkedList<ICommand>>();
|
||||
|
||||
private readonly _onDidRegisterCommand = new Emitter<string>();
|
||||
|
||||
onDidRegisterCommand(fn: EventListener<string>) {
|
||||
return this._onDidRegisterCommand.on(fn);
|
||||
}
|
||||
|
||||
registerCommand(idOrCommand: string | ICommand, handler?: ICommandHandler): EventDisposable {
|
||||
if (!idOrCommand) {
|
||||
throw new Error(`invalid command`);
|
||||
}
|
||||
|
||||
if (typeof idOrCommand === 'string') {
|
||||
if (!handler) {
|
||||
throw new Error(`invalid command`);
|
||||
}
|
||||
return this.registerCommand({ id: idOrCommand, handler });
|
||||
}
|
||||
|
||||
// add argument validation if rich command metadata is provided
|
||||
if (idOrCommand.metadata && Array.isArray(idOrCommand.metadata.args)) {
|
||||
const constraints: Array<TypeConstraint | undefined> = [];
|
||||
for (const arg of idOrCommand.metadata.args) {
|
||||
constraints.push(arg.constraint);
|
||||
}
|
||||
const actualHandler = idOrCommand.handler;
|
||||
idOrCommand.handler = function (accessor, ...args: any[]) {
|
||||
validateConstraints(args, constraints);
|
||||
return actualHandler(accessor, ...args);
|
||||
};
|
||||
}
|
||||
|
||||
// find a place to store the command
|
||||
const { id } = idOrCommand;
|
||||
|
||||
let commands = this._commands.get(id);
|
||||
if (!commands) {
|
||||
commands = new LinkedList<ICommand>();
|
||||
this._commands.set(id, commands);
|
||||
}
|
||||
|
||||
const removeFn = commands.unshift(idOrCommand);
|
||||
|
||||
const ret = toDisposable(() => {
|
||||
removeFn();
|
||||
const command = this._commands.get(id);
|
||||
if (command?.isEmpty()) {
|
||||
this._commands.delete(id);
|
||||
}
|
||||
});
|
||||
|
||||
// tell the world about this command
|
||||
this._onDidRegisterCommand.emit(id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
registerCommandAlias(oldId: string, newId: string): IDisposable {
|
||||
return this.registerCommand(oldId, (accessor, ...args) =>
|
||||
accessor.get(ICommandService).executeCommand(newId, ...args),
|
||||
);
|
||||
}
|
||||
|
||||
getCommand(id: string): ICommand | undefined {
|
||||
const list = this._commands.get(id);
|
||||
if (!list || list.isEmpty()) {
|
||||
return undefined;
|
||||
}
|
||||
return Iterable.first(list);
|
||||
}
|
||||
|
||||
getCommands(): ICommandsMap {
|
||||
const result = new Map<string, ICommand>();
|
||||
for (const key of this._commands.keys()) {
|
||||
const command = this.getCommand(key);
|
||||
if (command) {
|
||||
result.set(key, command);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
const commandsRegistry = new CommandsRegistry();
|
||||
|
||||
export const Extension = {
|
||||
command: 'base.contributions.command',
|
||||
};
|
||||
|
||||
Registry.add(Extension.command, commandsRegistry);
|
||||
20
packages/engine-core/src/command/commandService.ts
Normal file
20
packages/engine-core/src/command/commandService.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { createDecorator, Provide } from '@alilc/lowcode-shared';
|
||||
import { Registry } from '../extension';
|
||||
import { ICommandRegistry, Extension } from './commandRegistry';
|
||||
|
||||
export interface ICommandService {
|
||||
executeCommand<T = any>(commandId: string, ...args: any[]): Promise<T | undefined>;
|
||||
}
|
||||
|
||||
export const ICommandService = createDecorator<ICommandService>('commandService');
|
||||
|
||||
@Provide(ICommandService)
|
||||
export class CommandService implements ICommandService {
|
||||
executeCommand<T = any>(id: string, ...args: any[]): Promise<T | undefined> {
|
||||
const command = Registry.as<ICommandRegistry>(Extension.command).getCommand(id);
|
||||
|
||||
if (!command) {
|
||||
return Promise.reject(new Error(`command '${id}' not found`));
|
||||
}
|
||||
}
|
||||
}
|
||||
81
packages/engine-core/src/configuration/configuration.ts
Normal file
81
packages/engine-core/src/configuration/configuration.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import { type StringDictionary, Emitter, type EventListener } from '@alilc/lowcode-shared';
|
||||
import { ConfigurationModel } from './configurationModel';
|
||||
import {
|
||||
type IConfigurationRegistry,
|
||||
type IRegisteredConfigurationPropertySchema,
|
||||
Extension,
|
||||
} from './configurationRegistry';
|
||||
import { Registry } from '../extension';
|
||||
|
||||
export interface IConfigurationOverrides {
|
||||
overrideIdentifier?: string | null;
|
||||
}
|
||||
|
||||
export interface IConfigurationUpdateOverrides {
|
||||
overrideIdentifiers?: string[] | null;
|
||||
}
|
||||
|
||||
export class DefaultConfiguration {
|
||||
private emitter = new Emitter<{
|
||||
defaults: ConfigurationModel;
|
||||
properties: string[];
|
||||
}>();
|
||||
|
||||
private _configurationModel = ConfigurationModel.createEmptyModel();
|
||||
|
||||
get configurationModel(): ConfigurationModel {
|
||||
return this._configurationModel;
|
||||
}
|
||||
|
||||
initialize(): ConfigurationModel {
|
||||
this.resetConfigurationModel();
|
||||
Registry.as<IConfigurationRegistry>(Extension.Configuration).onDidUpdateConfiguration(
|
||||
({ properties }) => this.onDidUpdateConfiguration([...properties]),
|
||||
);
|
||||
|
||||
return this.configurationModel;
|
||||
}
|
||||
|
||||
reload(): ConfigurationModel {
|
||||
this.resetConfigurationModel();
|
||||
return this.configurationModel;
|
||||
}
|
||||
|
||||
onDidChangeConfiguration(
|
||||
listener: EventListener<[{ defaults: ConfigurationModel; properties: string[] }]>,
|
||||
) {
|
||||
return this.emitter.on(listener);
|
||||
}
|
||||
|
||||
private onDidUpdateConfiguration(properties: string[]): void {
|
||||
this.updateConfigurationModel(
|
||||
properties,
|
||||
Registry.as<IConfigurationRegistry>(Extension.Configuration).getConfigurationProperties(),
|
||||
);
|
||||
this.emitter.emit({ defaults: this.configurationModel, properties });
|
||||
}
|
||||
|
||||
private resetConfigurationModel(): void {
|
||||
this._configurationModel = ConfigurationModel.createEmptyModel();
|
||||
|
||||
const properties = Registry.as<IConfigurationRegistry>(
|
||||
Extension.Configuration,
|
||||
).getConfigurationProperties();
|
||||
|
||||
this.updateConfigurationModel(Object.keys(properties), properties);
|
||||
}
|
||||
|
||||
private updateConfigurationModel(
|
||||
properties: string[],
|
||||
configurationProperties: StringDictionary<IRegisteredConfigurationPropertySchema>,
|
||||
): void {
|
||||
for (const key of properties) {
|
||||
const propertySchema = configurationProperties[key];
|
||||
if (propertySchema) {
|
||||
this.configurationModel.setValue(key, propertySchema.default);
|
||||
} else {
|
||||
this.configurationModel.removeValue(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
406
packages/engine-core/src/configuration/configurationModel.ts
Normal file
406
packages/engine-core/src/configuration/configurationModel.ts
Normal file
@ -0,0 +1,406 @@
|
||||
import { type StringDictionary } from '@alilc/lowcode-shared';
|
||||
import { get as lodasgGet, isEqual, uniq, cloneDeep, isObject } from 'lodash-es';
|
||||
import { OVERRIDE_PROPERTY_REGEX, overrideIdentifiersFromKey } from './configurationRegistry';
|
||||
|
||||
export type InspectValue<V> = {
|
||||
readonly value?: V;
|
||||
readonly override?: V;
|
||||
readonly overrides?: { readonly identifiers: string[]; readonly value: V }[];
|
||||
merged?: V;
|
||||
};
|
||||
|
||||
export interface IConfigurationModel {
|
||||
contents: any;
|
||||
keys: string[];
|
||||
overrides: IOverrides[];
|
||||
}
|
||||
|
||||
export interface IOverrides {
|
||||
keys: string[];
|
||||
contents: any;
|
||||
identifiers: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 支持配置覆盖的 model 类
|
||||
*
|
||||
* 举例来说:
|
||||
* 假设有一个应用程序,它的行为在开发环境和生产环境下有细微差别。在开发环境中,可能需要额外的日志记录和调试工具,而生产环境则需要优化性能和安全性。
|
||||
* 使用配置覆盖,就可以为这两个环境创建一组基础配置,并通过以 [development] 或 [production] 为标识符的覆盖键来为两个环境提供不同的设置。
|
||||
*
|
||||
* case:
|
||||
* const model = ConfigurationModel.createEmptyModel();
|
||||
* const baseKey = 'baseKey';
|
||||
* const baseValue = 'baseValue';
|
||||
* const overrideKey = '[environment]';
|
||||
* const overrideValue = { baseKey: 'overrideValue' };
|
||||
*
|
||||
* const envModel = model.override('environment');
|
||||
* model.getValue(baseKey) === 'baseValue'
|
||||
* envModel.getValue(baseKey) === 'overrideValue'
|
||||
*/
|
||||
export class ConfigurationModel implements IConfigurationModel {
|
||||
static createEmptyModel(): ConfigurationModel {
|
||||
return new ConfigurationModel({}, [], [], undefined);
|
||||
}
|
||||
|
||||
private readonly overrideConfigurations = new Map<string, ConfigurationModel>();
|
||||
|
||||
constructor(
|
||||
private readonly _contents: StringDictionary,
|
||||
private readonly _keys: string[],
|
||||
private readonly _overrides: IOverrides[],
|
||||
public readonly raw?: ReadonlyArray<ConfigurationModel> | undefined,
|
||||
) {}
|
||||
|
||||
get contents(): any {
|
||||
return this._contents;
|
||||
}
|
||||
|
||||
get overrides(): IOverrides[] {
|
||||
return this._overrides;
|
||||
}
|
||||
|
||||
get keys(): string[] {
|
||||
return this._keys;
|
||||
}
|
||||
|
||||
private _rawConfiguration: ConfigurationModel | undefined;
|
||||
get rawConfiguration(): ConfigurationModel {
|
||||
if (!this._rawConfiguration) {
|
||||
if (this.raw?.length) {
|
||||
const rawConfigurationModels = this.raw;
|
||||
this._rawConfiguration = rawConfigurationModels.reduce(
|
||||
(previous, current) => (current === previous ? current : previous.merge(current)),
|
||||
rawConfigurationModels[0],
|
||||
);
|
||||
} else {
|
||||
// raw is same as current
|
||||
this._rawConfiguration = this;
|
||||
}
|
||||
}
|
||||
return this._rawConfiguration;
|
||||
}
|
||||
|
||||
toJSON(): IConfigurationModel {
|
||||
return {
|
||||
contents: this.contents,
|
||||
overrides: this.overrides,
|
||||
keys: this.keys,
|
||||
};
|
||||
}
|
||||
|
||||
inspect<V>(section?: string | undefined, overrideIdentifier?: string | null): InspectValue<V> {
|
||||
const _this = this;
|
||||
return {
|
||||
get value() {
|
||||
return _this.rawConfiguration.getValue<V>(section);
|
||||
},
|
||||
get override() {
|
||||
return overrideIdentifier
|
||||
? _this.rawConfiguration.getOverrideValue<V>(section, overrideIdentifier)
|
||||
: undefined;
|
||||
},
|
||||
get merged() {
|
||||
return overrideIdentifier
|
||||
? _this.rawConfiguration.override(overrideIdentifier).getValue<V>(section)
|
||||
: _this.rawConfiguration.getValue<V>(section);
|
||||
},
|
||||
get overrides() {
|
||||
const overrides: { readonly identifiers: string[]; readonly value: V }[] = [];
|
||||
for (const { contents, identifiers, keys } of _this.rawConfiguration.overrides) {
|
||||
const value = new ConfigurationModel(contents, keys, [], undefined).getValue<V>(section);
|
||||
if (value !== undefined) {
|
||||
overrides.push({ identifiers, value });
|
||||
}
|
||||
}
|
||||
return overrides.length ? overrides : undefined;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
merge(...others: ConfigurationModel[]): ConfigurationModel {
|
||||
const contents = cloneDeep(this.contents);
|
||||
const overrides = cloneDeep(this.overrides);
|
||||
const keys = [...this.keys];
|
||||
const raws = this.raw?.length ? [...this.raw] : [this];
|
||||
|
||||
for (const other of others) {
|
||||
raws.push(...(other.raw?.length ? other.raw : [other]));
|
||||
if (other.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
this.mergeContents(contents, other.contents);
|
||||
|
||||
for (const otherOverride of other.overrides) {
|
||||
const [override] = overrides.filter((o) =>
|
||||
isEqual(o.identifiers, otherOverride.identifiers),
|
||||
);
|
||||
if (override) {
|
||||
this.mergeContents(override.contents, otherOverride.contents);
|
||||
override.keys.push(...otherOverride.keys);
|
||||
override.keys = uniq(override.keys);
|
||||
} else {
|
||||
overrides.push(cloneDeep(otherOverride));
|
||||
}
|
||||
}
|
||||
for (const key of other.keys) {
|
||||
if (keys.indexOf(key) === -1) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ConfigurationModel(contents, keys, overrides, raws);
|
||||
}
|
||||
|
||||
override(identifier: string): ConfigurationModel {
|
||||
let overrideConfigurationModel = this.overrideConfigurations.get(identifier);
|
||||
if (!overrideConfigurationModel) {
|
||||
overrideConfigurationModel = this.createOverrideConfigurationModel(identifier);
|
||||
this.overrideConfigurations.set(identifier, overrideConfigurationModel);
|
||||
}
|
||||
return overrideConfigurationModel;
|
||||
}
|
||||
|
||||
private createOverrideConfigurationModel(identifier: string): ConfigurationModel {
|
||||
const overrideContents = this.getContentsForOverrideIdentifer(identifier);
|
||||
|
||||
if (
|
||||
!overrideContents ||
|
||||
typeof overrideContents !== 'object' ||
|
||||
!Object.keys(overrideContents).length
|
||||
) {
|
||||
// If there are no valid overrides, return self
|
||||
return this;
|
||||
}
|
||||
|
||||
const contents: any = {};
|
||||
for (const key of uniq([...Object.keys(this.contents), ...Object.keys(overrideContents)])) {
|
||||
let contentsForKey = this.contents[key];
|
||||
const overrideContentsForKey = overrideContents[key];
|
||||
|
||||
// If there are override contents for the key, clone and merge otherwise use base contents
|
||||
if (overrideContentsForKey) {
|
||||
// Clone and merge only if base contents and override contents are of type object otherwise just override
|
||||
if (typeof contentsForKey === 'object' && typeof overrideContentsForKey === 'object') {
|
||||
contentsForKey = cloneDeep(contentsForKey);
|
||||
this.mergeContents(contentsForKey, overrideContentsForKey);
|
||||
} else {
|
||||
contentsForKey = overrideContentsForKey;
|
||||
}
|
||||
}
|
||||
|
||||
contents[key] = contentsForKey;
|
||||
}
|
||||
|
||||
return new ConfigurationModel(contents, this.keys, this.overrides);
|
||||
}
|
||||
|
||||
private getContentsForOverrideIdentifer(identifier: string): any {
|
||||
let contentsForIdentifierOnly: StringDictionary | null = null;
|
||||
let contents: StringDictionary | null = null;
|
||||
const mergeContents = (contentsToMerge: any) => {
|
||||
if (contentsToMerge) {
|
||||
if (contents) {
|
||||
this.mergeContents(contents, contentsToMerge);
|
||||
} else {
|
||||
contents = cloneDeep(contentsToMerge);
|
||||
}
|
||||
}
|
||||
};
|
||||
for (const override of this.overrides) {
|
||||
if (override.identifiers.length === 1 && override.identifiers[0] === identifier) {
|
||||
contentsForIdentifierOnly = override.contents;
|
||||
} else if (override.identifiers.includes(identifier)) {
|
||||
mergeContents(override.contents);
|
||||
}
|
||||
}
|
||||
// Merge contents of the identifier only at the end to take precedence.
|
||||
mergeContents(contentsForIdentifierOnly);
|
||||
return contents;
|
||||
}
|
||||
|
||||
private mergeContents(source: any, target: any): void {
|
||||
for (const key of Object.keys(target)) {
|
||||
if (key in source) {
|
||||
if (isObject(source[key]) && isObject(target[key])) {
|
||||
this.mergeContents(source[key], target[key]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
source[key] = cloneDeep(target[key]);
|
||||
}
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
return (
|
||||
this._keys.length === 0 &&
|
||||
Object.keys(this._contents).length === 0 &&
|
||||
this._overrides.length === 0
|
||||
);
|
||||
}
|
||||
|
||||
getValue<V>(section?: string | undefined): V {
|
||||
return section ? lodasgGet(this.contents, section) : this.contents;
|
||||
}
|
||||
|
||||
// Update methods
|
||||
|
||||
addValue(key: string, value: any): void {
|
||||
this.updateValue(key, value, true);
|
||||
}
|
||||
|
||||
setValue(key: string, value: any): void {
|
||||
this.updateValue(key, value, false);
|
||||
}
|
||||
|
||||
removeValue(key: string): void {
|
||||
const index = this.keys.indexOf(key);
|
||||
if (index !== -1) {
|
||||
this.keys.splice(index, 1);
|
||||
removeFromValueTree(this.contents, key);
|
||||
}
|
||||
|
||||
const isOverrideKey = OVERRIDE_PROPERTY_REGEX.test(key);
|
||||
if (isOverrideKey) {
|
||||
const identifiers = overrideIdentifiersFromKey(key);
|
||||
const overrideIndex = this.overrides.findIndex((o) => isEqual(o.identifiers, identifiers));
|
||||
if (overrideIndex !== -1) {
|
||||
const override = this.overrides[overrideIndex];
|
||||
removeFromValueTree(override.contents, key);
|
||||
if (Object.keys(override.contents).length === 0) {
|
||||
this.overrides.splice(overrideIndex, 1);
|
||||
} else {
|
||||
override.keys = Object.keys(override.contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private updateValue(key: string, value: any, add: boolean): void {
|
||||
addToValueTree(this.contents, key, value);
|
||||
if (add || !this.keys.includes(key)) {
|
||||
this.keys.push(key);
|
||||
}
|
||||
|
||||
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
|
||||
const identifiers = overrideIdentifiersFromKey(key);
|
||||
const override = this.overrides.find((o) => isEqual(o.identifiers, identifiers));
|
||||
|
||||
if (override) {
|
||||
addToValueTree(override.contents, key, value);
|
||||
} else {
|
||||
this.overrides.push({
|
||||
identifiers,
|
||||
keys: Object.keys(this.contents[key]),
|
||||
contents: toValuesTree(this.contents[key]),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getOverrideValue<V>(section: string | undefined, overrideIdentifier: string): V | undefined {
|
||||
const overrideContents = this.getContentsForOverrideIdentifer(overrideIdentifier);
|
||||
return overrideContents
|
||||
? section
|
||||
? lodasgGet(overrideContents, section)
|
||||
: overrideContents
|
||||
: undefined;
|
||||
}
|
||||
|
||||
getKeysForOverrideIdentifier(identifier: string): string[] {
|
||||
const keys: string[] = [];
|
||||
for (const override of this.overrides) {
|
||||
if (override.identifiers.includes(identifier)) {
|
||||
keys.push(...override.keys);
|
||||
}
|
||||
}
|
||||
return uniq(keys);
|
||||
}
|
||||
|
||||
getAllOverrideIdentifiers(): string[] {
|
||||
const result: string[] = [];
|
||||
for (const override of this.overrides) {
|
||||
result.push(...override.identifiers);
|
||||
}
|
||||
return uniq(result);
|
||||
}
|
||||
}
|
||||
|
||||
function removeFromValueTree(valueTree: any, key: string): void {
|
||||
const segments = key.split('.');
|
||||
doRemoveFromValueTree(valueTree, segments);
|
||||
}
|
||||
|
||||
function doRemoveFromValueTree(valueTree: any, segments: string[]): void {
|
||||
const first = segments.shift()!;
|
||||
if (segments.length === 0) {
|
||||
// Reached last segment
|
||||
delete valueTree[first];
|
||||
return;
|
||||
}
|
||||
|
||||
if (Object.keys(valueTree).includes(first)) {
|
||||
const value = valueTree[first];
|
||||
if (typeof value === 'object' && !Array.isArray(value)) {
|
||||
doRemoveFromValueTree(value, segments);
|
||||
if (Object.keys(value).length === 0) {
|
||||
delete valueTree[first];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addToValueTree(
|
||||
settingsTreeRoot: any,
|
||||
key: string,
|
||||
value: any,
|
||||
conflictReporter: (message: string) => void = console.error,
|
||||
): void {
|
||||
const segments = key.split('.');
|
||||
const last = segments.pop()!;
|
||||
|
||||
let curr = settingsTreeRoot;
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
const s = segments[i];
|
||||
let obj = curr[s];
|
||||
switch (typeof obj) {
|
||||
case 'undefined':
|
||||
obj = curr[s] = Object.create(null);
|
||||
break;
|
||||
case 'object':
|
||||
if (obj === null) {
|
||||
conflictReporter(`Ignoring ${key} as ${segments.slice(0, i + 1).join('.')} is null`);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
conflictReporter(
|
||||
`Ignoring ${key} as ${segments.slice(0, i + 1).join('.')} is ${JSON.stringify(obj)}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
curr = obj;
|
||||
}
|
||||
|
||||
if (typeof curr === 'object' && curr !== null) {
|
||||
try {
|
||||
curr[last] = value; // workaround https://github.com/microsoft/vscode/issues/13606
|
||||
} catch (e) {
|
||||
conflictReporter(`Ignoring ${key} as ${segments.join('.')} is ${JSON.stringify(curr)}`);
|
||||
}
|
||||
} else {
|
||||
conflictReporter(`Ignoring ${key} as ${segments.join('.')} is ${JSON.stringify(curr)}`);
|
||||
}
|
||||
}
|
||||
|
||||
function toValuesTree(properties: StringDictionary): any {
|
||||
const root = Object.create(null);
|
||||
|
||||
for (const key in properties) {
|
||||
addToValueTree(root, key, properties[key]);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
301
packages/engine-core/src/configuration/configurationRegistry.ts
Normal file
301
packages/engine-core/src/configuration/configurationRegistry.ts
Normal file
@ -0,0 +1,301 @@
|
||||
import {
|
||||
type Event,
|
||||
Emitter,
|
||||
type StringDictionary,
|
||||
type JSONValueType,
|
||||
jsonTypes,
|
||||
} from '@alilc/lowcode-shared';
|
||||
import { uniq, isUndefined } from 'lodash-es';
|
||||
import { Registry } from '../extension/registry';
|
||||
|
||||
const OVERRIDE_IDENTIFIER_PATTERN = `\\[([^\\]]+)\\]`;
|
||||
const OVERRIDE_IDENTIFIER_REGEX = new RegExp(OVERRIDE_IDENTIFIER_PATTERN, 'g');
|
||||
export const OVERRIDE_PROPERTY_PATTERN = `^(${OVERRIDE_IDENTIFIER_PATTERN})+$`;
|
||||
export const OVERRIDE_PROPERTY_REGEX = new RegExp(OVERRIDE_PROPERTY_PATTERN);
|
||||
|
||||
export function overrideIdentifiersFromKey(key: string): string[] {
|
||||
const identifiers: string[] = [];
|
||||
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
|
||||
let matches = OVERRIDE_IDENTIFIER_REGEX.exec(key);
|
||||
while (matches?.length) {
|
||||
const identifier = matches[1].trim();
|
||||
if (identifier) {
|
||||
identifiers.push(identifier);
|
||||
}
|
||||
matches = OVERRIDE_IDENTIFIER_REGEX.exec(key);
|
||||
}
|
||||
}
|
||||
return uniq(identifiers);
|
||||
}
|
||||
|
||||
export interface IConfigurationRegistry {
|
||||
/**
|
||||
* Register a configuration to the registry.
|
||||
*/
|
||||
registerConfiguration(configuration: IConfigurationNode, validate?: boolean): void;
|
||||
|
||||
/**
|
||||
* Register multiple configurations to the registry.
|
||||
*/
|
||||
registerConfigurations(configurations: IConfigurationNode[], validate?: boolean): void;
|
||||
|
||||
/**
|
||||
* Deregister multiple configurations from the registry.
|
||||
*/
|
||||
deregisterConfigurations(configurations: IConfigurationNode[]): void;
|
||||
|
||||
/**
|
||||
* Signal that the schema of a configuration setting has changes. It is currently only supported to change enumeration values.
|
||||
* Property or default value changes are not allowed.
|
||||
*/
|
||||
notifyConfigurationSchemaUpdated(): void;
|
||||
/**
|
||||
* Event that fires whenever a configuration has been
|
||||
* registered.
|
||||
*/
|
||||
readonly onDidSchemaChange: Event<void>;
|
||||
/**
|
||||
* Event that fires whenever a configuration has been
|
||||
* registered.
|
||||
*/
|
||||
readonly onDidUpdateConfiguration: Event<{
|
||||
properties: ReadonlySet<string>;
|
||||
defaultsOverrides?: boolean;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Returns all configuration nodes contributed to this registry.
|
||||
*/
|
||||
getConfigurations(): IConfigurationNode[];
|
||||
/**
|
||||
* Returns all configurations settings of all configuration nodes contributed to this registry.
|
||||
*/
|
||||
getConfigurationProperties(): StringDictionary<IRegisteredConfigurationPropertySchema>;
|
||||
/**
|
||||
* Returns all excluded configurations settings of all configuration nodes contributed to this registry.
|
||||
*/
|
||||
getExcludedConfigurationProperties(): StringDictionary<IRegisteredConfigurationPropertySchema>;
|
||||
}
|
||||
|
||||
export interface IConfigurationNode {
|
||||
id?: string;
|
||||
order?: number;
|
||||
type?: JSONValueType | JSONValueType[];
|
||||
title?: string;
|
||||
description?: string;
|
||||
properties?: StringDictionary<IConfigurationPropertySchema>;
|
||||
allOf?: IConfigurationNode[];
|
||||
extensionInfo?: IExtensionInfo;
|
||||
}
|
||||
|
||||
export interface IConfigurationPropertySchema {
|
||||
type?: JSONValueType;
|
||||
default?: any;
|
||||
tags?: string[];
|
||||
included?: boolean;
|
||||
deprecated?: boolean;
|
||||
deprecationMessage?: string;
|
||||
}
|
||||
|
||||
export interface IExtensionInfo {
|
||||
id: string;
|
||||
displayName?: string;
|
||||
}
|
||||
|
||||
export type ConfigurationDefaultValueSource = IExtensionInfo | Map<string, IExtensionInfo>;
|
||||
|
||||
export interface IConfigurationDefaults {
|
||||
overrides: StringDictionary;
|
||||
source?: IExtensionInfo;
|
||||
}
|
||||
|
||||
export interface IRegisteredConfigurationPropertySchema extends IConfigurationPropertySchema {
|
||||
source?: IExtensionInfo; // Source of the Property
|
||||
defaultValueSource?: ConfigurationDefaultValueSource; // Source of the Default Value
|
||||
}
|
||||
|
||||
export class ConfigurationRegistry implements IConfigurationRegistry {
|
||||
private configurationContributors: IConfigurationNode[];
|
||||
private configurationProperties: StringDictionary<IRegisteredConfigurationPropertySchema>;
|
||||
private excludedConfigurationProperties: StringDictionary<IRegisteredConfigurationPropertySchema>;
|
||||
|
||||
private schemaChangeEmitter = new Emitter<void>();
|
||||
private updateConfigurationEmitter = new Emitter<{
|
||||
properties: ReadonlySet<string>;
|
||||
defaultsOverrides?: boolean;
|
||||
}>();
|
||||
|
||||
constructor() {
|
||||
this.configurationContributors = [];
|
||||
this.configurationProperties = {};
|
||||
this.excludedConfigurationProperties = {};
|
||||
}
|
||||
|
||||
registerConfiguration(configuration: IConfigurationNode, validate: boolean = true): void {
|
||||
this.registerConfigurations([configuration], validate);
|
||||
}
|
||||
|
||||
registerConfigurations(configurations: IConfigurationNode[], validate: boolean = true): void {
|
||||
const properties = new Set<string>();
|
||||
this.doRegisterConfigurations(configurations, validate, properties);
|
||||
|
||||
this.schemaChangeEmitter.emit();
|
||||
this.updateConfigurationEmitter.emit({ properties });
|
||||
}
|
||||
|
||||
private doRegisterConfigurations(
|
||||
configurations: IConfigurationNode[],
|
||||
validate: boolean,
|
||||
bucket: Set<string>,
|
||||
): void {
|
||||
configurations.forEach((configuration) => {
|
||||
this.validateAndRegisterProperties(
|
||||
configuration,
|
||||
validate,
|
||||
configuration.extensionInfo,
|
||||
bucket,
|
||||
);
|
||||
|
||||
this.configurationContributors.push(configuration);
|
||||
});
|
||||
}
|
||||
|
||||
private validateAndRegisterProperties(
|
||||
configuration: IConfigurationNode,
|
||||
validate: boolean = true,
|
||||
extensionInfo: IExtensionInfo | undefined,
|
||||
bucket: Set<string>,
|
||||
): void {
|
||||
const properties = configuration.properties;
|
||||
if (properties) {
|
||||
for (const key in properties) {
|
||||
const property: IRegisteredConfigurationPropertySchema = properties[key];
|
||||
|
||||
if (validate && this.validateProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
property.source = extensionInfo;
|
||||
|
||||
// update default value
|
||||
this.updatePropertyDefaultValue(property);
|
||||
|
||||
// Add to properties maps
|
||||
// Property is included by default if 'included' is unspecified
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(properties[key], 'included') &&
|
||||
!properties[key].included
|
||||
) {
|
||||
this.excludedConfigurationProperties[key] = properties[key];
|
||||
continue;
|
||||
}
|
||||
|
||||
this.configurationProperties[key] = properties[key];
|
||||
bucket.add(key);
|
||||
}
|
||||
|
||||
const subNodes = configuration.allOf;
|
||||
if (subNodes) {
|
||||
for (const node of subNodes) {
|
||||
this.validateAndRegisterProperties(node, validate, extensionInfo, bucket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private validateProperty(property: string): string | null {
|
||||
if (!property.trim()) {
|
||||
return 'Cannot register an empty property';
|
||||
}
|
||||
if (OVERRIDE_PROPERTY_REGEX.test(property)) {
|
||||
return `Cannot register ${property}. This matches property pattern '\\\\[.*\\\\]$' for describing language specific editor settings. Use 'configurationDefaults' contribution.`;
|
||||
}
|
||||
if (this.configurationProperties[property] !== undefined) {
|
||||
return `Cannot register ${property}. This property is already registered.`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private updatePropertyDefaultValue(property: IRegisteredConfigurationPropertySchema): void {
|
||||
let defaultValue = undefined;
|
||||
let defaultSource = undefined;
|
||||
|
||||
if (isUndefined(defaultValue)) {
|
||||
defaultValue = property.default;
|
||||
defaultSource = undefined;
|
||||
}
|
||||
if (isUndefined(defaultValue)) {
|
||||
defaultValue = jsonTypes.getDefaultValue(property.type);
|
||||
}
|
||||
|
||||
property.default = defaultValue;
|
||||
property.defaultValueSource = defaultSource;
|
||||
}
|
||||
|
||||
deregisterConfigurations(configurations: IConfigurationNode[]): void {
|
||||
const properties = new Set<string>();
|
||||
this.doDeregisterConfigurations(configurations, properties);
|
||||
|
||||
this.schemaChangeEmitter.emit();
|
||||
this.updateConfigurationEmitter.emit({ properties });
|
||||
}
|
||||
|
||||
private doDeregisterConfigurations(
|
||||
configurations: IConfigurationNode[],
|
||||
bucket: Set<string>,
|
||||
): void {
|
||||
const deregisterConfiguration = (configuration: IConfigurationNode) => {
|
||||
if (configuration.properties) {
|
||||
for (const key in configuration.properties) {
|
||||
bucket.add(key);
|
||||
delete this.configurationProperties[key];
|
||||
}
|
||||
}
|
||||
configuration.allOf?.forEach((node) => deregisterConfiguration(node));
|
||||
};
|
||||
|
||||
for (const configuration of configurations) {
|
||||
deregisterConfiguration(configuration);
|
||||
|
||||
const index = this.configurationContributors.indexOf(configuration);
|
||||
if (index !== -1) {
|
||||
this.configurationContributors.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifyConfigurationSchemaUpdated(): void {
|
||||
this.schemaChangeEmitter.emit();
|
||||
}
|
||||
|
||||
getConfigurationProperties(): StringDictionary<IRegisteredConfigurationPropertySchema> {
|
||||
return this.configurationProperties;
|
||||
}
|
||||
|
||||
getConfigurations(): IConfigurationNode[] {
|
||||
return this.configurationContributors;
|
||||
}
|
||||
|
||||
getExcludedConfigurationProperties(): StringDictionary<IRegisteredConfigurationPropertySchema> {
|
||||
return this.excludedConfigurationProperties;
|
||||
}
|
||||
|
||||
onDidUpdateConfiguration(
|
||||
fn: (change: {
|
||||
properties: ReadonlySet<string>;
|
||||
defaultsOverrides?: boolean | undefined;
|
||||
}) => void,
|
||||
) {
|
||||
return this.updateConfigurationEmitter.on(fn);
|
||||
}
|
||||
|
||||
onDidSchemaChange(fn: () => void) {
|
||||
return this.schemaChangeEmitter.on(fn);
|
||||
}
|
||||
}
|
||||
|
||||
export const Extension = {
|
||||
Configuration: 'base.contributions.configuration',
|
||||
};
|
||||
|
||||
Registry.add(Extension.Configuration, new ConfigurationRegistry());
|
||||
@ -0,0 +1,63 @@
|
||||
import { createDecorator, Provide, type Event } from '@alilc/lowcode-shared';
|
||||
import { IConfigurationOverrides, IConfigurationUpdateOverrides } from './configuration';
|
||||
|
||||
export interface IConfigurationChangeEvent {
|
||||
readonly affectedKeys: ReadonlySet<string>;
|
||||
readonly change: IConfigurationChange;
|
||||
|
||||
affectsConfiguration(configuration: string, overrides?: string[]): boolean;
|
||||
}
|
||||
|
||||
export interface IConfigurationChange {
|
||||
keys: string[];
|
||||
overrides: [string, string[]][];
|
||||
}
|
||||
|
||||
export interface IConfigurationService {
|
||||
/**
|
||||
* Fetches the value of the section for the given overrides.
|
||||
* Value can be of native type or an object keyed off the section name.
|
||||
*
|
||||
* @param section - Section of the configuration. Can be `null` or `undefined`.
|
||||
* @param overrides - Overrides that has to be applied while fetching
|
||||
*
|
||||
*/
|
||||
getValue<T>(): T;
|
||||
getValue<T>(section: string): T;
|
||||
getValue<T>(overrides: IConfigurationOverrides): T;
|
||||
getValue<T>(section: string, overrides: IConfigurationOverrides): T;
|
||||
|
||||
/**
|
||||
* Update a configuration value.
|
||||
*
|
||||
* Use `overrides` to update the configuration for a resource or for override identifiers or both.
|
||||
*
|
||||
* Passing a resource through overrides will update the configuration in the workspace folder containing that resource.
|
||||
*
|
||||
* *Note 1:* Updating configuration to a default value will remove the configuration from the requested target. If not target is passed, it will be removed from all writeable targets.
|
||||
*
|
||||
* *Note 2:* Use `undefined` value to remove the configuration from the given target. If not target is passed, it will be removed from all writeable targets.
|
||||
*
|
||||
* @param key setting to be updated
|
||||
* @param value The new value
|
||||
*/
|
||||
updateValue(key: string, value: any): Promise<void>;
|
||||
updateValue(
|
||||
key: string,
|
||||
value: any,
|
||||
overrides: IConfigurationOverrides | IConfigurationUpdateOverrides,
|
||||
): Promise<void>;
|
||||
|
||||
inspect<T>(key: string, overrides?: IConfigurationOverrides): Readonly<T>;
|
||||
|
||||
reloadConfiguration(): Promise<void>;
|
||||
|
||||
keys(): string[];
|
||||
|
||||
onDidChangeConfiguration: Event<IConfigurationChangeEvent>;
|
||||
}
|
||||
|
||||
export const IConfigurationService = createDecorator<IConfigurationService>('configurationService');
|
||||
|
||||
@Provide(IConfigurationService)
|
||||
export class ConfigurationService implements IConfigurationService {}
|
||||
3
packages/engine-core/src/configuration/index.ts
Normal file
3
packages/engine-core/src/configuration/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './configurationModel';
|
||||
export * from './configurationRegistry';
|
||||
export * from './configuration';
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user