executable-stories-react 0.14.0 → 0.16.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 +142 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +131 -52
- package/dist/index.js.map +1 -1
- package/dist/interactive.cjs +518 -361
- package/dist/interactive.cjs.map +1 -1
- package/dist/interactive.d.cts +4 -0
- package/dist/interactive.d.ts +4 -0
- package/dist/interactive.js +471 -314
- package/dist/interactive.js.map +1 -1
- package/dist/report-island.global.js +53 -51
- package/dist/ssr.cjs +110 -26
- package/dist/ssr.cjs.map +1 -1
- package/dist/ssr.d.cts +4 -0
- package/dist/ssr.d.ts +4 -0
- package/dist/ssr.js +97 -13
- package/dist/ssr.js.map +1 -1
- package/dist/tailwind.css +1 -1
- package/package.json +3 -3
package/dist/ssr.d.cts
CHANGED
|
@@ -87,6 +87,10 @@ interface RenderReportToHtmlOptions {
|
|
|
87
87
|
* static (non-island) output.
|
|
88
88
|
*/
|
|
89
89
|
scenarioHistory?: ScenarioHistoryMap;
|
|
90
|
+
/** Show the Share button in the interactive header (default false). `--html-share` turns it on. */
|
|
91
|
+
share?: boolean;
|
|
92
|
+
/** Command the share dialog hands over, e.g. `npx executable-stories share reports/`. */
|
|
93
|
+
shareCommand?: string;
|
|
90
94
|
}
|
|
91
95
|
/**
|
|
92
96
|
* Render a StoryReport (or a parse Result) to a complete, self-contained HTML
|
package/dist/ssr.d.ts
CHANGED
|
@@ -87,6 +87,10 @@ interface RenderReportToHtmlOptions {
|
|
|
87
87
|
* static (non-island) output.
|
|
88
88
|
*/
|
|
89
89
|
scenarioHistory?: ScenarioHistoryMap;
|
|
90
|
+
/** Show the Share button in the interactive header (default false). `--html-share` turns it on. */
|
|
91
|
+
share?: boolean;
|
|
92
|
+
/** Command the share dialog hands over, e.g. `npx executable-stories share reports/`. */
|
|
93
|
+
shareCommand?: string;
|
|
90
94
|
}
|
|
91
95
|
/**
|
|
92
96
|
* Render a StoryReport (or a parse Result) to a complete, self-contained HTML
|
package/dist/ssr.js
CHANGED
|
@@ -523,12 +523,29 @@ function DocVideo({ entry }) {
|
|
|
523
523
|
}
|
|
524
524
|
|
|
525
525
|
// src/components/doc/DocHtml.tsx
|
|
526
|
+
import { useEffect, useRef, useState } from "react";
|
|
526
527
|
import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
528
|
+
var MEASURE_SCRIPT = "<script>(function(){if(parent===window)return;var last=0,queued=false;var measure=function(){var kids=document.body.children,h=0;for(var i=0;i<kids.length;i++)h=Math.max(h,kids[i].getBoundingClientRect().bottom);return Math.ceil(h||document.body.getBoundingClientRect().bottom)};var post=function(){queued=false;var h=measure();if(h===last)return;last=h;parent.postMessage({__esHtmlHeight:h},'*')};var schedule=function(){if(queued)return;queued=true;requestAnimationFrame(post)};var start=function(){new ResizeObserver(schedule).observe(document.body);new MutationObserver(schedule).observe(document.body,{subtree:true,childList:true,attributes:true,characterData:true});addEventListener('load',schedule);post()};if(document.body)start();else addEventListener('DOMContentLoaded',start)})();</script>";
|
|
529
|
+
var MAX_MEASURED_HEIGHT = 5e3;
|
|
527
530
|
function DocHtml({ entry }) {
|
|
528
|
-
const
|
|
531
|
+
const declaredHeight = typeof entry.height === "number" ? `${entry.height}px` : entry.height ?? "400px";
|
|
529
532
|
const title = entry.title ?? "Embedded HTML";
|
|
530
533
|
const safeHref = safeUrl(entry.url ?? entry.path);
|
|
531
|
-
const
|
|
534
|
+
const frameRef = useRef(null);
|
|
535
|
+
const [measuredHeight, setMeasuredHeight] = useState();
|
|
536
|
+
useEffect(() => {
|
|
537
|
+
if (entry.content === void 0) return;
|
|
538
|
+
const onMessage = (event) => {
|
|
539
|
+
if (event.source !== frameRef.current?.contentWindow) return;
|
|
540
|
+
const reported = event.data?.__esHtmlHeight;
|
|
541
|
+
if (typeof reported !== "number" || !Number.isFinite(reported) || reported <= 0) return;
|
|
542
|
+
setMeasuredHeight(Math.min(reported, MAX_MEASURED_HEIGHT));
|
|
543
|
+
};
|
|
544
|
+
window.addEventListener("message", onMessage);
|
|
545
|
+
return () => window.removeEventListener("message", onMessage);
|
|
546
|
+
}, [entry.content]);
|
|
547
|
+
const height = measuredHeight === void 0 ? declaredHeight : `${measuredHeight}px`;
|
|
548
|
+
const frameProps = entry.content !== void 0 ? { srcDoc: entry.content + MEASURE_SCRIPT } : { src: safeHref ?? "" };
|
|
532
549
|
return /* @__PURE__ */ jsxs9("figure", { className: "my-3 overflow-hidden rounded-md border border-border", children: [
|
|
533
550
|
/* @__PURE__ */ jsxs9("figcaption", { className: "flex items-center justify-between border-b border-border bg-muted/40 px-3 py-1.5 text-xs font-medium text-muted-foreground", children: [
|
|
534
551
|
/* @__PURE__ */ jsx14("span", { children: entry.title ?? "HTML" }),
|
|
@@ -548,6 +565,7 @@ function DocHtml({ entry }) {
|
|
|
548
565
|
/* @__PURE__ */ jsx14(
|
|
549
566
|
"iframe",
|
|
550
567
|
{
|
|
568
|
+
ref: frameRef,
|
|
551
569
|
...frameProps,
|
|
552
570
|
sandbox: "allow-scripts",
|
|
553
571
|
loading: "lazy",
|
|
@@ -1096,7 +1114,7 @@ function ScenarioRunHistory({ scenarioId }) {
|
|
|
1096
1114
|
}
|
|
1097
1115
|
|
|
1098
1116
|
// src/components/ScenarioStaleness.tsx
|
|
1099
|
-
import { useEffect, useState } from "react";
|
|
1117
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
1100
1118
|
|
|
1101
1119
|
// src/lib/provenance.ts
|
|
1102
1120
|
function formatRelativeAge(thenMs, nowMs) {
|
|
@@ -1155,8 +1173,8 @@ import { jsxs as jsxs16 } from "react/jsx-runtime";
|
|
|
1155
1173
|
function ScenarioStaleness({ scenario }) {
|
|
1156
1174
|
const freshness = useScenarioFreshness();
|
|
1157
1175
|
const fixedNow = freshness?.nowMs;
|
|
1158
|
-
const [now, setNow] =
|
|
1159
|
-
|
|
1176
|
+
const [now, setNow] = useState2(fixedNow);
|
|
1177
|
+
useEffect2(() => {
|
|
1160
1178
|
if (fixedNow === void 0) setNow(Date.now());
|
|
1161
1179
|
}, [fixedNow]);
|
|
1162
1180
|
if (!freshness || now === void 0) return null;
|
|
@@ -1170,7 +1188,7 @@ function ScenarioStaleness({ scenario }) {
|
|
|
1170
1188
|
}
|
|
1171
1189
|
|
|
1172
1190
|
// src/interactive/collapse-context.ts
|
|
1173
|
-
import { createContext as createContext4, useCallback, useContext as useContext5, useState as
|
|
1191
|
+
import { createContext as createContext4, useCallback, useContext as useContext5, useState as useState3 } from "react";
|
|
1174
1192
|
var CollapseContext = createContext4(null);
|
|
1175
1193
|
var CollapseProvider = CollapseContext.Provider;
|
|
1176
1194
|
function useCollapse() {
|
|
@@ -1744,23 +1762,85 @@ function ReportErrorShell({
|
|
|
1744
1762
|
return /* @__PURE__ */ jsx36("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: /* @__PURE__ */ jsx36(ReportSchemaError, { error }) });
|
|
1745
1763
|
}
|
|
1746
1764
|
|
|
1747
|
-
// src/components/
|
|
1765
|
+
// src/components/ReportSpanGraph.tsx
|
|
1766
|
+
import { deriveSpanGraph, spanGraphNodeMark, spanGraphToMermaid } from "executable-stories-core/span-graph";
|
|
1767
|
+
import { useMemo as useMemo2 } from "react";
|
|
1748
1768
|
import { jsx as jsx37, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
1769
|
+
var LANE_LABEL = {
|
|
1770
|
+
edge: "Edge",
|
|
1771
|
+
service: "Service",
|
|
1772
|
+
queue: "Queue",
|
|
1773
|
+
datastore: "Data"
|
|
1774
|
+
};
|
|
1775
|
+
var MARK_LABEL = {
|
|
1776
|
+
failing: "Failing",
|
|
1777
|
+
added: "New",
|
|
1778
|
+
changed: "Changed"
|
|
1779
|
+
};
|
|
1780
|
+
var MARK_CLASS = {
|
|
1781
|
+
failing: "text-fail",
|
|
1782
|
+
added: "text-pass",
|
|
1783
|
+
changed: "text-pend"
|
|
1784
|
+
};
|
|
1785
|
+
function MarkBadge({ node }) {
|
|
1786
|
+
const mark = spanGraphNodeMark(node);
|
|
1787
|
+
if (!mark) return null;
|
|
1788
|
+
return /* @__PURE__ */ jsx37("span", { className: cn("ml-2 text-xs font-medium", MARK_CLASS[mark]), children: MARK_LABEL[mark] });
|
|
1789
|
+
}
|
|
1790
|
+
function ReportSpanGraph() {
|
|
1791
|
+
const report = useReport();
|
|
1792
|
+
const { code, graph, titleById } = useMemo2(() => {
|
|
1793
|
+
const scenarios = report.features.flatMap((feature) => feature.scenarios);
|
|
1794
|
+
const derived = deriveSpanGraph(scenarios);
|
|
1795
|
+
return {
|
|
1796
|
+
graph: derived,
|
|
1797
|
+
code: spanGraphToMermaid(derived),
|
|
1798
|
+
titleById: new Map(scenarios.map((scenario) => [scenario.id, scenario.title]))
|
|
1799
|
+
};
|
|
1800
|
+
}, [report]);
|
|
1801
|
+
if (code === "") return null;
|
|
1802
|
+
return /* @__PURE__ */ jsxs25("section", { className: "es-span-graph mt-6", "aria-labelledby": "es-span-graph-heading", children: [
|
|
1803
|
+
/* @__PURE__ */ jsx37("h2", { id: "es-span-graph-heading", className: "text-base font-semibold text-foreground", children: "Architecture, as it ran" }),
|
|
1804
|
+
/* @__PURE__ */ jsx37("p", { className: "mt-1 text-sm text-muted-foreground", children: "Derived from this run\u2019s traces. Every component was named by a span while a scenario ran, so nothing here is inferred from the code. Instrumented, exercised paths only: a component no scenario reaches does not appear." }),
|
|
1805
|
+
/* @__PURE__ */ jsx37(DocMermaid, { entry: { kind: "mermaid", phase: "static", code } }),
|
|
1806
|
+
/* @__PURE__ */ jsxs25("table", { className: "mt-2 w-full text-left text-sm", children: [
|
|
1807
|
+
/* @__PURE__ */ jsx37("caption", { className: "sr-only", children: "Each component and the scenarios that exercised it" }),
|
|
1808
|
+
/* @__PURE__ */ jsx37("thead", { children: /* @__PURE__ */ jsxs25("tr", { className: "text-xs text-muted-foreground", children: [
|
|
1809
|
+
/* @__PURE__ */ jsx37("th", { scope: "col", className: "py-1 pr-4 font-medium", children: "Component" }),
|
|
1810
|
+
/* @__PURE__ */ jsx37("th", { scope: "col", className: "py-1 pr-4 font-medium", children: "Lane" }),
|
|
1811
|
+
/* @__PURE__ */ jsx37("th", { scope: "col", className: "py-1 font-medium", children: "Covered by" })
|
|
1812
|
+
] }) }),
|
|
1813
|
+
/* @__PURE__ */ jsx37("tbody", { children: graph.nodes.map((node) => /* @__PURE__ */ jsxs25("tr", { className: "border-t border-border align-top", children: [
|
|
1814
|
+
/* @__PURE__ */ jsxs25("th", { scope: "row", className: "py-1.5 pr-4 font-mono text-xs font-normal", children: [
|
|
1815
|
+
node.id,
|
|
1816
|
+
/* @__PURE__ */ jsx37(MarkBadge, { node })
|
|
1817
|
+
] }),
|
|
1818
|
+
/* @__PURE__ */ jsx37("td", { className: "py-1.5 pr-4 text-xs text-muted-foreground", children: LANE_LABEL[node.kind] }),
|
|
1819
|
+
/* @__PURE__ */ jsx37("td", { className: "py-1.5", children: /* @__PURE__ */ jsx37("ul", { className: "flex flex-col gap-0.5", children: node.scenarioIds.map((id) => /* @__PURE__ */ jsx37("li", { children: /* @__PURE__ */ jsx37("a", { href: `#${id}`, className: "text-xs underline underline-offset-2", children: titleById.get(id) ?? id }) }, id)) }) })
|
|
1820
|
+
] }, node.id)) })
|
|
1821
|
+
] })
|
|
1822
|
+
] });
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
// src/components/Report.tsx
|
|
1826
|
+
import { jsx as jsx38, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
1749
1827
|
function Report(props) {
|
|
1750
1828
|
const { report, customRenderers, renderers, className, title, dataTheme } = props;
|
|
1751
1829
|
const result = unwrapReport(report);
|
|
1752
1830
|
if (!result.ok) {
|
|
1753
|
-
return /* @__PURE__ */
|
|
1831
|
+
return /* @__PURE__ */ jsx38(ReportErrorShell, { error: result.error, className, title, dataTheme });
|
|
1754
1832
|
}
|
|
1755
1833
|
const data = result.data;
|
|
1756
1834
|
const hasContent = data.features.length > 0;
|
|
1757
|
-
return /* @__PURE__ */
|
|
1758
|
-
/* @__PURE__ */
|
|
1759
|
-
|
|
1835
|
+
return /* @__PURE__ */ jsx38(ReportRoot, { report: data, customRenderers, renderers, children: /* @__PURE__ */ jsxs26("main", { className: cn("es-report", className), "aria-label": title ?? "Test report", "data-theme": dataTheme, children: [
|
|
1836
|
+
/* @__PURE__ */ jsx38("header", { className: "es-report-header", children: /* @__PURE__ */ jsx38(ReportTitleBlock, { title }) }),
|
|
1837
|
+
/* @__PURE__ */ jsx38(ReportSpanGraph, {}),
|
|
1838
|
+
hasContent ? /* @__PURE__ */ jsx38(ReportFeatureList, {}) : /* @__PURE__ */ jsx38(ReportEmpty, {})
|
|
1760
1839
|
] }) });
|
|
1761
1840
|
}
|
|
1762
1841
|
|
|
1763
1842
|
// src/ssr-entry.ts
|
|
1843
|
+
import { REPORT_FAVICON_LINK } from "executable-stories-core/utils/report-favicon";
|
|
1764
1844
|
var ROOT_ID = "es-report-root";
|
|
1765
1845
|
var DATA_ID = "es-report-data";
|
|
1766
1846
|
var HISTORY_ID = "es-report-history";
|
|
@@ -1814,7 +1894,9 @@ function renderReportToHtml(report, options = {}) {
|
|
|
1814
1894
|
mermaid = true,
|
|
1815
1895
|
islandScript = "",
|
|
1816
1896
|
staleAfterDays = 7,
|
|
1817
|
-
scenarioHistory
|
|
1897
|
+
scenarioHistory,
|
|
1898
|
+
share = false,
|
|
1899
|
+
shareCommand = ""
|
|
1818
1900
|
} = options;
|
|
1819
1901
|
const markup = renderToStaticMarkup(
|
|
1820
1902
|
createElement(Report, { report, title })
|
|
@@ -1827,7 +1909,8 @@ function renderReportToHtml(report, options = {}) {
|
|
|
1827
1909
|
const historyScript = interactive && scenarioHistory && Object.keys(scenarioHistory).length > 0 ? `<script type="application/json" id="${HISTORY_ID}">${escapeJsonForScript(JSON.stringify(scenarioHistory))}</script>` : "";
|
|
1828
1910
|
const islandTag = interactive ? `<script>${islandScript}</script>` : "";
|
|
1829
1911
|
const cdnBody = interactive ? "" : cdn.body;
|
|
1830
|
-
const
|
|
1912
|
+
const shareAttrs = share ? ` data-es-share="true"${shareCommand ? ` data-es-share-cmd="${escapeHtml(shareCommand)}"` : ""}` : "";
|
|
1913
|
+
const islandConfigAttrs = interactive ? ` data-es-syntax="${syntaxHighlighting ? "true" : "false"}" data-es-mermaid="${mermaid ? "true" : "false"}" data-es-stale-days="${Number.isFinite(staleAfterDays) && staleAfterDays >= 0 ? staleAfterDays : 7}"${shareAttrs}` : "";
|
|
1831
1914
|
const rootAttrs = interactive ? ` id="${ROOT_ID}" data-title="${escapeHtml(title)}"${islandConfigAttrs}` : "";
|
|
1832
1915
|
return `<!DOCTYPE html>
|
|
1833
1916
|
<html lang="${escapeHtml(lang)}" data-theme="${theme === "dark" ? "dark" : "light"}">
|
|
@@ -1835,6 +1918,7 @@ function renderReportToHtml(report, options = {}) {
|
|
|
1835
1918
|
<meta charset="UTF-8">
|
|
1836
1919
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1837
1920
|
<meta name="color-scheme" content="light dark">
|
|
1921
|
+
${REPORT_FAVICON_LINK}
|
|
1838
1922
|
<title>${escapeHtml(title)}</title>
|
|
1839
1923
|
${cdn.head}
|
|
1840
1924
|
${headExtra}
|