feat(editor): Schema preview UI updates (#13578)
Some checks are pending
Test Master / install-and-build (push) Waiting to run
Test Master / Unit tests (18.x) (push) Blocked by required conditions
Test Master / Unit tests (20.x) (push) Blocked by required conditions
Test Master / Unit tests (22.4) (push) Blocked by required conditions
Test Master / Lint (push) Blocked by required conditions
Test Master / Notify Slack on failure (push) Blocked by required conditions

This commit is contained in:
Elias Meire 2025-03-05 10:29:13 +01:00 committed by GitHub
parent c821f1c532
commit 8790a0df3d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 148 additions and 163 deletions

View file

@ -314,7 +314,7 @@ const onDragEnd = (el: HTMLElement) => {
@click="toggleNodeAndScrollTop(item.id)"
/>
<VirtualSchemaItem
v-else
v-else-if="item.type === 'item'"
v-bind="item"
:search="search"
:draggable="mappingEnabled"
@ -323,6 +323,10 @@ const onDragEnd = (el: HTMLElement) => {
@click="toggleLeaf(item.id)"
>
</VirtualSchemaItem>
<N8nTooltip v-else-if="item.type === 'icon'" :content="item.tooltip" placement="top">
<N8nIcon :size="14" :icon="item.icon" class="icon" />
</N8nTooltip>
</DynamicScrollerItem>
</template>
</DynamicScroller>
@ -347,4 +351,11 @@ const onDragEnd = (el: HTMLElement) => {
text-align: center;
padding: var(--spacing-s) var(--spacing-s) var(--spacing-xl) var(--spacing-s);
}
.icon {
display: inline-flex;
margin-left: var(--spacing-xl);
color: var(--color-text-light);
margin-bottom: var(--spacing-s);
}
</style>

View file

@ -4,8 +4,7 @@ import NodeIcon from '@/components/NodeIcon.vue';
import { type INodeTypeDescription } from 'n8n-workflow';
import { useI18n } from '@/composables/useI18n';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import { DATA_EDITING_DOCS_URL } from '@/constants';
import { N8nNotice } from '@n8n/design-system';
import { SCHEMA_PREVIEW_DOCS_URL } from '@/constants';
const props = defineProps<{
title: string;
@ -42,24 +41,28 @@ const emit = defineEmits<{
<span v-if="info" class="info">{{ info }}</span>
</div>
<FontAwesomeIcon v-if="isTrigger" class="trigger-icon" icon="bolt" size="xs" />
<div v-if="itemCount" class="item-count" data-test-id="run-data-schema-node-item-count">
<div v-if="itemCount" class="extra-info" data-test-id="run-data-schema-node-item-count">
{{ i18n.baseText('ndv.output.items', { interpolate: { count: itemCount } }) }}
</div>
<div v-else-if="preview" class="extra-info">
{{ i18n.baseText('dataMapping.schemaView.previewNode') }}
</div>
<N8nNotice
</div>
<div
v-if="preview && !collapsed"
class="notice"
theme="warning"
data-test-id="schema-preview-warning"
@click.stop
>
<i18n-t keypath="dataMapping.schemaView.preview">
<template #link>
<N8nLink :to="DATA_EDITING_DOCS_URL" size="small">
<N8nLink :to="SCHEMA_PREVIEW_DOCS_URL" size="small" bold>
{{ i18n.baseText('generic.learnMore') }}
</N8nLink>
</template>
</i18n-t>
</N8nNotice>
</div>
</div>
</template>
@ -117,7 +120,7 @@ const emit = defineEmits<{
color: var(--color-primary);
}
.item-count {
.extra-info {
font-size: var(--font-size-2xs);
color: var(--color-text-light);
margin-left: auto;
@ -126,6 +129,9 @@ const emit = defineEmits<{
.notice {
margin-left: var(--spacing-2xl);
margin-top: var(--spacing-2xs);
margin-bottom: 0;
padding-bottom: var(--spacing-2xs);
color: var(--color-text-base);
font-size: var(--font-size-2xs);
line-height: var(--font-line-height-loose);
}
</style>

View file

@ -43,7 +43,10 @@ const emit = defineEmits<{
:data-node-type="nodeType"
data-target="mappable"
class="pill"
:class="{ 'pill--highlight': highlight, 'pill--preview': preview }"
:class="{
'pill--highlight': highlight,
'pill--preview': preview,
}"
data-test-id="run-data-schema-node-name"
>
<FontAwesomeIcon class="type-icon" :icon size="sm" />
@ -77,6 +80,7 @@ const emit = defineEmits<{
justify-content: center;
cursor: pointer;
font-size: var(--font-size-s);
color: var(--color-text-light);
}
.pill {
@ -98,16 +102,31 @@ const emit = defineEmits<{
}
&.pill--preview {
border-style: dashed;
border-width: 1.5px;
/* Cannot use CSS variable inside data URL, so instead switching based on data-theme and media query */
--schema-preview-dashed-border: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' viewBox='0 0 400 400' fill='none' rx='4' ry='4' stroke='%230000002A' stroke-width='2' stroke-dasharray='4%2c 4' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e");
--schema-preview-dashed-border-dark: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' viewBox='0 0 400 400' fill='none' rx='4' ry='4' stroke='%23FFFFFF2A' stroke-width='2' stroke-dasharray='4%2c 4' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e");
color: var(--color-text-light);
background-color: var(--color-run-data-background);
border: none;
max-width: calc(100% - var(--spacing-l));
background-image: var(--schema-preview-dashed-border);
.title {
color: var(--color-text-light);
border-left: 1.5px dashed var(--color-foreground-light);
}
}
}
@media (prefers-color-scheme: dark) {
body:not([data-theme]) .pill--preview {
background-image: var(--schema-preview-dashed-border-dark);
}
}
[data-theme='dark'] .pill--preview {
background-image: var(--schema-preview-dashed-border-dark);
}
.draggable .pill.pill--highlight {
color: var(--color-primary);
border-color: var(--color-primary-tint-1);

View file

@ -110,7 +110,12 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
swapopacity="false"
symbol="false"
/>
<!--v-if-->
<div
class="extra-info"
data-v-882a318e=""
>
Preview
</div>
</div>
<div
class="notice"
@ -119,13 +124,12 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
theme="warning"
>
This is a preview of the schema, execute the node to see the exact schema and data.
Usually outputs the following fields. Execute the node to see the actual ones.
<a
class="n8n-link"
data-v-882a318e=""
href="https://docs.n8n.io/data/data-editing/"
href="https://docs.n8n.io/data/schema-preview/"
target="_blank"
>
@ -133,7 +137,7 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
class="primary"
>
<span
class="n8n-text size-small regular"
class="n8n-text size-small bold"
>
@ -146,7 +150,6 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
</a>
</div>
</div>
@ -328,70 +331,38 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
<div
class="schema-item draggable"
data-test-id="run-data-schema-item"
data-v-0f5e7239=""
<span
class="n8n-text compact size-14 regular n8n-icon icon el-tooltip__trigger el-tooltip__trigger icon el-tooltip__trigger el-tooltip__trigger n8n-icon icon el-tooltip__trigger el-tooltip__trigger icon el-tooltip__trigger el-tooltip__trigger"
data-v-d00cba9a=""
type="item"
>
<div
class="toggle-container"
data-v-0f5e7239=""
>
<!--v-if-->
</div>
<div
class="pill pill--preview"
data-name="..."
data-nest-level="1"
data-target="mappable"
data-test-id="run-data-schema-node-name"
data-v-0f5e7239=""
>
<font-awesome-icon-stub
beat="false"
beatfade="false"
border="false"
bounce="false"
class="type-icon"
data-v-0f5e7239=""
class="14"
fade="false"
fixedwidth="false"
flash="false"
flip="false"
icon=""
icon="ellipsis-h"
inverse="false"
listitem="false"
pulse="false"
shake="false"
size="sm"
spin="false"
spinpulse="false"
spinreverse="false"
swapopacity="false"
symbol="false"
/>
<span
class="content title"
data-v-0f5e7239=""
>
<span>
<!--v-if-->
...
</span>
</span>
</div>
<span
class="content text"
data-test-id="run-data-schema-item-value"
data-v-0f5e7239=""
>
<span />
</span>
</div>
<!--teleport start-->
<!--teleport end-->
@ -464,7 +435,12 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
<!--v-if-->
</div>
<!--v-if-->
<!--v-if-->
<div
class="extra-info"
data-v-882a318e=""
>
Preview
</div>
</div>
<div
class="notice"
@ -473,13 +449,12 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
theme="warning"
>
This is a preview of the schema, execute the node to see the exact schema and data.
Usually outputs the following fields. Execute the node to see the actual ones.
<a
class="n8n-link"
data-v-882a318e=""
href="https://docs.n8n.io/data/data-editing/"
href="https://docs.n8n.io/data/schema-preview/"
target="_blank"
>
@ -487,7 +462,7 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
class="primary"
>
<span
class="n8n-text size-small regular"
class="n8n-text size-small bold"
>
@ -500,7 +475,6 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
</a>
</div>
</div>
@ -682,70 +656,38 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
<div
class="schema-item draggable"
data-test-id="run-data-schema-item"
data-v-0f5e7239=""
<span
class="n8n-text compact size-14 regular n8n-icon icon el-tooltip__trigger el-tooltip__trigger icon el-tooltip__trigger el-tooltip__trigger n8n-icon icon el-tooltip__trigger el-tooltip__trigger icon el-tooltip__trigger el-tooltip__trigger"
data-v-d00cba9a=""
type="item"
>
<div
class="toggle-container"
data-v-0f5e7239=""
>
<!--v-if-->
</div>
<div
class="pill pill--preview"
data-name="..."
data-nest-level="1"
data-target="mappable"
data-test-id="run-data-schema-node-name"
data-v-0f5e7239=""
>
<font-awesome-icon-stub
beat="false"
beatfade="false"
border="false"
bounce="false"
class="type-icon"
data-v-0f5e7239=""
class="14"
fade="false"
fixedwidth="false"
flash="false"
flip="false"
icon=""
icon="ellipsis-h"
inverse="false"
listitem="false"
pulse="false"
shake="false"
size="sm"
spin="false"
spinpulse="false"
spinreverse="false"
swapopacity="false"
symbol="false"
/>
<span
class="content title"
data-v-0f5e7239=""
>
<span>
<!--v-if-->
...
</span>
</span>
</div>
<span
class="content text"
data-test-id="run-data-schema-item-value"
data-v-0f5e7239=""
>
<span />
</span>
</div>
<!--teleport start-->
<!--teleport end-->
@ -821,7 +763,7 @@ exports[`VirtualSchema.vue > renders previous nodes schema for AI tools 1`] = `
</div>
<!--v-if-->
<div
class="item-count"
class="extra-info"
data-test-id="run-data-schema-node-item-count"
data-v-882a318e=""
>
@ -893,7 +835,7 @@ exports[`VirtualSchema.vue > renders schema for correct output branch 1`] = `
</div>
<!--v-if-->
<div
class="item-count"
class="extra-info"
data-test-id="run-data-schema-node-item-count"
data-v-882a318e=""
>
@ -1447,7 +1389,7 @@ exports[`VirtualSchema.vue > renders schema with spaces and dots 1`] = `
symbol="false"
/>
<div
class="item-count"
class="extra-info"
data-test-id="run-data-schema-node-item-count"
data-v-882a318e=""
>

View file

@ -261,7 +261,14 @@ export type RenderHeader = {
preview?: boolean;
};
type Renders = RenderHeader | RenderItem;
export type RenderIcon = {
id: string;
type: 'icon';
icon: string;
tooltip: string;
};
type Renders = RenderHeader | RenderItem | RenderIcon;
const icons = {
object: 'cube',
@ -285,13 +292,11 @@ const emptyItem = (): RenderItem => ({
type: 'item',
});
const dummyItem = (): RenderItem => ({
id: `dummy-${window.crypto.randomUUID()}`,
icon: '',
level: 1,
title: '...',
type: 'item',
preview: true,
const moreFieldsItem = (): RenderIcon => ({
id: `moreFields-${window.crypto.randomUUID()}`,
type: 'icon',
icon: 'ellipsis-h',
tooltip: useI18n().baseText('dataMapping.schemaView.previewExtraFields'),
});
const isDataEmpty = (schema: Schema) => {
@ -445,7 +450,7 @@ export const useFlattenSchema = () => {
acc.push(...flattenSchema(item));
if (item.preview) {
acc.push(dummyItem());
acc.push(moreFieldsItem());
}
return acc;

View file

@ -92,6 +92,7 @@ export const BUILTIN_NODES_DOCS_URL = `https://${DOCS_DOMAIN}/integrations/built
export const BUILTIN_CREDENTIALS_DOCS_URL = `https://${DOCS_DOMAIN}/integrations/builtin/credentials/`;
export const DATA_PINNING_DOCS_URL = `https://${DOCS_DOMAIN}/data/data-pinning/`;
export const DATA_EDITING_DOCS_URL = `https://${DOCS_DOMAIN}/data/data-editing/`;
export const SCHEMA_PREVIEW_DOCS_URL = `https://${DOCS_DOMAIN}/data/schema-preview/`;
export const MFA_DOCS_URL = `https://${DOCS_DOMAIN}/user-management/two-factor-auth/`;
export const NPM_COMMUNITY_NODE_SEARCH_API_URL = 'https://api.npms.io/v2/';
export const NPM_PACKAGE_DOCS_BASE_URL = 'https://www.npmjs.com/package/';

View file

@ -661,8 +661,9 @@
"dataMapping.schemaView.emptyData": "No fields - item(s) exist, but they're empty",
"dataMapping.schemaView.disabled": "This node is disabled and will just pass data through",
"dataMapping.schemaView.noMatches": "No results for '{search}'",
"dataMapping.schemaView.preview": "This is a preview of the schema, execute the node to see the exact schema and data. {link}",
"dataMapping.schemaView.previewNode": "(schema preview)",
"dataMapping.schemaView.preview": "Usually outputs the following fields. Execute the node to see the actual ones. {link}",
"dataMapping.schemaView.previewExtraFields": "There may be more fields. Execute the node to be sure.",
"dataMapping.schemaView.previewNode": "Preview",
"displayWithChange.cancelEdit": "Cancel Edit",
"displayWithChange.clickToChange": "Click to Change",
"displayWithChange.setValue": "Set Value",