Skip to content

Commit

Permalink
fix: deep merge variants and overrides rather than overwrite with spread
Browse files Browse the repository at this point in the history
  • Loading branch information
alharris-at committed Nov 19, 2021
1 parent c819478 commit a779553
Show file tree
Hide file tree
Showing 8 changed files with 360 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -927,10 +927,30 @@ export default function ComplexTest4(
variantValues: { colors: \\"Red/Orange\\" },
},
];
const overrides = {
...getOverridesFromVariants(variants, props),
...overridesProp,
const mergeVariantsAndOverrides = (
variants: EscapeHatchProps,
overrides: EscapeHatchProps
): EscapeHatchProps => {
const overrideKeys = new Set(Object.keys(overrides));
const sharedKeys = Object.keys(variants).filter((variantKey) =>
overrideKeys.has(variantKey)
);
const merged = Object.fromEntries(
sharedKeys.map((sharedKey) => [
sharedKey,
{ ...variants[sharedKey], ...overrides[sharedKey] },
])
);
return {
...variants,
...overrides,
...merged,
};
};
const overrides = mergeVariantsAndOverrides(
getOverridesFromVariants(variants, props),
overridesProp || {}
);
return (
/* @ts-ignore: TS2322 */
<Flex
Expand Down Expand Up @@ -1491,10 +1511,30 @@ export default function CustomButton(
overrides: { Button: { width: \\"500\\" } },
},
];
const overrides = {
...getOverridesFromVariants(variants, props),
...overridesProp,
const mergeVariantsAndOverrides = (
variants: EscapeHatchProps,
overrides: EscapeHatchProps
): EscapeHatchProps => {
const overrideKeys = new Set(Object.keys(overrides));
const sharedKeys = Object.keys(variants).filter((variantKey) =>
overrideKeys.has(variantKey)
);
const merged = Object.fromEntries(
sharedKeys.map((sharedKey) => [
sharedKey,
{ ...variants[sharedKey], ...overrides[sharedKey] },
])
);
return {
...variants,
...overrides,
...merged,
};
};
const overrides = mergeVariantsAndOverrides(
getOverridesFromVariants(variants, props),
overridesProp || {}
);
return (
/* @ts-ignore: TS2322 */
<Button {...rest} {...getOverrideProps(overrides, \\"Button\\")}></Button>
Expand Down
255 changes: 238 additions & 17 deletions packages/codegen-ui-react/lib/react-studio-template-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,10 @@ export abstract class ReactStudioTemplateRenderer extends StudioTemplateRenderer
// TODO: In components, replace props.override with override (defined here).
}

if (isStudioComponentWithVariants(component)) {
statements.push(this.buildMergeOverridesFunction());
}

statements.push(this.buildOverridesDeclaration(isStudioComponentWithVariants(component)));

const authStatement = this.buildUseAuthenticatedUserStatement(component);
Expand Down Expand Up @@ -621,14 +625,245 @@ export abstract class ReactStudioTemplateRenderer extends StudioTemplateRenderer
);
}

private buildMergeOverridesFunction(): VariableStatement {
return factory.createVariableStatement(
undefined,
factory.createVariableDeclarationList(
[
factory.createVariableDeclaration(
factory.createIdentifier('mergeVariantsAndOverrides'),
undefined,
undefined,
factory.createArrowFunction(
undefined,
undefined,
[
factory.createParameterDeclaration(
undefined,
undefined,
undefined,
factory.createIdentifier('variants'),
undefined,
factory.createTypeReferenceNode(factory.createIdentifier('EscapeHatchProps'), undefined),
undefined,
),
factory.createParameterDeclaration(
undefined,
undefined,
undefined,
factory.createIdentifier('overrides'),
undefined,
factory.createTypeReferenceNode(factory.createIdentifier('EscapeHatchProps'), undefined),
undefined,
),
],
factory.createTypeReferenceNode(factory.createIdentifier('EscapeHatchProps'), undefined),
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
factory.createBlock(
[
factory.createVariableStatement(
undefined,
factory.createVariableDeclarationList(
[
factory.createVariableDeclaration(
factory.createIdentifier('overrideKeys'),
undefined,
undefined,
factory.createNewExpression(factory.createIdentifier('Set'), undefined, [
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier('Object'),
factory.createIdentifier('keys'),
),
undefined,
[factory.createIdentifier('overrides')],
),
]),
),
],
ts.NodeFlags.Const,
),
),
factory.createVariableStatement(
undefined,
factory.createVariableDeclarationList(
[
factory.createVariableDeclaration(
factory.createIdentifier('sharedKeys'),
undefined,
undefined,
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier('Object'),
factory.createIdentifier('keys'),
),
undefined,
[factory.createIdentifier('variants')],
),
factory.createIdentifier('filter'),
),
undefined,
[
factory.createArrowFunction(
undefined,
undefined,
[
factory.createParameterDeclaration(
undefined,
undefined,
undefined,
factory.createIdentifier('variantKey'),
undefined,
undefined,
undefined,
),
],
undefined,
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier('overrideKeys'),
factory.createIdentifier('has'),
),
undefined,
[factory.createIdentifier('variantKey')],
),
),
],
),
),
],
ts.NodeFlags.Const,
),
),
factory.createVariableStatement(
undefined,
factory.createVariableDeclarationList(
[
factory.createVariableDeclaration(
factory.createIdentifier('merged'),
undefined,
undefined,
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier('Object'),
factory.createIdentifier('fromEntries'),
),
undefined,
[
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier('sharedKeys'),
factory.createIdentifier('map'),
),
undefined,
[
factory.createArrowFunction(
undefined,
undefined,
[
factory.createParameterDeclaration(
undefined,
undefined,
undefined,
factory.createIdentifier('sharedKey'),
undefined,
undefined,
undefined,
),
],
undefined,
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
factory.createArrayLiteralExpression(
[
factory.createIdentifier('sharedKey'),
factory.createObjectLiteralExpression(
[
factory.createSpreadAssignment(
factory.createElementAccessExpression(
factory.createIdentifier('variants'),
factory.createIdentifier('sharedKey'),
),
),
factory.createSpreadAssignment(
factory.createElementAccessExpression(
factory.createIdentifier('overrides'),
factory.createIdentifier('sharedKey'),
),
),
],
false,
),
],
false,
),
),
],
),
],
),
),
],
ts.NodeFlags.Const,
),
),
factory.createReturnStatement(
factory.createObjectLiteralExpression(
[
factory.createSpreadAssignment(factory.createIdentifier('variants')),
factory.createSpreadAssignment(factory.createIdentifier('overrides')),
factory.createSpreadAssignment(factory.createIdentifier('merged')),
],
true,
),
),
],
true,
),
),
),
],
ts.NodeFlags.Const,
),
);
}

/**
* case: hasVariants = true => const overrides = { ...getOverridesFromVariants(variants, props) };
* case: hasVariants = false => const overrides = { ...overridesProp };
*/
private buildOverridesDeclaration(hasVariants: boolean): VariableStatement {
if (hasVariants) {
// TODO: ME
this.importCollection.addImport('@aws-amplify/ui-react', 'getOverridesFromVariants');
this.importCollection.addImport('@aws-amplify/ui-react', 'Variant');

return factory.createVariableStatement(
undefined,
factory.createVariableDeclarationList(
[
factory.createVariableDeclaration(
factory.createIdentifier('overrides'),
undefined,
undefined,
factory.createCallExpression(factory.createIdentifier('mergeVariantsAndOverrides'), undefined, [
factory.createCallExpression(factory.createIdentifier('getOverridesFromVariants'), undefined, [
factory.createIdentifier('variants'),
factory.createIdentifier('props'),
]),
factory.createBinaryExpression(
factory.createIdentifier('overridesProp'),
factory.createToken(ts.SyntaxKind.BarBarToken),
factory.createObjectLiteralExpression([], false),
),
]),
),
],
ts.NodeFlags.Const,
),
);
}

return factory.createVariableStatement(
Expand All @@ -639,23 +874,9 @@ export abstract class ReactStudioTemplateRenderer extends StudioTemplateRenderer
factory.createIdentifier('overrides'),
undefined,
undefined,
factory.createObjectLiteralExpression(
([] as ts.ObjectLiteralElementLike[])
.concat(
hasVariants
? [
factory.createSpreadAssignment(
factory.createCallExpression(
factory.createIdentifier('getOverridesFromVariants'),
undefined,
[factory.createIdentifier('variants'), factory.createIdentifier('props')],
),
),
]
: [],
)
.concat([factory.createSpreadAssignment(factory.createIdentifier('overridesProp'))]),
),
factory.createObjectLiteralExpression([
factory.createSpreadAssignment(factory.createIdentifier('overridesProp')),
]),
),
],
ts.NodeFlags.Const,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ describe('Generated Components', () => {
cy.get('#variants').get('#variant2').should('have.css', 'font-size', '40px');
cy.get('#variants').get('#variant3').should('have.css', 'width', '500px');
});

it('allows for use of both variants and overrides, prioritizing overrides if they collide', () => {
cy.visit('http://localhost:3000/component-tests');
cy.get('#variantAndOverrideDefault').contains('DefaultText');
cy.get('#variantAndOverrideVariantValue').contains('Hello');
cy.get('#variantAndOverrideOverrideApplied').contains('Overriden Text');
cy.get('#variantAndOverrideVariantValueAndNonOverlappingOverride').contains('Goodbye');
cy.get('#variantAndOverrideVariantValueAndOverlappingOverride').contains('Overriden Text');
});
});

describe('Data Binding', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
BasicComponentImage,
BasicComponentCustomRating,
ComponentWithVariant,
ComponentWithVariantAndOverrides,
SimplePropertyBindingDefaultValue,
BoundDefaultValue,
SimpleAndBoundDefaultValue,
Expand Down Expand Up @@ -210,6 +211,22 @@ export default function ComponentTests() {
<ComponentWithVariant id="variant1" variant="primary" />
<ComponentWithVariant id="variant2" variant="secondary" />
<ComponentWithVariant id="variant3" variant="primary" size="large" />
<ComponentWithVariantAndOverrides id="variantAndOverrideDefault" />
<ComponentWithVariantAndOverrides id="variantAndOverrideVariantValue" variant="greeting" />
<ComponentWithVariantAndOverrides
id="variantAndOverrideOverrideApplied"
overrides={{ Text: { children: 'Overriden Text' } }}
/>
<ComponentWithVariantAndOverrides
id="variantAndOverrideVariantValueAndNonOverlappingOverride"
variant="farewell"
overrides={{ Text: { color: 'red' } }}
/>
<ComponentWithVariantAndOverrides
id="variantAndOverrideVariantValueAndOverlappingOverride"
variant="farewell"
overrides={{ Text: { children: 'Overriden Text' } }}
/>
</div>
<div id="data-binding">
<h2>Data Binding</h2>
Expand Down
Loading

0 comments on commit a779553

Please sign in to comment.