feat: support for NotFoundComponent design state is optional (#1013)

This commit is contained in:
刘菊萍(絮黎) 2022-09-23 15:32:23 +08:00 committed by GitHub
parent 438cccd58e
commit d3c891e2a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 78 additions and 31 deletions

View File

@ -232,6 +232,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
return engineConfig.get('thisRequiredInJSE') ?? true;
}
get enableStrictNotFoundMode(): any {
return engineConfig.get('enableStrictNotFoundMode') ?? false;
}
@computed get componentsAsset(): Asset | undefined {
return this.get('componentsAsset');
}

View File

@ -176,12 +176,29 @@ export class ComponentMeta {
}
private parseMetadata(metadata: ComponentMetadata) {
const { componentName, npm } = metadata;
const { componentName, npm, ...others } = metadata;
let _metadata = metadata;
if (!npm && !Object.keys(others).length) {
// 没有注册的组件,只能删除,不支持复制、移动等操作
_metadata = {
componentName,
configure: {
component: {
disableBehaviors: ['copy', 'move', 'lock', 'unlock'],
},
advanced: {
callbacks: {
onMoveHook: () => false,
},
},
},
};
}
this._npm = npm || this._npm;
this._componentName = componentName;
// 额外转换逻辑
this._transformedMetadata = this.transformMetadata(metadata);
this._transformedMetadata = this.transformMetadata(_metadata);
const { title } = this._transformedMetadata;
if (title) {

View File

@ -23,6 +23,7 @@ import {
setShaken,
} from '../../src/designer/dragon';
import { Project } from '../../src/project/project';
import pageMetadata from '../fixtures/component-metadata/page';
import { Node } from '../../src/document/node/node';
import { Designer } from '../../src/designer/designer';
import { DocumentModel } from '../../src/document/document-model';
@ -46,6 +47,7 @@ describe('Host 测试', () => {
beforeEach(() => {
designer = new Designer({ editor });
project = designer.project;
designer.createComponentMeta(pageMetadata);
doc = project.createDocument(formSchema);
host = new BuiltinSimulatorHost(designer.project);
});
@ -373,6 +375,14 @@ describe('Host 测试', () => {
},
})).toBeNull();
});
it('notFoundComponent', () => {
expect(host.locate({
dragObject: {
type: DragObjectType.Node,
nodes: [doc.getNode('form')],
},
})).toBeUndefined();
})
it('locate', () => {
host.locate({
dragObject: {

View File

@ -137,6 +137,10 @@ const VALID_ENGINE_OPTIONS = {
type: 'boolean',
description: 'JSExpression 是否只支持使用 this 来访问上下文变量',
},
enableStrictNotFoundMode: {
type: 'boolean',
description: '当开启组件未找到严格模式时,渲染模块不会默认给一个容器组件',
},
};
export interface EngineOptions {
/**
@ -258,6 +262,12 @@ export interface EngineOptions {
* JSExpression 使 this 访 'state.xxx' false
*/
thisRequiredInJSE?: boolean;
/**
* @default false
*
*/
enableStrictNotFoundMode?: boolean;
}
const getStrictModeValue = (engineOptions: EngineOptions, defaultValue: boolean): boolean => {

View File

@ -255,6 +255,7 @@ class Renderer extends Component<{
onCompGetRef={(schema: any, ref: ReactInstance | null) => {
documentInstance.mountInstance(schema.id, ref);
}}
enableStrictNotFoundMode={host.enableStrictNotFoundMode}
/>
);
}

View File

@ -11,7 +11,7 @@ exports[`Base should be render NotFoundComponent 1`] = `
<div
componentName="Text"
>
Component Not Found
Text Component Not Found
</div>
</div>
</div>

View File

@ -21,21 +21,21 @@ class Adapter {
}
initRuntime() {
const Component: IGeneralConstructor = class {
const Component: IGeneralConstructor = class <T = any, S = any> {
setState() {}
forceUpdate() {}
render() {}
state: Record<string, unknown>;
props: Record<string, unknown>;
state: Readonly<S>;
props: Readonly<T> & Readonly<{ children?: any | undefined }>;
refs: Record<string, unknown>;
context: Record<string, unknown>;
};
const PureComponent: IGeneralConstructor = class {
const PureComponent = class <T = any, S = any> {
setState() {}
forceUpdate() {}
render() {}
state: Record<string, unknown>;
props: Record<string, unknown>;
state: Readonly<S>;
props: Readonly<T> & Readonly<{ children?: any | undefined }>;
refs: Record<string, unknown>;
context: Record<string, unknown>;
};

View File

@ -104,13 +104,7 @@ export default function baseRendererFactory(): IBaseRenderComponent {
return customBaseRenderer;
}
const runtime = adapter.getRuntime();
const Component = runtime.Component as IGeneralConstructor<
IBaseRendererProps,
Record<string, any>,
any
>;
const { createElement } = runtime;
const { Component, createElement } = adapter.getRuntime();
const Div = divFactory();
const VisualDom = visualDomFactory();
const AppContext = contextFactory();
@ -125,7 +119,7 @@ export default function baseRendererFactory(): IBaseRenderComponent {
const DEFAULT_LOOP_ARG_INDEX = 'index';
let scopeIdx = 0;
return class BaseRenderer extends Component {
return class BaseRenderer extends Component<IBaseRendererProps, Record<string, any>> {
static displayName = 'BaseRenderer';
static defaultProps = {
@ -533,6 +527,10 @@ export default function baseRendererFactory(): IBaseRenderComponent {
{
componentName: schema.componentName,
componentId: schema.id,
enableStrictNotFoundMode: engine.props.enableStrictNotFoundMode,
ref: (ref: any) => {
ref && engine.props?.onCompGetRef(schema, ref);
},
},
this.__getSchemaChildrenVirtualDom(schema, scope, Comp),
);

View File

@ -4,14 +4,11 @@ import contextFactory from '../context';
import { isFileSchema, isEmpty } from '../utils';
import baseRendererFactory from './base';
import divFactory from '../components/Div';
import { IGeneralConstructor, IRenderComponent, IRendererProps, IRendererState } from '../types';
import { RootSchema } from '@alilc/lowcode-types';
import { IRenderComponent, IRendererProps, IRendererState } from '../types';
import { NodeSchema, RootSchema } from '@alilc/lowcode-types';
export default function rendererFactory(): IRenderComponent {
const runtime = adapter.getRuntime();
const Component = runtime.Component as IGeneralConstructor<IRendererProps, Record<string, any>>;
const PureComponent = runtime.PureComponent as IGeneralConstructor<IRendererProps, Record<string, any>>;
const { createElement, findDOMNode } = runtime;
const { PureComponent, Component, createElement, findDOMNode } = adapter.getRuntime();
const RENDERER_COMPS: any = adapter.getRenderers();
const BaseRenderer = baseRendererFactory();
const AppContext = contextFactory();
@ -21,7 +18,7 @@ export default function rendererFactory(): IRenderComponent {
const debug = Debug('renderer:entry');
class FaultComponent extends PureComponent {
class FaultComponent extends PureComponent<NodeSchema> {
render() {
// FIXME: errorlog
console.error('render error', this.props);
@ -35,17 +32,22 @@ export default function rendererFactory(): IRenderComponent {
color: '#ff0000',
border: '2px solid #ff0000',
},
}, '组件渲染异常,请查看控制台日志');
}, `${this.props.componentName || ''} 组件渲染异常,请查看控制台日志`);
}
}
class NotFoundComponent extends PureComponent {
class NotFoundComponent extends PureComponent<{
componentName: string;
} & IRendererProps> {
render() {
return createElement(Div, this.props, this.props.children || 'Component Not Found');
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 {
return class Renderer extends Component<IRendererProps> {
static displayName = 'Renderer';
state: Partial<IRendererState> = {};

View File

@ -21,12 +21,12 @@ interface IGeneralComponent<P = {}, S = {}, SS = any> extends ComponentLifecycle
}
export type IGeneralConstructor<
P = {
T = {
[key: string]: any;
}, S = {
[key: string]: any;
}, SS = any
> = new (props: any, context: any) => IGeneralComponent<P, S, SS>;
}, D = any
> = new <TT = T, SS = S, DD = D>(props: TT, context: any) => IGeneralComponent<TT, SS, DD>;
/**
* duck-typed History
@ -133,6 +133,11 @@ export interface IRendererProps {
* JSExpression 使 this 访
*/
thisRequiredInJSE?: boolean;
/**
* @default false
*
*/
enableStrictNotFoundMode?: boolean;
}
export interface IRendererState {