@delmaredigital/payload-experiments 0.2.0-rc.6 → 0.3.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.
- package/README.md +129 -11
- package/dist/admin/PreviewLinks.d.ts +2 -1
- package/dist/admin/PreviewLinks.js +24 -16
- 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/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 -7
- package/dist/admin/copy/arms.js +1 -7
- 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 +19 -0
- package/dist/admin/copy/readiness.js +19 -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 +41 -2
- package/dist/admin/copy/results.js +42 -3
- package/dist/admin/copy/review.d.ts +1 -1
- package/dist/admin/copy/review.js +1 -1
- 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 -4
- package/dist/admin/copy/surface.js +9 -4
- package/dist/admin/copy/testPage.d.ts +12 -0
- package/dist/admin/copy/testPage.js +12 -0
- package/dist/admin/format/date.d.ts +2 -0
- package/dist/admin/format/date.js +10 -0
- package/dist/admin/labels.d.ts +2 -7
- package/dist/admin/labels.js +2 -7
- package/dist/admin/previewLinks.model.d.ts +2 -2
- package/dist/admin/previewLinks.model.js +5 -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/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 +6 -2
- package/dist/admin/results/results.model.js +12 -5
- package/dist/admin/surface/ResultsRoute.d.ts +1 -1
- package/dist/admin/surface/ResultsRoute.js +60 -31
- 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 +183 -112
- package/dist/admin/surface/TestsSurface.js +56 -16
- package/dist/admin/surface/contracts.d.ts +1 -0
- 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 +11 -3
- package/dist/admin/surface/goals/goals.css +10 -13
- package/dist/admin/surface/home.model.d.ts +7 -8
- package/dist/admin/surface/home.model.js +23 -12
- package/dist/admin/surface/newTest/NewTest.js +5 -5
- package/dist/admin/surface/pageBlock/parts.js +31 -26
- package/dist/admin/surface/readiness/ActionDialogs.js +43 -14
- package/dist/admin/surface/readiness/Banner.d.ts +3 -0
- package/dist/admin/surface/readiness/Banner.js +63 -36
- 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 +18 -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 +15 -2
- package/dist/admin/surface/results/ResultsBody.js +281 -107
- 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.js +2 -6
- 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 +62 -47
- package/dist/admin/surface/test/AdvancedSection.js +55 -48
- package/dist/admin/surface/test/GoalSection.js +39 -32
- 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 -59
- 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/useTestDocument.d.ts +1 -0
- package/dist/admin/surface/useTestDocument.js +40 -9
- 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/views/ExperimentsRootView.js +12 -2
- package/dist/collections/experiments/config.js +44 -1
- package/dist/collections/experiments/lockHook.js +16 -2
- 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/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/contentDone.d.ts +4 -0
- package/dist/contentDone.js +5 -0
- package/dist/db/identities.d.ts +8 -7
- package/dist/db/identities.js +9 -6
- 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/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 +23 -7
- package/dist/db/staleEdit.d.ts +6 -0
- package/dist/db/staleEdit.js +10 -0
- package/dist/db/sweep.js +11 -6
- package/dist/endpoints/events.js +3 -3
- package/dist/endpoints/index.js +19 -1
- package/dist/endpoints/recompute.js +2 -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/adminClient.d.ts +1 -0
- package/dist/exports/adminClient.js +1 -0
- package/dist/exports/index.d.ts +2 -1
- package/dist/exports/index.js +1 -0
- package/dist/exports/puckEditor.d.ts +1 -0
- package/dist/exports/puckEditor.js +1 -0
- package/dist/exports/rsc.d.ts +5 -1
- package/dist/exports/rsc.js +2 -0
- package/dist/hooks/hostHook.js +3 -2
- package/dist/hooks/pageGuard.d.ts +5 -0
- package/dist/hooks/pageGuard.js +121 -0
- package/dist/labels/arms.d.ts +7 -0
- package/dist/labels/arms.js +7 -0
- package/dist/log.d.ts +7 -0
- package/dist/log.js +38 -0
- package/dist/manifest/compile.d.ts +5 -0
- package/dist/manifest/compile.js +5 -1
- package/dist/options.js +23 -0
- package/dist/plugin.js +26 -2
- package/dist/puck/copy.d.ts +1 -1
- package/dist/puck/copy.js +6 -6
- package/dist/puck/editor/editor.chrome.css +235 -0
- package/dist/puck/editor/editor.css +241 -43
- 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/results/verdict.d.ts +2 -0
- package/dist/results/verdict.js +18 -1
- package/dist/sanitizeReadinessDetail.d.ts +7 -0
- package/dist/sanitizeReadinessDetail.js +54 -0
- package/dist/server/linkIdentity.d.ts +10 -1
- package/dist/server/linkIdentity.js +20 -18
- package/dist/server/metadata.d.ts +5 -0
- package/dist/server/metadata.js +9 -1
- package/dist/server/recordContentChange.js +2 -1
- package/dist/server/recordExposures.js +3 -3
- package/dist/server/runResults.d.ts +3 -2
- package/dist/server/runResults.js +125 -34
- package/dist/server/trackConversion.js +5 -6
- 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 +6 -3
- package/dist/transitions/checkStart.js +15 -0
- package/dist/transitions/contentFields.d.ts +7 -0
- package/dist/transitions/contentFields.js +20 -0
- package/dist/transitions/decide.js +6 -1
- package/dist/transitions/deleteExperiment.js +2 -1
- 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 +1 -0
- package/dist/transitions/placements.js +4 -0
- package/dist/transitions/preflightData.d.ts +1 -1
- package/dist/transitions/preflightData.js +42 -4
- 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 +30 -5
- package/dist/types/contracts.d.ts +14 -0
- 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 +14 -0
- package/dist/ui/Tabs.js +54 -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 +21 -0
- package/dist/ui/accent.js +81 -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 +1 -1
- 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 -69
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { relationshipId } from './checkStart.js';
|
|
2
|
+
import { withTransition } from './kernel.js';
|
|
3
|
+
import { readExperiment, refuse, updateExperiment } from './start.js';
|
|
4
|
+
export async function releasePages(req, { experimentId }) {
|
|
5
|
+
const before = await readExperiment(req, experimentId);
|
|
6
|
+
const { result: doc, revision } = await withTransition(req, {
|
|
7
|
+
experimentId,
|
|
8
|
+
layerId: relationshipId(before.layer)
|
|
9
|
+
}, async ({ req })=>{
|
|
10
|
+
const current = await readExperiment(req, experimentId);
|
|
11
|
+
const decision = current.decision;
|
|
12
|
+
if (current.kind !== 'page' || current.status !== 'archived' || !decision?.winnerArm || decision.discardedAt || decision.pagesReleasedAt) refuse('invalid-status');
|
|
13
|
+
const at = new Date().toISOString();
|
|
14
|
+
return updateExperiment(req, experimentId, {
|
|
15
|
+
decision: {
|
|
16
|
+
...decision,
|
|
17
|
+
pagesReleasedAt: at,
|
|
18
|
+
pagesReleasedBy: req.user ? String(req.user.id) : null
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
doc,
|
|
24
|
+
revision: revision
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PayloadRequest } from 'payload';
|
|
2
|
+
import type { DocId } from '../types/contracts.js';
|
|
3
|
+
import type { RestoreContentEntryBody } from '../types/endpoints.js';
|
|
4
|
+
export declare function restoreContentEntry(req: PayloadRequest, { experimentId, collection, id }: RestoreContentEntryBody & {
|
|
5
|
+
experimentId: DocId;
|
|
6
|
+
}): Promise<{
|
|
7
|
+
doc: import("payload").JsonObject & import("payload").TypeWithID;
|
|
8
|
+
}>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { relationshipId } from './checkStart.js';
|
|
2
|
+
import { withTransition } from './kernel.js';
|
|
3
|
+
import { readExperiment, refuse, updateExperiment } from './start.js';
|
|
4
|
+
export async function restoreContentEntry(req, { experimentId, collection, id }) {
|
|
5
|
+
const before = await readExperiment(req, experimentId);
|
|
6
|
+
// Only the audit report changes; assignment and the manifest stay the same.
|
|
7
|
+
const { result: doc } = await withTransition(req, {
|
|
8
|
+
experimentId,
|
|
9
|
+
layerId: relationshipId(before.layer),
|
|
10
|
+
flagPointId: before.kind === 'flag' ? before.flagPoint : undefined,
|
|
11
|
+
bump: false
|
|
12
|
+
}, async ({ req })=>{
|
|
13
|
+
const current = await readExperiment(req, experimentId);
|
|
14
|
+
const decision = current.decision;
|
|
15
|
+
const report = decision?.contentReport;
|
|
16
|
+
const index = Array.isArray(report?.docs) ? report.docs.findIndex((entry)=>entry.collection === collection && String(entry.id) === String(id)) : -1;
|
|
17
|
+
const entry = report?.docs?.[index];
|
|
18
|
+
if (current.status !== 'decided' || !decision?.winnerArm || decision.discardedAt) refuse('not-decided');
|
|
19
|
+
if (!entry) refuse('entry-not-found');
|
|
20
|
+
if (!entry.dismissedAt) refuse('entry-not-dismissed');
|
|
21
|
+
const docs = report.docs.map((entry, i)=>{
|
|
22
|
+
if (i !== index) return entry;
|
|
23
|
+
const { dismissedAt: _at, dismissedBy: _by, ...restored } = entry;
|
|
24
|
+
return {
|
|
25
|
+
...restored,
|
|
26
|
+
ok: false
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
return updateExperiment(req, experimentId, {
|
|
30
|
+
decision: {
|
|
31
|
+
...decision,
|
|
32
|
+
contentReport: {
|
|
33
|
+
...report,
|
|
34
|
+
docs
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
req.payload.logger.info({
|
|
40
|
+
experimentId,
|
|
41
|
+
collection,
|
|
42
|
+
id
|
|
43
|
+
}, 'Experiment content entry restored');
|
|
44
|
+
return {
|
|
45
|
+
doc
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { lockMetricsForFreeze } from '../db/metricUsage.js';
|
|
2
|
+
import { DEFAULT_SEQUENTIAL_TUNING_PARAMETER } from '../constants.js';
|
|
1
3
|
import { claimSlices } from '../db/slices.js';
|
|
4
|
+
import { unpublishedPageCopy } from '../admin/copy/readiness.js';
|
|
2
5
|
import { XpRefusal } from '../errors.js';
|
|
3
6
|
import { isOpValid } from '../targeting/ops.js';
|
|
4
7
|
import { newSeed } from '../runtime/seed.js';
|
|
@@ -27,21 +30,31 @@ export async function readExperiment(req, id) {
|
|
|
27
30
|
}
|
|
28
31
|
export function refuse(code, detail) {
|
|
29
32
|
const messages = {
|
|
33
|
+
'page-target-unpublished': unpublishedPageCopy(typeof detail === 'string' ? detail : 'This page'),
|
|
30
34
|
'invalid-status': 'This action is not available in the current status.',
|
|
31
35
|
'stale-draft': 'The experiment changed. Review it again before starting.',
|
|
32
36
|
'flag-point-busy': 'Another experiment is running or decided on this flag point.',
|
|
33
37
|
'layer-full': 'There is not enough room in this layer.',
|
|
34
38
|
'allocation-lower': 'Allocation can only increase while running. Stop and start a new round to lower it.',
|
|
35
|
-
'invalid-body': 'Check the experiment settings and try again.'
|
|
39
|
+
'invalid-body': 'Check the experiment settings and try again.',
|
|
40
|
+
'not-decided': 'This test has no standing winner.',
|
|
41
|
+
'entry-not-found': 'That page is not in the report.',
|
|
42
|
+
'entry-ok': 'That page was already updated.',
|
|
43
|
+
'entry-dismissed': 'That page was already dismissed.',
|
|
44
|
+
'entry-not-dismissed': 'That page is not dismissed.'
|
|
36
45
|
};
|
|
37
46
|
const conflict = [
|
|
38
47
|
'invalid-status',
|
|
39
48
|
'stale-draft',
|
|
40
49
|
'flag-point-busy',
|
|
41
50
|
'layer-full',
|
|
42
|
-
'allocation-lower'
|
|
51
|
+
'allocation-lower',
|
|
52
|
+
'not-decided',
|
|
53
|
+
'entry-ok',
|
|
54
|
+
'entry-dismissed',
|
|
55
|
+
'entry-not-dismissed'
|
|
43
56
|
].includes(code);
|
|
44
|
-
throw new XpRefusal(code, messages[code] ?? 'The experiment is not ready to start.', detail, conflict ? 409 : 422);
|
|
57
|
+
throw new XpRefusal(code, messages[code] ?? 'The experiment is not ready to start.', detail, code === 'entry-not-found' ? 404 : conflict ? 409 : 422);
|
|
45
58
|
}
|
|
46
59
|
export async function updateExperiment(req, id, data) {
|
|
47
60
|
// §7.8: access-authorised transition writes locked plugin fields.
|
|
@@ -78,8 +91,10 @@ export async function start(req, { experimentId, options }) {
|
|
|
78
91
|
kind: full.kind,
|
|
79
92
|
analysis: full.analysis
|
|
80
93
|
};
|
|
94
|
+
// Warning-only codes always have ok: true. These exclusions narrow
|
|
95
|
+
// ReadinessCode to RefusalCode, which intentionally excludes warnings.
|
|
81
96
|
const failed = checkStart(input).find((check)=>!check.ok);
|
|
82
|
-
if (failed) refuse(failed.code, failed.detail);
|
|
97
|
+
if (failed && failed.code !== 'page-targets-unchecked' && failed.code !== 'make-content-unsupported') refuse(failed.code, failed.detail);
|
|
83
98
|
const before = input.experiment;
|
|
84
99
|
const updatedAt = before.updatedAt;
|
|
85
100
|
const at = new Date();
|
|
@@ -134,8 +149,17 @@ export async function start(req, { experimentId, options }) {
|
|
|
134
149
|
...input,
|
|
135
150
|
experiment: current
|
|
136
151
|
}).find((check)=>!check.ok);
|
|
137
|
-
if (recheck) refuse(recheck.code, recheck.detail);
|
|
152
|
+
if (recheck && recheck.code !== 'page-targets-unchecked' && recheck.code !== 'make-content-unsupported') refuse(recheck.code, recheck.detail);
|
|
138
153
|
}
|
|
154
|
+
const lockedMetrics = await lockMetricsForFreeze(exec, caps.definitions.map((metric)=>metric.id));
|
|
155
|
+
if (!lockedMetrics) {
|
|
156
|
+
throw new XpRefusal('stale-draft', 'A goal in this test was deleted. Review the goals before starting.', undefined, 409);
|
|
157
|
+
}
|
|
158
|
+
// Reachable: a slug/source change takes FOR NO KEY UPDATE, which a draft's FK check does not wait on.
|
|
159
|
+
if (caps.definitions.some((metric)=>{
|
|
160
|
+
const locked = lockedMetrics.find((row)=>String(row.id) === String(metric.id));
|
|
161
|
+
return locked.slug !== metric.slug || locked.source !== metric.source;
|
|
162
|
+
})) throw new XpRefusal('stale-draft', 'A goal changed. Review it again before starting.', undefined, 409);
|
|
139
163
|
const targeting = (current.targeting ?? []).map((rule)=>{
|
|
140
164
|
const type = Object.hasOwn(options.attributeKeys ?? {}, rule.attribute) ? options.attributeKeys[rule.attribute].type : undefined;
|
|
141
165
|
const validValue = type === 'string' && [
|
|
@@ -183,6 +207,7 @@ export async function start(req, { experimentId, options }) {
|
|
|
183
207
|
...current.analysis,
|
|
184
208
|
method: current.analysis?.method ?? 'sequential',
|
|
185
209
|
alpha: 0.05,
|
|
210
|
+
sequentialTuningParameter: current.analysis?.sequentialTuningParameter ?? DEFAULT_SEQUENTIAL_TUNING_PARAMETER,
|
|
186
211
|
cupedLookbackDays: 14,
|
|
187
212
|
layerSlices,
|
|
188
213
|
minSampleSizes: Object.fromEntries(definitions.map((metric)=>[
|
|
@@ -105,6 +105,9 @@ export interface ExperimentsPluginOptions<TUser extends {
|
|
|
105
105
|
readResults?: ExperimentsAccess;
|
|
106
106
|
readIdentities?: ExperimentsAccess;
|
|
107
107
|
};
|
|
108
|
+
theme?: {
|
|
109
|
+
accent?: string;
|
|
110
|
+
};
|
|
108
111
|
admin?: {
|
|
109
112
|
editorUrl?: (args: {
|
|
110
113
|
collection: string;
|
|
@@ -131,6 +134,17 @@ export interface ExperimentsPluginOptions<TUser extends {
|
|
|
131
134
|
type: 'string' | 'number' | 'boolean';
|
|
132
135
|
values?: string[];
|
|
133
136
|
}>;
|
|
137
|
+
pages?: {
|
|
138
|
+
collections: Array<{
|
|
139
|
+
slug: string;
|
|
140
|
+
pathOf: (doc: Record<string, unknown>) => string | null;
|
|
141
|
+
/** Published-only lookup using the supplied req and overrideAccess: false. Results checks supply an anonymous req. */
|
|
142
|
+
findByPath: (path: string, req: PayloadRequest) => Promise<{
|
|
143
|
+
id: string | number;
|
|
144
|
+
published: boolean;
|
|
145
|
+
} | null>;
|
|
146
|
+
}>;
|
|
147
|
+
};
|
|
134
148
|
puck?: {
|
|
135
149
|
enabled: boolean;
|
|
136
150
|
collections: Array<string | {
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import type { ArmKind, DocId, ExposurePolicy, MetricSource, ResolvedOptions } from './contracts.js';
|
|
1
|
+
import type { ArmKind, DocId, ExperimentKind, ExposurePolicy, MetricSource, ResolvedOptions, Status } from './contracts.js';
|
|
2
|
+
import type { ContentReportReason } from './reasons.js';
|
|
2
3
|
import type { ArmSlot } from './puck.js';
|
|
3
4
|
import type { ReadinessCode } from '../transitions/checkStart.js';
|
|
4
5
|
export type { ReadinessCode } from '../transitions/checkStart.js';
|
|
5
6
|
import type { Snapshot, VerdictCode } from './snapshot.js';
|
|
6
7
|
export type ArmIssueCode = 'weights-total' | 'control-count' | 'control-first' | 'holdout-last' | 'holdout-count' | 'variant-count' | 'duplicate-key' | 'duplicate-slot' | 'slot-required' | 'invalid-key';
|
|
7
|
-
|
|
8
|
+
/** Content-report dismiss/restore refusals (CORE-44). */
|
|
9
|
+
export type ContentEntryRefusal = 'not-decided' | 'entry-not-found' | 'entry-ok' | 'entry-dismissed' | 'entry-not-dismissed';
|
|
10
|
+
export type RefusalCode = ArmIssueCode | 'not-found' | ContentEntryRefusal | 'no-planned-duration' | 'no-primary-metric' | 'no-placements' | 'empty-slot' | 'page-no-targets' | 'page-target-unpublished' | 'page-user-unit' | 'version-page-unavailable' | 'version-page-unchecked' | 'flag-no-point' | 'flag-params-invalid' | 'flag-point-busy' | 'flag-placement-missing' | 'layer-full' | 'layer-unit-mismatch' | 'unknown-metric-source' | 'unit-source-mismatch' | 'no-reach-per-day' | 'invalid-status' | 'stale-draft' | 'stale-edit' | 'commit-unverified' | 'allocation-lower' | 'confirmation-required' | 'invalid-winner' | 'slices-taken' | 'locked-placement' | 'not-implemented' | 'forbidden' | 'unauthenticated' | 'body-too-large' | 'invalid-body' | 'too-many-ids' | 'invalid-phase' | 'invalid-ids' | 'recompute-failed' | 'analysis-failed' | 'undeclared-targeting-key' | 'invalid-targeting-rule' | 'block-path-rule' | 'duplicate-metric';
|
|
8
11
|
export type ErrorEnvelope = {
|
|
9
12
|
errors: Array<{
|
|
10
13
|
code: RefusalCode;
|
|
@@ -32,12 +35,25 @@ export type ArchiveBody = {
|
|
|
32
35
|
mode: 'stopped' | 'after-content' | 'discard' | 'hard-coded';
|
|
33
36
|
confirmation?: string;
|
|
34
37
|
};
|
|
38
|
+
export type DismissContentEntryBody = {
|
|
39
|
+
collection: string;
|
|
40
|
+
id: DocId;
|
|
41
|
+
};
|
|
42
|
+
export type DismissContentEntryResult = Pick<TransitionSuccess, 'doc'>;
|
|
43
|
+
export type RestoreContentEntryBody = DismissContentEntryBody;
|
|
44
|
+
export type RestoreContentEntryResult = Pick<TransitionSuccess, 'doc'>;
|
|
45
|
+
export type ContentReport = {
|
|
46
|
+
docs: MakeContentResult['docs'];
|
|
47
|
+
} | MakeContentResult['docs'];
|
|
35
48
|
export type MakeContentResult = TransitionSuccess & {
|
|
36
49
|
docs: Array<{
|
|
37
50
|
collection: string;
|
|
38
51
|
id: DocId;
|
|
39
52
|
ok: boolean;
|
|
40
|
-
|
|
53
|
+
detail?: ContentReportReason | string;
|
|
54
|
+
error?: ContentReportReason | string;
|
|
55
|
+
dismissedAt?: string;
|
|
56
|
+
dismissedBy?: string | null;
|
|
41
57
|
}>;
|
|
42
58
|
};
|
|
43
59
|
export type PreflightResult = {
|
|
@@ -45,6 +61,7 @@ export type PreflightResult = {
|
|
|
45
61
|
code: ReadinessCode;
|
|
46
62
|
ok: boolean;
|
|
47
63
|
detail?: unknown;
|
|
64
|
+
warning?: boolean;
|
|
48
65
|
}>;
|
|
49
66
|
placements: Array<{
|
|
50
67
|
collection: string;
|
|
@@ -93,6 +110,12 @@ export interface PhaseSummary {
|
|
|
93
110
|
}>;
|
|
94
111
|
}
|
|
95
112
|
export type ResultsResponse = {
|
|
113
|
+
/** Filled by the results endpoint from CORE-48: what a results reader may see about the test. */
|
|
114
|
+
experiment?: {
|
|
115
|
+
name: string;
|
|
116
|
+
kind: ExperimentKind;
|
|
117
|
+
status: Status;
|
|
118
|
+
};
|
|
96
119
|
phases: PhaseSummary[];
|
|
97
120
|
snapshot: Snapshot | null;
|
|
98
121
|
decisionSnapshot?: Snapshot;
|
|
@@ -110,6 +133,14 @@ export type ResultsResponse = {
|
|
|
110
133
|
};
|
|
111
134
|
export type SummaryResponse = Record<DocId, {
|
|
112
135
|
ready?: boolean;
|
|
136
|
+
next?: {
|
|
137
|
+
code: ReadinessCode;
|
|
138
|
+
detail?: unknown;
|
|
139
|
+
};
|
|
140
|
+
health?: Array<{
|
|
141
|
+
code: 'version-page-unavailable' | 'version-page-unchecked';
|
|
142
|
+
path: string;
|
|
143
|
+
}>;
|
|
113
144
|
exposures: number;
|
|
114
145
|
days: number;
|
|
115
146
|
verdict: {
|
package/dist/types/index.d.ts
CHANGED
package/dist/types/index.js
CHANGED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Closed codes written by new results runs; stored snapshots may predate them. */
|
|
2
|
+
export type MetricUnavailableReason = 'source-failed' | 'source-unavailable' | 'unit-type-unavailable' | 'non-finite-values' | 'non-finite-covariates' | 'budget' | 'not-supplied' | 'analysis-failed';
|
|
3
|
+
export type ContentReportReason = 'localized' | 'locked' | 'forbidden' | 'not-found' | 'validation' | 'update-failed';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/** Closed codes written by new results runs; stored snapshots may predate them. */ export { };
|
package/dist/types/snapshot.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { GaussianPrior } from '@delmaredigital/experiments-stats';
|
|
2
2
|
import type { DocId, MetricSource } from './contracts.js';
|
|
3
|
+
import type { MetricUnavailableReason } from './reasons.js';
|
|
3
4
|
export type VerdictCode = 'unavailable' | 'data-problem' | 'guardrail-failing' | 'collecting' | 'original-better' | 'worse-none-better' | 'winner' | 'mixed' | 'keep-running' | 'no-difference' | 'rollout-collecting' | 'rollout-readout';
|
|
4
5
|
export interface Snapshot {
|
|
5
6
|
experiment: DocId;
|
|
@@ -54,7 +55,7 @@ export interface Snapshot {
|
|
|
54
55
|
kind: MetricSource['kind'];
|
|
55
56
|
role: 'primary' | 'secondary' | 'guardrail';
|
|
56
57
|
cupedApplied: boolean;
|
|
57
|
-
unavailable?: string;
|
|
58
|
+
unavailable?: MetricUnavailableReason | string;
|
|
58
59
|
perArm: Array<{
|
|
59
60
|
arm: string;
|
|
60
61
|
n: number;
|
|
@@ -107,6 +108,10 @@ export interface Snapshot {
|
|
|
107
108
|
guardrailLine?: string;
|
|
108
109
|
worstArm?: string;
|
|
109
110
|
};
|
|
111
|
+
health?: Array<{
|
|
112
|
+
code: 'version-page-unavailable' | 'version-page-unchecked';
|
|
113
|
+
path: string;
|
|
114
|
+
}>;
|
|
110
115
|
contentChanges: Array<{
|
|
111
116
|
at: string;
|
|
112
117
|
doc: {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import './ui.css';
|
|
3
|
+
export function Breadcrumb({ items }) {
|
|
4
|
+
return /*#__PURE__*/ _jsx("nav", {
|
|
5
|
+
className: "xp-ui-breadcrumb",
|
|
6
|
+
"aria-label": "Breadcrumb",
|
|
7
|
+
children: /*#__PURE__*/ _jsx("ol", {
|
|
8
|
+
children: items.map((item, index)=>/*#__PURE__*/ _jsxs("li", {
|
|
9
|
+
children: [
|
|
10
|
+
index ? /*#__PURE__*/ _jsx("span", {
|
|
11
|
+
"aria-hidden": "true",
|
|
12
|
+
children: "›"
|
|
13
|
+
}) : null,
|
|
14
|
+
index === items.length - 1 ? /*#__PURE__*/ _jsx("span", {
|
|
15
|
+
"aria-current": "page",
|
|
16
|
+
children: item.label
|
|
17
|
+
}) : /*#__PURE__*/ _jsx("a", {
|
|
18
|
+
href: item.href,
|
|
19
|
+
children: item.label
|
|
20
|
+
})
|
|
21
|
+
]
|
|
22
|
+
}, index))
|
|
23
|
+
})
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AnchorHTMLAttributes, ButtonHTMLAttributes } from 'react';
|
|
2
|
+
import './ui.css';
|
|
3
|
+
type Shared = {
|
|
4
|
+
variant?: 'primary' | 'secondary' | 'quiet' | 'link' | 'danger';
|
|
5
|
+
size?: 'md' | 'sm';
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
busy?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type ButtonProps = Shared & (ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
10
|
+
href?: never;
|
|
11
|
+
} | AnchorHTMLAttributes<HTMLAnchorElement> & {
|
|
12
|
+
href: string;
|
|
13
|
+
});
|
|
14
|
+
export declare function Button({ variant, size, disabled, busy, className, children, ...props }: ButtonProps): import("react").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import './ui.css';
|
|
4
|
+
export function Button({ variant = 'secondary', size = 'md', disabled, busy, className = '', children, ...props }) {
|
|
5
|
+
const classes = `xp-ui-button xp-ui-button--${variant} xp-ui-button--${size} ${className}`;
|
|
6
|
+
const blocked = disabled || busy;
|
|
7
|
+
if (props.href !== undefined) {
|
|
8
|
+
const { href, onClick, ...rest } = props;
|
|
9
|
+
return /*#__PURE__*/ _jsx("a", {
|
|
10
|
+
...rest,
|
|
11
|
+
className: classes,
|
|
12
|
+
href: blocked ? undefined : href,
|
|
13
|
+
role: blocked ? 'link' : undefined,
|
|
14
|
+
"aria-disabled": blocked || undefined,
|
|
15
|
+
"aria-busy": busy || undefined,
|
|
16
|
+
tabIndex: blocked ? -1 : rest.tabIndex,
|
|
17
|
+
onClick: (event)=>{
|
|
18
|
+
if (blocked) event.preventDefault();
|
|
19
|
+
else onClick?.(event);
|
|
20
|
+
},
|
|
21
|
+
children: children
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return /*#__PURE__*/ _jsx("button", {
|
|
25
|
+
...props,
|
|
26
|
+
type: props.type ?? 'button',
|
|
27
|
+
className: classes,
|
|
28
|
+
disabled: blocked,
|
|
29
|
+
"aria-busy": busy || undefined,
|
|
30
|
+
children: children
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import './ui.css';
|
|
3
|
+
export declare function Card({ className, ...props }: HTMLAttributes<HTMLDivElement>): import("react").JSX.Element;
|
|
4
|
+
export declare function SectionCard({ number, title, complete, children, id, headingId, className }: {
|
|
5
|
+
number: number;
|
|
6
|
+
title: string;
|
|
7
|
+
complete?: boolean;
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
id?: string;
|
|
10
|
+
headingId?: string;
|
|
11
|
+
className?: string;
|
|
12
|
+
}): import("react").JSX.Element;
|
package/dist/ui/Card.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import './ui.css';
|
|
3
|
+
export function Card({ className = '', ...props }) {
|
|
4
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
5
|
+
...props,
|
|
6
|
+
className: `xp-ui-card ${className}`
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export function SectionCard({ number, title, complete = false, children, id, headingId, className = '' }) {
|
|
10
|
+
return /*#__PURE__*/ _jsxs("section", {
|
|
11
|
+
id: id,
|
|
12
|
+
"aria-labelledby": headingId,
|
|
13
|
+
className: `xp-ui-card xp-ui-section ${className}`,
|
|
14
|
+
children: [
|
|
15
|
+
/*#__PURE__*/ _jsxs("header", {
|
|
16
|
+
className: "xp-ui-section-head",
|
|
17
|
+
children: [
|
|
18
|
+
/*#__PURE__*/ _jsx("span", {
|
|
19
|
+
className: "xp-ui-section-number",
|
|
20
|
+
children: number
|
|
21
|
+
}),
|
|
22
|
+
/*#__PURE__*/ _jsx("h2", {
|
|
23
|
+
id: headingId,
|
|
24
|
+
tabIndex: -1,
|
|
25
|
+
children: title
|
|
26
|
+
}),
|
|
27
|
+
/*#__PURE__*/ _jsx("span", {
|
|
28
|
+
className: "xp-ui-section-complete",
|
|
29
|
+
"aria-label": complete ? 'Complete' : 'Incomplete',
|
|
30
|
+
children: complete ? '✓' : '○'
|
|
31
|
+
})
|
|
32
|
+
]
|
|
33
|
+
}),
|
|
34
|
+
/*#__PURE__*/ _jsx("div", {
|
|
35
|
+
className: "xp-ui-section-body",
|
|
36
|
+
children: children
|
|
37
|
+
})
|
|
38
|
+
]
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import '
|
|
2
|
-
import './chipsInput.css';
|
|
1
|
+
import './ui.css';
|
|
3
2
|
export interface ChipsInputProps {
|
|
4
3
|
value: string[];
|
|
5
4
|
onChange: (value: string[]) => void;
|
|
6
5
|
label: string;
|
|
7
6
|
description?: string;
|
|
7
|
+
help?: string;
|
|
8
|
+
error?: string;
|
|
8
9
|
placeholder?: string;
|
|
9
10
|
disabled?: boolean;
|
|
10
11
|
id: string;
|
|
11
12
|
}
|
|
12
|
-
export declare function ChipsInput({ value, onChange, label, description, placeholder, disabled, id }: ChipsInputProps): import("react").JSX.Element;
|
|
13
|
+
export declare function ChipsInput({ value, onChange, label, description, help, error, placeholder, disabled, id }: ChipsInputProps): import("react").JSX.Element;
|
|
@@ -1,10 +1,8 @@
|
|
|
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
|
|
5
|
-
|
|
6
|
-
import './chipsInput.css';
|
|
7
|
-
export function ChipsInput({ value, onChange, label, description, placeholder, disabled = false, id }) {
|
|
4
|
+
import './ui.css';
|
|
5
|
+
export function ChipsInput({ value, onChange, label, description, help = description, error, placeholder, disabled = false, id }) {
|
|
8
6
|
const [draft, setDraft] = useState('');
|
|
9
7
|
const input = useRef(null);
|
|
10
8
|
const commit = (text)=>{
|
|
@@ -24,7 +22,7 @@ export function ChipsInput({ value, onChange, label, description, placeholder, d
|
|
|
24
22
|
input.current?.focus();
|
|
25
23
|
};
|
|
26
24
|
return /*#__PURE__*/ _jsxs("div", {
|
|
27
|
-
className: "xp-chips",
|
|
25
|
+
className: "xp-ui-chips",
|
|
28
26
|
role: "group",
|
|
29
27
|
"aria-labelledby": `${id}-label`,
|
|
30
28
|
"aria-disabled": disabled,
|
|
@@ -35,10 +33,10 @@ export function ChipsInput({ value, onChange, label, description, placeholder, d
|
|
|
35
33
|
children: label
|
|
36
34
|
}),
|
|
37
35
|
/*#__PURE__*/ _jsxs("div", {
|
|
38
|
-
className:
|
|
36
|
+
className: `xp-ui-chips-control${error ? ' xp-ui-chips-control--invalid' : ''}`,
|
|
39
37
|
children: [
|
|
40
38
|
value.map((item, index)=>/*#__PURE__*/ _jsxs("span", {
|
|
41
|
-
className: "xp-chips-item",
|
|
39
|
+
className: "xp-ui-chips-item",
|
|
42
40
|
children: [
|
|
43
41
|
/*#__PURE__*/ _jsx("span", {
|
|
44
42
|
children: item
|
|
@@ -46,9 +44,9 @@ export function ChipsInput({ value, onChange, label, description, placeholder, d
|
|
|
46
44
|
/*#__PURE__*/ _jsx("button", {
|
|
47
45
|
type: "button",
|
|
48
46
|
disabled: disabled,
|
|
49
|
-
"aria-label":
|
|
47
|
+
"aria-label": `Remove ${item}`,
|
|
50
48
|
onClick: ()=>remove(index),
|
|
51
|
-
children:
|
|
49
|
+
children: '×'
|
|
52
50
|
})
|
|
53
51
|
]
|
|
54
52
|
}, item)),
|
|
@@ -59,7 +57,11 @@ export function ChipsInput({ value, onChange, label, description, placeholder, d
|
|
|
59
57
|
value: draft,
|
|
60
58
|
disabled: disabled,
|
|
61
59
|
placeholder: placeholder,
|
|
62
|
-
"aria-
|
|
60
|
+
"aria-invalid": Boolean(error) || undefined,
|
|
61
|
+
"aria-describedby": [
|
|
62
|
+
help ? `${id}-description` : '',
|
|
63
|
+
error ? `${id}-error` : ''
|
|
64
|
+
].filter(Boolean).join(' ') || undefined,
|
|
63
65
|
onChange: (event)=>setDraft(event.target.value),
|
|
64
66
|
onBlur: ()=>{
|
|
65
67
|
if (draft.trim()) commit(draft);
|
|
@@ -87,9 +89,15 @@ export function ChipsInput({ value, onChange, label, description, placeholder, d
|
|
|
87
89
|
})
|
|
88
90
|
]
|
|
89
91
|
}),
|
|
90
|
-
|
|
92
|
+
help ? /*#__PURE__*/ _jsx("p", {
|
|
91
93
|
id: `${id}-description`,
|
|
92
|
-
children:
|
|
94
|
+
children: help
|
|
95
|
+
}) : null,
|
|
96
|
+
error ? /*#__PURE__*/ _jsx("p", {
|
|
97
|
+
className: "xp-ui-error",
|
|
98
|
+
id: `${id}-error`,
|
|
99
|
+
role: "alert",
|
|
100
|
+
children: error
|
|
93
101
|
}) : null
|
|
94
102
|
]
|
|
95
103
|
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import './ui.css';
|
|
3
|
+
/** Native modal stays in its rendering document, including a Puck canvas iframe.
|
|
4
|
+
* `initialFocus="cancel"` targets the focusable control marked `data-dialog-cancel`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function Dialog({ open, title, onClose, children, dismissible, initialFocus }: {
|
|
7
|
+
open: boolean;
|
|
8
|
+
dismissible?: boolean;
|
|
9
|
+
initialFocus?: 'cancel';
|
|
10
|
+
title: string;
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
}): import("react").JSX.Element;
|