1- import { getRole } from '../../commons/aria' ;
1+ import { getRole , getRoleType } from '../../commons/aria' ;
22import { sanitize , subtreeText } from '../../commons/text' ;
33import standards from '../../standards' ;
4+ import memoize from '../../core/utils/memoize' ;
45
56/**
67 * Check that an element does not use any prohibited ARIA attributes.
@@ -33,9 +34,14 @@ export default function ariaProhibitedAttrEvaluate(
3334) {
3435 const elementsAllowedAriaLabel = options ?. elementsAllowedAriaLabel || [ ] ;
3536 const { nodeName } = virtualNode . props ;
36- const role = getRole ( virtualNode , { chromium : true } ) ;
37+ const role = getRole ( virtualNode , {
38+ chromium : true ,
39+ // this check allows fallback roles. For example, `<div role="foo img" aria-label="...">` is legal.
40+ fallback : true
41+ } ) ;
3742
3843 const prohibitedList = listProhibitedAttrs (
44+ virtualNode ,
3945 role ,
4046 nodeName ,
4147 elementsAllowedAriaLabel
@@ -51,7 +57,7 @@ export default function ariaProhibitedAttrEvaluate(
5157 return false ;
5258 }
5359
54- let messageKey = virtualNode . hasAttr ( ' role' ) ? 'hasRole' : 'noRole' ;
60+ let messageKey = role !== null ? 'hasRole' : 'noRole' ;
5561 messageKey += prohibited . length > 1 ? 'Plural' : 'Singular' ;
5662 this . data ( { role, nodeName, messageKey, prohibited } ) ;
5763
@@ -64,13 +70,32 @@ export default function ariaProhibitedAttrEvaluate(
6470 return true ;
6571}
6672
67- function listProhibitedAttrs ( role , nodeName , elementsAllowedAriaLabel ) {
73+ function listProhibitedAttrs ( vNode , role , nodeName , elementsAllowedAriaLabel ) {
6874 const roleSpec = standards . ariaRoles [ role ] ;
6975 if ( roleSpec ) {
7076 return roleSpec . prohibitedAttrs || [ ] ;
7177 }
72- if ( ! ! role || elementsAllowedAriaLabel . includes ( nodeName ) ) {
78+ if (
79+ ! ! role ||
80+ elementsAllowedAriaLabel . includes ( nodeName ) ||
81+ getClosestAncestorRoleType ( vNode ) === 'widget'
82+ ) {
7383 return [ ] ;
7484 }
7585 return [ 'aria-label' , 'aria-labelledby' ] ;
7686}
87+
88+ const getClosestAncestorRoleType = memoize (
89+ function getClosestAncestorRoleTypeMemoized ( vNode ) {
90+ if ( ! vNode ) {
91+ return ;
92+ }
93+
94+ const role = getRole ( vNode , { noPresentational : true , chromium : true } ) ;
95+ if ( role ) {
96+ return getRoleType ( role ) ;
97+ }
98+
99+ return getClosestAncestorRoleType ( vNode . parent ) ;
100+ }
101+ ) ;
0 commit comments