diff --git a/demo/demo.js b/demo/demo.js
index 60bec6c96..85fd3b156 100644
--- a/demo/demo.js
+++ b/demo/demo.js
@@ -4396,36 +4396,80 @@ d3.select(".chart_area")
},
Region: {
- Region: {
- options: {
- data: {
- columns: [
- ["data1", 30, 200, 100, 400, 150, 250, 400],
- ["data2", 830, 1200, 1100, 1400, 1150, 1250, 1500]
- ],
- type: "line",
- axes: {
- data2: "y2"
- }
- },
- axis: {
- y2: {
- show: true
- }
- },
- regions: [
- {axis: "x", end: 1, class: "regionX"},
- {axis: "x", start: 2, end: 4, class: "regionX"},
- {axis: "x", start: 5, class: "regionX"},
- {axis: "y", end: 50, class: "regionY"},
- {axis: "y", start: 80, end: 140, class: "regionY"},
- {axis: "y", start: 400, class: "regionY"},
- {axis: "y2", end: 900, class: "regionY2"},
- {axis: "y2", start: 1150, end: 1250, class: "regionY2"},
- {axis: "y2", start: 1300, class: "regionY2"}
- ]
+ Region: [
+ {
+ options: {
+ data: {
+ columns: [
+ ["data1", 30, 200, 100, 400, 150, 250, 400],
+ ["data2", 830, 1200, 1100, 1400, 1150, 1250, 1500]
+ ],
+ type: "line",
+ axes: {
+ data2: "y2"
+ }
+ },
+ axis: {
+ y2: {
+ show: true
+ }
+ },
+ regions: [
+ {axis: "x", end: 1, class: "regionX"},
+ {axis: "x", start: 2, end: 4, class: "regionX"},
+ {axis: "x", start: 5, class: "regionX"},
+ {axis: "y", end: 50, class: "regionY"},
+ {axis: "y", start: 80, end: 140, class: "regionY"},
+ {axis: "y", start: 400, class: "regionY"},
+ {axis: "y2", end: 900, class: "regionY2"},
+ {axis: "y2", start: 1150, end: 1250, class: "regionY2"},
+ {axis: "y2", start: 1300, class: "regionY2"}
+ ]
+ }
+ },
+ {
+ options: {
+ data: {
+ columns: [
+ ["data1", 30, 200, 100, 400, 150, 250],
+ ["data2", 100, 150, 130, 200, 220, 190]
+ ],
+ axes: {
+ data2: "y2"
+ },
+ type: "line",
+ colors: {
+ data1: "#ff0000"
+ }
+ },
+ axis: {
+ x: {
+ type: "category",
+ categories: [
+ "cat1",
+ "cat2",
+ "cat3",
+ "cat4",
+ "cat5",
+ "cat6"
+ ]
+ }
+ },
+ regions: [
+ {
+ axis: "x",
+ start: "cat2",
+ end: "cat3"
+ },
+ {
+ axis: "x",
+ start: "cat5",
+ end: 5
+ }
+ ]
+ }
}
- },
+ ],
RegionLabel: {
options: {
data: {
diff --git a/src/Chart/api/regions.ts b/src/Chart/api/regions.ts
index 8e80d35f9..ddaeb1b8e 100644
--- a/src/Chart/api/regions.ts
+++ b/src/Chart/api/regions.ts
@@ -36,7 +36,7 @@ function regionsFn(regions: RegionsParam, isAdd = false): RegionsParam {
* @function regions
* @instance
* @memberof Chart
- * @param {Array} regions Regions will be replaced with this argument. The format of this argument is the same as regions.
+ * @param {Array} regions Regions will be replaced with this argument. The format of this argument is the same as [regions](./Options.html#.regions) option.
* @returns {Array} regions
* @example
* // Show 2 regions
@@ -65,7 +65,7 @@ extend(regions, {
* @function regions․add
* @instance
* @memberof Chart
- * @param {Array|object} regions New region will be added. The format of this argument is the same as regions and it's possible to give an Object if only one region will be added.
+ * @param {Array|object} regions New region will be added. The format of this argument is the same as [regions](./Options.html#.regions) and it's possible to give an Object if only one region will be added.
* @returns {Array} regions
* @example
* // Add a new region
diff --git a/src/ChartInternal/internals/region.ts b/src/ChartInternal/internals/region.ts
index bc7098258..96f5f189c 100644
--- a/src/ChartInternal/internals/region.ts
+++ b/src/ChartInternal/internals/region.ts
@@ -3,7 +3,8 @@
* billboard.js project is licensed under the MIT license
*/
import {select as d3Select} from "d3-selection"; // selection
-import type {AxisType, RegionsType} from "../../../types/types";
+import type {RegionOptions} from "../../../types/options";
+import type {AxisType} from "../../../types/types";
import {$REGION} from "../../config/classes";
import {isValue, parseDate} from "../../module/util";
@@ -98,76 +99,73 @@ export default {
];
},
- getRegionXY(type: AxisType, d: RegionsType): number {
- const $$ = this;
- const {config, scale} = $$;
- const isRotated = config.axis_rotated;
- const isX = type === "x";
- let key = "start";
- let currScale;
- let pos = 0;
-
- if (d.axis === "y" || d.axis === "y2") {
- if (!isX) {
- key = "end";
- }
-
- if ((isX ? isRotated : !isRotated) && key in d) {
- currScale = scale[d.axis];
- pos = currScale(d[key]);
- }
- } else if ((isX ? !isRotated : isRotated) && key in d) {
- currScale = scale.zoom || scale.x;
- pos = currScale($$.axis.isTimeSeries() ? parseDate.call($$, d[key]) : d[key]);
- }
+ regionX(d: RegionOptions): number {
+ return this.getRegionSize("x", d);
+ },
- return pos;
+ regionY(d: RegionOptions): number {
+ return this.getRegionSize("y", d);
},
- regionX(d: RegionsType): number {
- return this.getRegionXY("x", d);
+ regionWidth(d: RegionOptions): number {
+ return this.getRegionSize("width", d);
},
- regionY(d: RegionsType): number {
- return this.getRegionXY("y", d);
+ regionHeight(d: RegionOptions): number {
+ return this.getRegionSize("height", d);
},
- getRegionSize(type: "width" | "height", d: RegionsType): number {
+ /**
+ * Get Region size according start/end position
+ * @param {string} type Type string
+ * @param {ojbect} d Data object
+ * @returns {number}
+ * @private
+ */
+ getRegionSize(type: AxisType | "width" | "height", d: RegionOptions): number {
const $$ = this;
const {config, scale, state} = $$;
const isRotated = config.axis_rotated;
- const isWidth = type === "width";
- const start = $$[isWidth ? "regionX" : "regionY"](d);
+ const isAxisType = /(x|y|y2)/.test(type);
+
+ const isType = isAxisType ? type === "x" : type === "width";
+ const start = !isAxisType && $$[isType ? "regionX" : "regionY"](d);
+ let key = isAxisType ? "start" : "end";
+ let pos = isAxisType ? 0 : state[type];
let currScale;
- let key = "end";
- let end = state[type];
if (d.axis === "y" || d.axis === "y2") {
- if (!isWidth) {
+ if (!isAxisType && !isType) {
key = "start";
+ } else if (isAxisType && !isType) {
+ key = "end";
}
- if ((isWidth ? isRotated : !isRotated) && key in d) {
+ if ((isType ? isRotated : !isRotated) && key in d) {
currScale = scale[d.axis];
- end = currScale(d[key]);
}
- } else if ((isWidth ? !isRotated : isRotated) && key in d) {
+ } else if ((isType ? !isRotated : isRotated) && key in d) {
currScale = scale.zoom || scale.x;
- end = currScale($$.axis.isTimeSeries() ? parseDate.call($$, d[key]) : d[key]);
}
- return end < start ? 0 : end - start;
- },
+ if (currScale) {
+ let offset = 0;
+ pos = d[key];
- regionWidth(d: RegionsType): number {
- return this.getRegionSize("width", d);
- },
+ if ($$.axis.isTimeSeries(d.axis)) {
+ pos = parseDate.call($$, pos);
+ } else if (/(x|width)/.test(type) && $$.axis.isCategorized() && isNaN(pos)) {
+ pos = config.axis_x_categories.indexOf(pos);
+ offset = $$.axis.x.tickOffset() * (key === "start" ? -1 : 1);
+ }
- regionHeight(d: RegionsType): number {
- return this.getRegionSize("height", d);
+ pos = currScale(pos) + offset;
+ }
+
+ return isAxisType ? pos : pos < start ? 0 : pos - start;
},
- isRegionOnX(d: RegionsType): boolean {
+ isRegionOnX(d: RegionOptions): boolean {
return !d.axis || d.axis === "x";
}
};
diff --git a/src/config/Options/common/main.ts b/src/config/Options/common/main.ts
index b08ea3cea..7b38a6196 100644
--- a/src/config/Options/common/main.ts
+++ b/src/config/Options/common/main.ts
@@ -2,7 +2,7 @@
* Copyright (c) 2017 ~ present NAVER Corp.
* billboard.js project is licensed under the MIT license
*/
-import type {RegionsType} from "../../../../types/types";
+import type {RegionOptions} from "../../../../types/options";
/**
* main config options
@@ -399,17 +399,31 @@ export default {
/**
* Show rectangles inside the chart.
- * This option accepts array including object that has axis, start, end and class.
- * The keys start, end and class are optional.
- * axis must be x, y or y2. start and end should be the value where regions start and end.
- * If not specified, the edge values will be used.
- * If timeseries x axis, date string, Date object and unixtime integer can be used.
- * If class is set, the region element will have it as class.
+ * - **NOTE:**
+ * - axis must be x, y or y2. start and end should be the value where regions start and end.
+ * - If not specified, the edge values will be used.
+ * - If timeseries x axis, date string, Date object and unixtime integer can be used.
+ * - If category x axis, category name can be used for start and end.
+ * - If class is set, the region element will have it as class.
+ *
+ * This option accept array of object with below values:
+ * - `axis {string}`: 'x', 'y', or 'y2'
+ * - `[start] {number|Date|string}`: Start position of the region. If not set, the start will be the edge of the chart.
+ * - `[end] {number|Date|string}`: End position of the region. If not set, the end will be the edge of the chart.
+ * - `[class] {string}`: Class value to apply to the region.
+ * - `[label] {object}` Lable text option.
+ * - `text {string}`: Text value.
+ * - `x {number}`: x Position.
+ * - `y {number}`: y Position.
+ * - `color {string}`: Color string.
+ * - `rotated (boolean)`: Whether rotate label or not.
* @name regions
* @memberof Options
* @type {Array}
* @default []
- * @see [Demo](https://naver.github.io/billboard.js/demo/#Region.RegionLabel)
+ * @see [Demo: Regions](https://naver.github.io/billboard.js/demo/#Region.Region)
+ * @see [Demo: Regions Timeseries](https://naver.github.io/billboard.js/demo/#Region.RegionWithTimeseries)
+ * @see [Demo: Regions Label](https://naver.github.io/billboard.js/demo/#Region.RegionLabel)
* @example
* regions: [
* {
@@ -427,5 +441,5 @@ export default {
* }
* ]
*/
- regions: []
+ regions: []
};
diff --git a/test/internals/regions-spec.ts b/test/internals/regions-spec.ts
index b28b4b3ac..51a27180a 100644
--- a/test/internals/regions-spec.ts
+++ b/test/internals/regions-spec.ts
@@ -305,4 +305,113 @@ describe("REGIONS", function() {
expect(hasOverflow).to.be.false;
});
});
+
+ describe("category type", () => {
+ beforeAll(() => {
+ args = {
+ data: {
+ columns: [
+ ["data1", 30, 200, 100, 400, 150, 250],
+ ["data2", 100, 150, 130, 200, 220, 190]
+ ],
+ axes: {
+ data2: "y2"
+ },
+ type: "line",
+ colors: {
+ data1: "#ff0000"
+ }
+ },
+ axis: {
+ x: {
+ type: "category",
+ categories: [
+ "cat1",
+ "cat2",
+ "cat3",
+ "cat4",
+ "cat5",
+ "cat6"
+ ]
+ },
+ y2: {
+ show: true
+ }
+ },
+ regions: [
+ {
+ axis: "x",
+ start: "cat1",
+ end: "cat2",
+ class: "regions_class1",
+ label: {
+ text: "Regions 1",
+ color: "red"
+ }
+ },
+ {
+ axis: "x",
+ start: "cat4",
+ end: "cat4",
+ class: "regions_class4",
+ label: {
+ text: "Regions 4",
+ color: "blue"
+ }
+ }
+ ]
+ };
+ });
+
+ it("should render regions correctly", () => {
+ const {region: {list}} = chart.internal.$el;
+ const {x} = chart.internal.scale;
+ const expected = [
+ {start: -0.5, end: 1.5},
+ {start: 2.5, end: 3.5}
+ ];
+
+ list.select("rect").each(function(d, i) {
+ const {start, end} = expected[i];
+ const xPos = +this.getAttribute("x");
+ const width = +this.getAttribute("width");
+
+ expect(x(start)).to.be.equal(xPos);
+ expect(x(end)).to.be.equal(xPos + width);
+ });
+ });
+
+ it("set options: regions", () => {
+ args.regions = [
+ {
+ axis: "x",
+ start: 1,
+ end: "cat3"
+ },
+ {
+ axis: "x",
+ start: "cat5",
+ end: 4.5
+ }
+ ];
+ });
+
+ it("should render regions correctly", () => {
+ const {region: {list}} = chart.internal.$el;
+ const {x} = chart.internal.scale;
+ const expected = [
+ {start: 1, end: 2.5},
+ {start: 3.5, end: 4.5}
+ ];
+
+ list.select("rect").each(function(d, i) {
+ const {start, end} = expected[i];
+ const xPos = +this.getAttribute("x");
+ const width = +this.getAttribute("width");
+
+ expect(x(start)).to.be.closeTo(xPos, 0.1);
+ expect(x(end)).to.be.closeTo(xPos + width, 0.1);
+ });
+ });
+ });
});
diff --git a/types/types.d.ts b/types/types.d.ts
index 191cc37e0..802d9e6a7 100644
--- a/types/types.d.ts
+++ b/types/types.d.ts
@@ -66,17 +66,3 @@ export interface DataRegionsType {
}
};
}
-
-export interface RegionsType {
- axis?: "x" | "y" | "y2";
- start?: number;
- end?: number;
- class?: string;
- label?: {
- text: string;
- x?: number;
- y?: number;
- color?: string;
- rotated?: boolean;
- };
-}