Commit 3e9831e93c1 for woocommerce

commit 3e9831e93c1fa7930aae55d01c6bb95507a9c38d
Author: Luigi Teschio <gigitux@gmail.com>
Date:   Fri Apr 24 10:35:32 2026 +0200

    [DataViews - All Products] Integrate field registry (#64273)

    * Add shared controls for products app fields

    * Add pricing fields to products app

    * Add inventory fields to products app

    * Add organization fields to products app

    * Add media and SEO fields to products app

    * Integrate products app field registry

    * remove not necessary code

    * clean up code

    * Update packages/js/experimental-products-app/tsconfig.json

    Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

    * Update packages/js/experimental-products-app/src/fields/components/date-picker.tsx

    Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

    * Fix list-item: import style.scss and default alt to empty string

    Agent-Logs-Url: https://github.com/woocommerce/woocommerce/sessions/f3cf7f22-fc05-4c03-a256-0b68bdb81cd5

    Co-authored-by: gigitux <4463174+gigitux@users.noreply.github.com>

    * fix build

    * fix build

    * lint code

    * fix tsconfig

    * lint code

    * Apply suggestion from @Copilot

    Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

    * lint code

    * fix ts errors

    * fix ts errors

    * fix types

    * fix naming

    * remove test

    * remove not necessary field

    * remove no existent style

    * improve types

    ---------

    Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
    Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
    Co-authored-by: gigitux <4463174+gigitux@users.noreply.github.com>

diff --git a/packages/js/experimental-products-app/changelog/add-products-fields-integration b/packages/js/experimental-products-app/changelog/add-products-fields-integration
new file mode 100644
index 00000000000..0cf4a143696
--- /dev/null
+++ b/packages/js/experimental-products-app/changelog/add-products-fields-integration
@@ -0,0 +1,4 @@
+Significance: patch
+Type: add
+
+Integrate products app field modules into the product list
diff --git a/packages/js/experimental-products-app/src/fields/images/style.scss b/packages/js/experimental-products-app/src/fields/images/style.scss
index fe1b79ba7c6..a20e723763e 100644
--- a/packages/js/experimental-products-app/src/fields/images/style.scss
+++ b/packages/js/experimental-products-app/src/fields/images/style.scss
@@ -1,5 +1,3 @@
-@use "@wordpress/base-styles/variables";
-
 .woocommerce-fields-control__featured-image {
 	display: grid;
 	gap: 10px;
@@ -105,8 +103,8 @@
 	border: 1px solid var( --wpds-color-stroke-interactive-neutral );
 	background-color: var( --wpds-color-bg-surface-neutral-strong );
 	overflow: hidden;
-	width: variables.$button-size-small;
-	height: variables.$button-size-small;
+	width: $button-size-small;
+	height: $button-size-small;
 	opacity: 0;
 	transition: opacity 0.2s ease-in-out;
 	z-index: 1;
diff --git a/packages/js/experimental-products-app/src/product-list/fields.tsx b/packages/js/experimental-products-app/src/product-list/fields.tsx
index fce43ad2738..dd48aab940a 100644
--- a/packages/js/experimental-products-app/src/product-list/fields.tsx
+++ b/packages/js/experimental-products-app/src/product-list/fields.tsx
@@ -1,64 +1,146 @@
 /**
  * External dependencies
  */
-import { createElement, Fragment } from '@wordpress/element';
-import { Product } from '@woocommerce/data';
-import { __ } from '@wordpress/i18n';
-import { Field } from '@wordpress/dataviews';
+import type { Field } from '@wordpress/dataviews';

 /**
  * Internal dependencies
  */
-import { OPERATOR_IS } from '../constants';
+import { fieldExtensions as buttonTextFieldExtensions } from '../fields/button_text/field';
+import { fieldExtensions as catalogVisibilityFieldExtensions } from '../fields/catalog_visibility/field';
+import { fieldExtensions as categoriesFieldExtensions } from '../fields/categories/field';
+import { fieldExtensions as crossSellIdsFieldExtensions } from '../fields/cross_sell_ids/field';
+import { fieldExtensions as dateOnSaleFromFieldExtensions } from '../fields/date_on_sale_from/field';
+import { fieldExtensions as dateOnSaleToFieldExtensions } from '../fields/date_on_sale_to/field';
+import { fieldExtensions as descriptionFieldExtensions } from '../fields/description/field';
+import { fieldExtensions as downloadableFieldExtensions } from '../fields/downloadable/field';
+import { fieldExtensions as downloadableCountFieldExtensions } from '../fields/downloadable_count/field';
+import { fieldExtensions as externalUrlFieldExtensions } from '../fields/external_url/field';
+import { fieldExtensions as featuredFieldExtensions } from '../fields/featured/field';
+import { fieldExtensions as heightFieldExtensions } from '../fields/height/field';
+import { fieldExtensions as imagesFieldExtensions } from '../fields/images/field';
+import { fieldExtensions as imagesCountFieldExtensions } from '../fields/images_count/field';
+import { fieldExtensions as inventorySummaryFieldExtensions } from '../fields/inventory_summary/field';
+import { fieldExtensions as lengthFieldExtensions } from '../fields/length/field';
+import { fieldExtensions as linkedProductsCountFieldExtensions } from '../fields/linked_products_count/field';
+import { fieldExtensions as manageStockFieldExtensions } from '../fields/manage_stock/field';
+import { fieldExtensions as nameFieldExtensions } from '../fields/name/field';
+import { fieldExtensions as onSaleFieldExtensions } from '../fields/on_sale/field';
+import { fieldExtensions as organizationSummaryFieldExtensions } from '../fields/organization_summary/field';
+import { fieldExtensions as priceFieldExtensions } from '../fields/price/field';
+import { fieldExtensions as priceSummaryFieldExtensions } from '../fields/price_summary/field';
+import { fieldExtensions as productStatusFieldExtensions } from '../fields/product_status/field';
+import { fieldExtensions as regularPriceFieldExtensions } from '../fields/regular_price/field';
+import { fieldExtensions as salePriceFieldExtensions } from '../fields/sale_price/field';
+import { fieldExtensions as scheduleSaleFieldExtensions } from '../fields/schedule_sale/field';
+import { fieldExtensions as shippingClassFieldExtensions } from '../fields/shipping_class/field';
+import { fieldExtensions as shippingSummaryFieldExtensions } from '../fields/shipping_summary/field';
+import { fieldExtensions as shortDescriptionFieldExtensions } from '../fields/short_description/field';
+import { fieldExtensions as skuFieldExtensions } from '../fields/sku/field';
+import { fieldExtensions as stockFieldExtensions } from '../fields/stock/field';
+import { fieldExtensions as stockQuantityFieldExtensions } from '../fields/stock_quantity/field';
+import { fieldExtensions as tagsFieldExtensions } from '../fields/tags/field';
+import { fieldExtensions as taxStatusFieldExtensions } from '../fields/tax_status/field';
+import type { ProductEntityRecord } from '../fields/types';
+import { fieldExtensions as upsellIdsFieldExtensions } from '../fields/upsell_ids/field';
+import { fieldExtensions as visibilitySummaryFieldExtensions } from '../fields/visibility_summary/field';
+import { fieldExtensions as weightFieldExtensions } from '../fields/weight/field';
+import { fieldExtensions as widthFieldExtensions } from '../fields/width/field';

-const STATUSES = [
-	{ value: 'draft', label: __( 'Draft', 'woocommerce' ) },
-	{ value: 'future', label: __( 'Scheduled', 'woocommerce' ) },
-	{ value: 'private', label: __( 'Private', 'woocommerce' ) },
-	{ value: 'publish', label: __( 'Published', 'woocommerce' ) },
-	{ value: 'trash', label: __( 'Trash', 'woocommerce' ) },
-];
+type ProductField = Field< ProductEntityRecord >;
+type ProductFieldExtensions = Partial< ProductField >;

-/**
- * TODO: auto convert some of the product editor blocks ( from the blocks directory ) to this format.
- * The edit function should work relatively well with the edit from the blocks, the only difference is that the blocks rely on getEntityProp to get the value
- */
-export const productFields: Field< Product >[] = [
-	{
-		id: 'name',
-		label: __( 'Name', 'woocommerce' ),
-		enableHiding: false,
-		type: 'text',
-		render: function nameRender( { item }: { item: Product } ) {
-			return <>{ item.name }</>;
-		},
-	},
-	{
-		id: 'sku',
-		label: __( 'SKU', 'woocommerce' ),
-		enableHiding: false,
-		enableSorting: false,
-		render: ( { item }: { item: Product } ) => {
-			return <>{ item.sku }</>;
-		},
-	},
-	{
-		id: 'date',
-		label: __( 'Date', 'woocommerce' ),
-		render: ( { item }: { item: Product } ) => {
-			return <time>{ item.date_created }</time>;
-		},
-	},
+const PRODUCT_LIST_FIELD_IDS = [
+	'name',
+	'short_description',
+	'description',
+	'images',
+	'images_count',
+	'product_status',
+	'sku',
+	'price',
+	'regular_price',
+	'sale_price',
+	'schedule_sale',
+	'date_on_sale_from',
+	'date_on_sale_to',
+	'on_sale',
+	'price_summary',
+	'stock',
+	'stock_quantity',
+	'manage_stock',
+	'inventory_summary',
+	'categories',
+	'tags',
+	'organization_summary',
+	'featured',
+	'catalog_visibility',
+	'visibility_summary',
+	'downloadable',
+	'downloadable_count',
+	'external_url',
+	'button_text',
+	'weight',
+	'length',
+	'width',
+	'height',
+	'shipping_class',
+	'shipping_summary',
+	'tax_status',
+	'upsell_ids',
+	'cross_sell_ids',
+	'linked_products_count',
+	'seo_title',
+	'seo_description',
+	'seo_preview',
+] as const;
+
+const PRODUCT_LIST_FIELD_EXTENSIONS: Record< string, ProductFieldExtensions > =
 	{
-		label: __( 'Status', 'woocommerce' ),
-		id: 'status',
-		getValue: ( { item }: { item: Product } ) =>
-			STATUSES.find( ( { value } ) => value === item.status )?.label ??
-			item.status,
-		elements: STATUSES,
-		filterBy: {
-			operators: [ OPERATOR_IS ],
-		},
-		enableSorting: false,
-	},
-];
+		name: nameFieldExtensions,
+		short_description: shortDescriptionFieldExtensions,
+		description: descriptionFieldExtensions,
+		images: imagesFieldExtensions,
+		images_count: imagesCountFieldExtensions,
+		product_status: productStatusFieldExtensions,
+		sku: skuFieldExtensions,
+		price: priceFieldExtensions as ProductFieldExtensions,
+		regular_price: regularPriceFieldExtensions,
+		sale_price: salePriceFieldExtensions,
+		schedule_sale: scheduleSaleFieldExtensions,
+		date_on_sale_from: dateOnSaleFromFieldExtensions,
+		date_on_sale_to: dateOnSaleToFieldExtensions,
+		on_sale: onSaleFieldExtensions,
+		price_summary: priceSummaryFieldExtensions,
+		stock: stockFieldExtensions,
+		stock_quantity: stockQuantityFieldExtensions,
+		manage_stock: manageStockFieldExtensions,
+		inventory_summary: inventorySummaryFieldExtensions,
+		categories: categoriesFieldExtensions,
+		tags: tagsFieldExtensions,
+		organization_summary: organizationSummaryFieldExtensions,
+		featured: featuredFieldExtensions,
+		catalog_visibility: catalogVisibilityFieldExtensions,
+		visibility_summary: visibilitySummaryFieldExtensions,
+		downloadable: downloadableFieldExtensions,
+		downloadable_count: downloadableCountFieldExtensions,
+		external_url: externalUrlFieldExtensions,
+		button_text: buttonTextFieldExtensions,
+		weight: weightFieldExtensions,
+		length: lengthFieldExtensions,
+		width: widthFieldExtensions,
+		height: heightFieldExtensions,
+		shipping_class: shippingClassFieldExtensions,
+		shipping_summary: shippingSummaryFieldExtensions,
+		tax_status: taxStatusFieldExtensions,
+		upsell_ids: upsellIdsFieldExtensions,
+		cross_sell_ids: crossSellIdsFieldExtensions,
+		linked_products_count: linkedProductsCountFieldExtensions,
+	};
+
+export const productFields: ProductField[] = PRODUCT_LIST_FIELD_IDS.map(
+	( id ) => ( {
+		id,
+		...PRODUCT_LIST_FIELD_EXTENSIONS[ id ],
+	} )
+);
diff --git a/packages/js/experimental-products-app/src/product-list/index.tsx b/packages/js/experimental-products-app/src/product-list/index.tsx
index 89078b3c9fa..35bcc12e916 100644
--- a/packages/js/experimental-products-app/src/product-list/index.tsx
+++ b/packages/js/experimental-products-app/src/product-list/index.tsx
@@ -3,14 +3,13 @@
  */
 import { DataViews, View } from '@wordpress/dataviews';
 import {
-	createElement,
 	useState,
 	useMemo,
 	useCallback,
 	useEffect,
 	Fragment,
 } from '@wordpress/element';
-import { Product, ProductQuery, productsStore } from '@woocommerce/data';
+import { ProductQuery, productsStore } from '@woocommerce/data';
 import { privateApis as routerPrivateApis } from '@wordpress/router';
 import { store as coreStore } from '@wordpress/core-data';
 import { __ } from '@wordpress/i18n';
@@ -24,7 +23,12 @@ import { Page } from '@wordpress/admin-ui';
  * Internal dependencies
  */
 import { unlock } from '../lock-unlock';
+import type { ProductEntityRecord } from '../fields/types';
 import { productFields } from './fields';
+import {
+	DEFAULT_PRODUCT_TABLE_LAYOUT,
+	DEFAULT_PRODUCT_TABLE_VIEW,
+} from './layouts';
 import { useEditProductAction } from '../dataviews-actions';

 const { usePostActions } = unlock( editorPrivateApis );
@@ -37,16 +41,14 @@ export type ProductListProps = {
 	postType?: string;
 };

-const PAGE_SIZE = 25;
-const EMPTY_ARRAY: Product[] = [];
+const PAGE_SIZE = 20;
+const EMPTY_ARRAY: ProductEntityRecord[] = [];
 const DEFAULT_LAYOUTS = {
-	table: {} as const,
+	table: DEFAULT_PRODUCT_TABLE_LAYOUT,
 };
 const DEFAULT_VIEW: View = {
-	type: 'table',
+	...DEFAULT_PRODUCT_TABLE_VIEW,
 	page: 1,
-	perPage: PAGE_SIZE,
-	fields: [ 'name', 'sku', 'status', 'date' ],
 };

 /**
@@ -73,7 +75,7 @@ function useView( postType: string ): [ View, ( view: View ) => void ] {
 	return [ view, setView ];
 }

-function getItemId( item: Product ) {
+function getItemId( item: ProductEntityRecord ) {
 	return item.id.toString();
 }

@@ -96,7 +98,10 @@ export default function ProductList( {
 	const queryParams = useMemo( () => {
 		const filters: Partial< ProductQuery > = {};
 		view.filters?.forEach( ( filter ) => {
-			if ( filter.field === 'status' ) {
+			if (
+				filter.field === 'status' ||
+				filter.field === 'product_status'
+			) {
 				filters.status = Array.isArray( filter.value )
 					? filter.value.join( ',' )
 					: filter.value;
@@ -135,7 +140,7 @@ export default function ProductList( {
 			const { getProducts, getProductsTotalCount, isResolving } =
 				select( productsStore );
 			return {
-				records: getProducts( queryParams ) as Product[],
+				records: getProducts( queryParams ) as ProductEntityRecord[],
 				totalCount: getProductsTotalCount( queryParams ),
 				isLoading: isResolving( 'getProducts', [ queryParams ] ),
 			};
diff --git a/packages/js/experimental-products-app/src/product-list/layouts.ts b/packages/js/experimental-products-app/src/product-list/layouts.ts
new file mode 100644
index 00000000000..cb98167fafe
--- /dev/null
+++ b/packages/js/experimental-products-app/src/product-list/layouts.ts
@@ -0,0 +1,39 @@
+/**
+ * External dependencies
+ */
+import type { SupportedLayouts, ViewTable } from '@wordpress/dataviews';
+
+export const DEFAULT_PRODUCT_TABLE_TITLE_FIELD = 'name';
+export const DEFAULT_PRODUCT_TABLE_MEDIA_FIELD = 'images';
+
+export const DEFAULT_PRODUCT_TABLE_FIELDS = [
+	'product_status',
+	'sku',
+	'stock',
+	'price',
+] as const;
+
+export const DEFAULT_PRODUCT_TABLE_LAYOUT: NonNullable<
+	SupportedLayouts[ 'table' ]
+> = {
+	fields: [ ...DEFAULT_PRODUCT_TABLE_FIELDS ],
+	layout: {
+		styles: {
+			price: {
+				align: 'end',
+			},
+		},
+	},
+};
+
+export const DEFAULT_PRODUCT_TABLE_VIEW: ViewTable = {
+	type: 'table',
+	filters: [],
+	perPage: 20,
+	mediaField: DEFAULT_PRODUCT_TABLE_MEDIA_FIELD,
+	titleField: DEFAULT_PRODUCT_TABLE_TITLE_FIELD,
+	fields: [ ...DEFAULT_PRODUCT_TABLE_FIELDS ],
+	showLevels: false,
+	showMedia: true,
+	layout: DEFAULT_PRODUCT_TABLE_LAYOUT.layout,
+};
diff --git a/packages/js/experimental-products-app/src/style.scss b/packages/js/experimental-products-app/src/style.scss
index 35b592b865e..bafa651beb2 100644
--- a/packages/js/experimental-products-app/src/style.scss
+++ b/packages/js/experimental-products-app/src/style.scss
@@ -3,4 +3,9 @@

 .product_page_woocommerce-products-dashboard {
 	background-color: unset;
+	@import "./fields/components/list-item/style.scss";
+	@import "./fields/downloadable/style.scss";
+	@import "./fields/images/style.scss";
+	@import "./fields/stock/style.scss";
+	@import "./fields/visibility_summary/style.scss";
 }