executable-stories-react 0.17.1 → 0.19.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.d.cts CHANGED
@@ -100,9 +100,9 @@ declare function ReportSummaryView({ summary, className, ariaLabel }: ReportSumm
100
100
  declare function ReportMeta(): react.JSX.Element | null;
101
101
 
102
102
  /**
103
- * Renders scenario attachments. Base64 images embed inline; everything else
104
- * becomes a labelled data-URI download link. Mirrors the report's attachments
105
- * block (which also inlines images and links other media).
103
+ * Renders scenario attachments. Images embed inline; text documents preview
104
+ * above their download link; other types get a labelled download link. An
105
+ * `external` attachment links to its file path and shows no preview.
106
106
  */
107
107
  declare function ReportAttachments({ attachments }: {
108
108
  attachments: ReportAttachment[];
@@ -192,8 +192,13 @@ declare function DocTag({ entry }: {
192
192
  entry: ReportDocTag;
193
193
  }): react.JSX.Element;
194
194
 
195
- declare function DocKv({ entry }: {
196
- entry: ReportDocKv;
195
+ /**
196
+ * Consecutive `story.kv()` entries share one two-column grid so the values
197
+ * line up whatever the label lengths; a lone entry is just a one-row grid.
198
+ * `ReportDocEntries` groups the runs.
199
+ */
200
+ declare function DocKv({ entries }: {
201
+ entries: readonly ReportDocKv[];
197
202
  }): react.JSX.Element;
198
203
 
199
204
  declare function DocCode({ entry }: {
package/dist/index.d.ts CHANGED
@@ -100,9 +100,9 @@ declare function ReportSummaryView({ summary, className, ariaLabel }: ReportSumm
100
100
  declare function ReportMeta(): react.JSX.Element | null;
101
101
 
102
102
  /**
103
- * Renders scenario attachments. Base64 images embed inline; everything else
104
- * becomes a labelled data-URI download link. Mirrors the report's attachments
105
- * block (which also inlines images and links other media).
103
+ * Renders scenario attachments. Images embed inline; text documents preview
104
+ * above their download link; other types get a labelled download link. An
105
+ * `external` attachment links to its file path and shows no preview.
106
106
  */
107
107
  declare function ReportAttachments({ attachments }: {
108
108
  attachments: ReportAttachment[];
@@ -192,8 +192,13 @@ declare function DocTag({ entry }: {
192
192
  entry: ReportDocTag;
193
193
  }): react.JSX.Element;
194
194
 
195
- declare function DocKv({ entry }: {
196
- entry: ReportDocKv;
195
+ /**
196
+ * Consecutive `story.kv()` entries share one two-column grid so the values
197
+ * line up whatever the label lengths; a lone entry is just a one-row grid.
198
+ * `ReportDocEntries` groups the runs.
199
+ */
200
+ declare function DocKv({ entries }: {
201
+ entries: readonly ReportDocKv[];
197
202
  }): react.JSX.Element;
198
203
 
199
204
  declare function DocCode({ entry }: {
package/dist/index.js CHANGED
@@ -341,11 +341,11 @@ function formatValue(value) {
341
341
  return String(value);
342
342
  }
343
343
  }
344
- function DocKv({ entry }) {
345
- return /* @__PURE__ */ jsxs2("dl", { className: "my-2 flex flex-wrap items-baseline gap-x-2 text-sm", children: [
344
+ function DocKv({ entries }) {
345
+ return /* @__PURE__ */ jsx6("dl", { className: "my-2 grid grid-cols-[max-content_1fr] gap-x-3 gap-y-1 text-sm", children: entries.map((entry, i) => /* @__PURE__ */ jsxs2("div", { className: "contents", children: [
346
346
  /* @__PURE__ */ jsx6("dt", { className: "font-semibold text-muted-foreground", children: entry.label }),
347
347
  /* @__PURE__ */ jsx6("dd", { className: "min-w-0 break-words font-mono text-foreground", children: formatValue(entry.value) })
348
- ] });
348
+ ] }, i)) });
349
349
  }
350
350
 
351
351
  // src/hooks/useRenderers.ts
@@ -432,7 +432,7 @@ function DocSection({ entry }) {
432
432
  if (renderers.section) {
433
433
  return /* @__PURE__ */ jsx10(Fragment2, { children: renderers.section(entry) });
434
434
  }
435
- const html = safeMarkdownHtml(entry.markdown);
435
+ const html = safeMarkdownHtml(dedent(entry.markdown));
436
436
  return /* @__PURE__ */ jsxs5("section", { className: "my-3 text-sm", "aria-label": entry.title, children: [
437
437
  entry.title ? /* @__PURE__ */ jsx10("h4", { className: "mb-1 font-semibold text-foreground", children: entry.title }) : null,
438
438
  /* @__PURE__ */ jsx10(
@@ -618,7 +618,7 @@ function DocEntry({ entry }) {
618
618
  case "tag":
619
619
  return /* @__PURE__ */ jsx17(DocTag, { entry });
620
620
  case "kv":
621
- return /* @__PURE__ */ jsx17(DocKv, { entry });
621
+ return /* @__PURE__ */ jsx17(DocKv, { entries: [entry] });
622
622
  case "code":
623
623
  return /* @__PURE__ */ jsx17(DocCode, { entry });
624
624
  case "table":
@@ -646,7 +646,15 @@ function DocEntry({ entry }) {
646
646
  import { Fragment as Fragment5, jsx as jsx18 } from "react/jsx-runtime";
647
647
  function ReportDocEntries({ entries }) {
648
648
  if (entries.length === 0) return null;
649
- return /* @__PURE__ */ jsx18(Fragment5, { children: entries.map((entry, i) => /* @__PURE__ */ jsx18(DocEntry, { entry }, i)) });
649
+ const groups = [];
650
+ for (const entry of entries) {
651
+ const last = groups[groups.length - 1];
652
+ if (entry.kind === "kv" && Array.isArray(last)) last.push(entry);
653
+ else groups.push(entry.kind === "kv" ? [entry] : entry);
654
+ }
655
+ return /* @__PURE__ */ jsx18(Fragment5, { children: groups.map(
656
+ (g, i) => Array.isArray(g) ? /* @__PURE__ */ jsx18(DocKv, { entries: g }, i) : /* @__PURE__ */ jsx18(DocEntry, { entry: g }, i)
657
+ ) });
650
658
  }
651
659
 
652
660
  // src/components/ReportSteps.tsx
@@ -851,42 +859,76 @@ function StateCardView({ card }) {
851
859
 
852
860
  // src/components/ReportAttachments.tsx
853
861
  import { Fragment as Fragment6, jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
862
+ function attachmentText(att) {
863
+ if (att.contentEncoding !== "BASE64") return att.body;
864
+ try {
865
+ return new TextDecoder().decode(Uint8Array.from(atob(att.body), (c) => c.charCodeAt(0)));
866
+ } catch {
867
+ return void 0;
868
+ }
869
+ }
870
+ function preview(att) {
871
+ const type = att.mediaType.split(";")[0].trim().toLowerCase();
872
+ const text = attachmentText(att);
873
+ if (text === void 0) return null;
874
+ if (type === "text/plain") {
875
+ return /* @__PURE__ */ jsx21("pre", { className: "max-h-96 overflow-auto rounded-md border border-border bg-muted/40 p-3 text-xs whitespace-pre-wrap", children: text });
876
+ }
877
+ if (type === "text/markdown") {
878
+ return /* @__PURE__ */ jsx21(
879
+ "div",
880
+ {
881
+ className: "es-doc-prose prose prose-sm max-w-none rounded-md border border-border p-3",
882
+ dangerouslySetInnerHTML: { __html: safeMarkdownHtml(text) }
883
+ }
884
+ );
885
+ }
886
+ if (type === "text/html") {
887
+ return /* @__PURE__ */ jsx21(DocHtml, { entry: { kind: "html", content: text, title: att.name, phase: "runtime" } });
888
+ }
889
+ return null;
890
+ }
854
891
  function ReportAttachments({ attachments }) {
855
892
  if (attachments.length === 0) return null;
856
893
  return (
857
894
  // role="group" (not a labelled <section>) so multiple scenarios' attachment
858
895
  // blocks don't create duplicate "Attachments" region landmarks.
859
896
  /* @__PURE__ */ jsx21("div", { role: "group", "aria-label": "Attachments", className: "mt-3 flex flex-col gap-2", children: attachments.map((att, i) => {
860
- const dataUri = att.contentEncoding === "BASE64" ? `data:${att.mediaType};base64,${att.body}` : `data:${att.mediaType},${encodeURIComponent(att.body)}`;
861
- const isImage = att.mediaType.startsWith("image/");
897
+ const dataUri = att.external ? void 0 : att.contentEncoding === "BASE64" ? `data:${att.mediaType};base64,${att.body}` : `data:${att.mediaType},${encodeURIComponent(att.body)}`;
898
+ const href = dataUri ?? safeUrl(att.body);
899
+ const imageSrc = dataUri ?? safeImageUrl(att.body);
900
+ const isImage = att.mediaType.startsWith("image/") && imageSrc !== void 0;
862
901
  return /* @__PURE__ */ jsx21("figure", { className: "m-0", children: isImage ? /* @__PURE__ */ jsxs13(Fragment6, { children: [
863
902
  /* @__PURE__ */ jsx21(
864
903
  "img",
865
904
  {
866
- src: dataUri,
905
+ src: imageSrc,
867
906
  alt: att.name,
868
907
  loading: "lazy",
869
908
  className: "max-w-full rounded-md border border-border"
870
909
  }
871
910
  ),
872
911
  /* @__PURE__ */ jsx21("figcaption", { className: "mt-1 text-xs text-muted-foreground", children: att.name })
873
- ] }) : /* @__PURE__ */ jsxs13(
874
- "a",
875
- {
876
- href: dataUri,
877
- download: att.name,
878
- className: "text-xs text-link underline underline-offset-2",
879
- children: [
880
- att.name,
881
- " ",
882
- /* @__PURE__ */ jsxs13("span", { className: "text-muted-foreground", children: [
883
- "(",
884
- att.mediaType,
885
- ")"
886
- ] })
887
- ]
888
- }
889
- ) }, `${att.name}-${i}`);
912
+ ] }) : /* @__PURE__ */ jsxs13(Fragment6, { children: [
913
+ att.external ? null : preview(att),
914
+ /* @__PURE__ */ jsxs13(
915
+ "a",
916
+ {
917
+ href,
918
+ download: att.name,
919
+ className: "text-xs text-link underline underline-offset-2",
920
+ children: [
921
+ att.name,
922
+ " ",
923
+ /* @__PURE__ */ jsxs13("span", { className: "text-muted-foreground", children: [
924
+ "(",
925
+ att.mediaType,
926
+ ")"
927
+ ] })
928
+ ]
929
+ }
930
+ )
931
+ ] }) }, `${att.name}-${i}`);
890
932
  }) })
891
933
  );
892
934
  }
@@ -1436,10 +1478,17 @@ function ReportFeatureIntro({ feature }) {
1436
1478
  dangerouslySetInnerHTML: { __html: safeMarkdownHtml(dedent(feature.narrative)) }
1437
1479
  }
1438
1480
  ) : null,
1439
- hasGlossary ? /* @__PURE__ */ jsx28("dl", { className: "grid gap-1.5", "aria-label": "Glossary", children: feature.glossary.map((entry) => /* @__PURE__ */ jsxs19("div", { className: "flex flex-wrap gap-x-2 text-xs", children: [
1440
- /* @__PURE__ */ jsx28("dt", { className: "font-medium text-foreground", children: entry.term }),
1441
- /* @__PURE__ */ jsx28("dd", { className: "flex-1 text-muted-foreground", children: entry.definition })
1442
- ] }, entry.term)) }) : null
1481
+ hasGlossary ? /* @__PURE__ */ jsx28(
1482
+ "dl",
1483
+ {
1484
+ className: "grid grid-cols-[max-content_1fr] gap-x-4 gap-y-1.5 text-xs",
1485
+ "aria-label": "Glossary",
1486
+ children: feature.glossary.map((entry) => /* @__PURE__ */ jsxs19("div", { className: "contents", children: [
1487
+ /* @__PURE__ */ jsx28("dt", { className: "font-medium text-foreground", children: entry.term }),
1488
+ /* @__PURE__ */ jsx28("dd", { className: "text-muted-foreground", children: entry.definition })
1489
+ ] }, entry.term))
1490
+ }
1491
+ ) : null
1443
1492
  ] });
1444
1493
  }
1445
1494
  function featureKindLabel(kind) {
@@ -1455,7 +1504,6 @@ function ReportFeature({ feature }) {
1455
1504
  const titleId = `${feature.id}-title`;
1456
1505
  const bodyId = `${feature.id}-body`;
1457
1506
  const s = feature.summary;
1458
- const skipped = s.skipped + s.pending;
1459
1507
  const collapse = useCollapse();
1460
1508
  const collapsed = collapse?.isCollapsed(feature.id) ?? false;
1461
1509
  const kindLabel = featureKindLabel(feature.kind);
@@ -1495,7 +1543,12 @@ function ReportFeature({ feature }) {
1495
1543
  "p",
1496
1544
  {
1497
1545
  className: "flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground",
1498
- "aria-label": `${s.passed} passed, ${s.failed} failed, ${skipped} skipped`,
1546
+ "aria-label": [
1547
+ `${s.passed} passed`,
1548
+ `${s.failed} failed`,
1549
+ s.skipped > 0 && `${s.skipped} skipped`,
1550
+ s.pending > 0 && `${s.pending} pending`
1551
+ ].filter(Boolean).join(", "),
1499
1552
  children: [
1500
1553
  /* @__PURE__ */ jsxs20("span", { children: [
1501
1554
  s.passed,
@@ -1506,12 +1559,19 @@ function ReportFeature({ feature }) {
1506
1559
  s.failed,
1507
1560
  " failed"
1508
1561
  ] }),
1509
- skipped > 0 ? /* @__PURE__ */ jsxs20(Fragment9, { children: [
1562
+ s.skipped > 0 ? /* @__PURE__ */ jsxs20(Fragment9, { children: [
1510
1563
  /* @__PURE__ */ jsx29("span", { "aria-hidden": true, children: "\xB7" }),
1511
1564
  /* @__PURE__ */ jsxs20("span", { children: [
1512
- skipped,
1565
+ s.skipped,
1513
1566
  " skipped"
1514
1567
  ] })
1568
+ ] }) : null,
1569
+ s.pending > 0 ? /* @__PURE__ */ jsxs20(Fragment9, { children: [
1570
+ /* @__PURE__ */ jsx29("span", { "aria-hidden": true, children: "\xB7" }),
1571
+ /* @__PURE__ */ jsxs20("span", { children: [
1572
+ s.pending,
1573
+ " pending"
1574
+ ] })
1515
1575
  ] }) : null
1516
1576
  ]
1517
1577
  }
@@ -2502,6 +2562,10 @@ var story_report_v1_default = {
2502
2562
  contentEncoding: {
2503
2563
  type: "string",
2504
2564
  enum: ["BASE64", "IDENTITY"]
2565
+ },
2566
+ external: {
2567
+ const: true,
2568
+ description: "body holds the file's path or URL (file missing or too large to embed)."
2505
2569
  }
2506
2570
  },
2507
2571
  required: ["name", "mediaType", "body", "contentEncoding"],