@delmaredigital/payload-experiments 0.2.0-rc.7 → 0.3.0-rc.4
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.
- package/README.md +202 -26
- package/dist/admin/PreviewLinks.d.ts +2 -1
- package/dist/admin/PreviewLinks.js +50 -21
- package/dist/admin/TestsNavLink.d.ts +3 -1
- package/dist/admin/TestsNavLink.js +2 -2
- package/dist/admin/actions/ConfirmDialog.d.ts +7 -4
- package/dist/admin/actions/ConfirmDialog.js +37 -82
- package/dist/admin/actions/DecideDialog.d.ts +3 -1
- package/dist/admin/actions/DecideDialog.js +30 -13
- package/dist/admin/actions/MakeContentResult.d.ts +13 -2
- package/dist/admin/actions/MakeContentResult.js +115 -16
- package/dist/admin/actions/actions.model.d.ts +2 -4
- package/dist/admin/actions/actions.model.js +2 -3
- package/dist/admin/allocation.model.js +3 -5
- package/dist/admin/api.d.ts +10 -4
- package/dist/admin/api.js +11 -7
- package/dist/admin/armSelect/ArmSelectField.js +14 -22
- package/dist/admin/arms/arms.model.d.ts +1 -21
- package/dist/admin/arms/arms.model.js +1 -153
- package/dist/admin/copy/actions.d.ts +39 -3
- package/dist/admin/copy/actions.js +40 -4
- package/dist/admin/copy/armSelect.js +5 -5
- package/dist/admin/copy/arms.d.ts +1 -6
- package/dist/admin/copy/arms.js +1 -16
- package/dist/admin/copy/audience.d.ts +2 -1
- package/dist/admin/copy/audience.js +3 -2
- package/dist/admin/copy/common.d.ts +13 -6
- package/dist/admin/copy/common.js +14 -8
- package/dist/admin/copy/goals.d.ts +21 -6
- package/dist/admin/copy/goals.js +27 -6
- package/dist/admin/copy/readiness.d.ts +22 -0
- package/dist/admin/copy/readiness.js +21 -0
- package/dist/admin/copy/recommendation.d.ts +0 -3
- package/dist/admin/copy/recommendation.js +0 -3
- package/dist/admin/copy/results.d.ts +43 -2
- package/dist/admin/copy/results.js +44 -3
- package/dist/admin/copy/review.d.ts +2 -2
- package/dist/admin/copy/review.js +2 -2
- package/dist/admin/copy/setting.d.ts +2 -0
- package/dist/admin/copy/setting.js +2 -0
- package/dist/admin/copy/surface.d.ts +7 -5
- package/dist/admin/copy/surface.js +9 -5
- package/dist/admin/copy/testPage.d.ts +10 -0
- package/dist/admin/copy/testPage.js +10 -0
- package/dist/admin/format/date.d.ts +2 -0
- package/dist/admin/format/date.js +10 -0
- package/dist/admin/paths.d.ts +2 -1
- package/dist/admin/paths.js +2 -1
- package/dist/admin/previewLinks.model.d.ts +4 -2
- package/dist/admin/previewLinks.model.js +8 -7
- package/dist/admin/results/CiChart.js +11 -21
- package/dist/admin/results/DataQualityPanel.d.ts +4 -2
- package/dist/admin/results/DataQualityPanel.js +30 -17
- package/dist/admin/results/DataTable.d.ts +16 -0
- package/dist/admin/results/DataTable.js +152 -0
- package/dist/admin/results/StatTile.d.ts +7 -0
- package/dist/admin/results/StatTile.js +22 -0
- package/dist/admin/results/VerdictHero.d.ts +13 -0
- package/dist/admin/results/{VerdictCard.js → VerdictHero.js} +15 -12
- package/dist/admin/results/legacy.d.ts +16 -0
- package/dist/admin/results/legacy.js +27 -0
- package/dist/admin/results/releasePages.model.d.ts +17 -0
- package/dist/admin/results/releasePages.model.js +10 -0
- package/dist/admin/results/results.css +64 -0
- package/dist/admin/results/results.model.d.ts +11 -7
- package/dist/admin/results/results.model.js +24 -7
- package/dist/admin/results/snapshot.d.ts +14 -0
- package/dist/admin/results/snapshot.js +3 -0
- package/dist/admin/surface/ResultsRoute.d.ts +1 -1
- package/dist/admin/surface/ResultsRoute.js +59 -34
- package/dist/admin/surface/SurfaceShell.d.ts +11 -2
- package/dist/admin/surface/SurfaceShell.js +49 -14
- package/dist/admin/surface/TestsHome.d.ts +6 -1
- package/dist/admin/surface/TestsHome.js +182 -114
- package/dist/admin/surface/TestsSurface.js +56 -16
- package/dist/admin/surface/goals/GoalForm.js +341 -308
- package/dist/admin/surface/goals/GoalsList.js +123 -89
- package/dist/admin/surface/goals/data.d.ts +8 -2
- package/dist/admin/surface/goals/data.js +24 -16
- package/dist/admin/surface/goals/goals.css +10 -13
- package/dist/admin/surface/home.model.d.ts +8 -8
- package/dist/admin/surface/home.model.js +23 -12
- package/dist/admin/surface/newTest/NewTest.js +9 -9
- package/dist/admin/surface/pageBlock/parts.js +31 -26
- package/dist/admin/surface/readiness/ActionDialogs.js +47 -19
- package/dist/admin/surface/readiness/Banner.d.ts +3 -0
- package/dist/admin/surface/readiness/Banner.js +58 -33
- package/dist/admin/surface/readiness/SaveStatus.d.ts +1 -0
- package/dist/admin/surface/readiness/SaveStatus.js +6 -3
- package/dist/admin/surface/readiness/readiness.model.d.ts +2 -0
- package/dist/admin/surface/readiness/readiness.model.js +25 -9
- package/dist/admin/surface/readiness/useTransition.d.ts +1 -0
- package/dist/admin/surface/readiness/useTransition.js +2 -1
- package/dist/admin/surface/results/ReleasePages.d.ts +14 -0
- package/dist/admin/surface/results/ReleasePages.js +116 -0
- package/dist/admin/surface/results/ResultsBody.d.ts +11 -1
- package/dist/admin/surface/results/ResultsBody.js +277 -105
- package/dist/admin/surface/results/ResultsHeader.d.ts +10 -0
- package/dist/admin/surface/results/ResultsHeader.js +177 -0
- package/dist/admin/surface/results/recommendation.model.d.ts +3 -2
- package/dist/admin/surface/results/recommendation.model.js +12 -9
- package/dist/admin/surface/results/useResults.d.ts +21 -3
- package/dist/admin/surface/results/useResults.js +2 -1
- package/dist/admin/surface/setting/parts.js +75 -88
- package/dist/admin/surface/setting/setting.css +3 -15
- package/dist/admin/surface/surface.css +61 -47
- package/dist/admin/surface/test/AdvancedSection.js +55 -48
- package/dist/admin/surface/test/GoalSection.js +46 -47
- package/dist/admin/surface/test/LengthSection.js +46 -34
- package/dist/admin/surface/test/Rail.d.ts +4 -0
- package/dist/admin/surface/test/Rail.js +83 -0
- package/dist/admin/surface/test/ReadinessBar.d.ts +5 -0
- package/dist/admin/surface/test/ReadinessBar.js +33 -0
- package/dist/admin/surface/test/Section.d.ts +1 -1
- package/dist/admin/surface/test/Section.js +36 -16
- package/dist/admin/surface/test/TargetingSection.js +168 -142
- package/dist/admin/surface/test/TestPage.d.ts +2 -1
- package/dist/admin/surface/test/TestPage.js +146 -68
- package/dist/admin/surface/test/VersionsSection.js +151 -120
- package/dist/admin/surface/test/fields.d.ts +11 -4
- package/dist/admin/surface/test/fields.js +84 -85
- package/dist/admin/surface/test/jumpToSection.d.ts +3 -0
- package/dist/admin/surface/test/jumpToSection.js +10 -0
- package/dist/admin/surface/test/pageParts.js +9 -3
- package/dist/admin/surface/test/testPage.css +81 -14
- package/dist/admin/surface/test/testPage.model.d.ts +2 -3
- package/dist/admin/surface/test/testPage.model.js +1 -21
- package/dist/admin/surface/useTestDocument.js +12 -5
- package/dist/admin/targeting/targeting.model.d.ts +1 -3
- package/dist/admin/targeting/targeting.model.js +3 -8
- package/dist/admin/theme.css +115 -36
- package/dist/admin/theme.d.ts +38 -0
- package/dist/admin/theme.js +38 -1
- package/dist/admin/useApi.d.ts +17 -2
- package/dist/admin/useLayerUsage.js +14 -23
- package/dist/admin/views/ExperimentsRootView.js +12 -2
- package/dist/admin/views/TestsNavLinkServer.d.ts +2 -0
- package/dist/admin/views/TestsNavLinkServer.js +43 -0
- package/dist/arms/copy.d.ts +6 -0
- package/dist/arms/copy.js +16 -0
- package/dist/arms/model.d.ts +23 -0
- package/dist/arms/model.js +173 -0
- package/dist/collections/experiments/config.js +25 -0
- package/dist/collections/experiments/lockHook.js +3 -1
- package/dist/collections/identities.js +6 -1
- package/dist/collections/metrics.js +7 -0
- package/dist/collections/metricsGuards.d.ts +9 -0
- package/dist/collections/metricsGuards.js +33 -0
- package/dist/contentDone.d.ts +4 -0
- package/dist/contentDone.js +5 -0
- package/dist/db/appends.d.ts +6 -2
- package/dist/db/appends.js +10 -4
- package/dist/db/identities.d.ts +8 -7
- package/dist/db/identities.js +9 -6
- package/dist/db/locks.d.ts +6 -0
- package/dist/db/locks.js +13 -1
- package/dist/db/metricUsage.d.ts +19 -0
- package/dist/db/metricUsage.js +54 -0
- package/dist/db/names.d.ts +5 -0
- package/dist/db/names.js +5 -0
- package/dist/db/pageOccupancy.d.ts +12 -0
- package/dist/db/pageOccupancy.js +45 -0
- package/dist/db/publishedContent.d.ts +10 -0
- package/dist/db/publishedContent.js +43 -0
- package/dist/db/results.d.ts +2 -0
- package/dist/db/results.js +6 -2
- package/dist/db/snapshots.d.ts +18 -6
- package/dist/db/snapshots.js +25 -8
- package/dist/db/sweep.js +11 -6
- package/dist/endpoints/index.js +19 -1
- package/dist/endpoints/results.js +5 -0
- package/dist/endpoints/sanitizeReadinessDetail.d.ts +1 -0
- package/dist/endpoints/sanitizeReadinessDetail.js +2 -0
- package/dist/endpoints/summary.js +20 -2
- package/dist/endpoints/transitions.d.ts +15 -0
- package/dist/endpoints/transitions.js +36 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +28 -0
- package/dist/exports/adminRsc.d.ts +1 -0
- package/dist/exports/adminRsc.js +1 -0
- package/dist/exports/index.d.ts +2 -1
- package/dist/exports/index.js +1 -0
- package/dist/exports/puck.d.ts +2 -1
- package/dist/exports/puck.js +1 -1
- package/dist/exports/puckEditor.d.ts +4 -1
- package/dist/exports/puckEditor.js +3 -1
- package/dist/exports/rsc.d.ts +6 -1
- package/dist/exports/rsc.js +2 -0
- package/dist/hooks/pageGuard.d.ts +5 -0
- package/dist/hooks/pageGuard.js +139 -0
- package/dist/lib/fetchAllDocs.d.ts +16 -0
- package/dist/lib/fetchAllDocs.js +56 -0
- package/dist/log.js +6 -3
- package/dist/manifest/compile.d.ts +5 -0
- package/dist/manifest/compile.js +5 -1
- package/dist/options.js +16 -0
- package/dist/plugin.js +30 -2
- package/dist/puck/ExperimentPicker.js +47 -16
- package/dist/puck/ExperimentSlot.d.ts +51 -1
- package/dist/puck/ExperimentSlot.js +23 -12
- package/dist/puck/copy.d.ts +6 -5
- package/dist/puck/copy.js +6 -5
- package/dist/puck/editor/CreateExperimentDrawer.js +46 -56
- package/dist/puck/editor/ExperimentSlotEditor.d.ts +52 -1
- package/dist/puck/editor/ExperimentSlotEditor.js +292 -137
- package/dist/puck/editor/ResultsReadout.d.ts +6 -0
- package/dist/puck/editor/ResultsReadout.js +99 -0
- package/dist/puck/editor/VersionBuilder.d.ts +34 -0
- package/dist/puck/editor/VersionBuilder.js +317 -0
- package/dist/puck/editor/armWrites.d.ts +7 -0
- package/dist/puck/editor/armWrites.js +71 -0
- package/dist/puck/editor/armsCache.d.ts +14 -0
- package/dist/puck/editor/armsCache.js +78 -0
- package/dist/puck/editor/copy.d.ts +33 -1
- package/dist/puck/editor/copy.js +45 -8
- package/dist/puck/editor/createTest.d.ts +1 -1
- package/dist/puck/editor/createTest.js +25 -14
- package/dist/puck/editor/editor.chrome.css +121 -0
- package/dist/puck/editor/editor.css +288 -204
- package/dist/puck/editor/editor.model.d.ts +4 -1
- package/dist/puck/editor/editor.model.js +10 -7
- package/dist/puck/editor/experimentOutlinePlugin.d.ts +3 -0
- package/dist/puck/editor/experimentOutlinePlugin.js +89 -0
- package/dist/puck/experimentSlotField.d.ts +2 -0
- package/dist/puck/experimentSlotField.js +11 -3
- package/dist/puck/hostContext.d.ts +10 -0
- package/dist/puck/hostContext.js +12 -0
- package/dist/puck/routes.d.ts +12 -0
- package/dist/puck/routes.js +11 -0
- package/dist/puck/slotConfig.d.ts +1 -1
- package/dist/puck/slotConfig.js +2 -2
- package/dist/results/aggregate.js +4 -3
- package/dist/results/analyse.d.ts +4 -1
- package/dist/results/analyse.js +27 -11
- package/dist/results/cuped.js +3 -2
- package/dist/results/identityCredit.d.ts +15 -7
- package/dist/results/identityCredit.js +32 -33
- package/dist/results/rolloutUnits.js +2 -1
- package/dist/results/snapshot.js +8 -2
- package/dist/results/srm.js +2 -1
- package/dist/sanitizeReadinessDetail.d.ts +7 -0
- package/dist/sanitizeReadinessDetail.js +63 -0
- package/dist/server/linkIdentity.d.ts +10 -1
- package/dist/server/linkIdentity.js +17 -15
- package/dist/server/metadata.d.ts +5 -0
- package/dist/server/metadata.js +18 -3
- package/dist/server/normaliseContentRelationships.d.ts +7 -0
- package/dist/server/normaliseContentRelationships.js +62 -0
- package/dist/server/recordContentChange.d.ts +1 -1
- package/dist/server/recordContentChange.js +17 -8
- package/dist/server/runResults.d.ts +3 -2
- package/dist/server/runResults.js +157 -35
- package/dist/targeting/values.d.ts +4 -0
- package/dist/targeting/values.js +10 -0
- package/dist/transitions/addMetric.js +8 -0
- package/dist/transitions/archive.js +2 -3
- package/dist/transitions/checkStart.d.ts +10 -3
- package/dist/transitions/checkStart.js +25 -0
- package/dist/transitions/contentFields.d.ts +7 -0
- package/dist/transitions/contentFields.js +20 -0
- package/dist/transitions/decide.js +22 -2
- package/dist/transitions/dismissContentEntry.d.ts +8 -0
- package/dist/transitions/dismissContentEntry.js +45 -0
- package/dist/transitions/makeContent.d.ts +5 -2
- package/dist/transitions/makeContent.js +217 -118
- package/dist/transitions/phaseRecord.d.ts +8 -2
- package/dist/transitions/placements.d.ts +3 -6
- package/dist/transitions/placements.js +12 -1
- package/dist/transitions/preflightData.d.ts +9 -1
- package/dist/transitions/preflightData.js +138 -20
- package/dist/transitions/releasePages.d.ts +8 -0
- package/dist/transitions/releasePages.js +26 -0
- package/dist/transitions/restoreContentEntry.d.ts +8 -0
- package/dist/transitions/restoreContentEntry.js +47 -0
- package/dist/transitions/start.js +62 -7
- package/dist/types/contracts.d.ts +24 -4
- package/dist/types/endpoints.d.ts +34 -3
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/reasons.d.ts +3 -0
- package/dist/types/reasons.js +1 -0
- package/dist/types/snapshot.d.ts +6 -1
- package/dist/ui/Banner.d.ts +2 -0
- package/dist/ui/Banner.js +2 -0
- package/dist/ui/Breadcrumb.d.ts +7 -0
- package/dist/ui/Breadcrumb.js +25 -0
- package/dist/ui/Button.d.ts +15 -0
- package/dist/ui/Button.js +32 -0
- package/dist/ui/Card.d.ts +12 -0
- package/dist/ui/Card.js +40 -0
- package/dist/ui/Checkbox.d.ts +2 -0
- package/dist/ui/Checkbox.js +2 -0
- package/dist/{admin/components → ui}/ChipsInput.d.ts +4 -3
- package/dist/{admin/components → ui}/ChipsInput.js +20 -12
- package/dist/ui/Dialog.d.ts +13 -0
- package/dist/ui/Dialog.js +85 -0
- package/dist/ui/EmptyState.d.ts +2 -0
- package/dist/ui/EmptyState.js +2 -0
- package/dist/ui/Feedback.d.ts +24 -0
- package/dist/ui/Feedback.js +40 -0
- package/dist/ui/Inputs.d.ts +34 -0
- package/dist/ui/Inputs.js +225 -0
- package/dist/ui/NumberInput.d.ts +2 -0
- package/dist/ui/NumberInput.js +2 -0
- package/dist/ui/Range.d.ts +2 -0
- package/dist/ui/Range.js +2 -0
- package/dist/ui/SearchInput.d.ts +2 -0
- package/dist/ui/SearchInput.js +2 -0
- package/dist/ui/SectionCard.d.ts +2 -0
- package/dist/ui/SectionCard.js +2 -0
- package/dist/ui/Select.d.ts +2 -0
- package/dist/ui/Select.js +2 -0
- package/dist/ui/StatusChip.d.ts +2 -0
- package/dist/ui/StatusChip.js +2 -0
- package/dist/ui/Tabs.d.ts +16 -0
- package/dist/ui/Tabs.js +61 -0
- package/dist/ui/TextInput.d.ts +2 -0
- package/dist/ui/TextInput.js +2 -0
- package/dist/ui/Textarea.d.ts +2 -0
- package/dist/ui/Textarea.js +2 -0
- package/dist/ui/Toggle.d.ts +2 -0
- package/dist/ui/Toggle.js +2 -0
- package/dist/ui/accent.d.ts +40 -0
- package/dist/ui/accent.js +139 -0
- package/dist/ui/index.d.ts +9 -0
- package/dist/ui/index.js +9 -0
- package/dist/ui/ui.css +92 -0
- package/package.json +5 -2
- package/dist/admin/components/chipsInput.css +0 -11
- package/dist/admin/results/ArmTable.d.ts +0 -8
- package/dist/admin/results/ArmTable.js +0 -113
- package/dist/admin/results/VerdictCard.d.ts +0 -9
- package/dist/admin/surface/goals/GoalsBreadcrumb.d.ts +0 -5
- package/dist/admin/surface/goals/GoalsBreadcrumb.js +0 -47
- package/dist/admin/surface/test/TestHeader.d.ts +0 -4
- package/dist/admin/surface/test/TestHeader.js +0 -70
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @delmaredigital/payload-experiments
|
|
2
2
|
|
|
3
|
+
> ⚠️ Upgrading to 0.3? For 0.3.0-rc.1, every `linkIdentity` call must supply `verified`; it now returns an outcome. Run `payload migrate:create` for the nullable `xp_identities.verified_at` column and `user_id` index, plus the release-pages columns listed in the CHANGELOG (`decision_pages_released_at` / `decision_pages_released_by` on `xp_experiments` and their `version_` counterparts on `_xp_experiments_v`). Existing identity links become unverified; see the CHANGELOG for optional backfill.
|
|
4
|
+
|
|
3
5
|
> ⚠️ Upgrading to 0.2? After upgrading, run `payload generate:importmap`: the Tests surface is registered through admin.dependencies, and without it the Tests routes show a 'not installed' message instead of the Tests screen. In 0.2.0-rc.2, the Tests server views moved from `/rsc` to `/admin/rsc`, so the import map must be regenerated when upgrading from rc.1 too. The old experiments edit-form components are gone from `/admin/client`, and the `xp-experiments` admin list and edit views now redirect to the Tests surface.
|
|
4
6
|
|
|
5
7
|
A/B testing for Payload CMS and Next.js, with results inside the admin and data
|
|
@@ -87,7 +89,7 @@ permission is granted only for `true`. Optional `access.admin` and
|
|
|
87
89
|
`access.readResults` default to `manage`; `access.readIdentities` defaults to
|
|
88
90
|
`admin`. Use `admin` for code experiments and changing the analysis method.
|
|
89
91
|
Other plugin options are `flagPoints`, `metricSources`, `eventSources`,
|
|
90
|
-
`attributeKeys`, `puck` and `events`:
|
|
92
|
+
`attributeKeys`, `pages`, `puck` and `events`:
|
|
91
93
|
|
|
92
94
|
- `attributeKeys` declares targeting attributes as `{ type, values? }`, with
|
|
93
95
|
`type` one of `string`, `number`, `boolean`; optional string `values` supplies
|
|
@@ -261,29 +263,93 @@ Other server integrations:
|
|
|
261
263
|
outside a Next request; a user-only server conversion can supply `userId`.
|
|
262
264
|
Record revenue through trusted server code or a metric source, never a client
|
|
263
265
|
beacon.
|
|
264
|
-
- `linkIdentity(serverRuntime, { headers, userId })` belongs on
|
|
265
|
-
user-creation and sign-in path
|
|
266
|
-
opaque user id using insert-ignore on its own connection and never throws into
|
|
267
|
-
sign-in. For visitor experiments using user-keyed outcomes, credit goes to the
|
|
268
|
-
earliest-exposed linked visitor; cross-group links are reported separately.
|
|
266
|
+
- `linkIdentity(serverRuntime, { headers, userId, verified })` belongs on every
|
|
267
|
+
user-creation and proving sign-in path; see Identity linking below.
|
|
269
268
|
- `eraseUser(payload, { userId })` deletes the user's identities and associated
|
|
270
269
|
events transactionally and returns `{ identities, events }` counts. Call it
|
|
271
270
|
from the host's authorized user-erasure flow.
|
|
272
271
|
- `recordContentChange({ doc, previousDoc, req, collection })` can be called
|
|
273
272
|
from a host collection's `afterChange` for content tagged with the arm-select
|
|
274
273
|
field; for a collection with drafts, also add `captureContentBaseline` to its
|
|
275
|
-
`beforeChange` hooks so the previously published content can be compared.
|
|
274
|
+
`beforeChange` hooks so the previously published content can be compared.
|
|
275
|
+
Collections without drafts can also add `captureContentBaseline` for an exact
|
|
276
|
+
raw comparison; without it, the helper normalises relationships to ids. No host
|
|
277
|
+
change is required for collections without drafts. Supply that hook's transaction request. Puck collections get the hook
|
|
276
278
|
automatically when enabled. Markers compare published content; drafts and
|
|
277
279
|
autosaves do not add markers (collections without drafts record on save).
|
|
278
280
|
Neither the helper nor the installed hooks ever throw into your save: a
|
|
279
281
|
failure is logged and the content save proceeds.
|
|
280
282
|
- `experimentMetadata(serverRuntime, path)` returns metadata to merge into the
|
|
281
283
|
page's Next metadata: live variant targets get noindex and an Original
|
|
282
|
-
canonical; archived losing paths point canonically to the winner.
|
|
284
|
+
canonical; archived losing paths point canonically to the winner. It reads
|
|
285
|
+
the signed request context via `headers()`, so it decides from the URL the
|
|
286
|
+
visitor requested, not the rewritten route, and the Original's URL is never
|
|
287
|
+
noindex. While a test is live the Original's URL also gets a canonical to
|
|
288
|
+
itself, even when it is served with a variant's route: merge this result after
|
|
289
|
+
your own metadata so its `alternates.canonical` wins over one derived from the
|
|
290
|
+
rendered route. Call it only from a dynamic `generateMetadata`, never inside
|
|
291
|
+
`'use cache'`; `path` is the fallback when the proxy did not run.
|
|
283
292
|
`experimentSitemapFilter(path, manifest)` is a synchronous build-time filter:
|
|
284
293
|
pass a privately server-loaded manifest and keep paths for which it returns
|
|
285
294
|
`true`, excluding live variants and archived losing paths.
|
|
286
295
|
|
|
296
|
+
## Identity linking
|
|
297
|
+
|
|
298
|
+
Call `linkIdentity(serverRuntime, { headers, userId, verified })` on every
|
|
299
|
+
user-creation and proving sign-in path. It links the signed anonymous cookie
|
|
300
|
+
to an opaque user id on its own connection and never throws into sign-in.
|
|
301
|
+
`verified` is required. Pass `true` only when **this request proves, in this
|
|
302
|
+
browser, that the person owns the account**: a magic-link or one-time-code
|
|
303
|
+
callback, password sign-in, or OAuth callback. It does not mean "a session
|
|
304
|
+
exists" or "the account's email is verified". Registration alone uses `false`.
|
|
305
|
+
|
|
306
|
+
```ts
|
|
307
|
+
// Registration, before proof of account ownership:
|
|
308
|
+
await linkIdentity(serverRuntime, { headers, userId, verified: false })
|
|
309
|
+
// After successful magic-link/code verification in this browser:
|
|
310
|
+
await linkIdentity(serverRuntime, { headers, userId, verified: true })
|
|
311
|
+
// After a successful password sign-in (or OAuth callback):
|
|
312
|
+
await linkIdentity(serverRuntime, { headers, userId, verified: true })
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
An auth "after" hook that fires whenever a new session cookie is set also
|
|
316
|
+
fires on routine session refreshes; pass `verified: true` only from proving
|
|
317
|
+
sign-in paths, never from a refresh.
|
|
318
|
+
|
|
319
|
+
The result is `'linked'` for an inserted link, `'verified'` for an upgraded
|
|
320
|
+
unverified link, `'skipped'` for no consent, no valid unit cookie or an existing
|
|
321
|
+
link with nothing to change, and `'failed'` for an error (safely logged).
|
|
322
|
+
First link per browser wins; a different account never replaces it.
|
|
323
|
+
|
|
324
|
+
For visitor experiments with user-keyed events, verified links are always
|
|
325
|
+
eligible. Unverified links are eligible only before the first verification
|
|
326
|
+
across all of that user's browsers, or indefinitely if nobody ever verifies.
|
|
327
|
+
Each event goes to the earliest exposed eligible visitor. Eligible links
|
|
328
|
+
spanning groups are reported as a diagnostic. User-keyed CUPED covariates use
|
|
329
|
+
the same eligibility rule at each visitor's first exposure and credit one
|
|
330
|
+
aggregate value to the earliest eligible visitor per user.
|
|
331
|
+
|
|
332
|
+
Accepted limits:
|
|
333
|
+
|
|
334
|
+
- Cross-device: register in A, verify on B, keep using A. A's events after B's
|
|
335
|
+
proof are not credited until a proving sign-in on A upgrades A. Events before
|
|
336
|
+
B's proof, including the registration funnel, remain credited. Once A is
|
|
337
|
+
upgraded, its link is verified and eligible for all its events, so the next
|
|
338
|
+
results run also credits A's events from between B's proof and the upgrade.
|
|
339
|
+
- Password set before verification: a later password sign-in is proof the
|
|
340
|
+
plugin accepts. Whether unverified registration may set a password is a host
|
|
341
|
+
authentication concern.
|
|
342
|
+
- Shared device: a different user's verified sign-in never relinks an already
|
|
343
|
+
linked browser and returns `'skipped'`; that user's events get no credit
|
|
344
|
+
through that browser.
|
|
345
|
+
- If someone registers another person's email from their own browser and that
|
|
346
|
+
person never signs in, a later user-keyed event the host records against that
|
|
347
|
+
account (for example an offline gift) is credited to the registering browser.
|
|
348
|
+
It needs a deliberate registration of someone else's email plus an event
|
|
349
|
+
matched to that account, and it moves one unit's value, never assignment.
|
|
350
|
+
There is no time window, so recurring gifts from never-signed-in donors keep
|
|
351
|
+
counting.
|
|
352
|
+
|
|
287
353
|
## Results
|
|
288
354
|
|
|
289
355
|
The host owns scheduling and authentication. Call
|
|
@@ -368,7 +434,8 @@ a user-keyed source. Visitor experiments can use a user-keyed source through
|
|
|
368
434
|
linked identities.
|
|
369
435
|
|
|
370
436
|
Optional `covariate({ units: [{ unitId, firstExposureTs }], lookbackDays })`
|
|
371
|
-
returns `[{ unitId, value }]
|
|
437
|
+
returns `[{ unitId, value }]`, one aggregate value per unit for pre-exposure
|
|
438
|
+
CUPED adjustment (the plugin asks for a 14-day lookback). Percentile
|
|
372
439
|
revenue capping reuses `query` over the 90 days before Start; the resolved cap
|
|
373
440
|
is frozen for the round. Capping affects decision statistics, while results
|
|
374
441
|
also show uncapped totals. Count and binomial metrics are never capped.
|
|
@@ -389,16 +456,61 @@ are recorded with `trackConversion`. Beacon conversions for a non-writable name
|
|
|
389
456
|
are dropped silently. Only listed names become `events:<name>` sources. They
|
|
390
457
|
support visitor and user units and do not represent counts or revenue in v1.
|
|
391
458
|
|
|
459
|
+
### Whole-page options
|
|
460
|
+
|
|
461
|
+
| Option | Purpose |
|
|
462
|
+
| --- | --- |
|
|
463
|
+
| `pages.collections` | Optional array of `{ slug, pathOf, findByPath }`. `pathOf(doc)` derives a public path from the slug (used to protect live test paths); `findByPath(path, req)` resolves a published page to `{ id, published: true }`, or returns `null`. Start and readiness check every non-holdout version, including the Original. Registered collections must exist when this plugin runs. Localized publication status and field-level `beforeChange` hooks that rewrite paths after the collection guard are unsupported; derive paths from fields finalized before collection hooks. Without this option, readiness warns that pages are unchecked and Start keeps its existing behaviour. |
|
|
464
|
+
|
|
465
|
+
Do not swallow errors from nested updates of guarded page collections inside host hooks: a swallowed error leaves the guard's operation frame in place for the rest of the request because Payload's Local API does not call `afterError`.
|
|
466
|
+
|
|
467
|
+
For a collection with drafts enabled, use a published-only slug lookup with the
|
|
468
|
+
manager's request and `overrideAccess: false`:
|
|
469
|
+
|
|
470
|
+
```ts
|
|
471
|
+
pages: {
|
|
472
|
+
collections: [{
|
|
473
|
+
slug: 'pages',
|
|
474
|
+
pathOf: doc => typeof doc.slug === 'string' ? `/${doc.slug}` : null,
|
|
475
|
+
findByPath: async (path, req) => {
|
|
476
|
+
const { docs } = await req.payload.find({
|
|
477
|
+
collection: 'pages', req, overrideAccess: false, draft: false, depth: 0, limit: 1,
|
|
478
|
+
where: { and: [
|
|
479
|
+
{ slug: { equals: path.slice(1) } },
|
|
480
|
+
{ _status: { equals: 'published' } },
|
|
481
|
+
] },
|
|
482
|
+
})
|
|
483
|
+
return docs[0] ? { id: docs[0].id, published: true } : null
|
|
484
|
+
},
|
|
485
|
+
}],
|
|
486
|
+
},
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
Adapt both callbacks for the site's homepage path. These checks run before Start;
|
|
490
|
+
they do not protect pages from later deletion, unpublishing or renaming. Keep an
|
|
491
|
+
archived winner published at the same address: archived test URLs still point to it.
|
|
492
|
+
No migration is needed.
|
|
493
|
+
|
|
392
494
|
### `admin` options
|
|
393
495
|
|
|
394
496
|
Host links and kind descriptions are optional. For a host using payload-puck's
|
|
395
|
-
editor route and a `puck-templates` collection
|
|
497
|
+
editor route, public pages and a `puck-templates` collection:
|
|
498
|
+
|
|
499
|
+
| Option | Signature and behavior |
|
|
500
|
+
| --- | --- |
|
|
501
|
+
| `admin.previewUrl` | `(placement: PreviewPlacement) => string \| null \| undefined \| Promise<string \| null \| undefined>`. Receives `collection`, `id`, `title`, `published`, and optional `slug`. May be async; return `null` to hide preview for that placement. Each call has a 2 s limit, with at most 8 lookups awaited concurrently. |
|
|
502
|
+
|
|
503
|
+
For example, pass:
|
|
396
504
|
|
|
397
505
|
```ts
|
|
398
506
|
admin: {
|
|
399
507
|
editorUrl: ({ collection, id, draft }) =>
|
|
400
508
|
`/admin/puck-editor/${encodeURIComponent(collection)}/${encodeURIComponent(String(id))}`,
|
|
401
|
-
previewUrl: ({
|
|
509
|
+
previewUrl: async ({ collection, slug }) => {
|
|
510
|
+
if (collection !== 'pages' || slug === undefined) return null
|
|
511
|
+
// Match the site's public page routing, including its homepage convention.
|
|
512
|
+
return slug === 'home' ? '/' : `/${slug}`
|
|
513
|
+
},
|
|
402
514
|
descriptions: {
|
|
403
515
|
block: 'Try different content on a landing page.',
|
|
404
516
|
document: 'Try different versions of a donation ask.',
|
|
@@ -407,11 +519,21 @@ admin: {
|
|
|
407
519
|
}
|
|
408
520
|
```
|
|
409
521
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
522
|
+
`PreviewPlacement` is exported from the package root and `/rsc`. Its `slug` is
|
|
523
|
+
the published main row's string slug, including for a placement found in a newer
|
|
524
|
+
draft with a different slug. It is absent for unpublished documents. `published`
|
|
525
|
+
describes the placement's version; the callback receives no other document fields.
|
|
526
|
+
An async callback can also look up the public path by `collection` and `id` using
|
|
527
|
+
the host's own data access. The old `({ collection, id }) => string` form still works.
|
|
528
|
+
|
|
529
|
+
Preflight resolves the URL callbacks on the server for block placements and sends
|
|
530
|
+
only URL strings to the client. A null/undefined result, a non-string, an exception,
|
|
531
|
+
a rejected promise or a timeout leaves preview hidden for that placement. When an
|
|
532
|
+
editor URL exists, the admin offers "Edit on page"; when neither URL exists, it
|
|
533
|
+
omits the row. Timed-out host work cannot be cancelled and keeps its concurrency
|
|
534
|
+
slot until it settles; if all 8 slots remain occupied, further preview lookups
|
|
535
|
+
are skipped for that preflight. Kind descriptions are available to the server views through
|
|
536
|
+
`config.custom.xp`.
|
|
415
537
|
|
|
416
538
|
A flag point can also declare `description?: string` to explain the setting below its label.
|
|
417
539
|
|
|
@@ -471,7 +593,8 @@ dashboard also includes **Tests** and **Goals** cards above its widgets. Goals
|
|
|
471
593
|
appears only for users who can manage tests. Both additions follow the host's
|
|
472
594
|
admin route and keep host-provided navigation and dashboard entries first.
|
|
473
595
|
After upgrading, run `payload generate:importmap` to register the dashboard card
|
|
474
|
-
component.
|
|
596
|
+
component and the server nav wrapper.
|
|
597
|
+
A host nav group also labelled "Tests" shares Payload's NavGroup DOM id and open-state preference with the plugin's group, so pick another label.
|
|
475
598
|
|
|
476
599
|
### Client assignments and exposure
|
|
477
600
|
|
|
@@ -524,19 +647,41 @@ configured to allow client writes; record revenue on the server.
|
|
|
524
647
|
|
|
525
648
|
### Puck render and editor configs
|
|
526
649
|
|
|
527
|
-
Register
|
|
528
|
-
|
|
529
|
-
`@delmaredigital/payload-experiments/puck/editor` in your editor config, using
|
|
530
|
-
the same `ExperimentSlot` component name:
|
|
650
|
+
Register the factories from `/puck` and `/puck/editor`, using the same
|
|
651
|
+
`ExperimentSlot` component name in both configs. Pass the host's Payload routes:
|
|
531
652
|
|
|
532
653
|
```ts
|
|
654
|
+
import { createExperimentSlot } from '@delmaredigital/payload-experiments/puck'
|
|
655
|
+
import { createExperimentSlotEditor, experimentOutlinePlugin } from '@delmaredigital/payload-experiments/puck/editor'
|
|
656
|
+
|
|
657
|
+
const routes = { admin: '/manage', api: '/content-api' } // Same as Payload config.routes
|
|
658
|
+
const accent = '#1f3a68' // Same as the plugin's theme.accent
|
|
659
|
+
|
|
533
660
|
// Render config
|
|
534
|
-
components: { ExperimentSlot }
|
|
661
|
+
components: { ExperimentSlot: createExperimentSlot({ routes }) }
|
|
535
662
|
|
|
536
663
|
// Editor config
|
|
537
|
-
components: { ExperimentSlot:
|
|
664
|
+
components: { ExperimentSlot: createExperimentSlotEditor({ routes, accent }) }
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
Add `experimentOutlinePlugin()` to Puck's `plugins` alongside this block config
|
|
668
|
+
(with payload-puck, use its `plugins` option). It composes with the host's outline
|
|
669
|
+
override and hides empty version slots that the block's test doesn't use. Content
|
|
670
|
+
in an unused slot stays visible in the outline and gets a warning in the block.
|
|
671
|
+
|
|
672
|
+
```ts
|
|
673
|
+
plugins: [experimentOutlinePlugin()]
|
|
538
674
|
```
|
|
539
675
|
|
|
676
|
+
Routes default to Payload's `/admin` and `/api` when omitted. The existing
|
|
677
|
+
`ExperimentSlot` and `ExperimentSlotEditor` exports remain available with these
|
|
678
|
+
defaults. Hosts on non-default routes switch to the factories. The standalone
|
|
679
|
+
`createExperimentSlotField({ routes })` also accepts the host's routes.
|
|
680
|
+
|
|
681
|
+
Pass the same accent as the plugin's `theme.accent`: the editor cannot read
|
|
682
|
+
Payload config. Both use the same validator and report the same error for an
|
|
683
|
+
invalid colour. Without an accent the editor uses the neutral palette.
|
|
684
|
+
|
|
540
685
|
Load the editor stylesheet inside the Puck iframe. With Tailwind v4, add the
|
|
541
686
|
package import **after** Tailwind in the CSS that builds your editor iframe
|
|
542
687
|
stylesheet (the stylesheet listed in payload-puck's `editorStylesheets`):
|
|
@@ -552,6 +697,15 @@ The editor CSS includes its own light and dark palette, selected by
|
|
|
552
697
|
`data-theme="dark"` or the `dark` class on the iframe's `<html>`. The client component's CSS import alone
|
|
553
698
|
does not reach an iframe configured with `syncHostStyles: false`.
|
|
554
699
|
|
|
700
|
+
If Puck runs outside Payload's admin, the page needs Payload's admin styles (the plugin's tokens map onto them) and the plugin's admin styles, plus `data-theme="light"` or `data-theme="dark"` on `<html>`. Inside the admin all of this is already in place:
|
|
701
|
+
|
|
702
|
+
```css
|
|
703
|
+
@import "@payloadcms/next/css";
|
|
704
|
+
@import "@payloadcms/ui/styles.css";
|
|
705
|
+
@import "@delmaredigital/payload-experiments/admin/theme.css";
|
|
706
|
+
@import "@delmaredigital/payload-experiments/admin/ui.css";
|
|
707
|
+
```
|
|
708
|
+
|
|
555
709
|
Resolve the Puck data on the server with `resolveExperimentData` before
|
|
556
710
|
rendering it. The render component shows only the selected group's slot. The
|
|
557
711
|
editor lets you select a block experiment, edit each group's content, copy the
|
|
@@ -632,10 +786,32 @@ changes block actions**; save first so they use the saved experiment.
|
|
|
632
786
|
- The Puck picker lists 100 experiments.
|
|
633
787
|
- The Puck experiment picker always calls `/api`; hosts with a custom API route
|
|
634
788
|
see an empty picker.
|
|
635
|
-
- Making a winner permanent skips pages in collections that are localized,
|
|
636
|
-
have hidden fields, or contain fields the manager cannot read or update. Those pages
|
|
637
|
-
are listed with a note to update them by hand.
|
|
638
789
|
- Content-change markers are recorded on publish only (on save for collections
|
|
639
790
|
without drafts).
|
|
640
791
|
- Results cover the whole round; breakdowns by segment or dimension are not
|
|
641
792
|
available in v1.
|
|
793
|
+
|
|
794
|
+
### Limits
|
|
795
|
+
|
|
796
|
+
| Capability | Limit |
|
|
797
|
+
|---|---|
|
|
798
|
+
| Make it the content: localized collections | not supported yet |
|
|
799
|
+
| Make it the content: field defaults | An update-denied field empty in both versions may publish its default because Payload applies defaults when reading. |
|
|
800
|
+
|
|
801
|
+
## Theme
|
|
802
|
+
|
|
803
|
+
The admin inherits Payload's neutral palette by default. Set one host accent:
|
|
804
|
+
|
|
805
|
+
```ts
|
|
806
|
+
theme: { accent: '#1f3a68' }
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
`theme.accent` accepts `#rgb` or `#rrggbb`; empty or undefined uses the default.
|
|
810
|
+
The plugin derives darker hover/active fills (clamped to keep text contrast), a
|
|
811
|
+
contrasting focus ring, and an opaque 8% subtle tint.
|
|
812
|
+
In dark mode it raises HSL lightness when needed to reach 3:1 against the
|
|
813
|
+
reference dark surface, then selects white or near-black foreground text.
|
|
814
|
+
The WCAG 2.x gate requires at least 4.5:1 **foreground text on the accent fill**
|
|
815
|
+
in each theme (the accent is used as a fill, not as page text). A failing
|
|
816
|
+
colour is refused with its ratio. Status chips retain Payload semantic colours.
|
|
817
|
+
`deriveAccent` is also exported from `/puck/editor` for editor integrations.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { DocId } from '../types/contracts.js';
|
|
2
2
|
import type { PreflightResult } from '../types/endpoints.js';
|
|
3
|
-
export declare function PreviewLinks({ experimentId, placements, arms }: {
|
|
3
|
+
export declare function PreviewLinks({ experimentId, placements, arms, flagPoint }: {
|
|
4
|
+
flagPoint?: string;
|
|
4
5
|
experimentId: DocId;
|
|
5
6
|
placements: PreflightResult['placements'];
|
|
6
7
|
arms: readonly {
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useRef, useState } from 'react';
|
|
4
|
-
import { Button } from '
|
|
4
|
+
import { Button } from '../ui/Button.js';
|
|
5
5
|
import { armLabel } from './labels.js';
|
|
6
6
|
import { useApi } from './useApi.js';
|
|
7
7
|
import { REVIEW_COPY as C } from './copy/review.js';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
8
|
+
import { TEST_PAGE } from './copy/testPage.js';
|
|
9
|
+
import { API_MESSAGES, refusalMessage } from './copy/common.js';
|
|
10
|
+
import { actionablePreviewPages } from './previewLinks.model.js';
|
|
11
|
+
export function PreviewLinks({ experimentId, placements, arms, flagPoint }) {
|
|
11
12
|
const api = useApi();
|
|
12
13
|
const inFlight = useRef(false);
|
|
13
14
|
const [busy, setBusy] = useState(false);
|
|
@@ -17,20 +18,37 @@ export function PreviewLinks({ experimentId, placements, arms }) {
|
|
|
17
18
|
inFlight.current = true;
|
|
18
19
|
setBusy(true);
|
|
19
20
|
setError(null);
|
|
21
|
+
let win = null;
|
|
20
22
|
try {
|
|
23
|
+
win = window.open('', '_blank');
|
|
24
|
+
if (!win) {
|
|
25
|
+
setError(C.previewBlocked);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// Clearing opener immediately gives the same opener protection as noopener: the
|
|
29
|
+
// previewed page cannot reach window.opener. The admin URL may be sent as
|
|
30
|
+
// referrer to the host's own site (resolved against location.origin); accepted.
|
|
31
|
+
try {
|
|
32
|
+
win.opener = null;
|
|
33
|
+
} catch {}
|
|
21
34
|
const result = await api.previewToken(experimentId, arm);
|
|
35
|
+
// The person closed the blank tab while the token loaded: nothing to show or report.
|
|
36
|
+
if (win.closed) return;
|
|
22
37
|
if (!result.ok) {
|
|
23
|
-
|
|
38
|
+
win.close();
|
|
39
|
+
setError(result.status === 0 ? API_MESSAGES.network : refusalMessage(result.code, result.status));
|
|
24
40
|
return;
|
|
25
41
|
}
|
|
26
|
-
if (
|
|
42
|
+
if (typeof result.data?.token !== 'string' || !result.data.token) {
|
|
43
|
+
win.close();
|
|
27
44
|
setError(API_MESSAGES.unreadable);
|
|
28
45
|
return;
|
|
29
46
|
}
|
|
30
47
|
const target = new URL(url, window.location.origin);
|
|
31
48
|
target.searchParams.set('xp_preview', result.data.token);
|
|
32
|
-
|
|
49
|
+
win.location.replace(target.href);
|
|
33
50
|
} catch {
|
|
51
|
+
win?.close();
|
|
34
52
|
setError(API_MESSAGES.failed);
|
|
35
53
|
} finally{
|
|
36
54
|
inFlight.current = false;
|
|
@@ -40,7 +58,8 @@ export function PreviewLinks({ experimentId, placements, arms }) {
|
|
|
40
58
|
return /*#__PURE__*/ _jsxs("div", {
|
|
41
59
|
className: "xp-preview-links",
|
|
42
60
|
children: [
|
|
43
|
-
|
|
61
|
+
actionablePreviewPages(placements, flagPoint).map((placement)=>/*#__PURE__*/ _jsxs("div", {
|
|
62
|
+
className: "xp-preview-row",
|
|
44
63
|
children: [
|
|
45
64
|
/*#__PURE__*/ _jsxs("p", {
|
|
46
65
|
children: [
|
|
@@ -48,19 +67,29 @@ export function PreviewLinks({ experimentId, placements, arms }) {
|
|
|
48
67
|
placement.slotCount > 1 ? ` ${C.placedTimes(placement.slotCount)}` : null
|
|
49
68
|
]
|
|
50
69
|
}),
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
70
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
71
|
+
className: "xp-ui-button-group",
|
|
72
|
+
children: [
|
|
73
|
+
placement.url ? arms.filter((arm)=>arm.kind !== 'holdout' && arm.key !== 'holdout').map((arm)=>/*#__PURE__*/ _jsxs(Button, {
|
|
74
|
+
variant: "secondary",
|
|
75
|
+
size: "sm",
|
|
76
|
+
disabled: busy,
|
|
77
|
+
onClick: ()=>{
|
|
78
|
+
void preview(placement.url, arm.key);
|
|
79
|
+
},
|
|
80
|
+
children: [
|
|
81
|
+
C.preview,
|
|
82
|
+
" ",
|
|
83
|
+
arm.label?.trim() || armLabel(arm.key)
|
|
84
|
+
]
|
|
85
|
+
}, arm.key)) : null,
|
|
86
|
+
placement.editorUrl ? /*#__PURE__*/ _jsx(Button, {
|
|
87
|
+
variant: "link",
|
|
88
|
+
size: "sm",
|
|
89
|
+
href: placement.editorUrl,
|
|
90
|
+
children: TEST_PAGE.editOnPage
|
|
91
|
+
}) : null
|
|
92
|
+
]
|
|
64
93
|
})
|
|
65
94
|
]
|
|
66
95
|
}, placement.key)),
|
|
@@ -5,7 +5,7 @@ import { Link, NavGroup, useAuth, useConfig } from '@payloadcms/ui';
|
|
|
5
5
|
import { formatAdminURL } from 'payload/shared';
|
|
6
6
|
import { usePathname } from 'next/navigation.js';
|
|
7
7
|
import { SURFACE_COPY } from './copy/surface.js';
|
|
8
|
-
function TestsNav() {
|
|
8
|
+
function TestsNav({ isOpen = true }) {
|
|
9
9
|
const { config } = useConfig();
|
|
10
10
|
const { permissions } = useAuth();
|
|
11
11
|
const pathname = usePathname();
|
|
@@ -38,9 +38,9 @@ function TestsNav() {
|
|
|
38
38
|
active: goalsActive
|
|
39
39
|
}
|
|
40
40
|
];
|
|
41
|
-
// getPreference is async and can fetch; let NavGroup default to open.
|
|
42
41
|
return /*#__PURE__*/ _jsx(NavGroup, {
|
|
43
42
|
label: SURFACE_COPY.title,
|
|
43
|
+
isOpen: isOpen,
|
|
44
44
|
children: entries.map((entry)=>{
|
|
45
45
|
const props = {
|
|
46
46
|
className: 'nav__link',
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import '../theme.css';
|
|
2
1
|
import type { ReactNode } from 'react';
|
|
3
2
|
import type { DocId } from '../../types/contracts.js';
|
|
4
3
|
import type { ExperimentAction } from './actions.model.js';
|
|
5
|
-
export declare function DialogFrame({ title, onClose, children }: {
|
|
4
|
+
export declare function DialogFrame({ title, onClose, children, initialFocus, dismissible }: {
|
|
5
|
+
dismissible?: boolean;
|
|
6
|
+
initialFocus?: 'cancel';
|
|
6
7
|
title: string;
|
|
7
8
|
onClose: () => void;
|
|
8
9
|
children: ReactNode;
|
|
9
|
-
}): import("react").
|
|
10
|
-
export declare function ConfirmDialog({ id, action, winnerLabel, onClose }: {
|
|
10
|
+
}): import("react").JSX.Element;
|
|
11
|
+
export declare function ConfirmDialog({ id, action, winnerLabel, onClose, canManage, isDecided }: {
|
|
12
|
+
canManage?: boolean;
|
|
13
|
+
isDecided?: boolean;
|
|
11
14
|
id: DocId;
|
|
12
15
|
action: ExperimentAction;
|
|
13
16
|
winnerLabel: string;
|