From e02f446bb6cbc4e9f15131bb704d1d5b8c9da668 Mon Sep 17 00:00:00 2001 From: Michael Iwersen Date: Thu, 2 Mar 2023 18:02:36 +0100 Subject: [PATCH 1/3] feat(SVG): support for viewport in SVG only providing width and height --- src/charts/LineChart/LineChart.stories.ts | 39 +++++++++++++++++++++++ src/charts/LineChart/LineChart.ts | 3 +- src/core/creation.ts | 19 ++++++++--- src/core/types.ts | 9 ++++++ 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/src/charts/LineChart/LineChart.stories.ts b/src/charts/LineChart/LineChart.stories.ts index 90cf0386..95fd7967 100644 --- a/src/charts/LineChart/LineChart.stories.ts +++ b/src/charts/LineChart/LineChart.stories.ts @@ -623,3 +623,42 @@ export function PathAnimation() { return root; } + +export function ViewBox() { + const root = document.createElement('div'); + + new LineChart( + root, + { + labels: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'], + series: [ + [12, 9, 7, 8, 5], + [2, 1, 3.5, 7, 3], + [1, 3, 4, 5, 6] + ] + }, + { + fullWidth: true, + chartPadding: { + right: 40 + }, + viewBox: { + width: 800, + height: 400 + } + }, + [ + [ + 'screen and (max-width: 575px)', + { + viewBox: { + width: 400, + height: 300 + } + } + ] + ] + ); + + return root; +} diff --git a/src/charts/LineChart/LineChart.ts b/src/charts/LineChart/LineChart.ts index e0eb9691..ce0bd80d 100644 --- a/src/charts/LineChart/LineChart.ts +++ b/src/charts/LineChart/LineChart.ts @@ -261,7 +261,8 @@ export class LineChart extends BaseChart { this.container, options.width, options.height, - options.classNames.chart + options.classNames.chart, + options.viewBox ); this.svg = svg; diff --git a/src/core/creation.ts b/src/core/creation.ts index f35836c8..b6c7b957 100644 --- a/src/core/creation.ts +++ b/src/core/creation.ts @@ -5,7 +5,8 @@ import type { Label, GridDrawEvent, GridBackgroundDrawEvent, - LabelDrawEvent + LabelDrawEvent, + ViewBox } from './types'; import type { EventEmitter } from '../event'; import type { Axis } from '../axes'; @@ -25,7 +26,8 @@ export function createSvg( container: Element, width: number | string = '100%', height: number | string = '100%', - className?: string + className?: string, + viewBox?: ViewBox ) { if (!container) { throw new Error('Container element is not found'); @@ -52,6 +54,10 @@ export function createSvg( svg.addClass(className); } + if (viewBox) { + svg.attr({ viewBox: `0 0 ${viewBox.width} ${viewBox.height}` }); + } + // Add the DOM node to our container container.appendChild(svg.getNode()); @@ -97,8 +103,13 @@ export function createChartRect(svg: Svg, options: Options) { const yAxisPosition = options.axisY?.position; const xAxisPosition = options.axisX?.position; // If width or height results in invalid value (including 0) we fallback to the unitless settings or even 0 - let width = svg.width() || quantity(options.width).value || 0; - let height = svg.height() || quantity(options.height).value || 0; + let width = + options.viewBox?.width || svg.width() || quantity(options.width).value || 0; + let height = + options.viewBox?.height || + svg.height() || + quantity(options.height).value || + 0; const normalizedPadding = normalizePadding(options.chartPadding); // If settings were to small to cope with offset (legacy) and padding, we'll adjust diff --git a/src/core/types.ts b/src/core/types.ts index 53d3b4f3..a6fbefb1 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -25,6 +25,11 @@ export type Plugin = (chart: any, options?: any) => void; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type Meta = any; +export interface ViewBox { + width: number; + height: number; +} + export interface Options< TXAxisOptions = AxisOptions, TYAxisOptions = TXAxisOptions @@ -67,6 +72,10 @@ export interface Options< classNames?: Record; // eslint-disable-next-line @typescript-eslint/no-explicit-any plugins?: (Plugin | [Plugin, any])[]; + /** + * Define the ViewBox for an SVG, this is optional and only required if you need a scalable chart. This should be used together with responsive options to ensure a proper text size. + */ + viewBox?: ViewBox; } export interface AxisOptions { From 2470286d3e9ee0d1ec67bbfce81cdcf82cdc1145 Mon Sep 17 00:00:00 2001 From: Michael Iwersen Date: Sun, 26 Mar 2023 20:49:35 +0200 Subject: [PATCH 2/3] feat(SVG): added support for viewport to BarChart and PieChart --- src/charts/BarChart/BarChart.stories.ts | 43 +++++++++++++++++++++++++ src/charts/BarChart/BarChart.ts | 3 +- src/charts/PieChart/PieChart.stories.ts | 31 ++++++++++++++++++ src/charts/PieChart/PieChart.ts | 3 +- 4 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/charts/BarChart/BarChart.stories.ts b/src/charts/BarChart/BarChart.stories.ts index e61be1a1..2a468f11 100644 --- a/src/charts/BarChart/BarChart.stories.ts +++ b/src/charts/BarChart/BarChart.stories.ts @@ -445,3 +445,46 @@ export function AccumulateRelativeStack() { return root; } + +export function ViewBox() { + const root = document.createElement('div'); + + new BarChart( + root, + { + series: [ + [ + { x: 1, y: 1 }, + { x: 3, y: 5 } + ] + ] + }, + { + axisX: { + type: AutoScaleAxis, + onlyInteger: true + }, + axisY: { + type: AutoScaleAxis, + onlyInteger: true + }, + viewBox: { + width: 800, + height: 300 + } + }, + [ + [ + 'screen and (max-width: 575px)', + { + viewBox: { + width: 400, + height: 150 + } + } + ] + ] + ); + + return root; +} diff --git a/src/charts/BarChart/BarChart.ts b/src/charts/BarChart/BarChart.ts index 1dec901b..7a22758e 100644 --- a/src/charts/BarChart/BarChart.ts +++ b/src/charts/BarChart/BarChart.ts @@ -217,7 +217,8 @@ export class BarChart extends BaseChart { options.width, options.height, options.classNames.chart + - (options.horizontalBars ? ' ' + options.classNames.horizontalBars : '') + (options.horizontalBars ? ' ' + options.classNames.horizontalBars : ''), + options.viewBox ); const highLow = options.stackBars && diff --git a/src/charts/PieChart/PieChart.stories.ts b/src/charts/PieChart/PieChart.stories.ts index 0aa01286..387660cf 100644 --- a/src/charts/PieChart/PieChart.stories.ts +++ b/src/charts/PieChart/PieChart.stories.ts @@ -186,3 +186,34 @@ export function Solid() { return root; } + +export function ViewBox() { + const root = document.createElement('div'); + + new PieChart( + root, + { + series: [5, 3, 4] + }, + { + chartPadding: 10, + viewBox: { + width: 350, + height: 350 + } + }, + [ + [ + 'screen and (max-width: 575px)', + { + viewBox: { + width: 250, + height: 250 + } + } + ] + ] + ); + + return root; +} diff --git a/src/charts/PieChart/PieChart.ts b/src/charts/PieChart/PieChart.ts index 084957b8..31d1b982 100644 --- a/src/charts/PieChart/PieChart.ts +++ b/src/charts/PieChart/PieChart.ts @@ -209,7 +209,8 @@ export class PieChart extends BaseChart { options.height, options.donut ? options.classNames.chartDonut - : options.classNames.chartPie + : options.classNames.chartPie, + options.viewBox ); this.svg = svg; From 1fa9637801bb262314cb623d762aef96b4a71424 Mon Sep 17 00:00:00 2001 From: Michael Iwersen Date: Wed, 29 Mar 2023 10:24:34 +0200 Subject: [PATCH 3/3] feat(SVG): added tests --- src/core/creation.spec.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/core/creation.spec.ts b/src/core/creation.spec.ts index acd85e61..ea483abc 100644 --- a/src/core/creation.spec.ts +++ b/src/core/creation.spec.ts @@ -30,6 +30,7 @@ describe('Core', () => { expect(svg).toBeDefined(); expect(svg.classes()).toContain('ct-fish-bar'); + expect(svg.attr('viewBox')).toBeFalsy(); expect(container).toContainElement(document.querySelector('#foo')); expect(container).toContainElement(document.querySelector('#bar')); }); @@ -53,6 +54,27 @@ describe('Core', () => { document.querySelector('.ct-snake-bar') ); }); + + it('should add viewBox to svg elements', () => { + const fixture = addMockWrapper(` +
+
+ `); + + const container: any = + fixture.wrapper.querySelector('#chart-container'); + const svg = createSvg(container, '500px', '400px', 'ct-fish-bar', { + width: 300, + height: 200 + }); + + expect(svg).toBeDefined(); + expect(svg.classes()).toContain('ct-fish-bar'); + expect(svg.attr('viewBox')).toEqual('0 0 300 200'); + expect(container).toContainElement( + document.querySelector('.ct-fish-bar') + ); + }); }); describe('createGrid', () => {