Menus: Validate custom links and add accessible error messages.

Add URL validation in the admin navigation menu manager that matches the validation in the customizer when adding custom links. Improve accessibility of both custom link forms by adding `aria-invalid` and `aria-describedby` attributes with visible error messages and announcing the error using `wp.a11y.speak()`.

Props joedolson, nikitasolanki1812, akrocks, pathan-amaankhan, rcreators, ironprogrammer, audrasjb, ankit-k-gupta, chaion07, rinkalpagdar, snehapatil02, jainil07, parthvataliya.
Fixes #60619, #60969.
Built from https://develop.svn.wordpress.org/trunk@59948


git-svn-id: http://core.svn.wordpress.org/trunk@59290 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
joedolson
2025-03-06 23:48:23 +00:00
parent f9f39afce6
commit e57e4cdf1a
11 changed files with 112 additions and 17 deletions

View File

@@ -223,6 +223,9 @@
this.$el.on( 'input', '#custom-menu-item-name.invalid, #custom-menu-item-url.invalid', function() {
$( this ).removeClass( 'invalid' );
var errorMessageId = $( this ).attr( 'aria-describedby' );
$( '#' + errorMessageId ).hide();
$( this ).removeAttr( 'aria-invalid' ).removeAttr( 'aria-describedby' );
});
// Load available items if it looks like we'll need them.
@@ -546,8 +549,11 @@
var menuItem,
itemName = $( '#custom-menu-item-name' ),
itemUrl = $( '#custom-menu-item-url' ),
urlErrorMessage = $( '#custom-url-error' ),
nameErrorMessage = $( '#custom-name-error' ),
url = itemUrl.val().trim(),
urlRegex;
urlRegex,
errorText;
if ( ! this.currentMenuControl ) {
return;
@@ -566,15 +572,37 @@
* so this pattern does not need to be complete.
*/
urlRegex = /^((\w+:)?\/\/\w.*|\w+:(?!\/\/$)|\/|\?|#)/;
if ( '' === itemName.val() ) {
itemName.addClass( 'invalid' );
return;
} else if ( ! urlRegex.test( url ) ) {
itemUrl.addClass( 'invalid' );
if ( ! urlRegex.test( url ) || '' === itemName.val() ) {
if ( ! urlRegex.test( url ) ) {
itemUrl.addClass( 'invalid' )
.attr( 'aria-invalid', 'true' )
.attr( 'aria-describedby', 'custom-url-error' );
urlErrorMessage.show();
errorText = urlErrorMessage.text();
// Announce error message via screen reader
wp.a11y.speak( errorText, 'assertive' );
}
if ( '' === itemName.val() ) {
itemName.addClass( 'invalid' )
.attr( 'aria-invalid', 'true' )
.attr( 'aria-describedby', 'custom-name-error' );
nameErrorMessage.show();
errorText = ( '' === errorText ) ? nameErrorMessage.text() : errorText + nameErrorMessage.text();
// Announce error message via screen reader
wp.a11y.speak( errorText, 'assertive' );
}
return;
}
urlErrorMessage.hide();
nameErrorMessage.hide();
itemName.removeClass( 'invalid' )
.removeAttr( 'aria-invalid', 'true' )
.removeAttr( 'aria-describedby', 'custom-name-error' );
itemUrl.removeClass( 'invalid' )
.removeAttr( 'aria-invalid', 'true' )
.removeAttr( 'aria-describedby', 'custom-name-error' );
menuItem = {
'title': itemName.val(),
'url': url,

File diff suppressed because one or more lines are too long

View File

@@ -1102,13 +1102,53 @@
}, 500 ) );
$('#add-custom-links input[type="text"]').on( 'keypress', function(e){
$('#customlinkdiv').removeClass('form-invalid');
$( '#customlinkdiv' ).removeClass( 'form-invalid' );
$( '#custom-menu-item-url' ).removeAttr( 'aria-invalid' ).removeAttr( 'aria-describedby' );
$( '#custom-url-error' ).hide();
if ( e.keyCode === 13 ) {
e.preventDefault();
$( '#submit-customlinkdiv' ).trigger( 'click' );
}
});
$( '#submit-customlinkdiv' ).on( 'click', function (e) {
var urlInput = $( '#custom-menu-item-url' ),
url = urlInput.val().trim(),
errorMessage = $( '#custom-url-error' ),
urlWrap = $( '#menu-item-url-wrap' ),
urlRegex;
// Hide the error message initially
errorMessage.hide();
urlWrap.removeClass( 'has-error' );
/*
* Allow URLs including:
* - http://example.com/
* - //example.com
* - /directory/
* - ?query-param
* - #target
* - mailto:foo@example.com
*
* Any further validation will be handled on the server when the setting is attempted to be saved,
* so this pattern does not need to be complete.
*/
urlRegex = /^((\w+:)?\/\/\w.*|\w+:(?!\/\/$)|\/|\?|#)/;
if ( ! urlRegex.test( url ) ) {
e.preventDefault();
urlInput.addClass( 'form-invalid' )
.attr( 'aria-invalid', 'true' )
.attr( 'aria-describedby', 'custom-url-error' );
errorMessage.show();
var errorText = errorMessage.text();
urlWrap.addClass( 'has-error' );
// Announce error message via screen reader
wp.a11y.speak( errorText, 'assertive' );
}
});
},
/**
@@ -1389,7 +1429,8 @@
addCustomLink : function( processMethod ) {
var url = $('#custom-menu-item-url').val().toString(),
label = $('#custom-menu-item-name').val();
label = $('#custom-menu-item-name').val(),
urlRegex;
if ( '' !== url ) {
url = url.trim();
@@ -1397,7 +1438,20 @@
processMethod = processMethod || api.addMenuItemToBottom;
if ( '' === url || 'https://' == url || 'http://' == url ) {
/*
* Allow URLs including:
* - http://example.com/
* - //example.com
* - /directory/
* - ?query-param
* - #target
* - mailto:foo@example.com
*
* Any further validation will be handled on the server when the setting is attempted to be saved,
* so this pattern does not need to be complete.
*/
urlRegex = /^((\w+:)?\/\/\w.*|\w+:(?!\/\/$)|\/|\?|#)/;
if ( ! urlRegex.test( url ) ) {
$('#customlinkdiv').addClass('form-invalid');
return false;
}

File diff suppressed because one or more lines are too long