Skip to content

Commit 23fcf16

Browse files
authored
fix: Redo move templates (#9822)
* Fix: Add punctuation to live region announcements * fix: remove unused message * fix: update move announcement templates * fix: linting
1 parent 7700b00 commit 23fcf16

5 files changed

Lines changed: 161 additions & 141 deletions

File tree

packages/blockly/core/block_aria_composer.ts

Lines changed: 75 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export enum ConnectionPreposition {
2626
AFTER,
2727
AROUND,
2828
INSIDE,
29+
TO,
2930
}
3031

3132
/**
@@ -342,6 +343,55 @@ function getParentToolboxCategoryLabel(block: BlockSvg) {
342343
return undefined;
343344
}
344345

346+
/**
347+
* Returns the appropriate translated announcement template based on the connection type.
348+
*
349+
* @param preposition The relationship between the local and neighbour connections.
350+
* @returns A translated string template to use for announcing a block move.
351+
*/
352+
function getAnnouncementTemplate(preposition: ConnectionPreposition): string {
353+
switch (preposition) {
354+
case ConnectionPreposition.BEFORE:
355+
return Msg['ANNOUNCE_MOVE_BEFORE'];
356+
case ConnectionPreposition.AFTER:
357+
return Msg['ANNOUNCE_MOVE_AFTER'];
358+
case ConnectionPreposition.INSIDE:
359+
return Msg['ANNOUNCE_MOVE_INSIDE'];
360+
case ConnectionPreposition.AROUND:
361+
return Msg['ANNOUNCE_MOVE_AROUND'];
362+
default:
363+
return Msg['ANNOUNCE_MOVE_TO'];
364+
}
365+
}
366+
367+
/**
368+
* Returns a label for a connection includes either a block label, input label or both.
369+
*
370+
* @param conn The connection to generate a label for.
371+
* @param baseLabel An optional block label to include in the returned string.
372+
* @returns A label describing the given connection
373+
*/
374+
function computeMoveConnectionLabel(
375+
conn: RenderedConnection,
376+
baseLabel: string,
377+
): string {
378+
const input = conn.getParentInput();
379+
if (!input) return baseLabel;
380+
381+
const labels = getInputLabelsSubset(
382+
conn.getSourceBlock(),
383+
input,
384+
Verbosity.TERSE,
385+
);
386+
if (!labels.length) return baseLabel;
387+
388+
const inputLabel = labels.join(', ');
389+
390+
return baseLabel
391+
? Msg['ANNOUNCE_MOVE_OF'].replace('%1', inputLabel).replace('%2', baseLabel)
392+
: inputLabel;
393+
}
394+
345395
/**
346396
* Returns a translated string describing an in-progress move of a block to a new
347397
* connection, suitable for announcement on the ARIA live region. The returned string
@@ -364,67 +414,32 @@ export function computeMoveLabel(
364414
isMoveStart = false,
365415
): string {
366416
const preposition = getConnectionPreposition(local, neighbour);
367-
const neighbourBlock = neighbour.getSourceBlock() as BlockSvg;
368-
const neighbourBlockLabel = neighbourBlock.getAriaLabel(Verbosity.TERSE);
369-
const blockLabel = isMoveStart
417+
const template = getAnnouncementTemplate(preposition);
418+
419+
const needsDisambiguation = ![
420+
ConnectionPreposition.BEFORE,
421+
ConnectionPreposition.AFTER,
422+
].includes(preposition);
423+
424+
const includeLocalContext = needsDisambiguation && disambiguationPolicy(true);
425+
const includeNeighbourContext =
426+
needsDisambiguation && disambiguationPolicy(false);
427+
428+
let blockLabel = isMoveStart
370429
? local.getSourceBlock().getStackBlocksCountLabel()
371430
: '';
431+
let neighbourLabel = (neighbour.getSourceBlock() as BlockSvg).getAriaLabel(
432+
Verbosity.TERSE,
433+
);
372434

373-
let announcementTemplate;
374-
// Message strings take a format like 'moving %1 %2 to %3 %4' where:
375-
// "to" is replaced with a preposition based on the type of the connection candidate
376-
// (e.g. "before", "after", "inside", "around", etc), and the placeholders are replaced with:
377-
// %1 = optional label for the block being moved
378-
// %2 = optional label for the local connection
379-
// %3 = label for the neighbour block
380-
// %4 = optional label for the neighbour connection
381-
switch (preposition) {
382-
case ConnectionPreposition.BEFORE:
383-
announcementTemplate = Msg['ANNOUNCE_MOVE_BEFORE'];
384-
break;
385-
case ConnectionPreposition.AFTER:
386-
announcementTemplate = Msg['ANNOUNCE_MOVE_AFTER'];
387-
break;
388-
case ConnectionPreposition.INSIDE:
389-
announcementTemplate = Msg['ANNOUNCE_MOVE_INSIDE'];
390-
break;
391-
case ConnectionPreposition.AROUND:
392-
announcementTemplate = Msg['ANNOUNCE_MOVE_AROUND'];
393-
break;
394-
case ConnectionPreposition.UNKNOWN:
395-
announcementTemplate = Msg['ANNOUNCE_MOVE_UNKNOWN'];
435+
if (includeLocalContext) {
436+
blockLabel = computeMoveConnectionLabel(local, blockLabel);
437+
}
438+
if (includeNeighbourContext) {
439+
neighbourLabel = computeMoveConnectionLabel(neighbour, neighbourLabel);
396440
}
397441

398-
// If multiple compatible candidate connections exist for either/both pairs of the
399-
// current connection candidate, increase the verbosity of the announcement to help
400-
// disambiguate them.
401-
const requiresDisambiguation = [
402-
ConnectionPreposition.INSIDE,
403-
ConnectionPreposition.AROUND,
404-
].includes(preposition);
405-
const describeLocal = requiresDisambiguation && disambiguationPolicy(true);
406-
const describeNeighbour =
407-
requiresDisambiguation && disambiguationPolicy(false);
408-
409-
const localInput = local.getParentInput();
410-
const neighbourInput = neighbour.getParentInput();
411-
412-
const localConnLabel =
413-
(describeLocal &&
414-
localInput &&
415-
getInputLabelsSubset(local.getSourceBlock(), localInput).join(', ')) ||
416-
'';
417-
const neighbourConnLabel =
418-
(describeNeighbour &&
419-
neighbourInput &&
420-
getInputLabelsSubset(neighbourBlock, neighbourInput).join(', ')) ||
421-
'';
422-
423-
return announcementTemplate
424-
.replace('%1', blockLabel)
425-
.replace('%2', localConnLabel)
426-
.replace('%3', neighbourBlockLabel)
427-
.replace('%4', neighbourConnLabel);
442+
return template.replace('%1', blockLabel).replace('%2', neighbourLabel);
428443
}
429444

430445
/**
@@ -436,10 +451,9 @@ function getConnectionPreposition(
436451
neighbour: RenderedConnection,
437452
): ConnectionPreposition {
438453
switch (local.type) {
439-
case ConnectionType.OUTPUT_VALUE:
440-
return ConnectionPreposition.INSIDE;
441454
case ConnectionType.INPUT_VALUE:
442-
return ConnectionPreposition.AROUND;
455+
case ConnectionType.OUTPUT_VALUE:
456+
return ConnectionPreposition.TO;
443457
case ConnectionType.NEXT_STATEMENT:
444458
if (local === local.getSourceBlock().nextConnection) {
445459
return ConnectionPreposition.BEFORE;
@@ -453,8 +467,8 @@ function getConnectionPreposition(
453467
return ConnectionPreposition.INSIDE;
454468
}
455469
}
456-
// Not normally reachable since we should always have a connection candidate
457-
// with valid connection types. Satisfies the return type.
470+
// Not normally reachable since all valid connection types are covered.
471+
// Satisfies the return type.
458472
return ConnectionPreposition.UNKNOWN;
459473
}
460474

packages/blockly/msg/json/en.json

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"@metadata": {
33
"author": "Ellen Spertus <ellen.spertus@gmail.com>",
4-
"lastupdated": "2026-05-01 14:09:40.345417",
4+
"lastupdated": "2026-05-06 14:12:59.060731",
55
"locale": "en",
66
"messagedocumentation" : "qqq"
77
},
@@ -426,7 +426,7 @@
426426
"CUT_SHORTCUT": "Cut",
427427
"COPY_SHORTCUT": "Copy",
428428
"PASTE_SHORTCUT": "Paste",
429-
"HELP_PROMPT": "Press %1 for help on keyboard controls",
429+
"HELP_PROMPT": "Press %1 for help on keyboard controls.",
430430
"SHORTCUTS_GENERAL": "General",
431431
"SHORTCUTS_EDITING": "Editing",
432432
"SHORTCUTS_CODE_NAVIGATION": "Code navigation",
@@ -451,8 +451,8 @@
451451
"SHORTCUTS_DUPLICATE": "Duplicate",
452452
"SHORTCUTS_CLEANUP": "Clean up workspace",
453453
"SHORTCUTS_SHOW_TOOLTIP": "Show tooltip",
454-
"KEYBOARD_NAV_UNCONSTRAINED_MOVE_HINT": "Hold %1 and use arrow keys to move freely, then %2 to accept the position",
455-
"KEYBOARD_NAV_CONSTRAINED_MOVE_HINT": "Use the arrow keys to move, then %1 to accept the position",
454+
"KEYBOARD_NAV_UNCONSTRAINED_MOVE_HINT": "Hold %1 and use arrow keys to move freely, then %2 to accept the position.",
455+
"KEYBOARD_NAV_CONSTRAINED_MOVE_HINT": "Use the arrow keys to move, then %1 to accept the position.",
456456
"KEYBOARD_NAV_COPIED_HINT": "Copied. Press %1 to paste.",
457457
"KEYBOARD_NAV_CUT_HINT": "Cut. Press %1 to paste.",
458458
"WORKSPACE_LABEL_1_STACK": "Blocks workspace. 1 stack of blocks",
@@ -464,8 +464,8 @@
464464
"WORKSPACE_CONTENTS_BLOCKS_ZERO": "No blocks%2 in workspace.",
465465
"WORKSPACE_CONTENTS_COMMENTS_MANY": " and %1 comments",
466466
"WORKSPACE_CONTENTS_COMMENTS_ONE": " and one comment",
467-
"KEYBOARD_NAV_BLOCK_NAVIGATION_HINT": "Use the right arrow key to navigate inside of blocks",
468-
"KEYBOARD_NAV_WORKSPACE_NAVIGATION_HINT": "Use the arrow keys to navigate",
467+
"KEYBOARD_NAV_BLOCK_NAVIGATION_HINT": "Use the right arrow key to navigate inside of blocks.",
468+
"KEYBOARD_NAV_WORKSPACE_NAVIGATION_HINT": "Use the arrow keys to navigate.",
469469
"BLOCK_LABEL_BEGIN_STACK": "Begin stack",
470470
"BLOCK_LABEL_BEGIN_PREFIX": "Begin %1",
471471
"BLOCK_LABEL_TOOLBOX_CATEGORY": "%1 category",
@@ -479,13 +479,14 @@
479479
"BLOCK_LABEL_VALUE": "value",
480480
"BLOCK_LABEL_STACK_BLOCKS": "%1 stack blocks",
481481
"INPUT_LABEL_INDEX": "input %1",
482-
"ANNOUNCE_MOVE_WORKSPACE": "moving %1 on workspace",
483-
"ANNOUNCE_MOVE_BEFORE": "moving %1 before %3",
484-
"ANNOUNCE_MOVE_AFTER": "moving %1 after %3",
485-
"ANNOUNCE_MOVE_INSIDE": "moving %1 inside %3 %4",
486-
"ANNOUNCE_MOVE_AROUND": "moving %1 %2 around %3",
487-
"ANNOUNCE_MOVE_TO": "moving %1 %2 to %3 %4",
488-
"ANNOUNCE_MOVE_CANCELED": "Canceled movement",
482+
"ANNOUNCE_MOVE_WORKSPACE": "Moving %1 on workspace.",
483+
"ANNOUNCE_MOVE_BEFORE": "Moving %1 before %2.",
484+
"ANNOUNCE_MOVE_AFTER": "Moving %1 after %2.",
485+
"ANNOUNCE_MOVE_INSIDE": "Moving %1 inside %2.",
486+
"ANNOUNCE_MOVE_AROUND": "Moving %1 around %2.",
487+
"ANNOUNCE_MOVE_TO": "Moving %1 to %2.",
488+
"ANNOUNCE_MOVE_OF": "%1 of %2",
489+
"ANNOUNCE_MOVE_CANCELED": "Canceled movement.",
489490
"FIELD_LABEL_EMPTY": "empty",
490491
"ARIA_TYPE_FIELD_INPUT": "input",
491492
"ARIA_TYPE_FIELD_TEXT_INPUT": "text",

packages/blockly/msg/json/qqq.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -488,11 +488,12 @@
488488
"BLOCK_LABEL_STACK_BLOCKS": "Accessibility label for a block that indicates it is a stack of two or more blocks.",
489489
"INPUT_LABEL_INDEX": "Accessibility label for an unlabeled input that communicates its index on the block. \n\nParameters:\n* %1 - the index of the input, starting at 1",
490490
"ANNOUNCE_MOVE_WORKSPACE": "ARIA live region message announcing a block is being moved on the workspace, without specifying a target location or specific movement direction.",
491-
"ANNOUNCE_MOVE_BEFORE": "ARIA live region message announcing a block is being moved before another block \n\nParameters:\n* %1 - optional phrase describing the moving stack of blocks\n* %3 - the label of the target (neighbour) block \n\nExamples:\n* 'moving before repeat 10, times, do'\n* 'moving 2 stack blocks before repeat 10, times, do'",
492-
"ANNOUNCE_MOVE_AFTER": "ARIA live region message announcing a block is being moved after another block \n\nParameters:\n* %1 - optional phrase describing the moving stack of blocks\n* %3 - the label of the target (neighbour) block \n\nExamples:\n* 'moving after repeat 10, times, do'\n* 'moving 2 stack blocks after repeat 10, times, do' \n* 'moving block A after Function block output'",
493-
"ANNOUNCE_MOVE_INSIDE": "ARIA live region message announcing a block is being moved inside another block, optionally including connection-specific label for disambiguation.",
494-
"ANNOUNCE_MOVE_AROUND": "ARIA live region message announcing a block is being moved around another block, optionally including connection-specific label for disambiguation. \n\nParameters:\n* %1 - optional phrase describing the moving stack of blocks \n* %2 - optional phrase describing the local connection label \n* %3 - the label of the target (neighbour) block \n\nExamples:\n* 'moving around print abc'\n* 'moving if, do else statement around print abc'",
495-
"ANNOUNCE_MOVE_TO": "ARIA live region message announcing a block is being moved to a workspace location where the relationship is not specifically known. \n\nParameters:\n* %1 - optional phrase describing the moving stack of blocks \n* %2 - optional phrase describing the local connection label \n* %3 - the label of the target (neighbour) block or location \n* %4 - optional phrase describing the target connection label \n\nExamples:\n* 'moving to repeat 10, times, do'\n* 'moving 2 stack blocks else statement to repeat 10, times, do previous connection'",
491+
"ANNOUNCE_MOVE_BEFORE": "ARIA live region message announcing a block is being moved before another block \n\nParameters:\n* %1 - optional phrase describing the moving stack of blocks\n* %2 - the label of the target (neighbour) block \n\nExamples:\n* 'Moving before repeat, 10, times, do.'\n* 'Moving print before repeat, 10, times, do.'",
492+
"ANNOUNCE_MOVE_AFTER": "ARIA live region message announcing a block is being moved after another block \n\nParameters:\n* %1 - optional phrase describing the moving stack of blocks\n* %2 - the label of the target (neighbour) block \n\nExamples:\n* 'Moving after repeat, 10, times, do.'\n* 'Moving 2 stack blocks after repeat, 10, times, do.'",
493+
"ANNOUNCE_MOVE_INSIDE": "ARIA live region message announcing a block is being moved inside another block's statement connection, optionally including connection-specific label for disambiguation.",
494+
"ANNOUNCE_MOVE_AROUND": "ARIA live region message announcing a block is being moved around another block (using its own statement connection), optionally including connection-specific label for disambiguation. \n\nParameters:\n* %1 - optional phrase describing the moving stack of blocks or a local connection label \n* %2 - the label of the target (neighbour) block \n\nExamples:\n* 'Moving around print.'\n* 'Moving else around print.'",
495+
"ANNOUNCE_MOVE_TO": "ARIA live region message announcing a block is being moved to another block's value input connection, optionally including connection-specific label for disambiguation. \n\nParameters:\n* %1 - optional phrase describing the moving stack of blocks or a local connection label \n* %2 - the label of the target (neighbour) block or location \n\nExamples:\n* 'Moving to repeat, 10, times, do.'\n* 'Moving 2 stack blocks else statement to previous connection in repeat, 10, times, do.'",
496+
"ANNOUNCE_MOVE_OF": "A label describing a specific connection of a block. Part of an ARIA live region message announcing a block is being moved. \n\nParameters:\n* %1 - connection label\n* %2 - block label of the block the connection belongs to \n\nExamples:\n* 'else statement of if, do''",
496497
"ANNOUNCE_MOVE_CANCELED": "ARIA live region message announcing a block movement has been canceled.",
497498
"FIELD_LABEL_EMPTY": "Label for an empty field, used by screen readers to identify fields that have no content.",
498499
"ARIA_TYPE_FIELD_INPUT": "ARIA type name for an input field, used by screen readers to identify the type of field.",

0 commit comments

Comments
 (0)