@bolttech/molecules-input-combo
v0.1.1
Published
A composite form field that combines a dropdown selector with a text input in a single container. Ideal for phone numbers with country codes, names with title prefixes, addresses with country selectors, or monetary values with currency pickers.
Maintainers
Keywords
Readme
InputCombo Component
A composite form field that combines a dropdown selector with a text input in a single container. Ideal for phone numbers with country codes, names with title prefixes, addresses with country selectors, or monetary values with currency pickers.
Installation
npm install @bolttech/frontend-foundations @bolttech/molecules-input-comboor
yarn add @bolttech/frontend-foundations @bolttech/molecules-input-comboProps
| Prop | Type | Default | Description |
| ---------------------- | --------------------------------------------- | -------------------- | -------------------------------------------------------------- |
| id | string | 'input-combo-id' | Unique identifier for the component. |
| dataTestId | string | 'input-combo-test-id' | The data-testid attribute for testing. |
| variant | 'grey' \| 'border' | 'grey' | Visual variant of the component. |
| label | string | — | Label text for the text input. |
| placeholder | string | — | Placeholder for the text input. |
| required | boolean | — | Whether the field is required (shows asterisk). |
| disabled | boolean | — | Disables the entire component. |
| errorMessage | string | — | Error message — triggers error state when set. |
| helperMessage | string | — | Helper text displayed below the field. |
| value | string | — | Current value of the text input. |
| dropdownValue | string | — | Currently selected dropdown option id. |
| dropdownOptions | DropdownOption[] | (required) | Options for the dropdown prefix selector. |
| dropdownLabel | string | '' | Accessible label for the dropdown (visually hidden). |
| dropdownPlaceholder | string | — | Placeholder for the dropdown search input. |
| dropdownDisableSearch | boolean | true | Disables search/typing in the dropdown. |
| fullWidth | boolean | — | Whether the component takes the full width of its container. |
| icon | string | — | Icon name (Material Symbols Sharp). Defaults to 'info' when tooltip is provided. |
| usePortal | boolean | — | Render dropdown options in a portal (useful inside overflow containers). |
| tooltip | { text: ReactNode; variant: string; size: string } | — | Tooltip configuration for the info icon. |
| onChange | (combo: ComboValue) => void | — | Unified callback — returns { id, label, value } with dropdown and input data. |
| onDropdownChange | (option: DropdownOption) => void | — | Callback when dropdown selection changes. |
| onBlur | (evt: FocusEvent<HTMLInputElement>) => void | — | Callback when input loses focus. |
| onFocus | (evt: FocusEvent<HTMLInputElement>) => void | — | Callback when input gains focus. |
DropdownOption
type DropdownOption = {
label: string;
value: string;
id: string;
};ComboValue
type ComboValue = {
/** Value provided by the dropdown selected option id */
id: string;
/** Unified value from dropdown and input — e.g. "+55 999888777" */
label: string;
/** Value from typed input */
value: string;
};Usage
import React, { useState } from 'react';
import { InputCombo } from '@bolttech/molecules-input-combo';
import { bolttechTheme, BolttechThemeProvider } from '@bolttech/frontend-foundations';
const phoneOptions = [
{ label: '+1', value: '+1', id: 'phone-1' },
{ label: '+44', value: '+44', id: 'phone-44' },
{ label: '+55', value: '+55', id: 'phone-55' },
];
const PhoneInput = () => {
const [inputValue, setInputValue] = useState('');
const [dropdownId, setDropdownId] = useState('phone-1');
return (
<BolttechThemeProvider theme={bolttechTheme}>
<InputCombo
variant="grey"
label="Phone number"
placeholder="Enter your phone number"
required
helperMessage="Include area code"
dropdownOptions={phoneOptions}
dropdownValue={dropdownId}
value={inputValue}
onChange={(combo) => setInputValue(combo.value)}
onDropdownChange={(opt) => setDropdownId(opt.id)}
tooltip={{ text: 'International format', variant: 'light', size: 'short' }}
/>
</BolttechThemeProvider>
);
};
export default PhoneInput;Form Engine Integration
The component integrates with @bolttech/form-engine using schema-based configuration. Define the component in a form schema with component: 'inputcombo' and the engine handles value binding, validation, and error display automatically.
Schema example
import { Form, useForm } from '@bolttech/form-engine';
const phoneOptions = [
{ label: '+1', value: '+1', id: 'phone-1' },
{ label: '+55', value: '+55', id: 'phone-55' },
];
const MyForm = () => {
useForm({
index: 'DEFAULT_ID',
onData: ({ data }) => {
console.log('Form data:', data);
},
});
return (
<Form
index="DEFAULT_ID"
schema={{
index: 'DEFAULT_ID',
components: [
{
name: 'input-combo-phone',
component: 'inputcombo',
props: {
variant: 'grey',
label: 'Phone number',
placeholder: 'Enter your phone number',
required: true,
helperMessage: 'Include area code',
dropdownOptions: phoneOptions,
dropdownValue: 'phone-55',
},
filters: {
onlyNumbers: true,
},
validations: {
methods: {
required: true,
},
eventMessages: {
ON_FIELD_BLUR: ['required'],
ON_FIELD_CHANGE: ['required'],
},
messages: {
required: 'Phone number is required',
},
},
},
],
}}
/>
);
};Key points
- The
valueChangeEventreturns both the raw value (_value) and the full event metadata (_metadata), giving the form engine access to the dropdown selection alongside the input text. - Use
useFormwithonDatato observe form state changes in real time. - Schema-level
filters(e.g.onlyNumbers: true) can be applied to restrict input. - Validations are triggered on configurable events (
ON_FIELD_BLUR,ON_FIELD_CHANGE,ON_FIELD_MOUNT).
Variants
Grey (default)
Filled background with no visible border in the default state. Border appears on focus and error states.
Border
White background with a subtle border in all states.
States
| State | Behavior | | ------------- | ------------------------------------------------------------------------ | | Default | Label and placeholder visible, dropdown shows selected option. | | Filled | Label color changes to subtle, input shows the entered value. | | Focus | Container border changes to focus color. | | Error | Red border, label turns red (danger), error message shown below. | | Error + Filled| Red border, label returns to subtle color, error message shown below. | | Disabled | All interactions disabled, muted colors for label, input, icon, dropdown.|
Accessibility
aria-invalidis set whenerrorMessageis provided.aria-errormessagelinks the input to the error message element.aria-describedbylinks the input to the helper text element.aria-requiredis set whenrequiredis true.aria-labelledbylinks the input to its label.- The dropdown has
aria-haspopupandaria-expandedattributes. - The divider has
role="separator"witharia-orientation="vertical".
Theme Tokens
The component uses theme.components.inputCombo tokens for all styling, including:
- Container:
borderRadius,borderWidth,paddingHorizontal,paddingVertical - Colors per variant:
container.color,container.border,text.color.fieldLabel,text.color.inputPlaceholder,text.color.countryCode,icon.color - Typography:
fieldLabel,inputPlaceholder,error,helper
Internal Components
- atoms-input (
@bolttech/atoms-input) — renders the text input with label and icon. - molecules-dropdown (
@bolttech/molecules-dropdown) — renders the dropdown prefix selector. - atoms-divider (
@bolttech/divider) — renders the vertical divider between dropdown and input.
Contributing
Contributions are welcome! For any bug fixes, improvements, or new features, please open an issue or submit a pull request.
Please make sure to follow the code standards and test your changes before submitting.
