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
Add a product header component to the blocks interface #37152
Conversation
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.
I think this one might need a rebase to remove the non-header related changes.
const { isPostSavingLocked } = select( 'core/editor' ); | ||
return { | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore |
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.
I think I needed these during testing since it did not recognize these properties, but might be good to add a quick comment to the above line for why we need to ignore.
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.
After rebasing this was not longer needed
// @ts-ignore | ||
isProductLocked: isPostSavingLocked(), | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore |
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.
Same comment here.
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.
After rebasing this was not longer needed
const { saveEditedEntityRecord } = useDispatch( 'core' ); | ||
|
||
function handleSave() { | ||
saveEditedEntityRecord( 'postType', 'product', product?.id ); |
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.
Optional, but wondering if we will always have the product
or need to use ?
to check its existence.
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.
Done -> 3686408
Yes, that's why this PR is still |
Test Results SummaryCommit SHA: 1d0165e
To view the full API test report, click here. To view the full E2E test report, click here. To view all test reports, visit the WooCommerce Test Reports Dashboard. |
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## trunk #37152 +/- ##
========================================
Coverage 46.7% 46.7%
Complexity 17197 17197
========================================
Files 429 429
Lines 64884 64884
========================================
+ Hits 30287 30289 +2
+ Misses 34597 34595 -2
|
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.
I am inclined to approve this as is, as it does work, although I do think we should make better use of the available editor selectors, especially around the ones that are already aware what product we are editing.
Reason for this is, is that all the selectors will be memoized, and things would be far more optimized using specialized selectors instead of prop drilling an entire product object down.
This also falls inline with all of Gutenberg's current approach, especially around these components.
header={ <Header title={ product.name } /> } | ||
header={ | ||
<Header | ||
product={ product } |
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.
I think we should prevent prop drilling the product object down and instead make use of the available data store selectors for this.
For example, you can get the active product by calling select( 'core/editor' ).getCurrentPost()
Although I did realize this is currently not working as expected as product
is being passed in as an empty object, if you add this additional condition || ! product.id
here:
if ( ! product ) { |
It should fix this.
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.
done -> b34cd1d
title={ | ||
product.name || | ||
__( 'Add new product', 'woocommerce' ) | ||
} |
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.
We can probably do something similar for title and make use of the getEditedPostAttribute
selector, the Gutenberg TemplateTitle
does something similar.
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.
When in product creation, if we use getEditedPostAttribute
to get the name
of the product to use it as the title of the header then we will have AUTO_DRAFT
which is the default value set for the name. We want the title Add new product
when creating the product right @jarekmorawski ?
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's correct. The default title is Add new product
. It changes to the product's name after the user adds it.
const { isPostSavingLocked } = select( 'core/editor' ); | ||
return { | ||
isProductLocked: isPostSavingLocked(), | ||
isSaving: isSavingEntityRecord( |
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.
Instead of using isSavingEntityRecord
with the product id you can use isCurrentPostPending
I believe, or isSavingPost
.
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.
done -> b34cd1d
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.
According to what we discussed I reverted this back to what we had previously because using functions from core/editor
are very tied to the Post structure and the saving process does not work because of that. See -> 62efff6
const { saveEditedEntityRecord } = useDispatch( 'core' ); | ||
|
||
function handleSave() { | ||
saveEditedEntityRecord( 'postType', 'product', product.id ); |
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.
The editor store also has a savePost
action which should automatically use the active one.
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.
done -> b34cd1d
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.
Looking good @mdperez86
Just left one more comment about removing the product
prop from the Header
component now.
isSaving: isSavingPost(), | ||
}; | ||
}, | ||
[ product.id ] |
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.
We can remove this now right? and in turn also remove the product
prop from this component?
I believe the core/editor
should re-trigger this useSelect
if the active post gets updated, so listening to the id
won't be necessary.
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.
Ohh! right! Thanks
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.
Done -> 193d285 Thanks. Good catch.
62efff6
to
6b40ab2
Compare
isBusy={ isSaving } | ||
disabled={ isDisabled } | ||
> | ||
{ __( 'Save', 'woocommerce' ) } |
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.
I'm seeing in the new design that this is Add
when the product has not yet been created yet (auto-draft). Okay with keeping this for a follow-up though if you prefer.
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.
header={ | ||
<Header | ||
productId={ product.id } | ||
title={ product.name } |
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.
Just a note here that when the product
is new the name
doesn't get updated.
I assume this because we are not passing in the the product from a useSelect
hook in the product-page
but we are for existing products.
I think we probably don't want to do that anyway, and only pass the product in a constant and for the rest rely on the selectors
within.
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.
LGTM 🚀
This works well as is, although we should create follow up issues for the redirection of creating a brand new product and fixing the issue around state updates between existing and new products.
const { saveEditedEntityRecord } = useDispatch( 'core' ); | ||
|
||
function handleSave() { | ||
saveEditedEntityRecord( 'postType', 'product', productId ); |
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.
We should also re-direct to the new product form when saving a new post for the first time.
But I do realize that might of been out of scope for this issue.
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.
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.
This looks good on my end. One possible change going forward, but don't want it to block this PR.
I'll let @louwie17 give the final approval on this.
const isProductNameDirty = product.name !== productName; | ||
const isCreating = productName === AUTO_DRAFT_NAME; | ||
|
||
let title = ''; |
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.
Not a necessity in this PR since this is already pushing the original scope, but nice to have this as a util with some tests like we did before - https://github.com/woocommerce/woocommerce/blob/trunk/packages/js/product-editor/src/utils/get-product-title.ts#L16
But we can handle this in a follow-up.
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.
And what is the name you suggest here for that util function? We already have get-product-title
. It would be great if we could reuse the same one but it has a different behavior. Do you like this one get-header-title
for the new function?
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.
done -> 1d0165e
I did not add tests to that function now because testing are not configured for the current product-editor package yet. That configuration is been managed in other PR.
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.
Just left one final suggestion 🙈
Tested very well though :)
'product', | ||
productId | ||
), | ||
product: getEditedEntityRecord( |
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.
Did you actually want to return productName
only here, instead of the entire product
.
Sorry for harping on this, but return the product would trigger a re-render on every product update, although we only care about the name
.
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.
Should I use a different function for that, because I could not find it within the EntityRecord selectors.
Or do you meant calling the const { name } = getEditedEntityRecord(...)
like that and just return the name of the product like { productName: name }
?
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.
Yeah that second option.
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.
}; | ||
|
||
export function Header( { title }: HeaderProps ) { | ||
export function Header( { productId, productName }: HeaderProps ) { |
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.
To not conflict with the useSelect, maybe we want to convert productName
to initialProductName
or something like that, or the one within the useSelect could be editedProductName
.
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.
'postType', | ||
'product', | ||
productId | ||
) as Product, |
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.
Nice find, getEntityRecord
does make more sense here 🎉
… the default title value
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.
LGTM 🚀 Nice work @mdperez86, and thanks for addressing all the changes and the back and forth. This is good to merge 🚀
All Submissions:
Changes proposed in this Pull Request:
Closes #37005
How to test the changes in this Pull Request:
/wp-admin/admin.php?page=wc-admin&path=/add-product
a save button should be shown in the top-right corner of the screen.Other information:
pnpm --filter=<project> changelog add
?FOR PR REVIEWER ONLY: