Skip to content

Commit c3139a5

Browse files
Merge pull request #652 from gadget-inc/mill/RMN-176_AutoTableHasManyThrough
Added HasManyThrough field support to AutoTable
2 parents b30a841 + 138f92f commit c3139a5

File tree

7 files changed

+170
-14
lines changed

7 files changed

+170
-14
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@gadgetinc/react": patch
3+
---
4+
5+
Added `HasManyThrough` field support to AutoTable column selection

packages/react/spec/auto/PolarisAutoTable.stories.jsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,19 @@ export const HideSearchAndPagination = {
335335
},
336336
};
337337

338+
export const HasManyThroughFields = {
339+
args: {
340+
model: api.hasManyThrough.baseModel,
341+
columns: [
342+
"baseModelHmtField",
343+
{
344+
header: "Sibling ID",
345+
field: "baseModelHmtField.edges.node.id",
346+
},
347+
],
348+
},
349+
};
350+
338351
const windowAlert = (message) => {
339352
// eslint-disable-next-line no-undef
340353
window.alert(message);

packages/react/spec/auto/hooks/useTable.spec.tsx

Lines changed: 137 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ describe("useTable hook", () => {
327327

328328
it("should use default display field if the column string is a relationship field", async () => {
329329
const result = getUseTableResult({
330-
columns: ["name", "hasMany", "hasOne", "belongsTo"],
330+
columns: ["name", "hasMany", "hasOne", "belongsTo", "baseModelHmtField"],
331331
});
332332
loadMockWidgetModelMetadataForRelationship();
333333
loadMockWidgetDataForRelationship();
@@ -362,6 +362,14 @@ describe("useTable hook", () => {
362362
id
363363
email
364364
}
365+
baseModelHmtField {
366+
edges {
367+
node {
368+
id
369+
siblingName
370+
}
371+
}
372+
}
365373
__typename
366374
}
367375
}
@@ -372,10 +380,20 @@ describe("useTable hook", () => {
372380
}
373381
}"
374382
`);
375-
expect(result.current[0].columns?.map((column) => column.field)).toEqual(["name", "hasMany", "hasOne", "belongsTo"]);
383+
expect(result.current[0].columns?.map((column) => column.field)).toEqual([
384+
"name",
385+
"hasMany",
386+
"hasOne",
387+
"belongsTo",
388+
"baseModelHmtField",
389+
]);
376390
expect(result.current[0].rows).toMatchInlineSnapshot(`
377391
[
378392
{
393+
"baseModelHmtField": [
394+
"sibling 1",
395+
"sibling 2",
396+
],
379397
"belongsTo": "foo",
380398
"hasMany": [
381399
"gizmo 9",
@@ -392,7 +410,7 @@ describe("useTable hook", () => {
392410

393411
it("should return the related model field if the column values are relationship fields", async () => {
394412
const result = getUseTableResult({
395-
columns: ["name", "hasMany.edges.node.name", "hasOne.name", "belongsTo.str"],
413+
columns: ["name", "hasMany.edges.node.name", "hasOne.name", "belongsTo.str", "baseModelHmtField.edges.node.id"],
396414
});
397415
loadMockWidgetModelMetadataForRelationship();
398416
loadMockWidgetDataForRelationship();
@@ -428,6 +446,13 @@ describe("useTable hook", () => {
428446
id
429447
str
430448
}
449+
baseModelHmtField {
450+
edges {
451+
node {
452+
id
453+
}
454+
}
455+
}
431456
__typename
432457
}
433458
}
@@ -443,10 +468,15 @@ describe("useTable hook", () => {
443468
"hasMany.edges.node.name",
444469
"hasOne.name",
445470
"belongsTo.str",
471+
"baseModelHmtField.edges.node.id",
446472
]);
447473
expect(result.current[0].rows).toMatchInlineSnapshot(`
448474
[
449475
{
476+
"baseModelHmtField.edges.node.id": [
477+
"1",
478+
"2",
479+
],
450480
"belongsTo.str": "foo",
451481
"hasMany.edges.node.name": [
452482
"gizmo 9",
@@ -461,7 +491,7 @@ describe("useTable hook", () => {
461491
`);
462492
});
463493

464-
it.each(["password", "hasManyThrough"])("should throw an error if the selected field type is %s", async (fieldType) => {
494+
it.each(["password"])("should throw an error if the selected field type is %s", async (fieldType) => {
465495
let error: Error | undefined;
466496
try {
467497
getUseTableResult({
@@ -1206,6 +1236,88 @@ const loadMockWidgetModelMetadataForRelationship = () => {
12061236
},
12071237
},
12081238
},
1239+
{
1240+
name: "Base model hmt field",
1241+
apiIdentifier: "baseModelHmtField",
1242+
fieldType: "HasManyThrough",
1243+
requiredArgumentForInput: false,
1244+
sortable: false,
1245+
filterable: false,
1246+
__typename: "GadgetModelField",
1247+
configuration: {
1248+
__typename: "GadgetHasManyThroughConfig",
1249+
fieldType: "HasManyThrough",
1250+
validations: [],
1251+
relatedModel: {
1252+
key: "Oss4sCDW-DJU",
1253+
apiIdentifier: "siblingModel",
1254+
namespace: ["hasManyThrough"],
1255+
defaultDisplayField: {
1256+
name: "Sibling name",
1257+
apiIdentifier: "siblingName",
1258+
fieldType: "String",
1259+
__typename: "GadgetModelField",
1260+
},
1261+
fields: [
1262+
{
1263+
name: "Id",
1264+
apiIdentifier: "id",
1265+
fieldType: "ID",
1266+
__typename: "GadgetModelField",
1267+
},
1268+
{
1269+
name: "Sibling name",
1270+
apiIdentifier: "siblingName",
1271+
fieldType: "String",
1272+
__typename: "GadgetModelField",
1273+
},
1274+
{
1275+
name: "Sibling model hmt field",
1276+
apiIdentifier: "siblingModelHmtField",
1277+
fieldType: "HasManyThrough",
1278+
__typename: "GadgetModelField",
1279+
},
1280+
{
1281+
name: "Created at",
1282+
apiIdentifier: "createdAt",
1283+
fieldType: "DateTime",
1284+
__typename: "GadgetModelField",
1285+
},
1286+
{
1287+
name: "Updated at",
1288+
apiIdentifier: "updatedAt",
1289+
fieldType: "DateTime",
1290+
__typename: "GadgetModelField",
1291+
},
1292+
],
1293+
__typename: "GadgetModel",
1294+
},
1295+
inverseField: {
1296+
apiIdentifier: "siblingModelHmtField",
1297+
__typename: "GadgetModelField",
1298+
},
1299+
joinModel: {
1300+
key: "tJDsf_FvYqsi",
1301+
apiIdentifier: "joinerModel",
1302+
namespace: ["hasManyThrough"],
1303+
defaultDisplayField: {
1304+
name: "Id",
1305+
apiIdentifier: "id",
1306+
fieldType: "ID",
1307+
__typename: "GadgetModelField",
1308+
},
1309+
__typename: "GadgetModel",
1310+
},
1311+
inverseJoinModelField: {
1312+
apiIdentifier: "joinerBelongsToBase",
1313+
__typename: "GadgetModelField",
1314+
},
1315+
inverseRelatedModelField: {
1316+
apiIdentifier: "joinerBelongsToSibling",
1317+
__typename: "GadgetModelField",
1318+
},
1319+
},
1320+
},
12091321
],
12101322
__typename: "GadgetModel",
12111323
},
@@ -1246,6 +1358,27 @@ const loadMockWidgetDataForRelationship = () => {
12461358
},
12471359
],
12481360
},
1361+
baseModelHmtField: {
1362+
edges: [
1363+
{
1364+
node: {
1365+
id: "1",
1366+
siblingName: "sibling 1",
1367+
__typename: "HasManyThroughSiblingModel",
1368+
},
1369+
__typename: "HasManyThroughSiblingModelEdge",
1370+
},
1371+
{
1372+
node: {
1373+
id: "2",
1374+
siblingName: "sibling 2",
1375+
__typename: "HasManyThroughSiblingModel",
1376+
},
1377+
__typename: "HasManyThroughSiblingModelEdge",
1378+
},
1379+
],
1380+
__typename: "HasManyThroughSiblingModelConnection",
1381+
},
12491382
hasOne: {
12501383
name: "gizmo 12",
12511384
},

packages/react/src/auto/polaris/tableCells/PolarisAutoTableCellRenderer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const PolarisAutoTableCellRenderer = (props: { column: TableColumn; value
1717
return null;
1818
}
1919

20-
if (column.relationshipType === FieldType.HasMany) {
20+
if (column.relationshipType === FieldType.HasMany || column.relationshipType === FieldType.HasManyThrough) {
2121
return <PolarisAutoTableTagCell value={value as any} />;
2222
}
2323

packages/react/src/metadata.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,7 @@ export const acceptedAutoTableFieldTypes = new Set([
512512
// Relationships
513513
FieldType.BelongsTo,
514514
FieldType.HasMany,
515+
FieldType.HasManyThrough,
515516
FieldType.HasOne,
516517
]);
517518

packages/react/src/use-table/helpers.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,15 @@ export const getTableSelectionMap = (spec: TableSpec) => {
8686
throw new Error(errorMessages.RELATED_HAS_ONE_OR_BELONGS_TO_FIELD_NOT_EXIST);
8787
}
8888

89-
if (isHasManyField(firstField)) {
89+
if (isHasManyOrHasManyThroughField(firstField)) {
9090
throw new Error(errorMessages.RELATED_HAS_MANY_FIELD_NOT_EXIST);
9191
}
9292
}
9393
throw new Error(getFieldSelectionErrorMessage(currentColumnPath).NOT_EXIST);
9494
}
9595

9696
const isHasOneOrBelongsTo = isHasOneOrBelongsToField(fieldMetadata);
97-
const isHasMany = isHasManyField(fieldMetadata);
97+
const isHasMany = isHasManyOrHasManyThroughField(fieldMetadata);
9898

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

251251
const isHasOneOrBelongsTo = isHasOneOrBelongsToField(firstField);
252-
const isHasMany = isHasManyField(firstField);
252+
const isHasMany = isHasManyOrHasManyThroughField(firstField);
253253

254254
return {
255255
firstPathSegment,
@@ -282,7 +282,7 @@ const isColumnSortable = (fieldMetadata: FieldMetadataFragment, sortable: boolea
282282
};
283283

284284
const mergeColumnPathByFieldType = (columnPath: string, newSegment: string, field: { fieldType: GadgetFieldType }) => {
285-
if (isHasManyField(field)) {
285+
if (isHasManyOrHasManyThroughField(field)) {
286286
return `${columnPath}.edges.node.${newSegment}`;
287287
}
288288

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

296-
const isHasManyField = (field: { fieldType: GadgetFieldType }) => {
297-
return field.fieldType === GadgetFieldType.HasMany;
296+
const isHasManyOrHasManyThroughField = (field: { fieldType: GadgetFieldType }) => {
297+
return field.fieldType === GadgetFieldType.HasMany || field.fieldType === GadgetFieldType.HasManyThrough;
298298
};
299299

300300
const richTextSelection = {
@@ -352,7 +352,7 @@ export const fieldMetadataArrayToFieldMetadataTree = (fieldMetadataArray: FieldM
352352
$field: field,
353353
...fieldMetadataArrayToFieldMetadataTree(getRelatedModelFields(field) as any[]),
354354
};
355-
} else if (isHasManyField(field)) {
355+
} else if (isHasManyOrHasManyThroughField(field)) {
356356
map[field.apiIdentifier] = {
357357
$field: field,
358358
edges: {
@@ -383,7 +383,7 @@ const getFieldMetadataByColumnPath = (fieldMetadataTree: TableSpec["fieldMetadat
383383
};
384384

385385
const maybeGetRelatedModelFromRelationshipField = (field: FieldMetadataFragment) => {
386-
if ((isHasOneOrBelongsToField(field) || isHasManyField(field)) && "configuration" in field) {
386+
if ((isHasOneOrBelongsToField(field) || isHasManyOrHasManyThroughField(field)) && "configuration" in field) {
387387
return (field as FieldMetadataFragmentWithRelationshipConfig).configuration.relatedModel;
388388
}
389389
};

packages/react/src/use-table/types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ export type TableSpec = {
1919
defaultSelection: Record<string, any>;
2020
};
2121

22-
export type RelationshipType = GadgetFieldType.HasMany | GadgetFieldType.HasOne | GadgetFieldType.BelongsTo;
22+
export type RelationshipType =
23+
| GadgetFieldType.HasMany
24+
| GadgetFieldType.HasOne
25+
| GadgetFieldType.BelongsTo
26+
| GadgetFieldType.HasManyThrough;
2327

2428
export type TableColumn = {
2529
/** Identifier for the column */

0 commit comments

Comments
 (0)