feat(investments): warn and offer retry after exhausted read retries (WealthLedger/frontend#55) (#136)
This commit was merged in pull request #136.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import { MantineProvider } from "@mantine/core";
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { ReadRetry } from "./ReadRetry";
|
||||
|
||||
function renderRetry(props: Parameters<typeof ReadRetry>[0]) {
|
||||
return render(
|
||||
<MantineProvider>
|
||||
<ReadRetry {...props} />
|
||||
</MantineProvider>,
|
||||
);
|
||||
}
|
||||
|
||||
describe("ReadRetry", () => {
|
||||
it("renders nothing until the automatic retries are exhausted", () => {
|
||||
renderRetry({ exhausted: false, onRetry: vi.fn() });
|
||||
|
||||
expect(screen.queryByRole("status")).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("announces the failure politely and retries on activation", () => {
|
||||
const onRetry = vi.fn();
|
||||
renderRetry({ exhausted: true, onRetry });
|
||||
|
||||
expect(screen.getByRole("status")).toHaveTextContent("Не удалось обновить данные");
|
||||
fireEvent.click(screen.getByRole("button", { name: "Повторить" }));
|
||||
expect(onRetry).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("disables the action while the retry runs", () => {
|
||||
renderRetry({ exhausted: true, onRetry: vi.fn(), retrying: true });
|
||||
|
||||
expect(screen.getByRole("button", { name: "Повторить" })).toBeDisabled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import type { FC } from "react";
|
||||
|
||||
import { Alert, Button, Group, Text } from "@mantine/core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
// Shown only after the automatic read retries are exhausted. The action repeats
|
||||
// the failed GET of the affected query; it never starts a source sync.
|
||||
type ReadRetryProps = {
|
||||
exhausted: boolean;
|
||||
onRetry: () => void;
|
||||
retrying?: boolean;
|
||||
};
|
||||
|
||||
const ReadRetryButton: FC<Pick<ReadRetryProps, "onRetry" | "retrying">> = (
|
||||
props,
|
||||
) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Button disabled={props.retrying} onClick={props.onRetry} variant="light">
|
||||
{t("investment.retry")}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
const ReadRetry: FC<ReadRetryProps> = (props) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!props.exhausted) return null;
|
||||
|
||||
return (
|
||||
<Alert aria-live="polite" color="orange" role="status">
|
||||
<Group gap="sm" justify="space-between">
|
||||
<Text>{t("investment.readFailed")}</Text>
|
||||
|
||||
<ReadRetryButton onRetry={props.onRetry} retrying={props.retrying} />
|
||||
</Group>
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
export { ReadRetry, ReadRetryButton, type ReadRetryProps };
|
||||
@@ -5,4 +5,9 @@ export {
|
||||
EntryGroupTotal,
|
||||
} from "./EntryGroup/EntryGroup";
|
||||
export { PayoutCard } from "./PayoutCard/PayoutCard";
|
||||
export {
|
||||
ReadRetry,
|
||||
ReadRetryButton,
|
||||
type ReadRetryProps,
|
||||
} from "./ReadRetry/ReadRetry";
|
||||
export { SourceSyncStatus } from "./SourceSyncStatus/SourceSyncStatus";
|
||||
|
||||
Reference in New Issue
Block a user