From c10c70d951588e070a9bb7cb0b99f7bbaf137bb1 Mon Sep 17 00:00:00 2001 From: robinvrd Date: Fri, 30 Oct 2026 11:19:20 +0100 Subject: [PATCH 1/2] fix: truncate long bar chart category labels --- docs/ui.md | 2 + packages/data-app-react/src/ui/BarChart.css | 15 +++- packages/data-app-react/src/ui/BarChart.tsx | 4 +- tests/public-api/apps-charts.test.ts | 84 +++++++++++++++++++++ tests/public-api/fixtures/chart-legends.tsx | 31 ++++++++ 5 files changed, 133 insertions(+), 3 deletions(-) diff --git a/docs/ui.md b/docs/ui.md index 1ba62da0..ca319065 100644 --- a/docs/ui.md +++ b/docs/ui.md @@ -152,6 +152,8 @@ Compose the visual inside ` span { @@ -94,11 +95,23 @@ display: inline-flex; align-items: center; gap: 4px; + min-width: 0; + max-width: 100%; min-height: 0; padding: 0; color: inherit; } +.altertable-selectable-bars-label > span { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; +} + +.altertable-selectable-bars-label > svg { + flex-shrink: 0; +} + .altertable-chart-item-label-row button.altertable-selectable-bars-label { padding: var(--atbl-space-xs) 6px; border: 1px solid transparent; diff --git a/packages/data-app-react/src/ui/BarChart.tsx b/packages/data-app-react/src/ui/BarChart.tsx index 42649767..af986d6c 100644 --- a/packages/data-app-react/src/ui/BarChart.tsx +++ b/packages/data-app-react/src/ui/BarChart.tsx @@ -180,7 +180,7 @@ export function BarChart({ }} aria-label={`${item.label}: Actions`} > - {item.label} + {item.label} ({ ) : ( - {item.label} + {item.label} )} diff --git a/tests/public-api/apps-charts.test.ts b/tests/public-api/apps-charts.test.ts index fd6b56e2..517b11f1 100644 --- a/tests/public-api/apps-charts.test.ts +++ b/tests/public-api/apps-charts.test.ts @@ -1207,3 +1207,87 @@ test('legend controls stay separate from plot inspection', async ({ page }) => { await page.keyboard.press('ArrowRight'); await expect.poll(() => tooltip.getByRole('listitem').count()).toBe(2); }); + +test('long bar category labels fit their columns and retain full readings', async ({ + page, +}) => { + await page.goto('/chart-legends'); + for (const theme of ['light', 'dark']) { + if (theme === 'dark') + await page.getByRole('button', { name: 'Switch to dark theme' }).click(); + for (const width of [1280, 320]) { + await page.setViewportSize({ width, height: 900 }); + for (const mode of ['plain', 'with actions']) { + const chart = page.getByRole('region', { + name: `Long category bars ${mode}`, + exact: true, + }); + const labels = chart.getByTitle(/B2B|Business|International/); + await expect.poll(() => labels.count()).toBe(6); + await expect + .poll(async () => { + const bounds = await labels.evaluateAll(elements => + elements.map(element => { + const rect = element.getBoundingClientRect(); + return { + left: rect.left, + right: rect.right, + width: rect.width, + }; + }) + ); + const columns = await chart + .getByRole('button', { name: /: \d+ people/ }) + .evaluateAll(elements => + elements.map(element => { + const rect = element.getBoundingClientRect(); + return { left: rect.left, right: rect.right }; + }) + ); + const centers = columns.map( + column => (column.left + column.right) / 2 + ); + const halfColumn = (centers[1]! - centers[0]!) / 2; + return bounds.every( + (label, index) => + label.width > 0 && + label.left >= centers[index]! - halfColumn - 1 && + label.right <= centers[index]! + halfColumn + 1 + ); + }) + .toBe(true); + const last = 'B2BReactivationWithoutSpaces'; + expect( + await chart.getByTitle(last, { exact: true }).evaluate(element => { + const style = getComputedStyle(element); + return ( + element.scrollWidth > element.clientWidth && + style.overflowX === 'hidden' && + style.textOverflow === 'ellipsis' + ); + }) + ).toBe(true); + await chart + .getByRole('button', { name: new RegExp(`${last}: 20 people`) }) + .hover(); + await expect + .poll(() => page.getByRole('tooltip').textContent()) + .toContain(last); + await page.keyboard.press('Escape'); + if (mode === 'with actions') { + await chart + .getByRole('button', { name: `${last}: Actions`, exact: true }) + .click(); + await expect + .poll(() => + page + .getByRole('menuitem', { name: 'Inspect', exact: true }) + .isVisible() + ) + .toBe(true); + await page.keyboard.press('Escape'); + } + } + } + } +}); diff --git a/tests/public-api/fixtures/chart-legends.tsx b/tests/public-api/fixtures/chart-legends.tsx index ced37680..c53fd271 100644 --- a/tests/public-api/fixtures/chart-legends.tsx +++ b/tests/public-api/fixtures/chart-legends.tsx @@ -580,6 +580,37 @@ createRoot(document.getElementById('root')!).render( + {[false, true].map(withActions => ( + + ({ + id: String(index), + label, + value: 60 - index * 8, + }))} + itemActions={ + withActions + ? () => [ + { id: 'inspect', label: 'Inspect', onSelect: () => {} }, + ] + : undefined + } + /> + + ))} Date: Fri, 30 Oct 2026 11:28:15 +0100 Subject: [PATCH 2/2] chore: remove bar label documentation change --- docs/ui.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/ui.md b/docs/ui.md index ca319065..1ba62da0 100644 --- a/docs/ui.md +++ b/docs/ui.md @@ -152,8 +152,6 @@ Compose the visual inside `