Skip to content

Commit

Permalink
cleanup and improvements of artifact selection
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisinajar committed Feb 25, 2025
1 parent 96afdcf commit 9c9601b
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 77 deletions.
13 changes: 8 additions & 5 deletions src/components/artifact-selection-box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,35 @@ type ArtifactSelectionBoxProps = {
isSelected: boolean;
title: string;
onSelect: () => void;
disabled?: boolean;
};

export function ArtifactSelectionBox({
artifact,
isSelected,
title,
onSelect
onSelect,
disabled = false
}: ArtifactSelectionBoxProps): JSX.Element {
return (
<Paper
sx={{
flex: 1,
p: 2,
cursor: 'pointer',
cursor: disabled ? 'not-allowed' : 'pointer',
transition: 'all 0.2s',
border: '2px solid',
borderColor: isSelected ? 'primary.main' : 'divider',
bgcolor: isSelected ? 'action.selected' : 'background.paper',
opacity: disabled ? 0.5 : 1,
'&:hover': {
bgcolor: 'action.hover',
bgcolor: disabled ? 'background.paper' : 'action.hover',
},
}}
elevation={isSelected ? 8 : 1}
onClick={onSelect}
onClick={disabled ? undefined : onSelect}
>
<Typography variant="h6" gutterBottom>{title}</Typography>
<Typography variant="h6" gutterBottom data-testid={`${title.toLowerCase().replace(/\s+/g, '-')}-title`}>{title}</Typography>
{artifact ? (
<React.Fragment>
<Typography variant="subtitle1">{artifact.name}</Typography>
Expand Down
101 changes: 46 additions & 55 deletions src/components/chat/artifact-modal.test.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import React from "react";
import { render } from "@testing-library/react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";
import { MockedProvider } from "@apollo/client/testing";
import { ArtifactModal } from "./artifact-modal";
import { MeDocument, ArtifactAttributeType } from "src/generated/graphql";
import { ArtifactAttributeType } from "src/generated/graphql";

const mockArtifact = {
const equippedArtifact = {
__typename: "ArtifactItem" as const,
id: "test-id",
id: "current-id",
owner: "test-owner",
name: "Test Artifact",
name: "Super Equipped Test Artifact",
level: 1,
attributes: {
__typename: "ArtifactAttributes" as const,
Expand All @@ -22,71 +22,62 @@ const mockArtifact = {
},
};

const mocks = [
{
request: {
query: MeDocument,
},
result: {
data: {
me: {
account: {
hero: {
equipment: {
artifact: {
...mockArtifact,
id: "current-id",
name: "Current Artifact",
},
},
},
},
},
},
},
const newArtifact = {
__typename: "ArtifactItem" as const,
id: "test-id",
owner: "test-owner",
name: "Goofy Test Artifact of Testing",
level: 1,
attributes: {
__typename: "ArtifactAttributes" as const,
namePrefix: { __typename: "ArtifactAttribute" as const, type: ArtifactAttributeType.BonusPhysicalDamage, magnitude: 1 },
namePostfix: { __typename: "ArtifactAttribute" as const, type: ArtifactAttributeType.BonusHealth, magnitude: 1 },
bonusAffixes: [],
titlePrefix: null,
titlePostfix: null,
},
];
};

describe("ArtifactModal", () => {
it("renders both artifacts and allows selection", async () => {
it("handles artifact selection and confirmation flow when current artifact exists", async () => {
const user = userEvent.setup();
const { getByText } = render(
<MockedProvider mocks={mocks}>
<ArtifactModal artifact={mockArtifact} />

render(
<MockedProvider>
<ArtifactModal currentArtifact={equippedArtifact} newArtifact={newArtifact} />
</MockedProvider>
);

// Check that both artifacts are rendered
expect(getByText("Current Artifact")).toBeInTheDocument();
expect(getByText("Test Artifact")).toBeInTheDocument();

// Continue button should be disabled initially
const continueButton = getByText("Continue with Selected");
expect(continueButton).toBeDisabled();
// Initial state: both artifacts rendered, button disabled
expect(screen.getByText(equippedArtifact.name)).toBeInTheDocument();
expect(screen.getByText(newArtifact.name)).toBeInTheDocument();
expect(screen.getByRole('button', { name: /continue with selected/i })).toBeDisabled();

// Select new artifact
await user.click(getByText("Test Artifact"));
expect(continueButton).toBeEnabled();
// Select new artifact and verify button enables
await user.click(screen.getByText(newArtifact.name));
expect(screen.getByRole('button', { name: /continue with selected/i })).toBeEnabled();

// Open confirmation dialog
await user.click(continueButton);
expect(getByText("Confirm Your Choice")).toBeInTheDocument();
expect(getByText(/Are you sure you want to keep the new artifact/)).toBeInTheDocument();
// Open and verify confirmation dialog with warning
await user.click(screen.getByRole('button', { name: /continue with selected/i }));
expect(screen.getByText("Are you sure you want to keep the new artifact?")).toBeInTheDocument();
expect(screen.getByText("This action cannot be undone.")).toBeInTheDocument();
});

it("shows warning message in confirmation dialog", async () => {
it("automatically selects new artifact when no current artifact exists", async () => {
const user = userEvent.setup();
const { getByText } = render(
<MockedProvider mocks={mocks}>
<ArtifactModal artifact={mockArtifact} />
render(
<MockedProvider>
<ArtifactModal currentArtifact={null} newArtifact={newArtifact} />
</MockedProvider>
);

// Select new artifact and open confirmation
await user.click(getByText("Test Artifact"));
await user.click(getByText("Continue with Selected"));
// Verify initial state
expect(screen.getByText("You have no artifact equipped. The new artifact will be automatically equipped.")).toBeInTheDocument();
expect(screen.getByRole('button', { name: /accept new artifact/i })).toBeEnabled();

// Check warning message
expect(getByText("This action cannot be undone.")).toBeInTheDocument();
// Open and verify confirmation dialog
await user.click(screen.getByRole('button', { name: /accept new artifact/i }));
expect(screen.getByText("Are you sure you want to equip the new artifact?")).toBeInTheDocument();
expect(screen.queryByText("This action cannot be undone.")).not.toBeInTheDocument();
});
});
40 changes: 24 additions & 16 deletions src/components/chat/artifact-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,29 @@ import Button from "@mui/material/Button";

import {
ArtifactItem,
useMeQuery,
useAcceptArtifactMutation,
useRejectArtifactMutation,
} from "src/generated/graphql";
import { ArtifactSelectionBox } from "../artifact-selection-box";
import { ConfirmationDialog } from "../confirmation-dialog";

type ArtifactModalProps = {
currentArtifact: ArtifactItem | null;
newArtifact: ArtifactItem;
};

export function ArtifactModal({
artifact,
}: {
artifact: ArtifactItem;
}): JSX.Element {
currentArtifact,
newArtifact,
}: ArtifactModalProps): JSX.Element {
const [showConfirm, setShowConfirm] = useState(false);
const [selectedArtifact, setSelectedArtifact] = useState<"current" | "new" | null>(null);
const { data } = useMeQuery();
const [selectedArtifact, setSelectedArtifact] = useState<"current" | "new" | null>(
currentArtifact ? null : "new"
);

const [acceptArtifact] = useAcceptArtifactMutation();
const [rejectArtifact] = useRejectArtifactMutation();

const currentArtifact = data?.me?.account?.hero?.equipment?.artifact;

const handleConfirm = async () => {
if (selectedArtifact === "new") {
await acceptArtifact();
Expand All @@ -39,20 +42,23 @@ export function ArtifactModal({
<Box sx={{ textAlign: "center", mb: 4 }}>
<Typography variant="h4" gutterBottom>Choose Your Artifact</Typography>
<Typography variant="body1" color="text.secondary" gutterBottom>
Select which artifact you want to keep. This choice cannot be undone.
{currentArtifact
? "Select which artifact you want to keep. This choice cannot be undone."
: "You have no artifact equipped. The new artifact will be automatically equipped."}
</Typography>
</Box>

<Box sx={{ display: "flex", gap: 4, mb: 4 }}>
<ArtifactSelectionBox
artifact={currentArtifact ?? null}
artifact={currentArtifact}
isSelected={selectedArtifact === "current"}
title="Current Artifact"
onSelect={() => setSelectedArtifact("current")}
onSelect={() => currentArtifact && setSelectedArtifact("current")}
disabled={!currentArtifact}
/>

<ArtifactSelectionBox
artifact={artifact}
artifact={newArtifact}
isSelected={selectedArtifact === "new"}
title="New Artifact"
onSelect={() => setSelectedArtifact("new")}
Expand All @@ -67,15 +73,17 @@ export function ArtifactModal({
onClick={() => setShowConfirm(true)}
sx={{ minWidth: 200 }}
>
Continue with Selected
{currentArtifact ? "Continue with Selected" : "Accept New Artifact"}
</Button>
</Box>

<ConfirmationDialog
open={showConfirm}
title="Confirm Your Choice"
message={`Are you sure you want to keep the ${selectedArtifact === "new" ? "new" : "current"} artifact?`}
warningMessage="This action cannot be undone."
message={currentArtifact
? `Are you sure you want to keep the ${selectedArtifact === "new" ? "new" : "current"} artifact?`
: "Are you sure you want to equip the new artifact?"}
warningMessage={currentArtifact ? "This action cannot be undone." : undefined}
onConfirm={handleConfirm}
onCancel={() => setShowConfirm(false)}
/>
Expand Down
3 changes: 2 additions & 1 deletion src/components/chat/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,8 @@ export function Chat(): JSX.Element {
>
{pendingArtifact && (
<ArtifactModal
artifact={pendingArtifact}
currentArtifact={meData?.me?.account?.hero?.equipment?.artifact ?? null}
newArtifact={pendingArtifact}
/>
)}
</Box>
Expand Down

0 comments on commit 9c9601b

Please sign in to comment.