fix: Drill to detail blocked by tooltip (#22082)

Co-authored-by: Ville Brofeldt <ville.brofeldt@apple.com>
This commit is contained in:
Michael S. Molina 2022-11-23 14:50:06 -05:00 committed by GitHub
parent 1809d2b957
commit 3bc0865d90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 572 additions and 255 deletions

View File

@ -23,10 +23,13 @@ import {
extractTimegrain,
QueryFormData,
} from '@superset-ui/core';
import { BigNumberTotalChartProps } from '../types';
import { BigNumberTotalChartProps, BigNumberVizProps } from '../types';
import { getDateFormatter, parseMetricValue } from '../utils';
import { Refs } from '../../types';
export default function transformProps(chartProps: BigNumberTotalChartProps) {
export default function transformProps(
chartProps: BigNumberTotalChartProps,
): BigNumberVizProps {
const { width, height, queriesData, formData, rawFormData, hooks } =
chartProps;
const {
@ -38,6 +41,7 @@ export default function transformProps(chartProps: BigNumberTotalChartProps) {
timeFormat,
yAxisFormat,
} = formData;
const refs: Refs = {};
const { data = [], coltypes = [] } = queriesData[0];
const granularity = extractTimegrain(rawFormData as QueryFormData);
const metricName = getMetricLabel(metric);
@ -76,5 +80,6 @@ export default function transformProps(chartProps: BigNumberTotalChartProps) {
subheaderFontSize,
subheader: formattedSubheader,
onContextMenu,
refs,
};
}

View File

@ -20,17 +20,14 @@ import React, { MouseEvent } from 'react';
import {
t,
getNumberFormatter,
NumberFormatter,
smartDateVerboseFormatter,
TimeFormatter,
computeMaxFontSize,
BRAND_COLOR,
styled,
BinaryQueryObjectFilterClause,
} from '@superset-ui/core';
import { EChartsCoreOption } from 'echarts';
import Echart from '../components/Echart';
import { BigNumberWithTrendlineFormData, TimeSeriesDatum } from './types';
import { BigNumberVizProps } from './types';
import { EventHandlers } from '../types';
const defaultNumberFormatter = getNumberFormatter();
@ -44,36 +41,7 @@ const PROPORTION = {
TRENDLINE: 0.3,
};
type BigNumberVisProps = {
className?: string;
width: number;
height: number;
bigNumber?: number | null;
bigNumberFallback?: TimeSeriesDatum;
headerFormatter: NumberFormatter | TimeFormatter;
formatTime: TimeFormatter;
headerFontSize: number;
kickerFontSize: number;
subheader: string;
subheaderFontSize: number;
showTimestamp?: boolean;
showTrendLine?: boolean;
startYAxisAtZero?: boolean;
timeRangeFixed?: boolean;
timestamp?: number;
trendLineData?: TimeSeriesDatum[];
mainColor: string;
echartOptions: EChartsCoreOption;
onContextMenu?: (
clientX: number,
clientY: number,
filters?: BinaryQueryObjectFilterClause[],
) => void;
xValueFormatter?: TimeFormatter;
formData?: BigNumberWithTrendlineFormData;
};
class BigNumberVis extends React.PureComponent<BigNumberVisProps> {
class BigNumberVis extends React.PureComponent<BigNumberVizProps> {
static defaultProps = {
className: '',
headerFormatter: defaultNumberFormatter,
@ -108,7 +76,7 @@ class BigNumberVis extends React.PureComponent<BigNumberVisProps> {
renderFallbackWarning() {
const { bigNumberFallback, formatTime, showTimestamp } = this.props;
if (!bigNumberFallback || showTimestamp) return null;
if (!formatTime || !bigNumberFallback || showTimestamp) return null;
return (
<span
className="alert alert-warning"
@ -125,7 +93,13 @@ class BigNumberVis extends React.PureComponent<BigNumberVisProps> {
renderKicker(maxHeight: number) {
const { timestamp, showTimestamp, formatTime, width } = this.props;
if (!showTimestamp) return null;
if (
!formatTime ||
!showTimestamp ||
typeof timestamp === 'string' ||
typeof timestamp === 'boolean'
)
return null;
const text = timestamp === null ? '' : formatTime(timestamp);
@ -155,6 +129,7 @@ class BigNumberVis extends React.PureComponent<BigNumberVisProps> {
renderHeader(maxHeight: number) {
const { bigNumber, headerFormatter, width } = this.props;
// @ts-ignore
const text = bigNumber === null ? t('No data') : headerFormatter(bigNumber);
const container = this.createTemporaryContainer();
@ -231,7 +206,7 @@ class BigNumberVis extends React.PureComponent<BigNumberVisProps> {
}
renderTrendline(maxHeight: number) {
const { width, trendLineData, echartOptions } = this.props;
const { width, trendLineData, echartOptions, refs } = this.props;
// if can't find any non-null values, no point rendering the trendline
if (!trendLineData?.some(d => d[1] !== null)) {
@ -264,12 +239,15 @@ class BigNumberVis extends React.PureComponent<BigNumberVisProps> {
};
return (
<Echart
width={Math.floor(width)}
height={maxHeight}
echartOptions={echartOptions}
eventHandlers={eventHandlers}
/>
echartOptions && (
<Echart
refs={refs}
width={Math.floor(width)}
height={maxHeight}
echartOptions={echartOptions}
eventHandlers={eventHandlers}
/>
)
);
}
@ -292,7 +270,9 @@ class BigNumberVis extends React.PureComponent<BigNumberVisProps> {
<div className="text-container" style={{ height: allTextHeight }}>
{this.renderFallbackWarning()}
{this.renderKicker(
Math.ceil(kickerFontSize * (1 - PROPORTION.TRENDLINE) * height),
Math.ceil(
(kickerFontSize || 0) * (1 - PROPORTION.TRENDLINE) * height,
),
)}
{this.renderHeader(
Math.ceil(headerFontSize * (1 - PROPORTION.TRENDLINE) * height),
@ -311,7 +291,7 @@ class BigNumberVis extends React.PureComponent<BigNumberVisProps> {
return (
<div className={className} style={{ height }}>
{this.renderFallbackWarning()}
{this.renderKicker(kickerFontSize * height)}
{this.renderKicker((kickerFontSize || 0) * height)}
{this.renderHeader(Math.ceil(headerFontSize * height))}
{this.renderSubheader(Math.ceil(subheaderFontSize * height))}
</div>

View File

@ -30,11 +30,14 @@ import {
} from '@superset-ui/core';
import { EChartsCoreOption, graphic } from 'echarts';
import {
BigNumberVizProps,
BigNumberDatum,
BigNumberWithTrendlineChartProps,
TimeSeriesDatum,
} from '../types';
import { getDateFormatter, parseMetricValue } from '../utils';
import { getDefaultPosition } from '../../utils/tooltip';
import { Refs } from '../../types';
const defaultNumberFormatter = getNumberFormatter();
export function renderTooltipFactory(
@ -60,7 +63,7 @@ const formatPercentChange = getNumberFormatter(
export default function transformProps(
chartProps: BigNumberWithTrendlineChartProps,
) {
): BigNumberVizProps {
const {
width,
height,
@ -95,6 +98,7 @@ export default function transformProps(
from_dttm: fromDatetime,
to_dttm: toDatetime,
} = queriesData[0];
const refs: Refs = {};
const metricName = getMetricLabel(metric);
const compareLag = Number(compareLag_) || 0;
let formattedSubheader = subheader;
@ -103,7 +107,7 @@ export default function transformProps(
const mainColor = `rgb(${r}, ${g}, ${b})`;
const xAxisLabel = getXAxisLabel(rawFormData) as string;
let trendLineData;
let trendLineData: TimeSeriesDatum[] | undefined;
let percentChange = 0;
let bigNumber = data.length === 0 ? null : data[0][metricName];
let timestamp = data.length === 0 ? null : data[0][xAxisLabel];
@ -144,6 +148,7 @@ export default function transformProps(
}
}
sortedData.reverse();
// @ts-ignore
trendLineData = showTrendLine ? sortedData : undefined;
}
@ -229,10 +234,10 @@ export default function transformProps(
bottom: 0,
},
tooltip: {
position: getDefaultPosition(refs),
appendToBody: true,
show: !inContextMenu,
trigger: 'axis',
confine: true,
formatter: renderTooltipFactory(formatTime, headerFormatter),
},
aria: {
@ -250,6 +255,7 @@ export default function transformProps(
width,
height,
bigNumber,
// @ts-ignore
bigNumberFallback,
className,
headerFormatter,
@ -267,5 +273,6 @@ export default function transformProps(
echartOptions,
onContextMenu,
xValueFormatter: formatTime,
refs,
};
}

View File

@ -17,12 +17,17 @@
* under the License.
*/
import { EChartsCoreOption } from 'echarts';
import {
BinaryQueryObjectFilterClause,
ChartDataResponseResult,
ChartProps,
DataRecordValue,
NumberFormatter,
QueryFormData,
QueryFormMetric,
TimeFormatter,
} from '@superset-ui/core';
import { BaseChartProps, Refs } from '../types';
export interface BigNumberDatum {
[key: string]: number | null;
@ -43,15 +48,50 @@ export type BigNumberWithTrendlineFormData = BigNumberTotalFormData & {
compareLag?: string | number;
};
export type BigNumberTotalChartProps = ChartProps<QueryFormData> & {
formData: BigNumberTotalFormData;
queriesData: (ChartDataResponseResult & {
data?: BigNumberDatum[];
})[];
};
export interface BigNumberTotalChartDataResponseResult
extends ChartDataResponseResult {
data: BigNumberDatum[];
}
export type BigNumberWithTrendlineChartProps = BigNumberTotalChartProps & {
formData: BigNumberWithTrendlineFormData;
};
export type BigNumberTotalChartProps =
BaseChartProps<BigNumberTotalFormData> & {
formData: BigNumberTotalFormData;
queriesData: BigNumberTotalChartDataResponseResult[];
};
export type BigNumberWithTrendlineChartProps =
BaseChartProps<BigNumberWithTrendlineFormData> & {
formData: BigNumberWithTrendlineFormData;
};
export type TimeSeriesDatum = [number, number | null];
export type BigNumberVizProps = {
className?: string;
width: number;
height: number;
bigNumber?: DataRecordValue;
bigNumberFallback?: TimeSeriesDatum;
headerFormatter: NumberFormatter | TimeFormatter;
formatTime?: TimeFormatter;
headerFontSize: number;
kickerFontSize?: number;
subheader: string;
subheaderFontSize: number;
showTimestamp?: boolean;
showTrendLine?: boolean;
startYAxisAtZero?: boolean;
timeRangeFixed?: boolean;
timestamp?: DataRecordValue;
trendLineData?: TimeSeriesDatum[];
mainColor?: string;
echartOptions?: EChartsCoreOption;
onContextMenu?: (
clientX: number,
clientY: number,
filters?: BinaryQueryObjectFilterClause[],
) => void;
xValueFormatter?: TimeFormatter;
formData?: BigNumberWithTrendlineFormData;
refs: Refs;
};

View File

@ -31,6 +31,7 @@ export default function EchartsBoxPlot(props: BoxPlotChartTransformedProps) {
groupby,
selectedValues,
formData,
refs,
} = props;
const handleChange = useCallback(
(values: string[]) => {
@ -72,6 +73,7 @@ export default function EchartsBoxPlot(props: BoxPlotChartTransformedProps) {
return (
<Echart
refs={refs}
height={height}
width={width}
echartOptions={echartOptions}

View File

@ -36,9 +36,11 @@ import {
sanitizeHtml,
} from '../utils/series';
import { convertInteger } from '../utils/convertInteger';
import { defaultGrid, defaultTooltip, defaultYAxis } from '../defaults';
import { defaultGrid, defaultYAxis } from '../defaults';
import { getPadding } from '../Timeseries/transformers';
import { OpacityEnum } from '../constants';
import { getDefaultPosition } from '../utils/tooltip';
import { Refs } from '../types';
export default function transformProps(
chartProps: EchartsBoxPlotChartProps,
@ -71,6 +73,7 @@ export default function transformProps(
yAxisTitlePosition,
sliceId,
} = formData as BoxPlotQueryFormData;
const refs: Refs = {};
const colorFn = CategoricalColorNamespace.getScale(colorScheme as string);
const numberFormatter = getNumberFormatter(numberFormat);
const metricLabels = metrics.map(getMetricLabel);
@ -270,7 +273,7 @@ export default function transformProps(
nameLocation: yAxisTitlePosition === 'Left' ? 'middle' : 'end',
},
tooltip: {
...defaultTooltip,
position: getDefaultPosition(refs),
show: !inContextMenu,
trigger: 'item',
axisPointer: {
@ -291,5 +294,6 @@ export default function transformProps(
groupby,
selectedValues,
onContextMenu,
refs,
};
}

View File

@ -16,12 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
import { QueryFormData } from '@superset-ui/core';
import {
ChartDataResponseResult,
ChartProps,
QueryFormData,
} from '@superset-ui/core';
import { EchartsTitleFormData, EChartTransformedProps } from '../types';
BaseChartProps,
BaseTransformedProps,
ContextMenuTransformedProps,
CrossFilterTransformedProps,
TitleFormData,
} from '../types';
import { DEFAULT_TITLE_FORM_DATA } from '../constants';
export type BoxPlotQueryFormData = QueryFormData & {
@ -29,7 +31,7 @@ export type BoxPlotQueryFormData = QueryFormData & {
whiskerOptions?: BoxPlotFormDataWhiskerOptions;
xTickLayout?: BoxPlotFormXTickLayout;
emitFilter: boolean;
} & EchartsTitleFormData;
} & TitleFormData;
export type BoxPlotFormDataWhiskerOptions =
| 'Tukey'
@ -51,10 +53,11 @@ export const DEFAULT_FORM_DATA: BoxPlotQueryFormData = {
};
export interface EchartsBoxPlotChartProps
extends ChartProps<BoxPlotQueryFormData> {
extends BaseChartProps<BoxPlotQueryFormData> {
formData: BoxPlotQueryFormData;
queriesData: ChartDataResponseResult[];
}
export type BoxPlotChartTransformedProps =
EChartTransformedProps<BoxPlotQueryFormData>;
BaseTransformedProps<BoxPlotQueryFormData> &
CrossFilterTransformedProps &
ContextMenuTransformedProps;

View File

@ -31,6 +31,7 @@ export default function EchartsFunnel(props: FunnelChartTransformedProps) {
groupby,
selectedValues,
formData,
refs,
} = props;
const handleChange = useCallback(
(values: string[]) => {
@ -72,6 +73,7 @@ export default function EchartsFunnel(props: FunnelChartTransformedProps) {
return (
<Echart
refs={refs}
height={height}
width={width}
echartOptions={echartOptions}

View File

@ -40,8 +40,10 @@ import {
getLegendProps,
sanitizeHtml,
} from '../utils/series';
import { defaultGrid, defaultTooltip } from '../defaults';
import { defaultGrid } from '../defaults';
import { OpacityEnum, DEFAULT_LEGEND_FORM_DATA } from '../constants';
import { getDefaultPosition } from '../utils/tooltip';
import { Refs } from '../types';
const percentFormatter = getNumberFormatter(NumberFormats.PERCENT_2_POINT);
@ -115,6 +117,7 @@ export default function transformProps(
...DEFAULT_FUNNEL_FORM_DATA,
...formData,
};
const refs: Refs = {};
const metricLabel = getMetricLabel(metric);
const groupbyLabels = groupby.map(getColumnLabel);
const keys = data.map(datum =>
@ -212,7 +215,7 @@ export default function transformProps(
...defaultGrid,
},
tooltip: {
...defaultTooltip,
position: getDefaultPosition(refs),
show: !inContextMenu,
trigger: 'item',
formatter: (params: any) =>
@ -240,5 +243,6 @@ export default function transformProps(
groupby,
selectedValues,
onContextMenu,
refs,
};
}

View File

@ -16,21 +16,20 @@
* specific language governing permissions and limitations
* under the License.
*/
import { QueryFormData } from '@superset-ui/core';
import {
ChartDataResponseResult,
ChartProps,
QueryFormData,
} from '@superset-ui/core';
import {
EchartsLegendFormData,
EChartTransformedProps,
BaseChartProps,
BaseTransformedProps,
ContextMenuTransformedProps,
CrossFilterTransformedProps,
LegendFormData,
LegendOrientation,
LegendType,
} from '../types';
import { DEFAULT_LEGEND_FORM_DATA } from '../constants';
export type EchartsFunnelFormData = QueryFormData &
EchartsLegendFormData & {
LegendFormData & {
colorScheme?: string;
groupby: QueryFormData[];
labelLine: boolean;
@ -54,9 +53,8 @@ export enum EchartsFunnelLabelTypeType {
}
export interface EchartsFunnelChartProps
extends ChartProps<EchartsFunnelFormData> {
extends BaseChartProps<EchartsFunnelFormData> {
formData: EchartsFunnelFormData;
queriesData: ChartDataResponseResult[];
}
// @ts-ignore
@ -76,4 +74,6 @@ export const DEFAULT_FORM_DATA: EchartsFunnelFormData = {
};
export type FunnelChartTransformedProps =
EChartTransformedProps<EchartsFunnelFormData>;
BaseTransformedProps<EchartsFunnelFormData> &
CrossFilterTransformedProps &
ContextMenuTransformedProps;

View File

@ -31,6 +31,7 @@ export default function EchartsGauge(props: GaugeChartTransformedProps) {
groupby,
selectedValues,
formData: { emitFilter },
refs,
} = props;
const handleChange = useCallback(
(values: string[]) => {
@ -72,6 +73,7 @@ export default function EchartsGauge(props: GaugeChartTransformedProps) {
return (
<Echart
refs={refs}
height={height}
width={width}
echartOptions={echartOptions}

View File

@ -44,6 +44,8 @@ import {
FONT_SIZE_MULTIPLIERS,
} from './constants';
import { OpacityEnum } from '../constants';
import { getDefaultPosition } from '../utils/tooltip';
import { Refs } from '../types';
const setIntervalBoundsAndColors = (
intervals: string,
@ -118,6 +120,7 @@ export default function transformProps(
emitFilter,
sliceId,
}: EchartsGaugeFormData = { ...DEFAULT_GAUGE_FORM_DATA, ...formData };
const refs: Refs = {};
const data = (queriesData[0]?.data || []) as DataRecord[];
const numberFormatter = getNumberFormatter(numberFormat);
const colorFn = CategoricalColorNamespace.getScale(colorScheme as string);
@ -258,6 +261,7 @@ export default function transformProps(
color: gaugeSeriesOptions.detail?.color,
};
const tooltip = {
position: getDefaultPosition(refs),
formatter: (params: CallbackDataParams) => {
const { name, value } = params;
return `${name} : ${formatValue(value as number)}`;
@ -300,6 +304,7 @@ export default function transformProps(
axisTick,
pointer,
detail,
// @ts-ignore
tooltip,
radius:
Math.min(width, height) / 2 - axisLabelDistance - axisTickDistance,
@ -327,5 +332,6 @@ export default function transformProps(
groupby,
selectedValues: filterState.selectedValues || [],
onContextMenu,
refs,
};
}

View File

@ -16,13 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
import { QueryFormColumn, QueryFormData } from '@superset-ui/core';
import {
ChartDataResponseResult,
ChartProps,
QueryFormColumn,
QueryFormData,
} from '@superset-ui/core';
import { EChartTransformedProps } from '../types';
BaseChartProps,
BaseTransformedProps,
ContextMenuTransformedProps,
CrossFilterTransformedProps,
} from '../types';
import { DEFAULT_LEGEND_FORM_DATA } from '../constants';
export type AxisTickLineStyle = {
@ -80,10 +80,11 @@ export const DEFAULT_FORM_DATA: Partial<EchartsGaugeFormData> = {
};
export interface EchartsGaugeChartProps
extends ChartProps<EchartsGaugeFormData> {
extends BaseChartProps<EchartsGaugeFormData> {
formData: EchartsGaugeFormData;
queriesData: ChartDataResponseResult[];
}
export type GaugeChartTransformedProps =
EChartTransformedProps<EchartsGaugeFormData>;
BaseTransformedProps<EchartsGaugeFormData> &
ContextMenuTransformedProps &
CrossFilterTransformedProps;

View File

@ -34,6 +34,7 @@ export default function EchartsGraph({
echartOptions,
formData,
onContextMenu,
refs,
}: GraphChartTransformedProps) {
const eventHandlers: EventHandlers = {
contextmenu: (e: Event) => {
@ -68,6 +69,7 @@ export default function EchartsGraph({
};
return (
<Echart
refs={refs}
height={height}
width={width}
echartOptions={echartOptions}

View File

@ -18,7 +18,6 @@
*/
import {
CategoricalColorNamespace,
ChartProps,
getMetricLabel,
DataRecord,
DataRecordValue,
@ -32,9 +31,12 @@ import {
DEFAULT_FORM_DATA as DEFAULT_GRAPH_FORM_DATA,
EdgeSymbol,
GraphChartTransformedProps,
EchartsGraphChartProps,
} from './types';
import { DEFAULT_GRAPH_SERIES_OPTION } from './constants';
import { getChartPadding, getLegendProps, sanitizeHtml } from '../utils/series';
import { getDefaultPosition } from '../utils/tooltip';
import { Refs } from '../types';
type EdgeWithStyles = GraphEdgeItemOption & {
lineStyle: Exclude<GraphEdgeItemOption['lineStyle'], undefined>;
@ -158,7 +160,7 @@ function getCategoryName(columnName: string, name?: DataRecordValue) {
}
export default function transformProps(
chartProps: ChartProps,
chartProps: EchartsGraphChartProps,
): GraphChartTransformedProps {
const { width, height, formData, queriesData, hooks, inContextMenu } =
chartProps;
@ -294,11 +296,13 @@ export default function transformProps(
},
];
const refs: Refs = {};
const echartOptions: EChartsCoreOption = {
animationDuration: DEFAULT_GRAPH_SERIES_OPTION.animationDuration,
animationEasing: DEFAULT_GRAPH_SERIES_OPTION.animationEasing,
tooltip: {
show: !inContextMenu,
position: getDefaultPosition(refs),
formatter: (params: any): string =>
edgeFormatter(
params.data.source,
@ -322,5 +326,6 @@ export default function transformProps(
formData,
echartOptions,
onContextMenu,
refs,
};
}

View File

@ -16,16 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
import {
PlainObject,
QueryFormData,
BinaryQueryObjectFilterClause,
} from '@superset-ui/core';
import { QueryFormData } from '@superset-ui/core';
import { GraphNodeItemOption } from 'echarts/types/src/chart/graph/GraphSeries';
import { SeriesTooltipOption } from 'echarts/types/src/util/types';
import {
EchartsLegendFormData,
EchartsProps,
BaseChartProps,
BaseTransformedProps,
ContextMenuTransformedProps,
LegendFormData,
LegendOrientation,
LegendType,
} from '../types';
@ -34,7 +32,7 @@ import { DEFAULT_LEGEND_FORM_DATA } from '../constants';
export type EdgeSymbol = 'none' | 'circle' | 'arrow';
export type EchartsGraphFormData = QueryFormData &
EchartsLegendFormData & {
LegendFormData & {
source: string;
target: string;
sourceCategory?: string;
@ -85,11 +83,10 @@ export type tooltipFormatParams = {
data: { [name: string]: string };
};
export type GraphChartTransformedProps = EchartsProps & {
formData: PlainObject;
onContextMenu?: (
clientX: number,
clientY: number,
filters?: BinaryQueryObjectFilterClause[],
) => void;
};
export interface EchartsGraphChartProps
extends BaseChartProps<EchartsGraphFormData> {
formData: EchartsGraphFormData;
}
export type GraphChartTransformedProps =
BaseTransformedProps<EchartsGraphFormData> & ContextMenuTransformedProps;

View File

@ -43,6 +43,7 @@ export default function EchartsMixedTimeseries({
onContextMenu,
xValueFormatter,
xAxis,
refs,
}: EchartsMixedTimeseriesChartTransformedProps) {
const isFirstQuery = useCallback(
(seriesIndex: number) => seriesIndex < seriesBreakdown,
@ -61,7 +62,7 @@ export default function EchartsMixedTimeseries({
const currentGroupBy = isFirstQuery(seriesIndex) ? groupby : groupbyB;
const currentLabelMap = isFirstQuery(seriesIndex) ? labelMap : labelMapB;
const groupbyValues = values
.map(value => currentLabelMap[value])
.map(value => currentLabelMap?.[value])
.filter(value => !!value);
setDataMask({
@ -100,7 +101,7 @@ export default function EchartsMixedTimeseries({
const eventHandlers: EventHandlers = {
click: props => {
const { seriesName, seriesIndex } = props;
const values: string[] = Object.values(selectedValues);
const values: string[] = Object.values(selectedValues || {});
if (values.includes(seriesName)) {
handleChange(
values.filter(v => v !== seriesName),
@ -162,6 +163,7 @@ export default function EchartsMixedTimeseries({
return (
<Echart
refs={refs}
height={height}
width={width}
echartOptions={echartOptions}

View File

@ -40,7 +40,11 @@ import {
EchartsMixedTimeseriesChartTransformedProps,
EchartsMixedTimeseriesProps,
} from './types';
import { EchartsTimeseriesSeriesType, ForecastSeriesEnum } from '../types';
import {
EchartsTimeseriesSeriesType,
ForecastSeriesEnum,
Refs,
} from '../types';
import { parseYAxisBound } from '../utils/controls';
import {
getOverMaxHiddenFormatter,
@ -64,7 +68,7 @@ import {
rebaseForecastDatum,
} from '../utils/forecast';
import { convertInteger } from '../utils/convertInteger';
import { defaultGrid, defaultTooltip, defaultYAxis } from '../defaults';
import { defaultGrid, defaultYAxis } from '../defaults';
import {
getPadding,
getTooltipTimeFormatter,
@ -76,6 +80,7 @@ import {
transformTimeseriesAnnotation,
} from '../Timeseries/transformers';
import { TIMESERIES_CONSTANTS, TIMEGRAIN_TO_TIMESTAMP } from '../constants';
import { getDefaultPosition } from '../utils/tooltip';
export default function transformProps(
chartProps: EchartsMixedTimeseriesProps,
@ -152,6 +157,7 @@ export default function transformProps(
percentageThreshold,
}: EchartsMixedTimeseriesFormData = { ...DEFAULT_FORM_DATA, ...formData };
const refs: Refs = {};
const colorScale = CategoricalColorNamespace.getScale(colorScheme as string);
let xAxisLabel = getXAxisLabel(
@ -419,7 +425,7 @@ export default function transformProps(
},
],
tooltip: {
...defaultTooltip,
position: getDefaultPosition(refs),
show: !inContextMenu,
appendToBody: true,
trigger: richTooltip ? 'axis' : 'item',
@ -513,5 +519,6 @@ export default function transformProps(
label: xAxisLabel,
type: xAxisType,
},
refs,
};
}

View File

@ -20,19 +20,20 @@ import {
AnnotationLayer,
TimeGranularity,
QueryFormData,
ChartProps,
ChartDataResponseResult,
QueryFormColumn,
ContributionType,
TimeFormatter,
AxisType,
} from '@superset-ui/core';
import {
EchartsLegendFormData,
EchartsTitleFormData,
StackType,
BaseChartProps,
BaseTransformedProps,
ContextMenuTransformedProps,
CrossFilterTransformedProps,
EchartsTimeseriesSeriesType,
EChartTransformedProps,
LegendFormData,
StackType,
TitleFormData,
} from '../types';
import {
DEFAULT_LEGEND_FORM_DATA,
@ -86,8 +87,8 @@ export type EchartsMixedTimeseriesFormData = QueryFormData & {
groupby: QueryFormColumn[];
groupbyB: QueryFormColumn[];
emitFilter: boolean;
} & EchartsLegendFormData &
EchartsTitleFormData;
} & LegendFormData &
TitleFormData;
// @ts-ignore
export const DEFAULT_FORM_DATA: EchartsMixedTimeseriesFormData = {
@ -133,20 +134,22 @@ export const DEFAULT_FORM_DATA: EchartsMixedTimeseriesFormData = {
...DEFAULT_TITLE_FORM_DATA,
};
export interface EchartsMixedTimeseriesProps extends ChartProps {
export interface EchartsMixedTimeseriesProps
extends BaseChartProps<EchartsMixedTimeseriesFormData> {
formData: EchartsMixedTimeseriesFormData;
queriesData: ChartDataResponseResult[];
}
export type EchartsMixedTimeseriesChartTransformedProps =
EChartTransformedProps<EchartsMixedTimeseriesFormData> & {
emitFilterB: boolean;
groupbyB: QueryFormColumn[];
labelMapB: Record<string, string[]>;
seriesBreakdown: number;
xValueFormatter: TimeFormatter | StringConstructor;
xAxis: {
label: string;
type: AxisType;
BaseTransformedProps<EchartsMixedTimeseriesFormData> &
ContextMenuTransformedProps &
CrossFilterTransformedProps & {
emitFilterB: boolean;
groupbyB: QueryFormColumn[];
labelMapB: Record<string, string[]>;
seriesBreakdown: number;
xValueFormatter: TimeFormatter | StringConstructor;
xAxis: {
label: string;
type: AxisType;
};
};
};

View File

@ -31,6 +31,7 @@ export default function EchartsPie(props: PieChartTransformedProps) {
groupby,
selectedValues,
formData,
refs,
} = props;
const handleChange = useCallback(
(values: string[]) => {
@ -72,6 +73,7 @@ export default function EchartsPie(props: PieChartTransformedProps) {
return (
<Echart
refs={refs}
height={height}
width={width}
echartOptions={echartOptions}

View File

@ -43,8 +43,10 @@ import {
getLegendProps,
sanitizeHtml,
} from '../utils/series';
import { defaultGrid, defaultTooltip } from '../defaults';
import { defaultGrid } from '../defaults';
import { convertInteger } from '../utils/convertInteger';
import { getDefaultPosition } from '../utils/tooltip';
import { Refs } from '../types';
const percentFormatter = getNumberFormatter(NumberFormats.PERCENT_2_POINT);
@ -172,6 +174,7 @@ export default function transformProps(
...DEFAULT_PIE_FORM_DATA,
...formData,
};
const refs: Refs = {};
const metricLabel = getMetricLabel(metric);
const groupbyLabels = groupby.map(getColumnLabel);
const minShowLabelAngle = (showLabelsThreshold || 0) * 3.6;
@ -301,7 +304,7 @@ export default function transformProps(
},
tooltip: {
show: !inContextMenu,
...defaultTooltip,
position: getDefaultPosition(refs),
trigger: 'item',
formatter: (params: any) =>
formatPieLabel({
@ -341,5 +344,6 @@ export default function transformProps(
groupby,
selectedValues,
onContextMenu,
refs,
};
}

View File

@ -16,22 +16,20 @@
* specific language governing permissions and limitations
* under the License.
*/
import { QueryFormColumn, QueryFormData } from '@superset-ui/core';
import {
ChartDataResponseResult,
ChartProps,
QueryFormColumn,
QueryFormData,
} from '@superset-ui/core';
import {
EchartsLegendFormData,
EChartTransformedProps,
BaseChartProps,
BaseTransformedProps,
ContextMenuTransformedProps,
CrossFilterTransformedProps,
LegendFormData,
LegendOrientation,
LegendType,
} from '../types';
import { DEFAULT_LEGEND_FORM_DATA } from '../constants';
export type EchartsPieFormData = QueryFormData &
EchartsLegendFormData & {
LegendFormData & {
colorScheme?: string;
currentOwnValue?: string[] | null;
donut: boolean;
@ -59,9 +57,9 @@ export enum EchartsPieLabelType {
KeyValuePercent = 'key_value_percent',
}
export interface EchartsPieChartProps extends ChartProps<EchartsPieFormData> {
export interface EchartsPieChartProps
extends BaseChartProps<EchartsPieFormData> {
formData: EchartsPieFormData;
queriesData: ChartDataResponseResult[];
}
// @ts-ignore
@ -84,4 +82,6 @@ export const DEFAULT_FORM_DATA: EchartsPieFormData = {
};
export type PieChartTransformedProps =
EChartTransformedProps<EchartsPieFormData>;
BaseTransformedProps<EchartsPieFormData> &
ContextMenuTransformedProps &
CrossFilterTransformedProps;

View File

@ -31,6 +31,7 @@ export default function EchartsRadar(props: RadarChartTransformedProps) {
groupby,
selectedValues,
formData,
refs,
} = props;
const handleChange = useCallback(
(values: string[]) => {
@ -72,6 +73,7 @@ export default function EchartsRadar(props: RadarChartTransformedProps) {
return (
<Echart
refs={refs}
height={height}
width={width}
echartOptions={echartOptions}

View File

@ -42,7 +42,9 @@ import {
getColtypesMapping,
getLegendProps,
} from '../utils/series';
import { defaultGrid, defaultTooltip } from '../defaults';
import { defaultGrid } from '../defaults';
import { Refs } from '../types';
import { getDefaultPosition } from '../utils/tooltip';
export function formatLabel({
params,
@ -79,6 +81,7 @@ export default function transformProps(
theme,
inContextMenu,
} = chartProps;
const refs: Refs = {};
const { data = [] } = queriesData[0];
const coltypeMapping = getColtypesMapping(queriesData[0]);
@ -229,7 +232,7 @@ export default function transformProps(
...defaultGrid,
},
tooltip: {
...defaultTooltip,
position: getDefaultPosition(refs),
show: !inContextMenu,
trigger: 'item',
},
@ -255,5 +258,6 @@ export default function transformProps(
groupby,
selectedValues,
onContextMenu,
refs,
};
}

View File

@ -17,15 +17,16 @@
* under the License.
*/
import {
ChartDataResponseResult,
ChartProps,
QueryFormColumn,
QueryFormData,
QueryFormMetric,
} from '@superset-ui/core';
import {
EchartsLegendFormData,
EChartTransformedProps,
BaseChartProps,
BaseTransformedProps,
ContextMenuTransformedProps,
CrossFilterTransformedProps,
LegendFormData,
LabelPositionEnum,
LegendOrientation,
LegendType,
@ -35,7 +36,7 @@ import { DEFAULT_LEGEND_FORM_DATA } from '../constants';
type RadarColumnConfig = Record<string, { radarMetricMaxValue?: number }>;
export type EchartsRadarFormData = QueryFormData &
EchartsLegendFormData & {
LegendFormData & {
colorScheme?: string;
columnConfig?: RadarColumnConfig;
currentOwnValue?: string[] | null;
@ -57,9 +58,9 @@ export enum EchartsRadarLabelType {
KeyValue = 'key_value',
}
export interface EchartsRadarChartProps extends ChartProps {
export interface EchartsRadarChartProps
extends BaseChartProps<EchartsRadarFormData> {
formData: EchartsRadarFormData;
queriesData: ChartDataResponseResult[];
}
// @ts-ignore
@ -78,4 +79,6 @@ export const DEFAULT_FORM_DATA: EchartsRadarFormData = {
};
export type RadarChartTransformedProps =
EChartTransformedProps<EchartsRadarFormData>;
BaseTransformedProps<EchartsRadarFormData> &
ContextMenuTransformedProps &
CrossFilterTransformedProps;

View File

@ -48,9 +48,12 @@ export default function EchartsTimeseries({
onContextMenu,
xValueFormatter,
xAxis,
refs,
}: TimeseriesChartTransformedProps) {
const { emitFilter, stack } = formData;
const echartRef = useRef<EchartsHandler | null>(null);
// eslint-disable-next-line no-param-reassign
refs.echartRef = echartRef;
const lastTimeRef = useRef(Date.now());
const lastSelectedLegend = useRef('');
const clickTimer = useRef<ReturnType<typeof setTimeout>>();
@ -256,7 +259,7 @@ export default function EchartsTimeseries({
<ExtraControls formData={formData} setControlValue={setControlValue} />
</div>
<Echart
ref={echartRef}
refs={refs}
height={height - extraControlHeight}
width={width}
echartOptions={echartOptions}

View File

@ -44,7 +44,7 @@ import {
OrientationType,
} from './types';
import { DEFAULT_FORM_DATA } from './constants';
import { ForecastSeriesEnum, ForecastValue } from '../types';
import { ForecastSeriesEnum, ForecastValue, Refs } from '../types';
import { parseYAxisBound } from '../utils/controls';
import {
currentSeries,
@ -68,7 +68,7 @@ import {
rebaseForecastDatum,
} from '../utils/forecast';
import { convertInteger } from '../utils/convertInteger';
import { defaultGrid, defaultTooltip, defaultYAxis } from '../defaults';
import { defaultGrid, defaultYAxis } from '../defaults';
import {
getPadding,
getTooltipTimeFormatter,
@ -84,6 +84,7 @@ import {
TIMESERIES_CONSTANTS,
TIMEGRAIN_TO_TIMESTAMP,
} from '../constants';
import { getDefaultPosition } from '../utils/tooltip';
export default function transformProps(
chartProps: EchartsTimeseriesChartProps,
@ -147,6 +148,7 @@ export default function transformProps(
timeGrainSqla,
orientation,
}: EchartsTimeseriesFormData = { ...DEFAULT_FORM_DATA, ...formData };
const refs: Refs = {};
const colorScale = CategoricalColorNamespace.getScale(colorScheme as string);
const rebasedData = rebaseForecastDatum(data, verboseMap);
@ -380,7 +382,7 @@ export default function transformProps(
yAxis,
tooltip: {
show: !inContextMenu,
...defaultTooltip,
position: getDefaultPosition(refs),
appendToBody: true,
trigger: richTooltip ? 'axis' : 'item',
formatter: (params: any) => {
@ -463,5 +465,6 @@ export default function transformProps(
label: xAxisLabel,
type: xAxisType,
},
refs,
};
}

View File

@ -16,22 +16,24 @@
* specific language governing permissions and limitations
* under the License.
*/
import { OptionName } from 'echarts/types/src/util/types';
import {
AnnotationLayer,
ChartDataResponseResult,
ChartProps,
AxisType,
ContributionType,
QueryFormColumn,
QueryFormData,
TimeGranularity,
ContributionType,
TimeFormatter,
AxisType,
TimeGranularity,
} from '@superset-ui/core';
import {
EchartsLegendFormData,
EChartTransformedProps,
EchartsTitleFormData,
BaseChartProps,
BaseTransformedProps,
ContextMenuTransformedProps,
CrossFilterTransformedProps,
LegendFormData,
StackType,
TitleFormData,
} from '../types';
export enum OrientationType {
@ -85,20 +87,22 @@ export type EchartsTimeseriesFormData = QueryFormData & {
showExtraControls: boolean;
percentageThreshold: number;
orientation?: OrientationType;
} & EchartsLegendFormData &
EchartsTitleFormData;
} & LegendFormData &
TitleFormData;
export interface EchartsTimeseriesChartProps
extends ChartProps<EchartsTimeseriesFormData> {
extends BaseChartProps<EchartsTimeseriesFormData> {
formData: EchartsTimeseriesFormData;
queriesData: ChartDataResponseResult[];
}
export type TimeseriesChartTransformedProps =
EChartTransformedProps<EchartsTimeseriesFormData> & {
xValueFormatter: TimeFormatter | StringConstructor;
xAxis: {
label: string;
type: AxisType;
BaseTransformedProps<EchartsTimeseriesFormData> &
ContextMenuTransformedProps &
CrossFilterTransformedProps & {
legendData?: OptionName[];
xValueFormatter: TimeFormatter | StringConstructor;
xAxis: {
label: string;
type: AxisType;
};
};
};

View File

@ -21,9 +21,17 @@ import { EchartsProps } from '../types';
import Echart from '../components/Echart';
export default function EchartsGraph({
height,
width,
echartOptions,
height,
refs,
width,
}: EchartsProps) {
return <Echart height={height} width={width} echartOptions={echartOptions} />;
return (
<Echart
refs={refs}
height={height}
width={width}
echartOptions={echartOptions}
/>
);
}

View File

@ -17,6 +17,7 @@
* under the License.
*/
import { TreeSeriesOption } from 'echarts';
import { EchartsTreeFormData } from './types';
export const DEFAULT_TREE_SERIES_OPTION: TreeSeriesOption = {
label: {
@ -28,3 +29,18 @@ export const DEFAULT_TREE_SERIES_OPTION: TreeSeriesOption = {
animationEasing: 'cubicOut',
lineStyle: { color: 'source', width: 1.5 },
};
export const DEFAULT_FORM_DATA: Partial<EchartsTreeFormData> = {
id: '',
parent: '',
name: '',
rootNodeId: '',
layout: 'orthogonal',
orient: 'LR',
symbol: 'emptyCircle',
symbolSize: 7,
roam: true,
nodeLabelPosition: 'left',
childLabelPosition: 'bottom',
emphasis: 'descendant',
};

View File

@ -24,7 +24,7 @@ import {
sections,
sharedControls,
} from '@superset-ui/chart-controls';
import { DEFAULT_FORM_DATA } from './types';
import { DEFAULT_FORM_DATA } from './constants';
const requiredEntity = {
...sharedControls.entity,

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { ChartProps, getMetricLabel, DataRecordValue } from '@superset-ui/core';
import { getMetricLabel, DataRecordValue } from '@superset-ui/core';
import { EChartsCoreOption, TreeSeriesOption } from 'echarts';
import {
TreeSeriesCallbackDataParams,
@ -24,12 +24,13 @@ import {
} from 'echarts/types/src/chart/tree/TreeSeries';
import { OptionName } from 'echarts/types/src/util/types';
import {
EchartsTreeChartProps,
EchartsTreeFormData,
DEFAULT_FORM_DATA as DEFAULT_GRAPH_FORM_DATA,
TreeDataRecord,
TreeTransformedProps,
} from './types';
import { DEFAULT_TREE_SERIES_OPTION } from './constants';
import { EchartsProps } from '../types';
import { DEFAULT_FORM_DATA, DEFAULT_TREE_SERIES_OPTION } from './constants';
import { Refs } from '../types';
export function formatTooltip({
params,
@ -49,8 +50,11 @@ export function formatTooltip({
].join('');
}
export default function transformProps(chartProps: ChartProps): EchartsProps {
export default function transformProps(
chartProps: EchartsTreeChartProps,
): TreeTransformedProps {
const { width, height, formData, queriesData } = chartProps;
const refs: Refs = {};
const data: TreeDataRecord[] = queriesData[0].data || [];
const {
@ -67,7 +71,7 @@ export default function transformProps(chartProps: ChartProps): EchartsProps {
nodeLabelPosition,
childLabelPosition,
emphasis,
}: EchartsTreeFormData = { ...DEFAULT_GRAPH_FORM_DATA, ...formData };
}: EchartsTreeFormData = { ...DEFAULT_FORM_DATA, ...formData };
const metricLabel = getMetricLabel(metric);
const nameColumn = name || id;
@ -212,8 +216,10 @@ export default function transformProps(chartProps: ChartProps): EchartsProps {
};
return {
formData,
width,
height,
echartOptions,
refs,
};
}

View File

@ -16,9 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
import { OptionName } from 'echarts/types/src/util/types';
import { ChartDataResponseResult, QueryFormData } from '@superset-ui/core';
import { TreeSeriesNodeItemOption } from 'echarts/types/src/chart/tree/TreeSeries';
import { BaseChartProps, BaseTransformedProps } from '../types';
export type EchartsTreeFormData = {
export type EchartsTreeFormData = QueryFormData & {
id: string;
parent: string;
name: string;
@ -35,21 +38,18 @@ export type EchartsTreeFormData = {
emphasis: 'none' | 'ancestor' | 'descendant';
};
export const DEFAULT_FORM_DATA: EchartsTreeFormData = {
id: '',
parent: '',
name: '',
rootNodeId: '',
layout: 'orthogonal',
orient: 'LR',
symbol: 'emptyCircle',
symbolSize: 7,
roam: true,
nodeLabelPosition: 'left',
childLabelPosition: 'bottom',
emphasis: 'descendant',
export interface TreeChartDataResponseResult extends ChartDataResponseResult {
data: TreeDataRecord[];
}
export interface EchartsTreeChartProps
extends BaseChartProps<EchartsTreeFormData> {
formData: EchartsTreeFormData;
queriesData: TreeChartDataResponseResult[];
}
export type TreeDataRecord = Record<string, OptionName> & {
children?: TreeSeriesNodeItemOption[];
};
export type TreeDataRecord = Record<string, string | number> & {
children: TreeSeriesNodeItemOption[];
};
export type TreeTransformedProps = BaseTransformedProps<EchartsTreeFormData>;

View File

@ -28,15 +28,16 @@ import { extractTreePathInfo } from './constants';
import { TreemapTransformedProps } from './types';
export default function EchartsTreemap({
height,
width,
echartOptions,
setDataMask,
labelMap,
groupby,
selectedValues,
formData,
groupby,
height,
labelMap,
onContextMenu,
refs,
setDataMask,
selectedValues,
width,
}: TreemapTransformedProps) {
const handleChange = useCallback(
(values: string[]) => {
@ -113,6 +114,7 @@ export default function EchartsTreemap({
return (
<Echart
refs={refs}
height={height}
width={width}
echartOptions={echartOptions}

View File

@ -38,7 +38,6 @@ import {
TreemapTransformedProps,
} from './types';
import { formatSeriesName, getColtypesMapping } from '../utils/series';
import { defaultTooltip } from '../defaults';
import {
COLOR_SATURATION,
BORDER_WIDTH,
@ -48,6 +47,8 @@ import {
BORDER_COLOR,
} from './constants';
import { OpacityEnum } from '../constants';
import { getDefaultPosition } from '../utils/tooltip';
import { Refs } from '../types';
export function formatLabel({
params,
@ -139,7 +140,7 @@ export default function transformProps(
...DEFAULT_TREEMAP_FORM_DATA,
...formData,
};
const refs: Refs = {};
const colorFn = CategoricalColorNamespace.getScale(colorScheme as string);
const numberFormatter = getNumberFormatter(numberFormat);
const formatter = (params: TreemapSeriesCallbackDataParams) =>
@ -309,7 +310,7 @@ export default function transformProps(
const echartOptions: EChartsCoreOption = {
tooltip: {
...defaultTooltip,
position: getDefaultPosition(refs),
show: !inContextMenu,
trigger: 'item',
formatter: (params: any) =>
@ -332,5 +333,6 @@ export default function transformProps(
groupby,
selectedValues: filterState.selectedValues || [],
onContextMenu,
refs,
};
}

View File

@ -24,7 +24,12 @@ import {
QueryFormMetric,
} from '@superset-ui/core';
import { CallbackDataParams } from 'echarts/types/src/util/types';
import { EChartTransformedProps, LabelPositionEnum } from '../types';
import {
BaseTransformedProps,
ContextMenuTransformedProps,
CrossFilterTransformedProps,
LabelPositionEnum,
} from '../types';
export type EchartsTreemapFormData = QueryFormData & {
colorScheme?: string;
@ -73,4 +78,6 @@ export interface TreemapSeriesCallbackDataParams extends CallbackDataParams {
}
export type TreemapTransformedProps =
EChartTransformedProps<EchartsTreemapFormData>;
BaseTransformedProps<EchartsTreemapFormData> &
ContextMenuTransformedProps &
CrossFilterTransformedProps;

View File

@ -42,10 +42,15 @@ function Echart(
eventHandlers,
zrEventHandlers,
selectedValues = {},
refs,
}: EchartsProps,
ref: React.Ref<EchartsHandler>,
) {
const divRef = useRef<HTMLDivElement>(null);
if (refs) {
// eslint-disable-next-line no-param-reassign
refs.divRef = divRef;
}
const chartRef = useRef<ECharts>();
const currentSelection = useMemo(
() => Object.keys(selectedValues) || [],
@ -106,6 +111,7 @@ function Echart(
// did mount
useEffect(() => {
handleSizeChange({ width, height });
return () => chartRef.current?.dispose();
}, []);
useLayoutEffect(() => {

View File

@ -20,8 +20,8 @@
import { JsonValue, t, TimeGranularity } from '@superset-ui/core';
import { ReactNode } from 'react';
import {
EchartsLegendFormData,
EchartsTitleFormData,
LegendFormData,
TitleFormData,
LabelPositionEnum,
LegendOrientation,
LegendType,
@ -91,14 +91,14 @@ export const TIMEGRAIN_TO_TIMESTAMP = {
[TimeGranularity.YEAR]: 3600 * 1000 * 24 * 31 * 12,
};
export const DEFAULT_LEGEND_FORM_DATA: EchartsLegendFormData = {
export const DEFAULT_LEGEND_FORM_DATA: LegendFormData = {
legendMargin: null,
legendOrientation: LegendOrientation.Top,
legendType: LegendType.Scroll,
showLegend: true,
};
export const DEFAULT_TITLE_FORM_DATA: EchartsTitleFormData = {
export const DEFAULT_TITLE_FORM_DATA: TitleFormData = {
xAxisTitle: '',
xAxisTitleMargin: 0,
yAxisTitle: '',
@ -107,3 +107,10 @@ export const DEFAULT_TITLE_FORM_DATA: EchartsTitleFormData = {
};
export { DEFAULT_FORM_DATA } from './Timeseries/constants';
// How far away from the mouse should the tooltip be
export const TOOLTIP_POINTER_MARGIN = 10;
// If no satisfactory position can be found, how far away
// from the edge of the window should the tooltip be kept
export const TOOLTIP_OVERFLOW_MARGIN = 5;

View File

@ -1,4 +1,6 @@
import { CallbackDataParams } from 'echarts/types/src/util/types';
import { LegendOrientation } from './types';
import { TOOLTIP_POINTER_MARGIN, TOOLTIP_OVERFLOW_MARGIN } from './constants';
/**
* Licensed to the Apache Software Foundation (ASF) under one
@ -23,7 +25,59 @@ export const defaultGrid = {
};
export const defaultTooltip = {
confine: true,
position: (
canvasMousePos: [number, number],
params: CallbackDataParams,
tooltipDom: HTMLElement,
rect: any,
sizes: { contentSize: [number, number]; viewSize: [number, number] },
) => {
// algorithm copy-pasted from here:
// https://github.com/apache/echarts/issues/5004#issuecomment-559668309
// The chart canvas position
const canvasRect = tooltipDom.parentElement
?.getElementsByTagName('canvas')[0]
.getBoundingClientRect();
// The mouse coordinates relative to the whole window
// The first parameter to the position function is the mouse position relative to the canvas
const mouseX = canvasMousePos[0] + (canvasRect?.x || 0);
const mouseY = canvasMousePos[1] + (canvasRect?.y || 0);
// The width and height of the tooltip dom element
const tooltipWidth = sizes.contentSize[0];
const tooltipHeight = sizes.contentSize[1];
// Start by placing the tooltip top and right relative to the mouse position
let xPos = mouseX + TOOLTIP_POINTER_MARGIN;
let yPos = mouseY - TOOLTIP_POINTER_MARGIN - tooltipHeight;
// The tooltip is overflowing past the right edge of the window
if (xPos + tooltipWidth >= document.documentElement.clientWidth) {
// Attempt to place the tooltip to the left of the mouse position
xPos = mouseX - TOOLTIP_POINTER_MARGIN - tooltipWidth;
// The tooltip is overflowing past the left edge of the window
if (xPos <= 0)
// Place the tooltip a fixed distance from the left edge of the window
xPos = TOOLTIP_OVERFLOW_MARGIN;
}
// The tooltip is overflowing past the top edge of the window
if (yPos <= 0) {
// Attempt to place the tooltip to the bottom of the mouse position
yPos = mouseY + TOOLTIP_POINTER_MARGIN;
// The tooltip is overflowing past the bottom edge of the window
if (yPos + tooltipHeight >= document.documentElement.clientHeight)
// Place the tooltip a fixed distance from the top edge of the window
yPos = TOOLTIP_OVERFLOW_MARGIN;
}
// Return the position (converted back to a relative position on the canvas)
return [xPos - (canvasRect?.x || 0), yPos - (canvasRect?.y || 0)];
},
};
export const defaultYAxis = {

View File

@ -16,15 +16,17 @@
* specific language governing permissions and limitations
* under the License.
*/
import React, { RefObject } from 'react';
import {
BinaryQueryObjectFilterClause,
ChartDataResponseResult,
ChartProps,
HandlerFunction,
QueryFormColumn,
BinaryQueryObjectFilterClause,
SetDataMaskHook,
} from '@superset-ui/core';
import { EChartsCoreOption, ECharts } from 'echarts';
import { TooltipMarker } from 'echarts/types/src/util/format';
import { OptionName } from 'echarts/types/src/util/types';
import { AreaChartExtraControlsValue } from './constants';
export type EchartsStylesProps = {
@ -32,6 +34,11 @@ export type EchartsStylesProps = {
width: number;
};
export type Refs = {
echartRef?: React.Ref<EchartsHandler>;
divRef?: RefObject<HTMLDivElement>;
};
export interface EchartsProps {
height: number;
width: number;
@ -40,6 +47,7 @@ export interface EchartsProps {
zrEventHandlers?: EventHandlers;
selectedValues?: Record<number, string>;
forceClear?: boolean;
refs: Refs;
}
export interface EchartsHandler {
@ -78,7 +86,7 @@ export type ForecastValue = {
forecastUpper?: number;
};
export type EchartsLegendFormData = {
export type LegendFormData = {
legendMargin: number | null | string;
legendOrientation: LegendOrientation;
legendType: LegendType;
@ -103,26 +111,41 @@ export enum LabelPositionEnum {
InsideBottomRight = 'insideBottomRight',
}
export interface EChartTransformedProps<F> {
export interface BaseChartProps<T> extends ChartProps<T> {
queriesData: ChartDataResponseResult[];
}
export interface BaseTransformedProps<F> {
echartOptions: EChartsCoreOption;
formData: F;
height: number;
width: number;
echartOptions: EChartsCoreOption;
emitFilter: boolean;
setDataMask: SetDataMaskHook;
setControlValue?: HandlerFunction;
labelMap: Record<string, string[]>;
groupby: QueryFormColumn[];
selectedValues: Record<number, string>;
legendData?: OptionName[];
onContextMenu?: (
clientX: number,
clientY: number,
filters?: BinaryQueryObjectFilterClause[],
) => void;
refs: Refs;
width: number;
}
export interface EchartsTitleFormData {
export type CrossFilterTransformedProps = {
emitFilter: boolean;
groupby: QueryFormColumn[];
labelMap: Record<string, string[]>;
setControlValue?: HandlerFunction;
setDataMask: SetDataMaskHook;
selectedValues: Record<number, string>;
};
export type ContextMenuTransformedProps = {
onContextMenu?: (
clientX: number,
clientY: number,
filters?: BinaryQueryObjectFilterClause[],
) => void;
};
export interface TitleFormData {
xAxisTitle: string;
xAxisTitleMargin: number;
yAxisTitle: string;

View File

@ -17,7 +17,11 @@
* under the License.
*/
import { BinaryQueryObjectFilterClause } from '@superset-ui/core';
import { EChartTransformedProps, EventHandlers } from '../types';
import {
BaseTransformedProps,
CrossFilterTransformedProps,
EventHandlers,
} from '../types';
export type Event = {
name: string;
@ -40,8 +44,9 @@ export const clickEventHandler =
export const contextMenuEventHandler =
(
groupby: EChartTransformedProps<any>['groupby'],
onContextMenu: EChartTransformedProps<any>['onContextMenu'],
groupby: (BaseTransformedProps<any> &
CrossFilterTransformedProps)['groupby'],
onContextMenu: BaseTransformedProps<any>['onContextMenu'],
labelMap: Record<string, string[]>,
) =>
(e: Event) => {
@ -65,7 +70,7 @@ export const contextMenuEventHandler =
};
export const allEventHandlers = (
transformedProps: EChartTransformedProps<any>,
transformedProps: BaseTransformedProps<any> & CrossFilterTransformedProps,
handleChange: (values: string[]) => void,
) => {
const { groupby, selectedValues, onContextMenu, labelMap } = transformedProps;

View File

@ -0,0 +1,76 @@
/**
* 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 { CallbackDataParams } from 'echarts/types/src/util/types';
import { TOOLTIP_OVERFLOW_MARGIN, TOOLTIP_POINTER_MARGIN } from '../constants';
import { Refs } from '../types';
export function getDefaultPosition(refs: Refs) {
return (
canvasMousePos: [number, number],
params: CallbackDataParams,
tooltipDom: HTMLDivElement | null,
rect: any,
sizes: { contentSize: [number, number]; viewSize: [number, number] },
) => {
// algorithm partially based on this snippet:
// https://github.com/apache/echarts/issues/5004#issuecomment-559668309
// The chart canvas position
const divRect = refs.divRef?.current?.getBoundingClientRect();
// The mouse coordinates relative to the whole window
// The first parameter to the position function is the mouse position relative to the canvas
const mouseX = canvasMousePos[0] + (divRect?.x || 0);
const mouseY = canvasMousePos[1] + (divRect?.y || 0);
// The width and height of the tooltip dom element
const tooltipWidth = sizes.contentSize[0];
const tooltipHeight = sizes.contentSize[1];
// Start by placing the tooltip top and right relative to the mouse position
let xPos = mouseX + TOOLTIP_POINTER_MARGIN;
let yPos = mouseY - TOOLTIP_POINTER_MARGIN - tooltipHeight;
// The tooltip is overflowing past the right edge of the window
if (xPos + tooltipWidth >= document.documentElement.clientWidth) {
// Attempt to place the tooltip to the left of the mouse position
xPos = mouseX - TOOLTIP_POINTER_MARGIN - tooltipWidth;
// The tooltip is overflowing past the left edge of the window
if (xPos <= 0)
// Place the tooltip a fixed distance from the left edge of the window
xPos = TOOLTIP_OVERFLOW_MARGIN;
}
// The tooltip is overflowing past the top edge of the window
if (yPos <= 0) {
// Attempt to place the tooltip to the bottom of the mouse position
yPos = mouseY + TOOLTIP_POINTER_MARGIN;
// The tooltip is overflowing past the bottom edge of the window
if (yPos + tooltipHeight >= document.documentElement.clientHeight)
// Place the tooltip a fixed distance from the top edge of the window
yPos = TOOLTIP_OVERFLOW_MARGIN;
}
// Return the position (converted back to a relative position on the canvas)
return [xPos - (divRect?.x || 0), yPos - (divRect?.y || 0)];
};
}

View File

@ -25,6 +25,7 @@ import transformProps from '../../src/BigNumber/BigNumberWithTrendline/transform
import {
BigNumberDatum,
BigNumberWithTrendlineChartProps,
BigNumberWithTrendlineFormData,
} from '../../src/BigNumber/types';
const formData = {
@ -44,7 +45,8 @@ const formData = {
datasource: 'test_datasource',
};
const rawFormData = {
const rawFormData: BigNumberWithTrendlineFormData = {
colorPicker: { b: 0, g: 0, r: 0 },
datasource: '1__table',
metric: 'value',
color_picker: {
@ -129,7 +131,8 @@ describe('BigNumberWithTrendline', () => {
expect(transformed.bigNumber).toStrictEqual(1.2345);
expect(transformed.bigNumberFallback).not.toBeNull();
// should successfully formatTime by ganularity
// should successfully formatTime by granularity
// @ts-ignore
expect(transformed.formatTime(new Date('2020-01-01'))).toStrictEqual(
'2020-01-01 00:00:00',
);
@ -150,6 +153,7 @@ describe('BigNumberWithTrendline', () => {
},
};
const transformed = transformProps(propsWithDatasource);
// @ts-ignore
expect(transformed.headerFormatter(transformed.bigNumber)).toStrictEqual(
'1.23',
);

View File

@ -16,13 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
import { ChartProps, supersetTheme } from '@superset-ui/core';
import { ChartProps, SqlaFormData, supersetTheme } from '@superset-ui/core';
import transformProps from '../../src/Graph/transformProps';
import { DEFAULT_GRAPH_SERIES_OPTION } from '../../src/Graph/constants';
import { EchartsGraphChartProps } from '../../src/Graph/types';
describe('EchartsGraph transformProps', () => {
it('should transform chart props for viz without category', () => {
const formData = {
const formData: SqlaFormData = {
colorScheme: 'bnbColors',
datasource: '3__table',
granularity_sqla: 'ds',
@ -30,6 +31,7 @@ describe('EchartsGraph transformProps', () => {
source: 'source_column',
target: 'target_column',
category: null,
viz_type: 'graph',
};
const queriesData = [
{
@ -57,7 +59,7 @@ describe('EchartsGraph transformProps', () => {
};
const chartProps = new ChartProps(chartPropsConfig);
expect(transformProps(chartProps)).toEqual(
expect(transformProps(chartProps as EchartsGraphChartProps)).toEqual(
expect.objectContaining({
width: 800,
height: 600,
@ -151,7 +153,7 @@ describe('EchartsGraph transformProps', () => {
});
it('should transform chart props for viz with category and falsey normalization', () => {
const formData = {
const formData: SqlaFormData = {
colorScheme: 'bnbColors',
datasource: '3__table',
granularity_sqla: 'ds',
@ -160,6 +162,7 @@ describe('EchartsGraph transformProps', () => {
target: 'target_column',
sourceCategory: 'source_category_column',
targetCategory: 'target_category_column',
viz_type: 'graph',
};
const queriesData = [
{
@ -197,7 +200,7 @@ describe('EchartsGraph transformProps', () => {
};
const chartProps = new ChartProps(chartPropsConfig);
expect(transformProps(chartProps)).toEqual(
expect(transformProps(chartProps as EchartsGraphChartProps)).toEqual(
expect.objectContaining({
width: 800,
height: 600,

View File

@ -18,6 +18,7 @@
*/
import { ChartProps, supersetTheme } from '@superset-ui/core';
import transformProps from '../../src/Tree/transformProps';
import { EchartsTreeChartProps } from '../../src/Tree/types';
describe('EchartsTree transformProps', () => {
const formData = {
@ -70,7 +71,7 @@ describe('EchartsTree transformProps', () => {
];
const chartProps = new ChartProps({ ...chartPropsConfig, queriesData });
expect(transformProps(chartProps)).toEqual(
expect(transformProps(chartProps as EchartsTreeChartProps)).toEqual(
expect.objectContaining({
width: 800,
height: 600,
@ -137,7 +138,7 @@ describe('EchartsTree transformProps', () => {
];
const chartProps = new ChartProps({ ...chartPropsConfig, queriesData });
expect(transformProps(chartProps)).toEqual(
expect(transformProps(chartProps as EchartsTreeChartProps)).toEqual(
expect.objectContaining({
width: 800,
height: 600,
@ -223,7 +224,7 @@ describe('EchartsTree transformProps', () => {
];
const chartProps = new ChartProps({ ...chartPropsConfig, queriesData });
expect(transformProps(chartProps)).toEqual(
expect(transformProps(chartProps as EchartsTreeChartProps)).toEqual(
expect.objectContaining({
width: 800,
height: 600,
@ -299,7 +300,7 @@ describe('EchartsTree transformProps', () => {
];
const chartProps = new ChartProps({ ...chartPropsConfig, queriesData });
expect(transformProps(chartProps)).toEqual(
expect(transformProps(chartProps as EchartsTreeChartProps)).toEqual(
expect.objectContaining({
width: 800,
height: 600,
@ -385,7 +386,7 @@ describe('EchartsTree transformProps', () => {
];
const chartProps = new ChartProps({ ...chartPropsConfig, queriesData });
expect(transformProps(chartProps)).toEqual(
expect(transformProps(chartProps as EchartsTreeChartProps)).toEqual(
expect.objectContaining({
width: 800,
height: 600,