testomatio-editor-blocks 0.4.67 → 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.
- package/package/editor/blocks/step.js +50 -7
- package/package/editor/blocks/stepField.d.ts +14 -1
- package/package/editor/blocks/stepField.js +24 -3
- package/package/styles.css +93 -0
- package/package.json +1 -1
- package/src/editor/blocks/step.tsx +92 -22
- package/src/editor/blocks/stepField.tsx +44 -2
- package/src/editor/styles.css +93 -0
|
@@ -28,7 +28,8 @@ const readStepViewMode = () => {
|
|
|
28
28
|
return "vertical";
|
|
29
29
|
}
|
|
30
30
|
try {
|
|
31
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
457
|
-
|
|
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"
|
|
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 =
|
|
1438
|
-
|
|
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();
|
package/package/styles.css
CHANGED
|
@@ -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,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
|
-
|
|
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
|
|
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
|
-
|
|
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${
|
|
599
|
-
data-tooltip={
|
|
600
|
-
aria-label={
|
|
601
|
-
onClick={
|
|
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
|
-
|
|
606
|
-
<
|
|
607
|
-
<
|
|
608
|
-
</
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
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 (
|
|
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
|
|
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
|
|
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
|
-
|
|
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">
|
package/src/editor/styles.css
CHANGED
|
@@ -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);
|