/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import type { ComposeOption } from 'echarts/core'; import type { BarSeriesOption } from 'echarts/charts'; import type { GridComponentOption } from 'echarts/components'; import type { CallbackDataParams } from 'echarts/types/src/util/types'; import { isEmpty } from 'lodash'; import { CategoricalColorNamespace, NumberFormats, getColumnLabel, getValueFormatter, tooltipHtml, } from '@superset-ui/core'; import { HistogramChartProps, HistogramTransformedProps } from './types'; import { LegendOrientation, LegendType, Refs } from '../types'; import { defaultGrid, defaultYAxis } from '../defaults'; import { getLegendProps } from '../utils/series'; import { getDefaultTooltip } from '../utils/tooltip'; import { getPercentFormatter } from '../utils/formatters'; export default function transformProps( chartProps: HistogramChartProps, ): HistogramTransformedProps { const refs: Refs = {}; let focusedSeries: number | undefined; const { datasource: { currencyFormats = {}, columnFormats = {} }, formData, height, hooks, legendState = {}, queriesData, theme, width, } = chartProps; const { onLegendStateChanged } = hooks; const { colorScheme, column, groupby = [], normalize, showLegend, showValue, sliceId, xAxisFormat, xAxisTitle, yAxisTitle, yAxisFormat, } = formData; const { data } = queriesData[0]; const colorFn = CategoricalColorNamespace.getScale(colorScheme); const formatter = (format: string) => getValueFormatter( column, currencyFormats, columnFormats, format, undefined, ); const xAxisFormatter = formatter(xAxisFormat); const yAxisFormatter = formatter(yAxisFormat); const percentFormatter = getPercentFormatter(NumberFormats.PERCENT_2_POINT); const groupbySet = new Set(groupby); const xAxisData: string[] = Object.keys(data[0]) .filter(key => !groupbySet.has(key)) .map(key => { const array = key.split(' - ').map(value => parseFloat(value)); return `${xAxisFormatter(array[0])} '-' ${xAxisFormatter(array[1])}`; }); const barSeries: BarSeriesOption[] = data.map(datum => { const seriesName = groupby.length > 0 ? groupby.map(key => datum[getColumnLabel(key)]).join(', ') : getColumnLabel(column); const seriesData = Object.keys(datum) .filter(key => groupbySet.has(key) === false) .map(key => datum[key] as number); return { name: seriesName, type: 'bar', data: seriesData, itemStyle: { color: colorFn(seriesName, sliceId), }, label: { show: showValue, position: 'top', formatter: params => { const { value } = params; return yAxisFormatter.format(value as number); }, }, }; }); const legendOptions = barSeries.map(series => series.name as string); if (isEmpty(legendState)) { legendOptions.forEach(legend => { legendState[legend] = true; }); } const tooltipFormatter = (params: CallbackDataParams[]) => { const title = params[0].name; const rows = params.map(param => { const { marker, seriesName, value } = param; return [`${marker}${seriesName}`, yAxisFormatter.format(value as number)]; }); if (groupby.length > 0) { const total = params.reduce( (acc, param) => acc + (param.value as number), 0, ); if (!normalize) { rows.forEach((row, i) => row.push( percentFormatter.format((params[i].value as number) / (total || 1)), ), ); } const totalRow = ['Total', yAxisFormatter.format(total)]; if (!normalize) { totalRow.push(percentFormatter.format(1)); } rows.push(totalRow); } return tooltipHtml(rows, title, focusedSeries); }; const onFocusedSeries = (index?: number | undefined) => { focusedSeries = index; }; type EChartsOption = ComposeOption; const echartOptions: EChartsOption = { grid: { ...defaultGrid, left: '5%', right: '5%', top: '10%', bottom: '10%', }, xAxis: { data: xAxisData, name: xAxisTitle, nameGap: 35, type: 'category', nameLocation: 'middle', }, yAxis: { ...defaultYAxis, name: yAxisTitle, nameGap: normalize ? 55 : 40, type: 'value', nameLocation: 'middle', axisLabel: { formatter: (value: number) => yAxisFormatter.format(value), }, }, series: barSeries, legend: { ...getLegendProps( LegendType.Scroll, LegendOrientation.Top, showLegend, theme, false, legendState, ), data: legendOptions, }, tooltip: { ...getDefaultTooltip(refs), trigger: 'axis', formatter: tooltipFormatter, }, }; return { refs, formData, width, height, echartOptions, onFocusedSeries, onLegendStateChanged, }; }