From b8ac8c91389e0e98c07259298507c2a55d56fef8 Mon Sep 17 00:00:00 2001 From: lizschwab Date: Mon, 15 Jun 2026 17:33:49 -0700 Subject: [PATCH 1/3] fix: Coalesce adjacent field labels --- packages/blockly/core/block_aria_composer.ts | 78 +++++++++++++++++- packages/blockly/tests/mocha/input_test.js | 85 +++++++++++++++++--- 2 files changed, 150 insertions(+), 13 deletions(-) diff --git a/packages/blockly/core/block_aria_composer.ts b/packages/blockly/core/block_aria_composer.ts index 150335af55a..b83a9b4fb41 100644 --- a/packages/blockly/core/block_aria_composer.ts +++ b/packages/blockly/core/block_aria_composer.ts @@ -318,8 +318,84 @@ export function getInputLabels( ); } } + let adjacentFieldLabels: Array = []; + return inputsToLabel.flatMap((input, index, inputs) => { + const inputLabel = input.getLabel(verbosity); + if ( + index < inputs.length - 1 && + endsWithFieldLabel(input) && + beginsWithFieldLabel(inputs[index + 1]) && + // getVisualRowId(input) === getVisualRowId(inputs[index + 1]) + !isEndOfRow(input, inputs[index + 1]) + ) { + // This input ends with a field label and the next one begins with one. + // They are also on the same visual row. We want to combine these, so we + // add this one to the list for later handling. Value inputs are + // excluded from this as the value should be separated. + adjacentFieldLabels.push(inputLabel); + return []; + } else if (beginsWithFieldLabel(input) && adjacentFieldLabels.length > 0) { + // There is at least one adjacent FieldLabel before this one but none + // after. Combine the FieldLabels into one string. + adjacentFieldLabels.push(inputLabel); + const label = adjacentFieldLabels.join(' '); + adjacentFieldLabels = []; + return label; + } + return inputLabel; + }); +} + +/** + * Returns whether an input's list of visible fields begins with a FieldLabel + * + * @internal + * @param input the input to be evaluated + * @returns a boolean indicating whether the input's first visible field is of + * type FieldLabel + */ +function beginsWithFieldLabel(input: Input): boolean { + const visibleFields = input.fieldRow.filter((field) => field.isVisible()); + return visibleFields.length > 0 && visibleFields[0] instanceof FieldLabel; +} - return inputsToLabel.map((input) => input.getLabel(verbosity)); +/** + * Returns whether an input's list of visible fields ends with a FieldLabel + * + * @internal + * @param input the input to be evaluated + * @returns a boolean indicating whether the input's last visible field is of + * type FieldLabel + */ +function endsWithFieldLabel(input: Input): boolean { + // Values and statements never have a label at the end of the input. + if (input.type === inputTypes.VALUE || input.type === inputTypes.STATEMENT) { + return false; + } + const visibleFields = input.fieldRow.filter((field) => field.isVisible()); + return ( + visibleFields.length > 0 && + visibleFields[visibleFields.length - 1] instanceof FieldLabel + ); +} + +/** + * Returns whether the current input is the end of a visual "row" + * + * @param current the input of which to evaluate the end-of-row status + * @param next the next input on the block after "current" + * @returns a boolean representation of whether "current" is the end of a row + */ +function isEndOfRow(current: Input, next: Input): boolean { + const precedingStatementInput = + current.connection?.type === ConnectionType.NEXT_STATEMENT || + current.type === inputTypes.END_ROW; + + return ( + !current.getSourceBlock().getInputsInline() || + next.connection?.type === ConnectionType.NEXT_STATEMENT || + precedingStatementInput + ); } /** diff --git a/packages/blockly/tests/mocha/input_test.js b/packages/blockly/tests/mocha/input_test.js index f08f22c3d5d..f2ee09069db 100644 --- a/packages/blockly/tests/mocha/input_test.js +++ b/packages/blockly/tests/mocha/input_test.js @@ -379,19 +379,80 @@ suite('Inputs', function () { assert.include(label, 'first, 0'); }); - test('Adjacent labels for FieldLabels are combined into one field.', function () { - this.block - .appendDummyInput() - .appendField('first') - .appendField('second') - .appendField(new Blockly.FieldNumber(0)) - .appendField('third') - .appendField('fourth') - .appendField('fifth'); + suite( + 'Adjacent labels for FieldLabels are combined into one field', + function () { + test('Value inputs are not combined', function () { + this.block.appendDummyInput().appendField('first'); + this.block.appendValueInput('number').appendField('second'); + this.block + .appendDummyInput() + .appendField('third') + .appendField('fourth'); + + const label = this.block.getAriaLabel(); + + assert.include(label, 'first second,'); + assert.include(label, 'third fourth'); + }); + test('Statement inputs are not combined', function () { + const statementBlock = Blockly.Xml.domToBlock( + Blockly.utils.xml.textToDom(''), + this.workspace, + ); + statementBlock.appendDummyInput().appendField('first'); + statementBlock + .appendStatementInput('statement') + .appendField('second'); + statementBlock + .appendDummyInput() + .appendField('third') + .appendField('fourth'); + statementBlock.setPreviousStatement(true); + statementBlock.setNextStatement(true); + + const label = statementBlock.getAriaLabel(); + + assert.include(label, 'first, second, third fourth'); + }); + test('Dummy inputs on the same row are combined', function () { + this.block.appendDummyInput().appendField('first'); + this.block.appendDummyInput().appendField('second'); + this.block + .appendDummyInput() + .appendField('third') + .appendField('fourth'); + this.block.setInputsInline(true); + + const label = this.block.getAriaLabel(); + + assert.include(label, 'first second third fourth'); + }); + test('Dummy inputs on different rows are not combined', function () { + this.block.appendDummyInput().appendField('first'); + this.block.appendDummyInput().appendField('second'); + this.block + .appendDummyInput() + .appendField('third') + .appendField('fourth'); - const label = this.block.getAriaLabel(); + const label = this.block.getAriaLabel(); - assert.include(label, 'first second, 0, third fourth fifth'); - }); + assert.include(label, 'first, second, third fourth'); + }); + test('End row inputs are not combined', function () { + this.block.appendDummyInput().appendField('first'); + this.block.appendEndRowInput('endRowInput').appendField('second'); + this.block + .appendDummyInput() + .appendField('third') + .appendField('fourth'); + + const label = this.block.getAriaLabel(); + + assert.include(label, 'first second, third fourth'); + }); + }, + ); }); }); From 237807323faef4db1aac3c8a54c0d632877edfd5 Mon Sep 17 00:00:00 2001 From: lizschwab Date: Tue, 16 Jun 2026 08:53:48 -0700 Subject: [PATCH 2/3] fix: removed old commented-out line --- packages/blockly/core/block_aria_composer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/blockly/core/block_aria_composer.ts b/packages/blockly/core/block_aria_composer.ts index b83a9b4fb41..a9536850eb4 100644 --- a/packages/blockly/core/block_aria_composer.ts +++ b/packages/blockly/core/block_aria_composer.ts @@ -325,7 +325,6 @@ export function getInputLabels( index < inputs.length - 1 && endsWithFieldLabel(input) && beginsWithFieldLabel(inputs[index + 1]) && - // getVisualRowId(input) === getVisualRowId(inputs[index + 1]) !isEndOfRow(input, inputs[index + 1]) ) { // This input ends with a field label and the next one begins with one. From aacb208dcf665cdb168aeafbe691bacb2725ed83 Mon Sep 17 00:00:00 2001 From: lizschwab Date: Wed, 17 Jun 2026 14:22:41 -0700 Subject: [PATCH 3/3] fix: removed unnecessary @internal decorators and changed test from xml to functional block creation --- packages/blockly/core/block_aria_composer.ts | 2 -- packages/blockly/tests/mocha/input_test.js | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/blockly/core/block_aria_composer.ts b/packages/blockly/core/block_aria_composer.ts index a9536850eb4..e39de955127 100644 --- a/packages/blockly/core/block_aria_composer.ts +++ b/packages/blockly/core/block_aria_composer.ts @@ -348,7 +348,6 @@ export function getInputLabels( /** * Returns whether an input's list of visible fields begins with a FieldLabel * - * @internal * @param input the input to be evaluated * @returns a boolean indicating whether the input's first visible field is of * type FieldLabel @@ -361,7 +360,6 @@ function beginsWithFieldLabel(input: Input): boolean { /** * Returns whether an input's list of visible fields ends with a FieldLabel * - * @internal * @param input the input to be evaluated * @returns a boolean indicating whether the input's last visible field is of * type FieldLabel diff --git a/packages/blockly/tests/mocha/input_test.js b/packages/blockly/tests/mocha/input_test.js index f2ee09069db..b9f50484981 100644 --- a/packages/blockly/tests/mocha/input_test.js +++ b/packages/blockly/tests/mocha/input_test.js @@ -396,9 +396,9 @@ suite('Inputs', function () { assert.include(label, 'third fourth'); }); test('Statement inputs are not combined', function () { - const statementBlock = Blockly.Xml.domToBlock( - Blockly.utils.xml.textToDom(''), + const statementBlock = createRenderedBlock( this.workspace, + 'empty_block', ); statementBlock.appendDummyInput().appendField('first'); statementBlock