PriceSummary

Итоговая цена для checkout и конфигураторов: заголовок с периодом (ButtonDropdown), базовая сумма, скидки, аккордеон деталей заказа, состояния loading / dataError и ссылка на расчёт.

Демо

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

  • Страница заказа или боковая панель с полной детализацией цены.
  • Нужен выбор периода биллинга (period + periodOptions) и callback onPeriodChanged.
  • Требуется раскрываемый список позиций заказа (invoice) со скидками по строкам.

Когда не нужен PriceSummary:

  • Достаточно одной строки «Итого» без аккордеона и периода — PriceSummarySmall.
  • Только выпадающий список периода без блока цены — ButtonDropdown.

Анатомия

Корневой контейнер с acrylic-фоном (@cloud-ru/ds-materials) и внутренними секциями:

HeaderBlock

Строка «Итого» + период. При нескольких periodOptions рендерится ButtonDropdown (desktop — Droplist, mobile — modal). При одном периоде — статический текст.

ContentBlock

Обёртка контента с состояниями:

  • loading — спиннер вместо контента.
  • dataError — сообщение и кнопка retry (onRetry).
  • иначе — дочерние блоки ниже.

DiscountBlock

Опционально при discount: базовая цена и строки скидок с процентами.

TotalValueBlock

Крупная сумма (value), префикс totalSumType='from', подсказки (hint, hintTooltipText), дельта (valueDelta).

InvoiceBlock

Опционально при invoice: аккордеон «Детали заказа» с секциями и позициями.

Function-ссылка внизу блока (по умолчанию текст из locale).

Установка

pnpm add @cloud-ru/ds-uikit-product-price-summary
import { PRICE_PERIOD, PriceSummary } from '@cloud-ru/ds-uikit-product-price-summary'

Базовый пример

<PriceSummary
  value={9_999_999.99}
  period={PRICE_PERIOD.Month}
  periodOptions={[PRICE_PERIOD.Month, PRICE_PERIOD.Year]}
  onPeriodChanged={setPeriod}
  promoBadge={{ text: '−9%' }}
  discount={{
    price: 9_999_999.99,
    discounts: [
      { value: 1000, percent: 9 },
      { value: 1000, percent: 9 },
    ],
  }}
  hint='Дополнительная информация'
  invoice={/* …секции детализации… */}
  docsLink={{ href: 'https://example.com', text: 'Подробнее о тарифах и ценах' }}
/>

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

Со скидкой

Со скидкойБазовая цена и строки скидок.
tsx
import { PRICE_PERIOD, PriceSummary } from '@cloud-ru/ds-uikit-product-price-summary';

import styles from '../demoSurface.module.scss';

export function WithDiscount() {
  return (
    <div className={styles.surface}>
      <PriceSummary
        value={10800}
        period={PRICE_PERIOD.Month}
        periodOptions={[PRICE_PERIOD.Month]}
        discount={{
          price: 12000,
          discounts: [{ value: 1200, percent: 10 }],
        }}
      />
    </div>
  );
}

С детализацией заказа

С детализацией заказаАккордеон с invoice-секциями.
tsx
import { PRICE_PERIOD } from '@cloud-ru/ds-uikit-product-price-summary';

import styles from '../demoSurface.module.scss';
import { PriceSummaryControlled } from '../PriceSummaryControlled';

export function WithInvoice() {
  return (
    <div className={styles.surface}>
      <PriceSummaryControlled
        value={10800}
        period={PRICE_PERIOD.Month}
        periodOptions={[PRICE_PERIOD.Month, PRICE_PERIOD.Year]}
        invoice={[
          {
            title: 'Compute',
            items: [{ label: 'vCPU', price: 8000, primary: true }],
          },
        ]}
      />
    </div>
  );
}

Props

Types

PropsPriceSummaryProps
PropTypeDefaultRequiredDescription
classNamestringnoДополнительный класс корневого контейнера.
data-test-idstringno
dataErrorbooleanno
discountDiscountDetailsnoБлок базовой цены и скидок.
docsLink{ href?: string; label?: string; } | undefinednoСсылка «Подробнее о расчёте».
hintstringno
hintAppearance"default" | "systemError" | "userError" | "warning"no
hintLink{ href?: string; label: string; }no
hintTooltipTextReactNodeno
invoiceInvoiceDetails[]noСекции детализации заказа в аккордеоне.
invoiceExpandedDefaultbooleantruenoНачальное состояние раскрытия аккордеона invoice.
loadingbooleanno
onPeriodChanged((period: PricePeriod) => void)no
onRetry(() => void)no
period"day" | "hour" | "minute" | "month" | "year"yes
periodOptionsPricePeriod[]yes
promoBadgestring | Pick<PromoTagProps, "label" | "appearance">no
showHintLinkbooleanno
showHintTooltipbooleanfalseno
totalSumType"equal" | "from"no
valuenumberno
valueDeltaPriceDeltaDetailsno
vatType"excluding" | "including"no

Unions

Types

PriceSummaryProps

Unions

Адаптивность

PriceSummary — адаптивный компонент: вёрстка перестраивается под mobile, а вложенные адаптивные блоки сами переключают поверхность. Раскладку он берёт из AdaptiveProvider (контекст @cloud-ru/ds-adaptive); публичный API единый для обеих платформ:

  • desktop (по умолчанию) — выбор периода открывается в Droplist, подсказки QuestionTooltip всплывают по наведению.
  • mobile — выбор периода открывается в modal, а подсказки QuestionTooltip раскрываются по нажатию в BottomSheet.

Верстайте под desktop и поставьте один <AdaptiveProvider> в корне приложения — mobile-перестроение включается автоматически (desktop-first). Пропа layoutType у компонента нет: источник раскладки — только контекст.

Как форсировать платформу

Форс — только контекстом, не пропом:

  • Поддерево — вложенный провайдер:
    import { AdaptiveProvider } from '@cloud-ru/ds-adaptive'
    
    <AdaptiveProvider layoutType='mobile'>
      <PriceSummary value={value} period={period} periodOptions={periodOptions} />
    </AdaptiveProvider>
  • Отдельный компонент — withLayoutType (module-scope, сахар над провайдером):
    import { withLayoutType } from '@cloud-ru/ds-adaptive'
    import { PriceSummary } from '@cloud-ru/ds-uikit-product-price-summary'
    
    const MobilePriceSummary = withLayoutType(PriceSummary, 'mobile')

Mobile

MobileРаскладка форсирована в mobile: выбор периода открывается в modal, подсказки — в BottomSheet.

Total

VAT included

Base price
12 000,00 ₽

Discount -10%

-1 200,00 ₽

10 800,00 ₽

Дополнительная информация

tsx
import { AdaptiveProvider, LAYOUT_TYPE } from '@cloud-ru/ds-adaptive';
import { PRICE_PERIOD, PriceSummary } from '@cloud-ru/ds-uikit-product-price-summary';

export function MobileLayout() {
  return (
    <div style={{ maxWidth: 360 }}>
      <AdaptiveProvider layoutType={LAYOUT_TYPE.Mobile}>
        <PriceSummary
          value={10800}
          period={PRICE_PERIOD.Month}
          periodOptions={[PRICE_PERIOD.Month, PRICE_PERIOD.Year]}
          discount={{
            price: 12000,
            discounts: [{ value: 1200, percent: 10 }],
          }}
          hint='Дополнительная информация'
          hintTooltipText='Итоговая сумма с учётом скидки за выбранный период.'
        />
      </AdaptiveProvider>
    </div>
  );
}

Подробнее о модели адаптивности — Адаптивность — паттерн.

Storybook

Figma