diff --git a/packages/polaris-viz/src/components/FunnelChartNext/Chart.tsx b/packages/polaris-viz/src/components/FunnelChartNext/Chart.tsx
index 58b2493fd..712128643 100644
--- a/packages/polaris-viz/src/components/FunnelChartNext/Chart.tsx
+++ b/packages/polaris-viz/src/components/FunnelChartNext/Chart.tsx
@@ -25,7 +25,6 @@ import {SingleTextLine} from '../Labels';
import {ChartElements} from '../ChartElements';
import {FunnelChartXAxisLabels, Tooltip} from './components/';
-import {calculateDropOff} from './utilities/calculate-dropoff';
import type {FunnelChartNextProps} from './FunnelChartNext';
import {FunnelTooltip} from './components/FunnelTooltip/FunnelTooltip';
import {
diff --git a/packages/polaris-viz/src/components/FunnelChartNext/tests/Chart.test.tsx b/packages/polaris-viz/src/components/FunnelChartNext/tests/Chart.test.tsx
new file mode 100644
index 000000000..5d798920c
--- /dev/null
+++ b/packages/polaris-viz/src/components/FunnelChartNext/tests/Chart.test.tsx
@@ -0,0 +1,113 @@
+import {mount} from '@shopify/react-testing';
+import {ChartContext} from '@shopify/polaris-viz-core';
+import type {DataSeries} from '@shopify/polaris-viz-core';
+import React from 'react';
+
+import {Chart} from '../Chart';
+import {FunnelChartConnector, FunnelChartSegment} from '../../shared';
+import {FunnelTooltip} from '../components/FunnelTooltip/FunnelTooltip';
+import {SingleTextLine} from '../../Labels';
+
+const mockData: DataSeries[] = [
+ {
+ name: 'Group 1',
+ data: [
+ {key: 'Step 1', value: 100},
+ {key: 'Step 2', value: 75},
+ {key: 'Step 3', value: 50},
+ ],
+ },
+];
+
+const mockContext = {
+ containerBounds: {
+ width: 500,
+ height: 300,
+ x: 0,
+ y: 0,
+ },
+};
+
+const defaultProps = {
+ data: mockData,
+ showConnectionPercentage: false,
+ tooltipLabels: {
+ dropoff: 'Dropoff',
+ total: 'Total',
+ },
+ xAxisOptions: {
+ hide: false,
+ labelFormatter: (value: string) => value,
+ },
+ yAxisOptions: {
+ labelFormatter: (value: number) => value.toString(),
+ },
+};
+
+describe('', () => {
+ it('renders funnel segments for each data point', () => {
+ const chart = mount(
+
+
+ ,
+ );
+
+ expect(chart).toContainReactComponentTimes(FunnelChartSegment, 3);
+ });
+
+ it('renders n-1 connectors for n funnel segments, excluding the last segment', () => {
+ const chart = mount(
+
+
+ ,
+ );
+
+ expect(chart).toContainReactComponentTimes(FunnelChartConnector, 2);
+ });
+
+ it('formats labels using the provided formatters', () => {
+ const customFormatter = (value: string) => `Custom ${value}`;
+ const chart = mount(
+
+
+ ,
+ );
+
+ expect(chart).toContainReactComponent(SingleTextLine, {
+ text: 'Custom Step 1',
+ });
+ });
+
+ it('shows tooltip when hovering over a segment', () => {
+ const chart = mount(
+
+
+ ,
+ );
+
+ const firstSegment = chart.find(FunnelChartSegment);
+ firstSegment?.trigger('onMouseEnter', 0);
+
+ expect(chart).toContainReactComponent(FunnelTooltip);
+ });
+
+ it('hides tooltip when mouse leaves a segment', () => {
+ const chart = mount(
+
+
+ ,
+ );
+
+ const firstSegment = chart.find(FunnelChartSegment);
+ firstSegment?.trigger('onMouseEnter', 0);
+ firstSegment?.trigger('onMouseLeave');
+
+ expect(chart).not.toContainReactComponent(FunnelTooltip); // Tooltip container
+ });
+});
diff --git a/packages/polaris-viz/src/components/FunnelChartNext/tests/FunnelChartNext.test.tsx b/packages/polaris-viz/src/components/FunnelChartNext/tests/FunnelChartNext.test.tsx
new file mode 100644
index 000000000..4f1f4c3d8
--- /dev/null
+++ b/packages/polaris-viz/src/components/FunnelChartNext/tests/FunnelChartNext.test.tsx
@@ -0,0 +1,127 @@
+import React from 'react';
+import {mount} from '@shopify/react-testing';
+import {
+ ChartState,
+ ChartContext,
+ DEFAULT_THEME_NAME,
+} from '@shopify/polaris-viz-core';
+import {act} from 'react-dom/test-utils';
+
+import {FunnelChartNext} from '../FunnelChartNext';
+import {Chart} from '../Chart';
+import {ChartSkeleton} from '../../ChartSkeleton';
+import {FunnelChartSegment} from '../../shared';
+import {FunnelConnector} from '../components';
+
+const mockData = [
+ {
+ name: 'Funnel',
+ data: [
+ {key: 'Step 1', value: 1000},
+ {key: 'Step 2', value: 750},
+ {key: 'Step 3', value: 500},
+ {key: 'Step 4', value: 250},
+ ],
+ },
+];
+
+const mockTooltipLabels = {
+ reached: 'Reached',
+ dropped: 'Dropped',
+};
+
+describe('', () => {
+ describe('rendering states', () => {
+ it('renders a Chart when state is Success', () => {
+ const funnel = mount(
+ ,
+ );
+
+ expect(funnel).toContainReactComponent(Chart);
+ });
+
+ it('renders a ChartSkeleton when state is Loading', () => {
+ const funnel = mount(
+ ,
+ );
+
+ expect(funnel).toContainReactComponent(ChartSkeleton, {
+ type: 'Funnel',
+ state: ChartState.Loading,
+ });
+ });
+
+ it('renders a ChartSkeleton with error text when state is Error', () => {
+ const errorText = 'Something went wrong';
+ const funnel = mount(
+ ,
+ );
+
+ expect(funnel).toContainReactComponent(ChartSkeleton, {
+ type: 'Funnel',
+ errorText,
+ state: ChartState.Error,
+ });
+ });
+ });
+
+ describe('chart configuration', () => {
+ it('passes theme to Chart component', () => {
+ const funnel = mount(
+ ,
+ );
+
+ expect(funnel).toContainReactComponent(Chart);
+ });
+
+ it('passes xAxisOptions to Chart', () => {
+ const xAxisOptions = {hide: true};
+ const funnel = mount(
+ ,
+ );
+
+ expect(funnel).toContainReactComponent(Chart, {
+ xAxisOptions: expect.objectContaining({hide: true}),
+ });
+ });
+
+ it('passes yAxisOptions to Chart', () => {
+ const labelFormatter = (value: number) => `$${value}`;
+ const funnel = mount(
+ ,
+ );
+
+ expect(funnel).toContainReactComponent(Chart, {
+ yAxisOptions: expect.objectContaining({labelFormatter}),
+ });
+ });
+ });
+});
diff --git a/packages/polaris-viz/src/hooks/tests/useFunnelBarScaling.test.tsx b/packages/polaris-viz/src/hooks/tests/useFunnelBarScaling.test.tsx
new file mode 100644
index 000000000..51e325f74
--- /dev/null
+++ b/packages/polaris-viz/src/hooks/tests/useFunnelBarScaling.test.tsx
@@ -0,0 +1,118 @@
+import type {Root} from '@shopify/react-testing';
+import {mount} from '@shopify/react-testing';
+import {scaleLinear} from 'd3-scale';
+import React from 'react';
+
+import {
+ useFunnelBarScaling,
+ SCALING_RATIO_THRESHOLD,
+ MINIMUM_SEGMENT_HEIGHT_RATIO,
+} from '../useFunnelBarScaling';
+
+const mockYScale = scaleLinear().domain([0, 100]).range([0, 400]);
+
+function parseData(result: Root) {
+ return JSON.parse(result.domNode?.dataset.data ?? '');
+}
+
+describe('useFunnelBarScaling', () => {
+ it('returns shouldApplyScaling=false when ratio above threshold', () => {
+ function TestComponent() {
+ const data = useFunnelBarScaling({
+ yScale: mockYScale,
+ values: [90, 100],
+ });
+
+ return ;
+ }
+
+ const result = mount();
+ const data = parseData(result);
+
+ expect(data.shouldApplyScaling).toBe(false);
+ });
+
+ it('returns shouldApplyScaling=true when ratio below threshold', () => {
+ function TestComponent() {
+ const data = useFunnelBarScaling({
+ yScale: mockYScale,
+ values: [5, 100],
+ });
+
+ return ;
+ }
+
+ const result = mount();
+ const data = parseData(result);
+
+ expect(data.shouldApplyScaling).toBe(true);
+ });
+
+ describe('getBarHeight', () => {
+ it('returns original height when scaling not needed', () => {
+ function TestComponent() {
+ const data = useFunnelBarScaling({
+ yScale: mockYScale,
+ values: [90, 100],
+ });
+
+ const height = data.getBarHeight(90);
+ return ;
+ }
+
+ const result = mount();
+ const data = parseData(result);
+
+ expect(data.height).toBe(mockYScale(90));
+ });
+
+ it('returns scaled height when scaling needed', () => {
+ function TestComponent() {
+ const data = useFunnelBarScaling({
+ yScale: mockYScale,
+ values: [5, 100],
+ });
+
+ const scaledHeight = data.getBarHeight(5);
+ const originalHeight = mockYScale(5);
+ const tallestHeight = mockYScale(100);
+
+ return (
+
+ );
+ }
+
+ const result = mount();
+ const data = parseData(result);
+
+ expect(data.scaledHeight).toBeGreaterThan(data.originalHeight);
+ expect(data.scaledHeight).toBeLessThan(data.tallestHeight);
+ expect(data.scaledHeight / data.tallestHeight).toBeGreaterThanOrEqual(
+ MINIMUM_SEGMENT_HEIGHT_RATIO,
+ );
+ });
+
+ it('returns original height for tallest bar even when scaling applied', () => {
+ function TestComponent() {
+ const data = useFunnelBarScaling({
+ yScale: mockYScale,
+ values: [5, 100],
+ });
+
+ const height = data.getBarHeight(100);
+ return ;
+ }
+
+ const result = mount();
+ const data = parseData(result);
+
+ expect(data.height).toBe(mockYScale(100));
+ });
+ });
+});