testomatio-editor-blocks 0.4.68 → 0.4.69

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.
@@ -28,7 +28,8 @@ const readStepViewMode = () => {
28
28
  return "vertical";
29
29
  }
30
30
  try {
31
- return window.localStorage.getItem(VIEW_MODE_KEY) === "horizontal" ? "horizontal" : "vertical";
31
+ const stored = window.localStorage.getItem(VIEW_MODE_KEY);
32
+ return stored === "horizontal" || stored === "compact" ? stored : "vertical";
32
33
  }
33
34
  catch {
34
35
  return "vertical";
@@ -305,6 +306,9 @@ function TestStepContent({ block, editor, stepNumber, autoFocusEnabled = false,
305
306
  const [viewMode, setViewMode] = useState(() => readStepViewMode());
306
307
  const containerRef = useRef(null);
307
308
  const [forceVertical, setForceVertical] = useState(false);
309
+ // In compact mode each step collapses to a reading-focused row and only
310
+ // expands to the full editing layout while one of its fields has focus.
311
+ const [expanded, setExpanded] = useState(false);
308
312
  useEffect(() => {
309
313
  var _a;
310
314
  const el = (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.parentElement;
@@ -318,7 +322,11 @@ function TestStepContent({ block, editor, stepNumber, autoFocusEnabled = false,
318
322
  observer.observe(el);
319
323
  return () => observer.disconnect();
320
324
  }, []);
321
- const effectiveVertical = forceVertical || viewMode === "vertical";
325
+ const compactMode = viewMode === "compact";
326
+ const effectiveHorizontal = viewMode === "horizontal" && !forceVertical;
327
+ // Compact steps render the vertical layout but collapse their chrome until
328
+ // a field gains focus, at which point the step expands to "normal" editing.
329
+ const compactCollapsed = compactMode && !expanded;
322
330
  useEffect(() => {
323
331
  if (typeof window === "undefined") {
324
332
  return;
@@ -434,12 +442,40 @@ function TestStepContent({ block, editor, stepNumber, autoFocusEnabled = false,
434
442
  }
435
443
  }, [editor, block.id]);
436
444
  const handleToggleView = useCallback(() => {
437
- const next = viewMode === "horizontal" ? "vertical" : "horizontal";
445
+ // Cycle vertical horizontal compact → vertical. Skip horizontal when
446
+ // the container is too narrow to fit its two columns.
447
+ let next;
448
+ if (viewMode === "vertical") {
449
+ next = forceVertical ? "compact" : "horizontal";
450
+ }
451
+ else if (viewMode === "horizontal") {
452
+ next = "compact";
453
+ }
454
+ else {
455
+ next = "vertical";
456
+ }
438
457
  writeStepViewMode(next);
439
458
  setViewMode(next);
440
459
  if (typeof window !== "undefined") {
441
460
  window.dispatchEvent(new Event("bn-step-view-mode"));
442
461
  }
462
+ }, [viewMode, forceVertical]);
463
+ const handleContentFocusCapture = useCallback(() => {
464
+ if (viewMode === "compact") {
465
+ setExpanded(true);
466
+ }
467
+ }, [viewMode]);
468
+ const handleContentBlurCapture = useCallback((event) => {
469
+ if (viewMode !== "compact") {
470
+ return;
471
+ }
472
+ // Keep the step expanded while focus stays inside it (e.g. moving to a
473
+ // toolbar or action button); collapse only when focus leaves entirely.
474
+ const nextTarget = event.relatedTarget;
475
+ if (nextTarget && event.currentTarget.contains(nextTarget)) {
476
+ return;
477
+ }
478
+ setExpanded(false);
443
479
  }, [viewMode]);
444
480
  const [dataFocusSignal] = useState(0);
445
481
  const [expectedFocusSignal, setExpectedFocusSignal] = useState(0);
@@ -453,11 +489,18 @@ function TestStepContent({ block, editor, stepNumber, autoFocusEnabled = false,
453
489
  writeExpectedCollapsedPreference(true);
454
490
  editor.updateBlock(block.id, { props: { expectedResult: "" } });
455
491
  }, [editor, block.id]);
456
- const viewToggleButton = (_jsx("button", { type: "button", className: `bn-teststep__view-toggle${!effectiveVertical ? " bn-teststep__view-toggle--horizontal" : ""}${forceVertical ? " bn-teststep__view-toggle--disabled" : ""}`, "data-tooltip": forceVertical ? "Not enough space for horizontal view" : "Switch step view", "aria-label": forceVertical ? "Not enough space for horizontal view" : "Switch step view", onClick: forceVertical ? undefined : handleToggleView, "aria-disabled": forceVertical, tabIndex: -1, children: _jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [_jsx("mask", { id: "mask-toggle", style: { maskType: "alpha" }, maskUnits: "userSpaceOnUse", x: "0", y: "0", width: "16", height: "16", children: _jsx("rect", { width: "16", height: "16", fill: "#D9D9D9" }) }), _jsx("g", { mask: "url(#mask-toggle)", children: _jsx("path", { d: "M12.6667 2C13.0333 2 13.3472 2.13056 13.6083 2.39167C13.8694 2.65278 14 2.96667 14 3.33333L14 12.6667C14 13.0333 13.8694 13.3472 13.6083 13.6083C13.3472 13.8694 13.0333 14 12.6667 14L10 14C9.63333 14 9.31944 13.8694 9.05833 13.6083C8.79722 13.3472 8.66667 13.0333 8.66667 12.6667L8.66667 3.33333C8.66667 2.96667 8.79722 2.65278 9.05833 2.39167C9.31945 2.13055 9.63333 2 10 2L12.6667 2ZM6 2C6.36667 2 6.68056 2.13055 6.94167 2.39167C7.20278 2.65278 7.33333 2.96667 7.33333 3.33333L7.33333 12.6667C7.33333 13.0333 7.20278 13.3472 6.94167 13.6083C6.68055 13.8694 6.36667 14 6 14L3.33333 14C2.96667 14 2.65278 13.8694 2.39167 13.6083C2.13056 13.3472 2 13.0333 2 12.6667L2 3.33333C2 2.96667 2.13056 2.65278 2.39167 2.39167C2.65278 2.13055 2.96667 2 3.33333 2L6 2ZM3.33333 12.6667L6 12.6667L6 3.33333L3.33333 3.33333L3.33333 12.6667Z", fill: "currentColor" }) })] }) }));
457
- if (!effectiveVertical) {
492
+ const nextViewLabel = viewMode === "compact"
493
+ ? "Switch to vertical view"
494
+ : viewMode === "horizontal"
495
+ ? "Switch to compact view"
496
+ : forceVertical
497
+ ? "Switch to compact view"
498
+ : "Switch to horizontal view";
499
+ const viewToggleButton = (_jsx("button", { type: "button", className: `bn-teststep__view-toggle${effectiveHorizontal ? " bn-teststep__view-toggle--horizontal" : ""}${compactMode ? " bn-teststep__view-toggle--compact" : ""}`, "data-tooltip": nextViewLabel, "aria-label": nextViewLabel, onClick: handleToggleView, tabIndex: -1, children: compactMode ? (_jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M2 3.333h12V4.667H2V3.333Zm0 4h12v1.334H2V7.333Zm0 4h12v1.334H2v-1.334Z", fill: "currentColor" }) })) : (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [_jsx("mask", { id: "mask-toggle", style: { maskType: "alpha" }, maskUnits: "userSpaceOnUse", x: "0", y: "0", width: "16", height: "16", children: _jsx("rect", { width: "16", height: "16", fill: "#D9D9D9" }) }), _jsx("g", { mask: "url(#mask-toggle)", children: _jsx("path", { d: "M12.6667 2C13.0333 2 13.3472 2.13056 13.6083 2.39167C13.8694 2.65278 14 2.96667 14 3.33333L14 12.6667C14 13.0333 13.8694 13.3472 13.6083 13.6083C13.3472 13.8694 13.0333 14 12.6667 14L10 14C9.63333 14 9.31944 13.8694 9.05833 13.6083C8.79722 13.3472 8.66667 13.0333 8.66667 12.6667L8.66667 3.33333C8.66667 2.96667 8.79722 2.65278 9.05833 2.39167C9.31945 2.13055 9.63333 2 10 2L12.6667 2ZM6 2C6.36667 2 6.68056 2.13055 6.94167 2.39167C7.20278 2.65278 7.33333 2.96667 7.33333 3.33333L7.33333 12.6667C7.33333 13.0333 7.20278 13.3472 6.94167 13.6083C6.68055 13.8694 6.36667 14 6 14L3.33333 14C2.96667 14 2.65278 13.8694 2.39167 13.6083C2.13056 13.3472 2 13.0333 2 12.6667L2 3.33333C2 2.96667 2.13056 2.65278 2.39167 2.39167C2.65278 2.13055 2.96667 2 3.33333 2L6 2ZM3.33333 12.6667L6 12.6667L6 3.33333L3.33333 3.33333L3.33333 12.6667Z", fill: "currentColor" }) })] })) }));
500
+ if (effectiveHorizontal) {
458
501
  return (_jsx(StepHorizontalView, { ref: containerRef, blockId: block.id, stepNumber: stepNumber, stepValue: combinedStepValue, expectedResult: expectedResult, onStepChange: handleCombinedStepChange, onExpectedChange: handleExpectedChange, onInsertNextStep: handleInsertNextStep, onFieldFocus: handleFieldFocus, viewToggle: viewToggleButton, focusSignal: mountFocusSignal }));
459
502
  }
460
- return (_jsxs("div", { className: "bn-teststep", "data-block-id": block.id, ref: containerRef, children: [_jsxs("div", { className: "bn-teststep__timeline", children: [_jsx("span", { className: "bn-teststep__number", children: stepNumber }), _jsx("div", { className: "bn-teststep__line" })] }), _jsxs("div", { className: "bn-teststep__content", children: [_jsxs("div", { className: "bn-teststep__header", children: [_jsx("span", { className: "bn-teststep__title", children: "Step" }), viewToggleButton] }), _jsx(StepField, { label: "Step", showLabel: false, value: stepTitle, placeholder: STEP_TITLE_PLACEHOLDER, onChange: handleStepTitleChange, autoFocus: autoFocusEnabled && stepTitle.length === 0, focusSignal: mountFocusSignal, multiline: true, disableNewlines: true, enableAutocomplete: true, fieldName: "title", suggestionFilter: (suggestion) => suggestion.isSnippet !== true, onFieldFocus: handleFieldFocus, enableImageUpload: false, showFormattingButtons: true, onImageFile: async (file) => {
503
+ return (_jsxs("div", { className: `bn-teststep${compactMode ? " bn-teststep--compact" : ""}${compactCollapsed ? " bn-teststep--collapsed" : ""}`, "data-block-id": block.id, ref: containerRef, children: [_jsxs("div", { className: "bn-teststep__timeline", children: [_jsx("span", { className: "bn-teststep__number", children: stepNumber }), _jsx("div", { className: "bn-teststep__line" })] }), _jsxs("div", { className: "bn-teststep__content", onFocus: handleContentFocusCapture, onBlur: handleContentBlurCapture, children: [_jsxs("div", { className: "bn-teststep__header", children: [!compactMode && _jsx("span", { className: "bn-teststep__title", children: "Step" }), viewToggleButton] }), _jsx(StepField, { label: "Step", showLabel: false, value: stepTitle, placeholder: STEP_TITLE_PLACEHOLDER, onChange: handleStepTitleChange, autoFocus: autoFocusEnabled && stepTitle.length === 0, focusSignal: mountFocusSignal, multiline: true, disableNewlines: true, enableAutocomplete: true, fieldName: "title", compact: compactCollapsed, compactMode: compactMode, suggestionFilter: (suggestion) => suggestion.isSnippet !== true, onFieldFocus: handleFieldFocus, enableImageUpload: false, showFormattingButtons: true, onImageFile: async (file) => {
461
504
  if (!uploadImage) {
462
505
  return;
463
506
  }
@@ -477,7 +520,7 @@ function TestStepContent({ block, editor, stepNumber, autoFocusEnabled = false,
477
520
  catch (error) {
478
521
  console.error("Failed to upload image to Step Data", error);
479
522
  }
480
- } }), isDataVisible ? (_jsx(StepField, { label: "Step data", placeholder: STEP_DATA_PLACEHOLDER, labelAction: _jsx("button", { type: "button", className: "bn-step-field__dismiss", "data-tooltip": "Hide step data", onClick: handleHideData, "aria-label": "Hide step data", children: "\u00D7" }), value: stepData, onChange: handleStepDataChange, autoFocus: shouldFocusDataField, focusSignal: dataFocusSignal, multiline: true, enableAutocomplete: true, enableImageUpload: true, showFormattingButtons: true, showImageButton: true, onFieldFocus: handleFieldFocus })) : null, isExpectedVisible ? (_jsx(StepField, { label: "Expected result", placeholder: EXPECTED_RESULT_PLACEHOLDER, labelAction: _jsx("button", { type: "button", className: "bn-step-field__dismiss", "data-tooltip": "Hide expected result", onClick: handleHideExpected, tabIndex: -1, "aria-label": "Hide expected result", children: "\u00D7" }), value: expectedResult, onChange: handleExpectedChange, multiline: true, focusSignal: expectedFocusSignal, enableAutocomplete: true, enableImageUpload: true, showFormattingButtons: true, showImageButton: true, onFieldFocus: handleFieldFocus })) : null, _jsxs("div", { className: "bn-step-actions", children: [_jsxs("button", { type: "button", className: "bn-step-action-btn", onClick: handleInsertNextStep, children: [_jsx("svg", { className: "bn-step-action-btn__icon", width: "16", height: "16", viewBox: "0 0 13.334 13.334", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M6.667 0a6.667 6.667 0 1 1 0 13.334A6.667 6.667 0 0 1 6.667 0Zm0 1.334a5.333 5.333 0 1 0 0 10.666 5.333 5.333 0 0 0 0-10.666ZM7.334 3.334V6H10v1.334H7.334V10H6V7.334H3.334V6H6V3.334h1.334Z", fill: "currentColor" }) }), "Add new step"] }), !isDataVisible && (_jsxs("button", { type: "button", className: "bn-step-action-btn", onClick: handleShowData, children: [_jsx("svg", { className: "bn-step-action-btn__icon", width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M8.666 7.333H12.666V8.667H8.666V12.667H7.332V8.667H3.332V7.333H7.332V3.333H8.666V7.333Z", fill: "currentColor" }) }), "Step data"] })), !isExpectedVisible && (_jsxs("button", { type: "button", className: "bn-step-action-btn", onClick: handleShowExpected, children: [_jsx("svg", { className: "bn-step-action-btn__icon", width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M8.666 7.333H12.666V8.667H8.666V12.667H7.332V8.667H3.332V7.333H7.332V3.333H8.666V7.333Z", fill: "currentColor" }) }), "Expected result"] }))] })] })] }));
523
+ } }), isDataVisible ? (_jsx(StepField, { label: "Step data", showLabel: !compactCollapsed, compact: compactCollapsed, compactMode: compactMode, placeholder: STEP_DATA_PLACEHOLDER, labelAction: _jsx("button", { type: "button", className: "bn-step-field__dismiss", "data-tooltip": "Hide step data", onClick: handleHideData, "aria-label": "Hide step data", children: "\u00D7" }), value: stepData, onChange: handleStepDataChange, autoFocus: shouldFocusDataField, focusSignal: dataFocusSignal, multiline: true, enableAutocomplete: true, enableImageUpload: true, showFormattingButtons: true, showImageButton: true, onFieldFocus: handleFieldFocus })) : null, isExpectedVisible ? (_jsx(StepField, { label: "Expected result", showLabel: !compactCollapsed, compact: compactCollapsed, compactMode: compactMode, fieldName: "expected", placeholder: EXPECTED_RESULT_PLACEHOLDER, labelAction: _jsx("button", { type: "button", className: "bn-step-field__dismiss", "data-tooltip": "Hide expected result", onClick: handleHideExpected, tabIndex: -1, "aria-label": "Hide expected result", children: "\u00D7" }), value: expectedResult, onChange: handleExpectedChange, multiline: true, focusSignal: expectedFocusSignal, enableAutocomplete: true, enableImageUpload: true, showFormattingButtons: true, showImageButton: true, onFieldFocus: handleFieldFocus })) : null, _jsxs("div", { className: "bn-step-actions", children: [_jsxs("button", { type: "button", className: "bn-step-action-btn", onClick: handleInsertNextStep, children: [_jsx("svg", { className: "bn-step-action-btn__icon", width: "16", height: "16", viewBox: "0 0 13.334 13.334", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M6.667 0a6.667 6.667 0 1 1 0 13.334A6.667 6.667 0 0 1 6.667 0Zm0 1.334a5.333 5.333 0 1 0 0 10.666 5.333 5.333 0 0 0 0-10.666ZM7.334 3.334V6H10v1.334H7.334V10H6V7.334H3.334V6H6V3.334h1.334Z", fill: "currentColor" }) }), "Add new step"] }), !isDataVisible && (_jsxs("button", { type: "button", className: "bn-step-action-btn", onClick: handleShowData, children: [_jsx("svg", { className: "bn-step-action-btn__icon", width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M8.666 7.333H12.666V8.667H8.666V12.667H7.332V8.667H3.332V7.333H7.332V3.333H8.666V7.333Z", fill: "currentColor" }) }), "Step data"] })), !isExpectedVisible && (_jsxs("button", { type: "button", className: "bn-step-action-btn", onClick: handleShowExpected, children: [_jsx("svg", { className: "bn-step-action-btn__icon", width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M8.666 7.333H12.666V8.667H8.666V12.667H7.332V8.667H3.332V7.333H7.332V3.333H8.666V7.333Z", fill: "currentColor" }) }), "Expected result"] }))] })] })] }));
481
524
  }
482
525
  export const stepBlock = createReactBlockSpec({
483
526
  type: "testStep",
@@ -30,6 +30,19 @@ type StepFieldProps = {
30
30
  showFormattingButtons?: boolean;
31
31
  showImageButton?: boolean;
32
32
  onFieldFocus?: () => void;
33
+ /**
34
+ * Reading-focused presentation: suppresses the toolbar and tightens the
35
+ * field so it reads like plain text. The OverType editor stays mounted so
36
+ * focusing the field (which expands the step) preserves the caret.
37
+ */
38
+ compact?: boolean;
39
+ /**
40
+ * True whenever the step's view is compact, regardless of whether this field
41
+ * is currently collapsed or expanded. Used to drop the editor's tall
42
+ * min-height floor so reading rows hug their content — kept separate from
43
+ * `compact` so it stays stable across focus and never re-lays-out on expand.
44
+ */
45
+ compactMode?: boolean;
33
46
  };
34
47
  export type LinkMeta = {
35
48
  start: number;
@@ -54,5 +67,5 @@ export declare function applyInlineExclusion(formatting: FormattingMeta[], links
54
67
  links: LinkMeta[];
55
68
  };
56
69
  export declare function buildFullMarkdown(plainText: string, links: LinkMeta[], formatting: FormattingMeta[]): string;
57
- export declare function StepField({ label, showLabel, labelToggle, labelAction, placeholder, value, onChange, autoFocus, focusSignal, multiline, disableNewlines, enableAutocomplete, fieldName, suggestionFilter, suggestionsOverride, onSuggestionSelect, readOnly, showSuggestionsOnFocus, enableImageUpload, onImageFile, rightAction, showFormattingButtons, showImageButton, onFieldFocus, }: StepFieldProps): import("react/jsx-runtime").JSX.Element;
70
+ export declare function StepField({ label, showLabel, labelToggle, labelAction, placeholder, value, onChange, autoFocus, focusSignal, multiline, disableNewlines, enableAutocomplete, fieldName, suggestionFilter, suggestionsOverride, onSuggestionSelect, readOnly, showSuggestionsOnFocus, enableImageUpload, onImageFile, rightAction, showFormattingButtons, showImageButton, onFieldFocus, compact, compactMode, }: StepFieldProps): import("react/jsx-runtime").JSX.Element;
58
71
  export {};
@@ -532,7 +532,7 @@ function markdownToPlainText(markdown) {
532
532
  return markdown.replace(/!\[[^\]]*]\([^)]+\)/g, "").replace(/\[[^\]]*]\([^)]+\)/g, "").replace(/[*_`~]/g, "").replace(/\s+/g, " ").trim();
533
533
  }
534
534
  }
535
- export function StepField({ label, showLabel = true, labelToggle, labelAction, placeholder, value, onChange, autoFocus, focusSignal, multiline = false, disableNewlines = false, enableAutocomplete = false, fieldName, suggestionFilter, suggestionsOverride, onSuggestionSelect, readOnly = false, showSuggestionsOnFocus = false, enableImageUpload = false, onImageFile, rightAction, showFormattingButtons = false, showImageButton = false, onFieldFocus, }) {
535
+ export function StepField({ label, showLabel = true, labelToggle, labelAction, placeholder, value, onChange, autoFocus, focusSignal, multiline = false, disableNewlines = false, enableAutocomplete = false, fieldName, suggestionFilter, suggestionsOverride, onSuggestionSelect, readOnly = false, showSuggestionsOnFocus = false, enableImageUpload = false, onImageFile, rightAction, showFormattingButtons = false, showImageButton = false, onFieldFocus, compact = false, compactMode = false, }) {
536
536
  var _a, _b;
537
537
  const stepSuggestions = useStepAutocomplete();
538
538
  const suggestions = suggestionsOverride !== null && suggestionsOverride !== void 0 ? suggestionsOverride : stepSuggestions;
@@ -762,6 +762,26 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
762
762
  (_a = instance === null || instance === void 0 ? void 0 : instance._updateAutoHeight) === null || _a === void 0 ? void 0 : _a.call(instance);
763
763
  }, []),
764
764
  });
765
+ // In compact mode, drop OverType's tall min-height floor so reading rows hug
766
+ // their content. Mutating options.minHeight + recomputing avoids a re-init,
767
+ // so caret and value survive. Driven by the stable compactMode flag (not
768
+ // `compact`) so collapsed and expanded share one height — focusing never
769
+ // shifts the layout.
770
+ useEffect(() => {
771
+ var _a;
772
+ const instance = editorInstanceRef.current;
773
+ if (!(instance === null || instance === void 0 ? void 0 : instance.options)) {
774
+ return;
775
+ }
776
+ instance.options.minHeight = compactMode ? "0px" : multiline ? "4rem" : "2.5rem";
777
+ // A textarea's default rows=2 floors its scrollHeight at two lines, which
778
+ // autoResize then locks the box into; rows=1 lets compact rows hug a single
779
+ // line (autoResize still grows it for multi-line content).
780
+ if (instance.textarea) {
781
+ instance.textarea.rows = compactMode ? 1 : 2;
782
+ }
783
+ (_a = instance._updateAutoHeight) === null || _a === void 0 ? void 0 : _a.call(instance);
784
+ }, [compactMode, multiline, textareaNode]);
765
785
  useEffect(() => {
766
786
  var _a;
767
787
  const instance = editorInstanceRef.current;
@@ -1434,8 +1454,9 @@ export function StepField({ label, showLabel = true, labelToggle, labelAction, p
1434
1454
  ]
1435
1455
  .filter(Boolean)
1436
1456
  .join(" ");
1437
- const showToolbar = showFormattingButtons || (enableImageUpload && uploadImage && showImageButton) || Boolean(rightAction) || enableAutocomplete;
1438
- return (_jsxs("div", { className: "bn-step-field", children: [showLabel && (_jsxs("div", { className: "bn-step-field__top", children: [_jsx("div", { className: "bn-step-field__label-row", children: labelToggle ? (_jsx("span", { className: "bn-step-field__label bn-step-field__label--toggle", role: "button", tabIndex: -1, onClick: labelToggle.onClick, onKeyDown: (event) => {
1457
+ const showToolbar = !compact &&
1458
+ (showFormattingButtons || (enableImageUpload && uploadImage && showImageButton) || Boolean(rightAction) || enableAutocomplete);
1459
+ return (_jsxs("div", { className: `bn-step-field${compact ? " bn-step-field--compact" : ""}`, children: [showLabel && (_jsxs("div", { className: "bn-step-field__top", children: [_jsx("div", { className: "bn-step-field__label-row", children: labelToggle ? (_jsx("span", { className: "bn-step-field__label bn-step-field__label--toggle", role: "button", tabIndex: -1, onClick: labelToggle.onClick, onKeyDown: (event) => {
1439
1460
  if (event.key === "Enter" || event.key === " ") {
1440
1461
  event.preventDefault();
1441
1462
  labelToggle.onClick();
@@ -546,6 +546,99 @@ html.dark .bn-step-editor .overtype-wrapper .overtype-preview a.step-preview-lin
546
546
  min-height: 28px;
547
547
  }
548
548
 
549
+ /* Compact (reading-focused) view: tight rows with no chrome until a field is
550
+ focused, at which point the step expands to the normal editing layout.
551
+
552
+ Structural rules below apply to the whole compact mode (both collapsed and
553
+ expanded) so that expanding a step never shifts the field's position — the
554
+ view-toggle floats out of flow and the editor keeps its default padding, so
555
+ focusing only reveals chrome *below* the field instead of pushing it down. */
556
+ .bn-teststep--compact .bn-teststep__header {
557
+ position: absolute;
558
+ top: 0;
559
+ right: 0;
560
+ min-height: 0;
561
+ z-index: 2;
562
+ }
563
+
564
+ /* Tighten the gap between the step title and its data/expected fields while
565
+ reading. Applied to the whole compact mode (not just collapsed) so the
566
+ spacing is identical when a field expands — no jump on focus. */
567
+ .bn-teststep--compact .bn-teststep__content {
568
+ gap: 6px;
569
+ }
570
+
571
+ .bn-teststep--compact .bn-teststep__content > .bn-teststep__header + .bn-step-field {
572
+ margin-top: 0;
573
+ }
574
+
575
+ /* Collapsed-only rules below hide the remaining chrome for reading. None of
576
+ them change the field's top position, so toggling them on focus is jump-free
577
+ (the border only changes colour, min-height only grows the box downward). */
578
+ .bn-teststep--collapsed .bn-step-field__input {
579
+ border-color: transparent;
580
+ }
581
+
582
+ .bn-teststep--collapsed .bn-step-field__input:hover {
583
+ border-color: var(--step-input-border);
584
+ }
585
+
586
+ /* Zero the whole min-height chain in compact mode so reading rows hug their
587
+ content. OverType floors the textarea height at its CSS min-height (which the
588
+ wrapper inherits as 100% of the editor's old 4rem box), so dropping only the
589
+ OverType minHeight option isn't enough — the textarea's own min-height must
590
+ go too. Applied to all of compact (not just collapsed) so the height is
591
+ identical when a field expands: no jump on focus. Vertical padding is also
592
+ tightened consistently for the same reason. */
593
+ /* OverType injects its own stylesheet (after ours) that hard-codes
594
+ `.overtype-container.overtype-auto-resize .overtype-wrapper { min-height: 60px }`
595
+ with the same specificity we'd normally use, so it wins on source order. The
596
+ extra `.overtype-container` qualifier below raises our specificity above it. */
597
+ .bn-teststep--compact .bn-step-field__input--multiline,
598
+ .bn-teststep--compact .bn-step-editor,
599
+ .bn-teststep--compact .bn-step-editor--multiline,
600
+ .bn-teststep--compact .bn-step-editor .overtype-container .overtype-wrapper,
601
+ .bn-teststep--compact .bn-step-editor .overtype-container .overtype-wrapper .overtype-input,
602
+ .bn-teststep--compact .bn-step-editor .overtype-container .overtype-wrapper .overtype-preview {
603
+ min-height: 0 !important;
604
+ }
605
+
606
+ .bn-teststep--compact .bn-step-editor .overtype-wrapper .overtype-input,
607
+ .bn-teststep--compact .bn-step-editor .overtype-wrapper .overtype-preview {
608
+ padding: 4px 12px !important;
609
+ }
610
+
611
+ .bn-teststep--collapsed .bn-step-actions {
612
+ display: none;
613
+ }
614
+
615
+ /* Prefix the expected-result text with a small "Expected" label while reading,
616
+ so the line reads e.g. "Expected Login form is shown". OverType wraps each
617
+ line in its own block element, so the badge goes on the first line element to
618
+ sit inline before the text. It's a pseudo-element (not editable text), so it
619
+ never enters the serialized value. */
620
+ .bn-teststep--collapsed [data-step-field="expected"] .overtype-wrapper .overtype-preview > :first-child::before {
621
+ content: "Expected";
622
+ display: inline-block;
623
+ margin-right: 8px;
624
+ padding: 0 6px;
625
+ border-radius: 4px;
626
+ background: var(--step-bg-light);
627
+ color: var(--step-muted);
628
+ font-size: 11px;
629
+ font-weight: 600;
630
+ line-height: 18px;
631
+ vertical-align: 1px;
632
+ }
633
+
634
+ .bn-teststep__view-toggle--compact svg {
635
+ color: var(--step-muted);
636
+ }
637
+
638
+ html.dark .bn-teststep__view-toggle--compact svg {
639
+ color: var(--step-muted);
640
+ }
641
+
549
642
 
550
643
  .bn-snippet .bn-step-field__input {
551
644
  border-color: var(--snippet-border-light);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testomatio-editor-blocks",
3
- "version": "0.4.68",
3
+ "version": "0.4.69",
4
4
  "description": "Custom BlockNote schema, markdown conversion helpers, and UI for Testomatio-style test cases and steps.",
5
5
  "type": "module",
6
6
  "main": "./package/index.js",
@@ -1,5 +1,5 @@
1
1
  import { createReactBlockSpec, useEditorChange } from "@blocknote/react";
2
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import { useCallback, useEffect, useMemo, useRef, useState, type FocusEvent } from "react";
3
3
  import { StepField } from "./stepField";
4
4
  import { StepHorizontalView } from "./stepHorizontalView";
5
5
  import { useDeferredMount } from "./useDeferredMount";
@@ -11,7 +11,7 @@ const VIEW_MODE_KEY = "bn-step-view-mode";
11
11
  const STEP_TITLE_PLACEHOLDER = "Enter step title...";
12
12
  const STEP_DATA_PLACEHOLDER = "Enter step data...";
13
13
  const EXPECTED_RESULT_PLACEHOLDER = "Enter expected result...";
14
- type StepViewMode = "vertical" | "horizontal";
14
+ type StepViewMode = "vertical" | "horizontal" | "compact";
15
15
  const FORCE_VERTICAL_WIDTH = 550;
16
16
 
17
17
  /* readExpectedCollapsedPreference removed — currently unused */
@@ -32,7 +32,8 @@ const readStepViewMode = (): StepViewMode => {
32
32
  return "vertical";
33
33
  }
34
34
  try {
35
- return window.localStorage.getItem(VIEW_MODE_KEY) === "horizontal" ? "horizontal" : "vertical";
35
+ const stored = window.localStorage.getItem(VIEW_MODE_KEY);
36
+ return stored === "horizontal" || stored === "compact" ? stored : "vertical";
36
37
  } catch {
37
38
  return "vertical";
38
39
  }
@@ -413,6 +414,9 @@ function TestStepContent({
413
414
  const [viewMode, setViewMode] = useState<StepViewMode>(() => readStepViewMode());
414
415
  const containerRef = useRef<HTMLDivElement>(null);
415
416
  const [forceVertical, setForceVertical] = useState(false);
417
+ // In compact mode each step collapses to a reading-focused row and only
418
+ // expands to the full editing layout while one of its fields has focus.
419
+ const [expanded, setExpanded] = useState(false);
416
420
 
417
421
  useEffect(() => {
418
422
  const el = containerRef.current?.parentElement;
@@ -426,7 +430,11 @@ function TestStepContent({
426
430
  return () => observer.disconnect();
427
431
  }, []);
428
432
 
429
- const effectiveVertical = forceVertical || viewMode === "vertical";
433
+ const compactMode = viewMode === "compact";
434
+ const effectiveHorizontal = viewMode === "horizontal" && !forceVertical;
435
+ // Compact steps render the vertical layout but collapse their chrome until
436
+ // a field gains focus, at which point the step expands to "normal" editing.
437
+ const compactCollapsed = compactMode && !expanded;
430
438
 
431
439
  useEffect(() => {
432
440
  if (typeof window === "undefined") {
@@ -569,14 +577,45 @@ function TestStepContent({
569
577
  }, [editor, block.id]);
570
578
 
571
579
  const handleToggleView = useCallback(() => {
572
- const next = viewMode === "horizontal" ? "vertical" : "horizontal";
580
+ // Cycle vertical horizontal compact → vertical. Skip horizontal when
581
+ // the container is too narrow to fit its two columns.
582
+ let next: StepViewMode;
583
+ if (viewMode === "vertical") {
584
+ next = forceVertical ? "compact" : "horizontal";
585
+ } else if (viewMode === "horizontal") {
586
+ next = "compact";
587
+ } else {
588
+ next = "vertical";
589
+ }
573
590
  writeStepViewMode(next);
574
591
  setViewMode(next);
575
592
  if (typeof window !== "undefined") {
576
593
  window.dispatchEvent(new Event("bn-step-view-mode"));
577
594
  }
595
+ }, [viewMode, forceVertical]);
596
+
597
+ const handleContentFocusCapture = useCallback(() => {
598
+ if (viewMode === "compact") {
599
+ setExpanded(true);
600
+ }
578
601
  }, [viewMode]);
579
602
 
603
+ const handleContentBlurCapture = useCallback(
604
+ (event: FocusEvent<HTMLDivElement>) => {
605
+ if (viewMode !== "compact") {
606
+ return;
607
+ }
608
+ // Keep the step expanded while focus stays inside it (e.g. moving to a
609
+ // toolbar or action button); collapse only when focus leaves entirely.
610
+ const nextTarget = event.relatedTarget as Node | null;
611
+ if (nextTarget && event.currentTarget.contains(nextTarget)) {
612
+ return;
613
+ }
614
+ setExpanded(false);
615
+ },
616
+ [viewMode],
617
+ );
618
+
580
619
  const [dataFocusSignal] = useState(0);
581
620
  const [expectedFocusSignal, setExpectedFocusSignal] = useState(0);
582
621
 
@@ -592,28 +631,42 @@ function TestStepContent({
592
631
  editor.updateBlock(block.id, { props: { expectedResult: "" } });
593
632
  }, [editor, block.id]);
594
633
 
634
+ const nextViewLabel =
635
+ viewMode === "compact"
636
+ ? "Switch to vertical view"
637
+ : viewMode === "horizontal"
638
+ ? "Switch to compact view"
639
+ : forceVertical
640
+ ? "Switch to compact view"
641
+ : "Switch to horizontal view";
642
+
595
643
  const viewToggleButton = (
596
644
  <button
597
645
  type="button"
598
- className={`bn-teststep__view-toggle${!effectiveVertical ? " bn-teststep__view-toggle--horizontal" : ""}${forceVertical ? " bn-teststep__view-toggle--disabled" : ""}`}
599
- data-tooltip={forceVertical ? "Not enough space for horizontal view" : "Switch step view"}
600
- aria-label={forceVertical ? "Not enough space for horizontal view" : "Switch step view"}
601
- onClick={forceVertical ? undefined : handleToggleView}
602
- aria-disabled={forceVertical}
646
+ className={`bn-teststep__view-toggle${effectiveHorizontal ? " bn-teststep__view-toggle--horizontal" : ""}${compactMode ? " bn-teststep__view-toggle--compact" : ""}`}
647
+ data-tooltip={nextViewLabel}
648
+ aria-label={nextViewLabel}
649
+ onClick={handleToggleView}
603
650
  tabIndex={-1}
604
651
  >
605
- <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
606
- <mask id="mask-toggle" style={{maskType: "alpha"}} maskUnits="userSpaceOnUse" x="0" y="0" width="16" height="16">
607
- <rect width="16" height="16" fill="#D9D9D9"/>
608
- </mask>
609
- <g mask="url(#mask-toggle)">
610
- <path d="M12.6667 2C13.0333 2 13.3472 2.13056 13.6083 2.39167C13.8694 2.65278 14 2.96667 14 3.33333L14 12.6667C14 13.0333 13.8694 13.3472 13.6083 13.6083C13.3472 13.8694 13.0333 14 12.6667 14L10 14C9.63333 14 9.31944 13.8694 9.05833 13.6083C8.79722 13.3472 8.66667 13.0333 8.66667 12.6667L8.66667 3.33333C8.66667 2.96667 8.79722 2.65278 9.05833 2.39167C9.31945 2.13055 9.63333 2 10 2L12.6667 2ZM6 2C6.36667 2 6.68056 2.13055 6.94167 2.39167C7.20278 2.65278 7.33333 2.96667 7.33333 3.33333L7.33333 12.6667C7.33333 13.0333 7.20278 13.3472 6.94167 13.6083C6.68055 13.8694 6.36667 14 6 14L3.33333 14C2.96667 14 2.65278 13.8694 2.39167 13.6083C2.13056 13.3472 2 13.0333 2 12.6667L2 3.33333C2 2.96667 2.13056 2.65278 2.39167 2.39167C2.65278 2.13055 2.96667 2 3.33333 2L6 2ZM3.33333 12.6667L6 12.6667L6 3.33333L3.33333 3.33333L3.33333 12.6667Z" fill="currentColor"/>
611
- </g>
612
- </svg>
652
+ {compactMode ? (
653
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
654
+ <path d="M2 3.333h12V4.667H2V3.333Zm0 4h12v1.334H2V7.333Zm0 4h12v1.334H2v-1.334Z" fill="currentColor"/>
655
+ </svg>
656
+ ) : (
657
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
658
+ <mask id="mask-toggle" style={{maskType: "alpha"}} maskUnits="userSpaceOnUse" x="0" y="0" width="16" height="16">
659
+ <rect width="16" height="16" fill="#D9D9D9"/>
660
+ </mask>
661
+ <g mask="url(#mask-toggle)">
662
+ <path d="M12.6667 2C13.0333 2 13.3472 2.13056 13.6083 2.39167C13.8694 2.65278 14 2.96667 14 3.33333L14 12.6667C14 13.0333 13.8694 13.3472 13.6083 13.6083C13.3472 13.8694 13.0333 14 12.6667 14L10 14C9.63333 14 9.31944 13.8694 9.05833 13.6083C8.79722 13.3472 8.66667 13.0333 8.66667 12.6667L8.66667 3.33333C8.66667 2.96667 8.79722 2.65278 9.05833 2.39167C9.31945 2.13055 9.63333 2 10 2L12.6667 2ZM6 2C6.36667 2 6.68056 2.13055 6.94167 2.39167C7.20278 2.65278 7.33333 2.96667 7.33333 3.33333L7.33333 12.6667C7.33333 13.0333 7.20278 13.3472 6.94167 13.6083C6.68055 13.8694 6.36667 14 6 14L3.33333 14C2.96667 14 2.65278 13.8694 2.39167 13.6083C2.13056 13.3472 2 13.0333 2 12.6667L2 3.33333C2 2.96667 2.13056 2.65278 2.39167 2.39167C2.65278 2.13055 2.96667 2 3.33333 2L6 2ZM3.33333 12.6667L6 12.6667L6 3.33333L3.33333 3.33333L3.33333 12.6667Z" fill="currentColor"/>
663
+ </g>
664
+ </svg>
665
+ )}
613
666
  </button>
614
667
  );
615
668
 
616
- if (!effectiveVertical) {
669
+ if (effectiveHorizontal) {
617
670
  return (
618
671
  <StepHorizontalView
619
672
  ref={containerRef}
@@ -632,14 +685,22 @@ function TestStepContent({
632
685
  }
633
686
 
634
687
  return (
635
- <div className="bn-teststep" data-block-id={block.id} ref={containerRef}>
688
+ <div
689
+ className={`bn-teststep${compactMode ? " bn-teststep--compact" : ""}${compactCollapsed ? " bn-teststep--collapsed" : ""}`}
690
+ data-block-id={block.id}
691
+ ref={containerRef}
692
+ >
636
693
  <div className="bn-teststep__timeline">
637
694
  <span className="bn-teststep__number">{stepNumber}</span>
638
695
  <div className="bn-teststep__line" />
639
696
  </div>
640
- <div className="bn-teststep__content">
697
+ <div
698
+ className="bn-teststep__content"
699
+ onFocus={handleContentFocusCapture}
700
+ onBlur={handleContentBlurCapture}
701
+ >
641
702
  <div className="bn-teststep__header">
642
- <span className="bn-teststep__title">Step</span>
703
+ {!compactMode && <span className="bn-teststep__title">Step</span>}
643
704
  {viewToggleButton}
644
705
  </div>
645
706
  <StepField
@@ -654,6 +715,8 @@ function TestStepContent({
654
715
  disableNewlines
655
716
  enableAutocomplete
656
717
  fieldName="title"
718
+ compact={compactCollapsed}
719
+ compactMode={compactMode}
657
720
  suggestionFilter={(suggestion) => (suggestion as StepSuggestion).isSnippet !== true}
658
721
  onFieldFocus={handleFieldFocus}
659
722
  enableImageUpload={false}
@@ -683,6 +746,9 @@ function TestStepContent({
683
746
  {isDataVisible ? (
684
747
  <StepField
685
748
  label="Step data"
749
+ showLabel={!compactCollapsed}
750
+ compact={compactCollapsed}
751
+ compactMode={compactMode}
686
752
  placeholder={STEP_DATA_PLACEHOLDER}
687
753
  labelAction={
688
754
  <button
@@ -710,6 +776,10 @@ function TestStepContent({
710
776
  {isExpectedVisible ? (
711
777
  <StepField
712
778
  label="Expected result"
779
+ showLabel={!compactCollapsed}
780
+ compact={compactCollapsed}
781
+ compactMode={compactMode}
782
+ fieldName="expected"
713
783
  placeholder={EXPECTED_RESULT_PLACEHOLDER}
714
784
  labelAction={
715
785
  <button
@@ -39,6 +39,19 @@ type StepFieldProps = {
39
39
  showFormattingButtons?: boolean;
40
40
  showImageButton?: boolean;
41
41
  onFieldFocus?: () => void;
42
+ /**
43
+ * Reading-focused presentation: suppresses the toolbar and tightens the
44
+ * field so it reads like plain text. The OverType editor stays mounted so
45
+ * focusing the field (which expands the step) preserves the caret.
46
+ */
47
+ compact?: boolean;
48
+ /**
49
+ * True whenever the step's view is compact, regardless of whether this field
50
+ * is currently collapsed or expanded. Used to drop the editor's tall
51
+ * min-height floor so reading rows hug their content — kept separate from
52
+ * `compact` so it stays stable across focus and never re-lays-out on expand.
53
+ */
54
+ compactMode?: boolean;
42
55
  };
43
56
 
44
57
  const READ_ONLY_ALLOWED_KEYS = new Set([
@@ -686,6 +699,8 @@ export function StepField({
686
699
  showFormattingButtons = false,
687
700
  showImageButton = false,
688
701
  onFieldFocus,
702
+ compact = false,
703
+ compactMode = false,
689
704
  }: StepFieldProps) {
690
705
  const stepSuggestions = useStepAutocomplete();
691
706
  const suggestions = suggestionsOverride ?? stepSuggestions;
@@ -956,6 +971,32 @@ export function StepField({
956
971
  }, []),
957
972
  });
958
973
 
974
+ // In compact mode, drop OverType's tall min-height floor so reading rows hug
975
+ // their content. Mutating options.minHeight + recomputing avoids a re-init,
976
+ // so caret and value survive. Driven by the stable compactMode flag (not
977
+ // `compact`) so collapsed and expanded share one height — focusing never
978
+ // shifts the layout.
979
+ useEffect(() => {
980
+ const instance = editorInstanceRef.current as
981
+ | (OverTypeInstance & {
982
+ options?: { minHeight?: string };
983
+ textarea?: HTMLTextAreaElement;
984
+ _updateAutoHeight?: () => void;
985
+ })
986
+ | null;
987
+ if (!instance?.options) {
988
+ return;
989
+ }
990
+ instance.options.minHeight = compactMode ? "0px" : multiline ? "4rem" : "2.5rem";
991
+ // A textarea's default rows=2 floors its scrollHeight at two lines, which
992
+ // autoResize then locks the box into; rows=1 lets compact rows hug a single
993
+ // line (autoResize still grows it for multi-line content).
994
+ if (instance.textarea) {
995
+ instance.textarea.rows = compactMode ? 1 : 2;
996
+ }
997
+ instance._updateAutoHeight?.();
998
+ }, [compactMode, multiline, textareaNode]);
999
+
959
1000
  useEffect(() => {
960
1001
  const instance = editorInstanceRef.current;
961
1002
  if (!instance) {
@@ -1752,10 +1793,11 @@ export function StepField({
1752
1793
  .join(" ");
1753
1794
 
1754
1795
  const showToolbar =
1755
- showFormattingButtons || (enableImageUpload && uploadImage && showImageButton) || Boolean(rightAction) || enableAutocomplete;
1796
+ !compact &&
1797
+ (showFormattingButtons || (enableImageUpload && uploadImage && showImageButton) || Boolean(rightAction) || enableAutocomplete);
1756
1798
 
1757
1799
  return (
1758
- <div className="bn-step-field">
1800
+ <div className={`bn-step-field${compact ? " bn-step-field--compact" : ""}`}>
1759
1801
  {showLabel && (
1760
1802
  <div className="bn-step-field__top">
1761
1803
  <div className="bn-step-field__label-row">
@@ -546,6 +546,99 @@ html.dark .bn-step-editor .overtype-wrapper .overtype-preview a.step-preview-lin
546
546
  min-height: 28px;
547
547
  }
548
548
 
549
+ /* Compact (reading-focused) view: tight rows with no chrome until a field is
550
+ focused, at which point the step expands to the normal editing layout.
551
+
552
+ Structural rules below apply to the whole compact mode (both collapsed and
553
+ expanded) so that expanding a step never shifts the field's position — the
554
+ view-toggle floats out of flow and the editor keeps its default padding, so
555
+ focusing only reveals chrome *below* the field instead of pushing it down. */
556
+ .bn-teststep--compact .bn-teststep__header {
557
+ position: absolute;
558
+ top: 0;
559
+ right: 0;
560
+ min-height: 0;
561
+ z-index: 2;
562
+ }
563
+
564
+ /* Tighten the gap between the step title and its data/expected fields while
565
+ reading. Applied to the whole compact mode (not just collapsed) so the
566
+ spacing is identical when a field expands — no jump on focus. */
567
+ .bn-teststep--compact .bn-teststep__content {
568
+ gap: 6px;
569
+ }
570
+
571
+ .bn-teststep--compact .bn-teststep__content > .bn-teststep__header + .bn-step-field {
572
+ margin-top: 0;
573
+ }
574
+
575
+ /* Collapsed-only rules below hide the remaining chrome for reading. None of
576
+ them change the field's top position, so toggling them on focus is jump-free
577
+ (the border only changes colour, min-height only grows the box downward). */
578
+ .bn-teststep--collapsed .bn-step-field__input {
579
+ border-color: transparent;
580
+ }
581
+
582
+ .bn-teststep--collapsed .bn-step-field__input:hover {
583
+ border-color: var(--step-input-border);
584
+ }
585
+
586
+ /* Zero the whole min-height chain in compact mode so reading rows hug their
587
+ content. OverType floors the textarea height at its CSS min-height (which the
588
+ wrapper inherits as 100% of the editor's old 4rem box), so dropping only the
589
+ OverType minHeight option isn't enough — the textarea's own min-height must
590
+ go too. Applied to all of compact (not just collapsed) so the height is
591
+ identical when a field expands: no jump on focus. Vertical padding is also
592
+ tightened consistently for the same reason. */
593
+ /* OverType injects its own stylesheet (after ours) that hard-codes
594
+ `.overtype-container.overtype-auto-resize .overtype-wrapper { min-height: 60px }`
595
+ with the same specificity we'd normally use, so it wins on source order. The
596
+ extra `.overtype-container` qualifier below raises our specificity above it. */
597
+ .bn-teststep--compact .bn-step-field__input--multiline,
598
+ .bn-teststep--compact .bn-step-editor,
599
+ .bn-teststep--compact .bn-step-editor--multiline,
600
+ .bn-teststep--compact .bn-step-editor .overtype-container .overtype-wrapper,
601
+ .bn-teststep--compact .bn-step-editor .overtype-container .overtype-wrapper .overtype-input,
602
+ .bn-teststep--compact .bn-step-editor .overtype-container .overtype-wrapper .overtype-preview {
603
+ min-height: 0 !important;
604
+ }
605
+
606
+ .bn-teststep--compact .bn-step-editor .overtype-wrapper .overtype-input,
607
+ .bn-teststep--compact .bn-step-editor .overtype-wrapper .overtype-preview {
608
+ padding: 4px 12px !important;
609
+ }
610
+
611
+ .bn-teststep--collapsed .bn-step-actions {
612
+ display: none;
613
+ }
614
+
615
+ /* Prefix the expected-result text with a small "Expected" label while reading,
616
+ so the line reads e.g. "Expected Login form is shown". OverType wraps each
617
+ line in its own block element, so the badge goes on the first line element to
618
+ sit inline before the text. It's a pseudo-element (not editable text), so it
619
+ never enters the serialized value. */
620
+ .bn-teststep--collapsed [data-step-field="expected"] .overtype-wrapper .overtype-preview > :first-child::before {
621
+ content: "Expected";
622
+ display: inline-block;
623
+ margin-right: 8px;
624
+ padding: 0 6px;
625
+ border-radius: 4px;
626
+ background: var(--step-bg-light);
627
+ color: var(--step-muted);
628
+ font-size: 11px;
629
+ font-weight: 600;
630
+ line-height: 18px;
631
+ vertical-align: 1px;
632
+ }
633
+
634
+ .bn-teststep__view-toggle--compact svg {
635
+ color: var(--step-muted);
636
+ }
637
+
638
+ html.dark .bn-teststep__view-toggle--compact svg {
639
+ color: var(--step-muted);
640
+ }
641
+
549
642
 
550
643
  .bn-snippet .bn-step-field__input {
551
644
  border-color: var(--snippet-border-light);