@@ -1053,7 +1053,6 @@ export class SettingsRenderer implements ITreeRenderer {
10531053 template . labelElement . textContent = element . displayLabel ;
10541054 template . labelElement . title = titleTooltip ;
10551055
1056- this . renderValue ( element , templateId , < ISettingItemTemplate > template ) ;
10571056 template . descriptionElement . innerHTML = '' ;
10581057 if ( element . setting . descriptionIsMarkdown ) {
10591058 const renderedDescription = this . renderDescriptionMarkdown ( element , element . description , template . toDispose ) ;
@@ -1065,11 +1064,6 @@ export class SettingsRenderer implements ITreeRenderer {
10651064 const baseId = ( element . displayCategory + '_' + element . displayLabel ) . replace ( / / g, '_' ) . toLowerCase ( ) ;
10661065 template . descriptionElement . id = baseId + '_setting_description' ;
10671066
1068- if ( templateId === SETTINGS_BOOL_TEMPLATE_ID ) {
1069- // Add checkbox target to description clickable and able to toggle checkbox
1070- template . descriptionElement . setAttribute ( 'checkbox_label_target_id' , baseId + '_setting_item' ) ;
1071- }
1072-
10731067 template . otherOverridesElement . innerHTML = '' ;
10741068
10751069 if ( element . overriddenScopeList . length ) {
@@ -1097,8 +1091,11 @@ export class SettingsRenderer implements ITreeRenderer {
10971091 e . stopPropagation ( ) ;
10981092 } ) ;
10991093 }
1094+
11001095 }
11011096
1097+ this . renderValue ( element , templateId , < ISettingItemTemplate > template ) ;
1098+
11021099 // Remove tree attributes - sometimes overridden by tree - should be managed there
11031100 template . containerElement . parentElement . removeAttribute ( 'role' ) ;
11041101 template . containerElement . parentElement . removeAttribute ( 'aria-level' ) ;
@@ -1165,23 +1162,7 @@ export class SettingsRenderer implements ITreeRenderer {
11651162 template . onChange = onChange ;
11661163
11671164 // Setup and add ARIA attributes
1168- // Create id and label for control/input element - parent is wrapper div
1169- const baseId = ( dataElement . displayCategory + '_' + dataElement . displayLabel ) . replace ( / / g, '_' ) . toLowerCase ( ) ;
1170- const modifiedText = dataElement . isConfigured ? 'Modified' : '' ;
1171- const label = dataElement . displayCategory + ' ' + dataElement . displayLabel + ' ' + modifiedText ;
1172-
1173- // We use the parent control div for the aria-labelledby target
1174- // Does not appear you can use the direct label on the element itself within a tree
1175- template . checkbox . domNode . parentElement . id = baseId + '_setting_label' ;
1176- template . checkbox . domNode . parentElement . setAttribute ( 'aria-label' , label ) ;
1177-
1178- // Labels will not be read on descendent input elements of the parent treeitem
1179- // unless defined as role=treeitem and indirect aria-labelledby approach
1180- template . checkbox . domNode . id = baseId + '_setting_item' ;
1181- template . checkbox . domNode . setAttribute ( 'role' , 'checkbox' ) ;
1182- template . checkbox . domNode . setAttribute ( 'aria-labelledby' , baseId + '_setting_label' ) ;
1183- template . checkbox . domNode . setAttribute ( 'aria-describedby' , baseId + '_setting_description' ) ;
1184-
1165+ this . setElementAriaLabels ( dataElement , SETTINGS_BOOL_TEMPLATE_ID , template ) ;
11851166 }
11861167
11871168 private renderEnum ( dataElement : SettingsTreeSettingElement , template : ISettingEnumItemTemplate , onChange : ( value : string ) => void ) : void {
@@ -1198,88 +1179,41 @@ export class SettingsRenderer implements ITreeRenderer {
11981179 isMarkdown : enumDescriptionsAreMarkdown
11991180 } ) ) ;
12001181
1201- const modifiedText = dataElement . isConfigured ? 'Modified' : '' ;
1202-
1203- // Use ',.' as reader pause
1204- const label = dataElement . displayCategory + ' ' + dataElement . displayLabel + ',. ' + modifiedText ;
1205- const baseId = ( dataElement . displayCategory + '_' + dataElement . displayLabel ) . replace ( / / g, '_' ) . toLowerCase ( ) ;
1206-
1182+ const label = this . setElementAriaLabels ( dataElement , SETTINGS_ENUM_TEMPLATE_ID , template ) ;
12071183 template . selectBox . setAriaLabel ( label ) ;
12081184
12091185 const idx = dataElement . setting . enum . indexOf ( dataElement . value ) ;
12101186 template . onChange = null ;
12111187 template . selectBox . select ( idx ) ;
12121188 template . onChange = idx => onChange ( dataElement . setting . enum [ idx ] ) ;
12131189
1214- if ( template . controlElement . firstElementChild ) {
1215- // SelectBox needs to have treeitem changed to combobox to read correctly within tree
1216- template . controlElement . firstElementChild . setAttribute ( 'role' , 'combobox' ) ;
1217- template . controlElement . firstElementChild . setAttribute ( 'aria-describedby' , baseId + '_setting_description settings_aria_more_actions_shortcut_label' ) ;
1218- }
1219-
12201190 template . enumDescriptionElement . innerHTML = '' ;
12211191 }
12221192
12231193 private renderText ( dataElement : SettingsTreeSettingElement , template : ISettingTextItemTemplate , onChange : ( value : string ) => void ) : void {
1224- const modifiedText = dataElement . isConfigured ? 'Modified' : '' ;
12251194
1226- // Use ',.' as reader pause
1227- const label = dataElement . displayCategory + ' ' + dataElement . displayLabel + ',. ' + modifiedText ;
1195+ const label = this . setElementAriaLabels ( dataElement , SETTINGS_TEXT_TEMPLATE_ID , template ) ;
1196+
12281197 template . onChange = null ;
12291198 template . inputBox . value = dataElement . value ;
12301199 template . onChange = value => { renderValidations ( dataElement , template , false , label ) ; onChange ( value ) ; } ;
12311200
1232- // Setup and add ARIA attributes
1233- // Create id and label for control/input element - parent is wrapper div
1234- const baseId = ( dataElement . displayCategory + '_' + dataElement . displayLabel ) . replace ( / / g, '_' ) . toLowerCase ( ) ;
1235-
1236- // We use the parent control div for the aria-labelledby target
1237- // Does not appear you can use the direct label on the element itself within a tree
1238- template . inputBox . inputElement . parentElement . id = baseId + '_setting_label' ;
1239- template . inputBox . inputElement . parentElement . setAttribute ( 'aria-label' , label ) ;
1240-
1241- // Labels will not be read on descendent input elements of the parent treeitem
1242- // unless defined as role=treeitem and indirect aria-labelledby approach
1243- template . inputBox . inputElement . id = baseId + '_setting_item' ;
1244- template . inputBox . inputElement . setAttribute ( 'role' , 'textbox' ) ;
1245- template . inputBox . inputElement . setAttribute ( 'aria-labelledby' , baseId + '_setting_label' ) ;
1246- template . inputBox . inputElement . setAttribute ( 'aria-describedby' , baseId + '_setting_description settings_aria_more_actions_shortcut_label' ) ;
1247-
12481201 renderValidations ( dataElement , template , true , label ) ;
12491202 }
12501203
1251-
12521204 private renderNumber ( dataElement : SettingsTreeSettingElement , template : ISettingTextItemTemplate , onChange : ( value : number ) => void ) : void {
1253- const modifiedText = dataElement . isConfigured ? 'Modified' : '' ;
1254-
1255- // Use ',.' as reader pause
1256- const label = dataElement . displayCategory + ' ' + dataElement . displayLabel + ' number,. ' + modifiedText ;
12571205 const numParseFn = ( dataElement . valueType === 'integer' || dataElement . valueType === 'nullable-integer' )
12581206 ? parseInt : parseFloat ;
12591207
12601208 const nullNumParseFn = ( dataElement . valueType === 'nullable-integer' || dataElement . valueType === 'nullable-number' )
12611209 ? ( v => v === '' ? null : numParseFn ( v ) ) : numParseFn ;
12621210
1211+ const label = this . setElementAriaLabels ( dataElement , SETTINGS_NUMBER_TEMPLATE_ID , template ) ;
1212+
12631213 template . onChange = null ;
12641214 template . inputBox . value = dataElement . value ;
12651215 template . onChange = value => { renderValidations ( dataElement , template , false , label ) ; onChange ( nullNumParseFn ( value ) ) ; } ;
12661216
1267- // Setup and add ARIA attributes
1268- // Create id and label for control/input element - parent is wrapper div
1269- const baseId = ( dataElement . displayCategory + '_' + dataElement . displayLabel ) . replace ( / / g, '_' ) . toLowerCase ( ) ;
1270-
1271- // We use the parent control div for the aria-labelledby target
1272- // Does not appear you can use the direct label on the element itself within a tree
1273- template . inputBox . inputElement . parentElement . id = baseId + '_setting_label' ;
1274- template . inputBox . inputElement . parentElement . setAttribute ( 'aria-label' , label ) ;
1275-
1276- // Labels will not be read on descendent input elements of the parent treeitem
1277- // unless defined as role=treeitem and indirect aria-labelledby approach
1278- template . inputBox . inputElement . id = baseId + '_setting_item' ;
1279- template . inputBox . inputElement . setAttribute ( 'role' , 'textbox' ) ;
1280- template . inputBox . inputElement . setAttribute ( 'aria-labelledby' , baseId + '_setting_label' ) ;
1281- template . inputBox . inputElement . setAttribute ( 'aria-describedby' , baseId + '_setting_description settings_aria_more_actions_shortcut_label' ) ;
1282-
12831217 renderValidations ( dataElement , template , true , label ) ;
12841218 }
12851219
@@ -1293,6 +1227,64 @@ export class SettingsRenderer implements ITreeRenderer {
12931227 template . onChange = ( ) => this . _onDidOpenSettings . fire ( dataElement . setting . key ) ;
12941228 }
12951229
1230+
1231+ private setElementAriaLabels ( dataElement : SettingsTreeSettingElement , templateId : string , template : ISettingItemTemplate ) : string {
1232+ // Create base Id for element references
1233+ const baseId = ( dataElement . displayCategory + '_' + dataElement . displayLabel ) . replace ( / / g, '_' ) . toLowerCase ( ) ;
1234+
1235+ const modifiedText = template . otherOverridesElement . textContent ?
1236+ template . otherOverridesElement . textContent : ( dataElement . isConfigured ? localize ( 'settings.Modified' , ' Modified. ' ) : '' ) ;
1237+
1238+ let itemElement = null ;
1239+
1240+ // Use '.' as reader pause
1241+ let label = dataElement . displayCategory + ' ' + dataElement . displayLabel + '. ' ;
1242+
1243+ // Setup and add ARIA attributes
1244+ // Create id and label for control/input element - parent is wrapper div
1245+
1246+ if ( templateId === SETTINGS_TEXT_TEMPLATE_ID ) {
1247+ if ( itemElement = ( < ISettingTextItemTemplate > template ) . inputBox . inputElement ) {
1248+ itemElement . setAttribute ( 'role' , 'textbox' ) ;
1249+ label += modifiedText ;
1250+ }
1251+ } else if ( templateId === SETTINGS_NUMBER_TEMPLATE_ID ) {
1252+ if ( itemElement = ( < ISettingNumberItemTemplate > template ) . inputBox . inputElement ) {
1253+ itemElement . setAttribute ( 'role' , 'textbox' ) ;
1254+ label += ' number. ' + modifiedText ;
1255+ }
1256+ } else if ( templateId === SETTINGS_BOOL_TEMPLATE_ID ) {
1257+ if ( itemElement = ( < ISettingBoolItemTemplate > template ) . checkbox . domNode ) {
1258+ itemElement . setAttribute ( 'role' , 'checkbox' ) ;
1259+ label += modifiedText ;
1260+ // Add checkbox target to description clickable and able to toggle checkbox
1261+ template . descriptionElement . setAttribute ( 'checkbox_label_target_id' , baseId + '_setting_item' ) ;
1262+ }
1263+ } else if ( templateId === SETTINGS_ENUM_TEMPLATE_ID ) {
1264+ if ( itemElement = template . controlElement . firstElementChild ) {
1265+ itemElement . setAttribute ( 'role' , 'combobox' ) ;
1266+ label += modifiedText ;
1267+ }
1268+ } else {
1269+ // Don't change attributes if we don't know what we areFunctions
1270+ return '' ;
1271+ }
1272+
1273+ // We don't have control element, return empty label
1274+ if ( ! itemElement ) {
1275+ return '' ;
1276+ }
1277+
1278+ // Labels will not be read on descendent input elements of the parent treeitem
1279+ // unless defined as roles for input items
1280+ // voiceover does not seem to use labeledby correctly, set labels directly on input elements
1281+ itemElement . id = baseId + '_setting_item' ;
1282+ itemElement . setAttribute ( 'aria-label' , label ) ;
1283+ itemElement . setAttribute ( 'aria-describedby' , baseId + '_setting_description settings_aria_more_actions_shortcut_label' ) ;
1284+
1285+ return label ;
1286+ }
1287+
12961288 disposeTemplate ( tree : ITree , templateId : string , template : IDisposableTemplate ) : void {
12971289 dispose ( template . toDispose ) ;
12981290 }
@@ -1429,6 +1421,9 @@ export class SettingsAccessibilityProvider implements IAccessibilityProvider {
14291421 }
14301422
14311423 if ( element instanceof SettingsTreeSettingElement ) {
1424+ if ( element . valueType === 'boolean' ) {
1425+ return '' ;
1426+ }
14321427 return localize ( 'settingRowAriaLabel' , "{0} {1}, Setting" , element . displayCategory , element . displayLabel ) ;
14331428 }
14341429
0 commit comments