-
Notifications
You must be signed in to change notification settings - Fork 605
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: new chart type - heatmap (#1370)
* feat: new chart type - heatmap * feat: heatmap axis and shape * feat: shape and size * feat: new chart type - heatmap * feat: heatmap axis and shape * feat: shape and size * feat: heatmapStyle * fix: merge v2 * fix: resolve test case * fix: value map to size * feat: sizeRatio and some optimization * rename `shape` to `shapeType` to be consistent with 1.x * optimized the implementation of heatmap adaptor * add tuple type function * add test cases size-spec * fix: temporarily cancel legend * fix: test cases with semantic test dataset
- Loading branch information
Showing
18 changed files
with
955 additions
and
2 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
export const basicHeatmapData = [ | ||
{ name: 0, day: 0, sales: 10 }, | ||
{ name: 0, day: 1, sales: 19 }, | ||
{ name: 0, day: 2, sales: 8 }, | ||
{ name: 0, day: 3, sales: 24 }, | ||
{ name: 0, day: 4, sales: 67 }, | ||
{ name: 1, day: 0, sales: 92 }, | ||
{ name: 1, day: 1, sales: 58 }, | ||
{ name: 1, day: 2, sales: 78 }, | ||
{ name: 1, day: 3, sales: 117 }, | ||
{ name: 1, day: 4, sales: 48 }, | ||
{ name: 2, day: 0, sales: 35 }, | ||
{ name: 2, day: 1, sales: 15 }, | ||
{ name: 2, day: 2, sales: 123 }, | ||
{ name: 2, day: 3, sales: 64 }, | ||
{ name: 2, day: 4, sales: 52 }, | ||
{ name: 3, day: 0, sales: 72 }, | ||
{ name: 3, day: 1, sales: 132 }, | ||
{ name: 3, day: 2, sales: 114 }, | ||
{ name: 3, day: 3, sales: 19 }, | ||
{ name: 3, day: 4, sales: 16 }, | ||
{ name: 4, day: 0, sales: 38 }, | ||
{ name: 4, day: 1, sales: 5 }, | ||
{ name: 4, day: 2, sales: 8 }, | ||
{ name: 4, day: 3, sales: 117 }, | ||
{ name: 4, day: 4, sales: 115 }, | ||
{ name: 5, day: 0, sales: 88 }, | ||
{ name: 5, day: 1, sales: 32 }, | ||
{ name: 5, day: 2, sales: 12 }, | ||
{ name: 5, day: 3, sales: 6 }, | ||
{ name: 5, day: 4, sales: 120 }, | ||
{ name: 6, day: 0, sales: 13 }, | ||
{ name: 6, day: 1, sales: 44 }, | ||
{ name: 6, day: 2, sales: 88 }, | ||
{ name: 6, day: 3, sales: 98 }, | ||
{ name: 6, day: 4, sales: 96 }, | ||
{ name: 7, day: 0, sales: 31 }, | ||
{ name: 7, day: 1, sales: 1 }, | ||
{ name: 7, day: 2, sales: 82 }, | ||
{ name: 7, day: 3, sales: 32 }, | ||
{ name: 7, day: 4, sales: 30 }, | ||
{ name: 8, day: 0, sales: 85 }, | ||
{ name: 8, day: 1, sales: 97 }, | ||
{ name: 8, day: 2, sales: 123 }, | ||
{ name: 8, day: 3, sales: 64 }, | ||
{ name: 8, day: 4, sales: 84 }, | ||
{ name: 9, day: 0, sales: 47 }, | ||
{ name: 9, day: 1, sales: 114 }, | ||
{ name: 9, day: 2, sales: 31 }, | ||
{ name: 9, day: 3, sales: 48 }, | ||
{ name: 9, day: 4, sales: 91 }, | ||
]; | ||
|
||
const NAMES = ['Alexander', 'Marie', 'Maximilian', 'Sophia', 'Lukas', 'Maria', 'Leon', 'Anna', 'Tim', 'Laura']; | ||
const DAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']; | ||
|
||
export const semanticBasicHeatmapData = basicHeatmapData.map((row) => { | ||
return { | ||
name: NAMES[row.name], | ||
day: DAYS[row.day], | ||
sales: row.sales, | ||
}; | ||
}); |
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,74 @@ | ||
import { Heatmap } from '../../../../src'; | ||
import { semanticBasicHeatmapData } from '../../../data/basic-heatmap'; | ||
import { createDiv } from '../../../utils/dom'; | ||
|
||
describe('heatmap', () => { | ||
it('x*y*color and default axis', () => { | ||
const heatmap = new Heatmap(createDiv('default axis'), { | ||
width: 400, | ||
height: 300, | ||
data: semanticBasicHeatmapData, | ||
xField: 'name', | ||
yField: 'day', | ||
colorField: 'sales', | ||
shapeType: 'circle', | ||
label: { | ||
offset: -2, | ||
style: { | ||
fill: '#fff', | ||
shadowBlur: 2, | ||
shadowColor: 'rgba(0, 0, 0, .45)', | ||
}, | ||
}, | ||
}); | ||
|
||
heatmap.render(); | ||
|
||
// @ts-ignore | ||
expect(heatmap.chart.options.axes.day.grid.alignTick).toBe(false); | ||
// @ts-ignore | ||
expect(heatmap.chart.options.axes.name.grid.alignTick).toBe(false); | ||
}); | ||
|
||
it('x*y*color and custom axis', () => { | ||
const heatmap = new Heatmap(createDiv('custom axis'), { | ||
width: 400, | ||
height: 300, | ||
data: semanticBasicHeatmapData, | ||
xField: 'name', | ||
yField: 'day', | ||
colorField: 'sales', | ||
shapeType: 'circle', | ||
label: { | ||
offset: -2, | ||
style: { | ||
fill: '#fff', | ||
shadowBlur: 2, | ||
shadowColor: 'rgba(0, 0, 0, .45)', | ||
}, | ||
}, | ||
xAxis: { | ||
line: { | ||
style: { | ||
lineWidth: 2, | ||
stroke: '#ff0000', | ||
}, | ||
}, | ||
}, | ||
yAxis: { | ||
label: { | ||
style: { | ||
fill: 'red', | ||
}, | ||
}, | ||
}, | ||
}); | ||
|
||
heatmap.render(); | ||
|
||
// @ts-ignore | ||
expect(heatmap.chart.options.axes.name.line.style.lineWidth).toBe(2); | ||
// @ts-ignore | ||
expect(heatmap.chart.options.axes.day.label.style.fill).toBe('red'); | ||
}); | ||
}); |
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,93 @@ | ||
import { Heatmap } from '../../../../src'; | ||
import { basicHeatmapData, semanticBasicHeatmapData } from '../../../data/basic-heatmap'; | ||
import { createDiv } from '../../../utils/dom'; | ||
import { DEFAULT_COLORS } from '../../../../src/constant'; | ||
|
||
describe('heatmap', () => { | ||
it('x*y with color', () => { | ||
const heatmap = new Heatmap(createDiv('basic heatmap'), { | ||
width: 400, | ||
height: 300, | ||
data: semanticBasicHeatmapData, | ||
xField: 'name', | ||
yField: 'day', | ||
colorField: 'sales', | ||
}); | ||
|
||
heatmap.render(); | ||
|
||
const geometry = heatmap.chart.geometries[0]; | ||
|
||
const { elements } = geometry; | ||
|
||
let maxElementIndex = 0; | ||
let minElementIndex = 0; | ||
|
||
elements.forEach((e, i) => { | ||
const value = e.getData().sales; | ||
if (elements[maxElementIndex].getData().sales < value) { | ||
maxElementIndex = i; | ||
} | ||
if (elements[minElementIndex].getData().sales > value) { | ||
minElementIndex = i; | ||
} | ||
}); | ||
|
||
const colors = DEFAULT_COLORS.GRADIENT.CONTINUOUS.split('-'); | ||
expect(elements[maxElementIndex].getModel().color.toUpperCase()).toBe(colors[colors.length - 1]); | ||
expect(elements[minElementIndex].getModel().color.toUpperCase()).toBe(colors[0]); | ||
}); | ||
|
||
it('x*y with color and meta', () => { | ||
const NAMES = ['Alexander', 'Marie', 'Maximilian', 'Sophia', 'Lukas', 'Maria', 'Leon', 'Anna', 'Tim', 'Laura']; | ||
const DAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']; | ||
|
||
const heatmap = new Heatmap(createDiv('basic heatmap by meta'), { | ||
width: 400, | ||
height: 300, | ||
data: basicHeatmapData, | ||
xField: 'name', | ||
yField: 'day', | ||
meta: { | ||
name: { | ||
type: 'cat', | ||
values: NAMES, | ||
}, | ||
day: { | ||
type: 'cat', | ||
values: DAYS, | ||
}, | ||
}, | ||
colorField: 'sales', | ||
}); | ||
|
||
heatmap.render(); | ||
|
||
const geometry = heatmap.chart.geometries[0]; | ||
|
||
expect(geometry.scales.name.isCategory).toBe(true); | ||
expect(geometry.scales.name.values).toStrictEqual(NAMES); | ||
expect(geometry.scales.day.isCategory).toBe(true); | ||
expect(geometry.scales.day.values).toStrictEqual(DAYS); | ||
|
||
const { elements } = geometry; | ||
|
||
expect(elements.length).toBe(NAMES.length * DAYS.length); | ||
let maxElementIndex = 0; | ||
let minElementIndex = 0; | ||
|
||
elements.forEach((e, i) => { | ||
const value = e.getData().sales; | ||
if (elements[maxElementIndex].getData().sales < value) { | ||
maxElementIndex = i; | ||
} | ||
if (elements[minElementIndex].getData().sales > value) { | ||
minElementIndex = i; | ||
} | ||
}); | ||
|
||
const colors = DEFAULT_COLORS.GRADIENT.CONTINUOUS.split('-'); | ||
expect(elements[maxElementIndex].getModel().color.toUpperCase()).toBe(colors[colors.length - 1]); | ||
expect(elements[minElementIndex].getModel().color.toUpperCase()).toBe(colors[0]); | ||
}); | ||
}); |
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,36 @@ | ||
import { Heatmap } from '../../../../src'; | ||
import { semanticBasicHeatmapData } from '../../../data/basic-heatmap'; | ||
import { createDiv } from '../../../utils/dom'; | ||
|
||
describe('heatmap', () => { | ||
it('x*y*color and label', () => { | ||
const heatmap = new Heatmap(createDiv('label'), { | ||
width: 400, | ||
height: 300, | ||
data: semanticBasicHeatmapData, | ||
xField: 'name', | ||
yField: 'day', | ||
colorField: 'sales', | ||
label: { | ||
offset: -2, | ||
style: { | ||
fill: '#fff', | ||
shadowBlur: 2, | ||
shadowColor: 'rgba(0, 0, 0, .45)', | ||
}, | ||
}, | ||
}); | ||
|
||
heatmap.render(); | ||
|
||
// @ts-ignore | ||
expect(heatmap.chart.geometries[0].labelOption.cfg).toEqual({ | ||
offset: -2, | ||
style: { | ||
fill: '#fff', | ||
shadowBlur: 2, | ||
shadowColor: 'rgba(0, 0, 0, .45)', | ||
}, | ||
}); | ||
}); | ||
}); |
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,92 @@ | ||
import { Heatmap } from '../../../../src'; | ||
import { semanticBasicHeatmapData } from '../../../data/basic-heatmap'; | ||
import { createDiv } from '../../../utils/dom'; | ||
|
||
describe('heatmap', () => { | ||
it('x*y*color and shape', () => { | ||
const heatmap = new Heatmap(createDiv('just change shape'), { | ||
width: 400, | ||
height: 300, | ||
data: semanticBasicHeatmapData, | ||
xField: 'name', | ||
yField: 'day', | ||
colorField: 'sales', | ||
shapeType: 'square', | ||
label: { | ||
offset: -2, | ||
style: { | ||
fill: '#fff', | ||
shadowBlur: 2, | ||
shadowColor: 'rgba(0, 0, 0, .45)', | ||
}, | ||
}, | ||
}); | ||
|
||
heatmap.render(); | ||
|
||
const geometry = heatmap.chart.geometries[0]; | ||
const { elements } = geometry; | ||
expect(elements[0].shape.cfg.type).toEqual('rect'); | ||
}); | ||
|
||
it('x*y*color and size', () => { | ||
const heatmap = new Heatmap(createDiv('just change size'), { | ||
width: 400, | ||
height: 300, | ||
data: semanticBasicHeatmapData, | ||
xField: 'name', | ||
yField: 'day', | ||
colorField: 'sales', | ||
sizeField: 'sales', | ||
label: { | ||
offset: -2, | ||
style: { | ||
fill: '#fff', | ||
shadowBlur: 2, | ||
shadowColor: 'rgba(0, 0, 0, .45)', | ||
}, | ||
}, | ||
}); | ||
|
||
heatmap.render(); | ||
|
||
const geometry = heatmap.chart.geometries[0]; | ||
expect(geometry.scales.sales.isContinuous).toBe(true); | ||
// @ts-ignore | ||
expect(geometry.attributeOption.shape.fields).toEqual(['sales']); | ||
|
||
const { elements } = geometry; | ||
expect(elements[0].shape.cfg.type).toEqual('rect'); | ||
}); | ||
|
||
it('x*y*color and shape*size', () => { | ||
const heatmap = new Heatmap(createDiv('shape with size'), { | ||
width: 400, | ||
height: 300, | ||
data: semanticBasicHeatmapData, | ||
xField: 'name', | ||
yField: 'day', | ||
colorField: 'sales', | ||
shapeType: 'circle', | ||
sizeField: 'sales', | ||
label: { | ||
offset: -2, | ||
style: { | ||
fill: '#fff', | ||
shadowBlur: 2, | ||
shadowColor: 'rgba(0, 0, 0, .45)', | ||
}, | ||
}, | ||
}); | ||
|
||
heatmap.render(); | ||
|
||
const geometry = heatmap.chart.geometries[0]; | ||
expect(geometry.scales.sales.isContinuous).toBe(true); | ||
// @ts-ignore | ||
expect(geometry.attributeOption.shape.fields).toEqual(['sales']); | ||
|
||
const { elements } = geometry; | ||
expect(elements[0].shape.cfg.type).toEqual('circle'); | ||
}); | ||
}); |
Oops, something went wrong.