From 6bebe870cf5490dba9e2b19ac54b2a8ccacd7b2e Mon Sep 17 00:00:00 2001
From: Jan Maarten <83665577+janmaarten-a11y@users.noreply.github.com>
Date: Sun, 28 Jun 2026 13:27:21 -0700
Subject: [PATCH 1/3] Add Secret Scanning alert timeline stories
(proof-of-pattern: Created group)
Storybook-only stories recreating GitHub's secret-scanning alert timeline
events with Primer Timeline slots, mirroring the Issues and Dependabot
surfaces. Scopes this commit to the canonical Created/Opened event group plus
the full file scaffold (file-scoped FeatureFlags decorator, GitHub system actor
+ muted Time helpers, header doc block).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
...ecret-scanning.features.stories.module.css | 45 ++++++
...eline.secret-scanning.features.stories.tsx | 137 ++++++++++++++++++
2 files changed, 182 insertions(+)
create mode 100644 packages/react/src/Timeline/Timeline.secret-scanning.features.stories.module.css
create mode 100644 packages/react/src/Timeline/Timeline.secret-scanning.features.stories.tsx
diff --git a/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.module.css b/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.module.css
new file mode 100644
index 00000000000..a95131b6aa1
--- /dev/null
+++ b/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.module.css
@@ -0,0 +1,45 @@
+.RealisticTimeline {
+ /* GitHub renders the timeline at most 1012px wide in product surfaces. */
+ max-width: 1012px;
+}
+
+/* Story-only scaffolding: each variant is wrapped in its own with a
+ small caption heading ABOVE the event, so the event row itself (badge +
+ inline actor + body) renders exactly as it would in product — the caption
+ never sits inside Timeline.Body. Variants stay scannable like a Figma
+ component set. Not part of the faithful event. */
+.Variant {
+ margin-bottom: var(--base-size-24);
+}
+
+.VariantLabel {
+ margin: 0 0 var(--base-size-4);
+ font-size: var(--text-body-size-small);
+ font-weight: var(--base-text-weight-semibold);
+ color: var(--fgColor-muted);
+ text-transform: uppercase;
+ letter-spacing: 0.04em;
+}
+
+/* System "GitHub" actor — live `AlertTimeline.tsx` `TimelineItemBody` renders
+ `GitHub` (no avatar)
+ when `isGitHubActor`. This class spaces the leading octicon. */
+.ActorIcon {
+ vertical-align: middle;
+ margin-right: var(--base-size-4);
+}
+
+/* Bold "GitHub" label, mirroring the live `text-bold` span. */
+.ActorName {
+ font-weight: var(--base-text-weight-semibold);
+ color: var(--fgColor-default);
+ margin-right: var(--base-size-4);
+}
+
+/* Muted relative timestamp. The live secret-scanning `TimelineItemBody` renders
+ a plain `RelativeTime` (no link wrapper) — muted text only, matching the
+ Dependabot timeline (and a deliberate difference from the Issues `Ago`
+ deep-link). */
+.Timestamp {
+ color: var(--fgColor-muted);
+}
diff --git a/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.tsx b/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.tsx
new file mode 100644
index 00000000000..410f9306509
--- /dev/null
+++ b/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.tsx
@@ -0,0 +1,137 @@
+import type {Meta} from '@storybook/react-vite'
+import type {ComponentProps} from '../utils/types'
+import {FeatureFlags} from '../FeatureFlags'
+import Timeline from './Timeline'
+import {MarkGithubIcon, ShieldIcon} from '@primer/octicons-react'
+import Octicon from '../Octicon'
+import RelativeTime from '../RelativeTime'
+import classes from './Timeline.secret-scanning.features.stories.module.css'
+
+/**
+ * Secret Scanning alert Timeline event examples (Phase 2 of github/primer#6663).
+ *
+ * These stories recreate GitHub's live secret-scanning-alert-timeline events
+ * using the Primer `Timeline` compositional slots. They mirror the established
+ * Issues (`Timeline.issues.features.stories.tsx`) and Dependabot
+ * (`Timeline.dependabot.features.stories.tsx`) stories and are sourced from the
+ * `timeline-audit` inventory (`secret-scanning-timeline-events-for-figma.md`),
+ * verified against the live React implementation.
+ *
+ * SOURCE OF TRUTH — Secret Scanning is FULLY React (NOT ERB). The alert show
+ * page is a React SPA in `github/github-ui`, package
+ * `packages/secret-scanning-alerts/`. The timeline is rendered by
+ * `components/show/AlertTimeline.tsx`, whose `switch (event.type)` is the
+ * authoritative dispatch for every event's badge variant + octicon + copy. It
+ * already composes Primer React `Timeline` + `Timeline.Badge variant=`, so the
+ * badge colors map directly. The actor is rendered by
+ * `components/shared/UserComponent.tsx` (16px circle avatar + bold login); the
+ * system actor is `MarkGithubIcon` + bold "GitHub". (No ERB secret-scanning
+ * timeline exists — the migration to React is complete.)
+ *
+ * SCOPE: These are Storybook-only examples by design. They are intentionally
+ * NOT wired into components-json / the primer.style docs page (do NOT add this
+ * file to `Timeline.docs.json` or `build.ts`). Individual timeline events are
+ * not consumer-facing components — the primer.style Timeline page reflects the
+ * base `Timeline` component's own stories, and any docs-site representation is a
+ * Phase 3 consideration via base-component story changes, out of scope here.
+ *
+ * FUTURE FILTERING (taxonomy still open — github/primer#6663): category
+ * `data-*` attributes (e.g. `data-event-category="created"`) will attach to each
+ * `Timeline.Item` below so stories can be filtered/grouped by event family. We
+ * intentionally do NOT add them yet to avoid baking in a taxonomy.
+ *
+ * SLOT USAGE (Phase 1 slots — same convention as the Issues / Dependabot groups):
+ * - `Timeline.Avatar` (gutter slot, #6677): the 40px LEFT-GUTTER avatar.
+ * Reserved for comment-style events. Badge-row events like Created do NOT use
+ * it — the live `AlertTimeline.tsx` renders the actor INLINE in the body
+ * (`UserComponent`, or the system `MarkGithubIcon` + "GitHub"), not in the
+ * gutter. We mirror that: actor inline in `Timeline.Body`.
+ * - `Timeline.Actions` (right-controls slot, #6678): for buttons on the right
+ * edge (e.g. the delegated-closure "Review request" / "Cancel request"
+ * buttons). Created has no right controls, so it is omitted here.
+ *
+ * BADGE COLORS (live `Timeline.Badge variant=`): success (green) — Creation,
+ * Reopened; done (purple) — Closed as revoked; danger (red) — Validity active;
+ * attention (amber) — Validity unknown; default (gray) — everything else. Where
+ * the live code uses a default (gray) badge with no solid background, downstream
+ * groups should drive `--timelineBadge-bgColor` inline (as the Issues "not
+ * planned" variant does) rather than relying on a non-existent `neutral` variant.
+ */
+
+/**
+ * System "GitHub" actor — live `TimelineItemBody` (`AlertTimeline.tsx`) in
+ * `isGitHubActor` mode renders `GitHub` (no avatar). Used by the Created event and by
+ * automated validity changes.
+ */
+const GitHubActor = () => (
+ <>
+
+ GitHub
+ >
+)
+
+// Muted relative timestamp. The live secret-scanning `TimelineItemBody` renders
+// a plain `RelativeTime` with no link wrapper — muted text only (matching the
+// Dependabot timeline, unlike the Issues `Ago` deep-link).
+const Time = ({date}: {date: string}) => (
+
+
+
+)
+
+export default {
+ title: 'Components/Timeline/Events/Secret Scanning',
+ component: Timeline,
+ subcomponents: {
+ 'Timeline.Item': Timeline.Item,
+ 'Timeline.Avatar': Timeline.Avatar,
+ 'Timeline.Badge': Timeline.Badge,
+ 'Timeline.Body': Timeline.Body,
+ 'Timeline.Break': Timeline.Break,
+ 'Timeline.Actions': Timeline.Actions,
+ },
+ decorators: [
+ // File-scoped: render every story in the future-state list semantics
+ // (``/`
`). The `primer_react_timeline_list_semantics` flag is merged
+ // on main; this opts these stories into the DOM the timeline will ship.
+ Story => (
+
+
+
+ ),
+ ],
+} as Meta>
+
+/**
+ * The Created event group — `SecretScanTimeline.eventCreated` (audit § 1).
+ *
+ * Source: `case TimelineEventType.Creation` in `AlertTimeline.tsx` — the only
+ * event that uses `isGitHubActor` unconditionally. The actor is ALWAYS the
+ * GitHub system actor. Badge: `ShieldIcon` on `success` (green) — the live code
+ * renders `}>`. Copy is a
+ * fixed `"opened this alert"` + the relative time.
+ *
+ * Single variant — the live `Creation` case has exactly one rendering (no
+ * source / from-PR / from-push branches like Dependabot's Opened).
+ */
+export const EventCreated = () => (
+
+ {/* Created — GitHub system actor, ShieldIcon on success (green) */}
+
+
+)
From ea8c6a2eb6e7b704ac07ac1e469e10c7e8101e69 Mon Sep 17 00:00:00 2001
From: Jan Maarten <83665577+janmaarten-a11y@users.noreply.github.com>
Date: Sun, 28 Jun 2026 13:36:12 -0700
Subject: [PATCH 2/3] Add remaining Secret Scanning timeline event groups
Complete the secret-scanning surface with all 7 event groups (29 variants):
Resolution (7 closed reasons + Reopened), Bypass + delegated bypass, Validity
change (active/inactive/unknown x automated/manual), Report, delegated closure
request/approve/deny/cancel (with Review/Cancel right-controls), and Assignment
(actor + assignee avatars). Extract UserActor and CommentSubRow helpers. Every
group is verified against the live AlertTimeline.tsx switch.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
...ecret-scanning.features.stories.module.css | 28 +-
...eline.secret-scanning.features.stories.tsx | 686 +++++++++++++++++-
2 files changed, 703 insertions(+), 11 deletions(-)
diff --git a/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.module.css b/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.module.css
index a95131b6aa1..9d9a78e3980 100644
--- a/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.module.css
+++ b/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.module.css
@@ -29,13 +29,39 @@
margin-right: var(--base-size-4);
}
-/* Bold "GitHub" label, mirroring the live `text-bold` span. */
+/* Bold actor name, mirroring the live `text-bold` span. Shared by the system
+ "GitHub" label and the user `display_login` (live `UserComponent` renders the
+ login as bold TEXT, not a link). */
.ActorName {
font-weight: var(--base-text-weight-semibold);
color: var(--fgColor-default);
margin-right: var(--base-size-4);
}
+/* Inline user-actor avatar in the body. Live `UserComponent` renders a 16px
+ CIRCLE `GitHubAvatar` followed by the bold login. */
+.InlineAvatar {
+ vertical-align: middle;
+ margin-right: var(--base-size-4);
+}
+
+/* Optional comment sub-row. Live `TimelineItemBody` renders a small indented
+ block below the body: a 12px muted `CommentIcon` + f6 comment text. Driven by
+ `resolution.comment` / `exemption_request.requester_comment` /
+ `exemption_response.reviewer_comment`. */
+.CommentRow {
+ display: flex;
+ align-items: center;
+ margin-top: var(--base-size-4);
+ font-size: var(--text-body-size-small);
+ color: var(--fgColor-muted);
+}
+
+.CommentRowIcon {
+ margin-right: var(--base-size-4);
+ color: var(--fgColor-muted);
+}
+
/* Muted relative timestamp. The live secret-scanning `TimelineItemBody` renders
a plain `RelativeTime` (no link wrapper) — muted text only, matching the
Dependabot timeline (and a deliberate difference from the Issues `Ago`
diff --git a/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.tsx b/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.tsx
index 410f9306509..ee842603ca2 100644
--- a/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.tsx
+++ b/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.tsx
@@ -2,7 +2,22 @@ import type {Meta} from '@storybook/react-vite'
import type {ComponentProps} from '../utils/types'
import {FeatureFlags} from '../FeatureFlags'
import Timeline from './Timeline'
-import {MarkGithubIcon, ShieldIcon} from '@primer/octicons-react'
+import {
+ AlertIcon,
+ CheckCircleIcon,
+ CommentIcon,
+ MarkGithubIcon,
+ PersonIcon,
+ ShieldCheckIcon,
+ ShieldIcon,
+ ShieldSlashIcon,
+ SkipIcon,
+ SyncIcon,
+ XIcon,
+} from '@primer/octicons-react'
+import type React from 'react'
+import Avatar from '../Avatar'
+import {Button} from '../Button'
import Octicon from '../Octicon'
import RelativeTime from '../RelativeTime'
import classes from './Timeline.secret-scanning.features.stories.module.css'
@@ -26,7 +41,10 @@ import classes from './Timeline.secret-scanning.features.stories.module.css'
* badge colors map directly. The actor is rendered by
* `components/shared/UserComponent.tsx` (16px circle avatar + bold login); the
* system actor is `MarkGithubIcon` + bold "GitHub". (No ERB secret-scanning
- * timeline exists — the migration to React is complete.)
+ * timeline exists — the migration to React is complete.) The exact event list
+ * built below is the full set of cases the live `switch` actually renders; the
+ * defined-but-never-dispatched `REVOCATION` event type (no `case`) renders
+ * nothing and is intentionally NOT built.
*
* SCOPE: These are Storybook-only examples by design. They are intentionally
* NOT wired into components-json / the primer.style docs page (do NOT add this
@@ -42,22 +60,36 @@ import classes from './Timeline.secret-scanning.features.stories.module.css'
*
* SLOT USAGE (Phase 1 slots — same convention as the Issues / Dependabot groups):
* - `Timeline.Avatar` (gutter slot, #6677): the 40px LEFT-GUTTER avatar.
- * Reserved for comment-style events. Badge-row events like Created do NOT use
- * it — the live `AlertTimeline.tsx` renders the actor INLINE in the body
+ * Reserved for comment-style events. The badge-row events here do NOT use it —
+ * the live `AlertTimeline.tsx` renders the actor INLINE in the body
* (`UserComponent`, or the system `MarkGithubIcon` + "GitHub"), not in the
* gutter. We mirror that: actor inline in `Timeline.Body`.
* - `Timeline.Actions` (right-controls slot, #6678): for buttons on the right
- * edge (e.g. the delegated-closure "Review request" / "Cancel request"
- * buttons). Created has no right controls, so it is omitted here.
+ * edge. Only the delegated-closure "requested to dismiss" event has right
+ * controls (a "Review request" / "Cancel request" button), so it is the only
+ * group that uses this slot.
*
* BADGE COLORS (live `Timeline.Badge variant=`): success (green) — Creation,
* Reopened; done (purple) — Closed as revoked; danger (red) — Validity active;
- * attention (amber) — Validity unknown; default (gray) — everything else. Where
- * the live code uses a default (gray) badge with no solid background, downstream
- * groups should drive `--timelineBadge-bgColor` inline (as the Issues "not
- * planned" variant does) rather than relying on a non-existent `neutral` variant.
+ * attention (amber) — Validity unknown; default (gray) — everything else. The
+ * live code passes `variant={undefined}` for the gray events, which renders the
+ * DEFAULT `Timeline.Badge` (a muted icon on a subtle/borderless circle, NO solid
+ * fill). We render those as a BARE `` to match exactly — none of
+ * the secret-scanning gray events is a solid-gray badge, so (unlike the Issues
+ * "not planned" variant) the `--timelineBadge-bgColor` hook is not used here.
+ *
+ * ACCESSIBILITY NOTE: none of the secret-scanning events render an in-text
+ * `` — actors and resolution reasons are BOLD TEXT (the live
+ * `UserComponent` uses a `text-bold` span, not a profile link, and reasons are
+ * bolded plain text). So the axe `link-in-text-block` rule (which failed the
+ * Dependabot CI for underline-less in-text links) is never exercised by this
+ * surface. Any future in-text link added here must use `inline`/bold.
*/
+const MONALISA_AVATAR = 'https://avatars.githubusercontent.com/u/583231?v=4'
+const SIX7_AVATAR = 'https://avatars.githubusercontent.com/six7'
+const HUBOT_AVATAR = 'https://avatars.githubusercontent.com/hubot'
+
/**
* System "GitHub" actor — live `TimelineItemBody` (`AlertTimeline.tsx`) in
* `isGitHubActor` mode renders ``). Note the login is bold TEXT, not a link (hovercard attrs only),
+ * so there is no in-text-link a11y concern. Used by every non-system event.
+ */
+const UserActor = ({login = 'monalisa', src = MONALISA_AVATAR}: {login?: string; src?: string}) => (
+ <>
+
+ {login}
+ >
+)
+
// Muted relative timestamp. The live secret-scanning `TimelineItemBody` renders
// a plain `RelativeTime` with no link wrapper — muted text only (matching the
// Dependabot timeline, unlike the Issues `Ago` deep-link).
@@ -80,6 +125,21 @@ const Time = ({date}: {date: string}) => (
)
+/**
+ * Optional comment sub-row — live `TimelineItemBody` renders a
+ * `
{comment}
` below the body whenever any of
+ * `resolution.comment` / `exemption_request.requester_comment` /
+ * `exemption_response.reviewer_comment` is present. Shared by the Resolution
+ * closures and the delegated-closure request/approve/deny events.
+ */
+const CommentSubRow = ({children}: {children: React.ReactNode}) => (
+
)
+
+/**
+ * The Resolution event group — `SecretScanTimeline.eventResolution` (audit § 2).
+ *
+ * Source: `case TimelineEventType.Resolution` in `AlertTimeline.tsx`. Two
+ * shapes share this event type:
+ * - REOPENED (`resolution.type === 'reopened'`): `SyncIcon` on `success`
+ * (green), copy "reopened this". The live code renders a `Timeline.Break`
+ * immediately BEFORE this item (sibling-selector CSS) — reproduced here.
+ * - CLOSED (any other `resolution.type`): copy "closed this as {reason}", where
+ * `{reason}` is `resolutionText(resolution.type)` rendered as BOLD TEXT (not a
+ * link). Only `revoked` gets the special `ShieldCheckIcon` on `done` (purple);
+ * every other reason uses `ShieldSlashIcon` on the default (gray) badge.
+ *
+ * The seven closed reasons are exactly the `resolutionText()` outputs (live
+ * `helper.ts`): revoked, false positive, won't fix, used in tests, pattern
+ * deleted, pattern edited, ignored by configuration. Each may carry an optional
+ * `(comment)` sub-row from `resolution.comment` — demonstrated on the revoked
+ * variant. Actor is always the user.
+ */
+export const EventResolution = () => (
+
+ {/* Closed as revoked — the only reason with the purple `done` / ShieldCheck
+ badge. Shown WITH an optional resolution-comment sub-row. */}
+
+
Closed as revoked
+
+
+
+
+
+
+
+ {'closed this as '}
+ revoked
+ Rotated the leaked token and confirmed the provider revoked it.
+
+
+
+
+
+ {/* Closed as false positive — gray (default) ShieldSlash badge. */}
+
+
Closed as false positive
+
+
+
+
+
+
+
+ {'closed this as '}
+ false positive
+
+
+
+
+
+ {/* Closed as won't fix */}
+
+
Closed as won't fix
+
+
+
+
+
+
+
+ {'closed this as '}
+ won't fix
+
+
+
+
+
+ {/* Closed as used in tests */}
+
+
Closed as used in tests
+
+
+
+
+
+
+
+ {'closed this as '}
+ used in tests
+
+
+
+
+
+ {/* Closed as pattern deleted */}
+
+
Closed as pattern deleted
+
+
+
+
+
+
+
+ {'closed this as '}
+ pattern deleted
+
+
+
+
+
+ {/* Closed as pattern edited */}
+
+
Closed as pattern edited
+
+
+
+
+
+
+
+ {'closed this as '}
+ pattern edited
+
+
+
+
+
+ {/* Closed as ignored by configuration (resolution type `hidden_by_config`) */}
+
+
Closed as ignored by configuration
+
+
+
+
+
+
+
+ {'closed this as '}
+ ignored by configuration
+
+
+
+
+
+ {/* Reopened — SyncIcon on success (green), preceded by a Timeline.Break.
+ The live code emits the Break as a sibling immediately BEFORE the
+ reopened Item so the sibling-selector CSS applies. */}
+
+
Reopened
+
+
+
+
+
+
+
+
+ {'reopened this '}
+
+
+
+
+
+
+)
+
+/**
+ * The Push-protection / bypass event group —
+ * `SecretScanTimeline.eventPushProtection` (audit § 3).
+ *
+ * Source: `case TimelineEventType.Bypass`,
+ * `DelegatedBypassRequestOpened`, `DelegatedBypassRequestApproved` in
+ * `AlertTimeline.tsx`. All three use the user actor and the default (gray)
+ * badge; only the icon + copy differ. The two delegated variants render only
+ * for repos with delegated bypass enabled (backend-gated org feature).
+ */
+export const EventBypass = () => (
+
+)
+
+/**
+ * The Validity-change event group — `SecretScanTimeline.eventValidity`
+ * (audit § 4).
+ *
+ * Source: `ValidityChangeTimelineEvent` in `AlertTimeline.tsx`. The validity
+ * bucket drives the badge + icon: active -> `AlertIcon` on `danger` (red);
+ * inactive -> `SkipIcon` on default (gray); unknown -> `AlertIcon` on
+ * `attention` (amber). Whether the change is AUTOMATED (no `event.actor` ->
+ * GitHub system actor + "verified this secret is …" / "is unable to determine
+ * …") or MANUAL (`event.actor` present -> user actor + "set validity to …") is
+ * decided purely by `!event.actor`. Both forms are shown per bucket.
+ */
+export const EventValidityChange = () => (
+
+ {/* Active — automated (GitHub), AlertIcon on danger (red) */}
+
+
Validity: active (automated)
+
+
+
+
+
+
+
+ {'verified this secret is active '}
+
+
+
+
+
+
+ {/* Active — manual (user), same danger badge */}
+
+
+)
+
+/**
+ * The Report event group — `SecretScanTimeline.eventReport` (audit § 5).
+ *
+ * Source: `case TimelineEventType.Report` in `AlertTimeline.tsx`. Badge:
+ * `ShieldCheckIcon` on the default (gray) badge. Copy: "reported this secret".
+ * The case does NOT pass `isGitHubActor`, so the actor is the USER (not the
+ * GitHub system actor) — confirmed against the live switch.
+ */
+export const EventReport = () => (
+
+ {/* Reported — ShieldCheckIcon, default (gray) badge, user actor */}
+
+
+)
+
+/**
+ * The delegated-closure (dismissal) event group —
+ * `SecretScanTimeline.eventClosureRequest` (audit § 6).
+ *
+ * Source: `case TimelineEventType.DelegatedClosureRequestOpened` /
+ * `…Approved` / `…Rejected` / `…Cancelled` in `AlertTimeline.tsx`. This is the
+ * org-level delegated-dismissal feature (backend-gated). All four use the user
+ * actor and the default (gray) badge; icon + copy differ:
+ * - Opened -> `CommentIcon`, "requested to dismiss this[ as {reason}]"
+ * - Approved -> `CheckCircleIcon`, "approved dismissal"
+ * - Rejected -> `XIcon`, "denied dismissal"
+ * - Cancelled -> `SkipIcon`, "cancelled request to dismiss"
+ *
+ * RIGHT CONTROLS (`Timeline.Actions`): the Opened event shows, while the request
+ * is pending & not expired, EITHER a small primary "Review request" button
+ * (shown to reviewers via the `show_closure_request_review_buttons` payload
+ * flag, which opens `ClosureRequestReviewButtons`' review dialog) OR a small
+ * invisible "Cancel request" button (shown to the requester via
+ * `show_closure_request_cancel_button`). They are driven by two independent,
+ * viewer-specific payload flags — mutually exclusive per viewer — so both
+ * variants are shown below. The optional `(comment)` sub-row carries the
+ * requester comment (Opened) or reviewer comment (Approved / Denied).
+ */
+export const EventClosureRequest = () => (
+
+ {/* Requested — with the reviewer-facing "Review request" primary button and
+ a requester comment sub-row. The `[ as {reason}]` clause is the
+ un-bolded `resolutionText(exemption_request.reason)`. */}
+
+
Dismissal requested (reviewer view)
+
+
+
+
+
+
+
+ {'requested to dismiss this as false positive '}
+
+ This token only ever pointed at our throwaway sandbox.
+
+
+
+
+
+
+
+
+ {/* Requested — requester view: the invisible "Cancel request" button. */}
+
+
Dismissal requested (requester view)
+
+
+
+
+
+
+
+ {'requested to dismiss this as false positive '}
+
+
+
+
+
+
+
+
+
+ {/* Approved — CheckCircleIcon, with a reviewer comment sub-row. */}
+
+
+)
+
+/**
+ * The Assignment event group — `SecretScanTimeline.eventAssignments`
+ * (audit § 7).
+ *
+ * Source: `AssignmentChangeTimelineEvent` in `AlertTimeline.tsx`. Badge:
+ * `PersonIcon` on the default (gray) badge. Unlike the avatar-less PR/Issue
+ * assignment pattern, the actor AND the assignee/unassignee are each rendered
+ * with an avatar via `UserComponent` (matching Dependabot). The five shapes are
+ * derived from which of `assigned_user` / `unassigned_user` is present and
+ * whether the actor equals the (un)assignee (self vs other).
+ */
+export const EventAssignment = () => (
+
+)
From 2a80e64690785942722597553cf17696ee8cd662 Mon Sep 17 00:00:00 2001
From: Jan Maarten <83665577+janmaarten-a11y@users.noreply.github.com>
Date: Sun, 28 Jun 2026 15:04:08 -0700
Subject: [PATCH 3/3] Address Copilot review on Secret Scanning timeline
stories
- Render the Resolution "Reopened" variant with the preceding closed Item so
Timeline.Break sits between two items (mirrors live break-between-events
placement; avoids a stray first-child break).
- Use the u/{id}?v=4 avatar URL form for the demo assignee avatars, matching
the convention used elsewhere in the Timeline stories.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
...eline.secret-scanning.features.stories.tsx | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.tsx b/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.tsx
index ee842603ca2..cbe5952617d 100644
--- a/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.tsx
+++ b/packages/react/src/Timeline/Timeline.secret-scanning.features.stories.tsx
@@ -87,8 +87,8 @@ import classes from './Timeline.secret-scanning.features.stories.module.css'
*/
const MONALISA_AVATAR = 'https://avatars.githubusercontent.com/u/583231?v=4'
-const SIX7_AVATAR = 'https://avatars.githubusercontent.com/six7'
-const HUBOT_AVATAR = 'https://avatars.githubusercontent.com/hubot'
+const SIX7_AVATAR = 'https://avatars.githubusercontent.com/u/4548309?v=4'
+const HUBOT_AVATAR = 'https://avatars.githubusercontent.com/u/480938?v=4'
/**
* System "GitHub" actor — live `TimelineItemBody` (`AlertTimeline.tsx`) in
@@ -340,10 +340,23 @@ export const EventResolution = () => (
{/* Reopened — SyncIcon on success (green), preceded by a Timeline.Break.
The live code emits the Break as a sibling immediately BEFORE the
- reopened Item so the sibling-selector CSS applies. */}
+ reopened Item so the sibling-selector CSS applies. We include the
+ preceding (closed) Item here so the Break renders BETWEEN two items, as
+ it does in product — mirroring the live "break between events"
+ placement rather than leaving the Break as a stray first child. */}
Reopened
+
+
+
+
+
+
+ {'closed this as '}
+ false positive
+
+