From 5cdf6d83d770ff001a5d0312c4a2dc8a1bf4cbfc Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Mon, 7 Sep 2026 17:06:22 +0200 Subject: [PATCH 1/2] fix(ui): keep node title and subtitle on one line with ellipsis and tooltip Design rule for DS 2.0 nodes: fixed width, height grows, title and subtitle truncate to one line with the full text in a tooltip. The description block also stops contributing intrinsic width (contain: inline-size), so a long subtitle no longer widens bodies sized with min-width: max-content. --- .changeset/node-text-ellipsis.md | 5 +++ .../diagram-components/node-description.mdx | 6 +++ .../node-description.module.css | 10 ++++- .../node-description.spec.tsx | 39 +++++++++++++++++++ .../node-description/node-description.tsx | 8 +++- 5 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 .changeset/node-text-ellipsis.md create mode 100644 packages/ui/src/components/node/node-description/node-description.spec.tsx diff --git a/.changeset/node-text-ellipsis.md b/.changeset/node-text-ellipsis.md new file mode 100644 index 000000000..a817db82e --- /dev/null +++ b/.changeset/node-text-ellipsis.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/ui': patch +--- + +`NodeDescription` keeps both the title and the subtitle on one line: long text is truncated with an ellipsis and exposed in full through the element's native tooltip, and the text no longer widens nodes whose body sizes to its content (for example the Decision node). Nodes keep the fixed design width and grow in height only. diff --git a/apps/docs/src/content/docs/ui-library/diagram-components/node-description.mdx b/apps/docs/src/content/docs/ui-library/diagram-components/node-description.mdx index 3b9698f1d..e914f4b46 100644 --- a/apps/docs/src/content/docs/ui-library/diagram-components/node-description.mdx +++ b/apps/docs/src/content/docs/ui-library/diagram-components/node-description.mdx @@ -30,6 +30,12 @@ function NodeHeader({ icon, label, description }) { } ``` +## Truncation + +Both lines are single-line. Text that does not fit the node width is cut with an ellipsis and the +full text is available through the element's native tooltip (`title`). The block never widens its +node: a node keeps the design width and grows in height only through its body content. + ## Props diff --git a/packages/ui/src/components/node/node-description/node-description.module.css b/packages/ui/src/components/node/node-description/node-description.module.css index 2ab9ca640..9981c910b 100644 --- a/packages/ui/src/components/node/node-description/node-description.module.css +++ b/packages/ui/src/components/node/node-description/node-description.module.css @@ -11,15 +11,21 @@ align-items: flex-start; overflow: hidden; width: 100%; + min-width: 0; + contain: inline-size; - .title { - color: var(--wb-public-node-title-color); + .title, + .subtitle { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; width: 100%; } + .title { + color: var(--wb-public-node-title-color); + } + .subtitle { color: var(--wb-public-node-title-subtitle); } diff --git a/packages/ui/src/components/node/node-description/node-description.spec.tsx b/packages/ui/src/components/node/node-description/node-description.spec.tsx new file mode 100644 index 000000000..30cdd515d --- /dev/null +++ b/packages/ui/src/components/node/node-description/node-description.spec.tsx @@ -0,0 +1,39 @@ +import { act } from 'react'; +import { type Root, createRoot } from 'react-dom/client'; + +import { NodeDescription } from './node-description'; + +(globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true; + +let container: HTMLDivElement; +let root: Root; + +beforeEach(() => { + container = document.createElement('div'); + document.body.append(container); + root = createRoot(container); +}); + +afterEach(() => { + act(() => root.unmount()); + container.remove(); +}); + +describe('NodeDescription', () => { + it('exposes the full label and description as native tooltips for truncated text', () => { + act(() => + root.render(), + ); + + const [title, subtitle] = container.querySelectorAll('span'); + expect(title.getAttribute('title')).toBe('Route by Type'); + expect(subtitle.getAttribute('title')).toBe('Sends the ticket to the right responder.'); + }); + + it('renders no tooltip attribute when there is no description', () => { + act(() => root.render()); + + const [, subtitle] = container.querySelectorAll('span'); + expect(subtitle.hasAttribute('title')).toBe(false); + }); +}); diff --git a/packages/ui/src/components/node/node-description/node-description.tsx b/packages/ui/src/components/node/node-description/node-description.tsx index daab9ce01..80a8a9bd5 100644 --- a/packages/ui/src/components/node/node-description/node-description.tsx +++ b/packages/ui/src/components/node/node-description/node-description.tsx @@ -11,8 +11,12 @@ export type NodeDescriptionProps = { export function NodeDescription({ label, description, className }: NodeDescriptionProps) { return (
- {label} - {description} + + {label} + + + {description} +
); } From 81711891d93d83fad01c822ceeddd78968dc751a Mon Sep 17 00:00:00 2001 From: Jan Librowski Date: Wed, 9 Sep 2026 23:27:26 +0200 Subject: [PATCH 2/2] fix(sdk): show the full connectable item label in a tooltip Decision branch rows and AI tool rows already clip long labels with an ellipsis but exposed no way to read the full text. The label now carries a title attribute, matching the node title and subtitle behaviour. --- .changeset/connectable-item-tooltip.md | 5 ++++ .../connectable-item.spec.tsx | 25 +++++++++++++++++++ .../connectable-item/connectable-item.tsx | 4 ++- 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 .changeset/connectable-item-tooltip.md create mode 100644 packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.spec.tsx diff --git a/.changeset/connectable-item-tooltip.md b/.changeset/connectable-item-tooltip.md new file mode 100644 index 000000000..fb2e23c80 --- /dev/null +++ b/.changeset/connectable-item-tooltip.md @@ -0,0 +1,5 @@ +--- +'@workflowbuilder/sdk': patch +--- + +Decision branch rows and AI tool rows show the full label in a tooltip when the text is truncated. diff --git a/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.spec.tsx b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.spec.tsx new file mode 100644 index 000000000..087005930 --- /dev/null +++ b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.spec.tsx @@ -0,0 +1,25 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; + +import { ConnectableItem } from './connectable-item'; + +vi.mock('@xyflow/react', () => ({ + Handle: ({ id }: { id: string }) => , + Position: { Right: 'right', Bottom: 'bottom' }, +})); + +vi.mock('../../../../../store/store', () => ({ + useStore: (selector: (state: { layoutDirection: 'RIGHT' | 'DOWN' }) => unknown) => + selector({ layoutDirection: 'RIGHT' }), +})); + +describe('ConnectableItem', () => { + it('exposes the full label as a tooltip so a clipped row stays readable', () => { + const label = 'Small and medium business with an unusually long branch label'; + + render(); + + expect(screen.getByText(label).getAttribute('title')).toBe(label); + expect(screen.getByTestId('handle').dataset.handleId).toBe('source:inner:b2'); + }); +}); diff --git a/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.tsx b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.tsx index fe1ec7c32..273b5cf12 100644 --- a/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.tsx +++ b/packages/sdk/src/features/diagram/nodes/components/connectable-item/connectable-item.tsx @@ -22,7 +22,9 @@ export function ConnectableItem({ handleId, label, canHaveBottomHandle = true }: [styles['connectable-item--right']]: layoutDirection === 'RIGHT', })} > -
{label}
+
+ {label} +