@@ -71,7 +71,7 @@ exports[`components > N8nCallout > should render warning theme correctly 1`] = `
"
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/Icon.test.ts b/packages/frontend/@n8n/design-system/src/components/N8nIcon/Icon.test.ts
new file mode 100644
index 0000000000..55b0318d24
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/Icon.test.ts
@@ -0,0 +1,64 @@
+import { render } from '@testing-library/vue';
+
+import Icon from './Icon.vue';
+import { deprecatedIconSet, type IconName } from './icons';
+
+describe('Icon', () => {
+ it('should render correctly with default props', () => {
+ const wrapper = render(Icon, {
+ props: {
+ icon: 'check',
+ },
+ });
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ it('should render correctly with a custom size', () => {
+ const wrapper = render(Icon, {
+ props: {
+ icon: 'check',
+ size: 24,
+ },
+ });
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ it('should render correctly with predefined size', () => {
+ const wrapper = render(Icon, {
+ props: {
+ icon: 'check',
+ size: 'large',
+ },
+ });
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ it('should render correctly with spin enabled', () => {
+ const wrapper = render(Icon, {
+ props: {
+ icon: 'check',
+ spin: true,
+ },
+ });
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ it('should render correctly with a custom color', () => {
+ const wrapper = render(Icon, {
+ props: {
+ icon: 'check',
+ color: 'primary',
+ },
+ });
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ it('should render correctly with a deprecated icon', () => {
+ const wrapper = render(Icon, {
+ props: {
+ icon: Object.keys(deprecatedIconSet)[0] as IconName,
+ },
+ });
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+});
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/Icon.vue b/packages/frontend/@n8n/design-system/src/components/N8nIcon/Icon.vue
index d303d00e3a..b04a9dc50f 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nIcon/Icon.vue
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/Icon.vue
@@ -1,49 +1,116 @@
-
-
-
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/__snapshots__/Icon.test.ts.snap b/packages/frontend/@n8n/design-system/src/components/N8nIcon/__snapshots__/Icon.test.ts.snap
new file mode 100644
index 0000000000..aae79e6a2c
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/__snapshots__/Icon.test.ts.snap
@@ -0,0 +1,43 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`Icon > should render correctly with a custom color 1`] = `
+"
+
+ "
+`;
+
+exports[`Icon > should render correctly with a custom size 1`] = `
+"
+
+ "
+`;
+
+exports[`Icon > should render correctly with a deprecated icon 1`] = `
+"
+
+ "
+`;
+
+exports[`Icon > should render correctly with all props combined 1`] = `
+"
+
+ "
+`;
+
+exports[`Icon > should render correctly with default props 1`] = `
+"
+
+ "
+`;
+
+exports[`Icon > should render correctly with predefined size 1`] = `
+"
+
+ "
+`;
+
+exports[`Icon > should render correctly with spin enabled 1`] = `
+"
+
+ "
+`;
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/binary.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/binary.svg
new file mode 100644
index 0000000000..1445711730
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/binary.svg
@@ -0,0 +1 @@
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/grip-lines-vertical.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/grip-lines-vertical.svg
new file mode 100644
index 0000000000..9f8ac488a4
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/grip-lines-vertical.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/json.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/json.svg
new file mode 100644
index 0000000000..8951517e48
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/json.svg
@@ -0,0 +1 @@
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/pop-out.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/pop-out.svg
new file mode 100644
index 0000000000..9101766212
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/pop-out.svg
@@ -0,0 +1 @@
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/schema.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/schema.svg
new file mode 100644
index 0000000000..41e2702675
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/schema.svg
@@ -0,0 +1 @@
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/spinner.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/spinner.svg
new file mode 100644
index 0000000000..6ad33a62ff
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/spinner.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-canceled.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-canceled.svg
new file mode 100644
index 0000000000..14623115d9
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-canceled.svg
@@ -0,0 +1 @@
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-completed.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-completed.svg
new file mode 100644
index 0000000000..02f837325b
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-completed.svg
@@ -0,0 +1 @@
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-error.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-error.svg
new file mode 100644
index 0000000000..4a12bcea51
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-error.svg
@@ -0,0 +1 @@
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-new.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-new.svg
new file mode 100644
index 0000000000..da9890e6b9
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-new.svg
@@ -0,0 +1 @@
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-unknown.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-unknown.svg
new file mode 100644
index 0000000000..1412a8a815
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-unknown.svg
@@ -0,0 +1 @@
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-waiting.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-waiting.svg
new file mode 100644
index 0000000000..33d679b3f8
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-waiting.svg
@@ -0,0 +1 @@
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-warning.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-warning.svg
new file mode 100644
index 0000000000..41d2f062e3
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/status-warning.svg
@@ -0,0 +1 @@
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/text.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/text.svg
new file mode 100644
index 0000000000..13245abeb2
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/text.svg
@@ -0,0 +1 @@
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/toolbox.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/toolbox.svg
new file mode 100644
index 0000000000..2b0dcc2c09
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/toolbox.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/triangle.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/triangle.svg
new file mode 100644
index 0000000000..7677ef85d5
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/triangle.svg
@@ -0,0 +1 @@
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/vector-square.svg b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/vector-square.svg
new file mode 100644
index 0000000000..c1a057434f
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/custom/vector-square.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIcon/icons.ts b/packages/frontend/@n8n/design-system/src/components/N8nIcon/icons.ts
new file mode 100644
index 0000000000..cbbeefe449
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIcon/icons.ts
@@ -0,0 +1,576 @@
+import Binary from './custom/binary.svg';
+import GripLinesVertical from './custom/grip-lines-vertical.svg';
+import Json from './custom/json.svg';
+import PopOut from './custom/pop-out.svg';
+import Schema from './custom/schema.svg';
+import Spinner from './custom/spinner.svg';
+import StatusCanceled from './custom/status-canceled.svg';
+import StatusCompleted from './custom/status-completed.svg';
+import StatusError from './custom/status-error.svg';
+import StatusNew from './custom/status-new.svg';
+import StatusUnknown from './custom/status-unknown.svg';
+import StatusWaiting from './custom/status-waiting.svg';
+import StatusWarning from './custom/status-warning.svg';
+import Text from './custom/text.svg';
+import Toolbox from './custom/toolbox.svg';
+import Triangle from './custom/triangle.svg';
+import VectorSquare from './custom/vector-square.svg';
+
+import IconLucideAlignRight from '~icons/lucide/align-right';
+import IconLucideArchive from '~icons/lucide/archive';
+import IconLucideArrowDown from '~icons/lucide/arrow-down';
+import IconLucideArrowLeft from '~icons/lucide/arrow-left';
+import IconLucideArrowLeftRight from '~icons/lucide/arrow-left-right';
+import IconLucideArrowRight from '~icons/lucide/arrow-right';
+import IconLucideArrowUp from '~icons/lucide/arrow-up';
+import IconLucideAtSign from '~icons/lucide/at-sign';
+import IconLucideBan from '~icons/lucide/ban';
+import IconLucideBell from '~icons/lucide/bell';
+import IconLucideBook from '~icons/lucide/book';
+import IconLucideBot from '~icons/lucide/bot';
+import IconLucideBox from '~icons/lucide/box';
+import IconLucideBrain from '~icons/lucide/brain';
+import IconLucideBug from '~icons/lucide/bug';
+import IconLucideCalculator from '~icons/lucide/calculator';
+import IconLucideCalendar from '~icons/lucide/calendar';
+import IconLucideCaseUpper from '~icons/lucide/case-upper';
+import IconLucideChartColumnDecreasing from '~icons/lucide/chart-column-decreasing';
+import IconLucideCheck from '~icons/lucide/check';
+import IconLucideCheckCheck from '~icons/lucide/check-check';
+import IconLucideChevronDown from '~icons/lucide/chevron-down';
+import IconLucideChevronLeft from '~icons/lucide/chevron-left';
+import IconLucideChevronRight from '~icons/lucide/chevron-right';
+import IconLucideChevronUp from '~icons/lucide/chevron-up';
+import IconLucideChevronsLeft from '~icons/lucide/chevrons-left';
+import IconLucideChevronsUpDown from '~icons/lucide/chevrons-up-down';
+import IconLucideCircle from '~icons/lucide/circle';
+import IconLucideCircleAlert from '~icons/lucide/circle-alert';
+import IconLucideCircleCheck from '~icons/lucide/circle-check';
+import IconLucideCircleDot from '~icons/lucide/circle-dot';
+import IconLucideCircleHelp from '~icons/lucide/circle-help';
+import IconLucideCircleMinus from '~icons/lucide/circle-minus';
+import IconLucideCirclePause from '~icons/lucide/circle-pause';
+import IconLucideCirclePlay from '~icons/lucide/circle-play';
+import IconLucideCirclePlus from '~icons/lucide/circle-plus';
+import IconLucideCircleUserRound from '~icons/lucide/circle-user-round';
+import IconLucideCircleX from '~icons/lucide/circle-x';
+import IconLucideClipboardList from '~icons/lucide/clipboard-list';
+import IconLucideClock from '~icons/lucide/clock';
+import IconLucideCloud from '~icons/lucide/cloud';
+import IconLucideCloudDownload from '~icons/lucide/cloud-download';
+import IconLucideCode from '~icons/lucide/code';
+import IconLucideCog from '~icons/lucide/cog';
+import IconLucideContrast from '~icons/lucide/contrast';
+import IconLucideCopy from '~icons/lucide/copy';
+import IconLucideDatabase from '~icons/lucide/database';
+import IconLucideEarth from '~icons/lucide/earth';
+import IconLucideEllipsis from '~icons/lucide/ellipsis';
+import IconLucideEllipsisVertical from '~icons/lucide/ellipsis-vertical';
+import IconLucideEqual from '~icons/lucide/equal';
+import IconLucideExternalLink from '~icons/lucide/external-link';
+import IconLucideEye from '~icons/lucide/eye';
+import IconLucideEyeOff from '~icons/lucide/eye-off';
+import IconLucideFile from '~icons/lucide/file';
+import IconLucideFileArchive from '~icons/lucide/file-archive';
+import IconLucideFileCode from '~icons/lucide/file-code';
+import IconLucideFileDown from '~icons/lucide/file-down';
+import IconLucideFileInput from '~icons/lucide/file-input';
+import IconLucideFileOutput from '~icons/lucide/file-output';
+import IconLucideFileText from '~icons/lucide/file-text';
+import IconLucideFiles from '~icons/lucide/files';
+import IconLucideFingerprint from '~icons/lucide/fingerprint';
+import IconLucideFlaskConical from '~icons/lucide/flask-conical';
+import IconLucideFolder from '~icons/lucide/folder';
+import IconLucideFolderOpen from '~icons/lucide/folder-open';
+import IconLucideFolderPlus from '~icons/lucide/folder-plus';
+import IconLucideFunnel from '~icons/lucide/funnel';
+import IconLucideGem from '~icons/lucide/gem';
+import IconLucideGift from '~icons/lucide/gift';
+import IconLucideGitBranch from '~icons/lucide/git-branch';
+import IconLucideGlobe from '~icons/lucide/globe';
+import IconLucideGraduationCap from '~icons/lucide/graduation-cap';
+import IconLucideGrid2x2 from '~icons/lucide/grid-2x2';
+import IconLucideGripVertical from '~icons/lucide/grip-vertical';
+import IconLucideHandCoins from '~icons/lucide/hand-coins';
+import IconLucideHandshake from '~icons/lucide/handshake';
+import IconLucideHardDrive from '~icons/lucide/hard-drive';
+import IconLucideHardDriveDownload from '~icons/lucide/hard-drive-download';
+import IconLucideHash from '~icons/lucide/hash';
+import IconLucideHistory from '~icons/lucide/history';
+import IconLucideHourglass from '~icons/lucide/hourglass';
+import IconLucideHouse from '~icons/lucide/house';
+import IconLucideImage from '~icons/lucide/image';
+import IconLucideInbox from '~icons/lucide/inbox';
+import IconLucideInfo from '~icons/lucide/info';
+import IconLucideKeyRound from '~icons/lucide/key-round';
+import IconLucideLanguages from '~icons/lucide/languages';
+import IconLucideLayers from '~icons/lucide/layers';
+import IconLucideLightbulb from '~icons/lucide/lightbulb';
+import IconLucideLink from '~icons/lucide/link';
+import IconLucideList from '~icons/lucide/list';
+import IconLucideListChecks from '~icons/lucide/list-checks';
+import IconLucideLock from '~icons/lucide/lock';
+import IconLucideLogIn from '~icons/lucide/log-in';
+import IconLucideLogOut from '~icons/lucide/log-out';
+import IconLucideMail from '~icons/lucide/mail';
+import IconLucideMaximize from '~icons/lucide/maximize';
+import IconLucideMaximize2 from '~icons/lucide/maximize-2';
+import IconLucideMenu from '~icons/lucide/menu';
+import IconLucideMessageCircle from '~icons/lucide/message-circle';
+import IconLucideMessagesSquare from '~icons/lucide/messages-square';
+import IconLucideMilestone from '~icons/lucide/milestone';
+import IconLucideMinimize2 from '~icons/lucide/minimize-2';
+import IconLucideMousePointer from '~icons/lucide/mouse-pointer';
+import IconLucideNetwork from '~icons/lucide/network';
+import IconLucidePackageOpen from '~icons/lucide/package-open';
+import IconLucidePalette from '~icons/lucide/palette';
+import IconLucidePause from '~icons/lucide/pause';
+import IconLucidePen from '~icons/lucide/pen';
+import IconLucidePencil from '~icons/lucide/pencil';
+import IconLucidePin from '~icons/lucide/pin';
+import IconLucidePlay from '~icons/lucide/play';
+import IconLucidePlug from '~icons/lucide/plug';
+import IconLucidePlus from '~icons/lucide/plus';
+import IconLucidePocketKnife from '~icons/lucide/pocket-knife';
+import IconLucidePower from '~icons/lucide/power';
+import IconLucideRedo2 from '~icons/lucide/redo-2';
+import IconLucideRefreshCw from '~icons/lucide/refresh-cw';
+import IconLucideRemoveFormatting from '~icons/lucide/remove-formatting';
+import IconLucideRss from '~icons/lucide/rss';
+import IconLucideSatelliteDish from '~icons/lucide/satellite-dish';
+import IconLucideSave from '~icons/lucide/save';
+import IconLucideScale from '~icons/lucide/scale';
+import IconLucideScissors from '~icons/lucide/scissors';
+import IconLucideSearch from '~icons/lucide/search';
+import IconLucideSend from '~icons/lucide/send';
+import IconLucideServer from '~icons/lucide/server';
+import IconLucideShare from '~icons/lucide/share';
+import IconLucideSlidersHorizontal from '~icons/lucide/sliders-horizontal';
+import IconLucideSmile from '~icons/lucide/smile';
+import IconLucideSquare from '~icons/lucide/square';
+import IconLucideSquareCheck from '~icons/lucide/square-check';
+import IconLucideSquarePen from '~icons/lucide/square-pen';
+import IconLucideSquarePlus from '~icons/lucide/square-plus';
+import IconLucideStickyNote from '~icons/lucide/sticky-note';
+import IconLucideSun from '~icons/lucide/sun';
+import IconLucideTable from '~icons/lucide/table';
+import IconLucideTags from '~icons/lucide/tags';
+import IconLucideTerminal from '~icons/lucide/terminal';
+import IconLucideThumbsDown from '~icons/lucide/thumbs-down';
+import IconLucideThumbsUp from '~icons/lucide/thumbs-up';
+import IconLucideTrash2 from '~icons/lucide/trash-2';
+import IconLucideTreePine from '~icons/lucide/tree-pine';
+import IconLucideTriangleAlert from '~icons/lucide/triangle-alert';
+import IconLucideUndo2 from '~icons/lucide/undo-2';
+import IconLucideUnlink from '~icons/lucide/unlink';
+import IconLucideUser from '~icons/lucide/user';
+import IconLucideUserCheck from '~icons/lucide/user-check';
+import IconLucideUserLock from '~icons/lucide/user-lock';
+import IconLucideUserRound from '~icons/lucide/user-round';
+import IconLucideUsers from '~icons/lucide/users';
+import IconLucideVariable from '~icons/lucide/variable';
+import IconLucideVault from '~icons/lucide/vault';
+import IconLucideVideo from '~icons/lucide/video';
+import IconLucideWaypoints from '~icons/lucide/waypoints';
+import IconLucideWrench from '~icons/lucide/wrench';
+import IconLucideX from '~icons/lucide/x';
+import IconLucideZap from '~icons/lucide/zap';
+import IconLucideZoomIn from '~icons/lucide/zoom-in';
+import IconLucideZoomOut from '~icons/lucide/zoom-out';
+
+/**
+ * Need to keep old icon names
+ * To support old project icons
+ * Which used to include all icons in instance
+ */
+export const deprecatedIconSet = {
+ // customIcons
+ variable: IconLucideVariable,
+ 'pop-out': PopOut,
+ triangle: Triangle,
+ 'status-completed': StatusCompleted,
+ 'status-waiting': StatusWaiting,
+ 'status-error': StatusError,
+ 'status-canceled': StatusCanceled,
+ 'status-new': StatusNew,
+ 'status-unknown': StatusUnknown,
+ 'status-warning': StatusWarning,
+ 'vector-square': VectorSquare,
+ schema: Schema,
+ json: Json,
+ binary: Binary,
+ text: Text,
+ toolbox: Toolbox,
+ spinner: Spinner,
+ xmark: IconLucideX,
+
+ // fontAwesomeIcons
+ 'caret-up': IconLucideChevronUp,
+ 'caret-down': IconLucideChevronDown,
+ 'caret-right': IconLucideChevronRight,
+ 'caret-left': IconLucideChevronLeft,
+ 'folder-plus': IconLucideFolderPlus,
+ share: IconLucideShare,
+ 'user-check': IconLucideUserCheck,
+ 'check-double': IconLucideCheckCheck,
+ 'exclamation-circle': IconLucideCircleAlert,
+ circle: IconLucideCircle,
+ 'eye-slash': IconLucideEyeOff,
+ folder: IconLucideFolder,
+ 'minus-circle': IconLucideCircleMinus,
+ adjust: IconLucideContrast,
+ refresh: IconLucideRefreshCw,
+ vault: IconLucideVault,
+ 'angle-double-left': IconLucideChevronsLeft,
+ 'angle-down': IconLucideChevronDown,
+ 'angle-left': IconLucideChevronLeft,
+ 'angle-right': IconLucideChevronRight,
+ 'angle-up': IconLucideChevronUp,
+ archive: IconLucideArchive,
+ 'arrow-left': IconLucideArrowLeft,
+ 'arrow-right': IconLucideArrowRight,
+ 'arrow-up': IconLucideArrowUp,
+ 'arrow-down': IconLucideArrowDown,
+ at: IconLucideAtSign,
+ ban: IconLucideBan,
+ 'balance-scale-left': IconLucideScale,
+ bars: IconLucideMenu,
+ bolt: IconLucideZap,
+ book: IconLucideBook,
+ 'box-open': IconLucidePackageOpen,
+ bug: IconLucideBug,
+ brain: IconLucideBrain,
+ calculator: IconLucideCalculator,
+ calendar: IconLucideCalendar,
+ 'chart-bar': IconLucideChartColumnDecreasing,
+ check: IconLucideCheck,
+ 'check-circle': IconLucideCircleCheck,
+ 'check-square': IconLucideSquareCheck,
+ 'chevron-left': IconLucideChevronLeft,
+ 'chevron-right': IconLucideChevronRight,
+ 'chevron-down': IconLucideChevronDown,
+ 'chevron-up': IconLucideChevronUp,
+ code: IconLucideCode,
+ 'code-branch': IconLucideGitBranch,
+ cog: IconLucideCog,
+ cogs: IconLucideCog,
+ comment: IconLucideMessageCircle,
+ comments: IconLucideMessagesSquare,
+ 'clipboard-list': IconLucideClipboardList,
+ clock: IconLucideClock,
+ clone: IconLucideCopy,
+ cloud: IconLucideCloud,
+ 'cloud-download-alt': IconLucideCloudDownload,
+ compress: IconLucideChevronsUpDown,
+ copy: IconLucideFiles,
+ cube: IconLucideBox,
+ cut: IconLucideScissors,
+ database: IconLucideDatabase,
+ 'dot-circle': IconLucideCircleDot,
+ 'grip-lines-vertical': GripLinesVertical,
+ 'grip-vertical': IconLucideGripVertical,
+ edit: IconLucideSquarePen,
+ 'ellipsis-h': IconLucideEllipsis,
+ 'ellipsis-v': IconLucideEllipsisVertical,
+ envelope: IconLucideMail,
+ equals: IconLucideEqual,
+ eye: IconLucideEye,
+ 'exclamation-triangle': IconLucideTriangleAlert,
+ expand: IconLucideMaximize,
+ 'expand-alt': IconLucideMaximize2,
+ 'external-link-alt': IconLucideExternalLink,
+ 'exchange-alt': IconLucideArrowLeftRight,
+ file: IconLucideFile,
+ 'file-alt': IconLucideFileText,
+ 'file-archive': IconLucideFileArchive,
+ 'file-code': IconLucideFileCode,
+ 'file-download': IconLucideFileDown,
+ 'file-export': IconLucideFileOutput,
+ 'file-import': IconLucideFileInput,
+ 'file-pdf': IconLucideFileText,
+ filter: IconLucideFunnel,
+ fingerprint: IconLucideFingerprint,
+ flask: IconLucideFlaskConical,
+ 'folder-open': IconLucideFolderOpen,
+ font: IconLucideCaseUpper,
+ gift: IconLucideGift,
+ globe: IconLucideGlobe,
+ 'globe-americas': IconLucideEarth,
+ 'graduation-cap': IconLucideGraduationCap,
+ 'hand-holding-usd': IconLucideHandCoins,
+ 'hand-scissors': IconLucideScissors,
+ handshake: IconLucideHandshake,
+ 'hand-point-left': IconLucideArrowLeft,
+ hashtag: IconLucideHash,
+ hdd: IconLucideHardDrive,
+ history: IconLucideHistory,
+ home: IconLucideHouse,
+ hourglass: IconLucideHourglass,
+ image: IconLucideImage,
+ inbox: IconLucideInbox,
+ info: IconLucideInfo,
+ 'info-circle': IconLucideInfo,
+ key: IconLucideKeyRound,
+ language: IconLucideLanguages,
+ 'layer-group': IconLucideLayers,
+ link: IconLucideLink,
+ list: IconLucideList,
+ lightbulb: IconLucideLightbulb,
+ lock: IconLucideLock,
+ 'map-signs': IconLucideMilestone,
+ 'mouse-pointer': IconLucideMousePointer,
+ 'network-wired': IconLucideNetwork,
+ palette: IconLucidePalette,
+ pause: IconLucidePause,
+ 'pause-circle': IconLucideCirclePause,
+ pen: IconLucidePen,
+ 'pencil-alt': IconLucidePencil,
+ play: IconLucidePlay,
+ 'play-circle': IconLucideCirclePlay,
+ plug: IconLucidePlug,
+ plus: IconLucidePlus,
+ 'plus-circle': IconLucideCirclePlus,
+ 'plus-square': IconLucideSquarePlus,
+ 'project-diagram': IconLucideWaypoints,
+ question: IconLucideCircleHelp,
+ 'question-circle': IconLucideCircleHelp,
+ redo: IconLucideRedo2,
+ 'remove-format': IconLucideRemoveFormatting,
+ robot: IconLucideBot,
+ rss: IconLucideRss,
+ save: IconLucideSave,
+ 'satellite-dish': IconLucideSatelliteDish,
+ search: IconLucideSearch,
+ 'search-minus': IconLucideZoomOut,
+ 'search-plus': IconLucideZoomIn,
+ server: IconLucideServer,
+ screwdriver: IconLucidePocketKnife,
+ smile: IconLucideSmile,
+ 'sign-in-alt': IconLucideLogIn,
+ 'sign-out-alt': IconLucideLogOut,
+ 'sliders-h': IconLucideSlidersHorizontal,
+ 'sticky-note': IconLucideStickyNote,
+ stop: IconLucideSquare,
+ stream: IconLucideAlignRight,
+ sun: IconLucideSun,
+ sync: IconLucideRefreshCw,
+ 'sync-alt': IconLucideRefreshCw,
+ table: IconLucideTable,
+ tags: IconLucideTags,
+ tasks: IconLucideListChecks,
+ terminal: IconLucideTerminal,
+ 'th-large': IconLucideGrid2x2,
+ thumbtack: IconLucidePin,
+ 'thumbs-down': IconLucideThumbsDown,
+ 'thumbs-up': IconLucideThumbsUp,
+ times: IconLucideX,
+ 'times-circle': IconLucideCircleX,
+ tools: IconLucideWrench,
+ trash: IconLucideTrash2,
+ undo: IconLucideUndo2,
+ unlink: IconLucideUnlink,
+ user: IconLucideUser,
+ 'user-circle': IconLucideCircleUserRound,
+ 'user-friends': IconLucideUserRound,
+ users: IconLucideUsers,
+ video: IconLucideVideo,
+ tree: IconLucideTreePine,
+ 'user-lock': IconLucideUserLock,
+ gem: IconLucideGem,
+ download: IconLucideHardDriveDownload,
+ 'power-off': IconLucidePower,
+ 'paper-plane': IconLucideSend,
+ bell: IconLucideBell,
+} as const;
+
+export const updatedIconSet = {
+ // custom icons
+ 'grip-lines-vertical': GripLinesVertical,
+ variable: IconLucideVariable,
+ 'pop-out': PopOut,
+ triangle: Triangle,
+ 'status-completed': StatusCompleted,
+ 'status-waiting': StatusWaiting,
+ 'status-error': StatusError,
+ 'status-canceled': StatusCanceled,
+ 'status-new': StatusNew,
+ 'status-unknown': StatusUnknown,
+ 'status-warning': StatusWarning,
+ 'vector-square': VectorSquare,
+ schema: Schema,
+ json: Json,
+ binary: Binary,
+ text: Text,
+ toolbox: Toolbox,
+ spinner: Spinner,
+
+ // lucide
+ 'align-right': IconLucideAlignRight,
+ archive: IconLucideArchive,
+ 'arrow-down': IconLucideArrowDown,
+ 'arrow-left': IconLucideArrowLeft,
+ 'arrow-left-right': IconLucideArrowLeftRight,
+ 'arrow-right': IconLucideArrowRight,
+ 'arrow-up': IconLucideArrowUp,
+ 'at-sign': IconLucideAtSign,
+ ban: IconLucideBan,
+ bell: IconLucideBell,
+ book: IconLucideBook,
+ bot: IconLucideBot,
+ box: IconLucideBox,
+ brain: IconLucideBrain,
+ bug: IconLucideBug,
+ calculator: IconLucideCalculator,
+ calendar: IconLucideCalendar,
+ 'case-upper': IconLucideCaseUpper,
+ 'chart-column-decreasing': IconLucideChartColumnDecreasing,
+ check: IconLucideCheck,
+ 'check-check': IconLucideCheckCheck,
+ 'chevron-down': IconLucideChevronDown,
+ 'chevron-left': IconLucideChevronLeft,
+ 'chevron-right': IconLucideChevronRight,
+ 'chevron-up': IconLucideChevronUp,
+ 'chevrons-left': IconLucideChevronsLeft,
+ circle: IconLucideCircle,
+ 'circle-alert': IconLucideCircleAlert,
+ 'circle-check': IconLucideCircleCheck,
+ 'circle-dot': IconLucideCircleDot,
+ 'circle-help': IconLucideCircleHelp,
+ 'circle-minus': IconLucideCircleMinus,
+ 'circle-pause': IconLucideCirclePause,
+ 'circle-play': IconLucideCirclePlay,
+ 'circle-plus': IconLucideCirclePlus,
+ 'circle-user-round': IconLucideCircleUserRound,
+ 'circle-x': IconLucideCircleX,
+ 'clipboard-list': IconLucideClipboardList,
+ clock: IconLucideClock,
+ cloud: IconLucideCloud,
+ 'cloud-download': IconLucideCloudDownload,
+ code: IconLucideCode,
+ cog: IconLucideCog,
+ contrast: IconLucideContrast,
+ copy: IconLucideCopy,
+ database: IconLucideDatabase,
+ earth: IconLucideEarth,
+ ellipsis: IconLucideEllipsis,
+ 'ellipsis-vertical': IconLucideEllipsisVertical,
+ equal: IconLucideEqual,
+ 'external-link': IconLucideExternalLink,
+ eye: IconLucideEye,
+ 'eye-off': IconLucideEyeOff,
+ file: IconLucideFile,
+ 'file-archive': IconLucideFileArchive,
+ 'file-code': IconLucideFileCode,
+ 'file-down': IconLucideFileDown,
+ 'file-input': IconLucideFileInput,
+ 'file-output': IconLucideFileOutput,
+ 'file-text': IconLucideFileText,
+ files: IconLucideFiles,
+ fingerprint: IconLucideFingerprint,
+ 'flask-conical': IconLucideFlaskConical,
+ folder: IconLucideFolder,
+ 'folder-open': IconLucideFolderOpen,
+ 'folder-plus': IconLucideFolderPlus,
+ funnel: IconLucideFunnel,
+ gem: IconLucideGem,
+ gift: IconLucideGift,
+ 'git-branch': IconLucideGitBranch,
+ globe: IconLucideGlobe,
+ 'graduation-cap': IconLucideGraduationCap,
+ 'grid-2x2': IconLucideGrid2x2,
+ 'grip-vertical': IconLucideGripVertical,
+ 'hand-coins': IconLucideHandCoins,
+ handshake: IconLucideHandshake,
+ 'hard-drive': IconLucideHardDrive,
+ 'hard-drive-download': IconLucideHardDriveDownload,
+ hash: IconLucideHash,
+ history: IconLucideHistory,
+ hourglass: IconLucideHourglass,
+ house: IconLucideHouse,
+ image: IconLucideImage,
+ inbox: IconLucideInbox,
+ info: IconLucideInfo,
+ 'key-round': IconLucideKeyRound,
+ languages: IconLucideLanguages,
+ layers: IconLucideLayers,
+ lightbulb: IconLucideLightbulb,
+ link: IconLucideLink,
+ list: IconLucideList,
+ 'list-checks': IconLucideListChecks,
+ lock: IconLucideLock,
+ 'log-in': IconLucideLogIn,
+ 'log-out': IconLucideLogOut,
+ mail: IconLucideMail,
+ 'minimize-2': IconLucideMinimize2,
+ maximize: IconLucideMaximize,
+ 'maximize-2': IconLucideMaximize2,
+ menu: IconLucideMenu,
+ 'message-circle': IconLucideMessageCircle,
+ 'messages-square': IconLucideMessagesSquare,
+ milestone: IconLucideMilestone,
+ 'mouse-pointer': IconLucideMousePointer,
+ network: IconLucideNetwork,
+ 'package-open': IconLucidePackageOpen,
+ palette: IconLucidePalette,
+ pause: IconLucidePause,
+ pen: IconLucidePen,
+ pencil: IconLucidePencil,
+ pin: IconLucidePin,
+ play: IconLucidePlay,
+ plug: IconLucidePlug,
+ plus: IconLucidePlus,
+ 'pocket-knife': IconLucidePocketKnife,
+ power: IconLucidePower,
+ 'redo-2': IconLucideRedo2,
+ 'refresh-cw': IconLucideRefreshCw,
+ 'remove-formatting': IconLucideRemoveFormatting,
+ rss: IconLucideRss,
+ 'satellite-dish': IconLucideSatelliteDish,
+ save: IconLucideSave,
+ scale: IconLucideScale,
+ scissors: IconLucideScissors,
+ search: IconLucideSearch,
+ send: IconLucideSend,
+ server: IconLucideServer,
+ share: IconLucideShare,
+ 'sliders-horizontal': IconLucideSlidersHorizontal,
+ smile: IconLucideSmile,
+ square: IconLucideSquare,
+ 'square-check': IconLucideSquareCheck,
+ 'square-pen': IconLucideSquarePen,
+ 'square-plus': IconLucideSquarePlus,
+ 'sticky-note': IconLucideStickyNote,
+ sun: IconLucideSun,
+ table: IconLucideTable,
+ tags: IconLucideTags,
+ terminal: IconLucideTerminal,
+ 'thumbs-down': IconLucideThumbsDown,
+ 'thumbs-up': IconLucideThumbsUp,
+ 'trash-2': IconLucideTrash2,
+ 'tree-pine': IconLucideTreePine,
+ 'triangle-alert': IconLucideTriangleAlert,
+ 'undo-2': IconLucideUndo2,
+ unlink: IconLucideUnlink,
+ user: IconLucideUser,
+ 'user-check': IconLucideUserCheck,
+ 'user-lock': IconLucideUserLock,
+ 'user-round': IconLucideUserRound,
+ users: IconLucideUsers,
+ vault: IconLucideVault,
+ video: IconLucideVideo,
+ waypoints: IconLucideWaypoints,
+ wrench: IconLucideWrench,
+ x: IconLucideX,
+ zap: IconLucideZap,
+ 'zoom-in': IconLucideZoomIn,
+ 'zoom-out': IconLucideZoomOut,
+} as const;
+
+export type IconName = keyof typeof updatedIconSet; // only new icon names should be used moving forward
+
+export function isSupportedIconName(iconName?: string): iconName is IconName {
+ // support both deprecated and updated icon names
+ return (
+ typeof iconName === 'string' && (iconName in updatedIconSet || iconName in deprecatedIconSet)
+ );
+}
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/IconPicker.stories.ts b/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/IconPicker.stories.ts
index b03a0cc332..00dfc78550 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/IconPicker.stories.ts
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/IconPicker.stories.ts
@@ -1,9 +1,8 @@
import { action } from '@storybook/addon-actions';
import type { StoryFn } from '@storybook/vue3';
-import { TEST_ICONS } from './constants';
-import type { Icon } from './IconPicker.vue';
import N8nIconPicker from './IconPicker.vue';
+import { type IconOrEmoji } from './types';
export default {
title: 'Atoms/Icon Picker',
@@ -19,7 +18,7 @@ export default {
},
};
-function createTemplate(icon: Icon): StoryFn {
+function createTemplate(icon: IconOrEmoji): StoryFn {
return (args, { argTypes }) => ({
components: { N8nIconPicker },
props: Object.keys(argTypes),
@@ -39,13 +38,11 @@ const DefaultTemplate = createTemplate({ type: 'icon', value: 'smile' });
export const Default = DefaultTemplate.bind({});
Default.args = {
buttonTooltip: 'Select an icon',
- availableIcons: TEST_ICONS,
};
-const CustomTooltipTemplate = createTemplate({ type: 'icon', value: 'layer-group' });
+const CustomTooltipTemplate = createTemplate({ type: 'icon', value: 'layers' });
export const WithCustomIconAndTooltip = CustomTooltipTemplate.bind({});
WithCustomIconAndTooltip.args = {
- availableIcons: [...TEST_ICONS],
buttonTooltip: 'Select something...',
};
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/IconPicker.test.ts b/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/IconPicker.test.ts
index 71efa28b20..59c59a6ab6 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/IconPicker.test.ts
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/IconPicker.test.ts
@@ -3,7 +3,7 @@ import { fireEvent, render } from '@testing-library/vue';
import { createRouter, createWebHistory } from 'vue-router';
import IconPicker from '.';
-import { TEST_ICONS } from './constants';
+import { ALL_ICON_PICKER_ICONS } from './constants';
// Create a proxy handler that returns a mock icon object for any icon name
// and mock the entire icon library with the proxy
@@ -42,11 +42,6 @@ const components = {
template: '
',
props: ['icon'],
},
- N8nIcon: {
- template:
- '
',
- props: ['icon'],
- },
};
describe('IconPicker', () => {
@@ -55,12 +50,11 @@ describe('IconPicker', () => {
props: {
modelValue: { type: 'icon', value: 'smile' },
buttonTooltip: 'Select an icon',
- availableIcons: TEST_ICONS,
},
global: {
plugins: [router],
components,
- stubs: ['N8nButton'],
+ stubs: ['N8nButton', 'N8nIcon'],
},
});
const TEST_EMOJI_COUNT = 1962;
@@ -71,7 +65,8 @@ describe('IconPicker', () => {
expect(getByTestId('tab-icons').className).toContain('activeTab');
expect(getByTestId('icon-picker-popup')).toBeVisible();
// All icons should be rendered
- expect(getAllByTestId('icon-picker-icon')).toHaveLength(TEST_ICONS.length);
+ expect(getAllByTestId('icon-picker-icon')).toHaveLength(ALL_ICON_PICKER_ICONS.length);
+
// Click on emojis tab
await fireEvent.click(getByTestId('tab-emojis'));
// Emojis tab should be active
@@ -79,13 +74,13 @@ describe('IconPicker', () => {
// All emojis should be rendered
expect(getAllByTestId('icon-picker-emoji')).toHaveLength(TEST_EMOJI_COUNT);
});
+
it('renders icon picker with custom icon and tooltip', async () => {
- const ICON = 'layer-group';
+ const ICON = 'layers';
const TOOLTIP = 'Select something...';
const { getByTestId, getByRole } = render(IconPicker, {
props: {
modelValue: { type: 'icon', value: ICON },
- availableIcons: [...TEST_ICONS],
buttonTooltip: TOOLTIP,
},
global: {
@@ -98,13 +93,13 @@ describe('IconPicker', () => {
expect(getByRole('tooltip').textContent).toBe(TOOLTIP);
expect(getByTestId('icon-picker-button')).toHaveAttribute('icon', ICON);
});
+
it('renders emoji as default icon correctly', async () => {
const ICON = '🔥';
const TOOLTIP = 'Select something...';
const { getByTestId, getByRole } = render(IconPicker, {
props: {
modelValue: { type: 'emoji', value: ICON },
- availableIcons: [...TEST_ICONS],
buttonTooltip: TOOLTIP,
},
global: {
@@ -116,12 +111,12 @@ describe('IconPicker', () => {
expect(getByRole('tooltip').textContent).toBe(TOOLTIP);
expect(getByTestId('icon-picker-button')).toHaveTextContent(ICON);
});
+
it('renders icon picker with only emojis', () => {
const { queryByTestId } = render(IconPicker, {
props: {
modelValue: { type: 'icon', value: 'smile' },
buttonTooltip: 'Select an emoji',
- availableIcons: [],
},
global: {
plugins: [router],
@@ -131,42 +126,43 @@ describe('IconPicker', () => {
});
expect(queryByTestId('tab-icons')).not.toBeInTheDocument();
});
+
it('is able to select an icon', async () => {
const { getByTestId, getAllByTestId, queryByTestId, emitted } = render(IconPicker, {
props: {
modelValue: { type: 'icon', value: 'smile' },
buttonTooltip: 'Select an icon',
- availableIcons: TEST_ICONS,
},
global: {
plugins: [router],
components,
- stubs: ['N8nButton'],
+ stubs: ['N8nIcon', 'N8nButton'],
},
});
await fireEvent.click(getByTestId('icon-picker-button'));
// Select the first icon
await fireEvent.click(getAllByTestId('icon-picker-icon')[0]);
// Icon should be selected and popup should be closed
- expect(getByTestId('icon-picker-button')).toHaveAttribute('icon', TEST_ICONS[0]);
+ expect(getByTestId('icon-picker-button')).toHaveAttribute('icon', ALL_ICON_PICKER_ICONS[0]);
expect(queryByTestId('icon-picker-popup')).toBeNull();
expect(emitted()).toHaveProperty('update:modelValue');
// Should emit the selected icon
expect((emitted()['update:modelValue'] as unknown[][])[0][0]).toEqual({
type: 'icon',
- value: TEST_ICONS[0],
+ value: ALL_ICON_PICKER_ICONS[0],
});
});
+
it('is able to select an emoji', async () => {
const { getByTestId, getAllByTestId, queryByTestId, emitted } = render(IconPicker, {
props: {
modelValue: { type: 'emoji', value: '🔥' },
buttonTooltip: 'Select an emoji',
- availableIcons: TEST_ICONS,
},
global: {
plugins: [router],
components,
+ stubs: ['N8nIcon'],
},
});
await fireEvent.click(getByTestId('icon-picker-button'));
@@ -174,6 +170,7 @@ describe('IconPicker', () => {
expect(getByTestId('icon-picker-popup')).toBeVisible();
// Select the first emoji
await fireEvent.click(getAllByTestId('icon-picker-emoji')[0]);
+
// Emoji should be selected and popup should be closed
expect(getByTestId('icon-picker-button')).toHaveTextContent('😀');
expect(queryByTestId('icon-picker-popup')).toBeNull();
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/IconPicker.vue b/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/IconPicker.vue
index 1e89e51171..ec7eadecbd 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/IconPicker.vue
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/IconPicker.vue
@@ -5,6 +5,8 @@ import { onClickOutside } from '@vueuse/core';
import { isEmojiSupported } from 'is-emoji-supported';
import { ref, computed } from 'vue';
+import { ALL_ICON_PICKER_ICONS } from './constants';
+import type { IconOrEmoji } from './types';
import { useI18n } from '../../composables/useI18n';
import N8nButton from '../N8nButton';
import N8nIcon from '../N8nIcon';
@@ -31,27 +33,18 @@ const emojiRanges = [
[0x1f400, 0x1f4ff], // Additional pictographs
];
-export type Icon = {
- type: 'icon' | 'emoji';
- value: string;
-};
-
type Props = {
buttonTooltip: string;
- availableIcons: string[];
buttonSize?: 'small' | 'large';
};
const { t } = useI18n();
const props = withDefaults(defineProps
(), {
- availableIcons: () => [],
buttonSize: 'large',
});
-const model = defineModel({ default: { type: 'icon', value: 'smile' } });
-
-const hasAvailableIcons = computed(() => props.availableIcons.length > 0);
+const model = defineModel({ default: { type: 'icon', value: 'smile' } });
const emojis = computed(() => {
const emojisArray: string[] = [];
@@ -67,15 +60,11 @@ const emojis = computed(() => {
});
const popupVisible = ref(false);
-const tabs = ref>(
- hasAvailableIcons.value
- ? [
- { value: 'icons', label: t('iconPicker.tabs.icons') },
- { value: 'emojis', label: t('iconPicker.tabs.emojis') },
- ]
- : [{ value: 'emojis', label: t('iconPicker.tabs.emojis') }],
-);
-const selectedTab = ref(tabs.value[0].value);
+const tabs: Array<{ value: string; label: string }> = [
+ { value: 'icons', label: t('iconPicker.tabs.icons') },
+ { value: 'emojis', label: t('iconPicker.tabs.emojis') },
+];
+const selectedTab = ref(tabs[0].value);
const container = ref();
@@ -83,7 +72,7 @@ onClickOutside(container, () => {
popupVisible.value = false;
});
-const selectIcon = (value: Icon) => {
+const selectIcon = (value: IconOrEmoji) => {
model.value = value;
popupVisible.value = false;
};
@@ -91,7 +80,7 @@ const selectIcon = (value: Icon) => {
const togglePopup = () => {
popupVisible.value = !popupVisible.value;
if (popupVisible.value) {
- selectedTab.value = tabs.value[0].value;
+ selectedTab.value = tabs[0].value;
}
};
@@ -112,7 +101,7 @@ const togglePopup = () => {
{
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/constants.ts b/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/constants.ts
index ca62c03773..ac302f286e 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/constants.ts
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/constants.ts
@@ -1,163 +1,184 @@
-export const TEST_ICONS = [
- 'angle-double-left',
- 'angle-down',
- 'angle-left',
- 'angle-right',
- 'angle-up',
+import type { IconName } from '../N8nIcon/icons';
+
+export const ALL_ICON_PICKER_ICONS: IconName[] = [
+ 'folder-plus',
+ 'share',
+ 'user-check',
+ 'check-check',
+ 'circle',
+ 'eye-off',
+ 'folder',
+ 'circle-minus',
+ 'contrast',
+ 'refresh-cw',
+ 'vault',
+ 'chevrons-left',
'archive',
'arrow-left',
'arrow-right',
'arrow-up',
'arrow-down',
- 'at',
+ 'at-sign',
'ban',
- 'balance-scale-left',
- 'bars',
- 'bolt',
+ 'scale',
+ 'menu',
+ 'zap',
'book',
- 'box-open',
+ 'package-open',
'bug',
'brain',
'calculator',
'calendar',
- 'chart-bar',
+ 'chart-column-decreasing',
'check',
- 'check-circle',
- 'check-square',
+ 'circle-check',
+ 'square-check',
'chevron-left',
'chevron-right',
'chevron-down',
'chevron-up',
'code',
- 'code-branch',
+ 'git-branch',
'cog',
- 'cogs',
- 'comment',
- 'comments',
+ 'message-circle',
+ 'messages-square',
'clipboard-list',
'clock',
- 'clone',
- 'cloud',
- 'cloud-download-alt',
'copy',
- 'cube',
- 'cut',
+ 'cloud',
+ 'cloud-download',
+ 'files',
+ 'box',
+ 'scissors',
'database',
- 'dot-circle',
+ 'circle-dot',
'grip-lines-vertical',
'grip-vertical',
- 'edit',
- 'ellipsis-h',
- 'ellipsis-v',
- 'envelope',
- 'equals',
+ 'square-pen',
+ 'ellipsis',
+ 'ellipsis-vertical',
+ 'mail',
+ 'equal',
'eye',
- 'exclamation-triangle',
- 'expand',
- 'expand-alt',
- 'external-link-alt',
- 'exchange-alt',
+ 'triangle-alert',
+ 'maximize',
+ 'maximize-2',
+ 'external-link',
+ 'arrow-left-right',
'file',
- 'file-alt',
+ 'file-text',
'file-archive',
'file-code',
- 'file-download',
- 'file-export',
- 'file-import',
- 'file-pdf',
- 'filter',
+ 'file-down',
+ 'file-output',
+ 'file-input',
+ 'file-text',
+ 'funnel',
'fingerprint',
- 'flask',
+ 'flask-conical',
'folder-open',
- 'font',
+ 'case-upper',
'gift',
'globe',
- 'globe-americas',
+ 'earth',
'graduation-cap',
- 'hand-holding-usd',
- 'hand-scissors',
+ 'hand-coins',
+ 'scissors',
'handshake',
- 'hand-point-left',
- 'hashtag',
- 'hdd',
+ 'arrow-left',
+ 'hash',
+ 'hard-drive',
'history',
- 'home',
+ 'house',
'hourglass',
'image',
'inbox',
'info',
- 'info-circle',
- 'key',
- 'language',
- 'layer-group',
+ 'key-round',
+ 'languages',
+ 'layers',
'link',
'list',
'lightbulb',
'lock',
- 'map-signs',
+ 'milestone',
'mouse-pointer',
- 'network-wired',
+ 'network',
'palette',
'pause',
- 'pause-circle',
+ 'circle-pause',
'pen',
- 'pencil-alt',
+ 'pencil',
'play',
- 'play-circle',
+ 'circle-play',
'plug',
'plus',
- 'plus-circle',
- 'plus-square',
- 'project-diagram',
- 'question',
- 'question-circle',
- 'redo',
- 'remove-format',
- 'robot',
+ 'circle-plus',
+ 'square-plus',
+ 'waypoints',
+ 'circle-help',
+ 'circle-help',
+ 'redo-2',
+ 'remove-formatting',
+ 'bot',
'rss',
'save',
'satellite-dish',
'search',
- 'search-minus',
- 'search-plus',
+ 'zoom-out',
+ 'zoom-in',
'server',
- 'screwdriver',
+ 'pocket-knife',
'smile',
- 'sign-in-alt',
- 'sign-out-alt',
- 'sliders-h',
- 'spinner',
+ 'log-in',
+ 'log-out',
+ 'sliders-horizontal',
'sticky-note',
- 'stop',
- 'stream',
+ 'square',
+ 'align-right',
'sun',
- 'sync',
- 'sync-alt',
+ 'refresh-cw',
'table',
'tags',
- 'tasks',
+ 'list-checks',
'terminal',
- 'th-large',
- 'thumbtack',
+ 'grid-2x2',
+ 'pin',
'thumbs-down',
'thumbs-up',
- 'times',
- 'times-circle',
- 'toolbox',
- 'tools',
- 'trash',
- 'undo',
+ 'x',
+ 'circle-x',
+ 'wrench',
+ 'trash-2',
+ 'undo-2',
'unlink',
'user',
- 'user-circle',
- 'user-friends',
+ 'circle-user-round',
+ 'user-round',
'users',
- 'vector-square',
'video',
- 'tree',
+ 'tree-pine',
'user-lock',
'gem',
- 'download',
- 'power-off',
- 'paper-plane',
+ 'hard-drive-download',
+ 'power',
+ 'send',
+ 'bell',
+ 'variable',
+ 'pop-out',
+ 'triangle',
+ 'status-completed',
+ 'status-waiting',
+ 'status-error',
+ 'status-canceled',
+ 'status-new',
+ 'status-unknown',
+ 'status-warning',
+ 'vector-square',
+ 'schema',
+ 'json',
+ 'binary',
+ 'text',
+ 'toolbox',
+ 'spinner',
];
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/types.ts b/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/types.ts
new file mode 100644
index 0000000000..4d02a51338
--- /dev/null
+++ b/packages/frontend/@n8n/design-system/src/components/N8nIconPicker/types.ts
@@ -0,0 +1,22 @@
+import { type IconName } from '../N8nIcon/icons';
+
+export type IconOrEmoji =
+ | {
+ type: 'icon';
+ value: IconName;
+ }
+ | {
+ type: 'emoji';
+ value: string;
+ };
+
+export function isIconOrEmoji(icon: unknown): icon is IconOrEmoji {
+ return (
+ typeof icon === 'object' &&
+ icon !== null &&
+ 'type' in icon &&
+ (icon.type === 'icon' || icon.type === 'emoji') &&
+ 'value' in icon &&
+ typeof icon.value === 'string'
+ );
+}
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nInfoAccordion/InfoAccordion.vue b/packages/frontend/@n8n/design-system/src/components/N8nInfoAccordion/InfoAccordion.vue
index 4e37d88038..e8798a6a4d 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nInfoAccordion/InfoAccordion.vue
+++ b/packages/frontend/@n8n/design-system/src/components/N8nInfoAccordion/InfoAccordion.vue
@@ -5,13 +5,14 @@ import { onMounted, ref } from 'vue';
import type { IconColor } from '@n8n/design-system/types/icon';
import N8nIcon from '../N8nIcon';
+import { type IconName } from '../N8nIcon/icons';
import N8nText from '../N8nText';
import N8nTooltip from '../N8nTooltip';
export interface IAccordionItem {
id: string;
label: string;
- icon: string;
+ icon: IconName;
iconColor?: IconColor;
tooltip?: string | null;
}
@@ -21,7 +22,7 @@ interface InfoAccordionProps {
description?: string;
items?: IAccordionItem[];
initiallyExpanded?: boolean;
- headerIcon?: { icon: string; color: IconColor };
+ headerIcon?: { icon: IconName; color: IconColor };
eventBus?: EventBus;
}
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nInfoTip/InfoTip.test.ts b/packages/frontend/@n8n/design-system/src/components/N8nInfoTip/InfoTip.test.ts
index 8f4e905138..22c86476aa 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nInfoTip/InfoTip.test.ts
+++ b/packages/frontend/@n8n/design-system/src/components/N8nInfoTip/InfoTip.test.ts
@@ -5,7 +5,7 @@ import N8nInfoTip from './InfoTip.vue';
const slots = {
default: ['Need help doing something?', '
Open docs '],
};
-const stubs = ['n8n-tooltip'];
+const stubs = ['n8n-tooltip', 'n8n-icon'];
describe('N8nInfoTip', () => {
it('should render correctly as note', () => {
@@ -30,4 +30,17 @@ describe('N8nInfoTip', () => {
});
expect(wrapper.html()).toMatchSnapshot();
});
+
+ it('should render correctly with a specific size', () => {
+ const wrapper = render(N8nInfoTip, {
+ slots,
+ props: {
+ size: 'large',
+ },
+ global: {
+ stubs,
+ },
+ });
+ expect(wrapper.html()).toMatchSnapshot();
+ });
});
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nInfoTip/InfoTip.vue b/packages/frontend/@n8n/design-system/src/components/N8nInfoTip/InfoTip.vue
index e5af26818c..891e1fe9f1 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nInfoTip/InfoTip.vue
+++ b/packages/frontend/@n8n/design-system/src/components/N8nInfoTip/InfoTip.vue
@@ -2,23 +2,25 @@
import type { Placement } from 'element-plus';
import { computed } from 'vue';
+import type { IconSize } from '@n8n/design-system/types';
import type { IconColor } from '@n8n/design-system/types/icon';
import N8nIcon from '../N8nIcon';
+import { type IconName } from '../N8nIcon/icons';
import N8nTooltip from '../N8nTooltip';
const THEME = ['info', 'info-light', 'warning', 'warning-light', 'danger', 'success'] as const;
const TYPE = ['note', 'tooltip'] as const;
-const ICON_MAP = {
- info: 'info-circle',
+const ICON_MAP: { [name: string]: IconName } = {
+ info: 'info',
// eslint-disable-next-line @typescript-eslint/naming-convention
- 'info-light': 'info-circle',
- warning: 'exclamation-triangle',
+ 'info-light': 'info',
+ warning: 'triangle-alert',
// eslint-disable-next-line @typescript-eslint/naming-convention
'warning-light': 'triangle', // NOTE: This requires a custom icon
- danger: 'exclamation-triangle',
- success: 'check-circle',
+ danger: 'triangle-alert',
+ success: 'circle-check',
} as const;
const COLOR_MAP: Record
= {
@@ -40,6 +42,7 @@ interface InfoTipProps {
bold?: boolean;
tooltipPlacement?: Placement;
enterable?: boolean;
+ size?: IconSize;
}
defineOptions({ name: 'N8nInfoTip' });
@@ -49,9 +52,10 @@ const props = withDefaults(defineProps(), {
bold: true,
tooltipPlacement: 'top',
enterable: true,
+ size: undefined,
});
-const iconData = computed<{ icon: IconMap[keyof IconMap]; color: IconColor }>(() => {
+const iconData = computed<{ icon: IconName; color: IconColor }>(() => {
return {
icon: ICON_MAP[props.theme],
color: COLOR_MAP[props.theme],
@@ -79,7 +83,7 @@ const iconData = computed<{ icon: IconMap[keyof IconMap]; color: IconColor }>(()
:enterable
>
-
+
@@ -88,7 +92,7 @@ const iconData = computed<{ icon: IconMap[keyof IconMap]; color: IconColor }>(()
-
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nInfoTip/__snapshots__/InfoTip.test.ts.snap b/packages/frontend/@n8n/design-system/src/components/N8nInfoTip/__snapshots__/InfoTip.test.ts.snap
index ef7db8dfe8..3b722b8942 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nInfoTip/__snapshots__/InfoTip.test.ts.snap
+++ b/packages/frontend/@n8n/design-system/src/components/N8nInfoTip/__snapshots__/InfoTip.test.ts.snap
@@ -3,15 +3,22 @@
exports[`N8nInfoTip > should render correctly as note 1`] = `
"
Need help doing something?Open docs
+ the slot either in the tooltip of the icon or following it -->
Need help doing something?Open docs
"
`;
exports[`N8nInfoTip > should render correctly as tooltip 1`] = `
"
+ the slot either in the tooltip of the icon or following it -->
"
`;
+
+exports[`N8nInfoTip > should render correctly with a specific size 1`] = `
+""
+`;
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nInputLabel/InputLabel.vue b/packages/frontend/@n8n/design-system/src/components/N8nInputLabel/InputLabel.vue
index 5353516798..283535a9fb 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nInputLabel/InputLabel.vue
+++ b/packages/frontend/@n8n/design-system/src/components/N8nInputLabel/InputLabel.vue
@@ -74,7 +74,7 @@ const addTargetBlank = (html: string) =>
:class="[$style.infoIcon, showTooltip ? $style.visible : $style.hidden]"
>
-
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nMenu/Menu.stories.ts b/packages/frontend/@n8n/design-system/src/components/N8nMenu/Menu.stories.ts
index cf25a63431..bf35a1ed65 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nMenu/Menu.stories.ts
+++ b/packages/frontend/@n8n/design-system/src/components/N8nMenu/Menu.stories.ts
@@ -48,7 +48,7 @@ const templateWithHeaderAndFooter: StoryFn = (args, { argTypes }) => ({
-
+
John Smithson
@@ -82,7 +82,7 @@ const templateWithAllSlots: StoryFn = (args, { argTypes }) => ({
-
+
John Smithson
@@ -95,7 +95,7 @@ const templateWithAllSlots: StoryFn = (args, { argTypes }) => ({
const menuItems = [
{
id: 'workflows',
- icon: 'network-wired',
+ icon: 'network',
label: 'Workflows',
position: 'top',
},
@@ -107,7 +107,7 @@ const menuItems = [
},
{
id: 'disabled-item',
- icon: 'times',
+ icon: 'x',
label: 'Not Available',
available: false,
position: 'top',
@@ -132,7 +132,7 @@ const menuItems = [
{ icon: 'book', label: 'Documentation', id: 'docs' },
{
id: 'disabled-submenu-item',
- icon: 'times',
+ icon: 'x',
label: 'Not Available',
available: false,
position: 'top',
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nMenuItem/MenuItem.stories.ts b/packages/frontend/@n8n/design-system/src/components/N8nMenuItem/MenuItem.stories.ts
index 8cf0accb5d..7f3451d47a 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nMenuItem/MenuItem.stories.ts
+++ b/packages/frontend/@n8n/design-system/src/components/N8nMenuItem/MenuItem.stories.ts
@@ -28,7 +28,7 @@ export const defaultMenuItem = template.bind({});
defaultMenuItem.args = {
item: {
id: 'workflows',
- icon: 'heart',
+ icon: 'home',
label: 'Workflows',
},
};
@@ -37,7 +37,7 @@ export const withSecondaryIcon = template.bind({});
withSecondaryIcon.args = {
item: {
id: 'workflows',
- icon: 'heart',
+ icon: 'home',
label: 'Workflows',
secondaryIcon: { name: 'lock', size: 'small' },
},
@@ -47,7 +47,7 @@ export const withSecondaryIconTooltip = template.bind({});
withSecondaryIconTooltip.args = {
item: {
id: 'workflows',
- icon: 'heart',
+ icon: 'home',
label: 'Workflows',
secondaryIcon: {
name: 'lock',
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nNavigationDropdown/NavigationDropdown.vue b/packages/frontend/@n8n/design-system/src/components/N8nNavigationDropdown/NavigationDropdown.vue
index ce728a44ba..b3ad9d1daf 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nNavigationDropdown/NavigationDropdown.vue
+++ b/packages/frontend/@n8n/design-system/src/components/N8nNavigationDropdown/NavigationDropdown.vue
@@ -5,12 +5,13 @@ import type { RouteLocationRaw } from 'vue-router';
import ConditionalRouterLink from '../ConditionalRouterLink';
import N8nIcon from '../N8nIcon';
+import { type IconName } from '../N8nIcon/icons';
type BaseItem = {
id: string;
title: string;
disabled?: boolean;
- icon?: string;
+ icon?: IconName;
route?: RouteLocationRaw;
};
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nNodeCreatorNode/NodeCreatorNode.vue b/packages/frontend/@n8n/design-system/src/components/N8nNodeCreatorNode/NodeCreatorNode.vue
index 0a902e7b30..fddc0b3830 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nNodeCreatorNode/NodeCreatorNode.vue
+++ b/packages/frontend/@n8n/design-system/src/components/N8nNodeCreatorNode/NodeCreatorNode.vue
@@ -1,9 +1,9 @@
@@ -88,7 +97,9 @@ const N8nNodeIcon = getCurrentInstance()?.type;
>
- {{ nodeTypeName }}
+
+ {{ nodeTypeName }}
+
@@ -100,6 +111,12 @@ const N8nNodeIcon = getCurrentInstance()?.type;
+
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nSelectableList/SelectableList.test.ts b/packages/frontend/@n8n/design-system/src/components/N8nSelectableList/SelectableList.test.ts
index 017a99f218..6aa1e922c9 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nSelectableList/SelectableList.test.ts
+++ b/packages/frontend/@n8n/design-system/src/components/N8nSelectableList/SelectableList.test.ts
@@ -19,6 +19,9 @@ describe('N8nSelectableList', () => {
modelValue: {},
inputs: [{ name: 'propA', initialValue: '' }],
},
+ global: {
+ stubs: ['n8n-icon'],
+ },
});
expect(wrapper.getByTestId('selectable-list-selectable-propA')).toBeInTheDocument();
@@ -49,6 +52,9 @@ describe('N8nSelectableList', () => {
{ name: 'propA', initialValue: '' },
],
},
+ global: {
+ stubs: ['n8n-icon'],
+ },
});
expect(wrapper.queryByTestId('selectable-list-selectable-propA')).not.toBeInTheDocument();
@@ -87,6 +93,9 @@ describe('N8nSelectableList', () => {
{ name: 'propC', initialValue: '' },
],
},
+ global: {
+ stubs: ['n8n-icon'],
+ },
});
expect(wrapper.queryByTestId('selectable-list-selectable-propA')).not.toBeInTheDocument();
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nSelectableList/SelectableList.vue b/packages/frontend/@n8n/design-system/src/components/N8nSelectableList/SelectableList.vue
index db3754ef8c..f0afe9ba33 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nSelectableList/SelectableList.vue
+++ b/packages/frontend/@n8n/design-system/src/components/N8nSelectableList/SelectableList.vue
@@ -87,9 +87,10 @@ function itemComparator(a: Item, b: Item) {
:data-test-id="`selectable-list-slot-${item.name}`"
>
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nSelectableList/__snapshots__/SelectableList.test.ts.snap b/packages/frontend/@n8n/design-system/src/components/N8nSelectableList/__snapshots__/SelectableList.test.ts.snap
index 63397a468b..ae8af05d1b 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nSelectableList/__snapshots__/SelectableList.test.ts.snap
+++ b/packages/frontend/@n8n/design-system/src/components/N8nSelectableList/__snapshots__/SelectableList.test.ts.snap
@@ -3,7 +3,8 @@
exports[`N8nSelectableList > renders disabled collection without selectables 1`] = `
"
-
"
@@ -12,10 +13,12 @@ exports[`N8nSelectableList > renders disabled collection without selectables 1`]
exports[`N8nSelectableList > renders multiple elements with some pre-selected 1`] = `
"
+ Add a propB
+ Add a propD
-
+
-
"
diff --git a/packages/frontend/@n8n/design-system/src/components/N8nTabs/Tabs.vue b/packages/frontend/@n8n/design-system/src/components/N8nTabs/Tabs.vue
index 284d90d0c0..e28e776659 100644
--- a/packages/frontend/@n8n/design-system/src/components/N8nTabs/Tabs.vue
+++ b/packages/frontend/@n8n/design-system/src/components/N8nTabs/Tabs.vue
@@ -97,7 +97,7 @@ const scrollRight = () => scroll(50);
{{ option.label }}
-
+
diff --git a/packages/frontend/@n8n/design-system/src/types/action-dropdown.ts b/packages/frontend/@n8n/design-system/src/types/action-dropdown.ts
index e748785f1c..40b19851a9 100644
--- a/packages/frontend/@n8n/design-system/src/types/action-dropdown.ts
+++ b/packages/frontend/@n8n/design-system/src/types/action-dropdown.ts
@@ -1,11 +1,13 @@
import type { KeyboardShortcut } from '@n8n/design-system/types/keyboardshortcut';
+import type { IconName } from '../components/N8nIcon/icons';
+
export interface ActionDropdownItem {
id: string;
label: string;
badge?: string;
badgeProps?: Record
;
- icon?: string;
+ icon?: IconName;
divided?: boolean;
disabled?: boolean;
shortcut?: KeyboardShortcut;
diff --git a/packages/frontend/@n8n/design-system/src/types/button.ts b/packages/frontend/@n8n/design-system/src/types/button.ts
index 9f21adbb9a..4509938ab9 100644
--- a/packages/frontend/@n8n/design-system/src/types/button.ts
+++ b/packages/frontend/@n8n/design-system/src/types/button.ts
@@ -1,5 +1,6 @@
import { type IconSize } from './icon';
import type { TextFloat } from './text';
+import type { IconName } from '../components/N8nIcon/icons';
const BUTTON_ELEMENT = ['button', 'a'] as const;
export type ButtonElement = (typeof BUTTON_ELEMENT)[number];
@@ -17,7 +18,7 @@ export interface IconButtonProps {
active?: boolean;
disabled?: boolean;
float?: TextFloat;
- icon?: string | string[];
+ icon?: IconName;
loading?: boolean;
outline?: boolean;
size?: ButtonSize;
diff --git a/packages/frontend/@n8n/design-system/src/types/menu.ts b/packages/frontend/@n8n/design-system/src/types/menu.ts
index 353c8ab438..3cb769d2a7 100644
--- a/packages/frontend/@n8n/design-system/src/types/menu.ts
+++ b/packages/frontend/@n8n/design-system/src/types/menu.ts
@@ -3,13 +3,17 @@ import type { AnchorHTMLAttributes, Component } from 'vue';
import type { RouteLocationRaw, RouterLinkProps } from 'vue-router';
import type { IconColor } from './icon';
+import type { IconName } from '../components/N8nIcon/icons';
export type IMenuItem = {
id: string;
label: string;
- icon?: string | { type: 'icon' | 'emoji'; value: string; color?: IconColor };
+ icon?:
+ | IconName
+ | { type: 'icon'; value: IconName; color?: IconColor }
+ | { type: 'emoji'; value: string; color?: IconColor };
secondaryIcon?: {
- name: string;
+ name: IconName;
size?: 'xsmall' | 'small' | 'medium' | 'large';
tooltip?: Partial;
};
diff --git a/packages/frontend/@n8n/design-system/src/types/tabs.ts b/packages/frontend/@n8n/design-system/src/types/tabs.ts
index 68dfa3eccb..9739882f56 100644
--- a/packages/frontend/@n8n/design-system/src/types/tabs.ts
+++ b/packages/frontend/@n8n/design-system/src/types/tabs.ts
@@ -1,9 +1,11 @@
import type { RouteLocationRaw } from 'vue-router';
+import type { IconName } from '../components/N8nIcon/icons';
+
export interface TabOptions {
value: Value;
label?: string;
- icon?: string;
+ icon?: IconName;
href?: string;
tooltip?: string;
align?: 'left' | 'right';
diff --git a/packages/frontend/@n8n/design-system/tsconfig.json b/packages/frontend/@n8n/design-system/tsconfig.json
index 3e0c2d0daa..95d580f365 100644
--- a/packages/frontend/@n8n/design-system/tsconfig.json
+++ b/packages/frontend/@n8n/design-system/tsconfig.json
@@ -4,7 +4,7 @@
"baseUrl": ".",
"rootDirs": [".", "../composables/src"],
"outDir": "dist",
- "types": ["vite/client", "vitest/globals"],
+ "types": ["vite/client", "unplugin-icons/types/vue", "vitest/globals"],
"typeRoots": [
"./node_modules/@testing-library",
"./node_modules/@types",
diff --git a/packages/frontend/@n8n/design-system/vite.config.mts b/packages/frontend/@n8n/design-system/vite.config.mts
index d05987b6a3..5fce1c4433 100644
--- a/packages/frontend/@n8n/design-system/vite.config.mts
+++ b/packages/frontend/@n8n/design-system/vite.config.mts
@@ -21,7 +21,7 @@ export default mergeConfig(
dts: false,
resolvers: [
iconsResolver({
- prefix: 'icon',
+ prefix: 'Icon',
}),
],
}),
diff --git a/packages/frontend/editor-ui/package.json b/packages/frontend/editor-ui/package.json
index 8510691198..bc3d3244d4 100644
--- a/packages/frontend/editor-ui/package.json
+++ b/packages/frontend/editor-ui/package.json
@@ -103,7 +103,7 @@
},
"devDependencies": {
"@faker-js/faker": "^8.0.2",
- "@iconify/json": "^2.2.228",
+ "@iconify/json": "^2.2.349",
"@n8n/eslint-config": "workspace:*",
"@n8n/typescript-config": "workspace:*",
"@n8n/vitest-config": "workspace:*",
@@ -121,8 +121,8 @@
"browserslist-to-esbuild": "^2.1.1",
"fake-indexeddb": "^6.0.0",
"miragejs": "^0.1.48",
- "unplugin-icons": "^0.19.0",
- "unplugin-vue-components": "^0.27.2",
+ "unplugin-icons": "catalog:frontend",
+ "unplugin-vue-components": "catalog:frontend",
"vite": "catalog:",
"vite-plugin-static-copy": "2.2.0",
"vite-svg-loader": "5.1.0",
diff --git a/packages/frontend/editor-ui/src/Interface.ts b/packages/frontend/editor-ui/src/Interface.ts
index 1e15f898e6..09a66dc160 100644
--- a/packages/frontend/editor-ui/src/Interface.ts
+++ b/packages/frontend/editor-ui/src/Interface.ts
@@ -70,6 +70,7 @@ import type { BulkCommand, Undoable } from '@/models/history';
import type { ProjectSharingData } from '@/types/projects.types';
import type { PathItem } from '@n8n/design-system/components/N8nBreadcrumbs/Breadcrumbs.vue';
+import { type IconName } from '@n8n/design-system/src/components/N8nIcon/icons';
export * from '@n8n/design-system/types';
@@ -668,7 +669,7 @@ export type SimplifiedNodeType = Pick<
export interface SubcategoryItemProps {
description?: string;
iconType?: string;
- icon?: string;
+ icon?: IconName;
iconProps?: {
color?: string;
};
@@ -1072,7 +1073,7 @@ export interface ITab {
value: Value;
label?: string;
href?: string;
- icon?: string;
+ icon?: IconName;
align?: 'right';
tooltip?: string;
}
diff --git a/packages/frontend/editor-ui/src/components/AssignmentCollection/Assignment.vue b/packages/frontend/editor-ui/src/components/AssignmentCollection/Assignment.vue
index 081b8bc186..ffe01f0e5e 100644
--- a/packages/frontend/editor-ui/src/components/AssignmentCollection/Assignment.vue
+++ b/packages/frontend/editor-ui/src/components/AssignmentCollection/Assignment.vue
@@ -137,7 +137,7 @@ const onBlur = (): void => {
type="tertiary"
text
size="mini"
- icon="trash"
+ icon="trash-2"
data-test-id="assignment-remove"
:class="[$style.iconButton, $style.extraTopPadding]"
@click="onRemove"
diff --git a/packages/frontend/editor-ui/src/components/AssignmentCollection/TypeSelect.vue b/packages/frontend/editor-ui/src/components/AssignmentCollection/TypeSelect.vue
index 413586c1d7..d5d0843f46 100644
--- a/packages/frontend/editor-ui/src/components/AssignmentCollection/TypeSelect.vue
+++ b/packages/frontend/editor-ui/src/components/AssignmentCollection/TypeSelect.vue
@@ -3,6 +3,7 @@ import { useI18n } from '@n8n/i18n';
import type { BaseTextKey } from '@n8n/i18n';
import { ASSIGNMENT_TYPES } from './constants';
import { computed } from 'vue';
+import { type IconName } from '@n8n/design-system/components/N8nIcon/icons';
interface Props {
modelValue: string;
@@ -19,7 +20,9 @@ const i18n = useI18n();
const types = ASSIGNMENT_TYPES;
-const icon = computed(() => types.find((type) => type.type === props.modelValue)?.icon ?? 'cube');
+const icon = computed(
+ (): IconName => types.find((type) => type.type === props.modelValue)?.icon ?? 'box',
+);
const onTypeChange = (type: string): void => {
emit('update:model-value', type);
diff --git a/packages/frontend/editor-ui/src/components/AssignmentCollection/constants.ts b/packages/frontend/editor-ui/src/components/AssignmentCollection/constants.ts
index c448f96ce8..8c18216151 100644
--- a/packages/frontend/editor-ui/src/components/AssignmentCollection/constants.ts
+++ b/packages/frontend/editor-ui/src/components/AssignmentCollection/constants.ts
@@ -1,7 +1,9 @@
-export const ASSIGNMENT_TYPES = [
- { type: 'string', icon: 'font' },
- { type: 'number', icon: 'hashtag' },
- { type: 'boolean', icon: 'check-square' },
+import { type IconName } from '@n8n/design-system/components/N8nIcon/icons';
+
+export const ASSIGNMENT_TYPES: Array<{ type: string; icon: IconName }> = [
+ { type: 'string', icon: 'case-upper' },
+ { type: 'number', icon: 'hash' },
+ { type: 'boolean', icon: 'square-check' },
{ type: 'array', icon: 'list' },
- { type: 'object', icon: 'cube' },
+ { type: 'object', icon: 'box' },
];
diff --git a/packages/frontend/editor-ui/src/components/Banner.vue b/packages/frontend/editor-ui/src/components/Banner.vue
index e418002d4e..ecbeeab276 100644
--- a/packages/frontend/editor-ui/src/components/Banner.vue
+++ b/packages/frontend/editor-ui/src/components/Banner.vue
@@ -37,8 +37,8 @@ const onClick = () => {
-
diff --git a/packages/frontend/editor-ui/src/components/BecomeTemplateCreatorCta/BecomeTemplateCreatorCta.vue b/packages/frontend/editor-ui/src/components/BecomeTemplateCreatorCta/BecomeTemplateCreatorCta.vue
index ced0ee8717..02d242a8bf 100644
--- a/packages/frontend/editor-ui/src/components/BecomeTemplateCreatorCta/BecomeTemplateCreatorCta.vue
+++ b/packages/frontend/editor-ui/src/components/BecomeTemplateCreatorCta/BecomeTemplateCreatorCta.vue
@@ -28,7 +28,7 @@ const onClick = () => {
data-test-id="close-become-template-creator-cta"
@click="store.dismissCta()"
>
-
+
diff --git a/packages/frontend/editor-ui/src/components/CodeNodeEditor/AskAI/AskAI.vue b/packages/frontend/editor-ui/src/components/CodeNodeEditor/AskAI/AskAI.vue
index d0eecee057..3b0abfc7b1 100644
--- a/packages/frontend/editor-ui/src/components/CodeNodeEditor/AskAI/AskAI.vue
+++ b/packages/frontend/editor-ui/src/components/CodeNodeEditor/AskAI/AskAI.vue
@@ -256,7 +256,7 @@ onMounted(() => {
v-text="`${prompt.length} / ${ASK_AI_MAX_PROMPT_LENGTH}`"
/>
- {{
+ {{
i18n.baseText('codeNodeEditor.askAi.help')
}}
diff --git a/packages/frontend/editor-ui/src/components/CommunityPackageCard.vue b/packages/frontend/editor-ui/src/components/CommunityPackageCard.vue
index f6daf1c4c5..e7c8088371 100644
--- a/packages/frontend/editor-ui/src/components/CommunityPackageCard.vue
+++ b/packages/frontend/editor-ui/src/components/CommunityPackageCard.vue
@@ -133,7 +133,7 @@ watch(
{{ i18n.baseText('settings.communityNodes.failedToLoad.tooltip') }}
-
+
-
+
diff --git a/packages/frontend/editor-ui/src/components/CommunityPackageInstallModal.vue b/packages/frontend/editor-ui/src/components/CommunityPackageInstallModal.vue
index bc92f2eb97..2d1f3e0687 100644
--- a/packages/frontend/editor-ui/src/components/CommunityPackageInstallModal.vue
+++ b/packages/frontend/editor-ui/src/components/CommunityPackageInstallModal.vue
@@ -110,7 +110,7 @@ const onLearnMoreLinkClick = () => {
diff --git a/packages/frontend/editor-ui/src/components/CredentialEdit/CredentialConfig.vue b/packages/frontend/editor-ui/src/components/CredentialEdit/CredentialConfig.vue
index 96a0e34c35..f27f0b2903 100644
--- a/packages/frontend/editor-ui/src/components/CredentialEdit/CredentialConfig.vue
+++ b/packages/frontend/editor-ui/src/components/CredentialEdit/CredentialConfig.vue
@@ -225,7 +225,7 @@ watch(showOAuthSuccessBanner, (newValue, oldValue) => {
-
+
{{ i18n.baseText('freeAi.credits.credentials.edit') }}
diff --git a/packages/frontend/editor-ui/src/components/CredentialEdit/CredentialEdit.vue b/packages/frontend/editor-ui/src/components/CredentialEdit/CredentialEdit.vue
index 377a117f64..03cca5810c 100644
--- a/packages/frontend/editor-ui/src/components/CredentialEdit/CredentialEdit.vue
+++ b/packages/frontend/editor-ui/src/components/CredentialEdit/CredentialEdit.vue
@@ -1106,7 +1106,7 @@ const { width } = useElementSize(credNameRef);
-
+
diff --git a/packages/frontend/editor-ui/src/components/FilterConditions/constants.ts b/packages/frontend/editor-ui/src/components/FilterConditions/constants.ts
index 4410191cf9..c31ce37de8 100644
--- a/packages/frontend/editor-ui/src/components/FilterConditions/constants.ts
+++ b/packages/frontend/editor-ui/src/components/FilterConditions/constants.ts
@@ -285,13 +285,13 @@ export const OPERATOR_GROUPS: FilterOperatorGroup[] = [
{
id: 'string',
name: 'type.string',
- icon: 'font',
+ icon: 'case-upper',
children: OPERATORS.filter((operator) => operator.type === 'string'),
},
{
id: 'number',
name: 'type.number',
- icon: 'hashtag',
+ icon: 'hash',
children: OPERATORS.filter((operator) => operator.type === 'number'),
},
{
@@ -303,7 +303,7 @@ export const OPERATOR_GROUPS: FilterOperatorGroup[] = [
{
id: 'boolean',
name: 'type.boolean',
- icon: 'check-square',
+ icon: 'square-check',
children: OPERATORS.filter((operator) => operator.type === 'boolean'),
},
{
@@ -315,7 +315,7 @@ export const OPERATOR_GROUPS: FilterOperatorGroup[] = [
{
id: 'object',
name: 'type.object',
- icon: 'cube',
+ icon: 'box',
children: OPERATORS.filter((operator) => operator.type === 'object'),
},
];
diff --git a/packages/frontend/editor-ui/src/components/FilterConditions/types.ts b/packages/frontend/editor-ui/src/components/FilterConditions/types.ts
index 74d2ff5c1f..e0f7ea0e1f 100644
--- a/packages/frontend/editor-ui/src/components/FilterConditions/types.ts
+++ b/packages/frontend/editor-ui/src/components/FilterConditions/types.ts
@@ -1,3 +1,4 @@
+import type { IconName } from '@n8n/design-system/components/N8nIcon/icons';
import type { BaseTextKey } from '@n8n/i18n';
import type { FilterConditionValue, FilterOperatorValue } from 'n8n-workflow';
@@ -8,7 +9,7 @@ export interface FilterOperator extends FilterOperatorValue {
export interface FilterOperatorGroup {
id: string;
name: BaseTextKey;
- icon?: string;
+ icon?: IconName;
children: FilterOperator[];
}
diff --git a/packages/frontend/editor-ui/src/components/FixedCollectionParameter.vue b/packages/frontend/editor-ui/src/components/FixedCollectionParameter.vue
index a5147e0468..6bb86bca77 100644
--- a/packages/frontend/editor-ui/src/components/FixedCollectionParameter.vue
+++ b/packages/frontend/editor-ui/src/components/FixedCollectionParameter.vue
@@ -287,7 +287,7 @@ const trackWorkflowInputFieldAdded = () => {
type="tertiary"
text
size="mini"
- icon="trash"
+ icon="trash-2"
data-test-id="fixed-collection-delete"
:title="locale.baseText('fixedCollectionParameter.deleteItem')"
@click="deleteOption(property.name, index)"
@@ -315,7 +315,7 @@ const trackWorkflowInputFieldAdded = () => {
type="tertiary"
text
size="mini"
- icon="trash"
+ icon="trash-2"
data-test-id="fixed-collection-delete"
:title="locale.baseText('fixedCollectionParameter.deleteItem')"
@click="deleteOption(property.name)"
diff --git a/packages/frontend/editor-ui/src/components/Folders/FolderCard.vue b/packages/frontend/editor-ui/src/components/Folders/FolderCard.vue
index ab547391ff..9bc5b9970f 100644
--- a/packages/frontend/editor-ui/src/components/Folders/FolderCard.vue
+++ b/packages/frontend/editor-ui/src/components/Folders/FolderCard.vue
@@ -130,6 +130,7 @@ const onBreadcrumbItemClick = async (item: PathItem) => {
:class="$style['folder-icon']"
icon="folder"
size="xlarge"
+ :strokeWidth="1"
/>
diff --git a/packages/frontend/editor-ui/src/components/Folders/ProjectBreadcrumb.vue b/packages/frontend/editor-ui/src/components/Folders/ProjectBreadcrumb.vue
index 8bf50fb629..8f7630da3b 100644
--- a/packages/frontend/editor-ui/src/components/Folders/ProjectBreadcrumb.vue
+++ b/packages/frontend/editor-ui/src/components/Folders/ProjectBreadcrumb.vue
@@ -1,11 +1,8 @@
-
-
-
-
- {{ modelValue }}
-
-
-
-
- {{ placeholder }}
- {{ modelValue }}
-
-
-
-
-
-
-
-
- {{ subtitle }}
-
-
-
-
-
diff --git a/packages/frontend/editor-ui/src/components/InputPanel.vue b/packages/frontend/editor-ui/src/components/InputPanel.vue
index 7e9af9dfd9..f1647db3f2 100644
--- a/packages/frontend/editor-ui/src/components/InputPanel.vue
+++ b/packages/frontend/editor-ui/src/components/InputPanel.vue
@@ -480,7 +480,7 @@ function activatePane() {
{{ i18n.baseText('ndv.input.noOutputData.hint.tooltip') }}
-
+
diff --git a/packages/frontend/editor-ui/src/components/MainHeader/MainHeader.vue b/packages/frontend/editor-ui/src/components/MainHeader/MainHeader.vue
index b90e0ff35d..1f3a13acda 100644
--- a/packages/frontend/editor-ui/src/components/MainHeader/MainHeader.vue
+++ b/packages/frontend/editor-ui/src/components/MainHeader/MainHeader.vue
@@ -286,7 +286,7 @@ function hideGithubButton() {
diff --git a/packages/frontend/editor-ui/src/components/MainSidebar.vue b/packages/frontend/editor-ui/src/components/MainSidebar.vue
index 538c9dcd9a..19eced7d77 100644
--- a/packages/frontend/editor-ui/src/components/MainSidebar.vue
+++ b/packages/frontend/editor-ui/src/components/MainSidebar.vue
@@ -86,7 +86,7 @@ const mainMenuItems = computed(() => [
{
// Link to in-app templates, available if custom templates are enabled
id: 'templates',
- icon: 'box-open',
+ icon: 'package-open',
label: i18n.baseText('mainSidebar.templates'),
position: 'bottom',
available: settingsStore.isTemplatesEnabled && templatesStore.hasCustomTemplatesHost,
@@ -95,7 +95,7 @@ const mainMenuItems = computed(() => [
{
// Link to website templates, available if custom templates are not enabled
id: 'templates',
- icon: 'box-open',
+ icon: 'package-open',
label: i18n.baseText('mainSidebar.templates'),
position: 'bottom',
available: settingsStore.isTemplatesEnabled && !templatesStore.hasCustomTemplatesHost,
@@ -114,7 +114,7 @@ const mainMenuItems = computed(() => [
},
{
id: 'insights',
- icon: 'chart-bar',
+ icon: 'chart-column-decreasing',
label: 'Insights',
customIconSize: 'medium',
position: 'bottom',
@@ -125,7 +125,7 @@ const mainMenuItems = computed(() => [
},
{
id: 'help',
- icon: 'question',
+ icon: 'circle-help',
label: i18n.baseText('mainSidebar.help'),
position: 'bottom',
children: [
@@ -206,7 +206,7 @@ const mainMenuItems = computed(() => [
),
{
id: 'full-changelog',
- icon: 'external-link-alt',
+ icon: 'external-link',
label: i18n.baseText('mainSidebar.whatsNew.fullChangelog'),
link: {
href: RELEASE_NOTES_URL,
diff --git a/packages/frontend/editor-ui/src/components/MainSidebarSourceControl.vue b/packages/frontend/editor-ui/src/components/MainSidebarSourceControl.vue
index 21bd584e98..5aa323623f 100644
--- a/packages/frontend/editor-ui/src/components/MainSidebarSourceControl.vue
+++ b/packages/frontend/editor-ui/src/components/MainSidebarSourceControl.vue
@@ -128,7 +128,7 @@ async function pullWorkfolder() {
data-test-id="main-sidebar-source-control-connected"
>
-
+
{{ currentBranch }}
diff --git a/packages/frontend/editor-ui/src/components/MfaSetupModal.vue b/packages/frontend/editor-ui/src/components/MfaSetupModal.vue
index 695e80ff04..b4b5cb9572 100644
--- a/packages/frontend/editor-ui/src/components/MfaSetupModal.vue
+++ b/packages/frontend/editor-ui/src/components/MfaSetupModal.vue
@@ -241,7 +241,7 @@ onMounted(async () => {
{
:class="parameter.type"
>
-
-
-
{
props: {
rootNode: node,
},
+ global: {
+ stubs: {
+ N8nButton: true,
+ },
+ },
});
vi.advanceTimersByTime(1000); // Event debounce time
await waitFor(() => {});
expect(getByTestId('subnode-connection-group-ai_tool-0')).toBeVisible();
- expect(html()).toEqual(
- ``,
- );
+ expect(html()).toMatchSnapshot();
});
it('should not render container if no possible connections', async () => {
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreation.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreation.vue
index db7e7dc841..8a0acba366 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreation.vue
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreation.vue
@@ -109,7 +109,7 @@ function nodeTypeSelected(value: NodeTypeSelectedPayload[]) {
@@ -131,14 +131,14 @@ function nodeTypeSelected(value: NodeTypeSelectedPayload[]) {
v-if="experimentalNdvStore.isEnabled"
type="tertiary"
size="large"
- icon="expand"
+ icon="maximize-2"
@click="experimentalNdvStore.expandAllNodes"
/>
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/CategoryItem.test.ts b/packages/frontend/editor-ui/src/components/Node/NodeCreator/CategoryItem.test.ts
index 4d64abb728..b5591e8046 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/CategoryItem.test.ts
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/CategoryItem.test.ts
@@ -27,8 +27,8 @@ describe('CategoryItem', () => {
props: { name: 'Category Test', isTrigger: true },
});
- expect(container.querySelector('[data-icon="bolt"]')).toBeInTheDocument();
+ expect(container.querySelector('[data-icon="zap"]')).toBeInTheDocument();
await rerender({ isTrigger: false });
- expect(container.querySelector('[data-icon="bolt"]')).not.toBeInTheDocument();
+ expect(container.querySelector('[data-icon="zap"]')).not.toBeInTheDocument();
});
});
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/CategoryItem.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/CategoryItem.vue
index ead3d49232..b7f487531d 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/CategoryItem.vue
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/CategoryItem.vue
@@ -28,11 +28,11 @@ const categoryName = computed(() => {
-
+
-
-
+
+
@@ -72,10 +72,4 @@ const categoryName = computed(() => {
flex-grow: 1;
color: var(--color-text-dark);
}
-
-.arrow {
- font-size: var(--font-size-2xs);
- width: 12px;
- color: $node-creator-arrow-color;
-}
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/LinkItem.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/LinkItem.vue
index b5c3dc55a2..cb2ae243bf 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/LinkItem.vue
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/LinkItem.vue
@@ -18,7 +18,13 @@ defineProps();
:show-action-arrow="true"
>
-
+
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/NodeItem.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/NodeItem.vue
index 92e2f85af4..9158dfec88 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/NodeItem.vue
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/NodeItem.vue
@@ -212,7 +212,7 @@ function onCommunityNodeTooltipClick(event: MouseEvent) {
@click="onCommunityNodeTooltipClick"
/>
-
+
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/OpenTemplateItem.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/OpenTemplateItem.vue
index ebb84048fb..5761a637f4 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/OpenTemplateItem.vue
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/OpenTemplateItem.vue
@@ -18,7 +18,13 @@ defineProps();
:show-action-arrow="true"
>
-
+
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/SubcategoryItem.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/SubcategoryItem.vue
index c56d152a0b..3aac7c2855 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/SubcategoryItem.vue
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/SubcategoryItem.vue
@@ -30,6 +30,7 @@ const subcategoryName = computed(() => camelCase(props.item.subcategory || props
:name="item.icon"
:circle="false"
:show-tooltip="false"
+ :use-updated-icons="true"
v-bind="item.iconProps"
/>
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/ViewItem.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/ViewItem.vue
index 4759a0b45c..e2fe596746 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/ViewItem.vue
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/ItemTypes/ViewItem.vue
@@ -18,7 +18,13 @@ defineProps();
:show-action-arrow="true"
>
-
+
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/NodeCreator.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreator/NodeCreator.vue
index d74bcd9e90..a3e0dfc004 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/NodeCreator.vue
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/NodeCreator.vue
@@ -165,7 +165,7 @@ onBeforeUnmount(() => {
v-if="active"
:class="$style.close"
type="secondary"
- icon="times"
+ icon="x"
aria-label="Close Node Creator"
@click="emit('closeNodeCreator')"
/>
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/NodesListPanel.test.ts b/packages/frontend/editor-ui/src/components/Node/NodeCreator/NodesListPanel.test.ts
index eb4ba31cbb..82927d40db 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/NodesListPanel.test.ts
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/NodesListPanel.test.ts
@@ -249,7 +249,7 @@ describe('NodesListPanel', () => {
expect(screen.queryAllByTestId('item-iterator-item')).toHaveLength(0);
expect(screen.queryByText("We didn't make that... yet")).toBeInTheDocument();
- await fireEvent.click(container.querySelector('.clear')!);
+ await fireEvent.click(container.querySelector('svg[data-icon=circle-x]')!);
await nextTick();
expect(screen.queryAllByTestId('item-iterator-item')).toHaveLength(9);
});
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/CommunityNodeDetails.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/CommunityNodeDetails.vue
index 2ebf7e1574..ecc001f8bb 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/CommunityNodeDetails.vue
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/CommunityNodeDetails.vue
@@ -130,11 +130,7 @@ const onInstall = async () => {
-
+
{{ i18n.baseText('communityNodeDetails.installed') }}
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/CommunityNodeDocsLink.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/CommunityNodeDocsLink.vue
index cc59d7041f..b4578fe335 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/CommunityNodeDocsLink.vue
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/CommunityNodeDocsLink.vue
@@ -25,7 +25,7 @@ const openCommunityNodeDocsPage = () => {
{{ i18n.baseText('communityNodesDocsLink.title') }}
-
+
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/CommunityNodeInfo.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/CommunityNodeInfo.vue
index 9b38f452e2..7ea07d0762 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/CommunityNodeInfo.vue
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/CommunityNodeInfo.vue
@@ -126,7 +126,7 @@ onMounted(async () => {
{{ i18n.baseText('communityNodeInfo.unverified') }}
-
+
{{ i18n.baseText('communityNodeInfo.unverified.label') }}
@@ -134,21 +134,21 @@ onMounted(async () => {
-
+
{{ i18n.baseText('communityNodeInfo.downloads', { interpolate: { downloads } }) }}
-
+
{{ i18n.baseText('communityNodeInfo.publishedBy', { interpolate: { publisherName } }) }}
-
+
{{ i18n.baseText('communityNodeInfo.contact.admin') }}
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/CommunityNodeInstallHint.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/CommunityNodeInstallHint.vue
index fc9523de2a..a1e8e79789 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/CommunityNodeInstallHint.vue
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/CommunityNodeInstallHint.vue
@@ -15,7 +15,7 @@ defineProps
();
-
+
{{ hint }}
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/NoResults.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/NoResults.vue
index 36a25fe27e..60cf7738f7 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/NoResults.vue
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/NoResults.vue
@@ -53,9 +53,9 @@ const i18n = useI18n();
{{ i18n.baseText('nodeCreator.noResults.requestTheNode') }}
-
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/NodesListPanel.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/NodesListPanel.vue
index 8bc943f8d2..96c4c50ac0 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/NodesListPanel.vue
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/NodesListPanel.vue
@@ -184,7 +184,7 @@ function onBackButton() {
:class="$style.backButton"
@click="onBackButton"
>
-
+
@@ -275,12 +276,11 @@ function onBackButton() {
background: transparent;
border: none;
cursor: pointer;
- padding: 0 var(--spacing-xs) 0 0;
+ padding: var(--spacing-2xs) var(--spacing-xs) 0 0;
}
.backButtonIcon {
color: $node-creator-arrow-color;
- height: 16px;
padding: 0;
}
.nodeIcon {
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/SearchBar.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/SearchBar.vue
index d7a9f224bf..5b4269941a 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/SearchBar.vue
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/Panel/SearchBar.vue
@@ -53,7 +53,7 @@ defineExpose({
-
+
-
@@ -143,4 +141,8 @@ defineExpose({
fill: $node-creator-search-clear-background-color-hover;
}
}
+
+.clickable {
+ cursor: pointer;
+}
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/Renderers/CategorizedItemsRenderer.vue b/packages/frontend/editor-ui/src/components/Node/NodeCreator/Renderers/CategorizedItemsRenderer.vue
index 4a17c220b7..7636df0eac 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/Renderers/CategorizedItemsRenderer.vue
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/Renderers/CategorizedItemsRenderer.vue
@@ -113,7 +113,7 @@ registerKeyHook(`CategoryLeft_${props.category}`, {
>
-
+
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/__tests__/utils.ts b/packages/frontend/editor-ui/src/components/Node/NodeCreator/__tests__/utils.ts
index 69ae1591ac..033055feee 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/__tests__/utils.ts
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/__tests__/utils.ts
@@ -55,7 +55,7 @@ const mockSubcategoryItemProps = (
): SubcategoryItemProps => ({
description: 'Sample description',
iconType: 'sampleIconType',
- icon: 'sampleIcon',
+ icon: 'smile',
title: 'Sample title',
subcategory: 'sampleSubcategory',
defaults: { color: '#ffffff' },
diff --git a/packages/frontend/editor-ui/src/components/Node/NodeCreator/viewsData.ts b/packages/frontend/editor-ui/src/components/Node/NodeCreator/viewsData.ts
index 838eaeab72..24b5004d74 100644
--- a/packages/frontend/editor-ui/src/components/Node/NodeCreator/viewsData.ts
+++ b/packages/frontend/editor-ui/src/components/Node/NodeCreator/viewsData.ts
@@ -255,7 +255,7 @@ export function AINodesView(_nodes: SimplifiedNodeType[]): NodeView {
properties: {
title: AI_CATEGORY_DOCUMENT_LOADERS,
info: getSubcategoryInfo(AI_CATEGORY_DOCUMENT_LOADERS),
- icon: 'file-import',
+ icon: 'file-input',
...getAISubcategoryProperties(NodeConnectionTypes.AiDocument),
},
},
@@ -343,7 +343,7 @@ export function AINodesView(_nodes: SimplifiedNodeType[]): NodeView {
properties: {
title: AI_CATEGORY_VECTOR_STORES,
info: getSubcategoryInfo(AI_CATEGORY_VECTOR_STORES),
- icon: 'project-diagram',
+ icon: 'waypoints',
...getAISubcategoryProperties(NodeConnectionTypes.AiVectorStore),
},
},
@@ -571,7 +571,7 @@ export function RegularView(nodes: SimplifiedNodeType[]) {
category: CORE_NODES_CATEGORY,
properties: {
title: FLOWS_CONTROL_SUBCATEGORY,
- icon: 'code-branch',
+ icon: 'git-branch',
sections: [
{
key: 'popular',
diff --git a/packages/frontend/editor-ui/src/components/NodeCredentials.vue b/packages/frontend/editor-ui/src/components/NodeCredentials.vue
index 32fc6e79ce..f546d1cce8 100644
--- a/packages/frontend/editor-ui/src/components/NodeCredentials.vue
+++ b/packages/frontend/editor-ui/src/components/NodeCredentials.vue
@@ -582,7 +582,7 @@ async function onClickCreateCredential(type: ICredentialType | INodeCredentialDe
:items="getIssues(type.name)"
/>
-
+
diff --git a/packages/frontend/editor-ui/src/components/NodeDetailsView.vue b/packages/frontend/editor-ui/src/components/NodeDetailsView.vue
index 12f438c24d..29e34e647e 100644
--- a/packages/frontend/editor-ui/src/components/NodeDetailsView.vue
+++ b/packages/frontend/editor-ui/src/components/NodeDetailsView.vue
@@ -870,7 +870,7 @@ onBeforeUnmount(() => {
target="_blank"
@click="onFeatureRequestClick"
>
-
+
{{ i18n.baseText('ndv.featureRequest') }}
diff --git a/packages/frontend/editor-ui/src/components/NodeExecuteButton.vue b/packages/frontend/editor-ui/src/components/NodeExecuteButton.vue
index 0262290b6f..2df78f31a5 100644
--- a/packages/frontend/editor-ui/src/components/NodeExecuteButton.vue
+++ b/packages/frontend/editor-ui/src/components/NodeExecuteButton.vue
@@ -31,6 +31,7 @@ import { generateCodeForAiTransform } from '@/components/ButtonParameter/utils';
import { needsAgentInput } from '@/utils/nodes/nodeTransforms';
import { useUIStore } from '@/stores/ui.store';
import type { ButtonType } from '@n8n/design-system';
+import { type IconName } from '@n8n/design-system/components/N8nIcon/icons';
const NODE_TEST_STEP_POPUP_COUNT_KEY = 'N8N_NODE_TEST_STEP_POPUP_COUNT';
const MAX_POPUP_COUNT = 10;
@@ -221,9 +222,9 @@ const isLoading = computed(
(isNodeRunning.value && !isListeningForEvents.value && !isListeningForWorkflowEvents.value),
);
-const buttonIcon = computed(() => {
+const buttonIcon = computed((): IconName | undefined => {
if (shouldGenerateCode.value) return 'terminal';
- if (!isListeningForEvents.value && !props.hideIcon) return 'flask';
+ if (!isListeningForEvents.value && !props.hideIcon) return 'flask-conical';
return undefined;
});
diff --git a/packages/frontend/editor-ui/src/components/NodeSettings.vue b/packages/frontend/editor-ui/src/components/NodeSettings.vue
index c28edf6ae1..35210cd404 100644
--- a/packages/frontend/editor-ui/src/components/NodeSettings.vue
+++ b/packages/frontend/editor-ui/src/components/NodeSettings.vue
@@ -801,7 +801,7 @@ function handleWheelEvent(event: WheelEvent) {
-
+
diff --git a/packages/frontend/editor-ui/src/components/NodeSettingsTabs.vue b/packages/frontend/editor-ui/src/components/NodeSettingsTabs.vue
index 8b0e213717..cb008def39 100644
--- a/packages/frontend/editor-ui/src/components/NodeSettingsTabs.vue
+++ b/packages/frontend/editor-ui/src/components/NodeSettingsTabs.vue
@@ -95,7 +95,7 @@ const options = computed(() => {
if (isCommunityNode.value) {
options.push({
- icon: 'cube',
+ icon: 'box',
value: 'communityNode',
align: 'right',
tooltip: i18n.baseText('generic.communityNode.tooltip', {
diff --git a/packages/frontend/editor-ui/src/components/NodeWebhooks.vue b/packages/frontend/editor-ui/src/components/NodeWebhooks.vue
index 368cb2be3a..9c3f553aee 100644
--- a/packages/frontend/editor-ui/src/components/NodeWebhooks.vue
+++ b/packages/frontend/editor-ui/src/components/NodeWebhooks.vue
@@ -188,7 +188,7 @@ watch(
:title="isMinimized ? baseText.clickToDisplay : baseText.clickToHide"
@click="isMinimized = !isMinimized"
>
-
+
{{ baseText.toggleTitle }}
diff --git a/packages/frontend/editor-ui/src/components/PanelDragButton.vue b/packages/frontend/editor-ui/src/components/PanelDragButton.vue
index c89f3e27f7..2f2cd51e96 100644
--- a/packages/frontend/editor-ui/src/components/PanelDragButton.vue
+++ b/packages/frontend/editor-ui/src/components/PanelDragButton.vue
@@ -40,13 +40,13 @@ const onDragStart = () => {
v-if="canMoveLeft"
:class="{ [$style.leftArrow]: true, [$style.visible]: isDragging }"
>
-
+
-
+
diff --git a/packages/frontend/editor-ui/src/components/ParameterInput.vue b/packages/frontend/editor-ui/src/components/ParameterInput.vue
index b92e9313ee..579688a706 100644
--- a/packages/frontend/editor-ui/src/components/ParameterInput.vue
+++ b/packages/frontend/editor-ui/src/components/ParameterInput.vue
@@ -1399,7 +1399,7 @@ onUpdated(async () => {
{
{
{
{
{
{
{
issue
}}
-
+
@@ -721,13 +721,13 @@ const onCalloutDismiss = async (parameter: INodeProperties) => {
-
+
{{ i18n.baseText('parameterInputList.loadingFields') }}
-
+
{{ i18n.baseText('parameterInputList.loadingError') }}
{
type="tertiary"
text
size="mini"
- icon="trash"
+ icon="trash-2"
class="icon-button"
:title="i18n.baseText('parameterInputList.delete')"
@click="deleteOption(parameter.name)"
@@ -778,7 +778,7 @@ const onCalloutDismiss = async (parameter: INodeProperties) => {
type="tertiary"
text
size="mini"
- icon="trash"
+ icon="trash-2"
class="icon-button"
:title="i18n.baseText('parameterInputList.delete')"
@click="deleteOption(parameter.name)"
diff --git a/packages/frontend/editor-ui/src/components/ParameterInputOverrides/FromAiOverrideField.vue b/packages/frontend/editor-ui/src/components/ParameterInputOverrides/FromAiOverrideField.vue
index d472ab87eb..488d0a167c 100644
--- a/packages/frontend/editor-ui/src/components/ParameterInputOverrides/FromAiOverrideField.vue
+++ b/packages/frontend/editor-ui/src/components/ParameterInputOverrides/FromAiOverrideField.vue
@@ -27,7 +27,7 @@ const emit = defineEmits<{
type="tertiary"
:class="['n8n-input', $style.overrideCloseButton]"
:outline="false"
- icon="xmark"
+ icon="x"
size="mini"
@click="emit('close')"
/>
diff --git a/packages/frontend/editor-ui/src/components/ParameterIssues.vue b/packages/frontend/editor-ui/src/components/ParameterIssues.vue
index 154515834e..7d422c2de0 100644
--- a/packages/frontend/editor-ui/src/components/ParameterIssues.vue
+++ b/packages/frontend/editor-ui/src/components/ParameterIssues.vue
@@ -16,7 +16,7 @@ const i18n = useI18n();
-
+
diff --git a/packages/frontend/editor-ui/src/components/ParameterOptions.test.ts b/packages/frontend/editor-ui/src/components/ParameterOptions.test.ts
index c302f7e587..a4d49eed80 100644
--- a/packages/frontend/editor-ui/src/components/ParameterOptions.test.ts
+++ b/packages/frontend/editor-ui/src/components/ParameterOptions.test.ts
@@ -77,7 +77,7 @@ describe('ParameterOptions', () => {
iconOrientation: 'horizontal',
},
});
- expect(container.querySelector('[data-icon="ellipsis-h"]')).toBeInTheDocument();
+ expect(container.querySelector('[data-icon="ellipsis"]')).toBeInTheDocument();
});
it('should render custom actions', async () => {
diff --git a/packages/frontend/editor-ui/src/components/ParameterOptions.vue b/packages/frontend/editor-ui/src/components/ParameterOptions.vue
index 94fb6835d8..bf4071066e 100644
--- a/packages/frontend/editor-ui/src/components/ParameterOptions.vue
+++ b/packages/frontend/editor-ui/src/components/ParameterOptions.vue
@@ -177,7 +177,7 @@ const getArgument = (argumentName: string) => {
-
+
{{ loadingMessage }}
diff --git a/packages/frontend/editor-ui/src/components/Projects/ProjectCardBadge.vue b/packages/frontend/editor-ui/src/components/Projects/ProjectCardBadge.vue
index 9da538164c..085cd515d7 100644
--- a/packages/frontend/editor-ui/src/components/Projects/ProjectCardBadge.vue
+++ b/packages/frontend/editor-ui/src/components/Projects/ProjectCardBadge.vue
@@ -2,10 +2,11 @@
import { computed } from 'vue';
import { useI18n } from '@n8n/i18n';
import { ResourceType, splitName } from '@/utils/projects.utils';
-import type { Project, ProjectIcon as BadgeIcon } from '@/types/projects.types';
+import type { Project } from '@/types/projects.types';
import { ProjectTypes } from '@/types/projects.types';
import type { CredentialsResource, FolderResource, WorkflowResource } from '@/Interface';
import { VIEWS } from '@/constants';
+import { type IconOrEmoji, isIconOrEmoji } from '@n8n/design-system/components/N8nIconPicker/types';
type Props = {
resource: WorkflowResource | CredentialsResource | FolderResource;
@@ -76,7 +77,7 @@ const badgeText = computed(() => {
}
});
-const badgeIcon = computed
(() => {
+const badgeIcon = computed(() => {
switch (projectState.value) {
case ProjectState.Owned:
case ProjectState.SharedOwned:
@@ -85,9 +86,11 @@ const badgeIcon = computed(() => {
return { type: 'icon', value: 'user' };
case ProjectState.Team:
case ProjectState.SharedTeam:
- return props.resource.homeProject?.icon ?? { type: 'icon', value: 'layer-group' };
+ return isIconOrEmoji(props.resource.homeProject?.icon)
+ ? props.resource.homeProject?.icon
+ : { type: 'icon', value: 'layers' };
default:
- return { type: 'icon', value: 'layer-group' };
+ return { type: 'icon', value: 'layers' };
}
});
const badgeTooltip = computed(() => {
diff --git a/packages/frontend/editor-ui/src/components/Projects/ProjectCreateResource.vue b/packages/frontend/editor-ui/src/components/Projects/ProjectCreateResource.vue
index 02c35f3298..ca00a645d0 100644
--- a/packages/frontend/editor-ui/src/components/Projects/ProjectCreateResource.vue
+++ b/packages/frontend/editor-ui/src/components/Projects/ProjectCreateResource.vue
@@ -35,7 +35,7 @@ defineExpose({
diff --git a/packages/frontend/editor-ui/src/components/Projects/ProjectHeader.test.ts b/packages/frontend/editor-ui/src/components/Projects/ProjectHeader.test.ts
index 5a0b5da515..f1ce732928 100644
--- a/packages/frontend/editor-ui/src/components/Projects/ProjectHeader.test.ts
+++ b/packages/frontend/editor-ui/src/components/Projects/ProjectHeader.test.ts
@@ -76,7 +76,7 @@ describe('ProjectHeader', () => {
vi.spyOn(projectPages, 'isOverviewSubPage', 'get').mockReturnValue(true);
const { container } = renderComponent();
- expect(container.querySelector('.fa-home')).not.toBeInTheDocument();
+ expect(container.querySelector('svg[data-icon=home]')).not.toBeInTheDocument();
});
it('should render the correct icon', async () => {
@@ -86,12 +86,12 @@ describe('ProjectHeader', () => {
// We no longer render icon for personal project
projectsStore.currentProject = { type: ProjectTypes.Personal } as Project;
await rerender({});
- expect(container.querySelector('.fa-user')).not.toBeInTheDocument();
+ expect(container.querySelector('svg[data-icon=user]')).not.toBeInTheDocument();
const projectName = 'My Project';
projectsStore.currentProject = { name: projectName } as Project;
await rerender({});
- expect(container.querySelector('.fa-layer-group')).toBeVisible();
+ expect(container.querySelector('svg[data-icon=layers]')).toBeVisible();
});
it('Overview: should render the correct title and subtitle', async () => {
diff --git a/packages/frontend/editor-ui/src/components/Projects/ProjectHeader.vue b/packages/frontend/editor-ui/src/components/Projects/ProjectHeader.vue
index 0e774b42d3..ac26a7dc8a 100644
--- a/packages/frontend/editor-ui/src/components/Projects/ProjectHeader.vue
+++ b/packages/frontend/editor-ui/src/components/Projects/ProjectHeader.vue
@@ -5,7 +5,7 @@ import { useElementSize, useResizeObserver } from '@vueuse/core';
import type { UserAction } from '@n8n/design-system';
import { N8nButton, N8nTooltip } from '@n8n/design-system';
import { useI18n } from '@n8n/i18n';
-import { type ProjectIcon as ProjectIconType, ProjectTypes } from '@/types/projects.types';
+import { ProjectTypes } from '@/types/projects.types';
import { useProjectsStore } from '@/stores/projects.store';
import ProjectTabs from '@/components/Projects/ProjectTabs.vue';
import ProjectIcon from '@/components/Projects/ProjectIcon.vue';
@@ -16,7 +16,9 @@ import ProjectCreateResource from '@/components/Projects/ProjectCreateResource.v
import { useSettingsStore } from '@/stores/settings.store';
import { useProjectPages } from '@/composables/useProjectPages';
import { truncateTextToFitWidth } from '@/utils/formatters/textFormatter';
+import { type IconName } from '@n8n/design-system/components/N8nIcon/icons';
import type { IUser } from 'n8n-workflow';
+import { type IconOrEmoji, isIconOrEmoji } from '@n8n/design-system/components/N8nIconPicker/types';
const route = useRoute();
const router = useRouter();
@@ -30,13 +32,15 @@ const emit = defineEmits<{
createFolder: [];
}>();
-const headerIcon = computed((): ProjectIconType => {
+const headerIcon = computed((): IconOrEmoji => {
if (projectsStore.currentProject?.type === ProjectTypes.Personal) {
return { type: 'icon', value: 'user' };
} else if (projectsStore.currentProject?.name) {
- return projectsStore.currentProject.icon ?? { type: 'icon', value: 'layer-group' };
+ return isIconOrEmoji(projectsStore.currentProject.icon)
+ ? projectsStore.currentProject.icon
+ : { type: 'icon', value: 'layers' };
} else {
- return { type: 'icon', value: 'home' };
+ return { type: 'icon', value: 'house' };
}
});
@@ -89,7 +93,7 @@ type ActionTypes = (typeof ACTION_TYPES)[keyof typeof ACTION_TYPES];
const createWorkflowButton = computed(() => ({
value: ACTION_TYPES.WORKFLOW,
label: i18n.baseText('projects.header.create.workflow'),
- icon: sourceControlStore.preferences.branchReadOnly ? 'lock' : undefined,
+ icon: sourceControlStore.preferences.branchReadOnly ? ('lock' as IconName) : undefined,
size: 'mini' as const,
disabled:
sourceControlStore.preferences.branchReadOnly ||
diff --git a/packages/frontend/editor-ui/src/components/Projects/ProjectIcon.vue b/packages/frontend/editor-ui/src/components/Projects/ProjectIcon.vue
index 7b32c8c556..235be378aa 100644
--- a/packages/frontend/editor-ui/src/components/Projects/ProjectIcon.vue
+++ b/packages/frontend/editor-ui/src/components/Projects/ProjectIcon.vue
@@ -1,8 +1,8 @@
-
+
{{ i18n.baseText('workerList.item.chartsTitle') }}
diff --git a/packages/frontend/editor-ui/src/components/Workers/WorkerJobAccordion.ee.vue b/packages/frontend/editor-ui/src/components/Workers/WorkerJobAccordion.ee.vue
index 2b0fc98178..c181c81e50 100644
--- a/packages/frontend/editor-ui/src/components/Workers/WorkerJobAccordion.ee.vue
+++ b/packages/frontend/editor-ui/src/components/Workers/WorkerJobAccordion.ee.vue
@@ -20,7 +20,7 @@ function runningSince(started: Date): string {
-
+
{{ i18n.baseText('workerList.item.jobListTitle') }} ({{ items.length }})
diff --git a/packages/frontend/editor-ui/src/components/Workers/WorkerNetAccordion.ee.vue b/packages/frontend/editor-ui/src/components/Workers/WorkerNetAccordion.ee.vue
index e9f2b8c6c5..035cd2d56b 100644
--- a/packages/frontend/editor-ui/src/components/Workers/WorkerNetAccordion.ee.vue
+++ b/packages/frontend/editor-ui/src/components/Workers/WorkerNetAccordion.ee.vue
@@ -25,7 +25,7 @@ function onCopyToClipboard(content: string) {
-
+
{{ i18n.baseText('workerList.item.netListTitle') }} ({{ items.length }})
diff --git a/packages/frontend/editor-ui/src/components/WorkflowActivator.vue b/packages/frontend/editor-ui/src/components/WorkflowActivator.vue
index 3b30d4f30a..5fae599d97 100644
--- a/packages/frontend/editor-ui/src/components/WorkflowActivator.vue
+++ b/packages/frontend/editor-ui/src/components/WorkflowActivator.vue
@@ -262,7 +262,7 @@ watch(
@click="displayActivationError"
>
-
+
diff --git a/packages/frontend/editor-ui/src/components/WorkflowSelectorParameterInput/WorkflowSelectorParameterInput.vue b/packages/frontend/editor-ui/src/components/WorkflowSelectorParameterInput/WorkflowSelectorParameterInput.vue
index 223c901179..d32a96cd01 100644
--- a/packages/frontend/editor-ui/src/components/WorkflowSelectorParameterInput/WorkflowSelectorParameterInput.vue
+++ b/packages/frontend/editor-ui/src/components/WorkflowSelectorParameterInput/WorkflowSelectorParameterInput.vue
@@ -415,7 +415,7 @@ const onAddResourceClicked = async () => {
data-test-id="rlc-open-resource-link"
>
-
+
diff --git a/packages/frontend/editor-ui/src/components/WorkflowSettings.vue b/packages/frontend/editor-ui/src/components/WorkflowSettings.vue
index 738d74cb38..25e4e233ce 100644
--- a/packages/frontend/editor-ui/src/components/WorkflowSettings.vue
+++ b/packages/frontend/editor-ui/src/components/WorkflowSettings.vue
@@ -519,7 +519,7 @@ onMounted(async () => {
-
+
@@ -549,7 +549,7 @@ onMounted(async () => {
-
+
@@ -578,7 +578,7 @@ onMounted(async () => {
-
+
@@ -600,7 +600,7 @@ onMounted(async () => {
-
+
@@ -629,7 +629,7 @@ onMounted(async () => {
-
+
@@ -658,7 +658,7 @@ onMounted(async () => {
-
+
@@ -687,7 +687,7 @@ onMounted(async () => {
-
+
@@ -716,7 +716,7 @@ onMounted(async () => {
-
+
@@ -745,7 +745,7 @@ onMounted(async () => {
-
+
@@ -772,7 +772,7 @@ onMounted(async () => {
-
+
@@ -817,7 +817,7 @@ onMounted(async () => {
{{ i18n.baseText('workflowSettings.timeSavedPerExecution.tooltip') }}
-
+
diff --git a/packages/frontend/editor-ui/src/components/__snapshots__/NDVSubConnections.test.ts.snap b/packages/frontend/editor-ui/src/components/__snapshots__/NDVSubConnections.test.ts.snap
new file mode 100644
index 0000000000..99aff17416
--- /dev/null
+++ b/packages/frontend/editor-ui/src/components/__snapshots__/NDVSubConnections.test.ts.snap
@@ -0,0 +1,22 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`NDVSubConnections > should render container if possible connections 1`] = `
+"
"
+`;
diff --git a/packages/frontend/editor-ui/src/components/__snapshots__/PushConnectionTracker.test.ts.snap b/packages/frontend/editor-ui/src/components/__snapshots__/PushConnectionTracker.test.ts.snap
index 5e72b2eb94..f9534899e2 100644
--- a/packages/frontend/editor-ui/src/components/__snapshots__/PushConnectionTracker.test.ts.snap
+++ b/packages/frontend/editor-ui/src/components/__snapshots__/PushConnectionTracker.test.ts.snap
@@ -28,22 +28,10 @@ exports[`PushConnectionTracker > should render error when disconnected and conne
-
-
-
+
Connection lost
diff --git a/packages/frontend/editor-ui/src/components/__snapshots__/VirtualSchema.test.ts.snap b/packages/frontend/editor-ui/src/components/__snapshots__/VirtualSchema.test.ts.snap
index 62e4ff5fa2..9bb191e43b 100644
--- a/packages/frontend/editor-ui/src/components/__snapshots__/VirtualSchema.test.ts.snap
+++ b/packages/frontend/editor-ui/src/components/__snapshots__/VirtualSchema.test.ts.snap
@@ -37,27 +37,11 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
class="toggle"
data-v-882a318e=""
>
-
renders preview schema when enabled and available 1
class="toggle"
data-v-0f5e7239=""
>
-
@@ -187,28 +155,12 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
data-v-0f5e7239=""
data-value="{{ $json.account }}"
>
-
renders preview schema when enabled and available 1
data-v-0f5e7239=""
data-value="{{ $json.account.id }}"
>
-
renders preview schema when enabled and available 1
-
-
-
-
-
+ icon="ellipsis"
+ size="small"
+ spin="false"
+ />
@@ -366,27 +281,11 @@ exports[`VirtualSchema.vue > renders preview schema when enabled and available 1
class="toggle"
data-v-882a318e=""
>
-
@@ -424,27 +323,11 @@ exports[`VirtualSchema.vue > renders previous nodes schema for AI tools 1`] = `
class="toggle"
data-v-882a318e=""
>
-
renders schema for empty objects and arrays 1`] = `
class="toggle"
data-v-882a318e=""
>
-
renders schema for empty objects and arrays 1`] = `
Manual Trigger
-
@@ -665,28 +500,12 @@ exports[`VirtualSchema.vue > renders schema for empty objects and arrays 1`] = `
data-v-0f5e7239=""
data-value="{{ $json.empty }}"
>
-
renders schema for empty objects and arrays 1`] = `
class="toggle"
data-v-0f5e7239=""
>
-
@@ -765,28 +568,12 @@ exports[`VirtualSchema.vue > renders schema for empty objects and arrays 1`] = `
data-v-0f5e7239=""
data-value="{{ $json.emptyArray }}"
>
-
renders schema for empty objects and arrays 1`] = `
class="toggle"
data-v-0f5e7239=""
>
-
@@ -865,28 +636,12 @@ exports[`VirtualSchema.vue > renders schema for empty objects and arrays 1`] = `
data-v-0f5e7239=""
data-value="{{ $json.nested }}"
>
-