From 5a5aaef37ff29f2905d6e29ad51a1d19efbb03a0 Mon Sep 17 00:00:00 2001 From: Souptik Chakraborty <62941615+Souptik96@users.noreply.github.com> Date: Wed, 5 Aug 2026 12:13:33 +0530 Subject: [PATCH] fix(feedback): Wrap long error messages in the feedback dialog `.form__error-container` only declared `color` and `fill`, so an error message containing a token longer than the container overflowed the dialog instead of breaking onto the next line. The container is rendered at `var(--form-width, 272px)` while the screenshot editor is open, and `FeedbackErrorMessages` lets integrators supply arbitrary error copy, so long unbroken tokens are expected input rather than an edge case. Adds `overflow-wrap: break-word`, which only breaks a word when it cannot fit on a line of its own and leaves normal whitespace wrapping untouched. Address Bugbot review: parameterise the min-width cases with it.each instead of a for loop, per .cursor/BUGBOT.md ("Flag usage of loops testing multiple scenarios in one test and recommend using (it)|(test).each instead"). Each selector is now its own reported case, so a failure names the selector rather than requiring the reader to work out which iteration broke. --- .../src/modal/components/Dialog.css.ts | 3 + .../test/modal/components/Dialog.css.test.ts | 56 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 packages/feedback/test/modal/components/Dialog.css.test.ts diff --git a/packages/feedback/src/modal/components/Dialog.css.ts b/packages/feedback/src/modal/components/Dialog.css.ts index b67cae1de13c..b3aeee70601b 100644 --- a/packages/feedback/src/modal/components/Dialog.css.ts +++ b/packages/feedback/src/modal/components/Dialog.css.ts @@ -139,11 +139,14 @@ const FORM = ` display: flex; flex-direction: column; gap: 8px; + min-width: 0; } .form__error-container { color: var(--error-color); fill: var(--error-color); + overflow-wrap: break-word; + min-width: 0; } .form__label { diff --git a/packages/feedback/test/modal/components/Dialog.css.test.ts b/packages/feedback/test/modal/components/Dialog.css.test.ts new file mode 100644 index 000000000000..93201d0090b6 --- /dev/null +++ b/packages/feedback/test/modal/components/Dialog.css.test.ts @@ -0,0 +1,56 @@ +/** + * @vitest-environment jsdom + */ +import { describe, expect, it } from 'vitest'; +import { createDialogStyles } from '../../../src/modal/components/Dialog.css'; + +/** + * Returns the declarations inside the first rule block matching `selector`, so + * assertions target one rule rather than the whole stylesheet. + */ +function getRuleBlock(css: string, selector: string): string { + const escaped = selector.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const match = new RegExp(`^${escaped}\\s*\\{([^}]*)\\}`, 'm').exec(css); + return match ? match[1] : ''; +} + +describe('createDialogStyles', () => { + it('lets long error messages wrap instead of overflowing the dialog', () => { + const css = createDialogStyles().textContent ?? ''; + const rule = getRuleBlock(css, '.form__error-container'); + + // Guard: if the selector is renamed, fail loudly rather than pass vacuously. + expect(rule).not.toBe(''); + // `FeedbackErrorMessages` lets integrators supply arbitrary error copy, and + // the container is only 272px wide while the screenshot editor is open, so a + // single long token must be breakable. + expect(rule).toMatch(/overflow-wrap:\s*break-word/); + }); + + // `overflow-wrap` alone does not reduce an element's min-content contribution, + // so every flex item between the error and the fixed-width column keeps + // `min-width: auto` and stretches to fit the longest token. Without this, + // `.form__top` pushes `.form__right` past its 272px and the message overflows + // the dialog even though the text itself is breakable. + it.each(['.form__top', '.form__error-container'])('lets %s shrink below its content width', selector => { + const css = createDialogStyles().textContent ?? ''; + const rule = getRuleBlock(css, selector); + + // Guard: if the selector is renamed, fail loudly rather than pass vacuously. + expect(rule).not.toBe(''); + expect(rule).toMatch(/min-width:\s*0/); + }); + + it('keeps the error colour tokens on the same rule', () => { + const css = createDialogStyles().textContent ?? ''; + const rule = getRuleBlock(css, '.form__error-container'); + + expect(rule).toMatch(/color:\s*var\(--error-color\)/); + expect(rule).toMatch(/fill:\s*var\(--error-color\)/); + }); + + it('applies the nonce when one is supplied', () => { + expect(createDialogStyles('abc123').getAttribute('nonce')).toBe('abc123'); + expect(createDialogStyles().hasAttribute('nonce')).toBe(false); + }); +});