executable-stories-react 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +303 -240
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +301 -238
- package/dist/index.js.map +1 -1
- package/dist/interactive.cjs +843 -471
- package/dist/interactive.cjs.map +1 -1
- package/dist/interactive.d.cts +10 -1
- package/dist/interactive.d.ts +10 -1
- package/dist/interactive.js +806 -443
- package/dist/interactive.js.map +1 -1
- package/dist/report-island.global.js +103 -59
- package/dist/ssr.cjs +269 -206
- package/dist/ssr.cjs.map +1 -1
- package/dist/ssr.js +269 -206
- package/dist/ssr.js.map +1 -1
- package/dist/styles.css +18 -42
- package/dist/tailwind.css +1 -1
- package/package.json +1 -1
package/dist/interactive.js
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
import {
|
|
5
5
|
useCallback as useCallback4,
|
|
6
6
|
useDeferredValue,
|
|
7
|
+
useId as useId2,
|
|
7
8
|
useMemo as useMemo3,
|
|
8
9
|
useRef as useRef3,
|
|
9
|
-
useState as
|
|
10
|
+
useState as useState7
|
|
10
11
|
} from "react";
|
|
11
12
|
|
|
12
13
|
// src/result.ts
|
|
@@ -106,11 +107,15 @@ var badgeVariants = cva(
|
|
|
106
107
|
outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
107
108
|
ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
108
109
|
link: "text-primary underline-offset-4 [a&]:hover:underline",
|
|
109
|
-
|
|
110
|
+
// Pass/skip/pending are the quiet states: outline + coloured text, no
|
|
111
|
+
// fill, so a green report reads calm. Only `failed` keeps a tinted
|
|
112
|
+
// fill, making it the single loud badge on the page, so red pops the
|
|
113
|
+
// instant something breaks. Colour-as-accent, matching the KPI cards.
|
|
114
|
+
passed: "border-pass-border text-pass font-mono uppercase",
|
|
110
115
|
failed: "border-fail-border bg-fail-bg text-fail font-mono uppercase",
|
|
111
|
-
skipped: "border-skip-border
|
|
112
|
-
pending: "border-pend-border
|
|
113
|
-
tag: "border-
|
|
116
|
+
skipped: "border-skip-border text-skip font-mono uppercase",
|
|
117
|
+
pending: "border-pend-border text-pend font-mono uppercase",
|
|
118
|
+
tag: "border-border text-muted-foreground font-mono font-medium [a&]:hover:bg-muted"
|
|
114
119
|
}
|
|
115
120
|
},
|
|
116
121
|
defaultVariants: {
|
|
@@ -663,7 +668,7 @@ function ReportTrace({ spans }) {
|
|
|
663
668
|
{
|
|
664
669
|
className: "min-w-0 flex-1 truncate text-foreground",
|
|
665
670
|
style: { paddingLeft: `${row.depth * 12}px` },
|
|
666
|
-
title: row.statusMessage ? `${row.name}
|
|
671
|
+
title: row.statusMessage ? `${row.name}: ${row.statusMessage}` : row.name,
|
|
667
672
|
children: row.name
|
|
668
673
|
}
|
|
669
674
|
),
|
|
@@ -802,12 +807,12 @@ function useCollapse() {
|
|
|
802
807
|
}
|
|
803
808
|
var STORAGE_KEY = "es-collapsed-ids";
|
|
804
809
|
function readPersisted() {
|
|
805
|
-
if (typeof localStorage === "undefined") return
|
|
810
|
+
if (typeof localStorage === "undefined") return null;
|
|
806
811
|
try {
|
|
807
812
|
const raw = localStorage.getItem(STORAGE_KEY);
|
|
808
|
-
return raw ? new Set(JSON.parse(raw)) :
|
|
813
|
+
return raw ? new Set(JSON.parse(raw)) : null;
|
|
809
814
|
} catch {
|
|
810
|
-
return
|
|
815
|
+
return null;
|
|
811
816
|
}
|
|
812
817
|
}
|
|
813
818
|
function persist(ids) {
|
|
@@ -817,8 +822,10 @@ function persist(ids) {
|
|
|
817
822
|
} catch {
|
|
818
823
|
}
|
|
819
824
|
}
|
|
820
|
-
function useCollapseState() {
|
|
821
|
-
const [collapsed, setCollapsed] = useState(
|
|
825
|
+
function useCollapseState(computeDefault) {
|
|
826
|
+
const [collapsed, setCollapsed] = useState(
|
|
827
|
+
() => readPersisted() ?? computeDefault?.() ?? /* @__PURE__ */ new Set()
|
|
828
|
+
);
|
|
822
829
|
const isCollapsed = useCallback((id) => collapsed.has(id), [collapsed]);
|
|
823
830
|
const toggle = useCallback((id) => {
|
|
824
831
|
setCollapsed((prev) => {
|
|
@@ -896,7 +903,7 @@ function Card({ className, ...props }) {
|
|
|
896
903
|
{
|
|
897
904
|
"data-slot": "card",
|
|
898
905
|
className: cn(
|
|
899
|
-
"flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-
|
|
906
|
+
"flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-xs",
|
|
900
907
|
className
|
|
901
908
|
),
|
|
902
909
|
...props
|
|
@@ -927,52 +934,89 @@ function CardContent({ className, ...props }) {
|
|
|
927
934
|
);
|
|
928
935
|
}
|
|
929
936
|
|
|
930
|
-
// src/components/
|
|
937
|
+
// src/components/ui/dropdown-menu.tsx
|
|
938
|
+
import "react";
|
|
939
|
+
import { Menu as MenuPrimitive } from "@base-ui/react/menu";
|
|
940
|
+
import { CheckIcon } from "lucide-react";
|
|
931
941
|
import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
932
|
-
var
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
};
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
942
|
+
var itemClasses = "relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 [&_svg:not([class*='text-'])]:text-muted-foreground";
|
|
943
|
+
function DropdownMenu(props) {
|
|
944
|
+
return /* @__PURE__ */ jsx22(MenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
|
|
945
|
+
}
|
|
946
|
+
function DropdownMenuTrigger(props) {
|
|
947
|
+
return /* @__PURE__ */ jsx22(MenuPrimitive.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
|
|
948
|
+
}
|
|
949
|
+
function DropdownMenuContent({
|
|
950
|
+
className,
|
|
951
|
+
align = "end",
|
|
952
|
+
sideOffset = 4,
|
|
953
|
+
...props
|
|
954
|
+
}) {
|
|
955
|
+
return /* @__PURE__ */ jsx22(MenuPrimitive.Portal, { children: /* @__PURE__ */ jsx22(
|
|
956
|
+
MenuPrimitive.Positioner,
|
|
957
|
+
{
|
|
958
|
+
"data-slot": "dropdown-menu-positioner",
|
|
959
|
+
className: "z-50",
|
|
960
|
+
align,
|
|
961
|
+
sideOffset,
|
|
962
|
+
children: /* @__PURE__ */ jsx22(
|
|
963
|
+
MenuPrimitive.Popup,
|
|
964
|
+
{
|
|
965
|
+
"data-slot": "dropdown-menu-content",
|
|
966
|
+
className: cn(
|
|
967
|
+
"z-50 min-w-[11rem] origin-[var(--transform-origin)] overflow-hidden rounded-md border border-border bg-popover p-1 font-sans text-sm text-popover-foreground shadow-md transition-[opacity,transform] data-[ending-style]:scale-95 data-[ending-style]:opacity-0 data-[starting-style]:scale-95 data-[starting-style]:opacity-0",
|
|
968
|
+
className
|
|
969
|
+
),
|
|
970
|
+
...props
|
|
971
|
+
}
|
|
972
|
+
)
|
|
973
|
+
}
|
|
974
|
+
) });
|
|
975
|
+
}
|
|
976
|
+
function DropdownMenuItem({
|
|
977
|
+
className,
|
|
978
|
+
...props
|
|
979
|
+
}) {
|
|
980
|
+
return /* @__PURE__ */ jsx22(
|
|
981
|
+
MenuPrimitive.Item,
|
|
982
|
+
{
|
|
983
|
+
"data-slot": "dropdown-menu-item",
|
|
984
|
+
className: cn(itemClasses, className),
|
|
985
|
+
...props
|
|
986
|
+
}
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// src/components/ReportScenario.tsx
|
|
991
|
+
import { LinkIcon, FileTextIcon, SparklesIcon, MoreHorizontalIcon } from "lucide-react";
|
|
992
|
+
import { jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
993
|
+
var STATUS_META = {
|
|
994
|
+
passed: { label: "Passed", glyph: "\u2713", glyphColor: "text-pass", badgeVariant: "passed", showPill: false },
|
|
995
|
+
failed: { label: "Failed", glyph: "\u2717", glyphColor: "text-fail", badgeVariant: "failed", showPill: true },
|
|
996
|
+
skipped: { label: "Skipped", glyph: "\u25CB", glyphColor: "text-skip", badgeVariant: "skipped", showPill: true },
|
|
997
|
+
pending: { label: "Pending", glyph: "\u25CB", glyphColor: "text-pend", badgeVariant: "pending", showPill: true }
|
|
955
998
|
};
|
|
956
999
|
function ReportScenario({ scenario, hideTitle = false }) {
|
|
957
1000
|
const titleId = `${scenario.id}-title`;
|
|
958
1001
|
const bodyId = `${scenario.id}-body`;
|
|
959
|
-
const
|
|
1002
|
+
const meta = STATUS_META[scenario.status];
|
|
1003
|
+
const label = scenario.planned ? "Planned" : meta.label;
|
|
960
1004
|
const collapse = useCollapse();
|
|
961
1005
|
const collapsible = collapse !== null && !hideTitle;
|
|
962
1006
|
const collapsed = collapsible ? collapse.isCollapsed(scenario.id) : false;
|
|
963
1007
|
const actions = useScenarioActions();
|
|
964
|
-
return /* @__PURE__ */
|
|
1008
|
+
return /* @__PURE__ */ jsxs15(
|
|
965
1009
|
Card,
|
|
966
1010
|
{
|
|
967
1011
|
id: scenario.id,
|
|
968
1012
|
"data-status": scenario.status,
|
|
969
|
-
...hideTitle ? { "aria-label": `${scenario.title}
|
|
1013
|
+
...hideTitle ? { "aria-label": `${scenario.title}, ${label}` } : { "aria-labelledby": titleId },
|
|
970
1014
|
className: "gap-0 py-0",
|
|
971
1015
|
children: [
|
|
972
|
-
/* @__PURE__ */
|
|
973
|
-
/* @__PURE__ */
|
|
974
|
-
hideTitle ? null : /* @__PURE__ */
|
|
975
|
-
collapsible ? /* @__PURE__ */
|
|
1016
|
+
/* @__PURE__ */ jsxs15(CardHeader, { className: "px-4 py-3", children: [
|
|
1017
|
+
/* @__PURE__ */ jsxs15("div", { className: cn("flex items-start gap-4", hideTitle ? "justify-end" : "justify-between"), children: [
|
|
1018
|
+
hideTitle ? null : /* @__PURE__ */ jsxs15("h3", { id: titleId, className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
|
|
1019
|
+
collapsible ? /* @__PURE__ */ jsx23(
|
|
976
1020
|
"button",
|
|
977
1021
|
{
|
|
978
1022
|
type: "button",
|
|
@@ -981,60 +1025,49 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
981
1025
|
"aria-controls": bodyId,
|
|
982
1026
|
"aria-label": `Toggle ${scenario.title}`,
|
|
983
1027
|
className: "shrink-0 cursor-pointer text-muted-foreground hover:text-foreground",
|
|
984
|
-
children: /* @__PURE__ */
|
|
1028
|
+
children: /* @__PURE__ */ jsx23("span", { "aria-hidden": true, className: cn("inline-block text-xs transition-transform", !collapsed && "rotate-90"), children: "\u25B8" })
|
|
985
1029
|
}
|
|
986
1030
|
) : null,
|
|
987
|
-
/* @__PURE__ */
|
|
988
|
-
/* @__PURE__ */
|
|
1031
|
+
/* @__PURE__ */ jsx23("span", { "aria-hidden": true, className: cn("text-sm leading-none", meta.glyphColor), children: meta.glyph }),
|
|
1032
|
+
/* @__PURE__ */ jsx23("span", { children: scenario.title })
|
|
989
1033
|
] }),
|
|
990
|
-
/* @__PURE__ */
|
|
991
|
-
/* @__PURE__ */
|
|
992
|
-
scenario.durationMs > 0 ? /* @__PURE__ */
|
|
993
|
-
/* @__PURE__ */
|
|
994
|
-
actions ? /* @__PURE__ */
|
|
995
|
-
/* @__PURE__ */
|
|
996
|
-
|
|
997
|
-
{
|
|
998
|
-
type: "button",
|
|
999
|
-
onClick: () => actions.copyLink(scenario),
|
|
1000
|
-
"aria-label": `Copy link to ${scenario.title}`,
|
|
1001
|
-
title: "Copy permalink",
|
|
1002
|
-
className: "cursor-pointer rounded px-1 py-0.5 font-mono text-[0.625rem] text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
1003
|
-
children: "link"
|
|
1004
|
-
}
|
|
1005
|
-
),
|
|
1006
|
-
/* @__PURE__ */ jsx22(
|
|
1007
|
-
"button",
|
|
1034
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
1035
|
+
/* @__PURE__ */ jsx23(ScenarioRunHistory, { scenarioId: scenario.id }),
|
|
1036
|
+
scenario.durationMs > 0 ? /* @__PURE__ */ jsx23("span", { className: "whitespace-nowrap font-mono text-[0.6875rem] text-muted-foreground", children: formatDuration(scenario.durationMs) }) : null,
|
|
1037
|
+
meta.showPill ? /* @__PURE__ */ jsx23(Badge, { variant: meta.badgeVariant, "aria-label": `Status: ${label}`, children: label }) : /* @__PURE__ */ jsx23("span", { className: "sr-only", children: `Status: ${label}` }),
|
|
1038
|
+
actions ? /* @__PURE__ */ jsxs15(DropdownMenu, { children: [
|
|
1039
|
+
/* @__PURE__ */ jsx23(
|
|
1040
|
+
DropdownMenuTrigger,
|
|
1008
1041
|
{
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
"aria-
|
|
1012
|
-
title: "Copy as Markdown",
|
|
1013
|
-
className: "cursor-pointer rounded px-1 py-0.5 font-mono text-[0.625rem] text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
1014
|
-
children: "md"
|
|
1042
|
+
"aria-label": `Actions for ${scenario.title}`,
|
|
1043
|
+
className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
|
|
1044
|
+
children: /* @__PURE__ */ jsx23(MoreHorizontalIcon, { "aria-hidden": true })
|
|
1015
1045
|
}
|
|
1016
1046
|
),
|
|
1017
|
-
/* @__PURE__ */
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1047
|
+
/* @__PURE__ */ jsxs15(DropdownMenuContent, { align: "end", children: [
|
|
1048
|
+
/* @__PURE__ */ jsxs15(DropdownMenuItem, { onClick: () => actions.copyLink(scenario), children: [
|
|
1049
|
+
/* @__PURE__ */ jsx23(LinkIcon, {}),
|
|
1050
|
+
" Copy link"
|
|
1051
|
+
] }),
|
|
1052
|
+
/* @__PURE__ */ jsxs15(DropdownMenuItem, { onClick: () => actions.copyMarkdown(scenario), children: [
|
|
1053
|
+
/* @__PURE__ */ jsx23(FileTextIcon, {}),
|
|
1054
|
+
" Copy as Markdown"
|
|
1055
|
+
] }),
|
|
1056
|
+
/* @__PURE__ */ jsxs15(DropdownMenuItem, { onClick: () => actions.copyPrompt(scenario), children: [
|
|
1057
|
+
/* @__PURE__ */ jsx23(SparklesIcon, {}),
|
|
1058
|
+
" Explain with AI"
|
|
1059
|
+
] })
|
|
1060
|
+
] })
|
|
1028
1061
|
] }) : null
|
|
1029
1062
|
] })
|
|
1030
1063
|
] }),
|
|
1031
|
-
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */
|
|
1032
|
-
scenario.tags.map((t) => /* @__PURE__ */
|
|
1033
|
-
(scenario.tickets ?? []).map((ticket) => /* @__PURE__ */
|
|
1064
|
+
scenario.tags.length > 0 || (scenario.tickets?.length ?? 0) > 0 ? /* @__PURE__ */ jsxs15("ul", { "aria-label": "Tags and tickets", className: "flex flex-wrap gap-1.5", children: [
|
|
1065
|
+
scenario.tags.map((t) => /* @__PURE__ */ jsx23("li", { children: /* @__PURE__ */ jsx23(Badge, { variant: "tag", children: t }) }, `tag-${t}`)),
|
|
1066
|
+
(scenario.tickets ?? []).map((ticket) => /* @__PURE__ */ jsx23("li", { children: ticket.url ? /* @__PURE__ */ jsx23("a", { href: ticket.url, target: "_blank", rel: "noreferrer noopener", children: /* @__PURE__ */ jsx23(Badge, { variant: "outline", children: ticket.id }) }) : /* @__PURE__ */ jsx23(Badge, { variant: "outline", children: ticket.id }) }, `ticket-${ticket.id}`))
|
|
1034
1067
|
] }) : null
|
|
1035
1068
|
] }),
|
|
1036
|
-
/* @__PURE__ */
|
|
1037
|
-
scenario.errorMessage ? /* @__PURE__ */
|
|
1069
|
+
/* @__PURE__ */ jsxs15(CardContent, { id: bodyId, hidden: collapsed, className: "px-4 pt-0 pb-4", children: [
|
|
1070
|
+
scenario.errorMessage ? /* @__PURE__ */ jsx23(
|
|
1038
1071
|
"pre",
|
|
1039
1072
|
{
|
|
1040
1073
|
role: "alert",
|
|
@@ -1042,10 +1075,10 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1042
1075
|
children: scenario.errorMessage
|
|
1043
1076
|
}
|
|
1044
1077
|
) : null,
|
|
1045
|
-
/* @__PURE__ */
|
|
1046
|
-
scenario.docEntries.length > 0 ? /* @__PURE__ */
|
|
1047
|
-
/* @__PURE__ */
|
|
1048
|
-
/* @__PURE__ */
|
|
1078
|
+
/* @__PURE__ */ jsx23(ReportSteps, { scenario }),
|
|
1079
|
+
scenario.docEntries.length > 0 ? /* @__PURE__ */ jsx23("div", { "data-es-docs": true, className: "mt-3", children: /* @__PURE__ */ jsx23(ReportDocEntries, { entries: scenario.docEntries }) }) : null,
|
|
1080
|
+
/* @__PURE__ */ jsx23(ReportAttachments, { attachments: scenario.attachments }),
|
|
1081
|
+
/* @__PURE__ */ jsx23(ReportTrace, { spans: scenario.otelSpans })
|
|
1049
1082
|
] })
|
|
1050
1083
|
]
|
|
1051
1084
|
}
|
|
@@ -1053,13 +1086,13 @@ function ReportScenario({ scenario, hideTitle = false }) {
|
|
|
1053
1086
|
}
|
|
1054
1087
|
|
|
1055
1088
|
// src/components/ReportScenarioList.tsx
|
|
1056
|
-
import { Fragment as Fragment8, jsx as
|
|
1089
|
+
import { Fragment as Fragment8, jsx as jsx24 } from "react/jsx-runtime";
|
|
1057
1090
|
function ReportScenarioList({ feature }) {
|
|
1058
|
-
return /* @__PURE__ */
|
|
1091
|
+
return /* @__PURE__ */ jsx24(Fragment8, { children: feature.scenarios.map((scenario) => /* @__PURE__ */ jsx24(ReportScenario, { scenario }, scenario.id)) });
|
|
1059
1092
|
}
|
|
1060
1093
|
|
|
1061
1094
|
// src/components/ReportFeature.tsx
|
|
1062
|
-
import { jsx as
|
|
1095
|
+
import { Fragment as Fragment9, jsx as jsx25, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1063
1096
|
function ReportFeature({ feature }) {
|
|
1064
1097
|
const titleId = `${feature.id}-title`;
|
|
1065
1098
|
const bodyId = `${feature.id}-body`;
|
|
@@ -1067,74 +1100,80 @@ function ReportFeature({ feature }) {
|
|
|
1067
1100
|
const skipped = s.skipped + s.pending;
|
|
1068
1101
|
const collapse = useCollapse();
|
|
1069
1102
|
const collapsed = collapse?.isCollapsed(feature.id) ?? false;
|
|
1070
|
-
return
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
"
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1103
|
+
return (
|
|
1104
|
+
// Suite = a section heading + a divider, NOT another bordered card. Drops a
|
|
1105
|
+
// layer of nesting so the scenario cards below read as the primary unit.
|
|
1106
|
+
/* @__PURE__ */ jsxs16(
|
|
1107
|
+
"section",
|
|
1108
|
+
{
|
|
1109
|
+
id: feature.id,
|
|
1110
|
+
"data-slot": "feature",
|
|
1111
|
+
"aria-labelledby": titleId,
|
|
1112
|
+
className: "flex flex-col gap-3",
|
|
1113
|
+
children: [
|
|
1114
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex items-end justify-between gap-4 border-b border-border pb-2", children: [
|
|
1115
|
+
/* @__PURE__ */ jsxs16("div", { className: "min-w-0", children: [
|
|
1116
|
+
/* @__PURE__ */ jsx25("h2", { id: titleId, className: "text-base font-semibold tracking-tight text-foreground", children: collapse ? /* @__PURE__ */ jsxs16(
|
|
1117
|
+
"button",
|
|
1118
|
+
{
|
|
1119
|
+
type: "button",
|
|
1120
|
+
onClick: () => collapse.toggle(feature.id),
|
|
1121
|
+
"aria-expanded": !collapsed,
|
|
1122
|
+
"aria-controls": bodyId,
|
|
1123
|
+
className: "flex w-full cursor-pointer items-center gap-2 text-left hover:text-foreground/80",
|
|
1124
|
+
children: [
|
|
1125
|
+
/* @__PURE__ */ jsx25("span", { "aria-hidden": true, className: cn("shrink-0 text-xs text-muted-foreground transition-transform", !collapsed && "rotate-90"), children: "\u25B8" }),
|
|
1126
|
+
/* @__PURE__ */ jsx25("span", { className: "truncate", children: feature.title })
|
|
1127
|
+
]
|
|
1128
|
+
}
|
|
1129
|
+
) : feature.title }),
|
|
1130
|
+
/* @__PURE__ */ jsx25("p", { className: cn("mt-0.5 truncate font-mono text-xs text-muted-foreground", collapse && "pl-5"), children: feature.sourceFile })
|
|
1131
|
+
] }),
|
|
1132
|
+
/* @__PURE__ */ jsxs16(
|
|
1133
|
+
"p",
|
|
1082
1134
|
{
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1135
|
+
className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
|
|
1136
|
+
"aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
|
|
1137
|
+
children: [
|
|
1138
|
+
/* @__PURE__ */ jsxs16("span", { children: [
|
|
1139
|
+
s.passed,
|
|
1140
|
+
" passed"
|
|
1141
|
+
] }),
|
|
1142
|
+
/* @__PURE__ */ jsx25("span", { "aria-hidden": true, children: "\xB7" }),
|
|
1143
|
+
/* @__PURE__ */ jsxs16("span", { className: cn(s.failed > 0 && "font-medium text-fail"), children: [
|
|
1144
|
+
s.failed,
|
|
1145
|
+
" failed"
|
|
1146
|
+
] }),
|
|
1147
|
+
skipped > 0 ? /* @__PURE__ */ jsxs16(Fragment9, { children: [
|
|
1148
|
+
/* @__PURE__ */ jsx25("span", { "aria-hidden": true, children: "\xB7" }),
|
|
1149
|
+
/* @__PURE__ */ jsxs16("span", { children: [
|
|
1150
|
+
skipped,
|
|
1151
|
+
" skipped"
|
|
1152
|
+
] })
|
|
1153
|
+
] }) : null
|
|
1154
|
+
]
|
|
1090
1155
|
}
|
|
1091
|
-
)
|
|
1092
|
-
/* @__PURE__ */ jsxs15("div", { className: "min-w-0", children: [
|
|
1093
|
-
/* @__PURE__ */ jsx24("h2", { id: titleId, className: "text-sm font-semibold tracking-tight text-foreground", children: feature.title }),
|
|
1094
|
-
/* @__PURE__ */ jsx24("p", { className: "mt-0.5 truncate font-mono text-xs text-muted-foreground", children: feature.sourceFile })
|
|
1095
|
-
] })
|
|
1156
|
+
)
|
|
1096
1157
|
] }),
|
|
1097
|
-
/* @__PURE__ */
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
className: "flex shrink-0 items-center gap-2.5 font-mono text-xs font-medium",
|
|
1102
|
-
"aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
|
|
1103
|
-
children: [
|
|
1104
|
-
/* @__PURE__ */ jsxs15("span", { "aria-hidden": true, className: "text-pass", children: [
|
|
1105
|
-
"\u2713",
|
|
1106
|
-
s.passed
|
|
1107
|
-
] }),
|
|
1108
|
-
/* @__PURE__ */ jsxs15("span", { "aria-hidden": true, className: "text-fail", children: [
|
|
1109
|
-
"\u2717",
|
|
1110
|
-
s.failed
|
|
1111
|
-
] }),
|
|
1112
|
-
/* @__PURE__ */ jsxs15("span", { "aria-hidden": true, className: "text-skip", children: [
|
|
1113
|
-
"\u25CB",
|
|
1114
|
-
skipped
|
|
1115
|
-
] })
|
|
1116
|
-
]
|
|
1117
|
-
}
|
|
1118
|
-
)
|
|
1119
|
-
] }),
|
|
1120
|
-
/* @__PURE__ */ jsx24("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-2 p-3", children: /* @__PURE__ */ jsx24(ReportScenarioList, { feature }) })
|
|
1121
|
-
]
|
|
1122
|
-
}
|
|
1158
|
+
/* @__PURE__ */ jsx25("div", { id: bodyId, hidden: collapsed, className: "flex flex-col gap-3", children: /* @__PURE__ */ jsx25(ReportScenarioList, { feature }) })
|
|
1159
|
+
]
|
|
1160
|
+
}
|
|
1161
|
+
)
|
|
1123
1162
|
);
|
|
1124
1163
|
}
|
|
1125
1164
|
|
|
1126
1165
|
// src/components/ReportFeatureList.tsx
|
|
1127
|
-
import {
|
|
1166
|
+
import { jsx as jsx26 } from "react/jsx-runtime";
|
|
1128
1167
|
function ReportFeatureList() {
|
|
1129
1168
|
const report = useReport();
|
|
1130
|
-
return /* @__PURE__ */
|
|
1169
|
+
return /* @__PURE__ */ jsx26("div", { className: "flex flex-col gap-8", children: report.features.map((feature) => /* @__PURE__ */ jsx26(ReportFeature, { feature }, feature.id)) });
|
|
1131
1170
|
}
|
|
1132
1171
|
|
|
1133
1172
|
// src/components/ui/empty.tsx
|
|
1134
1173
|
import { cva as cva2 } from "class-variance-authority";
|
|
1135
|
-
import { jsx as
|
|
1174
|
+
import { jsx as jsx27 } from "react/jsx-runtime";
|
|
1136
1175
|
function Empty({ className, ...props }) {
|
|
1137
|
-
return /* @__PURE__ */
|
|
1176
|
+
return /* @__PURE__ */ jsx27(
|
|
1138
1177
|
"div",
|
|
1139
1178
|
{
|
|
1140
1179
|
"data-slot": "empty",
|
|
@@ -1147,7 +1186,7 @@ function Empty({ className, ...props }) {
|
|
|
1147
1186
|
);
|
|
1148
1187
|
}
|
|
1149
1188
|
function EmptyHeader({ className, ...props }) {
|
|
1150
|
-
return /* @__PURE__ */
|
|
1189
|
+
return /* @__PURE__ */ jsx27(
|
|
1151
1190
|
"div",
|
|
1152
1191
|
{
|
|
1153
1192
|
"data-slot": "empty-header",
|
|
@@ -1174,7 +1213,7 @@ var emptyMediaVariants = cva2(
|
|
|
1174
1213
|
}
|
|
1175
1214
|
);
|
|
1176
1215
|
function EmptyTitle({ className, ...props }) {
|
|
1177
|
-
return /* @__PURE__ */
|
|
1216
|
+
return /* @__PURE__ */ jsx27(
|
|
1178
1217
|
"div",
|
|
1179
1218
|
{
|
|
1180
1219
|
"data-slot": "empty-title",
|
|
@@ -1185,16 +1224,16 @@ function EmptyTitle({ className, ...props }) {
|
|
|
1185
1224
|
}
|
|
1186
1225
|
|
|
1187
1226
|
// src/components/ReportEmpty.tsx
|
|
1188
|
-
import { jsx as
|
|
1227
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
1189
1228
|
function ReportEmpty({ message }) {
|
|
1190
|
-
return /* @__PURE__ */
|
|
1229
|
+
return /* @__PURE__ */ jsx28(Empty, { "aria-live": "polite", className: "border", children: /* @__PURE__ */ jsx28(EmptyHeader, { children: /* @__PURE__ */ jsx28(EmptyTitle, { children: message ?? "No scenarios in this report." }) }) });
|
|
1191
1230
|
}
|
|
1192
1231
|
|
|
1193
1232
|
// src/components/ReportSummary.tsx
|
|
1194
|
-
import {
|
|
1233
|
+
import { jsx as jsx29, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1195
1234
|
function ReportSummary({ className }) {
|
|
1196
1235
|
const report = useReport();
|
|
1197
|
-
return /* @__PURE__ */
|
|
1236
|
+
return /* @__PURE__ */ jsx29(
|
|
1198
1237
|
ReportSummaryView,
|
|
1199
1238
|
{
|
|
1200
1239
|
summary: report.summary,
|
|
@@ -1203,38 +1242,54 @@ function ReportSummary({ className }) {
|
|
|
1203
1242
|
}
|
|
1204
1243
|
);
|
|
1205
1244
|
}
|
|
1245
|
+
var TONE = {
|
|
1246
|
+
total: { num: "text-foreground", dot: "bg-muted-foreground/50" },
|
|
1247
|
+
passed: { num: "text-pass", dot: "bg-pass" },
|
|
1248
|
+
failed: { num: "text-fail", dot: "bg-fail" },
|
|
1249
|
+
skipped: { num: "text-skip", dot: "bg-skip" },
|
|
1250
|
+
pending: { num: "text-pend", dot: "bg-pend" }
|
|
1251
|
+
};
|
|
1206
1252
|
function ReportSummaryView({ summary, className, ariaLabel }) {
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1253
|
+
const cards = [
|
|
1254
|
+
{ status: "total", label: "Total", value: summary.total },
|
|
1255
|
+
{ status: "passed", label: "Passed", value: summary.passed },
|
|
1256
|
+
{ status: "failed", label: "Failed", value: summary.failed },
|
|
1257
|
+
{ status: "skipped", label: "Skipped", value: summary.skipped }
|
|
1258
|
+
];
|
|
1259
|
+
if (summary.pending > 0) {
|
|
1260
|
+
cards.push({ status: "pending", label: "Pending", value: summary.pending });
|
|
1261
|
+
}
|
|
1262
|
+
return /* @__PURE__ */ jsx29(
|
|
1263
|
+
"dl",
|
|
1264
|
+
{
|
|
1265
|
+
className: cn(
|
|
1266
|
+
"grid grid-cols-2 gap-3",
|
|
1267
|
+
// Columns follow the card count so the row is always full — 5 statuses
|
|
1268
|
+
// (pending present) never leave a lone orphan card below a row of four.
|
|
1269
|
+
cards.length === 5 ? "sm:grid-cols-5" : "sm:grid-cols-4",
|
|
1270
|
+
className
|
|
1271
|
+
),
|
|
1272
|
+
"aria-label": ariaLabel,
|
|
1273
|
+
children: cards.map((c) => {
|
|
1274
|
+
const tone = TONE[c.status];
|
|
1275
|
+
return /* @__PURE__ */ jsxs17(
|
|
1276
|
+
"div",
|
|
1277
|
+
{
|
|
1278
|
+
"data-status": c.status,
|
|
1279
|
+
className: "rounded-lg border bg-card px-4 py-3",
|
|
1280
|
+
children: [
|
|
1281
|
+
/* @__PURE__ */ jsxs17("dt", { className: "flex items-center gap-1.5 font-mono text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: [
|
|
1282
|
+
/* @__PURE__ */ jsx29("span", { className: cn("size-1.5 shrink-0 rounded-full", tone.dot), "aria-hidden": "true" }),
|
|
1283
|
+
c.label
|
|
1284
|
+
] }),
|
|
1285
|
+
/* @__PURE__ */ jsx29("dd", { className: cn("mt-2 text-3xl font-semibold leading-none tracking-tight tabular-nums", tone.num), children: c.value })
|
|
1286
|
+
]
|
|
1287
|
+
},
|
|
1288
|
+
c.status
|
|
1289
|
+
);
|
|
1290
|
+
})
|
|
1291
|
+
}
|
|
1292
|
+
);
|
|
1238
1293
|
}
|
|
1239
1294
|
|
|
1240
1295
|
// src/lib/provenance.ts
|
|
@@ -1287,7 +1342,7 @@ function prUrl(ci) {
|
|
|
1287
1342
|
}
|
|
1288
1343
|
|
|
1289
1344
|
// src/components/ReportMeta.tsx
|
|
1290
|
-
import { jsx as
|
|
1345
|
+
import { jsx as jsx30, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1291
1346
|
function ReportMeta() {
|
|
1292
1347
|
const report = useReport();
|
|
1293
1348
|
const items = [];
|
|
@@ -1310,13 +1365,13 @@ function ReportMeta() {
|
|
|
1310
1365
|
const short = sha.slice(0, 8);
|
|
1311
1366
|
items.push({
|
|
1312
1367
|
label: "Commit",
|
|
1313
|
-
value: url ? /* @__PURE__ */
|
|
1368
|
+
value: url ? /* @__PURE__ */ jsx30("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: short }) : short
|
|
1314
1369
|
});
|
|
1315
1370
|
}
|
|
1316
1371
|
if (report.ci) {
|
|
1317
1372
|
items.push({
|
|
1318
1373
|
label: "CI",
|
|
1319
|
-
value: report.ci.url ? /* @__PURE__ */
|
|
1374
|
+
value: report.ci.url ? /* @__PURE__ */ jsxs18("a", { href: report.ci.url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: [
|
|
1320
1375
|
ciDisplayName(report.ci.name),
|
|
1321
1376
|
report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""
|
|
1322
1377
|
] }) : `${ciDisplayName(report.ci.name)}${report.ci.buildNumber ? ` #${report.ci.buildNumber}` : ""}`
|
|
@@ -1327,54 +1382,144 @@ function ReportMeta() {
|
|
|
1327
1382
|
const label = `#${report.ci.prNumber}`;
|
|
1328
1383
|
items.push({
|
|
1329
1384
|
label: "PR",
|
|
1330
|
-
value: url ? /* @__PURE__ */
|
|
1385
|
+
value: url ? /* @__PURE__ */ jsx30("a", { href: url, target: "_blank", rel: "noreferrer noopener", className: linkClass, children: label }) : label
|
|
1331
1386
|
});
|
|
1332
1387
|
}
|
|
1333
1388
|
if (items.length === 0) return null;
|
|
1334
|
-
return
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1389
|
+
return (
|
|
1390
|
+
// Run metadata is useful but secondary to "what failed" — tuck it into a
|
|
1391
|
+
// collapsed disclosure so it doesn't push the result down. Native <details>
|
|
1392
|
+
// so it works with no JS (the SSR/no-hydration report still toggles).
|
|
1393
|
+
/* @__PURE__ */ jsxs18("details", { "data-es-meta-card": true, className: "es-run-details group rounded-lg border border-border bg-card text-xs", children: [
|
|
1394
|
+
/* @__PURE__ */ jsxs18("summary", { className: "flex cursor-pointer list-none select-none items-center gap-1.5 px-4 py-2 font-medium text-muted-foreground [&::-webkit-details-marker]:hidden", children: [
|
|
1395
|
+
/* @__PURE__ */ jsx30("span", { "aria-hidden": true, className: "text-xs transition-transform group-open:rotate-90", children: "\u25B8" }),
|
|
1396
|
+
"Run details"
|
|
1397
|
+
] }),
|
|
1398
|
+
/* @__PURE__ */ jsx30("dl", { className: "flex flex-wrap gap-x-5 gap-y-2 border-t border-border px-4 py-3 text-muted-foreground", "aria-label": "Run metadata", children: items.map((item) => /* @__PURE__ */ jsxs18("div", { className: "flex items-baseline gap-1.5", children: [
|
|
1399
|
+
/* @__PURE__ */ jsx30("dt", { className: "font-medium", children: item.label }),
|
|
1400
|
+
/* @__PURE__ */ jsx30("dd", { className: "font-mono text-foreground", children: item.value })
|
|
1401
|
+
] }, item.label)) })
|
|
1402
|
+
] })
|
|
1403
|
+
);
|
|
1338
1404
|
}
|
|
1339
1405
|
|
|
1340
1406
|
// src/components/ReportSchemaError.tsx
|
|
1341
|
-
import { jsx as
|
|
1407
|
+
import { jsx as jsx31, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1342
1408
|
function ReportSchemaError({ error }) {
|
|
1343
|
-
return /* @__PURE__ */
|
|
1344
|
-
/* @__PURE__ */
|
|
1345
|
-
/* @__PURE__ */
|
|
1346
|
-
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */
|
|
1409
|
+
return /* @__PURE__ */ jsxs19("section", { className: "es-schema-error", role: "alert", "aria-live": "assertive", children: [
|
|
1410
|
+
/* @__PURE__ */ jsx31("p", { children: /* @__PURE__ */ jsx31("strong", { children: "Report could not be displayed." }) }),
|
|
1411
|
+
/* @__PURE__ */ jsx31("p", { children: error.message }),
|
|
1412
|
+
error.code === "SCHEMA_VERSION_MISMATCH" ? /* @__PURE__ */ jsxs19("p", { children: [
|
|
1347
1413
|
"The report bundle is newer than this version of ",
|
|
1348
|
-
/* @__PURE__ */
|
|
1414
|
+
/* @__PURE__ */ jsx31("code", { children: "executable-stories-react" }),
|
|
1349
1415
|
". Upgrade the package, or regenerate the report with an older formatters CLI."
|
|
1350
1416
|
] }) : null,
|
|
1351
|
-
error.issues && error.issues.length > 0 ? /* @__PURE__ */
|
|
1352
|
-
/* @__PURE__ */
|
|
1417
|
+
error.issues && error.issues.length > 0 ? /* @__PURE__ */ jsxs19("details", { children: [
|
|
1418
|
+
/* @__PURE__ */ jsxs19("summary", { children: [
|
|
1353
1419
|
error.issues.length,
|
|
1354
1420
|
" validation issue",
|
|
1355
1421
|
error.issues.length === 1 ? "" : "s"
|
|
1356
1422
|
] }),
|
|
1357
|
-
/* @__PURE__ */
|
|
1423
|
+
/* @__PURE__ */ jsx31("pre", { children: error.issues.slice(0, 20).map((i) => `${i.path}: ${i.message}`).join("\n") })
|
|
1358
1424
|
] }) : null
|
|
1359
1425
|
] });
|
|
1360
1426
|
}
|
|
1361
1427
|
|
|
1362
1428
|
// src/components/ReportShell.tsx
|
|
1363
|
-
import { Fragment as
|
|
1364
|
-
function ReportTitleBlock({ title }) {
|
|
1365
|
-
return /* @__PURE__ */ jsxs19(Fragment11, { children: [
|
|
1366
|
-
/* @__PURE__ */ jsx31("h1", { children: title ?? "Story Report" }),
|
|
1367
|
-
/* @__PURE__ */ jsx31(ReportSummary, {}),
|
|
1368
|
-
/* @__PURE__ */ jsx31(ReportMeta, {})
|
|
1369
|
-
] });
|
|
1370
|
-
}
|
|
1429
|
+
import { Fragment as Fragment10, jsx as jsx32, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1371
1430
|
function ReportErrorShell({
|
|
1372
1431
|
error,
|
|
1373
1432
|
className,
|
|
1374
1433
|
title,
|
|
1375
1434
|
dataTheme
|
|
1376
1435
|
}) {
|
|
1377
|
-
return /* @__PURE__ */
|
|
1436
|
+
return /* @__PURE__ */ jsx32("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ jsx32(ReportSchemaError, { error }) });
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// src/interactive/ReportInteractive.tsx
|
|
1440
|
+
import { Monitor, Moon, Sun } from "lucide-react";
|
|
1441
|
+
|
|
1442
|
+
// src/components/ui/switch.tsx
|
|
1443
|
+
import "react";
|
|
1444
|
+
import { Switch as SwitchPrimitive } from "@base-ui/react/switch";
|
|
1445
|
+
import { jsx as jsx33 } from "react/jsx-runtime";
|
|
1446
|
+
function Switch({
|
|
1447
|
+
className,
|
|
1448
|
+
...props
|
|
1449
|
+
}) {
|
|
1450
|
+
return /* @__PURE__ */ jsx33(
|
|
1451
|
+
SwitchPrimitive.Root,
|
|
1452
|
+
{
|
|
1453
|
+
"data-slot": "switch",
|
|
1454
|
+
className: cn(
|
|
1455
|
+
"peer inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[checked]:bg-primary data-[unchecked]:bg-input",
|
|
1456
|
+
className
|
|
1457
|
+
),
|
|
1458
|
+
...props,
|
|
1459
|
+
children: /* @__PURE__ */ jsx33(
|
|
1460
|
+
SwitchPrimitive.Thumb,
|
|
1461
|
+
{
|
|
1462
|
+
"data-slot": "switch-thumb",
|
|
1463
|
+
className: "pointer-events-none block size-4 rounded-full bg-background ring-0 transition-transform data-[checked]:translate-x-[calc(100%-2px)] data-[unchecked]:translate-x-0"
|
|
1464
|
+
}
|
|
1465
|
+
)
|
|
1466
|
+
}
|
|
1467
|
+
);
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
// src/components/ui/toggle-group.tsx
|
|
1471
|
+
import * as React5 from "react";
|
|
1472
|
+
import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group";
|
|
1473
|
+
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle";
|
|
1474
|
+
import { cva as cva3 } from "class-variance-authority";
|
|
1475
|
+
import { jsx as jsx34 } from "react/jsx-runtime";
|
|
1476
|
+
var toggleItemVariants = cva3(
|
|
1477
|
+
"inline-flex items-center justify-center gap-1.5 rounded-md font-medium whitespace-nowrap text-muted-foreground transition-[color,background-color,box-shadow] outline-none hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 data-[pressed]:bg-background data-[pressed]:text-foreground data-[pressed]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
1478
|
+
{
|
|
1479
|
+
variants: {
|
|
1480
|
+
size: {
|
|
1481
|
+
default: "h-9 px-3 text-sm",
|
|
1482
|
+
sm: "h-8 px-2.5 text-[0.82rem]"
|
|
1483
|
+
}
|
|
1484
|
+
},
|
|
1485
|
+
defaultVariants: { size: "default" }
|
|
1486
|
+
}
|
|
1487
|
+
);
|
|
1488
|
+
var ToggleGroupContext = React5.createContext({
|
|
1489
|
+
size: "default"
|
|
1490
|
+
});
|
|
1491
|
+
function ToggleGroup({
|
|
1492
|
+
className,
|
|
1493
|
+
size,
|
|
1494
|
+
children,
|
|
1495
|
+
...props
|
|
1496
|
+
}) {
|
|
1497
|
+
return /* @__PURE__ */ jsx34(
|
|
1498
|
+
ToggleGroupPrimitive,
|
|
1499
|
+
{
|
|
1500
|
+
"data-slot": "toggle-group",
|
|
1501
|
+
className: cn("flex w-fit items-center gap-0.5 rounded-lg bg-muted p-0.5", className),
|
|
1502
|
+
...props,
|
|
1503
|
+
children: /* @__PURE__ */ jsx34(ToggleGroupContext.Provider, { value: { size }, children })
|
|
1504
|
+
}
|
|
1505
|
+
);
|
|
1506
|
+
}
|
|
1507
|
+
function ToggleGroupItem({
|
|
1508
|
+
className,
|
|
1509
|
+
children,
|
|
1510
|
+
size,
|
|
1511
|
+
...props
|
|
1512
|
+
}) {
|
|
1513
|
+
const context = React5.useContext(ToggleGroupContext);
|
|
1514
|
+
return /* @__PURE__ */ jsx34(
|
|
1515
|
+
TogglePrimitive,
|
|
1516
|
+
{
|
|
1517
|
+
"data-slot": "toggle-group-item",
|
|
1518
|
+
className: cn(toggleItemVariants({ size: context.size ?? size }), "flex-1 shrink-0", className),
|
|
1519
|
+
...props,
|
|
1520
|
+
children
|
|
1521
|
+
}
|
|
1522
|
+
);
|
|
1378
1523
|
}
|
|
1379
1524
|
|
|
1380
1525
|
// src/interactive/ReportSearch.tsx
|
|
@@ -1382,7 +1527,30 @@ import {
|
|
|
1382
1527
|
forwardRef,
|
|
1383
1528
|
useId
|
|
1384
1529
|
} from "react";
|
|
1385
|
-
import {
|
|
1530
|
+
import { Search } from "lucide-react";
|
|
1531
|
+
|
|
1532
|
+
// src/components/ui/input.tsx
|
|
1533
|
+
import "react";
|
|
1534
|
+
import { jsx as jsx35 } from "react/jsx-runtime";
|
|
1535
|
+
function Input({ className, type, ...props }) {
|
|
1536
|
+
return /* @__PURE__ */ jsx35(
|
|
1537
|
+
"input",
|
|
1538
|
+
{
|
|
1539
|
+
type,
|
|
1540
|
+
"data-slot": "input",
|
|
1541
|
+
className: cn(
|
|
1542
|
+
"h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30",
|
|
1543
|
+
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
1544
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
|
|
1545
|
+
className
|
|
1546
|
+
),
|
|
1547
|
+
...props
|
|
1548
|
+
}
|
|
1549
|
+
);
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
// src/interactive/ReportSearch.tsx
|
|
1553
|
+
import { jsx as jsx36, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1386
1554
|
var ReportSearch = forwardRef(
|
|
1387
1555
|
function ReportSearch2(props, ref) {
|
|
1388
1556
|
const {
|
|
@@ -1390,11 +1558,11 @@ var ReportSearch = forwardRef(
|
|
|
1390
1558
|
onChange,
|
|
1391
1559
|
matchedCount,
|
|
1392
1560
|
totalCount,
|
|
1393
|
-
placeholder = "Search scenarios
|
|
1561
|
+
placeholder = "Search scenarios or tags\u2026",
|
|
1394
1562
|
className
|
|
1395
1563
|
} = props;
|
|
1396
1564
|
const inputId = useId();
|
|
1397
|
-
const showCounts = typeof matchedCount === "number" && typeof totalCount === "number";
|
|
1565
|
+
const showCounts = value.trim().length > 0 && typeof matchedCount === "number" && typeof totalCount === "number";
|
|
1398
1566
|
function handleChange(e) {
|
|
1399
1567
|
onChange(e.target.value);
|
|
1400
1568
|
}
|
|
@@ -1404,24 +1572,46 @@ var ReportSearch = forwardRef(
|
|
|
1404
1572
|
e.preventDefault();
|
|
1405
1573
|
}
|
|
1406
1574
|
}
|
|
1407
|
-
return /* @__PURE__ */
|
|
1408
|
-
/* @__PURE__ */
|
|
1409
|
-
/* @__PURE__ */
|
|
1410
|
-
|
|
1575
|
+
return /* @__PURE__ */ jsxs21("div", { className: cn("es-search flex flex-col gap-1", className), children: [
|
|
1576
|
+
/* @__PURE__ */ jsx36("label", { htmlFor: inputId, className: "sr-only", children: "Search" }),
|
|
1577
|
+
/* @__PURE__ */ jsxs21("div", { className: "relative w-full sm:w-72", children: [
|
|
1578
|
+
/* @__PURE__ */ jsx36(
|
|
1579
|
+
Search,
|
|
1580
|
+
{
|
|
1581
|
+
"aria-hidden": true,
|
|
1582
|
+
className: "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground"
|
|
1583
|
+
}
|
|
1584
|
+
),
|
|
1585
|
+
/* @__PURE__ */ jsx36(
|
|
1586
|
+
Input,
|
|
1587
|
+
{
|
|
1588
|
+
ref,
|
|
1589
|
+
id: inputId,
|
|
1590
|
+
type: "search",
|
|
1591
|
+
value,
|
|
1592
|
+
onChange: handleChange,
|
|
1593
|
+
onKeyDown: handleKeyDown,
|
|
1594
|
+
placeholder,
|
|
1595
|
+
autoComplete: "off",
|
|
1596
|
+
spellCheck: false,
|
|
1597
|
+
"aria-keyshortcuts": "/",
|
|
1598
|
+
className: "pl-8"
|
|
1599
|
+
}
|
|
1600
|
+
)
|
|
1601
|
+
] }),
|
|
1602
|
+
showCounts ? /* @__PURE__ */ jsxs21(
|
|
1603
|
+
"span",
|
|
1411
1604
|
{
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
spellCheck: false,
|
|
1421
|
-
"aria-keyshortcuts": "/"
|
|
1605
|
+
className: "text-xs tabular-nums text-muted-foreground",
|
|
1606
|
+
"aria-live": "polite",
|
|
1607
|
+
children: [
|
|
1608
|
+
matchedCount,
|
|
1609
|
+
" of ",
|
|
1610
|
+
totalCount,
|
|
1611
|
+
" scenarios"
|
|
1612
|
+
]
|
|
1422
1613
|
}
|
|
1423
|
-
)
|
|
1424
|
-
showCounts ? /* @__PURE__ */ jsx32("span", { className: "es-search-counts", "aria-live": "polite", children: value ? `${matchedCount} of ${totalCount}` : `${totalCount} total` }) : null
|
|
1614
|
+
) : null
|
|
1425
1615
|
] });
|
|
1426
1616
|
}
|
|
1427
1617
|
);
|
|
@@ -1450,7 +1640,7 @@ function scrollToScenarioId(scenarioId, options = {}) {
|
|
|
1450
1640
|
}
|
|
1451
1641
|
|
|
1452
1642
|
// src/interactive/ReportFailureBanner.tsx
|
|
1453
|
-
import { jsx as
|
|
1643
|
+
import { jsx as jsx37, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1454
1644
|
function ReportFailureBanner({ failures }) {
|
|
1455
1645
|
const first = failures[0];
|
|
1456
1646
|
const jumpToFirst = useCallback2(() => {
|
|
@@ -1458,7 +1648,7 @@ function ReportFailureBanner({ failures }) {
|
|
|
1458
1648
|
scrollToScenarioId(first.scenarioId);
|
|
1459
1649
|
}, [first]);
|
|
1460
1650
|
if (failures.length === 0) return null;
|
|
1461
|
-
return /* @__PURE__ */
|
|
1651
|
+
return /* @__PURE__ */ jsxs22(
|
|
1462
1652
|
"div",
|
|
1463
1653
|
{
|
|
1464
1654
|
className: "es-failure-banner",
|
|
@@ -1466,20 +1656,20 @@ function ReportFailureBanner({ failures }) {
|
|
|
1466
1656
|
"aria-live": "polite",
|
|
1467
1657
|
"aria-label": "Failure summary",
|
|
1468
1658
|
children: [
|
|
1469
|
-
/* @__PURE__ */
|
|
1470
|
-
/* @__PURE__ */
|
|
1659
|
+
/* @__PURE__ */ jsxs22("span", { className: "es-failure-banner-text", children: [
|
|
1660
|
+
/* @__PURE__ */ jsx37("strong", { children: failures.length }),
|
|
1471
1661
|
" ",
|
|
1472
1662
|
"failure",
|
|
1473
1663
|
failures.length === 1 ? "" : "s"
|
|
1474
1664
|
] }),
|
|
1475
|
-
/* @__PURE__ */
|
|
1665
|
+
/* @__PURE__ */ jsx37(
|
|
1476
1666
|
"button",
|
|
1477
1667
|
{
|
|
1478
1668
|
type: "button",
|
|
1479
1669
|
className: "es-failure-banner-jump",
|
|
1480
1670
|
onClick: jumpToFirst,
|
|
1481
|
-
"aria-label": "
|
|
1482
|
-
children: "
|
|
1671
|
+
"aria-label": "View first failure",
|
|
1672
|
+
children: "View first failure \u2193"
|
|
1483
1673
|
}
|
|
1484
1674
|
)
|
|
1485
1675
|
]
|
|
@@ -1489,7 +1679,7 @@ function ReportFailureBanner({ failures }) {
|
|
|
1489
1679
|
|
|
1490
1680
|
// src/interactive/ReportFreshness.tsx
|
|
1491
1681
|
import { useEffect, useState as useState2 } from "react";
|
|
1492
|
-
import { Fragment as
|
|
1682
|
+
import { Fragment as Fragment11, jsx as jsx38, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1493
1683
|
function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
|
|
1494
1684
|
const [now, setNow] = useState2(nowMs);
|
|
1495
1685
|
useEffect(() => {
|
|
@@ -1497,7 +1687,7 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
|
|
|
1497
1687
|
}, [nowMs]);
|
|
1498
1688
|
if (lastRunMs === void 0 || now === void 0) return null;
|
|
1499
1689
|
const age = formatRelativeAge(lastRunMs, now);
|
|
1500
|
-
const runLink = ciUrl ? /* @__PURE__ */
|
|
1690
|
+
const runLink = ciUrl ? /* @__PURE__ */ jsx38(
|
|
1501
1691
|
"a",
|
|
1502
1692
|
{
|
|
1503
1693
|
href: ciUrl,
|
|
@@ -1508,22 +1698,22 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
|
|
|
1508
1698
|
}
|
|
1509
1699
|
) : null;
|
|
1510
1700
|
if (isReportStale(lastRunMs, staleAfterDays, now)) {
|
|
1511
|
-
return /* @__PURE__ */
|
|
1512
|
-
/* @__PURE__ */
|
|
1513
|
-
/* @__PURE__ */
|
|
1701
|
+
return /* @__PURE__ */ jsxs23("div", { className: "es-stale-banner", role: "status", "aria-label": "Report freshness", children: [
|
|
1702
|
+
/* @__PURE__ */ jsxs23("span", { children: [
|
|
1703
|
+
/* @__PURE__ */ jsxs23("strong", { children: [
|
|
1514
1704
|
"Last verified ",
|
|
1515
1705
|
age,
|
|
1516
1706
|
"."
|
|
1517
1707
|
] }),
|
|
1518
|
-
" This report may no longer match the current behavior
|
|
1708
|
+
" This report may no longer match the current behavior. Look for a newer run."
|
|
1519
1709
|
] }),
|
|
1520
1710
|
runLink
|
|
1521
1711
|
] });
|
|
1522
1712
|
}
|
|
1523
|
-
return /* @__PURE__ */
|
|
1713
|
+
return /* @__PURE__ */ jsxs23("p", { className: "es-freshness", "aria-label": "Report freshness", children: [
|
|
1524
1714
|
"Verified ",
|
|
1525
1715
|
age,
|
|
1526
|
-
runLink ? /* @__PURE__ */
|
|
1716
|
+
runLink ? /* @__PURE__ */ jsxs23(Fragment11, { children: [
|
|
1527
1717
|
" \xB7 ",
|
|
1528
1718
|
runLink
|
|
1529
1719
|
] }) : null
|
|
@@ -1531,15 +1721,15 @@ function ReportFreshness({ lastRunMs, ciUrl, staleAfterDays, nowMs }) {
|
|
|
1531
1721
|
}
|
|
1532
1722
|
|
|
1533
1723
|
// src/interactive/ReportLastRunDelta.tsx
|
|
1534
|
-
import { Fragment as
|
|
1724
|
+
import { Fragment as Fragment12, jsx as jsx39, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
1535
1725
|
var MAX_LINKS = 5;
|
|
1536
1726
|
function ScenarioLinks({ scenarios, tone }) {
|
|
1537
|
-
return /* @__PURE__ */
|
|
1538
|
-
scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */
|
|
1727
|
+
return /* @__PURE__ */ jsxs24(Fragment12, { children: [
|
|
1728
|
+
scenarios.slice(0, MAX_LINKS).map((s, i) => /* @__PURE__ */ jsxs24("span", { children: [
|
|
1539
1729
|
i > 0 ? ", " : "",
|
|
1540
|
-
/* @__PURE__ */
|
|
1730
|
+
/* @__PURE__ */ jsx39("a", { href: `#${s.id}`, className: cn("underline underline-offset-2 hover:no-underline", tone), children: s.title })
|
|
1541
1731
|
] }, s.id)),
|
|
1542
|
-
scenarios.length > MAX_LINKS ? /* @__PURE__ */
|
|
1732
|
+
scenarios.length > MAX_LINKS ? /* @__PURE__ */ jsxs24("span", { children: [
|
|
1543
1733
|
" and ",
|
|
1544
1734
|
scenarios.length - MAX_LINKS,
|
|
1545
1735
|
" more"
|
|
@@ -1556,23 +1746,23 @@ function ReportLastRunDelta({
|
|
|
1556
1746
|
if (!delta) return null;
|
|
1557
1747
|
const { newlyFailing, newlyPassing, added } = delta;
|
|
1558
1748
|
if (newlyFailing.length === 0 && newlyPassing.length === 0 && added.length === 0) {
|
|
1559
|
-
return /* @__PURE__ */
|
|
1749
|
+
return /* @__PURE__ */ jsx39("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: "Since last run: no behavior changes." });
|
|
1560
1750
|
}
|
|
1561
|
-
return /* @__PURE__ */
|
|
1562
|
-
/* @__PURE__ */
|
|
1563
|
-
newlyFailing.length > 0 ? /* @__PURE__ */
|
|
1751
|
+
return /* @__PURE__ */ jsxs24("p", { className: "text-xs text-muted-foreground", "data-testid": "es-last-run-delta", children: [
|
|
1752
|
+
/* @__PURE__ */ jsx39("span", { className: "font-medium text-foreground", children: "Since last run:" }),
|
|
1753
|
+
newlyFailing.length > 0 ? /* @__PURE__ */ jsxs24("span", { className: "ml-2 text-fail", children: [
|
|
1564
1754
|
newlyFailing.length,
|
|
1565
1755
|
" newly failing (",
|
|
1566
|
-
/* @__PURE__ */
|
|
1756
|
+
/* @__PURE__ */ jsx39(ScenarioLinks, { scenarios: newlyFailing, tone: "text-fail" }),
|
|
1567
1757
|
")"
|
|
1568
1758
|
] }) : null,
|
|
1569
|
-
newlyPassing.length > 0 ? /* @__PURE__ */
|
|
1759
|
+
newlyPassing.length > 0 ? /* @__PURE__ */ jsxs24("span", { className: "ml-2 text-pass", children: [
|
|
1570
1760
|
newlyPassing.length,
|
|
1571
1761
|
" fixed (",
|
|
1572
|
-
/* @__PURE__ */
|
|
1762
|
+
/* @__PURE__ */ jsx39(ScenarioLinks, { scenarios: newlyPassing, tone: "text-pass" }),
|
|
1573
1763
|
")"
|
|
1574
1764
|
] }) : null,
|
|
1575
|
-
added.length > 0 ? /* @__PURE__ */
|
|
1765
|
+
added.length > 0 ? /* @__PURE__ */ jsxs24("span", { className: "ml-2", children: [
|
|
1576
1766
|
added.length,
|
|
1577
1767
|
" new"
|
|
1578
1768
|
] }) : null
|
|
@@ -1581,14 +1771,78 @@ function ReportLastRunDelta({
|
|
|
1581
1771
|
|
|
1582
1772
|
// src/interactive/ReportShortcutsHelp.tsx
|
|
1583
1773
|
import { useEffect as useEffect2, useRef } from "react";
|
|
1584
|
-
|
|
1774
|
+
|
|
1775
|
+
// src/components/ui/button.tsx
|
|
1776
|
+
import "react";
|
|
1777
|
+
import { cva as cva4 } from "class-variance-authority";
|
|
1778
|
+
import { useRender as useRender2 } from "@base-ui/react/use-render";
|
|
1779
|
+
var buttonVariants = cva4(
|
|
1780
|
+
"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,background-color,border-color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
1781
|
+
{
|
|
1782
|
+
variants: {
|
|
1783
|
+
variant: {
|
|
1784
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
1785
|
+
destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
|
|
1786
|
+
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
1787
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
1788
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
1789
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
1790
|
+
},
|
|
1791
|
+
size: {
|
|
1792
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
1793
|
+
xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
1794
|
+
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
|
|
1795
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
1796
|
+
icon: "size-9",
|
|
1797
|
+
"icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
|
|
1798
|
+
"icon-sm": "size-8",
|
|
1799
|
+
"icon-lg": "size-10"
|
|
1800
|
+
}
|
|
1801
|
+
},
|
|
1802
|
+
defaultVariants: {
|
|
1803
|
+
variant: "default",
|
|
1804
|
+
size: "default"
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
);
|
|
1808
|
+
function Button({
|
|
1809
|
+
className,
|
|
1810
|
+
variant = "default",
|
|
1811
|
+
size = "default",
|
|
1812
|
+
render,
|
|
1813
|
+
...props
|
|
1814
|
+
}) {
|
|
1815
|
+
return useRender2({
|
|
1816
|
+
defaultTagName: "button",
|
|
1817
|
+
render,
|
|
1818
|
+
props: {
|
|
1819
|
+
"data-slot": "button",
|
|
1820
|
+
"data-variant": variant,
|
|
1821
|
+
"data-size": size,
|
|
1822
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
1823
|
+
...props
|
|
1824
|
+
}
|
|
1825
|
+
});
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
// src/interactive/ReportShortcutsHelp.tsx
|
|
1829
|
+
import { jsx as jsx40, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
1585
1830
|
var SHORTCUTS = [
|
|
1586
1831
|
{ keys: "/", description: "Focus search" },
|
|
1587
1832
|
{ keys: "f", description: "Jump to next failure" },
|
|
1588
|
-
{ keys: "Shift
|
|
1589
|
-
{ keys: "
|
|
1590
|
-
{ keys: "
|
|
1833
|
+
{ keys: "Shift F", description: "Jump to previous failure" },
|
|
1834
|
+
{ keys: "e", description: "Expand all" },
|
|
1835
|
+
{ keys: "c", description: "Collapse all" },
|
|
1836
|
+
{ keys: "?", description: "Toggle this help" },
|
|
1837
|
+
{ keys: "Esc", description: "Clear search / close" }
|
|
1591
1838
|
];
|
|
1839
|
+
function Keys({ keys }) {
|
|
1840
|
+
const parts = keys.split(" ");
|
|
1841
|
+
return /* @__PURE__ */ jsx40("span", { className: "inline-flex items-center gap-1", children: parts.map((k, i) => /* @__PURE__ */ jsxs25("span", { className: "inline-flex items-center gap-1", children: [
|
|
1842
|
+
i > 0 ? /* @__PURE__ */ jsx40("span", { className: "text-xs text-muted-foreground", children: "+" }) : null,
|
|
1843
|
+
/* @__PURE__ */ jsx40("kbd", { className: "inline-flex h-6 min-w-6 items-center justify-center rounded border border-border bg-muted px-1.5 font-mono text-xs font-medium text-muted-foreground", children: k })
|
|
1844
|
+
] }, k)) });
|
|
1845
|
+
}
|
|
1592
1846
|
function ReportShortcutsHelp({ open, onClose }) {
|
|
1593
1847
|
const dialogRef = useRef(null);
|
|
1594
1848
|
useEffect2(() => {
|
|
@@ -1597,20 +1851,20 @@ function ReportShortcutsHelp({ open, onClose }) {
|
|
|
1597
1851
|
if (open && !dialog.open) dialog.showModal();
|
|
1598
1852
|
else if (!open && dialog.open) dialog.close();
|
|
1599
1853
|
}, [open]);
|
|
1600
|
-
return /* @__PURE__ */
|
|
1854
|
+
return /* @__PURE__ */ jsxs25(
|
|
1601
1855
|
"dialog",
|
|
1602
1856
|
{
|
|
1603
1857
|
ref: dialogRef,
|
|
1604
|
-
className: "es-shortcuts-help",
|
|
1858
|
+
className: "es-shortcuts-help m-auto w-[min(90vw,28rem)] rounded-lg border border-border bg-card p-6 text-card-foreground shadow-lg [overscroll-behavior:contain] backdrop:bg-black/40",
|
|
1605
1859
|
"aria-label": "Keyboard shortcuts",
|
|
1606
1860
|
onClose,
|
|
1607
1861
|
children: [
|
|
1608
|
-
/* @__PURE__ */
|
|
1609
|
-
/* @__PURE__ */
|
|
1610
|
-
/* @__PURE__ */
|
|
1611
|
-
/* @__PURE__ */
|
|
1862
|
+
/* @__PURE__ */ jsx40("h2", { className: "text-base font-semibold", children: "Keyboard shortcuts" }),
|
|
1863
|
+
/* @__PURE__ */ jsx40("dl", { className: "mt-4 grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2.5", children: SHORTCUTS.map((s) => /* @__PURE__ */ jsxs25("div", { className: "col-span-2 grid grid-cols-subgrid items-center", children: [
|
|
1864
|
+
/* @__PURE__ */ jsx40("dt", { className: "justify-self-start", children: /* @__PURE__ */ jsx40(Keys, { keys: s.keys }) }),
|
|
1865
|
+
/* @__PURE__ */ jsx40("dd", { className: "text-sm text-muted-foreground", children: s.description })
|
|
1612
1866
|
] }, s.keys)) }),
|
|
1613
|
-
/* @__PURE__ */
|
|
1867
|
+
/* @__PURE__ */ jsx40("form", { method: "dialog", className: "mt-6", children: /* @__PURE__ */ jsx40(Button, { type: "submit", variant: "outline", size: "sm", children: "Close" }) })
|
|
1614
1868
|
]
|
|
1615
1869
|
}
|
|
1616
1870
|
);
|
|
@@ -1783,7 +2037,9 @@ function listFailures(report) {
|
|
|
1783
2037
|
}
|
|
1784
2038
|
|
|
1785
2039
|
// src/interactive/ReportFilters.tsx
|
|
1786
|
-
import {
|
|
2040
|
+
import { useState as useState3 } from "react";
|
|
2041
|
+
import { jsx as jsx41, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2042
|
+
var TAG_LIMIT = 12;
|
|
1787
2043
|
function ReportFilters({
|
|
1788
2044
|
statuses,
|
|
1789
2045
|
status,
|
|
@@ -1792,11 +2048,14 @@ function ReportFilters({
|
|
|
1792
2048
|
activeTags,
|
|
1793
2049
|
onToggleTag
|
|
1794
2050
|
}) {
|
|
2051
|
+
const [showAllTags, setShowAllTags] = useState3(false);
|
|
1795
2052
|
if (statuses.length === 0 && tags.length === 0) return null;
|
|
1796
|
-
|
|
1797
|
-
|
|
2053
|
+
const tagsCollapsed = tags.length > TAG_LIMIT && !showAllTags;
|
|
2054
|
+
const visibleTags = tagsCollapsed ? tags.slice(0, TAG_LIMIT) : tags;
|
|
2055
|
+
return /* @__PURE__ */ jsxs26("div", { className: "flex flex-col gap-2", children: [
|
|
2056
|
+
statuses.length > 0 ? /* @__PURE__ */ jsx41("div", { role: "group", "aria-label": "Filter by status", className: "flex flex-wrap gap-1", children: statuses.map((opt) => {
|
|
1798
2057
|
const active = status === opt.key;
|
|
1799
|
-
return /* @__PURE__ */
|
|
2058
|
+
return /* @__PURE__ */ jsxs26(
|
|
1800
2059
|
"button",
|
|
1801
2060
|
{
|
|
1802
2061
|
type: "button",
|
|
@@ -1808,40 +2067,61 @@ function ReportFilters({
|
|
|
1808
2067
|
),
|
|
1809
2068
|
children: [
|
|
1810
2069
|
opt.label,
|
|
1811
|
-
/* @__PURE__ */
|
|
2070
|
+
/* @__PURE__ */ jsx41("span", { className: "ml-1 font-mono opacity-70", children: opt.count })
|
|
1812
2071
|
]
|
|
1813
2072
|
},
|
|
1814
2073
|
opt.key
|
|
1815
2074
|
);
|
|
1816
2075
|
}) }) : null,
|
|
1817
|
-
tags.length > 0 ? /* @__PURE__ */
|
|
1818
|
-
|
|
1819
|
-
|
|
2076
|
+
tags.length > 0 ? /* @__PURE__ */ jsxs26("ul", { "aria-label": "Filter by tag", className: "flex flex-wrap items-center gap-1.5", children: [
|
|
2077
|
+
visibleTags.map((tag) => {
|
|
2078
|
+
const active = activeTags.includes(tag);
|
|
2079
|
+
return /* @__PURE__ */ jsx41("li", { children: /* @__PURE__ */ jsx41(
|
|
2080
|
+
"button",
|
|
2081
|
+
{
|
|
2082
|
+
type: "button",
|
|
2083
|
+
"aria-pressed": active,
|
|
2084
|
+
onClick: () => onToggleTag(tag),
|
|
2085
|
+
className: "cursor-pointer",
|
|
2086
|
+
children: /* @__PURE__ */ jsx41(
|
|
2087
|
+
Badge,
|
|
2088
|
+
{
|
|
2089
|
+
variant: "tag",
|
|
2090
|
+
className: cn(
|
|
2091
|
+
active && "border-primary bg-primary text-primary-foreground"
|
|
2092
|
+
),
|
|
2093
|
+
children: tag
|
|
2094
|
+
}
|
|
2095
|
+
)
|
|
2096
|
+
}
|
|
2097
|
+
) }, tag);
|
|
2098
|
+
}),
|
|
2099
|
+
tags.length > TAG_LIMIT ? /* @__PURE__ */ jsx41("li", { children: /* @__PURE__ */ jsx41(
|
|
1820
2100
|
"button",
|
|
1821
2101
|
{
|
|
1822
2102
|
type: "button",
|
|
1823
|
-
"aria-
|
|
1824
|
-
onClick: () =>
|
|
1825
|
-
className: "cursor-pointer",
|
|
1826
|
-
children:
|
|
2103
|
+
"aria-expanded": showAllTags,
|
|
2104
|
+
onClick: () => setShowAllTags((v) => !v),
|
|
2105
|
+
className: "cursor-pointer rounded-full px-2 py-0.5 text-xs font-medium text-muted-foreground hover:text-foreground",
|
|
2106
|
+
children: tagsCollapsed ? `+${tags.length - TAG_LIMIT} more` : "Show fewer"
|
|
1827
2107
|
}
|
|
1828
|
-
) }
|
|
1829
|
-
|
|
2108
|
+
) }) : null
|
|
2109
|
+
] }) : null
|
|
1830
2110
|
] });
|
|
1831
2111
|
}
|
|
1832
2112
|
|
|
1833
2113
|
// src/interactive/ReportToc.tsx
|
|
1834
|
-
import { useEffect as useEffect5, useMemo as useMemo2, useState as
|
|
1835
|
-
import { jsx as
|
|
2114
|
+
import { useEffect as useEffect5, useMemo as useMemo2, useState as useState4 } from "react";
|
|
2115
|
+
import { jsx as jsx42, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
1836
2116
|
var DOT = {
|
|
1837
|
-
passed: "
|
|
1838
|
-
failed: "
|
|
1839
|
-
skipped: "
|
|
1840
|
-
pending: "
|
|
2117
|
+
passed: "bg-pass",
|
|
2118
|
+
failed: "bg-fail",
|
|
2119
|
+
skipped: "bg-skip",
|
|
2120
|
+
pending: "bg-pend"
|
|
1841
2121
|
};
|
|
1842
|
-
function
|
|
2122
|
+
function TocContent({ onNavigate }) {
|
|
1843
2123
|
const report = useReport();
|
|
1844
|
-
const [activeId, setActiveId] =
|
|
2124
|
+
const [activeId, setActiveId] = useState4(null);
|
|
1845
2125
|
const scenarioIds = useMemo2(
|
|
1846
2126
|
() => report.features.flatMap((f) => f.scenarios.map((s) => s.id)),
|
|
1847
2127
|
[report]
|
|
@@ -1861,91 +2141,166 @@ function ReportToc() {
|
|
|
1861
2141
|
return () => observer.disconnect();
|
|
1862
2142
|
}, [scenarioIds]);
|
|
1863
2143
|
if (report.features.length === 0) return null;
|
|
1864
|
-
return /* @__PURE__ */
|
|
2144
|
+
return /* @__PURE__ */ jsx42("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ jsxs27("li", { children: [
|
|
2145
|
+
/* @__PURE__ */ jsx42("p", { className: "mb-1.5 font-semibold leading-snug text-foreground", children: feature.title }),
|
|
2146
|
+
/* @__PURE__ */ jsx42("ul", { className: "flex list-none flex-col gap-0.5 pl-0", children: feature.scenarios.map((scenario) => {
|
|
2147
|
+
const active = activeId === scenario.id;
|
|
2148
|
+
return /* @__PURE__ */ jsx42("li", { children: /* @__PURE__ */ jsxs27(
|
|
2149
|
+
"a",
|
|
2150
|
+
{
|
|
2151
|
+
href: `#${scenario.id}`,
|
|
2152
|
+
onClick: (e) => {
|
|
2153
|
+
e.preventDefault();
|
|
2154
|
+
scrollToScenarioId(scenario.id, { updateHash: true });
|
|
2155
|
+
onNavigate?.();
|
|
2156
|
+
},
|
|
2157
|
+
"aria-current": active ? "true" : void 0,
|
|
2158
|
+
className: cn(
|
|
2159
|
+
"flex items-start gap-2 border-l-2 py-1 pl-2.5 leading-snug hover:text-foreground",
|
|
2160
|
+
active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
|
|
2161
|
+
),
|
|
2162
|
+
children: [
|
|
2163
|
+
/* @__PURE__ */ jsx42(
|
|
2164
|
+
"span",
|
|
2165
|
+
{
|
|
2166
|
+
"aria-hidden": true,
|
|
2167
|
+
className: cn("mt-1 size-1.5 shrink-0 rounded-full", DOT[scenario.status])
|
|
2168
|
+
}
|
|
2169
|
+
),
|
|
2170
|
+
/* @__PURE__ */ jsx42("span", { className: "break-words", children: scenario.title })
|
|
2171
|
+
]
|
|
2172
|
+
}
|
|
2173
|
+
) }, scenario.id);
|
|
2174
|
+
}) })
|
|
2175
|
+
] }, feature.id)) });
|
|
2176
|
+
}
|
|
2177
|
+
function ReportToc() {
|
|
2178
|
+
const report = useReport();
|
|
2179
|
+
if (report.features.length === 0) return null;
|
|
2180
|
+
return /* @__PURE__ */ jsxs27(
|
|
1865
2181
|
"nav",
|
|
1866
2182
|
{
|
|
1867
2183
|
"aria-label": "Table of contents",
|
|
1868
|
-
className: "sticky top-4 hidden h-fit w-
|
|
1869
|
-
children:
|
|
1870
|
-
/* @__PURE__ */
|
|
1871
|
-
/* @__PURE__ */
|
|
1872
|
-
|
|
1873
|
-
return /* @__PURE__ */ jsx38("li", { children: /* @__PURE__ */ jsxs26(
|
|
1874
|
-
"a",
|
|
1875
|
-
{
|
|
1876
|
-
href: `#${scenario.id}`,
|
|
1877
|
-
onClick: (e) => {
|
|
1878
|
-
e.preventDefault();
|
|
1879
|
-
scrollToScenarioId(scenario.id, { updateHash: true });
|
|
1880
|
-
},
|
|
1881
|
-
"aria-current": active ? "true" : void 0,
|
|
1882
|
-
className: cn(
|
|
1883
|
-
"flex items-center gap-1.5 truncate border-l-2 py-0.5 pl-2 hover:text-foreground",
|
|
1884
|
-
active ? "border-primary font-medium text-foreground" : "border-border text-muted-foreground"
|
|
1885
|
-
),
|
|
1886
|
-
children: [
|
|
1887
|
-
/* @__PURE__ */ jsx38("span", { "aria-hidden": true, className: cn("shrink-0", DOT[scenario.status]), children: "\u2022" }),
|
|
1888
|
-
/* @__PURE__ */ jsx38("span", { className: "truncate", children: scenario.title })
|
|
1889
|
-
]
|
|
1890
|
-
}
|
|
1891
|
-
) }, scenario.id);
|
|
1892
|
-
}) })
|
|
1893
|
-
] }, feature.id)) })
|
|
2184
|
+
className: "sticky top-4 hidden h-fit w-64 shrink-0 self-start text-xs lg:block",
|
|
2185
|
+
children: [
|
|
2186
|
+
/* @__PURE__ */ jsx42("p", { className: "mb-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: "Contents" }),
|
|
2187
|
+
/* @__PURE__ */ jsx42(TocContent, {})
|
|
2188
|
+
]
|
|
1894
2189
|
}
|
|
1895
2190
|
);
|
|
1896
2191
|
}
|
|
1897
2192
|
|
|
2193
|
+
// src/interactive/ReportTocDrawer.tsx
|
|
2194
|
+
import { useState as useState5 } from "react";
|
|
2195
|
+
import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
|
|
2196
|
+
import { ListIcon, XIcon } from "lucide-react";
|
|
2197
|
+
import { jsx as jsx43, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
2198
|
+
function ReportTocDrawer() {
|
|
2199
|
+
const report = useReport();
|
|
2200
|
+
const [open, setOpen] = useState5(false);
|
|
2201
|
+
if (report.features.length === 0) return null;
|
|
2202
|
+
return /* @__PURE__ */ jsxs28(DialogPrimitive.Root, { open, onOpenChange: setOpen, children: [
|
|
2203
|
+
/* @__PURE__ */ jsxs28(DialogPrimitive.Trigger, { className: "flex cursor-pointer items-center gap-1 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground lg:hidden [&_svg]:size-3.5", children: [
|
|
2204
|
+
/* @__PURE__ */ jsx43(ListIcon, { "aria-hidden": true }),
|
|
2205
|
+
" Contents"
|
|
2206
|
+
] }),
|
|
2207
|
+
/* @__PURE__ */ jsxs28(DialogPrimitive.Portal, { children: [
|
|
2208
|
+
/* @__PURE__ */ jsx43(DialogPrimitive.Backdrop, { className: "fixed inset-0 z-50 bg-black/50 transition-opacity duration-200 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0" }),
|
|
2209
|
+
/* @__PURE__ */ jsxs28(DialogPrimitive.Popup, { className: "fixed inset-y-0 left-0 z-50 flex w-72 max-w-[80vw] flex-col gap-3 overflow-y-auto border-r border-border bg-background p-4 font-sans text-xs shadow-lg outline-none transition-transform duration-200 data-[ending-style]:-translate-x-full data-[starting-style]:-translate-x-full", children: [
|
|
2210
|
+
/* @__PURE__ */ jsxs28("div", { className: "flex items-center justify-between", children: [
|
|
2211
|
+
/* @__PURE__ */ jsx43(DialogPrimitive.Title, { className: "text-xs font-semibold tracking-wide text-muted-foreground uppercase", children: "Contents" }),
|
|
2212
|
+
/* @__PURE__ */ jsx43(
|
|
2213
|
+
DialogPrimitive.Close,
|
|
2214
|
+
{
|
|
2215
|
+
"aria-label": "Close contents",
|
|
2216
|
+
className: "cursor-pointer rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground [&_svg]:size-4",
|
|
2217
|
+
children: /* @__PURE__ */ jsx43(XIcon, { "aria-hidden": true })
|
|
2218
|
+
}
|
|
2219
|
+
)
|
|
2220
|
+
] }),
|
|
2221
|
+
/* @__PURE__ */ jsx43(TocContent, { onNavigate: () => setOpen(false) })
|
|
2222
|
+
] })
|
|
2223
|
+
] })
|
|
2224
|
+
] });
|
|
2225
|
+
}
|
|
2226
|
+
|
|
1898
2227
|
// src/interactive/use-theme.ts
|
|
1899
|
-
import { useCallback as useCallback3, useEffect as useEffect6, useState as
|
|
2228
|
+
import { useCallback as useCallback3, useEffect as useEffect6, useState as useState6 } from "react";
|
|
1900
2229
|
var KEY = "es-theme";
|
|
1901
|
-
function
|
|
2230
|
+
function systemDark() {
|
|
2231
|
+
return typeof matchMedia !== "undefined" && matchMedia("(prefers-color-scheme: dark)").matches;
|
|
2232
|
+
}
|
|
2233
|
+
function resolve(pref) {
|
|
2234
|
+
return pref === "system" ? systemDark() ? "dark" : "light" : pref;
|
|
2235
|
+
}
|
|
2236
|
+
function applyTheme(pref) {
|
|
1902
2237
|
if (typeof document !== "undefined") {
|
|
1903
|
-
document.documentElement.setAttribute("data-theme",
|
|
2238
|
+
document.documentElement.setAttribute("data-theme", resolve(pref));
|
|
1904
2239
|
}
|
|
1905
2240
|
}
|
|
1906
|
-
function
|
|
2241
|
+
function initialPref() {
|
|
1907
2242
|
if (typeof localStorage !== "undefined") {
|
|
1908
2243
|
const stored = localStorage.getItem(KEY);
|
|
1909
|
-
if (stored === "light" || stored === "dark") return stored;
|
|
1910
|
-
}
|
|
1911
|
-
if (typeof document !== "undefined" && document.documentElement.getAttribute("data-theme") === "dark") {
|
|
1912
|
-
return "dark";
|
|
2244
|
+
if (stored === "light" || stored === "dark" || stored === "system") return stored;
|
|
1913
2245
|
}
|
|
1914
|
-
|
|
1915
|
-
return "dark";
|
|
1916
|
-
}
|
|
1917
|
-
return "light";
|
|
2246
|
+
return "system";
|
|
1918
2247
|
}
|
|
1919
2248
|
function useTheme() {
|
|
1920
|
-
const [
|
|
2249
|
+
const [pref, setPrefState] = useState6("system");
|
|
1921
2250
|
useEffect6(() => {
|
|
1922
|
-
const
|
|
1923
|
-
|
|
1924
|
-
applyTheme(
|
|
2251
|
+
const p = initialPref();
|
|
2252
|
+
setPrefState(p);
|
|
2253
|
+
applyTheme(p);
|
|
1925
2254
|
}, []);
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
2255
|
+
useEffect6(() => {
|
|
2256
|
+
if (typeof matchMedia === "undefined") return;
|
|
2257
|
+
const mq = matchMedia("(prefers-color-scheme: dark)");
|
|
2258
|
+
const onChange = () => {
|
|
2259
|
+
if (pref === "system") applyTheme("system");
|
|
2260
|
+
};
|
|
2261
|
+
mq.addEventListener("change", onChange);
|
|
2262
|
+
return () => mq.removeEventListener("change", onChange);
|
|
2263
|
+
}, [pref]);
|
|
2264
|
+
const setPref = useCallback3((p) => {
|
|
2265
|
+
setPrefState(p);
|
|
2266
|
+
applyTheme(p);
|
|
2267
|
+
try {
|
|
2268
|
+
localStorage.setItem(KEY, p);
|
|
2269
|
+
} catch {
|
|
2270
|
+
}
|
|
1936
2271
|
}, []);
|
|
1937
|
-
return {
|
|
2272
|
+
return { pref, setPref };
|
|
1938
2273
|
}
|
|
1939
2274
|
|
|
1940
2275
|
// src/interactive/ReportInteractive.tsx
|
|
1941
|
-
import { jsx as
|
|
2276
|
+
import { Fragment as Fragment13, jsx as jsx44, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
1942
2277
|
function ReportInteractive(props) {
|
|
1943
2278
|
const { report, className, title, dataTheme } = props;
|
|
1944
2279
|
const result = unwrapReport(report);
|
|
1945
2280
|
if (!result.ok) {
|
|
1946
|
-
return /* @__PURE__ */
|
|
2281
|
+
return /* @__PURE__ */ jsx44(ReportErrorShell, { error: result.error, className, title, dataTheme });
|
|
1947
2282
|
}
|
|
1948
|
-
return /* @__PURE__ */
|
|
2283
|
+
return /* @__PURE__ */ jsx44(ReportInteractiveView, { ...props, report: result.data });
|
|
2284
|
+
}
|
|
2285
|
+
var THEME_OPTIONS = [
|
|
2286
|
+
{ value: "light", label: "Light", Icon: Sun },
|
|
2287
|
+
{ value: "system", label: "System", Icon: Monitor },
|
|
2288
|
+
{ value: "dark", label: "Dark", Icon: Moon }
|
|
2289
|
+
];
|
|
2290
|
+
function ThemeSegment({ pref, onPref }) {
|
|
2291
|
+
return /* @__PURE__ */ jsx44(
|
|
2292
|
+
ToggleGroup,
|
|
2293
|
+
{
|
|
2294
|
+
size: "sm",
|
|
2295
|
+
value: [pref],
|
|
2296
|
+
onValueChange: (v) => {
|
|
2297
|
+
const next = v[0];
|
|
2298
|
+
if (next) onPref(next);
|
|
2299
|
+
},
|
|
2300
|
+
"aria-label": "Color theme",
|
|
2301
|
+
children: THEME_OPTIONS.map(({ value, label, Icon }) => /* @__PURE__ */ jsx44(ToggleGroupItem, { value, "aria-label": label, title: label, className: "px-2", children: /* @__PURE__ */ jsx44(Icon, {}) }, value))
|
|
2302
|
+
}
|
|
2303
|
+
);
|
|
1949
2304
|
}
|
|
1950
2305
|
function ReportInteractiveView({
|
|
1951
2306
|
report,
|
|
@@ -1955,16 +2310,19 @@ function ReportInteractiveView({
|
|
|
1955
2310
|
title,
|
|
1956
2311
|
dataTheme,
|
|
1957
2312
|
hideHeader = false,
|
|
2313
|
+
hideToc = false,
|
|
1958
2314
|
staleAfterDays = 7,
|
|
1959
2315
|
scenarioHistory
|
|
1960
2316
|
}) {
|
|
1961
|
-
const [query, setQuery] =
|
|
1962
|
-
const [statusFilter, setStatusFilter] =
|
|
1963
|
-
const [activeTags, setActiveTags] =
|
|
1964
|
-
const [detail, setDetail] =
|
|
1965
|
-
const [helpOpen, setHelpOpen] =
|
|
1966
|
-
const {
|
|
2317
|
+
const [query, setQuery] = useState7("");
|
|
2318
|
+
const [statusFilter, setStatusFilter] = useState7("all");
|
|
2319
|
+
const [activeTags, setActiveTags] = useState7([]);
|
|
2320
|
+
const [detail, setDetail] = useState7("full");
|
|
2321
|
+
const [helpOpen, setHelpOpen] = useState7(false);
|
|
2322
|
+
const { pref: themePref, setPref: setThemePref } = useTheme();
|
|
1967
2323
|
const searchRef = useRef3(null);
|
|
2324
|
+
const expandId = useId2();
|
|
2325
|
+
const docsId = useId2();
|
|
1968
2326
|
const deferredQuery = useDeferredValue(query);
|
|
1969
2327
|
const failures = useMemo3(() => listFailures(report), [report]);
|
|
1970
2328
|
const filtered = useMemo3(
|
|
@@ -1988,7 +2346,7 @@ function ReportInteractiveView({
|
|
|
1988
2346
|
(tag) => setActiveTags((prev) => prev.includes(tag) ? prev.filter((t) => t !== tag) : [...prev, tag]),
|
|
1989
2347
|
[]
|
|
1990
2348
|
);
|
|
1991
|
-
const [toast, setToast] =
|
|
2349
|
+
const [toast, setToast] = useState7(null);
|
|
1992
2350
|
const toastTimer = useRef3(null);
|
|
1993
2351
|
const showToast = useCallback4((msg) => {
|
|
1994
2352
|
setToast(msg);
|
|
@@ -2012,12 +2370,21 @@ function ReportInteractiveView({
|
|
|
2012
2370
|
[copy]
|
|
2013
2371
|
);
|
|
2014
2372
|
const failureIndexRef = useRef3(0);
|
|
2015
|
-
const collapse = useCollapseState();
|
|
2016
2373
|
const allCollapsibleIds = useMemo3(
|
|
2017
2374
|
() => report.features.flatMap((f) => [f.id, ...f.scenarios.map((s) => s.id)]),
|
|
2018
2375
|
[report]
|
|
2019
2376
|
);
|
|
2377
|
+
const collapse = useCollapseState();
|
|
2020
2378
|
const collapseAll = useCallback4(() => collapse.collapseAll(allCollapsibleIds), [collapse, allCollapsibleIds]);
|
|
2379
|
+
const [expandedAll, setExpandedAll] = useState7(true);
|
|
2380
|
+
const setExpanded = useCallback4(
|
|
2381
|
+
(expanded) => {
|
|
2382
|
+
if (expanded) collapse.expandAll();
|
|
2383
|
+
else collapseAll();
|
|
2384
|
+
setExpandedAll(expanded);
|
|
2385
|
+
},
|
|
2386
|
+
[collapse, collapseAll]
|
|
2387
|
+
);
|
|
2021
2388
|
const focusSearch = useCallback4(() => {
|
|
2022
2389
|
searchRef.current?.focus();
|
|
2023
2390
|
}, []);
|
|
@@ -2043,20 +2410,21 @@ function ReportInteractiveView({
|
|
|
2043
2410
|
onPrevFailure: () => stepFailure(-1),
|
|
2044
2411
|
onToggleHelp: toggleHelp,
|
|
2045
2412
|
onEscape: escape,
|
|
2046
|
-
onExpandAll:
|
|
2047
|
-
onCollapseAll:
|
|
2413
|
+
onExpandAll: () => setExpanded(true),
|
|
2414
|
+
onCollapseAll: () => setExpanded(false)
|
|
2048
2415
|
});
|
|
2049
2416
|
useDeepLinkScroll();
|
|
2050
2417
|
const hasContent = filtered.features.length > 0;
|
|
2051
2418
|
const totalScenarios = report.summary.total;
|
|
2052
2419
|
const matchedScenarios = filtered.summary.total;
|
|
2053
|
-
|
|
2420
|
+
const showToc = !hideToc && totalScenarios > 3;
|
|
2421
|
+
return /* @__PURE__ */ jsx44(CollapseProvider, { value: collapse.api, children: /* @__PURE__ */ jsx44(ScenarioActionsProvider, { value: scenarioActions, children: /* @__PURE__ */ jsx44(ScenarioHistoryProvider, { value: scenarioHistory ?? null, children: /* @__PURE__ */ jsx44(
|
|
2054
2422
|
ReportRoot,
|
|
2055
2423
|
{
|
|
2056
2424
|
report: filtered,
|
|
2057
2425
|
customRenderers,
|
|
2058
2426
|
renderers,
|
|
2059
|
-
children: /* @__PURE__ */
|
|
2427
|
+
children: /* @__PURE__ */ jsxs29(
|
|
2060
2428
|
"main",
|
|
2061
2429
|
{
|
|
2062
2430
|
className: cn("es-report", "es-report-interactive", className),
|
|
@@ -2065,9 +2433,38 @@ function ReportInteractiveView({
|
|
|
2065
2433
|
"data-theme": dataTheme,
|
|
2066
2434
|
"data-detail-level": detail,
|
|
2067
2435
|
children: [
|
|
2068
|
-
/* @__PURE__ */
|
|
2069
|
-
|
|
2070
|
-
|
|
2436
|
+
/* @__PURE__ */ jsxs29("header", { className: "es-report-header", children: [
|
|
2437
|
+
/* @__PURE__ */ jsxs29(
|
|
2438
|
+
"div",
|
|
2439
|
+
{
|
|
2440
|
+
"data-es-navbar": true,
|
|
2441
|
+
className: cn(
|
|
2442
|
+
"flex flex-wrap items-center gap-4",
|
|
2443
|
+
hideHeader ? "justify-end" : "justify-between"
|
|
2444
|
+
),
|
|
2445
|
+
children: [
|
|
2446
|
+
hideHeader ? null : /* @__PURE__ */ jsx44("h1", { className: "min-w-0", children: title ?? "Story Report" }),
|
|
2447
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-2", children: [
|
|
2448
|
+
/* @__PURE__ */ jsx44(
|
|
2449
|
+
ReportSearch,
|
|
2450
|
+
{
|
|
2451
|
+
ref: searchRef,
|
|
2452
|
+
value: query,
|
|
2453
|
+
onChange: setQuery,
|
|
2454
|
+
matchedCount: matchedScenarios,
|
|
2455
|
+
totalCount: totalScenarios
|
|
2456
|
+
}
|
|
2457
|
+
),
|
|
2458
|
+
/* @__PURE__ */ jsx44(ThemeSegment, { pref: themePref, onPref: setThemePref })
|
|
2459
|
+
] })
|
|
2460
|
+
]
|
|
2461
|
+
}
|
|
2462
|
+
),
|
|
2463
|
+
hideHeader ? null : /* @__PURE__ */ jsxs29(Fragment13, { children: [
|
|
2464
|
+
/* @__PURE__ */ jsx44(ReportSummary, {}),
|
|
2465
|
+
/* @__PURE__ */ jsx44(ReportMeta, {})
|
|
2466
|
+
] }),
|
|
2467
|
+
/* @__PURE__ */ jsx44(
|
|
2071
2468
|
ReportFreshness,
|
|
2072
2469
|
{
|
|
2073
2470
|
lastRunMs: reportLastRunMs(report),
|
|
@@ -2075,62 +2472,8 @@ function ReportInteractiveView({
|
|
|
2075
2472
|
staleAfterDays
|
|
2076
2473
|
}
|
|
2077
2474
|
),
|
|
2078
|
-
/* @__PURE__ */
|
|
2079
|
-
/* @__PURE__ */
|
|
2080
|
-
/* @__PURE__ */ jsx39(
|
|
2081
|
-
ReportSearch,
|
|
2082
|
-
{
|
|
2083
|
-
ref: searchRef,
|
|
2084
|
-
value: query,
|
|
2085
|
-
onChange: setQuery,
|
|
2086
|
-
matchedCount: matchedScenarios,
|
|
2087
|
-
totalCount: totalScenarios
|
|
2088
|
-
}
|
|
2089
|
-
),
|
|
2090
|
-
/* @__PURE__ */ jsxs27("div", { className: "flex shrink-0 gap-1", children: [
|
|
2091
|
-
/* @__PURE__ */ jsx39(
|
|
2092
|
-
"button",
|
|
2093
|
-
{
|
|
2094
|
-
type: "button",
|
|
2095
|
-
onClick: collapse.expandAll,
|
|
2096
|
-
"aria-keyshortcuts": "e",
|
|
2097
|
-
className: "cursor-pointer rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
2098
|
-
children: "Expand all"
|
|
2099
|
-
}
|
|
2100
|
-
),
|
|
2101
|
-
/* @__PURE__ */ jsx39(
|
|
2102
|
-
"button",
|
|
2103
|
-
{
|
|
2104
|
-
type: "button",
|
|
2105
|
-
onClick: collapseAll,
|
|
2106
|
-
"aria-keyshortcuts": "c",
|
|
2107
|
-
className: "cursor-pointer rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
2108
|
-
children: "Collapse all"
|
|
2109
|
-
}
|
|
2110
|
-
),
|
|
2111
|
-
/* @__PURE__ */ jsx39(
|
|
2112
|
-
"button",
|
|
2113
|
-
{
|
|
2114
|
-
type: "button",
|
|
2115
|
-
onClick: () => setDetail((d) => d === "full" ? "minimal" : "full"),
|
|
2116
|
-
"aria-pressed": detail === "minimal",
|
|
2117
|
-
className: "cursor-pointer rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
2118
|
-
children: detail === "full" ? "Hide docs" : "Show docs"
|
|
2119
|
-
}
|
|
2120
|
-
),
|
|
2121
|
-
/* @__PURE__ */ jsx39(
|
|
2122
|
-
"button",
|
|
2123
|
-
{
|
|
2124
|
-
type: "button",
|
|
2125
|
-
onClick: toggleTheme,
|
|
2126
|
-
"aria-label": `Switch to ${theme === "dark" ? "light" : "dark"} theme`,
|
|
2127
|
-
className: "cursor-pointer rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
2128
|
-
children: theme === "dark" ? "\u2600 Light" : "\u263E Dark"
|
|
2129
|
-
}
|
|
2130
|
-
)
|
|
2131
|
-
] })
|
|
2132
|
-
] }),
|
|
2133
|
-
/* @__PURE__ */ jsx39(
|
|
2475
|
+
/* @__PURE__ */ jsx44(ReportLastRunDelta, { history: scenarioHistory, report }),
|
|
2476
|
+
/* @__PURE__ */ jsx44(
|
|
2134
2477
|
ReportFilters,
|
|
2135
2478
|
{
|
|
2136
2479
|
statuses: statusOptions,
|
|
@@ -2140,26 +2483,44 @@ function ReportInteractiveView({
|
|
|
2140
2483
|
activeTags,
|
|
2141
2484
|
onToggleTag: toggleTag
|
|
2142
2485
|
}
|
|
2143
|
-
)
|
|
2486
|
+
),
|
|
2487
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-2 border-t border-border/60 pt-3", children: [
|
|
2488
|
+
showToc ? /* @__PURE__ */ jsx44(ReportTocDrawer, {}) : null,
|
|
2489
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-2", children: [
|
|
2490
|
+
/* @__PURE__ */ jsx44(Switch, { id: expandId, checked: expandedAll, onCheckedChange: setExpanded }),
|
|
2491
|
+
/* @__PURE__ */ jsx44("label", { htmlFor: expandId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Expand all" })
|
|
2492
|
+
] }),
|
|
2493
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-2", children: [
|
|
2494
|
+
/* @__PURE__ */ jsx44(
|
|
2495
|
+
Switch,
|
|
2496
|
+
{
|
|
2497
|
+
id: docsId,
|
|
2498
|
+
checked: detail === "full",
|
|
2499
|
+
onCheckedChange: (checked) => setDetail(checked ? "full" : "minimal")
|
|
2500
|
+
}
|
|
2501
|
+
),
|
|
2502
|
+
/* @__PURE__ */ jsx44("label", { htmlFor: docsId, className: "cursor-pointer text-sm text-muted-foreground select-none", children: "Show documentation" })
|
|
2503
|
+
] })
|
|
2504
|
+
] })
|
|
2144
2505
|
] }),
|
|
2145
|
-
/* @__PURE__ */
|
|
2146
|
-
hasContent ? /* @__PURE__ */
|
|
2147
|
-
/* @__PURE__ */
|
|
2148
|
-
/* @__PURE__ */
|
|
2149
|
-
] }) : /* @__PURE__ */
|
|
2150
|
-
/* @__PURE__ */
|
|
2506
|
+
/* @__PURE__ */ jsx44(ReportFailureBanner, { failures }),
|
|
2507
|
+
hasContent ? showToc ? /* @__PURE__ */ jsxs29("div", { className: "flex gap-6", children: [
|
|
2508
|
+
/* @__PURE__ */ jsx44(ReportToc, {}),
|
|
2509
|
+
/* @__PURE__ */ jsx44("div", { className: "flex min-w-0 flex-1 flex-col gap-4", children: /* @__PURE__ */ jsx44(ReportFeatureList, {}) })
|
|
2510
|
+
] }) : /* @__PURE__ */ jsx44("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ jsx44(ReportFeatureList, {}) }) : /* @__PURE__ */ jsx44(ReportEmpty, { message: query ? "No scenarios match the search." : void 0 }),
|
|
2511
|
+
/* @__PURE__ */ jsx44(
|
|
2151
2512
|
"button",
|
|
2152
2513
|
{
|
|
2153
2514
|
type: "button",
|
|
2154
|
-
className: "es-shortcuts-trigger",
|
|
2515
|
+
className: "es-shortcuts-trigger fixed right-4 bottom-4 z-40 flex size-9 items-center justify-center rounded-full border border-border bg-card text-muted-foreground shadow-sm transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50",
|
|
2155
2516
|
"aria-label": "Keyboard shortcuts",
|
|
2156
|
-
"aria-keyshortcuts": "
|
|
2517
|
+
"aria-keyshortcuts": "?",
|
|
2157
2518
|
onClick: toggleHelp,
|
|
2158
2519
|
children: "?"
|
|
2159
2520
|
}
|
|
2160
2521
|
),
|
|
2161
|
-
/* @__PURE__ */
|
|
2162
|
-
toast ? /* @__PURE__ */
|
|
2522
|
+
/* @__PURE__ */ jsx44(ReportShortcutsHelp, { open: helpOpen, onClose: () => setHelpOpen(false) }),
|
|
2523
|
+
toast ? /* @__PURE__ */ jsx44(
|
|
2163
2524
|
"div",
|
|
2164
2525
|
{
|
|
2165
2526
|
role: "status",
|
|
@@ -2179,11 +2540,11 @@ function ReportInteractiveView({
|
|
|
2179
2540
|
import { useMemo as useMemo4 } from "react";
|
|
2180
2541
|
|
|
2181
2542
|
// src/interactive/island-renderers.tsx
|
|
2182
|
-
import { createElement, useEffect as useEffect8, useState as
|
|
2543
|
+
import { createElement, useEffect as useEffect8, useState as useState9 } from "react";
|
|
2183
2544
|
|
|
2184
2545
|
// src/components/doc/MermaidView.tsx
|
|
2185
|
-
import { useEffect as useEffect7, useRef as useRef4, useState as
|
|
2186
|
-
import { jsx as
|
|
2546
|
+
import { useEffect as useEffect7, useRef as useRef4, useState as useState8 } from "react";
|
|
2547
|
+
import { jsx as jsx45, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
2187
2548
|
function prefersDark(el) {
|
|
2188
2549
|
const themed = el?.closest("[data-theme]");
|
|
2189
2550
|
if (themed?.dataset.theme === "dark") return true;
|
|
@@ -2192,7 +2553,7 @@ function prefersDark(el) {
|
|
|
2192
2553
|
}
|
|
2193
2554
|
function MermaidView({ entry, load }) {
|
|
2194
2555
|
const ref = useRef4(null);
|
|
2195
|
-
const [svg, setSvg] =
|
|
2556
|
+
const [svg, setSvg] = useState8(null);
|
|
2196
2557
|
useEffect7(() => {
|
|
2197
2558
|
let cancelled = false;
|
|
2198
2559
|
const draw = async () => {
|
|
@@ -2220,9 +2581,9 @@ function MermaidView({ entry, load }) {
|
|
|
2220
2581
|
};
|
|
2221
2582
|
}, [entry.code, load]);
|
|
2222
2583
|
if (svg) {
|
|
2223
|
-
return /* @__PURE__ */
|
|
2224
|
-
entry.title ? /* @__PURE__ */
|
|
2225
|
-
/* @__PURE__ */
|
|
2584
|
+
return /* @__PURE__ */ jsxs30("figure", { ref, className: "my-3", children: [
|
|
2585
|
+
entry.title ? /* @__PURE__ */ jsx45("figcaption", { className: "mb-1.5 text-xs font-medium text-muted-foreground", children: entry.title }) : null,
|
|
2586
|
+
/* @__PURE__ */ jsx45(
|
|
2226
2587
|
"div",
|
|
2227
2588
|
{
|
|
2228
2589
|
role: "img",
|
|
@@ -2233,11 +2594,11 @@ function MermaidView({ entry, load }) {
|
|
|
2233
2594
|
)
|
|
2234
2595
|
] });
|
|
2235
2596
|
}
|
|
2236
|
-
return /* @__PURE__ */
|
|
2597
|
+
return /* @__PURE__ */ jsx45(MermaidSource, { entry, ref });
|
|
2237
2598
|
}
|
|
2238
2599
|
|
|
2239
2600
|
// src/interactive/island-renderers.tsx
|
|
2240
|
-
import { jsx as
|
|
2601
|
+
import { jsx as jsx46 } from "react/jsx-runtime";
|
|
2241
2602
|
var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
|
|
2242
2603
|
var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
|
|
2243
2604
|
function cachedCdnModule(url) {
|
|
@@ -2254,7 +2615,7 @@ function cachedCdnModule(url) {
|
|
|
2254
2615
|
var loadMermaidFromCdn = cachedCdnModule(MERMAID_ESM);
|
|
2255
2616
|
var loadHljs = cachedCdnModule(HLJS_ESM);
|
|
2256
2617
|
function CdnHighlightedCode({ entry }) {
|
|
2257
|
-
const [html, setHtml] =
|
|
2618
|
+
const [html, setHtml] = useState9(null);
|
|
2258
2619
|
useEffect8(() => {
|
|
2259
2620
|
let cancelled = false;
|
|
2260
2621
|
void (async () => {
|
|
@@ -2271,13 +2632,13 @@ function CdnHighlightedCode({ entry }) {
|
|
|
2271
2632
|
cancelled = true;
|
|
2272
2633
|
};
|
|
2273
2634
|
}, [entry.content, entry.lang]);
|
|
2274
|
-
return /* @__PURE__ */
|
|
2635
|
+
return /* @__PURE__ */ jsx46(CodeFigure, { label: entry.label, children: html !== null ? /* @__PURE__ */ jsx46(
|
|
2275
2636
|
"code",
|
|
2276
2637
|
{
|
|
2277
2638
|
className: cn("hljs font-mono text-xs", entry.lang && `language-${entry.lang}`),
|
|
2278
2639
|
dangerouslySetInnerHTML: { __html: html }
|
|
2279
2640
|
}
|
|
2280
|
-
) : /* @__PURE__ */
|
|
2641
|
+
) : /* @__PURE__ */ jsx46("code", { className: cn("font-mono text-xs", entry.lang && `language-${entry.lang}`), children: entry.content }) });
|
|
2281
2642
|
}
|
|
2282
2643
|
function buildIslandRenderers(opts) {
|
|
2283
2644
|
const renderers = {};
|
|
@@ -2291,12 +2652,13 @@ function buildIslandRenderers(opts) {
|
|
|
2291
2652
|
}
|
|
2292
2653
|
|
|
2293
2654
|
// src/interactive/ReportInteractiveIsland.tsx
|
|
2294
|
-
import { jsx as
|
|
2655
|
+
import { jsx as jsx47 } from "react/jsx-runtime";
|
|
2295
2656
|
function ReportInteractiveIsland({
|
|
2296
2657
|
report,
|
|
2297
2658
|
title,
|
|
2298
2659
|
className,
|
|
2299
2660
|
hideHeader,
|
|
2661
|
+
hideToc,
|
|
2300
2662
|
syntaxHighlighting = true,
|
|
2301
2663
|
mermaid = true
|
|
2302
2664
|
}) {
|
|
@@ -2304,13 +2666,14 @@ function ReportInteractiveIsland({
|
|
|
2304
2666
|
() => buildIslandRenderers({ syntaxHighlighting, mermaid }),
|
|
2305
2667
|
[syntaxHighlighting, mermaid]
|
|
2306
2668
|
);
|
|
2307
|
-
return /* @__PURE__ */
|
|
2669
|
+
return /* @__PURE__ */ jsx47(
|
|
2308
2670
|
ReportInteractive,
|
|
2309
2671
|
{
|
|
2310
2672
|
report,
|
|
2311
2673
|
title,
|
|
2312
2674
|
className,
|
|
2313
2675
|
hideHeader,
|
|
2676
|
+
hideToc,
|
|
2314
2677
|
renderers
|
|
2315
2678
|
}
|
|
2316
2679
|
);
|