diff --git a/site/src/pages/TemplateBuilder/BaseTemplateParametersStep.tsx b/site/src/pages/TemplateBuilder/BaseTemplateParametersStep.tsx index bef9b4dde6..71d95ebe06 100644 --- a/site/src/pages/TemplateBuilder/BaseTemplateParametersStep.tsx +++ b/site/src/pages/TemplateBuilder/BaseTemplateParametersStep.tsx @@ -11,6 +11,7 @@ import { TemplateBuilderTitle, } from "#/pages/TemplateBuilder/TemplateBuilderHeader"; import type { ConfigurationFieldDefinition } from "./ConfigurationField"; +import { defaultPlaceholder } from "./defaultPlaceholder"; import { TemplateConfiguration } from "./TemplateConfiguration"; interface BaseTemplateParametersStepProps { @@ -53,7 +54,9 @@ function variableToField( label: variable.name, description: variable.description || undefined, required: variable.required, - placeholder: variable.required ? "Required" : "Optional", + placeholder: + defaultPlaceholder(variable.default) ?? + (variable.required ? "Required" : "Optional"), field: { name: variable.name, id, diff --git a/site/src/pages/TemplateBuilder/ModuleSettingsStep.tsx b/site/src/pages/TemplateBuilder/ModuleSettingsStep.tsx index 428182a1a5..ca2bcb0dd4 100644 --- a/site/src/pages/TemplateBuilder/ModuleSettingsStep.tsx +++ b/site/src/pages/TemplateBuilder/ModuleSettingsStep.tsx @@ -12,6 +12,7 @@ import { TemplateBuilderTitle, } from "#/pages/TemplateBuilder/TemplateBuilderHeader"; import type { ConfigurationFieldDefinition } from "./ConfigurationField"; +import { defaultPlaceholder } from "./defaultPlaceholder"; import { ModuleConfiguration } from "./ModuleConfiguration"; interface ModuleSettingsStepProps { @@ -51,7 +52,9 @@ function variableToField( label: variable.name, description: variable.description || undefined, required: variable.required, - placeholder: variable.required ? "Required" : "Optional", + placeholder: + defaultPlaceholder(variable.default) ?? + (variable.required ? "Required" : "Optional"), field: { name: variable.name, id, diff --git a/site/src/pages/TemplateBuilder/defaultPlaceholder.test.ts b/site/src/pages/TemplateBuilder/defaultPlaceholder.test.ts new file mode 100644 index 0000000000..90eef2c455 --- /dev/null +++ b/site/src/pages/TemplateBuilder/defaultPlaceholder.test.ts @@ -0,0 +1,28 @@ +import { defaultPlaceholder } from "./defaultPlaceholder"; + +describe("defaultPlaceholder", () => { + it("formats numeric defaults, including zero", () => { + expect(defaultPlaceholder(13337)).toBe("13337"); + expect(defaultPlaceholder(0)).toBe("0"); + }); + + it("formats boolean defaults", () => { + expect(defaultPlaceholder(false)).toBe("false"); + expect(defaultPlaceholder(true)).toBe("true"); + }); + + it("returns non-empty string defaults unchanged", () => { + expect(defaultPlaceholder("v0.10.0")).toBe("v0.10.0"); + }); + + it("returns undefined for empty or absent defaults", () => { + expect(defaultPlaceholder("")).toBeUndefined(); + expect(defaultPlaceholder(undefined)).toBeUndefined(); + expect(defaultPlaceholder(null)).toBeUndefined(); + }); + + it("returns undefined for object and array defaults", () => { + expect(defaultPlaceholder({ key: "value" })).toBeUndefined(); + expect(defaultPlaceholder(["a", "b"])).toBeUndefined(); + }); +}); diff --git a/site/src/pages/TemplateBuilder/defaultPlaceholder.ts b/site/src/pages/TemplateBuilder/defaultPlaceholder.ts new file mode 100644 index 0000000000..9370ad64a5 --- /dev/null +++ b/site/src/pages/TemplateBuilder/defaultPlaceholder.ts @@ -0,0 +1,22 @@ +/** + * Coerces a module variable's `default` into placeholder text. + * + * `TemplateBuilderModuleVariable.default` is generated as + * `Record` because every `json.RawMessage` field maps to that + * type, but the actual wire value is a JSON scalar (string, number, or + * boolean). The parameter is typed `unknown` so the generated value is accepted + * without a cast. + * + * An empty-string default returns `undefined` so callers fall back to their + * existing Required/Optional hint. Object or array defaults also return + * `undefined` since they have no meaningful placeholder representation. + */ +export function defaultPlaceholder(value: unknown): string | undefined { + if (typeof value === "number" || typeof value === "boolean") { + return String(value); + } + if (typeof value === "string" && value !== "") { + return value; + } + return undefined; +}