Skip to content

Commit

Permalink
fix: export aliased component name on data model collision
Browse files Browse the repository at this point in the history
  • Loading branch information
alharris-at committed Mar 2, 2022
1 parent eb434d3 commit 63e5317
Show file tree
Hide file tree
Showing 11 changed files with 168 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,47 @@ export default function DataBindingNamedClass(
"
`;

exports[`amplify render tests bindings data supports bindings with the same name as a model 1`] = `
"/* eslint-disable */
import React from \\"react\\";
import {
EscapeHatchProps,
createDataStorePredicate,
getOverrideProps,
useDataStoreBinding,
} from \\"@aws-amplify/ui-react/internal\\";
import { User } from \\"../models\\";
import { Text, TextProps } from \\"@aws-amplify/ui-react\\";

export type UserProps = React.PropsWithChildren<
Partial<TextProps> & {
user?: User;
} & {
overrides?: EscapeHatchProps | undefined | null;
}
>;
export default function User0(props: UserProps): React.ReactElement {
const { user: userProp, overrides, ...rest } = props;
const userFilterObj = { field: \\"firstName\\", operand: \\"Al\\", operator: \\"eq\\" };
const userFilter = createDataStorePredicate<User>(userFilterObj);
const userDataStore = useDataStoreBinding({
type: \\"collection\\",
model: User,
criteria: userFilter,
}).items[0];
const user = userProp !== undefined ? userProp : userDataStore;
return (
/* @ts-ignore: TS2322 */
<Text
children={user?.name}
{...rest}
{...getOverrideProps(overrides, \\"User\\")}
></Text>
);
}
"
`;

exports[`amplify render tests collection should render collection with data binding 1`] = `
"/* eslint-disable */
import React from \\"react\\";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
getSyntaxKindToken,
buildChildElement,
buildConditionalExpression,
getComponentNameWithoutCollision,
} from '../react-component-render-helper';

import { assertASTMatchesSnapshot } from './__utils__';
Expand Down Expand Up @@ -295,4 +296,32 @@ describe('react-component-render-helper', () => {
).toThrow('Parsed value 18 and type boolean mismatch');
});
});

describe('getComponentNameWithoutCollision', () => {
const generateComponentMetadataWithModels = (requiredDataModels: string[]): ComponentMetadata => {
return {
requiredDataModels,
hasAuthBindings: false,
stateReferences: [],
componentNameToTypeMap: {},
};
};

it('returns the component name with no collision', () => {
expect(getComponentNameWithoutCollision('User', generateComponentMetadataWithModels([]))).toEqual('User');
});

it('returns a component name with suffix if there is a collision', () => {
expect(getComponentNameWithoutCollision('User', generateComponentMetadataWithModels(['User']))).toEqual('User0');
});

it('searches for a valid name until it finds one', () => {
expect(
getComponentNameWithoutCollision(
'User',
generateComponentMetadataWithModels(['User', 'User0', 'User1', 'User2']),
),
).toEqual('User3');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,12 @@ describe('amplify render tests', () => {
it('supports bindings with reserved keywords', () => {
expect(generateWithAmplifyRenderer('bindings/data/dataBindingNamedClass').componentText).toMatchSnapshot();
});

it('supports bindings with the same name as a model', () => {
expect(
generateWithAmplifyRenderer('bindings/data/dataBindingWithSameNameAsComponent').componentText,
).toMatchSnapshot();
});
});
});
});
25 changes: 25 additions & 0 deletions packages/codegen-ui-react/lib/react-component-render-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -631,3 +631,28 @@ export function getSetStateName(stateReference: StateStudioComponentProperty): s
const stateName = getStateName(stateReference);
return ['set', stateName.charAt(0).toUpperCase() + stateName.slice(1)].join('');
}

/**
* If we determine there are other contextual contraints we wish to apply to our component names, logic
* should be added here.
*/
function doesComponentNameCollideWithContext(componentName: string, componentMetadata: ComponentMetadata): boolean {
return componentMetadata.requiredDataModels.includes(componentName);
}

/**
* If the component name collides with some other name in component context, suffix w/ a digit, and search
* that suffix space until you find a viable name. Because we expose this as a default export, the name
* is corrected in the `index` file which customers can import from.
*/
export function getComponentNameWithoutCollision(componentName: string, componentMetadata: ComponentMetadata): string {
if (!doesComponentNameCollideWithContext(componentName, componentMetadata)) {
return componentName;
}

let suffix = 0;
while (doesComponentNameCollideWithContext(`${componentName}${suffix}`, componentMetadata)) {
suffix += 1;
}
return `${componentName}${suffix}`;
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ import { ImportCollection, ImportSource, ImportValue } from './imports';
import { ReactOutputManager } from './react-output-manager';
import { ReactRenderConfig, ScriptKind, scriptKindToFileExtension } from './react-render-config';
import SampleCodeRenderer from './amplify-ui-renderers/sampleCodeRenderer';
import { getComponentPropName } from './react-component-render-helper';
import { getComponentNameWithoutCollision, getComponentPropName } from './react-component-render-helper';
import {
transpile,
buildPrinter,
Expand Down Expand Up @@ -212,6 +212,7 @@ export abstract class ReactStudioTemplateRenderer extends StudioTemplateRenderer
renderExport: boolean,
): FunctionDeclaration {
const componentPropType = getComponentPropName(componentName);
const componentNameWithoutCollision = getComponentNameWithoutCollision(componentName, this.componentMetadata);
const jsxStatement = factory.createReturnStatement(
factory.createParenthesizedExpression(
this.renderConfig.script !== ScriptKind.TSX
Expand All @@ -236,7 +237,7 @@ export abstract class ReactStudioTemplateRenderer extends StudioTemplateRenderer
undefined,
modifiers,
undefined,
factory.createIdentifier(componentName),
factory.createIdentifier(componentNameWithoutCollision),
typeParameter ? typeParameter.declaration() : undefined,
[
factory.createParameterDeclaration(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"id": "1234-5678-9010",
"componentType": "Text",
"name": "User",
"bindingProperties": {
"user": {
"type": "Data",
"bindingProperties": {
"model": "User",
"predicate": {
"field": "firstName",
"operand": "Al",
"operator": "eq"
}
}
}
},
"properties": {
"label": {
"bindingProperties": {
"property": "user",
"field": "name"
}
}
},
"schemaVersion": "1.0"
}
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ const EXPECTED_SUCCESSFUL_CASES = new Set([
'CreateModelWithComplexTypes',
'InitialValueBindings',
'DataBindingNamedClass',
'User',
]);
const EXPECTED_INVALID_INPUT_CASES = new Set([
'ComponentMissingProperties',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,4 +366,10 @@ describe('Generated Components', () => {
cy.get('#reserved-keywords').find('.amplify-text').should('have.text', 'biology');
});
});

describe('Conflicting names', () => {
it('renders with same name as a data model', () => {
cy.get('#user-component').contains('LUser2');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import {
SearchableCollection,
ComponentWithAuthBinding,
DataBindingNamedClass,
User as UserComponent,
} from './ui-components'; // eslint-disable-line import/extensions
import { initializeAuthMockData } from './mock-utils';

Expand Down Expand Up @@ -365,6 +366,7 @@ export default function ComponentTests() {
<div id="reserved-keywords">
<DataBindingNamedClass class={new Class({ name: 'biology' })} />
</div>
<UserComponent id="user-component" />
</AmplifyProvider>
);
}
27 changes: 27 additions & 0 deletions packages/test-generator/lib/components/bindings/data/User.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"id": "1234-5678-9010",
"componentType": "Text",
"name": "User",
"bindingProperties": {
"user": {
"type": "Data",
"bindingProperties": {
"model": "User",
"predicate": {
"field": "firstName",
"operand": "Another",
"operator": "eq"
}
}
}
},
"properties": {
"label": {
"bindingProperties": {
"property": "user",
"field": "lastName"
}
}
},
"schemaVersion": "1.0"
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@
limitations under the License.
*/
export { default as DataBindingNamedClass } from './dataBindingNamedClass.json';
export { default as User } from './User.json';

0 comments on commit 63e5317

Please sign in to comment.