Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep First sort from filtering stacks

When users click the First header with any time range, this sends mode=stack_new; the server does not only sort by first occurrence for that mode, it also adds a first_occurrence:[range] filter in EventController.GetInternalAsync via AddFirstOccurrenceFilter, so stacks whose first occurrence predates the current range disappear even if they have matching recent events. A column header presented as sorting the current stack list should not change the result set/count; either avoid exposing this as a sort or use an API mode that preserves all matching stacks.

Useful? React with 👍 / 👎.

}),
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.getAttribute('aria-pressed')).toBe('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' }).getAttribute('aria-pressed')).toBe('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