Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scroll to Event ID for links to Events #2541

Merged
merged 11 commits into from
Feb 24, 2025
1 change: 1 addition & 0 deletions src/lib/components/event/event-link.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
namespace: link.workflowEvent.namespace,
workflow: link.workflowEvent.workflowId,
run: link.workflowEvent.runId,
eventId: link.workflowEvent?.eventRef?.eventId,
});
</script>

Expand Down
1 change: 1 addition & 0 deletions src/lib/components/event/event-summary-row.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
class:canceled
class:terminated
class:typedError
data-eventid={event.id}
data-testid="event-summary-row"
on:click|stopPropagation={onLinkClick}
>
Expand Down
1 change: 1 addition & 0 deletions src/lib/components/event/event-summary-table.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
let:visibleItems
variant="split"
maxHeight="calc(100vh - 200px)"
hashField="eventid"
>
{#if !compact}
<HistoryGraph {groups} history={history(visibleItems)} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
namespace: link.workflowEvent.namespace,
workflow: link.workflowEvent.workflowId,
run: link.workflowEvent.runId,
eventId: link.workflowEvent?.eventRef?.eventId,
})}>{link.workflowEvent.workflowId}</Link
>
</div>
Expand Down
22 changes: 20 additions & 2 deletions src/lib/holocene/table/paginated-table/paginated.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<script lang="ts">
import { tick } from 'svelte';

import { page } from '$app/stores';

import Button from '$lib/holocene/button.svelte';
Expand Down Expand Up @@ -29,10 +31,12 @@
export let maxHeight = '';
export let pageSizeOptions: string[] = options;
export let fixed = false;
export let hashField = '';

$: url = $page.url;
$: perPageParam = url.searchParams.get(perPageKey) ?? pageSizeOptions[0];
$: currentPageParam = url.searchParams.get(currentPageKey) ?? '1';
$: currentPageParam = url.searchParams.get(currentPageKey) || '1';
$: hash = $page.url.hash;
$: store = pagination(items, perPageParam, currentPageParam);

// keep the 'page-size' url search param within the supported options
Expand Down Expand Up @@ -83,8 +87,22 @@
});
};

const scrollToHashEvent = async () => {
store.jumpToHashPage(hash);
await tick();
let id = hash.slice(1);
const row = document?.querySelector(`[data-${hashField}="${id}"]`);
if (row) {
setTimeout(() => {
row?.scrollIntoView({ behavior: 'smooth' });
}, 500);
}
};

$: {
if (currentPageParam) store.jumpToPage(currentPageParam);
if (currentPageParam && !hash) store.jumpToPage(currentPageParam);
if (hash && hashField && !url.searchParams.get(currentPageKey))
scrollToHashEvent();
if (perPageParam) store.adjustPageSize(perPageParam);
}
</script>
Expand Down
21 changes: 20 additions & 1 deletion src/lib/pages/workflow-history-event.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
import { page } from '$app/stores';

import EventSummaryTable from '$lib/components/event/event-summary-table.svelte';
import { groupEvents } from '$lib/models/event-groups';
import { fetchAllEvents } from '$lib/services/events-service';
import { eventFilterSort } from '$lib/stores/event-view';
import { fullEventHistory } from '$lib/stores/events';
import { workflowRun } from '$lib/stores/workflow-run';

$: ({ namespace, workflow: workflowId, run: runId } = $page.params);

Expand All @@ -29,9 +32,25 @@
$: fetchEvents(namespace, workflowId, runId);

$: updating = !$fullEventHistory.length;

$: ({ workflow } = $workflowRun);
$: pendingActivities = workflow?.pendingActivities;
$: pendingNexusOperations = workflow?.pendingNexusOperations;

$: ascendingGroups = groupEvents(
$fullEventHistory,
'ascending',
pendingActivities,
pendingNexusOperations,
);
$: groups =
$eventFilterSort === 'ascending'
? ascendingGroups
: [...ascendingGroups].reverse();

$: visibleItems = $fullEventHistory.filter((e) => e.id === $page.params.id);
</script>

<div class="px-8" data-testid="event-summary-table">
<EventSummaryTable items={visibleItems} {updating} openExpanded />
<EventSummaryTable items={visibleItems} {groups} {updating} openExpanded />
</div>
26 changes: 26 additions & 0 deletions src/lib/stores/pagination.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -542,3 +542,29 @@ describe('perPageFromSearchParameter', () => {
expect(perPageFromSearchParameter({} as any)).toBe(100);
});
});

describe('getStartingIndexForPage', () => {
it('should return 0 for the first page', () => {
expect(getStartingIndexForPage(1, 20, oneHundredResolutions)).toBe(0);
});

it('should return the first index of the second page for the something on the second page', () => {
expect(getStartingIndexForPage(2, 20, oneHundredResolutions)).toBe(20);
});

it('should return the first index of the last page for the something out of bounds', () => {
expect(getStartingIndexForPage(100, 20, oneHundredResolutions)).toBe(80);
});

it('should return 0 for the something out of bounds if the total number of items is less than itemsPerPage', () => {
expect(getStartingIndexForPage(3, 101, oneHundredResolutions)).toBe(0);
});

it('should return 0 if given a negative number for the page', () => {
expect(getStartingIndexForPage(-10, 20, oneHundredResolutions)).toBe(0);
});

it('should return 0 if given NaN', () => {
expect(getStartingIndexForPage(NaN, 20, oneHundredResolutions)).toBe(0);
});
});
27 changes: 21 additions & 6 deletions src/lib/stores/pagination.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { Readable } from 'svelte/store';
import { derived, get, writable } from 'svelte/store';

import { has } from '$lib/utilities/has';

export const defaultItemsPerPage = 100;
export const options: string[] = ['100', '250', '500'];
export const perPageKey = 'per-page';
Expand All @@ -12,6 +14,7 @@ type PaginationMethods<T> = {
next: () => void;
previous: () => void;
jumpToPage: (x: number | string) => void;
jumpToHashPage: (hash: string) => void;
jumpToIndex: (x: number | string) => void;
findIndex: (fn: (item: T) => boolean) => number;
findPage: (fn: (item: T) => boolean) => number;
Expand Down Expand Up @@ -157,15 +160,11 @@ export const outOfBounds = (
export const pagination = <T>(
items: Readonly<T[]> = [],
perPage: number | string = defaultItemsPerPage,
startingIndex: string | number = 0,
currentPage: string | number = 0,
): PaginationStore<T> => {
perPage = perPageFromSearchParameter(perPage);

const start = getNearestStartingIndex(
toNumber(startingIndex),
perPage,
items,
);
const start = getNearestStartingIndex(toNumber(currentPage), perPage, items);

const pageSize = writable(perPage);
const index = writable(start);
Expand Down Expand Up @@ -214,6 +213,21 @@ export const pagination = <T>(
jumpToPage(page);
};

const jumpToHashPage = (hash: string) => {
const hashId = hash?.slice(1);
if (hashId) {
const itemIndex = items.findIndex(
(item: unknown) => has(item, 'id') && item?.id === hashId,
);
if (itemIndex !== -1) {
const hashPage = getPageForIndex(itemIndex, get(pageSize));
if (hashPage !== currentPage) {
jumpToPage(hashPage);
}
}
}
};

const findIndex = (fn: (item: T) => boolean): number => {
for (let i = 0; i < items.length; i++) {
if (fn(items[i])) return i;
Expand Down Expand Up @@ -284,6 +298,7 @@ export const pagination = <T>(
previous,
jumpToPage,
jumpToIndex,
jumpToHashPage,
findIndex,
findPage,
nextRow,
Expand Down
6 changes: 3 additions & 3 deletions src/lib/utilities/route-for.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type RouteParameters = {
run: string;
view?: EventView | string;
queryParams?: Record<string, string>;
eventId: string;
eventId?: string;
scheduleId: string;
queue: string;
schedule: string;
Expand All @@ -38,7 +38,7 @@ export type ScheduleParameters = Pick<
>;
export type EventHistoryParameters = Pick<
RouteParameters,
'namespace' | 'workflow' | 'run' | 'view' | 'queryParams'
'namespace' | 'workflow' | 'run' | 'eventId' | 'view' | 'queryParams'
>;
export type EventParameters = Pick<
RouteParameters,
Expand Down Expand Up @@ -164,7 +164,7 @@ export const routeForEventHistory = ({
...parameters
}: EventHistoryParameters): string => {
const eventHistoryPath = `${routeForWorkflow(parameters)}/history`;
return toURL(`${eventHistoryPath}`, queryParams);
return toURL(`${eventHistoryPath}`, queryParams, parameters?.eventId);
};

export const routeForEventHistoryEvent = ({
Expand Down
14 changes: 14 additions & 0 deletions src/lib/utilities/to-url.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ describe('toURL', () => {
expect(toURL('/workflows')).toBe('/workflows');
});

it('should take a string and hash for the URL and return it', () => {
expect(toURL('/workflows', undefined, '123')).toBe('/workflows#123');
});

it('should turn the query params into a query string', () => {
const params = new URLSearchParams({ a: 'hello' });
expect(toURL('/workflows', params)).toBe('/workflows?a=hello');
Expand All @@ -16,4 +20,14 @@ describe('toURL', () => {
const params = { a: 'hello' };
expect(toURL('/workflows', params)).toBe('/workflows?a=hello');
});

it('should turn an object into a query string and include it with a hash', () => {
const params = { a: 'hello' };
expect(toURL('/workflows', params, '123')).toBe('/workflows?a=hello#123');
});

it('should turn the query params into a query string with a hash', () => {
const params = new URLSearchParams({ a: 'hello' });
expect(toURL('/workflows', params, '1')).toBe('/workflows?a=hello#1');
});
});
4 changes: 3 additions & 1 deletion src/lib/utilities/to-url.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
export const toURL = (
url: string,
params?: URLSearchParams | Record<string, string>,
hash?: string,
): string => {
const isURLSearchParams = params instanceof URLSearchParams;
if (params && !isURLSearchParams) params = new URLSearchParams(params);
if (params) return `${url}?${params}`;
if (params) url = `${url}?${params}`;
if (hash) url = `${url}#${hash}`;
return url;
};
Loading