Skip to content

Commit 8b96ced

Browse files
miaawongsgalsaleh
andauthored
Add dropdown and radio config item types (#4813)
* Add 'radio' and 'dropdown' config item types (#4811) --------- Co-authored-by: Salah Al Saleh <[email protected]>
1 parent f10a45c commit 8b96ced

File tree

5 files changed

+118
-0
lines changed

5 files changed

+118
-0
lines changed

e2e/playwright/tests/smoke-test/test.spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ test('smoke test', async ({ page }) => {
6161
await page.getByRole('link', { name: 'Config', exact: true }).click();
6262
await expect(page.locator('h3')).toContainText('My Example Config');
6363
await expect(page.locator('#version_sequence-group')).toContainText('This version is 1');
64+
await expect(page.getByRole("combobox")).toHaveValue("option_1");
65+
await page.getByRole("combobox").selectOption("option_2");
66+
await expect(page.getByRole("combobox")).toHaveValue("option_2");
67+
await expect(page.getByLabel("radio_1")).toBeChecked();
68+
await page.getByLabel("radio_2").click();
69+
await expect(page.getByLabel("radio_2")).toBeChecked();
6470
await expect(page.getByRole('button', { name: 'Save config' })).toBeVisible();
6571
await page.getByRole('link', { name: 'Troubleshoot' }).click();
6672
await expect(page.getByRole('button', { name: 'Analyze App Name' })).toBeVisible();

pkg/kotsadmconfig/types/types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const (
1010
PasswordItemType = "password"
1111
TextAreaItemType = "textarea"
1212
SelectOneItemType = "select_one"
13+
RadioItemType = "radio"
14+
DropdownItemType = "dropdown"
1315
)
1416

1517
type ConfigGroupValidationError struct {

pkg/template/config_context.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ func isReadOnly(item kotsv1beta1.ConfigItem) bool {
205205
"select": {},
206206
"select_many": {},
207207
"select_one": {},
208+
"radio": {},
209+
"dropdown": {},
208210
"text": {},
209211
"textarea": {},
210212
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { useState } from "react";
2+
import { ConfigWrapper } from "./ConfigComponents";
3+
import ConfigItemTitle from "./ConfigItemTitle";
4+
import Icon from "@components/Icon";
5+
import Markdown from "react-remarkable";
6+
import { isEmpty } from "lodash";
7+
8+
const ConfigDropdown = (props) => {
9+
const [selectedValue, setSelectedValue] = useState(
10+
props.value || props.default || ""
11+
);
12+
13+
let options = [{ value: "", label: "Select an option" }];
14+
15+
props.items.map((item) => {
16+
if (isEmpty(item)) {
17+
return null;
18+
}
19+
options.push({ value: item.name, label: item.title });
20+
});
21+
22+
const handleChange = (val) => {
23+
setSelectedValue(val);
24+
props.handleOnChange(props.name, val);
25+
};
26+
27+
return (
28+
<ConfigWrapper
29+
id={`${props.name}-group`}
30+
className={`field-type-dropdown`}
31+
marginTop={props.hidden || props.affix ? "0" : "15px"}
32+
hidden={props.hidden}
33+
>
34+
{props.title !== "" ? (
35+
<ConfigItemTitle
36+
title={props.title}
37+
recommended={props.recommended}
38+
required={props.required}
39+
name={props.name}
40+
error={props.error}
41+
/>
42+
) : null}
43+
{props.help_text !== "" ? (
44+
<div className="field-section-help-text help-text-color">
45+
<Markdown
46+
options={{
47+
linkTarget: "_blank",
48+
linkify: true,
49+
}}
50+
>
51+
{props.help_text}
52+
</Markdown>
53+
</div>
54+
) : null}
55+
<select
56+
className="Input tw-mt-4"
57+
value={selectedValue}
58+
onChange={(e) => handleChange(e.target.value)}
59+
>
60+
{options.map((option) => (
61+
<option key={option.value} value={option.value}>
62+
{option.label}
63+
</option>
64+
))}
65+
</select>
66+
{props.repeatable && (
67+
<div
68+
className="u-marginTop--10"
69+
onClick={() => props.handleAddItem(props.name)}
70+
>
71+
<span className="add-btn u-fontSize--small u-fontWeight--bold link">
72+
<Icon icon="plus" size={14} className="clickable" />
73+
Add another {props.title}
74+
</span>
75+
</div>
76+
)}
77+
</ConfigWrapper>
78+
);
79+
};
80+
81+
export default ConfigDropdown;

web/src/components/config_render/ConfigGroup.jsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import ConfigFileInput from "./ConfigFileInput";
1313
import { setOrder } from "./ConfigUtil";
1414
import { ConfigWrapper } from "./ConfigComponents";
1515
import Icon from "../Icon";
16+
import ConfigDropdown from "./ConfigDropdown";
1617

1718
const ConfigGroup = (props) => {
1819
const markdownNode = createRef();
@@ -150,6 +151,19 @@ const ConfigGroup = (props) => {
150151
/>
151152
</ConfigWrapper>
152153
);
154+
case "radio":
155+
return (
156+
<ConfigSelectOne
157+
key={`${i}-${item.name}`}
158+
handleOnChange={handleItemChange}
159+
hidden={item.hidden}
160+
groupName={props.item.name}
161+
when={item.when}
162+
{...item}
163+
readonly={isReadOnly}
164+
index={i + 1}
165+
/>
166+
);
153167
case "select_one":
154168
return (
155169
<ConfigSelectOne
@@ -163,6 +177,19 @@ const ConfigGroup = (props) => {
163177
index={i + 1}
164178
/>
165179
);
180+
case "dropdown":
181+
return (
182+
<ConfigDropdown
183+
key={`${i}-${item.name}`}
184+
handleOnChange={handleItemChange}
185+
hidden={item.hidden}
186+
groupName={props.item.name}
187+
when={item.when}
188+
{...item}
189+
readonly={isReadOnly}
190+
index={i + 1}
191+
/>
192+
);
166193
case "heading":
167194
return (
168195
<div

0 commit comments

Comments
 (0)