Skip to content

Commit 9cbf230

Browse files
committed
Fix horizontal chart label overflow with CSS ellipsis
- Add overflow-x-hidden to chart containers to prevent text overflow - Implement dynamic maxWidth constraints with 2% buffer for labels - Replace whitespace-nowrap with CSS truncate for proper ellipsis - Protect percentage values from shrinking with flex-shrink-0
1 parent e85ad0b commit 9cbf230

1 file changed

Lines changed: 20 additions & 12 deletions

File tree

src/components/HorizontalRatingsChart.jsx

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ const HorizontalRatingsChart = ({ questionId, questionTitle, filters = {}, showR
1515
const chartContainerRef = useRef(null);
1616
const [containerWidth, setContainerWidth] = useState(800);
1717

18+
// Helper function to truncate long labels
19+
const truncateLabel = (label, maxLength = 35) => {
20+
if (!label || label.length <= maxLength) return label;
21+
return label.substring(0, maxLength - 3) + '...';
22+
};
23+
1824
// Memoize filters to prevent infinite re-renders from object reference changes
1925
const filterKey = useMemo(() => JSON.stringify(filters), [filters]);
2026

@@ -384,7 +390,7 @@ const HorizontalRatingsChart = ({ questionId, questionTitle, filters = {}, showR
384390
}
385391

386392
return (
387-
<div className="relative">
393+
<div className="relative overflow-x-hidden">
388394
<div
389395
className="w-full bg-white border border-gray-300 rounded-[5px] flex overflow-hidden transition-all duration-200 shadow-sm"
390396
data-chart-id={questionId}
@@ -440,7 +446,7 @@ const HorizontalRatingsChart = ({ questionId, questionTitle, filters = {}, showR
440446

441447
{/* Staircase/Waterfall Bars */}
442448
<div className="p-4">
443-
<div ref={chartContainerRef} className="relative w-full" style={{ height: `${data.length * 24 + 10}px` }}>
449+
<div ref={chartContainerRef} className="relative w-full overflow-x-hidden" style={{ height: `${data.length * 24 + 10}px` }}>
444450
{data.map((item, index) => {
445451
// Calculate cumulative percentage for positioning
446452
const cumulativePercentage = data
@@ -510,46 +516,48 @@ const HorizontalRatingsChart = ({ questionId, questionTitle, filters = {}, showR
510516
{/* Label positioning - always show descriptors next to bars */}
511517
{isSmallBar ? (
512518
// For small bars, show label and percentage on the side of the bar
513-
<div
514-
className="absolute flex items-center gap-1"
519+
<div
520+
className="absolute flex items-center gap-1 max-w-full overflow-hidden"
515521
style={{
516522
// Check if there's enough space on the left (need at least 20% for text)
517523
// Also check if this bar itself is wide enough to avoid overlap
518524
left: (cumulativePercentage > 20 && barWidth < 15) ? `${cumulativePercentage - 1}%` : `${cumulativePercentage + barWidth + 1}%`,
519525
top: '0px',
520526
height: '24px',
521-
transform: (cumulativePercentage > 20 && barWidth < 15) ? 'translateX(-100%)' : 'none'
527+
transform: (cumulativePercentage > 20 && barWidth < 15) ? 'translateX(-100%)' : 'none',
528+
maxWidth: (cumulativePercentage > 20 && barWidth < 15) ? `${cumulativePercentage - 2}%` : `${100 - cumulativePercentage - barWidth - 2}%`
522529
}}
523530
>
524531
{/* Show percentage closest to bar, then descriptor */}
525532
{!percentageFitsInside && (
526-
<span className="text-gray-900 text-xs font-bold whitespace-nowrap">
533+
<span className="text-gray-900 text-xs font-bold whitespace-nowrap flex-shrink-0">
527534
{item.count === 0 ? '-' : (Math.round(item.percentage) === 0 ? '<1%' : `${Math.round(item.percentage)}%`)}
528535
</span>
529536
)}
530-
<span className="text-gray-700 text-xs font-semibold uppercase whitespace-nowrap">
537+
<span className="text-gray-700 text-xs font-semibold uppercase truncate">
531538
{item.label}
532539
</span>
533540
</div>
534541
) : (
535542
// For normal bars, show descriptor next to the bar (left side if space, otherwise right)
536-
<div
537-
className="absolute flex items-center gap-1"
543+
<div
544+
className="absolute flex items-center gap-1 max-w-full overflow-hidden"
538545
style={{
539546
// For larger bars, prefer left side if there's enough space (20%)
540547
left: cumulativePercentage > 20 ? `${cumulativePercentage - 1}%` : `${cumulativePercentage + barWidth + 1}%`,
541548
top: '0px',
542549
height: '24px',
543-
transform: cumulativePercentage > 20 ? 'translateX(-100%)' : 'none'
550+
transform: cumulativePercentage > 20 ? 'translateX(-100%)' : 'none',
551+
maxWidth: cumulativePercentage > 20 ? `${cumulativePercentage - 2}%` : `${100 - cumulativePercentage - barWidth - 2}%`
544552
}}
545553
>
546554
{/* Show percentage closest to bar if it doesn't fit inside, then descriptor */}
547555
{!percentageFitsInside && (
548-
<span className="text-gray-900 text-xs font-bold whitespace-nowrap">
556+
<span className="text-gray-900 text-xs font-bold whitespace-nowrap flex-shrink-0">
549557
{item.count === 0 ? '-' : (Math.round(item.percentage) === 0 ? '<1%' : `${Math.round(item.percentage)}%`)}
550558
</span>
551559
)}
552-
<span className="text-gray-700 text-xs font-semibold uppercase whitespace-nowrap">
560+
<span className="text-gray-700 text-xs font-semibold uppercase truncate">
553561
{item.label}
554562
</span>
555563
</div>

0 commit comments

Comments
 (0)