fix(editor): Properly set colors for connections and labels on nodes with pinned data (#8209)

Co-authored-by: Alex Grozav <alex@grozav.com>
This commit is contained in:
Csaba Tuncsik 2024-01-11 14:03:23 +01:00 committed by GitHub
parent 884396ea0d
commit 3b8ccb9fb9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 518 additions and 16 deletions

View file

@ -288,4 +288,126 @@ describe('Execution', () => {
workflowPage.getters.successToast().contains('Execution deleted');
});
});
describe('connections should be colored differently for pinned data', () => {
beforeEach(() => {
cy.createFixtureWorkflow('Schedule_pinned.json', `Schedule pinned ${uuid()}`);
workflowPage.actions.deselectAll();
workflowPage.getters.zoomToFitButton().click();
workflowPage.getters
.getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields')
.should('have.class', 'success')
.should('have.class', 'pinned')
.should('not.have.class', 'has-run');
workflowPage.getters
.getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields1')
.should('have.class', 'success')
.should('have.class', 'pinned')
.should('not.have.class', 'has-run');
workflowPage.getters
.getConnectionBetweenNodes('Edit Fields5', 'Edit Fields6')
.should('not.have.class', 'success')
.should('not.have.class', 'pinned');
workflowPage.getters
.getConnectionBetweenNodes('Edit Fields7', 'Edit Fields9')
.should('have.class', 'success')
.should('have.class', 'pinned')
.should('not.have.class', 'has-run');
workflowPage.getters
.getConnectionBetweenNodes('Edit Fields1', 'Edit Fields2')
.should('not.have.class', 'success')
.should('not.have.class', 'pinned');
workflowPage.getters
.getConnectionBetweenNodes('Edit Fields2', 'Edit Fields3')
.should('have.class', 'success')
.should('have.class', 'pinned')
.should('not.have.class', 'has-run');
});
it('when executing the workflow', () => {
workflowPage.actions.executeWorkflow();
workflowPage.getters
.getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields')
.should('have.class', 'success')
.should('have.class', 'pinned')
.should('have.class', 'has-run');
workflowPage.getters
.getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields1')
.should('have.class', 'success')
.should('have.class', 'pinned')
.should('have.class', 'has-run');
workflowPage.getters
.getConnectionBetweenNodes('Edit Fields5', 'Edit Fields6')
.should('have.class', 'success')
.should('not.have.class', 'pinned')
.should('not.have.class', 'has-run');
workflowPage.getters
.getConnectionBetweenNodes('Edit Fields7', 'Edit Fields9')
.should('have.class', 'success')
.should('have.class', 'pinned')
.should('have.class', 'has-run');
workflowPage.getters
.getConnectionBetweenNodes('Edit Fields1', 'Edit Fields2')
.should('have.class', 'success')
.should('not.have.class', 'pinned')
.should('not.have.class', 'has-run');
workflowPage.getters
.getConnectionBetweenNodes('Edit Fields2', 'Edit Fields3')
.should('have.class', 'success')
.should('have.class', 'pinned')
.should('have.class', 'has-run');
});
it('when executing a node', () => {
workflowPage.actions.executeNode('Edit Fields3');
workflowPage.getters
.getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields')
.should('have.class', 'success')
.should('have.class', 'pinned')
.should('have.class', 'has-run');
workflowPage.getters
.getConnectionBetweenNodes('Schedule Trigger', 'Edit Fields1')
.should('have.class', 'success')
.should('have.class', 'pinned')
.should('have.class', 'has-run');
workflowPage.getters
.getConnectionBetweenNodes('Edit Fields5', 'Edit Fields6')
.should('not.have.class', 'success')
.should('not.have.class', 'pinned')
.should('not.have.class', 'has-run');
workflowPage.getters
.getConnectionBetweenNodes('Edit Fields7', 'Edit Fields9')
.should('have.class', 'success')
.should('have.class', 'pinned')
.should('not.have.class', 'has-run');
workflowPage.getters
.getConnectionBetweenNodes('Edit Fields1', 'Edit Fields2')
.should('have.class', 'success')
.should('not.have.class', 'pinned')
.should('not.have.class', 'has-run');
workflowPage.getters
.getConnectionBetweenNodes('Edit Fields2', 'Edit Fields3')
.should('have.class', 'success')
.should('have.class', 'pinned')
.should('have.class', 'has-run');
});
});
});

View file

@ -0,0 +1,313 @@
{
"name": "Schedule + pinned",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{}
]
}
},
"id": "66358c29-b263-43dd-be25-3b068b0a88eb",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.1,
"position": [
660,
340
]
},
{
"parameters": {
"options": {}
},
"id": "6d903354-4e59-4032-81fe-426a5d6ec33c",
"name": "Edit Fields",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
860,
240
]
},
{
"parameters": {
"options": {}
},
"id": "d8a1e9cf-81d3-400f-97d4-ad6167e7b236",
"name": "Edit Fields1",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
860,
440
]
},
{
"parameters": {
"options": {}
},
"id": "bdc41148-067e-4649-8f21-5707b128d877",
"name": "Edit Fields2",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
1080,
440
]
},
{
"parameters": {
"options": {}
},
"id": "d5a4337f-a6b3-4b51-9b02-e668593d9ae8",
"name": "Edit Fields3",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
1300,
440
]
},
{
"parameters": {
"options": {}
},
"id": "fbc23f60-e7f6-4423-9329-33b0e4809a9a",
"name": "Edit Fields4",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
1500,
440
]
},
{
"parameters": {
"options": {}
},
"id": "eaee47b0-94ec-4137-bfeb-a6c1a2c63f81",
"name": "Edit Fields5",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
1080,
240
]
},
{
"parameters": {
"options": {}
},
"id": "eabb6308-21e9-4e59-8f74-9220a03c3186",
"name": "Edit Fields6",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
1300,
240
]
},
{
"parameters": {
"options": {}
},
"id": "8812a45b-5545-4080-aad8-8e9f7b17ecd7",
"name": "Edit Fields7",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
1500,
240
]
},
{
"parameters": {
"options": {}
},
"id": "d5ea3c5b-0b3e-4514-93e1-9c88563bab5c",
"name": "Edit Fields9",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
1700,
240
]
},
{
"parameters": {
"options": {}
},
"id": "7af34474-5cd0-40b1-abea-850858e3b495",
"name": "Edit Fields10",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
1700,
440
]
}
],
"pinData": {
"Schedule Trigger": [
{
"json": {
"name": "First item",
"code": 1
}
},
{
"json": {
"name": "Second item",
"code": 2
}
}
],
"Edit Fields7": [
{
"json": {
"name": "First item",
"code": 1
}
},
{
"json": {
"name": "Second item",
"code": 2
}
}
],
"Edit Fields2": [
{
"json": {
"name": "First item",
"code": 1
}
},
{
"json": {
"name": "Second item",
"code": 2
}
}
]
},
"connections": {
"Schedule Trigger": {
"main": [
[
{
"node": "Edit Fields",
"type": "main",
"index": 0
},
{
"node": "Edit Fields1",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields1": {
"main": [
[
{
"node": "Edit Fields2",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields2": {
"main": [
[
{
"node": "Edit Fields3",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields3": {
"main": [
[
{
"node": "Edit Fields4",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields5": {
"main": [
[
{
"node": "Edit Fields6",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields6": {
"main": [
[
{
"node": "Edit Fields7",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[
{
"node": "Edit Fields5",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields7": {
"main": [
[
{
"node": "Edit Fields9",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields4": {
"main": [
[
{
"node": "Edit Fields10",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "9b6c68c0-f94f-45bc-a604-bf97d17a47ac",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "8a47b83b4479b11330fdf21ccc96d4a8117035a968612e452b4c87bfd09c16c7"
},
"id": "nWzcnYUb3AVaZpHG",
"tags": []
}

View file

@ -1179,17 +1179,6 @@ export default defineComponent({
.drop-add-node-label {
z-index: 10;
}
.jtk-connector.success:not(.jtk-hover) {
path:not(.jtk-connector-outline) {
stroke: var(--color-success-light);
}
path[jtk-overlay-id='reverse-arrow'],
path[jtk-overlay-id='endpoint-arrow'],
path[jtk-overlay-id='midpoint-arrow'] {
fill: var(--color-success-light);
}
}
</style>
<style lang="scss">
@ -1262,7 +1251,6 @@ export default defineComponent({
white-space: nowrap;
font-size: var(--font-size-s);
font-weight: var(--font-weight-regular);
color: var(--color-success);
margin-top: -15px;
&.floating {

View file

@ -755,9 +755,15 @@ export const getRunItemsLabel = (output: { total: number; iterations: number }):
export const addConnectionOutputSuccess = (
connection: Connection,
output: { total: number; iterations: number },
output: { total: number; iterations: number; classNames?: string[] },
) => {
connection.addClass('success');
const classNames: string[] = ['success'];
if (output.classNames) {
classNames.push(...output.classNames);
}
connection.addClass(classNames.join(' '));
if (getOverlay(connection, OVERLAY_RUN_ITEMS_ID)) {
connection.removeOverlay(OVERLAY_RUN_ITEMS_ID);
}
@ -771,7 +777,7 @@ export const addConnectionOutputSuccess = (
const container = document.createElement('div');
const span = document.createElement('span');
container.classList.add('connection-run-items-label');
container.classList.add(...['connection-run-items-label', ...classNames]);
span.classList.add('floating');
span.innerHTML = getRunItemsLabel(output);
container.appendChild(span);
@ -791,6 +797,27 @@ export const addConnectionOutputSuccess = (
});
};
export const addClassesToOverlays = ({
connection,
overlayIds,
classNames,
includeConnector,
}: {
connection: Connection;
overlayIds: string[];
classNames: string[];
includeConnector?: boolean;
}) => {
overlayIds.forEach((overlayId) => {
const overlay = getOverlay(connection, overlayId);
overlay?.canvas?.classList.add(...classNames);
if (includeConnector) {
connection.connector.canvas?.classList.add(...classNames);
}
});
};
const getContentDimensions = (): { editorWidth: number; editorHeight: number } => {
let contentWidth = window.innerWidth;
let contentHeight = window.innerHeight;

View file

@ -2157,6 +2157,9 @@ export default defineComponent({
}, []);
this.workflowsStore.addWorkflowTagIds(tagIds);
setTimeout(() => {
this.addPinDataConnections(this.workflowsStore.pinnedWorkflowData || ({} as IPinData));
});
}
} catch (error) {
this.showError(error, this.$locale.baseText('nodeView.showError.importWorkflowData.title'));
@ -3803,7 +3806,22 @@ export default defineComponent({
}) {
const pinData = this.workflowsStore.pinnedWorkflowData;
if (pinData?.[name]) return;
if (pinData?.[name]) {
const { outgoing } = this.getIncomingOutgoingConnections(name);
outgoing.forEach((connection: Connection) => {
if (connection.__meta?.sourceNodeName === name) {
const hasRun = this.workflowsStore.getWorkflowResultDataByNodeName(name) !== null;
NodeViewUtils.addClassesToOverlays({
connection,
overlayIds: [NodeViewUtils.OVERLAY_RUN_ITEMS_ID],
classNames: hasRun ? ['has-run'] : [],
includeConnector: true,
});
}
});
return;
}
const sourceNodeName = name;
const sourceNode = this.workflowsStore.getNodeByName(sourceNodeName);
@ -4690,6 +4708,7 @@ export default defineComponent({
NodeViewUtils.addConnectionOutputSuccess(connection, {
total: pinData[nodeName].length,
iterations: 0,
classNames: ['pinned'],
});
});
});
@ -5209,3 +5228,36 @@ export default defineComponent({
top: var(--spacing-l);
}
</style>
<style lang="scss" scoped>
@mixin applyColorToConnection($partialSelector, $cssColorVarName, $labelCssColorVarName) {
.jtk-connector#{$partialSelector}:not(.jtk-hover) {
path:not(.jtk-connector-outline) {
stroke: var(#{$cssColorVarName});
}
path[jtk-overlay-id='reverse-arrow'],
path[jtk-overlay-id='endpoint-arrow'],
path[jtk-overlay-id='midpoint-arrow'] {
fill: var(#{$cssColorVarName});
}
}
.connection-run-items-label#{$partialSelector} {
color: var(#{$labelCssColorVarName});
}
}
:deep(.node-view) {
@include applyColorToConnection('.success', '--color-success-light', '--color-success');
@include applyColorToConnection(
'.success.pinned',
'--color-foreground-xdark',
'--color-foreground-xdark'
);
@include applyColorToConnection(
'.success.pinned.has-run',
'--color-secondary',
'--color-secondary'
);
}
</style>