mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-03-07 10:57:19 +00:00
Merge branch 'release/1.0.26' into release/0.9.54
This commit is contained in:
commit
031c89b81d
34
CHANGELOG.md
34
CHANGELOG.md
@ -3,6 +3,40 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="1.0.26-beta.0"></a>
|
||||
## [1.0.26-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.25-beta.1...v1.0.26-beta.0) (2020-12-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* rax perf ([3abe2ab](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/3abe2ab))
|
||||
* requestHandlersMap 没有加到 appContext 里 ([a8d43c3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a8d43c3))
|
||||
* simulator-renderer 补充丢失代码 ([67dd7e2](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/67dd7e2))
|
||||
* 传递正确的 removeIndex 给到 subtreeModified 钩子 ([822b2fd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/822b2fd))
|
||||
* 修复 overridePropsConfigure 参数为数组时的逻辑 ([4e58e09](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/4e58e09))
|
||||
* 修复组件不会插入到选中节点之内或者之后的逻辑 ([93b005b](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/93b005b))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 支持 build sourceMap, 方便用户调试 ([6bf75cd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6bf75cd))
|
||||
* 支持用户修改 builtinComponentActions ([bc183d1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/bc183d1))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.25-beta.1"></a>
|
||||
## [1.0.25-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.4...v1.0.25-beta.1) (2020-12-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复 prop 无法删除最后一个 item ([e18a386](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/e18a386))
|
||||
* 修复大纲树和组件面板来回点击异常 ([8b9a6ec](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/8b9a6ec))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.24-beta.4"></a>
|
||||
## [1.0.24-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.3...v1.0.24-beta.4) (2020-12-14)
|
||||
|
||||
|
||||
10
lerna.json
10
lerna.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"lerna": "2.11.0",
|
||||
"version": "1.0.24-beta.4",
|
||||
"version": "1.0.26-beta.0",
|
||||
"npmClient": "tyarn",
|
||||
"registry": "http://registry.npm.alibaba-inc.com",
|
||||
"useWorkspaces": true,
|
||||
@ -13,6 +13,14 @@
|
||||
"--no-package-lock"
|
||||
]
|
||||
},
|
||||
"version": {
|
||||
"allowBranch": [
|
||||
"master",
|
||||
"main",
|
||||
"release/*",
|
||||
"daily/*"
|
||||
]
|
||||
},
|
||||
"publish": {
|
||||
"npmClient": "tnpm",
|
||||
"verifyRegistry": false,
|
||||
|
||||
@ -15,7 +15,8 @@
|
||||
"lint": "eslint --ext .ts,.tsx,.js,.jsx ./ --quiet",
|
||||
"lint:fix": "eslint --ext .ts,.tsx,.js,.jsx ./ --quiet --fix",
|
||||
"pub": "lerna publish --force-publish --cd-version patch --message \"chore(release): publish %v\"",
|
||||
"pubbeta": "lerna publish --force-publish --cd-version prerelease --npm-tag beta --preid beta --message \"chore(release): publish %v\"",
|
||||
"pub:prerelease": "lerna publish --force-publish --cd-version prerelease --npm-tag beta --preid beta --message \"chore(release): publish %v\"",
|
||||
"pub:prepatch": "lerna publish --force-publish --cd-version prepatch --npm-tag beta --preid beta --message \"chore(release): publish %v\"",
|
||||
"setup": "./scripts/setup.sh",
|
||||
"start": "./scripts/start.sh",
|
||||
"start:server": "./scripts/start-server.sh",
|
||||
|
||||
@ -3,6 +3,36 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="1.0.26-beta.0"></a>
|
||||
## [1.0.26-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.25-beta.1...v1.0.26-beta.0) (2020-12-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 传递正确的 removeIndex 给到 subtreeModified 钩子 ([822b2fd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/822b2fd))
|
||||
* 修复 overridePropsConfigure 参数为数组时的逻辑 ([4e58e09](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/4e58e09))
|
||||
* 修复组件不会插入到选中节点之内或者之后的逻辑 ([93b005b](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/93b005b))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 支持 build sourceMap, 方便用户调试 ([6bf75cd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6bf75cd))
|
||||
* 支持用户修改 builtinComponentActions ([bc183d1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/bc183d1))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.25-beta.1"></a>
|
||||
## [1.0.25-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.4...v1.0.25-beta.1) (2020-12-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复 prop 无法删除最后一个 item ([e18a386](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/e18a386))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.24-beta.4"></a>
|
||||
## [1.0.24-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.3...v1.0.24-beta.4) (2020-12-14)
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ module.exports = {
|
||||
'!src/**/*.d.ts',
|
||||
'!src/icons/**',
|
||||
'!src/locale/**',
|
||||
'!src/builtin-simulator/utils/**',
|
||||
'!src/document/node/exclusive-group.ts',
|
||||
'!**/node_modules/**',
|
||||
'!**/vendor/**',
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-designer",
|
||||
"version": "1.0.24-beta.4",
|
||||
"version": "1.0.26-beta.0",
|
||||
"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": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-types": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-utils": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-editor-core": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-types": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-utils": "^1.0.26-beta.0",
|
||||
"classnames": "^2.2.6",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-adapter-react-16": "^1.15.5",
|
||||
@ -27,6 +27,7 @@
|
||||
"devDependencies": {
|
||||
"@ali/lowcode-test-mate": "^1.0.1",
|
||||
"@alib/build-scripts": "^0.1.29",
|
||||
"@testing-library/react": "^11.2.2",
|
||||
"@types/classnames": "^2.2.7",
|
||||
"@types/jest": "^26.0.16",
|
||||
"@types/lodash": "^4.14.165",
|
||||
|
||||
@ -602,7 +602,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
|
||||
node?.componentMeta?.componentName ||
|
||||
'';
|
||||
editor?.emit('desiger.builtinSimulator.contextmenu', {
|
||||
editor?.emit('designer.builtinSimulator.contextmenu', {
|
||||
selected,
|
||||
});
|
||||
});
|
||||
@ -817,6 +817,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
/**
|
||||
* @see ISimulator
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
scrollToNode(node: Node, detail?: any/* , tryTimes = 0 */) {
|
||||
this.tryScrollAgain = null;
|
||||
if (this.sensing) {
|
||||
|
||||
@ -103,7 +103,7 @@ LowcodeTypes.exact = (typesMap: any) => {
|
||||
const configs = Object.keys(typesMap).map(key => {
|
||||
return {
|
||||
name: key,
|
||||
propType: typesMap[key].lowcodeType || 'any',
|
||||
propType: typesMap[key]?.lowcodeType || 'any',
|
||||
};
|
||||
});
|
||||
return define(PropTypes.exact(typesMap), {
|
||||
@ -117,7 +117,7 @@ LowcodeTypes.shape = (typesMap: any) => {
|
||||
const configs = Object.keys(typesMap).map(key => {
|
||||
return {
|
||||
name: key,
|
||||
propType: typesMap[key].lowcodeType || 'any',
|
||||
propType: typesMap[key]?.lowcodeType || 'any',
|
||||
};
|
||||
});
|
||||
return define(PropTypes.shape(typesMap), {
|
||||
|
||||
@ -19,8 +19,7 @@ export default class Viewport implements IViewport {
|
||||
}
|
||||
|
||||
get contentBounds(): DOMRect {
|
||||
const { bounds } = this;
|
||||
const { scale } = this;
|
||||
const { bounds, scale } = this;
|
||||
return new DOMRect(0, 0, bounds.width / scale, bounds.height / scale);
|
||||
}
|
||||
|
||||
|
||||
@ -178,10 +178,10 @@ export class ComponentMeta {
|
||||
this._title =
|
||||
typeof title === 'string'
|
||||
? {
|
||||
type: 'i18n',
|
||||
'en-US': this.componentName,
|
||||
'zh-CN': title,
|
||||
}
|
||||
type: 'i18n',
|
||||
'en-US': this.componentName,
|
||||
'zh-CN': title,
|
||||
}
|
||||
: title;
|
||||
}
|
||||
|
||||
@ -243,14 +243,22 @@ export class ComponentMeta {
|
||||
}
|
||||
|
||||
isRootComponent(includeBlock = true) {
|
||||
return this.componentName === 'Page' || this.componentName === 'Component' || (includeBlock && this.componentName === 'Block');
|
||||
return (
|
||||
this.componentName === 'Page' ||
|
||||
this.componentName === 'Component' ||
|
||||
(includeBlock && this.componentName === 'Block')
|
||||
);
|
||||
}
|
||||
|
||||
@computed get availableActions() {
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { disableBehaviors, actions } = this._transformedMetadata?.configure.component || {};
|
||||
const disabled = ensureAList(disableBehaviors) || (this.isRootComponent(false) ? ['copy', 'remove'] : null);
|
||||
actions = builtinComponentActions.concat(this.designer.getGlobalComponentActions() || [], actions || []);
|
||||
const disabled =
|
||||
ensureAList(disableBehaviors) || (this.isRootComponent(false) ? ['copy', 'remove'] : null);
|
||||
actions = builtinComponentActions.concat(
|
||||
this.designer.getGlobalComponentActions() || [],
|
||||
actions || [],
|
||||
);
|
||||
|
||||
if (disabled) {
|
||||
if (disabled.includes('*')) {
|
||||
@ -331,7 +339,11 @@ export interface MetadataTransducer {
|
||||
}
|
||||
const metadataTransducers: MetadataTransducer[] = [];
|
||||
|
||||
export function registerMetadataTransducer(transducer: MetadataTransducer, level = 100, id?: string) {
|
||||
export function registerMetadataTransducer(
|
||||
transducer: MetadataTransducer,
|
||||
level = 100,
|
||||
id?: string,
|
||||
) {
|
||||
transducer.level = level;
|
||||
transducer.id = id;
|
||||
const i = metadataTransducers.findIndex((item) => item.level != null && item.level > level);
|
||||
@ -360,14 +372,14 @@ registerMetadataTransducer((metadata) => {
|
||||
childWhitelist: [`${m[1]}`],
|
||||
};
|
||||
}
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
} else if ((m = /^(.+)\.Node$/.exec(componentName))) {
|
||||
// uri match xx.Node set selfControlled: false, parentWhiteList
|
||||
// component.selfControlled = false;
|
||||
component.nestingRule = {
|
||||
parentWhitelist: [`${m[1]}`, componentName],
|
||||
};
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
} else if ((m = /^(.+)\.(Item|Node|Option)$/.exec(componentName))) {
|
||||
// uri match .Item .Node .Option set parentWhiteList
|
||||
component.nestingRule = {
|
||||
@ -440,3 +452,13 @@ export function removeBuiltinComponentAction(name: string) {
|
||||
export function addBuiltinComponentAction(action: ComponentAction) {
|
||||
builtinComponentActions.push(action);
|
||||
}
|
||||
|
||||
export function modifyBuiltinComponentAction(
|
||||
actionName,
|
||||
handle: (action: ComponentAction) => void,
|
||||
) {
|
||||
const builtinAction = builtinComponentActions.find((action) => action.name === actionName);
|
||||
if (builtinAction) {
|
||||
handle(builtinAction);
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +31,15 @@ export class ActiveTracker {
|
||||
return this._target?.detail;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
get intance() {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
get instance() {
|
||||
return this._target?.instance;
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,6 @@ function isInLiveEditing() {
|
||||
if (globalContext.has(Editor)) {
|
||||
return Boolean(globalContext.get(Editor).get('designer')?.project?.simulator?.liveEditing?.editing);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getNextForSelect(next: any, head?: any, parent?: any): any {
|
||||
|
||||
@ -138,7 +138,7 @@ export class Designer {
|
||||
parent?.componentMeta?.componentName ||
|
||||
'';
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
this.editor?.emit('designer.drag', {
|
||||
this.postEvent('drag', {
|
||||
time: (endTime - startTime).toFixed(2),
|
||||
selected: nodes
|
||||
?.map((n) => {
|
||||
@ -193,7 +193,7 @@ export class Designer {
|
||||
this.setupSelection();
|
||||
setupHistory();
|
||||
});
|
||||
this.postEvent('designer.init', this);
|
||||
this.postEvent('init', this);
|
||||
this.setupSelection();
|
||||
setupHistory();
|
||||
|
||||
|
||||
@ -179,7 +179,7 @@ function makeEventsHandler(
|
||||
}
|
||||
|
||||
function isDragEvent(e: any): e is DragEvent {
|
||||
return e?.type?.substr(0, 4) === 'drag';
|
||||
return e?.type?.startsWith('drag');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -245,7 +245,7 @@ export class Dragon {
|
||||
const handleEvents = makeEventsHandler(boostEvent, masterSensors);
|
||||
const newBie = !isDragNodeObject(dragObject);
|
||||
const forceCopyState = isDragNodeObject(dragObject) && dragObject.nodes.some((node) => node.isSlot());
|
||||
const isBoostFromDragAPI = boostEvent.type.substr(0, 4) === 'drag';
|
||||
const isBoostFromDragAPI = isDragEvent(boostEvent);
|
||||
let lastSensor: ISensor | undefined;
|
||||
|
||||
this._dragging = false;
|
||||
@ -259,6 +259,7 @@ export class Dragon {
|
||||
|
||||
let copy = false;
|
||||
const checkcopy = (e: MouseEvent | DragEvent | KeyboardEvent) => {
|
||||
/* istanbul ignore next */
|
||||
if (isDragEvent(e) && e.dataTransfer) {
|
||||
if (newBie || forceCopyState) {
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
@ -272,6 +273,7 @@ export class Dragon {
|
||||
if (e.altKey || e.ctrlKey) {
|
||||
copy = true;
|
||||
this.setCopyState(true);
|
||||
/* istanbul ignore next */
|
||||
if (isDragEvent(e) && e.dataTransfer) {
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
@ -279,6 +281,7 @@ export class Dragon {
|
||||
copy = false;
|
||||
if (!forceCopyState) {
|
||||
this.setCopyState(false);
|
||||
/* istanbul ignore next */
|
||||
if (isDragEvent(e) && e.dataTransfer) {
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
}
|
||||
@ -332,6 +335,7 @@ export class Dragon {
|
||||
|
||||
// route: drag-move
|
||||
const move = (e: MouseEvent | DragEvent) => {
|
||||
/* istanbul ignore next */
|
||||
if (isBoostFromDragAPI) {
|
||||
e.preventDefault();
|
||||
}
|
||||
@ -350,6 +354,7 @@ export class Dragon {
|
||||
};
|
||||
|
||||
let didDrop = true;
|
||||
/* istanbul ignore next */
|
||||
const drop = (e: DragEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
@ -358,12 +363,14 @@ export class Dragon {
|
||||
|
||||
// end-tail drag process
|
||||
const over = (e?: any) => {
|
||||
/* istanbul ignore next */
|
||||
if (e && isDragEvent(e)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
if (lastSensor) {
|
||||
lastSensor.deactiveSensor();
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
if (isBoostFromDragAPI) {
|
||||
if (!didDrop) {
|
||||
designer.clearLocation();
|
||||
@ -385,6 +392,7 @@ export class Dragon {
|
||||
designer.clearLocation();
|
||||
|
||||
handleEvents((doc) => {
|
||||
/* istanbul ignore next */
|
||||
if (isBoostFromDragAPI) {
|
||||
doc.removeEventListener('dragover', move, true);
|
||||
doc.removeEventListener('dragend', over, true);
|
||||
@ -418,7 +426,7 @@ export class Dragon {
|
||||
if (!sourceDocument || sourceDocument === document) {
|
||||
evt.globalX = e.clientX;
|
||||
evt.globalY = e.clientY;
|
||||
} else {
|
||||
} /* istanbul ignore next */ else {
|
||||
// event from simulator sandbox
|
||||
let srcSim: ISimulatorHost | undefined;
|
||||
const lastSim = lastSensor && isSimulatorHost(lastSensor) ? lastSensor : null;
|
||||
@ -449,6 +457,7 @@ export class Dragon {
|
||||
};
|
||||
|
||||
const sourceSensor = getSourceSensor(dragObject);
|
||||
/* istanbul ignore next */
|
||||
const chooseSensor = (e: LocateEvent) => {
|
||||
// this.sensors will change on dragstart
|
||||
const sensors: ISensor[] = (masterSensors as ISensor[]).concat(this.sensors);
|
||||
@ -477,6 +486,7 @@ export class Dragon {
|
||||
return sensor;
|
||||
};
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (isDragEvent(boostEvent)) {
|
||||
const { dataTransfer } = boostEvent;
|
||||
|
||||
@ -496,6 +506,7 @@ export class Dragon {
|
||||
}
|
||||
|
||||
handleEvents((doc) => {
|
||||
/* istanbul ignore next */
|
||||
if (isBoostFromDragAPI) {
|
||||
doc.addEventListener('dragover', move, true);
|
||||
// dragexit
|
||||
|
||||
@ -18,18 +18,18 @@ export class ScrollTarget {
|
||||
}
|
||||
|
||||
get scrollHeight(): number {
|
||||
return ((this.doe || this.target) as any).scrollHeight;
|
||||
return ((this.doc || this.target) as any).scrollHeight;
|
||||
}
|
||||
|
||||
get scrollWidth(): number {
|
||||
return ((this.doe || this.target) as any).scrollWidth;
|
||||
return ((this.doc || this.target) as any).scrollWidth;
|
||||
}
|
||||
|
||||
private doe?: HTMLElement;
|
||||
private doc?: HTMLElement;
|
||||
|
||||
constructor(private target: Window | Element) {
|
||||
if (isWindow(target)) {
|
||||
this.doe = target.document.documentElement;
|
||||
this.doc = target.document.documentElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,7 +86,7 @@ export class SettingPropEntry implements SettingEntry {
|
||||
}
|
||||
const propName = this.path.join('.');
|
||||
let l = this.nodes.length;
|
||||
while (l-- > 1) {
|
||||
while (l-- > 0) {
|
||||
this.nodes[l].getProp(propName, true)!.key = key;
|
||||
}
|
||||
this._name = key;
|
||||
|
||||
@ -128,6 +128,8 @@ export class NodeChildren {
|
||||
slotNode.remove(useMutator, purge);
|
||||
}, (iterable, idx) => (iterable as [])[idx]);
|
||||
}
|
||||
// 需要在从 children 中删除 node 前记录下 index,internalSetParent 中会执行删除(unlink)操作
|
||||
const i = this.children.indexOf(node);
|
||||
if (purge) {
|
||||
// should set parent null
|
||||
node.internalSetParent(null, useMutator);
|
||||
@ -142,14 +144,13 @@ export class NodeChildren {
|
||||
document.selection.remove(node.id);
|
||||
document.destroyNode(node);
|
||||
this.emitter.emit('change');
|
||||
const i = this.children.indexOf(node);
|
||||
if (useMutator) {
|
||||
this.reportModified(node, this.owner, { type: 'remove', removeIndex: i, removeNode: node });
|
||||
}
|
||||
if (i < 0) {
|
||||
return false;
|
||||
// purge 为 true 时,已在 internalSetParent 中删除了子节点
|
||||
if (i > -1 && !purge) {
|
||||
this.children.splice(i, 1);
|
||||
}
|
||||
this.children.splice(i, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -937,7 +937,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
||||
}
|
||||
|
||||
if (this.parent) {
|
||||
return this.parent.getSuitablePlace(node, ref);
|
||||
return this.parent.getSuitablePlace(node, { index: this.index });
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@ -15,10 +15,14 @@ export function getConvertedExtraKey(key: string): string {
|
||||
if (key.indexOf('.') > 0) {
|
||||
_key = key.split('.')[0];
|
||||
}
|
||||
return EXTRA_KEY_PREFIX + _key + EXTRA_KEY_PREFIX + key.substr(_key.length);
|
||||
return EXTRA_KEY_PREFIX + _key + EXTRA_KEY_PREFIX + key.substr(_key.length + 1);
|
||||
}
|
||||
export function getOriginalExtraKey(key: string): string {
|
||||
return key.replace(new RegExp(`${EXTRA_KEY_PREFIX}`, 'g'), '');
|
||||
// 移除串首、串尾的 EXTRA_KEY_PREFIX,将剩下的转成 .
|
||||
return key
|
||||
.replace(new RegExp(`^${EXTRA_KEY_PREFIX}`), '')
|
||||
.replace(new RegExp(`${EXTRA_KEY_PREFIX}$`), '')
|
||||
.replace(new RegExp(`${EXTRA_KEY_PREFIX}`, 'g'), '.');
|
||||
}
|
||||
|
||||
export class Props implements IPropParent {
|
||||
|
||||
@ -0,0 +1,131 @@
|
||||
import '../../fixtures/window';
|
||||
import { set } from '../../utils';
|
||||
import { Editor, globalContext } from '@ali/lowcode-editor-core';
|
||||
import { Project } from '../../../src/project/project';
|
||||
import { DocumentModel } from '../../../src/document/document-model';
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import DragResizeEngine from '../../../src/builtin-simulator/bem-tools/drag-resize-engine';
|
||||
import formSchema from '../../fixtures/schema/form';
|
||||
import divMetadata from '../../fixtures/component-metadata/div';
|
||||
import formMetadata from '../../fixtures/component-metadata/form';
|
||||
import otherMeta from '../../fixtures/component-metadata/other';
|
||||
import pageMetadata from '../../fixtures/component-metadata/page';
|
||||
import { fireEvent, createEvent } from '@testing-library/react';
|
||||
import { create } from 'lodash';
|
||||
|
||||
describe('DragResizeEngine 测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
let resizeEngine: DragResizeEngine;
|
||||
|
||||
beforeAll(() => {
|
||||
editor = new Editor();
|
||||
!globalContext.has(Editor) && globalContext.register(editor, Editor);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
designer = new Designer({ editor });
|
||||
project = designer.project;
|
||||
doc = project.createDocument(formSchema);
|
||||
doc.open();
|
||||
resizeEngine = new DragResizeEngine(designer);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
project.mountSimulator(undefined);
|
||||
designer.purge();
|
||||
resizeEngine = null;
|
||||
designer = null;
|
||||
project = null;
|
||||
});
|
||||
|
||||
it('from', () => {
|
||||
const resizeStartMockFn = jest.fn();
|
||||
const resizeMockFn = jest.fn();
|
||||
const resizeEndMockFn = jest.fn();
|
||||
|
||||
const offResizeStart = resizeEngine.onResizeStart(resizeStartMockFn);
|
||||
const offResize = resizeEngine.onResize(resizeMockFn);
|
||||
const offResizeEnd = resizeEngine.onResizeEnd(resizeEndMockFn);
|
||||
const boostedNode = doc.getNode('node_k1ow3cbn');
|
||||
const mockedBoostFn = jest
|
||||
.fn((e) => {
|
||||
return boostedNode;
|
||||
});
|
||||
|
||||
// do nothing
|
||||
resizeEngine.from();
|
||||
|
||||
const offFrom = resizeEngine.from(document, 'e', mockedBoostFn);
|
||||
|
||||
const mouseDownEvt = createEvent.mouseDown(document, { clientX: 100, clientY: 100 });
|
||||
fireEvent(document, mouseDownEvt);
|
||||
|
||||
expect(resizeStartMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(resizeStartMockFn.mock.calls[0][0]).toBe(mouseDownEvt);
|
||||
expect(resizeStartMockFn.mock.calls[0][1]).toBe('e');
|
||||
expect(resizeStartMockFn.mock.calls[0][2]).toBe(boostedNode);
|
||||
expect(resizeEngine.isDragResizing()).toBeTruthy();
|
||||
|
||||
const mouseMoveEvt1 = createEvent.mouseMove(document, { clientX: 108, clientY: 108 });
|
||||
fireEvent(document, mouseMoveEvt1);
|
||||
expect(resizeMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(resizeMockFn.mock.calls[0][0]).toBe(mouseMoveEvt1);
|
||||
expect(resizeMockFn.mock.calls[0][1]).toBe('e');
|
||||
expect(resizeMockFn.mock.calls[0][2]).toBe(boostedNode);
|
||||
expect(resizeMockFn.mock.calls[0][3]).toBe(8);
|
||||
expect(resizeMockFn.mock.calls[0][4]).toBe(8);
|
||||
|
||||
const mouseMoveEvt2 = createEvent.mouseMove(document, { clientX: 110, clientY: 110 }, 10, 10);
|
||||
fireEvent(document, mouseMoveEvt2);
|
||||
expect(resizeMockFn).toHaveBeenCalledTimes(2);
|
||||
expect(resizeMockFn.mock.calls[1][0]).toBe(mouseMoveEvt2);
|
||||
expect(resizeMockFn.mock.calls[1][1]).toBe('e');
|
||||
expect(resizeMockFn.mock.calls[1][2]).toBe(boostedNode);
|
||||
expect(resizeMockFn.mock.calls[1][3]).toBe(10);
|
||||
expect(resizeMockFn.mock.calls[1][4]).toBe(10);
|
||||
|
||||
const mouseUpEvt = createEvent.mouseUp(document, { clientX: 118, clientY: 118 });
|
||||
fireEvent(document, mouseUpEvt);
|
||||
|
||||
expect(resizeEndMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(resizeEndMockFn.mock.calls[0][0]).toBe(mouseUpEvt);
|
||||
expect(resizeEndMockFn.mock.calls[0][1]).toBe('e');
|
||||
expect(resizeEndMockFn.mock.calls[0][2]).toBe(boostedNode);
|
||||
expect(resizeEngine.isDragResizing()).toBeFalsy();
|
||||
|
||||
offResizeStart();
|
||||
offResize();
|
||||
offResizeEnd();
|
||||
resizeStartMockFn.mockClear();
|
||||
resizeMockFn.mockClear();
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 100, clientY: 100 });
|
||||
expect(resizeMockFn).not.toHaveBeenCalled();
|
||||
|
||||
offFrom();
|
||||
fireEvent.mouseDown(document, { clientX: 100, clientY: 100 });
|
||||
expect(resizeStartMockFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('has sensor', () => {
|
||||
const mockedDoc = document.createElement('iframe').contentWindow?.document;
|
||||
project.mountSimulator({
|
||||
sensorAvailable: true,
|
||||
contentDocument: document,
|
||||
});
|
||||
|
||||
const mockedBoostFn = jest
|
||||
.fn((e) => {
|
||||
return doc.getNode('node_k1ow3cbn');
|
||||
});
|
||||
|
||||
const offFrom = resizeEngine.from(document, 'e', mockedBoostFn);
|
||||
|
||||
// TODO: 想办法 mock 一个 iframe.currentDocument
|
||||
fireEvent.mouseDown(document, { clientX: 100, clientY: 100 });
|
||||
});
|
||||
});
|
||||
487
packages/designer/tests/builtin-simulator/host.test.ts
Normal file
487
packages/designer/tests/builtin-simulator/host.test.ts
Normal file
@ -0,0 +1,487 @@
|
||||
import React from 'react';
|
||||
import set from 'lodash/set';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import '../fixtures/window';
|
||||
import { Editor, globalContext } from '@ali/lowcode-editor-core';
|
||||
import {
|
||||
AssetLevel,
|
||||
Asset,
|
||||
AssetList,
|
||||
assetBundle,
|
||||
assetItem,
|
||||
AssetType,
|
||||
} from '@ali/lowcode-utils';
|
||||
import {
|
||||
Dragon,
|
||||
isDragNodeObject,
|
||||
isDragNodeDataObject,
|
||||
isDragAnyObject,
|
||||
isLocateEvent,
|
||||
DragObjectType,
|
||||
isShaken,
|
||||
setShaken,
|
||||
} from '../../src/designer/dragon';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { Node } from '../../src/document/node/node';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import { DocumentModel } from '../../src/document/document-model';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import { getMockDocument, getMockWindow, getMockEvent, delayObxTick } from '../utils';
|
||||
import { BuiltinSimulatorHost } from '../../src/builtin-simulator/host';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
|
||||
describe('Host 测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
let host: BuiltinSimulatorHost;
|
||||
|
||||
beforeAll(() => {
|
||||
editor = new Editor();
|
||||
!globalContext.has(Editor) && globalContext.register(editor, Editor);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
designer = new Designer({ editor });
|
||||
project = designer.project;
|
||||
doc = project.createDocument(formSchema);
|
||||
host = new BuiltinSimulatorHost(designer.project);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
project.mountSimulator(undefined);
|
||||
designer._componentMetasMap.clear();
|
||||
designer.purge();
|
||||
host.purge();
|
||||
designer = null;
|
||||
project = null;
|
||||
host = null;
|
||||
});
|
||||
|
||||
describe('基础方法测试', () => {
|
||||
it('setProps / get / set', async () => {
|
||||
expect(host.currentDocument).toBe(designer.project.currentDocument);
|
||||
expect(host.renderEnv).toBe('default');
|
||||
expect(host.device).toBe('default');
|
||||
expect(host.deviceClassName).toBeUndefined();
|
||||
expect(host.requestHandlersMap).toBeNull();
|
||||
host.setProps({
|
||||
renderEnv: 'rax',
|
||||
device: 'mobile',
|
||||
deviceClassName: 'mobile-rocks',
|
||||
componentsAsset: [
|
||||
{
|
||||
type: AssetType.JSText,
|
||||
content: 'console.log(1)',
|
||||
},
|
||||
{
|
||||
type: AssetType.JSUrl,
|
||||
content: '//path/to/js',
|
||||
},
|
||||
],
|
||||
theme: {
|
||||
type: AssetType.CSSText,
|
||||
content: '.theme {font-size: 50px;}',
|
||||
},
|
||||
requestHandlersMap: {},
|
||||
});
|
||||
expect(host.renderEnv).toBe('rax');
|
||||
expect(host.device).toBe('mobile');
|
||||
expect(host.deviceClassName).toBe('mobile-rocks');
|
||||
expect(host.componentsAsset).toEqual([
|
||||
{
|
||||
type: AssetType.JSText,
|
||||
content: 'console.log(1)',
|
||||
},
|
||||
{
|
||||
type: AssetType.JSUrl,
|
||||
content: '//path/to/js',
|
||||
},
|
||||
]);
|
||||
expect(host.theme).toEqual({
|
||||
type: AssetType.CSSText,
|
||||
content: '.theme {font-size: 50px;}',
|
||||
});
|
||||
expect(host.componentsMap).toBe(designer.componentsMap);
|
||||
expect(host.requestHandlersMap).toEqual({});
|
||||
|
||||
host.set('renderEnv', 'vue');
|
||||
expect(host.renderEnv).toBe('vue');
|
||||
|
||||
expect(host.getComponentContext).toThrow('Method not implemented.');
|
||||
});
|
||||
|
||||
it('connect', () => {
|
||||
const mockFn = jest.fn();
|
||||
const mockRenderer = { isSimulatorRenderer: true };
|
||||
host.connect(mockRenderer, mockFn);
|
||||
expect(host.renderer).toEqual(mockRenderer);
|
||||
|
||||
// await delayObxTick();
|
||||
expect(mockFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('mountViewport', () => {
|
||||
const mockBounds = {
|
||||
top: 10,
|
||||
bottom: 100,
|
||||
left: 10,
|
||||
right: 100,
|
||||
};
|
||||
host.mountViewport({
|
||||
getBoundingClientRect() {
|
||||
return mockBounds;
|
||||
},
|
||||
});
|
||||
expect(host.viewport.bounds).toEqual(mockBounds);
|
||||
});
|
||||
|
||||
it('autorun', () => {
|
||||
const mockFn = jest.fn();
|
||||
host.autorun(mockFn);
|
||||
expect(mockFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('purge', () => {
|
||||
host.purge();
|
||||
});
|
||||
|
||||
it('isEnter', () => {
|
||||
const mockBounds = {
|
||||
top: 10,
|
||||
bottom: 100,
|
||||
left: 10,
|
||||
right: 100,
|
||||
};
|
||||
host.mountViewport({
|
||||
getBoundingClientRect() {
|
||||
return mockBounds;
|
||||
},
|
||||
});
|
||||
expect(
|
||||
host.isEnter({
|
||||
globalX: 5,
|
||||
globalY: 50,
|
||||
}),
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
host.isEnter({
|
||||
globalX: 115,
|
||||
globalY: 50,
|
||||
}),
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
host.isEnter({
|
||||
globalX: 50,
|
||||
globalY: 50,
|
||||
}),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
host.isEnter({
|
||||
globalX: 50,
|
||||
globalY: 5,
|
||||
}),
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
host.isEnter({
|
||||
globalX: 50,
|
||||
globalY: 150,
|
||||
}),
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
host.isEnter({
|
||||
globalX: 150,
|
||||
globalY: 150,
|
||||
}),
|
||||
).toBeFalsy();
|
||||
});
|
||||
|
||||
it('fixEvent', () => {
|
||||
expect(host.fixEvent({ fixed: true, clientX: 1 })).toEqual({ fixed: true, clientX: 1 });
|
||||
|
||||
});
|
||||
|
||||
it('findDOMNodes', () => {
|
||||
host.connect({
|
||||
findDOMNodes: () => {
|
||||
return null;
|
||||
}
|
||||
}, () => {});
|
||||
expect(host.findDOMNodes()).toBeNull();
|
||||
|
||||
const mockElems = [document.createElement('div')]
|
||||
host.connect({
|
||||
findDOMNodes: () => {
|
||||
return mockElems;
|
||||
}
|
||||
}, () => {});
|
||||
expect(host.findDOMNodes({})).toBe(mockElems);
|
||||
expect(host.findDOMNodes({}, 'xxx')).toBeNull();
|
||||
expect(host.findDOMNodes({}, 'div')).toEqual(mockElems);
|
||||
});
|
||||
|
||||
it('getClosestNodeInstance', () => {
|
||||
const mockFn = jest.fn(() => {
|
||||
return {
|
||||
node: {},
|
||||
nodeId: 'id',
|
||||
docId: 'docId',
|
||||
};
|
||||
});
|
||||
host.connect({
|
||||
getClosestNodeInstance: mockFn
|
||||
}, () => {});
|
||||
expect(host.getClosestNodeInstance()).toEqual({
|
||||
node: {},
|
||||
nodeId: 'id',
|
||||
docId: 'docId',
|
||||
});
|
||||
});
|
||||
|
||||
it('getNodeInstanceFromElement', () => {
|
||||
expect(host.getNodeInstanceFromElement()).toBeNull();
|
||||
host.getClosestNodeInstance = () => {
|
||||
return null;
|
||||
}
|
||||
expect(host.getNodeInstanceFromElement({})).toBeNull();
|
||||
host.getClosestNodeInstance = () => {
|
||||
return {
|
||||
docId: project.currentDocument.id,
|
||||
nodeId: 'xxx'
|
||||
};
|
||||
}
|
||||
expect(host.getNodeInstanceFromElement({})).toBeTruthy();
|
||||
});
|
||||
|
||||
it('getDropContainer', () => {
|
||||
host.getNodeInstanceFromElement = () => {
|
||||
return {
|
||||
node: doc.rootNode,
|
||||
}
|
||||
}
|
||||
host.getDropContainer({
|
||||
target: {},
|
||||
dragObject: {
|
||||
type: DragObjectType.Node,
|
||||
nodes: [doc.getNode('page')],
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
it('getComponentInstances', () => {
|
||||
const mockNode = {
|
||||
document: { id: 'docId' }
|
||||
};
|
||||
host.instancesMap = {
|
||||
'docId': {
|
||||
get() {
|
||||
return [{ comp: true }, { comp2: true }];
|
||||
}
|
||||
}
|
||||
}
|
||||
expect(host.getComponentInstances(mockNode))
|
||||
.toEqual([{ comp: true }, { comp2: true }]);
|
||||
|
||||
const mockInst = { inst: true };
|
||||
host.getClosestNodeInstance = () => {
|
||||
return {
|
||||
instance: mockInst,
|
||||
}
|
||||
}
|
||||
expect(host.getComponentInstances(mockNode, { instance: mockInst }))
|
||||
.toEqual([{ comp: true }, { comp2: true }]);
|
||||
});
|
||||
|
||||
it('setNativeSelection / setDraggingState / setCopyState / clearState', () => {
|
||||
const mockFn1 = jest.fn();
|
||||
const mockFn2 = jest.fn();
|
||||
const mockFn3 = jest.fn();
|
||||
const mockFn4 = jest.fn();
|
||||
host.connect({
|
||||
setNativeSelection: mockFn1,
|
||||
setDraggingState: mockFn2,
|
||||
setCopyState: mockFn3,
|
||||
clearState: mockFn4,
|
||||
}, () => {});
|
||||
host.setNativeSelection(true);
|
||||
expect(mockFn1).toHaveBeenCalledWith(true);
|
||||
host.setDraggingState(false);
|
||||
expect(mockFn2).toHaveBeenCalledWith(false);
|
||||
host.setCopyState(true);
|
||||
expect(mockFn3).toHaveBeenCalledWith(true);
|
||||
host.clearState();
|
||||
expect(mockFn4).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('sensorAvailable / deactiveSensor', () => {
|
||||
expect(host.sensorAvailable).toBeTruthy();
|
||||
host.deactiveSensor();
|
||||
expect(host.sensing).toBeFalsy();
|
||||
})
|
||||
|
||||
it('getComponent', () => {
|
||||
host.connect({
|
||||
getComponent: () => {
|
||||
return {};
|
||||
}
|
||||
}, () => {});
|
||||
expect(host.getComponent()).toEqual({});
|
||||
expect(host.createComponent()).toBeNull();
|
||||
expect(host.setSuspense()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('setInstance', () => {
|
||||
host.instancesMap = {};
|
||||
host.setInstance('docId1', 'id1', [{}]);
|
||||
expect(host.instancesMap['docId1'].get('id1')).toEqual([{}]);
|
||||
|
||||
host.setInstance('docId1', 'id1', null);
|
||||
expect(host.instancesMap['docId1'].get('id1')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('locate 方法', () => {
|
||||
beforeEach(() => {
|
||||
const mockBounds = {
|
||||
top: 10,
|
||||
bottom: 100,
|
||||
left: 10,
|
||||
right: 100,
|
||||
};
|
||||
host.mountViewport({
|
||||
getBoundingClientRect() {
|
||||
return mockBounds;
|
||||
},
|
||||
});
|
||||
});
|
||||
it('locate,没有 nodes', () => {
|
||||
expect(host.locate({
|
||||
dragObject: {
|
||||
type: DragObjectType.Node,
|
||||
nodes: [],
|
||||
},
|
||||
})).toBeUndefined();
|
||||
});
|
||||
it('locate,没有 document', () => {
|
||||
project.removeDocument(doc);
|
||||
expect(host.locate({
|
||||
dragObject: {
|
||||
type: DragObjectType.Node,
|
||||
nodes: [doc.getNode('page')],
|
||||
},
|
||||
})).toBeNull();
|
||||
});
|
||||
it('locate', () => {
|
||||
host.locate({
|
||||
dragObject: {
|
||||
type: DragObjectType.Node,
|
||||
nodes: [doc.getNode('page')],
|
||||
},
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe('事件测试', () => {
|
||||
it('setupDragAndClick', () => {});
|
||||
it('setupContextMenu', async () => {
|
||||
const mockDocument = getMockDocument();
|
||||
const mockWindow = getMockWindow(mockDocument);
|
||||
const mockIframe = {
|
||||
contentWindow: mockWindow,
|
||||
contentDocument: mockDocument,
|
||||
dispatchEvent() {},
|
||||
};
|
||||
|
||||
host.set('library', [
|
||||
{
|
||||
package: '@ali/vc-deep',
|
||||
library: 'lib',
|
||||
urls: ['a.js', 'b.js'],
|
||||
},
|
||||
]);
|
||||
|
||||
host.componentsConsumer.consume(() => {});
|
||||
host.injectionConsumer.consume(() => {});
|
||||
await host.mountContentFrame(mockIframe);
|
||||
|
||||
host.setupContextMenu();
|
||||
host.getNodeInstanceFromElement = () => {
|
||||
return {
|
||||
node: { componentMeta: { componentName: 'Button' }},
|
||||
};
|
||||
}
|
||||
const mockFn = jest.fn();
|
||||
host.designer.editor.on('designer.builtinSimulator.contextmenu', mockFn);
|
||||
fireEvent.contextMenu(document, {});
|
||||
// TODO:
|
||||
// expect(mockFn).toHaveBeenCalledWith({ selected: 'Button' });
|
||||
});
|
||||
});
|
||||
|
||||
it('事件测试', async () => {
|
||||
const mockDocument = getMockDocument();
|
||||
const mockWindow = getMockWindow(mockDocument);
|
||||
const mockIframe = {
|
||||
contentWindow: mockWindow,
|
||||
contentDocument: mockDocument,
|
||||
dispatchEvent() {},
|
||||
};
|
||||
|
||||
// 非法分支测试
|
||||
host.mountContentFrame();
|
||||
expect(host._iframe).toBeUndefined();
|
||||
|
||||
host.set('library', [
|
||||
{
|
||||
package: '@ali/vc-deep',
|
||||
library: 'lib',
|
||||
urls: ['a.js', 'b.js'],
|
||||
},
|
||||
]);
|
||||
|
||||
host.componentsConsumer.consume(() => {});
|
||||
host.injectionConsumer.consume(() => {});
|
||||
await host.mountContentFrame(mockIframe);
|
||||
|
||||
expect(host.contentWindow).toBe(mockWindow);
|
||||
|
||||
mockDocument.triggerEventListener(
|
||||
'mouseover',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'mouseleave',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'mousedown',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'mouseup',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'mousemove',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener('click', getMockEvent(document.createElement('input')), host);
|
||||
mockDocument.triggerEventListener(
|
||||
'dblclick',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'contextmenu',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1,145 +0,0 @@
|
||||
import React from 'react';
|
||||
import set from 'lodash/set';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import '../fixtures/window';
|
||||
import { Editor } from '@ali/lowcode-editor-core';
|
||||
import {
|
||||
AssetLevel,
|
||||
Asset,
|
||||
AssetList,
|
||||
assetBundle,
|
||||
assetItem,
|
||||
AssetType,
|
||||
} from '@ali/lowcode-utils';
|
||||
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 { getMockDocument, getMockWindow, getMockEvent } from '../utils';
|
||||
import { BuiltinSimulatorHost } from '../../src/builtin-simulator/host';
|
||||
import { eq } from 'lodash';
|
||||
|
||||
const editor = new Editor();
|
||||
|
||||
describe('host 测试', () => {
|
||||
let designer: Designer;
|
||||
beforeEach(() => {
|
||||
designer = new Designer({ editor });
|
||||
});
|
||||
afterEach(() => {
|
||||
designer._componentMetasMap.clear();
|
||||
designer = null;
|
||||
});
|
||||
|
||||
it('基础方法测试', async () => {
|
||||
const host = new BuiltinSimulatorHost(designer.project);
|
||||
expect(host.currentDocument).toBe(designer.project.currentDocument);
|
||||
expect(host.renderEnv).toBe('default');
|
||||
expect(host.device).toBe('default');
|
||||
expect(host.deviceClassName).toBeUndefined();
|
||||
host.setProps({
|
||||
renderEnv: 'rax',
|
||||
device: 'mobile',
|
||||
deviceClassName: 'mobile-rocks',
|
||||
componentsAsset: [{
|
||||
type: AssetType.JSText,
|
||||
content: 'console.log(1)',
|
||||
}, {
|
||||
type: AssetType.JSUrl,
|
||||
content: '//path/to/js',
|
||||
}],
|
||||
theme: {
|
||||
type: AssetType.CSSText,
|
||||
content: '.theme {font-size: 50px;}',
|
||||
}
|
||||
});
|
||||
expect(host.renderEnv).toBe('rax');
|
||||
expect(host.device).toBe('mobile');
|
||||
expect(host.deviceClassName).toBe('mobile-rocks');
|
||||
expect(host.componentsAsset).toEqual([{
|
||||
type: AssetType.JSText,
|
||||
content: 'console.log(1)',
|
||||
}, {
|
||||
type: AssetType.JSUrl,
|
||||
content: '//path/to/js',
|
||||
}]);
|
||||
expect(host.theme).toEqual({
|
||||
type: AssetType.CSSText,
|
||||
content: '.theme {font-size: 50px;}',
|
||||
});
|
||||
expect(host.componentsMap).toBe(designer.componentsMap);
|
||||
|
||||
host.set('renderEnv', 'vue');
|
||||
expect(host.renderEnv).toBe('vue');
|
||||
|
||||
expect(host.getComponentContext).toThrow('Method not implemented.');
|
||||
});
|
||||
|
||||
it('事件测试', async () => {
|
||||
const host = new BuiltinSimulatorHost(designer.project);
|
||||
const mockDocument = getMockDocument();
|
||||
const mockWindow = getMockWindow(mockDocument);
|
||||
const mockIframe = {
|
||||
contentWindow: mockWindow,
|
||||
contentDocument: mockDocument,
|
||||
dispatchEvent() {},
|
||||
};
|
||||
|
||||
// 非法分支测试
|
||||
host.mountContentFrame();
|
||||
expect(host._iframe).toBeUndefined();
|
||||
|
||||
host.set('library', [{
|
||||
package: '@ali/vc-deep',
|
||||
library: 'lib',
|
||||
urls: ['a.js', 'b.js']
|
||||
}]);
|
||||
|
||||
host.componentsConsumer.consume(() => {});
|
||||
host.injectionConsumer.consume(() => {});
|
||||
await host.mountContentFrame(mockIframe);
|
||||
|
||||
expect(host.contentWindow).toBe(mockWindow);
|
||||
|
||||
mockDocument.triggerEventListener(
|
||||
'mouseover',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'mouseleave',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'mousedown',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'mouseup',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'mousemove',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'click',
|
||||
getMockEvent(document.createElement('input')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'dblclick',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
mockDocument.triggerEventListener(
|
||||
'contextmenu',
|
||||
getMockEvent(mockDocument.createElement('div')),
|
||||
host,
|
||||
);
|
||||
})
|
||||
});
|
||||
@ -0,0 +1,60 @@
|
||||
import ResourceConsumer from '../../src/builtin-simulator/resource-consumer';
|
||||
import { delayObxTick, delay } from '../utils';
|
||||
|
||||
it('ResourceConsumer 测试,先消费再监听', async () => {
|
||||
const con = new ResourceConsumer(() => ({ a: 1, b: 2}));
|
||||
|
||||
const mockFn = jest.fn();
|
||||
con.consume((data) => {
|
||||
mockFn(data);
|
||||
});
|
||||
|
||||
await delay(1000);
|
||||
|
||||
expect(mockFn).toHaveBeenCalledWith({ a: 1, b: 2 });
|
||||
con.consume(() => {});
|
||||
|
||||
await con.waitFirstConsume();
|
||||
|
||||
con.dispose();
|
||||
});
|
||||
|
||||
it('ResourceConsumer 测试,先消费再监听,isSimulatorRenderer', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const con = new ResourceConsumer(() => ({ a: 1, b: 2}), () => {
|
||||
const o = { a: 3, b: 4 };
|
||||
mockFn(o)
|
||||
return o;
|
||||
});
|
||||
|
||||
con.consume({ isSimulatorRenderer: true });
|
||||
|
||||
await delay(1000);
|
||||
|
||||
expect(mockFn).toHaveBeenCalledWith({ a: 3, b: 4 });
|
||||
con.consume(() => {});
|
||||
|
||||
await con.waitFirstConsume();
|
||||
});
|
||||
|
||||
it('ResourceConsumer 测试,先消费再监听,isSimulatorRenderer,没有 consume', async () => {
|
||||
const mockFn = jest.fn();
|
||||
const con = new ResourceConsumer(() => ({ a: 1, b: 2}));
|
||||
|
||||
con.consume({ isSimulatorRenderer: true });
|
||||
});
|
||||
|
||||
it('ResourceConsumer 测试,先监听再消费', async () => {
|
||||
const con = new ResourceConsumer(() => ({ a: 1, b: 2}));
|
||||
|
||||
con.waitFirstConsume();
|
||||
|
||||
const mockFn = jest.fn();
|
||||
con.consume((data) => {
|
||||
mockFn(data);
|
||||
});
|
||||
|
||||
await delay(1000);
|
||||
|
||||
expect(mockFn).toHaveBeenCalledWith({ a: 1, b: 2 });
|
||||
});
|
||||
@ -1,5 +1,5 @@
|
||||
import '../fixtures/window';
|
||||
import { parseMetadata } from '../../src/builtin-simulator/utils/parse-metadata';
|
||||
import '../../fixtures/window';
|
||||
import { parseMetadata } from '../../../src/builtin-simulator/utils/parse-metadata';
|
||||
|
||||
describe('parseMetadata', () => {
|
||||
it('parseMetadata', async () => {
|
||||
@ -7,7 +7,7 @@ import {
|
||||
removeVersion,
|
||||
resolveAbsoluatePath,
|
||||
joinPath,
|
||||
} from '../../src/builtin-simulator/utils/path';
|
||||
} from '../../../src/builtin-simulator/utils/path';
|
||||
|
||||
describe('builtin-simulator/utils/path 测试', () => {
|
||||
it('isPackagePath', () => {
|
||||
@ -1,5 +1,5 @@
|
||||
import '../fixtures/disable-raf';
|
||||
import { throttle } from '../../src/builtin-simulator/utils/throttle';
|
||||
import '../../fixtures/disable-raf';
|
||||
import { throttle } from '../../../src/builtin-simulator/utils/throttle';
|
||||
|
||||
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
180
packages/designer/tests/builtin-simulator/viewport.test.ts
Normal file
180
packages/designer/tests/builtin-simulator/viewport.test.ts
Normal file
@ -0,0 +1,180 @@
|
||||
import '../fixtures/window';
|
||||
import { getMockWindow, set } from '../utils';
|
||||
import { Editor, globalContext } from '@ali/lowcode-editor-core';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { DocumentModel } from '../../src/document/document-model';
|
||||
import Viewport from '../../src/builtin-simulator/viewport';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { getMockElement, delay } from '../utils';
|
||||
|
||||
describe('Viewport 测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
let viewport: Viewport;
|
||||
let viewportElem;
|
||||
|
||||
beforeAll(() => {
|
||||
editor = new Editor();
|
||||
!globalContext.has(Editor) && globalContext.register(editor, Editor);
|
||||
|
||||
window.DOMRect = class {
|
||||
constructor(top, left, width, height) {
|
||||
return { top, left, width, height };
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
designer = new Designer({ editor });
|
||||
project = designer.project;
|
||||
// doc = project.createDocument(formSchema);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
// project.mountSimulator(undefined);
|
||||
designer.purge();
|
||||
designer = null;
|
||||
project = null;
|
||||
viewport = null;
|
||||
});
|
||||
|
||||
it('基本函数测试', async () => {
|
||||
const rect = {
|
||||
width: 500,
|
||||
height: 500,
|
||||
top: 100,
|
||||
bottom: 500,
|
||||
left: 100,
|
||||
right: 500,
|
||||
};
|
||||
viewportElem = getMockElement('div', rect);
|
||||
viewport = new Viewport();
|
||||
viewport.mount();
|
||||
expect(viewport.viewportElement).toBeUndefined();
|
||||
expect(viewport.width).toBe(1000);
|
||||
expect(viewport.height).toBe(600);
|
||||
expect(viewport.toGlobalPoint({ left: 0, top: 0 })).toEqual({ left: 0, top: 0 });
|
||||
expect(viewport.toLocalPoint({ left: 0, top: 0 })).toEqual({ left: 0, top: 0 });
|
||||
|
||||
viewport.mount(viewportElem);
|
||||
expect(viewport.viewportElement).toBe(viewportElem);
|
||||
|
||||
expect(viewport.bounds).toEqual(rect);
|
||||
expect(viewport.contentBounds).toEqual({ top: 0, left: 0, width: 500, height: 500 });
|
||||
expect(viewport.rect).toEqual(rect);
|
||||
|
||||
expect(viewport.width).toBe(500);
|
||||
expect(viewport.contentWidth).toBe('100%');
|
||||
expect(viewport.height).toBe(500);
|
||||
expect(viewport.contentHeight).toBe('100%');
|
||||
|
||||
await delay(100);
|
||||
viewportElem.setWidth(300);
|
||||
viewport.width = 300;
|
||||
expect(viewport.width).toBe(300);
|
||||
|
||||
await delay(100);
|
||||
viewportElem.setHeight(300);
|
||||
viewport.height = 300;
|
||||
expect(viewport.height).toBe(300);
|
||||
|
||||
viewport.contentWidth = 200;
|
||||
expect(viewport.contentWidth).toBe(200);
|
||||
|
||||
viewport.contentHeight = 200;
|
||||
expect(viewport.contentHeight).toBe(200);
|
||||
});
|
||||
|
||||
it('scale', () => {
|
||||
const rect = {
|
||||
width: 500,
|
||||
height: 500,
|
||||
top: 100,
|
||||
bottom: 500,
|
||||
left: 100,
|
||||
right: 500,
|
||||
};
|
||||
viewportElem = getMockElement('div', rect);
|
||||
viewport = new Viewport();
|
||||
viewport.mount(viewportElem);
|
||||
|
||||
expect(viewport.scale).toBe(1);
|
||||
viewport.scale = 2;
|
||||
expect(viewport.scale).toBe(2);
|
||||
|
||||
expect(viewport.contentWidth).toBe(500 / 2);
|
||||
expect(viewport.contentHeight).toBe(500 / 2);
|
||||
|
||||
viewport.width = 300;
|
||||
viewportElem.setWidth(300);
|
||||
expect(viewport.contentWidth).toBe(300 / 2);
|
||||
|
||||
viewport.height = 300;
|
||||
viewportElem.setHeight(300);
|
||||
expect(viewport.contentHeight).toBe(300 / 2);
|
||||
|
||||
expect(() => viewport.scale = NaN).toThrow();
|
||||
expect(() => viewport.scale = -1).toThrow();
|
||||
});
|
||||
|
||||
it('setScrollTarget / scrollTarget / scrolling', async () => {
|
||||
const rect = {
|
||||
width: 500,
|
||||
height: 500,
|
||||
top: 100,
|
||||
bottom: 500,
|
||||
left: 100,
|
||||
right: 500,
|
||||
};
|
||||
viewportElem = getMockElement('div', rect);
|
||||
viewport = new Viewport();
|
||||
viewport.mount(viewportElem);
|
||||
|
||||
const mockWindow = getMockWindow();
|
||||
viewport.setScrollTarget(mockWindow);
|
||||
// TODO: 待 mock
|
||||
viewport.scrollTarget;
|
||||
// expect(viewport.scrollTarget).toBe(mockWindow);
|
||||
|
||||
// mock scrollTarget
|
||||
// viewport._scrollTarget = { left: 0, top: 0 };
|
||||
// viewport._scrollTarget.left = 123;
|
||||
// viewport._scrollTarget.top = 1234;
|
||||
mockWindow.triggerEventListener('scroll');
|
||||
expect(viewport.scrolling).toBeTruthy();
|
||||
// TODO: 待 mock
|
||||
viewport.scrollX;
|
||||
viewport.scrollY;
|
||||
// expect(viewport.scrollX).toBe(123);
|
||||
// expect(viewport.scrollY).toBe(1234);
|
||||
await delay(100);
|
||||
expect(viewport.scrolling).toBeFalsy();
|
||||
|
||||
mockWindow.triggerEventListener('resize');
|
||||
});
|
||||
|
||||
it('toGlobalPoint / toLocalPoint', () => {
|
||||
const rect = {
|
||||
width: 500,
|
||||
height: 500,
|
||||
top: 100,
|
||||
bottom: 500,
|
||||
left: 100,
|
||||
right: 500,
|
||||
};
|
||||
viewportElem = getMockElement('div', rect);
|
||||
viewport = new Viewport();
|
||||
viewport.mount(viewportElem);
|
||||
|
||||
expect(viewport.toGlobalPoint({ clientX: 100, clientY: 100 })).toEqual({ clientX: 200, clientY: 200 });
|
||||
expect(viewport.toLocalPoint({ clientX: 200, clientY: 200 })).toEqual({ clientX: 100, clientY: 100 });
|
||||
|
||||
viewport.scale = 2;
|
||||
expect(viewport.toGlobalPoint({ clientX: 100, clientY: 100 })).toEqual({ clientX: 300, clientY: 300 });
|
||||
expect(viewport.toLocalPoint({ clientX: 300, clientY: 300 })).toEqual({ clientX: 100, clientY: 100 });
|
||||
});
|
||||
});
|
||||
40
packages/designer/tests/designer/active-tracker.test.ts
Normal file
40
packages/designer/tests/designer/active-tracker.test.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { ActiveTracker } from '../../src/designer/active-tracker';
|
||||
|
||||
it('ActiveTracker 测试,Node', () => {
|
||||
const tracker = new ActiveTracker();
|
||||
|
||||
const mockFn = jest.fn();
|
||||
const mockNode = { isNode: true };
|
||||
const off = tracker.onChange(mockFn);
|
||||
|
||||
tracker.track(mockNode);
|
||||
expect(mockFn).toHaveBeenCalledWith({ node: mockNode });
|
||||
|
||||
expect(tracker.currentNode).toBe(mockNode);
|
||||
|
||||
off();
|
||||
mockFn.mockClear();
|
||||
tracker.track(mockNode);
|
||||
expect(mockFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ActiveTracker 测试,ActiveTarget', () => {
|
||||
const tracker = new ActiveTracker();
|
||||
|
||||
const mockFn = jest.fn();
|
||||
const mockNode = { isNode: true };
|
||||
const off = tracker.onChange(mockFn);
|
||||
const mockTarget = { node: mockNode, detail: { isDetail: true }, instance: { isInstance: true } };
|
||||
|
||||
tracker.track(mockTarget);
|
||||
expect(mockFn).toHaveBeenCalledWith(mockTarget);
|
||||
|
||||
expect(tracker.currentNode).toBe(mockNode);
|
||||
expect(tracker.detail).toEqual({ isDetail: true });
|
||||
expect(tracker.instance).toEqual({ isInstance: true });
|
||||
|
||||
off();
|
||||
mockFn.mockClear();
|
||||
tracker.track(mockNode);
|
||||
expect(mockFn).not.toHaveBeenCalled();
|
||||
});
|
||||
@ -6,6 +6,7 @@ import { Designer } from '../../src/designer/designer';
|
||||
import { Project } from '../../src/project/project';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import '../../src/designer/builtin-hotkey';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
|
||||
const editor = new Editor();
|
||||
|
||||
@ -28,8 +29,7 @@ describe('快捷键测试', () => {
|
||||
const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbj')!;
|
||||
firstCardNode.select();
|
||||
|
||||
let event = new KeyboardEvent('keydown', { keyCode: 39 });
|
||||
document.dispatchEvent(event);
|
||||
fireEvent.keyDown(document, { keyCode: 39 });
|
||||
|
||||
expect(designer.currentSelection?.selected.includes('node_k1ow3cbl')).toBeTruthy();
|
||||
});
|
||||
@ -38,8 +38,7 @@ describe('快捷键测试', () => {
|
||||
const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbl')!;
|
||||
firstCardNode.select();
|
||||
|
||||
let event = new KeyboardEvent('keydown', { keyCode: 37 });
|
||||
document.dispatchEvent(event);
|
||||
fireEvent.keyDown(document, { keyCode: 37 });
|
||||
|
||||
expect(designer.currentSelection?.selected.includes('node_k1ow3cbj')).toBeTruthy();
|
||||
});
|
||||
@ -48,8 +47,7 @@ describe('快捷键测试', () => {
|
||||
const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbl')!;
|
||||
firstCardNode.select();
|
||||
|
||||
let event = new KeyboardEvent('keydown', { keyCode: 40 });
|
||||
document.dispatchEvent(event);
|
||||
fireEvent.keyDown(document, { keyCode: 40 });
|
||||
|
||||
expect(designer.currentSelection?.selected.includes('node_k1ow3cbo')).toBeTruthy();
|
||||
});
|
||||
@ -58,8 +56,7 @@ describe('快捷键测试', () => {
|
||||
const secondCardNode = designer.currentDocument?.getNode('node_k1ow3cbm')!;
|
||||
secondCardNode.select();
|
||||
|
||||
let event = new KeyboardEvent('keydown', { keyCode: 38 });
|
||||
document.dispatchEvent(event);
|
||||
fireEvent.keyDown(document, { keyCode: 38 });
|
||||
|
||||
expect(designer.currentSelection?.selected.includes('node_k1ow3cbl')).toBeTruthy();
|
||||
});
|
||||
@ -69,8 +66,7 @@ describe('快捷键测试', () => {
|
||||
const firstButtonNode = designer.currentDocument?.getNode('node_k1ow3cbn')!;
|
||||
firstButtonNode.select();
|
||||
|
||||
let event = new KeyboardEvent('keydown', { keyCode: 39, altKey: true });
|
||||
document.dispatchEvent(event);
|
||||
fireEvent.keyDown(document, { keyCode: 39, altKey: true });
|
||||
|
||||
expect(firstButtonNode.prevSibling?.getId()).toBe('node_k1ow3cbp');
|
||||
});
|
||||
@ -80,8 +76,7 @@ describe('快捷键测试', () => {
|
||||
const secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
secondButtonNode.select();
|
||||
|
||||
let event = new KeyboardEvent('keydown', { keyCode: 37, altKey: true });
|
||||
document.dispatchEvent(event);
|
||||
fireEvent.keyDown(document, { keyCode: 37, altKey: true });
|
||||
|
||||
expect(secondButtonNode.nextSibling?.getId()).toBe('node_k1ow3cbn');
|
||||
});
|
||||
@ -91,8 +86,7 @@ describe('快捷键测试', () => {
|
||||
const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
firstCardNode.select();
|
||||
|
||||
let event = new KeyboardEvent('keydown', { keyCode: 38, altKey: true });
|
||||
document.dispatchEvent(event);
|
||||
fireEvent.keyDown(document, { keyCode: 38, altKey: true });
|
||||
});
|
||||
|
||||
// 将节点移入到兄弟节点中
|
||||
@ -100,8 +94,7 @@ describe('快捷键测试', () => {
|
||||
const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
firstCardNode.select();
|
||||
|
||||
let event = new KeyboardEvent('keydown', { keyCode: 40, altKey: true });
|
||||
document.dispatchEvent(event);
|
||||
fireEvent.keyDown(document, { keyCode: 40, altKey: true });
|
||||
});
|
||||
|
||||
// 撤销
|
||||
@ -114,8 +107,7 @@ describe('快捷键测试', () => {
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
let event = new KeyboardEvent('keydown', { keyCode: 90, metaKey: true });
|
||||
document.dispatchEvent(event);
|
||||
fireEvent.keyDown(document, { keyCode: 90, metaKey: true });
|
||||
|
||||
// 重新获取一次节点,因为 documentModel.import 是全画布刷新
|
||||
secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
@ -132,8 +124,7 @@ describe('快捷键测试', () => {
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
let event = new KeyboardEvent('keydown', { keyCode: 90, metaKey: true });
|
||||
document.dispatchEvent(event);
|
||||
fireEvent.keyDown(document, { keyCode: 90, metaKey: true });
|
||||
|
||||
// 重新获取一次节点,因为 documentModel.import 是全画布刷新
|
||||
secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
@ -141,8 +132,7 @@ describe('快捷键测试', () => {
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
event = new KeyboardEvent('keydown', { keyCode: 89, metaKey: true });
|
||||
document.dispatchEvent(event);
|
||||
fireEvent.keyDown(document, { keyCode: 89, metaKey: true });
|
||||
|
||||
// 重新获取一次节点,因为 documentModel.import 是全画布刷新
|
||||
secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
@ -153,19 +143,16 @@ describe('快捷键测试', () => {
|
||||
const firstCardNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
firstCardNode.select();
|
||||
|
||||
let event = new KeyboardEvent('keydown', { keyCode: 67, metaKey: true });
|
||||
document.dispatchEvent(event);
|
||||
fireEvent.keyDown(document, { keyCode: 67, metaKey: true });
|
||||
});
|
||||
|
||||
it('command + v', async () => {
|
||||
const secondButtonNode = designer.currentDocument?.getNode('node_k1ow3cbp')!;
|
||||
secondButtonNode.select();
|
||||
|
||||
let event = new KeyboardEvent('keydown', { keyCode: 67, metaKey: true });
|
||||
document.dispatchEvent(event);
|
||||
fireEvent.keyDown(document, { keyCode: 67, metaKey: true });
|
||||
|
||||
event = new KeyboardEvent('keydown', { keyCode: 86, metaKey: true });
|
||||
document.dispatchEvent(event);
|
||||
fireEvent.keyDown(document, { keyCode: 86, metaKey: true });
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
@ -180,8 +167,7 @@ describe('快捷键测试', () => {
|
||||
|
||||
expect(designer.currentSelection!.selected.includes('node_k1ow3cbp')).toBeTruthy();
|
||||
|
||||
let event = new KeyboardEvent('keydown', { keyCode: 27 });
|
||||
document.dispatchEvent(event);
|
||||
fireEvent.keyDown(document, { keyCode: 27 });
|
||||
|
||||
expect(designer.currentSelection!.selected.length).toBe(0);
|
||||
});
|
||||
@ -194,9 +180,109 @@ describe('快捷键测试', () => {
|
||||
|
||||
expect(secondButtonNode.prevSibling.id).toBe('node_k1ow3cbn');
|
||||
|
||||
let event = new KeyboardEvent('keydown', { keyCode: 46 });
|
||||
document.dispatchEvent(event);
|
||||
fireEvent.keyDown(document, { keyCode: 46 });
|
||||
|
||||
expect(secondButtonNode.prevSibling).toBeNull();
|
||||
});
|
||||
|
||||
|
||||
describe('非正常分支', () => {
|
||||
it('liveEditing mode', () => {
|
||||
designer.project.mountSimulator({
|
||||
liveEditing: {
|
||||
editing: {},
|
||||
},
|
||||
});
|
||||
editor.set('designer', designer);
|
||||
designer.currentDocument?.selection.select('page');
|
||||
// nothing happened
|
||||
fireEvent.keyDown(document, { keyCode: 39 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 37 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 40 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 38 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 39, altKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 37, altKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 40, altKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 38, altKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 90, metaKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 89, metaKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 67, metaKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 86, metaKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 27 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 46 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
});
|
||||
it('isFormEvent: true', () => {
|
||||
designer.currentDocument?.selection.select('page');
|
||||
// nothing happened
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 39 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 37 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 40 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 38 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 39, altKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 37, altKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 40, altKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 38, altKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 90, metaKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 89, metaKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 67, metaKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 86, metaKey: true });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 27 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 46 });
|
||||
expect(designer.currentDocument?.selection.selected[0]).toBe('page');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
404
packages/designer/tests/designer/designer.test.ts
Normal file
404
packages/designer/tests/designer/designer.test.ts
Normal file
@ -0,0 +1,404 @@
|
||||
import '../fixtures/window';
|
||||
import { Editor, globalContext } from '@ali/lowcode-editor-core';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { DocumentModel } from '../../src/document/document-model';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import { Dragon, DragObjectType } from '../../src/designer/dragon';
|
||||
import { TransformStage } from '../../src/document/node/transform-stage';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import buttonMetadata from '../fixtures/component-metadata/button';
|
||||
import pageMetadata from '../fixtures/component-metadata/page';
|
||||
import divMetadata from '../fixtures/component-metadata/div';
|
||||
import { delayObxTick } from '../utils';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
|
||||
describe('Designer 测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
let dragon: Dragon;
|
||||
|
||||
beforeAll(() => {
|
||||
editor = new Editor();
|
||||
!globalContext.has(Editor) && globalContext.register(editor, Editor);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
designer = new Designer({ editor });
|
||||
project = designer.project;
|
||||
doc = project.createDocument(formSchema);
|
||||
dragon = new Dragon(designer);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
project.mountSimulator(undefined);
|
||||
designer.purge();
|
||||
designer = null;
|
||||
project = null;
|
||||
dragon = null;
|
||||
});
|
||||
|
||||
describe('onDragstart / onDrag / onDragend', () => {
|
||||
it('DragObjectType.Node', () => {
|
||||
const dragStartMockFn = jest.fn();
|
||||
const dragMockFn = jest.fn();
|
||||
const dragEndMockFn = jest.fn();
|
||||
const dragStartMockFn2 = jest.fn();
|
||||
const dragMockFn2 = jest.fn();
|
||||
const dragEndMockFn2 = jest.fn();
|
||||
|
||||
const designer = new Designer({
|
||||
editor,
|
||||
onDragstart: dragStartMockFn,
|
||||
onDrag: dragMockFn,
|
||||
onDragend: dragEndMockFn,
|
||||
});
|
||||
editor.on('designer.dragstart', dragStartMockFn2);
|
||||
editor.on('designer.drag', dragMockFn2);
|
||||
editor.on('designer.dragend', dragEndMockFn2);
|
||||
const dragon = designer.dragon;
|
||||
|
||||
dragon.boost(
|
||||
{
|
||||
type: DragObjectType.Node,
|
||||
nodes: [doc.getNode('node_k1ow3cbn')],
|
||||
},
|
||||
new MouseEvent('mousedown', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 108, clientY: 108 });
|
||||
expect(dragStartMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragStartMockFn2).toHaveBeenCalledTimes(1);
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragMockFn2).toHaveBeenCalledTimes(1);
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 110, clientY: 110 });
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(2);
|
||||
expect(dragMockFn2).toHaveBeenCalledTimes(2);
|
||||
|
||||
setMockDropLocation();
|
||||
fireEvent.mouseUp(document, { clientX: 118, clientY: 118 });
|
||||
|
||||
expect(dragEndMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragEndMockFn2).toHaveBeenCalledTimes(1);
|
||||
|
||||
function setMockDropLocation() {
|
||||
const mockTarget = {
|
||||
document: doc,
|
||||
children: {
|
||||
get(x) {
|
||||
return x;
|
||||
},
|
||||
insert() {},
|
||||
},
|
||||
};
|
||||
const mockDetail = { type: 'Children', index: 1, near: { node: { x: 1 } } };
|
||||
const mockSource = {};
|
||||
const mockEvent = { type: 'LocateEvent', nodes: [] };
|
||||
|
||||
return designer.createLocation({
|
||||
target: mockTarget,
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('DragObjectType.NodeData', () => {
|
||||
const dragStartMockFn = jest.fn();
|
||||
const dragMockFn = jest.fn();
|
||||
const dragEndMockFn = jest.fn();
|
||||
const dragStartMockFn2 = jest.fn();
|
||||
const dragMockFn2 = jest.fn();
|
||||
const dragEndMockFn2 = jest.fn();
|
||||
|
||||
const designer = new Designer({
|
||||
editor,
|
||||
onDragstart: dragStartMockFn,
|
||||
onDrag: dragMockFn,
|
||||
onDragend: dragEndMockFn,
|
||||
});
|
||||
editor.on('designer.dragstart', dragStartMockFn2);
|
||||
editor.on('designer.drag', dragMockFn2);
|
||||
editor.on('designer.dragend', dragEndMockFn2);
|
||||
const dragon = designer.dragon;
|
||||
|
||||
dragon.boost(
|
||||
{
|
||||
type: DragObjectType.NodeData,
|
||||
data: [{
|
||||
componentName: 'Button',
|
||||
}],
|
||||
},
|
||||
new MouseEvent('mousedown', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 108, clientY: 108 });
|
||||
expect(dragStartMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragStartMockFn2).toHaveBeenCalledTimes(1);
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragMockFn2).toHaveBeenCalledTimes(1);
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 110, clientY: 110 });
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(2);
|
||||
expect(dragMockFn2).toHaveBeenCalledTimes(2);
|
||||
|
||||
setMockDropLocation();
|
||||
fireEvent.mouseUp(document, { clientX: 118, clientY: 118 });
|
||||
|
||||
expect(dragEndMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragEndMockFn2).toHaveBeenCalledTimes(1);
|
||||
|
||||
function setMockDropLocation() {
|
||||
const mockTarget = {
|
||||
document: doc,
|
||||
children: {
|
||||
get(x) {
|
||||
return x;
|
||||
},
|
||||
insert() {},
|
||||
},
|
||||
};
|
||||
const mockDetail = { type: 'Children', index: 1, near: { node: { x: 1 } } };
|
||||
const mockSource = {};
|
||||
const mockEvent = { type: 'LocateEvent', nodes: [] };
|
||||
|
||||
return designer.createLocation({
|
||||
target: mockTarget,
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('addPropsReducer / transformProps', () => {
|
||||
// 没有相应的 reducer
|
||||
expect(designer.transformProps({ num: 1 }, TransformStage.Init)).toEqual({ num: 1 });
|
||||
// props 是数组
|
||||
expect(designer.transformProps([{ num: 1 }], TransformStage.Init)).toEqual([{ num: 1 }]);
|
||||
|
||||
designer.addPropsReducer((props, node) => {
|
||||
props.num = props.num + 1;
|
||||
return props;
|
||||
}, TransformStage.Init);
|
||||
|
||||
designer.addPropsReducer((props, node) => {
|
||||
props.num = props.num + 1;
|
||||
return props;
|
||||
}, TransformStage.Init);
|
||||
|
||||
designer.addPropsReducer((props, node) => {
|
||||
props.num = props.num + 1;
|
||||
return props;
|
||||
}, TransformStage.Clone);
|
||||
|
||||
designer.addPropsReducer((props, node) => {
|
||||
props.num = props.num + 1;
|
||||
return props;
|
||||
}, TransformStage.Serilize);
|
||||
|
||||
designer.addPropsReducer((props, node) => {
|
||||
props.num = props.num + 1;
|
||||
return props;
|
||||
}, TransformStage.Render);
|
||||
|
||||
designer.addPropsReducer((props, node) => {
|
||||
props.num = props.num + 1;
|
||||
return props;
|
||||
}, TransformStage.Save);
|
||||
|
||||
designer.addPropsReducer((props, node) => {
|
||||
props.num = props.num + 1;
|
||||
return props;
|
||||
}, TransformStage.Upgrade);
|
||||
|
||||
expect(designer.transformProps({ num: 1 }, {}, TransformStage.Init)).toEqual({ num: 3 });
|
||||
expect(designer.transformProps({ num: 1 }, {}, TransformStage.Clone)).toEqual({ num: 2 });
|
||||
expect(designer.transformProps({ num: 1 }, {}, TransformStage.Serilize)).toEqual({ num: 2 });
|
||||
expect(designer.transformProps({ num: 1 }, {}, TransformStage.Render)).toEqual({ num: 2 });
|
||||
expect(designer.transformProps({ num: 1 }, {}, TransformStage.Save)).toEqual({ num: 2 });
|
||||
expect(designer.transformProps({ num: 1 }, {}, TransformStage.Upgrade)).toEqual({ num: 2 });
|
||||
|
||||
designer.addPropsReducer((props, node) => {
|
||||
throw new Error('calculate error');
|
||||
}, TransformStage.Upgrade);
|
||||
expect(designer.transformProps({ num: 1 }, {}, TransformStage.Upgrade)).toEqual({ num: 2 });
|
||||
});
|
||||
|
||||
it('setProps', () => {
|
||||
// 第一次设置 props
|
||||
const initialProps = {
|
||||
simulatorComponent: { isSimulatorComp: true },
|
||||
simulatorProps: { designMode: 'design' },
|
||||
suspensed: true,
|
||||
componentMetadatas: [buttonMetadata, divMetadata],
|
||||
};
|
||||
designer = new Designer({ editor, ...initialProps });
|
||||
|
||||
expect(designer.simulatorComponent).toEqual({ isSimulatorComp: true });
|
||||
expect(designer.simulatorProps).toEqual({ designMode: 'design' });
|
||||
expect(designer.suspensed).toBeTruthy();
|
||||
expect(designer._componentMetasMap.has('Div')).toBeTruthy();
|
||||
expect(designer._componentMetasMap.has('Button')).toBeTruthy();
|
||||
const { editor: editorFromDesigner, ...others } = designer.props;
|
||||
expect(others).toEqual(initialProps);
|
||||
expect(designer.get('simulatorProps')).toEqual({ designMode: 'design' });
|
||||
expect(designer.get('suspensed')).toBeTruthy();
|
||||
expect(designer.get('xxx')).toBeUndefined();
|
||||
|
||||
// 第二次设置 props
|
||||
const updatedProps = {
|
||||
simulatorComponent: { isSimulatorComp2: true },
|
||||
simulatorProps: { designMode: 'live' },
|
||||
suspensed: false,
|
||||
componentMetadatas: [buttonMetadata],
|
||||
};
|
||||
designer.setProps(updatedProps);
|
||||
|
||||
expect(designer.simulatorComponent).toEqual({ isSimulatorComp2: true });
|
||||
expect(designer.simulatorProps).toEqual({ designMode: 'live' });
|
||||
expect(designer.suspensed).toBeFalsy();
|
||||
expect(designer._componentMetasMap.has('Button')).toBeTruthy();
|
||||
expect(designer._componentMetasMap.has('Div')).toBeTruthy();
|
||||
const { editor: editorFromDesigner2, ...others2 } = designer.props;
|
||||
expect(others2).toEqual(updatedProps);
|
||||
});
|
||||
|
||||
describe('getSuitableInsertion', () => {
|
||||
it('没有 currentDocument', () => {
|
||||
project.unload();
|
||||
expect(designer.getSuitableInsertion({})).toBeNull();
|
||||
});
|
||||
|
||||
it('有选中节点,isContainer && 允许放子节点', () => {
|
||||
designer.createComponentMeta(divMetadata);
|
||||
designer.createComponentMeta(buttonMetadata);
|
||||
designer.currentSelection?.select('node_k1ow3cbo');
|
||||
const { target, index } = designer.getSuitableInsertion(
|
||||
doc.createNode({ componentName: 'Button' }),
|
||||
);
|
||||
expect(target).toBe(doc.getNode('node_k1ow3cbo'));
|
||||
expect(index).toBeUndefined();
|
||||
});
|
||||
|
||||
it('有选中节点,不是 isContainer', () => {
|
||||
designer.createComponentMeta(divMetadata);
|
||||
designer.createComponentMeta(buttonMetadata);
|
||||
designer.currentSelection?.select('node_k1ow3cbn');
|
||||
const { target, index } = designer.getSuitableInsertion(
|
||||
doc.createNode({ componentName: 'Button' }),
|
||||
);
|
||||
expect(target).toBe(doc.getNode('node_k1ow3cbo'));
|
||||
expect(index).toBe(1);
|
||||
});
|
||||
|
||||
it('无选中节点', () => {
|
||||
designer.createComponentMeta(pageMetadata);
|
||||
const { target, index } = designer.getSuitableInsertion(
|
||||
doc.createNode({ componentName: 'Button' }),
|
||||
);
|
||||
expect(target).toBe(doc.getNode('page'));
|
||||
expect(index).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('createLocation / clearLocation', () => {
|
||||
const mockTarget = {
|
||||
document: doc,
|
||||
children: {
|
||||
get(x) {
|
||||
return x;
|
||||
},
|
||||
insert() {},
|
||||
},
|
||||
};
|
||||
const mockDetail = { type: 'Children', index: 1, near: { node: { x: 1 } } };
|
||||
const mockSource = {};
|
||||
const mockEvent = { type: 'LocateEvent', nodes: [] };
|
||||
|
||||
const loc = designer.createLocation({
|
||||
target: mockTarget,
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
|
||||
expect(designer.dropLocation).toBe(loc);
|
||||
|
||||
const doc2 = project.createDocument({ componentName: 'Page' });
|
||||
designer.createLocation({
|
||||
target: {
|
||||
document: doc2,
|
||||
children: {
|
||||
get(x) {
|
||||
return x;
|
||||
},
|
||||
insert() {},
|
||||
},
|
||||
},
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
|
||||
designer.clearLocation();
|
||||
expect(designer.dropLocation).toBeUndefined();
|
||||
});
|
||||
|
||||
it('autorun', async () => {
|
||||
const mockFn = jest.fn();
|
||||
designer.autorun(() => {
|
||||
mockFn();
|
||||
}, true);
|
||||
|
||||
await delayObxTick();
|
||||
|
||||
expect(mockFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('suspensed', () => {
|
||||
designer.suspensed = true;
|
||||
expect(designer.suspensed).toBeTruthy();
|
||||
designer.suspensed = false;
|
||||
expect(designer.suspensed).toBeFalsy();
|
||||
});
|
||||
|
||||
it('schema', () => {
|
||||
// TODO: matchSnapshot
|
||||
designer.schema;
|
||||
designer.setSchema({
|
||||
componentsTree: [
|
||||
{
|
||||
componentName: 'Page',
|
||||
props: {},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('createOffsetObserver / clearOobxList / touchOffsetObserver', () => {
|
||||
project.mountSimulator({
|
||||
computeComponentInstanceRect() {},
|
||||
});
|
||||
designer.createOffsetObserver({ node: doc.getNode('page'), instance: {} });
|
||||
expect(designer.oobxList).toHaveLength(1);
|
||||
designer.createOffsetObserver({ node: doc.getNode('page'), instance: {} });
|
||||
expect(designer.oobxList).toHaveLength(2);
|
||||
|
||||
designer.clearOobxList(true);
|
||||
expect(designer.oobxList).toHaveLength(0);
|
||||
|
||||
const obx = designer.createOffsetObserver({ node: doc.getNode('page'), instance: {} });
|
||||
obx.pid = 'xxx';
|
||||
obx.compute = () => {};
|
||||
expect(designer.oobxList).toHaveLength(1);
|
||||
|
||||
designer.touchOffsetObserver();
|
||||
expect(designer.oobxList).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
22
packages/designer/tests/designer/detecting.test.ts
Normal file
22
packages/designer/tests/designer/detecting.test.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { Detecting } from '../../src/designer/detecting';
|
||||
|
||||
it('Detecting 测试', () => {
|
||||
const detecting = new Detecting();
|
||||
|
||||
expect(detecting.enable).toBeTruthy();
|
||||
|
||||
const mockNode = { document };
|
||||
detecting.capture(mockNode);
|
||||
expect(detecting.current).toBe(mockNode);
|
||||
|
||||
detecting.release(mockNode);
|
||||
expect(detecting.current).toBeNull();
|
||||
|
||||
detecting.capture(mockNode);
|
||||
detecting.leave(document);
|
||||
expect(detecting.current).toBeNull();
|
||||
|
||||
detecting.capture(mockNode);
|
||||
detecting.enable = false;
|
||||
expect(detecting.current).toBeNull();
|
||||
});
|
||||
346
packages/designer/tests/designer/dragon.test.ts
Normal file
346
packages/designer/tests/designer/dragon.test.ts
Normal file
@ -0,0 +1,346 @@
|
||||
import '../fixtures/window';
|
||||
import { set } from '../utils';
|
||||
import { Editor, globalContext } from '@ali/lowcode-editor-core';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { DocumentModel } from '../../src/document/document-model';
|
||||
import {
|
||||
isRootNode,
|
||||
Node,
|
||||
isNode,
|
||||
comparePosition,
|
||||
contains,
|
||||
insertChild,
|
||||
insertChildren,
|
||||
PositionNO,
|
||||
} from '../../src/document/node/node';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import {
|
||||
Dragon,
|
||||
isDragNodeObject,
|
||||
isDragNodeDataObject,
|
||||
isDragAnyObject,
|
||||
isLocateEvent,
|
||||
DragObjectType,
|
||||
isShaken,
|
||||
setShaken,
|
||||
} from '../../src/designer/dragon';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import divMetadata from '../fixtures/component-metadata/div';
|
||||
import formMetadata from '../fixtures/component-metadata/form';
|
||||
import otherMeta from '../fixtures/component-metadata/other';
|
||||
import pageMetadata from '../fixtures/component-metadata/page';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
|
||||
describe('Dragon 测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
let dragon: Dragon;
|
||||
|
||||
beforeAll(() => {
|
||||
editor = new Editor();
|
||||
!globalContext.has(Editor) && globalContext.register(editor, Editor);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
designer = new Designer({ editor });
|
||||
project = designer.project;
|
||||
doc = project.createDocument(formSchema);
|
||||
dragon = new Dragon(designer);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
project.mountSimulator(undefined);
|
||||
designer.purge();
|
||||
designer = null;
|
||||
project = null;
|
||||
dragon = null;
|
||||
});
|
||||
|
||||
it.skip('drag NodeData', () => {
|
||||
const dragStartMockFn = jest.fn();
|
||||
const dragMockFn = jest.fn();
|
||||
const dragEndMockFn = jest.fn();
|
||||
|
||||
dragon.onDragstart((e) => {
|
||||
console.log('start', e, e.originalEvent, e.originalEvent.clientX);
|
||||
});
|
||||
|
||||
dragon.onDrag((e) => {
|
||||
console.log('drag', e, e.originalEvent, e.originalEvent.clientX);
|
||||
});
|
||||
|
||||
dragon.onDragend((e) => {
|
||||
console.log('end', e, e.originalEvent);
|
||||
});
|
||||
|
||||
dragon.boost(
|
||||
{
|
||||
type: DragObjectType.NodeData,
|
||||
data: [{ componentName: 'Button' }],
|
||||
},
|
||||
new Event('dragstart', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
fireEvent.dragOver(document, { clientX: 108, clientY: 108 });
|
||||
fireEvent.dragEnd(document, { clientX: 118, clientY: 118 });
|
||||
});
|
||||
|
||||
it.skip('drag Node', () => {
|
||||
console.log(new MouseEvent('mousedown', { clientX: 1 }).clientX);
|
||||
// console.log(new Event('mousedown', { clientX: 1 }).clientX);
|
||||
// console.log(new Event('drag', { clientX: 1 }).clientX);
|
||||
// console.log(new CustomEvent('drag', { clientX: 1 }).clientX);
|
||||
console.log(document.createEvent('dragstart', { clientX: 1 }).clientX);
|
||||
});
|
||||
|
||||
it('mouse NodeData', () => {
|
||||
const dragStartMockFn = jest.fn();
|
||||
const dragMockFn = jest.fn();
|
||||
const dragEndMockFn = jest.fn();
|
||||
|
||||
const offDragStart = dragon.onDragstart(dragStartMockFn);
|
||||
|
||||
const offDrag = dragon.onDrag(dragMockFn);
|
||||
|
||||
const offDragEnd = dragon.onDragend(dragEndMockFn);
|
||||
|
||||
dragon.boost(
|
||||
{
|
||||
type: DragObjectType.NodeData,
|
||||
data: [{ componentName: 'Button' }],
|
||||
},
|
||||
new MouseEvent('mousedown', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 108, clientY: 108 });
|
||||
fireEvent.mouseMove(document, { clientX: 110, clientY: 110 });
|
||||
fireEvent.mouseUp(document, { clientX: 118, clientY: 118 });
|
||||
|
||||
expect(dragStartMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(2);
|
||||
expect(dragEndMockFn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('mouse Node', () => {
|
||||
const dragStartMockFn = jest.fn();
|
||||
const dragMockFn = jest.fn();
|
||||
const dragEndMockFn = jest.fn();
|
||||
|
||||
const offDragStart = dragon.onDragstart(dragStartMockFn);
|
||||
const offDrag = dragon.onDrag(dragMockFn);
|
||||
const offDragEnd = dragon.onDragend(dragEndMockFn);
|
||||
|
||||
dragon.boost(
|
||||
{
|
||||
type: DragObjectType.Node,
|
||||
nodes: [doc.getNode('node_k1ow3cbn')],
|
||||
},
|
||||
new MouseEvent('mousedown', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
// mouseDown 模式正常不会触发 dragStart 事件,除非 shaken 型
|
||||
expect(dragStartMockFn).not.toHaveBeenCalled();
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 108, clientY: 108 });
|
||||
expect(dragStartMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(1);
|
||||
fireEvent.mouseMove(document, { clientX: 110, clientY: 110 });
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(2);
|
||||
expect(dragon.dragging).toBeTruthy();
|
||||
|
||||
fireEvent.mouseUp(document, { clientX: 118, clientY: 118 });
|
||||
|
||||
expect(dragEndMockFn).toHaveBeenCalledTimes(1);
|
||||
|
||||
offDragStart();
|
||||
offDrag();
|
||||
offDragEnd();
|
||||
dragMockFn.mockClear();
|
||||
|
||||
dragon.boost(
|
||||
{
|
||||
type: DragObjectType.Node,
|
||||
nodes: [doc.getNode('node_k1ow3cbn')],
|
||||
},
|
||||
new MouseEvent('mousedown', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 108, clientY: 108 });
|
||||
|
||||
expect(dragMockFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('mouse Node & esc', () => {
|
||||
const dragStartMockFn = jest.fn();
|
||||
const dragMockFn = jest.fn();
|
||||
const dragEndMockFn = jest.fn();
|
||||
|
||||
const offDragStart = dragon.onDragstart(dragStartMockFn);
|
||||
const offDrag = dragon.onDrag(dragMockFn);
|
||||
const offDragEnd = dragon.onDragend(dragEndMockFn);
|
||||
|
||||
dragon.boost(
|
||||
{
|
||||
type: DragObjectType.Node,
|
||||
nodes: [doc.getNode('node_k1ow3cbn')],
|
||||
},
|
||||
new MouseEvent('mousedown', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
fireEvent.keyDown(document, { keyCode: 27 });
|
||||
expect(dragon.designer.dropLocation).toBeUndefined();
|
||||
});
|
||||
|
||||
it('mouse Node & copy', () => {
|
||||
const dragStartMockFn = jest.fn();
|
||||
const dragMockFn = jest.fn();
|
||||
const dragEndMockFn = jest.fn();
|
||||
|
||||
const offDragStart = dragon.onDragstart(dragStartMockFn);
|
||||
const offDrag = dragon.onDrag(dragMockFn);
|
||||
const offDragEnd = dragon.onDragend(dragEndMockFn);
|
||||
|
||||
dragon.boost(
|
||||
{
|
||||
type: DragObjectType.Node,
|
||||
nodes: [doc.getNode('node_k1ow3cbn')],
|
||||
},
|
||||
new MouseEvent('mousedown', { clientX: 100, clientY: 100 }),
|
||||
);
|
||||
|
||||
const mockedFn1 = jest.fn();
|
||||
project.mountSimulator({ setCopyState: mockedFn1 });
|
||||
expect(dragon.getSimulators().size).toBe(1);
|
||||
fireEvent.keyDown(document, { ctrlKey: true });
|
||||
expect(mockedFn1).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('from', () => {
|
||||
const dragStartMockFn = jest.fn();
|
||||
const dragMockFn = jest.fn();
|
||||
const dragEndMockFn = jest.fn();
|
||||
|
||||
const offDragStart = dragon.onDragstart(dragStartMockFn);
|
||||
const offDrag = dragon.onDrag(dragMockFn);
|
||||
const offDragEnd = dragon.onDragend(dragEndMockFn);
|
||||
const mockedBoostFn = jest
|
||||
.fn((e) => {
|
||||
return {
|
||||
type: DragObjectType.Node,
|
||||
nodes: [doc.getNode('node_k1ow3cbn')],
|
||||
};
|
||||
})
|
||||
.mockImplementationOnce(() => null);
|
||||
|
||||
const offFrom = dragon.from(document, mockedBoostFn);
|
||||
|
||||
// 无用 mouseDown,无效的按钮
|
||||
fireEvent.mouseDown(document, { button: 2 });
|
||||
expect(dragStartMockFn).not.toHaveBeenCalled();
|
||||
|
||||
// 无用 mouseDown,无效的 dragObject
|
||||
fireEvent.mouseDown(document, { clientX: 100, clientY: 100 });
|
||||
expect(dragStartMockFn).not.toHaveBeenCalled();
|
||||
|
||||
fireEvent.mouseDown(document, { clientX: 100, clientY: 100 });
|
||||
expect(dragStartMockFn).not.toHaveBeenCalled();
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 108, clientY: 108 });
|
||||
expect(dragStartMockFn).toHaveBeenCalledTimes(1);
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(1);
|
||||
fireEvent.mouseMove(document, { clientX: 110, clientY: 110 });
|
||||
expect(dragMockFn).toHaveBeenCalledTimes(2);
|
||||
expect(dragon.dragging).toBeTruthy();
|
||||
|
||||
fireEvent.mouseUp(document, { clientX: 118, clientY: 118 });
|
||||
|
||||
expect(dragEndMockFn).toHaveBeenCalledTimes(1);
|
||||
|
||||
offDragStart();
|
||||
offDrag();
|
||||
offDragEnd();
|
||||
dragMockFn.mockClear();
|
||||
|
||||
fireEvent.mouseMove(document, { clientX: 100, clientY: 100 });
|
||||
expect(dragMockFn).not.toHaveBeenCalled();
|
||||
|
||||
offFrom();
|
||||
fireEvent.mouseMove(document, { clientX: 100, clientY: 100 });
|
||||
expect(dragMockFn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('addSensor / removeSensor', () => {
|
||||
const sensor = {};
|
||||
dragon.addSensor(sensor);
|
||||
expect(dragon.sensors.length).toBe(1);
|
||||
dragon.removeSensor(sensor);
|
||||
expect(dragon.sensors.length).toBe(0);
|
||||
});
|
||||
|
||||
it('has sensor', () => {
|
||||
const mockedFn1 = jest.fn();
|
||||
const mockedDoc = document.createElement('iframe').contentWindow?.document;
|
||||
dragon.addSensor({
|
||||
fixEvent: () => {},
|
||||
locate: () => {},
|
||||
contentDocument: mockedDoc,
|
||||
});
|
||||
project.mountSimulator({
|
||||
setCopyState: mockedFn1,
|
||||
setNativeSelection: () => {},
|
||||
clearState: () => {},
|
||||
setDraggingState: () => {},
|
||||
});
|
||||
|
||||
const mockedBoostFn = jest
|
||||
.fn((e) => {
|
||||
return {
|
||||
type: DragObjectType.Node,
|
||||
nodes: [doc.getNode('node_k1ow3cbn')],
|
||||
};
|
||||
})
|
||||
.mockImplementationOnce(() => null);
|
||||
|
||||
const offFrom = dragon.from(document, mockedBoostFn);
|
||||
|
||||
// TODO: 想办法 mock 一个 iframe.currentDocument
|
||||
fireEvent.mouseDown(document, { clientX: 100, clientY: 100 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('导出的其他函数', () => {
|
||||
it('isDragNodeObject', () => {
|
||||
expect(isDragNodeObject({ type: DragObjectType.Node, nodes: [] })).toBeTruthy();
|
||||
});
|
||||
it('isDragNodeDataObject', () => {
|
||||
expect(isDragNodeDataObject({ type: DragObjectType.NodeData, data: [] })).toBeTruthy();
|
||||
});
|
||||
it('isDragAnyObject', () => {
|
||||
expect(isDragAnyObject()).toBeFalsy();
|
||||
expect(isDragAnyObject({ type: DragObjectType.Node, nodes: [] })).toBeFalsy();
|
||||
expect(isDragAnyObject({ type: DragObjectType.NodeData, data: [] })).toBeFalsy();
|
||||
expect(isDragAnyObject({ type: 'others', data: [] })).toBeTruthy();
|
||||
});
|
||||
it('isLocateEvent', () => {
|
||||
expect(isLocateEvent({ type: 'LocateEvent' })).toBeTruthy();
|
||||
});
|
||||
it('isShaken', () => {
|
||||
expect(
|
||||
isShaken(
|
||||
{ clientX: 1, clientY: 1, target: {} },
|
||||
{ clientX: 1, clientY: 1, target: { other: 1 } },
|
||||
),
|
||||
).toBeTruthy();
|
||||
expect(isShaken({ shaken: true })).toBeTruthy();
|
||||
expect(isShaken({ clientX: 1, clientY: 1 }, { clientX: 2, clientY: 2 })).toBeFalsy();
|
||||
expect(isShaken({ clientX: 1, clientY: 1 }, { clientX: 3, clientY: 5 })).toBeTruthy();
|
||||
});
|
||||
it('setShaken', () => {
|
||||
const e = {};
|
||||
setShaken(e);
|
||||
expect(isShaken(e)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
196
packages/designer/tests/designer/location.test.ts
Normal file
196
packages/designer/tests/designer/location.test.ts
Normal file
@ -0,0 +1,196 @@
|
||||
import {
|
||||
DropLocation,
|
||||
isLocationData,
|
||||
isLocationChildrenDetail,
|
||||
isRowContainer,
|
||||
isChildInline,
|
||||
getRectTarget,
|
||||
isVerticalContainer,
|
||||
isVertical,
|
||||
getWindow,
|
||||
} from '../../src/designer/location';
|
||||
import { getMockElement } from '../utils';
|
||||
|
||||
describe('DropLocation 测试', () => {
|
||||
it('constructor', () => {
|
||||
const mockTarget = { document };
|
||||
const mockDetail = {};
|
||||
const mockSource = {};
|
||||
const mockEvent = { type: 'LocateEvent', nodes: [] };
|
||||
const loc = new DropLocation({
|
||||
target: mockTarget,
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
|
||||
expect(loc.getContainer()).toBe(mockTarget);
|
||||
expect(loc.document).toBe(document);
|
||||
expect(loc.target).toBe(mockTarget);
|
||||
expect(loc.detail).toBe(mockDetail);
|
||||
expect(loc.source).toBe(mockSource);
|
||||
expect(loc.event).toBe(mockEvent);
|
||||
|
||||
const mockEvent2 = { type: 'LocateEvent', data: [] };
|
||||
const loc2 = loc.clone(mockEvent2);
|
||||
expect(loc2.target).toBe(mockTarget);
|
||||
expect(loc2.detail).toBe(mockDetail);
|
||||
expect(loc2.source).toBe(mockSource);
|
||||
expect(loc2.event).toBe(mockEvent2);
|
||||
});
|
||||
|
||||
it('constructor, detail: undefined', () => {
|
||||
const mockTarget = { document };
|
||||
const mockDetail = undefined;
|
||||
const mockSource = {};
|
||||
const mockEvent = { type: 'LocateEvent', nodes: [] };
|
||||
const loc = new DropLocation({
|
||||
target: mockTarget,
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
|
||||
expect(loc.getInsertion()).toBeNull();
|
||||
});
|
||||
|
||||
it('constructor, detail.type: Children, detail.index <= 0', () => {
|
||||
const mockTarget = { document };
|
||||
const mockDetail = { type: 'Children', index: -1 };
|
||||
const mockSource = {};
|
||||
const mockEvent = { type: 'LocateEvent', nodes: [] };
|
||||
const loc = new DropLocation({
|
||||
target: mockTarget,
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
|
||||
expect(loc.getInsertion()).toBeNull();
|
||||
});
|
||||
|
||||
it('constructor, detail.type: Children, detail.index > 0', () => {
|
||||
const mockTarget = {
|
||||
document,
|
||||
children: {
|
||||
get(x) {
|
||||
return x;
|
||||
},
|
||||
},
|
||||
};
|
||||
const mockDetail = { type: 'Children', index: 1 };
|
||||
const mockSource = {};
|
||||
const mockEvent = { type: 'LocateEvent', nodes: [] };
|
||||
const loc = new DropLocation({
|
||||
target: mockTarget,
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
|
||||
expect(loc.getInsertion()).toBe(0);
|
||||
});
|
||||
|
||||
it('constructor, detail.type: Prop', () => {
|
||||
const mockTarget = {
|
||||
document,
|
||||
children: {
|
||||
get(x) {
|
||||
return x;
|
||||
},
|
||||
},
|
||||
};
|
||||
const mockDetail = { type: 'Prop', index: 1, near: { node: { x: 1 } } };
|
||||
const mockSource = {};
|
||||
const mockEvent = { type: 'LocateEvent', nodes: [] };
|
||||
const loc = new DropLocation({
|
||||
target: mockTarget,
|
||||
detail: mockDetail,
|
||||
source: mockSource,
|
||||
event: mockEvent,
|
||||
});
|
||||
|
||||
expect(loc.getInsertion()).toEqual({ x: 1 });
|
||||
});
|
||||
});
|
||||
|
||||
it('isLocationData', () => {
|
||||
expect(isLocationData({ target: {}, detail: {} })).toBeTruthy();
|
||||
});
|
||||
|
||||
it('isLocationChildrenDetail', () => {
|
||||
expect(isLocationChildrenDetail({ type: 'Children' })).toBeTruthy();
|
||||
});
|
||||
|
||||
it('isRowContainer', () => {
|
||||
expect(isRowContainer({ nodeType: Node.TEXT_NODE })).toBeTruthy();
|
||||
window.getComputedStyle = jest
|
||||
.fn(() => {
|
||||
return {
|
||||
getPropertyValue: (pName) => {
|
||||
return pName === 'display' ? 'flex' : 'row';
|
||||
}
|
||||
}
|
||||
})
|
||||
.mockImplementationOnce(() => {
|
||||
return {
|
||||
getPropertyValue: (pName) => {
|
||||
return pName === 'display' ? 'flex' : 'column';
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(isRowContainer(getMockElement('div'))).toBeFalsy();
|
||||
expect(isRowContainer(getMockElement('div'))).toBeTruthy();
|
||||
});
|
||||
|
||||
it('isChildInline', () => {
|
||||
window.getComputedStyle = jest
|
||||
.fn(() => {
|
||||
return {
|
||||
getPropertyValue: (pName) => {
|
||||
return pName === 'display' ? 'inline' : 'float';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expect(isChildInline({ nodeType: Node.TEXT_NODE })).toBeTruthy();
|
||||
expect(isChildInline(getMockElement('div'))).toBeTruthy();
|
||||
});
|
||||
|
||||
it('getRectTarget', () => {
|
||||
expect(getRectTarget()).toBeNull();
|
||||
expect(getRectTarget({ computed: false })).toBeNull();
|
||||
expect(getRectTarget({ elements: [{}] })).toEqual({});
|
||||
});
|
||||
|
||||
it('isVerticalContainer', () => {
|
||||
window.getComputedStyle = jest
|
||||
.fn(() => {
|
||||
return {
|
||||
getPropertyValue: (pName) => {
|
||||
return pName === 'display' ? 'flex' : 'row';
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(isVerticalContainer()).toBeFalsy();
|
||||
expect(isVerticalContainer({ elements: [getMockElement('div')] })).toBeTruthy()
|
||||
});
|
||||
|
||||
it('isVertical', () => {
|
||||
expect(isVertical({ elements: [] })).toBeFalsy();
|
||||
expect(isVertical({ elements: [getMockElement('div')] })).toBeFalsy();
|
||||
window.getComputedStyle = jest
|
||||
.fn(() => {
|
||||
return {
|
||||
getPropertyValue: (pName) => {
|
||||
return pName === 'display' ? 'inline' : 'float';
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(isVertical({ elements: [getMockElement('div')] })).toBeTruthy();
|
||||
});
|
||||
|
||||
it('getWindow', () => {
|
||||
const mockElem = getMockElement('div');
|
||||
expect(getWindow(mockElem)).toBe(window);
|
||||
});
|
||||
158
packages/designer/tests/designer/scroller.test.ts
Normal file
158
packages/designer/tests/designer/scroller.test.ts
Normal file
@ -0,0 +1,158 @@
|
||||
import '../fixtures/window';
|
||||
import { set } from '../utils';
|
||||
import { Editor, globalContext } from '@ali/lowcode-editor-core';
|
||||
import { Project } from '../../src/project/project';
|
||||
import { DocumentModel } from '../../src/document/document-model';
|
||||
import { ScrollTarget, Scroller } from '../../src/designer/scroller';
|
||||
import {
|
||||
isRootNode,
|
||||
isNode,
|
||||
comparePosition,
|
||||
contains,
|
||||
insertChild,
|
||||
insertChildren,
|
||||
PositionNO,
|
||||
} from '../../src/document/node/node';
|
||||
import { Designer } from '../../src/designer/designer';
|
||||
import {
|
||||
Dragon,
|
||||
isDragNodeObject,
|
||||
isDragNodeDataObject,
|
||||
isDragAnyObject,
|
||||
isLocateEvent,
|
||||
DragObjectType,
|
||||
isShaken,
|
||||
setShaken,
|
||||
} from '../../src/designer/dragon';
|
||||
import formSchema from '../fixtures/schema/form';
|
||||
import divMetadata from '../fixtures/component-metadata/div';
|
||||
import formMetadata from '../fixtures/component-metadata/form';
|
||||
import otherMeta from '../fixtures/component-metadata/other';
|
||||
import pageMetadata from '../fixtures/component-metadata/page';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
|
||||
describe('Scroller 测试', () => {
|
||||
let editor: Editor;
|
||||
let designer: Designer;
|
||||
let project: Project;
|
||||
let doc: DocumentModel;
|
||||
let dragon: Dragon;
|
||||
|
||||
beforeAll(() => {
|
||||
editor = new Editor();
|
||||
!globalContext.has(Editor) && globalContext.register(editor, Editor);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
designer = new Designer({ editor });
|
||||
project = designer.project;
|
||||
doc = project.createDocument(formSchema);
|
||||
dragon = new Dragon(designer);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
project.unload();
|
||||
project.mountSimulator(undefined);
|
||||
designer.purge();
|
||||
designer = null;
|
||||
project = null;
|
||||
dragon = null;
|
||||
});
|
||||
|
||||
function getMockWindow() {
|
||||
let scrollX = 0;
|
||||
let scrollY = 0;
|
||||
const mockWindow = {
|
||||
scrollTo(x, y) {
|
||||
if (typeof x === 'number') {
|
||||
scrollX = x;
|
||||
scrollY = y;
|
||||
} else {
|
||||
scrollX = x.left;
|
||||
scrollY = x.top;
|
||||
}
|
||||
},
|
||||
get scrollX() { return scrollX; },
|
||||
get scrollY() { return scrollY; },
|
||||
scrollHeight: 1000,
|
||||
scrollWidth: 500,
|
||||
document: {},
|
||||
nodeType: Node.ELEMENT_NODE,
|
||||
}
|
||||
return mockWindow;
|
||||
}
|
||||
|
||||
describe('ScrollTarget 测试', () => {
|
||||
it('constructor', () => {
|
||||
const win = getMockWindow();
|
||||
const target = new ScrollTarget(win);
|
||||
expect(target.scrollWidth).toBe(500);
|
||||
expect(target.scrollHeight).toBe(1000);
|
||||
target.scrollToXY(50, 50);
|
||||
expect(target.left).toBe(50);
|
||||
expect(target.top).toBe(50);
|
||||
|
||||
target.scrollTo({ left: 100, top: 100 });
|
||||
expect(target.left).toBe(100);
|
||||
expect(target.top).toBe(100);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function mockRAF() {
|
||||
let rafCount = 0;
|
||||
window.requestAnimationFrame = (fn) => {
|
||||
if (rafCount++ < 2) {
|
||||
fn();
|
||||
} else {
|
||||
window.requestAnimationFrame = () => {};
|
||||
}
|
||||
};
|
||||
}
|
||||
describe('Scroller 测试', () => {
|
||||
it('scrollTarget: ScrollTarget', () => {
|
||||
const win = getMockWindow();
|
||||
const scrollTarget = new ScrollTarget(win);
|
||||
const scroller = new Scroller({ scrollTarget, bounds: { width: 50, height: 50, top: 50, bottom: 50, left: 50, right: 50 } });
|
||||
mockRAF();
|
||||
scroller.scrollTo({ left: 50, top: 50 });
|
||||
|
||||
mockRAF();
|
||||
scroller.scrolling({ globalX: 100, globalY: 100 });
|
||||
})
|
||||
|
||||
it('scrollTarget: ScrollTarget, same left / top', () => {
|
||||
const win = getMockWindow();
|
||||
const scrollTarget = new ScrollTarget(win);
|
||||
const scroller = new Scroller({ scrollTarget, bounds: { width: 50, height: 50, top: 50, bottom: 50, left: 50, right: 50 } });
|
||||
mockRAF();
|
||||
scrollTarget.scrollTo({ left: 50, top: 50 });
|
||||
scroller.scrollTo({ left: 50, top: 50 });
|
||||
|
||||
mockRAF();
|
||||
scroller.scrolling({ globalX: 100, globalY: 100 });
|
||||
})
|
||||
|
||||
it('scrollTarget: Element', () => {
|
||||
const win = getMockWindow();
|
||||
// const scrollTarget = new ScrollTarget(win);
|
||||
const scroller = new Scroller({ scrollTarget: win, bounds: { width: 50, height: 50, top: 50, bottom: 50, left: 50, right: 50 } });
|
||||
mockRAF();
|
||||
scroller.scrollTo({ left: 50, top: 50 });
|
||||
|
||||
mockRAF();
|
||||
scroller.scrolling({ globalX: 100, globalY: 100 });
|
||||
})
|
||||
|
||||
it('scrollTarget: null', () => {
|
||||
const win = getMockWindow();
|
||||
// const scrollTarget = new ScrollTarget(win);
|
||||
const scroller = new Scroller({ scrollTarget: null, bounds: { width: 50, height: 50, top: 50, bottom: 50, left: 50, right: 50 } });
|
||||
mockRAF();
|
||||
scroller.scrollTo({ left: 50, top: 50 });
|
||||
|
||||
mockRAF();
|
||||
scroller.scrolling({ globalX: 100, globalY: 100 });
|
||||
})
|
||||
});
|
||||
});
|
||||
@ -93,7 +93,7 @@ describe('setting-top-entry 测试', () => {
|
||||
const { currentDocument } = designer.project;
|
||||
const divNode = currentDocument?.getNode('div');
|
||||
|
||||
console.log(divNode?.getPropValue('behavior'));
|
||||
// console.log(divNode?.getPropValue('behavior'));
|
||||
const { settingEntry } = divNode!;
|
||||
|
||||
expect(typeof settingEntry.getChildren).toBe('function');
|
||||
@ -160,10 +160,10 @@ describe('Node 方法测试', () => {
|
||||
const pageMeta = designer.getComponentMeta('Page');
|
||||
set(pageMeta, 'prototype.options.canDropIn', () => true);
|
||||
|
||||
const o = doc.getNode('form')!.getSuitablePlace(doc.getNode('node_k1ow3cbj'), 1);
|
||||
const o = doc.getNode('form')!.getSuitablePlace(doc.getNode('node_k1ow3cbj'), { index: 1 });
|
||||
expect(o).toEqual({
|
||||
container: doc.rootNode,
|
||||
ref: 1,
|
||||
ref: { index: 1 },
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
394
packages/designer/tests/document/node/props/prop.test.ts
Normal file
394
packages/designer/tests/document/node/props/prop.test.ts
Normal file
@ -0,0 +1,394 @@
|
||||
import '../../../fixtures/window';
|
||||
import { set } from '../../../utils';
|
||||
import { Editor } from '@ali/lowcode-editor-core';
|
||||
import { Props } from '../../../../src/document/node/props/props';
|
||||
import { Designer } from '../../../../src/designer/designer';
|
||||
import { Project } from '../../../../src/project/project';
|
||||
import { DocumentModel } from '../../../../src/document/document-model';
|
||||
import { Prop, isProp, isValidArrayIndex } from '../../../../src/document/node/props/prop';
|
||||
import { TransformStage } from '@ali/lowcode-types';
|
||||
import { delayObxTick } from '../../../utils';
|
||||
|
||||
const mockedOwner = {
|
||||
componentName: 'Div',
|
||||
};
|
||||
|
||||
const mockedPropsInst = {
|
||||
owner: mockedOwner,
|
||||
};
|
||||
mockedPropsInst.props = mockedPropsInst;
|
||||
|
||||
describe('Prop 类测试', () => {
|
||||
describe('基础类型', () => {
|
||||
let boolProp: Prop;
|
||||
let strProp: Prop;
|
||||
let numProp: Prop;
|
||||
let nullProp: Prop;
|
||||
let expProp: Prop;
|
||||
let slotProp: Prop;
|
||||
beforeEach(() => {
|
||||
boolProp = new Prop(mockedPropsInst, true, 'boolProp');
|
||||
strProp = new Prop(mockedPropsInst, 'haha', 'strProp');
|
||||
numProp = new Prop(mockedPropsInst, 1, 'numProp');
|
||||
nullProp = new Prop(mockedPropsInst, null, 'nullProp');
|
||||
expProp = new Prop(mockedPropsInst, { type: 'JSExpression', value: 'state.haha' }, 'expProp');
|
||||
// slotProp = new Prop(mockedPropsInst, { type: 'JSSlot', value: [{ componentName: 'Button' }] }, 'slotProp');
|
||||
});
|
||||
afterEach(() => {
|
||||
boolProp.purge();
|
||||
strProp.purge();
|
||||
numProp.purge();
|
||||
nullProp.purge();
|
||||
expProp.purge();
|
||||
// slotProp.purge();
|
||||
});
|
||||
|
||||
it('consturctor / getProps / getNode', () => {
|
||||
expect(boolProp.parent).toBe(mockedPropsInst);
|
||||
expect(boolProp.getProps()).toBe(mockedPropsInst);
|
||||
expect(boolProp.getNode()).toBe(mockedOwner);
|
||||
});
|
||||
|
||||
it('misc', () => {
|
||||
expect(boolProp.get('x', false)).toBeNull();
|
||||
expect(boolProp.maps).toBeNull();
|
||||
expect(boolProp.add()).toBeNull();
|
||||
|
||||
strProp.unset();
|
||||
strProp.add(2, true);
|
||||
strProp.set(1);
|
||||
|
||||
expect(numProp.set()).toBeNull();
|
||||
expect(numProp.has()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('getValue / getAsString / setValue', () => {
|
||||
expect(strProp.getValue()).toBe('haha');
|
||||
strProp.setValue('heihei');
|
||||
expect(strProp.getValue()).toBe('heihei');
|
||||
expect(strProp.getAsString()).toBe('heihei');
|
||||
|
||||
strProp.unset();
|
||||
expect(strProp.getValue()).toBeUndefined();
|
||||
});
|
||||
|
||||
it('code', () => {
|
||||
expect(expProp.code).toBe('state.haha');
|
||||
expect(boolProp.code).toBe('true');
|
||||
expect(strProp.code).toBe('"haha"');
|
||||
|
||||
expProp.code = 'state.heihei';
|
||||
expect(expProp.code).toBe('state.heihei');
|
||||
expect(expProp.getValue()).toEqual({
|
||||
type: 'JSExpression',
|
||||
value: 'state.heihei',
|
||||
});
|
||||
|
||||
boolProp.code = 'false';
|
||||
expect(boolProp.code).toBe('false');
|
||||
expect(boolProp.getValue()).toBe(false);
|
||||
|
||||
strProp.code = '"heihei"';
|
||||
expect(strProp.code).toBe('"heihei"');
|
||||
expect(strProp.getValue()).toBe('heihei');
|
||||
|
||||
// TODO: 不确定为什么会有这个分支
|
||||
strProp.code = 'state.a';
|
||||
expect(strProp.code).toBe('state.a');
|
||||
expect(strProp.getValue()).toEqual({
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
mock: 'heihei',
|
||||
});
|
||||
});
|
||||
|
||||
it('export', () => {
|
||||
expect(boolProp.export(TransformStage.Save)).toBe(true);
|
||||
expect(strProp.export(TransformStage.Save)).toBe('haha');
|
||||
expect(numProp.export(TransformStage.Save)).toBe(1);
|
||||
expect(nullProp.export(TransformStage.Save)).toBe('');
|
||||
expect(nullProp.export(TransformStage.Serilize)).toBe(null);
|
||||
expect(expProp.export(TransformStage.Save)).toEqual({
|
||||
type: 'JSExpression',
|
||||
value: 'state.haha',
|
||||
});
|
||||
|
||||
strProp.unset();
|
||||
expect(strProp.getValue()).toBeUndefined();
|
||||
expect(strProp.isUnset()).toBeTruthy();
|
||||
expect(strProp.export(TransformStage.Save)).toBeUndefined();
|
||||
|
||||
expect(
|
||||
new Prop(mockedPropsInst, false, '___condition___').export(TransformStage.Render),
|
||||
).toBeTruthy();
|
||||
// console.log(slotProp.export(TransformStage.Render));
|
||||
});
|
||||
|
||||
it('compare', () => {
|
||||
const newProp = new Prop(mockedPropsInst, 'haha');
|
||||
expect(strProp.compare(newProp)).toBe(0);
|
||||
expect(strProp.compare(expProp)).toBe(2);
|
||||
|
||||
newProp.unset();
|
||||
expect(strProp.compare(newProp)).toBe(2);
|
||||
strProp.unset();
|
||||
expect(strProp.compare(newProp)).toBe(0);
|
||||
});
|
||||
|
||||
it('isVirtual', () => {
|
||||
expect(new Prop(mockedPropsInst, 111, '!virtualProp')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('purge', () => {
|
||||
boolProp.purge();
|
||||
expect(boolProp.purged).toBeTruthy();
|
||||
boolProp.purge();
|
||||
});
|
||||
|
||||
it('迭代器 / map / forEach', () => {
|
||||
const mockedFn = jest.fn();
|
||||
for (let item of strProp) {
|
||||
mockedFn();
|
||||
}
|
||||
expect(mockedFn).not.toHaveBeenCalled();
|
||||
mockedFn.mockClear();
|
||||
|
||||
strProp.forEach(item => {
|
||||
mockedFn();
|
||||
});
|
||||
expect(mockedFn).not.toHaveBeenCalled();
|
||||
mockedFn.mockClear();
|
||||
|
||||
strProp.map(item => {
|
||||
mockedFn();
|
||||
});
|
||||
expect(mockedFn).not.toHaveBeenCalled();
|
||||
mockedFn.mockClear();
|
||||
});
|
||||
});
|
||||
|
||||
describe('复杂类型', () => {
|
||||
describe('items(map 类型)', () => {
|
||||
let prop: Prop;
|
||||
beforeEach(() => {
|
||||
prop = new Prop(mockedPropsInst, {
|
||||
a: 1,
|
||||
b: 'str',
|
||||
c: true,
|
||||
d: {
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
},
|
||||
z: {
|
||||
z1: 1,
|
||||
z2: 'str',
|
||||
},
|
||||
});
|
||||
});
|
||||
afterEach(() => {
|
||||
prop.purge();
|
||||
});
|
||||
|
||||
it('items / get', async () => {
|
||||
expect(prop.size).toBe(5);
|
||||
|
||||
expect(prop.get('a').getValue()).toBe(1);
|
||||
expect(prop.get('b').getValue()).toBe('str');
|
||||
expect(prop.get('c').getValue()).toBe(true);
|
||||
expect(prop.get('d').getValue()).toEqual({ type: 'JSExpression', value: 'state.a' });
|
||||
expect(prop.get('z').getValue()).toEqual({
|
||||
z1: 1,
|
||||
z2: 'str',
|
||||
});
|
||||
|
||||
|
||||
expect(prop.getPropValue('a')).toBe(1);
|
||||
prop.setPropValue('a', 2);
|
||||
expect(prop.getPropValue('a')).toBe(2);
|
||||
prop.clearPropValue('a');
|
||||
expect(prop.get('a')?.isUnset()).toBeTruthy();
|
||||
|
||||
expect(prop.get('z.z1')?.getValue()).toBe(1);
|
||||
expect(prop.get('z.z2')?.getValue()).toBe('str');
|
||||
|
||||
const fromStashProp = prop.get('l');
|
||||
const fromStashNestedProp = prop.get('m.m1');
|
||||
fromStashProp.setValue('fromStashProp');
|
||||
fromStashNestedProp?.setValue('fromStashNestedProp')
|
||||
|
||||
await delayObxTick();
|
||||
expect(prop.get('l').getValue()).toBe('fromStashProp');
|
||||
expect(prop.get('m.m1').getValue()).toBe('fromStashNestedProp');
|
||||
});
|
||||
|
||||
it('export', () => {
|
||||
// TODO: 需要访问一下才能触发构造 _items
|
||||
prop.items;
|
||||
expect(prop.export()).toEqual({
|
||||
a: 1,
|
||||
b: 'str',
|
||||
c: true,
|
||||
d: {
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
},
|
||||
z: {
|
||||
z1: 1,
|
||||
z2: 'str',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('compare', () => {
|
||||
const prop1 = new Prop(mockedPropsInst, { a: 1 });
|
||||
const prop2 = new Prop(mockedPropsInst, { b: 1 });
|
||||
expect(prop1.compare(prop2)).toBe(1);
|
||||
});
|
||||
|
||||
it('has / add / delete / deleteKey / remove', () => {
|
||||
expect(prop.has('a')).toBeTruthy();
|
||||
expect(prop.has('b')).toBeTruthy();
|
||||
expect(prop.has('c')).toBeTruthy();
|
||||
expect(prop.has('d')).toBeTruthy();
|
||||
expect(prop.has('z')).toBeTruthy();
|
||||
expect(prop.has('y')).toBeFalsy();
|
||||
|
||||
// 触发一下内部 maps 构造
|
||||
prop.items;
|
||||
expect(prop.has('z')).toBeTruthy();
|
||||
|
||||
expect(prop.add(1)).toBeNull();
|
||||
|
||||
prop.deleteKey('c');
|
||||
expect(prop.get('c', false)).toBeNull();
|
||||
prop.delete(prop.get('b'));
|
||||
expect(prop.get('b', false)).toBeNull();
|
||||
|
||||
prop.get('d')?.remove();
|
||||
expect(prop.get('d', false)).toBeNull();
|
||||
});
|
||||
|
||||
it('set', () => {
|
||||
prop.set('e', 1);
|
||||
expect(prop.get('e', false)?.getValue()).toBe(1);
|
||||
prop.set('a', 5);
|
||||
expect(prop.get('a', false)?.getValue()).toBe(5);
|
||||
});
|
||||
|
||||
it('迭代器 / map / forEach', () => {
|
||||
const mockedFn = jest.fn();
|
||||
for (let item of prop) {
|
||||
mockedFn();
|
||||
}
|
||||
expect(mockedFn).toHaveBeenCalledTimes(5);
|
||||
mockedFn.mockClear();
|
||||
|
||||
prop.forEach(item => {
|
||||
mockedFn();
|
||||
});
|
||||
expect(mockedFn).toHaveBeenCalledTimes(5);
|
||||
mockedFn.mockClear();
|
||||
|
||||
prop.map(item => {
|
||||
mockedFn();
|
||||
});
|
||||
expect(mockedFn).toHaveBeenCalledTimes(5);
|
||||
mockedFn.mockClear();
|
||||
});
|
||||
|
||||
it('dispose', () => {
|
||||
prop.dispose();
|
||||
|
||||
expect(prop._items).toBeNull();
|
||||
expect(prop._maps).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('items(list 类型)', () => {
|
||||
let prop: Prop;
|
||||
beforeEach(() => {
|
||||
prop = new Prop(mockedPropsInst, [1, true, 'haha']);
|
||||
});
|
||||
afterEach(() => {
|
||||
prop.purge();
|
||||
});
|
||||
|
||||
it('items / get', () => {
|
||||
expect(prop.size).toBe(3);
|
||||
|
||||
expect(prop.get(0).getValue()).toBe(1);
|
||||
expect(prop.get(1).getValue()).toBe(true);
|
||||
expect(prop.get(2).getValue()).toBe('haha');
|
||||
|
||||
expect(prop.getAsString()).toBe('');
|
||||
});
|
||||
|
||||
it('export', () => {
|
||||
// 触发构造
|
||||
prop.items;
|
||||
expect(prop.export()).toEqual([1, true, 'haha']);
|
||||
});
|
||||
|
||||
it('compare', () => {
|
||||
const prop1 = new Prop(mockedPropsInst, [1]);
|
||||
const prop2 = new Prop(mockedPropsInst, [2]);
|
||||
const prop3 = new Prop(mockedPropsInst, [1, 2]);
|
||||
expect(prop1.compare(prop2)).toBe(1);
|
||||
expect(prop1.compare(prop3)).toBe(2);
|
||||
});
|
||||
|
||||
it('set', () => {
|
||||
prop.set(0, 1);
|
||||
expect(prop.get(0, false)?.getValue()).toBe(1);
|
||||
// illegal
|
||||
// expect(prop.set(5, 1)).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('slotNode / setAsSlot', () => {
|
||||
const editor = new Editor();
|
||||
const designer = new Designer({ editor });
|
||||
const doc = new DocumentModel(designer.project, {
|
||||
componentName: 'Page',
|
||||
children: [{
|
||||
id: 'div',
|
||||
componentName: 'Div',
|
||||
}],
|
||||
});
|
||||
const div = doc.getNode('div');
|
||||
|
||||
const slotProp = new Prop(div?.getProps(), {
|
||||
type: 'JSSlot',
|
||||
value: [{
|
||||
componentName: 'Button'
|
||||
}],
|
||||
});
|
||||
|
||||
expect(slotProp.slotNode?.componentName).toBe('Slot');
|
||||
|
||||
// TODO: id 总是变,不好断言
|
||||
expect(slotProp.code.includes('Button')).toBeTruthy();
|
||||
|
||||
slotProp.export();
|
||||
|
||||
expect(slotProp.export().value[0].componentName).toBe('Button');
|
||||
expect(slotProp.export(TransformStage.Serilize).value[0].componentName).toBe('Button');
|
||||
|
||||
slotProp.purge();
|
||||
expect(slotProp.purged).toBeTruthy();
|
||||
slotProp.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
describe('其他导出函数', () => {
|
||||
it('isProp', () => {
|
||||
expect(isProp({ isProp: true })).toBeTruthy();
|
||||
});
|
||||
|
||||
it('isValidArrayIndex', () => {
|
||||
expect(isValidArrayIndex('1')).toBeTruthy();
|
||||
expect(isValidArrayIndex('1', 2)).toBeTruthy();
|
||||
expect(isValidArrayIndex('2', 1)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
244
packages/designer/tests/document/node/props/props.test.ts
Normal file
244
packages/designer/tests/document/node/props/props.test.ts
Normal file
@ -0,0 +1,244 @@
|
||||
|
||||
import '../../../fixtures/window';
|
||||
import { set } from '../../../utils';
|
||||
import { Editor } from '@ali/lowcode-editor-core';
|
||||
import { Props, getConvertedExtraKey, getOriginalExtraKey } from '../../../../src/document/node/props/props';
|
||||
import { Designer } from '../../../../src/designer/designer';
|
||||
import { Project } from '../../../../src/project/project';
|
||||
import { DocumentModel } from '../../../../src/document/document-model';
|
||||
import { Prop, isProp, isValidArrayIndex } from '../../../../src/document/node/props/props';
|
||||
import { TransformStage } from '@ali/lowcode-types';
|
||||
import { delayObxTick } from '../../../utils';
|
||||
|
||||
const mockedOwner = { componentName: 'Page' };
|
||||
|
||||
describe('Props 类测试', () => {
|
||||
let props: Props;
|
||||
beforeEach(() => {
|
||||
props = new Props(mockedOwner, {
|
||||
a: 1,
|
||||
b: 'str',
|
||||
c: true,
|
||||
d: {
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
},
|
||||
z: {
|
||||
z1: 1,
|
||||
z2: 'str',
|
||||
},
|
||||
}, { condition: true });
|
||||
});
|
||||
afterEach(() => {
|
||||
props.purge();
|
||||
});
|
||||
|
||||
it('getNode', () => {
|
||||
expect(props.getNode()).toBe(mockedOwner);
|
||||
});
|
||||
|
||||
it('items / get', async () => {
|
||||
expect(props.size).toBe(6);
|
||||
|
||||
expect(props.get('a').getValue()).toBe(1);
|
||||
expect(props.get('b').getValue()).toBe('str');
|
||||
expect(props.get('c').getValue()).toBe(true);
|
||||
expect(props.get('d').getValue()).toEqual({ type: 'JSExpression', value: 'state.a' });
|
||||
expect(props.get('z').getValue()).toEqual({
|
||||
z1: 1,
|
||||
z2: 'str',
|
||||
});
|
||||
|
||||
|
||||
expect(props.getPropValue('a')).toBe(1);
|
||||
props.setPropValue('a', 2);
|
||||
expect(props.getPropValue('a')).toBe(2);
|
||||
// props.clearPropValue('a');
|
||||
// expect(props.get('a')?.isUnset()).toBeTruthy();
|
||||
|
||||
expect(props.get('z.z1')?.getValue()).toBe(1);
|
||||
expect(props.get('z.z2')?.getValue()).toBe('str');
|
||||
|
||||
const fromStashProp = props.get('l', true);
|
||||
const fromStashNestedProp = props.get('m.m1', true);
|
||||
fromStashProp.setValue('fromStashProp');
|
||||
fromStashNestedProp?.setValue('fromStashNestedProp')
|
||||
|
||||
await delayObxTick();
|
||||
expect(props.get('l').getValue()).toBe('fromStashProp');
|
||||
expect(props.get('m.m1').getValue()).toBe('fromStashNestedProp');
|
||||
});
|
||||
|
||||
it('export', () => {
|
||||
expect(props.export()).toEqual({
|
||||
props: {
|
||||
a: 1,
|
||||
b: 'str',
|
||||
c: true,
|
||||
d: {
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
},
|
||||
z: {
|
||||
z1: 1,
|
||||
z2: 'str',
|
||||
},
|
||||
},
|
||||
extras: {
|
||||
condition: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(props.toData()).toEqual({
|
||||
a: 1,
|
||||
b: 'str',
|
||||
c: true,
|
||||
d: {
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
},
|
||||
z: {
|
||||
z1: 1,
|
||||
z2: 'str',
|
||||
},
|
||||
});
|
||||
|
||||
props.get('a')?.unset();
|
||||
expect(props.toData()).toEqual({
|
||||
a: undefined,
|
||||
b: 'str',
|
||||
c: true,
|
||||
d: {
|
||||
type: 'JSExpression',
|
||||
value: 'state.a',
|
||||
},
|
||||
z: {
|
||||
z1: 1,
|
||||
z2: 'str',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('import', () => {
|
||||
props.import({
|
||||
x: 1,
|
||||
y: true
|
||||
}, { loop: false });
|
||||
expect(props.export()).toEqual({
|
||||
props: {
|
||||
x: 1,
|
||||
y: true,
|
||||
},
|
||||
extras: {
|
||||
loop: false,
|
||||
}
|
||||
});
|
||||
|
||||
props.import();
|
||||
});
|
||||
|
||||
it('merge', async () => {
|
||||
props.merge({ x: 1 });
|
||||
|
||||
await delayObxTick();
|
||||
|
||||
expect(props.get('x')?.getValue()).toBe(1);
|
||||
});
|
||||
|
||||
it('has / add / delete / deleteKey / remove', () => {
|
||||
expect(props.has('a')).toBeTruthy();
|
||||
expect(props.has('b')).toBeTruthy();
|
||||
expect(props.has('c')).toBeTruthy();
|
||||
expect(props.has('d')).toBeTruthy();
|
||||
expect(props.has('z')).toBeTruthy();
|
||||
expect(props.has('y')).toBeFalsy();
|
||||
|
||||
props.add(1, 'newAdded');
|
||||
expect(props.has('newAdded')).toBeTruthy();
|
||||
|
||||
props.deleteKey('c');
|
||||
expect(props.get('c', false)).toBeNull();
|
||||
props.delete(props.get('b'));
|
||||
expect(props.get('b', false)).toBeNull();
|
||||
|
||||
props.get('d')?.remove();
|
||||
expect(props.get('d', false)).toBeNull();
|
||||
});
|
||||
|
||||
it('迭代器 / map / forEach', () => {
|
||||
const mockedFn = jest.fn();
|
||||
for (let item of props) {
|
||||
mockedFn();
|
||||
}
|
||||
expect(mockedFn).toHaveBeenCalledTimes(6);
|
||||
mockedFn.mockClear();
|
||||
|
||||
props.forEach(item => {
|
||||
mockedFn();
|
||||
});
|
||||
expect(mockedFn).toHaveBeenCalledTimes(6);
|
||||
mockedFn.mockClear();
|
||||
|
||||
props.map(item => {
|
||||
mockedFn();
|
||||
});
|
||||
expect(mockedFn).toHaveBeenCalledTimes(6);
|
||||
mockedFn.mockClear();
|
||||
|
||||
props.filter(item => {
|
||||
mockedFn();
|
||||
});
|
||||
expect(mockedFn).toHaveBeenCalledTimes(6);
|
||||
mockedFn.mockClear();
|
||||
});
|
||||
|
||||
it('purge', () => {
|
||||
props.purge();
|
||||
|
||||
expect(props.purged).toBeTruthy();
|
||||
});
|
||||
|
||||
it('empty items', () => {
|
||||
expect(new Props(mockedOwner).export()).toEqual({});
|
||||
});
|
||||
|
||||
describe('list 类型', () => {
|
||||
let props: Props;
|
||||
beforeEach(() => {
|
||||
props = new Props(mockedOwner, [1, true, 'haha'], { condition: true });
|
||||
});
|
||||
it('constructor', () => {
|
||||
props.purge();
|
||||
});
|
||||
|
||||
it('export', () => {
|
||||
expect(props.export().extras).toEqual({
|
||||
condition: true
|
||||
})
|
||||
});
|
||||
|
||||
it('import', () => {
|
||||
props.import([1], { loop: true });
|
||||
expect(props.export().extras).toEqual({
|
||||
loop: true
|
||||
});
|
||||
|
||||
props.items[0]?.unset();
|
||||
props.export();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('其他函数', () => {
|
||||
it('getConvertedExtraKey', () => {
|
||||
expect(getConvertedExtraKey()).toBe('');
|
||||
expect(getConvertedExtraKey('a')).toBe('___a___');
|
||||
expect(getConvertedExtraKey('a.b')).toBe('___a___b');
|
||||
});
|
||||
|
||||
it('getOriginalExtraKey', () => {
|
||||
expect(getOriginalExtraKey('___a___')).toBe('a');
|
||||
expect(getOriginalExtraKey('___a___b')).toBe('a.b');
|
||||
});
|
||||
});
|
||||
@ -221,10 +221,9 @@ export default {
|
||||
},
|
||||
],
|
||||
component: {
|
||||
isContainer: true,
|
||||
nestingRule: {
|
||||
parentWhitelist: 'Div',
|
||||
childWhitelist: 'Div',
|
||||
// parentWhitelist: 'Div',
|
||||
// childWhitelist: 'Div',
|
||||
},
|
||||
},
|
||||
supports: {},
|
||||
|
||||
@ -223,8 +223,8 @@ export default {
|
||||
component: {
|
||||
isContainer: true,
|
||||
nestingRule: {
|
||||
parentWhitelist: 'Div',
|
||||
childWhitelist: 'Div',
|
||||
// parentWhitelist: 'Div',
|
||||
// childWhitelist: 'Div',
|
||||
},
|
||||
},
|
||||
supports: {},
|
||||
|
||||
4
packages/designer/tests/fixtures/window.ts
vendored
4
packages/designer/tests/fixtures/window.ts
vendored
@ -15,4 +15,6 @@ Object.defineProperty(window, 'matchMedia', {
|
||||
Object.defineProperty(window, 'React', {
|
||||
writable: true,
|
||||
value: {},
|
||||
});
|
||||
});
|
||||
|
||||
window.scrollTo = () => {};
|
||||
@ -14,6 +14,7 @@ interface MockDocument extends Document {
|
||||
|
||||
|
||||
const eventsMap : Map<string, Set<Function>> = new Map<string, Set<Function>>();
|
||||
const mockRemoveAttribute = jest.fn();
|
||||
const mockAddEventListener = jest.fn((eventName: string, cb) => {
|
||||
if (!eventsMap.has(eventName)) {
|
||||
eventsMap.set(eventName, new Set([cb]));
|
||||
@ -45,6 +46,7 @@ const mockCreateElement = jest.fn((tagName) => {
|
||||
addEventListener: mockAddEventListener,
|
||||
removeEventListener: mockRemoveEventListener,
|
||||
triggerEventListener: mockTriggerEventListener,
|
||||
removeAttribute: mockRemoveAttribute,
|
||||
}
|
||||
})
|
||||
|
||||
@ -74,4 +76,36 @@ export function getMockWindow(doc?: MockDocument) {
|
||||
|
||||
export function clearEventsMap() {
|
||||
eventsMap.clear();
|
||||
}
|
||||
|
||||
export function getMockElement(tagName, options = {}) {
|
||||
const elem = document.createElement(tagName);
|
||||
let {
|
||||
width = 0,
|
||||
height = 0,
|
||||
top = 0,
|
||||
bottom = 0,
|
||||
left = 0,
|
||||
right = 0,
|
||||
} = options;
|
||||
elem.getBoundingClientRect = () => {
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
top,
|
||||
bottom,
|
||||
left,
|
||||
right,
|
||||
};
|
||||
};
|
||||
elem.setWidth = (newWidth) => {
|
||||
width = newWidth;
|
||||
};
|
||||
elem.setHeight = (newHeight) => {
|
||||
height = newHeight;
|
||||
};
|
||||
// console.log(elem.ownerDocument);
|
||||
// elem.ownerDocument = document;
|
||||
// elem.ownerDocument.defaultView = window;
|
||||
return elem;
|
||||
}
|
||||
@ -2,7 +2,7 @@ export function getMockRenderer() {
|
||||
return {
|
||||
isSimulatorRenderer: true,
|
||||
run() {
|
||||
console.log('renderer run');
|
||||
// console.log('renderer run');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="1.0.26-beta.0"></a>
|
||||
## [1.0.26-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.25-beta.1...v1.0.26-beta.0) (2020-12-22)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-editor-core
|
||||
|
||||
<a name="1.0.25-beta.1"></a>
|
||||
## [1.0.25-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.4...v1.0.25-beta.1) (2020-12-15)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-editor-core
|
||||
|
||||
<a name="1.0.24-beta.4"></a>
|
||||
## [1.0.24-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.3...v1.0.24-beta.4) (2020-12-14)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-editor-core",
|
||||
"version": "1.0.24-beta.4",
|
||||
"version": "1.0.26-beta.0",
|
||||
"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": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-utils": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-types": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-utils": "^1.0.26-beta.0",
|
||||
"@alifd/next": "^1.19.16",
|
||||
"@recore/obx": "^1.0.9",
|
||||
"@recore/obx-react": "^1.0.8",
|
||||
|
||||
@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="1.0.26-beta.0"></a>
|
||||
## [1.0.26-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.25-beta.1...v1.0.26-beta.0) (2020-12-22)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-editor-preset-general
|
||||
|
||||
<a name="1.0.25-beta.1"></a>
|
||||
## [1.0.25-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.4...v1.0.25-beta.1) (2020-12-15)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-editor-preset-general
|
||||
|
||||
<a name="1.0.24-beta.4"></a>
|
||||
## [1.0.24-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.3...v1.0.24-beta.4) (2020-12-14)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-editor-preset-general",
|
||||
"version": "1.0.24-beta.4",
|
||||
"version": "1.0.26-beta.0",
|
||||
"description": "Ali General Editor Preset",
|
||||
"main": "lib/index.js",
|
||||
"files": [
|
||||
@ -14,12 +14,12 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ali/lowcode-editor-core": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-editor-skeleton": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-plugin-designer": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-plugin-outline-pane": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-types": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-utils": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-editor-core": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-editor-skeleton": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-plugin-designer": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-plugin-outline-pane": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-types": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-utils": "^1.0.26-beta.0",
|
||||
"@alifd/next": "^1.19.12",
|
||||
"@alife/theme-lowcode-dark": "^0.1.0",
|
||||
"@alife/theme-lowcode-light": "^0.1.0",
|
||||
|
||||
@ -3,6 +3,34 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="1.0.26-beta.0"></a>
|
||||
## [1.0.26-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.25-beta.1...v1.0.26-beta.0) (2020-12-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复 overridePropsConfigure 参数为数组时的逻辑 ([4e58e09](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/4e58e09))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 支持 build sourceMap, 方便用户调试 ([6bf75cd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6bf75cd))
|
||||
* 支持用户修改 builtinComponentActions ([bc183d1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/bc183d1))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.25-beta.1"></a>
|
||||
## [1.0.25-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.4...v1.0.25-beta.1) (2020-12-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复大纲树和组件面板来回点击异常 ([8b9a6ec](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/8b9a6ec))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.24-beta.4"></a>
|
||||
## [1.0.24-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.3...v1.0.24-beta.4) (2020-12-14)
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{
|
||||
"sourceMap": true,
|
||||
"plugins": [
|
||||
[
|
||||
"build-plugin-component",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-editor-preset-vision",
|
||||
"version": "1.0.24-beta.4",
|
||||
"version": "1.0.26-beta.0",
|
||||
"description": "Vision Polyfill for Ali lowCode engine",
|
||||
"main": "lib/index.js",
|
||||
"private": true,
|
||||
@ -17,13 +17,13 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ali/lowcode-designer": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-editor-core": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-designer": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-editor-core": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-editor-setters": "^1.0.22",
|
||||
"@ali/lowcode-editor-skeleton": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-plugin-designer": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-plugin-outline-pane": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-utils": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-editor-skeleton": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-plugin-designer": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-plugin-outline-pane": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-utils": "^1.0.26-beta.0",
|
||||
"@ali/ve-i18n-util": "^2.0.0",
|
||||
"@ali/ve-icons": "^4.1.9",
|
||||
"@ali/ve-less-variables": "2.0.3",
|
||||
|
||||
@ -127,7 +127,11 @@ registerMetadataTransducer(
|
||||
const override = Overrides[componentName]?.override;
|
||||
if (override) {
|
||||
if (Array.isArray(override)) {
|
||||
metadata.configure.combined = override;
|
||||
// 替换 #props,其他暂时忽略
|
||||
const idx = metadata.configure.combined?.findIndex(item => item.name === '#props');
|
||||
if (idx > -1) {
|
||||
metadata.configure.combined[idx].items = override;
|
||||
}
|
||||
} else {
|
||||
let l = top.length;
|
||||
let item;
|
||||
|
||||
@ -5,7 +5,12 @@ 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 {
|
||||
registerMetadataTransducer,
|
||||
addBuiltinComponentAction,
|
||||
removeBuiltinComponentAction,
|
||||
modifyBuiltinComponentAction,
|
||||
} 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';
|
||||
@ -68,8 +73,16 @@ const modules = {
|
||||
Prop,
|
||||
};
|
||||
|
||||
const designerHelper = {
|
||||
registerMetadataTransducer,
|
||||
addBuiltinComponentAction,
|
||||
removeBuiltinComponentAction,
|
||||
// modifyBuiltinComponentAction,
|
||||
};
|
||||
|
||||
const VisualEngine = {
|
||||
designer,
|
||||
designerHelper,
|
||||
editor,
|
||||
skeleton,
|
||||
/**
|
||||
@ -121,6 +134,7 @@ export default VisualEngine;
|
||||
|
||||
export {
|
||||
designer,
|
||||
designerHelper,
|
||||
editor,
|
||||
skeleton,
|
||||
/**
|
||||
|
||||
@ -206,9 +206,9 @@ const dockPane = Object.assign(skeleton.leftArea, {
|
||||
const f = (_: any, dock: any) => {
|
||||
fn(dock);
|
||||
};
|
||||
editor.on('skeleton.panel-dock.show', f);
|
||||
editor.on('skeleton.panel-dock.active', f);
|
||||
return () => {
|
||||
editor.removeListener('skeleton.panel-dock.show', f);
|
||||
editor.removeListener('skeleton.panel-dock.active', f);
|
||||
};
|
||||
},
|
||||
/**
|
||||
@ -218,9 +218,9 @@ const dockPane = Object.assign(skeleton.leftArea, {
|
||||
const f = (_: any, dock: any) => {
|
||||
fn(dock);
|
||||
};
|
||||
editor.on('skeleton.panel-dock.hide', f);
|
||||
editor.on('skeleton.panel-dock.unactive', f);
|
||||
return () => {
|
||||
editor.removeListener('skeleton.panel-dock.hide', f);
|
||||
editor.removeListener('skeleton.panel-dock.unactive', f);
|
||||
};
|
||||
},
|
||||
/**
|
||||
|
||||
@ -3,6 +3,25 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="1.0.26-beta.0"></a>
|
||||
## [1.0.26-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.25-beta.1...v1.0.26-beta.0) (2020-12-22)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-editor-skeleton
|
||||
|
||||
<a name="1.0.25-beta.1"></a>
|
||||
## [1.0.25-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.4...v1.0.25-beta.1) (2020-12-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复大纲树和组件面板来回点击异常 ([8b9a6ec](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/8b9a6ec))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.24-beta.4"></a>
|
||||
## [1.0.24-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.3...v1.0.24-beta.4) (2020-12-14)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-editor-skeleton",
|
||||
"version": "1.0.24-beta.4",
|
||||
"version": "1.0.26-beta.0",
|
||||
"description": "alibaba lowcode editor skeleton",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
@ -19,10 +19,10 @@
|
||||
"editor"
|
||||
],
|
||||
"dependencies": {
|
||||
"@ali/lowcode-designer": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-editor-core": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-types": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-utils": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-designer": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-editor-core": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-types": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-utils": "^1.0.26-beta.0",
|
||||
"@ali/ve-icons": "latest",
|
||||
"@ali/ve-less-variables": "^2.0.0",
|
||||
"@alifd/next": "^1.20.12",
|
||||
|
||||
@ -162,6 +162,11 @@ export default class Panel implements IWidget {
|
||||
if (flag) {
|
||||
this._actived = true;
|
||||
this.parent?.active(this);
|
||||
if (this.parent.name === 'leftFloatArea') {
|
||||
this.skeleton.leftFixedArea.container.unactiveAll();
|
||||
} else if (this.parent.name === 'leftFixedArea') {
|
||||
this.skeleton.leftFloatArea.container.unactiveAll();
|
||||
}
|
||||
if (!this.inited) {
|
||||
this.inited = true;
|
||||
}
|
||||
|
||||
@ -79,6 +79,10 @@ export default class WidgetContainer<T extends WidgetItem = any, G extends Widge
|
||||
}
|
||||
}
|
||||
|
||||
unactiveAll() {
|
||||
Object.keys(this.maps).forEach(name => this.unactive(name));
|
||||
}
|
||||
|
||||
add(item: T | G): T {
|
||||
item = this.handle(item);
|
||||
const origin = this.get(item.name);
|
||||
|
||||
@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="1.0.26-beta.0"></a>
|
||||
## [1.0.26-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.25-beta.1...v1.0.26-beta.0) (2020-12-22)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-ignitor
|
||||
|
||||
<a name="1.0.25-beta.1"></a>
|
||||
## [1.0.25-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.4...v1.0.25-beta.1) (2020-12-15)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-ignitor
|
||||
|
||||
<a name="1.0.24-beta.4"></a>
|
||||
## [1.0.24-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.3...v1.0.24-beta.4) (2020-12-14)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-ignitor",
|
||||
"version": "1.0.24-beta.4",
|
||||
"version": "1.0.26-beta.0",
|
||||
"description": "点火器,bootstrap lce project",
|
||||
"main": "lib/index.js",
|
||||
"private": true,
|
||||
|
||||
@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="1.0.26-beta.0"></a>
|
||||
## [1.0.26-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.25-beta.1...v1.0.26-beta.0) (2020-12-22)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-designer
|
||||
|
||||
<a name="1.0.25-beta.1"></a>
|
||||
## [1.0.25-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.4...v1.0.25-beta.1) (2020-12-15)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-designer
|
||||
|
||||
<a name="1.0.24-beta.4"></a>
|
||||
## [1.0.24-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.3...v1.0.24-beta.4) (2020-12-14)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-plugin-designer",
|
||||
"version": "1.0.24-beta.4",
|
||||
"version": "1.0.26-beta.0",
|
||||
"description": "alibaba lowcode editor designer plugin",
|
||||
"files": [
|
||||
"es",
|
||||
@ -20,8 +20,8 @@
|
||||
],
|
||||
"author": "xiayang.xy",
|
||||
"dependencies": {
|
||||
"@ali/lowcode-designer": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-editor-core": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-designer": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-editor-core": "^1.0.26-beta.0",
|
||||
"react": "^16.8.1",
|
||||
"react-dom": "^16.8.1"
|
||||
},
|
||||
|
||||
@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="1.0.26-beta.0"></a>
|
||||
## [1.0.26-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.25-beta.1...v1.0.26-beta.0) (2020-12-22)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.25-beta.1"></a>
|
||||
## [1.0.25-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.4...v1.0.25-beta.1) (2020-12-15)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-plugin-outline-pane
|
||||
|
||||
<a name="1.0.24-beta.4"></a>
|
||||
## [1.0.24-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.3...v1.0.24-beta.4) (2020-12-14)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-plugin-outline-pane",
|
||||
"version": "1.0.24-beta.4",
|
||||
"version": "1.0.26-beta.0",
|
||||
"description": "Outline pane for Ali lowCode engine",
|
||||
"files": [
|
||||
"es",
|
||||
@ -14,10 +14,10 @@
|
||||
"test:snapshot": "ava --update-snapshots"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ali/lowcode-designer": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-editor-core": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-types": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-utils": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-designer": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-editor-core": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-types": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-utils": "^1.0.26-beta.0",
|
||||
"@alifd/next": "^1.19.16",
|
||||
"classnames": "^2.2.6",
|
||||
"react": "^16",
|
||||
|
||||
@ -3,7 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="1.0.24-beta.4"></a>
|
||||
<a name="1.0.26-beta.0"></a>
|
||||
## [1.0.26-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.25-beta.1...v1.0.26-beta.0) (2020-12-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* rax perf ([3abe2ab](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/3abe2ab))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.25-beta.1"></a>
|
||||
## [1.0.25-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.4...v1.0.25-beta.1) (2020-12-15)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-rax-renderer
|
||||
|
||||
<a name="1.0.24-beta.4"></a>
|
||||
## [1.0.24-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.3...v1.0.24-beta.4) (2020-12-14)
|
||||
|
||||
|
||||
@ -11,7 +30,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-rax-renderer
|
||||
|
||||
<a name="1.0.24-beta.3"></a>
|
||||
<a name="1.0.24-beta.3"></a>
|
||||
## [1.0.24-beta.3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.2...v1.0.24-beta.3) (2020-12-11)
|
||||
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-rax-renderer",
|
||||
"version": "1.0.24-beta.4",
|
||||
"version": "1.0.26-beta.0",
|
||||
"description": "Rax renderer for Ali lowCode engine",
|
||||
"main": "lib/index.js",
|
||||
"module": "lib/index.js",
|
||||
@ -37,7 +37,7 @@
|
||||
"@ali/bzb-request": "2.6.1",
|
||||
"@ali/lib-mtop": "^2.5.1",
|
||||
"@ali/lowcode-datasource-engine": "^1.0.22",
|
||||
"@ali/lowcode-utils": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-utils": "^1.0.26-beta.0",
|
||||
"@ali/ui-table": "^1.0.1-beta.6",
|
||||
"classnames": "^2.2.6",
|
||||
"debug": "^4.1.1",
|
||||
|
||||
11
packages/rax-render/src/comp/Div.ts
Normal file
11
packages/rax-render/src/comp/Div.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { createElement, PureComponent } from 'rax';
|
||||
|
||||
export default class DivView extends PureComponent {
|
||||
static displayName = 'Div';
|
||||
|
||||
static version = '0.0.0';
|
||||
|
||||
render(): any {
|
||||
return createElement('div', this.props);
|
||||
}
|
||||
}
|
||||
@ -23,6 +23,7 @@ import {
|
||||
getFileCssName,
|
||||
} from '../utils';
|
||||
import VisualDom from '../comp/visualDom';
|
||||
import Div from '../comp/Div';
|
||||
import AppContext from '../context/appContext';
|
||||
|
||||
import compWrapper from '../hoc/compWrapper';
|
||||
@ -248,185 +249,214 @@ export default class BaseEngine extends Component {
|
||||
// parentInfo 父组件的信息,包含schema和Comp
|
||||
// idx 若为循环渲染的循环Index
|
||||
__createVirtualDom = (schema, self, parentInfo, idx) => {
|
||||
if (!schema) return null;
|
||||
// rax text prop 兼容处理
|
||||
if (schema.componentName === 'Text') {
|
||||
if (typeof schema.props.text === 'string') {
|
||||
schema = { ...schema };
|
||||
schema.children = [schema.props.text];
|
||||
}
|
||||
}
|
||||
|
||||
const { __appHelper: appHelper, __components: components = {}, __componentsMap: componentsMap = {} } = this.props || {};
|
||||
const { engine } = this.context || {};
|
||||
if (isJSExpression(schema)) {
|
||||
return parseExpression(schema, self);
|
||||
}
|
||||
if (typeof schema === 'string') return schema;
|
||||
if (typeof schema === 'number' || typeof schema === 'boolean') {
|
||||
return schema.toString();
|
||||
}
|
||||
if (Array.isArray(schema)) {
|
||||
if (schema.length === 1) return this.__createVirtualDom(schema[0], self, parentInfo);
|
||||
return schema.map((item, idx) => this.__createVirtualDom(item, self, parentInfo, item && item.__ctx && item.__ctx.lunaKey ? '' : idx));
|
||||
}
|
||||
try {
|
||||
if (!schema) return null;
|
||||
if (schema.componentName === 'Text') { // 这个是不是不应该在这里处理
|
||||
if (typeof schema.props.text === 'string') {
|
||||
schema = { ...schema };
|
||||
schema.children = [schema.props.text];
|
||||
}
|
||||
}
|
||||
|
||||
// 解析占位组件
|
||||
if (schema.componentName === 'Flagment' && schema.children) {
|
||||
const tarChildren = isJSExpression(schema.children) ? parseExpression(schema.children, self) : schema.children;
|
||||
return this.__createVirtualDom(tarChildren, self, parentInfo);
|
||||
}
|
||||
const { __appHelper: appHelper, __components: components = {} } = this.props || {};
|
||||
|
||||
if (schema.$$typeof) {
|
||||
return schema;
|
||||
}
|
||||
if (!isSchema(schema)) return null;
|
||||
let Comp = components[schema.componentName] || engine.getNotFoundComponent();
|
||||
if (isJSExpression(schema)) {
|
||||
return parseExpression(schema, self);
|
||||
}
|
||||
if (isJSSlot(schema)) {
|
||||
return this.__createVirtualDom(schema.value, self, parentInfo);
|
||||
}
|
||||
if (typeof schema === 'string') return schema;
|
||||
if (typeof schema === 'number' || typeof schema === 'boolean') {
|
||||
return schema.toString();
|
||||
}
|
||||
if (Array.isArray(schema)) {
|
||||
if (schema.length === 1) return this.__createVirtualDom(schema[0], self, parentInfo);
|
||||
return schema.map((item, idy) => this.__createVirtualDom(item, self, parentInfo, item && item.__ctx && item.__ctx.lunaKey ? '' : idy));
|
||||
}
|
||||
// FIXME
|
||||
const _children = this.getSchemaChildren(schema);
|
||||
// 解析占位组件
|
||||
if (schema.componentName === 'Flagment' && _children) {
|
||||
const tarChildren = isJSExpression(_children) ? parseExpression(_children, self) : _children;
|
||||
return this.__createVirtualDom(tarChildren, self, parentInfo);
|
||||
}
|
||||
|
||||
if (schema.hidden) {
|
||||
return null;
|
||||
}
|
||||
if (schema.$$typeof) {
|
||||
return schema;
|
||||
}
|
||||
if (!isSchema(schema)) return null;
|
||||
let Comp = components[schema.componentName] || engine.getNotFoundComponent();
|
||||
|
||||
if (schema.loop !== undefined) {
|
||||
return this.__createLoopVirtualDom(
|
||||
{
|
||||
...schema,
|
||||
loop: parseData(schema.loop, self),
|
||||
},
|
||||
if (schema.hidden) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (schema.loop != null) {
|
||||
const loop = parseData(schema.loop, self);
|
||||
if ((Array.isArray(loop) && loop.length > 0) || isJSExpression(loop)) {
|
||||
return this.__createLoopVirtualDom(
|
||||
{
|
||||
...schema,
|
||||
loop,
|
||||
},
|
||||
self,
|
||||
parentInfo,
|
||||
idx,
|
||||
);
|
||||
}
|
||||
}
|
||||
const condition = schema.condition == null ? true : parseData(schema.condition, self);
|
||||
if (!condition) return null;
|
||||
|
||||
let scopeKey = '';
|
||||
// 判断组件是否需要生成scope,且只生成一次,挂在this.__compScopes上
|
||||
if (Comp.generateScope) {
|
||||
const key = parseExpression(schema.props.key, self);
|
||||
if (key) {
|
||||
// 如果组件自己设置key则使用组件自己的key
|
||||
scopeKey = key;
|
||||
} else if (!schema.__ctx) {
|
||||
// 在生产环境schema没有__ctx上下文,需要手动生成一个lunaKey
|
||||
schema.__ctx = {
|
||||
lunaKey: `luna${++scopeIdx}`,
|
||||
};
|
||||
scopeKey = schema.__ctx.lunaKey;
|
||||
} else {
|
||||
// 需要判断循环的情况
|
||||
scopeKey = schema.__ctx.lunaKey + (idx !== undefined ? `_${idx}` : '');
|
||||
}
|
||||
if (!this.__compScopes[scopeKey]) {
|
||||
this.__compScopes[scopeKey] = Comp.generateScope(this, schema);
|
||||
}
|
||||
}
|
||||
// 如果组件有设置scope,需要为组件生成一个新的scope上下文
|
||||
if (scopeKey && this.__compScopes[scopeKey]) {
|
||||
const compSelf = { ...this.__compScopes[scopeKey] };
|
||||
compSelf.__proto__ = self;
|
||||
self = compSelf;
|
||||
}
|
||||
|
||||
// 容器类组件的上下文通过props传递,避免context传递带来的嵌套问题
|
||||
const otherProps = isFileSchema(schema)
|
||||
? {
|
||||
__schema: schema,
|
||||
__appHelper: appHelper,
|
||||
__components: components,
|
||||
}
|
||||
: {};
|
||||
if (engine && engine.props.designMode) {
|
||||
otherProps.__designMode = engine.props.designMode;
|
||||
}
|
||||
const componentInfo = {};
|
||||
const props =
|
||||
this.__parseProps(schema.props, self, '', {
|
||||
schema,
|
||||
Comp,
|
||||
componentInfo: {
|
||||
...componentInfo,
|
||||
props: transformArrayToMap(componentInfo.props, 'name'),
|
||||
},
|
||||
}) || {};
|
||||
// 对于可以获取到ref的组件做特殊处理
|
||||
if (!acceptsRef(Comp)) {
|
||||
Comp = compWrapper(Comp);
|
||||
}
|
||||
// if (acceptsRef(Comp)) {
|
||||
otherProps.ref = (ref) => {
|
||||
this.$(props.fieldId, ref); // 收集ref
|
||||
const refProps = props.ref;
|
||||
if (refProps && typeof refProps === 'string') {
|
||||
this[refProps] = ref;
|
||||
}
|
||||
ref && engine && engine.props.onCompGetRef(schema, ref);
|
||||
};
|
||||
// }
|
||||
// scope需要传入到组件上
|
||||
if (scopeKey && this.__compScopes[scopeKey]) {
|
||||
props.__scope = this.__compScopes[scopeKey];
|
||||
}
|
||||
// FIXME 这里清除 key 是为了避免循环渲染中更改 key 导致的渲染重复
|
||||
props.key = '';
|
||||
if (schema.__ctx && schema.__ctx.lunaKey) {
|
||||
if (!isFileSchema(schema)) {
|
||||
engine && engine.props.onCompGetCtx(schema, self);
|
||||
}
|
||||
props.key = props.key || `${schema.__ctx.lunaKey}_${schema.__ctx.idx || 0}_${idx !== undefined ? idx : ''}`;
|
||||
} else if (typeof idx === 'number' && !props.key) {
|
||||
props.key = idx;
|
||||
}
|
||||
|
||||
props.__id = schema.id;
|
||||
if (!props.key) {
|
||||
props.key = props.__id;
|
||||
}
|
||||
|
||||
let child = null;
|
||||
if (/*!isFileSchema(schema) && */!!_children) {
|
||||
child = this.__createVirtualDom(
|
||||
isJSExpression(_children) ? parseExpression(_children, self) : _children,
|
||||
self,
|
||||
{
|
||||
schema,
|
||||
Comp,
|
||||
},
|
||||
);
|
||||
}
|
||||
const renderComp = (props) => engine.createElement(Comp, props, child);
|
||||
// 设计模式下的特殊处理
|
||||
if (engine && [DESIGN_MODE.EXTEND, DESIGN_MODE.BORDER].includes(engine.props.designMode)) {
|
||||
// 对于overlay,dialog等组件为了使其在设计模式下显示,外层需要增加一个div容器
|
||||
if (OVERLAY_LIST.includes(schema.componentName)) {
|
||||
const { ref, ...overlayProps } = otherProps;
|
||||
return (
|
||||
<Div ref={ref} __designMode={engine.props.designMode}>
|
||||
{renderComp({ ...props, ...overlayProps })}
|
||||
</Div>
|
||||
);
|
||||
}
|
||||
// 虚拟dom显示
|
||||
if (componentInfo && componentInfo.parentRule) {
|
||||
const parentList = componentInfo.parentRule.split(',');
|
||||
const { schema: parentSchema, Comp: parentComp } = parentInfo;
|
||||
if (
|
||||
!parentList.includes(parentSchema.componentName) ||
|
||||
parentComp !== components[parentSchema.componentName]
|
||||
) {
|
||||
props.__componentName = schema.componentName;
|
||||
Comp = VisualDom;
|
||||
} else {
|
||||
// 若虚拟dom在正常的渲染上下文中,就不显示设计模式了
|
||||
props.__disableDesignMode = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return renderComp({ ...props, ...otherProps });
|
||||
} catch (e) {
|
||||
return engine.createElement(engine.getFaultComponent(), {
|
||||
error: e,
|
||||
schema,
|
||||
self,
|
||||
parentInfo,
|
||||
idx,
|
||||
);
|
||||
});
|
||||
}
|
||||
const condition = schema.condition === undefined ? true : parseData(schema.condition, self);
|
||||
if (!condition) return null;
|
||||
};
|
||||
|
||||
let scopeKey = '';
|
||||
// 判断组件是否需要生成scope,且只生成一次,挂在this.__compScopes上
|
||||
if (Comp.generateScope) {
|
||||
const key = parseExpression(schema.props.key, self);
|
||||
if (key) {
|
||||
// 如果组件自己设置key则使用组件自己的key
|
||||
scopeKey = key;
|
||||
} else if (!schema.__ctx) {
|
||||
// 在生产环境schema没有__ctx上下文,需要手动生成一个lunaKey
|
||||
schema.__ctx = {
|
||||
lunaKey: `luna${++scopeIdx}`,
|
||||
};
|
||||
scopeKey = schema.__ctx.lunaKey;
|
||||
} else {
|
||||
// 需要判断循环的情况
|
||||
scopeKey = schema.__ctx.lunaKey + (idx !== undefined ? `_${idx}` : '');
|
||||
}
|
||||
if (!this.__compScopes[scopeKey]) {
|
||||
this.__compScopes[scopeKey] = Comp.generateScope(this, schema);
|
||||
}
|
||||
getSchemaChildren = (schema) => {
|
||||
if (!schema || !schema.props) {
|
||||
return schema?.children;
|
||||
}
|
||||
// 如果组件有设置scope,需要为组件生成一个新的scope上下文
|
||||
if (scopeKey && this.__compScopes[scopeKey]) {
|
||||
const compSelf = { ...this.__compScopes[scopeKey] };
|
||||
compSelf.__proto__ = self;
|
||||
self = compSelf;
|
||||
if (!schema.children) return schema.props.children;
|
||||
if (!schema.props.children) return schema.children;
|
||||
let _children = [].concat(schema.children);
|
||||
if (Array.isArray(schema.props.children)) {
|
||||
_children = _children.concat(schema.props.children);
|
||||
} else {
|
||||
_children.push(schema.props.children);
|
||||
}
|
||||
|
||||
// 容器类组件的上下文通过props传递,避免context传递带来的嵌套问题
|
||||
const otherProps = isFileSchema(schema)
|
||||
? {
|
||||
__schema: schema,
|
||||
__appHelper: appHelper,
|
||||
__components: components,
|
||||
}
|
||||
: {};
|
||||
if (engine && engine.props.designMode) {
|
||||
otherProps.__designMode = engine.props.designMode;
|
||||
}
|
||||
const componentInfo = componentsMap[schema.componentName] || {};
|
||||
const props = this.__parseProps(schema.props, self, '', {
|
||||
schema,
|
||||
Comp,
|
||||
componentInfo: {
|
||||
...componentInfo,
|
||||
props: transformArrayToMap(componentInfo.props, 'name'),
|
||||
},
|
||||
});
|
||||
// 对于可以获取到ref的组件做特殊处理
|
||||
if (!acceptsRef(Comp)) {
|
||||
Comp = compWrapper(Comp);
|
||||
}
|
||||
otherProps.ref = (ref) => {
|
||||
this.$(props.fieldId, ref); // 收集ref
|
||||
const refProps = props.ref;
|
||||
if (refProps && typeof refProps === 'string') {
|
||||
this[refProps] = ref;
|
||||
}
|
||||
engine && engine.props.onCompGetRef(schema, ref);
|
||||
};
|
||||
|
||||
// scope需要传入到组件上
|
||||
if (scopeKey && this.__compScopes[scopeKey]) {
|
||||
props.__scope = this.__compScopes[scopeKey];
|
||||
}
|
||||
if (schema.__ctx && schema.__ctx.lunaKey) {
|
||||
if (!isFileSchema(schema)) {
|
||||
engine && engine.props.onCompGetCtx(schema, self);
|
||||
}
|
||||
props.key = props.key || `${schema.__ctx.lunaKey}_${schema.__ctx.idx || 0}_${idx !== undefined ? idx : ''}`;
|
||||
} else if (typeof idx === 'number' && !props.key) {
|
||||
props.key = idx;
|
||||
}
|
||||
props.__id = schema.id;
|
||||
|
||||
if (!isFileSchema(schema) && schema.children) {
|
||||
this.__createVirtualDom(
|
||||
isJSExpression(schema.children) ? parseExpression(schema.children, self) : schema.children,
|
||||
self,
|
||||
{
|
||||
schema,
|
||||
Comp,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const renderComp = (props) => engine.createElement(
|
||||
Comp,
|
||||
props,
|
||||
(!isFileSchema(schema) &&
|
||||
!!schema.children &&
|
||||
this.__createVirtualDom(
|
||||
isJSExpression(schema.children) ? parseExpression(schema.children, self) : schema.children,
|
||||
self,
|
||||
{
|
||||
schema,
|
||||
Comp,
|
||||
},
|
||||
))
|
||||
|| null,
|
||||
);
|
||||
// 设计模式下的特殊处理
|
||||
if (engine && [DESIGN_MODE.EXTEND, DESIGN_MODE.BORDER].includes(engine.props.designMode)) {
|
||||
// 对于overlay,dialog等组件为了使其在设计模式下显示,外层需要增加一个div容器
|
||||
if (OVERLAY_LIST.includes(schema.componentName)) {
|
||||
const { ref, ...overlayProps } = otherProps;
|
||||
return (
|
||||
<div ref={ref} __designMode={engine.props.designMode}>
|
||||
{renderComp({ ...props, ...overlayProps })}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// 虚拟dom显示
|
||||
if (componentInfo && componentInfo.parentRule) {
|
||||
const parentList = componentInfo.parentRule.split(',');
|
||||
const { schema: parentSchema, Comp: parentComp } = parentInfo;
|
||||
if (!parentList.includes(parentSchema.componentName) || parentComp !== components[parentSchema.componentName]) {
|
||||
props.__componentName = schema.componentName;
|
||||
Comp = VisualDom;
|
||||
} else {
|
||||
// 若虚拟dom在正常的渲染上下文中,就不显示设计模式了
|
||||
props.__disableDesignMode = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return renderComp({ ...props, ...otherProps });
|
||||
return _children;
|
||||
};
|
||||
|
||||
__createLoopVirtualDom = (schema, self, parentInfo, idx) => {
|
||||
|
||||
@ -3,6 +3,31 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="1.0.26-beta.0"></a>
|
||||
## [1.0.26-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.25-beta.1...v1.0.26-beta.0) (2020-12-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* requestHandlersMap 没有加到 appContext 里 ([a8d43c3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a8d43c3))
|
||||
* simulator-renderer 补充丢失代码 ([67dd7e2](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/67dd7e2))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 支持 build sourceMap, 方便用户调试 ([6bf75cd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6bf75cd))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.25-beta.1"></a>
|
||||
## [1.0.25-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.4...v1.0.25-beta.1) (2020-12-15)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-rax-simulator-renderer
|
||||
|
||||
<a name="1.0.24-beta.4"></a>
|
||||
## [1.0.24-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.3...v1.0.24-beta.4) (2020-12-14)
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{
|
||||
"sourceMap": true,
|
||||
"plugins": [
|
||||
[
|
||||
"build-plugin-component",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-rax-simulator-renderer",
|
||||
"version": "1.0.24-beta.4",
|
||||
"version": "1.0.26-beta.0",
|
||||
"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": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-rax-renderer": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-types": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-utils": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-designer": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-rax-renderer": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-types": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-utils": "^1.0.26-beta.0",
|
||||
"@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@1.0.24-beta.3/build/index.html"
|
||||
"homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-rax-simulator-renderer@1.0.25-beta.1/build/index.html"
|
||||
}
|
||||
|
||||
@ -257,6 +257,9 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
|
||||
// sync designMode
|
||||
this._designMode = host.designMode;
|
||||
|
||||
// sync requestHandlersMap
|
||||
this._requestHandlersMap = host.requestHandlersMap;
|
||||
|
||||
// sync device
|
||||
this._device = host.device;
|
||||
|
||||
@ -318,6 +321,7 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
|
||||
},
|
||||
},
|
||||
constants: {},
|
||||
requestHandlersMap: this._requestHandlersMap,
|
||||
};
|
||||
host.injectionConsumer.consume((data) => {
|
||||
// sync utils, i18n, contants,... config
|
||||
@ -357,6 +361,10 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
|
||||
@computed get device() {
|
||||
return this._device;
|
||||
}
|
||||
@obx.ref private _requestHandlersMap = null;
|
||||
@computed get requestHandlersMap(): any {
|
||||
return this._requestHandlersMap;
|
||||
}
|
||||
@obx.ref private _componentsMap = {};
|
||||
@computed get componentsMap(): any {
|
||||
return this._componentsMap;
|
||||
|
||||
@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="1.0.26-beta.0"></a>
|
||||
## [1.0.26-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.25-beta.1...v1.0.26-beta.0) (2020-12-22)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-react-renderer
|
||||
|
||||
<a name="1.0.25-beta.1"></a>
|
||||
## [1.0.25-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.4...v1.0.25-beta.1) (2020-12-15)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-react-renderer
|
||||
|
||||
<a name="1.0.24-beta.4"></a>
|
||||
## [1.0.24-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.3...v1.0.24-beta.4) (2020-12-14)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-react-renderer",
|
||||
"version": "1.0.24-beta.4",
|
||||
"version": "1.0.26-beta.0",
|
||||
"description": "react renderer for ali lowcode engine",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
|
||||
@ -3,6 +3,34 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="1.0.26-beta.0"></a>
|
||||
## [1.0.26-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.25-beta.1...v1.0.26-beta.0) (2020-12-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* requestHandlersMap 没有加到 appContext 里 ([a8d43c3](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/a8d43c3))
|
||||
* simulator-renderer 补充丢失代码 ([67dd7e2](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/67dd7e2))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* 支持 build sourceMap, 方便用户调试 ([6bf75cd](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/6bf75cd))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.25-beta.1"></a>
|
||||
## [1.0.25-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.4...v1.0.25-beta.1) (2020-12-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复 prop 无法删除最后一个 item ([e18a386](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/e18a386))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.24-beta.4"></a>
|
||||
## [1.0.24-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.3...v1.0.24-beta.4) (2020-12-14)
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{
|
||||
"sourceMap": true,
|
||||
"plugins": [
|
||||
[
|
||||
"build-plugin-component",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-react-simulator-renderer",
|
||||
"version": "1.0.24-beta.4",
|
||||
"version": "1.0.26-beta.0",
|
||||
"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": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-react-renderer": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-types": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-utils": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-designer": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-react-renderer": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-types": "^1.0.26-beta.0",
|
||||
"@ali/lowcode-utils": "^1.0.26-beta.0",
|
||||
"@ali/vu-css-style": "^1.0.2",
|
||||
"@recore/obx": "^1.0.8",
|
||||
"@recore/obx-react": "^1.0.7",
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { Node } from '@ali/lowcode-designer';
|
||||
import LowCodeRenderer from '@ali/lowcode-react-renderer';
|
||||
import { ReactInstance, Fragment, Component, createElement } from 'react';
|
||||
import { observer } from '@recore/obx-react';
|
||||
import { isFromVC } from '@ali/lowcode-utils';
|
||||
import { SimulatorRendererContainer, DocumentInstance } from './renderer';
|
||||
import { Router, Route, Switch } from 'react-router';
|
||||
import './renderer.less';
|
||||
@ -103,7 +105,7 @@ class Layout extends Component<{ rendererContainer: SimulatorRendererContainer }
|
||||
if (layout) {
|
||||
const { Component, props, componentName } = layout;
|
||||
if (Component) {
|
||||
return <Component key='layout' props={props}>{children}</Component>;
|
||||
return <Component key="layout" props={props}>{children}</Component>;
|
||||
}
|
||||
if (componentName && rendererContainer.getComponent(componentName)) {
|
||||
return createElement(
|
||||
@ -147,8 +149,10 @@ class Renderer extends Component<{
|
||||
customCreateElement={(Component: any, props: any, children: any) => {
|
||||
const { __id, __desingMode, ...viewProps } = props;
|
||||
viewProps.componentId = __id;
|
||||
const leaf = documentInstance.getNode(__id);
|
||||
viewProps._leaf = leaf;
|
||||
const leaf = documentInstance.getNode(__id) as Node;
|
||||
if (isFromVC(leaf?.componentMeta)) {
|
||||
viewProps._leaf = leaf;
|
||||
}
|
||||
viewProps._componentName = leaf?.componentName;
|
||||
// 如果是容器 && 无children && 高宽为空 增加一个占位容器,方便拖动
|
||||
if (
|
||||
|
||||
@ -211,6 +211,9 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
|
||||
// sync designMode
|
||||
this._designMode = host.designMode;
|
||||
|
||||
// sync requestHandlersMap
|
||||
this._requestHandlersMap = host.requestHandlersMap;
|
||||
|
||||
// sync device
|
||||
this._device = host.device;
|
||||
});
|
||||
@ -261,10 +264,11 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
|
||||
getUrlParams() {
|
||||
const search = history.location.search;
|
||||
return parseQuery(search);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
constants: {},
|
||||
requestHandlersMap: this._requestHandlersMap,
|
||||
};
|
||||
host.injectionConsumer.consume((data) => {
|
||||
// sync utils, i18n, contants,... config
|
||||
|
||||
@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="1.0.26-beta.0"></a>
|
||||
## [1.0.26-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.25-beta.1...v1.0.26-beta.0) (2020-12-22)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-types
|
||||
|
||||
<a name="1.0.25-beta.1"></a>
|
||||
## [1.0.25-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.4...v1.0.25-beta.1) (2020-12-15)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-types
|
||||
|
||||
<a name="1.0.24-beta.4"></a>
|
||||
## [1.0.24-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.3...v1.0.24-beta.4) (2020-12-14)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-types",
|
||||
"version": "1.0.24-beta.4",
|
||||
"version": "1.0.26-beta.0",
|
||||
"description": "Types for Ali lowCode engine",
|
||||
"files": [
|
||||
"es",
|
||||
|
||||
@ -3,6 +3,25 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
<a name="1.0.26-beta.0"></a>
|
||||
## [1.0.26-beta.0](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.25-beta.1...v1.0.26-beta.0) (2020-12-22)
|
||||
|
||||
|
||||
|
||||
|
||||
**Note:** Version bump only for package @ali/lowcode-utils
|
||||
|
||||
<a name="1.0.25-beta.1"></a>
|
||||
## [1.0.25-beta.1](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.4...v1.0.25-beta.1) (2020-12-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复 prop 无法删除最后一个 item ([e18a386](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/commit/e18a386))
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.0.24-beta.4"></a>
|
||||
## [1.0.24-beta.4](https://gitlab.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/compare/v1.0.24-beta.3...v1.0.24-beta.4) (2020-12-14)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ali/lowcode-utils",
|
||||
"version": "1.0.24-beta.4",
|
||||
"version": "1.0.26-beta.0",
|
||||
"description": "Utils for Ali lowCode engine",
|
||||
"files": [
|
||||
"es",
|
||||
@ -14,7 +14,7 @@
|
||||
"test:snapshot": "ava --update-snapshots"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ali/lowcode-types": "^1.0.24-beta.4",
|
||||
"@ali/lowcode-types": "^1.0.26-beta.0",
|
||||
"@alifd/next": "^1.19.16",
|
||||
"lodash.get": "^4.4.2",
|
||||
"react": "^16"
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
|
||||
import { isI18NObject } from './is-object';
|
||||
import get from 'lodash.get';
|
||||
import { ComponentMeta } from '@ali/lowcode-designer';
|
||||
|
||||
export function isUseI18NSetter(prototype: any, propName: string) {
|
||||
const configure = prototype?.options?.configure;
|
||||
@ -43,3 +44,11 @@ export function waitForThing(obj: any, path: string): Promise<any> {
|
||||
}
|
||||
return _innerWaitForThing(obj, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前 meta 是否从 vc prototype 转换而来
|
||||
* @param meta
|
||||
*/
|
||||
export function isFromVC(meta: ComponentMeta) {
|
||||
return !!meta?.getMetadata()?.experimental;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user