refactor: share source sync status presentation
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import { MantineProvider } from "@mantine/core";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { SourceSyncStatus, type SourceSyncStatusProps } from "./SourceSyncStatus";
|
||||
|
||||
function renderStatus(props: SourceSyncStatusProps) {
|
||||
render(
|
||||
<MantineProvider>
|
||||
<SourceSyncStatus {...props} />
|
||||
</MantineProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
describe("SourceSyncStatus", () => {
|
||||
it("renders nothing without source data", () => {
|
||||
renderStatus({});
|
||||
|
||||
expect(screen.queryByRole("status")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/Данные/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders the source timestamp", () => {
|
||||
renderStatus({ lastSuccessfulAt: "2026-02-03T10:15:00.000Z" });
|
||||
|
||||
expect(screen.getByText(/Данные на/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("announces the caller-provided error text as a status", () => {
|
||||
renderStatus({ errorText: "Обновление не удалось" });
|
||||
|
||||
expect(screen.getByRole("status")).toHaveTextContent("Обновление не удалось");
|
||||
});
|
||||
|
||||
it("keeps the notices next to the timestamp they belong to", () => {
|
||||
renderStatus({
|
||||
errorText: "Обновление не удалось",
|
||||
lastSuccessfulAt: "2026-02-03T10:15:00.000Z",
|
||||
stale: true,
|
||||
});
|
||||
|
||||
expect(screen.getByText(/Данные на/)).toBeInTheDocument();
|
||||
expect(screen.getByText("Данные требуют обновления")).toBeInTheDocument();
|
||||
expect(screen.getByRole("status")).toHaveTextContent("Обновление не удалось");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { FC } from "react";
|
||||
|
||||
import { Text } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { formatSnapshotTime } from "@/shared/lib";
|
||||
|
||||
// Owns the "when did the source last answer" line and the two notices that
|
||||
// always travel with it. Surfaces keep their own busy indicator: a list card
|
||||
// shows a muted line, a page header a progress bar — that is a product call.
|
||||
type SourceSyncStatusProps = {
|
||||
errorText?: null | string;
|
||||
lastSuccessfulAt?: null | string;
|
||||
stale?: boolean;
|
||||
};
|
||||
|
||||
const SourceSyncStatus: FC<SourceSyncStatusProps> = (props) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
{!!props.lastSuccessfulAt && (
|
||||
<Text c="dimmed" size="xs">
|
||||
{t("investment.sourceTime", {
|
||||
date: formatSnapshotTime(props.lastSuccessfulAt),
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{!!props.stale && <Text size="xs">{t("investment.stale")}</Text>}
|
||||
|
||||
{!!props.errorText && <Text role="status">{props.errorText}</Text>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export { SourceSyncStatus, type SourceSyncStatusProps };
|
||||
@@ -0,0 +1 @@
|
||||
export { SourceSyncStatus } from "./SourceSyncStatus/SourceSyncStatus";
|
||||
Reference in New Issue
Block a user