executable-stories-react 0.16.0 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -93,6 +93,11 @@ interface ReportInteractiveProps {
93
93
  scenarioHistory?: ScenarioHistoryMap;
94
94
  /** Show the Share button in the header (default false). `--html-share` turns it on. */
95
95
  share?: boolean;
96
+ /**
97
+ * Draw the "Architecture, as it ran" section (default false). Only an
98
+ * instrumented suite has anything to draw. `--html-architecture` turns it on.
99
+ */
100
+ architecture?: boolean;
96
101
  /** Command the share dialog hands over. Default: `npx executable-stories share reports/`. */
97
102
  shareCommand?: string;
98
103
  }
@@ -93,6 +93,11 @@ interface ReportInteractiveProps {
93
93
  scenarioHistory?: ScenarioHistoryMap;
94
94
  /** Show the Share button in the header (default false). `--html-share` turns it on. */
95
95
  share?: boolean;
96
+ /**
97
+ * Draw the "Architecture, as it ran" section (default false). Only an
98
+ * instrumented suite has anything to draw. `--html-architecture` turns it on.
99
+ */
100
+ architecture?: boolean;
96
101
  /** Command the share dialog hands over. Default: `npx executable-stories share reports/`. */
97
102
  shareCommand?: string;
98
103
  }
@@ -1711,7 +1711,7 @@ function ReportSpanGraph() {
1711
1711
  /* @__PURE__ */ jsx35("h2", { id: "es-span-graph-heading", className: "text-base font-semibold text-foreground", children: "Architecture, as it ran" }),
1712
1712
  /* @__PURE__ */ jsx35("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." }),
1713
1713
  /* @__PURE__ */ jsx35(DocMermaid, { entry: { kind: "mermaid", phase: "static", code } }),
1714
- /* @__PURE__ */ jsxs24("table", { className: "mt-2 w-full text-left text-sm", children: [
1714
+ /* @__PURE__ */ jsxs24("table", { className: "mt-2 w-full border-collapse text-left text-sm", children: [
1715
1715
  /* @__PURE__ */ jsx35("caption", { className: "sr-only", children: "Each component and the scenarios that exercised it" }),
1716
1716
  /* @__PURE__ */ jsx35("thead", { children: /* @__PURE__ */ jsxs24("tr", { className: "text-xs text-muted-foreground", children: [
1717
1717
  /* @__PURE__ */ jsx35("th", { scope: "col", className: "py-1 pr-4 font-medium", children: "Component" }),
@@ -1724,7 +1724,7 @@ function ReportSpanGraph() {
1724
1724
  /* @__PURE__ */ jsx35(MarkBadge, { node })
1725
1725
  ] }),
1726
1726
  /* @__PURE__ */ jsx35("td", { className: "py-1.5 pr-4 text-xs text-muted-foreground", children: LANE_LABEL[node.kind] }),
1727
- /* @__PURE__ */ jsx35("td", { className: "py-1.5", children: /* @__PURE__ */ jsx35("ul", { className: "flex flex-col gap-0.5", children: node.scenarioIds.map((id) => /* @__PURE__ */ jsx35("li", { children: /* @__PURE__ */ jsx35("a", { href: `#${id}`, className: "text-xs underline underline-offset-2", children: titleById.get(id) ?? id }) }, id)) }) })
1727
+ /* @__PURE__ */ jsx35("td", { className: "py-1.5", children: /* @__PURE__ */ jsx35("ul", { className: "flex flex-col gap-0.5", children: node.scenarioIds.map((id) => /* @__PURE__ */ jsx35("li", { children: /* @__PURE__ */ jsx35("a", { href: `#${id}`, className: "text-xs text-link underline underline-offset-2 hover:text-foreground", children: titleById.get(id) ?? id }) }, id)) }) })
1728
1728
  ] }, node.id)) })
1729
1729
  ] })
1730
1730
  ] });
@@ -2687,7 +2687,7 @@ function ReportShare({
2687
2687
  "a",
2688
2688
  {
2689
2689
  className: "text-xs underline underline-offset-2",
2690
- href: "https://executablestories.com/guides/sharing-reports/",
2690
+ href: "https://docs.executablestories.com/guides/sharing-reports/",
2691
2691
  target: "_blank",
2692
2692
  rel: "noreferrer",
2693
2693
  children: "How sharing works"
@@ -2874,18 +2874,36 @@ function TocContent({ onNavigate }) {
2874
2874
  [report]
2875
2875
  );
2876
2876
  useEffect7(() => {
2877
- if (typeof IntersectionObserver === "undefined") return;
2877
+ if (typeof window === "undefined") return;
2878
2878
  const els = scenarioIds.map((id) => document.getElementById(id)).filter((el) => el !== null);
2879
2879
  if (els.length === 0) return;
2880
- const observer = new IntersectionObserver(
2881
- (entries) => {
2882
- const visible = entries.filter((e) => e.isIntersecting).sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top);
2883
- if (visible[0]) setActiveId(visible[0].target.id);
2884
- },
2885
- { rootMargin: "-8% 0px -80% 0px" }
2886
- );
2887
- els.forEach((el) => observer.observe(el));
2888
- return () => observer.disconnect();
2880
+ let frame = 0;
2881
+ const update = () => {
2882
+ frame = 0;
2883
+ const atBottom = window.scrollY + window.innerHeight >= document.documentElement.scrollHeight - 2;
2884
+ const line = atBottom ? window.innerHeight : window.innerHeight * 0.2;
2885
+ let active = els[0];
2886
+ let best = -Infinity;
2887
+ for (const el of els) {
2888
+ const top = el.getBoundingClientRect().top;
2889
+ if (top <= line && top > best) {
2890
+ best = top;
2891
+ active = el;
2892
+ }
2893
+ }
2894
+ setActiveId(active.id);
2895
+ };
2896
+ const onScroll = () => {
2897
+ if (frame === 0) frame = requestAnimationFrame(update);
2898
+ };
2899
+ update();
2900
+ window.addEventListener("scroll", onScroll, { passive: true });
2901
+ window.addEventListener("resize", onScroll);
2902
+ return () => {
2903
+ window.removeEventListener("scroll", onScroll);
2904
+ window.removeEventListener("resize", onScroll);
2905
+ if (frame !== 0) cancelAnimationFrame(frame);
2906
+ };
2889
2907
  }, [scenarioIds]);
2890
2908
  if (report.features.length === 0) return null;
2891
2909
  return /* @__PURE__ */ jsx50("ul", { className: "flex list-none flex-col gap-4 pl-0", children: report.features.map((feature) => /* @__PURE__ */ jsxs35("li", { children: [
@@ -3431,6 +3449,7 @@ function ReportInteractiveView({
3431
3449
  staleAfterDays = 7,
3432
3450
  scenarioHistory,
3433
3451
  share = false,
3452
+ architecture = false,
3434
3453
  shareCommand
3435
3454
  }) {
3436
3455
  const [urlState, setUrlState] = useUrlState();
@@ -3641,7 +3660,7 @@ function ReportInteractiveView({
3641
3660
  /* @__PURE__ */ jsx52(ReportSummary, {}),
3642
3661
  /* @__PURE__ */ jsx52(ReportMeta, {})
3643
3662
  ] }),
3644
- /* @__PURE__ */ jsx52(ReportSpanGraph, {}),
3663
+ architecture ? /* @__PURE__ */ jsx52(ReportSpanGraph, {}) : null,
3645
3664
  /* @__PURE__ */ jsx52(
3646
3665
  ReportFreshness,
3647
3666
  {
@@ -3797,7 +3816,10 @@ function MermaidView({ entry, load }) {
3797
3816
  mermaid.initialize({
3798
3817
  startOnLoad: false,
3799
3818
  securityLevel: "strict",
3800
- theme: prefersDark(ref.current) ? "dark" : "default"
3819
+ // "neutral" in light: the same theme the static, no-JS render uses
3820
+ // in ssr-entry, so a diagram keeps its colours when the island takes
3821
+ // over.
3822
+ theme: prefersDark(ref.current) ? "dark" : "neutral"
3801
3823
  });
3802
3824
  await mermaid.parse(entry.code);
3803
3825
  const id = `es-mermaid-${Math.random().toString(36).slice(2)}`;
@@ -3856,7 +3878,7 @@ function MermaidView({ entry, load }) {
3856
3878
  // src/interactive/island-renderers.tsx
3857
3879
  import { jsx as jsx54 } from "react/jsx-runtime";
3858
3880
  var HLJS_ESM = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/es/highlight.min.js";
3859
- var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
3881
+ var MERMAID_ESM = "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
3860
3882
  function cachedCdnModule(url) {
3861
3883
  let loader;
3862
3884
  return () => {