feat(central-ui): ParameterValueForm.ValidChanged — additive validity callback

Claude-Session: https://claude.ai/code/session_01NcMG6xXdsJbmD2wYAgkK29
This commit is contained in:
Joseph Doherty
2026-09-08 01:09:30 -04:00
parent fefa3ffa77
commit a36c813b89
2 changed files with 25 additions and 0 deletions
@@ -48,6 +48,13 @@ else
[Parameter] public Dictionary<string, object?> Values { get; set; } = new();
[Parameter] public EventCallback<Dictionary<string, object?>> ValuesChanged { get; set; }
/// <summary>
/// Raised after every edit with <c>true</c> when the value tree has no parse or validation errors
/// (Flow Debugger script entry, design 2026-09-08 §5: the page gates Run on it). Additive; existing
/// callers ignore it.
/// </summary>
[Parameter] public EventCallback<bool> ValidChanged { get; set; }
private IReadOnlyList<ParameterShape> _shapes = Array.Empty<ParameterShape>();
private IReadOnlyList<InboundApiSchemaField> _topLevelFields = Array.Empty<InboundApiSchemaField>();
private InboundApiSchema? _rootSchema;
@@ -620,8 +627,11 @@ else
{
Validate();
await ValuesChanged.InvokeAsync(Values);
await ValidChanged.InvokeAsync(IsValid);
}
private bool IsValid => _parseErrors.Count == 0;
// Validates the whole value tree against the parsed schema and projects each
// path-qualified error onto the input that owns it. Raw-text parse failures
// (numeric / JSON) are added directly by the setters; schema validation adds
@@ -282,4 +282,19 @@ public class ParameterValueFormTests : BunitContext
Assert.Single(cut.FindAll("button.param-list-remove"));
Assert.NotEmpty(cut.FindAll(".text-danger"));
}
[Fact]
public void ValidChanged_ReportsFalseOnAParseError_AndTrueOnceFixed()
{
var valid = new List<bool>();
var cut = Render<ParameterValueForm>(p => p
.Add(x => x.ParameterDefinitions, """{"type":"object","properties":{"n":{"type":"integer"}}}""")
.Add(x => x.ValidChanged, v => valid.Add(v)));
var input = cut.Find("#param-n");
input.Input("abc");
Assert.False(valid.Last());
input.Input("5");
Assert.True(valid.Last());
}
}