From 642a4042c4b34dca74652a9e678f347ae66c94e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8A=9B=E7=9A=93?= Date: Mon, 2 Nov 2020 11:46:26 +0800 Subject: [PATCH 1/7] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3=20slot=20?= =?UTF-8?q?=E5=9C=A8=E5=85=B3=E9=97=AD=E6=97=B6=E6=B2=A1=E6=9C=89=E6=AD=A3?= =?UTF-8?q?=E5=B8=B8=E5=9B=9E=E6=94=B6=E8=8A=82=E7=82=B9=20fix:=20?= =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=90=8C=E5=90=8D=20slot=20=E5=9C=A8?= =?UTF-8?q?=E6=9B=BF=E6=8D=A2=E6=97=B6=E6=B2=A1=E6=9C=89=E6=AD=A3=E5=B8=B8?= =?UTF-8?q?=E5=9B=9E=E6=94=B6=E8=8A=82=E7=82=B9=20fix:=20findDOMNode=20?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA=20chore:=20=E5=9C=A8=20build-plugins=20start?= =?UTF-8?q?=20=E6=97=B6=E5=BC=80=E5=90=AF=20inline-source-map?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/demo/build.plugin.js | 5 +- packages/designer/package.json | 3 +- packages/designer/src/document/history.ts | 2 +- packages/designer/src/document/node/node.ts | 11 +- .../designer/src/document/node/props/prop.ts | 2 +- packages/designer/src/utils/slot.ts | 1 + packages/designer/tests/bugs/misc.ts | 55 ++ .../document-model/document-model.test.ts | 5 +- .../document/document-model/node.test.ts | 2 +- .../designer/tests/document/selection.test.ts | 245 ++++++++ .../tests/fixtures/component-metadata/div.ts | 272 +++++++++ .../designer/tests/fixtures/schema/form.ts | 17 +- .../tests/meta/component-meta.test.ts | 30 + packages/designer/tests/node/node.add.test.ts | 564 ++++++++++++++++++ .../designer/tests/node/node.dragdrop.test.ts | 61 ++ .../designer/tests/node/node.modify.test.ts | 456 ++++++++++++++ .../designer/tests/node/node.remove.test.ts | 122 ++++ .../designer/tests/project/project.test.ts | 194 +++--- packages/editor-preset-vision/src/editor.ts | 2 +- packages/react-renderer/package.json | 2 +- .../react-simulator-renderer/.eslintrc.js | 3 +- .../src/utils/react-find-dom-nodes.ts | 3 +- 22 files changed, 1918 insertions(+), 139 deletions(-) create mode 100644 packages/designer/tests/bugs/misc.ts create mode 100644 packages/designer/tests/document/selection.test.ts create mode 100644 packages/designer/tests/fixtures/component-metadata/div.ts create mode 100644 packages/designer/tests/meta/component-meta.test.ts create mode 100644 packages/designer/tests/node/node.add.test.ts create mode 100644 packages/designer/tests/node/node.dragdrop.test.ts create mode 100644 packages/designer/tests/node/node.modify.test.ts create mode 100644 packages/designer/tests/node/node.remove.test.ts diff --git a/packages/demo/build.plugin.js b/packages/demo/build.plugin.js index 1e50c137b..44698c569 100644 --- a/packages/demo/build.plugin.js +++ b/packages/demo/build.plugin.js @@ -1,7 +1,7 @@ const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); -module.exports = ({ onGetWebpackConfig }) => { +module.exports = ({ context, onGetWebpackConfig }) => { onGetWebpackConfig((config) => { config.resolve.plugin('tsconfigpaths').use(TsconfigPathsPlugin, [ { @@ -23,5 +23,8 @@ module.exports = ({ onGetWebpackConfig }) => { config.plugins.delete('hot'); config.devServer.hot(false); + if (context.command === 'start') { + config.devtool('inline-source-map'); + } }); }; diff --git a/packages/designer/package.json b/packages/designer/package.json index d8dd526f8..54d91ab75 100644 --- a/packages/designer/package.json +++ b/packages/designer/package.json @@ -33,8 +33,7 @@ "build-plugin-component": "^0.2.10", "build-scripts-config": "^0.1.8", "jest": "^26.5.2", - "lodash.clonedeep": "^4.5.0", - "lodash.set": "^4.3.2", + "lodash": "^4.17.20", "ts-jest": "^26.4.1", "typescript": "^4.0.3" }, diff --git a/packages/designer/src/document/history.ts b/packages/designer/src/document/history.ts index 972fced01..61f1539d0 100644 --- a/packages/designer/src/document/history.ts +++ b/packages/designer/src/document/history.ts @@ -222,7 +222,7 @@ class Session { end() { if (this.isActive()) { this.clearTimer(); - console.info('session end'); + // console.info('session end'); } } diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts index df0ebef6e..01ee191a2 100644 --- a/packages/designer/src/document/node/node.ts +++ b/packages/designer/src/document/node/node.ts @@ -23,6 +23,7 @@ import { ReactElement } from 'react'; import { SettingTopEntry } from 'designer/src/designer'; import { EventEmitter } from 'events'; import { includeSlot, removeSlot } from '../../utils/slot'; +import { foreachReverse } from '../../utils/tree'; /** * 基础节点 @@ -594,7 +595,11 @@ export class Node { import(data: Schema, checkId = false) { const { componentName, id, children, props, ...extras } = data; - + if (this.isSlot()) { + foreachReverse(this.children, (subNode: Node) => { + subNode.remove(true, true); + }, (iterable, idx) => (iterable as NodeChildren).get(idx)); + } if (this.isParental()) { this.props.import(props, extras); (this._children as NodeChildren).import(children, checkId); @@ -709,12 +714,12 @@ export class Node { } addSlot(slotNode: Node) { - slotNode.internalSetParent(this as ParentalNode, true); const slotName = slotNode?.getExtraProp('name')?.getAsString(); // 一个组件下的所有 slot,相同 slotName 的 slot 应该是唯一的 if (includeSlot(this, slotName)) { removeSlot(this, slotName); } + slotNode.internalSetParent(this as ParentalNode, true); this._slots.push(slotNode); } @@ -756,7 +761,7 @@ export class Node { this.purged = true; this.autoruns?.forEach((dispose) => dispose()); this.props.purge(); - this.document.destroyNode(this); + // this.document.destroyNode(this); } /** diff --git a/packages/designer/src/document/node/props/prop.ts b/packages/designer/src/document/node/props/prop.ts index 704a99ba6..8fdea7d66 100644 --- a/packages/designer/src/document/node/props/prop.ts +++ b/packages/designer/src/document/node/props/prop.ts @@ -269,7 +269,7 @@ export class Prop implements IPropParent { this.stash.clear(); } if (this._type !== 'slot' && this._slotNode) { - this._slotNode.purge(); + this._slotNode.remove(); this._slotNode = undefined; } } diff --git a/packages/designer/src/utils/slot.ts b/packages/designer/src/utils/slot.ts index 9fe1ef5f7..9061326b1 100644 --- a/packages/designer/src/utils/slot.ts +++ b/packages/designer/src/utils/slot.ts @@ -11,6 +11,7 @@ export function removeSlot(node: Node, slotName: string | undefined): boolean { const { slots = [] } = node; return slots.some((slot, idx) => { if (slotName && slotName === slot?.getExtraProp('name')?.getAsString()) { + slot.remove(); slots.splice(idx, 1); return true; } diff --git a/packages/designer/tests/bugs/misc.ts b/packages/designer/tests/bugs/misc.ts new file mode 100644 index 000000000..e101d868f --- /dev/null +++ b/packages/designer/tests/bugs/misc.ts @@ -0,0 +1,55 @@ +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 { experimental: 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); +}); \ No newline at end of file diff --git a/packages/designer/tests/document/document-model/document-model.test.ts b/packages/designer/tests/document/document-model/document-model.test.ts index ca0c9f173..94eda1871 100644 --- a/packages/designer/tests/document/document-model/document-model.test.ts +++ b/packages/designer/tests/document/document-model/document-model.test.ts @@ -1,12 +1,11 @@ import '../../fixtures/window'; -console.log('window.matchMedia', window.matchMedia); window.matchMedia('width=600px'); import { DocumentModel } from '../../../src/document/document-model'; // const { DocumentModel } = require('../../../src/document/document-model'); // const { Node } = require('../__mocks__/node'); -describe('basic utility', () => { - test.only('delegateMethod - useOriginMethodName', () => { +describe.skip('basic utility', () => { + test('delegateMethod - useOriginMethodName', () => { const node = new DocumentModel({}, { componentName: 'Component', diff --git a/packages/designer/tests/document/document-model/node.test.ts b/packages/designer/tests/document/document-model/node.test.ts index e183aa6bd..29ea34123 100644 --- a/packages/designer/tests/document/document-model/node.test.ts +++ b/packages/designer/tests/document/document-model/node.test.ts @@ -17,7 +17,7 @@ jest.mock('../../../src/document/document-model', () => { }; }); -describe('basic utility', () => { +describe.skip('basic utility', () => { test('delegateMethod - useOriginMethodName', () => { const dm = new DocumentModel({} as any, {} as any); console.log(dm.nextId); diff --git a/packages/designer/tests/document/selection.test.ts b/packages/designer/tests/document/selection.test.ts new file mode 100644 index 000000000..4d81e935e --- /dev/null +++ b/packages/designer/tests/document/selection.test.ts @@ -0,0 +1,245 @@ +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 { experimental: null }; + }, + }; + }, + 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'); + 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('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', 'form2']); + + const selectionChangeHandler = jest.fn(); + selection.onSelectionChange(selectionChangeHandler); + selection.dispose(); + + expect(selectionChangeHandler).toHaveBeenCalledTimes(1); + expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form', 'node_k1ow3cbj']); + expect(selection.selected).toEqual(['form', 'node_k1ow3cbj']); + selectionChangeHandler.mockClear(); + }); + + 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', 'form2']); + + const selectionChangeHandler = jest.fn(); + selection.onSelectionChange(selectionChangeHandler); + selection.dispose(); + + expect(selectionChangeHandler).toHaveBeenCalledTimes(1); + expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['form', 'node_k1ow3cbj']); + expect(selection.selected).toEqual(['form', 'node_k1ow3cbj']); + selectionChangeHandler.mockClear(); + }); + + 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('node_k1ow3cb9'))).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('node_k1ow3cb9'); + expect(selectionChangeHandler).toHaveBeenCalledTimes(1); + expect(selectionChangeHandler.mock.calls[0][0]).toEqual(['node_k1ow3cb9']); + expect(selection.selected).toEqual(['node_k1ow3cb9']); + expect(selection.has('node_k1ow3cb9')).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('node_k1ow3cb9'); + expect(selectionChangeHandler).toHaveBeenCalledTimes(0); + expect(selection.selected).toEqual(['node_k1ow3cb9']); + selectionChangeHandler.mockClear(); + }); +}); \ No newline at end of file diff --git a/packages/designer/tests/fixtures/component-metadata/div.ts b/packages/designer/tests/fixtures/component-metadata/div.ts new file mode 100644 index 000000000..27bcd18fc --- /dev/null +++ b/packages/designer/tests/fixtures/component-metadata/div.ts @@ -0,0 +1,272 @@ +export default { + componentName: 'Div', + title: '容器', + docUrl: 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md', + 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: {}, + }, + 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: [], + }, +}; diff --git a/packages/designer/tests/fixtures/schema/form.ts b/packages/designer/tests/fixtures/schema/form.ts index 8438c8b39..903b21756 100644 --- a/packages/designer/tests/fixtures/schema/form.ts +++ b/packages/designer/tests/fixtures/schema/form.ts @@ -1,6 +1,7 @@ export default { componentName: 'Page', id: 'node_k1ow3cb9', + title: 'hey, i\' a page!', props: { extensions: { 启用页头: true, @@ -111,7 +112,8 @@ export default { children: [ { componentName: 'Form', - id: 'node_k1ow3cbq', + id: 'form', + extraPropA: 'extraPropA', props: { size: 'medium', labelAlign: 'top', @@ -123,9 +125,15 @@ export default { type: 'variable', variable: 'state.formData', }, + obj: { + a: 1, + b: false, + c: 'string', + }, __style__: {}, fieldId: 'form', fieldOptions: {}, + slotA: '', }, condition: true, children: [ @@ -949,6 +957,13 @@ export default { }, __style__: ':root {\n width: 80px;\n}', fieldId: 'button_k1ow3h1p', + greeting: { + // type: 'JSSlot', + value: [{ + componentName: 'Text', + props: {}, + }] + } }, condition: true, }, diff --git a/packages/designer/tests/meta/component-meta.test.ts b/packages/designer/tests/meta/component-meta.test.ts new file mode 100644 index 000000000..cec01ba5f --- /dev/null +++ b/packages/designer/tests/meta/component-meta.test.ts @@ -0,0 +1,30 @@ +import set from 'lodash/set'; +import cloneDeep from 'lodash/cloneDeep'; +import '../fixtures/window'; +import { Node } from '../../src/document/node/node'; +import { Designer } from '../../src/designer/designer'; +import divMeta from '../fixtures/component-metadata/div'; +import { ComponentMeta } from '../../src/component-meta'; + +const mockCreateSettingEntry = jest.fn(); +jest.mock('../../src/designer/designer', () => { + return { + Designer: jest.fn().mockImplementation(() => { + return { + getGlobalComponentActions: () => [], + }; + }), + }; +}); + +let designer = null; +beforeAll(() => { + designer = new Designer({}); +}); + +describe('组件元数据处理', () => { + it('构造函数', () => { + const meta = new ComponentMeta(designer, divMeta); + console.log(meta); + }); +}); \ No newline at end of file diff --git a/packages/designer/tests/node/node.add.test.ts b/packages/designer/tests/node/node.add.test.ts new file mode 100644 index 000000000..9da7a3473 --- /dev/null +++ b/packages/designer/tests/node/node.add.test.ts @@ -0,0 +1,564 @@ +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'; +import { EBADF } from 'constants'; + +const mockCreateSettingEntry = jest.fn(); +jest.mock('../../src/designer/designer', () => { + return { + Designer: jest.fn().mockImplementation(() => { + return { + getComponentMeta() { + return { + getMetadata() { + return { experimental: null }; + }, + }; + }, + 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(); + }); + afterEach(() => { + project.unload(); + }); + it('基本的节点模型初始化,模型导出', () => { + 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 pageNode = currentDocument?.getNode('node_k1ow3cb9'); + expect(pageNode?.getComponentName()).toBe('Page'); + expect(pageNode?.getIcon()).toBeUndefined; + + const exportSchema = currentDocument?.export(1); + expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt); + expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt); + }); + + it('基本的节点模型初始化,节点深度', () => { + expect(project).toBeTruthy(); + const { currentDocument } = project; + const getNode = currentDocument.getNode.bind(currentDocument); + + const pageNode = getNode('node_k1ow3cb9'); + 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('node_k1ow3cb9'); + 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('node_k1ow3cb9'); + const nodeCreateHandler = jest.fn(); + currentDocument?.onNodeCreate(nodeCreateHandler); + + const node = createNode({ + componentName: 'TextInput', + props: { + propA: 'haha', + } + }); + 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(); + 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'); + }); + + 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('node_k1ow3cb9'); + 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'); + 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; + const formNode = nodesMap.get('form'); + 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; + const formNode = nodesMap.get('form') as Node; + 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; + const formNode = nodesMap.get('form'); + 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; + const formNode = nodesMap.get('form'); + 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; + const formNode = nodesMap.get('form'); + const inputNode = currentDocument?.createNode({ + componentName: 'TextInput', + id: 'nodeschema-id2', + props: { + propA: 'haha', + propB: 3 + } + }); + 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; + 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; + 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; + 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('场景二:插入 Node 实例,指定 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); + 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; + 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); + }); + }); +}); \ No newline at end of file diff --git a/packages/designer/tests/node/node.dragdrop.test.ts b/packages/designer/tests/node/node.dragdrop.test.ts new file mode 100644 index 000000000..be15bdfa7 --- /dev/null +++ b/packages/designer/tests/node/node.dragdrop.test.ts @@ -0,0 +1,61 @@ +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 { experimental: 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); + }); + + + }); +}); \ No newline at end of file diff --git a/packages/designer/tests/node/node.modify.test.ts b/packages/designer/tests/node/node.modify.test.ts new file mode 100644 index 000000000..e064d273f --- /dev/null +++ b/packages/designer/tests/node/node.modify.test.ts @@ -0,0 +1,456 @@ +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 { experimental: null }; + }, + }; + }, + 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); + }); + }); +}); \ No newline at end of file diff --git a/packages/designer/tests/node/node.remove.test.ts b/packages/designer/tests/node/node.remove.test.ts new file mode 100644 index 000000000..f68d57ca1 --- /dev/null +++ b/packages/designer/tests/node/node.remove.test.ts @@ -0,0 +1,122 @@ +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 { experimental: null }; + }, + }; + }, + 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); + }); +}); \ No newline at end of file diff --git a/packages/designer/tests/project/project.test.ts b/packages/designer/tests/project/project.test.ts index 54ccedf18..31ece4526 100644 --- a/packages/designer/tests/project/project.test.ts +++ b/packages/designer/tests/project/project.test.ts @@ -1,8 +1,8 @@ -import set from 'lodash.set'; -import cloneDeep from 'lodash.clonedeep'; +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 { Node } from '../../src/document/node/node'; import { Designer } from '../../src/designer/designer'; import formSchema from '../fixtures/schema/form'; import { getIdsFromSchema, getNodeFromSchemaById } from '../utils'; @@ -34,7 +34,11 @@ beforeAll(() => { describe('schema 生成节点模型测试', () => { describe('block ❌ | component ❌ | slot ❌', () => { - it('基本的节点模型初始化,模型导出', () => { + beforeEach(() => { + mockCreateSettingEntry.mockClear(); + }); + + it('基本的节点模型初始化,模型导出,初始化传入 schema', () => { const project = new Project(designer, { componentsTree: [ formSchema, @@ -56,129 +60,75 @@ describe('schema 生成节点模型测试', () => { expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt); }); - describe('节点新增(insertNode)', () => { - let project; - beforeEach(() => { - project = new Project(designer, { - componentsTree: [ - formSchema, - ], - }); - project.open(); - }); - it.only('场景一:插入 NodeSchema', () => { - expect(project).toBeTruthy(); - const ids = getIdsFromSchema(formSchema); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const formNode = nodesMap.get('node_k1ow3cbq'); - 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('基本的节点模型初始化,模型导出,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); }); - it.only('场景一:插入 NodeSchema,有 children', () => { - expect(project).toBeTruthy(); - const ids = getIdsFromSchema(formSchema); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const formNode = nodesMap.get('node_k1ow3cbq'); - 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.only('场景一:插入 NodeSchema,id 与现有 schema 重复', () => { - expect(project).toBeTruthy(); - const ids = getIdsFromSchema(formSchema); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const formNode = nodesMap.get('node_k1ow3cbq'); - 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.only('场景一:插入 NodeSchema,id 与现有 schema 重复,但关闭了 id 检测器', () => { - expect(project).toBeTruthy(); - const ids = getIdsFromSchema(formSchema); - const { currentDocument } = project; - const { nodesMap } = currentDocument; - const formNode = nodesMap.get('node_k1ow3cbq'); - 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; - const formNode = nodesMap.get('node_k1ow3cbq'); - const inputNode = currentDocument?.createNode({ - componentName: 'TextInput', - id: 'nodeschema-id2', - props: { - propA: 'haha', - propB: 3 - } - }); - expect(inputNode.id).toBe('nodeschema-id2'); - currentDocument?.insertNode(formNode, inputNode); - expect(nodesMap.get('nodeschema-id2').componentName).toBe('TextInput'); - expect(nodesMap.size).toBe(ids.length + 1); - }); - - it('场景二:插入 JSExpression', () => {}); + const exportSchema = currentDocument?.export(1); + expect(getIdsFromSchema(exportSchema).length).toBe(expectedNodeCnt); + expect(mockCreateSettingEntry).toBeCalledTimes(expectedNodeCnt); }); - }) + it('project 卸载所有 document - unload()', () => { + const project = new Project(designer); + project.open(formSchema); + expect(project).toBeTruthy(); + const { currentDocument, documents } = project; - it('block ❌ | component ❌ | slot ✅', () => { - const formSchemaWithSlot = set(cloneDeep(formSchema), 'children[0].children[0].props.title.type', 'JSSlot'); - const project = new Project(designer, { - componentsTree: [ - formSchemaWithSlot, - ], + expect(documents).toHaveLength(1); + expect(currentDocument).toBe(documents[0]); + + project.unload(); + + expect(documents).toHaveLength(0); }); - 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); + + 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); + }); + }); + + 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 测试', () => { + }); }); \ No newline at end of file diff --git a/packages/editor-preset-vision/src/editor.ts b/packages/editor-preset-vision/src/editor.ts index 6a1933c54..6d1cdd67d 100644 --- a/packages/editor-preset-vision/src/editor.ts +++ b/packages/editor-preset-vision/src/editor.ts @@ -127,7 +127,7 @@ designer.addPropsReducer((props, node) => { !isJSBlock(ov) && !isJSSlot(ov) && !isVariable(ov) && - isString(v)) { + (isString(v) || isI18NObject(v))) { newProps[item.name] = convertToI18NObject(v); } } catch (e) { diff --git a/packages/react-renderer/package.json b/packages/react-renderer/package.json index 9f46448c7..946612931 100644 --- a/packages/react-renderer/package.json +++ b/packages/react-renderer/package.json @@ -54,5 +54,5 @@ "publishConfig": { "registry": "http://registry.npm.alibaba-inc.com" }, - "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-react-renderer@0.13.1-9/build/index.html" + "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-react-renderer@0.13.1-10/build/index.html" } diff --git a/packages/react-simulator-renderer/.eslintrc.js b/packages/react-simulator-renderer/.eslintrc.js index 31d2e19c3..39a7d9067 100644 --- a/packages/react-simulator-renderer/.eslintrc.js +++ b/packages/react-simulator-renderer/.eslintrc.js @@ -11,6 +11,7 @@ module.exports = { 'no-shadow': 0, 'no-prototype-builtins': 0, 'array-callback-return': 0, - '@typescript-eslint/member-ordering': 0 + '@typescript-eslint/member-ordering': 0, + 'react/no-find-dom-node', 0 } } \ No newline at end of file diff --git a/packages/react-simulator-renderer/src/utils/react-find-dom-nodes.ts b/packages/react-simulator-renderer/src/utils/react-find-dom-nodes.ts index f7dde3925..36aa3bfb2 100644 --- a/packages/react-simulator-renderer/src/utils/react-find-dom-nodes.ts +++ b/packages/react-simulator-renderer/src/utils/react-find-dom-nodes.ts @@ -1,4 +1,5 @@ import { ReactInstance } from 'react'; +import { findDOMNode } from 'react-dom'; import { isElement } from '@ali/lowcode-utils'; import { isDOMNode } from './is-dom-node'; @@ -29,5 +30,5 @@ export function reactFindDOMNodes(elem: ReactInstance | null): Array = []; const fiberNode = (elem as any)[FIBER_KEY]; elementsFromFiber(fiberNode.child, elements); - return elements.length > 0 ? elements : null; + return elements.length > 0 ? elements : [findDOMNode(elem)]; } From 0e7268fae3c55edd6f2c547a424a6d52b261574b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8A=9B=E7=9A=93?= Date: Mon, 2 Nov 2020 14:06:20 +0800 Subject: [PATCH 2/7] v0.13.1-11 --- CHANGELOG.md | 11 +++++++ lerna.json | 2 +- packages/code-generator/CHANGELOG.md | 8 +++++ packages/code-generator/package.json | 2 +- packages/demo-server/CHANGELOG.md | 8 +++++ packages/demo-server/package.json | 2 +- packages/demo/CHANGELOG.md | 11 +++++++ packages/demo/package.json | 30 +++++++++---------- packages/designer/CHANGELOG.md | 11 +++++++ packages/designer/package.json | 8 ++--- packages/editor-core/CHANGELOG.md | 8 +++++ packages/editor-core/package.json | 6 ++-- packages/editor-preset-general/CHANGELOG.md | 8 +++++ packages/editor-preset-general/package.json | 16 +++++----- packages/editor-preset-vision/CHANGELOG.md | 11 +++++++ packages/editor-preset-vision/package.json | 12 ++++---- packages/editor-setters/CHANGELOG.md | 8 +++++ packages/editor-setters/package.json | 4 +-- packages/editor-skeleton/CHANGELOG.md | 8 +++++ packages/editor-skeleton/package.json | 10 +++---- packages/material-parser/CHANGELOG.md | 8 +++++ packages/material-parser/package.json | 2 +- packages/plugin-components-pane/CHANGELOG.md | 8 +++++ packages/plugin-components-pane/package.json | 8 ++--- packages/plugin-designer/CHANGELOG.md | 8 +++++ packages/plugin-designer/package.json | 6 ++-- .../plugin-event-bind-dialog/CHANGELOG.md | 8 +++++ .../plugin-event-bind-dialog/package.json | 6 ++-- packages/plugin-outline-pane/CHANGELOG.md | 8 +++++ packages/plugin-outline-pane/package.json | 10 +++---- packages/plugin-sample-logo/CHANGELOG.md | 8 +++++ packages/plugin-sample-logo/package.json | 4 +-- packages/plugin-sample-preview/CHANGELOG.md | 8 +++++ packages/plugin-sample-preview/package.json | 12 ++++---- packages/plugin-source-editor/CHANGELOG.md | 8 +++++ packages/plugin-source-editor/package.json | 4 +-- packages/plugin-undo-redo/CHANGELOG.md | 8 +++++ packages/plugin-undo-redo/package.json | 12 ++++---- .../plugin-variable-bind-dialog/CHANGELOG.md | 8 +++++ .../plugin-variable-bind-dialog/package.json | 4 +-- packages/plugin-zh-en/CHANGELOG.md | 8 +++++ packages/plugin-zh-en/package.json | 8 ++--- packages/rax-provider/CHANGELOG.md | 8 +++++ packages/rax-provider/package.json | 4 +-- packages/rax-render/CHANGELOG.md | 12 ++++++-- packages/rax-render/package.json | 4 +-- packages/rax-simulator-renderer/CHANGELOG.md | 8 +++++ packages/rax-simulator-renderer/package.json | 12 ++++---- packages/react-provider/CHANGELOG.md | 8 +++++ packages/react-provider/package.json | 4 +-- packages/react-renderer/CHANGELOG.md | 11 +++++++ packages/react-renderer/package.json | 2 +- .../react-simulator-renderer/CHANGELOG.md | 11 +++++++ .../react-simulator-renderer/package.json | 10 +++---- packages/runtime/CHANGELOG.md | 8 +++++ packages/runtime/package.json | 2 +- packages/types/CHANGELOG.md | 8 +++++ packages/types/package.json | 2 +- packages/utils/CHANGELOG.md | 8 +++++ packages/utils/package.json | 4 +-- 60 files changed, 366 insertions(+), 108 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfa027327..b7813f9b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + +### Bug Fixes + +* 解决 slot 在关闭时没有正常回收节点 ([642a404](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/642a404)) + + + + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/lerna.json b/lerna.json index 65f6a7d0d..65c7d3bd9 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "lerna": "2.11.0", - "version": "0.13.1-10", + "version": "0.13.1-11", "npmClient": "tyarn", "registry": "http://registry.npm.alibaba-inc.com", "useWorkspaces": true, diff --git a/packages/code-generator/CHANGELOG.md b/packages/code-generator/CHANGELOG.md index 9936155ba..ba3fca12c 100644 --- a/packages/code-generator/CHANGELOG.md +++ b/packages/code-generator/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-code-generator + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/code-generator/package.json b/packages/code-generator/package.json index 44be8fe8d..c60f82331 100644 --- a/packages/code-generator/package.json +++ b/packages/code-generator/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-code-generator", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "出码引擎 for LowCode Engine", "main": "lib/index.js", "files": [ diff --git a/packages/demo-server/CHANGELOG.md b/packages/demo-server/CHANGELOG.md index 61e3ef71b..545b5d9ac 100644 --- a/packages/demo-server/CHANGELOG.md +++ b/packages/demo-server/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-demo-server + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/demo-server/package.json b/packages/demo-server/package.json index ee5d486f8..e43c5ed7a 100644 --- a/packages/demo-server/package.json +++ b/packages/demo-server/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-demo-server", - "version": "0.13.1-10", + "version": "0.13.1-11", "private": true, "description": "低代码引擎 DEMO Server 端", "scripts": { diff --git a/packages/demo/CHANGELOG.md b/packages/demo/CHANGELOG.md index 016ccb49d..a1f655d59 100644 --- a/packages/demo/CHANGELOG.md +++ b/packages/demo/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + +### Bug Fixes + +* 解决 slot 在关闭时没有正常回收节点 ([642a404](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/642a404)) + + + + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/demo/package.json b/packages/demo/package.json index 26906a6ad..7dbf7c074 100644 --- a/packages/demo/package.json +++ b/packages/demo/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-demo", - "version": "0.13.1-10", + "version": "0.13.1-11", "private": true, "description": "低代码引擎 DEMO", "scripts": { @@ -9,21 +9,21 @@ }, "config": {}, "dependencies": { - "@ali/lowcode-editor-core": "^0.13.1-10", - "@ali/lowcode-editor-skeleton": "^0.13.1-10", - "@ali/lowcode-plugin-components-pane": "^0.13.1-10", - "@ali/lowcode-plugin-designer": "^0.13.1-10", - "@ali/lowcode-plugin-event-bind-dialog": "^0.13.1-10", - "@ali/lowcode-plugin-outline-pane": "^0.13.1-10", - "@ali/lowcode-plugin-sample-logo": "^0.13.1-10", - "@ali/lowcode-plugin-sample-preview": "^0.13.1-10", + "@ali/lowcode-editor-core": "^0.13.1-11", + "@ali/lowcode-editor-skeleton": "^0.13.1-11", + "@ali/lowcode-plugin-components-pane": "^0.13.1-11", + "@ali/lowcode-plugin-designer": "^0.13.1-11", + "@ali/lowcode-plugin-event-bind-dialog": "^0.13.1-11", + "@ali/lowcode-plugin-outline-pane": "^0.13.1-11", + "@ali/lowcode-plugin-sample-logo": "^0.13.1-11", + "@ali/lowcode-plugin-sample-preview": "^0.13.1-11", "@ali/lowcode-plugin-settings-pane": "^0.8.8", - "@ali/lowcode-plugin-undo-redo": "^0.13.1-10", - "@ali/lowcode-plugin-variable-bind-dialog": "^0.13.1-10", - "@ali/lowcode-plugin-zh-en": "^0.13.1-10", - "@ali/lowcode-react-renderer": "^0.13.1-10", - "@ali/lowcode-runtime": "^0.13.1-10", - "@ali/lowcode-utils": "^0.13.1-10", + "@ali/lowcode-plugin-undo-redo": "^0.13.1-11", + "@ali/lowcode-plugin-variable-bind-dialog": "^0.13.1-11", + "@ali/lowcode-plugin-zh-en": "^0.13.1-11", + "@ali/lowcode-react-renderer": "^0.13.1-11", + "@ali/lowcode-runtime": "^0.13.1-11", + "@ali/lowcode-utils": "^0.13.1-11", "@ali/ve-action-pane": "^4.7.0-beta.0", "@ali/ve-datapool-pane": "^6.4.3", "@ali/ve-history-pane": "4.0.0", diff --git a/packages/designer/CHANGELOG.md b/packages/designer/CHANGELOG.md index dca6d9f0e..776adcdef 100644 --- a/packages/designer/CHANGELOG.md +++ b/packages/designer/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + +### Bug Fixes + +* 解决 slot 在关闭时没有正常回收节点 ([642a404](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/642a404)) + + + + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/designer/package.json b/packages/designer/package.json index 54d91ab75..073ee151e 100644 --- a/packages/designer/package.json +++ b/packages/designer/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-designer", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "Designer for Ali LowCode Engine", "main": "lib/index.js", "module": "es/index.js", @@ -14,9 +14,9 @@ }, "license": "MIT", "dependencies": { - "@ali/lowcode-editor-core": "^0.13.1-10", - "@ali/lowcode-types": "^0.13.1-10", - "@ali/lowcode-utils": "^0.13.1-10", + "@ali/lowcode-editor-core": "^0.13.1-11", + "@ali/lowcode-types": "^0.13.1-11", + "@ali/lowcode-utils": "^0.13.1-11", "classnames": "^2.2.6", "event": "^1.0.0", "react": "^16", diff --git a/packages/editor-core/CHANGELOG.md b/packages/editor-core/CHANGELOG.md index 2cb4a3616..e597100d8 100644 --- a/packages/editor-core/CHANGELOG.md +++ b/packages/editor-core/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-editor-core + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/editor-core/package.json b/packages/editor-core/package.json index 67c63b3ed..dd21910b9 100644 --- a/packages/editor-core/package.json +++ b/packages/editor-core/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-editor-core", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "Core Api for Ali lowCode engine", "license": "MIT", "main": "lib/index.js", @@ -15,8 +15,8 @@ "cloud-build": "build-scripts build --skip-demo" }, "dependencies": { - "@ali/lowcode-types": "^0.13.1-10", - "@ali/lowcode-utils": "^0.13.1-10", + "@ali/lowcode-types": "^0.13.1-11", + "@ali/lowcode-utils": "^0.13.1-11", "@alifd/next": "^1.19.16", "@recore/obx": "^1.0.9", "@recore/obx-react": "^1.0.8", diff --git a/packages/editor-preset-general/CHANGELOG.md b/packages/editor-preset-general/CHANGELOG.md index 5908c87fe..5c268dc06 100644 --- a/packages/editor-preset-general/CHANGELOG.md +++ b/packages/editor-preset-general/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-editor-preset-general + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/editor-preset-general/package.json b/packages/editor-preset-general/package.json index 1028c9a31..564ebdca1 100644 --- a/packages/editor-preset-general/package.json +++ b/packages/editor-preset-general/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-editor-preset-general", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "Ali General Editor Preset", "main": "lib/index.js", "files": [ @@ -14,12 +14,12 @@ }, "license": "MIT", "dependencies": { - "@ali/lowcode-editor-core": "^0.13.1-10", - "@ali/lowcode-editor-skeleton": "^0.13.1-10", - "@ali/lowcode-plugin-designer": "^0.13.1-10", - "@ali/lowcode-plugin-outline-pane": "^0.13.1-10", - "@ali/lowcode-types": "^0.13.1-10", - "@ali/lowcode-utils": "^0.13.1-10", + "@ali/lowcode-editor-core": "^0.13.1-11", + "@ali/lowcode-editor-skeleton": "^0.13.1-11", + "@ali/lowcode-plugin-designer": "^0.13.1-11", + "@ali/lowcode-plugin-outline-pane": "^0.13.1-11", + "@ali/lowcode-types": "^0.13.1-11", + "@ali/lowcode-utils": "^0.13.1-11", "@alifd/next": "^1.19.12", "@alife/theme-lowcode-dark": "^0.1.0", "@alife/theme-lowcode-light": "^0.1.0", @@ -27,7 +27,7 @@ "react-dom": "^16.8.1" }, "devDependencies": { - "@ali/lowcode-editor-setters": "^0.13.1-10", + "@ali/lowcode-editor-setters": "^0.13.1-11", "@alib/build-scripts": "^0.1.18", "@types/events": "^3.0.0", "@types/react": "^16.8.3", diff --git a/packages/editor-preset-vision/CHANGELOG.md b/packages/editor-preset-vision/CHANGELOG.md index 631e70cbb..25459023d 100644 --- a/packages/editor-preset-vision/CHANGELOG.md +++ b/packages/editor-preset-vision/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + +### Bug Fixes + +* 解决 slot 在关闭时没有正常回收节点 ([642a404](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/642a404)) + + + + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/editor-preset-vision/package.json b/packages/editor-preset-vision/package.json index 0e151ef88..7a53ef1ab 100644 --- a/packages/editor-preset-vision/package.json +++ b/packages/editor-preset-vision/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-editor-preset-vision", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "Vision Polyfill for Ali lowCode engine", "main": "lib/index.js", "private": true, @@ -15,11 +15,11 @@ }, "license": "MIT", "dependencies": { - "@ali/lowcode-designer": "^0.13.1-10", - "@ali/lowcode-editor-core": "^0.13.1-10", - "@ali/lowcode-editor-skeleton": "^0.13.1-10", - "@ali/lowcode-plugin-designer": "^0.13.1-10", - "@ali/lowcode-plugin-outline-pane": "^0.13.1-10", + "@ali/lowcode-designer": "^0.13.1-11", + "@ali/lowcode-editor-core": "^0.13.1-11", + "@ali/lowcode-editor-skeleton": "^0.13.1-11", + "@ali/lowcode-plugin-designer": "^0.13.1-11", + "@ali/lowcode-plugin-outline-pane": "^0.13.1-11", "@ali/ve-i18n-util": "^2.0.0", "@ali/ve-icons": "^4.1.9", "@ali/ve-less-variables": "2.0.3", diff --git a/packages/editor-setters/CHANGELOG.md b/packages/editor-setters/CHANGELOG.md index e8c8ce42b..983509ec1 100644 --- a/packages/editor-setters/CHANGELOG.md +++ b/packages/editor-setters/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-editor-setters + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/editor-setters/package.json b/packages/editor-setters/package.json index c1410c53f..25818876b 100644 --- a/packages/editor-setters/package.json +++ b/packages/editor-setters/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-editor-setters", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "Builtin setters for Ali lowCode engine", "files": [ "es", @@ -22,7 +22,7 @@ "@ali/iceluna-comp-react-node": "^1.0.5", "@ali/iceluna-sdk": "^1.0.5-beta.24", "@ali/lc-style-setter": "^0.0.1", - "@ali/lowcode-editor-core": "^0.13.1-10", + "@ali/lowcode-editor-core": "^0.13.1-11", "@alifd/next": "^1.19.16", "acorn": "^6.4.1", "classnames": "^2.2.6", diff --git a/packages/editor-skeleton/CHANGELOG.md b/packages/editor-skeleton/CHANGELOG.md index d464b346f..ca517c4cf 100644 --- a/packages/editor-skeleton/CHANGELOG.md +++ b/packages/editor-skeleton/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-editor-skeleton + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/editor-skeleton/package.json b/packages/editor-skeleton/package.json index 1affbb8e5..dc45518cb 100644 --- a/packages/editor-skeleton/package.json +++ b/packages/editor-skeleton/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-editor-skeleton", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "alibaba lowcode editor skeleton", "main": "lib/index.js", "module": "es/index.js", @@ -19,10 +19,10 @@ "editor" ], "dependencies": { - "@ali/lowcode-designer": "^0.13.1-10", - "@ali/lowcode-editor-core": "^0.13.1-10", - "@ali/lowcode-types": "^0.13.1-10", - "@ali/lowcode-utils": "^0.13.1-10", + "@ali/lowcode-designer": "^0.13.1-11", + "@ali/lowcode-editor-core": "^0.13.1-11", + "@ali/lowcode-types": "^0.13.1-11", + "@ali/lowcode-utils": "^0.13.1-11", "@ali/ve-icons": "latest", "@ali/ve-less-variables": "^2.0.0", "@alifd/next": "^1.20.12", diff --git a/packages/material-parser/CHANGELOG.md b/packages/material-parser/CHANGELOG.md index abe423a4a..da624deaf 100644 --- a/packages/material-parser/CHANGELOG.md +++ b/packages/material-parser/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-material-parser + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/material-parser/package.json b/packages/material-parser/package.json index b1eb4eba9..631a22f73 100644 --- a/packages/material-parser/package.json +++ b/packages/material-parser/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-material-parser", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "material parser for Ali lowCode engine", "main": "lib/index.js", "files": [ diff --git a/packages/plugin-components-pane/CHANGELOG.md b/packages/plugin-components-pane/CHANGELOG.md index 68dcc90de..287e0d051 100644 --- a/packages/plugin-components-pane/CHANGELOG.md +++ b/packages/plugin-components-pane/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-components-pane + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/plugin-components-pane/package.json b/packages/plugin-components-pane/package.json index 4cf036e2f..bd529106e 100644 --- a/packages/plugin-components-pane/package.json +++ b/packages/plugin-components-pane/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-components-pane", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "alibaba lowcode editor component-list plugin", "files": [ "es/", @@ -20,9 +20,9 @@ ], "author": "xiayang.xy", "dependencies": { - "@ali/lowcode-designer": "^0.13.1-10", - "@ali/lowcode-editor-core": "^0.13.1-10", - "@ali/lowcode-types": "^0.13.1-10", + "@ali/lowcode-designer": "^0.13.1-11", + "@ali/lowcode-editor-core": "^0.13.1-11", + "@ali/lowcode-types": "^0.13.1-11", "@alifd/next": "^1.19.19", "react": "^16.8.1" }, diff --git a/packages/plugin-designer/CHANGELOG.md b/packages/plugin-designer/CHANGELOG.md index dff580821..0c99f3d48 100644 --- a/packages/plugin-designer/CHANGELOG.md +++ b/packages/plugin-designer/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-designer + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/plugin-designer/package.json b/packages/plugin-designer/package.json index 0be101962..d7b7ac77c 100644 --- a/packages/plugin-designer/package.json +++ b/packages/plugin-designer/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-designer", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "alibaba lowcode editor designer plugin", "files": [ "es", @@ -20,8 +20,8 @@ ], "author": "xiayang.xy", "dependencies": { - "@ali/lowcode-designer": "^0.13.1-10", - "@ali/lowcode-editor-core": "^0.13.1-10", + "@ali/lowcode-designer": "^0.13.1-11", + "@ali/lowcode-editor-core": "^0.13.1-11", "react": "^16.8.1", "react-dom": "^16.8.1" }, diff --git a/packages/plugin-event-bind-dialog/CHANGELOG.md b/packages/plugin-event-bind-dialog/CHANGELOG.md index 69bfdd3c3..8aa8b4d35 100644 --- a/packages/plugin-event-bind-dialog/CHANGELOG.md +++ b/packages/plugin-event-bind-dialog/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-event-bind-dialog + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/plugin-event-bind-dialog/package.json b/packages/plugin-event-bind-dialog/package.json index ba73780b4..960af4bac 100644 --- a/packages/plugin-event-bind-dialog/package.json +++ b/packages/plugin-event-bind-dialog/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-event-bind-dialog", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "alibaba lowcode editor event bind dialog plugin", "files": [ "es", @@ -19,8 +19,8 @@ ], "author": "zude.hzd", "dependencies": { - "@ali/lowcode-editor-core": "^0.13.1-10", - "@ali/lowcode-types": "^0.13.1-10", + "@ali/lowcode-editor-core": "^0.13.1-11", + "@ali/lowcode-types": "^0.13.1-11", "@alifd/next": "^1.19.16", "react": "^16.8.1", "react-dom": "^16.8.1" diff --git a/packages/plugin-outline-pane/CHANGELOG.md b/packages/plugin-outline-pane/CHANGELOG.md index 5af5fdc61..b42ed4331 100644 --- a/packages/plugin-outline-pane/CHANGELOG.md +++ b/packages/plugin-outline-pane/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/plugin-outline-pane/package.json b/packages/plugin-outline-pane/package.json index 2d73f0e57..40936d333 100644 --- a/packages/plugin-outline-pane/package.json +++ b/packages/plugin-outline-pane/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-outline-pane", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "Outline pane for Ali lowCode engine", "files": [ "es", @@ -14,10 +14,10 @@ "test:snapshot": "ava --update-snapshots" }, "dependencies": { - "@ali/lowcode-designer": "^0.13.1-10", - "@ali/lowcode-editor-core": "^0.13.1-10", - "@ali/lowcode-types": "^0.13.1-10", - "@ali/lowcode-utils": "^0.13.1-10", + "@ali/lowcode-designer": "^0.13.1-11", + "@ali/lowcode-editor-core": "^0.13.1-11", + "@ali/lowcode-types": "^0.13.1-11", + "@ali/lowcode-utils": "^0.13.1-11", "@alifd/next": "^1.19.16", "classnames": "^2.2.6", "react": "^16", diff --git a/packages/plugin-sample-logo/CHANGELOG.md b/packages/plugin-sample-logo/CHANGELOG.md index 5089ba982..3b1e543db 100644 --- a/packages/plugin-sample-logo/CHANGELOG.md +++ b/packages/plugin-sample-logo/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-sample-logo + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/plugin-sample-logo/package.json b/packages/plugin-sample-logo/package.json index e486d7686..df8d097e8 100644 --- a/packages/plugin-sample-logo/package.json +++ b/packages/plugin-sample-logo/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-sample-logo", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "alibaba lowcode editor logo plugin", "files": [ "es/", @@ -20,7 +20,7 @@ ], "author": "xiayang.xy", "dependencies": { - "@ali/lowcode-editor-core": "^0.13.1-10", + "@ali/lowcode-editor-core": "^0.13.1-11", "react": "^16.8.1" }, "devDependencies": { diff --git a/packages/plugin-sample-preview/CHANGELOG.md b/packages/plugin-sample-preview/CHANGELOG.md index fd0a5d069..cd06d209e 100644 --- a/packages/plugin-sample-preview/CHANGELOG.md +++ b/packages/plugin-sample-preview/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-sample-preview + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/plugin-sample-preview/package.json b/packages/plugin-sample-preview/package.json index ee025dbfe..d8ea6a432 100644 --- a/packages/plugin-sample-preview/package.json +++ b/packages/plugin-sample-preview/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-sample-preview", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "alibaba lowcode editor sample preview plugin", "files": [ "es", @@ -19,11 +19,11 @@ "editor" ], "dependencies": { - "@ali/lowcode-designer": "^0.13.1-10", - "@ali/lowcode-editor-core": "^0.13.1-10", - "@ali/lowcode-react-renderer": "^0.13.1-10", - "@ali/lowcode-types": "^0.13.1-10", - "@ali/lowcode-utils": "^0.13.1-10", + "@ali/lowcode-designer": "^0.13.1-11", + "@ali/lowcode-editor-core": "^0.13.1-11", + "@ali/lowcode-react-renderer": "^0.13.1-11", + "@ali/lowcode-types": "^0.13.1-11", + "@ali/lowcode-utils": "^0.13.1-11", "@alifd/next": "^1.x", "react": "^16.8.1" }, diff --git a/packages/plugin-source-editor/CHANGELOG.md b/packages/plugin-source-editor/CHANGELOG.md index 0db1b81d7..62d373636 100644 --- a/packages/plugin-source-editor/CHANGELOG.md +++ b/packages/plugin-source-editor/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-source-editor + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/plugin-source-editor/package.json b/packages/plugin-source-editor/package.json index 5be712bd0..b0f62d5c9 100644 --- a/packages/plugin-source-editor/package.json +++ b/packages/plugin-source-editor/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-source-editor", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "alibaba lowcode editor source-editor plugin", "files": [ "es", @@ -19,7 +19,7 @@ ], "author": "zude.hzd", "dependencies": { - "@ali/lowcode-editor-core": "^0.13.1-10", + "@ali/lowcode-editor-core": "^0.13.1-11", "@alifd/next": "^1.19.16", "js-beautify": "^1.10.1", "prettier": "^1.18.2", diff --git a/packages/plugin-undo-redo/CHANGELOG.md b/packages/plugin-undo-redo/CHANGELOG.md index 8a49bd3fa..14c8ca8a5 100644 --- a/packages/plugin-undo-redo/CHANGELOG.md +++ b/packages/plugin-undo-redo/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-undo-redo + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/plugin-undo-redo/package.json b/packages/plugin-undo-redo/package.json index d48bb0e4f..ed6040fb5 100644 --- a/packages/plugin-undo-redo/package.json +++ b/packages/plugin-undo-redo/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-undo-redo", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "alibaba lowcode editor undo redo plugin", "files": [ "es", @@ -19,11 +19,11 @@ ], "author": "xiayang.xy", "dependencies": { - "@ali/lowcode-designer": "^0.13.1-10", - "@ali/lowcode-editor-core": "^0.13.1-10", - "@ali/lowcode-editor-skeleton": "^0.13.1-10", - "@ali/lowcode-types": "^0.13.1-10", - "@ali/lowcode-utils": "^0.13.1-10", + "@ali/lowcode-designer": "^0.13.1-11", + "@ali/lowcode-editor-core": "^0.13.1-11", + "@ali/lowcode-editor-skeleton": "^0.13.1-11", + "@ali/lowcode-types": "^0.13.1-11", + "@ali/lowcode-utils": "^0.13.1-11", "react": "^16.8.1", "react-dom": "^16.8.1" }, diff --git a/packages/plugin-variable-bind-dialog/CHANGELOG.md b/packages/plugin-variable-bind-dialog/CHANGELOG.md index ced698638..84eb4bd2a 100644 --- a/packages/plugin-variable-bind-dialog/CHANGELOG.md +++ b/packages/plugin-variable-bind-dialog/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-variable-bind-dialog + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/plugin-variable-bind-dialog/package.json b/packages/plugin-variable-bind-dialog/package.json index 8a65321a2..5c95bb310 100644 --- a/packages/plugin-variable-bind-dialog/package.json +++ b/packages/plugin-variable-bind-dialog/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-variable-bind-dialog", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "alibaba lowcode editor variable bind dialog plugin", "files": [ "es", @@ -19,7 +19,7 @@ ], "author": "zude.hzd", "dependencies": { - "@ali/lowcode-editor-core": "^0.13.1-10", + "@ali/lowcode-editor-core": "^0.13.1-11", "@alifd/next": "^1.19.16", "react": "^16.8.1", "react-dom": "^16.8.1" diff --git a/packages/plugin-zh-en/CHANGELOG.md b/packages/plugin-zh-en/CHANGELOG.md index ba2c5e30f..e90c2636b 100644 --- a/packages/plugin-zh-en/CHANGELOG.md +++ b/packages/plugin-zh-en/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-plugin-zh-en + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/plugin-zh-en/package.json b/packages/plugin-zh-en/package.json index 150ea9ae1..663af1f27 100644 --- a/packages/plugin-zh-en/package.json +++ b/packages/plugin-zh-en/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-plugin-zh-en", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "alibaba lowcode editor zhong english plugin", "files": [ "es", @@ -14,9 +14,9 @@ "test:snapshot": "ava --update-snapshots" }, "dependencies": { - "@ali/lowcode-editor-core": "^0.13.1-10", - "@ali/lowcode-types": "^0.13.1-10", - "@ali/lowcode-utils": "^0.13.1-10", + "@ali/lowcode-editor-core": "^0.13.1-11", + "@ali/lowcode-types": "^0.13.1-11", + "@ali/lowcode-utils": "^0.13.1-11", "react": "^16.8.1", "react-dom": "^16.8.1" }, diff --git a/packages/rax-provider/CHANGELOG.md b/packages/rax-provider/CHANGELOG.md index 004fcfa80..0f025c5bd 100644 --- a/packages/rax-provider/CHANGELOG.md +++ b/packages/rax-provider/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-rax-provider + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/rax-provider/package.json b/packages/rax-provider/package.json index 2d74b8f8b..67eb8d4e5 100644 --- a/packages/rax-provider/package.json +++ b/packages/rax-provider/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-rax-provider", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "Rax Provider for Runtime", "files": [ "es", @@ -18,7 +18,7 @@ }, "license": "MIT", "dependencies": { - "@ali/lowcode-runtime": "^0.13.1-10", + "@ali/lowcode-runtime": "^0.13.1-11", "driver-universal": "^3.1.3", "history": "^4.10.1", "rax-use-router": "^3.0.0" diff --git a/packages/rax-render/CHANGELOG.md b/packages/rax-render/CHANGELOG.md index d967228c2..8cdbf7f27 100644 --- a/packages/rax-render/CHANGELOG.md +++ b/packages/rax-render/CHANGELOG.md @@ -3,7 +3,15 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-rax-renderer + + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) @@ -11,7 +19,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline **Note:** Version bump only for package @ali/lowcode-rax-renderer - + ## [0.13.1-9](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-7...v0.13.1-9) (2020-10-26) diff --git a/packages/rax-render/package.json b/packages/rax-render/package.json index 7367ca4d3..9fecfd70b 100644 --- a/packages/rax-render/package.json +++ b/packages/rax-render/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-rax-renderer", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "Rax renderer for Ali lowCode engine", "main": "lib/index.js", "module": "lib/index.js", @@ -36,7 +36,7 @@ "@ali/b3-one": "^0.0.17", "@ali/bzb-request": "2.6.1", "@ali/lib-mtop": "^2.5.1", - "@ali/lowcode-utils": "^0.13.1-10", + "@ali/lowcode-utils": "^0.13.1-11", "@ali/ui-table": "^1.0.1-beta.6", "classnames": "^2.2.6", "debug": "^4.1.1", diff --git a/packages/rax-simulator-renderer/CHANGELOG.md b/packages/rax-simulator-renderer/CHANGELOG.md index f31fcb062..3424a1d01 100644 --- a/packages/rax-simulator-renderer/CHANGELOG.md +++ b/packages/rax-simulator-renderer/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-rax-simulator-renderer + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/rax-simulator-renderer/package.json b/packages/rax-simulator-renderer/package.json index 84812c433..20f80bbcf 100644 --- a/packages/rax-simulator-renderer/package.json +++ b/packages/rax-simulator-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-rax-simulator-renderer", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "rax simulator renderer for alibaba lowcode designer", "main": "lib/index.js", "module": "es/index.js", @@ -13,10 +13,10 @@ "test:snapshot": "ava --update-snapshots" }, "dependencies": { - "@ali/lowcode-designer": "^0.13.1-10", - "@ali/lowcode-rax-renderer": "^0.13.1-10", - "@ali/lowcode-types": "^0.13.1-10", - "@ali/lowcode-utils": "^0.13.1-10", + "@ali/lowcode-designer": "^0.13.1-11", + "@ali/lowcode-rax-renderer": "^0.13.1-11", + "@ali/lowcode-types": "^0.13.1-11", + "@ali/lowcode-utils": "^0.13.1-11", "@ali/recore-rax": "^1.2.4", "@ali/vu-css-style": "^1.0.2", "@recore/obx": "^1.0.8", @@ -57,5 +57,5 @@ "publishConfig": { "registry": "https://registry.npm.alibaba-inc.com" }, - "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-rax-simulator-renderer@0.13.1-9/build/index.html" + "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-rax-simulator-renderer@0.13.1-10/build/index.html" } diff --git a/packages/react-provider/CHANGELOG.md b/packages/react-provider/CHANGELOG.md index f05e1032c..1c96be202 100644 --- a/packages/react-provider/CHANGELOG.md +++ b/packages/react-provider/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-react-provider + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/react-provider/package.json b/packages/react-provider/package.json index 1499b31d6..9e314c371 100644 --- a/packages/react-provider/package.json +++ b/packages/react-provider/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-react-provider", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "React Provider for Runtime", "files": [ "es", @@ -25,7 +25,7 @@ }, "license": "MIT", "dependencies": { - "@ali/lowcode-runtime": "^0.13.1-10", + "@ali/lowcode-runtime": "^0.13.1-11", "@ali/lowcode-utils": "^1.0.7", "@recore/router": "^1.0.11", "react": "^16", diff --git a/packages/react-renderer/CHANGELOG.md b/packages/react-renderer/CHANGELOG.md index a6ecdc018..53a5ef6b0 100644 --- a/packages/react-renderer/CHANGELOG.md +++ b/packages/react-renderer/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + +### Bug Fixes + +* 解决 slot 在关闭时没有正常回收节点 ([642a404](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/642a404)) + + + + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/react-renderer/package.json b/packages/react-renderer/package.json index 946612931..1714881f3 100644 --- a/packages/react-renderer/package.json +++ b/packages/react-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-react-renderer", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "react renderer for ali lowcode engine", "main": "lib/index.js", "module": "es/index.js", diff --git a/packages/react-simulator-renderer/CHANGELOG.md b/packages/react-simulator-renderer/CHANGELOG.md index 8598bf198..106612ec7 100644 --- a/packages/react-simulator-renderer/CHANGELOG.md +++ b/packages/react-simulator-renderer/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + +### Bug Fixes + +* 解决 slot 在关闭时没有正常回收节点 ([642a404](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/642a404)) + + + + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/react-simulator-renderer/package.json b/packages/react-simulator-renderer/package.json index 1b1d78350..a64bbaf7b 100644 --- a/packages/react-simulator-renderer/package.json +++ b/packages/react-simulator-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-react-simulator-renderer", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "react simulator renderer for alibaba lowcode designer", "main": "lib/index.js", "module": "es/index.js", @@ -16,10 +16,10 @@ "build": "build-scripts build --skip-demo" }, "dependencies": { - "@ali/lowcode-designer": "^0.13.1-10", - "@ali/lowcode-react-renderer": "^0.13.1-10", - "@ali/lowcode-types": "^0.13.1-10", - "@ali/lowcode-utils": "^0.13.1-10", + "@ali/lowcode-designer": "^0.13.1-11", + "@ali/lowcode-react-renderer": "^0.13.1-11", + "@ali/lowcode-types": "^0.13.1-11", + "@ali/lowcode-utils": "^0.13.1-11", "@ali/vu-css-style": "^1.0.2", "@recore/obx": "^1.0.8", "@recore/obx-react": "^1.0.7", diff --git a/packages/runtime/CHANGELOG.md b/packages/runtime/CHANGELOG.md index 9b587d125..84c90e1fc 100644 --- a/packages/runtime/CHANGELOG.md +++ b/packages/runtime/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-runtime + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 02bd8ff64..4b0fa825f 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-runtime", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "Runtime for Ali lowCode engine", "files": [ "es", diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index 7143cff9b..9b2fb33d1 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-types + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/types/package.json b/packages/types/package.json index 8a1e7ffa4..d0d8216f0 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-types", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "Types for Ali lowCode engine", "files": [ "es", diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index dc1b414a3..6fa46cca8 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.13.1-11](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-10...v0.13.1-11) (2020-11-02) + + + + +**Note:** Version bump only for package @ali/lowcode-utils + ## [0.13.1-10](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v0.13.1-9...v0.13.1-10) (2020-10-26) diff --git a/packages/utils/package.json b/packages/utils/package.json index 79360b681..e9cb1a30f 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-utils", - "version": "0.13.1-10", + "version": "0.13.1-11", "description": "Utils for Ali lowCode engine", "files": [ "es", @@ -14,7 +14,7 @@ "test:snapshot": "ava --update-snapshots" }, "dependencies": { - "@ali/lowcode-types": "^0.13.1-10", + "@ali/lowcode-types": "^0.13.1-11", "@alifd/next": "^1.19.16", "react": "^16" }, From 11e8e02f703b94dc5a58760f9a886871f027ea14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8A=9B=E7=9A=93?= Date: Thu, 5 Nov 2020 17:09:05 +0800 Subject: [PATCH 3/7] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3=20device=20?= =?UTF-8?q?=E5=8F=98=E5=8C=96=E5=90=8E=E4=B8=8D=E5=88=B7=E6=96=B0=E8=A7=86?= =?UTF-8?q?=E5=9B=BE=E7=9A=84=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/demo/package.json | 5 +- packages/designer/.eslintrc.js | 1 + packages/designer/build.test.json | 1 + packages/designer/package.json | 2 +- packages/designer/plugin.js | 12 + packages/designer/src/document/history.ts | 10 +- packages/editor-preset-vision/build.test.json | 19 + packages/editor-preset-vision/jest.config.js | 23 + packages/editor-preset-vision/package.json | 5 +- packages/editor-preset-vision/src/editor.ts | 2 +- packages/editor-preset-vision/src/index.ts | 4 +- .../tests/fixtures/schema/form.ts | 1111 +++++++++++++++++ .../tests/fixtures/window.ts | 5 + .../tests/vision-api/pages.test.ts | 46 + packages/react-renderer/package.json | 2 +- packages/react-renderer/src/renderer/base.tsx | 2 +- .../react-simulator-renderer/src/renderer.ts | 3 +- 17 files changed, 1238 insertions(+), 15 deletions(-) create mode 100644 packages/designer/plugin.js create mode 100644 packages/editor-preset-vision/build.test.json create mode 100644 packages/editor-preset-vision/jest.config.js create mode 100644 packages/editor-preset-vision/tests/fixtures/schema/form.ts create mode 100644 packages/editor-preset-vision/tests/fixtures/window.ts create mode 100644 packages/editor-preset-vision/tests/vision-api/pages.test.ts diff --git a/packages/demo/package.json b/packages/demo/package.json index 7dbf7c074..bbb30b4b9 100644 --- a/packages/demo/package.json +++ b/packages/demo/package.json @@ -4,8 +4,9 @@ "private": true, "description": "低代码引擎 DEMO", "scripts": { - "cloud-build": "build-scripts build --config cloud-build.json", - "start": "build-scripts start" + "start": "build-scripts start", + "build": "build-scripts build", + "cloud-build": "build-scripts build --config cloud-build.json" }, "config": {}, "dependencies": { diff --git a/packages/designer/.eslintrc.js b/packages/designer/.eslintrc.js index a44376585..35f9add82 100644 --- a/packages/designer/.eslintrc.js +++ b/packages/designer/.eslintrc.js @@ -14,5 +14,6 @@ module.exports = { 'no-useless-constructor': 1, 'no-empty-function': 1, '@typescript-eslint/member-ordering': 0, + 'lines-between-class-members': 0 } } \ No newline at end of file diff --git a/packages/designer/build.test.json b/packages/designer/build.test.json index 93dfcd983..228c07e7f 100644 --- a/packages/designer/build.test.json +++ b/packages/designer/build.test.json @@ -1,5 +1,6 @@ { "plugins": [ + "build-plugin-component", "@ali/lowcode-test-mate/plugin/index.ts" ] } diff --git a/packages/designer/package.json b/packages/designer/package.json index 073ee151e..5d07e9f8e 100644 --- a/packages/designer/package.json +++ b/packages/designer/package.json @@ -10,7 +10,7 @@ ], "scripts": { "build": "build-scripts build --skip-demo", - "test": "build-scripts --config build.test.json test" + "test": "build-scripts test --config build.test.json" }, "license": "MIT", "dependencies": { diff --git a/packages/designer/plugin.js b/packages/designer/plugin.js new file mode 100644 index 000000000..97c09d4b9 --- /dev/null +++ b/packages/designer/plugin.js @@ -0,0 +1,12 @@ +module.exports = ({ onGetJestConfig, onGetWebpackConfig }) => { + console.log('111111111111xxxxxxxxxxxx') + onGetWebpackConfig((config) => { + console.log('111111111111') + config.browserslist('last 2 versions, Firefox ESR, > 1%, ie >= 9, iOS >= 8, Android >= 4'); + }); + + onGetJestConfig((jestConfig) => { + console.log('11111111111122222222') + return jestConfig; + }); +}; diff --git a/packages/designer/src/document/history.ts b/packages/designer/src/document/history.ts index 61f1539d0..e4aa09e1a 100644 --- a/packages/designer/src/document/history.ts +++ b/packages/designer/src/document/history.ts @@ -9,7 +9,7 @@ export interface Serialization { unserialize(data: T): NodeSchema; } -let currentSerializion: Serialization = { +let currentSerialization: Serialization = { serialize(data: NodeSchema): string { return JSON.stringify(data); }, @@ -18,8 +18,8 @@ let currentSerializion: Serialization = { }, }; -export function setSerialization(serializion: Serialization) { - currentSerializion = serializion; +export function setSerialization(serialization: Serialization) { + currentSerialization = serialization; } export class History { @@ -46,7 +46,7 @@ export class History { return; } untracked(() => { - const log = currentSerializion.serialize(data); + const log = currentSerialization.serialize(data); if (this.session.cursor === 0 && this.session.isActive()) { // first log this.session.log(log); @@ -98,7 +98,7 @@ export class History { this.obx.sleep(); try { - this.redoer(currentSerializion.unserialize(hotData)); + this.redoer(currentSerialization.unserialize(hotData)); this.emitter.emit('cursor', hotData); } catch (e) { // diff --git a/packages/editor-preset-vision/build.test.json b/packages/editor-preset-vision/build.test.json new file mode 100644 index 000000000..4bde6acd1 --- /dev/null +++ b/packages/editor-preset-vision/build.test.json @@ -0,0 +1,19 @@ +{ + "plugins": [ + [ + "build-plugin-component", + { + "filename": "editor-preset-vision", + "library": "LowcodeEditor", + "libraryTarget": "umd", + "externals": { + "react": "var window.React", + "react-dom": "var window.ReactDOM", + "prop-types": "var window.PropTypes", + "rax": "var window.Rax" + } + } + ], + "@ali/lowcode-test-mate/plugin/index.ts" + ] +} diff --git a/packages/editor-preset-vision/jest.config.js b/packages/editor-preset-vision/jest.config.js new file mode 100644 index 000000000..810547389 --- /dev/null +++ b/packages/editor-preset-vision/jest.config.js @@ -0,0 +1,23 @@ +const esModules = [ + '@recore/obx-react', + '@ali/lowcode-editor-core', +].join('|'); + +module.exports = { + // transform: { + // '^.+\\.[jt]sx?$': 'babel-jest', + // // '^.+\\.(ts|tsx)$': 'ts-jest', + // // '^.+\\.(js|jsx)$': 'babel-jest', + // }, + // testMatch: ['(/tests?/.*(test))\\.[jt]s$'], + transformIgnorePatterns: [ + `/node_modules/(?!${esModules})/`, + ], + moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], + collectCoverage: false, + collectCoverageFrom: [ + 'src/**/*.{ts,tsx}', + '!**/node_modules/**', + '!**/vendor/**', + ], +}; diff --git a/packages/editor-preset-vision/package.json b/packages/editor-preset-vision/package.json index 7a53ef1ab..23f770f4e 100644 --- a/packages/editor-preset-vision/package.json +++ b/packages/editor-preset-vision/package.json @@ -11,7 +11,8 @@ ], "scripts": { "build": "build-scripts build --skip-demo", - "cloud-build": "build-scripts build --skip-demo" + "cloud-build": "build-scripts build --skip-demo", + "test": "build-scripts test --config build.test.json" }, "license": "MIT", "dependencies": { @@ -37,6 +38,7 @@ "react-dom": "^16.8.1" }, "devDependencies": { + "@ali/lowcode-test-mate": "^1.0.0", "@alib/build-scripts": "^0.1.18", "@types/domready": "^1.0.0", "@types/events": "^3.0.0", @@ -45,6 +47,7 @@ "build-plugin-fusion": "^0.1.0", "build-plugin-moment-locales": "^0.1.0", "build-plugin-react-app": "^1.1.2", + "prop-types": "^15.7.2", "tsconfig-paths-webpack-plugin": "^3.2.0" }, "publishConfig": { diff --git a/packages/editor-preset-vision/src/editor.ts b/packages/editor-preset-vision/src/editor.ts index 6d1cdd67d..33c650dcf 100644 --- a/packages/editor-preset-vision/src/editor.ts +++ b/packages/editor-preset-vision/src/editor.ts @@ -316,7 +316,7 @@ designer.addPropsReducer(deepValueParser, TransformStage.Render); // 清除空的 props value function removeEmptyProps(props: any, node: Node) { - if (node.isRoot() && props.dataSource) { + if (node.isRoot() && props.dataSource && Array.isArray(props.dataSource.online)) { const online = cloneDeep(props.dataSource.online); online.forEach((item: any) => { const newParam: any = {}; diff --git a/packages/editor-preset-vision/src/index.ts b/packages/editor-preset-vision/src/index.ts index 42b17fa89..06a04bb58 100644 --- a/packages/editor-preset-vision/src/index.ts +++ b/packages/editor-preset-vision/src/index.ts @@ -166,6 +166,6 @@ const version = '6.0.0(LowcodeEngine 0.9.3)'; console.log( `%c VisionEngine %c v${version} `, - 'padding: 2px 1px; border-radius: 3px 0 0 3px; color: #fff; background: #606060;font-weight:bold;', - 'padding: 2px 1px; border-radius: 0 3px 3px 0; color: #fff; background: #42c02e;font-weight:bold;', + 'padding: 2px 1px; border-radius: 3px 0 0 3px; color: #fff; background: #606060; font-weight: bold;', + 'padding: 2px 1px; border-radius: 0 3px 3px 0; color: #fff; background: #42c02e; font-weight: bold;', ); diff --git a/packages/editor-preset-vision/tests/fixtures/schema/form.ts b/packages/editor-preset-vision/tests/fixtures/schema/form.ts new file mode 100644 index 000000000..f9d9477fb --- /dev/null +++ b/packages/editor-preset-vision/tests/fixtures/schema/form.ts @@ -0,0 +1,1111 @@ +export default { + schemaType: 'superform', + schemaVersion: '5.0', + pages: [ + { + componentsMap: [ + { + componentName: 'Text', + package: '@ali/vc-text', + }, + { + componentName: 'Slot', + package: '@ali/vc-slot', + }, + { + componentName: 'PageHeader', + package: '@ali/vc-deep', + }, + { + componentName: 'RootHeader', + package: '@ali/vc-page', + }, + { + componentName: 'TextField', + package: '@ali/vc-deep', + }, + { + componentName: 'Column', + package: '@ali/vc-deep', + }, + { + componentName: 'SelectField', + package: '@ali/vc-deep', + }, + { + componentName: 'ColumnsLayout', + package: '@ali/vc-deep', + }, + { + componentName: 'CardContent', + package: '@ali/vc-deep', + }, + { + componentName: 'Card', + package: '@ali/vc-deep', + }, + { + componentName: 'Button', + package: '@ali/vc-deep', + }, + { + componentName: 'Div', + package: '@ali/vc-div', + }, + { + componentName: 'Form', + package: '@ali/vc-deep', + }, + { + componentName: 'RootContent', + package: '@ali/vc-page', + }, + { + componentName: 'RootFooter', + package: '@ali/vc-page', + }, + { + componentName: 'Page', + package: '@ali/vc-page', + }, + ], + componentsTree: [ + { + componentName: 'Page', + id: 'node_k1ow3cb9', + props: { + extensions: { + 启用页头: true, + }, + pageStyle: { + backgroundColor: '#f2f3f5', + }, + containerStyle: {}, + className: 'page_kh05zf9c', + 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_kh05zf9d {\n margin-bottom: 12px;\n}.card_kh05zf9e {\n margin-bottom: 12px;\n}.button_kh05zf9f {\n margin-right: 16px;\n width: 80px\n}.button_kh05zf9g {\n width: 80px;\n}.div_kh05zf9h {\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: 'JSBlock', + value: { + componentName: 'Slot', + children: [ + { + componentName: 'Text', + id: 'node_k1ow3cbf', + props: { + showTitle: false, + behavior: 'NORMAL', + content: { + type: 'variable', + value: { + use: 'zh_CN', + en_US: 'Title', + zh_CN: '个人信息', + type: 'i18n', + }, + variable: 'state.title', + }, + __style__: {}, + fieldId: 'text_k1ow3h1j', + maxLine: 0, + }, + condition: true, + }, + ], + props: { + slotTitle: '标题区域', + slotName: 'title', + }, + }, + }, + content: '', + __slot__logo: false, + __slot__crumb: false, + crumb: '', + tab: '', + logo: '', + action: '', + __slot__tab: false, + __style__: {}, + __slot__content: false, + fieldId: 'pageHeader_k1ow3h1i', + subTitle: { + type: 'JSBlock', + value: { + componentName: 'Slot', + children: [ + { + componentName: 'Text', + id: 'node_ockh05zr7j3', + props: { + content: { + type: 'i18n', + en_US: 'Title', + zh_CN: '副标题', + }, + showTitle: false, + behavior: 'NORMAL', + maxLine: 0, + __style__: {}, + fieldId: 'text_kh06n1mc', + }, + }, + ], + props: { + slotTitle: '副标题', + slotName: 'subTitle', + }, + }, + }, + }, + condition: true, + }, + ], + }, + { + componentName: 'RootContent', + id: 'node_k1ow3cbb', + props: { + contentBgColor: 'transparent', + contentPadding: '0', + contentMargin: '20', + }, + condition: true, + children: [ + { + componentName: 'Form', + id: 'node_k1ow3cbq', + props: { + size: 'medium', + labelAlign: 'top', + autoValidate: true, + scrollToFirstError: true, + autoUnmount: true, + behavior: 'NORMAL', + dataSource: { + type: 'variable', + variable: 'state.formData', + }, + __style__: {}, + fieldId: 'form', + fieldOptions: {}, + }, + 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_kh05zf9d', + 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: '4:8', + 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: '', + }, + maxLength: 200, + }, + 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: '', + }, + maxLength: 200, + }, + 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: '', + }, + maxLength: 200, + }, + 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: '', + }, + maxLength: 200, + }, + 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: '', + }, + useDetailValue: false, + 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_kh05zf9e', + 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: '', + }, + maxLength: 200, + }, + 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: '', + }, + maxLength: 200, + }, + 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: '', + }, + maxLength: 200, + }, + condition: true, + }, + ], + }, + ], + }, + ], + }, + ], + }, + { + componentName: 'Div', + id: 'node_k1ow3cbo', + props: { + className: 'div_kh05zf9h', + 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_kh05zf9f', + 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_kh05zf9g', + 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', + }, + condition: true, + }, + ], + }, + ], + }, + ], + }, + { + componentName: 'RootFooter', + id: 'node_k1ow3cbc', + props: {}, + condition: true, + }, + ], + }, + ], + id: 'FORM-MV766X71QI5KCL6T4CXKF9DJUME73BD0FQAGKE', + }, + ], + actions: { + module: { + compiled: + "'use strict';\n\nexports.__esModule = true;\nexports.helloPage = helloPage;\nexports.submit = submit;\n/**\n * 私有函数,只能JS面板内部调用\n * 动作面板帮助文档:\n * @see https://lark.alipay.com/legao/help/design-tool-logic\n */\nfunction printLog(obj) {\n console.info(obj);\n}\n\n/**\n * 页面内的函数,可以被页面内任何位置调用\n */\nfunction helloPage() {\n console.log('hello page');\n}\n\n/**\n* button onClick\n*/\nfunction submit() {\n var _this = this;\n\n this.$('form').submit(function (data, error) {\n if (data) {\n console.log(data); // 这是表单提交的数据\n _this.utils.toast({\n type: 'success',\n title: '提交成功'\n });\n }\n });\n}", + source: + "/**\n * 私有函数,只能JS面板内部调用\n * 动作面板帮助文档:\n * @see https://lark.alipay.com/legao/help/design-tool-logic\n */\nfunction printLog(obj) {\n console.info(obj);\n}\n\n/**\n * 页面内的函数,可以被页面内任何位置调用\n */\nexport function helloPage() {\n console.log('hello page');\n}\n\n\n/**\n* button onClick\n*/\nexport function submit(){\n this.$('form').submit((data, error) => {\n if (data) {\n console.log(data); // 这是表单提交的数据\n this.utils.toast({\n type: 'success',\n title: '提交成功'\n });\n }\n });\n}", + }, + type: 'FUNCTION', + list: [ + { + id: 'helloPage', + title: 'helloPage', + }, + { + id: 'submit', + title: 'submit', + }, + ], + }, +}; diff --git a/packages/editor-preset-vision/tests/fixtures/window.ts b/packages/editor-preset-vision/tests/fixtures/window.ts new file mode 100644 index 000000000..2e3cd691a --- /dev/null +++ b/packages/editor-preset-vision/tests/fixtures/window.ts @@ -0,0 +1,5 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +React.PropTypes = PropTypes; +window.React = React; \ No newline at end of file diff --git a/packages/editor-preset-vision/tests/vision-api/pages.test.ts b/packages/editor-preset-vision/tests/vision-api/pages.test.ts new file mode 100644 index 000000000..d020b6df1 --- /dev/null +++ b/packages/editor-preset-vision/tests/vision-api/pages.test.ts @@ -0,0 +1,46 @@ +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 VisualEngine from '../../src'; +// import { getIdsFromSchema, getNodeFromSchemaById } from '../utils'; + +// const mockCreateSettingEntry = jest.fn(); +// jest.mock('../../src/designer/designer', () => { +// return { +// Designer: jest.fn().mockImplementation(() => { +// return { +// getComponentMeta() { +// return { +// getMetadata() { +// return { experimental: null }; +// }, +// }; +// }, +// transformProps(props) { return props; }, +// createSettingEntry: mockCreateSettingEntry, +// postEvent() {}, +// }; +// }), +// }; +// }); + + + +// let designer = null; +// beforeAll(() => { +// designer = new Designer({}); +// }); + +describe('schema 生成节点模型测试', () => { + describe('block ❌ | component ❌ | slot ❌', () => { + it('基本的节点模型初始化,模型导出,初始化传入 schema', () => { + console.log(VisualEngine); + + console.log(VisualEngine.Pages.addPage(formSchema)); + }); + }); +}); \ No newline at end of file diff --git a/packages/react-renderer/package.json b/packages/react-renderer/package.json index 1714881f3..3da0c9537 100644 --- a/packages/react-renderer/package.json +++ b/packages/react-renderer/package.json @@ -54,5 +54,5 @@ "publishConfig": { "registry": "http://registry.npm.alibaba-inc.com" }, - "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-react-renderer@0.13.1-10/build/index.html" + "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-react-renderer@0.13.1-11/build/index.html" } diff --git a/packages/react-renderer/src/renderer/base.tsx b/packages/react-renderer/src/renderer/base.tsx index 8efa3ef79..1eab49f5c 100644 --- a/packages/react-renderer/src/renderer/base.tsx +++ b/packages/react-renderer/src/renderer/base.tsx @@ -339,7 +339,7 @@ export default class BaseRender extends PureComponent { if (refProps && typeof refProps === 'string') { this[refProps] = ref; } - engine && engine.props.onCompGetRef(schema, ref); + ref && engine && engine.props.onCompGetRef(schema, ref); }; } // scope需要传入到组件上 diff --git a/packages/react-simulator-renderer/src/renderer.ts b/packages/react-simulator-renderer/src/renderer.ts index b07935f0b..889a1a958 100644 --- a/packages/react-simulator-renderer/src/renderer.ts +++ b/packages/react-simulator-renderer/src/renderer.ts @@ -187,7 +187,7 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer { this._componentsMap = host.designer.componentsMap; // 需要注意的是,autorun 依赖收集的是同步执行的代码,所以 await / promise / callback 里的变量不会被收集依赖 // 此例中,host.designer.componentsMap 是需要被收集依赖的,否则无法响应式 - await host.waitForCurrentDocument(); + // await host.waitForCurrentDocument(); this.buildComponents(); } @@ -540,6 +540,7 @@ function getComponentController(schema: NodeSchema, componentsMap: any) { constructor(props: any) { super(props); + console.log('>>> current'); const node = host.currentDocument?.createNode(schema); this.renderSchema = node?.export(TransformStage.Render) || {}; } From 5e6e91b265f9ce778d0dc4de7d5156b1b3a4aff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8A=9B=E7=9A=93?= Date: Sun, 8 Nov 2020 00:42:02 +0800 Subject: [PATCH 4/7] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20project.unload?= =?UTF-8?q?=20=E6=97=A0=E6=B3=95=E6=AD=A3=E5=B8=B8=E5=88=A0=E9=99=A4=20doc?= =?UTF-8?q?ument=20=E7=9A=84=20bug=20chore(test):=20=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=AE=8C=E6=88=90=20pages=20/=20api=20=E7=AD=89=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore(test): 完成部分 prototype 单测 --- packages/designer/src/project/project.ts | 4 +- packages/editor-core/src/di/ioc-context.ts | 4 + packages/editor-preset-vision/.eslintrc.js | 3 +- packages/editor-preset-vision/jest.config.js | 2 +- packages/editor-preset-vision/src/bus.ts | 6 +- packages/editor-preset-vision/src/editor.ts | 307 +-- packages/editor-preset-vision/src/index.ts | 2 +- packages/editor-preset-vision/src/pages.ts | 1 - .../downgrade-schema-reducer.ts | 51 + .../src/props-reducers/filter-reducer.ts | 25 + .../src/props-reducers/index.ts | 7 + .../src/props-reducers/init-node-reducer.ts | 68 + .../props-reducers/live-lifecycle-reducer.ts | 27 + .../remove-empty-prop-reducer.ts | 23 + .../src/props-reducers/style-reducer.ts | 51 + .../src/props-reducers/upgrade-reducer.ts | 38 + .../editor-preset-vision/src/utils/index.ts | 24 +- .../tests/bundle/prototype.test.ts | 71 + .../tests/fixtures/prototype/div-meta.ts | 259 +++ .../tests/fixtures/prototype/div-vision.ts | 175 ++ .../tests/fixtures/schema/form.ts | 1822 ++++++++--------- .../editor-preset-vision/tests/utils/index.ts | 1 + .../tests/vision-api/api-export.test.ts | 89 + .../tests/vision-api/pages.test.ts | 189 +- .../tests/vision-api/project.test.ts | 38 + 25 files changed, 1961 insertions(+), 1326 deletions(-) create mode 100644 packages/editor-preset-vision/src/props-reducers/downgrade-schema-reducer.ts create mode 100644 packages/editor-preset-vision/src/props-reducers/filter-reducer.ts create mode 100644 packages/editor-preset-vision/src/props-reducers/index.ts create mode 100644 packages/editor-preset-vision/src/props-reducers/init-node-reducer.ts create mode 100644 packages/editor-preset-vision/src/props-reducers/live-lifecycle-reducer.ts create mode 100644 packages/editor-preset-vision/src/props-reducers/remove-empty-prop-reducer.ts create mode 100644 packages/editor-preset-vision/src/props-reducers/style-reducer.ts create mode 100644 packages/editor-preset-vision/src/props-reducers/upgrade-reducer.ts create mode 100644 packages/editor-preset-vision/tests/bundle/prototype.test.ts create mode 100644 packages/editor-preset-vision/tests/fixtures/prototype/div-meta.ts create mode 100644 packages/editor-preset-vision/tests/fixtures/prototype/div-vision.ts create mode 100644 packages/editor-preset-vision/tests/utils/index.ts create mode 100644 packages/editor-preset-vision/tests/vision-api/api-export.test.ts create mode 100644 packages/editor-preset-vision/tests/vision-api/project.test.ts diff --git a/packages/designer/src/project/project.ts b/packages/designer/src/project/project.ts index 2c16c25b0..9a482d3ee 100644 --- a/packages/designer/src/project/project.ts +++ b/packages/designer/src/project/project.ts @@ -101,7 +101,9 @@ export class Project { if (this.documents.length < 1) { return; } - this.documents.forEach((doc) => doc.remove()); + for (let i = this.documents.length - 1; i >= 0; i--) { + this.documents[i].remove(); + } } removeDocument(doc: DocumentModel) { diff --git a/packages/editor-core/src/di/ioc-context.ts b/packages/editor-core/src/di/ioc-context.ts index 27b854cab..5520230bf 100644 --- a/packages/editor-core/src/di/ioc-context.ts +++ b/packages/editor-core/src/di/ioc-context.ts @@ -1,5 +1,9 @@ import { IocContext } from 'power-di'; +// 原本的 canBeKey 里判断函数的方法是 instanceof Function,在某些 babel 编译 class 后的场景不满足该判断条件,此处暴力破解 +IocContext.prototype.canBeKey = (obj: any) => + typeof obj === 'function' || ['string', 'symbol'].includes(typeof obj); + export * from 'power-di'; export const globalContext = IocContext.DefaultInstance; diff --git a/packages/editor-preset-vision/.eslintrc.js b/packages/editor-preset-vision/.eslintrc.js index 4888cbdf0..4c845e2de 100644 --- a/packages/editor-preset-vision/.eslintrc.js +++ b/packages/editor-preset-vision/.eslintrc.js @@ -2,7 +2,7 @@ module.exports = { extends: 'eslint-config-ali/typescript/react', rules: { 'react/no-multi-comp': 1, - 'no-unused-expressions': 1, + 'no-unused-expressions': 0, 'implicit-arrow-linebreak': 1, 'no-nested-ternary': 1, 'no-mixed-operators': 1, @@ -16,5 +16,6 @@ module.exports = { 'react/no-deprecated': 1, 'no-useless-escape': 1, 'brace-style': 1, + '@typescript-eslint/member-ordering': 0, } } \ No newline at end of file diff --git a/packages/editor-preset-vision/jest.config.js b/packages/editor-preset-vision/jest.config.js index 810547389..2cbe0c3fe 100644 --- a/packages/editor-preset-vision/jest.config.js +++ b/packages/editor-preset-vision/jest.config.js @@ -1,6 +1,6 @@ const esModules = [ '@recore/obx-react', - '@ali/lowcode-editor-core', + // '@ali/lowcode-editor-core', ].join('|'); module.exports = { diff --git a/packages/editor-preset-vision/src/bus.ts b/packages/editor-preset-vision/src/bus.ts index dd6b8386a..83dd0db71 100644 --- a/packages/editor-preset-vision/src/bus.ts +++ b/packages/editor-preset-vision/src/bus.ts @@ -64,15 +64,15 @@ export class Bus { const bus = new Bus(); -editor.on('hotkey.callback.call', (data) => { +editor?.on('hotkey.callback.call', (data) => { bus.emit('ve.hotkey.callback.call', data); }); -editor.on('history.back', (data) => { +editor?.on('history.back', (data) => { bus.emit('ve.history.back', data); }); -editor.on('history.forward', (data) => { +editor?.on('history.forward', (data) => { bus.emit('ve.history.forward', data); }); diff --git a/packages/editor-preset-vision/src/editor.ts b/packages/editor-preset-vision/src/editor.ts index 33c650dcf..3ff2575b3 100644 --- a/packages/editor-preset-vision/src/editor.ts +++ b/packages/editor-preset-vision/src/editor.ts @@ -3,8 +3,6 @@ import { isPlainObject, hasOwnProperty, cloneDeep, isI18NObject, isUseI18NSetter import { globalContext, Editor } from '@ali/lowcode-editor-core'; import { Designer, LiveEditing, TransformStage, Node, getConvertedExtraKey } from '@ali/lowcode-designer'; import Outline, { OutlineBackupPane, getTreeMaster } from '@ali/lowcode-plugin-outline-pane'; -import { toCss } from '@ali/vu-css-style'; -import logger from '@ali/vu-logger'; import bus from './bus'; import { VE_EVENTS } from './base/const'; @@ -13,7 +11,16 @@ import { Skeleton, SettingsPrimaryPane, registerDefaults } from '@ali/lowcode-ed import { deepValueParser } from './deep-value-parser'; import { liveEditingRule, liveEditingSaveHander } from './vc-live-editing'; -import { isVariable } from './utils'; +import { + compatibleReducer, + compatiblePageReducer, + stylePropsReducer, + upgradePropsReducer, + filterReducer, + removeEmptyPropsReducer, + initNodeReducer, + liveLifecycleReducer, +} from './props-reducers'; export const editor = new Editor(); globalContext.register(editor, Editor); @@ -31,313 +38,31 @@ designer.project.onCurrentDocumentChange((doc) => { bus.emit(VE_EVENTS.VE_PAGE_PAGE_READY); }); -interface Variable { - type: 'variable'; - variable: string; - value: any; -} - -function upgradePropsReducer(props: 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; -} - // 升级 Props designer.addPropsReducer(upgradePropsReducer, TransformStage.Upgrade); -function getCurrentFieldIds() { - const fieldIds: any = []; - const nodesMap = designer?.currentDocument?.nodesMap || new Map(); - nodesMap.forEach((curNode: any) => { - const fieldId = nodesMap?.get(curNode.id)?.getPropValue('fieldId'); - if (fieldId) { - fieldIds.push(fieldId); - } - }); - return fieldIds; -} - // 节点 props 初始化 -designer.addPropsReducer((props, node) => { - // debugger; - // run initials - const newProps: any = { - ...props, - }; - if (newProps.fieldId) { - const fieldIds = getCurrentFieldIds(); +designer.addPropsReducer(initNodeReducer, TransformStage.Init); - // 全局的关闭 uniqueIdChecker 信号,在 ve-utils 中实现 - if (fieldIds.indexOf(props.fieldId) >= 0 && !(window as any).__disable_unique_id_checker__) { - newProps.fieldId = undefined; - } - } - const initials = node.componentMeta.getMetadata().experimental?.initials; - if (initials) { - const getRealValue = (propValue: any) => { - if (isVariable(propValue)) { - return propValue.value; - } - if (isJSExpression(propValue)) { - return propValue.mock; - } - return propValue; - }; - initials.forEach((item) => { - // FIXME! this implements SettingTarget - try { - // FIXME! item.name could be 'xxx.xxx' - const ov = newProps[item.name]; - const v = item.initial(node as any, getRealValue(ov)); - if (ov === undefined && v !== undefined) { - newProps[item.name] = v; - } - // 兼容 props 中的属性为 i18n 类型,但是仅提供了一个字符串值,非变量绑定 - if (isUseI18NSetter(node.componentMeta.prototype, item.name) && - !isI18NObject(ov) && - !isJSExpression(ov) && - !isJSBlock(ov) && - !isJSSlot(ov) && - !isVariable(ov) && - (isString(v) || isI18NObject(v))) { - newProps[item.name] = convertToI18NObject(v); - } - } catch (e) { - if (hasOwnProperty(props, item.name)) { - newProps[item.name] = props[item.name]; - } - } - if (newProps[item.name] && !node.props.has(item.name)) { - node.props.add(newProps[item.name], item.name, false, { skipSetSlot: true }); - } - }); - } - return newProps; -}, TransformStage.Init); +designer.addPropsReducer(liveLifecycleReducer, TransformStage.Render); -designer.addPropsReducer((props: any, node: Node) => { - // live 模式下解析 lifeCycles - if (node.isRoot() && props && props.lifeCycles) { - if (editor.get('designMode') === 'live') { - const lifeCycleMap = { - didMount: 'componentDidMount', - willUnmount: 'componentWillUnMount', - }; - const lifeCycles = props.lifeCycles; - Object.keys(lifeCycleMap).forEach(key => { - if (lifeCycles[key]) { - lifeCycles[lifeCycleMap[key]] = lifeCycles[key]; - } - }); - return props; - } - return { - ...props, - lifeCycles: {}, - }; - } - return props; -}, TransformStage.Render); - -function filterReducer(props: any, node: Node): any { - const filters = node.componentMeta.getMetadata().experimental?.filters; - if (filters && filters.length) { - const newProps = { ...props }; - filters.forEach((item) => { - // FIXME! item.name could be 'xxx.xxx' - if (!hasOwnProperty(newProps, item.name)) { - return; - } - try { - if (item.filter(node.settingEntry.getProp(item.name), props[item.name]) === false) { - delete newProps[item.name]; - } - } catch (e) { - console.warn(e); - logger.trace(e); - } - }); - return newProps; - } - return props; -} designer.addPropsReducer(filterReducer, TransformStage.Save); designer.addPropsReducer(filterReducer, TransformStage.Render); -function compatiableReducer(props: any) { - if (!props || !isPlainObject(props)) { - return props; - } - // 为了能降级到老版本,建议在后期版本去掉以下代码 - if (isJSSlot(props)) { - return { - type: 'JSBlock', - value: { - componentName: 'Slot', - children: props.value, - props: { - slotTitle: props.title, - slotName: props.name, - }, - }, - }; - } - if (isJSExpression(props) && !props.events) { - return { - type: 'variable', - value: props.mock, - variable: props.value, - }; - } - const newProps: any = {}; - Object.entries(props).forEach(([key, val]) => { - newProps[key] = compatiableReducer(val); - }); - return newProps; -} // FIXME: Dirty fix, will remove this reducer -designer.addPropsReducer(compatiableReducer, TransformStage.Save); +designer.addPropsReducer(compatibleReducer, TransformStage.Save); // 兼容历史版本的 Page 组件 -designer.addPropsReducer((props: any, node: Node) => { - const lifeCycleNames = ['didMount', 'willUnmount']; - if (node.isRoot()) { - lifeCycleNames.forEach((key) => { - if (props[key]) { - const lifeCycles = node.props.getPropValue(getConvertedExtraKey('lifeCycles')) || {}; - lifeCycles[key] = props[key]; - node.props.setPropValue(getConvertedExtraKey('lifeCycles'), lifeCycles); - } - }); - } - return props; -}, TransformStage.Save); - -// designer.addPropsReducer((props: any, node: Node) => { -// const lifeCycleNames = ['didMount', 'willUnmount']; -// const lifeCycleMap = { -// didMount: 'componentDidMount', -// willUnmount: 'componentWillUnMount', -// }; -// if (node.componentName === 'Page') { -// debugger; -// lifeCycleNames.forEach(key => { -// if (props[key]) { -// const lifeCycles = node.props.getPropValue(getConvertedExtraKey('lifeCycles')) || {}; -// lifeCycles[lifeCycleMap[key]] = props[key]; -// node.props.setPropValue(getConvertedExtraKey('lifeCycles'), lifeCycles); -// } else if (node.props.getPropValue(getConvertedExtraKey('lifeCycles'))) { -// const lifeCycles = node.props.getPropValue(getConvertedExtraKey('lifeCycles')) || {}; -// lifeCycles[lifeCycleMap[key]] = lifeCycles[key]; -// node.props.setPropValue(getConvertedExtraKey('lifeCycles'), lifeCycles); -// } -// }); -// } -// return props; -// }, TransformStage.Init); +designer.addPropsReducer(compatiblePageReducer, TransformStage.Save); // 设计器组件样式处理 -function stylePropsReducer(props: any, node: any) { - if (props && typeof props === 'object' && props.__style__) { - const cssId = `_style_pesudo_${ node.id.replace(/\$/g, '_')}`; - const cssClass = `_css_pesudo_${ node.id.replace(/\$/g, '_')}`; - const styleProp = props.__style__; - appendStyleNode(props, styleProp, cssClass, cssId); - } - if (props && typeof props === 'object' && props.pageStyle) { - const cssId = '_style_pesudo_engine-document'; - const cssClass = 'engine-document'; - const styleProp = props.pageStyle; - appendStyleNode(props, styleProp, cssClass, cssId); - } - if (props && typeof props === 'object' && props.containerStyle) { - const cssId = `_style_pesudo_${ node.id}`; - const cssClass = `_css_pesudo_${ node.id.replace(/\$/g, '_')}`; - const styleProp = props.containerStyle; - appendStyleNode(props, styleProp, cssClass, cssId); - } - return props; -} - -function appendStyleNode(props: any, styleProp: any, cssClass: string, cssId: string) { - const doc = designer.currentDocument?.simulator?.contentDocument; - if (!doc) { - return; - } - const dom = doc.getElementById(cssId); - if (dom) { - dom.parentNode?.removeChild(dom); - } - if (typeof styleProp === 'object') { - styleProp = toCss(styleProp); - } - if (typeof styleProp === 'string') { - const s = doc.createElement('style'); - props.className = cssClass; - s.setAttribute('type', 'text/css'); - s.setAttribute('id', cssId); - doc.getElementsByTagName('head')[0].appendChild(s); - - s.appendChild(doc.createTextNode(styleProp.replace(/(\d+)rpx/g, (a, b) => { - return `${b / 2}px`; - }).replace(/:root/g, `.${ cssClass}`))); - } -} designer.addPropsReducer(stylePropsReducer, TransformStage.Render); // 国际化 & Expression 渲染时处理 designer.addPropsReducer(deepValueParser, TransformStage.Render); -// 清除空的 props value -function removeEmptyProps(props: any, node: Node) { - if (node.isRoot() && props.dataSource && Array.isArray(props.dataSource.online)) { - const online = cloneDeep(props.dataSource.online); - online.forEach((item: any) => { - const newParam: any = {}; - if (Array.isArray(item?.options?.params)) { - item.options.params.forEach((element: any) => { - if (element.name) { - newParam[element.name] = element.value; - } - }); - item.options.params = newParam; - } - }); - props.dataSource.list = online; - } - return props; -} - // Init 的时候没有拿到 dataSource, 只能在 Render 和 Save 的时候都调用一次,理论上执行时机在 Init // Render 和 Save 都要各调用一次,感觉也是有问题的,是不是应该在 Render 执行一次就行了?见上 filterReducer 也是一样的处理方式。 -designer.addPropsReducer(removeEmptyProps, TransformStage.Render); -designer.addPropsReducer(removeEmptyProps, TransformStage.Save); +designer.addPropsReducer(removeEmptyPropsReducer, TransformStage.Render); +designer.addPropsReducer(removeEmptyPropsReducer, TransformStage.Save); skeleton.add({ area: 'mainArea', diff --git a/packages/editor-preset-vision/src/index.ts b/packages/editor-preset-vision/src/index.ts index 06a04bb58..47a684711 100644 --- a/packages/editor-preset-vision/src/index.ts +++ b/packages/editor-preset-vision/src/index.ts @@ -162,7 +162,7 @@ export { Symbols, }; -const version = '6.0.0(LowcodeEngine 0.9.3)'; +const version = '6.0.0 (LowcodeEngine 0.9.32)'; console.log( `%c VisionEngine %c v${version} `, diff --git a/packages/editor-preset-vision/src/pages.ts b/packages/editor-preset-vision/src/pages.ts index 81ae1d672..b6d3dc0ef 100644 --- a/packages/editor-preset-vision/src/pages.ts +++ b/packages/editor-preset-vision/src/pages.ts @@ -64,7 +64,6 @@ const pages = Object.assign(project, { item.methods = {}; } }); - console.log(pages, componentsTree); project.load( { version: '1.0.0', diff --git a/packages/editor-preset-vision/src/props-reducers/downgrade-schema-reducer.ts b/packages/editor-preset-vision/src/props-reducers/downgrade-schema-reducer.ts new file mode 100644 index 000000000..709d4390c --- /dev/null +++ b/packages/editor-preset-vision/src/props-reducers/downgrade-schema-reducer.ts @@ -0,0 +1,51 @@ +import { getConvertedExtraKey } from '@ali/lowcode-designer'; +import { + isPlainObject, +} from '@ali/lowcode-utils'; +import { isJSExpression, isJSSlot } from '@ali/lowcode-types'; + +export function compatibleReducer(props: any) { + if (!props || !isPlainObject(props)) { + return props; + } + // 为了能降级到老版本,建议在后期版本去掉以下代码 + if (isJSSlot(props)) { + return { + type: 'JSBlock', + value: { + componentName: 'Slot', + children: props.value, + props: { + slotTitle: props.title, + slotName: props.name, + }, + }, + }; + } + if (isJSExpression(props) && !props.events) { + return { + type: 'variable', + value: props.mock, + variable: props.value, + }; + } + const newProps: any = {}; + Object.entries(props).forEach(([key, val]) => { + newProps[key] = compatibleReducer(val); + }); + return newProps; +} + +export function compatiblePageReducer(props: any, node: Node) { + const lifeCycleNames = ['didMount', 'willUnmount']; + if (node.isRoot()) { + lifeCycleNames.forEach(key => { + if (props[key]) { + const lifeCycles = node.props.getPropValue(getConvertedExtraKey('lifeCycles')) || {}; + lifeCycles[key] = props[key]; + node.props.setPropValue(getConvertedExtraKey('lifeCycles'), lifeCycles); + } + }); + } + return props; +} diff --git a/packages/editor-preset-vision/src/props-reducers/filter-reducer.ts b/packages/editor-preset-vision/src/props-reducers/filter-reducer.ts new file mode 100644 index 000000000..928e8dd82 --- /dev/null +++ b/packages/editor-preset-vision/src/props-reducers/filter-reducer.ts @@ -0,0 +1,25 @@ +import logger from '@ali/vu-logger'; +import { hasOwnProperty } from '@ali/lowcode-utils'; + +export function filterReducer(props: any, node: Node): any { + const filters = node.componentMeta.getMetadata().experimental?.filters; + if (filters && filters.length) { + const newProps = { ...props }; + filters.forEach((item) => { + // FIXME! item.name could be 'xxx.xxx' + if (!hasOwnProperty(newProps, item.name)) { + return; + } + try { + if (item.filter(node.settingEntry.getProp(item.name), props[item.name]) === false) { + delete newProps[item.name]; + } + } catch (e) { + console.warn(e); + logger.trace(e); + } + }); + return newProps; + } + return props; +} \ No newline at end of file diff --git a/packages/editor-preset-vision/src/props-reducers/index.ts b/packages/editor-preset-vision/src/props-reducers/index.ts new file mode 100644 index 000000000..347e5edf9 --- /dev/null +++ b/packages/editor-preset-vision/src/props-reducers/index.ts @@ -0,0 +1,7 @@ +export * from './downgrade-schema-reducer'; +export * from './filter-reducer'; +export * from './init-node-reducer'; +export * from './live-lifecycle-reducer'; +export * from './remove-empty-prop-reducer'; +export * from './style-reducer'; +export * from './upgrade-reducer'; diff --git a/packages/editor-preset-vision/src/props-reducers/init-node-reducer.ts b/packages/editor-preset-vision/src/props-reducers/init-node-reducer.ts new file mode 100644 index 000000000..72e7f4e5d --- /dev/null +++ b/packages/editor-preset-vision/src/props-reducers/init-node-reducer.ts @@ -0,0 +1,68 @@ +import { + hasOwnProperty, + isI18NObject, + isUseI18NSetter, + convertToI18NObject, + isString, +} from '@ali/lowcode-utils'; +import { isJSExpression, isJSBlock, isJSSlot } from '@ali/lowcode-types'; +import { isVariable, getCurrentFieldIds } from '../utils'; + +export function initNodeReducer(props, node) { + // debugger; + // run initials + const newProps: any = { + ...props, + }; + if (newProps.fieldId) { + const fieldIds = getCurrentFieldIds(); + + // 全局的关闭 uniqueIdChecker 信号,在 ve-utils 中实现 + if (fieldIds.indexOf(props.fieldId) >= 0 && !(window as any).__disable_unique_id_checker__) { + newProps.fieldId = undefined; + } + } + const initials = node.componentMeta.getMetadata().experimental?.initials; + if (initials) { + const getRealValue = (propValue: any) => { + if (isVariable(propValue)) { + return propValue.value; + } + if (isJSExpression(propValue)) { + return propValue.mock; + } + return propValue; + }; + initials.forEach(item => { + // FIXME! this implements SettingTarget + try { + // FIXME! item.name could be 'xxx.xxx' + const ov = newProps[item.name]; + const v = item.initial(node as any, getRealValue(ov)); + if (ov === undefined && v !== undefined) { + newProps[item.name] = v; + } + // 兼容 props 中的属性为 i18n 类型,但是仅提供了一个字符串值,非变量绑定 + if ( + isUseI18NSetter(node.componentMeta.prototype, item.name) && + !isI18NObject(ov) && + !isJSExpression(ov) && + !isJSBlock(ov) && + !isJSSlot(ov) && + !isVariable(ov) && + (isString(v) || isI18NObject(v)) + ) { + newProps[item.name] = convertToI18NObject(v); + } + } catch (e) { + if (hasOwnProperty(props, item.name)) { + newProps[item.name] = props[item.name]; + } + } + if (newProps[item.name] && !node.props.has(item.name)) { + node.props.add(newProps[item.name], item.name, false, { skipSetSlot: true }); + } + }); + } + return newProps; +} diff --git a/packages/editor-preset-vision/src/props-reducers/live-lifecycle-reducer.ts b/packages/editor-preset-vision/src/props-reducers/live-lifecycle-reducer.ts new file mode 100644 index 000000000..a626c3b30 --- /dev/null +++ b/packages/editor-preset-vision/src/props-reducers/live-lifecycle-reducer.ts @@ -0,0 +1,27 @@ +import { globalContext, Editor } from '@ali/lowcode-editor-core'; +import { Node } from '@ali/lowcode-designer'; + +export function liveLifecycleReducer(props: any, node: Node) { + const editor = globalContext.get(Editor); + // live 模式下解析 lifeCycles + if (node.isRoot() && props && props.lifeCycles) { + if (editor.get('designMode') === 'live') { + const lifeCycleMap = { + didMount: 'componentDidMount', + willUnmount: 'componentWillUnMount', + }; + const lifeCycles = props.lifeCycles; + Object.keys(lifeCycleMap).forEach(key => { + if (lifeCycles[key]) { + lifeCycles[lifeCycleMap[key]] = lifeCycles[key]; + } + }); + return props; + } + return { + ...props, + lifeCycles: {}, + }; + } + return props; +} \ No newline at end of file diff --git a/packages/editor-preset-vision/src/props-reducers/remove-empty-prop-reducer.ts b/packages/editor-preset-vision/src/props-reducers/remove-empty-prop-reducer.ts new file mode 100644 index 000000000..305f0bdb7 --- /dev/null +++ b/packages/editor-preset-vision/src/props-reducers/remove-empty-prop-reducer.ts @@ -0,0 +1,23 @@ +import { + cloneDeep, +} from '@ali/lowcode-utils'; + +// 清除空的 props value +export function removeEmptyPropsReducer(props: any, node: Node) { + if (node.isRoot() && props.dataSource && Array.isArray(props.dataSource.online)) { + const online = cloneDeep(props.dataSource.online); + online.forEach((item: any) => { + const newParam: any = {}; + if (Array.isArray(item?.options?.params)) { + item.options.params.forEach((element: any) => { + if (element.name) { + newParam[element.name] = element.value; + } + }); + item.options.params = newParam; + } + }); + props.dataSource.list = online; + } + return props; +} diff --git a/packages/editor-preset-vision/src/props-reducers/style-reducer.ts b/packages/editor-preset-vision/src/props-reducers/style-reducer.ts new file mode 100644 index 000000000..d5facb0a6 --- /dev/null +++ b/packages/editor-preset-vision/src/props-reducers/style-reducer.ts @@ -0,0 +1,51 @@ +import { globalContext, Editor } from '@ali/lowcode-editor-core'; +import { toCss } from '@ali/vu-css-style'; + +export function stylePropsReducer(props: any, node: any) { + if (props && typeof props === 'object' && props.__style__) { + const cssId = `_style_pesudo_${ node.id.replace(/\$/g, '_')}`; + const cssClass = `_css_pesudo_${ node.id.replace(/\$/g, '_')}`; + const styleProp = props.__style__; + appendStyleNode(props, styleProp, cssClass, cssId); + } + if (props && typeof props === 'object' && props.pageStyle) { + const cssId = '_style_pesudo_engine-document'; + const cssClass = 'engine-document'; + const styleProp = props.pageStyle; + appendStyleNode(props, styleProp, cssClass, cssId); + } + if (props && typeof props === 'object' && props.containerStyle) { + const cssId = `_style_pesudo_${ node.id}`; + const cssClass = `_css_pesudo_${ node.id.replace(/\$/g, '_')}`; + const styleProp = props.containerStyle; + appendStyleNode(props, styleProp, cssClass, cssId); + } + return props; +} + +function appendStyleNode(props: any, styleProp: any, cssClass: string, cssId: string) { + const editor = globalContext.get(Editor); + const designer = editor.get('designer'); + const doc = designer.currentDocument?.simulator?.contentDocument; + if (!doc) { + return; + } + const dom = doc.getElementById(cssId); + if (dom) { + dom.parentNode?.removeChild(dom); + } + if (typeof styleProp === 'object') { + styleProp = toCss(styleProp); + } + if (typeof styleProp === 'string') { + const s = doc.createElement('style'); + props.className = cssClass; + s.setAttribute('type', 'text/css'); + s.setAttribute('id', cssId); + doc.getElementsByTagName('head')[0].appendChild(s); + + s.appendChild(doc.createTextNode(styleProp.replace(/(\d+)rpx/g, (a, b) => { + return `${b / 2}px`; + }).replace(/:root/g, `.${ cssClass}`))); + } +} \ No newline at end of file diff --git a/packages/editor-preset-vision/src/props-reducers/upgrade-reducer.ts b/packages/editor-preset-vision/src/props-reducers/upgrade-reducer.ts new file mode 100644 index 000000000..38bbca7ff --- /dev/null +++ b/packages/editor-preset-vision/src/props-reducers/upgrade-reducer.ts @@ -0,0 +1,38 @@ +import { + isPlainObject, +} from '@ali/lowcode-utils'; +import { isJSBlock } from '@ali/lowcode-types'; +import { isVariable } from '../utils'; + +export function upgradePropsReducer(props: 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; +} \ No newline at end of file diff --git a/packages/editor-preset-vision/src/utils/index.ts b/packages/editor-preset-vision/src/utils/index.ts index c07d6ad04..ec3d7ea31 100644 --- a/packages/editor-preset-vision/src/utils/index.ts +++ b/packages/editor-preset-vision/src/utils/index.ts @@ -1,3 +1,25 @@ -export function isVariable(obj: any) { +import { globalContext, Editor } from '@ali/lowcode-editor-core'; + +interface Variable { + type: 'variable'; + variable: string; + value: any; +} + +export function isVariable(obj: any): obj is Variable { return obj && obj.type === 'variable'; } + +export function getCurrentFieldIds() { + const editor = globalContext.get(Editor); + const designer = editor.get('designer'); + const fieldIds: any = []; + const nodesMap = designer?.currentDocument?.nodesMap || new Map(); + nodesMap.forEach((curNode: any) => { + const fieldId = nodesMap?.get(curNode.id)?.getPropValue('fieldId'); + if (fieldId) { + fieldIds.push(fieldId); + } + }); + return fieldIds; +} diff --git a/packages/editor-preset-vision/tests/bundle/prototype.test.ts b/packages/editor-preset-vision/tests/bundle/prototype.test.ts new file mode 100644 index 000000000..a120522a1 --- /dev/null +++ b/packages/editor-preset-vision/tests/bundle/prototype.test.ts @@ -0,0 +1,71 @@ +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 divPrototypeConfig from '../fixtures/prototype/div-vision'; +import divPrototypeMeta from '../fixtures/prototype/div-meta'; +// import VisualEngine from '../../src'; +import { designer } from '../../src/editor'; +import Prototype from '../../src/bundle/prototype'; +import { Editor } from '@ali/lowcode-editor-core'; +// import { getIdsFromSchema, getNodeFromSchemaById } from '../utils'; + + +describe('Prototype', () => { + it('构造函数 - OldPrototypeConfig', () => { + const proto = new Prototype(divPrototypeConfig); + expect(proto.getComponentName()).toBe('Div'); + expect(proto.getId()).toBe('Div'); + expect(proto.getCategory()).toBe('布局'); + expect(proto.getDocUrl()).toBe('http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md'); + expect(proto.getIcon()).toBeUndefined; + expect(proto.getTitle()).toBe('Div'); + expect(proto.isPrototype).toBeTruthy; + expect(proto.isContainer()).toBeTruthy; + expect(proto.isModal()).toBeFalsy; + }); + it('构造函数 - ComponentMetadata', () => { + const proto = new Prototype(divPrototypeMeta); + expect(proto.getComponentName()).toBe('Div'); + expect(proto.getId()).toBe('Div'); + expect(proto.getCategory()).toBe('布局'); + expect(proto.getDocUrl()).toBe('http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md'); + expect(proto.getIcon()).toBeUndefined; + expect(proto.getTitle()).toBe('Div'); + expect(proto.isPrototype).toBeTruthy; + expect(proto.isContainer()).toBeTruthy; + expect(proto.isModal()).toBeFalsy; + }); + it('构造函数 - ComponentMeta', () => { + const meta = designer.createComponentMeta(divPrototypeMeta); + const proto = new Prototype(meta); + expect(proto.getComponentName()).toBe('Div'); + expect(proto.getId()).toBe('Div'); + expect(proto.getCategory()).toBe('布局'); + expect(proto.getDocUrl()).toBe('http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md'); + expect(proto.getIcon()).toBeUndefined; + expect(proto.getTitle()).toBe('Div'); + expect(proto.isPrototype).toBeTruthy; + expect(proto.isContainer()).toBeTruthy; + expect(proto.isModal()).toBeFalsy; + }); + it('构造函数 - 静态函数 create', () => { + const proto = Prototype.create(divPrototypeConfig); + expect(proto.getComponentName()).toBe('Div'); + expect(proto.getId()).toBe('Div'); + expect(proto.getCategory()).toBe('布局'); + expect(proto.getDocUrl()).toBe('http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md'); + expect(proto.getIcon()).toBeUndefined; + expect(proto.getTitle()).toBe('Div'); + expect(proto.isPrototype).toBeTruthy; + expect(proto.isContainer()).toBeTruthy; + expect(proto.isModal()).toBeFalsy; + }); + it('构造函数 - lookup: true', () => { + const proto = Prototype.create(divPrototypeConfig); + const proto2 = Prototype.create(divPrototypeConfig, {}, true); + expect(proto).toBe(proto2); + }); +}); diff --git a/packages/editor-preset-vision/tests/fixtures/prototype/div-meta.ts b/packages/editor-preset-vision/tests/fixtures/prototype/div-meta.ts new file mode 100644 index 000000000..a2b410494 --- /dev/null +++ b/packages/editor-preset-vision/tests/fixtures/prototype/div-meta.ts @@ -0,0 +1,259 @@ +export default { + componentName: 'Div', + title: '容器', + docUrl: 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md', + 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: 'groupkh97h5kc', + 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: {}, + }, + supports: {}, + }, + experimental: { + callbacks: {}, + initials: [ + { + name: 'behavior', + }, + { + name: '__style__', + }, + { + name: 'fieldId', + }, + { + name: 'useFieldIdAsDomId', + }, + { + name: 'customClassName', + }, + { + name: 'events', + }, + { + name: 'onClick', + }, + { + name: 'onMouseEnter', + }, + { + name: 'onMouseLeave', + }, + ], + filters: [], + autoruns: [], + }, +}; diff --git a/packages/editor-preset-vision/tests/fixtures/prototype/div-vision.ts b/packages/editor-preset-vision/tests/fixtures/prototype/div-vision.ts new file mode 100644 index 000000000..c4ae4374f --- /dev/null +++ b/packages/editor-preset-vision/tests/fixtures/prototype/div-vision.ts @@ -0,0 +1,175 @@ +export default { + title: '容器', + componentName: 'Div', + docUrl: 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md', + category: '布局', + isContainer: true, + configure: [ + { + name: 'behavior', + title: '默认状态', + display: 'inline', + initialValue: 'NORMAL', + supportVariable: true, + setter: { + key: null, + ref: null, + props: { + options: [ + { + title: '普通', + value: 'NORMAL', + }, + { + title: '隐藏', + value: 'HIDDEN', + }, + ], + loose: false, + cancelable: false, + }, + _owner: null, + }, + }, + { + name: '__style__', + title: '样式设置', + display: 'accordion', + collapsed: false, + initialValue: {}, + tip: { + url: 'https://lark.alipay.com/legao/help/design-tool-style', + content: '点击 ? 查看样式设置器用法指南', + }, + setter: { + key: null, + ref: null, + props: { + advanced: true, + }, + _owner: null, + }, + }, + { + type: 'group', + title: '高级', + display: 'accordion', + items: [ + { + name: 'fieldId', + title: '唯一标识', + display: 'block', + tip: + '组件的唯一标识符,不能够与其它组件重名,不能够为空,且只能够使用以字母开头的,下划线以及数字的组合。', + setter: { + key: null, + ref: null, + props: { + placeholder: '请输入唯一标识', + multiline: false, + rows: 10, + required: false, + pattern: null, + maxLength: null, + }, + _owner: null, + }, + }, + { + name: 'useFieldIdAsDomId', + title: '将唯一标识用作 DOM ID', + display: 'block', + tip: + '开启这个配置项后,会在当前组件的 HTML 元素上加入 id="当前组件的 fieldId",一般用于做 utils 的绑定,不常用', + initialValue: false, + setter: { + key: null, + ref: null, + props: {}, + _owner: null, + }, + }, + { + name: 'customClassName', + title: '自定义样式类', + display: 'block', + supportVariable: true, + initialValue: '', + setter: { + key: null, + ref: null, + props: { + placeholder: null, + multiline: false, + rows: 10, + required: false, + pattern: null, + maxLength: null, + }, + _owner: null, + }, + }, + { + name: 'events', + title: '动作设置', + tip: { + url: 'https://lark.alipay.com/legao/legao/events-call', + content: '点击 ? 查看如何设置组件的事件响应动作', + }, + display: 'accordion', + initialValue: { + 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, + }, + }, + { + name: 'onClick', + display: 'none', + initialValue: { + ignored: true, + }, + }, + { + name: 'onMouseEnter', + display: 'none', + initialValue: { + ignored: true, + }, + }, + { + name: 'onMouseLeave', + display: 'none', + initialValue: { + ignored: true, + }, + }, + ], + }, + ], +}; diff --git a/packages/editor-preset-vision/tests/fixtures/schema/form.ts b/packages/editor-preset-vision/tests/fixtures/schema/form.ts index f9d9477fb..905b89561 100644 --- a/packages/editor-preset-vision/tests/fixtures/schema/form.ts +++ b/packages/editor-preset-vision/tests/fixtures/schema/form.ts @@ -1,750 +1,254 @@ export default { - schemaType: 'superform', - schemaVersion: '5.0', - pages: [ + componentName: 'Page', + id: 'node_k1ow3cb9', + props: { + extensions: { + 启用页头: true, + }, + pageStyle: { + backgroundColor: '#f2f3f5', + }, + containerStyle: {}, + className: 'page_kh05zf9c', + 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_kh05zf9d {\n margin-bottom: 12px;\n}.card_kh05zf9e {\n margin-bottom: 12px;\n}.button_kh05zf9f {\n margin-right: 16px;\n width: 80px\n}.button_kh05zf9g {\n width: 80px;\n}.div_kh05zf9h {\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: [ { - componentsMap: [ - { - componentName: 'Text', - package: '@ali/vc-text', - }, - { - componentName: 'Slot', - package: '@ali/vc-slot', - }, + componentName: 'RootHeader', + id: 'node_k1ow3cba', + props: {}, + condition: true, + children: [ { componentName: 'PageHeader', - package: '@ali/vc-deep', - }, - { - componentName: 'RootHeader', - package: '@ali/vc-page', - }, - { - componentName: 'TextField', - package: '@ali/vc-deep', - }, - { - componentName: 'Column', - package: '@ali/vc-deep', - }, - { - componentName: 'SelectField', - package: '@ali/vc-deep', - }, - { - componentName: 'ColumnsLayout', - package: '@ali/vc-deep', - }, - { - componentName: 'CardContent', - package: '@ali/vc-deep', - }, - { - componentName: 'Card', - package: '@ali/vc-deep', - }, - { - componentName: 'Button', - package: '@ali/vc-deep', - }, - { - componentName: 'Div', - package: '@ali/vc-div', - }, - { - componentName: 'Form', - package: '@ali/vc-deep', - }, - { - componentName: 'RootContent', - package: '@ali/vc-page', - }, - { - componentName: 'RootFooter', - package: '@ali/vc-page', - }, - { - componentName: 'Page', - package: '@ali/vc-page', - }, - ], - componentsTree: [ - { - componentName: 'Page', - id: 'node_k1ow3cb9', + id: 'node_k1ow3cbd', props: { - extensions: { - 启用页头: true, - }, - pageStyle: { - backgroundColor: '#f2f3f5', - }, - containerStyle: {}, - className: 'page_kh05zf9c', - 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}", - }, + extraContent: '', + __slot__extraContent: false, + __slot__action: false, + title: '', + content: '', + __slot__logo: false, + __slot__crumb: false, + crumb: '', + tab: '', + logo: '', + action: '', + __slot__tab: false, + __style__: {}, + __slot__content: false, + fieldId: 'pageHeader_k1ow3h1i', + subTitle: '', }, condition: true, - css: - 'body{background-color:#f2f3f5}.card_kh05zf9d {\n margin-bottom: 12px;\n}.card_kh05zf9e {\n margin-bottom: 12px;\n}.button_kh05zf9f {\n margin-right: 16px;\n width: 80px\n}.button_kh05zf9g {\n width: 80px;\n}.div_kh05zf9h {\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*/ }', + }, + ], + }, + { + componentName: 'RootContent', + id: 'node_k1ow3cbb', + props: { + contentBgColor: 'transparent', + contentPadding: '0', + contentMargin: '20', + }, + condition: true, + children: [ + { + componentName: 'Form', + id: 'node_k1ow3cbq', + props: { + size: 'medium', + labelAlign: 'top', + autoValidate: true, + scrollToFirstError: true, + autoUnmount: true, + behavior: 'NORMAL', + dataSource: { + type: 'variable', + variable: 'state.formData', }, + __style__: {}, + fieldId: 'form', + fieldOptions: {}, }, - dataSource: { - offline: [], - globalConfig: { - fit: { - compiled: '', - source: '', - type: 'js', - error: {}, - }, - }, - online: [], - sync: true, - list: [], - }, + condition: true, 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: 'JSBlock', - value: { - componentName: 'Slot', - children: [ - { - componentName: 'Text', - id: 'node_k1ow3cbf', - props: { - showTitle: false, - behavior: 'NORMAL', - content: { - type: 'variable', - value: { - use: 'zh_CN', - en_US: 'Title', - zh_CN: '个人信息', - type: 'i18n', - }, - variable: 'state.title', - }, - __style__: {}, - fieldId: 'text_k1ow3h1j', - maxLine: 0, - }, - condition: true, - }, - ], - props: { - slotTitle: '标题区域', - slotName: 'title', - }, - }, - }, - content: '', - __slot__logo: false, - __slot__crumb: false, - crumb: '', - tab: '', - logo: '', - action: '', - __slot__tab: false, - __style__: {}, - __slot__content: false, - fieldId: 'pageHeader_k1ow3h1i', - subTitle: { - type: 'JSBlock', - value: { - componentName: 'Slot', - children: [ - { - componentName: 'Text', - id: 'node_ockh05zr7j3', - props: { - content: { - type: 'i18n', - en_US: 'Title', - zh_CN: '副标题', - }, - showTitle: false, - behavior: 'NORMAL', - maxLine: 0, - __style__: {}, - fieldId: 'text_kh06n1mc', - }, - }, - ], - props: { - slotTitle: '副标题', - slotName: 'subTitle', - }, - }, - }, - }, - condition: true, - }, - ], - }, - { - componentName: 'RootContent', - id: 'node_k1ow3cbb', + componentName: 'Card', + id: 'node_k1ow3cbj', props: { - contentBgColor: 'transparent', - contentPadding: '0', - contentMargin: '20', + __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_kh05zf9d', + 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: 'Form', - id: 'node_k1ow3cbq', - props: { - size: 'medium', - labelAlign: 'top', - autoValidate: true, - scrollToFirstError: true, - autoUnmount: true, - behavior: 'NORMAL', - dataSource: { - type: 'variable', - variable: 'state.formData', - }, - __style__: {}, - fieldId: 'form', - fieldOptions: {}, - }, + componentName: 'CardContent', + id: 'node_k1ow3cbk', + props: {}, condition: true, children: [ { - componentName: 'Card', - id: 'node_k1ow3cbj', + componentName: 'ColumnsLayout', + id: 'node_k1ow3cbw', 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_kh05zf9d', - 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, + layout: '4:8', + columnGap: '20', + rowGap: 0, + __style__: {}, + fieldId: 'columns_k1ow3h1v', }, condition: true, children: [ { - componentName: 'CardContent', - id: 'node_k1ow3cbk', - props: {}, - condition: true, - children: [ - { - componentName: 'ColumnsLayout', - id: 'node_k1ow3cbw', - props: { - layout: '4:8', - 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: '', - }, - maxLength: 200, - }, - 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: '', - }, - maxLength: 200, - }, - 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: '', - }, - maxLength: 200, - }, - 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: '', - }, - maxLength: 200, - }, - 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: '', - }, - useDetailValue: false, - 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_kh05zf9e', - 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: {}, + componentName: 'Column', + id: 'node_k1ow3cbx', + props: { + colSpan: '', + __style__: {}, + fieldId: 'column_k1p1bnjm', + }, condition: true, children: [ { componentName: 'TextField', - id: 'node_k1ow3cc4', + id: 'node_k1ow3cbz', props: { - fieldName: 'department', + 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: '', + }, + maxLength: 200, + }, + condition: true, + }, + { + componentName: 'TextField', + id: 'node_k1ow3cc1', + props: { + fieldName: 'englishName', hasClear: false, autoFocus: false, tips: { @@ -776,14 +280,14 @@ export default { hasLimitHint: false, cutString: false, __style__: {}, - fieldId: 'textField_k1ow3h21', + fieldId: 'textField_k1ow3h1y', htmlType: 'input', autoHeight: false, labelColOffset: 0, label: { use: 'zh_CN', en_US: 'TextField', - zh_CN: '所属部门', + zh_CN: '英文名', type: 'i18n', }, __category__: 'form', @@ -812,264 +316,251 @@ export default { condition: true, }, { - componentName: 'ColumnsLayout', - id: 'node_k1ow3cc5', + componentName: 'TextField', + id: 'node_k1ow3cc3', props: { - layout: '6:6', - columnGap: '20', - rowGap: 0, + 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: 'columns_k1ow3h22', + 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: '', + }, + maxLength: 200, }, 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: '', - }, - maxLength: 200, - }, - 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: '', - }, - maxLength: 200, - }, - condition: true, - }, - ], - }, - ], }, ], }, - ], - }, - { - componentName: 'Div', - id: 'node_k1ow3cbo', - props: { - className: 'div_kh05zf9h', - 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', + componentName: 'Column', + id: 'node_k1ow3cby', 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', + 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: '', + }, + maxLength: 200, + }, + condition: true, }, - size: 'medium', - baseIcon: '', - otherIcon: '', - className: 'button_kh05zf9f', - type: 'primary', - behavior: 'NORMAL', - loading: false, - content: { - use: 'zh_CN', - en_US: 'Button', - zh_CN: '提交', - type: 'i18n', + { + 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: '', + }, + useDetailValue: false, + searchDelay: 300, + }, + condition: true, }, - __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_kh05zf9g', - 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', - }, - condition: true, + ], }, ], }, @@ -1078,34 +569,387 @@ export default { ], }, { - componentName: 'RootFooter', - id: 'node_k1ow3cbc', - props: {}, + 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_kh05zf9e', + 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: '', + }, + maxLength: 200, + }, + 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: '', + }, + maxLength: 200, + }, + 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: '', + }, + maxLength: 200, + }, + condition: true, + }, + ], + }, + ], + }, + ], + }, + ], + }, + { + componentName: 'Div', + id: 'node_k1ow3cbo', + props: { + className: 'div_kh05zf9h', + 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_kh05zf9f', + 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_kh05zf9g', + 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', + }, + condition: true, + }, + ], }, ], }, ], - id: 'FORM-MV766X71QI5KCL6T4CXKF9DJUME73BD0FQAGKE', + }, + { + componentName: 'RootFooter', + id: 'node_k1ow3cbc', + props: {}, + condition: true, }, ], - actions: { - module: { - compiled: - "'use strict';\n\nexports.__esModule = true;\nexports.helloPage = helloPage;\nexports.submit = submit;\n/**\n * 私有函数,只能JS面板内部调用\n * 动作面板帮助文档:\n * @see https://lark.alipay.com/legao/help/design-tool-logic\n */\nfunction printLog(obj) {\n console.info(obj);\n}\n\n/**\n * 页面内的函数,可以被页面内任何位置调用\n */\nfunction helloPage() {\n console.log('hello page');\n}\n\n/**\n* button onClick\n*/\nfunction submit() {\n var _this = this;\n\n this.$('form').submit(function (data, error) {\n if (data) {\n console.log(data); // 这是表单提交的数据\n _this.utils.toast({\n type: 'success',\n title: '提交成功'\n });\n }\n });\n}", - source: - "/**\n * 私有函数,只能JS面板内部调用\n * 动作面板帮助文档:\n * @see https://lark.alipay.com/legao/help/design-tool-logic\n */\nfunction printLog(obj) {\n console.info(obj);\n}\n\n/**\n * 页面内的函数,可以被页面内任何位置调用\n */\nexport function helloPage() {\n console.log('hello page');\n}\n\n\n/**\n* button onClick\n*/\nexport function submit(){\n this.$('form').submit((data, error) => {\n if (data) {\n console.log(data); // 这是表单提交的数据\n this.utils.toast({\n type: 'success',\n title: '提交成功'\n });\n }\n });\n}", - }, - type: 'FUNCTION', - list: [ - { - id: 'helloPage', - title: 'helloPage', - }, - { - id: 'submit', - title: 'submit', - }, - ], - }, }; diff --git a/packages/editor-preset-vision/tests/utils/index.ts b/packages/editor-preset-vision/tests/utils/index.ts new file mode 100644 index 000000000..70fce0af2 --- /dev/null +++ b/packages/editor-preset-vision/tests/utils/index.ts @@ -0,0 +1 @@ +export { getIdsFromSchema, getNodeFromSchemaById } from '@ali/lowcode-test-mate/es/utils'; diff --git a/packages/editor-preset-vision/tests/vision-api/api-export.test.ts b/packages/editor-preset-vision/tests/vision-api/api-export.test.ts new file mode 100644 index 000000000..50278dc72 --- /dev/null +++ b/packages/editor-preset-vision/tests/vision-api/api-export.test.ts @@ -0,0 +1,89 @@ +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 VisualEngine, { + designer, + editor, + skeleton, + /** + * VE.Popup + */ + Popup, + /** + * VE Utils + */ + utils, + I18nUtil, + Hotkey, + Env, + monitor, + /* pub/sub 集线器 */ + Bus, + /* 事件 */ + EVENTS, + /* 修饰方法 */ + HOOKS, + Exchange, + context, + /** + * VE.init + * + * Initialized the whole VisualEngine UI + */ + init, + ui, + Panes, + modules, + Trunk, + Prototype, + Bundle, + Pages, + DragEngine, + Viewport, + Version, + Project, + logger, + Symbols, +} from '../../src'; +import { Editor } from '@ali/lowcode-editor-core'; + +describe('API 多种导出场景测试', () => { + it('window.VisualEngine 和 npm 导出 API 测试', () => { + expect(VisualEngine).toBe(window.VisualEngine); + }); + + it('npm 导出 API 对比测试', () => { + expect(VisualEngine.designer).toBe(designer); + expect(VisualEngine.editor).toBe(editor); + expect(VisualEngine.skeleton).toBe(skeleton); + expect(VisualEngine.Popup).toBe(Popup); + expect(VisualEngine.utils).toBe(utils); + expect(VisualEngine.I18nUtil).toBe(I18nUtil); + expect(VisualEngine.Hotkey).toBe(Hotkey); + expect(VisualEngine.Env).toBe(Env); + expect(VisualEngine.monitor).toBe(monitor); + expect(VisualEngine.Bus).toBe(Bus); + expect(VisualEngine.EVENTS).toBe(EVENTS); + expect(VisualEngine.HOOKS).toBe(HOOKS); + expect(VisualEngine.Exchange).toBe(Exchange); + expect(VisualEngine.context).toBe(context); + expect(VisualEngine.init).toBe(init); + expect(VisualEngine.ui).toBe(ui); + expect(VisualEngine.Panes).toBe(Panes); + expect(VisualEngine.modules).toBe(modules); + expect(VisualEngine.Trunk).toBe(Trunk); + expect(VisualEngine.Prototype).toBe(Prototype); + expect(VisualEngine.Bundle).toBe(Bundle); + expect(VisualEngine.DragEngine).toBe(DragEngine); + expect(VisualEngine.Pages).toBe(Pages); + expect(VisualEngine.Viewport).toBe(Viewport); + expect(VisualEngine.Version).toBe(Version); + expect(VisualEngine.Project).toBe(Project); + expect(VisualEngine.logger).toBe(logger); + expect(VisualEngine.Symbols).toBe(Symbols); + }); +}); \ No newline at end of file diff --git a/packages/editor-preset-vision/tests/vision-api/pages.test.ts b/packages/editor-preset-vision/tests/vision-api/pages.test.ts index d020b6df1..259cbef63 100644 --- a/packages/editor-preset-vision/tests/vision-api/pages.test.ts +++ b/packages/editor-preset-vision/tests/vision-api/pages.test.ts @@ -1,46 +1,161 @@ 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 VisualEngine from '../../src'; -// import { getIdsFromSchema, getNodeFromSchemaById } from '../utils'; +import { Editor } from '@ali/lowcode-editor-core'; +import { getIdsFromSchema, getNodeFromSchemaById } from '../utils'; -// const mockCreateSettingEntry = jest.fn(); -// jest.mock('../../src/designer/designer', () => { -// return { -// Designer: jest.fn().mockImplementation(() => { -// return { -// getComponentMeta() { -// return { -// getMetadata() { -// return { experimental: null }; -// }, -// }; -// }, -// transformProps(props) { return props; }, -// createSettingEntry: mockCreateSettingEntry, -// postEvent() {}, -// }; -// }), -// }; -// }); +const pageSchema = { componentsTree: [formSchema] }; - - -// let designer = null; -// beforeAll(() => { -// designer = new Designer({}); -// }); - -describe('schema 生成节点模型测试', () => { - describe('block ❌ | component ❌ | slot ❌', () => { - it('基本的节点模型初始化,模型导出,初始化传入 schema', () => { - console.log(VisualEngine); - - console.log(VisualEngine.Pages.addPage(formSchema)); +describe('VisualEngine.Pages 相关 API 测试', () => { + afterEach(() => { + VisualEngine.Pages.unload(); + }); + describe('addPage 系列', () => { + it('基本的节点模型初始化,初始化传入 schema', () => { + const doc = VisualEngine.Pages.addPage(pageSchema)!; + expect(doc).toBeTruthy(); + const ids = getIdsFromSchema(formSchema); + const expectedNodeCnt = ids.length; + expect(doc.nodesMap.size).toBe(expectedNodeCnt); + }); + it('基本的节点模型初始化,初始化传入 schema,带有 slot', () => { + const formSchemaWithSlot = set(cloneDeep(formSchema), 'children[0].children[0].props.title', { + type: 'JSBlock', + value: { + componentName: 'Slot', + children: [ + { + componentName: 'Text', + id: 'node_k1ow3cbf', + props: { + showTitle: false, + behavior: 'NORMAL', + content: { + type: 'variable', + value: { + use: 'zh_CN', + en_US: 'Title', + zh_CN: '个人信息', + type: 'i18n', + }, + variable: 'state.title', + }, + __style__: {}, + fieldId: 'text_k1ow3h1j', + maxLine: 0, + }, + condition: true, + }, + ], + props: { + slotTitle: '标题区域', + slotName: 'title', + }, + }, + }); + const doc = VisualEngine.Pages.addPage({ componentsTree: [formSchemaWithSlot] })!; + expect(doc).toBeTruthy(); + const ids = getIdsFromSchema(formSchema); + const expectedNodeCnt = ids.length; + // slot 会多出(1 + N)个节点 + expect(doc.nodesMap.size).toBe(expectedNodeCnt + 2); + }); + it('导出 schema', () => { + const doc = VisualEngine.Pages.addPage(pageSchema)!; + expect(doc).toBeTruthy(); + const ids = getIdsFromSchema(formSchema); + const expectedNodeCnt = ids.length; + const exportedData = doc.toData(); + expect(exportedData).toHaveProperty('componentsMap'); + expect(exportedData).toHaveProperty('componentsTree'); + expect(exportedData.componentsTree).toHaveLength(1); + const exportedSchema = exportedData.componentsTree[0]; + expect(getIdsFromSchema(exportedSchema).length).toBe(expectedNodeCnt); }); }); -}); \ No newline at end of file + describe('removePage 系列', () => { + it('removePage', () => { + const doc = VisualEngine.Pages.addPage(pageSchema)!; + expect(doc).toBeTruthy(); + expect(VisualEngine.Pages.documents).toHaveLength(1); + VisualEngine.Pages.removePage(doc); + expect(VisualEngine.Pages.documents).toHaveLength(0); + }); + }); + describe('getPage 系列', () => { + it('getPage', () => { + const doc = VisualEngine.Pages.addPage(pageSchema); + const anotherFormSchema = set(cloneDeep(formSchema), 'id', 'page'); + const doc2 = VisualEngine.Pages.addPage({ componentsTree: [anotherFormSchema] }); + expect(VisualEngine.Pages.getPage(0)).toBe(doc); + expect(VisualEngine.Pages.getPage((_doc) => _doc.rootNode.id === 'page')).toBe(doc2); + }); + }); + describe('setPages 系列', () => { + it('setPages componentsTree 只有一个元素', () => { + VisualEngine.Pages.setPages([pageSchema]); + const { currentDocument } = VisualEngine.Pages; + const ids = getIdsFromSchema(formSchema); + const expectedNodeCnt = ids.length; + const exportedData = currentDocument.toData(); + expect(exportedData).toHaveProperty('componentsMap'); + expect(exportedData).toHaveProperty('componentsTree'); + expect(exportedData.componentsTree).toHaveLength(1); + const exportedSchema = exportedData.componentsTree[0]; + expect(getIdsFromSchema(exportedSchema).length).toBe(expectedNodeCnt); + }); + }); + describe('setCurrentPage / getCurrentPage / currentPage / currentDocument 系列', () => { + it('getCurrentPage', () => { + const doc = VisualEngine.Pages.addPage(pageSchema)!; + expect(doc).toBeTruthy(); + expect(doc).toBe(VisualEngine.Pages.getCurrentPage()); + expect(doc).toBe(VisualEngine.Pages.currentDocument); + expect(doc).toBe(VisualEngine.Pages.currentPage); + }); + it('setCurrentPage', () => { + const doc = VisualEngine.Pages.addPage(pageSchema); + expect(doc).toBe(VisualEngine.Pages.currentDocument); + const anotherFormSchema = set(cloneDeep(formSchema), 'id', 'page'); + const doc2 = VisualEngine.Pages.addPage({ componentsTree: [anotherFormSchema] }); + expect(doc2).toBe(VisualEngine.Pages.currentDocument); + VisualEngine.Pages.setCurrentPage(doc); + expect(doc).toBe(VisualEngine.Pages.currentDocument); + }); + }); + describe('onCurrentPageChange 系列', () => { + it('多次切换', () => { + const doc = VisualEngine.Pages.addPage(pageSchema); + const anotherFormSchema = set(cloneDeep(formSchema), 'id', 'page'); + const doc2 = VisualEngine.Pages.addPage({ componentsTree: [anotherFormSchema] }); + const docChangeHandler = jest.fn(); + VisualEngine.Pages.onCurrentDocumentChange(docChangeHandler); + VisualEngine.Pages.setCurrentPage(doc); + expect(docChangeHandler).toHaveBeenCalledTimes(1); + expect(docChangeHandler).toHaveBeenLastCalledWith(doc); + + VisualEngine.Pages.setCurrentPage(doc2); + expect(docChangeHandler).toHaveBeenCalledTimes(2); + expect(docChangeHandler).toHaveBeenLastCalledWith(doc2); + }); + }); + describe('toData 系列', () => { + it('基本的节点模型初始化,模型导出,初始化传入 schema', () => { + const doc = VisualEngine.Pages.addPage(pageSchema); + const anotherFormSchema = set(cloneDeep(formSchema), 'id', 'page'); + const doc2 = VisualEngine.Pages.addPage({ componentsTree: [anotherFormSchema] }); + const dataList = VisualEngine.Pages.toData(); + expect(dataList.length).toBe(2); + expect(dataList[0]).toHaveProperty('componentsMap'); + expect(dataList[0]).toHaveProperty('componentsTree'); + expect(dataList[0].componentsTree).toHaveLength(1); + expect(dataList[0].componentsTree[0].id).toBe('node_k1ow3cb9'); + expect(dataList[1]).toHaveProperty('componentsMap'); + expect(dataList[1]).toHaveProperty('componentsTree'); + expect(dataList[1].componentsTree).toHaveLength(1); + expect(dataList[1].componentsTree[0].id).toBe('page'); + }); + }); +}); diff --git a/packages/editor-preset-vision/tests/vision-api/project.test.ts b/packages/editor-preset-vision/tests/vision-api/project.test.ts new file mode 100644 index 000000000..6e07cfb02 --- /dev/null +++ b/packages/editor-preset-vision/tests/vision-api/project.test.ts @@ -0,0 +1,38 @@ +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 VisualEngine from '../../src'; +import { Editor } from '@ali/lowcode-editor-core'; + +// import { getIdsFromSchema, getNodeFromSchemaById } from '../utils'; + + +describe.skip('VisualEngine.Project 相关 API 测试', () => { + describe('getSchema / setSchema 系列', () => { + it('基本的节点模型初始化,模型导出,初始化传入 schema', () => { + // console.log(VisualEngine); + // console.log(Editor instanceof Function); + // console.log(Editor.toString()) + // console.log(new Editor()); + // console.log(Editor2 instanceof Function); + + console.log(VisualEngine.Pages.addPage(formSchema)); + }); + }); + + describe('setConfig 系列', () => { + it('基本的节点模型初始化,模型导出,初始化传入 schema', () => { + // console.log(VisualEngine); + // console.log(Editor instanceof Function); + // console.log(Editor.toString()) + // console.log(new Editor()); + // console.log(Editor2 instanceof Function); + + console.log(VisualEngine.Pages.addPage(formSchema)); + }); + }); +}); \ No newline at end of file From bfa0f363349d0b2393bb4468ba44eeeb71a1cdd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8A=9B=E7=9A=93?= Date: Mon, 9 Nov 2020 10:04:35 +0800 Subject: [PATCH 5/7] =?UTF-8?q?chore:=20=E5=88=A0=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E7=94=A8=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/designer/plugin.js | 12 ------------ packages/react-simulator-renderer/src/renderer.ts | 1 - 2 files changed, 13 deletions(-) delete mode 100644 packages/designer/plugin.js diff --git a/packages/designer/plugin.js b/packages/designer/plugin.js deleted file mode 100644 index 97c09d4b9..000000000 --- a/packages/designer/plugin.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = ({ onGetJestConfig, onGetWebpackConfig }) => { - console.log('111111111111xxxxxxxxxxxx') - onGetWebpackConfig((config) => { - console.log('111111111111') - config.browserslist('last 2 versions, Firefox ESR, > 1%, ie >= 9, iOS >= 8, Android >= 4'); - }); - - onGetJestConfig((jestConfig) => { - console.log('11111111111122222222') - return jestConfig; - }); -}; diff --git a/packages/react-simulator-renderer/src/renderer.ts b/packages/react-simulator-renderer/src/renderer.ts index 889a1a958..205d6eb3c 100644 --- a/packages/react-simulator-renderer/src/renderer.ts +++ b/packages/react-simulator-renderer/src/renderer.ts @@ -540,7 +540,6 @@ function getComponentController(schema: NodeSchema, componentsMap: any) { constructor(props: any) { super(props); - console.log('>>> current'); const node = host.currentDocument?.createNode(schema); this.renderSchema = node?.export(TransformStage.Render) || {}; } From 537258d9e6b8e90be33614e6888891eab04705f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8A=9B=E7=9A=93?= Date: Tue, 10 Nov 2020 14:03:11 +0800 Subject: [PATCH 6/7] =?UTF-8?q?chore:=20=E5=B0=86=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E7=9A=84=20parent=20=E4=BF=9D=E7=95=99?= =?UTF-8?q?=EF=BC=8CsubtreeModified=20=E6=97=B6=E8=83=BD=E6=8B=BF=E5=88=B0?= =?UTF-8?q?=E4=B8=80=E4=BA=9B=E7=88=B6=E5=AD=90=E5=85=B3=E7=B3=BB=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=20refactor:=20=E4=B8=8D=E5=86=8D=E5=9C=A8=20document?= =?UTF-8?q?=20=E5=88=9D=E5=A7=8B=E5=8C=96=E5=AE=8C=E6=88=90=E5=90=8E?= =?UTF-8?q?=E6=96=B0=E5=BB=BA=E7=9A=84=E8=8A=82=E7=82=B9=E9=83=BD=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E9=87=8D=E7=BD=AE=20id=20chore(test):=20=E8=A1=A5?= =?UTF-8?q?=E5=85=85=20prototype=20=E9=83=A8=E5=88=86=E7=9A=84=E5=8D=95?= =?UTF-8?q?=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bem-tools/border-resizing.tsx | 2 +- .../designer/src/document/document-model.ts | 2 +- packages/designer/src/document/node/node.ts | 4 +- packages/editor-core/src/di/ioc-context.ts | 4 - .../tests/bundle/prototype.test.ts | 144 +++++++++++++++++- .../src/transducers/addon-combine.ts | 2 +- packages/rax-simulator-renderer/package.json | 2 +- 7 files changed, 144 insertions(+), 16 deletions(-) diff --git a/packages/designer/src/builtin-simulator/bem-tools/border-resizing.tsx b/packages/designer/src/builtin-simulator/bem-tools/border-resizing.tsx index 12aa99174..7c60e0143 100644 --- a/packages/designer/src/builtin-simulator/bem-tools/border-resizing.tsx +++ b/packages/designer/src/builtin-simulator/bem-tools/border-resizing.tsx @@ -179,7 +179,7 @@ export class BoxResizingInstance extends Component<{ typeof metaData.experimental.callbacks.onResizeEnd === 'function' ) { (e as any).trigger = direction; - metaData.experimental.callbacks.onResizeStart(e, node); + metaData.experimental.callbacks.onResizeEnd(e, node); } const editor = globalContext.get(Editor); diff --git a/packages/designer/src/document/document-model.ts b/packages/designer/src/document/document-model.ts index 42b292880..cef2bc77e 100644 --- a/packages/designer/src/document/document-model.ts +++ b/packages/designer/src/document/document-model.ts @@ -205,7 +205,7 @@ export class DocumentModel { } let node: Node | null = null; - if (this.inited) { + if (this.hasNode(schema?.id)) { schema.id = null; } if (schema.id) { diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts index 01ee191a2..a2deabff7 100644 --- a/packages/designer/src/document/node/node.ts +++ b/packages/designer/src/document/node/node.ts @@ -295,9 +295,9 @@ export class Node { if (useMutator) { this._parent?.didDropOut(this); } - // 建立新的父子关系 - this._parent = parent; if (parent) { + // 建立新的父子关系,尤其注意:对于 parent 为 null 的场景,不会赋值,因为 subtreeModified 等事件可能需要知道该 node 被删除前的父子关系 + this._parent = parent; this.document.removeWillPurge(this); if (!this.conditionGroup) { // initial conditionGroup diff --git a/packages/editor-core/src/di/ioc-context.ts b/packages/editor-core/src/di/ioc-context.ts index 5520230bf..27b854cab 100644 --- a/packages/editor-core/src/di/ioc-context.ts +++ b/packages/editor-core/src/di/ioc-context.ts @@ -1,9 +1,5 @@ import { IocContext } from 'power-di'; -// 原本的 canBeKey 里判断函数的方法是 instanceof Function,在某些 babel 编译 class 后的场景不满足该判断条件,此处暴力破解 -IocContext.prototype.canBeKey = (obj: any) => - typeof obj === 'function' || ['string', 'symbol'].includes(typeof obj); - export * from 'power-di'; export const globalContext = IocContext.DefaultInstance; diff --git a/packages/editor-preset-vision/tests/bundle/prototype.test.ts b/packages/editor-preset-vision/tests/bundle/prototype.test.ts index a120522a1..cc9667563 100644 --- a/packages/editor-preset-vision/tests/bundle/prototype.test.ts +++ b/packages/editor-preset-vision/tests/bundle/prototype.test.ts @@ -1,3 +1,4 @@ +import { Component } from 'react'; import set from 'lodash/set'; import cloneDeep from 'lodash/clonedeep'; import '../fixtures/window'; @@ -8,18 +9,20 @@ import divPrototypeConfig from '../fixtures/prototype/div-vision'; import divPrototypeMeta from '../fixtures/prototype/div-meta'; // import VisualEngine from '../../src'; import { designer } from '../../src/editor'; -import Prototype from '../../src/bundle/prototype'; +import Prototype, { isPrototype } from '../../src/bundle/prototype'; import { Editor } from '@ali/lowcode-editor-core'; // import { getIdsFromSchema, getNodeFromSchemaById } from '../utils'; - describe('Prototype', () => { it('构造函数 - OldPrototypeConfig', () => { const proto = new Prototype(divPrototypeConfig); + expect(isPrototype(proto)).toBeTruthy; expect(proto.getComponentName()).toBe('Div'); expect(proto.getId()).toBe('Div'); expect(proto.getCategory()).toBe('布局'); - expect(proto.getDocUrl()).toBe('http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md'); + expect(proto.getDocUrl()).toBe( + 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md', + ); expect(proto.getIcon()).toBeUndefined; expect(proto.getTitle()).toBe('Div'); expect(proto.isPrototype).toBeTruthy; @@ -31,7 +34,9 @@ describe('Prototype', () => { expect(proto.getComponentName()).toBe('Div'); expect(proto.getId()).toBe('Div'); expect(proto.getCategory()).toBe('布局'); - expect(proto.getDocUrl()).toBe('http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md'); + expect(proto.getDocUrl()).toBe( + 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md', + ); expect(proto.getIcon()).toBeUndefined; expect(proto.getTitle()).toBe('Div'); expect(proto.isPrototype).toBeTruthy; @@ -44,7 +49,9 @@ describe('Prototype', () => { expect(proto.getComponentName()).toBe('Div'); expect(proto.getId()).toBe('Div'); expect(proto.getCategory()).toBe('布局'); - expect(proto.getDocUrl()).toBe('http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md'); + expect(proto.getDocUrl()).toBe( + 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md', + ); expect(proto.getIcon()).toBeUndefined; expect(proto.getTitle()).toBe('Div'); expect(proto.isPrototype).toBeTruthy; @@ -56,7 +63,9 @@ describe('Prototype', () => { expect(proto.getComponentName()).toBe('Div'); expect(proto.getId()).toBe('Div'); expect(proto.getCategory()).toBe('布局'); - expect(proto.getDocUrl()).toBe('http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md'); + expect(proto.getDocUrl()).toBe( + 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md', + ); expect(proto.getIcon()).toBeUndefined; expect(proto.getTitle()).toBe('Div'); expect(proto.isPrototype).toBeTruthy; @@ -68,4 +77,127 @@ describe('Prototype', () => { const proto2 = Prototype.create(divPrototypeConfig, {}, true); expect(proto).toBe(proto2); }); + describe('类成员函数', () => { + let proto: Prototype = null; + beforeEach(() => { + proto = new Prototype(divPrototypeConfig); + }); + afterEach(() => { + proto = null; + }); + it('各种函数', () => { + expect(proto.componentName).toBe('Div'); + expect(proto.getComponentName()).toBe('Div'); + expect(proto.getId()).toBe('Div'); + expect(proto.getContextInfo('anything')).toBeUndefined; + expect(proto.getPropConfigs()).toBe(divPrototypeConfig); + expect(proto.getConfig()).toBe(divPrototypeConfig); + expect(proto.getConfig('componentName')).toBe('Div'); + expect(proto.getConfig('configure')).toBe(divPrototypeConfig.configure); + expect(proto.getConfig('docUrl')).toBe( + 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md', + ); + expect(proto.getConfig('title')).toBe('容器'); + expect(proto.getConfig('isContainer')).toBeTruthy; + + class MockView extends Component {} + + expect(proto.getView()).toBeUndefined; + proto.setView(MockView); + expect(proto.getView()).toBe(MockView); + expect(proto.meta.getMetadata().experimental?.view).toBe(MockView); + + expect(proto.getPackageName()).toBeUndefined; + proto.setPackageName('@ali/vc-div'); + expect(proto.getPackageName()).toBe('@ali/vc-div'); + + expect(proto.getConfig('category')).toBe('布局'); + proto.setCategory('布局 new'); + expect(proto.getConfig('category')).toBe('布局 new'); + + expect(proto.getConfigure()).toHaveLength(3); + expect(proto.getConfigure()[0].name).toBe('#props'); + expect(proto.getConfigure()[1].name).toBe('#styles'); + expect(proto.getConfigure()[2].name).toBe('#advanced'); + + expect(proto.getRectSelector()).toBeUndefined; + expect(proto.isAutoGenerated()).toBeFalsy; + }); + }); + + describe('类成员函数', () => { + it('addGlobalPropsConfigure', () => { + Prototype.addGlobalPropsConfigure({ + name: 'globalInsertProp1', + }); + const proto1 = new Prototype(divPrototypeConfig); + expect(proto1.getConfigure()[2].items).toHaveLength(4); + expect(proto1.getConfigure()[2].items[3].name).toBe('globalInsertProp1'); + Prototype.addGlobalPropsConfigure({ + name: 'globalInsertProp2', + }); + const proto2 = new Prototype(divPrototypeConfig); + expect(proto2.getConfigure()[2].items).toHaveLength(5); + expect(proto1.getConfigure()[2].items[4].name).toBe('globalInsertProp2'); + + Prototype.addGlobalPropsConfigure({ + name: 'globalInsertProp3', + position: 'top', + }); + + const proto3 = new Prototype(divPrototypeConfig); + expect(proto3.getConfigure()[0].items).toHaveLength(3); + expect(proto1.getConfigure()[0].items[0].name).toBe('globalInsertProp3'); + }); + + it('removeGlobalPropsConfigure', () => { + Prototype.removeGlobalPropsConfigure('globalInsertProp1'); + Prototype.removeGlobalPropsConfigure('globalInsertProp2'); + Prototype.removeGlobalPropsConfigure('globalInsertProp3'); + const proto2 = new Prototype(divPrototypeConfig); + expect(proto2.getConfigure()[0].items).toHaveLength(2); + expect(proto2.getConfigure()[2].items).toHaveLength(3); + }); + + it('overridePropsConfigure', () => { + Prototype.addGlobalPropsConfigure({ + name: 'globalInsertProp1', + title: 'globalInsertPropTitle', + position: 'top', + }); + const proto1 = new Prototype(divPrototypeConfig); + expect(proto1.getConfigure()[0].items).toHaveLength(3); + expect(proto1.getConfigure()[0].items[0].name).toBe('globalInsertProp1'); + expect(proto1.getConfigure()[0].items[0].title).toBe('globalInsertPropTitle'); + + Prototype.overridePropsConfigure('Div', [ + { + name: 'globalInsertProp1', + title: 'globalInsertPropTitleChanged', + }, + ]); + const proto2 = new Prototype(divPrototypeConfig); + expect(proto2.getConfigure()[0].name).toBe('globalInsertProp1'); + expect(proto2.getConfigure()[0].title).toBe('globalInsertPropTitleChanged'); + + Prototype.overridePropsConfigure('Div', { + globalInsertProp1: { + name: 'globalInsertProp1', + title: 'globalInsertPropTitleChanged new', + position: 'top', + }, + }); + const proto3 = new Prototype(divPrototypeConfig); + expect(proto3.getConfigure()[0].items[0].name).toBe('globalInsertProp1'); + expect(proto3.getConfigure()[0].items[0].title).toBe('globalInsertPropTitleChanged new'); + }); + + it('addGlobalExtraActions', () => { + function haha() { return 'heihei'; } + Prototype.addGlobalExtraActions(haha); + const proto1 = new Prototype(divPrototypeConfig); + expect(proto1.meta.availableActions).toHaveLength(4); + expect(proto1.meta.availableActions[3].name).toBe('haha'); + }); + }); }); diff --git a/packages/editor-skeleton/src/transducers/addon-combine.ts b/packages/editor-skeleton/src/transducers/addon-combine.ts index fa8d846f8..4a4c5b598 100644 --- a/packages/editor-skeleton/src/transducers/addon-combine.ts +++ b/packages/editor-skeleton/src/transducers/addon-combine.ts @@ -329,7 +329,7 @@ export default function (metadata: TransformedComponentMetadata): TransformedCom if (advanceGroup.length > 0) { combined.push({ name: '#advanced', - title: { type: 'i18n', 'zh-CN': '高级', 'en-US': 'Advance' }, + title: { type: 'i18n', 'zh-CN': '高级', 'en-US': 'Advanced' }, items: advanceGroup, }); } diff --git a/packages/rax-simulator-renderer/package.json b/packages/rax-simulator-renderer/package.json index 20f80bbcf..5469de810 100644 --- a/packages/rax-simulator-renderer/package.json +++ b/packages/rax-simulator-renderer/package.json @@ -57,5 +57,5 @@ "publishConfig": { "registry": "https://registry.npm.alibaba-inc.com" }, - "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-rax-simulator-renderer@0.13.1-10/build/index.html" + "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-rax-simulator-renderer@0.13.1-11/build/index.html" } From cd12677138a223eaf2b1579d27d3bf0addc1565e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8A=9B=E7=9A=93?= Date: Fri, 13 Nov 2020 15:22:41 +0800 Subject: [PATCH 7/7] =?UTF-8?q?feat:=20=E6=9A=B4=E9=9C=B2=20registerMetada?= =?UTF-8?q?taTransducer=20=E6=8E=A5=E5=8F=A3=20chore(test):=20=E8=A1=A5?= =?UTF-8?q?=E5=85=85=E5=8D=95=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/designer/jest.config.js | 1 + .../builtin-simulator/utils/parse-metadata.ts | 2 +- .../src/designer/setting/setting-top-entry.ts | 2 +- .../builtin-simulator/parse-metadata.test.ts | 9 + .../tests/builtin-simulator/path.test.ts | 78 +++++ .../tests/builtin-simulator/throttle.test.ts | 22 ++ .../tests/fixtures/component-metadata/div.ts | 5 +- .../designer/tests/fixtures/disable-raf.ts | 3 + .../tests/fixtures/prototype/div-meta.ts | 259 ++++++++++++++++ .../designer/tests/fixtures/schema/setting.ts | 90 ++++++ .../tests/meta/component-meta.test.ts | 45 ++- .../setting-entry/setting-prop-entry.test.ts | 105 +++++++ .../setting-entry/setting-top-entry.test.ts | 174 +++++++++++ packages/editor-preset-vision/jest.config.js | 3 + packages/editor-preset-vision/src/index.ts | 3 + .../src/props-reducers/init-node-reducer.ts | 2 +- .../tests/bundle/bundle.test.ts | 116 +++++++ .../tests/bundle/prototype.test.ts | 16 + .../tests/bundle/trunk.test.ts | 111 +++++++ .../fixtures/prototype/div-vision-full.ts | 293 ++++++++++++++++++ .../tests/fixtures/schema/form.ts | 2 +- .../tests/props-reducer/init-node.test.ts | 0 .../tests/vision-api/exchange.test.ts | 18 ++ .../tests/vision-api/pages.test.ts | 11 +- .../tests/vision-api/project.test.ts | 42 +-- packages/editor-skeleton/.eslintrc.js | 1 + .../settings/settings-primary-pane.tsx | 8 +- 27 files changed, 1382 insertions(+), 39 deletions(-) create mode 100644 packages/designer/tests/builtin-simulator/parse-metadata.test.ts create mode 100644 packages/designer/tests/builtin-simulator/path.test.ts create mode 100644 packages/designer/tests/builtin-simulator/throttle.test.ts create mode 100644 packages/designer/tests/fixtures/disable-raf.ts create mode 100644 packages/designer/tests/fixtures/prototype/div-meta.ts create mode 100644 packages/designer/tests/fixtures/schema/setting.ts create mode 100644 packages/designer/tests/setting-entry/setting-prop-entry.test.ts create mode 100644 packages/designer/tests/setting-entry/setting-top-entry.test.ts create mode 100644 packages/editor-preset-vision/tests/bundle/bundle.test.ts create mode 100644 packages/editor-preset-vision/tests/bundle/trunk.test.ts create mode 100644 packages/editor-preset-vision/tests/fixtures/prototype/div-vision-full.ts create mode 100644 packages/editor-preset-vision/tests/props-reducer/init-node.test.ts create mode 100644 packages/editor-preset-vision/tests/vision-api/exchange.test.ts diff --git a/packages/designer/jest.config.js b/packages/designer/jest.config.js index 83eb1dc90..b1c191127 100644 --- a/packages/designer/jest.config.js +++ b/packages/designer/jest.config.js @@ -14,6 +14,7 @@ module.exports = { collectCoverage: false, collectCoverageFrom: [ 'src/**/*.{ts,tsx}', + '!src/**/*.d.ts', '!**/node_modules/**', '!**/vendor/**', ], diff --git a/packages/designer/src/builtin-simulator/utils/parse-metadata.ts b/packages/designer/src/builtin-simulator/utils/parse-metadata.ts index 3bcf9d6d4..96c92943a 100644 --- a/packages/designer/src/builtin-simulator/utils/parse-metadata.ts +++ b/packages/designer/src/builtin-simulator/utils/parse-metadata.ts @@ -54,7 +54,7 @@ const LowcodeTypes: any = { (window as any).PropTypes = LowcodeTypes; (window as any).React.PropTypes = LowcodeTypes; -// override primitive type chechers +// override primitive type checkers primitiveTypes.forEach(type => { const propType = (PropTypes as any)[type]; if (!propType) { diff --git a/packages/designer/src/designer/setting/setting-top-entry.ts b/packages/designer/src/designer/setting/setting-top-entry.ts index 507fc053c..405c76faa 100644 --- a/packages/designer/src/designer/setting/setting-top-entry.ts +++ b/packages/designer/src/designer/setting/setting-top-entry.ts @@ -68,7 +68,7 @@ export class SettingTopEntry implements SettingEntry { readonly designer: Designer; constructor(readonly editor: IEditor, readonly nodes: Node[]) { - if (nodes.length < 1) { + if (!Array.isArray(nodes) || nodes.length < 1) { throw new ReferenceError('nodes should not be empty'); } this.id = generateSessionId(nodes); diff --git a/packages/designer/tests/builtin-simulator/parse-metadata.test.ts b/packages/designer/tests/builtin-simulator/parse-metadata.test.ts new file mode 100644 index 000000000..eefb55f7c --- /dev/null +++ b/packages/designer/tests/builtin-simulator/parse-metadata.test.ts @@ -0,0 +1,9 @@ +import '../fixtures/window'; +import { parseMetadata } from '../../src/builtin-simulator/utils/parse-metadata'; + +describe('parseMetadata', () => { + it('parseMetadata', async () => { + console.log(parseMetadata('Div')) + console.log(parseMetadata({ componentName: 'Div' })); + }); +}); \ No newline at end of file diff --git a/packages/designer/tests/builtin-simulator/path.test.ts b/packages/designer/tests/builtin-simulator/path.test.ts new file mode 100644 index 000000000..6a15edbcf --- /dev/null +++ b/packages/designer/tests/builtin-simulator/path.test.ts @@ -0,0 +1,78 @@ +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'); + }); +}); \ No newline at end of file diff --git a/packages/designer/tests/builtin-simulator/throttle.test.ts b/packages/designer/tests/builtin-simulator/throttle.test.ts new file mode 100644 index 000000000..fd2d7ce33 --- /dev/null +++ b/packages/designer/tests/builtin-simulator/throttle.test.ts @@ -0,0 +1,22 @@ +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); + }); +}); \ No newline at end of file diff --git a/packages/designer/tests/fixtures/component-metadata/div.ts b/packages/designer/tests/fixtures/component-metadata/div.ts index 27bcd18fc..395cb58bb 100644 --- a/packages/designer/tests/fixtures/component-metadata/div.ts +++ b/packages/designer/tests/fixtures/component-metadata/div.ts @@ -218,7 +218,10 @@ export default { ], component: { isContainer: true, - nestingRule: {}, + nestingRule: { + parentWhitelist: 'Div', + childWhitelist: 'Div', + }, }, supports: {}, }, diff --git a/packages/designer/tests/fixtures/disable-raf.ts b/packages/designer/tests/fixtures/disable-raf.ts new file mode 100644 index 000000000..14b710125 --- /dev/null +++ b/packages/designer/tests/fixtures/disable-raf.ts @@ -0,0 +1,3 @@ +Object.defineProperty(window, 'requestAnimationFrame', { + value: null, +}) \ No newline at end of file diff --git a/packages/designer/tests/fixtures/prototype/div-meta.ts b/packages/designer/tests/fixtures/prototype/div-meta.ts new file mode 100644 index 000000000..a2b410494 --- /dev/null +++ b/packages/designer/tests/fixtures/prototype/div-meta.ts @@ -0,0 +1,259 @@ +export default { + componentName: 'Div', + title: '容器', + docUrl: 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md', + 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: 'groupkh97h5kc', + 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: {}, + }, + supports: {}, + }, + experimental: { + callbacks: {}, + initials: [ + { + name: 'behavior', + }, + { + name: '__style__', + }, + { + name: 'fieldId', + }, + { + name: 'useFieldIdAsDomId', + }, + { + name: 'customClassName', + }, + { + name: 'events', + }, + { + name: 'onClick', + }, + { + name: 'onMouseEnter', + }, + { + name: 'onMouseLeave', + }, + ], + filters: [], + autoruns: [], + }, +}; diff --git a/packages/designer/tests/fixtures/schema/setting.ts b/packages/designer/tests/fixtures/schema/setting.ts new file mode 100644 index 000000000..a0d45d0d9 --- /dev/null +++ b/packages/designer/tests/fixtures/schema/setting.ts @@ -0,0 +1,90 @@ +export default { + componentName: 'Page', + id: 'node_k1ow3cb9', + 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', + } + ], +}; diff --git a/packages/designer/tests/meta/component-meta.test.ts b/packages/designer/tests/meta/component-meta.test.ts index cec01ba5f..f3cdfd2a6 100644 --- a/packages/designer/tests/meta/component-meta.test.ts +++ b/packages/designer/tests/meta/component-meta.test.ts @@ -4,7 +4,7 @@ import '../fixtures/window'; import { Node } from '../../src/document/node/node'; import { Designer } from '../../src/designer/designer'; import divMeta from '../fixtures/component-metadata/div'; -import { ComponentMeta } from '../../src/component-meta'; +import { ComponentMeta, isComponentMeta, removeBuiltinComponentAction, addBuiltinComponentAction } from '../../src/component-meta'; const mockCreateSettingEntry = jest.fn(); jest.mock('../../src/designer/designer', () => { @@ -25,6 +25,47 @@ beforeAll(() => { describe('组件元数据处理', () => { it('构造函数', () => { const meta = new ComponentMeta(designer, divMeta); - console.log(meta); + expect(meta.isContainer).toBeTruthy; + expect(isComponentMeta(meta)).toBeTruthy; + expect(meta.acceptable).toBeTruthy; + expect(meta.isRootComponent()).toBeTruthy; + expect(meta.isModal).toBeFalsy; + expect(meta.rootSelector).toBeUndefined; + expect(meta.liveTextEditing).toBeUndefined; + expect(meta.descriptor).toBeUndefined; + expect(meta.icon).toBeUndefined; + expect(meta.getMetadata().title).toBe('容器'); + expect(meta.title).toEqual({ type: 'i18n', 'en-US': 'Div', 'zh-CN': '容器' }); + + meta.setNpm({ package: '@ali/vc-div', componentName: 'Div' }); + expect(meta.npm).toEqual({ package: '@ali/vc-div', componentName: 'Div' }); + + meta.setMetadata(divMeta); + }); + + it('availableActions', () => { + const meta = new ComponentMeta(designer, divMeta); + expect(meta.availableActions).toHaveLength(3); + expect(meta.availableActions[0].name).toBe('remove'); + expect(meta.availableActions[1].name).toBe('hide'); + expect(meta.availableActions[2].name).toBe('copy'); + + removeBuiltinComponentAction('remove'); + // availableActions 有 computed 缓存 + expect(meta.availableActions[0].name).toBe('remove'); + expect(meta.availableActions[1].name).toBe('hide'); + expect(meta.availableActions[2].name).toBe('copy'); + + addBuiltinComponentAction({ + name: 'new', + content: { + action() {} + } + }); + // availableActions 有 computed 缓存 + expect(meta.availableActions).toHaveLength(3); + expect(meta.availableActions[0].name).toBe('remove'); + expect(meta.availableActions[1].name).toBe('hide'); + expect(meta.availableActions[2].name).toBe('copy'); }); }); \ No newline at end of file diff --git a/packages/designer/tests/setting-entry/setting-prop-entry.test.ts b/packages/designer/tests/setting-entry/setting-prop-entry.test.ts new file mode 100644 index 000000000..1169dc70d --- /dev/null +++ b/packages/designer/tests/setting-entry/setting-prop-entry.test.ts @@ -0,0 +1,105 @@ +import set from 'lodash/set'; +import cloneDeep from 'lodash/clonedeep'; +import '../fixtures/window'; +import { Editor } from '@ali/lowcode-editor-core'; +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 settingSchema from '../fixtures/schema/setting'; +import divMeta from '../fixtures/prototype/div-meta'; +import { getIdsFromSchema, getNodeFromSchemaById } from '../utils'; + +const editor = new Editor(); + +describe('setting-prop-entry 测试', () => { + let designer: Designer; + beforeEach(() => { + designer = new Designer({ editor }); + }); + afterEach(() => { + designer._componentMetasMap.clear(); + designer = null; + }); + + describe('node 构造函数生成 settingEntry', () => { + it.only('常规方法测试', () => { + designer.createComponentMeta(divMeta); + designer.project.open(settingSchema); + const { currentDocument } = designer.project; + const divNode = currentDocument?.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.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(settingEntry.getProp('behavior').getValue()).toBeUndefined; + + behaviorProp.setValue('LARGE'); + expect(behaviorProp.getValue()).toBe('LARGE'); + behaviorProp.remove(); + expect(settingEntry.getProp('behavior').getValue()).toBeUndefined; + + 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.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', + }); + }); + }); +}); \ No newline at end of file diff --git a/packages/designer/tests/setting-entry/setting-top-entry.test.ts b/packages/designer/tests/setting-entry/setting-top-entry.test.ts new file mode 100644 index 000000000..435993926 --- /dev/null +++ b/packages/designer/tests/setting-entry/setting-top-entry.test.ts @@ -0,0 +1,174 @@ +import set from 'lodash/set'; +import cloneDeep from 'lodash/clonedeep'; +import '../fixtures/window'; +import { Editor } from '@ali/lowcode-editor-core'; +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 settingSchema from '../fixtures/schema/setting'; +import divMeta from '../fixtures/prototype/div-meta'; +import { getIdsFromSchema, getNodeFromSchemaById } from '../utils'; + +const editor = new Editor(); + +describe('setting-top-entry 测试', () => { + let designer: Designer; + beforeEach(() => { + designer = new Designer({ editor }); + }); + 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('清理方法测试', () => { + 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 强行调用,是否需要做健壮性保护? + }); + }); +}); \ No newline at end of file diff --git a/packages/editor-preset-vision/jest.config.js b/packages/editor-preset-vision/jest.config.js index 2cbe0c3fe..5b9f59ab5 100644 --- a/packages/editor-preset-vision/jest.config.js +++ b/packages/editor-preset-vision/jest.config.js @@ -17,6 +17,9 @@ module.exports = { collectCoverage: false, collectCoverageFrom: [ 'src/**/*.{ts,tsx}', + '!src/**/*.d.ts', + '!src/base/**', + '!src/prop.ts', '!**/node_modules/**', '!**/vendor/**', ], diff --git a/packages/editor-preset-vision/src/index.ts b/packages/editor-preset-vision/src/index.ts index 47a684711..2bade9959 100644 --- a/packages/editor-preset-vision/src/index.ts +++ b/packages/editor-preset-vision/src/index.ts @@ -5,6 +5,7 @@ import logger from '@ali/vu-logger'; import { render } from 'react-dom'; import I18nUtil from './i18n-util'; import { hotkey as Hotkey, monitor } from '@ali/lowcode-editor-core'; +import { registerMetadataTransducer } from '@ali/lowcode-designer'; import { createElement } from 'react'; import { VE_EVENTS as EVENTS, VE_HOOKS as HOOKS, VERSION as Version } from './base/const'; import Bus from './bus'; @@ -110,6 +111,7 @@ const VisualEngine = { Project, logger, Symbols, + registerMetadataTransducer, // Flags, }; @@ -160,6 +162,7 @@ export { Project, logger, Symbols, + registerMetadataTransducer, }; const version = '6.0.0 (LowcodeEngine 0.9.32)'; diff --git a/packages/editor-preset-vision/src/props-reducers/init-node-reducer.ts b/packages/editor-preset-vision/src/props-reducers/init-node-reducer.ts index 72e7f4e5d..031482f46 100644 --- a/packages/editor-preset-vision/src/props-reducers/init-node-reducer.ts +++ b/packages/editor-preset-vision/src/props-reducers/init-node-reducer.ts @@ -9,7 +9,6 @@ import { isJSExpression, isJSBlock, isJSSlot } from '@ali/lowcode-types'; import { isVariable, getCurrentFieldIds } from '../utils'; export function initNodeReducer(props, node) { - // debugger; // run initials const newProps: any = { ...props, @@ -23,6 +22,7 @@ export function initNodeReducer(props, node) { } } const initials = node.componentMeta.getMetadata().experimental?.initials; + if (initials) { const getRealValue = (propValue: any) => { if (isVariable(propValue)) { diff --git a/packages/editor-preset-vision/tests/bundle/bundle.test.ts b/packages/editor-preset-vision/tests/bundle/bundle.test.ts new file mode 100644 index 000000000..31174d481 --- /dev/null +++ b/packages/editor-preset-vision/tests/bundle/bundle.test.ts @@ -0,0 +1,116 @@ +import { Component } from 'react'; +import set from 'lodash/set'; +import cloneDeep from 'lodash/clonedeep'; +import '../fixtures/window'; +import divPrototypeConfig from '../fixtures/prototype/div-vision'; +import trunk from '../../src/bundle/trunk'; +import Prototype from '../../src/bundle/prototype'; +import Bundle from '../../src/bundle/bundle'; +import { Editor } from '@ali/lowcode-editor-core'; + +jest.mock('../../src/bundle/trunk', () => { + // mockComponentPrototype = jest.fn(); + // return { + // mockComponentPrototype: jest.fn().mockImplementation(() => { + // return proto; + // }), + // } + // return jest.fn().mockImplementation(() => { + // return {playSoundFile: fakePlaySoundFile}; + // }); + // return jest.fn().mockImplementation(() => { + // return { mockComponentPrototype }; + // }); + return { + __esModule: true, + default: { + mockComponentPrototype: jest.fn(), + }, + }; +}); + +function wrap(name, thing) { + return { + name, + componentName: name, + category: '布局', + module: thing, + }; +} + +const proto1 = new Prototype(divPrototypeConfig); +const protoConfig2 = cloneDeep(divPrototypeConfig); +set(protoConfig2, 'componentName', 'Div2'); +const proto2 = new Prototype(protoConfig2); + +const protoConfig3 = cloneDeep(divPrototypeConfig); +set(protoConfig3, 'componentName', 'Div3'); +const proto3 = new Prototype(protoConfig3); + +const protoConfig4 = cloneDeep(divPrototypeConfig); +set(protoConfig4, 'componentName', 'Div4'); +const proto4 = new Prototype(protoConfig4); + +const protoConfig5 = cloneDeep(divPrototypeConfig); +set(protoConfig5, 'componentName', 'Div5'); +const proto5 = new Prototype(protoConfig5); + +function getComponentProtos() { + return [ + wrap('Div', proto1), + // wrap('Div2', proto2), + // wrap('Div3', proto3), + wrap('DivPortal', [proto2, proto3]), + ]; +} + +class Div extends Component {} +Div.displayName = 'Div'; +class Div2 extends Component {} +Div2.displayName = 'Div2'; +class Div3 extends Component {} +Div3.displayName = 'Div3'; +class Div4 extends Component {} +Div4.displayName = 'Div4'; +class Div5 extends Component {} +Div5.displayName = 'Div5'; + +function getComponentViews() { + return [ + wrap('Div', Div), + // wrap('Div2', Div2), + // wrap('Div3', Div3), + wrap('DivPortal', [Div2, Div3]), + ]; +} + +describe('Bundle', () => { + it('构造函数', () => { + const protos = getComponentProtos(); + const views = getComponentViews(); + const bundle = new Bundle(protos, views); + expect(bundle.getList()).toHaveLength(3); + expect(bundle.get('Div')).toBe(proto1); + expect(bundle.get('Div2')).toBe(proto2); + expect(bundle.get('Div3')).toBe(proto3); + bundle.addComponentBundle([proto4, Div4]); + expect(bundle.getList()).toHaveLength(4); + expect(bundle.get('Div4')).toBe(proto4); + bundle.replacePrototype('Div4', proto3); + expect(proto3.getView()).toBe(Div4); + + bundle.removeComponentBundle('Div2'); + expect(bundle.getList()).toHaveLength(3); + expect(bundle.get('Div2')).toBeUndefined; + + expect(bundle.getFromMeta('Div')).toBe(proto1); + bundle.getFromMeta('Div5'); + expect(bundle.getList()).toHaveLength(4); + }); + it('静态方法 create', () => { + const protos = getComponentProtos(); + const views = getComponentViews(); + const bundle = Bundle.create(protos, views); + expect(bundle).toBeTruthy(); + }); +}); diff --git a/packages/editor-preset-vision/tests/bundle/prototype.test.ts b/packages/editor-preset-vision/tests/bundle/prototype.test.ts index cc9667563..4776ebb1e 100644 --- a/packages/editor-preset-vision/tests/bundle/prototype.test.ts +++ b/packages/editor-preset-vision/tests/bundle/prototype.test.ts @@ -6,6 +6,7 @@ import '../fixtures/window'; // import { Node } from '../../src/document/node/node'; // import { Designer } from '../../src/designer/designer'; import divPrototypeConfig from '../fixtures/prototype/div-vision'; +import divFullPrototypeConfig from '../fixtures/prototype/div-vision-full'; import divPrototypeMeta from '../fixtures/prototype/div-meta'; // import VisualEngine from '../../src'; import { designer } from '../../src/editor'; @@ -29,6 +30,21 @@ describe('Prototype', () => { expect(proto.isContainer()).toBeTruthy; expect(proto.isModal()).toBeFalsy; }); + it('构造函数 - 全量 OldPrototypeConfig', () => { + const proto = new Prototype(divFullPrototypeConfig); + expect(isPrototype(proto)).toBeTruthy; + expect(proto.getComponentName()).toBe('Div'); + expect(proto.getId()).toBe('Div'); + expect(proto.getCategory()).toBe('布局'); + expect(proto.getDocUrl()).toBe( + 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md', + ); + expect(proto.getIcon()).toBeUndefined; + expect(proto.getTitle()).toBe('Div'); + expect(proto.isPrototype).toBeTruthy; + expect(proto.isContainer()).toBeTruthy; + expect(proto.isModal()).toBeFalsy; + }); it('构造函数 - ComponentMetadata', () => { const proto = new Prototype(divPrototypeMeta); expect(proto.getComponentName()).toBe('Div'); diff --git a/packages/editor-preset-vision/tests/bundle/trunk.test.ts b/packages/editor-preset-vision/tests/bundle/trunk.test.ts new file mode 100644 index 000000000..d4bca021f --- /dev/null +++ b/packages/editor-preset-vision/tests/bundle/trunk.test.ts @@ -0,0 +1,111 @@ +import { Component } from 'react'; +import set from 'lodash/set'; +import cloneDeep from 'lodash/clonedeep'; +import '../fixtures/window'; +import divPrototypeConfig from '../fixtures/prototype/div-vision'; +import Prototype from '../../src/bundle/prototype'; +import Bundle from '../../src/bundle/bundle'; +import trunk from '../../src/bundle/trunk'; +import lg from '@ali/vu-logger'; + +const proto1 = new Prototype(divPrototypeConfig); +const protoConfig2 = cloneDeep(divPrototypeConfig); +set(protoConfig2, 'componentName', 'Div2'); +const proto2 = new Prototype(protoConfig2); + +const protoConfig3 = cloneDeep(divPrototypeConfig); +set(protoConfig3, 'componentName', 'Div3'); +const proto3 = new Prototype(protoConfig3); + +const mockComponentPrototype = jest.fn(); +jest.mock('../../src/bundle/bundle', () => { + // return { + // mockComponentPrototype: jest.fn().mockImplementation(() => { + // return proto; + // }), + // } + // return jest.fn().mockImplementation(() => { + // return {playSoundFile: fakePlaySoundFile}; + // }); + return jest.fn().mockImplementation(() => { + return { + get: () => {}, + getList: () => { return []; }, + getFromMeta: () => {}, + }; + }); +}); + +const mockError = jest.fn(); +jest.mock('@ali/vu-logger'); +lg.error = mockError; + +function wrap(name, thing) { + return { + name, + componentName: name, + category: '布局', + module: thing, + }; +} + +function getComponentProtos() { + return [ + wrap('Div', proto1), + // wrap('Div2', proto2), + // wrap('Div3', proto3), + wrap('DivPortal', [proto2, proto3]), + ]; +} + +class Div extends Component {} +Div.displayName = 'Div'; +class Div2 extends Component {} +Div2.displayName = 'Div2'; +class Div3 extends Component {} +Div3.displayName = 'Div3'; + +function getComponentViews() { + return [ + wrap('Div', Div), + // wrap('Div2', Div2), + // wrap('Div3', Div3), + wrap('DivPortal', [Div2, Div3]), + ]; +} + +describe('Trunk', () => { + it('构造函数', () => { + const warn = console.warn = jest.fn(); + const trunkChangeHandler = jest.fn(); + const off = trunk.onTrunkChange(trunkChangeHandler); + trunk.addBundle(new Bundle([proto1], [Div])); + trunk.addBundle(new Bundle([proto2], [Div2])); + expect(trunkChangeHandler).toHaveBeenCalledTimes(2); + off(); + trunk.addBundle(new Bundle([proto3], [Div3])); + expect(trunkChangeHandler).toHaveBeenCalledTimes(2); + trunk.getList(); + trunk.getPrototype('Div'); + trunk.getPrototypeById('Div'); + trunk.getPrototypeView('Div'); + trunk.listByCategory(); + expect(trunk.mockComponentPrototype(new Bundle([proto3], [Div3]))).toBeUndefined; + expect(mockError).toHaveBeenCalled(); + trunk.registerComponentPrototypeMocker({ mockPrototype: jest.fn().mockImplementation(() => { return proto3; }) }); + expect(trunk.mockComponentPrototype(new Bundle([proto3], [Div3]))).toBe(proto3); + const hahaSetter = () => 'haha'; + trunk.registerSetter('haha', hahaSetter); + expect(trunk.getSetter('haha')).toBe(hahaSetter); + trunk.getRecents(5); + trunk.setPackages(); + expect(warn).toHaveBeenCalledTimes(1); + trunk.beforeLoadBundle(); + expect(warn).toHaveBeenCalledTimes(2); + trunk.afterLoadBundle(); + expect(warn).toHaveBeenCalledTimes(3); + trunk.getBundle(); + expect(warn).toHaveBeenCalledTimes(4); + expect(trunk.isReady()).toBeTruthy; + }); +}); diff --git a/packages/editor-preset-vision/tests/fixtures/prototype/div-vision-full.ts b/packages/editor-preset-vision/tests/fixtures/prototype/div-vision-full.ts new file mode 100644 index 000000000..756c37649 --- /dev/null +++ b/packages/editor-preset-vision/tests/fixtures/prototype/div-vision-full.ts @@ -0,0 +1,293 @@ +export default { + title: '容器', + componentName: 'Div', + docUrl: 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md', + category: '布局', + isContainer: true, + canOperating: false, + extraActions: [], + canContain: 'Form', + canDropTo: 'Div', + canDropIn: 'Div', + canResizing: true, + canDraging: false, + context: {}, + initialChildren() {}, + didDropIn() {}, + didDropOut() {}, + subtreeModified() {}, + onResize() {}, + onResizeStart() {}, + onResizeEnd() {}, + canUseCondition: true, + canLoop: true, + snippets: [ + { + screenshot: 'https://img.alicdn.com/tfs/TB1CHN3u4z1gK0jSZSgXXavwpXa-112-64.png', + label: '普通型', + schema: { + componentName: 'Div', + props: {}, + }, + }, + ], + configure: [ + { + name: 'myName', + title: '我的名字', + display: 'tab', + initialValue: 'NORMAL', + defaultValue: 'NORMAL', + collapsed: true, + supportVariable: true, + accessor(field, val) {}, + mutator(field, val) {}, + disabled() { + return true; + }, + useVariableChange() {}, + allowTextInput: true, + liveTextEditing: true, + setter: [ + { + key: null, + ref: null, + props: { + options: [ + { + title: '普通', + value: 'NORMAL', + }, + { + title: '隐藏', + value: 'HIDDEN', + }, + ], + loose: false, + cancelable: false, + }, + _owner: null, + }, + { + key: null, + ref: null, + props: { + options: [ + { + title: '普通', + value: 'NORMAL', + }, + { + title: '隐藏', + value: 'HIDDEN', + }, + ], + loose: false, + cancelable: false, + }, + _owner: null, + }, + ], + }, + { + name: 'mySlotName', + slotName: 'mySlotName', + slotTitle: '我的 Slot 名字', + display: 'tab', + initialValue: 'NORMAL', + defaultValue: 'NORMAL', + collapsed: true, + supportVariable: true, + accessor(field, val) {}, + mutator(field, val) {}, + disabled() { + return true; + }, + setter: { + key: null, + ref: null, + props: { + options: [ + { + title: '普通', + value: 'NORMAL', + }, + { + title: '隐藏', + value: 'HIDDEN', + }, + ], + loose: false, + cancelable: false, + }, + _owner: null, + }, + }, + { + name: 'behavior', + title: '默认状态', + display: 'inline', + initialValue: 'NORMAL', + supportVariable: true, + setter: { + key: null, + ref: null, + props: { + options: [ + { + title: '普通', + value: 'NORMAL', + }, + { + title: '隐藏', + value: 'HIDDEN', + }, + ], + loose: false, + cancelable: false, + }, + _owner: null, + }, + }, + { + name: '__style__', + title: '样式设置', + display: 'accordion', + collapsed: false, + initialValue: {}, + tip: { + url: 'https://lark.alipay.com/legao/help/design-tool-style', + content: '点击 ? 查看样式设置器用法指南', + }, + setter: { + key: null, + ref: null, + props: { + advanced: true, + }, + _owner: null, + }, + }, + { + type: 'group', + title: '高级', + display: 'accordion', + items: [ + { + name: 'fieldId', + title: '唯一标识', + display: 'block', + tip: + '组件的唯一标识符,不能够与其它组件重名,不能够为空,且只能够使用以字母开头的,下划线以及数字的组合。', + setter: { + key: null, + ref: null, + props: { + placeholder: '请输入唯一标识', + multiline: false, + rows: 10, + required: false, + pattern: null, + maxLength: null, + }, + _owner: null, + }, + }, + { + name: 'useFieldIdAsDomId', + title: '将唯一标识用作 DOM ID', + display: 'block', + tip: + '开启这个配置项后,会在当前组件的 HTML 元素上加入 id="当前组件的 fieldId",一般用于做 utils 的绑定,不常用', + initialValue: false, + setter: { + key: null, + ref: null, + props: {}, + _owner: null, + }, + }, + { + name: 'customClassName', + title: '自定义样式类', + display: 'block', + supportVariable: true, + initialValue: '', + setter: { + key: null, + ref: null, + props: { + placeholder: null, + multiline: false, + rows: 10, + required: false, + pattern: null, + maxLength: null, + }, + _owner: null, + }, + }, + { + name: 'events', + title: '动作设置', + tip: { + url: 'https://lark.alipay.com/legao/legao/events-call', + content: '点击 ? 查看如何设置组件的事件响应动作', + }, + display: 'accordion', + initialValue: { + 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, + }, + }, + { + name: 'onClick', + display: 'none', + initialValue: { + ignored: true, + }, + }, + { + name: 'onMouseEnter', + display: 'none', + initialValue: { + ignored: true, + }, + }, + { + name: 'onMouseLeave', + display: 'none', + initialValue: { + ignored: true, + }, + }, + ], + }, + ], +}; diff --git a/packages/editor-preset-vision/tests/fixtures/schema/form.ts b/packages/editor-preset-vision/tests/fixtures/schema/form.ts index 905b89561..5492a9ffb 100644 --- a/packages/editor-preset-vision/tests/fixtures/schema/form.ts +++ b/packages/editor-preset-vision/tests/fixtures/schema/form.ts @@ -89,7 +89,7 @@ export default { children: [ { componentName: 'Form', - id: 'node_k1ow3cbq', + id: 'form', props: { size: 'medium', labelAlign: 'top', diff --git a/packages/editor-preset-vision/tests/props-reducer/init-node.test.ts b/packages/editor-preset-vision/tests/props-reducer/init-node.test.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-preset-vision/tests/vision-api/exchange.test.ts b/packages/editor-preset-vision/tests/vision-api/exchange.test.ts new file mode 100644 index 000000000..972f49541 --- /dev/null +++ b/packages/editor-preset-vision/tests/vision-api/exchange.test.ts @@ -0,0 +1,18 @@ +import set from 'lodash/set'; +import cloneDeep from 'lodash/clonedeep'; +import '../fixtures/window'; +import formSchema from '../fixtures/schema/form'; +import VisualEngine from '../../src'; + +describe('VisualEngine.Exchange 相关 API 测试', () => { + it('select / getSelected', () => { + const doc = VisualEngine.Pages.addPage(formSchema); + VisualEngine.Exchange.select(doc?.getNode('form')); + expect(VisualEngine.Exchange.getSelected()?.componentName).toBe('Form'); + expect(VisualEngine.Exchange.getSelected()?.id).toBe('form'); + }); + + it('onIntoView', () => { + expect(typeof VisualEngine.Exchange.onIntoView).toBe('function'); + }); +}); diff --git a/packages/editor-preset-vision/tests/vision-api/pages.test.ts b/packages/editor-preset-vision/tests/vision-api/pages.test.ts index 259cbef63..a6567bd24 100644 --- a/packages/editor-preset-vision/tests/vision-api/pages.test.ts +++ b/packages/editor-preset-vision/tests/vision-api/pages.test.ts @@ -2,9 +2,10 @@ import set from 'lodash/set'; import cloneDeep from 'lodash/clonedeep'; import '../fixtures/window'; import formSchema from '../fixtures/schema/form'; -import VisualEngine from '../../src'; +import VisualEngine, { Prototype } from '../../src'; import { Editor } from '@ali/lowcode-editor-core'; import { getIdsFromSchema, getNodeFromSchemaById } from '../utils'; +import divPrototypeConfig from '../fixtures/prototype/div-vision'; const pageSchema = { componentsTree: [formSchema] }; @@ -62,6 +63,14 @@ describe('VisualEngine.Pages 相关 API 测试', () => { // slot 会多出(1 + N)个节点 expect(doc.nodesMap.size).toBe(expectedNodeCnt + 2); }); + it.only('基本的节点模型初始化,初始化传入 schema,构造 prototype', () => { + const proto = new Prototype(divPrototypeConfig); + const doc = VisualEngine.Pages.addPage(pageSchema)!; + expect(doc).toBeTruthy(); + const ids = getIdsFromSchema(formSchema); + const expectedNodeCnt = ids.length; + expect(doc.nodesMap.size).toBe(expectedNodeCnt); + }); it('导出 schema', () => { const doc = VisualEngine.Pages.addPage(pageSchema)!; expect(doc).toBeTruthy(); diff --git a/packages/editor-preset-vision/tests/vision-api/project.test.ts b/packages/editor-preset-vision/tests/vision-api/project.test.ts index 6e07cfb02..6bb7cc566 100644 --- a/packages/editor-preset-vision/tests/vision-api/project.test.ts +++ b/packages/editor-preset-vision/tests/vision-api/project.test.ts @@ -1,38 +1,26 @@ 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 VisualEngine from '../../src'; -import { Editor } from '@ali/lowcode-editor-core'; - -// import { getIdsFromSchema, getNodeFromSchemaById } from '../utils'; - +import { Project } from '../../src'; describe.skip('VisualEngine.Project 相关 API 测试', () => { - describe('getSchema / setSchema 系列', () => { - it('基本的节点模型初始化,模型导出,初始化传入 schema', () => { - // console.log(VisualEngine); - // console.log(Editor instanceof Function); - // console.log(Editor.toString()) - // console.log(new Editor()); - // console.log(Editor2 instanceof Function); - - console.log(VisualEngine.Pages.addPage(formSchema)); + it('getSchema / setSchema 系列', () => { + Project.setSchema({ + componentsMap: {}, + componentsTree: [formSchema], }); + expect(Project.getSchema()).toEqual({ + componentsMap: {}, + componentsTree: [formSchema], + }); + }); - describe('setConfig 系列', () => { - it('基本的节点模型初始化,模型导出,初始化传入 schema', () => { - // console.log(VisualEngine); - // console.log(Editor instanceof Function); - // console.log(Editor.toString()) - // console.log(new Editor()); - // console.log(Editor2 instanceof Function); - - console.log(VisualEngine.Pages.addPage(formSchema)); + it('setConfig', () => { + Project.setConfig({ haha: 1 }); + expect(Project.get('config')).toEqual({ + haha: 1, }); }); -}); \ No newline at end of file +}); diff --git a/packages/editor-skeleton/.eslintrc.js b/packages/editor-skeleton/.eslintrc.js index 27e1e0d4f..c650ed99f 100644 --- a/packages/editor-skeleton/.eslintrc.js +++ b/packages/editor-skeleton/.eslintrc.js @@ -12,5 +12,6 @@ module.exports = { 'no-prototype-builtins': 1, 'no-confusing-arrow': 1, 'no-case-declarations': 1, + 'lines-between-class-members': 0, } } \ No newline at end of file diff --git a/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx b/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx index 69ba92a2f..6527cc690 100644 --- a/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx +++ b/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx @@ -11,7 +11,7 @@ import { createIcon } from '@ali/lowcode-utils'; @observer export class SettingsPrimaryPane extends Component<{ editor: Editor; config: any }, { shouldIgnoreRoot: boolean }> { state = { - shouldIgnoreRoot: false, + shouldIgnoreRoot: false, }; private main = new SettingsMain(this.props.editor); @@ -24,12 +24,12 @@ export class SettingsPrimaryPane extends Component<{ editor: Editor; config: any componentDidMount() { this.setShouldIgnoreRoot(); } - + async setShouldIgnoreRoot() { - let designMode = await this.props.editor.get('designMode'); + const designMode = await this.props.editor.get('designMode'); this.setState({ shouldIgnoreRoot: designMode === 'live', - }) + }); } componentWillUnmount() {