Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/timeline-badge-variant-clip-sidebar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react': minor
---

Timeline: add `variant` prop to `Timeline.Badge` for built-in color schemes (`accent`, `success`, `attention`, `severe`, `danger`, `done`, `open`, `closed`, `sponsors`)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions e2e/components/Timeline.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ const stories = [
title: 'Timeline Break',
id: 'components-timeline-features--timeline-break',
},
{
title: 'Badge Variants',
id: 'components-timeline-features--badge-variants',
},
] as const

test.describe('Timeline', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/doc-gen/src/__tests__/ts-utils.patterns.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {parseTypeInfo} from '../ts-utils'
const directory = path.resolve(import.meta.dirname)
const FIXTURE_PATH = path.join(directory, 'fixtures')

describe('getPropTypeForComponent', () => {
describe('getPropTypeForComponent', {timeout: 30_000}, () => {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this cause this one is super slow

it('extracts props for FunctionComponent', () => {
const info = parseTypeInfo(FIXTURE_PATH, 'FunctionComponent')
expect(info.props.foo).toMatchObject({name: 'foo', type: 'string', required: true})
Expand Down
11 changes: 10 additions & 1 deletion packages/react/src/Timeline/Timeline.docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
{
"id": "components-timeline-features--timeline-break"
},
{
"id": "components-timeline-features--badge-variants"
},
{
"id": "components-timeline-features--with-inline-links"
}
Expand All @@ -47,7 +50,13 @@
},
{
"name": "Timeline.Badge",
"props": []
"props": [
{
"name": "variant",
"type": "'accent' | 'success' | 'attention' | 'severe' | 'danger' | 'done' | 'open' | 'closed' | 'sponsors'",
"description": "The color variant of the badge."
}
]
},
{
"name": "Timeline.Body",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
.BadgeWithDoneBackground {
background-color: var(--bgColor-done-emphasis);
}

.LinkWithBoldStyle {
font-weight: var(--base-text-weight-semibold);
color: var(--fgColor-default);
Expand All @@ -11,7 +7,3 @@
.LinkWithBoldStyle:hover {
color: var(--fgColor-accent);
}

.GitMergeIcon {
color: var(--fgColor-onEmphasis);
}
76 changes: 73 additions & 3 deletions packages/react/src/Timeline/Timeline.features.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@ import type {Meta} from '@storybook/react-vite'
import type {ComponentProps} from '../utils/types'
import Timeline from './Timeline'
import Octicon from '../Octicon'
import {GitBranchIcon, GitCommitIcon, GitMergeIcon} from '@primer/octicons-react'
import {
FlameIcon,
GitBranchIcon,
GitCommitIcon,
GitMergeIcon,
GitPullRequestIcon,
HeartIcon,
IssueClosedIcon,
IssueOpenedIcon,
SkipIcon,
XIcon,
} from '@primer/octicons-react'
import Link from '../Link'
import classes from './Timeline.features.stories.module.css'

Expand Down Expand Up @@ -101,8 +112,8 @@ export const CondensedItems = () => (
export const TimelineBreak = () => (
<Timeline>
<Timeline.Item>
<Timeline.Badge className={classes.BadgeWithDoneBackground}>
<Octicon icon={GitMergeIcon} className={classes.GitMergeIcon} aria-label="Merged" />
<Timeline.Badge variant="done">
<Octicon icon={GitMergeIcon} aria-label="Merged" />
</Timeline.Badge>
<Timeline.Body>This is a message</Timeline.Body>
</Timeline.Item>
Expand All @@ -116,6 +127,65 @@ export const TimelineBreak = () => (
</Timeline>
)

export const BadgeVariants = () => (
<Timeline>
<Timeline.Item>
<Timeline.Badge variant="accent">
<Octicon icon={GitPullRequestIcon} aria-label="Pull request" />
</Timeline.Badge>
<Timeline.Body>Accent</Timeline.Body>
</Timeline.Item>
<Timeline.Item>
<Timeline.Badge variant="success">
<Octicon icon={IssueClosedIcon} aria-label="Closed" />
</Timeline.Badge>
<Timeline.Body>Success</Timeline.Body>
</Timeline.Item>
<Timeline.Item>
<Timeline.Badge variant="attention">
<Octicon icon={FlameIcon} aria-label="Attention" />
</Timeline.Badge>
<Timeline.Body>Attention</Timeline.Body>
</Timeline.Item>
<Timeline.Item>
<Timeline.Badge variant="severe">
<Octicon icon={SkipIcon} aria-label="Severe" />
</Timeline.Badge>
<Timeline.Body>Severe</Timeline.Body>
</Timeline.Item>
<Timeline.Item>
<Timeline.Badge variant="danger">
<Octicon icon={XIcon} aria-label="Danger" />
</Timeline.Badge>
<Timeline.Body>Danger</Timeline.Body>
</Timeline.Item>
<Timeline.Item>
<Timeline.Badge variant="done">
<Octicon icon={GitMergeIcon} aria-label="Merged" />
</Timeline.Badge>
<Timeline.Body>Done</Timeline.Body>
</Timeline.Item>
<Timeline.Item>
<Timeline.Badge variant="open">
<Octicon icon={IssueOpenedIcon} aria-label="Open" />
</Timeline.Badge>
<Timeline.Body>Open</Timeline.Body>
</Timeline.Item>
<Timeline.Item>
<Timeline.Badge variant="closed">
<Octicon icon={IssueClosedIcon} aria-label="Closed" />
</Timeline.Badge>
<Timeline.Body>Closed</Timeline.Body>
</Timeline.Item>
<Timeline.Item>
<Timeline.Badge variant="sponsors">
<Octicon icon={HeartIcon} aria-label="Sponsors" />
</Timeline.Badge>
<Timeline.Body>Sponsors</Timeline.Body>
</Timeline.Item>
</Timeline>
)

export const WithInlineLinks = () => (
<Timeline>
<Timeline.Item>
Expand Down
41 changes: 41 additions & 0 deletions packages/react/src/Timeline/Timeline.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,47 @@
border-radius: 50%;
align-items: center;
justify-content: center;

&:where([data-variant]) {
color: var(--fgColor-onEmphasis);
border-color: transparent;
}

&:where([data-variant='accent']) {
background-color: var(--bgColor-accent-emphasis);
}

&:where([data-variant='success']) {
background-color: var(--bgColor-success-emphasis);
}

&:where([data-variant='attention']) {
background-color: var(--bgColor-attention-emphasis);
}

&:where([data-variant='severe']) {
background-color: var(--bgColor-severe-emphasis);
}

&:where([data-variant='danger']) {
background-color: var(--bgColor-danger-emphasis);
}

&:where([data-variant='done']) {
background-color: var(--bgColor-done-emphasis);
}

&:where([data-variant='open']) {
background-color: var(--bgColor-open-emphasis);
}

&:where([data-variant='closed']) {
background-color: var(--bgColor-closed-emphasis);
}

&:where([data-variant='sponsors']) {
background-color: var(--bgColor-sponsors-emphasis);
}
}

.TimelineBody {
Expand Down
17 changes: 15 additions & 2 deletions packages/react/src/Timeline/Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,28 @@ const TimelineItem = React.forwardRef<HTMLDivElement, TimelineItemProps>(

TimelineItem.displayName = 'TimelineItem'

export type TimelineBadgeVariant =
| 'accent'
| 'success'
| 'attention'
| 'severe'
| 'danger'
| 'done'
| 'open'
| 'closed'
| 'sponsors'

export type TimelineBadgeProps = {
children?: React.ReactNode
className?: string
/** The color variant of the badge */
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TimelineBadgeVariant includes a 'default' value, but Timeline.Badge treats 'default' the same as omitting variant (no data-variant is rendered). Consider clarifying this in the variant JSDoc (e.g., “defaults to 'default' / omit for default styling”) or, if 'default' shouldn’t be public API, remove it from the union and update docs/tests accordingly.

Suggested change
/** The color variant of the badge */
/**
* The color variant of the badge.
*
* Defaults to the "default" styling. Omit this prop or set it to "default"
* for the default appearance; other values render a variant-specific style.
*/

Copilot uses AI. Check for mistakes.
variant?: TimelineBadgeVariant
} & React.ComponentPropsWithoutRef<'div'>

const TimelineBadge = ({className, ...props}: TimelineBadgeProps) => {
const TimelineBadge = ({className, variant, ...props}: TimelineBadgeProps) => {
return (
<div className={classes.TimelineBadgeWrapper}>
<div {...props} className={clsx(className, classes.TimelineBadge)} />
<div {...props} className={clsx(className, classes.TimelineBadge)} data-variant={variant} />
</div>
)
}
Expand Down
10 changes: 10 additions & 0 deletions packages/react/src/Timeline/__tests__/Timeline.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ describe('Timeline.Item', () => {

describe('Timeline.Badge', () => {
implementsClassName(Timeline.Badge, classes.TimelineBadge)

it('renders with variant prop', () => {
const {container} = render(<Timeline.Badge variant="done" />)
expect(container.querySelector(`.${classes.TimelineBadge}`)).toHaveAttribute('data-variant', 'done')
})

it('does not render data-variant when variant is omitted', () => {
const {container} = render(<Timeline.Badge />)
expect(container.querySelector(`.${classes.TimelineBadge}`)).not.toHaveAttribute('data-variant')
})
})

describe('Timeline.Body', () => {
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/Timeline/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export type {
TimelineProps,
TimelineItemsProps,
TimelineItemProps,
TimelineBadgeVariant,
TimelineBadgeProps,
TimelineBodyProps,
TimelineBreakProps,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ exports[`@primer/react > should not update exports without a semver change 1`] =
"ThemeShadowPaths",
"Timeline",
"type TimelineBadgeProps",
"type TimelineBadgeVariant",
"type TimelineBodyProps",
"type TimelineBreakProps",
"type TimelineItemProps",
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ export type {TextProps} from './Text'
export {default as Timeline} from './Timeline'
export type {
TimelineProps,
TimelineBadgeVariant,
TimelineBadgeProps,
TimelineBodyProps,
TimelineBreakProps,
Expand Down
Loading