Skip to content

Commit

Permalink
feat: concatenation binding implementation (#99)
Browse files Browse the repository at this point in the history
  • Loading branch information
frimfram authored Sep 29, 2021
1 parent 3e38fcc commit 1bfd428
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 69 deletions.
5 changes: 0 additions & 5 deletions packages/amplify-ui-codegen-schema/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
export * from './lib/types/index';
export * from './lib/types/primitives';
export * from './lib/types/studio-schema';

/* legacy
export * from "./lib/types/studio-component-property-type";
export * from "./lib/types/studio-component";
*/
47 changes: 46 additions & 1 deletion packages/amplify-ui-codegen-schema/lib/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ export type StudioComponentProperties = {
| FixedStudioComponentProperty
| BoundStudioComponentProperty
| CollectionStudioComponentProperty
| ConcatenatedStudioComponentProperty
| ConditionalStudioComponentProperty
| WorkflowStudioComponentProperty
| FormStudioComponentProperty;
};
Expand Down Expand Up @@ -269,6 +271,49 @@ export type CollectionStudioComponentProperty = {
defaultValue?: string;
};

/**
* Component property that contains concatenation of multiple properties
*/
export type ConcatenatedStudioComponentProperty = {
concat: (
| ConcatenatedStudioComponentProperty
| ConditionalStudioComponentProperty
| FixedStudioComponentProperty
| BoundStudioComponentProperty
| CollectionStudioComponentProperty
| WorkflowStudioComponentProperty
| FormStudioComponentProperty
)[];
};

/**
* Component property that represents a conditional expression
*/
export type ConditionalStudioComponentProperty = {
condition: {
property: string;
field: string;
operator: string;
operand: string | number | boolean;
then:
| FixedStudioComponentProperty
| BoundStudioComponentProperty
| CollectionStudioComponentProperty
| ConcatenatedStudioComponentProperty
| ConditionalStudioComponentProperty
| WorkflowStudioComponentProperty
| FormStudioComponentProperty;
else:
| FixedStudioComponentProperty
| BoundStudioComponentProperty
| CollectionStudioComponentProperty
| ConcatenatedStudioComponentProperty
| ConditionalStudioComponentProperty
| WorkflowStudioComponentProperty
| FormStudioComponentProperty;
};
};

/**
* This represents a component property that is configured with either
* data bound values
Expand Down Expand Up @@ -421,4 +466,4 @@ export type StudioComponentAuthBindingProperty = {
export type StudioComponentStorageBindingProperty = {
bucket: string;
key?: string;
};
};
170 changes: 107 additions & 63 deletions packages/studio-ui-codegen-react/lib/react-component-render-helper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {
ConcatenatedStudioComponentProperty,
ConditionalStudioComponentProperty,
FixedStudioComponentProperty,
BoundStudioComponentProperty,
CollectionStudioComponentProperty,
Expand All @@ -8,7 +10,7 @@ import {
StudioComponentChild,
} from '@amzn/amplify-ui-codegen-schema';

import { factory, JsxAttribute, JsxExpression, StringLiteral, SyntaxKind } from 'typescript';
import { Expression, factory, JsxAttribute, TemplateSpan, StringLiteral, SyntaxKind, JsxExpression } from 'typescript';

import { ImportCollection } from './import-collection';

Expand All @@ -23,58 +25,50 @@ export function getComponentPropName(componentName?: string): string {
return 'ComponentWithoutNameProps';
}

export function isFixedPropertyWithValue(
prop:
| FixedStudioComponentProperty
| BoundStudioComponentProperty
| CollectionStudioComponentProperty
| WorkflowStudioComponentProperty
| FormStudioComponentProperty,
): prop is FixedStudioComponentProperty {
type ComponentPropertyValueTypes =
| ConcatenatedStudioComponentProperty
| ConditionalStudioComponentProperty
| FixedStudioComponentProperty
| BoundStudioComponentProperty
| CollectionStudioComponentProperty
| WorkflowStudioComponentProperty
| FormStudioComponentProperty;

export function isFixedPropertyWithValue(prop: ComponentPropertyValueTypes): prop is FixedStudioComponentProperty {
return 'value' in prop;
}

export function isBoundProperty(
prop:
| FixedStudioComponentProperty
| BoundStudioComponentProperty
| CollectionStudioComponentProperty
| WorkflowStudioComponentProperty
| FormStudioComponentProperty,
): prop is BoundStudioComponentProperty {
export function isBoundProperty(prop: ComponentPropertyValueTypes): prop is BoundStudioComponentProperty {
return 'bindingProperties' in prop;
}

export function isCollectionItemBoundProperty(
prop:
| FixedStudioComponentProperty
| BoundStudioComponentProperty
| CollectionStudioComponentProperty
| WorkflowStudioComponentProperty
| FormStudioComponentProperty,
prop: ComponentPropertyValueTypes,
): prop is CollectionStudioComponentProperty {
return 'collectionBindingProperties' in prop;
}

export function isConcatenatedProperty(prop: ComponentPropertyValueTypes): prop is ConcatenatedStudioComponentProperty {
return 'concat' in prop;
}

export function isDefaultValueOnly(
prop:
| FixedStudioComponentProperty
| BoundStudioComponentProperty
| CollectionStudioComponentProperty
| WorkflowStudioComponentProperty
| FormStudioComponentProperty,
prop: ComponentPropertyValueTypes,
): prop is CollectionStudioComponentProperty | BoundStudioComponentProperty {
return 'defaultValue' in prop && !(isCollectionItemBoundProperty(prop) || isBoundProperty(prop));
}

export function buildBindingExpression(prop: BoundStudioComponentProperty): Expression {
return prop.bindingProperties.field === undefined
? factory.createIdentifier(prop.bindingProperties.property)
: factory.createPropertyAccessExpression(
factory.createIdentifier(prop.bindingProperties.property),
prop.bindingProperties.field,
);
}

export function buildBindingAttr(prop: BoundStudioComponentProperty, propName: string): JsxAttribute {
const expr =
prop.bindingProperties.field === undefined
? factory.createIdentifier(prop.bindingProperties.property)
: factory.createPropertyAccessExpression(
factory.createIdentifier(prop.bindingProperties.property),
prop.bindingProperties.field,
);
const expr = buildBindingExpression(prop);

const attr = factory.createJsxAttribute(
factory.createIdentifier(propName),
Expand All @@ -83,11 +77,10 @@ export function buildBindingAttr(prop: BoundStudioComponentProperty, propName: s
return attr;
}

export function buildBindingAttrWithDefault(
export function buildBindingWithDefaultExpression(
prop: BoundStudioComponentProperty,
propName: string,
defaultValue: string,
): JsxAttribute {
): Expression {
const rightExpr = factory.createStringLiteral(defaultValue);
const leftExpr =
prop.bindingProperties.field === undefined
Expand All @@ -97,15 +90,23 @@ export function buildBindingAttrWithDefault(
prop.bindingProperties.field,
);

const binaryExpr = factory.createBinaryExpression(leftExpr, factory.createToken(SyntaxKind.BarBarToken), rightExpr);
return factory.createBinaryExpression(leftExpr, factory.createToken(SyntaxKind.BarBarToken), rightExpr);
}

export function buildBindingAttrWithDefault(
prop: BoundStudioComponentProperty,
propName: string,
defaultValue: string,
): JsxAttribute {
const binaryExpr = buildBindingWithDefaultExpression(prop, defaultValue);
const attr = factory.createJsxAttribute(
factory.createIdentifier(propName),
factory.createJsxExpression(undefined, binaryExpr),
);
return attr;
}

export function buildFixedAttr(prop: FixedStudioComponentProperty, propName: string): JsxAttribute {
export function buildFixedExpression(prop: FixedStudioComponentProperty): StringLiteral | JsxExpression {
const currentPropValue = prop.value;
let propValueExpr: StringLiteral | JsxExpression = factory.createStringLiteral(currentPropValue.toString());
switch (typeof currentPropValue) {
Expand All @@ -121,30 +122,33 @@ export function buildFixedAttr(prop: FixedStudioComponentProperty, propName: str
default:
break;
}
return factory.createJsxAttribute(factory.createIdentifier(propName), propValueExpr);
return propValueExpr;
}

export function buildCollectionBindingAttr(prop: CollectionStudioComponentProperty, propName: string): JsxAttribute {
const expr =
prop.collectionBindingProperties.field === undefined
? factory.createIdentifier('item')
: factory.createPropertyAccessExpression(
factory.createIdentifier('item'),
prop.collectionBindingProperties.field,
);
export function buildFixedAttr(prop: FixedStudioComponentProperty, propName: string): JsxAttribute {
const expr = buildFixedExpression(prop);
return factory.createJsxAttribute(factory.createIdentifier(propName), expr);
}

export function buildCollectionBindingExpression(prop: CollectionStudioComponentProperty): Expression {
return prop.collectionBindingProperties.field === undefined
? factory.createIdentifier('item')
: factory.createPropertyAccessExpression(factory.createIdentifier('item'), prop.collectionBindingProperties.field);
}

export function buildCollectionBindingAttr(prop: CollectionStudioComponentProperty, propName: string): JsxAttribute {
const expr = buildCollectionBindingExpression(prop);
const attr = factory.createJsxAttribute(
factory.createIdentifier(propName),
factory.createJsxExpression(undefined, expr),
);
return attr;
}

export function buildCollectionBindingAttrWithDefault(
export function buildCollectionBindingWithDefaultExpression(
prop: CollectionStudioComponentProperty,
propName: string,
defaultValue: string,
): JsxAttribute {
): Expression {
const rightExpr = factory.createStringLiteral(defaultValue);
const leftExpr =
prop.collectionBindingProperties.field === undefined
Expand All @@ -154,23 +158,60 @@ export function buildCollectionBindingAttrWithDefault(
prop.collectionBindingProperties.field,
);

const binaryExpr = factory.createBinaryExpression(leftExpr, factory.createToken(SyntaxKind.BarBarToken), rightExpr);
return factory.createBinaryExpression(leftExpr, factory.createToken(SyntaxKind.BarBarToken), rightExpr);
}

export function buildCollectionBindingAttrWithDefault(
prop: CollectionStudioComponentProperty,
propName: string,
defaultValue: string,
): JsxAttribute {
const binaryExpr = buildCollectionBindingWithDefaultExpression(prop, defaultValue);
const attr = factory.createJsxAttribute(
factory.createIdentifier(propName),
factory.createJsxExpression(undefined, binaryExpr),
);
return attr;
}

export function buildOpeningElementAttributes(
prop:
| FixedStudioComponentProperty
| BoundStudioComponentProperty
| CollectionStudioComponentProperty
| WorkflowStudioComponentProperty
| FormStudioComponentProperty,
propName: string,
): JsxAttribute {
export function buildConcatExpression(prop: ConcatenatedStudioComponentProperty): Expression {
const expressions: Expression[] = [];
prop.concat.forEach((propItem) => {
if (isFixedPropertyWithValue(propItem)) {
expressions.push(buildFixedExpression(propItem));
} else if (isBoundProperty(propItem)) {
const expr =
propItem.defaultValue === undefined
? buildBindingExpression(propItem)
: buildBindingWithDefaultExpression(propItem, propItem.defaultValue);
expressions.push(expr);
} else if (isCollectionItemBoundProperty(propItem)) {
const expr =
propItem.defaultValue === undefined
? buildCollectionBindingExpression(propItem)
: buildCollectionBindingWithDefaultExpression(propItem, propItem.defaultValue);
expressions.push(expr);
} else if (isConcatenatedProperty(propItem)) {
expressions.push(buildConcatExpression(propItem));
}
});
const templateSpans: TemplateSpan[] = [];
expressions.forEach((expr, index) => {
const span =
index === expressions.length - 1
? factory.createTemplateSpan(expr, factory.createTemplateTail('', ''))
: factory.createTemplateSpan(expr, factory.createTemplateMiddle('', ''));
templateSpans.push(span);
});
return factory.createTemplateExpression(factory.createTemplateHead('', ''), templateSpans);
}

export function buildConcatAttr(prop: ConcatenatedStudioComponentProperty, propName: string): JsxAttribute {
const expr = buildConcatExpression(prop);
return factory.createJsxAttribute(factory.createIdentifier(propName), factory.createJsxExpression(undefined, expr));
}

export function buildOpeningElementAttributes(prop: ComponentPropertyValueTypes, propName: string): JsxAttribute {
if (isFixedPropertyWithValue(prop)) {
return buildFixedAttr(prop, propName);
}
Expand All @@ -188,6 +229,9 @@ export function buildOpeningElementAttributes(
: buildCollectionBindingAttrWithDefault(prop, propName, prop.defaultValue);
return attr;
}
if (isConcatenatedProperty(prop)) {
return buildConcatAttr(prop, propName);
}
return factory.createJsxAttribute(factory.createIdentifier(propName), undefined);
}
export function addBindingPropertiesImports(
Expand Down
47 changes: 47 additions & 0 deletions packages/test-generator/lib/concatTest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"id": "1234-5678-9010",
"componentType": "Button",
"name": "CustomButton",
"bindingProperties": {
"width": {
"type": "Number"
},
"buttonUser": {
"type": "Data",
"bindingProperties": {
"model": "User"
}
},
"buttonColor": {
"type": "String"
}
},
"properties": {
"label": {
"concat": [
{
"bindingProperties": {
"property": "buttonUser",
"field": "firstname"
},
"defaultValue": "Harrison"
},
{
"value": " "
},
{
"bindingProperties": {
"property": "buttonUser",
"field": "lastname"
},
"defaultValue": "Spain"
}
]
},
"labelWidth": {
"bindingProperties": {
"property": "width"
}
}
}
}
Loading

0 comments on commit 1bfd428

Please sign in to comment.