@redis-ui/components 43.0.2 → 43.2.1

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.
Files changed (85) hide show
  1. package/dist/HighlightedIcon/HighlightedIcon.cjs +26 -0
  2. package/dist/HighlightedIcon/HighlightedIcon.d.ts +3 -0
  3. package/dist/HighlightedIcon/HighlightedIcon.js +26 -0
  4. package/dist/HighlightedIcon/HighlightedIcon.style.cjs +49 -0
  5. package/dist/HighlightedIcon/HighlightedIcon.style.d.ts +5 -0
  6. package/dist/HighlightedIcon/HighlightedIcon.style.js +47 -0
  7. package/dist/HighlightedIcon/HighlightedIcon.types.d.ts +8 -0
  8. package/dist/HighlightedIcon/index.d.ts +3 -0
  9. package/dist/Stepper/Stepper.cjs +14 -5
  10. package/dist/Stepper/Stepper.d.ts +2 -0
  11. package/dist/Stepper/Stepper.js +14 -5
  12. package/dist/Stepper/Stepper.utils.cjs +12 -0
  13. package/dist/Stepper/Stepper.utils.d.ts +2 -0
  14. package/dist/Stepper/Stepper.utils.js +12 -0
  15. package/dist/Stepper/components/Compose/Compose.d.ts +1 -0
  16. package/dist/Stepper/components/Step/Step.cjs +2 -0
  17. package/dist/Stepper/components/Step/Step.d.ts +1 -0
  18. package/dist/Stepper/components/Step/Step.js +2 -0
  19. package/dist/Stepper/components/Step/components/Compose/Compose.cjs +2 -9
  20. package/dist/Stepper/components/Step/components/Compose/Compose.js +2 -9
  21. package/dist/Stepper/components/Step/components/Separator/Separator.cjs +15 -0
  22. package/dist/Stepper/components/Step/components/Separator/Separator.d.ts +3 -0
  23. package/dist/Stepper/components/Step/components/Separator/Separator.js +15 -0
  24. package/dist/Stepper/components/Step/components/Separator/Separator.style.cjs +24 -0
  25. package/dist/Stepper/components/Step/components/Separator/Separator.style.d.ts +1 -0
  26. package/dist/Stepper/components/Step/components/Separator/Separator.style.js +22 -0
  27. package/dist/Stepper/components/Step/components/Separator/Separator.types.d.ts +5 -0
  28. package/dist/index.cjs +4 -0
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.js +74 -70
  31. package/package.json +2 -2
  32. package/skills/redis-ui-components/SKILL.md +0 -2
  33. package/skills/redis-ui-components/references/ActionIconButton.md +96 -0
  34. package/skills/redis-ui-components/references/AppBar.md +161 -0
  35. package/skills/redis-ui-components/references/AppSelectionMenu.md +184 -0
  36. package/skills/redis-ui-components/references/AutoCompleteSelect.md +193 -0
  37. package/skills/redis-ui-components/references/Badge.md +77 -0
  38. package/skills/redis-ui-components/references/Banner.md +563 -0
  39. package/skills/redis-ui-components/references/BoxSelectionGroup.md +487 -0
  40. package/skills/redis-ui-components/references/Breadcrumbs.md +214 -0
  41. package/skills/redis-ui-components/references/ButtonGroup.md +126 -0
  42. package/skills/redis-ui-components/references/Card.md +56 -0
  43. package/skills/redis-ui-components/references/Checkbox.md +171 -0
  44. package/skills/redis-ui-components/references/Chip.md +154 -0
  45. package/skills/redis-ui-components/references/ChipList.md +307 -0
  46. package/skills/redis-ui-components/references/CopyToClipboardButton.md +47 -0
  47. package/skills/redis-ui-components/references/CountryFlag.md +57 -0
  48. package/skills/redis-ui-components/references/Drawer.md +298 -0
  49. package/skills/redis-ui-components/references/Filters.md +162 -0
  50. package/skills/redis-ui-components/references/FormField.md +678 -0
  51. package/skills/redis-ui-components/references/IconButton.md +63 -0
  52. package/skills/redis-ui-components/references/Input.md +295 -0
  53. package/skills/redis-ui-components/references/KeyValueList.md +501 -0
  54. package/skills/redis-ui-components/references/Label.md +238 -0
  55. package/skills/redis-ui-components/references/Link.md +402 -0
  56. package/skills/redis-ui-components/references/Loader.md +100 -0
  57. package/skills/redis-ui-components/references/Menu.md +988 -0
  58. package/skills/redis-ui-components/references/MidBar.md +358 -0
  59. package/skills/redis-ui-components/references/Modal.md +525 -0
  60. package/skills/redis-ui-components/references/MoreInfoIcon.md +119 -0
  61. package/skills/redis-ui-components/references/MultiSelect.md +558 -0
  62. package/skills/redis-ui-components/references/NumericInput.md +322 -0
  63. package/skills/redis-ui-components/references/Overflow.md +127 -0
  64. package/skills/redis-ui-components/references/Pagination.md +151 -0
  65. package/skills/redis-ui-components/references/PasswordInput.md +262 -0
  66. package/skills/redis-ui-components/references/Popover.md +868 -0
  67. package/skills/redis-ui-components/references/ProfileIcon.md +65 -0
  68. package/skills/redis-ui-components/references/ProgressBar.md +103 -0
  69. package/skills/redis-ui-components/references/QuantityCounter.md +555 -0
  70. package/skills/redis-ui-components/references/RadioGroup.md +265 -0
  71. package/skills/redis-ui-components/references/ScreenReaderAnnounce.md +147 -0
  72. package/skills/redis-ui-components/references/SearchBar.md +242 -0
  73. package/skills/redis-ui-components/references/SearchInput.md +213 -0
  74. package/skills/redis-ui-components/references/Section.md +349 -0
  75. package/skills/redis-ui-components/references/SideBar.md +468 -0
  76. package/skills/redis-ui-components/references/Slider.md +398 -0
  77. package/skills/redis-ui-components/references/Stepper.md +288 -0
  78. package/skills/redis-ui-components/references/Switch.md +193 -0
  79. package/skills/redis-ui-components/references/Tabs.md +383 -0
  80. package/skills/redis-ui-components/references/TextArea.md +139 -0
  81. package/skills/redis-ui-components/references/TextButton.md +217 -0
  82. package/skills/redis-ui-components/references/Toast.md +399 -0
  83. package/skills/redis-ui-components/references/ToggleButton.md +163 -0
  84. package/skills/redis-ui-components/references/Tooltip.md +636 -0
  85. package/skills/redis-ui-components/references/Typography.md +323 -0
@@ -0,0 +1,555 @@
1
+ # QuantityCounter
2
+
3
+ A quantity input with status indicators, validation controls, read-only rendering, and compound composition building blocks.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { QuantityCounter } from '@redislabsdev/redis-ui-components';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+ | id | `string` | - | Shared id for the composed input field |
16
+ | variant | `'outline' \| 'underline'` | `'outline'` | Visual variant for the composed input |
17
+ | error | `ReactElement \| string` | - | Error content; also drives error status |
18
+ | valid | `boolean` | `false` | Enables success status |
19
+ | readOnly | `boolean` | `false` | Switches the field into read-only mode |
20
+ | value | `number \| null` | - | Controlled numeric value |
21
+ | defaultValue | `number \| null` | - | Uncontrolled initial numeric value |
22
+ | onChange | `(newValue: number \| null) => void` | - | Called whenever the parsed numeric value changes |
23
+ | min | `number` | - | Minimum allowed value |
24
+ | max | `number` | - | Maximum allowed value |
25
+ | step | `number` | `1` | Step size used by arrow keys and step buttons |
26
+ | required | `boolean` | `false` | Requires a non-empty value when validation is enabled |
27
+ | maxDecimals | `number` | - | Limits fractional precision when auto-validation is enabled |
28
+ | autoValidate | `boolean` | `false` | Snaps typed values to constraints automatically |
29
+ | validate | `Validate` | - | Custom validation / snapping function |
30
+ | calcStepActionValue | `CalcStepActionValue` | - | Custom step value calculator for increment/decrement actions |
31
+ | valueLabel | `string` | - | Suffix label used next to the numeric value and in read-only mode |
32
+ | incrementButtonProps | `Omit<QuantityCounterStepButtonProps, 'action'>` | - | Overrides for the increment button |
33
+ | decrementButtonProps | `Omit<QuantityCounterStepButtonProps, 'action'>` | - | Overrides for the decrement button |
34
+
35
+ The component also forwards the remaining native `InputHTMLAttributes<HTMLInputElement>` props that are not omitted in `RestQuantityCounterProps`, such as `className`, `style`, `disabled`, `placeholder`, `name`, and `autoComplete`.
36
+
37
+ ## Sub-components
38
+
39
+ - `QuantityCounter.Compose` - Base compound wrapper that wires numeric state, validation, and read-only rendering.
40
+ - `QuantityCounter.InputGroup` - Input-and-label wrapper for the editable or read-only field.
41
+ - `QuantityCounter.InputGroup.Compose` - Low-level flex container used to build custom input-group layouts.
42
+ - `QuantityCounter.InputGroup.Tag` - Auto-sized input element used for the numeric field.
43
+ - `QuantityCounter.InputGroup.ValueLabel` - Suffix label rendered after the numeric value.
44
+ - `QuantityCounter.StepButton` - Increment or decrement action button with default plus/minus icons.
45
+ - `QuantityCounter.ReadonlyValue` - Styled read-only value wrapper used by custom read-only renderers.
46
+
47
+ ### QuantityCounter.Compose Props
48
+
49
+ | Prop | Type | Default | Description |
50
+ |------|------|---------|-------------|
51
+ | id | `string` | - | Shared id for the compound field |
52
+ | variant | `'outline' \| 'underline'` | `'outline'` | Compound field variant |
53
+ | error | `ReactElement \| string` | - | Error content propagated to the field state |
54
+ | valid | `boolean` | `false` | Success state flag |
55
+ | readOnly | `boolean` | `false` | Read-only mode flag |
56
+ | value | `number \| null` | - | Controlled value |
57
+ | defaultValue | `number \| null` | - | Uncontrolled initial value |
58
+ | onChange | `(newValue: number \| null) => void` | - | Value change handler |
59
+ | min | `number` | - | Minimum constraint |
60
+ | max | `number` | - | Maximum constraint |
61
+ | step | `number` | `1` | Step size for validation and stepping |
62
+ | required | `boolean` | `false` | Required flag for validation |
63
+ | maxDecimals | `number` | - | Fractional precision limit |
64
+ | autoValidate | `boolean` | `false` | Enables automatic constraint snapping |
65
+ | validate | `Validate` | - | Custom validation hook |
66
+ | calcStepActionValue | `CalcStepActionValue` | - | Custom step computation hook |
67
+ | valueLabel | `string` | - | Label suffix used in read-only rendering |
68
+ | readonlyRender | `ReadOnlyRenderComponent` | Generated from `valueLabel` when provided | Custom read-only renderer |
69
+ | readonlyProps | `HTMLAttributes<HTMLElement>` | - | Additional props passed to the read-only wrapper |
70
+ | children | `ReactNode` | - | Nested compound content |
71
+
72
+ The compose wrapper also accepts the remaining `ComposeElementProps` from the shared numeric compose layer, including standard container attributes such as `children`, `className`, `style`, and event handlers.
73
+
74
+ ### QuantityCounter.InputGroup Props
75
+
76
+ | Prop | Type | Default | Description |
77
+ |------|------|---------|-------------|
78
+ | valueLabel | `string` | - | Text shown after the numeric value |
79
+
80
+ The wrapper also forwards the remaining native input attributes from `RestInputTagProps` to the internal input tag, including `placeholder`, `disabled`, `autoComplete`, and event handlers.
81
+
82
+ ### QuantityCounter.InputGroup.Compose Props
83
+
84
+ | Prop | Type | Default | Description |
85
+ |------|------|---------|-------------|
86
+ | children | `ReactNode` | - | Nested input-group content |
87
+
88
+ This component is a layout wrapper and also accepts standard container attributes from `ComposeElementProps`.
89
+
90
+ ### QuantityCounter.InputGroup.Tag Props
91
+
92
+ | Prop | Type | Default | Description |
93
+ |------|------|---------|-------------|
94
+ | autoSize | `boolean` | `true` | Forces the input to auto-size inside the quantity counter |
95
+ | aria-description | `string` | `Press up or down arrow keys to change the value` | Describes the keyboard step interaction |
96
+
97
+ The tag also accepts native input props. The underlying input defaults to `type="text"` and `autoComplete="off"`.
98
+
99
+ ### QuantityCounter.InputGroup.ValueLabel Props
100
+
101
+ | Prop | Type | Default | Description |
102
+ |------|------|---------|-------------|
103
+ | children | `string \| ReactElement` | - | Value label content shown next to the numeric value |
104
+
105
+ If there is no value or no label content, the component renders nothing.
106
+
107
+ ### QuantityCounter.StepButton Props
108
+
109
+ | Prop | Type | Default | Description |
110
+ |------|------|---------|-------------|
111
+ | action | `'increment' \| 'decrement'` | required | Chooses whether the button increments or decrements the value |
112
+ | variant | `'primary' \| 'secondary'` | `'secondary'` | Button styling variant |
113
+ | size | `'L' \| 'M' \| 'S' \| 'XS'` | `'S'` | Icon button size |
114
+ | icon | `IconType` | `PlusIcon` or `MinusIcon` depending on `action` | Overrides the default plus/minus icon |
115
+ | title | `string` | `Increment` or `Decrement` depending on `action` | Accessible title / tooltip text |
116
+ | disabled | `boolean` | - | Manually disables the step button |
117
+ | onClick | `MouseEventHandler<HTMLButtonElement>` | - | Click handler for the step button |
118
+ | onMouseDown | `MouseEventHandler<HTMLButtonElement>` | - | Mouse-down handler for the step button |
119
+
120
+ The button also accepts the remaining `ActionIconButtonProps` and native button attributes. Internally it forces `tabIndex={-1}` and hides itself from assistive tech because the input drives the interaction.
121
+
122
+ ### QuantityCounter.ReadonlyValue Props
123
+
124
+ This is the shared read-only text wrapper used by custom read-only renderers. It behaves like the underlying `Typography.Body` wrapper and is typically paired with `ReadonlyRenderProps` inside `QuantityCounter.Compose`.
125
+
126
+ ## Examples
127
+
128
+ ### Basic Usage
129
+
130
+ ```tsx
131
+ import { QuantityCounter } from '@redislabsdev/redis-ui-components';
132
+
133
+ <QuantityCounter placeholder="QuantityCounter" id="custom-id" disabled={false} valueLabel="MB" />;
134
+ ```
135
+
136
+ ### Status
137
+
138
+ > Adding `valid` prop enables valid status.
139
+ >
140
+ > Adding some text to the `error` prop of `QuantityCounter` enables error status. Hover the error indicator to see the tooltip.
141
+ >
142
+ > Error status overrides valid status.
143
+
144
+ ```tsx
145
+ import { QuantityCounter } from '@redislabsdev/redis-ui-components';
146
+
147
+ <>
148
+ Valid status
149
+ <QuantityCounter valueLabel="MB" placeholder="Quantity Counter" valid />
150
+ Error status (hover for tooltip)
151
+ <QuantityCounter valueLabel="MB" placeholder="Quantity Counter" error="Error message" />
152
+ </>
153
+ ```
154
+
155
+ ### DisabledAndReadonly
156
+
157
+ > Adding `disabled` or `readOnly` prop switches the component to disabled or read-only states accordingly.
158
+ >
159
+ > In read-only mode only the value is visible. Other indicators are ignored.
160
+ >
161
+ > Read-only overrides disabled.
162
+
163
+ ```tsx
164
+ import { QuantityCounter } from '@redislabsdev/redis-ui-components';
165
+
166
+ <>
167
+ Disabled
168
+ <QuantityCounter valueLabel="MB" disabled />
169
+ Disabled with value
170
+ <QuantityCounter valueLabel="MB" disabled defaultValue={123.1} />
171
+ Disabled with error status (hover for tooltip)
172
+ <QuantityCounter valueLabel="MB" disabled defaultValue={123.1} error="Error message" />
173
+ Read only
174
+ <QuantityCounter valueLabel="MB" value={123.1} readOnly />
175
+ </>
176
+ ```
177
+
178
+ ### DisabledStepButtons
179
+
180
+ > Step buttons can be disabled conditionally or forcibly.
181
+ >
182
+ > Forced disabling is not recommended, because step actions are still available, for example via keyboard.
183
+ >
184
+ > Conditional disabling works if range bounds are defined with `min` and `max`.
185
+
186
+ ```tsx
187
+ import { QuantityCounter } from '@redislabsdev/redis-ui-components';
188
+
189
+ <>
190
+ Forcibly disabled increment button (not recommended)
191
+ <QuantityCounter
192
+ valueLabel="MB"
193
+ placeholder="QuantityCounter"
194
+ incrementButtonProps={{ disabled: true }}
195
+ />
196
+ Forcibly disabled decrement button (not recommended)
197
+ <QuantityCounter
198
+ valueLabel="MB"
199
+ placeholder="QuantityCounter"
200
+ decrementButtonProps={{ disabled: true }}
201
+ />
202
+ Auto disabled increment button, when value reached upper range
203
+ <QuantityCounter valueLabel="MB" placeholder="QuantityCounter" max={10} defaultValue={10} />
204
+ Auto disabled decrement button, when value reached lower range
205
+ <QuantityCounter valueLabel="MB" placeholder="QuantityCounter" min={-10} defaultValue={-10} />
206
+ </>
207
+ ```
208
+
209
+ ### WithLabel
210
+
211
+ > `QuantityCounter` does not have its own label or external decoration. Use `FormField` for that.
212
+ >
213
+ > If a custom id is specified for the input, `FormField` inherits it; otherwise the label is auto-connected with a generated id.
214
+ >
215
+ > Custom ids must start with a letter to work correctly.
216
+ >
217
+ > `FormField` also inherits status and some states from the nested input.
218
+
219
+ ```tsx
220
+ import { FormField, QuantityCounter } from '@redislabsdev/redis-ui-components';
221
+
222
+ <>
223
+ <FormField label="FormField Label with custom id">
224
+ <QuantityCounter valueLabel="MB" placeholder="Quantity Counter" id="custom-id" />
225
+ </FormField>
226
+ <FormField
227
+ label="FormField Label with auto-generated id"
228
+ additionalText="FormField additional text"
229
+ >
230
+ <QuantityCounter valueLabel="MB" placeholder="Quantity Counter" />
231
+ </FormField>
232
+ <FormField
233
+ label="FormField Label with read-only mode"
234
+ additionalText="FormField additional text"
235
+ >
236
+ <QuantityCounter valueLabel="MB" value={123.1} readOnly />
237
+ </FormField>
238
+ </>
239
+ ```
240
+
241
+ ### Variants
242
+
243
+ ```tsx
244
+ import { FormField, QuantityCounter } from '@redislabsdev/redis-ui-components';
245
+
246
+ <>
247
+ <FormField label="Outline (default)" additionalText="outline">
248
+ <QuantityCounter valueLabel="MB" placeholder="Quantity Counter" />
249
+ </FormField>
250
+ <FormField label="Underline" additionalText="underline">
251
+ <QuantityCounter valueLabel="MB" placeholder="Quantity Counter" variant="underline" />
252
+ </FormField>
253
+ </>
254
+ ```
255
+
256
+ ### AutoSize
257
+
258
+ > Unlike other text inputs, a regular input will ignore `width: auto`.
259
+ >
260
+ > `QuantityCounter` auto-sizes out of the box.
261
+
262
+ ```tsx
263
+ import { QuantityCounter } from '@redislabsdev/redis-ui-components';
264
+
265
+ <>
266
+ Type text in the input boxes to see how it auto-sizes
267
+ <QuantityCounter valueLabel="MB" placeholder="placeholder" />
268
+ QuantityCounter without placeholder:
269
+ <QuantityCounter valueLabel="MB" defaultValue={123.1} />
270
+ </>
271
+ ```
272
+
273
+ ### Validations
274
+
275
+ > `QuantityCounter` supports `min`, `max`, `step`, `required`, and `maxDecimals`.
276
+ >
277
+ > Constraints are applied when typing or blurring if `autoValidate` is enabled, and always when stepping with the keyboard or buttons.
278
+ >
279
+ > Validation can be overridden with `validate`.
280
+ >
281
+ > Step calculation can be customized with `calcStepActionValue`.
282
+
283
+ ```tsx
284
+ import { useState } from 'react';
285
+ import { FormField, QuantityCounter, Switch } from '@redislabsdev/redis-ui-components';
286
+ import { ConstraintsGroup } from '../InputStories.style';
287
+
288
+ const defValueLabel = 'MB';
289
+
290
+ function Render() {
291
+ const [props, setProps] = useState<{
292
+ min?: number;
293
+ max?: number;
294
+ step?: number;
295
+ required: boolean;
296
+ }>({ min: -99, max: 99, step: 10, required: false });
297
+ const [val1, setVal1] = useState<number | null>(null);
298
+ const [val2, setVal2] = useState<number | null>(null);
299
+ const [val3, setVal3] = useState<number | null>(null);
300
+ const [val4, setVal4] = useState<number | null>(null);
301
+
302
+ return (
303
+ <>
304
+ <strong>
305
+ Edit Constraints, type number, blur input, press Up/Down arrows or click +/- buttons to see
306
+ behavior of each one
307
+ </strong>
308
+ <ConstraintsGroup>
309
+ <strong>Constraints</strong>
310
+ <div>
311
+ <FormField label="min">
312
+ <QuantityCounter
313
+ valueLabel={defValueLabel}
314
+ value={props.min}
315
+ onChange={(min) => setProps((state) => ({ ...state, min: min ?? undefined }))}
316
+ />
317
+ </FormField>
318
+ <FormField label="max">
319
+ <QuantityCounter
320
+ valueLabel={defValueLabel}
321
+ value={props.max}
322
+ onChange={(max) => setProps((state) => ({ ...state, max: max ?? undefined }))}
323
+ />
324
+ </FormField>
325
+ <FormField label="step">
326
+ <QuantityCounter
327
+ valueLabel={defValueLabel}
328
+ value={props.step}
329
+ autoValidate
330
+ onChange={(step) => setProps((state) => ({ ...state, step: step ?? undefined }))}
331
+ />
332
+ </FormField>
333
+ <FormField label="required">
334
+ <Switch
335
+ checked={props.required}
336
+ onCheckedChange={() => setProps((state) => ({ ...state, required: !state.required }))}
337
+ />
338
+ </FormField>
339
+ </div>
340
+ </ConstraintsGroup>
341
+ <FormField
342
+ label={`Regular (value: ${val1})`}
343
+ additionalText="errors should be detected in custom way (step always validate min-max constraints)"
344
+ required={props.required}
345
+ >
346
+ <QuantityCounter valueLabel={defValueLabel} {...props} value={val1} onChange={setVal1} />
347
+ </FormField>
348
+ <FormField
349
+ label={`Auto Validated (value: ${val2})`}
350
+ additionalText="constraints are validating inside and outside we have valid value"
351
+ required={props.required}
352
+ >
353
+ <QuantityCounter
354
+ valueLabel={defValueLabel}
355
+ {...props}
356
+ value={val2}
357
+ onChange={setVal2}
358
+ autoValidate
359
+ />
360
+ </FormField>
361
+ <FormField
362
+ label={`Custom Step Calculation (value: ${val3})`}
363
+ additionalText="step value is calculated as simple addition the step size to the current value"
364
+ required={props.required}
365
+ >
366
+ <QuantityCounter
367
+ valueLabel={defValueLabel}
368
+ {...props}
369
+ value={val3}
370
+ onChange={setVal3}
371
+ calcStepActionValue={(params) =>
372
+ (params.value ?? (params.action === 'increment' ? props.min : props.max) ?? 0) +
373
+ (params.action === 'increment' ? params.step : -params.step)
374
+ }
375
+ />
376
+ </FormField>
377
+ <FormField
378
+ label={`Custom Validation (value: ${val4})`}
379
+ additionalText="makes the value a multiple of the step size (no other constraints applied)"
380
+ required={props.required}
381
+ >
382
+ <QuantityCounter
383
+ valueLabel={defValueLabel}
384
+ {...props}
385
+ value={val4}
386
+ onChange={setVal4}
387
+ autoValidate
388
+ validate={(params) =>
389
+ params.value && Math.round(params.value / (props.step || 1)) * (props.step || 1)
390
+ }
391
+ />
392
+ </FormField>
393
+ </>
394
+ );
395
+ }
396
+ ```
397
+
398
+ ### ComposeReadOnly
399
+
400
+ ```tsx
401
+ import { QuantityCounter } from '@redislabsdev/redis-ui-components';
402
+
403
+ <QuantityCounter.Compose readOnly defaultValue={123.1} valueLabel="MB">
404
+ <QuantityCounter.StepButton action="decrement" />
405
+ <QuantityCounter.InputGroup valueLabel="MB" />
406
+ <QuantityCounter.StepButton action="increment" />
407
+ </QuantityCounter.Compose>;
408
+ ```
409
+
410
+ > The story also supplies a custom `readonlyRender` in Storybook args to demonstrate `QuantityCounter.ReadonlyValue`.
411
+
412
+ ### ComposeValueLabelAsSelect
413
+
414
+ > Value labels can be composed as any component instead of plain text.
415
+ >
416
+ > This example uses a `Select` as a dynamic value label and shows it in read-only mode too.
417
+ >
418
+ > The story makes the label auto-sized and wraps it with `FormField.Nested` to avoid interference with the surrounding `QuantityCounter`. It also keeps the select value in sync with `valueLabel` for the read-only view.
419
+
420
+ ```tsx
421
+ import { useEffect, useState } from 'react';
422
+ import { Button, FormField, QuantityCounter, Select } from '@redislabsdev/redis-ui-components';
423
+
424
+ const defValueLabel = 'MB';
425
+ const options = [{ value: 'MB' }, { value: 'GB' }, { value: 'TB' }];
426
+
427
+ function Render({ disabled, ...args }) {
428
+ const [readOnly, setReadOnly] = useState(args.readOnly);
429
+ const [valueLabel, setValueLabel] = useState(defValueLabel);
430
+
431
+ useEffect(() => {
432
+ setReadOnly(args.readOnly);
433
+ }, [args.readOnly]);
434
+
435
+ return (
436
+ <>
437
+ <QuantityCounter.Compose {...args} readOnly={readOnly} valueLabel={valueLabel}>
438
+ <QuantityCounter.StepButton action="decrement" />
439
+ <QuantityCounter.InputGroup.Compose>
440
+ <QuantityCounter.InputGroup.Tag disabled={disabled} />
441
+ <QuantityCounter.InputGroup.ValueLabel>
442
+ <FormField.Nested>
443
+ <Select
444
+ options={options}
445
+ style={{
446
+ width: 'fit-content',
447
+ height: 'fit-content',
448
+ border: 'none',
449
+ padding: 0
450
+ }}
451
+ value={valueLabel}
452
+ onChange={(valueLabelSelect) => setValueLabel(valueLabelSelect)}
453
+ />
454
+ </FormField.Nested>
455
+ </QuantityCounter.InputGroup.ValueLabel>
456
+ </QuantityCounter.InputGroup.Compose>
457
+ <QuantityCounter.StepButton action="increment" />
458
+ </QuantityCounter.Compose>
459
+ <Button onClick={() => setReadOnly(!readOnly)} style={{ alignSelf: 'flex-start' }}>
460
+ Toggle Read-Only
461
+ </Button>
462
+ </>
463
+ );
464
+ }
465
+ ```
466
+
467
+ ### ComposeValueLabelAsInput
468
+
469
+ > Value labels can be composed as any component instead of plain text.
470
+ >
471
+ > This example uses a text `Input` as a dynamic value label.
472
+ >
473
+ > Make the label replacement component auto-sized and wrap it with `FormField.Nested` to avoid unnecessary interaction with the surrounding `QuantityCounter`. Keep its value controlled so the read-only mode can mirror `valueLabel`.
474
+
475
+ ```tsx
476
+ import { useEffect, useState } from 'react';
477
+ import { Button, FormField, Input, QuantityCounter } from '@redislabsdev/redis-ui-components';
478
+
479
+ const defValueLabel = 'MB';
480
+
481
+ function Render({ disabled, ...args }) {
482
+ const [readOnly, setReadOnly] = useState(args.readOnly);
483
+ const [valueLabel, setValueLabel] = useState(args.valueLabel);
484
+
485
+ useEffect(() => {
486
+ setValueLabel(args.valueLabel);
487
+ }, [args.valueLabel]);
488
+
489
+ useEffect(() => {
490
+ setReadOnly(args.readOnly);
491
+ }, [args.readOnly]);
492
+
493
+ return (
494
+ <>
495
+ <QuantityCounter.Compose {...args} readOnly={readOnly} valueLabel={valueLabel}>
496
+ <QuantityCounter.StepButton action="decrement" />
497
+ <QuantityCounter.InputGroup.Compose>
498
+ <QuantityCounter.InputGroup.Tag disabled={disabled} />
499
+ <QuantityCounter.InputGroup.ValueLabel>
500
+ <FormField.Nested>
501
+ <Input
502
+ defaultValue="TB"
503
+ autoSize
504
+ style={{
505
+ width: 'fit-content',
506
+ height: 'fit-content',
507
+ border: 'none',
508
+ padding: 0
509
+ }}
510
+ value={valueLabel}
511
+ onChange={(valueLabelInput) => setValueLabel(valueLabelInput)}
512
+ />
513
+ </FormField.Nested>
514
+ </QuantityCounter.InputGroup.ValueLabel>
515
+ </QuantityCounter.InputGroup.Compose>
516
+ <QuantityCounter.StepButton action="increment" />
517
+ </QuantityCounter.Compose>
518
+ <Button
519
+ onClick={() => {
520
+ setReadOnly(!readOnly);
521
+ }}
522
+ style={{ alignSelf: 'flex-start' }}
523
+ >
524
+ Toggle Read-Only
525
+ </Button>
526
+ </>
527
+ );
528
+ }
529
+ ```
530
+
531
+ ### ComposeOldLook
532
+
533
+ > With a few CSS changes and by reordering the compound parts you can change the view of the component.
534
+ >
535
+ > This example shows the old look of `QuantityCounter`.
536
+
537
+ ```tsx
538
+ import { QuantityCounter } from '@redislabsdev/redis-ui-components';
539
+
540
+ <QuantityCounter.Compose defaultValue={123.1} valueLabel="MB">
541
+ <QuantityCounter.InputGroup valueLabel="MB" style={{ justifyContent: 'flex-start' }} />
542
+ <QuantityCounter.StepButton action="increment" />
543
+ <QuantityCounter.StepButton action="decrement" />
544
+ </QuantityCounter.Compose>
545
+ ```
546
+
547
+ ## Notes
548
+
549
+ - `QuantityCounter` uses the shared numeric compose layer, so `readOnly` overrides `disabled`.
550
+ - Error status overrides valid status.
551
+ - Forced disabling of step buttons is not recommended because stepping still works from the keyboard.
552
+ - Use `FormField` when you need labels or additional text around the counter.
553
+ - The nested input auto-sizes by default and exposes an accessible description for the up/down key behavior.
554
+ - `QuantityCounter.InputGroup.ValueLabel` can render either plain text or a custom element, which makes it useful for custom read-only renderers.
555
+ - When composing a custom value label component, wrap it with `FormField.Nested` and keep it controlled so the read-only label stays in sync.