├── .editorconfig ├── .eslintrc ├── .gitattributes ├── .github └── workflows │ └── test.yml ├── .gitignore ├── .prettierrc ├── .vs ├── React-HighCharts │ └── v17 │ │ └── .suo ├── VSWorkspaceState.json └── slnx.sqlite ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── babel.config.js ├── index.html ├── package-lock.json ├── package.json └── packages ├── react-jsx-highcharts ├── .babelrc ├── README.md ├── jest.config.js ├── package.json ├── src │ ├── components │ │ ├── Annotation │ │ │ ├── Annotation.js │ │ │ └── index.js │ │ ├── Axis │ │ │ ├── Axis.js │ │ │ ├── AxisTitle.js │ │ │ ├── createProvidedAxis.js │ │ │ └── index.js │ │ ├── AxisContext │ │ │ └── index.js │ │ ├── BarSeries │ │ │ ├── BarSeries.js │ │ │ └── index.js │ │ ├── BaseChart │ │ │ ├── BaseChart.js │ │ │ ├── createProvidedChart.js │ │ │ └── index.js │ │ ├── Caption │ │ │ ├── Caption.js │ │ │ └── index.js │ │ ├── Chart │ │ │ ├── Chart.js │ │ │ └── index.js │ │ ├── ChartContext │ │ │ └── index.js │ │ ├── ColorAxis │ │ │ ├── ColorAxis.js │ │ │ ├── createProvidedColorAxis.js │ │ │ └── index.js │ │ ├── ColorAxisContext │ │ │ └── index.js │ │ ├── Credits │ │ │ ├── Credits.js │ │ │ └── index.js │ │ ├── Debug │ │ │ ├── Debug.js │ │ │ └── index.js │ │ ├── Highcharts3dChart │ │ │ ├── Highcharts3dChart.js │ │ │ └── index.js │ │ ├── HighchartsChart │ │ │ ├── HighchartsChart.js │ │ │ └── index.js │ │ ├── HighchartsContext │ │ │ └── index.js │ │ ├── HighchartsSparkline │ │ │ ├── HighchartsSparkline.js │ │ │ └── index.js │ │ ├── Legend │ │ │ ├── Legend.js │ │ │ ├── LegendTitle.js │ │ │ └── index.js │ │ ├── Loading │ │ │ ├── Loading.js │ │ │ └── index.js │ │ ├── Options3d │ │ │ ├── Options3d.js │ │ │ └── index.js │ │ ├── Pane │ │ │ ├── Pane.js │ │ │ └── index.js │ │ ├── PlotBandLine │ │ │ ├── PlotBand.js │ │ │ ├── PlotBandLineLabel.js │ │ │ ├── PlotLine.js │ │ │ ├── UsePlotBandLineLifecycle.js │ │ │ └── index.js │ │ ├── PlotBandLineContext │ │ │ └── index.js │ │ ├── Series │ │ │ ├── Series.js │ │ │ ├── createProvidedSeries.js │ │ │ └── index.js │ │ ├── SeriesContext │ │ │ └── index.js │ │ ├── Subtitle │ │ │ ├── Subtitle.js │ │ │ └── index.js │ │ ├── Title │ │ │ ├── Title.js │ │ │ └── index.js │ │ ├── Tooltip │ │ │ ├── Tooltip.js │ │ │ └── index.js │ │ ├── UseAxis │ │ │ └── index.js │ │ ├── UseChart │ │ │ └── index.js │ │ ├── UseChartUpdate │ │ │ └── index.js │ │ ├── UseColorAxis │ │ │ └── index.js │ │ ├── UseHighcharts │ │ │ └── index.js │ │ ├── UseManualEventHandlers │ │ │ └── index.js │ │ ├── UseModifiedProps │ │ │ └── index.js │ │ ├── UsePlotBandLine │ │ │ └── index.js │ │ ├── UsePrevious │ │ │ └── index.js │ │ ├── UseSeries │ │ │ └── index.js │ │ ├── WithHighcharts │ │ │ └── index.js │ │ ├── WithSeriesType │ │ │ └── index.js │ │ ├── XAxis │ │ │ ├── XAxis.js │ │ │ └── index.js │ │ ├── YAxis │ │ │ ├── YAxis.js │ │ │ └── index.js │ │ └── ZAxis │ │ │ ├── ZAxis.js │ │ │ └── index.js │ ├── index.js │ └── utils │ │ ├── debounce-raf.js │ │ ├── events.js │ │ ├── getModifiedProps.js │ │ ├── pickBy.js │ │ └── warnings.js ├── test │ ├── .eslintrc │ ├── ContextSpy.js │ ├── components │ │ ├── Annotation │ │ │ └── Annotation.spec.js │ │ ├── Axis │ │ │ ├── Axis.integration.spec.js │ │ │ ├── Axis.spec.js │ │ │ └── AxisTitle.spec.js │ │ ├── BarSeries │ │ │ └── BarSeries.spec.js │ │ ├── BaseChart │ │ │ └── BaseChart.spec.js │ │ ├── Caption │ │ │ └── Caption.spec.js │ │ ├── Chart │ │ │ └── Chart.spec.js │ │ ├── ColorAxis │ │ │ └── ColorAxis.integration.spec.js │ │ ├── Credits │ │ │ └── Credits.spec.js │ │ ├── HighchartsChart │ │ │ └── HighchartsChart.spec.js │ │ ├── Legend │ │ │ ├── Legend.spec.js │ │ │ └── LegendTitle.spec.js │ │ ├── Loading │ │ │ └── Loading.spec.js │ │ ├── Options3d │ │ │ └── Options3d.spec.js │ │ ├── Pane │ │ │ └── Pane.spec.js │ │ ├── PlotBandLine │ │ │ ├── PlotBand.integration.spec.js │ │ │ ├── PlotBandLineLabel.integration.spec.js │ │ │ ├── PlotBandLineLabel.spec.js │ │ │ └── PlotLine.spec.js │ │ ├── Series │ │ │ ├── Series.integration.spec.js │ │ │ ├── Series.spec.js │ │ │ ├── SeriesTypes.integration.spec.js │ │ │ └── SeriesTypes.spec.js │ │ ├── Subtitle │ │ │ └── Subtitle.spec.js │ │ ├── Title │ │ │ └── Title.spec.js │ │ ├── Tooltip │ │ │ └── Tooltip.spec.js │ │ ├── UseAxis │ │ │ └── useAxis.spec.js │ │ ├── UseChart │ │ │ └── useChart.spec.js │ │ ├── UseChartUpdate │ │ │ └── useChartUpdate.spec.js │ │ ├── UseHighcharts │ │ │ └── useHighcharts.spec.js │ │ ├── UseManualEventHandlers │ │ │ └── useManualEventHandlers.spec.js │ │ ├── UseModifiedProps │ │ │ └── useModifiedProps.spec.js │ │ ├── UsePlotBandLine │ │ │ └── usePlotBandLine.spec.js │ │ ├── UseSeries │ │ │ └── useSeries.spec.js │ │ ├── WithHighcharts │ │ │ └── index.spec.js │ │ ├── WithSeriesType │ │ │ └── index.spec.js │ │ ├── XAxis │ │ │ └── XAxis.spec.js │ │ ├── YAxis │ │ │ └── YAxis.spec.js │ │ └── ZAxis │ │ │ └── ZAxis.spec.js │ ├── test-helper.js │ ├── test-utils.js │ └── utils │ │ ├── events.spec.js │ │ ├── getModifiedProps.spec.js │ │ ├── pickBy.spec.js │ │ └── warnings.spec.js ├── tsconfig.json ├── types │ └── index.d.ts └── webpack.config.js ├── react-jsx-highmaps ├── .babelrc ├── README.md ├── jest.config.js ├── package.json ├── src │ ├── components │ │ ├── HighchartsMapChart │ │ │ ├── HighchartsMapChart.js │ │ │ └── index.js │ │ ├── MapNavigation │ │ │ ├── MapNavigation.js │ │ │ ├── MapNavigationButton.js │ │ │ ├── MapNavigationZoomIn.js │ │ │ ├── MapNavigationZoomOut.js │ │ │ └── index.js │ │ ├── XAxis │ │ │ ├── XAxis.js │ │ │ └── index.js │ │ └── YAxis │ │ │ ├── YAxis.js │ │ │ └── index.js │ └── index.js ├── test │ ├── .eslintrc │ ├── components │ │ ├── HighchartsMapChart │ │ │ └── HighchartsMapChart.spec.js │ │ ├── XAxis │ │ │ └── XAxis.spec.js │ │ └── YAxis │ │ │ └── YAxis.spec.js │ ├── test-helper.js │ └── test-utils.js ├── tsconfig.json ├── types │ └── index.d.ts └── webpack.config.js └── react-jsx-highstock ├── .babelrc ├── README.md ├── jest.config.js ├── package.json ├── src ├── components │ ├── HighchartsStockChart │ │ ├── HighchartsStockChart.js │ │ └── index.js │ ├── Navigator │ │ ├── Navigator.js │ │ ├── NavigatorAxis.js │ │ ├── NavigatorSeries.js │ │ ├── NavigatorXAxis.js │ │ ├── NavigatorYAxis.js │ │ └── index.js │ ├── RangeSelector │ │ ├── RangeSelector.js │ │ ├── RangeSelectorButton.js │ │ ├── RangeSelectorInput.js │ │ └── index.js │ └── Scrollbar │ │ ├── Scrollbar.js │ │ └── index.js └── index.js ├── test ├── .eslintrc ├── components │ ├── HighchartsStockChart │ │ └── HighchartsStockChart.spec.js │ ├── Navigator │ │ ├── Navigator.integration.spec.js │ │ ├── Navigator.spec.js │ │ └── NavigatorXAxis.integration.spec.js │ ├── RangeSelector │ │ ├── RangeSelector.spec.js │ │ └── RangeSelectorInput.spec.js │ └── Scrollbar │ │ └── Scrollbar.spec.js ├── test-helper.js └── test-utils.js ├── tsconfig.json ├── types └── index.d.ts └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | # A special property that should be specified at the top of the file outside of 4 | # any sections. Set to true to stop .editor config file search on current file 5 | root = true 6 | 7 | [*] 8 | # Indentation style 9 | # Possible values - tab, space 10 | indent_style = space 11 | 12 | # Indentation size in single-spaced characters 13 | # Possible values - an integer, tab 14 | indent_size = 2 15 | 16 | # Line ending file format 17 | # Possible values - lf, crlf, cr 18 | end_of_line = lf 19 | 20 | # File character encoding 21 | # Possible values - latin1, utf-8, utf-16be, utf-16le 22 | charset = utf-8 23 | 24 | # Denotes whether to trim whitespace at the end of lines 25 | # Possible values - true, false 26 | trim_trailing_whitespace = true 27 | 28 | # Denotes whether file should end with a newline 29 | # Possible values - true, false 30 | insert_final_newline = true -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@babel/eslint-parser", 4 | "plugins": ["react", "import", "react-hooks", "react-perf", "prettier"], 5 | "settings": { 6 | "react": { 7 | "version": "detect" 8 | } 9 | }, 10 | "parserOptions": { 11 | "ecmaVersion": "latest", 12 | "sourceType": "module", 13 | "ecmaFeatures": { 14 | "jsx": true 15 | } 16 | }, 17 | "env": { 18 | "browser": true, 19 | "es6": true 20 | }, 21 | "extends": [ 22 | "eslint:recommended", 23 | "plugin:react/recommended", 24 | "plugin:import/errors", 25 | "plugin:react-perf/recommended", 26 | "plugin:prettier/recommended" 27 | ], 28 | "rules": { 29 | "no-unused-vars": ["error", { "ignoreRestSiblings": true }], 30 | "react/prop-types": "off", 31 | "prefer-object-spread": "warn", 32 | "react-hooks/rules-of-hooks": "error", 33 | "react/jsx-no-constructed-context-values": "error" 34 | }, 35 | "globals": { 36 | "process": false 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf whitespace= 2 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | types: [opened, synchronize] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v3 15 | 16 | - name: Setup Node 17 | uses: actions/setup-node@v3 18 | with: 19 | node-version: lts/* 20 | cache: 'npm' 21 | 22 | - name: Install dependencies 23 | run: npm ci 24 | 25 | - name: Build 26 | run: npm run build:prod 27 | 28 | - name: Lint 29 | run: npm run lint 30 | 31 | - name: Test 32 | run: npm run test:coverage 33 | 34 | - name: Upload coverage 35 | uses: codecov/codecov-action@v2 36 | 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # Editors 61 | .idea 62 | 63 | # react-jsx-highcharts 64 | dist 65 | /examples/*/bundle.js 66 | /examples/*/index.html 67 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "tabWidth": 2, 4 | "trailingComma": "none", 5 | "arrowParens": "avoid", 6 | "endOfLine": "auto" 7 | } 8 | -------------------------------------------------------------------------------- /.vs/React-HighCharts/v17/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ITopGun/React-HighCharts/4b3d9dd6d051e1bfbd8de1ce0fd01583983db3a3/.vs/React-HighCharts/v17/.suo -------------------------------------------------------------------------------- /.vs/VSWorkspaceState.json: -------------------------------------------------------------------------------- 1 | { 2 | "ExpandedNodes": [ 3 | "" 4 | ], 5 | "PreviewInSolutionExplorer": false 6 | } -------------------------------------------------------------------------------- /.vs/slnx.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ITopGun/React-HighCharts/4b3d9dd6d051e1bfbd8de1ce0fd01583983db3a3/.vs/slnx.sqlite -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### Bug or Feature Request? 2 | 3 | 4 | #### Description 5 | 6 | 7 | #### How to reproduce 8 | 9 | 10 | #### Live demo demonstrating bug 11 | 12 | 13 | #### Versions 14 | * React JSX Highcharts version: 15 | * Highcharts version: 16 | * React version: 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Will Hawker 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 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | const ENV = process.env.BABEL_ENV || process.env.NODE_ENV || 'development'; 3 | 4 | let config = { 5 | plugins: [], 6 | presets: [ 7 | [ 8 | '@babel/env', 9 | { modules: ENV === 'test' ? 'commonjs' : false, bugfixes: true } 10 | ], 11 | ['@babel/react', { runtime: ENV === 'es' ? 'automatic' : 'classic' }] 12 | ] 13 | }; 14 | 15 | module.exports = config; 16 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React Highcharts JSX 7 | 26 | 27 | 28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build:prod": "npm run build:prod --workspaces", 5 | "build:jsx-highcharts": "npm run build:prod --workspace packages/react-jsx-highcharts", 6 | "lint": "eslint \"packages/*/+(src|test)/**\"", 7 | "test": "npm run build:jsx-highcharts && jest", 8 | "test:coverage": "npm run build:jsx-highcharts && jest --coverage", 9 | "test:watch": "jest --watch" 10 | }, 11 | "devDependencies": { 12 | "@babel/cli": "^7.19.3", 13 | "@babel/core": "^7.20.5", 14 | "@babel/eslint-parser": "^7.19.1", 15 | "@babel/preset-env": "^7.20.2", 16 | "@babel/preset-react": "^7.18.6", 17 | "@testing-library/react": "^13.4.0", 18 | "@types/react": "^18.0.26", 19 | "babel-loader": "^9.1.0", 20 | "cross-env": "^7.0.3", 21 | "eslint": "^8.29.0", 22 | "eslint-config-prettier": "^8.5.0", 23 | "eslint-plugin-import": "^2.26.0", 24 | "eslint-plugin-prettier": "^4.2.1", 25 | "eslint-plugin-react": "^7.31.11", 26 | "eslint-plugin-react-hooks": "^4.5.0", 27 | "eslint-plugin-react-perf": "^3.3.1", 28 | "highcharts": "^10.3.2", 29 | "jest": "^29.3.1", 30 | "jest-environment-jsdom": "^29.3.1", 31 | "prettier": "^2.8.1", 32 | "react": "^18.2.0", 33 | "react-dom": "^18.2.0", 34 | "react-test-renderer": "^18.2.0", 35 | "rimraf": "^3.0.2", 36 | "typescript": "^4.9.4", 37 | "webpack": "^5.75.0", 38 | "webpack-cli": "^5.0.1" 39 | }, 40 | "workspaces": { 41 | "packages": [ 42 | "packages/react-jsx-highcharts", 43 | "packages/react-jsx-highstock", 44 | "packages/react-jsx-highmaps" 45 | ] 46 | }, 47 | "jest": { 48 | "projects": [ 49 | "/packages/*" 50 | ] 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/.babelrc: -------------------------------------------------------------------------------- 1 | { "extends": "../../babel.config.js" } 2 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/jest.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | // For a detailed explanation regarding each configuration property, visit: 3 | // https://jestjs.io/docs/en/configuration.html 4 | 5 | /** @type {import('@jest/types').Config.InitialOptions} */ 6 | const config = { 7 | testEnvironment: 'jsdom', 8 | setupFilesAfterEnv: ['/test/test-helper.js'], 9 | testMatch: ['**/test/**/*.spec.js?(x)'] 10 | }; 11 | 12 | module.exports = config; 13 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-jsx-highcharts", 3 | "version": "5.0.0", 4 | "description": "Highcharts charts built using React components", 5 | "main": "dist/react-jsx-highcharts.min.js", 6 | "module": "dist/es/index.js", 7 | "types": "types/index.d.ts", 8 | "sideEffects": false, 9 | "files": [ 10 | "dist", 11 | "src", 12 | "types" 13 | ], 14 | "scripts": { 15 | "build": "cross-env NODE_ENV=development webpack", 16 | "build:prod": "npm run build:umd && npm run build:es", 17 | "build:umd": "cross-env NODE_ENV=production webpack", 18 | "build:es": "cross-env BABEL_ENV=es babel src --out-dir dist/es", 19 | "clean": "rimraf dist", 20 | "format": "prettier --write \"src/**/*.js\" \"test/**/*.js\" README.md \"../../README.md\"", 21 | "lint": "eslint src", 22 | "test": "jest", 23 | "test:coverage": "jest --coverage", 24 | "test:types": "tsc --noEmit" 25 | }, 26 | "author": "Will Hawker", 27 | "contributors": [ 28 | { 29 | "name": "Alex Mayants", 30 | "url": "https://github.com/AlexMayants" 31 | }, 32 | { 33 | "name": "mrawdon", 34 | "url": "https://github.com/mrawdon" 35 | }, 36 | { 37 | "name": "Ercan Akyürek", 38 | "url": "https://github.com/geforcefan" 39 | }, 40 | { 41 | "name": "anajavi", 42 | "url": "https://github.com/anajavi" 43 | } 44 | ], 45 | "license": "MIT", 46 | "repository": { 47 | "type": "git", 48 | "url": "https://github.com/whawker/react-jsx-highcharts.git" 49 | }, 50 | "bugs": "https://github.com/whawker/react-jsx-highcharts/issues", 51 | "homepage": "https://github.com/whawker/react-jsx-highcharts", 52 | "keywords": [ 53 | "react", 54 | "reactjs", 55 | "react-component", 56 | "highcharts", 57 | "highstock", 58 | "chart", 59 | "charts", 60 | "graphs", 61 | "visualization", 62 | "data" 63 | ], 64 | "dependencies": { 65 | "uuid": "^9.0.0" 66 | }, 67 | "peerDependencies": { 68 | "highcharts": "^9.1.2 || ^10.0.0", 69 | "react": "^17.0.0 || ^18.0.0", 70 | "react-dom": "^17.0.0 || ^18.0.0" 71 | }, 72 | "browserslist": [ 73 | "chrome >= 81", 74 | "edge >= 81", 75 | "firefox >= 78", 76 | "ios >= 12", 77 | "safari >= 12" 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Annotation/Annotation.js: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect, memo } from 'react'; 2 | import { v4 as uuid } from 'uuid'; 3 | import { logModuleErrorMessage } from '../../utils/warnings'; 4 | import useChart from '../UseChart'; 5 | 6 | const Annotation = memo(props => { 7 | const { id = uuid, children, ...rest } = props; 8 | 9 | const { addAnnotation, removeAnnotation } = useChart(); 10 | 11 | if (process.env.NODE_ENV === 'development') { 12 | if (addAnnotation === null) { 13 | logModuleErrorMessage('', 'annotations'); 14 | } 15 | } 16 | 17 | const idRef = useRef(); 18 | 19 | useEffect(() => { 20 | idRef.current = typeof id === 'function' ? id() : id; 21 | const myId = idRef.current; 22 | const opts = { 23 | id: myId, 24 | ...rest 25 | }; 26 | addAnnotation(opts); 27 | 28 | return () => { 29 | try { 30 | removeAnnotation(myId); 31 | } catch { 32 | // ignoring as parent chart might be unmounted 33 | } 34 | }; 35 | }); 36 | 37 | return null; 38 | }); 39 | 40 | Annotation.displayName = 'Annotation'; 41 | 42 | export default Annotation; 43 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Annotation/index.js: -------------------------------------------------------------------------------- 1 | import Annotation from './Annotation'; 2 | export default Annotation; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Axis/Axis.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useEffect, useState, useRef } from 'react'; 3 | import { v4 as uuid } from 'uuid'; 4 | import AxisContext from '../AxisContext'; 5 | import { getNonEventHandlerProps, getEventsConfig } from '../../utils/events'; 6 | import useModifiedProps from '../UseModifiedProps'; 7 | import useChart from '../UseChart'; 8 | import createProvidedAxis from './createProvidedAxis'; 9 | 10 | const Axis = ({ children = null, dynamicAxis = true, ...restProps }) => { 11 | const chart = useChart(); 12 | const axisRef = useRef(null); 13 | const providedAxisRef = useRef(null); 14 | const [hasAxis, setHasAxis] = useState(false); 15 | 16 | useEffect(() => { 17 | const axis = createAxis(chart, restProps, dynamicAxis); 18 | axisRef.current = axis; 19 | providedAxisRef.current = createProvidedAxis(axisRef.current); 20 | setHasAxis(true); 21 | chart.needsRedraw(); 22 | 23 | return () => { 24 | if (axis.remove && dynamicAxis) { 25 | try { 26 | axis.remove.bind(axis)(false); 27 | } catch { 28 | // Axis may have already been removed, i.e. when Chart unmounted 29 | } 30 | chart.needsRedraw(); 31 | } 32 | }; 33 | }, []); 34 | 35 | const modifiedProps = useModifiedProps(restProps); 36 | useEffect(() => { 37 | if (!hasAxis) return; 38 | if (modifiedProps !== false) { 39 | const axis = axisRef.current; 40 | const nonEventProps = getNonEventHandlerProps(modifiedProps); 41 | const events = getEventsConfig(restProps); // update all events to be on safeside 42 | const updateProps = { 43 | events, 44 | ...nonEventProps 45 | }; 46 | // if there are plotlines or bands, the chart needs to be redrawn before 47 | // they can be accessed 48 | if (axis.plotLinesAndBands && axis.plotLinesAndBands.length > 0) { 49 | axis.update(updateProps, true); 50 | } else { 51 | axis.update(updateProps, false); 52 | chart.needsRedraw(); 53 | } 54 | } 55 | }); 56 | 57 | if (!hasAxis) return null; 58 | 59 | return ( 60 | 61 | {children} 62 | 63 | ); 64 | }; 65 | 66 | const getAxisConfig = props => { 67 | const { id = uuid, ...rest } = props; 68 | 69 | const axisId = typeof id === 'function' ? id() : id; 70 | const nonEventProps = getNonEventHandlerProps(rest); 71 | const events = getEventsConfig(rest); 72 | 73 | return { 74 | id: axisId, 75 | title: { text: null }, 76 | events, 77 | ...nonEventProps 78 | }; 79 | }; 80 | 81 | const createAxis = (chart, props, dynamicAxis) => { 82 | const { id = uuid, isX } = props; 83 | 84 | // Create Highcharts Axis 85 | const opts = getAxisConfig(props); 86 | let axis; 87 | if (dynamicAxis) { 88 | axis = chart.addAxis(opts, isX, false); 89 | } else { 90 | // ZAxis cannot be added dynamically, Maps only have a single axes - update instead 91 | const axisId = typeof id === 'function' ? id() : id; 92 | axis = chart.get(axisId); 93 | axis.update.call(axis, opts, false); 94 | } 95 | return axis; 96 | }; 97 | 98 | export default Axis; 99 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Axis/AxisTitle.js: -------------------------------------------------------------------------------- 1 | import { useEffect, memo } from 'react'; 2 | import useAxis from '../UseAxis'; 3 | 4 | const AxisTitle = memo(({ children: text, axisId, ...restProps }) => { 5 | const axis = useAxis(axisId); 6 | 7 | useEffect(() => { 8 | if (axis) { 9 | updateAxisTitle({ text, ...restProps }, axis); 10 | } 11 | }); 12 | 13 | useEffect(() => { 14 | return () => { 15 | if (axis) { 16 | try { 17 | updateAxisTitle({ text: null }, axis); 18 | } catch { 19 | // ignore as axis might have been already unmounted 20 | } 21 | } 22 | }; 23 | }, [axis]); 24 | 25 | return null; 26 | }); 27 | 28 | const updateAxisTitle = (config, axis) => { 29 | axis.setTitle(config, true); 30 | }; 31 | 32 | AxisTitle.displayName = 'AxisTitle'; 33 | 34 | export default AxisTitle; 35 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Axis/createProvidedAxis.js: -------------------------------------------------------------------------------- 1 | const createProvidedAxis = axis => { 2 | if (!axis) return null; 3 | 4 | return { 5 | object: axis, 6 | id: axis.userOptions && axis.userOptions.id, 7 | type: axis.coll, 8 | update: axis.update.bind(axis), 9 | remove: axis.remove.bind(axis), 10 | addPlotBandOrLine: axis.addPlotBandOrLine.bind(axis), 11 | removePlotBandOrLine: axis.removePlotBandOrLine.bind(axis), 12 | getExtremes: axis.getExtremes.bind(axis), 13 | setExtremes: axis.setExtremes.bind(axis), 14 | setTitle: axis.setTitle.bind(axis) 15 | }; 16 | }; 17 | 18 | export default createProvidedAxis; 19 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Axis/index.js: -------------------------------------------------------------------------------- 1 | import Axis from './Axis'; 2 | import AxisTitle from './AxisTitle'; 3 | const ChartAxis = Axis; 4 | ChartAxis.Title = AxisTitle; 5 | export default ChartAxis; 6 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/AxisContext/index.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const AxisContext = createContext(); 4 | AxisContext.displayName = 'AxisContext'; 5 | 6 | export default AxisContext; 7 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/BarSeries/BarSeries.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useEffect } from 'react'; 3 | import Series from '../Series'; 4 | import useChart from '../UseChart'; 5 | 6 | const BarSeries = props => { 7 | const chart = useChart(); 8 | 9 | useEffect(() => { 10 | chart.update({ chart: { inverted: true } }); 11 | }, []); 12 | 13 | return ; 14 | }; 15 | 16 | export default BarSeries; 17 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/BarSeries/index.js: -------------------------------------------------------------------------------- 1 | import BarSeries from './BarSeries'; 2 | export default BarSeries; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/BaseChart/BaseChart.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useState, useEffect, useRef, useLayoutEffect } from 'react'; 3 | import ChartContext from '../ChartContext'; 4 | import usePrevious from '../UsePrevious'; 5 | import createProvidedChart from './createProvidedChart'; 6 | 7 | const noop = c => c; 8 | 9 | const BaseChart = ({ 10 | children = null, 11 | callback = noop, 12 | className = '', 13 | containerProps = null, 14 | ...restProps 15 | }) => { 16 | const [rendered, setRendered] = useState(false); 17 | const domNodeRef = useRef(null); 18 | const chartRef = useRef(null); 19 | const providedChartRef = useRef(null); 20 | 21 | useLayoutEffect(() => { 22 | const myChart = initHighcharts(restProps, domNodeRef.current); 23 | chartRef.current = myChart; 24 | providedChartRef.current = createProvidedChart( 25 | myChart, 26 | restProps.chartType 27 | ); 28 | 29 | callback(myChart); 30 | setRendered(true); 31 | }, []); 32 | 33 | useEffect(() => { 34 | const myChart = chartRef.current; 35 | return () => { 36 | if (myChart) { 37 | myChart.destroy.bind(myChart)(); 38 | myChart.__destroyed = true; 39 | } 40 | }; 41 | }, []); 42 | 43 | const prevProps = usePrevious(restProps); 44 | useEffect(() => { 45 | if (!rendered) return; 46 | const { plotOptions } = restProps; 47 | const myChart = chartRef.current; 48 | 49 | if (Object.is(prevProps.plotOptions, plotOptions) === false && myChart) { 50 | myChart.update({ plotOptions }, false); 51 | providedChartRef.current.needsRedraw(); 52 | } 53 | }); 54 | 55 | return ( 56 |
57 | {rendered && ( 58 | 59 | {children} 60 | 61 | )} 62 |
63 | ); 64 | }; 65 | 66 | const initHighcharts = (props, domNode) => { 67 | if (!domNode) { 68 | return; 69 | } 70 | 71 | const { 72 | chartCreationFunc, 73 | callback, 74 | chart, 75 | polar, 76 | gauge, 77 | styledMode = false, 78 | children, 79 | ...rest 80 | } = props; 81 | 82 | const opts = { 83 | chart: { 84 | styledMode, 85 | ...chart 86 | }, 87 | title: { 88 | text: null 89 | }, 90 | subtitle: { 91 | text: null 92 | }, 93 | legend: { 94 | enabled: false 95 | }, 96 | rangeSelector: { 97 | enabled: false 98 | }, 99 | navigator: { 100 | enabled: false 101 | }, 102 | scrollbar: { 103 | enabled: false 104 | }, 105 | tooltip: { 106 | enabled: false 107 | }, 108 | credits: { 109 | enabled: false 110 | }, 111 | series: [], 112 | xAxis: [], 113 | yAxis: [], 114 | ...rest 115 | }; 116 | const myChart = chartCreationFunc(domNode, opts); 117 | 118 | myChart.polar = polar; 119 | myChart.angular = gauge; 120 | 121 | return myChart; 122 | }; 123 | 124 | export default BaseChart; 125 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/BaseChart/createProvidedChart.js: -------------------------------------------------------------------------------- 1 | import debounce from '../../utils/debounce-raf'; 2 | 3 | const createProvidedChart = (chart, type) => ({ 4 | object: chart, 5 | type, 6 | get: chart.get.bind(chart), 7 | setSize: chart.setSize.bind(chart), 8 | update: chart.update.bind(chart), 9 | addAxis: chart.addAxis.bind(chart), 10 | addColorAxis: chart.addColorAxis.bind(chart), 11 | addSeries: chart.addSeries.bind(chart), 12 | setTitle: chart.setTitle.bind(chart), 13 | setCaption: chart.setCaption.bind(chart), 14 | showLoading: chart.showLoading.bind(chart), 15 | hideLoading: chart.hideLoading.bind(chart), 16 | addCredits: chart.addCredits.bind(chart), 17 | addAnnotation: chart.addAnnotation ? chart.addAnnotation.bind(chart) : null, 18 | removeAnnotation: chart.removeAnnotation 19 | ? chart.removeAnnotation.bind(chart) 20 | : null, 21 | needsRedraw: debounce(() => { 22 | if (!chart.__destroyed) { 23 | try { 24 | chart.redraw.bind(chart)(); 25 | } catch { 26 | // ignore 27 | } 28 | } 29 | }) 30 | }); 31 | 32 | export default createProvidedChart; 33 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/BaseChart/index.js: -------------------------------------------------------------------------------- 1 | import BaseChart from './BaseChart'; 2 | export default BaseChart; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Caption/Caption.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import useChartUpdate from '../UseChartUpdate'; 3 | const Caption = memo(props => { 4 | useChartUpdate(props, updateCaption, chart => 5 | updateCaption(chart, { text: null }) 6 | ); 7 | 8 | return null; 9 | }); 10 | 11 | const updateCaption = (chart, config) => { 12 | chart.setCaption(config); 13 | }; 14 | 15 | Caption.displayName = 'Caption'; 16 | 17 | export default Caption; 18 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Caption/index.js: -------------------------------------------------------------------------------- 1 | import Caption from './Caption'; 2 | export default Caption; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Chart/Chart.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, memo } from 'react'; 2 | import { getNonEventHandlerProps } from '../../utils/events'; 3 | import useModifiedProps from '../UseModifiedProps'; 4 | import useChart from '../UseChart'; 5 | import useManualEventHandlers from '../UseManualEventHandlers'; 6 | 7 | const Chart = memo(({ type = 'line', width, height, ...restProps }) => { 8 | const chart = useChart(); 9 | const mounted = useRef(false); 10 | 11 | const modifiedProps = useModifiedProps({ type, ...restProps }); 12 | 13 | useEffect(() => { 14 | if (!(width === undefined && height === undefined)) { 15 | chart.setSize(width, height); 16 | } 17 | }, [width, height]); 18 | 19 | useEffect(() => { 20 | if (modifiedProps !== false && mounted.current) { 21 | const notEventProps = getNonEventHandlerProps(modifiedProps); 22 | if (Object.getOwnPropertyNames(notEventProps).length > 0) { 23 | updateChart(modifiedProps, chart, chart.needsRedraw); 24 | } 25 | } 26 | }); 27 | 28 | useEffect(() => { 29 | const notEventProps = getNonEventHandlerProps({ type, ...restProps }); 30 | 31 | updateChart(notEventProps, chart); 32 | mounted.current = true; 33 | }, []); 34 | 35 | useManualEventHandlers(restProps, chart.object); 36 | 37 | return null; 38 | }); 39 | 40 | const updateChart = (config, chart) => { 41 | chart.update({ chart: config }, false); 42 | chart.needsRedraw(); 43 | }; 44 | 45 | Chart.displayName = 'Chart'; 46 | 47 | export default Chart; 48 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Chart/index.js: -------------------------------------------------------------------------------- 1 | import Chart from './Chart'; 2 | export default Chart; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/ChartContext/index.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const ChartContext = createContext(); 4 | ChartContext.displayName = 'ChartContext'; 5 | 6 | export default ChartContext; 7 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/ColorAxis/ColorAxis.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useEffect, useRef, useState } from 'react'; 3 | import { v4 as uuid } from 'uuid'; 4 | import { getNonEventHandlerProps, getEventsConfig } from '../../utils/events'; 5 | import ColorAxisContext from '../ColorAxisContext'; 6 | import useModifiedProps from '../UseModifiedProps'; 7 | import useChart from '../UseChart'; 8 | import createProvidedColorAxis from './createProvidedColorAxis'; 9 | 10 | const ColorAxis = ({ children = null, ...restProps }) => { 11 | const chart = useChart(); 12 | const colorAxisRef = useRef(null); 13 | const providedColorAxisRef = useRef(null); 14 | const [hasColorAxis, setHasColorAxis] = useState(false); 15 | 16 | useEffect(() => { 17 | const colorAxis = createColorAxis(chart, restProps); 18 | colorAxisRef.current = colorAxis; 19 | providedColorAxisRef.current = createProvidedColorAxis( 20 | colorAxisRef.current 21 | ); 22 | setHasColorAxis(true); 23 | chart.needsRedraw(); 24 | 25 | return () => { 26 | if (colorAxis && colorAxis.remove) { 27 | try { 28 | colorAxis.remove.bind(colorAxis)(false); 29 | } catch { 30 | // Axis may have already been removed, i.e. when Chart unmounted 31 | } 32 | chart.needsRedraw(); 33 | } 34 | }; 35 | }, []); 36 | 37 | const modifiedProps = useModifiedProps(restProps); 38 | 39 | useEffect(() => { 40 | if (colorAxisRef.current !== null && modifiedProps !== false) { 41 | const colorAxis = colorAxisRef.current; 42 | colorAxis.update(modifiedProps, false); 43 | chart.needsRedraw(); 44 | } 45 | }); 46 | 47 | if (!hasColorAxis) return null; 48 | 49 | return ( 50 | 51 | {children} 52 | 53 | ); 54 | }; 55 | 56 | const getColorAxisConfig = props => { 57 | const { id = uuid, ...rest } = props; 58 | 59 | const colorAxisId = typeof id === 'function' ? id() : id; 60 | const nonEventProps = getNonEventHandlerProps(rest); 61 | const events = getEventsConfig(rest); 62 | 63 | return { 64 | id: colorAxisId, 65 | events, 66 | ...nonEventProps 67 | }; 68 | }; 69 | 70 | const createColorAxis = (chart, props) => { 71 | const opts = getColorAxisConfig(props); 72 | return chart.addColorAxis(opts, false); 73 | }; 74 | 75 | export default ColorAxis; 76 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/ColorAxis/createProvidedColorAxis.js: -------------------------------------------------------------------------------- 1 | const createProvidedColorAxis = colorAxis => { 2 | if (!colorAxis) return null; 3 | 4 | return { 5 | object: colorAxis, 6 | id: colorAxis.userOptions && colorAxis.userOptions.id 7 | }; 8 | }; 9 | 10 | export default createProvidedColorAxis; 11 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/ColorAxis/index.js: -------------------------------------------------------------------------------- 1 | import ColorAxis from './ColorAxis'; 2 | 3 | export default ColorAxis; 4 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/ColorAxisContext/index.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const ColorAxisContext = createContext(); 4 | ColorAxisContext.displayName = 'ColorAxisContext'; 5 | 6 | export default ColorAxisContext; 7 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Credits/Credits.js: -------------------------------------------------------------------------------- 1 | import useChartUpdate from '../UseChartUpdate'; 2 | 3 | const Credits = ({ enabled = true, ...restProps }) => { 4 | useChartUpdate({ enabled, ...restProps }, updateCredits, chart => 5 | updateCredits(chart, { enabled: false }) 6 | ); 7 | 8 | return null; 9 | }; 10 | 11 | const updateCredits = (chart, config) => { 12 | // Use default Highcharts value if text is not explicitly set 13 | if ('text' in config && !config.text) delete config.text; 14 | chart.addCredits(config, true); 15 | }; 16 | 17 | export default Credits; 18 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Credits/index.js: -------------------------------------------------------------------------------- 1 | import Credits from './Credits'; 2 | export default Credits; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Debug/Debug.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import useChart from '../UseChart'; 3 | 4 | const Debug = ({ varName = 'chart' }) => { 5 | const chart = useChart(); 6 | 7 | useEffect(() => { 8 | window[varName] = chart.object; 9 | // eslint-disable-next-line no-console 10 | console.log( 11 | `Chart instance available as global variable as window.${varName}` 12 | ); 13 | 14 | return () => { 15 | window[varName] = undefined; 16 | }; 17 | }, [varName]); 18 | 19 | return null; 20 | }; 21 | 22 | export default Debug; 23 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Debug/index.js: -------------------------------------------------------------------------------- 1 | import Debug from './Debug'; 2 | export default Debug; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Highcharts3dChart/Highcharts3dChart.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import HighchartsChart from '../HighchartsChart'; 3 | import Options3d from '../Options3d'; 4 | 5 | const CHART = { 6 | options3d: { enabled: true } 7 | }; 8 | const ZAXIS = { 9 | id: 'zAxis' 10 | }; 11 | 12 | const Highcharts3dChart = ({ 13 | children, 14 | alpha, 15 | axisLabelPosition, 16 | beta, 17 | depth, 18 | fitToPlot, 19 | frame, 20 | viewDistance, 21 | ...rest 22 | }) => ( 23 | 24 | 33 | {children} 34 | 35 | ); 36 | Highcharts3dChart.propTypes = Options3d.propTypes; 37 | 38 | export default Highcharts3dChart; 39 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Highcharts3dChart/index.js: -------------------------------------------------------------------------------- 1 | import Highcharts3dChart from './Highcharts3dChart'; 2 | export default Highcharts3dChart; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/HighchartsChart/HighchartsChart.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import BaseChart from '../BaseChart'; 3 | import useHighcharts from '../UseHighcharts'; 4 | 5 | const HighchartsChart = props => { 6 | const Highcharts = useHighcharts(); 7 | 8 | return ( 9 | 14 | ); 15 | }; 16 | 17 | export default HighchartsChart; 18 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/HighchartsChart/index.js: -------------------------------------------------------------------------------- 1 | import HighchartsChart from './HighchartsChart'; 2 | export default HighchartsChart; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/HighchartsContext/index.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const HighchartsContext = createContext(); 4 | HighchartsContext.displayName = 'HighchartsContext'; 5 | export default HighchartsContext; 6 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/HighchartsSparkline/HighchartsSparkline.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useMemo } from 'react'; 3 | import HighchartsChart from '../HighchartsChart'; 4 | import Chart from '../Chart'; 5 | import XAxis from '../XAxis'; 6 | import YAxis from '../YAxis'; 7 | 8 | const defaultSparklinePlotOptions = { 9 | series: { 10 | animation: false, 11 | lineWidth: 1, 12 | shadow: false, 13 | states: { 14 | hover: { 15 | lineWidth: 1 16 | } 17 | }, 18 | marker: { 19 | radius: 1, 20 | states: { 21 | hover: { 22 | radius: 2 23 | } 24 | } 25 | }, 26 | fillOpacity: 0.25 27 | } 28 | }; 29 | 30 | const EMPTY_ARRAY = []; 31 | const EMPTY_OBJECT = {}; 32 | const ZERO_ARRAY = [0]; 33 | const LABELS_DISABLED = { enabled: false }; 34 | const DEFAULT_MARGIN = [2, 0, 2, 0]; 35 | 36 | const HighchartsSparkline = ({ 37 | height = 20, 38 | width = 120, 39 | margin = DEFAULT_MARGIN, 40 | style = EMPTY_OBJECT, 41 | series, 42 | children, 43 | plotOptions = defaultSparklinePlotOptions, 44 | ...rest 45 | }) => { 46 | const chartStyle = useMemo( 47 | () => ({ overflow: 'visible', ...style }), 48 | [style] 49 | ); 50 | 51 | const hasSeriesProp = !!series; 52 | // If you want to use functionality like Tooltips, pass the data component on the `series` prop 53 | const Series = hasSeriesProp ? series : children; 54 | 55 | return ( 56 | 57 | 67 | 68 | 74 | 75 | 82 | {Series} 83 | 84 | 85 | {hasSeriesProp && <>{children}} 86 | 87 | ); 88 | }; 89 | 90 | export default HighchartsSparkline; 91 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/HighchartsSparkline/index.js: -------------------------------------------------------------------------------- 1 | import HighchartsSparkline from './HighchartsSparkline'; 2 | export default HighchartsSparkline; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Legend/Legend.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import useChartUpdate from '../UseChartUpdate'; 3 | 4 | const Legend = memo(({ children = null, enabled = true, ...restProps }) => { 5 | useChartUpdate( 6 | { enabled, ...restProps }, 7 | updateLegend, 8 | chart => updateLegend(chart, { enabled: false }), 9 | false 10 | ); 11 | 12 | return children; 13 | }); 14 | const updateLegend = (chart, config) => { 15 | chart.update({ legend: config }, false); 16 | }; 17 | 18 | Legend.displayName = 'Legend'; 19 | 20 | export default Legend; 21 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Legend/LegendTitle.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import useChartUpdate from '../UseChartUpdate'; 3 | 4 | const LegendTitle = memo(props => { 5 | useChartUpdate(props, updateLegendTitle, chart => 6 | updateLegendTitle(chart, { text: null }) 7 | ); 8 | 9 | return null; 10 | }); 11 | 12 | const updateLegendTitle = (chart, config) => { 13 | chart.update( 14 | { 15 | legend: { 16 | title: config 17 | } 18 | }, 19 | false 20 | ); 21 | }; 22 | 23 | LegendTitle.displayName = 'LegendTitle'; 24 | 25 | export default LegendTitle; 26 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Legend/index.js: -------------------------------------------------------------------------------- 1 | import Legend from './Legend'; 2 | import LegendTitle from './LegendTitle'; 3 | const ChartLegend = Legend; 4 | ChartLegend.Title = LegendTitle; 5 | export default ChartLegend; 6 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Loading/Loading.js: -------------------------------------------------------------------------------- 1 | import { useEffect, memo } from 'react'; 2 | import useModifiedProps from '../UseModifiedProps'; 3 | import useChart from '../UseChart'; 4 | 5 | const Loading = memo(({ children, isLoading = true, ...restProps }) => { 6 | const chart = useChart(); 7 | const modifiedProps = useModifiedProps(restProps); 8 | 9 | useEffect(() => { 10 | if (modifiedProps !== false) { 11 | updateLoading(modifiedProps, chart); 12 | } 13 | isLoading ? chart.showLoading(children) : chart.hideLoading(); 14 | }); 15 | 16 | useEffect(() => { 17 | return () => { 18 | try { 19 | chart.hideLoading(); 20 | } catch { 21 | // ignore as chart might have been unmounted 22 | } 23 | }; 24 | }, []); 25 | 26 | return null; 27 | }); 28 | 29 | const updateLoading = (config, chart) => { 30 | chart.update({ loading: config }, true); 31 | }; 32 | 33 | Loading.displayName = 'Loading'; 34 | export default Loading; 35 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Loading/index.js: -------------------------------------------------------------------------------- 1 | import Loading from './Loading'; 2 | export default Loading; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Options3d/Options3d.js: -------------------------------------------------------------------------------- 1 | import { useEffect, memo } from 'react'; 2 | import { log3DModuleErrorMessage } from '../../utils/warnings'; 3 | import useHighcharts from '../UseHighcharts'; 4 | import useChart from '../UseChart'; 5 | 6 | const DEFAULT_FRAME = { 7 | visible: 'default', 8 | size: 1, 9 | bottom: {}, 10 | top: {}, 11 | left: {}, 12 | right: {}, 13 | back: {}, 14 | front: {} 15 | }; 16 | 17 | const Options3d = memo( 18 | ({ 19 | enabled = false, 20 | alpha = 0, 21 | beta = 0, 22 | depth = 100, 23 | fitToPlot = true, 24 | viewDistance = 25, 25 | axisLabelPosition = 'default', 26 | frame = DEFAULT_FRAME, 27 | ...restProps 28 | }) => { 29 | const props = { 30 | enabled, 31 | alpha, 32 | beta, 33 | depth, 34 | fitToPlot, 35 | viewDistance, 36 | axisLabelPosition, 37 | frame, 38 | ...restProps 39 | }; 40 | const Highcharts = useHighcharts(); 41 | const chart = useChart(); 42 | 43 | if (process.env.NODE_ENV === 'development') { 44 | if (!Highcharts.ZAxis) log3DModuleErrorMessage(); 45 | } 46 | 47 | useEffect(() => { 48 | update3dOptions(chart, props); 49 | }); 50 | 51 | return null; 52 | } 53 | ); 54 | 55 | const update3dOptions = (chart, props) => { 56 | const { 57 | alpha, 58 | axisLabelPosition, 59 | beta, 60 | depth, 61 | fitToPlot, 62 | frame, 63 | viewDistance 64 | } = props; 65 | 66 | const opts = { 67 | chart: { 68 | options3d: { 69 | enabled: true, 70 | alpha, 71 | axisLabelPosition, 72 | beta, 73 | depth, 74 | fitToPlot, 75 | frame, 76 | viewDistance 77 | } 78 | } 79 | }; 80 | chart.update(opts, true); 81 | }; 82 | 83 | Options3d.displayName = 'Options3d'; 84 | 85 | export default Options3d; 86 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Options3d/index.js: -------------------------------------------------------------------------------- 1 | import Options3d from './Options3d'; 2 | export default Options3d; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Pane/Pane.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import useChartUpdate from '../UseChartUpdate'; 3 | 4 | const Pane = memo(({ children, ...restProps }) => { 5 | useChartUpdate(restProps, updatePane, chart => updatePane(chart, {}), false); 6 | 7 | return null; 8 | }); 9 | 10 | const updatePane = (chart, config) => { 11 | chart.update({ pane: config }, false); 12 | }; 13 | Pane.displayName = 'Pane'; 14 | 15 | export default Pane; 16 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Pane/index.js: -------------------------------------------------------------------------------- 1 | import Pane from './Pane'; 2 | export default Pane; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/PlotBandLine/PlotBand.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { memo } from 'react'; 3 | import PlotBandLineContext from '../PlotBandLineContext'; 4 | import usePlotBandLineLifecycle from './UsePlotBandLineLifecycle'; 5 | 6 | const PlotBand = memo(props => { 7 | const plotband = usePlotBandLineLifecycle(props, 'plotBands'); 8 | 9 | const { children } = props; 10 | 11 | if (!children && !plotband) return null; 12 | 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | }); 19 | 20 | PlotBand.displayName = 'PlotBand'; 21 | export default PlotBand; 22 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/PlotBandLine/PlotBandLineLabel.js: -------------------------------------------------------------------------------- 1 | import { useEffect, memo } from 'react'; 2 | import usePlotBandLine from '../UsePlotBandLine'; 3 | 4 | const PlotBandLineLabel = memo(props => { 5 | const providedPlotbandline = usePlotBandLine(); 6 | 7 | useEffect(() => { 8 | if (!providedPlotbandline) return; 9 | const { children: text, id, ...rest } = props; 10 | updatePlotBandLineLabel(providedPlotbandline.object, { 11 | text, 12 | ...rest 13 | }); 14 | }); 15 | 16 | useEffect(() => { 17 | return () => { 18 | if (!providedPlotbandline) return; 19 | try { 20 | updatePlotBandLineLabel(providedPlotbandline.object, { 21 | text: null 22 | }); 23 | } catch { 24 | // ignore as axis might have been unmounted 25 | } 26 | }; 27 | }, []); 28 | 29 | return null; 30 | }); 31 | 32 | const updatePlotBandLineLabel = (plotbandline, config) => { 33 | if (plotbandline) { 34 | plotbandline.options.label = getLabelProps(config); 35 | plotbandline.render(); 36 | } 37 | }; 38 | 39 | const getLabelProps = props => { 40 | const { 41 | text, 42 | formatter, 43 | align, 44 | rotation, 45 | style, 46 | textAlign, 47 | useHTML, 48 | verticalAlign, 49 | x, 50 | y 51 | } = props; 52 | 53 | return { 54 | text, 55 | formatter, 56 | align, 57 | rotation, 58 | style, 59 | textAlign, 60 | useHTML, 61 | verticalAlign, 62 | x, 63 | y 64 | }; 65 | }; 66 | 67 | PlotBandLineLabel.displayName = 'PlotBandLineLabel'; 68 | 69 | export default PlotBandLineLabel; 70 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/PlotBandLine/PlotLine.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { memo } from 'react'; 3 | import PlotBandLineContext from '../PlotBandLineContext'; 4 | import usePlotBandLineLifecycle from './UsePlotBandLineLifecycle'; 5 | 6 | const PlotLine = memo(props => { 7 | const plotline = usePlotBandLineLifecycle(props, 'plotLines'); 8 | 9 | const { children } = props; 10 | 11 | if (!children && !plotline) return null; 12 | 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | }); 19 | 20 | PlotLine.displayName = 'PlotLine'; 21 | export default PlotLine; 22 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/PlotBandLine/UsePlotBandLineLifecycle.js: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect, useState } from 'react'; 2 | import { v4 as uuid } from 'uuid'; 3 | import useModifiedProps from '../UseModifiedProps'; 4 | import useAxis from '../UseAxis'; 5 | 6 | export default function usePlotBandLineLifecycle(props, plotType) { 7 | const { id = uuid, axisId, children, ...rest } = props; 8 | 9 | const axis = useAxis(axisId); 10 | const idRef = useRef(); 11 | const [plotbandline, setPlotbandline] = useState(null); 12 | const modifiedProps = useModifiedProps(rest); 13 | 14 | useEffect(() => { 15 | if (!axis) return; 16 | if (!plotbandline || modifiedProps !== false) { 17 | if (!plotbandline) { 18 | idRef.current = typeof id === 'function' ? id() : id; 19 | } 20 | const myId = idRef.current; 21 | const opts = { 22 | id: myId, 23 | ...rest 24 | }; 25 | 26 | if (plotbandline) axis.removePlotBandOrLine(idRef.current); 27 | axis.addPlotBandOrLine(opts, plotType); 28 | setPlotbandline({ 29 | id: myId, 30 | get object() { 31 | /* when parent axis is updated, the plotlines and plotbands are recreated 32 | therefore the object can't be cached here 33 | */ 34 | if (axis && axis.object && axis.object.plotLinesAndBands) { 35 | return axis.object.plotLinesAndBands.find(plb => plb.id === myId); 36 | } 37 | return null; 38 | } 39 | }); 40 | } 41 | }); 42 | 43 | useEffect(() => { 44 | return () => { 45 | try { 46 | axis.removePlotBandOrLine(idRef.current); 47 | } catch { 48 | // ignore as axis might have been already unmounted 49 | } 50 | }; 51 | }, []); 52 | 53 | return plotbandline; 54 | } 55 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/PlotBandLine/index.js: -------------------------------------------------------------------------------- 1 | import PlotBand from './PlotBand'; 2 | import PlotLine from './PlotLine'; 3 | import PlotBandLineLabel from './PlotBandLineLabel'; 4 | 5 | const ChartPlotBand = PlotBand; 6 | ChartPlotBand.Label = PlotBandLineLabel; 7 | const ChartPlotLine = PlotLine; 8 | ChartPlotLine.Label = PlotBandLineLabel; 9 | 10 | export { ChartPlotLine as PlotLine }; 11 | export { ChartPlotBand as PlotBand }; 12 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/PlotBandLineContext/index.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const PlotBandLineContext = createContext(); 4 | 5 | PlotBandLineContext.displayName = 'PlotBandLineContext'; 6 | 7 | export default PlotBandLineContext; 8 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Series/createProvidedSeries.js: -------------------------------------------------------------------------------- 1 | export default function createProvidedSeries(series) { 2 | if (!series) return null; 3 | 4 | return { 5 | object: series, 6 | id: series.userOptions && series.userOptions.id, 7 | type: series.type, 8 | update: series.update.bind(series), 9 | remove: series.remove.bind(series), 10 | setData: series.setData.bind(series), 11 | setVisible: series.setVisible.bind(series) 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Series/index.js: -------------------------------------------------------------------------------- 1 | import Series from './Series'; 2 | export default Series; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/SeriesContext/index.js: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const SeriesContext = createContext(); 4 | SeriesContext.displayName = 'SeriesContext'; 5 | 6 | export default SeriesContext; 7 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Subtitle/Subtitle.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import useChartUpdate from '../UseChartUpdate'; 3 | 4 | const Subtitle = memo(props => { 5 | useChartUpdate(props, updateSubtitle, chart => 6 | updateSubtitle(chart, { text: null }) 7 | ); 8 | 9 | return null; 10 | }); 11 | 12 | const updateSubtitle = (chart, config) => { 13 | chart.setTitle(undefined, config, false); 14 | }; 15 | 16 | Subtitle.displayName = 'Subtitle'; 17 | 18 | export default Subtitle; 19 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Subtitle/index.js: -------------------------------------------------------------------------------- 1 | import Subtitle from './Subtitle'; 2 | export default Subtitle; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Title/Title.js: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import useChartUpdate from '../UseChartUpdate'; 3 | 4 | const Title = memo(props => { 5 | useChartUpdate(props, updateTitle, chart => 6 | updateTitle(chart, { text: null }) 7 | ); 8 | 9 | return null; 10 | }); 11 | 12 | const updateTitle = (chart, config) => { 13 | chart.setTitle(config, null, false); 14 | }; 15 | 16 | Title.displayName = 'Title'; 17 | 18 | export default Title; 19 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Title/index.js: -------------------------------------------------------------------------------- 1 | import Title from './Title'; 2 | export default Title; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Tooltip/Tooltip.js: -------------------------------------------------------------------------------- 1 | import { useEffect, memo } from 'react'; 2 | import useChart from '../UseChart'; 3 | import useHighcharts from '../UseHighcharts'; 4 | import useModifiedProps from '../UseModifiedProps'; 5 | 6 | const Tooltip = memo(props => { 7 | // eslint-disable-next-line no-unused-vars 8 | const { children = null, ...restProps } = props; 9 | const chart = useChart(); 10 | const Highcharts = useHighcharts(); 11 | 12 | restProps.enabled = props.enabled ?? true; 13 | 14 | useEffect(() => { 15 | updateTooltip(chart, { 16 | ...(Highcharts.defaultOptions && Highcharts.defaultOptions.tooltip), 17 | ...restProps 18 | }); 19 | 20 | return () => { 21 | try { 22 | updateTooltip(chart, { enabled: false }); 23 | } catch { 24 | // ignore as chart might have been already unmounted 25 | } 26 | }; 27 | }, []); 28 | 29 | const modifiedProps = useModifiedProps(restProps); 30 | useEffect(() => { 31 | if (modifiedProps !== false) { 32 | updateTooltip(chart, modifiedProps); 33 | } 34 | }); 35 | 36 | return null; 37 | }); 38 | 39 | const updateTooltip = (chart, config) => { 40 | chart.update({ 41 | tooltip: config 42 | }); 43 | }; 44 | 45 | Tooltip.displayName = 'Tooltip'; 46 | 47 | export default Tooltip; 48 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/Tooltip/index.js: -------------------------------------------------------------------------------- 1 | import Tooltip from './Tooltip'; 2 | export default Tooltip; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/UseAxis/index.js: -------------------------------------------------------------------------------- 1 | import { useContext, useState, useEffect, useDebugValue } from 'react'; 2 | import AxisContext from '../AxisContext'; 3 | import useChart from '../UseChart'; 4 | import createProvidedAxis from '../Axis/createProvidedAxis'; 5 | 6 | export default function useAxis(axisId) { 7 | const chart = useChart(); 8 | const contextAxis = useContext(AxisContext); 9 | 10 | const createStateAxis = () => { 11 | if (contextAxis) return contextAxis; 12 | 13 | if (axisId) { 14 | const axis = chart.get(axisId); 15 | return createProvidedAxis(axis); 16 | } 17 | return null; 18 | }; 19 | 20 | const [providedAxis, setProvidedAxis] = useState(createStateAxis); 21 | useEffect(() => { 22 | if (providedAxis) return; // we already had axis 23 | // axis should now be created 24 | setProvidedAxis(createStateAxis()); 25 | }, []); 26 | useDebugValue(providedAxis ? providedAxis.id : null); 27 | 28 | return providedAxis; 29 | } 30 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/UseChart/index.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import ChartContext from '../ChartContext'; 3 | 4 | export default function useChart() { 5 | return useContext(ChartContext); 6 | } 7 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/UseChartUpdate/index.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import useChart from '../UseChart'; 3 | import useModifiedProps from '../UseModifiedProps'; 4 | 5 | const noop = c => c; 6 | 7 | const useChartUpdate = ( 8 | props, 9 | updateFn = noop, 10 | destroyFn = noop, 11 | childrenIsText = true 12 | ) => { 13 | const chart = useChart(); 14 | const modifiedProps = useModifiedProps(props, childrenIsText); 15 | 16 | useEffect(() => { 17 | if (modifiedProps !== false) { 18 | updateFn(chart, modifiedProps); 19 | chart.needsRedraw(); 20 | } 21 | }); 22 | 23 | useEffect(() => { 24 | return () => { 25 | try { 26 | destroyFn(chart); 27 | } catch { 28 | // ignore as chart might have been already unmounted 29 | } 30 | chart.needsRedraw(); 31 | }; 32 | }, []); 33 | }; 34 | 35 | export default useChartUpdate; 36 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/UseColorAxis/index.js: -------------------------------------------------------------------------------- 1 | import { useContext, useState, useEffect, useDebugValue } from 'react'; 2 | import ColorAxisContext from '../ColorAxisContext'; 3 | import useChart from '../UseChart'; 4 | import createProvidedColorAxis from '../ColorAxis/createProvidedColorAxis'; 5 | 6 | export default function useColorAxis(colorAxisId) { 7 | const chart = useChart(); 8 | const contextColorAxis = useContext(ColorAxisContext); 9 | 10 | const createStateColorAxis = () => { 11 | if (contextColorAxis) return contextColorAxis; 12 | 13 | if (colorAxisId) { 14 | const colorAxis = chart.get(colorAxisId); 15 | return createProvidedColorAxis(colorAxis); 16 | } 17 | return null; 18 | }; 19 | 20 | const [providedColorAxis, setProvidedColorAxis] = 21 | useState(createStateColorAxis); 22 | 23 | useEffect(() => { 24 | if (providedColorAxis) return; // we already had axis 25 | // axis should now be created 26 | setProvidedColorAxis(createStateColorAxis()); 27 | }, []); 28 | 29 | useDebugValue(providedColorAxis ? providedColorAxis.id : null); 30 | 31 | return providedColorAxis; 32 | } 33 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/UseHighcharts/index.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import HighchartsContext from '../HighchartsContext'; 3 | 4 | export default function useHighcharts() { 5 | return useContext(HighchartsContext); 6 | } 7 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/UseManualEventHandlers/index.js: -------------------------------------------------------------------------------- 1 | import useHighcharts from '../UseHighcharts'; 2 | import usePrevious from '../UsePrevious'; 3 | import { getEventsConfig } from '../../utils/events'; 4 | import getModifiedProps from '../../utils/getModifiedProps'; 5 | 6 | const useManualEventHandlers = function (props, target) { 7 | const Highcharts = useHighcharts(); 8 | const eventHandlers = getEventsConfig(props); 9 | const previousEventHandlers = usePrevious(eventHandlers); 10 | 11 | const modifiedEvenHandlers = getModifiedProps( 12 | previousEventHandlers, 13 | eventHandlers 14 | ); 15 | 16 | if (modifiedEvenHandlers !== false) { 17 | Object.keys(modifiedEvenHandlers).forEach(eventName => { 18 | if (previousEventHandlers) { 19 | const oldHandler = previousEventHandlers[eventName]; 20 | if (oldHandler) { 21 | Highcharts.removeEvent(target, eventName, oldHandler); 22 | } 23 | } 24 | const newHandler = modifiedEvenHandlers[eventName]; 25 | if (newHandler) { 26 | Highcharts.addEvent(target, eventName, newHandler); 27 | } 28 | }); 29 | } 30 | }; 31 | 32 | export default useManualEventHandlers; 33 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/UseModifiedProps/index.js: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect, useDebugValue } from 'react'; 2 | import getModifiedProps from '../../utils/getModifiedProps'; 3 | 4 | export default function useModifiedProps(props, childrenIsText = false) { 5 | const ref = useRef(); 6 | useEffect(() => { 7 | ref.current = props; 8 | }); 9 | const modifiedProps = getModifiedProps(ref.current, props, childrenIsText); 10 | 11 | useDebugValue(modifiedProps ? 'Modified' : 'Not modified'); 12 | 13 | return modifiedProps; 14 | } 15 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/UsePlotBandLine/index.js: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import PlotLineContext from '../PlotBandLineContext'; 3 | 4 | export default function usePlotBandLine() { 5 | return useContext(PlotLineContext); 6 | } 7 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/UsePrevious/index.js: -------------------------------------------------------------------------------- 1 | import { useRef, useEffect } from 'react'; 2 | 3 | export default function usePrevious(value) { 4 | const ref = useRef(); 5 | useEffect(() => { 6 | ref.current = value; 7 | }); 8 | return ref.current; 9 | } 10 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/UseSeries/index.js: -------------------------------------------------------------------------------- 1 | import { useContext, useState, useEffect, useDebugValue } from 'react'; 2 | import SeriesContext from '../SeriesContext'; 3 | import useChart from '../UseChart'; 4 | import createProvidedSeries from '../Series/createProvidedSeries'; 5 | 6 | export default function useSeries(seriesId) { 7 | const contextSeries = useContext(SeriesContext); 8 | const chart = useChart(); 9 | 10 | const createStateSeries = () => { 11 | if (contextSeries) return contextSeries; 12 | 13 | if (seriesId) { 14 | const mySeries = chart.get(seriesId); 15 | return createProvidedSeries(mySeries); 16 | } 17 | return null; 18 | }; 19 | 20 | const [providedSeries, setProvidedSeries] = useState(createStateSeries); 21 | useEffect(() => { 22 | if (providedSeries) return; // we already had series 23 | // series should now be created 24 | setProvidedSeries(createStateSeries()); 25 | }, []); 26 | useDebugValue(providedSeries ? providedSeries.id : null); 27 | 28 | return providedSeries; 29 | } 30 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/WithHighcharts/index.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import HighchartsContext from '../HighchartsContext'; 3 | 4 | // This is a HOC function. 5 | // It takes a component... 6 | export default function withHighcharts(Component, Highcharts) { 7 | // ...and returns another component... 8 | return function HighchartsWrappedComponent(props) { 9 | // ... and renders the wrapped component with the context theme! 10 | // Notice that we pass through any additional props as well 11 | return ( 12 | 13 | 14 | 15 | ); 16 | }; 17 | } 18 | 19 | export const HighchartsProvider = ({ Highcharts, children }) => ( 20 | 21 | {children} 22 | 23 | ); 24 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/WithSeriesType/index.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Series from '../Series'; 3 | 4 | // This HOC returns Series component with injected type 5 | export default function withSeriesType(seriesType, additionalProps = {}) { 6 | const SeriesComponent = props => ( 7 | 8 | ); 9 | 10 | SeriesComponent.displayName = `${seriesType}Series`; 11 | 12 | return SeriesComponent; 13 | } 14 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/XAxis/XAxis.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Axis from '../Axis'; 3 | import useChart from '../UseChart'; 4 | 5 | const XAxis = ({ id, ...rest }) => { 6 | const chart = useChart(); 7 | 8 | const isStockChart = chart.type === 'stockChart'; 9 | const type = isStockChart ? 'datetime' : 'linear'; 10 | const axisId = isStockChart ? 'xAxis' : id; 11 | 12 | return ; 13 | }; 14 | 15 | export default XAxis; 16 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/XAxis/index.js: -------------------------------------------------------------------------------- 1 | import XAxis from './XAxis'; 2 | import Axis from '../Axis'; 3 | const ChartXAxis = XAxis; 4 | ChartXAxis.Title = Axis.Title; 5 | export default ChartXAxis; 6 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/YAxis/YAxis.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Axis from '../Axis'; 3 | 4 | const YAxis = ({ type = 'linear', ...restProps }) => ( 5 | 6 | ); 7 | 8 | YAxis.displayName = 'YAxis'; 9 | 10 | YAxis.Title = Axis.Title; 11 | export default YAxis; 12 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/YAxis/index.js: -------------------------------------------------------------------------------- 1 | import YAxis from './YAxis'; 2 | export default YAxis; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/ZAxis/ZAxis.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Axis from '../Axis'; 3 | 4 | const ZAxis = ({ type = 'linear', ...restProps }) => ( 5 | 6 | ); 7 | 8 | ZAxis.displayName = 'ZAxis'; 9 | ZAxis.Title = Axis.Title; 10 | export default ZAxis; 11 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/components/ZAxis/index.js: -------------------------------------------------------------------------------- 1 | import ZAxis from './ZAxis'; 2 | export default ZAxis; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/utils/debounce-raf.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2019, Hyperdivision ApS 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | export default function (fn) { 18 | const cancelAnimationFrame = window.cancelAnimationFrame; 19 | const requestAnimationFrame = window.requestAnimationFrame; 20 | 21 | var queued; 22 | return function (...args) { 23 | if (queued) cancelAnimationFrame(queued); 24 | 25 | queued = requestAnimationFrame(fn.bind(fn, ...args)); 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/utils/events.js: -------------------------------------------------------------------------------- 1 | import pickBy from './pickBy'; 2 | 3 | export const getEventHandlerProps = props => { 4 | return pickBy(props, _isEventKey); 5 | }; 6 | 7 | export const getNonEventHandlerProps = props => { 8 | return pickBy(props, (key, value) => !_isEventKey(key, value)); 9 | }; 10 | 11 | export const getEventsConfig = props => { 12 | const eventProps = getEventHandlerProps(props); 13 | const eventsConfig = {}; 14 | 15 | Object.keys(eventProps).forEach(eventName => { 16 | const configName = eventName.slice(2)[0].toLowerCase() + eventName.slice(3); 17 | eventsConfig[configName] = eventProps[eventName]; 18 | }); 19 | 20 | return eventsConfig; 21 | }; 22 | 23 | const _isEventKey = (key, value) => 24 | key.indexOf('on') === 0 && key.length > 2 && typeof value === 'function'; 25 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/utils/getModifiedProps.js: -------------------------------------------------------------------------------- 1 | import pickBy from './pickBy'; 2 | 3 | export default function getModifiedProps( 4 | prevProps, 5 | currProps, 6 | childrenIsText = false 7 | ) { 8 | let { children, ...rest } = currProps; 9 | 10 | const modifiedProps = pickBy(rest, (propName, value) => { 11 | if (!prevProps) return true; 12 | 13 | return Object.is(value, prevProps[propName]) === false; 14 | }); 15 | 16 | if ( 17 | childrenIsText && 18 | (!prevProps || Object.is(prevProps.children, children) === false) 19 | ) { 20 | modifiedProps.text = children; 21 | } 22 | 23 | if (Object.keys(modifiedProps).length > 0) { 24 | return modifiedProps; 25 | } 26 | 27 | return false; 28 | } 29 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/src/utils/pickBy.js: -------------------------------------------------------------------------------- 1 | export default function (obj, filterFn) { 2 | let retProps = {}; 3 | if (obj) { 4 | Object.keys(obj) 5 | .filter(key => filterFn(key, obj[key])) 6 | .forEach(key => { 7 | retProps[key] = obj[key]; 8 | }); 9 | } 10 | return retProps; 11 | } 12 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "jest": true 5 | }, 6 | "rules": { 7 | "no-unused-vars": ["warn"], 8 | "react-perf/jsx-no-new-function-as-prop": "off", 9 | "react-perf/jsx-no-new-object-as-prop": "off", 10 | "react-perf/jsx-no-new-array-as-prop": "off", 11 | "react/display-name": "off" 12 | }, 13 | "globals": { 14 | "mount": true, 15 | "shallow": true, 16 | "render": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/ContextSpy.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { 3 | useAxis, 4 | useChart, 5 | useHighcharts, 6 | useSeries, 7 | usePlotBandLine 8 | } from '../src'; 9 | 10 | const ContextSpy = ({ 11 | axisId, 12 | axisRef, 13 | chartRef, 14 | highchartsRef, 15 | seriesRef, 16 | plotBandLineRef 17 | }) => { 18 | const axis = useAxis(axisId); 19 | const chart = useChart(); 20 | const Highcharts = useHighcharts(); 21 | const series = useSeries(); 22 | const plotbandline = usePlotBandLine(); 23 | 24 | useEffect(() => { 25 | if (highchartsRef) { 26 | highchartsRef.current = Highcharts; 27 | } 28 | 29 | return () => { 30 | if (highchartsRef) { 31 | highchartsRef.current = null; 32 | } 33 | }; 34 | }, [Highcharts]); 35 | 36 | useEffect(() => { 37 | if (chartRef) { 38 | chartRef.current = chart; 39 | } 40 | 41 | return () => { 42 | if (chartRef) { 43 | chartRef.current = null; 44 | } 45 | }; 46 | }, [chart]); 47 | 48 | useEffect(() => { 49 | if (axisRef) { 50 | axisRef.current = axis; 51 | axisRef.addPlotBandOrLineSpy = jest.spyOn(axis, 'addPlotBandOrLine'); 52 | axisRef.removePlotBandOrLineSpy = jest.spyOn( 53 | axis, 54 | 'removePlotBandOrLine' 55 | ); 56 | } 57 | 58 | return () => { 59 | if (axisRef) { 60 | axisRef.addPlotBandOrLineSpy.mockRestore(); 61 | axisRef.removePlotBandOrLineSpy.mockRestore(); 62 | axisRef.current = null; 63 | } 64 | }; 65 | }, [axis]); 66 | 67 | useEffect(() => { 68 | if (seriesRef) { 69 | seriesRef.current = series; 70 | } 71 | 72 | return () => { 73 | if (seriesRef) { 74 | seriesRef.current = null; 75 | } 76 | }; 77 | }, [series]); 78 | 79 | useEffect(() => { 80 | if (plotBandLineRef) { 81 | plotBandLineRef.current = plotbandline; 82 | } 83 | 84 | return () => { 85 | if (plotBandLineRef) { 86 | plotBandLineRef.current = null; 87 | } 88 | }; 89 | }, [plotbandline]); 90 | 91 | return null; 92 | }; 93 | 94 | export default ContextSpy; 95 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/Axis/Axis.integration.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Highcharts from 'highcharts'; 3 | import addAccessibility from 'highcharts/modules/accessibility'; 4 | 5 | import { render } from '@testing-library/react'; 6 | 7 | import { HighchartsChart, HighchartsProvider } from '../../../src'; 8 | import Axis from '../../../src/components/Axis'; 9 | import ContextSpy from '../../ContextSpy'; 10 | 11 | addAccessibility(Highcharts); 12 | 13 | describe(' integration', () => { 14 | describe('when rendered to document', () => { 15 | it('fires afterInit event', done => { 16 | const onAfterInit = () => { 17 | expect(true).toBe(true); 18 | done(); 19 | }; 20 | const Component = () => { 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | }; 29 | 30 | render(); 31 | }); 32 | }); 33 | describe('when updated', () => { 34 | it('updates eventhandlers', () => { 35 | const axisRef = {}; 36 | 37 | const Component = ({ axisProps }) => { 38 | return ( 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | ); 47 | }; 48 | 49 | const { rerender } = render(); 50 | 51 | const onSetExtremes = jest.fn(); 52 | 53 | rerender(); 54 | 55 | const onSetExtremes2 = jest.fn(); 56 | axisRef.current.setExtremes(10, 20); 57 | expect(onSetExtremes).toHaveBeenCalledTimes(1); 58 | onSetExtremes.mockClear(); 59 | 60 | rerender(); 61 | 62 | axisRef.current.setExtremes(100, 200); 63 | expect(onSetExtremes).not.toHaveBeenCalled(); 64 | 65 | expect(onSetExtremes2).toHaveBeenCalledTimes(1); 66 | }); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/Axis/AxisTitle.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import { createMockProvidedAxis } from '../../test-utils'; 5 | import AxisTitle from '../../../src/components/Axis/AxisTitle'; 6 | import * as useAxis from '../../../src/components/UseAxis'; 7 | 8 | describe('', () => { 9 | let testContext; 10 | let useAxisSpy; 11 | 12 | beforeEach(() => { 13 | testContext = {}; 14 | 15 | const { axisStubs, providedAxis } = createMockProvidedAxis({ 16 | id: 'myAxis', 17 | type: 'yAxis' 18 | }); 19 | testContext.axisStubs = axisStubs; 20 | useAxisSpy = jest 21 | .spyOn(useAxis, 'default') 22 | .mockImplementation(() => providedAxis); 23 | }); 24 | 25 | afterEach(() => { 26 | useAxisSpy.mockRestore(); 27 | }); 28 | 29 | describe('when mounted', () => { 30 | it('sets the correct axis title', () => { 31 | render(My Axis Title); 32 | expect(testContext.axisStubs.setTitle).toHaveBeenCalledWith( 33 | { 34 | text: 'My Axis Title' 35 | }, 36 | expect.any(Boolean) 37 | ); 38 | }); 39 | 40 | it('should pass additional props too', () => { 41 | render(My Axis Title); 42 | expect(testContext.axisStubs.setTitle).toHaveBeenCalledWith( 43 | { 44 | text: 'My Axis Title', 45 | align: 'high' 46 | }, 47 | expect.any(Boolean) 48 | ); 49 | }); 50 | }); 51 | 52 | describe('update', () => { 53 | it('should setTitle the correct axis title if the component props change', () => { 54 | const wrapper = render(My Axis Title); 55 | testContext.axisStubs.setTitle.mockClear(); 56 | 57 | wrapper.rerender( 58 | 59 | New Title 60 | 61 | ); 62 | 63 | expect(testContext.axisStubs.setTitle).toHaveBeenCalledWith( 64 | { 65 | text: 'New Title', 66 | dimension: 'x' 67 | }, 68 | expect.any(Boolean) 69 | ); 70 | }); 71 | }); 72 | 73 | describe('when unmounted', () => { 74 | it('removes the correct axis title (if the axis still exists)', () => { 75 | const wrapper = render(My Axis Title); 76 | wrapper.unmount(); 77 | expect(testContext.axisStubs.setTitle).toHaveBeenCalledWith( 78 | { 79 | text: null 80 | }, 81 | expect.any(Boolean) 82 | ); 83 | }); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/BarSeries/BarSeries.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import { 5 | Highcharts, 6 | createMockProvidedChart, 7 | createMockProvidedAxis 8 | } from '../../test-utils'; 9 | import BarSeries from '../../../src/components/BarSeries/BarSeries'; 10 | import ChartContext from '../../../src/components/ChartContext'; 11 | import AxisContext from '../../../src/components/AxisContext'; 12 | import HighchartsContext from '../../../src/components/HighchartsContext'; 13 | 14 | describe('', () => { 15 | let testContext; 16 | let ProvidedBarSeries; 17 | beforeEach(() => { 18 | testContext = {}; 19 | 20 | const { chartStubs } = createMockProvidedChart(); 21 | const { providedAxis } = createMockProvidedAxis({ 22 | id: 'myAxis', 23 | type: 'yAxis' 24 | }); 25 | testContext.chartStubs = chartStubs; 26 | testContext.providedAxis = providedAxis; 27 | 28 | const highchartsValue = () => Highcharts; 29 | 30 | ProvidedBarSeries = props => ( 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ); 39 | }); 40 | 41 | it('adds a series with type="bar" />', () => { 42 | render(); 43 | expect(testContext.chartStubs.addSeries).toHaveBeenCalledWith( 44 | expect.objectContaining({ type: 'bar' }), 45 | false 46 | ); 47 | }); 48 | 49 | it('passes other props through to series', () => { 50 | render(); 51 | expect(testContext.chartStubs.addSeries).toHaveBeenCalledWith( 52 | expect.objectContaining({ data: [1, 2, 3, 4] }), 53 | false 54 | ); 55 | }); 56 | 57 | it('inverts the chart on mount', () => { 58 | render(); 59 | expect(testContext.chartStubs.update).toHaveBeenCalledWith({ 60 | chart: { 61 | inverted: true 62 | } 63 | }); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/Caption/Caption.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import { createMockProvidedChart } from '../../test-utils'; 5 | import Caption from '../../../src/components/Caption/Caption'; 6 | import ChartContext from '../../../src/components/ChartContext'; 7 | 8 | describe('', () => { 9 | let testContext; 10 | let ProvidedCaption; 11 | 12 | beforeEach(() => { 13 | testContext = {}; 14 | const { chartStubs, needsRedraw } = createMockProvidedChart(); 15 | testContext.chartStubs = chartStubs; 16 | testContext.needsRedraw = needsRedraw; 17 | 18 | ProvidedCaption = props => ( 19 | 20 | 21 | 22 | ); 23 | }); 24 | 25 | describe('when mounted', () => { 26 | it('adds a caption using the Highcharts setCaption method', () => { 27 | render(My Caption); 28 | expect(testContext.chartStubs.setCaption).toHaveBeenCalledWith({ 29 | text: 'My Caption' 30 | }); 31 | expect(testContext.needsRedraw).toHaveBeenCalledTimes(1); 32 | }); 33 | 34 | it('should pass additional props through to Highcharts setTitle method', () => { 35 | render(My Other Caption); 36 | expect(testContext.chartStubs.setCaption).toHaveBeenCalledWith({ 37 | text: 'My Other Caption', 38 | align: 'right' 39 | }); 40 | }); 41 | }); 42 | 43 | describe('update', () => { 44 | it('should use the setCaption method when the data changes', () => { 45 | const wrapper = render(My Caption); 46 | wrapper.rerender( 47 | 48 | My New Caption 49 | 50 | ); 51 | 52 | expect(testContext.chartStubs.setCaption).toHaveBeenCalledWith({ 53 | x: 10, 54 | y: 20, 55 | text: 'My New Caption' 56 | }); 57 | expect(testContext.needsRedraw).toHaveBeenCalledTimes(2); 58 | }); 59 | }); 60 | 61 | describe('when unmounted', () => { 62 | it('removes the caption by setting the text to null', () => { 63 | const wrapper = render(My Caption); 64 | wrapper.unmount(); 65 | expect(testContext.chartStubs.setCaption).toHaveBeenCalledWith({ 66 | text: null 67 | }); 68 | expect(testContext.chartStubs.setCaption).toHaveBeenCalledTimes(2); 69 | expect(testContext.needsRedraw).toHaveBeenCalledTimes(2); 70 | }); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/Credits/Credits.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import { createMockProvidedChart } from '../../test-utils'; 5 | import Credits from '../../../src/components/Credits/Credits'; 6 | import ChartContext from '../../../src/components/ChartContext'; 7 | 8 | describe('', () => { 9 | let testContext; 10 | let ProvidedCredits; 11 | 12 | beforeEach(() => { 13 | testContext = {}; 14 | const { chartStubs } = createMockProvidedChart(); 15 | testContext.chartStubs = chartStubs; 16 | 17 | ProvidedCredits = props => ( 18 | 19 | 20 | 21 | ); 22 | }); 23 | 24 | describe('when mounted', () => { 25 | it('add credits using the Highcharts addCredits method', () => { 26 | render(github.com); 27 | expect(testContext.chartStubs.addCredits).toHaveBeenCalledWith( 28 | { 29 | enabled: true, 30 | text: 'github.com' 31 | }, 32 | true 33 | ); 34 | expect(testContext.chartStubs.addCredits).toHaveBeenCalledTimes(1); 35 | }); 36 | 37 | it('addCreditss the credits with the passed props', () => { 38 | render( 39 | 40 | github.com 41 | 42 | ); 43 | expect(testContext.chartStubs.addCredits).toHaveBeenCalledWith( 44 | { 45 | enabled: true, 46 | href: 'https://www.github.com', 47 | text: 'github.com' 48 | }, 49 | true 50 | ); 51 | }); 52 | }); 53 | 54 | describe('addCredits', () => { 55 | it('should use the addCredits method when props change', () => { 56 | const wrapper = render( 57 | 58 | github.com 59 | 60 | ); 61 | 62 | wrapper.rerender( 63 | 64 | github.com 65 | 66 | ); 67 | 68 | expect(testContext.chartStubs.addCredits).toHaveBeenCalledWith( 69 | { 70 | href: 'https://www.github.com/whawker' 71 | }, 72 | true 73 | ); 74 | }); 75 | }); 76 | 77 | describe('when unmounted', () => { 78 | it('should disable the Credits', () => { 79 | const wrapper = render(github.com); 80 | wrapper.unmount(); 81 | expect(testContext.chartStubs.addCredits).toHaveBeenCalledWith( 82 | { 83 | enabled: false 84 | }, 85 | true 86 | ); 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/HighchartsChart/HighchartsChart.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import { Highcharts, createMockChart } from '../../test-utils'; 5 | import HighchartsChart from '../../../src/components/HighchartsChart/HighchartsChart'; 6 | import HighchartsContext from '../../../src/components/HighchartsContext'; 7 | 8 | describe('', () => { 9 | let ProvidedHighchartsChart; 10 | let chart; 11 | 12 | beforeEach(() => { 13 | chart = createMockChart(); 14 | Highcharts.chart.mockReturnValue(chart); 15 | 16 | ProvidedHighchartsChart = props => ( 17 | 18 | 19 | 20 | ); 21 | }); 22 | 23 | afterEach(() => { 24 | Highcharts.chart.mockRestore(); 25 | }); 26 | 27 | it('creates a chart', () => { 28 | render(); 29 | 30 | expect(Highcharts.chart).toHaveBeenCalled(); 31 | }); 32 | 33 | it('creates chart with the correct chart type', () => { 34 | render(); 35 | 36 | expect(Highcharts.chart).toHaveBeenCalledWith( 37 | expect.anything(), 38 | expect.objectContaining({ 39 | chartType: 'chart' 40 | }) 41 | ); 42 | }); 43 | 44 | it('passes other props through to chart', () => { 45 | render(); 46 | 47 | expect(Highcharts.chart).toHaveBeenCalledWith( 48 | expect.anything(), 49 | expect.objectContaining({ 50 | plotOptions: { a: 'b' } 51 | }) 52 | ); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/Legend/Legend.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import { createMockProvidedChart } from '../../test-utils'; 5 | import Legend from '../../../src/components/Legend/Legend'; 6 | import ChartContext from '../../../src/components/ChartContext'; 7 | 8 | describe('', () => { 9 | let testContext; 10 | 11 | let ProvidedLegend; 12 | 13 | beforeEach(() => { 14 | testContext = {}; 15 | const { chartStubs, needsRedraw } = createMockProvidedChart(); 16 | 17 | ProvidedLegend = props => ( 18 | 19 | 20 | 21 | ); 22 | testContext.chartStubs = chartStubs; 23 | testContext.needsRedraw = needsRedraw; 24 | }); 25 | 26 | describe('when mounted', () => { 27 | it('add legend using the Highcharts update method', () => { 28 | render(); 29 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 30 | { 31 | legend: { 32 | enabled: true 33 | } 34 | }, 35 | false 36 | ); 37 | expect(testContext.chartStubs.update).toHaveBeenCalledTimes(1); 38 | expect(testContext.needsRedraw).toHaveBeenCalledTimes(1); 39 | }); 40 | 41 | it('updates the legend with the passed props', () => { 42 | render(); 43 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 44 | { 45 | legend: { 46 | enabled: true, 47 | align: 'left', 48 | y: 20 49 | } 50 | }, 51 | false 52 | ); 53 | expect(testContext.needsRedraw).toHaveBeenCalledTimes(1); 54 | }); 55 | }); 56 | 57 | describe('update', () => { 58 | it('should use the update method when props change', () => { 59 | const wrapper = render(); 60 | wrapper.rerender(); 61 | 62 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 63 | { 64 | legend: { 65 | backgroundColor: 'red' 66 | } 67 | }, 68 | false 69 | ); 70 | expect(testContext.needsRedraw).toHaveBeenCalledTimes(2); 71 | }); 72 | }); 73 | 74 | describe('when unmounted', () => { 75 | it('should disable the Legend', () => { 76 | const wrapper = render(); 77 | wrapper.unmount(); 78 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 79 | { 80 | legend: { 81 | enabled: false 82 | } 83 | }, 84 | false 85 | ); 86 | expect(testContext.needsRedraw).toHaveBeenCalledTimes(2); 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/Legend/LegendTitle.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import LegendTitle from '../../../src/components/Legend/LegendTitle'; 5 | import { createMockProvidedChart } from '../../test-utils'; 6 | import ChartContext from '../../../src/components/ChartContext'; 7 | 8 | describe('', () => { 9 | let testContext; 10 | let ProvidedLegendTitle; 11 | 12 | beforeEach(() => { 13 | testContext = {}; 14 | const { chartStubs, needsRedraw } = createMockProvidedChart(); 15 | testContext.chartStubs = chartStubs; 16 | 17 | ProvidedLegendTitle = props => ( 18 | 19 | 20 | 21 | ); 22 | testContext.chartStubs = chartStubs; 23 | testContext.needsRedraw = needsRedraw; 24 | }); 25 | 26 | describe('when mounted', () => { 27 | it('add legend using the Highcharts update method', () => { 28 | render(My Legend Title); 29 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 30 | { 31 | legend: { 32 | title: { 33 | text: 'My Legend Title' 34 | } 35 | } 36 | }, 37 | false 38 | ); 39 | }); 40 | 41 | it('updates the legend with the passed props', () => { 42 | render( 43 | 44 | My Legend Title 45 | 46 | ); 47 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 48 | { 49 | legend: { 50 | title: { 51 | text: 'My Legend Title', 52 | style: { color: 'red' } 53 | } 54 | } 55 | }, 56 | false 57 | ); 58 | }); 59 | }); 60 | 61 | describe('update', () => { 62 | it('should use the update method when props change', () => { 63 | const wrapper = render( 64 | My Legend Title 65 | ); 66 | wrapper.rerender( 67 | My New Legend Title 68 | ); 69 | 70 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 71 | { 72 | legend: { 73 | title: { 74 | text: 'My New Legend Title' 75 | } 76 | } 77 | }, 78 | false 79 | ); 80 | }); 81 | }); 82 | 83 | describe('when unmounted', () => { 84 | it('should disable the LegendTitle', () => { 85 | const wrapper = render( 86 | My Legend Title 87 | ); 88 | wrapper.unmount(); 89 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 90 | { 91 | legend: { 92 | title: { 93 | text: null 94 | } 95 | } 96 | }, 97 | false 98 | ); 99 | }); 100 | }); 101 | }); 102 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/Options3d/Options3d.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import { createMockProvidedChart } from '../../test-utils'; 5 | import Options3d from '../../../src/components/Options3d/Options3d'; 6 | import ChartContext from '../../../src/components/ChartContext'; 7 | 8 | const defaultProps = { 9 | enabled: false, 10 | alpha: 0, 11 | beta: 0, 12 | depth: 100, 13 | fitToPlot: true, 14 | viewDistance: 25, 15 | axisLabelPosition: 'default', 16 | frame: { 17 | visible: 'default', 18 | size: 1, 19 | bottom: {}, 20 | top: {}, 21 | left: {}, 22 | right: {}, 23 | back: {}, 24 | front: {} 25 | } 26 | }; 27 | 28 | describe('', () => { 29 | let testContext; 30 | let ProvidedOptions3d; 31 | 32 | beforeEach(() => { 33 | testContext = {}; 34 | const { chartStubs } = createMockProvidedChart(); 35 | testContext.chartStubs = chartStubs; 36 | ProvidedOptions3d = props => ( 37 | 38 | 39 | 40 | ); 41 | }); 42 | 43 | describe('when mounted', () => { 44 | it('updates the chart with the passed props', () => { 45 | render(); 46 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 47 | { 48 | chart: { 49 | options3d: { 50 | ...defaultProps, 51 | enabled: true, 52 | alpha: 10, 53 | beta: 20 54 | } 55 | } 56 | }, 57 | true 58 | ); 59 | }); 60 | }); 61 | 62 | describe('update', () => { 63 | it('should use the update method when props change', () => { 64 | const wrapper = render(); 65 | wrapper.rerender(); 66 | 67 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 68 | { 69 | chart: { 70 | options3d: { 71 | ...defaultProps, 72 | enabled: true, 73 | alpha: 45 74 | } 75 | } 76 | }, 77 | true 78 | ); 79 | }); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/Pane/Pane.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import { createMockProvidedChart } from '../../test-utils'; 5 | import Pane from '../../../src/components/Pane/Pane'; 6 | import ChartContext from '../../../src/components/ChartContext'; 7 | 8 | describe('', () => { 9 | let testContext; 10 | let ProvidedPane; 11 | beforeEach(() => { 12 | testContext = {}; 13 | const { chartStubs, needsRedraw } = createMockProvidedChart(); 14 | testContext.chartStubs = chartStubs; 15 | testContext.needsRedraw = needsRedraw; 16 | 17 | ProvidedPane = props => ( 18 | 19 | 20 | 21 | ); 22 | }); 23 | 24 | describe('when mounted', () => { 25 | it('set Pane options using the Highcharts update method', () => { 26 | render(); 27 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 28 | { 29 | pane: { 30 | center: ['50%', '85%'], 31 | size: '100%' 32 | } 33 | }, 34 | false 35 | ); 36 | expect(testContext.needsRedraw).toHaveBeenCalledTimes(1); 37 | }); 38 | 39 | it('does not add pane with empty props', () => { 40 | render(); 41 | expect(testContext.chartStubs.update).not.toHaveBeenCalled(); 42 | expect(testContext.needsRedraw).not.toHaveBeenCalled(); 43 | }); 44 | }); 45 | 46 | describe('update', () => { 47 | it('should use the update method when props change', () => { 48 | const wrapper = render(); 49 | wrapper.rerender(); 50 | 51 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 52 | { 53 | pane: { 54 | size: '50%' 55 | } 56 | }, 57 | false 58 | ); 59 | expect(testContext.needsRedraw).toHaveBeenCalledTimes(1); 60 | }); 61 | }); 62 | 63 | describe('when unmounted', () => { 64 | it('should disable the Pane', () => { 65 | const wrapper = render(); 66 | expect(testContext.needsRedraw).toHaveBeenCalledTimes(1); 67 | testContext.needsRedraw.mockClear(); 68 | wrapper.unmount(); 69 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 70 | { 71 | pane: {} 72 | }, 73 | false 74 | ); 75 | expect(testContext.needsRedraw).toHaveBeenCalledTimes(1); 76 | }); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/PlotBandLine/PlotBandLineLabel.integration.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Highcharts from 'highcharts'; 3 | import addAccessibility from 'highcharts/modules/accessibility'; 4 | 5 | import { render } from '@testing-library/react'; 6 | 7 | import { 8 | HighchartsChart, 9 | HighchartsProvider, 10 | PlotBand, 11 | YAxis, 12 | XAxis, 13 | LineSeries 14 | } from '../../../src'; 15 | import ContextSpy from '../../ContextSpy'; 16 | 17 | addAccessibility(Highcharts); 18 | 19 | describe(' integration', () => { 20 | let axisRef; 21 | const DEFAULT_SERIES_DATA = [1, 2, 3, 4, 5]; 22 | const Component = ({ 23 | id, 24 | from, 25 | to, 26 | labelStyle, 27 | yaxisLabels, 28 | seriesData = DEFAULT_SERIES_DATA 29 | }) => { 30 | return ( 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | ); 44 | }; 45 | 46 | beforeEach(() => { 47 | axisRef = { current: null }; 48 | }); 49 | 50 | describe('when parent axis is updated', () => { 51 | it('updates plotband style', () => { 52 | const wrapper = render( 53 | 59 | ); 60 | 61 | let axis = axisRef.current && axisRef.current.object; 62 | 63 | wrapper.rerender( 64 | 70 | ); 71 | 72 | expect(axis.plotLinesAndBands[0].options.label.style).toEqual({ 73 | color: '#bbb' 74 | }); 75 | 76 | wrapper.rerender( 77 | 83 | ); 84 | 85 | expect(axis.plotLinesAndBands[0].options.label.style).toEqual({ 86 | color: '#ccc' 87 | }); 88 | }); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/PlotBandLine/PlotBandLineLabel.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import PlotBandLineLabel from '../../../src/components/PlotBandLine/PlotBandLineLabel'; 5 | import PlotLineContext from '../../../src/components/PlotBandLineContext'; 6 | 7 | describe('', () => { 8 | let testContext; 9 | let ProvidedPlotBandLineLabel; 10 | beforeEach(() => { 11 | testContext = {}; 12 | 13 | testContext.plotLine = { 14 | id: 'myPlotLine', 15 | options: { label: { text: null } }, 16 | render: jest.fn() 17 | }; 18 | testContext.providedPlotline = { 19 | get object() { 20 | return testContext.plotLine; 21 | } 22 | }; 23 | ProvidedPlotBandLineLabel = props => ( 24 | 25 | 26 | 27 | ); 28 | }); 29 | 30 | describe('when mounted', () => { 31 | it('sets the correct plot line label', () => { 32 | render( 33 | My PlotLine Label 34 | ); 35 | 36 | expect(testContext.plotLine.options.label).toEqual({ 37 | text: 'My PlotLine Label' 38 | }); 39 | expect(testContext.plotLine.render).toHaveBeenCalledTimes(1); 40 | }); 41 | 42 | it('should pass additional props too', () => { 43 | render( 44 | 45 | My PlotLine Label 46 | 47 | ); 48 | 49 | expect(testContext.plotLine.options.label).toEqual({ 50 | text: 'My PlotLine Label', 51 | align: 'left' 52 | }); 53 | expect(testContext.plotLine.render).toHaveBeenCalledTimes(1); 54 | }); 55 | 56 | it('should pass formatter prop', () => { 57 | const formatter = () => 'My PlotLine Label'; 58 | render(); 59 | 60 | expect(testContext.plotLine.options.label).toEqual({ 61 | formatter 62 | }); 63 | expect(testContext.plotLine.render).toHaveBeenCalledTimes(1); 64 | }); 65 | }); 66 | 67 | describe('update', () => { 68 | it('should update the correct plot line if the component props change', () => { 69 | const wrapper = render( 70 | 71 | My PlotLine Label 72 | 73 | ); 74 | testContext.plotLine.render.mockClear(); 75 | wrapper.rerender( 76 | 77 | My New Label 78 | 79 | ); 80 | 81 | expect(testContext.plotLine.options.label).toEqual({ 82 | text: 'My New Label' 83 | }); 84 | expect(testContext.plotLine.render).toHaveBeenCalledTimes(1); 85 | }); 86 | }); 87 | 88 | describe('when unmounted', () => { 89 | it('sets the correct plot line label text to null', () => { 90 | const wrapper = render( 91 | 92 | My PlotLine Label 93 | 94 | ); 95 | testContext.plotLine.render.mockClear(); 96 | wrapper.unmount(); 97 | 98 | expect(testContext.plotLine.options.label).toEqual({ 99 | text: null 100 | }); 101 | expect(testContext.plotLine.render).toHaveBeenCalledTimes(1); 102 | }); 103 | }); 104 | }); 105 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/Series/Series.integration.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import Highcharts from 'highcharts'; 3 | import addAccessibility from 'highcharts/modules/accessibility'; 4 | 5 | import { render } from '@testing-library/react'; 6 | 7 | import { 8 | HighchartsChart, 9 | Chart, 10 | YAxis, 11 | XAxis, 12 | HighchartsProvider, 13 | Series 14 | } from '../../../src'; 15 | 16 | import ContextSpy from '../../ContextSpy'; 17 | 18 | addAccessibility(Highcharts); 19 | 20 | describe(' integration', () => { 21 | let Component; 22 | let seriesRef; 23 | beforeEach(() => { 24 | seriesRef = {}; 25 | 26 | Component = props => { 27 | return ( 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | ); 40 | }; 41 | }); 42 | 43 | it('fires onHide eventhandler', () => { 44 | const onHide = jest.fn(); 45 | 46 | render(); 47 | seriesRef.current.object.hide(); 48 | expect(onHide).toHaveBeenCalled(); 49 | }); 50 | 51 | it('changes onHide eventhandler when new one is passed', () => { 52 | const onHide1 = jest.fn(); 53 | const onHide2 = jest.fn(); 54 | 55 | const wrapper = render(); 56 | 57 | wrapper.rerender(); 58 | 59 | seriesRef.current.object.hide(); 60 | expect(onHide1).not.toHaveBeenCalled(); 61 | expect(onHide2).toHaveBeenCalled(); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/Series/SeriesTypes.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import ShallowRenderer from 'react-test-renderer/shallow'; 3 | 4 | import * as all from '../../../src'; 5 | import Series from '../../../src/components/Series'; 6 | 7 | const skippedSeries = ['BarSeries']; 8 | const noAxisSeries = [ 9 | 'FunnelSeries', 10 | 'PackedBubbleSeries', 11 | 'PieSeries', 12 | 'PyramidSeries', 13 | 'VariablePieSeries', 14 | 'VennSeries' 15 | ]; 16 | const needParentSeries = ['BellCurveSeries', 'HistogramSeries', 'ParetoSeries']; 17 | 18 | Object.keys(all) 19 | .filter(name => /^[A-Z].*Series$/.test(name)) 20 | .forEach(seriesName => { 21 | if (skippedSeries.includes(seriesName)) return; 22 | 23 | const seriesType = seriesName 24 | .substring(0, seriesName.indexOf('Series')) 25 | .toLowerCase(); 26 | const SeriesComponent = all[seriesName]; // eslint-disable-line import/namespace 27 | 28 | let props = {}; 29 | if (needParentSeries.includes(seriesName)) { 30 | props.baseSeries = 'myBaseSeries'; 31 | } 32 | 33 | describe(`<${seriesName} />`, () => { 34 | let renderer; 35 | beforeEach(() => { 36 | renderer = new ShallowRenderer(); 37 | }); 38 | 39 | it('renders a ', () => { 40 | renderer.render(); 41 | const result = renderer.getRenderOutput(); 42 | 43 | expect(result.type).toBe(Series); 44 | }); 45 | 46 | it(`renders a `, () => { 47 | renderer.render(); 48 | const result = renderer.getRenderOutput(); 49 | 50 | expect(result.props).toHaveProperty('type', seriesType); 51 | }); 52 | 53 | it('passes Data props through to ', () => { 54 | renderer.render( 55 | 56 | ); 57 | const result = renderer.getRenderOutput(); 58 | 59 | expect(result.props).toHaveProperty('data', [1, 2, 3, 4]); 60 | }); 61 | 62 | it('passes other props through to ', () => { 63 | renderer.render( 64 | 65 | ); 66 | const result = renderer.getRenderOutput(); 67 | 68 | expect(result.props).toHaveProperty('zIndex', -1); 69 | }); 70 | 71 | if (noAxisSeries.includes(seriesName)) { 72 | it('does not require an axis', () => { 73 | renderer.render(); 74 | const result = renderer.getRenderOutput(); 75 | 76 | expect(result.props).toHaveProperty('requiresAxis', false); 77 | }); 78 | } 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/Subtitle/Subtitle.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import { createMockProvidedChart } from '../../test-utils'; 5 | import Subtitle from '../../../src/components/Subtitle/Subtitle'; 6 | import ChartContext from '../../../src/components/ChartContext'; 7 | 8 | describe('', () => { 9 | let testContext; 10 | let ProvidedSubtitle; 11 | 12 | beforeEach(() => { 13 | testContext = {}; 14 | const { chartStubs } = createMockProvidedChart(); 15 | testContext.chartStubs = chartStubs; 16 | 17 | ProvidedSubtitle = props => ( 18 | 19 | 20 | 21 | ); 22 | }); 23 | 24 | describe('when mounted', () => { 25 | it('adds a subtitle using the Highcharts setTitle method', () => { 26 | render(My Subtitle); 27 | expect(testContext.chartStubs.setTitle).toHaveBeenCalledTimes(1); 28 | expect(testContext.chartStubs.setTitle).toHaveBeenCalledWith( 29 | undefined, 30 | { text: 'My Subtitle' }, 31 | false 32 | ); 33 | }); 34 | 35 | it('should pass additional props through to Highcharts setTitle method', () => { 36 | render( 37 | My Other Subtitle 38 | ); 39 | expect(testContext.chartStubs.setTitle).toHaveBeenCalledWith( 40 | undefined, 41 | { text: 'My Other Subtitle', align: 'right' }, 42 | false 43 | ); 44 | }); 45 | }); 46 | 47 | describe('update', () => { 48 | it('should use the setTitle method when the data changes', () => { 49 | const wrapper = render(My Subtitle); 50 | testContext.chartStubs.setTitle.mockClear(); 51 | wrapper.rerender( 52 | 53 | My New Subtitle 54 | 55 | ); 56 | 57 | expect(testContext.chartStubs.setTitle).toHaveBeenCalledTimes(1); 58 | expect(testContext.chartStubs.setTitle).toHaveBeenCalledWith( 59 | undefined, 60 | { x: 10, y: 20, text: 'My New Subtitle' }, 61 | false 62 | ); 63 | }); 64 | }); 65 | 66 | describe('when unmounted', () => { 67 | it('removes the subtitle by setting the subtitle to text', () => { 68 | const wrapper = render(My Subtitle); 69 | testContext.chartStubs.setTitle.mockClear(); 70 | wrapper.unmount(); 71 | 72 | expect(testContext.chartStubs.setTitle).toHaveBeenCalledTimes(1); 73 | expect(testContext.chartStubs.setTitle).toHaveBeenCalledWith( 74 | undefined, 75 | { text: null }, 76 | false 77 | ); 78 | }); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/Title/Title.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import { createMockProvidedChart } from '../../test-utils'; 5 | import Title from '../../../src/components/Title/Title'; 6 | import ChartContext from '../../../src/components/ChartContext'; 7 | 8 | describe('', () => { 9 | let testContext; 10 | let ProvidedTitle; 11 | 12 | beforeEach(() => { 13 | testContext = {}; 14 | const { chartStubs, needsRedraw } = createMockProvidedChart(); 15 | testContext.chartStubs = chartStubs; 16 | testContext.needsRedraw = needsRedraw; 17 | 18 | ProvidedTitle = props => ( 19 | <ChartContext.Provider value={chartStubs}> 20 | <Title {...props} /> 21 | </ChartContext.Provider> 22 | ); 23 | }); 24 | 25 | describe('when mounted', () => { 26 | it('adds a title using the Highcharts setTitle method', () => { 27 | render(<ProvidedTitle>My Title</ProvidedTitle>); 28 | expect(testContext.chartStubs.setTitle).toHaveBeenCalledWith( 29 | { text: 'My Title' }, 30 | null, 31 | false 32 | ); 33 | expect(testContext.chartStubs.setTitle).toHaveBeenCalledTimes(1); 34 | expect(testContext.needsRedraw).toHaveBeenCalledTimes(1); 35 | }); 36 | 37 | it('should pass additional props through to Highcharts setTitle method', () => { 38 | render(<ProvidedTitle align="right">My Other Title</ProvidedTitle>); 39 | expect(testContext.chartStubs.setTitle).toHaveBeenCalledWith( 40 | { text: 'My Other Title', align: 'right' }, 41 | null, 42 | false 43 | ); 44 | }); 45 | }); 46 | 47 | describe('update', () => { 48 | it('should use the setTitle method when the data changes', () => { 49 | const wrapper = render(<ProvidedTitle>My Title</ProvidedTitle>); 50 | wrapper.rerender( 51 | <ProvidedTitle x={10} y={20}> 52 | My New Title 53 | </ProvidedTitle> 54 | ); 55 | 56 | expect(testContext.chartStubs.setTitle).toHaveBeenCalledWith( 57 | { x: 10, y: 20, text: 'My New Title' }, 58 | null, 59 | false 60 | ); 61 | expect(testContext.needsRedraw).toHaveBeenCalledTimes(2); 62 | }); 63 | }); 64 | 65 | describe('when unmounted', () => { 66 | it('removes the title by setting the title to text', () => { 67 | const wrapper = render(<ProvidedTitle>My Title</ProvidedTitle>); 68 | wrapper.unmount(); 69 | expect(testContext.chartStubs.setTitle).toHaveBeenCalledWith( 70 | { text: null }, 71 | null, 72 | false 73 | ); 74 | expect(testContext.needsRedraw).toHaveBeenCalledTimes(2); 75 | }); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/Tooltip/Tooltip.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import { Highcharts, createMockProvidedChart } from '../../test-utils'; 5 | import Tooltip from '../../../src/components/Tooltip/Tooltip'; 6 | import HighchartsContext from '../../../src/components/HighchartsContext'; 7 | import ChartContext from '../../../src/components/ChartContext'; 8 | 9 | describe('<Tooltip />', () => { 10 | let testContext; 11 | let ProvidedTooltip; 12 | beforeEach(() => { 13 | testContext = {}; 14 | 15 | const { chartStubs } = createMockProvidedChart(); 16 | 17 | testContext.chartStubs = chartStubs; 18 | testContext.chart = {}; 19 | testContext.chartStubs.object = testContext.chart; 20 | ProvidedTooltip = props => ( 21 | <HighchartsContext.Provider value={Highcharts}> 22 | <ChartContext.Provider value={chartStubs}> 23 | <Tooltip {...props} /> 24 | </ChartContext.Provider> 25 | </HighchartsContext.Provider> 26 | ); 27 | }); 28 | 29 | describe('when mounted', () => { 30 | it('enables the tooltip', () => { 31 | render(<ProvidedTooltip />); 32 | expect(testContext.chartStubs.update).toHaveBeenCalledWith({ 33 | tooltip: { enabled: true } 34 | }); 35 | }); 36 | 37 | it('updates the chart with the passed props', () => { 38 | render(<ProvidedTooltip backgroundColor="red" shadow={false} />); 39 | 40 | expect(testContext.chartStubs.update).toHaveBeenCalledWith({ 41 | tooltip: { backgroundColor: 'red', enabled: true, shadow: false } 42 | }); 43 | }); 44 | }); 45 | 46 | describe('update', () => { 47 | it('should use the update method when props change', () => { 48 | const wrapper = render(<ProvidedTooltip selected={0} />); 49 | wrapper.rerender(<ProvidedTooltip selected={0} padding={2} />); 50 | 51 | expect(testContext.chartStubs.update).toHaveBeenCalledWith({ 52 | tooltip: { padding: 2 } 53 | }); 54 | }); 55 | }); 56 | 57 | describe('when unmounted', () => { 58 | it('should disable the Tooltip', () => { 59 | const wrapper = render(<ProvidedTooltip />); 60 | testContext.chartStubs.update.mockClear(); 61 | wrapper.unmount(); 62 | 63 | expect(testContext.chartStubs.update).toHaveBeenCalledTimes(1); 64 | expect(testContext.chartStubs.update).toHaveBeenCalledWith({ 65 | tooltip: { enabled: false } 66 | }); 67 | }); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/UseAxis/useAxis.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import useAxis from '../../../src/components/UseAxis'; 5 | import AxisContext from '../../../src/components/AxisContext'; 6 | import ChartContext from '../../../src/components/ChartContext'; 7 | import { createMockAxis } from '../../test-utils'; 8 | import * as createProvidedAxis from '../../../src/components/Axis/createProvidedAxis'; 9 | 10 | describe('useAxis', () => { 11 | let ChildComponent; 12 | let testAxis; 13 | let testChart; 14 | let axisCallback; 15 | beforeEach(() => { 16 | jest.useFakeTimers(); 17 | 18 | testAxis = createMockAxis(); 19 | 20 | testChart = { 21 | get: jest.fn().mockImplementation(() => testAxis) 22 | }; 23 | axisCallback = jest.fn(); 24 | 25 | jest.spyOn(createProvidedAxis, 'default').mockImplementation(c => c); 26 | 27 | ChildComponent = props => { 28 | const axis = useAxis(props.axisId); 29 | axisCallback(axis); 30 | return null; 31 | }; 32 | }); 33 | 34 | afterEach(() => { 35 | jest.clearAllTimers(); 36 | }); 37 | 38 | it('should return axis from context', () => { 39 | render( 40 | <AxisContext.Provider value={testAxis}> 41 | <ChildComponent /> 42 | </AxisContext.Provider> 43 | ); 44 | 45 | expect(axisCallback).toHaveBeenCalledWith(testAxis); 46 | }); 47 | 48 | it('should return axis outside the context', () => { 49 | render( 50 | <ChartContext.Provider value={testChart}> 51 | <ChildComponent axisId="myAxisId" /> 52 | </ChartContext.Provider> 53 | ); 54 | 55 | expect(testChart.get).toHaveBeenCalledWith('myAxisId'); 56 | expect(axisCallback).toHaveBeenCalledWith(testAxis); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/UseChart/useChart.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import ChartContext from '../../../src/components/ChartContext'; 5 | 6 | import ContextSpy from '../../ContextSpy'; 7 | 8 | describe('useChart', () => { 9 | let ProvidedChartComponent; 10 | let testChart; 11 | let chartRef; 12 | 13 | beforeEach(() => { 14 | testChart = {}; 15 | chartRef = {}; 16 | 17 | ProvidedChartComponent = () => ( 18 | <ChartContext.Provider value={testChart}> 19 | <ContextSpy chartRef={chartRef} /> 20 | </ChartContext.Provider> 21 | ); 22 | }); 23 | it('should return chart from context', () => { 24 | render(<ProvidedChartComponent />); 25 | 26 | expect(chartRef.current).toEqual(testChart); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/UseChartUpdate/useChartUpdate.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import useChartUpdate from '../../../src/components/UseChartUpdate'; 5 | import ChartContext from '../../../src/components/ChartContext'; 6 | import { createMockProvidedChart } from '../../test-utils'; 7 | 8 | describe('useChartUpdate', () => { 9 | let ProvidedChartComponent; 10 | let ChildComponent; 11 | let testChart; 12 | let updateFn; 13 | let destroyFn; 14 | 15 | beforeEach(() => { 16 | const { chartStubs } = createMockProvidedChart(); 17 | testChart = chartStubs; 18 | updateFn = jest.fn(); 19 | destroyFn = jest.fn(); 20 | 21 | ChildComponent = props => { 22 | useChartUpdate(props, updateFn, destroyFn, false); 23 | return null; 24 | }; 25 | 26 | ProvidedChartComponent = props => ( 27 | <ChartContext.Provider value={testChart}> 28 | <ChildComponent {...props}>testtext</ChildComponent> 29 | </ChartContext.Provider> 30 | ); 31 | }); 32 | 33 | it('should call update function on mount', () => { 34 | render(<ProvidedChartComponent firstProp="first" />); 35 | 36 | expect(updateFn).toHaveBeenCalledWith(testChart, { firstProp: 'first' }); 37 | expect(destroyFn).not.toHaveBeenCalled(); 38 | }); 39 | 40 | it('should call update function with modifiedProps', () => { 41 | const wrapper = render(<ProvidedChartComponent firstProp="first" />); 42 | updateFn.mockClear(); 43 | wrapper.rerender( 44 | <ProvidedChartComponent firstProp="first2" secondProp="second" /> 45 | ); 46 | 47 | expect(updateFn).toHaveBeenCalledWith(testChart, { 48 | firstProp: 'first2', 49 | secondProp: 'second' 50 | }); 51 | expect(destroyFn).not.toHaveBeenCalled(); 52 | }); 53 | 54 | it("should not call update function when props don't change", () => { 55 | const wrapper = render(<ProvidedChartComponent firstProp="first" />); 56 | 57 | updateFn.mockClear(); 58 | wrapper.rerender(<ProvidedChartComponent firstProp="first" />); 59 | 60 | expect(updateFn).not.toHaveBeenCalled(); 61 | }); 62 | 63 | it('should call destroy function on unmount', () => { 64 | const wrapper = render(<ProvidedChartComponent firstProp="first" />); 65 | 66 | updateFn.mockClear(); 67 | wrapper.unmount(); 68 | 69 | expect(destroyFn).toHaveBeenCalledWith(testChart); 70 | expect(updateFn).not.toHaveBeenCalled(); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/UseHighcharts/useHighcharts.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import HighchartsContext from '../../../src/components/HighchartsContext'; 5 | import { Highcharts } from '../../test-utils'; 6 | 7 | import ContextSpy from '../../ContextSpy'; 8 | 9 | describe('useHighcharts', () => { 10 | let ProvidedHighchartsComponent; 11 | let highchartsRef; 12 | 13 | beforeEach(() => { 14 | highchartsRef = {}; 15 | 16 | ProvidedHighchartsComponent = () => ( 17 | <HighchartsContext.Provider value={Highcharts}> 18 | <ContextSpy highchartsRef={highchartsRef} /> 19 | </HighchartsContext.Provider> 20 | ); 21 | }); 22 | it('should return Highcharts from context', () => { 23 | render(<ProvidedHighchartsComponent />); 24 | 25 | expect(highchartsRef.current).toEqual(Highcharts); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/UseManualEventHandlers/useManualEventHandlers.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import useManualEventHandlers from '../../../src/components/UseManualEventHandlers'; 5 | 6 | import { Highcharts } from '../../test-utils'; 7 | import HighchartContext from '../../../src/components/HighchartsContext'; 8 | 9 | describe('useManualEventHandlers', () => { 10 | let ProvidedComponent; 11 | let target; 12 | 13 | beforeEach(() => { 14 | target = new Object(); 15 | const Component = props => { 16 | useManualEventHandlers(props, target); 17 | return <div />; 18 | }; 19 | ProvidedComponent = props => ( 20 | <HighchartContext.Provider value={Highcharts}> 21 | <Component {...props} /> 22 | </HighchartContext.Provider> 23 | ); 24 | }); 25 | 26 | afterEach(() => { 27 | Highcharts.removeEvent.mockClear(); 28 | Highcharts.addEvent.mockClear(); 29 | }); 30 | 31 | describe('when mounted', () => { 32 | it('should call the Highcharts.addEvent', () => { 33 | const onEventHandler = jest.fn(); 34 | const onOtherEventHandler = jest.fn(); 35 | 36 | const props = { 37 | enabled: true, 38 | onEventHandler, 39 | onOtherEventHandler, 40 | onNotAFunction: 'trip', 41 | something: 'stringy', 42 | count: 14 43 | }; 44 | 45 | render(<ProvidedComponent {...props} />); 46 | 47 | expect(Highcharts.removeEvent).not.toHaveBeenCalled(); 48 | expect(Highcharts.addEvent).toHaveBeenCalledWith( 49 | target, 50 | 'eventHandler', 51 | onEventHandler 52 | ); 53 | expect(Highcharts.addEvent).toHaveBeenCalledWith( 54 | target, 55 | 'otherEventHandler', 56 | onOtherEventHandler 57 | ); 58 | expect(Highcharts.addEvent).not.toHaveBeenCalledWith( 59 | target, 60 | 'onNotAFunction', 61 | 'trip' 62 | ); 63 | }); 64 | 65 | it('should not call the Highcharts.addEvent when props is undefined', () => { 66 | render(<ProvidedComponent />); 67 | 68 | expect(Highcharts.addEvent).not.toHaveBeenCalled(); 69 | }); 70 | }); 71 | 72 | describe('when updated', () => { 73 | it('should call the Highcharts.removeEvent and addEvent with changed handlers', () => { 74 | const onEventHandler = jest.fn(); 75 | const onOtherEventHandler = jest.fn(); 76 | const onThirdEventHandler = jest.fn(); 77 | const props = { 78 | onEventHandler, 79 | onOtherEventHandler, 80 | onThirdEventHandler 81 | }; 82 | 83 | const wrapper = render(<ProvidedComponent {...props} />); 84 | Highcharts.addEvent.mockClear(); 85 | Highcharts.removeEvent.mockClear(); 86 | const onNewOtherEventHandler = jest.fn(); 87 | 88 | const updatedProps = { 89 | onEventHandler, 90 | onOtherEventHandler: onNewOtherEventHandler 91 | }; 92 | wrapper.rerender(<ProvidedComponent {...props} {...updatedProps} />); 93 | 94 | expect(Highcharts.removeEvent).not.toHaveBeenCalledWith( 95 | target, 96 | 'eventHandler', 97 | onEventHandler 98 | ); 99 | expect(Highcharts.addEvent).not.toHaveBeenCalledWith( 100 | target, 101 | 'eventHandler', 102 | onEventHandler 103 | ); 104 | 105 | expect(Highcharts.removeEvent).toHaveBeenCalledWith( 106 | target, 107 | 'otherEventHandler', 108 | onOtherEventHandler 109 | ); 110 | expect(Highcharts.addEvent).toHaveBeenCalledWith( 111 | target, 112 | 'otherEventHandler', 113 | onNewOtherEventHandler 114 | ); 115 | /* TODO removing eventhandlers completely fails. 116 | expect(Highcharts.removeEvent).toHaveBeenCalledWith( 117 | target, 118 | 'thirdEventHandler', 119 | onThirdEventHandler 120 | ); 121 | */ 122 | }); 123 | }); 124 | }); 125 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/UseModifiedProps/useModifiedProps.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import useModifiedProps from '../../../src/components/UseModifiedProps'; 5 | 6 | describe('useChartUpdate', () => { 7 | let Component; 8 | let callback; 9 | 10 | beforeEach(() => { 11 | callback = jest.fn(); 12 | Component = props => { 13 | const modifiedProps = useModifiedProps(props); 14 | callback(modifiedProps); 15 | return null; 16 | }; 17 | }); 18 | 19 | it('should return all props on initial mount', () => { 20 | render(<Component someProp={true} />); 21 | expect(callback).toHaveBeenCalledWith({ someProp: true }); 22 | }); 23 | 24 | it('should return changed props', () => { 25 | const wrapper = render(<Component someProp={false} otherProp={false} />); 26 | callback.mockClear(); 27 | wrapper.rerender(<Component someProp={true} otherProp={false} />); 28 | 29 | expect(callback).toHaveBeenCalledWith({ someProp: true }); 30 | }); 31 | 32 | it('should return false for not changed props', () => { 33 | const wrapper = render(<Component someProp={true} />); 34 | callback.mockClear(); 35 | wrapper.rerender(<Component someProp={true} />); 36 | 37 | expect(callback).toHaveBeenCalledWith(false); 38 | }); 39 | 40 | it('should return false for empty props', () => { 41 | render(<Component />); 42 | 43 | expect(callback).toHaveBeenCalledWith(false); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/UsePlotBandLine/usePlotBandLine.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import PlotBandLineContext from '../../../src/components/PlotBandLineContext'; 5 | 6 | import ContextSpy from '../../ContextSpy'; 7 | 8 | describe('usePlotBandLine', () => { 9 | let ProvidedPlotBandLineComponent; 10 | let testPlotBandLine; 11 | let plotBandLineRef; 12 | 13 | beforeEach(() => { 14 | testPlotBandLine = {}; 15 | plotBandLineRef = {}; 16 | 17 | ProvidedPlotBandLineComponent = () => ( 18 | <PlotBandLineContext.Provider value={testPlotBandLine}> 19 | <ContextSpy plotBandLineRef={plotBandLineRef} /> 20 | </PlotBandLineContext.Provider> 21 | ); 22 | }); 23 | it('should return PlotBandLine from context', () => { 24 | render(<ProvidedPlotBandLineComponent />); 25 | 26 | expect(plotBandLineRef.current).toEqual(testPlotBandLine); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/UseSeries/useSeries.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import useSeries from '../../../src/components/UseSeries'; 5 | import SeriesContext from '../../../src/components/SeriesContext'; 6 | import ChartContext from '../../../src/components/ChartContext'; 7 | import { createMockSeries } from '../../test-utils'; 8 | import * as createProvidedSeries from '../../../src/components/Series/createProvidedSeries'; 9 | 10 | describe('useSeries', () => { 11 | let ChildComponent; 12 | let testSeries; 13 | let testChart; 14 | let seriesCallback; 15 | 16 | beforeEach(() => { 17 | jest.useFakeTimers(); 18 | 19 | testSeries = createMockSeries(); 20 | 21 | testChart = { 22 | get: jest.fn().mockImplementation(() => testSeries) 23 | }; 24 | seriesCallback = jest.fn(); 25 | 26 | jest.spyOn(createProvidedSeries, 'default').mockImplementation(c => c); 27 | 28 | ChildComponent = props => { 29 | const axis = useSeries(props.seriesId); 30 | seriesCallback(axis); 31 | return null; 32 | }; 33 | }); 34 | 35 | afterEach(() => { 36 | jest.clearAllTimers(); 37 | }); 38 | 39 | it('should return series from context', () => { 40 | render( 41 | <SeriesContext.Provider value={testSeries}> 42 | <ChildComponent /> 43 | </SeriesContext.Provider> 44 | ); 45 | 46 | expect(seriesCallback).toHaveBeenCalledWith(testSeries); 47 | }); 48 | 49 | it('should return series outside the context', () => { 50 | render( 51 | <ChartContext.Provider value={testChart}> 52 | <ChildComponent seriesId="mySeriesId" /> 53 | </ChartContext.Provider> 54 | ); 55 | 56 | expect(testChart.get).toHaveBeenCalledWith('mySeriesId'); 57 | expect(seriesCallback).toHaveBeenCalledWith(testSeries); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/WithHighcharts/index.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import withHighcharts from '../../../src/components/WithHighcharts'; 5 | import { Highcharts } from '../../test-utils'; 6 | import ContextSpy from '../../ContextSpy'; 7 | 8 | describe('withHighcharts', () => { 9 | let highchartsRef; 10 | let WrappedComponent; 11 | 12 | beforeEach(() => { 13 | highchartsRef = {}; 14 | 15 | WrappedComponent = () => <ContextSpy highchartsRef={highchartsRef} />; 16 | }); 17 | 18 | it('should create Highcharts context with the provided object', () => { 19 | const WithHighchartsComponent = withHighcharts( 20 | WrappedComponent, 21 | Highcharts 22 | ); 23 | render(<WithHighchartsComponent />); 24 | 25 | expect(highchartsRef.current).toEqual(Highcharts); 26 | }); 27 | 28 | it('should create a Highcharts context with the provided object (2)', () => { 29 | const HighchartsWithExtraFunctionality = { 30 | ...Highcharts, 31 | Extras: () => 'Extras' 32 | }; 33 | const WithHighchartsComponent = withHighcharts( 34 | WrappedComponent, 35 | HighchartsWithExtraFunctionality 36 | ); 37 | render(<WithHighchartsComponent />); 38 | 39 | expect(highchartsRef.current).toEqual(HighchartsWithExtraFunctionality); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/WithSeriesType/index.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import ShallowRenderer from 'react-test-renderer/shallow'; 3 | 4 | import withSeriesType from '../../../src/components/WithSeriesType'; 5 | import Series from '../../../src/components/Series'; 6 | 7 | describe('withSeriesType', () => { 8 | let renderer; 9 | 10 | beforeEach(() => { 11 | renderer = new ShallowRenderer(); 12 | }); 13 | 14 | it('should create Series component', () => { 15 | const SeriesComponent = withSeriesType('line'); 16 | renderer.render(<SeriesComponent />); 17 | const result = renderer.getRenderOutput(); 18 | 19 | expect(result.type).toEqual(Series); 20 | }); 21 | 22 | it(`should set type attribute <Series type="line" />`, () => { 23 | const SeriesComponent = withSeriesType('line'); 24 | renderer.render(<SeriesComponent />); 25 | const result = renderer.getRenderOutput(); 26 | 27 | expect(result.props).toHaveProperty('type', 'line'); 28 | }); 29 | 30 | it(`the created component should pass additional props through to Series`, () => { 31 | const SeriesComponent = withSeriesType('line'); 32 | renderer.render(<SeriesComponent data={[1, 2, 3, 4]} />); 33 | const result = renderer.getRenderOutput(); 34 | 35 | expect(result.props).toHaveProperty('data', [1, 2, 3, 4]); 36 | }); 37 | 38 | it(`should pass additionalProps to Series`, () => { 39 | const SeriesComponent = withSeriesType('line', { requiresAxis: false }); 40 | renderer.render(<SeriesComponent />); 41 | const result = renderer.getRenderOutput(); 42 | 43 | expect(result.props).toHaveProperty('requiresAxis', false); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/YAxis/YAxis.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import ShallowRenderer from 'react-test-renderer/shallow'; 3 | 4 | import YAxis from '../../../src/components/YAxis/YAxis'; 5 | import Axis from '../../../src/components/Axis'; 6 | 7 | describe('<YAxis />', () => { 8 | let renderer; 9 | 10 | beforeEach(() => { 11 | renderer = new ShallowRenderer(); 12 | }); 13 | 14 | it('renders an <Axis />', () => { 15 | renderer.render(<YAxis id="y" />); 16 | const result = renderer.getRenderOutput(); 17 | 18 | expect(result.type).toEqual(Axis); 19 | }); 20 | 21 | it('renders an <Axis isX={false} />', () => { 22 | renderer.render(<YAxis id="yAxis" />); 23 | const result = renderer.getRenderOutput(); 24 | 25 | expect(result.props).toHaveProperty('isX', false); 26 | }); 27 | 28 | it('passes other props through to <Axis />', () => { 29 | renderer.render(<YAxis id="myOtherAxis" tickLength={1337} />); 30 | const result = renderer.getRenderOutput(); 31 | 32 | expect(result.props).toHaveProperty('tickLength', 1337); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/components/ZAxis/ZAxis.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import ShallowRenderer from 'react-test-renderer/shallow'; 3 | 4 | import ZAxis from '../../../src/components/ZAxis/ZAxis'; 5 | import Axis from '../../../src/components/Axis'; 6 | 7 | describe('<ZAxis />', () => { 8 | let renderer; 9 | 10 | beforeEach(() => { 11 | renderer = new ShallowRenderer(); 12 | }); 13 | 14 | it('renders an <Axis />', () => { 15 | renderer.render(<ZAxis />); 16 | const result = renderer.getRenderOutput(); 17 | 18 | expect(result.type).toEqual(Axis); 19 | }); 20 | 21 | it('should always have the id `zAxis`', () => { 22 | renderer.render(<ZAxis id="customId" />); 23 | const result = renderer.getRenderOutput(); 24 | 25 | expect(result.props).toHaveProperty('id', 'zAxis'); 26 | }); 27 | 28 | it('should NOT be a dynamic axis', () => { 29 | renderer.render(<ZAxis />); 30 | const result = renderer.getRenderOutput(); 31 | 32 | expect(result.props).toHaveProperty('dynamicAxis', false); 33 | }); 34 | 35 | it('renders an <Axis isX={false} />', () => { 36 | renderer.render(<ZAxis id="ZAxis" />); 37 | const result = renderer.getRenderOutput(); 38 | 39 | expect(result.props).toHaveProperty('isX', false); 40 | }); 41 | 42 | it('passes other props through to <Axis />', () => { 43 | renderer.render(<ZAxis tickLength={1337} />); 44 | const result = renderer.getRenderOutput(); 45 | 46 | expect(result.props).toHaveProperty('tickLength', 1337); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/test-helper.js: -------------------------------------------------------------------------------- 1 | // test setup here 2 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/test-utils.js: -------------------------------------------------------------------------------- 1 | export const uuidRegex = 2 | /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i; 3 | 4 | export const Highcharts = { 5 | chart: jest.fn(), 6 | addEvent: jest.fn(), 7 | removeEvent: jest.fn(), 8 | Tooltip: jest.fn().mockImplementation(() => ({ update: jest.fn() })) 9 | }; 10 | 11 | export const createMockChart = () => ({ 12 | addAxis: jest.fn(), 13 | addColorAxis: jest.fn(), 14 | addSeries: jest.fn(), 15 | get: jest.fn(), 16 | setSize: jest.fn(), 17 | update: jest.fn(), 18 | setTitle: jest.fn(), 19 | destroy: jest.fn(), 20 | showLoading: jest.fn(), 21 | hideLoading: jest.fn(), 22 | addCredits: jest.fn(), 23 | redraw: jest.fn(), 24 | setCaption: jest.fn() 25 | }); 26 | 27 | export const createMockProvidedChart = () => { 28 | const chartStubs = createMockChart(); 29 | chartStubs.needsRedraw = jest.fn(); 30 | return { 31 | chartStubs, 32 | needsRedraw: chartStubs.needsRedraw 33 | }; 34 | }; 35 | 36 | export const createMockAxis = ({ ...additional } = {}) => ({ 37 | ...additional, 38 | remove: jest.fn(), 39 | addPlotBandOrLine: jest 40 | .fn() 41 | .mockImplementation(() => ({ options: {}, render: jest.fn() })), 42 | removePlotBandOrLine: jest.fn(), 43 | getExtremes: jest.fn(), 44 | setExtremes: jest.fn(), 45 | update: jest.fn(), 46 | setTitle: jest.fn(), 47 | plotLinesAndBands: [] 48 | }); 49 | 50 | export const createMockProvidedAxis = ({ object, ...additional }) => { 51 | const axisStubs = createMockAxis(additional); 52 | 53 | return { 54 | axisStubs, 55 | providedAxis: { 56 | object, 57 | ...additional, 58 | ...axisStubs 59 | } 60 | }; 61 | }; 62 | 63 | export const createMockSeries = ({ ...additional } = {}) => ({ 64 | ...additional, 65 | remove: jest.fn(), 66 | setData: jest.fn(), 67 | setVisible: jest.fn(), 68 | update: jest.fn() 69 | }); 70 | 71 | export const createMockProvidedSeries = ({ object, ...additional }) => { 72 | const seriesStubs = createMockSeries(additional); 73 | 74 | return { 75 | seriesStubs, 76 | getSeries: () => ({ 77 | object, 78 | ...additional, 79 | ...seriesStubs 80 | }) 81 | }; 82 | }; 83 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/utils/events.spec.js: -------------------------------------------------------------------------------- 1 | import * as events from '../../src/utils/events'; 2 | import { Highcharts } from '../test-utils'; 3 | 4 | describe('utils/events', () => { 5 | beforeEach(() => { 6 | Highcharts.addEvent.mockClear(); 7 | Highcharts.removeEvent.mockClear(); 8 | }); 9 | 10 | describe('getEventHandlerProps', () => { 11 | const { getEventHandlerProps } = events; 12 | 13 | it('should return all props that look like an event handler', () => { 14 | const onEventHandler = jest.fn(); 15 | const onOtherEventHandler = jest.fn(); 16 | 17 | const config = { 18 | enabled: true, 19 | onEventHandler, 20 | onOtherEventHandler, 21 | onNotAFunction: 'trip', 22 | something: 'stringy', 23 | count: 14 24 | }; 25 | 26 | expect(getEventHandlerProps(config)).toEqual({ 27 | onEventHandler, 28 | onOtherEventHandler 29 | }); 30 | }); 31 | }); 32 | 33 | describe('getNonEventHandlerProps', () => { 34 | const { getNonEventHandlerProps } = events; 35 | 36 | it("should return all props that don't look like an event handler", () => { 37 | const config = { 38 | enabled: true, 39 | onEventHandler: jest.fn(), 40 | onOtherEventHandler: jest.fn(), 41 | onNotAFunction: 'trip', 42 | something: 'stringy', 43 | count: 14 44 | }; 45 | 46 | expect(getNonEventHandlerProps(config)).toEqual({ 47 | enabled: true, 48 | something: 'stringy', 49 | onNotAFunction: 'trip', 50 | count: 14 51 | }); 52 | }); 53 | }); 54 | 55 | describe('getEventsConfig', () => { 56 | const { getEventsConfig } = events; 57 | 58 | it('should return all props that look like an event handler, without the `on` prefix', () => { 59 | const onEventHandler = jest.fn(); 60 | const onOtherEventHandler = jest.fn(); 61 | 62 | const config = { 63 | enabled: true, 64 | onEventHandler, 65 | onOtherEventHandler, 66 | onNotAFunction: 'trip', 67 | something: 'stringy', 68 | count: 14 69 | }; 70 | 71 | expect(getEventsConfig(config)).toEqual({ 72 | eventHandler: onEventHandler, 73 | otherEventHandler: onOtherEventHandler 74 | }); 75 | }); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/utils/getModifiedProps.spec.js: -------------------------------------------------------------------------------- 1 | import getModifiedProps from '../../src/utils/getModifiedProps'; 2 | 3 | describe('utils/getModifiedProps', () => { 4 | it('should return only modified props', () => { 5 | const prevProps = { 6 | firstProp: true, 7 | secondProp: true 8 | }; 9 | 10 | const currProps = { 11 | firstProp: true, 12 | secondProp: false 13 | }; 14 | 15 | const returnedProps = getModifiedProps(prevProps, currProps, false); 16 | expect(returnedProps).toEqual({ secondProp: false }); 17 | }); 18 | 19 | it('should return false for non modified props', () => { 20 | const prevProps = { 21 | firstProp: true, 22 | secondProp: true 23 | }; 24 | 25 | const returnedProps = getModifiedProps(prevProps, prevProps, false); 26 | expect(returnedProps).toEqual(false); 27 | }); 28 | 29 | it('should return added props', () => { 30 | const prevProps = { 31 | firstProp: true 32 | }; 33 | 34 | const currProps = { 35 | secondProp: false 36 | }; 37 | 38 | const returnedProps = getModifiedProps(prevProps, currProps, false); 39 | expect(returnedProps).toEqual({ secondProp: false }); 40 | }); 41 | 42 | it('should return modified props for undefined prevProps', () => { 43 | const prevProps = undefined; 44 | 45 | const currProps = { 46 | firstProp: true, 47 | secondProp: false 48 | }; 49 | 50 | const returnedProps = getModifiedProps(prevProps, currProps, false); 51 | expect(returnedProps).toEqual(currProps); 52 | }); 53 | 54 | it('should return modified props for null prevProps', () => { 55 | const prevProps = null; 56 | 57 | const currProps = { 58 | firstProp: true, 59 | secondProp: false 60 | }; 61 | 62 | const returnedProps = getModifiedProps(prevProps, currProps, false); 63 | expect(returnedProps).toEqual(currProps); 64 | }); 65 | it('should not return text when children is not defined', () => { 66 | const prevProps = null; 67 | 68 | const currProps = { 69 | a: 1 70 | }; 71 | 72 | const returnedProps = getModifiedProps(prevProps, currProps, true); 73 | expect(returnedProps.text).not.toBeDefined(); 74 | expect(returnedProps.a).toBe(1); 75 | }); 76 | 77 | it('should return text when children changes', () => { 78 | const prevProps = {}; 79 | 80 | const currProps = { 81 | children: 'teststring' 82 | }; 83 | 84 | const returnedProps = getModifiedProps(prevProps, currProps, true); 85 | expect(returnedProps.text).toBe('teststring'); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/test/utils/pickBy.spec.js: -------------------------------------------------------------------------------- 1 | import pickBy from '../../src/utils/pickBy'; 2 | 3 | describe('utils/pickBy', () => { 4 | it('should return object which satisfies filter function', () => { 5 | const onInit = jest.fn(); 6 | const props = { 7 | onInit, 8 | noPassed: false 9 | }; 10 | const pickedProps = pickBy(props, key => { 11 | return key === 'onInit'; 12 | }); 13 | 14 | expect(pickedProps).toEqual({ onInit }); 15 | }); 16 | 17 | it('should return empty object for undefined object', () => { 18 | const pickedProps = pickBy(undefined, key => { 19 | return key === 'onInit'; 20 | }); 21 | 22 | expect(pickedProps).toEqual({}); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "noImplicitAny": true, 5 | "target": "ES2020" 6 | }, 7 | "include": ["./types/**/*"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/react-jsx-highcharts/webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | const path = require('path'); 3 | 4 | const isProd = process.env.NODE_ENV === 'production'; 5 | 6 | const webpackConfig = { 7 | mode: 'development', 8 | 9 | entry: path.resolve(__dirname, 'src'), 10 | 11 | output: { 12 | filename: isProd 13 | ? 'react-jsx-highcharts.min.js' 14 | : 'react-jsx-highcharts.js', 15 | path: path.resolve(__dirname, 'dist'), 16 | library: 'ReactHighcharts', 17 | libraryTarget: 'umd', 18 | // Prevents webpack from referencing `window` in the UMD build 19 | // Source: https://git.io/vppgU 20 | globalObject: "typeof self !== 'undefined' ? self : this" 21 | }, 22 | 23 | externals: { 24 | react: { 25 | commonjs: 'react', 26 | commonjs2: 'react', 27 | amd: 'react', 28 | root: 'React' 29 | }, 30 | 'react-dom': { 31 | commonjs: 'react-dom', 32 | commonjs2: 'react-dom', 33 | amd: 'react-dom', 34 | root: 'ReactDOM' 35 | }, 36 | highcharts: { 37 | commonjs: 'highcharts', 38 | commonjs2: 'highcharts', 39 | amd: 'highcharts', 40 | root: 'Highcharts' 41 | } 42 | }, 43 | 44 | module: { 45 | rules: [ 46 | { 47 | test: /\.js$/, 48 | loader: 'babel-loader', 49 | exclude: /node_modules/ 50 | } 51 | ] 52 | } 53 | }; 54 | 55 | if (isProd) { 56 | webpackConfig.mode = 'production'; 57 | } 58 | 59 | module.exports = webpackConfig; 60 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/.babelrc: -------------------------------------------------------------------------------- 1 | { "extends": "../../babel.config.js" } 2 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/jest.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | // For a detailed explanation regarding each configuration property, visit: 3 | // https://jestjs.io/docs/en/configuration.html 4 | 5 | /** @type {import('@jest/types').Config.InitialOptions} */ 6 | const config = { 7 | testEnvironment: 'jsdom', 8 | setupFilesAfterEnv: ['<rootDir>/test/test-helper.js'], 9 | testMatch: ['**/test/**/*.spec.js?(x)'] 10 | }; 11 | 12 | module.exports = config; 13 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-jsx-highmaps", 3 | "version": "3.0.0", 4 | "description": "Highmaps built using React components", 5 | "main": "dist/react-jsx-highmaps.min.js", 6 | "module": "dist/es/index.js", 7 | "sideEffects": false, 8 | "files": [ 9 | "dist", 10 | "src", 11 | "types" 12 | ], 13 | "scripts": { 14 | "build": "cross-env NODE_ENV=development webpack", 15 | "build:prod": "npm run build:umd && npm run build:es", 16 | "build:umd": "cross-env NODE_ENV=production webpack", 17 | "build:es": "cross-env BABEL_ENV=es babel src --out-dir dist/es", 18 | "clean": "rimraf dist", 19 | "format": "prettier --write \"src/**/*.js\" \"test/**/*.js\" README.md", 20 | "lint": "eslint src", 21 | "test": "jest", 22 | "test:coverage": "jest --coverage", 23 | "test:types": "tsc --noEmit" 24 | }, 25 | "author": "Will Hawker", 26 | "contributors": [], 27 | "license": "MIT", 28 | "repository": { 29 | "type": "git", 30 | "url": "https://github.com/whawker/react-jsx-highcharts.git" 31 | }, 32 | "bugs": "https://github.com/whawker/react-jsx-highcharts/issues", 33 | "homepage": "https://github.com/whawker/react-jsx-highcharts", 34 | "keywords": [ 35 | "react", 36 | "reactjs", 37 | "react-component", 38 | "highcharts", 39 | "highmaps", 40 | "chart", 41 | "charts", 42 | "graphs", 43 | "visualization", 44 | "data", 45 | "maps" 46 | ], 47 | "dependencies": { 48 | "react-jsx-highcharts": "5.0.0" 49 | }, 50 | "peerDependencies": { 51 | "highcharts": "^9.1.2 || ^10.0.0", 52 | "react": "^17.0.0 || ^18.0.0", 53 | "react-dom": "^17.0.0 || ^18.0.0" 54 | }, 55 | "types": "types/index.d.ts", 56 | "browserslist": [ 57 | "chrome >= 81", 58 | "edge >= 81", 59 | "firefox >= 78", 60 | "ios >= 12", 61 | "safari >= 12" 62 | ] 63 | } 64 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/src/components/HighchartsMapChart/HighchartsMapChart.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useMemo, useCallback } from 'react'; 3 | import { BaseChart, useHighcharts } from 'react-jsx-highcharts'; 4 | 5 | const XAXIS = { id: 'xAxis' }; 6 | const YAXIS = { id: 'yAxis' }; 7 | const MAP_NAVIGATION = { enabled: false }; 8 | 9 | const HighchartsMapChart = ({ map, chart, callback, ...restProps }) => { 10 | const Highcharts = useHighcharts(); 11 | const geojson = useMemo(() => { 12 | return createGeoJSON(map, Highcharts); 13 | }, [map]); 14 | const chartConfig = useMemo( 15 | () => ({ ...chart, map: geojson }), 16 | [geojson, chart] 17 | ); 18 | 19 | const chartCallback = useCallback( 20 | cbChart => { 21 | if (geojson) { 22 | const format = Highcharts.format; 23 | const { mapText, mapTextFull } = cbChart.options.credits; 24 | cbChart.mapCredits = format(mapText, { geojson }); 25 | cbChart.mapCreditsFull = format(mapTextFull, { geojson }); 26 | } 27 | 28 | if (callback) callback(cbChart); 29 | }, 30 | [callback] 31 | ); 32 | 33 | return ( 34 | <BaseChart 35 | chart={chartConfig} 36 | mapNavigation={MAP_NAVIGATION} 37 | xAxis={XAXIS} 38 | yAxis={YAXIS} 39 | {...restProps} 40 | callback={chartCallback} 41 | chartCreationFunc={Highcharts.mapChart} 42 | chartType="mapChart" 43 | /> 44 | ); 45 | }; 46 | 47 | const createGeoJSON = (map, Highcharts) => { 48 | if (!map) return; 49 | 50 | return typeof map === 'string' ? Highcharts.maps[map] : map; 51 | }; 52 | export default HighchartsMapChart; 53 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/src/components/HighchartsMapChart/index.js: -------------------------------------------------------------------------------- 1 | import HighchartsMapChart from './HighchartsMapChart'; 2 | export default HighchartsMapChart; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/src/components/MapNavigation/MapNavigation.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useEffect, useState } from 'react'; 3 | import { 4 | useModifiedProps, 5 | useChart, 6 | useHighcharts 7 | } from 'react-jsx-highcharts'; 8 | 9 | const MapNavigation = ({ children, enabled = true, ...restProps }) => { 10 | const [rendered, setRendered] = useState(false); 11 | const chart = useChart(); 12 | const Highcharts = useHighcharts(); 13 | 14 | useEffect(() => { 15 | // Workaround inferred from http://jsfiddle.net/x40me94t/2/ 16 | const chartObj = chart.object; 17 | chartObj.options.mapNavigation.enabled = true; 18 | // Initialise MapNavigation https://github.com/highcharts/highcharts/blob/dd730ab/js/parts-map/MapNavigation.js#L288-L294 19 | Highcharts.fireEvent(chartObj, 'beforeRender'); 20 | 21 | const opts = getMapNavigationConfig({ enabled, ...restProps }, Highcharts); 22 | updateMapNavigation(opts, chart); 23 | 24 | setRendered(true); 25 | 26 | return () => { 27 | try { 28 | updateMapNavigation({ enabled: false }, chart); 29 | } catch { 30 | // ignore as chart might have already been unmounted 31 | } 32 | }; 33 | }, []); 34 | 35 | const modifiedProps = useModifiedProps({ enabled, ...restProps }); 36 | 37 | useEffect(() => { 38 | if (!rendered) return; 39 | if (modifiedProps !== false) { 40 | updateMapNavigation(modifiedProps, chart); 41 | } 42 | }); 43 | 44 | if (!children || !rendered) return null; 45 | 46 | return <>{children}</>; 47 | }; 48 | const getMapNavigationConfig = (props, Highcharts) => { 49 | return { 50 | ...(Highcharts.defaultOptions && Highcharts.defaultOptions.mapNavigation), 51 | ...props, 52 | enableButtons: false, 53 | buttons: { 54 | zoomIn: {}, 55 | zoomOut: {} 56 | } 57 | }; 58 | }; 59 | 60 | const updateMapNavigation = (config, chart) => { 61 | chart.update({ mapNavigation: config }, true); 62 | }; 63 | 64 | export default MapNavigation; 65 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/src/components/MapNavigation/MapNavigationButton.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { useHighcharts, useChart } from 'react-jsx-highcharts'; 3 | 4 | const MapNavigationButton = props => { 5 | const Highcharts = useHighcharts(); 6 | const chart = useChart(); 7 | 8 | useEffect(() => { 9 | const { type, ...rest } = props; 10 | const opts = getMapNavigationButtonConfig(rest, Highcharts); 11 | updateMapNavigationButton(type, opts, chart); 12 | 13 | return () => { 14 | // TODO removeButton was missing in original class? 15 | //const { type } = props; 16 | //attempt(this.removeButton, type, {}); 17 | }; 18 | }, []); 19 | 20 | return null; 21 | }; 22 | const getMapNavigationButtonConfig = (props, Highcharts) => { 23 | const { children: text, onClick: onclick, ...rest } = props; 24 | 25 | return { 26 | ...(Highcharts.defaultOptions && 27 | Highcharts.defaultOptions.mapNavigation.buttonOptions), 28 | onclick, // Weird Highcharts inconsistency, onclick instead of events: { click } 29 | ...rest, 30 | text 31 | }; 32 | }; 33 | 34 | const updateMapNavigationButton = (type, config, chart) => { 35 | const enableButtons = Object.keys(config).length > 0; 36 | 37 | chart.update({ 38 | mapNavigation: { 39 | enableButtons, 40 | buttons: { 41 | [type]: config 42 | } 43 | } 44 | }); 45 | }; 46 | 47 | export default MapNavigationButton; 48 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/src/components/MapNavigation/MapNavigationZoomIn.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import MapNavigationButton from './MapNavigationButton'; 3 | 4 | const DEFAULT_ONCLICK = function () { 5 | this.mapZoom(0.5); 6 | }; 7 | 8 | const MapNavigationZoomIn = ({ 9 | children = '+', 10 | onClick = DEFAULT_ONCLICK, 11 | y = 0, 12 | ...restProps 13 | }) => ( 14 | <MapNavigationButton type="zoomIn" onClick={onClick} y={y} {...restProps}> 15 | {children} 16 | </MapNavigationButton> 17 | ); 18 | 19 | export default MapNavigationZoomIn; 20 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/src/components/MapNavigation/MapNavigationZoomOut.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import MapNavigationButton from './MapNavigationButton'; 3 | 4 | const DEFAULT_ONCLICK = function () { 5 | this.mapZoom(2); 6 | }; 7 | 8 | const MapNavigationZoomOut = ({ 9 | children = '-', 10 | onClick = DEFAULT_ONCLICK, 11 | y = 28, 12 | ...restProps 13 | }) => ( 14 | <MapNavigationButton type="zoomOut" onClick={onClick} y={y} {...restProps}> 15 | {children} 16 | </MapNavigationButton> 17 | ); 18 | 19 | export default MapNavigationZoomOut; 20 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/src/components/MapNavigation/index.js: -------------------------------------------------------------------------------- 1 | import MapNavigation from './MapNavigation'; 2 | import MapNavigationZoomIn from './MapNavigationZoomIn'; 3 | import MapNavigationZoomOut from './MapNavigationZoomOut'; 4 | const ChartMapNavigation = MapNavigation; 5 | ChartMapNavigation.ZoomIn = MapNavigationZoomIn; 6 | ChartMapNavigation.ZoomOut = MapNavigationZoomOut; 7 | export default ChartMapNavigation; 8 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/src/components/XAxis/XAxis.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { XAxis } from 'react-jsx-highcharts'; 3 | 4 | const MapXAxis = ({ 5 | endOnTick = false, 6 | visible = false, 7 | minPadding = 0, 8 | maxPadding = 0, 9 | startOnTick = false, 10 | ...restProps 11 | }) => ( 12 | <XAxis 13 | endOnTick={endOnTick} 14 | visible={visible} 15 | minPadding={minPadding} 16 | maxPadding={maxPadding} 17 | startOnTick={startOnTick} 18 | {...restProps} 19 | id="xAxis" 20 | dynamicAxis={false} 21 | /> 22 | ); 23 | 24 | export default MapXAxis; 25 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/src/components/XAxis/index.js: -------------------------------------------------------------------------------- 1 | import XAxis from './XAxis'; 2 | export default XAxis; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/src/components/YAxis/YAxis.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { YAxis } from 'react-jsx-highcharts'; 3 | 4 | const MapYAxis = ({ 5 | endOnTick = false, 6 | visible = false, 7 | minPadding = 0, 8 | maxPadding = 0, 9 | startOnTick = false, 10 | reversed = true, 11 | ...restProps 12 | }) => ( 13 | <YAxis 14 | endOnTick={endOnTick} 15 | visible={visible} 16 | minPadding={minPadding} 17 | maxPadding={maxPadding} 18 | startOnTick={startOnTick} 19 | reversed={reversed} 20 | {...restProps} 21 | id="yAxis" 22 | dynamicAxis={false} 23 | /> 24 | ); 25 | 26 | export default MapYAxis; 27 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/src/components/YAxis/index.js: -------------------------------------------------------------------------------- 1 | import YAxis from './YAxis'; 2 | export default YAxis; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/src/index.js: -------------------------------------------------------------------------------- 1 | import { withSeriesType } from 'react-jsx-highcharts'; 2 | export { 3 | Chart, 4 | ColorAxis, 5 | Credits, 6 | Debug, 7 | HighchartsProvider as HighmapsProvider, 8 | Loading, 9 | Legend, 10 | Series, 11 | Subtitle, 12 | Title, 13 | Tooltip, 14 | useHighcharts, 15 | useChart, 16 | useAxis, 17 | useSeries, 18 | withHighcharts as withHighmaps, 19 | withSeriesType 20 | } from 'react-jsx-highcharts'; 21 | 22 | // Charts 23 | export { default as HighchartsMapChart } from './components/HighchartsMapChart'; 24 | 25 | // Graph Parts 26 | export { default as MapNavigation } from './components/MapNavigation'; 27 | export { default as XAxis } from './components/XAxis'; 28 | export { default as YAxis } from './components/YAxis'; 29 | 30 | // Series 31 | const parentAxisId = { axisId: 'yAxis' }; 32 | export const MapBubbleSeries = withSeriesType('MapBubble', parentAxisId); 33 | export const MapLineSeries = withSeriesType('MapLine', parentAxisId); 34 | export const MapPointSeries = withSeriesType('MapPoint', parentAxisId); 35 | export const MapSeries = withSeriesType('Map', parentAxisId); 36 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "jest": true 5 | }, 6 | "rules": { 7 | "no-unused-vars": ["warn"], 8 | "react-perf/jsx-no-new-function-as-prop": "off", 9 | "react-perf/jsx-no-new-object-as-prop": "off", 10 | "react-perf/jsx-no-new-array-as-prop": "off", 11 | "react/display-name": "off" 12 | }, 13 | "globals": { 14 | "mount": true, 15 | "shallow": true, 16 | "render": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/test/components/HighchartsMapChart/HighchartsMapChart.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | jest.mock('react-jsx-highcharts', () => ({ 5 | ...jest.requireActual('react-jsx-highcharts'), 6 | useHighcharts: jest.fn() 7 | })); 8 | 9 | import { useHighcharts } from 'react-jsx-highcharts'; 10 | import { Highcharts, createMockChart } from '../../test-utils'; 11 | import HighchartsMapChart from '../../../src/components/HighchartsMapChart/HighchartsMapChart'; 12 | 13 | describe('<HighchartsMapChart />', () => { 14 | let chart; 15 | 16 | beforeEach(() => { 17 | chart = createMockChart(); 18 | Highcharts.mapChart.mockReturnValue(chart); 19 | useHighcharts.mockImplementation(() => Highcharts); 20 | }); 21 | 22 | afterEach(() => { 23 | Highcharts.mapChart.mockRestore(); 24 | }); 25 | 26 | it('creates a chart', () => { 27 | render(<HighchartsMapChart />); 28 | 29 | expect(Highcharts.mapChart).toHaveBeenCalled(); 30 | }); 31 | 32 | it('creates a chart with the correct chart type', () => { 33 | render(<HighchartsMapChart />); 34 | 35 | expect(Highcharts.mapChart).toHaveBeenCalledWith( 36 | expect.anything(), 37 | expect.objectContaining({ 38 | chartType: 'mapChart' 39 | }) 40 | ); 41 | }); 42 | 43 | it('creates a chart with GeoJSON from a string', () => { 44 | render(<HighchartsMapChart map="mock/map" />); 45 | 46 | expect(Highcharts.mapChart).toHaveBeenCalledWith( 47 | expect.anything(), 48 | expect.objectContaining({ 49 | chart: expect.objectContaining({ map: { some: 'data' } }) 50 | }) 51 | ); 52 | }); 53 | 54 | it('creates a chart with direct GeoJSON', () => { 55 | render(<HighchartsMapChart map={{ direct: 'input' }} />); 56 | 57 | expect(Highcharts.mapChart).toHaveBeenCalledWith( 58 | expect.anything(), 59 | expect.objectContaining({ 60 | chart: expect.objectContaining({ map: { direct: 'input' } }) 61 | }) 62 | ); 63 | }); 64 | 65 | it('passes other props through to chart', () => { 66 | render(<HighchartsMapChart plotOptions={{ c: 'd' }} />); 67 | 68 | expect(Highcharts.mapChart).toHaveBeenCalledWith( 69 | expect.anything(), 70 | expect.objectContaining({ plotOptions: { c: 'd' } }) 71 | ); 72 | }); 73 | 74 | it('return a chart instance to the callback prop', () => { 75 | let cbChart; 76 | const chartCallback = returnedChart => { 77 | cbChart = returnedChart; 78 | }; 79 | render(<HighchartsMapChart callback={chartCallback} />); 80 | expect(cbChart).toBeDefined(); 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/test/components/XAxis/XAxis.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import ShallowRenderer from 'react-test-renderer/shallow'; 3 | 4 | import { XAxis } from 'react-jsx-highcharts'; 5 | import MapXAxis from '../../../src/components/XAxis'; 6 | 7 | describe('<XAxis />', () => { 8 | let renderer; 9 | 10 | beforeEach(() => { 11 | renderer = new ShallowRenderer(); 12 | }); 13 | 14 | it('renders an <XAxis />', () => { 15 | renderer.render(<MapXAxis />); 16 | const result = renderer.getRenderOutput(); 17 | 18 | expect(result.type).toEqual(XAxis); 19 | }); 20 | 21 | it('should always have the id `xAxis`', () => { 22 | renderer.render(<MapXAxis id="customId" />); 23 | const result = renderer.getRenderOutput(); 24 | 25 | expect(result.props).toHaveProperty('id', 'xAxis'); 26 | }); 27 | 28 | it('should NOT be a dynamic axis', () => { 29 | renderer.render(<MapXAxis />); 30 | const result = renderer.getRenderOutput(); 31 | 32 | expect(result.props).toHaveProperty('dynamicAxis', false); 33 | }); 34 | 35 | it('passes other props through to <XAxis />', () => { 36 | renderer.render(<MapXAxis tickLength={1337} />); 37 | const result = renderer.getRenderOutput(); 38 | 39 | expect(result.props).toHaveProperty('tickLength', 1337); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/test/components/YAxis/YAxis.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import ShallowRenderer from 'react-test-renderer/shallow'; 3 | 4 | import { YAxis } from 'react-jsx-highcharts'; 5 | import MapYAxis from '../../../src/components/YAxis'; 6 | 7 | describe('<YAxis />', () => { 8 | let renderer; 9 | 10 | beforeEach(() => { 11 | renderer = new ShallowRenderer(); 12 | }); 13 | 14 | it('renders a <YAxis />', () => { 15 | renderer.render(<MapYAxis />); 16 | const result = renderer.getRenderOutput(); 17 | 18 | expect(result.type).toEqual(YAxis); 19 | }); 20 | 21 | it('should always have the id `yAxis`', () => { 22 | renderer.render(<MapYAxis id="customId" />); 23 | const result = renderer.getRenderOutput(); 24 | 25 | expect(result.props).toHaveProperty('id', 'yAxis'); 26 | }); 27 | 28 | it('should NOT be a dynamic axis', () => { 29 | renderer.render(<MapYAxis />); 30 | const result = renderer.getRenderOutput(); 31 | 32 | expect(result.props).toHaveProperty('dynamicAxis', false); 33 | }); 34 | 35 | it('passes other props through to <YAxis />', () => { 36 | renderer.render(<MapYAxis tickLength={1337} />); 37 | const result = renderer.getRenderOutput(); 38 | 39 | expect(result.props).toHaveProperty('tickLength', 1337); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/test/test-helper.js: -------------------------------------------------------------------------------- 1 | // test setup here 2 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/test/test-utils.js: -------------------------------------------------------------------------------- 1 | export const uuidRegex = 2 | /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i; 3 | 4 | export const Highcharts = { 5 | mapChart: jest.fn(), 6 | addEvent: jest.fn(), 7 | removeEvent: jest.fn(), 8 | maps: { 9 | 'mock/map': { some: 'data' } 10 | }, 11 | format: jest.fn() 12 | }; 13 | 14 | export const createMockChart = () => ({ 15 | addAxis: jest.fn(), 16 | addColorAxis: jest.fn(), 17 | addSeries: jest.fn(), 18 | get: jest.fn(), 19 | setSize: jest.fn(), 20 | update: jest.fn(), 21 | setTitle: jest.fn(), 22 | destroy: jest.fn(), 23 | showLoading: jest.fn(), 24 | hideLoading: jest.fn(), 25 | addCredits: jest.fn(), 26 | redraw: jest.fn(), 27 | setCaption: jest.fn(), 28 | options: { credits: {} } 29 | }); 30 | 31 | export const createMockProvidedChart = ({ object, ...additional }) => { 32 | const chartStubs = createMockChart(additional); 33 | 34 | return { 35 | chartStubs: { ...chartStubs, object } 36 | }; 37 | }; 38 | 39 | export const createMockAxis = ({ ...additional }) => ({ 40 | ...additional, 41 | remove: jest.fn(), 42 | addPlotBand: jest.fn(), 43 | removePlotBand: jest.fn(), 44 | addPlotLine: jest.fn(), 45 | removePlotLine: jest.fn(), 46 | getExtremes: jest.fn(), 47 | setExtremes: jest.fn(), 48 | update: jest.fn(), 49 | setTitle: jest.fn() 50 | }); 51 | 52 | export const createMockProvidedAxis = ({ object, ...additional }) => { 53 | const axisStubs = createMockAxis(additional); 54 | 55 | return { 56 | axisStubs, 57 | getAxis: () => ({ 58 | object, 59 | ...additional, 60 | ...axisStubs 61 | }) 62 | }; 63 | }; 64 | 65 | export const createMockSeries = ({ ...additional }) => ({ 66 | ...additional, 67 | remove: jest.fn(), 68 | setData: jest.fn(), 69 | setVisible: jest.fn(), 70 | update: jest.fn() 71 | }); 72 | 73 | export const createMockProvidedSeries = ({ object, ...additional }) => { 74 | const seriesStubs = createMockSeries(additional); 75 | 76 | return { 77 | seriesStubs, 78 | getSeries: () => ({ 79 | object, 80 | ...additional, 81 | ...seriesStubs 82 | }) 83 | }; 84 | }; 85 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "noImplicitAny": true, 5 | "target": "ES2020" 6 | }, 7 | "include": ["./types/**/*"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import type * as Highcharts from 'highcharts'; 2 | import type { ReactElement, ReactNode } from 'react'; 3 | import type { 4 | HighchartsChartProps, 5 | SeriesProps, 6 | AxisProps 7 | } from 'react-jsx-highcharts'; 8 | 9 | export { 10 | Chart, 11 | ColorAxis, 12 | Credits, 13 | Debug, 14 | HighchartsProvider as HighmapsProvider, 15 | Loading, 16 | Legend, 17 | Series, 18 | Subtitle, 19 | Title, 20 | Tooltip, 21 | useHighcharts, 22 | useChart, 23 | useAxis, 24 | useSeries, 25 | withHighcharts as withHighmaps, 26 | withSeriesType 27 | } from 'react-jsx-highcharts'; 28 | 29 | type HighchartsMapChartsProps = HighchartsChartProps & { 30 | map: Highcharts.GeoJSON; 31 | }; 32 | 33 | export function HighchartsMapChart( 34 | props: HighchartsMapChartsProps 35 | ): ReactElement; 36 | 37 | // Series 38 | export function MapSeries( 39 | props: SeriesProps<Highcharts.SeriesMapOptions> 40 | ): ReactElement; 41 | 42 | export function MapPointSeries( 43 | props: SeriesProps<Highcharts.SeriesMappointOptions> 44 | ): ReactElement; 45 | 46 | export function MapLineSeries( 47 | props: SeriesProps<Highcharts.SeriesMaplineOptions> 48 | ): ReactElement; 49 | 50 | export function MapBubbleSeries( 51 | props: SeriesProps<Highcharts.SeriesMapbubbleOptions> 52 | ): ReactElement; 53 | 54 | // map navigator 55 | type MapNavigatorProps = { 56 | children?: ReactNode; 57 | } & Partial<Highcharts.MapNavigationOptions>; 58 | 59 | export function MapNavigation(props: MapNavigatorProps): ReactElement; 60 | export namespace MapNavigation { 61 | type ZoomButtonProps<TButtonProps> = { children?: ReactNode } & Omit< 62 | TButtonProps, 63 | 'text' 64 | >; 65 | 66 | export function ZoomIn( 67 | props: ZoomButtonProps<Highcharts.MapNavigationButtonsZoomInOptions> 68 | ): ReactElement; 69 | 70 | export function ZoomOut( 71 | props: ZoomButtonProps<Highcharts.MapNavigationButtonsZoomOutOptions> 72 | ): ReactElement; 73 | 74 | type MapButtonProps = { 75 | children?: ReactNode; 76 | type: 'zoomIn' | 'zoomOut'; 77 | } & Omit<Highcharts.MapNavigationButtonOptions, 'text'>; 78 | export function Button(props: MapButtonProps): ReactElement; 79 | } 80 | 81 | // Axis components 82 | type MapAxisProps<TAxisOptions> = { 83 | endOnTick?: boolean; 84 | visible?: boolean; 85 | minPadding?: number; 86 | maxPadding?: number; 87 | startOnTick?: boolean; 88 | reversed?: boolean; 89 | } & AxisProps<TAxisOptions>; 90 | 91 | export function XAxis( 92 | props: MapAxisProps<Highcharts.XAxisOptions> 93 | ): ReactElement; 94 | export function YAxis( 95 | props: MapAxisProps<Highcharts.YAxisOptions> 96 | ): ReactElement; 97 | -------------------------------------------------------------------------------- /packages/react-jsx-highmaps/webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | const path = require('path'); 3 | 4 | const isProd = process.env.NODE_ENV === 'production'; 5 | 6 | const webpackConfig = { 7 | mode: 'development', 8 | 9 | entry: path.resolve(__dirname, 'src'), 10 | 11 | output: { 12 | filename: isProd ? 'react-jsx-highmaps.min.js' : 'react-jsx-highmaps.js', 13 | path: path.resolve(__dirname, 'dist'), 14 | library: 'ReactHighcharts', 15 | libraryTarget: 'umd', 16 | // Prevents webpack from referencing `window` in the UMD build 17 | // Source: https://git.io/vppgU 18 | globalObject: "typeof self !== 'undefined' ? self : this" 19 | }, 20 | 21 | externals: { 22 | react: { 23 | commonjs: 'react', 24 | commonjs2: 'react', 25 | amd: 'react', 26 | root: 'React' 27 | }, 28 | 'react-dom': { 29 | commonjs: 'react-dom', 30 | commonjs2: 'react-dom', 31 | amd: 'react-dom', 32 | root: 'ReactDOM' 33 | }, 34 | highcharts: { 35 | commonjs: 'highcharts', 36 | commonjs2: 'highcharts', 37 | amd: 'highcharts', 38 | root: 'Highcharts' 39 | } 40 | }, 41 | 42 | module: { 43 | rules: [ 44 | { 45 | test: /\.js$/, 46 | loader: 'babel-loader', 47 | exclude: /node_modules(\\|\/)(?!react-jsx-highcharts)/ 48 | } 49 | ] 50 | } 51 | }; 52 | 53 | if (isProd) { 54 | webpackConfig.mode = 'production'; 55 | } 56 | 57 | module.exports = webpackConfig; 58 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/.babelrc: -------------------------------------------------------------------------------- 1 | { "extends": "../../babel.config.js" } 2 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/jest.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | // For a detailed explanation regarding each configuration property, visit: 3 | // https://jestjs.io/docs/en/configuration.html 4 | 5 | /** @type {import('@jest/types').Config.InitialOptions} */ 6 | const config = { 7 | testEnvironment: 'jsdom', 8 | setupFilesAfterEnv: ['<rootDir>/test/test-helper.js'], 9 | testMatch: ['**/test/**/*.spec.js?(x)'] 10 | }; 11 | 12 | module.exports = config; 13 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-jsx-highstock", 3 | "version": "5.0.0", 4 | "description": "Highcharts (including Highstock) charts built using React components", 5 | "main": "dist/react-jsx-highstock.min.js", 6 | "module": "dist/es/index.js", 7 | "types": "types/index.d.ts", 8 | "sideEffects": false, 9 | "files": [ 10 | "dist", 11 | "src", 12 | "types" 13 | ], 14 | "scripts": { 15 | "build": "cross-env NODE_ENV=development webpack", 16 | "build:prod": "npm run build:umd && npm run build:es", 17 | "build:umd": "cross-env NODE_ENV=production webpack", 18 | "build:es": "cross-env BABEL_ENV=es babel src --out-dir dist/es", 19 | "clean": "rimraf dist", 20 | "format": "prettier --write \"src/**/*.js\" \"test/**/*.js\" README.md", 21 | "lint": "eslint src", 22 | "test": "jest", 23 | "test:coverage": "jest --coverage", 24 | "test:types": "tsc --noEmit" 25 | }, 26 | "author": "Will Hawker", 27 | "contributors": [ 28 | { 29 | "name": "Alex Mayants", 30 | "url": "https://github.com/AlexMayants" 31 | }, 32 | { 33 | "name": "mrawdon", 34 | "url": "https://github.com/mrawdon" 35 | }, 36 | { 37 | "name": "Ercan Akyürek", 38 | "url": "https://github.com/geforcefan" 39 | }, 40 | { 41 | "name": "anajavi", 42 | "url": "https://github.com/anajavi" 43 | } 44 | ], 45 | "license": "MIT", 46 | "repository": { 47 | "type": "git", 48 | "url": "https://github.com/whawker/react-jsx-highcharts.git" 49 | }, 50 | "bugs": "https://github.com/whawker/react-jsx-highcharts/issues", 51 | "homepage": "https://github.com/whawker/react-jsx-highcharts", 52 | "keywords": [ 53 | "react", 54 | "reactjs", 55 | "react-component", 56 | "highcharts", 57 | "highstock", 58 | "chart", 59 | "charts", 60 | "graphs", 61 | "visualization", 62 | "data" 63 | ], 64 | "dependencies": { 65 | "react-jsx-highcharts": "5.0.0" 66 | }, 67 | "peerDependencies": { 68 | "highcharts": "^9.1.2 || ^10.0.0", 69 | "react": "^17.0.0 || ^18.0.0", 70 | "react-dom": "^17.0.0 || ^18.0.0" 71 | }, 72 | "browserslist": [ 73 | "chrome >= 81", 74 | "edge >= 81", 75 | "firefox >= 78", 76 | "ios >= 12", 77 | "safari >= 12" 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/src/components/HighchartsStockChart/HighchartsStockChart.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { BaseChart, useHighcharts } from 'react-jsx-highcharts'; 3 | 4 | const HighchartsStockChart = props => { 5 | const Highcharts = useHighcharts(); 6 | 7 | return ( 8 | <BaseChart 9 | {...props} 10 | chartCreationFunc={Highcharts.stockChart} 11 | chartType="stockChart" 12 | /> 13 | ); 14 | }; 15 | 16 | export default HighchartsStockChart; 17 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/src/components/HighchartsStockChart/index.js: -------------------------------------------------------------------------------- 1 | import HighchartsStockChart from './HighchartsStockChart'; 2 | export default HighchartsStockChart; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/src/components/Navigator/Navigator.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useState, useEffect } from 'react'; 3 | import { 4 | useModifiedProps, 5 | useChart, 6 | useHighcharts 7 | } from 'react-jsx-highcharts'; 8 | import NavigatorXAxis from './NavigatorXAxis'; 9 | 10 | const Navigator = ({ enabled = true, ...restProps }) => { 11 | const props = { enabled, ...restProps }; 12 | const [rendered, setRendered] = useState(false); 13 | const chart = useChart(); 14 | const Highcharts = useHighcharts(); 15 | 16 | useEffect(() => { 17 | const { children, ...rest } = props; 18 | // Workaround from http://jsfiddle.net/x40me94t/2/ 19 | const chartObj = chart.object; 20 | chartObj.options.navigator.enabled = true; 21 | // Initialise Navigator https://github.com/highcharts/highcharts/blob/dd730ab/js/parts/Navigator.js#L1837-L1844 22 | Highcharts.fireEvent(chartObj, 'beforeRender'); 23 | 24 | updateNavigator(rest, chart); 25 | 26 | setRendered(true); 27 | 28 | return () => { 29 | try { 30 | updateNavigator({ enabled: false }, chart); 31 | } catch { 32 | // ignore as chart might have been already unmounted 33 | } 34 | }; 35 | }, []); 36 | 37 | const modifiedProps = useModifiedProps(props); 38 | 39 | useEffect(() => { 40 | if (modifiedProps !== false) { 41 | updateNavigator(modifiedProps, chart); 42 | } 43 | }); 44 | 45 | const { children } = props; 46 | if (!children || !rendered) return null; 47 | 48 | return <NavigatorXAxis>{children}</NavigatorXAxis>; 49 | }; 50 | 51 | const updateNavigator = (config, chart) => { 52 | chart.update({ navigator: config }, true); 53 | }; 54 | 55 | export default Navigator; 56 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/src/components/Navigator/NavigatorAxis.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { 3 | useRef, 4 | useEffect, 5 | Children, 6 | cloneElement, 7 | isValidElement 8 | } from 'react'; 9 | import { 10 | useAxis, 11 | useModifiedProps, 12 | getNonEventHandlerProps 13 | } from 'react-jsx-highcharts'; 14 | 15 | const NavigatorAxis = ({ children, axisId, ...restProps }) => { 16 | const axis = useAxis(axisId); 17 | const renderedRef = useRef(false); 18 | 19 | useEffect(() => { 20 | if (!axis) return; 21 | 22 | updateNavigatorAxis(getNonEventHandlerProps(restProps), axis); 23 | }, [axis]); 24 | 25 | const modifiedProps = useModifiedProps(restProps); 26 | 27 | useEffect(() => { 28 | if (!renderedRef.current) { 29 | // don't update on first render 30 | renderedRef.current = true; 31 | return; 32 | } 33 | 34 | if (!axis) return; 35 | 36 | if (modifiedProps !== false) { 37 | updateNavigatorAxis(modifiedProps, axis); 38 | } 39 | }); 40 | 41 | if (!children) return null; 42 | 43 | const axisChildren = Children.map(children, child => { 44 | if (isValidElement(child) === false) return child; 45 | return cloneElement(child, { axisId }); 46 | }); 47 | 48 | return <>{axisChildren}</>; 49 | }; 50 | const updateNavigatorAxis = (config, axis) => { 51 | axis.update(config); 52 | }; 53 | 54 | export default NavigatorAxis; 55 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/src/components/Navigator/NavigatorSeries.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { useSeries } from 'react-jsx-highcharts'; 3 | 4 | const NavigatorSeries = props => { 5 | const series = useSeries(props.seriesId); 6 | 7 | useEffect(() => { 8 | if (!series) return; 9 | 10 | updateNavigatorSeries(series, { showInNavigator: true }); 11 | return () => { 12 | try { 13 | updateNavigatorSeries(series, { showInNavigator: false }); 14 | } catch { 15 | // ignore as series might have been already unmounted 16 | } 17 | }; 18 | }, [series]); 19 | 20 | return null; 21 | }; 22 | 23 | const updateNavigatorSeries = (series, config) => { 24 | series.update(config); 25 | }; 26 | 27 | export default NavigatorSeries; 28 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/src/components/Navigator/NavigatorXAxis.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import NavigatorAxis from './NavigatorAxis'; 3 | 4 | const NavigatorXAxis = props => ( 5 | <NavigatorAxis {...props} axisId="navigator-x-axis" /> 6 | ); 7 | 8 | export default NavigatorXAxis; 9 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/src/components/Navigator/NavigatorYAxis.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import NavigatorAxis from './NavigatorAxis'; 3 | 4 | const NavigatorYAxis = props => ( 5 | <NavigatorAxis {...props} axisId="navigator-y-axis" /> 6 | ); 7 | 8 | export default NavigatorYAxis; 9 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/src/components/Navigator/index.js: -------------------------------------------------------------------------------- 1 | import Navigator from './Navigator'; 2 | import NavigatorSeries from './NavigatorSeries'; 3 | import NavigatorXAxis from './NavigatorXAxis'; 4 | import NavigatorYAxis from './NavigatorYAxis'; 5 | const ChartNavigator = Navigator; 6 | ChartNavigator.Series = NavigatorSeries; 7 | ChartNavigator.XAxis = NavigatorXAxis; 8 | ChartNavigator.YAxis = NavigatorYAxis; 9 | export default ChartNavigator; 10 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/src/components/RangeSelector/RangeSelector.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useState, useEffect } from 'react'; 3 | import { 4 | useHighcharts, 5 | useAxis, 6 | useChart, 7 | useModifiedProps 8 | } from 'react-jsx-highcharts'; 9 | 10 | const RangeSelector = ({ enabled = true, children, ...restProps }) => { 11 | const props = { enabled, ...restProps }; 12 | const [rendered, setRendered] = useState(false); 13 | const Highcharts = useHighcharts(); 14 | const chart = useChart(); 15 | const axis = useAxis('xAxis'); 16 | 17 | useEffect(() => { 18 | if (!axis) return; 19 | 20 | // Workaround inferred from http://jsfiddle.net/x40me94t/2/ 21 | const chartObj = chart.object; 22 | chartObj.options.rangeSelector.enabled = true; 23 | // Initialise RangeSelector (see https://github.com/highcharts/highcharts/blob/dd730ab/js/parts/RangeSelector.js#L1464-L1468) 24 | Highcharts.fireEvent(chartObj, 'afterGetContainer'); 25 | 26 | const opts = getRangeSelectorConfig(props, Highcharts); 27 | updateRangeSelector(opts, chart); 28 | 29 | const renderRangeSelector = createRenderRangeSelector(chart, axis); 30 | const axisObj = axis.object; 31 | Highcharts.addEvent(axisObj, 'afterSetExtremes', renderRangeSelector); 32 | 33 | setRendered(true); 34 | 35 | return () => { 36 | const axisObj = axis.object; 37 | Highcharts.removeEvent(axisObj, 'afterSetExtremes', renderRangeSelector); 38 | try { 39 | updateRangeSelector({ enabled: false }, chart); 40 | } catch { 41 | // ignore as chart might have been already unmounted 42 | } 43 | }; 44 | }, [axis]); 45 | 46 | const modifiedProps = useModifiedProps(props); 47 | 48 | useEffect(() => { 49 | if (!axis || !rendered) return; 50 | 51 | if (modifiedProps !== false) { 52 | updateRangeSelector(modifiedProps, chart); 53 | } 54 | }); 55 | 56 | if (!children || !rendered) return null; 57 | 58 | return <>{children}</>; 59 | }; 60 | 61 | const getRangeSelectorConfig = (props, Highcharts) => { 62 | return { 63 | ...(Highcharts.defaultOptions && Highcharts.defaultOptions.rangeSelector), 64 | ...props, 65 | inputEnabled: false, 66 | buttons: [] 67 | }; 68 | }; 69 | 70 | const updateRangeSelector = (config, chart) => { 71 | chart.update({ rangeSelector: config }, true); 72 | }; 73 | 74 | const createRenderRangeSelector = (chart, axis) => { 75 | return () => { 76 | const chartObj = chart.object; 77 | const extremes = axis.getExtremes(); 78 | // Fixes #40 79 | chartObj.rangeSelector.render.call( 80 | chartObj.rangeSelector, 81 | extremes.min, 82 | extremes.max 83 | ); 84 | }; 85 | }; 86 | 87 | export default RangeSelector; 88 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/src/components/RangeSelector/RangeSelectorButton.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { getEventsConfig, useChart } from 'react-jsx-highcharts'; 3 | 4 | const RangeSelectorButton = ({ 5 | count = 1, 6 | offsetMin = 0, 7 | offsetMax = 0, 8 | ...restProps 9 | }) => { 10 | const props = { count, offsetMin, offsetMax, ...restProps }; 11 | 12 | const chart = useChart(); 13 | 14 | useEffect(() => { 15 | const button = getButtonIndex(props, chart); 16 | if (button > -1) return; // Button already present 17 | 18 | const { 19 | count, 20 | type, 21 | offsetMin, 22 | offsetMax, 23 | dataGrouping, 24 | children: text, 25 | ...rest 26 | } = props; 27 | const opts = { 28 | count, 29 | type, 30 | offsetMin, 31 | offsetMax, 32 | dataGrouping, 33 | text, 34 | events: getEventsConfig(rest) 35 | }; 36 | 37 | addButton(opts, chart); 38 | 39 | return () => { 40 | try { 41 | removeButton(props, chart); 42 | } catch { 43 | // ignore as chart might have been already unmounted 44 | } 45 | }; 46 | }, []); 47 | 48 | return null; 49 | }; 50 | 51 | const getButtons = chart => { 52 | const chartObj = chart.object; 53 | if (chartObj && chartObj.options) { 54 | const { buttons = [] } = chartObj.options.rangeSelector; 55 | return buttons; 56 | } 57 | 58 | return []; 59 | }; 60 | 61 | const getButtonIndex = (props, chart) => { 62 | const { count, type } = props; 63 | return getButtons(chart).findIndex(b => { 64 | return b.count === count && b.type === type; 65 | }); 66 | }; 67 | 68 | const addButton = (config, chart) => { 69 | // Add button to array 70 | const buttons = [...getButtons(chart), config]; 71 | updateRangeSelectorButtons(buttons, chart); 72 | }; 73 | 74 | const removeButton = (props, chart) => { 75 | const button = getButtonIndex(props); 76 | if (button === -1) return; 77 | 78 | // Remove button from array 79 | const buttons = [...getButtons()]; 80 | buttons.splice(button, 1); 81 | updateRangeSelectorButtons(buttons, chart); 82 | }; 83 | 84 | const updateRangeSelectorButtons = (config, chart) => { 85 | chart.update({ 86 | rangeSelector: { 87 | buttons: config 88 | } 89 | }); 90 | }; 91 | 92 | export default RangeSelectorButton; 93 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/src/components/RangeSelector/RangeSelectorInput.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { useModifiedProps, useChart } from 'react-jsx-highcharts'; 3 | 4 | const RangeSelectorInput = ({ enabled = true, ...restProps }) => { 5 | const chart = useChart(); 6 | 7 | useEffect(() => { 8 | return () => { 9 | try { 10 | updateRangeSelectorInputs({ enabled: false }, chart); 11 | } catch { 12 | // ignore as chart might have been already unmounted 13 | } 14 | }; 15 | }, []); 16 | 17 | const modifiedProps = useModifiedProps({ enabled, ...restProps }); 18 | 19 | useEffect(() => { 20 | if (modifiedProps !== false) { 21 | updateRangeSelectorInputs(modifiedProps, chart); 22 | } 23 | }); 24 | 25 | return null; 26 | }; 27 | 28 | const upperFirst = str => { 29 | return str.charAt(0).toUpperCase() + str.slice(1); 30 | }; 31 | 32 | const prefixPropsWithInput = config => { 33 | const prefixedConfig = {}; 34 | Object.keys(config).forEach(key => { 35 | const newKey = key.indexOf('input') === 0 ? key : `input${upperFirst(key)}`; 36 | prefixedConfig[newKey] = config[key]; 37 | }); 38 | 39 | return prefixedConfig; 40 | }; 41 | 42 | const updateRangeSelectorInputs = (config, chart) => { 43 | const inputProps = prefixPropsWithInput(config); 44 | 45 | chart.update({ 46 | rangeSelector: { 47 | ...inputProps 48 | } 49 | }); 50 | }; 51 | 52 | export default RangeSelectorInput; 53 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/src/components/RangeSelector/index.js: -------------------------------------------------------------------------------- 1 | import RangeSelector from './RangeSelector'; 2 | import RangeSelectorButton from './RangeSelectorButton'; 3 | import RangeSelectorInput from './RangeSelectorInput'; 4 | const ChartRangeSelector = RangeSelector; 5 | ChartRangeSelector.Button = RangeSelectorButton; 6 | ChartRangeSelector.Input = RangeSelectorInput; 7 | export default ChartRangeSelector; 8 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/src/components/Scrollbar/Scrollbar.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useEffect } from 'react'; 3 | import { useModifiedProps, useChart } from 'react-jsx-highcharts'; 4 | 5 | const Scrollbar = ({ children, enabled = true, ...restProps }) => { 6 | const chart = useChart(); 7 | 8 | useEffect(() => { 9 | return () => { 10 | try { 11 | updateScrollbar({ enabled: false }, chart); 12 | } catch { 13 | // ignore as chart might have been already unmounted 14 | } 15 | }; 16 | }, []); 17 | 18 | const modifiedProps = useModifiedProps({ enabled, ...restProps }); 19 | 20 | useEffect(() => { 21 | if (modifiedProps !== false) { 22 | updateScrollbar(modifiedProps, chart); 23 | } 24 | }); 25 | 26 | if (!children) return null; 27 | 28 | return <>{children}</>; 29 | }; 30 | 31 | const updateScrollbar = (config, chart) => { 32 | chart.update({ scrollbar: config }, true); 33 | }; 34 | 35 | export default Scrollbar; 36 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/src/components/Scrollbar/index.js: -------------------------------------------------------------------------------- 1 | import Scrollbar from './Scrollbar'; 2 | export default Scrollbar; 3 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/src/index.js: -------------------------------------------------------------------------------- 1 | import { withSeriesType } from 'react-jsx-highcharts'; 2 | export * from 'react-jsx-highcharts'; 3 | 4 | // Charts 5 | export { default as HighchartsStockChart } from './components/HighchartsStockChart'; 6 | 7 | // Graph parts 8 | export { default as Navigator } from './components/Navigator'; 9 | export { default as RangeSelector } from './components/RangeSelector'; 10 | export { default as Scrollbar } from './components/Scrollbar'; 11 | 12 | // Series 13 | export const CandlestickSeries = withSeriesType('Candlestick'); 14 | export const FlagsSeries = withSeriesType('Flags'); 15 | export const OHLCSeries = withSeriesType('OHLC'); 16 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "jest": true 5 | }, 6 | "rules": { 7 | "no-unused-vars": ["warn"], 8 | "react-perf/jsx-no-new-function-as-prop": "off", 9 | "react-perf/jsx-no-new-object-as-prop": "off", 10 | "react-perf/jsx-no-new-array-as-prop": "off", 11 | "react/display-name": "off" 12 | }, 13 | "globals": { 14 | "mount": true, 15 | "shallow": true, 16 | "render": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/test/components/HighchartsStockChart/HighchartsStockChart.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | jest.mock('react-jsx-highcharts', () => ({ 5 | ...jest.requireActual('react-jsx-highcharts'), 6 | useHighcharts: jest.fn() 7 | })); 8 | 9 | import { useHighcharts } from 'react-jsx-highcharts'; 10 | import { Highcharts, createMockChart } from '../../test-utils'; 11 | import HighchartsStockChart from '../../../src/components/HighchartsStockChart/HighchartsStockChart'; 12 | 13 | describe('<HighchartsStockChart />', () => { 14 | beforeEach(() => { 15 | const chart = createMockChart(); 16 | Highcharts.stockChart.mockReturnValue(chart); 17 | 18 | useHighcharts.mockImplementation(() => Highcharts); 19 | }); 20 | 21 | it('creates a chart', () => { 22 | render(<HighchartsStockChart />); 23 | 24 | expect(Highcharts.stockChart).toHaveBeenCalled(); 25 | }); 26 | 27 | it('creates a chart with the correct chart type', () => { 28 | render(<HighchartsStockChart />); 29 | 30 | expect(Highcharts.stockChart).toHaveBeenCalledWith( 31 | expect.anything(), 32 | expect.objectContaining({ 33 | chartType: 'stockChart' 34 | }) 35 | ); 36 | }); 37 | 38 | it('passes other props through to the chart', () => { 39 | render(<HighchartsStockChart plotOptions={{ c: 'd' }} />); 40 | 41 | expect(Highcharts.stockChart).toHaveBeenCalledWith( 42 | expect.anything(), 43 | expect.objectContaining({ plotOptions: { c: 'd' } }) 44 | ); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/test/components/Navigator/Navigator.integration.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import Highstock from 'highcharts/highstock'; 5 | import addAccessibility from 'highcharts/modules/accessibility'; 6 | 7 | import { 8 | Chart, 9 | Debug, 10 | XAxis, 11 | YAxis, 12 | LineSeries, 13 | HighchartsProvider 14 | } from 'react-jsx-highcharts'; 15 | import { HighchartsStockChart, Navigator } from '../../../src'; 16 | 17 | addAccessibility(Highstock); 18 | 19 | describe('<Navigator /> integration', () => { 20 | describe('when mounted', () => { 21 | it('creates navigator on chart', () => { 22 | const data = [1, 2, 3, 4, 5]; 23 | const Component = () => { 24 | return ( 25 | <HighchartsProvider Highcharts={Highstock}> 26 | <HighchartsStockChart> 27 | <Debug /> 28 | <Chart /> 29 | <XAxis /> 30 | <YAxis> 31 | <LineSeries data={data} /> 32 | </YAxis> 33 | <Navigator /> 34 | </HighchartsStockChart> 35 | </HighchartsProvider> 36 | ); 37 | }; 38 | render(<Component />); 39 | const chart = window.chart; 40 | 41 | expect(chart.options.navigator.enabled).toEqual(true); 42 | }); 43 | 44 | it('passed additional props to navigator on chart', () => { 45 | const data = [1, 2, 3, 4, 5]; 46 | const Component = () => { 47 | return ( 48 | <HighchartsProvider Highcharts={Highstock}> 49 | <HighchartsStockChart> 50 | <Debug /> 51 | <Chart /> 52 | <XAxis /> 53 | <YAxis> 54 | <LineSeries data={data} /> 55 | </YAxis> 56 | <Navigator height={100} /> 57 | </HighchartsStockChart> 58 | </HighchartsProvider> 59 | ); 60 | }; 61 | render(<Component />); 62 | const chart = window.chart; 63 | 64 | expect(chart.options.navigator.height).toEqual(100); 65 | }); 66 | }); 67 | 68 | describe('when updated', () => { 69 | it('changes height of the chart', () => { 70 | const data = [1, 2, 3, 4, 5]; 71 | const Component = props => { 72 | return ( 73 | <HighchartsProvider Highcharts={Highstock}> 74 | <HighchartsStockChart> 75 | <Debug /> 76 | <Chart /> 77 | <XAxis /> 78 | <YAxis> 79 | <LineSeries data={data} /> 80 | </YAxis> 81 | <Navigator {...props} /> 82 | </HighchartsStockChart> 83 | </HighchartsProvider> 84 | ); 85 | }; 86 | 87 | const wrapper = render(<Component />); 88 | wrapper.rerender(<Component height={110} />); 89 | const chart = window.chart; 90 | 91 | expect(chart.options.navigator.height).toEqual(110); 92 | }); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/test/components/Navigator/Navigator.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | jest.mock('react-jsx-highcharts', () => ({ 5 | ...jest.requireActual('react-jsx-highcharts'), 6 | useChart: jest.fn(), 7 | useHighcharts: jest.fn() 8 | })); 9 | 10 | import { useChart, useHighcharts } from 'react-jsx-highcharts'; 11 | import Navigator from '../../../src/components/Navigator/Navigator'; 12 | import { Highcharts, createMockProvidedChart } from '../../test-utils'; 13 | 14 | describe('<Navigator />', () => { 15 | let testContext; 16 | 17 | beforeEach(() => { 18 | testContext = {}; 19 | testContext.object = { 20 | options: { navigator: { enabled: false } } 21 | }; 22 | const { chartStubs } = createMockProvidedChart({ 23 | object: testContext.object 24 | }); 25 | testContext.chartStubs = chartStubs; 26 | 27 | useChart.mockImplementation(() => chartStubs); 28 | useHighcharts.mockImplementation(() => Highcharts); 29 | }); 30 | 31 | afterEach(() => { 32 | jest.resetAllMocks(); 33 | }); 34 | 35 | describe('when mounted', () => { 36 | it('enables the Navigator', () => { 37 | render(<Navigator />); 38 | expect(testContext.object.options.navigator.enabled).toEqual(true); 39 | }); 40 | 41 | it('fires the `beforeRender` event to so Highcharts creates a Navigator', () => { 42 | render(<Navigator />); 43 | expect(Highcharts.fireEvent).toHaveBeenCalledWith( 44 | testContext.object, 45 | 'beforeRender' 46 | ); 47 | }); 48 | 49 | it('updates the chart with the passed props', () => { 50 | render(<Navigator height={100} maskFill="rgba(1,2,3,0.45)" />); 51 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 52 | { 53 | navigator: { 54 | enabled: true, 55 | height: 100, 56 | maskFill: 'rgba(1,2,3,0.45)' 57 | } 58 | }, 59 | true 60 | ); 61 | }); 62 | }); 63 | 64 | describe('update', () => { 65 | it('should use the update method when props change', () => { 66 | const wrapper = render(<Navigator />); 67 | wrapper.rerender(<Navigator maskInside={false} />); 68 | 69 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 70 | { 71 | navigator: { 72 | maskInside: false 73 | } 74 | }, 75 | expect.any(Boolean) 76 | ); 77 | }); 78 | }); 79 | 80 | describe('when unmounted', () => { 81 | it('should disable the Navigator', () => { 82 | const wrapper = render(<Navigator />); 83 | wrapper.unmount(); 84 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 85 | { 86 | navigator: { 87 | enabled: false 88 | } 89 | }, 90 | expect.any(Boolean) 91 | ); 92 | }); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/test/components/Navigator/NavigatorXAxis.integration.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import Highstock from 'highcharts/highstock'; 5 | import addAccessibility from 'highcharts/modules/accessibility'; 6 | 7 | import { 8 | Chart, 9 | Debug, 10 | XAxis, 11 | YAxis, 12 | LineSeries, 13 | HighchartsProvider 14 | } from 'react-jsx-highcharts'; 15 | import { HighchartsStockChart, Navigator } from '../../../src'; 16 | 17 | addAccessibility(Highstock); 18 | 19 | describe('<NavigatorXAxis /> integration', () => { 20 | describe('when mounted', () => { 21 | it('passes additional props to navigators xAxis', () => { 22 | const data = [1, 2, 3, 4, 5]; 23 | const labels = { x: 0, y: 12 }; 24 | const Component = () => { 25 | return ( 26 | <HighchartsProvider Highcharts={Highstock}> 27 | <HighchartsStockChart> 28 | <Debug /> 29 | <Chart /> 30 | <XAxis /> 31 | <YAxis> 32 | <LineSeries data={data} /> 33 | </YAxis> 34 | <Navigator> 35 | <Navigator.XAxis labels={labels} /> 36 | </Navigator> 37 | </HighchartsStockChart> 38 | </HighchartsProvider> 39 | ); 40 | }; 41 | render(<Component />); 42 | const chart = window.chart; 43 | const navigatorxaxis = chart.get('navigator-x-axis'); 44 | 45 | expect(navigatorxaxis.options.labels).toEqual( 46 | expect.objectContaining(labels) 47 | ); 48 | }); 49 | }); 50 | 51 | describe('when updated', () => { 52 | it('changes the navigator labels', () => { 53 | const data = [1, 2, 3, 4, 5]; 54 | const labels = { x: 0, y: 12 }; 55 | const Component = props => { 56 | return ( 57 | <HighchartsProvider Highcharts={Highstock}> 58 | <HighchartsStockChart> 59 | <Debug /> 60 | <Chart /> 61 | <XAxis /> 62 | <YAxis> 63 | <LineSeries data={data} /> 64 | </YAxis> 65 | <Navigator> 66 | <Navigator.XAxis {...props} /> 67 | </Navigator> 68 | </HighchartsStockChart> 69 | </HighchartsProvider> 70 | ); 71 | }; 72 | 73 | const wrapper = render(<Component />); 74 | wrapper.rerender(<Component labels={labels} />); 75 | const chart = window.chart; 76 | const navigatorxaxis = chart.get('navigator-x-axis'); 77 | 78 | expect(navigatorxaxis.options.labels).toEqual( 79 | expect.objectContaining(labels) 80 | ); 81 | }); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/test/components/RangeSelector/RangeSelector.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import { 5 | Highcharts, 6 | createMockProvidedChart, 7 | createMockProvidedAxis 8 | } from '../../test-utils'; 9 | jest.mock('react-jsx-highcharts', () => ({ 10 | ...jest.requireActual('react-jsx-highcharts'), 11 | useChart: jest.fn(), 12 | useHighcharts: jest.fn(), 13 | useAxis: jest.fn() 14 | })); 15 | import { useAxis, useChart, useHighcharts } from 'react-jsx-highcharts'; 16 | 17 | import RangeSelector from '../../../src/components/RangeSelector/RangeSelector'; 18 | 19 | describe('<RangeSelector />', () => { 20 | let testContext; 21 | 22 | beforeEach(() => { 23 | testContext = {}; 24 | testContext.object = { 25 | options: { rangeSelector: { enabled: false } } 26 | }; 27 | const { chartStubs } = createMockProvidedChart({ 28 | object: testContext.object 29 | }); 30 | testContext.chartStubs = chartStubs; 31 | testContext.chartStubs.update.mockReset(); 32 | testContext.axisObject = {}; 33 | const { axisStubs } = createMockProvidedAxis({ 34 | object: testContext.axisObject 35 | }); 36 | testContext.axisStubs = axisStubs; 37 | 38 | useAxis.mockImplementation(id => { 39 | axisStubs.id = id; 40 | return axisStubs; 41 | }); 42 | 43 | useChart.mockImplementation(() => chartStubs); 44 | useHighcharts.mockImplementation(() => Highcharts); 45 | }); 46 | 47 | describe('when mounted', () => { 48 | it('enables the RangeSelector', () => { 49 | render(<RangeSelector />); 50 | 51 | expect(testContext.object.options.rangeSelector.enabled).toEqual(true); 52 | }); 53 | 54 | it('fires the initialization event to so Highcharts creates a RangeSelector', () => { 55 | render(<RangeSelector />); 56 | 57 | expect(Highcharts.fireEvent).toHaveBeenCalledWith( 58 | testContext.object, 59 | 'afterGetContainer' 60 | ); 61 | }); 62 | 63 | it('updates the chart with the passed props', () => { 64 | render(<RangeSelector height={100} buttonSpacing={2} />); 65 | 66 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 67 | { 68 | rangeSelector: { 69 | enabled: true, 70 | inputEnabled: false, 71 | height: 100, 72 | buttonSpacing: 2, 73 | buttons: [] 74 | } 75 | }, 76 | expect.any(Boolean) 77 | ); 78 | }); 79 | 80 | it('updates the chart once', () => { 81 | render(<RangeSelector />); 82 | 83 | expect(testContext.chartStubs.update).toHaveBeenCalledTimes(1); 84 | }); 85 | }); 86 | 87 | describe('update', () => { 88 | it('should use the update method when props change', () => { 89 | const wrapper = render(<RangeSelector selected={0} />); 90 | testContext.chartStubs.update.mockClear(); 91 | wrapper.rerender(<RangeSelector selected={2} />); 92 | 93 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 94 | { 95 | rangeSelector: { 96 | selected: 2 97 | } 98 | }, 99 | expect.any(Boolean) 100 | ); 101 | }); 102 | }); 103 | 104 | describe('when unmounted', () => { 105 | it('should disable the RangeSelector', () => { 106 | const wrapper = render(<RangeSelector />); 107 | testContext.chartStubs.update.mockClear(); 108 | wrapper.unmount(); 109 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 110 | { 111 | rangeSelector: { 112 | enabled: false 113 | } 114 | }, 115 | expect.any(Boolean) 116 | ); 117 | }); 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/test/components/RangeSelector/RangeSelectorInput.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import { 5 | Highcharts, 6 | createMockProvidedChart, 7 | createMockProvidedAxis 8 | } from '../../test-utils'; 9 | jest.mock('react-jsx-highcharts', () => ({ 10 | ...jest.requireActual('react-jsx-highcharts'), 11 | useChart: jest.fn(), 12 | useHighcharts: jest.fn(), 13 | useAxis: jest.fn() 14 | })); 15 | import { useAxis, useChart, useHighcharts } from 'react-jsx-highcharts'; 16 | 17 | import RangeSelector from '../../../src/components/RangeSelector/RangeSelector'; 18 | import RangeSelectorInput from '../../../src/components/RangeSelector/RangeSelectorInput'; 19 | 20 | describe('<RangeSelectorInput />', () => { 21 | let testContext; 22 | 23 | beforeEach(() => { 24 | testContext = {}; 25 | testContext.object = { 26 | options: { rangeSelector: { enabled: false } } 27 | }; 28 | const { chartStubs } = createMockProvidedChart({ 29 | object: testContext.object 30 | }); 31 | testContext.chartStubs = chartStubs; 32 | testContext.chartStubs.update.mockReset(); 33 | testContext.axisObject = {}; 34 | const { axisStubs } = createMockProvidedAxis({ 35 | object: testContext.axisObject 36 | }); 37 | testContext.axisStubs = axisStubs; 38 | 39 | useAxis.mockImplementation(id => { 40 | axisStubs.id = id; 41 | return axisStubs; 42 | }); 43 | 44 | useChart.mockImplementation(() => chartStubs); 45 | useHighcharts.mockImplementation(() => Highcharts); 46 | }); 47 | 48 | describe('when mounted', () => { 49 | it('sets the rangeselector input', () => { 50 | render( 51 | <RangeSelector> 52 | <RangeSelectorInput boxBorderColor="#ffffff" /> 53 | </RangeSelector> 54 | ); 55 | 56 | expect(testContext.chartStubs.update).toHaveBeenCalledWith({ 57 | rangeSelector: { 58 | inputBoxBorderColor: '#ffffff', 59 | inputEnabled: true 60 | } 61 | }); 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/test/components/Scrollbar/Scrollbar.spec.js: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | 4 | import { createMockProvidedChart } from '../../test-utils'; 5 | 6 | jest.mock('react-jsx-highcharts', () => ({ 7 | ...jest.requireActual('react-jsx-highcharts'), 8 | useChart: jest.fn() 9 | })); 10 | 11 | import { useChart } from 'react-jsx-highcharts'; 12 | import Scrollbar from '../../../src/components/Scrollbar/Scrollbar'; 13 | 14 | describe('<Scrollbar />', () => { 15 | let testContext; 16 | 17 | beforeEach(() => { 18 | testContext = {}; 19 | const { chartStubs } = createMockProvidedChart({ 20 | object: testContext.object 21 | }); 22 | testContext.chartStubs = chartStubs; 23 | useChart.mockImplementation(() => chartStubs); 24 | }); 25 | 26 | describe('when mounted', () => { 27 | it('add scrollbar using the Highcharts update method', () => { 28 | render(<Scrollbar />); 29 | 30 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 31 | { 32 | scrollbar: { 33 | enabled: true 34 | } 35 | }, 36 | true 37 | ); 38 | }); 39 | 40 | it('updates the scrollbar with the passed props', () => { 41 | render(<Scrollbar barBackgroundColor="red" height={20} />); 42 | 43 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 44 | { 45 | scrollbar: { 46 | enabled: true, 47 | barBackgroundColor: 'red', 48 | height: 20 49 | } 50 | }, 51 | true 52 | ); 53 | }); 54 | }); 55 | 56 | describe('update', () => { 57 | it('should use the update method when props change', () => { 58 | const wrapper = render(<Scrollbar />); 59 | testContext.chartStubs.update.mockClear(); 60 | wrapper.rerender(<Scrollbar height={12345} />); 61 | 62 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 63 | { 64 | scrollbar: { 65 | height: 12345 66 | } 67 | }, 68 | true 69 | ); 70 | }); 71 | }); 72 | 73 | describe('when unmounted', () => { 74 | it('should disable the Scrollbar', () => { 75 | const wrapper = render(<Scrollbar />); 76 | testContext.chartStubs.update.mockClear(); 77 | wrapper.unmount(); 78 | expect(testContext.chartStubs.update).toHaveBeenCalledWith( 79 | { 80 | scrollbar: { 81 | enabled: false 82 | } 83 | }, 84 | true 85 | ); 86 | }); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/test/test-helper.js: -------------------------------------------------------------------------------- 1 | const nodeCrypto = require('crypto'); 2 | window.crypto = { 3 | getRandomValues: function (buffer) { 4 | return nodeCrypto.randomFillSync(buffer); 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/test/test-utils.js: -------------------------------------------------------------------------------- 1 | export const Highcharts = { 2 | stockChart: jest.fn(), 3 | addEvent: jest.fn(), 4 | removeEvent: jest.fn(), 5 | fireEvent: jest.fn() 6 | }; 7 | 8 | export const createMockChart = ({ ...additional } = {}) => ({ 9 | ...additional, 10 | addAxis: jest.fn(), 11 | addColorAxis: jest.fn(), 12 | addSeries: jest.fn(), 13 | get: jest.fn(), 14 | setSize: jest.fn(), 15 | update: jest.fn(), 16 | setTitle: jest.fn(), 17 | destroy: jest.fn(), 18 | showLoading: jest.fn(), 19 | hideLoading: jest.fn(), 20 | addCredits: jest.fn(), 21 | redraw: jest.fn(), 22 | setCaption: jest.fn() 23 | }); 24 | 25 | export const createMockProvidedChart = ({ object, ...additional }) => { 26 | const chartStubs = createMockChart(additional); 27 | 28 | return { 29 | chartStubs: { ...chartStubs, object } 30 | }; 31 | }; 32 | 33 | export const createMockAxis = ({ ...additional } = {}) => ({ 34 | ...additional, 35 | remove: jest.fn(), 36 | addPlotBand: jest.fn(), 37 | removePlotBand: jest.fn(), 38 | addPlotLine: jest.fn(), 39 | removePlotLine: jest.fn(), 40 | getExtremes: jest.fn(), 41 | setExtremes: jest.fn(), 42 | update: jest.fn(), 43 | setTitle: jest.fn() 44 | }); 45 | 46 | export const createMockProvidedAxis = ({ object, ...additional }) => { 47 | const axisStubs = createMockAxis(additional); 48 | 49 | return { 50 | axisStubs, 51 | getAxis: () => ({ 52 | object, 53 | ...additional, 54 | ...axisStubs 55 | }) 56 | }; 57 | }; 58 | 59 | export const createMockSeries = ({ ...additional } = {}) => ({ 60 | ...additional, 61 | remove: jest.fn(), 62 | setData: jest.fn(), 63 | setVisible: jest.fn(), 64 | update: jest.fn() 65 | }); 66 | 67 | export const createMockProvidedSeries = ({ object, ...additional }) => { 68 | const seriesStubs = createMockSeries(additional); 69 | 70 | return { 71 | seriesStubs, 72 | getSeries: () => ({ 73 | object, 74 | ...additional, 75 | ...seriesStubs 76 | }) 77 | }; 78 | }; 79 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "noImplicitAny": true, 5 | "target": "ES2020" 6 | }, 7 | "include": ["./types/**/*"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import type * as Highcharts from 'highcharts'; 2 | import type { ReactElement, ReactNode } from 'react'; 3 | import { HighchartsChartProps, SeriesProps } from 'react-jsx-highcharts'; 4 | 5 | export * from 'react-jsx-highcharts'; 6 | 7 | export function HighchartsStockChart(props: HighchartsChartProps): ReactElement; 8 | 9 | // Navigator 10 | type NavigatorProps = { 11 | children?: ReactNode; 12 | } & Partial<Highcharts.NavigatorOptions>; 13 | export function Navigator(props: NavigatorProps): ReactElement; 14 | export namespace Navigator { 15 | type NavigatorSeriesProps = { 16 | seriesId: string 17 | } 18 | export function Series(props: NavigatorSeriesProps): ReactElement; 19 | 20 | type NavigatorXAxisProps = { 21 | children?: ReactNode 22 | } & Partial<Highcharts.NavigatorXAxisOptions>; 23 | export function XAxis(props: NavigatorXAxisProps): ReactElement; 24 | 25 | type NavigatorYAxisProps = { 26 | children?: ReactNode 27 | } & Partial<Highcharts.NavigatorYAxisOptions>; 28 | export function YAxis(props: NavigatorYAxisProps): ReactElement; 29 | } 30 | 31 | // RangeSelector 32 | type RangeSelectorProps = { 33 | children?: ReactNode; 34 | } & Partial<Highcharts.RangeSelectorOptions>; 35 | export function RangeSelector( 36 | props: RangeSelectorProps 37 | ): ReactElement; 38 | export namespace RangeSelector { 39 | type RangeSelectorButtonProps = { 40 | children?: ReactNode; 41 | } & Partial<Omit<Highcharts.RangeSelectorButtonsOptions, "text">>; 42 | export function Button(props: RangeSelectorButtonProps): ReactElement; 43 | 44 | type RangeSelectorInputProps = { 45 | boxBorderColor?: Highcharts.ColorString; 46 | boxHeight?: number; 47 | boxWidth?: (number|undefined); 48 | dateFormat?: string; 49 | dateParser?: Highcharts.RangeSelectorParseCallbackFunction; 50 | editDateFormat?: string; 51 | enabled?: boolean; 52 | position?: Highcharts.RangeSelectorInputPositionOptions; 53 | spacing?: number; 54 | style?: Highcharts.CSSObject; 55 | } 56 | export function Input(props: RangeSelectorInputProps): ReactElement; 57 | } 58 | 59 | // Scrollbar 60 | export function Scrollbar(props: Highcharts.ScrollbarOptions): ReactElement; 61 | 62 | // Series 63 | export function CandlestickSeries( 64 | props: SeriesProps<Highcharts.SeriesCandlestickOptions> 65 | ): ReactElement; 66 | export function FlagsSeries( 67 | props: SeriesProps<Highcharts.SeriesFlagsOptions> 68 | ): ReactElement; 69 | export function OHLCSeries( 70 | props: SeriesProps<Highcharts.SeriesOhlcOptions> 71 | ): ReactElement; 72 | -------------------------------------------------------------------------------- /packages/react-jsx-highstock/webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | const path = require('path'); 3 | 4 | const isProd = process.env.NODE_ENV === 'production'; 5 | 6 | const webpackConfig = { 7 | mode: 'development', 8 | 9 | entry: path.resolve(__dirname, 'src'), 10 | 11 | output: { 12 | filename: isProd ? 'react-jsx-highstock.min.js' : 'react-jsx-highstock.js', 13 | path: path.resolve(__dirname, 'dist'), 14 | library: 'ReactHighcharts', 15 | libraryTarget: 'umd', 16 | // Prevents webpack from referencing `window` in the UMD build 17 | // Source: https://git.io/vppgU 18 | globalObject: "typeof self !== 'undefined' ? self : this" 19 | }, 20 | 21 | externals: { 22 | react: { 23 | commonjs: 'react', 24 | commonjs2: 'react', 25 | amd: 'react', 26 | root: 'React' 27 | }, 28 | 'react-dom': { 29 | commonjs: 'react-dom', 30 | commonjs2: 'react-dom', 31 | amd: 'react-dom', 32 | root: 'ReactDOM' 33 | }, 34 | highcharts: { 35 | commonjs: 'highcharts', 36 | commonjs2: 'highcharts', 37 | amd: 'highcharts', 38 | root: 'Highcharts' 39 | } 40 | }, 41 | 42 | module: { 43 | rules: [ 44 | { 45 | test: /\.js$/, 46 | loader: 'babel-loader', 47 | exclude: /node_modules(\\|\/)(?!react-jsx-highcharts)/ 48 | } 49 | ] 50 | } 51 | }; 52 | 53 | if (isProd) { 54 | webpackConfig.mode = 'production'; 55 | } 56 | 57 | module.exports = webpackConfig; 58 | --------------------------------------------------------------------------------