feat(portfolio): extend instrument logo rendering to bonds
- Rename presentation prop stock→wide; BOND section passes wide alongside STOCK - Add bond test cases: CDN logo, fallback, signed return, single-column grid - Add other-type regression test (no logo, labeled return preserved) - Update bundle baseline (+4 bytes)
This commit is contained in:
@@ -123,7 +123,8 @@ describe("AccountPage states", () => {
|
||||
.map((element) => element.textContent);
|
||||
expect(positions).toEqual(["Вторая", "Первая"]);
|
||||
expect(screen.getByText("10 ₽")).toBeInTheDocument();
|
||||
expect(screen.getByText("Доход: -1 ₽")).toBeInTheDocument();
|
||||
expect(screen.getByText("-1 ₽")).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Доход:/)).not.toBeInTheDocument();
|
||||
expect(screen.getByText("Средняя цена: 11 ₽")).toBeInTheDocument();
|
||||
expect(screen.getByText("Доля: 100 %")).toBeInTheDocument();
|
||||
expect(screen.getByText("RU000A0JX0J2")).toBeInTheDocument();
|
||||
@@ -173,6 +174,96 @@ describe("AccountPage states", () => {
|
||||
expect(screen.queryByRole("img", { name: "Абра" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders bond cards with logo, fallback, and signed unlabeled return like stocks", () => {
|
||||
renderPage(
|
||||
<AccountPageLoaded
|
||||
portfolio={{
|
||||
...portfolio,
|
||||
positions: [
|
||||
{
|
||||
figi: "FIGI-C",
|
||||
displayName: "Долг",
|
||||
type: "BOND",
|
||||
quantity: "5",
|
||||
currentValue: { currency: "RUB", value: "50" },
|
||||
averagePurchasePrice: null,
|
||||
absoluteReturn: { currency: "RUB", value: "2" },
|
||||
isin: "RU000C0XY1Z3",
|
||||
logoName: "dolg.png",
|
||||
},
|
||||
{
|
||||
figi: "FIGI-D",
|
||||
displayName: "Купон",
|
||||
type: "BOND",
|
||||
quantity: "1",
|
||||
currentValue: null,
|
||||
averagePurchasePrice: null,
|
||||
absoluteReturn: { currency: "RUB", value: "0" },
|
||||
isin: null,
|
||||
logoName: null,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByRole("img", { name: "Долг" })).toHaveAttribute(
|
||||
"src",
|
||||
"https://invest-brands.cdn-tinkoff.ru/dolgx160.png",
|
||||
);
|
||||
expect(screen.getByText("К")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole("img", { name: "Купон" }),
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.getByText("+2 ₽")).toBeInTheDocument();
|
||||
expect(screen.getByText("0 ₽")).toBeInTheDocument();
|
||||
expect(screen.queryByText(/Доход:/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the bonds section as a single-column grid on any screen width", () => {
|
||||
renderPage(
|
||||
<AccountPageLoaded
|
||||
portfolio={{
|
||||
...portfolio,
|
||||
positions: [portfolio.positions[1]],
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
const singleColumnGrids = Array.from(document.querySelectorAll("style"))
|
||||
.map((style) => (style.textContent ?? "").replace(/\s/g, ""))
|
||||
.filter(
|
||||
(css) =>
|
||||
css.includes("--sg-cols:1") &&
|
||||
!css.includes("@media") &&
|
||||
!/--sg-cols:[23]/.test(css),
|
||||
);
|
||||
expect(singleColumnGrids).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("keeps other-position cards compact with labeled return and no logo", () => {
|
||||
renderPage(
|
||||
<AccountPageLoaded
|
||||
portfolio={{
|
||||
...portfolio,
|
||||
positions: [
|
||||
{
|
||||
figi: "FIGI-E",
|
||||
displayName: "Фонд",
|
||||
type: "FUND",
|
||||
quantity: "3",
|
||||
currentValue: { currency: "RUB", value: "30" },
|
||||
averagePurchasePrice: null,
|
||||
absoluteReturn: { currency: "RUB", value: "-5" },
|
||||
isin: null,
|
||||
logoName: "fond.png",
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText("Доход: -5 ₽")).toBeInTheDocument();
|
||||
expect(screen.queryByRole("img", { name: "Фонд" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("uses the cached account title when available", () => {
|
||||
renderPage(
|
||||
<AccountPageLoaded
|
||||
|
||||
@@ -191,7 +191,7 @@ export function AccountPageLoaded({
|
||||
title={t("portfolio.stocks")}
|
||||
positions={stocks}
|
||||
denominators={denominators}
|
||||
stock
|
||||
wide
|
||||
/>
|
||||
) : null}
|
||||
{bonds.length ? (
|
||||
@@ -199,6 +199,7 @@ export function AccountPageLoaded({
|
||||
title={t("portfolio.bonds")}
|
||||
positions={bonds}
|
||||
denominators={denominators}
|
||||
wide
|
||||
/>
|
||||
) : null}
|
||||
{other.length ? (
|
||||
@@ -353,22 +354,22 @@ function PositionSection({
|
||||
title,
|
||||
positions,
|
||||
denominators,
|
||||
stock = false,
|
||||
wide = false,
|
||||
}: {
|
||||
title: string;
|
||||
positions: Portfolio["positions"];
|
||||
denominators: Map<string, number>;
|
||||
stock?: boolean;
|
||||
wide?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<Section title={title}>
|
||||
<SimpleGrid cols={stock ? 1 : { base: 1, sm: 2, md: 3 }}>
|
||||
<SimpleGrid cols={wide ? 1 : { base: 1, sm: 2, md: 3 }}>
|
||||
{positions.map((position) => (
|
||||
<PositionCard
|
||||
key={`${position.figi}-${position.displayName}-${position.quantity}`}
|
||||
position={position}
|
||||
denominators={denominators}
|
||||
stock={stock}
|
||||
wide={wide}
|
||||
/>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
@@ -378,11 +379,11 @@ function PositionSection({
|
||||
function PositionCard({
|
||||
position,
|
||||
denominators,
|
||||
stock = false,
|
||||
wide = false,
|
||||
}: {
|
||||
position: Portfolio["positions"][number];
|
||||
denominators: Map<string, number>;
|
||||
stock?: boolean;
|
||||
wide?: boolean;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const share = position.currentValue?.currency
|
||||
@@ -399,17 +400,17 @@ function PositionCard({
|
||||
: undefined
|
||||
: undefined;
|
||||
const returnText = position.absoluteReturn
|
||||
? stock
|
||||
? wide
|
||||
? `${Number(position.absoluteReturn.value) > 0 ? "+" : ""}${formatMoney(position.absoluteReturn)}`
|
||||
: t("portfolio.return", {
|
||||
value: formatMoney(position.absoluteReturn),
|
||||
})
|
||||
: null;
|
||||
const logoSrc = stock ? instrumentLogoUrl(position.logoName) : null;
|
||||
const logoSrc = wide ? instrumentLogoUrl(position.logoName) : null;
|
||||
return (
|
||||
<Card className={classes.tile} withBorder>
|
||||
<Group justify="space-between" align="flex-start" wrap="nowrap">
|
||||
{stock ? (
|
||||
{wide ? (
|
||||
<PositionLogo
|
||||
key={logoSrc ?? "no-logo"}
|
||||
src={logoSrc}
|
||||
|
||||
Reference in New Issue
Block a user