From 2f2cea137f48d6d65abe5747b64cf6854f12b168 Mon Sep 17 00:00:00 2001 From: MarcusNotheis Date: Wed, 17 Jul 2019 13:43:59 +0200 Subject: [PATCH 1/9] refactor(Charts): Update ref API and new styling BREAKING CHANGE `innerChartRef` is replaced by `ref` BREAKING CHANGE `sap_belize` and `sap_belize_plus` stylings are not longer exported BREAKING CHANGE Name of color variables changed. Now `sapUiChartAccent1` to `sapUiChartAccent12` plus `sapUiChartGood`, `sapUiChartBad` and `sapUiChartHighlight` are available --- packages/charts/package.json | 1 + .../src/components/BarChart/BarChart.test.tsx | 8 +- .../charts/src/components/BarChart/index.tsx | 143 ++++++------------ .../ChartContainer/withChartContainer.tsx | 52 ------- .../components/ColumnChart/demo.stories.tsx | 2 +- .../src/components/ColumnChart/index.tsx | 135 +++++------------ .../components/DonutChart/demo.stories.tsx | 2 +- .../src/components/DonutChart/index.tsx | 66 ++++---- .../charts/src/components/LineChart/index.tsx | 57 ++++--- .../charts/src/components/Loader/index.tsx | 7 - .../components/MicroBarChart/demo.stories.tsx | 2 +- .../src/components/MicroBarChart/index.tsx | 122 +++++++-------- .../charts/src/components/PieChart/index.tsx | 62 ++++---- .../src/components/RadarChart/index.tsx | 49 +++--- .../src/components/RadialChart/index.tsx | 141 ++++++++--------- packages/charts/src/config.ts | 1 - packages/charts/src/index.ts | 15 +- .../charts/src/interfaces/ChartBaseProps.ts | 5 +- .../src/interfaces/ChartInternalProps.ts | 4 - .../ChartContainer/withChartContainer.tsx | 54 +++++++ .../Loader/Loader.jss.ts | 2 +- packages/charts/src/internal/Loader/index.tsx | 17 +++ packages/charts/src/internal/Placeholder.tsx | 18 +++ packages/charts/src/themes/sap_belize.ts | 138 +++-------------- packages/charts/src/themes/sap_belize_plus.ts | 138 +++-------------- packages/charts/src/themes/sap_fiori_3.ts | 20 +++ .../charts/src/themes/sap_fiori_3_dark.ts | 20 +++ packages/charts/src/themes/themeMap.ts | 23 +++ packages/charts/src/util/populateData.ts | 42 +---- packages/charts/src/util/utils.ts | 3 +- yarn.lock | 23 ++- 31 files changed, 560 insertions(+), 812 deletions(-) delete mode 100644 packages/charts/src/components/ChartContainer/withChartContainer.tsx delete mode 100644 packages/charts/src/components/Loader/index.tsx delete mode 100644 packages/charts/src/interfaces/ChartInternalProps.ts create mode 100644 packages/charts/src/internal/ChartContainer/withChartContainer.tsx rename packages/charts/src/{components => internal}/Loader/Loader.jss.ts (96%) create mode 100644 packages/charts/src/internal/Loader/index.tsx create mode 100644 packages/charts/src/internal/Placeholder.tsx create mode 100644 packages/charts/src/themes/sap_fiori_3.ts create mode 100644 packages/charts/src/themes/sap_fiori_3_dark.ts create mode 100644 packages/charts/src/themes/themeMap.ts diff --git a/packages/charts/package.json b/packages/charts/package.json index 10aa1a13d8d..6481ef37133 100644 --- a/packages/charts/package.json +++ b/packages/charts/package.json @@ -20,6 +20,7 @@ "@ui5/webcomponents-react-base": "0.4.1-rc.0", "chart.js": "^2.8.0", "chartjs-plugin-datalabels": "^0.6.0", + "get-best-contrast-color": "^0.3.1", "is-mergeable-object": "^1.1.0", "react-chartjs-2": "^2.7.6", "react-content-loader": "^4.2.1" diff --git a/packages/charts/src/components/BarChart/BarChart.test.tsx b/packages/charts/src/components/BarChart/BarChart.test.tsx index 5604aa4d1f3..47b6b93cd78 100644 --- a/packages/charts/src/components/BarChart/BarChart.test.tsx +++ b/packages/charts/src/components/BarChart/BarChart.test.tsx @@ -1,4 +1,4 @@ -import { renderThemedComponent } from '@shared/tests/utils'; +import { mountThemedComponent, renderThemedComponent } from '@shared/tests/utils'; import * as React from 'react'; import { datasets, labels, singleDataset } from '../../test/resources/ChartProps'; import { BarChart } from './index'; @@ -16,6 +16,12 @@ describe('BarChart', () => { renderThemedComponent( `${d}%`} />); }); + test('with Ref', () => { + const ref = React.createRef(); + mountThemedComponent(); + expect(ref.current.hasOwnProperty('chartInstance')).toBe(true); + }); + test('stacked', () => { renderThemedComponent( { - static defaultProps = { - ...ChartBaseDefaultProps, - internalNoMerge: true - }; - - static LoadingPlaceholder = BarChartPlaceholder; - - // private static checkIfDataLabelIsInScale(context) { - // const chartElement = getCurrentChartElementFromContext(context); - // const maxXAxis = chartElement._xScale.width + chartElement._xScale.left; - // const chartWidth = chartElement._model.x; - // return chartWidth / maxXAxis > 0.9; - // } - - getAnchor = (context) => { - const { valueAxisFormatter } = this.props; - - try { - const datasetMeta = context.chart.getDatasetMeta(context.datasetIndex); - const dataSetLength = context.chart.data.datasets.length; - const xAxisId = datasetMeta.xAxisID; - const yAxisId = datasetMeta.yAxisID; - - const xAxis = context.chart.scales[xAxisId]; - const yAxis = context.chart.scales[yAxisId]; - if (xAxis.options.stacked) { - if (!yAxis.options.stacked) { - return 'end'; - } - if (dataSetLength - 1 === context.datasetIndex) { - // highest stack - return 'end'; - } else { - const chartElement = datasetMeta.data[context.dataIndex]; - const barWidth = Math.abs(chartElement._model.base - chartElement._model.x); - const text = valueAxisFormatter(context.dataset.data[context.dataIndex]); - const textWidth = getTextWidth(text); - if (barWidth < 1.5 * textWidth) { - // arbitrary estimate - return 'start'; - } - - return 'center'; - } - } - } catch (e) { - Logger.log(LOG_LEVEL.WARNING, e.message); - } - return 'end'; - }; - - render() { +const BarChart = withChartContainer( + forwardRef((props: BarChartPropTypes, ref: Ref) => { const { labels, datasets, - theme, options, categoryAxisFormatter, valueAxisFormatter, getDatasetAtEvent, getElementAtEvent, - colors - } = this.props as BarChartPropTypes & ChartInternalProps; + colors, + width, + height + } = props as BarChartPropTypes; - const bar = populateData(labels, datasets, colors, theme.theme); + const theme: any = useTheme(); + const data = populateData(labels, datasets, colors, theme.theme); const mergedOptions = mergeConfig( { @@ -112,33 +62,25 @@ export class BarChart extends PureComponent { }, plugins: { datalabels: { - // display: (context) => { - // const anchor = this.getAnchor(context); - // if (anchor === 'start') { - // // edge case - // const datasetMeta = context.chart.getDatasetMeta(context.datasetIndex); - // const chartElement = datasetMeta.data[context.dataIndex]; - // const barWidth = Math.abs(chartElement._model.base - chartElement._model.x); - // const text = valueAxisFormatter(context.dataset.data[context.dataIndex]); - // const textWidth = getTextWidth(text); - // if (barWidth < textWidth - 5) { - // // arbitrary 5px tolerance - // return false; - // } - // } - // return true; - // }, - anchor: this.getAnchor, - align: 'end', - offset: 0, + anchor: 'end', + align: 'start', + clip: true, + display: (context) => { + const datasetMeta = context.chart.getDatasetMeta(context.datasetIndex); + const dataMeta = datasetMeta.data[context.dataIndex]; + const width = dataMeta._view.x - dataMeta._view.base; + const formattedValue = valueAxisFormatter(context.dataset.data[context.dataIndex]); + const textWidth = getTextWidth(formattedValue) + 4; // offset + return width >= textWidth; + }, formatter: valueAxisFormatter, color: (context) => { - const anchor = this.getAnchor(context); - if (anchor === 'end') { - return '#666'; - } else { - return '#fff'; - } + const datasetMeta = context.chart.getDatasetMeta(context.datasetIndex); + const dataMeta = datasetMeta.data[context.dataIndex]; + return bestContrast(dataMeta._view.backgroundColor, [ + /* sapUiBaseText */ '#32363a', + /* sapUiContentContrastTextColor */ '#ffffff' + ]); } } } @@ -148,16 +90,23 @@ export class BarChart extends PureComponent { return ( ); - } -} + }) +); + +// @ts-ignore +BarChart.LoadingPlaceholder = BarChartPlaceholder; +BarChart.defaultProps = { + ...ChartBaseDefaultProps +}; +BarChart.displayName = 'BarChart'; + +export { BarChart }; diff --git a/packages/charts/src/components/ChartContainer/withChartContainer.tsx b/packages/charts/src/components/ChartContainer/withChartContainer.tsx deleted file mode 100644 index b218a1460da..00000000000 --- a/packages/charts/src/components/ChartContainer/withChartContainer.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React, { ComponentType } from 'react'; -import { withStyles } from '@ui5/webcomponents-react-base'; -import hoistNonReactStatics from 'hoist-non-react-statics'; -import { DEFAULT_OPTIONS } from '../../config'; -import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; -import { mergeConfig } from '../../util/utils'; -import { Loader } from '../Loader'; - -const styles = { - chart: { - position: 'relative', - paddingTop: '6px', - width: (props) => `${props.width}px`, - height: (props) => `${props.height}px`, - '& canvas': { - maxWidth: (props) => `${props.width}px`, - maxHeight: (props) => `${props.height}px` - }, - '& svg': { - width: (props) => `${props.width}px`, - height: (props) => `${props.height}px` - } - } -}; - -export const withChartContainer = (Component: ComponentType) => { - const ChartContainer = (props) => { - // @ts-ignore - const { options, internalNoMerge, style, className, tooltip, loading, datasets, ...rest } = props as ChartBaseProps; - const mergedOptions = internalNoMerge ? options : mergeConfig(DEFAULT_OPTIONS, options); - - let classes = props.classes.chart; - if (className) { - classes = `${classes} ${className}`; - } - - return ( -
- {loading && datasets.length > 0 && } - {/* - // @ts-ignore */} - {loading && datasets.length === 0 && } - {datasets.length > 0 && } -
- ); - }; - - ChartContainer.defaultProps = Component.defaultProps; - hoistNonReactStatics(ChartContainer, Component); - - return withStyles(styles)(ChartContainer); -}; diff --git a/packages/charts/src/components/ColumnChart/demo.stories.tsx b/packages/charts/src/components/ColumnChart/demo.stories.tsx index 08c5bd6d294..ada972065d1 100644 --- a/packages/charts/src/components/ColumnChart/demo.stories.tsx +++ b/packages/charts/src/components/ColumnChart/demo.stories.tsx @@ -12,7 +12,7 @@ const datasets = [ { label: 'Probable/Committed', - data: [5, 9, 8, 8, 5, 5, 4] + data: [5, 9, 8, 8, 5, 5, 1] } ]; diff --git a/packages/charts/src/components/ColumnChart/index.tsx b/packages/charts/src/components/ColumnChart/index.tsx index 7a89f02e05d..eb01b244c7c 100644 --- a/packages/charts/src/components/ColumnChart/index.tsx +++ b/packages/charts/src/components/ColumnChart/index.tsx @@ -1,82 +1,37 @@ -import React, { PureComponent } from 'react'; +import bestContrast from 'get-best-contrast-color'; +import React, { forwardRef, Ref } from 'react'; import { Bar } from 'react-chartjs-2'; +import { useTheme } from 'react-jss'; import { DEFAULT_OPTIONS } from '../../config'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; -import { ChartInternalProps } from '../../interfaces/ChartInternalProps'; +import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; import { populateData } from '../../util/populateData'; import { formatTooltipLabel, getTextHeight, getTextWidth, mergeConfig } from '../../util/utils'; -import { withChartContainer } from '../ChartContainer/withChartContainer'; import { ColumnChartPlaceholder } from './Placeholder'; export interface ColumnChartPropTypes extends ChartBaseProps {} -@withChartContainer -export class ColumnChart extends PureComponent { - static defaultProps = { - ...ChartBaseDefaultProps - }; - - static LoadingPlaceholder = ColumnChartPlaceholder; - - componentDidMount() { - this.setState({ options: { ...this.props.options } }); - } - - getAnchor = (context) => { - const datasetMeta = context.chart.getDatasetMeta(context.datasetIndex); - const dataSetLength = context.chart.data.datasets.length; - const xAxisId = datasetMeta.xAxisID; - const yAxisId = datasetMeta.yAxisID; - - const xAxis = context.chart.scales[xAxisId]; - const yAxis = context.chart.scales[yAxisId]; - - if (datasetMeta.type === 'bar') { - if (yAxis.options.stacked) { - if (!xAxis.options.stacked) { - return 'end'; - } - - if (dataSetLength - 1 === context.datasetIndex) { - // highest stack - return 'end'; - } else { - const chartElement = datasetMeta.data[context.dataIndex]; - const barHeight = Math.abs(chartElement._model.base - chartElement._model.y); - const textHeight = getTextHeight(); - if (barHeight < 1.5 * textHeight) { - // arbitrary estimate - return 'start'; - } - return 'center'; - } - } - if (Object.keys(context.chart.scales).length > 2) { - return 'center'; - } - } - return 'end'; - }; - - render() { +const ColumnChart = withChartContainer( + forwardRef((props: ColumnChartPropTypes, ref: Ref) => { const { labels, datasets, - theme, categoryAxisFormatter, valueAxisFormatter, getDatasetAtEvent, getElementAtEvent, colors, - options - } = this.props as ColumnChartPropTypes & ChartInternalProps; + options, + width, + height + } = props; - const bar = populateData(labels, datasets, colors, theme.theme); + const theme: any = useTheme(); + const data = populateData(labels, datasets, colors, theme.theme); const mergedOptions = mergeConfig( { - animation: false, layout: { padding: { top: 15 @@ -95,6 +50,7 @@ export class ColumnChart extends PureComponent { { ...DEFAULT_OPTIONS.scales.yAxes[0], ticks: { + ...DEFAULT_OPTIONS.scales.yAxes[0].ticks, callback: valueAxisFormatter } } @@ -108,40 +64,26 @@ export class ColumnChart extends PureComponent { plugins: { datalabels: { display: (context) => { - const anchor = this.getAnchor(context); const datasetMeta = context.chart.getDatasetMeta(context.datasetIndex); - const chartElement = datasetMeta.data[context.dataIndex]; - - if (anchor === 'start') { - // edge case - const barHeight = Math.abs(chartElement._model.base - chartElement._model.y); - const textHeight = getTextHeight(); - if (barHeight < textHeight) { - return false; - } - } - - // check whether label fits in bar - const barWidth = chartElement._model.width; - const text = valueAxisFormatter(context.dataset.data[context.dataIndex]); - const textWidth = getTextWidth(text); - if (barWidth < textWidth) { + const dataMeta = datasetMeta.data[context.dataIndex]; + const height = dataMeta._view.base - dataMeta._view.y; // offset + if (height < getTextHeight() + 6) { return false; } - - return true; + const formattedValue = valueAxisFormatter(context.dataset.data[context.dataIndex]); + const textWidth = getTextWidth(formattedValue); + return textWidth < dataMeta._view.width; }, - anchor: this.getAnchor, - align: 'end', - offset: 0, + anchor: 'end', + align: 'start', formatter: valueAxisFormatter, color: (context) => { - const anchor = this.getAnchor(context); - if (anchor === 'end') { - return '#666'; - } else { - return '#fff'; - } + const datasetMeta = context.chart.getDatasetMeta(context.datasetIndex); + const dataMeta = datasetMeta.data[context.dataIndex]; + return bestContrast(dataMeta._view.backgroundColor, [ + /* sapUiBaseText */ '#32363a', + /* sapUiContentContrastTextColor */ '#ffffff' + ]); } } } @@ -151,16 +93,23 @@ export class ColumnChart extends PureComponent { return ( ); - } -} + }) +); + +// @ts-ignore +ColumnChart.LoadingPlaceholder = ColumnChartPlaceholder; +ColumnChart.defaultProps = { + ...ChartBaseDefaultProps +}; +ColumnChart.displayName = 'ColumnChart'; + +export { ColumnChart }; diff --git a/packages/charts/src/components/DonutChart/demo.stories.tsx b/packages/charts/src/components/DonutChart/demo.stories.tsx index 2f066437e60..ba42a1c41ce 100644 --- a/packages/charts/src/components/DonutChart/demo.stories.tsx +++ b/packages/charts/src/components/DonutChart/demo.stories.tsx @@ -16,7 +16,7 @@ storiesOf('Charts | DonutChart', module) width={300} labels={['Stalled', 'Active']} datasets={[{ data: [65, 45] }]} - colors={['sapUiChartPaletteSemanticBad', 'sapUiChartPaletteSemanticGood']} + colors={['sapUiChartBad', 'sapUiChartGood']} loading={boolean('loading')} /> )) diff --git a/packages/charts/src/components/DonutChart/index.tsx b/packages/charts/src/components/DonutChart/index.tsx index 01d9377ac5b..50bb1f0b588 100644 --- a/packages/charts/src/components/DonutChart/index.tsx +++ b/packages/charts/src/components/DonutChart/index.tsx @@ -1,39 +1,32 @@ -import React, { PureComponent } from 'react'; -import { ChartInternalProps } from '../../interfaces/ChartInternalProps'; +import React, { FC, Ref, forwardRef } from 'react'; +import { Pie } from 'react-chartjs-2'; +import { useTheme } from 'react-jss'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; +import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; +import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; import { populateData } from '../../util/populateData'; import { formatTooltipLabelForPieCharts, mergeConfig } from '../../util/utils'; -import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; -import { Pie } from 'react-chartjs-2'; -import { withChartContainer } from '../ChartContainer/withChartContainer'; import { PieChartPlaceholder } from '../PieChart/Placeholder'; export interface DonutChartPropTypes extends ChartBaseProps {} -@withChartContainer -export class DonutChart extends PureComponent { - static defaultProps = { - ...ChartBaseDefaultProps, - colors: null, - internalNoMerge: true - }; - - static LoadingPlaceholder = PieChartPlaceholder; - - render() { +const DonutChart = withChartContainer( + forwardRef((props: DonutChartPropTypes, ref: Ref) => { const { labels, datasets, colors, - theme, categoryAxisFormatter, getDatasetAtEvent, getElementAtEvent, valueAxisFormatter, - options - } = this.props as DonutChartPropTypes & ChartInternalProps; + options, + width, + height + } = props; - const doughnut = populateData(labels, datasets, colors, theme.theme, true); + const theme: any = useTheme(); + const data = populateData(labels, datasets, colors, theme.theme, true); const mergedOptions = mergeConfig( { @@ -45,13 +38,10 @@ export class DonutChart extends PureComponent { }, plugins: { datalabels: { - anchor: 'center', - align: 'center', - offset: 0, + anchor: 'end', + align: 'end', color: (context) => { - return parseInt(context.dataset.backgroundColor[context.datasetIndex], 16) > 0xffffff / 2 - ? '#666' - : '#fff'; + return /* sapUiBaseText */ '#32363a'; }, formatter: valueAxisFormatter } @@ -62,16 +52,24 @@ export class DonutChart extends PureComponent { return ( ); - } -} + }) +); + +// @ts-ignore +DonutChart.LoadingPlaceholder = PieChartPlaceholder; +DonutChart.defaultProps = { + ...ChartBaseDefaultProps, + colors: null +}; +DonutChart.displayName = 'DonutChart'; + +export { DonutChart }; diff --git a/packages/charts/src/components/LineChart/index.tsx b/packages/charts/src/components/LineChart/index.tsx index 77a403eb1a7..0e07dfaedd3 100644 --- a/packages/charts/src/components/LineChart/index.tsx +++ b/packages/charts/src/components/LineChart/index.tsx @@ -1,46 +1,39 @@ -import React, { PureComponent } from 'react'; +import React, { forwardRef, Ref } from 'react'; import { Line } from 'react-chartjs-2'; -import { populateData } from '../../util/populateData'; +import { useTheme } from 'react-jss'; import { DEFAULT_OPTIONS } from '../../config'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; -import { ChartInternalProps } from '../../interfaces/ChartInternalProps'; -import { formatTooltipLabel, mergeConfig } from '../../util/utils'; +import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; -import { withChartContainer } from '../ChartContainer/withChartContainer'; +import { populateData } from '../../util/populateData'; +import { formatTooltipLabel, mergeConfig } from '../../util/utils'; import { LineChartPlaceholder } from './Placeholder'; export interface LineChartPropTypes extends ChartBaseProps {} -@withChartContainer -export class LineChart extends PureComponent { - static defaultProps = { - ...ChartBaseDefaultProps - }; - - static LoadingPlaceholder = LineChartPlaceholder; - - render() { +const LineChart = withChartContainer( + forwardRef((props: LineChartPropTypes, ref: Ref) => { const { labels, datasets, colors, - width, - height, options, valueAxisFormatter, categoryAxisFormatter, getElementAtEvent, getDatasetAtEvent, - theme - } = this.props as LineChartPropTypes & ChartInternalProps; + width, + height + } = props; const chartOptions = mergeConfig( { scales: { yAxes: [ { - display: false, + ...DEFAULT_OPTIONS.scales.yAxes[0], ticks: { + ...DEFAULT_OPTIONS.scales.yAxes[0].ticks, callback: valueAxisFormatter } } @@ -54,7 +47,6 @@ export class LineChart extends PureComponent { }, plugins: { datalabels: { - // offset: 100 formatter: valueAxisFormatter } } @@ -62,22 +54,27 @@ export class LineChart extends PureComponent { options ); - const line = populateData(labels, datasets, colors, theme.theme); - line.datasets.forEach((dataset) => { - dataset.backgroundColor = 'transparent'; - }); + const theme: any = useTheme(); + const data = populateData(labels, datasets, colors, theme.theme); return ( ); - } -} + }) +); + +// @ts-ignore +LineChart.LoadingPlaceholder = LineChartPlaceholder; +LineChart.defaultProps = { + ...ChartBaseDefaultProps +}; +LineChart.displayName = 'LineChart'; + +export { LineChart }; diff --git a/packages/charts/src/components/Loader/index.tsx b/packages/charts/src/components/Loader/index.tsx deleted file mode 100644 index 80a50b55711..00000000000 --- a/packages/charts/src/components/Loader/index.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import { withStyles } from '@ui5/webcomponents-react-base'; -import * as React from 'react'; -import { LoaderStyles } from './Loader.jss'; - -export const Loader = withStyles(LoaderStyles)(({ classes }) => ( -
Loading...
-)); diff --git a/packages/charts/src/components/MicroBarChart/demo.stories.tsx b/packages/charts/src/components/MicroBarChart/demo.stories.tsx index 53fa5958954..97334ec687e 100644 --- a/packages/charts/src/components/MicroBarChart/demo.stories.tsx +++ b/packages/charts/src/components/MicroBarChart/demo.stories.tsx @@ -9,7 +9,7 @@ const dataset = [ { value: 40, label: 'Bar Number Four' }, { value: 50, label: 'Bar Number Five' } ]; -const colors = ['sapUiChartPaletteSemanticBad', 'pink', '#de890d', 'rgb(33, 9, 205)', 'green']; +const colors = ['sapUiChartBad', 'pink', '#de890d', 'rgb(33, 9, 205)', 'green']; const valueFormatter = (value) => `${value}$`; const labelFormatter = (value) => `${value} in Dollar`; diff --git a/packages/charts/src/components/MicroBarChart/index.tsx b/packages/charts/src/components/MicroBarChart/index.tsx index c81d297b51c..4fd7c8e19ba 100644 --- a/packages/charts/src/components/MicroBarChart/index.tsx +++ b/packages/charts/src/components/MicroBarChart/index.tsx @@ -1,11 +1,10 @@ -import { withStyles } from '@ui5/webcomponents-react-base'; -import React, { CSSProperties, PureComponent } from 'react'; -import { ChartInternalProps } from '../../interfaces/ChartInternalProps'; +import React, { CSSProperties, FC, forwardRef, Ref } from 'react'; +// @ts-ignore +import { createUseStyles, useTheme } from 'react-jss'; import { CommonProps } from '../../interfaces/CommonProps'; -import BelizeColors from '../../themes/sap_belize'; -import { populateDataMicroChart } from '../../util/populateData'; +import { resolveColors } from '../../util/populateData'; -const BarStyles = () => ({ +const BarStyles = { container: { display: 'flex', flexDirection: 'column' @@ -20,7 +19,7 @@ const BarStyles = () => ({ valueBar: { height: '4px' }, fillUp: { height: '4px', - backgroundColor: BelizeColors.sapUiChartPaletteSemanticNeutralLight3, + backgroundColor: '#f1f2f3', flexGrow: 1 }, label: { @@ -43,7 +42,9 @@ const BarStyles = () => ({ whiteSpace: 'pre-line', color: '#333333' } -}); +}; + +const useStyles = createUseStyles(BarStyles); interface DataItems { value: number; @@ -59,64 +60,53 @@ export interface MicroBarChartPropTypes extends CommonProps { labelFormatter?: (value: any) => string | number; } -interface InternalProps extends MicroBarChartPropTypes, ChartInternalProps {} +const MicroBarChart = forwardRef((props: MicroBarChartPropTypes, ref: Ref) => { + const { className, dataset, colors, maxWidth, visibleDatasetCount, valueFormatter, labelFormatter, style } = props; + const classes = useStyles(); + const theme: any = useTheme(); + const visibleDatasetArray = visibleDatasetCount ? dataset.slice(0, visibleDatasetCount) : dataset; + + const colorPalette = resolveColors(colors, theme.theme); -@withStyles(BarStyles) -export class MicroBarChart extends PureComponent { - static defaultProps = { - valueFormatter: (value) => value, - labelFormatter: (value) => value - }; + const maxValue = Math.max(...dataset.map((item) => item.value)); - render() { - const { - className, - dataset, - colors, - maxWidth, - visibleDatasetCount, - valueFormatter, - labelFormatter, - classes, - style, - theme - } = this.props; - const visibleDatasetArray = dataset.slice(0, !visibleDatasetCount ? dataset.length : visibleDatasetCount); - const maxValue = Math.max(...dataset.map((item) => item.value)); - const colorPallets = populateDataMicroChart(colors, theme.theme); - const setColor = (index) => - colorPallets[index] ? colorPallets[index] : populateDataMicroChart(null, theme.theme)[index]; - return ( -
- {visibleDatasetArray.map((item, index) => { - return ( -
-
- {labelFormatter(item.label)} - - {valueFormatter(item.value)} - -
-
-
-
-
+ return ( +
+ {visibleDatasetArray.map((item, index) => { + return ( +
+
+ {labelFormatter(item.label)} + + {valueFormatter(item.value)} +
- ); - })} -
- ); - } -} +
+
+
+
+
+ ); + })} +
+ ); +}); + +MicroBarChart.displayName = 'MicroBarChart'; +MicroBarChart.defaultProps = { + valueFormatter: (value) => value, + labelFormatter: (value) => value +}; + +export { MicroBarChart }; diff --git a/packages/charts/src/components/PieChart/index.tsx b/packages/charts/src/components/PieChart/index.tsx index 2927698a265..ab7308c7d1b 100644 --- a/packages/charts/src/components/PieChart/index.tsx +++ b/packages/charts/src/components/PieChart/index.tsx @@ -1,38 +1,32 @@ -import React, { PureComponent } from 'react'; +import React, { FC, forwardRef, Ref } from 'react'; import { Pie } from 'react-chartjs-2'; -import { populateData } from '../../util/populateData'; -import { ChartInternalProps } from '../../interfaces/ChartInternalProps'; +import { useTheme } from 'react-jss'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; +import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; +import { populateData } from '../../util/populateData'; import { formatTooltipLabelForPieCharts, mergeConfig } from '../../util/utils'; -import { withChartContainer } from '../ChartContainer/withChartContainer'; import { PieChartPlaceholder } from './Placeholder'; export interface PieChartPropTypes extends ChartBaseProps {} -@withChartContainer -export class PieChart extends PureComponent { - static defaultProps = { - ...ChartBaseDefaultProps, - internalNoMerge: true - }; - - static LoadingPlaceholder = PieChartPlaceholder; - - render() { +const PieChart = withChartContainer( + forwardRef((props: PieChartPropTypes, ref: Ref) => { const { labels, datasets, colors, - theme, categoryAxisFormatter, getDatasetAtEvent, getElementAtEvent, valueAxisFormatter, - options - } = this.props as PieChartPropTypes & ChartInternalProps; + options, + width, + height + } = props; - const doughnut = populateData(labels, datasets, colors, theme.theme, true); + const theme: any = useTheme(); + const data = populateData(labels, datasets, colors, theme.theme, true); const mergedOptions = mergeConfig( { @@ -43,13 +37,10 @@ export class PieChart extends PureComponent { }, plugins: { datalabels: { - anchor: 'center', - align: 'center', - offset: 0, + anchor: 'end', + align: 'end', color: (context) => { - return parseInt(context.dataset.backgroundColor[context.datasetIndex], 16) > 0xffffff / 2 - ? '#666' - : '#fff'; + return /* sapUiBaseText */ '#32363a'; }, formatter: valueAxisFormatter } @@ -60,16 +51,23 @@ export class PieChart extends PureComponent { return ( ); - } -} + }) +); + +// @ts-ignore +PieChart.LoadingPlaceholder = PieChartPlaceholder; +PieChart.defaultProps = { + ...ChartBaseDefaultProps +}; +PieChart.displayName = 'PieChart'; + +export { PieChart }; diff --git a/packages/charts/src/components/RadarChart/index.tsx b/packages/charts/src/components/RadarChart/index.tsx index aa5fd52c8f3..159d0970310 100644 --- a/packages/charts/src/components/RadarChart/index.tsx +++ b/packages/charts/src/components/RadarChart/index.tsx @@ -1,26 +1,19 @@ -import React, { PureComponent } from 'react'; +import React, { FC, forwardRef, Ref } from 'react'; import { Radar } from 'react-chartjs-2'; -import { populateData } from '../../util/populateData'; -import { ChartInternalProps } from '../../interfaces/ChartInternalProps'; -import { formatTooltipLabel, mergeConfig } from '../../util/utils'; +import { useTheme } from 'react-jss'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; +import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; -import { HSLColor } from '@ui5/webcomponents-react-base'; -import { withChartContainer } from '../ChartContainer/withChartContainer'; +import { populateData } from '../../util/populateData'; +import { formatTooltipLabel, mergeConfig } from '../../util/utils'; export interface RadarChartPropTypes extends ChartBaseProps {} -@withChartContainer -export class RadarChart extends PureComponent { - static defaultProps = { - ...ChartBaseDefaultProps - }; - - render() { +const RadarChart: FC = withChartContainer( + forwardRef((props: RadarChartPropTypes, ref: Ref) => { const { labels, datasets, - theme, width, height, options, @@ -29,15 +22,10 @@ export class RadarChart extends PureComponent { getElementAtEvent, valueAxisFormatter, colors - } = this.props as RadarChartPropTypes & ChartInternalProps; + } = props; - const bar = populateData(labels, datasets, colors, theme.theme); - bar.datasets.map( - (set) => - (set.backgroundColor = HSLColor.of(set.backgroundColor) - .setAlpha(0.3) - .toString()) - ); + const theme: any = useTheme(); + const data = populateData(labels, datasets, colors, theme.theme); const mergedOptions = mergeConfig( { @@ -60,16 +48,21 @@ export class RadarChart extends PureComponent { return ( ); - } -} + }) +); + +RadarChart.defaultProps = { + ...ChartBaseDefaultProps +}; +RadarChart.displayName = 'RadarChart'; + +export { RadarChart }; diff --git a/packages/charts/src/components/RadialChart/index.tsx b/packages/charts/src/components/RadialChart/index.tsx index 9edd386d94c..e63ee3c597f 100644 --- a/packages/charts/src/components/RadialChart/index.tsx +++ b/packages/charts/src/components/RadialChart/index.tsx @@ -1,7 +1,8 @@ -import { StyleClassHelper, withStyles } from '@ui5/webcomponents-react-base'; +import { StyleClassHelper } from '@ui5/webcomponents-react-base'; import { ChartOptions } from 'chart.js'; -import React, { CSSProperties, PureComponent, Ref } from 'react'; -import { ChartInternalProps } from '../../interfaces/ChartInternalProps'; +import React, { CSSProperties, forwardRef, Ref } from 'react'; +// @ts-ignore +import { createUseStyles } from 'react-jss'; import { CommonProps } from '../../interfaces/CommonProps'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; import { mergeConfig } from '../../util/utils'; @@ -15,7 +16,6 @@ export interface RadialChartPropTypes extends CommonProps { options?: ChartOptions; height?: number; width?: number; - innerChartRef?: Ref; } const styles = { @@ -36,76 +36,77 @@ const styles = { } }; -@withStyles(styles) -export class RadialChart extends PureComponent { - static defaultProps = { - ...ChartBaseDefaultProps, - maxValue: 100, - colors: ['sapUiChartPaletteQualitativeHue1', 'sapUiChartPaletteSemanticNeutralLight3'], - internalNoMerge: true - }; +const useStyles = createUseStyles(styles); - render() { - const { maxValue, value, displayValue, classes, style, className, colors, options, width, height } = this - .props as RadialChartPropTypes & ChartInternalProps; +const RadialChart = forwardRef((props: RadialChartPropTypes, ref: Ref) => { + const { maxValue, value, displayValue, style, className, colors, options, width, height } = props; - const data = [value, maxValue - value]; - const mergedOptions = mergeConfig( - { - cutoutPercentage: 90, - tooltips: { - enabled: false - }, - legend: { - display: false - }, - plugins: { - datalabels: false - } + const data = [value, maxValue - value]; + const mergedOptions = mergeConfig( + { + cutoutPercentage: 90, + tooltips: { + enabled: false + }, + legend: { + display: false }, - options - ); + plugins: { + datalabels: false + } + }, + options + ); - const radialChartContainerStyles = { - width: `${width}px`, - height: `${height}px`, - ...style - }; + const classes = useStyles(); - const outerClasses = StyleClassHelper.of(classes.radialChart); - if (className) { - outerClasses.put(className); - } + const radialChartContainerStyles = { + width: `${width}px`, + height: `${height}px`, + ...style + }; - return ( -
- -
-

- {displayValue as string} -

-
-
- ); + const outerClasses = StyleClassHelper.of(classes.radialChart); + if (className) { + outerClasses.put(className); } -} + + return ( +
+ +
+

+ {displayValue as string} +

+
+
+ ); +}); + +RadialChart.defaultProps = { + ...ChartBaseDefaultProps, + maxValue: 100, + colors: ['#5899DA', '#adbcc3'] +}; + +export { RadialChart }; diff --git a/packages/charts/src/config.ts b/packages/charts/src/config.ts index 08f85e41b1d..4209324022e 100644 --- a/packages/charts/src/config.ts +++ b/packages/charts/src/config.ts @@ -2,7 +2,6 @@ import DataLabels from 'chartjs-plugin-datalabels'; import { defaults, pluginService } from 'chart.js'; export const defaultFont = { - // family: "'72', Arial, Helvetica, sans-serif", family: '"72", Arial, Helvetica, sans-serif', size: '12' }; diff --git a/packages/charts/src/index.ts b/packages/charts/src/index.ts index 6cf364c60bb..064fe0f34f4 100644 --- a/packages/charts/src/index.ts +++ b/packages/charts/src/index.ts @@ -7,18 +7,5 @@ import { PieChart } from './components/PieChart'; import { RadarChart } from './components/RadarChart'; import { RadialChart } from './components/RadialChart'; import { MicroBarChart } from './components/MicroBarChart'; -import sap_belize from './themes/sap_belize'; -import sap_belize_plus from './themes/sap_belize_plus'; -export { - ColumnChart, - DonutChart, - BarChart, - LineChart, - PieChart, - RadarChart, - RadialChart, - MicroBarChart, - sap_belize, - sap_belize_plus -}; +export { ColumnChart, DonutChart, BarChart, LineChart, PieChart, RadarChart, RadialChart, MicroBarChart }; diff --git a/packages/charts/src/interfaces/ChartBaseProps.ts b/packages/charts/src/interfaces/ChartBaseProps.ts index acf1388fa3f..7e5898a122a 100644 --- a/packages/charts/src/interfaces/ChartBaseProps.ts +++ b/packages/charts/src/interfaces/ChartBaseProps.ts @@ -11,8 +11,7 @@ export interface ChartBaseProps extends CommonProps { options?: ChartOptions; categoryAxisFormatter?: (value: any) => string | number; valueAxisFormatter?: (value: any) => string | number; - getDatasetAtEvent?: (dataset: ChartDataSets[], event: Event) => void; - getElementAtEvent?: (dataset: ChartDataSets[], event: Event) => void; - innerChartRef?: Ref; + getDatasetAtEvent?: (dataset: ChartDataSets[], event?: Event) => void; + getElementAtEvent?: (dataset: ChartDataSets[], event?: Event) => void; loading?: boolean; } diff --git a/packages/charts/src/interfaces/ChartInternalProps.ts b/packages/charts/src/interfaces/ChartInternalProps.ts deleted file mode 100644 index 05a4a933ca3..00000000000 --- a/packages/charts/src/interfaces/ChartInternalProps.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface ChartInternalProps { - theme?: any; - classes?: any; -} diff --git a/packages/charts/src/internal/ChartContainer/withChartContainer.tsx b/packages/charts/src/internal/ChartContainer/withChartContainer.tsx new file mode 100644 index 00000000000..a784bd65544 --- /dev/null +++ b/packages/charts/src/internal/ChartContainer/withChartContainer.tsx @@ -0,0 +1,54 @@ +import React, { ComponentType, CSSProperties, forwardRef, Ref } from 'react'; +// @ts-ignore +import { createUseStyles } from 'react-jss'; +import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; +import { getLoadingState } from '../Placeholder'; + +const styles = { + chart: { + '& canvas': { + maxWidth: (props) => `${props.width}px`, + maxHeight: (props) => `${props.height}px` + }, + '& svg': { + width: (props) => `${props.width}px`, + height: (props) => `${props.height}px` + } + } +}; + +const useStyles = createUseStyles(styles); + +export const withChartContainer = (Component: ComponentType) => { + const ChartContainer = forwardRef((props: ChartBaseProps, ref: Ref) => { + const { style, className, tooltip, loading, datasets, slot, ...rest } = props; + + const classes = useStyles(props); + let classNames = classes.chart; + if (className) { + classNames = `${classNames} ${className}`; + } + + const loadingIndicator = getLoadingState(loading, datasets, (Component as any).LoadingPlaceholder); + + const inlineStyle: CSSProperties = { + position: 'relative', + paddingTop: '6px', + width: `${props.width}px`, + height: `${props.height}px`, + ...style + }; + + return ( +
+ {loadingIndicator} + {datasets.length > 0 && } +
+ ); + }); + + ChartContainer.defaultProps = Component.defaultProps; + ChartContainer.displayName = Component.displayName; + + return ChartContainer; +}; diff --git a/packages/charts/src/components/Loader/Loader.jss.ts b/packages/charts/src/internal/Loader/Loader.jss.ts similarity index 96% rename from packages/charts/src/components/Loader/Loader.jss.ts rename to packages/charts/src/internal/Loader/Loader.jss.ts index 13c1b041654..5e61343ec39 100644 --- a/packages/charts/src/components/Loader/Loader.jss.ts +++ b/packages/charts/src/internal/Loader/Loader.jss.ts @@ -19,7 +19,7 @@ export const LoaderStyles = { left: '-256px', width: '256px', height: '4px', - animation: 'load 1.5s linear infinite' + animation: '$load 1.5s linear infinite' } }, loaderLight: { diff --git a/packages/charts/src/internal/Loader/index.tsx b/packages/charts/src/internal/Loader/index.tsx new file mode 100644 index 00000000000..24cde021a16 --- /dev/null +++ b/packages/charts/src/internal/Loader/index.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +// @ts-ignore +import { createUseStyles } from 'react-jss'; +import { LoaderStyles } from './Loader.jss'; + +const useStyles = createUseStyles(LoaderStyles); + +const Loader = React.forwardRef((props, ref: React.RefObject) => { + const classes = useStyles(); + return ( +
+ Loading... +
+ ); +}); + +export { Loader }; diff --git a/packages/charts/src/internal/Placeholder.tsx b/packages/charts/src/internal/Placeholder.tsx new file mode 100644 index 00000000000..45984f3c082 --- /dev/null +++ b/packages/charts/src/internal/Placeholder.tsx @@ -0,0 +1,18 @@ +import { Loader } from './Loader'; +import React from 'react'; + +export const getLoadingState = (loading, datasets, Placeholder) => { + if (!loading) { + return null; + } + + if (loading && datasets.length > 0) { + return ; + } + + if (loading && datasets.length === 0) { + return ; + } + + return null; +}; diff --git a/packages/charts/src/themes/sap_belize.ts b/packages/charts/src/themes/sap_belize.ts index 7c6841f53a2..c1a01d2a9e9 100644 --- a/packages/charts/src/themes/sap_belize.ts +++ b/packages/charts/src/themes/sap_belize.ts @@ -1,120 +1,20 @@ -export default { - sapUiChartPaletteQualitativeHue1: '#5899DA', - sapUiChartPaletteQualitativeHue2: '#E8743B', - sapUiChartPaletteQualitativeHue3: '#19A979', - sapUiChartPaletteQualitativeHue4: '#ED4A7B', - sapUiChartPaletteQualitativeHue5: '#945ECF', - sapUiChartPaletteQualitativeHue6: '#13A4B4', - sapUiChartPaletteQualitativeHue7: '#525DF4', - sapUiChartPaletteQualitativeHue8: '#BF399E', - sapUiChartPaletteQualitativeHue9: '#6C8893', - sapUiChartPaletteQualitativeHue10: '#EE6868', - sapUiChartPaletteQualitativeHue11: '#2F6497', - sapUiChartPaletteQualitativeHue12: '#1866b4', - sapUiChartPaletteQualitativeHue13: '#cc4300', - sapUiChartPaletteQualitativeHue14: '#03734d', - sapUiChartPaletteQualitativeHue15: '#d70947', - sapUiChartPaletteQualitativeHue16: '#772acb', - sapUiChartPaletteQualitativeHue17: '#087783', - sapUiChartPaletteQualitativeHue18: '#2531d4', - sapUiChartPaletteQualitativeHue19: '#921473', - sapUiChartPaletteQualitativeHue20: '#3c6372', - sapUiChartPaletteQualitativeHue21: '#d62f2f', - sapUiChartPaletteQualitativeHue22: '#144b7f', - sapUiChartPaletteSemanticBadLight3: '#f99494', - sapUiChartPaletteSemanticBadLight2: '#f66364', - sapUiChartPaletteSemanticBadLight1: '#f33334', - sapUiChartPaletteSemanticBad: '#dc0d0e', - sapUiChartPaletteSemanticBadDark1: '#b90c0d', - sapUiChartPaletteSemanticBadDark2: '#930a0a', - sapUiChartPaletteSemanticCriticalLight3: '#f8cc8c', - sapUiChartPaletteSemanticCriticalLight2: '#f5b04d', - sapUiChartPaletteSemanticCriticalLight1: '#f29b1d', - sapUiChartPaletteSemanticCritical: '#de890d', - sapUiChartPaletteSemanticCriticalDark1: '#c67a0c', - sapUiChartPaletteSemanticCriticalDark2: '#a4650a', - sapUiChartPaletteSemanticGoodLight3: '#a1dbb1', - sapUiChartPaletteSemanticGoodLight2: '#71c989', - sapUiChartPaletteSemanticGoodLight1: '#4cba6b', - sapUiChartPaletteSemanticGood: '#3fa45b', - sapUiChartPaletteSemanticGoodDark1: '#358a4d', - sapUiChartPaletteSemanticGoodDark2: '#2a6d3c', - sapUiChartPaletteSemanticNeutralLight3: '#d5dadc', - sapUiChartPaletteSemanticNeutralLight2: '#bac1c4', - sapUiChartPaletteSemanticNeutralLight1: '#9ea8ad', - sapUiChartPaletteSemanticNeutral: '#848f94', - sapUiChartPaletteSemanticNeutralDark1: '#69767c', - sapUiChartPaletteSemanticNeutralDark2: '#596468', - sapUiChartPaletteSequentialHue1Light3: '#b2d4f5', - sapUiChartPaletteSequentialHue1Light2: '#93bfeb', - sapUiChartPaletteSequentialHue1Light1: '#74abe2', - sapUiChartPaletteSequentialHue1: '#5899DA', - sapUiChartPaletteSequentialHue1Dark1: '#367dc4', - sapUiChartPaletteSequentialHue1Dark2: '#1866b4', - sapUiChartPaletteSequentialHue2Light3: '#fcc3a7', - sapUiChartPaletteSequentialHue2Light2: '#f5aa85', - sapUiChartPaletteSequentialHue2Light1: '#ef8d5d', - sapUiChartPaletteSequentialHue2: '#E8743B', - sapUiChartPaletteSequentialHue2Dark1: '#da5a1b', - sapUiChartPaletteSequentialHue2Dark2: '#cc4300', - sapUiChartPaletteSequentialHue3Light3: '#8fd1bb', - sapUiChartPaletteSequentialHue3Light2: '#66c2a3', - sapUiChartPaletteSequentialHue3Light1: '#3fb68e', - sapUiChartPaletteSequentialHue3: '#19A979', - sapUiChartPaletteSequentialHue3Dark1: '#0e8c62', - sapUiChartPaletteSequentialHue3Dark2: '#03734d', - sapUiChartPaletteSequentialHue4Light3: '#f8b4c9', - sapUiChartPaletteSequentialHue4Light2: '#f490ae', - sapUiChartPaletteSequentialHue4Light1: '#f06a93', - sapUiChartPaletteSequentialHue4: '#ED4A7B', - sapUiChartPaletteSequentialHue4Dark1: '#e32b62', - sapUiChartPaletteSequentialHue4Dark2: '#d70947', - sapUiChartPaletteSequentialHue5Light3: '#d3bdeb', - sapUiChartPaletteSequentialHue5Light2: '#be9de2', - sapUiChartPaletteSequentialHue5Light1: '#a97dd8', - sapUiChartPaletteSequentialHue5: '#945ECF', - sapUiChartPaletteSequentialHue5Dark1: '#8746ce', - sapUiChartPaletteSequentialHue5Dark2: '#772acb', - sapUiChartPaletteSequentialHue6Light3: '#83d1da', - sapUiChartPaletteSequentialHue6Light2: '#5dc2cd', - sapUiChartPaletteSequentialHue6Light1: '#3ab5c2', - sapUiChartPaletteSequentialHue6: '#13A4B4', - sapUiChartPaletteSequentialHue6Dark1: '#0e8f9d', - sapUiChartPaletteSequentialHue6Dark2: '#087783', - sapUiChartPaletteSequentialHue7Light3: '#99a0f9', - sapUiChartPaletteSequentialHue7Light2: '#828af7', - sapUiChartPaletteSequentialHue7Light1: '#6973f6', - sapUiChartPaletteSequentialHue7: '#525DF4', - sapUiChartPaletteSequentialHue7Dark1: '#3945e4', - sapUiChartPaletteSequentialHue7Dark2: '#2531d4', - sapUiChartPaletteSequentialHue8Light3: '#e597d2', - sapUiChartPaletteSequentialHue8Light2: '#d876c0', - sapUiChartPaletteSequentialHue8Light1: '#cd59b1', - sapUiChartPaletteSequentialHue8: '#BF399E', - sapUiChartPaletteSequentialHue8Dark1: '#a92689', - sapUiChartPaletteSequentialHue8Dark2: '#921473', - sapUiChartPaletteSequentialHue9Light3: '#d1d9dc', - sapUiChartPaletteSequentialHue9Light2: '#adbcc3', - sapUiChartPaletteSequentialHue9Light1: '#8ca2ab', - sapUiChartPaletteSequentialHue9: '#6C8893', - sapUiChartPaletteSequentialHue9Dark1: '#547582', - sapUiChartPaletteSequentialHue9Dark2: '#3c6372', - sapUiChartPaletteSequentialHue10Light3: '#fccaca', - sapUiChartPaletteSequentialHue10Light2: '#f8a6a6', - sapUiChartPaletteSequentialHue10Light1: '#f38787', - sapUiChartPaletteSequentialHue10: '#EE6868', - sapUiChartPaletteSequentialHue10Dark1: '#e24c4c', - sapUiChartPaletteSequentialHue10Dark2: '#d62f2f', - sapUiChartPaletteSequentialHue11Light3: '#85a1bb', - sapUiChartPaletteSequentialHue11Light2: '#698caf', - sapUiChartPaletteSequentialHue11Light1: '#4d78a2', - sapUiChartPaletteSequentialHue11: '#2F6497', - sapUiChartPaletteSequentialHue11Dark1: '#245a8e', - sapUiChartPaletteSequentialHue11Dark2: '#144b7f', - sapUiChartPaletteSequentialNeutralLight3: '#d5dadc', - sapUiChartPaletteSequentialNeutralLight2: '#bac1c4', - sapUiChartPaletteSequentialNeutralLight1: '#9ea8ad', - sapUiChartPaletteSequentialNeutral: '#848f94', - sapUiChartPaletteSequentialNeutralDark1: '#69767c', - sapUiChartPaletteSequentialNeutralDark2: '#596468' +export const sequentialColors = { + sapUiChartAccent1: '#5899DA', + sapUiChartAccent2: '#E8743B', + sapUiChartAccent3: '#19A979', + sapUiChartAccent4: '#ED4A7B', + sapUiChartAccent5: '#945ECF', + sapUiChartAccent6: '#13A4B4', + sapUiChartAccent7: '#525DF4', + sapUiChartAccent8: '#BF399E', + sapUiChartAccent9: '#6C8893', + sapUiChartAccent10: '#EE6868', + sapUiChartAccent11: '#2F6497', + sapUiChartAccent12: '#1866b4' +}; + +export const semanticColors = { + sapUiChartGood: '#3fa45b', + sapUiChartBad: '#dc0d0e', + sapUiChartHighlight: '#de890d' }; diff --git a/packages/charts/src/themes/sap_belize_plus.ts b/packages/charts/src/themes/sap_belize_plus.ts index 9faee207c8f..75d1ee61e01 100644 --- a/packages/charts/src/themes/sap_belize_plus.ts +++ b/packages/charts/src/themes/sap_belize_plus.ts @@ -1,120 +1,20 @@ -export default { - sapUiChartPaletteQualitativeHue1: '#5899DA', - sapUiChartPaletteQualitativeHue2: '#E8743B', - sapUiChartPaletteQualitativeHue3: '#19A979', - sapUiChartPaletteQualitativeHue4: '#ED4A7B', - sapUiChartPaletteQualitativeHue5: '#945ECF', - sapUiChartPaletteQualitativeHue6: '#13A4B4', - sapUiChartPaletteQualitativeHue7: '#525DF4', - sapUiChartPaletteQualitativeHue8: '#BF399E', - sapUiChartPaletteQualitativeHue9: '#6C8893', - sapUiChartPaletteQualitativeHue10: '#EE6868', - sapUiChartPaletteQualitativeHue11: '#2F6497', - sapUiChartPaletteQualitativeHue12: '#1866b4', - sapUiChartPaletteQualitativeHue13: '#cc4300', - sapUiChartPaletteQualitativeHue14: '#03734d', - sapUiChartPaletteQualitativeHue15: '#d70947', - sapUiChartPaletteQualitativeHue16: '#772acb', - sapUiChartPaletteQualitativeHue17: '#087783', - sapUiChartPaletteQualitativeHue18: '#2531d4', - sapUiChartPaletteQualitativeHue19: '#921473', - sapUiChartPaletteQualitativeHue20: '#3c6372', - sapUiChartPaletteQualitativeHue21: '#d62f2f', - sapUiChartPaletteQualitativeHue22: '#144b7f', - sapUiChartPaletteSemanticBadLight3: '#fbc4c4', - sapUiChartPaletteSemanticBadLight2: '#f99d9e', - sapUiChartPaletteSemanticBadLight1: '#f77b7c', - sapUiChartPaletteSemanticBad: '#f55556', - sapUiChartPaletteSemanticBadDark1: '#f33334', - sapUiChartPaletteSemanticBadDark2: '#ef0e0f', - sapUiChartPaletteSemanticCriticalLight3: '#fdecd5', - sapUiChartPaletteSemanticCriticalLight2: '#fad7a4', - sapUiChartPaletteSemanticCriticalLight1: '#f7c174', - sapUiChartPaletteSemanticCritical: '#f4ac44', - sapUiChartPaletteSemanticCriticalDark1: '#f19714', - sapUiChartPaletteSemanticCriticalDark2: '#de890d', - sapUiChartPaletteSemanticGoodLight3: '#d8f0df', - sapUiChartPaletteSemanticGoodLight2: '#b3e2c0', - sapUiChartPaletteSemanticGoodLight1: '#8ed4a2', - sapUiChartPaletteSemanticGood: '#69c683', - sapUiChartPaletteSemanticGoodDark1: '#45b765 ', - sapUiChartPaletteSemanticGoodDark2: '#3fa45b', - sapUiChartPaletteSemanticNeutralLight3: '#f1f2f3', - sapUiChartPaletteSemanticNeutralLight2: '#d5dadc', - sapUiChartPaletteSemanticNeutralLight1: '#bac1c4', - sapUiChartPaletteSemanticNeutral: '#9ea8ad', - sapUiChartPaletteSemanticNeutralDark1: '#848f94', - sapUiChartPaletteSemanticNeutralDark2: '#69767c', - sapUiChartPaletteSequentialHue1Light3: '#b2d4f5', - sapUiChartPaletteSequentialHue1Light2: '#93bfeb', - sapUiChartPaletteSequentialHue1Light1: '#74abe2', - sapUiChartPaletteSequentialHue1: '#5899DA', - sapUiChartPaletteSequentialHue1Dark1: '#367dc4', - sapUiChartPaletteSequentialHue1Dark2: '#1866b4', - sapUiChartPaletteSequentialHue2Light3: '#fcc3a7', - sapUiChartPaletteSequentialHue2Light2: '#f5aa85', - sapUiChartPaletteSequentialHue2Light1: '#ef8d5d', - sapUiChartPaletteSequentialHue2: '#E8743B', - sapUiChartPaletteSequentialHue2Dark1: '#da5a1b', - sapUiChartPaletteSequentialHue2Dark2: '#cc4300', - sapUiChartPaletteSequentialHue3Light3: '#8fd1bb', - sapUiChartPaletteSequentialHue3Light2: '#66c2a3', - sapUiChartPaletteSequentialHue3Light1: '#3fb68e', - sapUiChartPaletteSequentialHue3: '#19A979', - sapUiChartPaletteSequentialHue3Dark1: '#0e8c62', - sapUiChartPaletteSequentialHue3Dark2: '#03734d', - sapUiChartPaletteSequentialHue4Light3: '#f8b4c9', - sapUiChartPaletteSequentialHue4Light2: '#f490ae', - sapUiChartPaletteSequentialHue4Light1: '#f06a93', - sapUiChartPaletteSequentialHue4: '#ED4A7B', - sapUiChartPaletteSequentialHue4Dark1: '#e32b62', - sapUiChartPaletteSequentialHue4Dark2: '#d70947', - sapUiChartPaletteSequentialHue5Light3: '#d3bdeb', - sapUiChartPaletteSequentialHue5Light2: '#be9de2', - sapUiChartPaletteSequentialHue5Light1: '#a97dd8', - sapUiChartPaletteSequentialHue5: '#945ECF', - sapUiChartPaletteSequentialHue5Dark1: '#8746ce', - sapUiChartPaletteSequentialHue5Dark2: '#772acb', - sapUiChartPaletteSequentialHue6Light3: '#83d1da', - sapUiChartPaletteSequentialHue6Light2: '#5dc2cd', - sapUiChartPaletteSequentialHue6Light1: '#3ab5c2', - sapUiChartPaletteSequentialHue6: '#13A4B4', - sapUiChartPaletteSequentialHue6Dark1: '#0e8f9d', - sapUiChartPaletteSequentialHue6Dark2: '#087783', - sapUiChartPaletteSequentialHue7Light3: '#99a0f9', - sapUiChartPaletteSequentialHue7Light2: '#828af7', - sapUiChartPaletteSequentialHue7Light1: '#6973f6', - sapUiChartPaletteSequentialHue7: '#525DF4', - sapUiChartPaletteSequentialHue7Dark1: '#3945e4', - sapUiChartPaletteSequentialHue7Dark2: '#2531d4', - sapUiChartPaletteSequentialHue8Light3: '#e597d2', - sapUiChartPaletteSequentialHue8Light2: '#d876c0', - sapUiChartPaletteSequentialHue8Light1: '#cd59b1', - sapUiChartPaletteSequentialHue8: '#BF399E', - sapUiChartPaletteSequentialHue8Dark1: '#a92689', - sapUiChartPaletteSequentialHue8Dark2: '#921473', - sapUiChartPaletteSequentialHue9Light3: '#d1d9dc', - sapUiChartPaletteSequentialHue9Light2: '#adbcc3', - sapUiChartPaletteSequentialHue9Light1: '#8ca2ab', - sapUiChartPaletteSequentialHue9: '#6C8893', - sapUiChartPaletteSequentialHue9Dark1: '#547582', - sapUiChartPaletteSequentialHue9Dark2: '#3c6372', - sapUiChartPaletteSequentialHue10Light3: '#fccaca', - sapUiChartPaletteSequentialHue10Light2: '#f8a6a6', - sapUiChartPaletteSequentialHue10Light1: '#f38787', - sapUiChartPaletteSequentialHue10: '#EE6868', - sapUiChartPaletteSequentialHue10Dark1: '#e24c4c', - sapUiChartPaletteSequentialHue10Dark2: '#d62f2f', - sapUiChartPaletteSequentialHue11Light3: '#85a1bb', - sapUiChartPaletteSequentialHue11Light2: '#698caf', - sapUiChartPaletteSequentialHue11Light1: '#4d78a2', - sapUiChartPaletteSequentialHue11: '#2F6497', - sapUiChartPaletteSequentialHue11Dark1: '#245a8e', - sapUiChartPaletteSequentialHue11Dark2: '#144b7f', - sapUiChartPaletteSequentialNeutralLight3: '#f1f2f3', - sapUiChartPaletteSequentialNeutralLight2: '#d5dadc', - sapUiChartPaletteSequentialNeutralLight1: '#bac1c4', - sapUiChartPaletteSequentialNeutral: '#9ea8ad', - sapUiChartPaletteSequentialNeutralDark1: '#848f94', - sapUiChartPaletteSequentialNeutralDark2: '#69767c' +export const sequentialColors = { + sapUiChartAccent1: '#5899DA', + sapUiChartAccent2: '#E8743B', + sapUiChartAccent3: '#19A979', + sapUiChartAccent4: '#ED4A7B', + sapUiChartAccent5: '#945ECF', + sapUiChartAccent6: '#13A4B4', + sapUiChartAccent7: '#525DF4', + sapUiChartAccent8: '#BF399E', + sapUiChartAccent9: '#6C8893', + sapUiChartAccent10: '#EE6868', + sapUiChartAccent11: '#2F6497', + sapUiChartAccent12: '#1866b4' +}; + +export const semanticColors = { + sapUiChartGood: '#69c683', + sapUiChartBad: '#f55556', + sapUiChartHighlight: '#f4ac44' }; diff --git a/packages/charts/src/themes/sap_fiori_3.ts b/packages/charts/src/themes/sap_fiori_3.ts new file mode 100644 index 00000000000..51c1e77dcc6 --- /dev/null +++ b/packages/charts/src/themes/sap_fiori_3.ts @@ -0,0 +1,20 @@ +export const sequentialColors = { + sapUiChartAccent1: '#0f828f', + sapUiChartAccent2: '#5ac2ce', + sapUiChartAccent3: '#03734d', + sapUiChartAccent4: '#66c2a3', + sapUiChartAccent5: '#3c6372', + sapUiChartAccent6: '#adbcc3', + sapUiChartAccent7: '#144b7f', + sapUiChartAccent8: '#698caf', + sapUiChartAccent9: '#d62f2f', + sapUiChartAccent10: '#f8a6a6', + sapUiChartAccent11: '#921473', + sapUiChartAccent12: '#d876c0' +}; + +export const semanticColors = { + sapUiChartGood: '#3fa45b', + sapUiChartBad: '#dc0d0e', + sapUiChartHighlight: '#f0ab00' +}; diff --git a/packages/charts/src/themes/sap_fiori_3_dark.ts b/packages/charts/src/themes/sap_fiori_3_dark.ts new file mode 100644 index 00000000000..26efbe9cca7 --- /dev/null +++ b/packages/charts/src/themes/sap_fiori_3_dark.ts @@ -0,0 +1,20 @@ +export const sequentialColors = { + sapUiChartAccent1: '#13a4b4', + sapUiChartAccent2: '#81d1da', + sapUiChartAccent3: '#19a979', + sapUiChartAccent4: '#8fd1bb', + sapUiChartAccent5: '#6c8893', + sapUiChartAccent6: '#d1d9dc', + sapUiChartAccent7: '#2f6497', + sapUiChartAccent8: '#85a1bb', + sapUiChartAccent9: '#ee6868', + sapUiChartAccent10: '#fccaca', + sapUiChartAccent11: '#bf399e', + sapUiChartAccent12: '#e597d2' +}; + +export const semanticColors = { + sapUiChartGood: '#3fa45b', + sapUiChartBad: '#dc0d0e', + sapUiChartHighlight: '#f0ab00' +}; diff --git a/packages/charts/src/themes/themeMap.ts b/packages/charts/src/themes/themeMap.ts new file mode 100644 index 00000000000..6e9a3702d08 --- /dev/null +++ b/packages/charts/src/themes/themeMap.ts @@ -0,0 +1,23 @@ +import * as sap_belize from './sap_belize'; +import * as sap_belize_plus from './sap_belize_plus'; +import * as sap_fiori_3 from './sap_fiori_3'; +import * as sap_fiori_3_dark from './sap_fiori_3_dark'; + +const themeMap = new Map(); +themeMap.set('sap_belize', sap_belize); +themeMap.set('sap_belize_plus', sap_belize_plus); +themeMap.set('sap_fiori_3', sap_fiori_3); +themeMap.set('sap_fiori_3_dark', sap_fiori_3_dark); + +// export const getOrLoadTheme = async (theme) => { +// if (themeMap.has(theme)) { +// return themeMap.get(theme); +// } +// const { semanticColors, sequentialColors } = await import(`./${theme}`); +// themeMap.set(theme, { semanticColors, sequentialColors }); +// return { semanticColors, sequentialColors }; +// }; + +export const getOrLoadTheme = (theme) => { + return themeMap.get(theme); +}; diff --git a/packages/charts/src/util/populateData.ts b/packages/charts/src/util/populateData.ts index d053eee435f..96ee34992b4 100644 --- a/packages/charts/src/util/populateData.ts +++ b/packages/charts/src/util/populateData.ts @@ -1,33 +1,7 @@ -import belize from './../themes/sap_belize'; -import belizePlus from './../themes/sap_belize_plus'; - -const getMapForTheme = (theme) => { - switch (theme) { - case 'sap_belize': - case 'sap_fiori_3': // TODO This needs to change as soon there is a Fiori3 Color Map Available - return belize; - case 'sap_belize_plus': - return belizePlus; - default: - throw new Error('Unsupported Theme'); - } -}; - -const getQualitativePaletteForTheme = (theme) => - Object.entries(theme) - .filter(([key]) => /^sapUiChartPaletteQualitativeHue/.test(key)) - .map(([key, value]) => value); // eslint-disable-line no-unused-vars +import { getOrLoadTheme } from '../themes/themeMap'; export const populateData = (labels, datasets, colors, theme, isPie = false) => { - let colorPalette = []; - const themeMap = getMapForTheme(theme); - - if (colors && colors.length > 0) { - colorPalette = colors.map((key) => themeMap[key] || key); - } else { - colorPalette = getQualitativePaletteForTheme(themeMap); - } - + const colorPalette = resolveColors(colors, theme); return { labels, datasets: datasets.map((item, index) => ({ @@ -38,15 +12,11 @@ export const populateData = (labels, datasets, colors, theme, isPie = false) => }; }; -export const populateDataMicroChart = (colors, theme) => { - let colorPalette = []; - const themeMap = getMapForTheme(theme); +export const resolveColors = (colors, theme) => { + const { semanticColors, sequentialColors } = getOrLoadTheme(theme); if (colors && colors.length > 0) { - colorPalette = colors.map((key) => themeMap[key] || key); - } else { - colorPalette = getQualitativePaletteForTheme(themeMap); + return colors.map((key) => semanticColors[key] || sequentialColors[key] || key); } - - return colorPalette; + return Object.values(sequentialColors); }; diff --git a/packages/charts/src/util/utils.ts b/packages/charts/src/util/utils.ts index d135bb69f7b..484e57f10ca 100644 --- a/packages/charts/src/util/utils.ts +++ b/packages/charts/src/util/utils.ts @@ -56,7 +56,8 @@ export const formatTooltipLabelForPieCharts = (categoryFormatter, valueFormatter let canvas; let textHeight; -export const getTextWidth = (text, font = `normal ${defaultFont.size}pt ${defaultFont.family}`) => { +export const getTextWidth = (text) => { + const font = `normal ${defaultFont.size}pt ${defaultFont.family}`; // re-use canvas object for better performance canvas = canvas || (canvas = document.createElement('canvas')); const context = canvas.getContext('2d'); diff --git a/yarn.lock b/yarn.lock index 4c8dad3d9e2..9cb2fd1d5cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7228,6 +7228,13 @@ genfun@^5.0.0: resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537" integrity sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA== +get-best-contrast-color@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/get-best-contrast-color/-/get-best-contrast-color-0.3.1.tgz#e13e0ea32d82871ccdfb742ccab1d6e07b137f44" + integrity sha512-B88vyjLzu+fDq2mMky/75pg14Pwgb9Zl6RZ3ws43Jyk4tsdvgiF/KAvRkFZAaMEgd1HTuykKg0GaG2KnsBoaCA== + dependencies: + get-contrast-ratio "0.2.1" + get-caller-file@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" @@ -7238,6 +7245,13 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-contrast-ratio@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/get-contrast-ratio/-/get-contrast-ratio-0.2.1.tgz#3f8e2a9610df2fb55fc2004bfce002ff0aa25c0a" + integrity sha512-lNIpmDIoxlVJsEQ5G/XCxH2YiiXH0ukOZ0ASvPp9ku2f+2AOYG5OXQ9cLgc9p3lNom5gy8JLkfSzi8fZN9LdKg== + dependencies: + get-relative-luminance "0.3.1" + get-func-name@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" @@ -7269,6 +7283,13 @@ get-port@^3.2.0: resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" integrity sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw= +get-relative-luminance@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/get-relative-luminance/-/get-relative-luminance-0.3.1.tgz#f86b722194f87f23c9d4df9075e066329001907f" + integrity sha512-nLEJhDAPC4anQlFhBxt8lHRwxs5OvOxmN0mHCxOCFDtzy0x5cXTMKNKUycpulsAw1zNOtz6GIRcIaVMcUUcU7Q== + dependencies: + parse-color "1.0.0" + get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" @@ -11718,7 +11739,7 @@ parse-code-context@^1.0.0: resolved "https://registry.yarnpkg.com/parse-code-context/-/parse-code-context-1.0.0.tgz#718c295c593d0d19a37f898473268cc75e98de1e" integrity sha512-OZQaqKaQnR21iqhlnPfVisFjBWjhnMl5J9MgbP8xC+EwoVqbXrq78lp+9Zb3ahmLzrIX5Us/qbvBnaS3hkH6OA== -parse-color@^1.0.0: +parse-color@1.0.0, parse-color@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-color/-/parse-color-1.0.0.tgz#7b748b95a83f03f16a94f535e52d7f3d94658619" integrity sha1-e3SLlag/A/FqlPU15S1/PZRlhhk= From 6a9fbc44571cf62e89ae0cfe2cc76b34521d9ed1 Mon Sep 17 00:00:00 2001 From: MarcusNotheis Date: Wed, 17 Jul 2019 15:19:52 +0200 Subject: [PATCH 2/9] WIP: Improve Legends --- .../charts/src/components/LineChart/index.tsx | 1 + packages/charts/src/config.ts | 29 ++++++++++++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/charts/src/components/LineChart/index.tsx b/packages/charts/src/components/LineChart/index.tsx index 0e07dfaedd3..5cb093e94b1 100644 --- a/packages/charts/src/components/LineChart/index.tsx +++ b/packages/charts/src/components/LineChart/index.tsx @@ -32,6 +32,7 @@ const LineChart = withChartContainer( yAxes: [ { ...DEFAULT_OPTIONS.scales.yAxes[0], + display: true, ticks: { ...DEFAULT_OPTIONS.scales.yAxes[0].ticks, callback: valueAxisFormatter diff --git a/packages/charts/src/config.ts b/packages/charts/src/config.ts index 4209324022e..f9df6198900 100644 --- a/packages/charts/src/config.ts +++ b/packages/charts/src/config.ts @@ -6,15 +6,23 @@ export const defaultFont = { size: '12' }; -pluginService.register(DataLabels); -defaults.global.plugins.datalabels.align = 'top'; +defaults.global.animation.duration = 0; +defaults.scale.ticks.fontStyle = 'bold'; + +// Legend Configuration defaults.global.legend.position = 'bottom'; defaults.global.legend.labels.fontFamily = defaultFont.family; +defaults.global.legend.labels.fontColor = '#6a6d70'; /* sapUiContentLabelColor */ + +// Chart Type Configuration +defaults.global.elements.line.fill = false; + +// Data Labels Configuration +pluginService.register(DataLabels); +defaults.global.plugins.datalabels.align = 'top'; +defaults.global.plugins.datalabels.display = 'auto'; // @ts-ignore defaults.global.plugins.datalabels.font = defaultFont; -defaults.global.animation.duration = 0; -defaults.global.plugins.datalabels.display = 'auto'; -// defaults.global.maintainAspectRatio = false; export const DEFAULT_OPTIONS = { scales: { @@ -22,14 +30,21 @@ export const DEFAULT_OPTIONS = { { display: false, ticks: { - beginAtZero: true + beginAtZero: true, + maxTicksLimit: 6 + }, + gridLines: { + display: true } } ], xAxes: [ { gridLines: { - display: false + display: false, + lineWidth: 2, + color: '#32363a' /* sapUiBaseText */, + fontColor: '#32363a' /* sapUiBaseText */ } } ] From ff5c978aa0f7bde69138dcb3231b5bad56376a75 Mon Sep 17 00:00:00 2001 From: MarcusNotheis Date: Thu, 18 Jul 2019 15:14:10 +0200 Subject: [PATCH 3/9] WIP: Added custom legends --- .../src/components/BarChart/demo.stories.tsx | 1 + .../charts/src/components/BarChart/index.tsx | 60 ++++++++++++++----- .../src/components/ColumnChart/index.tsx | 59 +++++++++++++----- .../src/components/DonutChart/index.tsx | 54 +++++++++++++---- .../charts/src/components/LineChart/index.tsx | 55 +++++++++++++---- .../charts/src/components/PieChart/index.tsx | 54 +++++++++++++---- .../src/components/RadarChart/index.tsx | 53 ++++++++++++---- .../src/components/RadialChart/index.tsx | 4 +- packages/charts/src/config.ts | 13 +++- .../charts/src/interfaces/ChartBaseProps.ts | 1 + packages/charts/src/internal/ChartLegend.ts | 53 ++++++++++++++++ 11 files changed, 325 insertions(+), 82 deletions(-) create mode 100644 packages/charts/src/internal/ChartLegend.ts diff --git a/packages/charts/src/components/BarChart/demo.stories.tsx b/packages/charts/src/components/BarChart/demo.stories.tsx index e7135d846c2..3ced6ccab6a 100644 --- a/packages/charts/src/components/BarChart/demo.stories.tsx +++ b/packages/charts/src/components/BarChart/demo.stories.tsx @@ -39,6 +39,7 @@ storiesOf('Charts | BarChart', module) datasets={datasets} getElementAtEvent={action('getElementAtEvent')} loading={boolean('loading')} + noLegend={boolean('noLegend')} /> )) .add('with Formatter', () => ( diff --git a/packages/charts/src/components/BarChart/index.tsx b/packages/charts/src/components/BarChart/index.tsx index 5459139c4dc..fcf46ecbf95 100644 --- a/packages/charts/src/components/BarChart/index.tsx +++ b/packages/charts/src/components/BarChart/index.tsx @@ -1,10 +1,12 @@ +import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; import bestContrast from 'get-best-contrast-color'; -import React, { FC, forwardRef, Ref } from 'react'; +import React, { forwardRef, Ref, useEffect, useRef, RefObject, useCallback } from 'react'; import { HorizontalBar } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; import { DEFAULT_OPTIONS } from '../../config'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; +import { generateLegend } from '../../internal/ChartLegend'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; import { populateData } from '../../util/populateData'; import { formatTooltipLabel, getTextWidth, mergeConfig } from '../../util/utils'; @@ -24,19 +26,42 @@ const BarChart = withChartContainer( getElementAtEvent, colors, width, - height + height, + noLegend } = props as BarChartPropTypes; const theme: any = useTheme(); const data = populateData(labels, datasets, colors, theme.theme); + const chartRef = useConsolidatedRef(ref); + const legendRef: RefObject = useRef(); + + const handleLegendItemPress = useCallback( + (e) => { + const clickTarget = (e.currentTarget as unknown) as HTMLLIElement; + const datasetIndex = parseInt(clickTarget.dataset.datasetindex); + const { chartInstance } = chartRef.current; + const meta = chartInstance.getDatasetMeta(datasetIndex); + meta.hidden = meta.hidden === null ? !chartInstance.data.datasets[datasetIndex].hidden : null; + chartInstance.update(); + clickTarget.style.textDecoration = meta.hidden ? 'line-through' : 'unset'; + }, + [legendRef.current, chartRef.current] + ); + + useEffect(() => { + if (noLegend) { + legendRef.current.innerHTML = ''; + } else { + legendRef.current.innerHTML = chartRef.current.chartInstance.generateLegend(); + legendRef.current.querySelectorAll('li').forEach((legendItem) => { + legendItem.addEventListener('click', handleLegendItemPress); + }); + } + }, [chartRef.current, legendRef.current, noLegend]); + const mergedOptions = mergeConfig( { - layout: { - padding: { - right: 15 - } - }, scales: { xAxes: [ { @@ -89,15 +114,18 @@ const BarChart = withChartContainer( ); return ( - + <> + +
+ ); }) ); diff --git a/packages/charts/src/components/ColumnChart/index.tsx b/packages/charts/src/components/ColumnChart/index.tsx index eb01b244c7c..edc7ee34b6b 100644 --- a/packages/charts/src/components/ColumnChart/index.tsx +++ b/packages/charts/src/components/ColumnChart/index.tsx @@ -1,7 +1,8 @@ import bestContrast from 'get-best-contrast-color'; -import React, { forwardRef, Ref } from 'react'; +import React, { forwardRef, Ref, RefObject, useEffect, useRef, useCallback } from 'react'; import { Bar } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; +import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; import { DEFAULT_OPTIONS } from '../../config'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; @@ -24,19 +25,42 @@ const ColumnChart = withChartContainer( colors, options, width, - height + height, + noLegend } = props; const theme: any = useTheme(); const data = populateData(labels, datasets, colors, theme.theme); + const chartRef = useConsolidatedRef(ref); + const legendRef: RefObject = useRef(); + + const handleLegendItemPress = useCallback( + (e) => { + const clickTarget = (e.currentTarget as unknown) as HTMLLIElement; + const datasetIndex = parseInt(clickTarget.dataset.datasetindex); + const { chartInstance } = chartRef.current; + const meta = chartInstance.getDatasetMeta(datasetIndex); + meta.hidden = meta.hidden === null ? !chartInstance.data.datasets[datasetIndex].hidden : null; + chartInstance.update(); + clickTarget.style.textDecoration = meta.hidden ? 'line-through' : 'unset'; + }, + [legendRef.current, chartRef.current] + ); + + useEffect(() => { + if (noLegend) { + legendRef.current.innerHTML = ''; + } else { + legendRef.current.innerHTML = chartRef.current.chartInstance.generateLegend(); + legendRef.current.querySelectorAll('li').forEach((legendItem) => { + legendItem.addEventListener('click', handleLegendItemPress); + }); + } + }, [chartRef.current, legendRef.current, noLegend]); + const mergedOptions = mergeConfig( { - layout: { - padding: { - top: 15 - } - }, scales: { xAxes: [ { @@ -92,15 +116,18 @@ const ColumnChart = withChartContainer( ); return ( - + <> + +
+ ); }) ); diff --git a/packages/charts/src/components/DonutChart/index.tsx b/packages/charts/src/components/DonutChart/index.tsx index 50bb1f0b588..84b8d46f6aa 100644 --- a/packages/charts/src/components/DonutChart/index.tsx +++ b/packages/charts/src/components/DonutChart/index.tsx @@ -1,6 +1,7 @@ -import React, { FC, Ref, forwardRef } from 'react'; +import React, { FC, Ref, forwardRef, RefObject, useRef, useEffect, useCallback } from 'react'; import { Pie } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; +import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; @@ -22,7 +23,8 @@ const DonutChart = withChartContainer( valueAxisFormatter, options, width, - height + height, + noLegend } = props; const theme: any = useTheme(); @@ -50,16 +52,46 @@ const DonutChart = withChartContainer( options ); + const chartRef = useConsolidatedRef(ref); + const legendRef: RefObject = useRef(); + + const handleLegendItemPress = useCallback( + (e) => { + const clickTarget = (e.currentTarget as unknown) as HTMLLIElement; + const datasetIndex = parseInt(clickTarget.dataset.datasetindex); + const { chartInstance } = chartRef.current; + const meta = chartInstance.getDatasetMeta(datasetIndex); + meta.hidden = meta.hidden === null ? !chartInstance.data.datasets[datasetIndex].hidden : null; + chartInstance.update(); + clickTarget.style.textDecoration = meta.hidden ? 'line-through' : 'unset'; + }, + [legendRef.current, chartRef.current] + ); + + useEffect(() => { + if (noLegend) { + legendRef.current.innerHTML = ''; + } else { + legendRef.current.innerHTML = chartRef.current.chartInstance.generateLegend(); + legendRef.current.querySelectorAll('li').forEach((legendItem) => { + legendItem.addEventListener('click', handleLegendItemPress); + }); + } + }, [chartRef.current, legendRef.current, noLegend]); + return ( - + <> + +
+ ); }) ); diff --git a/packages/charts/src/components/LineChart/index.tsx b/packages/charts/src/components/LineChart/index.tsx index 5cb093e94b1..c48a6375071 100644 --- a/packages/charts/src/components/LineChart/index.tsx +++ b/packages/charts/src/components/LineChart/index.tsx @@ -1,9 +1,11 @@ -import React, { forwardRef, Ref } from 'react'; +import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; +import React, { forwardRef, Ref, RefObject, useEffect, useRef, useCallback } from 'react'; import { Line } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; import { DEFAULT_OPTIONS } from '../../config'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; +import { useLegend } from '../../internal/ChartLegend'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; import { populateData } from '../../util/populateData'; import { formatTooltipLabel, mergeConfig } from '../../util/utils'; @@ -23,7 +25,8 @@ const LineChart = withChartContainer( getElementAtEvent, getDatasetAtEvent, width, - height + height, + noLegend } = props; const chartOptions = mergeConfig( @@ -57,16 +60,46 @@ const LineChart = withChartContainer( const theme: any = useTheme(); const data = populateData(labels, datasets, colors, theme.theme); + + const chartRef = useConsolidatedRef(ref); + const legendRef: RefObject = useRef(); + const handleLegendItemPress = useCallback( + (e) => { + const clickTarget = (e.currentTarget as unknown) as HTMLLIElement; + const datasetIndex = parseInt(clickTarget.dataset.datasetindex); + const { chartInstance } = chartRef.current; + const meta = chartInstance.getDatasetMeta(datasetIndex); + meta.hidden = meta.hidden === null ? !chartInstance.data.datasets[datasetIndex].hidden : null; + chartInstance.update(); + clickTarget.style.textDecoration = meta.hidden ? 'line-through' : 'unset'; + }, + [legendRef.current, chartRef.current] + ); + + useEffect(() => { + if (noLegend) { + legendRef.current.innerHTML = ''; + } else { + legendRef.current.innerHTML = chartRef.current.chartInstance.generateLegend(); + legendRef.current.querySelectorAll('li').forEach((legendItem) => { + legendItem.addEventListener('click', handleLegendItemPress); + }); + } + }, [chartRef.current, legendRef.current, noLegend]); + return ( - + <> + +
+ ); }) ); diff --git a/packages/charts/src/components/PieChart/index.tsx b/packages/charts/src/components/PieChart/index.tsx index ab7308c7d1b..0d10afa878d 100644 --- a/packages/charts/src/components/PieChart/index.tsx +++ b/packages/charts/src/components/PieChart/index.tsx @@ -1,6 +1,7 @@ -import React, { FC, forwardRef, Ref } from 'react'; +import React, { FC, forwardRef, Ref, RefObject, useRef, useEffect, useCallback } from 'react'; import { Pie } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; +import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; @@ -22,7 +23,8 @@ const PieChart = withChartContainer( valueAxisFormatter, options, width, - height + height, + noLegend } = props; const theme: any = useTheme(); @@ -49,16 +51,46 @@ const PieChart = withChartContainer( options ); + const chartRef = useConsolidatedRef(ref); + const legendRef: RefObject = useRef(); + + const handleLegendItemPress = useCallback( + (e) => { + const clickTarget = (e.currentTarget as unknown) as HTMLLIElement; + const datasetIndex = parseInt(clickTarget.dataset.datasetindex); + const { chartInstance } = chartRef.current; + const meta = chartInstance.getDatasetMeta(datasetIndex); + meta.hidden = meta.hidden === null ? !chartInstance.data.datasets[datasetIndex].hidden : null; + chartInstance.update(); + clickTarget.style.textDecoration = meta.hidden ? 'line-through' : 'unset'; + }, + [legendRef.current, chartRef.current] + ); + + useEffect(() => { + if (noLegend) { + legendRef.current.innerHTML = ''; + } else { + legendRef.current.innerHTML = chartRef.current.chartInstance.generateLegend(); + legendRef.current.querySelectorAll('li').forEach((legendItem) => { + legendItem.addEventListener('click', handleLegendItemPress); + }); + } + }, [chartRef.current, legendRef.current, noLegend]); + return ( - + <> + +
+ ); }) ); diff --git a/packages/charts/src/components/RadarChart/index.tsx b/packages/charts/src/components/RadarChart/index.tsx index 159d0970310..0a999a1c3f1 100644 --- a/packages/charts/src/components/RadarChart/index.tsx +++ b/packages/charts/src/components/RadarChart/index.tsx @@ -1,6 +1,7 @@ -import React, { FC, forwardRef, Ref } from 'react'; +import React, { FC, forwardRef, Ref, RefObject, useRef, useEffect, useCallback } from 'react'; import { Radar } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; +import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; @@ -21,7 +22,8 @@ const RadarChart: FC = withChartContainer( getDatasetAtEvent, getElementAtEvent, valueAxisFormatter, - colors + colors, + noLegend } = props; const theme: any = useTheme(); @@ -46,16 +48,45 @@ const RadarChart: FC = withChartContainer( options ); + const chartRef = useConsolidatedRef(ref); + const legendRef: RefObject = useRef(); + const handleLegendItemPress = useCallback( + (e) => { + const clickTarget = (e.currentTarget as unknown) as HTMLLIElement; + const datasetIndex = parseInt(clickTarget.dataset.datasetindex); + const { chartInstance } = chartRef.current; + const meta = chartInstance.getDatasetMeta(datasetIndex); + meta.hidden = meta.hidden === null ? !chartInstance.data.datasets[datasetIndex].hidden : null; + chartInstance.update(); + clickTarget.style.textDecoration = meta.hidden ? 'line-through' : 'unset'; + }, + [legendRef.current, chartRef.current] + ); + + useEffect(() => { + if (noLegend) { + legendRef.current.innerHTML = ''; + } else { + legendRef.current.innerHTML = chartRef.current.chartInstance.generateLegend(); + legendRef.current.querySelectorAll('li').forEach((legendItem) => { + legendItem.addEventListener('click', handleLegendItemPress); + }); + } + }, [chartRef.current, legendRef.current, noLegend]); + return ( - + <> + {' '} +
+ ); }) ); diff --git a/packages/charts/src/components/RadialChart/index.tsx b/packages/charts/src/components/RadialChart/index.tsx index e63ee3c597f..5d882071ab8 100644 --- a/packages/charts/src/components/RadialChart/index.tsx +++ b/packages/charts/src/components/RadialChart/index.tsx @@ -48,9 +48,6 @@ const RadialChart = forwardRef((props: RadialChartPropTypes, ref: Ref

void; getElementAtEvent?: (dataset: ChartDataSets[], event?: Event) => void; loading?: boolean; + noLegend?: boolean; } diff --git a/packages/charts/src/internal/ChartLegend.ts b/packages/charts/src/internal/ChartLegend.ts new file mode 100644 index 00000000000..8bddaef4230 --- /dev/null +++ b/packages/charts/src/internal/ChartLegend.ts @@ -0,0 +1,53 @@ +import { Optional } from '@ui5/webcomponents-react-base'; +import { useEffect } from 'react'; + +const getSymbolForDataset = (type, dataset) => { + const datasetMeta: any = dataset.hasOwnProperty('_meta') ? Object.values(dataset._meta)[0] : null; + const elementType = Optional.of(datasetMeta, 'type').orElse(type); + console.log(datasetMeta); + switch (elementType) { + case 'line': + return ` +
+
+
+ `; + default: + return `
`; + } +}; + +const getLegendEntry = (dataset, type, index) => { + const symbol = getSymbolForDataset(type, dataset); + + return `
  • +${symbol} +${dataset.label} +
  • `; +}; + +export const generateLegend = (chart) => { + const { datasets } = chart.config.data; + const type = chart.config.type; + + let itemsForLegend = datasets; + if (['pie', 'doughnut'].includes(type)) { + let pieDataSet = Object.values(datasets[0]._meta)[0] as any; + itemsForLegend = pieDataSet.data.map((meta) => meta._model); + } + return `
    +${itemsForLegend.map((item, index) => getLegendEntry(item, type, index)).join(' ')} +
    `; +}; + +export const useLegend = ({ legend, chart, noLegend }, deps) => { + useEffect(() => { + if (legend && chart) { + if (noLegend) { + legend.innerHTML = ''; + } else { + legend.innerHTML = chart.chartInstance.generateLegend(); + } + } + }, deps); +}; From 05dc2dd43e1833eef092db9f0e16e972d91847d3 Mon Sep 17 00:00:00 2001 From: MarcusNotheis Date: Fri, 19 Jul 2019 08:36:47 +0200 Subject: [PATCH 4/9] WIP: Fixed Chart Legends for Donut and Pie Chart --- packages/charts/src/components/DonutChart/index.tsx | 4 ++-- packages/charts/src/components/PieChart/index.tsx | 4 ++-- packages/charts/src/internal/ChartLegend.ts | 12 ------------ 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/packages/charts/src/components/DonutChart/index.tsx b/packages/charts/src/components/DonutChart/index.tsx index 84b8d46f6aa..e60c918d178 100644 --- a/packages/charts/src/components/DonutChart/index.tsx +++ b/packages/charts/src/components/DonutChart/index.tsx @@ -60,8 +60,8 @@ const DonutChart = withChartContainer( const clickTarget = (e.currentTarget as unknown) as HTMLLIElement; const datasetIndex = parseInt(clickTarget.dataset.datasetindex); const { chartInstance } = chartRef.current; - const meta = chartInstance.getDatasetMeta(datasetIndex); - meta.hidden = meta.hidden === null ? !chartInstance.data.datasets[datasetIndex].hidden : null; + const meta = chartInstance.getDatasetMeta(0).data[datasetIndex]; + meta.hidden = !meta.hidden; chartInstance.update(); clickTarget.style.textDecoration = meta.hidden ? 'line-through' : 'unset'; }, diff --git a/packages/charts/src/components/PieChart/index.tsx b/packages/charts/src/components/PieChart/index.tsx index 0d10afa878d..809b6d947cc 100644 --- a/packages/charts/src/components/PieChart/index.tsx +++ b/packages/charts/src/components/PieChart/index.tsx @@ -59,8 +59,8 @@ const PieChart = withChartContainer( const clickTarget = (e.currentTarget as unknown) as HTMLLIElement; const datasetIndex = parseInt(clickTarget.dataset.datasetindex); const { chartInstance } = chartRef.current; - const meta = chartInstance.getDatasetMeta(datasetIndex); - meta.hidden = meta.hidden === null ? !chartInstance.data.datasets[datasetIndex].hidden : null; + const meta = chartInstance.getDatasetMeta(0).data[datasetIndex]; + meta.hidden = !meta.hidden; chartInstance.update(); clickTarget.style.textDecoration = meta.hidden ? 'line-through' : 'unset'; }, diff --git a/packages/charts/src/internal/ChartLegend.ts b/packages/charts/src/internal/ChartLegend.ts index 8bddaef4230..1e6f5c538b6 100644 --- a/packages/charts/src/internal/ChartLegend.ts +++ b/packages/charts/src/internal/ChartLegend.ts @@ -39,15 +39,3 @@ export const generateLegend = (chart) => { ${itemsForLegend.map((item, index) => getLegendEntry(item, type, index)).join(' ')}

    `; }; - -export const useLegend = ({ legend, chart, noLegend }, deps) => { - useEffect(() => { - if (legend && chart) { - if (noLegend) { - legend.innerHTML = ''; - } else { - legend.innerHTML = chart.chartInstance.generateLegend(); - } - } - }, deps); -}; From 49c75e52015053e2cb14af66dc5a1868693a35a1 Mon Sep 17 00:00:00 2001 From: MarcusNotheis Date: Fri, 19 Jul 2019 08:37:37 +0200 Subject: [PATCH 5/9] WIP: Make Legends part of chart height The legend is now part of the overall chart height (60px). When visible the legend height will deduct the chart height --- .../charts/src/components/BarChart/index.tsx | 2 +- .../src/components/ColumnChart/index.tsx | 2 +- .../src/components/DonutChart/index.tsx | 2 +- .../charts/src/components/LineChart/index.tsx | 2 +- .../charts/src/components/PieChart/index.tsx | 2 +- .../src/components/RadarChart/index.tsx | 2 +- .../ChartContainer/withChartContainer.tsx | 27 ++++++++++++++++--- 7 files changed, 29 insertions(+), 10 deletions(-) diff --git a/packages/charts/src/components/BarChart/index.tsx b/packages/charts/src/components/BarChart/index.tsx index fcf46ecbf95..7a5f60aee4c 100644 --- a/packages/charts/src/components/BarChart/index.tsx +++ b/packages/charts/src/components/BarChart/index.tsx @@ -124,7 +124,7 @@ const BarChart = withChartContainer( getDatasetAtEvent={getDatasetAtEvent} getElementAtEvent={getElementAtEvent} /> -
    +
    ); }) diff --git a/packages/charts/src/components/ColumnChart/index.tsx b/packages/charts/src/components/ColumnChart/index.tsx index edc7ee34b6b..7a061fc197a 100644 --- a/packages/charts/src/components/ColumnChart/index.tsx +++ b/packages/charts/src/components/ColumnChart/index.tsx @@ -126,7 +126,7 @@ const ColumnChart = withChartContainer( getDatasetAtEvent={getDatasetAtEvent} getElementAtEvent={getElementAtEvent} /> -
    +
    ); }) diff --git a/packages/charts/src/components/DonutChart/index.tsx b/packages/charts/src/components/DonutChart/index.tsx index e60c918d178..3d8906ea3c8 100644 --- a/packages/charts/src/components/DonutChart/index.tsx +++ b/packages/charts/src/components/DonutChart/index.tsx @@ -90,7 +90,7 @@ const DonutChart = withChartContainer( getDatasetAtEvent={getDatasetAtEvent} getElementAtEvent={getElementAtEvent} /> -
    +
    ); }) diff --git a/packages/charts/src/components/LineChart/index.tsx b/packages/charts/src/components/LineChart/index.tsx index c48a6375071..56bbaf9c484 100644 --- a/packages/charts/src/components/LineChart/index.tsx +++ b/packages/charts/src/components/LineChart/index.tsx @@ -98,7 +98,7 @@ const LineChart = withChartContainer( getDatasetAtEvent={getDatasetAtEvent} getElementAtEvent={getElementAtEvent} /> -
    +
    ); }) diff --git a/packages/charts/src/components/PieChart/index.tsx b/packages/charts/src/components/PieChart/index.tsx index 809b6d947cc..285d27cdf8b 100644 --- a/packages/charts/src/components/PieChart/index.tsx +++ b/packages/charts/src/components/PieChart/index.tsx @@ -89,7 +89,7 @@ const PieChart = withChartContainer( getDatasetAtEvent={getDatasetAtEvent} getElementAtEvent={getElementAtEvent} /> -
    +
    ); }) diff --git a/packages/charts/src/components/RadarChart/index.tsx b/packages/charts/src/components/RadarChart/index.tsx index 0a999a1c3f1..81231de0bf5 100644 --- a/packages/charts/src/components/RadarChart/index.tsx +++ b/packages/charts/src/components/RadarChart/index.tsx @@ -85,7 +85,7 @@ const RadarChart: FC = withChartContainer( getDatasetAtEvent={getDatasetAtEvent} getElementAtEvent={getElementAtEvent} />{' '} -
    +
    ); }) diff --git a/packages/charts/src/internal/ChartContainer/withChartContainer.tsx b/packages/charts/src/internal/ChartContainer/withChartContainer.tsx index a784bd65544..49ca7525f36 100644 --- a/packages/charts/src/internal/ChartContainer/withChartContainer.tsx +++ b/packages/charts/src/internal/ChartContainer/withChartContainer.tsx @@ -8,11 +8,19 @@ const styles = { chart: { '& canvas': { maxWidth: (props) => `${props.width}px`, - maxHeight: (props) => `${props.height}px` + maxHeight: (props) => `${props.height - props.noLegend ? 0 : 60}px` }, '& svg': { width: (props) => `${props.width}px`, - height: (props) => `${props.height}px` + height: (props) => `${props.height - props.noLegend ? 0 : 60}px` + }, + '& .legend': { + height: '55px', + maxHeight: '55px', + marginTop: '5px', + overflowY: 'auto', + display: 'flex', + alignItems: 'center' } } }; @@ -21,7 +29,7 @@ const useStyles = createUseStyles(styles); export const withChartContainer = (Component: ComponentType) => { const ChartContainer = forwardRef((props: ChartBaseProps, ref: Ref) => { - const { style, className, tooltip, loading, datasets, slot, ...rest } = props; + const { style, className, tooltip, loading, datasets, slot, noLegend, height, ...rest } = props; const classes = useStyles(props); let classNames = classes.chart; @@ -39,10 +47,21 @@ export const withChartContainer = (Component: ComponentType) => { ...style }; + const chartHeight = noLegend ? height : height - 60; + return (
    {loadingIndicator} - {datasets.length > 0 && } + {datasets.length > 0 && ( + + )}
    ); }); From 56581821c338b685550a4c8767061e1c6ed09c6c Mon Sep 17 00:00:00 2001 From: MarcusNotheis Date: Fri, 19 Jul 2019 09:01:15 +0200 Subject: [PATCH 6/9] WIP: Updated Test Snapshots --- .../charts/src/components/LineChart/index.tsx | 1 - .../__snapshots__/MicroBarChart.test.tsx.snap | 118 +++++++++--------- 2 files changed, 59 insertions(+), 60 deletions(-) diff --git a/packages/charts/src/components/LineChart/index.tsx b/packages/charts/src/components/LineChart/index.tsx index 56bbaf9c484..d81f8eac3b9 100644 --- a/packages/charts/src/components/LineChart/index.tsx +++ b/packages/charts/src/components/LineChart/index.tsx @@ -5,7 +5,6 @@ import { useTheme } from 'react-jss'; import { DEFAULT_OPTIONS } from '../../config'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; -import { useLegend } from '../../internal/ChartLegend'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; import { populateData } from '../../util/populateData'; import { formatTooltipLabel, mergeConfig } from '../../util/utils'; diff --git a/packages/charts/src/components/MicroBarChart/__snapshots__/MicroBarChart.test.tsx.snap b/packages/charts/src/components/MicroBarChart/__snapshots__/MicroBarChart.test.tsx.snap index 13b18b44876..efd2c5481bf 100644 --- a/packages/charts/src/components/MicroBarChart/__snapshots__/MicroBarChart.test.tsx.snap +++ b/packages/charts/src/components/MicroBarChart/__snapshots__/MicroBarChart.test.tsx.snap @@ -2,61 +2,61 @@ exports[`Micro Bar Chart Renders with data 1`] = `
    Bar Number One 10
    Bar Number Two 100
    @@ -65,61 +65,61 @@ exports[`Micro Bar Chart Renders with data 1`] = ` exports[`Micro Bar Chart custom colors 1`] = `
    Bar Number One 10
    Bar Number Two 100
    @@ -128,33 +128,33 @@ exports[`Micro Bar Chart custom colors 1`] = ` exports[`Micro Bar Chart element selection 1`] = `
    Bar Number One 10
    @@ -163,62 +163,62 @@ exports[`Micro Bar Chart element selection 1`] = ` exports[`Micro Bar Chart maxWidth selection 1`] = `
    Bar Number One 10
    Bar Number Two 100
    From 44a90d4f79aaab385364b8c7855633664ff01f98 Mon Sep 17 00:00:00 2001 From: MarcusNotheis Date: Fri, 19 Jul 2019 09:32:38 +0200 Subject: [PATCH 7/9] WIP: Fixed Donut Chart Demo --- packages/charts/src/components/DonutChart/demo.stories.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/charts/src/components/DonutChart/demo.stories.tsx b/packages/charts/src/components/DonutChart/demo.stories.tsx index ba42a1c41ce..38d9372461a 100644 --- a/packages/charts/src/components/DonutChart/demo.stories.tsx +++ b/packages/charts/src/components/DonutChart/demo.stories.tsx @@ -25,7 +25,6 @@ storiesOf('Charts | DonutChart', module) labels={labels} datasets={dataset} valueAxisFormatter={(number) => `${number}$`} - options={{ cutoutPercentage: 0 }} loading={boolean('loading')} /> )); From c547ce0d51bf3ed16a553972bd142f799d879d7d Mon Sep 17 00:00:00 2001 From: MarcusNotheis Date: Fri, 19 Jul 2019 10:01:56 +0200 Subject: [PATCH 8/9] WIP: Replace populateData with useChartData Hook --- packages/charts/src/components/BarChart/index.tsx | 7 +++---- packages/charts/src/components/ColumnChart/index.tsx | 8 ++++---- packages/charts/src/components/DonutChart/index.tsx | 8 ++++---- packages/charts/src/components/LineChart/index.tsx | 6 +++--- packages/charts/src/components/PieChart/index.tsx | 8 ++++---- packages/charts/src/components/RadarChart/index.tsx | 8 ++++---- packages/charts/src/util/populateData.ts | 5 +++++ 7 files changed, 27 insertions(+), 23 deletions(-) diff --git a/packages/charts/src/components/BarChart/index.tsx b/packages/charts/src/components/BarChart/index.tsx index 7a5f60aee4c..fddf4aa0173 100644 --- a/packages/charts/src/components/BarChart/index.tsx +++ b/packages/charts/src/components/BarChart/index.tsx @@ -1,14 +1,13 @@ import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; import bestContrast from 'get-best-contrast-color'; -import React, { forwardRef, Ref, useEffect, useRef, RefObject, useCallback } from 'react'; +import React, { forwardRef, Ref, RefObject, useCallback, useEffect, useRef } from 'react'; import { HorizontalBar } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; import { DEFAULT_OPTIONS } from '../../config'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; -import { generateLegend } from '../../internal/ChartLegend'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; -import { populateData } from '../../util/populateData'; +import { useChartData } from '../../util/populateData'; import { formatTooltipLabel, getTextWidth, mergeConfig } from '../../util/utils'; import { BarChartPlaceholder } from './Placeholder'; @@ -31,7 +30,7 @@ const BarChart = withChartContainer( } = props as BarChartPropTypes; const theme: any = useTheme(); - const data = populateData(labels, datasets, colors, theme.theme); + const data = useChartData(labels, datasets, colors, theme.theme); const chartRef = useConsolidatedRef(ref); const legendRef: RefObject = useRef(); diff --git a/packages/charts/src/components/ColumnChart/index.tsx b/packages/charts/src/components/ColumnChart/index.tsx index 7a061fc197a..371c3ba29eb 100644 --- a/packages/charts/src/components/ColumnChart/index.tsx +++ b/packages/charts/src/components/ColumnChart/index.tsx @@ -1,13 +1,13 @@ +import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; import bestContrast from 'get-best-contrast-color'; -import React, { forwardRef, Ref, RefObject, useEffect, useRef, useCallback } from 'react'; +import React, { forwardRef, Ref, RefObject, useCallback, useEffect, useRef } from 'react'; import { Bar } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; -import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; import { DEFAULT_OPTIONS } from '../../config'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; -import { populateData } from '../../util/populateData'; +import { useChartData } from '../../util/populateData'; import { formatTooltipLabel, getTextHeight, getTextWidth, mergeConfig } from '../../util/utils'; import { ColumnChartPlaceholder } from './Placeholder'; @@ -30,7 +30,7 @@ const ColumnChart = withChartContainer( } = props; const theme: any = useTheme(); - const data = populateData(labels, datasets, colors, theme.theme); + const data = useChartData(labels, datasets, colors, theme.theme); const chartRef = useConsolidatedRef(ref); const legendRef: RefObject = useRef(); diff --git a/packages/charts/src/components/DonutChart/index.tsx b/packages/charts/src/components/DonutChart/index.tsx index 3d8906ea3c8..a3cbfc1045c 100644 --- a/packages/charts/src/components/DonutChart/index.tsx +++ b/packages/charts/src/components/DonutChart/index.tsx @@ -1,11 +1,11 @@ -import React, { FC, Ref, forwardRef, RefObject, useRef, useEffect, useCallback } from 'react'; +import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; +import React, { forwardRef, Ref, RefObject, useCallback, useEffect, useRef } from 'react'; import { Pie } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; -import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; -import { populateData } from '../../util/populateData'; +import { useChartData } from '../../util/populateData'; import { formatTooltipLabelForPieCharts, mergeConfig } from '../../util/utils'; import { PieChartPlaceholder } from '../PieChart/Placeholder'; @@ -28,7 +28,7 @@ const DonutChart = withChartContainer( } = props; const theme: any = useTheme(); - const data = populateData(labels, datasets, colors, theme.theme, true); + const data = useChartData(labels, datasets, colors, theme.theme, true); const mergedOptions = mergeConfig( { diff --git a/packages/charts/src/components/LineChart/index.tsx b/packages/charts/src/components/LineChart/index.tsx index d81f8eac3b9..69804518899 100644 --- a/packages/charts/src/components/LineChart/index.tsx +++ b/packages/charts/src/components/LineChart/index.tsx @@ -1,12 +1,12 @@ import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; -import React, { forwardRef, Ref, RefObject, useEffect, useRef, useCallback } from 'react'; +import React, { forwardRef, Ref, RefObject, useCallback, useEffect, useRef } from 'react'; import { Line } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; import { DEFAULT_OPTIONS } from '../../config'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; -import { populateData } from '../../util/populateData'; +import { useChartData } from '../../util/populateData'; import { formatTooltipLabel, mergeConfig } from '../../util/utils'; import { LineChartPlaceholder } from './Placeholder'; @@ -58,7 +58,7 @@ const LineChart = withChartContainer( ); const theme: any = useTheme(); - const data = populateData(labels, datasets, colors, theme.theme); + const data = useChartData(labels, datasets, colors, theme.theme); const chartRef = useConsolidatedRef(ref); const legendRef: RefObject = useRef(); diff --git a/packages/charts/src/components/PieChart/index.tsx b/packages/charts/src/components/PieChart/index.tsx index 285d27cdf8b..d3aa5e737fb 100644 --- a/packages/charts/src/components/PieChart/index.tsx +++ b/packages/charts/src/components/PieChart/index.tsx @@ -1,11 +1,11 @@ -import React, { FC, forwardRef, Ref, RefObject, useRef, useEffect, useCallback } from 'react'; +import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; +import React, { forwardRef, Ref, RefObject, useCallback, useEffect, useRef } from 'react'; import { Pie } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; -import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; -import { populateData } from '../../util/populateData'; +import { useChartData } from '../../util/populateData'; import { formatTooltipLabelForPieCharts, mergeConfig } from '../../util/utils'; import { PieChartPlaceholder } from './Placeholder'; @@ -28,7 +28,7 @@ const PieChart = withChartContainer( } = props; const theme: any = useTheme(); - const data = populateData(labels, datasets, colors, theme.theme, true); + const data = useChartData(labels, datasets, colors, theme.theme, true); const mergedOptions = mergeConfig( { diff --git a/packages/charts/src/components/RadarChart/index.tsx b/packages/charts/src/components/RadarChart/index.tsx index 81231de0bf5..e30383a2f8f 100644 --- a/packages/charts/src/components/RadarChart/index.tsx +++ b/packages/charts/src/components/RadarChart/index.tsx @@ -1,11 +1,11 @@ -import React, { FC, forwardRef, Ref, RefObject, useRef, useEffect, useCallback } from 'react'; +import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; +import React, { FC, forwardRef, Ref, RefObject, useCallback, useEffect, useRef } from 'react'; import { Radar } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; -import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; -import { populateData } from '../../util/populateData'; +import { useChartData } from '../../util/populateData'; import { formatTooltipLabel, mergeConfig } from '../../util/utils'; export interface RadarChartPropTypes extends ChartBaseProps {} @@ -27,7 +27,7 @@ const RadarChart: FC = withChartContainer( } = props; const theme: any = useTheme(); - const data = populateData(labels, datasets, colors, theme.theme); + const data = useChartData(labels, datasets, colors, theme.theme); const mergedOptions = mergeConfig( { diff --git a/packages/charts/src/util/populateData.ts b/packages/charts/src/util/populateData.ts index 96ee34992b4..ecbc141fe1c 100644 --- a/packages/charts/src/util/populateData.ts +++ b/packages/charts/src/util/populateData.ts @@ -1,4 +1,5 @@ import { getOrLoadTheme } from '../themes/themeMap'; +import { useMemo } from 'react'; export const populateData = (labels, datasets, colors, theme, isPie = false) => { const colorPalette = resolveColors(colors, theme); @@ -20,3 +21,7 @@ export const resolveColors = (colors, theme) => { } return Object.values(sequentialColors); }; + +export const useChartData = (labels, datasets, colors, theme, isPie = false) => { + return useMemo(() => populateData(labels, datasets, colors, theme, isPie), [labels, datasets, colors, theme, isPie]); +}; From 4a415f6b0f5d3a6fd4a162de756e48a533dbedb4 Mon Sep 17 00:00:00 2001 From: MarcusNotheis Date: Fri, 19 Jul 2019 13:32:45 +0200 Subject: [PATCH 9/9] WIP: useMemo for config --- .../charts/src/components/BarChart/index.tsx | 15 ++++++----- .../src/components/ColumnChart/index.tsx | 15 ++++++----- .../src/components/DonutChart/index.tsx | 15 ++++++----- .../charts/src/components/LineChart/index.tsx | 14 +++++----- .../charts/src/components/PieChart/index.tsx | 15 ++++++----- .../src/components/RadarChart/index.tsx | 14 +++++----- .../src/components/RadialChart/index.tsx | 27 ++++++++++--------- .../ChartContainer/withChartContainer.tsx | 25 ++++++++++------- packages/charts/src/util/utils.ts | 7 ++++- 9 files changed, 82 insertions(+), 65 deletions(-) diff --git a/packages/charts/src/components/BarChart/index.tsx b/packages/charts/src/components/BarChart/index.tsx index fddf4aa0173..6e2584a1cba 100644 --- a/packages/charts/src/components/BarChart/index.tsx +++ b/packages/charts/src/components/BarChart/index.tsx @@ -1,6 +1,6 @@ import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; import bestContrast from 'get-best-contrast-color'; -import React, { forwardRef, Ref, RefObject, useCallback, useEffect, useRef } from 'react'; +import React, { forwardRef, Ref, RefObject, useCallback, useEffect, useRef, useMemo } from 'react'; import { HorizontalBar } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; import { DEFAULT_OPTIONS } from '../../config'; @@ -8,7 +8,7 @@ import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; import { useChartData } from '../../util/populateData'; -import { formatTooltipLabel, getTextWidth, mergeConfig } from '../../util/utils'; +import { formatTooltipLabel, getTextWidth, useMergedConfig } from '../../util/utils'; import { BarChartPlaceholder } from './Placeholder'; export interface BarChartPropTypes extends ChartBaseProps {} @@ -59,8 +59,8 @@ const BarChart = withChartContainer( } }, [chartRef.current, legendRef.current, noLegend]); - const mergedOptions = mergeConfig( - { + const barChartDefaultConfig = useMemo(() => { + return { scales: { xAxes: [ { @@ -108,9 +108,10 @@ const BarChart = withChartContainer( } } } - }, - options - ); + }; + }, [valueAxisFormatter, categoryAxisFormatter]); + + const mergedOptions = useMergedConfig(barChartDefaultConfig, options); return ( <> diff --git a/packages/charts/src/components/ColumnChart/index.tsx b/packages/charts/src/components/ColumnChart/index.tsx index 371c3ba29eb..08ce89e130b 100644 --- a/packages/charts/src/components/ColumnChart/index.tsx +++ b/packages/charts/src/components/ColumnChart/index.tsx @@ -1,6 +1,6 @@ import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; import bestContrast from 'get-best-contrast-color'; -import React, { forwardRef, Ref, RefObject, useCallback, useEffect, useRef } from 'react'; +import React, { forwardRef, Ref, RefObject, useCallback, useEffect, useRef, useMemo } from 'react'; import { Bar } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; import { DEFAULT_OPTIONS } from '../../config'; @@ -8,7 +8,7 @@ import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; import { useChartData } from '../../util/populateData'; -import { formatTooltipLabel, getTextHeight, getTextWidth, mergeConfig } from '../../util/utils'; +import { formatTooltipLabel, getTextHeight, getTextWidth, useMergedConfig } from '../../util/utils'; import { ColumnChartPlaceholder } from './Placeholder'; export interface ColumnChartPropTypes extends ChartBaseProps {} @@ -59,8 +59,8 @@ const ColumnChart = withChartContainer( } }, [chartRef.current, legendRef.current, noLegend]); - const mergedOptions = mergeConfig( - { + const columnChartDefaultConfig = useMemo(() => { + return { scales: { xAxes: [ { @@ -111,9 +111,10 @@ const ColumnChart = withChartContainer( } } } - }, - options - ); + }; + }, [categoryAxisFormatter, valueAxisFormatter]); + + const mergedOptions = useMergedConfig(columnChartDefaultConfig, options); return ( <> diff --git a/packages/charts/src/components/DonutChart/index.tsx b/packages/charts/src/components/DonutChart/index.tsx index a3cbfc1045c..1e798465cee 100644 --- a/packages/charts/src/components/DonutChart/index.tsx +++ b/packages/charts/src/components/DonutChart/index.tsx @@ -1,12 +1,12 @@ import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; -import React, { forwardRef, Ref, RefObject, useCallback, useEffect, useRef } from 'react'; +import React, { forwardRef, Ref, RefObject, useCallback, useEffect, useRef, useMemo } from 'react'; import { Pie } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; import { useChartData } from '../../util/populateData'; -import { formatTooltipLabelForPieCharts, mergeConfig } from '../../util/utils'; +import { formatTooltipLabelForPieCharts, useMergedConfig } from '../../util/utils'; import { PieChartPlaceholder } from '../PieChart/Placeholder'; export interface DonutChartPropTypes extends ChartBaseProps {} @@ -30,8 +30,8 @@ const DonutChart = withChartContainer( const theme: any = useTheme(); const data = useChartData(labels, datasets, colors, theme.theme, true); - const mergedOptions = mergeConfig( - { + const donutChartDefaultConfig = useMemo(() => { + return { cutoutPercentage: 70, tooltips: { callbacks: { @@ -48,9 +48,10 @@ const DonutChart = withChartContainer( formatter: valueAxisFormatter } } - }, - options - ); + }; + }, [categoryAxisFormatter, valueAxisFormatter]); + + const mergedOptions = useMergedConfig(donutChartDefaultConfig, options); const chartRef = useConsolidatedRef(ref); const legendRef: RefObject = useRef(); diff --git a/packages/charts/src/components/LineChart/index.tsx b/packages/charts/src/components/LineChart/index.tsx index 69804518899..dfa7d04eaf0 100644 --- a/packages/charts/src/components/LineChart/index.tsx +++ b/packages/charts/src/components/LineChart/index.tsx @@ -1,5 +1,5 @@ import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; -import React, { forwardRef, Ref, RefObject, useCallback, useEffect, useRef } from 'react'; +import React, { forwardRef, Ref, RefObject, useCallback, useEffect, useRef, useMemo } from 'react'; import { Line } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; import { DEFAULT_OPTIONS } from '../../config'; @@ -7,7 +7,7 @@ import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; import { useChartData } from '../../util/populateData'; -import { formatTooltipLabel, mergeConfig } from '../../util/utils'; +import { formatTooltipLabel, useMergedConfig } from '../../util/utils'; import { LineChartPlaceholder } from './Placeholder'; export interface LineChartPropTypes extends ChartBaseProps {} @@ -28,8 +28,8 @@ const LineChart = withChartContainer( noLegend } = props; - const chartOptions = mergeConfig( - { + const lineChartDefaultConfig = useMemo(() => { + return { scales: { yAxes: [ { @@ -53,9 +53,9 @@ const LineChart = withChartContainer( formatter: valueAxisFormatter } } - }, - options - ); + }; + }, [categoryAxisFormatter, valueAxisFormatter]); + const chartOptions = useMergedConfig(lineChartDefaultConfig, options); const theme: any = useTheme(); const data = useChartData(labels, datasets, colors, theme.theme); diff --git a/packages/charts/src/components/PieChart/index.tsx b/packages/charts/src/components/PieChart/index.tsx index d3aa5e737fb..d73791f8ff1 100644 --- a/packages/charts/src/components/PieChart/index.tsx +++ b/packages/charts/src/components/PieChart/index.tsx @@ -1,12 +1,12 @@ import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; -import React, { forwardRef, Ref, RefObject, useCallback, useEffect, useRef } from 'react'; +import React, { forwardRef, Ref, RefObject, useCallback, useEffect, useRef, useMemo } from 'react'; import { Pie } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; import { useChartData } from '../../util/populateData'; -import { formatTooltipLabelForPieCharts, mergeConfig } from '../../util/utils'; +import { formatTooltipLabelForPieCharts, useMergedConfig } from '../../util/utils'; import { PieChartPlaceholder } from './Placeholder'; export interface PieChartPropTypes extends ChartBaseProps {} @@ -30,8 +30,8 @@ const PieChart = withChartContainer( const theme: any = useTheme(); const data = useChartData(labels, datasets, colors, theme.theme, true); - const mergedOptions = mergeConfig( - { + const pieChartDefaultConfig = useMemo(() => { + return { tooltips: { callbacks: { label: formatTooltipLabelForPieCharts(categoryAxisFormatter, valueAxisFormatter) @@ -47,9 +47,10 @@ const PieChart = withChartContainer( formatter: valueAxisFormatter } } - }, - options - ); + }; + }, [categoryAxisFormatter, valueAxisFormatter]); + + const mergedOptions = useMergedConfig(pieChartDefaultConfig, options); const chartRef = useConsolidatedRef(ref); const legendRef: RefObject = useRef(); diff --git a/packages/charts/src/components/RadarChart/index.tsx b/packages/charts/src/components/RadarChart/index.tsx index e30383a2f8f..63ebcf1ff6b 100644 --- a/packages/charts/src/components/RadarChart/index.tsx +++ b/packages/charts/src/components/RadarChart/index.tsx @@ -1,12 +1,12 @@ import { useConsolidatedRef } from '@ui5/webcomponents-react-base'; -import React, { FC, forwardRef, Ref, RefObject, useCallback, useEffect, useRef } from 'react'; +import React, { FC, forwardRef, Ref, RefObject, useCallback, useEffect, useRef, useMemo } from 'react'; import { Radar } from 'react-chartjs-2'; import { useTheme } from 'react-jss'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; import { withChartContainer } from '../../internal/ChartContainer/withChartContainer'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; import { useChartData } from '../../util/populateData'; -import { formatTooltipLabel, mergeConfig } from '../../util/utils'; +import { formatTooltipLabel, useMergedConfig } from '../../util/utils'; export interface RadarChartPropTypes extends ChartBaseProps {} @@ -29,8 +29,8 @@ const RadarChart: FC = withChartContainer( const theme: any = useTheme(); const data = useChartData(labels, datasets, colors, theme.theme); - const mergedOptions = mergeConfig( - { + const radarChartDefaultConfig = useMemo(() => { + return { scale: { ticks: { callback: valueAxisFormatter @@ -44,9 +44,9 @@ const RadarChart: FC = withChartContainer( plugins: { datalabels: false } - }, - options - ); + }; + }, [categoryAxisFormatter, valueAxisFormatter]); + const mergedOptions = useMergedConfig(radarChartDefaultConfig, options); const chartRef = useConsolidatedRef(ref); const legendRef: RefObject = useRef(); diff --git a/packages/charts/src/components/RadialChart/index.tsx b/packages/charts/src/components/RadialChart/index.tsx index 5d882071ab8..7989ccf834d 100644 --- a/packages/charts/src/components/RadialChart/index.tsx +++ b/packages/charts/src/components/RadialChart/index.tsx @@ -1,11 +1,11 @@ import { StyleClassHelper } from '@ui5/webcomponents-react-base'; import { ChartOptions } from 'chart.js'; -import React, { CSSProperties, forwardRef, Ref } from 'react'; +import React, { CSSProperties, forwardRef, Ref, useMemo } from 'react'; // @ts-ignore import { createUseStyles } from 'react-jss'; import { CommonProps } from '../../interfaces/CommonProps'; import { ChartBaseDefaultProps } from '../../util/ChartBaseDefaultProps'; -import { mergeConfig } from '../../util/utils'; +import { useMergedConfig } from '../../util/utils'; import { DonutChart } from '../DonutChart'; export interface RadialChartPropTypes extends CommonProps { @@ -42,8 +42,8 @@ const RadialChart = forwardRef((props: RadialChartPropTypes, ref: Ref { + return { cutoutPercentage: 90, tooltips: { enabled: false @@ -51,17 +51,20 @@ const RadialChart = forwardRef((props: RadialChartPropTypes, ref: Ref ({ + width: `${width}px`, + height: `${height}px`, + ...style + }), + [width, height, style] + ); const outerClasses = StyleClassHelper.of(classes.radialChart); if (className) { diff --git a/packages/charts/src/internal/ChartContainer/withChartContainer.tsx b/packages/charts/src/internal/ChartContainer/withChartContainer.tsx index 49ca7525f36..b0482f5786c 100644 --- a/packages/charts/src/internal/ChartContainer/withChartContainer.tsx +++ b/packages/charts/src/internal/ChartContainer/withChartContainer.tsx @@ -1,4 +1,4 @@ -import React, { ComponentType, CSSProperties, forwardRef, Ref } from 'react'; +import React, { ComponentType, CSSProperties, forwardRef, Ref, useMemo } from 'react'; // @ts-ignore import { createUseStyles } from 'react-jss'; import { ChartBaseProps } from '../../interfaces/ChartBaseProps'; @@ -37,17 +37,22 @@ export const withChartContainer = (Component: ComponentType) => { classNames = `${classNames} ${className}`; } - const loadingIndicator = getLoadingState(loading, datasets, (Component as any).LoadingPlaceholder); + const loadingIndicator = useMemo(() => { + return getLoadingState(loading, datasets, (Component as any).LoadingPlaceholder); + }, [loading, datasets, Component]); - const inlineStyle: CSSProperties = { - position: 'relative', - paddingTop: '6px', - width: `${props.width}px`, - height: `${props.height}px`, - ...style - }; + const inlineStyle: CSSProperties = useMemo( + () => ({ + position: 'relative', + paddingTop: '6px', + width: `${props.width}px`, + height: `${props.height}px`, + ...style + }), + [props.width, props.height, style] + ); - const chartHeight = noLegend ? height : height - 60; + const chartHeight = useMemo(() => (noLegend ? height : height - 60), [noLegend, height]); return (
    diff --git a/packages/charts/src/util/utils.ts b/packages/charts/src/util/utils.ts index 484e57f10ca..a1d04226f8d 100644 --- a/packages/charts/src/util/utils.ts +++ b/packages/charts/src/util/utils.ts @@ -1,5 +1,6 @@ import merge from 'deepmerge'; import { defaultFont } from '../config'; +import { useMemo } from 'react'; export const getCurrentChartElementFromContext = (context) => { const datasetMeta = context.chart.getDatasetMeta(context.datasetIndex); @@ -27,7 +28,11 @@ const combineMerge = (target, source, options) => { return destination; }; -export const mergeConfig = (x, y, options?) => merge(x, y, { ...options, arrayMerge: combineMerge }); +export const useMergedConfig = (x, y) => { + return useMemo(() => { + return merge(x, y, { arrayMerge: combineMerge }); + }, [x, y]); +}; export const formatTooltipLabel = (categoryFormatter, valueFormatter, valueAccessor = 'yLabel') => ( tooltipItem,