NotificationCard

Карточка одного уведомления. Тип задаёт иконку статуса и цвет, состояние unread подсвечивает непрочитанное и шлёт onVisible при попадании во вьюпорт, опциональные слоты — лейбл, ссылка, дата, кнопки действий и меню.

Когда использовать

  • Элемент ленты внутри NotificationPanelContent.
  • Отдельная карточка статуса операции (деплой, бэкап, инцидент) в произвольном контейнере.

Анатомия

Appearance (default default)

Тип уведомления — задаёт иконку статуса и её цвет.

  • default — нейтральное событие (упоминание, информация).
  • error — ошибка или инцидент.
  • warning — предупреждение (лимит, деградация).
  • success — успешное завершение операции.

Состояние unread (default false)

unread подсвечивает карточку как непрочитанную и показывает индикатор. Когда карточка попадает во вьюпорт на 80%, один раз срабатывает onVisible(id) — потребитель помечает её прочитанной.

Слоты

  • label — короткий лейбл перед заголовком (категория, ID).
  • link — ссылка в подвале, рендерится через @cloud-ru/ds-link.
  • date — дата/время в подвале.
  • primaryButton — кнопка главного действия (Button view='tonal').
  • secondaryButton — кнопка второстепенного действия (Button view='simple').
  • actions — меню дополнительных действий (kebab-кнопка с droplist).
  • onClick — делает карточку кликабельной (role='button', фокус с клавиатуры). Вложенные кнопки, ссылка и меню активируются независимо.

Установка

pnpm add @cloud-ru/ds-uikit-product-notification
import { NotificationCard } from '@cloud-ru/ds-uikit-product-notification'

Примеры использования

Базовая карточка

Базовая карточкаМинимальный набор: тип success, лейбл, заголовок, контент и дата.
Backup
Резервная копия завершена
Бэкап БД prod-1 (412 ГБ) загружен в холодное хранилище s3://backups-prod/2026-05-27/
сегодня · 14:32
tsx
import { APPEARANCE, NotificationCard } from '@cloud-ru/ds-uikit-product-notification';

export function BasicCard() {
  return (
    <NotificationCard
      id='backup-1729'
      label='Backup'
      appearance={APPEARANCE.Success}
      title='Резервная копия завершена'
      description='Бэкап БД prod-1 (412 ГБ) загружен в холодное хранилище s3://backups-prod/2026-05-27/'
      date='сегодня · 14:32'
    />
  );
}

Непрочитанные с onVisible

Непрочитанные с onVisibleunread + onVisible помечает карточку прочитанной при попадании во вьюпорт.
Непрочитанное уведомление
Storage
Лимит дисковой квоты
Использовано 92% квоты на /var/data — осталось 38 ГБ из 480 ГБ.
вчера · 19:04
Непрочитанное уведомление
Storage
Лимит дисковой квоты
Использовано 87% квоты на /var/log — осталось 62 ГБ из 480 ГБ.
вчера · 17:50
tsx
import { APPEARANCE, NotificationCard } from '@cloud-ru/ds-uikit-product-notification';
import { useState } from 'react';

export function UnreadCard() {
  const [readIds, setReadIds] = useState<Set<string>>(new Set());
  const markRead = (id: string) => setReadIds(prev => (prev.has(id) ? prev : new Set(prev).add(id)));

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
      <NotificationCard
        id='quota-var-data'
        label='Storage'
        appearance={APPEARANCE.Warning}
        title='Лимит дисковой квоты'
        description='Использовано 92% квоты на /var/data — осталось 38 ГБ из 480 ГБ.'
        date='вчера · 19:04'
        unread={!readIds.has('quota-var-data')}
        onVisible={markRead}
      />
      <NotificationCard
        id='quota-var-log'
        label='Storage'
        appearance={APPEARANCE.Warning}
        title='Лимит дисковой квоты'
        description='Использовано 87% квоты на /var/log — осталось 62 ГБ из 480 ГБ.'
        date='вчера · 17:50'
        unread={!readIds.has('quota-var-log')}
        onVisible={markRead}
      />
    </div>
  );
}

Инцидент со ссылкой

Инцидент со ссылкойappearance=error, лейбл-ID, ссылка на инцидент в подвале.
Непрочитанное уведомление
INC-4821
Кластер k8s-prod-1 деградирован
3 из 5 нод недоступны последние 6 минут. Pod scheduling приостановлен.
Открыть инцидент
сегодня · 03:14
tsx
import { APPEARANCE, NotificationCard } from '@cloud-ru/ds-uikit-product-notification';

export function IncidentCard() {
  return (
    <NotificationCard
      id='inc-4821'
      label='INC-4821'
      appearance={APPEARANCE.Error}
      title='Кластер k8s-prod-1 деградирован'
      description='3 из 5 нод недоступны последние 6 минут. Pod scheduling приостановлен.'
      date='сегодня · 03:14'
      link={{ label: 'Открыть инцидент', href: '/incidents/INC-4821' }}
      unread
    />
  );
}

Действия и состояние

Действия и состояниеprimaryButton с loading, secondaryButton и меню actions — живой сценарий повтора деплоя на useState.
api-gateway · v2.18.3
Деплой не прошёл health-check
Readiness probe вернул 503 на 4 из 6 подов. Traffic не переключён на новую версию.
5 минут назад
tsx
import { APPEARANCE, NotificationCard } from '@cloud-ru/ds-uikit-product-notification';
import { useState } from 'react';

export function DeployFailureCard() {
  const [status, setStatus] = useState<'failed' | 'retrying' | 'dismissed'>('failed');

  if (status === 'dismissed') {
    return null;
  }

  return (
    <NotificationCard
      id='deploy-9217'
      label='api-gateway · v2.18.3'
      appearance={APPEARANCE.Error}
      title='Деплой не прошёл health-check'
      description='Readiness probe вернул 503 на 4 из 6 подов. Traffic не переключён на новую версию.'
      date='5 минут назад'
      primaryButton={{
        label: status === 'retrying' ? 'Запускаю…' : 'Повторить деплой',
        loading: status === 'retrying',
        onClick: () => {
          setStatus('retrying');
          // Если карточку закрыли во время повтора, по таймеру не возвращаем её в `failed`.
          setTimeout(() => setStatus(prev => (prev === 'retrying' ? 'failed' : prev)), 1200);
        },
      }}
      secondaryButton={{
        label: 'Логи пода',
        onClick: () => window.open('/logs/api-gateway/2.18.3', '_blank'),
      }}
      actions={[
        { content: { label: 'Отметить прочитанным' }, onClick: () => setStatus('dismissed') },
        { content: { label: 'Заглушить на 1 час' }, onClick: () => setStatus('dismissed') },
        { content: { label: 'Удалить' }, onClick: () => setStatus('dismissed') },
      ]}
    />
  );
}

Props

Types

PropsNotificationCardProps
PropTypeDefaultRequiredDescription
actionsAction[]noДополнительные действия у карточки
appearance"default" | "error" | "success" | "warning"defaultnoТип уведомления
classNamestringnoCSS-класс
data-test-idstringno
datestringyesДата уведомления
descriptionReactNodenoОписание уведомления — вторичный текст под заголовком
idstringyesИдентификатор уведомления
labelstringnoЛейбл перед заголовком
linkPickLinkProps<"a", "label" | "truncateVariant" | "insideText">noСсылка (рендерится как `<a href>` через `@cloud-ru/ds-link`)
onClickMouseEventHandler<HTMLDivElement>noКолбэк клика по карточке. Делает карточку кликабельной (`role='button'`, фокус с клавиатуры). Активация по карточке срабатывает только при фокусе на самой карточке — вложенные кнопки, ссылка и меню действий активируются независимо.
onVisible((cardId: string) => void)noКолбэк при попадании карточки в область видимости на 80%
primaryButtonNotificationButtonPropsnoКнопка главного действия у карточки (рендерится как Button view='tonal')
secondaryButtonNotificationButtonPropsnoКнопка второстепенного действия у карточки (рендерится как Button view='simple')
titlestringyesЗаголовок уведомления
unreadbooleannoУправление состоянием прочитано/не прочитано

Unions

Types

NotificationCardProps

Unions

Storybook

Figma