diff --git a/e2e/pageobjects/canvas.page.ts b/e2e/pageobjects/canvas.page.ts index 347b7286..ca5d60e4 100644 --- a/e2e/pageobjects/canvas.page.ts +++ b/e2e/pageobjects/canvas.page.ts @@ -137,6 +137,49 @@ export const canvas = { ); }, + /** + * What the language badge is drawn outside its band, and how many boxes the selected chip + * in the rail draws around it. + * + * ⚠️ Both come from the badge having a border of its own. The band is `overflow: hidden`, + * so a badge taller than its line box is cut; and a chip that draws its own outline around + * a badge that already has one reads as two rings rather than as a selection. + */ + badgeBoxes(): Promise<{ cutByBand: number; outlines: number } | null> { + return browser.execute(() => { + const band = document.querySelector('.card-head'); + const onCard = band?.querySelector('.lang-tag'); + const chip = document.querySelector('.language-chip.on'); + const inChip = chip?.querySelector('.lang-tag'); + if (!band || !onCard || !chip || !inChip) return null; + + // A border nobody can see is not an outline, whatever its width says. ⚠️ `transparent` + // computes to `rgba(…, 0)`, so the alpha is what decides — read by splitting rather + // than by matching, which is one escaping mistake fewer in a string sent to the page. + const drawn = (element: Element) => { + const style = getComputedStyle(element); + const channels = style.borderTopColor + .slice(style.borderTopColor.indexOf('(') + 1, style.borderTopColor.lastIndexOf(')')) + .split(','); + const alpha = channels.length > 3 ? Number(channels[3]) : 1; + + return alpha > 0 && Number.parseFloat(style.borderTopWidth) > 0 ? 1 : 0; + }; + + const bandBox = band.getBoundingClientRect(); + const badgeBox = onCard.getBoundingClientRect(); + + return { + cutByBand: Math.max( + 0, + Math.round(bandBox.top - badgeBox.top), + Math.round(badgeBox.bottom - bandBox.bottom), + ), + outlines: drawn(chip) + drawn(inChip), + }; + }); + }, + /** * How far each todo row is drawn **outside** the box that holds it, in pixels. ⚠️ Counting * the rows in the DOM is not the same question: `.card-items` is `overflow: hidden` and diff --git a/e2e/specs/06-search-and-filters.e2e.ts b/e2e/specs/06-search-and-filters.e2e.ts index 68d02961..47fbdf60 100644 --- a/e2e/specs/06-search-and-filters.e2e.ts +++ b/e2e/specs/06-search-and-filters.e2e.ts @@ -126,6 +126,25 @@ describe('Search, filters and facets', () => { expect(await $(testid('search-matched')).isExisting()).toBe(false); }); + /** + * ⚠️ Both halves of #201, and both came from the hairline #196 gave the badge. On a card + * the band is `overflow: hidden` and a `` is inline, so the badge's border hung + * outside the line box its host was sized to and was cut. In the rail the chip drew a + * second outline two pixels further out, at a different radius — two rings around one + * badge rather than a selection. + */ + it('draws the badge whole, with one outline around the selected one', async () => { + await canvas.toggleLanguage('sh'); + + const boxes = await canvas.badgeBoxes(); + + expect(boxes).not.toBeNull(); + expect(boxes?.cutByBand).toBe(0); + expect(boxes?.outlines).toBe(1); + + await canvas.toggleLanguage('sh'); + }); + it('drops the search, the tag and the language in one click', async () => { await canvas.search('Docker'); await canvas.toggleTag('ops'); diff --git a/src/app/notes/header/language-rail/language-rail.component.scss b/src/app/notes/header/language-rail/language-rail.component.scss index 3e4a9ced..dd1be54e 100644 --- a/src/app/notes/header/language-rail/language-rail.component.scss +++ b/src/app/notes/header/language-rail/language-rail.component.scss @@ -31,11 +31,15 @@ flex-shrink: 0; } -.language-chip:hover { +// ⚠️ The chip's own box stays invisible: the badge carries a hairline of its own since +// #196, and a second outline two pixels further out — at a different radius — read as two +// rings around one badge rather than as a selection. The padding stays, because it is what +// the chip is clickable by. +.language-chip:hover .lang-tag { border-color: var(--line); } -.language-chip.on { +.language-chip.on .lang-tag { border-color: var(--amber); background: var(--bg-2); } diff --git a/src/app/notes/ui/language-badge/language-badge.component.scss b/src/app/notes/ui/language-badge/language-badge.component.scss index 30c52bc2..82d71610 100644 --- a/src/app/notes/ui/language-badge/language-badge.component.scss +++ b/src/app/notes/ui/language-badge/language-badge.component.scss @@ -1,6 +1,11 @@ @use 'mixins' as *; +// ⚠️ A box of its own, not an inline run. A `` is inline by default, so its padding +// and its border hang outside the line box its host is sized to — and `.card-head` is +// `overflow: hidden`, which cut the badge by three pixels once #196 gave it a hairline. .lang-tag { + display: inline-flex; + align-items: center; font-family: var(--font-mono), serif; font-size: 9.5px; padding: 2px 6px;