Skip to content

Commit 28eaa26

Browse files
committed
add checkbox and radio components
1 parent b25fee6 commit 28eaa26

File tree

3 files changed

+148
-44
lines changed

3 files changed

+148
-44
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import React from 'react';
2+
import { useSettings } from '../../contexts/SettingsContext';
3+
4+
interface CheckboxProps {
5+
id: string;
6+
label: string;
7+
checked: boolean;
8+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
9+
disabled?: boolean;
10+
error?: string;
11+
helpText?: string;
12+
className?: string;
13+
labelClassName?: string;
14+
dataTestId?: string;
15+
}
16+
17+
const Checkbox: React.FC<CheckboxProps> = ({
18+
id,
19+
label,
20+
checked,
21+
onChange,
22+
disabled = false,
23+
error,
24+
helpText,
25+
className = '',
26+
labelClassName = '',
27+
dataTestId,
28+
}) => {
29+
const { settings } = useSettings();
30+
const themeColor = settings.themeColor;
31+
32+
return (
33+
<div className="mb-4">
34+
<div className="flex items-center space-x-3">
35+
<input
36+
id={id}
37+
type="checkbox"
38+
checked={checked}
39+
onChange={onChange}
40+
disabled={disabled}
41+
className={`h-4 w-4 focus:ring-offset-2 border-gray-300 rounded ${className}`}
42+
data-testid={dataTestId}
43+
style={{
44+
color: themeColor,
45+
'--tw-ring-color': themeColor,
46+
} as React.CSSProperties}
47+
/>
48+
<label htmlFor={id} className={`text-sm text-gray-700 ${labelClassName}`}>
49+
{label}
50+
</label>
51+
</div>
52+
{error && <p className="mt-1 text-sm text-red-500">{error}</p>}
53+
{helpText && !error && <p className="mt-1 text-sm text-gray-500">{helpText}</p>}
54+
</div>
55+
);
56+
};
57+
58+
export default Checkbox;

web/src/components/common/Radio.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import React from 'react';
2+
import { useSettings } from '../../contexts/SettingsContext';
3+
import { AppConfigChildItem } from '../../types';
4+
5+
interface RadioProps {
6+
id: string;
7+
label: string;
8+
value: string;
9+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
10+
options: AppConfigChildItem[];
11+
disabled?: boolean;
12+
error?: string;
13+
helpText?: string;
14+
className?: string;
15+
labelClassName?: string;
16+
dataTestId?: string;
17+
}
18+
19+
const Radio: React.FC<RadioProps> = ({
20+
id,
21+
label,
22+
value,
23+
onChange,
24+
options,
25+
disabled = false,
26+
error,
27+
helpText,
28+
className = '',
29+
labelClassName = '',
30+
dataTestId,
31+
}) => {
32+
const { settings } = useSettings();
33+
const themeColor = settings.themeColor;
34+
35+
return (
36+
<div className="mb-4">
37+
<label className={`block text-sm font-medium text-gray-700 mb-2 ${labelClassName}`}>
38+
{label}
39+
</label>
40+
<div className="space-y-2">
41+
{options.map(option => (
42+
<div key={option.name} className="flex items-center">
43+
<input
44+
type="radio"
45+
id={option.name}
46+
name={id}
47+
value={option.name}
48+
checked={value === option.name}
49+
onChange={onChange}
50+
disabled={disabled}
51+
className={`h-4 w-4 focus:ring-offset-2 border-gray-300 ${className}`}
52+
data-testid={dataTestId ? `${dataTestId}-${option.name}` : undefined}
53+
style={{
54+
color: themeColor,
55+
'--tw-ring-color': themeColor,
56+
} as React.CSSProperties}
57+
/>
58+
<label htmlFor={option.name} className="ml-3 text-sm text-gray-700">
59+
{option.title}
60+
</label>
61+
</div>
62+
))}
63+
</div>
64+
{error && <p className="mt-1 text-sm text-red-500">{error}</p>}
65+
{helpText && !error && <p className="mt-1 text-sm text-gray-500">{helpText}</p>}
66+
</div>
67+
);
68+
};
69+
70+
export default Radio;

web/src/components/wizard/config/ConfigurationStep.tsx

Lines changed: 20 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import Card from '../../common/Card';
44
import Button from '../../common/Button';
55
import Input from '../../common/Input';
66
import Textarea from '../../common/Textarea';
7+
import Checkbox from '../../common/Checkbox';
8+
import Radio from '../../common/Radio';
79
import { useWizard } from '../../../contexts/WizardModeContext';
810
import { useAuth } from '../../../contexts/AuthContext';
911
import { useSettings } from '../../../contexts/SettingsContext';
@@ -182,60 +184,34 @@ const ConfigurationStep: React.FC<ConfigurationStepProps> = ({ onNext }) => {
182184
value={getDisplayValue(item)}
183185
onChange={handleInputChange}
184186
dataTestId={`textarea-input-${item.name}`}
187+
helpText={item.help_text}
185188
/>
186189
);
187190

188191
case 'bool':
189192
return (
190-
<div className="flex items-center space-x-3">
191-
<input
192-
id={item.name}
193-
type="checkbox"
194-
checked={getEffectiveValue(item) === '1'}
195-
onChange={handleCheckboxChange}
196-
className="h-4 w-4 focus:ring-offset-2 border-gray-300 rounded"
197-
data-testid={`bool-input-${item.name}`}
198-
style={{
199-
color: themeColor,
200-
'--tw-ring-color': themeColor,
201-
} as React.CSSProperties}
202-
/>
203-
<label htmlFor={item.name} className="text-sm text-gray-700">
204-
{item.title}
205-
</label>
206-
</div>
193+
<Checkbox
194+
id={item.name}
195+
label={item.title}
196+
checked={getEffectiveValue(item) === '1'}
197+
onChange={handleCheckboxChange}
198+
dataTestId={`bool-input-${item.name}`}
199+
helpText={item.help_text}
200+
/>
207201
);
208202

209203
case 'radio':
210204
if (item.items) {
211205
return (
212-
<div className="space-y-2">
213-
<label className="block text-sm font-medium text-gray-700">
214-
{item.title}
215-
</label>
216-
<div className="space-y-2">
217-
{item.items.map(child => (
218-
<div key={child.name} className="flex items-center">
219-
<input
220-
type="radio"
221-
id={child.name}
222-
value={child.name}
223-
checked={getEffectiveValue(item) === child.name}
224-
onChange={e => handleRadioChange(item.name, e)}
225-
className="h-4 w-4 focus:ring-offset-2 border-gray-300"
226-
data-testid={`radio-input-${child.name}`}
227-
style={{
228-
color: themeColor,
229-
'--tw-ring-color': themeColor,
230-
} as React.CSSProperties}
231-
/>
232-
<label htmlFor={child.name} className="ml-3 text-sm text-gray-700">
233-
{child.title}
234-
</label>
235-
</div>
236-
))}
237-
</div>
238-
</div>
206+
<Radio
207+
id={item.name}
208+
label={item.title}
209+
value={getEffectiveValue(item)}
210+
onChange={e => handleRadioChange(item.name, e)}
211+
options={item.items}
212+
dataTestId={`radio-input-${item.name}`}
213+
helpText={item.help_text}
214+
/>
239215
);
240216
}
241217
return null;

0 commit comments

Comments
 (0)