Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

richer has many support in auto form #664

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
53 changes: 53 additions & 0 deletions packages/react/spec/auto/form/PolarisAutoRelatedForm.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { AppProvider, Card, Page } from "@shopify/polaris";
import translations from "@shopify/polaris/locales/en.json";
import React from "react";
import { FormProvider, useForm } from "react-hook-form";
import { Provider } from "../../../src/GadgetProvider.tsx";
import { PolarisAutoForm } from "../../../src/auto/polaris/PolarisAutoForm.tsx";
import { PolarisAutoInput } from "../../../src/auto/polaris/inputs/PolarisAutoInput.tsx";
import { PolarisAutoHasManyInput } from "../../../src/auto/polaris/inputs/relationships/PolarisAutoHasManyInput.tsx";
import { PolarisAutoRelatedForm } from "../../../src/auto/polaris/inputs/relationships/PolarisAutoRelatedForm.tsx";
import { PolarisAutoSubmit } from "../../../src/auto/polaris/submit/PolarisAutoSubmit.tsx";
import { testApi as api } from "../../apis.ts";

const ExampleAutoRelatedForm = (props) => {
return (
<PolarisAutoForm {...props}>
<PolarisAutoInput field="name" />
<PolarisAutoHasManyInput field="widgets" />
<PolarisAutoRelatedForm field="widgets">
<PolarisAutoInput field="name" />
</PolarisAutoRelatedForm>
<PolarisAutoSubmit />
</PolarisAutoForm>
);
};

export default {
title: "Polaris/AutoForm/AutoRelatedForm",
component: ExampleAutoRelatedForm,
decorators: [
(Story) => {
return (
<Provider api={api}>
<AppProvider i18n={translations}>
<FormProvider {...useForm()}>
<Page>
<Card>
<Story />
</Card>
</Page>
</FormProvider>
</AppProvider>
</Provider>
);
},
],
};

export const Primary = {
args: {
action: api.section.update,
findBy: "1",
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useController } from "react-hook-form";
import { useAutoFormMetadata } from "../AutoFormContext.js";
import type { AutoRelationshipInputProps } from "../interfaces/AutoRelationshipInputProps.js";
import { useFieldMetadata } from "./useFieldMetadata.js";
import { useRelatedModelOptions } from "./useRelatedModelOptions.js";
import { useRelatedModelOptions } from "./useRelatedModel.js";

export const useBelongsToInputController = (props: AutoRelationshipInputProps) => {
const { field, control } = props;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { GadgetHasManyConfig } from "../../internal/gql/graphql.js";
import { uniq } from "../../utils.js";
import type { AutoRelationshipInputProps } from "../interfaces/AutoRelationshipInputProps.js";
import { useFieldMetadata } from "./useFieldMetadata.js";
import { useRelatedModelOptions } from "./useRelatedModelOptions.js";
import { useRelatedModelOptions } from "./useRelatedModel.js";

export const useHasManyInputController = (props: AutoRelationshipInputProps) => {
const { field } = props;
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/auto/hooks/useHasOneInputController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { GadgetHasOneConfig } from "../../internal/gql/graphql.js";
import { uniq } from "../../utils.js";
import type { AutoRelationshipInputProps } from "../interfaces/AutoRelationshipInputProps.js";
import { useFieldMetadata } from "./useFieldMetadata.js";
import { useRelatedModelOptions } from "./useRelatedModelOptions.js";
import { useRelatedModelOptions } from "./useRelatedModel.js";

export const useHasOneInputController = (props: AutoRelationshipInputProps) => {
const { field } = props;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ import { useModelManager } from "./useModelManager.js";
export const optionRecordsToLoadCount = 25;
export const selectedRecordsToLoadCount = 25;

export const useRelatedModelOptions = (props: {
field: string; // Field API identifier
optionLabel?: OptionLabel; // The label to display for each related model record
}) => {
export const useRelatedModelRecords = (props: { field: string }) => {
const { field } = props;
const { metadata } = useFieldMetadata(field);
const { findBy, model } = useAutoFormMetadata();
Expand All @@ -30,11 +27,6 @@ export const useRelatedModelOptions = (props: {
const relatedModelInverseFieldApiId =
"inverseField" in relationshipFieldConfig ? relationshipFieldConfig.inverseField?.apiIdentifier : undefined;

const optionLabel = assert(
props.optionLabel ?? relationshipFieldConfig.relatedModel?.defaultDisplayField.apiIdentifier,
"Option label is required for relationships"
);

const { selected } = isBelongsToField
? // eslint-disable-next-line
useLinkedChildModelRelatedModelRecords({
Expand All @@ -54,8 +46,30 @@ export const useRelatedModelOptions = (props: {

const relatedModelRecords = useAllRelatedModelRecords({
relatedModel: { apiIdentifier: relatedModelApiIdentifier!, namespace: relatedModelNamespace },
optionLabel,
filter: isBelongsToField ? undefined : { [relatedModelInverseFieldApiId + "Id"]: { isSet: false } },
});

return {
selected,
relatedModelRecords,
};
};

export const useRelatedModelOptions = (props: {
field: string; // Field API identifier
optionLabel?: OptionLabel; // The label to display for each related model record
}) => {
const { field } = props;
const { metadata } = useFieldMetadata(field);

const relationshipFieldConfig = metadata.configuration as RelationshipFieldConfig;

const optionLabel = assert(
props.optionLabel ?? relationshipFieldConfig.relatedModel?.defaultDisplayField.apiIdentifier,
"Option label is required for relationships"
);
const { selected, relatedModelRecords } = useRelatedModelRecords(props);

const { relatedModel, pagination, search } = relatedModelRecords;

const getOptions = () => {
Expand Down Expand Up @@ -120,7 +134,7 @@ export const getRecordsAsOptions = (records: Record<string, any>[], optionLabel:
*
* The lookup is done using the `findBy` to lookup on the current model to retrieve the related model record data
*/
export const useLinkedChildModelRelatedModelRecords = (props: {
const useLinkedChildModelRelatedModelRecords = (props: {
belongsToFieldApiId: string;
currentRecordId?: string;
currentModel: { apiIdentifier: string; namespace?: string[] | string | null };
Expand Down Expand Up @@ -154,7 +168,7 @@ export const useLinkedChildModelRelatedModelRecords = (props: {
/**
* For getting the related child model records in a HasOne/HasMany relationship
*/
export const useLinkedParentModelRelatedModelRecords = (props: {
const useLinkedParentModelRelatedModelRecords = (props: {
relatedModel: {
apiIdentifier: string;
namespace?: string[] | string | null;
Expand All @@ -177,7 +191,7 @@ export const useLinkedParentModelRelatedModelRecords = (props: {
pause: !currentRecordId, // HasOne/HasMany need the current record to query the inverse field in the related model

first: selectedRecordsToLoadCount, // Many records can point to the current record in hasOne/hasMany
filter: { [inverseFieldApiIdentifier]: { equals: currentRecordId } }, // Filter by the inverse field belongsTo field value
filter: { [inverseFieldApiIdentifier + "Id"]: { equals: currentRecordId } }, // Filter by the inverse field belongsTo field value
});

return {
Expand All @@ -189,8 +203,9 @@ export const useLinkedParentModelRelatedModelRecords = (props: {
};
};

export const useAllRelatedModelRecords = (props: {
const useAllRelatedModelRecords = (props: {
optionLabel?: OptionLabel;
filter?: Record<string, any>;
relatedModel: { apiIdentifier: string; namespace?: string[] | string | null };
}) => {
const { optionLabel, relatedModel } = props;
Expand All @@ -204,6 +219,7 @@ export const useAllRelatedModelRecords = (props: {

const [{ data: newlyFetchedRecords, fetching, error }, _refetch] = useFindMany(relatedModelManager as any, {
first: optionRecordsToLoadCount,
...(props.filter && { filter: props.filter }),
...(paginationPage && { after: paginationPage }),
...(searchValue && { search: searchValue }),
...(optionLabelIsFieldName && { select: { id: true, [optionLabel]: true } }),
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/auto/polaris/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ export {
} from "./inputs/PolarisAutoTextInput.js";
export { PolarisAutoBelongsToInput as AutoBelongsToInput } from "./inputs/relationships/PolarisAutoBelongsToInput.js";
export { PolarisAutoHasManyInput as AutoHasManyInput } from "./inputs/relationships/PolarisAutoHasManyInput.js";
export { PolarisAutoRelatedForm as AutoRelatedForm } from "./inputs/relationships/PolarisAutoRelatedForm.js";
export { PolarisAutoSubmit as AutoSubmit } from "./submit/PolarisAutoSubmit.js";
export { PolarisSubmitResultBanner as SubmitResultBanner } from "./submit/PolarisSubmitResultBanner.js";
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Combobox, Tag } from "@shopify/polaris";
import React from "react";
import { useBelongsToInputController } from "../../../hooks/useBelongsToInputController.js";
import { optionRecordsToLoadCount } from "../../../hooks/useRelatedModelOptions.js";
import { optionRecordsToLoadCount } from "../../../hooks/useRelatedModel.js";
import type { AutoRelationshipInputProps } from "../../../interfaces/AutoRelationshipInputProps.js";
import { RelatedModelOptions } from "./RelatedModelOptions.js";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { GadgetRecordList } from "@gadgetinc/api-client-core";
import { Banner, Combobox } from "@shopify/polaris";
import React from "react";
import { useHasManyInputController } from "../../../hooks/useHasManyInputController.js";
import { optionRecordsToLoadCount } from "../../../hooks/useRelatedModelOptions.js";
import { optionRecordsToLoadCount } from "../../../hooks/useRelatedModel.js";
import type { AutoRelationshipInputProps } from "../../../interfaces/AutoRelationshipInputProps.js";
import { RelatedModelOptions } from "./RelatedModelOptions.js";
import { getSelectedRelatedRecordTags } from "./SelectedRelatedRecordTags.js";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Banner, Combobox } from "@shopify/polaris";
import React from "react";
import { useHasOneInputController } from "../../../hooks/useHasOneInputController.js";
import { optionRecordsToLoadCount } from "../../../hooks/useRelatedModelOptions.js";
import { optionRecordsToLoadCount } from "../../../hooks/useRelatedModel.js";
import type { AutoRelationshipInputProps } from "../../../interfaces/AutoRelationshipInputProps.js";
import { RelatedModelOptions } from "./RelatedModelOptions.js";
import { getSelectedRelatedRecordTags } from "./SelectedRelatedRecordTags.js";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from "react";
import { useFormContext } from "react-hook-form";
import { useFieldMetadata } from "../../../hooks/useFieldMetadata.js";
import { useRelatedModelRecords } from "../../../hooks/useRelatedModel.js";

export const PolarisAutoRelatedForm = (props: { field: string; children: React.ReactNode }) => {
const { field } = props;
const { getValues } = useFormContext();
const { metadata, path } = useFieldMetadata(field);
const { selected, relatedModelRecords } = useRelatedModelRecords(props);

const values = getValues(path);

console.log({ values, path, field, selected, relatedModelRecords, metadata }, "foo foo fas");

return <div>hi</div>;
};
2 changes: 2 additions & 0 deletions packages/react/src/use-action-form/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,8 @@ const getParentRelationshipFieldGraphqlApiInput = (props: { input: any; result:
const { input, result } = props;
const { __typename, ...rest } = result;

console.log({ input, result });

if ("__id" in rest) {
if ("__unlinkedInverseField" in rest && rest.__unlinkedInverseField) {
const inverseFieldApiId = rest.__unlinkedInverseField;
Expand Down
Loading