Commit b8de41cdb2 for woocommerce

commit b8de41cdb20afd1d117ebf12848de1437b43f513
Author: CAT the Tech <88114675+catthetech@users.noreply.github.com>
Date:   Wed Jan 14 09:18:28 2026 -0500

    Add variations autoselect (new) (#61389)

    * Added the functionality to display attribute values in conflict and specify what to do with them

    Signed-off-by: CAT the Tech <trottier.charlesantoine@gmail.com>

    * Added the "Auto-select on page load" option

    Signed-off-by: CAT the Tech <trottier.charlesantoine@gmail.com>

    * Made it so initial options are filtered according to possible variations

    Signed-off-by: CAT the Tech <trottier.charlesantoine@gmail.com>

    * Replace "option_gt_filter" by [value=""]

    - "option_gt_filter" targets the "Choose an option"/empty option
    - The "Choose an option"/empty option could be replaced by a
    plugin and not be the first option anymore

    - [value=""] is better because it will ALWAYS target the "Choose an
      option"/empty option because it is always empty
    - [value="" removes the need to handle the special case of show_option_none === 'no'

    Signed-off-by: CAT the Tech <trottier.charlesantoine@gmail.com>

    * Added changelog

    Signed-off-by: CAT the Tech <trottier.charlesantoine@gmail.com>

    * Modify the on page load logic to trigger a change and a click instead of a manual check-variations

    * Add settings for autoselect and autoselectOnPageLoad on the new Add to Cart + Options block

    * Enable autoselect for the new Add to Cart + Options block (both dropdown- and pill-style)

    * Enable autoselectOnPageLoad on the new Add to Cart + Options block

    * Handle disabled attributes when autoselecting

    * Correct labels and help texts; Be more consistent

    * Rename autoselect_lock to in_autoselect_scope; Explain a bit more what it does

    * More comments; Minor fixes

    * Change unattached_action to unattached_attributes_action

    * Fix an incompatibility with the dropdown and pill styles

    * Add in_autoselect_scope check to the old/legacy Add to Cart With Options block

    * Legacy Add to Cart block: Changed where the disabled options' color was set and allow enabled options to be dealt with along unattached options

    * Legacy Add to Cart block: check variations after resetting form when no variations are possible

    * Add disabledAttributesAction setting to the new Add to Cart + Options block

    * Add more comments, modify texts and other small tweaks

    * Write e2e-pw tests for autoselect and related options

    * Stay up-to-date with the development of the new Add to Cart + Options block

    * Moved changelog to reflect new branch name

    * Tests: clean up and remove the forced timeout in favor of manually checking if block settings change

    * Tests: Also test without autoselect (both on page load and on user selection)

    * Add to Cart + Options block: Allow auto-selecting disabled choices if they are valid but not yet enabled

    * Tests: add the selectAttribute function and shorten some expect statements

    * Tests: Add a test about combining Auto-select on user selection and the Values in conflict setting

    * Tests: Rename selectAttributes to selectBlockAttributes and add function expectSelectedAttributes

    * Tests: add else statements to throw errors

    * Tests: loop over attributeValues instead of putting them in a RegExp in expectSelectedAttributes

    * Revert all changes on the old/legacy Add to Cart block

    * Merge Autoselect and Autoselect on page load

    * Remove "gray" option from disabledAttributesAction

    * Tests: Add common setup and revert to the old/legacy Add to Cart block after testing is complete

    * Listen to CodeRabbitAI's suggestions

    * Add to Cart + options block Tests: Ensure the product type panel is open

    * Add to Cart + options block Tests: Only upgrade to the new block if it is not already

    * Move autoselect tests to the block's test file in @woocommerce/block-library

    * Don't use jQuery anymore in the new Add to Cart + Options block

    * Tests: Get the product's attributes directly instead of maintaining a copy in the test file

    * Always use 'data-wp-bind--disabled', and remove the switch statement in favor of a ternary operator

    * Added id and autoselect to the context to support multiple cart blocks at the same time

    * Rename attributesAutoselect and attributesAutoselectOthers to make it clear they're doing something, and move them under actions

    * Remove a switch statement in favor of a ternary operator

    * Improve typing

    * Manually check/select the value when auto-selecting instead of simulating a click

    WIP

    * Fix post_names in sample_products.xml

    * Modify the changelog to clarify the modifications apply to the Add to Cart + Options block

    * Use the pre-existing $attributes variable instead of fetching it manually

    * Refactor Add to Cart + Options autoselect so it doesn't query the DOM directly

    * Remove an unused mention of id; autoselectAttributes now has both includedAttributes and excludedAttributes as arguments; Don't auto-select when the user deselects an attribute

    * Listen to CodeRabbitAI's suggestions

    * Ensure productObject.variations is an array and productObject.variations.attributes is an object inside getProductAttributesAndOptions

    * Don't define context as it is not needed anymore

    * Fix typing for frontend.js

    * Nest test.describe under the root one and improve typing

    * Tests: productId is not needed anymore; Removed it

    * Simply check if productObject?.variations exists instead of checking if it is an array or object

    * Tests: Add missing parentheses

    * Tests: Removed function goToProductTemplateEditor and replaced by calls to pageObject.updateSingleProductTemplate

    * Tests: Make optionStyle values lowercase

    * Tests: Refactor the selectBlockAttributes and expectedSelectedAttributes fucntions and move them where other tests can use them

    * Tests: We don't need to import Page anymore; import Editor from woocommerce, not wordpress

    * Tests: Call pageObject.updateSingleProductTemplate manually before each test to make it clear instead of inside test.beforeEach

    * Tests: Don't forcefully set block attributes to their default values at the beginning of each test

    * Tests: Merge the two auto-select tests (on page load and on user selection) into one

    * Tests: Rename the setCartBlockAttributes function to setAddToCartWithOptionsAttributes

    * Tests: Remove the setDisabledAttributesAction function as it is redundant

    * Tests: Remove the preselect functions as they are used only once or twice

    * Tests: Set the optionStyle in the template editor at the beginning of each tests

    * Tests: Capitalize the optionStyle values again, because we are using them to locate the optionStyle radio button

    * Remove a useless return statement and rename blockCart to addToCartWithOptionsBlock

    * Tests: Rename some tests to not duplicate "Add to Cart + Options" at the start

    * Tests: Improve typing

    * Tests: Use wpCLI to add products before each tests instead of adding them in sample_products.xml where they affected all tests

    * Improve Linting and typing

    * Tests: Remove duplicate updateSingleProductTemplate and setAddToCartWithOptionsBlockAttributes function calls

    * Tests: Rename selectBlockAttribute to selectVariationSelectorBlockAttribute

    * Tests: Reword comments, use accurate names, and improve typing

    * Tests: Fix the disabledAttributesAction block attribute's main test

    * Tests: More linting

    * Make disabledAttributesAction an enum instead of a string in block.json

    * Tests: attributes and productVariations passed to wpCLI were not properly formatted

    * Tests: Linting

    * Specify the values of enum disabledAttributesAction are of type string

    ---------

    Signed-off-by: CAT the Tech <trottier.charlesantoine@gmail.com>
    Co-authored-by: Albert Juhé Lluveras <contact@albertjuhe.com>
    Co-authored-by: Sam Seay <samueljseay@gmail.com>

diff --git a/plugins/woocommerce/changelog/CATtheTech-autoselect-woo10 b/plugins/woocommerce/changelog/CATtheTech-autoselect-woo10
new file mode 100644
index 0000000000..279e68d430
--- /dev/null
+++ b/plugins/woocommerce/changelog/CATtheTech-autoselect-woo10
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Modified the Add to Cart + Options block to auto-select attribute fields, and display attributes in conflict
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/block.json b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/block.json
index 50bc8448c6..cc5caee3a8 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/block.json
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/block.json
@@ -11,6 +11,15 @@
 		"optionStyle": {
 			"type": "string",
 			"enum": [ "pills", "dropdown" ]
+		},
+		"autoselect": {
+			"type": "boolean",
+			"default": false
+		},
+		"disabledAttributesAction": {
+			"type": "string",
+			"enum": [ "disable", "hide" ],
+			"default": "disable"
 		}
 	},
 	"textdomain": "woocommerce",
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/edit.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/edit.tsx
index 45420b7a8b..966af28c78 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/edit.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/edit.tsx
@@ -8,6 +8,7 @@ import {
 	Disabled,
 	PanelBody,
 	SelectControl,
+	ToggleControl,
 	__experimentalToggleGroupControl as ToggleGroupControl,
 	__experimentalToggleGroupControlOption as ToggleGroupControlOption,
 } from '@wordpress/components';
@@ -23,6 +24,8 @@ import { useThemeColors } from '../../../../shared/hooks/use-theme-colors';
 interface Attributes {
 	className?: string;
 	optionStyle?: 'pills' | 'dropdown';
+	autoselect: boolean;
+	disabledAttributesAction: 'disable' | 'hide';
 }

 function Pills( {
@@ -61,7 +64,8 @@ export default function AttributeOptionsEdit(
 	props: BlockEditProps< Attributes >
 ) {
 	const { attributes, setAttributes } = props;
-	const { className, optionStyle } = attributes;
+	const { className, optionStyle, autoselect, disabledAttributesAction } =
+		attributes;

 	const blockProps = useBlockProps( {
 		className,
@@ -120,6 +124,55 @@ export default function AttributeOptionsEdit(
 						/>
 					</ToggleGroupControl>
 				</PanelBody>
+				<PanelBody title={ __( 'Auto-select', 'woocommerce' ) }>
+					<ToggleControl
+						label={ __(
+							'Auto-select when only one attribute is compatible',
+							'woocommerce'
+						) }
+						help={ __(
+							'This controls whether attributes will be auto-selected once upon loading the page and when an attribute is changed by the user. Only attributes with a single compatible value will be auto-selected.',
+							'woocommerce'
+						) }
+						checked={ autoselect }
+						onChange={ () =>
+							setAttributes( { autoselect: ! autoselect } )
+						}
+						__nextHasNoMarginBottom
+					/>
+					<SelectControl
+						label={ __(
+							'Values in conflict with current selection',
+							'woocommerce'
+						) }
+						help={ __(
+							'This controls what to do with attribute values that conflict with the current selection.',
+							'woocommerce'
+						) }
+						value={ disabledAttributesAction }
+						options={ [
+							{
+								label: __( 'Hidden', 'woocommerce' ),
+								value: 'hide',
+							},
+							{
+								label: __(
+									'Grayed-out/crossed-out and disabled',
+									'woocommerce'
+								),
+								value: 'disable',
+							},
+						] }
+						onChange={ ( value ) =>
+							setAttributes( {
+								disabledAttributesAction: value as
+									| 'disable'
+									| 'hide',
+							} )
+						}
+						__nextHasNoMarginBottom
+					/>
+				</PanelBody>
 			</InspectorControls>

 			<Disabled>
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/style.scss b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/style.scss
index 2067adecf2..0250b5a71d 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/style.scss
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/attribute-options/style.scss
@@ -41,6 +41,11 @@
 		);
 	}

+	/* When the pill button is hidden, we also need to hide the label (&) */
+	&:has(.wc-block-add-to-cart-with-options-variation-selector-attribute-options__pill-input[hidden]) {
+		display: none;
+	}
+
 	&:where(:focus-within) {
 		outline: 1px solid var(--pill-color);
 		outline-offset: 1px;
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/frontend.ts b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/frontend.ts
index 2cf4a2470c..7b155e8de9 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/frontend.ts
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/add-to-cart-with-options/variation-selector/frontend.ts
@@ -7,7 +7,10 @@ import {
 	getConfig,
 	getElement,
 } from '@wordpress/interactivity';
-import { SelectedAttributes } from '@woocommerce/stores/woocommerce/cart';
+import {
+	SelectedAttributes,
+	VariationData,
+} from '@woocommerce/stores/woocommerce/cart';
 import type { ChangeEvent } from 'react';
 import type { ProductDataStore } from '@woocommerce/stores/woocommerce/product-data';

@@ -19,6 +22,7 @@ import type {
 	AddToCartWithOptionsStore,
 	Context as AddToCartWithOptionsStoreContext,
 } from '../frontend';
+import type { NormalizedProductData } from '../types';
 import { getMatchedVariation } from '../../../base/utils/variations/get-matched-variation';
 import setStyles from './set-styles';

@@ -33,6 +37,7 @@ type Context = AddToCartWithOptionsStoreContext & {
 	selectedValue: string | null;
 	option: Option;
 	options: Option[];
+	autoselect: boolean;
 };

 // Set selected pill styles for proper contrast.
@@ -134,6 +139,40 @@ const isAttributeValueValid = ( {
 	} );
 };

+/**
+ * Return the product attributes and options.
+ */
+const getProductAttributesAndOptions = (
+	productObject: NormalizedProductData | null
+): Record< string, string[] > => {
+	if ( ! productObject?.variations ) {
+		return {};
+	}
+
+	const variations: VariationData[] = Object.values(
+		productObject.variations
+	);
+	const productAttributesAndOptions = {} as Record< string, string[] >;
+	variations.forEach( ( variation: VariationData ) => {
+		if ( ! variation?.attributes ) {
+			return;
+		}
+		Object.entries( variation.attributes ).forEach( ( [ key, value ] ) => {
+			if ( typeof key !== 'string' || typeof value !== 'string' ) {
+				return;
+			}
+			if ( ! Array.isArray( productAttributesAndOptions[ key ] ) ) {
+				productAttributesAndOptions[ key ] = [];
+			}
+			if ( ! productAttributesAndOptions[ key ].includes( value ) ) {
+				productAttributesAndOptions[ key ].push( value );
+			}
+		} );
+	} );
+
+	return productAttributesAndOptions;
+};
+
 export type VariableProductAddToCartWithOptionsStore =
 	AddToCartWithOptionsStore & {
 		state: {
@@ -148,6 +187,10 @@ export type VariableProductAddToCartWithOptionsStore =
 			handleDropdownChange: (
 				event: ChangeEvent< HTMLSelectElement >
 			) => void;
+			autoselectAttributes: ( args: {
+				includedAttributes?: string[];
+				excludedAttributes?: string[];
+			} ) => void;
 		};
 		callbacks: {
 			setDefaultSelectedAttribute: () => void;
@@ -175,8 +218,15 @@ const { actions, state } = store< VariableProductAddToCartWithOptionsStore >(
 				return context.selectedAttributes;
 			},
 			get isOptionSelected() {
-				const { selectedValue, option } = getContext< Context >();
-				return selectedValue === option.value;
+				const { selectedAttributes, option, name } =
+					getContext< Context >();
+
+				return selectedAttributes.some( ( attrObject ) => {
+					return (
+						attrObject.attribute === name &&
+						attrObject.value === option.value
+					);
+				} );
 			},
 			get isOptionDisabled() {
 				const { name, option, selectedAttributes } =
@@ -231,21 +281,78 @@ const { actions, state } = store< VariableProductAddToCartWithOptionsStore >(
 				}
 			},
 			handlePillClick() {
-				if ( state.isOptionDisabled ) {
-					return;
-				}
 				const context = getContext< Context >();
-				if ( context.selectedValue === context.option.value ) {
+
+				if ( state.isOptionSelected ) {
 					context.selectedValue = '';
 				} else {
 					context.selectedValue = context.option.value;
 				}
 				actions.setAttribute( context.name, context.selectedValue );
+				if ( context.selectedValue !== '' ) {
+					actions.autoselectAttributes( {
+						excludedAttributes: [ context.name ],
+					} );
+				}
 			},
 			handleDropdownChange( event: ChangeEvent< HTMLSelectElement > ) {
 				const context = getContext< Context >();
 				context.selectedValue = event.currentTarget.value;
 				actions.setAttribute( context.name, context.selectedValue );
+				if ( context.selectedValue !== '' ) {
+					actions.autoselectAttributes( {
+						excludedAttributes: [ context.name ],
+					} );
+				}
+			},
+			autoselectAttributes( {
+				includedAttributes = [],
+				excludedAttributes = [],
+			}: {
+				includedAttributes?: Array< string >;
+				excludedAttributes?: Array< string >;
+			} = {} ) {
+				const { autoselect, selectedAttributes } =
+					getContext< Context >();
+
+				if ( ! autoselect ) {
+					return;
+				}
+
+				const productObject: NormalizedProductData | null =
+					getProductData( productDataState.productId, [] );
+				if ( ! productObject ) {
+					return;
+				}
+				const productAttributesAndOptions: Record< string, string[] > =
+					getProductAttributesAndOptions( productObject );
+				Object.entries( productAttributesAndOptions ).forEach(
+					( [ attribute, options ] ) => {
+						if (
+							includedAttributes.length !== 0 &&
+							! includedAttributes.includes( attribute )
+						) {
+							return;
+						}
+						if (
+							excludedAttributes.length !== 0 &&
+							excludedAttributes.includes( attribute )
+						) {
+							return;
+						}
+						const validOptions = options.filter( ( option ) =>
+							isAttributeValueValid( {
+								attributeName: attribute,
+								attributeValue: option,
+								selectedAttributes,
+							} )
+						);
+						if ( validOptions.length === 1 ) {
+							const validOption = validOptions[ 0 ];
+							actions.setAttribute( attribute, validOption );
+						}
+					}
+				);
 			},
 		},
 		callbacks: {
@@ -255,6 +362,9 @@ const { actions, state } = store< VariableProductAddToCartWithOptionsStore >(
 				if ( context.selectedValue ) {
 					actions.setAttribute( context.name, context.selectedValue );
 				}
+				actions.autoselectAttributes( {
+					includedAttributes: [ context.name ],
+				} );
 			},
 			setSelectedVariationId: () => {
 				const { products } = getConfig( 'woocommerce' );
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts b/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts
index ca07f623af..2e53309bb4 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.block_theme.spec.ts
@@ -1,7 +1,7 @@
 /**
  * External dependencies
  */
-import { test as base, expect, wpCLI } from '@woocommerce/e2e-utils';
+import { test as base, expect, wpCLI, Editor } from '@woocommerce/e2e-utils';

 /**
  * Internal dependencies
@@ -1026,4 +1026,421 @@ test.describe( 'Add to Cart + Options Block', () => {
 			editor.canvas.getByLabel( 'Block: Product Gallery' )
 		).toBeVisible();
 	} );
+
+	test.describe( 'autoselect behavior', () => {
+		const productSlug = 'autoselect-t-shirt';
+		const productName = 'Autoselect T-shirt';
+		const productPermalink = '/product/' + productSlug;
+		const productPrice = '13.99';
+		const productAttributes: {
+			name: string;
+			options: string[];
+			variation: boolean;
+			visible: boolean;
+		}[] = [
+			{
+				name: 'Type',
+				options: [ 'T-shirt' ],
+				variation: true,
+				visible: true,
+			},
+			{
+				name: 'Color',
+				options: [ 'Red', 'Blue', 'Green' ],
+				variation: true,
+				visible: true,
+			},
+			{
+				name: 'Size',
+				options: [ 'S', 'L', 'XL' ],
+				variation: true,
+				visible: true,
+			},
+		];
+		const productVariations: {
+			attributes: {
+				name: string;
+				option: string;
+			}[];
+		}[] = [
+			{
+				attributes: [
+					{
+						name: 'Type',
+						option: 'T-shirt',
+					},
+					{
+						name: 'Color',
+						option: 'Green',
+					},
+					{
+						name: 'Size',
+						option: 'S',
+					},
+				],
+			},
+			{
+				attributes: [
+					{
+						name: 'Type',
+						option: 'T-shirt',
+					},
+					{
+						name: 'Color',
+						option: 'Red',
+					},
+					{
+						name: 'Size',
+						option: 'L',
+					},
+				],
+			},
+			{
+				attributes: [
+					{
+						name: 'Type',
+						option: 'T-shirt',
+					},
+					{
+						name: 'Color',
+						option: 'Red',
+					},
+					{
+						name: 'Size',
+						option: 'XL',
+					},
+				],
+			},
+			{
+				attributes: [
+					{
+						name: 'Type',
+						option: 'T-shirt',
+					},
+					{
+						name: 'Color',
+						option: 'Blue',
+					},
+					{
+						name: 'Size',
+						option: 'XL',
+					},
+				],
+			},
+		];
+
+		async function setAddToCartWithOptionsBlockAttributes(
+			pageObject: AddToCartWithOptionsPage,
+			editor: Editor,
+			{
+				optionStyle = 'Pills',
+				autoselect = false,
+				disabledAttributesAction = 'disable',
+			}: {
+				optionStyle?: 'Pills' | 'Dropdown';
+				autoselect?: boolean;
+				disabledAttributesAction?: 'disable' | 'hide';
+			} = {}
+		) {
+			const page = editor.page;
+			let isOnlyCurrentEntityDirty = true;
+
+			await pageObject.switchProductType( 'Variable product' );
+			await page.getByRole( 'tab', { name: 'Block' } ).click();
+			const addToCartWithOptionsBlock = editor.canvas.getByLabel(
+				'Block: Add to Cart + Options'
+			);
+			await addToCartWithOptionsBlock.click();
+			await addToCartWithOptionsBlock
+				.getByLabel( 'Block: Variation Selector: Attribute Options' )
+				.first()
+				.click();
+
+			const optionStyleInput = page.getByRole( 'radio', {
+				name: optionStyle,
+				exact: true,
+			} );
+			if ( ! ( await optionStyleInput.isChecked() ) ) {
+				isOnlyCurrentEntityDirty = false;
+				await optionStyleInput.click();
+			}
+
+			const autoselectInput = page.getByRole( 'checkbox', {
+				name: 'Auto-select when only one attribute is compatible',
+			} );
+			const disabledAttributesActionInput =
+				page.getByLabel( 'Values in conflict' );
+			if (
+				( await autoselectInput.isChecked() ) !== autoselect ||
+				( await disabledAttributesActionInput.inputValue() ) !==
+					disabledAttributesAction
+			) {
+				isOnlyCurrentEntityDirty = false;
+			}
+			await autoselectInput.setChecked( autoselect );
+			await disabledAttributesActionInput.selectOption( {
+				value: disabledAttributesAction,
+			} );
+			if (
+				await page
+					.getByRole( 'region', {
+						name: 'Editor top bar',
+					} )
+					.getByRole( 'button', {
+						name: 'Save',
+						exact: true,
+					} )
+					.isEnabled()
+			) {
+				await editor.saveSiteEditorEntities( {
+					isOnlyCurrentEntityDirty,
+				} );
+			}
+		}
+
+		test.beforeEach( async () => {
+			const cliOutput = await wpCLI(
+				`wc product create --user=1 --slug="${ productSlug }" --name="${ productName }" --type="variable" --attributes='${ JSON.stringify(
+					productAttributes
+				) }'`
+			);
+			const match: RegExpMatchArray | null = cliOutput.stdout.match(
+				/Success:\s+Created\s+product\s+(\d+)\.\n?$/
+			);
+			const productId: string | null = match ? match[ 1 ] : null;
+			if ( ! productId ) {
+				throw new Error(
+					`No productId found, cliOutput: ${ JSON.stringify(
+						cliOutput,
+						null,
+						2
+					) }`
+				);
+			}
+
+			for ( const productVariation of productVariations ) {
+				await wpCLI(
+					`wc product_variation create --user=1 "${ productId }" --regular_price="${ productPrice }" --attributes='${ JSON.stringify(
+						productVariation.attributes
+					) }'`
+				);
+			}
+		} );
+
+		for ( const optionStyle of [ 'Pills', 'Dropdown' ] as (
+			| 'Pills'
+			| 'Dropdown'
+		 )[] ) {
+			// eslint-disable-next-line playwright/expect-expect
+			test( `${ optionStyle }: Test the autoselect block attribute`, async ( {
+				page,
+				pageObject,
+				editor,
+			} ) => {
+				await pageObject.updateSingleProductTemplate();
+				await setAddToCartWithOptionsBlockAttributes(
+					pageObject,
+					editor,
+					{ optionStyle }
+				);
+
+				await test.step( `${ optionStyle }: Expect NOTHING to be auto-selected (on page load)`, async () => {
+					await page.goto( productPermalink );
+
+					await pageObject.expectSelectedAttributes(
+						productAttributes,
+						{ Type: '', Color: '', Size: '' },
+						optionStyle
+					);
+				} );
+
+				await test.step( `${ optionStyle }: Expect attributes to NOT auto-select when user selects something`, async () => {
+					await page.goto( productPermalink );
+
+					await pageObject.selectVariationSelectorOptionsBlockAttribute(
+						'Color',
+						'Blue',
+						optionStyle
+					);
+
+					// Expect nothing to be auto-selected
+					await pageObject.expectSelectedAttributes(
+						productAttributes,
+						{ Type: '', Color: 'Blue', Size: '' },
+						optionStyle
+					);
+				} );
+
+				await test.step( `${ optionStyle }: Set the autoselect setting to true`, async () => {
+					await pageObject.updateSingleProductTemplate();
+					await setAddToCartWithOptionsBlockAttributes(
+						pageObject,
+						editor,
+						{ optionStyle, autoselect: true }
+					);
+				} );
+
+				await test.step( `${ optionStyle }: Expect only the Type attribute to be auto-selected (on page load)`, async () => {
+					await page.goto( productPermalink );
+
+					// Expect the Type attribute to be auto-selected (on page load) to "T-shirt", the rest of the attributes should not be selected.
+					await pageObject.expectSelectedAttributes(
+						productAttributes,
+						{ Type: 'T-shirt', Color: '', Size: '' },
+						optionStyle
+					);
+				} );
+
+				await test.step( `${ optionStyle }: Expect attributes to auto-select when user selects something`, async () => {
+					await page.goto( productPermalink );
+
+					// By setting the Color to "Blue", we expect the Type attribute to be auto-selected to "T-shirt", and the Size to "XL".
+					await pageObject.selectVariationSelectorOptionsBlockAttribute(
+						'Color',
+						'Blue',
+						optionStyle
+					);
+
+					await pageObject.expectSelectedAttributes(
+						productAttributes,
+						{ Type: 'T-shirt', Color: 'Blue', Size: 'XL' },
+						optionStyle
+					);
+				} );
+			} );
+			test( `${ optionStyle }: Test the disabledAttributesAction block attribute`, async ( {
+				page,
+				pageObject,
+				editor,
+			} ) => {
+				await test.step( `${ optionStyle }: Set the disabledAttributesAction block attribute to "disable"`, async () => {
+					await pageObject.updateSingleProductTemplate();
+					await setAddToCartWithOptionsBlockAttributes(
+						pageObject,
+						editor,
+						{
+							optionStyle,
+							disabledAttributesAction: 'disable',
+						}
+					);
+				} );
+				await test.step( `${ optionStyle }: Expect invalid options to be disabled (by prop) and visible`, async () => {
+					await page.goto( productPermalink );
+
+					// By setting the Color to "Blue", the only possible Size remaining is "XL".
+					await pageObject.selectVariationSelectorOptionsBlockAttribute(
+						'Color',
+						'Blue',
+						optionStyle
+					);
+
+					await expect(
+						page
+							.getByLabel( 'Size' )
+							.getByText( 'L', { exact: true } )
+					).toBeDisabled();
+					await expect(
+						page
+							.getByLabel( 'Size' )
+							.getByText( 'L', { exact: true } )
+					).not.toHaveAttribute( 'hidden' );
+				} );
+
+				await test.step( `${ optionStyle }: Set the disabledAttributesAction block attribute to "hide"`, async () => {
+					await pageObject.updateSingleProductTemplate();
+					await setAddToCartWithOptionsBlockAttributes(
+						pageObject,
+						editor,
+						{
+							optionStyle,
+							disabledAttributesAction: 'hide',
+						}
+					);
+				} );
+				await test.step( `${ optionStyle }: Expect invalid options to be isabled (by prop) and hidden`, async () => {
+					await page.goto( productPermalink );
+
+					// By setting the Color to "Blue", the only possible Size remaining is "XL".
+					await pageObject.selectVariationSelectorOptionsBlockAttribute(
+						'Color',
+						'Blue',
+						optionStyle
+					);
+
+					await expect(
+						page
+							.getByLabel( 'Size' )
+							.getByText( 'L', { exact: true } )
+					).toBeDisabled();
+					await expect(
+						page
+							.getByLabel( 'Size' )
+							.getByText( 'L', { exact: true } )
+					).toBeHidden();
+				} );
+			} );
+			// eslint-disable-next-line playwright/expect-expect
+			test( `${ optionStyle }: Combining autoselect and disabledAttributesAction block attributes should work`, async ( {
+				page,
+				pageObject,
+				editor,
+			} ) => {
+				for ( const disabledAttributesAction of [
+					'disable',
+					'hide',
+				] as ( 'disable' | 'hide' )[] ) {
+					await pageObject.updateSingleProductTemplate();
+
+					await test.step( `${ optionStyle }: Set the disabledAttributesAction block attribute to "${ disabledAttributesAction }"`, async () => {
+						await setAddToCartWithOptionsBlockAttributes(
+							pageObject,
+							editor,
+							{
+								autoselect: true,
+								optionStyle,
+								disabledAttributesAction,
+							}
+						);
+					} );
+					await test.step( `disabledAttributesAction === ${ disabledAttributesAction }: Expect options to be properly auto-selected`, async () => {
+						await page.goto( productPermalink );
+
+						// By selecting the Color to "Blue", the only possible Size remaining is "XL".
+						await pageObject.selectVariationSelectorOptionsBlockAttribute(
+							'Color',
+							'Blue',
+							optionStyle
+						);
+						// Now, we deselect the Color.
+						await pageObject.selectVariationSelectorOptionsBlockAttribute(
+							'Color',
+							'',
+							optionStyle
+						);
+						// Now, the attributes should look like this:
+						// Type: T-shirt
+						// Color: ''
+						// Size: XL
+						// Because the Size is XL, the only Colors possible are Red and Blue.
+						// Now if we select Size: S, the Color should auto-select to Green.
+						await pageObject.selectVariationSelectorOptionsBlockAttribute(
+							'Size',
+							'S',
+							optionStyle
+						);
+						// Now, the options should look like this:
+						// Type: T-shirt
+						// Color: Green
+						// Size: S
+
+						await pageObject.expectSelectedAttributes(
+							productAttributes,
+							{ Type: 'T-shirt', Color: 'Green', Size: 'S' },
+							optionStyle
+						);
+					} );
+				}
+			} );
+		}
+	} );
 } );
diff --git a/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.page.ts b/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.page.ts
index e22f0ac2ae..a193e036e9 100644
--- a/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.page.ts
+++ b/plugins/woocommerce/client/blocks/tests/e2e/tests/add-to-cart-with-options/add-to-cart-with-options.page.ts
@@ -2,7 +2,12 @@
  * External dependencies
  */
 import { Page } from '@playwright/test';
-import { Editor, Admin, BLOCK_THEME_SLUG } from '@woocommerce/e2e-utils';
+import {
+	expect,
+	Editor,
+	Admin,
+	BLOCK_THEME_SLUG,
+} from '@woocommerce/e2e-utils';

 class AddToCartWithOptionsPage {
 	private page: Page;
@@ -27,9 +32,16 @@ class AddToCartWithOptionsPage {

 	async switchProductType( productType: string ) {
 		await this.page.getByRole( 'tab', { name: 'Template' } ).click();
-		await this.page
-			.getByRole( 'button', { name: 'Product Type', exact: true } )
-			.click();
+		const productTypePanel = this.page.getByRole( 'button', {
+			name: 'Product Type',
+			exact: true,
+		} );
+		if (
+			( await productTypePanel.getAttribute( 'aria-expanded' ) ) !==
+			'true'
+		) {
+			await productTypePanel.click();
+		}
 		await this.page
 			.getByLabel( 'Type switcher' )
 			.selectOption( { label: productType } );
@@ -76,13 +88,15 @@ class AddToCartWithOptionsPage {
 		const addToCartFormBlock = await this.editor.getBlockByName(
 			'woocommerce/add-to-cart-form'
 		);
-		await this.editor.selectBlocks( addToCartFormBlock );
+		if ( await addToCartFormBlock.isVisible() ) {
+			await this.editor.selectBlocks( addToCartFormBlock );

-		await this.page
-			.getByRole( 'button', {
-				name: 'Use the Add to Cart + Options block',
-			} )
-			.click();
+			await this.page
+				.getByRole( 'button', {
+					name: 'Use the Add to Cart + Options block',
+				} )
+				.click();
+		}
 	}

 	async updateSingleProductTemplate() {
@@ -120,6 +134,88 @@ class AddToCartWithOptionsPage {

 		await this.editor.publishAndVisitPost();
 	}
+
+	async selectVariationSelectorOptionsBlockAttribute(
+		attributeName: string,
+		attributeValue: string,
+		optionStyle: 'Pills' | 'Dropdown'
+	) {
+		if ( optionStyle === 'Dropdown' ) {
+			await this.page
+				.getByLabel( attributeName )
+				.selectOption( attributeValue );
+			return;
+		}
+		if ( attributeValue !== '' ) {
+			await this.page
+				.getByLabel( attributeName )
+				.getByText( attributeValue )
+				.click();
+			return;
+		}
+		await this.page
+			.getByLabel( attributeName )
+			.locator( 'label:has(:checked)' )
+			.click();
+	}
+
+	async expectSelectedAttributes(
+		productAttributes: {
+			name: string;
+			options: string[];
+			variation: boolean;
+			visible: boolean;
+		}[],
+		expectedValues: Record< string, string | RegExp > = {},
+		optionStyle: 'Pills' | 'Dropdown'
+	) {
+		for ( let {
+			name: attributeName,
+			options: attributeValues,
+		} of productAttributes ) {
+			const attributeNameLocator = this.page.getByLabel( attributeName, {
+				exact: true,
+			} );
+			if ( optionStyle === 'Dropdown' ) {
+				let expectedValue: string | RegExp;
+				if (
+					attributeName in expectedValues &&
+					expectedValues[ attributeName ] !== ''
+				) {
+					expectedValue = expectedValues[ attributeName ];
+				} else {
+					expectedValue = '';
+				}
+				await expect( attributeNameLocator ).toHaveValue(
+					expectedValue
+				);
+				return;
+			}
+			if (
+				attributeName in expectedValues &&
+				expectedValues[ attributeName ] !== ''
+			) {
+				attributeValues = attributeValues.filter(
+					( item ) => item !== expectedValues[ attributeName ]
+				); // Omit attributeName
+				await expect(
+					attributeNameLocator.getByLabel(
+						expectedValues[ attributeName ],
+						{ exact: true }
+					)
+				).toBeChecked();
+			}
+			if ( attributeValues.length ) {
+				for ( const attributeValue of attributeValues ) {
+					await expect(
+						attributeNameLocator.getByLabel( attributeValue, {
+							exact: true,
+						} )
+					).not.toBeChecked();
+				}
+			}
+		}
+	}
 }

 export default AddToCartWithOptionsPage;
diff --git a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/VariationSelectorAttributeOptions.php b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/VariationSelectorAttributeOptions.php
index 96d70c60aa..fd637c3ba9 100644
--- a/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/VariationSelectorAttributeOptions.php
+++ b/plugins/woocommerce/src/Blocks/BlockTypes/AddToCartWithOptions/VariationSelectorAttributeOptions.php
@@ -139,9 +139,11 @@ class VariationSelectorAttributeOptions extends AbstractBlock {
 	 * @return string The pills.
 	 */
 	protected function render_pills( $attributes, $content, $block ) {
-		$attribute_id    = $block->context['woocommerce/attributeId'];
-		$attribute_slug  = wc_variation_attribute_name( $block->context['woocommerce/attributeName'] );
-		$attribute_terms = $block->context['woocommerce/attributeTerms'];
+		$attribute_id               = $block->context['woocommerce/attributeId'];
+		$attribute_slug             = wc_variation_attribute_name( $block->context['woocommerce/attributeName'] );
+		$attribute_terms            = $block->context['woocommerce/attributeTerms'];
+		$autoselect                 = $attributes['autoselect'] ?? false;
+		$disabled_attributes_action = $attributes['disabledAttributesAction'] ?? 'disable';

 		wp_interactivity_state(
 			'woocommerce/add-to-cart-with-options',
@@ -166,6 +168,7 @@ class VariationSelectorAttributeOptions extends AbstractBlock {
 						'value'                  => $attribute_term['value'],
 						'data-wp-bind--checked'  => 'state.isOptionSelected',
 						'data-wp-bind--disabled' => 'state.isOptionDisabled',
+						'data-wp-bind--hidden'   => 'hide' === $disabled_attributes_action ? 'state.isOptionDisabled' : null,
 						'data-wp-watch'          => 'callbacks.watchSelected',
 						'data-wp-on--click'      => 'actions.handlePillClick',
 						'data-wp-on--keydown'    => 'actions.handleKeyDown',
@@ -192,6 +195,7 @@ class VariationSelectorAttributeOptions extends AbstractBlock {
 						'options'       => $attribute_terms,
 						'selectedValue' => $this->get_default_selected_attribute( $attribute_slug, $attribute_terms ),
 						'focused'       => '',
+						'autoselect'    => $autoselect,
 					),
 					'data-wp-init'    => 'callbacks.setDefaultSelectedAttribute',
 				),
@@ -223,13 +227,17 @@ class VariationSelectorAttributeOptions extends AbstractBlock {
 			$attribute_terms
 		);

-		$selected_attribute = $this->get_default_selected_attribute( $attribute_slug, $attribute_terms );
+		$selected_attribute         = $this->get_default_selected_attribute( $attribute_slug, $attribute_terms );
+		$autoselect                 = $attributes['autoselect'] ?? false;
+		$disabled_attributes_action = $attributes['disabledAttributesAction'] ?? 'disable';

 		$options = '';
 		foreach ( $attribute_terms as $attribute_term ) {
 			$option_attributes = array(
 				'value'                  => $attribute_term['value'],
+				'data-wp-bind--selected' => 'state.isOptionSelected',
 				'data-wp-bind--disabled' => 'state.isOptionDisabled',
+				'data-wp-bind--hidden'   => 'hide' === $disabled_attributes_action ? 'state.isOptionDisabled' : null,
 				'data-wp-context'        => array(
 					'option'  => $attribute_term,
 					'name'    => $attribute_slug,
@@ -260,6 +268,7 @@ class VariationSelectorAttributeOptions extends AbstractBlock {
 						'name'          => $attribute_slug,
 						'options'       => $attribute_terms,
 						'selectedValue' => $selected_attribute,
+						'autoselect'    => $autoselect,
 					),
 					'data-wp-init'       => 'callbacks.setDefaultSelectedAttribute',
 					'data-wp-on--change' => 'actions.handleDropdownChange',