Skip to content
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
3 changes: 3 additions & 0 deletions .lycheeignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ dist/(.*/)?404((/index)?\.html|/)$
# Oracle pages return 403 when connected
^https://www\.mysql\.com/
^https://www\.oracle\.com/

# NVD pages time out when requested from CI runners
^https://nvd\.nist\.gov/
10 changes: 10 additions & 0 deletions docs/design-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,13 @@ Colors are defined using OKLCH color space with `light-dark()` for theme switchi
| `xs` | < 768px | `--xs-only` |
| `md` | 768px – 1439px | `--md-only`, `--md-up` |
| `lg` | >= 1440px | `--lg-up`, `--lg-down` |

## Doc Bottom Navigation

`<DocBottomNav>` closes a doc page with links to the previous and next page in a single
flex row. The row uses `flex-wrap: wrap-reverse`, so when the two titles cannot sit side
by side the _Next_ link wraps onto its own line above _Previous_ instead of overflowing
the page. On wider screens both links stay side by side on one line.

The DOM order stays previous-then-next, so keyboard and screen reader order is unchanged
when the row wraps.
4 changes: 4 additions & 0 deletions src/components/patterns/DocBottomNav/DocBottomNav.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
@layer patterns {
.doc-nav {
display: flex;
/* Wrap in reverse so that when the two titles cannot sit side by side the
"Next" link moves onto its own line above "Previous" instead of
overflowing the page on narrow viewports. */
flex-wrap: wrap-reverse;
justify-content: space-between;
gap: var(--space-4);
margin-top: var(--space-8);
Expand Down
54 changes: 54 additions & 0 deletions tests/e2e/doc-bottom-nav.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { test, expect } from '@playwright/test';

// Regression coverage for https://github.com/expressjs/expressjs.com/issues/2486
const DOC_PATH = '/en/guide/migrating-4/';

test.describe('Doc bottom navigation', () => {
test('should not overflow horizontally on a narrow viewport', async ({ page }) => {
await page.setViewportSize({ width: 400, height: 900 });
await page.goto(DOC_PATH);

const nav = page.locator('.doc-nav');
await expect(nav).toBeVisible();

const overflow = await nav.evaluate((el) => el.scrollWidth - el.clientWidth);
expect(overflow).toBeLessThanOrEqual(1);
});

test('should move the next link above the previous link when they cannot share a line', async ({
page,
}) => {
await page.setViewportSize({ width: 400, height: 900 });
await page.goto(DOC_PATH);

const prev = page.locator('.doc-nav__link--prev');
const next = page.locator('.doc-nav__link--next');

await expect(prev).toBeVisible();
await expect(next).toBeVisible();

const prevBox = await prev.boundingBox();
const nextBox = await next.boundingBox();

if (!prevBox || !nextBox) {
throw new Error('expected both doc nav links to be laid out');
}

expect(nextBox.y + nextBox.height).toBeLessThanOrEqual(prevBox.y);
});

test('should keep both links on a single line on a wide viewport', async ({ page }) => {
await page.setViewportSize({ width: 1400, height: 900 });
await page.goto(DOC_PATH);

const prevBox = await page.locator('.doc-nav__link--prev').boundingBox();
const nextBox = await page.locator('.doc-nav__link--next').boundingBox();

if (!prevBox || !nextBox) {
throw new Error('expected both doc nav links to be laid out');
}

expect(nextBox.y).toBeCloseTo(prevBox.y, 0);
expect(nextBox.x).toBeGreaterThan(prevBox.x);
});
});
Loading