mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-12 08:58:15 +00:00
Merge branch feat/jsFunctionScope into develop
Title: feat: JSFunction 表达式支持 scope 值 Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/8088102
This commit is contained in:
commit
b5ed75a22d
@ -199,6 +199,8 @@ exports[`React Renderer render basic case 1`] = `
|
||||
maxLength={null}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onCompositionEnd={[Function]}
|
||||
onCompositionStart={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
placeholder="请选择"
|
||||
@ -296,6 +298,8 @@ exports[`React Renderer render basic case 1`] = `
|
||||
maxLength={null}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onCompositionEnd={[Function]}
|
||||
onCompositionStart={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
placeholder="请选择"
|
||||
@ -378,6 +382,8 @@ exports[`React Renderer render basic case 1`] = `
|
||||
maxLength={null}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onCompositionEnd={[Function]}
|
||||
onCompositionStart={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
readOnly={false}
|
||||
@ -826,6 +832,8 @@ exports[`React Renderer render basic case 1`] = `
|
||||
maxLength={null}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onCompositionEnd={[Function]}
|
||||
onCompositionStart={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
readOnly={false}
|
||||
|
||||
6
packages/renderer-core/build.test.json
Normal file
6
packages/renderer-core/build.test.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"plugins": [
|
||||
"build-plugin-component",
|
||||
"@ali/lowcode-test-mate/plugin/index.ts"
|
||||
]
|
||||
}
|
||||
20
packages/renderer-core/jest.config.js
Normal file
20
packages/renderer-core/jest.config.js
Normal file
@ -0,0 +1,20 @@
|
||||
const esModules = ['@recore/obx-react'].join('|');
|
||||
|
||||
module.exports = {
|
||||
// transform: {
|
||||
// '^.+\\.[jt]sx?$': 'babel-jest',
|
||||
// // '^.+\\.(ts|tsx)$': 'ts-jest',
|
||||
// // '^.+\\.(js|jsx)$': 'babel-jest',
|
||||
// },
|
||||
// testMatch: ['(/tests?/.*(test))\\.[jt]s$'],
|
||||
transformIgnorePatterns: [
|
||||
`/node_modules/(?!${esModules})/`,
|
||||
],
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: [
|
||||
'src/**/*.{ts,tsx}',
|
||||
'!**/node_modules/**',
|
||||
'!**/vendor/**',
|
||||
],
|
||||
};
|
||||
@ -10,8 +10,13 @@
|
||||
"es"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "build-scripts test --config build.test.json",
|
||||
"build": "build-scripts build --skip-demo"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@gitlab.alibaba-inc.com:ali-lowcode/ali-lowcode-engine.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ali/b3-one": "^0.0.17",
|
||||
"@ali/bzb-request": "^2.6.0-beta.13",
|
||||
|
||||
@ -32,7 +32,7 @@ import { compWrapper } from '../hoc';
|
||||
import { IComponentConstruct, IComponentHoc, leafWrapper } from '../hoc/leaf';
|
||||
import logger from '../utils/logger';
|
||||
|
||||
export default function baseRenererFactory() {
|
||||
export default function baseRendererFactory() {
|
||||
const { BaseRenderer: customBaseRenderer } = adapter.getRenderers();
|
||||
|
||||
if (customBaseRenderer) {
|
||||
@ -712,7 +712,7 @@ export default function baseRenererFactory() {
|
||||
) {
|
||||
return checkProps(props);
|
||||
}
|
||||
if (isJSExpression(props)) {
|
||||
if (isJSExpression(props) || isJSFunction(props)) {
|
||||
props = parseExpression(props, scope);
|
||||
// 只有当变量解析出来为模型结构的时候才会继续解析
|
||||
if (!isSchema(props) && !isJSSlot(props)) return checkProps(props);
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { BuiltinSimulatorHost } from '@ali/lowcode-designer';
|
||||
import { baseRendererFactory } from '../renderer';
|
||||
import baseRenererFactory from '../renderer/base';
|
||||
|
||||
export type IBaseRenderer = ReturnType<typeof baseRenererFactory>;
|
||||
export type IBaseRenderer = ReturnType<typeof baseRendererFactory>;
|
||||
export type IBaseRendererInstance = InstanceType<ReturnType<typeof baseRendererFactory>>;
|
||||
|
||||
export interface IProps {
|
||||
@ -28,7 +27,7 @@ export interface IProps {
|
||||
|
||||
export interface IState {
|
||||
engineRenderError?: boolean;
|
||||
error?: Error
|
||||
error?: Error;
|
||||
onCompGetRef: (schema: ISchema, ref: any) => void;
|
||||
onCompGetCtx: (schema: ISchema, ref: any) => void;
|
||||
customCreateElement: (...args: any) => any;
|
||||
@ -57,7 +56,7 @@ export interface ComponentModel {
|
||||
export interface ISchema {
|
||||
componentName: string;
|
||||
props: any;
|
||||
children: ComponentModel[]
|
||||
children: ComponentModel[];
|
||||
dataSource?: any;
|
||||
methods?: any;
|
||||
lifeCycles?: any;
|
||||
@ -68,7 +67,7 @@ export interface IInfo {
|
||||
schema: ISchema;
|
||||
Comp: any;
|
||||
componentInfo?: any;
|
||||
componentChildren?: any
|
||||
componentChildren?: any;
|
||||
}
|
||||
|
||||
export interface JSExpression {
|
||||
@ -113,9 +112,9 @@ export interface IRendererModules {
|
||||
BaseRenderer?: new(...args: any) => IRenderer;
|
||||
PageRenderer: any;
|
||||
ComponentRenderer: any;
|
||||
BlockRenderer?: any,
|
||||
AddonRenderer?: any,
|
||||
TempRenderer?: any,
|
||||
BlockRenderer?: any;
|
||||
AddonRenderer?: any;
|
||||
TempRenderer?: any;
|
||||
DivRenderer?: any;
|
||||
}
|
||||
|
||||
|
||||
567
packages/renderer-core/test/fixtures/schema/basic.ts
vendored
Normal file
567
packages/renderer-core/test/fixtures/schema/basic.ts
vendored
Normal file
@ -0,0 +1,567 @@
|
||||
export default {
|
||||
componentName: 'Page',
|
||||
id: 'node_dockcviv8fo1',
|
||||
props: {
|
||||
ref: 'outterView',
|
||||
autoLoading: true,
|
||||
style: {
|
||||
padding: '0 5px 0 5px',
|
||||
},
|
||||
},
|
||||
fileName: 'test',
|
||||
dataSource: {
|
||||
list: [],
|
||||
},
|
||||
state: {
|
||||
text: 'outter',
|
||||
isShowDialog: false,
|
||||
},
|
||||
css: 'body {font-size: 12px;} .botton{width:100px;color:#ff00ff}',
|
||||
lifeCycles: {
|
||||
componentDidMount: {
|
||||
type: 'JSFunction',
|
||||
value: "function() {\n console.log('did mount');\n }",
|
||||
},
|
||||
componentWillUnmount: {
|
||||
type: 'JSFunction',
|
||||
value: "function() {\n console.log('will umount');\n }",
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
testFunc: {
|
||||
type: 'JSFunction',
|
||||
value: "function() {\n console.log('test func');\n }",
|
||||
},
|
||||
onClick: {
|
||||
type: 'JSFunction',
|
||||
value: 'function() {\n this.setState({\n isShowDialog: true\n })\n }',
|
||||
},
|
||||
closeDialog: {
|
||||
type: 'JSFunction',
|
||||
value: 'function() {\n this.setState({\n isShowDialog: false\n })\n }',
|
||||
},
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Box',
|
||||
id: 'node_dockcy8n9xed',
|
||||
props: {
|
||||
style: {
|
||||
backgroundColor: 'rgba(31,56,88,0.1)',
|
||||
padding: '12px 12px 12px 12px',
|
||||
},
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Box',
|
||||
id: 'node_dockcy8n9xee',
|
||||
props: {
|
||||
style: {
|
||||
padding: '12px 12px 12px 12px',
|
||||
backgroundColor: '#ffffff',
|
||||
},
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Breadcrumb',
|
||||
id: 'node_dockcy8n9xef',
|
||||
props: {
|
||||
prefix: 'next-',
|
||||
maxNode: 100,
|
||||
component: 'nav',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Breadcrumb.Item',
|
||||
id: 'node_dockcy8n9xeg',
|
||||
props: {
|
||||
prefix: 'next-',
|
||||
children: '首页',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Breadcrumb.Item',
|
||||
id: 'node_dockcy8n9xei',
|
||||
props: {
|
||||
prefix: 'next-',
|
||||
children: '品质中台',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Breadcrumb.Item',
|
||||
id: 'node_dockcy8n9xek',
|
||||
props: {
|
||||
prefix: 'next-',
|
||||
children: '商家品质页面管理',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Breadcrumb.Item',
|
||||
id: 'node_dockcy8n9xem',
|
||||
props: {
|
||||
prefix: 'next-',
|
||||
children: '质检知识条配置',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Box',
|
||||
id: 'node_dockcy8n9xeo',
|
||||
props: {
|
||||
style: {
|
||||
marginTop: '12px',
|
||||
backgroundColor: '#ffffff',
|
||||
},
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Form',
|
||||
id: 'node_dockcy8n9xep',
|
||||
props: {
|
||||
inline: true,
|
||||
style: {
|
||||
marginTop: '12px',
|
||||
marginRight: '12px',
|
||||
marginLeft: '12px',
|
||||
},
|
||||
__events: [],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Form.Item',
|
||||
id: 'node_dockcy8n9xeq',
|
||||
props: {
|
||||
style: {
|
||||
marginBottom: '0',
|
||||
},
|
||||
label: '类目名:',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Select',
|
||||
id: 'node_dockcy8n9xer',
|
||||
props: {
|
||||
mode: 'single',
|
||||
hasArrow: true,
|
||||
cacheValue: true,
|
||||
style: {
|
||||
width: '150px',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Form.Item',
|
||||
id: 'node_dockcy8n9xes',
|
||||
props: {
|
||||
style: {
|
||||
marginBottom: '0',
|
||||
},
|
||||
label: '项目类型:',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Select',
|
||||
id: 'node_dockcy8n9xet',
|
||||
props: {
|
||||
mode: 'single',
|
||||
hasArrow: true,
|
||||
cacheValue: true,
|
||||
style: {
|
||||
width: '200px',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Form.Item',
|
||||
id: 'node_dockcy8n9xeu',
|
||||
props: {
|
||||
style: {
|
||||
marginBottom: '0',
|
||||
},
|
||||
label: '项目 ID:',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Input',
|
||||
id: 'node_dockcy8n9xev',
|
||||
props: {
|
||||
hasBorder: true,
|
||||
size: 'medium',
|
||||
autoComplete: 'off',
|
||||
style: {
|
||||
width: '200px',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Button.Group',
|
||||
id: 'node_dockcy8n9xew',
|
||||
props: {},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Button',
|
||||
id: 'node_dockcy8n9xex',
|
||||
props: {
|
||||
type: 'primary',
|
||||
style: {
|
||||
margin: '0 5px 0 5px',
|
||||
},
|
||||
htmlType: 'submit',
|
||||
children: '搜索',
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Button',
|
||||
id: 'node_dockcy8n9xe10',
|
||||
props: {
|
||||
type: 'normal',
|
||||
style: {
|
||||
margin: '0 5px 0 5px',
|
||||
},
|
||||
htmlType: 'reset',
|
||||
children: '清空',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Box',
|
||||
id: 'node_dockcy8n9xe1f',
|
||||
props: {
|
||||
style: {
|
||||
backgroundColor: '#ffffff',
|
||||
paddingBottom: '24px',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Button',
|
||||
id: 'node_dockd5nrh9p4',
|
||||
props: {
|
||||
type: 'primary',
|
||||
size: 'medium',
|
||||
htmlType: 'button',
|
||||
component: 'button',
|
||||
children: '新建配置',
|
||||
style: {},
|
||||
__events: [
|
||||
{
|
||||
type: 'componentEvent',
|
||||
name: 'onClick',
|
||||
relatedEventName: 'onClick',
|
||||
},
|
||||
],
|
||||
onClick: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(){ this.onClick() }',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Box',
|
||||
id: 'node_dockd5nrh9p5',
|
||||
props: {},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Table',
|
||||
id: 'node_dockjielosj1',
|
||||
props: {
|
||||
showMiniPager: true,
|
||||
showActionBar: true,
|
||||
actionBar: [
|
||||
{
|
||||
title: '新增',
|
||||
type: 'primary',
|
||||
},
|
||||
{
|
||||
title: '编辑',
|
||||
},
|
||||
],
|
||||
columns: [
|
||||
{
|
||||
dataKey: 'name',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
title: '姓名',
|
||||
editType: 'text',
|
||||
},
|
||||
{
|
||||
dataKey: 'age',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
title: '年龄',
|
||||
},
|
||||
{
|
||||
dataKey: 'email',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
title: '邮箱',
|
||||
},
|
||||
],
|
||||
data: [
|
||||
{
|
||||
name: '王小',
|
||||
id: '1',
|
||||
age: 15000,
|
||||
email: 'aaa@abc.com',
|
||||
},
|
||||
{
|
||||
name: '王中',
|
||||
id: '2',
|
||||
age: 25000,
|
||||
email: 'bbb@abc.com',
|
||||
},
|
||||
{
|
||||
name: '王大',
|
||||
id: '3',
|
||||
age: 35000,
|
||||
email: 'ccc@abc.com',
|
||||
},
|
||||
],
|
||||
actionTitle: '操作',
|
||||
actionWidth: 180,
|
||||
actionType: 'link',
|
||||
actionFixed: 'right',
|
||||
actionHidden: false,
|
||||
maxWebShownActionCount: 2,
|
||||
actionColumn: [
|
||||
{
|
||||
title: '编辑',
|
||||
callback: {
|
||||
type: 'JSFunction',
|
||||
value: '(rowData, action, table) => {\n return table.editRow(rowData).then((row) => {\n console.log(row);\n });\n }',
|
||||
},
|
||||
device: [
|
||||
'desktop',
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '保存',
|
||||
callback: {
|
||||
type: 'JSFunction',
|
||||
value: '(rowData, action, table) => { \nreturn table.saveRow(rowData).then((row) => { \nconsole.log(row); \n}); \n}',
|
||||
},
|
||||
mode: 'EDIT',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
componentName: 'Box',
|
||||
id: 'node_dockd5nrh9pg',
|
||||
props: {
|
||||
style: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Pagination',
|
||||
id: 'node_dockd5nrh9pf',
|
||||
props: {
|
||||
prefix: 'next-',
|
||||
type: 'normal',
|
||||
shape: 'normal',
|
||||
size: 'medium',
|
||||
defaultCurrent: 1,
|
||||
total: 100,
|
||||
pageShowCount: 5,
|
||||
pageSize: 10,
|
||||
pageSizePosition: 'start',
|
||||
showJump: true,
|
||||
style: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Dialog',
|
||||
id: 'node_dockcy8n9xe1h',
|
||||
props: {
|
||||
prefix: 'next-',
|
||||
footerAlign: 'right',
|
||||
footerActions: [
|
||||
'ok',
|
||||
'cancel',
|
||||
],
|
||||
closeable: 'esc,close',
|
||||
hasMask: true,
|
||||
align: 'cc cc',
|
||||
minMargin: 40,
|
||||
visible: {
|
||||
type: 'JSExpression',
|
||||
value: 'this.state.isShowDialog',
|
||||
},
|
||||
title: '标题',
|
||||
events: [],
|
||||
__events: [
|
||||
{
|
||||
type: 'componentEvent',
|
||||
name: 'onCancel',
|
||||
relatedEventName: 'closeDialog',
|
||||
},
|
||||
{
|
||||
type: 'componentEvent',
|
||||
name: 'onClose',
|
||||
relatedEventName: 'closeDialog',
|
||||
},
|
||||
{
|
||||
type: 'componentEvent',
|
||||
name: 'onOk',
|
||||
relatedEventName: 'testFunc',
|
||||
},
|
||||
],
|
||||
onCancel: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(){ this.closeDialog() }',
|
||||
},
|
||||
onClose: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(){ this.closeDialog() }',
|
||||
},
|
||||
onOk: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(){ this.testFunc() }',
|
||||
},
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Form',
|
||||
id: 'node_dockd5nrh9pi',
|
||||
props: {
|
||||
inline: false,
|
||||
labelAlign: 'top',
|
||||
labelTextAlign: 'right',
|
||||
size: 'medium',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Form.Item',
|
||||
id: 'node_dockd5nrh9pj',
|
||||
props: {
|
||||
style: {
|
||||
marginBottom: '0',
|
||||
minWidth: '200px',
|
||||
minHeight: '28px',
|
||||
},
|
||||
label: '商品类目',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Select',
|
||||
id: 'node_dockd5nrh9pk',
|
||||
props: {
|
||||
mode: 'single',
|
||||
hasArrow: true,
|
||||
cacheValue: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Form.Item',
|
||||
id: 'node_dockd5nrh9pl',
|
||||
props: {
|
||||
style: {
|
||||
marginBottom: '0',
|
||||
minWidth: '200px',
|
||||
minHeight: '28px',
|
||||
},
|
||||
label: '商品类目',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Select',
|
||||
id: 'node_dockd5nrh9pm',
|
||||
props: {
|
||||
mode: 'single',
|
||||
hasArrow: true,
|
||||
cacheValue: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Form.Item',
|
||||
id: 'node_dockd5nrh9pn',
|
||||
props: {
|
||||
style: {
|
||||
marginBottom: '0',
|
||||
minWidth: '200px',
|
||||
minHeight: '28px',
|
||||
},
|
||||
label: '商品类目',
|
||||
asterisk: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Select',
|
||||
id: 'node_dockd5nrh9po',
|
||||
props: {
|
||||
mode: 'single',
|
||||
hasArrow: true,
|
||||
cacheValue: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Form.Item',
|
||||
id: 'node_dockd5nrh9pp',
|
||||
props: {
|
||||
style: {
|
||||
marginBottom: '0',
|
||||
minWidth: '200px',
|
||||
minHeight: '28px',
|
||||
},
|
||||
label: '商品类目',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Input',
|
||||
id: 'node_dockd5nrh9pr',
|
||||
props: {
|
||||
hasBorder: true,
|
||||
size: 'medium',
|
||||
autoComplete: 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'ErrorComponent',
|
||||
id: 'node_dockd5nrh9pr',
|
||||
props: {
|
||||
name: 'error',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
241
packages/renderer-core/test/renderer/renderer.test.tsx
Normal file
241
packages/renderer-core/test/renderer/renderer.test.tsx
Normal file
@ -0,0 +1,241 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import schema from '../fixtures/schema/basic';
|
||||
import '../utils/mock-react-render';
|
||||
import rendererFactory from '../../src/renderer/renderer';
|
||||
import components from '../utils/components';
|
||||
|
||||
const Renderer = rendererFactory();
|
||||
|
||||
function getComp(schema, comp = null): Promise<{
|
||||
component,
|
||||
inst,
|
||||
}> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const component = renderer.create(
|
||||
// @ts-ignore
|
||||
<Renderer
|
||||
schema={schema}
|
||||
components={components}
|
||||
/>);
|
||||
|
||||
const componentInstance = component.root;
|
||||
|
||||
setTimeout(() => {
|
||||
resolve({
|
||||
inst: comp ? componentInstance.findAllByType(comp) : null,
|
||||
component,
|
||||
});
|
||||
}, 20);
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
});
|
||||
|
||||
let componentSnapshot;
|
||||
|
||||
afterEach(() => {
|
||||
if (componentSnapshot) {
|
||||
let tree = componentSnapshot.toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
componentSnapshot = null;
|
||||
}
|
||||
});
|
||||
|
||||
describe('Base Render', () => {
|
||||
it('renderComp', () => {
|
||||
const content = (
|
||||
// @ts-ignore
|
||||
<Renderer
|
||||
schema={schema}
|
||||
components={components}
|
||||
/>);
|
||||
const tree = renderer.create(content).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('JSExpression', () => {
|
||||
it('base props', (done) => {
|
||||
const schema = {
|
||||
componentName: 'Page',
|
||||
props: {},
|
||||
children: [
|
||||
{
|
||||
componentName: "Div",
|
||||
props: {
|
||||
className: 'div-ut',
|
||||
text: "123",
|
||||
visible: true,
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
getComp(schema, components.Div).then(({ component, inst }) => {
|
||||
expect(inst[0].props.text).toBe('123');
|
||||
expect(inst[0].props.visible).toBeTruthy();
|
||||
|
||||
componentSnapshot = component;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('JSExpression props', (done) => {
|
||||
const schema = {
|
||||
componentName: 'Page',
|
||||
props: {},
|
||||
state: {
|
||||
isShowDialog: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: "Div",
|
||||
props: {
|
||||
className: "div-ut",
|
||||
visible: {
|
||||
type: 'JSExpression',
|
||||
value: 'this.state.isShowDialog',
|
||||
},
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
getComp(schema, components.Div).then(({ component, inst }) => {
|
||||
expect(inst[0].props.visible).toBeTruthy();
|
||||
componentSnapshot = component;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('JSExpression props with loop', (done) => {
|
||||
const schema = {
|
||||
componentName: 'Page',
|
||||
props: {},
|
||||
state: {
|
||||
isShowDialog: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: "Div",
|
||||
loop: [
|
||||
{
|
||||
name: '1',
|
||||
},
|
||||
{
|
||||
name: '2'
|
||||
}
|
||||
],
|
||||
props: {
|
||||
className: "div-ut",
|
||||
name1: {
|
||||
type: 'JSExpression',
|
||||
value: 'this.item.name',
|
||||
},
|
||||
name2: {
|
||||
type: 'JSExpression',
|
||||
value: 'item.name',
|
||||
},
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
getComp(schema, components.Div).then(({ component, inst }) => {
|
||||
// expect(inst[0].props.visible).toBeTruthy();
|
||||
expect(inst.length).toEqual(2);
|
||||
[1, 2].forEach((i) => {
|
||||
expect(inst[0].props[`name${i}`]).toBe('1');
|
||||
expect(inst[1].props[`name${i}`]).toBe('2');
|
||||
})
|
||||
componentSnapshot = component;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('JSFunction props with loop', (done) => {
|
||||
const schema = {
|
||||
componentName: 'Page',
|
||||
props: {},
|
||||
state: {
|
||||
isShowDialog: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: "Div",
|
||||
loop: [
|
||||
{
|
||||
name: '1',
|
||||
},
|
||||
{
|
||||
name: '2'
|
||||
}
|
||||
],
|
||||
props: {
|
||||
className: "div-ut",
|
||||
onClick1: {
|
||||
type: 'JSFunction',
|
||||
value: '() => this.item.name',
|
||||
},
|
||||
onClick2: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(){ return this.item.name }',
|
||||
},
|
||||
onClick3: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(){ return item.name }',
|
||||
},
|
||||
onClick4: {
|
||||
type: 'JSFunction',
|
||||
value: '() => item.name',
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
getComp(schema, components.Div).then(({ component, inst }) => {
|
||||
// expect(inst[0].props.visible).toBeTruthy();
|
||||
expect(inst.length).toEqual(2);
|
||||
[1, 2, 3, 4].forEach((i) => {
|
||||
expect(inst[0].props[`onClick${i}`]()).toBe('1');
|
||||
expect(inst[1].props[`onClick${i}`]()).toBe('2');
|
||||
})
|
||||
componentSnapshot = component;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('JSFunction props', (done) => {
|
||||
const schema = {
|
||||
componentName: 'Page',
|
||||
props: {},
|
||||
state: {
|
||||
isShowDialog: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: "Div",
|
||||
props: {
|
||||
className: "div-ut",
|
||||
onClick: {
|
||||
type: 'JSExpression',
|
||||
value: '() => this.state.isShowDialog',
|
||||
},
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
getComp(schema, components.Div).then(({ component, inst }) => {
|
||||
expect(!!inst[0].props.onClick).toBeTruthy();
|
||||
expect(inst[0].props.onClick()).toBeTruthy();
|
||||
|
||||
componentSnapshot = component;
|
||||
done();
|
||||
});;
|
||||
});
|
||||
})
|
||||
22
packages/renderer-core/test/utils/components.tsx
Normal file
22
packages/renderer-core/test/utils/components.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import { Box, Breadcrumb, Form, Select, Input, Button, Table, Pagination, Dialog } from '@alifd/next';
|
||||
|
||||
const Div = (props) => (<div {...props}>{props.children}</div>);
|
||||
|
||||
const components = {
|
||||
Box,
|
||||
Breadcrumb,
|
||||
'Breadcrumb.Item': Breadcrumb.Item,
|
||||
Form,
|
||||
'Form.Item': Form.Item,
|
||||
Select,
|
||||
Input,
|
||||
Button,
|
||||
'Button.Group': Button.Group,
|
||||
Table,
|
||||
Pagination,
|
||||
Dialog,
|
||||
ErrorComponent: Select,
|
||||
Div,
|
||||
};
|
||||
|
||||
export default components;
|
||||
66
packages/renderer-core/test/utils/mock-react-render.ts
Normal file
66
packages/renderer-core/test/utils/mock-react-render.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import React, { Component, PureComponent, createElement, createContext, forwardRef, ReactInstance, ContextType } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import {
|
||||
adapter,
|
||||
pageRendererFactory,
|
||||
componentRendererFactory,
|
||||
blockRendererFactory,
|
||||
addonRendererFactory,
|
||||
tempRendererFactory,
|
||||
rendererFactory,
|
||||
types,
|
||||
} from '../../src';
|
||||
import ConfigProvider from '@alifd/next/lib/config-provider';
|
||||
|
||||
window.React = React;
|
||||
(window as any).ReactDom = ReactDOM;
|
||||
|
||||
adapter.setRuntime({
|
||||
Component,
|
||||
PureComponent,
|
||||
createContext,
|
||||
createElement,
|
||||
forwardRef,
|
||||
findDOMNode: ReactDOM.findDOMNode,
|
||||
});
|
||||
|
||||
adapter.setRenderers({
|
||||
PageRenderer: pageRendererFactory(),
|
||||
ComponentRenderer: componentRendererFactory(),
|
||||
BlockRenderer: blockRendererFactory(),
|
||||
AddonRenderer: addonRendererFactory(),
|
||||
TempRenderer: tempRendererFactory(),
|
||||
DivRenderer: blockRendererFactory(),
|
||||
});
|
||||
|
||||
adapter.setConfigProvider(ConfigProvider);
|
||||
|
||||
function factory() {
|
||||
const Renderer = rendererFactory();
|
||||
return class ReactRenderer extends Renderer implements Component {
|
||||
readonly props: types.IProps;
|
||||
|
||||
context: ContextType<any>;
|
||||
|
||||
setState: (
|
||||
state: types.IState,
|
||||
callback?: () => void,
|
||||
) => void;
|
||||
|
||||
forceUpdate: (callback?: () => void) => void;
|
||||
|
||||
refs: {
|
||||
[key: string]: ReactInstance,
|
||||
};
|
||||
|
||||
constructor(props: types.IProps, context: ContextType<any>) {
|
||||
super(props, context);
|
||||
}
|
||||
|
||||
isValidComponent(obj: any) {
|
||||
return obj?.prototype?.isReactComponent || obj?.prototype instanceof Component;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default factory();
|
||||
Loading…
x
Reference in New Issue
Block a user