@bolttech/molecules-input-combo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,223 @@
1
+ # InputCombo Component
2
+
3
+ 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.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @bolttech/frontend-foundations @bolttech/molecules-input-combo
9
+ ```
10
+
11
+ or
12
+
13
+ ```bash
14
+ yarn add @bolttech/frontend-foundations @bolttech/molecules-input-combo
15
+ ```
16
+
17
+ ## Props
18
+
19
+ | Prop | Type | Default | Description |
20
+ | ---------------------- | --------------------------------------------- | -------------------- | -------------------------------------------------------------- |
21
+ | id | `string` | `'input-combo-id'` | Unique identifier for the component. |
22
+ | dataTestId | `string` | `'input-combo-test-id'` | The `data-testid` attribute for testing. |
23
+ | variant | `'grey' \| 'border'` | `'grey'` | Visual variant of the component. |
24
+ | label | `string` | — | Label text for the text input. |
25
+ | placeholder | `string` | — | Placeholder for the text input. |
26
+ | required | `boolean` | — | Whether the field is required (shows asterisk). |
27
+ | disabled | `boolean` | — | Disables the entire component. |
28
+ | errorMessage | `string` | — | Error message — triggers error state when set. |
29
+ | helperMessage | `string` | — | Helper text displayed below the field. |
30
+ | value | `string` | — | Current value of the text input. |
31
+ | dropdownValue | `string` | — | Currently selected dropdown option id. |
32
+ | dropdownOptions | `DropdownOption[]` | **(required)** | Options for the dropdown prefix selector. |
33
+ | dropdownLabel | `string` | `''` | Accessible label for the dropdown (visually hidden). |
34
+ | dropdownPlaceholder | `string` | — | Placeholder for the dropdown search input. |
35
+ | dropdownDisableSearch | `boolean` | `true` | Disables search/typing in the dropdown. |
36
+ | fullWidth | `boolean` | — | Whether the component takes the full width of its container. |
37
+ | icon | `string` | — | Icon name (Material Symbols Sharp). Defaults to `'info'` when tooltip is provided. |
38
+ | usePortal | `boolean` | — | Render dropdown options in a portal (useful inside overflow containers). |
39
+ | tooltip | `{ text: ReactNode; variant: string; size: string }` | — | Tooltip configuration for the info icon. |
40
+ | onChange | `(combo: ComboValue) => void` | — | Unified callback — returns `{ id, label, value }` with dropdown and input data. |
41
+ | onDropdownChange | `(option: DropdownOption) => void` | — | Callback when dropdown selection changes. |
42
+ | onBlur | `(evt: FocusEvent<HTMLInputElement>) => void` | — | Callback when input loses focus. |
43
+ | onFocus | `(evt: FocusEvent<HTMLInputElement>) => void` | — | Callback when input gains focus. |
44
+
45
+ ### DropdownOption
46
+
47
+ ```ts
48
+ type DropdownOption = {
49
+ label: string;
50
+ value: string;
51
+ id: string;
52
+ };
53
+ ```
54
+
55
+ ### ComboValue
56
+
57
+ ```ts
58
+ type ComboValue = {
59
+ /** Value provided by the dropdown selected option id */
60
+ id: string;
61
+ /** Unified value from dropdown and input — e.g. "+55 999888777" */
62
+ label: string;
63
+ /** Value from typed input */
64
+ value: string;
65
+ };
66
+ ```
67
+
68
+ ## Usage
69
+
70
+ ```tsx
71
+ import React, { useState } from 'react';
72
+ import { InputCombo } from '@bolttech/molecules-input-combo';
73
+ import { bolttechTheme, BolttechThemeProvider } from '@bolttech/frontend-foundations';
74
+
75
+ const phoneOptions = [
76
+ { label: '+1', value: '+1', id: 'phone-1' },
77
+ { label: '+44', value: '+44', id: 'phone-44' },
78
+ { label: '+55', value: '+55', id: 'phone-55' },
79
+ ];
80
+
81
+ const PhoneInput = () => {
82
+ const [inputValue, setInputValue] = useState('');
83
+ const [dropdownId, setDropdownId] = useState('phone-1');
84
+
85
+ return (
86
+ <BolttechThemeProvider theme={bolttechTheme}>
87
+ <InputCombo
88
+ variant="grey"
89
+ label="Phone number"
90
+ placeholder="Enter your phone number"
91
+ required
92
+ helperMessage="Include area code"
93
+ dropdownOptions={phoneOptions}
94
+ dropdownValue={dropdownId}
95
+ value={inputValue}
96
+ onChange={(combo) => setInputValue(combo.value)}
97
+ onDropdownChange={(opt) => setDropdownId(opt.id)}
98
+ tooltip={{ text: 'International format', variant: 'light', size: 'short' }}
99
+ />
100
+ </BolttechThemeProvider>
101
+ );
102
+ };
103
+
104
+ export default PhoneInput;
105
+ ```
106
+
107
+ ## Form Engine Integration
108
+
109
+ 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.
110
+
111
+ ### Schema example
112
+
113
+ ```tsx
114
+ import { Form, useForm } from '@bolttech/form-engine';
115
+
116
+ const phoneOptions = [
117
+ { label: '+1', value: '+1', id: 'phone-1' },
118
+ { label: '+55', value: '+55', id: 'phone-55' },
119
+ ];
120
+
121
+ const MyForm = () => {
122
+ useForm({
123
+ index: 'DEFAULT_ID',
124
+ onData: ({ data }) => {
125
+ console.log('Form data:', data);
126
+ },
127
+ });
128
+
129
+ return (
130
+ <Form
131
+ index="DEFAULT_ID"
132
+ schema={{
133
+ index: 'DEFAULT_ID',
134
+ components: [
135
+ {
136
+ name: 'input-combo-phone',
137
+ component: 'inputcombo',
138
+ props: {
139
+ variant: 'grey',
140
+ label: 'Phone number',
141
+ placeholder: 'Enter your phone number',
142
+ required: true,
143
+ helperMessage: 'Include area code',
144
+ dropdownOptions: phoneOptions,
145
+ dropdownValue: 'phone-55',
146
+ },
147
+ filters: {
148
+ onlyNumbers: true,
149
+ },
150
+ validations: {
151
+ methods: {
152
+ required: true,
153
+ },
154
+ eventMessages: {
155
+ ON_FIELD_BLUR: ['required'],
156
+ ON_FIELD_CHANGE: ['required'],
157
+ },
158
+ messages: {
159
+ required: 'Phone number is required',
160
+ },
161
+ },
162
+ },
163
+ ],
164
+ }}
165
+ />
166
+ );
167
+ };
168
+ ```
169
+
170
+ ### Key points
171
+
172
+ - The `valueChangeEvent` returns both the raw value (`_value`) and the full event metadata (`_metadata`), giving the form engine access to the dropdown selection alongside the input text.
173
+ - Use `useForm` with `onData` to observe form state changes in real time.
174
+ - Schema-level `filters` (e.g. `onlyNumbers: true`) can be applied to restrict input.
175
+ - Validations are triggered on configurable events (`ON_FIELD_BLUR`, `ON_FIELD_CHANGE`, `ON_FIELD_MOUNT`).
176
+
177
+ ## Variants
178
+
179
+ ### Grey (default)
180
+ Filled background with no visible border in the default state. Border appears on focus and error states.
181
+
182
+ ### Border
183
+ White background with a subtle border in all states.
184
+
185
+ ## States
186
+
187
+ | State | Behavior |
188
+ | ------------- | ------------------------------------------------------------------------ |
189
+ | Default | Label and placeholder visible, dropdown shows selected option. |
190
+ | Filled | Label color changes to subtle, input shows the entered value. |
191
+ | Focus | Container border changes to focus color. |
192
+ | Error | Red border, label turns red (danger), error message shown below. |
193
+ | Error + Filled| Red border, label returns to subtle color, error message shown below. |
194
+ | Disabled | All interactions disabled, muted colors for label, input, icon, dropdown.|
195
+
196
+ ## Accessibility
197
+
198
+ - `aria-invalid` is set when `errorMessage` is provided.
199
+ - `aria-errormessage` links the input to the error message element.
200
+ - `aria-describedby` links the input to the helper text element.
201
+ - `aria-required` is set when `required` is true.
202
+ - `aria-labelledby` links the input to its label.
203
+ - The dropdown has `aria-haspopup` and `aria-expanded` attributes.
204
+ - The divider has `role="separator"` with `aria-orientation="vertical"`.
205
+
206
+ ## Theme Tokens
207
+
208
+ The component uses `theme.components.inputCombo` tokens for all styling, including:
209
+ - Container: `borderRadius`, `borderWidth`, `paddingHorizontal`, `paddingVertical`
210
+ - Colors per variant: `container.color`, `container.border`, `text.color.fieldLabel`, `text.color.inputPlaceholder`, `text.color.countryCode`, `icon.color`
211
+ - Typography: `fieldLabel`, `inputPlaceholder`, `error`, `helper`
212
+
213
+ ## Internal Components
214
+
215
+ - **atoms-input** (`@bolttech/atoms-input`) — renders the text input with label and icon.
216
+ - **molecules-dropdown** (`@bolttech/molecules-dropdown`) — renders the dropdown prefix selector.
217
+ - **atoms-divider** (`@bolttech/divider`) — renders the vertical divider between dropdown and input.
218
+
219
+ ## Contributing
220
+
221
+ Contributions are welcome! For any bug fixes, improvements, or new features, please open an issue or submit a pull request.
222
+
223
+ Please make sure to follow the code standards and test your changes before submitting.