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
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,41 @@
.MutedTime {
color: var(--fgColor-muted);
}

/* Underline treatment when MutedTime renders as a permalink to the event. */
.MutedTimeLink {
text-decoration: underline;
}

/* Span-branch actor name (UserActor without an href, and bot names). No hover-accent:
live-GitHub bold actor text doesn't hover-color — only real links (BoldLink) do. */
.BoldName {
font-weight: var(--base-text-weight-semibold);
color: var(--fgColor-default);
margin-right: var(--base-size-4);
}

/* Trailing "bot" Label spacing in UserActor. */
.BotLabel {
margin-left: var(--base-size-4);
}
Comment on lines +55 to +57

/* Glyph rendered by UserActor for system actors (in place of an avatar). */
.ActorIcon {
vertical-align: middle;
margin-right: var(--base-size-4);
}

/* Muted "icon + small text" sub-row rendered below an event body. */
.EventSubRow {
display: flex;
align-items: center;
margin-top: var(--base-size-4);
font-size: var(--text-body-size-small);
color: var(--fgColor-muted);
}

.EventSubRowIcon {
margin-right: var(--base-size-4);
color: var(--fgColor-muted);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import type {Meta} from '@storybook/react-vite'
import {BoldLink, Examples, InlineAvatar, MutedTime, RealisticTimeline, VariantSection} from './timelineStoryHelpers'
import {MarkGithubIcon, NoteIcon} from '@primer/octicons-react'
import {
BoldLink,
EventSubRow,
Examples,
InlineAvatar,
MutedTime,
RealisticTimeline,
UserActor,
VariantSection,
} from './timelineStoryHelpers'

/**
* Reference stories for the internal, story-only Timeline helpers
Expand Down Expand Up @@ -53,6 +63,24 @@ export const Helpers = () => (
time treatment.
</p>
</VariantSection>

<VariantSection label="UserActor — avatar + actor name (composed from InlineAvatar + BoldLink)">
<p>
<UserActor href="#" muted /> linked+muted, <UserActor size={16} /> unlinked 16px, and a system actor{' '}
<UserActor login="GitHub" icon={MarkGithubIcon} /> rendering a glyph instead of an avatar.
</p>
</VariantSection>

<VariantSection label="EventSubRow — muted icon + small-text sub-row below an event body">
<EventSubRow icon={NoteIcon}>Example note sub-row</EventSubRow>
</VariantSection>

<VariantSection label="MutedTime (permalink) — muted relative time linking to the event">
<p>
Updated <MutedTime date={new Date('2022-07-26T12:00:00Z')} href="#" /> as a permalink, beside a plain{' '}
<MutedTime date={new Date('2022-07-26T12:00:00Z')} /> with no href.
</p>
Comment on lines +67 to +82
</VariantSection>
</RealisticTimeline>
)

Expand Down
83 changes: 80 additions & 3 deletions packages/react/src/Timeline/internal/timelineStoryHelpers.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type React from 'react'
import {clsx} from 'clsx'
import Avatar from '../../Avatar'
import Label from '../../Label'
import Link from '../../Link'
import Octicon from '../../Octicon'
import RelativeTime from '../../RelativeTime'
import classes from './timelineStoryHelpers.module.css'

Expand Down Expand Up @@ -64,7 +66,82 @@ export const InlineAvatar = ({className, size = 20, alt = '', ...props}: React.C
<Avatar {...props} size={size} alt={alt} className={clsx(classes.InlineAvatar, className)} />
)

// TODO(github/primer#6828): remove when Timeline provides a muted relative-time treatment
export const MutedTime = ({date}: {date: Date}) => (
<RelativeTime date={date} format="relative" className={classes.MutedTime} />
export const MONALISA_AVATAR = 'https://avatars.githubusercontent.com/u/583231?v=4'

/**
* An actor "avatar + name" cluster, composed from the shared `InlineAvatar` + `BoldLink`
* helpers so every Timeline surface renders actors identically.
*
* The bot shape mirrors live-GitHub `shared.tsx`: a `[bot]` suffix is stripped from the
* display name, the name renders as an unlinked `<span>`, and a secondary `bot` `Label`
* follows it. The optional `icon` prop renders a glyph instead of an avatar for system
* actors (e.g. Secret scanning's GitHubActor: `<UserActor login="GitHub" icon={MarkGithubIcon} />`).
*
* TODO(github/primer#6827): remove when Primer ships an inline (in-text) avatar treatment.
*/
export const UserActor = ({
login = 'monalisa',
src = MONALISA_AVATAR,
size = 20,
href,
muted = false,
icon,
}: {
login?: string
src?: string
size?: number
href?: string
muted?: boolean
icon?: React.ElementType
}) => {
const isBot = login.endsWith('[bot]')
const display = isBot ? login.replace(/\[bot\]$/i, '') : login
return (
Comment on lines +97 to +99
<>
{icon ? <Octicon icon={icon} className={classes.ActorIcon} /> : <InlineAvatar src={src} size={size} />}
{href && !isBot ? (
<BoldLink href={href} muted={muted}>
{display}
</BoldLink>
) : (
<span className={classes.BoldName}>{display}</span>
)}
{isBot && (
<Label variant="secondary" className={classes.BotLabel}>
bot
</Label>
)}
</>
)
}

/**
* A muted "icon + small text" sub-row rendered below an event body (e.g. a commit
* reference or a note). The four current surfaces drift between flex/margin-top-4 and
* block/margin-top-8 layouts — that variance is incidental, so this canonicalizes to a
* single flex-centered layout. Only the icon and its size vary.
*/
export const EventSubRow = ({
icon,
iconSize = 16,
children,
}: {
icon: React.ElementType
iconSize?: number
children: React.ReactNode
}) => (
<div className={classes.EventSubRow}>
<Octicon icon={icon} size={iconSize} className={classes.EventSubRowIcon} />
<span>{children}</span>
</div>
)

// TODO(github/primer#6828): remove when Timeline provides a muted relative-time treatment
export const MutedTime = ({date, href}: {date: Date; href?: string}) =>
href ? (
<Link href={href} muted className={clsx(classes.MutedTime, classes.MutedTimeLink)}>
<RelativeTime date={date} format="relative" />
</Link>
) : (
<RelativeTime date={date} format="relative" className={classes.MutedTime} />
)
Loading