* feat: Add interfaces for keyboard navigation.
* feat: Add the Navigator.
* feat: Make core types conform to INavigable.
* feat: Require FlyoutItems elements to be INavigable.
* feat: Add navigation policies for built-in types.
* refactor: Convert Marker and LineCursor to operate on INavigables instead of ASTNodes.
* chore: Delete dead code in ASTNode.
* fix: Fix the tests.
* chore: Assuage the linter.
* fix: Fix advanced build/tests.
* chore: Restore ASTNode tests.
* refactor: Move isNavigable() validation into Navigator.
* refactor: Exercise navigation instead of ASTNode.
* chore: Rename astnode_test.js to navigation_test.js.
* chore: Enable the navigation tests.
* fix: Fix bug when retrieving the first child of an empty workspace.
## The basics
- [x] I [validated my changes](https://developers.google.com/blockly/guides/contribute/core#making_and_verifying_a_change)
## The details
### Resolves
Fixes#8922Fixes#8929
Fixes part of #8771
### Proposed Changes
This PR introduces support for fields to be focusable (and thus navigable with keyboard navigation when paired with downstream changes to `LineCursor` and the keyboard navigation plugin). This is a largely isolated change in how it fundamentally works:
- `Field` was updated to become an `IFocusableNode`. Note that it uses a specific string-based ID schema in order to ensure that it can be properly linked back to its unique block (which helps make the search for the field in `WorkspaceSvg` a bit more efficient). This could be done with a globally unique ID, instead, but all fields would need to be searched vs. just those for the field's parent block.
- The drop-down and widget divs have been updated to manage ephemeral focus with `FocusManager` when they're open for non-system dialogs (ephemeral focus isn't needed for system dialogs/prompts since those already take/restore focus in a native way that `FocusManager` will respond to--this may require future work, however, if the restoration causes unexpected behavior for users). This approach was done due to a suggestion from @maribethb as the alternative would be a more complicated breaking change (forcing `Field` subclasses to properly manage ephemeral focus). It may still be the case that certain cases will need to do so, but widget and drop-down divs seem to address the majority of possibilities.
**Important**: `Input`s are not explicitly being supported here. As far as I can tell, we can't run into a case where `LineCursor` tries to set an input node, though perhaps I simply haven't come across this case. Supporting `Fields` and `Connections` (per #8928) seems to cover the main needed cases, though making `Input`s focusable may be a future requirement.
### 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).
Note that #8929 isn't broadly addressed since making widget & drop down divs manage ephemeral focus directly addresses a large class of cases. Additional cases may arise where a plugin or Blockly integration may require additional effort to make keyboard navigation work for their field--this may be best addressed with documentation and guidance.
### 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 new documentation is planned, however it may be prudent to update the field documentation in the future to explain how to utilize ephemeral focus when specifically building compatibility for keyboard navigation.
### Additional Information
This includes changes that have been pulled from #8875.
* 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.
* refactor(events): Use "export ... from" where applicable
* refactor(events): Introduce EventType enum
Introduce an enum for the event .type values. We can't actually
use it as the type of the .type property on Abstract events,
because we want to allow developers to add their own custom
event types inheriting from this type, but at least this way we
can be reasonably sure that all of our own event subclasses have
distinct .type values—plus consistent use of enum syntax
(EventType.TYPE_NAME) is probably good for readability overall.
Put it in a separate module from the rest of events/utils.ts
because it would be helpful if event utils could use
event instanceof SomeEventType
for type narrowing but but at the moment most events are in
modules that depend on events/utils.ts for their .type
constant, and although circular ESM dependencies should work
in principle there are various restrictions and this
particular circularity causes issues at the moment.
A few of the event classes also depend on utils.ts for fire()
or other functions, which will be harder to deal with, but at
least this commit is win in terms of reducing the complexity
of our dependencies, making most of the Abstract event subclass
module dependent on type.ts, which has no imports, rather than
on utils.ts which has multiple imports.
* chore(deps): Add pretter-plugin-organize-imports
* chore: Remove insignificant blank lines in import sections
Since prettier-plugin-organize-imports sorts imports within
sections separated by blank lines, but preserves the section
divisions, remove any blank lines that are not dividing imports
into meaningful sections.
Do not remove blank lines separating side-effect-only imports
from main imports.
* chore: Remove unneded eslint-disable directives
* chore: Organise imports
* feat!: Add a blocklyFieldText CSS class to fields' text elements (#8291)
* add class instead of replace
Co-authored-by: Beka Westberg <bwestberg@google.com>
---------
Co-authored-by: Beka Westberg <bwestberg@google.com>
* feat: add ability to click fields in flyouts
* feat: control if icons are clickable in flyouts
* fix: make default icons not clickable in flyout
* fix: use booleans like a real programmer
* chore: delete dead code
* chore: moves location updating into the block
* chore: change dragging to use update component locations
* fix: field widgets not being moved when blocks are editted
* chore: remove unnecessary resizeEditor_ calls
* chore: format
* chore: fix build
* fix: tests
* chore: PR comments
* chore: format
* fix(build): Restore erroneously-deleted filter function
This was deleted in PR #7406 as it was mainly being used to
filter core/ vs. test/mocha/ deps into separate deps files -
but it turns out also to be used for filtering error
messages too. Oops.
* refactor(tests): Migrate advanced compilation test to ES Modules
* refactor(build): Migrate main.js to TypeScript
This turns out to be pretty straight forward, even if it would
cause crashing if one actually tried to import this module
instead of just feeding it to Closure Compiler.
* chore(build): Remove goog.declareModuleId calls
Replace goog.declareModuleId calls with a comment recording the
former module ID for posterity (or at least until we decide
how to reformat the renamings file.
* chore(tests): Delete closure/goog/*
For the moment we still need something to serve as base.js for
the benefit of closure-make-deps, so we keep a vestigial
base.js around, containing only the @provideGoog declaration.
* refactor(build): Remove vestigial base.js
By changing slightly the command line arguments to
closure-make-deps and closure-calculate-chunks the need to have
any base.js is eliminated.
* chore: Typo fix for PR #7415
* Copy core/events/events_block_change.ts to core/events/events_block_field_intermediate_change.ts
* New intermediate event type for field edits.
* Addressing PR feedback.
* Ran npm run format.
* Fixed procedure mutator responding to param edits.
* Intermediate events now inherit from BlockBase.
* Addressing feedback on PR.
* chore: format
* chore: move input and input types into new directory
* feat: define and export new input types
* feat: modify blocks to construct individual inputs
* chore: transition code to use actual type checks
* chore: fixup input type type
* chore: format
* chore: fixup PR comments
* chore: fix build
We introduced the SKIP_SETUP sentinel value when converting
Field and its subclasses to ES6 class syntax, because super
must be called before any other code in a subclass
constructor, breaking the previous mechanism where subclasses
would set some properties before calling their superclass
constructor.
SKIP_SETUP was a singleton value of class Sentinel.
Recently, in PR #6639 @btw17 introduced the isSentinel type
predicate to improve the typing of Field. Unfortunately, there
were some aspects of this change that were not very elegant:
- isSentinel was declared as a static method on Field (rather
than on Sentinel itself).
- It only checks against the specific value SKIP_SETUP,
rather than checking if the argument is instanceof Sentinel
(though perhaps this is for efficiency?)
Additionally - as a result of the migration from ES6 to TS, and
predating PR #6639 - The signature for the Field constructor's
first argument was typed T|Sentinel, with subclass constructors
generally being <some type(s)>|Sentinel.
This creates a small problem when attempting to port Fields from
core to plugins, because Sentinel is not reexported by
core/utils.ts (and therefore not from core/blockly.ts either).
The latter problem could be solved simply by reexporting Sentinel,
or by having plugin constructors not accept SKIP_SETUP (though
this potentially makes them more difficult to subclass), but
neither is particularly desirable.
Instead, this PR proposes that we:
- Make Field.SKIP_SETUP a (unique) Symbol.
- Change the value argument to the Field constructor to accept
T|typeof Field.SKIP_SETUP - where typeof Field.SKIP_SETUP is
(like a literal type) a type that accepts just the single
value SKIP_SETUP.
- Remove the Sentinel class and core/utils/sentinel.ts.
Not treating this as a breaking change:
- Removes Field.isSentinel - though this addition has not yet
been published, so it can only break our own as-yet-unreleased
code in samples.
- Changes the type of Field.SKIP_SETUP and the first argument
of the Field constructor from Sentinel to typeof SKIP_SETUP
(a unique Symbol) - but given that Sentinel has never been
exported this should not break any actual external code.
* Reduce usage of obsolete .keyCode property.
* Rename private properties/methods which violate eslint rules.
* Use arrays of bound events rather than individual properties.
* Improve typing info.
* Also fix O(n^2) recursive performance issue in theme's getComponentStyle function.
* And replace String(...) with '${...}' (smaller, faster).
* .toString() is considered harmful.
* refactor(xml): Move textToDom to core/utils/xml.ts
This function being in core/xml.ts was the cause for the last
remaining circular import in core/ (between variables.ts and
xml.ts).
Moving it to utils/xml.ts makes sense anyway, since there is
nothing Blockly-specific about this function.
Fixes#6817.
* fix(closure): Reenable goog.declareModuleId multiple-call check
Reenable an assertion which check to make sure that
goog.declareModuleId is not called more than once in a module
(and which also catches circular imports amongst ES modules, which
are not detected by closure-make-deps).
* chore(tests,demos): Augo-migrate use of textToDom
Testing the migration file entry by auto-migrating all uses of
Blockly.Xml.textToDom to Blockly.utils.xml.textToDom.
* chore(blocks): Manually migrate remaining use of textToDom
Update the one remaining call to textToDom (in blocks/lists.ts)
to the function's new location - also removing the last use of
the Blockly.Xml / core/xml.ts) module from this file.
* docs(xml): Remove unneeded @alias per comments on PR #6818
* fix(imports): Remove unused import
* chore: replace `AnyDuringMigration` for field value functions
* chore: removed unnecessary `KeyboardEvent` comments and `AnyDuringMigration` casts
* chore: cleaned up `doValueUpdate_` and `doClassValidation_` in `Field` subclasses
* fix: updated `FieldValidator` to allow returning `undefined` and restructured `setValue`
* fix: implemented initial `core/field_checkbox.ts` feedback
* fix: updated `Field` to accept `U` and `undefined` and reverted subclass constructor handling of the input value
* fix: reverted `getVars` to returning `string[]` and added related comment
* chore: removed unnecessary comment
* fix: updated `processValidation_` to no longer allow returning `undefined`
* fix: removed `Un` type alias for `undefined`
* chore: removed unnecessary string cast in `core/field_colour.ts`
* fix: updated `doClassValidation_` not to expect `null` since it will never come up in `setValue`
* fix: updated `doClassValidation_` to only allow `undefined` when the new value exists
* Updated `FieldValidator` type to expect `newValue` to exist
* cleanup: updated `picker` from type `Element` to type `HTMLElement`
* fix: updated `doValueInvalid_` type info in `core/field_input.ts` to handle `string` and `undefined`
* fix: reverted `getValue` in `core/field_checkbox.ts` to previous logic
* fix: updated the `Field` constructor to allow `value` to be optional
* chore: consolidated `Validation` with `FieldValidator` and `doClassValidation_`
* fix: reverted generic param `U` while handling diverging user input is being discussed
* fix: updated `doClassValidation_` return comment to work for TSDoc
* fix: misc keyboard event function tweaks
* refactor: Remove checks for PointerEvent support.
* refactor: Deprecate and remove calls to splitEventByTouches.
* refactor: Deprecate and remove calls to setClientFromTouch().
* refactor: Use PointerEvent in place of Event/MouseEvent/TouchEvent/PseudoEvent.
* refactor: Update references to mouse/touch events in code and documentation to reference pointer events.
* refactor: Merge Gesture and TouchGesture
* chore: clang-format changed files
* refactor: Bind and expect PointerEvents instead of MouseEvents.
* refactor: Rename TouchGesture to Gesture.
* fix: Fix test failures.
* chore: clang-format changed files.
* fix: Fix errant _ from merging
* refactor: Clean up dead code in browser_events.ts.
* chore: Update version in deprecation notices to reflect release schedule
* fix: Fixed a bug that caused the browser context menu to not be suppressed in Chrome.
* fix: Re-export Gesture as TouchGesture for backwards compatibility.
* refactor: Deprecate and remove uses of opt_noPreventDefault.
* chore: Fix error message in gesture.ts.
* chore: Removed obsolete todo.
* chore: add validator and field value types
* chore/clean up unused default and unnecessary field type setting
* chore: removed IRegisterableField and updated various Field instances
* fix: remove unused field image validator function
* chore: added pass-through constructor to field_textinput