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

RFC: Demo Admin Products Form: add nested object as fields #1393

Draft
wants to merge 1 commit into
base: next
Choose a base branch
from

Conversation

nsams
Copy link
Member

@nsams nsams commented Nov 13, 2023

Attention: This will be generated by crud generator, review carefully

When the GraphQL API provides nested objects, how can we handle them best in our forms?

packageDimensions?: ProductPackageDimensions = undefined;
@Embedded(() => ProductPackageDimensions)
@Field(() => ProductPackageDimensions)
packageDimensions: ProductPackageDimensions;
Copy link
Member Author

Choose a reason for hiding this comment

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

I make the field non-nullable as:

  • it is broken currently, as the database fields (of the embedded) are non-nullable
  • it simplifies the form; I'd discuss the simpler one first

@@ -262,6 +277,29 @@ function ProductForm({ id }: FormProps): React.ReactElement {
<Field name="image" isEqual={isEqual}>
{createFinalFormBlock(rootBlocks.image)}
</Field>
<FormSection title={<FormattedMessage id="product.packageDimensions" defaultMessage="Package Dimensions" />}>
Copy link
Member Author

Choose a reason for hiding this comment

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

maybe that should be a Fieldset, but that is not the important thing here

Copy link
Collaborator

Choose a reason for hiding this comment

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

We're adding the new FieldSet in #1082

depth: string;
};
}
>;
Copy link
Member Author

Choose a reason for hiding this comment

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

That is very special, uses MergeDeep from a new "type-fest" dependency. Problem is that { foo: string } & { foo: number } results in { foo: string | number }, while Merge results in { foo: number }. And MergeDeep does this also for nested objects.

type-fest is a very popular library

Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we "need"? Shouldn't this be also achievable using the (now removed) Omit statement? 🤔

Copy link
Member Author

Choose a reason for hiding this comment

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

Manual Omit is possible, but very verbose and complicated for nested objects. That's why I was looking for an alternative.

Omit<GQLProductFormManualFragment, "price" | "image" | "packageDimensions"> & {
    price: string;
    image: BlockState<typeof rootBlocks.image>;
    packageDimensions: Omit<GQLProductFormManualFragment["packageDimensions"], "width" | "height" | "depth"> & {
        width: string;
        height: string;
        depth: string;
    }
}

(as packageDimensions currently has only those thre fields this could be simplified, but let's assume there are other eg string fields in packageDimensions)

@@ -69,11 +69,11 @@ export class ProductInput {
@Field(() => ProductDimensions, { nullable: true })
dimensions?: ProductDimensions;

@IsNullable()
@IsNotEmpty()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this necessary? Shouldn't ValidateNested be enough?

@@ -262,6 +277,29 @@ function ProductForm({ id }: FormProps): React.ReactElement {
<Field name="image" isEqual={isEqual}>
{createFinalFormBlock(rootBlocks.image)}
</Field>
<FormSection title={<FormattedMessage id="product.packageDimensions" defaultMessage="Package Dimensions" />}>
Copy link
Collaborator

Choose a reason for hiding this comment

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

We're adding the new FieldSet in #1082

Comment on lines +97 to +102
packageDimensions: {
...data.product.packageDimensions,
width: String(data.product.packageDimensions.width),
height: String(data.product.packageDimensions.height),
depth: String(data.product.packageDimensions.depth),
},
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of stringifying/parsing we could also parse on field level, like we did for SpaceBlock. I don't know what I prefer though 🤷🏼‍♂️

Copy link
Member Author

Choose a reason for hiding this comment

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

I will test that out. Sounds good, even the type modification could be avoided with that.

Copy link
Member Author

@nsams nsams Nov 16, 2023

Choose a reason for hiding this comment

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

that would be:

                            parse={(v: number) => String(v)}
                            format={(v: string) => parseFloat(v)}
  • form state saves value as float (as the input and output api expect it)
  • dom input field value is a string (that's how it is)

Major issue: you can't type 1. (and continue with number after the ".")

For non-float numbers (as in the space block) it is no issue, as "." is not supported anyway.

Copy link
Member Author

Choose a reason for hiding this comment

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

See also Mr. final-form himself response here: https://stackoverflow.com/a/55121050

depth: string;
};
}
>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we "need"? Shouldn't this be also achievable using the (now removed) Omit statement? 🤔

fullWidth
name="packageDimensions.width"
component={FinalFormInput}
inputProps={{ type: "number" }}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Doesn't type="number" work as well?

@@ -84,6 +94,12 @@ function ProductForm({ id }: FormProps): React.ReactElement {
...filter<GQLProductFormManualFragment>(productFormFragment, data.product),
price: String(data.product.price),
image: rootBlocks.image.input2State(data.product.image),
packageDimensions: {
...data.product.packageDimensions,
Copy link
Collaborator

Choose a reason for hiding this comment

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

This spread is unnecessary as there's nothing in the dimensions except those three fields. But I believe you want to highlight the required transformations (e.g., a string would be perfectly fine in the spread).

@nsams nsams self-assigned this Nov 28, 2023
@nsams nsams marked this pull request as draft December 6, 2023 18:56
@johnnyomair johnnyomair mentioned this pull request Feb 20, 2024
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants