Update filter design (#13243)

In this PR:

- Adjust the edges to match the new Figma design
- Properly display the filters for workflows and workflow versions
(replaced shouldDisplayEdgeOptions with isEdgeEditable as we want to
display configured filters on workflow versions, but want to disallow
editing them)
- Wrote a few tests to make coverage pass


https://github.com/user-attachments/assets/d303d338-1938-4efe-b489-5a530d65fb30
This commit is contained in:
Baptiste Devessier
2025-07-17 15:36:46 +02:00
committed by GitHub
parent fca39d317f
commit 0752f24638
20 changed files with 852 additions and 209 deletions
@@ -0,0 +1,251 @@
import { WorkflowTrigger } from '@/workflow/types/Workflow';
import { getTriggerHeaderType } from '../getTriggerHeaderType';
describe('getTriggerHeaderType', () => {
describe('DATABASE_EVENT triggers', () => {
it('returns "Trigger · Record is created" for created event', () => {
const trigger: WorkflowTrigger = {
type: 'DATABASE_EVENT',
name: 'Company Created',
settings: {
eventName: 'company.created',
outputSchema: {},
},
};
const result = getTriggerHeaderType(trigger);
expect(result).toBe('Trigger · Record is created');
});
it('returns "Trigger · Record is updated" for updated event', () => {
const trigger: WorkflowTrigger = {
type: 'DATABASE_EVENT',
name: 'Company Updated',
settings: {
eventName: 'company.updated',
outputSchema: {},
},
};
const result = getTriggerHeaderType(trigger);
expect(result).toBe('Trigger · Record is updated');
});
it('returns "Trigger · Record is deleted" for deleted event', () => {
const trigger: WorkflowTrigger = {
type: 'DATABASE_EVENT',
name: 'Company Deleted',
settings: {
eventName: 'company.deleted',
outputSchema: {},
},
};
const result = getTriggerHeaderType(trigger);
expect(result).toBe('Trigger · Record is deleted');
});
it('works with different object types', () => {
const trigger: WorkflowTrigger = {
type: 'DATABASE_EVENT',
name: 'Person Created',
settings: {
eventName: 'person.created',
outputSchema: {},
},
};
const result = getTriggerHeaderType(trigger);
expect(result).toBe('Trigger · Record is created');
});
});
describe('MANUAL triggers', () => {
it('returns "Trigger · Manual" for manual trigger', () => {
const trigger: WorkflowTrigger = {
type: 'MANUAL',
name: 'Manual Trigger',
settings: {
objectType: 'company',
outputSchema: {},
icon: 'IconHandMove',
},
};
const result = getTriggerHeaderType(trigger);
expect(result).toBe('Trigger · Manual');
});
it('returns "Trigger · Manual" for manual trigger without objectType', () => {
const trigger: WorkflowTrigger = {
type: 'MANUAL',
name: 'Manual Trigger',
settings: {
outputSchema: {},
icon: 'IconHandMove',
},
};
const result = getTriggerHeaderType(trigger);
expect(result).toBe('Trigger · Manual');
});
});
describe('CRON triggers', () => {
it('returns "Trigger" for cron trigger with DAYS schedule', () => {
const trigger: WorkflowTrigger = {
type: 'CRON',
name: 'Scheduled Trigger',
settings: {
type: 'DAYS',
schedule: {
day: 1,
hour: 9,
minute: 0,
},
outputSchema: {},
},
};
const result = getTriggerHeaderType(trigger);
expect(result).toBe('Trigger');
});
it('returns "Trigger" for cron trigger with HOURS schedule', () => {
const trigger: WorkflowTrigger = {
type: 'CRON',
name: 'Hourly Trigger',
settings: {
type: 'HOURS',
schedule: {
hour: 2,
minute: 30,
},
outputSchema: {},
},
};
const result = getTriggerHeaderType(trigger);
expect(result).toBe('Trigger');
});
it('returns "Trigger" for cron trigger with MINUTES schedule', () => {
const trigger: WorkflowTrigger = {
type: 'CRON',
name: 'Minutely Trigger',
settings: {
type: 'MINUTES',
schedule: {
minute: 15,
},
outputSchema: {},
},
};
const result = getTriggerHeaderType(trigger);
expect(result).toBe('Trigger');
});
it('returns "Trigger" for cron trigger with CUSTOM schedule', () => {
const trigger: WorkflowTrigger = {
type: 'CRON',
name: 'Custom Trigger',
settings: {
type: 'CUSTOM',
pattern: '0 9 * * 1',
outputSchema: {},
},
};
const result = getTriggerHeaderType(trigger);
expect(result).toBe('Trigger');
});
});
describe('WEBHOOK triggers', () => {
it('returns "Trigger · Webhook" for webhook trigger with GET method', () => {
const trigger: WorkflowTrigger = {
type: 'WEBHOOK',
name: 'Webhook Trigger',
settings: {
httpMethod: 'GET',
authentication: null,
outputSchema: {},
},
};
const result = getTriggerHeaderType(trigger);
expect(result).toBe('Trigger · Webhook');
});
it('returns "Trigger · Webhook" for webhook trigger with POST method', () => {
const trigger: WorkflowTrigger = {
type: 'WEBHOOK',
name: 'Webhook Trigger',
settings: {
httpMethod: 'POST',
expectedBody: {
message: 'Workflow was started',
},
authentication: null,
outputSchema: {
message: {
icon: 'IconVariable',
isLeaf: true,
label: 'message',
type: 'string',
value: 'Workflow was started',
},
},
},
};
const result = getTriggerHeaderType(trigger);
expect(result).toBe('Trigger · Webhook');
});
it('returns "Trigger · Webhook" for webhook trigger with API_KEY authentication', () => {
const trigger: WorkflowTrigger = {
type: 'WEBHOOK',
name: 'Secure Webhook Trigger',
settings: {
httpMethod: 'GET',
authentication: 'API_KEY',
outputSchema: {},
},
};
const result = getTriggerHeaderType(trigger);
expect(result).toBe('Trigger · Webhook');
});
});
describe('error cases', () => {
it('throws error for unknown trigger type', () => {
const trigger = {
type: 'UNKNOWN_TYPE',
name: 'Unknown Trigger',
settings: {
outputSchema: {},
},
} as unknown as WorkflowTrigger;
expect(() => getTriggerHeaderType(trigger)).toThrow(
'Unknown trigger type',
);
});
});
});
@@ -0,0 +1,56 @@
/* eslint-disable @nx/workspace-no-hardcoded-colors */
import { Theme } from '@emotion/react';
import { getTriggerIconColor } from '../getTriggerIconColor';
describe('getTriggerIconColor', () => {
const mockTheme: Theme = {
font: {
color: {
primary: '#2c2c2c',
secondary: '#666666',
tertiary: '#999999',
light: '#cccccc',
},
},
} as unknown as Theme;
it('returns the tertiary font color from theme', () => {
const result = getTriggerIconColor({ theme: mockTheme });
expect(result).toBe('#999999');
});
it('works with different theme configurations', () => {
const differentTheme: Theme = {
font: {
color: {
primary: '#000000',
secondary: '#444444',
tertiary: '#888888',
light: '#ffffff',
},
},
} as unknown as Theme;
const result = getTriggerIconColor({ theme: differentTheme });
expect(result).toBe('#888888');
});
it('maintains reference to theme.font.color.tertiary', () => {
const customTheme: Theme = {
font: {
color: {
primary: '#111111',
secondary: '#333333',
tertiary: '#custom-tertiary-color',
light: '#eeeeee',
},
},
} as unknown as Theme;
const result = getTriggerIconColor({ theme: customTheme });
expect(result).toBe('#custom-tertiary-color');
});
});