refactor(person): move sort button into each section heading, sort independently
Replaced the single shared sort control with per-section sort buttons placed inline in each heading row (right-aligned via ml-auto). Each section now sorts independently, which matches user expectation and keeps the control visually anchored to the list it affects. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -96,21 +96,36 @@ test.describe('New person', () => {
|
||||
});
|
||||
|
||||
test.describe('Person detail — sort toggle', () => {
|
||||
test('sort toggle changes the button label when person has documents', async ({ page }) => {
|
||||
test('each section has its own sort toggle that works independently', async ({ page }) => {
|
||||
await page.goto('/persons');
|
||||
const firstPerson = page.locator('a[href^="/persons/"]').first();
|
||||
await firstPerson.click();
|
||||
await page.waitForSelector('[data-hydrated]');
|
||||
|
||||
const sortBtn = page.getByRole('button', { name: /Neueste zuerst|Älteste zuerst/i });
|
||||
if (await sortBtn.isVisible()) {
|
||||
await expect(sortBtn).toContainText('Neueste zuerst');
|
||||
await sortBtn.click();
|
||||
await expect(sortBtn).toContainText('Älteste zuerst');
|
||||
await sortBtn.click();
|
||||
await expect(sortBtn).toContainText('Neueste zuerst');
|
||||
await page.screenshot({ path: 'test-results/e2e/person-sort-toggle.png' });
|
||||
// Find sort buttons — there may be 0, 1 or 2 depending on whether sections have >1 doc
|
||||
const sortBtns = page.getByRole('button', { name: /Neueste zuerst|Älteste zuerst/i });
|
||||
const btnCount = await sortBtns.count();
|
||||
|
||||
if (btnCount >= 1) {
|
||||
const firstBtn = sortBtns.first();
|
||||
await expect(firstBtn).toContainText('Neueste zuerst');
|
||||
await firstBtn.click();
|
||||
await expect(firstBtn).toContainText('Älteste zuerst');
|
||||
await firstBtn.click();
|
||||
await expect(firstBtn).toContainText('Neueste zuerst');
|
||||
}
|
||||
|
||||
if (btnCount >= 2) {
|
||||
// Second sort button toggles independently
|
||||
const secondBtn = sortBtns.nth(1);
|
||||
await expect(secondBtn).toContainText('Neueste zuerst');
|
||||
await secondBtn.click();
|
||||
await expect(secondBtn).toContainText('Älteste zuerst');
|
||||
// First button should be unaffected
|
||||
await expect(sortBtns.first()).toContainText('Neueste zuerst');
|
||||
}
|
||||
|
||||
await page.screenshot({ path: 'test-results/e2e/person-sort-toggle.png' });
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -12,12 +12,13 @@
|
||||
|
||||
const DOCS_PREVIEW_LIMIT = 5;
|
||||
|
||||
let sortDir = $state<SortDir>('DESC');
|
||||
let sortDirSent = $state<SortDir>('DESC');
|
||||
let sortDirReceived = $state<SortDir>('DESC');
|
||||
let showAllSent = $state(false);
|
||||
let showAllReceived = $state(false);
|
||||
|
||||
const sortedSentDocuments = $derived(sortDocumentsByDate(sentDocuments, sortDir));
|
||||
const sortedReceivedDocuments = $derived(sortDocumentsByDate(receivedDocuments, sortDir));
|
||||
const sortedSentDocuments = $derived(sortDocumentsByDate(sentDocuments, sortDirSent));
|
||||
const sortedReceivedDocuments = $derived(sortDocumentsByDate(receivedDocuments, sortDirReceived));
|
||||
|
||||
const visibleSentDocuments = $derived(showAllSent ? sortedSentDocuments : sortedSentDocuments.slice(0, DOCS_PREVIEW_LIMIT));
|
||||
const visibleReceivedDocuments = $derived(showAllReceived ? sortedReceivedDocuments : sortedReceivedDocuments.slice(0, DOCS_PREVIEW_LIMIT));
|
||||
@@ -330,19 +331,6 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Sort control -->
|
||||
{#if allDocuments.length > 0}
|
||||
<div class="flex justify-end mb-4">
|
||||
<button
|
||||
onclick={() => (sortDir = sortDir === 'DESC' ? 'ASC' : 'DESC')}
|
||||
class="text-xs font-bold uppercase tracking-widest text-gray-400 hover:text-brand-navy transition-colors"
|
||||
aria-label={m.conv_sort_label()}
|
||||
>
|
||||
{sortDir === 'DESC' ? m.conv_sort_newest() : m.conv_sort_oldest()}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Sent Documents Section -->
|
||||
<div class="mb-10">
|
||||
<div class="flex items-center gap-3 mb-6 border-b border-brand-navy/10 pb-2">
|
||||
@@ -353,6 +341,14 @@
|
||||
{#if sentYearRange}
|
||||
<span class="text-xs font-sans text-gray-400">{sentYearRange}</span>
|
||||
{/if}
|
||||
{#if sentDocuments.length > 1}
|
||||
<button
|
||||
onclick={() => (sortDirSent = sortDirSent === 'DESC' ? 'ASC' : 'DESC')}
|
||||
class="ml-auto text-xs font-bold uppercase tracking-widest text-gray-400 hover:text-brand-navy transition-colors"
|
||||
>
|
||||
{sortDirSent === 'DESC' ? m.conv_sort_newest() : m.conv_sort_oldest()}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if sentDocuments.length === 0}
|
||||
@@ -417,6 +413,14 @@
|
||||
{#if receivedYearRange}
|
||||
<span class="text-xs font-sans text-gray-400">{receivedYearRange}</span>
|
||||
{/if}
|
||||
{#if receivedDocuments.length > 1}
|
||||
<button
|
||||
onclick={() => (sortDirReceived = sortDirReceived === 'DESC' ? 'ASC' : 'DESC')}
|
||||
class="ml-auto text-xs font-bold uppercase tracking-widest text-gray-400 hover:text-brand-navy transition-colors"
|
||||
>
|
||||
{sortDirReceived === 'DESC' ? m.conv_sort_newest() : m.conv_sort_oldest()}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if receivedDocuments.length === 0}
|
||||
|
||||
Reference in New Issue
Block a user