Repository: temporalio/ui
Pull request #272 (2022-02-24 03:29:40)

Refactor event groups
Simplifies event groups to use primitive data structures, improves types for events.

cloudflare-pages[bot]: ## Deploying with &nbsp;<a href="https://pages.dev"><img alt="Cloudflare Pages" src="https://user-images.githubusercontent.com/23264/106598434-9e719e00-654f-11eb-9e59-6167043cfa01.png" width="16"></a> &nbsp;Cloudflare Pages

<table><tr><td><strong>Latest commit:</strong> </td><td>
<code>401b13a</code>
</td></tr>
<tr><td><strong>Status:</strong></td><td>&nbsp;✅&nbsp; Deploy successful!</td></tr>
<tr><td><strong>Preview URL:</strong></td><td>
<a href='https://b450606c.ui-c3x.pages.dev'>https://b450606c.ui-c3x.pages.dev</a>
</td></tr>
</table>

[View logs](https://dash.cloudflare.com/?to=/:account/pages/view/temporal-pizza/b450606c-266c-4cf7-afdf-bc3f99b74b8d)


Patch:
diff --git a/src/events.d.ts b/src/events.d.ts
index dee76233..4cf56239 100644
--- a/src/events.d.ts
+++ b/src/events.d.ts
@@ -1,307 +1,14 @@
 type HistoryEvent = import('$types').HistoryEvent;
+type PendingActivityInfo = import('$types').PendingActivityInfo;
 
-type WorkflowExecutionStartedEventAttributes =
-  import('$types').WorkflowExecutionStartedEventAttributes;
-type WorkflowExecutionCompletedEventAttributes =
-  import('$types').WorkflowExecutionCompletedEventAttributes;
-type WorkflowExecutionFailedEventAttributes =
-  import('$types').WorkflowExecutionFailedEventAttributes;
-type WorkflowExecutionTimedOutEventAttributes =
-  import('$types').WorkflowExecutionTimedOutEventAttributes;
-type WorkflowTaskScheduledEventAttributes =
-  import('$types').WorkflowTaskScheduledEventAttributes;
-type WorkflowTaskStartedEventAttributes =
-  import('$types').WorkflowTaskStartedEventAttributes;
-type WorkflowTaskCompletedEventAttributes =
-  import('$types').WorkflowTaskCompletedEventAttributes;
-type WorkflowTaskTimedOutEventAttributes =
-  import('$types').WorkflowTaskTimedOutEventAttributes;
-type WorkflowTaskFailedEventAttributes =
-  import('$types').WorkflowTaskFailedEventAttributes;
-type ActivityTaskScheduledEventAttributes =
-  import('$types').ActivityTaskScheduledEventAttributes;
-type ActivityTaskStartedEventAttributes =
-  import('$types').ActivityTaskStartedEventAttributes;
-type ActivityTaskCompletedEventAttributes =
-  import('$types').ActivityTaskCompletedEventAttributes;
-type ActivityTaskFailedEventAttributes =
-  import('$types').ActivityTaskFailedEventAttributes;
-type ActivityTaskTimedOutEventAttributes =
-  import('$types').ActivityTaskTimedOutEventAttributes;
-type TimerStartedEventAttributes = import('$types').TimerStartedEventAttributes;
-type TimerFiredEventAttributes = import('$types').TimerFiredEventAttributes;
-type ActivityTaskCancelRequestedEventAttributes =
-  import('$types').ActivityTaskCancelRequestedEventAttributes;
-type ActivityTaskCanceledEventAttributes =
-  import('$types').ActivityTaskCanceledEventAttributes;
-type TimerCanceledEventAttributes =
-  import('$types').TimerCanceledEventAttributes;
-type MarkerRecordedEventAttributes =
-  import('$types').MarkerRecordedEventAttributes;
-type WorkflowExecutionSignaledEventAttributes =
-  import('$types').WorkflowExecutionSignaledEventAttributes;
-type WorkflowExecutionTerminatedEventAttributes =
-  import('$types').WorkflowExecutionTerminatedEventAttributes;
-type WorkflowExecutionCancelRequestedEventAttributes =
-  import('$types').WorkflowExecutionCancelRequestedEventAttributes;
-type WorkflowExecutionCanceledEventAttributes =
-  import('$types').WorkflowExecutionCanceledEventAttributes;
-type RequestCancelExternalWorkflowExecutionInitiatedEventAttributes =
-  import('$types').RequestCancelExternalWorkflowExecutionInitiatedEventAttributes;
-type RequestCancelExternalWorkflowExecutionFailedEventAttributes =
-  import('$types').RequestCancelExternalWorkflowExecutionFailedEventAttributes;
-type ExternalWorkflowExecutionCancelRequestedEventAttributes =
-  import('$types').ExternalWorkflowExecutionCancelRequestedEventAttributes;
-type WorkflowExecutionContinuedAsNewEventAttributes =
-  import('$types').WorkflowExecutionContinuedAsNewEventAttributes;
-type StartChildWorkflowExecutionInitiatedEventAttributes =
-  import('$types').StartChildWorkflowExecutionInitiatedEventAttributes;
-type StartChildWorkflowExecutionFailedEventAttributes =
-  import('$types').StartChildWorkflowExecutionFailedEventAttributes;
-type ChildWorkflowExecutionStartedEventAttributes =
-  import('$types').ChildWorkflowExecutionStartedEventAttributes;
-type ChildWorkflowExecutionCompletedEventAttributes =
-  import('$types').ChildWorkflowExecutionCompletedEventAttributes;
-type ChildWorkflowExecutionFailedEventAttributes =
-  import('$types').ChildWorkflowExecutionFailedEventAttributes;
-type ChildWorkflowExecutionCanceledEventAttributes =
-  import('$types').ChildWorkflowExecutionCanceledEventAttributes;
-type ChildWorkflowExecutionTimedOutEventAttributes =
-  import('$types').ChildWorkflowExecutionTimedOutEventAttributes;
-type ChildWorkflowExecutionTerminatedEventAttributes =
-  import('$types').ChildWorkflowExecutionTerminatedEventAttributes;
-type SignalExternalWorkflowExecutionInitiatedEventAttributes =
-  import('$types').SignalExternalWorkflowExecutionInitiatedEventAttributes;
-type SignalExternalWorkflowExecutionFailedEventAttributes =
-  import('$types').SignalExternalWorkflowExecutionFailedEventAttributes;
-type ExternalWorkflowExecutionSignaledEventAttributes =
-  import('$types').ExternalWorkflowExecutionSignaledEventAttributes;
-type UpsertWorkflowSearchAttributesEventAttributes =
-  import('$types').UpsertWorkflowSearchAttributesEventAttributes;
-
-type BaseEvent = Pick<
-  HistoryEvent,
-  'eventId' | 'eventTime' | 'eventType' | 'version' | 'taskId'
->;
-
-type WorkflowExecutionStartedEvent = BaseEvent &
-  Pick<HistoryEvent, 'workflowExecutionStartedEventAttributes'>;
-type WorkflowExecutionCompletedEvent = BaseEvent &
-  Pick<HistoryEvent, 'workflowExecutionCompletedEventAttributes'>;
-type WorkflowExecutionFailedEvent = BaseEvent &
-  Pick<HistoryEvent, 'workflowExecutionFailedEventAttributes'>;
-type WorkflowExecutionTimedOutEvent = BaseEvent &
-  Pick<HistoryEvent, 'workflowExecutionTimedOutEventAttributes'>;
-type WorkflowTaskScheduledEvent = BaseEvent &
-  Pick<HistoryEvent, 'workflowTaskScheduledEventAttributes'>;
-type WorkflowTaskStartedEvent = BaseEvent &
-  Pick<HistoryEvent, 'workflowTaskStartedEventAttributes'>;
-type WorkflowTaskCompletedEvent = BaseEvent &
-  Pick<HistoryEvent, 'workflowTaskCompletedEventAttributes'>;
-type WorkflowTaskTimedOutEvent = BaseEvent &
-  Pick<HistoryEvent, 'workflowTaskTimedOutEventAttributes'>;
-type WorkflowTaskFailedEvent = BaseEvent &
-  Pick<HistoryEvent, 'workflowTaskFailedEventAttributes'>;
-type ActivityTaskScheduledEvent = BaseEvent &
-  Pick<HistoryEvent, 'activityTaskScheduledEventAttributes'>;
-type ActivityTaskStartedEvent = BaseEvent &
-  Pick<HistoryEvent, 'activityTaskStartedEventAttributes'>;
-type ActivityTaskCompletedEvent = BaseEvent &
-  Pick<HistoryEvent, 'activityTaskCompletedEventAttributes'>;
-type ActivityTaskFailedEvent = BaseEvent &
-  Pick<HistoryEvent, 'activityTaskFailedEventAttributes'>;
-type ActivityTaskTimedOutEvent = BaseEvent &
-  Pick<HistoryEvent, 'activityTaskTimedOutEventAttributes'>;
-type TimerStartedEvent = BaseEvent &
-  Pick<HistoryEvent, 'timerStartedEventAttributes'>;
-type TimerFiredEvent = BaseEvent &
-  Pick<HistoryEvent, 'timerFiredEventAttributes'>;
-type ActivityTaskCancelRequestedEvent = BaseEvent &
-  Pick<HistoryEvent, 'activityTaskCancelRequestedEventAttributes'>;
-type ActivityTaskCanceledEvent = BaseEvent &
-  Pick<HistoryEvent, 'activityTaskCanceledEventAttributes'>;
-type TimerCanceledEvent = BaseEvent &
-  Pick<HistoryEvent, 'timerCanceledEventAttributes'>;
-type MarkerRecordedEvent = BaseEvent &
-  Pick<HistoryEvent, 'markerRecordedEventAttributes'>;
-type WorkflowExecutionSignaledEvent = BaseEvent &
-  Pick<HistoryEvent, 'workflowExecutionSignaledEventAttributes'>;
-type WorkflowExecutionTerminatedEvent = BaseEvent &
-  Pick<HistoryEvent, 'workflowExecutionTerminatedEventAttributes'>;
-type WorkflowExecutionCancelRequestedEvent = BaseEvent &
-  Pick<HistoryEvent, 'workflowExecutionCancelRequestedEventAttributes'>;
-type WorkflowExecutionCanceledEvent = BaseEvent &
-  Pick<HistoryEvent, 'workflowExecutionCanceledEventAttributes'>;
-type RequestCancelExternalWorkflowExecutionInitiatedEvent = BaseEvent &
-  Pick<
-    HistoryEvent,
-    'requestCancelExternalWorkflowExecutionInitiatedEventAttributes'
-  >;
-type RequestCancelExternalWorkflowExecutionFailedEvent = BaseEvent &
-  Pick<
-    HistoryEvent,
-    'requestCancelExternalWorkflowExecutionFailedEventAttributes'
-  >;
-type ExternalWorkflowExecutionCancelRequestedEvent = BaseEvent &
-  Pick<HistoryEvent, 'externalWorkflowExecutionCancelRequestedEventAttributes'>;
-type WorkflowExecutionContinuedAsNewEvent = BaseEvent &
-  Pick<HistoryEvent, 'workflowExecutionContinuedAsNewEventAttributes'>;
-type StartChildWorkflowExecutionInitiatedEvent = BaseEvent &
-  Pick<HistoryEvent, 'startChildWorkflowExecutionInitiatedEventAttributes'>;
-type StartChildWorkflowExecutionFailedEvent = BaseEvent &
-  Pick<HistoryEvent, 'startChildWorkflowExecutionFailedEventAttributes'>;
-type ChildWorkflowExecutionStartedEvent = BaseEvent &
-  Pick<HistoryEvent, 'childWorkflowExecutionStartedEventAttributes'>;
-type ChildWorkflowExecutionCompletedEvent = BaseEvent &
-  Pick<HistoryEvent, 'childWorkflowExecutionCompletedEventAttributes'>;
-type ChildWorkflowExecutionFailedEvent = BaseEvent &
-  Pick<HistoryEvent, 'childWorkflowExecutionFailedEventAttributes'>;
-type ChildWorkflowExecutionCanceledEvent = BaseEvent &
-  Pick<HistoryEvent, 'childWorkflowExecutionCanceledEventAttributes'>;
-type ChildWorkflowExecutionTimedOutEvent = BaseEvent &
-  Pick<HistoryEvent, 'childWorkflowExecutionTimedOutEventAttributes'>;
-type ChildWorkflowExecutionTerminatedEvent = BaseEvent &
-  Pick<HistoryEvent, 'childWorkflowExecutionTerminatedEventAttributes'>;
-type SignalExternalWorkflowExecutionInitiatedEvent = BaseEvent &
-  Pick<HistoryEvent, 'signalExternalWorkflowExecutionInitiatedEventAttributes'>;
-type SignalExternalWorkflowExecutionFailedEvent = BaseEvent &
-  Pick<HistoryEvent, 'signalExternalWorkflowExecutionFailedEventAttributes'>;
-type ExternalWorkflowExecutionSignaledEvent = BaseEvent &
-  Pick<HistoryEvent, 'externalWorkflowExecutionSignaledEventAttributes'>;
-type UpsertWorkflowSearchAttributesEvent = BaseEvent &
-  Pick<HistoryEvent, 'upsertWorkflowSearchAttributesEventAttributes'>;
-
-type EventAttributeKeys =
-  | 'activityTaskCancelRequestedEventAttributes'
-  | 'activityTaskCompletedEventAttributes'
-  | 'activityTaskFailedEventAttributes'
-  | 'activityTaskScheduledEventAttributes'
-  | 'activityTaskStartedEventAttributes'
-  | 'activityTaskTimedOutEventAttributes'
-  | 'childWorkflowExecutionCompletedEventAttributes'
-  | 'childWorkflowExecutionStartedEventAttributes'
-  | 'workflowTaskCompletedEventAttributes'
-  | 'workflowTaskScheduledEventAttributes'
-  | 'workflowTaskStartedEventAttributes'
-  | 'workflowTaskTimedOutEventAttributes'
-  | 'externalWorkflowExecutionSignaledEventAttributes'
-  | 'startChildWorkflowExecutionInitiatedEventAttributes'
-  | 'signalExternalWorkflowExecutionInitiatedEventAttributes'
-  | 'timerStartedEventAttributes'
-  | 'workflowExecutionStartedEventAttributes'
-  | 'workflowExecutionCompletedEventAttributes'
-  | 'workflowExecutionFailedEventAttributes'
-  | 'workflowExecutionTimedOutEventAttributes'
-  | 'workflowTaskFailedEventAttributes'
-  | 'childWorkflowExecutionFailedEventAttributes';
-
-interface TaskCancelAttrs extends ActivityTaskCancelRequestedEventAttributes {
-  type: 'activityTaskCancelRequestedEventAttribute';
-}
-interface TaskCompleteAttrs extends ActivityTaskCompletedEventAttributes {
-  type: 'activityTaskCompletedEventAttributes';
-}
-interface TaskFailedAttrs extends ActivityTaskFailedEventAttributes {
-  type: 'activityTaskFailedEventAttributes';
-}
-interface TaskScheduledAttrs extends ActivityTaskScheduledEventAttributes {
-  type: 'activityTaskScheduledEventAttributes';
-}
-interface TaskStartedAttrs extends ActivityTaskStartedEventAttributes {
-  type: 'activityTaskStartedEventAttributes';
-}
-interface TaskTimeoutAttrs extends ActivityTaskTimedOutEventAttributes {
-  type: 'activityTaskTimedOutEventAttributes';
-}
-interface ChildTaskCompleteAttrs
-  extends ChildWorkflowExecutionCompletedEventAttributes {
-  type: 'childWorkflowExecutionCompletedEventAttributes';
-}
-interface ChildWorkflowStartedAttrs
-  extends ChildWorkflowExecutionStartedEventAttributes {
-  type: 'childWorkflowExecutionStartedEventAttributes';
-}
-interface TaskWorkflowCompletedAttrs
-  extends WorkflowTaskCompletedEventAttributes {
-  type: 'workflowTaskCompletedEventAttributes';
-}
-interface TaskWorfklowScheduledAttrs
-  extends WorkflowTaskScheduledEventAttributes {
-  type: 'workflowTaskScheduledEventAttributes';
-}
-interface TaskWorkflowStartedAttrs extends WorkflowTaskStartedEventAttributes {
-  type: 'workflowTaskStartedEventAttributes';
-}
-
-interface TaskWorfklowTimedOut extends WorkflowTaskTimedOutEventAttributes {
-  type: 'workflowTaskTimedOutEventAttributes';
-}
-
-interface ExternalWorkflowExecutionSignaledAttrs
-  extends ExternalWorkflowExecutionSignaledEventAttributes {
-  type: 'externalWorkflowExecutionSignaledEventAttributes';
-}
-
-interface ChildWorkflowStartExecutionInitiatedAttrs
-  extends StartChildWorkflowExecutionInitiatedEventAttributes {
-  type: 'startChildWorkflowExecutionInitiatedEventAttributes';
-}
-interface ExternalWorkflowExecutionInitiatedAttrs
-  extends SignalExternalWorkflowExecutionInitiatedEventAttributes {
-  type: 'signalExternalWorkflowExecutionInitiatedEventAttributes';
-}
-
-interface TimerStartedAttrs extends TimerStartedEventAttributes {
-  type: 'timerStartedEventAttributes';
-}
-interface WorkflowExecutionStartedAttrs
-  extends WorkflowExecutionStartedEventAttributes {
-  type: 'workflowExecutionStartedEventAttributes';
-}
-interface WorkflowExecutionCompletedAttrs
-  extends WorkflowExecutionCompletedEventAttributes {
-  type: 'workflowExecutionCompletedEventAttributes';
-}
-interface WorfklowExecutionFailedAttrs
-  extends WorkflowExecutionFailedEventAttributes {
-  type: 'workflowExecutionFailedEventAttributes';
-}
-interface WorkflowExecutionTimedOutAttrs
-  extends WorkflowExecutionTimedOutEventAttributes {
-  type: 'workflowExecutionTimedOutEventAttributes';
-}
-interface WorkflowTaskFailedAttrs extends WorkflowTaskFailedEventAttributes {
-  type: 'workflowTaskFailedEventAttributes';
-}
-
-interface ChildWorkflowExecutionFailedAttrs
-  extends ChildWorkflowExecutionFailedEventAttributes {
-  type: 'childWorkflowExecutionFailedEventAttributes';
-}
+type EventType = import('$lib/utilities/is-event-type').EventType;
 
-type EventAttribute =
-  | TaskCompleteAttrs
-  | TaskCancelAttrs
-  | TaskFailedAttrs
-  | TaskScheduledAttrs
-  | TaskStartedAttrs
-  | TaskTimeoutAttrs
-  | ChildTaskCompleteAttrs
-  | ChildWorkflowStartedAttrs
-  | TaskWorkflowCompletedAttrs
-  | TaskWorfklowScheduledAttrs
-  | TaskWorkflowStartedAttrs
-  | TaskWorfklowTimedOut
-  | ExternalWorkflowExecutionSignaledAttrs
-  | ChildWorkflowStartExecutionInitiatedAttrs
-  | ExternalWorkflowExecutionInitiatedAttrs
-  | TimerStartedAttrs
-  | WorkflowExecutionStartedAttrs
-  | WorkflowExecutionCompletedAttrs
-  | WorfklowExecutionFailedAttrs
-  | WorkflowExecutionTimedOutAttrs
-  | WorkflowTaskFailedAttrs
-  | ChildWorkflowExecutionFailedAttrs;
+type ActivityType = import('$lib/utilities/is-event-type').ActivityType;
+type TimerType = import('$lib/utilities/is-event-type').TimerType;
+type SignalType = import('$lib/utilities/is-event-type').SignalType;
+type MarkerType = import('$lib/utilities/is-event-type').MarkerType;
+type ChildType = import('$lib/utilities/is-event-type').ChildType;
+type EventTypeCategory = import('$lib/utilities/get-event-categorization');
 
 interface HistoryEventWithId extends HistoryEvent {
   id: string;
@@ -309,73 +16,133 @@ interface HistoryEventWithId extends HistoryEvent {
   attributes: EventAttribute;
 }
 
-type PendingActivityInfo = import('$types').PendingActivityInfo;
-
 interface PendingActivity extends PendingActivityInfo {
+  id: typeof PendingActivityInfo.activityId;
   state: 'Unspecified' | 'Scheduled' | 'Started' | 'CancelRequested';
   activityType: { name: string };
-  id: typeof PendingActivityInfo.activityId;
 }
 
-type EventType =
-  | ActivityType
-  | TimerType
-  | SignalType
-  | MarkerType
-  | ChildType
-  | 'ChildWorkflowExecutionCanceled'
-  | 'ChildWorkflowExecutionFailed'
-  | 'ChildWorkflowExecutionTerminated'
-  | 'ChildWorkflowExecutionTimedOut'
-  | 'StartChildWorkflowExecutionFailed'
-  | 'SignalExternalWorkflowExecutionFailed'
-  | 'SignalExternalWorkflowExecutionInitiated'
-  | 'WorkflowExecutionCanceled'
-  | 'WorkflowExecutionCancelRequested'
-  | 'WorkflowExecutionCompleted'
-  | 'WorkflowExecutionContinuedAsNew'
-  | 'WorkflowExecutionFailed'
-  | 'WorkflowExecutionStarted'
-  | 'WorkflowExecutionTerminated'
-  | 'WorkflowExecutionTimedOut'
-  | 'WorkflowTaskCompleted'
-  | 'WorkflowTaskFailed'
-  | 'WorkflowTaskScheduled'
-  | 'WorkflowTaskStarted'
-  | 'WorkflowTaskTimedOut'
-  | 'ExternalWorkflowExecutionCancelRequested'
-  | 'RequestCancelExternalWorkflowExecutionFailed'
-  | 'RequestCancelExternalWorkflowExecutionInitiated'
-  | 'UpsertWorkflowSearchAttributes';
-
-type ActivityType =
-  | 'ActivityTaskCanceled'
-  | 'ActivityTaskCancelRequested'
-  | 'ActivityTaskCompleted'
-  | 'ActivityTaskFailed'
-  | 'ActivityTaskScheduled'
-  | 'ActivityTaskStarted'
-  | 'ActivityTaskTimedOut';
-
-type TimerType = 'TimerCanceled' | 'TimerFired' | 'TimerStarted';
-
-type SignalType =
-  | 'WorkflowExecutionSignaled'
-  | 'ExternalWorkflowExecutionSignaled';
-
-type MarkerType = 'MarkerRecorded';
-
-type ChildType =
-  | 'StartChildWorkflowExecutionInitiated'
-  | 'ChildWorkflowExecutionStarted'
-  | 'ChildWorkflowExecutionCompleted';
-
-type EventTypeCategory =
-  | 'activity'
-  | 'child-workflow'
-  | 'signal'
-  | 'timer'
-  | 'workflow'
-  | 'command';
-
-type EventualHistoryEvents = PromiseLike<HistoryEventWithId[]>;
+type CommonEventKey =
+  | 'id'
+  | 'eventType'
+  | 'attributes'
+  | 'eventId'
+  | 'eventTime'
+  | 'version'
+  | 'taskId';
+
+type CommonHistoryEvent = Pick<HistoryEventWithId, CommonEventKey>;
+
+type EventAttributeKey = keyof Omit<HistoryEvent, CommonEventKey>;
+type EventAttribute = HistoryEvent[EventAttributeKey];
+type EventAttributesWithType<K = EventAttributeKey> = HistoryEvent[K] & {
+  type: K;
+};
+
+type EventWithAttributes<A extends EventAttributeKey> = CommonHistoryEvent &
+  Pick<HistoryEventWithId, A> & { attributes: EventAttributesWithType<A> };
+
+type WorkflowExecutionStartedEvent =
+  EventWithAttributes<'workflowExecutionStartedEventAttributes'>;
+type WorkflowExecutionCompletedEvent =
+  EventWithAttributes<'workflowExecutionCompletedEventAttributes'>;
+type WorkflowExecutionFailedEvent =
+  EventWithAttributes<'workflowExecutionFailedEventAttributes'>;
+type WorkflowExecutionTimedOutEvent =
+  EventWithAttributes<'workflowExecutionTimedOutEventAttributes'>;
+type WorkflowTaskScheduledEvent =
+  EventWithAttributes<'workflowTaskScheduledEventAttributes'>;
+type WorkflowTaskStartedEvent =
+  EventWithAttributes<'workflowTaskStartedEventAttributes'>;
+type WorkflowTaskCompletedEvent =
+  EventWithAttributes<'workflowTaskCompletedEventAttributes'>;
+type WorkflowTaskTimedOutEvent =
+  EventWithAttributes<'workflowTaskTimedOutEventAttributes'>;
+type WorkflowTaskFailedEvent =
+  EventWithAttributes<'workflowTaskFailedEventAttributes'>;
+type ActivityTaskScheduledEvent =
+  EventWithAttributes<'activityTaskScheduledEventAttributes'>;
+type ActivityTaskStartedEvent =
+  EventWithAttributes<'activityTaskStartedEventAttributes'>;
+type ActivityTaskCompletedEvent =
+  EventWithAttributes<'activityTaskCompletedEventAttributes'>;
+type ActivityTaskFailedEvent =
+  EventWithAttributes<'activityTaskFailedEventAttributes'>;
+type ActivityTaskTimedOutEvent =
+  EventWithAttributes<'activityTaskTimedOutEventAttributes'>;
+type TimerStartedEvent = EventWithAttributes<'timerStartedEventAttributes'>;
+type TimerFiredEvent = EventWithAttributes<'timerFiredEventAttributes'>;
+type ActivityTaskCancelRequestedEvent =
+  EventWithAttributes<'activityTaskCancelRequestedEventAttributes'>;
+type ActivityTaskCanceledEvent =
+  EventWithAttributes<'activityTaskCanceledEventAttributes'>;
+type TimerCanceledEvent = EventWithAttributes<'timerCanceledEventAttributes'>;
+type MarkerRecordedEvent = EventWithAttributes<'markerRecordedEventAttributes'>;
+type WorkflowExecutionSignaledEvent =
+  EventWithAttributes<'workflowExecutionSignaledEventAttributes'>;
+type WorkflowExecutionTerminatedEvent =
+  EventWithAttributes<'workflowExecutionTerminatedEventAttributes'>;
+type WorkflowExecutionCancelRequestedEvent =
+  EventWithAttributes<'workflowExecutionCancelRequestedEventAttributes'>;
+type WorkflowExecutionCanceledEvent =
+  EventWithAttributes<'workflowExecutionCanceledEventAttributes'>;
+type RequestCancelExternalWorkflowExecutionInitiatedEvent =
+  EventWithAttributes<'requestCancelExternalWorkflowExecutionInitiatedEventAttributes'>;
+type RequestCancelExternalWorkflowExecutionFailedEvent =
+  EventWithAttributes<'requestCancelExternalWorkflowExecutionFailedEventAttributes'>;
+type ExternalWorkflowExecutionCancelRequestedEvent =
+  EventWithAttributes<'externalWorkflowExecutionCancelRequestedEventAttributes'>;
+type WorkflowExecutionContinuedAsNewEvent =
+  EventWithAttributes<'workflowExecutionContinuedAsNewEventAttributes'>;
+type StartChildWorkflowExecutionInitiatedEvent =
+  EventWithAttributes<'startChildWorkflowExecutionInitiatedEventAttributes'>;
+type StartChildWorkflowExecutionFailedEvent =
+  EventWithAttributes<'startChildWorkflowExecutionFailedEventAttributes'>;
+type ChildWorkflowExecutionStartedEvent =
+  EventWithAttributes<'childWorkflowExecutionStartedEventAttributes'>;
+type ChildWorkflowExecutionCompletedEvent =
+  EventWithAttributes<'childWorkflowExecutionCompletedEventAttributes'>;
+type ChildWorkflowExecutionFailedEvent =
+  EventWithAttributes<'childWorkflowExecutionFailedEventAttributes'>;
+type ChildWorkflowExecutionCanceledEvent =
+  EventWithAttributes<'childWorkflowExecutionCanceledEventAttributes'>;
+type ChildWorkflowExecutionTimedOutEvent =
+  EventWithAttributes<'childWorkflowExecutionTimedOutEventAttributes'>;
+type ChildWorkflowExecutionTerminatedEvent =
+  EventWithAttributes<'childWorkflowExecutionTerminatedEventAttributes'>;
+type SignalExternalWorkflowExecutionInitiatedEvent =
+  EventWithAttributes<'signalExternalWorkflowExecutionInitiatedEventAttributes'>;
+type SignalExternalWorkflowExecutionFailedEvent =
+  EventWithAttributes<'signalExternalWorkflowExecutionFailedEventAttributes'>;
+type ExternalWorkflowExecutionSignaledEvent =
+  EventWithAttributes<'externalWorkflowExecutionSignaledEventAttributes'>;
+type UpsertWorkflowSearchAttributesEvent =
+  EventWithAttributes<'upsertWorkflowSearchAttributesEventAttributes'>;
+
+type ActivityEvent = ActivityTaskScheduledEvent &
+  ActivityTaskStartedEvent &
+  ActivityTaskCompletedEvent &
+  ActivityTaskFailedEvent &
+  ActivityTaskTimedOutEvent &
+  ActivityTaskCancelRequestedEvent &
+  ActivityTaskCanceledEvent;
+
+type TimerEvent = TimerCanceledEvent & TimerStartedEvent & TimerFiredEvent;
+
+type SignalEvent = SignalExternalWorkflowExecutionInitiatedEvent &
+  SignalExternalWorkflowExecutionFailedEvent &
+  WorkflowExecutionSignaledEvent;
+
+type MarkerEvent = MarkerRecordedEvent;
+
+type ChildEvent = StartChildWorkflowExecutionInitiatedEvent &
+  StartChildWorkflowExecutionFailedEvent &
+  ChildWorkflowExecutionStartedEvent &
+  ChildWorkflowExecutionCompletedEvent &
+  ChildWorkflowExecutionFailedEvent &
+  ChildWorkflowExecutionCanceledEvent &
+  ChildWorkflowExecutionTimedOutEvent &
+  ChildWorkflowExecutionTerminatedEvent;
+
+type EventsOrActivities = (HistoryEventWithId | PendingActivity)[];
+type IterableEvents = EventsOrActivities | CompactEventGroups;
diff --git a/src/lib/components/badge.svelte b/src/lib/components/badge.svelte
new file mode 100644
index 00000000..b6f1aeaf
--- /dev/null
+++ b/src/lib/components/badge.svelte
@@ -0,0 +1,28 @@
+<script lang="ts">
+  type BadgeTypes = 'alpha' | 'beta';
+
+  export let type: BadgeTypes;
+</script>
+
+<div
+  class={`${type}     
+        rounded-sm
+        text-center 
+        inline-block
+        px-1
+        ml-1
+        text-sm
+        align-middle`}
+>
+  <slot />
+</div>
+
+<style lang="postcss">
+  .beta {
+    @apply bg-blue-100  text-blue-700  border-blue-700;
+  }
+
+  .alpha {
+    @apply bg-purple-100 text-purple-700 border-purple-700;
+  }
+</style>
diff --git a/src/lib/components/error-boundary.svelte b/src/lib/components/error-boundary.svelte
index 4829999f..8d24d26a 100644
--- a/src/lib/components/error-boundary.svelte
+++ b/src/lib/components/error-boundary.svelte
@@ -1,12 +1,11 @@
 <script lang="ts">
   import { networkError } from '$lib/stores/error';
-  import { isNetworkError } from '$lib/utilities/is-network-error';
   import Error from './error.svelte';
 
   export let error = null;
   export let onError = null;
 
-  let theError;
+  let theError: NetworkError | any;
 
   $: {
     if (error && $error) {
@@ -29,9 +28,6 @@
 {/if}
 
 <style>
-  .error {
-    border: 1px solid red;
-  }
   .trace {
     font-family: monospace;
   }
diff --git a/src/lib/components/event-table.svelte b/src/lib/components/event-table.svelte
index 42896787..61cb6cf9 100644
--- a/src/lib/components/event-table.svelte
+++ b/src/lib/components/event-table.svelte
@@ -1,9 +1,8 @@
 <script lang="ts">
-  import type { EventGroups } from '$lib/models/events-group';
   import Pagination from './pagination.svelte';
   import Event from './event.svelte';
 
-  export let events: (HistoryEventWithId | PendingActivity)[] | EventGroups;
+  export let events: IterableEvents;
 </script>
 
 <Pagination items={[...events]} let:visibleItems>
diff --git a/src/lib/components/event.svelte b/src/lib/components/event.svelte
index 6a2ebc89..f7b8a531 100644
--- a/src/lib/components/event.svelte
+++ b/src/lib/components/event.svelte
@@ -3,20 +3,18 @@
   import { faCalendar } from '@fortawesome/free-solid-svg-icons';
   import { page } from '$app/stores';
 
-  import type { EventsGroup } from '$lib/models/events-group';
-
   import { formatEvent } from '$lib/utilities/get-event-classification';
   import { formatDate } from '$lib/utilities/format-date';
 
   import EventLabel from '$lib/components/event-label.svelte';
 
-  export let event: HistoryEventWithId | PendingActivity | EventsGroup;
+  export let event: HistoryEventWithId | PendingActivity | CompactEventGroup;
 
   let { routeFor, pending, timeStamp, name, tag, classification, id } =
     formatEvent(event);
 
   let { namespace, workflow: workflowId, run: runId } = $page.params;
-  let href = routeFor({ namespace, workflowId, runId });
+  let href = routeFor({ namespace, workflowId, runId, query: $page.query });
 </script>
 
 <a
diff --git a/src/lib/components/export-history.svelte b/src/lib/components/export-history.svelte
index 3bcc7f8d..78f2b7da 100644
--- a/src/lib/components/export-history.svelte
+++ b/src/lib/components/export-history.svelte
@@ -1,13 +1,17 @@
 <script lang="ts">
+  import { page } from '$app/stores';
+
   import ToggleButton from '$lib/components/toggle-button.svelte';
   import { fetchRawEvents } from '$lib/services/events-service';
   import { faDownload } from '@fortawesome/free-solid-svg-icons';
 
+  const { workflow: executionId, run: runId, namespace } = $page.params;
+
   const exportHistory = async () => {
     const events = await fetchRawEvents({
       namespace,
-      executionId: workflowId,
-      runId: runId,
+      executionId,
+      runId,
     });
 
     const content = JSON.stringify(events, null, 2);
@@ -21,10 +25,6 @@
       a.click();
     }
   };
-
-  export let namespace: string;
-  export let workflowId: string;
-  export let runId: string;
 </script>
 
 <ToggleButton icon={faDownload} on:click={exportHistory} />
diff --git a/src/lib/models/event-history.ts b/src/lib/models/event-history.ts
index a88a580e..2db9275e 100644
--- a/src/lib/models/event-history.ts
+++ b/src/lib/models/event-history.ts
@@ -1,17 +1,17 @@
 import { convertPayloadToJson } from '$lib/utilities/decode-payload';
-import type { GetWorkflowExecutionHistoryResponse, HistoryEvent } from '$types';
+import { findAttributesAndKey } from '$lib/utilities/is-event-type';
+
+import type { GetWorkflowExecutionHistoryResponse } from '$types';
 
 export async function getEventAttributes(
   historyEvent: HistoryEvent,
-): Promise<EventAttribute> {
-  const attributeKey = Object.keys(historyEvent).find((key) =>
-    key.includes('Attributes'),
-  );
-  const attributes = await convertPayloadToJson(historyEvent[attributeKey]);
+): Promise<EventAttributesWithType> {
+  const { key, attributes } = findAttributesAndKey(historyEvent);
+  const decodedAttributes = await convertPayloadToJson(attributes);
 
   return {
-    type: attributeKey as EventAttributeKeys,
-    ...attributes,
+    type: key,
+    ...decodedAttributes,
   };
 }
 
diff --git a/src/lib/models/events-group.ts b/src/lib/models/events-group.ts
deleted file mode 100644
index 6d53c407..00000000
--- a/src/lib/models/events-group.ts
+++ /dev/null
@@ -1,259 +0,0 @@
-type ScheduledActivityEvent = HistoryEventWithId & {
-  eventType: 'ActivityTaskScheduled';
-};
-
-type CompactEventType =
-  | ActivityType
-  | TimerType
-  | SignalType
-  | MarkerType
-  | ChildType;
-
-type CompactEvent = HistoryEventWithId & {
-  eventType: CompactEventType;
-};
-
-const activityTypes = [
-  'ActivityTaskCanceled',
-  'ActivityTaskCancelRequested',
-  'ActivityTaskCompleted',
-  'ActivityTaskFailed',
-  'ActivityTaskScheduled',
-  'ActivityTaskStarted',
-  'ActivityTaskTimedOut',
-];
-
-const timerTypes = ['TimerStarted', 'TimerCanceled', 'TimerFired'];
-const signalTypes = ['WorkflowExecutionSignaled'];
-const markerTypes = ['MarkerRecorded'];
-
-const childTypes = [
-  'StartChildWorkflowExecutionInitiated',
-  'ChildWorkflowExecutionStarted',
-  'ChildWorkflowExecutionCompleted',
-];
-
-const isCompactEvent = (event: HistoryEventWithId): event is CompactEvent => {
-  if (
-    activityTypes.includes(event.eventType) ||
-    timerTypes.includes(event.eventType) ||
-    isSignalEvent(event) ||
-    isMarkerEvent(event) ||
-    childTypes.includes(event.eventType)
-  )
-    return true;
-  return false;
-};
-
-const isActivityScheduledEvent = (
-  event: HistoryEventWithId,
-): event is ScheduledActivityEvent => {
-  return event.eventType === 'ActivityTaskScheduled';
-};
-
-const isChildWorkflowInitializedEvent = (
-  event: HistoryEventWithId,
-): event is ScheduledActivityEvent => {
-  return event.eventType === 'StartChildWorkflowExecutionInitiated';
-};
-
-const isTimerStartedEvent = (
-  event: HistoryEventWithId,
-): event is ScheduledActivityEvent => {
-  return event.eventType === 'TimerStarted';
-};
-
-const isSignalEvent = (
-  event: HistoryEventWithId,
-): event is ScheduledActivityEvent => {
-  return signalTypes.includes(event.eventType);
-};
-
-const isMarkerEvent = (
-  event: HistoryEventWithId,
-): event is ScheduledActivityEvent => {
-  return markerTypes.includes(event.eventType);
-};
-
-export class EventsGroup {
-  id: string;
-  name: string;
-  private _events: Map<CompactEventType, HistoryEventWithId> = new Map();
-
-  constructor(event: CompactEvent) {
-    if (isActivityScheduledEvent(event)) {
-      this.id = event?.activityTaskScheduledEventAttributes?.activityId;
-      this.name =
-        event?.activityTaskScheduledEventAttributes?.activityType?.name;
-    }
-
-    if (isTimerStartedEvent(event)) {
-      this.id = event.id;
-      this.name = `Timer ${event?.timerStartedEventAttributes?.timerId} (${event?.timerStartedEventAttributes?.startToFireTimeout})`;
-    }
-
-    if (isSignalEvent(event)) {
-      this.id = event.id;
-      this.name = `Signal: ${event?.workflowExecutionSignaledEventAttributes?.signalName}`;
-    }
-
-    if (isMarkerEvent(event)) {
-      this.id = event.id;
-      this.name = `Marker: ${event?.markerRecordedEventAttributes?.markerName}`;
-    }
-
-    if (isChildWorkflowInitializedEvent(event)) {
-      this.id = event.id;
-      this.name = `Child Workflow: ${event?.startChildWorkflowExecutionInitiatedEventAttributes?.workflowType?.name}`;
-    }
-
-    this.set(event.eventType, event);
-  }
-
-  set(type: CompactEventType, event: HistoryEventWithId): void {
-    this._events.set(type, event);
-  }
-
-  get(type: CompactEventType): HistoryEventWithId {
-    return this._events.get(type);
-  }
-
-  toArray(): HistoryEventWithId[] {
-    return [...this];
-  }
-
-  get events(): HistoryEventWithId[] {
-    return [...this];
-  }
-
-  get length(): number {
-    return this._events.size;
-  }
-
-  get last(): HistoryEventWithId {
-    let last: HistoryEventWithId;
-
-    for (const event of this) {
-      last = event;
-    }
-
-    return last;
-  }
-
-  [Symbol.iterator](): IterableIterator<HistoryEventWithId> {
-    return this._events.values();
-  }
-}
-
-export class EventGroups {
-  private _groups: Map<string, EventsGroup> = new Map();
-
-  static async fromPromise(
-    events: PromiseLike<HistoryEventWithId[]>,
-  ): Promise<EventGroups> {
-    return EventGroups.from(await events);
-  }
-
-  static from = (
-    events: HistoryEventWithId[] = [],
-    groups = new EventGroups(),
-  ): EventGroups => {
-    for (const event of events) {
-      if (isCompactEvent(event)) {
-        groups.add(event);
-      }
-    }
-
-    return groups;
-  };
-
-  constructor(event?: CompactEvent | HistoryEventWithId[]) {
-    if (Array.isArray(event)) {
-      EventGroups.from(event, this);
-    } else if (event) {
-      this.add(event);
-    }
-  }
-
-  get(id: string | number | Long): EventsGroup {
-    return this._groups.get(String(id));
-  }
-
-  add(event: CompactEvent): void {
-    if (
-      isActivityScheduledEvent(event) ||
-      isTimerStartedEvent(event) ||
-      isSignalEvent(event) ||
-      isMarkerEvent(event) ||
-      isChildWorkflowInitializedEvent(event)
-    ) {
-      const id = String(event.id);
-      this._groups.set(id, new EventsGroup(event));
-    }
-
-    if (event.eventType === 'ActivityTaskStarted') {
-      const { scheduledEventId } = event.activityTaskStartedEventAttributes;
-      this.get(scheduledEventId).set(event.eventType, event);
-    }
-
-    if (event.eventType === 'ActivityTaskCanceled') {
-      const { scheduledEventId } = event.activityTaskCanceledEventAttributes;
-      this.get(scheduledEventId).set(event.eventType, event);
-    }
-
-    if (event.eventType === 'ActivityTaskCancelRequested') {
-      const { scheduledEventId } =
-        event.activityTaskCancelRequestedEventAttributes;
-      this.get(scheduledEventId).set(event.eventType, event);
-    }
-
-    if (event.eventType === 'ActivityTaskFailed') {
-      const { scheduledEventId } = event.activityTaskFailedEventAttributes;
-      this.get(scheduledEventId).set(event.eventType, event);
-    }
-
-    if (event.eventType === 'ActivityTaskTimedOut') {
-      const { scheduledEventId } = event.activityTaskTimedOutEventAttributes;
-      this.get(scheduledEventId).set(event.eventType, event);
-    }
-
-    if (event.eventType === 'ActivityTaskCompleted') {
-      const { scheduledEventId } = event.activityTaskCompletedEventAttributes;
-      this.get(scheduledEventId).set(event.eventType, event);
-    }
-
-    if (event.eventType === 'ChildWorkflowExecutionStarted') {
-      const { initiatedEventId } =
-        event.childWorkflowExecutionStartedEventAttributes;
-      this.get(initiatedEventId).set(event.eventType, event);
-    }
-
-    if (event.eventType === 'ChildWorkflowExecutionCompleted') {
-      const { initiatedEventId } =
-        event.childWorkflowExecutionCompletedEventAttributes;
-      this.get(initiatedEventId).set(event.eventType, event);
-    }
-
-    if (event.eventType === 'TimerFired') {
-      const { startedEventId } = event.timerFiredEventAttributes;
-      this.get(startedEventId).set(event.eventType, event);
-    }
-
-    if (event.eventType === 'TimerCanceled') {
-      const { startedEventId } = event.timerCanceledEventAttributes;
-      this.get(startedEventId).set(event.eventType, event);
-    }
-  }
-
-  get length(): number {
-    return this._groups.size;
-  }
-
-  slice(...args: Parameters<typeof Array.prototype.slice>): EventsGroup[] {
-    return [...this].slice(...args);
-  }
-
-  [Symbol.iterator](): IterableIterator<EventsGroup> {
-    return this._groups.values();
-  }
-}
diff --git a/src/lib/models/group-events/create-event-group.ts b/src/lib/models/group-events/create-event-group.ts
new file mode 100644
index 00000000..d5bab810
--- /dev/null
+++ b/src/lib/models/group-events/create-event-group.ts
@@ -0,0 +1,46 @@
+import { getName } from '$lib/utilities/get-event-name';
+import { getGroupId } from './get-group-id';
+
+import {
+  isActivityTaskScheduledEvent,
+  isMarkerRecordedEvent,
+  isSignalExternalWorkflowExecutionInitiatedEvent,
+  isStartChildWorkflowExecutionInitiatedEvent,
+  isTimerStartedEvent,
+} from '$lib/utilities/is-event-type';
+
+type StartingEvents = {
+  Activity: ActivityTaskScheduledEvent;
+  ChildWorkflow: StartChildWorkflowExecutionInitiatedEvent;
+  Timer: TimerStartedEvent;
+  Signal: SignalExternalWorkflowExecutionInitiatedEvent;
+  Marker: MarkerRecordedEvent;
+};
+
+const createGroupFor = <K extends keyof StartingEvents>(
+  event: StartingEvents[K],
+): CompactEventGroup => {
+  const id = getGroupId(event);
+  const name = getName(event);
+
+  const events = new Map<EventType, HistoryEventWithId>();
+
+  events.set(event.eventType, event);
+
+  return { id, name, events };
+};
+
+export const createEventGroup = (event: CommonHistoryEvent) => {
+  if (isActivityTaskScheduledEvent(event))
+    return createGroupFor<'Activity'>(event);
+
+  if (isStartChildWorkflowExecutionInitiatedEvent(event))
+    return createGroupFor<'ChildWorkflow'>(event);
+
+  if (isTimerStartedEvent(event)) return createGroupFor<'Timer'>(event);
+
+  if (isSignalExternalWorkflowExecutionInitiatedEvent(event))
+    return createGroupFor<'Signal'>(event);
+
+  if (isMarkerRecordedEvent(event)) return createGroupFor<'Marker'>(event);
+};
diff --git a/src/lib/models/group-events/event-groups.d.ts b/src/lib/models/group-events/event-groups.d.ts
new file mode 100644
index 00000000..c303980d
--- /dev/null
+++ b/src/lib/models/group-events/event-groups.d.ts
@@ -0,0 +1,10 @@
+type CompactEventGroup<T = EventType, E = HistoryEventWithId> = {
+  id: string;
+  name: string;
+  events: Map<T, E>;
+};
+
+type CompactEventGroups<
+  T = EventType,
+  E = HistoryEventWithId,
+> = CompactEventGroup[];
diff --git a/src/lib/models/group-events/get-group-id.ts b/src/lib/models/group-events/get-group-id.ts
new file mode 100644
index 00000000..15dec7e6
--- /dev/null
+++ b/src/lib/models/group-events/get-group-id.ts
@@ -0,0 +1,74 @@
+import {
+  isActivityTaskCanceledEvent,
+  isActivityTaskCancelRequestedEvent,
+  isActivityTaskCompletedEvent,
+  isActivityTaskFailedEvent,
+  isActivityTaskScheduledEvent,
+  isActivityTaskStartedEvent,
+  isActivityTaskTimedOutEvent,
+  isChildWorkflowExecutionCompletedEvent,
+  isChildWorkflowExecutionStartedEvent,
+  isMarkerRecordedEvent,
+  isSignalExternalWorkflowExecutionInitiatedEvent,
+  isStartChildWorkflowExecutionInitiatedEvent,
+  isTimerCanceledEvent,
+  isTimerFiredEvent,
+  isTimerStartedEvent,
+} from '$lib/utilities/is-event-type';
+
+export const getGroupId = (event: CommonHistoryEvent): string => {
+  if (isActivityTaskScheduledEvent(event)) return event.id;
+
+  if (isStartChildWorkflowExecutionInitiatedEvent(event)) return event.id;
+
+  if (isTimerStartedEvent(event)) return event.id;
+
+  if (isSignalExternalWorkflowExecutionInitiatedEvent(event)) return event.id;
+
+  if (isMarkerRecordedEvent(event)) return event.id;
+
+  if (isActivityTaskStartedEvent(event))
+    return String(event.activityTaskStartedEventAttributes.scheduledEventId);
+
+  if (isActivityTaskCanceledEvent(event)) {
+    return String(event.activityTaskCanceledEventAttributes.scheduledEventId);
+  }
+
+  if (isActivityTaskCancelRequestedEvent(event)) {
+    return String(
+      event.activityTaskCancelRequestedEventAttributes.scheduledEventId,
+    );
+  }
+
+  if (isActivityTaskFailedEvent(event)) {
+    return String(event.activityTaskFailedEventAttributes.scheduledEventId);
+  }
+
+  if (isActivityTaskTimedOutEvent(event)) {
+    return String(event.activityTaskTimedOutEventAttributes.scheduledEventId);
+  }
+
+  if (isActivityTaskCompletedEvent(event)) {
+    return String(event.activityTaskCompletedEventAttributes.scheduledEventId);
+  }
+
+  if (isChildWorkflowExecutionStartedEvent(event)) {
+    return String(
+      event.childWorkflowExecutionStartedEventAttributes.initiatedEventId,
+    );
+  }
+
+  if (isChildWorkflowExecutionCompletedEvent(event)) {
+    return String(
+      event.childWorkflowExecutionCompletedEventAttributes.initiatedEventId,
+    );
+  }
+
+  if (isTimerFiredEvent(event)) {
+    return String(event.timerFiredEventAttributes.startedEventId);
+  }
+
+  if (isTimerCanceledEvent(event)) {
+    return String(event.timerCanceledEventAttributes.startedEventId);
+  }
+};
diff --git a/src/lib/models/group-events/get-last-event.ts b/src/lib/models/group-events/get-last-event.ts
new file mode 100644
index 00000000..e86fa16d
--- /dev/null
+++ b/src/lib/models/group-events/get-last-event.ts
@@ -0,0 +1,16 @@
+export const getLastEvent = ({
+  events,
+}: CompactEventGroup): HistoryEventWithId => {
+  let latestEventKey = 0;
+  let result: HistoryEventWithId;
+
+  for (const event of events.values()) {
+    const k = Number(event.id);
+    if (k >= latestEventKey) {
+      latestEventKey = k;
+      result = event;
+    }
+  }
+
+  return result;
+};
diff --git a/src/lib/models/group-events/index.ts b/src/lib/models/group-events/index.ts
new file mode 100644
index 00000000..ccc0b12f
--- /dev/null
+++ b/src/lib/models/group-events/index.ts
@@ -0,0 +1,21 @@
+import { createEventGroup } from './create-event-group';
+import { getGroupId } from './get-group-id';
+
+export const groupEvents = (
+  events: CommonHistoryEvent[],
+): CompactEventGroups => {
+  const groups: Record<string, CompactEventGroup> = {};
+
+  for (const event of events) {
+    const id = getGroupId(event);
+    const group = createEventGroup(event);
+
+    if (group) {
+      groups[group.id] = group;
+    } else {
+      groups[id]?.events.set(event.eventType, event);
+    }
+  }
+
+  return Object.values(groups);
+};
diff --git a/src/lib/utilities/get-event-categorization.ts b/src/lib/utilities/get-event-categorization.ts
index 1921aabb..fec1da70 100644
--- a/src/lib/utilities/get-event-categorization.ts
+++ b/src/lib/utilities/get-event-categorization.ts
@@ -1,4 +1,6 @@
-export const eventTypeCategorizations: Record<EventType, EventTypeCategory> = {
+export const eventTypeCategorizations: Readonly<
+  Record<EventType, EventTypeCategory>
+> = {
   ActivityTaskCanceled: 'activity',
   ActivityTaskCancelRequested: 'activity',
   ActivityTaskCompleted: 'activity',
@@ -46,14 +48,15 @@ export const eventTypeCategorizations: Record<EventType, EventTypeCategory> = {
   UpsertWorkflowSearchAttributes: 'command',
 };
 
-const categories: EventTypeCategory[] = [
+export type EventTypeCategory = typeof categories[number];
+const categories = [
   'activity',
   'child-workflow',
   'signal',
   'timer',
   'workflow',
   'command',
-];
+] as const;
 
 export const eventTypeInCategory =
   (category?: EventTypeCategory) =>
diff --git a/src/lib/utilities/get-event-classification.ts b/src/lib/utilities/get-event-classification.ts
index ce4c62fd..d0573c1d 100644
--- a/src/lib/utilities/get-event-classification.ts
+++ b/src/lib/utilities/get-event-classification.ts
@@ -1,12 +1,12 @@
-import { EventsGroup } from '../models/events-group';
 import { format } from './format-camel-case';
 import { routeFor } from './route-for';
+import { getLastEvent } from '$lib/models/group-events/get-last-event';
 
 import type { WorkflowParameters } from './route-for';
 import type { Timestamp } from '$types';
 
 export type EventClassification = typeof eventClassifications[number];
-type EventOrGroup = HistoryEventWithId | PendingActivity | EventsGroup;
+type EventOrGroup = HistoryEventWithId | PendingActivity | CompactEventGroup;
 
 type EventSummary = {
   id: string;
@@ -60,21 +60,29 @@ export const isPendingActivity = (
   return false;
 };
 
-export const isEventsGroup = (event: EventOrGroup): event is EventsGroup => {
-  return event instanceof EventsGroup;
+export const isEventGroup = (
+  eventOrGroup: EventOrGroup,
+): eventOrGroup is CompactEventGroup => {
+  return has(eventOrGroup, 'events');
+};
+
+export const isEventGroups = (
+  eventsOrGroups: unknown[],
+): eventsOrGroups is CompactEventGroups => {
+  return eventsOrGroups.every(isEventGroup);
 };
 
 export const getEventClassification = (
   event: EventOrGroup,
 ): EventClassification => {
   if (isPendingActivity(event)) return event.state;
-  if (isEventsGroup(event)) {
+  if (isEventGroup(event)) {
     event =
-      event.get('ActivityTaskScheduled') ??
-      event.get('TimerStarted') ??
-      event.get('WorkflowExecutionSignaled') ??
-      event.get('MarkerRecorded') ??
-      event.get('StartChildWorkflowExecutionInitiated');
+      event.events.get('ActivityTaskScheduled') ??
+      event.events.get('TimerStarted') ??
+      event.events.get('WorkflowExecutionSignaled') ??
+      event.events.get('MarkerRecorded') ??
+      event.events.get('StartChildWorkflowExecutionInitiated');
   }
 
   const eventType = event.eventType;
@@ -89,7 +97,7 @@ const getName = (event: EventOrGroup): string => {
   if (isEvent(event)) return String(event.eventType);
   if (isPendingActivity(event))
     return `${event.activityType.name}:${event.state}`;
-  if (isEventsGroup(event)) return event.name;
+  if (isEventGroup(event)) return event.name;
 };
 
 const getTime = (event: EventOrGroup): string => {
@@ -97,7 +105,7 @@ const getTime = (event: EventOrGroup): string => {
 
   if (isEvent(event)) ts = event.eventTime;
   if (isPendingActivity(event)) ts = event.lastStartedTime;
-  if (isEventsGroup(event)) ts = event.last.eventTime;
+  if (isEventGroup(event)) ts = getLastEvent(event).eventTime;
 
   return ts ? String(ts) : null;
 };
@@ -105,7 +113,7 @@ const getTime = (event: EventOrGroup): string => {
 const getId = (event: EventOrGroup): string => {
   if (isEvent(event)) return String(event.eventId);
   if (isPendingActivity(event)) return String(event.activityId);
-  if (isEventsGroup(event)) return String(event.id);
+  if (isEventGroup(event)) return String(event.id);
 };
 
 const getType = (
@@ -113,7 +121,7 @@ const getType = (
 ): 'event' | 'pending-activity' | 'activity' => {
   if (isEvent(event)) return 'event';
   if (isPendingActivity(event)) return 'pending-activity';
-  if (isEventsGroup(event)) return 'activity';
+  if (isEventGroup(event)) return 'activity';
 };
 
 export const getHref = (
@@ -121,10 +129,12 @@ export const getHref = (
   parameters: WorkflowParameters,
 ): string => {
   if (isEvent(event)) {
-    return routeFor('workflow.events.full.event', {
+    const route = routeFor('workflow.events.full.event', {
       ...parameters,
       eventId: String(event.eventId),
     });
+
+    return route;
   }
 
   if (isPendingActivity(event)) {
@@ -134,7 +144,7 @@ export const getHref = (
     });
   }
 
-  if (isEventsGroup(event)) {
+  if (isEventGroup(event)) {
     return routeFor('workflow.events.compact.activity', {
       ...parameters,
       eventId: String(event.id),
@@ -142,9 +152,7 @@ export const getHref = (
   }
 };
 
-export const formatEvent = (
-  event: EventOrGroup | EventsGroup,
-): EventSummary => {
+export const formatEvent = (event: EventOrGroup): EventSummary => {
   return {
     id: getId(event),
     name: format(getName(event)),
@@ -153,7 +161,7 @@ export const formatEvent = (
     tag: getName(event),
     type: getType(event),
     pending: isPendingActivity(event),
-    activity: isEventsGroup(event),
+    activity: isEventGroup(event),
     routeFor: (parameters: WorkflowParameters) => getHref(event, parameters),
   };
 };
diff --git a/src/lib/utilities/get-event-name.ts b/src/lib/utilities/get-event-name.ts
new file mode 100644
index 00000000..c9d82d5a
--- /dev/null
+++ b/src/lib/utilities/get-event-name.ts
@@ -0,0 +1,31 @@
+import {
+  isActivityTaskScheduledEvent,
+  isStartChildWorkflowExecutionInitiatedEvent,
+  isTimerStartedEvent,
+  isSignalExternalWorkflowExecutionInitiatedEvent,
+  isMarkerRecordedEvent,
+} from '$lib/utilities/is-event-type';
+
+export const getName = (event: CommonHistoryEvent): string => {
+  if (!event) return;
+
+  if (isActivityTaskScheduledEvent(event)) {
+    return event.activityTaskScheduledEventAttributes?.activityType?.name;
+  }
+
+  if (isTimerStartedEvent(event)) {
+    return `Timer ${event.timerStartedEventAttributes?.timerId} (${event.timerStartedEventAttributes?.startToFireTimeout})`;
+  }
+
+  if (isSignalExternalWorkflowExecutionInitiatedEvent(event)) {
+    return `Signal: ${event.signalExternalWorkflowExecutionInitiatedEventAttributes?.signalName}`;
+  }
+
+  if (isMarkerRecordedEvent(event)) {
+    return `Marker: ${event.markerRecordedEventAttributes?.markerName}`;
+  }
+
+  if (isStartChildWorkflowExecutionInitiatedEvent(event)) {
+    return `Child Workflow: ${event.startChildWorkflowExecutionInitiatedEventAttributes?.workflowType?.name}`;
+  }
+};
diff --git a/src/lib/utilities/get-started-and-completed-events.ts b/src/lib/utilities/get-started-and-completed-events.ts
index bd836e91..3043e5f2 100644
--- a/src/lib/utilities/get-started-and-completed-events.ts
+++ b/src/lib/utilities/get-started-and-completed-events.ts
@@ -9,7 +9,7 @@ export const getWorkflowStartedAndCompletedEvents = async (
   events = await events;
 
   let input: string;
-  let result;
+  let result: string;
 
   const workflowStartedEvent: HistoryEventWithId = events?.find((event) => {
     return !!event.workflowExecutionStartedEventAttributes;
diff --git a/src/lib/utilities/get-visible-events.ts b/src/lib/utilities/get-visible-events.ts
index 7a0f135f..5604c0c5 100644
--- a/src/lib/utilities/get-visible-events.ts
+++ b/src/lib/utilities/get-visible-events.ts
@@ -1,16 +1,29 @@
 import type { WorkflowExecution } from '$lib/models/workflow-execution';
 
 import { eventTypeInCategory } from './get-event-categorization';
+import { isEventGroups } from './get-event-classification';
 
-export const getVisibleEvents = async (
-  eventsRequest: EventualHistoryEvents | HistoryEventWithId[],
-  workflowRequest: PromiseLike<WorkflowExecution> | WorkflowExecution,
+export function getVisibleEvents(
+  events: CompactEventGroups,
+  workflow: never,
+  category: never,
+): CompactEventGroups;
+
+export function getVisibleEvents(
+  events: HistoryEventWithId[],
+  workflow: WorkflowExecution,
+  category: EventTypeCategory,
+): EventsOrActivities;
+
+export function getVisibleEvents(
+  events: HistoryEventWithId[] | CompactEventGroups,
+  workflow: WorkflowExecution,
   category: EventTypeCategory,
-): Promise<(HistoryEventWithId | PendingActivity)[]> => {
-  const events = await eventsRequest;
-  const workflow = await workflowRequest;
+): IterableEvents {
+  if (isEventGroups(events)) return events;
+
   const pendingActivities = workflow.pendingActivities;
   const visibleEvents = events.filter(eventTypeInCategory(category));
 
   return [...pendingActivities, ...visibleEvents];
-};
+}
diff --git a/src/lib/utilities/is-event-type.ts b/src/lib/utilities/is-event-type.ts
new file mode 100644
index 00000000..21d7d85f
--- /dev/null
+++ b/src/lib/utilities/is-event-type.ts
@@ -0,0 +1,326 @@
+export type ActivityType = typeof activityEvents[number];
+export const activityEvents = [
+  'ActivityTaskCanceled',
+  'ActivityTaskCancelRequested',
+  'ActivityTaskCompleted',
+  'ActivityTaskFailed',
+  'ActivityTaskScheduled',
+  'ActivityTaskStarted',
+  'ActivityTaskTimedOut',
+] as const;
+
+export type TimerType = typeof timerEvents[number];
+export const timerEvents = [
+  'TimerStarted',
+  'TimerCanceled',
+  'TimerFired',
+] as const;
+
+export type SignalType = typeof signalEvents[number];
+export const signalEvents = [
+  'WorkflowExecutionSignaled',
+  'SignalExternalWorkflowExecutionFailed',
+  'SignalExternalWorkflowExecutionInitiated',
+] as const;
+
+export type MarkerType = typeof markerEvents[number];
+export const markerEvents = ['MarkerRecorded'] as const;
+
+const childEvents = [
+  'StartChildWorkflowExecutionInitiated',
+  'ChildWorkflowExecutionStarted',
+  'ChildWorkflowExecutionCompleted',
+  'ChildWorkflowExecutionCanceled',
+  'ChildWorkflowExecutionFailed',
+  'ChildWorkflowExecutionTerminated',
+  'ChildWorkflowExecutionTimedOut',
+  'StartChildWorkflowExecutionFailed',
+] as const;
+
+export type EventType = typeof eventTypes[number];
+export const eventTypes = [
+  ...activityEvents,
+  ...timerEvents,
+  ...signalEvents,
+  ...markerEvents,
+  ...childEvents,
+  'WorkflowExecutionCanceled',
+  'WorkflowExecutionCancelRequested',
+  'WorkflowExecutionCompleted',
+  'WorkflowExecutionContinuedAsNew',
+  'WorkflowExecutionFailed',
+  'WorkflowExecutionStarted',
+  'WorkflowExecutionTerminated',
+  'WorkflowExecutionTimedOut',
+  'WorkflowTaskCompleted',
+  'WorkflowTaskFailed',
+  'WorkflowTaskScheduled',
+  'WorkflowTaskStarted',
+  'WorkflowTaskTimedOut',
+  'ExternalWorkflowExecutionCancelRequested',
+  'ExternalWorkflowExecutionSignaled',
+  'RequestCancelExternalWorkflowExecutionFailed',
+  'RequestCancelExternalWorkflowExecutionInitiated',
+  'UpsertWorkflowSearchAttributes',
+] as const;
+
+export const eventAttributeKeys: Readonly<EventAttributeKey[]> = [
+  'workflowExecutionStartedEventAttributes',
+  'workflowExecutionCompletedEventAttributes',
+  'workflowExecutionFailedEventAttributes',
+  'workflowExecutionTimedOutEventAttributes',
+  'workflowTaskStartedEventAttributes',
+  'workflowTaskScheduledEventAttributes',
+  'workflowTaskCompletedEventAttributes',
+  'workflowTaskTimedOutEventAttributes',
+  'workflowTaskFailedEventAttributes',
+  'activityTaskScheduledEventAttributes',
+  'activityTaskCompletedEventAttributes',
+  'activityTaskStartedEventAttributes',
+  'activityTaskFailedEventAttributes',
+  'activityTaskTimedOutEventAttributes',
+  'timerStartedEventAttributes',
+  'timerFiredEventAttributes',
+  'activityTaskCancelRequestedEventAttributes',
+  'activityTaskCanceledEventAttributes',
+  'timerCanceledEventAttributes',
+  'markerRecordedEventAttributes',
+  'workflowExecutionSignaledEventAttributes',
+  'workflowExecutionTerminatedEventAttributes',
+  'workflowExecutionCancelRequestedEventAttributes',
+  'workflowExecutionCanceledEventAttributes',
+  'requestCancelExternalWorkflowExecutionInitiatedEventAttributes',
+  'requestCancelExternalWorkflowExecutionFailedEventAttributes',
+  'externalWorkflowExecutionCancelRequestedEventAttributes',
+  'workflowExecutionContinuedAsNewEventAttributes',
+  'startChildWorkflowExecutionInitiatedEventAttributes',
+  'startChildWorkflowExecutionFailedEventAttributes',
+  'childWorkflowExecutionStartedEventAttributes',
+  'childWorkflowExecutionCompletedEventAttributes',
+  'childWorkflowExecutionFailedEventAttributes',
+  'childWorkflowExecutionCanceledEventAttributes',
+  'childWorkflowExecutionTimedOutEventAttributes',
+  'childWorkflowExecutionTerminatedEventAttributes',
+  'signalExternalWorkflowExecutionInitiatedEventAttributes',
+  'signalExternalWorkflowExecutionFailedEventAttributes',
+  'externalWorkflowExecutionSignaledEventAttributes',
+  'upsertWorkflowSearchAttributesEventAttributes',
+] as const;
+
+export const findAttributeKey = (event: HistoryEvent): EventAttributeKey => {
+  for (const key of eventAttributeKeys) {
+    if (key in event) return key;
+  }
+};
+
+export const findAttributes = (
+  event: HistoryEvent,
+  key: EventAttributeKey,
+): EventAttribute => {
+  return event[key];
+};
+
+export const findAttributesAndKey = (
+  event: HistoryEvent,
+): { key: EventAttributeKey; attributes: EventAttribute } => {
+  const key = findAttributeKey(event);
+  const attributes = findAttributes(event, key);
+
+  return { key, attributes };
+};
+
+const hasAttributes =
+  <T extends EventWithAttributes<EventAttributeKey>>(key: EventAttributeKey) =>
+  (event: CommonHistoryEvent): event is T => {
+    return Boolean(event[key]);
+  };
+
+export const isWorkflowExecutionStartedEvent =
+  hasAttributes<WorkflowExecutionStartedEvent>(
+    'workflowExecutionStartedEventAttributes',
+  );
+
+export const isWorkflowExecutionCompletedEvent =
+  hasAttributes<WorkflowExecutionCompletedEvent>(
+    'workflowExecutionCompletedEventAttributes',
+  );
+
+export const isWorkflowExecutionFailedEvent =
+  hasAttributes<WorkflowExecutionFailedEvent>(
+    'workflowExecutionFailedEventAttributes',
+  );
+
+export const isWorkflowExecutionTimedOutEvent =
+  hasAttributes<WorkflowExecutionTimedOutEvent>(
+    'workflowExecutionTimedOutEventAttributes',
+  );
+
+export const isWorkflowTaskScheduledEvent =
+  hasAttributes<WorkflowTaskScheduledEvent>(
+    'workflowTaskScheduledEventAttributes',
+  );
+
+export const isWorkflowTaskStartedEvent =
+  hasAttributes<WorkflowTaskStartedEvent>('workflowTaskStartedEventAttributes');
+
+export const isWorkflowTaskCompletedEvent =
+  hasAttributes<WorkflowTaskCompletedEvent>(
+    'workflowTaskCompletedEventAttributes',
+  );
+
+export const isWorkflowTaskTimedOutEvent =
+  hasAttributes<WorkflowTaskTimedOutEvent>(
+    'workflowTaskTimedOutEventAttributes',
+  );
+
+export const isWorkflowTaskFailedEvent = hasAttributes<WorkflowTaskFailedEvent>(
+  'workflowTaskFailedEventAttributes',
+);
+
+export const isActivityTaskScheduledEvent =
+  hasAttributes<ActivityTaskScheduledEvent>(
+    'activityTaskScheduledEventAttributes',
+  );
+
+export const isActivityTaskStartedEvent =
+  hasAttributes<ActivityTaskStartedEvent>('activityTaskStartedEventAttributes');
+
+export const isActivityTaskCompletedEvent =
+  hasAttributes<ActivityTaskCompletedEvent>(
+    'activityTaskCompletedEventAttributes',
+  );
+
+export const isActivityTaskFailedEvent = hasAttributes<ActivityTaskFailedEvent>(
+  'activityTaskFailedEventAttributes',
+);
+
+export const isActivityTaskTimedOutEvent =
+  hasAttributes<ActivityTaskTimedOutEvent>(
+    'activityTaskTimedOutEventAttributes',
+  );
+
+export const isTimerStartedEvent = hasAttributes<TimerStartedEvent>(
+  'timerStartedEventAttributes',
+);
+
+export const isTimerFiredEvent = hasAttributes<TimerFiredEvent>(
+  'timerFiredEventAttributes',
+);
+
+export const isActivityTaskCancelRequestedEvent =
+  hasAttributes<ActivityTaskCancelRequestedEvent>(
+    'activityTaskCancelRequestedEventAttributes',
+  );
+
+export const isActivityTaskCanceledEvent =
+  hasAttributes<ActivityTaskCanceledEvent>(
+    'activityTaskCanceledEventAttributes',
+  );
+
+export const isTimerCanceledEvent = hasAttributes<TimerCanceledEvent>(
+  'timerCanceledEventAttributes',
+);
+
+export const isMarkerRecordedEvent = hasAttributes<MarkerRecordedEvent>(
+  'markerRecordedEventAttributes',
+);
+
+export const isWorkflowExecutionSignaledEvent =
+  hasAttributes<WorkflowExecutionSignaledEvent>(
+    'workflowExecutionSignaledEventAttributes',
+  );
+
+export const isWorkflowExecutionTerminatedEvent =
+  hasAttributes<WorkflowExecutionTerminatedEvent>(
+    'workflowExecutionTerminatedEventAttributes',
+  );
+
+export const isWorkflowExecutionCancelRequestedEvent =
+  hasAttributes<WorkflowExecutionCancelRequestedEvent>(
+    'workflowExecutionCancelRequestedEventAttributes',
+  );
+
+export const isWorkflowExecutionCanceledEvent =
+  hasAttributes<WorkflowExecutionCanceledEvent>(
+    'workflowExecutionCanceledEventAttributes',
+  );
+
+export const isRequestCancelExternalWorkflowExecutionInitiatedEvent =
+  hasAttributes<RequestCancelExternalWorkflowExecutionInitiatedEvent>(
+    'requestCancelExternalWorkflowExecutionInitiatedEventAttributes',
+  );
+
+export const isRequestCancelExternalWorkflowExecutionFailedEvent =
+  hasAttributes<RequestCancelExternalWorkflowExecutionFailedEvent>(
+    'requestCancelExternalWorkflowExecutionFailedEventAttributes',
+  );
+
+export const isExternalWorkflowExecutionCancelRequestedEvent =
+  hasAttributes<ExternalWorkflowExecutionCancelRequestedEvent>(
+    'externalWorkflowExecutionCancelRequestedEventAttributes',
+  );
+
+export const isWorkflowExecutionContinuedAsNewEvent =
+  hasAttributes<WorkflowExecutionContinuedAsNewEvent>(
+    'workflowExecutionContinuedAsNewEventAttributes',
+  );
+
+export const isStartChildWorkflowExecutionInitiatedEvent =
+  hasAttributes<StartChildWorkflowExecutionInitiatedEvent>(
+    'startChildWorkflowExecutionInitiatedEventAttributes',
+  );
+
+export const isStartChildWorkflowExecutionFailedEvent =
+  hasAttributes<StartChildWorkflowExecutionFailedEvent>(
+    'startChildWorkflowExecutionFailedEventAttributes',
+  );
+
+export const isChildWorkflowExecutionStartedEvent =
+  hasAttributes<ChildWorkflowExecutionStartedEvent>(
+    'childWorkflowExecutionStartedEventAttributes',
+  );
+
+export const isChildWorkflowExecutionCompletedEvent =
+  hasAttributes<ChildWorkflowExecutionCompletedEvent>(
+    'childWorkflowExecutionCompletedEventAttributes',
+  );
+
+export const isChildWorkflowExecutionFailedEvent =
+  hasAttributes<ChildWorkflowExecutionFailedEvent>(
+    'childWorkflowExecutionFailedEventAttributes',
+  );
+
+export const isChildWorkflowExecutionCanceledEvent =
+  hasAttributes<ChildWorkflowExecutionCanceledEvent>(
+    'childWorkflowExecutionCanceledEventAttributes',
+  );
+
+export const isChildWorkflowExecutionTimedOutEvent =
+  hasAttributes<ChildWorkflowExecutionTimedOutEvent>(
+    'childWorkflowExecutionTimedOutEventAttributes',
+  );
+
+export const isChildWorkflowExecutionTerminatedEvent =
+  hasAttributes<ChildWorkflowExecutionTerminatedEvent>(
+    'childWorkflowExecutionTerminatedEventAttributes',
+  );
+
+export const isSignalExternalWorkflowExecutionInitiatedEvent =
+  hasAttributes<SignalExternalWorkflowExecutionInitiatedEvent>(
+    'signalExternalWorkflowExecutionInitiatedEventAttributes',
+  );
+
+export const isSignalExternalWorkflowExecutionFailedEvent =
+  hasAttributes<SignalExternalWorkflowExecutionFailedEvent>(
+    'signalExternalWorkflowExecutionFailedEventAttributes',
+  );
+
+export const isExternalWorkflowExecutionSignaledEvent =
+  hasAttributes<ExternalWorkflowExecutionSignaledEvent>(
+    'externalWorkflowExecutionSignaledEventAttributes',
+  );
+
+export const isUpsertWorkflowSearchAttributesEvent =
+  hasAttributes<UpsertWorkflowSearchAttributesEvent>(
+    'upsertWorkflowSearchAttributesEventAttributes',
+  );
diff --git a/src/lib/utilities/route-for.ts b/src/lib/utilities/route-for.ts
index fe8bce9b..6217d6eb 100644
--- a/src/lib/utilities/route-for.ts
+++ b/src/lib/utilities/route-for.ts
@@ -17,7 +17,8 @@ type RoutePath =
 type EventView = 'event' | 'pending' | 'activity';
 type EventHistoryView = 'full' | 'compact' | 'json';
 
-export type NamespaceParameter = { namespace: string };
+export type QueryParameters = { query?: URLSearchParams };
+export type NamespaceParameter = { namespace: string } & QueryParameters;
 export type WorkflowParameters = {
   workflowId: string;
   runId: string;
@@ -123,32 +124,34 @@ export function routeFor(
   parameters: TaskQueueParameter,
 ): string;
 export function routeFor(path: RoutePath, parameters: RouteParameters): string {
+  let route: string;
+
   if (path === 'workflows') {
-    return routeForWorkflows(parameters);
+    route = routeForWorkflows(parameters);
   }
 
   if (path === 'workflow') {
-    return routeForWorkflow(parameters);
+    route = routeForWorkflow(parameters);
   }
 
   if (path === 'workflow.events') {
-    return routeForEventHistory(parameters);
+    route = routeForEventHistory(parameters);
   }
 
   if (path === 'workflow.events.full') {
-    return routeForEventHistory(parameters, 'full');
+    route = routeForEventHistory(parameters, 'full');
   }
 
   if (path === 'workflow.events.compact') {
-    return routeForEventHistory(parameters, 'compact');
+    route = routeForEventHistory(parameters, 'compact');
   }
 
   if (path === 'workflow.events.json') {
-    return routeForEventHistory(parameters, 'json');
+    route = routeForEventHistory(parameters, 'json');
   }
 
   if (path === 'workflow.events.full.event') {
-    return routeForEventHistoryItem(
+    route = routeForEventHistoryItem(
       parameters,
       'full',
       'event',
@@ -157,7 +160,7 @@ export function routeFor(path: RoutePath, parameters: RouteParameters): string {
   }
 
   if (path === 'workflow.events.full.pending') {
-    return routeForEventHistoryItem(
+    route = routeForEventHistoryItem(
       parameters,
       'full',
       'pending',
@@ -166,7 +169,7 @@ export function routeFor(path: RoutePath, parameters: RouteParameters): string {
   }
 
   if (path === 'workflow.events.compact.activity') {
-    return routeForEventHistoryItem(
+    route = routeForEventHistoryItem(
       parameters,
       'compact',
       'activity',
@@ -175,7 +178,7 @@ export function routeFor(path: RoutePath, parameters: RouteParameters): string {
   }
 
   if (path === 'workflow.events.compact.activity.event') {
-    return routeForActivity(
+    route = routeForActivity(
       parameters,
       'compact',
       'activity',
@@ -185,14 +188,20 @@ export function routeFor(path: RoutePath, parameters: RouteParameters): string {
   }
 
   if (path === 'workflow.query') {
-    return routeForWorkflow(parameters) + '/query';
+    route = routeForWorkflow(parameters) + '/query';
   }
 
   if (path === 'workflow.stack-trace') {
-    return routeForWorkflow(parameters) + '/stack-trace';
+    route = routeForWorkflow(parameters) + '/stack-trace';
   }
 
   if (path === 'workers') {
-    return routeForWorkers(parameters);
+    route = routeForWorkers(parameters);
+  }
+
+  if (parameters.query) {
+    return `${route}?${parameters.query}`;
+  } else {
+    return route;
   }
 }
diff --git a/src/lib/utilities/update-query-parameters.ts b/src/lib/utilities/update-query-parameters.ts
index e2f21242..58bb2b0d 100644
--- a/src/lib/utilities/update-query-parameters.ts
+++ b/src/lib/utilities/update-query-parameters.ts
@@ -10,7 +10,7 @@ type UpdateQueryParams = {
   invalidate?: typeof invalidate;
 };
 
-const options = { replaceState: true, keepfocus: true };
+const options = { replaceState: true, keepfocus: true, noscroll: true };
 
 export const updateQueryParameters = async ({
   parameter,
diff --git a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/history/__layout.svelte b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/history/__layout.svelte
index 0c419561..69ee068f 100644
--- a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/history/__layout.svelte
+++ b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/history/__layout.svelte
@@ -14,7 +14,6 @@
       props: {
         workflow: { workflowId, runId, namespace },
         events,
-        parameters,
       },
       stuff: {
         events,
@@ -38,7 +37,6 @@
   import InputAndResults from '$lib/components/input-and-result.svelte';
 
   export let workflow: WorkflowParameters;
-  export let parameters: Parameters<typeof fetchEvents>[0];
   export let events: HistoryEventWithId[];
 </script>
 
@@ -61,11 +59,7 @@
           href={routeFor('workflow.events.json', workflow)}
         />
       </ToggleButtons>
-      <ExportHistory
-        namespace={parameters.namespace}
-        workflowId={parameters.executionId}
-        runId={parameters.runId}
-      />
+      <ExportHistory />
     </div>
   </nav>
   <slot />
diff --git a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/history/compact/__layout.svelte b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/history/compact/__layout.svelte
index cd4c1dce..70ccbe5d 100644
--- a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/history/compact/__layout.svelte
+++ b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/history/compact/__layout.svelte
@@ -2,26 +2,24 @@
   import type { LoadInput } from '@sveltejs/kit';
 
   export const load = async ({ stuff }: LoadInput) => {
-    const { events } = stuff;
-
-    console.log({ events });
+    const { events } = stuff as { events: HistoryEventWithId[] };
+    const eventGroups = groupEvents(events);
 
     return {
-      props: { events },
+      props: { eventGroups },
+      stuff: { eventGroups },
     };
   };
 </script>
 
 <script lang="ts">
-  import { EventGroups } from '$lib/models/events-group';
+  import { groupEvents } from '$lib/models/group-events';
   import EventTable from '$lib/components/event-table.svelte';
 
-  export let events: HistoryEventWithId[];
-
-  const activities = EventGroups.from(events);
+  export let eventGroups: CompactEventGroups;
 </script>
 
-<EventTable events={activities}>
+<EventTable events={eventGroups}>
   <div slot="details" class="w-full h-full py-4">
     <slot />
   </div>
diff --git a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/history/compact/activity-[eventId]/__layout.svelte b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/history/compact/activity-[eventId]/__layout.svelte
index dbeab177..8722d776 100644
--- a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/history/compact/activity-[eventId]/__layout.svelte
+++ b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/history/compact/activity-[eventId]/__layout.svelte
@@ -10,10 +10,16 @@
       eventId,
     } = page.params;
 
-    const { events } = stuff;
+    const { eventGroups } = stuff;
+    const group = eventGroups.find(({ id }) => id === eventId);
+
+    if (!group) return { status: 404 };
+
+    const events = [...group.events.values()];
 
     return {
       props: {
+        group,
         events,
         params: {
           workflowId,
@@ -27,27 +33,17 @@
 </script>
 
 <script lang="ts">
-  import { EventGroups, EventsGroup } from '$lib/models/events-group';
   import { routeFor } from '$lib/utilities/route-for';
   import { page } from '$app/stores';
 
+  export let group: CompactEventGroup;
   export let events: HistoryEventWithId[];
   export let params: EventParameter;
 
-  const getEventsGroup = (
-    events: HistoryEventWithId[],
-    id: string,
-  ): { group: EventsGroup; events: HistoryEventWithId[] } => {
-    const groups = EventGroups.from(events);
-    const group = groups.get(id);
-
-    return {
-      group,
-      events: group.events,
-    };
-  };
-
-  const getHref = (group: EventsGroup, event: HistoryEventWithId): string =>
+  const getHref = (
+    group: CompactEventGroup,
+    event: HistoryEventWithId,
+  ): string =>
     routeFor('workflow.events.compact.activity.event', {
       ...params,
       activityId: group.id,
@@ -58,18 +54,16 @@
 <div class="flex flex-col w-full h-full">
   <nav class="mb-4">
     <ul class="flex gap-4 w-full items-start">
-      {#await getEventsGroup(events, params.eventId) then { group, events }}
-        {#each events as event}
-          <li>
-            <a
-              href={getHref(group, event)}
-              class:active={$page.path === getHref(group, event)}
-            >
-              {event.eventType}
-            </a>
-          </li>
-        {/each}
-      {/await}
+      {#each events as event}
+        <li>
+          <a
+            href={getHref(group, event)}
+            class:active={$page.path === getHref(group, event)}
+          >
+            {event.eventType}
+          </a>
+        </li>
+      {/each}
     </ul>
   </nav>
   <slot />
diff --git a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/history/full/__layout.svelte b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/history/full/__layout.svelte
index c1969a45..6b586bab 100644
--- a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/history/full/__layout.svelte
+++ b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/history/full/__layout.svelte
@@ -2,11 +2,12 @@
   import type { LoadInput } from '@sveltejs/kit';
   import type { WorkflowExecution } from '$lib/models/workflow-execution';
 
-  export const load = async ({ stuff }: LoadInput) => {
+  export const load = async ({ stuff, page }: LoadInput) => {
     const { workflow, events } = stuff;
+    const category = page.query.get('category');
 
     return {
-      props: { workflow, events },
+      props: { workflow, events, category },
     };
   };
 </script>
@@ -18,29 +19,26 @@
   import Option from '$lib/components/select/option.svelte';
   import EventTable from '$lib/components/event-table.svelte';
 
-  let category: EventTypeCategory = null;
-
   export let workflow: WorkflowExecution;
   export let events: HistoryEventWithId[];
+  export let category: EventTypeCategory = null;
 
-  const eventsAndActivities = getVisibleEvents(events, workflow, category);
+  $: visibleEvents = getVisibleEvents(events, workflow, category);
 </script>
 
-{#await eventsAndActivities then events}
-  <EventTable {events}>
-    <div slot="filters">
-      <FilterSelect parameter="event-type" bind:value={category}>
-        <Option value={null}>All</Option>
-        <Option value="activity">Activity</Option>
-        <Option value="command">Command</Option>
-        <Option value="signal">Signal</Option>
-        <Option value="timer">Timer</Option>
-        <Option value="child-workflow">Child Workflow</Option>
-        <Option value="workflow">Workflow</Option>
-      </FilterSelect>
-    </div>
-    <div slot="details" class="w-full h-full py-4">
-      <slot />
-    </div>
-  </EventTable>
-{/await}
+<EventTable events={visibleEvents}>
+  <div slot="filters">
+    <FilterSelect parameter="category" bind:value={category}>
+      <Option value={null}>All</Option>
+      <Option value="activity">Activity</Option>
+      <Option value="command">Command</Option>
+      <Option value="signal">Signal</Option>
+      <Option value="timer">Timer</Option>
+      <Option value="child-workflow">Child Workflow</Option>
+      <Option value="workflow">Workflow</Option>
+    </FilterSelect>
+  </div>
+  <div slot="details" class="w-full h-full py-4">
+    <slot />
+  </div>
+</EventTable>
diff --git a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/workers.svelte b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/workers.svelte
index 9bb93bd4..48f8a395 100644
--- a/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/workers.svelte
+++ b/src/routes/namespaces/[namespace]/workflows/[workflow]/[run]/workers.svelte
@@ -13,7 +13,7 @@
     const workers = await getPollers({ queue: taskQueue, namespace });
 
     return {
-      props: { workflow, workers, taskQueue, namespace },
+      props: { workers, taskQueue },
     };
   }
 </script>
@@ -23,8 +23,6 @@
   import WorkersRow from '$lib/components/workers-row.svelte';
   import EmptyState from '$lib/components/empty-state.svelte';
 
-  export let namespace: string;
-  export let workflow: WorkflowExecution;
   export let workers: GetPollersResponse;
   export let taskQueue: string;
 </script>
diff --git a/src/routes/namespaces/[namespace]/workflows/index.svelte b/src/routes/namespaces/[namespace]/workflows/index.svelte
index 4da1d4ed..88c52ea0 100644
--- a/src/routes/namespaces/[namespace]/workflows/index.svelte
+++ b/src/routes/namespaces/[namespace]/workflows/index.svelte
@@ -28,7 +28,7 @@
     const workflows = await fetchAllWorkflows(namespace, parameters, fetch);
 
     return {
-      props: { workflows, namespace, parameters, isAdvancedSearch },
+      props: { workflows, isAdvancedSearch },
     };
   }
 </script>
@@ -41,8 +41,8 @@
   import WorkflowFilters from './_workflow-filters.svelte';
   import EmptyState from '$lib/components/empty-state.svelte';
   import WorkflowsLoadingState from './_workflows-loading.svelte';
+  import Badge from '$lib/components/badge.svelte';
 
-  export let namespace: string;
   export let workflows: CombinedWorkflowExecutionsResponse;
   export let isAdvancedSearch: boolean;
 
@@ -53,7 +53,7 @@
     : 'If you have filters applied, try adjusting them.';
 </script>
 
-<h2 class="text-2xl">Workflows</h2>
+<h2 class="text-2xl">Workflows <Badge type="beta">Beta</Badge></h2>
 <WorkflowFilters bind:timeFormat />
 {#await workflows}
   <WorkflowsLoadingState />


Test patch:
diff --git a/src/lib/models/events-group.test.ts b/src/lib/models/events-group.test.ts
deleted file mode 100644
index 939c50e2..00000000
--- a/src/lib/models/events-group.test.ts
+++ /dev/null
@@ -1,218 +0,0 @@
-import { EventsGroup, EventGroups } from './events-group';
-
-const scheduledEvent = {
-  id: '5',
-  eventId: '5',
-  eventTime: '2021-12-27T15:23:21.806965200Z',
-  eventType: 'ActivityTaskScheduled',
-  version: '0',
-  taskId: '1049493',
-  activityTaskScheduledEventAttributes: {
-    activityId: '5',
-    activityType: {
-      name: 'CompletedActivity',
-    },
-    namespace: '',
-    taskQueue: {
-      name: 'rainbow-statuses',
-      kind: 'Normal',
-    },
-    input: null,
-  },
-};
-
-const anotherScheduledEvent = {
-  id: '99',
-  eventId: '99',
-  eventTime: '2021-12-27T15:23:21.806965200Z',
-  eventType: 'ActivityTaskScheduled',
-  version: '0',
-  taskId: '1049494',
-  activityTaskScheduledEventAttributes: {
-    activityId: '5',
-    activityType: {
-      name: 'FicticiousEvent',
-    },
-    namespace: '',
-    taskQueue: {
-      name: 'test-events',
-      kind: 'Normal',
-    },
-    input: null,
-  },
-};
-
-const startedEvent = {
-  id: '6',
-  eventId: '6',
-  eventTime: '2021-12-27T15:23:21.824600700Z',
-  eventType: 'ActivityTaskStarted',
-  version: '0',
-  taskId: '1049497',
-  activityTaskStartedEventAttributes: {
-    scheduledEventId: '5',
-    identity: '21665@temporal@',
-    requestId: '0202572f-485c-426a-a3ee-02ea46d3cad7',
-    attempt: 1,
-    lastFailure: null,
-  },
-};
-
-const completedEvent = {
-  id: '7',
-  eventId: '7',
-  eventTime: '2021-12-27T15:23:21.841178500Z',
-  eventType: 'ActivityTaskCompleted',
-  version: '0',
-  taskId: '1049498',
-  activityTaskCompletedEventAttributes: {
-    result: null,
-    scheduledEventId: '5',
-    startedEventId: '6',
-    identity: '21665@temporal@',
-  },
-};
-
-const eventHistory = [
-  scheduledEvent,
-  startedEvent,
-  anotherScheduledEvent,
-  completedEvent,
-];
-
-describe(EventsGroup, () => {
-  it('should grab the name of the groupTaskScheduledEvent', () => {
-    const group = new EventsGroup(scheduledEvent);
-
-    expect(group.name).toBe('CompletedActivity');
-  });
-
-  it('should grab the id of the groupTaskScheduledEvent', () => {
-    const group = new EventsGroup(scheduledEvent);
-
-    expect(group.id).toBe('5');
-  });
-
-  it('should store the groupTaskScheduled', () => {
-    const group = new EventsGroup(scheduledEvent);
-
-    expect(group.get('ActivityTaskScheduled')).toBe(scheduledEvent);
-  });
-
-  it('should be able to add a started event', () => {
-    const group = new EventsGroup(scheduledEvent);
-    group.set(completedEvent.eventType, completedEvent);
-
-    expect(group.length).toBe(2);
-    expect(group.get('ActivityTaskCompleted')).toBe(completedEvent);
-  });
-});
-
-describe(EventsGroup, () => {
-  it('should create a new entry when given an groupTaskScheduled event', () => {
-    const groups = new EventGroups();
-
-    groups.add(scheduledEvent);
-
-    const group = groups.get(scheduledEvent.id);
-
-    expect(group.get('ActivityTaskScheduled')).toBe(scheduledEvent);
-  });
-
-  it('should correctly set the name of the events group', () => {
-    const groups = new EventGroups();
-
-    groups.add(scheduledEvent);
-
-    const group = groups.get(scheduledEvent.id);
-
-    expect(group.name).toBe('CompletedActivity');
-  });
-
-  it('should correctly set the id of the group', () => {
-    const groups = new EventGroups();
-
-    groups.add(scheduledEvent);
-
-    const group = groups.get(scheduledEvent.id);
-
-    expect(group.id).toBe('5');
-  });
-
-  it('should correctly set the name of the group', () => {
-    const groups = new EventGroups();
-
-    groups.add(scheduledEvent);
-
-    const group = groups.get(scheduledEvent.id);
-
-    expect(group.id).toBe('5');
-  });
-
-  it('should be able to store multiple event groups', () => {
-    const groups = new EventGroups();
-
-    groups.add(scheduledEvent);
-    groups.add(anotherScheduledEvent);
-
-    expect(groups.length).toBe(2);
-  });
-
-  it('should add a completed event to the correct group', () => {
-    const groups = new EventGroups();
-
-    groups.add(scheduledEvent);
-    groups.add(completedEvent);
-
-    const group = groups.get(scheduledEvent.id);
-
-    expect(group.length).toBe(2);
-    expect(group.get('ActivityTaskCompleted')).toBe(completedEvent);
-  });
-
-  it('should be able to add multiple event groups and their associated events', () => {
-    const groups = new EventGroups();
-
-    groups.add(scheduledEvent);
-    groups.add(startedEvent);
-    groups.add(completedEvent);
-    groups.add(anotherScheduledEvent);
-
-    const [first, second] = groups;
-
-    expect(groups.length).toBe(2);
-    expect(first.length).toBe(3);
-    expect(second.length).toBe(1);
-  });
-
-  describe('from event history', () => {
-    it('should create a collection with the correct number of event groups', () => {
-      const groups = EventGroups.from(eventHistory);
-
-      const [first, second] = groups;
-
-      expect(groups.length).toBe(2);
-      expect(first.length).toBe(3);
-      expect(second.length).toBe(1);
-    });
-
-    it('should create a collection with the correct number of event groups using the constructor', () => {
-      const groups = new EventGroups(eventHistory);
-
-      const [first, second] = groups;
-
-      expect(groups.length).toBe(2);
-      expect(first.length).toBe(3);
-      expect(second.length).toBe(1);
-    });
-
-    it('should instantiate correctly when given a single event', () => {
-      const groups = new EventGroups(scheduledEvent);
-
-      const [group] = groups;
-
-      expect(groups.length).toBe(1);
-      expect(group.length).toBe(1);
-    });
-  });
-});
diff --git a/src/lib/models/group-events/create-event-group.test.ts b/src/lib/models/group-events/create-event-group.test.ts
new file mode 100644
index 00000000..1aea4756
--- /dev/null
+++ b/src/lib/models/group-events/create-event-group.test.ts
@@ -0,0 +1,109 @@
+import { createEventGroup } from './create-event-group';
+
+const scheduledEvent = {
+  id: '5',
+  eventId: '5',
+  eventTime: '2021-12-27T15:23:21.806965200Z',
+  eventType: 'ActivityTaskScheduled',
+  version: '0',
+  taskId: '1049493',
+  activityTaskScheduledEventAttributes: {
+    activityId: '5',
+    activityType: {
+      name: 'CompletedActivity',
+    },
+    namespace: '',
+    taskQueue: {
+      name: 'rainbow-statuses',
+      kind: 'Normal',
+    },
+    input: null,
+  },
+};
+
+const anotherScheduledEvent = {
+  id: '99',
+  eventId: '99',
+  eventTime: '2021-12-27T15:23:21.806965200Z',
+  eventType: 'ActivityTaskScheduled',
+  version: '0',
+  taskId: '1049494',
+  activityTaskScheduledEventAttributes: {
+    activityId: '5',
+    activityType: {
+      name: 'FicticiousEvent',
+    },
+    namespace: '',
+    taskQueue: {
+      name: 'test-events',
+      kind: 'Normal',
+    },
+    input: null,
+  },
+};
+
+const startedEvent = {
+  id: '6',
+  eventId: '6',
+  eventTime: '2021-12-27T15:23:21.824600700Z',
+  eventType: 'ActivityTaskStarted',
+  version: '0',
+  taskId: '1049497',
+  activityTaskStartedEventAttributes: {
+    scheduledEventId: '5',
+    identity: '21665@temporal@',
+    requestId: '0202572f-485c-426a-a3ee-02ea46d3cad7',
+    attempt: 1,
+    lastFailure: null,
+  },
+};
+
+const completedEvent = {
+  id: '7',
+  eventId: '7',
+  eventTime: '2021-12-27T15:23:21.841178500Z',
+  eventType: 'ActivityTaskCompleted',
+  version: '0',
+  taskId: '1049498',
+  activityTaskCompletedEventAttributes: {
+    result: null,
+    scheduledEventId: '5',
+    startedEventId: '6',
+    identity: '21665@temporal@',
+  },
+};
+
+const eventHistory = [
+  scheduledEvent,
+  startedEvent,
+  anotherScheduledEvent,
+  completedEvent,
+];
+
+describe(createEventGroup, () => {
+  it('should grab the name of the groupTaskScheduledEvent', () => {
+    const group = createEventGroup(scheduledEvent);
+
+    expect(group.name).toBe('CompletedActivity');
+  });
+
+  it('should grab the id of the groupTaskScheduledEvent', () => {
+    const group = createEventGroup(scheduledEvent);
+
+    expect(group.id).toBe('5');
+  });
+
+  it('should store the groupTaskScheduled', () => {
+    const group = createEventGroup(scheduledEvent);
+
+    expect(group.events.get('ActivityTaskScheduled')).toBe(scheduledEvent);
+  });
+
+  it('should be able to add a started event', () => {
+    const group = createEventGroup(scheduledEvent);
+    group.events.set(completedEvent.eventType, completedEvent);
+
+    expect(group.events.size).toBe(2);
+    expect(group.events.get('ActivityTaskCompleted')).toBe(completedEvent);
+  });
+});
diff --git a/src/lib/models/group-events/group-events.test.ts b/src/lib/models/group-events/group-events.test.ts
new file mode 100644
index 00000000..5ca44eb9
--- /dev/null
+++ b/src/lib/models/group-events/group-events.test.ts
@@ -0,0 +1,115 @@
+import { groupEvents } from './';
+
+const scheduledEvent = {
+  id: '5',
+  eventId: '5',
+  eventTime: '2021-12-27T15:23:21.806965200Z',
+  eventType: 'ActivityTaskScheduled',
+  version: '0',
+  taskId: '1049493',
+  activityTaskScheduledEventAttributes: {
+    activityId: '5',
+    activityType: {
+      name: 'CompletedActivity',
+    },
+    namespace: '',
+    taskQueue: {
+      name: 'rainbow-statuses',
+      kind: 'Normal',
+    },
+    input: null,
+  },
+};
+
+const anotherScheduledEvent = {
+  id: '99',
+  eventId: '99',
+  eventTime: '2021-12-27T15:23:21.806965200Z',
+  eventType: 'ActivityTaskScheduled',
+  version: '0',
+  taskId: '1049494',
+  activityTaskScheduledEventAttributes: {
+    activityId: '5',
+    activityType: {
+      name: 'FicticiousEvent',
+    },
+    namespace: '',
+    taskQueue: {
+      name: 'test-events',
+      kind: 'Normal',
+    },
+    input: null,
+  },
+};
+
+const startedEvent = {
+  id: '6',
+  eventId: '6',
+  eventTime: '2021-12-27T15:23:21.824600700Z',
+  eventType: 'ActivityTaskStarted',
+  version: '0',
+  taskId: '1049497',
+  activityTaskStartedEventAttributes: {
+    scheduledEventId: '5',
+    identity: '21665@temporal@',
+    requestId: '0202572f-485c-426a-a3ee-02ea46d3cad7',
+    attempt: 1,
+    lastFailure: null,
+  },
+};
+
+const completedEvent = {
+  id: '7',
+  eventId: '7',
+  eventTime: '2021-12-27T15:23:21.841178500Z',
+  eventType: 'ActivityTaskCompleted',
+  version: '0',
+  taskId: '1049498',
+  activityTaskCompletedEventAttributes: {
+    result: null,
+    scheduledEventId: '5',
+    startedEventId: '6',
+    identity: '21665@temporal@',
+  },
+};
+
+const eventHistory = [
+  scheduledEvent,
+  startedEvent,
+  anotherScheduledEvent,
+  completedEvent,
+];
+
+describe(groupEvents, () => {
+  it('should create a new entry when given a scheduled event', () => {
+    const groups = groupEvents([scheduledEvent]);
+    const group = groups.find(({ id }) => id === scheduledEvent.id);
+
+    expect(group.events.get('ActivityTaskScheduled')).toBe(scheduledEvent);
+  });
+
+  it('should be able to store multiple event groups', () => {
+    const groups = groupEvents([scheduledEvent, anotherScheduledEvent]);
+
+    expect(Object.keys(groups).length).toBe(2);
+  });
+
+  it('should add a completed event to the correct group', () => {
+    const groups = groupEvents([scheduledEvent, completedEvent]);
+
+    const group = groups.find(({ id }) => id === scheduledEvent.id);
+
+    expect(group.events.size).toBe(2);
+    expect(group.events.get('ActivityTaskCompleted')).toBe(completedEvent);
+  });
+
+  it('should be able to add multiple event groups and their associated events', () => {
+    const groups = groupEvents(eventHistory);
+
+    const [first, second] = Object.values(groups);
+
+    expect(Object.values(groups).length).toBe(2);
+    expect(first.events.size).toBe(3);
+    expect(second.events.size).toBe(1);
+  });
+});
diff --git a/src/lib/utilities/update-query-parameters.test.ts b/src/lib/utilities/update-query-parameters.test.ts
index 085efafb..e2e974af 100644
--- a/src/lib/utilities/update-query-parameters.test.ts
+++ b/src/lib/utilities/update-query-parameters.test.ts
@@ -1,6 +1,6 @@
 import { updateQueryParameters } from './update-query-parameters';
 
-const gotoOptions = { replaceState: true, keepfocus: true };
+const gotoOptions = { replaceState: true, keepfocus: true, noscroll: true };
 
 describe(updateQueryParameters, () => {
   it('should call the set method on the query when a value is provided', () => {