mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 20:54:07 -08:00
feat(editor): Add node name and version to NDV node settings (#7731)
<img width="580" alt="image" src="https://github.com/n8n-io/n8n/assets/8850410/85ac1c6e-9116-436a-a2ed-8d0ac162a287"> <img width="580" alt="image" src="https://github.com/n8n-io/n8n/assets/8850410/08b37377-cef5-4f80-80c0-addfdd37f728"> --------- Co-authored-by: Giulio Andreini <andreini@netseven.it>
This commit is contained in:
parent
902beffce5
commit
da851986f6
|
@ -1,7 +1,6 @@
|
||||||
import { WorkflowPage, NDV } from '../pages';
|
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import { getPopper, getVisiblePopper, getVisibleSelect } from '../utils';
|
import { NDV, WorkflowPage } from '../pages';
|
||||||
import { META_KEY } from '../constants';
|
import { getVisibleSelect } from '../utils';
|
||||||
|
|
||||||
const workflowPage = new WorkflowPage();
|
const workflowPage = new WorkflowPage();
|
||||||
const ndv = new NDV();
|
const ndv = new NDV();
|
||||||
|
@ -368,4 +367,23 @@ describe('NDV', () => {
|
||||||
// Should call the endpoint only once (on mount), not for every keystroke
|
// Should call the endpoint only once (on mount), not for every keystroke
|
||||||
cy.get('@fetchParameterOptions').should('have.been.calledOnce');
|
cy.get('@fetchParameterOptions').should('have.been.calledOnce');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should show node name and version in settings', () => {
|
||||||
|
cy.createFixtureWorkflow('Test_workflow_ndv_version.json', `NDV test version ${uuid()}`);
|
||||||
|
|
||||||
|
workflowPage.actions.openNode('Edit Fields (old)');
|
||||||
|
ndv.actions.openSettings();
|
||||||
|
ndv.getters.nodeVersion().should('have.text', 'Set node version 2 (Latest version: 3.2)');
|
||||||
|
ndv.actions.close();
|
||||||
|
|
||||||
|
workflowPage.actions.openNode('Edit Fields (latest)');
|
||||||
|
ndv.actions.openSettings();
|
||||||
|
ndv.getters.nodeVersion().should('have.text', 'Edit Fields (Set) node version 3.2 (Latest)');
|
||||||
|
ndv.actions.close();
|
||||||
|
|
||||||
|
workflowPage.actions.openNode('Function');
|
||||||
|
ndv.actions.openSettings();
|
||||||
|
ndv.getters.nodeVersion().should('have.text', 'Function node version 1 (Deprecated)');
|
||||||
|
ndv.actions.close();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
49
cypress/fixtures/Test_workflow_ndv_version.json
Normal file
49
cypress/fixtures/Test_workflow_ndv_version.json
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"name": "Node versions",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "aadaed66-84ed-4cf8-bf21-082e9a65db76",
|
||||||
|
"name": "When clicking \"Execute Workflow\"",
|
||||||
|
"type": "n8n-nodes-base.manualTrigger",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
1540,
|
||||||
|
780
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {},
|
||||||
|
"id": "93d73a85-82f0-4380-a032-713d5dc82b32",
|
||||||
|
"name": "Function",
|
||||||
|
"type": "n8n-nodes-base.function",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
2040,
|
||||||
|
780
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "50f322d9-c622-4dd0-8d38-e851502739dd",
|
||||||
|
"name": "Edit Fields (old)",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
1880,
|
||||||
|
780
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "93aaadac-55fe-4618-b1eb-f63e61d1446a",
|
||||||
|
"name": "Edit Fields (latest)",
|
||||||
|
"type": "n8n-nodes-base.set",
|
||||||
|
"typeVersion": 3.2,
|
||||||
|
"position": [
|
||||||
|
1720,
|
||||||
|
780
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pinData": {},
|
||||||
|
"connections": {}
|
||||||
|
}
|
|
@ -81,6 +81,8 @@ export class NDV extends BasePage {
|
||||||
sqlEditorContainer: () => cy.getByTestId('sql-editor-container'),
|
sqlEditorContainer: () => cy.getByTestId('sql-editor-container'),
|
||||||
searchInput: () => cy.getByTestId('ndv-search'),
|
searchInput: () => cy.getByTestId('ndv-search'),
|
||||||
pagination: () => cy.getByTestId('ndv-data-pagination'),
|
pagination: () => cy.getByTestId('ndv-data-pagination'),
|
||||||
|
nodeVersion: () => cy.getByTestId('node-version'),
|
||||||
|
nodeSettingsTab: () => cy.getByTestId('tab-settings'),
|
||||||
};
|
};
|
||||||
|
|
||||||
actions = {
|
actions = {
|
||||||
|
@ -225,6 +227,10 @@ export class NDV extends BasePage {
|
||||||
});
|
});
|
||||||
this.actions.validateExpressionPreview(fieldName, `node doesn't exist`);
|
this.actions.validateExpressionPreview(fieldName, `node doesn't exist`);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
openSettings: () => {
|
||||||
|
this.getters.nodeSettingsTab().click();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
:class="{ [$style.tab]: true, [$style.activeTab]: modelValue === option.value }"
|
:class="{ [$style.tab]: true, [$style.activeTab]: modelValue === option.value }"
|
||||||
|
:data-test-id="`tab-${option.value}`"
|
||||||
@click="() => handleTabClick(option.value)"
|
@click="() => handleTabClick(option.value)"
|
||||||
>
|
>
|
||||||
<n8n-icon v-if="option.icon" :icon="option.icon" size="medium" />
|
<n8n-icon v-if="option.icon" :icon="option.icon" size="medium" />
|
||||||
|
|
|
@ -152,6 +152,17 @@
|
||||||
@valueChanged="valueChanged"
|
@valueChanged="valueChanged"
|
||||||
@parameterBlur="onParameterBlur"
|
@parameterBlur="onParameterBlur"
|
||||||
/>
|
/>
|
||||||
|
<div class="node-version" data-test-id="node-version">
|
||||||
|
{{
|
||||||
|
$locale.baseText('nodeSettings.nodeVersion', {
|
||||||
|
interpolate: {
|
||||||
|
node: nodeType?.displayName as string,
|
||||||
|
version: node.typeVersion.toString(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
<span>({{ nodeVersionTag }})</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<n8n-block-ui :show="blockUI" />
|
<n8n-block-ui :show="blockUI" />
|
||||||
|
@ -258,6 +269,29 @@ export default defineComponent({
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
|
nodeTypeVersions(): number[] {
|
||||||
|
if (!this.node) return [];
|
||||||
|
return this.nodeTypesStore.getNodeVersions(this.node.type);
|
||||||
|
},
|
||||||
|
latestVersion(): number {
|
||||||
|
return Math.max(...this.nodeTypeVersions);
|
||||||
|
},
|
||||||
|
isLatestNodeVersion(): boolean {
|
||||||
|
return this.latestVersion === this.node?.typeVersion;
|
||||||
|
},
|
||||||
|
nodeVersionTag(): string {
|
||||||
|
if (!this.nodeType || this.nodeType.hidden) {
|
||||||
|
return this.$locale.baseText('nodeSettings.deprecated');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isLatestNodeVersion) {
|
||||||
|
return this.$locale.baseText('nodeSettings.latest');
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.$locale.baseText('nodeSettings.latestVersion', {
|
||||||
|
interpolate: { version: this.latestVersion.toString() },
|
||||||
|
});
|
||||||
|
},
|
||||||
nodeTypeDescription(): string {
|
nodeTypeDescription(): string {
|
||||||
if (this.nodeType?.description) {
|
if (this.nodeType?.description) {
|
||||||
const shortNodeType = this.$locale.shortNodeType(this.nodeType.name);
|
const shortNodeType = this.$locale.shortNodeType(this.nodeType.name);
|
||||||
|
@ -1126,6 +1160,14 @@ export default defineComponent({
|
||||||
top: -25px;
|
top: -25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.node-version {
|
||||||
|
border-top: var(--border-base);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
font-size: var(--font-size-2xs);
|
||||||
|
padding: var(--spacing-xs) 0 var(--spacing-2xs) 0;
|
||||||
|
color: var(--color-text-light);
|
||||||
|
}
|
||||||
|
|
||||||
.parameter-value {
|
.parameter-value {
|
||||||
input.expression {
|
input.expression {
|
||||||
border-style: dashed;
|
border-style: dashed;
|
||||||
|
|
|
@ -995,6 +995,11 @@
|
||||||
"nodeSettings.waitBetweenTries.description": "How long to wait between each attempt (in milliseconds)",
|
"nodeSettings.waitBetweenTries.description": "How long to wait between each attempt (in milliseconds)",
|
||||||
"nodeSettings.waitBetweenTries.displayName": "Wait Between Tries (ms)",
|
"nodeSettings.waitBetweenTries.displayName": "Wait Between Tries (ms)",
|
||||||
"nodeSettings.hasForeignCredential": "To edit this node, either:<br/>a) Ask {owner} to share the credential with you, or<br/>b) Duplicate the node and add your own credential",
|
"nodeSettings.hasForeignCredential": "To edit this node, either:<br/>a) Ask {owner} to share the credential with you, or<br/>b) Duplicate the node and add your own credential",
|
||||||
|
"nodeSettings.latest": "Latest",
|
||||||
|
"nodeSettings.deprecated": "Deprecated",
|
||||||
|
"nodeSettings.latestVersion": "Latest version: {version}",
|
||||||
|
"nodeSettings.nodeVersion": "{node} node version {version}",
|
||||||
|
"nodeView.addNode": "Add node",
|
||||||
"nodeView.openNodesPanel": "Open nodes panel",
|
"nodeView.openNodesPanel": "Open nodes panel",
|
||||||
"nodeView.addATriggerNodeFirst": "Add a <a data-action='showNodeCreator'>Trigger Node</a> first",
|
"nodeView.addATriggerNodeFirst": "Add a <a data-action='showNodeCreator'>Trigger Node</a> first",
|
||||||
"nodeView.addOrEnableTriggerNode": "<a data-action='showNodeCreator'>Add</a> or enable a Trigger node to execute the workflow",
|
"nodeView.addOrEnableTriggerNode": "<a data-action='showNodeCreator'>Add</a> or enable a Trigger node to execute the workflow",
|
||||||
|
|
|
@ -84,6 +84,11 @@ export const useNodeTypesStore = defineStore(STORES.NODE_TYPES, {
|
||||||
return nodeType ?? null;
|
return nodeType ?? null;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
getNodeVersions() {
|
||||||
|
return (nodeTypeName: string): number[] => {
|
||||||
|
return Object.keys(this.nodeTypes[nodeTypeName] ?? {}).map(Number);
|
||||||
|
};
|
||||||
|
},
|
||||||
getCredentialOnlyNodeType() {
|
getCredentialOnlyNodeType() {
|
||||||
return (nodeTypeName: string, version?: number): INodeTypeDescription | null => {
|
return (nodeTypeName: string, version?: number): INodeTypeDescription | null => {
|
||||||
const credentialName = getCredentialTypeName(nodeTypeName);
|
const credentialName = getCredentialTypeName(nodeTypeName);
|
||||||
|
|
Loading…
Reference in a new issue