mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-04-17 02:48:05 +00:00
chore: delete useless codes
This commit is contained in:
parent
03f7c76284
commit
fedbbca845
@ -3,7 +3,8 @@
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"packages/*",
|
||||
"runtime/*"
|
||||
"runtime/*",
|
||||
"examples/*"
|
||||
],
|
||||
"nohoist": [
|
||||
"**/css-modules-typescript-loader",
|
||||
|
||||
@ -1 +0,0 @@
|
||||
module.exports = require('../../babel.config');
|
||||
@ -1,45 +0,0 @@
|
||||
{
|
||||
"entry": {
|
||||
"AliLowCodeEngine": "../engine/src/index.ts",
|
||||
"ReactSimulatorRenderer": "../react-simulator-renderer/src/index.ts"
|
||||
},
|
||||
"vendor": false,
|
||||
"devServer": {
|
||||
"liveReload": false,
|
||||
"hot": false
|
||||
},
|
||||
"library": "[name]",
|
||||
"publicPath": "/",
|
||||
"externals": {
|
||||
"react": "var window.React",
|
||||
"react-dom": "var window.ReactDOM",
|
||||
"prop-types": "var window.PropTypes",
|
||||
"@alifd/next": "var window.Next",
|
||||
"rax": "var window.Rax",
|
||||
"@alilc/lowcode-engine": "var window.AliLowCodeEngine",
|
||||
"@alilc/lowcode-engine-ext": "var window.AliLowCodeEngineExt",
|
||||
"moment": "var moment",
|
||||
"lodash": "var _"
|
||||
},
|
||||
"plugins": [
|
||||
[
|
||||
"build-plugin-react-app"
|
||||
],
|
||||
[
|
||||
"build-plugin-fusion",
|
||||
{
|
||||
"themePackage": "@alifd/theme-lowcode-light",
|
||||
"externalNext": "umd"
|
||||
}
|
||||
],
|
||||
[
|
||||
"build-plugin-moment-locales",
|
||||
{
|
||||
"locales": [
|
||||
"zh-cn"
|
||||
]
|
||||
}
|
||||
],
|
||||
"./build.plugin.js"
|
||||
]
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
||||
const fse = require('fs-extra');
|
||||
// read from lerna
|
||||
const lernaConfig = JSON.parse(fse.readFileSync('../../lerna.json', 'utf8'));
|
||||
const { version } = lernaConfig;
|
||||
|
||||
module.exports = ({ context, onGetWebpackConfig }) => {
|
||||
onGetWebpackConfig((config) => {
|
||||
config.resolve.plugin('tsconfigpaths').use(TsconfigPathsPlugin, [
|
||||
{
|
||||
configFile: './tsconfig.json',
|
||||
},
|
||||
]);
|
||||
config
|
||||
.plugin('define')
|
||||
.use(context.webpack.DefinePlugin, [{
|
||||
VERSION_PLACEHOLDER: JSON.stringify(version),
|
||||
}]);
|
||||
config.plugins.delete('hot');
|
||||
config.devServer.hot(false);
|
||||
if (context.command === 'start') {
|
||||
config.devtool('inline-source-map');
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -1,47 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const { join } = require('path');
|
||||
const esModules = [].join('|');
|
||||
const pkgNames = fs.readdirSync(join('..')).filter(pkgName => !pkgName.startsWith('.'));
|
||||
|
||||
const jestConfig = {
|
||||
// transform: {
|
||||
// '^.+\\.[jt]sx?$': 'babel-jest',
|
||||
// // '^.+\\.(ts|tsx)$': 'ts-jest',
|
||||
// // '^.+\\.(js|jsx)$': 'babel-jest',
|
||||
// },
|
||||
// testMatch: ['**/node-children.test.ts'],
|
||||
// testMatch: ['**/plugin-manager.test.ts'],
|
||||
// testMatch: ['**/history/history.test.ts'],
|
||||
// testMatch: ['**/document-model.test.ts'],
|
||||
// testMatch: ['**/prop.test.ts'],
|
||||
// testMatch: ['(/tests?/.*(test))\\.[jt]s$'],
|
||||
transformIgnorePatterns: [
|
||||
`/node_modules/(?!${esModules})/`,
|
||||
],
|
||||
setupFiles: ['./tests/fixtures/unhandled-rejection.ts'],
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
|
||||
collectCoverage: false,
|
||||
collectCoverageFrom: [
|
||||
'src/**/*.ts',
|
||||
'!src/**/*.d.ts',
|
||||
'!src/icons/**',
|
||||
'!src/locale/**',
|
||||
'!src/builtin-simulator/utils/**',
|
||||
'!src/plugin/sequencify.ts',
|
||||
'!src/document/node/exclusive-group.ts',
|
||||
'!src/document/node/props/value-to-source.ts',
|
||||
'!src/builtin-simulator/live-editing/live-editing.ts',
|
||||
'!src/designer/offset-observer.ts',
|
||||
'!src/designer/clipboard.ts',
|
||||
'!src/designer/scroller.ts',
|
||||
'!src/builtin-simulator/host.ts',
|
||||
'!**/node_modules/**',
|
||||
'!**/vendor/**',
|
||||
],
|
||||
};
|
||||
|
||||
// 只对本仓库内的 pkg 做 mapping
|
||||
jestConfig.moduleNameMapper = {};
|
||||
jestConfig.moduleNameMapper[`^@alilc/lowcode\\-(${pkgNames.join('|')})$`] = '<rootDir>/../$1/src';
|
||||
|
||||
module.exports = jestConfig;
|
||||
@ -1,26 +0,0 @@
|
||||
{
|
||||
"name": "@alilc/lowcode-ignitor",
|
||||
"version": "1.3.2",
|
||||
"description": "点火器,bootstrap lce project",
|
||||
"main": "lib/index.js",
|
||||
"private": true,
|
||||
"files": [
|
||||
"dist",
|
||||
"es",
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "build-scripts start --disable-open --port 5555"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@alib/build-scripts": "^0.1.18",
|
||||
"fs-extra": "^10.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "http",
|
||||
"url": "https://github.com/alibaba/lowcode-engine/tree/main/packages/ignitor"
|
||||
},
|
||||
"bugs": "https://github.com/alibaba/lowcode-engine/issues",
|
||||
"homepage": "https://github.com/alibaba/lowcode-engine/#readme"
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.5 KiB |
@ -1,20 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>LowCodeEngine Editor DEMO</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>
|
||||
This project only provides engine resource files. For usage, go for
|
||||
<a href="https://github.com/alibaba/lowcode-demo" target="_blank">Lowcode Demo</a>
|
||||
</h1>
|
||||
<h2>
|
||||
For local debugging of lowcode engine, please visit
|
||||
<a href="https://lowcode-engine.cn/site/docs/participate/prepare" target="_blank">proxy documentation</a>
|
||||
to get more information.
|
||||
</h2>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib"
|
||||
},
|
||||
"include": [
|
||||
"./src/"
|
||||
]
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
{
|
||||
"plugins": [
|
||||
"@alilc/build-plugin-lce",
|
||||
"build-plugin-fusion",
|
||||
["build-plugin-moment-locales", {
|
||||
"locales": ["zh-cn"]
|
||||
}]
|
||||
]
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"plugins": [
|
||||
"@alilc/build-plugin-lce",
|
||||
"@alilc/lowcode-test-mate/plugin/index.ts"
|
||||
]
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
{
|
||||
"entry": {
|
||||
"react-renderer": "src/index"
|
||||
},
|
||||
"sourceMap": true,
|
||||
"library": "AliLowCodeReactRenderer",
|
||||
"libraryTarget": "umd",
|
||||
"externals": {
|
||||
"react": "var window.React",
|
||||
"react-dom": "var window.ReactDOM",
|
||||
"prop-types": "var window.PropTypes",
|
||||
"@alifd/next": "var Next",
|
||||
"moment": "var window.moment"
|
||||
},
|
||||
"polyfill": false,
|
||||
"outputDir": "dist",
|
||||
"vendor": false,
|
||||
"ignoreHtmlTemplate": true,
|
||||
"plugins": [
|
||||
"build-plugin-react-app",
|
||||
["build-plugin-fusion", {
|
||||
"externalNext": "umd"
|
||||
}],
|
||||
["build-plugin-moment-locales", {
|
||||
"locales": ["zh-cn"]
|
||||
}]
|
||||
]
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
---
|
||||
title: 复杂组件
|
||||
order: 2
|
||||
---
|
||||
|
||||
````jsx
|
||||
import React, { PureComponent } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactRenderer from '@alilc/lowcode-react-renderer';
|
||||
import schema from './schemas/compose';
|
||||
import components from './config/components/index';
|
||||
import utils from './config/utils';
|
||||
import constants from './config/constants';
|
||||
|
||||
class Demo extends PureComponent {
|
||||
static displayName = 'renderer-demo';
|
||||
render() {
|
||||
return (
|
||||
<div className="demo">
|
||||
<ReactRenderer
|
||||
key={schema.fileName}
|
||||
schema={schema}
|
||||
components={components}
|
||||
appHelper={{
|
||||
utils,
|
||||
constants
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render((
|
||||
<Demo />
|
||||
), mountNode);
|
||||
````
|
||||
@ -1,11 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
export default class AView extends PureComponent {
|
||||
static displayName = 'A';
|
||||
|
||||
static version = '0.0.0';
|
||||
|
||||
render() {
|
||||
return <a {...this.props} />;
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
export default class DivView extends PureComponent {
|
||||
static displayName = 'Div';
|
||||
|
||||
static version = '0.0.0';
|
||||
|
||||
render() {
|
||||
return <div {...this.props} />;
|
||||
}
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
export default class ImageView extends PureComponent {
|
||||
static displayName = 'Image';
|
||||
|
||||
static version = '0.0.0';
|
||||
|
||||
static defaultProps = {
|
||||
src: '//img.alicdn.com/tfs/TB1knm4bqNj0u4jSZFyXXXgMVXa-240-240.png_100x100.jpg',
|
||||
};
|
||||
|
||||
render() {
|
||||
return <img {...this.props} />;
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default class TextView extends PureComponent {
|
||||
static displayName = 'Text';
|
||||
|
||||
static version = '0.0.0';
|
||||
|
||||
static propTypes = {
|
||||
text: PropTypes.string,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { text, ...restProps } = this.props;
|
||||
let textNode = text;
|
||||
// 强制类型转换
|
||||
try {
|
||||
textNode = text.toString();
|
||||
} catch (e) {
|
||||
textNode = '';
|
||||
}
|
||||
if (window.__ctx && window.__ctx.canvasAppHelper) {
|
||||
textNode = textNode || 'Text';
|
||||
}
|
||||
return <span {...restProps}>{textNode}</span>;
|
||||
}
|
||||
}
|
||||
@ -1,175 +0,0 @@
|
||||
import Div from './Div';
|
||||
import Text from './Text';
|
||||
import A from './A';
|
||||
import Image from './Image';
|
||||
|
||||
import {
|
||||
Balloon,
|
||||
Button,
|
||||
Checkbox,
|
||||
Dropdown,
|
||||
Grid,
|
||||
Menu,
|
||||
Select,
|
||||
Tab,
|
||||
Table,
|
||||
Radio,
|
||||
Pagination,
|
||||
Input,
|
||||
Icon,
|
||||
Switch,
|
||||
Tree,
|
||||
NumberPicker,
|
||||
Collapse,
|
||||
Range,
|
||||
Dialog,
|
||||
Overlay,
|
||||
Search,
|
||||
Loading,
|
||||
MenuButton,
|
||||
Badge,
|
||||
Message,
|
||||
Slider,
|
||||
SplitButton,
|
||||
Paragraph,
|
||||
Nav,
|
||||
Breadcrumb,
|
||||
Step,
|
||||
DatePicker,
|
||||
TimePicker,
|
||||
Rating,
|
||||
Upload,
|
||||
Tag,
|
||||
Card,
|
||||
Calendar,
|
||||
Progress,
|
||||
Cascader,
|
||||
ConfigProvider,
|
||||
Animate,
|
||||
CascaderSelect,
|
||||
Transfer,
|
||||
TreeSelect,
|
||||
Timeline,
|
||||
VirtualList,
|
||||
} from '@alifd/next';
|
||||
|
||||
const { Row, Col } = Grid;
|
||||
const {
|
||||
Item: MenuItem,
|
||||
Group: MenuGroup,
|
||||
SubMenu,
|
||||
PopupItem: MenuPopupItem,
|
||||
CheckboxItem: MenuCheckboxItem,
|
||||
RadioItem: MenuRadioItem,
|
||||
Divider: MenuDivider,
|
||||
} = Menu;
|
||||
const { Item: TabItem } = Tab;
|
||||
const { Column: TableColumn, ColumnGroup: TableColumnGroup } = Table;
|
||||
const { Group: ButtonGroup } = Button;
|
||||
const { Group: RadioGroup } = Radio;
|
||||
const { Node: TreeNode } = Tree;
|
||||
const { Panel: CollapsePanel } = Collapse;
|
||||
const { Tooltip } = Balloon;
|
||||
const { AutoComplete: SelectAutoComplete, OptionGroup: SelectOptionGroup, Option: SelectOption } = Select;
|
||||
const { Item: MenuButtonItem } = MenuButton;
|
||||
const { Item: StepItem } = Step;
|
||||
const { Item: NavItem, SubNav, PopupItem: NavPopItem, Group: NavGroup } = Nav;
|
||||
const { Item: BreadcrumbItem } = Breadcrumb;
|
||||
const { MonthPicker, RangePicker, YearPicker } = DatePicker;
|
||||
const { Card: UploadCard, Dragger: UploadDragger, Selecter: UploadSelecter } = Upload;
|
||||
const { Closeable: TagCloseable, Selectable: TagSelectable } = Tag;
|
||||
const { Popup } = Overlay;
|
||||
const { Node: TreeSelectNode } = TreeSelect;
|
||||
const { Item: TimelineItem } = Timeline;
|
||||
|
||||
export default {
|
||||
Div,
|
||||
A,
|
||||
Text,
|
||||
Image,
|
||||
|
||||
Balloon,
|
||||
Tooltip,
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Checkbox,
|
||||
Row,
|
||||
Col,
|
||||
Select,
|
||||
SelectAutoComplete,
|
||||
SelectOptionGroup,
|
||||
SelectOption,
|
||||
Dropdown,
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuGroup,
|
||||
MenuDivider,
|
||||
SubMenu,
|
||||
MenuPopupItem,
|
||||
MenuCheckboxItem,
|
||||
MenuRadioItem,
|
||||
MenuButton,
|
||||
MenuButtonItem,
|
||||
Loading,
|
||||
Tab,
|
||||
TabItem,
|
||||
Table,
|
||||
TableColumn,
|
||||
TableColumnGroup,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Pagination,
|
||||
Input,
|
||||
Icon,
|
||||
Switch,
|
||||
Tree,
|
||||
TreeNode,
|
||||
NumberPicker,
|
||||
Collapse,
|
||||
Dialog,
|
||||
Overlay,
|
||||
Popup,
|
||||
CollapsePanel,
|
||||
Range,
|
||||
Search,
|
||||
Badge,
|
||||
Message,
|
||||
Slider,
|
||||
SplitButton,
|
||||
Paragraph,
|
||||
Nav,
|
||||
NavItem,
|
||||
NavPopItem,
|
||||
NavGroup,
|
||||
SubNav,
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
Rating,
|
||||
Step,
|
||||
StepItem,
|
||||
DatePicker,
|
||||
MonthPicker,
|
||||
RangePicker,
|
||||
YearPicker,
|
||||
TimePicker,
|
||||
Upload,
|
||||
UploadCard,
|
||||
UploadDragger,
|
||||
UploadSelecter,
|
||||
Tag,
|
||||
TagCloseable,
|
||||
TagSelectable,
|
||||
Card,
|
||||
Calendar,
|
||||
Progress,
|
||||
Cascader,
|
||||
ConfigProvider,
|
||||
Animate,
|
||||
CascaderSelect,
|
||||
Transfer,
|
||||
TreeSelect,
|
||||
TreeSelectNode,
|
||||
Timeline,
|
||||
TimelineItem,
|
||||
VirtualList,
|
||||
};
|
||||
@ -1,3 +0,0 @@
|
||||
export default {
|
||||
name: 'renderer-demo',
|
||||
};
|
||||
10
packages/react-renderer/demo/config/utils.js
vendored
10
packages/react-renderer/demo/config/utils.js
vendored
@ -1,10 +0,0 @@
|
||||
import { Message } from '@alifd/next';
|
||||
import moment from 'moment';
|
||||
|
||||
export default {
|
||||
Message,
|
||||
moment,
|
||||
test(msg) {
|
||||
this.Message.notice(msg);
|
||||
},
|
||||
};
|
||||
@ -1,37 +0,0 @@
|
||||
---
|
||||
title: 数据源使用
|
||||
order: 4
|
||||
---
|
||||
|
||||
````jsx
|
||||
import React, { PureComponent } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactRenderer from '@alilc/lowcode-react-renderer';
|
||||
import schema from './schemas/dataSource';
|
||||
import components from './config/components/index';
|
||||
import utils from './config/utils';
|
||||
import constants from './config/constants';
|
||||
|
||||
class Demo extends PureComponent {
|
||||
static displayName = 'renderer-demo';
|
||||
render() {
|
||||
return (
|
||||
<div className="demo">
|
||||
<ReactRenderer
|
||||
key={schema.fileName}
|
||||
schema={schema}
|
||||
components={components}
|
||||
appHelper={{
|
||||
utils,
|
||||
constants
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render((
|
||||
<Demo />
|
||||
), mountNode);
|
||||
````
|
||||
@ -1,42 +0,0 @@
|
||||
---
|
||||
title: 国际化
|
||||
order: 5
|
||||
---
|
||||
|
||||
````jsx
|
||||
import React, { PureComponent } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactRenderer from '@alilc/lowcode-react-renderer';
|
||||
import schema from './schemas/i18n';
|
||||
import components from './config/components/index';
|
||||
import utils from './config/utils';
|
||||
import constants from './config/constants';
|
||||
|
||||
class Demo extends PureComponent {
|
||||
static displayName = 'renderer-demo';
|
||||
render() {
|
||||
return (
|
||||
<div className="demo">
|
||||
<ReactRenderer
|
||||
key={schema.fileName}
|
||||
schema={schema}
|
||||
components={components}
|
||||
appHelper={{
|
||||
utils,
|
||||
constants
|
||||
}}
|
||||
locale="zh-CN"
|
||||
messages={{
|
||||
"hello": "你好",
|
||||
"china": "中国"
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render((
|
||||
<Demo />
|
||||
), mountNode);
|
||||
````
|
||||
@ -1,37 +0,0 @@
|
||||
---
|
||||
title: 列表
|
||||
order: 1
|
||||
---
|
||||
|
||||
````jsx
|
||||
import React, { PureComponent } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactRenderer from '@alilc/lowcode-react-renderer';
|
||||
import schema from './schemas/list';
|
||||
import components from './config/components/index';
|
||||
import utils from './config/utils';
|
||||
import constants from './config/constants';
|
||||
|
||||
class Demo extends PureComponent {
|
||||
static displayName = 'renderer-demo';
|
||||
render() {
|
||||
return (
|
||||
<div className="demo">
|
||||
<ReactRenderer
|
||||
key={schema.fileName}
|
||||
schema={schema}
|
||||
components={components}
|
||||
appHelper={{
|
||||
utils,
|
||||
constants
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render((
|
||||
<Demo />
|
||||
), mountNode);
|
||||
````
|
||||
61
packages/react-renderer/demo/schemas/compose.js
vendored
61
packages/react-renderer/demo/schemas/compose.js
vendored
@ -1,61 +0,0 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
componentName: 'Page',
|
||||
fileName: 'compose',
|
||||
props: {
|
||||
},
|
||||
children: [
|
||||
{
|
||||
"componentName": "Dropdown",
|
||||
"props": {
|
||||
"trigger": [
|
||||
{
|
||||
"componentName": "Button",
|
||||
"props": {
|
||||
"type": "primary"
|
||||
},
|
||||
"children": "确定"
|
||||
}
|
||||
],
|
||||
"triggerType": "click"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Menu",
|
||||
"props": {
|
||||
"style": {
|
||||
"width": 200
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{ "componentName": "MenuItem", "props": {}, "children": "Option 1" },
|
||||
{ "componentName": "MenuItem", "props": { "disabled": false }, "children": "option 2" },
|
||||
{ "componentName": "MenuItem", "props": { "disabled": false }, "children": "option 3" }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "Menu",
|
||||
"props": {
|
||||
"style": {
|
||||
"width": 200
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"componentName": "MenuItem",
|
||||
"props": {
|
||||
},
|
||||
"children": "Option 1"
|
||||
},
|
||||
{
|
||||
"componentName": "MenuItem",
|
||||
"props": {
|
||||
},
|
||||
"children": "Option 2"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
@ -1,37 +0,0 @@
|
||||
export default {
|
||||
componentName: 'Page',
|
||||
fileName: 'dataSource',
|
||||
props: {},
|
||||
children: [{
|
||||
componentName: 'Div',
|
||||
props: {},
|
||||
children: [{
|
||||
componentName: 'Text',
|
||||
props: {
|
||||
text: '{{this.item.title}}',
|
||||
},
|
||||
}, {
|
||||
componentName: 'Switch',
|
||||
props: {
|
||||
checkedChildren: '开',
|
||||
unCheckedChildren: '关',
|
||||
checked: '{{this.item.done}}',
|
||||
},
|
||||
}],
|
||||
loop: '{{this.dataSourceMap.todos.data}}',
|
||||
}],
|
||||
dataSource: {
|
||||
list: [{
|
||||
id: 'todos',
|
||||
isInit: true,
|
||||
type: 'jsonp',
|
||||
options: {
|
||||
method: 'GET',
|
||||
uri: 'https://mocks.alibaba-inc.com/mock/D8iUX7zB/todo_getAll',
|
||||
},
|
||||
dataHandler: function dataHandler(data) {
|
||||
return data.data;
|
||||
},
|
||||
}],
|
||||
},
|
||||
};
|
||||
20
packages/react-renderer/demo/schemas/i18n.js
vendored
20
packages/react-renderer/demo/schemas/i18n.js
vendored
@ -1,20 +0,0 @@
|
||||
export default {
|
||||
componentName: 'Page',
|
||||
fileName: 'i18n',
|
||||
props: {},
|
||||
children: [{
|
||||
componentName: 'Div',
|
||||
props: {},
|
||||
children: [{
|
||||
componentName: 'Text',
|
||||
props: {
|
||||
text: "{{this.i18n('hello')}}",
|
||||
},
|
||||
}, {
|
||||
componentName: 'Text',
|
||||
props: {
|
||||
text: "{{this.i18n('china')}}",
|
||||
},
|
||||
}],
|
||||
}],
|
||||
};
|
||||
225
packages/react-renderer/demo/schemas/list.js
vendored
225
packages/react-renderer/demo/schemas/list.js
vendored
@ -1,225 +0,0 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
componentName: 'Page',
|
||||
fileName: 'tab_article',
|
||||
props: {
|
||||
style: {
|
||||
paddingTop: 20,
|
||||
paddingRight: 20,
|
||||
paddingLeft: 20
|
||||
}
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Div',
|
||||
props: {
|
||||
style: {
|
||||
marginTop: 5,
|
||||
marginBottom: 15,
|
||||
borderBottom: '1px solid rgba(244,244,244)'
|
||||
}
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Div',
|
||||
props: {
|
||||
style: {
|
||||
marginBottom: 15
|
||||
}
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Text',
|
||||
props: {
|
||||
text: '{{this.item.title}}',
|
||||
style: {
|
||||
color: 'rgba(51,51,51)'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
componentName: 'Text',
|
||||
props: {
|
||||
text: '{{this.item.datetime}}',
|
||||
style: {
|
||||
fontSize: '12px',
|
||||
color: '#666',
|
||||
float: 'right'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
componentName: 'Div',
|
||||
props: {
|
||||
style: {
|
||||
paddingBottom: 15,
|
||||
fontSize: '13px',
|
||||
color: '#666'
|
||||
}
|
||||
},
|
||||
children: '{{this.item.description}}'
|
||||
},
|
||||
{
|
||||
componentName: 'Div',
|
||||
props: {
|
||||
style: {
|
||||
marginBottom: 15
|
||||
}
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Button',
|
||||
props: {
|
||||
type: 'normal',
|
||||
style: {
|
||||
marginRight: 5,
|
||||
marginLeft: 5
|
||||
},
|
||||
size: 'small'
|
||||
},
|
||||
children: '{{this.item}}',
|
||||
loop: '{{this.item.tags}}'
|
||||
},
|
||||
{
|
||||
componentName: 'Div',
|
||||
props: {
|
||||
style: {
|
||||
marginBottom: 15,
|
||||
float: 'right'
|
||||
}
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Div',
|
||||
props: {
|
||||
style: {
|
||||
display: 'inline-block',
|
||||
marginRight: 5,
|
||||
marginBottom: 15,
|
||||
marginLeft: 5,
|
||||
fontSize: 12,
|
||||
color: '#666',
|
||||
float: 'none'
|
||||
}
|
||||
},
|
||||
children: '{{"点赞:"+this.item.star}}'
|
||||
},
|
||||
{
|
||||
componentName: 'Div',
|
||||
props: {
|
||||
style: {
|
||||
display: 'inline-block',
|
||||
marginRight: 5,
|
||||
marginBottom: 15,
|
||||
marginLeft: 5,
|
||||
fontSize: 12,
|
||||
color: '#666',
|
||||
float: 'none'
|
||||
}
|
||||
},
|
||||
children: '{{"喜爱:"+this.item.like}}'
|
||||
},
|
||||
{
|
||||
componentName: 'Div',
|
||||
props: {
|
||||
style: {
|
||||
display: 'inline-block',
|
||||
marginRight: 5,
|
||||
marginBottom: 15,
|
||||
marginLeft: 5,
|
||||
fontSize: 12,
|
||||
color: '#66',
|
||||
float: 'none'
|
||||
}
|
||||
},
|
||||
children: '{{"评论:"+this.item.comment}}'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
loop: '{{this.state.dataSource}}'
|
||||
},
|
||||
{
|
||||
componentName: 'Pagination',
|
||||
props: {
|
||||
shape: 'normal',
|
||||
type: 'normal',
|
||||
size: 'medium',
|
||||
style: {
|
||||
marginTop: 10,
|
||||
marginBottom: 30,
|
||||
textAlign: 'right'
|
||||
},
|
||||
onChange: function onChange(current, e) {
|
||||
//页码发生改变时的回调函数
|
||||
//@param {Number} current 改变后的页码数
|
||||
//@param {Object} e 点击事件对象
|
||||
this.page.reloadDataSource();
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
dataSource: {
|
||||
dataHandler: function dataHandler(dataMap) {
|
||||
const dataSource = [
|
||||
{
|
||||
title: '越夏越嗨皮-7月官方营销活动-技能提升方向',
|
||||
description:
|
||||
'商家通过V任务选择主播并达成合作,费用按照商品链接计算,一个商品为一个价格,建议主播在一场直播里最多接60个商品,并提供不少于两个小时的直播服务,每个商品讲解时间不少于5分钟。 ',
|
||||
tags: ['直播', '大促', '简介'],
|
||||
datetime: '2017年12月12日 18:00',
|
||||
star: Math.floor(Math.random() * 100) + 100,
|
||||
like: Math.floor(Math.random() * 100) + 200,
|
||||
comment: Math.floor(Math.random() * 100) + 100
|
||||
},
|
||||
{
|
||||
title: '越夏越嗨皮-7月官方营销活动-技能提升方向',
|
||||
description:
|
||||
'商家通过V任务选择主播并达成合作,费用按照商品链接计算,一个商品为一个价格,建议主播在一场直播里最多接60个商品,并提供不少于两个小时的直播服务,每个商品讲解时间不少于5分钟。 ',
|
||||
tags: ['直播', '大促', '简介'],
|
||||
datetime: '2017年12月12日 18:00',
|
||||
star: Math.floor(Math.random() * 100) + 100,
|
||||
like: Math.floor(Math.random() * 100) + 200,
|
||||
comment: Math.floor(Math.random() * 100) + 100
|
||||
},
|
||||
{
|
||||
title: '越夏越嗨皮-7月官方营销活动-技能提升方向',
|
||||
description:
|
||||
'商家通过V任务选择主播并达成合作,费用按照商品链接计算,一个商品为一个价格,建议主播在一场直播里最多接60个商品,并提供不少于两个小时的直播服务,每个商品讲解时间不少于5分钟。 ',
|
||||
tags: ['直播', '大促', '简介'],
|
||||
datetime: '2017年12月12日 18:00',
|
||||
star: Math.floor(Math.random() * 100) + 100,
|
||||
like: Math.floor(Math.random() * 100) + 200,
|
||||
comment: Math.floor(Math.random() * 100) + 100
|
||||
},
|
||||
{
|
||||
title: '越夏越嗨皮-7月官方营销活动-技能提升方向',
|
||||
description:
|
||||
'商家通过V任务选择主播并达成合作,费用按照商品链接计算,一个商品为一个价格,建议主播在一场直播里最多接60个商品,并提供不少于两个小时的直播服务,每个商品讲解时间不少于5分钟。 ',
|
||||
tags: ['直播', '大促', '简介'],
|
||||
datetime: '2017年12月12日 18:00',
|
||||
star: Math.floor(Math.random() * 100) + 100,
|
||||
like: Math.floor(Math.random() * 100) + 200,
|
||||
comment: Math.floor(Math.random() * 100) + 100
|
||||
},
|
||||
{
|
||||
title: '越夏越嗨皮-7月官方营销活动-技能提升方向',
|
||||
description:
|
||||
'商家通过V任务选择主播并达成合作,费用按照商品链接计算,一个商品为一个价格,建议主播在一场直播里最多接60个商品,并提供不少于两个小时的直播服务,每个商品讲解时间不少于5分钟。 ',
|
||||
tags: ['直播', '大促', '简介'],
|
||||
datetime: '2017年12月12日 18:00',
|
||||
star: Math.floor(Math.random() * 100) + 100,
|
||||
like: Math.floor(Math.random() * 100) + 200,
|
||||
comment: Math.floor(Math.random() * 100) + 100
|
||||
}
|
||||
];
|
||||
return {
|
||||
dataSource
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
367
packages/react-renderer/demo/schemas/table.js
vendored
367
packages/react-renderer/demo/schemas/table.js
vendored
@ -1,367 +0,0 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
componentName: 'Page',
|
||||
fileName: 'filterTable',
|
||||
props: {
|
||||
style: {
|
||||
paddingRight: 20,
|
||||
paddingLeft: 20
|
||||
}
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Table',
|
||||
props: {
|
||||
hasBorder: false,
|
||||
hasHeader: true,
|
||||
dataSource: [
|
||||
{
|
||||
id: 1,
|
||||
title: '2017秋冬新款背带裙复古格子连衣裙清新背心裙a字裙短裙子',
|
||||
url: 'https://item.taobao.com/item.htm?id=558559528377',
|
||||
type: '清单',
|
||||
publishTime: '17-04-28 20:29:20',
|
||||
publishStatus: '已发布',
|
||||
pushStatus: '已推送至订阅号',
|
||||
operation: {
|
||||
edit: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '2017秋冬新款 高质感特定纱线欧美宽松马海毛羊毛毛衣',
|
||||
url: 'https://item.taobao.com/item.htm?id=558559528377',
|
||||
type: '清单',
|
||||
publishTime: '17-04-28 20:29:20',
|
||||
publishStatus: '已发布',
|
||||
pushStatus: '已推送至订阅号',
|
||||
operation: {
|
||||
edit: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '日式天然玉米皮草编碗垫锅垫隔热垫茶垫加厚餐垫GD-29',
|
||||
url: 'https://item.taobao.com/item.htm?id=558559528377',
|
||||
type: '清单',
|
||||
publishTime: '17-04-28 20:29:20',
|
||||
publishStatus: '已发布',
|
||||
pushStatus: '已推送至订阅号',
|
||||
operation: {
|
||||
edit: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: '2017秋冬新款 绑带腰封设计感超顺滑质感落肩铜氨丝连衣裙',
|
||||
url: 'https://item.taobao.com/item.htm?id=558559528377',
|
||||
type: '清单',
|
||||
publishTime: '17-04-28 20:29:20',
|
||||
publishStatus: '已发布',
|
||||
pushStatus: '已推送至订阅号',
|
||||
operation: {
|
||||
edit: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: '日式糖果色陶瓷柄不锈钢餐具西餐牛扒刀叉勺子咖啡勺',
|
||||
url: 'https://item.taobao.com/item.htm?id=558559528377',
|
||||
type: '清单',
|
||||
publishTime: '17-04-28 20:29:20',
|
||||
publishStatus: '已发布',
|
||||
pushStatus: '已推送至订阅号',
|
||||
operation: {
|
||||
edit: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: '日式和风深蓝素色文艺餐巾餐垫围裙锅垫隔热手套厨房桌布',
|
||||
url: 'https://item.taobao.com/item.htm?id=558559528377',
|
||||
type: '清单',
|
||||
publishTime: '17-04-28 20:29:20',
|
||||
publishStatus: '已发布',
|
||||
pushStatus: '已推送至订阅号',
|
||||
operation: {
|
||||
edit: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: '日式雪点樱花手绘陶瓷餐具米饭碗拉面碗寿司盘子汤碗',
|
||||
url: 'https://item.taobao.com/item.htm?id=558559528377',
|
||||
type: '清单',
|
||||
publishTime: '17-04-28 20:29:20',
|
||||
publishStatus: '已发布',
|
||||
pushStatus: '已推送至订阅号',
|
||||
operation: {
|
||||
edit: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: '川岛屋 釉下彩复古日式陶瓷盘子菜盘圆盘调味碟 米饭碗日式餐具',
|
||||
url: 'https://item.taobao.com/item.htm?id=558559528377',
|
||||
type: '清单',
|
||||
publishTime: '17-04-28 20:29:20',
|
||||
publishStatus: '已发布',
|
||||
pushStatus: '已推送至订阅号',
|
||||
operation: {
|
||||
edit: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'TableColumn',
|
||||
props: {
|
||||
dataIndex: 'title',
|
||||
title: '问题描述',
|
||||
resizable: false
|
||||
}
|
||||
},
|
||||
{
|
||||
componentName: 'TableColumn',
|
||||
props: {
|
||||
title: '问题分类',
|
||||
dataIndex: 'type'
|
||||
}
|
||||
},
|
||||
{
|
||||
componentName: 'TableColumn',
|
||||
props: {
|
||||
title: '发布时间',
|
||||
dataIndex: 'publishTime'
|
||||
}
|
||||
},
|
||||
{
|
||||
componentName: 'TableColumn',
|
||||
props: {
|
||||
title: '状态',
|
||||
dataIndex: 'publishStatus',
|
||||
cell: [
|
||||
{
|
||||
componentName: 'Button',
|
||||
props: {
|
||||
type: 'normal',
|
||||
size: 'small',
|
||||
component: 'div',
|
||||
text: true,
|
||||
ghost: false,
|
||||
style: {
|
||||
width: '30px',
|
||||
fontSize: '12px',
|
||||
color: '#666',
|
||||
cursor: 'auto',
|
||||
background: '#f7f8fa'
|
||||
}
|
||||
},
|
||||
children: '已发布',
|
||||
condition: false
|
||||
},
|
||||
{
|
||||
componentName: 'Text',
|
||||
props: {
|
||||
text: '已发布',
|
||||
style: {
|
||||
paddingTop: 2,
|
||||
paddingRight: 5,
|
||||
paddingBottom: 2,
|
||||
paddingLeft: 5,
|
||||
fontSize: '12px',
|
||||
color: '#666',
|
||||
borderRadius: 3,
|
||||
cursor: 'auto',
|
||||
background: '#f7f8fa'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
componentName: 'TableColumn',
|
||||
props: {
|
||||
title: '操作',
|
||||
cell: [
|
||||
{
|
||||
componentName: 'Button',
|
||||
props: {
|
||||
type: 'normal',
|
||||
component: 'a',
|
||||
size: 'medium',
|
||||
loading: false,
|
||||
text: true,
|
||||
style: {
|
||||
paddingRight: 10,
|
||||
paddingLeft: 10,
|
||||
color: '#2077ff'
|
||||
}
|
||||
},
|
||||
children: '解决'
|
||||
},
|
||||
{
|
||||
componentName: 'Button',
|
||||
props: {
|
||||
type: 'normal',
|
||||
component: 'a',
|
||||
text: true,
|
||||
style: {
|
||||
paddingRight: 10,
|
||||
paddingLeft: 10,
|
||||
color: '#2077ff'
|
||||
}
|
||||
},
|
||||
children: '详情'
|
||||
},
|
||||
{
|
||||
componentName: 'Button',
|
||||
props: {
|
||||
type: 'normal',
|
||||
text: true,
|
||||
component: 'a',
|
||||
style: {
|
||||
paddingRight: 10,
|
||||
paddingLeft: 10,
|
||||
color: '#2077ff'
|
||||
}
|
||||
},
|
||||
children: '分类'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
loopArgs: ['', '']
|
||||
},
|
||||
{
|
||||
componentName: 'Div',
|
||||
props: {
|
||||
style: {
|
||||
textAlign: 'right'
|
||||
}
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Pagination',
|
||||
props: {
|
||||
shape: 'normal',
|
||||
type: 'normal',
|
||||
size: 'medium',
|
||||
style: {
|
||||
marginTop: 20
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
dataSource: {
|
||||
dataHandler: function dataHandler(dataMap) {
|
||||
let dataSource = [
|
||||
{
|
||||
id: 1,
|
||||
title: '2017秋冬新款背带裙复古格子连衣裙清新背心裙a字裙短裙子',
|
||||
url: 'https://item.taobao.com/item.htm?id=558559528377',
|
||||
type: '清单',
|
||||
publishTime: '17-04-28 20:29:20',
|
||||
publishStatus: '已发布',
|
||||
pushStatus: '已推送至订阅号',
|
||||
operation: {
|
||||
edit: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '2017秋冬新款 高质感特定纱线欧美宽松马海毛羊毛毛衣',
|
||||
url: 'https://item.taobao.com/item.htm?id=558559528377',
|
||||
type: '清单',
|
||||
publishTime: '17-04-28 20:29:20',
|
||||
publishStatus: '已发布',
|
||||
pushStatus: '已推送至订阅号',
|
||||
operation: {
|
||||
edit: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '日式天然玉米皮草编碗垫锅垫隔热垫茶垫加厚餐垫GD-29',
|
||||
url: 'https://item.taobao.com/item.htm?id=558559528377',
|
||||
type: '清单',
|
||||
publishTime: '17-04-28 20:29:20',
|
||||
publishStatus: '已发布',
|
||||
pushStatus: '已推送至订阅号',
|
||||
operation: {
|
||||
edit: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: '2017秋冬新款 绑带腰封设计感超顺滑质感落肩铜氨丝连衣裙',
|
||||
url: 'https://item.taobao.com/item.htm?id=558559528377',
|
||||
type: '清单',
|
||||
publishTime: '17-04-28 20:29:20',
|
||||
publishStatus: '已发布',
|
||||
pushStatus: '已推送至订阅号',
|
||||
operation: {
|
||||
edit: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: '日式糖果色陶瓷柄不锈钢餐具西餐牛扒刀叉勺子咖啡勺',
|
||||
url: 'https://item.taobao.com/item.htm?id=558559528377',
|
||||
type: '清单',
|
||||
publishTime: '17-04-28 20:29:20',
|
||||
publishStatus: '已发布',
|
||||
pushStatus: '已推送至订阅号',
|
||||
operation: {
|
||||
edit: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: '日式和风深蓝素色文艺餐巾餐垫围裙锅垫隔热手套厨房桌布',
|
||||
url: 'https://item.taobao.com/item.htm?id=558559528377',
|
||||
type: '清单',
|
||||
publishTime: '17-04-28 20:29:20',
|
||||
publishStatus: '已发布',
|
||||
pushStatus: '已推送至订阅号',
|
||||
operation: {
|
||||
edit: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: '日式雪点樱花手绘陶瓷餐具米饭碗拉面碗寿司盘子汤碗',
|
||||
url: 'https://item.taobao.com/item.htm?id=558559528377',
|
||||
type: '清单',
|
||||
publishTime: '17-04-28 20:29:20',
|
||||
publishStatus: '已发布',
|
||||
pushStatus: '已推送至订阅号',
|
||||
operation: {
|
||||
edit: true
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: '川岛屋 釉下彩复古日式陶瓷盘子菜盘圆盘调味碟 米饭碗日式餐具',
|
||||
url: 'https://item.taobao.com/item.htm?id=558559528377',
|
||||
type: '清单',
|
||||
publishTime: '17-04-28 20:29:20',
|
||||
publishStatus: '已发布',
|
||||
pushStatus: '已推送至订阅号',
|
||||
operation: {
|
||||
edit: true
|
||||
}
|
||||
}
|
||||
];
|
||||
return {
|
||||
...dataMap,
|
||||
dataSource
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1,37 +0,0 @@
|
||||
---
|
||||
title: 表格
|
||||
order: 1
|
||||
---
|
||||
|
||||
````jsx
|
||||
import React, { PureComponent } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import ReactRenderer from '@alilc/lowcode-react-renderer';
|
||||
import schema from './schemas/table';
|
||||
import components from './config/components/index';
|
||||
import utils from './config/utils';
|
||||
import constants from './config/constants';
|
||||
|
||||
class Demo extends PureComponent {
|
||||
static displayName = 'renderer-demo';
|
||||
render() {
|
||||
return (
|
||||
<div className="demo">
|
||||
<ReactRenderer
|
||||
key={schema.fileName}
|
||||
schema={schema}
|
||||
components={components}
|
||||
appHelper={{
|
||||
utils,
|
||||
constants
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render((
|
||||
<Demo />
|
||||
), mountNode);
|
||||
````
|
||||
@ -1,32 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const { join } = require('path');
|
||||
const esModules = [].join('|');
|
||||
const pkgNames = fs.readdirSync(join('..')).filter(pkgName => !pkgName.startsWith('.'));
|
||||
|
||||
const jestConfig = {
|
||||
// transform: {
|
||||
// '^.+\\.[jt]sx?$': 'babel-jest',
|
||||
// // '^.+\\.(ts|tsx)$': 'ts-jest',
|
||||
// // '^.+\\.(js|jsx)$': 'babel-jest',
|
||||
// },
|
||||
// testMatch: ['**/document/node/node.test.ts'],
|
||||
// testMatch: ['**/designer/builtin-hotkey.test.ts'],
|
||||
// testMatch: ['**/plugin/plugin-manager.test.ts'],
|
||||
// testMatch: ['(/tests?/.*(test))\\.[jt]s$'],
|
||||
transformIgnorePatterns: [
|
||||
`/node_modules/(?!${esModules})/`,
|
||||
],
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: [
|
||||
'src/**/*.ts',
|
||||
'!src/**/*.d.ts',
|
||||
'!**/node_modules/**',
|
||||
],
|
||||
};
|
||||
|
||||
// 只对本仓库内的 pkg 做 mapping
|
||||
jestConfig.moduleNameMapper = {};
|
||||
jestConfig.moduleNameMapper[`^@alilc/lowcode\\-(${pkgNames.join('|')})$`] = '<rootDir>/../$1/src';
|
||||
|
||||
module.exports = jestConfig;
|
||||
@ -1,47 +0,0 @@
|
||||
{
|
||||
"name": "@alilc/lowcode-react-renderer",
|
||||
"version": "1.3.2",
|
||||
"description": "react renderer for ali lowcode engine",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
"files": [
|
||||
"lib",
|
||||
"es",
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "build-scripts test --config build.test.json",
|
||||
"start": "build-scripts start",
|
||||
"build": "build-scripts build",
|
||||
"build:umd": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build --config build.umd.json"
|
||||
},
|
||||
"keywords": [
|
||||
"lowcode",
|
||||
"engine",
|
||||
"react"
|
||||
],
|
||||
"dependencies": {
|
||||
"@alifd/next": "^1.21.16",
|
||||
"@alilc/lowcode-renderer-core": "1.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alib/build-scripts": "^0.1.18",
|
||||
"@alifd/next": "^1.19.17",
|
||||
"build-plugin-fusion": "^0.1.0",
|
||||
"build-plugin-moment-locales": "^0.1.0",
|
||||
"react": "^16.4.1",
|
||||
"react-dom": "^16.4.1",
|
||||
"react-test-renderer": "^16"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"repository": {
|
||||
"type": "http",
|
||||
"url": "https://github.com/alibaba/lowcode-engine/tree/main/packages/react-renderer"
|
||||
},
|
||||
"homepage": "https://github.com/alibaba/lowcode-engine/#readme",
|
||||
"gitHead": "2669f179e6f899d395ce1942d0fe04f9c5ed48a6",
|
||||
"bugs": "https://github.com/alibaba/lowcode-engine/issues"
|
||||
}
|
||||
@ -1,66 +0,0 @@
|
||||
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 '@alilc/lowcode-renderer-core';
|
||||
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(): types.IRenderComponent {
|
||||
const Renderer = rendererFactory();
|
||||
return class ReactRenderer extends Renderer implements Component {
|
||||
readonly props: types.IRendererProps;
|
||||
|
||||
context: ContextType<any>;
|
||||
|
||||
setState: (
|
||||
state: types.IRendererState,
|
||||
callback?: () => void,
|
||||
) => void;
|
||||
|
||||
forceUpdate: (callback?: () => void) => void;
|
||||
|
||||
refs: {
|
||||
[key: string]: ReactInstance;
|
||||
};
|
||||
|
||||
constructor(props: types.IRendererProps, context: ContextType<any>) {
|
||||
super(props, context);
|
||||
}
|
||||
|
||||
isValidComponent(obj: any) {
|
||||
return obj?.prototype?.isReactComponent || obj?.prototype instanceof Component;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default factory();
|
||||
@ -1,865 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`React Renderer render basic case 1`] = `
|
||||
<div
|
||||
className="lce-page lce-test"
|
||||
style={
|
||||
Object {
|
||||
"padding": "0 5px 0 5px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
__id="node_dockcy8n9xed"
|
||||
className="next-box"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(31,56,88,0.1)",
|
||||
"flexDirection": "column",
|
||||
"flexWrap": "nowrap",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexWrap": "none",
|
||||
"padding": "12px 12px 12px 12px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
__id="node_dockcy8n9xee"
|
||||
className="next-box"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#ffffff",
|
||||
"flexDirection": "column",
|
||||
"flexWrap": "nowrap",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexWrap": "none",
|
||||
"padding": "12px 12px 12px 12px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<nav
|
||||
__id="node_dockcy8n9xef"
|
||||
aria-label="Breadcrumb"
|
||||
style={
|
||||
Object {
|
||||
"position": "relative",
|
||||
}
|
||||
}
|
||||
>
|
||||
<ul
|
||||
className="next-breadcrumb"
|
||||
>
|
||||
<li
|
||||
className="next-breadcrumb-item"
|
||||
dir={null}
|
||||
>
|
||||
<span
|
||||
__id="node_dockcy8n9xeg"
|
||||
className="next-breadcrumb-text"
|
||||
>
|
||||
首页
|
||||
</span>
|
||||
<span
|
||||
className="next-breadcrumb-separator"
|
||||
>
|
||||
<i
|
||||
className="next-icon next-icon-arrow-right next-medium next-breadcrumb-icon-sep"
|
||||
style={Object {}}
|
||||
/>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
className="next-breadcrumb-item"
|
||||
dir={null}
|
||||
>
|
||||
<span
|
||||
__id="node_dockcy8n9xei"
|
||||
className="next-breadcrumb-text"
|
||||
>
|
||||
品质中台
|
||||
</span>
|
||||
<span
|
||||
className="next-breadcrumb-separator"
|
||||
>
|
||||
<i
|
||||
className="next-icon next-icon-arrow-right next-medium next-breadcrumb-icon-sep"
|
||||
style={Object {}}
|
||||
/>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
className="next-breadcrumb-item"
|
||||
dir={null}
|
||||
>
|
||||
<span
|
||||
__id="node_dockcy8n9xek"
|
||||
className="next-breadcrumb-text"
|
||||
>
|
||||
商家品质页面管理
|
||||
</span>
|
||||
<span
|
||||
className="next-breadcrumb-separator"
|
||||
>
|
||||
<i
|
||||
className="next-icon next-icon-arrow-right next-medium next-breadcrumb-icon-sep"
|
||||
style={Object {}}
|
||||
/>
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
className="next-breadcrumb-item"
|
||||
dir={null}
|
||||
>
|
||||
<span
|
||||
__id="node_dockcy8n9xem"
|
||||
aria-current="page"
|
||||
className="next-breadcrumb-text activated"
|
||||
>
|
||||
质检知识条配置
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div
|
||||
__id="node_dockcy8n9xeo"
|
||||
className="next-box"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#ffffff",
|
||||
"flexDirection": "column",
|
||||
"flexWrap": "nowrap",
|
||||
"marginTop": "12px",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexWrap": "none",
|
||||
}
|
||||
}
|
||||
>
|
||||
<form
|
||||
__events={Array []}
|
||||
__id="node_dockcy8n9xep"
|
||||
className="next-form next-inline next-medium"
|
||||
onSubmit={[Function]}
|
||||
role="grid"
|
||||
style={
|
||||
Object {
|
||||
"marginLeft": "12px",
|
||||
"marginRight": "12px",
|
||||
"marginTop": "12px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
__id="node_dockcy8n9xeq"
|
||||
className="next-form-item next-left next-medium"
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": "0",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="next-form-item-label"
|
||||
>
|
||||
<label>
|
||||
类目名:
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
className="next-form-item-control"
|
||||
>
|
||||
<span
|
||||
aria-haspopup={true}
|
||||
className="next-select next-select-trigger next-select-single next-medium next-inactive next-no-search"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"width": "150px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<span
|
||||
className="next-input next-medium next-select-inner"
|
||||
>
|
||||
<span
|
||||
className="next-select-values next-input-text-field"
|
||||
>
|
||||
<span
|
||||
className="next-select-trigger-search"
|
||||
>
|
||||
<input
|
||||
__id="node_dockcy8n9xer"
|
||||
autoComplete="off"
|
||||
disabled={false}
|
||||
height="100%"
|
||||
maxLength={null}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
placeholder="请选择"
|
||||
readOnly={true}
|
||||
role="combobox"
|
||||
size="1"
|
||||
tabIndex={0}
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
aria-hidden={true}
|
||||
>
|
||||
<span>
|
||||
请选择
|
||||
</span>
|
||||
<span>
|
||||
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className="next-input-control"
|
||||
>
|
||||
<span
|
||||
aria-hidden={true}
|
||||
className="next-select-arrow"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<i
|
||||
className="next-icon next-icon-arrow-down next-medium next-select-symbol-fold"
|
||||
style={Object {}}
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-live="polite"
|
||||
className="next-sr-only"
|
||||
>
|
||||
|
||||
</span>
|
||||
</span>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
__id="node_dockcy8n9xes"
|
||||
className="next-form-item next-left next-medium"
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": "0",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="next-form-item-label"
|
||||
>
|
||||
<label>
|
||||
项目类型:
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
className="next-form-item-control"
|
||||
>
|
||||
<span
|
||||
aria-haspopup={true}
|
||||
className="next-select next-select-trigger next-select-single next-medium next-inactive next-no-search"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onMouseEnter={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"width": "200px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<span
|
||||
className="next-input next-medium next-select-inner"
|
||||
>
|
||||
<span
|
||||
className="next-select-values next-input-text-field"
|
||||
>
|
||||
<span
|
||||
className="next-select-trigger-search"
|
||||
>
|
||||
<input
|
||||
__id="node_dockcy8n9xet"
|
||||
autoComplete="off"
|
||||
disabled={false}
|
||||
height="100%"
|
||||
maxLength={null}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
placeholder="请选择"
|
||||
readOnly={true}
|
||||
role="combobox"
|
||||
size="1"
|
||||
tabIndex={0}
|
||||
value=""
|
||||
/>
|
||||
<span
|
||||
aria-hidden={true}
|
||||
>
|
||||
<span>
|
||||
请选择
|
||||
</span>
|
||||
<span>
|
||||
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className="next-input-control"
|
||||
>
|
||||
<span
|
||||
aria-hidden={true}
|
||||
className="next-select-arrow"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<i
|
||||
className="next-icon next-icon-arrow-down next-medium next-select-symbol-fold"
|
||||
style={Object {}}
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
aria-live="polite"
|
||||
className="next-sr-only"
|
||||
>
|
||||
|
||||
</span>
|
||||
</span>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
__id="node_dockcy8n9xeu"
|
||||
className="next-form-item next-left next-medium"
|
||||
style={
|
||||
Object {
|
||||
"marginBottom": "0",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="next-form-item-label"
|
||||
>
|
||||
<label>
|
||||
项目 ID:
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
className="next-form-item-control"
|
||||
>
|
||||
<span
|
||||
className="next-input next-medium"
|
||||
style={
|
||||
Object {
|
||||
"width": "200px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<input
|
||||
__id="node_dockcy8n9xev"
|
||||
autoComplete="off"
|
||||
disabled={false}
|
||||
height="100%"
|
||||
maxLength={null}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
readOnly={false}
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
__id="node_dockcy8n9xew"
|
||||
className="next-btn-group"
|
||||
>
|
||||
<button
|
||||
__id="node_dockcy8n9xex"
|
||||
className="next-btn next-medium next-btn-primary"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"margin": "0 5px 0 5px",
|
||||
}
|
||||
}
|
||||
type="submit"
|
||||
>
|
||||
<span
|
||||
className="next-btn-helper"
|
||||
>
|
||||
搜索
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
__id="node_dockcy8n9xe10"
|
||||
className="next-btn next-medium next-btn-normal"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"margin": "0 5px 0 5px",
|
||||
}
|
||||
}
|
||||
type="reset"
|
||||
>
|
||||
<span
|
||||
className="next-btn-helper"
|
||||
>
|
||||
清空
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div
|
||||
__id="node_dockcy8n9xe1f"
|
||||
className="next-box"
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "#ffffff",
|
||||
"display": "flex",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "nowrap",
|
||||
"justifyContent": "flex-end",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexWrap": "none",
|
||||
"paddingBottom": "24px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<button
|
||||
__events={
|
||||
Array [
|
||||
Object {
|
||||
"name": "onClick",
|
||||
"relatedEventName": "onClick",
|
||||
"type": "componentEvent",
|
||||
},
|
||||
]
|
||||
}
|
||||
__id="node_dockd5nrh9p4"
|
||||
className="next-btn next-medium next-btn-primary"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
style={Object {}}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="next-btn-helper"
|
||||
>
|
||||
新建配置
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
__id="node_dockd5nrh9p5"
|
||||
className="next-box"
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "column",
|
||||
"flexWrap": "nowrap",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexWrap": "none",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
__id="node_dockjielosj1"
|
||||
actionBar={
|
||||
Array [
|
||||
Object {
|
||||
"title": "新增",
|
||||
"type": "primary",
|
||||
},
|
||||
Object {
|
||||
"title": "编辑",
|
||||
},
|
||||
]
|
||||
}
|
||||
actionColumn={
|
||||
Array [
|
||||
Object {
|
||||
"callback": [Function],
|
||||
"device": Array [
|
||||
"desktop",
|
||||
],
|
||||
"title": "编辑",
|
||||
},
|
||||
Object {
|
||||
"callback": [Function],
|
||||
"mode": "EDIT",
|
||||
"title": "保存",
|
||||
},
|
||||
]
|
||||
}
|
||||
actionFixed="right"
|
||||
actionHidden={false}
|
||||
actionTitle="操作"
|
||||
actionType="link"
|
||||
actionWidth={180}
|
||||
className="next-table next-table-medium"
|
||||
data={
|
||||
Array [
|
||||
Object {
|
||||
"age": 15000,
|
||||
"email": "aaa@abc.com",
|
||||
"id": "1",
|
||||
"name": "王小",
|
||||
},
|
||||
Object {
|
||||
"age": 25000,
|
||||
"email": "bbb@abc.com",
|
||||
"id": "2",
|
||||
"name": "王中",
|
||||
},
|
||||
Object {
|
||||
"age": 35000,
|
||||
"email": "ccc@abc.com",
|
||||
"id": "3",
|
||||
"name": "王大",
|
||||
},
|
||||
]
|
||||
}
|
||||
maxWebShownActionCount={2}
|
||||
showActionBar={true}
|
||||
showMiniPager={true}
|
||||
style={Object {}}
|
||||
>
|
||||
<table
|
||||
role="table"
|
||||
style={
|
||||
Object {
|
||||
"width": undefined,
|
||||
}
|
||||
}
|
||||
>
|
||||
<colgroup>
|
||||
<col
|
||||
style={
|
||||
Object {
|
||||
"width": 200,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<col
|
||||
style={
|
||||
Object {
|
||||
"width": 200,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<col
|
||||
style={
|
||||
Object {
|
||||
"width": 200,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</colgroup>
|
||||
<thead
|
||||
className="next-table-header"
|
||||
>
|
||||
<tr>
|
||||
<th
|
||||
className="next-table-cell next-table-header-node"
|
||||
dataKey="name"
|
||||
editType="text"
|
||||
role="gridcell"
|
||||
rowSpan={1}
|
||||
style={
|
||||
Object {
|
||||
"textAlign": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="next-table-cell-wrapper"
|
||||
data-next-table-col={0}
|
||||
>
|
||||
姓名
|
||||
</div>
|
||||
</th>
|
||||
<th
|
||||
className="next-table-cell next-table-header-node"
|
||||
dataKey="age"
|
||||
role="gridcell"
|
||||
rowSpan={1}
|
||||
style={
|
||||
Object {
|
||||
"textAlign": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="next-table-cell-wrapper"
|
||||
data-next-table-col={1}
|
||||
>
|
||||
年龄
|
||||
</div>
|
||||
</th>
|
||||
<th
|
||||
className="next-table-cell next-table-header-node"
|
||||
dataKey="email"
|
||||
role="gridcell"
|
||||
rowSpan={1}
|
||||
style={
|
||||
Object {
|
||||
"textAlign": "center",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="next-table-cell-wrapper"
|
||||
data-next-table-col={2}
|
||||
>
|
||||
邮箱
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody
|
||||
className="next-table-body"
|
||||
>
|
||||
<tr>
|
||||
<td
|
||||
colSpan={3}
|
||||
>
|
||||
<div
|
||||
className="next-table-empty"
|
||||
style={
|
||||
Object {
|
||||
"left": 0,
|
||||
"overflow": "hidden",
|
||||
"position": "sticky",
|
||||
"width": -1,
|
||||
}
|
||||
}
|
||||
>
|
||||
没有数据
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div
|
||||
__id="node_dockd5nrh9pg"
|
||||
className="next-box"
|
||||
style={
|
||||
Object {
|
||||
"display": "flex",
|
||||
"flexDirection": "row",
|
||||
"flexWrap": "nowrap",
|
||||
"justifyContent": "flex-end",
|
||||
"msFlexDirection": "column",
|
||||
"msFlexWrap": "none",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
__id="node_dockd5nrh9pf"
|
||||
className="next-pagination next-medium next-normal"
|
||||
style={Object {}}
|
||||
>
|
||||
<div
|
||||
className="next-pagination-pages"
|
||||
>
|
||||
<button
|
||||
aria-label="上一页,当前第1页"
|
||||
className="next-btn next-medium next-btn-normal next-pagination-item next-prev"
|
||||
disabled={true}
|
||||
onClick={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<i
|
||||
className="next-icon next-icon-arrow-left next-xs next-btn-icon next-icon-first next-pagination-icon-prev"
|
||||
style={Object {}}
|
||||
/>
|
||||
<span
|
||||
className="next-btn-helper"
|
||||
>
|
||||
上一页
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
className="next-pagination-list"
|
||||
>
|
||||
<button
|
||||
aria-label="第1页,共10页"
|
||||
className="next-btn next-medium next-btn-normal next-pagination-item next-current"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="next-btn-helper"
|
||||
>
|
||||
1
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
aria-label="第2页,共10页"
|
||||
className="next-btn next-medium next-btn-normal next-pagination-item"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="next-btn-helper"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
aria-label="第3页,共10页"
|
||||
className="next-btn next-medium next-btn-normal next-pagination-item"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="next-btn-helper"
|
||||
>
|
||||
3
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
aria-label="第4页,共10页"
|
||||
className="next-btn next-medium next-btn-normal next-pagination-item"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="next-btn-helper"
|
||||
>
|
||||
4
|
||||
</span>
|
||||
</button>
|
||||
<i
|
||||
className="next-icon next-icon-ellipsis next-medium next-pagination-ellipsis next-pagination-icon-ellipsis"
|
||||
style={Object {}}
|
||||
/>
|
||||
<button
|
||||
aria-label="第10页,共10页"
|
||||
className="next-btn next-medium next-btn-normal next-pagination-item"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="next-btn-helper"
|
||||
>
|
||||
10
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
aria-label="下一页,当前第1页"
|
||||
className="next-btn next-medium next-btn-normal next-pagination-item next-next"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="next-btn-helper"
|
||||
>
|
||||
下一页
|
||||
</span>
|
||||
<i
|
||||
className="next-icon next-icon-arrow-right next-xs next-btn-icon next-icon-last next-pagination-icon-next"
|
||||
style={Object {}}
|
||||
/>
|
||||
</button>
|
||||
<span
|
||||
className="next-pagination-display"
|
||||
>
|
||||
<em>
|
||||
1
|
||||
</em>
|
||||
/
|
||||
10
|
||||
</span>
|
||||
<span
|
||||
className="next-pagination-jump-text"
|
||||
>
|
||||
到第
|
||||
</span>
|
||||
<span
|
||||
className="next-input next-medium next-pagination-jump-input"
|
||||
>
|
||||
<input
|
||||
aria-label="请输入跳转到第几页"
|
||||
autoComplete="off"
|
||||
disabled={false}
|
||||
height="100%"
|
||||
maxLength={null}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
readOnly={false}
|
||||
value=""
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
className="next-pagination-jump-text"
|
||||
>
|
||||
页
|
||||
</span>
|
||||
<button
|
||||
className="next-btn next-medium next-btn-normal next-pagination-jump-go"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
className="next-btn-helper"
|
||||
>
|
||||
确定
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
__id="node_dockd5nrh9pr"
|
||||
name="error"
|
||||
>
|
||||
Component Not Found
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@ -1,567 +0,0 @@
|
||||
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',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -1,31 +0,0 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { Box, Breadcrumb, Form, Select, Input, Button, Table, Pagination, Dialog } from '@alifd/next';
|
||||
import ReactRenderer from '../src';
|
||||
import schema from './fixtures/schema/basic';
|
||||
|
||||
describe('React Renderer', () => {
|
||||
it('render basic case', () => {
|
||||
const components = {
|
||||
Box,
|
||||
Breadcrumb,
|
||||
'Breadcrumb.Item': Breadcrumb.Item,
|
||||
Form,
|
||||
'Form.Item': Form.Item,
|
||||
Select,
|
||||
Input,
|
||||
Button,
|
||||
'Button.Group': Button.Group,
|
||||
Table,
|
||||
Pagination,
|
||||
Dialog,
|
||||
};
|
||||
const content = (
|
||||
<ReactRenderer
|
||||
schema={schema}
|
||||
components={components}
|
||||
/>);
|
||||
const tree = renderer.create(content).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib"
|
||||
},
|
||||
"include": [
|
||||
"./src/"
|
||||
]
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"plugins": [
|
||||
["@babel/plugin-proposal-decorators", { "legacy": true }],
|
||||
["@babel/plugin-proposal-class-properties", { "loose": true }]
|
||||
]
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
module.exports = require('../../babel.config');
|
||||
@ -1,10 +0,0 @@
|
||||
{
|
||||
"plugins": [
|
||||
[
|
||||
"@alilc/build-plugin-lce",
|
||||
{
|
||||
"babelPlugins": ["@babel/plugin-transform-typescript"]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"plugins": [
|
||||
"@alilc/build-plugin-lce",
|
||||
"@alilc/lowcode-test-mate/plugin/index.ts"
|
||||
]
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const { join } = require('path');
|
||||
const esModules = [].join('|');
|
||||
const pkgNames = fs.readdirSync(join('..')).filter(pkgName => !pkgName.startsWith('.'));
|
||||
|
||||
const jestConfig = {
|
||||
// transform: {
|
||||
// // '^.+\\.[jt]sx?$': 'babel-jest',
|
||||
// '^.+\\.(ts|tsx)$': 'ts-jest',
|
||||
// // '^.+\\.(js|jsx)$': 'babel-jest',
|
||||
// },
|
||||
// testMatch: ['(/tests?/.*(test))\\.[jt]s$'],
|
||||
// testMatch: ['**/*/base.test.tsx'],
|
||||
// testMatch: ['**/utils/common.test.ts'],
|
||||
// testMatch: ['**/*/leaf.test.tsx'],
|
||||
// testMatch: ['**/*/is-use-loop.test.ts'],
|
||||
transformIgnorePatterns: [
|
||||
`/node_modules/(?!${esModules})/`,
|
||||
],
|
||||
setupFiles: [
|
||||
'./tests/fixtures/unhandled-rejection.ts',
|
||||
'./tests/setup.ts',
|
||||
],
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: [
|
||||
'src/**/*.ts',
|
||||
'src/**/*.tsx',
|
||||
'!src/utils/logger.ts',
|
||||
'!src/types/index.ts',
|
||||
],
|
||||
};
|
||||
|
||||
// 只对本仓库内的 pkg 做 mapping
|
||||
jestConfig.moduleNameMapper = {};
|
||||
jestConfig.moduleNameMapper[`^@alilc/lowcode\\-(${pkgNames.join('|')})$`] = '<rootDir>/../$1/src';
|
||||
|
||||
module.exports = jestConfig;
|
||||
@ -1,61 +0,0 @@
|
||||
{
|
||||
"name": "@alilc/lowcode-renderer-core",
|
||||
"version": "1.3.2",
|
||||
"description": "renderer core",
|
||||
"license": "MIT",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
"files": [
|
||||
"lib",
|
||||
"es"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "build-scripts build",
|
||||
"test": "build-scripts test --config build.test.json",
|
||||
"test:cov": "build-scripts test --config build.test.json --jest-coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alilc/lowcode-datasource-engine": "^1.0.0",
|
||||
"@alilc/lowcode-types": "1.3.2",
|
||||
"@alilc/lowcode-utils": "1.3.2",
|
||||
"classnames": "^2.2.6",
|
||||
"debug": "^4.1.1",
|
||||
"fetch-jsonp": "^1.1.3",
|
||||
"intl-messageformat": "^9.3.1",
|
||||
"jsonuri": "^2.1.2",
|
||||
"lodash": "^4.17.11",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^16.10.1",
|
||||
"socket.io-client": "^2.2.0",
|
||||
"whatwg-fetch": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alib/build-scripts": "^0.1.18",
|
||||
"@alifd/next": "^1.26.0",
|
||||
"@alilc/lowcode-designer": "1.3.2",
|
||||
"@babel/plugin-transform-typescript": "^7.16.8",
|
||||
"@testing-library/react": "^11.2.2",
|
||||
"@types/classnames": "^2.2.11",
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/jest": "^26.0.16",
|
||||
"@types/lodash": "^4.14.167",
|
||||
"@types/node": "^13.7.1",
|
||||
"@types/prop-types": "^15.7.3",
|
||||
"@types/react-is": "^17.0.3",
|
||||
"@types/react-test-renderer": "^17.0.1",
|
||||
"jest": "^26.6.3",
|
||||
"react-test-renderer": "^17.0.2",
|
||||
"ts-jest": "^26.5.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org/"
|
||||
},
|
||||
"repository": {
|
||||
"type": "http",
|
||||
"url": "https://github.com/alibaba/lowcode-engine/tree/main/packages/renderer-core"
|
||||
},
|
||||
"gitHead": "2669f179e6f899d395ce1942d0fe04f9c5ed48a6",
|
||||
"bugs": "https://github.com/alibaba/lowcode-engine/issues",
|
||||
"homepage": "https://github.com/alibaba/lowcode-engine/#readme"
|
||||
}
|
||||
@ -1,104 +0,0 @@
|
||||
import { IRuntime, IRendererModules, IGeneralConstructor } from '../types';
|
||||
|
||||
export enum Env {
|
||||
React = 'react',
|
||||
}
|
||||
|
||||
class Adapter {
|
||||
runtime: IRuntime;
|
||||
|
||||
builtinModules = ['Component', 'PureComponent', 'createElement', 'createContext', 'forwardRef', 'findDOMNode'];
|
||||
|
||||
env: Env;
|
||||
|
||||
renderers: IRendererModules;
|
||||
|
||||
configProvider: any;
|
||||
|
||||
constructor() {
|
||||
this.initRuntime();
|
||||
}
|
||||
|
||||
initRuntime() {
|
||||
const Component: IGeneralConstructor = class <T = any, S = any> {
|
||||
state: Readonly<S>;
|
||||
props: Readonly<T> & Readonly<{ children?: any | undefined }>;
|
||||
refs: Record<string, unknown>;
|
||||
context: Record<string, unknown>;
|
||||
setState() {}
|
||||
forceUpdate() {}
|
||||
render() {}
|
||||
};
|
||||
const PureComponent = class <T = any, S = any> {
|
||||
state: Readonly<S>;
|
||||
props: Readonly<T> & Readonly<{ children?: any | undefined }>;
|
||||
refs: Record<string, unknown>;
|
||||
context: Record<string, unknown>;
|
||||
setState() {}
|
||||
forceUpdate() {}
|
||||
render() {}
|
||||
};
|
||||
const createElement = () => {};
|
||||
const createContext = () => {};
|
||||
const forwardRef = () => {};
|
||||
const findDOMNode = () => {};
|
||||
this.runtime = {
|
||||
Component,
|
||||
PureComponent,
|
||||
createElement,
|
||||
createContext,
|
||||
forwardRef,
|
||||
findDOMNode,
|
||||
};
|
||||
}
|
||||
|
||||
setRuntime(runtime: IRuntime) {
|
||||
if (this.isValidRuntime(runtime)) {
|
||||
this.runtime = runtime;
|
||||
}
|
||||
}
|
||||
|
||||
isValidRuntime(runtime: IRuntime) {
|
||||
if (typeof runtime !== 'object' || Array.isArray(runtime)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.builtinModules.every((m) => {
|
||||
const flag = !!runtime[m];
|
||||
if (!flag) {
|
||||
throw new Error(`runtime is invalid, module '${m}' does not exist`);
|
||||
}
|
||||
return flag;
|
||||
});
|
||||
}
|
||||
|
||||
getRuntime() {
|
||||
return this.runtime;
|
||||
}
|
||||
|
||||
setEnv(env: Env) {
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
isReact() {
|
||||
return this.env === Env.React;
|
||||
}
|
||||
|
||||
setRenderers(renderers: IRendererModules) {
|
||||
this.renderers = renderers;
|
||||
}
|
||||
|
||||
getRenderers() {
|
||||
return this.renderers || {};
|
||||
}
|
||||
|
||||
setConfigProvider(Comp: any) {
|
||||
this.configProvider = Comp;
|
||||
}
|
||||
|
||||
getConfigProvider() {
|
||||
return this.configProvider;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Adapter();
|
||||
@ -1,15 +0,0 @@
|
||||
import adapter from '../adapter';
|
||||
import { IGeneralConstructor } from '../types';
|
||||
|
||||
export default function divFactory(): IGeneralConstructor {
|
||||
const { PureComponent, createElement } = adapter.getRuntime();
|
||||
return class Div extends PureComponent {
|
||||
static displayName = 'Div';
|
||||
|
||||
static version = '0.0.0';
|
||||
|
||||
render() {
|
||||
return createElement('div', this.props);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
.visual-dom .panel-container {
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #e9e9e9;
|
||||
}
|
||||
|
||||
.visual-dom .panel-container .title {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
background-color: #ebecf0;
|
||||
line-height: 28px;
|
||||
padding: 0 12px;
|
||||
border-bottom: 1px solid #e9e9e9;
|
||||
}
|
||||
|
||||
.visual-dom .panel-container .content {
|
||||
min-height: 20px;
|
||||
padding: 5px;
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import adapter from '../../adapter';
|
||||
import { IGeneralConstructor } from '../../types';
|
||||
import './index.css';
|
||||
|
||||
export default function visualDomFactory(): IGeneralConstructor {
|
||||
const { PureComponent, createElement } = adapter.getRuntime();
|
||||
return class VisualDom extends PureComponent {
|
||||
static displayName = 'VisualDom';
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)]),
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
children: null,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { children, cell, title, label, text, __componentName } = this.props;
|
||||
let mainContent = children;
|
||||
if (cell && typeof cell === 'function') {
|
||||
mainContent = cell();
|
||||
}
|
||||
return createElement('div', { className: 'visual-dom' },
|
||||
createElement('div', { className: 'panel-container' },
|
||||
[
|
||||
createElement('span', { className: 'title' }, title || label || text || __componentName),
|
||||
createElement('div', { className: 'content' }, mainContent),
|
||||
]));
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
import adapter from '../adapter';
|
||||
|
||||
|
||||
export default function contextFactory() {
|
||||
const { createContext } = adapter.getRuntime();
|
||||
|
||||
let context = (window as any).__appContext;
|
||||
if (!context) {
|
||||
context = createContext({});
|
||||
(window as any).__appContext = context;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
@ -1,88 +0,0 @@
|
||||
import { cloneEnumerableProperty } from '@alilc/lowcode-utils';
|
||||
import adapter from '../adapter';
|
||||
import { IBaseRendererInstance, IRendererProps } from '../types';
|
||||
|
||||
interface Options {
|
||||
baseRenderer: IBaseRendererInstance;
|
||||
schema: any;
|
||||
}
|
||||
|
||||
function patchDidCatch(Comp: any, { baseRenderer }: Options) {
|
||||
if (Comp.patchedCatch) {
|
||||
return;
|
||||
}
|
||||
Comp.patchedCatch = true;
|
||||
const { PureComponent } = adapter.getRuntime();
|
||||
// Rax 的 getDerivedStateFromError 有 BUG,这里先用 componentDidCatch 来替代
|
||||
// @see https://github.com/alibaba/rax/issues/2211
|
||||
const originalDidCatch = Comp.prototype.componentDidCatch;
|
||||
Comp.prototype.componentDidCatch = function didCatch(this: any, error: Error, errorInfo: any) {
|
||||
this.setState({ engineRenderError: true, error });
|
||||
if (originalDidCatch && typeof originalDidCatch === 'function') {
|
||||
originalDidCatch.call(this, error, errorInfo);
|
||||
}
|
||||
};
|
||||
|
||||
const { engine } = baseRenderer.context;
|
||||
const originRender = Comp.prototype.render;
|
||||
Comp.prototype.render = function () {
|
||||
if (this.state && this.state.engineRenderError) {
|
||||
this.state.engineRenderError = false;
|
||||
return engine.createElement(engine.getFaultComponent(), {
|
||||
...this.props,
|
||||
error: this.state.error,
|
||||
componentName: this.props._componentName,
|
||||
});
|
||||
}
|
||||
return originRender.call(this);
|
||||
};
|
||||
if (!(Comp.prototype instanceof PureComponent)) {
|
||||
const originShouldComponentUpdate = Comp.prototype.shouldComponentUpdate;
|
||||
Comp.prototype.shouldComponentUpdate = function (nextProps: IRendererProps, nextState: any) {
|
||||
if (nextState && nextState.engineRenderError) {
|
||||
return true;
|
||||
}
|
||||
return originShouldComponentUpdate
|
||||
? originShouldComponentUpdate.call(this, nextProps, nextState)
|
||||
: true;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const cache = new Map<string, { Comp: any; WrapperComponent: any }>();
|
||||
|
||||
export function compWrapper(Comp: any, options: Options) {
|
||||
const { createElement, Component, forwardRef } = adapter.getRuntime();
|
||||
if (
|
||||
Comp?.prototype?.isReactComponent || // react
|
||||
Comp?.prototype?.setState || // rax
|
||||
Comp?.prototype instanceof Component
|
||||
) {
|
||||
patchDidCatch(Comp, options);
|
||||
return Comp;
|
||||
}
|
||||
|
||||
if (cache.has(options.schema.id) && cache.get(options.schema.id)?.Comp === Comp) {
|
||||
return cache.get(options.schema.id)?.WrapperComponent;
|
||||
}
|
||||
|
||||
class Wrapper extends Component {
|
||||
render() {
|
||||
return createElement(Comp, { ...this.props, ref: this.props.forwardRef });
|
||||
}
|
||||
}
|
||||
(Wrapper as any).displayName = Comp.displayName;
|
||||
|
||||
patchDidCatch(Wrapper, options);
|
||||
|
||||
const WrapperComponent = cloneEnumerableProperty(
|
||||
forwardRef((props: any, ref: any) => {
|
||||
return createElement(Wrapper, { ...props, forwardRef: ref });
|
||||
}),
|
||||
Comp,
|
||||
);
|
||||
|
||||
cache.set(options.schema.id, { WrapperComponent, Comp });
|
||||
|
||||
return WrapperComponent;
|
||||
}
|
||||
@ -1,600 +0,0 @@
|
||||
import { INode, IPublicTypePropChangeOptions } from '@alilc/lowcode-designer';
|
||||
import { GlobalEvent, IPublicEnumTransformStage, IPublicTypeNodeSchema, IPublicTypeEngineOptions } from '@alilc/lowcode-types';
|
||||
import { isReactComponent, cloneEnumerableProperty } from '@alilc/lowcode-utils';
|
||||
import { debounce } from '../utils/common';
|
||||
import adapter from '../adapter';
|
||||
import * as types from '../types/index';
|
||||
import logger from '../utils/logger';
|
||||
|
||||
export interface IComponentHocInfo {
|
||||
schema: any;
|
||||
baseRenderer: types.IBaseRendererInstance;
|
||||
componentInfo: any;
|
||||
scope: any;
|
||||
}
|
||||
|
||||
export interface IComponentHocProps {
|
||||
__tag: any;
|
||||
componentId: any;
|
||||
_leaf: any;
|
||||
forwardedRef?: any;
|
||||
}
|
||||
|
||||
export interface IComponentHocState {
|
||||
childrenInState: boolean;
|
||||
nodeChildren: any;
|
||||
nodeCacheProps: any;
|
||||
|
||||
/** 控制是否显示隐藏 */
|
||||
visible: boolean;
|
||||
|
||||
/** 控制是否渲染 */
|
||||
condition: boolean;
|
||||
nodeProps: any;
|
||||
}
|
||||
|
||||
type DesignMode = Pick<IPublicTypeEngineOptions, 'designMode'>['designMode'];
|
||||
|
||||
export interface IComponentHoc {
|
||||
designMode: DesignMode | DesignMode[];
|
||||
hoc: IComponentConstruct;
|
||||
}
|
||||
|
||||
export type IComponentConstruct = (Comp: types.IBaseRenderComponent, info: IComponentHocInfo) => types.IGeneralConstructor;
|
||||
|
||||
interface IProps {
|
||||
_leaf: INode | undefined;
|
||||
|
||||
visible: boolean;
|
||||
|
||||
componentId: number;
|
||||
|
||||
children?: INode[];
|
||||
|
||||
__tag: number;
|
||||
|
||||
forwardedRef?: any;
|
||||
}
|
||||
|
||||
enum RerenderType {
|
||||
All = 'All',
|
||||
ChildChanged = 'ChildChanged',
|
||||
PropsChanged = 'PropsChanged',
|
||||
VisibleChanged = 'VisibleChanged',
|
||||
MinimalRenderUnit = 'MinimalRenderUnit',
|
||||
}
|
||||
|
||||
// 缓存 Leaf 层组件,防止重新渲染问题
|
||||
class LeafCache {
|
||||
|
||||
/** 组件缓存 */
|
||||
component = new Map();
|
||||
|
||||
/**
|
||||
* 状态缓存,场景:属性变化后,改组件被销毁,state 为空,没有展示修改后的属性
|
||||
*/
|
||||
state = new Map();
|
||||
|
||||
/**
|
||||
* 订阅事件缓存,导致 rerender 的订阅事件
|
||||
*/
|
||||
event = new Map();
|
||||
|
||||
ref = new Map();
|
||||
|
||||
constructor(public documentId: string, public device: string) {
|
||||
}
|
||||
}
|
||||
|
||||
let cache: LeafCache;
|
||||
|
||||
/** 部分没有渲染的 node 节点进行兜底处理 or 渲染方式没有渲染 LeafWrapper */
|
||||
function initRerenderEvent({
|
||||
schema,
|
||||
__debug,
|
||||
container,
|
||||
getNode,
|
||||
}: any) {
|
||||
const leaf = getNode?.(schema.id);
|
||||
if (!leaf
|
||||
|| cache.event.get(schema.id)?.clear
|
||||
|| leaf === cache.event.get(schema.id)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
cache.event.get(schema.id)?.dispose.forEach((disposeFn: any) => disposeFn && disposeFn());
|
||||
const debounceRerender = debounce(() => {
|
||||
container.rerender();
|
||||
}, 20);
|
||||
cache.event.set(schema.id, {
|
||||
clear: false,
|
||||
leaf,
|
||||
dispose: [
|
||||
leaf?.onPropChange?.(() => {
|
||||
if (!container.autoRepaintNode) {
|
||||
return;
|
||||
}
|
||||
__debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onPropsChange make rerender`);
|
||||
debounceRerender();
|
||||
}),
|
||||
leaf?.onChildrenChange?.(() => {
|
||||
if (!container.autoRepaintNode) {
|
||||
return;
|
||||
}
|
||||
__debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onChildrenChange make rerender`);
|
||||
debounceRerender();
|
||||
}) as Function,
|
||||
leaf?.onVisibleChange?.(() => {
|
||||
if (!container.autoRepaintNode) {
|
||||
return;
|
||||
}
|
||||
__debug(`${schema.componentName}[${schema.id}] leaf not render in SimulatorRendererView, leaf onVisibleChange make rerender`);
|
||||
debounceRerender();
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
/** 渲染的 node 节点全局注册事件清除 */
|
||||
function clearRerenderEvent(id: string): void {
|
||||
if (cache.event.get(id)?.clear) {
|
||||
return;
|
||||
}
|
||||
cache.event.get(id)?.dispose?.forEach((disposeFn: any) => disposeFn && disposeFn());
|
||||
cache.event.set(id, {
|
||||
clear: true,
|
||||
dispose: [],
|
||||
});
|
||||
}
|
||||
|
||||
// 给每个组件包裹一个 HOC Leaf,支持组件内部属性变化,自响应渲染
|
||||
export function leafWrapper(Comp: types.IBaseRenderComponent, {
|
||||
schema,
|
||||
baseRenderer,
|
||||
componentInfo,
|
||||
scope,
|
||||
}: IComponentHocInfo) {
|
||||
const {
|
||||
__debug,
|
||||
__getComponentProps: getProps,
|
||||
__getSchemaChildrenVirtualDom: getChildren,
|
||||
__parseData,
|
||||
} = baseRenderer;
|
||||
const { engine } = baseRenderer.context;
|
||||
const host = baseRenderer.props?.__host;
|
||||
const curDocumentId = baseRenderer.props?.documentId ?? '';
|
||||
const curDevice = baseRenderer.props?.device ?? '';
|
||||
const getNode = baseRenderer.props?.getNode;
|
||||
const container = baseRenderer.props?.__container;
|
||||
const setSchemaChangedSymbol = baseRenderer.props?.setSchemaChangedSymbol;
|
||||
const editor = host?.designer?.editor;
|
||||
const runtime = adapter.getRuntime();
|
||||
const { forwardRef, createElement } = runtime;
|
||||
const Component = runtime.Component as types.IGeneralConstructor<
|
||||
IComponentHocProps, IComponentHocState
|
||||
>;
|
||||
|
||||
const componentCacheId = schema.id;
|
||||
|
||||
if (!cache || (curDocumentId && curDocumentId !== cache.documentId) || (curDevice && curDevice !== cache.device)) {
|
||||
cache?.event.forEach(event => {
|
||||
event.dispose?.forEach((disposeFn: any) => disposeFn && disposeFn());
|
||||
});
|
||||
cache = new LeafCache(curDocumentId, curDevice);
|
||||
}
|
||||
|
||||
if (!isReactComponent(Comp)) {
|
||||
logger.error(`${schema.componentName} component may be has errors: `, Comp);
|
||||
}
|
||||
|
||||
initRerenderEvent({
|
||||
schema,
|
||||
__debug,
|
||||
container,
|
||||
getNode,
|
||||
});
|
||||
|
||||
if (curDocumentId && cache.component.has(componentCacheId) && (cache.component.get(componentCacheId).Comp === Comp)) {
|
||||
return cache.component.get(componentCacheId).LeafWrapper;
|
||||
}
|
||||
|
||||
class LeafHoc extends Component {
|
||||
recordInfo: {
|
||||
startTime?: number | null;
|
||||
type?: string;
|
||||
node?: INode;
|
||||
} = {};
|
||||
|
||||
private curEventLeaf: INode | undefined;
|
||||
|
||||
static displayName = schema.componentName;
|
||||
|
||||
disposeFunctions: Array<((() => void) | Function)> = [];
|
||||
|
||||
__component_tag = 'leafWrapper';
|
||||
|
||||
renderUnitInfo: {
|
||||
minimalUnitId?: string;
|
||||
minimalUnitName?: string;
|
||||
singleRender?: boolean;
|
||||
};
|
||||
|
||||
// 最小渲染单元做防抖处理
|
||||
makeUnitRenderDebounced = debounce(() => {
|
||||
this.beforeRender(RerenderType.MinimalRenderUnit);
|
||||
const schema = this.leaf?.export?.(IPublicEnumTransformStage.Render);
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
const nextProps = getProps(schema, scope, Comp, componentInfo);
|
||||
const children = getChildren(schema, scope, Comp);
|
||||
const nextState = {
|
||||
nodeProps: nextProps,
|
||||
nodeChildren: children,
|
||||
childrenInState: true,
|
||||
};
|
||||
if ('children' in nextProps) {
|
||||
nextState.nodeChildren = nextProps.children;
|
||||
}
|
||||
|
||||
__debug(`${this.leaf?.componentName}(${this.props.componentId}) MinimalRenderUnit Render!`);
|
||||
this.setState(nextState);
|
||||
}, 20);
|
||||
|
||||
constructor(props: IProps, context: any) {
|
||||
super(props, context);
|
||||
// 监听以下事件,当变化时更新自己
|
||||
__debug(`${schema.componentName}[${this.props.componentId}] leaf render in SimulatorRendererView`);
|
||||
clearRerenderEvent(componentCacheId);
|
||||
this.curEventLeaf = this.leaf;
|
||||
|
||||
cache.ref.set(componentCacheId, {
|
||||
makeUnitRender: this.makeUnitRender,
|
||||
});
|
||||
|
||||
let cacheState = cache.state.get(componentCacheId);
|
||||
if (!cacheState || cacheState.__tag !== props.__tag) {
|
||||
cacheState = this.getDefaultState(props);
|
||||
}
|
||||
|
||||
this.state = cacheState;
|
||||
}
|
||||
|
||||
recordTime = () => {
|
||||
if (!this.recordInfo.startTime) {
|
||||
return;
|
||||
}
|
||||
const endTime = Date.now();
|
||||
const nodeCount = host?.designer?.currentDocument?.getNodeCount?.();
|
||||
const componentName = this.recordInfo.node?.componentName || this.leaf?.componentName || 'UnknownComponent';
|
||||
editor?.eventBus.emit(GlobalEvent.Node.Rerender, {
|
||||
componentName,
|
||||
time: endTime - this.recordInfo.startTime,
|
||||
type: this.recordInfo.type,
|
||||
nodeCount,
|
||||
});
|
||||
this.recordInfo.startTime = null;
|
||||
};
|
||||
|
||||
makeUnitRender = () => {
|
||||
this.makeUnitRenderDebounced();
|
||||
};
|
||||
|
||||
get autoRepaintNode() {
|
||||
return container?.autoRepaintNode;
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.recordTime();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const _leaf = this.leaf;
|
||||
this.initOnPropsChangeEvent(_leaf);
|
||||
this.initOnChildrenChangeEvent(_leaf);
|
||||
this.initOnVisibleChangeEvent(_leaf);
|
||||
this.recordTime();
|
||||
}
|
||||
|
||||
getDefaultState(nextProps: any) {
|
||||
const {
|
||||
hidden = false,
|
||||
condition = true,
|
||||
} = nextProps.__inner__ || this.leaf?.export?.(IPublicEnumTransformStage.Render) || {};
|
||||
return {
|
||||
nodeChildren: null,
|
||||
childrenInState: false,
|
||||
visible: !hidden,
|
||||
condition: __parseData?.(condition, scope),
|
||||
nodeCacheProps: {},
|
||||
nodeProps: {},
|
||||
};
|
||||
}
|
||||
|
||||
setState(state: any) {
|
||||
cache.state.set(componentCacheId, {
|
||||
...this.state,
|
||||
...state,
|
||||
__tag: this.props.__tag,
|
||||
});
|
||||
super.setState(state);
|
||||
}
|
||||
|
||||
/** 由于内部属性变化,在触发渲染前,会执行该函数 */
|
||||
beforeRender(type: string, node?: INode): void {
|
||||
this.recordInfo.startTime = Date.now();
|
||||
this.recordInfo.type = type;
|
||||
this.recordInfo.node = node;
|
||||
setSchemaChangedSymbol?.(true);
|
||||
}
|
||||
|
||||
judgeMiniUnitRender() {
|
||||
if (!this.renderUnitInfo) {
|
||||
this.getRenderUnitInfo();
|
||||
}
|
||||
|
||||
const renderUnitInfo = this.renderUnitInfo || {
|
||||
singleRender: true,
|
||||
};
|
||||
|
||||
if (renderUnitInfo.singleRender) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ref = cache.ref.get(renderUnitInfo.minimalUnitId);
|
||||
|
||||
if (!ref) {
|
||||
__debug('Cant find minimalRenderUnit ref! This make rerender!');
|
||||
container?.rerender();
|
||||
return;
|
||||
}
|
||||
__debug(`${this.leaf?.componentName}(${this.props.componentId}) need render, make its minimalRenderUnit ${renderUnitInfo.minimalUnitName}(${renderUnitInfo.minimalUnitId})`);
|
||||
ref.makeUnitRender();
|
||||
}
|
||||
|
||||
getRenderUnitInfo(leaf = this.leaf) {
|
||||
// leaf 在低代码组件中存在 mock 的情况,退出最小渲染单元判断
|
||||
if (!leaf || typeof leaf.isRoot !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (leaf.isRootNode) {
|
||||
this.renderUnitInfo = {
|
||||
singleRender: true,
|
||||
...(this.renderUnitInfo || {}),
|
||||
};
|
||||
}
|
||||
if (leaf.componentMeta.isMinimalRenderUnit) {
|
||||
this.renderUnitInfo = {
|
||||
minimalUnitId: leaf.id,
|
||||
minimalUnitName: leaf.componentName,
|
||||
singleRender: false,
|
||||
};
|
||||
}
|
||||
if (leaf.hasLoop()) {
|
||||
// 含有循环配置的元素,父元素是最小渲染单元
|
||||
this.renderUnitInfo = {
|
||||
minimalUnitId: leaf?.parent?.id,
|
||||
minimalUnitName: leaf?.parent?.componentName,
|
||||
singleRender: false,
|
||||
};
|
||||
}
|
||||
if (leaf.parent) {
|
||||
this.getRenderUnitInfo(leaf.parent);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: any) {
|
||||
let { componentId } = nextProps;
|
||||
if (nextProps.__tag === this.props.__tag) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const _leaf = getNode?.(componentId);
|
||||
if (_leaf && this.curEventLeaf && _leaf !== this.curEventLeaf) {
|
||||
this.disposeFunctions.forEach((fn) => fn());
|
||||
this.disposeFunctions = [];
|
||||
this.initOnChildrenChangeEvent(_leaf);
|
||||
this.initOnPropsChangeEvent(_leaf);
|
||||
this.initOnVisibleChangeEvent(_leaf);
|
||||
this.curEventLeaf = _leaf;
|
||||
}
|
||||
|
||||
const {
|
||||
visible,
|
||||
...resetState
|
||||
} = this.getDefaultState(nextProps);
|
||||
this.setState(resetState);
|
||||
}
|
||||
|
||||
/** 监听参数变化 */
|
||||
initOnPropsChangeEvent(leaf = this.leaf): void {
|
||||
const handlePropsChange = debounce((propChangeInfo: IPublicTypePropChangeOptions) => {
|
||||
const {
|
||||
key,
|
||||
newValue = null,
|
||||
} = propChangeInfo;
|
||||
const node = leaf;
|
||||
|
||||
if (key === '___condition___') {
|
||||
const { condition = true } = this.leaf?.export(IPublicEnumTransformStage.Render) || {};
|
||||
const conditionValue = __parseData?.(condition, scope);
|
||||
__debug(`key is ___condition___, change condition value to [${condition}]`);
|
||||
// 条件表达式改变
|
||||
this.setState({
|
||||
condition: conditionValue,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果循坏条件变化,从根节点重新渲染
|
||||
// 目前多层循坏无法判断需要从哪一层开始渲染,故先粗暴解决
|
||||
if (key === '___loop___') {
|
||||
__debug('key is ___loop___, render a page!');
|
||||
container?.rerender();
|
||||
// 由于 scope 变化,需要清空缓存,使用新的 scope
|
||||
cache.component.delete(componentCacheId);
|
||||
return;
|
||||
}
|
||||
this.beforeRender(RerenderType.PropsChanged);
|
||||
const { state } = this;
|
||||
const { nodeCacheProps } = state;
|
||||
const nodeProps = getProps(node?.export?.(IPublicEnumTransformStage.Render) as IPublicTypeNodeSchema, scope, Comp, componentInfo);
|
||||
if (key && !(key in nodeProps) && (key in this.props)) {
|
||||
// 当 key 在 this.props 中时,且不存在在计算值中,需要用 newValue 覆盖掉 this.props 的取值
|
||||
nodeCacheProps[key] = newValue;
|
||||
}
|
||||
__debug(`${leaf?.componentName}[${this.props.componentId}] component trigger onPropsChange!`, nodeProps, nodeCacheProps, key, newValue);
|
||||
this.setState('children' in nodeProps ? {
|
||||
nodeChildren: nodeProps.children,
|
||||
nodeProps,
|
||||
childrenInState: true,
|
||||
nodeCacheProps,
|
||||
} : {
|
||||
nodeProps,
|
||||
nodeCacheProps,
|
||||
});
|
||||
|
||||
this.judgeMiniUnitRender();
|
||||
});
|
||||
const dispose = leaf?.onPropChange?.((propChangeInfo: IPublicTypePropChangeOptions) => {
|
||||
if (!this.autoRepaintNode) {
|
||||
return;
|
||||
}
|
||||
handlePropsChange(propChangeInfo);
|
||||
});
|
||||
|
||||
dispose && this.disposeFunctions.push(dispose);
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听显隐变化
|
||||
*/
|
||||
initOnVisibleChangeEvent(leaf = this.leaf) {
|
||||
const dispose = leaf?.onVisibleChange?.((flag: boolean) => {
|
||||
if (!this.autoRepaintNode) {
|
||||
return;
|
||||
}
|
||||
if (this.state.visible === flag) {
|
||||
return;
|
||||
}
|
||||
|
||||
__debug(`${leaf?.componentName}[${this.props.componentId}] component trigger onVisibleChange(${flag}) event`);
|
||||
this.beforeRender(RerenderType.VisibleChanged);
|
||||
this.setState({
|
||||
visible: flag,
|
||||
});
|
||||
this.judgeMiniUnitRender();
|
||||
});
|
||||
|
||||
dispose && this.disposeFunctions.push(dispose);
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听子元素变化(拖拽,删除...)
|
||||
*/
|
||||
initOnChildrenChangeEvent(leaf = this.leaf) {
|
||||
const dispose = leaf?.onChildrenChange?.((param): void => {
|
||||
if (!this.autoRepaintNode) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
type,
|
||||
node,
|
||||
} = param || {};
|
||||
this.beforeRender(`${RerenderType.ChildChanged}-${type}`, node);
|
||||
// TODO: 缓存同级其他元素的 children。
|
||||
// 缓存二级 children Next 查询筛选组件有问题
|
||||
// 缓存一级 children Next Tab 组件有问题
|
||||
const nextChild = getChildren(leaf?.export?.(IPublicEnumTransformStage.Render) as types.ISchema, scope, Comp);
|
||||
__debug(`${schema.componentName}[${this.props.componentId}] component trigger onChildrenChange event`, nextChild);
|
||||
this.setState({
|
||||
nodeChildren: nextChild,
|
||||
childrenInState: true,
|
||||
});
|
||||
this.judgeMiniUnitRender();
|
||||
});
|
||||
dispose && this.disposeFunctions.push(dispose);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.disposeFunctions.forEach(fn => fn());
|
||||
}
|
||||
|
||||
get hasChildren(): boolean {
|
||||
if (!this.state.childrenInState) {
|
||||
return 'children' in this.props;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
get children(): any {
|
||||
if (this.state.childrenInState) {
|
||||
return this.state.nodeChildren;
|
||||
}
|
||||
if (this.props.children && !Array.isArray(this.props.children)) {
|
||||
return [this.props.children];
|
||||
}
|
||||
if (this.props.children && this.props.children.length) {
|
||||
return this.props.children;
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
|
||||
get leaf(): INode | undefined {
|
||||
if (this.props._leaf?.isMock) {
|
||||
// 低代码组件作为一个整体更新,其内部的组件不需要监听相关事件
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return getNode?.(componentCacheId);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.state.visible || !this.state.condition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
forwardedRef,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
const compProps = {
|
||||
...rest,
|
||||
...(this.state.nodeCacheProps || {}),
|
||||
...(this.state.nodeProps || {}),
|
||||
children: [],
|
||||
__id: this.props.componentId,
|
||||
ref: forwardedRef,
|
||||
};
|
||||
|
||||
delete compProps.__inner__;
|
||||
|
||||
if (this.hasChildren) {
|
||||
return engine.createElement(Comp, compProps, this.children);
|
||||
}
|
||||
|
||||
return engine.createElement(Comp, compProps);
|
||||
}
|
||||
}
|
||||
|
||||
let LeafWrapper = forwardRef((props: any, ref: any) => {
|
||||
return createElement(LeafHoc, {
|
||||
...props,
|
||||
forwardedRef: ref,
|
||||
});
|
||||
});
|
||||
|
||||
LeafWrapper = cloneEnumerableProperty(LeafWrapper, Comp);
|
||||
|
||||
LeafWrapper.displayName = (Comp as any).displayName;
|
||||
|
||||
cache.component.set(componentCacheId, {
|
||||
LeafWrapper,
|
||||
Comp,
|
||||
});
|
||||
|
||||
return LeafWrapper;
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
import adapter from './adapter';
|
||||
import contextFactory from './context';
|
||||
|
||||
export { adapter, contextFactory };
|
||||
|
||||
export * from './renderer';
|
||||
export * as types from './types';
|
||||
export * as utils from './utils';
|
||||
export * from './hoc';
|
||||
@ -1,82 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import baseRendererFactory from './base';
|
||||
import { isEmpty } from '../utils';
|
||||
import { IRendererAppHelper, IBaseRendererProps, IBaseRenderComponent } from '../types';
|
||||
import logger from '../utils/logger';
|
||||
|
||||
export default function addonRendererFactory(): IBaseRenderComponent {
|
||||
const BaseRenderer = baseRendererFactory();
|
||||
return class AddonRenderer extends BaseRenderer {
|
||||
static displayName = 'AddonRenderer';
|
||||
|
||||
__namespace = 'addon';
|
||||
|
||||
static propTypes = {
|
||||
config: PropTypes.object,
|
||||
__schema: PropTypes.object,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
config: {},
|
||||
__schema: {},
|
||||
};
|
||||
|
||||
addonKey: any;
|
||||
appHelper: IRendererAppHelper;
|
||||
open: () => any;
|
||||
close: () => any;
|
||||
|
||||
__afterInit(props: IBaseRendererProps) {
|
||||
this.__generateCtx({
|
||||
component: this,
|
||||
});
|
||||
const schema = props.__schema || {};
|
||||
this.state = this.__parseData(schema.state || {});
|
||||
if (isEmpty(props.config) || !props.config?.addonKey) {
|
||||
logger.warn('lce addon has wrong config');
|
||||
this.setState({
|
||||
__hasError: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 注册插件
|
||||
this.addonKey = props.config.addonKey;
|
||||
this.appHelper.addons = this.appHelper.addons || {};
|
||||
this.appHelper.addons[this.addonKey] = this;
|
||||
this.__initDataSource(props);
|
||||
this.open = this.open || (() => { });
|
||||
this.close = this.close || (() => { });
|
||||
this.__executeLifeCycleMethod('constructor', [...arguments]);
|
||||
}
|
||||
|
||||
async componentWillUnmount() {
|
||||
super.componentWillUnmount?.apply(this, [...arguments] as any);
|
||||
// 注销插件
|
||||
const config = this.props.config || {};
|
||||
if (config && this.appHelper.addons) {
|
||||
delete this.appHelper.addons[config.addonKey];
|
||||
}
|
||||
}
|
||||
|
||||
get utils() {
|
||||
const { utils = {} } = this.context.config || {};
|
||||
return { ...this.appHelper.utils, ...utils };
|
||||
}
|
||||
|
||||
render() {
|
||||
const { __schema } = this.props;
|
||||
|
||||
if (this.__checkSchema(__schema)) {
|
||||
return '插件 schema 结构异常!';
|
||||
}
|
||||
|
||||
this.__debug(`${AddonRenderer.displayName} render - ${__schema.fileName}`);
|
||||
this.__generateCtx({
|
||||
component: this,
|
||||
});
|
||||
this.__render();
|
||||
|
||||
return this.__renderContent(this.__renderContextProvider({ compContext: this }));
|
||||
}
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,38 +0,0 @@
|
||||
import baseRendererFactory from './base';
|
||||
import { IBaseRendererProps, IBaseRenderComponent } from '../types';
|
||||
|
||||
export default function blockRendererFactory(): IBaseRenderComponent {
|
||||
const BaseRenderer = baseRendererFactory();
|
||||
return class BlockRenderer extends BaseRenderer {
|
||||
static displayName = 'BlockRenderer';
|
||||
|
||||
__namespace = 'block';
|
||||
|
||||
__afterInit(props: IBaseRendererProps) {
|
||||
this.__generateCtx({});
|
||||
const schema = props.__schema || {};
|
||||
this.state = this.__parseData(schema.state || {});
|
||||
this.__initDataSource(props);
|
||||
this.__executeLifeCycleMethod('constructor', [...arguments]);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { __schema, __components } = this.props;
|
||||
|
||||
if (this.__checkSchema(__schema, 'Div')) {
|
||||
return '区块 schema 结构异常!';
|
||||
}
|
||||
|
||||
this.__debug(`${BlockRenderer.displayName} render - ${__schema?.fileName}`);
|
||||
this.__generateCtx({});
|
||||
this.__render();
|
||||
|
||||
const { Block } = __components;
|
||||
if (Block) {
|
||||
return this.__renderComp(Block, {});
|
||||
}
|
||||
|
||||
return this.__renderContent(this.__renderContextProvider());
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
import baseRendererFactory from './base';
|
||||
import { IBaseRendererProps, IBaseRenderComponent } from '../types';
|
||||
|
||||
export default function componentRendererFactory(): IBaseRenderComponent {
|
||||
const BaseRenderer = baseRendererFactory();
|
||||
return class CompRenderer extends BaseRenderer {
|
||||
static displayName = 'CompRenderer';
|
||||
|
||||
__namespace = 'component';
|
||||
|
||||
__afterInit(props: IBaseRendererProps) {
|
||||
this.__generateCtx({
|
||||
component: this,
|
||||
});
|
||||
const schema = props.__schema || {};
|
||||
this.state = this.__parseData(schema.state || {});
|
||||
this.__initDataSource(props);
|
||||
this.__executeLifeCycleMethod('constructor', arguments as any);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { __schema, __components } = this.props;
|
||||
if (this.__checkSchema(__schema)) {
|
||||
return '自定义组件 schema 结构异常!';
|
||||
}
|
||||
this.__debug(`${CompRenderer.displayName} render - ${__schema.fileName}`);
|
||||
|
||||
this.__generateCtx({
|
||||
component: this,
|
||||
});
|
||||
this.__render();
|
||||
|
||||
const noContainer = this.__parseData(__schema.props?.noContainer);
|
||||
|
||||
this.__bindCustomMethods(this.props);
|
||||
|
||||
if (noContainer) {
|
||||
return this.__renderContextProvider({ compContext: this });
|
||||
}
|
||||
|
||||
const Component = __components?.[__schema?.componentName];
|
||||
|
||||
if (!Component) {
|
||||
return this.__renderContent(this.__renderContextProvider({ compContext: this }));
|
||||
}
|
||||
|
||||
return this.__renderComp(Component, this.__renderContextProvider({ compContext: this }));
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
import baseRendererFactory from './base';
|
||||
import pageRendererFactory from './page';
|
||||
import componentRendererFactory from './component';
|
||||
import blockRendererFactory from './block';
|
||||
import addonRendererFactory from './addon';
|
||||
import tempRendererFactory from './temp';
|
||||
import rendererFactory from './renderer';
|
||||
|
||||
export {
|
||||
baseRendererFactory,
|
||||
pageRendererFactory,
|
||||
componentRendererFactory,
|
||||
blockRendererFactory,
|
||||
addonRendererFactory,
|
||||
tempRendererFactory,
|
||||
rendererFactory,
|
||||
};
|
||||
@ -1,63 +0,0 @@
|
||||
import { getLogger } from '@alilc/lowcode-utils';
|
||||
import baseRendererFactory from './base';
|
||||
import { IBaseRendererProps, IBaseRenderComponent } from '../types';
|
||||
|
||||
const logger = getLogger({ level: 'warn', bizName: 'renderer-core:page' });
|
||||
|
||||
export default function pageRendererFactory(): IBaseRenderComponent {
|
||||
const BaseRenderer = baseRendererFactory();
|
||||
return class PageRenderer extends BaseRenderer {
|
||||
static displayName = 'PageRenderer';
|
||||
|
||||
__namespace = 'page';
|
||||
|
||||
__afterInit(props: IBaseRendererProps, ...rest: unknown[]) {
|
||||
this.__generateCtx({
|
||||
page: this,
|
||||
});
|
||||
const schema = props.__schema || {};
|
||||
this.state = this.__parseData(schema.state || {});
|
||||
this.__initDataSource(props);
|
||||
this.__executeLifeCycleMethod('constructor', [props, ...rest]);
|
||||
}
|
||||
|
||||
async componentDidUpdate(prevProps: IBaseRendererProps, _prevState: {}, snapshot: unknown) {
|
||||
const { __ctx } = this.props;
|
||||
// 当编排的时候修改 schema.state 值,需要将最新 schema.state 值 setState
|
||||
if (JSON.stringify(prevProps.__schema.state) != JSON.stringify(this.props.__schema.state)) {
|
||||
const newState = this.__parseData(this.props.__schema.state, __ctx);
|
||||
this.setState(newState);
|
||||
}
|
||||
|
||||
super.componentDidUpdate?.(prevProps, _prevState, snapshot);
|
||||
}
|
||||
|
||||
setState(state: any, callback?: () => void) {
|
||||
logger.info('page set state', state);
|
||||
super.setState(state, callback);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { __schema, __components } = this.props;
|
||||
if (this.__checkSchema(__schema)) {
|
||||
return '页面schema结构异常!';
|
||||
}
|
||||
this.__debug(`${PageRenderer.displayName} render - ${__schema.fileName}`);
|
||||
|
||||
this.__bindCustomMethods(this.props);
|
||||
this.__initDataSource(this.props);
|
||||
|
||||
this.__generateCtx({
|
||||
page: this,
|
||||
});
|
||||
this.__render();
|
||||
|
||||
const { Page } = __components;
|
||||
if (Page) {
|
||||
return this.__renderComp(Page, { pageContext: this });
|
||||
}
|
||||
|
||||
return this.__renderContent(this.__renderContextProvider({ pageContext: this }));
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
import Debug from 'debug';
|
||||
import adapter from '../adapter';
|
||||
import contextFactory from '../context';
|
||||
import { isFileSchema, isEmpty } from '../utils';
|
||||
import baseRendererFactory from './base';
|
||||
import divFactory from '../components/Div';
|
||||
import { IRenderComponent, IRendererProps, IRendererState } from '../types';
|
||||
import { IPublicTypeNodeSchema, IPublicTypeRootSchema } from '@alilc/lowcode-types';
|
||||
import logger from '../utils/logger';
|
||||
|
||||
export default function rendererFactory(): IRenderComponent {
|
||||
const { PureComponent, Component, createElement, findDOMNode } = adapter.getRuntime();
|
||||
const RENDERER_COMPS: any = adapter.getRenderers();
|
||||
const BaseRenderer = baseRendererFactory();
|
||||
const AppContext = contextFactory();
|
||||
const Div = divFactory();
|
||||
|
||||
const ConfigProvider = adapter.getConfigProvider() || Div;
|
||||
|
||||
const debug = Debug('renderer:entry');
|
||||
|
||||
class FaultComponent extends PureComponent<IPublicTypeNodeSchema | any> {
|
||||
render() {
|
||||
logger.error(`%c${this.props.componentName || ''} 组件渲染异常, 异常原因: ${this.props.error?.message || this.props.error || '未知'}`, 'color: #ff0000;');
|
||||
return createElement(Div, {
|
||||
style: {
|
||||
width: '100%',
|
||||
height: '50px',
|
||||
lineHeight: '50px',
|
||||
textAlign: 'center',
|
||||
fontSize: '15px',
|
||||
color: '#ff0000',
|
||||
border: '2px solid #ff0000',
|
||||
},
|
||||
}, `${this.props.componentName || ''} 组件渲染异常,请查看控制台日志`);
|
||||
}
|
||||
}
|
||||
|
||||
class NotFoundComponent extends PureComponent<{
|
||||
componentName: string;
|
||||
} & IRendererProps> {
|
||||
render() {
|
||||
if (this.props.enableStrictNotFoundMode) {
|
||||
return `${this.props.componentName || ''} Component Not Found`;
|
||||
}
|
||||
return createElement(Div, this.props, this.props.children || `${this.props.componentName || ''} Component Not Found`);
|
||||
}
|
||||
}
|
||||
|
||||
return class Renderer extends Component<IRendererProps> {
|
||||
static displayName = 'Renderer';
|
||||
|
||||
state: Partial<IRendererState> = {};
|
||||
|
||||
__ref: any;
|
||||
|
||||
static defaultProps: IRendererProps = {
|
||||
appHelper: undefined,
|
||||
components: {},
|
||||
designMode: '',
|
||||
suspended: false,
|
||||
schema: {} as IPublicTypeRootSchema,
|
||||
onCompGetRef: () => { },
|
||||
onCompGetCtx: () => { },
|
||||
thisRequiredInJSE: true,
|
||||
};
|
||||
|
||||
static findDOMNode = findDOMNode;
|
||||
|
||||
constructor(props: IRendererProps, context: any) {
|
||||
super(props, context);
|
||||
this.state = {};
|
||||
debug(`entry.constructor - ${props?.schema?.componentName}`);
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
debug(`entry.componentDidMount - ${this.props.schema && this.props.schema.componentName}`);
|
||||
}
|
||||
|
||||
async componentDidUpdate() {
|
||||
debug(`entry.componentDidUpdate - ${this.props?.schema?.componentName}`);
|
||||
}
|
||||
|
||||
async componentWillUnmount() {
|
||||
debug(`entry.componentWillUnmount - ${this.props?.schema?.componentName}`);
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error) {
|
||||
this.state.engineRenderError = true;
|
||||
this.state.error = error;
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps: IRendererProps) {
|
||||
return !nextProps.suspended;
|
||||
}
|
||||
|
||||
__getRef = (ref: any) => {
|
||||
this.__ref = ref;
|
||||
if (ref) {
|
||||
this.props.onCompGetRef?.(this.props.schema, ref);
|
||||
}
|
||||
};
|
||||
|
||||
isValidComponent(SetComponent: any) {
|
||||
return SetComponent;
|
||||
}
|
||||
|
||||
createElement(SetComponent: any, props: any, children?: any) {
|
||||
return (this.props.customCreateElement || createElement)(SetComponent, props, children);
|
||||
}
|
||||
|
||||
getNotFoundComponent() {
|
||||
return this.props.notFoundComponent || NotFoundComponent;
|
||||
}
|
||||
|
||||
getFaultComponent() {
|
||||
const { faultComponent, faultComponentMap, schema } = this.props;
|
||||
if (faultComponentMap) {
|
||||
const { componentName } = schema;
|
||||
return faultComponentMap[componentName] || faultComponent || FaultComponent;
|
||||
}
|
||||
return faultComponent || FaultComponent;
|
||||
}
|
||||
|
||||
getComp() {
|
||||
const { schema, components } = this.props;
|
||||
const { componentName } = schema;
|
||||
const allComponents = { ...RENDERER_COMPS, ...components };
|
||||
let Comp = allComponents[componentName] || RENDERER_COMPS[`${componentName}Renderer`];
|
||||
if (Comp && Comp.prototype) {
|
||||
if (!(Comp.prototype instanceof BaseRenderer)) {
|
||||
Comp = RENDERER_COMPS[`${componentName}Renderer`];
|
||||
}
|
||||
}
|
||||
return Comp;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { schema, designMode, appHelper, components } = this.props;
|
||||
if (isEmpty(schema)) {
|
||||
return null;
|
||||
}
|
||||
// 兼容乐高区块模板
|
||||
if (schema.componentName !== 'Div' && !isFileSchema(schema)) {
|
||||
logger.error('The root component name needs to be one of Page、Block、Component, please check the schema: ', schema);
|
||||
return '模型结构异常';
|
||||
}
|
||||
debug('entry.render');
|
||||
const allComponents = { ...RENDERER_COMPS, ...components };
|
||||
let Comp = this.getComp();
|
||||
|
||||
if (this.state && this.state.engineRenderError) {
|
||||
return createElement(this.getFaultComponent(), {
|
||||
...this.props,
|
||||
error: this.state.error,
|
||||
});
|
||||
}
|
||||
|
||||
if (Comp) {
|
||||
return createElement(AppContext.Provider, {
|
||||
value: {
|
||||
appHelper,
|
||||
components: allComponents,
|
||||
engine: this,
|
||||
},
|
||||
}, createElement(ConfigProvider, {
|
||||
device: this.props.device,
|
||||
locale: this.props.locale,
|
||||
}, createElement(Comp, {
|
||||
key: schema.__ctx && `${schema.__ctx.lceKey}_${schema.__ctx.idx || '0'}`,
|
||||
ref: this.__getRef,
|
||||
__appHelper: appHelper,
|
||||
__components: allComponents,
|
||||
__schema: schema,
|
||||
__designMode: designMode,
|
||||
...this.props,
|
||||
})));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
import { IBaseRenderComponent } from '../types';
|
||||
import logger from '../utils/logger';
|
||||
import baseRendererFactory from './base';
|
||||
|
||||
export default function tempRendererFactory(): IBaseRenderComponent {
|
||||
const BaseRenderer = baseRendererFactory();
|
||||
|
||||
return class TempRenderer extends BaseRenderer {
|
||||
static displayName = 'TempRenderer';
|
||||
|
||||
__namespace = 'temp';
|
||||
|
||||
cacheSetState?: Record<string, any>;
|
||||
|
||||
__init() {
|
||||
this.state = {};
|
||||
this.cacheSetState = {};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const ctx = this.props.__ctx;
|
||||
if (!ctx) return;
|
||||
const { setState } = ctx;
|
||||
this.cacheSetState = setState;
|
||||
ctx.setState = (...args: any) => {
|
||||
setState.call(ctx, ...args);
|
||||
setTimeout(() => this.forceUpdate(), 0);
|
||||
};
|
||||
this.__debug(`componentDidMount - ${this.props.__schema.fileName}`);
|
||||
}
|
||||
|
||||
async componentDidUpdate() {
|
||||
this.__debug(`componentDidUpdate - ${this.props.__schema.fileName}`);
|
||||
}
|
||||
|
||||
async componentWillUnmount() {
|
||||
const ctx = this.props.__ctx;
|
||||
if (!ctx || !this.cacheSetState) return;
|
||||
ctx.setState = this.cacheSetState;
|
||||
delete this.cacheSetState;
|
||||
this.__debug(`componentWillUnmount - ${this.props.__schema.fileName}`);
|
||||
}
|
||||
|
||||
async componentDidCatch(e: any) {
|
||||
logger.warn(e);
|
||||
this.__debug(`componentDidCatch - ${this.props.__schema.fileName}`);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { __schema, __ctx } = this.props;
|
||||
if (this.__checkSchema(__schema)) {
|
||||
return '下钻编辑 schema 结构异常!';
|
||||
}
|
||||
|
||||
this.__debug(`${TempRenderer.displayName} render - ${__schema?.fileName}`);
|
||||
|
||||
return this.__renderContent(this.__renderContextProvider({ __ctx }));
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1,342 +0,0 @@
|
||||
import type { ComponentLifecycle, CSSProperties } from 'react';
|
||||
import { BuiltinSimulatorHost, BuiltinSimulatorRenderer } from '@alilc/lowcode-designer';
|
||||
import { RequestHandler, IPublicTypeNodeSchema, IPublicTypeRootSchema, IPublicTypeJSONObject } from '@alilc/lowcode-types';
|
||||
|
||||
export type ISchema = IPublicTypeNodeSchema | IPublicTypeRootSchema;
|
||||
|
||||
/*
|
||||
** Duck typed component type supporting both react and rax
|
||||
*/
|
||||
interface IGeneralComponent<P = {}, S = {}, SS = any> extends ComponentLifecycle<P, S, SS> {
|
||||
readonly props: Readonly<P> & Readonly<{ children?: any | undefined }>;
|
||||
state: Readonly<S>;
|
||||
refs: Record<string, any>;
|
||||
context: any;
|
||||
setState<K extends keyof S>(
|
||||
state: ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null),
|
||||
callback?: () => void
|
||||
): void;
|
||||
forceUpdate(callback?: () => void): void;
|
||||
render(): any;
|
||||
}
|
||||
|
||||
export type IGeneralConstructor<
|
||||
T = {
|
||||
[key: string]: any;
|
||||
}, S = {
|
||||
[key: string]: any;
|
||||
}, D = any
|
||||
> = new <TT = T, SS = S, DD = D>(props: TT, context: any) => IGeneralComponent<TT, SS, DD>;
|
||||
|
||||
/**
|
||||
* duck-typed History
|
||||
*
|
||||
* @see https://github.com/ReactTraining/history/tree/master/docs/api-reference.md
|
||||
*/
|
||||
interface IHistoryLike {
|
||||
readonly action: any;
|
||||
readonly location: ILocationLike;
|
||||
createHref: (to: any) => string;
|
||||
push: (to: any, state?: any) => void;
|
||||
replace: (to: any, state?: any) => void;
|
||||
go: (delta: any) => void;
|
||||
back: () => void;
|
||||
forward: () => void;
|
||||
listen: (listener: any) => () => void;
|
||||
block: (blocker: any) => () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* duck-typed History.Location
|
||||
*
|
||||
* @see https://github.com/remix-run/history/blob/dev/docs/api-reference.md#location
|
||||
*/
|
||||
export interface ILocationLike {
|
||||
pathname: any;
|
||||
search: any;
|
||||
state: any;
|
||||
hash: any;
|
||||
key?: any;
|
||||
}
|
||||
|
||||
export type IRendererAppHelper = Partial<{
|
||||
|
||||
/** 全局公共函数 */
|
||||
utils: Record<string, any>;
|
||||
|
||||
/** 全局常量 */
|
||||
constants: Record<string, any>;
|
||||
|
||||
/** react-router 的 location 实例 */
|
||||
location: ILocationLike;
|
||||
|
||||
/** react-router 的 history 实例 */
|
||||
history: IHistoryLike;
|
||||
|
||||
/** @deprecated 已无业务使用 */
|
||||
match: any;
|
||||
|
||||
/** @experimental 内部使用 */
|
||||
logParams: Record<string, any>;
|
||||
|
||||
/** @experimental 内部使用 */
|
||||
addons: Record<string, any>;
|
||||
|
||||
/** @experimental 内部使用 */
|
||||
requestHandlersMap: Record<string, RequestHandler<{
|
||||
data: unknown;
|
||||
}>>;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* 渲染模块可用配置
|
||||
*
|
||||
* @see @todo @承虎
|
||||
*/
|
||||
export interface IRendererProps {
|
||||
|
||||
/** 符合低代码搭建协议的数据 */
|
||||
schema: IPublicTypeRootSchema | IPublicTypeNodeSchema;
|
||||
|
||||
/** 组件依赖的实例 */
|
||||
components: Record<string, IGeneralComponent>;
|
||||
|
||||
/** CSS 类名 */
|
||||
className?: string;
|
||||
|
||||
/** style */
|
||||
style?: CSSProperties;
|
||||
|
||||
/** id */
|
||||
id?: string | number;
|
||||
|
||||
/** 语言 */
|
||||
locale?: string;
|
||||
|
||||
/**
|
||||
* 多语言语料
|
||||
* 配置规范参见《低代码搭建组件描述协议》https://lowcode-engine.cn/lowcode 中 2.6 国际化多语言支持
|
||||
* */
|
||||
messages?: Record<string, any>;
|
||||
|
||||
/** 主要用于设置渲染模块的全局上下文,里面定义的内容可以在低代码中通过 this 来访问,比如 this.utils */
|
||||
appHelper?: IRendererAppHelper;
|
||||
|
||||
/**
|
||||
* 配置规范参见《低代码搭建组件描述协议》https://lowcode-engine.cn/lowcode
|
||||
* 主要在搭建场景中使用,用于提升用户搭建体验。
|
||||
*
|
||||
* > 在生产环境下不需要设置
|
||||
*/
|
||||
componentsMap?: { [key: string]: any };
|
||||
|
||||
/** 设计模式,可选值:live、design */
|
||||
designMode?: string;
|
||||
|
||||
/** 渲染模块是否挂起,当设置为 true 时,渲染模块最外层容器的 shouldComponentUpdate 将始终返回false,在下钻编辑或者多引擎渲染的场景会用到该参数。 */
|
||||
suspended?: boolean;
|
||||
|
||||
/** 组件获取 ref 时触发的钩子 */
|
||||
onCompGetRef?: (schema: IPublicTypeNodeSchema, ref: any) => void;
|
||||
|
||||
/** 组件 ctx 更新回调 */
|
||||
onCompGetCtx?: (schema: IPublicTypeNodeSchema, ref: any) => void;
|
||||
|
||||
/** 传入的 schema 是否有变更 */
|
||||
getSchemaChangedSymbol?: () => boolean;
|
||||
|
||||
/** 设置 schema 是否有变更 */
|
||||
setSchemaChangedSymbol?: (symbol: boolean) => void;
|
||||
|
||||
/** 自定义创建 element 的钩子 */
|
||||
customCreateElement?: (Component: any, props: any, children: any) => any;
|
||||
|
||||
/** 渲染类型,标识当前模块是以什么类型进行渲染的 */
|
||||
rendererName?: 'LowCodeRenderer' | 'PageRenderer' | string;
|
||||
|
||||
/** 当找不到组件时,显示的组件 */
|
||||
notFoundComponent?: IGeneralComponent;
|
||||
|
||||
/** 当组件渲染异常时,显示的组件 */
|
||||
faultComponent?: IGeneralComponent;
|
||||
|
||||
/** */
|
||||
faultComponentMap?: {
|
||||
[prop: string]: IGeneralComponent;
|
||||
};
|
||||
|
||||
/** 设备信息 */
|
||||
device?: string;
|
||||
|
||||
/**
|
||||
* @default true
|
||||
* JSExpression 是否只支持使用 this 来访问上下文变量
|
||||
*/
|
||||
thisRequiredInJSE?: boolean;
|
||||
|
||||
/**
|
||||
* @default false
|
||||
* 当开启组件未找到严格模式时,渲染模块不会默认给一个容器组件
|
||||
*/
|
||||
enableStrictNotFoundMode?: boolean;
|
||||
}
|
||||
|
||||
export interface IRendererState {
|
||||
engineRenderError?: boolean;
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染内部模块可用配置
|
||||
*/
|
||||
export interface IBaseRendererProps {
|
||||
locale?: string;
|
||||
messages: Record<string, any>;
|
||||
__appHelper: IRendererAppHelper;
|
||||
__components: Record<string, any>;
|
||||
__ctx: Record<string, any>;
|
||||
__schema: IPublicTypeRootSchema;
|
||||
__host?: BuiltinSimulatorHost;
|
||||
__container?: BuiltinSimulatorRenderer;
|
||||
config?: Record<string, any>;
|
||||
designMode?: 'design';
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
id?: string | number;
|
||||
getSchemaChangedSymbol?: () => boolean;
|
||||
setSchemaChangedSymbol?: (symbol: boolean) => void;
|
||||
thisRequiredInJSE?: boolean;
|
||||
documentId?: string;
|
||||
getNode?: any;
|
||||
|
||||
/**
|
||||
* 设备类型,默认值:'default'
|
||||
*/
|
||||
device?: 'default' | 'mobile' | string;
|
||||
componentName?: string;
|
||||
}
|
||||
|
||||
export interface INodeInfo {
|
||||
schema?: IPublicTypeNodeSchema;
|
||||
Comp: any;
|
||||
componentInfo?: any;
|
||||
componentChildren?: any;
|
||||
}
|
||||
|
||||
export interface JSExpression {
|
||||
type: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface DataSourceItem {
|
||||
id: string;
|
||||
isInit?: boolean | JSExpression;
|
||||
type?: string;
|
||||
options?: {
|
||||
uri: string | JSExpression;
|
||||
params?: IPublicTypeJSONObject | JSExpression;
|
||||
method?: string | JSExpression;
|
||||
shouldFetch?: string;
|
||||
willFetch?: string;
|
||||
fit?: string;
|
||||
didFetch?: string;
|
||||
};
|
||||
dataHandler?: JSExpression;
|
||||
}
|
||||
|
||||
export interface DataSource {
|
||||
list?: DataSourceItem[];
|
||||
dataHandler?: JSExpression;
|
||||
}
|
||||
|
||||
export interface IRuntime {
|
||||
[key: string]: any;
|
||||
Component: IGeneralConstructor;
|
||||
PureComponent: IGeneralConstructor;
|
||||
createElement: (...args: any) => any;
|
||||
createContext: (...args: any) => any;
|
||||
forwardRef: (...args: any) => any;
|
||||
findDOMNode: (...args: any) => any;
|
||||
}
|
||||
|
||||
export interface IRendererModules {
|
||||
BaseRenderer?: IBaseRenderComponent;
|
||||
PageRenderer: IBaseRenderComponent;
|
||||
ComponentRenderer: IBaseRenderComponent;
|
||||
BlockRenderer?: IBaseRenderComponent;
|
||||
AddonRenderer?: IBaseRenderComponent;
|
||||
TempRenderer?: IBaseRenderComponent;
|
||||
DivRenderer?: IBaseRenderComponent;
|
||||
}
|
||||
|
||||
export interface IBaseRendererContext {
|
||||
appHelper: IRendererAppHelper;
|
||||
components: Record<string, IGeneralComponent>;
|
||||
engine: IRuntime;
|
||||
pageContext?: IBaseRenderComponent;
|
||||
compContext?: IBaseRenderComponent;
|
||||
}
|
||||
|
||||
export type IBaseRendererInstance = IGeneralComponent<
|
||||
IBaseRendererProps,
|
||||
Record<string, any>,
|
||||
any
|
||||
>
|
||||
& {
|
||||
reloadDataSource(): Promise<any>;
|
||||
__beforeInit(props: IBaseRendererProps): void;
|
||||
__init(props: IBaseRendererProps): void;
|
||||
__afterInit(props: IBaseRendererProps): void;
|
||||
__executeLifeCycleMethod(method: string, args?: any[]): void;
|
||||
__bindCustomMethods(props: IBaseRendererProps): void;
|
||||
__generateCtx(ctx: Record<string, any>): void;
|
||||
__parseData(data: any, ctx?: any): any;
|
||||
__initDataSource(props: IBaseRendererProps): void;
|
||||
__render(): void;
|
||||
__getRef(ref: any): void;
|
||||
__getSchemaChildrenVirtualDom(
|
||||
schema: IPublicTypeNodeSchema | undefined,
|
||||
Comp: any,
|
||||
nodeChildrenMap?: any
|
||||
): any;
|
||||
__getComponentProps(schema: IPublicTypeNodeSchema | undefined, scope: any, Comp: any, componentInfo?: any): any;
|
||||
__createDom(): any;
|
||||
__createVirtualDom(schema: any, self: any, parentInfo: INodeInfo, idx: string | number): any;
|
||||
__createLoopVirtualDom(schema: any, self: any, parentInfo: INodeInfo, idx: number | string): any;
|
||||
__parseProps(props: any, self: any, path: string, info: INodeInfo): any;
|
||||
__initDebug?(): void;
|
||||
__debug(...args: any[]): void;
|
||||
__renderContextProvider(customProps?: object, children?: any): any;
|
||||
__renderContextConsumer(children: any): any;
|
||||
__renderContent(children: any): any;
|
||||
__checkSchema(schema: IPublicTypeNodeSchema | undefined, extraComponents?: string | string[]): any;
|
||||
__renderComp(Comp: any, ctxProps: object): any;
|
||||
$(filedId: string, instance?: any): any;
|
||||
};
|
||||
|
||||
export interface IBaseRenderComponent {
|
||||
new(
|
||||
props: IBaseRendererProps,
|
||||
context: any
|
||||
): IBaseRendererInstance;
|
||||
}
|
||||
|
||||
export interface IRenderComponent {
|
||||
displayName: string;
|
||||
defaultProps: IRendererProps;
|
||||
findDOMNode: (...args: any) => any;
|
||||
|
||||
new(props: IRendererProps, context: any): IGeneralComponent<IRendererProps, IRendererState> & {
|
||||
[x: string]: any;
|
||||
__getRef: (ref: any) => void;
|
||||
componentDidMount(): Promise<void> | void;
|
||||
componentDidUpdate(): Promise<void> | void;
|
||||
componentWillUnmount(): Promise<void> | void;
|
||||
componentDidCatch(e: any): Promise<void> | void;
|
||||
shouldComponentUpdate(nextProps: IRendererProps): boolean;
|
||||
isValidComponent(SetComponent: any): any;
|
||||
createElement(SetComponent: any, props: any, children?: any): any;
|
||||
getNotFoundComponent(): any;
|
||||
getFaultComponent(): any;
|
||||
};
|
||||
}
|
||||
@ -1,370 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
/* eslint-disable no-new-func */
|
||||
import logger from './logger';
|
||||
import { IPublicTypeRootSchema, IPublicTypeNodeSchema, IPublicTypeJSSlot } from '@alilc/lowcode-types';
|
||||
import { isI18nData, isJSExpression } from '@alilc/lowcode-utils';
|
||||
import { isEmpty } from 'lodash';
|
||||
import IntlMessageFormat from 'intl-messageformat';
|
||||
import pkg from '../../package.json';
|
||||
|
||||
(window as any).sdkVersion = pkg.version;
|
||||
|
||||
export { pick, isEqualWith as deepEqual, cloneDeep as clone, isEmpty, throttle, debounce } from 'lodash';
|
||||
|
||||
const EXPRESSION_TYPE = {
|
||||
JSEXPRESSION: 'JSExpression',
|
||||
JSFUNCTION: 'JSFunction',
|
||||
JSSLOT: 'JSSlot',
|
||||
JSBLOCK: 'JSBlock',
|
||||
I18N: 'i18n',
|
||||
};
|
||||
|
||||
/**
|
||||
* check if schema passed in is a valid schema
|
||||
* @name isSchema
|
||||
* @returns boolean
|
||||
*/
|
||||
export function isSchema(schema: any): schema is IPublicTypeNodeSchema {
|
||||
if (isEmpty(schema)) {
|
||||
return false;
|
||||
}
|
||||
// Leaf and Slot should be valid
|
||||
if (schema.componentName === 'Leaf' || schema.componentName === 'Slot') {
|
||||
return true;
|
||||
}
|
||||
if (Array.isArray(schema)) {
|
||||
return schema.every((item) => isSchema(item));
|
||||
}
|
||||
// check if props is valid
|
||||
const isValidProps = (props: any) => {
|
||||
if (!props) {
|
||||
return false;
|
||||
}
|
||||
if (isJSExpression(props)) {
|
||||
return true;
|
||||
}
|
||||
return (typeof schema.props === 'object' && !Array.isArray(props));
|
||||
};
|
||||
return !!(schema.componentName && isValidProps(schema.props));
|
||||
}
|
||||
|
||||
/**
|
||||
* check if schema passed in is a container type, including : Component Block Page
|
||||
* @param schema
|
||||
* @returns boolean
|
||||
*/
|
||||
export function isFileSchema(schema: IPublicTypeNodeSchema): schema is IPublicTypeRootSchema {
|
||||
if (!isSchema(schema)) {
|
||||
return false;
|
||||
}
|
||||
return ['Page', 'Block', 'Component'].includes(schema.componentName);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if current page is nested within another page with same host
|
||||
* @returns boolean
|
||||
*/
|
||||
export function inSameDomain() {
|
||||
try {
|
||||
return window.parent !== window && window.parent.location.host === window.location.host;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get css styled name from schema`s fileName
|
||||
* FileName -> lce-file-name
|
||||
* @returns string
|
||||
*/
|
||||
export function getFileCssName(fileName: string) {
|
||||
if (!fileName) {
|
||||
return;
|
||||
}
|
||||
const name = fileName.replace(/([A-Z])/g, '-$1').toLowerCase();
|
||||
return (`lce-${name}`)
|
||||
.split('-')
|
||||
.filter((p) => !!p)
|
||||
.join('-');
|
||||
}
|
||||
|
||||
/**
|
||||
* check if a object is type of JSSlot
|
||||
* @returns string
|
||||
*/
|
||||
export function isJSSlot(obj: any): obj is IPublicTypeJSSlot {
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
if (typeof obj !== 'object' || Array.isArray(obj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compatible with the old protocol JSBlock
|
||||
return [EXPRESSION_TYPE.JSSLOT, EXPRESSION_TYPE.JSBLOCK].includes(obj.type);
|
||||
}
|
||||
|
||||
/**
|
||||
* get value from an object
|
||||
* @returns string
|
||||
*/
|
||||
export function getValue(obj: any, path: string, defaultValue = {}) {
|
||||
// array is not valid type, return default value
|
||||
if (Array.isArray(obj)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
if (isEmpty(obj) || typeof obj !== 'object') {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
const res = path.split('.').reduce((pre, cur) => {
|
||||
return pre && pre[cur];
|
||||
}, obj);
|
||||
if (res === undefined) {
|
||||
return defaultValue;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于处理国际化字符串
|
||||
* @param {*} key 语料标识
|
||||
* @param {*} values 字符串模版变量
|
||||
* @param {*} locale 国际化标识,例如 zh-CN、en-US
|
||||
* @param {*} messages 国际化语言包
|
||||
*/
|
||||
export function getI18n(key: string, values = {}, locale = 'zh-CN', messages: Record<string, any> = {}) {
|
||||
if (!messages || !messages[locale] || !messages[locale][key]) {
|
||||
return '';
|
||||
}
|
||||
const formater = new IntlMessageFormat(messages[locale][key], locale);
|
||||
return formater.format(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前组件是否能够设置ref
|
||||
* @param {*} Comp 需要判断的组件
|
||||
*/
|
||||
export function canAcceptsRef(Comp: any) {
|
||||
const hasSymbol = typeof Symbol === 'function' && Symbol.for;
|
||||
const REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
|
||||
// eslint-disable-next-line max-len
|
||||
return Comp?.$$typeof === REACT_FORWARD_REF_TYPE || Comp?.prototype?.isReactComponent || Comp?.prototype?.setState || Comp._forwardRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* transform array to a object
|
||||
* @param arr array to be transformed
|
||||
* @param key key of array item, which`s value will be used as key in result map
|
||||
* @param overwrite overwrite existing item in result or not
|
||||
* @returns object result map
|
||||
*/
|
||||
export function transformArrayToMap(arr: any[], key: string, overwrite = true) {
|
||||
if (isEmpty(arr) || !Array.isArray(arr)) {
|
||||
return {};
|
||||
}
|
||||
const res: any = {};
|
||||
arr.forEach((item) => {
|
||||
const curKey = item[key];
|
||||
if (item[key] === undefined) {
|
||||
return;
|
||||
}
|
||||
if (res[curKey] && !overwrite) {
|
||||
return;
|
||||
}
|
||||
res[curKey] = item;
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* transform string to a function
|
||||
* @param str function in string form
|
||||
* @returns funtion
|
||||
*/
|
||||
export function transformStringToFunction(str: string) {
|
||||
if (typeof str !== 'string') {
|
||||
return str;
|
||||
}
|
||||
if (inSameDomain() && (window.parent as any).__newFunc) {
|
||||
return (window.parent as any).__newFunc(`"use strict"; return ${str}`)();
|
||||
} else {
|
||||
return new Function(`"use strict"; return ${str}`)();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象类型JSExpression,支持省略this
|
||||
* @param str expression in string form
|
||||
* @param self scope object
|
||||
* @returns funtion
|
||||
*/
|
||||
|
||||
function parseExpression(options: {
|
||||
str: any; self: any; thisRequired?: boolean; logScope?: string;
|
||||
}): any;
|
||||
function parseExpression(str: any, self: any, thisRequired?: boolean): any;
|
||||
function parseExpression(a: any, b?: any, c = false) {
|
||||
let str;
|
||||
let self;
|
||||
let thisRequired;
|
||||
let logScope;
|
||||
if (typeof a === 'object' && b === undefined) {
|
||||
str = a.str;
|
||||
self = a.self;
|
||||
thisRequired = a.thisRequired;
|
||||
logScope = a.logScope;
|
||||
} else {
|
||||
str = a;
|
||||
self = b;
|
||||
thisRequired = c;
|
||||
}
|
||||
try {
|
||||
const contextArr = ['"use strict";', 'var __self = arguments[0];'];
|
||||
contextArr.push('return ');
|
||||
let tarStr: string;
|
||||
|
||||
tarStr = (str.value || '').trim();
|
||||
|
||||
// NOTE: use __self replace 'this' in the original function str
|
||||
// may be wrong in extreme case which contains '__self' already
|
||||
tarStr = tarStr.replace(/this(\W|$)/g, (_a: any, b: any) => `__self${b}`);
|
||||
tarStr = contextArr.join('\n') + tarStr;
|
||||
|
||||
// 默认调用顶层窗口的parseObj, 保障new Function的window对象是顶层的window对象
|
||||
if (inSameDomain() && (window.parent as any).__newFunc) {
|
||||
return (window.parent as any).__newFunc(tarStr)(self);
|
||||
}
|
||||
const code = `with(${thisRequired ? '{}' : '$scope || {}'}) { ${tarStr} }`;
|
||||
return new Function('$scope', code)(self);
|
||||
} catch (err) {
|
||||
logger.error(`${logScope || ''} parseExpression.error`, err, str, self?.__self ?? self);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
parseExpression,
|
||||
};
|
||||
|
||||
export function parseThisRequiredExpression(str: any, self: any) {
|
||||
return parseExpression(str, self, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* capitalize first letter
|
||||
* @param word string to be proccessed
|
||||
* @returns string capitalized string
|
||||
*/
|
||||
export function capitalizeFirstLetter(word: string) {
|
||||
if (!word || !isString(word) || word.length === 0) {
|
||||
return word;
|
||||
}
|
||||
return word[0].toUpperCase() + word.slice(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* check str passed in is a string type of not
|
||||
* @param str obj to be checked
|
||||
* @returns boolean
|
||||
*/
|
||||
export function isString(str: any): boolean {
|
||||
return {}.toString.call(str) === '[object String]';
|
||||
}
|
||||
|
||||
/**
|
||||
* check if obj is type of variable structure
|
||||
* @param obj object to be checked
|
||||
* @returns boolean
|
||||
*/
|
||||
export function isVariable(obj: any) {
|
||||
if (!obj || Array.isArray(obj)) {
|
||||
return false;
|
||||
}
|
||||
return typeof obj === 'object' && obj?.type === 'variable';
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 i18n 结构,降级解释为对 i18n 接口的调用
|
||||
* @param i18nInfo object
|
||||
* @param self context
|
||||
*/
|
||||
export function parseI18n(i18nInfo: any, self: any) {
|
||||
return parseExpression({
|
||||
type: EXPRESSION_TYPE.JSEXPRESSION,
|
||||
value: `this.i18n('${i18nInfo.key}')`,
|
||||
}, self);
|
||||
}
|
||||
|
||||
/**
|
||||
* for each key in targetObj, run fn with the value of the value, and the context paased in.
|
||||
* @param targetObj object that keys will be for each
|
||||
* @param fn function that process each item
|
||||
* @param context
|
||||
*/
|
||||
export function forEach(targetObj: any, fn: any, context?: any) {
|
||||
if (!targetObj || Array.isArray(targetObj) || isString(targetObj) || typeof targetObj !== 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(targetObj).forEach((key) => fn.call(context, targetObj[key], key));
|
||||
}
|
||||
|
||||
interface IParseOptions {
|
||||
thisRequiredInJSE?: boolean;
|
||||
logScope?: string;
|
||||
}
|
||||
|
||||
export function parseData(schema: unknown, self: any, options: IParseOptions = {}): any {
|
||||
if (isJSExpression(schema)) {
|
||||
return parseExpression({
|
||||
str: schema,
|
||||
self,
|
||||
thisRequired: options.thisRequiredInJSE,
|
||||
logScope: options.logScope,
|
||||
});
|
||||
} else if (isI18nData(schema)) {
|
||||
return parseI18n(schema, self);
|
||||
} else if (typeof schema === 'string') {
|
||||
return schema.trim();
|
||||
} else if (Array.isArray(schema)) {
|
||||
return schema.map((item) => parseData(item, self, options));
|
||||
} else if (typeof schema === 'function') {
|
||||
return schema.bind(self);
|
||||
} else if (typeof schema === 'object') {
|
||||
// 对于undefined及null直接返回
|
||||
if (!schema) {
|
||||
return schema;
|
||||
}
|
||||
const res: any = {};
|
||||
forEach(schema, (val: any, key: string) => {
|
||||
if (key.startsWith('__')) {
|
||||
return;
|
||||
}
|
||||
res[key] = parseData(val, self, options);
|
||||
});
|
||||
return res;
|
||||
}
|
||||
return schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* process params for using in a url query
|
||||
* @param obj params to be processed
|
||||
* @returns string
|
||||
*/
|
||||
export function serializeParams(obj: any) {
|
||||
let result: any = [];
|
||||
forEach(obj, (val: any, key: any) => {
|
||||
if (val === null || val === undefined || val === '') {
|
||||
return;
|
||||
}
|
||||
if (typeof val === 'object') {
|
||||
result.push(`${key}=${encodeURIComponent(JSON.stringify(val))}`);
|
||||
} else {
|
||||
result.push(`${key}=${encodeURIComponent(val)}`);
|
||||
}
|
||||
});
|
||||
return result.join('&');
|
||||
}
|
||||
@ -1,309 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
/* eslint-disable max-len */
|
||||
/* eslint-disable object-curly-newline */
|
||||
import { isJSFunction } from '@alilc/lowcode-utils';
|
||||
import { transformArrayToMap, transformStringToFunction } from './common';
|
||||
import { jsonp, request, get, post } from './request';
|
||||
import logger from './logger';
|
||||
import { DataSource, DataSourceItem, IRendererAppHelper } from '../types';
|
||||
|
||||
const DS_STATUS = {
|
||||
INIT: 'init',
|
||||
LOADING: 'loading',
|
||||
LOADED: 'loaded',
|
||||
ERROR: 'error',
|
||||
};
|
||||
|
||||
type DataSourceType = 'fetch' | 'jsonp';
|
||||
|
||||
/**
|
||||
* do request for standard DataSourceType
|
||||
* @param {DataSourceType} type type of DataSourceItem
|
||||
* @param {any} options
|
||||
*/
|
||||
export function doRequest(type: DataSourceType, options: any) {
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { uri, url, method = 'GET', headers, params, ...otherProps } = options;
|
||||
otherProps = otherProps || {};
|
||||
if (type === 'jsonp') {
|
||||
return jsonp(uri, params, otherProps);
|
||||
}
|
||||
|
||||
if (type === 'fetch') {
|
||||
switch (method.toUpperCase()) {
|
||||
case 'GET':
|
||||
return get(uri, params, headers, otherProps);
|
||||
case 'POST':
|
||||
return post(uri, params, headers, otherProps);
|
||||
default:
|
||||
return request(uri, method, params, headers, otherProps);
|
||||
}
|
||||
}
|
||||
|
||||
logger.log(`Engine default dataSource does not support type:[${type}] dataSource request!`, options);
|
||||
}
|
||||
|
||||
// TODO: according to protocol, we should implement errorHandler/shouldFetch/willFetch/requestHandler and isSync controll.
|
||||
export class DataHelper {
|
||||
/**
|
||||
* host object that will be "this" object when excuting dataHandler
|
||||
*
|
||||
* @type {*}
|
||||
* @memberof DataHelper
|
||||
*/
|
||||
host: any;
|
||||
|
||||
/**
|
||||
* data source config
|
||||
*
|
||||
* @type {DataSource}
|
||||
* @memberof DataHelper
|
||||
*/
|
||||
config: DataSource;
|
||||
|
||||
/**
|
||||
* a parser function which will be called to process config data
|
||||
* which eventually will call common/utils.processData() to process data
|
||||
* (originalConfig) => parsedConfig
|
||||
* @type {*}
|
||||
* @memberof DataHelper
|
||||
*/
|
||||
parser: any;
|
||||
|
||||
/**
|
||||
* config.list
|
||||
*
|
||||
* @type {any[]}
|
||||
* @memberof DataHelper
|
||||
*/
|
||||
ajaxList: any[];
|
||||
|
||||
ajaxMap: any;
|
||||
|
||||
dataSourceMap: any;
|
||||
|
||||
appHelper: IRendererAppHelper;
|
||||
|
||||
constructor(comp: any, config: DataSource, appHelper: IRendererAppHelper, parser: any) {
|
||||
this.host = comp;
|
||||
this.config = config || {};
|
||||
this.parser = parser;
|
||||
this.ajaxList = config?.list || [];
|
||||
this.ajaxMap = transformArrayToMap(this.ajaxList, 'id');
|
||||
this.dataSourceMap = this.generateDataSourceMap();
|
||||
this.appHelper = appHelper;
|
||||
}
|
||||
|
||||
// 更新config,只会更新配置,状态保存;
|
||||
updateConfig(config = {}) {
|
||||
this.config = config as DataSource;
|
||||
this.ajaxList = (config as DataSource)?.list || [];
|
||||
const ajaxMap: any = transformArrayToMap(this.ajaxList, 'id');
|
||||
// 删除已经移除的接口
|
||||
Object.keys(this.ajaxMap).forEach((key) => {
|
||||
if (!ajaxMap[key]) {
|
||||
delete this.dataSourceMap[key];
|
||||
}
|
||||
});
|
||||
this.ajaxMap = ajaxMap;
|
||||
// 添加未加入到dataSourceMap中的接口
|
||||
this.ajaxList.forEach((item) => {
|
||||
if (!this.dataSourceMap[item.id]) {
|
||||
this.dataSourceMap[item.id] = {
|
||||
status: DS_STATUS.INIT,
|
||||
load: (...args: any) => {
|
||||
// @ts-ignore
|
||||
return this.getDataSource(item.id, ...args);
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
return this.dataSourceMap;
|
||||
}
|
||||
|
||||
generateDataSourceMap() {
|
||||
const res: any = {};
|
||||
this.ajaxList.forEach((item) => {
|
||||
res[item.id] = {
|
||||
status: DS_STATUS.INIT,
|
||||
load: (...args: any) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
return this.getDataSource(item.id, ...args);
|
||||
},
|
||||
};
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
updateDataSourceMap(id: string, data: any, error: any) {
|
||||
this.dataSourceMap[id].error = error || undefined;
|
||||
this.dataSourceMap[id].data = data;
|
||||
this.dataSourceMap[id].status = error ? DS_STATUS.ERROR : DS_STATUS.LOADED;
|
||||
}
|
||||
|
||||
/**
|
||||
* get all dataSourceItems which marked as isInit === true
|
||||
* @private
|
||||
* @returns
|
||||
* @memberof DataHelper
|
||||
*/
|
||||
getInitDataSourseConfigs() {
|
||||
const initConfigs = this.parser(this.ajaxList).filter((item: DataSourceItem) => {
|
||||
// according to [spec](https://lowcode-engine.cn/lowcode), isInit should be boolean true to be working
|
||||
if (item.isInit === true) {
|
||||
this.dataSourceMap[item.id].status = DS_STATUS.LOADING;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return initConfigs;
|
||||
}
|
||||
|
||||
/**
|
||||
* process all dataSourceItems which marked as isInit === true, and get dataSource request results.
|
||||
* @public
|
||||
* @returns
|
||||
* @memberof DataHelper
|
||||
*/
|
||||
getInitData() {
|
||||
const initSyncData = this.getInitDataSourseConfigs();
|
||||
// 所有 datasource 的 datahandler
|
||||
return this.asyncDataHandler(initSyncData).then((res) => {
|
||||
const { dataHandler } = this.config;
|
||||
return this.handleData(null, dataHandler, res, null);
|
||||
});
|
||||
}
|
||||
|
||||
getDataSource(id: string, params: any, otherOptions: any, callback: any) {
|
||||
const req = this.parser(this.ajaxMap[id]);
|
||||
const options = req.options || {};
|
||||
let callbackFn = callback;
|
||||
let otherOptionsObj = otherOptions;
|
||||
if (typeof otherOptions === 'function') {
|
||||
callbackFn = otherOptions;
|
||||
otherOptionsObj = {};
|
||||
}
|
||||
const { headers, ...otherProps } = otherOptionsObj || {};
|
||||
if (!req) {
|
||||
logger.warn(`getDataSource API named ${id} not exist`);
|
||||
return;
|
||||
}
|
||||
|
||||
return this.asyncDataHandler([
|
||||
{
|
||||
...req,
|
||||
options: {
|
||||
...options,
|
||||
// 支持参数为array的情况,当参数为array时,不做参数合并
|
||||
params:
|
||||
Array.isArray(options.params) || Array.isArray(params)
|
||||
? params || options.params
|
||||
: {
|
||||
...options.params,
|
||||
...params,
|
||||
},
|
||||
headers: {
|
||||
...options.headers,
|
||||
...headers,
|
||||
},
|
||||
...otherProps,
|
||||
},
|
||||
},
|
||||
])
|
||||
.then((res: any) => {
|
||||
try {
|
||||
callbackFn && callbackFn(res && res[id]);
|
||||
} catch (e) {
|
||||
logger.error('load请求回调函数报错', e);
|
||||
}
|
||||
return res && res[id];
|
||||
})
|
||||
.catch((err) => {
|
||||
try {
|
||||
callbackFn && callbackFn(null, err);
|
||||
} catch (e) {
|
||||
logger.error('load请求回调函数报错', e);
|
||||
}
|
||||
return err;
|
||||
});
|
||||
}
|
||||
|
||||
asyncDataHandler(asyncDataList: any[]) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const allReq: any[] = [];
|
||||
asyncDataList.forEach((req) => {
|
||||
const { id, type } = req;
|
||||
// TODO: need refactoring to remove 'legao' related logic
|
||||
if (!id || !type || type === 'legao') {
|
||||
return;
|
||||
}
|
||||
allReq.push(req);
|
||||
});
|
||||
|
||||
if (allReq.length === 0) {
|
||||
resolve({});
|
||||
}
|
||||
const res: any = {};
|
||||
Promise.all(
|
||||
allReq.map((item: any) => {
|
||||
return new Promise((innerResolve) => {
|
||||
const { type, id, dataHandler, options } = item;
|
||||
|
||||
const fetchHandler = (data: any, error: any) => {
|
||||
res[id] = this.handleData(id, dataHandler, data, error);
|
||||
this.updateDataSourceMap(id, res[id], error);
|
||||
innerResolve({});
|
||||
};
|
||||
|
||||
const doFetch = (innerType: string, innerOptions: any) => {
|
||||
doRequest(innerType as any, innerOptions)
|
||||
?.then((data: any) => {
|
||||
fetchHandler(data, undefined);
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
fetchHandler(undefined, err);
|
||||
});
|
||||
};
|
||||
|
||||
this.dataSourceMap[id].status = DS_STATUS.LOADING;
|
||||
doFetch(type, options);
|
||||
});
|
||||
}),
|
||||
).then(() => {
|
||||
resolve(res);
|
||||
}).catch((e) => {
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* process data using dataHandler
|
||||
*
|
||||
* @param {(string | null)} id request id, will be used in error message, can be null
|
||||
* @param {*} dataHandler
|
||||
* @param {*} data
|
||||
* @param {*} error
|
||||
* @returns
|
||||
* @memberof DataHelper
|
||||
*/
|
||||
handleData(id: string | null, dataHandler: any, data: any, error: any) {
|
||||
let dataHandlerFun = dataHandler;
|
||||
if (isJSFunction(dataHandler)) {
|
||||
dataHandlerFun = transformStringToFunction(dataHandler.value);
|
||||
}
|
||||
if (!dataHandlerFun || typeof dataHandlerFun !== 'function') {
|
||||
return data;
|
||||
}
|
||||
try {
|
||||
return dataHandlerFun.call(this.host, data, error);
|
||||
} catch (e) {
|
||||
if (id) {
|
||||
logger.error(`[${id}]单个请求数据处理函数运行出错`, e);
|
||||
} else {
|
||||
logger.error('请求数据处理函数运行出错', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
export * from './common';
|
||||
export * from './data-helper';
|
||||
export * from './request';
|
||||
@ -1,20 +0,0 @@
|
||||
import { IPublicTypeJSExpression } from '@alilc/lowcode-types';
|
||||
import { isJSExpression } from '@alilc/lowcode-utils';
|
||||
|
||||
// 1.渲染模式下,loop 是数组,则按照数组长度渲染组件
|
||||
// 2.设计模式下,loop 需要长度大于 0,按照循环模式渲染,防止无法设计的情况
|
||||
export default function isUseLoop(loop: null | any[] | IPublicTypeJSExpression, isDesignMode: boolean): boolean {
|
||||
if (isJSExpression(loop)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isDesignMode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Array.isArray(loop)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return loop.length > 0;
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
import { Logger } from '@alilc/lowcode-utils';
|
||||
|
||||
export default new Logger({ level: 'warn', bizName: 'renderer' });
|
||||
@ -1,198 +0,0 @@
|
||||
import 'whatwg-fetch';
|
||||
import fetchJsonp from 'fetch-jsonp';
|
||||
import { serializeParams } from '.';
|
||||
|
||||
/**
|
||||
* this is a private method, export for testing purposes only.
|
||||
*
|
||||
* @export
|
||||
* @param {*} dataAPI
|
||||
* @param {*} params
|
||||
* @returns
|
||||
*/
|
||||
export function buildUrl(dataAPI: any, params: any) {
|
||||
const paramStr = serializeParams(params);
|
||||
if (paramStr) {
|
||||
return dataAPI.indexOf('?') > 0 ? `${dataAPI}&${paramStr}` : `${dataAPI}?${paramStr}`;
|
||||
}
|
||||
return dataAPI;
|
||||
}
|
||||
|
||||
/**
|
||||
* do Get request
|
||||
*
|
||||
* @export
|
||||
* @param {*} dataAPI
|
||||
* @param {*} [params={}]
|
||||
* @param {*} [headers={}]
|
||||
* @param {*} [otherProps={}]
|
||||
* @returns
|
||||
*/
|
||||
export function get(dataAPI: any, params = {}, headers = {}, otherProps = {}) {
|
||||
const processedHeaders = {
|
||||
Accept: 'application/json',
|
||||
...headers,
|
||||
};
|
||||
const url = buildUrl(dataAPI, params);
|
||||
return request(url, 'GET', null, processedHeaders, otherProps);
|
||||
}
|
||||
|
||||
/**
|
||||
* do Post request
|
||||
*
|
||||
* @export
|
||||
* @param {*} dataAPI
|
||||
* @param {*} [params={}]
|
||||
* @param {*} [headers={}]
|
||||
* @param {*} [otherProps={}]
|
||||
* @returns
|
||||
*/
|
||||
export function post(dataAPI: any, params = {}, headers: any = {}, otherProps = {}) {
|
||||
const processedHeaders = {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
...headers,
|
||||
};
|
||||
const body = processedHeaders['Content-Type'].indexOf('application/json') > -1 || Array.isArray(params)
|
||||
? JSON.stringify(params)
|
||||
: serializeParams(params);
|
||||
|
||||
return request(
|
||||
dataAPI,
|
||||
'POST',
|
||||
body,
|
||||
processedHeaders,
|
||||
otherProps,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* do request
|
||||
*
|
||||
* @export
|
||||
* @param {*} dataAPI
|
||||
* @param {string} [method='GET']
|
||||
* @param {*} data
|
||||
* @param {*} [headers={}]
|
||||
* @param {*} [otherProps={}]
|
||||
* @returns
|
||||
*/
|
||||
export function request(dataAPI: any, method = 'GET', data: any, headers = {}, otherProps: any = {}) {
|
||||
let processedHeaders = headers || {};
|
||||
let payload = data;
|
||||
if (method === 'PUT' || method === 'DELETE') {
|
||||
processedHeaders = {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
...processedHeaders,
|
||||
};
|
||||
payload = JSON.stringify(payload || {});
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
if (otherProps.timeout) {
|
||||
setTimeout(() => {
|
||||
reject(new Error('timeout'));
|
||||
}, otherProps.timeout);
|
||||
}
|
||||
fetch(dataAPI, {
|
||||
method,
|
||||
credentials: 'include',
|
||||
headers: processedHeaders,
|
||||
body: payload,
|
||||
...otherProps,
|
||||
})
|
||||
.then((response) => {
|
||||
switch (response.status) {
|
||||
case 200:
|
||||
case 201:
|
||||
case 202:
|
||||
return response.json();
|
||||
case 204:
|
||||
if (method === 'DELETE') {
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
__success: false,
|
||||
code: response.status,
|
||||
};
|
||||
}
|
||||
case 400:
|
||||
case 401:
|
||||
case 403:
|
||||
case 404:
|
||||
case 406:
|
||||
case 410:
|
||||
case 422:
|
||||
case 500:
|
||||
return response
|
||||
.json()
|
||||
.then((res) => {
|
||||
return {
|
||||
__success: false,
|
||||
code: response.status,
|
||||
data: res,
|
||||
};
|
||||
})
|
||||
.catch(() => {
|
||||
return {
|
||||
__success: false,
|
||||
code: response.status,
|
||||
};
|
||||
});
|
||||
default:
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.then((json) => {
|
||||
if (!json) {
|
||||
reject(json);
|
||||
return;
|
||||
}
|
||||
if (json.__success !== false) {
|
||||
resolve(json);
|
||||
} else {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
delete json.__success;
|
||||
reject(json);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* do jsonp request
|
||||
*
|
||||
* @export
|
||||
* @param {*} dataAPI
|
||||
* @param {*} [params={}]
|
||||
* @param {*} [otherProps={}]
|
||||
* @returns
|
||||
*/
|
||||
export function jsonp(dataAPI: any, params = {}, otherProps = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const processedOtherProps = {
|
||||
timeout: 5000,
|
||||
...otherProps,
|
||||
};
|
||||
const url = buildUrl(dataAPI, params);
|
||||
fetchJsonp(url, processedOtherProps)
|
||||
.then((response) => {
|
||||
response.json();
|
||||
})
|
||||
.then((json) => {
|
||||
if (json) {
|
||||
resolve(json);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1,101 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import adapter, { Env } from '../../src/adapter';
|
||||
|
||||
|
||||
|
||||
describe('test src/adapter ', () => {
|
||||
|
||||
it('adapter basic use works', () => {
|
||||
expect(adapter).toBeTruthy();
|
||||
|
||||
});
|
||||
|
||||
it('isValidRuntime works', () => {
|
||||
expect(adapter.isValidRuntime([] as any)).toBeFalsy();
|
||||
|
||||
expect(adapter.isValidRuntime('' as any)).toBeFalsy();
|
||||
|
||||
let invalidRuntime = {};
|
||||
expect(() => adapter.isValidRuntime(invalidRuntime as any)).toThrowError(/Component/);
|
||||
invalidRuntime = {
|
||||
Component: {},
|
||||
};
|
||||
expect(() => adapter.isValidRuntime(invalidRuntime as any)).toThrowError(/PureComponent/);
|
||||
invalidRuntime = {
|
||||
Component: {},
|
||||
PureComponent: {},
|
||||
};
|
||||
expect(() => adapter.isValidRuntime(invalidRuntime as any)).toThrowError(/createElement/);
|
||||
invalidRuntime = {
|
||||
Component: {},
|
||||
PureComponent: {},
|
||||
createElement: {},
|
||||
};
|
||||
expect(() => adapter.isValidRuntime(invalidRuntime as any)).toThrowError(/createContext/);
|
||||
invalidRuntime = {
|
||||
Component: {},
|
||||
PureComponent: {},
|
||||
createElement: {},
|
||||
createContext: {},
|
||||
};
|
||||
expect(() => adapter.isValidRuntime(invalidRuntime as any)).toThrowError(/forwardRef/);
|
||||
invalidRuntime = {
|
||||
Component: {},
|
||||
PureComponent: {},
|
||||
createElement: {},
|
||||
createContext: {},
|
||||
forwardRef: {},
|
||||
};
|
||||
expect(() => adapter.isValidRuntime(invalidRuntime as any)).toThrowError(/findDOMNode/);
|
||||
const validRuntime = {
|
||||
Component: {},
|
||||
PureComponent: {},
|
||||
createElement: {},
|
||||
createContext: {},
|
||||
forwardRef: {},
|
||||
findDOMNode: {},
|
||||
};
|
||||
|
||||
expect(adapter.isValidRuntime(validRuntime as any)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('setRuntime/getRuntime works', () => {
|
||||
const validRuntime = {
|
||||
Component: {},
|
||||
PureComponent: {},
|
||||
createElement: {},
|
||||
createContext: {},
|
||||
forwardRef: {},
|
||||
findDOMNode: {},
|
||||
};
|
||||
|
||||
adapter.setRuntime(validRuntime as any);
|
||||
expect(adapter.getRuntime()).toBe(validRuntime);
|
||||
|
||||
// won`t work when invalid runtime paased in.
|
||||
adapter.setRuntime([] as any);
|
||||
expect(adapter.getRuntime()).toBe(validRuntime);
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('setEnv/.env/isReact works', () => {
|
||||
adapter.setEnv(Env.React);
|
||||
expect(adapter.env).toBe(Env.React);
|
||||
expect(adapter.isReact()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('setRenderers/getRenderers works', () => {
|
||||
const mockRenderers = { BaseRenderer: {} as IBaseRenderComponent};
|
||||
adapter.setRenderers(mockRenderers);
|
||||
expect(adapter.getRenderers()).toBe(mockRenderers);
|
||||
adapter.setRenderers(undefined);
|
||||
expect(adapter.getRenderers()).toStrictEqual({});
|
||||
});
|
||||
|
||||
it('setConfigProvider/getConfigProvider works', () => {
|
||||
const mockConfigProvider = { a: 111 };
|
||||
adapter.setConfigProvider(mockConfigProvider);
|
||||
expect(adapter.getConfigProvider()).toBe(mockConfigProvider);
|
||||
});
|
||||
});
|
||||
@ -1,567 +0,0 @@
|
||||
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',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -1,7 +0,0 @@
|
||||
if (!process.env.LISTENING_TO_UNHANDLED_REJECTION) {
|
||||
process.on('unhandledRejection', reason => {
|
||||
throw reason;
|
||||
});
|
||||
// Avoid memory leak by adding too many listeners
|
||||
process.env.LISTENING_TO_UNHANDLED_REJECTION = 'true';
|
||||
}
|
||||
@ -1,148 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`children this.props.children is array 1`] = `
|
||||
<div>
|
||||
<div
|
||||
content="content"
|
||||
>
|
||||
content
|
||||
</div>
|
||||
<div
|
||||
content="content"
|
||||
>
|
||||
content
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`lifecycle leaf change and make componentWillReceiveProps 1`] = `
|
||||
<div>
|
||||
<div
|
||||
__id="text6"
|
||||
__tag="222"
|
||||
componentId="text6"
|
||||
content="content new leaf"
|
||||
>
|
||||
content new leaf
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`lifecycle props change and make componentWillReceiveProps 1`] = `
|
||||
<div>
|
||||
<div
|
||||
content="content"
|
||||
>
|
||||
content
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`lifecycle props change and make componentWillReceiveProps 2`] = `
|
||||
<div>
|
||||
<div
|
||||
content="content 123"
|
||||
>
|
||||
content 123
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`lifecycle props change and make componentWillReceiveProps 3`] = `
|
||||
<div>
|
||||
<div
|
||||
__tag="111"
|
||||
content="content 123"
|
||||
>
|
||||
content 123
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`mini unit render leaf has a loop, render from parent 1`] = `
|
||||
<div>
|
||||
this is a new children
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`mini unit render make text props change 1`] = `
|
||||
<div>
|
||||
<div
|
||||
content="content"
|
||||
>
|
||||
content
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`mini unit render make text props change 2`] = `
|
||||
<div
|
||||
newPropKey="newPropValue"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`mini unit render parent is a mock leaf 1`] = `
|
||||
<div>
|
||||
<div
|
||||
content="new content to mock"
|
||||
>
|
||||
new content to mock
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`mini unit render props has new children 1`] = `
|
||||
<div>
|
||||
children 01
|
||||
children 02
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`onChildrenChange children is array string 1`] = `
|
||||
<div>
|
||||
onChildrenChange content 01
|
||||
onChildrenChange content 02
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`onPropChange change textNode [key:___condition___] props, but not hidden component 1`] = `
|
||||
<div>
|
||||
<div
|
||||
content="content"
|
||||
>
|
||||
content
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`onPropChange change textNode [key:___condition___] props, hide textNode component 1`] = `<div />`;
|
||||
|
||||
exports[`onPropChange change textNode [key:content], content in this.props but not in leaf.export result 1`] = `
|
||||
<div>
|
||||
<div
|
||||
content="content"
|
||||
>
|
||||
content
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`onPropChange change textNode [key:content], content in this.props but not in leaf.export result 2`] = `
|
||||
<div>
|
||||
<div
|
||||
content={null}
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`onVisibleChange visible is false 1`] = `<div />`;
|
||||
|
||||
exports[`onVisibleChange visible is true 1`] = `
|
||||
<div>
|
||||
<div
|
||||
content="content"
|
||||
>
|
||||
content
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@ -1,604 +0,0 @@
|
||||
import renderer from 'react-test-renderer';
|
||||
import React from 'react';
|
||||
import { createElement } from 'react';
|
||||
import '../utils/react-env-init';
|
||||
import { leafWrapper } from '../../src/hoc/leaf';
|
||||
import components from '../utils/components';
|
||||
import Node from '../utils/node';
|
||||
import { parseData } from '../../src/utils';
|
||||
|
||||
let rerenderCount = 0;
|
||||
|
||||
const nodeMap = new Map();
|
||||
|
||||
const makeSnapshot = (component) => {
|
||||
let tree = component.toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
}
|
||||
|
||||
const baseRenderer: any = {
|
||||
__debug () {},
|
||||
__getComponentProps (schema: any) {
|
||||
return schema.props;
|
||||
},
|
||||
__getSchemaChildrenVirtualDom (schema: any) {
|
||||
return schema.children;
|
||||
},
|
||||
context: {
|
||||
engine: {
|
||||
createElement,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
__host: {},
|
||||
getNode: (id) => nodeMap.get(id),
|
||||
__container: {
|
||||
rerender: () => {
|
||||
rerenderCount = 1 + rerenderCount;
|
||||
},
|
||||
autoRepaintNode: true,
|
||||
},
|
||||
documentId: '01'
|
||||
},
|
||||
__parseData (data, scope) {
|
||||
return parseData(data, scope, {});
|
||||
}
|
||||
}
|
||||
|
||||
let Div, DivNode, Text, TextNode, component, textSchema, divSchema;
|
||||
let id = 0;
|
||||
|
||||
beforeEach(() => {
|
||||
textSchema = {
|
||||
id: 'text' + id,
|
||||
props: {
|
||||
content: 'content'
|
||||
},
|
||||
};
|
||||
|
||||
divSchema = {
|
||||
id: 'div' + id,
|
||||
};
|
||||
|
||||
id++;
|
||||
|
||||
Div = leafWrapper(components.Div as any, {
|
||||
schema: divSchema,
|
||||
baseRenderer,
|
||||
componentInfo: {},
|
||||
scope: {},
|
||||
});
|
||||
|
||||
DivNode = new Node(divSchema);
|
||||
TextNode = new Node(textSchema);
|
||||
|
||||
nodeMap.set(divSchema.id, DivNode);
|
||||
nodeMap.set(textSchema.id, TextNode);
|
||||
|
||||
Text = leafWrapper(components.Text as any, {
|
||||
schema: textSchema,
|
||||
baseRenderer,
|
||||
componentInfo: {},
|
||||
scope: {},
|
||||
});
|
||||
|
||||
component = renderer.create(
|
||||
<Div _leaf={DivNode}>
|
||||
<Text _leaf={TextNode} content="content"></Text>
|
||||
</Div>
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
component.unmount(component);
|
||||
});
|
||||
|
||||
describe('onPropChange', () => {
|
||||
it('change textNode [key:content] props', () => {
|
||||
TextNode.emitPropChange({
|
||||
key: 'content',
|
||||
newValue: 'new content',
|
||||
} as any);
|
||||
|
||||
const root = component.root;
|
||||
expect(root.findByType(components.Text).props.content).toEqual('new content')
|
||||
});
|
||||
|
||||
it('change textNode [key:___condition___] props, hide textNode component', () => {
|
||||
// mock leaf?.export result
|
||||
TextNode.schema.condition = false;
|
||||
TextNode.emitPropChange({
|
||||
key: '___condition___',
|
||||
newValue: false,
|
||||
} as any);
|
||||
|
||||
makeSnapshot(component);
|
||||
});
|
||||
|
||||
it('change textNode [key:___condition___] props, but not hidden component', () => {
|
||||
TextNode.schema.condition = true;
|
||||
TextNode.emitPropChange({
|
||||
key: '___condition___',
|
||||
newValue: false,
|
||||
} as any);
|
||||
|
||||
makeSnapshot(component);
|
||||
});
|
||||
|
||||
it('change textNode [key:content], content in this.props but not in leaf.export result', () => {
|
||||
makeSnapshot(component);
|
||||
|
||||
delete TextNode.schema.props.content;
|
||||
TextNode.emitPropChange({
|
||||
key: 'content',
|
||||
newValue: null,
|
||||
} as any, true);
|
||||
|
||||
makeSnapshot(component);
|
||||
|
||||
const root = component.root;
|
||||
|
||||
const TextInst = root.findByType(components.Text);
|
||||
|
||||
expect(TextInst.props.content).toBeNull();
|
||||
});
|
||||
|
||||
it('change textNode [key:___loop___], make rerender', () => {
|
||||
expect(leafWrapper(components.Text as any, {
|
||||
schema: textSchema,
|
||||
baseRenderer,
|
||||
componentInfo: {},
|
||||
scope: {},
|
||||
})).toEqual(Text);
|
||||
|
||||
const nextRerenderCount = rerenderCount + 1;
|
||||
|
||||
TextNode.emitPropChange({
|
||||
key: '___loop___',
|
||||
newValue: 'new content',
|
||||
} as any);
|
||||
|
||||
expect(rerenderCount).toBe(nextRerenderCount);
|
||||
expect(leafWrapper(components.Text as any, {
|
||||
schema: textSchema,
|
||||
baseRenderer,
|
||||
componentInfo: {},
|
||||
scope: {},
|
||||
})).not.toEqual(Text);
|
||||
});
|
||||
});
|
||||
|
||||
describe('lifecycle', () => {
|
||||
it('props change and make componentWillReceiveProps', () => {
|
||||
makeSnapshot(component);
|
||||
|
||||
// 没有 __tag 标识
|
||||
component.update((
|
||||
<Div _leaf={DivNode}>
|
||||
<Text _leaf={TextNode} content="content 123"></Text>
|
||||
</Div>
|
||||
));
|
||||
|
||||
makeSnapshot(component);
|
||||
|
||||
// 有 __tag 标识
|
||||
component.update((
|
||||
<Div _leaf={DivNode}>
|
||||
<Text _leaf={TextNode} __tag="111" content="content 123"></Text>
|
||||
</Div>
|
||||
));
|
||||
|
||||
makeSnapshot(component);
|
||||
});
|
||||
|
||||
it('leaf change and make componentWillReceiveProps', () => {
|
||||
const newTextNodeLeaf = new Node(textSchema);
|
||||
nodeMap.set(textSchema.id, newTextNodeLeaf);
|
||||
component.update((
|
||||
<Div _leaf={DivNode}>
|
||||
<Text componentId={textSchema.id} __tag="222" content="content 123"></Text>
|
||||
</Div>
|
||||
));
|
||||
|
||||
newTextNodeLeaf.emitPropChange({
|
||||
key: 'content',
|
||||
newValue: 'content new leaf',
|
||||
});
|
||||
|
||||
makeSnapshot(component);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mini unit render', () => {
|
||||
let miniRenderSchema, MiniRenderDiv, MiniRenderDivNode;
|
||||
beforeEach(() => {
|
||||
miniRenderSchema = {
|
||||
id: 'miniDiv' + id,
|
||||
};
|
||||
|
||||
MiniRenderDiv = leafWrapper(components.MiniRenderDiv as any, {
|
||||
schema: miniRenderSchema,
|
||||
baseRenderer,
|
||||
componentInfo: {},
|
||||
scope: {},
|
||||
});
|
||||
|
||||
MiniRenderDivNode = new Node(miniRenderSchema, {
|
||||
componentMeta: {
|
||||
isMinimalRenderUnit: true,
|
||||
},
|
||||
});
|
||||
|
||||
TextNode = new Node(textSchema, {
|
||||
parent: MiniRenderDivNode,
|
||||
});
|
||||
|
||||
nodeMap.set(miniRenderSchema.id, MiniRenderDivNode);
|
||||
nodeMap.set(textSchema.id, TextNode);
|
||||
|
||||
component = renderer.create(
|
||||
<MiniRenderDiv _leaf={MiniRenderDivNode}>
|
||||
<Text _leaf={TextNode} content="content"></Text>
|
||||
</MiniRenderDiv>
|
||||
);
|
||||
})
|
||||
|
||||
it('make text props change', () => {
|
||||
if (!MiniRenderDivNode.schema.props) {
|
||||
MiniRenderDivNode.schema.props = {};
|
||||
}
|
||||
MiniRenderDivNode.schema.props['newPropKey'] = 'newPropValue';
|
||||
|
||||
makeSnapshot(component);
|
||||
|
||||
const inst = component.root;
|
||||
|
||||
const TextInst = inst.findByType(Text).children[0];
|
||||
|
||||
TextNode.emitPropChange({
|
||||
key: 'content',
|
||||
newValue: 'new content',
|
||||
} as any);
|
||||
|
||||
expect((TextInst as any)?._fiber.stateNode.renderUnitInfo).toEqual({
|
||||
singleRender: false,
|
||||
minimalUnitId: 'miniDiv' + id,
|
||||
minimalUnitName: undefined,
|
||||
});
|
||||
|
||||
makeSnapshot(component);
|
||||
});
|
||||
|
||||
it('dont render mini render component', () => {
|
||||
const TextNode = new Node(textSchema, {
|
||||
parent: new Node({
|
||||
id: 'random',
|
||||
}, {
|
||||
componentMeta: {
|
||||
isMinimalRenderUnit: true,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
nodeMap.set(textSchema.id, TextNode);
|
||||
|
||||
renderer.create(
|
||||
<div>
|
||||
<Text _leaf={TextNode} content="content"></Text>
|
||||
</div>
|
||||
);
|
||||
|
||||
const nextCount = rerenderCount + 1;
|
||||
|
||||
TextNode.emitPropChange({
|
||||
key: 'content',
|
||||
newValue: 'new content',
|
||||
} as any);
|
||||
|
||||
expect(rerenderCount).toBe(nextCount);
|
||||
});
|
||||
|
||||
it('leaf is a mock function', () => {
|
||||
const TextNode = new Node(textSchema, {
|
||||
parent: {
|
||||
isEmpty: () => false,
|
||||
}
|
||||
});
|
||||
|
||||
renderer.create(
|
||||
<div>
|
||||
<Text _leaf={TextNode} content="content"></Text>
|
||||
</div>
|
||||
);
|
||||
|
||||
TextNode.emitPropChange({
|
||||
key: 'content',
|
||||
newValue: 'new content',
|
||||
} as any);
|
||||
});
|
||||
|
||||
it('change component leaf isRoot is true', () => {
|
||||
const TextNode = new Node(textSchema, {
|
||||
isRoot: true,
|
||||
isRootNode: true,
|
||||
});
|
||||
|
||||
nodeMap.set(textSchema.id, TextNode);
|
||||
|
||||
const component = renderer.create(
|
||||
<Text _leaf={TextNode} content="content"></Text>
|
||||
);
|
||||
|
||||
const inst = component.root;
|
||||
|
||||
TextNode.emitPropChange({
|
||||
key: 'content',
|
||||
newValue: 'new content',
|
||||
} as any);
|
||||
|
||||
expect((inst.children[0] as any)?._fiber.stateNode.renderUnitInfo).toEqual({
|
||||
singleRender: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('change component leaf parent isRoot is true', () => {
|
||||
const TextNode = new Node(textSchema, {
|
||||
parent: new Node({
|
||||
id: 'first-parent',
|
||||
}, {
|
||||
componentMeta: {
|
||||
isMinimalRenderUnit: true,
|
||||
},
|
||||
parent: new Node({
|
||||
id: 'rootId',
|
||||
}, {
|
||||
isRoot: true,
|
||||
isRootNode: true
|
||||
}),
|
||||
})
|
||||
});
|
||||
|
||||
nodeMap.set(textSchema.id, TextNode);
|
||||
|
||||
const component = renderer.create(
|
||||
<Text _leaf={TextNode} content="content"></Text>
|
||||
);
|
||||
|
||||
const inst = component.root;
|
||||
|
||||
TextNode.emitPropChange({
|
||||
key: 'content',
|
||||
newValue: 'new content',
|
||||
} as any);
|
||||
|
||||
expect((inst.children[0] as any)?._fiber.stateNode.renderUnitInfo).toEqual({
|
||||
singleRender: false,
|
||||
minimalUnitId: 'first-parent',
|
||||
minimalUnitName: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('parent is a mock leaf', () => {
|
||||
const MiniRenderDivNode = {
|
||||
isMock: true,
|
||||
};
|
||||
|
||||
const component = renderer.create(
|
||||
<MiniRenderDiv _leaf={MiniRenderDivNode}>
|
||||
<Text _leaf={TextNode} content="content"></Text>
|
||||
</MiniRenderDiv>
|
||||
);
|
||||
|
||||
TextNode.emitPropChange({
|
||||
key: 'content',
|
||||
newValue: 'new content to mock',
|
||||
} as any);
|
||||
|
||||
makeSnapshot(component);
|
||||
});
|
||||
|
||||
it('props has new children', () => {
|
||||
MiniRenderDivNode.schema.props.children = [
|
||||
'children 01',
|
||||
'children 02',
|
||||
];
|
||||
|
||||
TextNode.emitPropChange({
|
||||
key: 'content',
|
||||
newValue: 'props'
|
||||
});
|
||||
|
||||
makeSnapshot(component);
|
||||
});
|
||||
|
||||
it('leaf has a loop, render from parent', () => {
|
||||
MiniRenderDivNode = new Node(miniRenderSchema, {});
|
||||
|
||||
TextNode = new Node(textSchema, {
|
||||
parent: MiniRenderDivNode,
|
||||
hasLoop: true,
|
||||
});
|
||||
|
||||
nodeMap.set(textSchema.id, TextNode);
|
||||
nodeMap.set(miniRenderSchema.id, MiniRenderDivNode);
|
||||
|
||||
component = renderer.create(
|
||||
<MiniRenderDiv _leaf={MiniRenderDivNode}>
|
||||
<Text _leaf={TextNode} content="content"></Text>
|
||||
</MiniRenderDiv>
|
||||
);
|
||||
|
||||
MiniRenderDivNode.schema.children = ['this is a new children'];
|
||||
|
||||
TextNode.emitPropChange({
|
||||
key: 'content',
|
||||
newValue: '1',
|
||||
});
|
||||
|
||||
makeSnapshot(component);
|
||||
});
|
||||
});
|
||||
|
||||
describe('component cache', () => {
|
||||
it('get different component with same is and different doc id', () => {
|
||||
const baseRenderer02 = {
|
||||
...baseRenderer,
|
||||
props: {
|
||||
...baseRenderer.props,
|
||||
documentId: '02',
|
||||
}
|
||||
}
|
||||
const Div3 = leafWrapper(components.Div as any, {
|
||||
schema: divSchema,
|
||||
baseRenderer: baseRenderer02,
|
||||
componentInfo: {},
|
||||
scope: {},
|
||||
});
|
||||
|
||||
expect(Div).not.toEqual(Div3);
|
||||
});
|
||||
|
||||
it('get component again and get ths cache component', () => {
|
||||
const Div2 = leafWrapper(components.Div as any, {
|
||||
schema: divSchema,
|
||||
baseRenderer,
|
||||
componentInfo: {},
|
||||
scope: {},
|
||||
});
|
||||
|
||||
expect(Div).toEqual(Div2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onVisibleChange', () => {
|
||||
it('visible is false', () => {
|
||||
TextNode.emitVisibleChange(false);
|
||||
makeSnapshot(component);
|
||||
});
|
||||
|
||||
it('visible is true', () => {
|
||||
TextNode.emitVisibleChange(true);
|
||||
makeSnapshot(component);
|
||||
});
|
||||
});
|
||||
|
||||
describe('children', () => {
|
||||
it('this.props.children is array', () => {
|
||||
const component = renderer.create(
|
||||
<Div _leaf={DivNode}>
|
||||
<Text _leaf={TextNode} content="content"></Text>
|
||||
<Text _leaf={TextNode} content="content"></Text>
|
||||
</Div>
|
||||
);
|
||||
|
||||
makeSnapshot(component);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onChildrenChange', () => {
|
||||
it('children is array string', () => {
|
||||
DivNode.schema.children = [
|
||||
'onChildrenChange content 01',
|
||||
'onChildrenChange content 02'
|
||||
]
|
||||
DivNode.emitChildrenChange();
|
||||
makeSnapshot(component);
|
||||
});
|
||||
|
||||
it('children is 0', () => {
|
||||
DivNode.schema.children = 0
|
||||
DivNode.emitChildrenChange();
|
||||
const componentInstance = component.root;
|
||||
expect(componentInstance.findByType(components.Div).props.children).toEqual(0);
|
||||
});
|
||||
|
||||
it('children is false', () => {
|
||||
DivNode.schema.children = false
|
||||
DivNode.emitChildrenChange();
|
||||
const componentInstance = component.root;
|
||||
expect(componentInstance.findByType(components.Div).props.children).toEqual(false);
|
||||
});
|
||||
|
||||
it('children is []', () => {
|
||||
DivNode.schema.children = []
|
||||
DivNode.emitChildrenChange();
|
||||
const componentInstance = component.root;
|
||||
expect(componentInstance.findByType(components.Div).props.children).toEqual([]);
|
||||
});
|
||||
|
||||
it('children is null', () => {
|
||||
DivNode.schema.children = null
|
||||
DivNode.emitChildrenChange();
|
||||
const componentInstance = component.root;
|
||||
expect(componentInstance.findByType(components.Div).props.children).toEqual(null);
|
||||
});
|
||||
|
||||
it('children is undefined', () => {
|
||||
DivNode.schema.children = undefined;
|
||||
DivNode.emitChildrenChange();
|
||||
const componentInstance = component.root;
|
||||
expect(componentInstance.findByType(components.Div).props.children).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('not render leaf', () => {
|
||||
let miniRenderSchema, MiniRenderDiv, MiniRenderDivNode;
|
||||
beforeEach(() => {
|
||||
miniRenderSchema = {
|
||||
id: 'miniDiv' + id,
|
||||
};
|
||||
|
||||
MiniRenderDivNode = new Node(miniRenderSchema, {
|
||||
componentMeta: {
|
||||
isMinimalRenderUnit: true,
|
||||
},
|
||||
});
|
||||
|
||||
nodeMap.set(miniRenderSchema.id, MiniRenderDivNode);
|
||||
|
||||
MiniRenderDiv = leafWrapper(components.MiniRenderDiv as any, {
|
||||
schema: miniRenderSchema,
|
||||
baseRenderer,
|
||||
componentInfo: {},
|
||||
scope: {},
|
||||
});
|
||||
|
||||
TextNode = new Node(textSchema, {
|
||||
parent: MiniRenderDivNode,
|
||||
});
|
||||
|
||||
component = renderer.create(
|
||||
<Text _leaf={TextNode} content="content"></Text>
|
||||
);
|
||||
});
|
||||
|
||||
it('onPropsChange', () => {
|
||||
const nextCount = rerenderCount + 1;
|
||||
|
||||
MiniRenderDivNode.emitPropChange({
|
||||
key: 'any',
|
||||
newValue: 'any',
|
||||
});
|
||||
|
||||
expect(rerenderCount).toBe(nextCount);
|
||||
});
|
||||
|
||||
it('onChildrenChange', () => {
|
||||
const nextCount = rerenderCount + 1;
|
||||
|
||||
MiniRenderDivNode.emitChildrenChange({
|
||||
key: 'any',
|
||||
newValue: 'any',
|
||||
});
|
||||
|
||||
expect(rerenderCount).toBe(nextCount);
|
||||
});
|
||||
|
||||
it('onVisibleChange', () => {
|
||||
const nextCount = rerenderCount + 1;
|
||||
|
||||
MiniRenderDivNode.emitVisibleChange(true);
|
||||
|
||||
expect(rerenderCount).toBe(nextCount);
|
||||
});
|
||||
});
|
||||
@ -1,221 +0,0 @@
|
||||
const schema = {
|
||||
"componentName": "Page",
|
||||
"id": "node_ocl1djd9o41",
|
||||
"docId": "docl1djd9o4",
|
||||
"props": {
|
||||
"templateVersion": "1.0.0",
|
||||
"containerStyle": {},
|
||||
"pageStyle": {
|
||||
"backgroundColor": "#f2f3f5"
|
||||
},
|
||||
"className": "_css_pseudo_node_ocl1djd9o41"
|
||||
},
|
||||
"dataSource": {
|
||||
"offline": [],
|
||||
"globalConfig": {},
|
||||
"online": [
|
||||
{
|
||||
"gmtModified": 1639385418000,
|
||||
"initialData": "",
|
||||
"globalUid": "AY866BC1ERSVK0BE55NU364515LH3NM0RF4XK61",
|
||||
"formUuid": "FORM-3KYJN7RV-J47BPFK63W2PHAGPO1VC3-B4H1WE5K-131",
|
||||
"name": "locale",
|
||||
"description": "当前语种(在 window.g_config 中设置)",
|
||||
"id": "AY866BC1ERSVK0BE55NU364515LH3NM0RF4XK61",
|
||||
"protocal": "VALUE",
|
||||
"shareType": "APP"
|
||||
},
|
||||
{
|
||||
"gmtModified": 1639385418000,
|
||||
"initialData": "",
|
||||
"globalUid": "AY866BC1ERSVK0BE55NU364515LH3SM0RF4XK71",
|
||||
"formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H",
|
||||
"name": "appType",
|
||||
"description": "应用的唯一 code",
|
||||
"id": "AY866BC1ERSVK0BE55NU364515LH3SM0RF4XK71",
|
||||
"protocal": "VALUE",
|
||||
"shareType": "APP"
|
||||
},
|
||||
{
|
||||
"gmtModified": 1639385418000,
|
||||
"initialData": "",
|
||||
"globalUid": "AY866BC1ERSVK0BE55NU364515LH3XM0RF4XK81",
|
||||
"formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H",
|
||||
"name": "version",
|
||||
"description": "应该版本,默认 0.1.0",
|
||||
"id": "AY866BC1ERSVK0BE55NU364515LH3XM0RF4XK81",
|
||||
"protocal": "VALUE",
|
||||
"shareType": "APP"
|
||||
},
|
||||
{
|
||||
"gmtModified": 1639385418000,
|
||||
"initialData": "",
|
||||
"globalUid": "AY866BC1ERSVK0BE55NU364515LH33N0RF4XK91",
|
||||
"formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H",
|
||||
"name": "apiPrefix",
|
||||
"description": "",
|
||||
"id": "AY866BC1ERSVK0BE55NU364515LH33N0RF4XK91",
|
||||
"protocal": "VALUE",
|
||||
"shareType": "APP"
|
||||
}
|
||||
],
|
||||
"sync": true,
|
||||
"list": [
|
||||
{
|
||||
"gmtModified": 1639385418000,
|
||||
"initialData": "",
|
||||
"globalUid": "AY866BC1ERSVK0BE55NU364515LH3NM0RF4XK61",
|
||||
"formUuid": "FORM-3KYJN7RV-J47BPFK63W2PHAGPO1VC3-B4H1WE5K-131",
|
||||
"name": "locale",
|
||||
"description": "当前语种(在 window.g_config 中设置)",
|
||||
"id": "AY866BC1ERSVK0BE55NU364515LH3NM0RF4XK61",
|
||||
"protocal": "VALUE",
|
||||
"shareType": "APP"
|
||||
},
|
||||
{
|
||||
"gmtModified": 1639385418000,
|
||||
"initialData": "",
|
||||
"globalUid": "AY866BC1ERSVK0BE55NU364515LH3SM0RF4XK71",
|
||||
"formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H",
|
||||
"name": "appType",
|
||||
"description": "应用的唯一 code",
|
||||
"id": "AY866BC1ERSVK0BE55NU364515LH3SM0RF4XK71",
|
||||
"protocal": "VALUE",
|
||||
"shareType": "APP"
|
||||
},
|
||||
{
|
||||
"gmtModified": 1639385418000,
|
||||
"initialData": "",
|
||||
"globalUid": "AY866BC1ERSVK0BE55NU364515LH3XM0RF4XK81",
|
||||
"formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H",
|
||||
"name": "version",
|
||||
"description": "应该版本,默认 0.1.0",
|
||||
"id": "AY866BC1ERSVK0BE55NU364515LH3XM0RF4XK81",
|
||||
"protocal": "VALUE",
|
||||
"shareType": "APP"
|
||||
},
|
||||
{
|
||||
"gmtModified": 1639385418000,
|
||||
"initialData": "",
|
||||
"globalUid": "AY866BC1ERSVK0BE55NU364515LH33N0RF4XK91",
|
||||
"formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H",
|
||||
"name": "apiPrefix",
|
||||
"description": "",
|
||||
"id": "AY866BC1ERSVK0BE55NU364515LH33N0RF4XK91",
|
||||
"protocal": "VALUE",
|
||||
"shareType": "APP"
|
||||
}
|
||||
]
|
||||
},
|
||||
"methods": {},
|
||||
"hidden": false,
|
||||
"title": "",
|
||||
"isLocked": false,
|
||||
"condition": true,
|
||||
"conditionGroup": "",
|
||||
"children": [
|
||||
{
|
||||
"componentName": "RootHeader",
|
||||
"id": "node_ocl1djd9o42",
|
||||
"docId": "docl1djd9o4",
|
||||
"props": {},
|
||||
"hidden": false,
|
||||
"title": "",
|
||||
"isLocked": false,
|
||||
"condition": true,
|
||||
"conditionGroup": ""
|
||||
},
|
||||
{
|
||||
"componentName": "RootContent",
|
||||
"id": "node_ocl1djd9o43",
|
||||
"docId": "docl1djd9o4",
|
||||
"props": {
|
||||
"contentMargin": "20",
|
||||
"contentPadding": "20",
|
||||
"contentBgColor": "white"
|
||||
},
|
||||
"hidden": false,
|
||||
"title": "",
|
||||
"isLocked": false,
|
||||
"condition": true,
|
||||
"conditionGroup": "",
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Div",
|
||||
"id": "node_ocl1djd9o45",
|
||||
"docId": "docl1djd9o4",
|
||||
"props": {
|
||||
"behavior": "NORMAL",
|
||||
"__style__": {},
|
||||
"fieldId": "div_l1djdj1n",
|
||||
"events": {
|
||||
"ignored": true
|
||||
},
|
||||
"useFieldIdAsDomId": false,
|
||||
"customClassName": "",
|
||||
"className": "_css_pseudo_node_ocl1djd9o45"
|
||||
},
|
||||
"hidden": false,
|
||||
"title": "",
|
||||
"isLocked": false,
|
||||
"condition": true,
|
||||
"conditionGroup": "",
|
||||
"loop": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"loopArgs": [
|
||||
null,
|
||||
null
|
||||
],
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Div",
|
||||
"id": "node_ocl1djd9o46",
|
||||
"docId": "docl1djd9o4",
|
||||
"props": {
|
||||
"behavior": "NORMAL",
|
||||
"__style__": {},
|
||||
"fieldId": "div_l1djdj1o",
|
||||
"events": {
|
||||
"ignored": true
|
||||
},
|
||||
"useFieldIdAsDomId": false,
|
||||
"customClassName": "",
|
||||
"className": "_css_pseudo_node_ocl1djd9o46"
|
||||
},
|
||||
"hidden": false,
|
||||
"title": "",
|
||||
"isLocked": false,
|
||||
"condition": true,
|
||||
"conditionGroup": "",
|
||||
"loop": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"loopArgs": [
|
||||
null,
|
||||
null
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "RootFooter",
|
||||
"id": "node_ocl1djd9o44",
|
||||
"docId": "docl1djd9o4",
|
||||
"props": {},
|
||||
"hidden": false,
|
||||
"title": "",
|
||||
"isLocked": false,
|
||||
"condition": true,
|
||||
"conditionGroup": ""
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default schema;
|
||||
@ -1,184 +0,0 @@
|
||||
export const sampleSchema = {
|
||||
"componentName": "Page",
|
||||
"id": "node_ockyigdqxl1",
|
||||
"docId": "dockyigdqxl",
|
||||
"props": {
|
||||
"templateVersion": "1.0.0",
|
||||
"containerStyle": {},
|
||||
"pageStyle": {
|
||||
"backgroundColor": "#f2f3f5"
|
||||
},
|
||||
"className": "_css_pseudo_node_ockyigdqxl1"
|
||||
},
|
||||
"dataSource": {
|
||||
"offline": [],
|
||||
"globalConfig": {},
|
||||
"online": [
|
||||
{
|
||||
"gmtModified": 1639385418000,
|
||||
"initialData": "",
|
||||
"globalUid": "AY866BC1ERSVK0BE55NU364515LH3NM0RF4XK61",
|
||||
"formUuid": "FORM-3KYJN7RV-J47BPFK63W2PHAGPO1VC3-B4H1WE5K-131",
|
||||
"name": "locale",
|
||||
"description": "当前语种(在 window.g_config 中设置)",
|
||||
"id": "AY866BC1ERSVK0BE55NU364515LH3NM0RF4XK61",
|
||||
"protocal": "VALUE",
|
||||
"shareType": "APP"
|
||||
},
|
||||
{
|
||||
"gmtModified": 1639385418000,
|
||||
"initialData": "",
|
||||
"globalUid": "AY866BC1ERSVK0BE55NU364515LH3SM0RF4XK71",
|
||||
"formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H",
|
||||
"name": "appType",
|
||||
"description": "应用的唯一 code",
|
||||
"id": "AY866BC1ERSVK0BE55NU364515LH3SM0RF4XK71",
|
||||
"protocal": "VALUE",
|
||||
"shareType": "APP"
|
||||
},
|
||||
{
|
||||
"gmtModified": 1639385418000,
|
||||
"initialData": "",
|
||||
"globalUid": "AY866BC1ERSVK0BE55NU364515LH3XM0RF4XK81",
|
||||
"formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H",
|
||||
"name": "version",
|
||||
"description": "应该版本,默认 0.1.0",
|
||||
"id": "AY866BC1ERSVK0BE55NU364515LH3XM0RF4XK81",
|
||||
"protocal": "VALUE",
|
||||
"shareType": "APP"
|
||||
},
|
||||
{
|
||||
"gmtModified": 1639385418000,
|
||||
"initialData": "",
|
||||
"globalUid": "AY866BC1ERSVK0BE55NU364515LH33N0RF4XK91",
|
||||
"formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H",
|
||||
"name": "apiPrefix",
|
||||
"description": "",
|
||||
"id": "AY866BC1ERSVK0BE55NU364515LH33N0RF4XK91",
|
||||
"protocal": "VALUE",
|
||||
"shareType": "APP"
|
||||
}
|
||||
],
|
||||
"sync": true,
|
||||
"list": [
|
||||
{
|
||||
"gmtModified": 1639385418000,
|
||||
"initialData": "",
|
||||
"globalUid": "AY866BC1ERSVK0BE55NU364515LH3NM0RF4XK61",
|
||||
"formUuid": "FORM-3KYJN7RV-J47BPFK63W2PHAGPO1VC3-B4H1WE5K-131",
|
||||
"name": "locale",
|
||||
"description": "当前语种(在 window.g_config 中设置)",
|
||||
"id": "AY866BC1ERSVK0BE55NU364515LH3NM0RF4XK61",
|
||||
"protocal": "VALUE",
|
||||
"shareType": "APP"
|
||||
},
|
||||
{
|
||||
"gmtModified": 1639385418000,
|
||||
"initialData": "",
|
||||
"globalUid": "AY866BC1ERSVK0BE55NU364515LH3SM0RF4XK71",
|
||||
"formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H",
|
||||
"name": "appType",
|
||||
"description": "应用的唯一 code",
|
||||
"id": "AY866BC1ERSVK0BE55NU364515LH3SM0RF4XK71",
|
||||
"protocal": "VALUE",
|
||||
"shareType": "APP"
|
||||
},
|
||||
{
|
||||
"gmtModified": 1639385418000,
|
||||
"initialData": "",
|
||||
"globalUid": "AY866BC1ERSVK0BE55NU364515LH3XM0RF4XK81",
|
||||
"formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H",
|
||||
"name": "version",
|
||||
"description": "应该版本,默认 0.1.0",
|
||||
"id": "AY866BC1ERSVK0BE55NU364515LH3XM0RF4XK81",
|
||||
"protocal": "VALUE",
|
||||
"shareType": "APP"
|
||||
},
|
||||
{
|
||||
"gmtModified": 1639385418000,
|
||||
"initialData": "",
|
||||
"globalUid": "AY866BC1ERSVK0BE55NU364515LH33N0RF4XK91",
|
||||
"formUuid": "FORM-RFYJTWKV-D47BWO6R0QHA74R062FN2-R5IPXK4K-0H",
|
||||
"name": "apiPrefix",
|
||||
"description": "",
|
||||
"id": "AY866BC1ERSVK0BE55NU364515LH33N0RF4XK91",
|
||||
"protocal": "VALUE",
|
||||
"shareType": "APP"
|
||||
}
|
||||
]
|
||||
},
|
||||
"methods": {},
|
||||
"lifeCycles": {},
|
||||
"hidden": false,
|
||||
"title": "",
|
||||
"isLocked": false,
|
||||
"condition": true,
|
||||
"conditionGroup": "",
|
||||
"children": [
|
||||
{
|
||||
"componentName": "RootHeader",
|
||||
"id": "node_ockyigdqxl2",
|
||||
"docId": "dockyigdqxl",
|
||||
"props": {},
|
||||
"hidden": false,
|
||||
"title": "",
|
||||
"isLocked": false,
|
||||
"condition": true,
|
||||
"conditionGroup": ""
|
||||
},
|
||||
{
|
||||
"componentName": "RootContent",
|
||||
"id": "node_ockyigdqxl3",
|
||||
"docId": "dockyigdqxl",
|
||||
"props": {
|
||||
"contentMargin": "20",
|
||||
"contentPadding": "20",
|
||||
"contentBgColor": "white"
|
||||
},
|
||||
"hidden": false,
|
||||
"title": "",
|
||||
"isLocked": false,
|
||||
"condition": true,
|
||||
"conditionGroup": "",
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Button",
|
||||
"id": "node_ockyigdqxl5",
|
||||
"docId": "dockyigdqxl",
|
||||
"props": {
|
||||
"content": "按 钮",
|
||||
"type": "primary",
|
||||
"size": "medium",
|
||||
"behavior": "NORMAL",
|
||||
"__style__": {},
|
||||
"fieldId": "button_kyige3yf",
|
||||
"events": {
|
||||
"ignored": true
|
||||
},
|
||||
"baseIcon": "",
|
||||
"otherIcon": "",
|
||||
"loading": false,
|
||||
"triggerEventsWhenLoading": false,
|
||||
"className": "_css_pseudo_node_ockyigdqxl5"
|
||||
},
|
||||
"hidden": false,
|
||||
"title": "",
|
||||
"isLocked": false,
|
||||
"condition": true,
|
||||
"conditionGroup": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "RootFooter",
|
||||
"id": "node_ockyigdqxl4",
|
||||
"docId": "dockyigdqxl",
|
||||
"props": {},
|
||||
"hidden": false,
|
||||
"title": "",
|
||||
"isLocked": false,
|
||||
"condition": true,
|
||||
"conditionGroup": ""
|
||||
}
|
||||
]
|
||||
};
|
||||
@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
process() {
|
||||
return '';
|
||||
},
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,222 +0,0 @@
|
||||
|
||||
import React, { Component, createElement, forwardRef, PureComponent, createContext } from 'react';
|
||||
const mockGetRenderers = jest.fn();
|
||||
const mockGetRuntime = jest.fn();
|
||||
const mockParseExpression = jest.fn();
|
||||
jest.mock('../../src/adapter', () => {
|
||||
return {
|
||||
getRenderers: () => { return mockGetRenderers();},
|
||||
getRuntime: () => { return mockGetRuntime();},
|
||||
};
|
||||
});
|
||||
jest.mock('../../src/utils', () => {
|
||||
const originalUtils = jest.requireActual('../../src/utils');
|
||||
return {
|
||||
...originalUtils,
|
||||
parseExpression: (...args) => { mockParseExpression(args);},
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
import baseRendererFactory from '../../src/renderer/base';
|
||||
import { IBaseRendererProps } from '../../src/types';
|
||||
import TestRenderer from 'react-test-renderer';
|
||||
import components from '../utils/components';
|
||||
import schema from '../fixtures/schema/basic';
|
||||
|
||||
|
||||
describe('Base Render factory', () => {
|
||||
it('customBaseRenderer logic works', () => {
|
||||
mockGetRenderers.mockReturnValue({BaseRenderer: {}});
|
||||
const baseRenderer = baseRendererFactory();
|
||||
expect(mockGetRenderers).toBeCalledTimes(1);
|
||||
expect(baseRenderer).toStrictEqual({});
|
||||
mockGetRenderers.mockClear();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Base Render methods', () => {
|
||||
let RendererClass;
|
||||
const mockRendererFactory = () => {
|
||||
return class extends Component {
|
||||
constructor(props: IBaseRendererProps, context: any) {
|
||||
super(props, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
beforeEach(() => {
|
||||
const mockRnederers = {
|
||||
PageRenderer: mockRendererFactory(),
|
||||
ComponentRenderer: mockRendererFactory(),
|
||||
BlockRenderer: mockRendererFactory(),
|
||||
AddonRenderer: mockRendererFactory(),
|
||||
TempRenderer: mockRendererFactory(),
|
||||
DivRenderer: mockRendererFactory(),
|
||||
};
|
||||
mockGetRenderers.mockReturnValue(mockRnederers);
|
||||
mockGetRuntime.mockReturnValue({
|
||||
Component,
|
||||
createElement,
|
||||
PureComponent,
|
||||
createContext,
|
||||
forwardRef,
|
||||
});
|
||||
RendererClass = baseRendererFactory();
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
mockGetRenderers.mockClear();
|
||||
})
|
||||
|
||||
it('should excute lifecycle.getDerivedStateFromProps when defined', () => {
|
||||
const mockGetDerivedStateFromProps = {
|
||||
type: 'JSFunction',
|
||||
value: 'function() {\n console.log(\'did mount\');\n }',
|
||||
};
|
||||
const mockSchema = schema;
|
||||
(mockSchema.lifeCycles as any).getDerivedStateFromProps = mockGetDerivedStateFromProps;
|
||||
|
||||
// const originalUtils = jest.requireActual('../../src/utils');
|
||||
// mockParseExpression.mockImplementation(originalUtils.parseExpression);
|
||||
const component = TestRenderer.create(
|
||||
<RendererClass
|
||||
__schema={mockSchema}
|
||||
components={components as any}
|
||||
thisRequiredInJSE={false}
|
||||
a='1'
|
||||
/>);
|
||||
// console.log(component.root.props.a);
|
||||
// component.update(<RendererClasssnippets
|
||||
// schema={mockSchema}
|
||||
// components={components as any}
|
||||
// thisRequiredInJSE={false}
|
||||
// a='2'
|
||||
// />);
|
||||
// console.log(component.root.props.a);
|
||||
// expect(mockParseExpression).toHaveBeenCalledWith(mockGetDerivedStateFromProps, expect.anything())
|
||||
// test lifecycle.getDerivedStateFromProps is null
|
||||
|
||||
// test lifecycle.getDerivedStateFromProps is JSExpression
|
||||
|
||||
// test lifecycle.getDerivedStateFromProps is JSFunction
|
||||
|
||||
// test lifecycle.getDerivedStateFromProps is function
|
||||
|
||||
});
|
||||
|
||||
|
||||
// it('should excute lifecycle.getSnapshotBeforeUpdate when defined', () => {
|
||||
// });
|
||||
|
||||
// it('should excute lifecycle.componentDidMount when defined', () => {
|
||||
// });
|
||||
|
||||
// it('should excute lifecycle.componentDidUpdate when defined', () => {
|
||||
// });
|
||||
|
||||
// it('should excute lifecycle.componentWillUnmount when defined', () => {
|
||||
// });
|
||||
|
||||
// it('should excute lifecycle.componentDidCatch when defined', () => {
|
||||
// });
|
||||
|
||||
// it('__executeLifeCycleMethod should work', () => {
|
||||
// });
|
||||
|
||||
// it('reloadDataSource should work', () => {
|
||||
// });
|
||||
|
||||
// it('shouldComponentUpdate should work', () => {
|
||||
// });
|
||||
|
||||
|
||||
// it('_getComponentView should work', () => {
|
||||
// });
|
||||
|
||||
// it('__bindCustomMethods should work', () => {
|
||||
// });
|
||||
|
||||
// it('__generateCtx should work', () => {
|
||||
// });
|
||||
|
||||
// it('__parseData should work', () => {
|
||||
// });
|
||||
|
||||
// it('__initDataSource should work', () => {
|
||||
// });
|
||||
|
||||
// it('__initI18nAPIs should work', () => {
|
||||
// });
|
||||
|
||||
// it('__writeCss should work', () => {
|
||||
// });
|
||||
|
||||
// it('__render should work', () => {
|
||||
// });
|
||||
|
||||
// it('getSchemaChildren should work', () => {
|
||||
// });
|
||||
|
||||
// it('__createDom should work', () => {
|
||||
// });
|
||||
|
||||
// it('__createVirtualDom should work', () => {
|
||||
// });
|
||||
|
||||
// it('__componentHOCs should work', () => {
|
||||
// });
|
||||
|
||||
// it('__getSchemaChildrenVirtualDom should work', () => {
|
||||
// });
|
||||
|
||||
// it('__getComponentProps should work', () => {
|
||||
// });
|
||||
|
||||
// it('__createLoopVirtualDom should work', () => {
|
||||
// });
|
||||
|
||||
// it('__designModeIsDesign should work', () => {
|
||||
// });
|
||||
|
||||
// it('__parseProps should work', () => {
|
||||
// });
|
||||
|
||||
// it('$ should work', () => {
|
||||
// });
|
||||
|
||||
// it('__renderContextProvider should work', () => {
|
||||
// });
|
||||
|
||||
// it('__renderContextConsumer should work', () => {
|
||||
// });
|
||||
|
||||
// it('__getHOCWrappedComponent should work', () => {
|
||||
// });
|
||||
|
||||
// it('__renderComp should work', () => {
|
||||
// });
|
||||
|
||||
// it('__renderContent should work', () => {
|
||||
// });
|
||||
|
||||
// it('__checkSchema should work', () => {
|
||||
// });
|
||||
|
||||
// it('requestHandlersMap should work', () => {
|
||||
// });
|
||||
|
||||
// it('utils should work', () => {
|
||||
// });
|
||||
|
||||
// it('constants should work', () => {
|
||||
// });
|
||||
|
||||
// it('history should work', () => {
|
||||
// });
|
||||
|
||||
// it('location should work', () => {
|
||||
// });
|
||||
|
||||
// it('match should work', () => {
|
||||
// });
|
||||
});
|
||||
@ -1,447 +0,0 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import schema from '../fixtures/schema/basic';
|
||||
import '../utils/react-env-init';
|
||||
import rendererFactory from '../../src/renderer/renderer';
|
||||
import components from '../utils/components';
|
||||
|
||||
const Renderer = rendererFactory();
|
||||
|
||||
function getComp(schema, comp = null, others = {}): Promise<{
|
||||
component,
|
||||
inst,
|
||||
}> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const component = renderer.create(
|
||||
<Renderer
|
||||
schema={schema}
|
||||
components={components as any}
|
||||
{...others}
|
||||
/>);
|
||||
|
||||
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 = (
|
||||
<Renderer
|
||||
schema={schema as any}
|
||||
components={components as any}
|
||||
/>);
|
||||
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, {
|
||||
thisRequiredInJSE: false,
|
||||
}).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('JSExpression props with loop, and thisRequiredInJSE is true', (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.length).toEqual(2);
|
||||
[0, 1].forEach((i) => {
|
||||
expect(inst[i].props[`name1`]).toBe(i + 1 + '');
|
||||
expect(inst[i].props[`name2`]).toBe(undefined);
|
||||
})
|
||||
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: 'JSFunction',
|
||||
value: 'function() {return 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();
|
||||
});
|
||||
});
|
||||
|
||||
it('JSSlot has loop', (done) => {
|
||||
const schema = {
|
||||
componentName: "Page",
|
||||
props: {},
|
||||
children: [
|
||||
{
|
||||
componentName: "SlotComponent",
|
||||
id: "node_k8bnubvz",
|
||||
props: {
|
||||
mobileSlot: {
|
||||
type: "JSSlot",
|
||||
title: "mobile容器",
|
||||
name: "mobileSlot",
|
||||
value: [
|
||||
{
|
||||
condition: true,
|
||||
hidden: false,
|
||||
children: [
|
||||
{
|
||||
condition: true,
|
||||
hidden: false,
|
||||
loopArgs: [
|
||||
"item",
|
||||
"index"
|
||||
],
|
||||
isLocked: false,
|
||||
conditionGroup: "",
|
||||
componentName: "Text",
|
||||
id: "node_ocl1ao1o7w4",
|
||||
title: "",
|
||||
props: {
|
||||
maxLine: 0,
|
||||
showTitle: false,
|
||||
className: "text_l1ao7pfb",
|
||||
behavior: "NORMAL",
|
||||
content: "这是一个低代码业务组件~",
|
||||
__style__: ":root {\n font-size: 14px;\n color: #666;\n}",
|
||||
fieldId: "text_l1ao7lvp"
|
||||
}
|
||||
}
|
||||
],
|
||||
loop: {
|
||||
type: "JSExpression",
|
||||
value: "this.state.content"
|
||||
},
|
||||
loopArgs: [
|
||||
"item",
|
||||
"index"
|
||||
],
|
||||
isLocked: false,
|
||||
conditionGroup: "",
|
||||
componentName: "Div",
|
||||
id: "node_ocl1ao1o7w3",
|
||||
title: "",
|
||||
props: {
|
||||
useFieldIdAsDomId: false,
|
||||
customClassName: "",
|
||||
className: "div_l1ao7pfc",
|
||||
behavior: "NORMAL",
|
||||
__style__: ":root {\n padding: 12px;\n background: #f2f2f2;\n border: 1px solid #ddd;\n}",
|
||||
fieldId: "div_l1ao7lvq"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
],
|
||||
state: {
|
||||
content: {
|
||||
type: "JSExpression",
|
||||
value: "[{}, {}, {}]",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
getComp(schema, components.Div).then(({ component, inst }) => {
|
||||
expect(inst.length).toBe(3);
|
||||
componentSnapshot = component;
|
||||
done();
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
describe("designMode", () => {
|
||||
it('designMode:default', (done) => {
|
||||
const schema = {
|
||||
componentName: 'Page',
|
||||
props: {},
|
||||
children: [
|
||||
{
|
||||
componentName: "Div",
|
||||
props: {
|
||||
className: 'div-ut',
|
||||
children: [
|
||||
{
|
||||
componentName: "Div",
|
||||
visible: true,
|
||||
props: {
|
||||
className: 'div-ut-children',
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
getComp(schema, components.Div).then(({ component, inst }) => {
|
||||
expect(inst.length).toBe(2);
|
||||
expect(inst[0].props.className).toBe('div-ut');
|
||||
expect(inst[1].props.className).toBe('div-ut-children');
|
||||
componentSnapshot = component;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('designMode:design', (done) => {
|
||||
const schema = {
|
||||
componentName: 'Page',
|
||||
props: {},
|
||||
children: [
|
||||
{
|
||||
componentName: "Div",
|
||||
id: '0',
|
||||
props: {
|
||||
className: 'div-ut',
|
||||
children: [
|
||||
{
|
||||
componentName: "Div",
|
||||
id: 'hiddenId',
|
||||
hidden: true,
|
||||
props: {
|
||||
className: 'div-ut-children',
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
getComp(schema, components.Div, {
|
||||
designMode: 'design',
|
||||
getNode: (id) => {
|
||||
if (id === 'hiddenId') {
|
||||
return {
|
||||
export() {
|
||||
return {
|
||||
hidden: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}).then(({ component, inst }) => {
|
||||
expect(inst.length).toBe(1);
|
||||
expect(inst[0].props.className).toBe('div-ut');
|
||||
done();
|
||||
});
|
||||
});
|
||||
})
|
||||
@ -1,14 +0,0 @@
|
||||
jest.mock('lodash', () => {
|
||||
const original = jest.requireActual('lodash');
|
||||
|
||||
return {
|
||||
...original,
|
||||
debounce: (fn) => (...args: any[]) => fn.apply(this, args),
|
||||
throttle: (fn) => (...args: any[]) => fn.apply(this, args),
|
||||
}
|
||||
})
|
||||
|
||||
export const mockConsoleWarn = jest.fn();
|
||||
console.warn = mockConsoleWarn;
|
||||
|
||||
process.env.NODE_ENV = 'production';
|
||||
@ -1,463 +0,0 @@
|
||||
import {
|
||||
isSchema,
|
||||
isFileSchema,
|
||||
inSameDomain,
|
||||
getFileCssName,
|
||||
isJSSlot,
|
||||
getValue,
|
||||
getI18n,
|
||||
transformArrayToMap,
|
||||
transformStringToFunction,
|
||||
isVariable,
|
||||
capitalizeFirstLetter,
|
||||
forEach,
|
||||
isString,
|
||||
serializeParams,
|
||||
parseExpression,
|
||||
parseThisRequiredExpression,
|
||||
parseI18n,
|
||||
parseData,
|
||||
} from '../../src/utils/common';
|
||||
import logger from '../../src/utils/logger';
|
||||
|
||||
describe('test isSchema', () => {
|
||||
it('should be false when empty value is passed', () => {
|
||||
expect(isSchema(null)).toBeFalsy();
|
||||
expect(isSchema(undefined)).toBeFalsy();
|
||||
expect(isSchema('')).toBeFalsy();
|
||||
expect(isSchema({})).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should be true when componentName is Leaf or Slot ', () => {
|
||||
expect(isSchema({ componentName: 'Leaf' })).toBeTruthy();
|
||||
expect(isSchema({ componentName: 'Slot' })).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should check each item of an array', () => {
|
||||
const validArraySchema = [
|
||||
{ componentName: 'Button', props: {}},
|
||||
{ componentName: 'Button', props: { type: 'JSExpression' }},
|
||||
{ componentName: 'Leaf' },
|
||||
{ componentName: 'Slot'},
|
||||
];
|
||||
const invalidArraySchema = [
|
||||
...validArraySchema,
|
||||
{ componentName: 'ComponentWithoutProps'},
|
||||
];
|
||||
expect(isSchema(validArraySchema)).toBeTruthy();
|
||||
expect(isSchema(invalidArraySchema)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('normal valid schema should contains componentName, and props of type object or JSExpression', () => {
|
||||
expect(isSchema({ componentName: 'Button', props: {}})).toBeTruthy();
|
||||
expect(isSchema({ componentName: 'Button', props: { type: 'JSExpression' }})).toBeTruthy();
|
||||
expect(isSchema({ xxxName: 'Button'})).toBeFalsy();
|
||||
expect(isSchema({ componentName: 'Button', props: null})).toBeFalsy();
|
||||
expect(isSchema({ componentName: 'Button', props: []})).toBeFalsy();
|
||||
expect(isSchema({ componentName: 'Button', props: 'props string'})).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('test isFileSchema ', () => {
|
||||
it('should be false when invalid schema is passed', () => {
|
||||
expect(isFileSchema({ xxxName: 'Button'})).toBeFalsy();
|
||||
expect(isFileSchema({ componentName: 'Button', props: null})).toBeFalsy();
|
||||
expect(isFileSchema({ componentName: 'Button', props: []})).toBeFalsy();
|
||||
expect(isFileSchema({ componentName: 'Button', props: 'props string'})).toBeFalsy();
|
||||
});
|
||||
it('should be true only when schema with root named Page || Block || Component is passed', () => {
|
||||
expect(isFileSchema({ componentName: 'Page', props: {}})).toBeTruthy();
|
||||
expect(isFileSchema({ componentName: 'Block', props: {}})).toBeTruthy();
|
||||
expect(isFileSchema({ componentName: 'Component', props: {}})).toBeTruthy();
|
||||
expect(isFileSchema({ componentName: 'Button', props: {}})).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('test inSameDomain ', () => {
|
||||
let windowSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
windowSpy = jest.spyOn(window, "window", "get");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
windowSpy.mockRestore();
|
||||
});
|
||||
it('should work', () => {
|
||||
|
||||
windowSpy.mockImplementation(() => ({
|
||||
parent: {
|
||||
location: {
|
||||
host: "example.com"
|
||||
},
|
||||
},
|
||||
location: {
|
||||
host: "example.com"
|
||||
}
|
||||
}));
|
||||
expect(inSameDomain()).toBeTruthy();
|
||||
|
||||
windowSpy.mockImplementation(() => ({
|
||||
parent: {
|
||||
location: {
|
||||
host: "example.com"
|
||||
},
|
||||
},
|
||||
location: {
|
||||
host: "another.com"
|
||||
}
|
||||
}));
|
||||
expect(inSameDomain()).toBeFalsy();
|
||||
|
||||
windowSpy.mockImplementation(() => ({
|
||||
parent: null,
|
||||
location: {
|
||||
host: "example.com"
|
||||
}
|
||||
}));
|
||||
|
||||
expect(inSameDomain()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('test getFileCssName ', () => {
|
||||
it('should work', () => {
|
||||
expect(getFileCssName(null)).toBe(undefined);
|
||||
expect(getFileCssName(undefined)).toBe(undefined);
|
||||
expect(getFileCssName('')).toBe(undefined);
|
||||
expect(getFileCssName('FileName')).toBe('lce-file-name');
|
||||
expect(getFileCssName('Page1_abc')).toBe('lce-page1_abc');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('test isJSSlot ', () => {
|
||||
it('should work', () => {
|
||||
expect(isJSSlot(null)).toBeFalsy();
|
||||
expect(isJSSlot(undefined)).toBeFalsy();
|
||||
expect(isJSSlot('stringValue')).toBeFalsy();
|
||||
expect(isJSSlot([1, 2, 3])).toBeFalsy();
|
||||
expect(isJSSlot({ type: 'JSSlot' })).toBeTruthy();
|
||||
expect(isJSSlot({ type: 'JSBlock' })).toBeTruthy();
|
||||
expect(isJSSlot({ type: 'anyOtherType' })).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('test getValue ', () => {
|
||||
it('should check params', () => {
|
||||
expect(getValue(null, 'somePath')).toStrictEqual({});
|
||||
expect(getValue(undefined, 'somePath')).toStrictEqual({});
|
||||
// array is not valid input, return default
|
||||
expect(getValue([], 'somePath')).toStrictEqual({});
|
||||
expect(getValue([], 'somePath', 'aaa')).toStrictEqual('aaa');
|
||||
expect(getValue([1, 2, 3], 'somePath', 'aaa')).toStrictEqual('aaa');
|
||||
|
||||
expect(getValue({}, 'somePath')).toStrictEqual({});
|
||||
expect(getValue({}, 'somePath', 'default')).toStrictEqual('default');
|
||||
});
|
||||
it('should work normally', () => {
|
||||
// single segment path
|
||||
expect(getValue({ a: 'aValue' }, 'a')).toStrictEqual('aValue');
|
||||
expect(getValue({ a: 'aValue', f:null }, 'f')).toBeNull();
|
||||
expect(getValue({ a: { b: 'bValue' } }, 'a.b')).toStrictEqual('bValue');
|
||||
expect(getValue({ a: { b: 'bValue', c: { d: 'dValue' } } }, 'a.c.d')).toStrictEqual('dValue');
|
||||
expect(getValue({ a: { b: 'bValue', c: { d: 'dValue' } } }, 'e')).toStrictEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('test getI18n ', () => {
|
||||
it('should work', () => {
|
||||
const messages = {
|
||||
'zh-CN': {
|
||||
'key1': '啊啊啊',
|
||||
'key2': '哈哈哈',
|
||||
},
|
||||
};
|
||||
expect(getI18n('keyString', {}, 'zh-CN')).toStrictEqual('');
|
||||
expect(getI18n('keyString', {}, 'zh-CN', null)).toStrictEqual('');
|
||||
expect(getI18n('keyString', {}, 'en-US', messages)).toStrictEqual('');
|
||||
expect(getI18n('key3', {}, 'zh-CN', messages)).toStrictEqual('');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('test transformArrayToMap ', () => {
|
||||
it('should work', () => {
|
||||
expect(transformArrayToMap([])).toStrictEqual({});
|
||||
expect(transformArrayToMap('not a array')).toStrictEqual({});
|
||||
expect(transformArrayToMap({'not Array': 1})).toStrictEqual({});
|
||||
|
||||
let mockArray = [
|
||||
{
|
||||
name: 'jack',
|
||||
age: 2,
|
||||
},
|
||||
{
|
||||
name: 'jack',
|
||||
age: 20,
|
||||
}
|
||||
];
|
||||
// test override
|
||||
expect(transformArrayToMap(mockArray, 'name', true).jack.age).toBe(20);
|
||||
expect(transformArrayToMap(mockArray, 'name').jack.age).toBe(20);
|
||||
expect(transformArrayToMap(mockArray, 'name', false).jack.age).toBe(2);
|
||||
|
||||
mockArray = [
|
||||
{
|
||||
name: 'jack',
|
||||
age: 2,
|
||||
},
|
||||
{
|
||||
name: 'rose',
|
||||
age: 20,
|
||||
}
|
||||
];
|
||||
// normal case
|
||||
expect(transformArrayToMap(mockArray, 'name').jack.age).toBe(2);
|
||||
expect(transformArrayToMap(mockArray, 'name').jack.name).toBe('jack');
|
||||
expect(transformArrayToMap(mockArray, 'name').rose.age).toBe(20);
|
||||
// key not exists
|
||||
expect(transformArrayToMap(mockArray, 'nameEn')).toStrictEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
describe('test transformStringToFunction ', () => {
|
||||
it('should work', () => {
|
||||
const mockFun = jest.fn();
|
||||
expect(transformStringToFunction(mockFun)).toBe(mockFun);
|
||||
expect(transformStringToFunction(111)).toBe(111);
|
||||
|
||||
let mockFnStr = 'function(){return 111;}';
|
||||
let fn = transformStringToFunction(mockFnStr);
|
||||
expect(fn()).toBe(111);
|
||||
|
||||
mockFnStr = '() => { return 222; }';
|
||||
fn = transformStringToFunction(mockFnStr);
|
||||
expect(fn()).toBe(222);
|
||||
|
||||
mockFnStr = 'function getValue() { return 333; }';
|
||||
fn = transformStringToFunction(mockFnStr);
|
||||
expect(fn()).toBe(333);
|
||||
|
||||
mockFnStr = 'function getValue(aaa) {\
|
||||
return aaa; \
|
||||
}';
|
||||
fn = transformStringToFunction(mockFnStr);
|
||||
expect(fn(123)).toBe(123);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('test isVariable ', () => {
|
||||
it('should work', () => {
|
||||
expect(isVariable(null)).toBeFalsy();
|
||||
expect(isVariable(undefined)).toBeFalsy();
|
||||
expect(isVariable([1, 2, 3])).toBeFalsy();
|
||||
expect(isVariable({})).toBeFalsy();
|
||||
expect(isVariable({ type: 'any other type' })).toBeFalsy();
|
||||
expect(isVariable({ type: 'variable' })).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('test capitalizeFirstLetter ', () => {
|
||||
it('should work', () => {
|
||||
expect(capitalizeFirstLetter(null)).toBeNull();
|
||||
expect(capitalizeFirstLetter()).toBeUndefined();
|
||||
expect(capitalizeFirstLetter([1, 2, 3])).toStrictEqual([1, 2, 3]);
|
||||
expect(capitalizeFirstLetter({ a: 1 })).toStrictEqual({ a: 1 });
|
||||
expect(capitalizeFirstLetter('')).toStrictEqual('');
|
||||
expect(capitalizeFirstLetter('a')).toStrictEqual('A');
|
||||
expect(capitalizeFirstLetter('abcd')).toStrictEqual('Abcd');
|
||||
});
|
||||
});
|
||||
|
||||
describe('test forEach ', () => {
|
||||
it('should work', () => {
|
||||
const mockFn = jest.fn();
|
||||
|
||||
forEach(null, mockFn);
|
||||
expect(mockFn).toBeCalledTimes(0);
|
||||
|
||||
forEach(undefined, mockFn);
|
||||
expect(mockFn).toBeCalledTimes(0);
|
||||
|
||||
forEach([1, 2, 3], mockFn);
|
||||
expect(mockFn).toBeCalledTimes(0);
|
||||
|
||||
forEach('stringValue', mockFn);
|
||||
expect(mockFn).toBeCalledTimes(0);
|
||||
|
||||
forEach({ a: 1, b: 2, c: 3 }, mockFn);
|
||||
expect(mockFn).toBeCalledTimes(3);
|
||||
|
||||
const mockFn2 = jest.fn();
|
||||
forEach({ a: 1 }, mockFn2, { b: 'bbb' });
|
||||
expect(mockFn2).toHaveBeenCalledWith(1, 'a');
|
||||
|
||||
let sum = 0;
|
||||
const mockFn3 = function(value, key) { sum = value + this.b; };
|
||||
forEach({ a: 1 }, mockFn3, { b: 10 });
|
||||
expect(sum).toEqual(11);
|
||||
});
|
||||
});
|
||||
|
||||
describe('test isString ', () => {
|
||||
it('should work', () => {
|
||||
expect(isString(123)).toBeFalsy();
|
||||
expect(isString([])).toBeFalsy();
|
||||
expect(isString({})).toBeFalsy();
|
||||
expect(isString(null)).toBeFalsy();
|
||||
expect(isString(undefined)).toBeFalsy();
|
||||
expect(isString(true)).toBeFalsy();
|
||||
expect(isString('111')).toBeTruthy();
|
||||
expect(isString(new String('111'))).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('test serializeParams ', () => {
|
||||
it('should work', () => {
|
||||
const mockParams = { a: 1, b: 2, c: 'cvalue', d:[1, 'a', {}], e: {e1: 'value1', e2: 'value2'}};
|
||||
const result = serializeParams(mockParams);
|
||||
const decodedParams = decodeURIComponent(result);
|
||||
expect(result).toBe('a=1&b=2&c=cvalue&d=%5B1%2C%22a%22%2C%7B%7D%5D&e=%7B%22e1%22%3A%22value1%22%2C%22e2%22%3A%22value2%22%7D');
|
||||
expect(decodedParams).toBe('a=1&b=2&c=cvalue&d=[1,"a",{}]&e={"e1":"value1","e2":"value2"}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('test parseExpression ', () => {
|
||||
it('can handle JSExpression', () => {
|
||||
const mockExpression = {
|
||||
"type": "JSExpression",
|
||||
"value": "function (params) { return this.scopeValue + params.param1 + 5;}"
|
||||
};
|
||||
const result = parseExpression(mockExpression, { scopeValue: 1 });
|
||||
expect(result({ param1: 2 })).toBe((1 + 2 + 5));
|
||||
});
|
||||
|
||||
it('[success] JSExpression handle without this use scopeValue', () => {
|
||||
const mockExpression = {
|
||||
"type": "JSExpression",
|
||||
"value": "state"
|
||||
};
|
||||
const result = parseExpression(mockExpression, { state: 1 });
|
||||
expect(result).toBe((1));
|
||||
});
|
||||
|
||||
it('[success] JSExpression handle without this use scopeValue', () => {
|
||||
const mockExpression = {
|
||||
"type": "JSExpression",
|
||||
"value": "this.state"
|
||||
};
|
||||
const result = parseExpression(mockExpression, { state: 1 });
|
||||
expect(result).toBe((1));
|
||||
});
|
||||
});
|
||||
|
||||
describe('test parseThisRequiredExpression', () => {
|
||||
it('can handle JSExpression', () => {
|
||||
const mockExpression = {
|
||||
"type": "JSExpression",
|
||||
"value": "function (params) { return this.scopeValue + params.param1 + 5;}"
|
||||
};
|
||||
const result = parseThisRequiredExpression(mockExpression, { scopeValue: 1 });
|
||||
expect(result({ param1: 2 })).toBe((1 + 2 + 5));
|
||||
});
|
||||
|
||||
it('[error] JSExpression handle without this use scopeValue', () => {
|
||||
const mockExpression = {
|
||||
"type": "JSExpression",
|
||||
"value": "state.text"
|
||||
};
|
||||
const fn = logger.error = jest.fn();
|
||||
parseThisRequiredExpression(mockExpression, { state: { text: 'text' } });
|
||||
expect(fn).toBeCalledWith(' parseExpression.error', new ReferenceError('state is not defined'), {"type": "JSExpression", "value": "state.text"}, {"state": {"text": "text"}});
|
||||
});
|
||||
|
||||
it('[success] JSExpression handle without this use scopeValue', () => {
|
||||
const mockExpression = {
|
||||
"type": "JSExpression",
|
||||
"value": "this.state"
|
||||
};
|
||||
const result = parseThisRequiredExpression(mockExpression, { state: 1 });
|
||||
expect(result).toBe((1));
|
||||
});
|
||||
})
|
||||
|
||||
describe('test parseI18n ', () => {
|
||||
it('can handle normal parseI18n', () => {
|
||||
const mockI18n = {
|
||||
"type": "i18n",
|
||||
"key": "keyA"
|
||||
};
|
||||
const mockI18nFun = (key) => { return 'hahaha' + key;};
|
||||
const result = parseI18n(mockI18n, { i18n: mockI18nFun });
|
||||
expect(result).toBe('hahahakeyA');
|
||||
});
|
||||
});
|
||||
|
||||
describe('test parseData ', () => {
|
||||
it('should work when isJSExpression === true', () => {
|
||||
const mockExpression = {
|
||||
"type": "JSExpression",
|
||||
"value": "function (params) { return this.scopeValue + params.param1 + 5;}"
|
||||
};
|
||||
const result = parseData(mockExpression, { scopeValue: 1 });
|
||||
expect(result({ param1: 2 })).toBe((1 + 2 + 5));
|
||||
});
|
||||
it('should work when isI18nData === true', () => {
|
||||
const mockI18n = {
|
||||
"type": "i18n",
|
||||
"key": "keyA"
|
||||
};
|
||||
const mockI18nFun = (key) => { return 'hahaha' + key;};
|
||||
const result = parseData(mockI18n, { i18n: mockI18nFun });
|
||||
expect(result).toBe('hahahakeyA');
|
||||
});
|
||||
it('should work when schema is string', () => {
|
||||
expect(parseData(' this is a normal string, will be trimmed only ')).toStrictEqual('this is a normal string, will be trimmed only');
|
||||
});
|
||||
|
||||
it('should work when schema is array', () => {
|
||||
const mockData = [
|
||||
{
|
||||
"type": "i18n",
|
||||
"key": "keyA"
|
||||
},
|
||||
' this is a normal string, will be trimmed only ',
|
||||
];
|
||||
|
||||
const mockI18nFun = (key) => { return 'hahaha' + key;};
|
||||
const result = parseData(mockData, { i18n: mockI18nFun });
|
||||
|
||||
expect(result[0]).toStrictEqual('hahahakeyA');
|
||||
expect(result[1]).toStrictEqual('this is a normal string, will be trimmed only');
|
||||
});
|
||||
it('should work when schema is function', () => {
|
||||
const mockFn = function() { return this.a; };
|
||||
const result = parseData(mockFn, { a: 111 });
|
||||
expect(result()).toBe(111);
|
||||
});
|
||||
it('should work when schema is null or undefined', () => {
|
||||
expect(parseData(null)).toBe(null);
|
||||
expect(parseData(undefined)).toBe(undefined);
|
||||
});
|
||||
it('should work when schema is normal object', () => {
|
||||
expect(parseData({})).toStrictEqual({});
|
||||
const mockI18nFun = (key) => { return 'hahaha' + key;};
|
||||
const result = parseData({
|
||||
key1: {
|
||||
"type": "i18n",
|
||||
"key": "keyA"
|
||||
},
|
||||
key2: ' this is a normal string, will be trimmed only ',
|
||||
__privateKey: 'any value',
|
||||
}, { i18n: mockI18nFun });
|
||||
expect(result.key1).toStrictEqual('hahahakeyA');
|
||||
expect(result.key2).toStrictEqual('this is a normal string, will be trimmed only');
|
||||
expect(result.__privateKey).toBeUndefined();
|
||||
|
||||
});
|
||||
});
|
||||
@ -1,38 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Box, Breadcrumb, Form, Select, Input, Button, Table, Pagination, Dialog } from '@alifd/next';
|
||||
|
||||
const Div = ({_leaf, ...rest}: any) => (<div {...rest}>{rest.children}</div>);
|
||||
|
||||
const MiniRenderDiv = ({_leaf, ...rest}: any) => {
|
||||
return (
|
||||
<div {...rest}>
|
||||
{rest.children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Text = ({_leaf, ...rest}: any) => (<div {...rest}>{rest.content}</div>);
|
||||
|
||||
const SlotComponent = (props: any) => props.mobileSlot;
|
||||
|
||||
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,
|
||||
SlotComponent,
|
||||
Text,
|
||||
MiniRenderDiv,
|
||||
};
|
||||
|
||||
export default components;
|
||||
@ -1,559 +0,0 @@
|
||||
// @ts-nocheck
|
||||
const mockJsonp = jest.fn();
|
||||
const mockRequest = jest.fn();
|
||||
const mockGet = jest.fn();
|
||||
const mockPost = jest.fn();
|
||||
jest.mock('../../src/utils/request', () => {
|
||||
return {
|
||||
jsonp: (uri, params, headers, otherProps) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(mockJsonp(uri, params, headers, otherProps));
|
||||
});
|
||||
},
|
||||
request: (uri, params, headers, otherProps) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(mockRequest(uri, params, headers, otherProps));
|
||||
});
|
||||
},
|
||||
get: (uri, params, headers, otherProps) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(mockGet(uri, params, headers, otherProps));
|
||||
});
|
||||
},
|
||||
post: (uri, params, headers, otherProps) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(mockPost(uri, params, headers, otherProps));
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
import { DataHelper, doRequest } from '../../src/utils/data-helper';
|
||||
import { parseData } from '../../src/utils/common';
|
||||
|
||||
describe('test DataHelper ', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
})
|
||||
it('can be inited', () => {
|
||||
const mockHost = {};
|
||||
let mockDataSourceConfig = {};
|
||||
const mockAppHelper = {};
|
||||
const mockParser = (config: any) => parseData(config);
|
||||
let dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||
|
||||
expect(dataHelper).toBeTruthy();
|
||||
expect(dataHelper.host).toBe(mockHost);
|
||||
expect(dataHelper.config).toBe(mockDataSourceConfig);
|
||||
expect(dataHelper.appHelper).toBe(mockAppHelper);
|
||||
expect(dataHelper.parser).toBe(mockParser);
|
||||
|
||||
|
||||
dataHelper = new DataHelper(mockHost, undefined, mockAppHelper, mockParser);
|
||||
expect(dataHelper.config).toStrictEqual({});
|
||||
expect(dataHelper.ajaxList).toStrictEqual([]);
|
||||
|
||||
mockDataSourceConfig = {
|
||||
list: [
|
||||
{
|
||||
id: 'ds1',
|
||||
}, {
|
||||
id: 'ds2',
|
||||
},
|
||||
]
|
||||
};
|
||||
dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||
expect(dataHelper.config).toBe(mockDataSourceConfig);
|
||||
expect(dataHelper.ajaxList.length).toBe(2);
|
||||
expect(dataHelper.ajaxMap.ds1).toStrictEqual({
|
||||
id: 'ds1',
|
||||
});
|
||||
});
|
||||
it('should handle generateDataSourceMap properly in constructor', () => {
|
||||
const mockHost = {};
|
||||
let mockDataSourceConfig = {};
|
||||
const mockAppHelper = {};
|
||||
const mockParser = (config: any) => parseData(config);
|
||||
let dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||
|
||||
// test generateDataSourceMap logic
|
||||
mockDataSourceConfig = {
|
||||
list: [
|
||||
{
|
||||
id: 'getInfo',
|
||||
isInit: true,
|
||||
type: 'fetch', // fetch/mtop/jsonp/custom
|
||||
options: {
|
||||
uri: 'mock/info.json',
|
||||
method: 'GET',
|
||||
params: { a: 1 },
|
||||
timeout: 5000,
|
||||
},
|
||||
}, {
|
||||
id: 'postInfo',
|
||||
isInit: true,
|
||||
type: 'fetch',
|
||||
options: {
|
||||
uri: 'mock/info.json',
|
||||
method: 'POST',
|
||||
params: { a: 1 },
|
||||
timeout: 5000,
|
||||
},
|
||||
},
|
||||
]
|
||||
};
|
||||
dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||
expect(Object.keys(dataHelper.dataSourceMap).length).toBe(2);
|
||||
expect(dataHelper.dataSourceMap.getInfo.status).toBe('init');
|
||||
expect(typeof dataHelper.dataSourceMap.getInfo.load).toBe('function');
|
||||
});
|
||||
|
||||
it('getInitDataSourseConfigs should work', () => {
|
||||
const mockHost = {};
|
||||
let mockDataSourceConfig = {};
|
||||
const mockAppHelper = {};
|
||||
const mockParser = (config: any) => parseData(config);
|
||||
|
||||
// test generateDataSourceMap logic
|
||||
mockDataSourceConfig = {
|
||||
list: [
|
||||
{
|
||||
id: 'getInfo',
|
||||
isInit: true,
|
||||
type: 'fetch', // fetch/mtop/jsonp/custom
|
||||
options: {
|
||||
uri: 'mock/info.json',
|
||||
method: 'GET',
|
||||
params: { a: 1 },
|
||||
timeout: 5000,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'postInfo',
|
||||
isInit: false,
|
||||
type: 'fetch',
|
||||
options: {
|
||||
uri: 'mock/info.json',
|
||||
method: 'POST',
|
||||
params: { a: 1 },
|
||||
timeout: 5000,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'getInfoLater',
|
||||
isInit: false,
|
||||
type: 'fetch',
|
||||
options: {
|
||||
uri: 'mock/info.json',
|
||||
method: 'POST',
|
||||
params: { a: 1 },
|
||||
timeout: 5000,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'getInfoLater2',
|
||||
isInit: 'not a valid boolean',
|
||||
type: 'fetch',
|
||||
options: {
|
||||
uri: 'mock/info.json',
|
||||
method: 'POST',
|
||||
params: { a: 1 },
|
||||
timeout: 5000,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||
expect(dataHelper.getInitDataSourseConfigs().length).toBe(1);
|
||||
expect(dataHelper.getInitDataSourseConfigs()[0].id).toBe('getInfo');
|
||||
});
|
||||
it('util function doRequest should work', () => {
|
||||
doRequest('jsonp', {
|
||||
uri: 'https://www.baidu.com',
|
||||
params: { a: 1 },
|
||||
otherStuff1: 'aaa',
|
||||
});
|
||||
expect(mockJsonp).toBeCalled();
|
||||
|
||||
// test GET
|
||||
doRequest('fetch', {
|
||||
uri: 'https://www.baidu.com',
|
||||
method: 'get',
|
||||
params: { a: 1 },
|
||||
otherStuff1: 'aaa',
|
||||
});
|
||||
expect(mockGet).toBeCalled();
|
||||
|
||||
mockGet.mockClear();
|
||||
doRequest('fetch', {
|
||||
uri: 'https://www.baidu.com',
|
||||
method: 'Get',
|
||||
params: { a: 1 },
|
||||
otherStuff1: 'aaa',
|
||||
});
|
||||
expect(mockGet).toBeCalled();
|
||||
|
||||
mockGet.mockClear();
|
||||
doRequest('fetch', {
|
||||
uri: 'https://www.baidu.com',
|
||||
method: 'GET',
|
||||
params: { a: 1 },
|
||||
otherStuff1: 'aaa',
|
||||
});
|
||||
expect(mockGet).toBeCalled();
|
||||
|
||||
mockGet.mockClear();
|
||||
|
||||
// test POST
|
||||
doRequest('fetch', {
|
||||
uri: 'https://www.baidu.com',
|
||||
method: 'post',
|
||||
params: { a: 1 },
|
||||
otherStuff1: 'aaa',
|
||||
});
|
||||
expect(mockPost).toBeCalled();
|
||||
mockPost.mockClear();
|
||||
|
||||
doRequest('fetch', {
|
||||
uri: 'https://www.baidu.com',
|
||||
method: 'POST',
|
||||
params: { a: 1 },
|
||||
otherStuff1: 'aaa',
|
||||
});
|
||||
expect(mockPost).toBeCalled();
|
||||
mockPost.mockClear();
|
||||
doRequest('fetch', {
|
||||
uri: 'https://www.baidu.com',
|
||||
method: 'Post',
|
||||
params: { a: 1 },
|
||||
otherStuff1: 'aaa',
|
||||
});
|
||||
expect(mockPost).toBeCalled();
|
||||
mockPost.mockClear();
|
||||
|
||||
// test default
|
||||
doRequest('fetch', {
|
||||
uri: 'https://www.baidu.com',
|
||||
method: 'whatever',
|
||||
params: { a: 1 },
|
||||
otherStuff1: 'aaa',
|
||||
});
|
||||
expect(mockRequest).toBeCalled();
|
||||
mockRequest.mockClear();
|
||||
mockGet.mockClear();
|
||||
|
||||
// method will be GET when not provided
|
||||
doRequest('fetch', {
|
||||
uri: 'https://www.baidu.com',
|
||||
params: { a: 1 },
|
||||
otherStuff1: 'aaa',
|
||||
});
|
||||
expect(mockRequest).toBeCalledTimes(0);
|
||||
expect(mockGet).toBeCalledTimes(1);
|
||||
|
||||
mockRequest.mockClear();
|
||||
mockGet.mockClear();
|
||||
mockPost.mockClear();
|
||||
mockJsonp.mockClear();
|
||||
|
||||
doRequest('someOtherType', {
|
||||
uri: 'https://www.baidu.com',
|
||||
params: { a: 1 },
|
||||
otherStuff1: 'aaa',
|
||||
});
|
||||
expect(mockRequest).toBeCalledTimes(0);
|
||||
expect(mockGet).toBeCalledTimes(0);
|
||||
expect(mockPost).toBeCalledTimes(0);
|
||||
expect(mockJsonp).toBeCalledTimes(0);
|
||||
});
|
||||
it('updateDataSourceMap should work', () => {
|
||||
const mockHost = {};
|
||||
const mockDataSourceConfig = {
|
||||
list: [
|
||||
{
|
||||
id: 'ds1',
|
||||
}, {
|
||||
id: 'ds2',
|
||||
},
|
||||
]
|
||||
};
|
||||
const mockAppHelper = {};
|
||||
const mockParser = (config: any) => parseData(config);
|
||||
const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||
dataHelper.updateDataSourceMap('ds1', { a: 1 }, null);
|
||||
expect(dataHelper.dataSourceMap['ds1']).toBeTruthy();
|
||||
expect(dataHelper.dataSourceMap['ds1'].data).toStrictEqual({ a: 1 });
|
||||
expect(dataHelper.dataSourceMap['ds1'].error).toBeUndefined();
|
||||
expect(dataHelper.dataSourceMap['ds1'].status).toBe('loaded');
|
||||
dataHelper.updateDataSourceMap('ds2', { b: 2 }, new Error());
|
||||
expect(dataHelper.dataSourceMap['ds2']).toBeTruthy();
|
||||
expect(dataHelper.dataSourceMap['ds2'].data).toStrictEqual({ b: 2 });
|
||||
expect(dataHelper.dataSourceMap['ds2'].status).toBe('error');
|
||||
expect(dataHelper.dataSourceMap['ds2'].error).toBeTruthy();
|
||||
});
|
||||
|
||||
it('handleData should work', () => {
|
||||
const mockHost = { stateA: 'aValue'};
|
||||
const mockDataSourceConfig = {
|
||||
list: [
|
||||
{
|
||||
id: 'fullConfigGet',
|
||||
isInit: true,
|
||||
options: {
|
||||
params: {},
|
||||
method: 'GET',
|
||||
isCors: true,
|
||||
timeout: 5000,
|
||||
headers: {},
|
||||
uri: 'mock/info.json',
|
||||
},
|
||||
shouldFetch: {
|
||||
type: 'JSFunction',
|
||||
value: 'function() { return true; }',
|
||||
},
|
||||
dataHandler: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(res) { return res.data; }',
|
||||
},
|
||||
errorHandler: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(error) {}',
|
||||
},
|
||||
willFetch: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(options) { return options; }',
|
||||
},
|
||||
},
|
||||
]
|
||||
};
|
||||
const mockAppHelper = {};
|
||||
const mockParser = (config: any) => parseData(config);
|
||||
const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||
// test valid case
|
||||
let mockDataHandler = {
|
||||
type: 'JSFunction',
|
||||
value: 'function(res) { return res.data + \'+\' + this.stateA; }',
|
||||
};
|
||||
let result = dataHelper.handleData('fullConfigGet', mockDataHandler, { data: 'mockDataValue' }, null);
|
||||
expect(result).toBe('mockDataValue+aValue');
|
||||
|
||||
// test invalid datahandler
|
||||
mockDataHandler = {
|
||||
type: 'not a JSFunction',
|
||||
value: 'function(res) { return res.data + \'+\' + this.stateA; }',
|
||||
};
|
||||
result = dataHelper.handleData('fullConfigGet', mockDataHandler, { data: 'mockDataValue' }, null);
|
||||
expect(result).toStrictEqual({ data: 'mockDataValue' });
|
||||
|
||||
// exception with id
|
||||
mockDataHandler = {
|
||||
type: 'JSFunction',
|
||||
value: 'function(res) { return res.data + \'+\' + JSON.parse({a:1}); }',
|
||||
};
|
||||
result = dataHelper.handleData('fullConfigGet', mockDataHandler, { data: 'mockDataValue' }, null);
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
// exception without id
|
||||
mockDataHandler = {
|
||||
type: 'JSFunction',
|
||||
value: 'function(res) { return res.data + \'+\' + JSON.parse({a:1}); }',
|
||||
};
|
||||
result = dataHelper.handleData(null, mockDataHandler, { data: 'mockDataValue' }, null);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('updateConfig should work', () => {
|
||||
const mockHost = { stateA: 'aValue'};
|
||||
const mockDataSourceConfig = {
|
||||
list: [
|
||||
{
|
||||
id: 'ds1',
|
||||
}, {
|
||||
id: 'ds2',
|
||||
},
|
||||
{
|
||||
id: 'fullConfigGet',
|
||||
isInit: true,
|
||||
options: {
|
||||
params: {},
|
||||
method: 'GET',
|
||||
isCors: true,
|
||||
timeout: 5000,
|
||||
headers: {},
|
||||
uri: 'mock/info.json',
|
||||
},
|
||||
shouldFetch: {
|
||||
type: 'JSFunction',
|
||||
value: 'function() { return true; }',
|
||||
},
|
||||
dataHandler: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(res) { return res.data; }',
|
||||
},
|
||||
errorHandler: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(error) {}',
|
||||
},
|
||||
willFetch: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(options) { return options; }',
|
||||
},
|
||||
},
|
||||
]
|
||||
};
|
||||
const mockAppHelper = {};
|
||||
const mockParser = (config: any) => parseData(config);
|
||||
const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||
|
||||
expect(dataHelper.ajaxList.length).toBe(3);
|
||||
|
||||
let updatedConfig = {
|
||||
list: [
|
||||
{
|
||||
id: 'ds2',
|
||||
},
|
||||
{
|
||||
id: 'fullConfigGet',
|
||||
},
|
||||
]
|
||||
};
|
||||
dataHelper.updateConfig(updatedConfig);
|
||||
|
||||
expect(dataHelper.ajaxList.length).toBe(2);
|
||||
expect(dataHelper.dataSourceMap.ds1).toBeUndefined();
|
||||
|
||||
updatedConfig = {
|
||||
list: [
|
||||
{
|
||||
id: 'ds2',
|
||||
},
|
||||
{
|
||||
id: 'fullConfigGet',
|
||||
},
|
||||
{
|
||||
id: 'ds3',
|
||||
},
|
||||
]
|
||||
};
|
||||
dataHelper.updateConfig(updatedConfig);
|
||||
expect(dataHelper.ajaxList.length).toBe(3);
|
||||
expect(dataHelper.dataSourceMap.ds3).toBeTruthy();
|
||||
});
|
||||
|
||||
it('getInitData should work', () => {
|
||||
const mockHost = { stateA: 'aValue'};
|
||||
const mockDataSourceConfig = {
|
||||
list: [
|
||||
{
|
||||
id: 'ds1',
|
||||
}, {
|
||||
id: 'ds2',
|
||||
},
|
||||
{
|
||||
id: 'fullConfigGet',
|
||||
isInit: true,
|
||||
type: 'fetch',
|
||||
options: {
|
||||
params: {},
|
||||
method: 'GET',
|
||||
isCors: true,
|
||||
timeout: 5000,
|
||||
headers: {
|
||||
headerA: 1,
|
||||
},
|
||||
uri: 'mock/info.json',
|
||||
},
|
||||
shouldFetch: {
|
||||
type: 'JSFunction',
|
||||
value: 'function() { return true; }',
|
||||
},
|
||||
dataHandler: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(res) { return 123; }',
|
||||
},
|
||||
errorHandler: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(error) {}',
|
||||
},
|
||||
willFetch: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(options) { return options; }',
|
||||
},
|
||||
},
|
||||
]
|
||||
};
|
||||
const mockAppHelper = {};
|
||||
const mockParser = (config: any) => parseData(config);
|
||||
const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||
|
||||
expect(dataHelper.ajaxList.length).toBe(3);
|
||||
expect(mockGet).toBeCalledTimes(0);
|
||||
dataHelper.getInitData().then(res => {
|
||||
expect(mockGet).toBeCalledTimes(1);
|
||||
expect(mockGet).toBeCalledWith('mock/info.json', {}, {
|
||||
headerA: 1,
|
||||
}, expect.anything());
|
||||
mockGet.mockClear();
|
||||
});
|
||||
});
|
||||
|
||||
it('getDataSource should work', () => {
|
||||
const mockHost = { stateA: 'aValue'};
|
||||
const mockDataSourceConfig = {
|
||||
list: [
|
||||
{
|
||||
id: 'ds1',
|
||||
}, {
|
||||
id: 'ds2',
|
||||
},
|
||||
{
|
||||
id: 'fullConfigGet',
|
||||
isInit: true,
|
||||
type: 'fetch',
|
||||
options: {
|
||||
params: {},
|
||||
method: 'GET',
|
||||
isCors: true,
|
||||
timeout: 5000,
|
||||
headers: {
|
||||
headerA: 1,
|
||||
},
|
||||
uri: 'mock/info.json',
|
||||
},
|
||||
shouldFetch: {
|
||||
type: 'JSFunction',
|
||||
value: 'function() { return true; }',
|
||||
},
|
||||
dataHandler: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(res) { return 123; }',
|
||||
},
|
||||
errorHandler: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(error) {}',
|
||||
},
|
||||
willFetch: {
|
||||
type: 'JSFunction',
|
||||
value: 'function(options) { return options; }',
|
||||
},
|
||||
},
|
||||
]
|
||||
};
|
||||
const mockAppHelper = {};
|
||||
const mockParser = (config: any) => parseData(config);
|
||||
const dataHelper = new DataHelper(mockHost, mockDataSourceConfig, mockAppHelper, mockParser);
|
||||
|
||||
expect(dataHelper.ajaxList.length).toBe(3);
|
||||
expect(mockGet).toBeCalledTimes(0);
|
||||
const callbackFn = jest.fn();
|
||||
dataHelper.getDataSource('fullConfigGet', { param1: 'value1' }, {}, callbackFn).then(res => {
|
||||
expect(mockGet).toBeCalledTimes(1);
|
||||
expect(mockGet).toBeCalledWith('mock/info.json', { param1: 'value1' }, {
|
||||
headerA: 1,
|
||||
}, expect.anything());
|
||||
mockGet.mockClear();
|
||||
expect(callbackFn).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,31 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import isUseLoop from '../../src/utils/is-use-loop';
|
||||
|
||||
describe('base test', () => {
|
||||
it('designMode is true', () => {
|
||||
expect(isUseLoop([], true)).toBeFalsy();
|
||||
expect(isUseLoop([{}], true)).toBeTruthy();
|
||||
expect(isUseLoop(null, true)).toBeFalsy();
|
||||
expect(isUseLoop(undefined, true)).toBeFalsy();
|
||||
expect(isUseLoop(0, true)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('loop is expression', () => {
|
||||
expect(isUseLoop({
|
||||
"type": "JSExpression",
|
||||
"value": "function() { console.log('componentDidMount'); }"
|
||||
}, true)).toBeTruthy();
|
||||
expect(isUseLoop({
|
||||
"type": "JSExpression",
|
||||
"value": "function() { console.log('componentDidMount'); }"
|
||||
}, false)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('designMode is false', () => {
|
||||
expect(isUseLoop([], false)).toBeTruthy();
|
||||
expect(isUseLoop([{}], false)).toBeTruthy();
|
||||
expect(isUseLoop(null, false)).toBeTruthy();
|
||||
expect(isUseLoop(undefined, false)).toBeTruthy();
|
||||
expect(isUseLoop(0, false)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -1,97 +0,0 @@
|
||||
import { IPublicTypePropChangeOptions } from "@ali/lowcode-designer";
|
||||
import EventEmitter from "events";
|
||||
|
||||
export default class Node {
|
||||
private emitter: EventEmitter;
|
||||
schema: any = {
|
||||
props: {},
|
||||
};
|
||||
|
||||
componentMeta = {};
|
||||
|
||||
parent;
|
||||
|
||||
hasLoop = () => this._hasLoop;
|
||||
|
||||
id;
|
||||
|
||||
_isRoot: false;
|
||||
|
||||
_hasLoop: false;
|
||||
|
||||
constructor(schema: any, info: any = {}) {
|
||||
this.emitter = new EventEmitter();
|
||||
const {
|
||||
componentMeta,
|
||||
parent,
|
||||
isRoot,
|
||||
hasLoop,
|
||||
} = info;
|
||||
this.schema = {
|
||||
props: {},
|
||||
...schema,
|
||||
};
|
||||
this.componentMeta = componentMeta || {};
|
||||
this.parent = parent;
|
||||
this.id = schema.id;
|
||||
this._isRoot = isRoot;
|
||||
this._hasLoop = hasLoop;
|
||||
}
|
||||
|
||||
isRoot = () => this._isRoot;
|
||||
|
||||
get isRootNode () {
|
||||
return this._isRoot;
|
||||
};
|
||||
|
||||
// componentMeta() {
|
||||
// return this.componentMeta;
|
||||
// }
|
||||
|
||||
// mockLoop() {
|
||||
// // this.hasLoop = true;
|
||||
// }
|
||||
|
||||
onChildrenChange(fn: any) {
|
||||
this.emitter.on('onChildrenChange', fn);
|
||||
return () => {
|
||||
this.emitter.off('onChildrenChange', fn);
|
||||
}
|
||||
}
|
||||
|
||||
emitChildrenChange() {
|
||||
this.emitter?.emit('onChildrenChange', {});
|
||||
}
|
||||
|
||||
onPropChange(fn: any) {
|
||||
this.emitter.on('onPropChange', fn);
|
||||
return () => {
|
||||
this.emitter.off('onPropChange', fn);
|
||||
}
|
||||
}
|
||||
|
||||
emitPropChange(val: IPublicTypePropChangeOptions, skip?: boolean) {
|
||||
if (!skip) {
|
||||
this.schema.props = {
|
||||
...this.schema.props,
|
||||
[val.key + '']: val.newValue,
|
||||
}
|
||||
}
|
||||
|
||||
this.emitter?.emit('onPropChange', val);
|
||||
}
|
||||
|
||||
onVisibleChange(fn: any) {
|
||||
this.emitter.on('onVisibleChange', fn);
|
||||
return () => {
|
||||
this.emitter.off('onVisibleChange', fn);
|
||||
}
|
||||
}
|
||||
|
||||
emitVisibleChange(val: boolean) {
|
||||
this.emitter?.emit('onVisibleChange', val);
|
||||
}
|
||||
export() {
|
||||
return this.schema;
|
||||
}
|
||||
}
|
||||
@ -1,66 +0,0 @@
|
||||
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 as any).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(): types.IRenderComponent {
|
||||
// const Renderer = rendererFactory();
|
||||
// return class ReactRenderer extends Renderer implements Component {
|
||||
// readonly props: types.IRendererProps;
|
||||
|
||||
// context: ContextType<any>;
|
||||
|
||||
// setState: (
|
||||
// state: types.IRendererState,
|
||||
// callback?: () => void,
|
||||
// ) => void;
|
||||
|
||||
// forceUpdate: (callback?: () => void) => void;
|
||||
|
||||
// refs: {
|
||||
// [key: string]: ReactInstance;
|
||||
// };
|
||||
|
||||
// constructor(props: types.IRendererProps, context: ContextType<any>) {
|
||||
// super(props, context);
|
||||
// }
|
||||
|
||||
// isValidComponent(obj: any) {
|
||||
// return obj?.prototype?.isReactComponent || obj?.prototype instanceof Component;
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
|
||||
// export default factory();
|
||||
@ -1,159 +0,0 @@
|
||||
// @ts-nocheck
|
||||
const mockSerializeParams = jest.fn();
|
||||
jest.mock('../../src/utils/common', () => {
|
||||
return {
|
||||
serializeParams: (params) => {
|
||||
return mockSerializeParams(params);
|
||||
},
|
||||
};
|
||||
});
|
||||
const mockFetchJsonp = jest.fn();
|
||||
jest.mock('fetch-jsonp', () => {
|
||||
return (uri, otherProps) => {
|
||||
mockFetchJsonp(uri, otherProps);
|
||||
return Promise.resolve({
|
||||
json: () => {
|
||||
return Promise.resolve({ data: [1, 2, 3]});
|
||||
} ,
|
||||
ok: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
import { get, post, buildUrl, request, jsonp } from '../../src/utils/request';
|
||||
|
||||
|
||||
describe('test utils/request.ts ', () => {
|
||||
|
||||
it('buildUrl should be working properly', () => {
|
||||
mockSerializeParams.mockImplementation((params) => {
|
||||
return 'serializedParams=serializedParams';
|
||||
});
|
||||
expect(buildUrl('mockDataApi', { a: 1, b: 'a', c: []})).toBe('mockDataApi?serializedParams=serializedParams');
|
||||
expect(buildUrl('mockDataApi?existingParamA=valueA', { a: 1, b: 'a', c: []})).toBe('mockDataApi?existingParamA=valueA&serializedParams=serializedParams');
|
||||
mockSerializeParams.mockClear();
|
||||
|
||||
mockSerializeParams.mockImplementation((params) => {
|
||||
return undefined;
|
||||
});
|
||||
expect(buildUrl('mockDataApi', { a: 1, b: 'a', c: []})).toBe('mockDataApi');
|
||||
mockSerializeParams.mockClear();
|
||||
});
|
||||
|
||||
it('request should be working properly', () => {
|
||||
const fetchMock = jest
|
||||
.spyOn(global, 'fetch')
|
||||
.mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
json: () => Promise.resolve([]) ,
|
||||
status: 200,
|
||||
})
|
||||
);
|
||||
|
||||
request('https://someradomurl/api/list', 'GET', {}, {}, {}).then((response) => {
|
||||
expect(fetchMock).toBeCalledWith('https://someradomurl/api/list', { body: {}, credentials: 'include', headers: {}, method: 'GET'});
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('get should be working properly', () => {
|
||||
const fetchMock = jest
|
||||
.spyOn(global, 'fetch')
|
||||
.mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
json: () => Promise.resolve([]) ,
|
||||
status: 200,
|
||||
})
|
||||
);
|
||||
|
||||
get('https://someradomurl/api/list', {}, {}, {}).then((response) => {
|
||||
expect(fetchMock).toBeCalledWith(
|
||||
'https://someradomurl/api/list',
|
||||
{
|
||||
body: null,
|
||||
headers: { Accept: 'application/json' },
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
});
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('post should be working properly', () => {
|
||||
const fetchMock = jest
|
||||
.spyOn(global, 'fetch')
|
||||
.mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
json: () => Promise.resolve([]) ,
|
||||
status: 200,
|
||||
})
|
||||
);
|
||||
|
||||
post('https://someradomurl/api/list', { a: 1, b: 'a', c: [] }, { 'Content-Type': 'application/json' }, {}).then((response) => {
|
||||
expect(fetchMock).toBeCalledWith(
|
||||
'https://someradomurl/api/list',
|
||||
{
|
||||
body: '{"a":1,"b":"a","c":[]}',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
|
||||
post('https://someradomurl/api/list', [ 1, 2, 3, 4 ], {}, {}).then((response) => {
|
||||
expect(fetchMock).toBeCalledWith(
|
||||
'https://someradomurl/api/list',
|
||||
{
|
||||
body: '[1,2,3,4]',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
mockSerializeParams.mockImplementation((params) => {
|
||||
return 'serializedParams=serializedParams';
|
||||
});
|
||||
post('https://someradomurl/api/list', { a: 1, b: 'a', c: [] }, {}, {}).then((response) => {
|
||||
expect(fetchMock).toBeCalledWith(
|
||||
'https://someradomurl/api/list',
|
||||
{
|
||||
body: 'serializedParams=serializedParams',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
});
|
||||
mockSerializeParams.mockClear();
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
|
||||
});
|
||||
it('jsonp should be working properly', () => {
|
||||
mockSerializeParams.mockImplementation((params) => {
|
||||
return 'params';
|
||||
});
|
||||
jsonp('https://someradomurl/api/list', {}, { otherParam1: '123'}).catch(() => {
|
||||
expect(mockFetchJsonp).toBeCalledWith('https://someradomurl/api/list?params', { timeout: 5000, otherParam1: '123' });
|
||||
mockSerializeParams.mockClear();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,8 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib",
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": ["./src/"],
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import { useEvent } from '@alilc/renderer-core';
|
||||
|
||||
export type HistoryState = Record<string | number, any>;
|
||||
export type HistoryState = History['state'];
|
||||
export type HistoryLocation = string;
|
||||
|
||||
export enum NavigationType {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user