mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-11 18:42:56 +00:00
Merge branch 'develop' into release/1.3.2-beta
This commit is contained in:
commit
1ae217df8d
@ -353,7 +353,6 @@ export class Prop implements IProp, IPropParent {
|
||||
@action
|
||||
setValue(val: IPublicTypeCompositeValue) {
|
||||
if (val === this._value) return;
|
||||
const editor = this.owner.document?.designer.editor;
|
||||
const oldValue = this._value;
|
||||
this._value = val;
|
||||
this._code = null;
|
||||
@ -386,22 +385,31 @@ export class Prop implements IProp, IPropParent {
|
||||
this.setupItems();
|
||||
|
||||
if (oldValue !== this._value) {
|
||||
const propsInfo = {
|
||||
key: this.key,
|
||||
prop: this,
|
||||
oldValue,
|
||||
newValue: this._value,
|
||||
};
|
||||
|
||||
editor?.eventBus.emit(GlobalEvent.Node.Prop.InnerChange, {
|
||||
node: this.owner as any,
|
||||
...propsInfo,
|
||||
});
|
||||
|
||||
this.owner?.emitPropChange?.(propsInfo);
|
||||
this.emitChange({ oldValue });
|
||||
}
|
||||
}
|
||||
|
||||
emitChange = ({
|
||||
oldValue,
|
||||
}: {
|
||||
oldValue: IPublicTypeCompositeValue | UNSET;
|
||||
}) => {
|
||||
const editor = this.owner.document?.designer.editor;
|
||||
const propsInfo = {
|
||||
key: this.key,
|
||||
prop: this,
|
||||
oldValue,
|
||||
newValue: this.type === 'unset' ? undefined : this._value,
|
||||
};
|
||||
|
||||
editor?.eventBus.emit(GlobalEvent.Node.Prop.InnerChange, {
|
||||
node: this.owner as any,
|
||||
...propsInfo,
|
||||
});
|
||||
|
||||
this.owner?.emitPropChange?.(propsInfo);
|
||||
};
|
||||
|
||||
getValue(): IPublicTypeCompositeValue {
|
||||
return this.export(IPublicEnumTransformStage.Serilize);
|
||||
}
|
||||
@ -462,7 +470,12 @@ export class Prop implements IProp, IPropParent {
|
||||
*/
|
||||
@action
|
||||
unset() {
|
||||
this._type = 'unset';
|
||||
if (this._type !== 'unset') {
|
||||
this._type = 'unset';
|
||||
this.emitChange({
|
||||
oldValue: this._value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -557,6 +570,7 @@ export class Prop implements IProp, IPropParent {
|
||||
@action
|
||||
remove() {
|
||||
this.parent.delete(this);
|
||||
this.unset();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -3,7 +3,7 @@ import { Editor, engineConfig } from '@alilc/lowcode-editor-core';
|
||||
import { Designer } from '../../../../src/designer/designer';
|
||||
import { DocumentModel } from '../../../../src/document/document-model';
|
||||
import { Prop, isProp, isValidArrayIndex } from '../../../../src/document/node/props/prop';
|
||||
import { IPublicEnumTransformStage } from '@alilc/lowcode-types';
|
||||
import { GlobalEvent, IPublicEnumTransformStage } from '@alilc/lowcode-types';
|
||||
import { shellModelFactory } from '../../../../../engine/src/modules/shell-model-factory';
|
||||
|
||||
const slotNodeImportMockFn = jest.fn();
|
||||
@ -24,14 +24,24 @@ const mockOwner = {
|
||||
remove: slotNodeRemoveMockFn,
|
||||
};
|
||||
},
|
||||
designer: {},
|
||||
designer: {
|
||||
editor: {
|
||||
eventBus: {
|
||||
emit: jest.fn(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
isInited: true,
|
||||
emitPropChange: jest.fn(),
|
||||
delete() {},
|
||||
};
|
||||
|
||||
const mockPropsInst = {
|
||||
owner: mockOwner,
|
||||
delete() {},
|
||||
};
|
||||
|
||||
mockPropsInst.props = mockPropsInst;
|
||||
|
||||
describe('Prop 类测试', () => {
|
||||
@ -564,3 +574,124 @@ describe('其他导出函数', () => {
|
||||
expect(isValidArrayIndex('2', 1)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('setValue with event', () => {
|
||||
let propInstance;
|
||||
let mockEmitChange;
|
||||
let mockEventBusEmit;
|
||||
let mockEmitPropChange;
|
||||
|
||||
beforeEach(() => {
|
||||
// Initialize the instance of your class
|
||||
propInstance = new Prop(mockPropsInst, true, 'stringProp');;
|
||||
|
||||
// Mock necessary methods and properties
|
||||
mockEmitChange = jest.spyOn(propInstance, 'emitChange');
|
||||
propInstance.owner = {
|
||||
document: {
|
||||
designer: {
|
||||
editor: {
|
||||
eventBus: {
|
||||
emit: jest.fn(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
emitPropChange: jest.fn(),
|
||||
delete() {},
|
||||
};
|
||||
mockEventBusEmit = jest.spyOn(propInstance.owner.document.designer.editor.eventBus, 'emit');
|
||||
mockEmitPropChange = jest.spyOn(propInstance.owner, 'emitPropChange');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should correctly handle string values and emit changes', () => {
|
||||
const oldValue = propInstance._value;
|
||||
const newValue = 'new string value';
|
||||
|
||||
propInstance.setValue(newValue);
|
||||
|
||||
const expectedPartialPropsInfo = expect.objectContaining({
|
||||
key: propInstance.key,
|
||||
newValue, // You can specifically test only certain keys
|
||||
oldValue,
|
||||
});
|
||||
|
||||
expect(propInstance.getValue()).toBe(newValue);
|
||||
expect(propInstance.type).toBe('literal');
|
||||
expect(mockEmitChange).toHaveBeenCalledWith({ oldValue });
|
||||
expect(mockEventBusEmit).toHaveBeenCalledWith(GlobalEvent.Node.Prop.InnerChange, expectedPartialPropsInfo);
|
||||
expect(mockEmitPropChange).toHaveBeenCalledWith(expectedPartialPropsInfo);
|
||||
});
|
||||
|
||||
it('should handle object values and set type to map', () => {
|
||||
const oldValue = propInstance._value;
|
||||
const newValue = 234;
|
||||
|
||||
const expectedPartialPropsInfo = expect.objectContaining({
|
||||
key: propInstance.key,
|
||||
newValue, // You can specifically test only certain keys
|
||||
oldValue,
|
||||
});
|
||||
|
||||
propInstance.setValue(newValue);
|
||||
|
||||
expect(propInstance.getValue()).toEqual(newValue);
|
||||
expect(propInstance.type).toBe('literal');
|
||||
expect(mockEmitChange).toHaveBeenCalledWith({ oldValue });
|
||||
expect(mockEventBusEmit).toHaveBeenCalledWith(GlobalEvent.Node.Prop.InnerChange, expectedPartialPropsInfo);
|
||||
expect(mockEmitPropChange).toHaveBeenCalledWith(expectedPartialPropsInfo);
|
||||
});
|
||||
|
||||
it('should has event when unset call', () => {
|
||||
const oldValue = propInstance._value;
|
||||
|
||||
propInstance.unset();
|
||||
|
||||
const expectedPartialPropsInfo = expect.objectContaining({
|
||||
key: propInstance.key,
|
||||
newValue: undefined, // You can specifically test only certain keys
|
||||
oldValue,
|
||||
});
|
||||
|
||||
expect(propInstance.getValue()).toEqual(undefined);
|
||||
expect(propInstance.type).toBe('unset');
|
||||
expect(mockEmitChange).toHaveBeenCalledWith({
|
||||
oldValue,
|
||||
newValue: undefined,
|
||||
});
|
||||
expect(mockEventBusEmit).toHaveBeenCalledWith(GlobalEvent.Node.Prop.InnerChange, expectedPartialPropsInfo);
|
||||
expect(mockEmitPropChange).toHaveBeenCalledWith(expectedPartialPropsInfo);
|
||||
|
||||
propInstance.unset();
|
||||
expect(mockEmitChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
// remove
|
||||
it('should has event when remove call', () => {
|
||||
const oldValue = propInstance._value;
|
||||
|
||||
propInstance.remove();
|
||||
|
||||
const expectedPartialPropsInfo = expect.objectContaining({
|
||||
key: propInstance.key,
|
||||
newValue: undefined, // You can specifically test only certain keys
|
||||
oldValue,
|
||||
});
|
||||
|
||||
expect(propInstance.getValue()).toEqual(undefined);
|
||||
// expect(propInstance.type).toBe('unset');
|
||||
expect(mockEmitChange).toHaveBeenCalledWith({
|
||||
oldValue,
|
||||
newValue: undefined,
|
||||
});
|
||||
expect(mockEventBusEmit).toHaveBeenCalledWith(GlobalEvent.Node.Prop.InnerChange, expectedPartialPropsInfo);
|
||||
expect(mockEmitPropChange).toHaveBeenCalledWith(expectedPartialPropsInfo);
|
||||
|
||||
propInstance.remove();
|
||||
expect(mockEmitChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@ -10,10 +10,7 @@ const sampleNodeSchema: IPublicTypePropType = {
|
||||
value: [
|
||||
{
|
||||
name: 'id',
|
||||
propType: {
|
||||
type: 'string',
|
||||
isRequired: true,
|
||||
},
|
||||
propType: 'string',
|
||||
},
|
||||
{
|
||||
name: 'componentName',
|
||||
@ -277,10 +274,12 @@ export const nodeCommand = (ctx: IPublicModelPluginContext) => {
|
||||
handler: (param: {
|
||||
parentNodeId: string;
|
||||
nodeSchema: IPublicTypeNodeSchema;
|
||||
index: number;
|
||||
}) => {
|
||||
const {
|
||||
parentNodeId,
|
||||
nodeSchema,
|
||||
index,
|
||||
} = param;
|
||||
const { project } = ctx;
|
||||
const parentNode = project.currentDocument?.getNodeById(parentNodeId);
|
||||
@ -296,7 +295,11 @@ export const nodeCommand = (ctx: IPublicModelPluginContext) => {
|
||||
throw new Error('Invalid node.');
|
||||
}
|
||||
|
||||
project.currentDocument?.insertNode(parentNode, nodeSchema);
|
||||
if (index < 0 || index > (parentNode.children?.size || 0)) {
|
||||
throw new Error(`Invalid index '${index}'.`);
|
||||
}
|
||||
|
||||
project.currentDocument?.insertNode(parentNode, nodeSchema, index);
|
||||
},
|
||||
parameters: [
|
||||
{
|
||||
@ -309,6 +312,11 @@ export const nodeCommand = (ctx: IPublicModelPluginContext) => {
|
||||
propType: nodeSchemaPropType,
|
||||
description: 'The node to be added.',
|
||||
},
|
||||
{
|
||||
name: 'index',
|
||||
propType: 'number',
|
||||
description: 'The index of the node to be added.',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@ -326,6 +334,14 @@ export const nodeCommand = (ctx: IPublicModelPluginContext) => {
|
||||
index = 0,
|
||||
} = param;
|
||||
|
||||
if (!nodeId) {
|
||||
throw new Error('Invalid node id.');
|
||||
}
|
||||
|
||||
if (!targetNodeId) {
|
||||
throw new Error('Invalid target node id.');
|
||||
}
|
||||
|
||||
const node = project.currentDocument?.getNodeById(nodeId);
|
||||
const targetNode = project.currentDocument?.getNodeById(targetNodeId);
|
||||
if (!node) {
|
||||
@ -350,12 +366,18 @@ export const nodeCommand = (ctx: IPublicModelPluginContext) => {
|
||||
parameters: [
|
||||
{
|
||||
name: 'nodeId',
|
||||
propType: 'string',
|
||||
propType: {
|
||||
type: 'string',
|
||||
isRequired: true,
|
||||
},
|
||||
description: 'The id of the node to be moved.',
|
||||
},
|
||||
{
|
||||
name: 'targetNodeId',
|
||||
propType: 'string',
|
||||
propType: {
|
||||
type: 'string',
|
||||
isRequired: true,
|
||||
},
|
||||
description: 'The id of the target node.',
|
||||
},
|
||||
{
|
||||
@ -393,8 +415,8 @@ export const nodeCommand = (ctx: IPublicModelPluginContext) => {
|
||||
});
|
||||
|
||||
command.registerCommand({
|
||||
name: 'replace',
|
||||
description: 'Replace a node with another node.',
|
||||
name: 'update',
|
||||
description: 'Update a node.',
|
||||
handler(param: {
|
||||
nodeId: string;
|
||||
nodeSchema: IPublicTypeNodeSchema;
|
||||
@ -419,12 +441,12 @@ export const nodeCommand = (ctx: IPublicModelPluginContext) => {
|
||||
{
|
||||
name: 'nodeId',
|
||||
propType: 'string',
|
||||
description: 'The id of the node to be replaced.',
|
||||
description: 'The id of the node to be updated.',
|
||||
},
|
||||
{
|
||||
name: 'nodeSchema',
|
||||
propType: nodeSchemaPropType,
|
||||
description: 'The node to replace.',
|
||||
description: 'The node to be updated.',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@ -614,7 +614,7 @@ function getNodeInstance(fiberNode: any, specId?: string): IPublicTypeNodeInstan
|
||||
|
||||
function checkInstanceMounted(instance: any): boolean {
|
||||
if (isElement(instance)) {
|
||||
return instance.parentElement != null;
|
||||
return instance.parentElement != null && window.document.contains(instance);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ export function ContextMenu({ children, menus, pluginContext }: {
|
||||
);
|
||||
}
|
||||
|
||||
if (!menus || !menus.length) {
|
||||
if (!menus) {
|
||||
return (
|
||||
<>{ children }</>
|
||||
);
|
||||
@ -53,6 +53,9 @@ export function ContextMenu({ children, menus, pluginContext }: {
|
||||
}
|
||||
|
||||
ContextMenu.create = (pluginContext: IPublicModelPluginContext, menus: IPublicTypeContextMenuAction[], event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const children: React.ReactNode[] = parseContextMenuAsReactNode(parseContextMenuProperties(menus, {
|
||||
pluginContext,
|
||||
}), {
|
||||
|
||||
@ -15,12 +15,12 @@ export interface IPublicApiCommand {
|
||||
/**
|
||||
* 通过名称和给定参数执行一个命令,会校验参数是否符合命令定义
|
||||
*/
|
||||
executeCommand(name: string, args: IPublicTypeCommandHandlerArgs): void;
|
||||
executeCommand(name: string, args?: IPublicTypeCommandHandlerArgs): void;
|
||||
|
||||
/**
|
||||
* 批量执行命令,执行完所有命令后再进行一次重绘,历史记录中只会记录一次
|
||||
*/
|
||||
batchExecuteCommand(commands: { name: string; args: IPublicTypeCommandHandlerArgs }[]): void;
|
||||
batchExecuteCommand(commands: { name: string; args?: IPublicTypeCommandHandlerArgs }[]): void;
|
||||
|
||||
/**
|
||||
* 列出所有已注册的命令
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user