Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/block-library/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
### Internal

- Heading: Declare the heading level and paragraph keyboard shortcuts on the block's variations and transforms, rather than in a `BlockKeyboardShortcuts` component that every editor had to render. The `BlockKeyboardShortcuts` private export has been removed ([#81588](https://github.com/WordPress/gutenberg/pull/81588)).
- Math: Use `ValidatedTextareaControl` from `@wordpress/ui` in the LaTeX popover instead of unlocking `ValidatedTextareaControl` from the `@wordpress/components` private APIs ([#81984](https://github.com/WordPress/gutenberg/pull/81984)).

### Enhancements

Expand Down
13 changes: 4 additions & 9 deletions packages/block-library/src/math/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,10 @@ import {
useBlockProps,
store as blockEditorStore,
} from '@wordpress/block-editor';
import {
Popover,
privateApis as componentsPrivateApis,
} from '@wordpress/components';
import { Popover } from '@wordpress/components';
import { ValidatedTextareaControl } from '@wordpress/ui';
import { useState, useEffect, useRef } from '@wordpress/element';
import { useDispatch } from '@wordpress/data';
import { unlock } from '../lock-unlock';

const { ValidatedTextareaControl } = unlock( componentsPrivateApis );

export default function MathEdit( { attributes, setAttributes, isSelected } ) {
const { latex, mathML } = attributes;
Expand Down Expand Up @@ -78,14 +73,14 @@ export default function MathEdit( { attributes, setAttributes, isSelected } ) {
<ValidatedTextareaControl
label={ __( 'LaTeX math syntax' ) }
hideLabelFromVision
value={ latex }

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working as expected:

Screen.Recording.2026-08-24.at.17.02.32.mov
Image

value={ latex ?? '' }
className="wp-block-math__textarea-control"
customValidity={
error
? { type: 'invalid', message: error }
: undefined
}
onChange={ ( newLatex ) => {
onValueChange={ ( newLatex ) => {
if ( ! latexToMathML ) {
setAttributes( { latex: newLatex } );
return;
Expand Down
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
- `ControlWithError`: Remove the internal implementation in favor of the `ControlWithError` component from `@wordpress/ui` ([#81230](https://github.com/WordPress/gutenberg/issues/81230)) ([#81574](https://github.com/WordPress/gutenberg/pull/81574)).
- Remove the unused `components-validated-control__indicator*` styles; the indicator is now rendered and styled by `ValidityIndicator` from `@wordpress/ui` ([#81230](https://github.com/WordPress/gutenberg/issues/81230)) ([#81574](https://github.com/WordPress/gutenberg/pull/81574)).
- Deprecate `ValidatedInputControl` in the private APIs; use `ValidatedInputControl` from `@wordpress/ui` instead ([#81627](https://github.com/WordPress/gutenberg/pull/81627)).
- Deprecate `ValidatedTextareaControl` in the private APIs; use `ValidatedTextareaControl` from `@wordpress/ui` instead ([#81984](https://github.com/WordPress/gutenberg/pull/81984)).

## 39.0.0 (2026-08-12)

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { forwardRef, useRef } from '@wordpress/element';
import { useMergeRefs } from '@wordpress/compose';
import deprecated from '@wordpress/deprecated';
import { ControlWithError } from '@wordpress/ui';
import type { ValidatedControlProps } from './types';
import TextareaControl from '../../textarea-control';
Expand All @@ -13,6 +14,12 @@ const UnforwardedValidatedTextareaControl = (
}: React.ComponentProps< typeof TextareaControl > & ValidatedControlProps,
forwardedRef: React.ForwardedRef< HTMLTextAreaElement >
) => {
deprecated( 'wp.components.privateApis.ValidatedTextareaControl', {
since: '7.2',
alternative: 'ValidatedTextareaControl from @wordpress/ui',
hint: 'This private API will be completely removed within a few Gutenberg plugin releases.',
} );

const validityTargetRef = useRef< HTMLTextAreaElement >( null );
const mergedRefs = useMergeRefs( [ forwardedRef, validityTargetRef ] );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@ import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ValidatedTextareaControl } from '../components';

describe( 'Shows a deprecation warning', () => {
it( 'ValidatedTextareaControl', () => {
render(
<ValidatedTextareaControl
label="Bio"
help="A short bio."
onChange={ () => {} }
value=""
/>
);

expect( console ).toHaveWarnedWith(
'wp.components.privateApis.ValidatedTextareaControl is deprecated since version 7.2. Please use ValidatedTextareaControl from @wordpress/ui instead. Note: This private API will be completely removed within a few Gutenberg plugin releases.'
);
} );
} );

describe( 'ValidatedTextareaControl', () => {
it( 'should preserve the help description', () => {
render(
Expand Down
1 change: 1 addition & 0 deletions packages/dataviews/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
- DataForm: Remove the vendored `ControlWithError` copy in favor of the `ControlWithError` component from `@wordpress/ui`. ([#81230](https://github.com/WordPress/gutenberg/issues/81230)) ([#81574](https://github.com/WordPress/gutenberg/pull/81574))
- DataForm: Render the `date` control's validity message with `ValidityIndicator` from `@wordpress/ui` instead of hand-rolled markup styled by `@wordpress/components` global class names. ([#81230](https://github.com/WordPress/gutenberg/issues/81230)) ([#81574](https://github.com/WordPress/gutenberg/pull/81574))
- DataForm: Use `ValidatedInputControl` from `@wordpress/ui` in the `text`, `email`, `telephone`, `url`, `password`, `color`, `datetime`, and `time` controls, instead of unlocking it from the `@wordpress/components` private APIs. Prefixes and suffixes now render in an `InputLayout.Slot` from `@wordpress/ui`. ([#81627](https://github.com/WordPress/gutenberg/pull/81627))
- DataForm: Use `ValidatedTextareaControl` from `@wordpress/ui` in the `textarea` control, instead of unlocking it from the `@wordpress/components` private APIs. This was the last private API used by the package, so the `@wordpress/private-apis` dependency is removed. ([#81984](https://github.com/WordPress/gutenberg/pull/81984))

## 18.0.0 (2026-08-12)

Expand Down
1 change: 0 additions & 1 deletion packages/dataviews/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
"@wordpress/kebab-case": "file:../kebab-case",
"@wordpress/keycodes": "file:../keycodes",
"@wordpress/primitives": "file:../primitives",
"@wordpress/private-apis": "file:../private-apis",
"@wordpress/ui": "file:../ui",
"@wordpress/warning": "file:../warning",
"clsx": "^2.1.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { privateApis } from '@wordpress/components';
import { ValidatedTextareaControl } from '@wordpress/ui';

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this is DataForm's validation story (working as expected).

import { useCallback } from '@wordpress/element';
import type { DataFormControlProps } from '../../types';
import { unlock } from '../../lock-unlock';
import getCustomValidity from './utils/get-custom-validity';

const { ValidatedTextareaControl } = unlock( privateApis );

export default function Textarea< Item >( {
data,
field,
Expand All @@ -20,7 +17,7 @@ export default function Textarea< Item >( {
const { label, placeholder, description, setValue, isValid } = field;
const value = field.getValue( { item: data } );

const onChangeControl = useCallback(
const onValueChangeControl = useCallback(
( newValue: string ) =>
onChange( setValue( { item: data, value: newValue } ) ),
[ data, onChange, setValue ]
Expand All @@ -34,8 +31,13 @@ export default function Textarea< Item >( {
label={ label }
placeholder={ placeholder }
value={ value ?? '' }
help={ description }
onChange={ onChangeControl }
description={
typeof description === 'string' ? description : undefined
}
details={
typeof description === 'string' ? undefined : description
}
Comment on lines +34 to +39

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

description can be a string or a React element (type).

onValueChange={ onValueChangeControl }
rows={ rows }
disabled={ disabled }
minLength={
Expand Down
7 changes: 0 additions & 7 deletions packages/dataviews/src/lock-unlock.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/dataviews/tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
{ "path": "../kebab-case" },
{ "path": "../keycodes/tsconfig.build.json" },
{ "path": "../primitives" },
{ "path": "../private-apis" },
{ "path": "../theme/tsconfig.build.json" },
{ "path": "../ui/tsconfig.build.json" },
{ "path": "../url" },
Expand Down
1 change: 0 additions & 1 deletion packages/dataviews/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
{ "path": "../kebab-case" },
{ "path": "../keycodes/tsconfig.build.json" },
{ "path": "../primitives" },
{ "path": "../private-apis" },
{ "path": "../theme/tsconfig.build.json" },
{ "path": "../ui/tsconfig.build.json" },
{ "path": "../url" },
Expand Down
1 change: 0 additions & 1 deletion packages/dataviews/tsconfig.stories.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
{ "path": "../kebab-case" },
{ "path": "../keycodes/tsconfig.build.json" },
{ "path": "../primitives" },
{ "path": "../private-apis" },
{ "path": "../theme/tsconfig.build.json" },
{ "path": "../ui/tsconfig.build.json" },
{ "path": "../url" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ ruleTester.run( 'use-recommended-components', rule, {
"import { KeyboardShortcutDescription, KeyboardShortcutDisplay, useKeyboardShortcutProps } from '@wordpress/ui';",
"import { ControlWithError } from '@wordpress/ui';",
"import { ValidatedInputControl, InputLayout } from '@wordpress/ui';",
"import { ValidatedTextareaControl } from '@wordpress/ui';",

// Unlocked private APIs are only checked for denied names.
"import { privateApis } from '@wordpress/components'; import { unlock } from '../../lock-unlock'; const { SomethingElse } = unlock( privateApis );",
Expand Down
1 change: 1 addition & 0 deletions packages/eslint-plugin/rules/use-recommended-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const ALLOWLIST = {
'TextareaControl',
'Tooltip',
'ValidatedInputControl',
'ValidatedTextareaControl',
'ValidityIndicator',
'VisuallyHidden',
'useKeyboardShortcutProps',
Expand Down
1 change: 1 addition & 0 deletions packages/ui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- Add `ControlWithError` component, ported from `@wordpress/components` and `@wordpress/dataviews` ([#81230](https://github.com/WordPress/gutenberg/issues/81230)) ([#81574](https://github.com/WordPress/gutenberg/pull/81574)).
- Export `ValidityIndicator`, so custom validated controls can render the same validity message as `ControlWithError` ([#81230](https://github.com/WordPress/gutenberg/issues/81230)) ([#81574](https://github.com/WordPress/gutenberg/pull/81574)).
- Add `ValidatedInputControl` component, an `InputControl` wrapped in `ControlWithError` ([#81627](https://github.com/WordPress/gutenberg/pull/81627)).
- Add `ValidatedTextareaControl` component, a `TextareaControl` wrapped in `ControlWithError` ([#81984](https://github.com/WordPress/gutenberg/pull/81984)).

### Breaking Changes

Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/form/with-validation/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './validated-input-control';
export * from './validated-textarea-control';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ValidatedTextareaControl } from './validated-textarea-control';
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { useState } from '@wordpress/element';
import { ValidatedTextareaControl } from '../index';
import { formDecorator } from '../../../stories/shared';

const meta: Meta< typeof ValidatedTextareaControl > = {
title: 'Design System/Components/Form/With Validation/ValidatedTextareaControl',
component: ValidatedTextareaControl,
argTypes: {
customValidity: { control: false },
defaultValue: { control: false },
onValueChange: { action: 'onValueChange' },
value: { control: false },
},
decorators: [ formDecorator ],
parameters: {
componentStatus: {
status: 'use-with-caution',
whereUsed: 'global',
notes: 'APIs and behavior are still prone to change.',
},
},
};
export default meta;

type Story = StoryObj< typeof ValidatedTextareaControl >;

/**
* Native constraint validation (here, `required`) is reported inline when the
* control is blurred or the form is submitted.
*/
export const Default: Story = {
args: {
label: 'Text',
required: true,
},
};

/**
* Consumer-driven validation results are passed through the `customValidity`
* prop. Type the word "error" and blur the field to trigger the custom error.
*/
export const WithCustomValidity: Story = {
render: function Template( args ) {
const [ value, setValue ] = useState( '' );

return (
<ValidatedTextareaControl
{ ...args }
value={ value }
onValueChange={ ( next ) => setValue( next ?? '' ) }
customValidity={
value.toLowerCase() === 'error'
? {
type: 'invalid',
message: 'The word "error" is not allowed.',
}
: undefined
}
/>
);
},
args: {
label: 'Text',
description: "The word 'error' will trigger an error.",
},
};
Loading
Loading