diff --git a/packages/design-system/src/directives/index.ts b/packages/design-system/src/directives/index.ts
new file mode 100644
index 0000000000..eb939f0531
--- /dev/null
+++ b/packages/design-system/src/directives/index.ts
@@ -0,0 +1 @@
+export { n8nTruncate } from './n8n-truncate';
diff --git a/packages/design-system/src/directives/n8n-truncate.test.ts b/packages/design-system/src/directives/n8n-truncate.test.ts
new file mode 100644
index 0000000000..ec8922d2bc
--- /dev/null
+++ b/packages/design-system/src/directives/n8n-truncate.test.ts
@@ -0,0 +1,76 @@
+import { render } from '@testing-library/vue';
+import { n8nTruncate } from './n8n-truncate';
+
+describe('Directive n8n-truncate', () => {
+ it('should truncate text to 30 chars by default', async () => {
+ const { html } = render(
+ {
+ props: {
+ text: {
+ type: String,
+ },
+ },
+ template: '
{{text}}
',
+ },
+ {
+ props: {
+ text: 'This is a very long text that should be truncated',
+ },
+ global: {
+ directives: {
+ n8nTruncate,
+ },
+ },
+ },
+ );
+ expect(html()).toBe('This is a very long text that...
');
+ });
+
+ it('should truncate text to 30 chars in case of wrong argument', async () => {
+ const { html } = render(
+ {
+ props: {
+ text: {
+ type: String,
+ },
+ },
+ template: '{{text}}
',
+ },
+ {
+ props: {
+ text: 'This is a very long text that should be truncated',
+ },
+ global: {
+ directives: {
+ n8nTruncate,
+ },
+ },
+ },
+ );
+ expect(html()).toBe('This is a very long text that...
');
+ });
+
+ it('should truncate text to given length', async () => {
+ const { html } = render(
+ {
+ props: {
+ text: {
+ type: String,
+ },
+ },
+ template: '{{text}}
',
+ },
+ {
+ props: {
+ text: 'This is a very long text that should be truncated',
+ },
+ global: {
+ directives: {
+ n8nTruncate,
+ },
+ },
+ },
+ );
+ expect(html()).toBe('This is a very long text...
');
+ });
+});
diff --git a/packages/design-system/src/directives/n8n-truncate.ts b/packages/design-system/src/directives/n8n-truncate.ts
new file mode 100644
index 0000000000..8d454654c0
--- /dev/null
+++ b/packages/design-system/src/directives/n8n-truncate.ts
@@ -0,0 +1,26 @@
+import type { DirectiveBinding, ObjectDirective } from 'vue';
+import { truncate } from '../utils/string';
+
+/**
+ * Custom directive `n8nTruncate` to truncate text content of an HTML element.
+ *
+ * Usage:
+ * In your Vue template, use the directive `v-n8n-truncate` with an argument to specify the length to truncate to.
+ *
+ * Example:
+ * Some long text that will be truncated
+ *
+ * This will truncate the text content of the paragraph to 10 characters.
+ *
+ * Hint: Do not use it on components
+ * https://vuejs.org/guide/reusability/custom-directives#usage-on-components
+ */
+
+export const n8nTruncate: ObjectDirective = {
+ mounted(el: HTMLElement, binding: DirectiveBinding) {
+ el.textContent = truncate(el.textContent ?? '', Number(binding.arg) || undefined);
+ },
+ updated(el: HTMLElement, binding: DirectiveBinding) {
+ el.textContent = truncate(el.textContent ?? '', Number(binding.arg) || undefined);
+ },
+};
diff --git a/packages/design-system/src/main.ts b/packages/design-system/src/main.ts
index 54934d6f1c..dddb89f888 100644
--- a/packages/design-system/src/main.ts
+++ b/packages/design-system/src/main.ts
@@ -5,4 +5,5 @@ export * from './components';
export * from './plugin';
export * from './types';
export * from './utils';
+export * from './directives';
export { locale };
diff --git a/packages/design-system/src/plugin.ts b/packages/design-system/src/plugin.ts
index 493fde38ec..c6d3d2a6c9 100644
--- a/packages/design-system/src/plugin.ts
+++ b/packages/design-system/src/plugin.ts
@@ -1,5 +1,6 @@
import type { Component, Plugin } from 'vue';
import * as components from './components';
+import * as directives from './directives';
export interface N8nPluginOptions {}
@@ -8,5 +9,9 @@ export const N8nPlugin: Plugin = {
for (const [name, component] of Object.entries(components)) {
app.component(name, component as unknown as Component);
}
+
+ for (const [name, directive] of Object.entries(directives)) {
+ app.directive(name, directive);
+ }
},
};
diff --git a/packages/design-system/src/utils/string.test.ts b/packages/design-system/src/utils/string.test.ts
new file mode 100644
index 0000000000..6f65775f9a
--- /dev/null
+++ b/packages/design-system/src/utils/string.test.ts
@@ -0,0 +1,17 @@
+import { truncate } from './string';
+
+describe('Utils string', () => {
+ describe('truncate', () => {
+ it('should truncate text to 30 chars by default', () => {
+ expect(truncate('This is a very long text that should be truncated')).toBe(
+ 'This is a very long text that...',
+ );
+ });
+
+ it('should truncate text to given length', () => {
+ expect(truncate('This is a very long text that should be truncated', 25)).toBe(
+ 'This is a very long text...',
+ );
+ });
+ });
+});
diff --git a/packages/design-system/src/utils/string.ts b/packages/design-system/src/utils/string.ts
new file mode 100644
index 0000000000..9170b57c00
--- /dev/null
+++ b/packages/design-system/src/utils/string.ts
@@ -0,0 +1,2 @@
+export const truncate = (text: string, length = 30): string =>
+ text.length > length ? text.slice(0, length).trim() + '...' : text;