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 4ddd85ff7..29cba4864 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
@@ -2262,6 +2262,1115 @@ export default function MyPostForm(props: MyPostFormProps): React.ReactElement;
"
`;
+exports[`amplify form renderer tests datastore form tests custom form tests should render an update form for model with composite keys 1`] = `
+"/* eslint-disable */
+import * as React from \\"react\\";
+import {
+ Autocomplete,
+ Badge,
+ Button,
+ Divider,
+ Flex,
+ Grid,
+ Icon,
+ ScrollView,
+ Text,
+ TextField,
+ useTheme,
+} from \\"@aws-amplify/ui-react\\";
+import {
+ getOverrideProps,
+ useDataStoreBinding,
+} from \\"@aws-amplify/ui-react/internal\\";
+import {
+ CompositeDog,
+ CompositeBowl as CompositeBowl0,
+ CompositeOwner as CompositeOwner0,
+ CompositeToy,
+ CompositeVet,
+ CompositeDogCompositeVet,
+} from \\"../models\\";
+import { fetchByPath, validateField } from \\"./utils\\";
+import { DataStore } from \\"aws-amplify\\";
+function ArrayField({
+ items = [],
+ onChange,
+ label,
+ inputFieldRef,
+ children,
+ hasError,
+ setFieldValue,
+ currentFieldValue,
+ defaultFieldValue,
+ lengthLimit,
+ getBadgeText,
+}) {
+ const labelElement = {label};
+ const { tokens } = useTheme();
+ const [selectedBadgeIndex, setSelectedBadgeIndex] = React.useState();
+ const [isEditing, setIsEditing] = React.useState();
+ React.useEffect(() => {
+ if (isEditing) {
+ inputFieldRef?.current?.focus();
+ }
+ }, [isEditing]);
+ const removeItem = async (removeIndex) => {
+ const newItems = items.filter((value, index) => index !== removeIndex);
+ await onChange(newItems);
+ setSelectedBadgeIndex(undefined);
+ };
+ const addItem = async () => {
+ if (
+ currentFieldValue !== undefined &&
+ currentFieldValue !== null &&
+ currentFieldValue !== \\"\\" &&
+ !hasError
+ ) {
+ const newItems = [...items];
+ if (selectedBadgeIndex !== undefined) {
+ newItems[selectedBadgeIndex] = currentFieldValue;
+ setSelectedBadgeIndex(undefined);
+ } else {
+ newItems.push(currentFieldValue);
+ }
+ await onChange(newItems);
+ setIsEditing(false);
+ }
+ };
+ const arraySection = (
+
+ {!!items?.length && (
+
+ {items.map((value, index) => {
+ return (
+ {
+ setSelectedBadgeIndex(index);
+ setFieldValue(items[index]);
+ setIsEditing(true);
+ }}
+ >
+ {getBadgeText ? getBadgeText(value) : value.toString()}
+ {
+ event.stopPropagation();
+ removeItem(index);
+ }}
+ />
+
+ );
+ })}
+
+ )}
+
+
+ );
+ if (lengthLimit !== undefined && items.length >= lengthLimit && !isEditing) {
+ return (
+
+ {labelElement}
+ {arraySection}
+
+ );
+ }
+ return (
+
+ {labelElement}
+ {isEditing && children}
+ {!isEditing ? (
+ <>
+
+ >
+ ) : (
+
+ {(currentFieldValue || isEditing) && (
+
+ )}
+
+
+ )}
+ {arraySection}
+
+ );
+}
+export default function UpdateCompositeDogForm(props) {
+ const {
+ name: nameProp,
+ compositeDog,
+ onSuccess,
+ onError,
+ onSubmit,
+ onValidate,
+ onChange,
+ overrides,
+ ...rest
+ } = props;
+ const initialValues = {
+ name: undefined,
+ description: undefined,
+ CompositeBowl: undefined,
+ CompositeOwner: undefined,
+ CompositeToys: [],
+ CompositeVets: [],
+ compositeDogCompositeBowlSize: undefined,
+ compositeDogCompositeOwnerFirstName: undefined,
+ };
+ const [name, setName] = React.useState(initialValues.name);
+ const [description, setDescription] = React.useState(
+ initialValues.description
+ );
+ const [CompositeBowl, setCompositeBowl] = React.useState(
+ initialValues.CompositeBowl
+ );
+ const [CompositeOwner, setCompositeOwner] = React.useState(
+ initialValues.CompositeOwner
+ );
+ const [CompositeToys, setCompositeToys] = React.useState(
+ initialValues.CompositeToys
+ );
+ const [CompositeVets, setCompositeVets] = React.useState(
+ initialValues.CompositeVets
+ );
+ const [compositeDogCompositeBowlSize, setCompositeDogCompositeBowlSize] =
+ React.useState(initialValues.compositeDogCompositeBowlSize);
+ const [
+ compositeDogCompositeOwnerFirstName,
+ setCompositeDogCompositeOwnerFirstName,
+ ] = React.useState(initialValues.compositeDogCompositeOwnerFirstName);
+ const [errors, setErrors] = React.useState({});
+ const resetStateValues = () => {
+ const cleanValues = compositeDogRecord
+ ? {
+ ...initialValues,
+ ...compositeDogRecord,
+ CompositeBowl,
+ CompositeOwner,
+ CompositeToys: linkedCompositeToys,
+ CompositeVets: linkedCompositeVets,
+ }
+ : initialValues;
+ setName(cleanValues.name);
+ setDescription(cleanValues.description);
+ setCompositeBowl(cleanValues.CompositeBowl);
+ setCurrentCompositeBowlValue(undefined);
+ setCurrentCompositeBowlDisplayValue(\\"\\");
+ setCompositeOwner(cleanValues.CompositeOwner);
+ setCurrentCompositeOwnerValue(undefined);
+ setCurrentCompositeOwnerDisplayValue(\\"\\");
+ setCompositeToys(cleanValues.CompositeToys ?? []);
+ setCurrentCompositeToysValue(undefined);
+ setCurrentCompositeToysDisplayValue(\\"\\");
+ setCompositeVets(cleanValues.CompositeVets ?? []);
+ setCurrentCompositeVetsValue(undefined);
+ setCurrentCompositeVetsDisplayValue(\\"\\");
+ setCompositeDogCompositeBowlSize(cleanValues.compositeDogCompositeBowlSize);
+ setCompositeDogCompositeOwnerFirstName(
+ cleanValues.compositeDogCompositeOwnerFirstName
+ );
+ setErrors({});
+ };
+ const [compositeDogRecord, setCompositeDogRecord] =
+ React.useState(compositeDog);
+ const [linkedCompositeToys, setLinkedCompositeToys] = React.useState([]);
+ const [linkedCompositeVets, setLinkedCompositeVets] = React.useState([]);
+ React.useEffect(() => {
+ const queryData = async () => {
+ const record = nameProp
+ ? await DataStore.query(CompositeDog, nameProp)
+ : compositeDog;
+ setCompositeDogRecord(record);
+ const CompositeBowlRecord = record
+ ? await record.CompositeBowl
+ : undefined;
+ setCompositeBowl(CompositeBowlRecord);
+ const CompositeOwnerRecord = record
+ ? await record.CompositeOwner
+ : undefined;
+ setCompositeOwner(CompositeOwnerRecord);
+ const linkedCompositeToys = record
+ ? await record.CompositeToys.toArray()
+ : [];
+ setLinkedCompositeToys(linkedCompositeToys);
+ const linkedCompositeVets = record
+ ? await Promise.all(
+ (
+ await record.CompositeVets.toArray()
+ ).map((r) => {
+ return r.compositeVet;
+ })
+ )
+ : [];
+ setLinkedCompositeVets(linkedCompositeVets);
+ };
+ queryData();
+ }, [nameProp, compositeDog]);
+ React.useEffect(resetStateValues, [
+ compositeDogRecord,
+ CompositeBowl,
+ CompositeOwner,
+ linkedCompositeToys,
+ linkedCompositeVets,
+ ]);
+ const [
+ currentCompositeBowlDisplayValue,
+ setCurrentCompositeBowlDisplayValue,
+ ] = React.useState(\\"\\");
+ const [currentCompositeBowlValue, setCurrentCompositeBowlValue] =
+ React.useState(undefined);
+ const CompositeBowlRef = React.createRef();
+ const [
+ currentCompositeOwnerDisplayValue,
+ setCurrentCompositeOwnerDisplayValue,
+ ] = React.useState(\\"\\");
+ const [currentCompositeOwnerValue, setCurrentCompositeOwnerValue] =
+ React.useState(undefined);
+ const CompositeOwnerRef = React.createRef();
+ const [
+ currentCompositeToysDisplayValue,
+ setCurrentCompositeToysDisplayValue,
+ ] = React.useState(\\"\\");
+ const [currentCompositeToysValue, setCurrentCompositeToysValue] =
+ React.useState(undefined);
+ const CompositeToysRef = React.createRef();
+ const [
+ currentCompositeVetsDisplayValue,
+ setCurrentCompositeVetsDisplayValue,
+ ] = React.useState(\\"\\");
+ const [currentCompositeVetsValue, setCurrentCompositeVetsValue] =
+ React.useState(undefined);
+ const CompositeVetsRef = React.createRef();
+ const getIDValue = {
+ CompositeBowl: (r) => JSON.stringify({ shape: r?.shape, size: r?.size }),
+ CompositeOwner: (r) =>
+ JSON.stringify({ lastName: r?.lastName, firstName: r?.firstName }),
+ CompositeToys: (r) => JSON.stringify({ kind: r?.kind, color: r?.color }),
+ CompositeVets: (r) =>
+ JSON.stringify({ specialty: r?.specialty, city: r?.city }),
+ };
+ const CompositeBowlIdSet = new Set(
+ Array.isArray(CompositeBowl)
+ ? CompositeBowl.map((r) => getIDValue.CompositeBowl?.(r))
+ : getIDValue.CompositeBowl?.(CompositeBowl)
+ );
+ const CompositeOwnerIdSet = new Set(
+ Array.isArray(CompositeOwner)
+ ? CompositeOwner.map((r) => getIDValue.CompositeOwner?.(r))
+ : getIDValue.CompositeOwner?.(CompositeOwner)
+ );
+ const CompositeToysIdSet = new Set(
+ Array.isArray(CompositeToys)
+ ? CompositeToys.map((r) => getIDValue.CompositeToys?.(r))
+ : getIDValue.CompositeToys?.(CompositeToys)
+ );
+ const CompositeVetsIdSet = new Set(
+ Array.isArray(CompositeVets)
+ ? CompositeVets.map((r) => getIDValue.CompositeVets?.(r))
+ : getIDValue.CompositeVets?.(CompositeVets)
+ );
+ const compositeBowlRecords = useDataStoreBinding({
+ type: \\"collection\\",
+ model: CompositeBowl0,
+ }).items;
+ const compositeOwnerRecords = useDataStoreBinding({
+ type: \\"collection\\",
+ model: CompositeOwner0,
+ }).items;
+ const compositeToyRecords = useDataStoreBinding({
+ type: \\"collection\\",
+ model: CompositeToy,
+ }).items;
+ const compositeVetRecords = useDataStoreBinding({
+ type: \\"collection\\",
+ model: CompositeVet,
+ }).items;
+ const getDisplayValue = {
+ CompositeBowl: (r) => \`\${r?.shape}\${\\" - \\"}\${r?.size}\`,
+ CompositeOwner: (r) => \`\${r?.lastName}\${\\" - \\"}\${r?.firstName}\`,
+ CompositeToys: (r) => \`\${r?.kind}\${\\" - \\"}\${r?.color}\`,
+ CompositeVets: (r) => \`\${r?.specialty}\${\\" - \\"}\${r?.city}\`,
+ };
+ const validations = {
+ name: [{ type: \\"Required\\" }],
+ description: [{ type: \\"Required\\" }],
+ CompositeBowl: [],
+ CompositeOwner: [],
+ CompositeToys: [],
+ CompositeVets: [],
+ compositeDogCompositeBowlSize: [],
+ compositeDogCompositeOwnerFirstName: [],
+ };
+ const runValidationTasks = async (
+ fieldName,
+ currentValue,
+ getDisplayValue
+ ) => {
+ const value = getDisplayValue
+ ? getDisplayValue(currentValue)
+ : currentValue;
+ let validationResponse = validateField(value, validations[fieldName]);
+ const customValidator = fetchByPath(onValidate, fieldName);
+ if (customValidator) {
+ validationResponse = await customValidator(value, validationResponse);
+ }
+ setErrors((errors) => ({ ...errors, [fieldName]: validationResponse }));
+ return validationResponse;
+ };
+ return (
+ {
+ event.preventDefault();
+ let modelFields = {
+ name,
+ description,
+ CompositeBowl,
+ CompositeOwner,
+ CompositeToys,
+ CompositeVets,
+ compositeDogCompositeBowlSize,
+ compositeDogCompositeOwnerFirstName,
+ };
+ const validationResponses = await Promise.all(
+ Object.keys(validations).reduce((promises, fieldName) => {
+ if (Array.isArray(modelFields[fieldName])) {
+ promises.push(
+ ...modelFields[fieldName].map((item) =>
+ runValidationTasks(
+ fieldName,
+ item,
+ getDisplayValue[fieldName]
+ )
+ )
+ );
+ return promises;
+ }
+ promises.push(
+ runValidationTasks(
+ fieldName,
+ modelFields[fieldName],
+ getDisplayValue[fieldName]
+ )
+ );
+ return promises;
+ }, [])
+ );
+ if (validationResponses.some((r) => r.hasError)) {
+ return;
+ }
+ if (onSubmit) {
+ modelFields = onSubmit(modelFields);
+ }
+ try {
+ const promises = [];
+ const compositeToysToLink = [];
+ const compositeToysToUnLink = [];
+ const compositeToysSet = new Set();
+ const linkedCompositeToysSet = new Set();
+ CompositeToys.forEach((r) => compositeToysSet.add(r.kind));
+ linkedCompositeToys.forEach((r) =>
+ linkedCompositeToysSet.add(r.kind)
+ );
+ linkedCompositeToys.forEach((r) => {
+ if (!compositeToysSet.has(r.id)) {
+ compositeToysToUnLink.push(r);
+ }
+ });
+ CompositeToys.forEach((r) => {
+ if (!linkedCompositeToysSet.has(r.id)) {
+ compositeToysToLink.push(r);
+ }
+ });
+ compositeToysToUnLink.forEach((original) => {
+ promises.push(
+ DataStore.save(
+ CompositeToy.copyOf(original, (updated) => {
+ updated.compositeDogCompositeToysName = null;
+ })
+ )
+ );
+ });
+ compositeToysToLink.forEach((original) => {
+ promises.push(
+ DataStore.save(
+ CompositeToy.copyOf(original, (updated) => {
+ updated.compositeDogCompositeToysName =
+ compositeDogRecord.name;
+ })
+ )
+ );
+ });
+ const compositeVetsToLinkMap = new Map();
+ const compositeVetsToUnLinkMap = new Map();
+ const compositeVetsMap = new Map();
+ const linkedCompositeVetsMap = new Map();
+ CompositeVets.forEach((r) => {
+ const count = compositeVetsMap.get(r.specialty);
+ const newCount = count ? count + 1 : 1;
+ compositeVetsMap.set(r.specialty, newCount);
+ });
+ linkedCompositeVets.forEach((r) => {
+ const count = linkedCompositeVetsMap.get(r.specialty);
+ const newCount = count ? count + 1 : 1;
+ linkedCompositeVetsMap.set(r.specialty, newCount);
+ });
+ linkedCompositeVetsMap.forEach((count, id) => {
+ const newCount = compositeVetsMap.get(id);
+ if (newCount) {
+ const diffCount = count - newCount;
+ if (diffCount > 0) {
+ compositeVetsToUnLinkMap.set(id, diffCount);
+ }
+ } else {
+ compositeVetsToUnLinkMap.set(id, count);
+ }
+ });
+ compositeVetsMap.forEach((count, id) => {
+ const originalCount = linkedCompositeVetsMap.get(id);
+ if (originalCount) {
+ const diffCount = count - originalCount;
+ if (diffCount > 0) {
+ compositeVetsToLinkMap.set(id, diffCount);
+ }
+ } else {
+ compositeVetsToLinkMap.set(id, count);
+ }
+ });
+ compositeVetsToUnLinkMap.forEach(async (count, id) => {
+ const compositeDogCompositeVetRecords = await DataStore.query(
+ CompositeDogCompositeVet,
+ (r) =>
+ r.and((r) => [
+ r.compositeVetID.eq(id),
+ r.compositeDogID.eq(compositeDogRecord.name),
+ ])
+ );
+ for (let i = 0; i < count; i++) {
+ promises.push(
+ DataStore.delete(compositeDogCompositeVetRecords[i])
+ );
+ }
+ });
+ compositeVetsToLinkMap.forEach((count, id) => {
+ for (let i = count; i > 0; i--) {
+ promises.push(
+ DataStore.save(
+ new CompositeDogCompositeVet({
+ compositeDogID: compositeDogRecord.name,
+ compositeVetID: id,
+ })
+ )
+ );
+ }
+ });
+ promises.push(
+ DataStore.save(
+ CompositeDog.copyOf(compositeDogRecord, (updated) => {
+ Object.assign(updated, modelFields);
+ if (!modelFields.CompositeBowl)
+ updated.compositeDogCompositeBowlShape = undefined;
+ if (!modelFields.CompositeOwner)
+ updated.compositeDogCompositeOwnerLastName = undefined;
+ })
+ )
+ );
+ await Promise.all(promises);
+ if (onSuccess) {
+ onSuccess(modelFields);
+ }
+ } catch (err) {
+ if (onError) {
+ onError(modelFields, err.message);
+ }
+ }
+ }}
+ {...rest}
+ {...getOverrideProps(overrides, \\"UpdateCompositeDogForm\\")}
+ >
+ {
+ let { value } = e.target;
+ if (onChange) {
+ const modelFields = {
+ name: value,
+ description,
+ CompositeBowl,
+ CompositeOwner,
+ CompositeToys,
+ CompositeVets,
+ compositeDogCompositeBowlSize,
+ compositeDogCompositeOwnerFirstName,
+ };
+ const result = onChange(modelFields);
+ value = result?.name ?? value;
+ }
+ if (errors.name?.hasError) {
+ runValidationTasks(\\"name\\", value);
+ }
+ setName(value);
+ }}
+ onBlur={() => runValidationTasks(\\"name\\", name)}
+ errorMessage={errors.name?.errorMessage}
+ hasError={errors.name?.hasError}
+ {...getOverrideProps(overrides, \\"name\\")}
+ >
+ {
+ let { value } = e.target;
+ if (onChange) {
+ const modelFields = {
+ name,
+ description: value,
+ CompositeBowl,
+ CompositeOwner,
+ CompositeToys,
+ CompositeVets,
+ compositeDogCompositeBowlSize,
+ compositeDogCompositeOwnerFirstName,
+ };
+ const result = onChange(modelFields);
+ value = result?.description ?? value;
+ }
+ if (errors.description?.hasError) {
+ runValidationTasks(\\"description\\", value);
+ }
+ setDescription(value);
+ }}
+ onBlur={() => runValidationTasks(\\"description\\", description)}
+ errorMessage={errors.description?.errorMessage}
+ hasError={errors.description?.hasError}
+ {...getOverrideProps(overrides, \\"description\\")}
+ >
+ {
+ let value = items[0];
+ if (onChange) {
+ const modelFields = {
+ name,
+ description,
+ CompositeBowl: value,
+ CompositeOwner,
+ CompositeToys,
+ CompositeVets,
+ compositeDogCompositeBowlSize,
+ compositeDogCompositeOwnerFirstName,
+ };
+ const result = onChange(modelFields);
+ value = result?.CompositeBowl ?? value;
+ }
+ setCompositeBowl(value);
+ setCurrentCompositeBowlValue(undefined);
+ setCurrentCompositeBowlDisplayValue(\\"\\");
+ }}
+ currentFieldValue={currentCompositeBowlValue}
+ label={\\"Composite bowl\\"}
+ items={CompositeBowl ? [CompositeBowl] : []}
+ hasError={errors.CompositeBowl?.hasError}
+ getBadgeText={getDisplayValue.CompositeBowl}
+ setFieldValue={(model) => {
+ setCurrentCompositeBowlDisplayValue(
+ getDisplayValue.CompositeBowl(model)
+ );
+ setCurrentCompositeBowlValue(model);
+ }}
+ inputFieldRef={CompositeBowlRef}
+ defaultFieldValue={\\"\\"}
+ >
+ !CompositeBowlIdSet.has(getIDValue.CompositeBowl?.(r))
+ )
+ .map((r) => ({
+ id: getIDValue.CompositeBowl?.(r),
+ label: getDisplayValue.CompositeBowl?.(r),
+ }))}
+ onSelect={({ id, label }) => {
+ setCurrentCompositeBowlValue(
+ compositeBowlRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
+ );
+ setCurrentCompositeBowlDisplayValue(label);
+ }}
+ onClear={() => {
+ setCurrentCompositeBowlDisplayValue(\\"\\");
+ }}
+ defaultValue={CompositeBowl}
+ onChange={(e) => {
+ let { value } = e.target;
+ if (errors.CompositeBowl?.hasError) {
+ runValidationTasks(\\"CompositeBowl\\", value);
+ }
+ setCurrentCompositeBowlDisplayValue(value);
+ setCurrentCompositeBowlValue(undefined);
+ }}
+ onBlur={() => runValidationTasks(\\"CompositeBowl\\", CompositeBowl)}
+ errorMessage={errors.CompositeBowl?.errorMessage}
+ hasError={errors.CompositeBowl?.hasError}
+ ref={CompositeBowlRef}
+ labelHidden={true}
+ {...getOverrideProps(overrides, \\"CompositeBowl\\")}
+ >
+
+ {
+ let value = items[0];
+ if (onChange) {
+ const modelFields = {
+ name,
+ description,
+ CompositeBowl,
+ CompositeOwner: value,
+ CompositeToys,
+ CompositeVets,
+ compositeDogCompositeBowlSize,
+ compositeDogCompositeOwnerFirstName,
+ };
+ const result = onChange(modelFields);
+ value = result?.CompositeOwner ?? value;
+ }
+ setCompositeOwner(value);
+ setCurrentCompositeOwnerValue(undefined);
+ setCurrentCompositeOwnerDisplayValue(\\"\\");
+ }}
+ currentFieldValue={currentCompositeOwnerValue}
+ label={\\"Composite owner\\"}
+ items={CompositeOwner ? [CompositeOwner] : []}
+ hasError={errors.CompositeOwner?.hasError}
+ getBadgeText={getDisplayValue.CompositeOwner}
+ setFieldValue={(model) => {
+ setCurrentCompositeOwnerDisplayValue(
+ getDisplayValue.CompositeOwner(model)
+ );
+ setCurrentCompositeOwnerValue(model);
+ }}
+ inputFieldRef={CompositeOwnerRef}
+ defaultFieldValue={\\"\\"}
+ >
+ !CompositeOwnerIdSet.has(getIDValue.CompositeOwner?.(r))
+ )
+ .map((r) => ({
+ id: getIDValue.CompositeOwner?.(r),
+ label: getDisplayValue.CompositeOwner?.(r),
+ }))}
+ onSelect={({ id, label }) => {
+ setCurrentCompositeOwnerValue(
+ compositeOwnerRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
+ );
+ setCurrentCompositeOwnerDisplayValue(label);
+ }}
+ onClear={() => {
+ setCurrentCompositeOwnerDisplayValue(\\"\\");
+ }}
+ defaultValue={CompositeOwner}
+ onChange={(e) => {
+ let { value } = e.target;
+ if (errors.CompositeOwner?.hasError) {
+ runValidationTasks(\\"CompositeOwner\\", value);
+ }
+ setCurrentCompositeOwnerDisplayValue(value);
+ setCurrentCompositeOwnerValue(undefined);
+ }}
+ onBlur={() => runValidationTasks(\\"CompositeOwner\\", CompositeOwner)}
+ errorMessage={errors.CompositeOwner?.errorMessage}
+ hasError={errors.CompositeOwner?.hasError}
+ ref={CompositeOwnerRef}
+ labelHidden={true}
+ {...getOverrideProps(overrides, \\"CompositeOwner\\")}
+ >
+
+ {
+ let values = items;
+ if (onChange) {
+ const modelFields = {
+ name,
+ description,
+ CompositeBowl,
+ CompositeOwner,
+ CompositeToys: values,
+ CompositeVets,
+ compositeDogCompositeBowlSize,
+ compositeDogCompositeOwnerFirstName,
+ };
+ const result = onChange(modelFields);
+ values = result?.CompositeToys ?? values;
+ }
+ setCompositeToys(values);
+ setCurrentCompositeToysValue(undefined);
+ setCurrentCompositeToysDisplayValue(\\"\\");
+ }}
+ currentFieldValue={currentCompositeToysValue}
+ label={\\"Composite toys\\"}
+ items={CompositeToys}
+ hasError={errors.CompositeToys?.hasError}
+ getBadgeText={getDisplayValue.CompositeToys}
+ setFieldValue={(model) => {
+ setCurrentCompositeToysDisplayValue(
+ getDisplayValue.CompositeToys(model)
+ );
+ setCurrentCompositeToysValue(model);
+ }}
+ inputFieldRef={CompositeToysRef}
+ defaultFieldValue={\\"\\"}
+ >
+ !CompositeToysIdSet.has(getIDValue.CompositeToys?.(r))
+ )
+ .map((r) => ({
+ id: getIDValue.CompositeToys?.(r),
+ label: getDisplayValue.CompositeToys?.(r),
+ }))}
+ onSelect={({ id, label }) => {
+ setCurrentCompositeToysValue(
+ compositeToyRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
+ );
+ setCurrentCompositeToysDisplayValue(label);
+ }}
+ onClear={() => {
+ setCurrentCompositeToysDisplayValue(\\"\\");
+ }}
+ onChange={(e) => {
+ let { value } = e.target;
+ if (errors.CompositeToys?.hasError) {
+ runValidationTasks(\\"CompositeToys\\", value);
+ }
+ setCurrentCompositeToysDisplayValue(value);
+ setCurrentCompositeToysValue(undefined);
+ }}
+ onBlur={() =>
+ runValidationTasks(\\"CompositeToys\\", currentCompositeToysValue)
+ }
+ errorMessage={errors.CompositeToys?.errorMessage}
+ hasError={errors.CompositeToys?.hasError}
+ ref={CompositeToysRef}
+ labelHidden={true}
+ {...getOverrideProps(overrides, \\"CompositeToys\\")}
+ >
+
+ {
+ let values = items;
+ if (onChange) {
+ const modelFields = {
+ name,
+ description,
+ CompositeBowl,
+ CompositeOwner,
+ CompositeToys,
+ CompositeVets: values,
+ compositeDogCompositeBowlSize,
+ compositeDogCompositeOwnerFirstName,
+ };
+ const result = onChange(modelFields);
+ values = result?.CompositeVets ?? values;
+ }
+ setCompositeVets(values);
+ setCurrentCompositeVetsValue(undefined);
+ setCurrentCompositeVetsDisplayValue(\\"\\");
+ }}
+ currentFieldValue={currentCompositeVetsValue}
+ label={\\"Composite vets\\"}
+ items={CompositeVets}
+ hasError={errors.CompositeVets?.hasError}
+ getBadgeText={getDisplayValue.CompositeVets}
+ setFieldValue={(model) => {
+ setCurrentCompositeVetsDisplayValue(
+ getDisplayValue.CompositeVets(model)
+ );
+ setCurrentCompositeVetsValue(model);
+ }}
+ inputFieldRef={CompositeVetsRef}
+ defaultFieldValue={\\"\\"}
+ >
+ !CompositeVetsIdSet.has(getIDValue.CompositeVets?.(r))
+ )
+ .map((r) => ({
+ id: getIDValue.CompositeVets?.(r),
+ label: getDisplayValue.CompositeVets?.(r),
+ }))}
+ onSelect={({ id, label }) => {
+ setCurrentCompositeVetsValue(
+ compositeVetRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
+ );
+ setCurrentCompositeVetsDisplayValue(label);
+ }}
+ onClear={() => {
+ setCurrentCompositeVetsDisplayValue(\\"\\");
+ }}
+ onChange={(e) => {
+ let { value } = e.target;
+ if (errors.CompositeVets?.hasError) {
+ runValidationTasks(\\"CompositeVets\\", value);
+ }
+ setCurrentCompositeVetsDisplayValue(value);
+ setCurrentCompositeVetsValue(undefined);
+ }}
+ onBlur={() =>
+ runValidationTasks(\\"CompositeVets\\", currentCompositeVetsValue)
+ }
+ errorMessage={errors.CompositeVets?.errorMessage}
+ hasError={errors.CompositeVets?.hasError}
+ ref={CompositeVetsRef}
+ labelHidden={true}
+ {...getOverrideProps(overrides, \\"CompositeVets\\")}
+ >
+
+ {
+ let { value } = e.target;
+ if (onChange) {
+ const modelFields = {
+ name,
+ description,
+ CompositeBowl,
+ CompositeOwner,
+ CompositeToys,
+ CompositeVets,
+ compositeDogCompositeBowlSize: value,
+ compositeDogCompositeOwnerFirstName,
+ };
+ const result = onChange(modelFields);
+ value = result?.compositeDogCompositeBowlSize ?? value;
+ }
+ if (errors.compositeDogCompositeBowlSize?.hasError) {
+ runValidationTasks(\\"compositeDogCompositeBowlSize\\", value);
+ }
+ setCompositeDogCompositeBowlSize(value);
+ }}
+ onBlur={() =>
+ runValidationTasks(
+ \\"compositeDogCompositeBowlSize\\",
+ compositeDogCompositeBowlSize
+ )
+ }
+ errorMessage={errors.compositeDogCompositeBowlSize?.errorMessage}
+ hasError={errors.compositeDogCompositeBowlSize?.hasError}
+ {...getOverrideProps(overrides, \\"compositeDogCompositeBowlSize\\")}
+ >
+ {
+ let { value } = e.target;
+ if (onChange) {
+ const modelFields = {
+ name,
+ description,
+ CompositeBowl,
+ CompositeOwner,
+ CompositeToys,
+ CompositeVets,
+ compositeDogCompositeBowlSize,
+ compositeDogCompositeOwnerFirstName: value,
+ };
+ const result = onChange(modelFields);
+ value = result?.compositeDogCompositeOwnerFirstName ?? value;
+ }
+ if (errors.compositeDogCompositeOwnerFirstName?.hasError) {
+ runValidationTasks(\\"compositeDogCompositeOwnerFirstName\\", value);
+ }
+ setCompositeDogCompositeOwnerFirstName(value);
+ }}
+ onBlur={() =>
+ runValidationTasks(
+ \\"compositeDogCompositeOwnerFirstName\\",
+ compositeDogCompositeOwnerFirstName
+ )
+ }
+ errorMessage={errors.compositeDogCompositeOwnerFirstName?.errorMessage}
+ hasError={errors.compositeDogCompositeOwnerFirstName?.hasError}
+ {...getOverrideProps(overrides, \\"compositeDogCompositeOwnerFirstName\\")}
+ >
+
+
+
+
+
+
+
+ );
+}
+"
+`;
+
+exports[`amplify form renderer tests datastore form tests custom form tests should render an update form for model with composite keys 2`] = `
+"import * as React from \\"react\\";
+import { AutocompleteProps, GridProps, TextFieldProps } from \\"@aws-amplify/ui-react\\";
+import { EscapeHatchProps } from \\"@aws-amplify/ui-react/internal\\";
+import { CompositeDog, CompositeBowl as CompositeBowl0, CompositeOwner as CompositeOwner0, CompositeToy, CompositeVet } from \\"../models\\";
+export declare type ValidationResponse = {
+ hasError: boolean;
+ errorMessage?: string;
+};
+export declare type ValidationFunction = (value: T, validationResponse: ValidationResponse) => ValidationResponse | Promise;
+export declare type UpdateCompositeDogFormInputValues = {
+ name?: string;
+ description?: string;
+ CompositeBowl?: CompositeBowl0;
+ CompositeOwner?: CompositeOwner0;
+ CompositeToys?: CompositeToy[];
+ CompositeVets?: CompositeVet[];
+ compositeDogCompositeBowlSize?: string;
+ compositeDogCompositeOwnerFirstName?: string;
+};
+export declare type UpdateCompositeDogFormValidationValues = {
+ name?: ValidationFunction;
+ description?: ValidationFunction;
+ CompositeBowl?: ValidationFunction;
+ CompositeOwner?: ValidationFunction;
+ CompositeToys?: ValidationFunction;
+ CompositeVets?: ValidationFunction;
+ compositeDogCompositeBowlSize?: ValidationFunction;
+ compositeDogCompositeOwnerFirstName?: ValidationFunction;
+};
+export declare type PrimitiveOverrideProps = Partial & React.DOMAttributes;
+export declare type UpdateCompositeDogFormOverridesProps = {
+ UpdateCompositeDogFormGrid?: PrimitiveOverrideProps;
+ name?: PrimitiveOverrideProps;
+ description?: PrimitiveOverrideProps;
+ CompositeBowl?: PrimitiveOverrideProps;
+ CompositeOwner?: PrimitiveOverrideProps;
+ CompositeToys?: PrimitiveOverrideProps;
+ CompositeVets?: PrimitiveOverrideProps;
+ compositeDogCompositeBowlSize?: PrimitiveOverrideProps;
+ compositeDogCompositeOwnerFirstName?: PrimitiveOverrideProps;
+} & EscapeHatchProps;
+export declare type UpdateCompositeDogFormProps = React.PropsWithChildren<{
+ overrides?: UpdateCompositeDogFormOverridesProps | undefined | null;
+} & {
+ name?: string;
+ compositeDog?: CompositeDog;
+ onSubmit?: (fields: UpdateCompositeDogFormInputValues) => UpdateCompositeDogFormInputValues;
+ onSuccess?: (fields: UpdateCompositeDogFormInputValues) => void;
+ onError?: (fields: UpdateCompositeDogFormInputValues, errorMessage: string) => void;
+ onChange?: (fields: UpdateCompositeDogFormInputValues) => UpdateCompositeDogFormInputValues;
+ onValidate?: UpdateCompositeDogFormValidationValues;
+} & React.CSSProperties>;
+export default function UpdateCompositeDogForm(props: UpdateCompositeDogFormProps): React.ReactElement;
+"
+`;
+
exports[`amplify form renderer tests datastore form tests custom form tests should render an update form for model with cpk 1`] = `
"/* eslint-disable */
import * as React from \\"react\\";
@@ -2464,21 +3573,6 @@ export default function UpdateCPKTeacherForm(props) {
const [CPKProjects, setCPKProjects] = React.useState(
initialValues.CPKProjects
);
- const CPKStudentIdSet = new Set(
- Array.isArray(CPKStudent)
- ? CPKStudent.map((cPKStudent) => cPKStudent.id)
- : CPKStudent?.id
- );
- const CPKClassesIdSet = new Set(
- Array.isArray(CPKClasses)
- ? CPKClasses.map((cPKClass) => cPKClass.id)
- : CPKClasses?.id
- );
- const CPKProjectsIdSet = new Set(
- Array.isArray(CPKProjects)
- ? CPKProjects.map((cPKProject) => cPKProject.id)
- : CPKProjects?.id
- );
const [errors, setErrors] = React.useState({});
const resetStateValues = () => {
const cleanValues = cPKTeacherRecord
@@ -2551,6 +3645,28 @@ export default function UpdateCPKTeacherForm(props) {
const [currentCPKProjectsValue, setCurrentCPKProjectsValue] =
React.useState(undefined);
const CPKProjectsRef = React.createRef();
+ const getIDValue = {
+ CPKStudent: (r) =>
+ JSON.stringify({ specialStudentId: r?.specialStudentId }),
+ CPKClasses: (r) => JSON.stringify({ specialClassId: r?.specialClassId }),
+ CPKProjects: (r) =>
+ JSON.stringify({ specialProjectId: r?.specialProjectId }),
+ };
+ const CPKStudentIdSet = new Set(
+ Array.isArray(CPKStudent)
+ ? CPKStudent.map((r) => getIDValue.CPKStudent?.(r))
+ : getIDValue.CPKStudent?.(CPKStudent)
+ );
+ const CPKClassesIdSet = new Set(
+ Array.isArray(CPKClasses)
+ ? CPKClasses.map((r) => getIDValue.CPKClasses?.(r))
+ : getIDValue.CPKClasses?.(CPKClasses)
+ );
+ const CPKProjectsIdSet = new Set(
+ Array.isArray(CPKProjects)
+ ? CPKProjects.map((r) => getIDValue.CPKProjects?.(r))
+ : getIDValue.CPKProjects?.(CPKProjects)
+ );
const cPKStudentRecords = useDataStoreBinding({
type: \\"collection\\",
model: CPKStudent0,
@@ -2564,9 +3680,9 @@ export default function UpdateCPKTeacherForm(props) {
model: CPKProject,
}).items;
const getDisplayValue = {
- CPKStudent: (record) => record?.specialStudentId,
- CPKClasses: (record) => record?.specialClassId,
- CPKProjects: (record) => record?.specialProjectId,
+ CPKStudent: (r) => r?.specialStudentId,
+ CPKClasses: (r) => r?.specialClassId,
+ CPKProjects: (r) => r?.specialProjectId,
};
const validations = {
specialTeacherId: [{ type: \\"Required\\" }],
@@ -2818,14 +3934,18 @@ export default function UpdateCPKTeacherForm(props) {
isReadOnly={false}
value={currentCPKStudentDisplayValue}
options={cPKStudentRecords
- .filter((cPKStudent) => !CPKStudentIdSet.has(cPKStudent.id))
+ .filter((r) => !CPKStudentIdSet.has(getIDValue.CPKStudent?.(r)))
.map((r) => ({
- id: r.specialStudentId,
+ id: getIDValue.CPKStudent?.(r),
label: getDisplayValue.CPKStudent?.(r),
}))}
onSelect={({ id, label }) => {
setCurrentCPKStudentValue(
- cPKStudentRecords.find((r) => r.specialStudentId === id)
+ cPKStudentRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
);
setCurrentCPKStudentDisplayValue(label);
}}
@@ -2884,14 +4004,18 @@ export default function UpdateCPKTeacherForm(props) {
isReadOnly={false}
value={currentCPKClassesDisplayValue}
options={cPKClassRecords
- .filter((cPKClass) => !CPKClassesIdSet.has(cPKClass.id))
+ .filter((r) => !CPKClassesIdSet.has(getIDValue.CPKClasses?.(r)))
.map((r) => ({
- id: r.specialClassId,
+ id: getIDValue.CPKClasses?.(r),
label: getDisplayValue.CPKClasses?.(r),
}))}
onSelect={({ id, label }) => {
setCurrentCPKClassesValue(
- cPKClassRecords.find((r) => r.specialClassId === id)
+ cPKClassRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
);
setCurrentCPKClassesDisplayValue(label);
}}
@@ -2951,14 +4075,18 @@ export default function UpdateCPKTeacherForm(props) {
isReadOnly={false}
value={currentCPKProjectsDisplayValue}
options={cPKProjectRecords
- .filter((cPKProject) => !CPKProjectsIdSet.has(cPKProject.id))
+ .filter((r) => !CPKProjectsIdSet.has(getIDValue.CPKProjects?.(r)))
.map((r) => ({
- id: r.specialProjectId,
+ id: getIDValue.CPKProjects?.(r),
label: getDisplayValue.CPKProjects?.(r),
}))}
onSelect={({ id, label }) => {
setCurrentCPKProjectsValue(
- cPKProjectRecords.find((r) => r.specialProjectId === id)
+ cPKProjectRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
);
setCurrentCPKProjectsDisplayValue(label);
}}
@@ -5015,9 +6143,6 @@ export default function TagCreateForm(props) {
const [label, setLabel] = React.useState(initialValues.label);
const [Posts, setPosts] = React.useState(initialValues.Posts);
const [statuses, setStatuses] = React.useState(initialValues.statuses);
- const PostsIdSet = new Set(
- Array.isArray(Posts) ? Posts.map((post) => post.id) : Posts?.id
- );
const [errors, setErrors] = React.useState({});
const resetStateValues = () => {
setLabel(initialValues.label);
@@ -5035,19 +6160,27 @@ export default function TagCreateForm(props) {
const [currentStatusesValue, setCurrentStatusesValue] =
React.useState(undefined);
const statusesRef = React.createRef();
+ const getIDValue = {
+ Posts: (r) => JSON.stringify({ id: r?.id }),
+ };
+ const PostsIdSet = new Set(
+ Array.isArray(Posts)
+ ? Posts.map((r) => getIDValue.Posts?.(r))
+ : getIDValue.Posts?.(Posts)
+ );
const postRecords = useDataStoreBinding({
type: \\"collection\\",
model: Post,
}).items;
const getDisplayValue = {
- Posts: (record) => record?.title,
- statuses: (record) => {
+ Posts: (r) => r?.title,
+ statuses: (r) => {
const enumDisplayValueMap = {
PENDING: \\"Pending\\",
POSTED: \\"Posted\\",
IN_REVIEW: \\"In review\\",
};
- return enumDisplayValueMap[record];
+ return enumDisplayValueMap[r];
},
};
const validations = {
@@ -5203,13 +6336,19 @@ export default function TagCreateForm(props) {
isReadOnly={false}
value={currentPostsDisplayValue}
options={postRecords
- .filter((post) => !PostsIdSet.has(post.id))
+ .filter((r) => !PostsIdSet.has(getIDValue.Posts?.(r)))
.map((r) => ({
- id: r.id,
+ id: getIDValue.Posts?.(r),
label: getDisplayValue.Posts?.(r),
}))}
onSelect={({ id, label }) => {
- setCurrentPostsValue(postRecords.find((r) => r.id === id));
+ setCurrentPostsValue(
+ postRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
+ );
setCurrentPostsDisplayValue(label);
}}
onClear={() => {
@@ -5558,9 +6697,6 @@ export default function MyMemberForm(props) {
};
const [name, setName] = React.useState(initialValues.name);
const [Team, setTeam] = React.useState(initialValues.Team);
- const TeamIdSet = new Set(
- Array.isArray(Team) ? Team.map((team) => team.id) : Team?.id
- );
const [errors, setErrors] = React.useState({});
const resetStateValues = () => {
setName(initialValues.name);
@@ -5573,12 +6709,20 @@ export default function MyMemberForm(props) {
React.useState(\\"\\");
const [currentTeamValue, setCurrentTeamValue] = React.useState(undefined);
const TeamRef = React.createRef();
+ const getIDValue = {
+ Team: (r) => JSON.stringify({ id: r?.id }),
+ };
+ const TeamIdSet = new Set(
+ Array.isArray(Team)
+ ? Team.map((r) => getIDValue.Team?.(r))
+ : getIDValue.Team?.(Team)
+ );
const teamRecords = useDataStoreBinding({
type: \\"collection\\",
model: Team0,
}).items;
const getDisplayValue = {
- Team: (record) => record?.name,
+ Team: (r) => r?.name,
};
const validations = {
name: [],
@@ -5748,13 +6892,19 @@ export default function MyMemberForm(props) {
isReadOnly={false}
value={currentTeamDisplayValue}
options={teamRecords
- .filter((team) => !TeamIdSet.has(team.id))
+ .filter((r) => !TeamIdSet.has(getIDValue.Team?.(r)))
.map((r) => ({
- id: r.id,
+ id: getIDValue.Team?.(r),
label: getDisplayValue.Team?.(r),
}))}
onSelect={({ id, label }) => {
- setCurrentTeamValue(teamRecords.find((r) => r.id === id));
+ setCurrentTeamValue(
+ teamRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
+ );
setCurrentTeamDisplayValue(label);
}}
onClear={() => {
@@ -6009,11 +7159,6 @@ export default function SchoolCreateForm(props) {
};
const [name, setName] = React.useState(initialValues.name);
const [Students, setStudents] = React.useState(initialValues.Students);
- const StudentsIdSet = new Set(
- Array.isArray(Students)
- ? Students.map((student) => student.id)
- : Students?.id
- );
const [errors, setErrors] = React.useState({});
const resetStateValues = () => {
setName(initialValues.name);
@@ -6027,12 +7172,20 @@ export default function SchoolCreateForm(props) {
const [currentStudentsValue, setCurrentStudentsValue] =
React.useState(undefined);
const StudentsRef = React.createRef();
+ const getIDValue = {
+ Students: (r) => JSON.stringify({ id: r?.id }),
+ };
+ const StudentsIdSet = new Set(
+ Array.isArray(Students)
+ ? Students.map((r) => getIDValue.Students?.(r))
+ : getIDValue.Students?.(Students)
+ );
const studentRecords = useDataStoreBinding({
type: \\"collection\\",
model: Student,
}).items;
const getDisplayValue = {
- Students: (record) => record?.name,
+ Students: (r) => r?.name,
};
const validations = {
name: [],
@@ -6182,13 +7335,19 @@ export default function SchoolCreateForm(props) {
isReadOnly={false}
value={currentStudentsDisplayValue}
options={studentRecords
- .filter((student) => !StudentsIdSet.has(student.id))
+ .filter((r) => !StudentsIdSet.has(getIDValue.Students?.(r)))
.map((r) => ({
- id: r.id,
+ id: getIDValue.Students?.(r),
label: getDisplayValue.Students?.(r),
}))}
onSelect={({ id, label }) => {
- setCurrentStudentsValue(studentRecords.find((r) => r.id === id));
+ setCurrentStudentsValue(
+ studentRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
+ );
setCurrentStudentsDisplayValue(label);
}}
onClear={() => {
@@ -6476,11 +7635,6 @@ export default function BookCreateForm(props) {
const [primaryAuthor, setPrimaryAuthor] = React.useState(
initialValues.primaryAuthor
);
- const primaryAuthorIdSet = new Set(
- Array.isArray(primaryAuthor)
- ? primaryAuthor.map((author) => author.id)
- : primaryAuthor?.id
- );
const [errors, setErrors] = React.useState({});
const resetStateValues = () => {
setName(initialValues.name);
@@ -6496,12 +7650,20 @@ export default function BookCreateForm(props) {
const [currentPrimaryAuthorValue, setCurrentPrimaryAuthorValue] =
React.useState(undefined);
const primaryAuthorRef = React.createRef();
+ const getIDValue = {
+ primaryAuthor: (r) => JSON.stringify({ id: r?.id }),
+ };
+ const primaryAuthorIdSet = new Set(
+ Array.isArray(primaryAuthor)
+ ? primaryAuthor.map((r) => getIDValue.primaryAuthor?.(r))
+ : getIDValue.primaryAuthor?.(primaryAuthor)
+ );
const authorRecords = useDataStoreBinding({
type: \\"collection\\",
model: Author,
}).items;
const getDisplayValue = {
- primaryAuthor: (record) => record?.name,
+ primaryAuthor: (r) => r?.name,
};
const validations = {
name: [],
@@ -6673,14 +7835,20 @@ export default function BookCreateForm(props) {
isReadOnly={false}
value={currentPrimaryAuthorDisplayValue}
options={authorRecords
- .filter((author) => !primaryAuthorIdSet.has(author.id))
+ .filter(
+ (r) => !primaryAuthorIdSet.has(getIDValue.primaryAuthor?.(r))
+ )
.map((r) => ({
- id: r.id,
+ id: getIDValue.primaryAuthor?.(r),
label: getDisplayValue.primaryAuthor?.(r),
}))}
onSelect={({ id, label }) => {
setCurrentPrimaryAuthorValue(
- authorRecords.find((r) => r.id === id)
+ authorRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
);
setCurrentPrimaryAuthorDisplayValue(label);
}}
@@ -6939,9 +8107,6 @@ export default function TagCreateForm(props) {
const [label, setLabel] = React.useState(initialValues.label);
const [Posts, setPosts] = React.useState(initialValues.Posts);
const [statuses, setStatuses] = React.useState(initialValues.statuses);
- const PostsIdSet = new Set(
- Array.isArray(Posts) ? Posts.map((post) => post.id) : Posts?.id
- );
const [errors, setErrors] = React.useState({});
const resetStateValues = () => {
setLabel(initialValues.label);
@@ -6959,19 +8124,27 @@ export default function TagCreateForm(props) {
const [currentStatusesValue, setCurrentStatusesValue] =
React.useState(undefined);
const statusesRef = React.createRef();
+ const getIDValue = {
+ Posts: (r) => JSON.stringify({ id: r?.id }),
+ };
+ const PostsIdSet = new Set(
+ Array.isArray(Posts)
+ ? Posts.map((r) => getIDValue.Posts?.(r))
+ : getIDValue.Posts?.(Posts)
+ );
const postRecords = useDataStoreBinding({
type: \\"collection\\",
model: Post,
}).items;
const getDisplayValue = {
- Posts: (record) => record?.title,
- statuses: (record) => {
+ Posts: (r) => r?.title,
+ statuses: (r) => {
const enumDisplayValueMap = {
PENDING: \\"Pending\\",
POSTED: \\"Posted\\",
IN_REVIEW: \\"In review\\",
};
- return enumDisplayValueMap[record];
+ return enumDisplayValueMap[r];
},
};
const validations = {
@@ -7127,13 +8300,19 @@ export default function TagCreateForm(props) {
isReadOnly={false}
value={currentPostsDisplayValue}
options={postRecords
- .filter((post) => !PostsIdSet.has(post.id))
+ .filter((r) => !PostsIdSet.has(getIDValue.Posts?.(r)))
.map((r) => ({
- id: r.id,
+ id: getIDValue.Posts?.(r),
label: getDisplayValue.Posts?.(r),
}))}
onSelect={({ id, label }) => {
- setCurrentPostsValue(postRecords.find((r) => r.id === id));
+ setCurrentPostsValue(
+ postRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
+ );
setCurrentPostsDisplayValue(label);
}}
onClear={() => {
@@ -7488,16 +8667,6 @@ export default function BookCreateForm(props) {
const [primaryTitle, setPrimaryTitle] = React.useState(
initialValues.primaryTitle
);
- const primaryAuthorIdSet = new Set(
- Array.isArray(primaryAuthor)
- ? primaryAuthor.map((author) => author.id)
- : primaryAuthor?.id
- );
- const primaryTitleIdSet = new Set(
- Array.isArray(primaryTitle)
- ? primaryTitle.map((title) => title.id)
- : primaryTitle?.id
- );
const [errors, setErrors] = React.useState({});
const resetStateValues = () => {
setName(initialValues.name);
@@ -7521,6 +8690,20 @@ export default function BookCreateForm(props) {
const [currentPrimaryTitleValue, setCurrentPrimaryTitleValue] =
React.useState(undefined);
const primaryTitleRef = React.createRef();
+ const getIDValue = {
+ primaryAuthor: (r) => JSON.stringify({ id: r?.id }),
+ primaryTitle: (r) => JSON.stringify({ id: r?.id }),
+ };
+ const primaryAuthorIdSet = new Set(
+ Array.isArray(primaryAuthor)
+ ? primaryAuthor.map((r) => getIDValue.primaryAuthor?.(r))
+ : getIDValue.primaryAuthor?.(primaryAuthor)
+ );
+ const primaryTitleIdSet = new Set(
+ Array.isArray(primaryTitle)
+ ? primaryTitle.map((r) => getIDValue.primaryTitle?.(r))
+ : getIDValue.primaryTitle?.(primaryTitle)
+ );
const authorRecords = useDataStoreBinding({
type: \\"collection\\",
model: Author,
@@ -7530,8 +8713,8 @@ export default function BookCreateForm(props) {
model: Title,
}).items;
const getDisplayValue = {
- primaryAuthor: (record) => record?.name,
- primaryTitle: (record) => record?.name,
+ primaryAuthor: (r) => r?.name,
+ primaryTitle: (r) => r?.name,
};
const validations = {
name: [],
@@ -7707,14 +8890,20 @@ export default function BookCreateForm(props) {
isReadOnly={false}
value={currentPrimaryAuthorDisplayValue}
options={authorRecords
- .filter((author) => !primaryAuthorIdSet.has(author.id))
+ .filter(
+ (r) => !primaryAuthorIdSet.has(getIDValue.primaryAuthor?.(r))
+ )
.map((r) => ({
- id: r.id,
+ id: getIDValue.primaryAuthor?.(r),
label: getDisplayValue.primaryAuthor?.(r),
}))}
onSelect={({ id, label }) => {
setCurrentPrimaryAuthorValue(
- authorRecords.find((r) => r.id === id)
+ authorRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
);
setCurrentPrimaryAuthorDisplayValue(label);
}}
@@ -7774,13 +8963,19 @@ export default function BookCreateForm(props) {
isReadOnly={false}
value={currentPrimaryTitleDisplayValue}
options={titleRecords
- .filter((title) => !primaryTitleIdSet.has(title.id))
+ .filter((r) => !primaryTitleIdSet.has(getIDValue.primaryTitle?.(r)))
.map((r) => ({
- id: r.id,
+ id: getIDValue.primaryTitle?.(r),
label: getDisplayValue.primaryTitle?.(r),
}))}
onSelect={({ id, label }) => {
- setCurrentPrimaryTitleValue(titleRecords.find((r) => r.id === id));
+ setCurrentPrimaryTitleValue(
+ titleRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
+ );
setCurrentPrimaryTitleDisplayValue(label);
}}
onClear={() => {
@@ -8538,11 +9733,6 @@ export default function SchoolUpdateForm(props) {
};
const [name, setName] = React.useState(initialValues.name);
const [Students, setStudents] = React.useState(initialValues.Students);
- const StudentsIdSet = new Set(
- Array.isArray(Students)
- ? Students.map((student) => student.id)
- : Students?.id
- );
const [errors, setErrors] = React.useState({});
const resetStateValues = () => {
const cleanValues = schoolRecord
@@ -8571,12 +9761,20 @@ export default function SchoolUpdateForm(props) {
const [currentStudentsValue, setCurrentStudentsValue] =
React.useState(undefined);
const StudentsRef = React.createRef();
+ const getIDValue = {
+ Students: (r) => JSON.stringify({ id: r?.id }),
+ };
+ const StudentsIdSet = new Set(
+ Array.isArray(Students)
+ ? Students.map((r) => getIDValue.Students?.(r))
+ : getIDValue.Students?.(Students)
+ );
const studentRecords = useDataStoreBinding({
type: \\"collection\\",
model: Student,
}).items;
const getDisplayValue = {
- Students: (record) => record?.name,
+ Students: (r) => r?.name,
};
const validations = {
name: [],
@@ -8754,13 +9952,19 @@ export default function SchoolUpdateForm(props) {
isReadOnly={false}
value={currentStudentsDisplayValue}
options={studentRecords
- .filter((student) => !StudentsIdSet.has(student.id))
+ .filter((r) => !StudentsIdSet.has(getIDValue.Students?.(r)))
.map((r) => ({
- id: r.id,
+ id: getIDValue.Students?.(r),
label: getDisplayValue.Students?.(r),
}))}
onSelect={({ id, label }) => {
- setCurrentStudentsValue(studentRecords.find((r) => r.id === id));
+ setCurrentStudentsValue(
+ studentRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
+ );
setCurrentStudentsDisplayValue(label);
}}
onClear={() => {
@@ -9048,9 +10252,6 @@ export default function MyMemberForm(props) {
};
const [name, setName] = React.useState(initialValues.name);
const [Team, setTeam] = React.useState(initialValues.Team);
- const TeamIdSet = new Set(
- Array.isArray(Team) ? Team.map((team) => team.id) : Team?.id
- );
const [errors, setErrors] = React.useState({});
const resetStateValues = () => {
const cleanValues = memberRecord
@@ -9077,12 +10278,20 @@ export default function MyMemberForm(props) {
React.useState(\\"\\");
const [currentTeamValue, setCurrentTeamValue] = React.useState(undefined);
const TeamRef = React.createRef();
+ const getIDValue = {
+ Team: (r) => JSON.stringify({ id: r?.id }),
+ };
+ const TeamIdSet = new Set(
+ Array.isArray(Team)
+ ? Team.map((r) => getIDValue.Team?.(r))
+ : getIDValue.Team?.(Team)
+ );
const teamRecords = useDataStoreBinding({
type: \\"collection\\",
model: Team0,
}).items;
const getDisplayValue = {
- Team: (record) => record?.name,
+ Team: (r) => r?.name,
};
const validations = {
name: [],
@@ -9255,13 +10464,19 @@ export default function MyMemberForm(props) {
isReadOnly={false}
value={currentTeamDisplayValue}
options={teamRecords
- .filter((team) => !TeamIdSet.has(team.id))
+ .filter((r) => !TeamIdSet.has(getIDValue.Team?.(r)))
.map((r) => ({
- id: r.id,
+ id: getIDValue.Team?.(r),
label: getDisplayValue.Team?.(r),
}))}
onSelect={({ id, label }) => {
- setCurrentTeamValue(teamRecords.find((r) => r.id === id));
+ setCurrentTeamValue(
+ teamRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
+ );
setCurrentTeamDisplayValue(label);
}}
onClear={() => {
@@ -9522,9 +10737,6 @@ export default function TagUpdateForm(props) {
const [label, setLabel] = React.useState(initialValues.label);
const [Posts, setPosts] = React.useState(initialValues.Posts);
const [statuses, setStatuses] = React.useState(initialValues.statuses);
- const PostsIdSet = new Set(
- Array.isArray(Posts) ? Posts.map((post) => post.id) : Posts?.id
- );
const [errors, setErrors] = React.useState({});
const resetStateValues = () => {
const cleanValues = tagRecord
@@ -9565,19 +10777,27 @@ export default function TagUpdateForm(props) {
const [currentStatusesValue, setCurrentStatusesValue] =
React.useState(undefined);
const statusesRef = React.createRef();
+ const getIDValue = {
+ Posts: (r) => JSON.stringify({ id: r?.id }),
+ };
+ const PostsIdSet = new Set(
+ Array.isArray(Posts)
+ ? Posts.map((r) => getIDValue.Posts?.(r))
+ : getIDValue.Posts?.(Posts)
+ );
const postRecords = useDataStoreBinding({
type: \\"collection\\",
model: Post,
}).items;
const getDisplayValue = {
- Posts: (record) => record?.title,
- statuses: (record) => {
+ Posts: (r) => r?.title,
+ statuses: (r) => {
const enumDisplayValueMap = {
PENDING: \\"Pending\\",
POSTED: \\"Posted\\",
IN_REVIEW: \\"In review\\",
};
- return enumDisplayValueMap[record];
+ return enumDisplayValueMap[r];
},
};
const validations = {
@@ -9782,13 +11002,19 @@ export default function TagUpdateForm(props) {
isReadOnly={false}
value={currentPostsDisplayValue}
options={postRecords
- .filter((post) => !PostsIdSet.has(post.id))
+ .filter((r) => !PostsIdSet.has(getIDValue.Posts?.(r)))
.map((r) => ({
- id: r.id,
+ id: getIDValue.Posts?.(r),
label: getDisplayValue.Posts?.(r),
}))}
onSelect={({ id, label }) => {
- setCurrentPostsValue(postRecords.find((r) => r.id === id));
+ setCurrentPostsValue(
+ postRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
+ );
setCurrentPostsDisplayValue(label);
}}
onClear={() => {
@@ -14068,9 +15294,6 @@ export default function MyMemberForm(props) {
};
const [name, setName] = React.useState(initialValues.name);
const [Team, setTeam] = React.useState(initialValues.Team);
- const TeamIdSet = new Set(
- Array.isArray(Team) ? Team.map((team) => team.id) : Team?.id
- );
const [errors, setErrors] = React.useState({});
const resetStateValues = () => {
setName(initialValues.name);
@@ -14083,12 +15306,20 @@ export default function MyMemberForm(props) {
React.useState(\\"\\");
const [currentTeamValue, setCurrentTeamValue] = React.useState(undefined);
const TeamRef = React.createRef();
+ const getIDValue = {
+ Team: (r) => JSON.stringify({ id: r?.id }),
+ };
+ const TeamIdSet = new Set(
+ Array.isArray(Team)
+ ? Team.map((r) => getIDValue.Team?.(r))
+ : getIDValue.Team?.(Team)
+ );
const teamRecords = useDataStoreBinding({
type: \\"collection\\",
model: Team0,
}).items;
const getDisplayValue = {
- Team: (record) => record?.name,
+ Team: (r) => r?.name,
};
const validations = {
name: [],
@@ -14258,13 +15489,19 @@ export default function MyMemberForm(props) {
isReadOnly={false}
value={currentTeamDisplayValue}
options={teamRecords
- .filter((team) => !TeamIdSet.has(team.id))
+ .filter((r) => !TeamIdSet.has(getIDValue.Team?.(r)))
.map((r) => ({
- id: r.id,
+ id: getIDValue.Team?.(r),
label: getDisplayValue.Team?.(r),
}))}
onSelect={({ id, label }) => {
- setCurrentTeamValue(teamRecords.find((r) => r.id === id));
+ setCurrentTeamValue(
+ teamRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(
+ ([key, value]) => r[key] === value
+ )
+ )
+ );
setCurrentTeamDisplayValue(label);
}}
onClear={() => {
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 998de0329..6b4a6c5ed 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
@@ -97,7 +97,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: (r) => r?.name');
expect(componentText).toMatchSnapshot();
expect(declaration).toMatchSnapshot();
@@ -115,7 +115,7 @@ describe('amplify form renderer tests', () => {
expect(componentText).toContain('const postRecords = useDataStoreBinding({');
// check custom display value is set
- expect(componentText).toContain('Posts: (record) => record?.title');
+ expect(componentText).toContain('Posts: (r) => r?.title');
expect(componentText).toMatchSnapshot();
expect(declaration).toMatchSnapshot();
@@ -136,7 +136,7 @@ describe('amplify form renderer tests', () => {
expect(componentText).toContain('await record.Posts.toArray()');
// check custom display value is set
- expect(componentText).toContain('Posts: (record) => record?.title');
+ expect(componentText).toContain('Posts: (r) => r?.title');
// check linked data useState is generate
expect(componentText).toContain('const [linkedPosts, setLinkedPosts] = React.useState([]);');
@@ -154,8 +154,8 @@ describe('amplify form renderer tests', () => {
'datastore/tag-post',
);
// get displayValue function
- expect(componentText).toContain('statuses: (record) => {');
- expect(componentText).toContain('return enumDisplayValueMap[record];');
+ expect(componentText).toContain('statuses: (r) => {');
+ expect(componentText).toContain('return enumDisplayValueMap[r];');
// ArrayField returns the item on a badge click
expect(componentText).toContain('setFieldValue(items[index]);');
// set the badgeText param
@@ -178,7 +178,7 @@ describe('amplify form renderer tests', () => {
expect(componentText).toContain('const studentRecords = useDataStoreBinding({');
// check custom display value is set
- expect(componentText).toContain('Students: (record) => record?.name');
+ expect(componentText).toContain('Students: (r) => r?.name');
expect(componentText).toMatchSnapshot();
expect(declaration).toMatchSnapshot();
@@ -199,7 +199,7 @@ describe('amplify form renderer tests', () => {
expect(componentText).toContain('const linkedStudents = record ? await record.Students.toArray() : [];');
// check custom display value is set
- expect(componentText).toContain('Students: (record) => record?.name');
+ expect(componentText).toContain('Students: (r) => r?.name');
// check linked data useState is generate
expect(componentText).toContain('const [linkedStudents, setLinkedStudents] = React.useState([]);');
@@ -385,8 +385,8 @@ describe('amplify form renderer tests', () => {
// hasOne
expect(componentText).toContain('specialTeacherId: specialTeacherIdProp');
expect(componentText).toContain('await DataStore.query(CPKTeacher, specialTeacherIdProp)');
- expect(componentText).toContain('Student: (record) => record?.specialStudentId');
- expect(componentText).toContain('id: r.specialStudentId');
+ expect(componentText).toContain('Student: (r) => r?.specialStudentId');
+ expect(componentText).toContain('JSON.stringify({ specialStudentId: r?.specialStudentId })');
// manyToMany
expect(componentText).toContain('const count = cPKClassesMap.get(r.specialClassId)');
@@ -404,6 +404,16 @@ describe('amplify form renderer tests', () => {
expect(componentText).toMatchSnapshot();
expect(declaration).toMatchSnapshot();
});
+
+ it('should render an update form for model with composite keys', () => {
+ const { componentText, declaration } = generateWithAmplifyFormRenderer(
+ 'forms/composite-dog-datastore-update',
+ 'datastore/composite-relationships',
+ );
+
+ expect(componentText).toMatchSnapshot();
+ expect(declaration).toMatchSnapshot();
+ });
});
});
});
diff --git a/packages/codegen-ui-react/lib/forms/form-renderer-helper/all-props.ts b/packages/codegen-ui-react/lib/forms/form-renderer-helper/all-props.ts
index bf0cca36e..e9535e167 100644
--- a/packages/codegen-ui-react/lib/forms/form-renderer-helper/all-props.ts
+++ b/packages/codegen-ui-react/lib/forms/form-renderer-helper/all-props.ts
@@ -25,7 +25,7 @@ import {
} from './event-handler-props';
import { getArrayChildRefName, resetValuesName } from './form-state';
import { shouldWrapInArrayField } from './render-checkers';
-import { getAutocompleteOptionsProp } from './display-value';
+import { getAutocompleteOptionsProp } from './model-values';
import { buildCtaLayoutProperties } from '../../react-component-render-helper';
export const addFormAttributes = (component: StudioComponent | StudioComponentChild, formMetadata: FormMetadata) => {
diff --git a/packages/codegen-ui-react/lib/forms/form-renderer-helper/cta-props.ts b/packages/codegen-ui-react/lib/forms/form-renderer-helper/cta-props.ts
index 60309e96d..a2b1e3304 100644
--- a/packages/codegen-ui-react/lib/forms/form-renderer-helper/cta-props.ts
+++ b/packages/codegen-ui-react/lib/forms/form-renderer-helper/cta-props.ts
@@ -24,7 +24,7 @@ import {
ExpressionStatement,
} from 'typescript';
import { lowerCaseFirst } from '../../helpers';
-import { getDisplayValueObjectName } from './display-value';
+import { getDisplayValueObjectName } from './model-values';
import { getSetNameIdentifier } from './form-state';
import {
buildHasManyRelationshipDataStoreStatements,
diff --git a/packages/codegen-ui-react/lib/forms/form-renderer-helper/display-value.ts b/packages/codegen-ui-react/lib/forms/form-renderer-helper/display-value.ts
deleted file mode 100644
index 0366f99ef..000000000
--- a/packages/codegen-ui-react/lib/forms/form-renderer-helper/display-value.ts
+++ /dev/null
@@ -1,370 +0,0 @@
-/*
- Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
-
- Licensed under the Apache License, Version 2.0 (the "License").
- You may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
-import {
- FieldConfigMetadata,
- InvalidInputError,
- isValidVariableName,
- StudioFormValueMappings,
-} from '@aws-amplify/codegen-ui';
-import { StudioFormInputFieldProperty } from '@aws-amplify/codegen-ui/lib/types/form/input-config';
-import { isEnumFieldType } from '@aws-amplify/datastore';
-import {
- Expression,
- factory,
- JsxAttribute,
- PropertyAssignment,
- SyntaxKind,
- NodeFlags,
- CallExpression,
- VariableStatement,
-} from 'typescript';
-import { lowerCaseFirst } from '../../helpers';
-import {
- buildBindingExpression,
- buildConcatExpression,
- isBoundProperty,
- isConcatenatedProperty,
- isFixedPropertyWithValue,
-} from '../../react-component-render-helper';
-import { getRecordsName } from './form-state';
-import { getElementAccessExpression } from './invalid-variable-helpers';
-import { isModelDataType } from './render-checkers';
-
-export const getDisplayValueObjectName = 'getDisplayValue';
-
-/**
- authorRecords.map((r) => ({
- id: r.id,
- label: r.id,
- }))
- */
-
-/**
- examples:
- for model -
- authorRecords.map((r) => ({
- id: r.id,
- label: getDisplayValue['primaryAuthor']?.(r),
- }))
-
- for scalar -
- authorRecords.map((r) => ({
- id: r.id,
- label: r.id,
- }))
- */
-function getModelTypeSuggestions({
- modelName,
- fieldName,
- key,
- isModelType,
-}: {
- modelName: string;
- fieldName: string;
- key: string;
- isModelType: boolean;
-}): CallExpression {
- const recordString = 'r';
-
- const labelExpression = isModelType
- ? factory.createCallChain(
- getElementAccessExpression('getDisplayValue', fieldName),
- factory.createToken(SyntaxKind.QuestionDotToken),
- undefined,
- [factory.createIdentifier(recordString)],
- )
- : getElementAccessExpression(recordString, key);
-
- const filterOptionsExpression = isModelType
- ? factory.createCallExpression(
- factory.createPropertyAccessExpression(
- factory.createIdentifier(getRecordsName(modelName)),
- factory.createIdentifier('filter'),
- ),
- undefined,
- [
- factory.createArrowFunction(
- undefined,
- undefined,
- [
- factory.createParameterDeclaration(
- undefined,
- undefined,
- undefined,
- factory.createIdentifier(lowerCaseFirst(modelName)),
- undefined,
- undefined,
- undefined,
- ),
- ],
- undefined,
- factory.createToken(SyntaxKind.EqualsGreaterThanToken),
- factory.createPrefixUnaryExpression(
- SyntaxKind.ExclamationToken,
- factory.createCallExpression(
- factory.createPropertyAccessExpression(
- factory.createIdentifier(`${fieldName}IdSet`),
- factory.createIdentifier('has'),
- ),
- undefined,
- [
- factory.createPropertyAccessExpression(
- factory.createIdentifier(lowerCaseFirst(modelName)),
- factory.createIdentifier('id'),
- ),
- ],
- ),
- ),
- ),
- ],
- )
- : factory.createIdentifier(getRecordsName(modelName));
-
- return factory.createCallExpression(
- factory.createPropertyAccessExpression(filterOptionsExpression, factory.createIdentifier('map')),
- undefined,
- [
- factory.createArrowFunction(
- undefined,
- undefined,
- [
- factory.createParameterDeclaration(
- undefined,
- undefined,
- undefined,
- factory.createIdentifier(recordString),
- undefined,
- undefined,
- undefined,
- ),
- ],
- undefined,
- factory.createToken(SyntaxKind.EqualsGreaterThanToken),
- factory.createParenthesizedExpression(
- factory.createObjectLiteralExpression(
- [
- factory.createPropertyAssignment(
- factory.createIdentifier('id'),
- getElementAccessExpression(recordString, key),
- ),
- factory.createPropertyAssignment(factory.createIdentifier('label'), labelExpression),
- ],
- true,
- ),
- ),
- ),
- ],
- );
-}
-
-export function extractModelAndKey(valueMappings?: StudioFormValueMappings): { model?: string; key?: string } {
- let model: undefined | string;
- let key: undefined | string;
- const bindingProperty = valueMappings?.bindingProperties && Object.values(valueMappings.bindingProperties)[0];
- if (bindingProperty && bindingProperty.type === 'Data') {
- model = bindingProperty.bindingProperties.model;
- const { value } = valueMappings.values[0];
- if (isBoundProperty(value) && value.bindingProperties.field) {
- key = value.bindingProperties.field;
- }
- }
-
- return { model, key };
-}
-
-/**
- example:
- options={authorRecords.map(r) => ({
- id: r.id,
- label: getDisplayValue['primaryAuthor']?.(r) ?? r.id,
- }))}
- */
-export function getAutocompleteOptionsProp({
- fieldName,
- fieldConfig,
-}: {
- fieldName: string;
- fieldConfig: FieldConfigMetadata;
-}): JsxAttribute {
- let options: Expression | undefined;
-
- const { valueMappings } = fieldConfig;
- const { model, key } = extractModelAndKey(valueMappings);
-
- if (model && key) {
- options = getModelTypeSuggestions({
- modelName: model,
- fieldName,
- key,
- isModelType: isModelDataType(fieldConfig),
- });
- }
-
- if (!options) {
- throw new InvalidInputError(`Invalid value mappings on ${fieldName}`);
- }
-
- return factory.createJsxAttribute(
- factory.createIdentifier('options'),
- factory.createJsxExpression(undefined, options),
- );
-}
-
-// impure helper
-/* eslint-disable no-param-reassign */
-function replaceProperty(prop: StudioFormInputFieldProperty, toReplace: string, replaceWith: string): void {
- if (isBoundProperty(prop) && prop.bindingProperties.property === toReplace) {
- prop.bindingProperties.property = replaceWith;
- }
- if (isConcatenatedProperty(prop)) {
- prop.concat.forEach((subProp) => replaceProperty(subProp as StudioFormInputFieldProperty, toReplace, replaceWith));
- }
-}
-/* eslint-enable no-param-reassign */
-
-export function getDisplayValueObject(displayValueFunctions: PropertyAssignment[]) {
- return factory.createVariableStatement(
- undefined,
- factory.createVariableDeclarationList(
- [
- factory.createVariableDeclaration(
- factory.createIdentifier(getDisplayValueObjectName),
- undefined,
- undefined,
- factory.createObjectLiteralExpression(displayValueFunctions, true),
- ),
- ],
- NodeFlags.Const,
- ),
- );
-}
-
-// example - primaryAuthor: (record) => record?.name,
-export function buildDisplayValueFunction(fieldName: string, fieldConfig: FieldConfigMetadata): PropertyAssignment {
- const recordString = 'record';
- const propertyName = isValidVariableName(fieldName)
- ? factory.createIdentifier(fieldName)
- : factory.createStringLiteral(fieldName);
- let additionalStatements: VariableStatement[] = [];
- const { key: primaryKey } = extractModelAndKey(fieldConfig.valueMappings);
-
- let renderedDisplayValue: Expression = factory.createPropertyAccessChain(
- factory.createIdentifier(recordString),
- factory.createToken(SyntaxKind.QuestionDotToken),
- // if this expression is used, primaryKey should exist
- factory.createIdentifier(primaryKey || ''),
- );
-
- if (isModelDataType(fieldConfig) && fieldConfig.valueMappings) {
- const valueConfig = fieldConfig.valueMappings.values[0];
- if (valueConfig) {
- const displayValueProperty = valueConfig.displayValue || valueConfig.value;
- const modelName = fieldConfig.dataType.model;
- replaceProperty(displayValueProperty, modelName, recordString);
- if (isConcatenatedProperty(displayValueProperty)) {
- renderedDisplayValue = buildConcatExpression(displayValueProperty);
- } else if (isBoundProperty(displayValueProperty)) {
- renderedDisplayValue = buildBindingExpression(displayValueProperty);
- }
- }
- }
-
- if (isEnumFieldType(fieldConfig.dataType) && fieldConfig.valueMappings && fieldConfig.isArray) {
- const displayValueMapName = `enumDisplayValueMap`;
- additionalStatements = [
- factory.createVariableStatement(
- undefined,
- factory.createVariableDeclarationList(
- [
- factory.createVariableDeclaration(
- factory.createIdentifier(displayValueMapName),
- undefined,
- undefined,
- factory.createObjectLiteralExpression(
- fieldConfig.valueMappings.values.map((v) => {
- let value = '';
- let displayValue = '';
- if (isFixedPropertyWithValue(v.value)) {
- value = v.value.value.toString();
- }
- if (v.displayValue && isFixedPropertyWithValue(v.displayValue)) {
- displayValue = v.displayValue.value.toString();
- }
- if (value === '') {
- throw Error('Enum cannot have an empty value');
- }
- return factory.createPropertyAssignment(
- factory.createStringLiteral(value),
- factory.createStringLiteral(displayValue ?? value),
- );
- }),
-
- true,
- ),
- ),
- ],
- NodeFlags.Const,
- ),
- ),
- ];
- renderedDisplayValue = factory.createElementAccessExpression(
- factory.createIdentifier(displayValueMapName),
- factory.createIdentifier(recordString),
- );
- }
-
- return factory.createPropertyAssignment(
- propertyName,
- factory.createArrowFunction(
- undefined,
- undefined,
- [
- factory.createParameterDeclaration(
- undefined,
- undefined,
- undefined,
- factory.createIdentifier(recordString),
- undefined,
- undefined,
- undefined,
- ),
- ],
- undefined,
- factory.createToken(SyntaxKind.EqualsGreaterThanToken),
- additionalStatements.length
- ? factory.createBlock([...additionalStatements, factory.createReturnStatement(renderedDisplayValue)], false)
- : renderedDisplayValue,
- ),
- );
-}
-
-export function getModelsToImport(fieldConfig: FieldConfigMetadata): string[] {
- const modelDependencies: string[] = [];
- if (fieldConfig.valueMappings && fieldConfig.valueMappings.bindingProperties) {
- Object.values(fieldConfig.valueMappings.bindingProperties).forEach((prop) => {
- if (prop.type === 'Data' && prop.bindingProperties.model) {
- modelDependencies.push(prop.bindingProperties.model);
- }
- });
- }
-
- // Import join table model
- if (fieldConfig.relationship?.type === 'HAS_MANY' && fieldConfig.relationship.relatedJoinTableName) {
- modelDependencies.push(fieldConfig.relationship.relatedJoinTableName);
- }
-
- return modelDependencies;
-}
diff --git a/packages/codegen-ui-react/lib/forms/form-renderer-helper/event-handler-props.ts b/packages/codegen-ui-react/lib/forms/form-renderer-helper/event-handler-props.ts
index 3d4109a25..41812471e 100644
--- a/packages/codegen-ui-react/lib/forms/form-renderer-helper/event-handler-props.ts
+++ b/packages/codegen-ui-react/lib/forms/form-renderer-helper/event-handler-props.ts
@@ -54,7 +54,7 @@ import {
import { getOnChangeValidationBlock } from './validation';
import { buildModelFieldObject } from './model-fields';
import { isModelDataType, shouldWrapInArrayField } from './render-checkers';
-import { extractModelAndKey } from './display-value';
+import { extractModelAndKeys, getMatchEveryModelFieldCallExpression } from './model-values';
export const buildMutationBindings = (form: StudioForm, primaryKey?: string) => {
const {
@@ -318,17 +318,21 @@ export const buildOnChangeStatement = (
);
};
-// onSelect={({ id }) => {
-// setCurrentPrimaryAuthorValue(
-// id
-// );
-// setCurrentPrimaryAuthorDisplayValue(id);
-// }}
-
/**
- example:
+examples:
+
+ scalar:
+ onSelect={({ id }) => {
+ setCurrentPrimaryAuthorValue(id);
+ setCurrentPrimaryAuthorDisplayValue(id);
+ }}
+
+ model:
onSelect={({ id, label }) => {
- setCurrentPrimaryAuthorValue(authorRecords.find((r) => r.id === id));
+ setCurrentPrimaryAuthorValue(
+ primaryAuthorRecords.find((r) => Object.entries(JSON.parse(id)).every(([key, value]) =>
+ r[key] === value)));
+ );
setCurrentPrimaryAuthorDisplayValue(label);
}}
*/
@@ -339,14 +343,8 @@ export function buildOnSelect({
sanitizedFieldName: string;
fieldConfig: FieldConfigMetadata;
}): JsxAttribute {
- const { model, key } = extractModelAndKey(fieldConfig.valueMappings);
- if (!model || !key) {
- throw new InvalidInputError(`Invalid value mappings`);
- }
-
const labelString = 'label';
const idString = 'id';
- const recordString = 'r';
const props: BindingElement[] = [
factory.createBindingElement(undefined, undefined, factory.createIdentifier(idString), undefined),
@@ -356,44 +354,19 @@ export function buildOnSelect({
let nextCurrentDisplayValue: Expression = factory.createIdentifier(idString);
if (isModelDataType(fieldConfig)) {
+ const { model, keys } = extractModelAndKeys(fieldConfig.valueMappings);
+ if (!model || !keys || !keys.length) {
+ throw new InvalidInputError(`Invalid value mappings`);
+ }
+
props.push(factory.createBindingElement(undefined, undefined, factory.createIdentifier(labelString), undefined));
nextCurrentDisplayValue = factory.createIdentifier(labelString);
- nextCurrentValue = factory.createCallExpression(
- factory.createPropertyAccessExpression(
- factory.createIdentifier(getRecordsName(model)),
- factory.createIdentifier('find'),
- ),
- undefined,
- [
- factory.createArrowFunction(
- undefined,
- undefined,
- [
- factory.createParameterDeclaration(
- undefined,
- undefined,
- undefined,
- factory.createIdentifier(recordString),
- undefined,
- undefined,
- undefined,
- ),
- ],
- undefined,
- factory.createToken(SyntaxKind.EqualsGreaterThanToken),
- factory.createBinaryExpression(
- factory.createPropertyAccessExpression(
- factory.createIdentifier(recordString),
- factory.createIdentifier(key),
- ),
- factory.createToken(SyntaxKind.EqualsEqualsEqualsToken),
- factory.createIdentifier(idString),
- ),
- ),
- ],
- );
+ nextCurrentValue = getMatchEveryModelFieldCallExpression({
+ recordsArrayName: getRecordsName(model),
+ JSONName: idString,
+ });
}
const setStateExpressions: ExpressionStatement[] = [
diff --git a/packages/codegen-ui-react/lib/forms/form-renderer-helper/form-state.ts b/packages/codegen-ui-react/lib/forms/form-renderer-helper/form-state.ts
index 4eb219f42..91dde618e 100644
--- a/packages/codegen-ui-react/lib/forms/form-renderer-helper/form-state.ts
+++ b/packages/codegen-ui-react/lib/forms/form-renderer-helper/form-state.ts
@@ -599,80 +599,3 @@ export const buildResetValuesOnRecordUpdate = (recordName: string, linkedDataNam
),
);
};
-
-// e.g. const PostsIdSet = new Set(Posts.map(post => post.id));
-export const buildSelectedRecordsIdSet = (fieldConfigs: Record): Statement[] => {
- const statements: Statement[] = [];
- Object.entries(fieldConfigs).forEach((fieldConfig) => {
- const [name, fieldConfigMetaData] = fieldConfig;
- const fieldName = fieldConfigMetaData.sanitizedFieldName || name;
- if (fieldConfigMetaData.relationship) {
- const { relatedModelName } = fieldConfigMetaData.relationship;
- statements.push(
- factory.createVariableStatement(
- undefined,
- factory.createVariableDeclarationList(
- [
- factory.createVariableDeclaration(
- factory.createIdentifier(`${fieldName}IdSet`),
- undefined,
- undefined,
- factory.createNewExpression(factory.createIdentifier('Set'), undefined, [
- factory.createConditionalExpression(
- factory.createCallExpression(
- factory.createPropertyAccessExpression(
- factory.createIdentifier('Array'),
- factory.createIdentifier('isArray'),
- ),
- undefined,
- [factory.createIdentifier(fieldName)],
- ),
- factory.createToken(SyntaxKind.QuestionToken),
- factory.createCallExpression(
- factory.createPropertyAccessExpression(
- factory.createIdentifier(fieldName),
- factory.createIdentifier('map'),
- ),
- undefined,
- [
- factory.createArrowFunction(
- undefined,
- undefined,
- [
- factory.createParameterDeclaration(
- undefined,
- undefined,
- undefined,
- factory.createIdentifier(lowerCaseFirst(relatedModelName)),
- undefined,
- undefined,
- undefined,
- ),
- ],
- undefined,
- factory.createToken(SyntaxKind.EqualsGreaterThanToken),
- factory.createPropertyAccessExpression(
- factory.createIdentifier(lowerCaseFirst(relatedModelName)),
- factory.createIdentifier('id'),
- ),
- ),
- ],
- ),
- factory.createToken(SyntaxKind.ColonToken),
- factory.createPropertyAccessChain(
- factory.createIdentifier(fieldName),
- factory.createToken(SyntaxKind.QuestionDotToken),
- factory.createIdentifier('id'),
- ),
- ),
- ]),
- ),
- ],
- NodeFlags.Const,
- ),
- ),
- );
- }
- });
- return statements;
-};
diff --git a/packages/codegen-ui-react/lib/forms/form-renderer-helper/index.ts b/packages/codegen-ui-react/lib/forms/form-renderer-helper/index.ts
index f4021da8b..37e7f18e7 100644
--- a/packages/codegen-ui-react/lib/forms/form-renderer-helper/index.ts
+++ b/packages/codegen-ui-react/lib/forms/form-renderer-helper/index.ts
@@ -21,13 +21,7 @@ export { buildMutationBindings, buildOverrideOnChangeStatement } from './event-h
export { buildOverrideTypesBindings } from './type-helper';
-export {
- buildResetValuesOnRecordUpdate,
- buildSetStateFunction,
- buildSelectedRecordsIdSet,
- getLinkedDataName,
- getPropName,
-} from './form-state';
+export { buildResetValuesOnRecordUpdate, buildSetStateFunction, getLinkedDataName, getPropName } from './form-state';
export { buildValidations, runValidationTasksFunction } from './validation';
diff --git a/packages/codegen-ui-react/lib/forms/form-renderer-helper/invalid-variable-helpers.ts b/packages/codegen-ui-react/lib/forms/form-renderer-helper/invalid-variable-helpers.ts
index 1fc0560bd..6bbeade28 100644
--- a/packages/codegen-ui-react/lib/forms/form-renderer-helper/invalid-variable-helpers.ts
+++ b/packages/codegen-ui-react/lib/forms/form-renderer-helper/invalid-variable-helpers.ts
@@ -28,3 +28,7 @@ export function getElementAccessExpression(elementName: string, propertyName: st
factory.createStringLiteral(propertyName),
);
}
+
+export function getValidProperty(key: string) {
+ return isValidVariableName(key) ? factory.createIdentifier(key) : factory.createStringLiteral(key);
+}
diff --git a/packages/codegen-ui-react/lib/forms/form-renderer-helper/map-from-fieldConfigs.ts b/packages/codegen-ui-react/lib/forms/form-renderer-helper/map-from-fieldConfigs.ts
index 1db82baa6..b051a6fd9 100644
--- a/packages/codegen-ui-react/lib/forms/form-renderer-helper/map-from-fieldConfigs.ts
+++ b/packages/codegen-ui-react/lib/forms/form-renderer-helper/map-from-fieldConfigs.ts
@@ -15,8 +15,18 @@
*/
import { FieldConfigMetadata } from '@aws-amplify/codegen-ui';
import { PropertyAssignment } from 'typescript';
-import { buildDisplayValueFunction, getDisplayValueObject, getModelsToImport } from './display-value';
-import { shouldImplementDisplayValueFunction, shouldWrapInArrayField } from './render-checkers';
+import {
+ buildDisplayValueFunction,
+ getDisplayValueObject,
+ getModelsToImport,
+ buildIDValueFunction,
+ getIDValueObject,
+} from './model-values';
+import {
+ shouldImplementDisplayValueFunction,
+ shouldWrapInArrayField,
+ shouldImplementIDValueFunction,
+} from './render-checkers';
import { buildValidationForField, buildValidations } from './validation';
/**
@@ -28,6 +38,7 @@ export function mapFromFieldConfigs(fieldConfigs: Record {
+ return factory.createCallChain(
+ getElementAccessExpression(getIDValueObjectName, fieldName),
+ factory.createToken(SyntaxKind.QuestionDotToken),
+ undefined,
+ [factory.createIdentifier(recordString)],
+ );
+};
+
+const getDisplayValueCallChain = ({ fieldName, recordString }: { fieldName: string; recordString: string }) => {
+ return factory.createCallChain(
+ getElementAccessExpression(getDisplayValueObjectName, fieldName),
+ factory.createToken(SyntaxKind.QuestionDotToken),
+ undefined,
+ [factory.createIdentifier(recordString)],
+ );
+};
+
+/**
+ examples:
+ for model -
+ authorRecords
+ .map((r) => ({
+ id: r?.id,
+ label: r?.id,
+ }))
+ */
+function getSuggestionsForRelationshipScalar({ modelName, key }: { modelName: string; key: string }): CallExpression {
+ const recordString = 'r';
+
+ return factory.createCallExpression(
+ factory.createPropertyAccessExpression(
+ factory.createIdentifier(getRecordsName(modelName)),
+ factory.createIdentifier('map'),
+ ),
+ undefined,
+ [
+ factory.createArrowFunction(
+ undefined,
+ undefined,
+ [
+ factory.createParameterDeclaration(
+ undefined,
+ undefined,
+ undefined,
+ factory.createIdentifier(recordString),
+ undefined,
+ undefined,
+ undefined,
+ ),
+ ],
+ undefined,
+ factory.createToken(SyntaxKind.EqualsGreaterThanToken),
+ factory.createParenthesizedExpression(
+ factory.createObjectLiteralExpression(
+ [
+ factory.createPropertyAssignment(factory.createIdentifier('id'), buildAccessChain([recordString, key])),
+ factory.createPropertyAssignment(
+ factory.createIdentifier('label'),
+ buildAccessChain([recordString, key]),
+ ),
+ ],
+ true,
+ ),
+ ),
+ ),
+ ],
+ );
+}
+
+/**
+ example:
+ for model -
+ authorRecords
+ .filter(r => !primaryAuthorSet.has(getIDValue.primaryAuthor?.(r))
+ .map((r) => ({
+ id: getIDValue['primaryAuthor]?.(r),
+ label: getDisplayValue['primaryAuthor']?.(r),
+ }))
+ */
+function getModelTypeSuggestions({ modelName, fieldName }: { modelName: string; fieldName: string }): CallExpression {
+ const recordString = 'r';
+
+ const labelExpression = getDisplayValueCallChain({ fieldName, recordString });
+
+ const filterOptionsExpression = factory.createCallExpression(
+ factory.createPropertyAccessExpression(
+ factory.createIdentifier(getRecordsName(modelName)),
+ factory.createIdentifier('filter'),
+ ),
+ undefined,
+ [
+ factory.createArrowFunction(
+ undefined,
+ undefined,
+ [
+ factory.createParameterDeclaration(
+ undefined,
+ undefined,
+ undefined,
+ factory.createIdentifier(recordString),
+ undefined,
+ undefined,
+ undefined,
+ ),
+ ],
+ undefined,
+ factory.createToken(SyntaxKind.EqualsGreaterThanToken),
+ factory.createPrefixUnaryExpression(
+ SyntaxKind.ExclamationToken,
+ factory.createCallExpression(
+ factory.createPropertyAccessExpression(
+ factory.createIdentifier(`${fieldName}IdSet`),
+ factory.createIdentifier('has'),
+ ),
+ undefined,
+ [getIDValueCallChain({ fieldName, recordString })],
+ ),
+ ),
+ ),
+ ],
+ );
+
+ return factory.createCallExpression(
+ factory.createPropertyAccessExpression(filterOptionsExpression, factory.createIdentifier('map')),
+ undefined,
+ [
+ factory.createArrowFunction(
+ undefined,
+ undefined,
+ [
+ factory.createParameterDeclaration(
+ undefined,
+ undefined,
+ undefined,
+ factory.createIdentifier(recordString),
+ undefined,
+ undefined,
+ undefined,
+ ),
+ ],
+ undefined,
+ factory.createToken(SyntaxKind.EqualsGreaterThanToken),
+ factory.createParenthesizedExpression(
+ factory.createObjectLiteralExpression(
+ [
+ factory.createPropertyAssignment(
+ factory.createIdentifier('id'),
+ getIDValueCallChain({ fieldName, recordString }),
+ ),
+ factory.createPropertyAssignment(factory.createIdentifier('label'), labelExpression),
+ ],
+ true,
+ ),
+ ),
+ ),
+ ],
+ );
+}
+
+export function extractModelAndKeys(valueMappings?: StudioFormValueMappings): { model?: string; keys?: string[] } {
+ let model: undefined | string;
+ let keys: undefined | string[];
+ const bindingProperty = valueMappings?.bindingProperties && Object.values(valueMappings.bindingProperties)[0];
+ if (bindingProperty && bindingProperty.type === 'Data') {
+ model = bindingProperty.bindingProperties.model;
+ const { values } = valueMappings;
+ values.forEach((v) => {
+ const { value } = v;
+ if (isBoundProperty(value) && value.bindingProperties.field) {
+ if (!keys) {
+ keys = [];
+ }
+ keys.push(value.bindingProperties.field);
+ }
+ });
+ }
+ console.log('KEYSSS', keys);
+ return { model, keys };
+}
+
+/**
+ example:
+ options={authorRecords.map(r) => ({
+ id: getIDValue['primaryAuthor]?.(r),
+ label: getDisplayValue['primaryAuthor']?.(r),
+ }))}
+ */
+export function getAutocompleteOptionsProp({
+ fieldName,
+ fieldConfig,
+}: {
+ fieldName: string;
+ fieldConfig: FieldConfigMetadata;
+}): JsxAttribute {
+ let options: Expression | undefined;
+
+ const { valueMappings } = fieldConfig;
+
+ const { model, keys } = extractModelAndKeys(valueMappings);
+ if (model) {
+ if (isModelDataType(fieldConfig)) {
+ options = getModelTypeSuggestions({
+ modelName: model,
+ fieldName,
+ });
+ } else if (keys) {
+ options = getSuggestionsForRelationshipScalar({ modelName: model, key: keys[0] });
+ }
+ }
+
+ if (!options) {
+ throw new InvalidInputError(`Invalid value mappings on ${fieldName}`);
+ }
+
+ return factory.createJsxAttribute(
+ factory.createIdentifier('options'),
+ factory.createJsxExpression(undefined, options),
+ );
+}
+
+// impure helper
+/* eslint-disable no-param-reassign */
+function replaceProperty(prop: StudioFormInputFieldProperty, toReplace: string, replaceWith: string): void {
+ if (isBoundProperty(prop) && prop.bindingProperties.property === toReplace) {
+ prop.bindingProperties.property = replaceWith;
+ }
+ if (isConcatenatedProperty(prop)) {
+ prop.concat.forEach((subProp) => replaceProperty(subProp as StudioFormInputFieldProperty, toReplace, replaceWith));
+ }
+}
+/* eslint-enable no-param-reassign */
+
+export function getDisplayValueObject(displayValueFunctions: PropertyAssignment[]) {
+ return factory.createVariableStatement(
+ undefined,
+ factory.createVariableDeclarationList(
+ [
+ factory.createVariableDeclaration(
+ factory.createIdentifier(getDisplayValueObjectName),
+ undefined,
+ undefined,
+ factory.createObjectLiteralExpression(displayValueFunctions, true),
+ ),
+ ],
+ NodeFlags.Const,
+ ),
+ );
+}
+
+export function getIDValueObject(idValueFunctions: PropertyAssignment[]) {
+ return factory.createVariableStatement(
+ undefined,
+ factory.createVariableDeclarationList(
+ [
+ factory.createVariableDeclaration(
+ factory.createIdentifier(getIDValueObjectName),
+ undefined,
+ undefined,
+ factory.createObjectLiteralExpression(idValueFunctions, true),
+ ),
+ ],
+ NodeFlags.Const,
+ ),
+ );
+}
+
+function getDefaultDisplayValue({
+ fieldConfig,
+ modelName,
+}: {
+ fieldConfig: FieldConfigMetadata;
+ modelName: string;
+}): BoundStudioComponentProperty | ConcatenatedStudioComponentProperty {
+ const { keys } = extractModelAndKeys(fieldConfig.valueMappings);
+ if (!keys || !keys.length) {
+ throw new InternalError(`Unable to find primary key(s) for ${modelName}`);
+ }
+ if (keys.length === 1) {
+ return { bindingProperties: { property: modelName, field: keys[0] } };
+ }
+
+ const concatArray: StudioComponentProperty[] = [];
+
+ keys.forEach((key, index) => {
+ concatArray.push({
+ bindingProperties: { property: modelName, field: key },
+ });
+ if (index !== keys.length - 1) {
+ concatArray.push({
+ value: ' - ',
+ });
+ }
+ });
+
+ return { concat: concatArray };
+}
+
+// CompositeBowl: (r) => JSON.stringify({ shape: r?.shape, size: r?.size })
+export function buildIDValueFunction(fieldName: string, fieldConfig: FieldConfigMetadata): PropertyAssignment {
+ const recordString = 'r';
+
+ const { keys } = extractModelAndKeys(fieldConfig.valueMappings);
+ if (!keys || !keys.length) {
+ throw new InternalError(`Unable to render IDValue function for ${fieldName}`);
+ }
+
+ const idObjectProperties = keys.map((key) =>
+ factory.createPropertyAssignment(getValidProperty(key), buildAccessChain([recordString, key])),
+ );
+
+ return factory.createPropertyAssignment(
+ getValidProperty(fieldName),
+ factory.createArrowFunction(
+ undefined,
+ undefined,
+ [
+ factory.createParameterDeclaration(
+ undefined,
+ undefined,
+ undefined,
+ factory.createIdentifier(recordString),
+ undefined,
+ undefined,
+ undefined,
+ ),
+ ],
+ undefined,
+ factory.createToken(SyntaxKind.EqualsGreaterThanToken),
+ factory.createCallExpression(
+ factory.createPropertyAccessExpression(factory.createIdentifier('JSON'), factory.createIdentifier('stringify')),
+ undefined,
+ [factory.createObjectLiteralExpression(idObjectProperties, false)],
+ ),
+ ),
+ );
+}
+
+// examples:
+// primaryAuthor: (r) => r?.name,
+// compositePrimaryAuthor: (r) => r?.name + ' - ' + r?.birthYear
+export function buildDisplayValueFunction(fieldName: string, fieldConfig: FieldConfigMetadata): PropertyAssignment {
+ const recordString = 'r';
+
+ let additionalStatements: VariableStatement[] = [];
+
+ let renderedDisplayValue: Expression | undefined;
+
+ if (isModelDataType(fieldConfig) && fieldConfig.valueMappings) {
+ const valueConfig = fieldConfig.valueMappings.values[0];
+ if (valueConfig) {
+ const modelName = fieldConfig.dataType.model;
+ const displayValueProperty = valueConfig.displayValue || getDefaultDisplayValue({ fieldConfig, modelName });
+ replaceProperty(displayValueProperty, modelName, recordString);
+ if (isConcatenatedProperty(displayValueProperty)) {
+ renderedDisplayValue = buildConcatExpression(displayValueProperty);
+ } else if (isBoundProperty(displayValueProperty)) {
+ renderedDisplayValue = buildBindingExpression(displayValueProperty);
+ }
+ }
+ }
+
+ if (isEnumFieldType(fieldConfig.dataType) && fieldConfig.valueMappings && fieldConfig.isArray) {
+ const displayValueMapName = `enumDisplayValueMap`;
+ additionalStatements = [
+ factory.createVariableStatement(
+ undefined,
+ factory.createVariableDeclarationList(
+ [
+ factory.createVariableDeclaration(
+ factory.createIdentifier(displayValueMapName),
+ undefined,
+ undefined,
+ factory.createObjectLiteralExpression(
+ fieldConfig.valueMappings.values.map((v) => {
+ let value = '';
+ let displayValue = '';
+ if (isFixedPropertyWithValue(v.value)) {
+ value = v.value.value.toString();
+ }
+ if (v.displayValue && isFixedPropertyWithValue(v.displayValue)) {
+ displayValue = v.displayValue.value.toString();
+ }
+ if (value === '') {
+ throw Error('Enum cannot have an empty value');
+ }
+ return factory.createPropertyAssignment(
+ factory.createStringLiteral(value),
+ factory.createStringLiteral(displayValue ?? value),
+ );
+ }),
+
+ true,
+ ),
+ ),
+ ],
+ NodeFlags.Const,
+ ),
+ ),
+ ];
+ renderedDisplayValue = factory.createElementAccessExpression(
+ factory.createIdentifier(displayValueMapName),
+ factory.createIdentifier(recordString),
+ );
+ }
+
+ if (!renderedDisplayValue) {
+ throw new InternalError(`Unable to render display value for ${fieldName}`);
+ }
+
+ return factory.createPropertyAssignment(
+ getValidProperty(fieldName),
+ factory.createArrowFunction(
+ undefined,
+ undefined,
+ [
+ factory.createParameterDeclaration(
+ undefined,
+ undefined,
+ undefined,
+ factory.createIdentifier(recordString),
+ undefined,
+ undefined,
+ undefined,
+ ),
+ ],
+ undefined,
+ factory.createToken(SyntaxKind.EqualsGreaterThanToken),
+ additionalStatements.length
+ ? factory.createBlock([...additionalStatements, factory.createReturnStatement(renderedDisplayValue)], false)
+ : renderedDisplayValue,
+ ),
+ );
+}
+
+export function getModelsToImport(fieldConfig: FieldConfigMetadata): string[] {
+ const modelDependencies: string[] = [];
+ if (fieldConfig.valueMappings && fieldConfig.valueMappings.bindingProperties) {
+ Object.values(fieldConfig.valueMappings.bindingProperties).forEach((prop) => {
+ if (prop.type === 'Data' && prop.bindingProperties.model) {
+ modelDependencies.push(prop.bindingProperties.model);
+ }
+ });
+ }
+
+ // Import join table model
+ if (fieldConfig.relationship?.type === 'HAS_MANY' && fieldConfig.relationship.relatedJoinTableName) {
+ modelDependencies.push(fieldConfig.relationship.relatedJoinTableName);
+ }
+
+ return modelDependencies;
+}
+
+/**
+ compositeVetRecords.find((r) =>
+ Object.entries(JSON.parse(id)).every(([key, value]) => r[key] === value)
+ );
+ */
+export function getMatchEveryModelFieldCallExpression({
+ recordsArrayName,
+ JSONName,
+}: {
+ recordsArrayName: string;
+ JSONName: string;
+}): CallExpression {
+ const recordString = 'r';
+ const keyString = 'key';
+ const valueString = 'value';
+ return factory.createCallExpression(
+ factory.createPropertyAccessExpression(
+ factory.createIdentifier(recordsArrayName),
+ factory.createIdentifier('find'),
+ ),
+ undefined,
+ [
+ factory.createArrowFunction(
+ undefined,
+ undefined,
+ [
+ factory.createParameterDeclaration(
+ undefined,
+ undefined,
+ undefined,
+ factory.createIdentifier(recordString),
+ undefined,
+ undefined,
+ undefined,
+ ),
+ ],
+ undefined,
+ factory.createToken(SyntaxKind.EqualsGreaterThanToken),
+ factory.createCallExpression(
+ factory.createPropertyAccessExpression(
+ factory.createCallExpression(
+ factory.createPropertyAccessExpression(
+ factory.createIdentifier('Object'),
+ factory.createIdentifier('entries'),
+ ),
+ undefined,
+ [
+ factory.createCallExpression(
+ factory.createPropertyAccessExpression(
+ factory.createIdentifier('JSON'),
+ factory.createIdentifier('parse'),
+ ),
+ undefined,
+ [factory.createIdentifier(JSONName)],
+ ),
+ ],
+ ),
+ factory.createIdentifier('every'),
+ ),
+ undefined,
+ [
+ factory.createArrowFunction(
+ undefined,
+ undefined,
+ [
+ factory.createParameterDeclaration(
+ undefined,
+ undefined,
+ undefined,
+ factory.createArrayBindingPattern([
+ factory.createBindingElement(undefined, undefined, factory.createIdentifier('key'), undefined),
+ factory.createBindingElement(undefined, undefined, factory.createIdentifier('value'), undefined),
+ ]),
+ undefined,
+ undefined,
+ undefined,
+ ),
+ ],
+ undefined,
+ factory.createToken(SyntaxKind.EqualsGreaterThanToken),
+ factory.createBinaryExpression(
+ factory.createElementAccessExpression(
+ factory.createIdentifier(recordString),
+ factory.createIdentifier(keyString),
+ ),
+ factory.createToken(SyntaxKind.EqualsEqualsEqualsToken),
+ factory.createIdentifier(valueString),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ );
+}
+
+/**
+ const CompositeBowlIdSet = new Set(
+ Array.isArray(CompositeBowl)
+ ? CompositeBowl.map((r) => getIDValue.CompositeBowl?.(r))
+ : getIDValue.CompositeBowl?.(CompositeBowl)
+ );
+ */
+export const buildSelectedRecordsIdSet = (fieldConfigs: Record): Statement[] => {
+ const statements: Statement[] = [];
+ Object.entries(fieldConfigs).forEach((fieldConfig) => {
+ const [name, fieldConfigMetaData] = fieldConfig;
+ const fieldName = fieldConfigMetaData.sanitizedFieldName || name;
+ const recordString = 'r';
+ if (fieldConfigMetaData.relationship && isModelDataType(fieldConfigMetaData)) {
+ statements.push(
+ factory.createVariableStatement(
+ undefined,
+ factory.createVariableDeclarationList(
+ [
+ factory.createVariableDeclaration(
+ factory.createIdentifier(`${fieldName}IdSet`),
+ undefined,
+ undefined,
+ factory.createNewExpression(factory.createIdentifier('Set'), undefined, [
+ factory.createConditionalExpression(
+ factory.createCallExpression(
+ factory.createPropertyAccessExpression(
+ factory.createIdentifier('Array'),
+ factory.createIdentifier('isArray'),
+ ),
+ undefined,
+ [factory.createIdentifier(fieldName)],
+ ),
+ factory.createToken(SyntaxKind.QuestionToken),
+ factory.createCallExpression(
+ factory.createPropertyAccessExpression(
+ factory.createIdentifier(fieldName),
+ factory.createIdentifier('map'),
+ ),
+ undefined,
+ [
+ factory.createArrowFunction(
+ undefined,
+ undefined,
+ [
+ factory.createParameterDeclaration(
+ undefined,
+ undefined,
+ undefined,
+ factory.createIdentifier(recordString),
+ undefined,
+ undefined,
+ undefined,
+ ),
+ ],
+ undefined,
+ factory.createToken(SyntaxKind.EqualsGreaterThanToken),
+ getIDValueCallChain({ fieldName, recordString }),
+ ),
+ ],
+ ),
+ factory.createToken(SyntaxKind.ColonToken),
+ getIDValueCallChain({ fieldName, recordString: fieldName }),
+ ),
+ ]),
+ ),
+ ],
+ NodeFlags.Const,
+ ),
+ ),
+ );
+ }
+ });
+ return statements;
+};
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 085b513cf..094acd326 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
@@ -25,7 +25,7 @@ import { buildBaseCollectionVariableStatement } from '../../react-studio-templat
import { ImportCollection, ImportSource } from '../../imports';
import { lowerCaseFirst } from '../../helpers';
import { isManyToManyRelationship } from './map-from-fieldConfigs';
-import { extractModelAndKey } from './display-value';
+import { extractModelAndKeys } from './model-values';
import { isModelDataType } from './render-checkers';
export const buildRelationshipQuery = (
@@ -66,11 +66,12 @@ export const buildManyToManyRelationshipDataStoreStatements = (
const dataToUnlinkMap = `${lowerCaseFirst(fieldName)}ToUnLinkMap`;
const updatedMap = `${lowerCaseFirst(fieldName)}Map`;
const originalMap = `${linkedDataName}Map`;
- const { key } = extractModelAndKey(fieldConfigMetaData.valueMappings);
- if (!key) {
- throw new InternalError(`Could not identify primary key for ${relatedModelName}`);
+ const { keys } = extractModelAndKeys(fieldConfigMetaData.valueMappings);
+ if (!keys) {
+ throw new InternalError(`Could not identify primary key(s) for ${relatedModelName}`);
}
- const relatedModelPrimaryKey = key;
+ // TODO: update for composite
+ const relatedModelPrimaryKey = keys[0];
return [
factory.createVariableStatement(
@@ -1164,11 +1165,12 @@ export const buildHasManyRelationshipDataStoreStatements = (
const dataToUnLink = `${lowerCaseFirst(fieldName)}ToUnLink`;
const dataToLinkSet = `${lowerCaseFirst(fieldName)}Set`;
const linkedDataSet = `${linkedDataName}Set`;
- const { key } = extractModelAndKey(fieldConfigMetaData.valueMappings);
- if (!key) {
- throw new InternalError(`Could not identify primary key for ${relatedModelName}`);
+ const { keys } = extractModelAndKeys(fieldConfigMetaData.valueMappings);
+ if (!keys) {
+ throw new InternalError(`Could not identify primary key(s) for ${relatedModelName}`);
}
- const relatedModelPrimaryKey = key;
+ // TODO: update for composite keys
+ const relatedModelPrimaryKey = keys[0];
if (dataStoreActionType === 'update') {
return [
factory.createVariableStatement(
diff --git a/packages/codegen-ui-react/lib/forms/form-renderer-helper/render-array-field.ts b/packages/codegen-ui-react/lib/forms/form-renderer-helper/render-array-field.ts
index f46838d71..cf73684c6 100644
--- a/packages/codegen-ui-react/lib/forms/form-renderer-helper/render-array-field.ts
+++ b/packages/codegen-ui-react/lib/forms/form-renderer-helper/render-array-field.ts
@@ -27,7 +27,7 @@ import {
} from './form-state';
import { buildOverrideOnChangeStatement } from './event-handler-props';
import { isModelDataType, shouldImplementDisplayValueFunction } from './render-checkers';
-import { getDisplayValueObjectName } from './display-value';
+import { getDisplayValueObjectName } from './model-values';
import { getElementAccessExpression } from './invalid-variable-helpers';
function getOnChangeAttribute({
diff --git a/packages/codegen-ui-react/lib/forms/form-renderer-helper/render-checkers.ts b/packages/codegen-ui-react/lib/forms/form-renderer-helper/render-checkers.ts
index 68f4bb398..cffaba05e 100644
--- a/packages/codegen-ui-react/lib/forms/form-renderer-helper/render-checkers.ts
+++ b/packages/codegen-ui-react/lib/forms/form-renderer-helper/render-checkers.ts
@@ -26,3 +26,7 @@ export const isModelDataType = (
export const shouldImplementDisplayValueFunction = (config: FieldConfigMetadata): boolean => {
return isModelDataType(config) || (isEnumFieldType(config.dataType) && shouldWrapInArrayField(config));
};
+
+export const shouldImplementIDValueFunction = (config: FieldConfigMetadata): boolean => {
+ return !!(isModelDataType(config) && config.valueMappings);
+};
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 564040799..a20ae5938 100644
--- a/packages/codegen-ui-react/lib/forms/react-form-renderer.ts
+++ b/packages/codegen-ui-react/lib/forms/react-form-renderer.ts
@@ -80,7 +80,6 @@ import {
} from './form-renderer-helper';
import {
buildUseStateExpression,
- buildSelectedRecordsIdSet,
getCurrentDisplayValueName,
getArrayChildRefName,
getCurrentValueName,
@@ -96,6 +95,7 @@ import {
validationFunctionType,
validationResponseType,
} from './form-renderer-helper/type-helper';
+import { buildSelectedRecordsIdSet } from './form-renderer-helper/model-values';
type RenderComponentOnlyResponse = {
compText: string;
@@ -452,8 +452,6 @@ export abstract class ReactFormTemplateRenderer extends StudioTemplateRenderer<
statements.push(...getUseStateHooks(formMetadata.fieldConfigs));
- statements.push(...buildSelectedRecordsIdSet(formMetadata.fieldConfigs));
-
statements.push(buildUseStateExpression('errors', factory.createObjectLiteralExpression()));
let defaultValueVariableName: undefined | string;
@@ -570,9 +568,14 @@ export abstract class ReactFormTemplateRenderer extends StudioTemplateRenderer<
}
});
- const { validationsObject, dataTypesMap, displayValueObject, modelsToImport, usesArrayField } = mapFromFieldConfigs(
- formMetadata.fieldConfigs,
- );
+ const { validationsObject, dataTypesMap, displayValueObject, idValueObject, modelsToImport, usesArrayField } =
+ mapFromFieldConfigs(formMetadata.fieldConfigs);
+
+ if (idValueObject) {
+ statements.push(idValueObject);
+ }
+
+ statements.push(...buildSelectedRecordsIdSet(formMetadata.fieldConfigs));
this.shouldRenderArrayField = usesArrayField;
diff --git a/packages/codegen-ui/example-schemas/datastore/composite-relationships.json b/packages/codegen-ui/example-schemas/datastore/composite-relationships.json
new file mode 100644
index 000000000..5c9069d2a
--- /dev/null
+++ b/packages/codegen-ui/example-schemas/datastore/composite-relationships.json
@@ -0,0 +1,554 @@
+{
+ "models": {
+ "CompositeDog": {
+ "name": "CompositeDog",
+ "fields": {
+ "name": {
+ "name": "name",
+ "isArray": false,
+ "type": "ID",
+ "isRequired": true,
+ "attributes": []
+ },
+ "description": {
+ "name": "description",
+ "isArray": false,
+ "type": "String",
+ "isRequired": true,
+ "attributes": []
+ },
+ "CompositeBowl": {
+ "name": "CompositeBowl",
+ "isArray": false,
+ "type": {
+ "model": "CompositeBowl"
+ },
+ "isRequired": false,
+ "attributes": [],
+ "association": {
+ "connectionType": "HAS_ONE",
+ "associatedWith": [
+ "shape",
+ "size"
+ ],
+ "targetNames": [
+ "compositeDogCompositeBowlShape",
+ "compositeDogCompositeBowlSize"
+ ]
+ }
+ },
+ "CompositeOwner": {
+ "name": "CompositeOwner",
+ "isArray": false,
+ "type": {
+ "model": "CompositeOwner"
+ },
+ "isRequired": false,
+ "attributes": [],
+ "association": {
+ "connectionType": "BELONGS_TO",
+ "targetNames": [
+ "compositeDogCompositeOwnerLastName",
+ "compositeDogCompositeOwnerFirstName"
+ ]
+ }
+ },
+ "CompositeToys": {
+ "name": "CompositeToys",
+ "isArray": true,
+ "type": {
+ "model": "CompositeToy"
+ },
+ "isRequired": false,
+ "attributes": [],
+ "isArrayNullable": true,
+ "association": {
+ "connectionType": "HAS_MANY",
+ "associatedWith": [
+ "compositeDogCompositeToysName",
+ "compositeDogCompositeToysDescription"
+ ]
+ }
+ },
+ "CompositeVets": {
+ "name": "CompositeVets",
+ "isArray": true,
+ "type": {
+ "model": "CompositeDogCompositeVet"
+ },
+ "isRequired": false,
+ "attributes": [],
+ "isArrayNullable": true,
+ "association": {
+ "connectionType": "HAS_MANY",
+ "associatedWith": [
+ "compositeDog"
+ ]
+ }
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "isArray": false,
+ "type": "AWSDateTime",
+ "isRequired": false,
+ "attributes": [],
+ "isReadOnly": true
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "isArray": false,
+ "type": "AWSDateTime",
+ "isRequired": false,
+ "attributes": [],
+ "isReadOnly": true
+ },
+ "compositeDogCompositeBowlShape": {
+ "name": "compositeDogCompositeBowlShape",
+ "isArray": false,
+ "type": "ID",
+ "isRequired": false,
+ "attributes": []
+ },
+ "compositeDogCompositeBowlSize": {
+ "name": "compositeDogCompositeBowlSize",
+ "isArray": false,
+ "type": "String",
+ "isRequired": false,
+ "attributes": []
+ },
+ "compositeDogCompositeOwnerLastName": {
+ "name": "compositeDogCompositeOwnerLastName",
+ "isArray": false,
+ "type": "ID",
+ "isRequired": false,
+ "attributes": []
+ },
+ "compositeDogCompositeOwnerFirstName": {
+ "name": "compositeDogCompositeOwnerFirstName",
+ "isArray": false,
+ "type": "String",
+ "isRequired": false,
+ "attributes": []
+ }
+ },
+ "syncable": true,
+ "pluralName": "CompositeDogs",
+ "attributes": [
+ {
+ "type": "model",
+ "properties": {}
+ },
+ {
+ "type": "key",
+ "properties": {
+ "fields": [
+ "name",
+ "description"
+ ]
+ }
+ }
+ ]
+ },
+ "CompositeBowl": {
+ "name": "CompositeBowl",
+ "fields": {
+ "shape": {
+ "name": "shape",
+ "isArray": false,
+ "type": "ID",
+ "isRequired": true,
+ "attributes": []
+ },
+ "size": {
+ "name": "size",
+ "isArray": false,
+ "type": "String",
+ "isRequired": true,
+ "attributes": []
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "isArray": false,
+ "type": "AWSDateTime",
+ "isRequired": false,
+ "attributes": [],
+ "isReadOnly": true
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "isArray": false,
+ "type": "AWSDateTime",
+ "isRequired": false,
+ "attributes": [],
+ "isReadOnly": true
+ }
+ },
+ "syncable": true,
+ "pluralName": "CompositeBowls",
+ "attributes": [
+ {
+ "type": "model",
+ "properties": {}
+ },
+ {
+ "type": "key",
+ "properties": {
+ "fields": [
+ "shape",
+ "size"
+ ]
+ }
+ }
+ ]
+ },
+ "CompositeOwner": {
+ "name": "CompositeOwner",
+ "fields": {
+ "lastName": {
+ "name": "lastName",
+ "isArray": false,
+ "type": "ID",
+ "isRequired": true,
+ "attributes": []
+ },
+ "firstName": {
+ "name": "firstName",
+ "isArray": false,
+ "type": "String",
+ "isRequired": true,
+ "attributes": []
+ },
+ "CompositeDog": {
+ "name": "CompositeDog",
+ "isArray": false,
+ "type": {
+ "model": "CompositeDog"
+ },
+ "isRequired": false,
+ "attributes": [],
+ "association": {
+ "connectionType": "HAS_ONE",
+ "associatedWith": [
+ "CompositeOwner"
+ ],
+ "targetNames": [
+ "compositeOwnerCompositeDogName",
+ "compositeOwnerCompositeDogDescription"
+ ]
+ }
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "isArray": false,
+ "type": "AWSDateTime",
+ "isRequired": false,
+ "attributes": [],
+ "isReadOnly": true
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "isArray": false,
+ "type": "AWSDateTime",
+ "isRequired": false,
+ "attributes": [],
+ "isReadOnly": true
+ },
+ "compositeOwnerCompositeDogName": {
+ "name": "compositeOwnerCompositeDogName",
+ "isArray": false,
+ "type": "ID",
+ "isRequired": false,
+ "attributes": []
+ },
+ "compositeOwnerCompositeDogDescription": {
+ "name": "compositeOwnerCompositeDogDescription",
+ "isArray": false,
+ "type": "String",
+ "isRequired": false,
+ "attributes": []
+ }
+ },
+ "syncable": true,
+ "pluralName": "CompositeOwners",
+ "attributes": [
+ {
+ "type": "model",
+ "properties": {}
+ },
+ {
+ "type": "key",
+ "properties": {
+ "fields": [
+ "lastName",
+ "firstName"
+ ]
+ }
+ }
+ ]
+ },
+ "CompositeToy": {
+ "name": "CompositeToy",
+ "fields": {
+ "kind": {
+ "name": "kind",
+ "isArray": false,
+ "type": "ID",
+ "isRequired": true,
+ "attributes": []
+ },
+ "color": {
+ "name": "color",
+ "isArray": false,
+ "type": "String",
+ "isRequired": true,
+ "attributes": []
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "isArray": false,
+ "type": "AWSDateTime",
+ "isRequired": false,
+ "attributes": [],
+ "isReadOnly": true
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "isArray": false,
+ "type": "AWSDateTime",
+ "isRequired": false,
+ "attributes": [],
+ "isReadOnly": true
+ },
+ "compositeDogCompositeToysName": {
+ "name": "compositeDogCompositeToysName",
+ "isArray": false,
+ "type": "ID",
+ "isRequired": false,
+ "attributes": []
+ },
+ "compositeDogCompositeToysDescription": {
+ "name": "compositeDogCompositeToysDescription",
+ "isArray": false,
+ "type": "String",
+ "isRequired": false,
+ "attributes": []
+ }
+ },
+ "syncable": true,
+ "pluralName": "CompositeToys",
+ "attributes": [
+ {
+ "type": "model",
+ "properties": {}
+ },
+ {
+ "type": "key",
+ "properties": {
+ "fields": [
+ "kind",
+ "color"
+ ]
+ }
+ },
+ {
+ "type": "key",
+ "properties": {
+ "name": "gsi-CompositeDog.CompositeToys",
+ "fields": [
+ "compositeDogCompositeToysName",
+ "compositeDogCompositeToysDescription"
+ ]
+ }
+ }
+ ]
+ },
+ "CompositeVet": {
+ "name": "CompositeVet",
+ "fields": {
+ "specialty": {
+ "name": "specialty",
+ "isArray": false,
+ "type": "ID",
+ "isRequired": true,
+ "attributes": []
+ },
+ "city": {
+ "name": "city",
+ "isArray": false,
+ "type": "String",
+ "isRequired": true,
+ "attributes": []
+ },
+ "CompositeDogs": {
+ "name": "CompositeDogs",
+ "isArray": true,
+ "type": {
+ "model": "CompositeDogCompositeVet"
+ },
+ "isRequired": false,
+ "attributes": [],
+ "isArrayNullable": true,
+ "association": {
+ "connectionType": "HAS_MANY",
+ "associatedWith": [
+ "compositeVet"
+ ]
+ }
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "isArray": false,
+ "type": "AWSDateTime",
+ "isRequired": false,
+ "attributes": [],
+ "isReadOnly": true
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "isArray": false,
+ "type": "AWSDateTime",
+ "isRequired": false,
+ "attributes": [],
+ "isReadOnly": true
+ }
+ },
+ "syncable": true,
+ "pluralName": "CompositeVets",
+ "attributes": [
+ {
+ "type": "model",
+ "properties": {}
+ },
+ {
+ "type": "key",
+ "properties": {
+ "fields": [
+ "specialty",
+ "city"
+ ]
+ }
+ }
+ ]
+ },
+ "CompositeDogCompositeVet": {
+ "name": "CompositeDogCompositeVet",
+ "fields": {
+ "id": {
+ "name": "id",
+ "isArray": false,
+ "type": "ID",
+ "isRequired": true,
+ "attributes": []
+ },
+ "compositeDogName": {
+ "name": "compositeDogName",
+ "isArray": false,
+ "type": "ID",
+ "isRequired": false,
+ "attributes": []
+ },
+ "compositeDogdescription": {
+ "name": "compositeDogdescription",
+ "isArray": false,
+ "type": "String",
+ "isRequired": false,
+ "attributes": []
+ },
+ "compositeVetSpecialty": {
+ "name": "compositeVetSpecialty",
+ "isArray": false,
+ "type": "ID",
+ "isRequired": false,
+ "attributes": []
+ },
+ "compositeVetcity": {
+ "name": "compositeVetcity",
+ "isArray": false,
+ "type": "String",
+ "isRequired": false,
+ "attributes": []
+ },
+ "compositeDog": {
+ "name": "compositeDog",
+ "isArray": false,
+ "type": {
+ "model": "CompositeDog"
+ },
+ "isRequired": true,
+ "attributes": [],
+ "association": {
+ "connectionType": "BELONGS_TO",
+ "targetNames": [
+ "compositeDogName",
+ "compositeDogdescription"
+ ]
+ }
+ },
+ "compositeVet": {
+ "name": "compositeVet",
+ "isArray": false,
+ "type": {
+ "model": "CompositeVet"
+ },
+ "isRequired": true,
+ "attributes": [],
+ "association": {
+ "connectionType": "BELONGS_TO",
+ "targetNames": [
+ "compositeVetSpecialty",
+ "compositeVetcity"
+ ]
+ }
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "isArray": false,
+ "type": "AWSDateTime",
+ "isRequired": false,
+ "attributes": [],
+ "isReadOnly": true
+ },
+ "updatedAt": {
+ "name": "updatedAt",
+ "isArray": false,
+ "type": "AWSDateTime",
+ "isRequired": false,
+ "attributes": [],
+ "isReadOnly": true
+ }
+ },
+ "syncable": true,
+ "pluralName": "CompositeDogCompositeVets",
+ "attributes": [
+ {
+ "type": "model",
+ "properties": {}
+ },
+ {
+ "type": "key",
+ "properties": {
+ "name": "byCompositeDog",
+ "fields": [
+ "compositeDogName",
+ "compositeDogdescription"
+ ]
+ }
+ },
+ {
+ "type": "key",
+ "properties": {
+ "name": "byCompositeVet",
+ "fields": [
+ "compositeVetSpecialty",
+ "compositeVetcity"
+ ]
+ }
+ }
+ ]
+ }
+ },
+ "enums": {},
+ "nonModels": {},
+ "codegenVersion": "3.3.2",
+ "version": "8f8e59ee8fb2e3ca4efda3aa25b0211f"
+}
\ No newline at end of file
diff --git a/packages/codegen-ui/example-schemas/forms/composite-dog-datastore-update.json b/packages/codegen-ui/example-schemas/forms/composite-dog-datastore-update.json
new file mode 100644
index 000000000..72f6304fa
--- /dev/null
+++ b/packages/codegen-ui/example-schemas/forms/composite-dog-datastore-update.json
@@ -0,0 +1,12 @@
+{
+ "name": "UpdateCompositeDogForm",
+ "dataType": {
+ "dataSourceType": "DataStore",
+ "dataTypeName": "CompositeDog"
+ },
+ "formActionType": "update",
+ "fields": {},
+ "sectionalElements": {},
+ "style": {},
+ "cta": {}
+}
\ No newline at end of file
diff --git a/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/form-field.test.ts b/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/form-field.test.ts
index b1c32cf57..f40e51a60 100644
--- a/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/form-field.test.ts
+++ b/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/form-field.test.ts
@@ -309,7 +309,6 @@ describe('getFormDefinitionInputElement', () => {
props: { label: 'Label', isDisabled: true, placeholder: 'Please select an option' },
valueMappings: {
values: [{ value: { value: 'value1' }, displayvalue: { value: 'displayValue1' } }],
- bindingProperties: {},
},
defaultValue: 'value1',
});
@@ -495,7 +494,6 @@ describe('getFormDefinitionInputElement', () => {
props: { label: 'Label', name: 'MyFieldName' },
valueMappings: {
values: [{ value: { value: 'value1' }, displayvalue: { value: 'displayValue1' } }],
- bindingProperties: {},
},
});
});
@@ -642,21 +640,22 @@ describe('mergeValueMappings', () => {
expect(mergedMappings.values.find((v) => 'value' in v.value && v.value.value === 'AUSTIN')).toBeUndefined();
});
- it('should merge base and override bindingProperties', () => {
+ it('should transfer bindingProperties', () => {
expect(
mergeValueMappings(
{
values: [{ value: { value: 'sdjoiflj' }, displayValue: { bindingProperties: { property: 'Dog' } } }],
bindingProperties: {
Dog: { type: 'Data', bindingProperties: { model: 'Dog' } },
- Person: { type: 'Data', bindingProperties: { model: 'Person' } },
},
},
- { values: [], bindingProperties: { Dog: { type: 'Data', bindingProperties: { model: 'MyDog' } } } },
+ {
+ values: [{ value: { value: 'sdjoiflj' } }],
+ bindingProperties: { Dog: { type: 'Data', bindingProperties: { model: 'Dog' } } },
+ },
).bindingProperties,
).toStrictEqual({
- Person: { type: 'Data', bindingProperties: { model: 'Person' } },
- Dog: { type: 'Data', bindingProperties: { model: 'MyDog' } },
+ Dog: { type: 'Data', bindingProperties: { model: 'Dog' } },
});
});
});
diff --git a/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/model-fields-configs.test.ts b/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/model-fields-configs.test.ts
index 3f66e8f97..f0ec032c2 100644
--- a/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/model-fields-configs.test.ts
+++ b/packages/codegen-ui/lib/__tests__/generate-form-definition/helpers/model-fields-configs.test.ts
@@ -260,7 +260,7 @@ describe('mapModelFieldsConfigs', () => {
value: 'ownerId',
isArray: false,
valueMappings: {
- values: [{ value: { bindingProperties: { property: 'Owner', field: 'id' } } }],
+ values: [{ value: { bindingProperties: { property: 'Owner', field: 'ownerId' } } }],
bindingProperties: { Owner: { type: 'Data', bindingProperties: { model: 'Owner' } } },
},
},
diff --git a/packages/codegen-ui/lib/generate-form-definition/helpers/form-field.ts b/packages/codegen-ui/lib/generate-form-definition/helpers/form-field.ts
index 5e7a49ea3..1b9a565fb 100644
--- a/packages/codegen-ui/lib/generate-form-definition/helpers/form-field.ts
+++ b/packages/codegen-ui/lib/generate-form-definition/helpers/form-field.ts
@@ -33,6 +33,22 @@ export function mergeValueMappings(
base?: StudioFormValueMappings,
override?: StudioFormValueMappings,
): StudioFormValueMappings {
+ // if model-based
+ if (base?.bindingProperties || override?.bindingProperties) {
+ const valueMappings = override || base;
+ const firstValue = valueMappings?.values[0];
+ if (!firstValue) {
+ throw new InternalError(`No valueMapping found for model-bound field`);
+ }
+ firstValue.displayValue = override?.values?.[0]?.displayValue;
+
+ return {
+ values: valueMappings.values,
+ bindingProperties: valueMappings.bindingProperties,
+ };
+ }
+
+ // if not model-based
let values: StudioFormValueMappings['values'] = [];
if (!base && override) {
@@ -55,7 +71,6 @@ export function mergeValueMappings(
return {
values,
- bindingProperties: { ...base?.bindingProperties, ...override?.bindingProperties },
};
}
diff --git a/packages/codegen-ui/lib/generate-form-definition/helpers/model-fields-configs.ts b/packages/codegen-ui/lib/generate-form-definition/helpers/model-fields-configs.ts
index f533494dd..733b12df9 100644
--- a/packages/codegen-ui/lib/generate-form-definition/helpers/model-fields-configs.ts
+++ b/packages/codegen-ui/lib/generate-form-definition/helpers/model-fields-configs.ts
@@ -46,10 +46,12 @@ export function getFieldTypeMapKey(field: GenericDataField): FieldTypeMapKeys {
}
function getValueMappings({
+ fieldName,
field,
enums,
allModels,
}: {
+ fieldName: string;
field: GenericDataField;
enums: GenericDataSchema['enums'];
allModels: { [modelName: string]: GenericDataModel };
@@ -72,8 +74,14 @@ function getValueMappings({
// if relationship
if (field.relationship) {
const modelName = field.relationship.relatedModelName;
+ // if model, store all keys; else, store field as key
+ const keys =
+ typeof field.dataType === 'object' && 'model' in field.dataType ? allModels[modelName].primaryKeys : [fieldName];
+ const values: StudioFormValueMappings['values'] = keys.map((key) => ({
+ value: { bindingProperties: { property: modelName, field: key } },
+ }));
return {
- values: [{ value: { bindingProperties: { property: modelName, field: allModels[modelName].primaryKeys[0] } } }],
+ values,
bindingProperties: { [modelName]: { type: 'Data', bindingProperties: { model: modelName } } },
};
}
@@ -126,7 +134,7 @@ export function getFieldConfigFromModelField({
config.relationship = field.relationship;
}
- const valueMappings = getValueMappings({ field, enums: dataSchema.enums, allModels: dataSchema.models });
+ const valueMappings = getValueMappings({ fieldName, field, enums: dataSchema.enums, allModels: dataSchema.models });
if (valueMappings) {
config.inputType.valueMappings = valueMappings;
}