## The basics
- [x] I [validated my changes](https://developers.google.com/blockly/guides/contribute/core#making_and_verifying_a_change)
## The details
### Resolves
Fixes https://github.com/google/blockly-keyboard-experimentation/issues/578
Fixes part of #8915
### Proposed Changes
Ensures fields update focus to the next field when tabbing between field editors. The old behavior can be observed in [#578](https://github.com/google/blockly-keyboard-experimentation/issues/578) and the new behavior can be observed here:
[Screen recording 2025-06-25 1.39.28 PM.webm](https://github.com/user-attachments/assets/e00fcb55-5c20-4d5c-81a8-be9049cc0d7e)
### Reason for Changes
Having focus reset back to the original field editor that was opened is an unexpected experience for users. This approach is better.
Note that there are some separate changes added here, as well:
- Connections and fields now check if their block IDs contain their indicator prefixes since this will all-but-guarantee focus breaks for those nodes. This is an excellent example of why #9171 is needed.
- Some minor naming updates for `FieldInput`: it was incorrectly implying that key events are sent for changes to the `input` element used by the field editor, but they are actually `InputEvent`s per https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event.
### Test Coverage
New tests were added for field editing in general (since this seems to be missing), including tabbing support to ensure the fixes originally introduced in #9049.
One new test has been added specifically for verifying that focus updates with tabbing. This has been verified to fail with the fix removed (as have all tabbing tests with the tabbing code from #9049 removed).
Some specific notes for the test changes:
- There's a slight test dependency inversion happening here. `FieldInput` is being tested with a specific `FieldNumber` class via real block loading. This isn't ideal, but it seems fine given the circumstances (otherwise a lot of extra setup would be necessary for the tests).
- The workspace actually needs to be made visible during tests in order for focus to work correctly (though it's reset at the end of each test, but this may cause some flickering while the tests are running).
- It's the case that a bunch of tests were actually setting up blocks incorrectly (i.e. not defining a must-have `id` property which caused some issues with the new field and connection ID validation checks). These tests have been corrected, but it's worth noting that the blocks are likely still technically wrong since they are not conforming to their TypeScript contracts.
- Similar to the previous point, one test was incorrectly setting the first ID to be returned by the ID generator as `undefined` since (presumably due to a copy-and-paste error when the test was introduced) it was referencing a `TEST_BLOCK_ID` property that hadn't been defined for that test suite. This has been corrected as, without it, there are failures due to the new validation checks.
- For the connection database checks, a new ID is generated instead of fixing the block ID to ensure that it's always unique even if called multiple times (otherwise a block ID would need to be piped through from the calling tests, or an invalid situation would need to be introduced where multiple blocks shared an ID; the former seemed unnecessary and the latter seemed nonideal).
- There are distinct Geras/Zelos tests to validate the case where a full-block field should have its parent block, rather than the field itself, focused on tabbing. See this conversation for more context: https://github.com/google/blockly/pull/9173#discussion_r2172921455.
### Documentation
No documentation changes should be needed here.
### Additional Information
Nothing to add.
* feat: Enhance the Rect API.
* feat: Add support for sorting IBoundedElements in general.
* fix: Improve typings of getTopElement/Comment methods.
* feat: Add classes to represent comment icons.
* refactor: Use comment icons in comment view.
* feat: Update navigation policies to support workspace comments.
* feat: Make the navigator and workspace handle workspace comments.
* feat: Visit workspace comments when navigating with the up/down arrows.
* chore: Make the linter happy.
* chore: Rename comment icons to bar buttons.
* refactor: Rename CommentIcons to CommentBarButtons.
* chore: Improve docstrings.
* chore: Clarify unit type.
* refactor: Remove workspace argument from `navigateStacks()`.
* fix: Fix errant find and replace in CSS.
* fix: Fix issue that could cause delete button to become misaligned.
## The basics
- [x] I [validated my changes](https://developers.google.com/blockly/guides/contribute/core#making_and_verifying_a_change)
## The details
### Resolves
Fixes#9155
### Proposed Changes
In cases when an ID is missing for an element passed to `FocusableTreeTraverser.findFocusableNodeFor()`, always return `null`.
Additionally, the new short-circuit logic exposed that `Toolbox` actually wasn't being set up correctly (that is, its root element was not being configured with a valid ID). This has been fixed.
### Reason for Changes
These are cases when a valid node should never be matched (and it's technically possible to incorrectly match if an `IFocusableNode` is set up incorrectly and is providing a focusable element with an unset ID). This avoids the extra computation time of potentially calling deep into `WorkspaceSvg` and exploring all possible nodes for an ID that should never match.
Note that there is a weird quirk with `null` IDs actually being the string `"null"`. This is a side effect of how `setAttribute` and attributes in general work with HTML elements. There's nothing really that can be done here, so it's now considered invalid to also have an ID of string `"null"` just to ensure the `null` case is properly short-circuited.
Finally, the issue with toolbox being configured incorrectly was discovered with the introducing of a new hard failure in `FocusManager.registerTree()` when a tree with an invalid root element is registered. From testing there are no other such trees that need to be updated.
A new warning was also added if `focusNode()` is used on a node with an element that has an invalid ID. This isn't a hard failure to follow the convention of other invalid `focusNode()` situations. It's much more fragile for `focusNode()` to throw than `registerTree()` since the former generally happens much earlier in a page lifecycle, and is less prone to dynamic behaviors.
### Test Coverage
New tests were added to validate the various empty ID cases for `FocusableTreeTraverser.findFocusableNodeFor()`, and to validate the new error check for `FocusManager.registerTree()`.
### Documentation
No new documentation should be needed.
### Additional Information
Nothing to add.
* refactor(build): Rename "package" gulp task (but not npm script) to "pack"
This is to avoid an issue due to "package" being a reserved word
in JavaScript, and therefore not a valid export identifier.
* refactor(build): Convert gulpfile.js from CJS to ESM.
* refactor(build): Convert scripts/gulpfiles/*.js from CJS to ESM
* fix(build): Fix eslint warning for @license tag in gulpfile.mjs
* chore(build): Remove unused imports
* fix(build): Fix incorrect import of gulp-gzip
* fix(build): Fix incorrect sourcemaps import reference
The order of the modifiers is not significant to Blockly but it's conventional
to say e.g. Cmd+Shift+Z. Following that order here means that UI like the
keyboard navigation shortcut dialog gets the correct order without having to
sort.
* refactor(interfaces): Use typeof ... === 'function' to test for methods
Testing for
'name' in object
or
obj.name !== undefined
only checks for the existence of the property (and in the latter
case that the property is not set to undefined). That's fine if
the interface specifies a property of indeterminate type, but in
the usual case that the interface member is a method we can do
one better and check to make sure the property's value is
callable.
* refactor(interfaces): Always check obj is not null/undefined
Since most type predicates take an argument of type any but then
check for the existence of certain properties, explicitly check
that the argument is not null or undefined (or check implicitly
by calling another type predicate that does so first, which
necessitates adding a few casts because tsc infers the type of
the argument too narrowly).
* fix(interfaces): Add missing check to hasBubble type predicate
This appears to have inadvertently been omitted in PR #9004.
* fix(interfaces): Fix misplaced typeof
* fix: Fix typos in JSDocs
* fix(tests): Make Mocks conform to corresponding interfaces
Introduce a new MockFocusable, and add methods to MockIcon,
MockBubbleIcon and MockComment, so that they fulfil the
IFocusableNode, IIcon, IHasBubble and ICommentIcon interfaces
respectively.
* chore(tests): Add assertions verifying mocks conform to predicates
Add (test) runtime assertions that:
- isFocusableNode(MockFocusable) returns true
- isIcon(MockIcon) returns true
- hasBubble(MockBubbleIcon) returns true
- isCommentIcon(MockCommentIcon) returns true
(The latter is currently failing because Blockly is undefined when
isCommentIcon calls the MockCommentIcon's getType method.)
* fix(tests): Don't rely on Blockly being set in Mock methods
For some reason the global Blockly binding is not visible at the
time when isCommentIcon calls MockCommentIcon's getType method,
and presumably this problem would apply to getBubbleSize too,
so directly import the required items.
* refactor(tests): Make MockCommentIcon a MockBubbleIcon
This slightly simplifies it and makes it less likely to accidentally
stop conforming to IHasBubble.
* fix(interfaces): Fix incorrect check in isSelectable
Fix an error which caused ISelectable instances to fail
isSelectable() checks, one of the results of which is that
Blockly.common.getSelected() would generally return null.
Whoops!
## The basics
- [x] I [validated my changes](https://developers.google.com/blockly/guides/contribute/core#making_and_verifying_a_change)
## The details
### Resolves
N/A (no tracking issue)
### Proposed Changes
Introduces a GitHub actions CI workflow to run the webdriver IO tests from https://github.com/google/blockly-keyboard-experimentation as part of core Blockly's CI.
### Reason for Changes
Since development on the plugin is going to continue for many months yet, this ensures that behavioral changes in core Blockly don't inadvertently break the plugin.
Note that this shouldn't be made a blocking workflow since there may be cases where it's necessary to break the plugin before a change to the plugin itself can be introduced to then fix it (as this has happened many times in the past). However, the CI check is forced signal to both author and reviewer as to whether their change affects the plugin without having to manually check the test suite.
### Test Coverage
N/A -- Verifying that the CI workflow runs is sufficient.
### Documentation
No documentation changes are needed here.
### Additional Information
Nothing.