datahub-web-react/src/app/entityV2/document/changeHistory/ARCHITECTURE.md
The Document Change History feature provides a timeline view of all changes made to a document, including creation, title changes, content modifications, moves, state changes, and deletions. Users can view previous versions and restore them if needed.
This implementation follows best practices for:
changeHistory/
├── ARCHITECTURE.md # This file
├── hooks/
│ └── useParentDocumentTitle.ts # Custom hook for fetching parent titles
├── utils/
│ └── changeUtils.ts # Pure utility functions for data extraction
├── changeMessages/
│ ├── ChangeMessageComponents.tsx # Individual message components + router
│ └── README.md # Guide for adding new change types
├── DocumentChangeHistoryDrawer.tsx # Main drawer container
├── DocumentHistoryTimeline.tsx # Timeline list component
├── DocumentChangeTimelineContent.tsx # Individual timeline entry
├── DocumentChangeTimelineDot.tsx # User avatar for each change
└── PreviousVersionModal.tsx # View/restore previous content
DocumentChangeHistoryDrawer
└── DocumentHistoryTimeline
└── Timeline (from alchemy-components)
├── DocumentChangeTimelineDot (for each item)
└── DocumentChangeTimelineContent (for each item)
├── ChangeMessage (router component)
│ ├── CreatedMessage
│ ├── TitleChangedMessage
│ ├── TextChangedMessage
│ ├── StateChangedMessage
│ ├── ParentChangedMessage (uses useParentDocumentTitle hook)
│ ├── DeletedMessage
│ └── DefaultMessage
└── PreviousVersionModal (conditional, for text changes)
extractChangeDetails(details)Converts GraphQL StringMapEntry[] to Record<string, string>
utils/changeUtils.ts// Easy to unit test
const details = [
{ key: 'oldTitle', value: 'Old' },
{ key: 'newTitle', value: 'New' },
];
const result = extractChangeDetails(details);
// { oldTitle: 'Old', newTitle: 'New' }
getActorDisplayName(actor, getDisplayName)Extracts actor display name with fallback
utils/changeUtils.tsuseParentDocumentTitle(parentUrn)Fetches parent document title with proper state handling
{ title, loading, error }'...''Unknown Document' + logs error'...'hooks/useParentDocumentTitle.ts| Component | Loading Handled | Error Handled | Notes |
|---|---|---|---|
useParentDocumentTitle | ✅ | ✅ | Shows '...' while loading, 'Unknown Document' on error |
PreviousVersionModal | ✅ | ✅ | Disables buttons during restore, shows error message |
DocumentHistoryTimeline | ✅ | ✅ | Shows loading skeleton, empty state message |
All messages follow the pattern: {ActorName} {action} {target}
See changeMessages/README.md for detailed instructions. Quick summary:
export const MyNewMessage: React.FC<ActorWithDetailsProps> = ({ actorName, details }) => (
<ActionText>
<ActorName>{actorName}</ActorName> did something
</ActionText>
);
case DocumentChangeType.MyNewType:
return <MyNewMessage actorName={actorName} details={details} />;
yarn generate to update typesquery getDocumentChangeHistory($urn: String!, $limit: Int) {
document(urn: $urn) {
changeHistory(limit: $limit) {
changeType
description
actor {
urn
type
username
info
editableProperties
}
timestamp
details {
key
value
}
}
}
}
mutation updateDocumentContents($input: UpdateDocumentContentsInput!) {
updateDocumentContents(input: $input)
}
changeUtils.ts functions (pure functions, easy to test)useParentDocumentTitle hook (mock GraphQL responses)details object memoized in componentChangeMessageComponents.tsxhooks/utils/@app/entityV2/document - Document queries and mutations@app/useEntityRegistry - Entity display names@app/sharedV2/modals/ConfirmationModal - Confirmation dialogs@src/alchemy-components - UI components (Timeline, Popover, Button, etc.)dayjs - Date formatting and relative timeantd - Modal componentreact - Component frameworkstyled-components - StylingUpdate message text: Edit the appropriate component in changeMessages/ChangeMessageComponents.tsx
Change timestamp format: Modify dayjs.format() calls in DocumentChangeTimelineContent.tsx
Add new detail field: Update backend event generator → regenerate types → use in message component
Customize styling: Update styled-components in respective files
Timeline not showing: Check GraphQL query response in DevTools Network tab
Parent name shows '...': Check if parent URN is valid and document exists
Restore not working: Check mutation response and refetch behavior
Wrong actor name: Verify actor data in change history response