Skip to content
Next Next commit
initial pattern validation version
  • Loading branch information
jorgefilipecosta committed Nov 21, 2025
commit 305bc1094576de50e467e5cf6ef6d6852b200c6d
8 changes: 8 additions & 0 deletions packages/dataviews/src/dataform-controls/textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ export default function Textarea< Item >( {
[ data, onChange, setValue ]
);

// Convert pattern to string if it's a RegExp
const pattern = isValid?.pattern
? isValid.pattern instanceof RegExp
? isValid.pattern.source
: isValid.pattern
: undefined;

return (
<ValidatedTextareaControl
required={ !! isValid?.required }
Expand All @@ -41,6 +48,7 @@ export default function Textarea< Item >( {
help={ description }
onChange={ onChangeControl }
rows={ rows }
pattern={ pattern }
__next40pxDefaultSize
__nextHasNoMarginBottom
hideLabelFromVision={ hideLabelFromVision }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ export default function ValidatedText< Item >( {
[ data, setValue, onChange ]
);

// Convert pattern to string if it's a RegExp
const pattern = isValid?.pattern
? isValid.pattern instanceof RegExp
? isValid.pattern.source
: isValid.pattern
: undefined;

return (
<ValidatedInputControl
required={ !! isValid?.required }
Expand All @@ -67,6 +74,7 @@ export default function ValidatedText< Item >( {
type={ type }
prefix={ prefix }
suffix={ suffix }
pattern={ pattern }
__next40pxDefaultSize
/>
);
Expand Down
86 changes: 86 additions & 0 deletions packages/dataviews/src/stories/dataform.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,12 @@ const ValidationComponent = ( {
date?: string;
dateRange?: string;
datetime?: string;
username?: string;
zipcode?: string;
phonePattern?: string;
emailPattern?: string;
urlPattern?: string;
textareaPattern?: string;
};

const [ post, setPost ] = useState< ValidatedItem >( {
Expand All @@ -602,6 +608,12 @@ const ValidationComponent = ( {
date: undefined,
dateRange: undefined,
datetime: undefined,
username: 'john_doe_123',
zipcode: '12345',
phonePattern: '+1-555-123-4567',
emailPattern: 'user@company.com',
urlPattern: 'https://github.com/wordpress/gutenberg',
textareaPattern: '#wordpress #gutenberg #react',
} );

// Cache for getElements functions - ensures promises are only created once
Expand Down Expand Up @@ -918,6 +930,29 @@ const ValidationComponent = ( {
custom: maybeCustomRule( customTextRule ),
},
},
{
id: 'username',
type: 'text',
label: 'Username (pattern: alphanumeric + underscore)',
placeholder: 'user_name123',
description:
'Must contain only letters, numbers, and underscores',
isValid: {
required,
pattern: /^[a-zA-Z0-9_]+$/,
},
},
{
id: 'zipcode',
type: 'text',
label: 'Zip Code (pattern: 5 digits)',
placeholder: '12345',
description: 'Must be exactly 5 digits',
isValid: {
required,
pattern: '[0-9]{5}',
},
},
{
id: 'select',
type: 'text',
Expand Down Expand Up @@ -970,6 +1005,18 @@ const ValidationComponent = ( {
custom: maybeCustomRule( customTextareaRule ),
},
},
{
id: 'textareaPattern',
type: 'text',
Edit: 'textarea',
label: 'Textarea (pattern: hashtags only)',
placeholder: '#tag1 #tag2 #tag3',
description: 'Must contain only hashtags separated by spaces',
isValid: {
required,
pattern: /^(#\w+\s*)+$/,
},
},
{
id: 'email',
type: 'email',
Expand All @@ -980,6 +1027,17 @@ const ValidationComponent = ( {
custom: maybeCustomRule( customEmailRule ),
},
},
{
id: 'emailPattern',
type: 'email',
label: 'Email (pattern: must end with @company.com)',
placeholder: 'user@company.com',
description: 'Email must be from @company.com domain',
isValid: {
required,
pattern: /^[a-zA-Z0-9._%+-]+@company\.com$/,
},
},
{
id: 'telephone',
type: 'telephone',
Expand All @@ -990,6 +1048,17 @@ const ValidationComponent = ( {
custom: maybeCustomRule( customTelephoneRule ),
},
},
{
id: 'phonePattern',
type: 'telephone',
label: 'Phone (pattern: +1-XXX-XXX-XXXX)',
placeholder: '+1-555-123-4567',
description: 'US phone format with country code',
isValid: {
required,
pattern: /^\+1-\d{3}-\d{3}-\d{4}$/,
},
},
{
id: 'url',
type: 'url',
Expand All @@ -1000,6 +1069,17 @@ const ValidationComponent = ( {
custom: maybeCustomRule( customUrlRule ),
},
},
{
id: 'urlPattern',
type: 'url',
label: 'URL (pattern: must be GitHub repo)',
placeholder: 'https://github.com/user/repo',
description: 'Must be a GitHub repository URL',
isValid: {
required,
pattern: /^https:\/\/github\.com\/[\w-]+\/[\w-]+\/?$/,
},
},
{
id: 'color',
type: 'color',
Expand Down Expand Up @@ -1158,6 +1238,8 @@ const ValidationComponent = ( {
() => ( {
fields: [
'text',
'username',
'zipcode',
{ id: 'customEdit' },
{
id: 'level1Integer',
Expand All @@ -1180,6 +1262,7 @@ const ValidationComponent = ( {
},
],
},
'emailPattern',
{
id: 'level1Telephone',
children: [
Expand All @@ -1199,10 +1282,13 @@ const ValidationComponent = ( {
},
],
},
'phonePattern',
'url',
'urlPattern',
'color',
'password',
'textarea',
'textareaPattern',
'select',
'textWithRadio',
'boolean',
Expand Down
1 change: 1 addition & 0 deletions packages/dataviews/src/types/field-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export type FieldType =
export type Rules< Item > = {
required?: boolean;
elements?: boolean;
pattern?: string | RegExp;
custom?:
| ( ( item: Item, field: NormalizedField< Item > ) => null | string )
| ( (
Expand Down