Skip to content
Merged
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
12 changes: 5 additions & 7 deletions app/components/gameCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export function createGameCard(manifest, progress) {
// Show time played today if available.
const today = getTodayDateString();
if (progress.dailyTime && typeof progress.dailyTime[today] === 'number'
&& progress.dailyTime[today] > 0) {
&& progress.dailyTime[today] > 0) {
details.push(`Today: ${formatDuration(progress.dailyTime[today])}`);
}
if (details.length > 0) {
Expand All @@ -71,17 +71,15 @@ export function createGameCard(manifest, progress) {
button.textContent = `Play ${manifest.name}`;
button.setAttribute('aria-label', `Play ${manifest.name}`);

/**
* Dispatches a custom event when the game card button is clicked.
* @fires CustomEvent#game:select
*/
button.addEventListener('click', () => {

// Dispatches a game:select custom event when any part of the card is clicked.
article.addEventListener('click', () => {
const event = new CustomEvent('game:select', {
bubbles: true,
composed: true,
detail: { gameId: manifest.id },
});
button.dispatchEvent(event);
article.dispatchEvent(event);
});

article.appendChild(img);
Expand Down
16 changes: 16 additions & 0 deletions app/components/tests/gameCard.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,22 @@ describe('createGameCard', () => {
expect(firedEvent.detail.gameId).toBe(validManifest.id);
});

it('clicking the card body (outside the button) fires game:select', () => {
const card = createGameCard(validManifest);
document.body.appendChild(card);

let firedEvent = null;
card.addEventListener('game:select', (e) => {
firedEvent = e;
});

// Simulate a click directly on the article element (not via the button).
card.click();

expect(firedEvent).not.toBeNull();
expect(firedEvent.detail.gameId).toBe(validManifest.id);
});

it('<img> has a non-empty alt attribute', () => {
const card = createGameCard(validManifest);
const img = card.querySelector('img');
Expand Down
7 changes: 7 additions & 0 deletions app/styles/game-card.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,19 @@
overflow: hidden;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
transition: box-shadow var(--transition-fast);
cursor: pointer;
}

.game-card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
}

/* Show a card-level focus ring when the Play button inside receives keyboard focus. */
.game-card:focus-within {
outline: 3px solid var(--focus-ring);
outline-offset: 2px;
}

.game-card img {
width: 100%;
aspect-ratio: 1 / 1;
Expand Down
Loading