diff --git a/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap b/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap index 8e3b3ccb4..a0eaa4257 100644 --- a/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap +++ b/packages/codegen-ui-react/lib/__tests__/__snapshots__/studio-ui-codegen-react-forms.test.ts.snap @@ -3445,7 +3445,7 @@ exports[`amplify form renderer tests datastore form tests should generate a crea "/* eslint-disable */ import * as React from \\"react\\"; import { fetchByPath, validateField } from \\"./utils\\"; -import { Member, Team } from \\"../models\\"; +import { Member, Team as Team0 } from \\"../models\\"; import { getOverrideProps, useDataStoreBinding, @@ -3622,14 +3622,14 @@ export default function MyMemberForm(props) { } = props; const initialValues = { name: undefined, - team: undefined, + Team: undefined, }; const [name, setName] = React.useState(initialValues.name); - const [team, setTeam] = React.useState(initialValues.team); + const [Team, setTeam] = React.useState(initialValues.Team); const [errors, setErrors] = React.useState({}); const resetStateValues = () => { setName(initialValues.name); - setTeam(initialValues.team); + setTeam(initialValues.Team); setCurrentTeamValue(undefined); setCurrentTeamDisplayValue(\\"\\"); setErrors({}); @@ -3637,17 +3637,17 @@ export default function MyMemberForm(props) { const [currentTeamDisplayValue, setCurrentTeamDisplayValue] = React.useState(\\"\\"); const [currentTeamValue, setCurrentTeamValue] = React.useState(undefined); - const teamRef = React.createRef(); + const TeamRef = React.createRef(); const teamRecords = useDataStoreBinding({ type: \\"collection\\", - model: Team, + model: Team0, }).items; const getDisplayValue = { - team: (record) => record?.name, + Team: (record) => record?.name, }; const validations = { name: [], - team: [], + Team: [], }; const runValidationTasks = async ( fieldName, @@ -3675,7 +3675,7 @@ export default function MyMemberForm(props) { event.preventDefault(); let modelFields = { name, - team, + Team, }; const validationResponses = await Promise.all( Object.keys(validations).reduce((promises, fieldName) => { @@ -3761,7 +3761,7 @@ export default function MyMemberForm(props) { if (onChange) { const modelFields = { name: value, - team, + Team, }; const result = onChange(modelFields); value = result?.name ?? value; @@ -3783,10 +3783,10 @@ export default function MyMemberForm(props) { if (onChange) { const modelFields = { name, - team: value, + Team: value, }; const result = onChange(modelFields); - value = result?.team ?? value; + value = result?.Team ?? value; } setTeam(value); setCurrentTeamValue(undefined); @@ -3794,11 +3794,11 @@ export default function MyMemberForm(props) { }} currentFieldValue={currentTeamValue} label={\\"Team Label\\"} - items={team ? [team] : []} - hasError={errors.team?.hasError} - getBadgeText={getDisplayValue.team} + items={Team ? [Team] : []} + hasError={errors.Team?.hasError} + getBadgeText={getDisplayValue.Team} setFieldValue={setCurrentTeamDisplayValue} - inputFieldRef={teamRef} + inputFieldRef={TeamRef} defaultFieldValue={\\"\\"} > ({ id: r.id, - label: getDisplayValue.team?.(r) ?? r.id, + label: getDisplayValue.Team?.(r) ?? r.id, }))} onSelect={({ id, label }) => { setCurrentTeamValue(teamRecords.find((r) => r.id === id)); @@ -3819,17 +3819,17 @@ export default function MyMemberForm(props) { }} onChange={(e) => { let { value } = e.target; - if (errors.team?.hasError) { - runValidationTasks(\\"team\\", value); + if (errors.Team?.hasError) { + runValidationTasks(\\"Team\\", value); } setCurrentTeamDisplayValue(value); setCurrentTeamValue(undefined); }} - onBlur={() => runValidationTasks(\\"team\\", team)} - errorMessage={errors.team?.errorMessage} - hasError={errors.team?.hasError} - ref={teamRef} - {...getOverrideProps(overrides, \\"team\\")} + onBlur={() => runValidationTasks(\\"Team\\", Team)} + errorMessage={errors.Team?.errorMessage} + hasError={errors.Team?.hasError} + ref={TeamRef} + {...getOverrideProps(overrides, \\"Team\\")} > @@ -3840,7 +3840,7 @@ export default function MyMemberForm(props) { exports[`amplify form renderer tests datastore form tests should generate a create form with belongsTo relationship 2`] = ` "import * as React from \\"react\\"; -import { Team } from \\"../models\\"; +import { Team as Team0 } from \\"../models\\"; import { EscapeHatchProps } from \\"@aws-amplify/ui-react/internal\\"; import { AutocompleteProps, GridProps, TextFieldProps } from \\"@aws-amplify/ui-react\\"; export declare type ValidationResponse = { @@ -3850,17 +3850,17 @@ export declare type ValidationResponse = { export declare type ValidationFunction = (value: T, validationResponse: ValidationResponse) => ValidationResponse | Promise; export declare type MyMemberFormInputValues = { name?: string; - team?: Team; + Team?: Team0; }; export declare type MyMemberFormValidationValues = { name?: ValidationFunction; - team?: ValidationFunction; + Team?: ValidationFunction; }; export declare type FormProps = Partial & React.DOMAttributes; export declare type MyMemberFormOverridesProps = { MyMemberFormGrid?: FormProps; name?: FormProps; - team?: FormProps; + Team?: FormProps; } & EscapeHatchProps; export declare type MyMemberFormProps = React.PropsWithChildren<{ overrides?: MyMemberFormOverridesProps | undefined | null; @@ -9575,7 +9575,7 @@ exports[`amplify form renderer tests datastore form tests should use proper fiel "/* eslint-disable */ import * as React from \\"react\\"; import { fetchByPath, validateField } from \\"./utils\\"; -import { Member, Team } from \\"../models\\"; +import { Member, Team as Team0 } from \\"../models\\"; import { getOverrideProps, useDataStoreBinding, @@ -9752,14 +9752,14 @@ export default function MyMemberForm(props) { } = props; const initialValues = { name: undefined, - team: undefined, + Team: undefined, }; const [name, setName] = React.useState(initialValues.name); - const [team, setTeam] = React.useState(initialValues.team); + const [Team, setTeam] = React.useState(initialValues.Team); const [errors, setErrors] = React.useState({}); const resetStateValues = () => { setName(initialValues.name); - setTeam(initialValues.team); + setTeam(initialValues.Team); setCurrentTeamValue(undefined); setCurrentTeamDisplayValue(\\"\\"); setErrors({}); @@ -9767,17 +9767,17 @@ export default function MyMemberForm(props) { const [currentTeamDisplayValue, setCurrentTeamDisplayValue] = React.useState(\\"\\"); const [currentTeamValue, setCurrentTeamValue] = React.useState(undefined); - const teamRef = React.createRef(); + const TeamRef = React.createRef(); const teamRecords = useDataStoreBinding({ type: \\"collection\\", - model: Team, + model: Team0, }).items; const getDisplayValue = { - team: (record) => record?.name, + Team: (record) => record?.name, }; const validations = { name: [], - team: [], + Team: [], }; const runValidationTasks = async ( fieldName, @@ -9805,7 +9805,7 @@ export default function MyMemberForm(props) { event.preventDefault(); let modelFields = { name, - team, + Team, }; const validationResponses = await Promise.all( Object.keys(validations).reduce((promises, fieldName) => { @@ -9891,7 +9891,7 @@ export default function MyMemberForm(props) { if (onChange) { const modelFields = { name: value, - team, + Team, }; const result = onChange(modelFields); value = result?.name ?? value; @@ -9913,10 +9913,10 @@ export default function MyMemberForm(props) { if (onChange) { const modelFields = { name, - team: value, + Team: value, }; const result = onChange(modelFields); - value = result?.team ?? value; + value = result?.Team ?? value; } setTeam(value); setCurrentTeamValue(undefined); @@ -9924,11 +9924,11 @@ export default function MyMemberForm(props) { }} currentFieldValue={currentTeamValue} label={\\"Team Label\\"} - items={team ? [team] : []} - hasError={errors.team?.hasError} - getBadgeText={getDisplayValue.team} + items={Team ? [Team] : []} + hasError={errors.Team?.hasError} + getBadgeText={getDisplayValue.Team} setFieldValue={setCurrentTeamDisplayValue} - inputFieldRef={teamRef} + inputFieldRef={TeamRef} defaultFieldValue={\\"\\"} > ({ id: r.id, - label: getDisplayValue.team?.(r) ?? r.id, + label: getDisplayValue.Team?.(r) ?? r.id, }))} onSelect={({ id, label }) => { setCurrentTeamValue(teamRecords.find((r) => r.id === id)); @@ -9949,17 +9949,17 @@ export default function MyMemberForm(props) { }} onChange={(e) => { let { value } = e.target; - if (errors.team?.hasError) { - runValidationTasks(\\"team\\", value); + if (errors.Team?.hasError) { + runValidationTasks(\\"Team\\", value); } setCurrentTeamDisplayValue(value); setCurrentTeamValue(undefined); }} - onBlur={() => runValidationTasks(\\"team\\", team)} - errorMessage={errors.team?.errorMessage} - hasError={errors.team?.hasError} - ref={teamRef} - {...getOverrideProps(overrides, \\"team\\")} + onBlur={() => runValidationTasks(\\"Team\\", Team)} + errorMessage={errors.Team?.errorMessage} + hasError={errors.Team?.hasError} + ref={TeamRef} + {...getOverrideProps(overrides, \\"Team\\")} > @@ -9970,7 +9970,7 @@ export default function MyMemberForm(props) { exports[`amplify form renderer tests datastore form tests should use proper field overrides for belongsTo relationship 2`] = ` "import * as React from \\"react\\"; -import { Team } from \\"../models\\"; +import { Team as Team0 } from \\"../models\\"; import { EscapeHatchProps } from \\"@aws-amplify/ui-react/internal\\"; import { AutocompleteProps, GridProps, TextFieldProps } from \\"@aws-amplify/ui-react\\"; export declare type ValidationResponse = { @@ -9980,17 +9980,17 @@ export declare type ValidationResponse = { export declare type ValidationFunction = (value: T, validationResponse: ValidationResponse) => ValidationResponse | Promise; export declare type MyMemberFormInputValues = { name?: string; - team?: Team; + Team?: Team0; }; export declare type MyMemberFormValidationValues = { name?: ValidationFunction; - team?: ValidationFunction; + Team?: ValidationFunction; }; export declare type FormProps = Partial & React.DOMAttributes; export declare type MyMemberFormOverridesProps = { MyMemberFormGrid?: FormProps; name?: FormProps; - team?: FormProps; + Team?: FormProps; } & EscapeHatchProps; export declare type MyMemberFormProps = React.PropsWithChildren<{ overrides?: MyMemberFormOverridesProps | undefined | null; diff --git a/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react-forms.test.ts b/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react-forms.test.ts index d8873da6a..1b70fa7d4 100644 --- a/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react-forms.test.ts +++ b/packages/codegen-ui-react/lib/__tests__/studio-ui-codegen-react-forms.test.ts @@ -64,7 +64,7 @@ describe('amplify form renderer tests', () => { 'datastore/project-team-model', ); // check nested model is imported - expect(componentText).toContain('import { Member, Team } from "../models";'); + expect(componentText).toContain('import { Member, Team as Team0 } from "../models";'); // check binding call is generated expect(componentText).toContain('const teamRecords = useDataStoreBinding({'); @@ -81,7 +81,7 @@ describe('amplify form renderer tests', () => { // Check that custom field label is working as expected expect(componentText).toContain('Team Label'); // Check that Autocomplete custom display value is set - expect(componentText).toContain('team: (record) => record?.name'); + expect(componentText).toContain('Team: (record) => record?.name'); expect(componentText).toMatchSnapshot(); expect(declaration).toMatchSnapshot(); diff --git a/packages/codegen-ui-react/lib/forms/form-renderer-helper/relationship.ts b/packages/codegen-ui-react/lib/forms/form-renderer-helper/relationship.ts index 14d78e2c2..3d8048d24 100644 --- a/packages/codegen-ui-react/lib/forms/form-renderer-helper/relationship.ts +++ b/packages/codegen-ui-react/lib/forms/form-renderer-helper/relationship.ts @@ -17,13 +17,20 @@ import { factory, NodeFlags, SyntaxKind } from 'typescript'; import { FieldConfigMetadata, GenericDataRelationshipType, HasManyRelationshipType } from '@aws-amplify/codegen-ui'; import { getRecordsName } from './form-state'; import { buildBaseCollectionVariableStatement } from '../../react-studio-template-renderer-helper'; +import { ImportCollection, ImportSource } from '../../imports'; -export const buildRelationshipQuery = (relationship: GenericDataRelationshipType) => { +export const buildRelationshipQuery = ( + relationship: GenericDataRelationshipType, + importCollection: ImportCollection, +) => { const { relatedModelName } = relationship; const itemsName = getRecordsName(relatedModelName); const objectProperties = [ factory.createPropertyAssignment(factory.createIdentifier('type'), factory.createStringLiteral('collection')), - factory.createPropertyAssignment(factory.createIdentifier('model'), factory.createIdentifier(relatedModelName)), + factory.createPropertyAssignment( + factory.createIdentifier('model'), + factory.createIdentifier(importCollection.getMappedAlias(ImportSource.LOCAL_MODELS, relatedModelName)), + ), ]; return buildBaseCollectionVariableStatement( itemsName, diff --git a/packages/codegen-ui-react/lib/forms/form-renderer-helper/type-helper.ts b/packages/codegen-ui-react/lib/forms/form-renderer-helper/type-helper.ts index 9215f6c60..dd5cc761d 100644 --- a/packages/codegen-ui-react/lib/forms/form-renderer-helper/type-helper.ts +++ b/packages/codegen-ui-react/lib/forms/form-renderer-helper/type-helper.ts @@ -67,8 +67,8 @@ const getTypeNode = ({ componentType, dataType, isArray, isValidation, importCol if (dataType && typeof dataType === 'object' && 'model' in dataType) { const modelName = dataType.model; - importCollection?.addImport(ImportSource.LOCAL_MODELS, modelName); - typeNode = factory.createTypeReferenceNode(factory.createIdentifier(modelName)); + const aliasedModel = importCollection?.addImport(ImportSource.LOCAL_MODELS, modelName); + typeNode = factory.createTypeReferenceNode(factory.createIdentifier(aliasedModel || modelName)); } if (isValidation) { diff --git a/packages/codegen-ui-react/lib/forms/react-form-renderer.ts b/packages/codegen-ui-react/lib/forms/react-form-renderer.ts index fb7d99208..e04efb5a3 100644 --- a/packages/codegen-ui-react/lib/forms/react-form-renderer.ts +++ b/packages/codegen-ui-react/lib/forms/react-form-renderer.ts @@ -271,7 +271,6 @@ export abstract class ReactFormTemplateRenderer extends StudioTemplateRenderer< private renderBindingPropsType(): TypeAliasDeclaration[] { const { name: formName, - formActionType, dataType: { dataSourceType, dataTypeName }, } = this.component; const fieldConfigs = this.componentMetadata.formMetadata?.fieldConfigs ?? {}; @@ -297,19 +296,19 @@ export abstract class ReactFormTemplateRenderer extends StudioTemplateRenderer< this.importCollection.addMappedImport(ImportValue.ESCAPE_HATCH_PROPS); let modelName = dataTypeName; - if (dataSourceType === 'DataStore' && formActionType === 'update') { - // add model import for datastore type - if (dataSourceType === 'DataStore') { - this.requiredDataModels.push(dataTypeName); - modelName = this.importCollection.addImport(ImportSource.LOCAL_MODELS, dataTypeName); - } + + // add model import for datastore type + if (dataSourceType === 'DataStore') { + this.requiredDataModels.push(dataTypeName); + modelName = this.importCollection.addImport(ImportSource.LOCAL_MODELS, dataTypeName); } + return [ validationResponseType, validationFunctionType, // pass in importCollection once to collect models to import generateFieldTypes(formName, 'input', fieldConfigs, this.importCollection), - generateFieldTypes(formName, 'validation', fieldConfigs), + generateFieldTypes(formName, 'validation', fieldConfigs, this.importCollection), formOverrideProp, overrideTypeAliasDeclaration, factory.createTypeAliasDeclaration( @@ -512,6 +511,16 @@ export abstract class ReactFormTemplateRenderer extends StudioTemplateRenderer< } }); + const { validationsObject, dataTypesMap, displayValueObject, modelsToImport, usesArrayField } = mapFromFieldConfigs( + formMetadata.fieldConfigs, + ); + + this.shouldRenderArrayField = usesArrayField; + + this.requiredDataModels.push(...modelsToImport); + + modelsToImport.forEach((model) => this.importCollection.addImport(ImportSource.LOCAL_MODELS, model)); + // datastore relationship query /** const authorRecords = useDataStoreBinding({ @@ -521,19 +530,11 @@ export abstract class ReactFormTemplateRenderer extends StudioTemplateRenderer< */ if (relationshipCollection.length) { this.importCollection.addMappedImport(ImportValue.USE_DATA_STORE_BINDING); - statements.push(...relationshipCollection.map((relationship) => buildRelationshipQuery(relationship))); + statements.push( + ...relationshipCollection.map((relationship) => buildRelationshipQuery(relationship, this.importCollection)), + ); } - const { validationsObject, dataTypesMap, displayValueObject, modelsToImport, usesArrayField } = mapFromFieldConfigs( - formMetadata.fieldConfigs, - ); - - this.shouldRenderArrayField = usesArrayField; - - this.requiredDataModels.push(...modelsToImport); - - modelsToImport.forEach((model) => this.importCollection.addImport(ImportSource.LOCAL_MODELS, model)); - if (displayValueObject) { statements.push(displayValueObject); } diff --git a/packages/codegen-ui-react/lib/imports/import-collection.ts b/packages/codegen-ui-react/lib/imports/import-collection.ts index 2d357a009..803f2e6d1 100644 --- a/packages/codegen-ui-react/lib/imports/import-collection.ts +++ b/packages/codegen-ui-react/lib/imports/import-collection.ts @@ -23,6 +23,10 @@ import { createUniqueName } from '../helpers'; export class ImportCollection { constructor(componentMetadata?: ComponentMetadata) { this.importedNames = new Set(Object.values(componentMetadata?.componentNameToTypeMap || {}).concat(reservedWords)); + // Add form fields so we dont reuse the identifier + if (componentMetadata?.formMetadata) { + Object.keys(componentMetadata.formMetadata.fieldConfigs).forEach((key) => this.importedNames.add(key)); + } } importedNames: Set; diff --git a/packages/codegen-ui/example-schemas/datastore/project-team-model.json b/packages/codegen-ui/example-schemas/datastore/project-team-model.json index 8e7ef77af..e30ccc75c 100644 --- a/packages/codegen-ui/example-schemas/datastore/project-team-model.json +++ b/packages/codegen-ui/example-schemas/datastore/project-team-model.json @@ -6,8 +6,8 @@ "id": { "name": "id", "isArray": false, "type": "ID", "isRequired": true, "attributes": [] }, "name": { "name": "name", "isArray": false, "type": "String", "isRequired": false, "attributes": [] }, "teamID": { "name": "teamID", "isArray": false, "type": "ID", "isRequired": true, "attributes": [] }, - "team": { - "name": "team", + "Team": { + "name": "Team", "isArray": false, "type": { "model": "Team" }, "isRequired": false, @@ -103,8 +103,8 @@ "fields": { "id": { "name": "id", "isArray": false, "type": "ID", "isRequired": true, "attributes": [] }, "name": { "name": "name", "isArray": false, "type": "String", "isRequired": true, "attributes": [] }, - "team": { - "name": "team", + "Team": { + "name": "Team", "isArray": false, "type": { "model": "Team" }, "isRequired": false, diff --git a/packages/codegen-ui/example-schemas/forms/member-datastore-create.json b/packages/codegen-ui/example-schemas/forms/member-datastore-create.json index 932419339..11856dea2 100644 --- a/packages/codegen-ui/example-schemas/forms/member-datastore-create.json +++ b/packages/codegen-ui/example-schemas/forms/member-datastore-create.json @@ -6,7 +6,7 @@ "dataTypeName": "Member" }, "fields": { - "team": { + "Team": { "inputType": { "type": "Autocomplete", "valueMappings": {