diff --git a/src/Chart/api/subchart.ts b/src/Chart/api/subchart.ts new file mode 100644 index 000000000..346428ab0 --- /dev/null +++ b/src/Chart/api/subchart.ts @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2017 ~ present NAVER Corp. + * billboard.js project is licensed under the MIT license + */ +import CLASS from "../../config/classes"; + +export default { + subchart: { + /** + * Show subchart + * - **NOTE:** for ESM imports, needs to import 'subchart' exports and instantiate it by calling `subchart()`. + * @function subchart․show + * @instance + * @memberof Chart + * @example + * // for ESM imports, needs to import 'subchart' and must be instantiated first to enable subchart's API. + * import {subchart} from "billboard.js"; + * + * const chart = bb.generate({ + * ... + * subchart: { + * // need to be instantiated by calling 'subchart()' + * enabled: subchart() + * + * // in case don't want subchart to be shown at initialization, instantiate with '!subchart()' + * enabled: !subchart() + * } + * }); + * + * chart.subchart.show(); + */ + show(): void { + const $$ = this.internal; + const {$el: {subchart}, config} = $$; + const show = config.subchart_show; + + if (!show) { + config.subchart_show = !show; + !subchart.main && $$.initSubchart(); + + let $target = subchart.main.selectAll(`.${CLASS.target}`); + + // need to cover when new data has been loaded + if ($$.data.targets.length !== $target.size()) { + $$.updateSizes(); + $$.updateTargetsForSubchart($$.data.targets); + + $target = subchart.main.selectAll(`.${CLASS.target}`); + } + + $target.style("opacity", "1"); + subchart.main.style("display", null); + + this.flush(); + } + }, + + /** + * Hide generated subchart + * - **NOTE:** for ESM imports, needs to import 'subchart' exports and instantiate it by calling `subchart()`. + * @function subchart․hide + * @instance + * @memberof Chart + * @example + * chart.subchart.hide(); + */ + hide(): void { + const $$ = this.internal; + const {$el: {subchart}, config} = $$; + + if (config.subchart_show && subchart.main.style("display") !== "none") { + config.subchart_show = false; + subchart.main.style("display", "none"); + + this.flush(); + } + }, + + /** + * Toggle the visiblity of subchart + * - **NOTE:** for ESM imports, needs to import 'subchart' exports and instantiate it by calling `subchart()`. + * @function subchart․toggle + * @instance + * @memberof Chart + * @example + * // When subchart is hidden, will be shown + * // When subchart is shown, will be hidden + * chart.subchart.toggle(); + */ + toggle(): void { + const $$ = this.internal; + const {config} = $$; + + this.subchart[config.subchart_show ? "hide" : "show"](); + } + } +}; diff --git a/src/config/resolver/interaction.ts b/src/config/resolver/interaction.ts index 5d678afc1..5c1df68f8 100644 --- a/src/config/resolver/interaction.ts +++ b/src/config/resolver/interaction.ts @@ -9,6 +9,7 @@ import {extend} from "../../module/util"; // Chart import apiSelection from "../../Chart/api/selection"; +import apiSubchart from "../../Chart/api/subchart"; import apiZoom from "../../Chart/api/zoom"; // ChartInternal @@ -37,6 +38,7 @@ let selectionModule = (): boolean => { let subchartModule = (): boolean => { extend(ChartInternal.prototype, subchart); + extend(Chart.prototype, apiSubchart); Options.setOptions([optSubchart]); return (subchartModule = () => true)(); diff --git a/test/api/subchart-spec.ts b/test/api/subchart-spec.ts new file mode 100644 index 000000000..73fe85a73 --- /dev/null +++ b/test/api/subchart-spec.ts @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2017 ~ present NAVER Corp. + * billboard.js project is licensed under the MIT license + */ +/* eslint-disable */ +import {expect} from "chai"; +import util from "../assets/util"; + +describe("API subchart", () => { + let chart; + let args; + + beforeEach(() => { + chart = util.generate(args); + }); + + describe(".subchart.show/hide/toggle()", () => { + before(() => { + args = { + data: { + columns: [ + ["data1", 30, 200, 100, 400, 150, 250] + ], + type: "line", + }, + subchart: { + show: false + } + }; + }); + + it("check subchart interactions", () => { + const {subchart} = chart.internal.$el; + + expect(subchart.main).to.be.null; + + // when + chart.subchart.show(); + + expect(subchart.main).to.be.not.null; + + // when + chart.subchart.hide(); + + expect(subchart.main.style("display")).to.be.equal("none"); + + // when + chart.subchart.toggle(); + + expect(subchart.main.style("display")).to.be.not.equal("none"); + + // when + chart.subchart.toggle(); + + expect(subchart.main.style("display")).to.be.equal("none"); + }); + + it("dynamic data load", done => { + // when + chart.subchart.show(); + + const path = chart.internal.$el.subchart.line.attr("d"); + + chart.subchart.hide(); + + chart.load({ + columns: [ + ["data1", 30, 20, 50] + ], + done: function() { + // show subchart again + this.subchart.show(); + + const currentPath = this.internal.$el.subchart.line.attr("d") + + expect(currentPath).to.be.not.equal(path); + expect(currentPath).to.be.equal("M6,38.69444444444444L299,55.083333333333336L593,5.916666666666669"); + done(); + } + }); + }); + }); +}); diff --git a/types/chart.d.ts b/types/chart.d.ts index 0234e5554..dc6ffea60 100644 --- a/types/chart.d.ts +++ b/types/chart.d.ts @@ -250,6 +250,26 @@ export interface Chart { range(range?: { min?: number, max?: number }): { min: number, max: number } }; + subchart: { + /** + * Hide generated subchart + * - **NOTE:** for ESM imports, needs to import 'subchart' exports and instantiate it by calling `subchart()`. + */ + hide(): void; + + /** + * Show subchart + * - **NOTE:** for ESM imports, needs to import 'subchart' exports and instantiate it by calling `subchart()`. + */ + show(): void + + /** + * Hide generated subchart + * - **NOTE:** for ESM imports, needs to import 'subchart' exports and instantiate it by calling `subchart()`. + */ + toggle(): void; + }; + tooltip: { /** * Hide tooltip