fix(centralui): Topology inline area rename commits the typed name on Enter (Gitea #38)
The rename input bound _renameBuffer with the default onchange trigger. A
browser fires change only after the Enter keydown (or on blur, which cancels
by design), so CommitRename always saw the original name and silently
no-oped: a UI rename never persisted. Present since the page was written
(pre-dates 513fa1aa); the Playwright test masked it by dispatching a
synthetic change event before Enter. Bind on oninput instead.
Tests: bUnit InlineRename_TypeThenEnter_PersistsNewName (fails on the old
binding); the Playwright rename test now types + Enter like an operator.
Also fix the Playwright OpenStableTopologyAsync race: it counted 'Load
instances' loaders immediately after clicking Expand all, before the
circuit re-render landed, and returned an unexpanded tree; it now waits
for no collapsed branch first.
This commit is contained in:
@@ -618,6 +618,7 @@
|
||||
aria-label="@($"Rename {node.Label}")"
|
||||
@ref="_renameInput"
|
||||
@bind="_renameBuffer"
|
||||
@bind:event="oninput"
|
||||
@onkeydown="(e) => OnRenameKeyDown(e, node)"
|
||||
@onblur="() => CancelRename()" />
|
||||
@if (!string.IsNullOrEmpty(_renameError))
|
||||
@@ -755,6 +756,10 @@
|
||||
_renameError = null;
|
||||
}
|
||||
|
||||
// The buffer binds on `oninput` (Gitea #38): with the default `onchange` binding the
|
||||
// browser's change event fires only AFTER the Enter keydown — so CommitRename saw the
|
||||
// unchanged original name and silently cancelled — or on blur, which cancels by design.
|
||||
// Binding per keystroke means Enter always commits what the operator typed.
|
||||
private async Task OnRenameKeyDown(KeyboardEventArgs e, TopoNode node)
|
||||
{
|
||||
if (e.Key == "Escape")
|
||||
|
||||
+9
-6
@@ -139,13 +139,10 @@ public class TopologyAreaTests : IClassFixture<DeploymentFixture>
|
||||
|
||||
var input = page.Locator($"input[aria-label='Rename {original}']");
|
||||
await Assertions.Expect(input).ToBeVisibleAsync();
|
||||
// Type then Enter, exactly as an operator does. The input binds per keystroke
|
||||
// (`oninput`, Gitea #38) — this test used to dispatch a synthetic `change` event
|
||||
// first, which masked the bug that a real Enter never committed the typed name.
|
||||
await input.FillAsync(renamed);
|
||||
// The input is @bind="_renameBuffer" with the default onchange trigger, which a
|
||||
// raw Enter keystroke does NOT fire (onchange only fires on blur in the browser) —
|
||||
// and blur CANCELS the rename. So dispatch a `change` event to flush the bound
|
||||
// value to the server WITHOUT losing focus, then press Enter to run CommitRename
|
||||
// against the now-updated _renameBuffer.
|
||||
await input.DispatchEventAsync("change");
|
||||
await input.PressAsync("Enter"); // commit WITHOUT blurring (blur would cancel)
|
||||
|
||||
await Assertions.Expect(page.Locator(".toast")).ToHaveCountAsync(1, new() { Timeout = 15_000 });
|
||||
@@ -321,9 +318,15 @@ public class TopologyAreaTests : IClassFixture<DeploymentFixture>
|
||||
if (await live.IsCheckedAsync()) await live.UncheckAsync(); // stop the 15s tree reload
|
||||
// Lazy topology loads a bounded first page per branch. These small authoring
|
||||
// fixtures need their sites/areas loaded before addressing labels or instances.
|
||||
// "Expand all" re-renders over the SignalR circuit, so the click returns before the
|
||||
// newly-expanded branches (and their "Load instances" loaders) exist. Wait until no
|
||||
// collapsed branch remains before counting loaders — counting immediately raced the
|
||||
// render and returned an unexpanded tree whenever the round-trip lost.
|
||||
var collapsed = page.Locator("li[role='treeitem'][aria-expanded='false']");
|
||||
for (var branch = 0; branch < 300; branch++)
|
||||
{
|
||||
await page.Locator("button[aria-label='Expand all areas']").ClickAsync();
|
||||
await Assertions.Expect(collapsed).ToHaveCountAsync(0);
|
||||
var load = page.GetByRole(AriaRole.Button, new() { Name = "Load instances", Exact = true });
|
||||
if (await load.CountAsync() == 0) return page;
|
||||
var pages = page.GetByText("Page 1", new() { Exact = true });
|
||||
|
||||
@@ -449,6 +449,33 @@ public class TopologyPageTests : BunitContext
|
||||
Assert.NotNull(cut.Find("input.form-control-sm.d-inline-block"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gitea #38: typing a new name and pressing Enter must persist the rename. The buffer
|
||||
/// used to bind on <c>onchange</c>, which a browser fires only after the Enter keydown
|
||||
/// (or on blur, which cancels) — so the commit always saw the original name and no-oped.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task InlineRename_TypeThenEnter_PersistsNewName()
|
||||
{
|
||||
var area = new Area("Line-1") { Id = 10, SiteId = 1 };
|
||||
var areasBySite = new Dictionary<int, IReadOnlyList<Area>> { [1] = new List<Area> { area } };
|
||||
SeedRepos(sites: new[] { new Site("Plant-A", "plant-a") { Id = 1 } }, areasBySite: areasBySite);
|
||||
_repo.GetAreaByIdAsync(10, Arg.Any<CancellationToken>()).Returns(area);
|
||||
|
||||
var cut = RenderLoadedTopology();
|
||||
FindToggleForLabel(cut, "Plant-A")!.Click();
|
||||
cut.FindAll("span.tv-label").First(s => s.TextContent == "Line-1").DoubleClick();
|
||||
|
||||
var input = cut.Find("input[aria-label='Rename Line-1']");
|
||||
// Keystroke-level binding: no change event (that only arrives after Enter, or on blur).
|
||||
input.Input("Line-1-renamed");
|
||||
await cut.Find("input[aria-label='Rename Line-1']").KeyDownAsync(new Microsoft.AspNetCore.Components.Web.KeyboardEventArgs { Key = "Enter" });
|
||||
|
||||
await _repo.Received(1).UpdateAreaAsync(
|
||||
Arg.Is<Area>(a => a.Id == 10 && a.Name == "Line-1-renamed"), Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
/// <summary>Verifies that instance rows do not support double-click rename.</summary>
|
||||
[Fact]
|
||||
public void InstanceRows_DoNotHaveDoubleClickRename()
|
||||
|
||||
Reference in New Issue
Block a user