Skip to content

Commit d036e1f

Browse files
committed
Fix mobile scaling for iOS Safari with portal-based tooltips
- Replace CSS zoom with transform: scale(0.7) for iOS Safari compatibility - Create Tooltip component using React Portal to render outside scaled container - Update all 12 chart components to use new Tooltip component - Simplify tooltip-utils.js by removing scale compensation (no longer needed) This fixes the issue where CSS zoom property was completely ignored on iOS Safari, and resolves tooltip positioning issues caused by position:fixed being broken inside transform:scale() containers.
1 parent 71e250c commit d036e1f

15 files changed

Lines changed: 119 additions & 177 deletions

src/components/BarChart.jsx

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { useRef, useState, useEffect } from 'react';
22
import { chart, cn } from '../styles/classNames';
33
import { defaultChartColor } from '../utils/colorPalette';
44
import { getTooltipPosition, useHideTooltipOnScroll } from '../utils/tooltip-utils';
5+
import Tooltip from './Tooltip';
56

67
const BarChart = ({
78
data,
@@ -326,18 +327,11 @@ const BarChart = ({
326327
</div>
327328

328329
{/* Tooltip */}
329-
{showTooltip && (
330-
<div
331-
className="fixed z-50 bg-gray-900 text-white px-3 py-2 rounded-lg shadow-xl pointer-events-none text-sm whitespace-pre-line border border-gray-600"
332-
style={{
333-
left: tooltipPosition.x,
334-
top: tooltipPosition.y,
335-
maxWidth: '300px'
336-
}}
337-
>
338-
{tooltipContent}
339-
</div>
340-
)}
330+
<Tooltip
331+
show={showTooltip}
332+
position={tooltipPosition}
333+
content={tooltipContent}
334+
/>
341335
</div>
342336
);
343337
};

src/components/ChannelRatingsGrid.jsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
22
import { getRatingScheme } from '../utils/colorPalette';
33
import RespondentIcon from './RespondentIcon';
44
import { getTooltipPosition, useHideTooltipOnScroll } from '../utils/tooltip-utils';
5+
import Tooltip from './Tooltip';
56

67
const CHANNELS = [
78
{ id: 'QRZ4AX', name: 'Official Website & Node-RED documentation' },
@@ -269,18 +270,12 @@ const ChannelRatingsGrid = ({ filters = {}, compact = true, wasmService }) => {
269270
</div>
270271

271272
{/* Tooltip */}
272-
{showTooltip && (
273-
<div
274-
className="fixed z-50 bg-gray-900 text-white px-3 py-2 rounded-lg shadow-xl pointer-events-none text-sm whitespace-pre-line border border-gray-600"
275-
style={{
276-
left: tooltipPosition.x,
277-
top: tooltipPosition.y,
278-
maxWidth: '200px'
279-
}}
280-
>
281-
{tooltipContent}
282-
</div>
283-
)}
273+
<Tooltip
274+
show={showTooltip}
275+
position={tooltipPosition}
276+
content={tooltipContent}
277+
maxWidth="200px"
278+
/>
284279
</div>
285280
);
286281
};

src/components/ChoroplethMap.jsx

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { geoPath, geoMercator } from 'd3-geo';
33
import { feature } from 'topojson-client';
44
import RespondentIcon from './RespondentIcon';
55
import { getTooltipPosition, useHideTooltipOnScroll } from '../utils/tooltip-utils';
6+
import Tooltip from './Tooltip';
67

78
const ChoroplethMap = ({ questionId, questionTitle, filters, _color, wasmService }) => {
89
if (import.meta.env.DEV) console.log('=== ChoroplethMap RENDER ===', { questionId, hasWasmService: !!wasmService });
@@ -361,19 +362,12 @@ const ChoroplethMap = ({ questionId, questionTitle, filters, _color, wasmService
361362
</div>
362363

363364
{/* Tooltip */}
364-
{showTooltip && (
365-
<div
366-
className="fixed z-50 bg-gray-900 text-white px-3 py-2 rounded-lg shadow-xl pointer-events-none text-sm whitespace-pre-line border border-gray-600"
367-
data-testid="choropleth-tooltip"
368-
style={{
369-
left: tooltipPosition.x,
370-
top: tooltipPosition.y,
371-
maxWidth: '200px'
372-
}}
373-
>
374-
{tooltipContent}
375-
</div>
376-
)}
365+
<Tooltip
366+
show={showTooltip}
367+
position={tooltipPosition}
368+
content={tooltipContent}
369+
maxWidth="200px"
370+
/>
377371
</div>
378372
</div>
379373
</div>

src/components/DesignChangesRatingsGrid.jsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
22
import { getRatingScheme } from '../utils/colorPalette';
33
import RespondentIcon from './RespondentIcon';
44
import { getTooltipPosition, useHideTooltipOnScroll } from '../utils/tooltip-utils';
5+
import Tooltip from './Tooltip';
56

67
const DESIGN_CHANGE_QUESTIONS = [
78
{ id: '089k8A', name: 'Node-RED branding (logo, website, forum)' },
@@ -262,18 +263,12 @@ const DesignChangesRatingsGrid = ({ filters = {}, wasmService }) => {
262263
</div>
263264

264265
{/* Tooltip */}
265-
{showTooltip && (
266-
<div
267-
className="fixed z-50 bg-gray-900 text-white px-3 py-2 rounded-lg shadow-xl pointer-events-none text-sm whitespace-pre-line border border-gray-600"
268-
style={{
269-
left: tooltipPosition.x,
270-
top: tooltipPosition.y,
271-
maxWidth: '200px'
272-
}}
273-
>
274-
{tooltipContent}
275-
</div>
276-
)}
266+
<Tooltip
267+
show={showTooltip}
268+
position={tooltipPosition}
269+
content={tooltipContent}
270+
maxWidth="200px"
271+
/>
277272
</div>
278273
);
279274
};

src/components/DeviceSatisfactionGrid.jsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
22
import { getRatingScheme } from '../utils/colorPalette';
33
import RespondentIcon from './RespondentIcon';
44
import { getTooltipPosition, useHideTooltipOnScroll } from '../utils/tooltip-utils';
5+
import Tooltip from './Tooltip';
56

67
const DEVICE_QUESTIONS = [
78
{ id: 'bepze7', name: 'Desktop/Laptop', colorScheme: 'yellow' },
@@ -264,18 +265,12 @@ const DeviceSatisfactionGrid = ({ filters = {}, wasmService }) => {
264265
</div>
265266

266267
{/* Tooltip */}
267-
{showTooltip && (
268-
<div
269-
className="fixed z-50 bg-gray-900 text-white px-3 py-2 rounded-lg shadow-xl pointer-events-none text-sm whitespace-pre-line border border-gray-600"
270-
style={{
271-
left: tooltipPosition.x,
272-
top: tooltipPosition.y,
273-
maxWidth: '200px'
274-
}}
275-
>
276-
{tooltipContent}
277-
</div>
278-
)}
268+
<Tooltip
269+
show={showTooltip}
270+
position={tooltipPosition}
271+
content={tooltipContent}
272+
maxWidth="200px"
273+
/>
279274
</div>
280275
);
281276
};

src/components/HorizontalRatingsChart.jsx

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { getRatingScheme } from '../utils/colorPalette';
33
import { ORDINAL_ORDERS } from '../utils/ordinalOrdering';
44
import RespondentIcon from './RespondentIcon';
55
import { getTooltipPosition, useHideTooltipOnScroll } from '../utils/tooltip-utils';
6+
import Tooltip from './Tooltip';
67

78
const HorizontalRatingsChart = ({ questionId, questionTitle, filters = {}, _showRatingScale = false, _ratingScale = 7, wasmService }) => {
89
const [data, setData] = useState(null);
@@ -490,19 +491,12 @@ const HorizontalRatingsChart = ({ questionId, questionTitle, filters = {}, _show
490491
</div>
491492

492493
{/* Tooltip */}
493-
{showTooltip && (
494-
<div
495-
data-testid="bar-tooltip"
496-
className="fixed z-50 bg-gray-900 text-white px-3 py-2 rounded-lg shadow-xl pointer-events-none text-sm whitespace-pre-line border border-gray-600"
497-
style={{
498-
left: tooltipPosition.x,
499-
top: tooltipPosition.y,
500-
maxWidth: '200px'
501-
}}
502-
>
503-
{tooltipContent}
504-
</div>
505-
)}
494+
<Tooltip
495+
show={showTooltip}
496+
position={tooltipPosition}
497+
content={tooltipContent}
498+
maxWidth="200px"
499+
/>
506500
</div>
507501
);
508502
};

src/components/MatrixChart.jsx

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useEffect, useState } from 'react';
22
import RespondentIcon from './RespondentIcon';
33
import { getTooltipPosition, useHideTooltipOnScroll } from '../utils/tooltip-utils';
4+
import Tooltip from './Tooltip';
45

56
const MatrixChart = ({ questionId, questionTitle, filters, _color, wasmService }) => {
67
const [data, setData] = useState([]);
@@ -453,18 +454,11 @@ const MatrixChart = ({ questionId, questionTitle, filters, _color, wasmService }
453454
</div>
454455

455456
{/* Tooltip */}
456-
{showTooltip && (
457-
<div
458-
className="fixed z-50 bg-gray-900 text-white px-3 py-2 rounded-lg shadow-xl pointer-events-none text-sm whitespace-pre-line border border-gray-600"
459-
style={{
460-
left: tooltipPosition.x,
461-
top: tooltipPosition.y,
462-
maxWidth: '300px'
463-
}}
464-
>
465-
{tooltipContent}
466-
</div>
467-
)}
457+
<Tooltip
458+
show={showTooltip}
459+
position={tooltipPosition}
460+
content={tooltipContent}
461+
/>
468462
</div>
469463
);
470464
};

src/components/QualitativeAnalysis.jsx

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useState, useEffect, memo } from 'react';
22
import RespondentIcon from './RespondentIcon';
33
import { getTooltipPosition, useHideTooltipOnScroll } from '../utils/tooltip-utils';
4+
import Tooltip from './Tooltip';
45

56
const QualitativeAnalysis = ({ questionId, questionText, filters = {}, color = '#64748b', wasmService, baselineOrder }) => {
67
const [data, setData] = useState(null);
@@ -383,18 +384,11 @@ const QualitativeAnalysis = ({ questionId, questionText, filters = {}, color = '
383384
</div>
384385

385386
{/* Tooltip */}
386-
{showTooltip && (
387-
<div
388-
className="fixed z-50 bg-gray-900 text-white px-3 py-2 rounded-lg shadow-xl pointer-events-none text-sm whitespace-pre-line border border-gray-600"
389-
style={{
390-
left: tooltipPosition.x,
391-
top: tooltipPosition.y,
392-
maxWidth: '300px'
393-
}}
394-
>
395-
{tooltipContent}
396-
</div>
397-
)}
387+
<Tooltip
388+
show={showTooltip}
389+
position={tooltipPosition}
390+
content={tooltipContent}
391+
/>
398392
</div>
399393
</div>
400394
);

src/components/QualityComparisonRatingsGrid.jsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
22
import { getRatingScheme } from '../utils/colorPalette';
33
import RespondentIcon from './RespondentIcon';
44
import { getTooltipPosition, useHideTooltipOnScroll } from '../utils/tooltip-utils';
5+
import Tooltip from './Tooltip';
56

67
const QUALITY_COMPARISON_QUESTIONS = [
78
{ id: 'RoNjbJ', name: 'Ease of use/Ease to learn' },
@@ -262,18 +263,12 @@ const QualityComparisonRatingsGrid = ({ filters = {}, wasmService }) => {
262263
</div>
263264

264265
{/* Tooltip */}
265-
{showTooltip && (
266-
<div
267-
className="fixed z-50 bg-gray-900 text-white px-3 py-2 rounded-lg shadow-xl pointer-events-none text-sm whitespace-pre-line border border-gray-600"
268-
style={{
269-
left: tooltipPosition.x,
270-
top: tooltipPosition.y,
271-
maxWidth: '200px'
272-
}}
273-
>
274-
{tooltipContent}
275-
</div>
276-
)}
266+
<Tooltip
267+
show={showTooltip}
268+
position={tooltipPosition}
269+
content={tooltipContent}
270+
maxWidth="200px"
271+
/>
277272
</div>
278273
);
279274
};

src/components/RatingsChart.jsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useState, useEffect, useMemo } from 'react';
22
import { getRatingScheme } from '../utils/colorPalette';
33
import RespondentIcon from './RespondentIcon';
44
import { getTooltipPosition, useHideTooltipOnScroll } from '../utils/tooltip-utils';
5+
import Tooltip from './Tooltip';
56

67
const RatingsChart = ({ questionId, questionTitle, filters = {}, _color, _colorScheme = "blue", ratingScale = 7, compact = false, wasmService }) => {
78
const [data, setData] = useState(null);
@@ -272,18 +273,12 @@ const RatingsChart = ({ questionId, questionTitle, filters = {}, _color, _colorS
272273
</div>
273274

274275
{/* Tooltip */}
275-
{showTooltip && (
276-
<div
277-
className="fixed z-50 bg-gray-900 text-white px-3 py-2 rounded-lg shadow-xl pointer-events-none text-sm whitespace-pre-line border border-gray-600"
278-
style={{
279-
left: tooltipPosition.x,
280-
top: tooltipPosition.y,
281-
maxWidth: '200px'
282-
}}
283-
>
284-
{tooltipContent}
285-
</div>
286-
)}
276+
<Tooltip
277+
show={showTooltip}
278+
position={tooltipPosition}
279+
content={tooltipContent}
280+
maxWidth="200px"
281+
/>
287282
</div>
288283
);
289284
};

0 commit comments

Comments
 (0)