From c962498a340da0d98705bba58520714a4e2294c4 Mon Sep 17 00:00:00 2001 From: DavertMik Date: Wed, 3 Jun 2026 11:03:06 +0300 Subject: [PATCH 1/2] feat: compact (reading) view for TestMeta block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Render the test/suite metadata block as a single truncated one-liner by default (e.g. `priority: high · tags: smoke, login`), with an expand/collapse chevron pinned to the far right. Expanding reveals the full editable key/value rows and the add-field button; collapsing returns to the summary line. - A block with no readable fields starts expanded so it is immediately editable. - Clicking the one-liner (or the chevron) expands it; the summary ellipsizes when it overflows the header. - `expanded` is UI-only local state — the markdown serialization is unchanged. Co-Authored-By: Claude Opus 4.8 --- src/editor/blocks/testMeta.tsx | 40 +++++++++++++++++++-- src/editor/styles.css | 63 +++++++++++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 4 deletions(-) diff --git a/src/editor/blocks/testMeta.tsx b/src/editor/blocks/testMeta.tsx index 4f0f009..0dcb7de 100644 --- a/src/editor/blocks/testMeta.tsx +++ b/src/editor/blocks/testMeta.tsx @@ -187,9 +187,19 @@ export const testMetaBlock = createReactBlockSpec( .map((field, index) => ({ field, index })) .filter(({ field }) => !ID_KEYS.has(field.key.trim().toLowerCase())); + // Compact (reading) view: collapse the rows into a single truncated line, + // with an expand/collapse toggle pinned to the far right that reveals the + // full editable rows. A block with nothing to read starts expanded so it + // is immediately editable. `expanded` is UI-only state — never serialized. + const [expanded, setExpanded] = useState(() => editableFields.length === 0); + const summaryText = editableFields + .filter(({ field }) => field.key.trim().length > 0) + .map(({ field }) => (field.value.trim() ? `${field.key}: ${field.value}` : field.key)) + .join(" · "); + return (
{kind.toUpperCase()} {idField?.value && {idField.value}} - + {!expanded && ( + + )} +
+ {expanded && } + +
- {editableFields.length > 0 && ( + {expanded && editableFields.length > 0 && (
{editableFields.map(({ field, index }) => (
diff --git a/src/editor/styles.css b/src/editor/styles.css index 1d8710d..9aa5f14 100644 --- a/src/editor/styles.css +++ b/src/editor/styles.css @@ -659,8 +659,69 @@ html.dark .bn-step-editor .overtype-wrapper .overtype-preview a.step-preview-lin margin-left: 8px; } -.bn-testmeta__header .bn-testmeta__add-wrap { +/* Add button + expand/collapse toggle live in a right-pinned action group. */ +.bn-testmeta__header .bn-testmeta__actions { margin-left: auto; + display: inline-flex; + align-items: center; + gap: 2px; + flex-shrink: 0; +} + +/* Compact one-liner: the metadata rendered as truncated read text, click to + expand. Takes the remaining header width and ellipsizes when it overflows. */ +.bn-testmeta__summary { + flex: 1 1 auto; + min-width: 0; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + text-align: left; + font-family: inherit; + font-size: 13px; + color: var(--text-muted); + background: transparent; + border: none; + padding: 0; + cursor: pointer; +} + +.bn-testmeta__summary:hover { + color: var(--text-primary); +} + +.bn-testmeta__summary--empty { + font-style: italic; + opacity: 0.7; +} + +.bn-testmeta__toggle { + width: 24px; + height: 24px; + display: inline-flex; + align-items: center; + justify-content: center; + color: var(--text-muted); + background: transparent; + border: none; + border-radius: 6px; + cursor: pointer; + padding: 0; + flex-shrink: 0; + transition: background-color 120ms ease; +} + +.bn-testmeta__toggle:hover { + background: var(--step-bg-button-hover); + color: var(--text-primary); +} + +.bn-testmeta__toggle svg { + transition: transform 150ms ease; +} + +.bn-testmeta__toggle--expanded svg { + transform: rotate(180deg); } .bn-testmeta__label { From afc888186bef01db40c19b4a027e16e50616178f Mon Sep 17 00:00:00 2001 From: DavertMik Date: Thu, 4 Jun 2026 15:53:39 +0300 Subject: [PATCH 2/2] fix: omit keys without values from TestMeta compact summary The collapsed one-liner now lists only fields that have a value; keys with empty values are an editing-only concern and are shown only in the expanded rows. A block with no valued fields starts expanded so the summary is never misleadingly empty. Co-Authored-By: Claude Opus 4.8 --- src/editor/blocks/testMeta.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/editor/blocks/testMeta.tsx b/src/editor/blocks/testMeta.tsx index 0dcb7de..b4ee6ba 100644 --- a/src/editor/blocks/testMeta.tsx +++ b/src/editor/blocks/testMeta.tsx @@ -187,15 +187,19 @@ export const testMetaBlock = createReactBlockSpec( .map((field, index) => ({ field, index })) .filter(({ field }) => !ID_KEYS.has(field.key.trim().toLowerCase())); - // Compact (reading) view: collapse the rows into a single truncated line, - // with an expand/collapse toggle pinned to the far right that reveals the - // full editable rows. A block with nothing to read starts expanded so it - // is immediately editable. `expanded` is UI-only state — never serialized. - const [expanded, setExpanded] = useState(() => editableFields.length === 0); + // Compact (reading) view: collapse the rows into a single truncated line + // built only from fields that actually have a value — keys without values + // are an editing-only concern and never appear in the compact summary (the + // expanded rows still show them so they can be filled in). The + // expand/collapse toggle pinned to the far right reveals the full rows. const summaryText = editableFields - .filter(({ field }) => field.key.trim().length > 0) - .map(({ field }) => (field.value.trim() ? `${field.key}: ${field.value}` : field.key)) + .filter(({ field }) => field.key.trim().length > 0 && field.value.trim().length > 0) + .map(({ field }) => `${field.key}: ${field.value}`) .join(" · "); + // Nothing readable to summarise (no fields, or only empty values) -> start + // expanded so the block is immediately editable. `expanded` is UI-only + // state — never serialized. + const [expanded, setExpanded] = useState(() => summaryText.length === 0); return (