fix(editor): New NDV design tweaks (#19903)

Co-authored-by: Elias Meire <elias@meire.dev>
This commit is contained in:
Jake Ranallo 2025-10-07 15:38:09 +02:00 committed by GitHub
parent 278ca8b9e8
commit ca84331761
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 76 additions and 52 deletions

View File

@ -1233,7 +1233,7 @@
"ndv.output.noOutputData.title": "No output data returned",
"ndv.output.noOutputData.v2.title": "No output data",
"ndv.output.noOutputData.v2.description": "{link} to view output data",
"ndv.output.noOutputData.v2.action": "Test this step",
"ndv.output.noOutputData.v2.action": "Execute step",
"ndv.output.noOutputData.trigger.title": "No trigger output",
"ndv.output.noOutputData.trigger.action": "Test this trigger",
"ndv.output.noOutputDataInBranch": "No output data in this branch",

View File

@ -497,7 +497,11 @@ function handleChangeCollapsingColumn(columnName: string | null) {
<template v-else-if="isNDVV2">
<NDVEmptyState
v-if="isMappingEnabled || hasRootNodeRun"
v-if="readOnly"
:title="i18n.baseText('ndv.input.noOutputData.v2.title')"
/>
<NDVEmptyState
v-else-if="isMappingEnabled || hasRootNodeRun"
:title="i18n.baseText('ndv.input.noOutputData.v2.title')"
icon="arrow-right-to-line"
>

View File

@ -12,20 +12,9 @@ describe('NDVHeader', () => {
readOnly: false,
};
it('renders docs label with node type name if name is customized', () => {
it('renders docs label', () => {
const { getByText } = renderComponent(NDVHeader, { props: defaultProps });
expect(getByText('Edit Fields Docs')).toBeInTheDocument();
});
it('renders nodeTypeName if docsUrl is not provided and name is custom', () => {
const { getByText, queryByText } = renderComponent(NDVHeader, {
props: {
...defaultProps,
docsUrl: undefined,
},
});
expect(getByText('Edit Fields')).toBeInTheDocument();
expect(queryByText('Docs')).not.toBeInTheDocument();
expect(getByText('Docs')).toBeInTheDocument();
});
it('emits rename when inline text is changed', async () => {

View File

@ -1,8 +1,7 @@
<script setup lang="ts">
import NodeIcon from '@/components/NodeIcon.vue';
import type { NodeIconSource } from '@/utils/nodeIcon';
import { useI18n } from '@n8n/i18n';
import { computed } from 'vue';
import NodeIcon from '@/components/NodeIcon.vue';
import {
N8nIcon,
@ -24,15 +23,6 @@ const i18n = useI18n();
const emit = defineEmits<{ close: []; rename: [name: string] }>();
const hasCustomName = computed(() => props.nodeName !== props.nodeTypeName);
const docsLabel = computed(() => {
if (!hasCustomName.value) {
return i18n.baseText('nodeSettings.docs');
}
return `${props.nodeTypeName} ${i18n.baseText('nodeSettings.docs')}`;
});
function onRename(newNodeName: string) {
emit('rename', newNodeName || props.nodeTypeName);
}
@ -52,27 +42,24 @@ function onRename(newNodeName: string) {
@update:model-value="onRename"
/>
</div>
</div>
<div :class="$style.actions">
<N8nLink v-if="docsUrl" theme="text" target="_blank" :href="docsUrl">
<span :class="$style.docsLabel">
<N8nText size="small" bold>
{{ docsLabel }}
{{ i18n.baseText('nodeSettings.docs') }}
</N8nText>
<N8nIcon icon="external-link" />
</span>
</N8nLink>
<N8nText v-else-if="hasCustomName" size="small" bold>
{{ nodeTypeName }}
</N8nText>
<N8nTooltip>
<template #content>
{{ i18n.baseText('ndv.close.tooltip') }}
</template>
<N8nIconButton icon="x" type="tertiary" text @click="emit('close')" />
</N8nTooltip>
</div>
<N8nTooltip>
<template #content>
{{ i18n.baseText('ndv.close.tooltip') }}
</template>
<N8nIconButton icon="x" type="tertiary" @click="emit('close')" />
</N8nTooltip>
</header>
</template>
@ -83,27 +70,35 @@ function onRename(newNodeName: string) {
align-items: center;
justify-content: space-between;
gap: var(--spacing-2xs);
padding: var(--spacing-2xs);
padding: var(--spacing-4xs);
background: var(--color-background-xlight);
}
.content {
display: flex;
align-items: flex-end;
align-items: center;
gap: var(--spacing-2xs);
margin-left: var(--spacing-2xs);
}
.title {
color: var(--color-text-dark);
font-size: var(--font-size-m);
.actions {
display: flex;
align-items: center;
gap: var(--spacing-4xs);
}
.subtitle {
display: flex;
align-items: baseline;
gap: var(--spacing-2xs);
margin: 0;
.actions button:hover {
background-color: var(--color-background-base);
}
.actions > *:not(:last-child) {
border-right: var(--border-base);
padding-right: var(--spacing-2xs);
}
.title {
color: var(--color-text-dark);
font-size: var(--font-size-s);
}
.docsLabel {

View File

@ -873,6 +873,7 @@ onBeforeUnmount(() => {
padding: 0;
margin: 0;
display: flex;
outline: none;
}
.container {
@ -933,7 +934,7 @@ onBeforeUnmount(() => {
}
.draggable {
--draggable-height: 22px;
--draggable-height: 18px;
position: absolute;
top: calc(-1 * var(--draggable-height));
left: 50%;

View File

@ -358,6 +358,17 @@ function handleChangeCollapsingColumn(columnName: string | null) {
<template #node-not-run>
<template v-if="isNDVV2">
<NDVEmptyState
v-if="isReadOnly"
:title="
i18n.baseText(
isTriggerNode
? 'ndv.output.noOutputData.trigger.title'
: 'ndv.output.noOutputData.v2.title',
)
"
/>
<NDVEmptyState
v-else
:title="
i18n.baseText(
isTriggerNode
@ -379,6 +390,7 @@ function handleChangeCollapsingColumn(columnName: string | null) {
</template>
<template #default>
<I18nT
v-if="!canPinData || isSubNodeType"
tag="span"
:keypath="
isSubNodeType
@ -406,6 +418,28 @@ function handleChangeCollapsingColumn(columnName: string | null) {
<br />
</template>
</I18nT>
<template v-else>
<NodeExecuteButton
hide-icon
transparent
type="secondary"
:node-name="activeNode?.name ?? ''"
:label="
i18n.baseText(
isTriggerNode
? 'ndv.output.noOutputData.trigger.action'
: 'ndv.output.noOutputData.v2.action',
)
"
telemetry-source="inputs"
@execute="emit('execute')"
/>
<br />
{{ i18n.baseText('generic.or') }}
<N8nText tag="a" size="medium" color="primary" @click="insertTestData">
{{ i18n.baseText('ndv.output.insertTestData') }}
</N8nText>
</template>
</template>
</NDVEmptyState>
</template>

View File

@ -55,8 +55,8 @@ const onDragStart = () => {
display: flex;
align-items: baseline;
gap: var(--spacing-2xs);
padding: var(--spacing-4xs) var(--spacing-2xs) var(--spacing-4xs) var(--spacing-2xs);
gap: var(--spacing-3xs);
padding: var(--spacing-4xs) var(--spacing-3xs) 0 var(--spacing-3xs);
color: var(--color-foreground-dark);
border: var(--border-base);
border-bottom: none;

View File

@ -1989,13 +1989,14 @@ defineExpose({ enterEditMode });
border-top: 0;
border-left: 0;
border-right: 0;
height: 40px;
}
.header {
display: flex;
align-items: center;
margin-bottom: var(--ndv-spacing);
padding: var(--ndv-spacing) var(--ndv-spacing) 0 var(--ndv-spacing);
padding: var(--ndv-spacing) var(--spacing-3xs) 0 var(--ndv-spacing);
position: relative;
overflow-x: auto;
overflow-y: hidden;