From 4dcca1ca10b097409a81fc9b1bd91b54f776a3f8 Mon Sep 17 00:00:00 2001 From: Keith Guerin Date: Wed, 18 Mar 2026 11:39:12 -0700 Subject: [PATCH] feat(ui): format multi-line banner warnings with a bold title (#22955) Co-authored-by: Sehoon Shon --- .../cli/src/ui/components/Banner.test.tsx | 19 +++++++-------- packages/cli/src/ui/components/Banner.tsx | 15 ++++++------ ...r-Banner-handles-newlines-in-text.snap.svg | 20 ++++++++++++++++ ...anner-Banner-renders-in-info-mode.snap.svg | 23 +++++++++++++++++++ ...ner-renders-in-multi-line-warning.snap.svg | 19 +++++++++++++++ ...er-Banner-renders-in-warning-mode.snap.svg | 13 +++++++++++ .../__snapshots__/Banner.test.tsx.snap | 17 +++++++++----- 7 files changed, 104 insertions(+), 22 deletions(-) create mode 100644 packages/cli/src/ui/components/__snapshots__/Banner-Banner-handles-newlines-in-text.snap.svg create mode 100644 packages/cli/src/ui/components/__snapshots__/Banner-Banner-renders-in-info-mode.snap.svg create mode 100644 packages/cli/src/ui/components/__snapshots__/Banner-Banner-renders-in-multi-line-warning.snap.svg create mode 100644 packages/cli/src/ui/components/__snapshots__/Banner-Banner-renders-in-warning-mode.snap.svg diff --git a/packages/cli/src/ui/components/Banner.test.tsx b/packages/cli/src/ui/components/Banner.test.tsx index 46c47b8a71..00a2bf609f 100644 --- a/packages/cli/src/ui/components/Banner.test.tsx +++ b/packages/cli/src/ui/components/Banner.test.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { render } from '../../test-utils/render.js'; +import { renderWithProviders } from '../../test-utils/render.js'; import { Banner } from './Banner.js'; import { describe, it, expect } from 'vitest'; @@ -12,22 +12,23 @@ describe('Banner', () => { it.each([ ['warning mode', true, 'Warning Message'], ['info mode', false, 'Info Message'], + ['multi-line warning', true, 'Title Line\\nBody Line 1\\nBody Line 2'], ])('renders in %s', async (_, isWarning, text) => { - const { lastFrame, waitUntilReady, unmount } = render( + const renderResult = renderWithProviders( , ); - await waitUntilReady(); - expect(lastFrame()).toMatchSnapshot(); - unmount(); + await renderResult.waitUntilReady(); + await expect(renderResult).toMatchSvgSnapshot(); + renderResult.unmount(); }); it('handles newlines in text', async () => { const text = 'Line 1\\nLine 2'; - const { lastFrame, waitUntilReady, unmount } = render( + const renderResult = renderWithProviders( , ); - await waitUntilReady(); - expect(lastFrame()).toMatchSnapshot(); - unmount(); + await renderResult.waitUntilReady(); + await expect(renderResult).toMatchSvgSnapshot(); + renderResult.unmount(); }); }); diff --git a/packages/cli/src/ui/components/Banner.tsx b/packages/cli/src/ui/components/Banner.tsx index 99f573a68e..3f9777aa45 100644 --- a/packages/cli/src/ui/components/Banner.tsx +++ b/packages/cli/src/ui/components/Banner.tsx @@ -14,20 +14,21 @@ export function getFormattedBannerContent( isWarning: boolean, subsequentLineColor: string, ): ReactNode { - if (isWarning) { - return ( - {rawText.replace(/\\n/g, '\n')} - ); - } - const text = rawText.replace(/\\n/g, '\n'); const lines = text.split('\n'); return lines.map((line, index) => { if (index === 0) { + if (isWarning) { + return ( + + {line} + + ); + } return ( - {line} + {line} ); } diff --git a/packages/cli/src/ui/components/__snapshots__/Banner-Banner-handles-newlines-in-text.snap.svg b/packages/cli/src/ui/components/__snapshots__/Banner-Banner-handles-newlines-in-text.snap.svg new file mode 100644 index 0000000000..a6272e0fa9 --- /dev/null +++ b/packages/cli/src/ui/components/__snapshots__/Banner-Banner-handles-newlines-in-text.snap.svg @@ -0,0 +1,20 @@ + + + + + ╭──────────────────────────────────────────────────────────────────────────────╮ + + L + i + n + e + 1 + + + Line 2 + + ╰──────────────────────────────────────────────────────────────────────────────╯ + + \ No newline at end of file diff --git a/packages/cli/src/ui/components/__snapshots__/Banner-Banner-renders-in-info-mode.snap.svg b/packages/cli/src/ui/components/__snapshots__/Banner-Banner-renders-in-info-mode.snap.svg new file mode 100644 index 0000000000..89d219005d --- /dev/null +++ b/packages/cli/src/ui/components/__snapshots__/Banner-Banner-renders-in-info-mode.snap.svg @@ -0,0 +1,23 @@ + + + + + ╭──────────────────────────────────────────────────────────────────────────────╮ + + I + n + f + o + M + e + s + s + a + g + e + + ╰──────────────────────────────────────────────────────────────────────────────╯ + + \ No newline at end of file diff --git a/packages/cli/src/ui/components/__snapshots__/Banner-Banner-renders-in-multi-line-warning.snap.svg b/packages/cli/src/ui/components/__snapshots__/Banner-Banner-renders-in-multi-line-warning.snap.svg new file mode 100644 index 0000000000..6b3250fc6b --- /dev/null +++ b/packages/cli/src/ui/components/__snapshots__/Banner-Banner-renders-in-multi-line-warning.snap.svg @@ -0,0 +1,19 @@ + + + + + ╭──────────────────────────────────────────────────────────────────────────────╮ + + Title Line + + + Body Line 1 + + + Body Line 2 + + ╰──────────────────────────────────────────────────────────────────────────────╯ + + \ No newline at end of file diff --git a/packages/cli/src/ui/components/__snapshots__/Banner-Banner-renders-in-warning-mode.snap.svg b/packages/cli/src/ui/components/__snapshots__/Banner-Banner-renders-in-warning-mode.snap.svg new file mode 100644 index 0000000000..4f3ee74723 --- /dev/null +++ b/packages/cli/src/ui/components/__snapshots__/Banner-Banner-renders-in-warning-mode.snap.svg @@ -0,0 +1,13 @@ + + + + + ╭──────────────────────────────────────────────────────────────────────────────╮ + + Warning Message + + ╰──────────────────────────────────────────────────────────────────────────────╯ + + \ No newline at end of file diff --git a/packages/cli/src/ui/components/__snapshots__/Banner.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/Banner.test.tsx.snap index 7766c808ae..6df246dede 100644 --- a/packages/cli/src/ui/components/__snapshots__/Banner.test.tsx.snap +++ b/packages/cli/src/ui/components/__snapshots__/Banner.test.tsx.snap @@ -4,20 +4,25 @@ exports[`Banner > handles newlines in text 1`] = ` "╭──────────────────────────────────────────────────────────────────────────────╮ │ Line 1 │ │ Line 2 │ -╰──────────────────────────────────────────────────────────────────────────────╯ -" +╰──────────────────────────────────────────────────────────────────────────────╯" `; exports[`Banner > renders in info mode 1`] = ` "╭──────────────────────────────────────────────────────────────────────────────╮ │ Info Message │ -╰──────────────────────────────────────────────────────────────────────────────╯ -" +╰──────────────────────────────────────────────────────────────────────────────╯" +`; + +exports[`Banner > renders in multi-line warning 1`] = ` +"╭──────────────────────────────────────────────────────────────────────────────╮ +│ Title Line │ +│ Body Line 1 │ +│ Body Line 2 │ +╰──────────────────────────────────────────────────────────────────────────────╯" `; exports[`Banner > renders in warning mode 1`] = ` "╭──────────────────────────────────────────────────────────────────────────────╮ │ Warning Message │ -╰──────────────────────────────────────────────────────────────────────────────╯ -" +╰──────────────────────────────────────────────────────────────────────────────╯" `;