├── .github ├── ISSUE_TEMPLATE │ ├── Bug_report.yml │ ├── Feature_Request.yml │ └── Issue_report.yml ├── pull_request_template.md └── workflows │ └── ci.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── README.md ├── commitlint.config.js ├── lerna.json ├── package.json ├── packages ├── ez-core │ ├── .gitignore │ ├── LICENSE.md │ ├── README.md │ ├── babel.config.js │ ├── jest.config.js │ ├── jsdoc.json │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── global.test.ts │ │ │ └── jest.setup.ts │ │ ├── animation │ │ │ ├── __tests__ │ │ │ │ └── HcAnimation.test.ts │ │ │ ├── index.ts │ │ │ └── types.ts │ │ ├── axis │ │ │ ├── README.md │ │ │ ├── __tests__ │ │ │ │ └── Axis.test.ts │ │ │ ├── index.ts │ │ │ └── types.ts │ │ ├── index.ts │ │ ├── sample-data.ts │ │ ├── scales │ │ │ ├── AbstractScale.ts │ │ │ ├── README.md │ │ │ ├── ScaleBand.ts │ │ │ ├── ScaleDiverging.ts │ │ │ ├── ScaleDivergingLogarithmic.ts │ │ │ ├── ScaleDivergingPower.ts │ │ │ ├── ScaleDivergingSqrt.ts │ │ │ ├── ScaleDivergingSymLog.ts │ │ │ ├── ScaleIdentity.ts │ │ │ ├── ScaleLinear.ts │ │ │ ├── ScaleLogarithmic.ts │ │ │ ├── ScaleOrdinal.ts │ │ │ ├── ScalePoint.ts │ │ │ ├── ScalePower.ts │ │ │ ├── ScaleQuantile.ts │ │ │ ├── ScaleQuantize.ts │ │ │ ├── ScaleRadial.ts │ │ │ ├── ScaleSequential.ts │ │ │ ├── ScaleSequentialLogarithmic.ts │ │ │ ├── ScaleSequentialPower.ts │ │ │ ├── ScaleSequentialSqrt.ts │ │ │ ├── ScaleSequentialSymLog.ts │ │ │ ├── ScaleSqrt.ts │ │ │ ├── ScaleSymLog.ts │ │ │ ├── ScaleThreshold.ts │ │ │ ├── ScaleTime.ts │ │ │ ├── ScaleUtc.ts │ │ │ ├── __tests__ │ │ │ │ ├── AbstractScale.test.ts │ │ │ │ ├── ScaleBand.test.ts │ │ │ │ ├── ScaleDiverging.test.ts │ │ │ │ ├── ScaleDivergingLogarithmic.test.ts │ │ │ │ ├── ScaleDivergingPower.test.ts │ │ │ │ ├── ScaleDivergingSymLog.test.ts │ │ │ │ ├── ScaleIdentity.test.ts │ │ │ │ ├── ScaleLinear.test.ts │ │ │ │ ├── ScaleLogarithmic.test.ts │ │ │ │ ├── ScaleOrdinal.test.ts │ │ │ │ ├── ScalePoint.test.ts │ │ │ │ ├── ScalePower.test.ts │ │ │ │ ├── ScaleQuantile.test.ts │ │ │ │ ├── ScaleQuantize.test.ts │ │ │ │ ├── ScaleRadial.test.ts │ │ │ │ ├── ScaleSequential.test.ts │ │ │ │ ├── ScaleSequentialLogarithmic.test.ts │ │ │ │ ├── ScaleSequentialPower.test.ts │ │ │ │ ├── ScaleSequentialSymLog.test.ts │ │ │ │ ├── ScaleSymLog.test.ts │ │ │ │ ├── ScaleThreshold.test.ts │ │ │ │ └── ScaleTime.test.ts │ │ │ ├── helpers │ │ │ │ ├── CategoricalScaleHelpers.ts │ │ │ │ ├── ContinuousScaleHelpers.ts │ │ │ │ ├── DivergingScaleHelpers.ts │ │ │ │ ├── SequentialScaleHelpers.ts │ │ │ │ ├── ThresholdScaleHelpers.ts │ │ │ │ ├── __tests__ │ │ │ │ │ ├── CategoricalScaleHelpers.test.ts │ │ │ │ │ ├── ContinuousScaleHelpers.test.ts │ │ │ │ │ ├── DivergingScalesHelpers.test.ts │ │ │ │ │ ├── SequentialScaleHelpers.test.ts │ │ │ │ │ └── ThresholdScalesHelpers.test.ts │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ └── types.ts │ │ ├── types.ts │ │ └── utils │ │ │ ├── __tests__ │ │ │ ├── __snapshots__ │ │ │ │ └── axis.test.ts.snap │ │ │ ├── axis.test.ts │ │ │ ├── clamp.test.ts │ │ │ ├── data.test.ts │ │ │ └── svg.test.ts │ │ │ ├── axis.ts │ │ │ ├── clamp.ts │ │ │ ├── data.ts │ │ │ ├── debounce.ts │ │ │ ├── defaults.ts │ │ │ ├── geojson.ts │ │ │ ├── grid.ts │ │ │ ├── index.ts │ │ │ ├── line.ts │ │ │ ├── logger.ts │ │ │ ├── map.ts │ │ │ ├── pie.ts │ │ │ ├── scale.ts │ │ │ ├── svg.ts │ │ │ └── types.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── yarn.lock ├── ez-css │ ├── LICENSE.md │ ├── README.md │ ├── css │ │ └── style.css │ ├── index.html │ ├── package.json │ ├── sass │ │ ├── addons │ │ │ ├── legend.scss │ │ │ └── tooltip.scss │ │ ├── chart.scss │ │ ├── main.scss │ │ ├── recipes │ │ │ ├── bar.scss │ │ │ └── column.scss │ │ ├── scales │ │ │ ├── axis.scss │ │ │ └── grid.scss │ │ └── shapes │ │ │ ├── bar.scss │ │ │ ├── line.scss │ │ │ └── point.scss │ └── yarn.lock ├── ez-dev │ ├── LICENSE.md │ ├── README.md │ ├── jest │ │ ├── base.config.js │ │ ├── data.ts │ │ ├── index.js │ │ ├── jest-html-serializer.js │ │ ├── shims-jest.d.ts │ │ ├── snapshotResolver.js │ │ └── snapshots │ │ │ ├── components │ │ │ ├── Arcs.spec.tsx.snap │ │ │ ├── Area.spec.tsx.snap │ │ │ ├── Bars.spec.tsx.snap │ │ │ ├── Bubbles.spec.tsx.snap │ │ │ ├── Chart.spec.tsx.snap │ │ │ ├── IrregularArcs.spec.tsx.snap │ │ │ ├── Map.spec.tsx.snap │ │ │ ├── Pie.spec.tsx.snap │ │ │ ├── Points.spec.tsx.snap │ │ │ ├── addons │ │ │ │ ├── Legend.spec.tsx.snap │ │ │ │ └── Tooltip.spec.tsx.snap │ │ │ ├── scales │ │ │ │ ├── Axis.spec.tsx.snap │ │ │ │ └── GridLines.spec.tsx.snap │ │ │ └── shapes │ │ │ │ ├── Arc.spec.tsx.snap │ │ │ │ ├── AreaPath.spec.tsx.snap │ │ │ │ ├── Bar.spec.tsx.snap │ │ │ │ ├── LinePath.spec.tsx.snap │ │ │ │ ├── MapPath.spec.tsx.snap │ │ │ │ └── Point.spec.tsx.snap │ │ │ └── recipes │ │ │ ├── area │ │ │ ├── AreaChart.spec.tsx.snap │ │ │ └── MultiAreaChart.spec.tsx.snap │ │ │ ├── bar │ │ │ └── BarChart.spec.tsx.snap │ │ │ ├── column │ │ │ ├── ColumnChart.spec.tsx.snap │ │ │ └── LineColumnChart.spec.tsx.snap │ │ │ ├── line │ │ │ ├── LineChart.spec.tsx.snap │ │ │ ├── LineErrorMarginChart.spec.tsx.snap │ │ │ └── MultiLineChart.spec.tsx.snap │ │ │ ├── map │ │ │ └── MapChart.spec.tsx.snap │ │ │ ├── pie │ │ │ ├── IrregularPieChart.spec.tsx.snap │ │ │ ├── PieChart.spec.tsx.snap │ │ │ ├── RadialChart.spec.tsx.snap │ │ │ └── SemiCircleChart.spec.tsx.snap │ │ │ └── scatter │ │ │ ├── BubbleChart.spec.tsx.snap │ │ │ └── ScatterChart.spec.tsx.snap │ ├── package.json │ ├── storybook │ │ ├── africa-map.geojson.json │ │ ├── data.ts │ │ ├── storybook-configs.ts │ │ └── utils.ts │ ├── tsconfig.json │ └── yarn.lock ├── ez-react │ ├── .eslintrc.js │ ├── .gitignore │ ├── .prettierrc │ ├── .storybook │ │ ├── docs-root.css │ │ ├── main.js │ │ ├── manager-head.html │ │ ├── manager.js │ │ ├── preview.js │ │ └── theme.js │ ├── LICENSE.md │ ├── README.md │ ├── babel.config.js │ ├── jest.config.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── components │ │ │ ├── Arcs.tsx │ │ │ ├── Area.tsx │ │ │ ├── Bars.tsx │ │ │ ├── Bubbles.tsx │ │ │ ├── Chart.tsx │ │ │ ├── IrregularArcs.tsx │ │ │ ├── Map.tsx │ │ │ ├── MapBubbles.tsx │ │ │ ├── Pie.tsx │ │ │ ├── Points.tsx │ │ │ ├── ResponsiveChartContainer.tsx │ │ │ ├── Segments.tsx │ │ │ ├── addons │ │ │ │ ├── legend │ │ │ │ │ ├── Legend.tsx │ │ │ │ │ ├── LegendItem.tsx │ │ │ │ │ └── LegendProvider.tsx │ │ │ │ └── tooltip │ │ │ │ │ ├── Tooltip.tsx │ │ │ │ │ ├── TooltipContext.ts │ │ │ │ │ ├── TooltipProvider.tsx │ │ │ │ │ └── use-tooltip.ts │ │ │ ├── scales │ │ │ │ ├── Axis.tsx │ │ │ │ ├── CartesianScale.tsx │ │ │ │ ├── ColorScale.tsx │ │ │ │ ├── LinearScale.tsx │ │ │ │ ├── SqrtScale.tsx │ │ │ │ └── grid │ │ │ │ │ ├── Grid.tsx │ │ │ │ │ └── GridLines.tsx │ │ │ └── shapes │ │ │ │ ├── Arc.tsx │ │ │ │ ├── AreaPath.tsx │ │ │ │ ├── Bar.tsx │ │ │ │ ├── Bubble.tsx │ │ │ │ ├── LinePath.tsx │ │ │ │ ├── MapPath.tsx │ │ │ │ └── Point.tsx │ │ ├── docs │ │ │ ├── 1_introdution.stories.mdx │ │ │ ├── 2_installation.stories.mdx │ │ │ ├── 3_yourfirstchart.stories.mdx │ │ │ ├── 4_1_customCSS.stories.mdx │ │ │ ├── 4_2_responsiveCharts.stories.mdx │ │ │ ├── 4_3_commonProps.stories.mdx │ │ │ ├── TabTitle.tsx │ │ │ ├── Tabs.tsx │ │ │ └── styles.css │ │ ├── index.ts │ │ ├── lib │ │ │ ├── Fragment.tsx │ │ │ ├── storybook-utils.tsx │ │ │ ├── use-animation.ts │ │ │ ├── use-chart.ts │ │ │ ├── use-map.ts │ │ │ ├── use-responsive-chart.ts │ │ │ ├── useToggableDatum.ts │ │ │ └── useToggableDomainKey.ts │ │ ├── recipes │ │ │ ├── area │ │ │ │ ├── AreaChart.stories.tsx │ │ │ │ ├── AreaChart.tsx │ │ │ │ ├── MultiAreaChart.stories.tsx │ │ │ │ └── MultiAreaChart.tsx │ │ │ ├── bar │ │ │ │ ├── BarChart.stories.tsx │ │ │ │ └── BarChart.tsx │ │ │ ├── column │ │ │ │ ├── ColumnChart.stories.tsx │ │ │ │ ├── ColumnChart.tsx │ │ │ │ ├── LineColumnChart.stories.tsx │ │ │ │ └── LineColumnChart.tsx │ │ │ ├── line │ │ │ │ ├── LineChart.stories.tsx │ │ │ │ ├── LineChart.tsx │ │ │ │ ├── LineErrorMarginChart.stories.tsx │ │ │ │ ├── LineErrorMarginChart.tsx │ │ │ │ ├── MultiLineChart.stories.tsx │ │ │ │ └── MultiLineChart.tsx │ │ │ ├── map │ │ │ │ ├── BubbleMapChart.stories.tsx │ │ │ │ ├── BubbleMapChart.tsx │ │ │ │ ├── MapChart.stories.tsx │ │ │ │ └── MapChart.tsx │ │ │ ├── pie │ │ │ │ ├── IrregularPieChart.tsx │ │ │ │ ├── PieChart.stories.tsx │ │ │ │ ├── PieChart.tsx │ │ │ │ ├── RadialChart.stories.tsx │ │ │ │ ├── RadialChart.tsx │ │ │ │ └── SemiCircleChart.tsx │ │ │ └── scatter │ │ │ │ ├── BubbleChart.stories.tsx │ │ │ │ ├── BubbleChart.tsx │ │ │ │ ├── ScatterChart.stories.tsx │ │ │ │ └── ScatterChart.tsx │ │ └── setupTests.ts │ ├── tests │ │ ├── common.ts │ │ ├── mocks │ │ │ └── ResizeObserver.ts │ │ └── unit │ │ │ ├── components │ │ │ ├── Arcs.spec.tsx │ │ │ ├── Area.spec.tsx │ │ │ ├── Bars.spec.tsx │ │ │ ├── Bubbles.spec.tsx │ │ │ ├── Chart.spec.tsx │ │ │ ├── IrregularArcs.spec.tsx │ │ │ ├── Map.spec.tsx │ │ │ ├── Pie.spec.tsx │ │ │ ├── Points.spec.tsx │ │ │ ├── addons │ │ │ │ ├── Legend.spec.tsx │ │ │ │ └── Tooltip.spec.tsx │ │ │ ├── scales │ │ │ │ ├── Axis.spec.tsx │ │ │ │ └── GridLines.spec.tsx │ │ │ └── shapes │ │ │ │ ├── Arc.spec.tsx │ │ │ │ ├── AreaPath.spec.tsx │ │ │ │ ├── Bar.spec.tsx │ │ │ │ ├── LinePath.spec.tsx │ │ │ │ ├── MapPath.spec.tsx │ │ │ │ └── Point.spec.tsx │ │ │ └── recipes │ │ │ ├── area │ │ │ ├── AreaChart.spec.tsx │ │ │ └── MultiAreaChart.spec.tsx │ │ │ ├── bar │ │ │ └── BarChart.spec.tsx │ │ │ ├── column │ │ │ ├── ColumnChart.spec.tsx │ │ │ └── LineColumnChart.spec.tsx │ │ │ ├── line │ │ │ ├── LineChart.spec.tsx │ │ │ ├── LineErrorMarginChart.spec.tsx │ │ │ └── MultiLineChart.spec.tsx │ │ │ ├── map │ │ │ └── MapChart.spec.tsx │ │ │ ├── pie │ │ │ ├── IrregularPieChart.spec.tsx │ │ │ ├── PieChart.spec.tsx │ │ │ ├── RadialChart.spec.tsx │ │ │ └── SemiCircleChart.spec.tsx │ │ │ └── scatter │ │ │ ├── BubbleChart.spec.tsx │ │ │ └── ScatterChart.spec.tsx │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── yarn.lock └── ez-vue │ ├── .browserslistrc │ ├── .editorconfig │ ├── .eslintrc.js │ ├── .gitignore │ ├── .storybook │ ├── main.js │ ├── manager-head.html │ ├── manager.js │ ├── preview.js │ └── theme.js │ ├── README.md │ ├── babel.config.js │ ├── index.ts │ ├── jest.config.js │ ├── package.json │ ├── src │ ├── components │ │ ├── Arcs.tsx │ │ ├── Area.tsx │ │ ├── Bars.tsx │ │ ├── Bubbles.tsx │ │ ├── Chart.tsx │ │ ├── IrregularArcs.tsx │ │ ├── Map.tsx │ │ ├── MapBubbles.tsx │ │ ├── Pie.tsx │ │ ├── Points.tsx │ │ ├── ResponsiveChartContainer.tsx │ │ ├── Segments.tsx │ │ ├── addons │ │ │ ├── legend │ │ │ │ ├── Legend.tsx │ │ │ │ ├── LegendItem.tsx │ │ │ │ └── LegendProvider.tsx │ │ │ └── tooltip │ │ │ │ ├── Tooltip.tsx │ │ │ │ └── TooltipProvider.tsx │ │ ├── scales │ │ │ ├── Axis.tsx │ │ │ ├── CartesianScale.tsx │ │ │ ├── ColorScale.tsx │ │ │ ├── LinearScale.tsx │ │ │ ├── SqrtScale.tsx │ │ │ └── grid │ │ │ │ ├── Grid.tsx │ │ │ │ └── GridLines.tsx │ │ └── shapes │ │ │ ├── Arc.tsx │ │ │ ├── AreaPath.tsx │ │ │ ├── Bar.tsx │ │ │ ├── Bubble.tsx │ │ │ ├── LinePath.tsx │ │ │ ├── MapPath.tsx │ │ │ └── Point.tsx │ ├── index.ts │ ├── lib │ │ ├── AnimationMixin.ts │ │ ├── Fragment.tsx │ │ ├── ToggleDatumMixin.ts │ │ ├── ToggleDomainKeyMixin.ts │ │ └── storybook-utils.ts │ ├── recipes │ │ ├── area │ │ │ ├── AreaChart.stories.tsx │ │ │ ├── AreaChart.tsx │ │ │ ├── MultiAreaChart.stories.tsx │ │ │ ├── MultiAreaChart.tsx │ │ │ └── index.ts │ │ ├── bar │ │ │ ├── BarChart.stories.tsx │ │ │ ├── BarChart.tsx │ │ │ └── index.ts │ │ ├── column │ │ │ ├── ColumnChart.stories.tsx │ │ │ ├── ColumnChart.tsx │ │ │ ├── LineColumnChart.stories.tsx │ │ │ ├── LineColumnChart.tsx │ │ │ └── index.ts │ │ ├── line │ │ │ ├── LineChart.stories.tsx │ │ │ ├── LineChart.tsx │ │ │ ├── LineErrorMarginChart.stories.tsx │ │ │ ├── LineErrorMarginChart.tsx │ │ │ ├── MultiLineChart.stories.tsx │ │ │ ├── MultiLineChart.tsx │ │ │ └── index.ts │ │ ├── map │ │ │ ├── BubbleMapChart.stories.tsx │ │ │ ├── BubbleMapChart.tsx │ │ │ ├── MapChart.stories.tsx │ │ │ └── MapChart.tsx │ │ ├── pie │ │ │ ├── IrregularPieChart.tsx │ │ │ ├── PieChart.stories.tsx │ │ │ ├── PieChart.tsx │ │ │ ├── RadialChart.stories.tsx │ │ │ ├── RadialChart.tsx │ │ │ ├── SemiCircleChart.tsx │ │ │ └── index.ts │ │ └── scatter │ │ │ ├── BubbleChart.stories.tsx │ │ │ ├── BubbleChart.tsx │ │ │ ├── ScatterChart.stories.tsx │ │ │ ├── ScatterChart.tsx │ │ │ └── index.ts │ ├── resize-observer.d.ts │ ├── shims-tsx.d.ts │ └── shims-vue.d.ts │ ├── tests │ ├── mocks │ │ └── ResizeObserver.tsx │ └── unit │ │ ├── components │ │ ├── Arcs.spec.tsx │ │ ├── Area.spec.tsx │ │ ├── Bars.spec.tsx │ │ ├── Bubbles.spec.tsx │ │ ├── Chart.spec.tsx │ │ ├── IrregularArcs.spec.tsx │ │ ├── Map.spec.tsx │ │ ├── Pie.spec.tsx │ │ ├── Points.spec.tsx │ │ ├── addons │ │ │ ├── Legend.spec.tsx │ │ │ └── Tooltip.spec.tsx │ │ ├── scales │ │ │ ├── Axis.spec.tsx │ │ │ └── GridLines.spec.tsx │ │ └── shapes │ │ │ ├── Arc.spec.tsx │ │ │ ├── AreaPath.spec.tsx │ │ │ ├── Bar.spec.tsx │ │ │ ├── LinePath.spec.tsx │ │ │ ├── MapPath.spec.tsx │ │ │ └── Point.spec.tsx │ │ └── recipes │ │ ├── area │ │ ├── AreaChart.spec.tsx │ │ └── MultiAreaChart.spec.tsx │ │ ├── bar │ │ └── BarChart.spec.tsx │ │ ├── column │ │ ├── ColumnChart.spec.tsx │ │ └── LineColumnChart.spec.tsx │ │ ├── line │ │ ├── LineChart.spec.tsx │ │ ├── LineErrorMarginChart.spec.tsx │ │ └── MultiLineChart.spec.tsx │ │ ├── map │ │ └── MapChart.spec.tsx │ │ ├── pie │ │ ├── IrregularPieChart.spec.tsx │ │ ├── PieChart.spec.tsx │ │ ├── RadialChart.spec.tsx │ │ └── SemiCircleChart.spec.tsx │ │ └── scatter │ │ ├── BubbleChart.spec.tsx │ │ └── ScatterChart.spec.tsx │ ├── tsconfig.json │ ├── vue.config.js │ └── yarn.lock ├── tsconfig.json └── yarn.lock /.github/ISSUE_TEMPLATE/Issue_report.yml: -------------------------------------------------------------------------------- 1 | name: "🤔 Issue Report" 2 | description: Create a new ticket for you Issue. 3 | title: "🤔 [ISSUE] - eazychart-[css | core | react | vue]- " 4 | labels: [ 5 | "help-needed" 6 | ] 7 | body: 8 | - type: textarea 9 | id: description 10 | attributes: 11 | label: "Description" 12 | description: Please enter an explicit description of your issue 13 | placeholder: Short and explicit description of your incident... 14 | validations: 15 | required: true 16 | - type: textarea 17 | id: reprod 18 | attributes: 19 | label: "Reproduction steps" 20 | description: Please enter an explicit description of your issue 21 | value: | 22 | 1. Go to '...' 23 | 2. Click on '....' 24 | 3. Scroll down to '....' 25 | 4. See error 26 | render: bash 27 | validations: 28 | required: true 29 | - type: textarea 30 | id: logs 31 | attributes: 32 | label: "Logs" 33 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 34 | render: bash 35 | validations: 36 | required: false 37 | - type: dropdown 38 | id: browsers 39 | attributes: 40 | label: "Browsers" 41 | description: What browsers are you seeing the problem on ? 42 | multiple: true 43 | options: 44 | - Firefox 45 | - Chrome 46 | - Safari 47 | - Microsoft Edge 48 | - Opera 49 | validations: 50 | required: false 51 | - type: dropdown 52 | id: os 53 | attributes: 54 | label: "OS" 55 | description: What is the impacted environment ? 56 | multiple: true 57 | options: 58 | - Windows 59 | - Linux 60 | - Mac 61 | validations: 62 | required: false -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Motivation 2 | 3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. 4 | 5 | Fixes # (issue) 6 | 7 | ## Type of change 8 | 9 | Please delete options that are not relevant. 10 | 11 | - [ ] Bug fix (non-breaking change which fixes an issue) 12 | - [ ] New feature (non-breaking change which adds functionality) 13 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 14 | - [ ] This change requires a documentation update 15 | 16 | # Checklist: 17 | 18 | - [ ] I have done the work for both react and vue 19 | - [ ] I have performed a self-review of my own code 20 | - [ ] I have commented my code, particularly in hard-to-understand areas 21 | - [ ] I have made corresponding changes to the documentation (Storybook) 22 | - [ ] I have added unit tests that prove my fix is effective or that my feature works 23 | - [ ] New and existing unit tests pass locally with my changes 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .vscode 4 | *.tgz 5 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | # yarn test 5 | lerna run --concurrency 1 --stream precommit --since HEAD --exclude-dependents 6 | 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. 6 | 7 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 8 | 9 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. 10 | 11 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 12 | 13 | This Code of Conduct is adapted from the [Contributor Covenant](https://contributor-covenant.org), version 1.0.0, available at [https://contributor-covenant.org/version/1/0/0/](https://contributor-covenant.org/version/1/0/0/). 14 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Hexastack 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 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "0.12.0-alpha.0", 6 | "npmClient": "yarn", 7 | "useWorkspaces": false 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eazychart-monorepo", 3 | "version": "0.0.1", 4 | "description": "EazyChart is an easy to use reactive library that allows you to add customizable charts into your React or Vue web applications.", 5 | "keywords": ["chart", "library", "svg", "react", "dataviz", "graph", "typescript", "javascript", "data", "visualization", "web"], 6 | "author": "Hexastack", 7 | "license": "MIT", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/Hexastack/eazychart.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/Hexastack/eazychart/issues" 14 | }, 15 | "homepage": "https://eazychart.com", 16 | "private": true, 17 | "devDependencies": { 18 | "react-syntax-highlighter": "^15.5.0", 19 | "@commitlint/cli": "^16.0.1", 20 | "@commitlint/config-conventional": "^16.0.0", 21 | "husky": "^7.0.4", 22 | "lerna": "^4.0.0" 23 | }, 24 | "engines": { 25 | "node": "=16" 26 | }, 27 | "scripts": { 28 | "prepare": "husky install", 29 | "clean": "lerna clean", 30 | "bootstrap": "lerna bootstrap", 31 | "setup": "lerna clean -y && rm -rf node_modules/ && yarn && lerna bootstrap", 32 | "storybook:react": "lerna run storybook --scope=eazychart-react --stream", 33 | "storybook:vue": "lerna run storybook --scope=eazychart-vue --stream", 34 | "storybook": "lerna run storybook --scope=eazychart-vue --scope=eazychart-react --stream", 35 | "test:core": "lerna run test --scope=eazychart-core --stream", 36 | "test:vue": "lerna run test --scope=eazychart-vue --stream", 37 | "test:react": "lerna run test --scope=eazychart-react --stream", 38 | "test": "lerna run test --stream", 39 | "build": "lerna run build --stream" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/ez-core/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | dist 5 | coverage/ 6 | docs/ -------------------------------------------------------------------------------- /packages/ez-core/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Hexastack 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 | -------------------------------------------------------------------------------- /packages/ez-core/README.md: -------------------------------------------------------------------------------- 1 | # EazyChart 2 | <img align="center" width="500" alt="EazyChart logo" src="https://eazychart.com/img/logo.png"/> 3 | <br/> 4 | <a href="https://eazychart.com/">EazyChart</a> is a reactive chart library, it offers the ability to easily add charts in your React and Vue web applications. EazyChart does not depend on a JS chart library instead it depends only on some of the <a href="https://d3js.org/">D3.js</a> library's submodules. 5 | 6 | <br /> 7 | <a href="https://docs.eazychart.com/">Website</a> 8 | <span>  •  </span> 9 | <a href="https://docs.eazychart.com/?path=/story/get-started-introduction--page">Get Started</a> 10 | <span>  •  </span> 11 | <a href="https://docs.eazychart.com/?path=/story/get-started-installation--page">Installation</a> 12 | <br /> 13 | <hr /> 14 | 15 | This package is a utility lib that is used by both `eazychart-react` and `eazychart-vue` packages. It includes mainly : 16 | - Typescript declaration file 17 | - Scale classes 18 | - Helper functions 19 | 20 | Please refer to the main repo [README](../../README.md) to learn more about this package. 21 | -------------------------------------------------------------------------------- /packages/ez-core/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | test: { 4 | plugins: ['@babel/plugin-transform-modules-commonjs'], 5 | }, 6 | }, 7 | }; -------------------------------------------------------------------------------- /packages/ez-core/jest.config.js: -------------------------------------------------------------------------------- 1 | const esModules = [ 2 | 'd3-scale', 3 | 'd3-array', 4 | 'internmap', 5 | 'd3-interpolate', 6 | 'd3-color', 7 | 'd3-format', 8 | 'd3-time', 9 | 'd3-shape', 10 | 'd3-path', 11 | 'd3-ease', 12 | 'd3-geo', 13 | ].join('|'); 14 | 15 | module.exports = { 16 | transformIgnorePatterns: [`/node_modules/(?!${esModules})`], 17 | setupFilesAfterEnv: ['<rootDir>/src/__tests__/jest.setup.ts'], 18 | }; 19 | -------------------------------------------------------------------------------- /packages/ez-core/jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true 4 | }, 5 | "opts": { 6 | "template": "node_modules/better-docs", 7 | "encoding": "utf8", 8 | "destination": "./docs/", 9 | "recurse": true 10 | }, 11 | "plugins": [ 12 | "node_modules/better-docs/typescript" 13 | ], 14 | "source": { 15 | "include": [ 16 | "src/" 17 | ], 18 | "includePattern": "\\.(jsx|js|ts|tsx)$" 19 | } 20 | } -------------------------------------------------------------------------------- /packages/ez-core/src/animation/types.ts: -------------------------------------------------------------------------------- 1 | import * as easings from 'd3-ease'; 2 | 3 | export type AnimationEasingFn = Exclude< 4 | typeof easings, 5 | 'PolynomialEasingFactory' | 'BackEasingFactory' | 'ElasticEasingFactory' 6 | >; 7 | export type AnimationEasing = keyof AnimationEasingFn; 8 | 9 | export type CustomEasingFn = (normalizedTime: number) => number; 10 | 11 | export type Interpolables = number | string | Date | Array<any> | object; 12 | 13 | export type InterpolationFunction<T> = (t: number) => T; 14 | 15 | export type Interpolation<T extends Interpolables> = ( 16 | a: T, 17 | b: T 18 | ) => InterpolationFunction<T>; 19 | 20 | export type AnyAnimationEasingFn = 21 | | AnimationEasingFn[AnimationEasing] 22 | | CustomEasingFn; 23 | 24 | export type AnimationOptions = { 25 | duration: number; // milliseconds 26 | delay: number; // milliseconds 27 | easing: CustomEasingFn | AnimationEasing; 28 | }; 29 | -------------------------------------------------------------------------------- /packages/ez-core/src/axis/README.md: -------------------------------------------------------------------------------- 1 | # Axis 2 | Axis is meant to translate [d3-axis](https://github.com/d3/d3-axis) into a configurable class, and instead of instantly rendering to d3-selection. It just returns an object containing details about how the axis can be rendered. 3 | 4 | 5 | ## Perspectives (enhancements) 6 | Since the axis always returns an object when it's method `axis` is called, we can just add that returned object as a member to the class and have be computed each time the class mutate. 7 | -------------------------------------------------------------------------------- /packages/ez-core/src/axis/types.ts: -------------------------------------------------------------------------------- 1 | import { Anchor, Point } from '../types'; 2 | 3 | export interface AxisTimeInterval { 4 | range(start: Date, stop: Date, step?: number): Date[]; 5 | } 6 | 7 | // next line does not translate well on typescript < 4.0.9 but it is more helpful 8 | // export type tickArgs = [count?: number, specifier?: string]; 9 | export type tickArgs = [number?, string?]; 10 | 11 | export type tickFormater = 12 | | ((domainValue: any[], index: number) => string) 13 | | ((domainValue: Date) => string) 14 | | ((t: unknown) => unknown); 15 | 16 | export type AxisTickLine = { x2?: number; y2?: number }; 17 | 18 | export type AxisTickText = { 19 | x?: number; 20 | y?: number; 21 | dy: number; 22 | text: string; 23 | }; 24 | 25 | export type AxisTick = { 26 | transform: Point; 27 | line: AxisTickLine; 28 | text: AxisTickText; 29 | }; 30 | 31 | export type AxisPath = { 32 | range0: number; 33 | range1: number; 34 | tick: number; 35 | }; 36 | 37 | export type AxisTicks = { 38 | anchor: Anchor; 39 | path: AxisPath; 40 | ticks: AxisTick[]; 41 | }; 42 | 43 | export type AxisData = AxisTicks & Point; 44 | -------------------------------------------------------------------------------- /packages/ez-core/src/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | AbstractScale, 3 | ScaleIdentity, 4 | ScaleLinear, 5 | ScaleLogarithmic, 6 | ScalePower, 7 | ScaleRadial, 8 | ScaleSymLog, 9 | ScaleTime, 10 | ScaleUtc, 11 | ScaleOrdinal, 12 | ScalePoint, 13 | ScaleBand, 14 | ScaleDiverging, 15 | ScaleDivergingLogarithmic, 16 | ScaleDivergingPower, 17 | ScaleDivergingSqrt, 18 | ScaleDivergingSymLog, 19 | ScaleSequential, 20 | ScaleSequentialLogarithmic, 21 | ScaleSequentialPower, 22 | ScaleSequentialSqrt, 23 | ScaleSequentialSymLog, 24 | ScaleSqrt, 25 | ScaleThreshold, 26 | ScaleQuantize, 27 | ScaleQuantile, 28 | } from './scales'; 29 | 30 | export { 31 | Animation, 32 | animate, 33 | animationMap, 34 | getEasingFunction, 35 | } from './animation'; 36 | 37 | export { default as Axis } from './axis'; 38 | 39 | export * from './utils'; 40 | 41 | export * from './types'; 42 | -------------------------------------------------------------------------------- /packages/ez-core/src/scales/ScalePoint.ts: -------------------------------------------------------------------------------- 1 | import { ScalePoint as D3ScalePoint, scalePoint } from 'd3-scale'; 2 | import { Dimensions, RawData } from '../types'; 3 | import { 4 | ScaleInterface, 5 | ScalePointDefinition, 6 | ScalePointMinimalDefinition, 7 | StringLike, 8 | } from './types'; 9 | import AbstractScale from './AbstractScale'; 10 | import { 11 | getCategoricalScaleDomain, 12 | getCategoricalScaleRange, 13 | configureCategoricalScale, 14 | } from './helpers'; 15 | 16 | /** 17 | * @class 18 | * @see D3ScalePoint 19 | */ 20 | class ScalePoint 21 | extends AbstractScale 22 | implements 23 | ScaleInterface<ScalePointDefinition, ScalePointMinimalDefinition> 24 | { 25 | constructor(definition: ScalePointDefinition = {}) { 26 | super(); 27 | this.userDefinition = definition; 28 | this.mergeDefinition(); 29 | } 30 | 31 | declare public userDefinition: ScalePointDefinition; 32 | 33 | declare public definition: ScalePointMinimalDefinition; 34 | 35 | declare public scale: D3ScalePoint<StringLike>; 36 | 37 | protected defaultDefinition: ScalePointMinimalDefinition = { 38 | reverse: false, 39 | roundRange: false, 40 | round: false, 41 | padding: 0.1, 42 | align: 0.5, 43 | }; 44 | 45 | protected compute( 46 | dimensions: Dimensions, 47 | data: RawData 48 | ): D3ScalePoint<StringLike> { 49 | let domain = getCategoricalScaleDomain(this.definition, data); 50 | let range = getCategoricalScaleRange(this.definition, dimensions); 51 | let scale: D3ScalePoint<StringLike> = scalePoint(domain, range); 52 | scale = configureCategoricalScale(scale, this.definition); 53 | return scale; 54 | } 55 | } 56 | 57 | export default ScalePoint; 58 | -------------------------------------------------------------------------------- /packages/ez-core/src/scales/ScaleUtc.ts: -------------------------------------------------------------------------------- 1 | import { ScaleTime as D3ScaleTime, scaleUtc } from 'd3-scale'; 2 | import { Dimensions, Direction, RawData } from '../types'; 3 | import { 4 | ScaleInterface, 5 | ScaleUtcDefinition, 6 | ScaleUtcMinimalDefinition, 7 | NumberLike, 8 | } from './types'; 9 | import { 10 | getContinuousScaleRange, 11 | getContinuousScaleDomain, 12 | configureContinuousScale, 13 | } from './helpers'; 14 | import ScaleTime from './ScaleTime'; 15 | 16 | /** 17 | * @class 18 | * @see D3ScaleTime 19 | */ 20 | class ScaleUtc 21 | extends ScaleTime 22 | implements ScaleInterface<ScaleUtcDefinition, ScaleUtcMinimalDefinition> 23 | { 24 | declare public userDefinition: ScaleUtcDefinition; 25 | 26 | declare public definition: ScaleUtcMinimalDefinition; 27 | 28 | declare public scale: D3ScaleTime<NumberLike, number, number>; 29 | 30 | protected defaultDefinition: ScaleUtcMinimalDefinition = { 31 | direction: Direction.HORIZONTAL, 32 | domainKey: 'time', 33 | maxPadding: 0, 34 | minPadding: 0, 35 | roundRange: false, 36 | nice: 0, 37 | softMin: Infinity, 38 | softMax: -Infinity, 39 | clamp: false, 40 | reverse: false, 41 | }; 42 | 43 | protected compute( 44 | dimensions: Dimensions, 45 | data: RawData 46 | ): D3ScaleTime<NumberLike, number, number> { 47 | const domain = getContinuousScaleDomain(this.definition, data, [ 48 | +new Date('2000-01-01'), 49 | +new Date('2000-01-02'), 50 | ]); 51 | const range = getContinuousScaleRange(this.definition, dimensions); 52 | let scale: D3ScaleTime<NumberLike, number, number> = scaleUtc(domain, range); 53 | // @ts-ignore d3-scale types does not consider scale time as a Continuous scale (while it is) 54 | scale = configureContinuousScale(scale, this.definition); 55 | return scale; 56 | } 57 | } 58 | 59 | export default ScaleUtc; 60 | -------------------------------------------------------------------------------- /packages/ez-core/src/scales/__tests__/ScaleDiverging.test.ts: -------------------------------------------------------------------------------- 1 | import D3ScaleDiverging from '../ScaleDiverging'; 2 | 3 | describe('ScaleDiverging', () => { 4 | it('Instanciate and computes with 0 config with a range of 0 - 800 (width) and a domain of 0 - 1', () => { 5 | const diverging = new D3ScaleDiverging(); 6 | expect(diverging).toBeDefined(); 7 | expect(diverging.scale).toBeDefined(); 8 | 9 | expect(diverging.scale.domain()).toEqual([0, 0.5, 1]); 10 | expect(diverging.scale.range()).toEqual([0, 400, 800]); 11 | }); 12 | 13 | it('Generates a new computed scale when user definition changes', () => { 14 | const diverging = new D3ScaleDiverging(); 15 | 16 | diverging.setDefinition({ range: [0, 50] }); 17 | 18 | expect(diverging.scale.range()).toEqual([0, 25, 50]); 19 | }); 20 | 21 | it('Outputs the scaled value (from the computed scale)', () => { 22 | const diverging = new D3ScaleDiverging({ 23 | domain: [-10, 0, 10], 24 | range: [0, 10, 100], 25 | }); 26 | 27 | // With a domain [-10, 0, 10] and range [0, 10, 100] 28 | // an input value -10 should be result on an output of 0 29 | // an input value 0 should be result on an output of 10 30 | // an input value 5 should be result on an output of 55 31 | // an input value 10 should be result on an output of 100 32 | // output can be out of the range boundaries as clamping is not applied 33 | expect(diverging.scale(-5)).toEqual(5); 34 | expect(diverging.scale(0)).toEqual(10); 35 | expect(diverging.scale(5)).toEqual(55); 36 | expect(diverging.scale(20)).toEqual(190); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/ez-core/src/scales/__tests__/ScaleLinear.test.ts: -------------------------------------------------------------------------------- 1 | import D3ScaleLinear from '../ScaleLinear'; 2 | 3 | describe('ScaleLinear', () => { 4 | it('Instanciate and computes with 0 config with a range of 0 - 800 (width) and a domain of 0 - 1', () => { 5 | const linear = new D3ScaleLinear(); 6 | expect(linear).toBeDefined(); 7 | expect(linear.scale).toBeDefined(); 8 | 9 | expect(linear.scale.domain()).toEqual([0, 1]); 10 | expect(linear.scale.range()).toEqual([0, 800]); 11 | }); 12 | 13 | it('Generates a new computed scale when user definition changes', () => { 14 | const linear = new D3ScaleLinear(); 15 | 16 | // nice is always set to 0 on tests to avoid that rounding affects in the tests 17 | linear.setDefinition({ range: [0, 50], nice: 0 }); 18 | expect(linear.scale.range()).toEqual([0, 50]); 19 | }); 20 | 21 | it('Outputs the scaled value (from the computed scale)', () => { 22 | const linear = new D3ScaleLinear({ 23 | domain: [0, 10], 24 | range: [0, 100], 25 | nice: 0, 26 | }); 27 | 28 | // With a domain [0, 10] and range [0, 100] 29 | // an input value 0 should be result on an output of 0 30 | // an input value 1 should be result on an output of 10 31 | // an input value 5 should be result on an output of 50 32 | // output can be out of the range boundaries as clamping is not applied 33 | expect(linear.scale(5)).toEqual(50); 34 | expect(linear.scale(10)).toEqual(100); 35 | expect(linear.scale(15)).toEqual(150); 36 | expect(linear.scale(-5)).toEqual(-50); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/ez-core/src/scales/__tests__/ScaleRadial.test.ts: -------------------------------------------------------------------------------- 1 | import D3ScaleRadial from '../ScaleRadial'; 2 | 3 | describe('ScaleRadial', () => { 4 | it('Instanciate and computes with 0 config with a range of 0 - 800 (width) and a domain of 0 - 1', () => { 5 | const radial = new D3ScaleRadial(); 6 | expect(radial).toBeDefined(); 7 | expect(radial.scale).toBeDefined(); 8 | 9 | expect(radial.scale.domain()).toEqual([0, 1]); 10 | expect(radial.scale.range()).toEqual([0, 800]); 11 | }); 12 | 13 | it('Generates a new computed scale when user definition changes', () => { 14 | const radial = new D3ScaleRadial(); 15 | 16 | // nice is always set to 0 on tests to avoid that rounding affects in the tests 17 | radial.setDefinition({ range: [0, 50], nice: 0 }); 18 | expect(radial.scale.range()).toEqual([0, 50]); 19 | }); 20 | 21 | it('Outputs the scaled value (from the computed scale)', () => { 22 | const radial = new D3ScaleRadial({ 23 | domain: [0, 10], 24 | range: [0, 100], 25 | nice: 0, 26 | }); 27 | 28 | // With a domain [0, 10] and range [0, 100] 29 | // an input value 0 should be result on an output of 0 30 | // an input value 2 should be result on an output of ~ 100 / Math.sqrt(5) 31 | // an input value 5 should be result on an output of ~ 100 / Math.sqrt(2) 32 | // output can be out of the range boundaries as clamping is not applied 33 | expect(radial.scale(0)).toEqual(0); 34 | expect(radial.scale(2)).toBeCloseTo(100 / Math.sqrt(5)); 35 | expect(radial.scale(5)).toBeCloseTo(100 / Math.sqrt(2)); 36 | expect(radial.scale(10)).toBeCloseTo(100); 37 | expect(radial.scale(-5)).toBeCloseTo(-100 / Math.sqrt(2)); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /packages/ez-core/src/scales/__tests__/ScaleSequential.test.ts: -------------------------------------------------------------------------------- 1 | import SaleSequential from '../ScaleSequential'; 2 | 3 | describe('ScaleSequential', () => { 4 | it('Instanciate and computes with 0 config with a range of 0 - 800 (width) and a domain of 0 - 1', () => { 5 | const sequential = new SaleSequential(); 6 | expect(sequential).toBeDefined(); 7 | expect(sequential.scale).toBeDefined(); 8 | 9 | expect(sequential.scale.domain()).toEqual([0, 1]); 10 | expect(sequential.scale.range()).toEqual([0, 800]); 11 | }); 12 | 13 | it('Generates a new computed scale when user definition changes', () => { 14 | const sequential = new SaleSequential(); 15 | 16 | sequential.setDefinition({ range: [0, 50] }); 17 | expect(sequential.scale.range()).toEqual([0, 50]); 18 | }); 19 | 20 | it('Outputs the scaled value (from the computed scale)', () => { 21 | const sequential = new SaleSequential({ 22 | domain: [0, 10], 23 | range: [0, 100], 24 | }); 25 | 26 | // With a domain [0, 10] and range [0, 100] 27 | // an input value 0 should be result on an output of 0 28 | // an input value 1 should be result on an output of 10 29 | // an input value 5 should be result on an output of 50 30 | // output can be out of the range boundaries as clamping is not applied 31 | expect(sequential.scale(5)).toEqual(50); 32 | expect(sequential.scale(10)).toEqual(100); 33 | expect(sequential.scale(15)).toEqual(150); 34 | expect(sequential.scale(-5)).toEqual(-50); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/ez-core/src/scales/__tests__/ScaleSequentialLogarithmic.test.ts: -------------------------------------------------------------------------------- 1 | import ScaleSequentialLogarithmic from '../ScaleSequentialLogarithmic'; 2 | 3 | describe('ScaleSequentialLogarithmic', () => { 4 | it('Instanciate and computes with 0 config with a range of 0 - 800 (width) and a domain of 0 - 1', () => { 5 | const sequentialLog = new ScaleSequentialLogarithmic(); 6 | expect(sequentialLog).toBeDefined(); 7 | expect(sequentialLog.scale).toBeDefined(); 8 | 9 | expect(sequentialLog.scale.domain()).toEqual([0.1, 10]); 10 | expect(sequentialLog.scale.range()).toEqual([0, 800]); 11 | }); 12 | 13 | it('Generates a new computed scale when user definition changes', () => { 14 | const sequentialLog = new ScaleSequentialLogarithmic(); 15 | 16 | sequentialLog.setDefinition({ range: [0, 50] }); 17 | 18 | expect(sequentialLog.scale.range()).toEqual([0, 50]); 19 | }); 20 | 21 | it('Outputs the scaled value (from the computed scale)', () => { 22 | const sequentialLog = new ScaleSequentialLogarithmic({ 23 | domain: [1, 1000], 24 | range: [0, 3], 25 | }); 26 | 27 | expect(sequentialLog.scale(1)).toEqual(0); 28 | expect(sequentialLog.scale(10)).toEqual(1); 29 | expect(sequentialLog.scale(1000)).toEqual(3); 30 | expect(sequentialLog.scale(10000)).toEqual(4); 31 | expect(sequentialLog.scale(0)).toEqual(NaN); 32 | }); 33 | 34 | it('Outputs the scaled value with a custom base', () => { 35 | const sequentialLog = new ScaleSequentialLogarithmic({ 36 | domain: [1, 64], 37 | range: [0, 6], 38 | base: 2, 39 | }); 40 | 41 | expect(sequentialLog.scale(1)).toBeCloseTo(0); 42 | expect(sequentialLog.scale(2)).toBeCloseTo(1); 43 | expect(sequentialLog.scale(32)).toBeCloseTo(5); 44 | expect(sequentialLog.scale(64)).toBeCloseTo(6); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /packages/ez-core/src/scales/__tests__/ScaleThreshold.test.ts: -------------------------------------------------------------------------------- 1 | import D3ScaleThreshold from '../ScaleThreshold'; 2 | 3 | describe('ThresholdScale', () => { 4 | it('Instanciate and computes with 0 config with a range of 0 - 800 (width) and a domain of 0.5', () => { 5 | const threshold = new D3ScaleThreshold(); 6 | expect(threshold).toBeDefined(); 7 | expect(threshold.scale).toBeDefined(); 8 | expect(threshold.scale.domain()).toEqual([0.5]); 9 | expect(threshold.scale.range()).toEqual([0, 800]); 10 | }); 11 | 12 | it('Generates a new computed scale when user definition changes', () => { 13 | const threshold = new D3ScaleThreshold(); 14 | threshold.setDefinition({ range: [0, 50] }); 15 | expect(threshold.scale.range()).toEqual([0, 50]); 16 | }); 17 | 18 | it('Outputs the scaled value (from the computed scale)', () => { 19 | const threshold = new D3ScaleThreshold({ 20 | domain: [0, 10], 21 | range: [0, 5, 10], 22 | }); 23 | 24 | // With a domain [0, 10] and range [0, 5, 10] 25 | // an input value 0 should be result on an output of 5 26 | // an input value 1 should be result on an output of 5 27 | // an input value 5 should be result on an output of 5 28 | // an input value 10 should be result on an output of 10 29 | // output can be out of the range boundaries as clamping is not applied 30 | expect(threshold.scale(-1)).toEqual(0); 31 | 32 | expect(threshold.scale(0)).toEqual(5); 33 | expect(threshold.scale(1)).toEqual(5); 34 | expect(threshold.scale(5)).toEqual(5); 35 | expect(threshold.scale(10)).toEqual(10); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /packages/ez-core/src/scales/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | getContinuousScaleDomain, 3 | getContinuousScaleRange, 4 | configureContinuousScale, 5 | } from './ContinuousScaleHelpers'; 6 | export { 7 | getCategoricalScaleDomain, 8 | getCategoricalScaleRange, 9 | configureCategoricalScale, 10 | } from './CategoricalScaleHelpers'; 11 | export { 12 | getDivergingScaleDomain, 13 | getDivergingScaleRange, 14 | configureDivergingScale, 15 | } from './DivergingScaleHelpers'; 16 | export { 17 | getSequentialScaleRange, 18 | getSequentialScaleDomain, 19 | configureSequentialScale, 20 | } from './SequentialScaleHelpers'; 21 | export { 22 | getThresholdScaleRange, 23 | getThresholdScaleDomain, 24 | configureThresholdScale, 25 | } from './ThresholdScaleHelpers'; 26 | -------------------------------------------------------------------------------- /packages/ez-core/src/scales/index.ts: -------------------------------------------------------------------------------- 1 | export { default as AbstractScale } from './AbstractScale'; 2 | export { default as ScaleIdentity } from './ScaleIdentity'; 3 | export { default as ScaleLinear } from './ScaleLinear'; 4 | export { default as ScaleLogarithmic } from './ScaleLogarithmic'; 5 | export { default as ScalePower } from './ScalePower'; 6 | export { default as ScaleRadial } from './ScaleRadial'; 7 | export { default as ScaleSymLog } from './ScaleSymLog'; 8 | export { default as ScaleTime } from './ScaleTime'; 9 | export { default as ScaleUtc } from './ScaleUtc'; 10 | export { default as ScaleOrdinal } from './ScaleOrdinal'; 11 | export { default as ScalePoint } from './ScalePoint'; 12 | export { default as ScaleBand } from './ScaleBand'; 13 | export { default as ScaleDiverging } from './ScaleDiverging'; 14 | export { default as ScaleDivergingLogarithmic } from './ScaleDivergingLogarithmic'; 15 | export { default as ScaleDivergingPower } from './ScaleDivergingPower'; 16 | export { default as ScaleDivergingSqrt } from './ScaleDivergingSqrt'; 17 | export { default as ScaleDivergingSymLog } from './ScaleDivergingSymLog'; 18 | export { default as ScaleSequential } from './ScaleSequential'; 19 | export { default as ScaleSequentialLogarithmic } from './ScaleSequentialLogarithmic'; 20 | export { default as ScaleSequentialPower } from './ScaleSequentialPower'; 21 | export { default as ScaleSequentialSqrt } from './ScaleSequentialSqrt'; 22 | export { default as ScaleSequentialSymLog } from './ScaleSequentialSymLog'; 23 | export { default as ScaleSqrt } from './ScaleSqrt'; 24 | export { default as ScaleThreshold } from './ScaleThreshold'; 25 | export { default as ScaleQuantize } from './ScaleQuantize'; 26 | export { default as ScaleQuantile } from './ScaleQuantile'; 27 | -------------------------------------------------------------------------------- /packages/ez-core/src/utils/__tests__/clamp.test.ts: -------------------------------------------------------------------------------- 1 | import { clamp } from '../clamp'; 2 | 3 | describe('clamp', () => { 4 | it('returns the same value', () => { 5 | expect(clamp(50, 0, 100)).toEqual(50); 6 | }); 7 | it('returns the max value', () => { 8 | expect(clamp(120, 0, 100)).toEqual(100); 9 | }); 10 | it('returns the min value', () => { 11 | expect(clamp(-1, 0, 100)).toEqual(0); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /packages/ez-core/src/utils/__tests__/data.test.ts: -------------------------------------------------------------------------------- 1 | import { normalizeData } from '../data'; 2 | 3 | describe('normalizeData', () => { 4 | const minimalData = [ 5 | { 6 | x: 1, 7 | }, 8 | { 9 | x: 2, 10 | }, 11 | { 12 | x: 3, 13 | }, 14 | ]; 15 | const normalizedData = { 16 | '0': { 17 | id: '0', 18 | label: '0', 19 | x: 1, 20 | }, 21 | '1': { 22 | id: '1', 23 | label: '1', 24 | x: 2, 25 | }, 26 | '2': { 27 | id: '2', 28 | label: '2', 29 | x: 3, 30 | }, 31 | }; 32 | it('takes raw data and returns a nomalized data as a dict', () => { 33 | const result = normalizeData(minimalData); 34 | expect(result).toEqual(normalizedData); 35 | }); 36 | 37 | it('returns the data as is if already normalized', () => { 38 | expect( 39 | normalizeData(Object.values(normalizedData)) 40 | ).toEqual(normalizedData); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /packages/ez-core/src/utils/__tests__/svg.test.ts: -------------------------------------------------------------------------------- 1 | import { transformTranslate } from '../svg'; 2 | 3 | describe('transformTranslate', () => { 4 | it('returns an svg transform translation statement', () => { 5 | expect(transformTranslate({ x: 10, y: 20 })).toEqual('translate(10, 20)'); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/ez-core/src/utils/clamp.ts: -------------------------------------------------------------------------------- 1 | export const clamp = (value: number, min: number, max: number) => 2 | Math.min(Math.max(value, min), max); 3 | -------------------------------------------------------------------------------- /packages/ez-core/src/utils/data.ts: -------------------------------------------------------------------------------- 1 | import { 2 | RawDatum, 3 | NormalizedDatum, 4 | RawData, 5 | NormalizedDataDict, 6 | } from '../types'; 7 | 8 | export const findDatumLabel = (datum: RawDatum) => { 9 | const labelKey = Object.keys(datum).find( 10 | (key: string) => typeof datum[key] === 'string' 11 | ); 12 | return labelKey ? datum[labelKey] : undefined; 13 | }; 14 | 15 | export const normalizeData = ( 16 | data: RawData, 17 | labelKey = 'label' 18 | ): NormalizedDataDict => { 19 | return data 20 | .map((d: RawDatum, index: number) => { 21 | return { 22 | ...d, 23 | id: d.id || index.toString(), 24 | label: 25 | labelKey in d ? d[labelKey] : findDatumLabel(d) || index.toString(), 26 | } as NormalizedDatum; 27 | }) 28 | .reduce((acc: NormalizedDataDict, d: NormalizedDatum) => { 29 | acc[d.id] = d; 30 | return acc; 31 | }, {} as NormalizedDataDict); 32 | }; 33 | -------------------------------------------------------------------------------- /packages/ez-core/src/utils/debounce.ts: -------------------------------------------------------------------------------- 1 | export function debounce(func: Function, timeout = 300) { 2 | // @ts-ignore 3 | const self = this; 4 | let timer: number; 5 | return (...args: any) => { 6 | if (typeof window !== 'undefined') { 7 | window.clearTimeout(timer); 8 | timer = window.setTimeout(() => { 9 | func.apply(self, args); 10 | }, timeout); 11 | } else { 12 | func.apply(self, args); 13 | } 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /packages/ez-core/src/utils/grid.ts: -------------------------------------------------------------------------------- 1 | import { Position } from '../types'; 2 | 3 | export const isVerticalPosition = (position: Position) => { 4 | return position === Position.LEFT || position === Position.RIGHT; 5 | }; 6 | -------------------------------------------------------------------------------- /packages/ez-core/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './defaults'; 2 | export * from './axis'; 3 | export * from './grid'; 4 | export * from './data'; 5 | export * from './line'; 6 | export * from './logger'; 7 | export * from './pie'; 8 | export * from './scale'; 9 | export * from './svg'; 10 | export * from './debounce'; 11 | export * from './clamp'; 12 | export * from './map'; 13 | -------------------------------------------------------------------------------- /packages/ez-core/src/utils/logger.ts: -------------------------------------------------------------------------------- 1 | // inspired from https://en.wikipedia.org/wiki/Syslog 2 | export enum Levels { 3 | debug, // Debug-level messages 4 | info, // Informational messages 5 | log, // Normal but significant 6 | warn, // Not harmful but to be taken seriously 7 | error, // Error conditions 8 | critical, // Critical conditions 9 | alert, // Action must be taken immediately 10 | emergency, // System is unusable 11 | } 12 | 13 | type LogFunctions = 'debug' | 'info' | 'log' | 'warn' | 'error'; 14 | 15 | interface Logger extends Console { 16 | emergency: Console['error']; 17 | alert: Console['error']; 18 | critical: Console['error']; 19 | } 20 | 21 | function createLogger(severityThreshold: number) { 22 | return new Proxy(console as Logger, { 23 | get(target, prop: string, reciever?: any) { 24 | if (prop in Levels) { 25 | if (severityThreshold <= Levels[prop as keyof typeof Levels]) { 26 | if (['emergency', 'alert', 'critical'].includes(prop)) { 27 | // we can do further customization for each danger level 28 | return (...args: any[]) => 29 | target.error( 30 | `%c${prop.toUpperCase()}`, 31 | 'font-size: 20px; color: red; display: block; background: white;', 32 | ...args 33 | ); 34 | } 35 | return target[prop as LogFunctions]; 36 | } else { 37 | return () => {}; 38 | } 39 | } 40 | return Reflect.get(target, prop, reciever); 41 | }, 42 | }); 43 | } 44 | let severity = 0; // allows any log level 45 | if (process.env.NODE_ENV === 'production') { 46 | severity = 3; // allow warning and errors 47 | } 48 | export const logger = createLogger(severity); 49 | -------------------------------------------------------------------------------- /packages/ez-core/src/utils/pie.ts: -------------------------------------------------------------------------------- 1 | import { ArcDatum } from './types'; 2 | import { arc } from 'd3-shape'; 3 | 4 | /** 5 | * Constructs a new arc generator with the default settings and generates the path. 6 | * 7 | * Ensure that the accessors used with the arc generator correspond to 8 | * the arguments passed into them, or set them to constants as appropriate. 9 | * 10 | * @param shapeDatum Interface corresponding to the minimum data type assumed by the accessor functions of the Arc generator. 11 | * @param outerRadius Sets the outer radius to the specified number and returns this arc generator. 12 | * @param innerRadius Sets the inner radius to the specified number and returns this arc generator. 13 | * @param cornerRadius Computes the midpoint [x, y] of the center line of the arc that would be generated by the given arguments. 14 | * @param padAngle Sets the pad angle to the specified number and returns this arc generator. 15 | * @returns arc path 16 | */ 17 | export const generateArc = ( 18 | shapeDatum: ArcDatum, 19 | outerRadius: number, 20 | innerRadius: number, 21 | cornerRadius: number = 0, 22 | padAngle: number = 0, 23 | padRadius: number = 0 24 | ) => { 25 | const arcGenerator = arc().outerRadius(outerRadius).innerRadius(innerRadius); 26 | if (cornerRadius) { 27 | arcGenerator.cornerRadius(cornerRadius); 28 | } 29 | if (padAngle) { 30 | arcGenerator.padAngle(padAngle); 31 | } 32 | if (padRadius) { 33 | arcGenerator.padRadius(padRadius); 34 | } 35 | return arcGenerator({ 36 | startAngle: shapeDatum.startAngle, 37 | endAngle: shapeDatum.endAngle, 38 | innerRadius, 39 | outerRadius, 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /packages/ez-core/src/utils/svg.ts: -------------------------------------------------------------------------------- 1 | import { Point } from '../types'; 2 | 3 | export const transformTranslate = ({ x, y }: Point) => { 4 | return `translate(${x}, ${y})`; 5 | }; 6 | -------------------------------------------------------------------------------- /packages/ez-core/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "target": "ES6", 5 | } 6 | } -------------------------------------------------------------------------------- /packages/ez-core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs 3 | "include": [ 4 | "src", 5 | "types", 6 | ], 7 | "compilerOptions": { 8 | "module": "esnext", 9 | "lib": [ 10 | "dom", 11 | "esnext" 12 | ], 13 | "target": "ES6", 14 | "importHelpers": true, 15 | // output .d.ts declaration files for consumers 16 | "declaration": true, 17 | // output .js.map sourcemap files for consumers 18 | "sourceMap": true, 19 | // match output dir to input dir. e.g. dist/index instead of dist/src/index 20 | "rootDir": "./src", 21 | // stricter type-checking for stronger correctness. Recommended by TS 22 | "strict": true, 23 | // linter checks for common issues 24 | "noImplicitReturns": true, 25 | "noFallthroughCasesInSwitch": true, 26 | // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative 27 | "noUnusedLocals": true, 28 | "noUnusedParameters": true, 29 | // use Node's module resolution algorithm, instead of the legacy TS one 30 | "moduleResolution": "node", 31 | // interop between ESM and CJS modules. Recommended by TS 32 | "esModuleInterop": true, 33 | // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS 34 | "skipLibCheck": true, 35 | // error out if import and file system have a casing mismatch. Recommended by TS 36 | "forceConsistentCasingInFileNames": true, 37 | // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc` 38 | "noEmit": true 39 | } 40 | } -------------------------------------------------------------------------------- /packages/ez-css/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Hexastack 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 | -------------------------------------------------------------------------------- /packages/ez-css/README.md: -------------------------------------------------------------------------------- 1 | # EazyChart 2 | <img align="center" width="500" alt="EazyChart logo" src="https://eazychart.com/img/logo.png"/> 3 | <br/> 4 | <a href="https://eazychart.com/">EazyChart</a> is a reactive chart library, it offers the ability to easily add charts in your React and Vue web applications. EazyChart does not depend on a JS chart library instead it depends only on some of the <a href="https://d3js.org/">D3.js</a> library's submodules. 5 | 6 | <br /> 7 | <a href="https://docs.eazychart.com/">Website</a> 8 | <span>  •  </span> 9 | <a href="https://docs.eazychart.com/?path=/story/get-started-introduction--page">Get Started</a> 10 | <span>  •  </span> 11 | <a href="https://docs.eazychart.com/?path=/story/get-started-installation--page">Installation</a> 12 | <br /> 13 | <hr /> 14 | 15 | This package is the CSS lib that is used by both `eazychart-react` and `eazychart-vue` packages. It contains the base CSS stylesheets required by EazyChart to work properly. Please refer to the main repo [README](../../README.md) to learn more about this package. 16 | -------------------------------------------------------------------------------- /packages/ez-css/css/style.css: -------------------------------------------------------------------------------- 1 | .ez-fragment-filter{height:100%;width:100%}.ez-legend{text-align:center;padding-top:1rem}.ez-legend-key{display:inline-block;cursor:pointer;margin-right:1rem}.ez-legend-box{width:1rem;height:1rem;display:inline-block;vertical-align:sub}.ez-fragment-tooltip{height:100%;width:100%}.ez-tooltip{position:fixed;display:flex;align-items:flex-start;background-color:rgba(0,0,0,0.85);pointer-events:none;padding:1rem;white-space:nowrap;color:#FFF;border-radius:.25rem;box-shadow:1px 1px 16px rgba(0,0,0,0.25);font-size:1.125rem}.ez-tooltip-color{width:24px;height:24px;vertical-align:middle;border:1px solid #FFF;margin:.25rem}.ez-tooltip-text{margin:0 0 0 .5rem}.ez-tooltip-attribute{margin:0 .25rem;display:table-row}.ez-tooltip-attribute--name,.ez-tooltip-attribute--value{display:table-cell;padding:.125rem}.ez-tooltip-attribute--name{text-align:right}.ez-tooltip-attribute--value{font-weight:700}.ez-axis{fill:none;stroke:#000}.ez-axis-tick-text{stroke:none;fill:#000}.ez-axis-title{font-weight:500;fill:#000;stroke:none}.ez-grid{fill:none;stroke:#a8a8a8}.ez-point:hover{filter:brightness(0.75)}.ez-line{fill:none;stroke-linejoin:round;stroke-linecap:round}.ez-viz{height:100%;width:100%}.ez-chart{display:flex} 2 | -------------------------------------------------------------------------------- /packages/ez-css/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html lang="en"> 3 | 4 | <head> 5 | <meta charset="UTF-8"> 6 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 | <meta http-equiv="X-UA-Compatible" content="ie=edge"> 8 | <link rel="stylesheet" href="css/style.css"> 9 | <title>My app 10 | 11 | 12 | 13 |

My app

14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/ez-css/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eazychart-css", 3 | "version": "0.2.1-alpha.0", 4 | "description": "EazyChart SCSS lib following a simplified version of the 7-1 pattern", 5 | "keywords": [ 6 | "chart", 7 | "library", 8 | "svg", 9 | "react", 10 | "dataviz", 11 | "graph", 12 | "typescript", 13 | "javascript", 14 | "data", 15 | "visualization", 16 | "web" 17 | ], 18 | "author": "Hexastack", 19 | "license": "MIT", 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/Hexastack/eazychart.git" 23 | }, 24 | "bugs": { 25 | "url": "https://github.com/Hexastack/eazychart/issues" 26 | }, 27 | "homepage": "https://eazychart.com", 28 | "main": "css/style.css", 29 | "private": false, 30 | "publishConfig": { 31 | "access": "public", 32 | "registry": "https://registry.npmjs.org/" 33 | }, 34 | "scripts": { 35 | "start": "npm-run-all --parallel liveserver watch", 36 | "liveserver": "live-server", 37 | "watch": "node-sass sass/main.scss css/style.css -w", 38 | "compile": "node-sass sass/main.scss css/style.css", 39 | "prefix": "postcss css/style.css --use autoprefixer -o css/style.css", 40 | "compress": "node-sass css/style.css css/style.css --output-style compressed", 41 | "build": "npm-run-all compile prefix compress", 42 | "prepare": "yarn build" 43 | }, 44 | "devDependencies": { 45 | "autoprefixer": "^10.4.1", 46 | "node-sass": "^7.0.1", 47 | "npm-run-all": "^4.1.5", 48 | "postcss": "^8.4.5", 49 | "postcss-cli": "^9.1.0" 50 | }, 51 | "gitHead": "232adf76d2550b73fbdcb01e4203f8b9cde03515" 52 | } 53 | -------------------------------------------------------------------------------- /packages/ez-css/sass/addons/legend.scss: -------------------------------------------------------------------------------- 1 | .ez-fragment-filter { 2 | height: 100%; 3 | width: 100%; 4 | } 5 | 6 | .ez-legend { 7 | text-align: center; 8 | padding-top: 1rem; 9 | } 10 | 11 | .ez-legend-key { 12 | display: inline-block; 13 | cursor: pointer; 14 | margin-right: 1rem; 15 | } 16 | 17 | .ez-legend-box { 18 | width: 1rem; 19 | height: 1rem; 20 | display: inline-block; 21 | vertical-align: sub; 22 | } 23 | -------------------------------------------------------------------------------- /packages/ez-css/sass/addons/tooltip.scss: -------------------------------------------------------------------------------- 1 | .ez-fragment-tooltip { 2 | height: 100%; 3 | width: 100%; 4 | } 5 | 6 | .ez-tooltip { 7 | position: fixed; 8 | display: flex; 9 | align-items: flex-start; 10 | background-color: rgba(0, 0, 0, 0.85); 11 | pointer-events: none; 12 | padding: 1rem; 13 | white-space: nowrap; 14 | color: #FFF; 15 | border-radius: .25rem; 16 | box-shadow: 1px 1px 16px rgba(0, 0, 0, 0.25); 17 | font-size: 1.125rem; 18 | } 19 | 20 | .ez-tooltip-color { 21 | width: 24px; 22 | height: 24px; 23 | vertical-align: middle; 24 | border: 1px solid #FFF; 25 | margin: .25rem; 26 | } 27 | 28 | .ez-tooltip-text { 29 | margin: 0 0 0 .5rem; 30 | } 31 | 32 | .ez-tooltip-attribute { 33 | margin: 0 .25rem; 34 | display: table-row; 35 | } 36 | 37 | .ez-tooltip-attribute--name, 38 | .ez-tooltip-attribute--value { 39 | display: table-cell; 40 | padding: .125rem; 41 | } 42 | 43 | .ez-tooltip-attribute--name { 44 | // font-weight: 700; 45 | text-align: right; 46 | } 47 | 48 | 49 | .ez-tooltip-attribute--value { 50 | font-weight: 700; 51 | } -------------------------------------------------------------------------------- /packages/ez-css/sass/chart.scss: -------------------------------------------------------------------------------- 1 | .ez-viz { 2 | height: 100%; 3 | width: 100%; 4 | } 5 | .ez-chart { 6 | display: flex; 7 | } 8 | -------------------------------------------------------------------------------- /packages/ez-css/sass/main.scss: -------------------------------------------------------------------------------- 1 | @import "addons/legend"; 2 | @import "addons/tooltip"; 3 | 4 | @import "recipes/bar"; 5 | @import "recipes/column"; 6 | 7 | @import "scales/axis"; 8 | @import "scales/grid"; 9 | 10 | @import "shapes/point"; 11 | @import "shapes/line"; 12 | @import "shapes/bar"; 13 | 14 | @import "chart"; 15 | -------------------------------------------------------------------------------- /packages/ez-css/sass/recipes/bar.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hexastack/eazychart/d54f8cb33045b57097534c31a021ea5a049d3916/packages/ez-css/sass/recipes/bar.scss -------------------------------------------------------------------------------- /packages/ez-css/sass/recipes/column.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hexastack/eazychart/d54f8cb33045b57097534c31a021ea5a049d3916/packages/ez-css/sass/recipes/column.scss -------------------------------------------------------------------------------- /packages/ez-css/sass/scales/axis.scss: -------------------------------------------------------------------------------- 1 | .ez-axis { 2 | fill: none; 3 | stroke: #000; 4 | } 5 | 6 | .ez-axis-tick-text { 7 | stroke: none; 8 | fill: #000; 9 | } 10 | 11 | .ez-axis-title { 12 | font-weight: 500; 13 | fill: #000; 14 | stroke: none; 15 | } 16 | -------------------------------------------------------------------------------- /packages/ez-css/sass/scales/grid.scss: -------------------------------------------------------------------------------- 1 | .ez-grid { 2 | fill: none; 3 | stroke: #a8a8a8; 4 | } 5 | -------------------------------------------------------------------------------- /packages/ez-css/sass/shapes/bar.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hexastack/eazychart/d54f8cb33045b57097534c31a021ea5a049d3916/packages/ez-css/sass/shapes/bar.scss -------------------------------------------------------------------------------- /packages/ez-css/sass/shapes/line.scss: -------------------------------------------------------------------------------- 1 | .ez-line { 2 | fill: none; 3 | stroke-linejoin: round; 4 | stroke-linecap: round; 5 | } 6 | -------------------------------------------------------------------------------- /packages/ez-css/sass/shapes/point.scss: -------------------------------------------------------------------------------- 1 | .ez-point:hover { 2 | filter: brightness(0.75); 3 | } 4 | -------------------------------------------------------------------------------- /packages/ez-dev/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Hexastack 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 | -------------------------------------------------------------------------------- /packages/ez-dev/README.md: -------------------------------------------------------------------------------- 1 | # `ez-dev` 2 | 3 | > This project is contains common development tools and configs used in the lib packages : eazychart-vue and eazychart-react in order to reduce code configs redundancy (jest, eslint, tsconfig, ...). 4 | 5 | ## Structure 6 | 7 | ### JEST 8 | 9 | This folder contains common unit tests snapshots and configs used in the lib packages : eazychart-vue and eazychart-react make sure that the libraries delivers the same results (quasi). 10 | 11 | #### snapshots/ 12 | 13 | Directory that contains all the snap files used by Jest to match the results. 14 | 15 | #### lib/snapshotResolver.js 16 | 17 | #This is used for example in @eazychart-*/jest.config.js in order to point Jest to the "snapshots" folder. 18 | 19 | #### lib/data.ts 20 | 21 | This is mock data used for unit tests, so let's say that this is the input while the snapshots are the output. 22 | 23 | ### IMPORTANT 24 | 25 | Note that Jest uses the directory structure, filenames as well as the string passed to `it()` calls in order to index the snapshots. For this matter, we need to manually ensure that we have consistent file / test cases naming in order to guarantee that vue / react components are matched against the same snapshot. 26 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/base.config.js: -------------------------------------------------------------------------------- 1 | const esModules = [ 2 | 'd3-scale', 3 | 'd3-interpolate', 4 | 'd3-color', 5 | 'd3-ease', 6 | 'd3-array', 7 | 'internmap', 8 | 'd3-format', 9 | 'd3-time', 10 | 'd3-shape', 11 | 'd3-path', 12 | 'd3-geo', 13 | ].join('|'); 14 | 15 | module.exports = { 16 | transformIgnorePatterns: [`/node_modules/(?!${esModules})`], 17 | snapshotResolver: '../ez-dev/jest/snapshotResolver.js', 18 | snapshotSerializers: ['../ez-dev/jest/jest-html-serializer'], 19 | }; 20 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param testPath Path of the test file being test3ed 4 | * @param snapshotExtension The extension for snapshots (.snap usually) 5 | */ 6 | const resolveSnapshotPath = (testPath, snapshotExtension) => { 7 | const snapshotFilePath = testPath + snapshotExtension; //(i.e. some.test.js + '.snap') 8 | return snapshotFilePath; 9 | } 10 | 11 | /** 12 | * 13 | * @param snapshotFilePath The filename of the snapshot (i.e. some.test.js.snap) 14 | * @param snapshotExtension The extension for snapshots (.snap) 15 | */ 16 | const resolveTestPath = (snapshotFilePath, snapshotExtension) => { 17 | const testPath = snapshotFilePath.replace(snapshotExtension, '').replace('__snapshots__/', ''); //Remove the .snap 18 | return testPath; 19 | } 20 | 21 | /* Used to validate resolveTestPath(resolveSnapshotPath( {this} )) */ 22 | const testPathForConsistencyCheck = 'some.test.js'; 23 | 24 | module.exports = { 25 | resolveSnapshotPath, resolveTestPath, testPathForConsistencyCheck 26 | }; 27 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/jest-html-serializer.js: -------------------------------------------------------------------------------- 1 | var toDiffableHtml = require('diffable-html'); 2 | 3 | module.exports = { 4 | test(object) { 5 | const trimmed = object.toString().trim(); 6 | return ( 7 | trimmed.length > 2 && 8 | trimmed[0] === '<' && 9 | trimmed[trimmed.length - 1] === '>' 10 | ); 11 | }, 12 | // ez-react 13 | serialize(val) { 14 | return toDiffableHtml(val, { sortAttributes: (arr) => arr.sort() }).trim(); 15 | }, 16 | // ez-vue 17 | print(val) { 18 | return toDiffableHtml(val).trim(); 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/shims-jest.d.ts: -------------------------------------------------------------------------------- 1 | declare var jest: { fn: Function }; 2 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshotResolver.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const SEP = path.sep; 3 | 4 | const resolveLocal = (path) => { 5 | return path 6 | .replace(`ez-vue${SEP}`, `ez-dev${SEP}`) 7 | .replace(`ez-react${SEP}`, `ez-dev${SEP}`) 8 | .replace(`tests${SEP}unit${SEP}`, `jest${SEP}snapshots${SEP}`); 9 | }; 10 | 11 | /** 12 | * 13 | * @param testPath Path of the test file being test3ed 14 | * @param snapshotExtension The extension for snapshots (.snap usually) 15 | */ 16 | const resolveSnapshotPath = (testPath, snapshotExtension) => { 17 | const snapshotFilePath = resolveLocal(testPath) + snapshotExtension; //(i.e. some.test.js + '.snap') 18 | return snapshotFilePath; 19 | }; 20 | 21 | /** 22 | * 23 | * @param snapshotFilePath The filename of the snapshot (i.e. some.test.js.snap) 24 | * @param snapshotExtension The extension for snapshots (.snap) 25 | */ 26 | const resolveTestPath = (snapshotFilePath, snapshotExtension) => { 27 | const testPath = resolveLocal(snapshotFilePath) 28 | .replace(snapshotExtension, '') 29 | .replace(`__snapshots__${SEP}`, ''); // Remove the .snap 30 | return testPath; 31 | }; 32 | 33 | /* Used to validate resolveTestPath(resolveSnapshotPath( {this} )) */ 34 | const testPathForConsistencyCheck = 'some.spec.ts'; 35 | 36 | module.exports = { 37 | resolveSnapshotPath, 38 | resolveTestPath, 39 | testPathForConsistencyCheck, 40 | }; 41 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/Arcs.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Arcs renders svg radial with the right coordinates / dimensions 1`] = ` 4 | 8 | 15 | 16 | 23 | 24 | 31 | 32 | 33 | `; 34 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/Area.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Area renders svg area with the right coordinates / dimensions 1`] = ` 4 | 5 | 6 | 16 | 17 | 26 | 27 | 28 | 29 | `; 30 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/Bars.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Bars renders svg rects with the right coordinates / dimensions 1`] = ` 4 | 5 | 14 | 15 | 24 | 25 | 34 | 35 | 36 | `; 37 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/Bubbles.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Bubbles renders svg bubbles with the right coordinates / path 1`] = ` 4 | 5 | 14 | 15 | 24 | 25 | 34 | 35 | 36 | `; 37 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/Chart.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Chart renders svg chart container with the right slots 1`] = ` 4 |
8 |
9 |
10 |
11 | 15 | 16 |
17 | Default Slot 18 |
19 | 20 | 21 |
22 |
23 | Legend Slot 24 |
25 |
26 |
27 | Tooltip Slot 28 |
29 |
30 |
31 | `; 32 | 33 | exports[`Chart should provide the chart data to the children components 1`] = ` 34 |
38 |
39 |
40 |
41 | 45 | 46 |
47 | [{"id":"1","label":"Alpha","value":50,"amount":10},{"id":"2","label":"Beta","value":100,"amount":20},{"id":"3","label":"Gamma","value":75,"amount":30}] 48 |
49 | 50 | 51 |
52 |
53 |
57 |
58 |
59 |
60 | `; 61 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/IrregularArcs.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`IrregularArcs renders svg irregular arcs with the right coordinates / dimensions 1`] = ` 4 | 8 | 15 | 16 | 23 | 24 | 31 | 32 | 33 | `; 34 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/Map.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Map renders svg areas using GeoJSON features 1`] = ` 4 | 5 | 14 | 15 | 24 | 25 | 34 | 35 | 36 | `; 37 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/Pie.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Pie renders svg pie with the right coordinates / dimensions 1`] = ` 4 | 8 | 15 | 16 | 23 | 24 | 31 | 32 | 33 | `; 34 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/Points.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Points renders svg points with the right coordinates / path 1`] = ` 4 | 5 | 15 | 16 | 26 | 27 | 37 | 38 | 39 | `; 40 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/addons/Legend.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Legend renders filters for data supplied 1`] = ` 4 |
5 |
9 |
13 |
14 | 15 | Alpha 16 | 17 |
18 |
22 |
26 |
27 | 28 | Beta 29 | 30 |
31 |
35 |
39 |
40 | 41 | Gamma 42 | 43 |
44 |
45 | `; 46 | 47 | exports[`Legend renders no filters for empty data 1`] = ` 48 |
49 |
50 | `; 51 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/addons/Tooltip.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Tooltip renders no tooltip when shape is being hovered 1`] = ` 4 |
8 |
9 | `; 10 | 11 | exports[`Tooltip renders the tooltip when a shape is hovered 1`] = ` 12 |
16 |
20 |
21 |
22 |
23 |
24 | value : 25 |
26 |
27 | 50 28 |
29 |
30 |
31 |
32 | amount : 33 |
34 |
35 | 10 36 |
37 |
38 |
39 |
40 | `; 41 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/scales/GridLines.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`GridLines renders horizontal grid lines with four ticks 1`] = ` 4 | 5 | 12 | 13 | 20 | 21 | 28 | 29 | 36 | 37 | 44 | 45 | 46 | `; 47 | 48 | exports[`GridLines renders vertical grid lines with four ticks 1`] = ` 49 | 50 | 57 | 58 | 65 | 66 | 73 | 74 | 81 | 82 | 83 | `; 84 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/shapes/Arc.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Arc renders an svg arc with the right coordinates / dimensions 1`] = ` 4 | 11 | 12 | `; 13 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/shapes/AreaPath.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`AreaPath renders an svg path with the right coordinates / path 1`] = ` 4 | 14 | 15 | `; 16 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/shapes/Bar.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Bar renders an svg rect with the right coordinates / dimensions 1`] = ` 4 | 13 | 14 | `; 15 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/shapes/LinePath.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`LinePath renders an svg path with the right coordinates / path 1`] = ` 4 | 13 | 14 | `; 15 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/shapes/MapPath.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`MapPath renders an svg path given a GeoJSON feature 1`] = ` 4 | 13 | 14 | `; 15 | -------------------------------------------------------------------------------- /packages/ez-dev/jest/snapshots/components/shapes/Point.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Point renders an svg circle with the right coordinates 1`] = ` 4 | 14 | 15 | `; 16 | -------------------------------------------------------------------------------- /packages/ez-dev/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eazychart-dev", 3 | "version": "0.7.1-alpha.0", 4 | "description": "Common jest code (used by react/vue)", 5 | "keywords": [ 6 | "chart", 7 | "library", 8 | "svg", 9 | "react", 10 | "dataviz", 11 | "graph", 12 | "typescript", 13 | "javascript", 14 | "data", 15 | "visualization", 16 | "web" 17 | ], 18 | "author": "Hexastack", 19 | "license": "MIT", 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/Hexastack/eazychart.git" 23 | }, 24 | "bugs": { 25 | "url": "https://github.com/Hexastack/eazychart/issues" 26 | }, 27 | "homepage": "https://eazychart.com", 28 | "private": true, 29 | "main": "lib/index.js", 30 | "directories": { 31 | "lib": "jest" 32 | }, 33 | "files": [ 34 | "jest" 35 | ], 36 | "devDependencies": { 37 | "diffable-html": "^5.0.0", 38 | "eazychart-core": "^0.7.1-alpha.0" 39 | }, 40 | "gitHead": "4776e52be4c44e634cd6983f02bef30547617978" 41 | } 42 | -------------------------------------------------------------------------------- /packages/ez-dev/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs 3 | "include": ["jest"], 4 | "compilerOptions": { 5 | "module": "esnext", 6 | "lib": ["dom", "esnext"], 7 | "importHelpers": true, 8 | // output .d.ts declaration files for consumers 9 | "declaration": true, 10 | // output .js.map sourcemap files for consumers 11 | "sourceMap": true, 12 | // match output dir to input dir. e.g. dist/index instead of dist/src/index 13 | "rootDir": "./", 14 | // stricter type-checking for stronger correctness. Recommended by TS 15 | "strict": true, 16 | // linter checks for common issues 17 | "noImplicitReturns": true, 18 | "noFallthroughCasesInSwitch": true, 19 | // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | // use Node's module resolution algorithm, instead of the legacy TS one 23 | "moduleResolution": "node", 24 | // transpile JSX to React.createElement 25 | "jsx": "react", 26 | // interop between ESM and CJS modules. Recommended by TS 27 | "esModuleInterop": true, 28 | // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS 29 | "skipLibCheck": true, 30 | // error out if import and file system have a casing mismatch. Recommended by TS 31 | "forceConsistentCasingInFileNames": true, 32 | // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc` 33 | "noEmit": true, 34 | "types": ["node"] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/ez-react/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'react-app', 4 | 'prettier/@typescript-eslint', 5 | 'plugin:prettier/recommended', 6 | ], 7 | rules: { 8 | 'no-console': 'error', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/ez-react/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | .cache 5 | dist 6 | /storybook-static 7 | -------------------------------------------------------------------------------- /packages/ez-react/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "tabWidth": 2, 4 | "printWidth": 80, 5 | "singleQuote": true, 6 | "trailingComma": "es5" 7 | } 8 | -------------------------------------------------------------------------------- /packages/ez-react/.storybook/main.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | stories: ['../src/**/*.stories.@(ts|tsx|js|jsx|mdx)'], 5 | addons: ['@storybook/addon-links', '@storybook/addon-essentials'], 6 | framework: '@storybook/react', 7 | // https://storybook.js.org/docs/react/configure/typescript#mainjs-configuration 8 | typescript: { 9 | check: true, // type-check stories during Storybook build 10 | }, 11 | webpackFinal: async (config) => { 12 | config.resolve.modules = [ 13 | ...(config.resolve.modules || []), 14 | path.resolve(__dirname, '../'), 15 | ]; 16 | 17 | config.resolve.alias = { 18 | ...config.resolve.alias, 19 | '@': path.resolve(__dirname, '../src'), 20 | }; 21 | 22 | return config; 23 | }, 24 | refs: (config, { configType }) => { 25 | if (configType === 'DEVELOPMENT') { 26 | return { 27 | vue: { 28 | title: 'VUE', 29 | url: 'http://localhost:6007', 30 | expanded: true 31 | }, 32 | }; 33 | } 34 | return { 35 | vue: { 36 | title: 'VUE', 37 | url: 'https://docs-vue.eazychart.com', 38 | expanded: true, 39 | }, 40 | }; 41 | }, 42 | }; 43 | -------------------------------------------------------------------------------- /packages/ez-react/.storybook/manager-head.html: -------------------------------------------------------------------------------- 1 | EazyChart - React Charts Library 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /packages/ez-react/.storybook/manager.js: -------------------------------------------------------------------------------- 1 | import { addons } from '@storybook/addons'; 2 | import customTheme from './theme'; 3 | 4 | addons.setConfig({ 5 | theme: customTheme, 6 | }); 7 | -------------------------------------------------------------------------------- /packages/ez-react/.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import 'eazychart-css/css/style.css'; 2 | import './docs-root.css'; 3 | 4 | export const parameters = { 5 | // https://storybook.js.org/docs/react/essentials/actions#automatically-matching-args 6 | actions: { argTypesRegex: '^on.*' }, 7 | options: { 8 | storySort: { 9 | method: 'alphabetical', 10 | order: [ 11 | 'Get Started', 12 | [ 13 | 'Introduction', 14 | 'Installation', 15 | 'Your First Chart', 16 | 'Essentials', 17 | ['Custom CSS', 'Responsive Charts', 'Common Props'], 18 | ], 19 | 'React', 20 | ], 21 | locales: 'en-US', 22 | }, 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /packages/ez-react/.storybook/theme.js: -------------------------------------------------------------------------------- 1 | import { create } from '@storybook/theming'; 2 | 3 | export default create({ 4 | base: 'light', 5 | brandTitle: 'EazyChart', 6 | brandUrl: 'https://eazychart.com', 7 | brandImage: 'https://docs.eazychart.com/images/logo_350x150.png', 8 | brandTarget: '_self', 9 | }); 10 | -------------------------------------------------------------------------------- /packages/ez-react/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Hexastack 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 | -------------------------------------------------------------------------------- /packages/ez-react/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@babel/preset-env', 4 | '@babel/preset-react', 5 | '@babel/preset-typescript', 6 | ], 7 | plugins: [ 8 | ['@babel/plugin-transform-typescript', { allowDeclareFields: true }], 9 | ['@babel/plugin-proposal-class-properties', { loose: true }], 10 | ['@babel/plugin-proposal-private-methods', { loose: true }], 11 | ['@babel/plugin-proposal-private-property-in-object', { loose: true }], 12 | ], 13 | env: { 14 | test: { 15 | plugins: [ 16 | '@babel/plugin-transform-modules-commonjs', 17 | '@babel/transform-runtime', 18 | ], 19 | }, 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /packages/ez-react/jest.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('eazychart-dev/jest/base.config'); 2 | 3 | module.exports = { 4 | ...baseConfig, 5 | moduleNameMapper: { 6 | '@/(.*)': '/src/$1', 7 | 'tests/(.*)': '/tests/$1', 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/ez-react/rollup.config.js: -------------------------------------------------------------------------------- 1 | import peerDepsExternal from 'rollup-plugin-peer-deps-external'; 2 | import resolve from '@rollup/plugin-node-resolve'; 3 | import typescript from 'rollup-plugin-typescript2'; 4 | // import postcss from 'rollup-plugin-postcss'; 5 | import { uglify } from 'rollup-plugin-uglify'; 6 | import { babel } from '@rollup/plugin-babel'; 7 | 8 | // eslint-disable-next-line import/no-anonymous-default-export 9 | export default { 10 | input: './src/index.ts', 11 | output: [ 12 | { file: 'dist/eazychart.cjs.js', format: 'cjs' }, 13 | { file: 'dist/eazychart.esm.js', format: 'es' }, 14 | ], 15 | plugins: [ 16 | peerDepsExternal(), 17 | resolve(), 18 | typescript({ 19 | tsconfig: 'tsconfig.build.json', 20 | useTsconfigDeclarationDir: true, 21 | }), 22 | uglify(), 23 | babel(), 24 | ], 25 | }; 26 | -------------------------------------------------------------------------------- /packages/ez-react/src/components/Bars.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, SVGAttributes, useMemo } from 'react'; 2 | import { scaleRectangleData } from 'eazychart-core/src'; 3 | import { Bar } from '@/components/shapes/Bar'; 4 | import { useChart } from '@/lib/use-chart'; 5 | import { useCartesianScales } from '@/components/scales/CartesianScale'; 6 | import { useColorScale } from './scales/ColorScale'; 7 | 8 | export interface BarsProps extends SVGAttributes { 9 | xDomainKey: string; 10 | yDomainKey: string; 11 | } 12 | 13 | export const Bars: FC = ({ xDomainKey, yDomainKey, ...rest }) => { 14 | const { data, dimensions, isRTL } = useChart(); 15 | const { xScale, yScale } = useCartesianScales(); 16 | const { colorScale } = useColorScale(); 17 | 18 | const scaledData = useMemo(() => { 19 | return scaleRectangleData( 20 | data, 21 | xDomainKey, 22 | yDomainKey, 23 | xScale, 24 | yScale, 25 | colorScale, 26 | dimensions, 27 | isRTL 28 | ); 29 | }, [ 30 | data, 31 | xDomainKey, 32 | yDomainKey, 33 | xScale, 34 | yScale, 35 | colorScale, 36 | dimensions, 37 | isRTL, 38 | ]); 39 | 40 | return ( 41 | 42 | {scaledData.map((rectDatum) => { 43 | return ; 44 | })} 45 | 46 | ); 47 | }; 48 | -------------------------------------------------------------------------------- /packages/ez-react/src/components/addons/legend/LegendItem.tsx: -------------------------------------------------------------------------------- 1 | import { LegendClickEventHandler } from 'eazychart-core/src/types'; 2 | import React, { DOMAttributes, useState } from 'react'; 3 | 4 | export interface LegendItemProps extends DOMAttributes { 5 | label: string; 6 | color: string; 7 | onToggle?: LegendClickEventHandler; 8 | } 9 | 10 | export const LegendItem: React.FC = ({ 11 | label, 12 | color, 13 | onToggle, 14 | onClick, 15 | ...rest 16 | }) => { 17 | const [isActive, setIsActive] = useState(true); 18 | 19 | const handleClick = () => { 20 | if (onToggle) { 21 | onToggle(label, !isActive, color); 22 | setIsActive(!isActive); 23 | } 24 | }; 25 | 26 | return ( 27 |
33 |
40 | {label} 41 |
42 | ); 43 | }; 44 | -------------------------------------------------------------------------------- /packages/ez-react/src/components/addons/legend/LegendProvider.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment } from '@/lib/Fragment'; 2 | import React, { FC } from 'react'; 3 | 4 | export interface LegendProviderProps { 5 | Legend?: JSX.Element; 6 | children: React.ReactNode; 7 | isWrapped?: boolean; 8 | } 9 | 10 | export const LegendProvider: FC = ({ 11 | Legend, 12 | children, 13 | isWrapped = true, 14 | }) => { 15 | return isWrapped ? ( 16 | 17 | {children} 18 | {Legend} 19 | 20 | ) : ( 21 | <> 22 | {children} 23 | {Legend} 24 | 25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /packages/ez-react/src/components/addons/tooltip/TooltipContext.ts: -------------------------------------------------------------------------------- 1 | import { defaultTooltipContext } from 'eazychart-core/src'; 2 | import { createContext } from 'react'; 3 | 4 | export const TooltipContext = createContext(defaultTooltipContext); 5 | -------------------------------------------------------------------------------- /packages/ez-react/src/components/addons/tooltip/use-tooltip.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import { TooltipContext } from './TooltipContext'; 3 | 4 | export const useTooltip = () => { 5 | return useContext(TooltipContext); 6 | }; 7 | -------------------------------------------------------------------------------- /packages/ez-react/src/components/scales/LinearScale.tsx: -------------------------------------------------------------------------------- 1 | import React, { 2 | createContext, 3 | FC, 4 | useContext, 5 | useEffect, 6 | useMemo, 7 | } from 'react'; 8 | import { useChart } from '@/lib/use-chart'; 9 | import { ScaleLinear } from 'eazychart-core/src'; 10 | import { ScaleLinearDefinition } from 'eazychart-core/src/types'; 11 | import { Fragment } from '@/lib/Fragment'; 12 | 13 | const LinearScaleContext = createContext<{ 14 | linearScale: ScaleLinear; 15 | }>({ 16 | linearScale: new ScaleLinear(), 17 | }); 18 | 19 | export const useLinearScale = () => { 20 | return useContext(LinearScaleContext); 21 | }; 22 | 23 | export type LinearScaleProps = ScaleLinearDefinition & { 24 | children: React.ReactNode; 25 | isWrapped?: boolean; 26 | }; 27 | 28 | export const LinearScale: FC = ({ 29 | children, 30 | isWrapped = true, 31 | ...definition 32 | }) => { 33 | const { data, dimensions } = useChart(); 34 | 35 | const linearScale = useMemo(() => { 36 | const scale = new ScaleLinear(definition); 37 | scale.computeScale(dimensions, data); 38 | return scale; 39 | // eslint-disable-next-line react-hooks/exhaustive-deps 40 | }, [definition]); 41 | 42 | useEffect(() => { 43 | linearScale.computeScale(dimensions, data); 44 | }, [dimensions, data, linearScale]); 45 | 46 | return ( 47 | 48 | {isWrapped ? ( 49 | 50 | {children} 51 | 52 | ) : ( 53 | children 54 | )} 55 | 56 | ); 57 | }; 58 | -------------------------------------------------------------------------------- /packages/ez-react/src/components/scales/SqrtScale.tsx: -------------------------------------------------------------------------------- 1 | import React, { 2 | createContext, 3 | FC, 4 | useContext, 5 | useEffect, 6 | useMemo, 7 | } from 'react'; 8 | import { useChart } from '@/lib/use-chart'; 9 | import { ScaleSqrt } from 'eazychart-core/src'; 10 | import { ScaleSqrtDefinition } from 'eazychart-core/src/types'; 11 | import { Fragment } from '@/lib/Fragment'; 12 | 13 | const SqrtScaleContext = createContext<{ 14 | sqrtScale: ScaleSqrt; 15 | }>({ 16 | sqrtScale: new ScaleSqrt(), 17 | }); 18 | 19 | export const useSqrtScale = () => { 20 | return useContext(SqrtScaleContext); 21 | }; 22 | 23 | export type SqrtScaleProps = ScaleSqrtDefinition & { 24 | children: React.ReactNode; 25 | isWrapped?: boolean; 26 | }; 27 | 28 | export const SqrtScale: FC = ({ 29 | children, 30 | isWrapped = true, 31 | ...definition 32 | }) => { 33 | const { data, dimensions } = useChart(); 34 | 35 | const sqrtScale = useMemo(() => { 36 | const scale = new ScaleSqrt(definition); 37 | scale.computeScale(dimensions, data); 38 | return scale; 39 | // eslint-disable-next-line react-hooks/exhaustive-deps 40 | }, [definition]); 41 | 42 | useEffect(() => { 43 | sqrtScale.computeScale(dimensions, data); 44 | }, [dimensions, data, sqrtScale]); 45 | 46 | return ( 47 | 48 | {isWrapped ? ( 49 | 50 | {children} 51 | 52 | ) : ( 53 | children 54 | )} 55 | 56 | ); 57 | }; 58 | -------------------------------------------------------------------------------- /packages/ez-react/src/components/scales/grid/Grid.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, SVGAttributes } from 'react'; 2 | import { Direction, GridConfig } from 'eazychart-core/src/types'; 3 | import { GridLines } from '@/components/scales/grid/GridLines'; 4 | 5 | export interface GridProps 6 | extends Omit, 'directions'>, 7 | GridConfig {} 8 | 9 | export const Grid: FC = ({ 10 | directions = [Direction.HORIZONTAL, Direction.VERTICAL], 11 | color = '#a8a8a8', 12 | ...rest 13 | }) => { 14 | return Array.isArray(directions) && directions.length > 0 ? ( 15 | 16 | {directions.includes(Direction.HORIZONTAL) && ( 17 | 18 | )} 19 | {directions.includes(Direction.VERTICAL) && ( 20 | 21 | )} 22 | 23 | ) : null; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/ez-react/src/components/shapes/AreaPath.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, SVGAttributes, useMemo } from 'react'; 2 | import { AreaCurve, AreaData } from 'eazychart-core/src/types'; 3 | import { defaultColor, generateAreaPath } from 'eazychart-core/src'; 4 | import { useAnimation } from '../../lib/use-animation'; 5 | import { useChart } from '@/lib/use-chart'; 6 | 7 | export interface AreaPathProps extends SVGAttributes { 8 | shapeData?: AreaData; 9 | curve?: AreaCurve; 10 | beta?: number; 11 | stroke?: string; 12 | strokeWidth?: number; 13 | } 14 | 15 | export const AreaPath: FC = ({ 16 | shapeData = [], 17 | curve = 'curveLinear', 18 | beta, 19 | stroke = '#1f77b4', 20 | strokeWidth = 0, 21 | fill = defaultColor, 22 | opacity = 1, 23 | ...rest 24 | }) => { 25 | const { animationOptions } = useChart(); 26 | const dataPath = useMemo( 27 | () => generateAreaPath(shapeData, curve, beta), 28 | [shapeData, curve, beta] 29 | ); 30 | const currentData = 31 | useAnimation(dataPath, '', animationOptions, [curve]) || ''; 32 | return ( 33 | 44 | ); 45 | }; 46 | -------------------------------------------------------------------------------- /packages/ez-react/src/components/shapes/Bubble.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { defaultPointDatum } from 'eazychart-core/src'; 3 | import { Point, PointProps } from './Point'; 4 | 5 | export interface BubbleProps extends PointProps { 6 | fill?: string; 7 | } 8 | 9 | export const Bubble: FC = ({ 10 | shapeDatum = defaultPointDatum, 11 | fill, 12 | ...rest 13 | }) => { 14 | return ( 15 | 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /packages/ez-react/src/components/shapes/LinePath.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, SVGAttributes, useMemo } from 'react'; 2 | import { LineData, LineCurve } from 'eazychart-core/src/types'; 3 | import { defaultColor, generateLinePath } from 'eazychart-core/src'; 4 | import { useAnimation } from '@/lib/use-animation'; 5 | import { useChart } from '@/lib/use-chart'; 6 | 7 | export interface LinePathProps extends SVGAttributes { 8 | shapeData?: LineData; 9 | curve?: LineCurve; 10 | beta?: number; 11 | } 12 | 13 | export const LinePath: FC = ({ 14 | shapeData = [], 15 | curve = 'curveLinear', 16 | beta, 17 | stroke = defaultColor, 18 | strokeWidth = 1, 19 | ...rest 20 | }) => { 21 | const { animationOptions } = useChart(); 22 | const dataPath = useMemo( 23 | () => generateLinePath(shapeData, curve, beta), 24 | [shapeData, curve, beta] 25 | ); 26 | const currentData = 27 | useAnimation(dataPath, '', animationOptions, [curve]) || ''; 28 | return ( 29 | 39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /packages/ez-react/src/docs/2_installation.stories.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from '@storybook/addon-docs'; 2 | import Tabs from './Tabs'; 3 | 4 | 8 | 9 | # Installation 10 | 11 | First things first, you need to install the **EazyChart** library for your project. EazyChart is available in both **React** and **Vue** web apps. So open up your terminal of choice, navigate to your project directory and use one of the commands below to install the library : 12 | 13 | If you are using npm or yarn for your project, use one of the command line below depending on the project: 14 | 15 | 16 | 21 | 26 | 27 | 28 | Great, now you installed **EazyChart**, you are ready for the next step where you can add your first chart 29 | -------------------------------------------------------------------------------- /packages/ez-react/src/docs/4_2_responsiveCharts.stories.mdx: -------------------------------------------------------------------------------- 1 | import { Meta } from '@storybook/addon-docs'; 2 | import Tabs from './Tabs'; 3 | 4 | 8 | 9 | # Responsive Charts 10 | 11 | All chart recipes in **EazyChart** come with fixed width, 12 | by default the chart comes with a `800x600` dimensions which can be overwritten if you wish to have certain chart dimensions. 13 | 14 | Although, the chart recipes can have a responsive behavior. 15 | To do so, wrap your chart recipe with the `ResponsiveChartContainer`. 16 | This container will let your chart have the dimensions of the outer container where you are putting it. 17 | 18 | For React Web app: 19 | 20 | 24 | { 31 | // your component logic 32 | return ( 33 | 34 | // your chart recipe 35 | 36 | ); 37 | };`} 38 | > 39 | 40 | 45 | 46 | # your chart recipe 47 | 48 | \n 49 | `}> 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /packages/ez-react/src/docs/TabTitle.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './styles.css'; 3 | 4 | type Props = { 5 | title: string; 6 | index: number; 7 | selectedTab: number; 8 | setSelectedTab: (index: number) => void; 9 | }; 10 | 11 | const TabTitle: React.FC = ({ 12 | title, 13 | setSelectedTab, 14 | index, 15 | selectedTab, 16 | }) => { 17 | return ( 18 |
  • 19 | 29 |
  • 30 | ); 31 | }; 32 | 33 | export default TabTitle; 34 | -------------------------------------------------------------------------------- /packages/ez-react/src/lib/Fragment.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | 3 | export interface FragmentProps { 4 | type: string; 5 | name: string; 6 | children: React.ReactNode; 7 | } 8 | 9 | export const Fragment: FC = ({ type, name, children }) => { 10 | return React.createElement( 11 | type, 12 | { 13 | className: `ez-fragment-${name}`, 14 | }, 15 | children 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /packages/ez-react/src/lib/storybook-utils.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import { Story } from '@storybook/react/dist/ts3.9/client/index'; 3 | import { unFlattenArgs, ArgsType } from 'eazychart-dev/storybook/utils'; 4 | 5 | export const ChartWrapper: FC<{ children: React.ReactNode; style?: any }> = ({ 6 | children, 7 | ...props 8 | }) => { 9 | return
    {children}
    ; 10 | }; 11 | 12 | // Wraps the stories and passes the unflattened args to them 13 | export const buildTemplate = ( 14 | storyFn: Story 15 | ): ((args: T, context: any) => React.ReactElement) => { 16 | return (args: T, context: any) => { 17 | const compactedArgs: T = unFlattenArgs(args as ArgsType); 18 | return storyFn(compactedArgs, context); 19 | }; 20 | }; 21 | -------------------------------------------------------------------------------- /packages/ez-react/src/lib/use-chart.ts: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from 'react'; 2 | import { defaultChartContext } from 'eazychart-core/src'; 3 | 4 | export const ChartContext = createContext(defaultChartContext); 5 | 6 | export const useChart = () => { 7 | return useContext(ChartContext); 8 | }; 9 | -------------------------------------------------------------------------------- /packages/ez-react/src/lib/use-map.ts: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from 'react'; 2 | import { defaultMapContext } from 'eazychart-core/src'; 3 | 4 | export const MapContext = createContext(defaultMapContext); 5 | 6 | export const useMap = () => { 7 | return useContext(MapContext); 8 | }; 9 | -------------------------------------------------------------------------------- /packages/ez-react/src/lib/use-responsive-chart.ts: -------------------------------------------------------------------------------- 1 | import { Dimensions } from 'eazychart-core/src/types'; 2 | import { createContext, useContext } from 'react'; 3 | 4 | export const ResponsiveChartContext = createContext<{ 5 | dimensions?: Dimensions; 6 | }>({ 7 | dimensions: undefined, 8 | }); 9 | 10 | export const useResponsiveChart = () => { 11 | return useContext(ResponsiveChartContext); 12 | }; 13 | -------------------------------------------------------------------------------- /packages/ez-react/src/lib/useToggableDatum.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useMemo, useState } from 'react'; 2 | import { RawData } from 'eazychart-core/src/types'; 3 | 4 | export const useToggableDatum = ( 5 | data: RawData, 6 | domainKey: string, 7 | colors: string[] 8 | ) => { 9 | const [excludedKeys, setExcludedKeys] = useState<{ [key: string]: string }>( 10 | {} 11 | ); 12 | const activeData = useMemo( 13 | () => 14 | data.filter((datum) => { 15 | return !((datum[domainKey] as string) in excludedKeys); 16 | }), 17 | [excludedKeys, data, domainKey] 18 | ); 19 | const activeColors = useMemo( 20 | () => 21 | colors.filter((color) => { 22 | return !Object.values(excludedKeys).includes(color); 23 | }), 24 | [colors, excludedKeys] 25 | ); 26 | 27 | const toggleDatum = useCallback( 28 | (key: string, isActive: boolean, color: string) => { 29 | if (isActive) { 30 | const { [key]: _removed, ...newExcludedKeys } = excludedKeys; 31 | setExcludedKeys(newExcludedKeys); 32 | } else { 33 | setExcludedKeys({ ...excludedKeys, [key]: color }); 34 | } 35 | }, 36 | [excludedKeys, setExcludedKeys] 37 | ); 38 | return { activeData, activeColors, toggleDatum }; 39 | }; 40 | -------------------------------------------------------------------------------- /packages/ez-react/src/lib/useToggableDomainKey.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useMemo, useState } from 'react'; 2 | import { RawData } from 'eazychart-core/src/types'; 3 | import { getDomainByKeys } from 'eazychart-core'; 4 | 5 | export const useToggableDomainKey = (data: RawData, domainKeys: string[]) => { 6 | // Setup a state for the domain keys to make them toggable 7 | const [activeDomainKeys, setActiveDomainKeys] = 8 | useState(domainKeys); 9 | 10 | // Toggle Y axis domain keys whenever a legend key is clicked 11 | const toggleDomainKey = useCallback( 12 | (key: string, isActive: boolean, _color: string) => { 13 | if (isActive) { 14 | setActiveDomainKeys([...activeDomainKeys, key]); 15 | } else { 16 | setActiveDomainKeys( 17 | activeDomainKeys.filter((domainKey) => domainKey !== key) 18 | ); 19 | } 20 | }, 21 | [activeDomainKeys, setActiveDomainKeys] 22 | ); 23 | 24 | // Re-scale the Y axis 25 | const activeDomain = useMemo( 26 | () => getDomainByKeys(activeDomainKeys, data), 27 | [activeDomainKeys, data] 28 | ); 29 | 30 | return { activeDomainKeys, activeDomain, toggleDomainKey }; 31 | }; 32 | -------------------------------------------------------------------------------- /packages/ez-react/src/recipes/pie/RadialChart.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Meta, Story } from '@storybook/react'; 3 | import { RadialChart, RadialChartProps } from '@/recipes/pie/RadialChart'; 4 | import { ChartWrapper, buildTemplate } from '@/lib/storybook-utils'; 5 | import { 6 | flattenArgs, 7 | BASE_CHART_ARG_TYPES, 8 | PIE_ARGTYPES, 9 | } from 'eazychart-dev/storybook/utils'; 10 | import { 11 | colors, 12 | rawData, 13 | animationOptions, 14 | padding, 15 | } from 'eazychart-dev/storybook/data'; 16 | import { DISABLED_DEFAULT_ARG } from 'eazychart-dev/storybook/storybook-configs'; 17 | 18 | const pieChartArgTypes = { 19 | ...BASE_CHART_ARG_TYPES, 20 | ...PIE_ARGTYPES, 21 | arc: DISABLED_DEFAULT_ARG, 22 | }; 23 | 24 | const meta: Meta = { 25 | id: '8', 26 | title: 'React/Pie Chart/Radial', 27 | component: RadialChart, 28 | parameters: { 29 | controls: { expanded: true }, 30 | }, 31 | argTypes: pieChartArgTypes, 32 | }; 33 | 34 | export default meta; 35 | 36 | const RadialTemplate: Story = buildTemplate( 37 | (args: RadialChartProps) => { 38 | return ( 39 | 40 | 41 | 42 | ); 43 | } 44 | ); 45 | 46 | // By passing using the Args format for exported stories, you can control the props for a component for reuse in a test 47 | // https://storybook.js.org/docs/react/workflows/unit-testing 48 | 49 | const defaultArguments = flattenArgs({ 50 | colors, 51 | valueDomainKey: 'value', 52 | labelDomainKey: 'name', 53 | dimensions: { width: 800, height: 600 }, 54 | animationOptions, 55 | padding, 56 | data: rawData, 57 | }); 58 | 59 | export const Radial = RadialTemplate.bind({}); 60 | 61 | Radial.args = defaultArguments; 62 | -------------------------------------------------------------------------------- /packages/ez-react/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | import '@testing-library/jest-dom/extend-expect'; 2 | -------------------------------------------------------------------------------- /packages/ez-react/tests/common.ts: -------------------------------------------------------------------------------- 1 | import { ChartProps } from '@/components/Chart'; 2 | import { RenderResult, RenderOptions, render } from '@testing-library/react'; 3 | 4 | export const baseChartProps: ChartProps = { 5 | rawData: [], 6 | animationOptions: { 7 | easing: 'easeLinear', 8 | duration: 0, 9 | delay: 0, 10 | }, 11 | padding: { 12 | top: 0, 13 | right: 0, 14 | bottom: 0, 15 | left: 0, 16 | }, 17 | scopedSlots: {}, 18 | isWrapped: false, 19 | }; 20 | 21 | export const renderSVG = ( 22 | ui: React.ReactElement, 23 | options?: Omit 24 | ): RenderResult => { 25 | const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 26 | return render(ui, { 27 | ...options, 28 | container: svg as Element as HTMLElement, 29 | }); 30 | }; 31 | -------------------------------------------------------------------------------- /packages/ez-react/tests/mocks/ResizeObserver.ts: -------------------------------------------------------------------------------- 1 | class ResizeObserver { 2 | observe() { 3 | // do nothing 4 | } 5 | 6 | unobserve() { 7 | // do nothing 8 | } 9 | 10 | disconnect() { 11 | // do nothing 12 | } 13 | } 14 | 15 | window.ResizeObserver = ResizeObserver; 16 | export default ResizeObserver; 17 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/components/Arcs.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { act, RenderResult, waitFor } from '@testing-library/react'; 3 | import { Chart } from '@/components/Chart'; 4 | import { 5 | colors, 6 | dimensions, 7 | radialLinearScaleDef, 8 | rawData, 9 | } from 'eazychart-core/src/sample-data'; 10 | import { baseChartProps, renderSVG } from 'tests/common'; 11 | import 'tests/mocks/ResizeObserver'; 12 | import { Arcs } from '@/components/Arcs'; 13 | import { LinearScale } from '@/components/scales/LinearScale'; 14 | import { ColorScale } from '@/components/scales/ColorScale'; 15 | 16 | describe('Arcs', () => { 17 | it('renders svg radial with the right coordinates / dimensions', async () => { 18 | let wrapper: RenderResult; 19 | act(() => { 20 | wrapper = renderSVG( 21 | <>{null}, 27 | TooltipComponent: () => <>{null}, 28 | }} 29 | > 30 | 31 | 32 | 33 | 34 | 35 | 36 | ); 37 | }); 38 | 39 | await waitFor(() => { 40 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/components/Area.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 3 | import { Chart } from '@/components/Chart'; 4 | import { 5 | dimensions, 6 | rawData, 7 | horizontalLinearScaleDef, 8 | verticalLinearScaleDef, 9 | } from 'eazychart-core/src/sample-data'; 10 | import { baseChartProps } from 'tests/common'; 11 | import 'tests/mocks/ResizeObserver'; 12 | import { Area } from '@/components/Area'; 13 | import { CartesianScale } from '@/components/scales/CartesianScale'; 14 | import { ScaleLinear } from 'eazychart-core/src'; 15 | 16 | describe('Area', () => { 17 | it('renders svg area with the right coordinates / dimensions', async () => { 18 | let wrapper: RenderResult; 19 | act(() => { 20 | wrapper = render( 21 | <>{null}, 27 | TooltipComponent: () => <>{null}, 28 | }} 29 | > 30 | 41 | 42 | 43 | 44 | ); 45 | }); 46 | 47 | await waitFor(() => { 48 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/components/Chart.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import { 4 | dimensions, 5 | padding, 6 | animationOptions, 7 | rawData, 8 | } from 'eazychart-core/src/sample-data'; 9 | import { Chart } from '@/components/Chart'; 10 | import 'tests/mocks/ResizeObserver'; 11 | import { Tooltip } from '@/components/addons/tooltip/Tooltip'; 12 | 13 | describe('Chart', () => { 14 | it('renders svg chart container with the right slots', async () => { 15 | const wrapper = render( 16 |
    Legend Slot
    , 23 | TooltipComponent: () =>
    Tooltip Slot
    , 24 | }} 25 | > 26 |
    Default Slot
    27 |
    28 | ); 29 | 30 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 31 | }); 32 | 33 | it('should provide the chart data to the children components', async () => { 34 | const wrapper = render( 35 | 42 |
    {JSON.stringify(rawData)}
    43 |
    44 | ); 45 | 46 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/components/IrregularArcs.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { act, RenderResult, waitFor } from '@testing-library/react'; 3 | import { 4 | dimensions, 5 | verticalLinearScaleDef, 6 | colors, 7 | rawData, 8 | } from 'eazychart-core/src/sample-data'; 9 | import { IrregularArcs } from '@/components/IrregularArcs'; 10 | import { Chart } from '@/components/Chart'; 11 | import { baseChartProps, renderSVG } from 'tests/common'; 12 | import 'tests/mocks/ResizeObserver'; 13 | import { LinearScale } from '@/components/scales/LinearScale'; 14 | import { ColorScale } from '@/components/scales/ColorScale'; 15 | 16 | describe('IrregularArcs', () => { 17 | it('renders svg irregular arcs with the right coordinates / dimensions', async () => { 18 | let wrapper: RenderResult; 19 | act(() => { 20 | wrapper = renderSVG( 21 | <>{null}, 27 | TooltipComponent: () => <>{null}, 28 | }} 29 | > 30 | 35 | 36 | 41 | 42 | 43 | 44 | ); 45 | }); 46 | 47 | await waitFor(() => { 48 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/components/Map.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 3 | import { 4 | dimensions, 5 | mapFeatureData, 6 | rawData, 7 | } from 'eazychart-core/src/sample-data'; 8 | // import 'tests/mocks/ResizeObserver'; 9 | import { Map } from '@/components/Map'; 10 | import { useChart } from '@/lib/use-chart'; 11 | 12 | jest.mock('@/lib/use-chart'); 13 | 14 | describe('Map', () => { 15 | beforeEach(() => { 16 | (useChart as jest.Mock).mockImplementation(() => ({ 17 | data: rawData, 18 | dimensions, 19 | animationOptions: { 20 | easing: 'easeLinear', 21 | duration: 0, 22 | delay: 0, 23 | }, 24 | })); 25 | }); 26 | afterAll(() => { 27 | jest.clearAllMocks(); 28 | }); 29 | 30 | it('renders svg areas using GeoJSON features', async () => { 31 | let wrapper: RenderResult; 32 | act(() => { 33 | wrapper = render( 34 | 44 | ); 45 | }); 46 | 47 | await waitFor(() => { 48 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/components/Pie.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { act, RenderResult, waitFor } from '@testing-library/react'; 3 | import { dimensions, colors, rawData } from 'eazychart-core/src/sample-data'; 4 | import { Pie } from '@/components/Pie'; 5 | import { Chart } from '@/components/Chart'; 6 | import { baseChartProps, renderSVG } from 'tests/common'; 7 | import 'tests/mocks/ResizeObserver'; 8 | import { ColorScale } from '@/components/scales/ColorScale'; 9 | 10 | describe('Pie', () => { 11 | it('renders svg pie with the right coordinates / dimensions', async () => { 12 | let wrapper: RenderResult; 13 | act(() => { 14 | wrapper = renderSVG( 15 | <>{null}, 21 | TooltipComponent: () => <>{null}, 22 | }} 23 | > 24 | 25 | 30 | 31 | 32 | ); 33 | }); 34 | 35 | await waitFor(() => { 36 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/components/shapes/AreaPath.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 3 | import { AreaPath } from '@/components/shapes/AreaPath'; 4 | import { Chart } from '@/components/Chart'; 5 | import { areaData } from 'eazychart-core/src/sample-data'; 6 | import { baseChartProps } from 'tests/common'; 7 | import 'tests/mocks/ResizeObserver'; 8 | 9 | describe('AreaPath', () => { 10 | it('renders an svg path with the right coordinates / path', async () => { 11 | let wrapper: RenderResult; 12 | await act(async () => { 13 | wrapper = render( 14 | <>{null}, 18 | TooltipComponent: () => <>{null}, 19 | }} 20 | > 21 | 22 | 23 | ); 24 | }); 25 | 26 | await waitFor(() => { 27 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/components/shapes/LinePath.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { act, RenderResult, waitFor } from '@testing-library/react'; 3 | import { LinePath } from '@/components/shapes/LinePath'; 4 | import { Chart } from '@/components/Chart'; 5 | import { pointsData } from 'eazychart-core/src/sample-data'; 6 | import { baseChartProps, renderSVG } from 'tests/common'; 7 | import 'tests/mocks/ResizeObserver'; 8 | 9 | describe('LinePath', () => { 10 | it('renders an svg path with the right coordinates / path', async () => { 11 | let wrapper: RenderResult; 12 | await act(async () => { 13 | wrapper = renderSVG( 14 | <>{null}, 18 | TooltipComponent: () => <>{null}, 19 | }} 20 | > 21 | 22 | 23 | ); 24 | }); 25 | 26 | await waitFor(() => { 27 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/recipes/area/AreaChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { dimensions, pointsRawData } from 'eazychart-core/src/sample-data'; 3 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 4 | import { AreaChart } from '@/recipes/area/AreaChart'; 5 | import 'tests/mocks/ResizeObserver'; 6 | 7 | describe('AreaChart', () => { 8 | it('renders an area chart', async () => { 9 | let wrapper: RenderResult; 10 | act(() => { 11 | wrapper = render( 12 | 34 | ); 35 | }); 36 | await waitFor(() => { 37 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/recipes/area/MultiAreaChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { dimensions, pointsRawData } from 'eazychart-core/src/sample-data'; 3 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 4 | import 'tests/mocks/ResizeObserver'; 5 | import { MultiAreaChart } from '@/recipes/area/MultiAreaChart'; 6 | 7 | describe('MultiAreaChart', () => { 8 | it('renders a multiarea chart', async () => { 9 | let wrapper: RenderResult; 10 | act(() => { 11 | wrapper = render( 12 | 36 | ); 37 | }); 38 | 39 | await waitFor(() => { 40 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/recipes/bar/BarChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Position } from 'eazychart-core/src/types'; 3 | import { colors, rawData, dimensions } from 'eazychart-core/src/sample-data'; 4 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 5 | import { BarChart } from '@/recipes/bar/BarChart'; 6 | import 'tests/mocks/ResizeObserver'; 7 | 8 | describe('BarChart', () => { 9 | it('renders a bar chart', async () => { 10 | let wrapper: RenderResult; 11 | act(() => { 12 | wrapper = render( 13 | 32 | ); 33 | }); 34 | 35 | await waitFor(() => { 36 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/recipes/column/ColumnChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 3 | import { colors, dimensions, rawData } from 'eazychart-core/src/sample-data'; 4 | import { Position } from 'eazychart-core/src/types'; 5 | import { ColumnChart } from '@/recipes/column/ColumnChart'; 6 | import 'tests/mocks/ResizeObserver'; 7 | 8 | describe('ColumnChart', () => { 9 | it('renders a column chart', async () => { 10 | let wrapper: RenderResult; 11 | act(() => { 12 | // 1st render 13 | wrapper = render( 14 | 33 | ); 34 | }); 35 | 36 | await waitFor(() => { 37 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/recipes/column/LineColumnChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Position } from 'eazychart-core/src/types'; 3 | import { colors, dimensions, rawData } from 'eazychart-core/src/sample-data'; 4 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 5 | import { LineColumnChart } from '@/recipes/column/LineColumnChart'; 6 | import 'tests/mocks/ResizeObserver'; 7 | 8 | describe('LineColumnChart', () => { 9 | it('renders a line & column chart', async () => { 10 | let wrapper: RenderResult; 11 | act(() => { 12 | wrapper = render( 13 | 36 | ); 37 | }); 38 | 39 | await waitFor(() => { 40 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/recipes/line/LineChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { dimensions, pointsRawData } from 'eazychart-core/src/sample-data'; 3 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 4 | import { LineChart } from '@/recipes/line/LineChart'; 5 | import 'tests/mocks/ResizeObserver'; 6 | 7 | describe('LineChart', () => { 8 | it('renders a line chart', async () => { 9 | let wrapper: RenderResult; 10 | act(() => { 11 | wrapper = render( 12 | 31 | ); 32 | }); 33 | 34 | await waitFor(() => { 35 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/recipes/line/LineErrorMarginChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | dimensions, 4 | pointsWithMarginData, 5 | } from 'eazychart-core/src/sample-data'; 6 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 7 | import { LineErrorMarginChart } from '@/recipes/line/LineErrorMarginChart'; 8 | import 'tests/mocks/ResizeObserver'; 9 | 10 | describe('LineErrorMarginChart', () => { 11 | it('renders a line error margin chart', async () => { 12 | let wrapper: RenderResult; 13 | act(() => { 14 | wrapper = render( 15 | 34 | ); 35 | }); 36 | 37 | await waitFor(() => { 38 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/recipes/line/MultiLineChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { dimensions, pointsRawData } from 'eazychart-core/src/sample-data'; 3 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 4 | import 'tests/mocks/ResizeObserver'; 5 | import { MultiLineChart } from '@/recipes/line/MultiLineChart'; 6 | 7 | describe('MultiLineChart', () => { 8 | it('renders a multiline chart', async () => { 9 | let wrapper: RenderResult; 10 | act(() => { 11 | wrapper = render( 12 | 34 | ); 35 | }); 36 | 37 | await waitFor(() => { 38 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/recipes/map/MapChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | colors, 4 | dimensions, 5 | mapFeatureData, 6 | rawData, 7 | } from 'eazychart-core/src/sample-data'; 8 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 9 | import { MapChart } from '@/recipes/map/MapChart'; 10 | import 'tests/mocks/ResizeObserver'; 11 | 12 | describe('MapChart', () => { 13 | it('renders a map chart', async () => { 14 | let wrapper: RenderResult; 15 | act(() => { 16 | wrapper = render( 17 | 35 | ); 36 | }); 37 | await waitFor(() => { 38 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/recipes/pie/IrregularPieChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { colors, dimensions, rawData } from 'eazychart-core/src/sample-data'; 3 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 4 | import { IrregularPieChart } from '@/recipes/pie/IrregularPieChart'; 5 | import 'tests/mocks/ResizeObserver'; 6 | 7 | describe('IrregularPieChart', () => { 8 | it('renders a irregular pie chart', async () => { 9 | let wrapper: RenderResult; 10 | act(() => { 11 | wrapper = render( 12 | 24 | ); 25 | }); 26 | 27 | await waitFor(() => { 28 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/recipes/pie/PieChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { colors, dimensions, rawData } from 'eazychart-core/src/sample-data'; 3 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 4 | import { PieChart } from '@/recipes/pie/PieChart'; 5 | import 'tests/mocks/ResizeObserver'; 6 | 7 | describe('PieChart', () => { 8 | it('renders a pie chart', async () => { 9 | let wrapper: RenderResult; 10 | act(() => { 11 | wrapper = render( 12 | 24 | ); 25 | }); 26 | 27 | await waitFor(() => { 28 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/recipes/pie/RadialChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { colors, dimensions, rawData } from 'eazychart-core/src/sample-data'; 3 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 4 | import { RadialChart } from '@/recipes/pie/RadialChart'; 5 | import 'tests/mocks/ResizeObserver'; 6 | 7 | describe('RadialChart', () => { 8 | it('renders a Radial chart', async () => { 9 | let wrapper: RenderResult; 10 | act(() => { 11 | wrapper = render( 12 | 24 | ); 25 | }); 26 | 27 | await waitFor(() => { 28 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/recipes/pie/SemiCircleChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { colors, dimensions, rawData } from 'eazychart-core/src/sample-data'; 3 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 4 | import { SemiCircleChart } from '@/recipes/pie/SemiCircleChart'; 5 | import 'eazychart-react/tests/mocks/ResizeObserver'; 6 | 7 | describe('SemiCircleChart', () => { 8 | it('renders a semi-circle chart', async () => { 9 | let wrapper: RenderResult; 10 | act(() => { 11 | wrapper = render( 12 | 24 | ); 25 | }); 26 | 27 | await waitFor(() => { 28 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/recipes/scatter/BubbleChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { dimensions, rawData } from 'eazychart-core/src/sample-data'; 3 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 4 | import { BubbleChart } from '@/recipes/scatter/BubbleChart'; 5 | import 'tests/mocks/ResizeObserver'; 6 | 7 | describe('BubbleChart', () => { 8 | it('renders a bubble chart', async () => { 9 | let wrapper: RenderResult; 10 | act(() => { 11 | wrapper = render( 12 | 34 | ); 35 | }); 36 | 37 | await waitFor(() => { 38 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /packages/ez-react/tests/unit/recipes/scatter/ScatterChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { dimensions, rawData } from 'eazychart-core/src/sample-data'; 3 | import { act, render, RenderResult, waitFor } from '@testing-library/react'; 4 | import { ScatterChart } from '@/recipes/scatter/ScatterChart'; 5 | import 'tests/mocks/ResizeObserver'; 6 | 7 | describe('ScatterChart', () => { 8 | it('renders a scatter chart', async () => { 9 | let wrapper: RenderResult; 10 | act(() => { 11 | wrapper = render( 12 | 33 | ); 34 | }); 35 | 36 | await waitFor(() => { 37 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /packages/ez-react/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "./", 5 | "declaration": true, 6 | "declarationDir": "./dist/types" 7 | }, 8 | "include": [ 9 | "src/**/*.ts", 10 | "src/**/*.tsx", 11 | ], 12 | "exclude": [ 13 | "node_modules", 14 | "tests" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /packages/ez-react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | // transpile JSX to React.createElement 5 | "jsx": "react", 6 | "baseUrl": "./", 7 | "paths": { 8 | "@/*": [ 9 | "src/*" 10 | ], 11 | "tests/*": [ 12 | "tests/*" 13 | ] 14 | }, 15 | }, 16 | "include": [ 17 | "src/**/*.ts", 18 | "src/**/*.tsx", 19 | "tests/**/*.ts", 20 | "tests/**/*.tsx" 21 | ], 22 | "exclude": [ 23 | "node_modules" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/ez-vue/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /packages/ez-vue/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | end_of_line = lf 5 | trim_trailing_whitespace = true 6 | insert_final_newline = true 7 | max_line_length = 100 8 | -------------------------------------------------------------------------------- /packages/ez-vue/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /storybook-static 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /packages/ez-vue/.storybook/main.js: -------------------------------------------------------------------------------- 1 | const vueConfig = require('@vue/cli-service/webpack.config.js'); 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | stories: [ 6 | '../src/**/*.stories.mdx', 7 | '../src/**/*.stories.@(js|jsx|ts|tsx)', 8 | ], 9 | addons: ['@storybook/addon-links', '@storybook/addon-essentials'], 10 | framework: '@storybook/vue', 11 | webpackFinal: async (config) => { 12 | // Add TS/TSX support 13 | const tsxRule = vueConfig.module.rules.find( 14 | ({ test }) => test.toString() === /\.tsx$/.toString(), 15 | ); 16 | const tsRule = vueConfig.module.rules.find( 17 | ({ test }) => test.toString() === /\.ts$/.toString(), 18 | ); 19 | config.module.rules.push(tsxRule); 20 | config.module.rules.push(tsRule); 21 | // Add eslint loader 22 | // const eslintRule = vueConfig.module.rules.find( 23 | // ({ test }) => test.toString() === /\.(vue|(j|t)sx?)$/.toString(), 24 | // ); 25 | // config.module.rules.push(eslintRule); 26 | // Resolve aliases 27 | config.resolve.alias = { 28 | ...config.resolve.alias, 29 | '@': path.resolve(__dirname, '../src'), 30 | }; 31 | // Disable symbolic links 32 | config.resolve.symlinks = false; 33 | return config; 34 | }, 35 | features: { 36 | // We enable this to build the stories.json file. 37 | // This is useful to be able to display vue storybook inside the react storybook. 38 | buildStoriesJson: true, 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /packages/ez-vue/.storybook/manager-head.html: -------------------------------------------------------------------------------- 1 | EazyChart - Vue.js Charts Library 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /packages/ez-vue/.storybook/manager.js: -------------------------------------------------------------------------------- 1 | import { addons } from '@storybook/addons'; 2 | import customTheme from './theme'; 3 | 4 | addons.setConfig({ 5 | theme: customTheme, 6 | }); 7 | -------------------------------------------------------------------------------- /packages/ez-vue/.storybook/preview.js: -------------------------------------------------------------------------------- 1 | import 'eazychart-css/css/style.css'; 2 | 3 | export const parameters = { 4 | actions: { argTypesRegex: "^on[A-Z].*" }, 5 | controls: { 6 | matchers: { 7 | color: /(background|color)$/i, 8 | date: /Date$/, 9 | }, 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /packages/ez-vue/.storybook/theme.js: -------------------------------------------------------------------------------- 1 | import { create } from '@storybook/theming'; 2 | 3 | export default create({ 4 | base: 'light', 5 | brandTitle: 'EazyChart', 6 | brandUrl: 'https://eazychart.com', 7 | brandImage: 'https://docs.eazychart.com/images/logo_350x150.png', 8 | brandTarget: '_self', 9 | }); 10 | -------------------------------------------------------------------------------- /packages/ez-vue/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset', 4 | ], 5 | env: { 6 | test: { 7 | plugins: ['@babel/plugin-transform-modules-commonjs'], 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/ez-vue/index.ts: -------------------------------------------------------------------------------- 1 | import BarChart from './src/recipes/bar/BarChart'; 2 | import PieChart from './src/recipes/pie/PieChart'; 3 | import IrregularPieChart from './src/recipes/pie/IrregularPieChart'; 4 | import RadialChart from './src/recipes/pie/RadialChart'; 5 | import SemiCircleChart from './src/recipes/pie/SemiCircleChart'; 6 | import LineChart from './src/recipes/line/LineChart'; 7 | import LineErrorMarginChart from './src/recipes/line/LineErrorMarginChart'; 8 | import BubbleChart from './src/recipes/scatter/BubbleChart'; 9 | import ScatterChart from './src/recipes/scatter/ScatterChart'; 10 | import AreaChart from './src/recipes/area/AreaChart'; 11 | import ColumnChart from './src/recipes/column/ColumnChart'; 12 | import LineColumnChart from './src/recipes/column/LineColumnChart'; 13 | import Legend from './src/components/addons/legend/Legend'; 14 | import Tooltip from './src/components/addons/tooltip/Tooltip'; 15 | import MultiLineChart from './src/recipes/line/MultiLineChart'; 16 | import MapChart from './src/recipes/map/MapChart'; 17 | import BubbleMapChart from './src/recipes/map/BubbleMapChart'; 18 | import ResponsiveChartContainer from './src/components/ResponsiveChartContainer'; 19 | 20 | export { 21 | BarChart, 22 | PieChart, 23 | IrregularPieChart, 24 | RadialChart, 25 | SemiCircleChart, 26 | LineChart, 27 | LineErrorMarginChart, 28 | BubbleChart, 29 | ScatterChart, 30 | AreaChart, 31 | ColumnChart, 32 | LineColumnChart, 33 | Legend, 34 | Tooltip, 35 | MultiLineChart, 36 | MapChart, 37 | BubbleMapChart, 38 | ResponsiveChartContainer, 39 | }; 40 | -------------------------------------------------------------------------------- /packages/ez-vue/jest.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('eazychart-dev/jest/base.config'); 2 | 3 | const esModules = [ 4 | 'd3-scale', 5 | 'd3-array', 6 | 'internmap', 7 | 'd3-interpolate', 8 | 'd3-color', 9 | 'd3-format', 10 | 'd3-time', 11 | 'd3-shape', 12 | 'd3-path', 13 | 'd3-ease', 14 | 'd3-geo', 15 | ].join('|'); 16 | 17 | module.exports = { 18 | ...baseConfig, 19 | preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel', 20 | moduleNameMapper: { 21 | 'tests/(.*)': '/tests/$1', 22 | }, 23 | transformIgnorePatterns: [`/node_modules/(?!${esModules})`], 24 | }; 25 | -------------------------------------------------------------------------------- /packages/ez-vue/src/components/Bars.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Component from 'vue-class-component'; 3 | import { ChartContext, ScaleLinearOrBand } from 'eazychart-core/src/types'; 4 | import { InjectReactive, Prop } from 'vue-property-decorator'; 5 | import { ScaleOrdinal, scaleRectangleData } from 'eazychart-core/src'; 6 | import Bar from '@/components/shapes/Bar'; 7 | 8 | @Component({ components: { Bar } }) 9 | export default class Bars extends Vue { 10 | @InjectReactive('chart') 11 | private chart!: ChartContext; 12 | 13 | @InjectReactive('cartesianScale') 14 | private cartesianScale!: { xScale: ScaleLinearOrBand, yScale: ScaleLinearOrBand }; 15 | 16 | @InjectReactive('colorScale') 17 | private colorScale!: ScaleOrdinal; 18 | 19 | @Prop({ 20 | type: String, 21 | required: true, 22 | }) 23 | private readonly xDomainKey!: string; 24 | 25 | @Prop({ 26 | type: String, 27 | required: true, 28 | }) 29 | private readonly yDomainKey!: string; 30 | 31 | get shapeData() { 32 | return scaleRectangleData( 33 | this.chart.data, 34 | this.xDomainKey, 35 | this.yDomainKey, 36 | this.cartesianScale.xScale, 37 | this.cartesianScale.yScale, 38 | this.colorScale, 39 | this.chart.dimensions, 40 | this.chart.isRTL, 41 | ); 42 | } 43 | 44 | render() { 45 | const { shapeData } = this; 46 | return ( 47 | 48 | {shapeData.map((rectDatum) => ( 49 | 50 | ))} 51 | 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /packages/ez-vue/src/components/addons/legend/Legend.tsx: -------------------------------------------------------------------------------- 1 | import Vue, { PropType } from 'vue'; 2 | import Component from 'vue-class-component'; 3 | import { ChartContext } from 'eazychart-core/src/types'; 4 | import { InjectReactive, Prop, Watch } from 'vue-property-decorator'; 5 | import LegendItem from './LegendItem'; 6 | 7 | @Component 8 | export default class Legend extends Vue { 9 | @InjectReactive('chart') 10 | private chart!: ChartContext; 11 | 12 | @Prop({ 13 | type: Function as PropType< 14 | (key: string, isActive: boolean, color: string) => void 15 | >, 16 | required: false, 17 | }) 18 | private readonly onToggle!: ( 19 | key: string, 20 | isActive: boolean, 21 | color: string, 22 | ) => void; 23 | 24 | private keyDict: { [key: string]: string } = {}; 25 | 26 | @Watch('colorScale') 27 | updateColorMap() { 28 | this.computeKeyColorMap(); 29 | } 30 | 31 | mounted() { 32 | this.computeKeyColorMap(); 33 | } 34 | 35 | computeKeyColorMap() { 36 | if (this.colorScale) { 37 | this.keyDict = this.colorScale.scale 38 | .domain() 39 | .reduce((map, domainKey) => ({ 40 | ...map, 41 | [domainKey]: this.colorScale?.scale(domainKey), 42 | }), {}); 43 | } 44 | } 45 | 46 | get colorScale() { 47 | return this.chart.getScale('colorScale'); 48 | } 49 | 50 | render() { 51 | const { keyDict, onToggle } = this; 52 | return ( 53 |
    54 | {Object.entries(keyDict).map(([key, color]) => ( 55 | 61 | ))} 62 |
    63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/ez-vue/src/components/addons/legend/LegendItem.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Component from 'vue-class-component'; 3 | import { Prop } from 'vue-property-decorator'; 4 | 5 | export type LegendToggleHandler = (key: string, isActive: boolean, color: string) => void; 6 | 7 | @Component 8 | export default class LegendItem extends Vue { 9 | @Prop({ 10 | type: String, 11 | required: true, 12 | }) 13 | private readonly label!: string; 14 | 15 | @Prop({ 16 | type: String, 17 | required: true, 18 | }) 19 | private readonly color!: string; 20 | 21 | private isActive = true; 22 | 23 | handleClick() { 24 | this.isActive = !this.isActive; 25 | this.$emit('toggle', this.label, this.isActive, this.color); 26 | } 27 | 28 | render() { 29 | const { 30 | label, color, isActive, handleClick, 31 | } = this; 32 | return ( 33 |
    38 |
    45 | {label} 46 |
    47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/ez-vue/src/components/addons/legend/LegendProvider.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Component from 'vue-class-component'; 3 | import Fragment from '@/lib/Fragment'; 4 | 5 | @Component 6 | export default class LegendProvider extends Vue { 7 | render() { 8 | const DefaultSlot = this.$scopedSlots.default 9 | ? this.$scopedSlots.default({}) 10 | : null; 11 | const LegendOverride = this.$scopedSlots.Legend 12 | ? this.$scopedSlots.Legend({}) 13 | : null; 14 | return ( 15 | 16 | {DefaultSlot} 17 | {LegendOverride} 18 | 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/ez-vue/src/components/scales/LinearScale.tsx: -------------------------------------------------------------------------------- 1 | import Vue, { PropType } from 'vue'; 2 | import Component from 'vue-class-component'; 3 | import { 4 | InjectReactive, 5 | Prop, 6 | ProvideReactive, 7 | Watch, 8 | } from 'vue-property-decorator'; 9 | import { ChartContext, ScaleLinearDefinition } from 'eazychart-core/src/types'; 10 | import { ScaleLinear } from 'eazychart-core/src'; 11 | import Fragment from '@/lib/Fragment'; 12 | 13 | @Component 14 | export default class LinearScale extends Vue { 15 | @InjectReactive('chart') 16 | private chart!: ChartContext; 17 | 18 | @ProvideReactive('linearScale') 19 | private linearScale = new ScaleLinear(); 20 | 21 | @Prop({ 22 | type: Object as PropType, 23 | required: true, 24 | }) 25 | private readonly definition!: ScaleLinearDefinition; 26 | 27 | mounted() { 28 | this.defineScale(); 29 | } 30 | 31 | @Watch('chart.dimensions') 32 | @Watch('chart.data') 33 | recomputeScale() { 34 | const { dimensions, data } = this.chart; 35 | this.linearScale.computeScale(dimensions, data); 36 | } 37 | 38 | @Watch('chart.definition') 39 | defineScale() { 40 | const { definition } = this; 41 | const { dimensions, data } = this.chart; 42 | this.linearScale = new ScaleLinear(definition); 43 | this.linearScale.computeScale(dimensions, data); 44 | } 45 | 46 | render() { 47 | const DefaultSlot = this.$scopedSlots.default 48 | ? this.$scopedSlots.default({}) 49 | : null; 50 | return ( 51 | 52 | {DefaultSlot} 53 | 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/ez-vue/src/components/scales/SqrtScale.tsx: -------------------------------------------------------------------------------- 1 | import Vue, { PropType } from 'vue'; 2 | import Component from 'vue-class-component'; 3 | import { 4 | InjectReactive, 5 | Prop, 6 | ProvideReactive, 7 | Watch, 8 | } from 'vue-property-decorator'; 9 | import { ChartContext, ScaleSqrtDefinition } from 'eazychart-core/src/types'; 10 | import { ScaleSqrt } from 'eazychart-core/src'; 11 | import Fragment from '@/lib/Fragment'; 12 | 13 | @Component 14 | export default class SqrtScale extends Vue { 15 | @InjectReactive('chart') 16 | private chart!: ChartContext; 17 | 18 | @ProvideReactive('sqrtScale') 19 | private sqrtScale = new ScaleSqrt(); 20 | 21 | @Prop({ 22 | type: Object as PropType, 23 | required: true, 24 | }) 25 | private readonly definition!: ScaleSqrtDefinition; 26 | 27 | mounted() { 28 | this.defineScale(); 29 | } 30 | 31 | @Watch('chart.dimensions') 32 | @Watch('chart.data') 33 | recomputeScale() { 34 | const { dimensions, data } = this.chart; 35 | this.sqrtScale.computeScale(dimensions, data); 36 | } 37 | 38 | @Watch('chart.definition') 39 | defineScale() { 40 | const { definition } = this; 41 | const { dimensions, data } = this.chart; 42 | this.sqrtScale = new ScaleSqrt(definition); 43 | this.sqrtScale.computeScale(dimensions, data); 44 | } 45 | 46 | render() { 47 | const DefaultSlot = this.$scopedSlots.default 48 | ? this.$scopedSlots.default({}) 49 | : null; 50 | return ( 51 | 52 | {DefaultSlot} 53 | 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/ez-vue/src/components/scales/grid/Grid.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Component from 'vue-class-component'; 3 | import { Prop } from 'vue-property-decorator'; 4 | import { Direction } from 'eazychart-core/src/types'; 5 | import GridLines from './GridLines'; 6 | 7 | @Component 8 | export default class Grid extends Vue { 9 | @Prop({ 10 | type: Array, 11 | default() { 12 | return [Direction.HORIZONTAL, Direction.VERTICAL]; 13 | }, 14 | }) 15 | private readonly directions!: Direction[]; 16 | 17 | @Prop({ 18 | type: String, 19 | default: '#a8a8a8', 20 | }) 21 | private readonly color!: string; 22 | 23 | render() { 24 | const { directions, color } = this; 25 | 26 | if (!directions || directions.length === 0) { 27 | return null; 28 | } 29 | 30 | return ( 31 | 32 | {directions.includes(Direction.HORIZONTAL) && ( 33 | 34 | )} 35 | {directions.includes(Direction.VERTICAL) && ( 36 | 37 | )} 38 | 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/ez-vue/src/components/shapes/Bubble.tsx: -------------------------------------------------------------------------------- 1 | import Vue, { PropType } from 'vue'; 2 | import Component from 'vue-class-component'; 3 | import { PointDatum } from 'eazychart-core/src/types'; 4 | import { defaultBubbleDatum } from 'eazychart-core/src'; 5 | import { Prop } from 'vue-property-decorator'; 6 | import Point from './Point'; 7 | 8 | @Component 9 | export default class Bubble extends Vue { 10 | @Prop({ 11 | type: Object as PropType, 12 | default() { 13 | return defaultBubbleDatum; 14 | }, 15 | }) 16 | private readonly shapeDatum!: PointDatum; 17 | 18 | @Prop({ 19 | type: String, 20 | default: null, 21 | }) 22 | private readonly fill!: string; 23 | 24 | render() { 25 | const { 26 | shapeDatum, 27 | fill, 28 | } = this; 29 | return ( 30 | 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/ez-vue/src/lib/AnimationMixin.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Component from 'vue-class-component'; 3 | import { animate } from 'eazychart-core/src'; 4 | import { AnimationOptions, Interpolables } from 'eazychart-core/src/types'; 5 | 6 | type AnimationArguments = { 7 | from: Interpolables; 8 | to: Interpolables; 9 | options: AnimationOptions | undefined; 10 | // eslint-disable-next-line 11 | onUpdate: (v: any) => void; 12 | dependencies: string[]; 13 | }; 14 | 15 | @Component 16 | export default class AnimationMixin extends Vue { 17 | public cancelAnimation: Function | null = null; 18 | 19 | // @ts-ignore 20 | private mounted() { 21 | this.animate(); 22 | this.animationArguments.dependencies.forEach((dep) => { 23 | this.$watch(dep, this.animate, { deep: true }); 24 | }); 25 | } 26 | 27 | animate() { 28 | const { 29 | from, 30 | to, 31 | options, 32 | onUpdate, 33 | } = this.animationArguments; 34 | this.cancelAnimation && this.cancelAnimation(); 35 | this.cancelAnimation = animate(from, to, options, onUpdate); 36 | } 37 | 38 | get animationArguments(): AnimationArguments { 39 | throw new Error( 40 | 'The component using the mixin should implement the animationArguments() getter', 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/ez-vue/src/lib/Fragment.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Component from 'vue-class-component'; 3 | import { Prop } from 'vue-property-decorator'; 4 | 5 | @Component 6 | export default class Fragment extends Vue { 7 | @Prop({ 8 | type: String, 9 | required: true, 10 | }) 11 | private readonly type!: string; 12 | 13 | @Prop({ 14 | type: String, 15 | required: true, 16 | }) 17 | private readonly name!: string; 18 | 19 | render(h: Function) { 20 | const { type, name } = this; 21 | const DefaultSlot = this.$scopedSlots.default 22 | ? this.$scopedSlots.default({}) 23 | : null; 24 | return h(type, { attrs: { class: `ez-fragment-${name}` } }, DefaultSlot); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/ez-vue/src/lib/ToggleDatumMixin.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Component from 'vue-class-component'; 3 | import { RawData } from 'eazychart-core/src/types'; 4 | 5 | @Component 6 | export default class ToggleDatumMixin extends Vue { 7 | private excludedKeys: { [key: string]: string } = {}; 8 | 9 | get activeData() { 10 | const domainKey = this.getDomainKey(); 11 | return this.getData().filter((datum) => !((datum[domainKey] as string) in this.excludedKeys)); 12 | } 13 | 14 | get activeColors() { 15 | return this.getColors().filter((color) => !Object.values(this.excludedKeys).includes(color)); 16 | } 17 | 18 | toggleDatum(key: string, isActive: boolean, color: string) { 19 | if (isActive) { 20 | // eslint-disable-next-line 21 | const { [key]: _removed, ...newExcludedKeys } = this.excludedKeys; 22 | this.excludedKeys = newExcludedKeys; 23 | } else { 24 | this.excludedKeys = { ...this.excludedKeys, [key]: color }; 25 | } 26 | } 27 | 28 | getData(): RawData { 29 | throw new Error( 30 | 'The component using the mixin should implement the getData() getter', 31 | ); 32 | } 33 | 34 | getColors(): string[] { 35 | throw new Error( 36 | 'The component using the mixin should implement the getColors() getter', 37 | ); 38 | } 39 | 40 | getDomainKey(): string { 41 | throw new Error( 42 | 'The component using the mixin should implement the getDomainKey() getter', 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/ez-vue/src/lib/ToggleDomainKeyMixin.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Component from 'vue-class-component'; 3 | import { RawData } from 'eazychart-core/src/types'; 4 | import { getDomainByKeys } from 'eazychart-core/src'; 5 | 6 | @Component 7 | export default class ToggleDomainKeyMixin extends Vue { 8 | public activeDomainKeys: string[] = []; 9 | 10 | get activeDomain() { 11 | return getDomainByKeys(this.activeDomainKeys, this.getData()); 12 | } 13 | 14 | toggleDomainKey(key: string, isActive: boolean, _color: string) { 15 | if (isActive) { 16 | this.activeDomainKeys = [...this.activeDomainKeys, key]; 17 | } else { 18 | this.activeDomainKeys = this.activeDomainKeys.filter((domainKey) => domainKey !== key); 19 | } 20 | } 21 | 22 | mounted() { 23 | this.activeDomainKeys = this.getDomainKeys(); 24 | } 25 | 26 | getData(): RawData { 27 | throw new Error( 28 | 'The component using the mixin should implement the getData() getter', 29 | ); 30 | } 31 | 32 | getDomainKeys(): string[] { 33 | throw new Error( 34 | 'The component using the mixin should implement the getDomainKeys() getter', 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/ez-vue/src/lib/storybook-utils.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import Component from 'vue-class-component'; 3 | import Vue from 'vue'; 4 | import { unFlattenArgs, ArgsType } from 'eazychart-dev/storybook/utils'; 5 | // eslint-disable-next-line import/no-unresolved 6 | import { Story } from '@storybook/vue/dist/ts3.9/client/index'; 7 | 8 | @Component 9 | export class ChartWrapper extends Vue { 10 | render(h: Function) { 11 | return h('div', {}, this.$slots.default); 12 | } 13 | } 14 | 15 | @Component 16 | export class ResizableChartWrapper extends Vue { 17 | render(h: Function) { 18 | return h( 19 | 'div', 20 | { 21 | style: { 22 | width: '100%', 23 | height: '100vh', 24 | border: '2px solid #ccc', 25 | resize: 'auto', 26 | overflow: 'scroll', 27 | }, 28 | }, 29 | this.$slots.default, 30 | ); 31 | } 32 | } 33 | 34 | // Wraps the stories and passes the unflattened args to them 35 | export const buildTemplate = ( 36 | storyFn: Story, 37 | ): ((args: T, context: any) => string | Vue.Component) => (args: T, context: any) => { 38 | const compactedArgs = unFlattenArgs(args as ArgsType); 39 | return storyFn(compactedArgs, context); 40 | }; 41 | -------------------------------------------------------------------------------- /packages/ez-vue/src/recipes/area/index.ts: -------------------------------------------------------------------------------- 1 | import AreaChart from '@/recipes/area/AreaChart'; 2 | 3 | /* eslint-disable @typescript-eslint/ban-ts-comment */ 4 | // @ts-ignore 5 | AreaChart.install = function install(Vue: Vue) { 6 | // @ts-ignore 7 | Vue.component(AreaChart.name, AreaChart); 8 | }; 9 | 10 | export default AreaChart; 11 | -------------------------------------------------------------------------------- /packages/ez-vue/src/recipes/bar/index.ts: -------------------------------------------------------------------------------- 1 | import BarChart from './BarChart'; 2 | 3 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 4 | // @ts-ignore 5 | BarChart.install = function install(Vue) { 6 | Vue.component(BarChart.name, BarChart); 7 | }; 8 | 9 | export default BarChart; 10 | -------------------------------------------------------------------------------- /packages/ez-vue/src/recipes/column/index.ts: -------------------------------------------------------------------------------- 1 | import ColumnChart from './ColumnChart'; 2 | 3 | /* eslint-disable @typescript-eslint/ban-ts-comment */ 4 | // @ts-ignore 5 | ColumnChart.install = function install(Vue: Vue) { 6 | // @ts-ignore 7 | Vue.component(ColumnChart.name, ColumnChart); 8 | }; 9 | 10 | export default ColumnChart; 11 | -------------------------------------------------------------------------------- /packages/ez-vue/src/recipes/line/index.ts: -------------------------------------------------------------------------------- 1 | import LineChart from '@/recipes/line/LineChart'; 2 | import LineErrorMarginChart from '@/recipes/line/LineErrorMarginChart'; 3 | import MultiLineChart from '@/recipes/line/MultiLineChart'; 4 | 5 | /* eslint-disable @typescript-eslint/ban-ts-comment */ 6 | // @ts-ignore 7 | LineChart.install = function install(Vue: Vue) { 8 | // @ts-ignore 9 | Vue.component(LineChart.name, LineChart); 10 | }; 11 | 12 | /* eslint-disable @typescript-eslint/ban-ts-comment */ 13 | // @ts-ignore 14 | LineErrorMarginChart.install = function install(Vue: Vue) { 15 | // @ts-ignore 16 | Vue.component(LineErrorMarginChart.name, LineErrorMarginChart); 17 | }; 18 | 19 | /* eslint-disable @typescript-eslint/ban-ts-comment */ 20 | // @ts-ignore 21 | MultiLineChart.install = function install(Vue: Vue) { 22 | // @ts-ignore 23 | Vue.component(MultiLineChart.name, MultiLineChart); 24 | }; 25 | 26 | export default { 27 | LineChart, 28 | LineErrorMarginChart, 29 | MultiLineChart, 30 | }; 31 | -------------------------------------------------------------------------------- /packages/ez-vue/src/recipes/pie/index.ts: -------------------------------------------------------------------------------- 1 | import PieChart from './PieChart'; 2 | import SemiCircleChart from './SemiCircleChart'; 3 | import RadialChart from './RadialChart'; 4 | import IrregularPieChart from './IrregularPieChart'; 5 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 6 | // @ts-ignore 7 | PieChart.install = function install(Vue) { 8 | Vue.component(PieChart.name, PieChart); 9 | }; 10 | 11 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 12 | // @ts-ignore 13 | SemiCircleChart.install = function install(Vue) { 14 | Vue.component(SemiCircleChart.name, SemiCircleChart); 15 | }; 16 | 17 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 18 | // @ts-ignore 19 | RadialChart.install = function install(Vue) { 20 | Vue.component(RadialChart.name, RadialChart); 21 | }; 22 | 23 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 24 | // @ts-ignore 25 | IrregularPieChart.install = function install(Vue) { 26 | Vue.component(IrregularPieChart.name, IrregularPieChart); 27 | }; 28 | 29 | export default { 30 | PieChart, 31 | SemiCircleChart, 32 | RadialChart, 33 | IrregularPieChart, 34 | }; 35 | -------------------------------------------------------------------------------- /packages/ez-vue/src/recipes/scatter/index.ts: -------------------------------------------------------------------------------- 1 | import ScatterChart from './ScatterChart'; 2 | import BubbleChart from './BubbleChart'; 3 | 4 | /* eslint-disable @typescript-eslint/ban-ts-comment */ 5 | // @ts-ignore 6 | ScatterChart.install = function install(Vue: Vue) { 7 | // @ts-ignore 8 | Vue.component(ScatterChart.name, ScatterChart); 9 | }; 10 | 11 | /* eslint-disable @typescript-eslint/ban-ts-comment */ 12 | // @ts-ignore 13 | BubbleChart.install = function install(Vue: Vue) { 14 | // @ts-ignore 15 | Vue.component(BubbleChart.name, BubbleChart); 16 | }; 17 | 18 | export default { 19 | ScatterChart, 20 | BubbleChart, 21 | }; 22 | -------------------------------------------------------------------------------- /packages/ez-vue/src/resize-observer.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for non-npm package resize-observer-browser 0.1 2 | // Project: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver, https://developers.google.com/web/updates/2016/10/resizeobserver, https://wicg.github.io/ResizeObserver/ 3 | // Definitions by: Chives 4 | // William Furr 5 | // Alexander Shushunov 6 | // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped 7 | 8 | interface Window { 9 | ResizeObserver: typeof ResizeObserver; 10 | } 11 | 12 | interface ResizeObserverOptions { 13 | /** 14 | * Sets which box model the observer will observe changes to. Possible values 15 | * are `content-box` (the default), and `border-box`. 16 | * 17 | * @default 'content-box' 18 | */ 19 | box?: 'content-box' | 'border-box' | 'device-pixel-content-box' | undefined; 20 | } 21 | 22 | interface ResizeObserverSize { 23 | readonly inlineSize: number; 24 | readonly blockSize: number; 25 | } 26 | 27 | interface ResizeObserver { 28 | disconnect(): void; 29 | observe(target: Element, options?: ResizeObserverOptions): void; 30 | unobserve(target: Element): void; 31 | } 32 | 33 | declare var ResizeObserver: { 34 | new (callback: ResizeObserverCallback): ResizeObserver; 35 | prototype: ResizeObserver; 36 | }; 37 | 38 | interface ResizeObserverCallback { 39 | (entries: ResizeObserverEntry[], observer: ResizeObserver): void; 40 | } 41 | 42 | interface ResizeObserverEntry { 43 | readonly target: Element; 44 | readonly contentRect: DOMRectReadOnly; 45 | readonly borderBoxSize: ReadonlyArray; 46 | readonly contentBoxSize: ReadonlyArray; 47 | readonly devicePixelContentBoxSize: ReadonlyArray; 48 | } 49 | -------------------------------------------------------------------------------- /packages/ez-vue/src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue'; 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementAttributesProperty { $props: {}; } 9 | // tslint:disable no-empty-interface 10 | interface ElementClass extends Vue {} 11 | interface IntrinsicElements { 12 | [elem: string]: any; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/ez-vue/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue'; 3 | 4 | export default Vue; 5 | } 6 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/mocks/ResizeObserver.tsx: -------------------------------------------------------------------------------- 1 | class ResizeObserver { 2 | observe() { 3 | // do nothing 4 | } 5 | 6 | unobserve() { 7 | // do nothing 8 | } 9 | 10 | disconnect() { 11 | // do nothing 12 | } 13 | } 14 | 15 | // eslint-disable-next-line 16 | (window as any).ResizeObserver = ResizeObserver; 17 | export default ResizeObserver; 18 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/components/Arcs.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { render } from '@testing-library/vue'; 3 | import { 4 | dimensions, 5 | tooltip, 6 | rawData, 7 | radialLinearScale, 8 | colorScale, 9 | } from 'eazychart-core/src/sample-data'; 10 | import Arcs from '@/components/Arcs'; 11 | 12 | describe('Arcs', () => { 13 | it('renders svg radial with the right coordinates / dimensions', async () => { 14 | const wrapper = render(Arcs, { 15 | propsData: { 16 | valueDomainKey: 'amount', 17 | labelDomainKey: 'label', 18 | }, 19 | provide: { 20 | __reactiveInject__: { 21 | chart: { 22 | data: rawData, 23 | dimensions, 24 | animationOptions: { 25 | easing: 'easeLinear', 26 | duration: 0, 27 | delay: 0, 28 | }, 29 | }, 30 | linearScale: radialLinearScale, 31 | colorScale, 32 | }, 33 | tooltip, 34 | }, 35 | }); 36 | 37 | await Vue.nextTick(); 38 | 39 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/components/Area.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { render } from '@testing-library/vue'; 3 | import { 4 | dimensions, 5 | verticalLinearScale, 6 | horizontalLinearScale, 7 | tooltip, 8 | rawData, 9 | } from 'eazychart-core/src/sample-data'; 10 | import Area from '@/components/Area'; 11 | 12 | describe('Area', () => { 13 | it('renders svg area with the right coordinates / dimensions', async () => { 14 | const wrapper = render(Area, { 15 | propsData: { 16 | xDomainKey: horizontalLinearScale.definition.domainKey, 17 | yDomainKey: verticalLinearScale.definition.domainKey, 18 | }, 19 | provide: { 20 | __reactiveInject__: { 21 | chart: { 22 | data: rawData, 23 | dimensions, 24 | animationOptions: { 25 | easing: 'easeLinear', 26 | duration: 0, 27 | delay: 0, 28 | }, 29 | }, 30 | cartesianScale: { 31 | xScale: horizontalLinearScale, 32 | yScale: verticalLinearScale, 33 | }, 34 | }, 35 | tooltip, 36 | }, 37 | }); 38 | 39 | await Vue.nextTick(); 40 | 41 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/components/Bars.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { render } from '@testing-library/vue'; 3 | import { 4 | dimensions, 5 | tooltip, 6 | horizontalBandScale, 7 | verticalLinearScale, 8 | rawData, 9 | horizontalBandScaleDef, 10 | verticalLinearScaleDef, 11 | colorScale, 12 | } from 'eazychart-core/src/sample-data'; 13 | import Bars from '@/components/Bars'; 14 | 15 | describe('Bars', () => { 16 | it('renders svg rects with the right coordinates / dimensions', async () => { 17 | const wrapper = render(Bars, { 18 | propsData: { 19 | xDomainKey: horizontalBandScaleDef.domainKey, 20 | yDomainKey: verticalLinearScaleDef.domainKey, 21 | }, 22 | provide: { 23 | __reactiveInject__: { 24 | chart: { 25 | data: rawData, 26 | dimensions, 27 | animationOptions: { 28 | easing: 'easeLinear', 29 | duration: 0, 30 | delay: 0, 31 | }, 32 | }, 33 | cartesianScale: { 34 | xScale: horizontalBandScale, 35 | yScale: verticalLinearScale, 36 | }, 37 | colorScale, 38 | }, 39 | tooltip, 40 | }, 41 | }); 42 | 43 | await Vue.nextTick(); 44 | 45 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/components/Bubbles.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { render } from '@testing-library/vue'; 3 | import { 4 | dimensions, 5 | verticalLinearScale, 6 | horizontalLinearScale, 7 | tooltip, 8 | rawData, 9 | radialLinearScale, 10 | } from 'eazychart-core/src/sample-data'; 11 | import Bubbles from '@/components/Bubbles'; 12 | 13 | describe('Bubbles', () => { 14 | it('renders svg bubbles with the right coordinates / path', async () => { 15 | const wrapper = render(Bubbles, { 16 | propsData: { 17 | xDomainKey: horizontalLinearScale.definition.domainKey, 18 | yDomainKey: verticalLinearScale.definition.domainKey, 19 | rDomainKey: radialLinearScale.definition.domainKey, 20 | fill: 'blue', 21 | }, 22 | provide: { 23 | __reactiveInject__: { 24 | chart: { 25 | data: rawData, 26 | dimensions, 27 | animationOptions: { 28 | easing: 'easeLinear', 29 | duration: 0, 30 | delay: 0, 31 | }, 32 | }, 33 | cartesianScale: { 34 | xScale: horizontalLinearScale, 35 | yScale: verticalLinearScale, 36 | }, 37 | linearScale: radialLinearScale, 38 | }, 39 | tooltip, 40 | }, 41 | }); 42 | 43 | await Vue.nextTick(); 44 | 45 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/components/Chart.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { render } from '@testing-library/vue'; 3 | import { 4 | dimensions, 5 | padding, 6 | animationOptions, 7 | rawData, 8 | } from 'eazychart-core/src/sample-data'; 9 | import { Component, InjectReactive } from 'vue-property-decorator'; 10 | import { ChartContext } from 'eazychart-core/src/types'; 11 | import Chart from '@/components/Chart'; 12 | // eslint-disable-next-line import/no-unresolved 13 | import 'tests/mocks/ResizeObserver'; 14 | 15 | @Component 16 | export default class DataDump extends Vue { 17 | @InjectReactive('chart') 18 | private chart!: ChartContext; 19 | 20 | render() { 21 | const { chart } = this; 22 | return
    {JSON.stringify(chart.data)}
    ; 23 | } 24 | } 25 | 26 | describe('Chart', () => { 27 | it('renders svg chart container with the right slots', async () => { 28 | const wrapper = render(Chart, { 29 | propsData: { 30 | dimensions, 31 | padding, 32 | animationOptions, 33 | rawData, 34 | }, 35 | slots: { 36 | default: '
    Default Slot
    ', 37 | Legend: '
    Legend Slot
    ', 38 | Tooltip: '
    Tooltip Slot
    ', 39 | }, 40 | }); 41 | 42 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 43 | }); 44 | 45 | it('should provide the chart data to the children components', async () => { 46 | const wrapper = render(Chart, { 47 | propsData: { 48 | dimensions, 49 | padding, 50 | animationOptions, 51 | rawData, 52 | }, 53 | slots: { 54 | default: DataDump, 55 | }, 56 | }); 57 | 58 | await Vue.nextTick(); 59 | 60 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/components/IrregularArcs.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { render } from '@testing-library/vue'; 3 | import { 4 | colorScale, 5 | dimensions, 6 | rawData, 7 | tooltip, 8 | verticalLinearScale, 9 | } from 'eazychart-core/src/sample-data'; 10 | import IrregularArcs from '@/components/IrregularArcs'; 11 | 12 | describe('IrregularArcs', () => { 13 | it('renders svg irregular arcs with the right coordinates / dimensions', async () => { 14 | const wrapper = render(IrregularArcs, { 15 | propsData: { 16 | valueDomainKey: verticalLinearScale.definition.domainKey, 17 | labelDomainKey: colorScale.definition.domainKey, 18 | donutRadius: 0, 19 | }, 20 | provide: { 21 | __reactiveInject__: { 22 | chart: { 23 | data: rawData, 24 | dimensions, 25 | animationOptions: { 26 | easing: 'easeLinear', 27 | duration: 0, 28 | delay: 0, 29 | }, 30 | }, 31 | linearScale: verticalLinearScale, 32 | colorScale, 33 | }, 34 | tooltip, 35 | }, 36 | }); 37 | 38 | await Vue.nextTick(); 39 | 40 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/components/Map.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { render } from '@testing-library/vue'; 3 | import { 4 | dimensions, 5 | tooltip, 6 | rawData, 7 | mapFeatureData, 8 | } from 'eazychart-core/src/sample-data'; 9 | import Map from '@/components/Map'; 10 | 11 | describe('Map', () => { 12 | it('renders svg areas using GeoJSON features', async () => { 13 | const wrapper = render(Map, { 14 | propsData: { 15 | geoJson: mapFeatureData, 16 | map: { 17 | geoDomainKey: 'label', 18 | valueDomainKey: 'value', 19 | projectionType: 'geoMercator', 20 | fill: 'blue', 21 | stroke: 'red', 22 | }, 23 | }, 24 | provide: { 25 | __reactiveInject__: { 26 | chart: { 27 | data: rawData, 28 | dimensions, 29 | animationOptions: { 30 | easing: 'easeLinear', 31 | duration: 0, 32 | delay: 0, 33 | }, 34 | }, 35 | }, 36 | tooltip, 37 | }, 38 | }); 39 | 40 | await Vue.nextTick(); 41 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/components/Pie.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { render } from '@testing-library/vue'; 3 | import { 4 | dimensions, 5 | tooltip, 6 | rawData, 7 | colorScale, 8 | } from 'eazychart-core/src/sample-data'; 9 | import Pie from '@/components/Pie'; 10 | 11 | describe('Pie', () => { 12 | it('renders svg pie with the right coordinates / dimensions', async () => { 13 | const wrapper = render(Pie, { 14 | propsData: { 15 | valueDomainKey: 'amount', 16 | labelDomainKey: 'label', 17 | donutRadius: 0, 18 | }, 19 | provide: { 20 | __reactiveInject__: { 21 | chart: { 22 | data: rawData, 23 | dimensions, 24 | animationOptions: { 25 | easing: 'easeLinear', 26 | duration: 0, 27 | delay: 0, 28 | }, 29 | }, 30 | colorScale, 31 | }, 32 | tooltip, 33 | }, 34 | }); 35 | 36 | await Vue.nextTick(); 37 | 38 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/components/Points.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { render } from '@testing-library/vue'; 3 | import { 4 | dimensions, 5 | verticalLinearScale, 6 | horizontalLinearScale, 7 | tooltip, 8 | rawData, 9 | } from 'eazychart-core/src/sample-data'; 10 | import Points from '@/components/Points'; 11 | 12 | describe('Points', () => { 13 | it('renders svg points with the right coordinates / path', async () => { 14 | const wrapper = render(Points, { 15 | propsData: { 16 | xDomainKey: horizontalLinearScale.definition.domainKey, 17 | yDomainKey: verticalLinearScale.definition.domainKey, 18 | r: 6, 19 | fill: 'red', 20 | stroke: 'red', 21 | }, 22 | provide: { 23 | __reactiveInject__: { 24 | chart: { 25 | data: rawData, 26 | dimensions, 27 | animationOptions: { 28 | easing: 'easeLinear', 29 | duration: 0, 30 | delay: 0, 31 | }, 32 | }, 33 | cartesianScale: { 34 | xScale: horizontalLinearScale, 35 | yScale: verticalLinearScale, 36 | }, 37 | }, 38 | tooltip, 39 | }, 40 | }); 41 | 42 | await Vue.nextTick(); 43 | 44 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/components/addons/Tooltip.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { render } from '@testing-library/vue'; 3 | import { chartData, tooltip } from 'eazychart-core/src/sample-data'; 4 | import { ShapeDatum } from 'eazychart-core/src/types'; 5 | import Tooltip from '@/components/addons/tooltip/Tooltip'; 6 | 7 | describe('Tooltip', () => { 8 | it('renders no tooltip when shape is being hovered', async () => { 9 | const wrapper = render(Tooltip, { 10 | propsData: { 11 | datum: null, 12 | shapeDatum: null, 13 | mousePosition: { x: 400, y: 300 }, 14 | isVisible: false, 15 | animationOptions: { 16 | ease: 'easeLinear', 17 | delay: 0, 18 | duration: 0, 19 | }, 20 | }, 21 | provide: { 22 | tooltip, 23 | }, 24 | }); 25 | await Vue.nextTick(); 26 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 27 | }); 28 | 29 | it('renders the tooltip when a shape is hovered', async () => { 30 | const wrapper = render(Tooltip, { 31 | propsData: { 32 | offset: { 33 | x: -10, 34 | y: 20, 35 | }, 36 | datum: chartData[0], 37 | shapeDatum: { 38 | color: chartData[0].color, 39 | } as ShapeDatum, 40 | mousePosition: { x: 50, y: 150 }, 41 | isVisible: true, 42 | animationOptions: { 43 | ease: 'easeLinear', 44 | delay: 0, 45 | duration: 0, 46 | }, 47 | }, 48 | provide: { 49 | tooltip, 50 | }, 51 | }); 52 | await Vue.nextTick(); 53 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/components/scales/Axis.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/vue'; 2 | import { Position } from 'eazychart-core/src/types'; 3 | import { 4 | dimensions, 5 | horizontalLinearScale, 6 | padding, 7 | verticalLinearScale, 8 | } from 'eazychart-core/src/sample-data'; 9 | import Axis from '@/components/scales/Axis'; 10 | 11 | // eslint-disable-next-line import/no-unresolved 12 | import 'tests/mocks/ResizeObserver'; 13 | 14 | describe('Axis', () => { 15 | it('renders axis with four ticks', () => { 16 | const wrapper = render(Axis, { 17 | propsData: { 18 | position: Position.BOTTOM, 19 | tickCount: 4, 20 | }, 21 | provide: { 22 | __reactiveInject__: { 23 | chart: { 24 | dimensions, 25 | padding, 26 | }, 27 | cartesianScale: { 28 | xScale: horizontalLinearScale, 29 | yScale: verticalLinearScale, 30 | }, 31 | }, 32 | }, 33 | }); 34 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/components/scales/GridLines.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/vue'; 2 | import { Direction } from 'eazychart-core/src/types'; 3 | import { 4 | dimensions, 5 | horizontalLinearScale, 6 | verticalLinearScale, 7 | } from 'eazychart-core/src/sample-data'; 8 | import GridLines from '@/components/scales/grid/GridLines'; 9 | 10 | describe('GridLines', () => { 11 | it('renders horizontal grid lines with four ticks', async () => { 12 | const wrapper = render(GridLines, { 13 | propsData: { 14 | direction: Direction.HORIZONTAL, 15 | tickCount: 4, 16 | }, 17 | provide: { 18 | __reactiveInject__: { 19 | chart: { 20 | dimensions, 21 | }, 22 | cartesianScale: { 23 | xScale: horizontalLinearScale, 24 | yScale: verticalLinearScale, 25 | }, 26 | }, 27 | }, 28 | }); 29 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 30 | }); 31 | 32 | it('renders vertical grid lines with four ticks', async () => { 33 | const wrapper = render(GridLines, { 34 | propsData: { 35 | direction: Direction.VERTICAL, 36 | tickCount: 4, 37 | }, 38 | provide: { 39 | __reactiveInject__: { 40 | chart: { 41 | dimensions, 42 | }, 43 | cartesianScale: { 44 | xScale: horizontalLinearScale, 45 | yScale: verticalLinearScale, 46 | }, 47 | }, 48 | }, 49 | }); 50 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/components/shapes/AreaPath.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { render } from '@testing-library/vue'; 3 | import { dimensions, areaData, tooltip } from 'eazychart-core/src/sample-data'; 4 | import AreaPath from '@/components/shapes/AreaPath'; 5 | 6 | describe('AreaPath', () => { 7 | it('renders an svg path with the right coordinates / path', async () => { 8 | const wrapper = render(AreaPath, { 9 | propsData: { 10 | shapeData: areaData, 11 | }, 12 | provide: { 13 | __reactiveInject__: { 14 | chart: { 15 | animationOptions: { 16 | easing: 'easeLinear', 17 | duration: 0, 18 | delay: 0, 19 | }, 20 | }, 21 | }, 22 | tooltip, 23 | dimensions, 24 | }, 25 | }); 26 | 27 | await Vue.nextTick(); 28 | 29 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/components/shapes/LinePath.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { render } from '@testing-library/vue'; 3 | import { dimensions, pointsData, tooltip } from 'eazychart-core/src/sample-data'; 4 | import LinePath from '@/components/shapes/LinePath'; 5 | 6 | describe('LinePath', () => { 7 | it('renders an svg path with the right coordinates / path', async () => { 8 | const wrapper = render(LinePath, { 9 | propsData: { 10 | shapeData: pointsData, 11 | }, 12 | provide: { 13 | __reactiveInject__: { 14 | chart: { 15 | animationOptions: { 16 | easing: 'easeLinear', 17 | duration: 0, 18 | delay: 0, 19 | }, 20 | }, 21 | }, 22 | tooltip, 23 | dimensions, 24 | }, 25 | }); 26 | 27 | await Vue.nextTick(); 28 | 29 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/components/shapes/MapPath.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { render } from '@testing-library/vue'; 3 | import { geoFeatureA, tooltip } from 'eazychart-core/src/sample-data'; 4 | import MapPath from '@/components/shapes/MapPath'; 5 | import { 6 | calculateGeoProjectionViewport, 7 | computeMapProjection, 8 | defaultChartDimensions, 9 | } from 'eazychart-core/src'; 10 | import { GeoFeatureDatum } from 'eazychart-core/src/types'; 11 | 12 | describe('MapPath', () => { 13 | it('renders an svg path given a GeoJSON feature', async () => { 14 | const projectionViewport = calculateGeoProjectionViewport( 15 | { type: 'FeatureCollection', features: [geoFeatureA] }, 16 | 'geoMercator', 17 | defaultChartDimensions, 18 | ); 19 | const projection = computeMapProjection('geoMercator', projectionViewport); 20 | 21 | const wrapper = render(MapPath, { 22 | propsData: { 23 | shapeDatum: { 24 | id: '1', 25 | color: 'red', 26 | feature: geoFeatureA, 27 | } as GeoFeatureDatum, 28 | }, 29 | provide: { 30 | __reactiveInject__: { 31 | chart: { 32 | animationOptions: { 33 | easing: 'easeLinear', 34 | duration: 0, 35 | delay: 0, 36 | }, 37 | }, 38 | mapContext: { projection }, 39 | }, 40 | tooltip, 41 | }, 42 | }); 43 | 44 | await Vue.nextTick(); 45 | 46 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/recipes/area/AreaChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { dimensions, pointsRawData } from 'eazychart-core/src/sample-data'; 3 | import { render } from '@testing-library/vue'; 4 | import AreaChart from '@/recipes/area/AreaChart'; 5 | 6 | // eslint-disable-next-line import/no-unresolved 7 | import 'tests/mocks/ResizeObserver'; 8 | 9 | describe('AreaChart', () => { 10 | it('renders an area chart', async () => { 11 | const wrapper = render(AreaChart, { 12 | propsData: { 13 | data: pointsRawData, 14 | area: { 15 | stroke: 'orange', 16 | strokeWidth: 2, 17 | fill: 'orange', 18 | }, 19 | marker: { 20 | hidden: false, 21 | color: 'orange', 22 | radius: 2, 23 | }, 24 | xAxis: { domainKey: 'xValue' }, 25 | yAxis: { domainKey: 'yValue' }, 26 | grid: { directions: [] }, 27 | dimensions, 28 | animationOptions: { 29 | easing: 'easeBack', 30 | duration: 0, 31 | delay: 0, 32 | }, 33 | }, 34 | }); 35 | 36 | await Vue.nextTick(); 37 | 38 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/recipes/area/MultiAreaChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { dimensions, pointsRawData } from 'eazychart-core/src/sample-data'; 3 | import { render } from '@testing-library/vue'; 4 | import MultiAreaChart from '@/recipes/area/MultiAreaChart'; 5 | 6 | // eslint-disable-next-line import/no-unresolved 7 | import 'tests/mocks/ResizeObserver'; 8 | 9 | describe('MultiAreaChart', () => { 10 | it('renders a multiarea chart', async () => { 11 | const wrapper = render(MultiAreaChart, { 12 | propsData: { 13 | data: pointsRawData, 14 | area: { 15 | stroke: 'red', 16 | strokeWidth: 2, 17 | opacity: 0.5, 18 | }, 19 | marker: { 20 | hidden: false, 21 | color: 'blue', 22 | radius: 2, 23 | }, 24 | grid: { directions: [] }, 25 | dimensions, 26 | xAxis: { domainKey: 'xValue' }, 27 | yAxis: { 28 | domainKeys: ['yValue', 'zValue'], 29 | }, 30 | animationOptions: { 31 | easing: 'easeBack', 32 | duration: 0, 33 | delay: 0, 34 | }, 35 | }, 36 | }); 37 | 38 | await Vue.nextTick(); 39 | 40 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/recipes/bar/BarChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { colors, dimensions, rawData } from 'eazychart-core/src/sample-data'; 3 | import { Position } from 'eazychart-core/src/types'; 4 | import { render } from '@testing-library/vue'; 5 | import BarChart from '@/recipes/bar/BarChart'; 6 | 7 | // eslint-disable-next-line import/no-unresolved 8 | import 'tests/mocks/ResizeObserver'; 9 | 10 | describe('BarChart', () => { 11 | it('renders a bar chart', async () => { 12 | const wrapper = render(BarChart, { 13 | propsData: { 14 | data: rawData, 15 | colors, 16 | grid: { directions: [] }, 17 | dimensions, 18 | xAxis: { 19 | domainKey: 'value', 20 | position: Position.BOTTOM, 21 | }, 22 | yAxis: { 23 | domainKey: 'label', 24 | position: Position.LEFT, 25 | }, 26 | animationOptions: { 27 | easing: 'easeBack', 28 | duration: 0, 29 | delay: 0, 30 | }, 31 | }, 32 | }); 33 | 34 | await Vue.nextTick(); 35 | 36 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/recipes/column/ColumnChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { colors, dimensions, rawData } from 'eazychart-core/src/sample-data'; 3 | import { Position } from 'eazychart-core/src/types'; 4 | import { render } from '@testing-library/vue'; 5 | import ColumnChart from '@/recipes/column/ColumnChart'; 6 | 7 | // eslint-disable-next-line import/no-unresolved 8 | import 'tests/mocks/ResizeObserver'; 9 | 10 | describe('ColumnChart', () => { 11 | it('renders a column chart', async () => { 12 | const wrapper = render(ColumnChart, { 13 | propsData: { 14 | data: rawData, 15 | colors, 16 | grid: { directions: [] }, 17 | dimensions, 18 | xAxis: { 19 | domainKey: 'label', 20 | position: Position.BOTTOM, 21 | }, 22 | yAxis: { 23 | domainKey: 'value', 24 | position: Position.LEFT, 25 | }, 26 | animationOptions: { 27 | easing: 'easeBack', 28 | duration: 0, 29 | delay: 0, 30 | }, 31 | }, 32 | }); 33 | 34 | await Vue.nextTick(); 35 | 36 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/recipes/column/LineColumnChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { colors, dimensions, rawData } from 'eazychart-core/src/sample-data'; 3 | import { Position } from 'eazychart-core/src/types'; 4 | import { render } from '@testing-library/vue'; 5 | import LineColumnChart from '@/recipes/column/LineColumnChart'; 6 | 7 | // eslint-disable-next-line import/no-unresolved 8 | import 'tests/mocks/ResizeObserver'; 9 | 10 | describe('LineColumnChart', () => { 11 | it('renders a line & column chart', async () => { 12 | const wrapper = render(LineColumnChart, { 13 | propsData: { 14 | data: rawData, 15 | colors, 16 | grid: { directions: [] }, 17 | dimensions, 18 | xAxis: { 19 | domainKey: 'label', 20 | position: Position.BOTTOM, 21 | }, 22 | yAxis: { 23 | domainKey: 'value', 24 | position: Position.LEFT, 25 | }, 26 | yLineAxis: { 27 | domainKey: 'amount', 28 | position: Position.RIGHT, 29 | }, 30 | animationOptions: { 31 | easing: 'easeBack', 32 | duration: 0, 33 | delay: 0, 34 | }, 35 | }, 36 | }); 37 | 38 | await Vue.nextTick(); 39 | 40 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/recipes/line/LineChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { dimensions, pointsRawData } from 'eazychart-core/src/sample-data'; 3 | import { render } from '@testing-library/vue'; 4 | import LineChart from '@/recipes/line/LineChart'; 5 | 6 | // eslint-disable-next-line import/no-unresolved 7 | import 'tests/mocks/ResizeObserver'; 8 | 9 | describe('LineChart', () => { 10 | it('renders a line chart', async () => { 11 | const wrapper = render(LineChart, { 12 | propsData: { 13 | data: pointsRawData, 14 | line: { 15 | stroke: 'red', 16 | strokeWidth: 2, 17 | }, 18 | marker: { 19 | hidden: false, 20 | color: 'blue', 21 | radius: 2, 22 | }, 23 | grid: { directions: [] }, 24 | dimensions, 25 | animationOptions: { 26 | easing: 'easeBack', 27 | duration: 0, 28 | delay: 0, 29 | }, 30 | }, 31 | }); 32 | 33 | await Vue.nextTick(); 34 | 35 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/recipes/line/LineErrorMarginChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import { dimensions, pointsWithMarginData } from 'eazychart-core/src/sample-data'; 2 | import Vue from 'vue'; 3 | import { render } from '@testing-library/vue'; 4 | import LineErrorMarginChart from '@/recipes/line/LineErrorMarginChart'; 5 | 6 | // eslint-disable-next-line import/no-unresolved 7 | import 'tests/mocks/ResizeObserver'; 8 | 9 | describe('LineErrorMarginChart', () => { 10 | it('renders a line error margin chart', async () => { 11 | const wrapper = render(LineErrorMarginChart, { 12 | propsData: { 13 | data: pointsWithMarginData, 14 | line: { 15 | stroke: 'red', 16 | strokeWidth: 2, 17 | }, 18 | marker: { 19 | hidden: false, 20 | color: 'red', 21 | radius: 2, 22 | }, 23 | grid: { directions: [] }, 24 | dimensions, 25 | animationOptions: { 26 | easing: 'easeBack', 27 | duration: 0, 28 | delay: 0, 29 | }, 30 | }, 31 | }); 32 | 33 | await Vue.nextTick(); 34 | 35 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/recipes/line/MultiLineChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { dimensions, pointsRawData } from 'eazychart-core/src/sample-data'; 3 | import { render } from '@testing-library/vue'; 4 | import MultiLineChart from '@/recipes/line/MultiLineChart'; 5 | 6 | // eslint-disable-next-line import/no-unresolved 7 | import 'tests/mocks/ResizeObserver'; 8 | 9 | describe('MultiLineChart', () => { 10 | it('renders a multiline chart', async () => { 11 | const wrapper = render(MultiLineChart, { 12 | propsData: { 13 | data: pointsRawData, 14 | line: { 15 | stroke: 'red', 16 | strokeWidth: 2, 17 | }, 18 | marker: { 19 | hidden: false, 20 | color: 'blue', 21 | radius: 2, 22 | }, 23 | grid: { directions: [] }, 24 | dimensions, 25 | yAxis: { 26 | domainKeys: ['yValue', 'zValue'], 27 | }, 28 | animationOptions: { 29 | easing: 'easeBack', 30 | duration: 0, 31 | delay: 0, 32 | }, 33 | }, 34 | }); 35 | 36 | await Vue.nextTick(); 37 | 38 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/recipes/map/MapChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { 3 | colors, 4 | dimensions, 5 | mapFeatureData, 6 | rawData, 7 | } from 'eazychart-core/src/sample-data'; 8 | import { render } from '@testing-library/vue'; 9 | import MapChart from '@/recipes/map/MapChart'; 10 | 11 | // eslint-disable-next-line import/no-unresolved 12 | import 'tests/mocks/ResizeObserver'; 13 | 14 | describe('MapChart', () => { 15 | it('renders a map chart', async () => { 16 | const wrapper = render(MapChart, { 17 | propsData: { 18 | data: rawData, 19 | geoJson: mapFeatureData, 20 | map: { 21 | geoDomainKey: 'label', 22 | valueDomainKey: 'value', 23 | projectionType: 'geoMercator', 24 | fill: 'blue', 25 | stroke: 'red', 26 | }, 27 | dimensions, 28 | colors, 29 | animationOptions: { 30 | easing: 'easeBack', 31 | duration: 0, 32 | delay: 0, 33 | }, 34 | }, 35 | }); 36 | 37 | await Vue.nextTick(); 38 | 39 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/recipes/pie/IrregularPieChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import { colors, dimensions, rawData } from 'eazychart-core/src/sample-data'; 2 | import Vue from 'vue'; 3 | import { render } from '@testing-library/vue'; 4 | import IrregularPieChart from '@/recipes/pie/IrregularPieChart'; 5 | 6 | // eslint-disable-next-line import/no-unresolved 7 | import 'tests/mocks/ResizeObserver'; 8 | 9 | describe('IrregularPieChart', () => { 10 | it('renders a irregular pie chart', async () => { 11 | const wrapper = render(IrregularPieChart, { 12 | propsData: { 13 | data: rawData, 14 | colors, 15 | valueDomainKey: 'value', 16 | labelDomainKey: 'label', 17 | dimensions, 18 | animationOptions: { 19 | easing: 'easeLinear', 20 | duration: 0, 21 | delay: 0, 22 | }, 23 | }, 24 | }); 25 | 26 | await Vue.nextTick(); 27 | 28 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/recipes/pie/PieChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import { colors, dimensions, rawData } from 'eazychart-core/src/sample-data'; 2 | import Vue from 'vue'; 3 | import { render } from '@testing-library/vue'; 4 | import PieChart from '@/recipes/pie/PieChart'; 5 | 6 | // eslint-disable-next-line import/no-unresolved 7 | import 'tests/mocks/ResizeObserver'; 8 | 9 | describe('PieChart', () => { 10 | it('renders a pie chart', async () => { 11 | const wrapper = render(PieChart, { 12 | propsData: { 13 | data: rawData, 14 | colors, 15 | valueDomainKey: 'value', 16 | labelDomainKey: 'label', 17 | dimensions, 18 | animationOptions: { 19 | easing: 'easeLinear', 20 | duration: 0, 21 | delay: 0, 22 | }, 23 | }, 24 | }); 25 | 26 | await Vue.nextTick(); 27 | 28 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/recipes/pie/RadialChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import { colors, dimensions, rawData } from 'eazychart-core/src/sample-data'; 2 | import Vue from 'vue'; 3 | import { render } from '@testing-library/vue'; 4 | import RadialChart from '@/recipes/pie/RadialChart'; 5 | 6 | // eslint-disable-next-line import/no-unresolved 7 | import 'tests/mocks/ResizeObserver'; 8 | 9 | describe('RadialChart', () => { 10 | it('renders a Radial chart', async () => { 11 | const wrapper = render(RadialChart, { 12 | propsData: { 13 | data: rawData, 14 | colors, 15 | valueDomainKey: 'value', 16 | labelDomainKey: 'label', 17 | dimensions, 18 | animationOptions: { 19 | easing: 'easeLinear', 20 | duration: 0, 21 | delay: 0, 22 | }, 23 | }, 24 | }); 25 | 26 | await Vue.nextTick(); 27 | 28 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/recipes/pie/SemiCircleChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import { colors, dimensions, rawData } from 'eazychart-core/src/sample-data'; 2 | import Vue from 'vue'; 3 | import { render } from '@testing-library/vue'; 4 | import SemiCircleChart from '@/recipes/pie/SemiCircleChart'; 5 | // eslint-disable-next-line import/no-unresolved 6 | import 'tests/mocks/ResizeObserver'; 7 | 8 | describe('SemiCircleChart', () => { 9 | it('renders a semi-circle chart', async () => { 10 | const wrapper = render(SemiCircleChart, { 11 | propsData: { 12 | data: rawData, 13 | colors, 14 | valueDomainKey: 'value', 15 | labelDomainKey: 'label', 16 | dimensions, 17 | animationOptions: { 18 | easing: 'easeLinear', 19 | duration: 0, 20 | delay: 0, 21 | }, 22 | }, 23 | }); 24 | 25 | await Vue.nextTick(); 26 | 27 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/recipes/scatter/BubbleChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import { dimensions, rawData } from 'eazychart-core/src/sample-data'; 3 | import { render } from '@testing-library/vue'; 4 | import BubbleChart from '@/recipes/scatter/BubbleChart'; 5 | // eslint-disable-next-line import/no-unresolved 6 | import 'tests/mocks/ResizeObserver'; 7 | 8 | describe('BubbleChart', () => { 9 | it('renders a bubble chart', async () => { 10 | const wrapper = render(BubbleChart, { 11 | propsData: { 12 | data: rawData, 13 | bubble: { 14 | domainKey: 'amount', 15 | minRadius: 1, 16 | maxRadius: 10, 17 | fill: 'blue', 18 | }, 19 | grid: { directions: [] }, 20 | dimensions, 21 | xAxis: { 22 | domainKey: 'value', 23 | }, 24 | yAxis: { 25 | domainKey: 'amount', 26 | }, 27 | animationOptions: { 28 | easing: 'easeLinear', 29 | duration: 0, 30 | delay: 0, 31 | }, 32 | }, 33 | }); 34 | 35 | await Vue.nextTick(); 36 | 37 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /packages/ez-vue/tests/unit/recipes/scatter/ScatterChart.spec.tsx: -------------------------------------------------------------------------------- 1 | import { dimensions, rawData } from 'eazychart-core/src/sample-data'; 2 | import Vue from 'vue'; 3 | import { render } from '@testing-library/vue'; 4 | import ScatterChart from '@/recipes/scatter/ScatterChart'; 5 | // eslint-disable-next-line import/no-unresolved 6 | import 'tests/mocks/ResizeObserver'; 7 | 8 | describe('ScatterChart', () => { 9 | it('renders a scatter chart', async () => { 10 | const wrapper = render(ScatterChart, { 11 | propsData: { 12 | data: rawData, 13 | point: { 14 | radius: 6, 15 | color: 'red', 16 | }, 17 | grid: { directions: [] }, 18 | dimensions, 19 | xAxis: { 20 | domainKey: 'value', 21 | }, 22 | yAxis: { 23 | domainKey: 'amount', 24 | }, 25 | animationOptions: { 26 | easing: 'easeLinear', 27 | duration: 0, 28 | delay: 0, 29 | }, 30 | }, 31 | }); 32 | 33 | await Vue.nextTick(); 34 | 35 | expect(wrapper.container.innerHTML).toMatchSnapshot(); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /packages/ez-vue/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "jsx": "preserve", 5 | "experimentalDecorators": true, 6 | "allowSyntheticDefaultImports": true, 7 | "types": [ 8 | "webpack-env", 9 | "jest" 10 | ], 11 | "baseUrl": ".", 12 | "paths": { 13 | "@": [ 14 | "src" 15 | ], 16 | "@/*": [ 17 | "src/*" 18 | ], 19 | "tests/*": [ 20 | "tests/*" 21 | ] 22 | }, 23 | }, 24 | "include": [ 25 | "src/**/*.ts", 26 | "src/**/*.tsx", 27 | "src/**/*.vue", 28 | "tests/**/*.ts", 29 | "tests/**/*.tsx", 30 | ], 31 | "exclude": [ 32 | "node_modules" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /packages/ez-vue/vue.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-undef 2 | module.exports = { 3 | chainWebpack: (config) => config.resolve.symlinks(false), 4 | }; 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs 3 | "compilerOptions": { 4 | "target": "esnext", 5 | "module": "esnext", 6 | "lib": [ 7 | "dom", 8 | "esnext", 9 | "dom.iterable", 10 | "scripthost" 11 | ], 12 | "importHelpers": true, 13 | // output .d.ts declaration files for consumers 14 | "declaration": true, 15 | // output .js.map sourcemap files for consumers 16 | "sourceMap": true, 17 | // match output dir to input dir. e.g. dist/index instead of dist/src/index 18 | "rootDir": "./", 19 | // stricter type-checking for stronger correctness. Recommended by TS 20 | "strict": true, 21 | // linter checks for common issues 22 | "noImplicitReturns": true, 23 | "noFallthroughCasesInSwitch": true, 24 | // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative 25 | "noUnusedLocals": true, 26 | "noUnusedParameters": true, 27 | // use Node's module resolution algorithm, instead of the legacy TS one 28 | "moduleResolution": "node", 29 | // interop between ESM and CJS modules. Recommended by TS 30 | "esModuleInterop": true, 31 | // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS 32 | "skipLibCheck": true, 33 | // error out if import and file system have a casing mismatch. Recommended by TS 34 | "forceConsistentCasingInFileNames": true, 35 | // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc` 36 | "noEmit": true, 37 | "preserveSymlinks": true 38 | }, 39 | "include": [ 40 | "src/**/*.ts", 41 | "src/**/*.tsx", 42 | "tests/**/*.ts", 43 | "tests/**/*.tsx" 44 | ], 45 | "exclude": [ 46 | "node_modules" 47 | ] 48 | } 49 | --------------------------------------------------------------------------------