diff --git a/docs/component-adapter/component-inventory.md b/docs/component-adapter/component-inventory.md index c5ef44b7d..a1bc364ef 100644 --- a/docs/component-adapter/component-inventory.md +++ b/docs/component-adapter/component-inventory.md @@ -245,7 +245,7 @@ | **onChange** | `(value: string) => void` | No | Callback when selection changes | | **onBlur** | `() => void` | No | Handler for blur events | | **options** | [ComboBoxOption](#comboboxoption)[] | Yes | Array of options to display in the dropdown | -| **value** | `string` | No | Currently selected value | +| **value** | `null \| string` | No | Currently selected value | | **inputRef** | `Ref` | No | React ref for the combo box input element | | **allowsCustomValue** | `boolean` | No | Allows the user to type any value, not just options in the list. The options list becomes a suggestion helper rather than a strict constraint. | | **description** | `React.ReactNode` | No | Optional description text for the field | @@ -482,7 +482,7 @@ type PaginationItemsPerPage = 5 | 10 | 50 | **isInvalid** | `boolean` | No | Indicates that the field has an error | | **isDisabled** | `boolean` | No | Disables all radio options in the group | | **options** | [RadioGroupOption](#radiogroupoption)[] | Yes | Array of radio options to display | -| **value** | `string` | No | Currently selected value | +| **value** | `null \| string` | No | Currently selected value | | **defaultValue** | `string` | No | Initially selected value | | **onChange** | `(value: string) => void` | No | Callback when selection changes | | **inputRef** | `Ref` | No | React ref for the first radio input element | @@ -532,7 +532,7 @@ type PaginationItemsPerPage = 5 | 10 | 50 | **onBlur** | `() => void` | No | Handler for blur events | | **options** | [SelectOption](#selectoption)[] | Yes | Array of options to display in the select dropdown | | **placeholder** | `string` | No | Placeholder text when no option is selected | -| **value** | `string` | No | Currently selected value | +| **value** | `null \| string` | No | Currently selected value | | **inputRef** | `Ref` | No | React ref for the select button element | | **portalContainer** | `HTMLElement` | No | Element to use as the portal container | | **description** | `React.ReactNode` | No | Optional description text for the field | diff --git a/src/components/Common/Fields/hooks/useStringifyGenericFieldValue.test.tsx b/src/components/Common/Fields/hooks/useStringifyGenericFieldValue.test.tsx index 1adafc955..3094380ec 100644 --- a/src/components/Common/Fields/hooks/useStringifyGenericFieldValue.test.tsx +++ b/src/components/Common/Fields/hooks/useStringifyGenericFieldValue.test.tsx @@ -61,7 +61,7 @@ describe('useStringifyGenericFieldValue', () => { }), ) - expect(result.current.value).toBeUndefined() + expect(result.current.value).toBeNull() }) test('should use custom convertValueToString function', () => { @@ -137,7 +137,7 @@ describe('useStringifyGenericFieldValueArray', () => { }), ) - expect(result.current.value).toBeUndefined() + expect(result.current.value).toEqual([]) }) test('should use custom convertValueToString function with arrays', () => { diff --git a/src/components/Common/Fields/hooks/useStringifyGenericFieldValue.ts b/src/components/Common/Fields/hooks/useStringifyGenericFieldValue.ts index d2012fafa..a3218c9e2 100644 --- a/src/components/Common/Fields/hooks/useStringifyGenericFieldValue.ts +++ b/src/components/Common/Fields/hooks/useStringifyGenericFieldValue.ts @@ -65,7 +65,7 @@ export function useStringifyGenericFieldValue({ ) const stringValue = - value === null || typeof value === 'undefined' ? undefined : convertValueToString(value) + value === null || typeof value === 'undefined' ? null : convertValueToString(value) return { options: optionsWithStringValues, @@ -109,7 +109,7 @@ export function useStringifyGenericFieldValueArray({ [optionValuesMap, onChange], ) - const stringValue = value ? value.map(convertValueToString) : value + const stringValue = value ? value.map(convertValueToString) : [] return { options: optionsWithStringValues, diff --git a/src/components/Common/UI/ComboBox/ComboBox.tsx b/src/components/Common/UI/ComboBox/ComboBox.tsx index a36459869..6fcdfd78d 100644 --- a/src/components/Common/UI/ComboBox/ComboBox.tsx +++ b/src/components/Common/UI/ComboBox/ComboBox.tsx @@ -79,7 +79,7 @@ export const ComboBox = ({ onInputChange: (inputVal: string) => onChange?.(inputVal), } : { - selectedKey: value ? (value as Key) : undefined, + selectedKey: value ? (value as Key) : null, onSelectionChange: (key: Key | null) => { if (key) onChange?.(key.toString()) }, diff --git a/src/components/Common/UI/ComboBox/ComboBoxTypes.ts b/src/components/Common/UI/ComboBox/ComboBoxTypes.ts index f5a4e4b31..8a65f4249 100644 --- a/src/components/Common/UI/ComboBox/ComboBoxTypes.ts +++ b/src/components/Common/UI/ComboBox/ComboBoxTypes.ts @@ -43,7 +43,7 @@ export interface ComboBoxProps /** * Currently selected value */ - value?: string + value?: string | null /** * React ref for the combo box input element */ diff --git a/src/components/Common/UI/Input/Input.tsx b/src/components/Common/UI/Input/Input.tsx index 43f1b6feb..bbafb1cc8 100644 --- a/src/components/Common/UI/Input/Input.tsx +++ b/src/components/Common/UI/Input/Input.tsx @@ -17,6 +17,7 @@ export function Input(rawProps: InputProps) { 'aria-invalid': ariaInvalid, ...otherProps } = resolvedProps + return (
diff --git a/src/components/Common/UI/Select/SelectTypes.ts b/src/components/Common/UI/Select/SelectTypes.ts index 1ef13c6e5..b8b89a21c 100644 --- a/src/components/Common/UI/Select/SelectTypes.ts +++ b/src/components/Common/UI/Select/SelectTypes.ts @@ -47,7 +47,7 @@ export interface SelectProps /** * Currently selected value */ - value?: string + value?: string | null /** * React ref for the select button element */ diff --git a/src/components/Common/UI/TextArea/TextArea.tsx b/src/components/Common/UI/TextArea/TextArea.tsx index 77d7305e1..e353116c0 100644 --- a/src/components/Common/UI/TextArea/TextArea.tsx +++ b/src/components/Common/UI/TextArea/TextArea.tsx @@ -63,7 +63,7 @@ export function TextArea(rawProps: TextAreaProps) { id={inputId} ref={inputRef} name={name} - value={value} + value={value ?? ''} placeholder={placeholder} rows={rows} cols={cols} diff --git a/src/components/Common/UI/TextInput/TextInput.test.tsx b/src/components/Common/UI/TextInput/TextInput.test.tsx index 482733b81..65f5ca9fe 100644 --- a/src/components/Common/UI/TextInput/TextInput.test.tsx +++ b/src/components/Common/UI/TextInput/TextInput.test.tsx @@ -52,6 +52,17 @@ describe('TextInput', () => { expect(input).toHaveAttribute('max', '100') }) + it('defaults to empty string when value is undefined to stay controlled', () => { + const { rerender } = renderWithProviders( + {}} />, + ) + const input = screen.getByRole('textbox') + expect(input).toHaveValue('') + + rerender( {}} />) + expect(input).toHaveValue('hello') + }) + describe('Accessibility', () => { const testCases = [ { diff --git a/src/components/Common/UI/TextInput/TextInput.tsx b/src/components/Common/UI/TextInput/TextInput.tsx index b189eb6fd..c5254672a 100644 --- a/src/components/Common/UI/TextInput/TextInput.tsx +++ b/src/components/Common/UI/TextInput/TextInput.tsx @@ -63,7 +63,7 @@ export function TextInput(rawProps: TextInputProps) { inputRef={inputRef} name={name} type={type} - value={value} + value={value ?? ''} placeholder={placeholder} onChange={handleChange} onBlur={onBlur}