Skip to content

Commit 8804c44

Browse files
authored
feat: form_type and styling metadata arguments added (#375)
Adds `form_type` as a field to parameters. By default, legacy behavior is maintained by deducing the default `form_type` value from the existing inputs. `styling` argument also added for cosmetic usage on the frontend form
1 parent 3a2c18d commit 8804c44

File tree

5 files changed

+664
-25
lines changed

5 files changed

+664
-25
lines changed

docs/data-sources/parameter.md

+2
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,12 @@ data "coder_parameter" "home_volume_size" {
145145
- `description` (String) Describe what this parameter does.
146146
- `display_name` (String) The displayed name of the parameter as it will appear in the interface.
147147
- `ephemeral` (Boolean) The value of an ephemeral parameter will not be preserved between consecutive workspace builds.
148+
- `form_type` (String) The type of this parameter. Must be one of: [radio, slider, input, dropdown, checkbox, switch, multi-select, tag-select, textarea, error].
148149
- `icon` (String) A URL to an icon that will display in the dashboard. View built-in icons [here](https://github.com/coder/coder/tree/main/site/static/icon). Use a built-in icon with `"${data.coder_workspace.me.access_url}/icon/<path>"`.
149150
- `mutable` (Boolean) Whether this value can be changed after workspace creation. This can be destructive for values like region, so use with caution!
150151
- `option` (Block List) Each `option` block defines a value for a user to select from. (see [below for nested schema](#nestedblock--option))
151152
- `order` (Number) The order determines the position of a template parameter in the UI/CLI presentation. The lowest order is shown first and parameters with equal order are sorted by name (ascending order).
153+
- `styling` (String) JSON encoded string containing the metadata for controlling the appearance of this parameter in the UI. This option is purely cosmetic and does not affect the function of the parameter in terraform.
152154
- `type` (String) The type of this parameter. Must be one of: `"number"`, `"string"`, `"bool"`, or `"list(string)"`.
153155
- `validation` (Block List, Max: 1) Validate the input of a parameter. (see [below for nested schema](#nestedblock--validation))
154156

provider/formtype.go

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package provider
2+
3+
import (
4+
"slices"
5+
6+
"golang.org/x/xerrors"
7+
)
8+
9+
// OptionType is a type of option that can be used in the 'type' argument of
10+
// a parameter. These should match types as defined in terraform:
11+
//
12+
// https://developer.hashicorp.com/terraform/language/expressions/types
13+
//
14+
// The value have to be string literals, as type constraint keywords are not
15+
// supported in providers.
16+
type OptionType string
17+
18+
const (
19+
OptionTypeString OptionType = "string"
20+
OptionTypeNumber OptionType = "number"
21+
OptionTypeBoolean OptionType = "bool"
22+
OptionTypeListString OptionType = "list(string)"
23+
)
24+
25+
func OptionTypes() []OptionType {
26+
return []OptionType{
27+
OptionTypeString,
28+
OptionTypeNumber,
29+
OptionTypeBoolean,
30+
OptionTypeListString,
31+
}
32+
}
33+
34+
// ParameterFormType is the list of supported form types for display in
35+
// the Coder "create workspace" form. These form types are functional as well
36+
// as cosmetic. Refer to `formTypeTruthTable` for the allowed pairings.
37+
// For example, "multi-select" has the type "list(string)" but the option
38+
// values are "string".
39+
type ParameterFormType string
40+
41+
const (
42+
ParameterFormTypeDefault ParameterFormType = ""
43+
ParameterFormTypeRadio ParameterFormType = "radio"
44+
ParameterFormTypeSlider ParameterFormType = "slider"
45+
ParameterFormTypeInput ParameterFormType = "input"
46+
ParameterFormTypeDropdown ParameterFormType = "dropdown"
47+
ParameterFormTypeCheckbox ParameterFormType = "checkbox"
48+
ParameterFormTypeSwitch ParameterFormType = "switch"
49+
ParameterFormTypeMultiSelect ParameterFormType = "multi-select"
50+
ParameterFormTypeTagSelect ParameterFormType = "tag-select"
51+
ParameterFormTypeTextArea ParameterFormType = "textarea"
52+
ParameterFormTypeError ParameterFormType = "error"
53+
)
54+
55+
// ParameterFormTypes should be kept in sync with the enum list above.
56+
func ParameterFormTypes() []ParameterFormType {
57+
return []ParameterFormType{
58+
// Intentionally omit "ParameterFormTypeDefault" from this set.
59+
// It is a valid enum, but will always be mapped to a real value when
60+
// being used.
61+
ParameterFormTypeRadio,
62+
ParameterFormTypeSlider,
63+
ParameterFormTypeInput,
64+
ParameterFormTypeDropdown,
65+
ParameterFormTypeCheckbox,
66+
ParameterFormTypeSwitch,
67+
ParameterFormTypeMultiSelect,
68+
ParameterFormTypeTagSelect,
69+
ParameterFormTypeTextArea,
70+
ParameterFormTypeError,
71+
}
72+
}
73+
74+
// formTypeTruthTable is a map of [`type`][`optionCount` > 0] to `form_type`.
75+
// The first value in the slice is the default value assuming `form_type` is
76+
// not specified.
77+
//
78+
// The boolean key indicates whether the `options` field is specified.
79+
// | Type | Options | Specified Form Type | form_type | Notes |
80+
// |-------------------|---------|---------------------|----------------|--------------------------------|
81+
// | `string` `number` | Y | | `radio` | |
82+
// | `string` `number` | Y | `dropdown` | `dropdown` | |
83+
// | `string` `number` | N | | `input` | |
84+
// | `string` | N | 'textarea' | `textarea` | |
85+
// | `number` | N | 'slider' | `slider` | min/max validation |
86+
// | `bool` | Y | | `radio` | |
87+
// | `bool` | N | | `checkbox` | |
88+
// | `bool` | N | `switch` | `switch` | |
89+
// | `list(string)` | Y | | `radio` | |
90+
// | `list(string)` | N | | `tag-select` | |
91+
// | `list(string)` | Y | `multi-select` | `multi-select` | Option values will be `string` |
92+
var formTypeTruthTable = map[OptionType]map[bool][]ParameterFormType{
93+
OptionTypeString: {
94+
true: {ParameterFormTypeRadio, ParameterFormTypeDropdown},
95+
false: {ParameterFormTypeInput, ParameterFormTypeTextArea},
96+
},
97+
OptionTypeNumber: {
98+
true: {ParameterFormTypeRadio, ParameterFormTypeDropdown},
99+
false: {ParameterFormTypeInput, ParameterFormTypeSlider},
100+
},
101+
OptionTypeBoolean: {
102+
true: {ParameterFormTypeRadio},
103+
false: {ParameterFormTypeCheckbox, ParameterFormTypeSwitch},
104+
},
105+
OptionTypeListString: {
106+
true: {ParameterFormTypeRadio, ParameterFormTypeMultiSelect},
107+
false: {ParameterFormTypeTagSelect},
108+
},
109+
}
110+
111+
// ValidateFormType handles the truth table for the valid set of `type` and
112+
// `form_type` options.
113+
// The OptionType is also returned because it is possible the 'type' of the
114+
// 'value' & 'default' fields is different from the 'type' of the options.
115+
// The use case is when using multi-select. The options are 'string' and the
116+
// value is 'list(string)'.
117+
func ValidateFormType(paramType OptionType, optionCount int, specifiedFormType ParameterFormType) (OptionType, ParameterFormType, error) {
118+
allowed, ok := formTypeTruthTable[paramType][optionCount > 0]
119+
if !ok || len(allowed) == 0 {
120+
return paramType, specifiedFormType, xerrors.Errorf("value type %q is not supported for 'form_types'", paramType)
121+
}
122+
123+
if specifiedFormType == ParameterFormTypeDefault {
124+
// handle the default case
125+
specifiedFormType = allowed[0]
126+
}
127+
128+
if !slices.Contains(allowed, specifiedFormType) {
129+
return paramType, specifiedFormType, xerrors.Errorf("value type %q is not supported for 'form_types'", specifiedFormType)
130+
}
131+
132+
// This is the only current special case. If 'multi-select' is selected, the type
133+
// of 'value' and an options 'value' are different. The type of the parameter is
134+
// `list(string)` but the type of the individual options is `string`.
135+
if paramType == OptionTypeListString && specifiedFormType == ParameterFormTypeMultiSelect {
136+
return OptionTypeString, ParameterFormTypeMultiSelect, nil
137+
}
138+
139+
return paramType, specifiedFormType, nil
140+
}
141+
142+
func toStrings[A ~string](l []A) []string {
143+
var r []string
144+
for _, v := range l {
145+
r = append(r, string(v))
146+
}
147+
return r
148+
}

0 commit comments

Comments
 (0)