Commit 21862e62f7a for woocommerce
commit 21862e62f7ab4e6ba3567f9dc7604763e88a47ab
Author: Luigi Teschio <gigitux@gmail.com>
Date: Sat May 16 14:31:11 2026 +0200
Add searchable chip select to products app (#64909)
* Add tree select control to products app
* Replace tree select control with searchable chip select
* fix build
* lint code
diff --git a/packages/js/experimental-products-app/changelog/add-tree-select-control b/packages/js/experimental-products-app/changelog/add-tree-select-control
new file mode 100644
index 00000000000..2f3dcd7f0ff
--- /dev/null
+++ b/packages/js/experimental-products-app/changelog/add-tree-select-control
@@ -0,0 +1,4 @@
+Significance: patch
+Type: add
+
+Add a tree select control to the experimental products app.
diff --git a/packages/js/experimental-products-app/package.json b/packages/js/experimental-products-app/package.json
index ec3a564b7cc..ae9da6e0565 100644
--- a/packages/js/experimental-products-app/package.json
+++ b/packages/js/experimental-products-app/package.json
@@ -59,6 +59,7 @@
"@wordpress/api-fetch": "7.44.0",
"@wordpress/url": "catalog:wp-min",
"@wordpress/base-styles": "6.20.0",
+ "@base-ui/react": "1.4.1",
"clsx": "2.1.x",
"react": "18.3.x",
"react-dom": "18.3.x"
diff --git a/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/chip-with-remove.tsx b/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/chip-with-remove.tsx
new file mode 100644
index 00000000000..f11471a8e08
--- /dev/null
+++ b/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/chip-with-remove.tsx
@@ -0,0 +1,46 @@
+/**
+ * External dependencies
+ */
+import { Combobox as BaseCombobox } from '@base-ui/react/combobox';
+import clsx from 'clsx';
+import { forwardRef } from 'react';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import type { SearchableChipSelectChipWithRemoveProps } from './types';
+
+export const ChipWithRemove = forwardRef<
+ HTMLDivElement,
+ SearchableChipSelectChipWithRemoveProps
+>( function ChipWithRemove(
+ { className, children, prefix, ...restProps },
+ ref
+) {
+ return (
+ <BaseCombobox.Chip
+ ref={ ref }
+ className={ clsx(
+ 'woocommerce-searchable-chip-select__chip',
+ className
+ ) }
+ { ...restProps }
+ >
+ { prefix && (
+ <span className="woocommerce-searchable-chip-select__chip-prefix">
+ { prefix }
+ </span>
+ ) }
+ <span className="woocommerce-searchable-chip-select__chip-content">
+ { children }
+ </span>
+ <BaseCombobox.ChipRemove
+ className="woocommerce-searchable-chip-select__chip-remove"
+ aria-label={ __( 'Remove', 'woocommerce' ) }
+ >
+ <span aria-hidden="true">×</span>
+ </BaseCombobox.ChipRemove>
+ </BaseCombobox.Chip>
+ );
+} );
diff --git a/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/index.ts b/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/index.ts
new file mode 100644
index 00000000000..0e5ececc8d3
--- /dev/null
+++ b/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/index.ts
@@ -0,0 +1,39 @@
+/*
+ * These components are copy-pasted from a private repository while we wait for
+ * @wordpress/ui to support an equivalent component. Remove this implementation
+ * once @wordpress/ui provides one.
+ */
+
+/**
+ * Internal dependencies
+ */
+import { ChipWithRemove } from './chip-with-remove';
+import { Item } from './item';
+import { SearchableChipSelect as _SearchableChipSelect } from './searchable-chip-select';
+import { SearchableChipSelectControl as _SearchableChipSelectControl } from './searchable-chip-select-control';
+
+Item.displayName = 'SearchableChipSelect.Item';
+ChipWithRemove.displayName = 'SearchableChipSelect.ChipWithRemove';
+
+export const SearchableChipSelect = Object.assign( _SearchableChipSelect, {
+ Item,
+ ChipWithRemove,
+} );
+
+export const Combobox = SearchableChipSelect;
+
+export const SearchableChipSelectControl = Object.assign(
+ _SearchableChipSelectControl,
+ {
+ Item,
+ ChipWithRemove,
+ }
+);
+
+export type {
+ Item as SearchableChipSelectItem,
+ SearchableChipSelectChipWithRemoveProps,
+ SearchableChipSelectControlProps,
+ SearchableChipSelectItemProps,
+ SearchableChipSelectProps,
+} from './types';
diff --git a/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/item.tsx b/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/item.tsx
new file mode 100644
index 00000000000..8c33a0649d9
--- /dev/null
+++ b/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/item.tsx
@@ -0,0 +1,26 @@
+/**
+ * External dependencies
+ */
+import { Combobox as BaseCombobox } from '@base-ui/react/combobox';
+import clsx from 'clsx';
+import { forwardRef } from 'react';
+
+/**
+ * Internal dependencies
+ */
+import type { SearchableChipSelectItemProps } from './types';
+
+export const Item = forwardRef< HTMLDivElement, SearchableChipSelectItemProps >(
+ function Item( { className, ...restProps }, ref ) {
+ return (
+ <BaseCombobox.Item
+ ref={ ref }
+ className={ clsx(
+ 'woocommerce-searchable-chip-select__item',
+ className
+ ) }
+ { ...restProps }
+ />
+ );
+ }
+);
diff --git a/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/searchable-chip-select-control.tsx b/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/searchable-chip-select-control.tsx
new file mode 100644
index 00000000000..6017f385cdb
--- /dev/null
+++ b/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/searchable-chip-select-control.tsx
@@ -0,0 +1,61 @@
+/**
+ * External dependencies
+ */
+import { forwardRef, useId } from 'react';
+
+/**
+ * Internal dependencies
+ */
+import { SearchableChipSelect } from './searchable-chip-select';
+import type { SearchableChipSelectControlProps } from './types';
+
+function mergeIds( ...ids: Array< string | undefined > ) {
+ return ids.filter( Boolean ).join( ' ' ) || undefined;
+}
+
+/**
+ * A searchable multi-selection field with label and description wiring.
+ */
+export const SearchableChipSelectControl = forwardRef<
+ HTMLDivElement,
+ SearchableChipSelectControlProps
+>( function SearchableChipSelectControl(
+ {
+ className,
+ label,
+ description,
+ 'aria-labelledby': ariaLabelledby,
+ 'aria-describedby': ariaDescribedby,
+ ...restProps
+ },
+ ref
+) {
+ const id = useId();
+ const labelId = `${ id }-label`;
+ const descriptionId = description ? `${ id }-description` : undefined;
+
+ return (
+ <div className={ className }>
+ <div
+ id={ labelId }
+ className="woocommerce-searchable-chip-select__label"
+ >
+ { label }
+ </div>
+ <SearchableChipSelect
+ ref={ ref }
+ aria-labelledby={ mergeIds( ariaLabelledby, labelId ) }
+ aria-describedby={ mergeIds( ariaDescribedby, descriptionId ) }
+ { ...restProps }
+ />
+ { description && (
+ <div
+ id={ descriptionId }
+ className="woocommerce-searchable-chip-select__description"
+ >
+ { description }
+ </div>
+ ) }
+ </div>
+ );
+} );
diff --git a/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/searchable-chip-select.tsx b/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/searchable-chip-select.tsx
new file mode 100644
index 00000000000..181622df584
--- /dev/null
+++ b/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/searchable-chip-select.tsx
@@ -0,0 +1,176 @@
+/**
+ * External dependencies
+ */
+import { Combobox as BaseCombobox } from '@base-ui/react/combobox';
+import clsx from 'clsx';
+import { forwardRef } from 'react';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import type { Item, SearchableChipSelectProps } from './types';
+
+const DEFAULT_ITEMS: Item[] = [];
+
+function itemToStringLabel( item: Item ) {
+ return item.label;
+}
+
+function itemToStringValue( item: Item ) {
+ return item.value;
+}
+
+function isItemEqualToValue( item: Item, value: Item ) {
+ return item.value === value.value;
+}
+
+/**
+ * A searchable multi-selection component with chips, with support for
+ * a footer item to create new items.
+ */
+export const SearchableChipSelect = forwardRef<
+ HTMLDivElement,
+ SearchableChipSelectProps
+>( function SearchableChipSelect(
+ {
+ children,
+ creatableItem,
+ disabled,
+ emptyContent = __( 'No results found.', 'woocommerce' ),
+ items = DEFAULT_ITEMS,
+ chipsContent,
+ searchPlaceholder = __( 'Search', 'woocommerce' ),
+ showClearButton = true,
+ clearButtonLabel = __( 'Clear all', 'woocommerce' ),
+ 'aria-label': ariaLabel,
+ 'aria-labelledby': ariaLabelledby,
+ 'aria-describedby': ariaDescribedby,
+ itemToStringLabel: customItemToStringLabel = itemToStringLabel,
+ itemToStringValue: customItemToStringValue = itemToStringValue,
+ isItemEqualToValue: customIsItemEqualToValue = isItemEqualToValue,
+ ...restProps
+ },
+ ref
+) {
+ const rootItems = creatableItem ? [ ...items, creatableItem ] : items;
+
+ return (
+ <BaseCombobox.Root< Item, true >
+ items={ rootItems }
+ multiple
+ disabled={ disabled }
+ itemToStringLabel={ customItemToStringLabel }
+ itemToStringValue={ customItemToStringValue }
+ isItemEqualToValue={ customIsItemEqualToValue }
+ { ...restProps }
+ >
+ <BaseCombobox.Chips
+ ref={ ref }
+ render={
+ <div
+ className={ clsx(
+ 'woocommerce-searchable-chip-select',
+ disabled &&
+ 'woocommerce-searchable-chip-select--is-disabled'
+ ) }
+ />
+ }
+ >
+ <BaseCombobox.Value>
+ { ( value: Item[] ) =>
+ value.length > 0 ? (
+ <div className="woocommerce-searchable-chip-select__chips-edit-area">
+ <div className="woocommerce-searchable-chip-select__chips-list">
+ { chipsContent
+ ? chipsContent( value )
+ : value.map( ( item ) => (
+ <BaseCombobox.Chip
+ key={ item.value }
+ className="woocommerce-searchable-chip-select__chip"
+ >
+ <span className="woocommerce-searchable-chip-select__chip-content">
+ { item.label }
+ </span>
+ <BaseCombobox.ChipRemove
+ className="woocommerce-searchable-chip-select__chip-remove"
+ aria-label={ __(
+ 'Remove',
+ 'woocommerce'
+ ) }
+ >
+ <span aria-hidden="true">
+ ×
+ </span>
+ </BaseCombobox.ChipRemove>
+ </BaseCombobox.Chip>
+ ) ) }
+ </div>
+ { showClearButton && (
+ <BaseCombobox.Clear
+ className="woocommerce-searchable-chip-select__clear"
+ aria-label={ clearButtonLabel }
+ >
+ <span aria-hidden="true">×</span>
+ </BaseCombobox.Clear>
+ ) }
+ </div>
+ ) : null
+ }
+ </BaseCombobox.Value>
+
+ <BaseCombobox.Input
+ className="woocommerce-searchable-chip-select__input"
+ placeholder={ searchPlaceholder }
+ aria-label={ ariaLabel }
+ aria-labelledby={ ariaLabelledby }
+ aria-describedby={ ariaDescribedby }
+ />
+ </BaseCombobox.Chips>
+
+ <BaseCombobox.Portal>
+ <BaseCombobox.Positioner className="woocommerce-searchable-chip-select__positioner">
+ <BaseCombobox.Popup className="woocommerce-searchable-chip-select__popup">
+ <BaseCombobox.Empty className="woocommerce-searchable-chip-select__empty">
+ { emptyContent }
+ </BaseCombobox.Empty>
+ <BaseCombobox.List className="woocommerce-searchable-chip-select__list">
+ <BaseCombobox.Collection>
+ { ( item: Item, index: number ) => {
+ if ( item.value === creatableItem?.value ) {
+ return null;
+ }
+ if ( children ) {
+ return children( item, index );
+ }
+ return (
+ <BaseCombobox.Item
+ key={ item.value }
+ value={ item }
+ disabled={ item.disabled }
+ className="woocommerce-searchable-chip-select__item"
+ >
+ { item.label }
+ </BaseCombobox.Item>
+ );
+ } }
+ </BaseCombobox.Collection>
+ { creatableItem && (
+ <BaseCombobox.Item
+ value={ creatableItem }
+ disabled={ creatableItem.disabled }
+ className={ clsx(
+ 'woocommerce-searchable-chip-select__item',
+ 'woocommerce-searchable-chip-select__item--is-creatable'
+ ) }
+ >
+ { creatableItem.label }
+ </BaseCombobox.Item>
+ ) }
+ </BaseCombobox.List>
+ </BaseCombobox.Popup>
+ </BaseCombobox.Positioner>
+ </BaseCombobox.Portal>
+ </BaseCombobox.Root>
+ );
+} );
diff --git a/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/style.scss b/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/style.scss
new file mode 100644
index 00000000000..7b8be7b7b6e
--- /dev/null
+++ b/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/style.scss
@@ -0,0 +1,163 @@
+.woocommerce-searchable-chip-select {
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ min-height: 40px;
+ border: 1px solid #949494;
+ border-radius: 2px;
+ background: #fff;
+ color: #1e1e1e;
+ font-size: 13px;
+ line-height: 1.4;
+
+ &:focus-within {
+ border-color: var( --wp-admin-theme-color, #3858e9 );
+ box-shadow: 0 0 0 1px var( --wp-admin-theme-color, #3858e9 );
+ outline: 2px solid transparent;
+ }
+
+ &--is-disabled {
+ background: #f6f7f7;
+ color: #757575;
+ }
+
+ &__chips-edit-area {
+ display: flex;
+ gap: 8px;
+ align-items: flex-start;
+ padding: 8px 8px 0;
+ }
+
+ &__chips-list {
+ display: flex;
+ flex: 1 1 auto;
+ flex-wrap: wrap;
+ gap: 4px;
+ }
+
+ &__chip {
+ box-sizing: border-box;
+ display: inline-flex;
+ align-items: center;
+ overflow: hidden;
+ min-height: 24px;
+ border: 1px solid #c3c4c7;
+ border-radius: 12px;
+ background: #f6f7f7;
+ color: #1e1e1e;
+ }
+
+ &__chip-prefix {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 20px;
+ height: 20px;
+ margin-inline-start: 1px;
+ overflow: hidden;
+ border-radius: 50%;
+ }
+
+ &__chip-content {
+ padding: 2px 4px 2px 8px;
+ }
+
+ &__chip-remove,
+ &__clear {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 24px;
+ height: 24px;
+ border: 0;
+ border-radius: 2px;
+ background: transparent;
+ color: inherit;
+ cursor: pointer;
+
+ &:hover {
+ background: #e0e0e0;
+ }
+ }
+
+ &__input {
+ box-sizing: border-box;
+ width: 100%;
+ min-height: 38px;
+ padding: 8px;
+ border: 0;
+ background: transparent;
+ color: inherit;
+ font: inherit;
+ outline: none;
+
+ &:focus {
+ border: 0;
+ box-shadow: none;
+ outline: none;
+ }
+ }
+
+ &__positioner {
+ z-index: 100000;
+ }
+
+ &__popup {
+ box-sizing: border-box;
+ width: var( --anchor-width );
+ max-height: min( var( --available-height ), 320px );
+ overflow: auto;
+ border: 1px solid #c3c4c7;
+ border-radius: 2px;
+ background: #fff;
+ box-shadow: 0 2px 6px rgb( 0 0 0 / 20% );
+ }
+
+ &__list {
+ margin: 0;
+ padding: 4px 0;
+ }
+
+ &__item,
+ &__empty {
+ box-sizing: border-box;
+ padding: 8px 12px;
+ }
+
+ &__item {
+ cursor: default;
+
+ &[data-highlighted] {
+ background: var( --wp-admin-theme-color, #3858e9 );
+ color: #fff;
+ }
+
+ &[data-selected] {
+ font-weight: 600;
+ }
+
+ &[data-disabled] {
+ color: #757575;
+ }
+
+ &--is-creatable {
+ border-top: 1px solid #e0e0e0;
+ }
+ }
+
+ &__empty {
+ color: #757575;
+ }
+
+ &__label {
+ margin-bottom: 4px;
+ font-weight: 500;
+ }
+
+ &__description {
+ margin-top: 4px;
+ color: #757575;
+ font-size: 12px;
+ }
+}
diff --git a/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/test/index.test.tsx b/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/test/index.test.tsx
new file mode 100644
index 00000000000..b070e3f4dbf
--- /dev/null
+++ b/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/test/index.test.tsx
@@ -0,0 +1,146 @@
+/**
+ * External dependencies
+ */
+import { render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { createRef } from 'react';
+
+/**
+ * Internal dependencies
+ */
+import { SearchableChipSelect, SearchableChipSelectControl } from '../index';
+
+describe( 'SearchableChipSelect', () => {
+ const mockItems = [
+ { value: 'apple', label: 'Apple' },
+ { value: 'banana', label: 'Banana' },
+ { value: 'cherry', label: 'Cherry' },
+ ];
+
+ it( 'forwards ref', () => {
+ const ref = createRef< HTMLDivElement >();
+
+ render( <SearchableChipSelect ref={ ref } /> );
+
+ expect( ref.current ).toBeInstanceOf( HTMLDivElement );
+ } );
+
+ it( 'passes aria-label and aria-describedby props to the input', () => {
+ render(
+ <>
+ <SearchableChipSelect
+ aria-label="My label"
+ aria-describedby="searchable-chip-select-description"
+ />
+ <p id="searchable-chip-select-description">My description</p>
+ </>
+ );
+
+ expect(
+ screen.getByRole( 'combobox', {
+ name: 'My label',
+ description: 'My description',
+ } )
+ ).toBeInTheDocument();
+ } );
+
+ it( 'passes aria-labelledby prop to the input', () => {
+ render(
+ <>
+ <p id="searchable-chip-select-label">My label</p>
+ <SearchableChipSelect aria-labelledby="searchable-chip-select-label" />
+ </>
+ );
+
+ expect(
+ screen.getByRole( 'combobox', {
+ name: 'My label',
+ } )
+ ).toBeInTheDocument();
+ } );
+
+ it( 'renders accessible control label and description', () => {
+ render(
+ <SearchableChipSelectControl
+ label="Fruits"
+ description="Choose your favorite fruits"
+ items={ mockItems }
+ />
+ );
+
+ expect(
+ screen.getByRole( 'combobox', {
+ name: 'Fruits',
+ description: 'Choose your favorite fruits',
+ } )
+ ).toBeVisible();
+ } );
+
+ it( 'renders custom empty content', async () => {
+ render(
+ <SearchableChipSelectControl
+ label="Fruits"
+ items={ [] }
+ emptyContent="No fruit found."
+ />
+ );
+
+ await userEvent.click(
+ screen.getByRole( 'combobox', {
+ name: 'Fruits',
+ } )
+ );
+
+ expect( screen.getByRole( 'status' ) ).toHaveTextContent(
+ 'No fruit found.'
+ );
+ } );
+
+ it( 'supports custom chip and item renderers', async () => {
+ const ref = createRef< HTMLDivElement >();
+ const chipRef = createRef< HTMLDivElement >();
+ const itemRef = createRef< HTMLDivElement >();
+
+ render(
+ <SearchableChipSelectControl
+ ref={ ref }
+ label="Fruits"
+ items={ mockItems }
+ defaultValue={ [ mockItems[ 0 ] ] }
+ chipsContent={ ( value ) =>
+ value.map( ( item ) => (
+ <SearchableChipSelectControl.ChipWithRemove
+ key={ item.value }
+ ref={ chipRef }
+ >
+ { item.label }
+ </SearchableChipSelectControl.ChipWithRemove>
+ ) )
+ }
+ >
+ { ( item ) => (
+ <SearchableChipSelectControl.Item
+ key={ item.value }
+ ref={ item.value === 'banana' ? itemRef : undefined }
+ value={ item }
+ >
+ { item.label }
+ </SearchableChipSelectControl.Item>
+ ) }
+ </SearchableChipSelectControl>
+ );
+
+ expect( ref.current ).toBeInstanceOf( HTMLDivElement );
+ expect( chipRef.current ).toBeInstanceOf( HTMLDivElement );
+
+ await userEvent.click(
+ screen.getByRole( 'combobox', {
+ name: 'Fruits',
+ } )
+ );
+
+ await waitFor( () => {
+ expect( itemRef.current ).toBeInstanceOf( HTMLDivElement );
+ } );
+ } );
+} );
diff --git a/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/types.ts b/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/types.ts
new file mode 100644
index 00000000000..461c4637402
--- /dev/null
+++ b/packages/js/experimental-products-app/src/fields/components/searchable-chip-select/types.ts
@@ -0,0 +1,87 @@
+/**
+ * External dependencies
+ */
+import type {
+ ComboboxChipProps,
+ ComboboxCollectionProps,
+ ComboboxEmptyProps,
+ ComboboxInputProps,
+ ComboboxItemProps,
+ ComboboxRootProps,
+} from '@base-ui/react/combobox';
+import type { ReactNode } from 'react';
+
+export type SearchableChipSelectItem = {
+ label: string;
+ value: string;
+ disabled?: boolean;
+};
+
+export type Item = SearchableChipSelectItem;
+
+export type SearchableChipSelectProps = Omit<
+ ComboboxRootProps< SearchableChipSelectItem, true >,
+ 'children' | 'items' | 'multiple'
+> &
+ Partial<
+ Pick<
+ ComboboxInputProps,
+ 'aria-label' | 'aria-labelledby' | 'aria-describedby'
+ >
+ > & {
+ /**
+ * The array of option items.
+ */
+ items?: SearchableChipSelectItem[];
+ /**
+ * A render function for custom rendering the list of matching items.
+ */
+ children?: ComboboxCollectionProps[ 'children' ];
+ /**
+ * The item that triggers the creation of a new item.
+ */
+ creatableItem?: SearchableChipSelectItem;
+ /**
+ * A render function for custom rendering the selected chips.
+ */
+ chipsContent?: ( value: SearchableChipSelectItem[] ) => ReactNode;
+ /**
+ * The custom content to use instead of the default empty state.
+ */
+ emptyContent?: ComboboxEmptyProps[ 'children' ];
+ /**
+ * The placeholder text to use for the search input.
+ */
+ searchPlaceholder?: ComboboxInputProps[ 'placeholder' ];
+ /**
+ * Whether to show the clear button to remove all selected items.
+ *
+ * @default true
+ */
+ showClearButton?: boolean;
+ /**
+ * The aria-label for the clear button.
+ */
+ clearButtonLabel?: string;
+ };
+
+export type SearchableChipSelectControlProps = SearchableChipSelectProps & {
+ className?: string;
+ label: ReactNode;
+ description?: ReactNode;
+};
+
+export type SearchableChipSelectItemProps = ComboboxItemProps & {
+ children?: ReactNode;
+};
+
+export type SearchableChipSelectChipWithRemoveProps = Omit<
+ ComboboxChipProps,
+ 'prefix'
+> & {
+ children?: ReactNode;
+ /**
+ * Circular element to render before the chip content.
+ */
+ prefix?: ReactNode;
+};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3c4c0309a75..4f6f21044e5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1698,6 +1698,9 @@ importers:
packages/js/experimental-products-app:
dependencies:
+ '@base-ui/react':
+ specifier: 1.4.1
+ version: 1.4.1(@date-fns/tz@1.4.1)(@types/react@18.3.28)(date-fns@4.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@dnd-kit/react':
specifier: 0.4.0
version: 0.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -32843,7 +32846,7 @@ snapshots:
'@webpack-cli/configtest@2.1.1(webpack-cli@5.1.4)(webpack@5.97.1)':
dependencies:
webpack: 5.97.1(@swc/core@1.15.24)(webpack-cli@5.1.4)
- webpack-cli: 5.1.4(webpack-bundle-analyzer@4.9.1)(webpack-dev-server@4.15.2)(webpack@5.97.1)
+ webpack-cli: 5.1.4(webpack@5.97.1)
'@webpack-cli/info@1.5.0(webpack-cli@4.10.0(webpack-bundle-analyzer@4.9.1)(webpack@5.97.1))':
dependencies:
@@ -32853,7 +32856,7 @@ snapshots:
'@webpack-cli/info@2.0.2(webpack-cli@5.1.4)(webpack@5.97.1)':
dependencies:
webpack: 5.97.1(@swc/core@1.15.24)(webpack-cli@5.1.4)
- webpack-cli: 5.1.4(webpack-bundle-analyzer@4.9.1)(webpack-dev-server@4.15.2)(webpack@5.97.1)
+ webpack-cli: 5.1.4(webpack@5.97.1)
'@webpack-cli/serve@1.7.0(webpack-cli@4.10.0(webpack-bundle-analyzer@4.9.1)(webpack@5.97.1))':
dependencies:
@@ -35578,7 +35581,7 @@ snapshots:
'@wordpress/dependency-extraction-webpack-plugin@6.43.1-next.v.202604091042.0(webpack@5.97.1)':
dependencies:
json2php: 0.0.7
- webpack: 5.97.1(@swc/core@1.15.24)(esbuild@0.18.20)(webpack-cli@5.1.4)
+ webpack: 5.97.1(@swc/core@1.15.24)(webpack-cli@5.1.4)
'@wordpress/dependency-extraction-webpack-plugin@6.44.0(webpack@5.97.1)':
dependencies:
@@ -54721,7 +54724,7 @@ snapshots:
watchpack: 2.5.1
webpack-sources: 3.3.4
optionalDependencies:
- webpack-cli: 5.1.4(webpack-bundle-analyzer@4.9.1)(webpack-dev-server@4.15.2)(webpack@5.97.1)
+ webpack-cli: 5.1.4(webpack@5.97.1)
transitivePeerDependencies:
- '@swc/core'
- esbuild