diff --git a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/form/form.tsx b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/form/form.tsx index 36092522df1..28ed4a7f40b 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/form/form.tsx +++ b/plugins/woocommerce-blocks/assets/js/base/components/cart-checkout/form/form.tsx @@ -33,9 +33,9 @@ import { AddressFormProps, AddressFormFields } from './types'; import prepareFormFields from './prepare-form-fields'; import validateShippingCountry from './validate-shipping-country'; import customValidationHandler from './custom-validation-handler'; -import Combobox from '../../combobox'; import AddressLineFields from './address-line-fields'; import { createFieldProps, getFieldData } from './utils'; +import { Select } from '../../select'; /** * Checkout form. @@ -228,9 +228,10 @@ const Form = < T extends AddressFormValues | ContactFormValues >( { } return ( - { - if ( country ) { - const countryInput = screen.queryByRole( 'combobox', { - name: countryRegExp, - } ); - await userEvent.type( countryInput, country + '{arrowdown}{enter}' ); + if ( countryKey ) { + const countryInput = screen.getByLabelText( 'Country/Region' ); + + if ( countryInput ) { + await userEvent.selectOptions( countryInput, countryKey ); + } } if ( city ) { const cityInput = screen.getByLabelText( cityRegExp ); @@ -81,15 +87,12 @@ const inputAddress = async ( { } if ( state ) { - const stateButton = screen.queryByRole( 'combobox', { - name: stateRegExp, + const stateButton = screen.queryByLabelText( stateRegExp, { + selector: 'select', } ); // State input might be a select or a text input. if ( stateButton ) { - await userEvent.click( stateButton ); - await userEvent.click( - screen.getByRole( 'option', { name: state } ) - ); + await userEvent.selectOptions( stateButton, state ); } else { const stateInput = screen.getByLabelText( stateRegExp ); await userEvent.type( stateInput, state ); @@ -182,24 +185,16 @@ describe( 'Form Component', () => { it( 'input values are reset after changing the country', async () => { renderInCheckoutProvider( ); + // First enter an address with no state, but fill the city. await act( async () => { await inputAddress( secondaryAddress ); } ); - // Only update `country` to verify other values are reset. + // Update country to another country without state. await act( async () => { - await inputAddress( { country: primaryAddress.country } ); + await inputAddress( { countryKey: quaternaryAddress.countryKey } ); } ); - expect( screen.getByLabelText( stateRegExp ).value ).toBe( '' ); - - // Repeat the test with an address which has a select for the state. - await act( async () => { - await inputAddress( tertiaryAddress ); - } ); - await act( async () => { - await inputAddress( { country: primaryAddress.country } ); - } ); - expect( screen.getByLabelText( stateRegExp ).value ).toBe( '' ); + expect( screen.getByLabelText( postalCodeRegExp ).value ).toBe( '' ); } ); } ); diff --git a/plugins/woocommerce-blocks/assets/js/base/components/country-input/billing-country-input.tsx b/plugins/woocommerce-blocks/assets/js/base/components/country-input/billing-country-input.tsx index 82e1efe4d5f..57c8483efe6 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/country-input/billing-country-input.tsx +++ b/plugins/woocommerce-blocks/assets/js/base/components/country-input/billing-country-input.tsx @@ -10,7 +10,10 @@ import CountryInput from './country-input'; import type { CountryInputProps } from './CountryInputProps'; const BillingCountryInput = ( props: CountryInputProps ): JSX.Element => { - return ; + // TODO - are errorMessage and errorId still relevant when select always has a value? + const { errorMessage: _, errorId: __, ...restOfProps } = props; + + return ; }; export default BillingCountryInput; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/country-input/country-input.tsx b/plugins/woocommerce-blocks/assets/js/base/components/country-input/country-input.tsx index 30091aa33ec..2ca59e72216 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/country-input/country-input.tsx +++ b/plugins/woocommerce-blocks/assets/js/base/components/country-input/country-input.tsx @@ -2,16 +2,15 @@ * External dependencies */ import { useMemo } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; import { decodeEntities } from '@wordpress/html-entities'; import clsx from 'clsx'; /** * Internal dependencies */ -import Combobox from '../combobox'; import './style.scss'; import type { CountryInputWithCountriesProps } from './CountryInputProps'; +import { Select } from '../select'; export const CountryInput = ( { className, @@ -22,8 +21,6 @@ export const CountryInput = ( { value = '', autoComplete = 'off', required = false, - errorId, - errorMessage = __( 'Please select a country', 'woocommerce' ), }: CountryInputWithCountriesProps ): JSX.Element => { const options = useMemo( () => @@ -40,14 +37,12 @@ export const CountryInput = ( {
- diff --git a/plugins/woocommerce-blocks/assets/js/base/components/index.ts b/plugins/woocommerce-blocks/assets/js/base/components/index.ts index ac18ea408aa..971ca329e58 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/index.ts +++ b/plugins/woocommerce-blocks/assets/js/base/components/index.ts @@ -23,6 +23,7 @@ export * from './product-rating'; export * from './quantity-selector'; export * from './read-more'; export * from './reviews'; +export * from './select'; export * from './sidebar-layout'; export * from './snackbar-list'; export * from './state-input'; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/select/index.tsx b/plugins/woocommerce-blocks/assets/js/base/components/select/index.tsx new file mode 100644 index 00000000000..9f8e1b0173b --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/base/components/select/index.tsx @@ -0,0 +1,71 @@ +/** + * External dependencies + */ +import { Icon, chevronDown } from '@wordpress/icons'; +import { useCallback, useId } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import './style.scss'; + +type SelectProps = Omit< + React.SelectHTMLAttributes< HTMLSelectElement >, + 'onChange' +> & { + options: { value: string; label: string }[]; + label: string; + onChange: ( newVal: string ) => void; +}; + +export const Select = ( props: SelectProps ) => { + const { onChange, options, label, value, className, size, ...restOfProps } = + props; + + const selectOnChange = useCallback( + ( event: React.ChangeEvent< HTMLSelectElement > ) => { + onChange( event.target.value ); + }, + [ onChange ] + ); + + const generatedId = useId(); + + const inputId = + restOfProps.id || `wc-blocks-components-select-${ generatedId }`; + + return ( +
+
+ + + +
+
+ ); +}; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/select/style.scss b/plugins/woocommerce-blocks/assets/js/base/components/select/style.scss new file mode 100644 index 00000000000..76b2530a958 --- /dev/null +++ b/plugins/woocommerce-blocks/assets/js/base/components/select/style.scss @@ -0,0 +1,78 @@ +.wc-blocks-components-select { + width: 100%; + + .wc-blocks-components-select__container { + border-radius: $universal-border-radius; + box-sizing: border-box; + border: 1px solid $universal-border-light; + background: #fff; + width: 100%; + height: 50px; + position: relative; + + .has-dark-controls & { + background-color: $input-background-dark; + border-color: $input-border-dark; + color: $input-text-dark; + + &:focus { + background-color: $input-background-dark; + color: $input-text-dark; + box-shadow: 0 0 0 2px $input-border-gray; + } + } + } + + .wc-blocks-components-select__select { + @include reset-typography(); + @include font-size(regular); + border-radius: $universal-border-radius; + width: 100%; + height: 100%; + appearance: none; + background: none; + padding: em($gap) em($gap-smaller) 0; + color: currentColor; + + &:focus { + outline: 0; + box-shadow: 0 0 0 1px currentColor; + } + } + + .wc-blocks-components-select__label { + @include reset-typography(); + @include font-size(regular); + position: absolute; + line-height: 1.25; // =20px when font-size is 16px. + left: em($gap-smaller); + top: 0; + transform-origin: top left; + transition: all 200ms ease; + color: currentColor; + z-index: 1; + margin: 0; + overflow: hidden; + text-overflow: ellipsis; + max-width: calc(100% - #{2 * $gap}); + white-space: nowrap; + + .has-dark-controls & { + color: $input-placeholder-dark; + } + @media screen and (prefers-reduced-motion: reduce) { + transition: none; + } + + transform: translateY(15%) scale(0.75); + } + + .wc-blocks-components-select__expand { + position: absolute; + transform: translate(0%, -50%); + top: 50%; + right: $gap-small; + pointer-events: none; + fill: currentColor; + } +} diff --git a/plugins/woocommerce-blocks/assets/js/base/components/state-input/billing-state-input.tsx b/plugins/woocommerce-blocks/assets/js/base/components/state-input/billing-state-input.tsx index 847c71d2039..b7a82c578ef 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/state-input/billing-state-input.tsx +++ b/plugins/woocommerce-blocks/assets/js/base/components/state-input/billing-state-input.tsx @@ -10,7 +10,10 @@ import StateInput from './state-input'; import type { StateInputProps } from './StateInputProps'; const BillingStateInput = ( props: StateInputProps ): JSX.Element => { - return ; + // TODO - are errorMessage and errorId still relevant when select always has a value? + const { errorMessage: _, errorId: __, ...restOfProps } = props; + + return ; }; export default BillingStateInput; diff --git a/plugins/woocommerce-blocks/assets/js/base/components/state-input/state-input.tsx b/plugins/woocommerce-blocks/assets/js/base/components/state-input/state-input.tsx index a569716cd80..2ad82384a6a 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/state-input/state-input.tsx +++ b/plugins/woocommerce-blocks/assets/js/base/components/state-input/state-input.tsx @@ -1,18 +1,16 @@ /** * External dependencies */ -import { __ } from '@wordpress/i18n'; import { decodeEntities } from '@wordpress/html-entities'; import { useCallback, useMemo, useEffect, useRef } from '@wordpress/element'; -import clsx from 'clsx'; import { ValidatedTextInput } from '@woocommerce/blocks-components'; /** * Internal dependencies */ -import Combobox from '../combobox'; import './style.scss'; import type { StateInputWithStatesProps } from './StateInputProps'; +import { Select } from '../select'; const optionMatcher = ( value: string, @@ -36,7 +34,6 @@ const StateInput = ( { autoComplete = 'off', value = '', required = false, - errorId = '', }: StateInputWithStatesProps ): JSX.Element => { const countryStates = states[ country ]; const options = useMemo( @@ -91,20 +88,17 @@ const StateInput = ( { if ( options.length > 0 ) { return ( - ); } diff --git a/plugins/woocommerce-blocks/assets/js/base/components/state-input/style.scss b/plugins/woocommerce-blocks/assets/js/base/components/state-input/style.scss index 47288567e82..5ef6f3c0339 100644 --- a/plugins/woocommerce-blocks/assets/js/base/components/state-input/style.scss +++ b/plugins/woocommerce-blocks/assets/js/base/components/state-input/style.scss @@ -1,3 +1,3 @@ -.wc-block-components-state-input { +.wc-blocks-components-select__container { margin-top: $gap; } diff --git a/plugins/woocommerce-blocks/bin/add-split-chunk-dependencies.js b/plugins/woocommerce-blocks/bin/add-split-chunk-dependencies.js index 19bf51e7cb0..245186faffb 100644 --- a/plugins/woocommerce-blocks/bin/add-split-chunk-dependencies.js +++ b/plugins/woocommerce-blocks/bin/add-split-chunk-dependencies.js @@ -14,8 +14,7 @@ class AddSplitChunkDependencies { apply( compiler ) { compiler.hooks.thisCompilation.tap( 'AddStableChunksToAssets', - // eslint-disable-next-line @typescript-eslint/no-unused-vars -- not used. - ( compilation, callback ) => { + ( compilation ) => { compilation.hooks.processAssets.tap( { name: 'AddStableChunksToAssets', diff --git a/plugins/woocommerce-blocks/bin/webpack-configs.js b/plugins/woocommerce-blocks/bin/webpack-configs.js index efbaff18e76..936ab7a8cfd 100644 --- a/plugins/woocommerce-blocks/bin/webpack-configs.js +++ b/plugins/woocommerce-blocks/bin/webpack-configs.js @@ -330,8 +330,7 @@ const getFrontConfig = ( options = {} ) => { // translations which we must avoid. // @see https://github.com/Automattic/jetpack/pull/20926 chunkFilename: `[name]-frontend${ fileSuffix }.js?ver=[contenthash]`, - // eslint-disable-next-line @typescript-eslint/no-unused-vars -- not used. - filename: ( pathData ) => { + filename: () => { return `[name]-frontend${ fileSuffix }.js`; }, uniqueName: 'webpackWcBlocksFrontendJsonp', diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/checkout/additional-fields.guest-shopper.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/checkout/additional-fields.guest-shopper.block_theme.spec.ts index 7a4d9042897..b18760003bd 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/checkout/additional-fields.guest-shopper.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/checkout/additional-fields.guest-shopper.block_theme.spec.ts @@ -76,11 +76,6 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { 'Please enter a valid government id' ) ).toBeVisible(); - await expect( - checkoutPageObject.page.getByText( - 'Please select a valid option' - ) - ).toBeVisible(); } ); test( 'Shopper can fill in the checkout form with additional fields and can have different value for same field in shipping and billing address', async ( { @@ -122,13 +117,13 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { name: 'Shipping address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'wide' ); + .selectOption( 'wide' ); await checkoutPageObject.page .getByRole( 'group', { name: 'Billing address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'narrow' ); + .selectOption( 'narrow' ); await checkoutPageObject.page.evaluate( 'document.activeElement.blur()' @@ -217,7 +212,7 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { .getByLabel( 'Is this a personal purchase or a business purchase?' ) - ).toHaveValue( 'Business' ); + ).toHaveValue( 'business' ); await expect( checkoutPageObject.page .getByRole( 'group', { @@ -252,7 +247,7 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { name: 'Shipping address', } ) .getByLabel( 'How wide is your road?' ) - ).toHaveValue( 'Wide' ); + ).toHaveValue( 'wide' ); await expect( checkoutPageObject.page .getByRole( 'group', { @@ -280,7 +275,7 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { name: 'Billing address', } ) .getByLabel( 'How wide is your road?' ) - ).toHaveValue( 'Narrow' ); + ).toHaveValue( 'narrow' ); } ); } ); } ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/checkout/additional-fields.merchant.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/checkout/additional-fields.merchant.block_theme.spec.ts index bdc9b0e03df..6b82919576b 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/checkout/additional-fields.merchant.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/checkout/additional-fields.merchant.block_theme.spec.ts @@ -70,13 +70,13 @@ test.describe( 'Merchant → Additional Checkout Fields', () => { name: 'Shipping address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'wide' ); + .selectOption( 'wide' ); await checkoutPageObject.page .getByRole( 'group', { name: 'Billing address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'narrow' ); + .selectOption( 'narrow' ); await checkoutPageObject.page.evaluate( 'document.activeElement.blur()' @@ -206,13 +206,13 @@ test.describe( 'Merchant → Additional Checkout Fields', () => { name: 'Shipping address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'wide' ); + .selectOption( 'wide' ); await checkoutPageObject.page .getByRole( 'group', { name: 'Billing address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'narrow' ); + .selectOption( 'narrow' ); await checkoutPageObject.page.evaluate( 'document.activeElement.blur()' diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/checkout/additional-fields.shopper.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/checkout/additional-fields.shopper.block_theme.spec.ts index 6e377fe0965..1a80841a03d 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/checkout/additional-fields.shopper.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/checkout/additional-fields.shopper.block_theme.spec.ts @@ -73,13 +73,13 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { name: 'Shipping address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'wide' ); + .selectOption( 'wide' ); await checkoutPageObject.page .getByRole( 'group', { name: 'Billing address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'narrow' ); + .selectOption( 'narrow' ); await checkoutPageObject.page.evaluate( 'document.activeElement.blur()' @@ -155,7 +155,7 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { .getByLabel( 'Is this a personal purchase or a business purchase?' ) - ).toHaveValue( 'Business' ); + ).toHaveValue( 'business' ); await expect( checkoutPageObject.page .getByRole( 'group', { @@ -190,7 +190,7 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { name: 'Shipping address', } ) .getByLabel( 'How wide is your road?' ) - ).toHaveValue( 'Wide' ); + ).toHaveValue( 'wide' ); await expect( checkoutPageObject.page .getByRole( 'group', { @@ -218,7 +218,7 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { name: 'Billing address', } ) .getByLabel( 'How wide is your road?' ) - ).toHaveValue( 'Narrow' ); + ).toHaveValue( 'narrow' ); } ); test( 'Shopper can change the values of fields multiple times and place the order', async ( { @@ -261,13 +261,13 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { name: 'Shipping address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'wide' ); + .selectOption( 'wide' ); await checkoutPageObject.page .getByRole( 'group', { name: 'Billing address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'narrow' ); + .selectOption( 'narrow' ); await checkoutPageObject.waitForCustomerDataUpdate(); // Change the shipping and billing select fields again. @@ -276,13 +276,13 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { name: 'Billing address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'wide' ); + .selectOption( 'wide' ); await checkoutPageObject.page .getByRole( 'group', { name: 'Shipping address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'super-wide' ); + .selectOption( 'super-wide' ); await checkoutPageObject.waitForCustomerDataUpdate(); await checkoutPageObject.page @@ -354,11 +354,9 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { .getByRole( 'group', { name: 'Additional order information', } ) - .locator( - 'ul.components-form-token-field__suggestions-list > li' - ) + .locator( 'select' ) .first() - .click(); + .selectOption( { index: 0 } ); await checkoutPageObject.waitForCustomerDataUpdate(); await checkoutPageObject.placeOrder(); @@ -445,13 +443,13 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { name: 'Shipping address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'wide' ); + .selectOption( 'wide' ); await checkoutPageObject.page .getByRole( 'group', { name: 'Billing address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'narrow' ); + .selectOption( 'narrow' ); await checkoutPageObject.page.evaluate( 'document.activeElement.blur()' @@ -527,7 +525,7 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { .getByLabel( 'Is this a personal purchase or a business purchase?' ) - ).toHaveValue( 'Business' ); + ).toHaveValue( 'business' ); await expect( checkoutPageObject.page .getByRole( 'group', { @@ -562,7 +560,7 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { name: 'Shipping address', } ) .getByLabel( 'How wide is your road?' ) - ).toHaveValue( 'Wide' ); + ).toHaveValue( 'wide' ); await expect( checkoutPageObject.page .getByRole( 'group', { @@ -590,7 +588,7 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { name: 'Billing address', } ) .getByLabel( 'How wide is your road?' ) - ).toHaveValue( 'Narrow' ); + ).toHaveValue( 'narrow' ); } ); test( 'Shopper can see server-side validation errors', async ( { @@ -682,13 +680,13 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { name: 'Shipping address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'wide' ); + .selectOption( 'wide' ); await checkoutPageObject.page .getByRole( 'group', { name: 'Billing address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'narrow' ); + .selectOption( 'narrow' ); await checkoutPageObject.page.evaluate( 'document.activeElement.blur()' ); @@ -766,13 +764,13 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { name: 'Shipping address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'wide' ); + .selectOption( 'wide' ); await checkoutPageObject.page .getByRole( 'group', { name: 'Billing address', } ) .getByLabel( 'How wide is your road?' ) - .fill( 'narrow' ); + .selectOption( 'narrow' ); // Blur after editing the select fields since they need to be blurred to save. await checkoutPageObject.page.evaluate( @@ -925,7 +923,7 @@ test.describe( 'Shopper → Additional Checkout Fields', () => { await govIdInput.fill( '11111' ); await confirmGovIdInput.fill( '11111' ); await shippingTruckFittingCheckbox.uncheck(); - await shippingRoadSizeSelect.selectOption( 'Narrow' ); + await shippingRoadSizeSelect.selectOption( 'narrow' ); await checkoutPageObject.page.getByText( 'Save address' ).click(); // Check the updated values are visible in the addresses. diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/checkout/checkout-block.shopper.block_theme.spec.ts b/plugins/woocommerce-blocks/tests/e2e/tests/checkout/checkout-block.shopper.block_theme.spec.ts index a280ca67491..c2af748a6cb 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/checkout/checkout-block.shopper.block_theme.spec.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/checkout/checkout-block.shopper.block_theme.spec.ts @@ -228,6 +228,7 @@ test.describe( 'Shopper → Shipping and Billing Addresses', () => { city: 'San Francisco', state: 'California', country: 'United Kingdom', + countryKey: 'GB', postcode: 'SW1 1AA', phone: '123456789', email: 'john.doe@example.com', @@ -241,6 +242,7 @@ test.describe( 'Shopper → Shipping and Billing Addresses', () => { city: 'Los Angeles', phone: '987654321', country: 'Albania', + countryKey: 'AL', state: 'Berat', postcode: '1234', }; @@ -349,7 +351,7 @@ test.describe( 'Shopper → Shipping (customer user)', () => { lastname: 'Perez', addressfirstline: '123 Test Street', addresssecondline: 'Apartment 6', - country: 'ES', + countryKey: 'ES', city: 'Madrid', postcode: '08830', state: 'M', @@ -546,6 +548,7 @@ test.describe( 'Billing Address Form', () => { addressfirstline: '123 Easy Street', addresssecondline: 'Testville', country: 'United States (US)', + countryKey: 'US', city: 'New York', state: 'New York', postcode: '90210', @@ -571,14 +574,14 @@ test.describe( 'Billing Address Form', () => { shippingForm.getByLabel( 'Apartment, suite, etc. (optional)' ) ).toHaveValue( 'Testville' ); await expect( - shippingForm.getByLabel( 'United States (US), Country/' ) - ).toHaveValue( 'United States (US)' ); + shippingForm.getByLabel( 'Country/Region' ) + ).toHaveValue( 'US' ); await expect( shippingForm.getByLabel( 'City' ) ).toHaveValue( 'New York' ); - await expect( - shippingForm.getByLabel( 'New York, State' ) - ).toHaveValue( 'New York' ); + await expect( shippingForm.getByLabel( 'State' ) ).toHaveValue( + 'NY' + ); await expect( shippingForm.getByLabel( 'ZIP Code' ) ).toHaveValue( '90210' ); @@ -605,12 +608,12 @@ test.describe( 'Billing Address Form', () => { } ) ).toBeVisible(); await expect( - billingForm.getByLabel( 'United States (US), Country/' ) - ).toHaveValue( 'United States (US)' ); + billingForm.getByLabel( 'Country/Region' ) + ).toHaveValue( 'US' ); await expect( billingForm.getByLabel( 'City' ) ).toHaveValue( '' ); - await expect( - billingForm.getByLabel( 'New York, State' ) - ).toHaveValue( 'New York' ); + await expect( billingForm.getByLabel( 'State' ) ).toHaveValue( + 'NY' + ); await expect( billingForm.getByLabel( 'ZIP Code' ) ).toHaveValue( '' ); diff --git a/plugins/woocommerce-blocks/tests/e2e/tests/checkout/checkout.page.ts b/plugins/woocommerce-blocks/tests/e2e/tests/checkout/checkout.page.ts index 41960434059..d56d8715dc6 100644 --- a/plugins/woocommerce-blocks/tests/e2e/tests/checkout/checkout.page.ts +++ b/plugins/woocommerce-blocks/tests/e2e/tests/checkout/checkout.page.ts @@ -23,6 +23,7 @@ export class CheckoutPage { addressfirstline: '123 Easy Street', addresssecondline: 'Testville', country: 'United States (US)', + countryKey: 'US', city: 'New York', state: 'New York', postcode: '90210', @@ -124,7 +125,16 @@ export class CheckoutPage { // Rest of additional data passed in from the overrideData object. for ( const [ label, value ] of Object.entries( additionalFields ) ) { const field = contactSection.getByLabel( label ); - await field.fill( value ); + + const tagName = await field.evaluate( ( element ) => + element.tagName.toLowerCase() + ); + + if ( tagName === 'select' ) { + await field.selectOption( value ); + } else { + await field.fill( value ); + } } } @@ -138,7 +148,16 @@ export class CheckoutPage { // Rest of additional data passed in from the overrideData object. for ( const [ label, value ] of Object.entries( additionalFields ) ) { const field = contactSection.getByLabel( label ); - await field.fill( value ); + + const tagName = await field.evaluate( ( element ) => + element.tagName.toLowerCase() + ); + + if ( tagName === 'select' ) { + await field.selectOption( value ); + } else { + await field.fill( value ); + } } } @@ -151,7 +170,16 @@ export class CheckoutPage { // Rest of additional data passed in from the overrideData object. for ( const { label, value } of additionalFields ) { const field = this.page.getByLabel( label ); - await field.fill( value ); + + const tagName = await field.evaluate( ( element ) => + element.tagName.toLowerCase() + ); + + if ( tagName === 'select' ) { + await field.selectOption( value ); + } else { + await field.fill( value ); + } } } @@ -337,7 +365,7 @@ export class CheckoutPage { await email.fill( customerBillingDetails.email ); await firstName.fill( customerBillingDetails.firstname ); await lastName.fill( customerBillingDetails.lastname ); - await country.fill( customerBillingDetails.country ); + await country.selectOption( customerBillingDetails.countryKey ); await address1.fill( customerBillingDetails.addressfirstline ); if ( customerBillingDetails.addresssecondline ) { @@ -360,10 +388,17 @@ export class CheckoutPage { } ); const county = billingForm.getByLabel( 'County' ); - await state - .or( province ) - .or( county ) - .fill( customerBillingDetails.state ); + const elementToFill = state.or( province ).or( county ); + const tagName = await elementToFill.evaluate( ( element ) => + element.tagName.toLowerCase() + ); + if ( tagName === 'select' ) { + await elementToFill.selectOption( + customerBillingDetails.state + ); + } else { + await elementToFill.fill( customerBillingDetails.state ); + } } if ( customerBillingDetails.postcode ) { @@ -376,7 +411,16 @@ export class CheckoutPage { // Rest of additional data passed in from the overrideData object. for ( const [ label, value ] of Object.entries( additionalFields ) ) { const field = billingForm.getByLabel( label, { exact: true } ); - await field.fill( value ); + + const tagName = await field.evaluate( ( element ) => + element.tagName.toLowerCase() + ); + + if ( tagName === 'select' ) { + await field.selectOption( value ); + } else { + await field.fill( value ); + } } } @@ -407,7 +451,7 @@ export class CheckoutPage { await firstName.fill( customerShippingDetails.firstname ); await lastName.fill( customerShippingDetails.lastname ); - await country.fill( customerShippingDetails.country ); + await country.selectOption( customerShippingDetails.country ); await address1.fill( customerShippingDetails.addressfirstline ); if ( customerShippingDetails.addresssecondline ) { @@ -430,10 +474,17 @@ export class CheckoutPage { } ); const county = shippingForm.getByLabel( 'County' ); - await state - .or( province ) - .or( county ) - .fill( customerShippingDetails.state ); + const elementToFill = state.or( province ).or( county ); + const tagName = await elementToFill.evaluate( ( element ) => + element.tagName.toLowerCase() + ); + if ( tagName === 'select' ) { + await elementToFill.selectOption( + customerShippingDetails.state + ); + } else { + await elementToFill.fill( customerShippingDetails.state ); + } } if ( customerShippingDetails.postcode ) { @@ -446,7 +497,16 @@ export class CheckoutPage { // Rest of additional data passed in from the overrideData object. for ( const [ label, value ] of Object.entries( additionalFields ) ) { const field = shippingForm.getByLabel( label, { exact: true } ); - await field.fill( value ); + + const tagName = await field.evaluate( ( element ) => + element.tagName.toLowerCase() + ); + + if ( tagName === 'select' ) { + await field.selectOption( value ); + } else { + await field.fill( value ); + } } // Blur active field to trigger customer address update. diff --git a/plugins/woocommerce/changelog/48180-spike-checkout-native-select-migration b/plugins/woocommerce/changelog/48180-spike-checkout-native-select-migration new file mode 100644 index 00000000000..562d169b455 --- /dev/null +++ b/plugins/woocommerce/changelog/48180-spike-checkout-native-select-migration @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Migrate the cart and checkout block's state, country and custom field input to a native select \ No newline at end of file diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-block-calculate-shipping.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-block-calculate-shipping.spec.js index 16b9f679b44..8649bd7e246 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-block-calculate-shipping.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/cart-block-calculate-shipping.spec.js @@ -142,7 +142,10 @@ test.describe( // Set shipping country to Netherlands await page.getByLabel( 'Add an address for shipping' ).click(); - await page.getByRole( 'combobox' ).first().fill( 'Netherlands' ); + await page + .getByRole( 'combobox' ) + .first() + .selectOption( 'Netherlands' ); await page.getByLabel( 'Postal code' ).fill( '1011AA' ); await page.getByLabel( 'City' ).fill( 'Amsterdam' ); await page.getByRole( 'button', { name: 'Update' } ).click(); @@ -171,7 +174,10 @@ test.describe( // Set shipping country to Portugal await page.getByLabel( 'Add an address for shipping' ).click(); - await page.getByRole( 'combobox' ).first().fill( 'Portugal' ); + await page + .getByRole( 'combobox' ) + .first() + .selectOption( 'Portugal' ); await page.getByLabel( 'Postal code' ).fill( '1000-001' ); await page.getByLabel( 'City' ).fill( 'Lisbon' ); await page.getByRole( 'button', { name: 'Update' } ).click(); @@ -207,7 +213,10 @@ test.describe( // Set shipping country to Portugal await page.getByLabel( 'Add an address for shipping' ).click(); - await page.getByRole( 'combobox' ).first().fill( 'Portugal' ); + await page + .getByRole( 'combobox' ) + .first() + .selectOption( 'Portugal' ); await page.getByLabel( 'Postal code' ).fill( '1000-001' ); await page.getByLabel( 'City' ).fill( 'Lisbon' ); await page.getByRole( 'button', { name: 'Update' } ).click(); @@ -238,7 +247,10 @@ test.describe( // Set shipping country to Portugal await page.getByLabel( 'Add an address for shipping' ).click(); - await page.getByRole( 'combobox' ).first().fill( 'Portugal' ); + await page + .getByRole( 'combobox' ) + .first() + .selectOption( 'Portugal' ); await page.getByLabel( 'Postal code' ).fill( '1000-001' ); await page.getByLabel( 'City' ).fill( 'Lisbon' ); await page.getByRole( 'button', { name: 'Update' } ).click(); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/shopper/checkout-block.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/shopper/checkout-block.spec.js index a993b79ac9b..563bf17c99b 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/shopper/checkout-block.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/shopper/checkout-block.spec.js @@ -376,12 +376,8 @@ test.describe( await expect( page.getByText( '+ Add apartment, suite, etc.' ) ).toBeEnabled(); - await expect( - page.getByLabel( 'United States (US), Country/Region' ) - ).toBeEditable(); - await expect( - page.getByLabel( 'California, State' ) - ).toBeEditable(); + await expect( page.getByLabel( 'Country/Region' ) ).toBeEnabled(); + await expect( page.getByLabel( 'State' ) ).toBeEnabled(); await expect( page.getByLabel( 'City' ) ).toBeEditable(); await expect( page.getByLabel( 'ZIP Code' ) ).toBeEnabled(); await expect(