diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 325e1f3e1..e57542210 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,6 +25,7 @@ jobs:
run: |
npm config set package-lock false
npm install --save --legacy-peer-deps
+ npx playwright install
- name: Lint
run: npm run lint
diff --git a/.gitignore b/.gitignore
index 3b9a492a3..0f1b46830 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
*.log
*.iml
package-lock.json
+.yarn/*
/*.css
/*.html
/*.js
@@ -12,3 +13,4 @@ package-lock.json
/doc
/monitor
/node_modules
+/test/**/__screenshots__
\ No newline at end of file
diff --git a/.husky/pre-commit b/.husky/pre-commit
index 4e7a998cc..e97b93f72 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -1,3 +1,2 @@
-
npm run format
npm run lint-staged
diff --git a/.yarnrc.yml b/.yarnrc.yml
new file mode 100644
index 000000000..951a379df
--- /dev/null
+++ b/.yarnrc.yml
@@ -0,0 +1,2 @@
+nodeLinker: node-modules
+yarnPath: .yarn/releases/yarn-4.3.1.cjs
diff --git a/README.md b/README.md
index 856b50a0c..cbee7105c 100644
--- a/README.md
+++ b/README.md
@@ -74,8 +74,9 @@ You can download the compressed files for production
- https://naver.github.io/billboard.js/release/[VERSION]/dist/billboard.min.css
### Packaged version (with D3.js inclusion)
-> Packaged version is not an official distribution.
-> It's to provide an easy way to load 'billboard.js' with dependency.
+
+> ⚠️ Packaged version is not an official distribution.
+> It's to provide an easy way to load 'billboard.js' with dependencies.
- **Latest**
- https://naver.github.io/billboard.js/release/latest/dist/billboard.pkgd.js
@@ -87,6 +88,7 @@ You can download the compressed files for production
### Themes
+> [!NOTE]
> If you want apply themes, simply load one of the theme css file provided instead of the default css file.
> - [Screenshot Preview](https://github.com/naver/billboard.js/wiki/Themes)
@@ -123,6 +125,7 @@ With nightly, you can try upcoming changes prior the official release.
- https://github.com/naver/billboard.js/tree/nightly/dist
+> [!NOTE]
> The version info will be given as the build datetime: `x.x.x-nightly-yyyymmddhhmmss`
There're two ways to install from `nightly` branch directly.
@@ -170,6 +173,7 @@ If you want to use 'billboard.js' without installation, load files directly from
## Supported Browsers
+> [!IMPORTANT]
> - Basically will work on all SVG and ES6+ supported browsers.
> - *Notes for legacy browsers:
> - Recommended to use `packaged` build or construct your own build following [`How to bundle for legacy browsers?`](https://github.com/naver/billboard.js/wiki/How-to-bundle-for-legacy-browsers%3F) instruction.
@@ -184,6 +188,8 @@ If you want to use 'billboard.js' without installation, load files directly from
4.x ~ 5.x | 1.x ~ 2.x
6.x+ | 3.x+
+# Getting Started
+
Load billboard.js after D3.js.
```html
@@ -201,6 +207,7 @@ Load billboard.js after D3.js.
```
or use importing ESM.
+> [!TIP]
> 📌 Also check: [How to load as ESM directly from the browser?](https://github.com/naver/billboard.js/wiki/How-to-load-as-ESM-directly-from-the-browser%3F)
```js
@@ -219,7 +226,7 @@ import "billboard.js/dist/billboard.css";
import "billboard.js/dist/theme/insight.css"
```
-> **Note**
+> [!NOTE]
> - For migration from C3.js, checkout the [migration guide](https://github.com/naver/billboard.js/wiki/How-to-migrate-from-C3.js%3F).
> - If has an issue bundling for legacy browsers, checkout "[How to bundle for legacy browsers?](https://github.com/naver/billboard.js/wiki/How-to-bundle-for-legacy-browsers%3F)".
diff --git a/demo/demo.js b/demo/demo.js
index 09908c346..b701bfbdc 100644
--- a/demo/demo.js
+++ b/demo/demo.js
@@ -3517,28 +3517,51 @@ d3.select(".chart_area")
}
}
},
- LegendFormat: {
- description: "Stay hovering on each of legend items to see full data name text.",
- options: {
- data: {
- columns: [
- ["SELECT idx, title, date, count from TEST_TABLE WHERE idx=5", 2, 3, 5],
- ["very long long data name needed to be", 1, 2, 2],
- ],
- type: "line"
- },
- legend: {
- format: function(id) {
- if (id.length > 5) {
- id = id.substr(0, 5) + "...";
- }
-
- return id;
+ LegendFormat: [
+ {
+ description: "Stay hovering on each of legend items to see full data name text.",
+ options: {
+ data: {
+ columns: [
+ ["SELECT idx, title, date, count from TEST_TABLE WHERE idx=5", 2, 3, 5],
+ ["very long long data name needed to be", 1, 2, 2],
+ ],
+ type: "line"
},
- tooltip: true
+ legend: {
+ format: function(id) {
+ if (id.length > 5) {
+ id = id.substr(0, 5) + "...";
+ }
+
+ return id;
+ },
+ tooltip: true
+ }
+ }
+ },
+ {
+ options: {
+ data: {
+ names: {
+ "data1": "Detailed Name",
+ "data2": "Name Detailed"
+ },
+ columns: [
+ ["data1", 71.4],
+ ["data2", 10],
+ ],
+ type: "gauge"
+ },
+ legend: {
+ format: function(id, dataId) {
+ return id === "Name Detailed" ? dataId : id;
+ },
+ tooltip: true
+ }
}
}
- },
+ ],
LegendItemInteraction: [
{
description: "Single click + AltKey(Win)/optionKey(Mac)
or Double click legend item to show/hide data series",
diff --git a/karma.conf.cjs b/karma.conf.cjs
deleted file mode 100644
index 4a4c4686d..000000000
--- a/karma.conf.cjs
+++ /dev/null
@@ -1,158 +0,0 @@
-/* eslint-disable */
-// @ts-nocheck
-// import {webpack} from "webpack";
-// import {platform} from "os";
-
-const webpack = require("webpack");
-const isWin = require("os").platform() === "win32";
-
-// file extension to be tested
-const fileExtensions = /(\.[jt]s)$/;
-
-module.exports = function(config) {
- const {TEST_TYPE: type = false} = process.env;
- const isCoverage = type === "coverage";
- const isChrome = type === "chrome";
-
- const karmaConfig = {
- frameworks: ["mocha", "chai", "sinon", "webpack"],
- files: [
- "./node_modules/lite-fixture/index.js",
- "./node_modules/hammer-simulator/index.js",
- "./test/assets/hammer-simulator.run.js",
- "./src/scss/billboard.scss",
- "./test/assets/common.css",
- "./test/**/*-spec.ts",
- {
- pattern: "./test/assets/data/*",
- watched: false,
- included: false,
- served: true
- }
- ],
-
- client: {
- mocha: {
- opts: "./mocha.opts"
- }
- },
-
- webpack: {
- devtool: "cheap-module-source-map",
- mode: "development",
- stats: "none",
- resolve: {
- extensions: [".ts", ".js"]
- },
- target: ["web", "es5"],
- module: {
- rules: [
- {
- test: require.resolve("./src/module/browser.ts"),
- loader: "exports-loader",
- options: {
- type: "module",
- exports: ["getGlobal", "getFallback"]
- }
- },
- {
- test: require.resolve("./src/module/worker.ts"),
- loader: "exports-loader",
- options: {
- type: "module",
- exports: "getWorker"
- }
- },
- {
- test: /\.[jt]s$/,
- loader: "esbuild-loader",
- options: {
- target: "es2015"
- },
- exclude: {
- and: [/node_modules/],
- not: [/(d3\-.*)$/, /internmap/]
- }
- }
- ]
- },
- optimization: {
- usedExports: true
- },
- plugins: isWin ? [
- new webpack.NormalModuleReplacementPlugin(
- /module\/util/i, function(resource) {
- resource.request = resource.request.replace("module/util", "../test/assets/module/util");
- }
- ),
- new webpack.NormalModuleReplacementPlugin(
- /fake/i, function(resource) {
- if (/test\\assets\\module/i.test(resource.context)) {
- resource.request = "../../../src/module/util";
- }
- }
- )
- ] : [
- new webpack.NormalModuleReplacementPlugin(
- /module\/util\.ts/i, "../../test/assets/module/util.ts"
- ),
- new webpack.NormalModuleReplacementPlugin(
- /fake\.ts/i, "../../../src/module/util.ts"
- )
- ]
- },
-
- // preprocess matching files before serving them to the browser
- // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
- preprocessors: {
- "./src/scss/billboard.scss": ["scss"],
- "./test/**/*-spec.ts": isCoverage ? ["webpack"] : ["webpack", "sourcemap"],
- },
-
- scssPreprocessor: {
- options: {
- sourceMap: true,
- outputStyle: "expanded",
- }
- },
-
- // start these browsers
- // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
- browsers: [],
-
- reporters: ["mocha"],
- colors: true,
- webpackMiddleware: {
- logLevel: "error"
- },
-
- // https://github.com/karma-runner/karma/blob/master/docs/config/01-configuration-file.md#browsernoactivitytimeout
- browserNoActivityTimeout: 50000
- };
-
- karmaConfig.browsers.push(isChrome ? "Chrome" : "ChromeHeadless");
-
- if (isCoverage) {
- karmaConfig.reporters.push("coverage-istanbul");
-
- karmaConfig.coverageIstanbulReporter = {
- reports: ["text-summary", "html", "lcovonly"],
- dir: "./coverage"
- };
-
- karmaConfig.webpack.module.rules.unshift({
- test: fileExtensions,
- exclude: /(node_modules|test)/,
- use: {
- loader: "istanbul-instrumenter-loader",
- options: {
- esModules: true
- }
- }
- });
-
- karmaConfig.singleRun = true;
- }
-
- config.set(karmaConfig);
-};
diff --git a/package.json b/package.json
index 9c61c4556..741e951e3 100644
--- a/package.json
+++ b/package.json
@@ -39,9 +39,8 @@
"lint": "eslint",
"format": "dprint fmt",
"loc": "cloc --by-file src",
- "test": "node --max-old-space-size=3072 ./node_modules/karma/bin/karma start ./karma.conf.cjs",
- "test:chrome": "cross-env TEST_TYPE=chrome npm test",
- "coverage": "cross-env TEST_TYPE=coverage npm test",
+ "test": "vitest",
+ "coverage": "vitest run",
"coveralls": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
"jsdoc": "node ./config/jsdoc.js",
"jsdoc:cmd": "jsdoc -c jsdoc.json",
@@ -104,7 +103,7 @@
"devDependencies": {
"@commitlint/cli": "19.3.0",
"@commitlint/config-conventional": "^19.2.2",
- "@eslint/js": "^9.7.0",
+ "@eslint/js": "^9.8.0",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-replace": "^5.0.7",
"@rollup/plugin-typescript": "^11.1.6",
@@ -114,12 +113,14 @@
"@semantic-release/git": "^10.0.1",
"@semantic-release/npm": "^12.0.1",
"@semantic-release/release-notes-generator": "^14.0.1",
- "@types/chai": "^4.3.16",
+ "@testing-library/dom": "^10.4.0",
+ "@testing-library/react": "^16.0.0",
"@types/d3": "^7.4.3",
- "@types/mocha": "^10.0.7",
"@types/sinon": "^17.0.3",
+ "@vitest/browser": "^2.0.5",
+ "@vitest/coverage-istanbul": "^2.0.5",
+ "@vitest/ui": "^2.0.5",
"better-docs": "^2.7.3",
- "chai": "^4.3.10",
"clean-webpack-plugin": "^4.0.0",
"cloc": "^2.11.0",
"core-js": "^3.37.1",
@@ -133,44 +134,28 @@
"docdash": "^2.0.2",
"dprint": "^0.47.2",
"esbuild-loader": "^4.2.2",
- "eslint": "^9.7.0",
- "eslint-config-naver": "^2.1.0",
+ "eslint": "^9.8.0",
"eslint-plugin-import": "^2.29.1",
- "eslint-plugin-jsdoc": "^48.8.3",
- "exports-loader": "^5.0.0",
- "hammer-simulator": "0.0.1",
- "husky": "^9.1.1",
- "istanbul-instrumenter-loader": "^3.0.1",
- "istanbul-lib-instrument": "^6.0.3",
+ "eslint-plugin-jsdoc": "^48.9.2",
+ "husky": "^9.1.4",
"jsdoc": "^4.0.3",
- "karma": "^6.4.3",
- "karma-chai": "^0.1.0",
- "karma-chrome-launcher": "^3.2.0",
- "karma-coverage-istanbul-reporter": "^3.0.3",
- "karma-mocha": "^2.0.1",
- "karma-mocha-reporter": "^2.2.3",
- "karma-scss-preprocessor": "^4.0.0",
- "karma-sinon": "^1.0.5",
- "karma-sourcemap-loader": "^0.4.0",
- "karma-webpack": "^5.0.1",
"lint-staged": "^15.2.7",
- "lite-fixture": "^1.0.2",
"mini-css-extract-plugin": "^2.9.0",
- "mocha": "^10.7.0",
- "node-sass": "^9.0.0",
+ "playwright": "^1.46.0",
"regenerator-runtime": "^0.14.1",
- "rollup": "^4.19.0",
+ "rollup": "^4.19.1",
"rollup-plugin-delete": "^2.0.0",
- "sass-loader": "^14.2.1",
+ "sass": "^1.77.8",
+ "sass-loader": "^16.0.0",
"semantic-release": "^24.0.0",
"simulant": "^0.2.2",
"sinon": "^18.0.0",
"string-replace-loader": "^3.1.0",
"style-loader": "^4.0.0",
- "taffydb": "^2.7.3",
"tslib": "^2.6.3",
"typescript": "5.5.4",
- "typescript-eslint": "^7.17.0",
+ "typescript-eslint": "^7.18.0",
+ "vitest": "^2.0.5",
"webpack": "^5.93.0",
"webpack-bundle-analyzer": "^4.10.2",
"webpack-clean": "^1.2.5",
@@ -180,5 +165,6 @@
"webpack-merge": "^6.0.1",
"webpackbar": "^6.0.1",
"write-file-webpack-plugin": "^4.5.1"
- }
+ },
+ "packageManager": "yarn@4.3.1"
}
diff --git a/src/Chart/api/show.ts b/src/Chart/api/show.ts
index a10765d4e..2b2f9625c 100644
--- a/src/Chart/api/show.ts
+++ b/src/Chart/api/show.ts
@@ -36,7 +36,7 @@ function showHide(show: boolean, targetIdsValue: string[], options: any): void {
// https://github.com/naver/billboard.js/issues/1758
if (!show && hiddenIds.length === 0) {
targets.style("display", "none");
- callFn($$.config.data_onhidden, this, targetIds);
+ callFn($$.config?.data_onhidden, this, targetIds);
}
targets.style("opacity", opacity);
diff --git a/src/ChartInternal/interactions/zoom.ts b/src/ChartInternal/interactions/zoom.ts
index 3e5f5c006..722084974 100644
--- a/src/ChartInternal/interactions/zoom.ts
+++ b/src/ChartInternal/interactions/zoom.ts
@@ -9,6 +9,7 @@ import {
zoomTransform as d3ZoomTransform
} from "d3-zoom";
import {$COMMON, $ZOOM} from "../../config/classes";
+import {window} from "../../module/browser";
import {callFn, diffDomain, getPointer, isFunction} from "../../module/util";
export default {
@@ -329,9 +330,19 @@ export default {
*/
bindZoomOnEventRect(): void {
const $$ = this;
- const {config, $el: {eventRect}} = $$;
+ const {config, $el: {eventRect, svg}} = $$;
const behaviour = config.zoom_type === "drag" ? $$.zoomBehaviour : $$.zoom;
+ // On Safari, event can't be built inside the svg content
+ // for workaround, register wheel event on