Skip to content

Commit ec003b7

Browse files
authored
fix: update default value handling for dynamic defaults (coder#17609)
resolves coder/preview#102
1 parent 4587082 commit ec003b7

File tree

2 files changed

+68
-41
lines changed

2 files changed

+68
-41
lines changed

site/src/modules/workspaces/DynamicParameter/DynamicParameter.tsx

+54-35
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
TooltipTrigger,
3333
} from "components/Tooltip/Tooltip";
3434
import { Info, Settings, TriangleAlert } from "lucide-react";
35-
import { type FC, useId } from "react";
35+
import { type FC, useEffect, useId, useState } from "react";
3636
import type { AutofillBuildParameter } from "utils/richParameters";
3737
import * as Yup from "yup";
3838

@@ -164,14 +164,18 @@ const ParameterField: FC<ParameterFieldProps> = ({
164164
id,
165165
}) => {
166166
const value = validValue(parameter.value);
167-
const defaultValue = validValue(parameter.default_value);
167+
const [localValue, setLocalValue] = useState(value);
168+
169+
useEffect(() => {
170+
setLocalValue(value);
171+
}, [value]);
168172

169173
switch (parameter.form_type) {
170174
case "dropdown":
171175
return (
172176
<Select
173177
onValueChange={onChange}
174-
defaultValue={defaultValue}
178+
value={value}
175179
disabled={disabled}
176180
required={parameter.required}
177181
>
@@ -194,31 +198,48 @@ const ParameterField: FC<ParameterFieldProps> = ({
194198
);
195199

196200
case "multi-select": {
201+
let values: string[] = [];
202+
203+
if (value) {
204+
try {
205+
const parsed = JSON.parse(value);
206+
if (Array.isArray(parsed)) {
207+
values = parsed;
208+
}
209+
} catch (e) {
210+
console.error(
211+
"Error parsing parameter value with form_type multi-select",
212+
e,
213+
);
214+
}
215+
}
216+
197217
// Map parameter options to MultiSelectCombobox options format
198-
const comboboxOptions: Option[] = parameter.options.map((opt) => ({
218+
const options: Option[] = parameter.options.map((opt) => ({
199219
value: opt.value.value,
200220
label: opt.name,
201221
disable: false,
202222
}));
203223

204-
const defaultOptions: Option[] = JSON.parse(defaultValue).map(
205-
(val: string) => {
206-
const option = parameter.options.find((o) => o.value.value === val);
207-
return {
208-
value: val,
209-
label: option?.name || val,
210-
disable: false,
211-
};
212-
},
224+
const optionMap = new Map(
225+
parameter.options.map((opt) => [opt.value.value, opt.name]),
213226
);
214227

228+
const selectedOptions: Option[] = values.map((val) => {
229+
return {
230+
value: val,
231+
label: optionMap.get(val) || val,
232+
disable: false,
233+
};
234+
});
235+
215236
return (
216237
<MultiSelectCombobox
217238
inputProps={{
218239
id: `${id}-${parameter.name}`,
219240
}}
220-
options={comboboxOptions}
221-
defaultOptions={defaultOptions}
241+
options={options}
242+
defaultOptions={selectedOptions}
222243
onChange={(newValues) => {
223244
const values = newValues.map((option) => option.value);
224245
onChange(JSON.stringify(values));
@@ -251,11 +272,7 @@ const ParameterField: FC<ParameterFieldProps> = ({
251272

252273
case "radio":
253274
return (
254-
<RadioGroup
255-
onValueChange={onChange}
256-
disabled={disabled}
257-
defaultValue={defaultValue}
258-
>
275+
<RadioGroup onValueChange={onChange} disabled={disabled} value={value}>
259276
{parameter.options.map((option) => (
260277
<div
261278
key={option.value.value}
@@ -282,7 +299,6 @@ const ParameterField: FC<ParameterFieldProps> = ({
282299
<Checkbox
283300
id={parameter.name}
284301
checked={value === "true"}
285-
defaultChecked={defaultValue === "true"} // TODO: defaultChecked is always overridden by checked
286302
onCheckedChange={(checked) => {
287303
onChange(checked ? "true" : "false");
288304
}}
@@ -299,14 +315,11 @@ const ParameterField: FC<ParameterFieldProps> = ({
299315
<div className="flex flex-row items-baseline gap-3">
300316
<Slider
301317
className="mt-2"
302-
defaultValue={[
303-
Number(
304-
parameter.default_value.valid
305-
? parameter.default_value.value
306-
: 0,
307-
),
308-
]}
309-
onValueChange={([value]) => onChange(value.toString())}
318+
value={[Number(localValue ?? 0)]}
319+
onValueChange={([value]) => {
320+
setLocalValue(value.toString());
321+
onChange(value.toString());
322+
}}
310323
min={parameter.validations[0]?.validation_min ?? 0}
311324
max={parameter.validations[0]?.validation_max ?? 100}
312325
disabled={disabled}
@@ -319,8 +332,11 @@ const ParameterField: FC<ParameterFieldProps> = ({
319332
return (
320333
<Textarea
321334
className="max-w-2xl"
322-
defaultValue={defaultValue}
323-
onChange={(e) => onChange(e.target.value)}
335+
value={localValue}
336+
onChange={(e) => {
337+
setLocalValue(e.target.value);
338+
onChange(e.target.value);
339+
}}
324340
onInput={(e) => {
325341
const target = e.currentTarget;
326342
target.style.maxHeight = "700px";
@@ -354,8 +370,11 @@ const ParameterField: FC<ParameterFieldProps> = ({
354370
return (
355371
<Input
356372
type={inputType}
357-
defaultValue={defaultValue}
358-
onChange={(e) => onChange(e.target.value)}
373+
value={localValue}
374+
onChange={(e) => {
375+
setLocalValue(e.target.value);
376+
onChange(e.target.value);
377+
}}
359378
disabled={disabled}
360379
required={parameter.required}
361380
placeholder={
@@ -435,7 +454,7 @@ export const getInitialParameterValues = (
435454
if (parameter.ephemeral) {
436455
return {
437456
name: parameter.name,
438-
value: validValue(parameter.default_value),
457+
value: validValue(parameter.value),
439458
};
440459
}
441460

@@ -450,7 +469,7 @@ export const getInitialParameterValues = (
450469
isValidParameterOption(parameter, autofillParam) &&
451470
autofillParam.value
452471
? autofillParam.value
453-
: validValue(parameter.default_value),
472+
: validValue(parameter.value),
454473
};
455474
});
456475
};

site/src/pages/CreateWorkspacePage/CreateWorkspacePageViewExperimental.tsx

+14-6
Original file line numberDiff line numberDiff line change
@@ -213,17 +213,23 @@ export const CreateWorkspacePageViewExperimental: FC<
213213
parameters,
214214
]);
215215

216+
// send the last user modified parameter and all touched parameters to the websocket
216217
const sendDynamicParamsRequest = (
217218
parameter: PreviewParameter,
218219
value: string,
219220
) => {
220-
const formInputs = Object.fromEntries(
221-
form.values.rich_parameter_values?.map((value) => {
222-
return [value.name, value.value];
223-
}) ?? [],
224-
);
225-
// Update the input for the changed parameter
221+
const formInputs: { [k: string]: string } = {};
226222
formInputs[parameter.name] = value;
223+
const parameters = form.values.rich_parameter_values ?? [];
224+
225+
for (const [fieldName, isTouched] of Object.entries(form.touched)) {
226+
if (isTouched && fieldName !== parameter.name) {
227+
const param = parameters.find((p) => p.name === fieldName);
228+
if (param?.value) {
229+
formInputs[fieldName] = param.value;
230+
}
231+
}
232+
}
227233

228234
sendMessage(formInputs);
229235
};
@@ -238,6 +244,7 @@ export const CreateWorkspacePageViewExperimental: FC<
238244
name: parameter.name,
239245
value,
240246
});
247+
form.setFieldTouched(parameter.name, true);
241248
sendDynamicParamsRequest(parameter, value);
242249
},
243250
500,
@@ -255,6 +262,7 @@ export const CreateWorkspacePageViewExperimental: FC<
255262
name: parameter.name,
256263
value,
257264
});
265+
form.setFieldTouched(parameter.name, true);
258266
sendDynamicParamsRequest(parameter, value);
259267
}
260268
};

0 commit comments

Comments
 (0)