From b849c94e03c75a3f882863fbbf186751f37ae3b5 Mon Sep 17 00:00:00 2001 From: KillariDev <13102010+KillariDev@users.noreply.github.com> Date: Tue, 19 May 2026 13:56:42 +0000 Subject: [PATCH] Improve modal header accessibility and layout - Make modal headers sticky with clearer close controls - Switch close actions to accessible icon buttons - Update modal tests for role-based queries --- ui/css/index.css | 30 ++++++++++++++++++++++++++- ui/dist/css/index.css | 30 ++++++++++++++++++++++++++- ui/ts/components/LiquidationModal.tsx | 6 +++--- ui/ts/components/OperationModal.tsx | 6 +++--- ui/ts/tests/liquidationModal.test.tsx | 2 +- ui/ts/tests/operationModal.test.tsx | 18 ++++++++++++++++ 6 files changed, 83 insertions(+), 9 deletions(-) diff --git a/ui/css/index.css b/ui/css/index.css index 68604edb..132b4857 100644 --- a/ui/css/index.css +++ b/ui/css/index.css @@ -2254,10 +2254,38 @@ strong.metric-value-danger { justify-content: space-between; align-items: flex-start; gap: 1rem; + position: sticky; + top: -1.25rem; + z-index: 1; + margin: -1.25rem -1.25rem 0; + padding: 0.9rem 1.25rem 0.85rem; + border-bottom: 1px solid var(--border-default); + background: linear-gradient(180deg, rgba(11, 23, 39, 0.985) 0%, rgba(7, 17, 31, 0.96) 100%); + box-shadow: 0 0.75rem 1rem rgba(0, 0, 0, 0.12); +} + +.modal-header-title { + flex: 1 1 auto; + min-width: 0; } .modal-header h3 { - margin-top: 0.15rem; + margin: 0; + overflow-wrap: anywhere; +} + +.modal-close-button { + flex: none; + display: inline-flex; + align-items: center; + justify-content: center; + width: 2.25rem; + min-width: 2.25rem; + min-height: 2.25rem; + padding: 0; + border-radius: 0.7rem; + font-size: 1.35rem; + line-height: 1; } .modal-summary-grid { diff --git a/ui/dist/css/index.css b/ui/dist/css/index.css index 68604edb..132b4857 100644 --- a/ui/dist/css/index.css +++ b/ui/dist/css/index.css @@ -2254,10 +2254,38 @@ strong.metric-value-danger { justify-content: space-between; align-items: flex-start; gap: 1rem; + position: sticky; + top: -1.25rem; + z-index: 1; + margin: -1.25rem -1.25rem 0; + padding: 0.9rem 1.25rem 0.85rem; + border-bottom: 1px solid var(--border-default); + background: linear-gradient(180deg, rgba(11, 23, 39, 0.985) 0%, rgba(7, 17, 31, 0.96) 100%); + box-shadow: 0 0.75rem 1rem rgba(0, 0, 0, 0.12); +} + +.modal-header-title { + flex: 1 1 auto; + min-width: 0; } .modal-header h3 { - margin-top: 0.15rem; + margin: 0; + overflow-wrap: anywhere; +} + +.modal-close-button { + flex: none; + display: inline-flex; + align-items: center; + justify-content: center; + width: 2.25rem; + min-width: 2.25rem; + min-height: 2.25rem; + padding: 0; + border-radius: 0.7rem; + font-size: 1.35rem; + line-height: 1; } .modal-summary-grid { diff --git a/ui/ts/components/LiquidationModal.tsx b/ui/ts/components/LiquidationModal.tsx index 5ace7e49..0548529b 100644 --- a/ui/ts/components/LiquidationModal.tsx +++ b/ui/ts/components/LiquidationModal.tsx @@ -229,11 +229,11 @@ export function LiquidationModal({
event.stopPropagation()}>
-
+

{getLiquidationModalTitle(currentPoolOracleManagerDetails)}

-
{queuedLiquidationStatus === undefined ? null : queuedLiquidationStatus === 'queued' ? ( diff --git a/ui/ts/components/OperationModal.tsx b/ui/ts/components/OperationModal.tsx index 81252d9d..e6f50506 100644 --- a/ui/ts/components/OperationModal.tsx +++ b/ui/ts/components/OperationModal.tsx @@ -45,11 +45,11 @@ export function OperationModal({ children, description, isOpen, onClose, title }
event.stopPropagation()}>
-
+

{title}

-
{description === undefined ? undefined :

{description}

} diff --git a/ui/ts/tests/liquidationModal.test.tsx b/ui/ts/tests/liquidationModal.test.tsx index 18a0f25e..ab31dfc4 100644 --- a/ui/ts/tests/liquidationModal.test.tsx +++ b/ui/ts/tests/liquidationModal.test.tsx @@ -160,7 +160,7 @@ describe('LiquidationModal', () => { let renderedComponent = await renderModal() cleanupRenderedComponent = renderedComponent.cleanup - const closeButton = within(document.body).getByText('Close') as HTMLButtonElement + const closeButton = within(document.body).getByRole('button', { name: 'Close' }) as HTMLButtonElement const cancelButton = within(document.body).getByText('Cancel') as HTMLButtonElement expect(document.activeElement).toBe(closeButton) diff --git a/ui/ts/tests/operationModal.test.tsx b/ui/ts/tests/operationModal.test.tsx index 1866a7c7..18cc7c54 100644 --- a/ui/ts/tests/operationModal.test.tsx +++ b/ui/ts/tests/operationModal.test.tsx @@ -36,6 +36,24 @@ describe('OperationModal', () => { restoreDomEnvironment = undefined }) + test('exposes the dialog title and close control accessibly', async () => { + const container = document.createElement('div') + document.body.appendChild(container) + + await act(() => { + render(, container) + }) + + const dialog = within(container).getByRole('dialog', { name: 'Edit amount' }) + const closeButton = within(dialog).getByRole('button', { name: 'Close' }) + + expect(dialog).not.toBeNull() + expect(closeButton.textContent).toBe('×') + + render(null, container) + container.remove() + }) + test('keeps focus on the edited input while the modal rerenders', async () => { const container = document.createElement('div') document.body.appendChild(container)