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 оборачивает InputPrivate в типовую разметку label/hint.
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>
  );
}

Счётчик длины

Счётчик длиныLength показывает «текущая/максимум», обновляется по изменению значения.
Опционально
Кратко расскажите о себе
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, счётчик подсвечивается (data-limit-exceeded).
Счётчик подсвечивается, когда 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>
  );
}

Подсказка к заголовку

Подсказка к заголовкуlabelTooltip добавляет иконку вопроса после label; required рисует звёздочку. Требует PortalContextProvider.
*
Подсказка к заголовку выводится через иконку вопроса
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

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 отдельно

Label отдельноСтроку заголовка можно рендерить самостоятельно — например, над кастомной композицией.
*
Опционально
tsx
import { Label } from '@cloud-ru/ds-field-decorator';

export function LabelStandalone() {
  return (
    <Label
      label='Заголовок поля'
      caption='Опционально'
      required
      labelTooltip={{ tip: 'Пояснение к заголовку через иконку вопроса' }}
    />
  );
}

Hint по состояниям валидации

Hint по состояниям валидацииПодсказка меняет цвет и статус-иконку по validationState.
Нейтральная подсказка под полем
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

PropsFieldDecoratorProps
PropTypeDefaultRequiredDescription
captionstringnoВторичная подпись справа
childrenReactNodeyesСодержимое (декорируемое поле)
classNamestringnoCSS-класс
data-test-idstringno
disabledbooleannoПоле выключено
errorstringnoОшибка (приоритетнее `hint`; форсит `validationState=error`)
hintstringnoПодсказка
innerRefRef<HTMLDivElement>noRef на корневой DOM-элемент
labelstringnoЗаголовок
labelForstringnoHTML-атрибут `for` для `<label>`
labelTooltipQuestionTooltipPropsnoПодсказка (question-tooltip) у заголовка
lengthFieldLengthnoСчётчик длины `current/max`
readonlybooleannoТолько для чтения
requiredbooleannoПоказать знак обязательности `*`
showHintIconbooleantruenoОтображение статус-иконки у подсказки (по умолчанию `true`)
size"l" | "m" | "s"mnoРазмер
validationState"default" | "error" | "success" | "warning"defaultnoСостояние валидации

Unions

Types

FieldDecoratorProps

Unions

Label

Types

PropsLabelProps
PropTypeDefaultRequiredDescription
captionstringnoВторичная подпись справа
classNamestringnoCSS-класс
data-test-idstringno
disabledbooleannoПоле выключено
innerRefRef<HTMLDivElement>noRef на корневой DOM-элемент
labelstringnoЗаголовок
labelForstringnoHTML-атрибут `for` для `<label>`
labelTooltipQuestionTooltipPropsnoПодсказка (question-tooltip) у заголовка
requiredbooleannoПоказать знак обязательности `*`
size"l" | "m" | "s"mnoРазмер

Unions

Types

LabelProps

Unions

Hint

Types

PropsHintProps
PropTypeDefaultRequiredDescription
classNamestringnoCSS-класс
data-test-idstringno
disabledbooleannoПоле выключено
errorstringnoОшибка (приоритетнее `hint`; форсит `validationState=error`)
hintstringnoПодсказка
innerRefRef<HTMLDivElement>noRef на корневой DOM-элемент
lengthFieldLengthnoСчётчик длины `current/max`
maxLinesnumbernoОбрезать подсказку до N строк многоточием (через `TruncateString`, с тултипом полного текста на ховере). Без значения подсказка переносится без ограничения (дефолт поля). Нужно для карточек фиксированной высоты (`@cloud-ru/ds-attachment`), где длинный текст ошибки иначе выходит за границы.
readonlybooleannoТолько для чтения
showHintIconbooleantruenoОтображение статус-иконки у подсказки (по умолчанию `true`)
size"l" | "m" | "s"mnoРазмер
validationState"default" | "error" | "success" | "warning"defaultnoСостояние валидации

Unions

Types

HintProps

Unions

Storybook

Figma