FieldDecorator
Низкоуровневый каркас для полей: рендерит блоки label, caption, hint, error, счётчик length и звёздочку required, а в children подставляется любой input или композиция (@cloud-ru/ds-input-private, дата-пикеры, селекты).
Пакет отдаёт три публичных компонента:
Label— строка заголовка: текст, звёздочка required, question-tooltip и подпись caption.Hint— подвал поля: подсказка/ошибка со статус-иконкой по валидации и счётчик length.FieldDecorator— композицияLabel+children+Hintв единой сетке.
Когда использовать
- Когда нужно обернуть нестандартный input в типовую разметку поля.
- Когда
FieldText/FieldSecureне подходят:- отдельный date-picker;
- masked input;
- кастомная композиция со счётчиком
lengthили валидационной подсказкой.
Анатомия
Size (default m)
| Значение | Когда |
|---|---|
s | Плотные таблицы, inline-редактирование |
m | Стандартные формы (по умолчанию) |
l | Лендинги, primary-формы |
ValidationState (default default)
Управляет цветом подсказки и иконкой showHintIcon. Проп error форсит error поверх любого validationState. На неактивном поле (disabled / readonly) иконка валидации не выводится — подсказка нейтральна.
| Значение | Когда |
|---|---|
default | Нет валидации — нейтральный baseline: подсказка в textTertiary, без иконки и без заливки |
error | Не прошло валидацию |
warning | Предупреждение |
success | Подтверждение |
Состояния (disabled / readonly)
Оси неактивного поля. Не комбинируются с цветом валидации: на неактивном поле иконка валидации не выводится, счётчик length скрыт.
| Значение | Поведение |
|---|---|
disabled | Поле выключено: счётчик скрыт, подсказка нейтральна |
readonly | Только для чтения: то же поведение подвала, что у disabled |
Заголовок (Label)
Шапка поля складывается из слотов:
label— текст заголовка;labelForсвязывает его с input через HTML-атрибутfor.required— звёздочка*рядом с заголовком, маркирует обязательное поле.labelTooltip— иконка вопроса с подсказкой (QuestionTooltip) после заголовка. ТребуетPortalContextProviderв дереве.caption— вспомогательная подпись справа в шапке.
Подвал (Hint)
Под содержимым выводится:
hint/error— текст подсказки.errorимеет приоритет надhintи форситvalidationState='error'.length— счётчик длиныcurrent/max. Когдаcurrentпревышаетmax, счётчик подсвечивается (data-limit-exceeded). Наdisabled/readonlyсчётчик скрыт.
Установка
pnpm add @cloud-ru/ds-field-decorator
import { FieldDecorator, Label, Hint } from '@cloud-ru/ds-field-decorator'
Примеры использования
Базовая обёртка
FieldDecorator оборачивает любой input
tsx
import { FieldDecorator } from '@cloud-ru/ds-field-decorator';
import { InputPrivate } from '@cloud-ru/ds-input-private';
import { useState } from 'react';
export function DecoratorBasic() {
const [value, setValue] = useState('');
return (
<FieldDecorator label='Custom field' hint='FieldDecorator оборачивает любой input' showHintIcon>
<InputPrivate value={value} onChange={setValue} placeholder='Type here' />
</FieldDecorator>
);
}Счётчик длины
Опционально
Кратко расскажите о себе
0/120
tsx
import { FieldDecorator } from '@cloud-ru/ds-field-decorator';
import { InputPrivate } from '@cloud-ru/ds-input-private';
import { useState } from 'react';
export function DecoratorLength() {
const [value, setValue] = useState('');
return (
<FieldDecorator
label='Bio'
caption='Опционально'
hint='Кратко расскажите о себе'
length={{ current: value.length, max: 120 }}
>
<InputPrivate value={value} onChange={setValue} maxLength={120} placeholder='Hi there' />
</FieldDecorator>
);
}Превышение лимита
Счётчик подсвечивается, когда current превышает max
49/20
tsx
import { FieldDecorator } from '@cloud-ru/ds-field-decorator';
import { InputPrivate } from '@cloud-ru/ds-input-private';
import { useState } from 'react';
export function DecoratorLimitExceeded() {
const [value, setValue] = useState('Слишком длинное значение, которое превышает лимит');
return (
<FieldDecorator
label='Заголовок'
hint='Счётчик подсвечивается, когда current превышает max'
length={{ current: value.length, max: 20 }}
>
<InputPrivate value={value} onChange={setValue} placeholder='Введите текст' />
</FieldDecorator>
);
}Подсказка к заголовку
*
Подсказка к заголовку выводится через иконку вопроса
tsx
import { FieldDecorator } from '@cloud-ru/ds-field-decorator';
import { InputPrivate } from '@cloud-ru/ds-input-private';
import { useState } from 'react';
export function DecoratorLabelTooltip() {
const [value, setValue] = useState('');
return (
<FieldDecorator
label='Идентификатор'
required
labelTooltip={{ tip: 'Уникальный идентификатор ресурса. Наведите на иконку рядом с заголовком.' }}
hint='Подсказка к заголовку выводится через иконку вопроса'
>
<InputPrivate value={value} onChange={setValue} placeholder='res-id' />
</FieldDecorator>
);
}Disabled и Readonly
На неактивном поле счётчик скрыт, подсказка нейтральна
Readonly также нейтрализует подсказку и прячет счётчик
tsx
import { FieldDecorator } from '@cloud-ru/ds-field-decorator';
import { InputPrivate } from '@cloud-ru/ds-input-private';
export function DecoratorDisabledReadonly() {
return (
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', alignItems: 'flex-start' }}>
<FieldDecorator
label='Disabled'
hint='На неактивном поле счётчик скрыт, подсказка нейтральна'
validationState='error'
showHintIcon
disabled
length={{ current: 5, max: 20 }}
>
<InputPrivate value='value' onChange={() => undefined} disabled />
</FieldDecorator>
<FieldDecorator
label='Readonly'
hint='Readonly также нейтрализует подсказку и прячет счётчик'
validationState='warning'
showHintIcon
readonly
length={{ current: 5, max: 20 }}
>
<InputPrivate value='value' onChange={() => undefined} readonly />
</FieldDecorator>
</div>
);
}Label отдельно
*
Опционально
tsx
import { Label } from '@cloud-ru/ds-field-decorator';
export function LabelStandalone() {
return (
<Label
label='Заголовок поля'
caption='Опционально'
required
labelTooltip={{ tip: 'Пояснение к заголовку через иконку вопроса' }}
/>
);
}Hint по состояниям валидации
Нейтральная подсказка под полем
12/100
Ошибка валидации
Предупреждение
Проверка пройдена
tsx
import { Hint } from '@cloud-ru/ds-field-decorator';
export function HintStandalone() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
<Hint hint='Нейтральная подсказка под полем' length={{ current: 12, max: 100 }} />
<Hint hint='Ошибка валидации' validationState='error' showHintIcon />
<Hint hint='Предупреждение' validationState='warning' showHintIcon />
<Hint hint='Проверка пройдена' validationState='success' showHintIcon />
</div>
);
}Props
FieldDecorator
Types
Props
FieldDecoratorProps| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
caption | string | — | no | Вторичная подпись справа |
children | ReactNode | — | yes | Содержимое (декорируемое поле) |
className | string | — | no | CSS-класс |
data-test-id | string | — | no | |
disabled | boolean | — | no | Поле выключено |
error | string | — | no | Ошибка (приоритетнее `hint`; форсит `validationState=error`) |
hint | string | — | no | Подсказка |
innerRef | Ref<HTMLDivElement> | — | no | Ref на корневой DOM-элемент |
label | string | — | no | Заголовок |
labelFor | string | — | no | HTML-атрибут `for` для `<label>` |
labelTooltip | QuestionTooltipProps | — | no | Подсказка (question-tooltip) у заголовка |
length | FieldLength | — | no | Счётчик длины `current/max` |
readonly | boolean | — | no | Только для чтения |
required | boolean | — | no | Показать знак обязательности `*` |
showHintIcon | boolean | true | no | Отображение статус-иконки у подсказки (по умолчанию `true`) |
size | "l" | "m" | "s" | m | no | Размер |
validationState | "default" | "error" | "success" | "warning" | default | no | Состояние валидации |
Unions
Types
FieldDecoratorProps
FieldLength
Unions
Size
ValidationState
Related props
QuestionTooltipProps
Label
Types
Props
LabelProps| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
caption | string | — | no | Вторичная подпись справа |
className | string | — | no | CSS-класс |
data-test-id | string | — | no | |
disabled | boolean | — | no | Поле выключено |
innerRef | Ref<HTMLDivElement> | — | no | Ref на корневой DOM-элемент |
label | string | — | no | Заголовок |
labelFor | string | — | no | HTML-атрибут `for` для `<label>` |
labelTooltip | QuestionTooltipProps | — | no | Подсказка (question-tooltip) у заголовка |
required | boolean | — | no | Показать знак обязательности `*` |
size | "l" | "m" | "s" | m | no | Размер |
Unions
Types
LabelProps
Unions
Size
Related props
QuestionTooltipProps
Hint
Types
Props
HintProps| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
className | string | — | no | CSS-класс |
data-test-id | string | — | no | |
disabled | boolean | — | no | Поле выключено |
error | string | — | no | Ошибка (приоритетнее `hint`; форсит `validationState=error`) |
hint | string | — | no | Подсказка |
innerRef | Ref<HTMLDivElement> | — | no | Ref на корневой DOM-элемент |
length | FieldLength | — | no | Счётчик длины `current/max` |
maxLines | number | — | no | Обрезать подсказку до N строк многоточием (через `TruncateString`, с тултипом полного текста на ховере). Без значения подсказка переносится без ограничения (дефолт поля). Нужно для карточек фиксированной высоты (`@cloud-ru/ds-attachment`), где длинный текст ошибки иначе выходит за границы. |
readonly | boolean | — | no | Только для чтения |
showHintIcon | boolean | true | no | Отображение статус-иконки у подсказки (по умолчанию `true`) |
size | "l" | "m" | "s" | m | no | Размер |
validationState | "default" | "error" | "success" | "warning" | default | no | Состояние валидации |