## The basics
- [x] I [validated my changes](https://developers.google.com/blockly/guides/contribute/core#making_and_verifying_a_change)
## The details
### Resolves
Fixes#8918Fixes#8919
Fixes part of #8771
### Proposed Changes
This updates several classes in order to make toolboxes and flyouts focusable:
- `IFlyout` is now an `IFocusableTree` with corresponding implementations in `FlyoutBase`.
- `IToolbox` is now an `IFocusableTree` with corresponding implementations in `Toolbox`.
- `IToolboxItem` is now an `IFocusableNode` with corresponding implementations in `ToolboxItem`.
- As the primary toolbox items, `ToolboxCategory` and `ToolboxSeparator` were updated to have -1 tab indexes and defined IDs to help `ToolboxItem` fulfill its contracted for `IFocusableNode.getFocusableElement`.
Each of these two new focusable trees have specific noteworthy behaviors behaviors:
- `Toolbox` will automatically indicate that its first item should be focused (if one is present), even overriding the ability to focus the toolbox's root (however there are some cases where that can still happen).
- `Toolbox` will automatically synchronize its selection state with its item nodes being focused.
- `FlyoutBase`, now being a focusable tree, has had a tab index of 0 added. Normally a tab index of -1 is all that's needed, but the keyboard navigation plugin specifically uses 0 for flyout so that the flyout is tabbable. This is a **new** tab stop being introduced.
- `FlyoutBase` holds a workspace (for rendering blocks) and, since `WorkspaceSvg` is already set up to be a focusable tree, it's represented as a subtree to `FlyoutBase`. This does introduce some wonky behaviors: the flyout's root will have passive focus while its contents have active focus. This could be manually disabled with some CSS if it ends up being a confusing user experience.
- Both `FlyoutBase` and `WorkspaceSvg` have built-in behaviors for detecting when a user tries navigating away from an open flyout to ensure that the flyout is closed when it's supposed to be. That is, the flyout is auto-hideable and a non-flyout, non-toolbox node has then been focused. This matches parity with the `T`/`Esc` flows supported in the keyboard navigation plugin playground.
One other thing to note: `Toolbox` had a few tests to update that were trying to reinit a toolbox without first disposing of it (which was caught by one of `FocusManager`'s state guardrails).
### Reason for Changes
This is part of an ongoing effort to ensure key components of Blockly are focusable so that they can be keyboard-navigable (with other needed changes yet both in Core Blockly and the keyboard navigation plugin).
### Test Coverage
No new tests have been added. It's certainly possible to add unit tests for the focusable configurations being introduced in this PR, but it may not be highly beneficial. It's largely assumed that the individual implementations should work due to a highly tested FocusManager, and it may be the case that the interactions of the components working together is far more important to verify (that is, the end user flows). The latter is planned to be tackled as part of #8915.
### Documentation
No documentation changes should be needed here.
### Additional Information
This includes changes that have been pulled from #8875.
* feat: Allow resetting alert/prompt/confirm to defaults.
* chore: Add unit tests for Blockly.dialog.
* fix: Removed TEST_ONLY hack from Blockly.dialog.
* feat: Add a default toast notification implementation.
* feat: Add support for toasts to Blockly.dialog.
* chore: Add tests for default toast implementation.
* chore: Fix docstring.
* refactor: Use default arguments for dialog functions.
* refactor: Add 'close' to the list of messages.
* chore: Add new message in several other places.
* chore: clarify docstrings.
* feat: Make toast assertiveness configurable.
This introduces new callback methods for IFocusableTree and
IFocusableNode for providing a basis of synchronizing domain state with
focus changes. It also introduces support for implementations of
IFocusableTree to better manage initial state cases, especially when a
tree is focused using tab navigation.
FocusManager has also been updated to ensure functional parity between
tab-navigating to a tree and using focusTree() on that tree. This means
that tab navigating to a tree will actually restore focus back to that
tree's previous focused node rather than the root (unless the root is
navigated to from within the tree itself). This is meant to provide
better consistency between tab and non-tab keyboard navigation.
Note that these changes originally came from #8875 and are required for
later PRs that will introduce IFocusableNode and IFocusableTree
implementations.
* chore: tests for cursor getNextNode
* chore: add tests for getPreviousNode
* feat: add looping to getPreviousNode and getNextNode
* chore: inline returns
* chore: fix test that results in a stack node
* chore: fix annotations
* refactor: Backport LineCursor to core.
* fix: Fix instantiation of LineCursor.
* fix: Fix tests.
* chore: Assauge the linter.
* chore: Fix some typos.
* feat: Make padding configurable for scrollBoundsIntoView.
* chore: Merge in the latest changes from keyboard-experimentation.
* refactor: Clarify name and docs for findSiblingOrParentSibling().
* fix: Improve scrollBoundsIntoView() behavior.
* fix: Export CursorOptions.
* refactor: Further clarify second parameter of setCurNode().
* fix: Revert change that could prevent scrolling bounds into view.
This adds new tests for the FocusableTreeTraverser and fixes a number of
issues with the original implementation (one of which required two new
API methods to be added to IFocusableTree). More tests have also been
added for FocusManager, and defocusing tracked nodes/trees has been
fully implemented in FocusManager.
This is the bulk of the work for introducing the central logical unit
for managing and sychronizing focus as a first-class Blockly concept
with that of DOM focus.
There's a lot to do yet, including:
- Ensuring clicks within Blockly's scope correctly sync back to focus
changes.
- Adding support for, and testing, cases when focus is lost from all
registered trees.
- Testing nested tree propagation.
- Testing the traverser utility class.
- Adding implementations for IFocusableTree and IFocusableNode
throughout Blockly.
* feat: Add methods for toggling and inspecting the readonly state of a workspace.
* refactor: Use the new readonly setters/getters in place of checking the injection options.
* fix: Fix bug that allowed dragging blocks from a flyout onto a readonly workspace.
* feat: Toggle a `blocklyReadOnly` class when readonly status is changed.
* chore: Placate the linter.
* chore: Placate the compiler.
This largely reduces the dependence on direct value testing and instead
updates clean-up tests to verify logic based on relative positioning.
This guards against potential system-specific flakes that can occur when
there are subtle calculation differences on different systems.
* chore: rename .eslintrc.js to eslint.config.js
* chore: Rename eslint.config.js to eslint.config.mjs.
* refactor: Migrate ESLint config to new flat format.
* chore: Remove old per-directory and global ignore ESLint config files.
* fix: Allowlist JSDoc tag aliases.
* fix: Don't require @license in tests/*.
* fix: Add NodeJS globals to several files that run under Node.
* chore: Remove now-unneeded ESLint directives in core.
* chore: Remove invalid/unneeded ESLint directives.
* fix: Fix invalid use of `await` outside of an `async` function.
* fix: Improve screenshot error message.
* fix: Update ESLint config file to not warn on existing violations.
* chore: Remove suppressions of rules that weren't triggering.
* chore: Fix package-lock.json.
* refactor!: Rename blocklyTreeIconClosed to blocklyToolboxCategoryIconClosed.
* refactor!: Rename blocklyTreeLabel to blocklyToolboxCategoryLabel
* refactor!: Rename blocklyToolboxDiv to blocklyToolbox.
* refactor: remove unreferenced CSS classes.
* refactor!: Remove the blocklyArrowTop and blocklyArrowBottom classes.
* feat: Add a blocklyTextInputField class to text fields.
* docs(events): JSDoc improvments
* fix(filter): Introduce enqueueEvent; don't reorder in filter
Remove the broken BlockChange event reordering code from filter.
Instead, do necessary event reordering (correctly, this time)
in a new enqueueEvent function used by fireInternal.
* chore(events): Deprecate forward parameter of filter
Since we don't filter in undo any more, and filtering in reverse
order causes problems, prepare to remove this opportunity for
error.
* fix(events): Only merge adjacent events
Simplify filter by having it consider only adjacent events in the
queue for merging.
Previously any events in the queue could potentially be merged if
they were of suitable (typically identical) .type, even if other
events had occurred between them (with the sole exception of
BlockMove events, which would only be merged with adjacent
events). This could potentially result in replay failures during
undo/redo/mirroring, though it is unknown whether any such
problems occurred in practice.
* refactor(events): Tweaks
- Use for…of loop where appropriate.
- Make filter reason-merging code more concise.
- Use arrow functions when calling Array.prototype.filter.
* chore(events): Fix typos for PR #8539.
* fix: Detect if deleting shadow block affects selection highlight
* Use contains instead of descendsFrom for html consistency.
* Added tests.
* Removing and manually inlining new contains function.