@miragon/mcp-toolkit-ui 2.6.0 → 2.7.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 (348) hide show
  1. package/README.md +7 -1
  2. package/dist/app/builder/BuilderBanners.d.ts +7 -2
  3. package/dist/app/builder/BuilderBanners.d.ts.map +1 -1
  4. package/dist/app/builder/BuilderBanners.js +8 -3
  5. package/dist/app/builder/BuilderBanners.js.map +1 -1
  6. package/dist/app/builder/CanvasRow.d.ts.map +1 -1
  7. package/dist/app/builder/CanvasRow.js +12 -14
  8. package/dist/app/builder/CanvasRow.js.map +1 -1
  9. package/dist/app/builder/CatalogueSheet.d.ts.map +1 -1
  10. package/dist/app/builder/CatalogueSheet.js +6 -5
  11. package/dist/app/builder/CatalogueSheet.js.map +1 -1
  12. package/dist/app/builder/EmptyHint.d.ts +4 -0
  13. package/dist/app/builder/EmptyHint.d.ts.map +1 -1
  14. package/dist/app/builder/EmptyHint.js +6 -1
  15. package/dist/app/builder/EmptyHint.js.map +1 -1
  16. package/dist/app/builder/LayoutSurface.js +3 -3
  17. package/dist/app/builder/LayoutSurface.js.map +1 -1
  18. package/dist/app/builder/PipelineStrip.d.ts +3 -1
  19. package/dist/app/builder/PipelineStrip.d.ts.map +1 -1
  20. package/dist/app/builder/PipelineStrip.js +11 -6
  21. package/dist/app/builder/PipelineStrip.js.map +1 -1
  22. package/dist/app/builder/SaveDialog.d.ts.map +1 -1
  23. package/dist/app/builder/SaveDialog.js +2 -2
  24. package/dist/app/builder/SaveDialog.js.map +1 -1
  25. package/dist/app/builder/Toolbar.d.ts +3 -1
  26. package/dist/app/builder/Toolbar.d.ts.map +1 -1
  27. package/dist/app/builder/Toolbar.js +9 -3
  28. package/dist/app/builder/Toolbar.js.map +1 -1
  29. package/dist/app/builder/Workspace.js +2 -2
  30. package/dist/app/builder/Workspace.js.map +1 -1
  31. package/dist/app/builder/builder-model.d.ts +3 -2
  32. package/dist/app/builder/builder-model.d.ts.map +1 -1
  33. package/dist/app/builder/builder-model.js +5 -4
  34. package/dist/app/builder/builder-model.js.map +1 -1
  35. package/dist/app/builder/labels.d.ts +13 -62
  36. package/dist/app/builder/labels.d.ts.map +1 -1
  37. package/dist/app/builder/labels.js +9 -58
  38. package/dist/app/builder/labels.js.map +1 -1
  39. package/dist/app/builder/status-chip.d.ts.map +1 -1
  40. package/dist/app/builder/status-chip.js +3 -2
  41. package/dist/app/builder/status-chip.js.map +1 -1
  42. package/dist/app/builder/use-builder-catalogue.d.ts +1 -0
  43. package/dist/app/builder/use-builder-catalogue.d.ts.map +1 -1
  44. package/dist/app/builder/use-builder-catalogue.js +6 -0
  45. package/dist/app/builder/use-builder-catalogue.js.map +1 -1
  46. package/dist/app/builder/use-save-dashboard.d.ts +3 -1
  47. package/dist/app/builder/use-save-dashboard.d.ts.map +1 -1
  48. package/dist/app/builder/use-save-dashboard.js +3 -1
  49. package/dist/app/builder/use-save-dashboard.js.map +1 -1
  50. package/dist/app/index.d.ts +1 -1
  51. package/dist/app/index.d.ts.map +1 -1
  52. package/dist/app/index.js.map +1 -1
  53. package/dist/app/layout-builder.d.ts +7 -2
  54. package/dist/app/layout-builder.d.ts.map +1 -1
  55. package/dist/app/layout-builder.js +17 -5
  56. package/dist/app/layout-builder.js.map +1 -1
  57. package/dist/app/mcp-app-view-chrome.d.ts +94 -20
  58. package/dist/app/mcp-app-view-chrome.d.ts.map +1 -1
  59. package/dist/app/mcp-app-view-chrome.js +109 -40
  60. package/dist/app/mcp-app-view-chrome.js.map +1 -1
  61. package/dist/app/mcp-app-view-states.d.ts +15 -0
  62. package/dist/app/mcp-app-view-states.d.ts.map +1 -0
  63. package/dist/app/mcp-app-view-states.js +34 -0
  64. package/dist/app/mcp-app-view-states.js.map +1 -0
  65. package/dist/app/mcp-app-view.d.ts +40 -5
  66. package/dist/app/mcp-app-view.d.ts.map +1 -1
  67. package/dist/app/mcp-app-view.js +174 -128
  68. package/dist/app/mcp-app-view.js.map +1 -1
  69. package/dist/app/mcp-toolkit-app.d.ts +2 -1
  70. package/dist/app/mcp-toolkit-app.d.ts.map +1 -1
  71. package/dist/app/mcp-toolkit-app.js +25 -3
  72. package/dist/app/mcp-toolkit-app.js.map +1 -1
  73. package/dist/app/use-view-lifecycle.d.ts +38 -0
  74. package/dist/app/use-view-lifecycle.d.ts.map +1 -0
  75. package/dist/app/use-view-lifecycle.js +79 -0
  76. package/dist/app/use-view-lifecycle.js.map +1 -0
  77. package/dist/app/view-lifecycle.d.ts +109 -0
  78. package/dist/app/view-lifecycle.d.ts.map +1 -0
  79. package/dist/app/view-lifecycle.js +120 -0
  80. package/dist/app/view-lifecycle.js.map +1 -0
  81. package/dist/app/widget-props-sheet.d.ts +3 -9
  82. package/dist/app/widget-props-sheet.d.ts.map +1 -1
  83. package/dist/app/widget-props-sheet.js +6 -13
  84. package/dist/app/widget-props-sheet.js.map +1 -1
  85. package/dist/app/widget-renderer.d.ts +41 -1
  86. package/dist/app/widget-renderer.d.ts.map +1 -1
  87. package/dist/app/widget-renderer.js +82 -31
  88. package/dist/app/widget-renderer.js.map +1 -1
  89. package/dist/components/DrillButton.d.ts.map +1 -1
  90. package/dist/components/DrillButton.js +2 -1
  91. package/dist/components/DrillButton.js.map +1 -1
  92. package/dist/components/EmptyState.d.ts +33 -0
  93. package/dist/components/EmptyState.d.ts.map +1 -0
  94. package/dist/components/EmptyState.js +17 -0
  95. package/dist/components/EmptyState.js.map +1 -0
  96. package/dist/components/ErrorState.d.ts +50 -0
  97. package/dist/components/ErrorState.d.ts.map +1 -0
  98. package/dist/components/ErrorState.js +60 -0
  99. package/dist/components/ErrorState.js.map +1 -0
  100. package/dist/components/FilterBar.d.ts +1 -1
  101. package/dist/components/FilterBar.d.ts.map +1 -1
  102. package/dist/components/FilterBar.js +7 -4
  103. package/dist/components/FilterBar.js.map +1 -1
  104. package/dist/components/GroupCard.d.ts.map +1 -1
  105. package/dist/components/GroupCard.js +2 -1
  106. package/dist/components/GroupCard.js.map +1 -1
  107. package/dist/components/KpiGrid.d.ts +10 -3
  108. package/dist/components/KpiGrid.d.ts.map +1 -1
  109. package/dist/components/KpiGrid.js +31 -9
  110. package/dist/components/KpiGrid.js.map +1 -1
  111. package/dist/components/ListFooter.d.ts +3 -2
  112. package/dist/components/ListFooter.d.ts.map +1 -1
  113. package/dist/components/ListFooter.js +14 -4
  114. package/dist/components/ListFooter.js.map +1 -1
  115. package/dist/components/Pills.d.ts +10 -6
  116. package/dist/components/Pills.d.ts.map +1 -1
  117. package/dist/components/Pills.js +12 -6
  118. package/dist/components/Pills.js.map +1 -1
  119. package/dist/components/Toast.d.ts +37 -0
  120. package/dist/components/Toast.d.ts.map +1 -0
  121. package/dist/components/Toast.js +91 -0
  122. package/dist/components/Toast.js.map +1 -0
  123. package/dist/components/WidgetHeader.js +1 -1
  124. package/dist/hooks/use-tool-result-recovery.d.ts +23 -1
  125. package/dist/hooks/use-tool-result-recovery.d.ts.map +1 -1
  126. package/dist/hooks/use-tool-result-recovery.js +21 -7
  127. package/dist/hooks/use-tool-result-recovery.js.map +1 -1
  128. package/dist/hooks/use-view-data.d.ts +6 -0
  129. package/dist/hooks/use-view-data.d.ts.map +1 -1
  130. package/dist/hooks/use-view-data.js +6 -1
  131. package/dist/hooks/use-view-data.js.map +1 -1
  132. package/dist/index.d.ts +12 -5
  133. package/dist/index.d.ts.map +1 -1
  134. package/dist/index.js +14 -4
  135. package/dist/index.js.map +1 -1
  136. package/dist/lib/focus-ring.d.ts +18 -0
  137. package/dist/lib/focus-ring.d.ts.map +1 -0
  138. package/dist/lib/focus-ring.js +18 -0
  139. package/dist/lib/focus-ring.js.map +1 -0
  140. package/dist/lib/icon-stroke.d.ts +12 -0
  141. package/dist/lib/icon-stroke.d.ts.map +1 -0
  142. package/dist/lib/icon-stroke.js +12 -0
  143. package/dist/lib/icon-stroke.js.map +1 -0
  144. package/dist/lib/toast-model.d.ts +46 -0
  145. package/dist/lib/toast-model.d.ts.map +1 -0
  146. package/dist/lib/toast-model.js +36 -0
  147. package/dist/lib/toast-model.js.map +1 -0
  148. package/dist/lib/tone-utils.d.ts +43 -9
  149. package/dist/lib/tone-utils.d.ts.map +1 -1
  150. package/dist/lib/tone-utils.js +36 -7
  151. package/dist/lib/tone-utils.js.map +1 -1
  152. package/dist/locale/index.d.ts +3 -1
  153. package/dist/locale/index.d.ts.map +1 -1
  154. package/dist/locale/index.js +2 -1
  155. package/dist/locale/index.js.map +1 -1
  156. package/dist/locale/locale-provider.d.ts +42 -1
  157. package/dist/locale/locale-provider.d.ts.map +1 -1
  158. package/dist/locale/locale-provider.js +44 -3
  159. package/dist/locale/locale-provider.js.map +1 -1
  160. package/dist/locale/message-types.d.ts +223 -0
  161. package/dist/locale/message-types.d.ts.map +1 -0
  162. package/dist/locale/message-types.js +7 -0
  163. package/dist/locale/message-types.js.map +1 -0
  164. package/dist/locale/messages-de.d.ts +9 -0
  165. package/dist/locale/messages-de.d.ts.map +1 -0
  166. package/dist/locale/messages-de.js +151 -0
  167. package/dist/locale/messages-de.js.map +1 -0
  168. package/dist/locale/messages-en.d.ts +8 -0
  169. package/dist/locale/messages-en.d.ts.map +1 -0
  170. package/dist/locale/messages-en.js +150 -0
  171. package/dist/locale/messages-en.js.map +1 -0
  172. package/dist/locale/toolkit-messages.d.ts +39 -0
  173. package/dist/locale/toolkit-messages.d.ts.map +1 -0
  174. package/dist/locale/toolkit-messages.js +50 -0
  175. package/dist/locale/toolkit-messages.js.map +1 -0
  176. package/dist/primitives/alert.d.ts.map +1 -1
  177. package/dist/primitives/alert.js +4 -2
  178. package/dist/primitives/alert.js.map +1 -1
  179. package/dist/primitives/badge.d.ts +1 -1
  180. package/dist/primitives/badge.d.ts.map +1 -1
  181. package/dist/primitives/badge.js +14 -6
  182. package/dist/primitives/badge.js.map +1 -1
  183. package/dist/primitives/button.d.ts.map +1 -1
  184. package/dist/primitives/button.js +12 -8
  185. package/dist/primitives/button.js.map +1 -1
  186. package/dist/primitives/card.js +4 -4
  187. package/dist/primitives/card.js.map +1 -1
  188. package/dist/primitives/dialog.d.ts +7 -3
  189. package/dist/primitives/dialog.d.ts.map +1 -1
  190. package/dist/primitives/dialog.js +14 -7
  191. package/dist/primitives/dialog.js.map +1 -1
  192. package/dist/primitives/input.d.ts.map +1 -1
  193. package/dist/primitives/input.js +4 -1
  194. package/dist/primitives/input.js.map +1 -1
  195. package/dist/primitives/scroll-area.js +1 -1
  196. package/dist/primitives/scroll-area.js.map +1 -1
  197. package/dist/primitives/select.d.ts.map +1 -1
  198. package/dist/primitives/select.js +5 -2
  199. package/dist/primitives/select.js.map +1 -1
  200. package/dist/primitives/sheet.d.ts +3 -1
  201. package/dist/primitives/sheet.d.ts.map +1 -1
  202. package/dist/primitives/sheet.js +12 -6
  203. package/dist/primitives/sheet.js.map +1 -1
  204. package/dist/primitives/skeleton.js +1 -1
  205. package/dist/primitives/skeleton.js.map +1 -1
  206. package/dist/primitives/switch.d.ts.map +1 -1
  207. package/dist/primitives/switch.js +2 -1
  208. package/dist/primitives/switch.js.map +1 -1
  209. package/dist/primitives/tabs.d.ts.map +1 -1
  210. package/dist/primitives/tabs.js +5 -3
  211. package/dist/primitives/tabs.js.map +1 -1
  212. package/dist/render-cases.d.ts.map +1 -1
  213. package/dist/render-cases.js +20 -2
  214. package/dist/render-cases.js.map +1 -1
  215. package/dist/render-scan.d.ts +25 -0
  216. package/dist/render-scan.d.ts.map +1 -0
  217. package/dist/render-scan.js +66 -0
  218. package/dist/render-scan.js.map +1 -0
  219. package/dist/theme/cd-tokens.d.ts +66 -0
  220. package/dist/theme/cd-tokens.d.ts.map +1 -0
  221. package/dist/theme/cd-tokens.js +64 -0
  222. package/dist/theme/cd-tokens.js.map +1 -0
  223. package/dist/theme/create-theme.d.ts +196 -39
  224. package/dist/theme/create-theme.d.ts.map +1 -1
  225. package/dist/theme/create-theme.js +189 -13
  226. package/dist/theme/create-theme.js.map +1 -1
  227. package/dist/theme/fonts.d.ts +38 -0
  228. package/dist/theme/fonts.d.ts.map +1 -0
  229. package/dist/theme/fonts.js +56 -0
  230. package/dist/theme/fonts.js.map +1 -0
  231. package/dist/theme/host-fonts.d.ts +39 -0
  232. package/dist/theme/host-fonts.d.ts.map +1 -0
  233. package/dist/theme/host-fonts.js +44 -0
  234. package/dist/theme/host-fonts.js.map +1 -0
  235. package/dist/theme/index.d.ts +8 -1
  236. package/dist/theme/index.d.ts.map +1 -1
  237. package/dist/theme/index.js +5 -1
  238. package/dist/theme/index.js.map +1 -1
  239. package/dist/theme/portal-container.d.ts +16 -0
  240. package/dist/theme/portal-container.d.ts.map +1 -0
  241. package/dist/theme/portal-container.js +19 -0
  242. package/dist/theme/portal-container.js.map +1 -0
  243. package/dist/theme/presets.d.ts +24 -9
  244. package/dist/theme/presets.d.ts.map +1 -1
  245. package/dist/theme/presets.js +44 -11
  246. package/dist/theme/presets.js.map +1 -1
  247. package/dist/theme/theme-provider.d.ts +27 -9
  248. package/dist/theme/theme-provider.d.ts.map +1 -1
  249. package/dist/theme/theme-provider.js +42 -20
  250. package/dist/theme/theme-provider.js.map +1 -1
  251. package/package.json +9 -4
  252. package/src/app/builder/BuilderBanners.tsx +26 -13
  253. package/src/app/builder/CanvasRow.tsx +22 -21
  254. package/src/app/builder/CatalogueSheet.tsx +16 -14
  255. package/src/app/builder/EmptyHint.tsx +16 -11
  256. package/src/app/builder/LayoutSurface.tsx +7 -7
  257. package/src/app/builder/PipelineStrip.tsx +26 -17
  258. package/src/app/builder/SaveDialog.tsx +11 -12
  259. package/src/app/builder/Toolbar.tsx +19 -7
  260. package/src/app/builder/Workspace.tsx +5 -3
  261. package/src/app/builder/builder-feedback.test.tsx +75 -0
  262. package/src/app/builder/builder-model.test.ts +5 -0
  263. package/src/app/builder/builder-model.ts +5 -4
  264. package/src/app/builder/labels.ts +15 -121
  265. package/src/app/builder/status-chip.tsx +8 -4
  266. package/src/app/builder/use-builder-catalogue.ts +6 -0
  267. package/src/app/builder/use-save-dashboard.ts +5 -0
  268. package/src/app/index.ts +8 -1
  269. package/src/app/layout-builder.tsx +23 -3
  270. package/src/app/mcp-app-view-chrome.test.tsx +347 -14
  271. package/src/app/mcp-app-view-chrome.tsx +278 -100
  272. package/src/app/mcp-app-view-states.tsx +100 -0
  273. package/src/app/mcp-app-view.test.tsx +258 -0
  274. package/src/app/mcp-app-view.tsx +292 -185
  275. package/src/app/mcp-toolkit-app.tsx +31 -7
  276. package/src/app/use-view-lifecycle.ts +116 -0
  277. package/src/app/view-lifecycle.test.ts +227 -0
  278. package/src/app/view-lifecycle.ts +174 -0
  279. package/src/app/widget-props-sheet.tsx +13 -26
  280. package/src/app/widget-renderer.render.test.tsx +131 -0
  281. package/src/app/widget-renderer.tsx +136 -38
  282. package/src/components/DrillButton.tsx +2 -1
  283. package/src/components/EmptyState.tsx +81 -0
  284. package/src/components/ErrorState.tsx +182 -0
  285. package/src/components/FilterBar.tsx +9 -6
  286. package/src/components/GroupCard.tsx +3 -2
  287. package/src/components/KpiGrid.tsx +75 -44
  288. package/src/components/ListFooter.tsx +17 -6
  289. package/src/components/Pills.tsx +20 -11
  290. package/src/components/Toast.tsx +188 -0
  291. package/src/components/WidgetHeader.tsx +1 -1
  292. package/src/components/feedback-states.test.tsx +225 -0
  293. package/src/components/tone-figures.test.tsx +54 -0
  294. package/src/fonts.css +19 -0
  295. package/src/globals.css +9 -165
  296. package/src/hooks/use-tool-result-recovery.test.ts +25 -1
  297. package/src/hooks/use-tool-result-recovery.ts +34 -6
  298. package/src/hooks/use-view-data.test.tsx +60 -0
  299. package/src/hooks/use-view-data.ts +12 -1
  300. package/src/i18n-coverage.test.tsx +323 -0
  301. package/src/index.ts +57 -5
  302. package/src/lib/focus-ring.ts +20 -0
  303. package/src/lib/icon-stroke.test.ts +59 -0
  304. package/src/lib/icon-stroke.ts +13 -0
  305. package/src/lib/toast-model.test.ts +68 -0
  306. package/src/lib/toast-model.ts +63 -0
  307. package/src/lib/tone-utils.test.ts +47 -14
  308. package/src/lib/tone-utils.ts +61 -13
  309. package/src/locale/index.ts +13 -1
  310. package/src/locale/locale-provider.test.tsx +87 -0
  311. package/src/locale/locale-provider.tsx +90 -3
  312. package/src/locale/message-types.ts +221 -0
  313. package/src/locale/messages-de.ts +159 -0
  314. package/src/locale/messages-en.ts +157 -0
  315. package/src/locale/toolkit-messages.test.ts +216 -0
  316. package/src/locale/toolkit-messages.ts +82 -0
  317. package/src/no-hardcoded-copy.test.ts +432 -0
  318. package/src/primitives/alert.tsx +4 -2
  319. package/src/primitives/badge.tsx +14 -7
  320. package/src/primitives/button.tsx +13 -8
  321. package/src/primitives/card.tsx +4 -4
  322. package/src/primitives/dialog.tsx +32 -7
  323. package/src/primitives/input.tsx +5 -3
  324. package/src/primitives/scroll-area.tsx +1 -1
  325. package/src/primitives/select.tsx +6 -3
  326. package/src/primitives/sheet.tsx +26 -6
  327. package/src/primitives/skeleton.tsx +1 -1
  328. package/src/primitives/switch.tsx +4 -2
  329. package/src/primitives/tabs.tsx +9 -6
  330. package/src/render-cases.tsx +45 -1
  331. package/src/render-scan.ts +75 -0
  332. package/src/theme/cd-tokens.generated.css +48 -0
  333. package/src/theme/cd-tokens.test.ts +107 -0
  334. package/src/theme/cd-tokens.ts +67 -0
  335. package/src/theme/create-theme.test.ts +243 -5
  336. package/src/theme/create-theme.ts +313 -41
  337. package/src/theme/fonts.test.ts +85 -0
  338. package/src/theme/fonts.ts +72 -0
  339. package/src/theme/host-fonts.test.ts +135 -0
  340. package/src/theme/host-fonts.ts +52 -0
  341. package/src/theme/index.ts +15 -1
  342. package/src/theme/portal-container.ts +20 -0
  343. package/src/theme/presets.ts +44 -11
  344. package/src/theme/theme-css.test.ts +568 -0
  345. package/src/theme/theme-provider.test.tsx +82 -0
  346. package/src/theme/theme-provider.tsx +60 -24
  347. package/src/theme.css +314 -0
  348. package/ui-catalog.json +344 -17
@@ -1,25 +1,358 @@
1
+ import type { ReactNode } from "react"
1
2
  import { renderToStaticMarkup } from "react-dom/server"
2
3
  import { describe, expect, it } from "vitest"
3
- import { DEFAULT_LABELS, ViewPlaceholder } from "./mcp-app-view-chrome.js"
4
+ import { LocaleProvider } from "../locale/locale-provider.js"
5
+ import { TOOLKIT_MESSAGES } from "../locale/toolkit-messages.js"
6
+ import { attribute, decodeEntities, openingTags } from "../render-scan.js"
7
+ import {
8
+ applyBuilderCommit,
9
+ DEFAULT_LABELS,
10
+ fullscreenAvailable,
11
+ StepErrors,
12
+ toolbarControls,
13
+ ViewToolbar,
14
+ type ViewData,
15
+ type ViewToolbarProps,
16
+ } from "./mcp-app-view-chrome.js"
17
+ import { ViewPlaceholder } from "./mcp-app-view-states.js"
18
+ import { resolveViewLifecycle, RETRY_IDLE, type ViewLifecycleInput } from "./view-lifecycle.js"
4
19
 
5
- describe("ViewPlaceholder", () => {
6
- it("shows the loading skeleton while the rendering call is still open", () => {
7
- const html = renderToStaticMarkup(<ViewPlaceholder cancelled={false} labels={DEFAULT_LABELS} />)
20
+ const en = TOOLKIT_MESSAGES.en.view
21
+ const de = TOOLKIT_MESSAGES.de.view
22
+ const noop = (): void => {}
23
+ const asyncNoop = (): Promise<void> => Promise.resolve()
24
+
25
+ const placeholder = (lifecycle: Parameters<typeof ViewPlaceholder>[0]["lifecycle"], labels = en) =>
26
+ renderToStaticMarkup(<ViewPlaceholder lifecycle={lifecycle} labels={labels} onRetry={noop} />)
27
+
28
+ describe("ViewPlaceholder — one rendering per lifecycle phase (toolkit#176)", () => {
29
+ it("waiting: skeleton plus a status line, no error, no retry", () => {
30
+ const html = placeholder({ phase: "waiting", slow: false })
31
+ expect(html).toContain('data-slot="skeleton"')
32
+ expect(html).toMatch(/role="status"[^>]*>.*Loading view…/)
33
+ expect(html).not.toContain(en.slowHint)
34
+ expect(html).not.toContain('role="alert"')
35
+ expect(html).not.toContain("<button")
36
+ })
37
+
38
+ it("waiting: the live region sits in no aria-busy subtree (AT may hold back the slow hint)", () => {
39
+ expect(placeholder({ phase: "waiting", slow: true })).not.toMatch(/aria-busy="true"/)
40
+ })
41
+
42
+ it("waiting too long: the slow hint joins the status line", () => {
43
+ const html = placeholder({ phase: "waiting", slow: true })
44
+ expect(html).toContain(en.slowHint)
45
+ expect(html).toContain('data-slot="slow-hint"')
8
46
  expect(html).toContain('data-slot="skeleton"')
9
- expect(html).toContain(DEFAULT_LABELS.loading)
10
- expect(html).not.toContain(DEFAULT_LABELS.cancelled)
11
47
  })
12
48
 
13
- it("replaces the skeleton with the cancellation notice once the host cancelled the call", () => {
14
- const html = renderToStaticMarkup(<ViewPlaceholder cancelled labels={DEFAULT_LABELS} />)
15
- expect(html).toContain(DEFAULT_LABELS.cancelled)
49
+ it("error: icon, title, what to do, the raw reason under Details, a retry — and NO skeleton (K14)", () => {
50
+ // The exact input McpAppView feeds for an isError rendering result.
51
+ const lifecycle = resolveViewLifecycle({
52
+ hasViewData: false,
53
+ callStatus: "error",
54
+ cancelled: false,
55
+ callErrorReason: "[503 ProcessEngineException] Engine is shutting down",
56
+ recoveryFailed: false,
57
+ retryState: RETRY_IDLE,
58
+ slow: true,
59
+ canRetry: true,
60
+ } satisfies ViewLifecycleInput)
61
+ const html = placeholder(lifecycle)
62
+ expect(html).not.toContain('data-slot="skeleton"')
63
+ expect(html).not.toContain(en.loading)
64
+ expect(html).toContain('role="alert"')
65
+ expect(html).toContain("<svg")
66
+ expect(decodeEntities(html)).toContain(en.errorTitle)
67
+ expect(html).toContain(en.errorHint)
68
+ expect(html).toMatch(/<summary[^>]*>Details<\/summary>/)
69
+ expect(html).toContain("[503 ProcessEngineException] Engine is shutting down")
70
+ expect(html).toContain(`${en.retry}</button>`)
71
+ })
72
+
73
+ it("error without a known originating tool: no retry button, and a hint that promises none", () => {
74
+ const html = placeholder({ phase: "error", reason: undefined, canRetry: false })
75
+ expect(decodeEntities(html)).toContain(en.errorTitle)
76
+ expect(html).toContain(en.errorHintNoRetry)
77
+ expect(html).not.toContain(en.errorHint)
78
+ expect(html).not.toContain("<button")
79
+ expect(html).not.toContain("<details")
80
+ const de = placeholder(
81
+ { phase: "error", reason: undefined, canRetry: false },
82
+ TOOLKIT_MESSAGES.de.view,
83
+ )
84
+ expect(de).toContain(TOOLKIT_MESSAGES.de.view.errorHintNoRetry)
85
+ expect(de).not.toContain(TOOLKIT_MESSAGES.de.view.errorHint)
86
+ })
87
+
88
+ it("error copy maps through describeError while the raw reason stays under Details", () => {
89
+ const html = renderToStaticMarkup(
90
+ <ViewPlaceholder
91
+ lifecycle={{ phase: "error", reason: "HTTP 503", canRetry: true }}
92
+ labels={en}
93
+ onRetry={noop}
94
+ describeError={(reason) =>
95
+ reason?.includes("503") ? { title: "The engine is down" } : undefined
96
+ }
97
+ />,
98
+ )
99
+ expect(html).toContain("The engine is down")
100
+ expect(html).not.toContain(en.errorTitle)
101
+ expect(html).toContain(en.errorHint)
102
+ expect(html).toContain("HTTP 503")
103
+ })
104
+
105
+ it("cancelled: the cancellation notice, no skeleton, no retry", () => {
106
+ const html = placeholder({ phase: "cancelled" })
107
+ expect(html).toContain(en.cancelled)
108
+ expect(html).toContain('role="status"')
16
109
  expect(html).not.toContain('data-slot="skeleton"')
17
- expect(html).not.toContain(DEFAULT_LABELS.loading)
110
+ expect(html).not.toContain("<button")
111
+ })
112
+
113
+ it("result: renders nothing (the view takes over)", () => {
114
+ expect(placeholder({ phase: "result" })).toBe("")
115
+ })
116
+
117
+ it("renders the German catalog and consumer overrides", () => {
118
+ expect(placeholder({ phase: "waiting", slow: true }, de)).toContain(de.slowHint)
119
+ expect(placeholder({ phase: "cancelled" }, { ...de, cancelled: "Abgebrochen." })).toContain(
120
+ "Abgebrochen.",
121
+ )
122
+ })
123
+
124
+ it("keeps the deprecated English DEFAULT_LABELS alias", () => {
125
+ expect(DEFAULT_LABELS).toBe(en)
126
+ })
127
+ })
128
+
129
+ const view: ViewData = {
130
+ title: "Process definitions",
131
+ context: { keys: {}, stepIds: [], stepData: {}, errors: [] },
132
+ layout: { rows: [] },
133
+ _refreshParams: { layout: { rows: [] } },
134
+ }
135
+
136
+ function toolbar(over: Partial<ViewToolbarProps> = {}, wrap = (n: ReactNode) => n) {
137
+ return renderToStaticMarkup(
138
+ wrap(
139
+ <ViewToolbar
140
+ hidden={false}
141
+ viewData={view}
142
+ builderAvailable={false}
143
+ isFullscreen={false}
144
+ canFullscreen
145
+ isRefreshing={false}
146
+ labels={en}
147
+ titleMode="show"
148
+ titleLevel={2}
149
+ onRefresh={asyncNoop}
150
+ onEnterBuildMode={noop}
151
+ onToggleFullscreen={asyncNoop}
152
+ {...over}
153
+ />,
154
+ ),
155
+ )
156
+ }
157
+
158
+ /**
159
+ * CI §8 chrome button (Icon-Button, Chrome): surface, `--cd-linie` border,
160
+ * shadow-1, blue on hover. The outline variant carries the surface, border and
161
+ * hover; the shadow is the chrome's own and must render.
162
+ */
163
+ function expectChromeButton(tag: string) {
164
+ expect(attribute(tag, "data-variant")).toBe("outline")
165
+ const classes = (attribute(tag, "class") ?? "").split(/\s+/)
166
+ expect(classes).toEqual(
167
+ expect.arrayContaining(["shadow-1", "border", "border-border", "bg-background"]),
168
+ )
169
+ expect(classes).toContain("hover:border-focus")
170
+ }
171
+
172
+ describe("ViewToolbar", () => {
173
+ it("renders Refresh and Fullscreen as 32 px CI chrome icon buttons named by aria-label", () => {
174
+ const html = toolbar()
175
+ const buttons = openingTags(html, "button")
176
+ expect(buttons.map((b) => attribute(b, "aria-label"))).toEqual([en.refresh, en.enterFullscreen])
177
+ for (const b of buttons) {
178
+ expect(attribute(b, "data-size")).toBe("icon-sm")
179
+ expect(attribute(b, "type")).toBe("button")
180
+ expectChromeButton(b)
181
+ }
182
+ // Icons are decorative (the name is on the button) and use the dense stroke.
183
+ for (const svg of openingTags(html, "svg")) {
184
+ expect(attribute(svg, "aria-hidden")).toBe("true")
185
+ expect(attribute(svg, "stroke-width")).toBe("2")
186
+ }
187
+ expect(html).toMatch(/data-slot="view-toolbar"[^>]*px-4/)
188
+ })
189
+
190
+ it("gates Fullscreen on the host's offer (toolkit#178 K22)", () => {
191
+ expect(toolbar({ canFullscreen: false })).not.toContain(en.enterFullscreen)
192
+ const exit = toolbar({ isFullscreen: true })
193
+ expect(exit).toContain(`aria-label="${en.exitFullscreen}"`)
194
+ expect(exit).toContain("lucide-minimize-2")
195
+ })
196
+
197
+ it("names the refresh button for its running state and disables it", () => {
198
+ const html = toolbar({ isRefreshing: true })
199
+ const refresh = openingTags(html, "button")[0] ?? ""
200
+ expect(attribute(refresh, "aria-label")).toBe(en.refreshing)
201
+ expect(refresh).toMatch(/\sdisabled=""/)
202
+ expect(html).toContain("motion-safe:animate-spin")
203
+ })
204
+
205
+ it("shows the edit button with its label and a decorative pencil", () => {
206
+ const html = toolbar({ builderAvailable: true })
207
+ expect(html).toContain(`${en.build}</button>`)
208
+ expect(html).toContain("lucide-pencil")
209
+ const edit = openingTags(html, "button")[0] ?? ""
210
+ expectChromeButton(edit)
211
+ })
212
+
213
+ it("titles the view once: show (configurable level), hide, or slot", () => {
214
+ expect(toolbar()).toMatch(/<h2[^>]*>Process definitions<\/h2>/)
215
+ expect(toolbar({ titleLevel: 3 })).toMatch(/<h3[^>]*>Process definitions<\/h3>/)
216
+ expect(toolbar({ titleMode: "hide" })).not.toMatch(/<h\d/)
217
+ const slot = toolbar({
218
+ titleMode: "slot",
219
+ renderTitle: (t) => <span data-testid="slot">{`${t} (slot)`}</span>,
220
+ })
221
+ expect(slot).toContain("Process definitions (slot)")
222
+ expect(slot).not.toMatch(/<h\d/)
223
+ })
224
+
225
+ it("renders nothing in build mode or when it would be an empty bar", () => {
226
+ expect(toolbar({ hidden: true })).toBe("")
227
+ expect(
228
+ toolbar({
229
+ canFullscreen: false,
230
+ titleMode: "hide",
231
+ viewData: { ...view, _refreshParams: undefined },
232
+ }),
233
+ ).toBe("")
234
+ })
235
+
236
+ it("follows the German catalog", () => {
237
+ const html = toolbar({ labels: de })
238
+ expect(html).toContain(`aria-label="${de.refresh}"`)
239
+ expect(html).toContain(`aria-label="${de.enterFullscreen}"`)
240
+ })
241
+ })
242
+
243
+ describe("toolbarControls / fullscreenAvailable", () => {
244
+ it("derives each control from the payload and the host", () => {
245
+ expect(
246
+ toolbarControls({
247
+ viewData: view,
248
+ builderAvailable: true,
249
+ canFullscreen: true,
250
+ titleMode: "show",
251
+ }),
252
+ ).toEqual({ title: true, refresh: true, edit: true, fullscreen: true, any: true })
253
+ expect(
254
+ toolbarControls({
255
+ viewData: { title: undefined, layout: undefined, _refreshParams: undefined },
256
+ builderAvailable: true,
257
+ canFullscreen: false,
258
+ titleMode: "show",
259
+ }),
260
+ ).toEqual({ title: false, refresh: false, edit: false, fullscreen: false, any: false })
261
+ expect(
262
+ toolbarControls({
263
+ viewData: { title: undefined, layout: { rows: [] }, _refreshParams: undefined },
264
+ builderAvailable: false,
265
+ canFullscreen: false,
266
+ titleMode: "slot",
267
+ }),
268
+ ).toMatchObject({ title: true, refresh: false, any: true })
269
+ })
270
+
271
+ it("offers fullscreen only when the host negotiates it, keeping the exit while in it", () => {
272
+ expect(fullscreenAvailable("inline", ["inline", "fullscreen"])).toBe(true)
273
+ expect(fullscreenAvailable("inline", ["inline"])).toBe(false)
274
+ expect(fullscreenAvailable("inline", [])).toBe(false)
275
+ expect(fullscreenAvailable("fullscreen", ["inline"])).toBe(true)
276
+ expect(fullscreenAvailable("pip", ["inline", "pip"])).toBe(false)
277
+ })
278
+ })
279
+
280
+ describe("StepErrors — an alert with step titles and a retry", () => {
281
+ const errors = [
282
+ { stepId: "inc", reason: "[503] Engine is shutting down" },
283
+ { stepId: "kpi", reason: "timeout" },
284
+ ]
285
+ const steps = [
286
+ { id: "inc", step: "camunda7:incidents" },
287
+ { id: "kpi", step: "camunda7:kpis" },
288
+ ]
289
+
290
+ it("lists each failed step by its human title, else its step id, plus what to do", () => {
291
+ const html = renderToStaticMarkup(
292
+ <LocaleProvider locale="de">
293
+ <StepErrors
294
+ hidden={false}
295
+ errors={errors}
296
+ steps={steps}
297
+ labels={de}
298
+ describeStep={({ step }) => (step === "camunda7:incidents" ? "Incidents" : undefined)}
299
+ onRetry={noop}
300
+ />
301
+ </LocaleProvider>,
302
+ )
303
+ expect(html).toContain('role="alert"')
304
+ expect(html).toContain(de.stepErrorsTitle)
305
+ expect(html).toMatch(/font-medium">Incidents<\/span>/)
306
+ expect(html).toMatch(/font-mono[^"]*">camunda7:kpis<\/span>/)
307
+ expect(html).not.toContain(">inc<")
308
+ expect(html).toContain(de.stepErrorsHint)
309
+ expect(html).toContain(`${de.retry}</button>`)
310
+ })
311
+
312
+ it("keeps the raw step reasons under Details, never in the alert text", () => {
313
+ const html = renderToStaticMarkup(
314
+ <StepErrors hidden={false} errors={errors} steps={steps} labels={en} onRetry={noop} />,
315
+ )
316
+ const [body, details = ""] = html.split("<details")
317
+ expect(body).not.toContain("Engine is shutting down")
318
+ expect(body).not.toContain("timeout")
319
+ expect(details).toMatch(/<summary[^>]*>Details<\/summary>/)
320
+ expect(details).toContain("camunda7:incidents: [503] Engine is shutting down")
321
+ expect(details).toContain("camunda7:kpis: timeout")
322
+ })
323
+
324
+ it("falls back to the step ref id and, without a refresh, offers no retry nor promises one", () => {
325
+ const html = renderToStaticMarkup(
326
+ <StepErrors hidden={false} errors={[{ stepId: "x", reason: "boom" }]} labels={en} />,
327
+ )
328
+ expect(html).toMatch(/font-mono[^"]*">x<\/span>/)
329
+ expect(html).not.toContain("<button")
330
+ expect(html).toContain(en.errorHintNoRetry)
331
+ expect(html).not.toContain(en.stepErrorsHint)
332
+ })
333
+
334
+ it("renders nothing without errors or in build mode", () => {
335
+ expect(renderToStaticMarkup(<StepErrors hidden={false} errors={[]} labels={en} />)).toBe("")
336
+ expect(renderToStaticMarkup(<StepErrors hidden errors={errors} labels={en} />)).toBe("")
337
+ })
338
+ })
339
+
340
+ describe("applyBuilderCommit", () => {
341
+ it("commits layout, title, context and the refresh params of the builder draft", () => {
342
+ const layout = { rows: [{ row: [{ widget: "a:b" }] }] }
343
+ const steps = [{ id: "s", step: "a:step" }]
344
+ const next = applyBuilderCommit(view, { layout, keys: { k: 1 }, steps, title: "New" })
345
+ expect(next.layout).toBe(layout)
346
+ expect(next.title).toBe("New")
347
+ expect(next.context).toBe(view.context)
348
+ expect(next._refreshParams).toEqual({ keys: { k: 1 }, steps, layout, title: "New" })
18
349
  })
19
350
 
20
- it("renders the consumer's label override", () => {
21
- const labels = { ...DEFAULT_LABELS, cancelled: "Abgebrochen." }
22
- const html = renderToStaticMarkup(<ViewPlaceholder cancelled labels={labels} />)
23
- expect(html).toContain("Abgebrochen.")
351
+ it("keeps the previous title and takes a new context when given", () => {
352
+ const context = { keys: { x: 1 }, stepIds: [], stepData: {}, errors: [] }
353
+ const next = applyBuilderCommit(view, { layout: { rows: [] }, context })
354
+ expect(next.title).toBe("Process definitions")
355
+ expect(next._refreshParams?.title).toBe("Process definitions")
356
+ expect(next.context).toBe(context)
24
357
  })
25
358
  })