Skip to content

Conversation

@louwie17
Copy link
Contributor

@louwie17 louwie17 commented Oct 7, 2022

All Submissions:

Changes proposed in this Pull Request:

Adds the functionality to add a product attribute to the product. This will open up a Modal for users to add products.
Note: The ability to create new attributes and attribute terms is not added yet, as it would would drastically increase the size of this PR.

Mostly addresses #34331 .

Kapture.2022-10-11.at.09.55.37.mp4

How to test the changes in this Pull Request:

  1. Load this branch, build it, and make sure you have the new-product-management-experience feature flag enabled (you can do so using the latest version of the Beta tester within the mono repo).
  2. Go to Products > Add new (MVP) and fill out some of the initial details like name and price
  3. Move down to the attributes section and select Add first attribute a Modal should popup that looks similar/same to the design: 5sAIeTRd9Yp7nSCT33BAWz-fi-5180%3A129070
  4. A initial row should be added with an empty attribute and attribute term field, the attribute term field should be disabled and hitting the remove icon should not do anything.
  5. Search for a category and select one, the attribute term field should be enabled now and you can search/select attribute terms (note that the attribute term field relies on the API for searching, it's not the smoothest at this point, but this will require separate updates to the SelectControl component, I do have a tentative PR in flight for this, but it will require some updating).
  6. You should be able to click + Add another which will add another row, you should be able to delete any of them except for the last one. So if you click + Add another you can remove the first row if you want.
  7. Once you have added items you can click Add this should add it to the product list, any empty rows should be ignored, this includes rows with a selected attribute, but no terms selected.

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 successfully run tests with your changes locally?
  • Have you created a changelog file for each project being changed, ie pnpm --filter=<project> run changelog add?

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.

@github-actions github-actions bot added focus: react admin package: @woocommerce/components issues related to @woocommerce/components package: @woocommerce/data issues related to @woocommerce/data labels Oct 7, 2022
@github-actions
Copy link
Contributor

github-actions bot commented Oct 7, 2022

Test Results Summary

Commit SHA: 3f269cf

Test 🧪Passed ✅Failed 🚨Broken 🚧Skipped ⏭️Unknown ❔Total 📊Duration ⏱️
API Tests19600201980m 53s
E2E Tests186003018913m 51s

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.

@louwie17 louwie17 force-pushed the add/34331_popover_to_select_control_dropdown branch from 043975f to 34ecb71 Compare October 7, 2022 18:30
@louwie17 louwie17 force-pushed the add/34331_add_attributes_modal branch from d45c49e to f61ed3c Compare October 11, 2022 12:29
@louwie17 louwie17 force-pushed the add/34331_popover_to_select_control_dropdown branch from 34ecb71 to 8b4c672 Compare October 11, 2022 12:43
@louwie17 louwie17 force-pushed the add/34331_add_attributes_modal branch from f61ed3c to e8844f0 Compare October 11, 2022 12:48
@github-actions github-actions bot added the plugin: woocommerce Issues related to the WooCommerce Core plugin. label Oct 11, 2022
@louwie17 louwie17 marked this pull request as ready for review October 11, 2022 13:07
@louwie17 louwie17 requested a review from a team October 11, 2022 13:07
@louwie17 louwie17 force-pushed the add/34331_popover_to_select_control_dropdown branch from 8b4c672 to 562de3a Compare October 11, 2022 15:50
@louwie17 louwie17 force-pushed the add/34331_add_attributes_modal branch 2 times, most recently from 05bf022 to 7076251 Compare October 11, 2022 18:14
@louwie17 louwie17 force-pushed the add/34331_popover_to_select_control_dropdown branch from b2dc654 to 047d8ce Compare October 12, 2022 12:25
@louwie17 louwie17 force-pushed the add/34331_add_attributes_modal branch from 7076251 to c028408 Compare October 12, 2022 12:26
@louwie17 louwie17 force-pushed the add/34331_popover_to_select_control_dropdown branch 2 times, most recently from a12b0c0 to a9440a1 Compare October 13, 2022 13:11
@louwie17 louwie17 force-pushed the add/34331_add_attributes_modal branch from c028408 to 6ad1b26 Compare October 13, 2022 13:12
@louwie17 louwie17 force-pushed the add/34331_popover_to_select_control_dropdown branch from a9440a1 to 1de8135 Compare October 17, 2022 14:19
@louwie17 louwie17 mentioned this pull request Oct 17, 2022
8 tasks
@joelclimbsthings joelclimbsthings requested review from joelclimbsthings and removed request for a team October 18, 2022 17:06
@louwie17 louwie17 force-pushed the add/34331_popover_to_select_control_dropdown branch from 8e895d4 to 6551eb0 Compare October 18, 2022 19:05
Base automatically changed from add/34331_popover_to_select_control_dropdown to trunk October 18, 2022 19:55
@louwie17 louwie17 force-pushed the add/34331_add_attributes_modal branch from 8d5ce35 to 7420a10 Compare October 18, 2022 20:22
} from '@woocommerce/components';

type AttributeTermInputFieldProps = {
value?: ProductAttributeTerm[];
Copy link
Contributor

Choose a reason for hiding this comment

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

I've noticed we're using the generic prop name value in a number of these components. Would it make sense to make it something more descriptive/explicit? If I'm working in several of them, it can become a bit confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason for doing this, is that I wanted to follow the same format as an InputField (envisioning that every field has a value prop and a onChange handler.
This way, if we go the schema route for example it will be much easier to integrate these field components within a form and we don't have to add a custom alias for each one.

I know this has mostly been something I have been adhering to, but it hasn't really been a team discussion, I could mention this in Slack if you think that is valuable, depending on what you think of this approach?

Copy link
Contributor

Choose a reason for hiding this comment

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

@louwie17 When you say "going the schema route," are you talking in terms of documentation? I'm not all too familiar with how that would look; it sounds like an alias within the component (while keeping the value prop name) would still influence the schema in this way?

That being the case, sounds good to me. Although this should definitely be something that we make the team aware of, since it would require consistency in order to be more valuable.

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 you say "going the schema route," are you talking in terms of documentation?

Mostly in terms of extensibility, but I am not sure what our plan for that is yet, so we may never go that route. It would only be if we decide that 3PD can extend the form using PHP, which in turn would require some kind of config to define a field, which makes it easier if all fields have the same inputs/outputs.

That being the case, sounds good to me. Although this should definitely be something that we make the team aware of, since it would require consistency in order to be more valuable.

True, I will throw something up in Slack in the short term.

return (
<Modal
title={ __( 'Add attributes', 'woocommerce' ) }
onRequestClose={ () => onCancel() }
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we might want a conditional confirmation dialogue here if they've made changes, since the user will lose any attributes they've plugged in on accidental clicks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah that is a good suggestion, I can definitely add this. I could add a browser alert, similar to the one we show when deleting on of the attributes?
cc'ing @jarekmorawski on 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.

I did add this btw as part of: 3f269cf
Let me know if the copy should change?
Screen Shot 2022-10-19 at 2 20 44 PM

<AttributeField { ...getInputProps( 'attributes' ) } />
</CardBody>
</Card>
<AttributeField { ...getInputProps( 'attributes' ) } />
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 passing all input props here, it would be clearer within the <AttributeField /> component if we just move the useContext hook inside. Of course you could make the case that this is a bit more flexible, so entirely optional if we see other use cases that could benefit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As mentioned in my reasoning for the value prop. My vision for these fields was similar as InputFields, where they are relatively stand alone but follow the same format as InputFields.
Moving the useContext hook inside makes it too reliant on the Form component incase we wanted to use it outside of that context, or if we decide to export it and a 3PD wants to use it outside of that context.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I thought that may be the case. I would say that it's likely that an input field would normally appear inside of a form in some manner, but I understand the value of treating this as a standard input. Sounds good 👍🏻

Copy link
Contributor

@joelclimbsthings joelclimbsthings left a comment

Choose a reason for hiding this comment

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

Thanks @louwie17 , well done! I left a couple minor comments that may be subjective, and also noticed a little strange behavior:

  • For the attribute field, if you enter a value that doesn't exist it just shows a single line (border?) for the dropdown, which likely shouldn't display at all with no results.
  • When you first enter a character on the Value drop-down, it will quickly flash the previous values before refreshing with the new (filtered) values. I believe we should have the dropdown hidden until the values have been refetched.
  • On smaller viewports the trash icon is cut off a bit.

Let me know if you need any more clarity on the above^

@@ -0,0 +1,172 @@
/**
Copy link
Contributor

Choose a reason for hiding this comment

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

A general thought on this component. It's currently accepting a value prop that is expected to be the format of an AttributeTerm as it's returned from the API, although the fetching itself is done in this component. Hence is someone wants to preselect certain values (cough edit modal ;)) then this is a bit of a catch-22.

I could potentially address this is my follow-up PR, but if you have any thoughts or want to address it here, that will definitely be a requirement for this component.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmmm this is actually a tough one from my perspective, as the ProductAttribute returns a list of terms as an array of strings by name (not slugs 😢 ), it would be a bit strange to change the entire field to only use a string array, as there could be duplicate names, and in general it's not specific enough.

An option could be to add an option to pass the product id in and retrieve all the terms by product id ( the getProductAttributeTerms api allows for this) and use that as the value instead, but then we have a slight dilemma on what the source of truth is, the value prop or the terms coming from the data store attached to the specific product id.

My proposal would be to leave this as is, and handle the retrieving of the ProductAttributeTerm outside of this field, in a similar manner as what I do now, I only set the options prop in the Modal not in the AttributeTerm field.

Retrieving all the product attributes for the edited product should be relatively easy by doing:

getProductAttributeTerms({
  atttribute_id: attributeId,
  product: productId
});

You could trigger that within the edit modal and pass those values straight into the ProductAttributeTermsInputField.
Hope that makes sense? and does my reasoning seem ok in this regard?

Copy link
Contributor

@joelclimbsthings joelclimbsthings Oct 19, 2022

Choose a reason for hiding this comment

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

the ProductAttribute returns a list of terms as an array of strings by name (not slugs cry ),

By this, do you mean the ProductAttribute API? Definitely annoying that it wouldn't include the slugs 🤔

I'm fine with this approach, although it feels mildly painful to throw another request in. That makes sense given our limitations.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

By this, do you mean the ProductAttribute API?

I actually mean the Products API, if you look at the Product - Attributes properties type here: https://woocommerce.github.io/woocommerce-rest-api-docs/#product-properties you can see it returns a list of options (an array of strings) which is a "list of available term names of the attribute.".

This would mean you did have to trigger a request anyhow to display the items correctly within the typeahead field. If names were unique this wouldn't be as much of a problem and we could probably rely on names only, but given that is not the case, we should definitely retrieve the full attribute term object using the attribute term API endpoint.

@louwie17
Copy link
Contributor Author

Thanks for the review @joelclimbsthings, I left some comments in reply to some of your comments, all three of the below bugs you found should be fixed.
In terms of the loading indicator, I decided to add the spinner as the top item, so that any items initially retrieved will still show and only get updated once the list is retrieved.

  • For the attribute field, if you enter a value that doesn't exist it just shows a single line (border?) for the dropdown, which likely shouldn't display at all with no results.
  • When you first enter a character on the Value drop-down, it will quickly flash the previous values before refreshing with the new (filtered) values. I believe we should have the dropdown hidden until the values have been refetched.
  • On smaller viewports the trash icon is cut off a bit.

Copy link
Contributor

@joelclimbsthings joelclimbsthings left a comment

Choose a reason for hiding this comment

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

Awesome @louwie17 , great work.

When you first enter a character on the Value drop-down, it will quickly flash the previous values before refreshing with the new (filtered) values. I believe we should have the dropdown hidden until the values have been refetched.

The behavior here still feels a bit odd to me. When I first type in this prompt I see:

  • The previous values
  • Then the loading spinner appears above
  • Then the list refreshes to reflect the new query

That said, I might just be being weird at this point, so I'll approve and we can revisit if need be.

@louwie17
Copy link
Contributor Author

That said, I might just be being weird at this point, so I'll approve and we can revisit if need be.

I can do a follow up for sure, as there might be some slight delay/discrepancy between the isOpen and the inputValueChange call as the initial list is there so a user can use the down arrow and see the options already.

@louwie17 louwie17 merged commit 5f2c656 into trunk Oct 19, 2022
@louwie17 louwie17 deleted the add/34331_add_attributes_modal branch October 19, 2022 19:28
@github-actions github-actions bot added this to the 7.2.0 milestone Oct 19, 2022
@github-actions
Copy link
Contributor

Hi @louwie17, thanks for merging this pull request. Please take a look at these follow-up tasks you may need to perform:

  • Add the release: add testing instructions label

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

package: @woocommerce/components issues related to @woocommerce/components package: @woocommerce/data issues related to @woocommerce/data plugin: woocommerce Issues related to the WooCommerce Core plugin.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants