@delmaredigital/payload-experiments 0.3.0-rc.1 → 0.4.0-rc.1

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 (338) hide show
  1. package/README.md +172 -21
  2. package/dist/admin/MetricSourceSelect.js +3 -3
  3. package/dist/admin/PlacementStatus.d.ts +3 -0
  4. package/dist/admin/PlacementStatus.js +54 -0
  5. package/dist/admin/PreviewLinks.d.ts +2 -1
  6. package/dist/admin/PreviewLinks.js +47 -26
  7. package/dist/admin/TestsNavLink.d.ts +3 -1
  8. package/dist/admin/TestsNavLink.js +2 -2
  9. package/dist/admin/actions/ConfirmDialog.d.ts +2 -1
  10. package/dist/admin/actions/ConfirmDialog.js +2 -2
  11. package/dist/admin/actions/DecideDialog.d.ts +2 -1
  12. package/dist/admin/actions/DecideDialog.js +2 -2
  13. package/dist/admin/api.d.ts +11 -2
  14. package/dist/admin/api.js +20 -2
  15. package/dist/admin/armSelect/ArmSelectField.js +14 -22
  16. package/dist/admin/arms/arms.model.d.ts +1 -21
  17. package/dist/admin/arms/arms.model.js +1 -153
  18. package/dist/admin/blocks/BlockPanel.d.ts +4 -0
  19. package/dist/admin/blocks/BlockPanel.js +153 -0
  20. package/dist/admin/blocks/TestBlockField.d.ts +5 -0
  21. package/dist/admin/blocks/TestBlockField.js +163 -0
  22. package/dist/admin/blocks/formApi.d.ts +43 -0
  23. package/dist/admin/blocks/formApi.js +153 -0
  24. package/dist/admin/blocks/inject.d.ts +7 -0
  25. package/dist/admin/blocks/inject.js +41 -0
  26. package/dist/admin/blocks/model.d.ts +17 -0
  27. package/dist/admin/blocks/model.js +45 -0
  28. package/dist/admin/blocks/statusCache.d.ts +7 -0
  29. package/dist/admin/blocks/statusCache.js +83 -0
  30. package/dist/admin/blocks/useBlockForm.d.ts +17 -0
  31. package/dist/admin/blocks/useBlockForm.js +46 -0
  32. package/dist/admin/copy/actions.d.ts +2 -0
  33. package/dist/admin/copy/actions.js +9 -0
  34. package/dist/admin/copy/arms.d.ts +1 -6
  35. package/dist/admin/copy/arms.js +1 -16
  36. package/dist/admin/copy/blocks.d.ts +27 -0
  37. package/dist/admin/copy/blocks.js +38 -0
  38. package/dist/admin/copy/goals.d.ts +2 -0
  39. package/dist/admin/copy/goals.js +4 -1
  40. package/dist/admin/copy/newTest.d.ts +10 -0
  41. package/dist/admin/copy/newTest.js +10 -0
  42. package/dist/admin/copy/pages.d.ts +26 -0
  43. package/dist/admin/copy/pages.js +26 -0
  44. package/dist/admin/copy/readiness.d.ts +25 -3
  45. package/dist/admin/copy/readiness.js +25 -3
  46. package/dist/admin/copy/results.d.ts +3 -0
  47. package/dist/admin/copy/results.js +4 -1
  48. package/dist/admin/copy/review.d.ts +1 -1
  49. package/dist/admin/copy/review.js +1 -1
  50. package/dist/admin/copy/surface.d.ts +0 -1
  51. package/dist/admin/copy/surface.js +0 -1
  52. package/dist/admin/copy/testPage.d.ts +4 -0
  53. package/dist/admin/copy/testPage.js +4 -0
  54. package/dist/admin/goals/XpGoalField.d.ts +2 -0
  55. package/dist/admin/goals/XpGoalField.js +152 -0
  56. package/dist/admin/metricSource.model.d.ts +2 -0
  57. package/dist/admin/metricSource.model.js +4 -0
  58. package/dist/admin/pageCollections.d.ts +10 -0
  59. package/dist/admin/pageCollections.js +20 -0
  60. package/dist/admin/paths.d.ts +8 -1
  61. package/dist/admin/paths.js +8 -1
  62. package/dist/admin/placementStatus.model.d.ts +2 -0
  63. package/dist/admin/placementStatus.model.js +19 -0
  64. package/dist/admin/placements/placementLabels.d.ts +11 -0
  65. package/dist/admin/placements/placementLabels.js +21 -0
  66. package/dist/admin/placements/placementLabels.model.d.ts +13 -0
  67. package/dist/admin/placements/placementLabels.model.js +12 -0
  68. package/dist/admin/placements/usePlacementLabels.d.ts +12 -0
  69. package/dist/admin/placements/usePlacementLabels.js +102 -0
  70. package/dist/admin/previewLinks.model.d.ts +2 -0
  71. package/dist/admin/previewLinks.model.js +3 -0
  72. package/dist/admin/results/DataQualityPanel.js +4 -1
  73. package/dist/admin/results/legacy.d.ts +16 -0
  74. package/dist/admin/results/legacy.js +27 -0
  75. package/dist/admin/results/results.model.d.ts +9 -6
  76. package/dist/admin/results/results.model.js +22 -9
  77. package/dist/admin/results/snapshot.d.ts +14 -0
  78. package/dist/admin/results/snapshot.js +3 -0
  79. package/dist/admin/surface/TestsHome.js +43 -15
  80. package/dist/admin/surface/TestsSurface.js +5 -1
  81. package/dist/admin/surface/contracts.d.ts +6 -5
  82. package/dist/admin/surface/goals/GoalForm.js +67 -34
  83. package/dist/admin/surface/goals/GoalsList.js +3 -3
  84. package/dist/admin/surface/goals/data.js +19 -16
  85. package/dist/admin/surface/goals/goals.model.d.ts +1 -2
  86. package/dist/admin/surface/goals/goals.model.js +1 -3
  87. package/dist/admin/surface/home.model.d.ts +2 -1
  88. package/dist/admin/surface/home.model.js +8 -4
  89. package/dist/admin/surface/newTest/NewTest.js +58 -17
  90. package/dist/admin/surface/newTest/PageWizard.d.ts +7 -0
  91. package/dist/admin/surface/newTest/PageWizard.js +209 -0
  92. package/dist/admin/surface/pagePicker/CopyRoute.d.ts +6 -0
  93. package/dist/admin/surface/pagePicker/CopyRoute.js +53 -0
  94. package/dist/admin/surface/pagePicker/PagePicker.d.ts +10 -0
  95. package/dist/admin/surface/pagePicker/PagePicker.js +172 -0
  96. package/dist/admin/surface/pagePicker/pagePicker.model.d.ts +25 -0
  97. package/dist/admin/surface/pagePicker/pagePicker.model.js +25 -0
  98. package/dist/admin/surface/readiness/ActionDialogs.js +103 -69
  99. package/dist/admin/surface/readiness/Banner.js +30 -6
  100. package/dist/admin/surface/readiness/readiness.model.d.ts +8 -1
  101. package/dist/admin/surface/readiness/readiness.model.js +95 -6
  102. package/dist/admin/surface/results/ReleasePages.d.ts +3 -1
  103. package/dist/admin/surface/results/ReleasePages.js +3 -7
  104. package/dist/admin/surface/results/ResultsBody.js +23 -3
  105. package/dist/admin/surface/results/ResultsHeader.js +2 -5
  106. package/dist/admin/surface/results/recommendation.model.d.ts +3 -2
  107. package/dist/admin/surface/results/recommendation.model.js +10 -3
  108. package/dist/admin/surface/results/useResults.d.ts +10 -1
  109. package/dist/admin/surface/surface.css +0 -1
  110. package/dist/admin/surface/test/AdvancedSection.js +111 -104
  111. package/dist/admin/surface/test/GoalSection.js +97 -22
  112. package/dist/admin/surface/test/LengthSection.js +1 -1
  113. package/dist/admin/surface/test/PageHoldout.d.ts +3 -0
  114. package/dist/admin/surface/test/PageHoldout.js +67 -0
  115. package/dist/admin/surface/test/TestPage.js +2 -1
  116. package/dist/admin/surface/test/VersionsSection.js +12 -5
  117. package/dist/admin/surface/test/fields.d.ts +13 -4
  118. package/dist/admin/surface/test/fields.js +46 -14
  119. package/dist/admin/surface/test/jumpToSection.d.ts +1 -1
  120. package/dist/admin/surface/test/jumpToSection.js +4 -3
  121. package/dist/admin/surface/test/pageParts.d.ts +4 -1
  122. package/dist/admin/surface/test/pageParts.js +79 -2
  123. package/dist/admin/surface/test/testPage.css +8 -1
  124. package/dist/admin/surface/test/testPage.model.d.ts +2 -3
  125. package/dist/admin/surface/test/testPage.model.js +1 -21
  126. package/dist/admin/surface/testDocument.model.d.ts +1 -0
  127. package/dist/admin/surface/testDocument.model.js +2 -1
  128. package/dist/admin/surface/useTestDocument.d.ts +11 -2
  129. package/dist/admin/surface/useTestDocument.js +55 -16
  130. package/dist/admin/useApi.d.ts +11 -2
  131. package/dist/admin/useApi.js +3 -5
  132. package/dist/admin/useLayerUsage.js +14 -23
  133. package/dist/admin/views/ExperimentsRootView.js +58 -16
  134. package/dist/admin/views/TestsNavLinkServer.d.ts +2 -0
  135. package/dist/admin/views/TestsNavLinkServer.js +43 -0
  136. package/dist/arms/copy.d.ts +6 -0
  137. package/dist/arms/copy.js +16 -0
  138. package/dist/arms/model.d.ts +23 -0
  139. package/dist/arms/model.js +173 -0
  140. package/dist/blocks/bindings.d.ts +3 -0
  141. package/dist/blocks/bindings.js +34 -0
  142. package/dist/blocks/createExperimentBlock.d.ts +6 -0
  143. package/dist/blocks/createExperimentBlock.js +77 -0
  144. package/dist/blocks/formState.d.ts +31 -0
  145. package/dist/blocks/formState.js +146 -0
  146. package/dist/blocks/model.d.ts +41 -0
  147. package/dist/blocks/model.js +34 -0
  148. package/dist/client/ExperimentGroup.d.ts +6 -0
  149. package/dist/client/ExperimentGroup.js +9 -0
  150. package/dist/client/ExperimentsProvider.js +20 -4
  151. package/dist/client/clickGoals.d.ts +4 -0
  152. package/dist/client/clickGoals.js +43 -0
  153. package/dist/collections/experiments/config.js +12 -2
  154. package/dist/collections/experiments/deleteGuard.d.ts +2 -1
  155. package/dist/collections/experiments/deleteGuard.js +12 -1
  156. package/dist/collections/experiments/editability.js +4 -2
  157. package/dist/collections/experiments/pickSlug.d.ts +2 -0
  158. package/dist/collections/experiments/pickSlug.js +8 -0
  159. package/dist/collections/labels.d.ts +3 -0
  160. package/dist/collections/labels.js +20 -0
  161. package/dist/collections/metrics.js +12 -2
  162. package/dist/collections/phases.js +1 -0
  163. package/dist/collections/serialize.js +7 -0
  164. package/dist/db/appends.d.ts +6 -2
  165. package/dist/db/appends.js +10 -4
  166. package/dist/db/clickGoals.d.ts +9 -0
  167. package/dist/db/clickGoals.js +22 -0
  168. package/dist/db/delete.d.ts +1 -0
  169. package/dist/db/delete.js +1 -0
  170. package/dist/db/locks.d.ts +14 -0
  171. package/dist/db/locks.js +36 -1
  172. package/dist/db/names.d.ts +4 -0
  173. package/dist/db/names.js +4 -0
  174. package/dist/db/pageBindings.d.ts +4 -0
  175. package/dist/db/pageBindings.js +17 -0
  176. package/dist/db/pageBusy.d.ts +2 -0
  177. package/dist/db/pageBusy.js +10 -0
  178. package/dist/db/pageContent.d.ts +10 -0
  179. package/dist/db/pageContent.js +79 -0
  180. package/dist/db/pageOccupancy.d.ts +12 -0
  181. package/dist/db/pageOccupancy.js +45 -0
  182. package/dist/db/placements.d.ts +33 -0
  183. package/dist/db/placements.js +54 -0
  184. package/dist/db/slugs.d.ts +8 -0
  185. package/dist/db/slugs.js +40 -0
  186. package/dist/db/snapshots.d.ts +6 -0
  187. package/dist/db/snapshots.js +9 -5
  188. package/dist/endpoints/clickKeys.d.ts +5 -0
  189. package/dist/endpoints/clickKeys.js +17 -0
  190. package/dist/endpoints/events.js +4 -1
  191. package/dist/endpoints/index.js +28 -0
  192. package/dist/endpoints/pages.d.ts +5 -0
  193. package/dist/endpoints/pages.js +120 -0
  194. package/dist/endpoints/placementStatus.d.ts +6 -0
  195. package/dist/endpoints/placementStatus.js +238 -0
  196. package/dist/endpoints/readBody.d.ts +2 -0
  197. package/dist/endpoints/readBody.js +24 -0
  198. package/dist/endpoints/results.js +5 -1
  199. package/dist/endpoints/summary.js +11 -4
  200. package/dist/endpoints/wrapBlock.d.ts +6 -0
  201. package/dist/endpoints/wrapBlock.js +76 -0
  202. package/dist/errors.d.ts +3 -3
  203. package/dist/errors.js +4 -4
  204. package/dist/events/rows.d.ts +1 -1
  205. package/dist/events/rows.js +5 -3
  206. package/dist/exports/adminClient.d.ts +5 -0
  207. package/dist/exports/adminClient.js +5 -0
  208. package/dist/exports/adminRsc.d.ts +1 -0
  209. package/dist/exports/adminRsc.js +1 -0
  210. package/dist/exports/blocks.d.ts +2 -0
  211. package/dist/exports/blocks.js +1 -0
  212. package/dist/exports/client.d.ts +1 -0
  213. package/dist/exports/client.js +1 -0
  214. package/dist/exports/index.d.ts +6 -1
  215. package/dist/exports/index.js +3 -0
  216. package/dist/exports/puck.d.ts +5 -2
  217. package/dist/exports/puck.js +3 -1
  218. package/dist/exports/puckEditor.d.ts +6 -1
  219. package/dist/exports/puckEditor.js +4 -1
  220. package/dist/exports/rsc.d.ts +4 -0
  221. package/dist/exports/rsc.js +2 -0
  222. package/dist/fields/xpGoal.d.ts +3 -0
  223. package/dist/fields/xpGoal.js +28 -0
  224. package/dist/globals/state.js +10 -0
  225. package/dist/goals/cache.d.ts +3 -0
  226. package/dist/goals/cache.js +35 -0
  227. package/dist/goals/fairness.d.ts +40 -0
  228. package/dist/goals/fairness.js +200 -0
  229. package/dist/goals/key.d.ts +4 -0
  230. package/dist/goals/key.js +3 -0
  231. package/dist/goals/picker/clickGoalPicker.model.d.ts +29 -0
  232. package/dist/goals/picker/clickGoalPicker.model.js +45 -0
  233. package/dist/goals/picker/copy.d.ts +16 -0
  234. package/dist/goals/picker/copy.js +15 -0
  235. package/dist/goals/picker/useClickGoalPicker.d.ts +30 -0
  236. package/dist/goals/picker/useClickGoalPicker.js +220 -0
  237. package/dist/hooks/pageGuard.d.ts +5 -1
  238. package/dist/hooks/pageGuard.js +160 -80
  239. package/dist/lib/fetchAllDocs.d.ts +16 -0
  240. package/dist/lib/fetchAllDocs.js +56 -0
  241. package/dist/onInit.js +29 -0
  242. package/dist/options.d.ts +2 -0
  243. package/dist/options.js +43 -10
  244. package/dist/plugin.js +45 -28
  245. package/dist/puck/ExperimentPicker.js +47 -16
  246. package/dist/puck/ExperimentSlot.d.ts +51 -1
  247. package/dist/puck/ExperimentSlot.js +23 -12
  248. package/dist/puck/copy.d.ts +6 -4
  249. package/dist/puck/copy.js +6 -4
  250. package/dist/puck/editor/CreateExperimentDrawer.js +46 -56
  251. package/dist/puck/editor/ExperimentSlotEditor.d.ts +52 -1
  252. package/dist/puck/editor/ExperimentSlotEditor.js +292 -137
  253. package/dist/puck/editor/ResultsReadout.d.ts +6 -0
  254. package/dist/puck/editor/ResultsReadout.js +99 -0
  255. package/dist/puck/editor/VersionBuilder.d.ts +34 -0
  256. package/dist/puck/editor/VersionBuilder.js +317 -0
  257. package/dist/puck/editor/armWrites.d.ts +7 -0
  258. package/dist/puck/editor/armWrites.js +71 -0
  259. package/dist/puck/editor/armsCache.d.ts +14 -0
  260. package/dist/puck/editor/armsCache.js +78 -0
  261. package/dist/puck/editor/copy.d.ts +33 -1
  262. package/dist/puck/editor/copy.js +45 -8
  263. package/dist/puck/editor/createTest.d.ts +1 -1
  264. package/dist/puck/editor/createTest.js +25 -14
  265. package/dist/puck/editor/editor.chrome.css +48 -161
  266. package/dist/puck/editor/editor.css +48 -161
  267. package/dist/puck/editor/editor.model.d.ts +8 -3
  268. package/dist/puck/editor/editor.model.js +16 -10
  269. package/dist/puck/editor/experimentOutlinePlugin.d.ts +3 -0
  270. package/dist/puck/editor/experimentOutlinePlugin.js +89 -0
  271. package/dist/puck/editor/integration.d.ts +10 -0
  272. package/dist/puck/editor/integration.js +30 -0
  273. package/dist/puck/editor/xpGoalPuckEditorField.d.ts +7 -0
  274. package/dist/puck/editor/xpGoalPuckEditorField.js +134 -0
  275. package/dist/puck/experimentSlotField.d.ts +2 -0
  276. package/dist/puck/experimentSlotField.js +11 -3
  277. package/dist/puck/hostContext.d.ts +10 -0
  278. package/dist/puck/hostContext.js +12 -0
  279. package/dist/puck/integration.d.ts +6 -0
  280. package/dist/puck/integration.js +21 -0
  281. package/dist/puck/routes.d.ts +12 -0
  282. package/dist/puck/routes.js +11 -0
  283. package/dist/puck/slotConfig.d.ts +1 -1
  284. package/dist/puck/slotConfig.js +2 -2
  285. package/dist/puck/xpGoalField.d.ts +3 -0
  286. package/dist/puck/xpGoalField.js +6 -0
  287. package/dist/results/health.d.ts +4 -0
  288. package/dist/results/health.js +27 -0
  289. package/dist/results/snapshot.js +15 -2
  290. package/dist/sanitizeReadinessDetail.js +51 -1
  291. package/dist/server/metadata.js +9 -2
  292. package/dist/server/normaliseContentRelationships.d.ts +7 -0
  293. package/dist/server/normaliseContentRelationships.js +62 -0
  294. package/dist/server/pageKindSeoContract.d.ts +27 -0
  295. package/dist/server/pageKindSeoContract.js +80 -0
  296. package/dist/server/recordContentChange.d.ts +1 -1
  297. package/dist/server/recordContentChange.js +72 -13
  298. package/dist/server/resolveExperimentBlocks.d.ts +5 -0
  299. package/dist/server/resolveExperimentBlocks.js +54 -0
  300. package/dist/server/runResults.js +92 -19
  301. package/dist/server/toClientAssignments.js +2 -1
  302. package/dist/transitions/addMetric.d.ts +2 -1
  303. package/dist/transitions/addMetric.js +28 -1
  304. package/dist/transitions/checkStart.d.ts +17 -1
  305. package/dist/transitions/checkStart.js +99 -7
  306. package/dist/transitions/decide.js +40 -1
  307. package/dist/transitions/deleteExperiment.d.ts +3 -1
  308. package/dist/transitions/deleteExperiment.js +27 -1
  309. package/dist/transitions/kernel.js +4 -0
  310. package/dist/transitions/makeContent.d.ts +5 -0
  311. package/dist/transitions/makeContent.js +39 -11
  312. package/dist/transitions/pageCollection.d.ts +10 -0
  313. package/dist/transitions/pageCollection.js +20 -0
  314. package/dist/transitions/pageVersions.d.ts +10 -0
  315. package/dist/transitions/pageVersions.js +44 -0
  316. package/dist/transitions/phaseRecord.d.ts +1 -0
  317. package/dist/transitions/placementInteractions.d.ts +40 -0
  318. package/dist/transitions/placementInteractions.js +94 -0
  319. package/dist/transitions/placements.d.ts +10 -6
  320. package/dist/transitions/placements.js +156 -2
  321. package/dist/transitions/preflightData.d.ts +8 -0
  322. package/dist/transitions/preflightData.js +248 -36
  323. package/dist/transitions/resolveCaps.d.ts +1 -0
  324. package/dist/transitions/resolveCaps.js +6 -2
  325. package/dist/transitions/start.js +77 -3
  326. package/dist/types/admin.d.ts +1 -1
  327. package/dist/types/assignments.d.ts +1 -0
  328. package/dist/types/contracts.d.ts +31 -10
  329. package/dist/types/endpoints.d.ts +92 -5
  330. package/dist/types/index.d.ts +3 -0
  331. package/dist/types/puck.d.ts +7 -1
  332. package/dist/types/snapshot.d.ts +17 -4
  333. package/dist/types/surface.d.ts +13 -0
  334. package/dist/ui/Tabs.d.ts +4 -2
  335. package/dist/ui/Tabs.js +33 -26
  336. package/dist/ui/accent.d.ts +22 -3
  337. package/dist/ui/accent.js +74 -16
  338. package/package.json +12 -2
package/dist/options.js CHANGED
@@ -1,4 +1,4 @@
1
- import { contrast, deriveAccent } from './ui/accent.js';
1
+ import { validateAccent } from './ui/accent.js';
2
2
  import { DEFAULT_MAX_BODY_BYTES } from './constants.js';
3
3
  const normalisedCollections = new WeakMap();
4
4
  /** Resolve once per registration list; every consumer shares the same field defaults. */ export function puckCollections(options) {
@@ -49,7 +49,8 @@ export function resolveOptions(input) {
49
49
  if (![
50
50
  'block',
51
51
  'document',
52
- 'setting'
52
+ 'setting',
53
+ 'page'
53
54
  ].includes(key)) throw new Error(`Unknown experiments admin.descriptions option: ${key}`);
54
55
  if (value !== undefined && typeof value !== 'string') throw new Error(`Experiments admin.descriptions.${key} must be a string`);
55
56
  }
@@ -62,20 +63,38 @@ export function resolveOptions(input) {
62
63
  if (!entry || typeof entry.slug !== 'string' || !entry.slug.trim() || slugs.has(entry.slug) || typeof entry.pathOf !== 'function' || typeof entry.findByPath !== 'function') {
63
64
  throw new Error('Experiments pages.collections requires unique slugs, pathOf and findByPath functions');
64
65
  }
66
+ if (entry.contentOf !== undefined && typeof entry.contentOf !== 'function') throw new Error('Experiments contentOf must be a function');
67
+ for (const [key, max] of [
68
+ [
69
+ 'copyHint',
70
+ 200
71
+ ],
72
+ [
73
+ 'copyLabel',
74
+ 40
75
+ ]
76
+ ]){
77
+ const value = entry[key];
78
+ if (value !== undefined && (typeof value !== 'string' || !value.trim() || value.length > max)) throw new Error(`Experiments ${key} must be a non-empty string of at most ${max} characters`);
79
+ }
80
+ if (entry.copyUrl !== undefined && !validCopyUrl(entry.copyUrl)) throw new Error('Experiments copyUrl must be a root-relative path');
81
+ if (entry.copyLabel !== undefined && entry.copyUrl === undefined) throw new Error('Experiments copyLabel requires copyUrl');
82
+ slugs.add(entry.slug);
83
+ }
84
+ }
85
+ if (input.blocks !== undefined) {
86
+ if (!input.blocks || !Array.isArray(input.blocks.collections) || !input.blocks.collections.length) throw new Error('Experiments blocks.collections must be a non-empty array');
87
+ const slugs = new Set();
88
+ for (const entry of input.blocks.collections){
89
+ if (!entry || typeof entry.slug !== 'string' || !entry.slug.trim() || slugs.has(entry.slug) || typeof entry.field !== 'string' || !entry.field.trim() || entry.blockSlug !== undefined && (typeof entry.blockSlug !== 'string' || !entry.blockSlug.trim()) || entry.appliesTo !== undefined && typeof entry.appliesTo !== 'function') throw new Error('Invalid experiments blocks collection');
90
+ if (puckCollections(input).some((puck)=>puck.slug === entry.slug) && !entry.appliesTo) throw new Error('A collection registered in puck and blocks requires appliesTo');
65
91
  slugs.add(entry.slug);
66
92
  }
67
93
  }
68
94
  if (input.theme !== undefined) {
69
95
  if (!input.theme || typeof input.theme !== 'object' || Array.isArray(input.theme)) throw new Error('Experiments theme must be an object');
70
96
  for (const key of Object.keys(input.theme))if (key !== 'accent') throw new Error(`Unknown experiments theme option: ${key}`);
71
- const accent = input.theme.accent;
72
- if (accent !== undefined && accent !== '') {
73
- if (typeof accent !== 'string' || !/^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test(accent)) throw new Error('Experiments theme.accent must be a CSS hex colour (#rgb or #rrggbb)');
74
- for (const [mode, set] of Object.entries(deriveAccent(accent))){
75
- const ratio = contrast(set.accent, set.fg);
76
- if (ratio < 4.5) throw new Error(`Experiments theme.accent ${accent}: ${mode} foreground contrast ${ratio.toFixed(2)}:1 is below 4.5:1`);
77
- }
78
- }
97
+ validateAccent(input.theme.accent);
79
98
  }
80
99
  const ids = new Set();
81
100
  for (const point of input.flagPoints ?? []){
@@ -157,3 +176,17 @@ export function resolveOptions(input) {
157
176
  }
158
177
  };
159
178
  }
179
+ function validCopyUrl(value) {
180
+ if (typeof value !== 'string' || !/^\/(?![/\\])/.test(value) || /[\s\x00-\x1f\x7f]/.test(value)) return false;
181
+ try {
182
+ return new URL(value, 'http://x').origin === 'http://x';
183
+ } catch {
184
+ return false;
185
+ }
186
+ }
187
+ /** One published-row rule shared by scans, bindings, writes and editor links. */ export function documentEditor(options, collection, doc) {
188
+ const block = options.blocks?.collections.find((entry)=>entry.slug === collection);
189
+ const puck = options.puck?.enabled !== false && puckCollections(options).some((entry)=>entry.slug === collection);
190
+ if (block && (block.appliesTo ? block.appliesTo(doc) : true)) return 'blocks';
191
+ return puck ? 'puck' : 'page';
192
+ }
package/dist/plugin.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import { buildCollections } from './collections/index.js';
2
2
  import { buildEndpoints } from './endpoints/index.js';
3
3
  import { contentChangesBeforeHook, contentChangesHook } from './hooks/contentChanges.js';
4
- import { pageGuardAfterOperation, pageGuardBeforeChange, pageGuardBeforeDelete, pageGuardBeforeOperation } from './hooks/pageGuard.js';
4
+ import { pageGuardAfterChange, pageGuardAfterOperation, pageGuardBeforeChange, pageGuardBeforeDelete, pageGuardBeforeOperation } from './hooks/pageGuard.js';
5
5
  import { createOnInit } from './onInit.js';
6
6
  import { puckCollections, resolveOptions } from './options.js';
7
+ import { injectTestBlock } from './admin/blocks/inject.js';
7
8
  import * as paths from './admin/paths.js';
8
9
  export function createExperimentsPlugin(input) {
9
10
  const options = resolveOptions(input);
@@ -15,6 +16,9 @@ export function createExperimentsPlugin(input) {
15
16
  for (const { slug } of options.pages?.collections ?? []){
16
17
  if (!config.collections?.some((collection)=>collection.slug === slug)) throw new Error(`Unknown experiments page collection: ${slug}`);
17
18
  }
19
+ for (const { slug } of options.blocks?.collections ?? []){
20
+ if (!config.collections?.some((collection)=>collection.slug === slug)) throw new Error(`Unknown experiments blocks collection: ${slug}`);
21
+ }
18
22
  // The frozen factories consume registration/access metadata, never TUser callbacks.
19
23
  // Erase the user subtype only at this non-generic factory boundary.
20
24
  const { collections, globals } = buildCollections(options);
@@ -30,6 +34,10 @@ export function createExperimentsPlugin(input) {
30
34
  xpTestsSurface: {
31
35
  path: paths.XP_TESTS_SURFACE,
32
36
  type: 'component'
37
+ },
38
+ xpTestsNav: {
39
+ path: paths.XP_TESTS_NAV_CLIENT,
40
+ type: 'component'
33
41
  }
34
42
  },
35
43
  components: {
@@ -55,40 +63,49 @@ export function createExperimentsPlugin(input) {
55
63
  ...(config.collections ?? []).map((collection)=>{
56
64
  const puck = options.puck?.enabled && registrations.some((entry)=>entry.slug === collection.slug);
57
65
  const page = options.pages?.collections.some((entry)=>entry.slug === collection.slug);
58
- if (!puck && !page) return collection;
66
+ const blocks = options.blocks?.collections.find((entry)=>entry.slug === collection.slug);
67
+ if (!puck && !page && !blocks) return collection;
59
68
  return {
60
69
  ...collection,
70
+ fields: [
71
+ ...page ? [
72
+ {
73
+ name: 'xpPlacementStatus',
74
+ type: 'ui',
75
+ admin: {
76
+ position: 'sidebar',
77
+ components: {
78
+ Field: paths.XP_PLACEMENT_STATUS
79
+ }
80
+ }
81
+ }
82
+ ] : [],
83
+ ...blocks ? injectTestBlock(collection.fields, blocks) : collection.fields
84
+ ],
61
85
  hooks: {
62
86
  ...collection.hooks,
63
87
  beforeChange: [
64
88
  ...collection.hooks?.beforeChange ?? [],
65
- ...page ? [
66
- pageGuardBeforeChange
67
- ] : [],
68
- ...puck ? [
69
- contentChangesBeforeHook
70
- ] : []
89
+ pageGuardBeforeChange,
90
+ contentChangesBeforeHook
91
+ ],
92
+ beforeOperation: [
93
+ ...collection.hooks?.beforeOperation ?? [],
94
+ pageGuardBeforeOperation
95
+ ],
96
+ beforeDelete: [
97
+ ...collection.hooks?.beforeDelete ?? [],
98
+ pageGuardBeforeDelete
99
+ ],
100
+ afterOperation: [
101
+ pageGuardAfterOperation,
102
+ ...collection.hooks?.afterOperation ?? []
71
103
  ],
72
- ...page ? {
73
- beforeOperation: [
74
- ...collection.hooks?.beforeOperation ?? [],
75
- pageGuardBeforeOperation
76
- ],
77
- beforeDelete: [
78
- ...collection.hooks?.beforeDelete ?? [],
79
- pageGuardBeforeDelete
80
- ],
81
- afterOperation: [
82
- pageGuardAfterOperation,
83
- ...collection.hooks?.afterOperation ?? []
84
- ]
85
- } : {},
86
- ...puck ? {
87
- afterChange: [
88
- ...collection.hooks?.afterChange ?? [],
89
- contentChangesHook
90
- ]
91
- } : {}
104
+ afterChange: [
105
+ ...collection.hooks?.afterChange ?? [],
106
+ pageGuardAfterChange,
107
+ contentChangesHook
108
+ ]
92
109
  }
93
110
  };
94
111
  }),
@@ -1,6 +1,8 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useEffect, useState } from 'react';
3
+ import { useEffect, useId, useState } from 'react';
4
+ import { fetchAllDocs } from '../lib/fetchAllDocs.js';
5
+ import { usePuckRoutes } from './hostContext.js';
4
6
  import { pickerCopy } from './copy.js';
5
7
  function isExperimentOption(value) {
6
8
  if (!value || typeof value !== 'object') return false;
@@ -8,14 +10,17 @@ function isExperimentOption(value) {
8
10
  return (typeof doc.id === 'string' || typeof doc.id === 'number') && typeof doc.slug === 'string' && typeof doc.name === 'string' && typeof doc.status === 'string' && Object.hasOwn(pickerCopy.statuses, doc.status);
9
11
  }
10
12
  export function ExperimentPicker({ value, onChange }) {
13
+ const { api } = usePuckRoutes();
14
+ const id = useId();
11
15
  const [experiments, setExperiments] = useState([]);
16
+ const [search, setSearch] = useState('');
12
17
  const [state, setState] = useState('loading');
13
18
  useEffect(()=>{
14
19
  let active = true;
15
20
  const controller = new AbortController();
16
21
  async function load() {
17
22
  try {
18
- const response = await fetch('/api/xp-experiments?where[kind][equals]=block&depth=0&limit=100&sort=-updatedAt', {
23
+ const response = await fetchAllDocs(`${api}/xp-experiments?where[kind][equals]=block&depth=0&sort=id`, {
19
24
  credentials: 'include',
20
25
  signal: controller.signal
21
26
  });
@@ -24,13 +29,7 @@ export function ExperimentPicker({ value, onChange }) {
24
29
  setState(response.status === 403 ? 'forbidden' : 'error');
25
30
  return;
26
31
  }
27
- const data = await response.json();
28
- if (!active) return;
29
- if (!data || typeof data !== 'object' || !('docs' in data) || !Array.isArray(data.docs)) {
30
- setState('error');
31
- return;
32
- }
33
- setExperiments(data.docs.filter(isExperimentOption));
32
+ setExperiments(response.docs.filter(isExperimentOption).sort((a, b)=>(Date.parse(b.updatedAt ?? '') || 0) - (Date.parse(a.updatedAt ?? '') || 0)));
34
33
  setState('ready');
35
34
  } catch {
36
35
  if (active) setState('error');
@@ -41,12 +40,38 @@ export function ExperimentPicker({ value, onChange }) {
41
40
  active = false;
42
41
  controller.abort();
43
42
  };
44
- }, []);
43
+ }, [
44
+ api
45
+ ]);
45
46
  const selected = value ? String(value.id) : '';
47
+ const query = search.trim().toLocaleLowerCase('en-US');
48
+ const visible = experiments.filter((doc)=>String(doc.id) === selected || doc.name.toLocaleLowerCase('en-US').includes(query) || doc.slug.toLocaleLowerCase('en-US').includes(query));
46
49
  return /*#__PURE__*/ _jsxs("div", {
50
+ className: "xp-ui-field",
47
51
  children: [
52
+ experiments.length > 8 ? /*#__PURE__*/ _jsxs("div", {
53
+ className: "xp-ui-field",
54
+ children: [
55
+ /*#__PURE__*/ _jsx("label", {
56
+ htmlFor: `${id}-search`,
57
+ children: pickerCopy.search
58
+ }),
59
+ /*#__PURE__*/ _jsx("input", {
60
+ id: `${id}-search`,
61
+ type: "search",
62
+ className: "xp-ui-input",
63
+ value: search,
64
+ onChange: (event)=>setSearch(event.currentTarget.value)
65
+ })
66
+ ]
67
+ }) : null,
68
+ /*#__PURE__*/ _jsx("label", {
69
+ htmlFor: id,
70
+ children: pickerCopy.label
71
+ }),
48
72
  /*#__PURE__*/ _jsxs("select", {
49
- "aria-label": pickerCopy.label,
73
+ id: id,
74
+ className: "xp-ui-input xp-ui-select",
50
75
  value: selected,
51
76
  disabled: state === 'loading',
52
77
  onChange: (event)=>{
@@ -65,7 +90,7 @@ export function ExperimentPicker({ value, onChange }) {
65
90
  value: selected,
66
91
  children: value.slug
67
92
  }) : null,
68
- experiments.map((doc)=>/*#__PURE__*/ _jsxs("option", {
93
+ visible.map((doc)=>/*#__PURE__*/ _jsxs("option", {
69
94
  value: String(doc.id),
70
95
  children: [
71
96
  doc.name,
@@ -75,13 +100,19 @@ export function ExperimentPicker({ value, onChange }) {
75
100
  }, doc.id))
76
101
  ]
77
102
  }),
78
- state === 'loading' ? /*#__PURE__*/ _jsx("p", {
103
+ state === 'loading' ? /*#__PURE__*/ _jsx("div", {
104
+ className: "xp-ui-banner",
79
105
  role: "status",
80
- children: pickerCopy.loading
106
+ children: /*#__PURE__*/ _jsx("p", {
107
+ children: pickerCopy.loading
108
+ })
81
109
  }) : null,
82
- state === 'error' || state === 'forbidden' ? /*#__PURE__*/ _jsx("p", {
110
+ state === 'error' || state === 'forbidden' ? /*#__PURE__*/ _jsx("div", {
111
+ className: "xp-ui-banner xp-ui-banner--error",
83
112
  role: "alert",
84
- children: pickerCopy[state]
113
+ children: /*#__PURE__*/ _jsx("p", {
114
+ children: pickerCopy[state]
115
+ })
85
116
  }) : null
86
117
  ]
87
118
  });
@@ -1,3 +1,53 @@
1
1
  import type { ComponentConfig } from '@puckeditor/core';
2
2
  import type { ExperimentSlotProps } from '../types/puck.js';
3
- export declare const ExperimentSlot: ComponentConfig<ExperimentSlotProps>;
3
+ import { type PuckRoutes } from './routes.js';
4
+ export declare function createExperimentSlot({ routes: inputRoutes }?: {
5
+ routes?: PuckRoutes;
6
+ }): ComponentConfig<ExperimentSlotProps>;
7
+ export declare const ExperimentSlot: {
8
+ render: import("@puckeditor/core").PuckComponent<ExperimentSlotProps>;
9
+ label?: string;
10
+ defaultProps?: ExperimentSlotProps | undefined;
11
+ fields?: import("@puckeditor/core").Fields<ExperimentSlotProps, {}> | undefined;
12
+ renderFields?: ((props: {
13
+ fields: Partial<Record<keyof ExperimentSlotProps, import("react").ReactNode>>;
14
+ }) => import("react").ReactElement) | undefined;
15
+ permissions?: Partial<import("@puckeditor/core").Permissions>;
16
+ inline?: boolean;
17
+ resolveFields?: ((data: Omit<import("@puckeditor/core").ComponentData<ExperimentSlotProps, string, Record<string, import("@puckeditor/core").DefaultComponentProps>>, "type">, params: {
18
+ changed: Partial<Record<keyof ExperimentSlotProps, boolean> & {
19
+ id: string;
20
+ }>;
21
+ fields: import("@puckeditor/core").Fields<ExperimentSlotProps, {}>;
22
+ lastFields: import("@puckeditor/core").Fields<ExperimentSlotProps, {}>;
23
+ lastData: Omit<import("@puckeditor/core").ComponentData<ExperimentSlotProps, string, Record<string, import("@puckeditor/core").DefaultComponentProps>>, "type"> | null;
24
+ metadata: import("@puckeditor/core").ComponentMetadata;
25
+ appState: import("@puckeditor/core").AppState;
26
+ parent: import("@puckeditor/core").ComponentData | null;
27
+ }) => import("@puckeditor/core").Fields<ExperimentSlotProps, {}> | Promise<import("@puckeditor/core").Fields<ExperimentSlotProps, {}>>) | undefined;
28
+ resolveData?: ((data: Omit<import("@puckeditor/core").ComponentData<ExperimentSlotProps, string, Record<string, import("@puckeditor/core").DefaultComponentProps>>, "type">, params: {
29
+ changed: Partial<Record<keyof ExperimentSlotProps, boolean> & {
30
+ id: string;
31
+ }>;
32
+ lastData: Omit<import("@puckeditor/core").ComponentData<ExperimentSlotProps, string, Record<string, import("@puckeditor/core").DefaultComponentProps>>, "type"> | null;
33
+ metadata: import("@puckeditor/core").ComponentMetadata;
34
+ trigger: import("@puckeditor/core").ResolveDataTrigger;
35
+ parent: import("@puckeditor/core").ComponentData | null;
36
+ root: import("@puckeditor/core").RootData;
37
+ }) => (Omit<Omit<import("@puckeditor/core").ComponentData<ExperimentSlotProps, string, Record<string, import("@puckeditor/core").DefaultComponentProps>>, "type">, "props"> & {
38
+ props?: Partial<ExperimentSlotProps> | undefined;
39
+ }) | Promise<Omit<Omit<import("@puckeditor/core").ComponentData<ExperimentSlotProps, string, Record<string, import("@puckeditor/core").DefaultComponentProps>>, "type">, "props"> & {
40
+ props?: Partial<ExperimentSlotProps> | undefined;
41
+ }>) | undefined;
42
+ resolvePermissions?: ((data: Omit<import("@puckeditor/core").ComponentData<ExperimentSlotProps, string, Record<string, import("@puckeditor/core").DefaultComponentProps>>, "type">, params: {
43
+ changed: Partial<Record<keyof ExperimentSlotProps, boolean> & {
44
+ id: string;
45
+ }>;
46
+ lastPermissions: Partial<import("@puckeditor/core").Permissions>;
47
+ permissions: Partial<import("@puckeditor/core").Permissions>;
48
+ appState: import("@puckeditor/core").AppState;
49
+ lastData: Omit<import("@puckeditor/core").ComponentData<ExperimentSlotProps, string, Record<string, import("@puckeditor/core").DefaultComponentProps>>, "type"> | null;
50
+ parent: import("@puckeditor/core").ComponentData | null;
51
+ }) => Promise<Partial<import("@puckeditor/core").Permissions>> | Partial<import("@puckeditor/core").Permissions>) | undefined;
52
+ metadata?: import("@puckeditor/core").ComponentMetadata;
53
+ } & import("@puckeditor/core").ComponentConfigExtensions;
@@ -1,16 +1,27 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { ViewportExposure } from '../client/ViewportExposure.js';
3
3
  import { experimentSlotDefaultProps, experimentSlotFields, experimentSlotLabel } from './slotConfig.js';
4
+ import { createExperimentSlotField } from './experimentSlotField.js';
5
+ import { resolvePuckRoutes } from './routes.js';
4
6
  import { slotToRender } from './slot.model.js';
5
- export const ExperimentSlot = {
6
- label: experimentSlotLabel,
7
- fields: experimentSlotFields,
8
- defaultProps: experimentSlotDefaultProps,
9
- render: (props)=>{
10
- const Slot = props[slotToRender(props)];
11
- return /*#__PURE__*/ _jsx(ViewportExposure, {
12
- expose: props.xpExpose,
13
- children: /*#__PURE__*/ _jsx(Slot, {})
14
- });
15
- }
16
- };
7
+ export function createExperimentSlot({ routes: inputRoutes } = {}) {
8
+ const routes = resolvePuckRoutes(inputRoutes);
9
+ return {
10
+ label: experimentSlotLabel,
11
+ fields: {
12
+ ...experimentSlotFields,
13
+ experiment: createExperimentSlotField({
14
+ routes
15
+ })
16
+ },
17
+ defaultProps: experimentSlotDefaultProps,
18
+ render: (props)=>{
19
+ const Slot = props[slotToRender(props)];
20
+ return /*#__PURE__*/ _jsx(ViewportExposure, {
21
+ expose: props.xpExpose,
22
+ children: /*#__PURE__*/ _jsx(Slot, {})
23
+ });
24
+ }
25
+ };
26
+ }
27
+ export const ExperimentSlot = createExperimentSlot();
@@ -1,3 +1,4 @@
1
+ export declare const categoryTitle = "A/B tests";
1
2
  export declare const slotLabels: {
2
3
  readonly control: "Original";
3
4
  readonly variantA: "Variant A";
@@ -5,10 +6,11 @@ export declare const slotLabels: {
5
6
  readonly variantC: "Variant C";
6
7
  };
7
8
  export declare const pickerCopy: {
8
- readonly label: "Experiment";
9
- readonly none: "No experiment";
10
- readonly loading: "Loading experiments…";
11
- readonly error: "Couldn't load experiments.";
9
+ readonly label: "Test";
10
+ readonly none: "No test";
11
+ readonly search: "Search tests";
12
+ readonly loading: "Loading tests…";
13
+ readonly error: "Couldn't load tests.";
12
14
  readonly forbidden: "You can't see tests. Ask an admin.";
13
15
  readonly statuses: {
14
16
  readonly draft: "Draft";
package/dist/puck/copy.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { ARM_LABELS } from '../labels/arms.js';
2
+ export const categoryTitle = 'A/B tests';
2
3
  export const slotLabels = {
3
4
  control: ARM_LABELS.control,
4
5
  variantA: ARM_LABELS.variantA,
@@ -6,10 +7,11 @@ export const slotLabels = {
6
7
  variantC: ARM_LABELS.variantC
7
8
  };
8
9
  export const pickerCopy = {
9
- label: 'Experiment',
10
- none: 'No experiment',
11
- loading: 'Loading experiments…',
12
- error: "Couldn't load experiments.",
10
+ label: 'Test',
11
+ none: 'No test',
12
+ search: 'Search tests',
13
+ loading: 'Loading tests…',
14
+ error: "Couldn't load tests.",
13
15
  forbidden: "You can't see tests. Ask an admin.",
14
16
  statuses: {
15
17
  draft: 'Draft',
@@ -1,11 +1,16 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useId, useRef, useState } from 'react';
4
+ import { Dialog } from '../../ui/Dialog.js';
5
+ import { TextInput } from '../../ui/TextInput.js';
6
+ import { Button } from '../../ui/Button.js';
7
+ import { Banner } from '../../ui/Banner.js';
8
+ import { usePuckRoutes } from '../hostContext.js';
4
9
  import { CreateRefused, createTest } from './createTest.js';
5
10
  import { editorCopy } from './copy.js';
6
11
  import './editor.css';
7
12
  export function CreateExperimentDrawer({ onCreate, onClose, initialName = '' }) {
8
- const titleId = useId();
13
+ const { api } = usePuckRoutes();
9
14
  const nameId = useId();
10
15
  const [name, setName] = useState(initialName);
11
16
  const inFlight = useRef(false);
@@ -17,7 +22,7 @@ export function CreateExperimentDrawer({ onCreate, onClose, initialName = '' })
17
22
  setPending(true);
18
23
  setError('');
19
24
  try {
20
- const experiment = await createTest('/api', {
25
+ const experiment = await createTest(api, {
21
26
  name,
22
27
  kind: 'block',
23
28
  unitType: 'visitor'
@@ -31,59 +36,44 @@ export function CreateExperimentDrawer({ onCreate, onClose, initialName = '' })
31
36
  setPending(false);
32
37
  }
33
38
  }
34
- return /*#__PURE__*/ _jsxs("aside", {
35
- className: "xp-slot-root xp-slot-drawer",
36
- role: "dialog",
37
- "aria-labelledby": titleId,
38
- children: [
39
- /*#__PURE__*/ _jsx("h3", {
40
- className: "xp-slot-title",
41
- id: titleId,
42
- children: editorCopy.createLabel
43
- }),
44
- /*#__PURE__*/ _jsxs("div", {
45
- className: "xp-slot-field",
46
- children: [
47
- /*#__PURE__*/ _jsx("label", {
48
- className: "xp-slot-label",
49
- htmlFor: nameId,
50
- children: editorCopy.name
51
- }),
52
- /*#__PURE__*/ _jsx("input", {
53
- className: "xp-slot-input",
54
- id: nameId,
55
- value: name,
56
- disabled: pending,
57
- onChange: (event)=>setName(event.currentTarget.value)
58
- })
59
- ]
60
- }),
61
- error ? /*#__PURE__*/ _jsx("p", {
62
- className: "xp-slot-error",
63
- role: "alert",
64
- children: error
65
- }) : null,
66
- /*#__PURE__*/ _jsxs("div", {
67
- className: "xp-slot-actions",
68
- children: [
69
- /*#__PURE__*/ _jsx("button", {
70
- className: "xp-slot-button xp-slot-button-primary",
71
- type: "button",
72
- disabled: pending || !name.trim(),
73
- onClick: ()=>{
74
- void submitNew();
75
- },
76
- children: pending ? editorCopy.creating : editorCopy.createLabel
77
- }),
78
- /*#__PURE__*/ _jsx("button", {
79
- className: "xp-slot-button",
80
- type: "button",
81
- disabled: pending,
82
- onClick: onClose,
83
- children: editorCopy.cancel
84
- })
85
- ]
86
- })
87
- ]
39
+ return /*#__PURE__*/ _jsx(Dialog, {
40
+ open: true,
41
+ title: editorCopy.createLabel,
42
+ onClose: onClose,
43
+ dismissible: !pending,
44
+ children: /*#__PURE__*/ _jsxs("div", {
45
+ className: "xp-slot-drawer",
46
+ children: [
47
+ /*#__PURE__*/ _jsx(TextInput, {
48
+ id: nameId,
49
+ label: editorCopy.name,
50
+ value: name,
51
+ disabled: pending,
52
+ onChange: (event)=>setName(event.currentTarget.value)
53
+ }),
54
+ error ? /*#__PURE__*/ _jsx(Banner, {
55
+ tone: "error",
56
+ children: error
57
+ }) : null,
58
+ /*#__PURE__*/ _jsxs("div", {
59
+ className: "xp-ui-button-group",
60
+ children: [
61
+ /*#__PURE__*/ _jsx(Button, {
62
+ variant: "primary",
63
+ disabled: pending || !name.trim(),
64
+ onClick: ()=>{
65
+ void submitNew();
66
+ },
67
+ children: pending ? editorCopy.creating : editorCopy.createLabel
68
+ }),
69
+ /*#__PURE__*/ _jsx(Button, {
70
+ disabled: pending,
71
+ onClick: onClose,
72
+ children: editorCopy.cancel
73
+ })
74
+ ]
75
+ })
76
+ ]
77
+ })
88
78
  });
89
79
  }
@@ -1,4 +1,55 @@
1
1
  import { type ComponentConfig } from '@puckeditor/core';
2
2
  import type { ExperimentSlotProps } from '../../types/puck.js';
3
+ import { type PuckRoutes } from '../routes.js';
3
4
  import './editor.css';
4
- export declare const ExperimentSlotEditor: ComponentConfig<ExperimentSlotProps>;
5
+ export declare function createExperimentSlotEditor({ routes: inputRoutes, accent }?: {
6
+ routes?: PuckRoutes;
7
+ accent?: string;
8
+ }): ComponentConfig<ExperimentSlotProps>;
9
+ export declare const ExperimentSlotEditor: {
10
+ render: import("@puckeditor/core").PuckComponent<ExperimentSlotProps>;
11
+ label?: string;
12
+ defaultProps?: ExperimentSlotProps | undefined;
13
+ fields?: import("@puckeditor/core").Fields<ExperimentSlotProps, {}> | undefined;
14
+ renderFields?: ((props: {
15
+ fields: Partial<Record<keyof ExperimentSlotProps, import("react").ReactNode>>;
16
+ }) => import("react").ReactElement) | undefined;
17
+ permissions?: Partial<import("@puckeditor/core").Permissions>;
18
+ inline?: boolean;
19
+ resolveFields?: ((data: Omit<import("@puckeditor/core").ComponentData<ExperimentSlotProps, string, Record<string, import("@puckeditor/core").DefaultComponentProps>>, "type">, params: {
20
+ changed: Partial<Record<keyof ExperimentSlotProps, boolean> & {
21
+ id: string;
22
+ }>;
23
+ fields: import("@puckeditor/core").Fields<ExperimentSlotProps, {}>;
24
+ lastFields: import("@puckeditor/core").Fields<ExperimentSlotProps, {}>;
25
+ lastData: Omit<import("@puckeditor/core").ComponentData<ExperimentSlotProps, string, Record<string, import("@puckeditor/core").DefaultComponentProps>>, "type"> | null;
26
+ metadata: import("@puckeditor/core").ComponentMetadata;
27
+ appState: import("@puckeditor/core").AppState;
28
+ parent: import("@puckeditor/core").ComponentData | null;
29
+ }) => import("@puckeditor/core").Fields<ExperimentSlotProps, {}> | Promise<import("@puckeditor/core").Fields<ExperimentSlotProps, {}>>) | undefined;
30
+ resolveData?: ((data: Omit<import("@puckeditor/core").ComponentData<ExperimentSlotProps, string, Record<string, import("@puckeditor/core").DefaultComponentProps>>, "type">, params: {
31
+ changed: Partial<Record<keyof ExperimentSlotProps, boolean> & {
32
+ id: string;
33
+ }>;
34
+ lastData: Omit<import("@puckeditor/core").ComponentData<ExperimentSlotProps, string, Record<string, import("@puckeditor/core").DefaultComponentProps>>, "type"> | null;
35
+ metadata: import("@puckeditor/core").ComponentMetadata;
36
+ trigger: import("@puckeditor/core").ResolveDataTrigger;
37
+ parent: import("@puckeditor/core").ComponentData | null;
38
+ root: import("@puckeditor/core").RootData;
39
+ }) => (Omit<Omit<import("@puckeditor/core").ComponentData<ExperimentSlotProps, string, Record<string, import("@puckeditor/core").DefaultComponentProps>>, "type">, "props"> & {
40
+ props?: Partial<ExperimentSlotProps> | undefined;
41
+ }) | Promise<Omit<Omit<import("@puckeditor/core").ComponentData<ExperimentSlotProps, string, Record<string, import("@puckeditor/core").DefaultComponentProps>>, "type">, "props"> & {
42
+ props?: Partial<ExperimentSlotProps> | undefined;
43
+ }>) | undefined;
44
+ resolvePermissions?: ((data: Omit<import("@puckeditor/core").ComponentData<ExperimentSlotProps, string, Record<string, import("@puckeditor/core").DefaultComponentProps>>, "type">, params: {
45
+ changed: Partial<Record<keyof ExperimentSlotProps, boolean> & {
46
+ id: string;
47
+ }>;
48
+ lastPermissions: Partial<import("@puckeditor/core").Permissions>;
49
+ permissions: Partial<import("@puckeditor/core").Permissions>;
50
+ appState: import("@puckeditor/core").AppState;
51
+ lastData: Omit<import("@puckeditor/core").ComponentData<ExperimentSlotProps, string, Record<string, import("@puckeditor/core").DefaultComponentProps>>, "type"> | null;
52
+ parent: import("@puckeditor/core").ComponentData | null;
53
+ }) => Promise<Partial<import("@puckeditor/core").Permissions>> | Partial<import("@puckeditor/core").Permissions>) | undefined;
54
+ metadata?: import("@puckeditor/core").ComponentMetadata;
55
+ } & import("@puckeditor/core").ComponentConfigExtensions;