-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
41643de
commit b546085
Showing
4 changed files
with
358 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 113 additions & 0 deletions
113
packages/polaris-viz/src/components/FunnelChartNext/tests/Chart.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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('<Chart />', () => { | ||
it('renders funnel segments for each data point', () => { | ||
const chart = mount( | ||
<ChartContext.Provider value={mockContext}> | ||
<Chart {...defaultProps} /> | ||
</ChartContext.Provider>, | ||
); | ||
|
||
expect(chart).toContainReactComponentTimes(FunnelChartSegment, 3); | ||
}); | ||
|
||
it('renders n-1 connectors for n funnel segments, excluding the last segment', () => { | ||
const chart = mount( | ||
<ChartContext.Provider value={mockContext}> | ||
<Chart {...defaultProps} /> | ||
</ChartContext.Provider>, | ||
); | ||
|
||
expect(chart).toContainReactComponentTimes(FunnelChartConnector, 2); | ||
}); | ||
|
||
it('formats labels using the provided formatters', () => { | ||
const customFormatter = (value: string) => `Custom ${value}`; | ||
const chart = mount( | ||
<ChartContext.Provider value={mockContext}> | ||
<Chart | ||
{...defaultProps} | ||
xAxisOptions={{ | ||
...defaultProps.xAxisOptions, | ||
labelFormatter: customFormatter, | ||
}} | ||
/> | ||
</ChartContext.Provider>, | ||
); | ||
|
||
expect(chart).toContainReactComponent(SingleTextLine, { | ||
text: 'Custom Step 1', | ||
}); | ||
}); | ||
|
||
it('shows tooltip when hovering over a segment', () => { | ||
const chart = mount( | ||
<ChartContext.Provider value={mockContext}> | ||
<Chart {...defaultProps} /> | ||
</ChartContext.Provider>, | ||
); | ||
|
||
const firstSegment = chart.find(FunnelChartSegment); | ||
firstSegment?.trigger('onMouseEnter', 0); | ||
|
||
expect(chart).toContainReactComponent(FunnelTooltip); | ||
}); | ||
|
||
it('hides tooltip when mouse leaves a segment', () => { | ||
const chart = mount( | ||
<ChartContext.Provider value={mockContext}> | ||
<Chart {...defaultProps} /> | ||
</ChartContext.Provider>, | ||
); | ||
|
||
const firstSegment = chart.find(FunnelChartSegment); | ||
firstSegment?.trigger('onMouseEnter', 0); | ||
firstSegment?.trigger('onMouseLeave'); | ||
|
||
expect(chart).not.toContainReactComponent(FunnelTooltip); // Tooltip container | ||
}); | ||
}); |
127 changes: 127 additions & 0 deletions
127
packages/polaris-viz/src/components/FunnelChartNext/tests/FunnelChartNext.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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('<FunnelChartNext />', () => { | ||
describe('rendering states', () => { | ||
it('renders a Chart when state is Success', () => { | ||
const funnel = mount( | ||
<FunnelChartNext | ||
data={mockData} | ||
tooltipLabels={mockTooltipLabels} | ||
state={ChartState.Success} | ||
/>, | ||
); | ||
|
||
expect(funnel).toContainReactComponent(Chart); | ||
}); | ||
|
||
it('renders a ChartSkeleton when state is Loading', () => { | ||
const funnel = mount( | ||
<FunnelChartNext | ||
data={mockData} | ||
tooltipLabels={mockTooltipLabels} | ||
state={ChartState.Loading} | ||
/>, | ||
); | ||
|
||
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( | ||
<FunnelChartNext | ||
data={mockData} | ||
tooltipLabels={mockTooltipLabels} | ||
state={ChartState.Error} | ||
errorText={errorText} | ||
/>, | ||
); | ||
|
||
expect(funnel).toContainReactComponent(ChartSkeleton, { | ||
type: 'Funnel', | ||
errorText, | ||
state: ChartState.Error, | ||
}); | ||
}); | ||
}); | ||
|
||
describe('chart configuration', () => { | ||
it('passes theme to Chart component', () => { | ||
const funnel = mount( | ||
<FunnelChartNext | ||
data={mockData} | ||
tooltipLabels={mockTooltipLabels} | ||
theme={DEFAULT_THEME_NAME} | ||
state={ChartState.Success} | ||
/>, | ||
); | ||
|
||
expect(funnel).toContainReactComponent(Chart); | ||
}); | ||
|
||
it('passes xAxisOptions to Chart', () => { | ||
const xAxisOptions = {hide: true}; | ||
const funnel = mount( | ||
<FunnelChartNext | ||
data={mockData} | ||
tooltipLabels={mockTooltipLabels} | ||
xAxisOptions={xAxisOptions} | ||
state={ChartState.Success} | ||
/>, | ||
); | ||
|
||
expect(funnel).toContainReactComponent(Chart, { | ||
xAxisOptions: expect.objectContaining({hide: true}), | ||
}); | ||
}); | ||
|
||
it('passes yAxisOptions to Chart', () => { | ||
const labelFormatter = (value: number) => `$${value}`; | ||
const funnel = mount( | ||
<FunnelChartNext | ||
data={mockData} | ||
tooltipLabels={mockTooltipLabels} | ||
yAxisOptions={{labelFormatter}} | ||
state={ChartState.Success} | ||
/>, | ||
); | ||
|
||
expect(funnel).toContainReactComponent(Chart, { | ||
yAxisOptions: expect.objectContaining({labelFormatter}), | ||
}); | ||
}); | ||
}); | ||
}); |
118 changes: 118 additions & 0 deletions
118
packages/polaris-viz/src/hooks/tests/useFunnelBarScaling.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<any>) { | ||
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 <span data-data={`${JSON.stringify(data)}`} />; | ||
} | ||
|
||
const result = mount(<TestComponent />); | ||
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 <span data-data={`${JSON.stringify(data)}`} />; | ||
} | ||
|
||
const result = mount(<TestComponent />); | ||
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 <span data-data={`${JSON.stringify({height})}`} />; | ||
} | ||
|
||
const result = mount(<TestComponent />); | ||
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 ( | ||
<span | ||
data-data={`${JSON.stringify({ | ||
scaledHeight, | ||
originalHeight, | ||
tallestHeight, | ||
})}`} | ||
/> | ||
); | ||
} | ||
|
||
const result = mount(<TestComponent />); | ||
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 <span data-data={`${JSON.stringify({height})}`} />; | ||
} | ||
|
||
const result = mount(<TestComponent />); | ||
const data = parseData(result); | ||
|
||
expect(data.height).toBe(mockYScale(100)); | ||
}); | ||
}); | ||
}); |