@gntik-ai/blocks 0.0.0-stage → 0.6.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 (397) hide show
  1. package/dist/index.d.ts +3626 -0
  2. package/dist/index.js +15417 -0
  3. package/dist/index.js.map +1 -0
  4. package/package.json +62 -3
  5. package/src/ai/AssistantCardGrid/AssistantCardGrid.test.tsx +32 -0
  6. package/src/ai/AssistantCardGrid/AssistantCardGrid.tsx +98 -0
  7. package/src/ai/AssistantCardGrid/block.meta.ts +10 -0
  8. package/src/ai/AssistantCardGrid/fixtures.ts +72 -0
  9. package/src/ai/EvaluationScorecard/EvaluationScorecard.test.tsx +39 -0
  10. package/src/ai/EvaluationScorecard/EvaluationScorecard.tsx +233 -0
  11. package/src/ai/EvaluationScorecard/block.meta.ts +10 -0
  12. package/src/ai/EvaluationScorecard/fixtures.ts +79 -0
  13. package/src/ai/ModelKeysList/ModelKeysList.test.tsx +44 -0
  14. package/src/ai/ModelKeysList/ModelKeysList.tsx +203 -0
  15. package/src/ai/ModelKeysList/block.meta.ts +10 -0
  16. package/src/ai/ModelKeysList/fixtures.ts +44 -0
  17. package/src/ai/PromptEditor/PromptEditor.test.tsx +97 -0
  18. package/src/ai/PromptEditor/PromptEditor.tsx +202 -0
  19. package/src/ai/PromptEditor/block.meta.ts +10 -0
  20. package/src/ai/PromptEditor/examples/controlled-variables.tsx +23 -0
  21. package/src/ai/PromptEditor/fixtures.ts +12 -0
  22. package/src/ai/TokenCostCard/TokenCostCard.test.tsx +36 -0
  23. package/src/ai/TokenCostCard/TokenCostCard.tsx +130 -0
  24. package/src/ai/TokenCostCard/block.meta.ts +9 -0
  25. package/src/ai/TokenCostCard/fixtures.ts +18 -0
  26. package/src/ai/TraceWaterfall/TraceWaterfall.test.tsx +119 -0
  27. package/src/ai/TraceWaterfall/TraceWaterfall.tsx +317 -0
  28. package/src/ai/TraceWaterfall/block.meta.ts +10 -0
  29. package/src/ai/TraceWaterfall/examples/no-details.tsx +6 -0
  30. package/src/ai/TraceWaterfall/examples/running.tsx +16 -0
  31. package/src/ai/TraceWaterfall/fixtures.ts +133 -0
  32. package/src/ai/TraceWaterfall/span-details.tsx +51 -0
  33. package/src/ai/index.ts +13 -0
  34. package/src/auth/ForgotPasswordForm/ForgotPasswordForm.test.tsx +209 -0
  35. package/src/auth/ForgotPasswordForm/ForgotPasswordForm.tsx +211 -0
  36. package/src/auth/ForgotPasswordForm/block.meta.ts +10 -0
  37. package/src/auth/ForgotPasswordForm/examples/UsernameOrEmail.tsx +24 -0
  38. package/src/auth/MfaChallenge/MfaChallenge.test.tsx +35 -0
  39. package/src/auth/MfaChallenge/MfaChallenge.tsx +170 -0
  40. package/src/auth/MfaChallenge/block.meta.ts +9 -0
  41. package/src/auth/ResetPasswordForm/ResetPasswordForm.test.tsx +27 -0
  42. package/src/auth/ResetPasswordForm/ResetPasswordForm.tsx +100 -0
  43. package/src/auth/ResetPasswordForm/block.meta.ts +10 -0
  44. package/src/auth/SignInForm/SignInForm.test.tsx +222 -0
  45. package/src/auth/SignInForm/SignInForm.tsx +226 -0
  46. package/src/auth/SignInForm/block.meta.ts +22 -0
  47. package/src/auth/SignInForm/examples/Username.tsx +26 -0
  48. package/src/auth/SignUpForm/SignUpForm.configured.test.tsx +316 -0
  49. package/src/auth/SignUpForm/SignUpForm.test.tsx +33 -0
  50. package/src/auth/SignUpForm/SignUpForm.tsx +298 -0
  51. package/src/auth/SignUpForm/block.meta.ts +22 -0
  52. package/src/auth/SignUpForm/examples/CustomFields.tsx +56 -0
  53. package/src/auth/SignUpForm/examples/Error.tsx +5 -0
  54. package/src/auth/SignUpForm/examples/Loading.tsx +6 -0
  55. package/src/auth/SignUpForm/examples/NoTerms.tsx +6 -0
  56. package/src/auth/SignUpForm/examples/Submitting.tsx +5 -0
  57. package/src/auth/SignUpForm/examples/Unavailable.tsx +15 -0
  58. package/src/auth/SsoButtons/SsoButtons.test.tsx +23 -0
  59. package/src/auth/SsoButtons/SsoButtons.tsx +52 -0
  60. package/src/auth/SsoButtons/block.meta.ts +9 -0
  61. package/src/auth/SsoButtons/fixtures.ts +15 -0
  62. package/src/auth/index.ts +16 -0
  63. package/src/auth/shared.tsx +147 -0
  64. package/src/billing/CostBreakdown/CostBreakdown.test.tsx +24 -0
  65. package/src/billing/CostBreakdown/CostBreakdown.tsx +95 -0
  66. package/src/billing/CostBreakdown/block.meta.ts +10 -0
  67. package/src/billing/CostBreakdown/fixtures.ts +9 -0
  68. package/src/billing/InvoiceTable/InvoiceTable.test.tsx +29 -0
  69. package/src/billing/InvoiceTable/InvoiceTable.tsx +130 -0
  70. package/src/billing/InvoiceTable/block.meta.ts +10 -0
  71. package/src/billing/InvoiceTable/fixtures.ts +38 -0
  72. package/src/billing/PaymentMethodCard/PaymentMethodCard.test.tsx +28 -0
  73. package/src/billing/PaymentMethodCard/PaymentMethodCard.tsx +103 -0
  74. package/src/billing/PaymentMethodCard/block.meta.ts +10 -0
  75. package/src/billing/PaymentMethodCard/fixtures.ts +12 -0
  76. package/src/billing/PlanCard/PlanCard.baseline.test.tsx +26 -0
  77. package/src/billing/PlanCard/PlanCard.summary.test.tsx +227 -0
  78. package/src/billing/PlanCard/PlanCard.test.tsx +27 -0
  79. package/src/billing/PlanCard/PlanCard.tsx +140 -0
  80. package/src/billing/PlanCard/PlanSummary.tsx +101 -0
  81. package/src/billing/PlanCard/README.md +23 -0
  82. package/src/billing/PlanCard/__snapshots__/PlanCard.baseline.test.tsx.snap +5 -0
  83. package/src/billing/PlanCard/block.meta.ts +60 -0
  84. package/src/billing/PlanCard/examples/SummaryActionsOnly.tsx +12 -0
  85. package/src/billing/PlanCard/examples/SummaryFeaturesOnly.tsx +14 -0
  86. package/src/billing/PlanCard/examples/SummaryFull.tsx +28 -0
  87. package/src/billing/PlanCard/examples/SummaryMinimal.tsx +5 -0
  88. package/src/billing/PlanCard/examples/SummaryPriceOnly.tsx +15 -0
  89. package/src/billing/PlanCard/examples/SummaryRenewalOnly.tsx +14 -0
  90. package/src/billing/PlanCard/examples/SummaryUnassigned.tsx +5 -0
  91. package/src/billing/PlanCard/examples/SummaryUnassignedNoLabel.tsx +5 -0
  92. package/src/billing/PlanCard/examples/SummaryZeroPrice.tsx +15 -0
  93. package/src/billing/PlanCard/fixtures.ts +19 -0
  94. package/src/billing/PlanDefinitionCard/PlanDefinitionCard.test.tsx +111 -0
  95. package/src/billing/PlanDefinitionCard/PlanDefinitionCard.tsx +86 -0
  96. package/src/billing/PlanDefinitionCard/README.md +29 -0
  97. package/src/billing/PlanDefinitionCard/block.meta.ts +46 -0
  98. package/src/billing/PlanDefinitionCard/examples/Compact.tsx +23 -0
  99. package/src/billing/PlanDefinitionCard/examples/Full.tsx +23 -0
  100. package/src/billing/PlanDefinitionCard/fixtures.ts +12 -0
  101. package/src/billing/QuotaMeters/QuotaMeters.test.tsx +23 -0
  102. package/src/billing/QuotaMeters/QuotaMeters.tsx +92 -0
  103. package/src/billing/QuotaMeters/block.meta.ts +10 -0
  104. package/src/billing/QuotaMeters/fixtures.ts +9 -0
  105. package/src/billing/SpendVsBudget/SpendVsBudget.test.tsx +24 -0
  106. package/src/billing/SpendVsBudget/SpendVsBudget.tsx +105 -0
  107. package/src/billing/SpendVsBudget/block.meta.ts +10 -0
  108. package/src/billing/SpendVsBudget/fixtures.ts +18 -0
  109. package/src/billing/format.ts +42 -0
  110. package/src/billing/index.ts +21 -0
  111. package/src/builders/CodePanel/CodePanel.test.tsx +280 -0
  112. package/src/builders/CodePanel/CodePanel.tsx +239 -0
  113. package/src/builders/CodePanel/block.meta.ts +10 -0
  114. package/src/builders/CodePanel/examples/controlled.tsx +15 -0
  115. package/src/builders/CodePanel/examples/diagnostics.tsx +40 -0
  116. package/src/builders/CodePanel/fixtures.ts +95 -0
  117. package/src/builders/CodePanel/marker-store.ts +90 -0
  118. package/src/builders/CodePanel/markers.ts +62 -0
  119. package/src/builders/FlowBuilder/FlowBuilder.test.tsx +480 -0
  120. package/src/builders/FlowBuilder/FlowBuilder.tsx +343 -0
  121. package/src/builders/FlowBuilder/block.meta.ts +20 -0
  122. package/src/builders/FlowBuilder/connections.ts +67 -0
  123. package/src/builders/FlowBuilder/decorated-nodes.ts +80 -0
  124. package/src/builders/FlowBuilder/examples/extensions.tsx +120 -0
  125. package/src/builders/FlowBuilder/extensions.ts +37 -0
  126. package/src/builders/FlowBuilder/fixtures.ts +80 -0
  127. package/src/builders/FlowBuilder/parts.tsx +196 -0
  128. package/src/builders/FlowBuilder/problems.tsx +40 -0
  129. package/src/builders/LogViewer/LogViewer.test.tsx +97 -0
  130. package/src/builders/LogViewer/LogViewer.tsx +253 -0
  131. package/src/builders/LogViewer/block.meta.ts +10 -0
  132. package/src/builders/LogViewer/fixtures.ts +49 -0
  133. package/src/builders/index.ts +15 -0
  134. package/src/data-display/ActivityFeed/ActivityFeed.test.tsx +62 -0
  135. package/src/data-display/ActivityFeed/ActivityFeed.tsx +118 -0
  136. package/src/data-display/ActivityFeed/block.meta.ts +10 -0
  137. package/src/data-display/ActivityFeed/fixtures.ts +29 -0
  138. package/src/data-display/ChartCard/ChartCard.ranges.test.tsx +282 -0
  139. package/src/data-display/ChartCard/ChartCard.test.tsx +54 -0
  140. package/src/data-display/ChartCard/ChartCard.tsx +218 -0
  141. package/src/data-display/ChartCard/block.meta.ts +10 -0
  142. package/src/data-display/ChartCard/examples/custom-ranges.tsx +37 -0
  143. package/src/data-display/ChartCard/examples/heatmap.tsx +20 -0
  144. package/src/data-display/ChartCard/examples/toolbar-owned-range.tsx +34 -0
  145. package/src/data-display/ChartCard/fixtures.ts +82 -0
  146. package/src/data-display/CommentThread/CommentThread.test.tsx +29 -0
  147. package/src/data-display/CommentThread/CommentThread.tsx +120 -0
  148. package/src/data-display/CommentThread/block.meta.ts +10 -0
  149. package/src/data-display/CommentThread/fixtures.ts +28 -0
  150. package/src/data-display/DescriptionListCard/DescriptionListCard.test.tsx +28 -0
  151. package/src/data-display/DescriptionListCard/DescriptionListCard.tsx +120 -0
  152. package/src/data-display/DescriptionListCard/block.meta.ts +9 -0
  153. package/src/data-display/DescriptionListCard/fixtures.ts +12 -0
  154. package/src/data-display/KpiRow/KpiRow.test.tsx +29 -0
  155. package/src/data-display/KpiRow/KpiRow.tsx +101 -0
  156. package/src/data-display/KpiRow/block.meta.ts +10 -0
  157. package/src/data-display/KpiRow/fixtures.ts +34 -0
  158. package/src/data-display/ResourceCardGrid/ResourceCardGrid.test.tsx +22 -0
  159. package/src/data-display/ResourceCardGrid/ResourceCardGrid.tsx +98 -0
  160. package/src/data-display/ResourceCardGrid/block.meta.ts +10 -0
  161. package/src/data-display/ResourceCardGrid/fixtures.ts +72 -0
  162. package/src/data-display/StatCard/StatCard.test.tsx +30 -0
  163. package/src/data-display/StatCard/StatCard.tsx +138 -0
  164. package/src/data-display/StatCard/block.meta.ts +10 -0
  165. package/src/data-display/StatCard/fixtures.ts +26 -0
  166. package/src/data-display/StatusTimeline/StatusTimeline.test.tsx +153 -0
  167. package/src/data-display/StatusTimeline/StatusTimeline.tsx +114 -0
  168. package/src/data-display/StatusTimeline/block.meta.ts +10 -0
  169. package/src/data-display/StatusTimeline/examples/Empty.tsx +6 -0
  170. package/src/data-display/StatusTimeline/fixtures.ts +62 -0
  171. package/src/data-display/index.ts +34 -0
  172. package/src/data-display/shared/TrendDelta.tsx +60 -0
  173. package/src/feedback/ConfirmDestructive/ConfirmDestructive.test.tsx +47 -0
  174. package/src/feedback/ConfirmDestructive/ConfirmDestructive.tsx +145 -0
  175. package/src/feedback/ConfirmDestructive/block.meta.ts +10 -0
  176. package/src/feedback/EmptyStates/EmptyStates.test.tsx +48 -0
  177. package/src/feedback/EmptyStates/EmptyStates.tsx +191 -0
  178. package/src/feedback/EmptyStates/block.meta.ts +10 -0
  179. package/src/feedback/EmptyStates/fixtures.ts +6 -0
  180. package/src/feedback/ErrorPanel/ErrorPanel.test.tsx +37 -0
  181. package/src/feedback/ErrorPanel/ErrorPanel.tsx +146 -0
  182. package/src/feedback/ErrorPanel/block.meta.ts +10 -0
  183. package/src/feedback/InlineCallout/InlineCallout.test.tsx +33 -0
  184. package/src/feedback/InlineCallout/InlineCallout.tsx +81 -0
  185. package/src/feedback/InlineCallout/block.meta.ts +9 -0
  186. package/src/feedback/Skeletons/Skeletons.test.tsx +25 -0
  187. package/src/feedback/Skeletons/Skeletons.tsx +142 -0
  188. package/src/feedback/Skeletons/block.meta.ts +10 -0
  189. package/src/feedback/SystemBanner/SystemBanner.test.tsx +29 -0
  190. package/src/feedback/SystemBanner/SystemBanner.tsx +105 -0
  191. package/src/feedback/SystemBanner/block.meta.ts +9 -0
  192. package/src/feedback/index.ts +14 -0
  193. package/src/feedback/types.ts +13 -0
  194. package/src/forms/CreateApiKeyDialog/CreateApiKeyDialog.test.tsx +36 -0
  195. package/src/forms/CreateApiKeyDialog/CreateApiKeyDialog.tsx +197 -0
  196. package/src/forms/CreateApiKeyDialog/block.meta.ts +10 -0
  197. package/src/forms/CreateApiKeyDialog/fixtures.ts +31 -0
  198. package/src/forms/CredentialDisclosureDialog/CredentialDisclosureDialog.test.tsx +339 -0
  199. package/src/forms/CredentialDisclosureDialog/CredentialDisclosureDialog.tsx +193 -0
  200. package/src/forms/CredentialDisclosureDialog/block.meta.ts +10 -0
  201. package/src/forms/CredentialDisclosureDialog/examples/Fresh.tsx +32 -0
  202. package/src/forms/CredentialDisclosureDialog/examples/ResponseGating.tsx +69 -0
  203. package/src/forms/CredentialDisclosureDialog/examples/Stored.tsx +32 -0
  204. package/src/forms/CredentialDisclosureDialog/fixtures.ts +14 -0
  205. package/src/forms/DangerZone/ConfirmDestructiveDialog.tsx +101 -0
  206. package/src/forms/DangerZone/DangerZone.test.tsx +28 -0
  207. package/src/forms/DangerZone/DangerZone.tsx +86 -0
  208. package/src/forms/DangerZone/block.meta.ts +10 -0
  209. package/src/forms/DangerZone/fixtures.ts +36 -0
  210. package/src/forms/DateRangeFilter/DateRangeFilter.test.tsx +26 -0
  211. package/src/forms/DateRangeFilter/DateRangeFilter.tsx +83 -0
  212. package/src/forms/DateRangeFilter/block.meta.ts +9 -0
  213. package/src/forms/DialogWizard/DialogWizard.test.tsx +95 -0
  214. package/src/forms/DialogWizard/DialogWizard.tsx +39 -0
  215. package/src/forms/DialogWizard/block.meta.ts +10 -0
  216. package/src/forms/DialogWizard/examples/CreateResource.tsx +65 -0
  217. package/src/forms/DialogWizard/fixtures.ts +20 -0
  218. package/src/forms/FileUploadPanel/FileUploadPanel.test.tsx +43 -0
  219. package/src/forms/FileUploadPanel/FileUploadPanel.tsx +244 -0
  220. package/src/forms/FileUploadPanel/block.meta.ts +10 -0
  221. package/src/forms/FileUploadPanel/fixtures.ts +66 -0
  222. package/src/forms/FormSection/FormSection.test.tsx +34 -0
  223. package/src/forms/FormSection/FormSection.tsx +77 -0
  224. package/src/forms/FormSection/block.meta.ts +9 -0
  225. package/src/forms/InviteMembersDialog/InviteMembersDialog.test.tsx +37 -0
  226. package/src/forms/InviteMembersDialog/InviteMembersDialog.tsx +183 -0
  227. package/src/forms/InviteMembersDialog/block.meta.ts +10 -0
  228. package/src/forms/InviteMembersDialog/fixtures.ts +14 -0
  229. package/src/forms/SettingsRow/SettingsRow.test.tsx +50 -0
  230. package/src/forms/SettingsRow/SettingsRow.tsx +74 -0
  231. package/src/forms/SettingsRow/block.meta.ts +9 -0
  232. package/src/forms/ValidationSummary/ValidationSummary.test.tsx +46 -0
  233. package/src/forms/ValidationSummary/ValidationSummary.tsx +75 -0
  234. package/src/forms/ValidationSummary/block.meta.ts +9 -0
  235. package/src/forms/ValidationSummary/fixtures.ts +13 -0
  236. package/src/forms/index.ts +27 -0
  237. package/src/index.ts +14 -0
  238. package/src/marketing/ComparisonTable/ComparisonTable.test.tsx +69 -0
  239. package/src/marketing/ComparisonTable/ComparisonTable.tsx +245 -0
  240. package/src/marketing/ComparisonTable/block.meta.ts +10 -0
  241. package/src/marketing/ComparisonTable/examples/options.tsx +48 -0
  242. package/src/marketing/ComparisonTable/fixtures.ts +83 -0
  243. package/src/marketing/Faq/Faq.test.tsx +25 -0
  244. package/src/marketing/Faq/Faq.tsx +68 -0
  245. package/src/marketing/Faq/block.meta.ts +10 -0
  246. package/src/marketing/Faq/fixtures.ts +32 -0
  247. package/src/marketing/FeatureGrid/FeatureGrid.test.tsx +19 -0
  248. package/src/marketing/FeatureGrid/FeatureGrid.tsx +79 -0
  249. package/src/marketing/FeatureGrid/block.meta.ts +10 -0
  250. package/src/marketing/FeatureGrid/fixtures.ts +36 -0
  251. package/src/marketing/Hero/Hero.test.tsx +33 -0
  252. package/src/marketing/Hero/Hero.tsx +107 -0
  253. package/src/marketing/Hero/block.meta.ts +10 -0
  254. package/src/marketing/PricingTable/PricingTable.test.tsx +92 -0
  255. package/src/marketing/PricingTable/PricingTable.tsx +323 -0
  256. package/src/marketing/PricingTable/block.meta.ts +10 -0
  257. package/src/marketing/PricingTable/fixtures.ts +34 -0
  258. package/src/marketing/actions.tsx +40 -0
  259. package/src/marketing/index.ts +15 -0
  260. package/src/meta.ts +25 -0
  261. package/src/page-chrome/BulkActionBar/BulkActionBar.test.tsx +34 -0
  262. package/src/page-chrome/BulkActionBar/BulkActionBar.tsx +76 -0
  263. package/src/page-chrome/BulkActionBar/block.meta.ts +9 -0
  264. package/src/page-chrome/PageHeader/PageHeader.test.tsx +68 -0
  265. package/src/page-chrome/PageHeader/PageHeader.tsx +152 -0
  266. package/src/page-chrome/PageHeader/block.meta.ts +10 -0
  267. package/src/page-chrome/PageHeader/fixtures.ts +29 -0
  268. package/src/page-chrome/PageToolbar/PageToolbar.test.tsx +38 -0
  269. package/src/page-chrome/PageToolbar/PageToolbar.tsx +138 -0
  270. package/src/page-chrome/PageToolbar/block.meta.ts +10 -0
  271. package/src/page-chrome/PageToolbar/fixtures.ts +8 -0
  272. package/src/page-chrome/SectionHeader/SectionHeader.test.tsx +28 -0
  273. package/src/page-chrome/SectionHeader/SectionHeader.tsx +73 -0
  274. package/src/page-chrome/SectionHeader/block.meta.ts +9 -0
  275. package/src/page-chrome/StickyActionBar/StickyActionBar.test.tsx +29 -0
  276. package/src/page-chrome/StickyActionBar/StickyActionBar.tsx +66 -0
  277. package/src/page-chrome/StickyActionBar/block.meta.ts +9 -0
  278. package/src/page-chrome/index.ts +13 -0
  279. package/src/page-chrome/types.ts +25 -0
  280. package/src/scheduling/AgendaList/AgendaList.test.tsx +28 -0
  281. package/src/scheduling/AgendaList/AgendaList.tsx +165 -0
  282. package/src/scheduling/AgendaList/block.meta.ts +10 -0
  283. package/src/scheduling/MonthCalendar/MonthCalendar.test.tsx +32 -0
  284. package/src/scheduling/MonthCalendar/MonthCalendar.tsx +223 -0
  285. package/src/scheduling/MonthCalendar/block.meta.ts +10 -0
  286. package/src/scheduling/TimeSlotPicker/TimeSlotPicker.test.tsx +26 -0
  287. package/src/scheduling/TimeSlotPicker/TimeSlotPicker.tsx +115 -0
  288. package/src/scheduling/TimeSlotPicker/block.meta.ts +10 -0
  289. package/src/scheduling/TimeSlotPicker/fixtures.ts +10 -0
  290. package/src/scheduling/WeekCalendar/WeekCalendar.test.tsx +27 -0
  291. package/src/scheduling/WeekCalendar/WeekCalendar.tsx +274 -0
  292. package/src/scheduling/WeekCalendar/block.meta.ts +10 -0
  293. package/src/scheduling/index.ts +11 -0
  294. package/src/scheduling/shared/CalendarHeader.tsx +54 -0
  295. package/src/scheduling/shared/EventTile.tsx +21 -0
  296. package/src/scheduling/shared/dates.ts +98 -0
  297. package/src/scheduling/shared/events.ts +90 -0
  298. package/src/scheduling/shared/fixtures.ts +54 -0
  299. package/src/shell/AppSidebar/AppSidebar.test.tsx +38 -0
  300. package/src/shell/AppSidebar/AppSidebar.tsx +226 -0
  301. package/src/shell/AppSidebar/block.meta.ts +10 -0
  302. package/src/shell/AppSidebar/fixtures.ts +58 -0
  303. package/src/shell/AppTopbar/AppTopbar.test.tsx +41 -0
  304. package/src/shell/AppTopbar/AppTopbar.tsx +82 -0
  305. package/src/shell/AppTopbar/block.meta.ts +19 -0
  306. package/src/shell/AppTopbar/fixtures.ts +22 -0
  307. package/src/shell/EnvironmentBadge/EnvironmentBadge.test.tsx +22 -0
  308. package/src/shell/EnvironmentBadge/EnvironmentBadge.tsx +55 -0
  309. package/src/shell/EnvironmentBadge/block.meta.ts +10 -0
  310. package/src/shell/GlobalSearch/GlobalSearch.test.tsx +28 -0
  311. package/src/shell/GlobalSearch/GlobalSearch.tsx +80 -0
  312. package/src/shell/GlobalSearch/block.meta.ts +10 -0
  313. package/src/shell/GlobalSearch/fixtures.ts +64 -0
  314. package/src/shell/index.ts +11 -0
  315. package/src/styles.css +2 -0
  316. package/src/tables/DataTable/DataTable.edit-views.test.tsx +249 -0
  317. package/src/tables/DataTable/DataTable.labels.test.tsx +291 -0
  318. package/src/tables/DataTable/DataTable.layout.test.tsx +188 -0
  319. package/src/tables/DataTable/DataTable.overflow-labels.test.tsx +59 -0
  320. package/src/tables/DataTable/DataTable.single-select.test.tsx +501 -0
  321. package/src/tables/DataTable/DataTable.test.tsx +268 -0
  322. package/src/tables/DataTable/DataTable.tsx +531 -0
  323. package/src/tables/DataTable/block.meta.ts +129 -0
  324. package/src/tables/DataTable/column-menu.tsx +112 -0
  325. package/src/tables/DataTable/column-sizing.ts +166 -0
  326. package/src/tables/DataTable/data-table-body.tsx +195 -0
  327. package/src/tables/DataTable/data-table-props.ts +159 -0
  328. package/src/tables/DataTable/data-table-row.tsx +231 -0
  329. package/src/tables/DataTable/data-table-utils.ts +195 -0
  330. package/src/tables/DataTable/density-toggle.tsx +33 -0
  331. package/src/tables/DataTable/editable-cell.tsx +287 -0
  332. package/src/tables/DataTable/examples/editable.tsx +71 -0
  333. package/src/tables/DataTable/examples/grouped.tsx +18 -0
  334. package/src/tables/DataTable/examples/resizable-pinned.tsx +20 -0
  335. package/src/tables/DataTable/examples/saved-views.tsx +61 -0
  336. package/src/tables/DataTable/examples/single-selection.tsx +55 -0
  337. package/src/tables/DataTable/examples/virtualized.tsx +20 -0
  338. package/src/tables/DataTable/fixtures.ts +119 -0
  339. package/src/tables/DataTable/header-cell.tsx +90 -0
  340. package/src/tables/DataTable/labels.ts +54 -0
  341. package/src/tables/DataTable/resize-handle.tsx +91 -0
  342. package/src/tables/DataTable/single-selection.tsx +149 -0
  343. package/src/tables/DataTable/use-table-views.ts +79 -0
  344. package/src/tables/DataTable/use-virtual-rows.ts +83 -0
  345. package/src/tables/DataTable/views.tsx +193 -0
  346. package/src/tables/FilterBar/FilterBar.labels.test.tsx +69 -0
  347. package/src/tables/FilterBar/FilterBar.test.tsx +43 -0
  348. package/src/tables/FilterBar/FilterBar.tsx +232 -0
  349. package/src/tables/FilterBar/block.meta.ts +32 -0
  350. package/src/tables/FilterBar/fixtures.ts +32 -0
  351. package/src/tables/InlineEditRow/InlineEditRow.test.tsx +41 -0
  352. package/src/tables/InlineEditRow/InlineEditRow.tsx +215 -0
  353. package/src/tables/InlineEditRow/block.meta.ts +10 -0
  354. package/src/tables/InlineEditRow/fixtures.ts +28 -0
  355. package/src/tables/PaginationFooter/PaginationFooter.labels.test.tsx +57 -0
  356. package/src/tables/PaginationFooter/PaginationFooter.test.tsx +29 -0
  357. package/src/tables/PaginationFooter/PaginationFooter.tsx +118 -0
  358. package/src/tables/PaginationFooter/block.meta.ts +33 -0
  359. package/src/tables/SubordinateList/SubordinateList.test.tsx +351 -0
  360. package/src/tables/SubordinateList/SubordinateList.tsx +140 -0
  361. package/src/tables/SubordinateList/block.meta.ts +10 -0
  362. package/src/tables/SubordinateList/examples/ResourceForm.tsx +129 -0
  363. package/src/tables/SubordinateList/examples/empty.tsx +5 -0
  364. package/src/tables/SubordinateList/examples/error.tsx +5 -0
  365. package/src/tables/SubordinateList/examples/loading.tsx +5 -0
  366. package/src/tables/TruncatedCollectionFooter/TruncatedCollectionFooter.test.tsx +159 -0
  367. package/src/tables/TruncatedCollectionFooter/TruncatedCollectionFooter.tsx +60 -0
  368. package/src/tables/TruncatedCollectionFooter/block.meta.ts +10 -0
  369. package/src/tables/TruncatedCollectionFooter/examples/exact.tsx +6 -0
  370. package/src/tables/TruncatedCollectionFooter/examples/has-more.tsx +12 -0
  371. package/src/tables/TruncatedCollectionFooter/fixtures.ts +2 -0
  372. package/src/tables/index.ts +45 -0
  373. package/src/team/MembersTable/MembersTable.test.tsx +29 -0
  374. package/src/team/MembersTable/MembersTable.tsx +141 -0
  375. package/src/team/MembersTable/block.meta.ts +9 -0
  376. package/src/team/MembersTable/fixtures.ts +19 -0
  377. package/src/team/NotificationMatrix/NotificationMatrix.test.tsx +36 -0
  378. package/src/team/NotificationMatrix/NotificationMatrix.tsx +144 -0
  379. package/src/team/NotificationMatrix/block.meta.ts +9 -0
  380. package/src/team/NotificationMatrix/fixtures.ts +43 -0
  381. package/src/team/PendingInvitations/PendingInvitations.test.tsx +30 -0
  382. package/src/team/PendingInvitations/PendingInvitations.tsx +132 -0
  383. package/src/team/PendingInvitations/block.meta.ts +9 -0
  384. package/src/team/PendingInvitations/fixtures.ts +39 -0
  385. package/src/team/ProfileHeader/ProfileHeader.test.tsx +29 -0
  386. package/src/team/ProfileHeader/ProfileHeader.tsx +106 -0
  387. package/src/team/ProfileHeader/block.meta.ts +10 -0
  388. package/src/team/ProfileHeader/fixtures.ts +28 -0
  389. package/src/team/SessionsDevices/SessionsDevices.test.tsx +30 -0
  390. package/src/team/SessionsDevices/SessionsDevices.tsx +126 -0
  391. package/src/team/SessionsDevices/block.meta.ts +10 -0
  392. package/src/team/SessionsDevices/fixtures.ts +58 -0
  393. package/src/team/index.ts +15 -0
  394. package/src/test/a11y.ts +18 -0
  395. package/src/test/heading-levels.test.tsx +69 -0
  396. package/src/test/setup.ts +36 -0
  397. package/README.md +0 -3
@@ -0,0 +1,73 @@
1
+ import { Button, cn } from '@gntik-ai/ui';
2
+ import type { ReactNode } from 'react';
3
+ import type { PageChromeAction } from '../types';
4
+
5
+ export interface SectionHeaderProps {
6
+ title?: ReactNode;
7
+ description?: ReactNode;
8
+ /** Count pill after the title (members, items…). */
9
+ count?: number;
10
+ /** Small actions on the right; the last one without a variant is secondary. */
11
+ actions?: PageChromeAction[];
12
+ /** Heading level that fits the page outline. */
13
+ as?: 'h2' | 'h3';
14
+ /** Hairline under the header. */
15
+ bordered?: boolean;
16
+ /** Id for the heading, so a section can use `aria-labelledby`. */
17
+ headingId?: string;
18
+ className?: string;
19
+ }
20
+
21
+ const sectionHeaderDefaultActions: PageChromeAction[] = [{ label: 'Add policy', variant: 'secondary' }];
22
+
23
+ /** Heading for a section inside a page: title (+ count), description and small actions. */
24
+ export function SectionHeader({
25
+ title = 'Policies',
26
+ description = 'Guardrails applied to every deployment in this project.',
27
+ count,
28
+ actions = sectionHeaderDefaultActions,
29
+ as: Heading = 'h2',
30
+ bordered = true,
31
+ headingId,
32
+ className,
33
+ }: SectionHeaderProps) {
34
+ return (
35
+ <div
36
+ className={cn(
37
+ 'flex flex-wrap items-end justify-between gap-x-4 gap-y-3',
38
+ bordered && 'border-b border-border pb-3',
39
+ className,
40
+ )}
41
+ >
42
+ <div className="min-w-0">
43
+ <div className="flex items-center gap-2.5">
44
+ <Heading id={headingId} className="text-[17px] font-semibold tracking-tight text-foreground">
45
+ {title}
46
+ </Heading>
47
+ {count != null && (
48
+ <span className="inline-flex h-5 items-center rounded-full bg-secondary px-2 font-mono text-[11px] text-muted-foreground">
49
+ {count.toLocaleString('en-US')}
50
+ </span>
51
+ )}
52
+ </div>
53
+ {description != null && <p className="mt-1 text-[13px] text-muted-foreground">{description}</p>}
54
+ </div>
55
+ {actions.length > 0 && (
56
+ <div className="flex shrink-0 flex-wrap items-center gap-2">
57
+ {actions.map((a) => (
58
+ <Button
59
+ key={a.id ?? a.label}
60
+ size="sm"
61
+ variant={a.variant ?? 'secondary'}
62
+ icon={a.icon}
63
+ disabled={a.disabled}
64
+ onClick={a.onClick}
65
+ >
66
+ {a.label}
67
+ </Button>
68
+ ))}
69
+ </div>
70
+ )}
71
+ </div>
72
+ );
73
+ }
@@ -0,0 +1,9 @@
1
+ import type { BlockMeta } from '../../meta';
2
+
3
+ export const meta: BlockMeta = {
4
+ name: 'Section header',
5
+ family: 'page-chrome',
6
+ status: 'beta',
7
+ description: 'Heading for a section within a page: title with optional count, description and small actions.',
8
+ uses: ['Button'],
9
+ };
@@ -0,0 +1,29 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { expectNoAxeViolations } from '../../test/a11y';
4
+ import { StickyActionBar } from './StickyActionBar';
5
+
6
+ describe('StickyActionBar', () => {
7
+ it('renders the dirty state by default', async () => {
8
+ const { container } = render(<StickyActionBar />);
9
+ expect(screen.getByRole('region', { name: 'Save changes' })).toBeInTheDocument();
10
+ expect(screen.getByRole('status')).toHaveTextContent('You have unsaved changes');
11
+ await expectNoAxeViolations(container);
12
+ });
13
+
14
+ it('saves, discards and disables while clean', async () => {
15
+ const user = userEvent.setup();
16
+ const onSave = vi.fn();
17
+ const onCancel = vi.fn();
18
+ const { rerender } = render(<StickyActionBar onSave={onSave} onCancel={onCancel} />);
19
+ await user.click(screen.getByRole('button', { name: 'Save changes' }));
20
+ await user.click(screen.getByRole('button', { name: 'Discard' }));
21
+ expect(onSave).toHaveBeenCalledTimes(1);
22
+ expect(onCancel).toHaveBeenCalledTimes(1);
23
+ rerender(<StickyActionBar dirty={false} onSave={onSave} />);
24
+ expect(screen.getByRole('status')).toHaveTextContent('All changes saved');
25
+ expect(screen.getByRole('button', { name: 'Save changes' })).toBeDisabled();
26
+ rerender(<StickyActionBar dirty={false} hideWhenClean />);
27
+ expect(screen.queryByRole('region')).not.toBeInTheDocument();
28
+ });
29
+ });
@@ -0,0 +1,66 @@
1
+ import { Button, cn, useI18n } from '@gntik-ai/ui';
2
+ import type { ReactNode } from 'react';
3
+
4
+ export interface StickyActionBarProps {
5
+ /** Whether the form has unsaved changes; Save and Discard are disabled while clean. */
6
+ dirty?: boolean;
7
+ /** Shows a spinner on Save and blocks both buttons. */
8
+ saving?: boolean;
9
+ onSave?: () => void;
10
+ onCancel?: () => void;
11
+ /** Text while dirty. */
12
+ dirtyMessage?: ReactNode;
13
+ /** Text while clean. */
14
+ cleanMessage?: ReactNode;
15
+ saveLabel?: string;
16
+ cancelLabel?: string;
17
+ /** Render nothing while clean (the bar slides in only when there is something to save). */
18
+ hideWhenClean?: boolean;
19
+ /** Accessible name of the region. */
20
+ 'aria-label'?: string;
21
+ className?: string;
22
+ }
23
+
24
+ /** Bottom bar for long forms: unsaved-changes status plus Discard / Save, sticky to the viewport. */
25
+ export function StickyActionBar({
26
+ dirty = true,
27
+ saving = false,
28
+ onSave,
29
+ onCancel,
30
+ dirtyMessage: dirtyMessageProp,
31
+ cleanMessage: cleanMessageProp,
32
+ saveLabel: saveLabelProp,
33
+ cancelLabel: cancelLabelProp,
34
+ hideWhenClean = false,
35
+ 'aria-label': ariaLabelProp,
36
+ className,
37
+ }: StickyActionBarProps) {
38
+ const { t } = useI18n();
39
+ const dirtyMessage = dirtyMessageProp ?? t('actionBar.unsaved');
40
+ const cleanMessage = cleanMessageProp ?? t('actionBar.saved');
41
+ const saveLabel = saveLabelProp ?? t('actionBar.save');
42
+ const cancelLabel = cancelLabelProp ?? t('actionBar.discard');
43
+ const ariaLabel = ariaLabelProp ?? t('actionBar.save');
44
+ if (hideWhenClean && !dirty && !saving) return null;
45
+ return (
46
+ <section
47
+ aria-label={ariaLabel}
48
+ className={cn('sticky bottom-0 z-20 border-t border-border bg-card px-4 py-3 shadow-sm sm:px-6', className)}
49
+ >
50
+ <div className="mx-auto flex max-w-5xl flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
51
+ <p role="status" className="flex items-center gap-2 text-[13px] text-foreground">
52
+ <span aria-hidden className={cn('size-2 shrink-0 rounded-full', dirty ? 'bg-warning' : 'bg-success')} />
53
+ {saving ? t('actionBar.saving') : dirty ? dirtyMessage : cleanMessage}
54
+ </p>
55
+ <div className="flex items-center gap-2 sm:justify-end [&>*]:flex-1 sm:[&>*]:flex-none">
56
+ <Button variant="ghost" disabled={!dirty || saving} onClick={onCancel}>
57
+ {cancelLabel}
58
+ </Button>
59
+ <Button variant="primary" loading={saving} disabled={!dirty} onClick={onSave}>
60
+ {saveLabel}
61
+ </Button>
62
+ </div>
63
+ </div>
64
+ </section>
65
+ );
66
+ }
@@ -0,0 +1,9 @@
1
+ import type { BlockMeta } from '../../meta';
2
+
3
+ export const meta: BlockMeta = {
4
+ name: 'Sticky action bar',
5
+ family: 'page-chrome',
6
+ status: 'beta',
7
+ description: 'Sticky bottom bar for long forms with an unsaved-changes status, Discard and Save (with saving state).',
8
+ uses: ['Button'],
9
+ };
@@ -0,0 +1,13 @@
1
+ export type { PageChromeAction } from './types';
2
+ export * from './PageHeader/PageHeader';
3
+ export * from './PageHeader/fixtures';
4
+ export { meta as pageHeaderMeta } from './PageHeader/block.meta';
5
+ export * from './SectionHeader/SectionHeader';
6
+ export { meta as sectionHeaderMeta } from './SectionHeader/block.meta';
7
+ export * from './PageToolbar/PageToolbar';
8
+ export * from './PageToolbar/fixtures';
9
+ export { meta as pageToolbarMeta } from './PageToolbar/block.meta';
10
+ export * from './StickyActionBar/StickyActionBar';
11
+ export { meta as stickyActionBarMeta } from './StickyActionBar/block.meta';
12
+ export * from './BulkActionBar/BulkActionBar';
13
+ export { meta as bulkActionBarMeta } from './BulkActionBar/block.meta';
@@ -0,0 +1,25 @@
1
+ import type { ButtonProps, MoreMenuAction } from '@gntik-ai/ui';
2
+ import type { LucideIcon } from '@gntik-ai/icons';
3
+
4
+ /** A header/toolbar action rendered as a kit Button (or a MoreMenu item when folded). */
5
+ export interface PageChromeAction {
6
+ /** Stable key; defaults to `label`. */
7
+ id?: string;
8
+ label: string;
9
+ icon?: LucideIcon;
10
+ /** Button style; when omitted the block picks one (primary for the main action, secondary otherwise). */
11
+ variant?: ButtonProps['variant'];
12
+ disabled?: boolean;
13
+ onClick?: () => void;
14
+ }
15
+
16
+ /** Maps a PageChromeAction to a MoreMenu item (destructive variant → destructive item). */
17
+ export function toMenuAction(action: PageChromeAction): MoreMenuAction {
18
+ return {
19
+ label: action.label,
20
+ icon: action.icon,
21
+ disabled: action.disabled,
22
+ destructive: action.variant === 'destructive',
23
+ onSelect: action.onClick,
24
+ };
25
+ }
@@ -0,0 +1,28 @@
1
+ import { render, screen, within } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { expectNoAxeViolations } from '../../test/a11y';
4
+ import { AgendaList } from './AgendaList';
5
+
6
+ describe('AgendaList', () => {
7
+ it('groups the next seven days by day', async () => {
8
+ const { container } = render(<AgendaList />);
9
+ expect(screen.getByRole('heading', { name: 'Today · Wednesday, April 15' })).toBeInTheDocument();
10
+ expect(screen.getByRole('heading', { name: 'Tomorrow · Thursday, April 16' })).toBeInTheDocument();
11
+ expect(
12
+ within(screen.getByRole('list', { name: 'Today · Wednesday, April 15' })).getAllByRole('listitem'),
13
+ ).toHaveLength(9);
14
+ expect(screen.queryByRole('heading', { name: /April 22/ })).not.toBeInTheDocument();
15
+ await expectNoAxeViolations(container);
16
+ });
17
+
18
+ it('reports clicks and shows the empty state', async () => {
19
+ const user = userEvent.setup();
20
+ const onEventClick = vi.fn();
21
+ const { unmount } = render(<AgendaList onEventClick={onEventClick} />);
22
+ await user.click(screen.getByRole('button', { name: /Checkout hotfix/ }));
23
+ expect(onEventClick).toHaveBeenCalledWith(expect.objectContaining({ title: 'Checkout hotfix' }));
24
+ unmount();
25
+ render(<AgendaList events={[]} />);
26
+ expect(screen.getByRole('heading', { name: 'Nothing scheduled' })).toBeInTheDocument();
27
+ });
28
+ });
@@ -0,0 +1,165 @@
1
+ import { useId, useMemo } from 'react';
2
+ import { Badge, EmptyState, cn, useI18n } from '@gntik-ai/ui';
3
+ import { CalendarDays, ChevronRight, Clock } from '@gntik-ai/icons';
4
+ import { EventTile } from '../shared/EventTile';
5
+ import { addDays, dayKey, formatDay, formatRange, sameDay } from '../shared/dates';
6
+ import { defaultEventKinds, groupByDay, kindOf, TONE, type EventKind, type ScheduleEvent } from '../shared/events';
7
+ import { sampleEvents, sampleToday } from '../shared/fixtures';
8
+
9
+ export interface AgendaListProps {
10
+ events?: ScheduleEvent[];
11
+ /** The app's "today": labels the Today/Tomorrow groups. */
12
+ today?: Date;
13
+ /** Only events on or after this day. Defaults to `today`; pass null for all. */
14
+ from?: Date | null;
15
+ /** Number of days shown from `from`. Default 7; null for no limit. */
16
+ days?: number | null;
17
+ /** Makes each row a button. */
18
+ onEventClick?: (event: ScheduleEvent) => void;
19
+ kinds?: Record<string, EventKind>;
20
+ title?: string;
21
+ /** Max height of the scrolling list in px. */
22
+ maxHeight?: number;
23
+ emptyTitle?: string;
24
+ emptyDescription?: string;
25
+ /** Heading level of the title, to fit the page outline (default h3); inner headings use the next level. */
26
+ titleAs?: 'h2' | 'h3' | 'h4';
27
+ className?: string;
28
+ }
29
+
30
+ /** Upcoming scheduled work grouped by day, each row with a kind tile, time range and location. */
31
+ export function AgendaList({
32
+ events = sampleEvents,
33
+ today = sampleToday,
34
+ from,
35
+ days = 7,
36
+ onEventClick,
37
+ kinds = defaultEventKinds,
38
+ title: titleProp,
39
+ maxHeight,
40
+ emptyTitle: emptyTitleProp,
41
+ emptyDescription: emptyDescriptionProp,
42
+ titleAs: TitleTag = 'h3',
43
+ className,
44
+ }: AgendaListProps) {
45
+ const { t, locale } = useI18n();
46
+ const title = titleProp ?? t('calendar.agenda');
47
+ const emptyTitle = emptyTitleProp ?? t('calendar.emptyTitle');
48
+ const emptyDescription = emptyDescriptionProp ?? t('calendar.emptyDescription');
49
+ const SubTag = TitleTag === 'h2' ? 'h3' : TitleTag === 'h3' ? 'h4' : 'h5';
50
+ const uid = useId();
51
+ const start = from === undefined ? today : from;
52
+ const groups = useMemo(() => {
53
+ const first = start ? dayKey(start) : null;
54
+ const last = start && days != null ? dayKey(addDays(start, days - 1)) : null;
55
+ return [...groupByDay(events).entries()]
56
+ .filter(([key]) => (first == null || key >= first) && (last == null || key <= last))
57
+ .sort(([a], [b]) => a.localeCompare(b));
58
+ }, [events, start, days]);
59
+
60
+ const dayLabel = (d: Date) => {
61
+ if (sameDay(d, today)) return `${t('calendar.today')} · ${formatDay(d, locale)}`;
62
+ if (sameDay(d, addDays(today, 1))) return `${t('calendar.tomorrow')} · ${formatDay(d, locale)}`;
63
+ return formatDay(d, locale);
64
+ };
65
+
66
+ return (
67
+ <section aria-label={title} className={cn('overflow-hidden rounded-xl border border-border bg-card', className)}>
68
+ <div className="flex items-center justify-between gap-3 border-b border-border px-4 py-3 sm:px-5">
69
+ <TitleTag className="text-[15px] font-semibold tracking-tight text-foreground">{title}</TitleTag>
70
+ <span className="font-mono text-[11px] text-muted-foreground">
71
+ {groups.reduce((n, [, list]) => n + list.length, 0)} scheduled
72
+ </span>
73
+ </div>
74
+ {groups.length === 0 ? (
75
+ <EmptyState
76
+ icon={CalendarDays}
77
+ title={emptyTitle}
78
+ titleAs={TitleTag === 'h2' ? 'h3' : 'h4'}
79
+ description={emptyDescription}
80
+ className="py-12"
81
+ />
82
+ ) : (
83
+ <div
84
+ style={maxHeight ? { maxHeight } : undefined}
85
+ tabIndex={maxHeight ? 0 : undefined}
86
+ role={maxHeight ? 'region' : undefined}
87
+ aria-label={maxHeight ? `${title} list` : undefined}
88
+ className="overflow-y-auto focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-focus-ring"
89
+ >
90
+ {groups.map(([key, list]) => {
91
+ const first = list[0];
92
+ if (!first) return null;
93
+ const headingId = `${uid}-${key}`;
94
+ return (
95
+ <div key={key}>
96
+ <SubTag
97
+ id={headingId}
98
+ className="border-b border-border bg-secondary/30 px-4 py-2 font-mono text-[11px] tracking-wide text-muted-foreground uppercase sm:px-5"
99
+ >
100
+ {dayLabel(first.start)}
101
+ </SubTag>
102
+ <ul
103
+ aria-labelledby={headingId}
104
+ className="divide-y divide-border border-b border-border last:border-b-0"
105
+ >
106
+ {list.map((event) => {
107
+ const kind = kindOf(event, kinds);
108
+ const range = formatRange(event.start, event.end, locale);
109
+ const content = (
110
+ <>
111
+ <EventTile kind={kind} />
112
+ <span className="min-w-0 flex-1">
113
+ <span className="block truncate text-[13px] font-semibold text-foreground">
114
+ {event.title}
115
+ </span>
116
+ <span className="mt-0.5 flex items-center gap-1.5 font-mono text-[11.5px] text-muted-foreground">
117
+ <Clock size={12} aria-hidden className="shrink-0" />
118
+ <span>{range}</span>
119
+ {event.location && (
120
+ <>
121
+ <span aria-hidden>·</span>
122
+ <span className="truncate">{event.location}</span>
123
+ </>
124
+ )}
125
+ </span>
126
+ </span>
127
+ <Badge tone={TONE[kind.tone].badge} size="sm" className="hidden sm:inline-flex">
128
+ {kind.label}
129
+ </Badge>
130
+ </>
131
+ );
132
+ const row = 'flex w-full items-center gap-3.5 px-4 py-3 text-start sm:px-5';
133
+ return (
134
+ <li key={event.id}>
135
+ {onEventClick ? (
136
+ <button
137
+ type="button"
138
+ onClick={() => onEventClick(event)}
139
+ className={cn(
140
+ row,
141
+ 'group transition-colors hover:bg-accent/30 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-focus-ring motion-reduce:transition-none',
142
+ )}
143
+ >
144
+ {content}
145
+ <ChevronRight
146
+ size={16}
147
+ aria-hidden
148
+ className="shrink-0 text-muted-foreground transition-colors group-hover:text-foreground rtl:-scale-x-100"
149
+ />
150
+ </button>
151
+ ) : (
152
+ <div className={row}>{content}</div>
153
+ )}
154
+ </li>
155
+ );
156
+ })}
157
+ </ul>
158
+ </div>
159
+ );
160
+ })}
161
+ </div>
162
+ )}
163
+ </section>
164
+ );
165
+ }
@@ -0,0 +1,10 @@
1
+ import type { BlockMeta } from '../../meta';
2
+
3
+ export const meta: BlockMeta = {
4
+ name: 'Agenda list',
5
+ family: 'scheduling',
6
+ status: 'beta',
7
+ description:
8
+ 'Upcoming scheduled work grouped by day (Today/Tomorrow labels), each row with a kind tile, time range, location and kind badge; empty state included.',
9
+ uses: ['Badge', 'EmptyState'],
10
+ };
@@ -0,0 +1,32 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { expectNoAxeViolations } from '../../test/a11y';
4
+ import { MonthCalendar } from './MonthCalendar';
5
+
6
+ describe('MonthCalendar', () => {
7
+ it('renders the sample month with today and event counts', async () => {
8
+ const { container } = render(<MonthCalendar />);
9
+ expect(screen.getByRole('heading', { name: 'April 2026' })).toBeInTheDocument();
10
+ expect(screen.getByRole('grid', { name: 'April 2026' })).toBeInTheDocument();
11
+ const today = screen.getByRole('button', { name: 'Wednesday, April 15, 2026, 9 events' });
12
+ expect(today).toHaveAttribute('aria-current', 'date');
13
+ expect(today).toHaveAttribute('tabindex', '0');
14
+ await expectNoAxeViolations(container);
15
+ });
16
+
17
+ it('navigates with the keyboard and selects a day', async () => {
18
+ const user = userEvent.setup();
19
+ const onSelectDate = vi.fn();
20
+ render(<MonthCalendar onSelectDate={onSelectDate} />);
21
+ screen.getByRole('button', { name: /April 15, 2026/ }).focus();
22
+ await user.keyboard('{ArrowDown}{ArrowRight}');
23
+ expect(screen.getByRole('button', { name: /April 23, 2026/ })).toHaveFocus();
24
+ await user.keyboard('{Enter}');
25
+ expect(onSelectDate).toHaveBeenCalledWith(new Date(2026, 3, 23), []);
26
+ await user.keyboard('{PageDown}');
27
+ expect(screen.getByRole('heading', { name: 'May 2026' })).toBeInTheDocument();
28
+ expect(screen.getByRole('button', { name: /May 23, 2026/ })).toHaveFocus();
29
+ await user.click(screen.getByRole('button', { name: 'Previous month' }));
30
+ expect(screen.getByRole('heading', { name: 'April 2026' })).toBeInTheDocument();
31
+ });
32
+ });