Consume the provided resource-envelope API snapshot. Preserve financial snapshots during refresh, observe synchronization without importing, and fence late responses across account and connection changes.
229 lines
6.1 KiB
TypeScript
229 lines
6.1 KiB
TypeScript
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
import { MemoryRouter } from "react-router";
|
|
import type { BrokerageAccount } from "../../api/useBrokerageAccounts";
|
|
import { brokerageAccountCards } from "../../model/display";
|
|
import { BrokerageAccountList } from "./BrokerageAccountList";
|
|
|
|
const syntheticAccount = {
|
|
id: "storybook-account",
|
|
name: "Учебный брокерский счёт",
|
|
type: "BROKERAGE",
|
|
openedOn: "2025-03-15",
|
|
portfolioSummary: {
|
|
stockPositionCount: 5,
|
|
bondPositionCount: 12,
|
|
totalsByCurrency: [
|
|
{ currency: "RUB", value: "125000.50" },
|
|
{ currency: "USD", value: "320" },
|
|
{ currency: "EUR", value: "210" },
|
|
],
|
|
},
|
|
} satisfies BrokerageAccount;
|
|
|
|
const meta = {
|
|
title: "Product/BrokerageAccountList",
|
|
component: BrokerageAccountList,
|
|
tags: ["autodocs"],
|
|
argTypes: {
|
|
accounts: { control: false },
|
|
},
|
|
decorators: [
|
|
(Story) => (
|
|
<MemoryRouter initialEntries={["/accounts"]}>
|
|
<Story />
|
|
</MemoryRouter>
|
|
),
|
|
],
|
|
parameters: {
|
|
docs: {
|
|
description: {
|
|
component:
|
|
"Карточки счетов WealthLedger со сводкой по валютам и числом наименований акций и облигаций. Используйте на экране выбора счёта; отсутствие сводки и отсутствие сумм имеют разные сообщения. Примеры содержат вымышленные данные. Ссылки меняют только локальный маршрут MemoryRouter; страница назначения в этой истории не отображается.",
|
|
},
|
|
},
|
|
},
|
|
args: { accounts: brokerageAccountCards([syntheticAccount]) },
|
|
} satisfies Meta<typeof BrokerageAccountList>;
|
|
|
|
export default meta;
|
|
|
|
type Story = StoryObj<typeof meta>;
|
|
|
|
export const Available: Story = {
|
|
name: "Сводка по нескольким валютам",
|
|
parameters: {
|
|
docs: {
|
|
description: {
|
|
story:
|
|
"Показываются первые две валюты и количество остальных. Суммы разных валют не складываются в общий итог.",
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
export const Unavailable: Story = {
|
|
name: "Сводка недоступна",
|
|
args: {
|
|
accounts: brokerageAccountCards([
|
|
{ ...syntheticAccount, portfolioSummary: null, openedOn: null },
|
|
]),
|
|
},
|
|
parameters: {
|
|
docs: {
|
|
description: {
|
|
story:
|
|
"Счёт остаётся доступен для открытия, даже если сводка временно недоступна. Неизвестные суммы не заменяются нулём; отсутствующая дата открытия не отображается.",
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
export const Empty: Story = {
|
|
name: "Нет текущих сумм",
|
|
args: {
|
|
accounts: brokerageAccountCards([
|
|
{
|
|
...syntheticAccount,
|
|
portfolioSummary: {
|
|
totalsByCurrency: [],
|
|
stockPositionCount: 0,
|
|
bondPositionCount: 0,
|
|
},
|
|
},
|
|
]),
|
|
},
|
|
parameters: {
|
|
docs: {
|
|
description: {
|
|
story:
|
|
"Успешно полученная пустая сводка отличается от ошибки получения данных. Карточка сохраняет название, тип счёта и переход.",
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
export const LongName: Story = {
|
|
name: "Длинное название счёта",
|
|
args: {
|
|
accounts: brokerageAccountCards([
|
|
{
|
|
...syntheticAccount,
|
|
name: "Учебный индивидуальный инвестиционный счёт для долгосрочных накоплений",
|
|
type: "INDIVIDUAL_INVESTMENT",
|
|
},
|
|
]),
|
|
},
|
|
parameters: {
|
|
docs: {
|
|
description: {
|
|
story:
|
|
"Проверьте перенос длинного названия на узком экране и доступность всей карточки с клавиатуры.",
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
export const AccountGrid: Story = {
|
|
name: "Несколько счетов",
|
|
args: {
|
|
accounts: brokerageAccountCards([
|
|
{
|
|
...syntheticAccount,
|
|
name: "Основной",
|
|
portfolioSummary: {
|
|
totalsByCurrency: [{ currency: "RUB", value: "1256430.50" }],
|
|
stockPositionCount: 8,
|
|
bondPositionCount: 14,
|
|
},
|
|
},
|
|
{
|
|
...syntheticAccount,
|
|
id: "storybook-iis",
|
|
name: "Долгосрочные инвестиции",
|
|
type: "INDIVIDUAL_INVESTMENT",
|
|
portfolioSummary: {
|
|
totalsByCurrency: [{ currency: "RUB", value: "480250" }],
|
|
stockPositionCount: 0,
|
|
bondPositionCount: 6,
|
|
},
|
|
},
|
|
]),
|
|
},
|
|
};
|
|
|
|
export const UnvaluedPositions: Story = {
|
|
name: "Бумаги без текущей оценки",
|
|
args: {
|
|
accounts: brokerageAccountCards([
|
|
{
|
|
...syntheticAccount,
|
|
portfolioSummary: {
|
|
totalsByCurrency: [],
|
|
stockPositionCount: 2,
|
|
bondPositionCount: 1,
|
|
},
|
|
},
|
|
]),
|
|
},
|
|
};
|
|
|
|
export const LargeValues: Story = {
|
|
name: "Длинные суммы",
|
|
args: {
|
|
accounts: brokerageAccountCards([
|
|
{
|
|
...syntheticAccount,
|
|
portfolioSummary: {
|
|
totalsByCurrency: [
|
|
{ currency: "RUB", value: "1234567890123456789.50" },
|
|
{ currency: "USD", value: "-9876543210987654321.50" },
|
|
],
|
|
stockPositionCount: 21,
|
|
bondPositionCount: 105,
|
|
},
|
|
},
|
|
]),
|
|
},
|
|
};
|
|
|
|
export const IndependentFreshness: Story = {
|
|
name: "Независимая свежесть и ошибка сводок",
|
|
args: {
|
|
accounts: brokerageAccountCards(
|
|
[
|
|
syntheticAccount,
|
|
{
|
|
...syntheticAccount,
|
|
id: "waiting",
|
|
name: "Новый счёт",
|
|
portfolioSummary: null,
|
|
},
|
|
],
|
|
[
|
|
{
|
|
resourceKey: "portfolio/storybook-account",
|
|
dataRevision: "1",
|
|
stateVersion: "failed",
|
|
availability: "complete",
|
|
freshness: "stale",
|
|
lastSuccessfulAt: "2026-09-24T10:30:00Z",
|
|
sync: {
|
|
id: "failed",
|
|
state: "failed",
|
|
errorCode: "TBANK_UNAVAILABLE",
|
|
},
|
|
},
|
|
{
|
|
resourceKey: "portfolio/waiting",
|
|
dataRevision: "0",
|
|
stateVersion: "waiting",
|
|
availability: "missing",
|
|
freshness: "unknown",
|
|
lastSuccessfulAt: null,
|
|
sync: { id: "waiting", state: "running", errorCode: null },
|
|
},
|
|
],
|
|
),
|
|
},
|
|
};
|