Skip to content

Commit

Permalink
feat: concatenation binding implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
frimfram committed Sep 29, 2021
1 parent 3e38fcc commit 59dc91c
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 75 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;
};
};
179 changes: 110 additions & 69 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 } 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,54 +90,62 @@ 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): Expression {
const currentPropValue = prop.value;
let propValueExpr: StringLiteral | JsxExpression = factory.createStringLiteral(currentPropValue.toString());
let propValueExpr: Expression = factory.createStringLiteral(currentPropValue.toString());
switch (typeof currentPropValue) {
case 'number':
propValueExpr = factory.createJsxExpression(undefined, factory.createNumericLiteral(currentPropValue, undefined));
propValueExpr = factory.createNumericLiteral(currentPropValue, undefined);
break;
case 'boolean':
propValueExpr = factory.createJsxExpression(
undefined,
currentPropValue ? factory.createTrue() : factory.createFalse(),
);
propValueExpr = currentPropValue ? factory.createTrue() : factory.createFalse();
break;
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), factory.createJsxExpression(undefined, 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 +155,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 +226,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
Loading

0 comments on commit 59dc91c

Please sign in to comment.