mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2024-11-27 08:39:59 +08:00
fix: the renaming, types and tests stuffs
This commit is contained in:
parent
b6b7083e15
commit
b0c88fd872
@ -9,52 +9,60 @@ describe('Field test', () => {
|
||||
it('parse static property', () => {
|
||||
const field = new FieldModel('Hello, world!');
|
||||
expect(field.isDynamic).toEqual(false);
|
||||
expect(field.refs).toEqual({});
|
||||
expect(field.refComponentInfos).toEqual({});
|
||||
expect(field.rawValue).toEqual('Hello, world!');
|
||||
});
|
||||
|
||||
it('parse expression', () => {
|
||||
const field = new FieldModel('{{input.value}} + {{list[0].text}}');
|
||||
expect(field.isDynamic).toEqual(true);
|
||||
expect(field.refs['input'].properties).toEqual(['value']);
|
||||
expect(field.refs['list'].properties).toEqual(['[0]', '[0].text']);
|
||||
expect(field.refComponentInfos['input' as ComponentId].refProperties).toEqual([
|
||||
'value',
|
||||
]);
|
||||
expect(field.refComponentInfos['list' as ComponentId].refProperties).toEqual([
|
||||
'[0]',
|
||||
'[0].text',
|
||||
]);
|
||||
expect(field.rawValue).toEqual('{{input.value}} + {{list[0].text}}');
|
||||
});
|
||||
|
||||
it('parse inline variable in expression', () => {
|
||||
const field = new FieldModel('{{ [].length }}');
|
||||
expect(field.isDynamic).toEqual(true);
|
||||
expect(field.refs).toEqual({});
|
||||
expect(field.refComponentInfos).toEqual({});
|
||||
});
|
||||
|
||||
it('parse object property', () => {
|
||||
const field = new FieldModel({ raw: '{{input.value}}', format: 'md' });
|
||||
expect(field.isDynamic).toEqual(false);
|
||||
expect(field.refs).toEqual({});
|
||||
expect(field.refComponentInfos).toEqual({});
|
||||
expect(field.rawValue).toEqual({ raw: '{{input.value}}', format: 'md' });
|
||||
expect(field.getProperty('raw')!.rawValue).toEqual('{{input.value}}');
|
||||
expect(field.getProperty('raw')!.isDynamic).toEqual(true);
|
||||
expect(field.getProperty('raw')!.refs['input'].properties).toEqual(['value']);
|
||||
expect(
|
||||
field.getProperty('raw')!.refComponentInfos['input' as ComponentId].refProperties
|
||||
).toEqual(['value']);
|
||||
expect(field.getProperty('format')!.rawValue).toEqual('md');
|
||||
expect(field.getProperty('format')!.isDynamic).toEqual(false);
|
||||
expect(field.getProperty('format')!.refs).toEqual({});
|
||||
expect(field.getProperty('format')!.refComponentInfos).toEqual({});
|
||||
});
|
||||
|
||||
it('parse array property', () => {
|
||||
const field = new FieldModel({ data: [1, '{{fetch.data}}'] });
|
||||
expect(field.isDynamic).toEqual(false);
|
||||
expect(field.refs).toEqual({});
|
||||
expect(field.refComponentInfos).toEqual({});
|
||||
expect(field.rawValue).toEqual({ data: [1, '{{fetch.data}}'] });
|
||||
expect(field.getProperty('data')!.rawValue).toEqual([1, '{{fetch.data}}']);
|
||||
expect(field.getProperty('data')!.isDynamic).toEqual(false);
|
||||
expect(field.getProperty('data')!.refs).toEqual({});
|
||||
expect(field.getProperty('data')!.refComponentInfos).toEqual({});
|
||||
expect(field.getProperty('data')!.getProperty(0)!.rawValue).toEqual(1);
|
||||
expect(field.getProperty('data')!.getProperty(0)!.isDynamic).toEqual(false);
|
||||
expect(field.getProperty('data')!.getProperty(1)!.rawValue).toEqual('{{fetch.data}}');
|
||||
expect(field.getProperty('data')!.getProperty(1)!.isDynamic).toEqual(true);
|
||||
expect(field.getProperty('data')!.getProperty(1)!.refs['fetch'].properties).toEqual([
|
||||
'data',
|
||||
]);
|
||||
expect(
|
||||
field.getProperty('data')!.getProperty(1)!.refComponentInfos['fetch' as ComponentId]
|
||||
.refProperties
|
||||
).toEqual(['data']);
|
||||
});
|
||||
|
||||
it('update array property', () => {
|
||||
@ -85,18 +93,25 @@ describe('Field test', () => {
|
||||
const text = appModel.getComponentById('text' as ComponentId);
|
||||
const button = appModel.getComponentById('button' as ComponentId);
|
||||
|
||||
input.changeId('input1' as ComponentId);
|
||||
input!.changeId('input1' as ComponentId);
|
||||
|
||||
expect(input.id).toEqual('input1' as ComponentId);
|
||||
expect(text.properties.rawValue).toEqual({
|
||||
expect(input!.id).toEqual('input1' as ComponentId);
|
||||
expect(text!.properties.rawValue).toEqual({
|
||||
value: {
|
||||
raw: "pre {{(function () {\n const object = { value: input1.value + input1.notExistKey };\n return '-' + object.value + '-';\n}());}} end",
|
||||
format: 'plain',
|
||||
},
|
||||
string: 'Please input here',
|
||||
expressionString: "{{ 'input' }}",
|
||||
array: ['input'],
|
||||
expressionArray: "{{['input']}}",
|
||||
object: { input: 'input' },
|
||||
expressionObject: "{{{'input': 'input'}}}",
|
||||
});
|
||||
expect(
|
||||
button.traits.find(trait => trait.type === `${CORE_VERSION}/${CoreTraitName.Event}`)
|
||||
.properties.rawValue
|
||||
button!.traits.find(
|
||||
trait => trait.type === `${CORE_VERSION}/${CoreTraitName.Event}`
|
||||
)!.properties.rawValue
|
||||
).toEqual({
|
||||
handlers: [
|
||||
{
|
||||
|
@ -252,6 +252,12 @@ export const ChangeIdMockSchema: ComponentSchema[] = [
|
||||
raw: "pre {{(function () {\n const object = { value: input.value + input.notExistKey };\n return '-' + object.value + '-';\n}());}} end",
|
||||
format: 'plain',
|
||||
},
|
||||
string: 'Please input here',
|
||||
expressionString: "{{ 'input' }}",
|
||||
array: ['input'],
|
||||
expressionArray: "{{['input']}}",
|
||||
object: { input: 'input' },
|
||||
expressionObject: "{{{'input': 'input'}}}",
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
|
@ -12,24 +12,24 @@ import {
|
||||
IFieldModel,
|
||||
ModuleId,
|
||||
RefInfo,
|
||||
ASTNode,
|
||||
AppModelEventType,
|
||||
} from './IAppModel';
|
||||
import escodegen from 'escodegen';
|
||||
import { JSONSchema7 } from 'json-schema';
|
||||
|
||||
type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;
|
||||
|
||||
export class FieldModel implements IFieldModel {
|
||||
isDynamic = false;
|
||||
refs: Record<ComponentId | ModuleId, RefInfo> = {};
|
||||
private nodes: Record<string, acorn.Node> = {};
|
||||
refComponentInfos: Record<ComponentId | ModuleId, RefInfo> = {};
|
||||
private astNodes: Record<string, ASTNode> = {};
|
||||
private value: unknown | Array<IFieldModel> | Record<string, IFieldModel>;
|
||||
|
||||
constructor(
|
||||
value: unknown,
|
||||
public spec?: JSONSchema7 & SpecOptions,
|
||||
public appModel?: IAppModel,
|
||||
public componentModel?: IComponentModel,
|
||||
public traitModel?: ITraitModel
|
||||
private appModel?: IAppModel,
|
||||
private componentModel?: IComponentModel,
|
||||
private traitModel?: ITraitModel
|
||||
) {
|
||||
this.update(value);
|
||||
this.appModel?.emitter.on('idChange', this.onReferenceIdChange.bind(this));
|
||||
@ -137,14 +137,14 @@ export class FieldModel implements IFieldModel {
|
||||
parseExpression(this.value as string).filter(exp => typeof exp !== 'string')
|
||||
);
|
||||
|
||||
this.refs = {};
|
||||
this.nodes = {};
|
||||
this.refComponentInfos = {};
|
||||
this.astNodes = {};
|
||||
|
||||
exps.forEach(exp => {
|
||||
let lastIdentifier: ComponentId = '' as ComponentId;
|
||||
const node = (acornLoose as typeof acorn).parse(exp, { ecmaVersion: 2020 });
|
||||
|
||||
this.nodes[exp] = node;
|
||||
this.astNodes[exp] = node as ASTNode;
|
||||
|
||||
simpleWalk(node, {
|
||||
Expression: expressionNode => {
|
||||
@ -155,12 +155,14 @@ export class FieldModel implements IFieldModel {
|
||||
expressionNode.end
|
||||
) as ComponentId;
|
||||
|
||||
if (this.refs[key]) {
|
||||
this.refs[key].nodes.push(expressionNode as Flatten<RefInfo['nodes']>);
|
||||
if (this.refComponentInfos[key]) {
|
||||
this.refComponentInfos[key].componentIdASTNodes.push(
|
||||
expressionNode as ASTNode
|
||||
);
|
||||
} else {
|
||||
this.refs[key] = {
|
||||
nodes: [expressionNode as Flatten<RefInfo['nodes']>],
|
||||
properties: [],
|
||||
this.refComponentInfos[key] = {
|
||||
componentIdASTNodes: [expressionNode as ASTNode],
|
||||
refProperties: [],
|
||||
};
|
||||
}
|
||||
lastIdentifier = key;
|
||||
@ -172,7 +174,7 @@ export class FieldModel implements IFieldModel {
|
||||
if (path.startsWith('.')) {
|
||||
path = path.slice(1, path.length);
|
||||
}
|
||||
this.refs[lastIdentifier]?.properties.push(path);
|
||||
this.refComponentInfos[lastIdentifier]?.refProperties.push(path);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
@ -181,7 +183,7 @@ export class FieldModel implements IFieldModel {
|
||||
});
|
||||
}
|
||||
|
||||
onReferenceIdChange({ oldId, newId }: { oldId: ComponentId; newId: ComponentId }) {
|
||||
private onReferenceIdChange({ oldId, newId }: AppModelEventType['idChange']) {
|
||||
if (!this.componentModel) {
|
||||
return;
|
||||
}
|
||||
@ -192,20 +194,20 @@ export class FieldModel implements IFieldModel {
|
||||
}
|
||||
this.componentModel._isDirty = true;
|
||||
this.update(newId);
|
||||
} else if (this.refs[oldId]) {
|
||||
} else if (this.refComponentInfos[oldId]) {
|
||||
const exps = parseExpression(this.value as string);
|
||||
const newExps = exps.map(exp => {
|
||||
const node = this.nodes[exp.toString()];
|
||||
const node = this.astNodes[exp.toString()];
|
||||
|
||||
if (node) {
|
||||
const ref = this.refs[oldId];
|
||||
const ref = this.refComponentInfos[oldId];
|
||||
|
||||
ref.nodes.forEach(refNode => {
|
||||
ref.componentIdASTNodes.forEach(refNode => {
|
||||
refNode.name = newId;
|
||||
});
|
||||
|
||||
this.refs[newId] = ref;
|
||||
delete this.refs[oldId];
|
||||
this.refComponentInfos[newId] = ref;
|
||||
delete this.refComponentInfos[oldId];
|
||||
|
||||
return [escodegen.generate(node)];
|
||||
}
|
||||
@ -214,6 +216,9 @@ export class FieldModel implements IFieldModel {
|
||||
});
|
||||
const value = expChunkToString(newExps);
|
||||
|
||||
if (this.traitModel) {
|
||||
this.traitModel._isDirty = true;
|
||||
}
|
||||
this.componentModel._isDirty = true;
|
||||
this.update(value);
|
||||
}
|
||||
|
@ -113,7 +113,6 @@ export interface IComponentModel {
|
||||
|
||||
export interface ITraitModel {
|
||||
// trait id only exists in model, doesn't exist in schema
|
||||
appModel: IAppModel;
|
||||
spec: RuntimeTrait;
|
||||
id: TraitId;
|
||||
parent: IComponentModel;
|
||||
@ -126,15 +125,15 @@ export interface ITraitModel {
|
||||
updateProperty: (key: string, value: any) => void;
|
||||
}
|
||||
|
||||
export type ASTNode = Node & { name: string };
|
||||
|
||||
export type RefInfo = {
|
||||
nodes: (Node & { name: string })[];
|
||||
properties: string[];
|
||||
componentIdASTNodes: ASTNode[];
|
||||
refProperties: string[];
|
||||
};
|
||||
|
||||
export interface IFieldModel {
|
||||
// value: any;
|
||||
appModel?: IAppModel;
|
||||
componentModel?: IComponentModel;
|
||||
spec?: JSONSchema7 & SpecOptions;
|
||||
isDynamic: boolean;
|
||||
rawValue: any;
|
||||
@ -142,7 +141,6 @@ export interface IFieldModel {
|
||||
getProperty: (key: string) => IFieldModel | void;
|
||||
getValue: () => unknown | void | IFieldModel;
|
||||
traverse: (cb: (f: IFieldModel, key: string) => void) => void;
|
||||
onReferenceIdChange: (params: AppModelEventType['idChange']) => void;
|
||||
// ids of used components in the expression
|
||||
refs: Record<ComponentId | ModuleId, RefInfo>;
|
||||
refComponentInfos: Record<ComponentId | ModuleId, RefInfo>;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ export class TraitModel implements ITraitModel {
|
||||
constructor(
|
||||
trait: TraitSchema,
|
||||
private registry: RegistryInterface,
|
||||
public appModel: IAppModel,
|
||||
private appModel: IAppModel,
|
||||
public parent: IComponentModel
|
||||
) {
|
||||
this.schema = trait;
|
||||
|
@ -67,9 +67,9 @@ class ExpressionValidatorRule implements PropertiesValidatorRule {
|
||||
|
||||
// validate expression
|
||||
properties.traverse((fieldModel, key) => {
|
||||
Object.keys(fieldModel.refs).forEach((id: string) => {
|
||||
Object.keys(fieldModel.refComponentInfos).forEach((id: string) => {
|
||||
const targetComponent = appModel.getComponentById(id as ComponentId);
|
||||
const paths = fieldModel.refs[id as ComponentId].properties;
|
||||
const paths = fieldModel.refComponentInfos[id as ComponentId].refProperties;
|
||||
|
||||
if (targetComponent) {
|
||||
// case 1: id is a component
|
||||
|
Loading…
Reference in New Issue
Block a user