Skip to content

Card art accessibility improvements #114

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions src/components/CardArt.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,24 @@ const showErrorOverlay = computed(() => {
<template>
<div class="art-frame">
<div v-if="showErrorOverlay" class="overlay error">
<div class="shape-x"></div>
<div>
<div class="shape-x"></div>
<p>There was an error loading the next card. You should skip this one.</p>
</div>
</div>
<div v-else-if="showLoadingOverlay" class="overlay loading">
<LoadingPulser class="pulser" />
<p class="vh">Loading next card</p>
</div>
<div class="flavor-mask" v-if="card.flavor_name">Hint: this print has a flavor name.</div>
<img alt="" class="vh preload" :src="imageUri" :onload="onLoad" :onerror="onError" />
<img
alt=""
class="vh preload"
:src="imageUri"
:onload="onLoad"
:onerror="onError"
data-testid="card-art-preload"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needed to add a data-testid here to target it in the test to trigger the load and error events.

/>
<img :src="imageUri" />
</div>
</template>
Expand Down Expand Up @@ -74,12 +85,15 @@ $default-art-width: 626px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both the style changes here are to make the text and x in error overlay be centered.

padding: 1rem;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added some padding here because the class/saga art crops don't give you much space and the text was butting up against the edge.


background: rgba(0 0 0 / 0.5);
}

.shape-x {
color: var(--c-salmon);
margin: auto;
}

img {
Expand Down
167 changes: 167 additions & 0 deletions src/components/__test__/CardArt.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import { fireEvent, render, screen } from "@testing-library/vue";
import CardArt from "../CardArt.vue";
import { LoadingStatus } from "../../store";
import { CardBank } from "../../models/__test__/card-bank.util";

describe("CardArt", () => {
it("shows an error overlay if loadingNext prop is Failed", async () => {
const { rerender } = render(CardArt, {
props: {
card: CardBank.ArborElf,
loadingNext: LoadingStatus.Success,
},
});
expect(
screen.queryByText("There was an error loading the next card. You should skip this one.")
).not.toBeInTheDocument();

await rerender({
loadingNext: LoadingStatus.Failed,
});
expect(
screen.queryByText("There was an error loading the next card. You should skip this one.")
).toBeInTheDocument();
});

it("shows an error overlay if the card image fails to load", async () => {
render(CardArt, {
props: {
card: CardBank.ArborElf,
loadingNext: LoadingStatus.Success,
},
});
expect(
screen.queryByText("There was an error loading the next card. You should skip this one.")
).not.toBeInTheDocument();

await fireEvent.error(screen.getByTestId("card-art-preload"));

expect(
screen.queryByText("There was an error loading the next card. You should skip this one.")
).toBeInTheDocument();
});

it("starts out with a loading overlay until image loads", async () => {
render(CardArt, {
props: {
card: CardBank.ArborElf,
loadingNext: LoadingStatus.Success,
},
});

expect(screen.queryByText("Loading next card")).toBeInTheDocument();

await fireEvent.load(screen.getByTestId("card-art-preload"));
expect(screen.queryByText("Loading next card")).not.toBeInTheDocument();
});

it("shows a loading overlay if loadingNext prop is Pending", async () => {
const { rerender } = render(CardArt, {
props: {
card: CardBank.ArborElf,
loadingNext: LoadingStatus.Success,
},
});
await fireEvent.load(screen.getByTestId("card-art-preload"));

expect(screen.queryByText("Loading next card")).not.toBeInTheDocument();

await rerender({
loadingNext: LoadingStatus.Pending,
});
expect(screen.queryByText("Loading next card")).toBeInTheDocument();
});

it("shows a loading overlay when the card image uri changes", async () => {
const { rerender } = render(CardArt, {
props: {
card: CardBank.ArborElf,
loadingNext: LoadingStatus.Success,
},
});
await fireEvent.load(screen.getByTestId("card-art-preload"));

expect(screen.queryByText("Loading next card")).not.toBeInTheDocument();

await rerender({
card: CardBank.Alesha,
});
expect(screen.getByText("Loading next card")).toBeInTheDocument();
});

it("prefers error overlay over loading overlay if both would apply", async () => {
render(CardArt, {
props: {
card: CardBank.ArborElf,
loadingNext: LoadingStatus.Pending,
},
});
await fireEvent.error(screen.getByTestId("card-art-preload"));

expect(screen.queryByText("Loading next card")).not.toBeInTheDocument();
expect(
screen.queryByText("There was an error loading the next card. You should skip this one.")
).toBeInTheDocument();
});

it("uses art crop for a card with a top level image uris object", async () => {
render(CardArt, {
props: {
card: CardBank.ArborElf,
loadingNext: LoadingStatus.Success,
},
});

// just a double check that this card does have an art crop
expect(CardBank.ArborElf.image_uris?.art_crop).toBeTruthy();
expect(screen.getByTestId("card-art-preload")).toHaveAttribute(
"src",
CardBank.ArborElf.image_uris?.art_crop
);
});

it("uses a random face from a double sided card", async () => {
const mathSpy = vi.spyOn(Math, "random").mockReturnValue(0.3);
const { rerender } = render(CardArt, {
props: {
card: CardBank.HeartOfTheExplorer,
loadingNext: LoadingStatus.Success,
},
});

// just a double check that this card does have an art crop
const expectedFirstFaceArtCrop = CardBank.HeartOfTheExplorer.card_faces[0].image_uris?.art_crop;
expect(expectedFirstFaceArtCrop).toBeTruthy();
expect(screen.getByTestId("card-art-preload")).toHaveAttribute("src", expectedFirstFaceArtCrop);

mathSpy.mockReturnValue(0.7);
await rerender({
card: CardBank.DraculaLordOfBlood,
});

// just a double check that this card does have an art crop
const expectedSecondFaceArtCrop =
CardBank.HeartOfTheExplorer.card_faces[1].image_uris?.art_crop;
expect(expectedSecondFaceArtCrop).toBeTruthy();
expect(screen.getByTestId("card-art-preload")).toHaveAttribute(
"src",
expectedSecondFaceArtCrop
);
});

it("provides a flavor mask for cards that have a flavor name", async () => {
const { rerender } = render(CardArt, {
props: {
card: CardBank.ArborElf,
loadingNext: LoadingStatus.Success,
},
});

expect(screen.queryByText("Hint: this print has a flavor name.")).not.toBeInTheDocument();

await rerender({
card: CardBank.Eleven,
});
expect(screen.queryByText("Hint: this print has a flavor name.")).toBeInTheDocument();
});
});
2 changes: 1 addition & 1 deletion src/models/__test__/card.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function makeCard<T extends ScryfallCard>(overrides: Partial<T> = {}): T
scryfall_uri: "<scryfall uri>",
type_line: "<type line>",
set: "<set code>",
image_uris: makeImageUris(),
image_uris: makeImageUris(overrides.name),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is so that the image uri does change for the computed property, so the watch on the imageUri does trigger.

...overrides,
};
return card as T;
Expand Down