Skip to content

Commit

Permalink
transaction capture modal improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
Cloud11PL committed Jul 31, 2024
1 parent 862e5dc commit 46c0453
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 36 deletions.
5 changes: 5 additions & 0 deletions .changeset/nine-steaks-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"saleor-dashboard": patch
---

Transaction capture modal no longer shows "Error" text when API error occurs. This means that the modal closes when mutation finishes so that result is visible.
58 changes: 58 additions & 0 deletions src/components/ButtonWithLoader/ButtonWithLoader.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { fireEvent, render, screen } from "@testing-library/react";
import React from "react";

import { ButtonWithLoader } from "./ButtonWithLoader";

jest.mock("react-intl", () => ({
useIntl: jest.fn(() => ({
formatMessage: jest.fn(x => x.defaultMessage),
})),
defineMessages: jest.fn(x => x),
FormattedMessage: ({ defaultMessage }: { defaultMessage: string }) => <>{defaultMessage}</>,
}));

describe("ConfirmButton", () => {
it("should render a button with confirm label", () => {
// Arrange & Act
render(<ButtonWithLoader transitionState="default">Confirm</ButtonWithLoader>);
// Assert
expect(screen.getByRole("button")).toBeInTheDocument();
expect(screen.getByRole("button")).toHaveTextContent("Confirm");
});

it("should render a button with loading spinner", () => {
// Arrange & Act
render(<ButtonWithLoader transitionState="loading" />);
// Assert
expect(screen.getByRole("button")).toBeInTheDocument();
expect(screen.getByTestId("button-progress")).toBeInTheDocument();
});

it("should call onClick when clicked", () => {
// Arrange
const onClick = jest.fn();

// Act
render(<ButtonWithLoader transitionState="default" onClick={onClick} />);
fireEvent.click(screen.getByRole("button"));

// Assert
expect(onClick).toHaveBeenCalled();
});

it("should render original label after loading state", async () => {
// Arrange & Act
const { rerender } = render(
<ButtonWithLoader transitionState="loading">Confirm</ButtonWithLoader>,
);

// Assert
expect(screen.getByTestId("button-progress")).toBeInTheDocument();

// Act
rerender(<ButtonWithLoader transitionState="default">Confirm</ButtonWithLoader>);

// Assert
expect(screen.queryByTestId("button-progress")).not.toBeInTheDocument();
});
});
56 changes: 56 additions & 0 deletions src/components/ButtonWithLoader/ButtonWithLoader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { buttonMessages } from "@dashboard/intl";
import { Box, Button, ButtonProps, Spinner, sprinkles } from "@saleor/macaw-ui-next";
import React from "react";
import { useIntl } from "react-intl";

import { ConfirmButtonTransitionState } from "../ConfirmButton";

interface ButtonWithLoaderProps extends ButtonProps {
transitionState: ConfirmButtonTransitionState;
}

export const ButtonWithLoader = ({
transitionState,
onClick,
disabled,
children,
...props
}: ButtonWithLoaderProps) => {
const intl = useIntl();
const isLoading = transitionState === "loading";

const renderSpinner = () => {
if (isLoading) {
return (
<Box data-test-id="button-progress" display="flex" position="absolute">
<Spinner />
</Box>
);
}

return null;
};

const getByLabelText = () => {
return children || intl.formatMessage(buttonMessages.save);
};

return (
<Button
{...props}
disabled={isLoading && disabled}
onClick={isLoading ? undefined : onClick}
data-test-state={isLoading ? "loading" : "default"}
>
{renderSpinner()}
<span
className={sprinkles({
opacity: isLoading ? "0" : "1",
transition: "ease",
})}
>
{getByLabelText()}
</span>
</Button>
);
};
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { ConfirmButton, ConfirmButtonTransitionState } from "@dashboard/components/ConfirmButton";
import { ButtonWithLoader } from "@dashboard/components/ButtonWithLoader/ButtonWithLoader";
import { ConfirmButtonTransitionState } from "@dashboard/components/ConfirmButton";
import { DashboardModal } from "@dashboard/components/Modal";
import { TransactionActionEnum } from "@dashboard/graphql";
import { buttonMessages } from "@dashboard/intl";
import { Dialog, DialogActions, DialogContent, DialogProps, DialogTitle } from "@material-ui/core";
import { Button } from "@saleor/macaw-ui";
import { Button, Text } from "@saleor/macaw-ui-next";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";

import { mapActionToMessage } from "../OrderTransaction/utils";
import { messages } from "./messages";

export interface OrderTransactionActionDialogProps extends DialogProps {
export interface OrderTransactionActionDialogProps {
open: boolean;
confirmButtonState: ConfirmButtonTransitionState;
onClose: () => void;
onSubmit: () => void;
Expand All @@ -28,29 +30,31 @@ export const OrderTransactionActionDialog: React.FC<OrderTransactionActionDialog
const actionType = actionIntl.toLowerCase();

return (
<Dialog open={open} onClose={onClose}>
<DialogTitle>
<FormattedMessage
{...messages.title}
values={{
actionType,
}}
/>
</DialogTitle>
<DialogContent>
<FormattedMessage {...messages.warningText} values={{ actionType }} />
</DialogContent>
<DialogActions>
<Button data-test-id="back" variant="secondary" color="text" onClick={onClose}>
<FormattedMessage {...buttonMessages.back} />
</Button>
<ConfirmButton
onClick={onSubmit}
labels={{ confirm: actionIntl }}
transitionState={confirmButtonState}
type="submit"
/>
</DialogActions>
</Dialog>
<DashboardModal open={open} onChange={onClose}>
<DashboardModal.Content>
<DashboardModal.Title>
<FormattedMessage
{...messages.title}
values={{
actionType,
}}
/>
</DashboardModal.Title>

<Text>
<FormattedMessage {...messages.warningText} values={{ actionType }} />
</Text>

<DashboardModal.Actions>
<Button data-test-id="back" variant="secondary" onClick={onClose}>
<FormattedMessage {...buttonMessages.back} />
</Button>

<ButtonWithLoader onClick={onSubmit} transitionState={confirmButtonState} type="submit">
{actionIntl}
</ButtonWithLoader>
</DashboardModal.Actions>
</DashboardModal.Content>
</DashboardModal>
);
};
10 changes: 6 additions & 4 deletions src/orders/views/OrderDetails/OrderNormalDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -290,10 +290,12 @@ export const OrderNormalDetails: React.FC<OrderNormalDetailsProps> = ({
open={params.action === "transaction-action"}
action={params.type}
onSubmit={() =>
orderTransactionAction.mutate({
action: params.type,
transactionId: params.id,
})
orderTransactionAction
.mutate({
action: params.type,
transactionId: params.id,
})
.finally(() => closeModal())
}
/>
<OrderMetadataDialog
Expand Down
10 changes: 6 additions & 4 deletions src/orders/views/OrderDetails/OrderUnconfirmedDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -347,10 +347,12 @@ export const OrderUnconfirmedDetails: React.FC<OrderUnconfirmedDetailsProps> = (
open={params.action === "transaction-action"}
action={params.type}
onSubmit={() =>
orderTransactionAction.mutate({
action: params.type,
transactionId: params.id,
})
orderTransactionAction
.mutate({
action: params.type,
transactionId: params.id,
})
.finally(() => closeModal())
}
/>
<OrderMetadataDialog
Expand Down

0 comments on commit 46c0453

Please sign in to comment.