@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,69 @@
1
+ import { Alert, Button } from '@gntik-ai/ui';
2
+ import { useRef, useState } from 'react';
3
+ import { CredentialDisclosureDialog, type CredentialDisclosure } from '../CredentialDisclosureDialog';
4
+ import { storedDisclosure } from '../fixtures';
5
+
6
+ /** Pending/error UI belongs to the consumer; only a successful response sets disclosure. */
7
+ export default function ResponseGating() {
8
+ const [disclosure, setDisclosure] = useState<CredentialDisclosure | null>(null);
9
+ const [status, setStatus] = useState<'idle' | 'pending' | 'failed'>('idle');
10
+ const heading = useRef<HTMLHeadingElement>(null);
11
+ const requestId = useRef(0);
12
+ const request = async (result: 'pending' | 'failed' | 'success') => {
13
+ const id = ++requestId.current;
14
+ setDisclosure(null);
15
+ setStatus('pending');
16
+ try {
17
+ const response = await (result === 'pending'
18
+ ? new Promise<CredentialDisclosure>(() => {})
19
+ : result === 'failed'
20
+ ? Promise.reject(new Error('Synthetic request failure'))
21
+ : Promise.resolve(storedDisclosure));
22
+ if (id === requestId.current) {
23
+ setStatus('idle');
24
+ setDisclosure(response);
25
+ }
26
+ } catch {
27
+ if (id === requestId.current) setStatus('failed');
28
+ }
29
+ };
30
+ return (
31
+ <>
32
+ <h1 ref={heading} tabIndex={-1}>
33
+ Credentials
34
+ </h1>
35
+ <div className="flex flex-wrap gap-2">
36
+ <Button
37
+ onClick={() => {
38
+ void request('pending');
39
+ }}
40
+ >
41
+ Request pending response
42
+ </Button>
43
+ <Button
44
+ onClick={() => {
45
+ void request('failed');
46
+ }}
47
+ >
48
+ Request rejected response
49
+ </Button>
50
+ <Button
51
+ onClick={() => {
52
+ void request('success');
53
+ }}
54
+ >
55
+ Request successful response
56
+ </Button>
57
+ </div>
58
+ {status === 'pending' && <p>Request pending</p>}
59
+ {status === 'failed' && (
60
+ <Alert tone="destructive" title="Request failed" description="No credential was disclosed." />
61
+ )}
62
+ <CredentialDisclosureDialog
63
+ disclosure={disclosure}
64
+ onClose={() => setDisclosure(null)}
65
+ fallbackFocus={() => heading.current}
66
+ />
67
+ </>
68
+ );
69
+ }
@@ -0,0 +1,32 @@
1
+ import { Button } from '@gntik-ai/ui';
2
+ import { useRef, useState } from 'react';
3
+ import { CredentialDisclosureDialog, type CredentialDisclosure } from '../CredentialDisclosureDialog';
4
+ import { storedDisclosure } from '../fixtures';
5
+
6
+ /** The initial synthetic stored value stays masked in catalog previews and visual baselines. */
7
+ export default function Stored() {
8
+ const [disclosure, setDisclosure] = useState<CredentialDisclosure | null>(storedDisclosure);
9
+ const heading = useRef<HTMLHeadingElement>(null);
10
+ const trigger = useRef<HTMLButtonElement>(null);
11
+ return (
12
+ <>
13
+ <h1 ref={heading} tabIndex={-1}>
14
+ Credentials
15
+ </h1>
16
+ <Button
17
+ ref={trigger}
18
+ onClick={() => {
19
+ void Promise.resolve(storedDisclosure).then(setDisclosure);
20
+ }}
21
+ >
22
+ Disclose stored secret
23
+ </Button>
24
+ <CredentialDisclosureDialog
25
+ disclosure={disclosure}
26
+ onClose={() => setDisclosure(null)}
27
+ resolveReturnFocus={() => trigger.current}
28
+ fallbackFocus={() => heading.current}
29
+ />
30
+ </>
31
+ );
32
+ }
@@ -0,0 +1,14 @@
1
+ import type { CredentialDisclosure } from './CredentialDisclosureDialog';
2
+
3
+ /** Synthetic values only: never use live credentials in previews or tests. */
4
+ export const storedDisclosure: CredentialDisclosure = {
5
+ variant: 'stored',
6
+ credentialId: 'cred_demo_01',
7
+ expiresAt: 'January 1, 2030',
8
+ secret: 'synthetic_stored_secret_for_demo_only',
9
+ };
10
+ export const freshDisclosure: CredentialDisclosure = {
11
+ ...storedDisclosure,
12
+ variant: 'fresh',
13
+ secret: 'synthetic_fresh_secret_for_demo_only',
14
+ };
@@ -0,0 +1,101 @@
1
+ import {
2
+ AlertDialog,
3
+ AlertDialogCancel,
4
+ AlertDialogContent,
5
+ AlertDialogDescription,
6
+ AlertDialogFooter,
7
+ AlertDialogHeader,
8
+ AlertDialogTitle,
9
+ Alert,
10
+ Button,
11
+ Field,
12
+ FieldLabel,
13
+ Input,
14
+ } from '@gntik-ai/ui';
15
+ import { useState, type ReactNode } from 'react';
16
+
17
+ export interface ConfirmDestructiveDialogProps {
18
+ open: boolean;
19
+ onOpenChange: (open: boolean) => void;
20
+ title: ReactNode;
21
+ description: ReactNode;
22
+ /** Text that must be typed exactly to enable the confirming button. */
23
+ confirmText?: string;
24
+ confirmLabel: string;
25
+ /** Runs on confirm; a returned promise shows a loading state and keeps the dialog open until it settles. */
26
+ onConfirm?: () => void | Promise<void>;
27
+ }
28
+
29
+ /** Destructive confirmation (alertdialog) that can require typing the resource name first. */
30
+ export function ConfirmDestructiveDialog({
31
+ open,
32
+ onOpenChange,
33
+ title,
34
+ description,
35
+ confirmText,
36
+ confirmLabel,
37
+ onConfirm,
38
+ }: ConfirmDestructiveDialogProps) {
39
+ const [typed, setTyped] = useState('');
40
+ const [loading, setLoading] = useState(false);
41
+ const [error, setError] = useState<string | null>(null);
42
+ const matches = confirmText == null || typed === confirmText;
43
+
44
+ const change = (next: boolean) => {
45
+ if (loading) return;
46
+ if (!next) {
47
+ setTyped('');
48
+ setError(null);
49
+ }
50
+ onOpenChange(next);
51
+ };
52
+
53
+ const confirm = async () => {
54
+ if (!matches) return;
55
+ setError(null);
56
+ setLoading(true);
57
+ try {
58
+ await onConfirm?.();
59
+ setLoading(false);
60
+ setTyped('');
61
+ onOpenChange(false);
62
+ } catch (e) {
63
+ setLoading(false);
64
+ setError(e instanceof Error ? e.message : 'Something went wrong. Try again.');
65
+ }
66
+ };
67
+
68
+ return (
69
+ <AlertDialog open={open} onOpenChange={change}>
70
+ <AlertDialogContent size="md">
71
+ <AlertDialogHeader tone="destructive">
72
+ <AlertDialogTitle>{title}</AlertDialogTitle>
73
+ <AlertDialogDescription>{description}</AlertDialogDescription>
74
+ </AlertDialogHeader>
75
+ <form
76
+ className="mt-5 grid gap-4"
77
+ onSubmit={(e) => {
78
+ e.preventDefault();
79
+ void confirm();
80
+ }}
81
+ >
82
+ {confirmText != null && (
83
+ <Field>
84
+ <FieldLabel>
85
+ Type <span className="font-mono font-semibold text-foreground">{confirmText}</span> to confirm
86
+ </FieldLabel>
87
+ <Input value={typed} onValueChange={setTyped} autoComplete="off" spellCheck={false} disabled={loading} />
88
+ </Field>
89
+ )}
90
+ {error && <Alert tone="destructive" description={error} />}
91
+ <AlertDialogFooter className="mt-1">
92
+ <AlertDialogCancel disabled={loading}>Cancel</AlertDialogCancel>
93
+ <Button type="submit" variant="destructive" disabled={!matches} loading={loading}>
94
+ {confirmLabel}
95
+ </Button>
96
+ </AlertDialogFooter>
97
+ </form>
98
+ </AlertDialogContent>
99
+ </AlertDialog>
100
+ );
101
+ }
@@ -0,0 +1,28 @@
1
+ import { render, screen, waitFor } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { expectNoAxeViolations } from '../../test/a11y';
4
+ import { DangerZone } from './DangerZone';
5
+
6
+ describe('DangerZone', () => {
7
+ it('renders the default actions', async () => {
8
+ const { container } = render(<DangerZone />);
9
+ expect(screen.getByRole('region', { name: 'Danger zone' })).toBeInTheDocument();
10
+ expect(screen.getByRole('button', { name: 'Delete project' })).toBeInTheDocument();
11
+ await expectNoAxeViolations(container);
12
+ });
13
+
14
+ it('requires typing the name before confirming', async () => {
15
+ const onConfirm = vi.fn();
16
+ render(<DangerZone onConfirm={onConfirm} />);
17
+ await userEvent.click(screen.getByRole('button', { name: 'Delete project' }));
18
+ const dialog = await screen.findByRole('alertdialog');
19
+ const confirm = screen.getAllByRole('button', { name: 'Delete project' }).at(-1)!;
20
+ expect(confirm).toBeDisabled();
21
+ await expectNoAxeViolations(dialog);
22
+ await userEvent.type(screen.getByLabelText(/to confirm/), 'billing-dashboard');
23
+ expect(confirm).toBeEnabled();
24
+ await userEvent.click(confirm);
25
+ expect(onConfirm).toHaveBeenCalledWith('delete');
26
+ await waitFor(() => expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument());
27
+ });
28
+ });
@@ -0,0 +1,86 @@
1
+ import { Button, cn } from '@gntik-ai/ui';
2
+ import { useId, useState, type ReactNode } from 'react';
3
+ import { ConfirmDestructiveDialog } from './ConfirmDestructiveDialog';
4
+ import { dangerZoneActions, type DangerZoneAction } from './fixtures';
5
+
6
+ export type { DangerZoneAction } from './fixtures';
7
+
8
+ export interface DangerZoneProps {
9
+ title?: ReactNode;
10
+ description?: ReactNode;
11
+ actions?: DangerZoneAction[];
12
+ /** Called after the user confirms an action. A returned promise shows a loading state. */
13
+ onConfirm?: (actionId: string) => void | Promise<void>;
14
+ headingLevel?: 'h2' | 'h3' | 'h4';
15
+ className?: string;
16
+ }
17
+
18
+ /**
19
+ * Bordered destructive section. Each action opens a confirmation dialog; actions with
20
+ * `confirmText` require typing it (e.g. the project name) before the confirm button enables.
21
+ */
22
+ export function DangerZone({
23
+ title = 'Danger zone',
24
+ description = 'These actions affect the whole project and every member who can access it.',
25
+ actions = dangerZoneActions,
26
+ onConfirm,
27
+ headingLevel: Heading = 'h2',
28
+ className,
29
+ }: DangerZoneProps) {
30
+ const id = useId();
31
+ const [pending, setPending] = useState<DangerZoneAction | null>(null);
32
+ const [open, setOpen] = useState(false);
33
+
34
+ return (
35
+ <section
36
+ aria-labelledby={`${id}-title`}
37
+ className={cn('rounded-lg border border-destructive/35 bg-card shadow-sm', className)}
38
+ >
39
+ <div className="border-b border-destructive/20 px-6 py-4">
40
+ <Heading id={`${id}-title`} className="text-[15px] font-semibold tracking-tight text-destructive-text">
41
+ {title}
42
+ </Heading>
43
+ {description != null && (
44
+ <p className="mt-1 text-[13px] leading-6 text-pretty text-muted-foreground">{description}</p>
45
+ )}
46
+ </div>
47
+ <ul className="divide-y divide-border">
48
+ {actions.map((action) => (
49
+ <li
50
+ key={action.id}
51
+ className="flex flex-col gap-3 px-6 py-4 sm:flex-row sm:items-center sm:justify-between sm:gap-6"
52
+ >
53
+ <div className="min-w-0">
54
+ <div className="text-[13.5px] font-semibold text-foreground">{action.title}</div>
55
+ <p className="mt-0.5 max-w-xl text-[12.5px] leading-5 text-pretty text-muted-foreground">
56
+ {action.description}
57
+ </p>
58
+ </div>
59
+ <Button
60
+ variant={action.destructive === false ? 'secondary' : 'destructive'}
61
+ className="shrink-0 self-start sm:self-auto"
62
+ onClick={() => {
63
+ setPending(action);
64
+ setOpen(true);
65
+ }}
66
+ >
67
+ {action.actionLabel}
68
+ </Button>
69
+ </li>
70
+ ))}
71
+ </ul>
72
+ {pending && (
73
+ <ConfirmDestructiveDialog
74
+ key={pending.id}
75
+ open={open}
76
+ onOpenChange={setOpen}
77
+ title={`${pending.title}?`}
78
+ description={pending.description}
79
+ confirmText={pending.confirmText}
80
+ confirmLabel={pending.actionLabel}
81
+ onConfirm={() => onConfirm?.(pending.id)}
82
+ />
83
+ )}
84
+ </section>
85
+ );
86
+ }
@@ -0,0 +1,10 @@
1
+ import type { BlockMeta } from '../../meta';
2
+
3
+ export const meta: BlockMeta = {
4
+ name: 'Danger zone',
5
+ family: 'forms',
6
+ status: 'stable',
7
+ description:
8
+ 'Bordered destructive section whose actions open an alert dialog that can require typing the resource name.',
9
+ uses: ['Button', 'AlertDialog', 'Field', 'Input', 'Alert'],
10
+ };
@@ -0,0 +1,36 @@
1
+ export interface DangerZoneAction {
2
+ id: string;
3
+ title: string;
4
+ description: string;
5
+ /** Label of the row button and of the confirming button. */
6
+ actionLabel: string;
7
+ /** Text the user must type to confirm (e.g. the resource name). Omit for a plain confirmation. */
8
+ confirmText?: string;
9
+ /** Use the destructive button style in the row (default true); `false` renders a secondary button. */
10
+ destructive?: boolean;
11
+ }
12
+
13
+ export const dangerZoneActions: DangerZoneAction[] = [
14
+ {
15
+ id: 'transfer',
16
+ title: 'Transfer project',
17
+ description: 'Move this project and its deployments to another workspace you administer.',
18
+ actionLabel: 'Transfer',
19
+ destructive: false,
20
+ },
21
+ {
22
+ id: 'archive',
23
+ title: 'Archive project',
24
+ description: 'Stops every deployment and makes the project read-only. You can restore it later.',
25
+ actionLabel: 'Archive',
26
+ confirmText: 'billing-dashboard',
27
+ },
28
+ {
29
+ id: 'delete',
30
+ title: 'Delete project',
31
+ description:
32
+ 'Permanently deletes billing-dashboard, its deployments, secrets and audit log. This cannot be undone.',
33
+ actionLabel: 'Delete project',
34
+ confirmText: 'billing-dashboard',
35
+ },
36
+ ];
@@ -0,0 +1,26 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { expectNoAxeViolations } from '../../test/a11y';
4
+ import { DateRangeFilter } from './DateRangeFilter';
5
+
6
+ const today = new Date(2026, 5, 15);
7
+
8
+ describe('DateRangeFilter', () => {
9
+ it('renders the last 7 days by default', async () => {
10
+ const { container } = render(<DateRangeFilter today={today} />);
11
+ const field = screen.getByRole('combobox', { name: 'Date range' });
12
+ expect((field as HTMLInputElement).value).toMatch(/Jun 9/);
13
+ await expectNoAxeViolations(container);
14
+ });
15
+
16
+ it('applies a preset and clears', async () => {
17
+ const onValueChange = vi.fn();
18
+ render(<DateRangeFilter today={today} onValueChange={onValueChange} />);
19
+ await userEvent.click(screen.getByRole('combobox', { name: 'Date range' }));
20
+ await userEvent.click(await screen.findByRole('button', { name: 'Today' }));
21
+ expect(onValueChange).toHaveBeenLastCalledWith({ start: today, end: today });
22
+ await userEvent.click(screen.getByRole('button', { name: 'Clear date range' }));
23
+ expect(onValueChange).toHaveBeenLastCalledWith({ start: null, end: null });
24
+ expect(screen.getByRole('combobox', { name: 'Date range' })).toHaveValue('');
25
+ });
26
+ });
@@ -0,0 +1,83 @@
1
+ import {
2
+ DEFAULT_DATE_RANGE_PRESETS,
3
+ DateRangePicker,
4
+ IconButton,
5
+ cn,
6
+ type DateRange,
7
+ type DateRangePreset,
8
+ useI18n,
9
+ } from '@gntik-ai/ui';
10
+ import { X } from '@gntik-ai/icons';
11
+ import { useState } from 'react';
12
+
13
+ export interface DateRangeFilterProps {
14
+ value?: DateRange;
15
+ /** Initial range; defaults to the "Last 7 days" preset. Pass `{ start: null, end: null }` for none. */
16
+ defaultValue?: DateRange;
17
+ /** Called with a complete range, or an empty one when cleared. */
18
+ onValueChange?: (range: DateRange) => void;
19
+ presets?: DateRangePreset[];
20
+ /** "Today" for the presets (defaults to the current date). */
21
+ today?: Date;
22
+ /** Accessible name of the field. */
23
+ label?: string;
24
+ placeholder?: string;
25
+ /** Show a clear button while a range is set. */
26
+ clearable?: boolean;
27
+ disabled?: boolean;
28
+ className?: string;
29
+ }
30
+
31
+ const EMPTY: DateRange = { start: null, end: null };
32
+ const SHORT: Intl.DateTimeFormatOptions = { month: 'short', day: 'numeric' };
33
+
34
+ /** Compact DateRangePicker for toolbars: small trigger, short dates, presets and a clear button. */
35
+ export function DateRangeFilter({
36
+ value,
37
+ defaultValue,
38
+ onValueChange,
39
+ presets = DEFAULT_DATE_RANGE_PRESETS,
40
+ today,
41
+ label: labelProp,
42
+ placeholder: placeholderProp,
43
+ clearable = true,
44
+ disabled,
45
+ className,
46
+ }: DateRangeFilterProps) {
47
+ const { t } = useI18n();
48
+ const label = labelProp ?? t('dateRange.label');
49
+ const placeholder = placeholderProp ?? t('dateRange.any');
50
+ const [inner, setInner] = useState<DateRange>(
51
+ () => defaultValue ?? presets.find((p) => p.id === 'last-7')?.range?.(today ?? new Date()) ?? EMPTY,
52
+ );
53
+ const range = value ?? inner;
54
+ const set = (next: DateRange) => {
55
+ setInner(next);
56
+ onValueChange?.(next);
57
+ };
58
+ return (
59
+ <div className={cn('inline-flex items-center gap-1', className)}>
60
+ <DateRangePicker
61
+ size="sm"
62
+ aria-label={label}
63
+ value={range}
64
+ onValueChange={set}
65
+ presets={presets}
66
+ today={today}
67
+ placeholder={placeholder}
68
+ formatOptions={SHORT}
69
+ disabled={disabled}
70
+ className="w-[13.5rem]"
71
+ />
72
+ {clearable && range.start && (
73
+ <IconButton
74
+ icon={X}
75
+ size="sm"
76
+ label={t('common.clearItem', { label: label.toLowerCase() })}
77
+ disabled={disabled}
78
+ onClick={() => set(EMPTY)}
79
+ />
80
+ )}
81
+ </div>
82
+ );
83
+ }
@@ -0,0 +1,9 @@
1
+ import type { BlockMeta } from '../../meta';
2
+
3
+ export const meta: BlockMeta = {
4
+ name: 'Date range filter',
5
+ family: 'forms',
6
+ status: 'stable',
7
+ description: 'Compact date range picker for toolbars: small trigger with short dates, presets and a clear button.',
8
+ uses: ['DateRangePicker', 'IconButton'],
9
+ };
@@ -0,0 +1,95 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { DescriptionListCard } from '../../data-display/DescriptionListCard/DescriptionListCard';
4
+ import { ValidationSummary } from '../ValidationSummary/ValidationSummary';
5
+ import { DialogWizard } from './DialogWizard';
6
+ import { expectNoAxeViolations } from '../../test/a11y';
7
+
8
+ describe('DialogWizard', () => {
9
+ it('uses consumer labels and shows validation and review slots with the shared step footer', async () => {
10
+ const user = userEvent.setup();
11
+ const props = {
12
+ open: true,
13
+ title: 'Provision database',
14
+ description: 'Configure storage',
15
+ steps: [{ label: 'Engine' }, { label: 'Check answers' }],
16
+ cancelLabel: 'Abandon',
17
+ backLabel: 'Previous',
18
+ nextLabel: 'Continue',
19
+ finishLabel: 'Provision',
20
+ exitLabel: 'Close flow',
21
+ stepsLabel: 'Stages',
22
+ stepsSummaryLabel: 'Stage count',
23
+ pendingLabel: 'Provisioning',
24
+ stepAnnouncement: ({ position }: { position: number }) => `Stage ${position}`,
25
+ };
26
+ const { rerender } = render(
27
+ <DialogWizard
28
+ {...props}
29
+ current={0}
30
+ nextDisabled
31
+ stepError={
32
+ <ValidationSummary title="Fix the engine" errors={[{ fieldId: 'engine', message: 'Choose an engine' }]} />
33
+ }
34
+ >
35
+ <input id="engine" aria-label="Engine choice" />
36
+ </DialogWizard>,
37
+ );
38
+ expect(await screen.findByRole('dialog', { name: 'Provision database' })).toHaveAccessibleDescription(
39
+ 'Configure storage',
40
+ );
41
+ expect(screen.getByRole('button', { name: 'Continue' })).toBeDisabled();
42
+ expect(screen.getByText('Fix the engine')).toBeInTheDocument();
43
+ expect(screen.getByRole('button', { name: 'Abandon' })).toBeInTheDocument();
44
+ await user.click(screen.getByRole('link', { name: 'Choose an engine' }));
45
+ expect(screen.getByRole('textbox')).toHaveFocus();
46
+ const onFinish = vi.fn();
47
+ rerender(
48
+ <DialogWizard
49
+ {...props}
50
+ current={1}
51
+ onFinish={onFinish}
52
+ reviewStep={
53
+ <DescriptionListCard
54
+ title="Your answers"
55
+ description="Review storage"
56
+ items={[{ id: 'engine', label: 'Engine', value: 'Standard' }]}
57
+ />
58
+ }
59
+ />,
60
+ );
61
+ expect(screen.getByText('Your answers')).toBeInTheDocument();
62
+ expect(screen.getByRole('button', { name: 'Previous' })).toBeInTheDocument();
63
+ await user.click(screen.getByRole('button', { name: 'Provision' }));
64
+ expect(onFinish).toHaveBeenCalledTimes(1);
65
+ });
66
+
67
+ for (const theme of ['dark', 'light', 'high_contrast']) {
68
+ for (const size of ['dialog', 'fullscreen'] as const) {
69
+ it.each([1, 2])(`has no axe violations in ${theme}, ${size}, step %s`, async (current) => {
70
+ document.documentElement.className = theme === 'light' ? '' : theme;
71
+ render(
72
+ <DialogWizard
73
+ open
74
+ title="Create resource"
75
+ description="Configure storage"
76
+ size={size}
77
+ steps={[{ label: 'Details' }, { label: 'Configuration' }, { label: 'Review' }]}
78
+ current={current}
79
+ reviewStep={
80
+ <DescriptionListCard
81
+ title="Answers"
82
+ description="Review your choices"
83
+ items={[{ id: 'name', label: 'Name', value: 'Resource' }]}
84
+ />
85
+ }
86
+ >
87
+ <h2>Configuration</h2>
88
+ <input aria-label="Region" />
89
+ </DialogWizard>,
90
+ );
91
+ await expectNoAxeViolations(await screen.findByRole('dialog'));
92
+ });
93
+ }
94
+ }
95
+ });
@@ -0,0 +1,39 @@
1
+ import { useI18n, WizardLayout, type WizardLayoutProps } from '@gntik-ai/ui';
2
+ import type { ReactNode } from 'react';
3
+
4
+ export interface DialogWizardProps extends Omit<WizardLayoutProps, 'mode' | 'steps' | 'current'> {
5
+ steps?: WizardLayoutProps['steps'];
6
+ current?: number;
7
+ /** Step-local validation surface, normally a ValidationSummary with consumer copy. */
8
+ stepError?: ReactNode;
9
+ /** Replaces the last step body, normally a DescriptionListCard with consumer copy. */
10
+ reviewStep?: ReactNode;
11
+ }
12
+
13
+ /** Modal variant of create-wizard for a flow over its parent page. State stays consumer-owned. */
14
+ export function DialogWizard({
15
+ steps,
16
+ current = 0,
17
+ size = 'dialog',
18
+ stepError,
19
+ reviewStep,
20
+ children,
21
+ title,
22
+ ...props
23
+ }: DialogWizardProps) {
24
+ const { t } = useI18n();
25
+ const items = steps ?? [{ label: t('wizard.details') }, { label: t('wizard.review') }];
26
+ return (
27
+ <WizardLayout
28
+ {...props}
29
+ mode="overlay"
30
+ size={size}
31
+ steps={items}
32
+ current={current}
33
+ title={title ?? t('wizard.title')}
34
+ >
35
+ {stepError}
36
+ {current >= items.length - 1 && reviewStep != null ? reviewStep : children}
37
+ </WizardLayout>
38
+ );
39
+ }
@@ -0,0 +1,10 @@
1
+ import type { BlockMeta } from '../../meta';
2
+
3
+ export const meta: BlockMeta = {
4
+ name: 'Dialog wizard',
5
+ family: 'forms',
6
+ status: 'beta',
7
+ description:
8
+ 'Modal variant of create-wizard for create flows over their parent page, without a route change. Defaults to dialog size (fullscreen on mobile); size="fullscreen" fills every viewport. Consumer-controlled step state survives closing. Use the create-wizard page template for a dedicated page flow, or WizardLayout mode="overlay" for a fullscreen flow. Supply all labels, pendingLabel and stepAnnouncement from your own i18n; stepError accepts ValidationSummary and reviewStep accepts DescriptionListCard.',
9
+ uses: ['WizardLayout', 'Dialog', 'Stepper', 'Button'],
10
+ };