diff --git a/packages/data-app-react/src/ui/BarChart.css b/packages/data-app-react/src/ui/BarChart.css index c218a5f..49e0203 100644 --- a/packages/data-app-react/src/ui/BarChart.css +++ b/packages/data-app-react/src/ui/BarChart.css @@ -80,7 +80,8 @@ justify-content: center; gap: var(--atbl-space-xs); min-height: var(--atbl-control-compact-height); - padding-top: 4px; + min-width: 0; + padding: 4px 4px 0; color: var(--atbl-muted); } .altertable-chart-item-label-row > 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 4264976..af986d6 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 fd6b56e..517b11f 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 ced3768..c53fd27 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 + } + /> + + ))}