diff --git a/packages/react/src/Timeline/Timeline.code-scanning.features.stories.tsx b/packages/react/src/Timeline/Timeline.code-scanning.features.stories.tsx
index 9e03994be10..1edbc345fb2 100644
--- a/packages/react/src/Timeline/Timeline.code-scanning.features.stories.tsx
+++ b/packages/react/src/Timeline/Timeline.code-scanning.features.stories.tsx
@@ -26,6 +26,7 @@ import {
UserActor,
VariantSection,
} from './internal/timelineStoryHelpers'
+import {CODE_SCANNING_TAXONOMY, actorTypeForLogin, toEventDataAttributes, type CodeScanningEventType} from './taxonomy'
import classes from './Timeline.code-scanning.features.stories.module.css'
/**
@@ -172,6 +173,26 @@ const ConfigPill = ({category}: {category: string}) => (
*/
const SubRow = ({children}: {children: React.ReactNode}) =>
{children}
+/**
+ * Per-row taxonomy `data-*` attributes (Phase 3 tagging, github/primer#6664,
+ * epic #6654). Projects a Code Scanning leaf event type through the shared
+ * `toEventDataAttributes` serializer from the taxonomy module (primer/react#8180),
+ * mirroring the License Compliance pilot (primer/react#8216). `category` and
+ * `visibility` are derived FROM `CODE_SCANNING_TAXONOMY` so the story never
+ * hand-maintains them. Pass the row's rendered `UserActor` login for USER events
+ * (resolves `data-actor-type` via `actorTypeForLogin`); omit it for SYSTEM rows
+ * so `data-actor-type` is left off entirely. Spread the result onto each
+ * `Timeline.Item`.
+ */
+const codeScanningAttrs = (type: CodeScanningEventType, login?: string) =>
+ toEventDataAttributes({
+ scope: 'code-scanning',
+ type,
+ category: CODE_SCANNING_TAXONOMY[type].category,
+ visibility: CODE_SCANNING_TAXONOMY[type].visibility,
+ actorType: login ? actorTypeForLogin(login) : undefined,
+ })
+
export default {
title: 'Components/Timeline/Events/Code Scanning',
component: Timeline,
@@ -219,7 +240,7 @@ export const EventDetected = () => (
sub-row, and a right-aligned tool-version Label (Timeline.Actions). */}
-
+
@@ -253,7 +274,7 @@ export const EventDetected = () => (
card — `show_timeline_commit?` is false for this event). */}
-
+
@@ -283,7 +304,7 @@ export const EventDetected = () => (
{category}" pill (rendered when the alert has more than one category). */}
-
+
@@ -314,13 +335,20 @@ export const EventDetected = () => (
* Config-deleted renders the analysis category as an inline subtle mono pill
* (`MonoPill`); when the category is empty the live ERB substitutes "API
* Upload".
+ *
+ * TAXONOMY (github/primer#6664): the two "Fixed in branch" rows are the
+ * ALERT_CLOSED_BECAME_FIXED wire event and carry `data-event-type="fixed"`. The
+ * two "Configuration deleted" rows are visually grouped under Fixed here, but the
+ * taxonomy catalog folds their ALERT_CLOSED_BECAME_OUTDATED wire event into
+ * `closed` (a SYSTEM closure), so those rows carry `data-event-type="closed"`.
+ * Both are system events with no actor, so neither emits `data-actor-type`.
*/
export const EventFixed = () => (
{/* Fixed — selected ref → SOLID purple shield-check */}
-
+
@@ -335,7 +363,7 @@ export const EventFixed = () => (
{/* Fixed — non-selected ref → bare default check */}
-
+
@@ -348,9 +376,12 @@ export const EventFixed = () => (
{/* Config deleted — selected ref → SOLID purple shield-check */}
+ {/* Config-deletion is the ALERT_CLOSED_BECAME_OUTDATED wire event, which the
+ taxonomy folds into `closed` (system closure), not `fixed` — so this row
+ carries data-event-type="closed" despite its visual "Fixed" grouping. */}
-
+
@@ -366,7 +397,7 @@ export const EventFixed = () => (
{/* Config deleted — non-selected ref → bare default check */}
-
+
@@ -398,7 +429,7 @@ export const EventClosedByUser = () => (
{/* Closed as false positive — with a resolution-note sub-row */}
-
+
@@ -415,7 +446,7 @@ export const EventClosedByUser = () => (
{/* Closed as used in tests */}
-
+
@@ -431,7 +462,7 @@ export const EventClosedByUser = () => (
{/* Closed as won't fix */}
-
+
@@ -448,7 +479,7 @@ export const EventClosedByUser = () => (
`resolution == :NO_RESOLUTION`. */}
-
+
@@ -475,7 +506,7 @@ export const EventReopened = () => (
-
+
@@ -510,7 +541,7 @@ export const EventDismissalRequested = () => (
-
+
@@ -550,7 +581,7 @@ export const EventDismissalReviewed = () => (
{/* Approved — check icon + reviewer-comment sub-row */}
-
+
@@ -567,7 +598,7 @@ export const EventDismissalReviewed = () => (
{/* Denied — x icon */}
-
+
diff --git a/packages/react/src/Timeline/Timeline.dependabot.features.stories.tsx b/packages/react/src/Timeline/Timeline.dependabot.features.stories.tsx
index 7768c168dff..b756a162b32 100644
--- a/packages/react/src/Timeline/Timeline.dependabot.features.stories.tsx
+++ b/packages/react/src/Timeline/Timeline.dependabot.features.stories.tsx
@@ -29,6 +29,7 @@ import {
UserActor,
VariantSection,
} from './internal/timelineStoryHelpers'
+import {actorTypeForLogin, DEPENDABOT_TAXONOMY, toEventDataAttributes, type DependabotEventType} from './taxonomy'
/**
* Dependabot alert Timeline event examples (Phase 2 of github/primer#6663).
@@ -56,10 +57,26 @@ import {
* 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="opened"`) 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.
+ * TAXONOMY `data-*` CONTRACT: every cataloged `Timeline.Item` below carries the
+ * event `data-*` attributes projected from the merged taxonomy module
+ * (`./taxonomy`, primer/react#8180) — the single source of truth for Timeline
+ * event categorization (github/primer#6664, docs github/primer#6888). Each row
+ * spreads the output of `toEventDataAttributes` via the local `dependabotAttrs`
+ * helper, which derives `category` / `visibility` FROM the catalog entry
+ * (`DEPENDABOT_TAXONOMY`) so the stories stay in sync with the catalog, and
+ * resolves `data-actor-type` at runtime from each row's rendered actor login
+ * (`actorTypeForLogin`): Dependabot-authored rows resolve to `bot`, user-driven
+ * rows to `user`. The contract per rendered ``: `data-event-scope`,
+ * `data-event-type` (the UNSCOPED leaf), `data-event-category`,
+ * `data-event-visibility` (defaults `primary`), and `data-actor-type`. This
+ * applies the same contract proven by the License Compliance pilot
+ * (primer/react#8216).
+ *
+ * SHARED / PARKED EVENTS (left untagged): the Assignment and Copilot-work groups
+ * are cross-surface SHARED events, deliberately kept OUT of the per-surface
+ * Dependabot catalog (github/primer#6888), so they intentionally carry NO
+ * `data-*` attributes. See the code comments above `EventAssignment` and
+ * `EventCopilotWork`.
*
* SLOT USAGE (Phase 1 slots — same convention as the Issues group):
* - `Timeline.Avatar` (gutter slot, #6677): the 40px LEFT-GUTTER avatar.
@@ -120,6 +137,25 @@ const PushPill = ({sha}: {sha: string}) => (
)
+/**
+ * Local projection of the taxonomy `data-*` contract for this surface. Given a
+ * Dependabot leaf `type` (and, when the row renders an actor, that actor's
+ * `login`), it returns the `data-*` attribute set to spread on the
+ * `Timeline.Item`. `category` and `visibility` come FROM the catalog entry so
+ * the stories track `DEPENDABOT_TAXONOMY`; `data-actor-type` is resolved at
+ * runtime from the login (Dependabot-authored rows pass a `…[bot]` login so they
+ * resolve to `bot`; user-driven rows pass the rendered user login). See
+ * github/primer#6664 and the taxonomy docs (github/primer#6888).
+ */
+const dependabotAttrs = (type: DependabotEventType, login?: string) =>
+ toEventDataAttributes({
+ scope: 'dependabot',
+ type,
+ category: DEPENDABOT_TAXONOMY[type].category,
+ visibility: DEPENDABOT_TAXONOMY[type].visibility,
+ actorType: login ? actorTypeForLogin(login) : undefined,
+ })
+
export default {
title: 'Components/Timeline/Events/Dependabot',
component: Timeline,
@@ -160,7 +196,7 @@ export const EventOpened = () => (
{/* Opened — no source */}
-
+
@@ -176,7 +212,7 @@ export const EventOpened = () => (
{/* OpenedFromPR — bold `#123` pull-request link (scheme: primary, bold) */}
-
+
@@ -192,7 +228,7 @@ export const EventOpened = () => (
{/* OpenedFromPush — blue push-pill with the 7-char `after` SHA */}
-
+
@@ -225,7 +261,7 @@ export const EventFixed = () => (
{/* Fixed — no source */}
-
+
@@ -241,7 +277,7 @@ export const EventFixed = () => (
{/* FixedViaPR — bold `#123` pull-request link */}
-
+
@@ -257,7 +293,7 @@ export const EventFixed = () => (
{/* FixedViaPush — blue push-pill */}
-
+
@@ -290,7 +326,7 @@ export const EventDismissed = () => (
{/* Manual — risk is tolerable (with an optional dismissal note) */}
-
+
@@ -309,7 +345,7 @@ export const EventDismissed = () => (
{/* Manual — fix started */}
-
+
@@ -325,7 +361,7 @@ export const EventDismissed = () => (
{/* Manual — no bandwidth to fix this */}
-
+
@@ -341,7 +377,7 @@ export const EventDismissed = () => (
{/* Manual — vulnerable code is not actually used */}
-
+
@@ -357,7 +393,7 @@ export const EventDismissed = () => (
{/* Manual — inaccurate */}
-
+
@@ -373,7 +409,7 @@ export const EventDismissed = () => (
{/* Auto — rule-based, no source (with optional rule comment) */}
-
+
@@ -396,7 +432,7 @@ export const EventDismissed = () => (
{/* Auto — from a pull request */}
-
+
@@ -412,7 +448,7 @@ export const EventDismissed = () => (
{/* Auto — from a push */}
-
+
@@ -441,7 +477,7 @@ export const EventReopened = () => (
{/* Manual reopen — user actor */}
-
+
@@ -457,7 +493,7 @@ export const EventReopened = () => (
{/* Reintroduced — no source */}
-
+
@@ -473,7 +509,7 @@ export const EventReopened = () => (
{/* Reintroduced — from a pull request */}
-
+
@@ -489,7 +525,7 @@ export const EventReopened = () => (
{/* Reintroduced — from a push */}
-
+
@@ -505,7 +541,7 @@ export const EventReopened = () => (
{/* Auto-reopened — rule change (with optional rule comment) */}
-
+
@@ -560,7 +596,7 @@ export const EventDismissalRequest = () => (
place that right-aligned control in the `Timeline.Actions` slot. */}
-
+
@@ -584,7 +620,7 @@ export const EventDismissalRequest = () => (
{/* Dismissal approved — circle user actor, success/check badge */}
-
+
@@ -601,7 +637,7 @@ export const EventDismissalRequest = () => (
{/* Dismissal denied — circle user actor, danger/x badge */}
-
+
@@ -619,7 +655,7 @@ export const EventDismissalRequest = () => (
"x")` (no bg) → a bare default badge, not a danger/emphasis one. */}
-
+
@@ -651,6 +687,12 @@ export const EventDismissalRequest = () => (
* The five variants mirror the Secret scanning assignment story: self-assign,
* assign another, self-unassign, unassign another, and a combined assign +
* unassign example.
+ *
+ * SHARED / PARKED — NO `data-*` TAXONOMY: assignment is a cross-surface SHARED
+ * event, deliberately kept OUT of the per-surface Dependabot catalog
+ * (`DEPENDABOT_TAXONOMY` has no assignment leaf), per the taxonomy docs
+ * (github/primer#6888). It has no per-surface taxonomy leaf yet, so these rows
+ * intentionally carry NO `data-*` attributes.
*/
export const EventAssignment = () => (
@@ -758,6 +800,12 @@ export const EventAssignment = () => (
* button above.
* - Work finished (`with_badge(icon: "repo-push")`): default (gray) badge, no
* right control.
+ *
+ * SHARED / PARKED — NO `data-*` TAXONOMY: Copilot-agent work is a cross-surface
+ * SHARED event, deliberately kept OUT of the per-surface Dependabot catalog
+ * (`DEPENDABOT_TAXONOMY` has no copilot leaf), per the taxonomy docs
+ * (github/primer#6888). It has no per-surface taxonomy leaf yet, so these rows
+ * intentionally carry NO `data-*` attributes.
*/
export const EventCopilotWork = () => (
diff --git a/packages/react/src/Timeline/Timeline.issues.features.stories.tsx b/packages/react/src/Timeline/Timeline.issues.features.stories.tsx
index 20324b0b6ff..82d1c27d627 100644
--- a/packages/react/src/Timeline/Timeline.issues.features.stories.tsx
+++ b/packages/react/src/Timeline/Timeline.issues.features.stories.tsx
@@ -40,6 +40,8 @@ import Octicon from '../Octicon'
import Token from '../Token'
import classes from './Timeline.issues.features.stories.module.css'
import {BoldLink, Examples, MutedTime, UserActor, VariantSection} from './internal/timelineStoryHelpers'
+import {actorTypeForLogin, ISSUE_TAXONOMY, toEventDataAttributes} from './taxonomy'
+import type {IssueEventType} from './taxonomy'
/**
* Issue Timeline event examples (Phase 2 of github/primer#6663).
@@ -104,6 +106,23 @@ export default {
],
} as Meta>
+/**
+ * Serialize the taxonomy `data-*` contract (github/primer#6664) for one Issues
+ * event row. `type` is an ISSUE_TAXONOMY leaf; `category` + `visibility` are read
+ * from the catalog, so metadata leaves (labels, assignees, milestones, project
+ * fields, issue types, rename) pick up `visibility: 'auditOnly'` automatically.
+ * Pass the row's rendered actor login so `data-actor-type` resolves (every Issues
+ * event carries an actor).
+ */
+const issueAttrs = (type: IssueEventType, login?: string) =>
+ toEventDataAttributes({
+ scope: 'issue',
+ type,
+ category: ISSUE_TAXONOMY[type].category,
+ visibility: ISSUE_TAXONOMY[type].visibility,
+ actorType: login ? actorTypeForLogin(login) : undefined,
+ })
+
/**
* The Closed event group — `IssueTimeline.eventClosed` (audit § 2).
*
@@ -118,7 +137,7 @@ export const EventClosed = () => (
{/* Closed as completed */}
-
+
@@ -139,7 +158,7 @@ export const EventClosed = () => (
(portable for docs copy-paste; matches production `TimelineRow`). */}
-
+
(
{/* Closed via pull request */}
-
+
@@ -185,7 +204,7 @@ export const EventClosed = () => (
{/* Closed via commit */}
-
+
@@ -210,7 +229,7 @@ export const EventClosed = () => (
equivalent for github-ui's `ProjectV2` closer link. */}
-
+
@@ -233,7 +252,7 @@ export const EventClosed = () => (
composed here from Primer primitives. */}
-
+
(
{/* Closed with no state reason */}
-
+
@@ -293,7 +312,7 @@ export const EventState = () => (
{/* Reopened — open (green) badge via useIssueState (ReopenedEvent.tsx) */}
-
+
@@ -310,7 +329,7 @@ export const EventState = () => (
plain inline Link (the audit shows it bold; live code is canonical). */}
-
+
@@ -329,7 +348,7 @@ export const EventState = () => (
{/* Pinned */}
-
+
@@ -345,7 +364,7 @@ export const EventState = () => (
{/* Unpinned */}
-
+
@@ -362,7 +381,7 @@ export const EventState = () => (
the resulting discussion by number. */}
-
+
@@ -378,6 +397,7 @@ export const EventState = () => (
{/* Converted from draft */}
+ {/* Untagged: a metadata/auditOnly event that ISSUE_TAXONOMY does not yet enumerate as a distinct leaf (candidate leaf to add in a follow-up, not a defect). Emitting a data-event-type without a real catalog leaf would break the taxonomy single source of truth. */}
@@ -407,7 +427,7 @@ export const EventReferences = () => (
open PR state icon inline before the PR title). */}
-
+
@@ -431,7 +451,7 @@ export const EventReferences = () => (
icon is behind the live code here. */}
-
+
@@ -451,7 +471,7 @@ export const EventReferences = () => (
inline (showAgoTimestamp={false}) and the commit card is sub-content. */}
-
+
@@ -482,7 +502,7 @@ export const EventReferences = () => (
{/* Multiple commit references — same event, pluralized copy + N cards. */}
-
+
@@ -538,7 +558,7 @@ export const EventDuplicates = () => (
IssueLink uses the open (green) state icon. */}
-
+
@@ -562,6 +582,7 @@ export const EventDuplicates = () => (
{/* Marked as a duplicate of this issue — no right controls. */}
+ {/* Untagged: a metadata/auditOnly event that ISSUE_TAXONOMY does not yet enumerate as a distinct leaf (the catalog has only 'marked_as_duplicate'); candidate leaf to add in a follow-up, not a defect. */}
@@ -584,6 +605,7 @@ export const EventDuplicates = () => (
{/* Unmarked this as a duplicate of . */}
+ {/* Untagged: a metadata/auditOnly event that ISSUE_TAXONOMY does not yet enumerate as a distinct leaf (the catalog has only 'marked_as_duplicate'); candidate leaf to add in a follow-up, not a defect. */}
@@ -605,6 +627,7 @@ export const EventDuplicates = () => (
{/* Unmarked as a duplicate of this issue. */}
+ {/* Untagged: a metadata/auditOnly event that ISSUE_TAXONOMY does not yet enumerate as a distinct leaf (the catalog has only 'marked_as_duplicate'); candidate leaf to add in a follow-up, not a defect. */}
@@ -638,7 +661,7 @@ export const EventModeration = () => (
{/* User blocked (permanent) */}
-
+
@@ -654,7 +677,7 @@ export const EventModeration = () => (
{/* User temporarily blocked */}
-
+