Skip to content

Commit

Permalink
feat(tooltip): Enhance passing ratio value for stacked area/bar
Browse files Browse the repository at this point in the history
Make to pass 'ratio' to tooltip.format.value callback for
stacked area/bar type.

Ref naver#2921
  • Loading branch information
netil committed Oct 31, 2022
1 parent 0809ae7 commit 62ea3aa
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 17 deletions.
23 changes: 21 additions & 2 deletions src/ChartInternal/data/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ export default {
const cacheKey = KEY.dataTotalPerIndex;
let sum = $$.cache.get(cacheKey);

if ($$.isStackNormalized() && !sum) {
if (($$.config.data_groups.length || $$.isStackNormalized()) && !sum) {
sum = [];

$$.data.targets.forEach(row => {
Expand Down Expand Up @@ -883,6 +883,25 @@ export default {
return value;
},

/**
* Set ratio for grouped data
* @param {Array} data Data array
* @private
*/
setRatioForGroupedData(data: (IDataRow | IData)[]): void {
const $$ = this;
const {config} = $$;

// calculate ratio if grouped data exists
if (config.data_groups.length && data.some(d => $$.isGrouped(d.id))) {
const setter = (d: IDataRow) => $$.getRatio("index", d, true);

data.forEach(v => {
"values" in v ? v.values.forEach(setter) : setter(v);
});
}
},

/**
* Get ratio value
* @param {string} type Ratio for given type
Expand Down Expand Up @@ -927,7 +946,7 @@ export default {
}
}

d.ratio = isNumber(d.value) && total && total[d.index] > 0 ?
d.ratio = isNumber(d.value) && total ?
d.value / total[d.index] : 0;

ratio = d.ratio;
Expand Down
3 changes: 3 additions & 0 deletions src/ChartInternal/shape/area.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ export default {
.merge(area);

area.style("opacity", state.orgAreaOpacity);

// calculate ratio if grouped data exists
$$.setRatioForGroupedData($root.area.data());
},

/**
Expand Down
3 changes: 3 additions & 0 deletions src/ChartInternal/shape/bar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ export default {
.style("fill", $$.updateBarColor.bind($$))
.merge(bar)
.style("opacity", initialOpacity);

// calculate ratio if grouped data exists
$$.setRatioForGroupedData($root.bar.data());
},

/**
Expand Down
19 changes: 11 additions & 8 deletions src/config/Options/common/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,20 @@ export default {
* Specified function receives x of the data point to show.
* @property {Function} [tooltip.format.name] Set format for the name of each data in tooltip.<br>
* Specified function receives name, ratio, id and index of the data point to show. ratio will be undefined if the chart is not donut/pie/gauge.
* @property {Function} [tooltip.format.value] Set format for the value of each data in tooltip.<br>
* Specified function receives name, ratio, id and index of the data point to show. ratio will be undefined if the chart is not donut/pie/gauge.
* If undefined returned, the row of that value will be skipped.
* @property {Function} [tooltip.format.value] Set format for the value of each data in tooltip. If undefined returned, the row of that value will be skipped to be called.
* - Will pass following arguments to the given function:
* - `value {string}`: Value of the data point
* - `ratio {number}`: Ratio of the data point in the `pie/donut/gauge` and `area/bar` when contains grouped data. Otherwise is `undefined`.
* - `id {string}`: id of the data point
* - `index {number}`: Index of the data point
* @property {Function} [tooltip.position] Set custom position function for the tooltip.<br>
* This option can be used to modify the tooltip position by returning object that has top and left.
* - Will pass following arguments to the given function:
* - `data {Array}`: Current selected data array object.
* - `width {number}`: Width of tooltip.
* - `height {number}`: Height of tooltip.
* - `element {SVGElement}`: Tooltip event bound element
* - `pos {object}`: Current position of the tooltip.
* - `data {Array}`: Current selected data array object.
* - `width {number}`: Width of tooltip.
* - `height {number}`: Height of tooltip.
* - `element {SVGElement}`: Tooltip event bound element
* - `pos {object}`: Current position of the tooltip.
* @property {Function|object} [tooltip.contents] Set custom HTML for the tooltip.<br>
* If tooltip.grouped is true, data includes multiple data points.<br><br>
* Specified function receives `data` array and `defaultTitleFormat`, `defaultValueFormat` and `color` functions of the data point to show.
Expand Down
74 changes: 73 additions & 1 deletion test/internals/tooltip-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1580,7 +1580,6 @@ describe("TOOLTIP", function() {
});

describe("tooltip: bar type within a range", () => {

it("should display start ~ end", () => {
chart = util.generate({
data: {
Expand All @@ -1596,4 +1595,77 @@ describe("TOOLTIP", function() {
.to.be.equal("1300 ~ 1339");
});
});

describe("tooltip: format", () => {
const spy = sinon.spy(function(value, ratio, id, index) {
return [value, ratio, id, index];
});

before(() => {
args = {
data: {
columns: [
["data1", 50, 80, 10],
["data2", 50, 20, 90]
],
type: "bar", // for ESM specify as: bar()
groups: [
["data1", "data2"]
],
},
tooltip: {
format: {
value: spy
}
}
};
});

it("check if ratio value is given to format function for 'bar' type.", () => {
chart.data.values("data1").forEach((v, i) => {
chart.tooltip.show({x: i});

expect(spy.callCount).to.be.equal(args.data.columns.length);

// check ratio
expect(spy.returnValues.reduce((p, a) => p?.[1] ?? p + a[1], 0)).to.be.equal(1);

spy.resetHistory();
});
});

it("set option: data.type='area'", () => {
args.data.type = "area";
});

it("check if ratio value is given to format function for 'area' type.", () => {
chart.data.values("data1").forEach((v, i) => {
chart.tooltip.show({x: i});

expect(spy.callCount).to.be.equal(args.data.columns.length);

// check ratio
expect(spy.returnValues.reduce((p, a) => p?.[1] ?? p + a[1], 0)).to.be.equal(1);

spy.resetHistory();
});
});

it("set option: data.type='area'", () => {
args.data.columns.push(["data3", 50, 20, 90]);
});

it("check correct ratio value is given when contains non-grouped single data series.", () => {
chart.data.values("data1").forEach((v, i) => {
chart.tooltip.show({x: i});

expect(spy.callCount).to.be.equal(args.data.columns.length);

// check ratio
expect(spy.returnValues.reduce((p, a) => p?.[1] ?? p + a[1], 0)).to.be.equal(1);

spy.resetHistory();
});
});
});
});
14 changes: 8 additions & 6 deletions types/options.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1108,12 +1108,14 @@ export interface TooltipOptions {
name?(this: Chart, name: string, ratio: number, id: string, index: number): string;

/**
* Set format for the value of each data in tooltip.
* Specified function receives name, ratio, id and index of the data point to show.
* ratio will be undefined if the chart is not donut/pie/gauge.
* If undefined returned, the row of that value will be skipped.
*/
value?(this: Chart, value: any, ratio: number, id: string, index: number): string;
* Set format for the value of each data in tooltip. If undefined returned, the row of that value will be skipped to be called.
* - Will pass following arguments to the given function:
* - `value {string}`: Value of the data point
* - `ratio {number}`: Ratio of the data point in the `pie/donut/gauge` and `area/bar` when contains grouped data. Otherwise is `undefined`.
* - `id {string}`: id of the data point
* - `index {number}`: Index of the data point
*/
value?(this: Chart, value: number, ratio: number | undefined, id: string, index: number): string;
};
/**
* Set tooltip values order
Expand Down

0 comments on commit 62ea3aa

Please sign in to comment.