diff --git a/packages/block-editor/src/hooks/position.jsx b/packages/block-editor/src/hooks/position.jsx
index 8ff0a29fac193d..e69579bcc1cb4f 100644
--- a/packages/block-editor/src/hooks/position.jsx
+++ b/packages/block-editor/src/hooks/position.jsx
@@ -4,7 +4,6 @@ import { getBlockSupport, hasBlockSupport } from '@wordpress/blocks';
import { useInstanceId } from '@wordpress/compose';
import { useSelect } from '@wordpress/data';
import { useMemo } from '@wordpress/element';
-// eslint-disable-next-line @wordpress/use-recommended-components -- Use the portal-based popup to avoid inspector clipping.
import { SelectControl } from '@wordpress/ui';
import { useSettings } from '../components/use-settings';
import InspectorControls from '../components/inspector-controls';
diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 4d7723b519875c..647bdd2c6f39af 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -11,6 +11,7 @@
### Enhancements
+- `SelectControl`: Mark as not recommended for use in a WordPress environment, in favour of `SelectControl` from `@wordpress/ui` ([#83030](https://github.com/WordPress/gutenberg/pull/83030)).
- `PaletteEdit`: Use standard menu semantics and keyboard navigation for palette option actions through `Menu` from `@wordpress/ui` ([#82768](https://github.com/WordPress/gutenberg/pull/82768)).
- `CheckboxControl`: Match the `@wordpress/ui` checkmark size and disabled fill ([#82555](https://github.com/WordPress/gutenberg/pull/82555)).
- `Snackbar`: Show the action's focus ring with the design system's outline instead of a legacy dotted outline, and let `Button`/`ExternalLink` own the ring ([#82640](https://github.com/WordPress/gutenberg/pull/82640)).
diff --git a/packages/components/src/select-control/stories/index.story.tsx b/packages/components/src/select-control/stories/index.story.tsx
index 88202ff6120955..476f42d076ef05 100644
--- a/packages/components/src/select-control/stories/index.story.tsx
+++ b/packages/components/src/select-control/stories/index.story.tsx
@@ -5,7 +5,6 @@ import SelectControl from '../';
import { InputControlPrefixWrapper } from '../../input-control/input-prefix-wrapper';
const meta: Meta< typeof SelectControl > = {
- tags: [ 'manifest' ],
title: 'Components/Selection & Input/Common/SelectControl',
id: 'components-selectcontrol',
component: SelectControl,
@@ -23,9 +22,9 @@ const meta: Meta< typeof SelectControl > = {
controls: { expanded: true },
docs: { canvas: { sourceState: 'shown' } },
componentStatus: {
- status: 'recommended',
+ status: 'not-recommended',
whereUsed: 'global',
- notes: 'Will be superseded by [`SelectControl`](?path=/docs/design-system-components-form-selectcontrol--docs) in `@wordpress/ui`, but continue using for now.',
+ notes: 'Use [`SelectControl`](?path=/docs/design-system-components-form-selectcontrol--docs) from `@wordpress/ui` instead. For the `minimal` variant, use the [`Select`](?path=/docs/design-system-components-form-primitives-select--docs) primitive rather than `SelectControl`.',
},
},
};
diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md
index 1ea454c1c35630..3a6a2e8da5f863 100644
--- a/packages/eslint-plugin/CHANGELOG.md
+++ b/packages/eslint-plugin/CHANGELOG.md
@@ -8,6 +8,7 @@
### Enhancements
+- Update `use-recommended-components` rule to mark `Select` and `SelectControl` from `@wordpress/ui` as recommended ([#83030](https://github.com/WordPress/gutenberg/pull/83030)).
- `recommended`: Apply the TypeScript rules and parser to `.mts` and `.cts` files ([#83071](https://github.com/WordPress/gutenberg/pull/83071)).
- `recommended`: Resolve imports through `.mjs`, `.cjs`, `.mts` and `.cts`, and exempt `.d.mts` and `.d.cts` from the TypeScript resolver settings alongside `.d.ts` ([#83071](https://github.com/WordPress/gutenberg/pull/83071)).
- `no-i18n-in-save`: Recognise the `.mjs`, `.cjs`, `.mts` and `.cts` spellings of `save` and `deprecated` ([#83071](https://github.com/WordPress/gutenberg/pull/83071)).
diff --git a/packages/eslint-plugin/rules/use-recommended-components.js b/packages/eslint-plugin/rules/use-recommended-components.js
index 268cbe729791b2..f151463ee7793a 100644
--- a/packages/eslint-plugin/rules/use-recommended-components.js
+++ b/packages/eslint-plugin/rules/use-recommended-components.js
@@ -36,6 +36,8 @@ const ALLOWLIST = {
'RangeCalendar',
'SearchableChipSelect',
'SearchableChipSelectControl',
+ 'Select',
+ 'SelectControl',
'Skeleton',
'Spinner',
'Stack',
diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md
index 91b7c9356d89b5..44d67fc82b6140 100644
--- a/packages/ui/CHANGELOG.md
+++ b/packages/ui/CHANGELOG.md
@@ -13,6 +13,7 @@
### Enhancements
+- `Select`, `SelectControl`: Mark as recommended for use in a WordPress environment ([#83030](https://github.com/WordPress/gutenberg/pull/83030)).
- `Select`, `SelectControl`: Add `ItemLabel` and `ItemDescription` subcomponents. Multiple descriptions contribute to `aria-describedby` in DOM order. `SelectControl` `items` may include an optional `description` string. ([#82369](https://github.com/WordPress/gutenberg/pull/82369))
- `Select`, `SelectControl`: Align the selected item with the trigger by default, and drop the popup max-height cap when that alignment is enabled ([#82043](https://github.com/WordPress/gutenberg/pull/82043)).
- `Select`, `Combobox`, `Autocomplete`: Allow browsing read-only controls while keeping their value unchanged ([#82835](https://github.com/WordPress/gutenberg/pull/82835)).
diff --git a/packages/ui/src/form/primitives/select/root.tsx b/packages/ui/src/form/primitives/select/root.tsx
index 058d68153ed629..6cb13b6320740f 100644
--- a/packages/ui/src/form/primitives/select/root.tsx
+++ b/packages/ui/src/form/primitives/select/root.tsx
@@ -5,6 +5,8 @@ import { DirectionProvider } from '../../../utils/direction-provider';
/**
* A component that lets users choose one option from a list.
*
+ * Prefer `SelectControl` when using with a standard label and description.
+ *
* When using object values, pass an `items` array so `Select.Trigger` can
* auto-resolve the selected item's label. By default, items should use a
* `{ value, label }` shape, or provide `itemToStringLabel` for a custom shape.
diff --git a/packages/ui/src/form/primitives/select/stories/index.story.tsx b/packages/ui/src/form/primitives/select/stories/index.story.tsx
index 5b61baf8c0ccf7..e6895ae78c9d42 100644
--- a/packages/ui/src/form/primitives/select/stories/index.story.tsx
+++ b/packages/ui/src/form/primitives/select/stories/index.story.tsx
@@ -2,8 +2,14 @@ import type { Meta, StoryObj } from '@storybook/react-vite';
import * as Select from '../';
const meta: Meta< typeof Select.Root > = {
+ tags: [ 'manifest' ],
title: 'Design System/Components/Form/Primitives/Select',
component: Select.Root,
+ // Temporary: Due to an upstream bug, render the root explicitly so the
+ // components manifest extractor can resolve props from the JSX.
+ //
+ // See: https://github.com/storybookjs/storybook/issues/34877
+ render: ( args ) => ,
subcomponents: {
'Select.Trigger': Select.Trigger,
'Select.Portal': Select.Portal,
@@ -17,9 +23,8 @@ const meta: Meta< typeof Select.Root > = {
},
parameters: {
componentStatus: {
- status: 'use-with-caution',
+ status: 'recommended',
whereUsed: 'global',
- notes: 'Not yet recommended for use alongside components from `@wordpress/components`, pending review of style consistency with `@wordpress/components`, overlays compatibility, and component set completeness. See [WordPress/gutenberg#76135](https://github.com/WordPress/gutenberg/issues/76135).',
},
},
};
diff --git a/packages/ui/src/form/select-control/stories/index.story.tsx b/packages/ui/src/form/select-control/stories/index.story.tsx
index 8a6a43b19b5f47..60ec9e3b1f956e 100644
--- a/packages/ui/src/form/select-control/stories/index.story.tsx
+++ b/packages/ui/src/form/select-control/stories/index.story.tsx
@@ -8,8 +8,14 @@ import {
} from '../../stories/shared';
const meta: Meta< typeof SelectControl > = {
+ tags: [ 'manifest' ],
title: 'Design System/Components/Form/SelectControl',
component: SelectControl,
+ // Temporary: Due to an upstream bug, render the root explicitly so the
+ // components manifest extractor can resolve props from the JSX.
+ //
+ // See: https://github.com/storybookjs/storybook/issues/34877
+ render: ( args ) => ,
subcomponents: {
'SelectControl.Group': SelectControl.Group,
'SelectControl.GroupLabel': SelectControl.GroupLabel,
@@ -20,6 +26,12 @@ const meta: Meta< typeof SelectControl > = {
argTypes: {
onValueChange: { action: 'onValueChange' },
},
+ parameters: {
+ componentStatus: {
+ status: 'recommended',
+ whereUsed: 'global',
+ },
+ },
};
export default meta;
diff --git a/storybook/components-manifest.yml b/storybook/components-manifest.yml
index e10db8ca8f9bd3..d3a943932976b2 100644
--- a/storybook/components-manifest.yml
+++ b/storybook/components-manifest.yml
@@ -1272,26 +1272,182 @@
- placeholder
- size
- value
+- name: Select.Root
+ props:
+ - actionsRef
+ - autoComplete
+ - children
+ - defaultOpen
+ - defaultValue
+ - disabled
+ - form
+ - highlightItemOnHover
+ - id
+ - inputRef
+ - isItemEqualToValue
+ - items
+ - itemToStringLabel
+ - itemToStringValue
+ - modal
+ - name
+ - onOpenChange
+ - onOpenChangeComplete
+ - onValueChange
+ - open
+ - readOnly
+ - required
+ - value
+ subcomponents:
+ - name: Select.Group
+ props:
+ - children
+ - className
+ - render
+ - style
+ - name: Select.GroupLabel
+ props:
+ - children
+ - className
+ - render
+ - style
+ - name: Select.Item
+ props:
+ - children
+ - className
+ - disabled
+ - label
+ - nativeButton
+ - render
+ - size
+ - style
+ - value
+ - name: Select.ItemDescription
+ props:
+ - children
+ - className
+ - render
+ - style
+ - name: Select.ItemLabel
+ props:
+ - children
+ - className
+ - render
+ - style
+ - name: Select.Popup
+ props:
+ - children
+ - className
+ - finalFocus
+ - portal
+ - positioner
+ - render
+ - style
+ - width
+ - name: Select.Portal
+ props:
+ - className
+ - container
+ - render
+ - style
+ - name: Select.Positioner
+ props:
+ - align
+ - alignItemWithTrigger
+ - alignOffset
+ - anchor
+ - arrowPadding
+ - className
+ - collisionAvoidance
+ - collisionBoundary
+ - collisionPadding
+ - disableAnchorTracking
+ - positionMethod
+ - render
+ - side
+ - sideOffset
+ - sticky
+ - style
+ - name: Select.Trigger
+ props:
+ - children
+ - className
+ - disabled
+ - nativeButton
+ - placeholder
+ - render
+ - size
+ - style
+ - variant
- name: SelectControl
props:
- - __next36pxDefaultSize
- - __next40pxDefaultSize
- - __nextHasNoMarginBottom
+ - actionsRef
+ - autoComplete
- children
+ - className
+ - defaultOpen
+ - defaultValue
+ - description
+ - details
- disabled
- - help
+ - form
- hideLabelFromVision
+ - highlightItemOnHover
+ - id
+ - isItemEqualToValue
+ - items
+ - itemToStringLabel
+ - itemToStringValue
+ - key
- label
- - labelPosition
- - multiple
- - onChange
- - options
- - prefix
+ - modal
+ - name
+ - onOpenChange
+ - onOpenChangeComplete
+ - onValueChange
+ - open
+ - placeholder
+ - popupWidth
+ - readOnly
- ref
+ - required
- size
- - suffix
+ - triggerContent
- value
- - variant
+ subcomponents:
+ - name: SelectControl.Group
+ props:
+ - children
+ - className
+ - render
+ - style
+ - name: SelectControl.GroupLabel
+ props:
+ - children
+ - className
+ - render
+ - style
+ - name: SelectControl.Item
+ props:
+ - children
+ - className
+ - disabled
+ - label
+ - nativeButton
+ - render
+ - style
+ - value
+ - name: SelectControl.ItemDescription
+ props:
+ - children
+ - className
+ - render
+ - style
+ - name: SelectControl.ItemLabel
+ props:
+ - children
+ - className
+ - render
+ - style
- name: Shortcut
props:
- className
diff --git a/storybook/prop-description-allowlist.json b/storybook/prop-description-allowlist.json
index c3ea0e720b992b..0b72d525203acf 100644
--- a/storybook/prop-description-allowlist.json
+++ b/storybook/prop-description-allowlist.json
@@ -97,7 +97,17 @@
"SearchableChipSelectControl>SearchableChipSelectControl.GroupLabel:children",
"SearchableChipSelectControl>SearchableChipSelectControl.Item",
"SearchableChipSelectControl>SearchableChipSelectControl.Item:children",
- "SelectControl:ref",
+ "Select.Root:children",
+ "Select.Root>Select.Group:children",
+ "Select.Root>Select.GroupLabel:children",
+ "Select.Root>Select.Item",
+ "Select.Root>Select.Popup",
+ "Select.Root>Select.Trigger",
+ "SelectControl:children",
+ "SelectControl:key",
+ "SelectControl>SelectControl.Group:children",
+ "SelectControl>SelectControl.GroupLabel:children",
+ "SelectControl>SelectControl.Item",
"Slot>Fill",
"Slot>SlotFillProvider",
"ToggleControl:className",