From 0e953ea0385ecef3f6d443e6cd2c27322fd18387 Mon Sep 17 00:00:00 2001 From: Matt Hill <9935159+MattDHill@users.noreply.github.com> Date: Sun, 30 Aug 2026 16:12:22 -0600 Subject: [PATCH] fix(sdk): let a select or union render with no option preselected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Value.select` and `Value.union` typed `default` as a required, non-nullable key, so a packager could not express "the user must choose" — every such field had to name a winner up front. A preselected option is indistinguishable from one the user picked, so the choice gets made silently and they never learn they had one. Everything downstream already supported it. `ValueSpecSelect.default` and `ValueSpecUnion.default` are `string | null`; the UI builds a select control as `formBuilder.control(spec.default, [Validators.required])`, so a null default renders unselected and blocks submission until a choice is made, and `getUnionObject` falls through to an empty sub-form. The builder's own doc comment already documented `@type { (keyof Values & string) | null }` and `@example default: null`. Only the public signature disagreed. Every other builder — text, textarea, number, color, datetime, triState — already accepts a null default; select and union were the outliers. Co-Authored-By: Claude Opus 5 (1M context) --- projects/start-sdk/CHANGELOG.md | 4 ++++ .../lib/actions/input/builder/value.ts | 19 ++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/projects/start-sdk/CHANGELOG.md b/projects/start-sdk/CHANGELOG.md index b38395af6..788b8fbe9 100644 --- a/projects/start-sdk/CHANGELOG.md +++ b/projects/start-sdk/CHANGELOG.md @@ -107,6 +107,10 @@ one canonical origin. See [Nominating an Address to Open](https://docs.start9.com/packaging/interfaces.html#nominating-an-address-to-open). +- **`Value.select`, `Value.dynamicSelect`, `Value.union` and `Value.dynamicUnion` + accept `default: null`**, which renders the field unselected and holds the form + unsubmittable until the user picks one + ### Fixed - **Scaffolded package CI builds a draft PR when it becomes ready and rebuilds diff --git a/shared-libs/ts-modules/start-core/lib/actions/input/builder/value.ts b/shared-libs/ts-modules/start-core/lib/actions/input/builder/value.ts index 0d662a7b9..9664715a4 100644 --- a/shared-libs/ts-modules/start-core/lib/actions/input/builder/value.ts +++ b/shared-libs/ts-modules/start-core/lib/actions/input/builder/value.ts @@ -885,12 +885,12 @@ export class Value< /** Supplementary text rendered persistently beneath the field. */ footnote?: string | null /** - * @description Determines if the field is required. If so, optionally provide a default value from the list of values. + * @description The option to preselect. `null` leaves the field unselected; a selection is required either way. * @type { (keyof Values & string) | null } * @example default: null * @example default: 'radio1' */ - default: keyof Values & string + default: (keyof Values & string) | null /** * @description A mapping of unique radio options to their human readable display format. * @example @@ -937,7 +937,7 @@ export class Value< description?: string | null warning?: string | null footnote?: string | null - default: string + default: string | null values: Values disabled?: false | string | string[] }, @@ -1223,11 +1223,12 @@ export class Value< warning?: string | null variants: Variants /** - * @description Provide a default value from the list of variants. - * @type { string } + * @description The variant to preselect. `null` leaves the field unselected; a selection is required either way. + * @type { (keyof VariantValues & string) | null } + * @example default: null * @example default: 'variant1' */ - default: keyof VariantValues & string + default: (keyof VariantValues & string) | null /** * @description Once set, the value can never be changed. * @default false @@ -1269,7 +1270,7 @@ export class Value< description?: string | null warning?: string | null variants: Variants - default: keyof VariantValues & string + default: (keyof VariantValues & string) | null disabled: string[] | false | string }, OuterType @@ -1292,7 +1293,7 @@ export class Value< description?: string | null warning?: string | null variants: Variants - default: keyof VariantValues & string + default: (keyof VariantValues & string) | null disabled: string[] | false | string }, OuterType @@ -1318,7 +1319,7 @@ export class Value< description?: string | null warning?: string | null variants: Variants - default: keyof VariantValues & string + default: (keyof VariantValues & string) | null disabled: string[] | false | string }, OuterType