fix(conversations): keep swap button in DOM to prevent grid column width shift
All checks were successful
CI / Unit & Component Tests (push) Successful in 1m56s
CI / Backend Unit Tests (push) Successful in 1m53s
CI / E2E Tests (push) Successful in 16m37s
CI / Unit & Component Tests (pull_request) Successful in 2m2s
CI / Backend Unit Tests (pull_request) Successful in 2m2s
CI / E2E Tests (pull_request) Successful in 16m55s

The swap button was conditionally removed from the DOM with {#if}, which
caused the receiver input to collapse into the narrow auto column of the
grid-cols-[1fr_auto_1fr] layout on desktop when no persons were selected.

The button is now always rendered. On desktop it becomes invisible
(visibility:hidden) when no persons are selected, preserving the middle
column width so both 1fr columns stay equal. On mobile it remains hidden
(display:none) via the hidden class so no empty gap appears between the
stacked inputs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit was merged in pull request #43.
This commit is contained in:
Marcel
2026-03-20 18:32:37 +01:00
parent 0f8b582813
commit 513a7290b0
2 changed files with 32 additions and 28 deletions

View File

@@ -91,32 +91,35 @@ const enrichedDocuments = $derived(
/>
</div>
<!-- Swap button — icon-only on desktop, full-width with text on mobile -->
{#if senderId && receiverId}
<div class="flex items-end">
<button
data-testid="conv-swap-btn"
onclick={swapPersons}
class="flex w-full items-center justify-center gap-2 border border-brand-sand px-3 py-2.5 text-xs font-bold tracking-widest text-brand-navy uppercase transition-colors hover:bg-brand-navy hover:text-white md:w-auto"
title={m.conv_swap_btn()}
<!-- Swap button — always rendered to hold grid column width on desktop.
On mobile: hidden (display:none) when no persons selected so no gap appears.
On desktop: invisible (visibility:hidden) when no persons so both 1fr columns stay equal. -->
<div class="{senderId && receiverId ? 'flex' : 'hidden md:flex'} items-end">
<button
data-testid="conv-swap-btn"
onclick={swapPersons}
class="flex w-full items-center justify-center gap-2 border border-brand-sand px-3 py-2.5 text-xs font-bold tracking-widest text-brand-navy uppercase transition-colors hover:bg-brand-navy hover:text-white md:w-auto {senderId &&
receiverId
? ''
: 'invisible'}"
title={m.conv_swap_btn()}
>
<svg
class="h-4 w-4 flex-shrink-0 md:rotate-90"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<svg
class="h-4 w-4 flex-shrink-0 md:rotate-90"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 16V4m0 0L3 8m4-4l4 4M17 8v12m0 0l4-4m-4 4l-4-4"
></path>
</svg>
<span class="md:hidden">{m.conv_swap_btn()}</span>
</button>
</div>
{/if}
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 16V4m0 0L3 8m4-4l4 4M17 8v12m0 0l4-4m-4 4l-4-4"
></path>
</svg>
<span class="md:hidden">{m.conv_swap_btn()}</span>
</button>
</div>
<!-- Receiver -->
<div

View File

@@ -51,9 +51,10 @@ describe('Conversations page empty state', () => {
await expect.element(page.getByText(/Wählen Sie zwei Personen aus/i)).toBeInTheDocument();
});
it('does not show the swap button when no persons are selected', async () => {
it('hides the swap button when no persons are selected', async () => {
render(Page, { data: baseData });
await expect.element(page.getByTestId('conv-swap-btn')).not.toBeInTheDocument();
// Button is always in the DOM (holds grid column width on desktop) but made invisible
await expect.element(page.getByTestId('conv-swap-btn')).toHaveClass('invisible');
});
it('does not show the new document link when no persons are selected', async () => {
@@ -76,7 +77,7 @@ describe('Conversations page no results', () => {
describe('Conversations page swap button', () => {
it('shows the swap button when both persons are selected', async () => {
render(Page, { data: withPersons });
await expect.element(page.getByTestId('conv-swap-btn')).toBeInTheDocument();
await expect.element(page.getByTestId('conv-swap-btn')).not.toHaveClass('invisible');
});
it('calls goto with swapped sender and receiver when clicked', async () => {