Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { EventSummaryModel, StackSummaryModel, SummaryTemplateKeys } from '$features/events/components/summary';

import { describe, expect, it } from 'vitest';

import { getColumns } from './options.svelte';

describe('getColumns', () => {
it('uses dedicated stack-mode controls instead of API sort parameters', () => {
const result = getColumns<StackSummaryModel<SummaryTemplateKeys>>('stack_frequent');
const columnsById = Object.fromEntries(result.map((column) => [column.id, column]));

expect(columnsById.events?.enableSorting).toBe(false);
expect(columnsById.first?.enableSorting).toBe(false);
expect(columnsById.last?.enableSorting).toBe(false);
expect(columnsById.events?.header).toBeTypeOf('function');
expect(columnsById.first?.header).toBeTypeOf('function');
expect(columnsById.last?.header).toBeTypeOf('function');
});

it('keeps summary message column unsortable', () => {
const result = getColumns<EventSummaryModel<SummaryTemplateKeys>>('summary');
const columnsById = Object.fromEntries(result.map((column) => [column.id, column]));

expect(columnsById.message?.enableSorting).toBe(false);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { EventSummaryModel, StackSummaryModel, SummaryModel, SummaryTemplat
import LogLevel from '../log-level.svelte';
import Summary from '../summary/summary.svelte';
import EventsUserIdentitySummaryCell from './events-user-identity-summary-cell.svelte';
import StackSortHeader from './stack-sort-header.svelte';
import StackStatusCell from './stack-status-cell.svelte';
import StackUsersSummaryCell from './stack-users-summary-cell.svelte';

Expand All @@ -25,9 +26,11 @@ export const defaultEventColumnVisibility: ColumnVisibilityState = {
version: false
};

export type StackSortMode = Extract<GetEventsMode, 'stack_frequent' | 'stack_new' | 'stack_recent'>;

export function getColumns<TSummaryModel extends SummaryModel<SummaryTemplateKeys>>(
mode: GetEventsMode = 'summary',
options?: { showType?: boolean }
options?: { onStackSort?: (mode: StackSortMode) => void; showType?: boolean }
): ColumnDef<StockFeatures, TSummaryModel, unknown>[] {
const showType = options?.showType ?? true;
const columns: ColumnDef<StockFeatures, TSummaryModel, unknown>[] = [
Expand Down Expand Up @@ -176,7 +179,12 @@ export function getColumns<TSummaryModel extends SummaryModel<SummaryTemplateKey
accessorKey: nameof<StackSummaryModel<SummaryTemplateKeys>>('total'),
cell: (prop) => renderComponent(NumberFormatter, { value: prop.getValue<number>() }),
enableSorting: false,
header: 'Events',
header: () =>
renderComponent(StackSortHeader, {
active: mode === 'stack_frequent',
label: 'Events',
onclick: () => options?.onStackSort?.('stack_frequent')
}),
id: 'events',
meta: {
class: 'w-24'
Expand All @@ -186,7 +194,12 @@ export function getColumns<TSummaryModel extends SummaryModel<SummaryTemplateKey
accessorKey: nameof<StackSummaryModel<SummaryTemplateKeys>>('first_occurrence'),
cell: (prop) => renderComponent(TimeAgo, { value: prop.getValue<string>() }),
enableSorting: false,
header: 'First',
header: () =>
renderComponent(StackSortHeader, {
active: mode === 'stack_new',
label: 'First',
onclick: () => options?.onStackSort?.('stack_new')
Comment thread
niemyjski marked this conversation as resolved.
Outdated
}),
id: 'first',
meta: {
class: 'w-36'
Expand All @@ -196,7 +209,12 @@ export function getColumns<TSummaryModel extends SummaryModel<SummaryTemplateKey
accessorKey: nameof<StackSummaryModel<SummaryTemplateKeys>>('last_occurrence'),
cell: (prop) => renderComponent(TimeAgo, { value: prop.getValue<string>() }),
enableSorting: false,
header: 'Last',
header: () =>
renderComponent(StackSortHeader, {
active: mode === 'stack_recent',
label: 'Last',
onclick: () => options?.onStackSort?.('stack_recent')
}),
id: 'last',
meta: {
class: 'w-36'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script lang="ts">
import { Button } from '$comp/ui/button';
import ArrowDown from '@lucide/svelte/icons/arrow-down';

interface Props {
active: boolean;
label: string;
onclick: () => void;
}

let { active, label, onclick }: Props = $props();
</script>

<Button aria-label={`Sort by ${label} descending`} aria-pressed={active} class="h-8" {onclick} variant="ghost">
{label}
{#if active}
<ArrowDown aria-hidden="true" />
{/if}
</Button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { fireEvent, render, screen } from '@testing-library/svelte';
import { describe, expect, it, vi } from 'vitest';

import StackSortHeader from './stack-sort-header.svelte';

describe('StackSortHeader', () => {
it('exposes the active descending sort and handles selection', async () => {
const onclick = vi.fn();
render(StackSortHeader, { active: true, label: 'Events', onclick });

const button = screen.getByRole('button', { name: 'Sort by Events descending' });
expect(button).toHaveAttribute('aria-pressed', 'true');

await fireEvent.click(button);

expect(onclick).toHaveBeenCalledOnce();
});

it('does not mark inactive sort modes as selected', () => {
render(StackSortHeader, { active: false, label: 'First', onclick: vi.fn() });

expect(screen.getByRole('button', { name: 'Sort by First descending' })).toHaveAttribute('aria-pressed', 'false');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,10 @@
columnPersistenceKey: 'stacks-column-visibility',
get columns() {
return getColumns<EventSummaryModel<SummaryTemplateKeys>>(eventsQueryParameters.mode, {
onStackSort: (mode) => {
eventsQueryParameters.mode = mode;
table.setPageIndex(0);
},
showType: !hasSingleTypeFilter(eventsQueryParameters.filter)
});
},
Expand Down
Loading