feat: Workflow History V2 - implement deep-linking to history events #1120
Conversation
Signed-off-by: Adhitya Mamallan <adhitya.mamallan@uber.com>
Signed-off-by: Adhitya Mamallan <adhitya.mamallan@uber.com>
Signed-off-by: Adhitya Mamallan <adhitya.mamallan@uber.com>
Signed-off-by: Adhitya Mamallan <adhitya.mamallan@uber.com>
Signed-off-by: Adhitya Mamallan <adhitya.mamallan@uber.com>
There was a problem hiding this comment.
Pull request overview
This PR implements deep linking functionality for History v2, allowing users to share and navigate directly to specific events in workflow history. The implementation strips the summary_ prefix from event IDs in query parameters and passes the cleaned ID to both grouped and ungrouped history views, enabling proper event highlighting and navigation.
Key Changes
- Added logic to strip
summary_prefix fromhistorySelectedEventIdquery parameter - Updated event group components to accept
selectedEventIdinstead of booleanselectedprop for better precision - Implemented
WorkflowHistoryEventLinkButtoncomponent to generate and copy shareable event links - Changed scroll alignment from 'start' to 'center' for better UX when navigating to deep-linked events
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| workflow-history-v2.tsx | Added selectedEventIdWithinGroup memo to strip summary_ prefix and updated prop passing |
| workflow-history-ungrouped-table.tsx | Removed unused initialStartIndex prop and updated scroll alignment to 'center' |
| workflow-history-ungrouped-table.types.ts | Removed initialStartIndex from types |
| workflow-history-ungrouped-table.test.tsx | Removed initialStartIndex parameter from test setup |
| workflow-history-ungrouped-event.tsx | Added isUngroupedView prop to WorkflowHistoryGroupDetails |
| workflow-history-grouped-table.tsx | Changed selected boolean to selectedEventId string and updated alignment to 'center' |
| workflow-history-grouped-table.test.tsx | Updated tests to verify selectedEventId prop passing |
| workflow-history-group-details.types.ts | Added isUngroupedView optional prop |
| workflow-history-group-details.tsx | Added link button component and memo for selected event tracking |
| workflow-history-group-details.test.tsx | Added tests for link button functionality |
| workflow-history-event-link-button.types.ts | Defined types for new link button component |
| workflow-history-event-link-button.tsx | Implemented link button with copy functionality |
| workflow-history-event-link-button.test.tsx | Added comprehensive tests for link button |
| workflow-history-event-group.types.ts | Reorganized props with comments and changed selected to selectedEventId |
| workflow-history-event-group.tsx | Updated to use selectedEventId for animation trigger and initial event selection |
| workflow-history-event-group.test.tsx | Updated tests to use selectedEventId instead of selected |
| workflow-history-v2.test.tsx | Added tests for summary_ prefix stripping functionality |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import { type Props as WorkflowHistoryProps } from '../workflow-history-v2.types'; | ||
|
|
||
| export type Props = { | ||
| // Core data props |
There was a problem hiding this comment.
Just rearranged & grouped the props here 😅
| defaultItemHeight={36} | ||
| rangeChanged={setVisibleRange} | ||
| {...(initialStartIndex === undefined | ||
| ? {} |
There was a problem hiding this comment.
This prop wasn't even being passed/meaningfully used so I just removed it
Signed-off-by: Adhitya Mamallan <adhitya.mamallan@uber.com>
| import { type Props as WorkflowHistoryProps } from '../workflow-history-v2.types'; | ||
|
|
||
| export type Props = { | ||
| // Core data props |
| it('renders correctly', () => { | ||
| render(<WorkflowHistoryEventLinkButton historyEventId="123" />); | ||
| const button = screen.getByRole('button'); | ||
| expect(button).toBeInTheDocument(); | ||
| expect(button).toHaveAccessibleName('Copy link to event'); | ||
| }); |
There was a problem hiding this comment.
FYI this type of "assert correct rendering" are a great fit for visual snapshots.
Each expect is converted to pixel-level assertions.
| // TODO: this is a bit hacky, see if there is a better way to mock the window location property | ||
| const originalWindow = window; | ||
| window = Object.create(window); | ||
| Object.defineProperty(window, 'location', { | ||
| value: { | ||
| ...window.location, | ||
| origin: 'http://localhost', | ||
| pathname: | ||
| '/domains/test-domain/workflows/test-workflow/test-run/history', | ||
| }, | ||
| writable: true, | ||
| }); |
There was a problem hiding this comment.
Good call. I'm ok with it as is.
One improvement:
- Type safety.
- Auto "reset" the mock (because we sometimes forget or the test fails midway).
This can be achieved with a mock window util.
There was a problem hiding this comment.
Yup, there are quite a few places that windows are mocked. I'll see if we can write a common mock to use in all these places.
Summary
Test plan
Added/updated unit tests + ran locally.
Loading into an event in the grouped view
Screen.Recording.2025-12-16.at.15.47.15.mov
Loading into an event in the ungrouped view
Screen.Recording.2025-12-16.at.15.59.19.mov