From 28eaa2652118a172b02e103b43b156b3e788aa78 Mon Sep 17 00:00:00 2001 From: Steven Crespo Date: Thu, 10 Jul 2025 16:52:52 -0700 Subject: [PATCH 1/5] add checkbox and radio components --- web/src/components/common/Checkbox.tsx | 58 +++++++++++++++ web/src/components/common/Radio.tsx | 70 +++++++++++++++++++ .../wizard/config/ConfigurationStep.tsx | 64 ++++++----------- 3 files changed, 148 insertions(+), 44 deletions(-) create mode 100644 web/src/components/common/Checkbox.tsx create mode 100644 web/src/components/common/Radio.tsx diff --git a/web/src/components/common/Checkbox.tsx b/web/src/components/common/Checkbox.tsx new file mode 100644 index 000000000..4c027d53d --- /dev/null +++ b/web/src/components/common/Checkbox.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { useSettings } from '../../contexts/SettingsContext'; + +interface CheckboxProps { + id: string; + label: string; + checked: boolean; + onChange: (e: React.ChangeEvent) => void; + disabled?: boolean; + error?: string; + helpText?: string; + className?: string; + labelClassName?: string; + dataTestId?: string; +} + +const Checkbox: React.FC = ({ + id, + label, + checked, + onChange, + disabled = false, + error, + helpText, + className = '', + labelClassName = '', + dataTestId, +}) => { + const { settings } = useSettings(); + const themeColor = settings.themeColor; + + return ( +
+
+ + +
+ {error &&

{error}

} + {helpText && !error &&

{helpText}

} +
+ ); +}; + +export default Checkbox; diff --git a/web/src/components/common/Radio.tsx b/web/src/components/common/Radio.tsx new file mode 100644 index 000000000..b7deb7def --- /dev/null +++ b/web/src/components/common/Radio.tsx @@ -0,0 +1,70 @@ +import React from 'react'; +import { useSettings } from '../../contexts/SettingsContext'; +import { AppConfigChildItem } from '../../types'; + +interface RadioProps { + id: string; + label: string; + value: string; + onChange: (e: React.ChangeEvent) => void; + options: AppConfigChildItem[]; + disabled?: boolean; + error?: string; + helpText?: string; + className?: string; + labelClassName?: string; + dataTestId?: string; +} + +const Radio: React.FC = ({ + id, + label, + value, + onChange, + options, + disabled = false, + error, + helpText, + className = '', + labelClassName = '', + dataTestId, +}) => { + const { settings } = useSettings(); + const themeColor = settings.themeColor; + + return ( +
+ +
+ {options.map(option => ( +
+ + +
+ ))} +
+ {error &&

{error}

} + {helpText && !error &&

{helpText}

} +
+ ); +}; + +export default Radio; diff --git a/web/src/components/wizard/config/ConfigurationStep.tsx b/web/src/components/wizard/config/ConfigurationStep.tsx index 07301883b..66f829fe6 100644 --- a/web/src/components/wizard/config/ConfigurationStep.tsx +++ b/web/src/components/wizard/config/ConfigurationStep.tsx @@ -4,6 +4,8 @@ import Card from '../../common/Card'; import Button from '../../common/Button'; import Input from '../../common/Input'; import Textarea from '../../common/Textarea'; +import Checkbox from '../../common/Checkbox'; +import Radio from '../../common/Radio'; import { useWizard } from '../../../contexts/WizardModeContext'; import { useAuth } from '../../../contexts/AuthContext'; import { useSettings } from '../../../contexts/SettingsContext'; @@ -182,60 +184,34 @@ const ConfigurationStep: React.FC = ({ onNext }) => { value={getDisplayValue(item)} onChange={handleInputChange} dataTestId={`textarea-input-${item.name}`} + helpText={item.help_text} /> ); case 'bool': return ( -
- - -
+ ); case 'radio': if (item.items) { return ( -
- -
- {item.items.map(child => ( -
- handleRadioChange(item.name, e)} - className="h-4 w-4 focus:ring-offset-2 border-gray-300" - data-testid={`radio-input-${child.name}`} - style={{ - color: themeColor, - '--tw-ring-color': themeColor, - } as React.CSSProperties} - /> - -
- ))} -
-
+ handleRadioChange(item.name, e)} + options={item.items} + dataTestId={`radio-input-${item.name}`} + helpText={item.help_text} + /> ); } return null; From 45cd8c522bdaac37d581993ca5aa3ec1f1743a3c Mon Sep 17 00:00:00 2001 From: Steven Crespo Date: Fri, 11 Jul 2025 08:38:24 -0700 Subject: [PATCH 2/5] f --- web/src/components/common/ConfigItem.tsx | 52 +++++++++++++++++++ .../wizard/config/ConfigurationStep.tsx | 24 ++++----- 2 files changed, 63 insertions(+), 13 deletions(-) create mode 100644 web/src/components/common/ConfigItem.tsx diff --git a/web/src/components/common/ConfigItem.tsx b/web/src/components/common/ConfigItem.tsx new file mode 100644 index 000000000..ab74da9db --- /dev/null +++ b/web/src/components/common/ConfigItem.tsx @@ -0,0 +1,52 @@ +import React from 'react'; + +interface ConfigItemProps { + id: string; + label: string; + dataTestId?: string; + helpText?: string; + children: React.ReactElement; +} + +const ConfigItem: React.FC = ({ + id, + label, + dataTestId, + helpText, + children, +}) => { + // Clone the child element and inject the common props + const enhancedChild = React.cloneElement(children, { + id, + label, + dataTestId, + helpText, + } as any); + + return ( +
+ {enhancedChild} +
+ ); +}; + +// Helper function to create a ConfigItem-wrapped component +export const withConfigItem =

( + WrappedComponent: React.ComponentType

+) => { + return React.forwardRef>((props, ref) => { + const { id, label, dataTestId, helpText, ...wrappedComponentProps } = props; + return ( + + + + ); + }); +}; + +export default ConfigItem; diff --git a/web/src/components/wizard/config/ConfigurationStep.tsx b/web/src/components/wizard/config/ConfigurationStep.tsx index 66f829fe6..ca0993740 100644 --- a/web/src/components/wizard/config/ConfigurationStep.tsx +++ b/web/src/components/wizard/config/ConfigurationStep.tsx @@ -163,40 +163,40 @@ const ConfigurationStep: React.FC = ({ onNext }) => { }; const renderConfigItem = (item: AppConfigItem) => { + const sharedProps = { + id: item.name, + label: item.title, + helpText: item.help_text, + } + switch (item.type) { case 'text': return ( ); case 'textarea': return (