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);
|
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.
|
* The nearby-schools section at MOBILE.md's three reference widths.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -283,6 +283,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* `position: sticky` with a z-index makes .sectionNav a stacking context, so
|
||||||
|
the jump sheet's own z-index only orders it INSIDE that context. Against the
|
||||||
|
fixed bottom tab bar (Navigation.module.css, z-index 1000) what counts is
|
||||||
|
.sectionNav's 10 — 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: above the bar, below
|
||||||
|
the comparison toast (2000), the fullscreen map (5000) and the info popover
|
||||||
|
(9999). This rule carries the z-index and nothing else; every other
|
||||||
|
declaration belongs to .sectionNav in both states. */
|
||||||
|
.sectionNavSheetOpen {
|
||||||
|
z-index: 1100;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.sectionNavBack {
|
.sectionNavBack {
|
||||||
flex: none;
|
flex: none;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|||||||
@@ -344,7 +344,10 @@ export function SchoolDetailShell({
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
{/* Sticky Section Navigation — docks under the global 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">
|
<button onClick={scrollToTop} className={styles.sectionNavBack} aria-label="Back to top">
|
||||||
<span aria-hidden="true">↑</span>
|
<span aria-hidden="true">↑</span>
|
||||||
<span className={styles.sectionNavBackLabel}>Top</span>
|
<span className={styles.sectionNavBackLabel}>Top</span>
|
||||||
|
|||||||
Reference in new issue
Block a user