From abd02247327bf80770e1cfb3242de7236ceac1b7 Mon Sep 17 00:00:00 2001 From: Souptik Chakraborty Date: Tue, 4 Aug 2026 15:42:10 +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. --- .../src/modal/components/Dialog.css.ts | 1 + .../test/modal/components/Dialog.css.test.ts | 42 +++++++++++++++++++ 2 files changed, 43 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..774994ac7aa1 100644 --- a/packages/feedback/src/modal/components/Dialog.css.ts +++ b/packages/feedback/src/modal/components/Dialog.css.ts @@ -144,6 +144,7 @@ const FORM = ` .form__error-container { color: var(--error-color); fill: var(--error-color); + overflow-wrap: break-word; } .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..5446dfb6511c --- /dev/null +++ b/packages/feedback/test/modal/components/Dialog.css.test.ts @@ -0,0 +1,42 @@ +/** + * @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/); + }); + + 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); + }); +});