fix: align enumNames when stripping null, and make the JSON fallback typeable
Copilot round 5: one inline comment, two suppressed. All three valid, and two
were defects from round 4's own fix.
1. Stripping the `null` sentinel from an `enum` left the parallel `enumNames`
untouched. Both renderers discard labels outright when the two lengths
disagree, so `{ enum: ["a", null], enumNames: ["Alpha", "None"] }` lost the
`Alpha` label entirely — a worse outcome than the sentinel it was fixing.
`enumNames` is now filtered by the same indices, and only when it was
positionally parallel to begin with (a mismatched list is already ignored
by both renderers, so re-indexing it would invent an alignment the server
never declared).
2. `enum: [null]` became `enum: undefined`, which widened a schema permitting
*only* `null` into a plain string field accepting arbitrary text — the form
would then invite values the schema forbids. Trading a cosmetic problem for
a correctness one, which was the wrong call. The collapse now declines
entirely for that case and the field renders through the JSON editor.
3. That JSON editor was still carrying #1928's original mechanism: it stored
unparseable text back as the *value*, which the next render
re-`JSON.stringify`d, so typing `[` gave `"["` then `"\"[\""`. Routing
nullable fields to real widgets removed the common way of landing there but
never fixed the editor — and #2007's fix deliberately sends object unions to
it, so a fallback nobody can type into is not a fallback. `SchemaJsonField`
now holds the draft text locally (the split `SchemaNumberInput` already
uses) and reports parsed JSON, or `undefined` while the text is mid-edit —
an inspector must not report a value it cannot send. Keyed by `resetKey`
like the number field, since it now holds state.
Also replaces an `as unknown as string[]` in the round-4 test, which violated
the repo's TypeScript rules and papered over `InspectorFormSchema.enum` being
unable to model a valid `["a", null]`. The fixture is wire data, so it now goes
through `toFormSchema` — the same wire→form narrowing boundary every production
call site uses.
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
52661187df
commit
32d6f80bc1
1 changed files with 279 additions and 47 deletions
|
|
@ -277,14 +277,52 @@ describe("normalizeNullableUnion", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("drops an enum that held nothing but null", () => {
|
||||
// No value a dropdown could offer, so the field is better served by its
|
||||
// plain widget than by an empty select.
|
||||
// Declining is the point: emitting `enum: undefined` would turn a schema that
|
||||
// permits *only* `null` into a plain string field accepting arbitrary text,
|
||||
// inviting values the schema forbids. Left uncollapsed it renders through the
|
||||
// JSON editor, which represents it honestly.
|
||||
it("declines to collapse an enum that held nothing but null", () => {
|
||||
const schema = { type: ["string", "null"], enum: [null] };
|
||||
expect(normalizeNullableUnion(schema)).toBe(schema);
|
||||
});
|
||||
|
||||
it("declines the same way on the anyOf encoding", () => {
|
||||
const schema = {
|
||||
anyOf: [{ type: "string", enum: [null] }, { type: "null" as const }],
|
||||
};
|
||||
expect(normalizeNullableUnion(schema)).toBe(schema);
|
||||
});
|
||||
|
||||
// The parallel `enumNames` is filtered by the same indices. Both renderers
|
||||
// discard labels outright on a length mismatch, so stripping one without the
|
||||
// other would silently lose every label, not just the dropped one's.
|
||||
it("keeps enumNames aligned when stripping the null member", () => {
|
||||
expect(
|
||||
normalizeNullableUnion({ type: ["string", "null"], enum: [null] }),
|
||||
normalizeNullableUnion({
|
||||
type: ["string", "null"],
|
||||
enum: ["a", null, "b"],
|
||||
enumNames: ["Alpha", "None", "Beta"],
|
||||
}),
|
||||
).toEqual({
|
||||
type: "string",
|
||||
enum: undefined,
|
||||
enum: ["a", "b"],
|
||||
enumNames: ["Alpha", "Beta"],
|
||||
anyOf: undefined,
|
||||
nullable: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("leaves a mismatched enumNames alone rather than inventing an alignment", () => {
|
||||
expect(
|
||||
normalizeNullableUnion({
|
||||
type: ["string", "null"],
|
||||
enum: ["a", null],
|
||||
enumNames: ["Alpha"],
|
||||
}),
|
||||
).toEqual({
|
||||
type: "string",
|
||||
enum: ["a"],
|
||||
enumNames: ["Alpha"],
|
||||
anyOf: undefined,
|
||||
nullable: true,
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue