From 383d2248a20f821f5e70a63f97fa47d34ff28a29 Mon Sep 17 00:00:00 2001 From: Anso Date: Mon, 6 Jul 2026 17:13:12 -0400 Subject: [PATCH] fix: keep confirm modal actions visible for long titles (#1578) --- .../EditorLayout/DeleteStackDialog.tsx | 11 +++++-- .../__tests__/DeleteStackDialog.test.tsx | 31 +++++++++++++++++ frontend/src/components/ui/modal.tsx | 33 ++++++++++++++----- 3 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 frontend/src/components/EditorLayout/__tests__/DeleteStackDialog.test.tsx diff --git a/frontend/src/components/EditorLayout/DeleteStackDialog.tsx b/frontend/src/components/EditorLayout/DeleteStackDialog.tsx index a4a6a18f..387188fa 100644 --- a/frontend/src/components/EditorLayout/DeleteStackDialog.tsx +++ b/frontend/src/components/EditorLayout/DeleteStackDialog.tsx @@ -22,11 +22,18 @@ export function DeleteStackDialog({ open, onOpenChange, stackName, onConfirm }: open={open} onOpenChange={handleOpenChange} variant="destructive" - kicker={`${(stackName ?? 'STACK').toUpperCase()} · REMOVE · IRREVERSIBLE`} + kicker="REMOVE · IRREVERSIBLE" title={ stackName ? ( <> - Delete {stackName}? + Delete{' '} + + {stackName} + + ? ) : ( 'Delete stack?' diff --git a/frontend/src/components/EditorLayout/__tests__/DeleteStackDialog.test.tsx b/frontend/src/components/EditorLayout/__tests__/DeleteStackDialog.test.tsx new file mode 100644 index 00000000..65a3fdab --- /dev/null +++ b/frontend/src/components/EditorLayout/__tests__/DeleteStackDialog.test.tsx @@ -0,0 +1,31 @@ +import { describe, it, expect, vi } from 'vitest'; +import { render, screen } from '@testing-library/react'; +import { DeleteStackDialog } from '../DeleteStackDialog'; + +const LONG_STACK_NAME = 'this-is-a-very-long-stack-name-that-should-not-push-actions-off-screen'; + +function renderLongNameDialog() { + render( + , + ); +} + +describe('DeleteStackDialog', () => { + it('keeps Delete and Cancel visible when the stack name is very long', () => { + renderLongNameDialog(); + + expect(screen.getByRole('button', { name: 'Delete' })).toBeVisible(); + expect(screen.getByRole('button', { name: 'Cancel' })).toBeVisible(); + }); + + it('exposes the full stack name on hover via title', () => { + renderLongNameDialog(); + + expect(screen.getByTitle(LONG_STACK_NAME)).toHaveTextContent(LONG_STACK_NAME); + }); +}); diff --git a/frontend/src/components/ui/modal.tsx b/frontend/src/components/ui/modal.tsx index 983dbe5a..fcbcf162 100644 --- a/frontend/src/components/ui/modal.tsx +++ b/frontend/src/components/ui/modal.tsx @@ -88,12 +88,15 @@ function HeaderShell({ }: HeaderShellProps) { const v = HEADER_VARIANT[variant]; return ( -
+
-
+
{kicker}
- + {title} @@ -147,8 +150,22 @@ function ConfirmDestructiveHeader(props: ModalHeaderBaseProps) { ); } -export function ModalBody({ className, ...props }: React.HTMLAttributes) { - return
; +interface ModalBodyProps extends React.HTMLAttributes { + /** Grow within a flex-column confirm shell; body scrolls instead of stretching the dialog. */ + fill?: boolean; +} + +export function ModalBody({ className, fill, ...props }: ModalBodyProps) { + return ( +
+ ); } interface ModalFooterProps { @@ -160,7 +177,7 @@ interface ModalFooterProps { export function ModalFooter({ primary, secondary, hint, hintAccent }: ModalFooterProps) { return ( -
+
{hint} {hintAccent !== undefined && ( @@ -221,9 +238,9 @@ export function ConfirmModal({ return ( - +
- {children !== undefined && {children}} + {children !== undefined && {children}}