Skip to content

Commit

Permalink
#302 Add confirmation for deleting specification (#306)
Browse files Browse the repository at this point in the history
  • Loading branch information
nevendyulgerov authored Jan 20, 2025
1 parent 59b60d9 commit 72ec8fd
Show file tree
Hide file tree
Showing 2 changed files with 230 additions and 101 deletions.
246 changes: 145 additions & 101 deletions apps/web/src/components/specification/SpecificationListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
Flex,
Grid,
Group,
Modal,
SegmentedControl,
Skeleton,
Stack,
Expand All @@ -20,6 +21,7 @@ import {
useMantineTheme,
VisuallyHidden,
} from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { cond, filter, isEmpty, propEq, range, T } from "ramda";
import { isNilOrEmpty, isNotNilOrEmpty } from "ramda-adjunct";
import React, { FC, useState } from "react";
Expand Down Expand Up @@ -303,6 +305,10 @@ export const SpecificationListView: FC = () => {
useSpecification();

const specifications = listSpecifications();
const [opened, { open, close }] = useDisclosure(false);
const [specForRemoval, setSpecForRemoval] = useState<Specification | null>(
null,
);

if (fetching) return <Feedback />;
if (isNilOrEmpty(specifications)) return <NoSpecifications />;
Expand All @@ -313,113 +319,151 @@ export const SpecificationListView: FC = () => {
});

return (
<Stack>
<Flex justify="stretch">
<Group mr="auto">
<SegmentedControl
data-testid="specification-filter-control"
data={[
{ value: "all", label: "All" },
{ value: JSON_ABI, label: "JSON ABI" },
{ value: ABI_PARAMS, label: "ABI Params" },
]}
value={filter}
onChange={(value) => setFilter(value as ModeFilter)}
/>
<NewSpecificationButton />
<SpecificationsActionsMenu />
<>
<Modal
opened={opened}
onClose={close}
title="Delete specification?"
centered
>
<Text>
This will delete the data for this specification. Are you
sure you want to proceed?
</Text>

<Group mt="xl" justify="flex-end">
<Button variant="default" onClick={close}>
Cancel
</Button>
<Button
onClick={() => {
if (specForRemoval) {
removeSpecification(specForRemoval.id!);
}
close();
}}
>
Confirm
</Button>
</Group>
</Flex>
</Modal>

{isNilOrEmpty(filteredSpecs) && (
<NoSpecificationsFiltered
filterName={filter}
quantity={specifications?.length ?? 0}
/>
)}

<Grid justify="flex-start" align="stretch" data-testid="specs-grid">
{filteredSpecs?.map((spec, idx) => (
<Grid.Col span={{ base: 12, md: 6 }} key={spec.id}>
<Card
style={{ minHeight: CARD_MIN_HEIGHT }}
data-testid={`specification-${spec.id}-card`}
>
<Card.Section
inheritPadding
py="sm"
data-testid={`specification-${spec.id}`}
<Stack>
<Flex justify="stretch">
<Group mr="auto">
<SegmentedControl
data-testid="specification-filter-control"
data={[
{ value: "all", label: "All" },
{ value: JSON_ABI, label: "JSON ABI" },
{ value: ABI_PARAMS, label: "ABI Params" },
]}
value={filter}
onChange={(value) => setFilter(value as ModeFilter)}
/>
<NewSpecificationButton />
<SpecificationsActionsMenu />
</Group>
</Flex>

{isNilOrEmpty(filteredSpecs) && (
<NoSpecificationsFiltered
filterName={filter}
quantity={specifications?.length ?? 0}
/>
)}

<Grid
justify="flex-start"
align="stretch"
data-testid="specs-grid"
>
{filteredSpecs?.map((spec, idx) => (
<Grid.Col span={{ base: 12, md: 6 }} key={spec.id}>
<Card
style={{ minHeight: CARD_MIN_HEIGHT }}
data-testid={`specification-${spec.id}-card`}
>
<Group justify="space-between" wrap="nowrap">
<Title
order={3}
lineClamp={1}
title={spec.name}
<Card.Section
inheritPadding
py="sm"
data-testid={`specification-${spec.id}`}
>
<Group
justify="space-between"
wrap="nowrap"
>
{spec.name}
</Title>
<Group gap={0} wrap="nowrap">
<EditSpecificationButton
id={spec.id!}
iconSize={theme.other.iconSize}
/>
<Button
aria-label={`remove-${spec.name}`}
role="button"
size="compact-sm"
variant="transparent"
color="red"
data-testid={`remove-specification-${spec.id}`}
onClick={() =>
removeSpecification(spec.id!)
}
<Title
order={3}
lineClamp={1}
title={spec.name}
>
<TbTrash
size={theme.other.iconSize}
{spec.name}
</Title>
<Group gap={0} wrap="nowrap">
<EditSpecificationButton
id={spec.id!}
iconSize={theme.other.iconSize}
/>
<VisuallyHidden>
Remove specification id{" "}
{spec.id}
</VisuallyHidden>
</Button>
<Button
aria-label={`remove-${spec.name}`}
role="button"
size="compact-sm"
variant="transparent"
color="red"
data-testid={`remove-specification-${spec.id}`}
onClick={() => {
setSpecForRemoval(spec);
open();
}}
>
<TbTrash
size={theme.other.iconSize}
/>
<VisuallyHidden>
Remove specification id{" "}
{spec.id}
</VisuallyHidden>
</Button>
</Group>
</Group>
</Group>
</Card.Section>
<Badge>
{spec.mode === "abi_params"
? "ABI Parameters"
: "Json ABI"}
</Badge>

<Accordion
variant="default"
chevronPosition="right"
py="sm"
data-testid={`specification-${spec.id}-accordion`}
>
{spec.mode === "json_abi" && (
<DisplayABI abi={spec.abi} />
)}

{spec.mode === "abi_params" && (
<>
<DisplayABIParams
abiParams={spec.abiParams}
sliceTarget={spec.sliceTarget}
/>
<DisplayInstructions
slices={spec.sliceInstructions}
/>
</>
)}
<DisplayConditional
conditionals={spec.conditionals ?? []}
/>
</Accordion>
</Card>
</Grid.Col>
))}
</Grid>
</Stack>
</Card.Section>
<Badge>
{spec.mode === "abi_params"
? "ABI Parameters"
: "Json ABI"}
</Badge>

<Accordion
variant="default"
chevronPosition="right"
py="sm"
data-testid={`specification-${spec.id}-accordion`}
>
{spec.mode === "json_abi" && (
<DisplayABI abi={spec.abi} />
)}

{spec.mode === "abi_params" && (
<>
<DisplayABIParams
abiParams={spec.abiParams}
sliceTarget={spec.sliceTarget}
/>
<DisplayInstructions
slices={spec.sliceInstructions}
/>
</>
)}
<DisplayConditional
conditionals={spec.conditionals ?? []}
/>
</Accordion>
</Card>
</Grid.Col>
))}
</Grid>
</Stack>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
getByText,
render,
screen,
waitFor,
waitForElementToBeRemoved,
} from "@testing-library/react";
import { clone } from "ramda";
Expand Down Expand Up @@ -46,6 +47,85 @@ describe("Specification Listing View", () => {
expect(screen.getByText("Import specifications")).toBeInTheDocument();
});

it("should open the confirmation modal when clicking on the trash icon", async () => {
render(
<StatefulView
specs={[erc1155JSONABISpecStub, systemSpecificationAsList[0]]}
/>,
);

await waitForElementToBeRemoved(
screen.getByTestId("fetching-feedback"),
);

expect(
screen.getByText(erc1155JSONABISpecStub.name),
).toBeInTheDocument();

expect(
screen.getByText(systemSpecificationAsList[0].name),
).toBeInTheDocument();

fireEvent.click(
screen.getByTestId(
`remove-specification-${erc1155JSONABISpecStub.id}`,
),
);

await waitFor(() => screen.getByText("Delete specification?"));
expect(
screen.getByText(
"This will delete the data for this specification. Are you sure you want to proceed?",
),
).toBeInTheDocument();
});

it("should close the confirmation modal when clicking on cancel button", async () => {
render(
<StatefulView
specs={[erc1155JSONABISpecStub, systemSpecificationAsList[0]]}
/>,
);

await waitForElementToBeRemoved(
screen.getByTestId("fetching-feedback"),
);

expect(
screen.getByText(erc1155JSONABISpecStub.name),
).toBeInTheDocument();

expect(
screen.getByText(systemSpecificationAsList[0].name),
).toBeInTheDocument();

fireEvent.click(
screen.getByTestId(
`remove-specification-${erc1155JSONABISpecStub.id}`,
),
);

await waitFor(() => screen.getByText("Delete specification?"));
expect(
screen.getByText(
"This will delete the data for this specification. Are you sure you want to proceed?",
),
).toBeInTheDocument();

const cancelButton = screen.getByText("Cancel");
fireEvent.click(cancelButton);

await waitFor(() =>
expect(() => screen.getByText("Delete specification?")).toThrow(
"Unable to find an element with the text: Delete specification?",
),
);

expect(
screen.getByText(systemSpecificationAsList[0].name),
).toBeInTheDocument();
});

it("should be able to delete a specification", async () => {
render(
<StatefulView
Expand All @@ -71,6 +151,11 @@ describe("Specification Listing View", () => {
),
);

await waitFor(() => screen.getByText("Delete specification?"));

const confirmButton = screen.getByText("Confirm");
fireEvent.click(confirmButton);

await waitForElementToBeRemoved(
screen.getByText(erc1155JSONABISpecStub.name),
);
Expand Down

0 comments on commit 72ec8fd

Please sign in to comment.