Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Switch from Select to Combobox for Country and State Inputs #4369

Merged
merged 15 commits into from Aug 12, 2021

Conversation

mikejolley
Copy link
Member

@mikejolley mikejolley commented Jun 18, 2021

This PR implements a new Combobox control which essentially allows text input to search a list of values. This is useful for country and state fields and is similar to what Woo Core has on the core checkout.

The control being used is the ComboBox Control from Gutenberg.

Fixes #1752

Looking for feedback and ideas to further polish. To get the label working I needed to simulate onBlur. Autofill was also challenging. I ended up making it search when the input changes which causes an immediate update so I am not 100% sure this is a good solution.

Accessibility

Tested Keyboard controls.

Screenshots

Screenshot 2021-06-18 at 13 50 00
Screenshot 2021-06-18 at 13 49 33
Screenshot 2021-06-18 at 13 48 53

How to test the changes in this Pull Request:

  1. Try searching for values in the country input. Value should persist on change.
  2. Repeat for state input (for countries with states)
  3. Test autofill populates the field
  4. Test on a mobile browser (I used mobile view on Edge)
  5. Test on a variety of themes

I went through all the TwentyX themes and Storefront.

Changelog

Checkout: Switch from select element to combobox for country and state inputs so contents are searchable

@mikejolley mikejolley requested a review from Aljullu June 18, 2021 12:57
@mikejolley mikejolley self-assigned this Jun 18, 2021
@mikejolley mikejolley requested a review from a team as a code owner June 18, 2021 12:57
@github-actions
Copy link
Contributor

github-actions bot commented Jun 18, 2021

Size Change: +28.9 kB (+3%)

Total Size: 1.13 MB

Filename Size Change
build/active-filters-frontend.js 8.25 kB -3 B (0%)
build/active-filters.js 7.83 kB +7 B (0%)
build/all-products-frontend.js 23.1 kB +117 B (+1%)
build/all-products.js 37 kB -41 B (0%)
build/all-reviews.js 9.6 kB +24 B (0%)
build/atomic-block-components/add-to-cart--atomic-block-components/button--atomic-block-components/image---a7e2bb9b.js 2.56 kB +2 B (0%)
build/atomic-block-components/add-to-cart--atomic-block-components/button.js 1.81 kB -3 B (0%)
build/atomic-block-components/add-to-cart-frontend.js 8.39 kB +6 B (0%)
build/atomic-block-components/add-to-cart.js 7.73 kB +12 B (0%)
build/atomic-block-components/button-frontend.js 1.74 kB -3 B (0%)
build/atomic-block-components/button.js 875 B -1 B (0%)
build/atomic-block-components/category-list-frontend.js 468 B -2 B (0%)
build/atomic-block-components/image-frontend.js 1.88 kB -2 B (0%)
build/atomic-block-components/image.js 1.34 kB -2 B (0%)
build/atomic-block-components/price-frontend.js 2.1 kB +1 B (0%)
build/atomic-block-components/price.js 2.11 kB -1 B (0%)
build/atomic-block-components/rating.js 566 B -1 B (0%)
build/atomic-block-components/sale-badge-frontend.js 859 B -1 B (0%)
build/atomic-block-components/sale-badge.js 867 B -1 B (0%)
build/atomic-block-components/sku-frontend.js 388 B -1 B (0%)
build/atomic-block-components/stock-indicator-frontend.js 610 B -2 B (0%)
build/atomic-block-components/stock-indicator.js 611 B -1 B (0%)
build/atomic-block-components/summary-frontend.js 907 B +3 B (0%)
build/atomic-block-components/summary.js 912 B +3 B (0%)
build/atomic-block-components/tag-list-frontend.js 466 B -2 B (0%)
build/atomic-block-components/tag-list.js 471 B -1 B (0%)
build/atomic-block-components/title-frontend.js 1.43 kB -3 B (0%)
build/atomic-block-components/title.js 1.28 kB -4 B (0%)
build/attribute-filter-frontend.js 18.1 kB +49 B (0%)
build/attribute-filter.js 11.9 kB +1 B (0%)
build/cart-frontend.js 90.5 kB +13.3 kB (+17%) ⚠️
build/cart.js 45.7 kB +73 B (0%)
build/checkout-blocks/sample.js 173 B -1 B (-1%)
build/checkout-frontend.js 94.6 kB +13.3 kB (+16%) ⚠️
build/checkout-i2-frontend.js 51.5 kB +165 B (0%)
build/checkout-i2.js 48.5 kB +108 B (0%)
build/checkout.js 48.7 kB +127 B (0%)
build/featured-category.js 7.81 kB +18 B (0%)
build/featured-product.js 9.51 kB +28 B (0%)
build/handpicked-products.js 6.35 kB +8 B (0%)
build/price-filter-frontend.js 14.3 kB +83 B (+1%)
build/price-filter.js 9.56 kB -15 B (0%)
build/product-best-sellers.js 6.69 kB +7 B (0%)
build/product-category.js 7.56 kB +5 B (0%)
build/product-new.js 6.85 kB +6 B (0%)
build/product-on-sale.js 7.21 kB +12 B (0%)
build/product-search.js 2.67 kB -2 B (0%)
build/product-tag.js 6.67 kB +8 B (0%)
build/product-top-rated.js 6.82 kB +7 B (0%)
build/products-by-attribute.js 7.79 kB +8 B (0%)
build/reviews-by-category.js 11.6 kB +21 B (0%)
build/reviews-by-product.js 13.1 kB +13 B (0%)
build/reviews-frontend.js 9.01 kB +3 B (0%)
build/single-product-frontend.js 25.9 kB +109 B (0%)
build/single-product.js 9.8 kB +18 B (0%)
build/vendors--atomic-block-components/add-to-cart-frontend.js 20.2 kB +167 B (+1%)
build/vendors--atomic-block-components/price-frontend.js 5.71 kB +1 B (0%)
build/wc-blocks-style-rtl.css 20 kB +133 B (+1%)
build/wc-blocks-style.css 20 kB +132 B (+1%)
build/wc-blocks-vendors-style-rtl.css 1.37 kB +319 B (+30%) 🚨
build/wc-blocks-vendors-style.css 1.37 kB +319 B (+30%) 🚨
build/wc-blocks-vendors.js 253 kB +275 B (0%)
build/wc-blocks.js 3.5 kB +2 B (0%)
ℹ️ View Unchanged
Filename Size
build/atomic-block-components/add-to-cart--atomic-block-components/image--atomic-block-components/title.js 334 B
build/atomic-block-components/category-list.js 476 B
build/atomic-block-components/rating-frontend.js 561 B
build/atomic-block-components/sku.js 394 B
build/blocks-checkout.js 21.2 kB
build/price-format.js 1.37 kB
build/product-categories.js 3.38 kB
build/wc-blocks-data.js 10.8 kB
build/wc-blocks-editor-style-rtl.css 15.4 kB
build/wc-blocks-editor-style.css 15.4 kB
build/wc-blocks-google-analytics.js 1.98 kB
build/wc-blocks-middleware.js 1.47 kB
build/wc-blocks-registry.js 2.74 kB
build/wc-blocks-shared-context.js 1.54 kB
build/wc-blocks-shared-hocs.js 1.75 kB
build/wc-payment-method-bacs.js 806 B
build/wc-payment-method-cheque.js 806 B
build/wc-payment-method-cod.js 898 B
build/wc-payment-method-paypal.js 839 B
build/wc-payment-method-stripe.js 12.2 kB
build/wc-settings.js 2.91 kB

compressed-size-action

@mikejolley
Copy link
Member Author

Tests will need updating due to the different country select markup.

Copy link
Contributor

@Aljullu Aljullu left a comment

Choose a reason for hiding this comment

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

I really love how it works and looks. I think this is a big improvement to the user experience. 👏

I tested several flows and features:

  • Desktop, mobile and screen reader.
  • Several themes.
  • Autofill.
  • Validation.

It worked great in all of them!

The only thing that worries me a bit is the size increase in cart and checkout frontend scripts. But I think the increase is worth it considering how the combobox simplifies the filling experience.

To get the label working I needed to simulate onBlur.

If it's only for styling purposes, I wonder if we could use the :focus-within pseudoclass. Now that we don't need to support IE11, this is a new tool we can make use of.

@@ -0,0 +1,156 @@
.wc-block-components-form .wc-block-components-combobox,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this selector required? If it is for specificity reasons, I would add a comment explaining that, and maybe using .wc-block-components-combobox.wc-block-components-combobox would be better so styles have the same specificity independently if it's inside a form component or not?

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 based this off of the select box control's CSS which has the same.

Copy link
Contributor

Choose a reason for hiding this comment

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

Aaah, I see. I would make the change in both, but not blocking this PR, then.

@mikejolley
Copy link
Member Author

@Aljullu Feedback taken care of :) The focus-within change worked really well, thanks for that.

Copy link
Contributor

@Aljullu Aljullu left a comment

Choose a reason for hiding this comment

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

Thanks for updating this PR @mikejolley! I didn't repeat the tests in all devices, but I did it on desktop and it's working fine.

Something that I think would improve the user experience is auto-selecting the first option of the combobox by default, would that be possible?

Selecting a country

In the example above, I'm writing estonia and pressing Enter, but the only suggestion available is not autoselected. Instead, I would need to press Key down + Enter or select it with the mouse. In trunk, however, writing a country name and pressing Enter works, so this is kind of a regression (not sure if blocking, though).

@mikejolley
Copy link
Member Author

@Aljullu That issue seems to be coming from the combo control: https://github.com/WordPress/gutenberg/blob/trunk/packages/components/src/combobox-control/index.js#L112-L117

If you have a value already, then type, then hit enter, the above code triggers and returns your selection to the original value.

You'll see that if you just type, or click out of the input, our custom logic triggers and keeps the new value selected.

It doesn't look like we have control over the behaviour or enter. Any suggestions?

Copy link
Contributor

@Aljullu Aljullu left a comment

Choose a reason for hiding this comment

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

@Aljullu That issue seems to be coming from the combo control: https://github.com/WordPress/gutenberg/blob/trunk/packages/components/src/combobox-control/index.js#L112-L117

Thanks for investigating! Tested the ComboboxControl from the GB storybook and this is how it works there too.

I still feel this behavior is a bit counter-intuitive. I would open an issue in Gutenberg and see if they agree with us and would accept PRs. If yes, maybe we can add a task to contribute a PR upstream to our next cooldown?


I left one comment below about an onChange() which runs on every render, but besides that, pre-approving.

...values,
[ field.key ]: '',
} );
}
Copy link
Contributor

Choose a reason for hiding this comment

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

That means onChange() is called on each render, right? Could this be refactored so it's run inside a useEffect() when the country or state change?

@mikejolley
Copy link
Member Author

Logged an issue here: WordPress/gutenberg#33920

@mikejolley
Copy link
Member Author

@nerrad @senadir @Aljullu I've added something called Patch Package which allows us to patch one of our dependencies and have the patch applied after NPM install. I've used this to apply the fix from my upstream PR so something is in place until that is updated and we bump the version we're using.

Are you happy with this solution?

@nerrad
Copy link
Contributor

nerrad commented Aug 9, 2021

Are you happy with this solution?

Nifty tool, first I learned of it! The only question I have is whether this particular usability improvement of combobox warrants the extra complexity to surface it now for all users, or if it's sufficient to just have the behaviour show up for sites running WordPress 5.9 (which is presumably when the fix will show up in WordPress core - sometime in December?) or if they have GB active?

@mikejolley
Copy link
Member Author

@nerrad By users do you mean us developers? This particular package is one of our direct imports (wordpress-components) which get included in the build. End-users won't notice a difference.

@nerrad
Copy link
Contributor

nerrad commented Aug 9, 2021

No sorry, I mean users of whatever UI implements the component.

@nerrad
Copy link
Contributor

nerrad commented Aug 10, 2021

We've chatted about my question in DM, I'm just leaving my feedback in here for reference. I was a bit concerned about the additional complexity for addressing a usability issue (a user typing something in the field powered by combobox and pressing enter and it not selecting the first matching item) given:

  • Mike has a fix that is approved and being merged in the relevant package in Gutenberg.
  • The combobox is being used directly from the @wordpress/components package so presumably we'll be able to get the fix on the next package update of WP components (at first I didn't realize it's directly imported which shortens the window even more for users seeing the fix than what I originally thought).
  • I personally wasn't sure usability issue was significant.

However, in conversing with Mike, it appears that the third point is more significant than I thought so I'm happy to defer to the choice to apply the patch until its no longer necessary.

How will we make sure we remove the patch (and package) when it's no longer necessary?

I haven't reviewed the actual code or tested this so I can't give a green check - I mostly just came into this PR to answer the question directly asked of me. But if it's good to go and tested, there's nothing blocking from me.

Copy link
Contributor

@Aljullu Aljullu left a comment

Choose a reason for hiding this comment

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

I tested it again on desktop and I love how it works now. It makes filling addresses much faster. 😍

Good job @mikejolley! :shipit:

How will we make sure we remove the patch (and package) when it's no longer necessary?

Should we open an issue (or add a @todo comment) so we don't forget about this?

@mikejolley
Copy link
Member Author

Todo added! 👍🏻

@mikejolley mikejolley merged commit 56476e1 into trunk Aug 12, 2021
@mikejolley mikejolley deleted the add/combo-control branch August 12, 2021 16:30
@mikejolley mikejolley added type: enhancement The issue is a request for an enhancement. block: checkout Issues related to the checkout block. labels Aug 16, 2021
@Aljullu Aljullu mentioned this pull request May 26, 2022
4 tasks
onFilterValueChange={ ( filterValue: string ) => {
if ( filterValue.length ) {
// If we have a value and the combobox is not focussed, this could be from browser autofill.
const activeElement = isObject( controlRef.current )
Copy link
Member

Choose a reason for hiding this comment

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

why was isObject used here? a dom element would never evaluate to true here.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
block: checkout Issues related to the checkout block. type: enhancement The issue is a request for an enhancement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Migrate CountryInput and StateInput to ComboboxControl
4 participants