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 1a388e60b..7a39db04d 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 @@ -15,7 +15,8 @@ import { TextField, } from \\"@aws-amplify/ui-react\\"; export default function CustomDataForm(props) { - const { onSubmit, onCancel, onValidate, overrides, ...rest } = props; + const { onSubmit, onCancel, onValidate, onChange, overrides, ...rest } = + props; const [name, setName] = React.useState(undefined); const [email, setEmail] = React.useState(undefined); const [city, setCity] = React.useState(undefined); @@ -79,7 +80,17 @@ export default function CustomDataForm(props) { isRequired={true} defaultValue=\\"John Doe\\" onChange={async (e) => { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + name, + email, + city, + category, + }; + const result = onChange(modelFields); + value = result?.name ?? value; + } if (errors.name?.hasError) { await runValidationTasks(\\"name\\", value); } @@ -95,7 +106,17 @@ export default function CustomDataForm(props) { isRequired={true} defaultValue=\\"johndoe@amplify.com\\" onChange={async (e) => { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + name, + email, + city, + category, + }; + const result = onChange(modelFields); + value = result?.email ?? value; + } if (errors.email?.hasError) { await runValidationTasks(\\"email\\", value); } @@ -109,7 +130,17 @@ export default function CustomDataForm(props) { { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + name, + email, + city, + category, + }; + const result = onChange(modelFields); + value = result?.city ?? value; + } if (errors.city?.hasError) { await runValidationTasks(\\"city\\", value); } @@ -142,7 +173,17 @@ export default function CustomDataForm(props) { name=\\"fieldName\\" defaultValue=\\"Hobbies\\" onChange={async (e) => { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + name, + email, + city, + category, + }; + const result = onChange(modelFields); + value = result?.category ?? value; + } if (errors.category?.hasError) { await runValidationTasks(\\"category\\", value); } @@ -230,6 +271,7 @@ export declare type CustomDataFormProps = React.PropsWithChildren<{ } & { onSubmit: (fields: Record) => void; onCancel?: () => void; + onChange?: (fields: Record) => Record; onValidate?: CustomDataFormInputValues; }>; export default function CustomDataForm(props: CustomDataFormProps): React.ReactElement; @@ -243,7 +285,8 @@ import { fetchByPath, validateField } from \\"./utils\\"; import { getOverrideProps } from \\"@aws-amplify/ui-react/internal\\"; import { Button, Flex, Grid, Heading, TextField } from \\"@aws-amplify/ui-react\\"; export default function NestedJson(props) { - const { onSubmit, onCancel, onValidate, overrides, ...rest } = props; + const { onSubmit, onCancel, onValidate, onChange, overrides, ...rest } = + props; const [firstName, setFirstName] = React.useState(undefined); const [lastName, setLastName] = React.useState(undefined); const [bio, setBio] = React.useState({}); @@ -303,7 +346,16 @@ export default function NestedJson(props) { { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + firstName, + lastName, + bio, + }; + const result = onChange(modelFields); + value = result?.firstName ?? value; + } if (errors.firstName?.hasError) { await runValidationTasks(\\"firstName\\", value); } @@ -317,7 +369,16 @@ export default function NestedJson(props) { { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + firstName, + lastName, + bio, + }; + const result = onChange(modelFields); + value = result?.lastName ?? value; + } if (errors.lastName?.hasError) { await runValidationTasks(\\"lastName\\", value); } @@ -336,7 +397,16 @@ export default function NestedJson(props) { { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + firstName, + lastName, + bio, + }; + const result = onChange(modelFields); + value = result?.bio?.favoriteQuote ?? value; + } if (errors.bio.favoriteQuote?.hasError) { await runValidationTasks(\\"bio.favoriteQuote\\", value); } @@ -352,7 +422,16 @@ export default function NestedJson(props) { { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + firstName, + lastName, + bio, + }; + const result = onChange(modelFields); + value = result?.bio?.favoriteAnimal ?? value; + } if (errors.bio.favoriteAnimal?.hasError) { await runValidationTasks(\\"bio.favoriteAnimal\\", value); } @@ -429,6 +508,7 @@ export declare type NestedJsonProps = React.PropsWithChildren<{ } & { onSubmit: (fields: Record) => void; onCancel?: () => void; + onChange?: (fields: Record) => Record; onValidate?: NestedJsonInputValues; }>; export default function NestedJson(props: NestedJsonProps): React.ReactElement; @@ -450,7 +530,8 @@ import { TextField, } from \\"@aws-amplify/ui-react\\"; export default function CustomWithSectionalElements(props) { - const { onSubmit, onCancel, onValidate, overrides, ...rest } = props; + const { onSubmit, onCancel, onValidate, onChange, overrides, ...rest } = + props; const [name, setName] = React.useState(undefined); const [errors, setErrors] = React.useState({}); const validations = { @@ -508,7 +589,14 @@ export default function CustomWithSectionalElements(props) { { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + name, + }; + const result = onChange(modelFields); + value = result?.name ?? value; + } if (errors.name?.hasError) { await runValidationTasks(\\"name\\", value); } @@ -585,6 +673,7 @@ export declare type CustomWithSectionalElementsProps = React.PropsWithChildren<{ } & { onSubmit: (fields: Record) => void; onCancel?: () => void; + onChange?: (fields: Record) => Record; onValidate?: CustomWithSectionalElementsInputValues; }>; export default function CustomWithSectionalElements(props: CustomWithSectionalElementsProps): React.ReactElement; @@ -606,6 +695,7 @@ export default function MyPostForm(props) { onSubmit, onCancel, onValidate, + onChange, overrides, ...rest } = props; @@ -711,7 +801,17 @@ export default function MyPostForm(props) { isRequired={false} isReadOnly={false} onChange={async (e) => { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + caption, + username, + post_url, + profile_url, + }; + const result = onChange(modelFields); + value = result?.caption ?? value; + } if (errors.caption?.hasError) { await runValidationTasks(\\"caption\\", value); } @@ -727,7 +827,17 @@ export default function MyPostForm(props) { isRequired={false} isReadOnly={false} onChange={async (e) => { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + caption, + username, + post_url, + profile_url, + }; + const result = onChange(modelFields); + value = result?.username ?? value; + } if (errors.username?.hasError) { await runValidationTasks(\\"username\\", value); } @@ -743,7 +853,17 @@ export default function MyPostForm(props) { isRequired={false} isReadOnly={false} onChange={async (e) => { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + caption, + username, + post_url, + profile_url, + }; + const result = onChange(modelFields); + value = result?.post_url ?? value; + } if (errors.post_url?.hasError) { await runValidationTasks(\\"post_url\\", value); } @@ -759,7 +879,17 @@ export default function MyPostForm(props) { isRequired={false} isReadOnly={false} onChange={async (e) => { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + caption, + username, + post_url, + profile_url, + }; + const result = onChange(modelFields); + value = result?.profile_url ?? value; + } if (errors.profile_url?.hasError) { await runValidationTasks(\\"profile_url\\", value); } @@ -808,6 +938,7 @@ export declare type MyPostFormProps = React.PropsWithChildren<{ onSuccess?: (fields: Record) => void; onError?: (fields: Record, errorMessage: string) => void; onCancel?: () => void; + onChange?: (fields: Record) => Record; onValidate?: MyPostFormInputValues; }>; export default function MyPostForm(props: MyPostFormProps): React.ReactElement; @@ -837,6 +968,7 @@ export default function MyPostForm(props) { onSubmit, onCancel, onValidate, + onChange, overrides, ...rest } = props; @@ -961,7 +1093,18 @@ export default function MyPostForm(props) { { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + TextAreaFieldbbd63464, + caption, + username, + profile_url, + post_url, + }; + const result = onChange(modelFields); + value = result?.TextAreaFieldbbd63464 ?? value; + } if (errors.TextAreaFieldbbd63464?.hasError) { await runValidationTasks(\\"TextAreaFieldbbd63464\\", value); } @@ -983,7 +1126,18 @@ export default function MyPostForm(props) { isRequired={false} isReadOnly={false} onChange={async (e) => { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + TextAreaFieldbbd63464, + caption, + username, + profile_url, + post_url, + }; + const result = onChange(modelFields); + value = result?.caption ?? value; + } if (errors.caption?.hasError) { await runValidationTasks(\\"caption\\", value); } @@ -1000,7 +1154,18 @@ export default function MyPostForm(props) { isRequired={false} isReadOnly={false} onChange={async (e) => { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + TextAreaFieldbbd63464, + caption, + username, + profile_url, + post_url, + }; + const result = onChange(modelFields); + value = result?.username ?? value; + } if (errors.username?.hasError) { await runValidationTasks(\\"username\\", value); } @@ -1017,7 +1182,18 @@ export default function MyPostForm(props) { isRequired={false} isReadOnly={false} onChange={async (e) => { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + TextAreaFieldbbd63464, + caption, + username, + profile_url, + post_url, + }; + const result = onChange(modelFields); + value = result?.profile_url ?? value; + } if (errors.profile_url?.hasError) { await runValidationTasks(\\"profile_url\\", value); } @@ -1036,7 +1212,18 @@ export default function MyPostForm(props) { isRequired={false} isReadOnly={false} onChange={async (e) => { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + TextAreaFieldbbd63464, + caption, + username, + profile_url, + post_url, + }; + const result = onChange(modelFields); + value = result?.post_url ?? value; + } if (errors.post_url?.hasError) { await runValidationTasks(\\"post_url\\", value); } @@ -1116,6 +1303,7 @@ export declare type MyPostFormProps = React.PropsWithChildren<{ onSuccess?: (fields: Record) => void; onError?: (fields: Record, errorMessage: string) => void; onCancel?: () => void; + onChange?: (fields: Record) => Record; onValidate?: MyPostFormInputValues; }>; export default function MyPostForm(props: MyPostFormProps): React.ReactElement; @@ -1249,6 +1437,7 @@ export default function InputGalleryCreateForm(props) { onSubmit, onCancel, onValidate, + onChange, overrides, ...rest } = props; @@ -1346,7 +1535,22 @@ export default function InputGalleryCreateForm(props) { isReadOnly={false} type=\\"number\\" onChange={async (e) => { - const value = parseInt(e.target.value); + let value = parseInt(e.target.value); + if (onChange) { + const modelFields = { + num, + rootbeer, + attend, + maybeSlide, + maybeCheck, + arrayTypeField, + timestamp, + ippy, + timeisnow, + }; + const result = onChange(modelFields); + value = result?.num ?? value; + } if (errors.num?.hasError) { await runValidationTasks(\\"num\\", value); } @@ -1363,7 +1567,22 @@ export default function InputGalleryCreateForm(props) { isReadOnly={false} type=\\"number\\" onChange={async (e) => { - const value = Number(e.target.value); + let value = Number(e.target.value); + if (onChange) { + const modelFields = { + num, + rootbeer, + attend, + maybeSlide, + maybeCheck, + arrayTypeField, + timestamp, + ippy, + timeisnow, + }; + const result = onChange(modelFields); + value = result?.rootbeer ?? value; + } if (errors.rootbeer?.hasError) { await runValidationTasks(\\"rootbeer\\", value); } @@ -1380,7 +1599,22 @@ export default function InputGalleryCreateForm(props) { isReadOnly={false} isRequired=\\"false\\" onChange={async (e) => { - const value = e.target.value === \\"true\\"; + let value = e.target.value === \\"true\\"; + if (onChange) { + const modelFields = { + num, + rootbeer, + attend, + maybeSlide, + maybeCheck, + arrayTypeField, + timestamp, + ippy, + timeisnow, + }; + const result = onChange(modelFields); + value = result?.attend ?? value; + } if (errors.attend?.hasError) { await runValidationTasks(\\"attend\\", value); } @@ -1407,7 +1641,22 @@ export default function InputGalleryCreateForm(props) { isDisabled={false} defaultPressed={false} onChange={async (e) => { - const value = e.target.checked; + let value = e.target.checked; + if (onChange) { + const modelFields = { + num, + rootbeer, + attend, + maybeSlide, + maybeCheck, + arrayTypeField, + timestamp, + ippy, + timeisnow, + }; + const result = onChange(modelFields); + value = result?.maybeSlide ?? value; + } if (errors.maybeSlide?.hasError) { await runValidationTasks(\\"maybeSlide\\", value); } @@ -1425,7 +1674,22 @@ export default function InputGalleryCreateForm(props) { isDisabled={false} defaultChecked={false} onChange={async (e) => { - const value = e.target.checked; + let value = e.target.checked; + if (onChange) { + const modelFields = { + num, + rootbeer, + attend, + maybeSlide, + maybeCheck, + arrayTypeField, + timestamp, + ippy, + timeisnow, + }; + const result = onChange(modelFields); + value = result?.maybeCheck ?? value; + } if (errors.maybeCheck?.hasError) { await runValidationTasks(\\"maybeCheck\\", value); } @@ -1451,7 +1715,22 @@ export default function InputGalleryCreateForm(props) { isRequired={false} isReadOnly={false} onChange={async (e) => { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + num, + rootbeer, + attend, + maybeSlide, + maybeCheck, + arrayTypeField, + timestamp, + ippy, + timeisnow, + }; + const result = onChange(modelFields); + value = result?.arrayTypeField ?? value; + } if (errors.arrayTypeField?.hasError) { await runValidationTasks(\\"arrayTypeField\\", value); } @@ -1473,7 +1752,22 @@ export default function InputGalleryCreateForm(props) { isReadOnly={false} type=\\"datetime-local\\" onChange={async (e) => { - const value = Number(new Date(e.target.value)); + let value = Number(new Date(e.target.value)); + if (onChange) { + const modelFields = { + num, + rootbeer, + attend, + maybeSlide, + maybeCheck, + arrayTypeField, + timestamp, + ippy, + timeisnow, + }; + const result = onChange(modelFields); + value = result?.timestamp ?? value; + } if (errors.timestamp?.hasError) { await runValidationTasks(\\"timestamp\\", value); } @@ -1489,7 +1783,22 @@ export default function InputGalleryCreateForm(props) { isRequired={false} isReadOnly={false} onChange={async (e) => { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + num, + rootbeer, + attend, + maybeSlide, + maybeCheck, + arrayTypeField, + timestamp, + ippy, + timeisnow, + }; + const result = onChange(modelFields); + value = result?.ippy ?? value; + } if (errors.ippy?.hasError) { await runValidationTasks(\\"ippy\\", value); } @@ -1506,7 +1815,22 @@ export default function InputGalleryCreateForm(props) { isReadOnly={false} type=\\"time\\" onChange={async (e) => { - const { value } = e.target; + let { value } = e.target; + if (onChange) { + const modelFields = { + num, + rootbeer, + attend, + maybeSlide, + maybeCheck, + arrayTypeField, + timestamp, + ippy, + timeisnow, + }; + const result = onChange(modelFields); + value = result?.timeisnow ?? value; + } if (errors.timeisnow?.hasError) { await runValidationTasks(\\"timeisnow\\", value); } @@ -1590,6 +1914,7 @@ export declare type InputGalleryCreateFormProps = React.PropsWithChildren<{ onSuccess?: (fields: Record) => void; onError?: (fields: Record, errorMessage: string) => void; onCancel?: () => void; + onChange?: (fields: Record) => Record; onValidate?: InputGalleryCreateFormInputValues; }>; export default function InputGalleryCreateForm(props: InputGalleryCreateFormProps): React.ReactElement; diff --git a/packages/codegen-ui-react/lib/__tests__/react-forms/__snapshots__/form-renderer-helper.test.ts.snap b/packages/codegen-ui-react/lib/__tests__/react-forms/__snapshots__/form-renderer-helper.test.ts.snap index dc5c88fb6..0e28eadbc 100644 --- a/packages/codegen-ui-react/lib/__tests__/react-forms/__snapshots__/form-renderer-helper.test.ts.snap +++ b/packages/codegen-ui-react/lib/__tests__/react-forms/__snapshots__/form-renderer-helper.test.ts.snap @@ -6,6 +6,7 @@ exports[`form-render utils should generate before & complete types if datastore onSuccess?: (fields: Record) => void; onError?: (fields: Record, errorMessage: string) => void; onCancel?: () => void; + onChange?: (fields: Record) => Record; onValidate?: mySampleFormInputValues; }" `; @@ -14,6 +15,7 @@ exports[`form-render utils should generate regular onsubmit if dataSourceType is "{ onSubmit: (fields: Record) => void; onCancel?: () => void; + onChange?: (fields: Record) => Record; onValidate?: myCustomFormInputValues; }" `; diff --git a/packages/codegen-ui-react/lib/amplify-ui-renderers/form.ts b/packages/codegen-ui-react/lib/amplify-ui-renderers/form.ts index dc84e57c2..8094d6fbe 100644 --- a/packages/codegen-ui-react/lib/amplify-ui-renderers/form.ts +++ b/packages/codegen-ui-react/lib/amplify-ui-renderers/form.ts @@ -206,7 +206,7 @@ export default class FormRenderer extends ReactComponentRenderer { const { @@ -67,7 +67,6 @@ export const buildMutationBindings = (form: StudioForm) => { ); } elements.push(factory.createBindingElement(undefined, undefined, factory.createIdentifier('onSubmit'), undefined)); - elements.push(factory.createBindingElement(undefined, undefined, factory.createIdentifier('onCancel'), undefined)); return elements; }; @@ -212,16 +211,43 @@ export const buildFormPropNode = (form: StudioForm) => { ), ); } - // onCancel?: () => void propSignatures.push( + // onCancel?: () => void factory.createPropertySignature( undefined, 'onCancel', factory.createToken(SyntaxKind.QuestionToken), factory.createFunctionTypeNode(undefined, [], factory.createKeywordTypeNode(SyntaxKind.VoidKeyword)), ), + // onChange?: (fields: Record) => Record + factory.createPropertySignature( + undefined, + 'onChange', + factory.createToken(SyntaxKind.QuestionToken), + factory.createFunctionTypeNode( + undefined, + [ + factory.createParameterDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier('fields'), + undefined, + factory.createTypeReferenceNode(factory.createIdentifier('Record'), [ + factory.createKeywordTypeNode(SyntaxKind.StringKeyword), + factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword), + ]), + undefined, + ), + ], + factory.createTypeReferenceNode(factory.createIdentifier('Record'), [ + factory.createKeywordTypeNode(SyntaxKind.StringKeyword), + factory.createKeywordTypeNode(SyntaxKind.UnknownKeyword), + ]), + ), + ), + buildOnValidateType(form.name), ); - propSignatures.push(buildOnValidateType(form.name)); return factory.createTypeLiteralNode(propSignatures); }; @@ -298,7 +324,7 @@ export const addFormAttributes = (component: StudioComponent | StudioComponentCh factory.createIdentifier('errors'), factory.createIdentifier(component.name), ); - attributes.push(buildOnChangeStatement(component, fieldConfig)); + attributes.push(buildOnChangeStatement(component, formMetadata.fieldConfigs)); attributes.push(buildOnBlurStatement(component.name)); attributes.push( factory.createJsxAttribute( @@ -480,12 +506,67 @@ export function buildOnBlurStatement(fieldName: string) { ); } +/** + * if the onChange variable is defined it will send the current state of the fields into the function + * the function expects all fields in return + * the value for that fields onChange will be used from the return object for validation and updating the new state + * + * + * ex. if the field is email + * const returnObject = onChange({ email, ...otherFieldsForForm }); + * const value = returnObject.email; + * + * this value is now used in email validation and setting the state + */ +export const buildOverrideOnChangeStatement = ( + fieldName: string, + fieldConfigs: Record, +): IfStatement => { + return factory.createIfStatement( + factory.createIdentifier('onChange'), + factory.createBlock( + [ + buildModelFieldObject(true, fieldConfigs), + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier('result'), + undefined, + undefined, + factory.createCallExpression(factory.createIdentifier('onChange'), undefined, [ + factory.createIdentifier('modelFields'), + ]), + ), + ], + NodeFlags.Const, + ), + ), + factory.createExpressionStatement( + factory.createBinaryExpression( + factory.createIdentifier('value'), + factory.createToken(SyntaxKind.EqualsToken), + factory.createBinaryExpression( + buildAccessChain(['result', ...fieldName.split('.')]), + factory.createToken(SyntaxKind.QuestionQuestionToken), + factory.createIdentifier('value'), + ), + ), + ), + ], + true, + ), + undefined, + ); +}; + export const buildOnChangeStatement = ( component: StudioComponent | StudioComponentChild, - fieldConfig: FieldConfigMetadata, + fieldConfigs: Record, ) => { const { name: fieldName, componentType: fieldType } = component; - const { dataType, isArray } = fieldConfig; + const { dataType, isArray } = fieldConfigs[fieldName]; if (isArray) { return factory.createJsxAttribute( factory.createIdentifier('onChange'), @@ -510,6 +591,7 @@ export const buildOnChangeStatement = ( factory.createBlock( [ buildTargetVariable(fieldType, dataType), + buildOverrideOnChangeStatement(fieldName, fieldConfigs), getOnChangeValidationBlock(fieldName), factory.createExpressionStatement( factory.createCallExpression( @@ -547,7 +629,8 @@ export const buildOnChangeStatement = ( factory.createToken(SyntaxKind.EqualsGreaterThanToken), factory.createBlock( [ - buildTargetVariable(fieldType, fieldConfig.dataType), + buildTargetVariable(fieldType, dataType), + buildOverrideOnChangeStatement(fieldName, fieldConfigs), getOnChangeValidationBlock(fieldName), factory.createExpressionStatement(setFieldState(fieldName, factory.createIdentifier('value'))), ], @@ -893,7 +976,7 @@ export const runValidationTasksFunction = factory.createVariableStatement( * @returns */ export const buildModelFieldObject = ( - dataSourceType: StudioDataSourceType, + shouldBeConst: boolean, fieldConfigs: Record = {}, ) => { const fieldSet = new Set(); @@ -917,7 +1000,7 @@ export const buildModelFieldObject = ( factory.createObjectLiteralExpression(fields, true), ), ], - dataSourceType === 'DataStore' ? NodeFlags.Let : NodeFlags.Const, + shouldBeConst ? NodeFlags.Const : NodeFlags.Let, ), ); }; 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 9aafc540a..09d9df48e 100644 --- a/packages/codegen-ui-react/lib/forms/react-form-renderer.ts +++ b/packages/codegen-ui-react/lib/forms/react-form-renderer.ts @@ -320,7 +320,6 @@ export abstract class ReactFormTemplateRenderer extends StudioTemplateRenderer< */ private buildVariableStatements() { const statements: Statement[] = []; - const elements: BindingElement[] = []; const { formMetadata } = this.componentMetadata; const { dataType: { dataTypeName, dataSourceType }, @@ -332,24 +331,25 @@ export abstract class ReactFormTemplateRenderer extends StudioTemplateRenderer< throw new Error(`Form Metadata is missing from form: ${this.component.name}`); } - // add in hooks for before/complete with ds and basic onSubmit with props - elements.push(...buildMutationBindings(this.component)); - // add onValidate prop - elements.push( + const elements: BindingElement[] = [ + // add in hooks for before/complete with ds and basic onSubmit with props + ...buildMutationBindings(this.component), + // onCancel prop + factory.createBindingElement(undefined, undefined, factory.createIdentifier('onCancel'), undefined), + // onValidate prop factory.createBindingElement(undefined, undefined, factory.createIdentifier('onValidate'), undefined), - ); - // overrides - elements.push(factory.createBindingElement(undefined, undefined, factory.createIdentifier('overrides'), undefined)); - - // get rest of props to pass to top level component - elements.push( + // onChange prop + factory.createBindingElement(undefined, undefined, factory.createIdentifier('onChange'), undefined), + // overrides + factory.createBindingElement(undefined, undefined, factory.createIdentifier('overrides'), undefined), + // get rest of props to pass to top level component factory.createBindingElement( factory.createToken(SyntaxKind.DotDotDotToken), undefined, factory.createIdentifier('rest'), undefined, ), - ); + ]; // add binding elments to statements statements.push(