Skip to content

Commit 00e1ea4

Browse files
authored
feat: add the ability to hide preset parameters (#17168)
This PR adds the ability to hide presets on the workspace creation form. When showing them, a clear indication is now made as to which inputs were preset and which weren't. ![image](https://github.com/user-attachments/assets/6c8f690c-7cf6-44a9-9657-65039b2b3cb7)
1 parent 88bae05 commit 00e1ea4

File tree

4 files changed

+152
-43
lines changed

4 files changed

+152
-43
lines changed

site/src/components/RichParameterInput/RichParameterInput.stories.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,3 +374,44 @@ export const SmallBasicWithDisplayName: Story = {
374374
size: "small",
375375
},
376376
};
377+
378+
export const WithPreset: Story = {
379+
args: {
380+
value: "preset-value",
381+
id: "project_name",
382+
parameter: createTemplateVersionParameter({
383+
name: "project_name",
384+
description:
385+
"Customize the name of a Google Cloud project that will be created!",
386+
}),
387+
isPreset: true,
388+
},
389+
};
390+
391+
export const WithPresetAndImmutable: Story = {
392+
args: {
393+
value: "preset-value",
394+
id: "project_name",
395+
parameter: createTemplateVersionParameter({
396+
name: "project_name",
397+
description:
398+
"Customize the name of a Google Cloud project that will be created!",
399+
mutable: false,
400+
}),
401+
isPreset: true,
402+
},
403+
};
404+
405+
export const WithPresetAndOptional: Story = {
406+
args: {
407+
value: "preset-value",
408+
id: "project_name",
409+
parameter: createTemplateVersionParameter({
410+
name: "project_name",
411+
description:
412+
"Customize the name of a Google Cloud project that will be created!",
413+
required: false,
414+
}),
415+
isPreset: true,
416+
},
417+
};

site/src/components/RichParameterInput/RichParameterInput.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Interpolation, Theme } from "@emotion/react";
22
import ErrorOutline from "@mui/icons-material/ErrorOutline";
3+
import SettingsIcon from "@mui/icons-material/Settings";
34
import Button from "@mui/material/Button";
45
import FormControlLabel from "@mui/material/FormControlLabel";
56
import FormHelperText from "@mui/material/FormHelperText";
@@ -122,9 +123,10 @@ const styles = {
122123

123124
export interface ParameterLabelProps {
124125
parameter: TemplateVersionParameter;
126+
isPreset?: boolean;
125127
}
126128

127-
const ParameterLabel: FC<ParameterLabelProps> = ({ parameter }) => {
129+
const ParameterLabel: FC<ParameterLabelProps> = ({ parameter, isPreset }) => {
128130
const hasDescription = parameter.description && parameter.description !== "";
129131
const displayName = parameter.display_name
130132
? parameter.display_name
@@ -146,6 +148,13 @@ const ParameterLabel: FC<ParameterLabelProps> = ({ parameter }) => {
146148
</Pill>
147149
</Tooltip>
148150
)}
151+
{isPreset && (
152+
<Tooltip title="This value was set by a preset">
153+
<Pill type="info" icon={<SettingsIcon />}>
154+
Preset
155+
</Pill>
156+
</Tooltip>
157+
)}
149158
</span>
150159
);
151160

@@ -187,6 +196,7 @@ export type RichParameterInputProps = Omit<
187196
parameterAutofill?: AutofillBuildParameter;
188197
onChange: (value: string) => void;
189198
size?: Size;
199+
isPreset?: boolean;
190200
};
191201

192202
const autofillDescription: Partial<Record<AutofillSource, ReactNode>> = {
@@ -198,6 +208,7 @@ export const RichParameterInput: FC<RichParameterInputProps> = ({
198208
parameter,
199209
parameterAutofill,
200210
onChange,
211+
isPreset,
201212
...fieldProps
202213
}) => {
203214
const autofillSource = parameterAutofill?.source;
@@ -211,7 +222,7 @@ export const RichParameterInput: FC<RichParameterInputProps> = ({
211222
className={size}
212223
data-testid={`parameter-field-${parameter.name}`}
213224
>
214-
<ParameterLabel parameter={parameter} />
225+
<ParameterLabel parameter={parameter} isPreset={isPreset} />
215226
<div css={{ display: "flex", flexDirection: "column" }}>
216227
<RichParameterField
217228
{...fieldProps}

site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.stories.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,28 @@ export const PresetSelected: Story = {
159159
},
160160
};
161161

162+
export const PresetSelectedWithHiddenParameters: Story = {
163+
args: PresetsButNoneSelected.args,
164+
play: async ({ canvasElement }) => {
165+
const canvas = within(canvasElement);
166+
// Select a preset
167+
await userEvent.click(canvas.getByLabelText("Preset"));
168+
await userEvent.click(canvas.getByText("Preset 1"));
169+
},
170+
};
171+
172+
export const PresetSelectedWithVisibleParameters: Story = {
173+
args: PresetsButNoneSelected.args,
174+
play: async ({ canvasElement }) => {
175+
const canvas = within(canvasElement);
176+
// Select a preset
177+
await userEvent.click(canvas.getByLabelText("Preset"));
178+
await userEvent.click(canvas.getByText("Preset 1"));
179+
// Toggle off the show preset parameters switch
180+
await userEvent.click(canvas.getByLabelText("Show preset parameters"));
181+
},
182+
};
183+
162184
export const PresetReselected: Story = {
163185
args: PresetsButNoneSelected.args,
164186
play: async ({ canvasElement }) => {

site/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx

Lines changed: 76 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Interpolation, Theme } from "@emotion/react";
2+
import FormControlLabel from "@mui/material/FormControlLabel";
23
import FormHelperText from "@mui/material/FormHelperText";
34
import TextField from "@mui/material/TextField";
45
import type * as TypesGen from "api/typesGenerated";
@@ -24,6 +25,7 @@ import { Pill } from "components/Pill/Pill";
2425
import { RichParameterInput } from "components/RichParameterInput/RichParameterInput";
2526
import { Spinner } from "components/Spinner/Spinner";
2627
import { Stack } from "components/Stack/Stack";
28+
import { Switch } from "components/Switch/Switch";
2729
import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete";
2830
import { type FormikContextType, useFormik } from "formik";
2931
import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName";
@@ -101,6 +103,7 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
101103
const [suggestedName, setSuggestedName] = useState(() =>
102104
generateWorkspaceName(),
103105
);
106+
const [showPresetParameters, setShowPresetParameters] = useState(false);
104107

105108
const rerollSuggestedName = useCallback(() => {
106109
setSuggestedName(() => generateWorkspaceName());
@@ -273,33 +276,6 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
273276
</Stack>
274277
)}
275278

276-
{presets.length > 0 && (
277-
<Stack direction="column" spacing={2}>
278-
<Stack direction="row" spacing={2} alignItems="center">
279-
<span css={styles.description}>
280-
Select a preset to get started
281-
</span>
282-
<FeatureStageBadge contentType={"beta"} size="md" />
283-
</Stack>
284-
<Stack direction="row" spacing={2}>
285-
<SelectFilter
286-
label="Preset"
287-
options={presetOptions}
288-
onSelect={(option) => {
289-
const index = presetOptions.findIndex(
290-
(preset) => preset.value === option?.value,
291-
);
292-
if (index === -1) {
293-
return;
294-
}
295-
setSelectedPresetIndex(index);
296-
}}
297-
placeholder="Select a preset"
298-
selectedOption={presetOptions[selectedPresetIndex]}
299-
/>
300-
</Stack>
301-
</Stack>
302-
)}
303279
<div>
304280
<TextField
305281
{...getFieldHelpers("name")}
@@ -373,30 +349,89 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
373349
hence they require additional vertical spacing for better readability and
374350
user experience. */}
375351
<FormFields css={{ gap: 36 }}>
352+
{presets.length > 0 && (
353+
<Stack direction="column" spacing={2}>
354+
<Stack direction="row" spacing={2} alignItems="center">
355+
<span css={styles.description}>
356+
Select a preset to get started
357+
</span>
358+
<FeatureStageBadge contentType={"beta"} size="md" />
359+
</Stack>
360+
<Stack direction="column" spacing={2}>
361+
<Stack direction="row" spacing={2}>
362+
<SelectFilter
363+
label="Preset"
364+
options={presetOptions}
365+
onSelect={(option) => {
366+
const index = presetOptions.findIndex(
367+
(preset) => preset.value === option?.value,
368+
);
369+
if (index === -1) {
370+
return;
371+
}
372+
setSelectedPresetIndex(index);
373+
}}
374+
placeholder="Select a preset"
375+
selectedOption={presetOptions[selectedPresetIndex]}
376+
/>
377+
</Stack>
378+
<div
379+
css={{
380+
display: "flex",
381+
alignItems: "center",
382+
gap: "8px",
383+
}}
384+
>
385+
<Switch
386+
id="show-preset-parameters"
387+
checked={showPresetParameters}
388+
onCheckedChange={setShowPresetParameters}
389+
/>
390+
<label
391+
htmlFor="show-preset-parameters"
392+
css={styles.description}
393+
>
394+
Show preset parameters
395+
</label>
396+
</div>
397+
</Stack>
398+
</Stack>
399+
)}
400+
376401
{parameters.map((parameter, index) => {
377402
const parameterField = `rich_parameter_values.${index}`;
378403
const parameterInputName = `${parameterField}.value`;
404+
const isPresetParameter = presetParameterNames.includes(
405+
parameter.name,
406+
);
379407
const isDisabled =
380408
disabledParams?.includes(
381409
parameter.name.toLowerCase().replace(/ /g, "_"),
382410
) ||
383411
creatingWorkspace ||
384-
presetParameterNames.includes(parameter.name);
412+
isPresetParameter;
413+
414+
// Hide preset parameters if showPresetParameters is false
415+
if (!showPresetParameters && isPresetParameter) {
416+
return null;
417+
}
385418

386419
return (
387-
<RichParameterInput
388-
{...getFieldHelpers(parameterInputName)}
389-
onChange={async (value) => {
390-
await form.setFieldValue(parameterField, {
391-
name: parameter.name,
392-
value,
393-
});
394-
}}
395-
key={parameter.name}
396-
parameter={parameter}
397-
parameterAutofill={autofillByName[parameter.name]}
398-
disabled={isDisabled}
399-
/>
420+
<div key={parameter.name}>
421+
<RichParameterInput
422+
{...getFieldHelpers(parameterInputName)}
423+
onChange={async (value) => {
424+
await form.setFieldValue(parameterField, {
425+
name: parameter.name,
426+
value,
427+
});
428+
}}
429+
parameter={parameter}
430+
parameterAutofill={autofillByName[parameter.name]}
431+
disabled={isDisabled}
432+
isPreset={isPresetParameter}
433+
/>
434+
</div>
400435
);
401436
})}
402437
</FormFields>

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy