From 610697115ad9815b62b5b3e8c55f23dfe40b7c03 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 15:20:29 +0800 Subject: [PATCH 01/21] Charts: Add labelOverflow ellipsis option for bar chart axis labels When bar charts are narrow, long categorical axis labels overlap and become unreadable. This adds a `labelOverflow: 'ellipsis'` option to axis configuration that truncates labels to fit the available bandwidth, with full text shown on hover via tooltip. Co-Authored-By: Claude Opus 4.5 --- .../add-bar-chart-label-overflow-ellipsis | 4 + .../src/charts/bar-chart/private/index.ts | 1 + .../private/truncated-tick-component.tsx | 105 ++++++++++++++++++ .../private/use-bar-chart-options.ts | 7 ++ .../bar-chart/stories/index.stories.tsx | 62 +++++++++++ projects/js-packages/charts/src/types.ts | 9 ++ 6 files changed, 188 insertions(+) create mode 100644 projects/js-packages/charts/changelog/add-bar-chart-label-overflow-ellipsis create mode 100644 projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx diff --git a/projects/js-packages/charts/changelog/add-bar-chart-label-overflow-ellipsis b/projects/js-packages/charts/changelog/add-bar-chart-label-overflow-ellipsis new file mode 100644 index 000000000000..2c822e6c87b1 --- /dev/null +++ b/projects/js-packages/charts/changelog/add-bar-chart-label-overflow-ellipsis @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +BarChart: add labelOverflow ellipsis option to truncate long axis labels. diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/index.ts b/projects/js-packages/charts/src/charts/bar-chart/private/index.ts index 51a486b2cefa..0745a2e117d0 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/index.ts +++ b/projects/js-packages/charts/src/charts/bar-chart/private/index.ts @@ -1 +1,2 @@ export { useBarChartOptions } from './use-bar-chart-options'; +export { createTruncatedTickComponent, TruncatedTickComponent } from './truncated-tick-component'; diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx new file mode 100644 index 000000000000..1dc8fc011045 --- /dev/null +++ b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx @@ -0,0 +1,105 @@ +import { DataContext } from '@visx/xychart'; +import { useContext } from 'react'; +import type { AxisScale, TickRendererProps } from '@visx/axis'; +import type { FC, CSSProperties } from 'react'; + +/** + * Get the bandwidth of a scale + * + * @param scale - The scale to get the bandwidth of + * @return The bandwidth of the scale + */ +const getScaleBandwidth = < Scale extends AxisScale >( scale?: Scale ) => { + const s = scale as AxisScale; + return s && 'bandwidth' in s ? s?.bandwidth() ?? 0 : 0; +}; + +interface TruncatedTickComponentProps extends TickRendererProps { + /** Which axis this tick belongs to */ + axis: 'x' | 'y'; +} + +const MINI_TICK_LABEL_LENGTH = 20; + +/** + * A tick component that renders labels with text truncation (ellipsis) when they exceed + * the available bandwidth. Shows the full text on hover via native title attribute. + * + * Uses foreignObject to embed HTML within SVG, enabling CSS text-overflow: ellipsis. + * Inherits text styles from tickLabelProps passed by visx Axis component. + * + * @param props - The props for the truncated tick component + * @param props.x - The x position of the tick + * @param props.y - The y position of the tick + * @param props.formattedValue - The formatted value of the tick + * @param props.axis - The axis this tick belongs to + * @param props.textAnchor - The text anchor of the tick + * @param props.fill - The fill color of the tick + * @param props.dy - The dy offset of the tick + * + * @return The truncated tick component + */ +export const TruncatedTickComponent: FC< TruncatedTickComponentProps > = ( { + x, + y, + formattedValue, + axis, + textAnchor, + fill, + dy, + ...textProps +} ) => { + // Get max width of the tick label + const { xScale, yScale } = useContext( DataContext ) || {}; + const scale = axis === 'x' ? xScale : yScale; + const bandwidth = getScaleBandwidth( scale ); + const maxWidth = Math.max( bandwidth, MINI_TICK_LABEL_LENGTH ); + + // Offset to center the text on the tick position + const xOffset = -maxWidth / 2; + + // Map textAnchor to CSS textAlign + let textAlign: 'left' | 'right' | 'center' = 'left'; + if ( textAnchor === 'start' ) { + textAlign = 'left'; + } else if ( textAnchor === 'end' ) { + textAlign = 'right'; + } + + const textStyles: CSSProperties = { + // Offset y to convert from baseline to top-left positioning because svg text is positioned by baseline, but html div is positioned by top-left. + transform: 'translateY(-100%)', + ...( textProps as unknown as CSSProperties ), + // Convert svg text styles to CSS styles for the div + color: fill ?? 'inherit', + textAlign, + // Ensure text is truncated with ellipsis, remains on one line, and shows the full value in a tooltip on hover. + // The surrounding div uses CSS to handle overflow, and the 'title' attribute is set for accessibility. + width: maxWidth, + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + cursor: 'default', + pointerEvents: 'auto', + }; + + return ( + +
+ { formattedValue } +
+
+ ); +}; + +export const createTruncatedTickComponent = ( axis: 'x' | 'y' ) => ( props: TickRendererProps ) => { + return ; +}; diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/use-bar-chart-options.ts b/projects/js-packages/charts/src/charts/bar-chart/private/use-bar-chart-options.ts index b3b6e631bb7a..95ae162013c3 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/use-bar-chart-options.ts +++ b/projects/js-packages/charts/src/charts/bar-chart/private/use-bar-chart-options.ts @@ -1,5 +1,6 @@ import { formatNumberCompact } from '@automattic/number-formatters'; import { useMemo } from 'react'; +import { createTruncatedTickComponent } from './truncated-tick-component'; import type { EnhancedDataPoint } from '../../../hooks/use-zero-value-display'; import type { DataPointDate, BaseChartProps, SeriesData } from '../../../types'; import type { TickFormatter } from '@visx/axis'; @@ -116,12 +117,18 @@ export function useBarChartOptions( numTicks: 4, tickFormat: xTickFormat, ...( options.axis?.x || {} ), + ...( options.axis?.x?.labelOverflow === 'ellipsis' + ? { tickComponent: createTruncatedTickComponent( 'x' ) } + : {} ), }, y: { orientation: 'left' as const, numTicks: 4, tickFormat: yTickFormat, ...( options.axis?.y || {} ), + ...( options.axis?.y?.labelOverflow === 'ellipsis' + ? { tickComponent: createTruncatedTickComponent( 'y' ) } + : {} ), }, }, barGroup: { diff --git a/projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx b/projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx index b3ce32c2e66a..3bf5d87793b0 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx @@ -419,3 +419,65 @@ export const ZeroValueComparison: StoryObj< typeof BarChart > = { }, }, }; + +// Data with long categorical labels to demonstrate overlapping issue +const longLabelData = [ + { + group: 'sales', + label: 'Sales by Channel', + data: [ + { label: 'Organic Search Traffic', value: 12500 }, + { label: 'Paid Advertising Campaign', value: 8750 }, + { label: 'Social Media Marketing', value: 6250 }, + { label: 'Email Newsletter Subscribers', value: 4375 }, + { label: 'Direct Website Visitors', value: 3125 }, + { label: 'Affiliate Partner Referrals', value: 2500 }, + ], + }, +]; + +export const LabelOverflowEllipsis: StoryObj< typeof BarChart > = { + render: () => ( +
+
+

Without labelOverflow (Default - Labels Overlap)

+

+ Default behavior: long labels overlap and become unreadable at narrow widths. +

+
+ +
+
+ +
+

With labelOverflow: 'ellipsis' (Labels Truncated)

+

+ With labelOverflow: 'ellipsis', labels are truncated to fit the + available bandwidth. Hover over a label to see the full text. +

+
+ +
+
+
+ ), + parameters: { + docs: { + description: { + story: + 'Demonstrates the `labelOverflow: "ellipsis"` option that truncates long axis labels to fit the available bandwidth. The full label text is shown on hover via a native tooltip. This is useful for narrow widget contexts where space is limited.', + }, + }, + }, +}; diff --git a/projects/js-packages/charts/src/types.ts b/projects/js-packages/charts/src/types.ts index 87ddff7f7174..30a353cb90e6 100644 --- a/projects/js-packages/charts/src/types.ts +++ b/projects/js-packages/charts/src/types.ts @@ -307,6 +307,15 @@ declare type AxisOptions = { * For more control over rendering or to add event handlers to datum, pass a function as children. */ children?: ( renderProps: AxisRendererProps< AxisScale > ) => ReactNode; + /** + * Controls tick label overflow (bar charts only): + * + * - 'ellipsis': Truncate with ellipsis and fit to available space. + * - undefined: No truncation; labels may overlap. + * + * Default: No truncation; labels may overlap. + */ + labelOverflow?: 'ellipsis'; }; export type ScaleOptions = { From be1749674ad37239344bff43de4404d3976996a2 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 15:34:00 +0800 Subject: [PATCH 02/21] Update changelog --- .../charts/changelog/add-bar-chart-label-overflow-ellipsis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/js-packages/charts/changelog/add-bar-chart-label-overflow-ellipsis b/projects/js-packages/charts/changelog/add-bar-chart-label-overflow-ellipsis index 2c822e6c87b1..e9bf5111a1e5 100644 --- a/projects/js-packages/charts/changelog/add-bar-chart-label-overflow-ellipsis +++ b/projects/js-packages/charts/changelog/add-bar-chart-label-overflow-ellipsis @@ -1,4 +1,4 @@ Significance: minor Type: added -BarChart: add labelOverflow ellipsis option to truncate long axis labels. +Add labelOverflow ellipsis option to truncate long axis labels for bar chart From f7299d2624ddcf5e7a9afea9a6dc89fa7acf9e90 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 15:34:02 +0800 Subject: [PATCH 03/21] Address Copilot PR feedback for TruncatedTickComponent - Fix textAlign mapping to handle 'middle' textAnchor (maps to 'center') - Fix xOffset calculation based on actual text alignment - Add JSDoc for MINI_TICK_LABEL_LENGTH constant - Only set title attribute when formattedValue has content (avoid empty tooltips) - Add aria-label for screen reader accessibility - Improve type safety by explicitly mapping compatible SVG text props to CSS Co-Authored-By: Claude Opus 4.5 --- .../private/truncated-tick-component.tsx | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx index 1dc8fc011045..2e3042b647cf 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx @@ -19,6 +19,10 @@ interface TruncatedTickComponentProps extends TickRendererProps { axis: 'x' | 'y'; } +/** + * Minimum width in pixels for tick labels when scale bandwidth is very small. + * Prevents labels from collapsing to unreadable widths on dense charts. + */ const MINI_TICK_LABEL_LENGTH = 20; /** @@ -55,21 +59,44 @@ export const TruncatedTickComponent: FC< TruncatedTickComponentProps > = ( { const bandwidth = getScaleBandwidth( scale ); const maxWidth = Math.max( bandwidth, MINI_TICK_LABEL_LENGTH ); - // Offset to center the text on the tick position - const xOffset = -maxWidth / 2; - - // Map textAnchor to CSS textAlign - let textAlign: 'left' | 'right' | 'center' = 'left'; + // Map SVG textAnchor to CSS textAlign + let textAlign: 'left' | 'right' | 'center' = 'center'; if ( textAnchor === 'start' ) { textAlign = 'left'; } else if ( textAnchor === 'end' ) { textAlign = 'right'; + } else if ( textAnchor === 'middle' ) { + textAlign = 'center'; } + // Calculate x offset based on text alignment + let xOffset = 0; + if ( textAlign === 'center' ) { + xOffset = -maxWidth / 2; + } else if ( textAlign === 'right' ) { + xOffset = -maxWidth; + } + + // Extract compatible style properties from SVG text props + const { fontSize, fontFamily, fontWeight, fontStyle, letterSpacing, opacity } = textProps as { + fontSize?: CSSProperties[ 'fontSize' ]; + fontFamily?: CSSProperties[ 'fontFamily' ]; + fontWeight?: CSSProperties[ 'fontWeight' ]; + fontStyle?: CSSProperties[ 'fontStyle' ]; + letterSpacing?: CSSProperties[ 'letterSpacing' ]; + opacity?: CSSProperties[ 'opacity' ]; + }; + const textStyles: CSSProperties = { // Offset y to convert from baseline to top-left positioning because svg text is positioned by baseline, but html div is positioned by top-left. transform: 'translateY(-100%)', - ...( textProps as unknown as CSSProperties ), + // Apply compatible SVG text styles + fontSize, + fontFamily, + fontWeight, + fontStyle, + letterSpacing, + opacity, // Convert svg text styles to CSS styles for the div color: fill ?? 'inherit', textAlign, @@ -93,7 +120,11 @@ export const TruncatedTickComponent: FC< TruncatedTickComponentProps > = ( { // create a compound effect that requires doubling dy to match original text position. style={ { transform: `translateY(calc(${ dy ?? '0' } * 2))` } } > -
+
{ formattedValue }
From c3f157cf9b21096b9d3ff632532a4a6ad1319e36 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 15:51:33 +0800 Subject: [PATCH 04/21] Refactor bar chart axis options to improve labelOverflow handling - Extracted labelOverflow from axis options for both x and y axes. - Updated axis configuration to utilize extracted options for better readability and maintainability. - Ensured compatibility with the existing labelOverflow ellipsis feature. This change enhances the clarity of the axis configuration and prepares for future enhancements. --- .../charts/bar-chart/private/use-bar-chart-options.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/use-bar-chart-options.ts b/projects/js-packages/charts/src/charts/bar-chart/private/use-bar-chart-options.ts index 95ae162013c3..79c6d73a1e69 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/use-bar-chart-options.ts +++ b/projects/js-packages/charts/src/charts/bar-chart/private/use-bar-chart-options.ts @@ -103,6 +103,9 @@ export function useBarChartOptions( ? options.axis?.y?.tickFormat : options.axis?.x?.tickFormat; + const { labelOverflow: xLabelOverflow, ...xAxisOptions } = options.axis?.x || {}; + const { labelOverflow: yLabelOverflow, ...yAxisOptions } = options.axis?.y || {}; + return { gridVisibility, xScale, @@ -116,19 +119,19 @@ export function useBarChartOptions( orientation: 'bottom' as const, numTicks: 4, tickFormat: xTickFormat, - ...( options.axis?.x || {} ), - ...( options.axis?.x?.labelOverflow === 'ellipsis' + ...( xLabelOverflow === 'ellipsis' ? { tickComponent: createTruncatedTickComponent( 'x' ) } : {} ), + ...xAxisOptions, }, y: { orientation: 'left' as const, numTicks: 4, tickFormat: yTickFormat, - ...( options.axis?.y || {} ), - ...( options.axis?.y?.labelOverflow === 'ellipsis' + ...( yLabelOverflow === 'ellipsis' ? { tickComponent: createTruncatedTickComponent( 'y' ) } : {} ), + ...yAxisOptions, }, }, barGroup: { From 7962ef4cb9d78fc8cab163536858043a7c37c2b4 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 16:06:43 +0800 Subject: [PATCH 05/21] Enhance TruncatedTickComponent and add tests for label overflow handling - Renamed constant MINI_TICK_LABEL_LENGTH to MIN_TICK_LABEL_WIDTH for clarity. - Updated logic to use the new constant for determining maximum tick label width. - Introduced a comprehensive test suite for label overflow ellipsis functionality, ensuring proper rendering and accessibility features for long labels in bar charts. This update improves the readability of the code and ensures that long labels are handled gracefully in various chart orientations. --- .../private/truncated-tick-component.tsx | 4 +- .../charts/bar-chart/test/bar-chart.test.tsx | 158 ++++++++++++++++++ 2 files changed, 160 insertions(+), 2 deletions(-) diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx index 2e3042b647cf..ab48e0b38f75 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx @@ -23,7 +23,7 @@ interface TruncatedTickComponentProps extends TickRendererProps { * Minimum width in pixels for tick labels when scale bandwidth is very small. * Prevents labels from collapsing to unreadable widths on dense charts. */ -const MINI_TICK_LABEL_LENGTH = 20; +const MIN_TICK_LABEL_WIDTH = 20; /** * A tick component that renders labels with text truncation (ellipsis) when they exceed @@ -57,7 +57,7 @@ export const TruncatedTickComponent: FC< TruncatedTickComponentProps > = ( { const { xScale, yScale } = useContext( DataContext ) || {}; const scale = axis === 'x' ? xScale : yScale; const bandwidth = getScaleBandwidth( scale ); - const maxWidth = Math.max( bandwidth, MINI_TICK_LABEL_LENGTH ); + const maxWidth = Math.max( bandwidth, MIN_TICK_LABEL_WIDTH ); // Map SVG textAnchor to CSS textAlign let textAlign: 'left' | 'right' | 'center' = 'center'; diff --git a/projects/js-packages/charts/src/charts/bar-chart/test/bar-chart.test.tsx b/projects/js-packages/charts/src/charts/bar-chart/test/bar-chart.test.tsx index 019238461fea..8de0c413b056 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/test/bar-chart.test.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/test/bar-chart.test.tsx @@ -560,6 +560,164 @@ describe( 'BarChart', () => { /* eslint-enable testing-library/no-node-access */ + describe( 'Label Overflow Ellipsis', () => { + const longLabelData = [ + { + label: 'Series A', + data: [ + { label: 'Very Long Category Label One', value: 100 }, + { label: 'Very Long Category Label Two', value: 200 }, + { label: 'Very Long Category Label Three', value: 150 }, + ], + options: {}, + }, + ]; + + test( 'renders chart with labelOverflow ellipsis option', () => { + renderWithTheme( { + data: longLabelData, + options: { + axis: { + x: { + labelOverflow: 'ellipsis', + }, + }, + }, + } ); + + expect( screen.getByRole( 'grid', { name: /bar chart/i } ) ).toBeInTheDocument(); + } ); + + test( 'truncates labels with CSS text-overflow ellipsis', () => { + renderWithTheme( { + width: 300, // Narrow width to force truncation + data: longLabelData, + options: { + axis: { + x: { + labelOverflow: 'ellipsis', + }, + }, + }, + } ); + + // Labels should be rendered with truncation styles + const label = screen.getByText( /Very Long Category Label One/i ); + expect( label ).toHaveStyle( { textOverflow: 'ellipsis' } ); + expect( label ).toHaveStyle( { overflow: 'hidden' } ); + expect( label ).toHaveStyle( { whiteSpace: 'nowrap' } ); + } ); + + test( 'sets title attribute for hover tooltips on truncated labels', () => { + renderWithTheme( { + width: 300, + data: longLabelData, + options: { + axis: { + x: { + labelOverflow: 'ellipsis', + }, + }, + }, + } ); + + // Title attribute should show full text on hover + const label = screen.getByText( /Very Long Category Label One/i ); + expect( label ).toHaveAttribute( 'title', 'Very Long Category Label One' ); + } ); + + test( 'sets aria-label for screen reader accessibility', () => { + renderWithTheme( { + width: 300, + data: longLabelData, + options: { + axis: { + x: { + labelOverflow: 'ellipsis', + }, + }, + }, + } ); + + const label = screen.getByText( /Very Long Category Label One/i ); + expect( label ).toHaveAttribute( 'aria-label', 'Very Long Category Label One' ); + } ); + + test( 'applies truncation to x-axis for vertical bar charts', () => { + renderWithTheme( { + width: 300, + data: longLabelData, + orientation: 'vertical', + options: { + axis: { + x: { + labelOverflow: 'ellipsis', + }, + }, + }, + } ); + + // X-axis labels should have truncation + const label = screen.getByText( /Very Long Category Label One/i ); + expect( label ).toHaveStyle( { textOverflow: 'ellipsis' } ); + } ); + + test( 'applies truncation to y-axis for horizontal bar charts', () => { + renderWithTheme( { + width: 300, + data: longLabelData, + orientation: 'horizontal', + options: { + axis: { + y: { + labelOverflow: 'ellipsis', + }, + }, + }, + } ); + + // Y-axis labels should have truncation in horizontal mode + const label = screen.getByText( /Very Long Category Label One/i ); + expect( label ).toHaveStyle( { textOverflow: 'ellipsis' } ); + } ); + + test( 'handles very small chart widths gracefully', () => { + renderWithTheme( { + width: 100, // Very small width + data: longLabelData, + options: { + axis: { + x: { + labelOverflow: 'ellipsis', + }, + }, + }, + } ); + + // Chart should still render without errors + expect( screen.getByRole( 'grid', { name: /bar chart/i } ) ).toBeInTheDocument(); + + // Labels should still be present and have minimum width applied + const label = screen.getByText( /Very Long Category Label One/i ); + expect( label ).toBeInTheDocument(); + } ); + + test( 'does not apply truncation styles when labelOverflow is not set', () => { + renderWithTheme( { + width: 300, + data: longLabelData, + } ); + + // Without labelOverflow, labels should use default SVG text rendering + // which doesn't have CSS text-overflow + const labels = screen.getAllByText( /Very Long Category Label/i ); + labels.forEach( label => { + // SVG text elements don't have textOverflow style + expect( label.tagName.toLowerCase() ).not.toBe( 'div' ); + } ); + } ); + } ); + describe( 'Interactive Legend', () => { it( 'filters series when interactive legend is enabled and series is toggled', async () => { const user = userEvent.setup(); From ca007fa6985f74dbb68273822c92fab3aa977215 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 16:09:41 +0800 Subject: [PATCH 06/21] Document MIN_TICK_LABEL_WIDTH overlap behavior and mitigation strategies Add documentation explaining the trade-off when bandwidth is less than the minimum label width (20px): labels may overlap on very dense charts. Include mitigation strategies (numTicks, tickFormat, chart sizing). Co-Authored-By: Claude Opus 4.5 --- .../bar-chart/private/truncated-tick-component.tsx | 13 +++++++++++++ projects/js-packages/charts/src/types.ts | 5 ++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx index ab48e0b38f75..e3f47cb67892 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx @@ -22,6 +22,15 @@ interface TruncatedTickComponentProps extends TickRendererProps { /** * Minimum width in pixels for tick labels when scale bandwidth is very small. * Prevents labels from collapsing to unreadable widths on dense charts. + * + * Trade-off: When bandwidth is less than this minimum (e.g., many bars in a narrow chart), + * adjacent labels may overlap since each label uses this minimum width regardless of + * available space. This prioritizes label readability over preventing overlap. + * + * For very dense charts where overlap occurs, consider: + * - Using `numTicks` option to reduce the number of displayed labels + * - Using `tickFormat` to abbreviate label text + * - Increasing chart width or reducing data points */ const MIN_TICK_LABEL_WIDTH = 20; @@ -32,6 +41,10 @@ const MIN_TICK_LABEL_WIDTH = 20; * Uses foreignObject to embed HTML within SVG, enabling CSS text-overflow: ellipsis. * Inherits text styles from tickLabelProps passed by visx Axis component. * + * Note: A minimum label width (MIN_TICK_LABEL_WIDTH) is enforced to keep labels readable. + * On very dense charts where bandwidth < 20px, this may cause label overlap. + * See MIN_TICK_LABEL_WIDTH documentation for mitigation strategies. + * * @param props - The props for the truncated tick component * @param props.x - The x position of the tick * @param props.y - The y position of the tick diff --git a/projects/js-packages/charts/src/types.ts b/projects/js-packages/charts/src/types.ts index 30a353cb90e6..90801cdde88b 100644 --- a/projects/js-packages/charts/src/types.ts +++ b/projects/js-packages/charts/src/types.ts @@ -310,7 +310,10 @@ declare type AxisOptions = { /** * Controls tick label overflow (bar charts only): * - * - 'ellipsis': Truncate with ellipsis and fit to available space. + * - 'ellipsis': Truncate with ellipsis and fit to available space. Labels show full text + * on hover via native tooltip. Note: A minimum width (20px) is enforced for readability. + * On very dense charts (bandwidth < 20px), labels may overlap. To mitigate, use `numTicks` + * to reduce labels or `tickFormat` to abbreviate text. * - undefined: No truncation; labels may overlap. * * Default: No truncation; labels may overlap. From e1b7e15b3089aa15ddc2d41c30aacda930a695d6 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 16:12:33 +0800 Subject: [PATCH 07/21] Add minor update to changelog for bar chart labelOverflow ellipsis option Corrected punctuation in the changelog entry for the labelOverflow ellipsis feature, ensuring clarity in the description of the functionality that truncates long axis labels for bar charts. --- .../charts/changelog/add-bar-chart-label-overflow-ellipsis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/js-packages/charts/changelog/add-bar-chart-label-overflow-ellipsis b/projects/js-packages/charts/changelog/add-bar-chart-label-overflow-ellipsis index e9bf5111a1e5..9a9d90ae8342 100644 --- a/projects/js-packages/charts/changelog/add-bar-chart-label-overflow-ellipsis +++ b/projects/js-packages/charts/changelog/add-bar-chart-label-overflow-ellipsis @@ -1,4 +1,4 @@ Significance: minor Type: added -Add labelOverflow ellipsis option to truncate long axis labels for bar chart +Add labelOverflow ellipsis option to truncate long axis labels for bar chart. From 97aff7ad0aa4e8754455981656a2e0f8a8917302 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 16:13:12 +0800 Subject: [PATCH 08/21] Refactor bar chart exports to streamline component usage Removed the export of TruncatedTickComponent from the bar chart index file, simplifying the module's interface and focusing on the essential exports for better maintainability. --- .../js-packages/charts/src/charts/bar-chart/private/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/index.ts b/projects/js-packages/charts/src/charts/bar-chart/private/index.ts index 0745a2e117d0..11e1f0034301 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/index.ts +++ b/projects/js-packages/charts/src/charts/bar-chart/private/index.ts @@ -1,2 +1,2 @@ export { useBarChartOptions } from './use-bar-chart-options'; -export { createTruncatedTickComponent, TruncatedTickComponent } from './truncated-tick-component'; +export { createTruncatedTickComponent } from './truncated-tick-component'; From 8c77d0bfdc582e13d24ec117d91fe6b4dd20984b Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 16:49:03 +0800 Subject: [PATCH 09/21] Improve TruncatedTickComponent positioning for better label alignment - Adjusted the transform property to align HTML
elements within to match SVG vertical alignment. - Updated the position style to 'fixed' to enhance compatibility across browsers, particularly Safari. - Removed redundant transform logic to streamline the component's rendering. These changes enhance the visual consistency of tick labels in bar charts, ensuring they are displayed correctly across different environments. --- .../private/truncated-tick-component.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx index e3f47cb67892..af8dd86ea674 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx @@ -101,8 +101,14 @@ export const TruncatedTickComponent: FC< TruncatedTickComponentProps > = ( { }; const textStyles: CSSProperties = { + /** + * SVG elements are vertically aligned to the baseline by default, but HTML
elements inside + * are positioned relative to the top-left corner. To visually align the tick label like SVG text, + * we shift the div up by 100% of its height and adjust by twice the SVG dy value (from visx) to approximate original placement. + */ + transform: `translateY(calc(-100% + ${ dy ?? '0' } * 2))`, + position: 'fixed', // Offset y to convert from baseline to top-left positioning because svg text is positioned by baseline, but html div is positioned by top-left. - transform: 'translateY(-100%)', // Apply compatible SVG text styles fontSize, fontFamily, @@ -124,15 +130,7 @@ export const TruncatedTickComponent: FC< TruncatedTickComponentProps > = ( { }; return ( - +
Date: Mon, 19 Jan 2026 16:49:19 +0800 Subject: [PATCH 10/21] Add doc comment --- .../src/charts/bar-chart/private/truncated-tick-component.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx index af8dd86ea674..5e17ceb371a9 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx @@ -107,6 +107,7 @@ export const TruncatedTickComponent: FC< TruncatedTickComponentProps > = ( { * we shift the div up by 100% of its height and adjust by twice the SVG dy value (from visx) to approximate original placement. */ transform: `translateY(calc(-100% + ${ dy ?? '0' } * 2))`, + // Safari doesn't work well with foreignObject, this is a workaround to position the div correctly. position: 'fixed', // Offset y to convert from baseline to top-left positioning because svg text is positioned by baseline, but html div is positioned by top-left. // Apply compatible SVG text styles From cb32e32b2be9636fc7529af44dd04d9e82c83b39 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 16:49:41 +0800 Subject: [PATCH 11/21] Update projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/charts/bar-chart/private/truncated-tick-component.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx index 5e17ceb371a9..f160d9c0c029 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx @@ -135,7 +135,6 @@ export const TruncatedTickComponent: FC< TruncatedTickComponentProps > = ( {
{ formattedValue }
From e240a6da8b1f0673273daf52d28761e4c7ec87a7 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 16:49:54 +0800 Subject: [PATCH 12/21] Update projects/js-packages/charts/src/charts/bar-chart/test/bar-chart.test.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../charts/bar-chart/test/bar-chart.test.tsx | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/projects/js-packages/charts/src/charts/bar-chart/test/bar-chart.test.tsx b/projects/js-packages/charts/src/charts/bar-chart/test/bar-chart.test.tsx index 8de0c413b056..af3c5334e280 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/test/bar-chart.test.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/test/bar-chart.test.tsx @@ -626,23 +626,6 @@ describe( 'BarChart', () => { expect( label ).toHaveAttribute( 'title', 'Very Long Category Label One' ); } ); - test( 'sets aria-label for screen reader accessibility', () => { - renderWithTheme( { - width: 300, - data: longLabelData, - options: { - axis: { - x: { - labelOverflow: 'ellipsis', - }, - }, - }, - } ); - - const label = screen.getByText( /Very Long Category Label One/i ); - expect( label ).toHaveAttribute( 'aria-label', 'Very Long Category Label One' ); - } ); - test( 'applies truncation to x-axis for vertical bar charts', () => { renderWithTheme( { width: 300, From aedc9c07489f54a221e1d668efb3889f986c77a0 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 16:54:38 +0800 Subject: [PATCH 13/21] Update projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../charts/src/charts/bar-chart/stories/index.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx b/projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx index 3bf5d87793b0..5f937318f702 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx @@ -476,7 +476,7 @@ export const LabelOverflowEllipsis: StoryObj< typeof BarChart > = { docs: { description: { story: - 'Demonstrates the `labelOverflow: "ellipsis"` option that truncates long axis labels to fit the available bandwidth. The full label text is shown on hover via a native tooltip. This is useful for narrow widget contexts where space is limited.', + "Demonstrates the `labelOverflow: 'ellipsis'` option that truncates long axis labels to fit the available bandwidth. The full label text is shown on hover via a native tooltip. This is useful for narrow widget contexts where space is limited.", }, }, }, From ed18e54645ff57c885daa4dda449fc2808bf6c97 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 17:10:30 +0800 Subject: [PATCH 14/21] Update projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../charts/src/charts/bar-chart/stories/index.stories.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx b/projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx index 5f937318f702..c85d4b7b0021 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx @@ -450,9 +450,9 @@ export const LabelOverflowEllipsis: StoryObj< typeof BarChart > = {
-

With labelOverflow: 'ellipsis' (Labels Truncated)

+

With labelOverflow: 'ellipsis' (Labels Truncated)

- With labelOverflow: 'ellipsis', labels are truncated to fit the + With labelOverflow: 'ellipsis', labels are truncated to fit the available bandwidth. Hover over a label to see the full text.

From 37fe8804483b28bc5dec2ba34137dbefdee90213 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 17:10:50 +0800 Subject: [PATCH 15/21] Update projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/charts/bar-chart/private/truncated-tick-component.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx index f160d9c0c029..73b442ee2630 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx @@ -109,8 +109,7 @@ export const TruncatedTickComponent: FC< TruncatedTickComponentProps > = ( { transform: `translateY(calc(-100% + ${ dy ?? '0' } * 2))`, // Safari doesn't work well with foreignObject, this is a workaround to position the div correctly. position: 'fixed', - // Offset y to convert from baseline to top-left positioning because svg text is positioned by baseline, but html div is positioned by top-left. - // Apply compatible SVG text styles + // Apply SVG-like font properties from visx text props to the HTML div. fontSize, fontFamily, fontWeight, From affdc6540f3a501e41c9d257848072e6c750b5ae Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 17:11:08 +0800 Subject: [PATCH 16/21] Update projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/charts/bar-chart/private/truncated-tick-component.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx index 73b442ee2630..2efbe8b1ed95 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx @@ -10,10 +10,8 @@ import type { FC, CSSProperties } from 'react'; * @return The bandwidth of the scale */ const getScaleBandwidth = < Scale extends AxisScale >( scale?: Scale ) => { - const s = scale as AxisScale; - return s && 'bandwidth' in s ? s?.bandwidth() ?? 0 : 0; + return scale && 'bandwidth' in scale ? scale.bandwidth() ?? 0 : 0; }; - interface TruncatedTickComponentProps extends TickRendererProps { /** Which axis this tick belongs to */ axis: 'x' | 'y'; From 66eaba3c6efcf8b51963fa4088bbeaffa91ad5db Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 17:14:52 +0800 Subject: [PATCH 17/21] Memoize tick components and conditionally apply Safari workaround - Pre-create TruncatedXTickComponent and TruncatedYTickComponent at module level to prevent component recreation on every render - Apply Safari position:fixed workaround only on Safari browsers using existing isSafari() utility - Update exports and imports to use memoized component instances Co-Authored-By: Claude Opus 4.5 --- .../src/charts/bar-chart/private/index.ts | 2 +- .../private/truncated-tick-component.tsx | 31 +++++++++++++------ .../private/use-bar-chart-options.ts | 10 ++---- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/index.ts b/projects/js-packages/charts/src/charts/bar-chart/private/index.ts index 11e1f0034301..9e0dd549ff06 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/index.ts +++ b/projects/js-packages/charts/src/charts/bar-chart/private/index.ts @@ -1,2 +1,2 @@ export { useBarChartOptions } from './use-bar-chart-options'; -export { createTruncatedTickComponent } from './truncated-tick-component'; +export { TruncatedXTickComponent, TruncatedYTickComponent } from './truncated-tick-component'; diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx index 2efbe8b1ed95..f17b344e244b 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx @@ -1,5 +1,6 @@ import { DataContext } from '@visx/xychart'; import { useContext } from 'react'; +import { isSafari } from '../../../utils'; import type { AxisScale, TickRendererProps } from '@visx/axis'; import type { FC, CSSProperties } from 'react'; @@ -105,9 +106,9 @@ export const TruncatedTickComponent: FC< TruncatedTickComponentProps > = ( { * we shift the div up by 100% of its height and adjust by twice the SVG dy value (from visx) to approximate original placement. */ transform: `translateY(calc(-100% + ${ dy ?? '0' } * 2))`, - // Safari doesn't work well with foreignObject, this is a workaround to position the div correctly. - position: 'fixed', - // Apply SVG-like font properties from visx text props to the HTML div. + // Safari doesn't work well with foreignObject positioning. Use position: fixed as a workaround. + ...( isSafari() ? { position: 'fixed' as const } : {} ), + // Apply compatible SVG text styles fontSize, fontFamily, fontWeight, @@ -128,17 +129,29 @@ export const TruncatedTickComponent: FC< TruncatedTickComponentProps > = ( { }; return ( - -
+ +
{ formattedValue }
); }; -export const createTruncatedTickComponent = ( axis: 'x' | 'y' ) => ( props: TickRendererProps ) => { +/** + * Factory function to create a truncated tick component for a specific axis. + * Returns a component that can be passed to visx's tickComponent prop. + * + * @param axis - The axis this tick component is for ('x' or 'y') + * @return A tick component function compatible with visx's TickRendererProps + */ +const createTruncatedTickComponent = ( axis: 'x' | 'y' ) => ( props: TickRendererProps ) => { return ; }; + +/** + * Pre-created tick components for x and y axes. + * These are memoized at module level to prevent component recreation on every render, + * which would cause unnecessary DOM operations and potential state loss. + */ +export const TruncatedXTickComponent = createTruncatedTickComponent( 'x' ); +export const TruncatedYTickComponent = createTruncatedTickComponent( 'y' ); diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/use-bar-chart-options.ts b/projects/js-packages/charts/src/charts/bar-chart/private/use-bar-chart-options.ts index 79c6d73a1e69..b141d0ef6ad2 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/use-bar-chart-options.ts +++ b/projects/js-packages/charts/src/charts/bar-chart/private/use-bar-chart-options.ts @@ -1,6 +1,6 @@ import { formatNumberCompact } from '@automattic/number-formatters'; import { useMemo } from 'react'; -import { createTruncatedTickComponent } from './truncated-tick-component'; +import { TruncatedXTickComponent, TruncatedYTickComponent } from './truncated-tick-component'; import type { EnhancedDataPoint } from '../../../hooks/use-zero-value-display'; import type { DataPointDate, BaseChartProps, SeriesData } from '../../../types'; import type { TickFormatter } from '@visx/axis'; @@ -119,18 +119,14 @@ export function useBarChartOptions( orientation: 'bottom' as const, numTicks: 4, tickFormat: xTickFormat, - ...( xLabelOverflow === 'ellipsis' - ? { tickComponent: createTruncatedTickComponent( 'x' ) } - : {} ), + ...( xLabelOverflow === 'ellipsis' ? { tickComponent: TruncatedXTickComponent } : {} ), ...xAxisOptions, }, y: { orientation: 'left' as const, numTicks: 4, tickFormat: yTickFormat, - ...( yLabelOverflow === 'ellipsis' - ? { tickComponent: createTruncatedTickComponent( 'y' ) } - : {} ), + ...( yLabelOverflow === 'ellipsis' ? { tickComponent: TruncatedYTickComponent } : {} ), ...yAxisOptions, }, }, From 8198ce7f7e726fe0d31c22f42446c9fd4491960a Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 17:31:30 +0800 Subject: [PATCH 18/21] Fix lint --- .../charts/src/charts/bar-chart/stories/index.stories.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx b/projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx index c85d4b7b0021..f7836100d798 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/stories/index.stories.tsx @@ -448,11 +448,10 @@ export const LabelOverflowEllipsis: StoryObj< typeof BarChart > = {
-
-

With labelOverflow: 'ellipsis' (Labels Truncated)

+

With labelOverflow: 'ellipsis' (Labels Truncated)

- With labelOverflow: 'ellipsis', labels are truncated to fit the + With labelOverflow: 'ellipsis', labels are truncated to fit the available bandwidth. Hover over a label to see the full text.

From e61bb0e05fa944f7aab4e88ccb141e893d2ce374 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 17:36:13 +0800 Subject: [PATCH 19/21] Update projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/charts/bar-chart/private/truncated-tick-component.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx index f17b344e244b..3d67a744b0d6 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx @@ -150,8 +150,8 @@ const createTruncatedTickComponent = ( axis: 'x' | 'y' ) => ( props: TickRendere /** * Pre-created tick components for x and y axes. - * These are memoized at module level to prevent component recreation on every render, - * which would cause unnecessary DOM operations and potential state loss. + * These functions are created once at module initialization and reused, + * avoiding repeated factory calls when configuring axes. */ export const TruncatedXTickComponent = createTruncatedTickComponent( 'x' ); export const TruncatedYTickComponent = createTruncatedTickComponent( 'y' ); From 6b4d738d2917dc76d04d92deef94303effabc26b Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 17:36:25 +0800 Subject: [PATCH 20/21] Update projects/js-packages/charts/src/types.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- projects/js-packages/charts/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/js-packages/charts/src/types.ts b/projects/js-packages/charts/src/types.ts index 90801cdde88b..1737d5692797 100644 --- a/projects/js-packages/charts/src/types.ts +++ b/projects/js-packages/charts/src/types.ts @@ -312,7 +312,7 @@ declare type AxisOptions = { * * - 'ellipsis': Truncate with ellipsis and fit to available space. Labels show full text * on hover via native tooltip. Note: A minimum width (20px) is enforced for readability. - * On very dense charts (bandwidth < 20px), labels may overlap. To mitigate, use `numTicks` + * On very dense charts (bandwidth < 20px), adjacent labels may overlap. To mitigate, use `numTicks` * to reduce labels or `tickFormat` to abbreviate text. * - undefined: No truncation; labels may overlap. * From 9f0c5a67853b6d60ad8667f093de2a247bc8d923 Mon Sep 17 00:00:00 2001 From: Chi-Hsuan Huang Date: Mon, 19 Jan 2026 17:36:47 +0800 Subject: [PATCH 21/21] Update projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/charts/bar-chart/private/truncated-tick-component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx index 3d67a744b0d6..59afc79f064a 100644 --- a/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx +++ b/projects/js-packages/charts/src/charts/bar-chart/private/truncated-tick-component.tsx @@ -130,7 +130,7 @@ export const TruncatedTickComponent: FC< TruncatedTickComponentProps > = ( { return ( -
+
{ formattedValue }