Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 44 additions & 18 deletions packages/react/src/auto/AutoForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,15 @@ import type {
UseActionFormSubmit,
} from "../use-action-form/types.js";
import { isPlainObject, processDefaultValues, toDefaultValues } from "../use-action-form/utils.js";
import { getRelatedModelFields, isHasManyOrHasManyThroughField, isRelationshipField, pathListToSelection } from "../use-table/helpers.js";
import {
fileSelection,
getRelatedModelFields,
isHasManyOrHasManyThroughField,
isRelationshipField,
pathListToSelection,
richTextSelection,
roleAssignmentsSelection,
} from "../use-table/helpers.js";
import type { FieldErrors, FieldValues, UseFormReturn } from "../useActionForm.js";
import { useActionForm } from "../useActionForm.js";
import { get, getFlattenedObjectKeys, set, type ErrorWrapper, type OptionsType } from "../utils.js";
Expand Down Expand Up @@ -219,7 +227,7 @@ const useFormSelection = (props: {
if (!select || !modelApiIdentifier) {
return;
}
return forceIdsIntoSelect({ select, rootFieldsMetadata });
return forceRequiredFieldsIntoSelect({ select, rootFieldsMetadata });
}, [select, modelApiIdentifier, rootFieldsMetadata]);

if (!modelApiIdentifier || !fields.length) {
Expand All @@ -236,38 +244,56 @@ const useFormSelection = (props: {
return pathListToSelection(modelApiIdentifier, paths, fieldMetaData);
};

const forceIdsIntoSelect = (props: { select: FieldSelection; rootFieldsMetadata: FieldMetadata[] }) => {
const forceRequiredFieldsIntoSelect = (props: { select: FieldSelection; rootFieldsMetadata: FieldMetadata[] }) => {
const { select: originalSelect, rootFieldsMetadata } = props;
const select = structuredClone(originalSelect);

select.id = true; // Always select the ID for the root model

const addIdToSelection = (selectPath: string, fieldMetadata: FieldMetadata) => {
if (!isRelationshipField(fieldMetadata)) {
return; // Non relationships do not need additional selection
}
const addRequiredFieldsToSelection = (selectPath: string, fieldMetadata: FieldMetadata) => {
const isRichTextField = fieldMetadata.fieldType === FieldType.RichText;
const isFileField = fieldMetadata.fieldType === FieldType.File;
const isRolesField = fieldMetadata.fieldType === FieldType.RoleAssignments;
const isRelationship = isRelationshipField(fieldMetadata);

const existingSelection = get(select, selectPath);
if (!existingSelection || typeof existingSelection !== "object") {
// Do not go deeper than what is defined in the select object
return;
if (!existingSelection) {
return; // Do not select at all
}

const isManyRelation = isHasManyOrHasManyThroughField(fieldMetadata);
const currentFieldSelectPathPrefix = isManyRelation ? `${selectPath}.edges.node` : `${selectPath}`;
const idPath = `${currentFieldSelectPathPrefix}.id`;
if (isRichTextField) {
return set(select, selectPath, richTextSelection); // Assume that the whole rich text is expected to be selected
}

set(select, idPath, true);
if (isFileField) {
return set(select, selectPath, fileSelection); // Assume whole file is expected to be selected
}
if (isRolesField) {
return set(select, selectPath, roleAssignmentsSelection); // Assume whole role assignments are expected to be selected
}

const relatedModelFields = getRelatedModelFields(fieldMetadata);
if (isRelationship) {
if (typeof existingSelection !== "object") {
// Do not go deeper than what is defined in the select object
return;
}

const isManyRelation = isHasManyOrHasManyThroughField(fieldMetadata);
const currentFieldSelectPathPrefix = isManyRelation ? `${selectPath}.edges.node` : `${selectPath}`;
const idPath = `${currentFieldSelectPathPrefix}.id`;

set(select, idPath, true);

for (const relatedModelField of relatedModelFields) {
addIdToSelection(`${currentFieldSelectPathPrefix}.${relatedModelField.apiIdentifier}`, relatedModelField);
const relatedModelFields = getRelatedModelFields(fieldMetadata);

for (const relatedModelField of relatedModelFields) {
addRequiredFieldsToSelection(`${currentFieldSelectPathPrefix}.${relatedModelField.apiIdentifier}`, relatedModelField);
}
}
};

for (const field of rootFieldsMetadata) {
addIdToSelection(field.apiIdentifier, field);
addRequiredFieldsToSelection(field.apiIdentifier, field);
}

return select;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this is complicated enough to deserve a test -- if you're outskie today LMK and I can whip one up!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll be around until about 4pm. I'll try to get one up before then

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately I gotta run now. If you could add some quick tests, that would be spectacular 🫡

Expand Down
6 changes: 3 additions & 3 deletions packages/react/src/use-table/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -331,18 +331,18 @@ export const isRelationshipField = (field: { fieldType: GadgetFieldType }) => {
return isHasOneOrBelongsToField(field) || isHasManyOrHasManyThroughField(field);
};

const richTextSelection = {
export const richTextSelection = {
markdown: true,
truncatedHTML: true,
};

const fileSelection = {
export const fileSelection = {
url: true,
mimeType: true,
fileName: true,
};

const roleAssignmentsSelection = {
export const roleAssignmentsSelection = {
key: true,
name: true,
};
Expand Down