mirror of
https://github.com/google/blockly.git
synced 2026-01-04 15:40:08 +01:00
Update aria labels. In the process, refactor lists to comply with HTML5 spec to avoid ChromeVox getting confused about how many elements are in a list.
This commit is contained in:
@@ -44,7 +44,7 @@ blocklyApp.AppView = ng.core
|
|||||||
<label aria-hidden="true" hidden id="blockly-argument-input">{{'ARGUMENT_INPUT'|translate}}</label>
|
<label aria-hidden="true" hidden id="blockly-argument-input">{{'ARGUMENT_INPUT'|translate}}</label>
|
||||||
<label aria-hidden="true" hidden id="blockly-argument-menu">{{'ARGUMENT_OPTIONS_LIST'|translate}}</label>
|
<label aria-hidden="true" hidden id="blockly-argument-menu">{{'ARGUMENT_OPTIONS_LIST'|translate}}</label>
|
||||||
<label aria-hidden="true" hidden id="blockly-argument-text">{{'TEXT'|translate}}</label>
|
<label aria-hidden="true" hidden id="blockly-argument-text">{{'TEXT'|translate}}</label>
|
||||||
<label aria-hidden="true" hidden id="blockly-block-menu">{{'BLOCK_ACTION_LIST'|translate}}</label>
|
<label aria-hidden="true" hidden id="blockly-block-menu">{{'BLOCK_ACTION_LIST'|translate}} {{'FOR'|translate}}</label>
|
||||||
<label aria-hidden="true" hidden id="blockly-block-summary">{{'BLOCK_SUMMARY'|translate}}</label>
|
<label aria-hidden="true" hidden id="blockly-block-summary">{{'BLOCK_SUMMARY'|translate}}</label>
|
||||||
<label aria-hidden="true" hidden id="blockly-button">{{'BUTTON'|translate}}</label>
|
<label aria-hidden="true" hidden id="blockly-button">{{'BUTTON'|translate}}</label>
|
||||||
<label aria-hidden="true" hidden id="blockly-disabled">{{'DISABLED'|translate}}</label>
|
<label aria-hidden="true" hidden id="blockly-disabled">{{'DISABLED'|translate}}</label>
|
||||||
|
|||||||
@@ -28,44 +28,37 @@ blocklyApp.FieldComponent = ng.core
|
|||||||
.Component({
|
.Component({
|
||||||
selector: 'blockly-field',
|
selector: 'blockly-field',
|
||||||
template: `
|
template: `
|
||||||
<li [id]="idMap['listItem']" role="treeitem" *ngIf="isTextInput()"
|
<input *ngIf="isTextInput()" [id]="idMap['input']"
|
||||||
[attr.aria-level]="level">
|
[ngModel]="field.getValue()" (ngModelChange)="field.setValue($event)"
|
||||||
<input [id]="idMap['input']" [ngModel]="field.getValue()" (ngModelChange)="field.setValue($event)"
|
[disabled]="disabled" type="text" aria-label="Press Enter to edit text">
|
||||||
[disabled]="disabled" type="text" aria-label="text">
|
|
||||||
</li>
|
<input *ngIf="isNumberInput()" [id]="idMap['input']"
|
||||||
<li [id]="idMap['listItem']" role="treeitem" *ngIf="isNumberInput()"
|
[ngModel]="field.getValue()" (ngModelChange)="field.setValue($event)"
|
||||||
[attr.aria-level]="level">
|
[disabled]="disabled" type="number" aria-label="Press Enter to edit number">
|
||||||
<input [id]="idMap['input']" [ngModel]="field.getValue()" (ngModelChange)="field.setValue($event)"
|
|
||||||
[disabled]="disabled" type="number" aria-label="number">
|
<div *ngIf="isDropdown()"
|
||||||
</li>
|
[attr.aria-labelledBy]="generateAriaLabelledByAttr('blockly-argument-menu', idMap['label'])">
|
||||||
<li [id]="idMap['listItem']" role="treeitem" *ngIf="isDropdown()"
|
|
||||||
[attr.aria-labelledBy]="generateAriaLabelledByAttr('blockly-argument-menu', idMap['label'])"
|
|
||||||
[attr.aria-level]="level">
|
|
||||||
<label [id]="idMap['label']">{{'CURRENT_ARGUMENT_VALUE'|translate}} {{field.getText()}}</label>
|
<label [id]="idMap['label']">{{'CURRENT_ARGUMENT_VALUE'|translate}} {{field.getText()}}</label>
|
||||||
<ol role="group">
|
<ol role="group">
|
||||||
<li [id]="idMap[optionValue]" role="treeitem" *ngFor="#optionValue of getOptions()"
|
<li [id]="idMap[optionValue]" role="treeitem" *ngFor="#optionValue of getOptions()"
|
||||||
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap[optionValue + 'Button'], 'blockly-button')"
|
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap[optionValue + 'Button'], 'blockly-button')">
|
||||||
[attr.aria-level]="level + 1">
|
|
||||||
<button [id]="idMap[optionValue + 'Button']" (click)="handleDropdownChange(field, optionValue)"
|
<button [id]="idMap[optionValue + 'Button']" (click)="handleDropdownChange(field, optionValue)"
|
||||||
[disabled]="disabled">
|
[disabled]="disabled">
|
||||||
{{optionText[optionValue]}}
|
{{optionText[optionValue]}}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</li>
|
</div>
|
||||||
<li [id]="idMap['listItem']" role="treeitem" *ngIf="isCheckbox()"
|
|
||||||
[attr.aria-level]="level">
|
<div *ngIf="isCheckbox()">
|
||||||
// Checkboxes are not currently supported.
|
// Checkboxes are not currently supported.
|
||||||
</li>
|
</div>
|
||||||
<li [id]="idMap['listItem']" role="treeitem" *ngIf="isTextField() && hasVisibleText()"
|
|
||||||
[attr.aria-labelledBy]="utilsService.generateAriaLabelledByAttr('blockly-argument-text', idMap['label'])"
|
<label [id]="idMap['label']" *ngIf="isTextField() && hasVisibleText()">
|
||||||
[attr.aria-level]="level">
|
{{field.getText()}}
|
||||||
<label [id]="idMap['label']">
|
</label>
|
||||||
{{field.getText()}}
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
`,
|
`,
|
||||||
inputs: ['field', 'level', 'index', 'parentId', 'disabled'],
|
inputs: ['field', 'index', 'parentId', 'disabled'],
|
||||||
pipes: [blocklyApp.TranslatePipe]
|
pipes: [blocklyApp.TranslatePipe]
|
||||||
})
|
})
|
||||||
.Class({
|
.Class({
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ Blockly.Msg.COPY_TO_MARKED_SPOT = 'copy to marked spot';
|
|||||||
Blockly.Msg.TOOLBOX = 'Toolbox';
|
Blockly.Msg.TOOLBOX = 'Toolbox';
|
||||||
Blockly.Msg.WORKSPACE = 'Workspace';
|
Blockly.Msg.WORKSPACE = 'Workspace';
|
||||||
Blockly.Msg.ANY = 'any';
|
Blockly.Msg.ANY = 'any';
|
||||||
|
Blockly.Msg.FOR = 'for';
|
||||||
Blockly.Msg.STATEMENT = 'statement';
|
Blockly.Msg.STATEMENT = 'statement';
|
||||||
Blockly.Msg.VALUE = 'value';
|
Blockly.Msg.VALUE = 'value';
|
||||||
Blockly.Msg.CUT_BLOCK_MSG = 'Cut block: ';
|
Blockly.Msg.CUT_BLOCK_MSG = 'Cut block: ';
|
||||||
|
|||||||
@@ -40,57 +40,51 @@ blocklyApp.ToolboxTreeComponent = ng.core
|
|||||||
[attr.aria-level]="level + 1">
|
[attr.aria-level]="level + 1">
|
||||||
<label #label [id]="idMap['label']">{{'BLOCK_ACTION_LIST'|translate}}</label>
|
<label #label [id]="idMap['label']">{{'BLOCK_ACTION_LIST'|translate}}</label>
|
||||||
<ol role="group" *ngIf="displayBlockMenu">
|
<ol role="group" *ngIf="displayBlockMenu">
|
||||||
<li #workspaceCopy [id]="idMap['workspaceCopy']" role="treeitem"
|
<li [id]="idMap['workspaceCopy']" role="treeitem"
|
||||||
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['workspaceCopyButton'], 'blockly-button')"
|
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['workspaceCopyButton'], 'blockly-button')"
|
||||||
[attr.aria-level]="level + 2">
|
[attr.aria-level]="level + 2">
|
||||||
<button #workspaceCopyButton [id]="idMap['workspaceCopyButton']"
|
<button [id]="idMap['workspaceCopyButton']" (click)="copyToWorkspace()">
|
||||||
(click)="copyToWorkspace()">
|
|
||||||
{{'COPY_TO_WORKSPACE'|translate}}
|
{{'COPY_TO_WORKSPACE'|translate}}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li #blockCopy [id]="idMap['blockCopy']" role="treeitem"
|
<li [id]="idMap['blockCopy']" role="treeitem"
|
||||||
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['blockCopyButton'], 'blockly-button')"
|
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['blockCopyButton'], 'blockly-button')"
|
||||||
[attr.aria-level]="level + 2">
|
[attr.aria-level]="level + 2">
|
||||||
<button #blockCopyButton
|
<button [id]="idMap['blockCopyButton']" (click)="copyToClipboard()">
|
||||||
[id]="idMap['blockCopyButton']"
|
|
||||||
(click)="copyToClipboard()">
|
|
||||||
{{'COPY_TO_CLIPBOARD'|translate}}
|
{{'COPY_TO_CLIPBOARD'|translate}}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li #sendToSelected [id]="idMap['sendToSelected']" role="treeitem"
|
<li [id]="idMap['sendToSelected']" role="treeitem"
|
||||||
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['sendToSelectedButton'], 'blockly-button', !canBeCopiedToMarkedConnection())"
|
[attr.aria-labelledBy]="generateAriaLabelledByAttr(idMap['sendToSelectedButton'], 'blockly-button', !canBeCopiedToMarkedConnection())"
|
||||||
[attr.aria-level]="level + 2">
|
[attr.aria-level]="level + 2">
|
||||||
<button #sendToSelectedButton
|
<button [id]="idMap['sendToSelectedButton']" (click)="copyToMarkedSpot()"
|
||||||
[id]="idMap['sendToSelectedButton']"
|
|
||||||
(click)="copyToMarkedSpot()"
|
|
||||||
[disabled]="!canBeCopiedToMarkedConnection()">
|
[disabled]="!canBeCopiedToMarkedConnection()">
|
||||||
{{'COPY_TO_MARKED_SPOT'|translate}}
|
{{'COPY_TO_MARKED_SPOT'|translate}}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</li>
|
</li>
|
||||||
<div *ngFor="#inputBlock of block.inputList; #i=index">
|
<template ngFor #inputBlock [ngForOf]="block.inputList" #i="index">
|
||||||
<blockly-field *ngFor="#field of inputBlock.fieldRow; #j=index"
|
<li role="treeitem" [id]="idMap['listItem' + i]" [attr.aria-level]="level + 1">
|
||||||
[field]="field" [level]="level + 1" [disabled]="true">
|
<blockly-field *ngFor="#field of inputBlock.fieldRow" [field]="field" [disabled]="true">
|
||||||
</blockly-field>
|
</blockly-field>
|
||||||
|
</li>
|
||||||
|
|
||||||
<blockly-toolbox-tree *ngIf="inputBlock.connection && inputBlock.connection.targetBlock()"
|
<blockly-toolbox-tree *ngIf="inputBlock.connection && inputBlock.connection.targetBlock()"
|
||||||
[block]="inputBlock.connection.targetBlock()"
|
[block]="inputBlock.connection.targetBlock()" [level]="level + 1"
|
||||||
[displayBlockMenu]="false"
|
[displayBlockMenu]="false">
|
||||||
[level]="level + 1">
|
|
||||||
</blockly-toolbox-tree>
|
</blockly-toolbox-tree>
|
||||||
<li #listItem1 [id]="idMap['listItem' + i]" role="treeitem"
|
<li [id]="idMap['inputList' + i]" role="treeitem"
|
||||||
*ngIf="inputBlock.connection && !inputBlock.connection.targetBlock()"
|
*ngIf="inputBlock.connection && !inputBlock.connection.targetBlock()"
|
||||||
[attr.aria-labelledBy]="generateAriaLabelledByAttr('blockly-argument-text', idMap['listItem' + i + 'Label'])"
|
|
||||||
[attr.aria-level]="level + 1">
|
[attr.aria-level]="level + 1">
|
||||||
<!--TODO(madeeha): i18n here will need to happen in a different way due to the way grammar changes based on language.-->
|
<label>
|
||||||
<label #label [id]="idMap['listItem' + i + 'Label']">
|
{{utilsService.getInputTypeLabel(inputBlock.connection)}} {{utilsService.getBlockTypeLabel(inputBlock)}} needed:
|
||||||
{{utilsService.getInputTypeLabel(inputBlock.connection)}}
|
|
||||||
{{utilsService.getBlockTypeLabel(inputBlock)}} needed:
|
|
||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
</div>
|
</template>
|
||||||
</ol>
|
</ol>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<blockly-toolbox-tree *ngIf= "block.nextConnection && block.nextConnection.targetBlock()"
|
<blockly-toolbox-tree *ngIf= "block.nextConnection && block.nextConnection.targetBlock()"
|
||||||
[level]="level"
|
[level]="level"
|
||||||
[block]="block.nextConnection.targetBlock()"
|
[block]="block.nextConnection.targetBlock()"
|
||||||
@@ -123,7 +117,7 @@ blocklyApp.ToolboxTreeComponent = ng.core
|
|||||||
'blockCopyButton', 'sendToSelected', 'sendToSelectedButton']);
|
'blockCopyButton', 'sendToSelected', 'sendToSelectedButton']);
|
||||||
}
|
}
|
||||||
for (var i = 0; i < this.block.inputList.length; i++){
|
for (var i = 0; i < this.block.inputList.length; i++){
|
||||||
elementsNeedingIds.push('listItem' + i, 'listItem' + i + 'Label')
|
elementsNeedingIds.push('listItem' + i, 'inputList' + i);
|
||||||
}
|
}
|
||||||
this.idMap = this.utilsService.generateIds(elementsNeedingIds);
|
this.idMap = this.utilsService.generateIds(elementsNeedingIds);
|
||||||
if (this.isTopLevel) {
|
if (this.isTopLevel) {
|
||||||
|
|||||||
@@ -246,12 +246,13 @@ blocklyApp.TreeService = ng.core
|
|||||||
if (e.keyCode == 13) {
|
if (e.keyCode == 13) {
|
||||||
// Enter key. The user wants to interact with a button or an input
|
// Enter key. The user wants to interact with a button or an input
|
||||||
// field.
|
// field.
|
||||||
if (activeDesc.children.length == 1) {
|
var currentChild = activeDesc;
|
||||||
var child = activeDesc.children[0];
|
while (currentChild.children.length) {
|
||||||
if (child.tagName == 'BUTTON') {
|
currentChild = currentChild.children[0];
|
||||||
child.click();
|
if (currentChild.tagName == 'BUTTON') {
|
||||||
} else if (child.tagName == 'INPUT') {
|
currentChild.click();
|
||||||
child.focus();
|
} else if (currentChild.tagName == 'INPUT') {
|
||||||
|
currentChild.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (e.keyCode == 9) {
|
} else if (e.keyCode == 9) {
|
||||||
|
|||||||
@@ -48,13 +48,10 @@ blocklyApp.UtilsService = ng.core
|
|||||||
return attrValue;
|
return attrValue;
|
||||||
},
|
},
|
||||||
getInputTypeLabel: function(connection) {
|
getInputTypeLabel: function(connection) {
|
||||||
// Returns an upper case string in the case of official input type names.
|
// Returns the input type name, or 'any' if any official input type
|
||||||
// Returns the lower case string 'any' if any official input type qualifies.
|
// qualifies.
|
||||||
// The differentiation between upper and lower case signifies the difference
|
|
||||||
// between an input type (BOOLEAN, LIST, etc) and the colloquial english term
|
|
||||||
// 'any'.
|
|
||||||
if (connection.check_) {
|
if (connection.check_) {
|
||||||
return connection.check_.join(', ').toUpperCase();
|
return connection.check_.join(', ');
|
||||||
} else {
|
} else {
|
||||||
return Blockly.Msg.ANY;
|
return Blockly.Msg.ANY;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,17 +51,24 @@ blocklyApp.WorkspaceTreeComponent = ng.core
|
|||||||
</ol>
|
</ol>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<div *ngFor="#inputBlock of block.inputList; #i = index">
|
<template ngFor #inputBlock [ngForOf]="block.inputList" #i="index">
|
||||||
<blockly-field *ngFor="#field of inputBlock.fieldRow" [field]="field" [level]="level + 1"></blockly-field>
|
<li role="treeitem" [id]="idMap['listItem' + i]" [attr.aria-level]="level + 1" *ngIf="inputBlock.fieldRow.length">
|
||||||
|
<blockly-field *ngFor="#field of inputBlock.fieldRow" [field]="field">
|
||||||
|
</blockly-field>
|
||||||
|
</li>
|
||||||
|
|
||||||
<blockly-workspace-tree *ngIf="inputBlock.connection && inputBlock.connection.targetBlock()"
|
<blockly-workspace-tree *ngIf="inputBlock.connection && inputBlock.connection.targetBlock()"
|
||||||
[block]="inputBlock.connection.targetBlock()" [level]="level + 1"
|
[block]="inputBlock.connection.targetBlock()" [level]="level + 1"
|
||||||
[tree]="tree">
|
[tree]="tree">
|
||||||
</blockly-workspace-tree>
|
</blockly-workspace-tree>
|
||||||
<li #inputList [attr.aria-level]="level + 1" [id]="idMap['inputList' + i]"
|
<li #inputList [id]="idMap['inputList' + i]" role="treeitem"
|
||||||
|
*ngIf="inputBlock.connection && !inputBlock.connection.targetBlock()"
|
||||||
[attr.aria-labelledBy]="generateAriaLabelledByAttr('blockly-menu', idMap['inputMenuLabel' + i])"
|
[attr.aria-labelledBy]="generateAriaLabelledByAttr('blockly-menu', idMap['inputMenuLabel' + i])"
|
||||||
*ngIf="inputBlock.connection && !inputBlock.connection.targetBlock()" (keydown)="treeService.onKeypress($event, tree)">
|
[attr.aria-level]="level + 1"
|
||||||
<!-- TODO(madeeha): i18n here will need to happen in a different way due to the way grammar changes based on language. -->
|
(keydown)="treeService.onKeypress($event, tree)">
|
||||||
<label [id]="idMap['inputMenuLabel' + i]"> {{utilsService.getInputTypeLabel(inputBlock.connection)}} {{utilsService.getBlockTypeLabel(inputBlock)}} needed: </label>
|
<label [id]="idMap['inputMenuLabel' + i]">
|
||||||
|
{{utilsService.getInputTypeLabel(inputBlock.connection)}} {{utilsService.getBlockTypeLabel(inputBlock)}} needed:
|
||||||
|
</label>
|
||||||
<ol role="group">
|
<ol role="group">
|
||||||
<li *ngFor="#fieldButtonInfo of fieldButtonsInfo"
|
<li *ngFor="#fieldButtonInfo of fieldButtonsInfo"
|
||||||
[id]="idMap[fieldButtonInfo.baseIdKey]" role="treeitem"
|
[id]="idMap[fieldButtonInfo.baseIdKey]" role="treeitem"
|
||||||
@@ -75,7 +82,7 @@ blocklyApp.WorkspaceTreeComponent = ng.core
|
|||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</li>
|
</li>
|
||||||
</div>
|
</template>
|
||||||
</ol>
|
</ol>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
@@ -318,11 +325,10 @@ blocklyApp.WorkspaceTreeComponent = ng.core
|
|||||||
});
|
});
|
||||||
for (var i = 0; i < this.block.inputList.length; i++) {
|
for (var i = 0; i < this.block.inputList.length; i++) {
|
||||||
var inputBlock = this.block.inputList[i];
|
var inputBlock = this.block.inputList[i];
|
||||||
if (inputBlock.connection && !inputBlock.connection.targetBlock()) {
|
that.idKeys.push(
|
||||||
that.idKeys.push(
|
'inputList' + i, 'inputMenuLabel' + i, 'markSpot' + i,
|
||||||
'inputList' + i, 'inputMenuLabel' + i, 'markSpot' + i,
|
'markSpotButton' + i, 'paste' + i, 'pasteButton' + i,
|
||||||
'markSpotButton' + i, 'paste' + i, 'pasteButton' + i);
|
'listItem' + i);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ngDoCheck: function() {
|
ngDoCheck: function() {
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
.blocklyTree .blocklyActiveDescendant > label,
|
.blocklyTree .blocklyActiveDescendant > label,
|
||||||
.blocklyTree .blocklyActiveDescendant > div > label,
|
.blocklyTree .blocklyActiveDescendant > div > label,
|
||||||
.blocklyActiveDescendant > button,
|
.blocklyActiveDescendant > button,
|
||||||
.blocklyActiveDescendant > input {
|
.blocklyActiveDescendant > input,
|
||||||
|
.blocklyActiveDescendant > blockly-field > label,
|
||||||
|
.blocklyActiveDescendant > blockly-field > input,
|
||||||
|
.blocklyActiveDescendant > blockly-field > div > label {
|
||||||
outline: 2px dotted #00f;
|
outline: 2px dotted #00f;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user