Skip to content

Commit d021369

Browse files
committed
Implement validation slug
1 parent 3b9bb77 commit d021369

File tree

8 files changed

+53
-17
lines changed

8 files changed

+53
-17
lines changed

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/dataviews/src/normalize-fields.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,8 @@ export function normalizeFields< Item >(
3838

3939
const isValid =
4040
field.isValid ??
41-
function isValid( item, context ) {
42-
return fieldTypeDefinition.isValid(
43-
getValue( { item } ),
44-
context
45-
);
41+
function isValid( value, item, context ) {
42+
return fieldTypeDefinition.isValid( value, context );
4643
};
4744

4845
const Edit = getControl( field, fieldTypeDefinition );

packages/dataviews/src/types.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,11 @@ export type Field< Item > = {
121121
/**
122122
* Callback used to validate the field.
123123
*/
124-
isValid?: ( item: Item, context?: ValidationContext ) => boolean;
124+
isValid?: (
125+
value: any,
126+
item?: Item,
127+
context?: ValidationContext
128+
) => boolean;
125129

126130
/**
127131
* Callback used to decide if a field should be displayed.
@@ -167,7 +171,7 @@ export type NormalizedField< Item > = Field< Item > & {
167171
render: ComponentType< DataViewRenderFieldProps< Item > >;
168172
Edit: ComponentType< DataFormControlProps< Item > >;
169173
sort: ( a: Item, b: Item, direction: SortDirection ) => number;
170-
isValid: ( item: Item, context?: ValidationContext ) => boolean;
174+
isValid: ( value: any, item: Item, context?: ValidationContext ) => boolean;
171175
enableHiding: boolean;
172176
enableSorting: boolean;
173177
};

packages/dataviews/src/validation.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export function isItemValid< Item >(
1313
fields.filter( ( { id } ) => !! form.fields?.includes( id ) )
1414
);
1515
return _fields.every( ( field ) => {
16-
return field.isValid( item, { elements: field.elements } );
16+
const value = field.getValue( { item } );
17+
return field.isValid( value, item, { elements: field.elements } );
1718
} );
1819
}

packages/fields/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"@wordpress/warning": "*",
5656
"change-case": "4.1.2",
5757
"client-zip": "^2.4.5",
58+
"clsx": "^2.1.1",
5859
"remove-accents": "^0.5.0"
5960
},
6061
"peerDependencies": {

packages/fields/src/fields/slug/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,16 @@ import type { BasePost } from '../../types';
1010
import { __ } from '@wordpress/i18n';
1111
import SlugEdit from './slug-edit';
1212
import SlugView from './slug-view';
13+
import { getSlug } from './utils';
1314

1415
const slugField: Field< BasePost > = {
1516
id: 'slug',
1617
type: 'text',
1718
label: __( 'Slug' ),
18-
getValue: ( { item } ) => item.slug,
19+
getValue: ( { item } ) => getSlug( item ),
20+
isValid: ( value ) => {
21+
return ( value && value.length > 0 ) || false;
22+
},
1923
Edit: SlugEdit,
2024
render: SlugView,
2125
};

packages/fields/src/fields/slug/slug-edit.tsx

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,21 @@ import {
1111
import { copySmall } from '@wordpress/icons';
1212
import { useCopyToClipboard, useInstanceId } from '@wordpress/compose';
1313
import { useDispatch } from '@wordpress/data';
14-
import { useCallback, useEffect, useRef } from '@wordpress/element';
14+
import { useCallback, useEffect, useRef, useState } from '@wordpress/element';
1515
import { store as noticesStore } from '@wordpress/notices';
1616
import { safeDecodeURIComponent } from '@wordpress/url';
1717
import type { DataFormControlProps } from '@wordpress/dataviews';
1818
import { __ } from '@wordpress/i18n';
1919

20+
/**
21+
* External dependencies
22+
*/
23+
import clsx from 'clsx';
24+
2025
/**
2126
* Internal dependencies
2227
*/
2328
import type { BasePost } from '../../types';
24-
import { getSlug } from './utils';
2529

2630
const SlugEdit = ( {
2731
field,
@@ -30,7 +34,10 @@ const SlugEdit = ( {
3034
}: DataFormControlProps< BasePost > ) => {
3135
const { id } = field;
3236

33-
const slug = field.getValue( { item: data } ) || getSlug( data );
37+
const slug = field.getValue( { item: data } );
38+
39+
const [ isValid, setIsValid ] = useState( true );
40+
3441
const permalinkTemplate = data.permalink_template || '';
3542
const PERMALINK_POSTNAME_REGEX = /%(?:postname|pagename)%/;
3643
const [ prefix, suffix ] = permalinkTemplate.split(
@@ -40,7 +47,7 @@ const SlugEdit = ( {
4047
const permalinkSuffix = suffix;
4148
const isEditable = PERMALINK_POSTNAME_REGEX.test( permalinkTemplate );
4249
const originalSlugRef = useRef( slug );
43-
const slugToDisplay = slug || originalSlugRef.current;
50+
const slugToDisplay = slug;
4451
const permalink = isEditable
4552
? `${ permalinkPrefix }${ slugToDisplay }${ permalinkSuffix }`
4653
: safeDecodeURIComponent( data.link || '' );
@@ -52,11 +59,13 @@ const SlugEdit = ( {
5259
}, [ slug ] );
5360

5461
const onChangeControl = useCallback(
55-
( newValue?: string ) =>
62+
( newValue?: string ) => {
63+
setIsValid( field.isValid( newValue ?? '' ) );
5664
onChange( {
5765
[ id ]: newValue,
58-
} ),
59-
[ id, onChange ]
66+
} );
67+
},
68+
[ field, id, onChange ]
6069
);
6170

6271
const { createNotice } = useDispatch( noticesStore );
@@ -106,7 +115,9 @@ const SlugEdit = ( {
106115
autoComplete="off"
107116
spellCheck="false"
108117
type="text"
109-
className="fields-controls__slug-input"
118+
className={ clsx( 'fields-controls__slug-input', {
119+
'fields-controls__slug-input--invalid': ! isValid,
120+
} ) }
110121
onChange={ ( newValue?: string ) => {
111122
onChangeControl( newValue );
112123
} }
@@ -117,6 +128,11 @@ const SlugEdit = ( {
117128
} }
118129
aria-describedby={ postUrlSlugDescriptionId }
119130
/>
131+
{ ! isValid && (
132+
<div className="fields-controls__slug-error">
133+
<span>{ __( 'The slug is invalid.' ) }</span>
134+
</div>
135+
) }
120136
<div className="fields-controls__slug-help">
121137
<span className="fields-controls__slug-help-visual-label">
122138
{ __( 'Permalink:' ) }

packages/fields/src/fields/slug/style.scss

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@
77
padding-inline-start: 0 !important;
88
}
99

10+
.fields-controls__slug-input--invalid {
11+
.components-input-control__backdrop,
12+
.components-input-control__backdrop:focus-within {
13+
border-color: $alert-red !important;
14+
box-shadow: 0 0 0 0.5px $alert-red !important;
15+
}
16+
}
17+
18+
.fields-controls__slug-error {
19+
color: $alert-red;
20+
}
21+
1022
.fields-controls__slug-help-link {
1123
word-break: break-word;
1224
}

0 commit comments

Comments
 (0)