Skip to content

Commit

Permalink
feat(api): Enhance resizeAfter for .load/unload APIs
Browse files Browse the repository at this point in the history
Set as default the resizing after .load() or .unload() is called.
To keep backward compatibility, add `resizeAfter` option to on/off this behavior.

Ref naver#3157
  • Loading branch information
netil committed Mar 30, 2023
1 parent 9d897ef commit c5122dc
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 26 deletions.
48 changes: 29 additions & 19 deletions src/Chart/api/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/
import {window} from "../../module/browser";
import {isString, isArray} from "../../module/util";
import {callDone} from "../../ChartInternal/data/load";

export default {
/**
Expand All @@ -19,23 +20,29 @@ export default {
* @memberof Chart
* @param {object} args The object can consist with following members:<br>
*
* | Key | Description |
* | --- | --- |
* | - url<br>- json<br>- rows<br>- columns | The data will be loaded. If data that has the same target id is given, the chart will be updated. Otherwise, new target will be added |
* | data | Data objects to be loaded. Checkout the example. |
* | append | Load data appending it to the current dataseries.<br>If the existing chart has`x` value, should provide with corresponding `x` value for newly loaded data. |
* | names | Same as data.names() |
* | xs | Same as data.xs option |
* | classes | The classes specified by data.classes will be updated. classes must be Object that has target id as keys. |
* | categories | The categories specified by axis.x.categories or data.x will be updated. categories must be Array. |
* | axes | The axes specified by data.axes will be updated. axes must be Object that has target id as keys. |
* | colors | The colors specified by data.colors will be updated. colors must be Object that has target id as keys. |
* | headers | Set request header if loading via `data.url`.<br>@see [data․headers](Options.html#.data%25E2%2580%25A4headers) |
* | keys | Choose which JSON objects keys correspond to desired data.<br>**NOTE:** Only for JSON object given as array.<br>@see [data․keys](Options.html#.data%25E2%2580%25A4keys) |
* | mimeType | Set 'json' if loading JSON via url.<br>@see [data․mimeType](Options.html#.data%25E2%2580%25A4mimeType) |
* | - type<br>- types | The type of targets will be updated. type must be String and types must be Object. |
* | unload | Specify the data will be unloaded before loading new data. If true given, all of data will be unloaded. If target ids given as String or Array, specified targets will be unloaded. If absent or false given, unload will not occur. |
* | done | The specified function will be called after data loaded.|
* | Key | Type | Description |
* | --- | --- | --- |
* | columns | Array | The `columns` data will be loaded. If data that has the same target id is given, the chart will be updated. Otherwise, new target will be added |
* | json | Array | The `json` data will be loaded. If data that has the same target id is given, the chart will be updated. Otherwise, new target will be added |
* | rows | Array | The `rows` data will be loaded. If data that has the same target id is given, the chart will be updated. Otherwise, new target will be added |
* | url | string | The data from `url` will be loaded. If data that has the same target id is given, the chart will be updated. Otherwise, new target will be added |
* | &nbsp; | | |
* | append | boolean | Load data appending it to the current dataseries.<br>If the existing chart has`x` value, should provide with corresponding `x` value for newly loaded data. |
* | axes | Object | The axes specified by data.axes will be updated. axes must be Object that has target id as keys. |
* | categories | Array | The categories specified by axis.x.categories or data.x will be updated. categories must be Array. |
* | classes | Object | The classes specified by data.classes will be updated. classes must be Object that has target id as keys. |
* | colors | Object | The colors specified by data.colors will be updated. colors must be Object that has target id as keys. |
* | data | Obejct | Data objects to be loaded. Checkout the example. |
* | done | Function | The specified function will be called after data loaded.|
* | headers | string | Set request header if loading via `data.url`.<br>@see [data․headers](Options.html#.data%25E2%2580%25A4headers) |
* | keys | Object | Choose which JSON objects keys correspond to desired data.<br>**NOTE:** Only for JSON object given as array.<br>@see [data․keys](Options.html#.data%25E2%2580%25A4keys) |
* | mimeType | string | Set 'json' if loading JSON via url.<br>@see [data․mimeType](Options.html#.data%25E2%2580%25A4mimeType) |
* | names | Object | Same as data.names() |
* | resizeAfter | boolean | Resize after the load. Default value is `true`. This option won't call `onresize` neither `onresized`. |
* | type | string | The type of targets will be updated. |
* | types | Object | The types of targets will be updated. |
* | unload | Array | Specify the data will be unloaded before loading new data. If true given, all of data will be unloaded. If target ids given as String or Array, specified targets will be unloaded. If absent or false given, unload will not occur. |
* | xs | string | Same as data.xs option |
* @see [Demo](https://naver.github.io/billboard.js/demo/#Data.DataFromURL)
* @example
* // Load data1 and unload data2 and data3
Expand All @@ -47,6 +54,7 @@ export default {
* unload: ["data2", "data3"],
* url: "...",
* done: function() { ... }
* resizeAfter: false // will not resize after load
* });
* @example
* const chart = bb.generate({
Expand Down Expand Up @@ -193,13 +201,15 @@ export default {
* | --- | --- | --- |
* | ids | String &vert; Array | Target id data to be unloaded. If not given, all data will be unloaded. |
* | done | Fuction | Callback after data is unloaded. |
* | resizeAfter | boolean | Resize after the unload. Default value is `true`. This option won't call `onresize` neither `onresized`. |
* @example
* // Unload data2 and data3
* chart.unload({
* ids: ["data2", "data3"],
* done: function() {
* // called after the unloaded
* }
* },
* resizeAfter: false // will not resize after unload
* });
*/
unload(argsValue): void {
Expand All @@ -222,7 +232,7 @@ export default {
});

$$.cache.remove(ids);
args.done && args.done.call(this);
callDone.call($$, args.done, args.resizeAfter);
});
}
};
26 changes: 22 additions & 4 deletions src/ChartInternal/data/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,27 @@
import {$LEGEND} from "../../config/classes";
import {endall} from "../../module/util";

/**
* Call done callback with resize after transition
* @param {Function} fn Callback function
* @param {boolean} resizeAfter Weather to resize chart after the load
* @private
*/
export function callDone(fn, resizeAfter = true) {
const $$ = this;
const {api} = $$;

if (resizeAfter !== false) {
$$.api.flush(true);
}

fn?.call(api);
}

export default {
load(rawTargets, args): void {
const $$ = this;
const {data} = $$;
const {append} = args;
let targets = rawTargets;

Expand All @@ -27,7 +45,7 @@ export default {
}

// Update/Add data
$$.data.targets.forEach(d => {
data.targets.forEach(d => {
for (let i = 0; i < targets.length; i++) {
if (d.id === targets[i].id) {
d.values = append ?
Expand All @@ -39,11 +57,11 @@ export default {
}
});

$$.data.targets = $$.data.targets.concat(targets); // add remained
data.targets = data.targets.concat(targets); // add remained
}

// Set targets
$$.updateTargets($$.data.targets);
$$.updateTargets(data.targets);

// Redraw with new targets
$$.redraw({
Expand All @@ -55,7 +73,7 @@ export default {
// Update current state chart type and elements list after redraw
$$.updateTypesElements();

args.done?.call($$.api);
callDone.call($$, args.done, args.resizeAfter);
},

loadFromArgs(args): void {
Expand Down
4 changes: 3 additions & 1 deletion src/config/Options/common/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,9 @@ export default {
* @property {boolean} [resize.auto=true] Set chart resize automatically on viewport changes.
* @property {boolean|number} [resize.timer=true] Set resize timer option.
* - **NOTE:**
* - The resize function will be called using: true - `setTimeout()`, false - `requestIdleCallback()`.
* - The resize function will be called using:
* - true: `setTimeout()`
* - false: `requestIdleCallback()`
* - Given number(delay in ms) value, resize function will be triggered using `setTimer()` with given delay.
* @example
* resize: {
Expand Down
2 changes: 1 addition & 1 deletion src/module/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const {setTimeout, clearTimeout} = window;
* @private
*/
export function generateResize(option: boolean|number) {
const fn: any[] = [];
const fn: Function[] = [];
let timeout;

const callResizeFn = function() {
Expand Down
139 changes: 139 additions & 0 deletions test/api/load-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -834,4 +834,143 @@ describe("API load", function() {
});
});
});

describe("Check 'resizeAfter' option", () => {
before(() => {
args = {
data: {
columns: [
["data1", 30, 200, 100, 400, 150]
],
type: "line"
}
};
});

it("should resize correctly after the load", done => {
const {height} = chart.internal.state;

// when
chart.load({
columns: [
["www-abcd-abcd", 100, 200, 150, 300],
["www2-abcd-abcd", 200, 200, 150, 300],
["www3-abcd-abcd", 300, 200, 150, 300],
["www4-abcd-abcd", 400, 200, 150, 300],
["www5-abcd-abcd", 500, 200, 150, 300],
["www6-abcd-abcd", 600, 200, 150, 300],
["www7-abcd-abcd", 700, 200, 150, 300]
],
done: function() {
const lastLegend = this.$.legend.select("g:last-child").node();
const chartNode = this.$.chart.node();

setTimeout(() => {
expect(this.internal.state.height).to.be.below(height);
expect(lastLegend.getBoundingClientRect().bottom).to.be.below(chartNode.getBoundingClientRect().bottom);

done();
}, 300);
}
});
});

it("shouldn't resize after the load", done => {
const {height} = chart.internal.state;

// when
chart.load({
columns: [
["www-abcd-abcd", 100, 200, 150, 300],
["www2-abcd-abcd", 200, 200, 150, 300],
["www3-abcd-abcd", 300, 200, 150, 300],
["www4-abcd-abcd", 400, 200, 150, 300],
["www5-abcd-abcd", 500, 200, 150, 300],
["www6-abcd-abcd", 600, 200, 150, 300],
["www7-abcd-abcd", 700, 200, 150, 300]
],
resizeAfter: false,
done: function() {
const lastLegend = this.$.legend.select("g:last-child").node();
const chartNode = this.$.chart.node();

setTimeout(() => {
expect(this.internal.state.height).to.be.equal(height);
expect(lastLegend.getBoundingClientRect().bottom).to.be.above(chartNode.getBoundingClientRect().bottom);

done();
}, 300);
}
});
});

it("should resize correctly after the unload", done => {
// when
chart.load({
columns: [
["www-abcd-abcd", 100, 200, 150, 300],
["www2-abcd-abcd", 200, 200, 150, 300],
["www3-abcd-abcd", 300, 200, 150, 300],
["www4-abcd-abcd", 400, 200, 150, 300],
["www5-abcd-abcd", 500, 200, 150, 300],
["www6-abcd-abcd", 600, 200, 150, 300],
["www7-abcd-abcd", 700, 200, 150, 300],
],
done: function() {
this.unload({
ids: ["www-abcd-abcd", "www2-abcd-abcd", "www3-abcd-abcd"],
done: function() {
const lastLegend = this.$.legend.select("g:last-child").node();
const chartNode = this.$.chart.node();

setTimeout(() => {
const legendBottom = lastLegend.getBoundingClientRect().bottom;
const chartBottom = chartNode.getBoundingClientRect().bottom;

expect(legendBottom).to.be.closeTo(chartBottom, 10);
expect(legendBottom < chartBottom).to.be.true;

done();
}, 300);
}
})
}
});
});

it("shouldn't resize after the unload", done => {
// when
chart.load({
columns: [
["www-abcd-abcd", 100, 200, 150, 300],
["www2-abcd-abcd", 200, 200, 150, 300],
["www3-abcd-abcd", 300, 200, 150, 300],
["www4-abcd-abcd", 400, 200, 150, 300],
["www5-abcd-abcd", 500, 200, 150, 300],
["www6-abcd-abcd", 600, 200, 150, 300],
["www7-abcd-abcd", 700, 200, 150, 300],
],
done: function() {
this.unload({
ids: ["www-abcd-abcd", "www2-abcd-abcd", "www3-abcd-abcd"],
resizeAfter: false,
done: function() {
const lastLegend = this.$.legend.select("g:last-child").node();
const chartNode = this.$.chart.node();

setTimeout(() => {
const legendBottom = lastLegend.getBoundingClientRect().bottom;
const chartBottom = chartNode.getBoundingClientRect().bottom;

expect(legendBottom).to.not.be.closeTo(chartBottom, 10);
expect(chartBottom - legendBottom > 25).to.be.true;

done();
}, 300);
}
})
}
});
});
});
});
7 changes: 6 additions & 1 deletion types/chart.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ export interface Chart {
types?: { [key: string]: string };
unload?: boolean | ArrayOrString;
done?: (this: Chart) => void;
resizeAfter?: boolean;
}): void;

/**
Expand All @@ -378,7 +379,11 @@ export interface Chart {
* - If you call load API soon after/before unload, unload param of load should be used. Otherwise chart will not be rendered properly because of cancel of animation.
* - done will be called after data loaded, but it's not after rendering. It's because rendering will finish after some transition and there is some time lag between loading and rendering.
*/
unload(this: Chart, targetIds?: TargetIds, done?: (this: Chart) => void): void;
unload(this: Chart, args?: TargetIds | {
ids?: TargetIds,
done?: (this: Chart) => void,
resizeAfter?: boolean;
}): void;

/**
* Flow data to the chart. By this API, you can append new data points to the chart.
Expand Down

0 comments on commit c5122dc

Please sign in to comment.