fix(web): lift the mobile jump sheet above the bottom tab bar #154
No files matched your search
@@ -2826,6 +2826,52 @@ test('nearby schools link on to other schools and into compare', async ({ page }
|
||||
expect(Math.abs(offsetAfter - offsetBefore)).toBeLessThanOrEqual(8);
|
||||
});
|
||||
|
||||
/**
|
||||
* Every section in the mobile jump sheet can actually be reached.
|
||||
*
|
||||
* The sheet is a fixed bottom sheet, and the app has a fixed bottom tab bar.
|
||||
* `position: sticky` with a z-index on the sticky nav makes it a stacking
|
||||
* context, so the sheet's own z-index orders it only within that context —
|
||||
* against the tab bar, the nav's value is what counts. The last item in the
|
||||
* sheet was therefore painted over and untappable as soon as the list grew
|
||||
* long enough to reach the bar, which adding "Nearby schools" is what did.
|
||||
*
|
||||
* Bounding boxes are not enough to catch this: the item is in the viewport and
|
||||
* the right size, it is simply underneath something. So this asks the question
|
||||
* a thumb asks — what is on top at this point.
|
||||
*/
|
||||
test('every section in the mobile jump sheet is tappable, not under the tab bar', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await searchByName(page, 'Primary');
|
||||
await schoolLinks(page).first().click();
|
||||
await page.waitForURL(/\/school\//);
|
||||
|
||||
// Scroll down so the sticky nav is docked and the sheet has somewhere to open.
|
||||
await page.evaluate(() => window.scrollTo({ top: 1200 }));
|
||||
// Two controls carry aria-haspopup: the mobile "Section" button and the
|
||||
// desktop "All" one, which is display:none here but still in the DOM.
|
||||
await page.locator('[aria-haspopup="menu"]:visible').click();
|
||||
|
||||
const sheet = page.locator('[role="menu"]');
|
||||
await expect(sheet).toBeVisible();
|
||||
|
||||
const covered = await sheet.evaluate((panel: HTMLElement) =>
|
||||
Array.from(panel.querySelectorAll('[role="menuitem"]'))
|
||||
.map((el) => {
|
||||
const box = el.getBoundingClientRect();
|
||||
const hit = document.elementFromPoint(
|
||||
Math.round(box.left + box.width / 2),
|
||||
Math.round(box.top + box.height / 2),
|
||||
);
|
||||
return { label: (el as HTMLElement).innerText.trim().replace(/\s+/g, ' '), reachable: !!(hit && hit.closest('[role="menuitem"]')) };
|
||||
})
|
||||
.filter((item) => !item.reachable)
|
||||
.map((item) => item.label),
|
||||
);
|
||||
|
||||
expect(covered).toEqual([]);
|
||||
});
|
||||
|
||||
/**
|
||||
* The nearby-schools section at MOBILE.md's three reference widths.
|
||||
*
|
||||
|
||||
@@ -270,11 +270,23 @@
|
||||
position: sticky;
|
||||
top: 64px; /* global header height on desktop */
|
||||
z-index: 10;
|
||||
/* `position: sticky` with a z-index makes this a stacking context, so the
|
||||
jump sheet's own z-index only orders it INSIDE here. Against the fixed
|
||||
bottom tab bar (Navigation.module.css, z-index 1000) what counts is this
|
||||
value — which is why the sheet's last item was painted over, and
|
||||
untappable, once the list grew long enough to reach the bar. Lifted only
|
||||
while the sheet is open, and only to 1100: below the comparison toast
|
||||
(2000), the fullscreen map (5000) and the info popover (9999). */
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-top: none;
|
||||
border-radius: 0 0 10px 10px;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
|
||||
.sectionNavSheetOpen {
|
||||
z-index: 1100;
|
||||
margin-bottom: 1rem;
|
||||
box-shadow: 0 2px 4px rgba(var(--shadow-rgb), 0.04);
|
||||
display: flex;
|
||||
|
||||
@@ -344,7 +344,10 @@ export function SchoolDetailShell({
|
||||
</header>
|
||||
|
||||
{/* Sticky Section Navigation — docks under the global header */}
|
||||
<nav className={styles.sectionNav} aria-label="Page sections">
|
||||
<nav
|
||||
className={`${styles.sectionNav}${sectionsOpen ? ` ${styles.sectionNavSheetOpen}` : ''}`}
|
||||
aria-label="Page sections"
|
||||
>
|
||||
<button onClick={scrollToTop} className={styles.sectionNavBack} aria-label="Back to top">
|
||||
<span aria-hidden="true">↑</span>
|
||||
<span className={styles.sectionNavBackLabel}>Top</span>
|
||||
|
||||
Reference in new issue
Block a user