Files
houseplan-card/test/hp-dialog-contract.test.mjs
Codexandclaude[bot] e38beed796 fix: the summary panel settings dialog scrolls in Home Assistant
In HA hp-dialog renders ha-dialog, whose own `.body` is the scroller and
is not a flex container; `.summary-editor` (overflow:auto,
overscroll-behavior:contain, min-height:0) therefore grew to its content
and became a scroll container that never scrolls — Chromium stops wheel
and touch scroll chaining at such a child, so nothing moved (reproduced
on ha.jbstudio.pro, HA 2026.9.1, and in an isolated Playwright page).
hp-dialog gains an opt-in `flex-content` attribute forwarded as
ha-dialog's `flexcontent`, which lays the body out as a flex column: the
editor is height-bound again and scrolls itself, header and footer stay,
exactly as the native branch already did. Only the summary dialog opts in.

Smoke demo/smoke_summary_dialog_scroll.mjs stubs ha-dialog after the HA
2026.9 contract: wheel on desktop, touch swipe on a phone, dialog within
the viewport, and a witness that the stub reproduces the bug without
flexcontent. Docs screenshots: 11/11 pixel-identical (docs:accept
--identical, #512), fingerprint refreshed.

Mutants: summary-dialog-drops-flex-content, hp-dialog-ignores-flex-content.

Issue: #508
User-Visible: yes
2026-09-09 19:49:18 +00:00

28 lines
1.6 KiB
JavaScript

// #508: hp-dialog forwards `flex-content` to ha-dialog; the summary settings dialog asks for it.
import assert from 'node:assert/strict';
import test from 'node:test';
import { readFileSync } from 'node:fs';
const read = (rel) => readFileSync(new URL(`../src/${rel}`, import.meta.url), 'utf8');
test('#508 AC1: both ha-dialog renders forward flexcontent from the flex-content attribute', () => {
const source = read('hp-dialog.ts');
assert.match(source, /flexContent: \{ type: Boolean, reflect: true, attribute: 'flex-content' \}/);
const renders = source.match(/<ha-dialog[\s\S]*?>/g) || [];
assert.equal(renders.length, 2, 'two ha-dialog renders (with and without described-by)');
for (const render of renders) assert.match(render, /\?flexcontent=\$\{this\.flexContent\}/, render);
});
test('#508 AC1: the summary settings dialog declares flex-content', () => {
const source = read('summary-panel-editor.ts');
const open = source.match(/<hp-dialog[\s\S]*?data-kind="summary"[\s\S]*?>/)?.[0] || '';
assert.match(open, /\bflex-content\b/, open);
});
test('#508 AC4: no other dialog opts in — flex-content stays a summary-dialog contract', () => {
const files = ['space-copy-runtime.ts', 'houseplan-editor-runtime.ts', 'backdrop-pick.ts', 'decor-image-editor.ts',
'summary-panel-editor.ts', 'houseplan-card.ts', 'hp-confirm.ts', 'pdf/hp-pdf-dialog.ts', 'houseplan-onboarding-runtime.ts'];
const optIns = files.flatMap((rel) => (read(rel).match(/<hp-dialog[\s\S]*?>/g) || []).filter((tag) => /\bflex-content\b/.test(tag)));
assert.equal(optIns.length, 1, optIns.join('\n'));
});