diff --git a/packages/polaris-viz/src/components/BarChart/stories/playground/ExternalTooltip.stories.tsx b/packages/polaris-viz/src/components/BarChart/stories/playground/ExternalTooltip.stories.tsx index 86ff4b0f3..914a590a4 100644 --- a/packages/polaris-viz/src/components/BarChart/stories/playground/ExternalTooltip.stories.tsx +++ b/packages/polaris-viz/src/components/BarChart/stories/playground/ExternalTooltip.stories.tsx @@ -77,6 +77,7 @@ export const ExternalTooltipWithFrame: Story = TemplateWithFrame.bind({}); ExternalTooltip.args = { + direction: 'horizontal', data: [ { name: 'Apr 1 – Apr 14, 2020', diff --git a/packages/polaris-viz/src/components/ChartContainer/ChartContainer.tsx b/packages/polaris-viz/src/components/ChartContainer/ChartContainer.tsx index 847587099..996f1bd77 100644 --- a/packages/polaris-viz/src/components/ChartContainer/ChartContainer.tsx +++ b/packages/polaris-viz/src/components/ChartContainer/ChartContainer.tsx @@ -98,7 +98,8 @@ export const ChartContainer = (props: Props) => {
{
)} diff --git a/packages/polaris-viz/src/components/TooltipWrapper/TooltipWrapper.tsx b/packages/polaris-viz/src/components/TooltipWrapper/TooltipWrapper.tsx index 8d31f8193..fffb9b055 100644 --- a/packages/polaris-viz/src/components/TooltipWrapper/TooltipWrapper.tsx +++ b/packages/polaris-viz/src/components/TooltipWrapper/TooltipWrapper.tsx @@ -37,6 +37,7 @@ interface BaseProps { xScale: ScaleLinear | ScaleBand; bandwidth?: number; onIndexChange?: (index: number | null) => void; + highestValueForSeries?: number[]; id?: string; type?: ChartType; yScale?: ScaleLinear; @@ -56,6 +57,7 @@ function TooltipWrapperRaw(props: BaseProps) { type, xScale, yScale, + highestValueForSeries, } = props; const {scrollContainer, isTouchDevice, containerBounds} = useChartContext(); const [position, setPosition] = useState({ @@ -111,6 +113,7 @@ function TooltipWrapperRaw(props: BaseProps) { case InternalChartType.HorizontalBar: return getHorizontalBarChartTooltipPosition({ chartBounds, + containerBounds, data, event, eventType, @@ -118,6 +121,11 @@ function TooltipWrapperRaw(props: BaseProps) { longestSeriesIndex, type, xScale: xScale as ScaleLinear, + highestValueForSeries, + bandwidth, + scrollY: scrollContainer + ? scrollContainer.scrollTop + : window.scrollY, }); case InternalChartType.Bar: default: @@ -136,6 +144,8 @@ function TooltipWrapperRaw(props: BaseProps) { } }, [ + highestValueForSeries, + bandwidth, chartBounds, containerBounds, chartType, diff --git a/packages/polaris-viz/src/components/TooltipWrapper/utilities/getAlteredHorizontalBarPosition.ts b/packages/polaris-viz/src/components/TooltipWrapper/utilities/getAlteredHorizontalBarPosition.ts index 9da13058f..451a4c68b 100644 --- a/packages/polaris-viz/src/components/TooltipWrapper/utilities/getAlteredHorizontalBarPosition.ts +++ b/packages/polaris-viz/src/components/TooltipWrapper/utilities/getAlteredHorizontalBarPosition.ts @@ -1,7 +1,7 @@ -import type {BoundingRect, Dimensions} from '@shopify/polaris-viz-core'; -import {HORIZONTAL_GROUP_LABEL_HEIGHT} from '@shopify/polaris-viz-core'; +import type {Dimensions} from '@shopify/polaris-viz-core'; +import {clamp, HORIZONTAL_GROUP_LABEL_HEIGHT} from '@shopify/polaris-viz-core'; -import {TOOLTIP_MARGIN} from '../constants'; +import {SCROLLBAR_WIDTH, TOOLTIP_MARGIN} from '../constants'; import type {AlteredPositionProps, AlteredPositionReturn} from '../types'; export function getAlteredHorizontalBarPosition( @@ -20,100 +20,59 @@ function getNegativeOffset(props: AlteredPositionProps): AlteredPositionReturn { const yOffset = (bandwidth - tooltipDimensions.height) / 2; const y = currentY - tooltipDimensions.height; + if (flippedX - tooltipDimensions.width < 0) { - return {x: flippedX, y: y < 0 ? 0 : y}; + return clampPosition({ + x: flippedX, + y: y < 0 ? 0 : y, + tooltipDimensions, + }); } - return { + return clampPosition({ x: flippedX - tooltipDimensions.width - TOOLTIP_MARGIN, y: currentY + HORIZONTAL_GROUP_LABEL_HEIGHT + yOffset, - }; + tooltipDimensions, + }); } function getPositiveOffset(props: AlteredPositionProps): AlteredPositionReturn { - const {bandwidth, currentX, currentY, tooltipDimensions, chartBounds} = props; + const {currentX, currentY} = props; - const isOutside = isOutsideBounds({ + return clampPosition({ x: currentX, y: currentY, - tooltipDimensions, - chartBounds, + tooltipDimensions: props.tooltipDimensions, }); - - if (isOutside.top && isOutside.right) { - return { - x: chartBounds.width - tooltipDimensions.width, - y: 0, - }; - } - - if (isOutside.top && !isOutside.right) { - return { - x: currentX + TOOLTIP_MARGIN, - y: 0, - }; - } - - if (!isOutside.right && !isOutside.bottom) { - const yOffset = (bandwidth - tooltipDimensions.height) / 2; - return { - x: currentX + TOOLTIP_MARGIN, - y: currentY + HORIZONTAL_GROUP_LABEL_HEIGHT + yOffset, - }; - } - - if (isOutside.right) { - const x = currentX - tooltipDimensions.width; - const y = - currentY - - tooltipDimensions.height + - HORIZONTAL_GROUP_LABEL_HEIGHT - - TOOLTIP_MARGIN; - - if (y < 0) { - return { - x, - y: bandwidth + HORIZONTAL_GROUP_LABEL_HEIGHT + TOOLTIP_MARGIN, - }; - } - - return { - x, - y, - }; - } - - if (isOutside.bottom) { - return { - x: currentX + TOOLTIP_MARGIN, - y: - chartBounds.height - - tooltipDimensions.height - - HORIZONTAL_GROUP_LABEL_HEIGHT, - }; - } - - return {x: currentX, y: currentY}; } -function isOutsideBounds({ +function clampPosition({ x, y, tooltipDimensions, - chartBounds, }: { x: number; y: number; tooltipDimensions: Dimensions; - chartBounds: BoundingRect; }) { - const right = x + TOOLTIP_MARGIN + tooltipDimensions.width; - const bottom = y + tooltipDimensions.height; - return { - left: x <= 0, - right: right > chartBounds.width, - bottom: bottom > chartBounds.height, - top: y <= 0, + x: clamp({ + amount: x, + min: TOOLTIP_MARGIN, + max: + window.innerWidth - + tooltipDimensions.width - + TOOLTIP_MARGIN - + SCROLLBAR_WIDTH, + }), + y: clamp({ + amount: y, + min: window.scrollY + TOOLTIP_MARGIN, + max: + window.scrollY + + window.innerHeight - + tooltipDimensions.height - + TOOLTIP_MARGIN, + }), }; } diff --git a/packages/polaris-viz/src/components/TooltipWrapper/utilities/getHorizontalBarChartTooltipPosition.ts b/packages/polaris-viz/src/components/TooltipWrapper/utilities/getHorizontalBarChartTooltipPosition.ts index 12747696b..a9a5a0025 100644 --- a/packages/polaris-viz/src/components/TooltipWrapper/utilities/getHorizontalBarChartTooltipPosition.ts +++ b/packages/polaris-viz/src/components/TooltipWrapper/utilities/getHorizontalBarChartTooltipPosition.ts @@ -1,4 +1,6 @@ import type {ScaleLinear} from 'd3-scale'; +import type {BoundingRect} from '@shopify/polaris-viz-core'; +import {Y_AXIS_CHART_SPACING} from '@shopify/polaris-viz-core'; import {getStackedValuesFromDataSeries} from '../../../utilities/getStackedValuesFromDataSeries'; import type {TooltipPosition, TooltipPositionParams} from '../types'; @@ -6,12 +8,19 @@ import {TOOLTIP_POSITION_DEFAULT_RETURN} from '../constants'; import {eventPointNative} from './eventPoint'; +const SPACING = 10; + interface Props extends Omit { + bandwidth: number; + containerBounds: BoundingRect; + highestValueForSeries: number[]; + scrollY: number; xScale: ScaleLinear; } export function getHorizontalBarChartTooltipPosition({ chartBounds, + containerBounds, data, event, eventType, @@ -19,8 +28,13 @@ export function getHorizontalBarChartTooltipPosition({ longestSeriesIndex, type, xScale, + scrollY, + highestValueForSeries, + bandwidth, }: Props): TooltipPosition { - const groupHeight = chartBounds.height / data[longestSeriesIndex].data.length; + const groupHeight = + (chartBounds.height - Y_AXIS_CHART_SPACING) / + data[longestSeriesIndex].data.length; const isStacked = type === 'stacked'; if (eventType === 'mouse' && event) { @@ -32,7 +46,7 @@ export function getHorizontalBarChartTooltipPosition({ const {svgY} = point; - const currentPoint = svgY - 0; + const currentPoint = svgY - scrollY; const currentIndex = Math.floor(currentPoint / groupHeight); if ( @@ -42,14 +56,17 @@ export function getHorizontalBarChartTooltipPosition({ return TOOLTIP_POSITION_DEFAULT_RETURN; } - return formatPositionForTooltip(currentIndex); + return formatPositionForTooltip(currentIndex, containerBounds); } else if (index != null) { - return formatPositionForTooltip(index); + return formatPositionForTooltip(index, containerBounds); } return TOOLTIP_POSITION_DEFAULT_RETURN; - function formatPositionForTooltip(index: number): TooltipPosition { + function formatPositionForTooltip( + index: number, + containerBounds: BoundingRect, + ): TooltipPosition { if (isStacked) { const {formattedStackedValues} = getStackedValuesFromDataSeries(data); @@ -64,18 +81,18 @@ export function getHorizontalBarChartTooltipPosition({ }, xScale(0)); return { - x: chartBounds.x + x, - y: chartBounds.y + groupHeight * index, + x: containerBounds.x + x, + y: containerBounds.y + groupHeight * index, activeIndex: index, }; } - const highestValue = data[longestSeriesIndex].data[index].value ?? 0; - const x = chartBounds.x + (xScale(highestValue ?? 0) ?? 0); + const highestValue = highestValueForSeries[index] ?? 0; + const x = containerBounds.x + (xScale(highestValue ?? 0) ?? 0) + SPACING; return { x: highestValue < 0 ? -x : x, - y: groupHeight * index, + y: containerBounds.y + groupHeight * index - bandwidth * index, activeIndex: index, }; }