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; return engineConfig.get('thisRequiredInJSE') ?? true;
} }
get enableStrictNotFoundMode(): any {
return engineConfig.get('enableStrictNotFoundMode') ?? false;
}
@computed get componentsAsset(): Asset | undefined { @computed get componentsAsset(): Asset | undefined {
return this.get('componentsAsset'); return this.get('componentsAsset');
} }

View File

@ -176,12 +176,29 @@ export class ComponentMeta {
} }
private parseMetadata(metadata: ComponentMetadata) { 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._npm = npm || this._npm;
this._componentName = componentName; this._componentName = componentName;
// 额外转换逻辑 // 额外转换逻辑
this._transformedMetadata = this.transformMetadata(metadata); this._transformedMetadata = this.transformMetadata(_metadata);
const { title } = this._transformedMetadata; const { title } = this._transformedMetadata;
if (title) { if (title) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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