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

Add a product header component to the blocks interface #37152

Merged
merged 7 commits into from Mar 17, 2023
Merged

Conversation

mdperez86
Copy link
Contributor

@mdperez86 mdperez86 commented Mar 9, 2023

All Submissions:

Changes proposed in this Pull Request:

Closes #37005

How to test the changes in this Pull Request:

  1. Go to /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:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your changes, as applicable?
  • Have you created a changelog file for each project being changed, ie pnpm --filter=<project> changelog add?
  • Have you included testing instructions?

FOR PR REVIEWER ONLY:

  • I have reviewed that everything is sanitized/escaped appropriately for any SQL or XSS injection possibilities. I made sure Linting is not ignored or disabled.

@mdperez86 mdperez86 requested a review from a team March 9, 2023 19:30
@mdperez86 mdperez86 self-assigned this Mar 9, 2023
@github-actions github-actions bot added focus: react admin [team:Ghidorah] plugin: woocommerce Issues related to the WooCommerce Core plugin. labels Mar 9, 2023
Copy link
Contributor

@joshuatf joshuatf left a 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
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment here.

Copy link
Contributor Author

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 );
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done -> 3686408

@mdperez86
Copy link
Contributor Author

I think this one might need a rebase to remove the non-header related changes.

Yes, that's why this PR is still In Progress status. This depends on #37132.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 13, 2023

Test Results Summary

Commit SHA: 1d0165e

Test 🧪Passed ✅Failed 🚨Broken 🚧Skipped ⏭️Unknown ❔Total 📊Duration ⏱️
API Tests25900202610m 47s
E2E Tests1910010020113m 58s

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.

@github-actions github-actions bot removed the plugin: woocommerce Issues related to the WooCommerce Core plugin. label Mar 13, 2023
@codecov
Copy link

codecov bot commented Mar 13, 2023

Codecov Report

Merging #37152 (1d0165e) into trunk (cbafbd5) will increase coverage by 0.0%.
The diff coverage is 66.7%.

Additional details and impacted files

Impacted file tree graph

@@           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     
Impacted Files Coverage Δ
plugins/woocommerce/includes/class-wc-install.php 69.0% <66.7%> (ø)

... and 1 file with indirect coverage changes

@github-actions github-actions bot added the plugin: woocommerce Issues related to the WooCommerce Core plugin. label Mar 13, 2023
@mdperez86 mdperez86 requested a review from joshuatf March 14, 2023 13:46
Copy link
Contributor

@louwie17 louwie17 left a 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 }
Copy link
Contributor

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:


It should fix this.

Copy link
Contributor Author

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' )
}
Copy link
Contributor

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.

Copy link
Contributor Author

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 ?

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(
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done -> b34cd1d

Copy link
Contributor Author

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 );
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done -> b34cd1d

Copy link
Contributor

@louwie17 louwie17 left a 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 ]
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ohh! right! Thanks

Copy link
Contributor Author

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.

isBusy={ isSaving }
disabled={ isDisabled }
>
{ __( 'Save', 'woocommerce' ) }
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done here -> d28c40b Thanks @joshuatf

header={
<Header
productId={ product.id }
title={ product.name }
Copy link
Contributor

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.

louwie17
louwie17 previously approved these changes Mar 16, 2023
Copy link
Contributor

@louwie17 louwie17 left a 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 );
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

see -> 64df88c
see -> 0665be4

Copy link
Contributor

@joshuatf joshuatf left a 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 = '';
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor Author

@mdperez86 mdperez86 Mar 17, 2023

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.

Copy link
Contributor

@louwie17 louwie17 left a 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(
Copy link
Contributor

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.

Copy link
Contributor Author

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 }?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah that second option.

Copy link
Contributor Author

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 ) {
Copy link
Contributor

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.

Copy link
Contributor Author

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,
Copy link
Contributor

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 🎉

Copy link
Contributor

@louwie17 louwie17 left a 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 🚀

@mdperez86 mdperez86 merged commit e370f25 into trunk Mar 17, 2023
20 checks passed
@mdperez86 mdperez86 deleted the add/37005 branch March 17, 2023 16:21
@github-actions github-actions bot added this to the 7.6.0 milestone Mar 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
plugin: woocommerce Issues related to the WooCommerce Core plugin.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add a product header component to the blocks interface
4 participants