From bf16d1e046545554071029141997096517e26d61 Mon Sep 17 00:00:00 2001 From: Rei Kurata Date: Wed, 19 Aug 2026 20:33:22 -0700 Subject: [PATCH 1/2] fix(frontend): restore hover/click on Community rail rows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The playdate-badge commit (848a5f5) wrapped each row in a plain div with an absolutely-positioned full-row Link underneath, but also gave the avatar and name/city content their own `relative z-0`. Same z-index + later DOM order meant those decorative elements painted (and hit-tested) above the link, so clicking the avatar or the family name — the two most obvious click targets — hit inert spans instead of navigating, and the cursor never showed a hand over them. Swap `relative z-0` for `pointer-events-none` on the decorative avatar/ name content so clicks and hover fall through to the underlying link, and add `pointer-events-auto` to the nested Playdate badge link so it stays independently clickable above the row link. Verified with a standalone Playwright repro of the exact before/after CSS: before, elementFromPoint at the name's coordinates resolved to the name div (cursor: auto) and a real click there produced no navigation; after, it resolves to the row link (cursor: pointer) and the click fires correctly, with the Playdate badge unaffected in both cases. Adds frontend/e2e/home-community-rail.spec.ts covering direct clicks on the name and the avatar, plus the pointer-cursor affordance. --- frontend/e2e/home-community-rail.spec.ts | 45 ++++++++++++++++++++++++ frontend/src/pages/Home.tsx | 8 ++--- 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 frontend/e2e/home-community-rail.spec.ts diff --git a/frontend/e2e/home-community-rail.spec.ts b/frontend/e2e/home-community-rail.spec.ts new file mode 100644 index 0000000..64d1b1b --- /dev/null +++ b/frontend/e2e/home-community-rail.spec.ts @@ -0,0 +1,45 @@ +import { test, expect } from '@playwright/test'; +import { loginAs } from './utils/login'; + +test.describe('home community rail', () => { + test.beforeEach(async ({ page }) => { + await loginAs(page, 'anderson@dummy.test'); + await page.goto('/'); + }); + + test('clicking directly on a family name navigates to their profile', async ({ page }) => { + const name = page.getByText('The Chen Family', { exact: true }); + await expect(name).toBeVisible(); + + const box = await name.boundingBox(); + if (!box) throw new Error('Could not measure the family name element'); + await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2); + + await expect(page.getByRole('heading', { name: /Chen/i })).toBeVisible(); + }); + + test('clicking directly on a family avatar navigates to their profile', async ({ page }) => { + const row = page.locator('li', { hasText: 'The Davis Family' }); + const avatar = row.locator('span[aria-hidden="true"]'); + await expect(avatar).toBeVisible(); + + const box = await avatar.boundingBox(); + if (!box) throw new Error('Could not measure the avatar element'); + await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2); + + await expect(page.getByRole('heading', { name: /Davis/i })).toBeVisible(); + }); + + test('the whole row shows a pointer cursor, including over the name text', async ({ page }) => { + const name = page.getByText('The Brooks Family', { exact: true }); + const box = await name.boundingBox(); + if (!box) throw new Error('Could not measure the family name element'); + + const cursor = await page.evaluate(([x, y]) => { + const el = document.elementFromPoint(x, y); + return el ? getComputedStyle(el).cursor : null; + }, [box.x + box.width / 2, box.y + box.height / 2]); + + expect(cursor).toBe('pointer'); + }); +}); diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index 2c50822..5dc3e6d 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -109,17 +109,17 @@ export default function HomePage() { ) : ( )} - + {fam.name} @@ -133,7 +133,7 @@ export default function HomePage() { 🗓 Playdate From 61b8a2ad8aa0c1c9ebf079f59f07de223dd81d2d Mon Sep 17 00:00:00 2001 From: Rei Kurata Date: Wed, 19 Aug 2026 20:36:41 -0700 Subject: [PATCH 2/2] fix(frontend): match either avatar image or initials fallback in e2e test seed-dummy.ts backfilled dicebear avatars onto the dummy families after this branch was cut, so the Davis family row may now render an instead of the aria-hidden initials span this test originally assumed. --- frontend/e2e/home-community-rail.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/e2e/home-community-rail.spec.ts b/frontend/e2e/home-community-rail.spec.ts index 64d1b1b..9ffaeb3 100644 --- a/frontend/e2e/home-community-rail.spec.ts +++ b/frontend/e2e/home-community-rail.spec.ts @@ -20,7 +20,9 @@ test.describe('home community rail', () => { test('clicking directly on a family avatar navigates to their profile', async ({ page }) => { const row = page.locator('li', { hasText: 'The Davis Family' }); - const avatar = row.locator('span[aria-hidden="true"]'); + // Seeded families may render either a real avatar image or the initials + // fallback — match whichever is actually there. + const avatar = row.locator('img, span[aria-hidden="true"]').first(); await expect(avatar).toBeVisible(); const box = await avatar.boundingBox();