feat: add quiet reading with rare confirmations
Part of the quiet-user-feedback change; docs and specs land in their own repositories.
This commit is contained in:
@@ -256,21 +256,19 @@ describe("CardPage", () => {
|
||||
}),
|
||||
);
|
||||
fireEvent.click(screen.getByRole("button", { name: "Далее" }));
|
||||
await waitFor(() =>
|
||||
expect(screen.getByRole("button", { name: "Далее" })).toBeDisabled(),
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("alert")).not.toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText("Перевод на «Запасная»")).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "Изменить" })).toBeDisabled();
|
||||
expect(
|
||||
screen.queryByLabelText("Загрузка операций"),
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByLabelText("Загрузка операций")).not.toBeInTheDocument();
|
||||
expect(scroll).not.toHaveBeenCalled();
|
||||
measure.mockReturnValue({ top: 200 } as DOMRect);
|
||||
release();
|
||||
await screen.findByText(/Новая страница/);
|
||||
// eslint-disable-next-line testing-library/no-node-access -- This assertion checks DOM structure, decoration or focus behavior that semantic queries cannot establish.
|
||||
const list = screen.getByText(/Новая страница/).closest("[aria-busy]");
|
||||
expect(list?.parentElement?.style.minHeight).toBe("");
|
||||
const newPageText = screen.getByText(/Новая страница/);
|
||||
expect(newPageText).toBeInTheDocument();
|
||||
|
||||
expect(scroll).toHaveBeenCalledExactlyOnceWith({
|
||||
behavior: "instant",
|
||||
top: -400,
|
||||
@@ -317,7 +315,9 @@ describe("CardPage", () => {
|
||||
it("keeps card and journal when summary fails", async () => {
|
||||
setup({ summaryError: true });
|
||||
await screen.findByRole("heading", { name: "Повседневная" });
|
||||
await screen.findByRole("alert");
|
||||
expect(
|
||||
screen.queryByRole("alert"),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByText("Перевод на «Запасная»"),
|
||||
).toBeInTheDocument();
|
||||
@@ -331,7 +331,9 @@ describe("CardPage", () => {
|
||||
http.get("*/api/v1/bank-cards/a/operations", () => HttpResponse.error()),
|
||||
);
|
||||
await client.invalidateQueries();
|
||||
await screen.findAllByRole("alert");
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("alert")).not.toBeInTheDocument();
|
||||
});
|
||||
expect(
|
||||
screen.getByRole("heading", { name: "Повседневная" }),
|
||||
).toBeInTheDocument();
|
||||
@@ -401,9 +403,9 @@ describe("CardPage", () => {
|
||||
it("recovers the prerequisite card list while preserving the visible balance and journal", async () => {
|
||||
setup({ cardsError: true });
|
||||
await screen.findByText("Перевод на «Запасная»");
|
||||
expect(await screen.findByRole("alert")).toHaveTextContent(
|
||||
"Не удалось загрузить данные карт.",
|
||||
);
|
||||
expect(
|
||||
screen.queryByRole("alert"),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("button", { name: "Добавить операцию" }),
|
||||
).toBeDisabled();
|
||||
@@ -412,11 +414,6 @@ describe("CardPage", () => {
|
||||
HttpResponse.json([card, { ...card, id: "b", name: "Запасная" }]),
|
||||
),
|
||||
);
|
||||
fireEvent.click(
|
||||
within(screen.getByRole("alert")).getByRole("button", {
|
||||
name: "Повторить",
|
||||
}),
|
||||
);
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
screen.getByRole("button", { name: "Добавить операцию" }),
|
||||
|
||||
@@ -75,7 +75,6 @@ const CardPageView: FC<CardPageViewProps> = (props) => {
|
||||
{t("bank.back")}
|
||||
</Button>
|
||||
|
||||
{!vm.notFound && <Button onClick={vm.retryCard}>{t("bank.retry")}</Button>}
|
||||
</Group>
|
||||
)}
|
||||
error
|
||||
@@ -125,27 +124,16 @@ const CardPageView: FC<CardPageViewProps> = (props) => {
|
||||
</div>
|
||||
|
||||
{!!vm.archiveError && (
|
||||
<BankFeedback error={vm.archiveError} retry={vm.retryCard} />
|
||||
<BankFeedback error={vm.archiveError} />
|
||||
)}
|
||||
|
||||
{card.archivedAt && <Text>{t("bank.archiveHint")}</Text>}
|
||||
</Stack>
|
||||
|
||||
{vm.cardRefreshError && (
|
||||
<Group role="alert">
|
||||
<Text>{t("bank.refreshError")}</Text>
|
||||
|
||||
<Button onClick={vm.retryCard}>{t("bank.retry")}</Button>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
{vm.cardRefreshing && <Text role="status">{t("bank.refreshing")}</Text>}
|
||||
|
||||
{vm.cardsFeedback && (
|
||||
<BankFeedback
|
||||
{...vm.cardsFeedback}
|
||||
loadingError
|
||||
retry={vm.retryCards}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -187,7 +175,6 @@ const CardPageView: FC<CardPageViewProps> = (props) => {
|
||||
</Group>
|
||||
|
||||
<Stack
|
||||
aria-busy={vm.history.refreshing}
|
||||
className={classes.operations}
|
||||
data-pending={vm.history.changingPage || undefined}
|
||||
gap="lg"
|
||||
@@ -196,13 +183,7 @@ const CardPageView: FC<CardPageViewProps> = (props) => {
|
||||
<CardHistory history={vm.history} period={vm.period} rows={vm.rows} />
|
||||
</Stack>
|
||||
|
||||
{vm.history.error && (
|
||||
<Group role="alert">
|
||||
<Text>{t(vm.rows ? "bank.refreshError" : "bank.error")}</Text>
|
||||
|
||||
<Button onClick={vm.history.retry}>{t("bank.retry")}</Button>
|
||||
</Group>
|
||||
)}
|
||||
{vm.history.error && !vm.rows && <Text role="alert">{t("bank.error")}</Text>}
|
||||
|
||||
{vm.history.page !== undefined && vm.pageSize !== "all" && (
|
||||
<Group
|
||||
@@ -213,7 +194,6 @@ const CardPageView: FC<CardPageViewProps> = (props) => {
|
||||
ref={paginationRef}
|
||||
>
|
||||
<Pagination
|
||||
disabled={vm.history.refreshing}
|
||||
getControlProps={getControlProps}
|
||||
getItemProps={getItemProps}
|
||||
onChange={onPageChange}
|
||||
|
||||
@@ -115,8 +115,6 @@ function useCardPageVm() {
|
||||
error: history.isError,
|
||||
loading: history.isLoading,
|
||||
page: history.data?.page,
|
||||
refreshing: history.isFetching,
|
||||
retry: () => void history.refetch(),
|
||||
},
|
||||
notFound: isApiErrorCode(
|
||||
apiErrorCode(card.error),
|
||||
@@ -128,8 +126,6 @@ function useCardPageVm() {
|
||||
page,
|
||||
pageSize,
|
||||
period,
|
||||
retryCard: () => void card.refetch(),
|
||||
retryCards: () => void cards.refetch(),
|
||||
rows,
|
||||
setPage: (value: number) =>
|
||||
setParameters({ ...period, limit: pageSize, page: String(value) }),
|
||||
@@ -146,7 +142,6 @@ function useCardPageVm() {
|
||||
error: summary.isError,
|
||||
fetching: summary.isFetching,
|
||||
loading: summary.isLoading,
|
||||
retry: () => void summary.refetch(),
|
||||
},
|
||||
toggleArchive: () => {
|
||||
if (!card.data) return;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
@@ -44,10 +45,16 @@ function useDeleteOperationVm(props: DeleteOperationProps) {
|
||||
setError(undefined);
|
||||
|
||||
try {
|
||||
const actionId = crypto.randomUUID();
|
||||
await operationDeletion.mutateAsync({
|
||||
id: record.id,
|
||||
version: record.version,
|
||||
});
|
||||
|
||||
notifications.show({
|
||||
id: "bank-operation-deleted-" + actionId,
|
||||
message: t("bank.operationDeleted"),
|
||||
});
|
||||
setOpened(false);
|
||||
}
|
||||
catch (error_) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { useState } from "react";
|
||||
import { useForm, useWatch } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -112,6 +113,8 @@ const useOperationFormVm = (props: OperationFormProps) => {
|
||||
description: values.description,
|
||||
};
|
||||
|
||||
const actionId = crypto.randomUUID();
|
||||
|
||||
if (props.operation)
|
||||
await updateOperation.mutateAsync({
|
||||
body: { ...body, version },
|
||||
@@ -122,6 +125,11 @@ const useOperationFormVm = (props: OperationFormProps) => {
|
||||
id: intent,
|
||||
...body,
|
||||
});
|
||||
|
||||
notifications.show({
|
||||
id: "bank-operation-saved-" + actionId,
|
||||
message: t("bank.operationSaved"),
|
||||
});
|
||||
setOpened(false);
|
||||
}
|
||||
catch (error_) {
|
||||
|
||||
Reference in New Issue
Block a user