@usefragments/ui 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/assets/ui.css +327 -274
  2. package/dist/components/Checkbox/Checkbox.module.scss.cjs +11 -11
  3. package/dist/components/Checkbox/Checkbox.module.scss.js +11 -11
  4. package/dist/components/Chip/Chip.module.scss.cjs +15 -15
  5. package/dist/components/Chip/Chip.module.scss.js +15 -15
  6. package/dist/components/Combobox/index.cjs +106 -9
  7. package/dist/components/Combobox/index.d.ts.map +1 -1
  8. package/dist/components/Combobox/index.js +106 -9
  9. package/dist/components/DataTable/DataTable.module.scss.cjs +37 -34
  10. package/dist/components/DataTable/DataTable.module.scss.js +37 -34
  11. package/dist/components/Main/Main.module.scss.cjs +26 -0
  12. package/dist/components/Main/Main.module.scss.js +26 -0
  13. package/dist/components/Main/index.cjs +128 -0
  14. package/dist/components/Main/index.d.ts +22 -0
  15. package/dist/components/Main/index.d.ts.map +1 -0
  16. package/dist/components/Main/index.js +111 -0
  17. package/dist/components/Table/Table.module.scss.cjs +19 -19
  18. package/dist/components/Table/Table.module.scss.js +19 -19
  19. package/dist/components/TableOfContents/TableOfContents.module.scss.cjs +15 -15
  20. package/dist/components/TableOfContents/TableOfContents.module.scss.js +15 -15
  21. package/dist/components/TableOfContents/index.cjs +22 -42
  22. package/dist/components/TableOfContents/index.d.ts +3 -3
  23. package/dist/components/TableOfContents/index.d.ts.map +1 -1
  24. package/dist/components/TableOfContents/index.js +22 -42
  25. package/dist/index.cjs +87 -85
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +2 -0
  29. package/fragments.json +1 -1
  30. package/package.json +7 -2
  31. package/src/components/Checkbox/Checkbox.contract.json +5 -24
  32. package/src/components/Checkbox/Checkbox.module.scss +4 -2
  33. package/src/components/Chip/Chip.contract.json +11 -28
  34. package/src/components/Chip/Chip.module.scss +21 -45
  35. package/src/components/Combobox/Combobox.contract.json +6 -24
  36. package/src/components/Combobox/index.tsx +132 -6
  37. package/src/components/DataTable/DataTable.module.scss +31 -8
  38. package/src/components/Main/Main.contract.json +79 -0
  39. package/src/components/Main/Main.module.scss +31 -0
  40. package/src/components/Main/index.tsx +124 -0
  41. package/src/components/Table/Table.module.scss +22 -5
  42. package/src/components/TableOfContents/TableOfContents.contract.json +6 -16
  43. package/src/components/TableOfContents/TableOfContents.module.scss +10 -13
  44. package/src/components/TableOfContents/index.tsx +27 -38
  45. package/src/index.ts +3 -0
  46. package/src/tokens/_component-properties.scss +1 -0
  47. package/dist/components/Cloud/DashboardLayoutPrototypes.shared.d.ts +0 -33
  48. package/dist/components/Cloud/DashboardLayoutPrototypes.shared.d.ts.map +0 -1
  49. package/dist/components/Cloud/DashboardLayoutPrototypes.shell.d.ts +0 -11
  50. package/dist/components/Cloud/DashboardLayoutPrototypes.shell.d.ts.map +0 -1
  51. package/dist/components/Cloud/PullRequestsPrototypes.content.d.ts +0 -5
  52. package/dist/components/Cloud/PullRequestsPrototypes.content.d.ts.map +0 -1
  53. package/dist/components/Cloud/PullRequestsPrototypes.detail-panels.d.ts +0 -4
  54. package/dist/components/Cloud/PullRequestsPrototypes.detail-panels.d.ts.map +0 -1
  55. package/dist/components/Cloud/PullRequestsPrototypes.detail.d.ts +0 -3
  56. package/dist/components/Cloud/PullRequestsPrototypes.detail.d.ts.map +0 -1
  57. package/dist/components/Cloud/PullRequestsPrototypes.id-data.d.ts +0 -86
  58. package/dist/components/Cloud/PullRequestsPrototypes.id-data.d.ts.map +0 -1
  59. package/dist/components/Cloud/PullRequestsPrototypes.id-page.d.ts +0 -2
  60. package/dist/components/Cloud/PullRequestsPrototypes.id-page.d.ts.map +0 -1
  61. package/src/components/Accordion/Accordion.stories.tsx +0 -106
  62. package/src/components/Alert/Alert.stories.tsx +0 -119
  63. package/src/components/AppShell/AppShell.stories.tsx +0 -156
  64. package/src/components/Avatar/Avatar.stories.tsx +0 -74
  65. package/src/components/Badge/Badge.stories.tsx +0 -92
  66. package/src/components/BentoGrid/BentoGrid.stories.tsx +0 -98
  67. package/src/components/Box/Box.stories.tsx +0 -150
  68. package/src/components/Breadcrumbs/Breadcrumbs.stories.tsx +0 -87
  69. package/src/components/Button/Button.stories.tsx +0 -103
  70. package/src/components/ButtonGroup/ButtonGroup.stories.tsx +0 -97
  71. package/src/components/Card/Card.stories.tsx +0 -144
  72. package/src/components/Chart/Chart.stories.tsx +0 -119
  73. package/src/components/Checkbox/Checkbox.stories.tsx +0 -68
  74. package/src/components/Chip/Chip.stories.tsx +0 -70
  75. package/src/components/Cloud/CloudGovernanceShell.module.scss +0 -88
  76. package/src/components/Cloud/CloudGovernanceShell.stories.tsx +0 -446
  77. package/src/components/Cloud/ControlSizing.module.scss +0 -49
  78. package/src/components/Cloud/ControlSizing.stories.tsx +0 -238
  79. package/src/components/Cloud/DashboardLayoutPrototypes.module.scss +0 -462
  80. package/src/components/Cloud/DashboardLayoutPrototypes.shared.tsx +0 -375
  81. package/src/components/Cloud/DashboardLayoutPrototypes.shell.tsx +0 -147
  82. package/src/components/Cloud/DashboardLayoutPrototypes.stories.tsx +0 -328
  83. package/src/components/Cloud/PullRequestsPrototypes.content.tsx +0 -283
  84. package/src/components/Cloud/PullRequestsPrototypes.detail-panels.tsx +0 -381
  85. package/src/components/Cloud/PullRequestsPrototypes.detail.tsx +0 -204
  86. package/src/components/Cloud/PullRequestsPrototypes.id-data.ts +0 -395
  87. package/src/components/Cloud/PullRequestsPrototypes.id-page.tsx +0 -349
  88. package/src/components/Cloud/PullRequestsPrototypes.stories.tsx +0 -81
  89. package/src/components/CodeBlock/CodeBlock.stories.tsx +0 -147
  90. package/src/components/Collapsible/Collapsible.stories.tsx +0 -94
  91. package/src/components/ColorPicker/ColorPicker.stories.tsx +0 -74
  92. package/src/components/Combobox/Combobox.stories.tsx +0 -133
  93. package/src/components/Command/Command.stories.tsx +0 -102
  94. package/src/components/ConversationList/ConversationList.stories.tsx +0 -93
  95. package/src/components/DataTable/DataTable.stories.tsx +0 -129
  96. package/src/components/DatePicker/DatePicker.stories.tsx +0 -129
  97. package/src/components/Dialog/Dialog.stories.tsx +0 -154
  98. package/src/components/Drawer/Drawer.stories.tsx +0 -144
  99. package/src/components/Editor/Editor.stories.tsx +0 -99
  100. package/src/components/EmptyState/EmptyState.stories.tsx +0 -97
  101. package/src/components/Field/Field.stories.tsx +0 -109
  102. package/src/components/Fieldset/Fieldset.stories.tsx +0 -116
  103. package/src/components/Form/Form.stories.tsx +0 -142
  104. package/src/components/Grid/Grid.stories.tsx +0 -125
  105. package/src/components/Header/Header.stories.tsx +0 -133
  106. package/src/components/Icon/Icon.stories.tsx +0 -80
  107. package/src/components/IconButton/IconButton.stories.tsx +0 -78
  108. package/src/components/Image/Image.stories.tsx +0 -109
  109. package/src/components/Input/Input.stories.tsx +0 -104
  110. package/src/components/Link/Link.stories.tsx +0 -67
  111. package/src/components/List/List.stories.tsx +0 -95
  112. package/src/components/Listbox/Listbox.stories.tsx +0 -81
  113. package/src/components/Loading/Loading.stories.tsx +0 -69
  114. package/src/components/Markdown/Markdown.stories.tsx +0 -75
  115. package/src/components/Menu/Menu.stories.tsx +0 -113
  116. package/src/components/Message/Message.stories.tsx +0 -85
  117. package/src/components/NavigationMenu/NavigationMenu.stories.tsx +0 -161
  118. package/src/components/Pagination/Pagination.stories.tsx +0 -81
  119. package/src/components/Popover/Popover.stories.tsx +0 -152
  120. package/src/components/Progress/Progress.stories.tsx +0 -64
  121. package/src/components/Prompt/Prompt.stories.tsx +0 -93
  122. package/src/components/RadioGroup/RadioGroup.stories.tsx +0 -107
  123. package/src/components/ScrollArea/ScrollArea.stories.tsx +0 -97
  124. package/src/components/Select/Select.stories.tsx +0 -127
  125. package/src/components/Separator/Separator.stories.tsx +0 -84
  126. package/src/components/Sidebar/Sidebar.stories.tsx +0 -122
  127. package/src/components/Skeleton/Skeleton.stories.tsx +0 -80
  128. package/src/components/Slider/Slider.stories.tsx +0 -73
  129. package/src/components/Stack/Stack.stories.tsx +0 -73
  130. package/src/components/Switch/Switch.stories.tsx +0 -59
  131. package/src/components/Table/Table.stories.tsx +0 -219
  132. package/src/components/TableOfContents/TableOfContents.stories.tsx +0 -120
  133. package/src/components/Tabs/Tabs.stories.tsx +0 -136
  134. package/src/components/Text/Text.stories.tsx +0 -133
  135. package/src/components/Textarea/Textarea.stories.tsx +0 -107
  136. package/src/components/Theme/Theme.stories.tsx +0 -95
  137. package/src/components/ThinkingIndicator/ThinkingIndicator.stories.tsx +0 -85
  138. package/src/components/Toast/Toast.stories.tsx +0 -91
  139. package/src/components/ToggleGroup/ToggleGroup.stories.tsx +0 -114
  140. package/src/components/Tooltip/Tooltip.stories.tsx +0 -95
  141. package/src/components/VisuallyHidden/VisuallyHidden.stories.tsx +0 -78
@@ -1,381 +0,0 @@
1
- import { Avatar } from "../Avatar";
2
- import { Badge } from "../Badge";
3
- import { Box } from "../Box";
4
- import { Button } from "../Button";
5
- import { Card } from "../Card";
6
- import { DataTable, type DataTableColumn } from "../DataTable";
7
- import { Select } from "../Select";
8
- import { Stack } from "../Stack";
9
- import { Text } from "../Text";
10
- import {
11
- blockingFindings,
12
- changedFiles,
13
- introducedFindings,
14
- prFindings,
15
- prIdPagePullRequest as pr,
16
- threadFindings,
17
- type ChangedFile,
18
- type PrFinding,
19
- } from "./PullRequestsPrototypes.id-data";
20
- import styles from "./DashboardLayoutPrototypes.module.scss";
21
-
22
- const legacyCount = prFindings.length - introducedFindings.length;
23
-
24
- function actionLabel(finding: PrFinding) {
25
- if (finding.fix === "Deterministic") return "Create fix";
26
- if (finding.severity === "Blocking") return "Request waiver";
27
- return "Mark intentional";
28
- }
29
-
30
- // ============================================================================
31
- // Findings tab — the table-first workbench
32
- // ============================================================================
33
-
34
- const findingColumns: DataTableColumn<PrFinding>[] = [
35
- {
36
- id: "severity",
37
- header: "Severity",
38
- size: 104,
39
- cell: (context) => {
40
- const finding = context.row.original as PrFinding;
41
- return (
42
- <Badge variant={finding.variant} size="sm">
43
- {finding.severity}
44
- </Badge>
45
- );
46
- },
47
- },
48
- {
49
- id: "finding",
50
- header: "Finding",
51
- minSize: 280,
52
- cell: (context) => {
53
- const finding = context.row.original as PrFinding;
54
- return (
55
- <Stack gap="none" className={styles.minWidthZero}>
56
- <Text size="sm" weight="medium" truncate>
57
- {finding.suggestion}
58
- </Text>
59
- <Text size="xs" color="tertiary" font="mono" truncate>
60
- {finding.code} · {finding.file}:{finding.line}
61
- </Text>
62
- </Stack>
63
- );
64
- },
65
- },
66
- { accessorKey: "rule", header: "Rule", size: 150, truncate: true },
67
- {
68
- id: "occurrences",
69
- header: "Sites",
70
- size: 72,
71
- align: "right",
72
- cell: (context) => (
73
- <Text size="xs" color="secondary" tabularNums>
74
- ×{(context.row.original as PrFinding).occurrences}
75
- </Text>
76
- ),
77
- },
78
- {
79
- id: "origin",
80
- header: "Origin",
81
- size: 104,
82
- cell: (context) => {
83
- const finding = context.row.original as PrFinding;
84
- return (
85
- <Text size="xs" color={finding.origin === "Introduced" ? "secondary" : "tertiary"}>
86
- {finding.origin}
87
- </Text>
88
- );
89
- },
90
- },
91
- {
92
- id: "fix",
93
- header: "Fix",
94
- size: 116,
95
- cell: (context) => (
96
- <Text size="xs" color="tertiary">
97
- {(context.row.original as PrFinding).fix}
98
- </Text>
99
- ),
100
- },
101
- {
102
- id: "actions",
103
- header: "",
104
- size: 132,
105
- align: "right",
106
- cell: (context) => (
107
- <Button variant="outlined" size="xs">
108
- {actionLabel(context.row.original as PrFinding)}
109
- </Button>
110
- ),
111
- },
112
- ];
113
-
114
- export function FindingsPanel() {
115
- return (
116
- <Stack gap="sm">
117
- <Box className={styles.prFindingsToolbar}>
118
- <Select aria-label="Group findings" defaultValue="rule" size="sm">
119
- <Select.Trigger />
120
- <Select.Content>
121
- <Select.Item value="rule">Group by rule</Select.Item>
122
- <Select.Item value="area">Group by area</Select.Item>
123
- <Select.Item value="severity">Group by severity</Select.Item>
124
- <Select.Item value="file">Group by file</Select.Item>
125
- </Select.Content>
126
- </Select>
127
- <Stack direction="row" gap="xs" align="center" wrap>
128
- <Button variant="secondary" size="sm">
129
- Create {pr.safeFixes} fixes
130
- </Button>
131
- <Button variant="ghost" size="sm">
132
- Mark intentional
133
- </Button>
134
- </Stack>
135
- </Box>
136
- <Stack direction="row" gap="xs" wrap>
137
- <Badge variant="dim" size="md" active>
138
- All {prFindings.length}
139
- </Badge>
140
- <Badge variant="dim" size="md">
141
- Blocking {blockingFindings.length}
142
- </Badge>
143
- <Badge variant="dim" size="md">
144
- Safe fixes {pr.safeFixes}
145
- </Badge>
146
- <Badge variant="dim" size="md">
147
- Introduced {introducedFindings.length}
148
- </Badge>
149
- <Badge variant="dim" size="md">
150
- Legacy {legacyCount}
151
- </Badge>
152
- </Stack>
153
- <DataTable
154
- columns={findingColumns}
155
- data={prFindings}
156
- getRowId={(row) => row.id}
157
- selectable
158
- showCheckbox
159
- density="condensed"
160
- bordered
161
- caption="Pull request findings"
162
- captionHidden
163
- wrapperClassName={styles.prTable}
164
- />
165
- <Text size="xs" color="tertiary">
166
- {prFindings.length} findings · {introducedFindings.length} introduced gate this PR ·{" "}
167
- {legacyCount} legacy shown for context
168
- </Text>
169
- </Stack>
170
- );
171
- }
172
-
173
- // ============================================================================
174
- // Conversation tab — the human-readable timeline on canonical Avatar + Card
175
- // ============================================================================
176
-
177
- export function ConversationPanel() {
178
- return (
179
- <Stack gap="md" className={styles.prIdTimeline}>
180
- <TimelineItem author={pr.author} meta="opened this pull request" avatarName={pr.author}>
181
- <Stack gap="sm">
182
- <Text size="sm">
183
- Refreshes the public launch cards for the June campaign. The branch changes hero CTA
184
- behavior, form danger states, and campaign-card spacing.
185
- </Text>
186
- <Box className={styles.prIdPrBodyTable}>
187
- <SummaryRow label="Area" value="Marketing / public launch" />
188
- <SummaryRow label="Designer" value="Growth design" />
189
- <SummaryRow label="Fragments gate" value="Blocked by contract" />
190
- </Box>
191
- </Stack>
192
- </TimelineItem>
193
-
194
- <TimelineItem
195
- author="fragments[bot]"
196
- meta="summarized the governance report"
197
- avatarName="Fragments bot"
198
- >
199
- <Stack gap="xs">
200
- <Text size="sm" weight="semibold">
201
- Fragments governance report
202
- </Text>
203
- <Text size="sm" color="secondary">
204
- {blockingFindings.length} blocking and {pr.safeFixes} safe-fix findings on a public
205
- marketing flow. Merge stays blocked until the blocking contract comments resolve — see
206
- Impacted areas for the area rollup.
207
- </Text>
208
- </Stack>
209
- </TimelineItem>
210
-
211
- {threadFindings.map((finding) => (
212
- <ReviewThread key={finding.id} finding={finding} />
213
- ))}
214
-
215
- <TimelineItem author="Noah Lee" meta="Growth design" avatarName="Noah Lee">
216
- <Text size="sm" color="secondary">
217
- I can approve the visual direction once the Button replacement and semantic danger token
218
- land. The spacing rhythm can be an intentional campaign exception.
219
- </Text>
220
- </TimelineItem>
221
- </Stack>
222
- );
223
- }
224
-
225
- function TimelineItem({
226
- author,
227
- meta,
228
- avatarName,
229
- children,
230
- }: {
231
- author: string;
232
- meta: string;
233
- avatarName: string;
234
- children: React.ReactNode;
235
- }) {
236
- return (
237
- <Box className={styles.prIdTimelineItem}>
238
- <Avatar name={avatarName} size="md" />
239
- <Card variant="panel" padding="none">
240
- <Card.Header divided>
241
- <Stack
242
- direction="row"
243
- gap="sm"
244
- align="baseline"
245
- justify="between"
246
- className={styles.full}
247
- >
248
- <Text size="sm" weight="semibold">
249
- {author}
250
- </Text>
251
- <Text size="xs" color="tertiary">
252
- {meta}
253
- </Text>
254
- </Stack>
255
- </Card.Header>
256
- <Card.Body padding="md">{children}</Card.Body>
257
- </Card>
258
- </Box>
259
- );
260
- }
261
-
262
- function ReviewThread({ finding }: { finding: PrFinding }) {
263
- return (
264
- <Box className={styles.prIdTimelineItem}>
265
- <Avatar name="Fragments bot" size="md" />
266
- <Card variant="panel" padding="none">
267
- <Card.Header divided>
268
- <Stack direction="row" gap="sm" align="center" justify="between" className={styles.full}>
269
- <Text size="xs" color="secondary" font="mono" truncate>
270
- {finding.file}:{finding.line}
271
- </Text>
272
- <Badge variant={finding.variant} size="sm">
273
- {finding.severity}
274
- </Badge>
275
- </Stack>
276
- </Card.Header>
277
- <Card.Body padding="md">
278
- <Stack gap="sm">
279
- <Text size="sm" color="secondary">
280
- {finding.body}
281
- </Text>
282
- {finding.designerSignal ? (
283
- <Text size="xs" color="tertiary">
284
- Designer signal: {finding.designerSignal}
285
- </Text>
286
- ) : null}
287
- <Stack direction="row" gap="sm" wrap>
288
- {finding.fix === "Deterministic" ? (
289
- <Button variant="outlined" size="xs">
290
- Create fix
291
- </Button>
292
- ) : null}
293
- <Button variant="ghost" size="xs">
294
- {finding.severity === "Blocking" ? "Request waiver" : "Mark intentional"}
295
- </Button>
296
- </Stack>
297
- </Stack>
298
- </Card.Body>
299
- </Card>
300
- </Box>
301
- );
302
- }
303
-
304
- function SummaryRow({ label, value }: { label: string; value: string }) {
305
- return (
306
- <Box className={styles.prIdSummaryRow}>
307
- <Text size="xs" color="tertiary">
308
- {label}
309
- </Text>
310
- <Text size="xs" color="secondary">
311
- {value}
312
- </Text>
313
- </Box>
314
- );
315
- }
316
-
317
- // ============================================================================
318
- // Changed files tab
319
- // ============================================================================
320
-
321
- const changedFileColumns: DataTableColumn<ChangedFile>[] = [
322
- {
323
- accessorKey: "path",
324
- header: "File",
325
- minSize: 320,
326
- truncate: true,
327
- cell: (context) => (
328
- <Text size="xs" font="mono" truncate>
329
- {(context.row.original as ChangedFile).path}
330
- </Text>
331
- ),
332
- },
333
- { accessorKey: "area", header: "Area", size: 120, truncate: true },
334
- {
335
- id: "delta",
336
- header: "Changes",
337
- size: 120,
338
- align: "right",
339
- cell: (context) => {
340
- const file = context.row.original as ChangedFile;
341
- return (
342
- <Text size="xs" color="secondary" tabularNums>
343
- +{file.additions} -{file.deletions}
344
- </Text>
345
- );
346
- },
347
- },
348
- {
349
- id: "findings",
350
- header: "Findings",
351
- size: 96,
352
- align: "right",
353
- cell: (context) => {
354
- const file = context.row.original as ChangedFile;
355
- return file.findings > 0 ? (
356
- <Badge variant="warning" size="sm">
357
- {file.findings}
358
- </Badge>
359
- ) : (
360
- <Text size="xs" color="tertiary">
361
- 0
362
- </Text>
363
- );
364
- },
365
- },
366
- ];
367
-
368
- export function ChangedFilesPanel() {
369
- return (
370
- <DataTable
371
- columns={changedFileColumns}
372
- data={changedFiles}
373
- getRowId={(row) => row.path}
374
- density="condensed"
375
- bordered
376
- caption="Changed files"
377
- captionHidden
378
- wrapperClassName={styles.prTable}
379
- />
380
- );
381
- }
@@ -1,204 +0,0 @@
1
- import { CheckCircle, GithubLogo, Warning, Wrench, XCircle } from "@phosphor-icons/react";
2
- import { Alert } from "../Alert";
3
- import { Box } from "../Box";
4
- import { Button } from "../Button";
5
- import { Card } from "../Card";
6
- import { Stack } from "../Stack";
7
- import { Tabs } from "../Tabs";
8
- import { Text } from "../Text";
9
- import {
10
- ChangedFilesPanel,
11
- ConversationPanel,
12
- FindingsPanel,
13
- } from "./PullRequestsPrototypes.detail-panels";
14
- import {
15
- blockingFindings,
16
- changedFiles,
17
- githubChecks,
18
- impactAreas,
19
- prFindings,
20
- prIdPagePullRequest as pr,
21
- reviewers,
22
- threadFindings,
23
- type GithubCheck,
24
- } from "./PullRequestsPrototypes.id-data";
25
- import styles from "./DashboardLayoutPrototypes.module.scss";
26
-
27
- const coverageDelta = pr.coverageAfter - pr.coverageBefore;
28
- // PR opened + governance report + designer reply wrap the inline review threads.
29
- const conversationCount = threadFindings.length + 3;
30
-
31
- // Gate-aware decision actions live in the shell header so the page title is the
32
- // real PR title — no second header, no generic "Pull Request" h1 above it.
33
- export function PullRequestDecisionActions() {
34
- return (
35
- <Stack direction="row" align="center" gap="sm" wrap>
36
- <Button variant="primary" size="sm">
37
- <Wrench size={14} weight="fill" />
38
- Create fix PR
39
- </Button>
40
- <Button variant="outlined" size="sm">
41
- Request changes
42
- </Button>
43
- <Button
44
- as="a"
45
- href="https://github.com/fragments-sdk/fragments/pull/248"
46
- target="_blank"
47
- rel="noopener noreferrer"
48
- variant="outlined"
49
- size="sm"
50
- >
51
- <GithubLogo size={14} weight="fill" />
52
- GitHub
53
- </Button>
54
- </Stack>
55
- );
56
- }
57
-
58
- export function PullRequestDetail() {
59
- return (
60
- <Stack gap="md">
61
- <GateBanner />
62
- <Box className={styles.prIdGithubLayout}>
63
- <Box className={styles.minWidthZero}>
64
- <Tabs defaultValue="findings" variant="pills">
65
- <Tabs.List>
66
- <Tabs.Tab value="findings">Findings {prFindings.length}</Tabs.Tab>
67
- <Tabs.Tab value="conversation">Conversation {conversationCount}</Tabs.Tab>
68
- <Tabs.Tab value="files">Changed files {changedFiles.length}</Tabs.Tab>
69
- </Tabs.List>
70
- <Tabs.Panel value="findings" flush>
71
- <FindingsPanel />
72
- </Tabs.Panel>
73
- <Tabs.Panel value="conversation" flush>
74
- <ConversationPanel />
75
- </Tabs.Panel>
76
- <Tabs.Panel value="files" flush>
77
- <ChangedFilesPanel />
78
- </Tabs.Panel>
79
- </Tabs>
80
- </Box>
81
- <DetailRail />
82
- </Box>
83
- </Stack>
84
- );
85
- }
86
-
87
- function GateBanner() {
88
- return (
89
- <Alert severity="error">
90
- <Alert.Icon />
91
- <Alert.Body>
92
- <Alert.Title>Merge blocked · fragments/contract-compliance</Alert.Title>
93
- <Alert.Content>
94
- {blockingFindings.length} blocking design-contract comments must be resolved before this
95
- PR can merge. Marketing contract coverage fell {pr.coverageBefore}% to {pr.coverageAfter}%
96
- ({coverageDelta} points), and {pr.safeFixes} of {prFindings.length} findings have
97
- deterministic fixes.
98
- </Alert.Content>
99
- </Alert.Body>
100
- </Alert>
101
- );
102
- }
103
-
104
- // ============================================================================
105
- // Sidebar status rail — every status fact has exactly one home here
106
- // ============================================================================
107
-
108
- function DetailRail() {
109
- return (
110
- <aside className={styles.prDetailRail} aria-label="Pull request status">
111
- <RailCard title="Merge gate">
112
- <Stack gap="sm">
113
- {githubChecks.map((check) => (
114
- <CheckRow key={check.name} check={check} />
115
- ))}
116
- </Stack>
117
- </RailCard>
118
- <RailCard title="Coverage">
119
- <Stack gap="xs">
120
- <RailRow label="Marketing coverage" value={`${pr.coverageAfter}%`} />
121
- <RailRow label="Coverage delta" value={`${coverageDelta} points`} variant="error" />
122
- <RailRow label="Safe fixes" value={String(pr.safeFixes)} />
123
- </Stack>
124
- </RailCard>
125
- <RailCard title="Impacted areas">
126
- <Stack gap="xs">
127
- {impactAreas.map((area) => (
128
- <RailRow key={area.area} label={area.area} value={area.review} variant={area.variant} />
129
- ))}
130
- </Stack>
131
- </RailCard>
132
- <RailCard title="Reviewers">
133
- <Stack gap="xs">
134
- {reviewers.map((reviewer) => (
135
- <RailRow
136
- key={reviewer.name}
137
- label={reviewer.name}
138
- value={reviewer.status}
139
- variant={reviewer.variant}
140
- />
141
- ))}
142
- </Stack>
143
- </RailCard>
144
- </aside>
145
- );
146
- }
147
-
148
- function RailCard({ title, children }: { title: string; children: React.ReactNode }) {
149
- return (
150
- <Card variant="panel" padding="none">
151
- <Card.Header divided>
152
- <Card.Title>{title}</Card.Title>
153
- </Card.Header>
154
- <Card.Body padding="md">{children}</Card.Body>
155
- </Card>
156
- );
157
- }
158
-
159
- function RailRow({
160
- label,
161
- value,
162
- variant,
163
- }: {
164
- label: string;
165
- value: string;
166
- variant?: GithubCheck["variant"];
167
- }) {
168
- return (
169
- <Box className={styles.prIdSidebarRow} data-variant={variant}>
170
- <Text size="xs" color="tertiary">
171
- {label}
172
- </Text>
173
- <Text size="xs" color="secondary">
174
- {value}
175
- </Text>
176
- </Box>
177
- );
178
- }
179
-
180
- function CheckRow({ check }: { check: GithubCheck }) {
181
- const Icon =
182
- check.state === "passed" ? CheckCircle : check.state === "failed" ? XCircle : Warning;
183
-
184
- return (
185
- <Box className={styles.prIdCheckSummary}>
186
- <span className={styles.prIdCheckIcon} data-state={check.state} aria-hidden>
187
- <Icon size={14} weight="fill" />
188
- </span>
189
- <Stack gap="none" className={styles.minWidthZero}>
190
- <Stack direction="row" gap="sm" align="baseline" justify="between" className={styles.full}>
191
- <Text size="xs" weight="medium" truncate>
192
- {check.name}
193
- </Text>
194
- <Text size="2xs" color="tertiary">
195
- {check.label}
196
- </Text>
197
- </Stack>
198
- <Text size="xs" color="tertiary" truncate>
199
- {check.summary}
200
- </Text>
201
- </Stack>
202
- </Box>
203
- );
204
- }