From 6c3012a40b470310061735a61abeaf7610fdf965 Mon Sep 17 00:00:00 2001 From: fkwp Date: Thu, 17 Sep 2026 11:45:15 +0200 Subject: [PATCH] Line the level meter up with the device rows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Measured: the icon's centre sat 3px right of the radio controls above it. - Two causes, both off the padding: the row is inset a border width to keep the menu's frame visible, and the icon is wider than a radio control. - Vertically the icon was right and the text was not — a paragraph's bottom margin means the margin box gets centred, lifting the words. - Guarded in a story, not a unit test: jsdom lays nothing out. --- .../MicrophoneLevelMeter.module.css | 20 ++++++++++++++----- .../MicrophoneLevelMeter.stories.tsx | 11 ++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/components/MicrophoneLevelMeter.module.css b/src/components/MicrophoneLevelMeter.module.css index 04135f94d..a7ec78ccb 100644 --- a/src/components/MicrophoneLevelMeter.module.css +++ b/src/components/MicrophoneLevelMeter.module.css @@ -10,11 +10,17 @@ Please see LICENSE in the repository root for full details. align-items: center; gap: var(--cpd-space-3x); padding-block: var(--cpd-space-2x); - /* Aligned with the device rows above: a menu item reserves a trailing column - for its chevron and gives its label an end margin, so the text stops well - short of the item's own padding. Without the same inset the bars run past - where the device names end. */ - padding-inline-start: var(--cpd-space-4x); + /* The icon shares a centre with the radio controls in the device rows above, + which sit one 4x padding in from the menu's edge. Two things pull this row + out of line with them, and both come off the padding: the row is inset by a + border width so that the menu's frame stays visible behind it (see + `.stickyMeter`), and the icon is wider than a radio control, so it has to + start further left for the two to share a centre. Both are + measured in the MeterAlignsWithTheDeviceRows story rather than trusted: + jsdom lays nothing out, so only a real browser can hold this. */ + padding-inline-start: calc( + var(--cpd-space-4x) - var(--cpd-border-width-1) - 2px + ); padding-inline-end: calc(var(--cpd-space-4x) * 2 + var(--cpd-space-2x)); } @@ -67,4 +73,8 @@ Please see LICENSE in the repository root for full details. .message { color: var(--cpd-color-text-secondary); + /* A paragraph brings a margin below it, which in a centred row does not push + the text down but lifts it: the margin box is what gets centred, so the + words end up above the middle and the icon beside them looks low. */ + margin-block: 0; } diff --git a/src/components/MicrophoneLevelMeter.stories.tsx b/src/components/MicrophoneLevelMeter.stories.tsx index b18a64c6e..5ebb10da1 100644 --- a/src/components/MicrophoneLevelMeter.stories.tsx +++ b/src/components/MicrophoneLevelMeter.stories.tsx @@ -125,6 +125,17 @@ export const NoDevice: Story = { const canvas = within(canvasElement); await expect(canvas.queryByRole("meter")).toBeNull(); await expect(canvas.getByText(/No microphone found/)).toBeVisible(); + + // The icon sits on the middle of the words, however many lines they run to. + // A paragraph's own margin would centre its margin box instead, leaving the + // text high and the icon looking low beside it. + const middle = (element: Element): number => { + const box = element.getBoundingClientRect(); + return box.top + box.height / 2; + }; + const icon = canvasElement.getElementsByClassName(styles.icon)[0]; + const words = canvasElement.getElementsByClassName(styles.message)[0]; + await expect(Math.abs(middle(icon) - middle(words))).toBeLessThanOrEqual(1); }, };