Skip to content

Commit

Permalink
Added HasManyThrough field support to AutoTable
Browse files Browse the repository at this point in the history
  • Loading branch information
MillanWangGadget committed Sep 26, 2024
1 parent b30a841 commit 138f92f
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 14 deletions.
5 changes: 5 additions & 0 deletions packages/react/.changeset/poor-hairs-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@gadgetinc/react": patch
---

Added `HasManyThrough` field support to AutoTable column selection
13 changes: 13 additions & 0 deletions packages/react/spec/auto/PolarisAutoTable.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,19 @@ export const HideSearchAndPagination = {
},
};

export const HasManyThroughFields = {
args: {
model: api.hasManyThrough.baseModel,
columns: [
"baseModelHmtField",
{
header: "Sibling ID",
field: "baseModelHmtField.edges.node.id",
},
],
},
};

const windowAlert = (message) => {
// eslint-disable-next-line no-undef
window.alert(message);
Expand Down
141 changes: 137 additions & 4 deletions packages/react/spec/auto/hooks/useTable.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ describe("useTable hook", () => {

it("should use default display field if the column string is a relationship field", async () => {
const result = getUseTableResult({
columns: ["name", "hasMany", "hasOne", "belongsTo"],
columns: ["name", "hasMany", "hasOne", "belongsTo", "baseModelHmtField"],
});
loadMockWidgetModelMetadataForRelationship();
loadMockWidgetDataForRelationship();
Expand Down Expand Up @@ -362,6 +362,14 @@ describe("useTable hook", () => {
id
email
}
baseModelHmtField {
edges {
node {
id
siblingName
}
}
}
__typename
}
}
Expand All @@ -372,10 +380,20 @@ describe("useTable hook", () => {
}
}"
`);
expect(result.current[0].columns?.map((column) => column.field)).toEqual(["name", "hasMany", "hasOne", "belongsTo"]);
expect(result.current[0].columns?.map((column) => column.field)).toEqual([
"name",
"hasMany",
"hasOne",
"belongsTo",
"baseModelHmtField",
]);
expect(result.current[0].rows).toMatchInlineSnapshot(`
[
{
"baseModelHmtField": [
"sibling 1",
"sibling 2",
],
"belongsTo": "foo",
"hasMany": [
"gizmo 9",
Expand All @@ -392,7 +410,7 @@ describe("useTable hook", () => {

it("should return the related model field if the column values are relationship fields", async () => {
const result = getUseTableResult({
columns: ["name", "hasMany.edges.node.name", "hasOne.name", "belongsTo.str"],
columns: ["name", "hasMany.edges.node.name", "hasOne.name", "belongsTo.str", "baseModelHmtField.edges.node.id"],
});
loadMockWidgetModelMetadataForRelationship();
loadMockWidgetDataForRelationship();
Expand Down Expand Up @@ -428,6 +446,13 @@ describe("useTable hook", () => {
id
str
}
baseModelHmtField {
edges {
node {
id
}
}
}
__typename
}
}
Expand All @@ -443,10 +468,15 @@ describe("useTable hook", () => {
"hasMany.edges.node.name",
"hasOne.name",
"belongsTo.str",
"baseModelHmtField.edges.node.id",
]);
expect(result.current[0].rows).toMatchInlineSnapshot(`
[
{
"baseModelHmtField.edges.node.id": [
"1",
"2",
],
"belongsTo.str": "foo",
"hasMany.edges.node.name": [
"gizmo 9",
Expand All @@ -461,7 +491,7 @@ describe("useTable hook", () => {
`);
});

it.each(["password", "hasManyThrough"])("should throw an error if the selected field type is %s", async (fieldType) => {
it.each(["password"])("should throw an error if the selected field type is %s", async (fieldType) => {
let error: Error | undefined;
try {
getUseTableResult({
Expand Down Expand Up @@ -1206,6 +1236,88 @@ const loadMockWidgetModelMetadataForRelationship = () => {
},
},
},
{
name: "Base model hmt field",
apiIdentifier: "baseModelHmtField",
fieldType: "HasManyThrough",
requiredArgumentForInput: false,
sortable: false,
filterable: false,
__typename: "GadgetModelField",
configuration: {
__typename: "GadgetHasManyThroughConfig",
fieldType: "HasManyThrough",
validations: [],
relatedModel: {
key: "Oss4sCDW-DJU",
apiIdentifier: "siblingModel",
namespace: ["hasManyThrough"],
defaultDisplayField: {
name: "Sibling name",
apiIdentifier: "siblingName",
fieldType: "String",
__typename: "GadgetModelField",
},
fields: [
{
name: "Id",
apiIdentifier: "id",
fieldType: "ID",
__typename: "GadgetModelField",
},
{
name: "Sibling name",
apiIdentifier: "siblingName",
fieldType: "String",
__typename: "GadgetModelField",
},
{
name: "Sibling model hmt field",
apiIdentifier: "siblingModelHmtField",
fieldType: "HasManyThrough",
__typename: "GadgetModelField",
},
{
name: "Created at",
apiIdentifier: "createdAt",
fieldType: "DateTime",
__typename: "GadgetModelField",
},
{
name: "Updated at",
apiIdentifier: "updatedAt",
fieldType: "DateTime",
__typename: "GadgetModelField",
},
],
__typename: "GadgetModel",
},
inverseField: {
apiIdentifier: "siblingModelHmtField",
__typename: "GadgetModelField",
},
joinModel: {
key: "tJDsf_FvYqsi",
apiIdentifier: "joinerModel",
namespace: ["hasManyThrough"],
defaultDisplayField: {
name: "Id",
apiIdentifier: "id",
fieldType: "ID",
__typename: "GadgetModelField",
},
__typename: "GadgetModel",
},
inverseJoinModelField: {
apiIdentifier: "joinerBelongsToBase",
__typename: "GadgetModelField",
},
inverseRelatedModelField: {
apiIdentifier: "joinerBelongsToSibling",
__typename: "GadgetModelField",
},
},
},
],
__typename: "GadgetModel",
},
Expand Down Expand Up @@ -1246,6 +1358,27 @@ const loadMockWidgetDataForRelationship = () => {
},
],
},
baseModelHmtField: {
edges: [
{
node: {
id: "1",
siblingName: "sibling 1",
__typename: "HasManyThroughSiblingModel",
},
__typename: "HasManyThroughSiblingModelEdge",
},
{
node: {
id: "2",
siblingName: "sibling 2",
__typename: "HasManyThroughSiblingModel",
},
__typename: "HasManyThroughSiblingModelEdge",
},
],
__typename: "HasManyThroughSiblingModelConnection",
},
hasOne: {
name: "gizmo 12",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const PolarisAutoTableCellRenderer = (props: { column: TableColumn; value
return null;
}

if (column.relationshipType === FieldType.HasMany) {
if (column.relationshipType === FieldType.HasMany || column.relationshipType === FieldType.HasManyThrough) {
return <PolarisAutoTableTagCell value={value as any} />;
}

Expand Down
1 change: 1 addition & 0 deletions packages/react/src/metadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ export const acceptedAutoTableFieldTypes = new Set([
// Relationships
FieldType.BelongsTo,
FieldType.HasMany,
FieldType.HasManyThrough,
FieldType.HasOne,
]);

Expand Down
16 changes: 8 additions & 8 deletions packages/react/src/use-table/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,15 @@ export const getTableSelectionMap = (spec: TableSpec) => {
throw new Error(errorMessages.RELATED_HAS_ONE_OR_BELONGS_TO_FIELD_NOT_EXIST);
}

if (isHasManyField(firstField)) {
if (isHasManyOrHasManyThroughField(firstField)) {
throw new Error(errorMessages.RELATED_HAS_MANY_FIELD_NOT_EXIST);
}
}
throw new Error(getFieldSelectionErrorMessage(currentColumnPath).NOT_EXIST);
}

const isHasOneOrBelongsTo = isHasOneOrBelongsToField(fieldMetadata);
const isHasMany = isHasManyField(fieldMetadata);
const isHasMany = isHasManyOrHasManyThroughField(fieldMetadata);

if (!acceptedAutoTableFieldTypes.has(fieldMetadata.fieldType)) {
throw new Error(`Field '${columnPath}' cannot be shown in the table`);
Expand Down Expand Up @@ -249,7 +249,7 @@ const getFieldInformationByColumnPath = (fieldMetadataTree: TableSpec["fieldMeta
const targetField = getFieldMetadataByColumnPath(fieldMetadataTree, columnPath);

const isHasOneOrBelongsTo = isHasOneOrBelongsToField(firstField);
const isHasMany = isHasManyField(firstField);
const isHasMany = isHasManyOrHasManyThroughField(firstField);

return {
firstPathSegment,
Expand Down Expand Up @@ -282,7 +282,7 @@ const isColumnSortable = (fieldMetadata: FieldMetadataFragment, sortable: boolea
};

const mergeColumnPathByFieldType = (columnPath: string, newSegment: string, field: { fieldType: GadgetFieldType }) => {
if (isHasManyField(field)) {
if (isHasManyOrHasManyThroughField(field)) {
return `${columnPath}.edges.node.${newSegment}`;
}

Expand All @@ -293,8 +293,8 @@ const isHasOneOrBelongsToField = (field: { fieldType: GadgetFieldType }) => {
return field.fieldType === GadgetFieldType.HasOne || field.fieldType === GadgetFieldType.BelongsTo;
};

const isHasManyField = (field: { fieldType: GadgetFieldType }) => {
return field.fieldType === GadgetFieldType.HasMany;
const isHasManyOrHasManyThroughField = (field: { fieldType: GadgetFieldType }) => {
return field.fieldType === GadgetFieldType.HasMany || field.fieldType === GadgetFieldType.HasManyThrough;
};

const richTextSelection = {
Expand Down Expand Up @@ -352,7 +352,7 @@ export const fieldMetadataArrayToFieldMetadataTree = (fieldMetadataArray: FieldM
$field: field,
...fieldMetadataArrayToFieldMetadataTree(getRelatedModelFields(field) as any[]),
};
} else if (isHasManyField(field)) {
} else if (isHasManyOrHasManyThroughField(field)) {
map[field.apiIdentifier] = {
$field: field,
edges: {
Expand Down Expand Up @@ -383,7 +383,7 @@ const getFieldMetadataByColumnPath = (fieldMetadataTree: TableSpec["fieldMetadat
};

const maybeGetRelatedModelFromRelationshipField = (field: FieldMetadataFragment) => {
if ((isHasOneOrBelongsToField(field) || isHasManyField(field)) && "configuration" in field) {
if ((isHasOneOrBelongsToField(field) || isHasManyOrHasManyThroughField(field)) && "configuration" in field) {
return (field as FieldMetadataFragmentWithRelationshipConfig).configuration.relatedModel;
}
};
Expand Down
6 changes: 5 additions & 1 deletion packages/react/src/use-table/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ export type TableSpec = {
defaultSelection: Record<string, any>;
};

export type RelationshipType = GadgetFieldType.HasMany | GadgetFieldType.HasOne | GadgetFieldType.BelongsTo;
export type RelationshipType =
| GadgetFieldType.HasMany
| GadgetFieldType.HasOne
| GadgetFieldType.BelongsTo
| GadgetFieldType.HasManyThrough;

export type TableColumn = {
/** Identifier for the column */
Expand Down

0 comments on commit 138f92f

Please sign in to comment.