-
Notifications
You must be signed in to change notification settings - Fork 8
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
base: next
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,11 @@ export const productFormFragment = gql` | |
id | ||
title | ||
} | ||
packageDimensions { | ||
width | ||
height | ||
depth | ||
} | ||
} | ||
`; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ import { | |
FinalFormSaveSplitButton, | ||
FinalFormSelect, | ||
FinalFormSubmitEvent, | ||
FormSection, | ||
Loading, | ||
MainContent, | ||
Toolbar, | ||
|
@@ -29,6 +30,7 @@ import { filter } from "graphql-anywhere"; | |
import isEqual from "lodash.isequal"; | ||
import React from "react"; | ||
import { FormattedMessage } from "react-intl"; | ||
import { MergeDeep } from "type-fest"; | ||
|
||
import { | ||
createProductMutation, | ||
|
@@ -62,10 +64,18 @@ const rootBlocks = { | |
image: DamImageBlock, | ||
}; | ||
|
||
type FormValues = Omit<GQLProductFormManualFragment, "price" | "image"> & { | ||
price: string; | ||
image: BlockState<typeof rootBlocks.image>; | ||
}; | ||
type FormValues = MergeDeep< | ||
GQLProductFormManualFragment, | ||
{ | ||
price: string; | ||
image: BlockState<typeof rootBlocks.image>; | ||
packageDimensions: { | ||
width: string; | ||
height: string; | ||
depth: string; | ||
}; | ||
} | ||
>; | ||
|
||
function ProductForm({ id }: FormProps): React.ReactElement { | ||
const stackApi = useStackApi(); | ||
|
@@ -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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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). |
||
width: String(data.product.packageDimensions.width), | ||
height: String(data.product.packageDimensions.height), | ||
depth: String(data.product.packageDimensions.depth), | ||
}, | ||
Comment on lines
+97
to
+102
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 🤷🏼♂️ There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that would be:
Major issue: you can't type For non-float numbers (as in the space block) it is no issue, as "." is not supported anyway. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
} | ||
: { | ||
image: rootBlocks.image.defaultValues(), | ||
|
@@ -113,7 +129,12 @@ function ProductForm({ id }: FormProps): React.ReactElement { | |
variants: [], | ||
articleNumbers: [], | ||
discounts: [], | ||
packageDimensions: { width: 0, height: 0, depth: 0 }, | ||
packageDimensions: { | ||
...formValues.packageDimensions, | ||
width: parseFloat(formValues.packageDimensions.width), | ||
height: parseFloat(formValues.packageDimensions.height), | ||
depth: parseFloat(formValues.packageDimensions.depth), | ||
}, | ||
statistics: { views: 0 }, | ||
}; | ||
if (mode === "edit") { | ||
|
@@ -262,6 +283,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" />}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're adding the new FieldSet in #1082 |
||
<Field | ||
fullWidth | ||
name="packageDimensions.width" | ||
component={FinalFormInput} | ||
inputProps={{ type: "number" }} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't |
||
label={<FormattedMessage id="product.width" defaultMessage="Width" />} | ||
/> | ||
<Field | ||
fullWidth | ||
name="packageDimensions.height" | ||
component={FinalFormInput} | ||
inputProps={{ type: "number" }} | ||
label={<FormattedMessage id="product.height" defaultMessage="Height" />} | ||
/> | ||
<Field | ||
fullWidth | ||
name="packageDimensions.depth" | ||
component={FinalFormInput} | ||
inputProps={{ type: "number" }} | ||
label={<FormattedMessage id="product.depth" defaultMessage="Depth" />} | ||
/> | ||
</FormSection> | ||
</MainContent> | ||
</EditPageLayout> | ||
)} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -147,9 +147,9 @@ export class Product extends BaseEntity<Product, "id"> implements DocumentInterf | |
@Field(() => ProductDimensions, { nullable: true }) | ||
dimensions?: ProductDimensions = undefined; | ||
|
||
@Embedded(() => ProductPackageDimensions, { nullable: true }) | ||
@Field(() => ProductPackageDimensions, { nullable: true }) | ||
packageDimensions?: ProductPackageDimensions = undefined; | ||
@Embedded(() => ProductPackageDimensions) | ||
@Field(() => ProductPackageDimensions) | ||
packageDimensions: ProductPackageDimensions; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I make the field non-nullable as:
|
||
|
||
@OneToOne(() => ProductStatistics, { inversedBy: "product", owner: true, ref: true, nullable: true }) | ||
@Field(() => ProductStatistics, { nullable: true }) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -69,11 +69,11 @@ export class ProductInput { | |
@Field(() => ProductDimensions, { nullable: true }) | ||
dimensions?: ProductDimensions; | ||
|
||
@IsNullable() | ||
@IsNotEmpty() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this necessary? Shouldn't ValidateNested be enough? |
||
@ValidateNested() | ||
@Type(() => ProductPackageDimensions) | ||
@Field(() => ProductPackageDimensions, { nullable: true }) | ||
packageDimensions?: ProductPackageDimensions; | ||
@Field(() => ProductPackageDimensions) | ||
packageDimensions: ProductPackageDimensions; | ||
|
||
@IsNullable() | ||
@Field(() => ProductStatisticsInput, { nullable: true }) | ||
|
There was a problem hiding this comment.
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 }
, whileMerge
results in{ foo: number }
. AndMergeDeep
does this also for nested objects.type-fest is a very popular library
There was a problem hiding this comment.
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? 🤔
There was a problem hiding this comment.
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.
(as packageDimensions currently has only those thre fields this could be simplified, but let's assume there are other eg string fields in packageDimensions)