@gntik-ai/blocks 0.0.0-stage → 0.5.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 (385) hide show
  1. package/dist/index.d.ts +3494 -0
  2. package/dist/index.js +14911 -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.test.tsx +33 -0
  49. package/src/auth/SignUpForm/SignUpForm.tsx +154 -0
  50. package/src/auth/SignUpForm/block.meta.ts +9 -0
  51. package/src/auth/SsoButtons/SsoButtons.test.tsx +23 -0
  52. package/src/auth/SsoButtons/SsoButtons.tsx +52 -0
  53. package/src/auth/SsoButtons/block.meta.ts +9 -0
  54. package/src/auth/SsoButtons/fixtures.ts +15 -0
  55. package/src/auth/index.ts +15 -0
  56. package/src/auth/shared.tsx +113 -0
  57. package/src/billing/CostBreakdown/CostBreakdown.test.tsx +24 -0
  58. package/src/billing/CostBreakdown/CostBreakdown.tsx +95 -0
  59. package/src/billing/CostBreakdown/block.meta.ts +10 -0
  60. package/src/billing/CostBreakdown/fixtures.ts +9 -0
  61. package/src/billing/InvoiceTable/InvoiceTable.test.tsx +29 -0
  62. package/src/billing/InvoiceTable/InvoiceTable.tsx +130 -0
  63. package/src/billing/InvoiceTable/block.meta.ts +10 -0
  64. package/src/billing/InvoiceTable/fixtures.ts +38 -0
  65. package/src/billing/PaymentMethodCard/PaymentMethodCard.test.tsx +28 -0
  66. package/src/billing/PaymentMethodCard/PaymentMethodCard.tsx +103 -0
  67. package/src/billing/PaymentMethodCard/block.meta.ts +10 -0
  68. package/src/billing/PaymentMethodCard/fixtures.ts +12 -0
  69. package/src/billing/PlanCard/PlanCard.baseline.test.tsx +26 -0
  70. package/src/billing/PlanCard/PlanCard.summary.test.tsx +227 -0
  71. package/src/billing/PlanCard/PlanCard.test.tsx +27 -0
  72. package/src/billing/PlanCard/PlanCard.tsx +140 -0
  73. package/src/billing/PlanCard/PlanSummary.tsx +101 -0
  74. package/src/billing/PlanCard/README.md +23 -0
  75. package/src/billing/PlanCard/__snapshots__/PlanCard.baseline.test.tsx.snap +5 -0
  76. package/src/billing/PlanCard/block.meta.ts +60 -0
  77. package/src/billing/PlanCard/examples/SummaryActionsOnly.tsx +12 -0
  78. package/src/billing/PlanCard/examples/SummaryFeaturesOnly.tsx +14 -0
  79. package/src/billing/PlanCard/examples/SummaryFull.tsx +28 -0
  80. package/src/billing/PlanCard/examples/SummaryMinimal.tsx +5 -0
  81. package/src/billing/PlanCard/examples/SummaryPriceOnly.tsx +15 -0
  82. package/src/billing/PlanCard/examples/SummaryRenewalOnly.tsx +14 -0
  83. package/src/billing/PlanCard/examples/SummaryUnassigned.tsx +5 -0
  84. package/src/billing/PlanCard/examples/SummaryUnassignedNoLabel.tsx +5 -0
  85. package/src/billing/PlanCard/examples/SummaryZeroPrice.tsx +15 -0
  86. package/src/billing/PlanCard/fixtures.ts +19 -0
  87. package/src/billing/PlanDefinitionCard/PlanDefinitionCard.test.tsx +111 -0
  88. package/src/billing/PlanDefinitionCard/PlanDefinitionCard.tsx +86 -0
  89. package/src/billing/PlanDefinitionCard/README.md +29 -0
  90. package/src/billing/PlanDefinitionCard/block.meta.ts +46 -0
  91. package/src/billing/PlanDefinitionCard/examples/Compact.tsx +23 -0
  92. package/src/billing/PlanDefinitionCard/examples/Full.tsx +23 -0
  93. package/src/billing/PlanDefinitionCard/fixtures.ts +12 -0
  94. package/src/billing/QuotaMeters/QuotaMeters.test.tsx +23 -0
  95. package/src/billing/QuotaMeters/QuotaMeters.tsx +92 -0
  96. package/src/billing/QuotaMeters/block.meta.ts +10 -0
  97. package/src/billing/QuotaMeters/fixtures.ts +9 -0
  98. package/src/billing/SpendVsBudget/SpendVsBudget.test.tsx +24 -0
  99. package/src/billing/SpendVsBudget/SpendVsBudget.tsx +105 -0
  100. package/src/billing/SpendVsBudget/block.meta.ts +10 -0
  101. package/src/billing/SpendVsBudget/fixtures.ts +18 -0
  102. package/src/billing/format.ts +42 -0
  103. package/src/billing/index.ts +21 -0
  104. package/src/builders/CodePanel/CodePanel.test.tsx +280 -0
  105. package/src/builders/CodePanel/CodePanel.tsx +239 -0
  106. package/src/builders/CodePanel/block.meta.ts +10 -0
  107. package/src/builders/CodePanel/examples/controlled.tsx +15 -0
  108. package/src/builders/CodePanel/examples/diagnostics.tsx +40 -0
  109. package/src/builders/CodePanel/fixtures.ts +95 -0
  110. package/src/builders/CodePanel/marker-store.ts +90 -0
  111. package/src/builders/CodePanel/markers.ts +62 -0
  112. package/src/builders/FlowBuilder/FlowBuilder.test.tsx +480 -0
  113. package/src/builders/FlowBuilder/FlowBuilder.tsx +343 -0
  114. package/src/builders/FlowBuilder/block.meta.ts +20 -0
  115. package/src/builders/FlowBuilder/connections.ts +67 -0
  116. package/src/builders/FlowBuilder/decorated-nodes.ts +80 -0
  117. package/src/builders/FlowBuilder/examples/extensions.tsx +120 -0
  118. package/src/builders/FlowBuilder/extensions.ts +37 -0
  119. package/src/builders/FlowBuilder/fixtures.ts +80 -0
  120. package/src/builders/FlowBuilder/parts.tsx +196 -0
  121. package/src/builders/FlowBuilder/problems.tsx +40 -0
  122. package/src/builders/LogViewer/LogViewer.test.tsx +97 -0
  123. package/src/builders/LogViewer/LogViewer.tsx +253 -0
  124. package/src/builders/LogViewer/block.meta.ts +10 -0
  125. package/src/builders/LogViewer/fixtures.ts +49 -0
  126. package/src/builders/index.ts +15 -0
  127. package/src/data-display/ActivityFeed/ActivityFeed.test.tsx +62 -0
  128. package/src/data-display/ActivityFeed/ActivityFeed.tsx +118 -0
  129. package/src/data-display/ActivityFeed/block.meta.ts +10 -0
  130. package/src/data-display/ActivityFeed/fixtures.ts +29 -0
  131. package/src/data-display/ChartCard/ChartCard.ranges.test.tsx +282 -0
  132. package/src/data-display/ChartCard/ChartCard.test.tsx +54 -0
  133. package/src/data-display/ChartCard/ChartCard.tsx +218 -0
  134. package/src/data-display/ChartCard/block.meta.ts +10 -0
  135. package/src/data-display/ChartCard/examples/custom-ranges.tsx +37 -0
  136. package/src/data-display/ChartCard/examples/heatmap.tsx +20 -0
  137. package/src/data-display/ChartCard/examples/toolbar-owned-range.tsx +34 -0
  138. package/src/data-display/ChartCard/fixtures.ts +82 -0
  139. package/src/data-display/CommentThread/CommentThread.test.tsx +29 -0
  140. package/src/data-display/CommentThread/CommentThread.tsx +120 -0
  141. package/src/data-display/CommentThread/block.meta.ts +10 -0
  142. package/src/data-display/CommentThread/fixtures.ts +28 -0
  143. package/src/data-display/DescriptionListCard/DescriptionListCard.test.tsx +28 -0
  144. package/src/data-display/DescriptionListCard/DescriptionListCard.tsx +120 -0
  145. package/src/data-display/DescriptionListCard/block.meta.ts +9 -0
  146. package/src/data-display/DescriptionListCard/fixtures.ts +12 -0
  147. package/src/data-display/KpiRow/KpiRow.test.tsx +29 -0
  148. package/src/data-display/KpiRow/KpiRow.tsx +101 -0
  149. package/src/data-display/KpiRow/block.meta.ts +10 -0
  150. package/src/data-display/KpiRow/fixtures.ts +34 -0
  151. package/src/data-display/ResourceCardGrid/ResourceCardGrid.test.tsx +22 -0
  152. package/src/data-display/ResourceCardGrid/ResourceCardGrid.tsx +98 -0
  153. package/src/data-display/ResourceCardGrid/block.meta.ts +10 -0
  154. package/src/data-display/ResourceCardGrid/fixtures.ts +72 -0
  155. package/src/data-display/StatCard/StatCard.test.tsx +30 -0
  156. package/src/data-display/StatCard/StatCard.tsx +138 -0
  157. package/src/data-display/StatCard/block.meta.ts +10 -0
  158. package/src/data-display/StatCard/fixtures.ts +26 -0
  159. package/src/data-display/StatusTimeline/StatusTimeline.test.tsx +153 -0
  160. package/src/data-display/StatusTimeline/StatusTimeline.tsx +114 -0
  161. package/src/data-display/StatusTimeline/block.meta.ts +10 -0
  162. package/src/data-display/StatusTimeline/examples/Empty.tsx +6 -0
  163. package/src/data-display/StatusTimeline/fixtures.ts +62 -0
  164. package/src/data-display/index.ts +34 -0
  165. package/src/data-display/shared/TrendDelta.tsx +60 -0
  166. package/src/feedback/ConfirmDestructive/ConfirmDestructive.test.tsx +47 -0
  167. package/src/feedback/ConfirmDestructive/ConfirmDestructive.tsx +145 -0
  168. package/src/feedback/ConfirmDestructive/block.meta.ts +10 -0
  169. package/src/feedback/EmptyStates/EmptyStates.test.tsx +48 -0
  170. package/src/feedback/EmptyStates/EmptyStates.tsx +191 -0
  171. package/src/feedback/EmptyStates/block.meta.ts +10 -0
  172. package/src/feedback/EmptyStates/fixtures.ts +6 -0
  173. package/src/feedback/ErrorPanel/ErrorPanel.test.tsx +37 -0
  174. package/src/feedback/ErrorPanel/ErrorPanel.tsx +146 -0
  175. package/src/feedback/ErrorPanel/block.meta.ts +10 -0
  176. package/src/feedback/InlineCallout/InlineCallout.test.tsx +33 -0
  177. package/src/feedback/InlineCallout/InlineCallout.tsx +81 -0
  178. package/src/feedback/InlineCallout/block.meta.ts +9 -0
  179. package/src/feedback/Skeletons/Skeletons.test.tsx +25 -0
  180. package/src/feedback/Skeletons/Skeletons.tsx +142 -0
  181. package/src/feedback/Skeletons/block.meta.ts +10 -0
  182. package/src/feedback/SystemBanner/SystemBanner.test.tsx +29 -0
  183. package/src/feedback/SystemBanner/SystemBanner.tsx +105 -0
  184. package/src/feedback/SystemBanner/block.meta.ts +9 -0
  185. package/src/feedback/index.ts +14 -0
  186. package/src/feedback/types.ts +13 -0
  187. package/src/forms/CreateApiKeyDialog/CreateApiKeyDialog.test.tsx +36 -0
  188. package/src/forms/CreateApiKeyDialog/CreateApiKeyDialog.tsx +197 -0
  189. package/src/forms/CreateApiKeyDialog/block.meta.ts +10 -0
  190. package/src/forms/CreateApiKeyDialog/fixtures.ts +31 -0
  191. package/src/forms/CredentialDisclosureDialog/CredentialDisclosureDialog.test.tsx +339 -0
  192. package/src/forms/CredentialDisclosureDialog/CredentialDisclosureDialog.tsx +193 -0
  193. package/src/forms/CredentialDisclosureDialog/block.meta.ts +10 -0
  194. package/src/forms/CredentialDisclosureDialog/examples/Fresh.tsx +32 -0
  195. package/src/forms/CredentialDisclosureDialog/examples/ResponseGating.tsx +69 -0
  196. package/src/forms/CredentialDisclosureDialog/examples/Stored.tsx +32 -0
  197. package/src/forms/CredentialDisclosureDialog/fixtures.ts +14 -0
  198. package/src/forms/DangerZone/ConfirmDestructiveDialog.tsx +101 -0
  199. package/src/forms/DangerZone/DangerZone.test.tsx +28 -0
  200. package/src/forms/DangerZone/DangerZone.tsx +86 -0
  201. package/src/forms/DangerZone/block.meta.ts +10 -0
  202. package/src/forms/DangerZone/fixtures.ts +36 -0
  203. package/src/forms/DateRangeFilter/DateRangeFilter.test.tsx +26 -0
  204. package/src/forms/DateRangeFilter/DateRangeFilter.tsx +83 -0
  205. package/src/forms/DateRangeFilter/block.meta.ts +9 -0
  206. package/src/forms/DialogWizard/DialogWizard.test.tsx +95 -0
  207. package/src/forms/DialogWizard/DialogWizard.tsx +39 -0
  208. package/src/forms/DialogWizard/block.meta.ts +10 -0
  209. package/src/forms/DialogWizard/examples/CreateResource.tsx +65 -0
  210. package/src/forms/DialogWizard/fixtures.ts +20 -0
  211. package/src/forms/FileUploadPanel/FileUploadPanel.test.tsx +43 -0
  212. package/src/forms/FileUploadPanel/FileUploadPanel.tsx +244 -0
  213. package/src/forms/FileUploadPanel/block.meta.ts +10 -0
  214. package/src/forms/FileUploadPanel/fixtures.ts +66 -0
  215. package/src/forms/FormSection/FormSection.test.tsx +34 -0
  216. package/src/forms/FormSection/FormSection.tsx +77 -0
  217. package/src/forms/FormSection/block.meta.ts +9 -0
  218. package/src/forms/InviteMembersDialog/InviteMembersDialog.test.tsx +37 -0
  219. package/src/forms/InviteMembersDialog/InviteMembersDialog.tsx +183 -0
  220. package/src/forms/InviteMembersDialog/block.meta.ts +10 -0
  221. package/src/forms/InviteMembersDialog/fixtures.ts +14 -0
  222. package/src/forms/SettingsRow/SettingsRow.test.tsx +50 -0
  223. package/src/forms/SettingsRow/SettingsRow.tsx +74 -0
  224. package/src/forms/SettingsRow/block.meta.ts +9 -0
  225. package/src/forms/ValidationSummary/ValidationSummary.test.tsx +46 -0
  226. package/src/forms/ValidationSummary/ValidationSummary.tsx +75 -0
  227. package/src/forms/ValidationSummary/block.meta.ts +9 -0
  228. package/src/forms/ValidationSummary/fixtures.ts +13 -0
  229. package/src/forms/index.ts +27 -0
  230. package/src/index.ts +14 -0
  231. package/src/marketing/ComparisonTable/ComparisonTable.test.tsx +69 -0
  232. package/src/marketing/ComparisonTable/ComparisonTable.tsx +245 -0
  233. package/src/marketing/ComparisonTable/block.meta.ts +10 -0
  234. package/src/marketing/ComparisonTable/examples/options.tsx +48 -0
  235. package/src/marketing/ComparisonTable/fixtures.ts +83 -0
  236. package/src/marketing/Faq/Faq.test.tsx +25 -0
  237. package/src/marketing/Faq/Faq.tsx +68 -0
  238. package/src/marketing/Faq/block.meta.ts +10 -0
  239. package/src/marketing/Faq/fixtures.ts +32 -0
  240. package/src/marketing/FeatureGrid/FeatureGrid.test.tsx +19 -0
  241. package/src/marketing/FeatureGrid/FeatureGrid.tsx +79 -0
  242. package/src/marketing/FeatureGrid/block.meta.ts +10 -0
  243. package/src/marketing/FeatureGrid/fixtures.ts +36 -0
  244. package/src/marketing/Hero/Hero.test.tsx +33 -0
  245. package/src/marketing/Hero/Hero.tsx +107 -0
  246. package/src/marketing/Hero/block.meta.ts +10 -0
  247. package/src/marketing/PricingTable/PricingTable.test.tsx +92 -0
  248. package/src/marketing/PricingTable/PricingTable.tsx +323 -0
  249. package/src/marketing/PricingTable/block.meta.ts +10 -0
  250. package/src/marketing/PricingTable/fixtures.ts +34 -0
  251. package/src/marketing/actions.tsx +40 -0
  252. package/src/marketing/index.ts +15 -0
  253. package/src/meta.ts +23 -0
  254. package/src/page-chrome/BulkActionBar/BulkActionBar.test.tsx +34 -0
  255. package/src/page-chrome/BulkActionBar/BulkActionBar.tsx +76 -0
  256. package/src/page-chrome/BulkActionBar/block.meta.ts +9 -0
  257. package/src/page-chrome/PageHeader/PageHeader.test.tsx +68 -0
  258. package/src/page-chrome/PageHeader/PageHeader.tsx +152 -0
  259. package/src/page-chrome/PageHeader/block.meta.ts +10 -0
  260. package/src/page-chrome/PageHeader/fixtures.ts +29 -0
  261. package/src/page-chrome/PageToolbar/PageToolbar.test.tsx +38 -0
  262. package/src/page-chrome/PageToolbar/PageToolbar.tsx +138 -0
  263. package/src/page-chrome/PageToolbar/block.meta.ts +10 -0
  264. package/src/page-chrome/PageToolbar/fixtures.ts +8 -0
  265. package/src/page-chrome/SectionHeader/SectionHeader.test.tsx +28 -0
  266. package/src/page-chrome/SectionHeader/SectionHeader.tsx +73 -0
  267. package/src/page-chrome/SectionHeader/block.meta.ts +9 -0
  268. package/src/page-chrome/StickyActionBar/StickyActionBar.test.tsx +29 -0
  269. package/src/page-chrome/StickyActionBar/StickyActionBar.tsx +66 -0
  270. package/src/page-chrome/StickyActionBar/block.meta.ts +9 -0
  271. package/src/page-chrome/index.ts +13 -0
  272. package/src/page-chrome/types.ts +25 -0
  273. package/src/scheduling/AgendaList/AgendaList.test.tsx +28 -0
  274. package/src/scheduling/AgendaList/AgendaList.tsx +165 -0
  275. package/src/scheduling/AgendaList/block.meta.ts +10 -0
  276. package/src/scheduling/MonthCalendar/MonthCalendar.test.tsx +32 -0
  277. package/src/scheduling/MonthCalendar/MonthCalendar.tsx +223 -0
  278. package/src/scheduling/MonthCalendar/block.meta.ts +10 -0
  279. package/src/scheduling/TimeSlotPicker/TimeSlotPicker.test.tsx +26 -0
  280. package/src/scheduling/TimeSlotPicker/TimeSlotPicker.tsx +115 -0
  281. package/src/scheduling/TimeSlotPicker/block.meta.ts +10 -0
  282. package/src/scheduling/TimeSlotPicker/fixtures.ts +10 -0
  283. package/src/scheduling/WeekCalendar/WeekCalendar.test.tsx +27 -0
  284. package/src/scheduling/WeekCalendar/WeekCalendar.tsx +274 -0
  285. package/src/scheduling/WeekCalendar/block.meta.ts +10 -0
  286. package/src/scheduling/index.ts +11 -0
  287. package/src/scheduling/shared/CalendarHeader.tsx +54 -0
  288. package/src/scheduling/shared/EventTile.tsx +21 -0
  289. package/src/scheduling/shared/dates.ts +98 -0
  290. package/src/scheduling/shared/events.ts +90 -0
  291. package/src/scheduling/shared/fixtures.ts +54 -0
  292. package/src/shell/AppSidebar/AppSidebar.test.tsx +38 -0
  293. package/src/shell/AppSidebar/AppSidebar.tsx +226 -0
  294. package/src/shell/AppSidebar/block.meta.ts +10 -0
  295. package/src/shell/AppSidebar/fixtures.ts +58 -0
  296. package/src/shell/AppTopbar/AppTopbar.test.tsx +41 -0
  297. package/src/shell/AppTopbar/AppTopbar.tsx +82 -0
  298. package/src/shell/AppTopbar/block.meta.ts +19 -0
  299. package/src/shell/AppTopbar/fixtures.ts +22 -0
  300. package/src/shell/EnvironmentBadge/EnvironmentBadge.test.tsx +22 -0
  301. package/src/shell/EnvironmentBadge/EnvironmentBadge.tsx +55 -0
  302. package/src/shell/EnvironmentBadge/block.meta.ts +10 -0
  303. package/src/shell/GlobalSearch/GlobalSearch.test.tsx +28 -0
  304. package/src/shell/GlobalSearch/GlobalSearch.tsx +80 -0
  305. package/src/shell/GlobalSearch/block.meta.ts +10 -0
  306. package/src/shell/GlobalSearch/fixtures.ts +64 -0
  307. package/src/shell/index.ts +11 -0
  308. package/src/styles.css +2 -0
  309. package/src/tables/DataTable/DataTable.edit-views.test.tsx +249 -0
  310. package/src/tables/DataTable/DataTable.layout.test.tsx +188 -0
  311. package/src/tables/DataTable/DataTable.single-select.test.tsx +501 -0
  312. package/src/tables/DataTable/DataTable.test.tsx +268 -0
  313. package/src/tables/DataTable/DataTable.tsx +520 -0
  314. package/src/tables/DataTable/block.meta.ts +27 -0
  315. package/src/tables/DataTable/column-menu.tsx +109 -0
  316. package/src/tables/DataTable/column-sizing.ts +166 -0
  317. package/src/tables/DataTable/data-table-body.tsx +194 -0
  318. package/src/tables/DataTable/data-table-props.ts +156 -0
  319. package/src/tables/DataTable/data-table-row.tsx +220 -0
  320. package/src/tables/DataTable/data-table-utils.ts +195 -0
  321. package/src/tables/DataTable/density-toggle.tsx +30 -0
  322. package/src/tables/DataTable/editable-cell.tsx +274 -0
  323. package/src/tables/DataTable/examples/editable.tsx +71 -0
  324. package/src/tables/DataTable/examples/grouped.tsx +18 -0
  325. package/src/tables/DataTable/examples/resizable-pinned.tsx +20 -0
  326. package/src/tables/DataTable/examples/saved-views.tsx +61 -0
  327. package/src/tables/DataTable/examples/single-selection.tsx +55 -0
  328. package/src/tables/DataTable/examples/virtualized.tsx +20 -0
  329. package/src/tables/DataTable/fixtures.ts +119 -0
  330. package/src/tables/DataTable/header-cell.tsx +86 -0
  331. package/src/tables/DataTable/resize-handle.tsx +88 -0
  332. package/src/tables/DataTable/single-selection.tsx +149 -0
  333. package/src/tables/DataTable/use-table-views.ts +79 -0
  334. package/src/tables/DataTable/use-virtual-rows.ts +83 -0
  335. package/src/tables/DataTable/views.tsx +186 -0
  336. package/src/tables/FilterBar/FilterBar.test.tsx +43 -0
  337. package/src/tables/FilterBar/FilterBar.tsx +199 -0
  338. package/src/tables/FilterBar/block.meta.ts +10 -0
  339. package/src/tables/FilterBar/fixtures.ts +32 -0
  340. package/src/tables/InlineEditRow/InlineEditRow.test.tsx +41 -0
  341. package/src/tables/InlineEditRow/InlineEditRow.tsx +215 -0
  342. package/src/tables/InlineEditRow/block.meta.ts +10 -0
  343. package/src/tables/InlineEditRow/fixtures.ts +28 -0
  344. package/src/tables/PaginationFooter/PaginationFooter.test.tsx +29 -0
  345. package/src/tables/PaginationFooter/PaginationFooter.tsx +94 -0
  346. package/src/tables/PaginationFooter/block.meta.ts +10 -0
  347. package/src/tables/SubordinateList/SubordinateList.test.tsx +351 -0
  348. package/src/tables/SubordinateList/SubordinateList.tsx +140 -0
  349. package/src/tables/SubordinateList/block.meta.ts +10 -0
  350. package/src/tables/SubordinateList/examples/ResourceForm.tsx +129 -0
  351. package/src/tables/SubordinateList/examples/empty.tsx +5 -0
  352. package/src/tables/SubordinateList/examples/error.tsx +5 -0
  353. package/src/tables/SubordinateList/examples/loading.tsx +5 -0
  354. package/src/tables/TruncatedCollectionFooter/TruncatedCollectionFooter.test.tsx +159 -0
  355. package/src/tables/TruncatedCollectionFooter/TruncatedCollectionFooter.tsx +60 -0
  356. package/src/tables/TruncatedCollectionFooter/block.meta.ts +10 -0
  357. package/src/tables/TruncatedCollectionFooter/examples/exact.tsx +6 -0
  358. package/src/tables/TruncatedCollectionFooter/examples/has-more.tsx +12 -0
  359. package/src/tables/TruncatedCollectionFooter/fixtures.ts +2 -0
  360. package/src/tables/index.ts +45 -0
  361. package/src/team/MembersTable/MembersTable.test.tsx +29 -0
  362. package/src/team/MembersTable/MembersTable.tsx +141 -0
  363. package/src/team/MembersTable/block.meta.ts +9 -0
  364. package/src/team/MembersTable/fixtures.ts +19 -0
  365. package/src/team/NotificationMatrix/NotificationMatrix.test.tsx +36 -0
  366. package/src/team/NotificationMatrix/NotificationMatrix.tsx +144 -0
  367. package/src/team/NotificationMatrix/block.meta.ts +9 -0
  368. package/src/team/NotificationMatrix/fixtures.ts +43 -0
  369. package/src/team/PendingInvitations/PendingInvitations.test.tsx +30 -0
  370. package/src/team/PendingInvitations/PendingInvitations.tsx +132 -0
  371. package/src/team/PendingInvitations/block.meta.ts +9 -0
  372. package/src/team/PendingInvitations/fixtures.ts +39 -0
  373. package/src/team/ProfileHeader/ProfileHeader.test.tsx +29 -0
  374. package/src/team/ProfileHeader/ProfileHeader.tsx +106 -0
  375. package/src/team/ProfileHeader/block.meta.ts +10 -0
  376. package/src/team/ProfileHeader/fixtures.ts +28 -0
  377. package/src/team/SessionsDevices/SessionsDevices.test.tsx +30 -0
  378. package/src/team/SessionsDevices/SessionsDevices.tsx +126 -0
  379. package/src/team/SessionsDevices/block.meta.ts +10 -0
  380. package/src/team/SessionsDevices/fixtures.ts +58 -0
  381. package/src/team/index.ts +15 -0
  382. package/src/test/a11y.ts +18 -0
  383. package/src/test/heading-levels.test.tsx +69 -0
  384. package/src/test/setup.ts +36 -0
  385. package/README.md +0 -3
@@ -0,0 +1,226 @@
1
+ import {
2
+ Alert,
3
+ Button,
4
+ Checkbox,
5
+ Field,
6
+ FieldDescription,
7
+ FieldError,
8
+ FieldLabel,
9
+ Input,
10
+ Link,
11
+ PasswordInput,
12
+ cn,
13
+ } from '@gntik-ai/ui';
14
+ import { Lock, Mail } from '@gntik-ai/icons';
15
+ import { useImperativeHandle, useRef, useState, type MouseEvent, type ReactNode, type Ref } from 'react';
16
+ import { SsoButtons } from '../SsoButtons/SsoButtons';
17
+ import {
18
+ AuthDivider,
19
+ AuthHeader,
20
+ authIdentifierError,
21
+ authFormClass,
22
+ useAsyncSubmit,
23
+ type AuthIdentifierConfig,
24
+ type AuthValidationMessages,
25
+ type AuthFormAccessibilityProps,
26
+ type AuthHeaderProps,
27
+ } from '../shared';
28
+
29
+ export interface SignInValues {
30
+ email: string;
31
+ /** Trimmed identifier in text mode. `email` remains available for existing consumers. */
32
+ identifier?: string;
33
+ password: string;
34
+ remember: boolean;
35
+ }
36
+
37
+ export interface SignInFormRef {
38
+ focus(field: 'identifier' | 'password'): void;
39
+ }
40
+
41
+ export interface SignInFormProps extends AuthFormAccessibilityProps {
42
+ /** React 19 ref for focus after a server response. */
43
+ ref?: Ref<SignInFormRef>;
44
+ identifier?: AuthIdentifierConfig;
45
+ /** The kit validates before submit. Supply copy here; do not add a second inline validator. */
46
+ messages?: AuthValidationMessages;
47
+ passwordHelp?: ReactNode;
48
+ passwordHelpId?: string;
49
+ /** Signs in. A returned promise shows the loading state; a rejection's message shows in the alert. */
50
+ onSubmit?: (values: SignInValues) => void | Promise<void>;
51
+ /** Error from the server (e.g. "Incorrect email or password"). */
52
+ error?: string | null;
53
+ /** Forces the loading state (when the parent tracks the request). */
54
+ loading?: boolean;
55
+ defaultEmail?: string;
56
+ forgotPasswordHref?: string;
57
+ onForgotPassword?: () => void;
58
+ signUpHref?: string;
59
+ onSignUp?: () => void;
60
+ /** Replaces the sign-up prompt; `null` hides it. Defaults to the existing account link. */
61
+ signUpPrompt?: ReactNode;
62
+ /** SSO area under the divider; defaults to SsoButtons, `null` hides it. */
63
+ sso?: ReactNode;
64
+ eyebrow?: ReactNode;
65
+ title?: ReactNode;
66
+ description?: ReactNode;
67
+ /** Omit the internal heading with null; the form keeps its accessible name. */
68
+ headingLevel?: AuthHeaderProps['headingLevel'];
69
+ className?: string;
70
+ }
71
+
72
+ const linkHandler = (fn?: () => void) =>
73
+ fn
74
+ ? (e: MouseEvent) => {
75
+ e.preventDefault();
76
+ fn();
77
+ }
78
+ : undefined;
79
+
80
+ /** Email + password sign-in with remember me, forgot-password link, SSO slot, error alert and loading. */
81
+ export function SignInForm({
82
+ identifier,
83
+ messages,
84
+ passwordHelp,
85
+ passwordHelpId,
86
+ ref,
87
+ fieldInvalid,
88
+ feedbackId,
89
+ 'aria-describedby': describedBy,
90
+ 'aria-label': ariaLabel = 'Sign in',
91
+ 'aria-labelledby': labelledBy,
92
+ onSubmit,
93
+ error: errorProp,
94
+ loading: loadingProp,
95
+ defaultEmail = '',
96
+ forgotPasswordHref = '#',
97
+ onForgotPassword,
98
+ signUpHref = '#',
99
+ onSignUp,
100
+ signUpPrompt,
101
+ sso = <SsoButtons />,
102
+ eyebrow = 'Secure access',
103
+ title = 'Sign in to your workspace',
104
+ description = 'Use your work email to continue.',
105
+ headingLevel,
106
+ className,
107
+ }: SignInFormProps) {
108
+ const [email, setEmail] = useState(defaultEmail);
109
+ const [password, setPassword] = useState('');
110
+ const [remember, setRemember] = useState(true);
111
+ const [submitted, setSubmitted] = useState(false);
112
+ const { loading, error, run } = useAsyncSubmit('Sign-in failed. Try again.');
113
+ const busy = loadingProp ?? loading;
114
+ const alert = errorProp ?? error;
115
+ const identifierInput = useRef<HTMLInputElement>(null);
116
+ const passwordInput = useRef<HTMLInputElement>(null);
117
+ useImperativeHandle(
118
+ ref,
119
+ () => ({
120
+ focus: (field) => (field === 'password' ? passwordInput : identifierInput).current?.focus(),
121
+ }),
122
+ [],
123
+ );
124
+ const identifierError = authIdentifierError(email, identifier, messages);
125
+ const emailInvalid = submitted && identifierError !== null;
126
+ const passwordInvalid = submitted && password === '';
127
+
128
+ return (
129
+ <div className={cn(authFormClass, className)}>
130
+ <AuthHeader eyebrow={eyebrow} title={title} description={description} headingLevel={headingLevel} />
131
+ <form
132
+ noValidate
133
+ aria-label={ariaLabel}
134
+ aria-labelledby={labelledBy}
135
+ aria-describedby={describedBy}
136
+ className="flex flex-col gap-4"
137
+ onSubmit={(e) => {
138
+ e.preventDefault();
139
+ setSubmitted(true);
140
+ if (identifierError !== null) {
141
+ identifierInput.current?.focus();
142
+ return;
143
+ }
144
+ if (password === '') {
145
+ passwordInput.current?.focus();
146
+ return;
147
+ }
148
+ void run(() =>
149
+ onSubmit?.({
150
+ email: email.trim(),
151
+ ...(identifier?.type === 'text' ? { identifier: email.trim() } : {}),
152
+ password,
153
+ remember,
154
+ }),
155
+ );
156
+ }}
157
+ >
158
+ {alert && <Alert id={feedbackId} tone="destructive" description={alert} />}
159
+ <Field invalid={emailInvalid || fieldInvalid?.identifier === true}>
160
+ <FieldLabel>{identifier?.label ?? 'Email'}</FieldLabel>
161
+ <Input
162
+ ref={identifierInput}
163
+ aria-describedby={fieldInvalid?.identifier ? feedbackId : undefined}
164
+ type={identifier?.type ?? 'email'}
165
+ leadingIcon={Mail}
166
+ autoComplete={identifier?.autoComplete ?? 'email'}
167
+ placeholder={identifier?.placeholder ?? 'name@company.com'}
168
+ minLength={identifier?.minLength}
169
+ maxLength={identifier?.maxLength}
170
+ value={email}
171
+ onValueChange={setEmail}
172
+ required
173
+ />
174
+ {identifier?.help != null && <FieldDescription id={identifier.helpId}>{identifier.help}</FieldDescription>}
175
+ <FieldError role="alert" match={emailInvalid}>
176
+ {identifierError}
177
+ </FieldError>
178
+ </Field>
179
+ <Field invalid={passwordInvalid || fieldInvalid?.password === true}>
180
+ <div className="flex items-center justify-between gap-3">
181
+ <FieldLabel>Password</FieldLabel>
182
+ <Link href={forgotPasswordHref} onClick={linkHandler(onForgotPassword)} className="text-[12.5px]">
183
+ Forgot password?
184
+ </Link>
185
+ </div>
186
+ <PasswordInput
187
+ ref={passwordInput}
188
+ aria-describedby={fieldInvalid?.password ? feedbackId : undefined}
189
+ leadingIcon={Lock}
190
+ value={password}
191
+ onValueChange={setPassword}
192
+ required
193
+ />
194
+ {passwordHelp != null && <FieldDescription id={passwordHelpId}>{passwordHelp}</FieldDescription>}
195
+ <FieldError role="alert" match={passwordInvalid}>
196
+ {messages?.passwordRequired ?? 'Enter your password.'}
197
+ </FieldError>
198
+ </Field>
199
+ <Checkbox label="Keep me signed in" checked={remember} onCheckedChange={setRemember} />
200
+ <Button type="submit" size="lg" loading={busy} className="mt-2 w-full">
201
+ Sign in
202
+ </Button>
203
+ </form>
204
+ {sso != null && (
205
+ <>
206
+ <AuthDivider>Or continue with</AuthDivider>
207
+ {sso}
208
+ </>
209
+ )}
210
+ {signUpPrompt !== null && (
211
+ <p className="mt-7 text-center text-[13px] text-muted-foreground">
212
+ {signUpPrompt === undefined ? (
213
+ <>
214
+ New here?{' '}
215
+ <Link href={signUpHref} onClick={linkHandler(onSignUp)}>
216
+ Create an account
217
+ </Link>
218
+ </>
219
+ ) : (
220
+ signUpPrompt
221
+ )}
222
+ </p>
223
+ )}
224
+ </div>
225
+ );
226
+ }
@@ -0,0 +1,22 @@
1
+ import type { BlockMeta } from '../../meta';
2
+
3
+ export const meta: BlockMeta = {
4
+ name: 'Sign-in form',
5
+ family: 'auth',
6
+ status: 'stable',
7
+ description:
8
+ 'Email (default) or text identifier and password sign-in. Supports identifier (type, autoComplete, label, help/helpId, placeholder, minLength/maxLength), messages (identifierRequired/identifierInvalid/identifierTooShort/passwordRequired), and React 19 ref.focus. The kit owns inline validation and focuses the first invalid field; consumers supply messages, not a second inline validator. Server errors use error + fieldInvalid + feedbackId; aria-describedby describes the form, while flagged inputs also retain their help and error ids. headingLevel (h1/h2/h3/null) suppresses the heading without losing the form name; aria-label/aria-labelledby override the default name. passwordHelp/passwordHelpId describe the password. Text submissions add values.identifier and retain values.email; email-mode payloads are unchanged. sso and signUpPrompt accept replacements or null. Preview: sign-in-form--username.',
9
+ uses: [
10
+ 'Field',
11
+ 'FieldLabel',
12
+ 'FieldDescription',
13
+ 'FieldError',
14
+ 'Input',
15
+ 'PasswordInput',
16
+ 'Checkbox',
17
+ 'Button',
18
+ 'Alert',
19
+ 'Link',
20
+ 'SsoButtons',
21
+ ],
22
+ };
@@ -0,0 +1,26 @@
1
+ import { SignInForm, type SignInFormProps } from '../SignInForm';
2
+
3
+ /** /preview.html?kind=block&id=sign-in-form--username */
4
+ export default function Username(props: Pick<SignInFormProps, 'headingLevel'>) {
5
+ return (
6
+ <SignInForm
7
+ {...props}
8
+ identifier={{
9
+ type: 'text',
10
+ autoComplete: 'username',
11
+ label: 'Username',
12
+ help: 'Use your account name.',
13
+ placeholder: 'Account name',
14
+ minLength: 3,
15
+ maxLength: 120,
16
+ }}
17
+ messages={{
18
+ identifierRequired: 'Enter your username.',
19
+ identifierInvalid: 'Use at least three characters.',
20
+ passwordRequired: 'Enter your password.',
21
+ }}
22
+ description="Use your account name to continue."
23
+ sso={null}
24
+ />
25
+ );
26
+ }
@@ -0,0 +1,33 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { expectNoAxeViolations } from '../../test/a11y';
4
+ import { SignUpForm } from './SignUpForm';
5
+
6
+ describe('SignUpForm', () => {
7
+ it('renders the default form', async () => {
8
+ const { container } = render(<SignUpForm />);
9
+ expect(screen.getByRole('form', { name: 'Create account' })).toBeInTheDocument();
10
+ await expectNoAxeViolations(container);
11
+ });
12
+
13
+ it('requires the terms and submits the values', async () => {
14
+ const onSubmit = vi.fn();
15
+ const { container } = render(<SignUpForm onSubmit={onSubmit} sso={null} />);
16
+ await userEvent.type(screen.getByLabelText('Full name'), 'Avery Collins');
17
+ await userEvent.type(screen.getByLabelText('Work email'), 'avery@example.com');
18
+ await userEvent.type(screen.getByLabelText('Password'), 'correct-horse-42');
19
+ expect(screen.getByText(/Strength:/)).toBeInTheDocument();
20
+ await userEvent.click(screen.getByRole('button', { name: 'Create account' }));
21
+ expect(onSubmit).not.toHaveBeenCalled();
22
+ const terms = screen.getByRole('checkbox', { name: /I agree/ });
23
+ expect(terms).toHaveAccessibleDescription('Accept the terms to continue.');
24
+ await expectNoAxeViolations(container);
25
+ await userEvent.click(terms);
26
+ await userEvent.click(screen.getByRole('button', { name: 'Create account' }));
27
+ expect(onSubmit).toHaveBeenCalledWith({
28
+ name: 'Avery Collins',
29
+ email: 'avery@example.com',
30
+ password: 'correct-horse-42',
31
+ });
32
+ });
33
+ });
@@ -0,0 +1,154 @@
1
+ import { Alert, Button, Checkbox, Field, FieldError, FieldLabel, Input, Link, PasswordInput, cn } from '@gntik-ai/ui';
2
+ import { Mail, User } from '@gntik-ai/icons';
3
+ import { useId, useState, type MouseEvent, type ReactNode } from 'react';
4
+ import { SsoButtons } from '../SsoButtons/SsoButtons';
5
+ import { AuthDivider, AuthHeader, EMAIL_RE, authFormClass, useAsyncSubmit } from '../shared';
6
+
7
+ export interface SignUpValues {
8
+ name: string;
9
+ email: string;
10
+ password: string;
11
+ }
12
+
13
+ export interface SignUpFormProps {
14
+ /** Creates the account. A returned promise shows the loading state; a rejection shows in the alert. */
15
+ onSubmit?: (values: SignUpValues) => void | Promise<void>;
16
+ error?: string | null;
17
+ loading?: boolean;
18
+ /** Minimum password length. */
19
+ minPasswordLength?: number;
20
+ termsHref?: string;
21
+ privacyHref?: string;
22
+ signInHref?: string;
23
+ onSignIn?: () => void;
24
+ /** SSO area under the divider; defaults to SsoButtons, `null` hides it. */
25
+ sso?: ReactNode;
26
+ eyebrow?: ReactNode;
27
+ title?: ReactNode;
28
+ description?: ReactNode;
29
+ className?: string;
30
+ }
31
+
32
+ /** Account creation: name, work email, password with a strength meter and a required terms checkbox. */
33
+ export function SignUpForm({
34
+ onSubmit,
35
+ error: errorProp,
36
+ loading: loadingProp,
37
+ minPasswordLength = 8,
38
+ termsHref = '#',
39
+ privacyHref = '#',
40
+ signInHref = '#',
41
+ onSignIn,
42
+ sso = <SsoButtons />,
43
+ eyebrow = 'Get started',
44
+ title = 'Create your account',
45
+ description = 'Set up a workspace for your team in a couple of minutes.',
46
+ className,
47
+ }: SignUpFormProps) {
48
+ const termsErrorId = useId();
49
+ const [name, setName] = useState('');
50
+ const [email, setEmail] = useState('');
51
+ const [password, setPassword] = useState('');
52
+ const [terms, setTerms] = useState(false);
53
+ const [submitted, setSubmitted] = useState(false);
54
+ const { loading, error, run } = useAsyncSubmit('The account could not be created.');
55
+ const busy = loadingProp ?? loading;
56
+ const alert = errorProp ?? error;
57
+ const nameInvalid = submitted && name.trim() === '';
58
+ const emailInvalid = submitted && !EMAIL_RE.test(email.trim());
59
+ const passwordInvalid = submitted && password.length < minPasswordLength;
60
+ const termsInvalid = submitted && !terms;
61
+
62
+ return (
63
+ <div className={cn(authFormClass, className)}>
64
+ <AuthHeader eyebrow={eyebrow} title={title} description={description} />
65
+ <form
66
+ noValidate
67
+ aria-label="Create account"
68
+ className="flex flex-col gap-4"
69
+ onSubmit={(e) => {
70
+ e.preventDefault();
71
+ setSubmitted(true);
72
+ if (name.trim() === '' || !EMAIL_RE.test(email.trim()) || password.length < minPasswordLength || !terms)
73
+ return;
74
+ void run(() => onSubmit?.({ name: name.trim(), email: email.trim(), password }));
75
+ }}
76
+ >
77
+ {alert && <Alert tone="destructive" description={alert} />}
78
+ <Field invalid={nameInvalid}>
79
+ <FieldLabel>Full name</FieldLabel>
80
+ <Input
81
+ leadingIcon={User}
82
+ autoComplete="name"
83
+ placeholder="Avery Collins"
84
+ value={name}
85
+ onValueChange={setName}
86
+ required
87
+ />
88
+ <FieldError match={nameInvalid}>Enter your name.</FieldError>
89
+ </Field>
90
+ <Field invalid={emailInvalid}>
91
+ <FieldLabel>Work email</FieldLabel>
92
+ <Input
93
+ type="email"
94
+ leadingIcon={Mail}
95
+ autoComplete="email"
96
+ placeholder="name@company.com"
97
+ value={email}
98
+ onValueChange={setEmail}
99
+ required
100
+ />
101
+ <FieldError match={emailInvalid}>Enter a valid email address.</FieldError>
102
+ </Field>
103
+ <Field invalid={passwordInvalid}>
104
+ <FieldLabel>Password</FieldLabel>
105
+ <PasswordInput showStrength value={password} onValueChange={setPassword} required />
106
+ <FieldError match={passwordInvalid}>Use at least {minPasswordLength} characters.</FieldError>
107
+ </Field>
108
+ <div>
109
+ <Checkbox
110
+ checked={terms}
111
+ onCheckedChange={setTerms}
112
+ aria-invalid={termsInvalid || undefined}
113
+ aria-describedby={termsInvalid ? termsErrorId : undefined}
114
+ label={
115
+ <>
116
+ I agree to the <Link href={termsHref}>Terms</Link> and <Link href={privacyHref}>Privacy Policy</Link>.
117
+ </>
118
+ }
119
+ />
120
+ {termsInvalid && (
121
+ <p id={termsErrorId} className="mt-1.5 text-[12.5px] text-destructive-text">
122
+ Accept the terms to continue.
123
+ </p>
124
+ )}
125
+ </div>
126
+ <Button type="submit" size="lg" loading={busy} className="mt-2 w-full">
127
+ Create account
128
+ </Button>
129
+ </form>
130
+ {sso != null && (
131
+ <>
132
+ <AuthDivider>Or sign up with</AuthDivider>
133
+ {sso}
134
+ </>
135
+ )}
136
+ <p className="mt-7 text-center text-[13px] text-muted-foreground">
137
+ Already have an account?{' '}
138
+ <Link
139
+ href={signInHref}
140
+ onClick={
141
+ onSignIn
142
+ ? (e: MouseEvent) => {
143
+ e.preventDefault();
144
+ onSignIn();
145
+ }
146
+ : undefined
147
+ }
148
+ >
149
+ Sign in
150
+ </Link>
151
+ </p>
152
+ </div>
153
+ );
154
+ }
@@ -0,0 +1,9 @@
1
+ import type { BlockMeta } from '../../meta';
2
+
3
+ export const meta: BlockMeta = {
4
+ name: 'Sign-up form',
5
+ family: 'auth',
6
+ status: 'stable',
7
+ description: 'Account creation with name, email, a password strength meter and a required terms checkbox.',
8
+ uses: ['Field', 'Input', 'PasswordInput', 'Checkbox', 'Button', 'Alert', 'Link', 'SsoButtons'],
9
+ };
@@ -0,0 +1,23 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { expectNoAxeViolations } from '../../test/a11y';
4
+ import { SsoButtons } from './SsoButtons';
5
+
6
+ describe('SsoButtons', () => {
7
+ it('renders the default providers in a named group', async () => {
8
+ const { container } = render(<SsoButtons />);
9
+ expect(screen.getByRole('group', { name: 'Single sign-on' })).toBeInTheDocument();
10
+ expect(screen.getByRole('button', { name: 'Continue with SSO' })).toBeInTheDocument();
11
+ await expectNoAxeViolations(container);
12
+ });
13
+
14
+ it('reports the chosen provider and shows its loading state', async () => {
15
+ const onSelect = vi.fn();
16
+ const { rerender } = render(<SsoButtons onSelect={onSelect} />);
17
+ await userEvent.click(screen.getByRole('button', { name: 'Continue with Google' }));
18
+ expect(onSelect).toHaveBeenCalledWith('google');
19
+ rerender(<SsoButtons onSelect={onSelect} loadingId="google" />);
20
+ expect(screen.getByRole('button', { name: 'Continue with Google' })).toHaveAttribute('aria-busy', 'true');
21
+ expect(screen.getByRole('button', { name: 'Continue with SSO' })).toBeDisabled();
22
+ });
23
+ });
@@ -0,0 +1,52 @@
1
+ import { Button, cn } from '@gntik-ai/ui';
2
+ import { ssoProviders, type SsoProvider } from './fixtures';
3
+
4
+ export type { SsoProvider } from './fixtures';
5
+
6
+ export interface SsoButtonsProps {
7
+ providers?: SsoProvider[];
8
+ /** Called with the provider id. */
9
+ onSelect?: (providerId: string) => void;
10
+ /** Provider currently redirecting (shows a spinner and disables the others). */
11
+ loadingId?: string | null;
12
+ disabled?: boolean;
13
+ /** `stack` (full-width rows, default) or `grid` (two columns from `sm`). */
14
+ layout?: 'stack' | 'grid';
15
+ /** Accessible name of the group. */
16
+ label?: string;
17
+ className?: string;
18
+ }
19
+
20
+ /** Neutral single-sign-on buttons: text labels and lucide icons, no brand colours or logos. */
21
+ export function SsoButtons({
22
+ providers = ssoProviders,
23
+ onSelect,
24
+ loadingId = null,
25
+ disabled = false,
26
+ layout = 'stack',
27
+ label = 'Single sign-on',
28
+ className,
29
+ }: SsoButtonsProps) {
30
+ return (
31
+ <div
32
+ role="group"
33
+ aria-label={label}
34
+ className={cn('grid gap-2.5', layout === 'grid' && 'sm:grid-cols-2', className)}
35
+ >
36
+ {providers.map((p) => (
37
+ <Button
38
+ key={p.id}
39
+ variant="secondary"
40
+ size="lg"
41
+ icon={p.icon}
42
+ loading={loadingId === p.id}
43
+ disabled={disabled || (loadingId != null && loadingId !== p.id)}
44
+ onClick={() => onSelect?.(p.id)}
45
+ className="w-full text-[13.5px] font-medium"
46
+ >
47
+ {p.label}
48
+ </Button>
49
+ ))}
50
+ </div>
51
+ );
52
+ }
@@ -0,0 +1,9 @@
1
+ import type { BlockMeta } from '../../meta';
2
+
3
+ export const meta: BlockMeta = {
4
+ name: 'SSO buttons',
5
+ family: 'auth',
6
+ status: 'stable',
7
+ description: 'Neutral "Continue with …" provider buttons with lucide icons and a per-provider loading state.',
8
+ uses: ['Button'],
9
+ };
@@ -0,0 +1,15 @@
1
+ import { Building2, Globe, KeyRound, type LucideIcon } from '@gntik-ai/icons';
2
+
3
+ export interface SsoProvider {
4
+ id: string;
5
+ /** Full button text, e.g. "Continue with SSO". */
6
+ label: string;
7
+ /** A neutral lucide icon (no brand logos or colours). */
8
+ icon?: LucideIcon;
9
+ }
10
+
11
+ export const ssoProviders: SsoProvider[] = [
12
+ { id: 'sso', label: 'Continue with SSO', icon: Building2 },
13
+ { id: 'google', label: 'Continue with Google', icon: Globe },
14
+ { id: 'passkey', label: 'Continue with a passkey', icon: KeyRound },
15
+ ];
@@ -0,0 +1,15 @@
1
+ export * from './ForgotPasswordForm/ForgotPasswordForm';
2
+ export { meta as forgotPasswordFormMeta } from './ForgotPasswordForm/block.meta';
3
+ export * from './MfaChallenge/MfaChallenge';
4
+ export { meta as mfaChallengeMeta } from './MfaChallenge/block.meta';
5
+ export * from './ResetPasswordForm/ResetPasswordForm';
6
+ export { meta as resetPasswordFormMeta } from './ResetPasswordForm/block.meta';
7
+ export * from './SignInForm/SignInForm';
8
+ export { meta as signInFormMeta } from './SignInForm/block.meta';
9
+ export * from './SignUpForm/SignUpForm';
10
+ export { meta as signUpFormMeta } from './SignUpForm/block.meta';
11
+ export * from './SsoButtons/SsoButtons';
12
+ export { meta as ssoButtonsMeta } from './SsoButtons/block.meta';
13
+ export { ssoProviders } from './SsoButtons/fixtures';
14
+ export { AuthHeader, AuthDivider, type AuthHeaderProps } from './shared';
15
+ export type { AuthIdentifierConfig, AuthValidationMessages, AuthFormAccessibilityProps } from './shared';