From 621d6bfd946b37c985b402f7285c9146c622f41c Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 8 Sep 2026 09:37:44 -0400 Subject: [PATCH] feat(central-ui): InstancePicker search filter + keyboard (Enter/Escape/ArrowDown) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Typing in the field is the filter: the forest is rebuilt to the matching instances plus their ancestor areas and force-expanded so every match is on screen; clearing the text restores the full forest, collapsed. Both bulk expansions are deferred to OnAfterRenderAsync, because the TreeView only exists once the panel has rendered. Enter picks the first instance the operator can actually SEE — document order, descending into a branch only when the tree reports it expanded — so with areas collapsed it picks the first root instance, and with a filter (every branch expanded) it degrades to plain document order. Escape closes and restores the field text from the current selection; ArrowDown hands keyboard navigation to the tree via a new additive TreeView.FocusFirstVisibleAsync(), which reuses the existing roving-tabindex _focusNeedsApply path rather than adding a second focus mechanism. No preventDefault: Blazor's flag is all-or-nothing per element, and neither default is harmful (Enter in a text input outside a form does nothing, ArrowDown just moves the caret) while suppressing Tab would trap the control. Claude-Session: https://claude.ai/code/session_01NcMG6xXdsJbmD2wYAgkK29 --- .../Components/Shared/InstancePicker.razor | 5 +- .../Components/Shared/InstancePicker.razor.cs | 123 +++++++++++++ .../Components/Shared/TreeView.razor | 17 ++ .../Shared/InstancePickerTests.cs | 166 +++++++++++++++++- 4 files changed, 309 insertions(+), 2 deletions(-) diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/InstancePicker.razor b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/InstancePicker.razor index 4e32178c..0e5144ca 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/InstancePicker.razor +++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Shared/InstancePicker.razor @@ -3,6 +3,9 @@ 2026-09-08). The field shows the selected instance; the panel hosts the shared TreeView over InstanceTreeBuilder's forest with every area collapsed. Clicking an area row toggles it, clicking an instance row selects it and closes the panel. + Typing filters the forest to the matching instances and their ancestors; Enter picks + the first visible match, ArrowDown hands keyboard navigation to the tree and Escape + closes without changing the selection. Bootstrap only, no JavaScript: the transparent fixed backdrop closes the panel on an outside click, the same pattern TreeView uses for its context menu. *@ @@ -16,7 +19,7 @@ aria-expanded="@(_open ? "true" : "false")" aria-controls="@_panelId" placeholder="@Placeholder" disabled="@Disabled" data-test="@($"{DataTestPrefix}-input")" - value="@_text" @onfocus="Open" /> + value="@_text" @onfocus="Open" @oninput="OnInput" @onkeydown="OnInputKeyDown" />