├── .clean-publish
├── .commitlintrc.json
├── .czrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .github
├── ISSUE_TEMPLATE
│ ├── bug-report.yml
│ ├── config.yml
│ └── feature-request.yml
├── renovate.json
└── workflows
│ ├── checks.yml
│ ├── ci.yml
│ ├── commit.yml
│ ├── release.yml
│ └── website.yml
├── .gitignore
├── .nano-staged.json
├── .npmrc
├── .prettierrc
├── .simple-git-hooks.json
├── .size-limit.json
├── .storybook
├── main.js
├── manager.js
├── package.json
├── preview.js
└── theme.js
├── .tool-versions
├── CHANGELOG.md
├── LICENSE
├── LICENSE.md
├── README.md
├── package.json
├── pnpm-lock.yaml
├── rollup.config.js
├── sandboxes
├── bar
│ ├── grouped
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
│ ├── horizontal
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
│ ├── stacked
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
│ └── vertical
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
├── bubble
│ └── default
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
├── chart
│ ├── canvas
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
│ ├── events
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
│ ├── multitype
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
│ └── ref
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
├── doughnut
│ └── default
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
├── line
│ ├── area
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
│ ├── default
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
│ └── multiaxis
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
├── pie
│ └── default
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
├── polarArea
│ └── default
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
├── radar
│ └── default
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
├── scatter
│ └── default
│ │ ├── App.tsx
│ │ ├── index.html
│ │ ├── index.tsx
│ │ ├── package.json
│ │ └── vite.config.js
└── tsconfig.json
├── src
├── chart.tsx
├── index.ts
├── typedCharts.tsx
├── types.ts
└── utils.ts
├── stories
├── Bar.stories.tsx
├── Bubble.stories.tsx
├── Chart.data.ts
├── Chart.stories.tsx
├── Doughnut.stories.tsx
├── Line.stories.tsx
├── Pie.stories.tsx
├── PolarArea.stories.tsx
├── Radar.stories.tsx
├── Scatter.stories.tsx
└── data.ts
├── test
├── .eslintrc.json
├── chart.test-d.tsx
├── chart.test.tsx
└── setup.ts
├── tsconfig.json
├── vite.config.js
└── website
├── .gitignore
├── CNAME
├── README.md
├── babel.config.js
├── docs
├── chartjs-v2.mdx
├── chartjs-v3.mdx
├── components
│ ├── bar.mdx
│ ├── bubble.mdx
│ ├── chart.mdx
│ ├── docs.js
│ ├── doughnut.mdx
│ ├── index.mdx
│ ├── line.mdx
│ ├── pie.mdx
│ ├── polar-area.mdx
│ ├── radar.mdx
│ └── scatter.mdx
├── docs.js
├── examples
│ ├── area-chart.mdx
│ ├── bubble-chart.mdx
│ ├── chart-events.mdx
│ ├── chart-ref.mdx
│ ├── docs.js
│ ├── doughnut-chart.mdx
│ ├── gradient-chart.mdx
│ ├── grouped-bar-chart.mdx
│ ├── horizontal-bar-chart.mdx
│ ├── index.mdx
│ ├── line-chart.mdx
│ ├── multiaxis-line-chart.mdx
│ ├── multitype-chart.mdx
│ ├── pie-chart.mdx
│ ├── polar-area-chart.mdx
│ ├── radar-chart.mdx
│ ├── scatter-chart.mdx
│ ├── stacked-bar-chart.mdx
│ └── vertical-bar-chart.mdx
├── faq
│ ├── canvas-context.md
│ ├── chartjs-instance.md
│ ├── docs.js
│ ├── esm-only.md
│ ├── fill-property.md
│ ├── index.mdx
│ ├── maintain-aspect-ratio.md
│ ├── registered-element.md
│ ├── registered-scale.md
│ ├── typescript.md
│ └── why-two.md
├── index.mdx
├── migration-to-v4.md
├── migration-to-v5.md
├── working-with-datasets.md
└── working-with-events.md
├── docusaurus.config.js
├── package.json
├── pnpm-lock.yaml
├── sidebars.js
├── src
├── components
│ ├── ContextProvider.tsx
│ └── PropsTable.tsx
└── css
│ └── custom.css
├── static
├── .nojekyll
└── img
│ ├── favicon.ico
│ └── logo.svg
└── tsconfig.json
/.clean-publish:
--------------------------------------------------------------------------------
1 | {
2 | "withoutPublish": true,
3 | "tempDir": "package",
4 | "fields": ["tsd"],
5 | "files": ["website"]
6 | }
7 |
--------------------------------------------------------------------------------
/.commitlintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["@commitlint/config-conventional"],
3 | "rules": {
4 | "body-max-line-length": [0]
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.czrc:
--------------------------------------------------------------------------------
1 | {
2 | "path": "@commitlint/cz-commitlint"
3 | }
4 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs
2 | # editorconfig.org
3 | root = true
4 |
5 | [*]
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 | indent_style = space
11 | indent_size = 2
12 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | .publish/*
2 | dist/*
3 | lib/*
4 | node_modules/*
5 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "plugins": ["@typescript-eslint", "prettier"],
4 | "extends": [
5 | "standard",
6 | "standard-react",
7 | "plugin:@typescript-eslint/recommended",
8 | "plugin:prettier/recommended"
9 | ],
10 | "env": {
11 | "browser": true
12 | },
13 | "parserOptions": {
14 | "ecmaVersion": "latest",
15 | "ecmaFeatures": {
16 | "jsx": true
17 | }
18 | },
19 | "settings": {
20 | "react": {
21 | "version": "detect"
22 | }
23 | },
24 | "rules": {
25 | "curly": [2, "multi-line"],
26 | "quotes": [2, "single", "avoid-escape"],
27 | "react/display-name": 0,
28 | "react/jsx-boolean-value": 1,
29 | "jsx-quotes": [1, "prefer-single"],
30 | "react/jsx-no-undef": 1,
31 | "react/jsx-sort-props": 0,
32 | "react/jsx-uses-react": 1,
33 | "react/jsx-uses-vars": 1,
34 | "react/no-did-mount-set-state": 1,
35 | "react/no-did-update-set-state": 1,
36 | "react/no-unknown-property": 1,
37 | "react/prop-types": 0,
38 | "react/react-in-jsx-scope": 1,
39 | "react/self-closing-comp": 1,
40 | "semi": [2, "always"],
41 | "strict": 0,
42 | "no-use-before-define": 0,
43 | "@typescript-eslint/no-use-before-define": 2,
44 | "@typescript-eslint/consistent-type-imports": "error",
45 | "@typescript-eslint/no-import-type-side-effects": "error"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report.yml:
--------------------------------------------------------------------------------
1 | name: "🐛 Bug Report"
2 | description: "If something isn't working as expected."
3 | title: "[Bug]: "
4 | labels: ["bug"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: Thanks for taking the time to file a bug report! Please fill out this form as completely as possible.
9 |
10 | - type: markdown
11 | attributes:
12 | value: ⚠️ react-chartjs-2 is just the wrapper around Chart.js, so if you are experiencing an issue with charts rendering, please create a related issue in [Chart.js repository](https://github.com/chartjs/Chart.js/issues).
13 |
14 | - type: checkboxes
15 | id: input1
16 | attributes:
17 | label: Would you like to work on a fix?
18 | options:
19 | - label: Check this if you would like to implement a PR, we are more than happy to help you go through the process.
20 |
21 | - type: textarea
22 | attributes:
23 | label: Current and expected behavior
24 | description: A clear and concise description of what the library is doing and what you would expect.
25 | validations:
26 | required: true
27 |
28 | - type: input
29 | attributes:
30 | label: Reproduction
31 | description: |
32 | Please provide issue reproduction.
33 | You can give a link to a repository with the reproduction or make a fork of [this sandbox](https://codesandbox.io/s/github/reactchartjs/react-chartjs-2/tree/master/sandboxes/chart/ref) and reproduce the issue there.
34 | validations:
35 | required: true
36 |
37 | - type: input
38 | attributes:
39 | label: chart.js version
40 | description: Which version of `chart.js` are you using?
41 | placeholder: v0.0.0
42 | validations:
43 | required: true
44 |
45 | - type: input
46 | attributes:
47 | label: react-chartjs-2 version
48 | description: Which version of `react-chartjs-2` are you using?
49 | placeholder: v0.0.0
50 | validations:
51 | required: true
52 |
53 | - type: textarea
54 | attributes:
55 | label: Possible solution
56 | description: If you have suggestions on a fix for the bug.
57 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 |
2 | blank_issues_enabled: false
3 | contact_links:
4 | - name: 🤔 Have a Question?
5 | url: https://stackoverflow.com/questions/tagged/react-chartjs-2
6 | about: Feel free to ask questions on Stack Overflow.
7 | - name: 📊 Have a Problem With Chart.js?
8 | url: https://github.com/chartjs/Chart.js/issues
9 | about: react-chartjs-2 is just the wrapper around Chart.js, so if you are experiencing an issue with charts rendering, please create a related issue in Chart.js repository.
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request.yml:
--------------------------------------------------------------------------------
1 | name: "🚀 Feature Request"
2 | description: "I have a specific suggestion!"
3 | labels: ["enhancement"]
4 | body:
5 | - type: markdown
6 | attributes:
7 | value: Thanks for taking the time to suggest a new feature! Please fill out this form as completely as possible.
8 |
9 | - type: markdown
10 | attributes:
11 | value: ⚠️ react-chartjs-2 is just the wrapper around Chart.js, so if you are experiencing an issue with charts rendering, please create a related issue in [Chart.js repository](https://github.com/chartjs/Chart.js/issues).
12 |
13 | - type: checkboxes
14 | id: input1
15 | attributes:
16 | label: Would you like to work on this feature?
17 | options:
18 | - label: Check this if you would like to implement a PR, we are more than happy to help you go through the process.
19 |
20 | - type: textarea
21 | attributes:
22 | label: What problem are you trying to solve?
23 | description: |
24 | A concise description of what the problem is.
25 | placeholder: |
26 | I have an issue when [...]
27 | validations:
28 | required: true
29 |
30 | - type: textarea
31 | attributes:
32 | label: Describe the solution you'd like
33 | validations:
34 | required: true
35 |
36 | - type: textarea
37 | attributes:
38 | label: Describe alternatives you've considered
39 |
40 | - type: textarea
41 | attributes:
42 | label: Documentation, Adoption, Migration Strategy
43 | description: |
44 | If you can, explain how users will be able to use this and how it might be documented. Maybe a mock-up?
45 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base",
4 | ":preserveSemverRanges"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/.github/workflows/checks.yml:
--------------------------------------------------------------------------------
1 | name: Checks
2 | on:
3 | pull_request:
4 | branches:
5 | - master
6 | jobs:
7 | size:
8 | runs-on: ubuntu-latest
9 | name: size-limit
10 | steps:
11 | - name: Checkout the repository
12 | uses: actions/checkout@v4
13 | - name: Install pnpm
14 | uses: pnpm/action-setup@v2
15 | with:
16 | version: 9
17 | - name: Install Node.js
18 | uses: actions/setup-node@v4
19 | with:
20 | node-version: 22
21 | cache: 'pnpm'
22 | - name: Check size
23 | uses: andresz1/size-limit-action@master
24 | with:
25 | github_token: ${{ secrets.GITHUB_TOKEN }}
26 | typings:
27 | runs-on: ubuntu-latest
28 | name: typings
29 | steps:
30 | - name: Checkout the repository
31 | uses: actions/checkout@v4
32 | - name: Install pnpm
33 | uses: pnpm/action-setup@v2
34 | with:
35 | version: 9
36 | - name: Install Node.js
37 | uses: actions/setup-node@v4
38 | with:
39 | node-version: 22
40 | cache: 'pnpm'
41 | - name: Install dependencies
42 | run: pnpm install
43 | - name: Prebuild
44 | run: pnpm build
45 | - name: Check typings
46 | if: success()
47 | run: pnpm test:typings
48 | storybook:
49 | runs-on: ubuntu-latest
50 | name: storybook
51 | steps:
52 | - name: Checkout the repository
53 | uses: actions/checkout@v4
54 | - name: Install pnpm
55 | uses: pnpm/action-setup@v2
56 | with:
57 | version: 9
58 | - name: Install Node.js
59 | uses: actions/setup-node@v4
60 | with:
61 | node-version: 22
62 | cache: 'pnpm'
63 | - name: Install dependencies
64 | run: pnpm install
65 | - name: Check storybook
66 | run: pnpm build:storybook
67 | editorconfig:
68 | runs-on: ubuntu-latest
69 | name: editorconfig
70 | steps:
71 | - name: Checkout the repository
72 | uses: actions/checkout@v4
73 | - name: Check editorconfig
74 | uses: editorconfig-checker/action-editorconfig-checker@main
75 | website:
76 | runs-on: ubuntu-latest
77 | name: website
78 | steps:
79 | - name: Checkout the repository
80 | uses: actions/checkout@v4
81 | - name: Install pnpm
82 | uses: pnpm/action-setup@v2
83 | with:
84 | version: 9
85 | - name: Install Node.js
86 | uses: actions/setup-node@v4
87 | with:
88 | node-version: 22
89 | cache: 'pnpm'
90 | - name: Install dependencies
91 | run: pnpm install
92 | - name: Install website dependencies
93 | run: pnpm install
94 | working-directory: ./website
95 | - name: Check website
96 | run: pnpm build
97 | working-directory: ./website
98 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | pull_request:
4 | push:
5 | branches:
6 | - master
7 | jobs:
8 | test:
9 | runs-on: ubuntu-latest
10 | name: Tests
11 | steps:
12 | - name: Checkout the repository
13 | uses: actions/checkout@v4
14 | - name: Install pnpm
15 | uses: pnpm/action-setup@v2
16 | with:
17 | version: 9
18 | - name: Install Node.js
19 | uses: actions/setup-node@v4
20 | with:
21 | node-version: 22
22 | cache: 'pnpm'
23 | - name: Install dependencies
24 | run: pnpm install
25 | - name: Run tests
26 | run: pnpm test
27 | - name: Collect coverage
28 | uses: codecov/codecov-action@v5
29 | if: success()
30 | with:
31 | token: ${{ secrets.CODECOV_TOKEN }}
32 | files: ./coverage/lcov.info
33 | fail_ci_if_error: true
34 |
--------------------------------------------------------------------------------
/.github/workflows/commit.yml:
--------------------------------------------------------------------------------
1 | name: Commit
2 | on:
3 | push:
4 | jobs:
5 | commitlint:
6 | runs-on: ubuntu-latest
7 | name: commitlint
8 | steps:
9 | - name: Checkout the repository
10 | uses: actions/checkout@v4
11 | with:
12 | fetch-depth: 0
13 | - name: Run commitlint
14 | uses: wagoid/commitlint-github-action@v6
15 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 | on:
3 | release:
4 | types: [created]
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | name: Publish package
9 | steps:
10 | - name: Checkout the repository
11 | uses: actions/checkout@v4
12 | - name: Install pnpm
13 | uses: pnpm/action-setup@v2
14 | with:
15 | version: 9
16 | - name: Install Node.js
17 | uses: actions/setup-node@v4
18 | with:
19 | node-version: 22
20 | cache: 'pnpm'
21 | registry-url: 'https://registry.npmjs.org'
22 | - name: Install dependencies
23 | run: pnpm install
24 | - name: Publish
25 | run: pnpm publish --no-git-checks
26 | env:
27 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
28 |
--------------------------------------------------------------------------------
/.github/workflows/website.yml:
--------------------------------------------------------------------------------
1 | name: Website
2 | on:
3 | push:
4 | branches:
5 | - master
6 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
7 | permissions:
8 | contents: read
9 | pages: write
10 | id-token: write
11 | # Allow one concurrent deployment
12 | concurrency:
13 | group: "pages"
14 | cancel-in-progress: true
15 | jobs:
16 | deploy:
17 | environment:
18 | name: github-pages
19 | url: ${{ steps.deployment.outputs.page_url }}
20 | runs-on: ubuntu-latest
21 | name: deploy website
22 | steps:
23 | - name: Checkout the repository
24 | uses: actions/checkout@v4
25 | - name: Install pnpm
26 | uses: pnpm/action-setup@v2
27 | with:
28 | version: 9
29 | - name: Install Node.js
30 | uses: actions/setup-node@v4
31 | with:
32 | node-version: 22
33 | cache: 'pnpm'
34 | - name: Install dependencies
35 | run: pnpm install
36 | - name: Install website dependencies
37 | run: pnpm install
38 | working-directory: ./website
39 | - name: Build website
40 | run: pnpm build
41 | working-directory: ./website
42 | - name: Setup Pages
43 | uses: actions/configure-pages@v5
44 | - name: Upload artifact
45 | uses: actions/upload-pages-artifact@v3
46 | with:
47 | path: './website/build'
48 | - name: Deploy to GitHub Pages
49 | id: deployment
50 | uses: actions/deploy-pages@v4
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 |
6 | # builds
7 | package
8 | dist
9 | storybook-static
10 |
11 | # misc
12 | .DS_Store
13 | .env
14 | .env.*
15 |
16 | npm-debug.log*
17 |
18 | # testing
19 | coverage
20 |
--------------------------------------------------------------------------------
/.nano-staged.json:
--------------------------------------------------------------------------------
1 | {
2 | "**/*.{js,ts,tsx}": ["prettier --write", "eslint"]
3 | }
4 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | strict-peer-dependencies=false
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "jsxSingleQuote": true,
4 | "semi": true,
5 | "tabWidth": 2,
6 | "bracketSpacing": true,
7 | "arrowParens": "avoid",
8 | "trailingComma": "es5"
9 | }
10 |
--------------------------------------------------------------------------------
/.simple-git-hooks.json:
--------------------------------------------------------------------------------
1 | {
2 | "commit-msg": "pnpm commitlint --edit \"$1\"",
3 | "pre-commit": "pnpm nano-staged",
4 | "pre-push": "pnpm test"
5 | }
6 |
--------------------------------------------------------------------------------
/.size-limit.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "path": "dist/index.js",
4 | "limit": "1.4 KB",
5 | "webpack": false,
6 | "running": false
7 | },
8 | {
9 | "path": "dist/index.js",
10 | "limit": "590 B",
11 | "import": "{ Chart }"
12 | }
13 | ]
14 |
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { mergeConfig } = require('vite');
3 |
4 | module.exports = {
5 | viteFinal(config) {
6 | return mergeConfig(config, {
7 | resolve: {
8 | alias: {
9 | 'react-chartjs-2': path.resolve(__dirname, '../src'),
10 | },
11 | },
12 | });
13 | },
14 | stories: ['../stories/*.tsx'],
15 | addons: [
16 | '@storybook/addon-docs',
17 | '@storybook/addon-controls',
18 | '@storybook/addon-actions',
19 | ],
20 | framework: {
21 | name: '@storybook/react-vite',
22 | options: {},
23 | },
24 | docs: {},
25 | };
26 |
--------------------------------------------------------------------------------
/.storybook/manager.js:
--------------------------------------------------------------------------------
1 | import { addons } from '@storybook/manager-api';
2 |
3 | import { theme } from './theme';
4 |
5 | addons.setConfig({
6 | theme,
7 | panelPosition: 'right',
8 | });
9 |
--------------------------------------------------------------------------------
/.storybook/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "commonjs"
3 | }
4 |
--------------------------------------------------------------------------------
/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | import { configureActions } from '@storybook/addon-actions';
2 |
3 | configureActions({
4 | depth: 5,
5 | });
6 |
--------------------------------------------------------------------------------
/.storybook/theme.js:
--------------------------------------------------------------------------------
1 | import { create } from '@storybook/theming';
2 |
3 | export const theme = create({
4 | base: 'light',
5 | brandTitle: 'react-chartjs-2',
6 | brandUrl: 'https://github.com/reactchartjs/react-chartjs-2',
7 | });
8 |
--------------------------------------------------------------------------------
/.tool-versions:
--------------------------------------------------------------------------------
1 | nodejs 22.4.1
2 | pnpm 9.15.2
3 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 | ## [5.3.0](https://github.com/reactchartjs/react-chartjs-2/compare/v5.2.0...v5.3.0) (2025-01-01)
6 |
7 |
8 | ### Features
9 |
10 | * support react 19 ([#1236](https://github.com/reactchartjs/react-chartjs-2/issues/1236)) ([055b601](https://github.com/reactchartjs/react-chartjs-2/commit/055b601f22da8aac8c04a37cba16d48d8e4914ee))
11 |
12 |
13 | ### Bug Fixes
14 |
15 | * docs typo ([#1202](https://github.com/reactchartjs/react-chartjs-2/issues/1202)) ([65b68c6](https://github.com/reactchartjs/react-chartjs-2/commit/65b68c6cf177fb98636876af3b0b96ffcd0ce483))
16 |
17 | ## [5.2.0](https://github.com/reactchartjs/react-chartjs-2/compare/v5.1.0...v5.2.0) (2023-01-09)
18 |
19 |
20 | ### Features
21 |
22 | * restore compatability with webpack 4 ([#1146](https://github.com/reactchartjs/react-chartjs-2/issues/1146)) ([082c26d](https://github.com/reactchartjs/react-chartjs-2/commit/082c26d559f210af7c55c31a826fe4821a429700))
23 |
24 | ## [5.1.0](https://github.com/reactchartjs/react-chartjs-2/compare/v5.0.1...v5.1.0) (2022-12-19)
25 |
26 |
27 | ### Features
28 |
29 | * restore CommonJS bundle ([#1137](https://github.com/reactchartjs/react-chartjs-2/issues/1137)) ([7db2643](https://github.com/reactchartjs/react-chartjs-2/commit/7db264337200c999bc987e0e288df08ef7813ee3))
30 |
31 | ### [5.0.1](https://github.com/reactchartjs/react-chartjs-2/compare/v5.0.0...v5.0.1) (2022-11-17)
32 |
33 |
34 | ### Bug Fixes
35 |
36 | * remove chart.js v3 from peer dependencies ([7ae5702](https://github.com/reactchartjs/react-chartjs-2/commit/7ae57023783f4c64210119f0447bd1c0e1ba92e9))
37 |
38 | ## [5.0.0](https://github.com/reactchartjs/react-chartjs-2/compare/v4.3.1...v5.0.0) (2022-11-17)
39 |
40 |
41 | ### ⚠ BREAKING CHANGES
42 |
43 | * no commonjs support
44 |
45 | ### Features
46 |
47 | * chart.js v4 support ([#1109](https://github.com/reactchartjs/react-chartjs-2/issues/1109)) ([deb2110](https://github.com/reactchartjs/react-chartjs-2/commit/deb211084757225e3ddb2574b567e805ba7eeeb6))
48 | * package type module ([#1108](https://github.com/reactchartjs/react-chartjs-2/issues/1108)) ([6eb8ce7](https://github.com/reactchartjs/react-chartjs-2/commit/6eb8ce7f23654e46d17aedc104375112b2f2a955))
49 |
50 |
51 | ### Bug Fixes
52 |
53 | * **deps:** update dependency chart.js to v4 ([#1104](https://github.com/reactchartjs/react-chartjs-2/issues/1104)) ([6192408](https://github.com/reactchartjs/react-chartjs-2/commit/61924087de28a7ee0bba6f61c50a0bde362b662c))
54 | * **deps:** update dependency react-scripts to v5.0.1 ([#1049](https://github.com/reactchartjs/react-chartjs-2/issues/1049)) ([38a9796](https://github.com/reactchartjs/react-chartjs-2/commit/38a9796f1cefa63390d4eaf3eec960c77fe78d34))
55 | * **deps:** update react monorepo to v18 ([#1031](https://github.com/reactchartjs/react-chartjs-2/issues/1031)) ([ad73425](https://github.com/reactchartjs/react-chartjs-2/commit/ad734256e173353222995b4edfbf7a8ad0dc96f3))
56 | * **deps:** update react monorepo to v18.2.0 ([#1082](https://github.com/reactchartjs/react-chartjs-2/issues/1082)) ([72e7352](https://github.com/reactchartjs/react-chartjs-2/commit/72e7352fcdff182465bfdcd6a7f6c4579d062d01))
57 | * **site:** [#1076](https://github.com/reactchartjs/react-chartjs-2/issues/1076) expand the docusaurus container content to give the sandboxes more space ([#1077](https://github.com/reactchartjs/react-chartjs-2/issues/1077)) ([b512e8d](https://github.com/reactchartjs/react-chartjs-2/commit/b512e8dc130e28c1284be77d4ef6c3a788b9e262))
58 |
59 | ### [4.3.1](https://github.com/reactchartjs/react-chartjs-2/compare/v4.3.0...v4.3.1) (2022-07-13)
60 |
61 |
62 | ### Bug Fixes
63 |
64 | * update options without resetting chart ([#1061](https://github.com/reactchartjs/react-chartjs-2/issues/1061)) ([fa0a4bb](https://github.com/reactchartjs/react-chartjs-2/commit/fa0a4bbdb3d85735db289494c4bdf1da083a51da))
65 |
66 | ## [4.3.0](https://github.com/reactchartjs/react-chartjs-2/compare/v4.2.0...v4.3.0) (2022-07-12)
67 |
68 |
69 | ### Features
70 |
71 | * assure redraw occurs when type changes ([#1054](https://github.com/reactchartjs/react-chartjs-2/issues/1054)) ([#1055](https://github.com/reactchartjs/react-chartjs-2/issues/1055)) ([bf0538f](https://github.com/reactchartjs/react-chartjs-2/commit/bf0538fd953ee878659d0b5647676fbfda460c76))
72 |
73 | ## [4.2.0](https://github.com/reactchartjs/react-chartjs-2/compare/v4.1.0...v4.2.0) (2022-06-07)
74 |
75 |
76 | ### Features
77 |
78 | * implement passing updateMode property to chart's update method ([#1043](https://github.com/reactchartjs/react-chartjs-2/issues/1043)) ([96d2714](https://github.com/reactchartjs/react-chartjs-2/commit/96d2714c3df88346152a1b66b8fe729d43151e40))
79 |
80 | ## [4.1.0](https://github.com/reactchartjs/react-chartjs-2/compare/v4.0.1...v4.1.0) (2022-04-07)
81 |
82 |
83 | ### Features
84 |
85 | * react 18 support ([#1034](https://github.com/reactchartjs/react-chartjs-2/issues/1034)) ([058035a](https://github.com/reactchartjs/react-chartjs-2/commit/058035a3e2da17ad9ee0c9f50793da3aaefb3913))
86 |
87 | ### [4.0.1](https://github.com/reactchartjs/react-chartjs-2/compare/v4.0.0...v4.0.1) (2022-01-19)
88 |
89 |
90 | ### Bug Fixes
91 |
92 | * compatability with react 16 typings ([#987](https://github.com/reactchartjs/react-chartjs-2/issues/987)) ([4a01054](https://github.com/reactchartjs/react-chartjs-2/commit/4a010540ac01b1e4b299705ddd93f412df4875d1)), closes [#870](https://github.com/reactchartjs/react-chartjs-2/issues/870)
93 | * **deps:** update dependency @svgr/webpack to v6 ([#999](https://github.com/reactchartjs/react-chartjs-2/issues/999)) ([7611ebd](https://github.com/reactchartjs/react-chartjs-2/commit/7611ebdbdbf4e91991b1a15d393fbadf2de01246))
94 | * **deps:** update dependency react-scripts to v5 ([#1001](https://github.com/reactchartjs/react-chartjs-2/issues/1001)) ([3936227](https://github.com/reactchartjs/react-chartjs-2/commit/3936227b4e6865bbd20419af4a5b0b49561f608c))
95 | * **deps:** update docusaurus monorepo to v2.0.0-beta.14 ([#994](https://github.com/reactchartjs/react-chartjs-2/issues/994)) ([2a24ee9](https://github.com/reactchartjs/react-chartjs-2/commit/2a24ee92203c703d16c3784eccb0011b5b870802))
96 |
97 | ## [4.0.0](https://github.com/reactchartjs/react-chartjs-2/compare/v3.3.0...v4.0.0) (2021-11-22)
98 |
99 |
100 | ### ⚠ BREAKING CHANGES
101 |
102 | * getDatasetAtEvent, getElementAtEvent and getElementsAtEvent props are removed,
103 | utils with the same names can used instead
104 | * New target browsers: ie 11 dropped for regular bundle, modern bundle builds for
105 | browsers with es6 modules support.
106 | * Functional data prop is removed
107 | * Added support of tree-shaking of Chart.js. Now you should register Chart.js
108 | components by yourself.
109 | * default export is renamed to Chart
110 | * Chart.js re-exports are removed
111 |
112 | ### Features
113 |
114 | * datasetIdKey prop ([#848](https://github.com/reactchartjs/react-chartjs-2/issues/848)) ([f895766](https://github.com/reactchartjs/react-chartjs-2/commit/f895766f012c0d3781d75b5f83adc6dbc8de0b03))
115 |
116 |
117 | * default export is renamed to Chart ([#836](https://github.com/reactchartjs/react-chartjs-2/issues/836)) ([131daa0](https://github.com/reactchartjs/react-chartjs-2/commit/131daa008d3a3c280ba9e751c67ca926708b60e4))
118 | * functional data prop is removed ([#840](https://github.com/reactchartjs/react-chartjs-2/issues/840)) ([b64dfb0](https://github.com/reactchartjs/react-chartjs-2/commit/b64dfb0430bf5817a5f8b8708551934ad426921e))
119 | * getDatasetAtEvent, getElementAtEvent and getElementsAtEvent props are removed ([#845](https://github.com/reactchartjs/react-chartjs-2/issues/845)) ([6a9b2a7](https://github.com/reactchartjs/react-chartjs-2/commit/6a9b2a7527d23e7409c9273ad32eb100122ffb51))
120 | * new target browsers ([#841](https://github.com/reactchartjs/react-chartjs-2/issues/841)) ([b1e83db](https://github.com/reactchartjs/react-chartjs-2/commit/b1e83db599e7f9b832c2fe1942b5e5f296730dd9))
121 | * removed chart.js re-exports ([#835](https://github.com/reactchartjs/react-chartjs-2/issues/835)) ([30d5c2d](https://github.com/reactchartjs/react-chartjs-2/commit/30d5c2d457eae0b1142ea4ffb6eff8f583b60817))
122 | * tree-shaking ([#839](https://github.com/reactchartjs/react-chartjs-2/issues/839)) ([fcd2849](https://github.com/reactchartjs/react-chartjs-2/commit/fcd2849037bb01d2eeadbfbc90c90054eb620d4c))
123 |
124 | ## [3.3.0](https://github.com/reactchartjs/react-chartjs-2/compare/v3.2.0...v3.3.0) (2021-10-26)
125 |
126 |
127 | ### Features
128 |
129 | * export chart props types ([#810](https://github.com/reactchartjs/react-chartjs-2/issues/810)) ([82ab334](https://github.com/reactchartjs/react-chartjs-2/commit/82ab334c62939fb4924ed6021502fccfea29a5a2)), closes [#720](https://github.com/reactchartjs/react-chartjs-2/issues/720)
130 |
131 |
132 | ### Bug Fixes
133 |
134 | * data updating fix ([#807](https://github.com/reactchartjs/react-chartjs-2/issues/807)) ([45a50cc](https://github.com/reactchartjs/react-chartjs-2/commit/45a50cc46196ce64088a463b6f3b384a6c98eb06)), closes [#806](https://github.com/reactchartjs/react-chartjs-2/issues/806)
135 |
136 | ## [3.2.0](https://github.com/reactchartjs/react-chartjs-2/compare/v3.1.1...v3.2.0) (2021-10-21)
137 |
138 |
139 | ### Features
140 |
141 | * remove lodash ([#784](https://github.com/reactchartjs/react-chartjs-2/issues/784)) ([5594170](https://github.com/reactchartjs/react-chartjs-2/commit/559417024ef2fb34005727ff16d8fae8615cb071))
142 |
143 |
144 | ### Bug Fixes
145 |
146 | * improve and fix rerendering ([#790](https://github.com/reactchartjs/react-chartjs-2/issues/790)) ([330fb1c](https://github.com/reactchartjs/react-chartjs-2/commit/330fb1cf0913bdbacda5ef755fb58c79482e1ea2))
147 | * multitype chart typings ([#792](https://github.com/reactchartjs/react-chartjs-2/issues/792)) ([2f19eb3](https://github.com/reactchartjs/react-chartjs-2/commit/2f19eb3eba9681f383ca23e7a3a1f1c581c89061))
148 |
149 | ### [3.1.1](https://github.com/reactchartjs/react-chartjs-2/compare/v3.1.0...v3.1.1) (2021-10-19)
150 |
151 |
152 | ### Bug Fixes
153 |
154 | * components' props types ([#782](https://github.com/reactchartjs/react-chartjs-2/issues/782)) ([eba8a27](https://github.com/reactchartjs/react-chartjs-2/commit/eba8a2794bb802dacc395a450110af8765fea868)), closes [#734](https://github.com/reactchartjs/react-chartjs-2/issues/734) [#741](https://github.com/reactchartjs/react-chartjs-2/issues/741)
155 |
156 | ## [3.1.0](https://github.com/reactchartjs/react-chartjs-2/compare/v2.4.0...v3.1.0) (2021-10-17)
157 |
158 |
159 | ### Features
160 |
161 | * Reduced package size
162 |
163 |
164 | ### Bug Fixes
165 |
166 | * minor types fixes ([#759](https://github.com/reactchartjs/react-chartjs-2/issues/759)) ([fe6c00f](https://github.com/reactchartjs/react-chartjs-2/commit/fe6c00f05cdc3099a66a7ac0c05fb5e6f216209a))
167 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2020 Jeremy Ayerst
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Jeremy Ayerst
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-chartjs-2
2 |
3 |
4 |
5 | React components for Chart.js, the most popular charting library.
6 |
7 | Supports Chart.js v4 and v3.
8 |
9 | [![NPM version][npm]][npm-url]
10 | [![Downloads][downloads]][downloads-url]
11 | [![Build status][build]][build-url]
12 | [![Coverage status][coverage]][coverage-url]
13 | [![Bundle size][size]][size-url]
14 |
15 | [npm]: https://img.shields.io/npm/v/react-chartjs-2.svg
16 | [npm-url]: https://www.npmjs.com/package/react-chartjs-2
17 |
18 | [downloads]: https://img.shields.io/npm/dm/react-chartjs-2.svg
19 | [downloads-url]: https://www.npmjs.com/package/react-chartjs-2
20 |
21 | [build]: https://img.shields.io/github/actions/workflow/status/reactchartjs/react-chartjs-2/ci.yml?branch=master
22 | [build-url]: https://github.com/reactchartjs/react-chartjs-2/actions
23 |
24 | [coverage]: https://img.shields.io/codecov/c/github/reactchartjs/react-chartjs-2.svg
25 | [coverage-url]: https://app.codecov.io/gh/reactchartjs/react-chartjs-2
26 |
27 | [size]: https://img.shields.io/bundlephobia/minzip/react-chartjs-2
28 | [size-url]: https://bundlephobia.com/package/react-chartjs-2
29 |
30 |
31 | Quickstart
32 | •
33 | Docs
34 | •
35 | Stack Overflow
36 |
37 |
38 |
39 | ## Quickstart
40 |
41 | Install this library with peer dependencies:
42 |
43 | ```bash
44 | pnpm add react-chartjs-2 chart.js
45 | # or
46 | yarn add react-chartjs-2 chart.js
47 | # or
48 | npm i react-chartjs-2 chart.js
49 | ```
50 |
51 | We recommend using `chart.js@^4.0.0`.
52 |
53 | Then, import and use individual components:
54 |
55 | ```jsx
56 | import { Doughnut } from 'react-chartjs-2';
57 |
58 |
59 | ```
60 |
61 | ## Docs
62 |
63 | - [Migration to v4](https://react-chartjs-2.js.org/docs/migration-to-v4)
64 | - [Working with datasets](https://react-chartjs-2.js.org/docs/working-with-datasets)
65 | - [Working with events](https://react-chartjs-2.js.org/docs/working-with-events)
66 | - [FAQ](https://react-chartjs-2.js.org/faq)
67 | - [Components](https://react-chartjs-2.js.org/components)
68 | - [Examples](https://react-chartjs-2.js.org/examples)
69 |
70 | ## License
71 |
72 | [MIT Licensed](LICENSE)
73 | Copyright (c) 2020 Jeremy Ayerst
74 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-chartjs-2",
3 | "type": "module",
4 | "version": "5.3.0",
5 | "description": "React components for Chart.js",
6 | "author": "Jeremy Ayerst",
7 | "homepage": "https://github.com/reactchartjs/react-chartjs-2",
8 | "license": "MIT",
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/reactchartjs/react-chartjs-2.git"
12 | },
13 | "bugs": {
14 | "url": "https://github.com/reactchartjs/react-chartjs-2/issues"
15 | },
16 | "keywords": [
17 | "chart",
18 | "chart-js",
19 | "chart.js",
20 | "react-chartjs-2",
21 | "react chart.js",
22 | "react-chart.js"
23 | ],
24 | "sideEffects": false,
25 | "types": "./dist/index.d.ts",
26 | "exports": "./src/index.ts",
27 | "publishConfig": {
28 | "main": "./dist/index.cjs",
29 | "module": "./dist/index.js",
30 | "exports": {
31 | "types": "./dist/index.d.ts",
32 | "import": "./dist/index.js",
33 | "require": "./dist/index.cjs"
34 | },
35 | "directory": "package"
36 | },
37 | "files": [
38 | "dist"
39 | ],
40 | "scripts": {
41 | "prepublishOnly": "pnpm test && pnpm build && del ./package && clean-publish",
42 | "postpublish": "del ./package",
43 | "emitDeclarations": "tsc --emitDeclarationOnly",
44 | "build": "rollup -c && pnpm emitDeclarations",
45 | "start:storybook": "storybook dev -p 6006 --ci",
46 | "build:storybook": "del ./storybook-static; NODE_ENV=production storybook build",
47 | "test:lint": "eslint \"src/**/*.{ts,tsx}\" \"stories/**/*.{ts,tsx}\" \"sandboxes/**/*.{ts,tsx}\" \"test/**/*.{ts,tsx}\"",
48 | "test:unit": "vitest run --coverage",
49 | "test:unit:watch": "vitest watch",
50 | "test:size": "size-limit",
51 | "test:typings": "tsd",
52 | "test": "pnpm test:lint && pnpm test:unit",
53 | "format": "prettier --write src",
54 | "commit": "cz",
55 | "bumpVersion": "standard-version",
56 | "createGithubRelease": "simple-github-release",
57 | "release": "pnpm bumpVersion && git push origin master --tags && pnpm createGithubRelease",
58 | "updateGitHooks": "simple-git-hooks"
59 | },
60 | "peerDependencies": {
61 | "chart.js": "^4.1.1",
62 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
63 | },
64 | "devDependencies": {
65 | "@commitlint/cli": "^17.0.0",
66 | "@commitlint/config-conventional": "^17.0.0",
67 | "@commitlint/cz-commitlint": "^17.0.0",
68 | "@rollup/plugin-node-resolve": "^16.0.0",
69 | "@size-limit/preset-big-lib": "^11.1.6",
70 | "@storybook/addon-actions": "^8.4.7",
71 | "@storybook/addon-controls": "^8.4.7",
72 | "@storybook/addon-docs": "^8.4.7",
73 | "@storybook/client-logger": "^8.4.7",
74 | "@storybook/manager-api": "^8.4.7",
75 | "@storybook/preview-api": "^8.4.7",
76 | "@storybook/react": "^8.4.7",
77 | "@storybook/react-vite": "^8.4.7",
78 | "@storybook/theming": "^8.4.7",
79 | "@swc/core": "^1.10.1",
80 | "@swc/helpers": "^0.5.15",
81 | "@testing-library/dom": "^10.4.0",
82 | "@testing-library/jest-dom": "^6.6.3",
83 | "@testing-library/react": "^16.1.0",
84 | "@types/faker": "^5.5.8",
85 | "@types/node": "^18.0.0",
86 | "@types/react": "^19.0.2",
87 | "@types/react-dom": "^19.0.2",
88 | "@typescript-eslint/eslint-plugin": "^8.18.0",
89 | "@typescript-eslint/parser": "^8.18.0",
90 | "@vitejs/plugin-react": "^4.3.4",
91 | "@vitest/coverage-v8": "^2.1.8",
92 | "browserslist": "^4.24.2",
93 | "chart.js": "^4.4.7",
94 | "chartjs-adapter-date-fns": "^3.0.0",
95 | "chartjs-plugin-annotation": "^3.1.0",
96 | "chartjs-plugin-zoom": "^2.2.0",
97 | "clean-publish": "^5.0.0",
98 | "commitizen": "^4.2.4",
99 | "date-fns": "^2.25.0",
100 | "del-cli": "^6.0.0",
101 | "eslint": "^8.57.1",
102 | "eslint-config-prettier": "^9.1.0",
103 | "eslint-config-standard": "^17.1.0",
104 | "eslint-config-standard-react": "^13.0.0",
105 | "eslint-plugin-import": "^2.25.1",
106 | "eslint-plugin-n": "^17.0.0",
107 | "eslint-plugin-prettier": "^5.2.1",
108 | "eslint-plugin-promise": "^7.0.0",
109 | "eslint-plugin-react": "^7.17.0",
110 | "faker": "^5.5.3",
111 | "happy-dom": "^16.1.0",
112 | "nano-staged": "^0.8.0",
113 | "prettier": "^3.4.2",
114 | "react": "^19.0.0",
115 | "react-dom": "^19.0.0",
116 | "rollup": "^4.0.0",
117 | "rollup-plugin-swc3": "^0.12.0",
118 | "simple-git-hooks": "^2.6.1",
119 | "simple-github-release": "^1.0.0",
120 | "size-limit": "^11.1.6",
121 | "standard-version": "^9.3.1",
122 | "storybook": "^8.4.7",
123 | "tsd": "^0.31.0",
124 | "typescript": "^5.7.2",
125 | "vite": "^6.0.6",
126 | "vitest": "^2.1.8",
127 | "vitest-canvas-mock": "^0.3.3"
128 | },
129 | "tsd": {
130 | "directory": "./test",
131 | "compilerOptions": {
132 | "paths": {
133 | "react-chartjs-2": [
134 | "./src"
135 | ]
136 | }
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { swc } from 'rollup-plugin-swc3';
2 | import { nodeResolve } from '@rollup/plugin-node-resolve';
3 | import pkg from './package.json' with { type: 'json' };
4 |
5 | const extensions = ['.js', '.ts', '.tsx'];
6 | const external = _ => /node_modules/.test(_) && !/@swc\/helpers/.test(_);
7 | const plugins = targets => [
8 | nodeResolve({
9 | extensions,
10 | }),
11 | swc({
12 | tsconfig: false,
13 | jsc: {
14 | parser: {
15 | syntax: 'typescript',
16 | tsx: true,
17 | },
18 | transform: {
19 | react: {
20 | useBuiltins: true,
21 | },
22 | },
23 | externalHelpers: true,
24 | },
25 | env: {
26 | targets,
27 | },
28 | module: {
29 | type: 'es6',
30 | },
31 | sourceMaps: true,
32 | }),
33 | ];
34 |
35 | export default {
36 | input: pkg.exports,
37 | plugins: plugins('defaults and supports es6-module'),
38 | external,
39 | output: [
40 | {
41 | file: pkg.publishConfig.exports.import,
42 | format: 'es',
43 | sourcemap: true,
44 | },
45 | {
46 | file: pkg.publishConfig.exports.require,
47 | format: 'cjs',
48 | sourcemap: true,
49 | },
50 | ],
51 | };
52 |
--------------------------------------------------------------------------------
/sandboxes/bar/grouped/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | BarElement,
7 | Title,
8 | Tooltip,
9 | Legend,
10 | } from 'chart.js';
11 | import { Bar } from 'react-chartjs-2';
12 | import faker from 'faker';
13 |
14 | ChartJS.register(
15 | CategoryScale,
16 | LinearScale,
17 | BarElement,
18 | Title,
19 | Tooltip,
20 | Legend
21 | );
22 |
23 | export const options = {
24 | plugins: {
25 | title: {
26 | display: true,
27 | text: 'Chart.js Bar Chart - Stacked',
28 | },
29 | },
30 | responsive: true,
31 | interaction: {
32 | mode: 'index' as const,
33 | intersect: false,
34 | },
35 | scales: {
36 | x: {
37 | stacked: true,
38 | },
39 | y: {
40 | stacked: true,
41 | },
42 | },
43 | };
44 |
45 | const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
46 |
47 | export const data = {
48 | labels,
49 | datasets: [
50 | {
51 | label: 'Dataset 1',
52 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
53 | backgroundColor: 'rgb(255, 99, 132)',
54 | stack: 'Stack 0',
55 | },
56 | {
57 | label: 'Dataset 2',
58 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
59 | backgroundColor: 'rgb(75, 192, 192)',
60 | stack: 'Stack 0',
61 | },
62 | {
63 | label: 'Dataset 3',
64 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
65 | backgroundColor: 'rgb(53, 162, 235)',
66 | stack: 'Stack 1',
67 | },
68 | ],
69 | };
70 |
71 | export function App() {
72 | return ;
73 | }
74 |
--------------------------------------------------------------------------------
/sandboxes/bar/grouped/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/bar/grouped/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/bar/grouped/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/bar/grouped/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/bar/horizontal/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | BarElement,
7 | Title,
8 | Tooltip,
9 | Legend,
10 | } from 'chart.js';
11 | import { Bar } from 'react-chartjs-2';
12 | import faker from 'faker';
13 |
14 | ChartJS.register(
15 | CategoryScale,
16 | LinearScale,
17 | BarElement,
18 | Title,
19 | Tooltip,
20 | Legend
21 | );
22 |
23 | export const options = {
24 | indexAxis: 'y' as const,
25 | elements: {
26 | bar: {
27 | borderWidth: 2,
28 | },
29 | },
30 | responsive: true,
31 | plugins: {
32 | legend: {
33 | position: 'right' as const,
34 | },
35 | title: {
36 | display: true,
37 | text: 'Chart.js Horizontal Bar Chart',
38 | },
39 | },
40 | };
41 |
42 | const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
43 |
44 | export const data = {
45 | labels,
46 | datasets: [
47 | {
48 | label: 'Dataset 1',
49 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
50 | borderColor: 'rgb(255, 99, 132)',
51 | backgroundColor: 'rgba(255, 99, 132, 0.5)',
52 | },
53 | {
54 | label: 'Dataset 2',
55 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
56 | borderColor: 'rgb(53, 162, 235)',
57 | backgroundColor: 'rgba(53, 162, 235, 0.5)',
58 | },
59 | ],
60 | };
61 |
62 | export function App() {
63 | return ;
64 | }
65 |
--------------------------------------------------------------------------------
/sandboxes/bar/horizontal/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/bar/horizontal/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/bar/horizontal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/bar/horizontal/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/bar/stacked/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | BarElement,
7 | Title,
8 | Tooltip,
9 | Legend,
10 | } from 'chart.js';
11 | import { Bar } from 'react-chartjs-2';
12 | import faker from 'faker';
13 |
14 | ChartJS.register(
15 | CategoryScale,
16 | LinearScale,
17 | BarElement,
18 | Title,
19 | Tooltip,
20 | Legend
21 | );
22 |
23 | export const options = {
24 | plugins: {
25 | title: {
26 | display: true,
27 | text: 'Chart.js Bar Chart - Stacked',
28 | },
29 | },
30 | responsive: true,
31 | scales: {
32 | x: {
33 | stacked: true,
34 | },
35 | y: {
36 | stacked: true,
37 | },
38 | },
39 | };
40 |
41 | const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
42 |
43 | export const data = {
44 | labels,
45 | datasets: [
46 | {
47 | label: 'Dataset 1',
48 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
49 | backgroundColor: 'rgb(255, 99, 132)',
50 | },
51 | {
52 | label: 'Dataset 2',
53 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
54 | backgroundColor: 'rgb(75, 192, 192)',
55 | },
56 | {
57 | label: 'Dataset 3',
58 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
59 | backgroundColor: 'rgb(53, 162, 235)',
60 | },
61 | ],
62 | };
63 |
64 | export function App() {
65 | return ;
66 | }
67 |
--------------------------------------------------------------------------------
/sandboxes/bar/stacked/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/bar/stacked/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/bar/stacked/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/bar/stacked/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/bar/vertical/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | BarElement,
7 | Title,
8 | Tooltip,
9 | Legend,
10 | } from 'chart.js';
11 | import { Bar } from 'react-chartjs-2';
12 | import faker from 'faker';
13 |
14 | ChartJS.register(
15 | CategoryScale,
16 | LinearScale,
17 | BarElement,
18 | Title,
19 | Tooltip,
20 | Legend
21 | );
22 |
23 | export const options = {
24 | responsive: true,
25 | plugins: {
26 | legend: {
27 | position: 'top' as const,
28 | },
29 | title: {
30 | display: true,
31 | text: 'Chart.js Bar Chart',
32 | },
33 | },
34 | };
35 |
36 | const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
37 |
38 | export const data = {
39 | labels,
40 | datasets: [
41 | {
42 | label: 'Dataset 1',
43 | data: labels.map(() => faker.datatype.number({ min: 0, max: 1000 })),
44 | backgroundColor: 'rgba(255, 99, 132, 0.5)',
45 | },
46 | {
47 | label: 'Dataset 2',
48 | data: labels.map(() => faker.datatype.number({ min: 0, max: 1000 })),
49 | backgroundColor: 'rgba(53, 162, 235, 0.5)',
50 | },
51 | ],
52 | };
53 |
54 | export function App() {
55 | return ;
56 | }
57 |
--------------------------------------------------------------------------------
/sandboxes/bar/vertical/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/bar/vertical/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/bar/vertical/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/bar/vertical/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/bubble/default/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | LinearScale,
5 | PointElement,
6 | Tooltip,
7 | Legend,
8 | } from 'chart.js';
9 | import { Bubble } from 'react-chartjs-2';
10 | import faker from 'faker';
11 |
12 | ChartJS.register(LinearScale, PointElement, Tooltip, Legend);
13 |
14 | export const options = {
15 | scales: {
16 | y: {
17 | beginAtZero: true,
18 | },
19 | },
20 | };
21 |
22 | export const data = {
23 | datasets: [
24 | {
25 | label: 'Red dataset',
26 | data: Array.from({ length: 50 }, () => ({
27 | x: faker.datatype.number({ min: -100, max: 100 }),
28 | y: faker.datatype.number({ min: -100, max: 100 }),
29 | r: faker.datatype.number({ min: 5, max: 20 }),
30 | })),
31 | backgroundColor: 'rgba(255, 99, 132, 0.5)',
32 | },
33 | {
34 | label: 'Blue dataset',
35 | data: Array.from({ length: 50 }, () => ({
36 | x: faker.datatype.number({ min: -100, max: 100 }),
37 | y: faker.datatype.number({ min: -100, max: 100 }),
38 | r: faker.datatype.number({ min: 5, max: 20 }),
39 | })),
40 | backgroundColor: 'rgba(53, 162, 235, 0.5)',
41 | },
42 | ],
43 | };
44 |
45 | export function App() {
46 | return ;
47 | }
48 |
--------------------------------------------------------------------------------
/sandboxes/bubble/default/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/bubble/default/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/bubble/default/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/bubble/default/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/chart/canvas/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef, useEffect, useState } from 'react';
2 | import type { ChartData, ChartArea } from 'chart.js';
3 | import {
4 | Chart as ChartJS,
5 | CategoryScale,
6 | LinearScale,
7 | PointElement,
8 | LineElement,
9 | Tooltip,
10 | Legend,
11 | } from 'chart.js';
12 | import { Chart } from 'react-chartjs-2';
13 | import faker from 'faker';
14 |
15 | ChartJS.register(
16 | CategoryScale,
17 | LinearScale,
18 | PointElement,
19 | LineElement,
20 | Tooltip,
21 | Legend
22 | );
23 |
24 | const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
25 | const colors = [
26 | 'red',
27 | 'orange',
28 | 'yellow',
29 | 'lime',
30 | 'green',
31 | 'teal',
32 | 'blue',
33 | 'purple',
34 | ];
35 |
36 | export const data = {
37 | labels,
38 | datasets: [
39 | {
40 | label: 'Dataset 1',
41 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
42 | },
43 | {
44 | label: 'Dataset 2',
45 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
46 | },
47 | ],
48 | };
49 |
50 | function createGradient(ctx: CanvasRenderingContext2D, area: ChartArea) {
51 | const colorStart = faker.random.arrayElement(colors);
52 | const colorMid = faker.random.arrayElement(
53 | colors.filter(color => color !== colorStart)
54 | );
55 | const colorEnd = faker.random.arrayElement(
56 | colors.filter(color => color !== colorStart && color !== colorMid)
57 | );
58 |
59 | const gradient = ctx.createLinearGradient(0, area.bottom, 0, area.top);
60 |
61 | gradient.addColorStop(0, colorStart);
62 | gradient.addColorStop(0.5, colorMid);
63 | gradient.addColorStop(1, colorEnd);
64 |
65 | return gradient;
66 | }
67 |
68 | export function App() {
69 | const chartRef = useRef(null);
70 | const [chartData, setChartData] = useState>({
71 | datasets: [],
72 | });
73 |
74 | useEffect(() => {
75 | const chart = chartRef.current;
76 |
77 | if (!chart) {
78 | return;
79 | }
80 |
81 | const chartData = {
82 | ...data,
83 | datasets: data.datasets.map(dataset => ({
84 | ...dataset,
85 | borderColor: createGradient(chart.ctx, chart.chartArea),
86 | })),
87 | };
88 |
89 | setChartData(chartData);
90 | }, []);
91 |
92 | return ;
93 | }
94 |
--------------------------------------------------------------------------------
/sandboxes/chart/canvas/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/chart/canvas/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/chart/canvas/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/chart/canvas/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/chart/events/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { type MouseEvent, useRef } from 'react';
2 | import type { InteractionItem } from 'chart.js';
3 | import {
4 | Chart as ChartJS,
5 | LinearScale,
6 | CategoryScale,
7 | BarElement,
8 | PointElement,
9 | LineElement,
10 | Legend,
11 | Tooltip,
12 | } from 'chart.js';
13 | import {
14 | Chart,
15 | getDatasetAtEvent,
16 | getElementAtEvent,
17 | getElementsAtEvent,
18 | } from 'react-chartjs-2';
19 | import faker from 'faker';
20 |
21 | ChartJS.register(
22 | LinearScale,
23 | CategoryScale,
24 | BarElement,
25 | PointElement,
26 | LineElement,
27 | Legend,
28 | Tooltip
29 | );
30 |
31 | export const options = {
32 | scales: {
33 | y: {
34 | beginAtZero: true,
35 | },
36 | },
37 | };
38 |
39 | const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
40 |
41 | export const data = {
42 | labels,
43 | datasets: [
44 | {
45 | type: 'line' as const,
46 | label: 'Dataset 1',
47 | borderColor: 'rgb(255, 99, 132)',
48 | borderWidth: 2,
49 | fill: false,
50 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
51 | },
52 | {
53 | type: 'bar' as const,
54 | label: 'Dataset 2',
55 | backgroundColor: 'rgb(75, 192, 192)',
56 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
57 | borderColor: 'white',
58 | borderWidth: 2,
59 | },
60 | {
61 | type: 'bar' as const,
62 | label: 'Dataset 3',
63 | backgroundColor: 'rgb(53, 162, 235)',
64 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
65 | },
66 | ],
67 | };
68 |
69 | export function App() {
70 | const printDatasetAtEvent = (dataset: InteractionItem[]) => {
71 | if (!dataset.length) return;
72 |
73 | const datasetIndex = dataset[0].datasetIndex;
74 |
75 | console.log(data.datasets[datasetIndex].label);
76 | };
77 |
78 | const printElementAtEvent = (element: InteractionItem[]) => {
79 | if (!element.length) return;
80 |
81 | const { datasetIndex, index } = element[0];
82 |
83 | console.log(data.labels[index], data.datasets[datasetIndex].data[index]);
84 | };
85 |
86 | const printElementsAtEvent = (elements: InteractionItem[]) => {
87 | if (!elements.length) return;
88 |
89 | console.log(elements.length);
90 | };
91 |
92 | const chartRef = useRef(null);
93 |
94 | const onClick = (event: MouseEvent) => {
95 | const { current: chart } = chartRef;
96 |
97 | if (!chart) {
98 | return;
99 | }
100 |
101 | printDatasetAtEvent(getDatasetAtEvent(chart, event));
102 | printElementAtEvent(getElementAtEvent(chart, event));
103 | printElementsAtEvent(getElementsAtEvent(chart, event));
104 | };
105 |
106 | return (
107 |
114 | );
115 | }
116 |
--------------------------------------------------------------------------------
/sandboxes/chart/events/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/chart/events/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/chart/events/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/chart/events/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/chart/multitype/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | LinearScale,
5 | CategoryScale,
6 | BarElement,
7 | PointElement,
8 | LineElement,
9 | Legend,
10 | Tooltip,
11 | LineController,
12 | BarController,
13 | } from 'chart.js';
14 | import { Chart } from 'react-chartjs-2';
15 | import faker from 'faker';
16 |
17 | ChartJS.register(
18 | LinearScale,
19 | CategoryScale,
20 | BarElement,
21 | PointElement,
22 | LineElement,
23 | Legend,
24 | Tooltip,
25 | LineController,
26 | BarController
27 | );
28 |
29 | const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
30 |
31 | export const data = {
32 | labels,
33 | datasets: [
34 | {
35 | type: 'line' as const,
36 | label: 'Dataset 1',
37 | borderColor: 'rgb(255, 99, 132)',
38 | borderWidth: 2,
39 | fill: false,
40 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
41 | },
42 | {
43 | type: 'bar' as const,
44 | label: 'Dataset 2',
45 | backgroundColor: 'rgb(75, 192, 192)',
46 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
47 | borderColor: 'white',
48 | borderWidth: 2,
49 | },
50 | {
51 | type: 'bar' as const,
52 | label: 'Dataset 3',
53 | backgroundColor: 'rgb(53, 162, 235)',
54 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
55 | },
56 | ],
57 | };
58 |
59 | export function App() {
60 | return ;
61 | }
62 |
--------------------------------------------------------------------------------
/sandboxes/chart/multitype/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/chart/multitype/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/chart/multitype/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/chart/multitype/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/chart/ref/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef, useEffect } from 'react';
2 | import {
3 | Chart as ChartJS,
4 | LinearScale,
5 | CategoryScale,
6 | BarElement,
7 | PointElement,
8 | LineElement,
9 | Legend,
10 | Tooltip,
11 | } from 'chart.js';
12 | import { Chart } from 'react-chartjs-2';
13 | import faker from 'faker';
14 |
15 | ChartJS.register(
16 | LinearScale,
17 | CategoryScale,
18 | BarElement,
19 | PointElement,
20 | LineElement,
21 | Legend,
22 | Tooltip
23 | );
24 |
25 | const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
26 |
27 | export const data = {
28 | labels,
29 | datasets: [
30 | {
31 | type: 'line' as const,
32 | label: 'Dataset 1',
33 | borderColor: 'rgb(255, 99, 132)',
34 | borderWidth: 2,
35 | fill: false,
36 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
37 | },
38 | {
39 | type: 'bar' as const,
40 | label: 'Dataset 2',
41 | backgroundColor: 'rgb(75, 192, 192)',
42 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
43 | borderColor: 'white',
44 | borderWidth: 2,
45 | },
46 | {
47 | type: 'bar' as const,
48 | label: 'Dataset 3',
49 | backgroundColor: 'rgb(53, 162, 235)',
50 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
51 | },
52 | ],
53 | };
54 |
55 | function triggerTooltip(chart: ChartJS | null) {
56 | const tooltip = chart?.tooltip;
57 |
58 | if (!tooltip) {
59 | return;
60 | }
61 |
62 | if (tooltip.getActiveElements().length > 0) {
63 | tooltip.setActiveElements([], { x: 0, y: 0 });
64 | } else {
65 | const { chartArea } = chart;
66 |
67 | tooltip.setActiveElements(
68 | [
69 | {
70 | datasetIndex: 0,
71 | index: 2,
72 | },
73 | {
74 | datasetIndex: 1,
75 | index: 2,
76 | },
77 | ],
78 | {
79 | x: (chartArea.left + chartArea.right) / 2,
80 | y: (chartArea.top + chartArea.bottom) / 2,
81 | }
82 | );
83 | }
84 |
85 | chart.update();
86 | }
87 |
88 | export function App() {
89 | const chartRef = useRef(null);
90 |
91 | useEffect(() => {
92 | const chart = chartRef.current;
93 |
94 | triggerTooltip(chart);
95 | }, []);
96 |
97 | return ;
98 | }
99 |
--------------------------------------------------------------------------------
/sandboxes/chart/ref/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/chart/ref/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/chart/ref/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/chart/ref/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/doughnut/default/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
3 | import { Doughnut } from 'react-chartjs-2';
4 |
5 | ChartJS.register(ArcElement, Tooltip, Legend);
6 |
7 | export const data = {
8 | labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
9 | datasets: [
10 | {
11 | label: '# of Votes',
12 | data: [12, 19, 3, 5, 2, 3],
13 | backgroundColor: [
14 | 'rgba(255, 99, 132, 0.2)',
15 | 'rgba(54, 162, 235, 0.2)',
16 | 'rgba(255, 206, 86, 0.2)',
17 | 'rgba(75, 192, 192, 0.2)',
18 | 'rgba(153, 102, 255, 0.2)',
19 | 'rgba(255, 159, 64, 0.2)',
20 | ],
21 | borderColor: [
22 | 'rgba(255, 99, 132, 1)',
23 | 'rgba(54, 162, 235, 1)',
24 | 'rgba(255, 206, 86, 1)',
25 | 'rgba(75, 192, 192, 1)',
26 | 'rgba(153, 102, 255, 1)',
27 | 'rgba(255, 159, 64, 1)',
28 | ],
29 | borderWidth: 1,
30 | },
31 | ],
32 | };
33 |
34 | export function App() {
35 | return ;
36 | }
37 |
--------------------------------------------------------------------------------
/sandboxes/doughnut/default/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/doughnut/default/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/doughnut/default/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/doughnut/default/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/line/area/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | PointElement,
7 | LineElement,
8 | Title,
9 | Tooltip,
10 | Filler,
11 | Legend,
12 | } from 'chart.js';
13 | import { Line } from 'react-chartjs-2';
14 | import faker from 'faker';
15 |
16 | ChartJS.register(
17 | CategoryScale,
18 | LinearScale,
19 | PointElement,
20 | LineElement,
21 | Title,
22 | Tooltip,
23 | Filler,
24 | Legend
25 | );
26 |
27 | export const options = {
28 | responsive: true,
29 | plugins: {
30 | legend: {
31 | position: 'top' as const,
32 | },
33 | title: {
34 | display: true,
35 | text: 'Chart.js Line Chart',
36 | },
37 | },
38 | };
39 |
40 | const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
41 |
42 | export const data = {
43 | labels,
44 | datasets: [
45 | {
46 | fill: true,
47 | label: 'Dataset 2',
48 | data: labels.map(() => faker.datatype.number({ min: 0, max: 1000 })),
49 | borderColor: 'rgb(53, 162, 235)',
50 | backgroundColor: 'rgba(53, 162, 235, 0.5)',
51 | },
52 | ],
53 | };
54 |
55 | export function App() {
56 | return ;
57 | }
58 |
--------------------------------------------------------------------------------
/sandboxes/line/area/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/line/area/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/line/area/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/line/area/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/line/default/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | PointElement,
7 | LineElement,
8 | Title,
9 | Tooltip,
10 | Legend,
11 | } from 'chart.js';
12 | import { Line } from 'react-chartjs-2';
13 | import faker from 'faker';
14 |
15 | ChartJS.register(
16 | CategoryScale,
17 | LinearScale,
18 | PointElement,
19 | LineElement,
20 | Title,
21 | Tooltip,
22 | Legend
23 | );
24 |
25 | export const options = {
26 | responsive: true,
27 | plugins: {
28 | legend: {
29 | position: 'top' as const,
30 | },
31 | title: {
32 | display: true,
33 | text: 'Chart.js Line Chart',
34 | },
35 | },
36 | };
37 |
38 | const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
39 |
40 | export const data = {
41 | labels,
42 | datasets: [
43 | {
44 | label: 'Dataset 1',
45 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
46 | borderColor: 'rgb(255, 99, 132)',
47 | backgroundColor: 'rgba(255, 99, 132, 0.5)',
48 | },
49 | {
50 | label: 'Dataset 2',
51 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
52 | borderColor: 'rgb(53, 162, 235)',
53 | backgroundColor: 'rgba(53, 162, 235, 0.5)',
54 | },
55 | ],
56 | };
57 |
58 | export function App() {
59 | return ;
60 | }
61 |
--------------------------------------------------------------------------------
/sandboxes/line/default/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/line/default/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/line/default/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/line/default/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/line/multiaxis/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | CategoryScale,
5 | LinearScale,
6 | PointElement,
7 | LineElement,
8 | Title,
9 | Tooltip,
10 | Legend,
11 | } from 'chart.js';
12 | import { Line } from 'react-chartjs-2';
13 | import faker from 'faker';
14 |
15 | ChartJS.register(
16 | CategoryScale,
17 | LinearScale,
18 | PointElement,
19 | LineElement,
20 | Title,
21 | Tooltip,
22 | Legend
23 | );
24 |
25 | export const options = {
26 | responsive: true,
27 | interaction: {
28 | mode: 'index' as const,
29 | intersect: false,
30 | },
31 | stacked: false,
32 | plugins: {
33 | title: {
34 | display: true,
35 | text: 'Chart.js Line Chart - Multi Axis',
36 | },
37 | },
38 | scales: {
39 | y: {
40 | type: 'linear' as const,
41 | display: true,
42 | position: 'left' as const,
43 | },
44 | y1: {
45 | type: 'linear' as const,
46 | display: true,
47 | position: 'right' as const,
48 | grid: {
49 | drawOnChartArea: false,
50 | },
51 | },
52 | },
53 | };
54 |
55 | const labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
56 |
57 | export const data = {
58 | labels,
59 | datasets: [
60 | {
61 | label: 'Dataset 1',
62 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
63 | borderColor: 'rgb(255, 99, 132)',
64 | backgroundColor: 'rgba(255, 99, 132, 0.5)',
65 | yAxisID: 'y',
66 | },
67 | {
68 | label: 'Dataset 2',
69 | data: labels.map(() => faker.datatype.number({ min: -1000, max: 1000 })),
70 | borderColor: 'rgb(53, 162, 235)',
71 | backgroundColor: 'rgba(53, 162, 235, 0.5)',
72 | yAxisID: 'y1',
73 | },
74 | ],
75 | };
76 |
77 | export function App() {
78 | return ;
79 | }
80 |
--------------------------------------------------------------------------------
/sandboxes/line/multiaxis/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/line/multiaxis/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/line/multiaxis/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/line/multiaxis/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/pie/default/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js';
3 | import { Pie } from 'react-chartjs-2';
4 |
5 | ChartJS.register(ArcElement, Tooltip, Legend);
6 |
7 | export const data = {
8 | labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
9 | datasets: [
10 | {
11 | label: '# of Votes',
12 | data: [12, 19, 3, 5, 2, 3],
13 | backgroundColor: [
14 | 'rgba(255, 99, 132, 0.2)',
15 | 'rgba(54, 162, 235, 0.2)',
16 | 'rgba(255, 206, 86, 0.2)',
17 | 'rgba(75, 192, 192, 0.2)',
18 | 'rgba(153, 102, 255, 0.2)',
19 | 'rgba(255, 159, 64, 0.2)',
20 | ],
21 | borderColor: [
22 | 'rgba(255, 99, 132, 1)',
23 | 'rgba(54, 162, 235, 1)',
24 | 'rgba(255, 206, 86, 1)',
25 | 'rgba(75, 192, 192, 1)',
26 | 'rgba(153, 102, 255, 1)',
27 | 'rgba(255, 159, 64, 1)',
28 | ],
29 | borderWidth: 1,
30 | },
31 | ],
32 | };
33 |
34 | export function App() {
35 | return ;
36 | }
37 |
--------------------------------------------------------------------------------
/sandboxes/pie/default/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/pie/default/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/pie/default/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/pie/default/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/polarArea/default/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | RadialLinearScale,
5 | ArcElement,
6 | Tooltip,
7 | Legend,
8 | } from 'chart.js';
9 | import { PolarArea } from 'react-chartjs-2';
10 |
11 | ChartJS.register(RadialLinearScale, ArcElement, Tooltip, Legend);
12 |
13 | export const data = {
14 | labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
15 | datasets: [
16 | {
17 | label: '# of Votes',
18 | data: [12, 19, 3, 5, 2, 3],
19 | backgroundColor: [
20 | 'rgba(255, 99, 132, 0.5)',
21 | 'rgba(54, 162, 235, 0.5)',
22 | 'rgba(255, 206, 86, 0.5)',
23 | 'rgba(75, 192, 192, 0.5)',
24 | 'rgba(153, 102, 255, 0.5)',
25 | 'rgba(255, 159, 64, 0.5)',
26 | ],
27 | borderWidth: 1,
28 | },
29 | ],
30 | };
31 |
32 | export function App() {
33 | return ;
34 | }
35 |
--------------------------------------------------------------------------------
/sandboxes/polarArea/default/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/polarArea/default/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/polarArea/default/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/polarArea/default/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/radar/default/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | RadialLinearScale,
5 | PointElement,
6 | LineElement,
7 | Filler,
8 | Tooltip,
9 | Legend,
10 | } from 'chart.js';
11 | import { Radar } from 'react-chartjs-2';
12 |
13 | ChartJS.register(
14 | RadialLinearScale,
15 | PointElement,
16 | LineElement,
17 | Filler,
18 | Tooltip,
19 | Legend
20 | );
21 |
22 | export const data = {
23 | labels: ['Thing 1', 'Thing 2', 'Thing 3', 'Thing 4', 'Thing 5', 'Thing 6'],
24 | datasets: [
25 | {
26 | label: '# of Votes',
27 | data: [2, 9, 3, 5, 2, 3],
28 | backgroundColor: 'rgba(255, 99, 132, 0.2)',
29 | borderColor: 'rgba(255, 99, 132, 1)',
30 | borderWidth: 1,
31 | },
32 | ],
33 | };
34 |
35 | export function App() {
36 | return ;
37 | }
38 |
--------------------------------------------------------------------------------
/sandboxes/radar/default/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/radar/default/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/radar/default/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/radar/default/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/scatter/default/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | Chart as ChartJS,
4 | LinearScale,
5 | PointElement,
6 | LineElement,
7 | Tooltip,
8 | Legend,
9 | } from 'chart.js';
10 | import { Scatter } from 'react-chartjs-2';
11 | import faker from 'faker';
12 |
13 | ChartJS.register(LinearScale, PointElement, LineElement, Tooltip, Legend);
14 |
15 | export const options = {
16 | scales: {
17 | y: {
18 | beginAtZero: true,
19 | },
20 | },
21 | };
22 |
23 | export const data = {
24 | datasets: [
25 | {
26 | label: 'A dataset',
27 | data: Array.from({ length: 100 }, () => ({
28 | x: faker.datatype.number({ min: -100, max: 100 }),
29 | y: faker.datatype.number({ min: -100, max: 100 }),
30 | })),
31 | backgroundColor: 'rgba(255, 99, 132, 1)',
32 | },
33 | ],
34 | };
35 |
36 | export function App() {
37 | return ;
38 | }
39 |
--------------------------------------------------------------------------------
/sandboxes/scatter/default/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/sandboxes/scatter/default/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App.js';
5 |
6 | const rootElement = document.getElementById('root');
7 | createRoot(rootElement!).render();
8 |
--------------------------------------------------------------------------------
/sandboxes/scatter/default/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "start": "vite"
5 | },
6 | "dependencies": {
7 | "chart.js": "^4.0.0",
8 | "faker": "5.5.3",
9 | "react": "18.2.0",
10 | "react-chartjs-2": "^5.0.0",
11 | "react-dom": "18.2.0"
12 | },
13 | "devDependencies": {
14 | "@types/faker": "5.5.9",
15 | "@types/react": "18.0.26",
16 | "@types/react-dom": "18.0.10",
17 | "@vitejs/plugin-react": "^4.0.0",
18 | "typescript": "4.9.5",
19 | "vite": "^6.0.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sandboxes/scatter/default/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/sandboxes/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "baseUrl": ".",
5 | "paths": {
6 | "react-chartjs-2": ["../src"]
7 | }
8 | },
9 | "include": ["."]
10 | }
11 |
--------------------------------------------------------------------------------
/src/chart.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef, forwardRef } from 'react';
2 | import { Chart as ChartJS } from 'chart.js';
3 | import type { ChartType, DefaultDataPoint } from 'chart.js';
4 |
5 | import type { ForwardedRef, ChartProps, BaseChartComponent } from './types.js';
6 | import {
7 | reforwardRef,
8 | cloneData,
9 | setOptions,
10 | setLabels,
11 | setDatasets,
12 | } from './utils.js';
13 |
14 | function ChartComponent<
15 | TType extends ChartType = ChartType,
16 | TData = DefaultDataPoint,
17 | TLabel = unknown,
18 | >(
19 | props: ChartProps,
20 | ref: ForwardedRef>
21 | ) {
22 | const {
23 | height = 150,
24 | width = 300,
25 | redraw = false,
26 | datasetIdKey,
27 | type,
28 | data,
29 | options,
30 | plugins = [],
31 | fallbackContent,
32 | updateMode,
33 | ...canvasProps
34 | } = props;
35 | const canvasRef = useRef(null);
36 | const chartRef = useRef | null>(null);
37 |
38 | const renderChart = () => {
39 | if (!canvasRef.current) return;
40 |
41 | chartRef.current = new ChartJS(canvasRef.current, {
42 | type,
43 | data: cloneData(data, datasetIdKey),
44 | options: options && { ...options },
45 | plugins,
46 | });
47 |
48 | reforwardRef(ref, chartRef.current);
49 | };
50 |
51 | const destroyChart = () => {
52 | reforwardRef(ref, null);
53 |
54 | if (chartRef.current) {
55 | chartRef.current.destroy();
56 | chartRef.current = null;
57 | }
58 | };
59 |
60 | useEffect(() => {
61 | if (!redraw && chartRef.current && options) {
62 | setOptions(chartRef.current, options);
63 | }
64 | }, [redraw, options]);
65 |
66 | useEffect(() => {
67 | if (!redraw && chartRef.current) {
68 | setLabels(chartRef.current.config.data, data.labels);
69 | }
70 | }, [redraw, data.labels]);
71 |
72 | useEffect(() => {
73 | if (!redraw && chartRef.current && data.datasets) {
74 | setDatasets(chartRef.current.config.data, data.datasets, datasetIdKey);
75 | }
76 | }, [redraw, data.datasets]);
77 |
78 | useEffect(() => {
79 | if (!chartRef.current) return;
80 |
81 | if (redraw) {
82 | destroyChart();
83 | setTimeout(renderChart);
84 | } else {
85 | chartRef.current.update(updateMode);
86 | }
87 | }, [redraw, options, data.labels, data.datasets, updateMode]);
88 |
89 | useEffect(() => {
90 | if (!chartRef.current) return;
91 |
92 | destroyChart();
93 | setTimeout(renderChart);
94 | }, [type]);
95 |
96 | useEffect(() => {
97 | renderChart();
98 |
99 | return () => destroyChart();
100 | }, []);
101 |
102 | return (
103 |
112 | );
113 | }
114 |
115 | export const Chart = forwardRef(ChartComponent) as BaseChartComponent;
116 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export type { ChartProps } from './types.js';
2 | export * from './chart.js';
3 | export * from './typedCharts.js';
4 | export {
5 | getDatasetAtEvent,
6 | getElementAtEvent,
7 | getElementsAtEvent,
8 | } from './utils.js';
9 |
--------------------------------------------------------------------------------
/src/typedCharts.tsx:
--------------------------------------------------------------------------------
1 | import React, { forwardRef } from 'react';
2 | import {
3 | Chart as ChartJS,
4 | LineController,
5 | BarController,
6 | RadarController,
7 | DoughnutController,
8 | PolarAreaController,
9 | BubbleController,
10 | PieController,
11 | ScatterController,
12 | } from 'chart.js';
13 | import type { ChartType, ChartComponentLike } from 'chart.js';
14 |
15 | import type {
16 | ChartProps,
17 | ChartJSOrUndefined,
18 | TypedChartComponent,
19 | } from './types.js';
20 | import { Chart } from './chart.js';
21 |
22 | function createTypedChart(
23 | type: T,
24 | registerables: ChartComponentLike
25 | ) {
26 | ChartJS.register(registerables);
27 |
28 | return forwardRef, Omit, 'type'>>(
29 | (props, ref) =>
30 | ) as TypedChartComponent;
31 | }
32 |
33 | export const Line = /* #__PURE__ */ createTypedChart('line', LineController);
34 |
35 | export const Bar = /* #__PURE__ */ createTypedChart('bar', BarController);
36 |
37 | export const Radar = /* #__PURE__ */ createTypedChart('radar', RadarController);
38 |
39 | export const Doughnut = /* #__PURE__ */ createTypedChart(
40 | 'doughnut',
41 | DoughnutController
42 | );
43 |
44 | export const PolarArea = /* #__PURE__ */ createTypedChart(
45 | 'polarArea',
46 | PolarAreaController
47 | );
48 |
49 | export const Bubble = /* #__PURE__ */ createTypedChart(
50 | 'bubble',
51 | BubbleController
52 | );
53 |
54 | export const Pie = /* #__PURE__ */ createTypedChart('pie', PieController);
55 |
56 | export const Scatter = /* #__PURE__ */ createTypedChart(
57 | 'scatter',
58 | ScatterController
59 | );
60 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | CanvasHTMLAttributes,
3 | MutableRefObject,
4 | ReactNode,
5 | JSX,
6 | } from 'react';
7 | import type {
8 | Chart,
9 | ChartType,
10 | ChartData,
11 | ChartOptions,
12 | DefaultDataPoint,
13 | Plugin,
14 | UpdateMode,
15 | } from 'chart.js';
16 |
17 | export type ForwardedRef =
18 | | ((instance: T | null) => void)
19 | | MutableRefObject
20 | | null;
21 |
22 | export interface ChartProps<
23 | TType extends ChartType = ChartType,
24 | TData = DefaultDataPoint,
25 | TLabel = unknown,
26 | > extends CanvasHTMLAttributes {
27 | /**
28 | * Chart.js chart type
29 | */
30 | type: TType;
31 | /**
32 | * The data object that is passed into the Chart.js chart
33 | * @see https://www.chartjs.org/docs/latest/getting-started/
34 | */
35 | data: ChartData;
36 | /**
37 | * The options object that is passed into the Chart.js chart
38 | * @see https://www.chartjs.org/docs/latest/general/options.html
39 | * @default {}
40 | */
41 | options?: ChartOptions;
42 | /**
43 | * The plugins array that is passed into the Chart.js chart
44 | * @see https://www.chartjs.org/docs/latest/developers/plugins.html
45 | * @default []
46 | */
47 | plugins?: Plugin[];
48 | /**
49 | * Teardown and redraw chart on every update
50 | * @default false
51 | */
52 | redraw?: boolean;
53 | /**
54 | * Key name to identificate dataset
55 | * @default 'label'
56 | */
57 | datasetIdKey?: string;
58 | /**
59 | * A fallback for when the canvas cannot be rendered. Can be used for accessible chart descriptions
60 | * @see https://www.chartjs.org/docs/latest/general/accessibility.html
61 | * @default null
62 | * @todo Replace with `children` prop.
63 | */
64 | fallbackContent?: ReactNode;
65 | /**
66 | * A mode string to indicate transition configuration should be used.
67 | * @see https://www.chartjs.org/docs/latest/developers/api.html#update-mode
68 | */
69 | updateMode?: UpdateMode;
70 | }
71 |
72 | /**
73 | * @todo Replace `undefined` with `null`
74 | */
75 | export type ChartJSOrUndefined<
76 | TType extends ChartType = ChartType,
77 | TData = DefaultDataPoint,
78 | TLabel = unknown,
79 | > = Chart | undefined;
80 |
81 | export type BaseChartComponent = <
82 | TType extends ChartType = ChartType,
83 | TData = DefaultDataPoint,
84 | TLabel = unknown,
85 | >(
86 | props: ChartProps & {
87 | ref?: ForwardedRef>;
88 | }
89 | ) => JSX.Element;
90 |
91 | export type TypedChartComponent = <
92 | TData = DefaultDataPoint,
93 | TLabel = unknown,
94 | >(
95 | props: Omit, 'type'> & {
96 | ref?: ForwardedRef>;
97 | }
98 | ) => JSX.Element;
99 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import type { MouseEvent } from 'react';
2 | import type {
3 | ChartType,
4 | ChartData,
5 | DefaultDataPoint,
6 | ChartDataset,
7 | ChartOptions,
8 | Chart,
9 | } from 'chart.js';
10 |
11 | import type { ForwardedRef } from './types.js';
12 |
13 | const defaultDatasetIdKey = 'label';
14 |
15 | export function reforwardRef(ref: ForwardedRef, value: T) {
16 | if (typeof ref === 'function') {
17 | ref(value);
18 | } else if (ref) {
19 | ref.current = value;
20 | }
21 | }
22 |
23 | export function setOptions<
24 | TType extends ChartType = ChartType,
25 | TData = DefaultDataPoint,
26 | TLabel = unknown,
27 | >(chart: Chart, nextOptions: ChartOptions) {
28 | const options = chart.options;
29 |
30 | if (options && nextOptions) {
31 | Object.assign(options, nextOptions);
32 | }
33 | }
34 |
35 | export function setLabels<
36 | TType extends ChartType = ChartType,
37 | TData = DefaultDataPoint,
38 | TLabel = unknown,
39 | >(
40 | currentData: ChartData,
41 | nextLabels: TLabel[] | undefined
42 | ) {
43 | currentData.labels = nextLabels;
44 | }
45 |
46 | export function setDatasets<
47 | TType extends ChartType = ChartType,
48 | TData = DefaultDataPoint,
49 | TLabel = unknown,
50 | >(
51 | currentData: ChartData,
52 | nextDatasets: ChartDataset[],
53 | datasetIdKey = defaultDatasetIdKey
54 | ) {
55 | const addedDatasets: ChartDataset[] = [];
56 |
57 | currentData.datasets = nextDatasets.map(
58 | (nextDataset: Record) => {
59 | // given the new set, find it's current match
60 | const currentDataset = currentData.datasets.find(
61 | (dataset: Record) =>
62 | dataset[datasetIdKey] === nextDataset[datasetIdKey]
63 | );
64 |
65 | // There is no original to update, so simply add new one
66 | if (
67 | !currentDataset ||
68 | !nextDataset.data ||
69 | addedDatasets.includes(currentDataset)
70 | ) {
71 | return { ...nextDataset } as ChartDataset;
72 | }
73 |
74 | addedDatasets.push(currentDataset);
75 |
76 | Object.assign(currentDataset, nextDataset);
77 |
78 | return currentDataset;
79 | }
80 | );
81 | }
82 |
83 | export function cloneData<
84 | TType extends ChartType = ChartType,
85 | TData = DefaultDataPoint,
86 | TLabel = unknown,
87 | >(data: ChartData, datasetIdKey = defaultDatasetIdKey) {
88 | const nextData: ChartData = {
89 | labels: [],
90 | datasets: [],
91 | };
92 |
93 | setLabels(nextData, data.labels);
94 | setDatasets(nextData, data.datasets, datasetIdKey);
95 |
96 | return nextData;
97 | }
98 |
99 | /**
100 | * Get dataset from mouse click event
101 | * @param chart - Chart.js instance
102 | * @param event - Mouse click event
103 | * @returns Dataset
104 | */
105 | export function getDatasetAtEvent(
106 | chart: Chart,
107 | event: MouseEvent
108 | ) {
109 | return chart.getElementsAtEventForMode(
110 | event.nativeEvent,
111 | 'dataset',
112 | { intersect: true },
113 | false
114 | );
115 | }
116 |
117 | /**
118 | * Get single dataset element from mouse click event
119 | * @param chart - Chart.js instance
120 | * @param event - Mouse click event
121 | * @returns Dataset
122 | */
123 | export function getElementAtEvent(
124 | chart: Chart,
125 | event: MouseEvent
126 | ) {
127 | return chart.getElementsAtEventForMode(
128 | event.nativeEvent,
129 | 'nearest',
130 | { intersect: true },
131 | false
132 | );
133 | }
134 |
135 | /**
136 | * Get all dataset elements from mouse click event
137 | * @param chart - Chart.js instance
138 | * @param event - Mouse click event
139 | * @returns Dataset
140 | */
141 | export function getElementsAtEvent(
142 | chart: Chart,
143 | event: MouseEvent
144 | ) {
145 | return chart.getElementsAtEventForMode(
146 | event.nativeEvent,
147 | 'index',
148 | { intersect: true },
149 | false
150 | );
151 | }
152 |
--------------------------------------------------------------------------------
/stories/Bar.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import 'chart.js/auto';
3 | import { Bar } from '../src';
4 | import * as verticalBar from '../sandboxes/bar/vertical/App';
5 | import * as horizontalBar from '../sandboxes/bar/horizontal/App';
6 | import * as stackedBar from '../sandboxes/bar/stacked/App';
7 | import * as groupedBar from '../sandboxes/bar/grouped/App';
8 |
9 | export default {
10 | title: 'Components/Bar',
11 | component: Bar,
12 | parameters: {
13 | layout: 'centered',
14 | },
15 | args: {
16 | width: 500,
17 | height: 400,
18 | },
19 | };
20 |
21 | export const Vertical = args => ;
22 |
23 | Vertical.args = {
24 | data: verticalBar.data,
25 | options: verticalBar.options,
26 | };
27 |
28 | export const Horizontal = args => ;
29 |
30 | Horizontal.args = {
31 | data: horizontalBar.data,
32 | options: horizontalBar.options,
33 | };
34 |
35 | export const Stacked = args => ;
36 |
37 | Stacked.args = {
38 | data: stackedBar.data,
39 | options: stackedBar.options,
40 | };
41 |
42 | export const Grouped = args => ;
43 |
44 | Grouped.args = {
45 | data: groupedBar.data,
46 | options: groupedBar.options,
47 | };
48 |
--------------------------------------------------------------------------------
/stories/Bubble.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import 'chart.js/auto';
3 | import { Bubble } from '../src';
4 | import { data, options } from '../sandboxes/bubble/default/App';
5 |
6 | export default {
7 | title: 'Components/Bubble',
8 | component: Bubble,
9 | parameters: {
10 | layout: 'centered',
11 | },
12 | args: {
13 | width: 500,
14 | height: 400,
15 | },
16 | };
17 |
18 | export const Default = args => ;
19 |
20 | Default.args = {
21 | data,
22 | options,
23 | };
24 |
--------------------------------------------------------------------------------
/stories/Chart.data.ts:
--------------------------------------------------------------------------------
1 | import faker from 'faker';
2 | import { colorRed } from './data';
3 |
4 | export const dynamicOptions = {
5 | scales: {
6 | y: {
7 | beginAtZero: true,
8 | },
9 | },
10 | };
11 |
12 | export const getDynamicData = () => ({
13 | labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
14 | datasets: [
15 | {
16 | type: 'bar',
17 | label: 'Scale',
18 | data: Array.from({ length: 6 }, () =>
19 | faker.datatype.number({ min: -10, max: 10 })
20 | ),
21 | backgroundColor: [
22 | 'rgba(255, 99, 132, 0.2)',
23 | 'rgba(54, 162, 235, 0.2)',
24 | 'rgba(255, 206, 86, 0.2)',
25 | 'rgba(75, 192, 192, 0.2)',
26 | 'rgba(153, 102, 255, 0.2)',
27 | 'rgba(255, 159, 64, 0.2)',
28 | ],
29 | borderColor: [
30 | 'rgba(255, 99, 132, 1)',
31 | 'rgba(54, 162, 235, 1)',
32 | 'rgba(255, 206, 86, 1)',
33 | 'rgba(75, 192, 192, 1)',
34 | 'rgba(153, 102, 255, 1)',
35 | 'rgba(255, 159, 64, 1)',
36 | ],
37 | borderWidth: 1,
38 | },
39 | ],
40 | });
41 |
42 | export const sameData1 = {
43 | labels: [
44 | 'Jan',
45 | 'Feb',
46 | 'Mar',
47 | 'Apr',
48 | 'Mei',
49 | 'Jun',
50 | 'Jul',
51 | 'Aug',
52 | 'Sep',
53 | 'Oct',
54 | 'Nov',
55 | 'Dec',
56 | ],
57 | datasets: [
58 | {
59 | label: 'My First dataset',
60 | backgroundColor: 'rgba(75,192,192,0.4)',
61 | data: [33, 53, 85, 41, 44, 65, 61, 47, 52, 53, 62, 82],
62 | },
63 | {
64 | label: 'My Second dataset',
65 | backgroundColor: '#742774',
66 | data: [33, 25, 35, 51, 54, 76, 65, 40, 42, 39, 51, 55],
67 | },
68 | ],
69 | };
70 |
71 | export const sameData2 = {
72 | labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
73 | datasets: [
74 | {
75 | label: 'My First dataset',
76 | backgroundColor: 'rgba(75,192,192,0.4)',
77 | data: [42, 13, 45, 29, 44, 25, 27],
78 | },
79 | {
80 | label: 'My Second dataset',
81 | backgroundColor: '#742774',
82 | data: [33, 25, 35, 44, 50, 40, 48],
83 | },
84 | ],
85 | };
86 |
87 | export const decimationOptions = {
88 | // Turn off animations and data parsing for performance
89 | animation: false,
90 | parsing: false,
91 |
92 | interaction: {
93 | mode: 'nearest',
94 | axis: 'x',
95 | intersect: false,
96 | },
97 | plugins: {
98 | decimation: {
99 | enabled: true,
100 | algorithm: 'lttb',
101 | samples: 500,
102 | },
103 | },
104 | scales: {
105 | x: {
106 | type: 'time',
107 | ticks: {
108 | source: 'auto',
109 | // Disabled rotation for performance
110 | maxRotation: 0,
111 | autoSkip: true,
112 | },
113 | },
114 | },
115 | };
116 |
117 | export const getDecimationData = () => {
118 | const start = Date.now();
119 | const data = Array.from({ length: 100000 }, (_, i) => ({
120 | x: start + i * 30000,
121 | y: faker.datatype.number({ min: 0, max: Math.random() < 0.001 ? 100 : 20 }),
122 | }));
123 |
124 | return {
125 | datasets: [
126 | {
127 | borderColor: colorRed,
128 | borderWidth: 1,
129 | data,
130 | label: 'Large Dataset',
131 | radius: 0,
132 | },
133 | ],
134 | };
135 | };
136 |
--------------------------------------------------------------------------------
/stories/Chart.stories.tsx:
--------------------------------------------------------------------------------
1 | import React, {
2 | type MouseEvent,
3 | useRef,
4 | useState,
5 | useEffect,
6 | useReducer,
7 | useMemo,
8 | } from 'react';
9 | import 'chart.js/auto';
10 | import type { InteractionItem } from 'chart.js';
11 | import { Chart as ChartJS } from 'chart.js';
12 | import 'chartjs-adapter-date-fns';
13 | import annotationPlugin from 'chartjs-plugin-annotation';
14 | import zoomPlugin from 'chartjs-plugin-zoom';
15 | import {
16 | Chart,
17 | getDatasetAtEvent,
18 | getElementAtEvent,
19 | getElementsAtEvent,
20 | } from '../src';
21 | import * as multitypeChart from '../sandboxes/chart/multitype/App';
22 | import * as eventsChart from '../sandboxes/chart/events/App';
23 | import * as data from './Chart.data';
24 |
25 | ChartJS.register(annotationPlugin, zoomPlugin);
26 |
27 | export default {
28 | title: 'Components/Chart',
29 | component: Chart,
30 | parameters: {
31 | layout: 'centered',
32 | },
33 | args: {
34 | width: 500,
35 | height: 400,
36 | },
37 | };
38 |
39 | export const MultiType = args => ;
40 |
41 | MultiType.args = {
42 | data: multitypeChart.data,
43 | };
44 |
45 | export const Dynamic = args => {
46 | const [dynamicData, setDynamicData] = useState(data.getDynamicData);
47 |
48 | useEffect(() => {
49 | const interval = setInterval(
50 | () => setDynamicData(data.getDynamicData()),
51 | 3000
52 | );
53 |
54 | return () => clearInterval(interval);
55 | }, []);
56 |
57 | return ;
58 | };
59 |
60 | Dynamic.args = {
61 | options: data.dynamicOptions,
62 | };
63 |
64 | export const ClickEvents = ({
65 | onDatasetClick,
66 | onElementClick,
67 | onElementsClick,
68 | options,
69 | data,
70 | ...args
71 | }) => {
72 | const datasetAtEvent = (dataset: InteractionItem[]) => {
73 | if (!dataset.length) return;
74 |
75 | const datasetIndex = dataset[0].datasetIndex;
76 |
77 | onDatasetClick(data.datasets[datasetIndex].label);
78 | };
79 |
80 | const elementAtEvent = (element: InteractionItem[]) => {
81 | if (!element.length) return;
82 |
83 | const { datasetIndex, index } = element[0];
84 |
85 | onElementClick(data.labels[index], data.datasets[datasetIndex].data[index]);
86 | };
87 |
88 | const elementsAtEvent = (elements: InteractionItem[]) => {
89 | if (!elements.length) return;
90 |
91 | onElementsClick(elements);
92 | };
93 |
94 | const chartRef = useRef(null);
95 |
96 | const onClick = (event: MouseEvent) => {
97 | const { current: chart } = chartRef;
98 |
99 | if (!chart) {
100 | return;
101 | }
102 |
103 | datasetAtEvent(getDatasetAtEvent(chart, event));
104 | elementAtEvent(getElementAtEvent(chart, event));
105 | elementsAtEvent(getElementsAtEvent(chart, event));
106 | };
107 |
108 | return (
109 |
117 | );
118 | };
119 |
120 | ClickEvents.args = {
121 | options: eventsChart.options,
122 | data: eventsChart.data,
123 | };
124 |
125 | ClickEvents.argTypes = {
126 | onDatasetClick: { action: 'dataset clicked' },
127 | onElementClick: { action: 'element clicked' },
128 | onElementsClick: { action: 'elements clicked' },
129 | };
130 |
131 | export const Redraw = args => ;
132 |
133 | Redraw.args = {
134 | data: multitypeChart.data,
135 | redraw: true,
136 | };
137 |
138 | export const SameDataToggle = args => {
139 | const [currentData, toggleData] = useReducer(
140 | prevState =>
141 | prevState === data.sameData1 ? data.sameData2 : data.sameData1,
142 | data.sameData1
143 | );
144 |
145 | return ;
146 | };
147 |
148 | SameDataToggle.args = {
149 | type: 'bar',
150 | };
151 |
152 | export const Decimation = args => {
153 | const [currentData, toggleData] = useReducer(
154 | data.getDecimationData,
155 | data.getDecimationData()
156 | );
157 |
158 | return ;
159 | };
160 |
161 | Decimation.args = {
162 | type: 'line',
163 | options: data.decimationOptions,
164 | };
165 |
166 | export const DynamicOptions = args => {
167 | const [yMax, setYMax] = useState(100);
168 | const options = useMemo(
169 | () => ({
170 | plugins: {
171 | annotation: {
172 | annotations: {
173 | box1: {
174 | type: 'box',
175 | xMin: 1,
176 | xMax: 2,
177 | yMin: 50,
178 | yMax: yMax,
179 | backgroundColor: 'rgba(255, 99, 132, 0.25)',
180 | },
181 | },
182 | },
183 | zoom: {
184 | zoom: {
185 | wheel: {
186 | enabled: true,
187 | },
188 | pinch: {
189 | enabled: true,
190 | },
191 | mode: 'xy',
192 | },
193 | },
194 | },
195 | }),
196 | [yMax]
197 | );
198 |
199 | return (
200 | <>
201 |
202 |
203 | >
204 | );
205 | };
206 |
207 | DynamicOptions.args = {
208 | data: multitypeChart.data,
209 | };
210 |
--------------------------------------------------------------------------------
/stories/Doughnut.stories.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react';
2 | import 'chart.js/auto';
3 | import { Doughnut } from '../src';
4 | import { data } from '../sandboxes/doughnut/default/App';
5 |
6 | export default {
7 | title: 'Components/Doughnut',
8 | component: Doughnut,
9 | parameters: {
10 | layout: 'centered',
11 | },
12 | args: {
13 | width: 500,
14 | height: 400,
15 | },
16 | };
17 |
18 | export const Default = args => ;
19 |
20 | Default.args = {
21 | data,
22 | };
23 |
24 | export const Rotation = args => {
25 | const [rotation, setRotation] = useState(0);
26 |
27 | useEffect(() => {
28 | const interval = setInterval(() => {
29 | setRotation(rotation => rotation + 90);
30 | }, 3000);
31 |
32 | return () => clearInterval(interval);
33 | });
34 |
35 | return ;
36 | };
37 |
38 | Rotation.args = {
39 | data,
40 | };
41 |
--------------------------------------------------------------------------------
/stories/Line.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import 'chart.js/auto';
3 | import { Line } from '../src';
4 | import * as defaultLine from '../sandboxes/line/default/App';
5 | import * as multiaxisLine from '../sandboxes/line/multiaxis/App';
6 |
7 | export default {
8 | title: 'Components/Line',
9 | component: Line,
10 | parameters: {
11 | layout: 'centered',
12 | },
13 | args: {
14 | width: 500,
15 | height: 400,
16 | },
17 | };
18 |
19 | export const Default = args => ;
20 |
21 | Default.args = {
22 | data: defaultLine.data,
23 | options: defaultLine.options,
24 | };
25 |
26 | export const MultiAxis = args => ;
27 |
28 | MultiAxis.args = {
29 | data: multiaxisLine.data,
30 | options: multiaxisLine.options,
31 | };
32 |
--------------------------------------------------------------------------------
/stories/Pie.stories.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import faker from 'faker';
3 | import 'chart.js/auto';
4 | import { Pie } from '../src';
5 | import { data } from '../sandboxes/pie/default/App';
6 |
7 | export default {
8 | title: 'Components/Pie',
9 | component: Pie,
10 | parameters: {
11 | layout: 'centered',
12 | },
13 | args: {
14 | width: 500,
15 | height: 400,
16 | },
17 | };
18 |
19 | export const Default = args => ;
20 |
21 | Default.args = {
22 | data,
23 | };
24 |
25 | function randomDataset() {
26 | return {
27 | value: faker.datatype.number({ min: -100, max: 100 }),
28 | color: faker.internet.color(),
29 | };
30 | }
31 |
32 | export const Dynamic = args => {
33 | const [datasets, setDatasets] = useState(() => [randomDataset()]);
34 | const onAdd = () => {
35 | setDatasets(datasets => [...datasets, randomDataset()]);
36 | };
37 | const onRemove = () => {
38 | setDatasets(datasets => datasets.slice(0, -1));
39 | };
40 | const data = {
41 | labels: datasets.map((_, i) => `#${i}`),
42 | datasets: [
43 | {
44 | data: datasets.map(({ value }) => value),
45 | backgroundColor: datasets.map(({ color }) => color),
46 | },
47 | ],
48 | };
49 |
50 | return (
51 | <>
52 |
53 |
54 |
55 |
56 | {datasets.map(({ value, color }, i) => (
57 | -
58 | {value}
59 |
60 | ))}
61 |
62 | >
63 | );
64 | };
65 |
--------------------------------------------------------------------------------
/stories/PolarArea.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import 'chart.js/auto';
3 | import { PolarArea } from '../src';
4 | import { data } from '../sandboxes/polarArea/default/App';
5 |
6 | export default {
7 | title: 'Components/PolarArea',
8 | component: PolarArea,
9 | parameters: {
10 | layout: 'centered',
11 | },
12 | args: {
13 | width: 500,
14 | height: 400,
15 | },
16 | };
17 |
18 | export const Default = args => ;
19 |
20 | Default.args = {
21 | data,
22 | };
23 |
--------------------------------------------------------------------------------
/stories/Radar.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import 'chart.js/auto';
3 | import { Radar } from '../src';
4 | import { data } from '../sandboxes/radar/default/App';
5 |
6 | export default {
7 | title: 'Components/Radar',
8 | component: Radar,
9 | parameters: {
10 | layout: 'centered',
11 | },
12 | args: {
13 | width: 500,
14 | height: 400,
15 | },
16 | };
17 |
18 | export const Default = args => ;
19 |
20 | Default.args = {
21 | data,
22 | };
23 |
--------------------------------------------------------------------------------
/stories/Scatter.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import 'chart.js/auto';
3 | import { Scatter } from '../src';
4 | import { data, options } from '../sandboxes/scatter/default/App';
5 |
6 | export default {
7 | title: 'Components/Scatter',
8 | component: Scatter,
9 | parameters: {
10 | layout: 'centered',
11 | },
12 | args: {
13 | width: 500,
14 | height: 400,
15 | },
16 | };
17 |
18 | export const Default = args => ;
19 |
20 | Default.args = {
21 | data,
22 | options,
23 | };
24 |
--------------------------------------------------------------------------------
/stories/data.ts:
--------------------------------------------------------------------------------
1 | import faker from 'faker';
2 |
3 | export const months = [
4 | 'January',
5 | 'February',
6 | 'March',
7 | 'April',
8 | 'May',
9 | 'June',
10 | 'July',
11 | ];
12 | export const colorRed = 'rgb(255, 99, 132)';
13 | export const colorRed05 = 'rgba(255, 99, 132, 0.5)';
14 | export const colorGreen = 'rgb(75, 192, 192)';
15 | export const colorGreen05 = 'rgba(75, 192, 192, 0.5)';
16 | export const colorBlue = 'rgb(53, 162, 235)';
17 | export const colorBlue05 = 'rgba(53, 162, 235, 0.5)';
18 |
19 | export function numbers(count = months.length) {
20 | return Array.from({ length: count }, () =>
21 | faker.datatype.number({ min: -100, max: 100 })
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/test/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "@typescript-eslint/no-explicit-any": "off"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/test/chart.test-d.tsx:
--------------------------------------------------------------------------------
1 | import { expectError } from 'tsd';
2 | import React from 'react';
3 | import type { Plugin } from 'chart.js';
4 | import { Chart, Scatter, Doughnut } from '../src/index.js';
5 |
6 | const data = {
7 | datasets: [],
8 | };
9 | const multiTypeData = {
10 | datasets: [
11 | {
12 | type: 'line' as const,
13 | label: 'Dataset 1',
14 | data: [],
15 | },
16 | {
17 | type: 'bar' as const,
18 | label: 'Dataset 2',
19 | data: [],
20 | },
21 | ],
22 | };
23 |
24 | /**
25 | * Should check type-specific props
26 | */
27 |
28 | []} />;
29 | []} />;
30 | ;
31 | []} />;
32 |
33 | expectError([]} />);
34 |
35 | /**
36 | * Should check type-specific options
37 | */
38 |
39 | ;
45 |
46 | expectError(
47 |
53 | );
54 |
--------------------------------------------------------------------------------
/test/chart.test.tsx:
--------------------------------------------------------------------------------
1 | import { vi, beforeEach, afterEach, describe, it, expect } from 'vitest';
2 | import React from 'react';
3 | import { render, cleanup, fireEvent } from '@testing-library/react';
4 | import 'chart.js/auto';
5 | import { Chart as ChartJS } from 'chart.js';
6 | import { Chart } from '../src/index.js';
7 |
8 | describe('', () => {
9 | const data = {
10 | labels: ['red', 'blue'],
11 | datasets: [{ label: 'colors', data: [1, 2] }],
12 | };
13 |
14 | const options = {
15 | responsive: false,
16 | };
17 |
18 | let chart: any, update: any, destroy: any;
19 | const ref = (el: ChartJS | undefined | null): void => {
20 | chart = el;
21 |
22 | if (chart) {
23 | update = vi.spyOn(chart, 'update');
24 | destroy = vi.spyOn(chart, 'destroy');
25 | }
26 | };
27 |
28 | beforeEach(() => {
29 | chart = null;
30 | });
31 |
32 | afterEach(() => {
33 | if (chart) chart.destroy();
34 |
35 | cleanup();
36 |
37 | if (update) update.mockClear();
38 | if (destroy) destroy.mockClear();
39 | });
40 |
41 | it('should not pollute props', () => {
42 | render();
43 |
44 | expect(data).toStrictEqual({
45 | labels: ['red', 'blue'],
46 | datasets: [{ label: 'colors', data: [1, 2] }],
47 | });
48 |
49 | // expect(options).toStrictEqual({
50 | // responsive: false,
51 | // });
52 | });
53 |
54 | it('should set ref to chart instance', () => {
55 | render();
56 |
57 | expect(chart).toBeTruthy();
58 | expect(chart instanceof ChartJS).toBe(true);
59 | });
60 |
61 | it('should pass props onto chart', () => {
62 | render();
63 |
64 | expect(chart.config.data).toMatchObject(data);
65 | expect(chart.config.options).toMatchObject(options);
66 | expect(chart.config.type).toEqual('bar');
67 | });
68 |
69 | it('should pass new data on data change', () => {
70 | const newData = {
71 | labels: ['red', 'blue'],
72 | datasets: [{ label: 'colors', data: [2, 1] }],
73 | };
74 |
75 | const { rerender } = render(
76 |
77 | );
78 |
79 | // const meta = chart.config.data.datasets[0]._meta;
80 | const id = chart.id;
81 |
82 | rerender();
83 |
84 | expect(chart.config.data).toMatchObject(newData);
85 | // make sure that other properties were maintained
86 | // expect(chart.config.data.datasets[0]._meta).toEqual(meta);
87 | expect(update).toHaveBeenCalled();
88 | expect(chart.id).toEqual(id);
89 | });
90 |
91 | it('should properly update with entirely new data', () => {
92 | const newData = {
93 | labels: ['purple', 'pink'],
94 | datasets: [{ label: 'new-colors', data: [1, 10] }],
95 | };
96 |
97 | const { rerender } = render(
98 |
99 | );
100 |
101 | const meta = chart.config.data.datasets[0]._meta;
102 | const id = chart.id;
103 |
104 | rerender();
105 |
106 | expect(chart.config.data).toMatchObject(newData);
107 | expect(meta).not.toEqual(chart.config.data.datasets[0]);
108 | expect(update).toHaveBeenCalled();
109 | expect(chart.id).toEqual(id);
110 | });
111 |
112 | it('should properly update with a new chart type', () => {
113 | const newType = 'line';
114 |
115 | const { rerender } = render(
116 |
117 | );
118 |
119 | const originalChartDestroy = destroy;
120 |
121 | rerender();
122 |
123 | expect(originalChartDestroy).toHaveBeenCalled();
124 | });
125 |
126 | it('should properly maintain order with new data', () => {
127 | const oldData = {
128 | labels: ['red', 'blue'],
129 | datasets: [
130 | { label: 'new-colors', data: [1, 2] },
131 | { label: 'colors', data: [3, 2] },
132 | ],
133 | };
134 |
135 | const newData = {
136 | labels: ['red', 'blue'],
137 | datasets: [
138 | { label: 'colors', data: [4, 5] },
139 | { label: 'new-colors', data: [1, 2] },
140 | ],
141 | };
142 |
143 | const { rerender } = render(
144 |
145 | );
146 |
147 | const meta = Object.assign({}, chart._metasets);
148 |
149 | const id = chart.id;
150 |
151 | rerender();
152 |
153 | expect(chart.config.data).toMatchObject(newData);
154 | expect(meta[0]).toBe(chart._metasets[1]);
155 | expect(meta[1]).toBe(chart._metasets[0]);
156 | expect(update).toHaveBeenCalled();
157 | expect(chart.id).toEqual(id);
158 | });
159 |
160 | it('should properly update when original data did not exist', () => {
161 | const oldData = {
162 | labels: ['red', 'blue'],
163 | datasets: [
164 | { label: 'new-colors', data: [] },
165 | { label: 'colors', data: [3, 2] },
166 | ],
167 | };
168 |
169 | const newData = {
170 | labels: ['red', 'blue'],
171 | datasets: [
172 | { label: 'colors', data: [4, 5] },
173 | { label: 'new-colors', data: [1, 2] },
174 | ],
175 | };
176 |
177 | const { rerender } = render(
178 |
179 | );
180 |
181 | // even when we feed the data as undefined, the constructor will
182 | // force it to []. Here we force it back
183 | chart.config.data.datasets[0].data = undefined;
184 | const meta = Object.assign({}, chart._metasets);
185 |
186 | const id = chart.id;
187 |
188 | rerender();
189 |
190 | expect(chart.config.data).toMatchObject(newData);
191 | expect(meta[0]).toBe(chart._metasets[1]);
192 | expect(update).toHaveBeenCalled();
193 | expect(chart.id).toEqual(id);
194 | });
195 |
196 | it('should properly update when incoming data does not exist', () => {
197 | const oldData = {
198 | labels: ['red', 'blue'],
199 | datasets: [
200 | { label: 'new-colors', data: [1, 2] },
201 | { label: 'colors', data: [3, 2] },
202 | ],
203 | };
204 |
205 | const newData = {
206 | labels: ['red', 'blue'],
207 | datasets: [
208 | { label: 'colors', data: [4, 5] },
209 | { label: 'new-colors', data: [] },
210 | ],
211 | };
212 |
213 | const { rerender } = render(
214 |
215 | );
216 |
217 | const id = chart.id;
218 |
219 | rerender();
220 |
221 | expect(chart.config.data).toMatchObject(newData);
222 | expect(update).toHaveBeenCalled();
223 | expect(chart.id).toEqual(id);
224 | });
225 |
226 | it('should pass new options on options change', () => {
227 | const newOptions = {
228 | responsive: true,
229 | };
230 |
231 | const { rerender } = render(
232 |
233 | );
234 |
235 | const id = chart.id;
236 |
237 | rerender();
238 |
239 | expect(chart.options).toMatchObject(newOptions);
240 | expect(update).toHaveBeenCalled();
241 | expect(chart.id).toEqual(id);
242 | });
243 |
244 | it('should destroy and rerender when set to redraw', () => {
245 | const newData = {
246 | labels: ['red', 'blue'],
247 | datasets: [{ label: 'colors', data: [2, 1] }],
248 | };
249 |
250 | const { rerender } = render(
251 |
252 | );
253 |
254 | // const id = chart.id;
255 | const originalChartDestroy = destroy;
256 |
257 | rerender(
258 |
259 | );
260 |
261 | expect(originalChartDestroy).toHaveBeenCalled();
262 | });
263 |
264 | it('should destroy when unmounted', () => {
265 | const { unmount } = render(
266 |
267 | );
268 |
269 | expect(chart).toBeTruthy();
270 |
271 | unmount();
272 |
273 | expect(chart).toBe(null);
274 | });
275 |
276 | it('should add className ', () => {
277 | render(
278 |
285 | );
286 |
287 | expect(chart).toBeTruthy();
288 | expect(chart.canvas).toHaveProperty('className');
289 | expect(chart.canvas).toHaveClass('chart-example');
290 | });
291 |
292 | it('should call onClick', () => {
293 | const onClick = vi.fn();
294 |
295 | const { getByTestId } = render(
296 |
304 | );
305 |
306 | fireEvent.click(getByTestId('canvas'));
307 |
308 | expect(onClick).toHaveBeenCalled();
309 | });
310 |
311 | it('should show fallback content if given', () => {
312 | const fallback = Fallback content
;
313 | const { getByTestId } = render(
314 |
322 | );
323 |
324 | expect(chart).toBeTruthy();
325 | expect(chart.canvas).toContainElement(getByTestId('fallbackContent'));
326 | });
327 |
328 | it('should pass through aria labels to the canvas element', () => {
329 | const ariaLabel = 'ARIA LABEL';
330 | render(
331 |
338 | );
339 |
340 | expect(chart.canvas.getAttribute('aria-label')).toBe(ariaLabel);
341 | });
342 |
343 | it('should rerender datasets with same labels', () => {
344 | const getData = () => ({
345 | labels: [1, 2, 3],
346 | datasets: [
347 | {
348 | label: '',
349 | data: [5, 6, 7],
350 | },
351 | {
352 | label: '',
353 | data: [3, 2, 1],
354 | },
355 | ],
356 | });
357 |
358 | const { rerender } = render(
359 |
360 | );
361 |
362 | const [prevDataset1, prevDataset2] = chart.config.data.datasets;
363 |
364 | rerender();
365 |
366 | const [nextDataset1, nextDataset2] = chart.config.data.datasets;
367 |
368 | expect(prevDataset1).toBe(nextDataset1);
369 | expect(prevDataset2).not.toBe(nextDataset2);
370 | });
371 |
372 | it('should rerender datasets with id', () => {
373 | const getData = () => ({
374 | labels: [1, 2, 3],
375 | datasets: [
376 | {
377 | id: 1,
378 | label: '',
379 | data: [5, 6, 7],
380 | },
381 | {
382 | id: 2,
383 | label: '',
384 | data: [3, 2, 1],
385 | },
386 | ],
387 | });
388 |
389 | const { rerender } = render(
390 |
391 | );
392 |
393 | const [prevDataset1, prevDataset2] = chart.config.data.datasets;
394 |
395 | rerender(
396 |
397 | );
398 |
399 | const [nextDataset1, nextDataset2] = chart.config.data.datasets;
400 |
401 | expect(prevDataset1).toBe(nextDataset1);
402 | expect(prevDataset2).toBe(nextDataset2);
403 | });
404 |
405 | it('should pass updateMode prop to update method', () => {
406 | const newData = {
407 | labels: ['purple', 'pink'],
408 | datasets: [{ label: 'new-colors', data: [1, 10] }],
409 | };
410 |
411 | const { rerender } = render(
412 |
419 | );
420 |
421 | rerender(
422 |
429 | );
430 |
431 | expect(update).toHaveBeenCalledTimes(1);
432 | expect(update).toBeCalledWith('active');
433 | });
434 | });
435 |
--------------------------------------------------------------------------------
/test/setup.ts:
--------------------------------------------------------------------------------
1 | import '@testing-library/jest-dom/vitest';
2 |
3 | import 'vitest-canvas-mock';
4 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Type Checking */
4 | "strict": true,
5 | "strictBindCallApply": true,
6 | "noFallthroughCasesInSwitch": true,
7 | "noImplicitOverride": true,
8 | "noImplicitReturns": true,
9 | "noUnusedLocals": true,
10 | "noUnusedParameters": true,
11 | /* Modules */
12 | "baseUrl": ".",
13 | "module": "NodeNext",
14 | "moduleResolution": "NodeNext",
15 | "resolveJsonModule": true,
16 | /* Emit */
17 | "declaration": true,
18 | "declarationMap": true,
19 | "inlineSourceMap": true,
20 | "outDir": "dist",
21 | /* Interop Constraints */
22 | "allowSyntheticDefaultImports": true,
23 | "isolatedModules": true,
24 | /* Language and Environment */
25 | "jsx": "react",
26 | "lib": [
27 | "dom",
28 | "esnext"
29 | ],
30 | "target": "ESNext",
31 | /* Completeness */
32 | "skipLibCheck": true
33 | },
34 | "include": [
35 | "src"
36 | ]
37 | }
38 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | test: {
7 | environment: 'happy-dom',
8 | setupFiles: ['test/setup.ts'],
9 | server: {
10 | deps: {
11 | inline: ['vitest-canvas-mock'],
12 | },
13 | },
14 | coverage: {
15 | reporter: ['lcovonly', 'text'],
16 | },
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/website/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | /node_modules
3 |
4 | # Production
5 | /build
6 |
7 | # Generated files
8 | .docusaurus
9 | .cache-loader
10 |
11 | # Misc
12 | .DS_Store
13 | .env.*
14 |
15 | npm-debug.log*
16 |
--------------------------------------------------------------------------------
/website/CNAME:
--------------------------------------------------------------------------------
1 | react-chartjs-2.js.org
2 |
--------------------------------------------------------------------------------
/website/README.md:
--------------------------------------------------------------------------------
1 | # Website
2 |
3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.
4 |
5 | ### Installation
6 |
7 | ```
8 | $ pnpm install
9 | ```
10 |
11 | ### Local Development
12 |
13 | ```
14 | $ pnpm start
15 | ```
16 |
17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
18 |
19 | ### Build
20 |
21 | ```
22 | $ pnpm build
23 | ```
24 |
25 | This command generates static content into the `build` directory and can be served using any static contents hosting service.
26 |
27 | ### Deployment
28 |
29 | ```
30 | $ GIT_USER= USE_SSH=true pnpm deploy
31 | ```
32 |
33 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
34 |
--------------------------------------------------------------------------------
/website/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3 | };
4 |
--------------------------------------------------------------------------------
/website/docs/chartjs-v2.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | slug: /docs/chartjs-v2
3 | description: Using react-chartjs-2 with Chart.js v2
4 | ---
5 |
6 | import Tabs from '@theme/Tabs';
7 | import TabItem from '@theme/TabItem';
8 |
9 | # Using with Chart.js v2
10 |
11 | If your app needs [Chart.js v2](https://www.chartjs.org/docs/2.9.4/), please use a [compatible version](https://www.npmjs.com/package/react-chartjs-2/v/2.11.2) of this library:
12 |
13 |
14 |
15 |
16 | ```bash
17 | yarn add chart.js@^2.9.4 react-chartjs-2@^2.11.2
18 | ```
19 |
20 |
21 |
22 |
23 | ```bash
24 | pnpm add chart.js@^2.9.4 react-chartjs-2@^2.11.2
25 | ```
26 |
27 |
28 |
29 |
30 | ```bash
31 | npm install --save chart.js@^2.9.4 react-chartjs-2@^2.11.2
32 | ```
33 |
34 |
35 |
36 |
37 | Also, please consider upgrading your app to [Chart.js v3](#upgrading-to-chartjs-v3).
38 |
39 | ## Upgrading to Chart.js v3
40 |
41 | First, upgrade packages. You'll need to install Chart.js v3 and the latest version of this library:
42 |
43 |
44 |
45 |
46 | ```bash
47 | yarn add chart.js@^3.6.0 react-chartjs-2@^4.0.0
48 | ```
49 |
50 |
51 |
52 |
53 | ```bash
54 | pnpm add chart.js@^3.6.0 react-chartjs-2@^4.0.0
55 | ```
56 |
57 |
58 |
59 |
60 | ```bash
61 | npm install --save chart.js@^3.6.0 react-chartjs-2@^4.0.0
62 | ```
63 |
64 |
65 |
66 |
67 | Then, please follow the [this guide](/docs/migration-to-v4).
68 |
--------------------------------------------------------------------------------
/website/docs/chartjs-v3.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | slug: /docs/chartjs-v3
3 | description: Using react-chartjs-2 with Chart.js v3
4 | ---
5 |
6 | import Tabs from '@theme/Tabs';
7 | import TabItem from '@theme/TabItem';
8 |
9 | # Using with Chart.js v3
10 |
11 | If your app needs [Chart.js v3](https://www.chartjs.org/docs/3.9.1/), please use a [compatible version](https://www.npmjs.com/package/react-chartjs-2/v/4.3.1) of this library:
12 |
13 |
14 |
15 |
16 | ```bash
17 | yarn add chart.js@^3.9.1 react-chartjs-2@^4.3.1
18 | ```
19 |
20 |
21 |
22 |
23 | ```bash
24 | pnpm add chart.js@^3.9.1 react-chartjs-2@^4.3.1
25 | ```
26 |
27 |
28 |
29 |
30 | ```bash
31 | npm install --save chart.js@^3.9.1 react-chartjs-2@^4.3.1
32 | ```
33 |
34 |
35 |
36 |
37 | Also, please consider upgrading your app to [Chart.js v4](#upgrading-to-chartjs-v4).
38 |
39 | ## Upgrading to Chart.js v4
40 |
41 | First, upgrade packages. You'll need to install Chart.js v3 and the latest version of this library:
42 |
43 |
44 |
45 |
46 | ```bash
47 | yarn add chart.js@^4.0.0 react-chartjs-2@^5.0.0
48 | ```
49 |
50 |
51 |
52 |
53 | ```bash
54 | pnpm add chart.js@^4.0.0 react-chartjs-2@^5.0.0
55 | ```
56 |
57 |
58 |
59 |
60 | ```bash
61 | npm install --save chart.js@^4.0.0 react-chartjs-2@^5.0.0
62 | ```
63 |
64 |
65 |
66 |
67 | Then, please follow the [this guide](/docs/migration-to-v5).
68 |
--------------------------------------------------------------------------------
/website/docs/components/bar.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | description: Description of Bar component from react-chartjs-2.
3 | ---
4 |
5 | import PropsTable from '../../src/components/PropsTable'
6 |
7 | # Bar
8 |
9 | ## Usage
10 |
11 | ```jsx
12 | import { Bar } from 'react-chartjs-2';
13 |
14 |
19 | ```
20 |
21 | [See full usage examples.](/tags/bar-chart)
22 |
23 | ## Props
24 |
25 | Also supports all standard `