Merge branch 'develop' into release/1.3.2-beta

This commit is contained in:
liujuping 2024-01-30 16:41:37 +08:00
commit 1ae217df8d
6 changed files with 202 additions and 32 deletions

View File

@ -353,7 +353,6 @@ export class Prop implements IProp, IPropParent {
@action @action
setValue(val: IPublicTypeCompositeValue) { setValue(val: IPublicTypeCompositeValue) {
if (val === this._value) return; if (val === this._value) return;
const editor = this.owner.document?.designer.editor;
const oldValue = this._value; const oldValue = this._value;
this._value = val; this._value = val;
this._code = null; this._code = null;
@ -386,22 +385,31 @@ export class Prop implements IProp, IPropParent {
this.setupItems(); this.setupItems();
if (oldValue !== this._value) { if (oldValue !== this._value) {
const propsInfo = { this.emitChange({ oldValue });
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);
} }
} }
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 { getValue(): IPublicTypeCompositeValue {
return this.export(IPublicEnumTransformStage.Serilize); return this.export(IPublicEnumTransformStage.Serilize);
} }
@ -462,7 +470,12 @@ export class Prop implements IProp, IPropParent {
*/ */
@action @action
unset() { 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 @action
remove() { remove() {
this.parent.delete(this); this.parent.delete(this);
this.unset();
} }
/** /**

View File

@ -3,7 +3,7 @@ import { Editor, engineConfig } from '@alilc/lowcode-editor-core';
import { Designer } from '../../../../src/designer/designer'; import { Designer } from '../../../../src/designer/designer';
import { DocumentModel } from '../../../../src/document/document-model'; import { DocumentModel } from '../../../../src/document/document-model';
import { Prop, isProp, isValidArrayIndex } from '../../../../src/document/node/props/prop'; 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'; import { shellModelFactory } from '../../../../../engine/src/modules/shell-model-factory';
const slotNodeImportMockFn = jest.fn(); const slotNodeImportMockFn = jest.fn();
@ -24,14 +24,24 @@ const mockOwner = {
remove: slotNodeRemoveMockFn, remove: slotNodeRemoveMockFn,
}; };
}, },
designer: {}, designer: {
editor: {
eventBus: {
emit: jest.fn(),
},
},
},
}, },
isInited: true, isInited: true,
emitPropChange: jest.fn(),
delete() {},
}; };
const mockPropsInst = { const mockPropsInst = {
owner: mockOwner, owner: mockOwner,
delete() {},
}; };
mockPropsInst.props = mockPropsInst; mockPropsInst.props = mockPropsInst;
describe('Prop 类测试', () => { describe('Prop 类测试', () => {
@ -564,3 +574,124 @@ describe('其他导出函数', () => {
expect(isValidArrayIndex('2', 1)).toBeFalsy(); 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);
});
});

View File

@ -10,10 +10,7 @@ const sampleNodeSchema: IPublicTypePropType = {
value: [ value: [
{ {
name: 'id', name: 'id',
propType: { propType: 'string',
type: 'string',
isRequired: true,
},
}, },
{ {
name: 'componentName', name: 'componentName',
@ -277,10 +274,12 @@ export const nodeCommand = (ctx: IPublicModelPluginContext) => {
handler: (param: { handler: (param: {
parentNodeId: string; parentNodeId: string;
nodeSchema: IPublicTypeNodeSchema; nodeSchema: IPublicTypeNodeSchema;
index: number;
}) => { }) => {
const { const {
parentNodeId, parentNodeId,
nodeSchema, nodeSchema,
index,
} = param; } = param;
const { project } = ctx; const { project } = ctx;
const parentNode = project.currentDocument?.getNodeById(parentNodeId); const parentNode = project.currentDocument?.getNodeById(parentNodeId);
@ -296,7 +295,11 @@ export const nodeCommand = (ctx: IPublicModelPluginContext) => {
throw new Error('Invalid node.'); 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: [ parameters: [
{ {
@ -309,6 +312,11 @@ export const nodeCommand = (ctx: IPublicModelPluginContext) => {
propType: nodeSchemaPropType, propType: nodeSchemaPropType,
description: 'The node to be added.', 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, index = 0,
} = param; } = 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 node = project.currentDocument?.getNodeById(nodeId);
const targetNode = project.currentDocument?.getNodeById(targetNodeId); const targetNode = project.currentDocument?.getNodeById(targetNodeId);
if (!node) { if (!node) {
@ -350,12 +366,18 @@ export const nodeCommand = (ctx: IPublicModelPluginContext) => {
parameters: [ parameters: [
{ {
name: 'nodeId', name: 'nodeId',
propType: 'string', propType: {
type: 'string',
isRequired: true,
},
description: 'The id of the node to be moved.', description: 'The id of the node to be moved.',
}, },
{ {
name: 'targetNodeId', name: 'targetNodeId',
propType: 'string', propType: {
type: 'string',
isRequired: true,
},
description: 'The id of the target node.', description: 'The id of the target node.',
}, },
{ {
@ -393,8 +415,8 @@ export const nodeCommand = (ctx: IPublicModelPluginContext) => {
}); });
command.registerCommand({ command.registerCommand({
name: 'replace', name: 'update',
description: 'Replace a node with another node.', description: 'Update a node.',
handler(param: { handler(param: {
nodeId: string; nodeId: string;
nodeSchema: IPublicTypeNodeSchema; nodeSchema: IPublicTypeNodeSchema;
@ -419,12 +441,12 @@ export const nodeCommand = (ctx: IPublicModelPluginContext) => {
{ {
name: 'nodeId', name: 'nodeId',
propType: 'string', propType: 'string',
description: 'The id of the node to be replaced.', description: 'The id of the node to be updated.',
}, },
{ {
name: 'nodeSchema', name: 'nodeSchema',
propType: nodeSchemaPropType, propType: nodeSchemaPropType,
description: 'The node to replace.', description: 'The node to be updated.',
}, },
], ],
}); });

View File

@ -614,7 +614,7 @@ function getNodeInstance(fiberNode: any, specId?: string): IPublicTypeNodeInstan
function checkInstanceMounted(instance: any): boolean { function checkInstanceMounted(instance: any): boolean {
if (isElement(instance)) { if (isElement(instance)) {
return instance.parentElement != null; return instance.parentElement != null && window.document.contains(instance);
} }
return true; return true;
} }

View File

@ -34,7 +34,7 @@ export function ContextMenu({ children, menus, pluginContext }: {
); );
} }
if (!menus || !menus.length) { if (!menus) {
return ( return (
<>{ children }</> <>{ children }</>
); );
@ -53,6 +53,9 @@ export function ContextMenu({ children, menus, pluginContext }: {
} }
ContextMenu.create = (pluginContext: IPublicModelPluginContext, menus: IPublicTypeContextMenuAction[], event: MouseEvent) => { ContextMenu.create = (pluginContext: IPublicModelPluginContext, menus: IPublicTypeContextMenuAction[], event: MouseEvent) => {
event.preventDefault();
event.stopPropagation();
const children: React.ReactNode[] = parseContextMenuAsReactNode(parseContextMenuProperties(menus, { const children: React.ReactNode[] = parseContextMenuAsReactNode(parseContextMenuProperties(menus, {
pluginContext, pluginContext,
}), { }), {

View File

@ -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;
/** /**
* *