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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,18 +203,46 @@ This strategy can be used by rendering the `<GitLog.GraphCanvas2D />` subcompone

# Git Log Data

The array of `GitLogEntry` objects is the source of data used by the `GitLog` component. It has the following properties:
The array of `GitLogEntry` objects is the source of data used by the core `GitLog` components. It has the following properties:

| Property | Type | Description |
|-----------------|-----------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
| `hash` | `string` | The unique hash identifier of the commit. |
| `branch` | `string` | The name of the branch this commit belongs to. |
| `parents` | `string[]` | An array of parent commit hashes. If this is a merge commit, it will have multiple parents. If it's an initial commit, it will have none. |
| `message` | `string` | The commit message describing the changes made in this commit. |
| `author` | `CommitAuthor?` | Details of the user who authored the commit. |
| `committerDate` | `string` | The date and time when the commit was applied by the committer. Typically the timestamp when the commit was finalized. |
| `author` | `CommitAuthor?` | *(Optional)* Details of the user who authored the commit. |
| `authorDate` | `string?` | *(Optional)* The date and time when the commit was originally authored. May differ from `committerDate` if the commit was rebased or amended. |

You can pass a generic type to the `GitLog` or `GitLogPaged` components to augment the `GitLogEntry` data so that any `Commit` instances surfaced by callback functions will have your custom metadata passed back to you for convenience. For example:

```typescript jsx
import { GitLog } from "@tomplum/react-git-log"

interface MyCustomCommit {
myCustomField: string
}

const YourConsumer = () => {
const { entries, currentBranch } = useYourDataSource()

return (
<GitLog
entries={entries}
currentBranch={currentBranch}
onSelectCommit={({ commit }) => {
console.log(commit.myCustomField)
}}
>
<GitLog.Tags />
<GitLog.GraphHTMLGrid />
<GitLog.Table />
</GitLog>
)
}
```

> [!TIP]
> Usually you'd be sourcing this data from a backend service like a web-api, but you can extract it from the command line with the following command.

Expand Down
32 changes: 30 additions & 2 deletions packages/library/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,18 +120,46 @@ A flexible and interactive React component for visualising Git commit history. D

# Git Log Data

The array of `GitLogEntry` objects is the source of data used by the `GitLog` component. It has the following properties:
The array of `GitLogEntry` objects is the source of data used by the core `GitLog` components. It has the following properties:

| Property | Type | Description |
|-----------------|-----------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
| `hash` | `string` | The unique hash identifier of the commit. |
| `branch` | `string` | The name of the branch this commit belongs to. |
| `parents` | `string[]` | An array of parent commit hashes. If this is a merge commit, it will have multiple parents. If it's an initial commit, it will have none. |
| `message` | `string` | The commit message describing the changes made in this commit. |
| `author` | `CommitAuthor?` | Details of the user who authored the commit. |
| `committerDate` | `string` | The date and time when the commit was applied by the committer. Typically the timestamp when the commit was finalized. |
| `author` | `CommitAuthor?` | *(Optional)* Details of the user who authored the commit. |
| `authorDate` | `string?` | *(Optional)* The date and time when the commit was originally authored. May differ from `committerDate` if the commit was rebased or amended. |

You can pass a generic type to the `GitLog` or `GitLogPaged` components to augment the `GitLogEntry` data so that any `Commit` instances surfaced by callback functions will have your custom metadata passed back to you for convenience. For example:

```typescript jsx
import { GitLog } from "@tomplum/react-git-log"

interface MyCustomCommit {
myCustomField: string
}

const YourConsumer = () => {
const { entries, currentBranch } = useYourDataSource()

return (
<GitLog
entries={entries}
currentBranch={currentBranch}
onSelectCommit={({ commit }) => {
console.log(commit.myCustomField)
}}
>
<GitLog.Tags />
<GitLog.GraphHTMLGrid />
<GitLog.Table />
</GitLog>
)
}
```

> [!TIP]
> Usually you'd be sourcing this data from a backend service like a web-api, but you can extract it from the command line with the following command.

Expand Down
3 changes: 1 addition & 2 deletions packages/library/src/GitLog.integration.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ describe.skip('GitLog Integration', () => {
if (columnState.isNode) {
const missingNodeMsg = `Expected commit node element in row ${rowIndex}, column ${columnIndex} with hash ${commit.hash}, but it was not found in the graph`
const commitNodeTestId = graphColumn.commitNodeId({ hash: commit.hash })
expect(insideCurrentColumn.getByTestId(commitNodeTestId), missingNodeMsg)
expect(insideCurrentColumn.getByTestId(commitNodeTestId), missingNodeMsg).toBeInTheDocument()
debugMetrics['commit-nodes'] = (debugMetrics['commit-nodes'] ?? 0) + 1

// If the commit is a merge commit
Expand Down Expand Up @@ -238,7 +238,6 @@ describe.skip('GitLog Integration', () => {
const isHeadCommit = commit.hash === headCommit?.hash

if (!headCommit && (columnState.isColumnAboveEmpty || commit.isBranchTip)) {
console.log(commit.hash, columnState)
expect(insideCurrentColumn.getByTestId(graphColumn.bottomHalfVerticalLineId)).toBeInTheDocument()
}

Expand Down
100 changes: 99 additions & 1 deletion packages/library/src/GitLog.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { act } from 'react'
import { Commit } from 'types/Commit'
import { table } from 'test/elements/Table'
import { createCanvas } from 'canvas'
import { GitLogEntry } from 'types/GitLogEntry'

const today = Date.UTC(2025, 2, 24, 18, 0, 0)

Expand All @@ -22,7 +23,7 @@ const urlBuilderFunction: GitLogUrlBuilder = ({ commit }) => ({

const sleepRepositoryLogEntries = parseGitLogOutput(sleepRepositoryData)

const getSleepRepositoriesLogEntries = (quantity: number) => {
const getSleepRepositoriesLogEntries = (quantity: number): GitLogEntry[] => {
const entries = sleepRepositoryLogEntries.slice(0, quantity + 1)
entries[quantity - 1].parents = []
return entries
Expand Down Expand Up @@ -345,6 +346,56 @@ describe('GitLog', () => {
})
})

it('should call onSelectCommit with custom data passed into the log entries', () => {
const handleSelectCommit = vi.fn()

interface CustomCommit {
customField: string,
moreMetaData: number[]
}

render(
<GitLog<CustomCommit>
showGitIndex
currentBranch='release'
onSelectCommit={handleSelectCommit}
entries={getSleepRepositoriesLogEntries(6).map(entry => ({
...entry,
customField: 'testing',
moreMetaData: [678]
}))}
>
<GitLog.GraphHTMLGrid />
<GitLog.Table />
</GitLog>
)

expect(handleSelectCommit).not.toHaveBeenCalled()

act(() => {
graphColumn.at({ row: 1, column: 0 })?.click()
})

expect(handleSelectCommit).toHaveBeenCalledExactlyOnceWith<Commit<CustomCommit>[]>({
authorDate: '2025-03-24 17:03:58 +0000',
branch: 'refs/remotes/origin/renovate/all-minor-patch',
children: [],
committerDate: '2025-03-24 17:03:58 +0000',
author: {
email: '29139614+renovate[bot]@users.noreply.github.com',
name: 'renovate[bot]',
},
hash: '2079fb6',
isBranchTip: true,
message: 'fix(deps): update all non-major dependencies',
parents: [
'1352f4c',
],
customField: 'testing',
moreMetaData: [678]
})
})

it.each([0, 1, 2])('should call onSelectCommit with the commit details when clicking on column index [%s] in a commits row', (columnIndex: number) => {
const handleSelectCommit = vi.fn()

Expand Down Expand Up @@ -526,6 +577,53 @@ describe('GitLog', () => {
})
})

it('should call onPreviewCommit with the custom data passed into the log', () => {
const handlePreviewCommit = vi.fn()

interface CustomCommit {
test: string
anotherTest: number
}

render(
<GitLog<CustomCommit>
currentBranch='release'
onPreviewCommit={handlePreviewCommit}
entries={getSleepRepositoriesLogEntries(3).map(entry => ({
...entry,
test: 'hello',
anotherTest: 5
}))}
>
<GitLog.GraphHTMLGrid />
<GitLog.Table />
</GitLog>
)

expect(handlePreviewCommit).not.toHaveBeenCalled()

act(() => {
fireEvent.mouseOver(graphColumn.at({ row: 1, column: 0 }))
})

expect(handlePreviewCommit).toHaveBeenCalledExactlyOnceWith<Commit<CustomCommit>[]>({
authorDate: '2025-03-24 17:03:58 +0000',
branch: 'refs/remotes/origin/renovate/all-minor-patch',
author: {
name: 'renovate[bot]',
email: '29139614+renovate[bot]@users.noreply.github.com',
},
children: [],
committerDate: '2025-03-24 17:03:58 +0000',
hash: '2079fb6',
isBranchTip: true,
message: 'fix(deps): update all non-major dependencies',
parents: ['1352f4c'],
test: 'hello',
anotherTest: 5
})
})

it('should call onPreviewCommit with the commit details when mousing over on one of the table columns of the index pseudo-commit row', () => {
const handlePreviewCommit = vi.fn()

Expand Down
7 changes: 2 additions & 5 deletions packages/library/src/GitLog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@ import { GraphCanvas2D, GraphHTMLGrid } from './modules/Graph'
import { Table } from './modules/Table'
import { GitLogCore } from './components/GitLogCore'

export const GitLog = ({ children, ...props }: PropsWithChildren<GitLogProps>) => {
export const GitLog = <T,>({ children, ...props }: PropsWithChildren<GitLogProps<T>>) => {
return (
<GitLogCore
{...props}
componentName="GitLog"
>
<GitLogCore<T> {...props} componentName="GitLog">
{children}
</GitLogCore>
)
Expand Down
Loading