feat: 支持无组件配置的设置面板形态

This commit is contained in:
力皓 2020-11-25 21:32:13 +08:00
parent 803c33d5bc
commit 46c5bf90ac
13 changed files with 537 additions and 4 deletions

View File

@ -19,6 +19,7 @@ module.exports = {
'src/**/*.{ts,tsx}',
'!src/**/*.d.ts',
'!src/base/**',
'!src/fields/**',
'!src/prop.ts',
'!**/node_modules/**',
'!**/vendor/**',

View File

@ -40,7 +40,7 @@
"react-dom": "^16.8.1"
},
"devDependencies": {
"@ali/lowcode-test-mate": "^1.0.0",
"@ali/lowcode-test-mate": "^1.0.1",
"@alib/build-scripts": "^0.1.18",
"@types/domready": "^1.0.0",
"@types/events": "^3.0.0",

View File

@ -104,8 +104,8 @@ registerMetadataTransducer(
let top: FieldConfig[];
let bottom: FieldConfig[];
if (combined) {
top = combined?.[0].items || combined;
bottom = combined?.[combined.length - 1].items || combined;
top = combined?.[0]?.items || combined;
bottom = combined?.[combined.length - 1]?.items || combined;
} else if (props) {
top = props;
bottom = props;

View File

@ -80,6 +80,11 @@ export class Env {
};
}
clear() {
this.envs = {};
this.featureMap = {};
}
getAliSchemaVersion() {
return ALI_SCHEMA_VERSION;
}

View File

@ -0,0 +1,50 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`deepValueParser 测试 designMode: design 1`] = `
Object {
"a": "111",
"arr": Array [
"111",
"111",
],
"b": "222",
"c": "中文",
"slot": Object {
"type": "JSSlot",
"value": Array [
Object {
"componentName": "Div",
"props": Object {},
},
],
},
}
`;
exports[`deepValueParser 测试 designMode: live 1`] = `
Object {
"a": Object {
"mock": "111",
"type": "JSExpression",
"value": "state.a",
},
"arr": Array [
Object {
"mock": "111",
"type": "JSExpression",
"value": "state.a",
},
Object {
"mock": "111",
"type": "JSExpression",
"value": "state.b",
},
],
"b": Object {
"mock": "222",
"type": "JSExpression",
"value": "state.b",
},
"c": "中文",
}
`;

View File

@ -0,0 +1,246 @@
import set from 'lodash/set';
import cloneDeep from 'lodash/clonedeep';
import '../fixtures/window';
// import { Project } from '../../src/project/project';
// import { Node } from '../../src/document/node/node';
// import { Designer } from '../../src/designer/designer';
import formSchema from '../fixtures/schema/form';
import bus from '../../src/bus';
import { editor } from '../../src/editor';
describe('bus 测试', () => {
afterEach(() => {
bus.unsub('evt1');
});
it('sub / pub 测试', () => {
const mockFn1 = jest.fn();
const off1 = bus.sub('evt1', mockFn1);
const evtData = { a: 1 };
bus.pub('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
off1();
bus.pub('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
});
it('on / emit 测试', () => {
const mockFn1 = jest.fn();
const off1 = bus.on('evt1', mockFn1);
const evtData = { a: 1 };
bus.emit('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
off1();
bus.emit('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
});
it('once / emit 测试', () => {
const mockFn1 = jest.fn();
bus.once('evt1', mockFn1);
const evtData = { a: 1 };
bus.emit('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
bus.emit('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
});
it('once / emit 测试,调用解绑函数', () => {
const mockFn1 = jest.fn();
const off1 = bus.once('evt1', mockFn1);
off1();
const evtData = { a: 1 };
bus.emit('evt1', evtData);
expect(mockFn1).not.toHaveBeenCalled();
});
it('removeListener 测试', () => {
const mockFn1 = jest.fn();
bus.on('evt1', mockFn1);
const evtData = { a: 1 };
bus.emit('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
bus.removeListener('evt1', mockFn1);
bus.emit('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
});
it('unsub 测试 - 只有一个 handler', () => {
const mockFn1 = jest.fn();
bus.on('evt1', mockFn1);
const evtData = { a: 1 };
bus.emit('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
bus.unsub('evt1', mockFn1);
bus.emit('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
});
it('unsub 测试 - 只 unsub 一个 handler', () => {
const mockFn1 = jest.fn();
const mockFn2 = jest.fn();
bus.on('evt1', mockFn1);
bus.on('evt1', mockFn2);
const evtData = { a: 1 };
bus.emit('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
expect(mockFn2).toHaveBeenCalledTimes(1);
expect(mockFn2).toHaveBeenCalledWith(evtData);
bus.unsub('evt1', mockFn1);
const evtData2 = { a: 2 };
bus.emit('evt1', evtData2);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
expect(mockFn2).toHaveBeenCalledTimes(2);
expect(mockFn2).toHaveBeenLastCalledWith(evtData2);
});
it('unsub 测试 - 多个 handler', () => {
const mockFn1 = jest.fn();
const mockFn2 = jest.fn();
bus.on('evt1', mockFn1);
bus.on('evt1', mockFn2);
const evtData = { a: 1 };
bus.emit('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
expect(mockFn2).toHaveBeenCalledTimes(1);
expect(mockFn2).toHaveBeenCalledWith(evtData);
bus.unsub('evt1');
bus.emit('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
expect(mockFn2).toHaveBeenCalledTimes(1);
expect(mockFn2).toHaveBeenCalledWith(evtData);
});
it('off 测试 - 只有一个 handler', () => {
const mockFn1 = jest.fn();
bus.on('evt1', mockFn1);
const evtData = { a: 1 };
bus.emit('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
bus.off('evt1', mockFn1);
bus.emit('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
});
it('off 测试 - 只 off 一个 handler', () => {
const mockFn1 = jest.fn();
const mockFn2 = jest.fn();
bus.on('evt1', mockFn1);
bus.on('evt1', mockFn2);
const evtData = { a: 1 };
bus.emit('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
expect(mockFn2).toHaveBeenCalledTimes(1);
expect(mockFn2).toHaveBeenCalledWith(evtData);
bus.off('evt1', mockFn1);
const evtData2 = { a: 2 };
bus.emit('evt1', evtData2);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
expect(mockFn2).toHaveBeenCalledTimes(2);
expect(mockFn2).toHaveBeenLastCalledWith(evtData2);
});
it('off 测试 - 多个 handler', () => {
const mockFn1 = jest.fn();
const mockFn2 = jest.fn();
bus.on('evt1', mockFn1);
bus.on('evt1', mockFn2);
const evtData = { a: 1 };
bus.emit('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
expect(mockFn2).toHaveBeenCalledTimes(1);
expect(mockFn2).toHaveBeenCalledWith(evtData);
bus.off('evt1');
bus.emit('evt1', evtData);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData);
expect(mockFn2).toHaveBeenCalledTimes(1);
expect(mockFn2).toHaveBeenCalledWith(evtData);
});
it('简单测试dummy', () => {
bus.getEmitter();
});
describe('editor 事件转发', () => {
const fwdEvtMap = {
've.hotkey.callback.call': 'hotkey.callback.call',
've.history.back': 'history.back',
've.history.forward': 'history.forward',
'node.prop.change': 'node.prop.change',
};
Object.keys(fwdEvtMap).forEach(veEventName => {
it(`${veEventName} 测试`, () => {
const mockFn1 = jest.fn();
const evtData1 = { a: 1 };
bus.on(veEventName, mockFn1);
editor.emit(fwdEvtMap[veEventName], evtData1);
expect(mockFn1).toHaveBeenCalledTimes(1);
expect(mockFn1).toHaveBeenCalledWith(evtData1);
});
});
});
});

View File

@ -0,0 +1,84 @@
import '../fixtures/window';
import { deepValueParser } from '../../src/deep-value-parser';
import { editor } from '../../src/editor';
describe('deepValueParser 测试', () => {
it('null & undefined', () => {
expect(deepValueParser()).toBeNull;
expect(deepValueParser()).toBeUndefined;
});
it('designMode: design', () => {
expect(deepValueParser({
a: {
type: 'variable',
variable: 'state.a',
value: '111',
},
b: {
type: 'JSExpression',
value: 'state.b',
mock: '222',
},
c: {
type: 'i18n',
use: 'zh_CN',
zh_CN: '中文',
en_US: 'eng',
},
slot: {
type: 'JSSlot',
value: [{
componentName: 'Div',
props: {},
}],
},
arr: [
{
type: 'variable',
variable: 'state.a',
value: '111',
},
{
type: 'variable',
variable: 'state.b',
value: '111',
},
],
})).toMatchSnapshot();
});
it('designMode: live', () => {
editor.set('designMode', 'live');
expect(deepValueParser({
a: {
type: 'variable',
variable: 'state.a',
value: '111',
},
b: {
type: 'JSExpression',
value: 'state.b',
mock: '222',
},
c: {
type: 'i18n',
use: 'zh_CN',
zh_CN: '中文',
en_US: 'eng',
},
arr: [
{
type: 'variable',
variable: 'state.a',
value: '111',
},
{
type: 'variable',
variable: 'state.b',
value: '111',
},
],
})).toMatchSnapshot();
});
});

View File

@ -0,0 +1,114 @@
import set from 'lodash/set';
import cloneDeep from 'lodash/clonedeep';
import '../fixtures/window';
// import { Project } from '../../src/project/project';
// import { Node } from '../../src/document/node/node';
// import { Designer } from '../../src/designer/designer';
import env from '../../src/env';
import bus from 'editor-preset-vision/src/bus';
import { autorun } from '@ali/lowcode-editor-core';
describe('env 测试', () => {
describe('常规 API 测试', () => {
it('setEnv / getEnv / setEnvMap / set / get', () => {
expect(env.getEnv('xxx')).toBeUndefined;
const mockFn1 = jest.fn();
const off1 = env.onEnvChange(mockFn1);
const envData = { a: 1 };
env.setEnv('xxx', envData);
expect(env.getEnv('xxx')).toEqual(envData);
expect(env.get('xxx')).toEqual(envData);
expect(mockFn1).toHaveBeenCalled();
expect(mockFn1).toHaveBeenCalledWith(env.envs, 'xxx', envData);
mockFn1.mockClear();
// 设置相同的值
env.setEnv('xxx', envData);
expect(env.getEnv('xxx')).toEqual(envData);
expect(env.get('xxx')).toEqual(envData);
expect(mockFn1).not.toHaveBeenCalled();
mockFn1.mockClear();
// 设置另一个 envName
const envData2 = { b: 1 };
env.set('yyy', envData2);
expect(env.getEnv('yyy')).toEqual(envData2);
expect(env.get('yyy')).toEqual(envData2);
expect(mockFn1).toHaveBeenCalled();
expect(mockFn1).toHaveBeenCalledWith(env.envs, 'yyy', envData2);
mockFn1.mockClear();
env.setEnvMap({
zzz: { a: 1, b: 1 },
});
expect(env.getEnv('xxx')).toBeUndefined;
expect(env.getEnv('yyy')).toBeUndefined;
expect(env.getEnv('zzz')).toEqual({ a: 1, b: 1 });
expect(mockFn1).toHaveBeenCalled();
expect(mockFn1).toHaveBeenCalledWith(env.envs);
mockFn1.mockClear();
// 解绑事件
off1();
env.setEnvMap({
zzz: { a: 1, b: 1 },
});
expect(mockFn1).not.toHaveBeenCalled();
mockFn1.mockClear();
});
it('setLocale / getLocale', () => {
expect(env.getLocale()).toBe('zh_CN');
env.setLocale('en_US');
expect(env.getLocale()).toBe('en_US');
});
it('setExpertMode / isExpertMode', () => {
expect(env.isExpertMode()).toBeFalsy;
env.setExpertMode('truthy value');
expect(env.isExpertMode()).toBeTruthy;
});
it('getSupportFeatures / setSupportFeatures / supports', () => {
expect(env.getSupportFeatures()).toEqual({});
env.setSupportFeatures({
mobile: true,
pc: true,
});
expect(env.getSupportFeatures()).toEqual({
mobile: true,
pc: true,
});
expect(env.supports('mobile')).toBeTruthy;
expect(env.supports('pc')).toBeTruthy;
expect(env.supports('iot')).toBeFalsy;
});
it('getAliSchemaVersion', () => {
expect(env.getAliSchemaVersion()).toBe('1.0.0');
});
it('envs obx 测试', async () => {
const mockFn = jest.fn();
env.clear();
autorun(() => {
mockFn(env.envs);
env.envs;
});
await new Promise(resolve => setTimeout(resolve, 16));
expect(mockFn).toHaveBeenCalledTimes(1);
expect(mockFn).toHaveBeenLastCalledWith({});
env.setEnv('abc', { a: 1 });
await new Promise(resolve => setTimeout(resolve, 16));
expect(mockFn).toHaveBeenCalledTimes(2);
expect(mockFn).toHaveBeenLastCalledWith({ abc: { a: 1 } });
});
});
});

View File

@ -0,0 +1,15 @@
import set from 'lodash/set';
import cloneDeep from 'lodash/clonedeep';
import '../fixtures/window';
// import { Project } from '../../src/project/project';
// import { Node } from '../../src/document/node/node';
// import { Designer } from '../../src/designer/designer';
import symbols from '../../src/symbols';
describe('symbols 测试', () => {
it('API', () => {
symbols.create('abc');
symbols.create('abc');
symbols.get('abc');
});
});

View File

@ -0,0 +1,3 @@
it.skip('should ', () => {
});

View File

@ -10,9 +10,14 @@ describe('VisualEngine.Exchange 相关 API 测试', () => {
VisualEngine.Exchange.select(doc?.getNode('form'));
expect(VisualEngine.Exchange.getSelected()?.componentName).toBe('Form');
expect(VisualEngine.Exchange.getSelected()?.id).toBe('form');
// clear selection
VisualEngine.Exchange.select();
expect(VisualEngine.Exchange.getSelected()).toBeUndefined;
});
it('onIntoView', () => {
expect(typeof VisualEngine.Exchange.onIntoView).toBe('function');
VisualEngine.Exchange.onIntoView();
});
});

View File

@ -4,7 +4,7 @@ import '../fixtures/window';
import formSchema from '../fixtures/schema/form';
import { Project } from '../../src';
describe.skip('VisualEngine.Project 相关 API 测试', () => {
describe('VisualEngine.Project 相关 API 测试', () => {
it('getSchema / setSchema 系列', () => {
Project.setSchema({
componentsMap: {},

View File

@ -120,6 +120,16 @@ export class SettingsPrimaryPane extends Component<{ editor: Editor; config: any
);
}
if (Array.isArray(settings.items) && settings.items.length === 0) {
return (
<div className="lc-settings-main">
<div className="lc-settings-notice">
<p></p>
</div>
</div>
);
}
if (!settings.isSameComponent) {
// todo: future support 获取设置项交集编辑
return (